/* =========================================================================
   Design tokens
   ========================================================================= */
:root {
    /* Surfaces */
    --bg: #0a0a0d;
    --surface: #131319;
    --surface-elev: #1a1a22;
    --border: #232330;
    --border-strong: #34343f;

    /* Text */
    --text: #f4f4f9;
    --text-2: #a7a7b8;
    /* `--text-3` is the tertiary tier — captions, footnotes,
       eyebrow labels. Was `#6a6a78` (~ 3.3:1 against the dark bg,
       which clears AA-Large but fails AA-Normal at 4.5:1).
       Bumped to `#8b8b9a` for ~ 5.6:1 so 12 px / 14 px captions
       pass WCAG AA-Normal. */
    --text-3: #8b8b9a;

    /* Accents */
    --accent: #5cb0ff;
    --accent-2: #9b8bff;
    --accent-soft: rgba(92, 176, 255, 0.13);
    --accent-ring: rgba(92, 176, 255, 0.22);
    --warn: #f4b342;
    --warn-soft: rgba(244, 179, 66, 0.12);
    --error: #ef6b6b;

    /* Spacing scale (Tailwind-ish 4/8/...) */
    --s-1: 4px;
    --s-2: 8px;
    --s-3: 12px;
    --s-4: 16px;
    --s-5: 24px;
    --s-6: 32px;
    --s-7: 48px;
    --s-8: 64px;
    --s-9: 96px;
    --s-10: 128px;

    /* Layout */
    --content-max: 1080px;
    --content-pad: 24px;

    /* Type */
    --font-sans: -apple-system, BlinkMacSystemFont, "SF Pro Display",
        "Inter", "Segoe UI", system-ui, sans-serif;
    --font-mono: ui-monospace, "SF Mono", Menlo, Consolas, monospace;

    /* Motion */
    --ease: cubic-bezier(0.22, 1, 0.36, 1);
    --t-fast: 120ms;
    --t-med: 240ms;
}

@media (prefers-color-scheme: light) {
    :root {
        --bg: #fbfbfd;
        --surface: #ffffff;
        --surface-elev: #ffffff;
        --border: #e6e6ec;
        --border-strong: #cccdd6;
        --text: #0d0d12;
        --text-2: #4a4a55;
        /* Light-mode tertiary — symmetric WCAG-AA bump from
           `#8a8a96` (~ 3.2:1 against the near-white bg) down to
           `#5e5e68` for ~ 6.8:1. Captions / footnotes / eyebrows
           now pass AA-Normal in both colour schemes. */
        --text-3: #5e5e68;
        --accent: #2569e0;
        --accent-2: #6a5cf0;
        --accent-soft: rgba(37, 105, 224, 0.09);
        --accent-ring: rgba(37, 105, 224, 0.18);
    }
}

/* =========================================================================
   Base
   ========================================================================= */
* { box-sizing: border-box; }

html {
    -webkit-text-size-adjust: 100%;
    scroll-behavior: smooth;
}

body {
    margin: 0;
    background: var(--bg);
    color: var(--text);
    font-family: var(--font-sans);
    font-size: 16px;
    line-height: 1.55;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
    text-rendering: optimizeLegibility;
}

a {
    color: var(--accent);
    text-decoration: none;
    transition: opacity var(--t-fast) var(--ease);
}
a:hover { opacity: 0.82; }

:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 3px;
    border-radius: 4px;
}

.page {
    max-width: var(--content-max);
    margin: 0 auto;
    padding: 0 var(--content-pad);
}

/* =========================================================================
   Hero
   ========================================================================= */
.hero {
    position: relative;
    /* Was `var(--s-3) 0 var(--s-9)` when the nav lived inside this
       header. Bumped top to `var(--s-6)` so the hero has breathing
       room below the now-sticky nav above the `.page` wrapper. */
    padding: var(--s-6) 0 var(--s-9);
    overflow: visible;
}

.backdrop {
    position: absolute;
    inset: -120px -240px auto -240px;
    height: 640px;
    background:
        radial-gradient(ellipse at 55% 0%, var(--accent-soft) 0%, transparent 55%),
        radial-gradient(ellipse at 20% 6%, rgba(155, 139, 255, 0.10) 0%, transparent 50%);
    pointer-events: none;
    z-index: 0;
}

