/* /tokens.css */
/* TrailGoat — design tokens: the one palette vocabulary (TG-281).
 *
 * Loaded FIRST on every page, before the page's own stylesheet, so every
 * colour on the site traces back here. Page stylesheets (fuel.css, home.css,
 * site_performance.css) no longer carry their own :root palette; the theme
 * files (emerald / alpenglow / aurora / firstlight) override exactly these
 * names under their html.theme-* class, values unchanged.
 *
 * "Forest" — the default dark "trail at dusk" palette — is the plain :root
 * block below. Every token here is either today's value under a new name, or
 * a new step nothing consumes yet: this file is a visual no-op by contract. */

:root {
    /* ---- Ground: four steps, well → page → panel → raised ---- */
    --bg-0: #07120f;          /* deepest pine — the page */
    --bg-1: #0a1a1c;          /* forest navy — panels */
    --bg-well: color-mix(in srgb, var(--bg-0) 80%, #000);   /* inset tracks & wells, below the page */
    --bg-2:    color-mix(in srgb, var(--bg-1) 93%, #fff);   /* raised — --glass-2 flattened over --bg-1 */

    /* ---- Ink: four steps, bright → text → muted → faint ---- */
    --ink: #eaf4ee;           /* near-white text */
    --muted: #8ea7a0;         /* muted sage */
    --ink-bright: color-mix(in srgb, var(--ink) 60%, #fff);          /* headline emphasis */
    --faint:      color-mix(in srgb, var(--muted) 70%, var(--bg-0)); /* hints, placeholders, disabled */

    /* ---- Accent: the one hue on duty for actions and "current" ---- */
    /* Moss (TG-282). Orange is retired from chrome — it survives only as the
       Fueling data tint below. Each theme sets its own green-family accent;
       --accent-ink is the text colour on a solid accent fill (dark ink here,
       ≥12:1 on the forest ground; First Light flips it to paper). */
    --accent: #9ae600;
    --accent-ink: var(--bg-well);
    --selection: color-mix(in srgb, var(--accent) 28%, transparent);

    /* ---- The neutral ladder: every interactive state that is not the accent ----
       Chrome (buttons, toggles, nav links, hovers, menus, toasts, inputs) uses
       ONLY --accent plus these steps of the ink. Fills: idle 4% → hover 8% →
       selected 18%. Edges: a defined border sits at 40%; hover/focus edges and
       focus rings at 25%. Never a feature tint, never a coloured glow. */
    --fill-idle: color-mix(in srgb, var(--ink) 4%, transparent);
    --fill-hover: color-mix(in srgb, var(--ink) 8%, transparent);
    --fill-selected: color-mix(in srgb, var(--ink) 18%, transparent);
    --edge: color-mix(in srgb, var(--ink) 40%, transparent);
    --edge-soft: color-mix(in srgb, var(--ink) 25%, transparent);
    --ring: color-mix(in srgb, var(--ink) 25%, transparent);

    /* ---- Lines & glass ---- */
    --line: rgba(255, 255, 255, 0.09);
    /* Half a hairline, for edges that should be felt rather than seen — the
       code chip below. Derived from --line on purpose: a theme that re-lights
       the line gets a matching faint for free (TG-299). */
    --line-faint: color-mix(in srgb, var(--line) 55%, transparent);
    --glass: rgba(255, 255, 255, 0.045);
    --glass-2: rgba(255, 255, 255, 0.07);

    /* ---- Feature palette: one hue per Run Planner section ----
       Nine sections, nine hues, spread around the wheel so no two cards read
       as the same colour (KRI-119). Every theme overrides all of them; add a
       token here and there. These belong to data-viz and section iconography
       — charts, elevation, map layers, section icons — never to chrome (TG-282):
       not buttons, not hovers, not active/selected states. */
    --c-fuel: #ff7a1a;        /* Fueling */
    --c-pace: #9ae600;        /* Pacing */
    --c-crew: #3ad17f;        /* Crew — meadow */
    --c-course: #19c3b1;      /* Course */
    --c-gear: #8b6cff;        /* Gear */
    --c-weather: #e070ee;     /* Weather — dusk sky */
    --c-climb: #ff5d73;       /* Climbs */
    --c-aid: #36c5f0;         /* Aid stations + descents */
    --gold: #ffd23f;          /* AI coach — matches Capra's wordmark gradient; brand hue, outside the feature set */

    /* ---- Status: the three semantic colours (one status vocabulary) ----
       "Saved", "needs a look", "failed" — the words every status chip, error
       line and toast dot mean. They were five different reds and a handful of
       greens across the stylesheets; now they are three names, set per theme
       like the feature hues. Semantic, not accent: --ok is never the button
       colour and --accent is never "success" (TG-282). Each theme overrides
       all three, and only the state's colour changes — the chip's shape, its
       mono caps and its 1px currentColor edge stay the same everywhere. */
    --ok: #3ad17f;            /* ready, saved, synced */
    --warn: #ffb020;          /* draft, stale, needs a look */
    --bad: #ff5d73;           /* failed, invalid, destructive */

    /* ---- Legacy names: aliases of the feature palette during transition ----
       Every existing stylesheet reads these. They resolve through the theme's
       override of the --c-* token, so a theme only sets the new names. */
    --orange: var(--c-fuel);
    --moss: var(--c-pace);
    --fern: var(--c-crew);
    --teal: var(--c-course);
    --violet: var(--c-gear);
    --plum: var(--c-weather);
    --coral: var(--c-climb);
    --aqua: var(--c-aid);

    /* ---- Corners: two radii for the whole chrome (TG-283) ----
       Depth comes from thin borders and ground steps, not from blur, so the
       corners come down to two steps: --r-ctl for anything you press (nav
       links, buttons, chips, unit toggles, inputs, menu rows) and --r-card for
       the surfaces they sit on (panels, cards, menus, modals, toasts, the
       command palette). Avatars and the theme swatch dots stay round — they
       are pictures, not controls — and so do meters, progress bars and slider
       tracks, which are data. */
    --r-ctl: 6px;
    --r-card: 10px;

    /* ---- Type: three cuts of one family (TG-288) ---- */
    /* The Red Hat superfamily, in three roles: Display for headlines and the
       wordmark, Text for body copy and the chrome, Mono for the topbar, the
       small-caps labels and every figure. TrailGoat is secretly a numbers
       product, so its figures stay set like instrument readings — tabular,
       units tracked out in caps (TG-284) — but the mono now visibly belongs to
       the same family as the prose beside it instead of being a fourth rented
       font. The system fallbacks are deliberate: block fonts.googleapis and the
       page still reads and the columns still line up. */
    --display: 'Red Hat Display', -apple-system, BlinkMacSystemFont, sans-serif;
    --body: 'Red Hat Text', -apple-system, BlinkMacSystemFont, sans-serif;
    --mono: 'Red Hat Mono', ui-monospace, 'SF Mono', Menlo, Consolas, monospace;

    /* ---- The floor under a mono-caps label (TG-301) ----
       Mono caps with letter-spacing is the product's label voice, and it is
       the voice that shrinks first: several of these had drifted to 0.6rem —
       9.6px of tracked-out uppercase, which is under what a label can be read
       at. This is a floor, not a size. A label already above it keeps its own
       size via max(); one below it comes up to meet this. It steps UP on a
       phone, where the screen is closer but smaller and the same type is
       harder, not easier. Decorative marks (the goat's bib, the `i` glyph)
       are not labels and are exempt. */
    --mono-cap: 0.6875rem;                 /* 11px */

    /* ---- Motion: announce the change, then stop (TG-285) ---- */
    /* One easing curve and two durations for the whole product. Motion here
       exists to say something arrived — a menu, a toast, the palette — and
       then it is over. Nothing in the chrome loops forever under a planning
       tool the runner is trying to read. */
    --ease-snap: cubic-bezier(0.23, 1, 0.32, 1);   /* easeOutQuint */
    --dur-ctl: 150ms;                              /* controls */
    --dur-layer: 250ms;                            /* layers that arrive */

    /* ---- You are here: the one two-colour mark (TG-287) ----
       Omarchy's single indulgence, ported: the active window's gradient border.
       Exactly ONE element per view wears it — the section chip in view on the
       planner and Training, the active view tab on /courses. The pair is the
       wordmark's (teal→moss), so each theme's own --teal/--accent makes it
       theirs. The fill is the raised ground: the padding-box layer must be
       opaque, or the ring would bleed through the middle. Never on cards,
       buttons, menus or the topbar — that keeps its flat underline. */
    --here-ring: linear-gradient(45deg, var(--teal), var(--accent));
    --here-fill: var(--bg-2);
    --here-bg: linear-gradient(var(--here-fill), var(--here-fill)) padding-box,
               var(--here-ring) border-box;

    /* ---- Shadow: one modest pop, for floating layers only ---- */
    /* Anything that leaves the page plane — dropdown menus, the command
       palette, toasts, tooltips, popovers — shares this one token. Resting
       surfaces get a 1px --line edge and a step up the ground instead of a
       shadow (TG-283), so nothing on the page floats.
       The rest of the depth system (--raise-*, --well, --ledge-orange) stays in
       fuel.css on purpose: home.css never defined it, and nav.css reads it
       without fallbacks, so hoisting it here would grow shadows on the home
       page's chrome — a visual change that refactor could not make. */
    --shadow-pop: 0 8px 24px rgba(0, 0, 0, 0.45);
}

/* The phone step-up for --mono-cap (TG-301). Overriding the token once beats
   restating a size in every consumer's own media query. */
@media (max-width: 860px) {
    :root { --mono-cap: 0.75rem; }         /* 12px */
}

/* ===== The body face (TG-288) =====
   The default for every page, declared once here because tokens.css loads
   before any page stylesheet. A page sheet that still sets its own body face
   (fuel.css, site_performance.css) loads later and keeps winning — which is
   exactly what holds the unswept tool pages on their old families until the
   sweep issue lands. */
body { font-family: var(--body); }

/* ===== The code voice (TG-299) =====
   `code`, `kbd` and `pre` are the third cut too. Without this rule they fall
   through to the UA's own monospace — a fourth family the type system never
   chose, and the one the `<iframe>` mention on /for-race-directors was still
   set in. The chip treatment is what three page stylesheets were each drawing
   for themselves; declared once here, a `<code>` reads the same wherever it
   appears. `.85em` rather than a fixed size, so it tracks the copy around it. */
code, kbd, pre {
    font-family: var(--mono);
    font-size: 0.85em;
    background: var(--bg-well);
    border: 1px solid var(--line-faint);
    border-radius: 4px;
    padding: 1px 5px;
}

/* ===== Body links (TG-299) =====
   info.css has carried the base `a` rule since TG-220 — but only the info
   pages load info.css, which is why the one prose link on the signed-out
   Creator still rendered in the UA's blue. The rule belongs here, where every
   page already loads it, and it carries the underline a body link should have.

   Scoped to classless anchors on purpose. Every anchor the chrome draws — the
   topbar, the footer, the tool cards, the anchor buttons below — wears a class
   and paints itself; a bare `a` rule would repaint all of them and drag an
   underline through the nav. `a:not([class])` is exactly the prose link this
   is for, and it is why the button classes need no exclusion list of their
   own to stay out of it. */
a:not([class]) {
    color: var(--accent);
    text-decoration: underline;
    text-underline-offset: 3px;
}

/* An anchor that is a button never wears one. `.export-btn` (fuel.css) and
   `.btn` / `.btn-ghost` (home.css) are drawn for `<button>` first, and only
   `.btn` ever reset the UA underline — so the `<a>` form of the others showed
   one on /courses, /discover, /pricing, /404, /about, /crew-sheet and
   /dashboard, wherever a page loaded fuel.css without the local patch
   dashboard.css had been carrying. Reset here, in every state, for every
   page. */
a.export-btn, a.btn, a.btn-ghost,
a.export-btn:hover, a.btn:hover, a.btn-ghost:hover,
a.export-btn:focus, a.btn:focus, a.btn-ghost:focus,
a.export-btn:active, a.btn:active, a.btn-ghost:active {
    text-decoration: none;
}

/* ===== The instrument voice (TG-284) =====
   Two utilities, used site-wide. `.data` is any figure the product computes —
   a pace, a distance, a vert total, a cutoff, a fuel target, an ETA — and the
   tabular figures are what make a column of them line up digit for digit.
   `.data-label` is the small-caps voice that names one: eyebrows, section
   labels, table headers.

   Page stylesheets apply the same two declarations directly to their own value
   classes (`.metric-num`, `.cs-val`, `.plan-table td`, …) rather than every
   emitter growing a class — they read `var(--mono)` at the point of use, which
   is what the token is for. These utilities are for markup that has no class of
   its own to carry it. */
.data {
    font-family: var(--mono);
    font-variant-numeric: tabular-nums;
    font-feature-settings: "tnum" 1;
}
/* The unit rides with the figure but never competes with it: smaller, tracked
   out, in caps, and one weight down (TG-284 AC-5 — hierarchy by weight, not by
   another colour). */
.data-u,
.data .unit,
.data .u {
    font-size: 0.8em;
    font-weight: 500;
    letter-spacing: 0.08em;
    text-transform: uppercase;
}
.data-label {
    font-family: var(--mono);
    font-weight: 500;
    letter-spacing: 0.1em;
    text-transform: uppercase;
}

/* ===== You are here (TG-287) =====
   The double-background technique: a flat fill on the padding box over the
   gradient on the border box, with the border itself transparent so the
   gradient shows through a 1px ring. Static by design — no transition, and
   nothing here for prefers-reduced-motion to suppress. Components whose own
   base rule loads later (`.section-chip`, `.cp-chip`) restate these four
   declarations at the point of use, reading the same tokens. */
.tg-here {
    color: var(--accent);
    border: 1px solid transparent;
    background: var(--here-bg);
    transition: none;
}

/* ===== The one entrance (TG-285) =====
   Every floating layer arrives the same way: a fast fade up from 96%, on
   --ease-snap. Defined here rather than in nav.css so the pages that skip nav
   (the crew sheet, the embed) can use it too. Each consumer sets its own
   transform-origin so the layer grows out of the control that opened it. */
@keyframes tg-pop-in {
    from { opacity: 0; transform: scale(0.96); }
    to   { opacity: 1; transform: none; }
}

/* ===== Touch (2026-09-04) =====
   Two properties, applied once here because tokens.css loads first on every
   page, so every control on the site inherits the behaviour rather than each
   stylesheet remembering it.

   `touch-action: manipulation` tells the browser a tap on a control can never
   be the first half of a double-tap-to-zoom, so it dispatches the click
   immediately instead of waiting to find out. That wait is the "not very
   responsive" feeling on a phone: the control is fine, the browser is just
   holding the event. Only interactive elements are named — the page body keeps
   pinch-zoom, which is an accessibility affordance, not a bug.

   The tap highlight is turned off in favour of each control's own :active
   state: the default is a grey rectangle that ignores border-radius and paints
   over the themed press feedback the components already define. */
a, button, summary, label, select, input, textarea,
[role="button"], [role="tab"], [role="switch"], .tg-tab {
    touch-action: manipulation;
    -webkit-tap-highlight-color: transparent;
}

/* ===== Focus: one ring, every page (2026-09-24) =====
   A keyboard visitor needs to see where they are, and until now that depended
   on which stylesheet a control happened to live in — the topbar drew a ring,
   the Creator, the info pages and most of the dashboard drew nothing but the
   browser default, which is a blue that belongs to no theme. This is the site
   default: a 2px ring in the accent, 2px off the control. `outline` follows
   `border-radius` in every current engine, so a pill gets a pill-shaped ring
   and a card gets a card-shaped one without naming either.

   Wrapped in :where() so it carries ZERO specificity: any control that already
   draws its own focus treatment (an inset ring on a tab, a border-colour swap
   on a text field) keeps it, because any class selector outranks this. It only
   fills the gaps. The main landmark is the skip link's target and takes focus
   programmatically; a ring round the whole page body says nothing. */
:where(a[href], button, input, select, textarea, summary,
       [tabindex]:not([tabindex="-1"]), [contenteditable="true"]):focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}
:where(main, [tabindex="-1"]):focus { outline: none; }

/* ===== Tokyo Night — the default palette, and why it lives HERE =====
   Every other theme is a scoped-override stylesheet that tg-store.js loads on
   demand, which is right for a palette most visitors never see. It is wrong
   for the DEFAULT one: tokens.css is parsed before anything paints, so the
   page comes up in the :root palette below (Forest) and only turns Tokyo Night
   when a second file arrives — a visible flash on every first visit, reported
   2026-09-05.

   A default that costs a request is a default that flashes. So Tokyo Night's
   palette and its handful of literal-colour spots ride in this file, which is
   already on the critical path, and tg-store.js loads no stylesheet for it.
   The class is still applied the same way, so nothing else changes; if Tokyo
   Night ever stops being the default, this block moves back out to its own
   file and the roster gets its `file` back. */
html.theme-tokyonight {
    --bg-0: #1a1b26;          /* tokyo midnight */
    --bg-1: #24283b;          /* storm panels */
    --ink: #c0caf5;           /* neon-sign periwinkle white */
    --muted: #9aa5ce;         /* streetlight slate */
    --accent: #7aa2f7;        /* signal blue — the one accent on duty (TG-282) */
    --c-fuel: #ff9e64;        /* Fueling — lantern orange */
    --gold: #e0af68;          /* AI coach — Capra gradient, brass */
    --c-pace: #7aa2f7;        /* Pacing — signal blue */
    --c-crew: #9ece6a;        /* Crew — vending-machine green */
    --c-course: #0db9d7;      /* Course — neon cyan */
    --c-gear: #bb9af7;        /* Gear — arcade violet */
    /* The scheme's two purples sit 1° apart (KRI-119 would call them the same
       card twice), so Weather takes a pink-shifted dusk neon instead — the
       same separation move Emerald made for --c-aid. */
    --c-weather: #d98ae0;     /* Weather — dusk neon */
    --c-climb: #f7768e;       /* Climbs — vermilion sign */
    --c-aid: #7dcfff;         /* Aid stations + descents — pale sky glass */
    --ok: #9ece6a;            /* Status — vending-machine green */
    --warn: #e0af68;          /* Status — brass */
    --bad: #f7768e;           /* Status — vermilion sign */
}

/* --- Literal-colour spots that don't read the tokens (home.css) --- */
/* .grad / .eyebrow / .btn-primary already read the tokens — signal-blue hero for free. */
/* Backdrop tints → neon cyan + arcade violet over the indigo ground */
html.theme-tokyonight .backdrop {
    background:
        radial-gradient(1200px 600px at 70% -10%, rgba(13, 185, 215, 0.10), transparent 60%),
        radial-gradient(900px 500px at 10% 10%, rgba(187, 154, 247, 0.08), transparent 55%),
        linear-gradient(180deg, var(--bg-0), var(--bg-1));
}
html.theme-tokyonight .aurora {
    background: conic-gradient(from 180deg at 50% 50%, transparent, rgba(122, 162, 247, 0.07), transparent 40%);
}
/* Goat glow → signal blue, and stop the LED colour-cycle (it hardcodes old brand hues) */
html.theme-tokyonight .goat {
    filter: drop-shadow(0 24px 36px rgba(0, 0, 0, 0.55)) drop-shadow(0 0 10px rgba(122, 162, 247, 0.45));
    animation: bob 3.2s ease-in-out infinite;
}
/* Closing CTA band glow → violet neon */
html.theme-tokyonight .cta-band {
    background-image: radial-gradient(600px 200px at 50% -20%, rgba(187, 154, 247, 0.12), transparent 70%);
}
/* Hero route SVG gradient stops → cyan → signal blue → arcade violet */
html.theme-tokyonight #hr-grad stop:nth-of-type(1) { stop-color: #0db9d7; }
html.theme-tokyonight #hr-grad stop:nth-of-type(2) { stop-color: #7aa2f7; }
html.theme-tokyonight #hr-grad stop:nth-of-type(3) { stop-color: #bb9af7; }

/* /home.css */
/* TrailGoat — product home. Dark "trail at dusk" theme, shared palette + the Red Hat cuts.
   The palette lives in tokens.css (TG-281); this block keeps only what is page-local. */
:root {
    --radius: var(--r-card);   /* alias of the shared card corner (TG-283) */
}

* { box-sizing: border-box; margin: 0; padding: 0; }

@media (prefers-reduced-motion: no-preference) {
    /* Smooth only for in-page anchor jumps (the target receives focus);
       programmatic scrolls and scroll restoration stay instant. */
    html:focus-within { scroll-behavior: smooth; }
}

body {
    font-family: var(--body);
    background: var(--bg-0);
    color: var(--ink);
    min-height: 100vh;
    line-height: 1.55;
    -webkit-font-smoothing: antialiased;
    overflow-x: clip;   /* not `hidden`: hidden makes body a scroll container, which eats smooth scrolls */
}

/* ---- Animated backdrop (shared with the tools) ---- */
.backdrop { position: fixed; inset: 0; z-index: -1; overflow: hidden; background:
    radial-gradient(1200px 600px at 70% -10%, rgba(25, 195, 177, 0.10), transparent 60%),
    radial-gradient(900px 500px at 10% 10%, rgba(255, 122, 26, 0.08), transparent 55%),
    linear-gradient(180deg, var(--bg-0), var(--bg-1)); }
.aurora { position: absolute; inset: -40%; opacity: 0.7;
    background: conic-gradient(from 180deg at 50% 50%, transparent, rgba(154,230,0,0.06), transparent 40%); }
.ridge { position: absolute; bottom: 0; left: 0; width: 100%; height: 42vh; }
.ridge path { fill: rgba(255,255,255,0.025); }
.ridge .ridge-mid { fill: rgba(255,255,255,0.035); }
.ridge .ridge-front { fill: rgba(255,255,255,0.05); }

/* ---- Top bar ---- */
.topbar { display: flex; align-items: center; justify-content: space-between; gap: 14px;
    max-width: none; margin: 0; padding: 22px clamp(16px, 2vw, 30px); }
.wordmark { font-family: var(--display); font-weight: 800; font-size: 1.45rem;
    letter-spacing: 0.02em; color: var(--ink); text-decoration: none; }
.grad { background: linear-gradient(100deg, var(--teal), var(--accent));
    -webkit-background-clip: text; background-clip: text; -webkit-text-fill-color: transparent; }
.nav { display: flex; align-items: center; gap: 6px; }
.nav a { color: var(--muted); text-decoration: none; font-weight: 600; font-size: 0.92rem;
    padding: 8px 14px; border-radius: var(--r-ctl); transition: color 0.2s ease, background 0.2s ease; }
.nav a:hover { color: var(--ink); background: var(--glass); }
.topbar .cta { color: var(--accent-ink); background: var(--accent); text-decoration: none; font-weight: 700;
    font-size: 0.92rem; padding: 8px 14px; border-radius: var(--r-ctl); transition: filter 0.2s ease; }
.topbar .cta:hover { filter: brightness(1.08); }
.nav .beta-tag { font-size: 0.62rem; font-weight: 700; color: var(--gold); margin-left: 4px;
    letter-spacing: 0.05em; vertical-align: super; }

/* ---- Layout ---- */
.wrap { max-width: 1140px; margin: 0 auto; padding: 0 24px 90px; }
section { position: relative; }
/* Section rhythm: each block in the wrap opens with its own space, so a kicker
   reads as a new section rather than a caption on the cards above it. The old
   hero card's bottom margin was doing this for the first section only. */
main.wrap > section { margin-top: 96px; }
main.wrap > section:first-child { margin-top: 72px; }

/* ---- Hero ---- */
/* ===== Hero: the Western States flythrough behind the copy =====
   Full-bleed inside the card: the video fills the box, a left-to-right scrim
   keeps the headline legible, the copy sits on top. .hero-still (set by the
   gate script: reduced motion, Save-Data, First Light, autoplay refused)
   hides the video and lets the poster show through. */
.hero { position: relative; overflow: hidden; display: grid; align-items: center;
    /* Fills the first screen under the sticky topbar (its measured height in
       --topbar-h, set by the hero script), never shorter than the copy needs.
       svh so a phone's collapsing browser chrome does not make it jump. */
    min-height: max(560px, calc(100svh - var(--topbar-h, 78px))); margin: 0;
    border-bottom: 1px solid var(--line);
    background: var(--bg-0); }
.hero-still { background: var(--bg-0) url("/media/hero/wser.webp") center / cover no-repeat; }
/* Overshoots the box by a pixel each way: a sub-pixel seam at the bottom edge
   showed a row of the ground through (the noise Kris saw). */
.hero-video { position: absolute; inset: -1px; width: calc(100% + 2px); height: calc(100% + 2px); object-fit: cover; }
.hero-still .hero-video { display: none; }
.hero-scrim { position: absolute; inset: 0; pointer-events: none;
    background: linear-gradient(90deg, color-mix(in srgb, var(--bg-0) 92%, transparent) 0%,
        color-mix(in srgb, var(--bg-0) 66%, transparent) 46%, transparent 100%); }
.hero-inner { position: relative; z-index: 1; width: 100%; max-width: 1140px; margin: 0 auto; padding: 0 24px; box-sizing: border-box; }
.hero-copy { padding: 72px 0; max-width: 660px; }
.hero-cap { position: absolute; right: 24px; bottom: 14px; z-index: 1; text-decoration: none;
    font-family: var(--mono); font-size: 0.72rem; color: var(--muted); letter-spacing: 0.06em;
    text-transform: uppercase; }
.hero-cap:hover { color: var(--ink); }

.eyebrow { display: inline-flex; align-items: center; gap: 8px; color: var(--accent);
    font-family: var(--mono); font-weight: 500; letter-spacing: 0.1em; text-transform: uppercase; font-size: 0.76rem;
    border: 1px solid color-mix(in srgb, var(--accent) 40%, transparent); border-radius: var(--r-ctl); padding: 6px 13px;
    background: color-mix(in srgb, var(--accent) 7%, transparent); margin-bottom: 22px; }
.eyebrow .dot { width: 7px; height: 7px; border-radius: 50%; background: var(--accent); }
.hero h1 { font-family: var(--display); font-weight: 800;
    font-size: clamp(2.4rem, 6vw, 4.1rem); line-height: 1.02; letter-spacing: -0.015em; }
.hero h1 .wiggle { display: inline-block; }
.hero-sub { color: var(--muted); font-size: 1.12rem; max-width: 34rem; margin-top: 20px; }
.hero-cta { display: flex; flex-wrap: wrap; gap: 12px; margin-top: 30px; }
.btn { display: inline-flex; align-items: center; gap: 9px; font-family: var(--display);
    font-weight: 700; font-size: 0.98rem; text-decoration: none; padding: 13px 22px;
    border-radius: var(--r-ctl); transition: transform 0.13s ease, box-shadow 0.13s ease, background 0.2s ease, filter 0.13s ease; }
.btn:hover { transform: translateY(-2px); }
/* The primary CTA — solid accent with dark ink: the home page's one solid
   accent (TG-282), and the site's only "Plan a run" button since the topbar's
   went (TG-290). No gradient, no ledge shadow. */
.btn-primary { color: var(--accent-ink);
    background: var(--accent);
    border: 1px solid var(--accent); }
.btn-primary:hover { transform: translateY(-2px); filter: brightness(1.08); }
.btn-primary:active { transform: translateY(0); }
.btn-ghost { color: var(--ink); border: 1px solid rgba(255,255,255,0.12);
    background: linear-gradient(180deg, rgba(255,255,255,0.09), rgba(255,255,255,0.04));
    box-shadow: inset 0 1px 0 rgba(255,255,255,0.1), 0 2px 5px rgba(0,0,0,0.4),
        0 10px 22px -10px rgba(0,0,0,0.6); }
.btn-ghost:hover { border-color: var(--edge); }
.btn-ghost:active { transform: translateY(1px);
    box-shadow: inset 0 2px 6px rgba(0,0,0,0.45); }
.btn .arrow { transition: transform 0.18s ease; }
.btn:hover .arrow { transform: translateX(3px); }

/* Hero goat accent */
/* ---- Section heading ---- */
.section-head { text-align: center; max-width: 38rem; margin: 0 auto 36px; }
.kicker { color: var(--accent); font-family: var(--mono); font-weight: 500; letter-spacing: 0.1em;
    text-transform: uppercase; font-size: 0.74rem; margin-bottom: 12px; }
.section-head h2 { font-family: var(--display); font-weight: 800; font-size: clamp(1.7rem, 4vw, 2.5rem);
    letter-spacing: -0.015em; }
.section-head p { color: var(--muted); margin-top: 12px; font-size: 1.05rem; }

/* ---- Tool cards ---- */
.tools { display: grid; grid-template-columns: repeat(auto-fit, minmax(232px, 1fr)); gap: 18px; }
.tool { position: relative; display: flex; flex-direction: column; text-decoration: none;
    color: inherit; border: 1px solid var(--line); border-top-color: rgba(255,255,255,0.16);
    background: var(--bg-2);
    border-radius: var(--radius); padding: 26px; overflow: hidden;
    box-shadow: inset 0 1px 0 rgba(255,255,255,0.07);
    transition: transform 0.18s ease, border-color 0.2s ease, background 0.2s ease; }
/* Each card's identity hue is --card-acc (set inline in home.html) — scoped
   iconography, deliberately NOT the global --accent (TG-282, AC-7). The hover
   border is the neutral ladder, not the card's tint. */
.tool::before { content: ""; position: absolute; inset: 0 0 auto 0; height: 4px;
    background: var(--card-acc, var(--accent)); opacity: 0.9; }
.tool:hover { transform: translateY(-5px); border-color: var(--edge);
    background: color-mix(in srgb, var(--bg-2) 92%, var(--ink)); }
.tool-emoji .ico { width: 26px; height: 26px; color: var(--card-acc, var(--accent)); }
.hero-tick .ico { color: var(--accent); vertical-align: -2px; }
.tool-emoji { font-size: 1.9rem; width: 56px; height: 56px; display: grid; place-items: center;
    border-radius: var(--r-card); border: 1px solid rgba(255,255,255,0.1); margin-bottom: 16px;
    background: linear-gradient(180deg, color-mix(in srgb, var(--card-acc, var(--accent)) 20%, transparent),
        color-mix(in srgb, var(--card-acc, var(--accent)) 7%, transparent));
    box-shadow: inset 0 1px 0 rgba(255,255,255,0.12), 0 2px 5px rgba(0,0,0,0.35); }
.tool h3 { font-family: var(--display); font-weight: 700; font-size: 1.25rem;
    display: flex; align-items: center; gap: 8px; }
.tool .pill { font-size: 0.6rem; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase;
    color: var(--gold); border: 1px solid rgba(255,210,63,0.45); border-radius: var(--r-ctl);
    padding: 2px 7px; background: rgba(255,210,63,0.08); }
.tool p { color: var(--muted); font-size: 0.96rem; margin-top: 10px; flex: 1; }
.tool .go { margin-top: 18px; font-family: var(--display); font-weight: 700; font-size: 0.92rem;
    color: var(--card-acc, var(--accent)); display: inline-flex; align-items: center; gap: 7px; }
/* The step number (TG-301). It reads as the card's ordinal, in the card's own
   hue — the same tinted tile the icon wears, one size down. aria-hidden in the
   markup: the heading already says what the step is, and "1 Get a course" read
   aloud is worse than "Get a course". */
.tool-num {
    flex: 0 0 auto; display: inline-grid; place-items: center;
    width: 24px; height: 24px; border-radius: 7px;
    font-family: var(--mono); font-weight: 700; font-size: 0.8rem; line-height: 1;
    color: var(--card-acc, var(--accent));
    background: color-mix(in srgb, var(--card-acc, var(--accent)) 16%, transparent);
    border: 1px solid color-mix(in srgb, var(--card-acc, var(--accent)) 34%, transparent);
}
/* Step 1 is a div, not an anchor — it has two destinations. Its .go stacks
   them instead of sitting on one line. */
.tool-step .go a { color: inherit; text-decoration: none;
    display: inline-flex; align-items: center; gap: 7px; }
.tool-step .go a:hover .arrow { transform: translateX(4px); }
.tool-step .go a .arrow { transition: transform 0.18s ease; }
.tool .go:has(a) { flex-direction: column; align-items: flex-start; gap: 8px; }
.tool:hover .go .arrow { transform: translateX(4px); }
.tool .go .arrow { transition: transform 0.18s ease; }

/* The "How it flows" trail strip lived here. TG-301 merged it into the tool
   cards above — one section, numbered — so its .flow / .step rules went with
   the markup. */

/* ---- Final CTA band ---- */
.cta-band { margin-top: 90px; text-align: center; background: var(--glass);
    border: 1px solid var(--line); border-radius: var(--radius); padding: 52px 28px;
    background-image: radial-gradient(600px 200px at 50% -20%, rgba(255,122,26,0.12), transparent 70%); }
.cta-band h2 { font-family: var(--display); font-weight: 800; font-size: clamp(1.6rem, 4vw, 2.4rem);
    letter-spacing: -0.015em; }
.cta-band p { color: var(--muted); margin: 12px auto 26px; max-width: 32rem; }

/* ---- Footer ---- */
.foot { text-align: center; color: var(--muted); font-size: 0.86rem; padding: 40px 24px 20px;
    border-top: 1px solid var(--line); max-width: 1140px; margin: 70px auto 0; }
.foot a { color: var(--accent); text-decoration: none; }
.foot a:hover { text-decoration: underline; }

/* ---- Reveal on scroll ---- */
/* Reveal-on-scroll is progressive enhancement: content is visible by default,
   and only hides once the script confirms an IntersectionObserver is live
   (html.reveal-armed). No observer → no blank sections, ever. */
.reveal-armed .reveal { opacity: 0; transform: translateY(18px); transition: opacity 0.6s ease, transform 0.6s ease; }
.reveal-armed .reveal.in { opacity: 1; transform: none; }

/* ---- Responsive ---- */
@media (max-width: 860px) {
    .hero { grid-template-columns: 1fr; text-align: center; padding-top: 20px; }
    .hero { min-height: max(520px, calc(100svh - var(--topbar-h, 70px))); }
    main.wrap > section { margin-top: 72px; }
    main.wrap > section:first-child { margin-top: 56px; }
    .hero-copy { padding: 48px 0; margin: 0 auto; }
    /* On a phone the scrim runs top-to-bottom: the copy is centred over the
       video rather than beside it. */
    .hero-scrim { background: linear-gradient(180deg, color-mix(in srgb, var(--bg-0) 88%, transparent) 0%,
        color-mix(in srgb, var(--bg-0) 62%, transparent) 60%, color-mix(in srgb, var(--bg-0) 40%, transparent) 100%); }
    .hero-cap { position: static; display: block; text-align: center; padding: 0 16px 14px; }
    /* Shorter, subtler reveal on touch — fast scrolling shouldn't outrun it. */
    .reveal-armed .reveal { transform: translateY(10px); transition-duration: 0.35s; }
    .eyebrow, .hero-cta { justify-content: center; }
    .hero-sub { margin-left: auto; margin-right: auto; }
    .tools { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 560px) {
    /* Tool cards become compact tap rows: icon · title · arrow. Five 330px
       cards were ~1,700px of near-identical scroll; the tool pages introduce
       themselves, so the descriptions are desktop texture. */
    .tools { grid-template-columns: 1fr; gap: 12px; }
    .tool { display: grid; grid-template-columns: 44px 1fr auto; align-items: center;
        column-gap: 14px; padding: 16px 18px; }
    .tool-emoji { width: 44px; height: 44px; font-size: 1.35rem; margin: 0; }
    .tool-emoji .ico { width: 20px; height: 20px; }
    .tool h3 { margin: 0; font-size: 1.08rem; }
    .tool p { display: none; }
    /* Keep only the arrow of "Open Pacing →" (the row is one big link). */
    .tool .go { margin: 0; font-size: 0; }
    .tool .go .arrow { font-size: 1.15rem; }

    /* ...except the card that ISN'T one big link. Step 1 is a <div> holding
       the catalog link and the Creator link, so the row treatment above left
       it as an icon, a title and two unlabelled arrows — nothing card-wide to
       tap and no way to tell which arrow went where. It keeps its labels: the
       two links stack full-width under the title, each its own 40px row, which
       is the floor AC-6 sets for everything else on this screen. The other
       three cards are anchors and are untouched. */
    .tool:has(.go a) { grid-template-columns: 44px 1fr; }
    .tool:has(.go a) .go { grid-column: 1 / -1; margin-top: 10px;
        font-size: 0.92rem; gap: 0; }
    .tool:has(.go a) .go a { min-height: 40px; }
    .tool:has(.go a) .go .arrow { font-size: inherit; }
}
@media (prefers-reduced-motion: reduce) {
    .reveal-armed .reveal { transition: none; opacity: 1; transform: none; }
}

/* ===== Depth & page transitions (audit second wave) ===== */
/* A whisper of film grain keeps the big dark fields from reading flat.
   Inline SVG turbulence — no request, ~2% opacity. */
.backdrop::after {
    content: ""; position: absolute; inset: 0; pointer-events: none;
    opacity: 0.025;
    background-image: 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.9' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(/%23n)'/%3E%3C/svg%3E");
}

/* Cross-fade between tool pages (same-origin MPA). Free in Chromium/Safari,
   gracefully nothing elsewhere. */
@view-transition { navigation: auto; }
::view-transition-old(root), ::view-transition-new(root) { animation-duration: 0.18s; }
@media (prefers-reduced-motion: reduce) {
    @view-transition { navigation: none; }
}

/* ===== Hero route animation + live ticker ===== */
.hero-tick { margin-top: 14px; color: var(--muted); font-size: 0.92rem; }
.hero-tick a { color: var(--accent); text-decoration: none; }
.hero-tick a:hover { text-decoration: underline; }
/* ===== Home course list (the catalog teaser) ===== */
.hm-courses { }
.hm-course-list {
    list-style: none; margin: 22px 0 0; padding: 0;
    display: grid; gap: 10px;
    grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
}
/* Each card carries an --acc from the server (cycling the section palette) —
   a flat accent edge, never a fill, per the brand rules. */
.hm-course-list li { --acc: var(--teal); }
.hm-course-list a {
    display: flex; flex-direction: column; gap: 3px;
    padding: 12px 14px 12px 13px; text-decoration: none;
    background: var(--glass); border: 1px solid var(--line);
    border-left: 3px solid color-mix(in srgb, var(--acc) 65%, transparent);
    border-radius: var(--radius);
    transition: border-color 0.18s ease, transform 0.18s ease;
}
.hm-course-list a:hover { border-color: var(--acc); border-left-color: var(--acc); transform: translateY(-2px); }
.hm-c-name { color: var(--ink); font-weight: 600; font-size: 15px; line-height: 1.3; }
.hm-course-list small { color: var(--muted); font-size: 12.5px; }
.hm-courses-cta {
    display: flex; flex-wrap: wrap; gap: 12px;
    justify-content: center; margin-top: 26px;
}


/* The one extra line a tool card carries (TG-298): quieter than the body copy
   it follows, so it reads as a footnote rather than a second pitch. */
.tool-note {
    margin: 10px 0 0; color: var(--muted); font-size: 0.86rem;
}

/* ===== In your pocket (TG-298) =====
   The phone is drawn, not photographed: a CSS/SVG composition of the installed
   Run Planner with its tab bar. A screenshot would go stale against the real
   app the first time the chrome moved, and would cost image bytes on the page
   that has to load fastest. Tokens only — one accent, no glow. */
.hm-pocket {
    display: grid; grid-template-columns: 1fr auto; gap: clamp(24px, 5vw, 64px);
    align-items: center; max-width: 1180px; margin: clamp(40px, 7vw, 72px) auto 0;
    padding: 0 20px;
}
.hm-pocket-copy { min-width: 0; }
.hm-pocket-list { list-style: none; margin: 18px 0 24px; padding: 0; display: grid; gap: 12px; }
.hm-pocket-list li { color: var(--muted); line-height: 1.65; }
.hm-pocket-list b { color: var(--ink); }

.hm-phone {
    flex: 0 0 auto; width: 190px; height: 380px; padding: 9px;
    background: var(--bg-2); border: 1px solid var(--line);
    border-radius: 26px;
}
.hm-phone-screen {
    display: flex; flex-direction: column; height: 100%; overflow: hidden;
    background: var(--bg-0); border-radius: 18px;
}
.hm-phone-bar {
    flex: 0 0 auto; padding: 10px 12px 8px; border-bottom: 1px solid var(--line);
}
.hm-phone-mark {
    font-family: var(--display); font-weight: 800; font-size: 0.72rem;
    letter-spacing: 0.06em; color: var(--ink);
}
.hm-phone-mark i { font-style: normal; color: var(--accent); }
.hm-phone-body { flex: 1 1 auto; padding: 12px; display: flex; flex-direction: column; gap: 12px; }
.hm-phone-profile { width: 100%; height: 74px; display: block; }
.hm-phone-fill { fill: color-mix(in srgb, var(--accent) 18%, transparent); }
.hm-phone-line { fill: none; stroke: var(--accent); stroke-width: 2; stroke-linejoin: round; }
.hm-phone-rows { display: grid; gap: 8px; }
.hm-phone-rows span {
    height: 9px; border-radius: 3px; background: var(--fill-idle);
}
.hm-phone-rows span:nth-child(2) { width: 82%; }
.hm-phone-rows span:nth-child(3) { width: 64%; }
.hm-phone-rows span:nth-child(4) { width: 74%; }
.hm-phone-tabs {
    flex: 0 0 auto; display: flex; border-top: 1px solid var(--line);
    padding: 8px 4px 10px;
}
.hm-phone-tabs span {
    flex: 1 1 0; text-align: center; color: var(--muted);
    font-family: var(--mono); font-weight: 500; font-size: 0.5rem;
    letter-spacing: 0.1em; text-transform: uppercase;
}
.hm-phone-tabs .on { color: var(--accent); }

@media (max-width: 860px) {
    /* The phone leads on a phone — you are holding the thing being described.
       Which puts it directly under the crew sheet's button: the section carried
       no top margin at all, so the phone's frame landed flush on that button's
       edge and read as one overlapping the other (TG-327). The margin above is
       what holds them apart; keep it whenever this section moves. */
    .hm-pocket { grid-template-columns: 1fr; justify-items: center; text-align: left; }
    .hm-phone { order: -1; width: 164px; height: 328px; }
}

/* ---- A living hero: the race readout that follows the flythrough's headlamp ----
   It sits on the video, which is dark in every theme (First Light shows the dark
   poster), so the panel keeps its own dark glass and light ink rather than the
   theme's. Climb and descent reuse the Run Planner's orange and blue. */
.hero-read {
    --hr-ink: #eef3f0; --hr-muted: #b4c2bc; --hr-panel: rgba(8, 14, 13, 0.66);
    position: absolute; right: 24px; top: 50%; transform: translateY(-50%);
    width: 344px; margin: 0; display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 1px; border-radius: var(--r-card); overflow: hidden;
    border: 1px solid rgba(255, 255, 255, 0.12); background: rgba(255, 255, 255, 0.08);
    -webkit-backdrop-filter: blur(8px); backdrop-filter: blur(8px);
    box-shadow: 0 12px 32px rgba(0, 0, 0, 0.35);
}
.hero-read > div { background: var(--hr-panel); padding: 10px 12px; display: grid; gap: 2px; min-width: 0; }
.hero-read dt { font: 500 0.66rem/1.2 var(--mono); letter-spacing: 0.12em; text-transform: uppercase; color: var(--hr-muted); }
.hero-read dd { margin: 0; font: 700 1rem/1.2 var(--mono); font-variant-numeric: tabular-nums; color: var(--hr-ink);
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.hero-read dd small { font-size: 0.66em; font-weight: 500; letter-spacing: 0.06em; color: var(--hr-muted); margin-left: 0.3em; text-transform: uppercase; }
.hero-read .hr-wide { grid-column: 1 / -1; }
.hero-read .hr-head { grid-column: 1 / -1; grid-template-columns: 1fr auto; align-items: baseline; gap: 8px; }
.hero-read .hr-head dt { color: var(--hr-ink); font-weight: 700; white-space: nowrap; }
.hero-read .hr-head dd { font: 500 0.66rem/1.2 var(--mono); letter-spacing: 0.08em; text-transform: uppercase; color: var(--hr-muted);
    display: inline-flex; align-items: center; gap: 6px; }
.hero-read .hr-head dd::before { content: ""; width: 7px; height: 7px; border-radius: 50%; background: #ffd23f;
    box-shadow: 0 0 8px #ffd23f; }
.hero-read .up { color: #ff9a55; }
.hero-read .down { color: #6fd0ff; }
.hero-read .fin { color: #a8e05f; }
.hero-read > div.flash { animation: hr-flash 1.1s cubic-bezier(0.23, 1, 0.32, 1); }
@keyframes hr-flash { from { background: rgba(111, 208, 255, 0.34); } to { background: var(--hr-panel); } }
.hero-still .hero-read .hr-head dd::before { box-shadow: none; }
/* .hero-inner is already the centred 1140px column, so right: 24px lines the panel up
   with the page grid at every width; an extra viewport offset pushed it over the copy. */
@media (max-width: 1120px) {
    /* No room beside the copy: the readout becomes a strip along the hero's foot —
       mile, height and grade on top, next aid and the clock below; carbs drop out. */
    .hero-read { position: static; transform: none; width: 100%; max-width: 660px; margin: -40px 0 56px;
        grid-template-columns: repeat(6, minmax(0, 1fr)); }
    .hero-read > div { grid-column: span 2; }
    .hero-read .hr-head { grid-column: span 6; }
    .hero-read .hr-wide { grid-column: span 3; }
    .hero-read .hr-carbs { display: none; }
}
@media (max-width: 860px) {
    .hero-read { margin: 4px auto 24px; text-align: left; }   /* below the race caption, never over it */
    .hero-read .hr-head { display: none; }      /* the caption just above already names the race */
}
@media (max-width: 560px) {
    .hero-read > div { padding: 8px 10px; }
    .hero-read .hr-head dd { display: none; }
    .hero-read dd { font-size: 0.92rem; }
    .hero-read .hr-wide dd { white-space: normal; }
}
@media (prefers-reduced-motion: reduce) { .hero-read > div.flash { animation: none; } }

/* ---- The Leadville story, scrolled (home-motion.js) ----
   The canvas sticks while six steps scroll past; the scroll position is the
   timeline. Desktop: canvas left, steps right. Phone: the canvas pins to the
   top and the step cards scroll up over its lower edge. */
.plan-story { position: relative; display: grid; grid-template-columns: minmax(0, 1.6fr) minmax(0, 1fr); gap: 0 28px; margin: 0 0 32px; }
.plan-story.ps-nogl { display: none; }
.ps-sticky { position: sticky; top: max(84px, calc(50vh - 270px)); height: min(540px, calc(100vh - 110px)); align-self: start;
    border: 1px solid var(--line); border-radius: var(--r-card); background: var(--bg-1); overflow: hidden; }
.ps-sticky canvas { position: absolute; inset: 0; width: 100%; height: 100%; display: block; touch-action: pan-y; }
.ps-labels { position: absolute; inset: 0; pointer-events: none; transition: opacity 220ms ease; }
.ps-steps { list-style: none; margin: 0; padding: 16vh 0 30vh; display: grid; }
.ps-step { min-height: 70vh; display: grid; align-content: center; }
.ps-card { display: grid; gap: 8px; padding: 16px 18px; border: 1px solid var(--line); border-radius: var(--r-card); background: var(--bg-1);
    opacity: 0.5; transition: opacity 250ms ease, border-color 250ms ease; }
.ps-step.on .ps-card { opacity: 1; border-color: var(--accent); }
.ps-k { font: 500 0.7rem/1.3 var(--mono); letter-spacing: 0.1em; text-transform: uppercase; color: var(--muted); }
.ps-card p { margin: 0; color: var(--ink); font-size: 0.98rem; line-height: 1.55; }
.ps-card p b { color: var(--ink-bright, var(--ink)); }
.ps-stat { font: 600 0.76rem/1.4 var(--mono); color: var(--accent); font-variant-numeric: tabular-nums; }
@media (max-width: 860px) {
    .plan-story { grid-template-columns: minmax(0, 1fr); }
    .ps-sticky, .ps-steps { grid-area: 1 / 1; }
    .ps-sticky { top: calc(var(--topbar-h, 64px) + 8px); height: 56vh; }
    .ps-steps { position: relative; z-index: 1; padding: 46vh 0 10vh; pointer-events: none; }
    .ps-step { min-height: 86vh; align-content: end; }
    .ps-card { pointer-events: auto; opacity: 1; background: color-mix(in srgb, var(--bg-1) 90%, transparent);
        -webkit-backdrop-filter: blur(8px); backdrop-filter: blur(8px); }
}
.ps-lab { position: absolute; transform: translate(-50%, -100%); font: 600 0.72rem/1.2 var(--mono); letter-spacing: 0.03em;
    color: var(--ink); white-space: nowrap; }
.ps-lab.left { transform: translate(0, -50%); }
.ps-lab.right { transform: translate(-100%, -50%); }
.ps-lab.peak { color: var(--ink-bright, var(--ink)); }
.ps-lab.axis { transform: translate(-50%, 0); color: var(--faint); font-weight: 500; }
.ps-lab.g { color: var(--c-fuel); font-weight: 700; font-size: 0.68rem; }
.ps-lab.col { transform: translate(-100%, 0) rotate(-35deg); transform-origin: 100% 0; color: var(--muted); font-size: 0.64rem; font-weight: 500; }
.ps-lab.up { color: var(--c-fuel); }
.ps-lab.down { color: var(--c-aid); }
.ps-lab.legend { display: inline-flex; gap: 6px; align-items: center; transform: translate(-100%, 0); color: var(--muted);
    background: color-mix(in srgb, var(--bg-1) 80%, transparent); padding: 4px 8px; border-radius: 4px; }
.ps-lab .sw { display: inline-block; width: 8px; height: 8px; border-radius: 50%; margin-left: 6px; }
.ps-lab .sw.gold { background: var(--gold); }
.ps-lab .sw.gear { background: var(--c-gear); }
.ps-lab .sw.day { background: color-mix(in srgb, var(--ink) 40%, var(--bg-1)); }

/* ---- Signal drop: the phone's status bar, cards and the no-signal toast ---- */
.hm-phone-screen { position: relative; }
.hm-phone-status { flex: 0 0 auto; display: flex; justify-content: space-between; align-items: center; padding: 8px 12px 2px;
    font: 700 0.56rem/1 var(--mono); color: var(--ink); }
.ph-bars { display: inline-flex; align-items: flex-end; gap: 1.5px; height: 8px; margin-right: 4px; vertical-align: -1px; }
.ph-bars i { width: 2.5px; background: var(--ink); border-radius: 1px; transition: opacity 250ms ease; }
.ph-bars i:nth-child(1) { height: 30%; } .ph-bars i:nth-child(2) { height: 55%; } .ph-bars i:nth-child(3) { height: 78%; } .ph-bars i:nth-child(4) { height: 100%; }
.ph-bars i.off { opacity: 0.2; }
.ph-net { font-weight: 500; color: var(--muted); }
.hm-phone .hm-phone-body { gap: 8px; padding: 10px; }
.ph-race { margin: 0; font: 800 0.72rem/1.2 var(--display); color: var(--ink); }
.ph-card { display: grid; gap: 2px; padding: 7px 8px; border: 1px solid var(--line); border-radius: 8px; background: var(--bg-1); }
.ph-k { font: 500 0.46rem/1.2 var(--mono); letter-spacing: 0.1em; text-transform: uppercase; color: var(--muted); }
.ph-card b { font: 700 0.72rem/1.2 var(--mono); color: var(--ink); }
.ph-meta { font: 500 0.5rem/1.2 var(--mono); color: var(--muted); font-variant-numeric: tabular-nums; }
.ph-card .hm-phone-profile { height: 44px; }
.ph-dot { fill: var(--ink); stroke: var(--bg-1); stroke-width: 1.5; }
.ph-saved { display: inline-flex; gap: 4px; align-items: center; font: 600 0.5rem/1.2 var(--mono); color: var(--ok, var(--accent)); }
.ph-toast { position: absolute; left: 8px; right: 8px; bottom: 40px; padding: 8px 9px; border-radius: 8px; background: var(--bg-2);
    border: 1px solid var(--edge); font-size: 0.56rem; line-height: 1.35; color: var(--muted); box-shadow: var(--shadow-pop);
    transform: translateY(24px); opacity: 0; transition: transform 450ms cubic-bezier(0.23, 1, 0.32, 1), opacity 300ms ease; }
.ph-toast b { display: block; color: var(--ink); font-size: 0.6rem; }
.ph-toast.show { transform: none; opacity: 1; }
@media (max-width: 860px) { .hm-phone { height: 360px; } }
@media (prefers-reduced-motion: reduce) { .ph-toast { transition: none; } }

/* ---- Sand signatures on the featured course cards ---- */
.hm-sand { display: block; width: 100%; height: 34px; margin: 0 0 8px; }

/* ---- Ridgeline: the catalog stacked like the Unknown Pleasures cover ---- */
.hm-ridge { margin: 4px 0 28px; }
.hm-ridge-plot { position: relative; padding: 8px 12px 14px; background: var(--bg-0); border: 1px solid var(--line); border-radius: var(--r-card); }   /* the lines hide each other with bg-0 fills, so the plot sits on its own bg-0 sleeve */
.hm-ridge-plot svg { display: block; width: 100%; height: auto; }
.hm-ridge .rl .f { fill: var(--bg-0); }
.hm-ridge .rl .l { fill: none; stroke: var(--ink); stroke-width: 1.2; opacity: 0.8; stroke-dasharray: 1; stroke-dashoffset: 0;
    transition: stroke-dashoffset 1.1s cubic-bezier(0.23, 1, 0.32, 1) var(--d), stroke 150ms, opacity 150ms; }
.hm-ridge-plot.primed .rl .l { stroke-dashoffset: 1; transition: none; }
.hm-ridge .rl.on .l { stroke: var(--accent); stroke-width: 2.2; opacity: 1; }
.hm-ridge .rl:focus-visible .l { stroke: var(--accent); stroke-width: 2.2; opacity: 1; }
.hm-ridge-tip { position: absolute; transform: translate(-50%, -100%); display: grid; gap: 2px; padding: 6px 9px; border-radius: 6px;
    background: var(--bg-2); border: 1px solid var(--line); box-shadow: var(--shadow-pop); pointer-events: none; white-space: nowrap; }
.hm-ridge-tip[hidden] { display: none; }   /* its own display: grid would otherwise beat the hidden attribute */
.hm-ridge-tip b { font: 700 0.85rem var(--display); color: var(--ink); }
.hm-ridge-tip span { font: 500 0.72rem var(--mono); color: var(--muted); }
.hm-ridge figcaption { margin-top: 10px; text-align: center; color: var(--muted); font-size: 0.86rem; }
@media (prefers-reduced-motion: reduce) { .hm-ridge .rl .l { transition: none; } }

/* iOS draws its own play button over a video it won't autoplay (Low Power Mode);
   the hero falls back to the poster then, so the glyph must never show. */
.hero-video::-webkit-media-controls-start-playback-button { display: none !important; -webkit-appearance: none; }
.hero-video::-webkit-media-controls { display: none !important; }

/* ---- Closing band: copy beside dots with momentum on Western States ----
   Two columns when the field is on; the band stays centred copy without it. */
.cta-band:has(.cta-field:not([hidden])) { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1.1fr);
    gap: 12px 28px; align-items: center; text-align: left; padding: 40px 36px; }
.cta-band:has(.cta-field:not([hidden])) p { margin-left: 0; }
.cta-tick { margin-top: 18px; }
.cta-field { position: relative; margin: 0; height: 340px; }
.cta-field[hidden] { display: none; }
.cta-field canvas { position: absolute; inset: 0; width: 100%; height: 100%; display: block; cursor: crosshair; touch-action: pan-y; }
.cta-field figcaption { position: absolute; left: 8px; bottom: 0; font: 500 0.7rem/1.3 var(--mono); letter-spacing: 0.06em;
    text-transform: uppercase; color: var(--muted); pointer-events: none; }
@media (max-width: 860px) {
    .cta-band:has(.cta-field:not([hidden])) { grid-template-columns: minmax(0, 1fr); text-align: center; padding: 32px 20px; }
    .cta-band:has(.cta-field:not([hidden])) p { margin-left: auto; }
    .cta-field { height: 260px; order: -1; }
    .cta-field figcaption { left: 0; right: 0; text-align: center; }
}

/* /nav.css */
/* ===== Shared site navigation (all pages) =====
   Desktop (≥861px): the existing inline pill nav, unchanged — this file only adds
   the active-page state. Mobile (≤860px): the topbar becomes a compact sticky bar
   with a hamburger; the nav links + unit toggles collapse into a dropdown panel
   (.topbar-tools) opened by nav.js. Uses the page tokens (--bg-*, --ink, --line…)
   defined by each page's own stylesheet, so it must load AFTER it. */

/* --- Inline stroke icons (shared, every page) ---
   Duplicate of the fuel.css base so pages that don't load fuel.css
   (landing, stats) still render them. Icons inherit the text color. */
.ico { width: 15px; height: 15px; flex: 0 0 auto; vertical-align: -0.18em; }
.ico path, .ico circle, .ico rect, .ico line, .ico polyline { fill: none; stroke: currentColor; stroke-width: 1.75;
    stroke-linecap: round; stroke-linejoin: round; }
.nav-ico .ico { width: 13px; height: 13px; }
.nav-search .ico, .nav-gear .ico { width: 16px; height: 16px; }

/* --- Active page (all widths) — set by nav.js via aria-current --- */
/* One accent on duty (TG-282): the current page wears --accent on a ladder
   fill. No per-tool hue, no glow. */
.nav a[aria-current="page"] {
    color: var(--accent);
    background: var(--fill-selected);
}

/* --- Dropdown groups (Option A, 2026-08): Courses ▾ / Why TrailGoat ▾ --- */
.nav-group { position: relative; display: inline-flex; }
.nav-caret { display: inline-flex; margin-left: 5px; opacity: 0.65; transition: transform 0.15s ease; }
.nav-caret svg { width: 10px; height: 10px; fill: none; stroke: currentColor;
    stroke-width: 2.4; stroke-linecap: round; stroke-linejoin: round; }
.nav-group-btn[aria-expanded="true"] .nav-caret { transform: rotate(180deg); }
/* The group wears a quiet glow when the current page lives inside it. */
.nav .nav-group-here { color: var(--ink); background: var(--glass, rgba(255,255,255,0.045)); }

@media (min-width: 861px) {
    .nav-group-menu {
        position: absolute; top: calc(100% + 6px); left: 0; z-index: 2100;
        min-width: 220px; padding: 6px;
        background: color-mix(in srgb, var(--bg-1, #0a1a1c) 96%, #ffffff 4%);
        border: 1px solid var(--line); border-radius: var(--r-card);
        box-shadow: var(--shadow-pop);
        transform-origin: top left;
        animation: tg-pop-in var(--dur-layer) var(--ease-snap);
    }
    .nav .nav-group-menu a { display: flex; align-items: center; width: 100%;
        padding: 9px 12px; border-radius: var(--r-ctl); }
    .nav .nav-group-menu a small { margin-left: 6px; color: var(--muted); font-weight: 500; }
}
@media (max-width: 860px) {
    /* In the hamburger, groups flatten into labeled sections: the trigger is a
       normal row (it navigates on mobile) and its children sit indented under
       it, always visible. display:contents beats the [hidden] UA rule, which
       is exactly what we want here. */
    .nav-group, .nav-group-menu, .nav-group-menu[hidden] { display: contents; }
    .nav-caret { display: none; }
    /* `.topbar .topbar-tools …`, not `.topbar-tools …`: the canonical
       `.topbar .nav a` block further down ties a two-class selector on
       specificity and wins by coming later, which turned these rows into
       centred pills. One more class here and the panel rows win outright. */
    .topbar .topbar-tools .nav .nav-group-menu a { padding-left: 36px; min-height: 42px; font-size: 0.95rem; }
    .topbar .topbar-tools .nav a small { margin-left: 6px; color: var(--muted); font-weight: 500; }
}

/* --- Hamburger button (hidden on desktop) --- */
.nav-burger { display: none; }


@media (max-width: 860px) {
    /* Compact sticky bar — the nav is reachable from anywhere on 4,000px+ pages. */
    .topbar {
        position: sticky; top: 0; z-index: 2000;
        flex-wrap: nowrap;
        padding-top: 10px; padding-bottom: 10px;
        background: color-mix(in srgb, var(--bg-0, #07120f) 86%, transparent);
        -webkit-backdrop-filter: blur(14px);
        backdrop-filter: blur(14px);
        border-bottom: 1px solid var(--line);
    }

    /* No blur support (old WebViews, battery savers) → solid bars, still legible. */
    @supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
        .topbar, .section-chips { background: var(--bg-0, #07120f); }
    }

    .nav-burger {
        display: inline-flex; align-items: center; justify-content: center;
        order: 10;                      /* far right, after any topbar CTA */
        flex: 0 0 auto;
        width: 44px; height: 44px;      /* full-size touch target */
        margin: -4px 0;                 /* …without growing the bar */
        background: transparent; border: none; border-radius: var(--r-ctl);
        cursor: pointer; color: var(--ink);
    }
    .nav-burger:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

    /* The three lines → X morph */
    .nav-burger-box { position: relative; width: 20px; height: 14px; display: block; }
    .nav-burger-box span {
        position: absolute; left: 0; width: 20px; height: 2px; border-radius: 2px;
        background: currentColor; transition: transform 0.2s ease, opacity 0.15s ease, top 0.2s ease;
    }
    .nav-burger-box span:nth-child(1) { top: 0; }
    .nav-burger-box span:nth-child(2) { top: 6px; }
    .nav-burger-box span:nth-child(3) { top: 12px; }
    .menu-open .nav-burger-box span:nth-child(1) { top: 6px; transform: rotate(45deg); }
    .menu-open .nav-burger-box span:nth-child(2) { opacity: 0; }
    .menu-open .nav-burger-box span:nth-child(3) { top: 6px; transform: rotate(-45deg); }

    /* The mascot hugs the wordmark; the auto margin absorbs the slack so
       space-between can't center him between wordmark and burger. */
    .topbar-mascot { display: inline-flex; margin-right: auto; }

    /* Landing-page CTA stays visible in the bar, compacted. */

    /* --- Dropdown panel --- */
    .topbar-tools {
        display: none;
        position: absolute; top: calc(100% + 8px); left: 12px; right: 12px;
        flex-direction: column; align-items: stretch; gap: 4px;
        /* fuel.css's .toggles sets flex-wrap:wrap AND justify-content:flex-end.
           In a height-capped COLUMN that wrapped overflow into a clipped second
           column, and packed items to the bottom so anything that didn't fit
           spilled off the TOP where no scrollbar can reach it. One column,
           packed from the top, scrolled. */
        flex-wrap: nowrap; justify-content: flex-start;
        padding: 8px;
        background: color-mix(in srgb, var(--bg-1, #0a1a1c) 96%, #ffffff 4%);
        border: 1px solid var(--line); border-radius: var(--r-card);
        box-shadow: var(--shadow-pop);
        max-height: calc(100dvh - 90px); overflow-y: auto;
    }
    .topbar.menu-open .topbar-tools {
        display: flex; transform-origin: top center;
        animation: tg-pop-in var(--dur-layer) var(--ease-snap);
    }

    /* Links become full-width touch rows. (Three classes — see the note on the
       group rows above for why two were not enough.) */
    .topbar .topbar-tools .nav { display: flex; flex-direction: column; align-items: stretch; flex-wrap: nowrap; gap: 2px; }
    .topbar .topbar-tools .nav a {
        display: flex; align-items: center; min-height: 46px;
        padding: 10px 14px; border-radius: var(--r-ctl);
        font-size: 1rem; font-weight: 600;
    }
    .topbar .topbar-tools .nav a[aria-current="page"] { color: var(--accent); }
    .topbar .topbar-tools .nav a:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }

    /* Unit toggles ride along in the panel as full-width segmented controls
       (they're inside .topbar-tools on the pages that have them). */
    .topbar-tools .unit-toggle { display: flex; width: 100%; }
    .topbar-tools .unit-toggle button { flex: 1 1 0; padding-top: 10px; padding-bottom: 10px; }
    .topbar-tools .unit-toggle:first-of-type {
        margin-top: 6px; border-radius: var(--r-ctl);
    }
    .topbar-tools .unit-toggle:first-of-type::before { display: none; }
    .topbar-tools .unit-toggle + .unit-toggle { border-radius: var(--r-ctl); }

    /* Dim + block the page behind the open menu; tapping it closes (nav.js). */
    body.nav-open::after {
        content: ""; position: fixed; inset: 0; z-index: 1900;
        background: rgba(4, 10, 8, 0.45);
    }
}

@media (prefers-reduced-motion: reduce) {
    .topbar.menu-open .topbar-tools { animation: none; }
    .nav-burger-box span { transition: none; }
}

/* ===== Section-jump chips (built by nav.js on the long tool pages) =====
   Pill styles are shared; each breakpoint defines its own ROW. Narrow: sticky
   under the topbar (the page scrolls). Desktop split pages: a quiet static row
   between topbar and panes (the RIGHT PANE scrolls — audit F5: a 6,000px pane
   had no navigation at all on desktop). */
.section-chips { display: none; }
.section-chip {
    flex: 0 0 auto; white-space: nowrap;
    min-height: 36px; padding: 6px 14px;
    background: var(--fill-idle);
    color: var(--muted);
    border: 1px solid var(--line);
    border-radius: var(--r-ctl);
    font: inherit; font-family: var(--body); font-size: 0.82rem; font-weight: 600;
    cursor: pointer;
    transition: box-shadow 0.25s ease, background 0.25s ease, border-color 0.25s ease;
}
/* Hover steps the fill. The section in view wears the one "you are here"
   mark (TG-287): the teal→moss ring from tokens.css, restated here because
   this base rule loads after tokens.css and would otherwise win the fill.
   nav.js stamps .tg-here alongside aria-current; nothing reads --chip-acc. */
.section-chip:hover { color: var(--ink); background: var(--fill-hover); border-color: var(--edge-soft); }
.section-chip[aria-current], .section-chip.tg-here {
    color: var(--accent);
    border-color: transparent;
    background: var(--here-bg);
    transition: none;
}
.section-chip:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.section-chips::-webkit-scrollbar { display: none; }
@media (max-width: 860px) {
    /* A thumb needs 40px of height; the chips stood at 36 (TG-301). The extra
       comes out of padding — the label size is what makes the row readable
       while scrolling past it, so it does not move. */
    .section-chip { min-height: 40px; }
    .section-chips {
        position: sticky; z-index: 1890;   /* under the topbar (2000) and the menu scrim (1900) */
        top: 63px;                          /* nav.js keeps this synced to the real topbar height */
        display: flex; gap: 8px;
        padding: 8px 16px 10px;
        overflow-x: auto;
        -webkit-overflow-scrolling: touch;
        scrollbar-width: none;
        background: color-mix(in srgb, var(--bg-0, #07120f) 86%, transparent);
        -webkit-backdrop-filter: blur(14px);
        backdrop-filter: blur(14px);
        border-bottom: 1px solid var(--line);
    }
    .section-chips[hidden] { display: none; }
    /* Scrollability affordance (KRI-58): the clipped edge fades to nothing —
       a half-faded chip is the "more this way" signal. Mask-only, so the row's
       background and blur fade with the chips instead of leaving a hard seam.
       nav.js toggles the classes from scroll position. */
    .section-chips.chips-more-right {
        -webkit-mask-image: linear-gradient(90deg, #000 calc(100% - 44px), transparent);
        mask-image: linear-gradient(90deg, #000 calc(100% - 44px), transparent);
    }
    .section-chips.chips-more-left {
        -webkit-mask-image: linear-gradient(270deg, #000 calc(100% - 44px), transparent);
        mask-image: linear-gradient(270deg, #000 calc(100% - 44px), transparent);
    }
    .section-chips.chips-more-left.chips-more-right {
        -webkit-mask-image: linear-gradient(90deg, transparent, #000 44px, #000 calc(100% - 44px), transparent);
        mask-image: linear-gradient(90deg, transparent, #000 44px, #000 calc(100% - 44px), transparent);
    }
}
@media (min-width: 861px) {
    /* Inside the right pane (nav.js moves it there): a sticky floating toolbar
       pinned to the top of the column it controls. Centered, and it WRAPS —
       never clips — when the pane is narrow. z-index clears the scroll fade (6). */
    body.split-page .split-right > .section-chips {
        display: flex; flex-wrap: wrap; justify-content: center; gap: 7px;
        position: sticky; top: 0; z-index: 7;
        margin: 0; padding: 10px 12px;
        background: var(--bg-0, #07120f);
        border: 1px solid var(--line); border-radius: var(--r-card);
    }
    /* An explicit display beats the [hidden] UA rule — restate it (known trap). */
    body.split-page .split-right > .section-chips[hidden] { display: none; }
    body.split-page .split-right > .section-chips .section-chip { padding: 6px 10px; font-size: 0.8rem; }
    /* Auto-hide, in lockstep with the plan-action bar (one timer in raceday.js
       toggles .rd-actions-hidden on both). Opacity-only fade: the sticky slot
       stays in flow so chip-click / scrollspy offsets that use row.offsetHeight
       don't shift. Slides UP — it's a top bar (the plan bar slides down). */
    body.split-page .split-right > .section-chips {
        transition: opacity 0.28s ease, transform 0.28s ease;
    }
    body.split-page .split-right > .section-chips.rd-actions-hidden {
        opacity: 0; transform: translateY(-10px); pointer-events: none;
    }
}
@media (prefers-reduced-motion: reduce) {
    body.split-page .split-right > .section-chips.rd-actions-hidden { transform: none; }
}

/* ===== Topbar mascot — the goat mascot trotting beside the wordmark =====
   The landing hero's goat, miniaturised: same LED palette-cycle glow hugging
   his silhouette, a small bob, and the poof trailing behind him (he runs
   right, so the dust kicks off to his left — `order` puts it there without
   touching the template). */
/* Mobile-only: on desktop the wordmark stands alone. */
.topbar-mascot { display: none; align-items: center; gap: 1px; margin-left: 8px; flex: 0 0 auto; }
@media (max-width: 860px) { .topbar-mascot { display: inline-flex; } }
.topbar-goat {
    height: 32px; width: auto; display: block;
    animation: tg-mini-bob 3.2s ease-in-out infinite;
}
/* The palette cycle is a moment now, not a 22s loop (TG-285): nav.js adds
   .goat-led for one 2.2s pass through the brand hues when the page settles, a
   plan is saved, or a tour step advances. Same react() plumbing as the sprint
   and the hop; the idle bob keeps running underneath. */
.topbar-mascot.goat-led .topbar-goat {
    animation: tg-mini-bob 3.2s ease-in-out infinite, tg-mini-led 2.2s linear 1;
}
/* An SVG puff, not the emoji it used to be: the brand rule is SVG icons only,
   and the mascot <img> beside it is the single exception (KRI-200). The
   keyframes drive opacity/transform, so they work on an <svg> unchanged. */
.topbar-dust { width: 14px; height: 14px; fill: var(--muted); flex: 0 0 auto; order: -1;
    animation: tg-mini-dust 1.6s ease-in-out infinite; }
@keyframes tg-mini-bob {
    0%, 100% { transform: translateY(0) rotate(-1deg); }
    50%      { transform: translateY(-2px) rotate(1deg); }
}
@keyframes tg-mini-led {
    0%, 100% { filter: drop-shadow(0 0 2px rgba(255,122,26,0.55)) drop-shadow(0 0 7px rgba(255,122,26,0.4)); }
    16.6%    { filter: drop-shadow(0 0 2px rgba(255,210,63,0.55)) drop-shadow(0 0 7px rgba(255,210,63,0.4)); }
    33.3%    { filter: drop-shadow(0 0 2px rgba(154,230,0,0.55)) drop-shadow(0 0 7px rgba(154,230,0,0.4)); }
    50%      { filter: drop-shadow(0 0 2px rgba(25,195,177,0.55)) drop-shadow(0 0 7px rgba(25,195,177,0.4)); }
    66.6%    { filter: drop-shadow(0 0 2px rgba(139,108,255,0.55)) drop-shadow(0 0 7px rgba(139,108,255,0.4)); }
    83.3%    { filter: drop-shadow(0 0 2px rgba(255,93,115,0.55)) drop-shadow(0 0 7px rgba(255,93,115,0.4)); }
}
@keyframes tg-mini-dust {
    0%   { opacity: 0; transform: translateX(3px) scale(0.8); }
    40%  { opacity: 0.85; }
    100% { opacity: 0; transform: translateX(-10px) scale(1.1); }
}
@media (prefers-reduced-motion: reduce) {
    .topbar-goat, .topbar-dust { animation: none; }
}

/* ===== Status chips: one vocabulary, five states =====
   Draft · Ready · Shared · Live · Finished, on the three status tokens plus
   two feature hues the state already owns. The chip is the label voice —
   mono caps, tracked out — with a 1px edge in currentColor, so a state is
   one class and one colour: `<span class="tg-status is-ready">ready</span>`.
   Anything that says "saved", "draft" or "failed" should wear this rather
   than invent its own tint. Same shape in every theme; only the ink moves. */
.tg-status {
    display: inline-flex; align-items: center; gap: 5px;
    font-family: var(--mono); font-size: var(--mono-cap); font-weight: 600;
    letter-spacing: 0.08em; text-transform: uppercase; line-height: 1;
    padding: 3px 7px; border-radius: var(--r-ctl);
    border: 1px solid currentColor; color: var(--muted);
    white-space: nowrap; vertical-align: middle;
}
.tg-status.is-draft    { color: var(--muted); }
.tg-status.is-ready    { color: var(--ok); }
.tg-status.is-shared   { color: var(--c-aid); }
.tg-status.is-live     { color: var(--c-crew); }
.tg-status.is-finished { color: var(--gold); }
/* The generic three, for a chip that reports an outcome rather than a stage. */
.tg-status.is-ok       { color: var(--ok); }
.tg-status.is-warn     { color: var(--warn); }
.tg-status.is-bad      { color: var(--bad); }
/* Live carries a dot that breathes — the one status allowed to move, because
   it reports a clock that is running. Stops under reduced motion. */
.tg-status.is-live::before {
    content: ""; width: 6px; height: 6px; border-radius: 50%;
    background: currentColor; animation: tg-status-pulse 2s ease-in-out infinite;
}
@keyframes tg-status-pulse { 50% { opacity: 0.35; } }
@media (prefers-reduced-motion: reduce) { .tg-status.is-live::before { animation: none; } }

/* ===== Toasts (TrailGoatToast — nav.js, and tg-toast.js on pages without nav) =====
   Bottom-centre, not the corner. On a phone the corner IS the centre, so the
   two layouts only differ on a desktop, and there the centre is where the eye
   already is after pressing a control in the page body — the corner sat
   under a scrollbar and next to nothing. Nothing else on the site pins to
   the bottom edge except the installed app's tab bar, which the stack lifts
   itself above. Newest toast at the bottom, closest to the hand. */
.tg-toast-stack {
    position: fixed; left: 50%; bottom: max(18px, env(safe-area-inset-bottom, 0px));
    transform: translateX(-50%); z-index: 4000;
    width: max-content; max-width: min(calc(100vw - 32px), 480px);
    display: flex; flex-direction: column; align-items: center; gap: 10px;
    pointer-events: none;
}
html.tg-tabs .tg-toast-stack {
    bottom: calc(var(--tg-tabbar-space, 0px) + 12px);
}
.tg-toast {
    pointer-events: auto;
    display: flex; align-items: center; gap: 10px;
    transform-origin: bottom center;
    max-width: 100%; box-sizing: border-box;
    background: var(--bg-2, #0e1f20);
    border: 1px solid var(--line, rgba(255, 255, 255, 0.1));
    border-left: 3px solid var(--tg-toast-acc, var(--accent));
    color: var(--ink, #eef5ef);
    border-radius: var(--r-card); padding: 10px 14px;
    font-size: 0.92rem; line-height: 1.45;
    box-shadow: var(--shadow-pop);
    opacity: 0; transform: translateY(8px) scale(0.96);
    transition: opacity var(--dur-layer) var(--ease-snap), transform var(--dur-layer) var(--ease-snap);
}
.tg-toast.show { opacity: 1; transform: none; }
/* kind: the bar takes the status colour unless the caller passed an accent,
   and a small dot in the same ink leads the message so the state reads even
   before the words do. */
.tg-toast.is-ok   { --tg-toast-kind: var(--ok); }
.tg-toast.is-warn { --tg-toast-kind: var(--warn); }
.tg-toast.is-bad  { --tg-toast-kind: var(--bad); }
.tg-toast.is-ok, .tg-toast.is-warn, .tg-toast.is-bad {
    border-left-color: var(--tg-toast-acc, var(--tg-toast-kind));
}
.tg-toast-dot {
    flex: none; width: 8px; height: 8px; border-radius: 50%;
    background: var(--tg-toast-kind, var(--accent));
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--tg-toast-kind, var(--accent)) 22%, transparent);
}
.tg-toast-msg { flex: 1 1 auto; min-width: 0; }
/* The action (Undo, Reload): the quiet rung of the button ladder, in the
   accent because it is the one thing here you can press. 44px tall on a
   phone — the toast is exactly the control a thumb reaches for in a hurry. */
.tg-toast-act {
    flex: none; margin: -6px -8px -6px 2px;
    padding: 6px 10px; min-height: 32px;
    border: 0; border-radius: var(--r-ctl); background: transparent;
    color: var(--accent); font: inherit; font-weight: 700; cursor: pointer;
    transition: background var(--dur-ctl) var(--ease-snap);
}
.tg-toast-act:hover { background: var(--fill-hover); }
.tg-toast-act:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
@media (max-width: 860px) {
    .tg-toast-act { min-height: 44px; padding: 6px 12px; }
}
@media (prefers-reduced-motion: reduce) {
    .tg-toast { transition: none; transform: none; }
}

/* ===== Command palette (⌘K) ===== */
.tg-pal-backdrop {
    position: fixed; inset: 0; z-index: 4500; display: none;
    background: rgba(3, 9, 7, 0.55);
    -webkit-backdrop-filter: blur(6px); backdrop-filter: blur(6px);
    padding: 12vh 16px 0;
}
.tg-pal-backdrop.open { display: block; }
.tg-pal {
    max-width: 520px; margin: 0 auto;
    transform-origin: top center;
    animation: tg-pop-in var(--dur-layer) var(--ease-snap);
    background: var(--bg-1, #0a1a1c);
    border: 1px solid var(--line, rgba(255, 255, 255, 0.1));
    border-radius: var(--r-card); overflow: hidden;
    box-shadow: var(--shadow-pop);
}
.tg-pal-input {
    width: 100%; padding: 16px 18px;
    background: transparent; border: none; outline: none;
    color: var(--ink, #eef5ef); font: inherit; font-family: var(--body); font-size: 1.05rem;
    border-bottom: 1px solid var(--line, rgba(255, 255, 255, 0.1));
}
.tg-pal-list { max-height: 320px; overflow-y: auto; padding: 6px; }
.tg-pal-item {
    display: flex; align-items: baseline; gap: 10px; width: 100%;
    padding: 10px 12px; border: none; border-radius: var(--r-ctl);
    background: transparent; color: var(--ink, #eef5ef);
    font: inherit; font-family: var(--body); text-align: left; cursor: pointer;
}
.tg-pal-item.sel { background: var(--fill-selected); }
.tg-pal-kind {
    flex: 0 0 auto; font-size: 0.68rem; font-weight: 700; letter-spacing: 0.08em;
    text-transform: uppercase; color: var(--muted, #9ab5a8);
    border: 1px solid var(--line, rgba(255, 255, 255, 0.1));
    border-radius: var(--r-ctl); padding: 1px 6px;
}
.tg-pal-name { font-weight: 600; }
.tg-pal-meta { color: var(--muted, #9ab5a8); font-size: 0.85rem; margin-left: auto;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 45%; }
.tg-pal-none { padding: 18px; color: var(--muted, #9ab5a8); font-size: 0.92rem; }
.tg-pal-hint {
    padding: 8px 16px; border-top: 1px solid var(--line, rgba(255, 255, 255, 0.08));
    color: var(--muted, #9ab5a8); font-size: 0.75rem;
}
body.pal-open { overflow: hidden; }

/* Palette 2.0 (nav.js): a head with the field, grouped results with counts,
   skeleton rows while a search is in flight. The Planner's course browser
   (gpx.js) reuses .tg-pal / .tg-pal-item as a flat list and never gets the
   head, the groups or .tg-pal-text, so nothing below changes it. */
.tg-pal-head {
    display: flex; align-items: center; gap: 10px; padding: 0 8px 0 16px;
    border-bottom: 1px solid var(--line);
}
.tg-pal-head > .ico { width: 18px; height: 18px; flex: 0 0 auto; color: var(--muted); }
/* Page stylesheets dress bare text inputs; this field is the whole card's
   subject, so it stays bare, and the open palette is its focus indication. */
.tg-pal-head .tg-pal-input {
    flex: 1 1 auto; min-width: 0; padding: 16px 0; margin: 0;
    border: 0; border-radius: 0; background: transparent; box-shadow: none;
    font-size: max(1.05rem, 16px);
}
.tg-pal-head .tg-pal-input:focus, .tg-pal-head .tg-pal-input:focus-visible { outline: none; border: 0; box-shadow: none; }
.tg-pal-esc {
    flex: 0 0 auto; display: inline-flex; align-items: center; justify-content: center;
    min-width: 44px; min-height: 44px; padding: 0; cursor: pointer;
    background: none; border: 0; border-radius: var(--r-ctl);
}
.tg-pal-esc:hover { background: var(--fill-hover); }
.tg-pal kbd {
    display: inline-flex; align-items: center; justify-content: center;
    min-width: 20px; height: 20px; padding: 0 5px; box-sizing: border-box;
    font: 500 0.68rem/1 var(--mono); color: var(--muted);
    background: var(--fill-idle); border: 1px solid var(--edge-soft);
    border-bottom-width: 2px; border-radius: var(--r-ctl);
}
.tg-pal-head ~ .tg-pal-list { max-height: min(420px, 56vh); }
.tg-pal-group + .tg-pal-group { margin-top: 4px; }
.tg-pal-gh {
    display: flex; align-items: baseline; justify-content: space-between; gap: 8px;
    padding: 8px 12px 4px;
}
.tg-pal-gh .data-label { font-size: 0.66rem; color: var(--muted); }
.tg-pal-count { font-size: 0.7rem; color: var(--faint, var(--muted)); }
div.tg-pal-item { align-items: center; min-height: 44px; box-sizing: border-box; }
.tg-pal-text { display: flex; flex-direction: column; gap: 1px; min-width: 0; flex: 1 1 auto; }
.tg-pal-text .tg-pal-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tg-pal-text .tg-pal-meta { margin-left: 0; max-width: none; font-size: 0.8rem; }
.tg-pal-side { flex: 0 0 auto; font-size: 0.76rem; color: var(--muted); }
.tg-pal-item .tg-pal-kind { min-width: 78px; text-align: center; box-sizing: border-box; }
.tg-pal-item.sel .tg-pal-kind { color: var(--ink); border-color: var(--edge); }
.tg-pal-note {
    margin: 6px 6px 2px; padding: 8px 10px; border-radius: var(--r-ctl);
    background: var(--fill-idle); color: var(--muted); font-size: 0.8rem;
}
.tg-pal-none a { color: var(--accent); font-weight: 600; }
.tg-pal-skel { display: flex; align-items: center; gap: 10px; min-height: 44px; padding: 0 12px; }
.tg-pal-skel-k, .tg-pal-skel-l {
    height: 12px; border-radius: var(--r-ctl);
    background: linear-gradient(90deg, var(--fill-idle) 25%, var(--fill-hover) 50%, var(--fill-idle) 75%);
    background-size: 200% 100%;
    animation: tg-pal-shimmer 1.2s ease-in-out infinite;
}
.tg-pal-skel-k { width: 78px; height: 18px; flex: 0 0 auto; }
@keyframes tg-pal-shimmer { 0% { background-position: 180% 0; } 100% { background-position: -80% 0; } }
.tg-pal-hint { display: flex; flex-wrap: wrap; gap: 6px 16px; }
.tg-pal-hint span { display: inline-flex; align-items: center; gap: 3px; }
.tg-pal-hint kbd { min-width: 18px; height: 18px; }
@media (max-width: 860px) {
    .tg-pal-backdrop { padding-top: 8px; }
    .tg-pal-hint-wide { display: none !important; }
    .tg-pal-side { display: none; }
}
@media (prefers-reduced-motion: reduce) { .tg-pal-skel-k, .tg-pal-skel-l { animation: none; } }

/* ===== Goat moments ===== */
.topbar-mascot.goat-sprint .topbar-goat { animation: goat-sprint 0.35s ease-in-out 4; }
.topbar-mascot.goat-sprint .topbar-dust { animation: goat-dust 1.4s ease-out 1; }
.topbar-mascot.goat-party .topbar-goat { animation: goat-hop 0.4s cubic-bezier(.3,1.6,.5,1) 4; }
@keyframes goat-sprint {
    0%, 100% { transform: translateX(0) rotate(0); }
    50% { transform: translateX(-5px) rotate(-6deg); }
}
@keyframes goat-dust {
    0% { opacity: 0; transform: translateX(0) scale(0.8); }
    30% { opacity: 1; }
    100% { opacity: 0; transform: translateX(10px) scale(1.3); }
}
@keyframes goat-hop {
    0%, 100% { transform: translateY(0); }
    45% { transform: translateY(-7px) rotate(6deg); }
}

/* ===== Auth (Sign in with Google — auth.js) ===== */
.tg-auth { position: relative; display: inline-flex; align-items: center; }
.tg-auth-btn {
    font: inherit; font-family: var(--body); font-size: 0.88rem; font-weight: 600;
    color: var(--ink, #eef5ef);
    background: var(--glass, rgba(255, 255, 255, 0.045));
    border: 1px solid var(--line, rgba(255, 255, 255, 0.1));
    border-radius: var(--r-ctl); padding: 7px 16px; cursor: pointer;
    transition: border-color var(--dur-ctl) var(--ease-snap), background var(--dur-ctl) var(--ease-snap);
}
.tg-auth-btn:hover { border-color: var(--edge-soft); background: var(--fill-hover); }
.tg-auth-chip {
    display: inline-flex; align-items: center; gap: 8px;
    font: inherit; font-family: var(--body); font-size: 0.88rem; font-weight: 600;
    color: var(--ink, #eef5ef);
    background: var(--glass, rgba(255, 255, 255, 0.045));
    border: 1px solid var(--line, rgba(255, 255, 255, 0.1));
    border-radius: var(--r-ctl); padding: 4px 14px 4px 5px; cursor: pointer;
}
.tg-auth-avatar { width: 26px; height: 26px; border-radius: 50%; display: block; }
.tg-auth-menu {
    position: absolute; top: calc(100% + 8px); right: 0; z-index: 3000;
    min-width: 220px; transform-origin: top right;
    animation: tg-pop-in var(--dur-layer) var(--ease-snap);
    background: var(--bg-1, #0a1a1c);
    border: 1px solid var(--line, rgba(255, 255, 255, 0.1));
    border-radius: var(--r-card); padding: 10px;
    box-shadow: var(--shadow-pop);
}
.tg-auth-menu[hidden] { display: none; }
.tg-auth-who { padding: 4px 6px 10px; font-weight: 600; color: var(--ink, #eef5ef); }
.tg-auth-who small { display: block; font-weight: 400; color: var(--muted, #9ab5a8); margin-top: 2px; }
.tg-auth-out {
    width: 100%; text-align: left; font: inherit; font-family: var(--body); font-size: 0.9rem;
    color: var(--ink, #eef5ef); background: transparent;
    border: 1px solid var(--line, rgba(255, 255, 255, 0.1));
    border-radius: var(--r-ctl); padding: 9px 12px; cursor: pointer;
}
.tg-auth-out:hover { border-color: var(--edge-soft); background: var(--fill-hover); }
.tg-auth-head { font-weight: 700; padding: 4px 6px 10px; }
.tg-auth-google { display: flex; justify-content: center; padding: 4px 0 8px; min-height: 44px; }
.tg-auth-note { color: var(--muted, #9ab5a8); font-size: 0.8rem; margin: 4px 6px 2px; line-height: 1.45; }

@media (max-width: 860px) {
    /* Inside the hamburger panel the auth control becomes a full-width row,
       and its dropdown opens in flow (an absolute menu would clip).
       ORDERED FIRST: appended last it sat under nine nav links plus search
       and settings, so on a phone (browser chrome eating the height) the one
       control a signed-out visitor came for was below the fold — and with
       Dashboard off the navbar, so was the way back to their account. */
    .tg-auth {
        display: block; order: -1;
        margin: 0 0 8px; padding-bottom: 8px;
        border-bottom: 1px solid var(--line);
    }
    .tg-auth-btn, .tg-auth-chip { width: 100%; justify-content: center; display: inline-flex; min-height: 46px; }
    .tg-auth-menu { position: static; margin-top: 6px; width: 100%; box-shadow: none; animation: none; }
}

/* ===== My plans (auth.js phase 2) ===== */
.tg-plans-head {
    font-family: var(--mono); font-size: 0.72rem; font-weight: 500; letter-spacing: 0.1em; text-transform: uppercase;
    color: var(--muted, #9ab5a8); padding: 8px 6px 6px;
    border-top: 1px solid var(--line, rgba(255, 255, 255, 0.08)); margin-top: 4px;
}
.tg-plans-list { max-height: 260px; overflow-y: auto; margin-bottom: 8px; }
.tg-plans-empty { color: var(--muted, #9ab5a8); font-size: 0.85rem; padding: 6px; line-height: 1.45; }
.tg-plan-row { display: flex; align-items: stretch; gap: 4px; }
.tg-plan-open {
    flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 1px;
    text-align: left; font: inherit; font-family: var(--body); color: var(--ink, #eef5ef);
    background: transparent; border: 1px solid transparent; border-radius: var(--r-ctl);
    padding: 8px 10px; cursor: pointer;
}
.tg-plan-open:hover { background: var(--fill-hover); border-color: var(--edge-soft); }
.tg-plan-name { font-weight: 600; font-size: 0.92rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tg-plan-meta { color: var(--muted, #9ab5a8); font-size: 0.78rem; }
/* Crew-sheet line under a plan that has a bound share (KRI-121): the runner
   grabs the /plan/{token} link straight from the list. Flat text actions. */
.tg-plan-main { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; }
.tg-plan-crew { display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
    padding: 2px 10px 7px; font-size: 0.76rem; color: var(--muted, #9ab5a8); }
.tg-plan-crew .ico { width: 13px; height: 13px; flex: 0 0 auto;
    fill: none; stroke: currentColor; stroke-width: 1.8;
    stroke-linecap: round; stroke-linejoin: round; }
.tg-plan-crew-copy, .tg-plan-crew-open {
    background: none; border: none; padding: 2px 0; cursor: pointer;
    font: 600 0.76rem/1.3 inherit; font-family: var(--body);
    color: var(--accent); text-decoration: underline;
    text-underline-offset: 3px; }
.tg-plan-crew-copy:hover, .tg-plan-crew-open:hover,
.tg-plan-crew-copy:focus-visible, .tg-plan-crew-open:focus-visible {
    color: var(--ink, #eef5ef); }
.tg-plan-del {
    flex: 0 0 auto; width: 34px; font: inherit; font-family: var(--body); font-size: 1rem;
    color: var(--muted, #9ab5a8); background: transparent;
    border: 1px solid transparent; border-radius: var(--r-ctl); cursor: pointer;
}
.tg-plan-del:hover { color: var(--ink); border-color: var(--edge-soft); background: var(--fill-hover); }

/* "Save plan" beside Clear course (fuel/raceday, signed-in with a course) —
   a secondary verb: the ladder's bordered neutral, no glow (TG-282). */
.tg-plan-save {
    font: inherit; font-family: var(--body); font-size: 0.88rem; font-weight: 600;
    color: var(--ink);
    background: var(--fill-idle);
    border: 1px solid var(--edge);
    border-radius: var(--r-ctl); padding: 8px 16px; cursor: pointer; margin-right: 8px;
    transition: background 0.15s ease, transform 0.15s ease;
}
.tg-plan-save:hover { background: var(--fill-hover); transform: translateY(-1px); }
.tg-plan-save[hidden] { display: none; }
@media (prefers-reduced-motion: reduce) { .tg-plan-save:hover { transform: none; } }

/* ===== Search + settings controls ===== */
/* Settings (unit toggles, About you) collapse behind a gear; search fronts
   the ⌘K palette. The desktop bar that holds them is further down. */
.nav-ico { margin-right: 7px; }
.nav-search, .nav-gear {
    flex: 0 0 auto;
    width: 36px; height: 36px;
    display: inline-flex; align-items: center; justify-content: center;
    font-size: 0.95rem; cursor: pointer;
    color: var(--ink, #eef5ef);
    background: var(--glass, rgba(255, 255, 255, 0.045));
    border: 1px solid var(--line, rgba(255, 255, 255, 0.1));
    border-radius: var(--r-ctl);
    transition: border-color var(--dur-ctl) var(--ease-snap), background var(--dur-ctl) var(--ease-snap);
}
.nav-search:hover, .nav-gear:hover {
    border-color: var(--edge-soft);
    background: var(--fill-hover);
}
.nav-search:focus-visible, .nav-gear:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* The desktop door links run the full height of the bar, so the site ring's
   2px offset (tokens.css) would be cut off by the bar's top edge — inset it. */
.topbar .nav a:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }

.nav-settings { position: relative; flex: 0 0 auto; }
.nav-settings-menu {
    position: absolute; top: calc(100% + 8px); right: 0; z-index: 3000;
    min-width: 260px; transform-origin: top right;
    animation: tg-pop-in var(--dur-layer) var(--ease-snap);
    background: var(--bg-1, #0a1a1c);
    border: 1px solid var(--line, rgba(255, 255, 255, 0.1));
    border-radius: var(--r-card); padding: 12px;
    box-shadow: var(--shadow-pop);
    display: flex; flex-direction: column; gap: 10px;
}
.nav-settings-menu[hidden] { display: none; }
.set-row { display: flex; align-items: center; justify-content: space-between; gap: 14px; }
.set-label { font-size: 0.88rem; font-weight: 600; }
.set-row .unit-toggle { margin: 0; }
/* Theme picker: five options — stack the row and let the pills wrap. */
.set-row-theme { flex-direction: column; align-items: stretch; gap: 8px; }
/* Five themes never fitted the 2-segment .unit-toggle pill: wrapping left a
   999px-rounded box with ragged rows and the active pill riding its curve.
   Own component — an even grid, cells of equal width, modest corners. */
/* minmax(0,1fr), not 1fr: plain 1fr floors at min-content, so the longest
   label ('Alpenglow') would widen its column and skew the grid. */
.theme-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 5px;
    background: rgba(0,0,0,0.35); border: 1px solid rgba(255,255,255,0.09);
    border-radius: var(--r-card); padding: 5px;
    box-shadow: var(--well, inset 0 2px 6px rgba(0,0,0,0.5)); }
/* An odd one out spans the row, so the block stays symmetric instead of
   lopsided — but only when the count IS odd; an even grid keeps its columns. */
.theme-grid button:last-child:nth-child(odd) { grid-column: 1 / -1; }
.theme-grid button { display: flex; align-items: center; gap: 8px; width: 100%; min-width: 0;
    white-space: nowrap;
    background: transparent; color: var(--muted, #8ea7a0); border: 1px solid transparent;
    cursor: pointer; font-family: var(--body); font-weight: 700; font-size: 0.78rem;
    letter-spacing: 0.03em; padding: 7px 10px; border-radius: var(--r-ctl);
    transition: background 0.15s, color 0.15s, border-color 0.15s; }
.theme-grid button:hover { color: var(--ink, #eaf4ee); background: rgba(255,255,255,0.05); }
.theme-grid button.active { color: var(--ink, #eaf4ee);
    background: linear-gradient(180deg, rgba(255,255,255,0.16), rgba(255,255,255,0.08));
    border-color: rgba(255,255,255,0.16);
    box-shadow: inset 0 1px 0 rgba(255,255,255,0.2), 0 3px 8px rgba(0,0,0,0.45); }
.theme-grid button:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
/* Palette preview: the theme's ground with its two accents sitting on it. */
.theme-swatch { position: relative; flex: 0 0 auto; width: 22px; height: 16px; border-radius: 5px;
    border: 1px solid rgba(255,255,255,0.18); overflow: hidden; display: block; }
.theme-swatch i { position: absolute; bottom: 3px; width: 6px; height: 6px; border-radius: 50%; }
.theme-swatch i:first-child { left: 3px; }
.theme-swatch i:last-child { right: 3px; }
.set-note { margin: 0; color: var(--muted, #9ab5a8); font-size: 0.76rem; }

@media (max-width: 860px) {
    /* In the hamburger panel: search becomes a full-width row; the gear hides
       and the settings rows show inline (an explicit display beats [hidden]). */
    .nav-search {
        width: 100%; height: auto; min-height: 46px;
        justify-content: flex-start; gap: 10px;
        border-radius: var(--r-ctl); padding: 10px 14px;
        font-size: 1rem; font-weight: 600;
    }
    .nav-search-label { display: inline; font-size: 1rem; font-family: var(--body); letter-spacing: 0; text-transform: none; }
    .nav-gear { display: none; }
    .nav-settings { width: 100%; }
    .nav-settings-menu, .nav-settings-menu[hidden] {
        display: flex; position: static; min-width: 0; width: 100%;
        box-shadow: none; padding: 10px 4px 2px; border: none; border-top: 1px solid var(--line);
        border-radius: 0; background: transparent; animation: none;
    }
}

/* ===== Canonical topbar look (pairs with nav-template.js) =====
   The topbar markup is injected from ONE template on every page; these rules
   (loaded after each page's own stylesheet) make it render identically
   everywhere, whatever the page CSS defines for .topbar/.nav/.unit-toggle. */
/* Full-width on EVERY page — the wordmark sits hard left regardless of window
   width or body class. (It used to be a centered 1180px container, so pages
   without the app-shell override — Dashboard, Training — floated the wordmark
   ~190px inboard on wide monitors.) */
.topbar { display: flex; align-items: center; justify-content: space-between; gap: 10px;
    flex-wrap: wrap; padding: 18px clamp(16px, 2vw, 30px); max-width: none; margin: 0; }
.topbar .wordmark { font-family: var(--display); font-weight: 800; font-size: 1.4rem;
    letter-spacing: 0.02em; color: var(--ink, #eaf4ee); text-decoration: none; }
.topbar .grad { background: linear-gradient(100deg, var(--teal), var(--accent));
    -webkit-background-clip: text; background-clip: text; -webkit-text-fill-color: transparent; }
.topbar .nav { display: flex; align-items: center; gap: 4px; flex-wrap: wrap; }
.topbar .nav a { color: var(--muted, #8ea7a0); text-decoration: none; font-weight: 600; font-size: 0.92rem;
    padding: 8px 14px; border-radius: var(--r-ctl); transition: color 0.2s ease, background 0.2s ease; }
.topbar .nav a:hover { color: var(--ink, #eaf4ee); background: var(--glass, rgba(255,255,255,0.045)); }
/* No topbar CTA (retired 2026-09-02, TG-290): the doors are the way in and
   the bar's spare room belongs to the search field. The one solid accent per
   view (TG-282) is now the page's own primary verb where it has one (the home
   hero's "Plan a run"); the tool pages run on bordered neutrals. */

/* ===== The terminal topbar (TG-286) =====
   One 44px row on --bg-0 with a hairline under it, and it cannot wrap: the
   eleven items that used to fold into an off-centre second row between 861
   and ~1250px are now three doors — Plan, Courses, Why TrailGoat — plus the
   search field, the gear, the account chip and the one CTA. Small mono caps
   for the doors (the same voice as every label since TG-284); the current page
   wears the accent and a 2px inset underline, and a door whose page is open
   wears the underline on its trigger. The menus underneath keep the ladder
   look from TG-282/283. */
@media (min-width: 861px) {
    .topbar {
        flex-wrap: nowrap; height: 44px; gap: 14px;
        padding-top: 0; padding-bottom: 0;
        background: var(--bg-0); border-bottom: 1px solid var(--line);
    }
    body.split-page > .topbar, body.app-shell > .topbar { padding-top: 0; padding-bottom: 0; }
    .topbar .wordmark { flex: 0 0 auto; font-size: 1.15rem; }
    /* The cluster is also fuel.css's .toggles, which wraps and packs right —
       inside a 44px row a wrapped item would float below the bar. Nowrap
       here, packed from the left; the guard below measures this box too. */
    .topbar-tools {
        display: flex; align-items: center; justify-content: flex-start; gap: 8px;
        flex-wrap: nowrap; flex: 1 1 auto; min-width: 0; height: 44px; margin: 0;
    }
    .topbar .nav.tool-switch {
        display: inline-flex; align-items: stretch; gap: 0; flex: 0 0 auto; height: 44px;
        margin: 0; padding: 0; background: none; border: 0; border-radius: 0;
    }
    .nav-group { display: inline-flex; align-items: stretch; }
    /* The three doors. */
    .topbar .nav > .nav-group > .nav-group-btn, .topbar .nav > a {
        display: inline-flex; align-items: center; height: 44px; padding: 0 11px;
        border-radius: 0; white-space: nowrap;
        font-family: var(--mono); font-weight: 500; font-size: 0.72rem;
        letter-spacing: 0.1em; text-transform: uppercase; color: var(--muted);
        transition: color var(--dur-ctl) var(--ease-snap), background var(--dur-ctl) var(--ease-snap);
    }
    .topbar .nav > .nav-group > .nav-group-btn:hover, .topbar .nav > a:hover,
    .topbar .nav > .nav-group > .nav-group-btn[aria-expanded="true"] {
        color: var(--ink); background: var(--fill-hover);
    }
    /* You are here: accent ink and a 2px inset underline — on the page's own
       link, or on the door it lives behind. No fill, no glow. */
    .topbar .nav > a[aria-current="page"],
    .topbar .nav > .nav-group > .nav-group-btn.nav-group-here {
        color: var(--accent); background: none;
        box-shadow: inset 0 -2px 0 var(--accent);
    }
    .topbar .nav > .nav-group > .nav-group-btn.nav-group-here:hover { background: var(--fill-hover); }
    .topbar .nav-caret { margin-left: 6px; }
    /* Menu rows stay in the body face with the ladder's selected fill. */
    .topbar .nav .nav-group-menu a {
        font-family: var(--body); font-weight: 600; font-size: 0.92rem;
        letter-spacing: 0; text-transform: none; height: auto; padding: 9px 12px;
        border-radius: var(--r-ctl); box-shadow: none; color: var(--muted);
    }
    .topbar .nav .nav-group-menu a:hover { color: var(--ink); background: var(--fill-hover); }
    .topbar .nav .nav-group-menu a[aria-current="page"] { color: var(--accent); background: var(--fill-selected); }
    /* The search field — centred in whatever room the doors and the controls
       leave, labelled with the shortcut that opens it. Wider since the topbar
       CTA went (TG-290): that room was the point of retiring it. */
    .nav-search {
        flex: 0 1 440px; min-width: 0; width: auto; height: 30px; margin: 0 auto;
        justify-content: flex-start; gap: 8px; padding: 0 12px 0 10px;
        color: var(--muted); font-family: var(--mono); font-size: 0.68rem;
        letter-spacing: 0.06em; text-transform: none;
    }
    .nav-search:hover { color: var(--ink); }
    /* The hint inherits 0.68rem from the field — 10.9px of mono, a hair under
       the label floor. It takes the floor for itself; the field's own size
       still governs the icon and the box (TG-301). */
    .nav-search-label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
        font-size: max(0.68rem, var(--mono-cap)); }
    .nav-gear { width: 32px; height: 32px; }
    .tg-auth-btn, .tg-auth-chip { flex: 0 0 auto; }
    /* Below ~1080px the field is today's icon button; the same shape is what the
       measured overflow guard (nav.js) falls back to at any width where the bar
       would otherwise spill — say, 861px at 125% zoom with a long account name. */
    .topbar.topbar-tight .nav-search { flex: 0 0 auto; width: 32px; height: 32px; padding: 0; margin: 0 0 0 auto; justify-content: center; }
    .topbar.topbar-tight .nav-search-label { display: none; }
    /* The account chip shows a first name; it may not crowd the search field
       off the bar. Ellipsis past 12 characters, and at the tightest notch the
       chip is the avatar alone. */
    .tg-auth-name { display: inline-block; max-width: 12ch; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
    /* Second notch: the door icons step back and the chip drops its name. */
    .topbar.topbar-tighter .nav > .nav-group > .nav-group-btn .nav-ico { display: none; }
    .topbar.topbar-tighter .tg-auth-chip { padding-right: 5px; }
    .topbar.topbar-tighter .tg-auth-name { display: none; }
}
@media (min-width: 861px) and (max-width: 1079px) {
    .nav-search { flex: 0 0 auto; width: 32px; height: 32px; padding: 0; margin: 0 0 0 auto; justify-content: center; }
    .nav-search-label { display: none; }
}

/* App-shell pages (Race Day's split layout, the Course Creator): the body is a
   flex column, which shrink-wraps the topbar to its content — the wordmark ends
   up bunched against the nav pills in a floating centered cluster. Stretch it
   full-width so space-between lays it out like the landing page: wordmark hard
   left, pills centered, controls right. */
body.split-page > .topbar,
body.app-shell > .topbar { align-self: stretch; width: auto; max-width: none;
    margin-left: 0; margin-right: 0;   /* the shared .topbar's `margin: 0 auto` absorbs the stretch */
    padding-left: clamp(16px, 2vw, 30px); padding-right: clamp(16px, 2vw, 30px); }
/* Unit toggles — inset track + raised neutral key (one look sitewide). */
.unit-toggle { display: inline-flex; gap: 4px; background: rgba(0,0,0,0.35);
    border: 1px solid rgba(255,255,255,0.09); border-radius: var(--r-ctl); padding: 4px;
    box-shadow: var(--well, inset 0 2px 6px rgba(0,0,0,0.5)); }
.unit-toggle button { background: transparent; color: var(--muted, #8ea7a0); border: none; cursor: pointer;
    font-family: var(--body); font-weight: 700; font-size: 0.78rem; letter-spacing: 0.04em;
    padding: 7px 16px; border-radius: calc(var(--r-ctl) - 2px); transition: all 0.15s; }
.unit-toggle button.active { color: var(--ink, #eaf4ee);
    background: linear-gradient(180deg, rgba(255,255,255,0.16), rgba(255,255,255,0.08));
    box-shadow: inset 0 1px 0 rgba(255,255,255,0.2), 0 3px 8px rgba(0,0,0,0.45); }

/* ===== "About you" inside the settings menu (Race Day, data-athlete) ===== */
.nav-settings-menu { max-height: min(76vh, 640px); overflow-y: auto; }
.set-sec { margin-top: 6px; padding-top: 10px; border-top: 1px solid var(--line, rgba(255,255,255,0.1));
    font-size: 0.88rem; font-weight: 700; }
.set-sec small { display: block; font-weight: 400; font-size: 0.72rem;
    color: var(--muted, #8ea7a0); margin-top: 1px; }
.set-field { display: flex; flex-direction: column; gap: 6px; }
.set-field .field-label { font-size: 0.8rem; font-weight: 600; color: var(--muted, #8ea7a0); }
.set-field .field-label em { font-style: normal; color: var(--muted, #8ea7a0); }
.set-field select, .set-field input[type="number"] {
    width: 100%; background: rgba(0,0,0,0.3); color: var(--ink, #eaf4ee);
    border: 1px solid var(--line, rgba(255,255,255,0.1)); border-radius: var(--r-ctl);
    padding: 8px 10px; font-family: var(--body); font-size: 0.9rem;
    box-shadow: var(--well, inset 0 2px 6px rgba(0,0,0,0.5)); color-scheme: dark;
}

/* "I've measured it" (TG-341) — the escape hatch under each 1-10 slider for a
   runner who has done a sweat test. Deliberately quiet: the slider is still the
   model and this only snaps it, so it reads as a secondary affordance rather
   than a second control. The input has to out-specify the full-width rule
   above, hence `input.` rather than a bare class. */
.slider-measured { display: flex; align-items: center; gap: 8px; margin-top: 8px; }
.slider-measured-label { flex: 1 1 auto; font-size: 0.72rem; color: var(--muted, #8ea7a0); }
.set-field input.slider-measured-input { flex: 0 0 88px; width: 88px; padding: 6px 8px;
    font-size: 0.82rem; text-align: right; }
.slider-measured-unit { flex: 0 0 auto; font-style: normal; font-size: 0.72rem;
    color: var(--muted, #8ea7a0); min-width: 46px; }
.set-field select:focus, .set-field input:focus { outline: none; border-color: var(--accent); }

/* ===== Tips (pairs with tg-tips.js, injected sitewide by nav-template.js) ===== */
.tg-info { display: inline-grid; place-items: center; width: 16px; height: 16px;
    padding: 0; border-radius: 50%; border: 1px solid rgba(255,255,255,0.28);
    background: rgba(255,255,255,0.06); color: var(--muted, #8ea7a0);
    font: 700 0.62rem/1 var(--mono); cursor: help;
    vertical-align: -2px; flex: 0 0 auto;
    transition: color 0.15s, border-color 0.15s, background 0.15s; }
.tg-info:hover, .tg-info:focus-visible { color: var(--accent-ink); background: var(--accent);
    border-color: var(--accent); outline: none; }
/* Touch screens: 16px is un-tappable — grow the target (KRI-109). */
@media (pointer: coarse) {
    .tg-info { width: 24px; height: 24px; font-size: 0.72rem; }
}
/* Raised ground, so the tip is the theme's own colour (it was a Forest-green
   literal that read as a stray green box under Tokyo Night). */
#tg-tip { position: fixed; z-index: 9999; max-width: 260px; background: var(--bg-2, #10231e);
    border: 1px solid var(--edge-soft); color: var(--ink, #eaf4ee);
    font-size: 0.78rem; line-height: 1.5; padding: 9px 12px; border-radius: var(--r-card);
    box-shadow: var(--shadow-pop);
    opacity: 0; pointer-events: none; transition: opacity var(--dur-ctl) var(--ease-snap); }
#tg-tip.show { opacity: 1; }
/* Colour legend rows inside a tip (data-tip-legend): dot + label, one per line. */
#tg-tip .tg-tip-legend { list-style: none; margin: 7px 0 0; padding: 7px 0 0;
    border-top: 1px solid var(--line-faint, rgba(255, 255, 255, 0.08)); display: grid; gap: 4px; }
#tg-tip .tg-tip-legend li { display: flex; align-items: center; gap: 8px; }
#tg-tip .tg-tip-legend i { flex: 0 0 auto; width: 9px; height: 9px; border-radius: 50%; }
#tg-tip::before { content: ""; position: absolute; top: 100%; left: var(--arrow-x, 50%);
    transform: translateX(-50%); border: 6px solid transparent;
    border-top-color: var(--edge-soft); }
#tg-tip.below::before { top: auto; bottom: 100%; border-top-color: transparent;
    border-bottom-color: var(--edge-soft); }
#tg-pop { position: fixed; z-index: 9999; width: min(280px, calc(100vw - 24px));
    background: var(--bg-1, #0e211c); border: 1px solid var(--edge-soft); border-radius: var(--r-card);
    padding: 14px 32px 14px 16px; box-shadow: var(--shadow-pop);
    font-size: 0.8rem; color: var(--muted, #8ea7a0); line-height: 1.55;
    opacity: 0; pointer-events: none; transition: opacity var(--dur-ctl) var(--ease-snap); }
#tg-pop.show { opacity: 1; pointer-events: auto; }
#tg-pop b { color: var(--ink, #eaf4ee); display: block; margin-bottom: 4px; }
#tg-pop .tg-pop-more { display: block; margin-top: 8px; color: var(--accent);
    font-weight: 600; text-decoration: none; }
#tg-pop .tg-pop-more:hover { text-decoration: underline; }
#tg-pop .tg-pop-x { position: absolute; top: 8px; right: 10px; background: none; border: none;
    color: var(--muted, #8ea7a0); cursor: pointer; font-size: 0.9rem; padding: 2px; }
@media (prefers-reduced-motion: reduce) { #tg-tip, #tg-pop { transition: none; } }

/* First-visit tour styles live in tg-tour.css — tg-tour.js injects that file
   itself, so pages without nav.css (the crew sheet) render tours identically. */

/* Feedback line at the bottom of the settings menu (nav-template.js). */
.set-feedback { display: block; margin-top: 10px; padding-top: 10px; border-top: 1px solid var(--line);
    font-size: 0.85rem; font-weight: 600; color: var(--accent); text-decoration: none; }
.set-feedback:hover { text-decoration: underline; }

/* ===== Dashboard link in the account chip menu (auth.js) ===== */
.tg-auth-dash { display: block; font-size: 0.9rem; font-weight: 600;
    color: var(--accent); text-decoration: none;
    border: 1px solid var(--line, rgba(255, 255, 255, 0.1));
    border-radius: var(--r-ctl); padding: 9px 12px; margin: 0 0 4px; }
.tg-auth-dash:hover { border-color: var(--edge-soft); background: var(--fill-hover); }
/* Admins get a second row under it into /admin (auth.js draws it only for an
   admin). Ink, not accent: the dashboard link stays the menu's one action. */
.tg-auth-admin { display: flex; align-items: center; gap: 8px; color: var(--ink); margin-top: 6px; }
.tg-auth-admin .ico { width: 16px; height: 16px; flex: none; color: var(--muted); }
/* Every page draws this menu, and not every page loads fuel.css's .ico. */
.tg-auth-admin .ico path { fill: none; stroke: currentColor; stroke-width: 1.75;
    stroke-linecap: round; stroke-linejoin: round; }

/* ===== Site footer (ONE template — filled by nav-template.js) =====
   The page shells only carry <footer class="foot"></footer>; content and
   styling both live with the nav template so every page matches. */
footer.foot { text-align: left; max-width: 1180px; margin: 0 auto; }
.foot-grid { display: grid; grid-template-columns: repeat(var(--foot-cols, 3), 1fr); gap: 28px;
    padding: 30px 8px 22px; border-top: 1px solid var(--line, rgba(255, 255, 255, 0.1)); }
.foot-col h4 { margin: 0 0 10px; font-family: var(--mono); font-size: 0.72rem; font-weight: 500;
    letter-spacing: 0.1em; text-transform: uppercase; color: var(--ink, #eef5ef); }
.foot-link { display: inline-block; color: var(--accent); text-decoration: none;
    font-weight: 600; font-size: 0.92rem; }
/* Footer links are the densest tap targets on the site — a column of them at
   ~20px line boxes, on the screen where a thumb is the only pointer. Padding
   only, at the phone width only, so the desktop footer keeps its rhythm
   (TG-301). */
@media (max-width: 860px) {
    .foot-link { min-height: 40px; display: inline-flex; align-items: center; }
    .foot-legal a, .foot-nav a { min-height: 40px; display: inline-flex; align-items: center; padding: 0 4px; }
}
.foot-link:hover { text-decoration: underline; }
.foot-note { margin: 8px 0 0; font-size: 0.8rem; line-height: 1.5; color: var(--muted, #9ab5a8); }
/* A secondary verb — the ladder's bordered neutral (TG-282). */
.foot-donate { display: inline-flex; align-items: center; gap: 7px;
    font-weight: 700; font-size: 0.92rem; text-decoration: none;
    color: var(--ink);
    background: var(--fill-idle);
    border: 1px solid var(--edge);
    border-radius: var(--r-ctl); padding: 9px 18px;
    transition: background 0.13s ease, border-color 0.13s ease; }
.foot-donate:hover { background: var(--fill-hover); }
.foot-social { display: flex; gap: 10px; }
.foot-soc { display: grid; place-items: center; width: 38px; height: 38px; border-radius: 50%;
    color: var(--muted, #9ab5a8); background: var(--glass, rgba(255, 255, 255, 0.045));
    border: 1px solid var(--line, rgba(255, 255, 255, 0.1)); transition: color 0.15s, border-color 0.15s, transform 0.15s; }
.foot-soc:hover { color: var(--ink, #eef5ef); border-color: var(--edge-soft); background: var(--fill-hover); transform: translateY(-2px); }
.foot-credits { text-align: center; color: var(--muted, #9ab5a8); font-size: 0.8rem;
    padding: 14px 8px 6px; border-top: 1px solid var(--line, rgba(255, 255, 255, 0.06)); }
.foot-credits p { margin: 0; }
@media (max-width: 720px) {
    .foot-grid { grid-template-columns: 1fr; gap: 22px; }
}

/* ===== Offline plans (TG-296) =====
   The per-plan toggle on every plan row, the locked state that is the paid
   layer's clearest shop window, and the storage line. Neutral ladder for the
   control, the one accent on duty when it is on — no second hue, no glow. */
.tg-plan-offline {
    display: inline-flex; align-items: center; gap: 6px;
    margin-top: 6px; padding: 5px 11px;
    background: var(--fill-idle); border: 1px solid var(--edge); border-radius: var(--r-ctl);
    color: var(--muted); cursor: pointer;
    font-family: var(--mono); font-weight: 500; font-size: 0.64rem;
    letter-spacing: 0.1em; text-transform: uppercase;
}
.tg-plan-offline .ico { width: 13px; height: 13px; }
/* Kris requested the existing gold for an incomplete map's retry action. */
.tg-map-retry-label { color: var(--gold); }
@media (max-width: 720px) {
    .tg-plan-offline, .tg-tile-prog .export-btn { min-height: 44px; }
}
.tg-plan-offline:hover:not(:disabled) { background: var(--fill-hover); color: var(--ink); }
.tg-plan-offline:disabled { opacity: 0.55; cursor: default; }
.tg-plan-offline.on { color: var(--accent); border-color: color-mix(in srgb, var(--accent) 45%, transparent); }
/* Locked reads as "not yours yet", not as broken: the same neutral control
   with a padlock. The dialog behind it does the selling. */
.tg-plan-offline.locked::after {
    content: ""; width: 11px; height: 11px; flex: 0 0 auto;
    background: currentColor; opacity: 0.75;
    -webkit-mask: var(--tg-lock) center / contain no-repeat;
    mask: var(--tg-lock) center / contain no-repeat;
}
:root {
    /* Drawn, not an emoji — the brand rule holds in pseudo-elements too. */
    --tg-lock: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill="black" d="M12 2a5 5 0 0 0-5 5v3H6a2 2 0 0 0-2 2v8a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-8a2 2 0 0 0-2-2h-1V7a5 5 0 0 0-5-5zm-3 8V7a3 3 0 1 1 6 0v3H9z"/></svg>');
}
.tg-offline-lapsed { margin-top: 8px; }
/* The header on the offline plan list: this is a partial view, and saying so
   is the difference between "here are your plans" and "here are the ones you
   can reach right now". */
.tg-plans-offline-head { margin-bottom: 6px; }
.tg-plan-offline:disabled { opacity: 1; }   /* a state, not a dead control */

/* Tile download progress (TG-297). A flat channel and a flat fill — the meter
   treatment, no gloss, no gradient — with the size in the mono figure voice
   every other computed number wears. */
.tg-tile-prog { display: flex; align-items: center; gap: 8px; margin-top: 6px; }
.tg-tile-bar {
    flex: 1 1 auto; height: 4px; min-width: 80px;
    background: var(--fill-idle); border-radius: 999px; overflow: hidden;
}
.tg-tile-bar i {
    display: block; height: 100%; width: 0;
    background: var(--accent); transition: width 0.2s linear;
}
.tg-tile-size {
    flex: 0 0 auto; color: var(--muted);
    font-family: var(--mono); font-variant-numeric: tabular-nums;
    font-size: 0.64rem; letter-spacing: 0.06em;
}
@media (prefers-reduced-motion: reduce) { .tg-tile-bar i { transition: none; } }
.tg-offline-lapsed a { color: var(--accent); }

/* Managing a subscription is bookkeeping, not a sell (TG-345), so it reads as
   the link it sits beside rather than as a button — same size, same accent as
   the lapsed note's "See Pro". A <button>, not an <a>, because the destination
   is a single-use URL the server mints on the click. */
.tg-offline-pro { margin-top: 8px; }
.tg-portal-link {
    background: none; border: 0; padding: 0; cursor: pointer;
    font: inherit; color: var(--accent);
}
.tg-portal-link:hover { text-decoration: underline; }
.tg-portal-link:disabled { opacity: 0.6; cursor: default; text-decoration: none; }

.tg-storage-clear {
    flex: 0 0 auto; background: var(--fill-idle); border: 1px solid var(--edge);
    color: var(--ink); border-radius: var(--r-ctl); padding: 7px 15px;
    font: 600 0.82rem/1 var(--body); cursor: pointer;
}
.tg-storage-clear:hover { background: var(--fill-hover); }

/* The weather card's stale stamp: a fact, in the muted ink every other
   parenthetical uses. */
.wx-stale { color: var(--muted); }

/* ===== Offline badge (TG-295) =====
   The app shell loads with no network now, so a page can look completely
   normal while every list on it is empty. The badge is the one place that
   says why. Mono small-caps like every other label since TG-284, on the
   neutral ladder — this is a state, not an alarm, and the one accent on duty
   is spoken for. */
.tg-offline {
    flex: 0 0 auto; align-self: center;
    padding: 3px 8px; border-radius: var(--r-ctl);
    background: var(--fill-idle); border: 1px solid var(--edge); color: var(--muted);
    font-family: var(--mono); font-weight: 500; font-size: 0.62rem;
    letter-spacing: 0.12em; text-transform: uppercase; white-space: nowrap;
}
.tg-offline[hidden] { display: none; }
@media (max-width: 860px) {
    /* Beside the burger rather than in the fold — a state you cannot see is
       not a state that helps. */
    .tg-offline { order: 9; margin-left: auto; }
}

/* ===== Installed-app navigation (TG-294) + the phone tab bar (2026-09-24) =====
   The tab bar is gated on html.tg-tabs AND the phone breakpoint. nav-template.js
   sets tg-tabs on every installed page and, in a browser tab, on the four hub
   pages only (Plan, Courses, Library, Me) — so a desktop at any width, a
   tablet, and every page you arrive at rather than tab to keep the nav they
   already have. The back arrow and the More sheet stay behind
   html.tg-standalone: a browser has its own back button, and the hamburger keeps the long
   tail. Tokens only — no new colours, no glow. */
:root { --tg-tabbar-h: 56px; }

.tg-back { display: none; }
.tg-tabbar, .tg-sheet-scrim { display: none; }

@media (max-width: 860px) {
    html.tg-tabs {
        /* How much room the bar takes, published for anything that has to clear
           it from another stylesheet — tg-tour.css's docked card, today. It is
           declared HERE, inside the same two gates the bar itself is behind, so
           a page that renders no bar (an installed crew sheet, an installed
           tablet) never sees a non-zero value and keeps its own layout. */
        --tg-tabbar-space: calc(var(--tg-tabbar-h) + env(safe-area-inset-bottom, 0px));
    }
    /* Drawing in the Creator: the route toolbar owns the bottom edge, so the
       bar steps aside, and so does everything that clears it (the variable is
       re-declared on body, which every consumer sits inside). */
    html.tg-tabs body.creator-drawing { --tg-tabbar-space: 0px; }
    html.tg-tabs body.creator-drawing .tg-tabbar { display: none; }
    html.tg-tabs body {
        /* The bar is fixed, so the last thing on every page would otherwise sit
           under it. The inset keeps it off the iOS home indicator too. */
        padding-bottom: var(--tg-tabbar-space);
    }

    /* --- The bar --- */
    html.tg-tabs .tg-tabbar {
        position: fixed; left: 0; right: 0; bottom: 0; z-index: 2050;
        display: flex; align-items: stretch;
        height: calc(var(--tg-tabbar-h) + env(safe-area-inset-bottom, 0px));
        padding-bottom: env(safe-area-inset-bottom, 0px);
        background: color-mix(in srgb, var(--bg-0, #07120f) 92%, transparent);
        -webkit-backdrop-filter: blur(14px);
        backdrop-filter: blur(14px);
        border-top: 1px solid var(--line);
    }
    @supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
        html.tg-tabs .tg-tabbar { background: var(--bg-0, #07120f); }
    }
    html.tg-tabs .tg-tab {
        flex: 1 1 0; min-width: 0;
        display: flex; flex-direction: column; align-items: center; justify-content: center;
        /* 44px is the floor a thumb needs (Apple HIG, and the WCAG 2.2 target
           size minimum is 24). These measured 43 — the margin was eating the
           bar. The pill still reads inset because the padding, not the margin,
           draws it. */
        min-height: 44px;
        gap: 3px; padding: 6px 2px; margin: 4px 4px;
        position: relative;                 /* anchors the lit tab's rail */
        background: none; border: 1px solid transparent; border-radius: var(--r-ctl);
        color: var(--muted); text-decoration: none; cursor: pointer;
        /* The mono small-caps voice every label has worn since TG-284. */
        font-family: var(--mono); font-weight: 500; font-size: 0.6rem;
        letter-spacing: 0.1em; text-transform: uppercase;
    }
    html.tg-tabs .tg-tab .ico { width: 20px; height: 20px; }
    html.tg-tabs .tg-tab:active { background: var(--fill-hover); }
    html.tg-tabs .tg-tab:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
    /* Lit tab: accent ink plus a rail on the bar's top edge.
       It used to wear the TG-287 gradient ring, but a ring is a BOX, and this
       box sits 4px from the display's left edge and 6px from its bottom — a
       6px corner tucked inside a 47-62px screen corner, which reads as a
       mistake on every rounded phone. The rail carries the same "you are here"
       with no shape near the corner at all, at any radius. The ring stays the
       one-per-view mark everywhere else; the tab bar simply opts out, so
       nav.js no longer arbitrates for it. */
    html.tg-tabs .tg-tab-on { color: var(--accent); }
    html.tg-tabs .tg-tab-on::before {
        content: "";
        position: absolute; top: -6px;      /* the tab's own 6px top margin */
        left: 22%; right: 22%; height: 2px;
        background: var(--accent);
        border-radius: 0 0 2px 2px;
    }

    /* --- The More sheet --- */
    html.tg-standalone .tg-sheet-scrim {
        position: fixed; inset: 0; z-index: 2100;
        display: flex; align-items: flex-end;
        background: rgba(0, 0, 0, 0.45);
    }
    html.tg-standalone .tg-sheet-scrim[hidden] { display: none; }
    html.tg-standalone .tg-sheet {
        width: 100%; max-height: 78dvh; overflow-y: auto;
        display: flex; flex-direction: column; gap: 2px;
        /* Only the home indicator, not the tab bar: the scrim covers the bar
           while the sheet is open, so reserving its height would just be a
           strip of dead space under the last row. */
        padding: 10px 10px calc(10px + env(safe-area-inset-bottom, 0px));
        background: color-mix(in srgb, var(--bg-1, #0a1a1c) 96%, #ffffff 4%);
        border-top: 1px solid var(--line);
        border-radius: var(--r-card) var(--r-card) 0 0;
        transform-origin: bottom center;
        animation: tg-pop-in var(--dur-layer) var(--ease-snap);
    }
    html.tg-standalone .tg-sheet .nav-link {
        display: flex; align-items: center; gap: 10px;
        width: 100%; min-height: 48px; padding: 0 14px;
        background: none; border: 0; border-radius: var(--r-ctl);
        color: var(--ink); text-decoration: none; text-align: left;
        font: 600 0.95rem var(--body); cursor: pointer;
    }
    html.tg-standalone .tg-sheet .nav-link:active { background: var(--fill-hover); }
    html.tg-standalone .tg-sheet .nav-link small { margin-left: 6px; color: var(--muted); font-weight: 500; }

    /* --- The back arrow --- */
    html.tg-standalone .tg-back {
        display: inline-flex; align-items: center; justify-content: center;
        order: -1; flex: 0 0 auto;
        width: 40px; height: 40px; margin: -4px 2px -4px -8px;
        background: transparent; border: 0; border-radius: var(--r-ctl);
        color: var(--ink); cursor: pointer;
    }
    html.tg-standalone .tg-back[hidden] { display: none; }
    html.tg-standalone .tg-back .ico { width: 20px; height: 20px; }
    html.tg-standalone .tg-back:active { background: var(--fill-hover); }
    html.tg-standalone .tg-back:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

    /* The hamburger panel is height-capped against the viewport; the bar eats
       the bottom of it, and a panel that scrolls under the tabs hides its own
       last rows (AC-5 — the nav groups stay reachable). Browser hub pages too. */
    html.tg-tabs .topbar-tools {
        max-height: calc(100dvh - 90px - var(--tg-tabbar-h) - env(safe-area-inset-bottom, 0px));
    }
}

/* ===== Install offer (TG-293) =====
   A dismissible bar on the Dashboard and under a loaded course on the Run
   Planner, plus the permanent row in the settings menu. Neutral ink ladder and
   the one accent on duty — no coloured glow, and the glyphs are drawn SVG, not
   emoji. Class names are `tg-install-*` on purpose: the crew sheet's own offer
   is `.pwa-*`, styled page-locally in crew.html, and the two must not collide
   on the shared assets a sheet pulls from the root. */
.tg-install {
    display: flex; align-items: center; gap: 10px; margin: 0 0 16px;
    background: var(--glass); border: 1px solid var(--line);
    border-radius: var(--r-card); padding: 11px 13px;
}
.tg-install-ico {
    flex: 0 0 auto; width: 18px; height: 18px; fill: none; stroke: var(--accent);
    stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round;
}
.tg-install-txt { flex: 1 1 auto; font-size: 0.85rem; color: var(--ink); }
.tg-install-go, .tg-install-row-btn {
    flex: 0 0 auto; background: var(--fill-idle); border: 1px solid var(--edge);
    color: var(--ink); border-radius: var(--r-ctl); padding: 7px 15px;
    font: 600 0.82rem/1 var(--body); cursor: pointer;
}
.tg-install-go:hover, .tg-install-row-btn:hover:not(:disabled) { background: var(--fill-hover); }
.tg-install-go:active, .tg-install-row-btn:active:not(:disabled) { background: var(--fill-selected); }
.tg-install-row-btn:disabled { color: var(--muted); cursor: default; }
.tg-install-x {
    flex: 0 0 auto; background: none; border: 0; color: var(--muted);
    font-size: 1.15rem; line-height: 1; cursor: pointer; padding: 2px 4px;
}
.tg-install-x:hover { color: var(--ink); }
/* Inside the installed app there is nothing left to offer. Belt and braces:
   tg-pwa.js already refuses to build the bar when html.tg-standalone is set. */
html.tg-standalone .tg-install { display: none; }

/* ===== Brand mascot as an inline icon =====
   TrailGoat never uses the generic goat emoji — it renders differently on every
   OS and reads as clip-art. This is the drawn mascot (goat-pin.png, the small
   transparent cut — goat.png is the 400 KB hero illustration, hero use only).
   Sized in em so it scales with whatever slot it sits in. */
.tg-goat {
    width: 1.35em; height: 1.35em; object-fit: contain;
    display: inline-block; vertical-align: -0.32em; flex-shrink: 0;
}

/* Footer legal links (About · Privacy) — sit between the grid and credits. */
/* .foot-nav is the crawlable link row each page ships in its served HTML;
   nav-template.js re-renders those same links as .foot-legal once it rebuilds
   the footer (at DOMContentLoaded, so .foot-nav does paint first). Both are
   styled identically so nothing shifts when the swap happens. */
.foot-legal, .foot-nav { display: flex; justify-content: center; align-items: center;
    gap: 10px; flex-wrap: wrap; margin-top: 18px; }
.foot-legal a, .foot-nav a { color: var(--muted); font-size: 12.5px; text-decoration: none; }
.foot-legal a:hover, .foot-nav a:hover { color: var(--accent); }
.foot-legal span { color: var(--muted); opacity: 0.5; font-size: 12px; }

/* ===== Keyboard shortcuts (nav.js — ? anywhere) =====
   The card, head, Esc and phone sheet are the dialog shell's (tg-dialog.css);
   this is only the list inside it. Keys are the mono voice in a
   bordered-neutral cap; group names are .data-label. */
.tg-keys-dlg .tg-dlg-body { padding: 0; }
.tg-keys-body {
    display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 18px 28px;
    padding: 16px 20px 20px;
}
.tg-keys-group h3 { margin: 0 0 8px; font-size: 0.66rem; color: var(--muted); }
.tg-keys-group dl {
    display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 8px 12px;
    margin: 0; align-items: center;
}
.tg-keys-group dt { font-size: 0.84rem; color: var(--ink); }
.tg-keys-group dd { margin: 0; display: inline-flex; align-items: center; gap: 3px; justify-self: end; }
.tg-keys kbd {
    display: inline-flex; align-items: center; justify-content: center;
    min-width: 22px; height: 22px; padding: 0 6px; box-sizing: border-box;
    font: 500 0.7rem/1 var(--mono); color: var(--ink);
    background: var(--fill-idle); border: 1px solid var(--edge-soft);
    border-bottom-width: 2px; border-radius: var(--r-ctl);
}
.tg-keys-then { margin: 0 3px; font-size: 0.7rem; color: var(--muted); }
@media (max-width: 860px) {
    .tg-keys-body { grid-template-columns: minmax(0, 1fr); }
}

/* ===== What's new (nav.js) =====
   The dot rides the wordmark's top-right corner in a 24px hit area (WCAG 2.2
   target minimum) placed absolutely, so showing it never moves the bar. Gold
   is the brand hue outside the feature set; the popover is the palette's card
   on the one entrance. */
.tg-wm-wrap { position: relative; display: inline-flex; align-items: center; flex: 0 0 auto; }
.tg-new-dot {
    position: absolute; top: -10px; right: -16px;
    width: 24px; height: 24px; padding: 0;
    display: inline-flex; align-items: center; justify-content: center;
    background: none; border: 0; border-radius: 50%; cursor: pointer;
}
.tg-new-dot[hidden] { display: none; }
.tg-new-dot span {
    width: 8px; height: 8px; border-radius: 50%;
    background: var(--gold);
    box-shadow: 0 0 0 2px var(--bg-0);
}
.tg-new-dot:hover span { transform: scale(1.25); }
.tg-new-dot:focus-visible { outline: 2px solid var(--accent); outline-offset: -3px; }
.tg-new {
    position: fixed; z-index: 3050;
    background: var(--bg-1); color: var(--ink);
    border: 1px solid var(--line); border-radius: var(--r-card);
    box-shadow: var(--shadow-pop);
    transform-origin: top left;
    animation: tg-pop-in var(--dur-layer) var(--ease-snap);
}
.tg-new[hidden] { display: none; }
.tg-new-head {
    display: flex; align-items: baseline; justify-content: space-between; gap: 10px;
    padding: 12px 14px 10px; border-bottom: 1px solid var(--line);
}
.tg-new-h { font-size: 0.7rem; color: var(--gold); }
.tg-new-when { font-size: 0.72rem; color: var(--muted); }
.tg-new-list { list-style: none; margin: 0; padding: 6px 6px; }
.tg-new-list li { display: flex; align-items: baseline; gap: 10px; padding: 7px 8px; }
.tg-new-date { flex: 0 0 auto; font-size: 0.7rem; color: var(--muted); }
.tg-new-list a, .tg-new-list li > span:last-child {
    color: var(--ink); text-decoration: none; font-weight: 600; font-size: 0.86rem; line-height: 1.35;
}
.tg-new-list a:hover { color: var(--accent); }
.tg-new-foot {
    display: flex; align-items: center; justify-content: space-between; gap: 10px;
    padding: 8px 14px 12px;
}
.tg-new-all { color: var(--accent); font-size: 0.82rem; font-weight: 600; text-underline-offset: 3px; }
.tg-new-ok {
    min-height: 32px; padding: 6px 14px; cursor: pointer;
    background: var(--fill-idle); color: var(--ink);
    border: 1px solid var(--edge); border-radius: var(--r-ctl);
    font: 600 0.8rem/1 var(--body);
}
.tg-new-ok:hover { background: var(--fill-hover); }
.tg-new-ok:active { background: var(--fill-selected); }
@media (pointer: coarse) { .tg-new-ok { min-height: 44px; } }
button.tg-new-all { background: none; border: 0; padding: 6px 0; cursor: pointer; font-family: var(--body); text-decoration: underline; }
/* The full list, in the dialog shell */
.tg-new-full { list-style: none; margin: -6px 0; padding: 0; }
.tg-new-full li {
    display: grid; grid-template-columns: 52px auto minmax(0, 1fr); align-items: baseline; gap: 10px;
    padding: 9px 0; border-bottom: 1px solid var(--line-faint);
}
.tg-new-full li:last-child { border-bottom: 0; }
.tg-new-full a { color: var(--ink); font-weight: 600; font-size: 0.9rem; line-height: 1.35; text-decoration: none; }
.tg-new-full a:hover { color: var(--accent); }
.tg-new-tag {
    font-size: 0.66rem; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase;
    color: var(--muted); border: 1px solid var(--line); border-radius: var(--r-ctl); padding: 1px 6px;
}
@media (prefers-reduced-motion: reduce) { .tg-new { animation: none; } }

/* ===== Reduced motion: the layers arrive without arriving (TG-285) =====
   This block sits at the end of the file on purpose. `.tg-pal`, `.tg-auth-menu`
   and `.nav-settings-menu` are all defined further down than the reduced-motion
   block up in the mobile section, so an override placed there loses the cascade
   to their own `animation` declarations — equal specificity, later wins. Keep
   any new arriving layer's suppression here. */
@media (prefers-reduced-motion: reduce) {
    .nav-group-menu,
    .topbar.menu-open .topbar-tools,
    .tg-auth-menu,
    .nav-settings-menu,
    .tg-pal,
    .topbar-mascot.goat-led .topbar-goat { animation: none; }
}

/* Opening a course: the tapped link goes busy until the planner paints (nav.js). */
.tg-opening { opacity: 0.6; pointer-events: none; cursor: progress; }

/* ===== Skip to content (nav-template.js) =====
   Off-screen until it takes focus, then a plain bordered-neutral pill in the
   top-left corner over the bar. Clipped rather than display:none so it stays
   in the tab order; the site-wide :focus-visible ring (tokens.css) marks it. */
.tg-skip {
    position: fixed; top: 8px; left: 8px; z-index: 12000;
    padding: 10px 14px; min-height: 44px; box-sizing: border-box;
    display: inline-flex; align-items: center;
    background: var(--bg-1); color: var(--ink);
    border: 1px solid var(--edge); border-radius: var(--r-ctl);
    font: 600 0.88rem/1 var(--body); text-decoration: none;
    box-shadow: var(--shadow-pop);
    transform: translateY(calc(-100% - 16px));
}
.tg-skip:focus, .tg-skip:focus-visible { transform: none; }

/* ===== Notification bell (nav-template.js markup, nav.js behavior) =====
   An icon button in the bar, signed in only; the dot is the unread state in
   the one accent. The popover is the What's-new card: panel colour, one
   hairline, the pop shadow. Unread rows carry the same accent dot. */
.tg-bell { position: relative; flex: 0 0 auto; display: inline-flex; }
.tg-bell[hidden] { display: none; }
.tg-bell-btn {
    position: relative; display: inline-flex; align-items: center; justify-content: center;
    width: 40px; height: 40px; padding: 0; cursor: pointer;
    background: none; border: 0; border-radius: var(--r-ctl); color: var(--muted);
    transition: background var(--dur-ctl) var(--ease-snap), color var(--dur-ctl) var(--ease-snap);
}
.tg-bell-btn .ico { width: 19px; height: 19px; }
.tg-bell-btn:hover, .tg-bell-btn[aria-expanded="true"] { background: var(--fill-hover); color: var(--ink); }
.tg-bell-dot {
    position: absolute; top: 8px; right: 9px; width: 8px; height: 8px; border-radius: 50%;
    background: var(--accent); box-shadow: 0 0 0 2px var(--bg-0);
}
.tg-bell-dot[hidden] { display: none; }
.tg-bell-pop {
    position: fixed; z-index: 3050; max-height: min(70vh, 520px); overflow-y: auto;
    background: var(--bg-1); color: var(--ink);
    border: 1px solid var(--line); border-radius: var(--r-card);
    box-shadow: var(--shadow-pop);
    transform-origin: top right;
    animation: tg-pop-in var(--dur-layer) var(--ease-snap);
}
.tg-bell-pop[hidden] { display: none; }
.tg-bell-head {
    position: sticky; top: 0; z-index: 1; background: var(--bg-1);
    display: flex; align-items: center; justify-content: space-between; gap: 10px;
    padding: 8px 8px 8px 16px; border-bottom: 1px solid var(--line);
}
.tg-bell-h { margin: 0; font-size: 0.7rem; color: var(--muted); outline: none; }
.tg-bell-all {
    min-height: 36px; padding: 6px 10px; cursor: pointer;
    background: none; border: 0; border-radius: var(--r-ctl);
    color: var(--accent); font: 600 0.8rem/1 var(--body);
}
.tg-bell-all:hover { background: var(--fill-hover); }
.tg-bell-list { list-style: none; margin: 0; padding: 6px; }
.tg-bell-list a {
    display: flex; align-items: flex-start; gap: 12px; padding: 10px;
    border-radius: var(--r-ctl); color: var(--ink); text-decoration: none;
}
.tg-bell-list a:hover { background: var(--fill-hover); }
.tg-bell-ico {
    flex: 0 0 auto; display: inline-flex; align-items: center; justify-content: center;
    width: 30px; height: 30px; border-radius: var(--r-ctl);
    background: var(--fill-idle); border: 1px solid var(--line); color: var(--muted);
}
.tg-bell-ico .ico { width: 15px; height: 15px; }
.tg-bell-text { display: flex; flex-direction: column; gap: 2px; min-width: 0; flex: 1 1 auto; }
.tg-bell-title { font-weight: 600; font-size: 0.88rem; line-height: 1.35; }
.tg-bell-meta { font-size: 0.78rem; color: var(--muted); line-height: 1.35; }
.tg-bell-list li:not(.is-unread) .tg-bell-title { color: var(--muted); font-weight: 500; }
.tg-bell-unread { flex: 0 0 auto; width: 8px; height: 8px; margin-top: 6px; border-radius: 50%; background: var(--accent); }
.tg-bell-sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
.tg-bell-empty { margin: 0; padding: 16px; color: var(--muted); font-size: 0.86rem; line-height: 1.5; }
@media (max-width: 860px) {
    .tg-bell { order: 9; margin: -4px 0; }
    .tg-bell-btn { width: 44px; height: 44px; }
    .tg-bell-all { min-height: 44px; }
}
@media (prefers-reduced-motion: reduce) { .tg-bell-pop { animation: none; } }
.tg-bell-meta .data { white-space: nowrap; }
/* ===== Empty states and skeletons (shared; polish pass 2026-09-25) =====
   .tg-empty — an empty list as an invitation, the same shape everywhere: the
   goat in a ring, a headline naming what this place will hold, one line, ONE
   primary action (.export-btn.tonal, the ladder's strongest verb here) and one
   quiet alternative. `.is-compact` for dashboard tiles and side panels.
   .tg-skel — the shimmer a list shows while its fetch is pending, drawn in the
   shape of the cards that will replace it (.tg-skel-card); pages show it only
   after 150 ms so a fast answer never flashes. Static under reduced motion. */
.tg-empty { display: flex; flex-direction: column; align-items: center; text-align: center;
    max-width: 440px; margin: 0 auto; padding: 26px 16px 22px; font-style: normal; }
.tg-empty[hidden] { display: none; }
.tg-empty-art { display: grid; place-items: center; width: 60px; height: 60px; margin: 0 0 12px;
    border-radius: 50%; background: var(--fill-idle); border: 1px solid var(--line); }
.tg-empty-art img { width: 36px; height: 36px; object-fit: contain; }
.tg-empty-title { margin: 0; font-family: var(--display); font-weight: 800; font-size: 1.05rem;
    line-height: 1.25; color: var(--ink); text-wrap: balance; }
.tg-empty-line { margin: 6px 0 14px; color: var(--muted); font-size: 0.88rem; line-height: 1.5;
    text-wrap: pretty; }
.tg-empty-go { min-height: 40px; text-decoration: none; }
.tg-empty-go .ico { color: var(--accent); }
.tg-empty-alt { margin: 10px 0 0; color: var(--muted); font-size: 0.84rem; line-height: 1.4; }
.tg-empty-alt a, .tg-empty-alt button { display: inline-flex; align-items: center; min-height: 32px;
    padding: 0; border: 0; background: none; font: inherit; color: var(--accent); cursor: pointer;
    text-decoration: underline; text-decoration-color: color-mix(in srgb, var(--accent) 40%, transparent);
    text-underline-offset: 3px; }
.tg-empty-alt a:hover, .tg-empty-alt button:hover { text-decoration-color: var(--accent); }
.tg-empty-go:focus-visible, .tg-empty-alt a:focus-visible, .tg-empty-alt button:focus-visible {
    outline: 2px solid var(--accent); outline-offset: 2px; border-radius: var(--r-ctl); }
.tg-empty.is-compact { padding: 16px 10px 14px; }
.tg-empty.is-compact .tg-empty-art { width: 46px; height: 46px; margin-bottom: 10px; }
.tg-empty.is-compact .tg-empty-art img { width: 28px; height: 28px; }
.tg-empty.is-compact .tg-empty-title { font-size: 0.96rem; }
.tg-empty.is-compact .tg-empty-line { font-size: 0.84rem; margin-bottom: 12px; }
@media (max-width: 860px) {
    .tg-empty-go { min-height: 44px; }
    .tg-empty-alt a, .tg-empty-alt button { min-height: 44px; }
}

.tg-skel { display: block; height: 12px; border-radius: var(--r-ctl);
    background: linear-gradient(90deg, var(--fill-idle) 25%, var(--fill-hover) 50%, var(--fill-idle) 75%);
    background-size: 200% 100%; animation: tg-skel 1.3s ease-in-out infinite; }
.tg-skel.is-title { height: 14px; }
.tg-skel.is-sm { height: 9px; }
.tg-skel.is-chip { height: 18px; width: 56px; flex: 0 0 auto; }
.tg-skel.is-block { height: 34px; }
.tg-skel-list { display: grid; gap: 10px; }
.tg-skel-card { display: grid; gap: 9px; padding: 13px 14px; border: 1px solid var(--line);
    border-radius: var(--r-card); background: var(--glass); }
.tg-skel-row { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
@keyframes tg-skel { 0% { background-position: 180% 0; } 100% { background-position: -80% 0; } }
@media (prefers-reduced-motion: reduce) {
    .tg-skel { animation: none; background: var(--fill-hover); }
}

/* /launch-beta.css */
/* The launch offer extends the shared shell, palette and typography. */
.tg-launch-note { font: 400 .88rem/1.55 var(--body); color: var(--ink); background: var(--bg-1); border: 1px solid var(--line); border-radius: var(--r-card); padding: 12px 16px; margin: 12px 0; }
.tg-launch-note a { color: var(--accent); text-decoration: underline; text-underline-offset: 3px; }
.tg-beta-label { display: none; }
html[data-launch-beta="true"] .tg-beta-label,
html[data-launch-beta="true"] .tg-plan-offline::after { display: inline-block; font: 500 var(--mono-cap)/1.3 var(--mono); text-transform: uppercase; letter-spacing: .05em; color: var(--muted); border: 1px solid var(--edge-soft); border-radius: var(--r-ctl); padding: 2px 5px; margin-left: 6px; }
html[data-launch-beta="true"] .tg-plan-offline::after { content: "Beta"; }
.tg-beta-pricing .pr-grid { margin-top: 24px; }
.tg-beta-pricing .pr-card { background: var(--bg-1); }
.tg-beta-pricing .export-btn { min-height: 44px; }
.tg-beta-pricing .info-tldr { margin: 24px 0; }
.tg-beta-pricing > .info-card { margin-top: 24px; padding: 22px; background: var(--bg-1); border: 1px solid var(--line); border-radius: var(--r-card); }
@media print { .tg-launch-note, .tg-beta-label { display: none !important; } }

html[data-launch-beta="true"] .wordmark .beta-pill { font: 500 var(--mono-cap)/1.3 var(--mono); text-transform: uppercase; letter-spacing: .04em; color: var(--muted); border: 1px solid var(--edge-soft); border-radius: var(--r-ctl); padding: 3px 5px; margin-left: 8px; }

/* Pricing during launch: every price shown, each paid card saying it's free
   until the launch window closes. The line is the accent — it is the offer —
   and sits under the price it qualifies, never in place of it. */
.tg-beta-pricing .pr-launch { font: 600 .84rem/1.4 var(--body); color: var(--accent); margin: -2px 0 12px; }
.tg-beta-pricing .pr-crew-note { color: var(--muted); font-size: .92rem; line-height: 1.5; margin: 14px 2px 0; }
.tg-beta-pricing .pr-dayblock .pr-day { margin-top: 14px; }