/* Sticky nav header — sits outside `.page` so the translucent
   backdrop spans the full viewport while the inner `<nav>` content
   stays constrained. `isolation: isolate` forces a fresh stacking
   context so the hero's backdrop blur layer below can't accidentally
   composite over the sticky bar. `-webkit-sticky` is the Safari
   fallback (still alive on older iOS Safari).

   The inner `.nav` uses a narrower max-width (880 px) than the
   content body (1080 px) so the brand and links sit closer together
   visually — at 1080 px the gap between brand and links read as
   "two ends of a yardstick" rather than a single bar. The 880 px
   value lands the inner ends of the brand + link group at a more
   comfortable middle-of-the-bar position on typical desktops. */
.site-nav {
    position: -webkit-sticky;
    position: sticky;
    top: 0;
    z-index: 100;
    isolation: isolate;
    /* Solid fallback first (browsers without `color-mix`), then
       the translucent layer modern browsers will pick up. */
    background: var(--bg);
    background: color-mix(in srgb, var(--bg) 82%, transparent);
    -webkit-backdrop-filter: saturate(180%) blur(20px);
    backdrop-filter: saturate(180%) blur(20px);
    border-bottom: 1px solid var(--border);
}

.nav {
    max-width: 880px;
    margin: 0 auto;
    display: flex;
    align-items: center;
    gap: var(--s-5);
    padding: var(--s-3) var(--content-pad);
}

.brand {
    display: inline-flex;
    align-items: center;
    gap: var(--s-3);
    color: var(--text);
    font-weight: 600;
    font-size: 17px;
    letter-spacing: -0.01em;
}
.brand:hover { opacity: 1; }

.nav-links {
    margin-left: auto;
    display: flex;
    gap: var(--s-5);
}

.nav-links a {
    position: relative;
    color: var(--text-2);
    font-size: 14px;
    font-weight: 500;
    padding: 4px 0;
    transition: color var(--t-fast) var(--ease);
}
.nav-links a:hover {
    color: var(--text);
    opacity: 1;
}

/* Active-section highlight — covers both forms:
   - `aria-current="true"` set by the IntersectionObserver in
     `script.js` as the user scrolls past each section, and
   - `aria-current="page"` hardcoded on the Privacy link in
     privacy.html so the active state shows when the user is
     on the privacy page itself.
   The `:not([aria-current="false"])` guard avoids matching the
   bare attribute with a false-y value if some other path ever
   sets it explicitly. */
.nav-links a[aria-current]:not([aria-current="false"]) {
    color: var(--text);
}
.nav-links a[aria-current]:not([aria-current="false"])::after {
    content: '';
    position: absolute;
    left: 0;
    right: 0;
    bottom: -2px;
    height: 2px;
    border-radius: 1px;
    background: var(--accent);
    box-shadow: 0 0 8px var(--accent-ring);
}

.hero-content {
    position: relative;
    z-index: 1;
    max-width: 940px;
    padding-top: var(--s-3);
}

/* Side-by-side block: icon on the left, all the text on the right.
   Replaces the previous icon-above-title stack. On desktop the icon
   anchors visually; on narrow screens the row collapses so the icon
   sits above the text and stays a sensible size. */
.hero-top {
    display: flex;
    align-items: flex-start;
    gap: clamp(var(--s-5), 4vw, var(--s-7));
    margin-bottom: var(--s-7);
}

.hero-text {
    flex: 1;
    min-width: 0;
}

.hero-icon-wrap {
    position: relative;
    flex: 0 0 auto;
    width: clamp(112px, 14vw, 168px);
    height: clamp(112px, 14vw, 168px);
    /* No bottom margin here — the flex parent owns the spacing. */
}

@media (max-width: 640px) {
    .hero-top {
        flex-direction: column;
        gap: var(--s-5);
    }
    .hero-icon-wrap {
        width: clamp(96px, 22vw, 120px);
        height: clamp(96px, 22vw, 120px);
    }
}

.hero-icon-glow {
    position: absolute;
    inset: -36px;
    background: radial-gradient(circle, var(--accent-ring) 0%, transparent 65%);
    filter: blur(18px);
    pointer-events: none;
}

.hero-icon {
    position: relative;
    width: 100%;
    height: 100%;
    border-radius: clamp(24px, 4vw, 38px);
    display: block;
    box-shadow:
        0 28px 60px -18px rgba(0, 0, 0, 0.55),
        0 1px 0 rgba(255, 255, 255, 0.05) inset;
}

.hero-title {
    font-size: clamp(34px, 5.5vw, 60px);
    font-weight: 600;
    line-height: 1.05;
    letter-spacing: -0.025em;
    margin: 0 0 var(--s-5);
    color: var(--text);
}

.hero-title .accent {
    color: var(--accent);
}

@supports (-webkit-background-clip: text) or (background-clip: text) {
    .hero-title .accent {
        background: linear-gradient(120deg, var(--accent) 0%, var(--accent-2) 95%);
        -webkit-background-clip: text;
        background-clip: text;
        -webkit-text-fill-color: transparent;
        color: transparent;
    }
}

.hero-lead {
    font-size: 18px;
    line-height: 1.55;
    color: var(--text-2);
    max-width: 600px;
    margin: 0 0 var(--s-6);
}

/* Legacy `.trust-pills` kept around in case the markup ever
   gets rolled back; the active hero now uses `.hero-benefits`
   below for a label+benefit dl layout. */
.trust-pills {
    list-style: none;
    padding: 0;
    margin: 0;
    display: flex;
    flex-wrap: wrap;
    gap: var(--s-2);
}

.trust-pills li {
    display: inline-flex;
    align-items: center;
    gap: var(--s-2);
    padding: 6px 12px;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 999px;
    font-size: 13px;
    color: var(--text-2);
    font-weight: 500;
}

.pill-dot {
    width: 6px;
    height: 6px;
    border-radius: 50%;
    background: var(--accent);
    box-shadow: 0 0 6px var(--accent);
}

/* =========================================================================
   Hero CTA (Get-notified primary + How-it-works anchor)
   ========================================================================= */
.hero-cta {
    display: flex;
    flex-wrap: wrap;
    gap: var(--s-3);
    margin-top: var(--s-5);
    margin-bottom: var(--s-6);
    align-items: center;
}

.cta-primary {
    display: inline-flex;
    align-items: center;
    padding: 12px 22px;
    background: var(--accent);
    color: var(--bg);
    font-weight: 600;
    font-size: 15px;
    border-radius: 12px;
    text-decoration: none;
    box-shadow: 0 0 24px var(--accent-ring);
    transition: transform 80ms ease, box-shadow 120ms ease;
}

.cta-primary:hover {
    opacity: 1;
    transform: translateY(-1px);
    box-shadow: 0 0 32px var(--accent-ring);
}

/* Secondary text-only link alongside the primary CTA.
   Used for "Read the security model →" — a quieter discovery
   path for visitors who want technical depth before they hand
   over an email address. */
.cta-link {
    color: var(--text-2);
    font-size: 14px;
    font-weight: 500;
    padding: 12px 4px;
}
.cta-link:hover {
    color: var(--text);
    opacity: 1;
}

/* "Read more" affordance at the foot of a section — quiet but
   discoverable. Used at the end of §02 to point at the deeper
   `/security.html` once the visitor has scanned the in-page
   summary. Centered + slightly italic so it reads as editorial
   guidance rather than another competing CTA. */
.section-readmore {
    margin: var(--s-6) 0 0 0;
    color: var(--text-2);
    font-size: 14px;
    line-height: 1.6;
    max-width: 640px;
    border-top: 1px solid var(--border);
    padding-top: var(--s-4);
}
.section-readmore a {
    color: var(--accent);
    font-weight: 500;
}
.section-readmore a:hover { opacity: 1; }

/* Availability status — three small status rows under the hero CTA.
   The previous single-line `display:flex` paragraph had a bug where
   each text node became its own flex item ("internal TestFlight"
   got pushed into its own column). Rebuilt as a real `<ul>` of
   rows so each line lays out properly and the visual hierarchy
   (current / next / later) reads at a glance.

   Dot styling encodes status:
     filled + pulsing accent  → current state
     hollow accent border     → next, scheduled
     hollow neutral border    → later, no committed date
*/
.hero-status {
    list-style: none;
    padding: 0;
    margin: var(--s-4) 0 var(--s-6) 0;
    display: flex;
    flex-direction: column;
    gap: var(--s-2);
    font-size: 13px;
}

.status-row {
    display: grid;
    grid-template-columns: 12px 160px 1fr;
    align-items: center;
    column-gap: var(--s-3);
    color: var(--text-2);
    line-height: 1.5;
}

.status-row .status-dot {
    width: 8px;
    height: 8px;
    border-radius: 50%;
    box-sizing: border-box;
}

.status-current .status-dot {
    background: var(--accent);
    box-shadow: 0 0 8px var(--accent-ring);
    /* Slow pulse — a few seconds per cycle so the eye registers
       "live" without the dot feeling busy. */
    animation: status-dot-pulse 2.4s ease-in-out infinite;
}

.status-next .status-dot {
    background: transparent;
    border: 1.5px solid var(--accent);
}

.status-later .status-dot {
    background: transparent;
    border: 1.5px solid var(--border-strong);
}

.status-label {
    color: var(--text);
    font-weight: 600;
}

.status-value {
    color: var(--text-2);
}

@keyframes status-dot-pulse {
    0%, 100% { opacity: 1; }
    50%      { opacity: 0.55; }
}

@media (max-width: 560px) {
    .status-row {
        grid-template-columns: 12px 1fr;
        row-gap: 2px;
    }
    .status-value {
        grid-column: 2 / 3;
    }
}

/* =========================================================================
   Hero benefits — clean label/description rows, no card boxes.
   The earlier auto-fill card grid produced a lopsided 3+2 layout for
   the 5 items and the boxed-card visual weight fought the hero's
   minimalist tone. Switched to a flex column of two-column rows:
   small accent label on the left, plain-language sentence on the
   right. A single hairline above the list delineates it from the
   lead paragraph + CTA without adding chrome around each row.
   ========================================================================= */
.hero-benefits {
    display: flex;
    flex-direction: column;
    gap: var(--s-3);
    padding: var(--s-4) 0 0 0;
    margin: 0;
    border-top: 1px solid var(--border);
}

.hero-benefits .benefit {
    display: grid;
    grid-template-columns: 180px 1fr;
    column-gap: var(--s-4);
    row-gap: 4px;
    align-items: baseline;
}

.hero-benefits dt {
    font-size: 12px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.09em;
    color: var(--accent);
    margin: 0;
    white-space: nowrap;
}

.hero-benefits dd {
    margin: 0;
    color: var(--text-2);
    font-size: 14px;
    line-height: 1.5;
}

/* Stack label above description on narrow screens — the
   180-col label gutter would compress the description text too
   hard otherwise. */
@media (max-width: 560px) {
    .hero-benefits .benefit {
        grid-template-columns: 1fr;
    }
    .hero-benefits dt {
        white-space: normal;
    }
}

/* =========================================================================
   Frame section — the "Who it's for" framing between hero and §01
   ========================================================================= */
/* Editorial framing block between the hero and §01.
   Now left-aligned to match the rest of the page (all
   sections, headings, and prose share a single left edge).
   `max-width: 640 px` mirrors `.prose` / `.caption` so the
   reading column has consistent width across the page. */
.frame {
    max-width: 640px;
    padding: clamp(48px, 8vw, 96px) 0;
}

.frame-eyebrow {
    font-size: 11px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.16em;
    color: var(--accent);
    margin: 0 0 var(--s-3) 0;
}

.frame-title {
    font-size: clamp(28px, 4vw, 38px);
    font-weight: 600;
    line-height: 1.18;
    color: var(--text);
    margin: 0 0 var(--s-4) 0;
    letter-spacing: -0.01em;
}

.frame-body {
    font-size: 17px;
    line-height: 1.6;
    color: var(--text-2);
    margin: 0;
}

/* =========================================================================
   Sections
   ========================================================================= */
.section {
    /* Top padding upper-cap bumped 96 → 112 px so sections on a
       desktop viewport feel like distinct chapters rather than
       continuous scroll. The mobile minimum stays at 48 px so
       small screens don't waste real estate. */
    padding: clamp(48px, 8vw, 112px) 0 var(--s-7);
    border-top: 1px solid var(--border);
}

.section-head {
    display: flex;
    align-items: baseline;
    gap: var(--s-4);
    margin-bottom: var(--s-6);
}

/* Editorial chapter marker. Slightly more deliberate now — same
   monospace family but tabular nums + a touch more weight + wider
   tracking so the "01 / 02 / 03 / 04" sequence reads as
   chapter numbers rather than faded captions. */
.section-num {
    font-family: var(--font-mono);
    font-size: 13px;
    font-weight: 500;
    color: var(--text-3);
    letter-spacing: 0.12em;
    font-variant-numeric: tabular-nums;
}

.section h2 {
    font-size: clamp(26px, 3vw, 34px);
    font-weight: 600;
    /* Tight heading leading. Default `line-height: 1.5` rendered
       the 34 px h2 at ~51 px line-height, which felt loose;
       1.15 lets the heading sit cleanly on a single visual baseline. */
    line-height: 1.15;
    letter-spacing: -0.02em;
    margin: 0;
    color: var(--text);
}

.caption {
    color: var(--text-3);
    font-size: 14px;
    line-height: 1.6;
    /* Previously `-8px 0 var(--s-6)`. The negative top margin was
       compensating for too-wide `.section-head` spacing; now that
       the head's `margin-bottom` is `var(--s-6)`, no negative pull
       is needed. */
    margin: 0 0 var(--s-6) 0;
    max-width: 640px;
}

.prose {
    max-width: 640px;
}

.prose p {
    color: var(--text-2);
    margin: 0 0 var(--s-4);
    font-size: 16px;
    line-height: 1.65;
}

.prose p:last-child { margin-bottom: 0; }

/* =========================================================================
   Architecture diagram (§02 header)
   Horizontal flow: Your phone → Relay → Their phone.
   On wide viewports it lays out as five grid cells (node, edge, node,
   edge, node); on narrow viewports the grid collapses to a single
   column and the edges flip from horizontal lines to vertical ones.
   ========================================================================= */
.arch-diagram {
    margin: var(--s-3) 0 var(--s-7) 0;
    padding: var(--s-5);
    border: 1px solid var(--border);
    border-radius: 16px;
    background: var(--surface);
}

.arch-caption {
    text-align: center;
    color: var(--text);
    font-size: 13px;
    font-weight: 600;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    margin: 0 0 var(--s-5) 0;
}

.arch-flow {
    display: grid;
    grid-template-columns: 1fr auto 1fr auto 1fr;
    align-items: stretch;
    gap: var(--s-3);
}

.arch-node {
    text-align: center;
    padding: var(--s-4) var(--s-3);
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: 12px;
    display: flex;
    flex-direction: column;
    align-items: center;
}

/* Relay node uses a dashed border so it visually reads as
   "third-party, not inside your trust boundary" — a subtle
   distinction from the two solid-border endpoint phones. */
.arch-node.arch-relay {
    border-style: dashed;
    border-color: var(--border-strong);
    background: transparent;
}

.arch-glyph {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 36px;
    height: 36px;
    color: var(--accent);
    margin-bottom: var(--s-3);
}
.arch-glyph svg { width: 100%; height: 100%; }

.arch-title {
    font-size: 13px;
    font-weight: 600;
    color: var(--text);
    margin-bottom: 4px;
}

.arch-sub {
    font-size: 12px;
    line-height: 1.5;
    color: var(--text-2);
    margin: 0;
    max-width: 200px;
}

.arch-edge {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    min-width: 80px;
}

.arch-edge-label {
    font-size: 10px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: var(--text-3);
    margin-bottom: var(--s-2);
    white-space: nowrap;
    text-align: center;
}

.arch-edge-line {
    width: 100%;
    height: 2px;
    background: linear-gradient(90deg,
        transparent 0%,
        var(--accent-soft) 12%,
        var(--accent) 50%,
        var(--accent-soft) 88%,
        transparent 100%);
    border-radius: 1px;
}

.arch-footnote {
    text-align: center;
    margin: var(--s-5) 0 0 0;
    font-size: 12px;
    color: var(--text-2);
    font-style: italic;
}

/* On narrow screens the row collapses to a column. Edges
   flip 90° — the same gradient runs vertically and the
   "Tor · sealed envelope" label sits to the right of the
   short pipe. */
@media (max-width: 720px) {
    .arch-flow {
        grid-template-columns: 1fr;
        gap: var(--s-2);
    }
    .arch-node {
        padding: var(--s-4);
    }
    .arch-sub {
        max-width: none;
    }
    .arch-edge {
        flex-direction: row;
        gap: var(--s-3);
        min-width: 0;
        padding: var(--s-2) 0;
    }
    .arch-edge-label {
        margin-bottom: 0;
    }
    .arch-edge-line {
        width: 2px;
        height: 28px;
        background: linear-gradient(180deg,
            transparent 0%,
            var(--accent-soft) 12%,
            var(--accent) 50%,
            var(--accent-soft) 88%,
            transparent 100%);
    }
}

/* =========================================================================
   Property grid
   ========================================================================= */
.prop-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
    gap: var(--s-3);
}

.prop {
    position: relative;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 12px;
    padding: var(--s-5);
    transition:
        border-color var(--t-med) var(--ease),
        transform var(--t-med) var(--ease),
        background var(--t-med) var(--ease);
}

.prop:hover {
    border-color: var(--border-strong);
    background: var(--surface-elev);
    transform: translateY(-2px);
}

.prop-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 36px;
    height: 36px;
    border-radius: 10px;
    background: var(--accent-soft);
    color: var(--accent);
    margin-bottom: var(--s-3);
    transition: background var(--t-med) var(--ease);
}

.prop:hover .prop-icon {
    background: var(--accent-ring);
}

.prop-icon svg {
    width: 20px;
    height: 20px;
}

.prop h3 {
    font-size: 15px;
    font-weight: 600;
    color: var(--text);
    margin: 0 0 6px;
    letter-spacing: -0.01em;
}

.prop p {
    font-size: 14px;
    color: var(--text-2);
    margin: 0;
    line-height: 1.55;
}

/* =========================================================================
   Roadmap timeline
   ========================================================================= */
.timeline {
    position: relative;
    list-style: none;
    padding: 0;
    margin: var(--s-2) 0 0;
}

.timeline::before {
    content: '';
    position: absolute;
    /* Centred under the 22 px-wide dot at `left: 0` of each item.
       (Dot centre = 11 px; line width 2 px; left = 11 − 1 = 10). */
    left: 10px;
    top: 17px;
    bottom: 17px;
    width: 2px;
    background: linear-gradient(180deg, var(--border-strong) 0%, var(--border) 100%);
}

.t-item {
    position: relative;
    padding: 0 0 var(--s-5) var(--s-7);
    /* Subtle hover lift signals each row is a unit and ties the
       timeline to the rest of the page's hover idioms. */
    transition: transform var(--t-fast) var(--ease);
}

.t-item:hover {
    transform: translateX(3px);
}

.t-item:last-child { padding-bottom: 0; }

.t-dot {
    position: absolute;
    left: 0;
    top: 6px;
    width: 22px;
    height: 22px;
    border-radius: 50%;
    background: var(--bg);
    border: 2px solid var(--border-strong);
    box-sizing: border-box;
    /* Inner flex so glyph-bearing dots (shipped checkmark) centre
       their content trivially. */
    display: flex;
    align-items: center;
    justify-content: center;
    transition: border-color var(--t-med) var(--ease),
                box-shadow var(--t-med) var(--ease);
}

/* Active milestone — accent-filled dot with a slow pulse on its
   outer halo, matching the hero status-dot idiom so the page has
   one consistent "live" signal. */
.t-current .t-dot {
    background: var(--accent);
    border-color: var(--accent);
    box-shadow: 0 0 0 5px var(--accent-soft);
    animation: t-dot-pulse 2.4s ease-in-out infinite;
}

@keyframes t-dot-pulse {
    0%, 100% {
        box-shadow: 0 0 0 5px var(--accent-soft),
                    0 0 0 0 var(--accent-ring);
    }
    50% {
        box-shadow: 0 0 0 8px var(--accent-soft),
                    0 0 12px 2px var(--accent-ring);
    }
}

/* Shipped milestones — green-filled dot with a softer halo than
   `t-current` AND a small white checkmark inside so the eye reads
   "done" without needing the pill text. The check uses
   `::after` on the dot itself so it tracks the dot's centre at
   every viewport size. */
.t-shipped .t-dot {
    background: #3ecf7a;
    border-color: #3ecf7a;
    box-shadow: 0 0 0 3px rgba(62, 207, 122, 0.15);
}

.t-shipped .t-dot::after {
    content: '';
    width: 11px;
    height: 6px;
    /* Two-segment border = ✓ stroke. Smaller than a font-rendered
       glyph and crisp at every device-pixel ratio. */
    border-left: 2px solid #0a0a0d;
    border-bottom: 2px solid #0a0a0d;
    transform: rotate(-45deg) translate(1px, -1px);
}

.t-meta {
    display: flex;
    align-items: center;
    gap: var(--s-3);
    margin-bottom: 4px;
    /* Lets the title wrap onto a second line within the meta row
       on narrow screens, with the flag staying after it. */
    flex-wrap: wrap;
}

.t-tag {
    font-family: var(--font-mono);
    font-size: 13px;
    color: var(--text);
    font-weight: 600;
}

.t-current .t-tag { color: var(--accent); }
.t-shipped .t-tag { color: #3ecf7a; }

.t-flag {
    font-size: 10.5px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    padding: 3px 8px;
    border-radius: 999px;
    background: var(--surface);
    color: var(--text-3);
    border: 1px solid var(--border);
    /* Tabular spacing reads as a status badge, not as content text. */
    line-height: 1.4;
}

/* Active milestone flag — fills with accent and adds a soft outer
   ring that tracks the dot's pulse so the two indicators read as
   one. Subtle (not animated) glow on the pill itself; the row's
   live-signal motion stays on the dot. */
.t-flag.current {
    background: var(--accent-soft);
    color: var(--accent);
    border-color: transparent;
    box-shadow: 0 0 8px var(--accent-ring);
}

.t-flag.shipped {
    background: rgba(62, 207, 122, 0.14);
    color: #3ecf7a;
    border-color: transparent;
}

.t-flag.paid {
    background: var(--warn-soft);
    color: var(--warn);
    border-color: transparent;
}

/* Milestone headline. Sits on the SAME row as the V-tag and
   status pill: `[V6]  Proximity pairing (Bluetooth)  [NEXT]`.
   `flex: 1` claims the remaining horizontal space so the pill
   gets pushed to the right edge of the meta row.
   Typography matches `.t-tag`: same 13 px size, same 600
   weight, same per-state colour (accent on current, green on
   shipped, primary text otherwise) so the V-tag and the title
   read as one unit. Font stays sans-serif (not monospace) so
   the human-readable name doesn't look like code. */
.t-title {
    flex: 1;
    min-width: 0;
    font-size: 13px;
    font-weight: 600;
    color: var(--text);
    line-height: 1.4;
    margin: 0;
}

.t-current .t-title { color: var(--accent); }
.t-shipped .t-title { color: #3ecf7a; }

.t-body {
    color: var(--text-2);
    margin: 0;
    font-size: 14px;
    line-height: 1.55;
    max-width: 560px;
}

/* =========================================================================
   Stats
   ========================================================================= */
.stat-cards {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
    gap: var(--s-3);
    margin-top: var(--s-2);
}

.stat-card {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 12px;
    padding: var(--s-5);
    display: flex;
    flex-direction: column;
    transition: border-color var(--t-med) var(--ease);
}

.stat-card:hover { border-color: var(--border-strong); }

.stat-label {
    font-size: 12px;
    text-transform: uppercase;
    letter-spacing: 0.07em;
    color: var(--text-3);
    font-weight: 600;
}

.stat-value {
    font-size: 32px;
    font-weight: 600;
    letter-spacing: -0.02em;
    color: var(--text);
    font-variant-numeric: tabular-nums;
    line-height: 1.1;
    margin-top: 6px;
}

/* Per-card sparkline.
   `viewBox` is 100×32 with `preserveAspectRatio="none"` on the
   element so it stretches edge-to-edge of the card's content
   column. The reserved height keeps the card layout stable while
   the JS is still fetching — without it the value + label would
   reflow when the path landed.

   Color comes from JS at render time (resolved CSS variable
   substituted into the gradient stops + stroke), so dark and
   light modes both pick up the right accent without per-mode
   CSS branches. */
.stat-spark {
    width: 100%;
    height: 32px;
    margin: var(--s-3) 0 var(--s-2) 0;
    display: block;
}

.stat-sub {
    font-size: 12px;
    color: var(--text-3);
    margin-top: 4px;
}

#stats-table-wrap { margin-top: var(--s-5); }

#stats-table-wrap summary {
    cursor: pointer;
    color: var(--text-2);
    font-size: 14px;
    padding: 8px 0;
    user-select: none;
    list-style: none;
    display: inline-flex;
    align-items: center;
    gap: 6px;
    transition: color var(--t-fast) var(--ease);
}

#stats-table-wrap summary::-webkit-details-marker { display: none; }

#stats-table-wrap summary::before {
    content: '▸';
    font-size: 10px;
    color: var(--text-3);
    transition: transform var(--t-fast) var(--ease);
    display: inline-block;
}

#stats-table-wrap[open] summary::before {
    transform: rotate(90deg);
}

#stats-table-wrap summary:hover { color: var(--text); }

#daily-table {
    width: 100%;
    border-collapse: collapse;
    margin-top: var(--s-3);
    font-size: 13px;
}

#daily-table th,
#daily-table td {
    text-align: left;
    padding: 10px 12px;
    border-bottom: 1px solid var(--border);
    font-variant-numeric: tabular-nums;
}

#daily-table th {
    color: var(--text-3);
    font-weight: 500;
    font-size: 12px;
    text-transform: uppercase;
    letter-spacing: 0.05em;
}

#daily-table td { color: var(--text-2); }

#daily-table tbody tr:hover {
    background: var(--surface);
}

.warning {
    background: var(--warn-soft);
    color: var(--warn);
    padding: 10px 14px;
    border-radius: 8px;
    font-size: 13px;
    margin-bottom: var(--s-4);
    border: 1px solid var(--warn-soft);
}

.error {
    color: var(--error);
    font-size: 14px;
    margin: var(--s-3) 0 0;
}

/* =========================================================================
   Footer
   ========================================================================= */
.site-footer {
    border-top: 1px solid var(--border);
    padding: var(--s-7) 0 var(--s-9);
    color: var(--text-3);
    font-size: 13px;
}

.footer-brand {
    display: inline-flex;
    align-items: center;
    gap: var(--s-2);
    font-weight: 600;
    color: var(--text-2);
    margin-bottom: var(--s-4);
}

.footer-brand img {
    border-radius: 6px;
}

.footer-line {
    margin: 0 0 var(--s-2);
    color: var(--text-2);
}

.footer-fine {
    margin: 0;
    color: var(--text-3);
    line-height: 1.55;
    max-width: 520px;
}

/* =========================================================================
   Responsive
   ========================================================================= */
@media (max-width: 720px) {
    :root { --content-pad: 20px; }
    .nav-links { gap: var(--s-4); }
    .hero-content { padding-top: 0; }
    .hero-lead { font-size: 17px; }
    .section { padding-top: clamp(40px, 8vw, 64px); }
}

@media (max-width: 480px) {
    .nav-links { display: none; }
    .stat-value { font-size: 28px; }
}

/* =========================================================================
   Light-mode polish
   ========================================================================= */
@media (prefers-color-scheme: light) {
    .hero-icon {
        box-shadow:
            0 16px 40px -12px rgba(20, 28, 60, 0.18),
            0 2px 4px rgba(20, 28, 60, 0.04);
    }
    .hero-icon-glow {
        background: radial-gradient(circle, var(--accent-soft) 0%, transparent 65%);
        filter: blur(24px);
    }
    .stat-card,
    .prop {
        box-shadow: 0 1px 2px rgba(20, 28, 60, 0.03);
    }
    .pill-dot { box-shadow: 0 0 6px var(--accent-ring); }
}

/* =========================================================================
   Motion / accessibility
   ========================================================================= */
@media (prefers-reduced-motion: reduce) {
    *,
    *::before,
    *::after {
        animation-duration: 0.01ms !important;
        transition-duration: 0.01ms !important;
    }
    html { scroll-behavior: auto; }
}
