/* /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; }

/* /fuel.css */
/* TrailGoat Fuel Planner — shares the palette + type of the main dashboard.
   The palette and feature accents live in tokens.css (TG-281), which every
   page loads first; this block keeps the radius and the depth system. */
:root {
    /* Legacy card corner — now an alias of the shared --r-card (TG-283). */
    --radius: var(--r-card);

    /* Depth system — one light source for the whole site.
       raise-1: resting surfaces · raise-2: interactive/raised controls ·
       raise-primary: legacy solid-CTA depth, unused since buttons went flat ·
       well: inset tracks & inputs */
    --raise-1: inset 0 1px 0 rgba(255,255,255,0.07), 0 2px 6px rgba(0,0,0,0.35);
    --raise-2: inset 0 1px 0 rgba(255,255,255,0.09), 0 2px 5px rgba(0,0,0,0.45),
               0 10px 22px -8px rgba(0,0,0,0.55);
    --raise-primary: inset 0 1px 0 rgba(255,255,255,0.38), inset 0 -2px 4px rgba(120,45,0,0.3),
               0 2px 5px rgba(0,0,0,0.45), 0 10px 22px -8px rgba(255,122,26,0.5);
    --well: inset 0 2px 6px rgba(0,0,0,0.5), inset 0 -1px 0 rgba(255,255,255,0.05);
    --ledge-orange: 0 4px 0 #a34d03;
}

/* Inline stroke icons — inherit the button's text color so they always match. */
.ico { width: 15px; height: 15px; flex: 0 0 auto; }
.ico path, .ico circle, .ico rect, .ico line, .ico polyline { fill: none; stroke: currentColor; stroke-width: 1.75;
    stroke-linecap: round; stroke-linejoin: round; }

* { 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.5;
    -webkit-font-smoothing: antialiased;
    overflow-x: clip;   /* not `hidden`: hidden makes body a scroll container, which eats smooth scrolls */
}

/* --- Backdrop --- */
.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: -20% -20% auto -20%; height: 60%;
    background: conic-gradient(from 180deg at 50% 50%, transparent, rgba(154,230,0,0.06), transparent 40%);
    filter: blur(40px); }
.ridge { position: absolute; bottom: 0; left: 0; width: 100%; height: 38vh; opacity: 0.5; }
.ridge-back { fill: #0c2220; } .ridge-mid { fill: #0a1c1a; } .ridge-front { fill: #07140f; }

/* --- Top bar --- */
.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; }
.wordmark { font-family: var(--display); font-weight: 800; font-size: 1.4rem;
    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; }
/* Inline brand lockup for AI-coach headings — "TRAILGOAT AI" in the wordmark's
   display cut, GOAT + AI in the brand gradient (.grad). "Ask" keeps the heading's
   own font, so on the all-caps cards it stays in the mono. */
.coach-brand { font-family: var(--display); font-weight: 800; letter-spacing: 0.02em;
    color: var(--ink); text-transform: none; }

.toggles { display: flex; gap: 8px; flex-wrap: wrap; justify-content: flex-end; }
/* Unit toggles — an inset track with a raised "key" for the active side, so the
   switch reads as physical and orange stays reserved for primary actions. */
.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); }
.unit-toggle button { background: transparent; color: var(--muted); border: none; cursor: pointer;
    font-family: inherit; 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);
    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); }

/* --- Layout --- */
.wrap { max-width: 1180px; margin: 0 auto; padding: 10px clamp(16px, 4vw, 40px) 80px; }

.hero { margin: 18px 0 30px; }
.eyebrow { color: var(--accent); font-family: var(--mono); font-weight: 500; letter-spacing: 0.1em;
    text-transform: uppercase; font-size: 0.8rem; margin-bottom: 12px; }
.hero h1 { font-family: var(--display); font-weight: 800;
    font-size: clamp(2rem, 5vw, 3.2rem); line-height: 1.05; letter-spacing: -0.02em; }
.hero-sub { color: var(--muted); font-size: 1.05rem; max-width: 40rem; margin-top: 16px; }

/* --- Panels --- */
.panel { background: var(--bg-2);
    border: 1px solid var(--line); border-top-color: rgba(255,255,255,0.16);
    border-radius: var(--radius);
    padding: 24px; box-shadow: var(--raise-1); }
.inputs, .fueling-plan { margin-bottom: 22px; }
.panel-title { font-family: var(--display); font-weight: 700; font-size: 1.15rem; }
.panel-head-row { display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap; }
/* Buttons — one solid primary per view; everything else is tonal or quiet.
   All three share the same geometry so they line up in a toolbar. */
/* Secondary verbs are the ladder's bordered neutral (TG-282): ink label, a
   defined edge, the fill steps on hover. Never a solid accent: the tool
   pages have no solid CTA since the topbar's went (TG-290). */
.export-btn { display: inline-flex; align-items: center; justify-content: center; gap: 7px;
    background: var(--fill-idle);
    color: var(--ink); border: 1px solid var(--edge); border-radius: var(--r-ctl);
    padding: 9px 18px; font-family: inherit; font-weight: 700; font-size: 0.85rem; cursor: pointer;
    transition: transform 0.13s ease, background 0.13s ease, border-color 0.13s ease; }
.export-btn:hover { transform: translateY(-1px);
    background: var(--fill-hover); }
.export-btn:active { transform: translateY(1px); background: var(--fill-selected); }
.export-btn:disabled { opacity: 0.6; cursor: default; transform: none; }
/* Tonal — tinted glass for secondary verbs (open, reverse, undo). */
.export-btn.tonal { color: var(--ink); border: 1px solid rgba(255,255,255,0.12);
    background: linear-gradient(180deg, rgba(255,255,255,0.085), rgba(255,255,255,0.04));
    box-shadow: var(--raise-1); }
.export-btn.tonal:hover { border-color: rgba(255,255,255,0.24); filter: none; }
.export-btn.tonal:active { box-shadow: inset 0 2px 6px rgba(0,0,0,0.45); }
.export-btn.tonal .ico { color: var(--accent); }

/* The effort — three intuitive groups side by side (stack on mobile) */
.effort-cols { display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
    gap: 26px; margin-top: 18px; }
.effort-col { display: flex; flex-direction: column; }
.effort-col > * + * { margin-top: 16px; }
.col-head { font-family: var(--display); font-weight: 600; font-size: 0.98rem; color: var(--ink);
    padding-bottom: 8px; border-bottom: 1px solid var(--line); }

/* Fueling plan — each section is its own card under a shared header.
   The food pack is a full-width card on top; per-hour + total sit side-by-side below it. */
.fuel-head { margin-bottom: 16px; }
.fuel-cards { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 18px; }
.fuel-card-full { grid-column: 1 / -1; }
@media (max-width: 600px) { .fuel-cards { grid-template-columns: 1fr; } }
.fuel-card { background: var(--bg-2);
    border: 1px solid var(--line); border-top-color: rgba(255,255,255,0.16);
    border-radius: var(--radius);
    padding: 22px; box-shadow: var(--raise-1); }
.result-group { display: flex; flex-direction: column; }
.methodology { margin-top: 22px; }

/* --- Form fields --- */
.field-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 14px; }
.adv-grid { max-width: 460px; margin-top: 14px; }
.field { display: flex; flex-direction: column; gap: 6px; }
.field-block { display: flex; flex-direction: column; gap: 10px; }
.field-label { color: var(--muted); font-size: 0.85rem; font-weight: 500; }
.field-label em { color: var(--teal); font-style: normal; font-weight: 600; }
.field-label small { color: var(--muted); opacity: 0.7; font-weight: 400; }
.field-label strong { color: var(--ink); font-family: var(--display); }
.slider-hint { color: var(--gold); font-weight: 600; margin-left: 4px; }

input[type="number"], input[type="text"], input[type="search"], input[type="datetime-local"], input[type="date"], select {
    background: var(--glass-2); color: var(--ink); border: 1px solid var(--line);
    border-radius: var(--r-ctl); padding: 11px 14px; font-family: inherit; font-size: 1rem; width: 100%;
    transition: border-color 0.15s; color-scheme: dark; -webkit-appearance: none; appearance: none; }
input[type="number"]:focus, input[type="text"]:focus, input[type="search"]:focus, input[type="datetime-local"]:focus, input[type="date"]:focus, select:focus {
    outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px var(--ring); }
input[type="search"]::-webkit-search-decoration, input[type="search"]::-webkit-search-cancel-button { -webkit-appearance: none; }
select { cursor: pointer; }
select option { background: var(--bg-1); color: var(--ink); }

/* Segmented control */
.segmented { display: inline-flex; gap: 4px; background: var(--glass-2);
    border: 1px solid var(--line); border-radius: var(--r-ctl); padding: 4px; }
.segmented button { flex: 1; background: transparent; color: var(--muted); border: none;
    cursor: pointer; font-family: inherit; font-weight: 600; font-size: 0.9rem;
    padding: 9px 12px; border-radius: calc(var(--r-ctl) - 2px); transition: all 0.15s; white-space: nowrap; }
.segmented button.active { background: var(--fill-selected); color: var(--accent); }
/* Phones: a segmented group must never widen the page (pace-split-modes hit
   500px and dragged the whole planner into horizontal scroll at 375px, KRI-109).
   The group scrolls in place instead, scrollbar hidden. */
@media (max-width: 600px) {
    .segmented { display: flex; max-width: 100%; overflow-x: auto;
        -webkit-overflow-scrolling: touch; scrollbar-width: none; }
    .segmented::-webkit-scrollbar { display: none; }
    .segmented button { flex: 0 0 auto; }
}

/* Sliders */
.slider { -webkit-appearance: none; appearance: none; width: 100%; height: 6px; border-radius: 999px;
    background: linear-gradient(90deg, var(--teal), var(--gold), var(--orange)); outline: none; }
/* outline:none above outranks the site's :where() ring, so every range input
   had no keyboard focus mark at all. Restore it, clear of the 22px thumb. */
.slider:focus-visible { outline: 2px solid var(--accent); outline-offset: 4px; }
.slider::-webkit-slider-thumb { -webkit-appearance: none; appearance: none; width: 22px; height: 22px;
    border-radius: 50%; background: var(--ink); border: 3px solid var(--accent); cursor: pointer;
    box-shadow: 0 2px 8px rgba(0,0,0,0.4); }
.slider::-moz-range-thumb { width: 22px; height: 22px; border-radius: 50%; background: var(--ink);
    border: 3px solid var(--accent); cursor: pointer; }

/* Flat pace + duration estimate */
.pace-row { display: flex; gap: 10px; }
.pace-row input { flex: 0 0 96px; text-align: center; }
.estimate-btn { flex: 1; color: var(--ink);
    background: linear-gradient(180deg, rgba(255,255,255,0.085), rgba(255,255,255,0.04));
    border: 1px solid var(--line); border-radius: var(--r-ctl); padding: 0 14px;
    font-family: inherit; font-weight: 600; font-size: 0.9rem; cursor: pointer;
    box-shadow: var(--raise-1);
    transition: all 0.15s; }
.estimate-btn:hover { border-color: var(--edge-soft); background: var(--fill-hover); }
.estimate-btn:active { box-shadow: inset 0 2px 6px rgba(0,0,0,0.45); }
.estimate-out { color: var(--muted); font-size: 0.85rem; margin-top: 4px; min-height: 1.1em; }
.estimate-out strong { color: var(--gold); font-family: var(--mono);
    font-weight: 700; }
.estimate-out small { opacity: 0.85; }
.estimate-out.warn { color: var(--warn); }
.estimate-out.warn strong { color: var(--warn); }

/* Weather row (date/time + button) */
.weather-row { display: flex; gap: 10px; flex-wrap: wrap; }
.weather-row input { flex: 1 1 170px; }
.weather-row .estimate-btn { flex: 0 0 auto; }

.advanced { margin-top: 20px; border-top: 1px solid var(--line); padding-top: 14px; }
.advanced summary, .methodology summary { cursor: pointer; color: var(--muted); font-weight: 600;
    font-size: 0.9rem; list-style: none; }
.advanced summary::-webkit-details-marker, .methodology summary::-webkit-details-marker { display: none; }
.advanced summary::before, .methodology summary::before { content: '＋ '; color: var(--accent); }
.advanced[open] summary::before, .methodology[open] summary::before { content: '－ '; }

/* --- Result metrics --- */
/* Gut training on the Per-hour card (TG-340) — the same segmented control the settings
   menu uses, so the two places this one value can be set look like one control. It sits
   above the grid because it is the reason the carbs number reads what it reads. */
.gut-row { display: flex; align-items: center; flex-wrap: wrap; gap: 8px 12px; margin-top: 14px; }
.gut-row-label { font-size: 0.78rem; font-weight: 700; letter-spacing: 0.04em;
    text-transform: uppercase; color: var(--muted); }
.gut-chips { flex: 1 1 auto; }
.gut-chips button { flex: 1 1 0; padding-left: 10px; padding-right: 10px; }
@media (max-width: 480px) { .gut-row-label { flex: 1 0 100%; } }

.metric-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; margin-top: 16px; }
.metric { position: relative; background: var(--glass-2); border: 1px solid var(--line);
    border-radius: var(--radius); padding: 15px 16px; overflow: hidden; }
.metric::before { content: ''; position: absolute; top: 0; left: 0; width: 100%; height: 3px;
    background: var(--stat-acc); opacity: 0.9; }
.metric::after { content: ''; position: absolute; top: -40px; right: -40px; width: 110px; height: 110px;
    border-radius: 50%; background: var(--stat-acc); opacity: 0.12; filter: blur(8px); }
/* Scoped as --stat-acc (a data tint), never the global --accent (TG-282). */
.metric-water  { --stat-acc: var(--teal); }
.metric-sodium { --stat-acc: var(--violet); }
.metric-carbs  { --stat-acc: var(--gold); }
.metric-cals   { --stat-acc: var(--orange); }
.metric-label { color: var(--muted); font-family: var(--mono); font-size: max(0.74rem, var(--mono-cap)); font-weight: 500;
    letter-spacing: 0.1em; text-transform: uppercase; margin-bottom: 8px; }
.metric-num { font-family: var(--mono); font-weight: 700;
    font-size: 1.5rem; line-height: 1.05; color: var(--stat-acc); }
/* Keep a range (e.g. "998–1,831") together; let the unit wrap below if tight.
   inline-block so JS can measure its width and auto-shrink to fit the card. */
.metric-num > span:first-child { white-space: nowrap; display: inline-block; }
.metric-unit { font-size: 0.8rem; font-weight: 500; letter-spacing: 0.08em; text-transform: uppercase;
    color: var(--muted); margin-left: 3px; }

/* Kit list */
.kit-list { list-style: none; margin-top: 14px; display: flex; flex-direction: column; gap: 12px; }
.kit-list li { display: flex; align-items: baseline; gap: 10px; font-size: 1rem; }
.kit-list small { color: var(--muted); }
.kit-num { font-family: var(--mono); font-weight: 700;
    font-size: 1.4rem; color: var(--moss);
    min-width: 1.6em; text-align: right; white-space: nowrap; }
.kit-note { color: var(--muted); font-size: 0.85rem; margin-top: 14px; }

/* --- Course / GPX --- */
.course { margin-bottom: 22px; }
.course-head { margin-bottom: 16px; }
.course-name-field { margin-bottom: 16px; max-width: 420px; position: relative; }
/* z-index must clear the mini map below the field: Leaflet panes/controls run
   z 400–1000 in this same stacking context, so 50 hid the dropdown behind it. */
.course-suggest { position: absolute; top: 100%; left: 0; right: 0; z-index: 1100; margin-top: 6px;
    background: rgba(7, 18, 15, 0.98); border: 1px solid var(--line); border-radius: var(--r-card);
    box-shadow: var(--shadow-pop); max-height: 300px; overflow-y: auto; }
.suggest-item { display: flex; flex-direction: column; gap: 2px; width: 100%; text-align: left;
    background: none; border: none; border-bottom: 1px solid var(--line); padding: 10px 14px;
    cursor: pointer; color: var(--ink); font-family: inherit; }
.suggest-item:last-child { border-bottom: none; }
.suggest-item:hover { background: var(--glass-2); }
.suggest-name { font-weight: 600; font-size: 0.92rem; }
.suggest-meta { color: var(--muted); font-size: 0.8rem; }
/* "Still looking" while the search reaches past the local index (TG-326) —
   deliberately not a .suggest-item: there is nothing here to click yet. */
.suggest-wait { color: var(--muted); font-size: 0.86rem; padding: 10px 14px; }
.panel-sub { color: var(--muted); font-size: 0.88rem; margin-top: 4px; }
.panel-sub.rd-sec-lead { margin-top: 0; }   /* a section's first line, straight under its title */
/* "Community" on a course search result (TG-228): official matches rank first. */
.tg-community-badge { border: 1px solid currentColor; border-radius: var(--r-ctl);
    padding: 0 6px; font-size: 10px; opacity: 0.85; }
.inline-link { color: var(--accent); text-decoration: none; font-weight: 600; white-space: nowrap; }
.inline-link:hover { text-decoration: underline; }

.dropzone { display: flex; flex-direction: column; align-items: center; justify-content: center;
    gap: 8px; text-align: center; padding: 32px 20px; border: 1.5px dashed var(--line);
    border-radius: var(--radius); background: var(--glass-2); cursor: pointer;
    transition: border-color 0.15s, background 0.15s; }
.dropzone:hover, .dropzone.dragover { border-color: var(--accent); background: var(--fill-hover); }
.dz-icon { font-size: 2rem; }
.dz-icon .ico { width: 30px; height: 30px; color: var(--muted); }
.base-map-cap .ico { color: var(--teal); vertical-align: -2px; }
.tg-browse-btn .ico { color: var(--accent); }
.dz-text { color: var(--ink); font-size: 0.98rem; }
.dz-text strong { font-weight: 600; }
.dz-status { color: var(--muted); font-size: 0.85rem; min-height: 1em; }
.dz-status.error { color: var(--bad); }
/* As a rail row of its own (TG-320) it must not hold a blank line open — the
   rail's gap would space an empty box like a real one. */
.rd-status:empty { display: none; }

.course-title { font-family: var(--display); font-weight: 800; font-size: 1.4rem; color: var(--ink);
    margin: 0 2px 12px; line-height: 1.2; }
.course-stats { display: flex; flex-wrap: wrap; gap: 10px; margin: 22px 0 16px; align-items: stretch; }
/* One line per label, one baseline per row (TG-301). The tiles are a flex row
   of equal-ish boxes, so a label that wraps makes its own tile taller and
   drops its figure below the others' — the row stops reading as a set of
   readings and starts reading as six unrelated boxes. The label gets its own
   line and is not allowed to take a second; the tiles stretch to a common
   height and the figure is pushed to the bottom of each, so every figure sits
   on one baseline whether or not its label needed the room. */
.course-stat { background: var(--glass-2); border: 1px solid var(--line); border-radius: var(--r-card);
    padding: 10px 14px; flex: 1 1 110px;
    display: flex; flex-direction: column; }
.course-stat .cs-label { color: var(--muted); font-family: var(--mono); font-weight: 500;
    font-size: max(0.72rem, var(--mono-cap)); text-transform: uppercase; letter-spacing: 0.1em;
    white-space: nowrap; }
/* The qualifier that used to ride in the unit slot after the figure. Muted,
   un-tracked and sentence case: it is a footnote to the label, not a label. */
.course-stat .cs-sub { display: block; font-family: var(--body); font-weight: 400;
    font-size: 0.7rem; letter-spacing: normal; text-transform: none;
    color: var(--muted); opacity: 0.85; white-space: nowrap; }
.course-stat .cs-val { font-family: var(--mono); font-weight: 700;
    font-size: 1.2rem; margin-top: auto; padding-top: 3px; }
.course-stat .cs-val .unit { font-size: 0.72rem; font-weight: 500; letter-spacing: 0.08em;
    text-transform: uppercase; color: var(--muted); margin-left: 4px; }
.cs-gain .cs-val { color: var(--orange); }
.cs-loss .cs-val { color: var(--aqua); }
.cs-dist .cs-val { color: var(--moss); }
.cs-high .cs-val { color: var(--gold); }
.cs-low  .cs-val { color: var(--teal); }
.cs-oxy  .cs-val { color: var(--plum); }

/* Surface mix (KRI-54). Deliberately the SAME tokens the map route, the profile
   ribbon and the pace-table chips already use — grey paved · gold gravel · moss
   trail · faded unknown — so the colour language is learned once and a theme
   restyles all of it together. The tile's value takes the dominant surface's
   colour, so the number and the strip below it agree at a glance. */
.cs-surface .cs-val { color: var(--moss); }          /* d-trail (default) */
.cs-surface.d-gravel .cs-val { color: var(--gold); }
.cs-surface.d-paved  .cs-val { color: var(--muted); }
.cs-mix { display: flex; height: 5px; border-radius: 3px; overflow: hidden; margin-top: 8px; }
.cs-mix i { display: block; min-width: 1px; }
.sfx-trail   { background: var(--moss); }
.sfx-gravel  { background: var(--gold); }
.sfx-paved   { background: var(--muted); }
/* Unknown is an ABSENCE of data, not a surface — same hue, dialled back so it
   reads as "nothing here" rather than as a fourth category. */
.sfx-unknown { background: var(--muted); opacity: 0.3; }

/* Event attribution under the course title (KRI-40) */
/* Event byline ("Jun 27, 2026 · by Western States Endurance Run"): a second
   line INSIDE the course card's header band, under the title (TG-318). It is
   a summary child, so that band wraps; body face, no caps, and the chevron
   stays on the title row (its order 0 beats the byline's 11). Links inside a
   <summary> follow without toggling the card — the anchor's activation wins. */
#course-view-panel > summary { flex-wrap: wrap; row-gap: 2px; }
.rd-section > summary .course-event-line {
    flex: 1 1 100%; order: 11; margin: 0 0 0 35px;
    font-family: var(--body); font-weight: 400; font-size: 0.82rem; color: var(--muted);
    text-transform: none; letter-spacing: normal;
}
.course-event-line a { color: var(--accent); text-decoration: none; }
.course-event-line a:hover { text-decoration: underline; }
/* "Train for this race →" (KRI-82) — a CTA, so it gets the site's orange. */
.course-event-line a.rd-train-link { color: var(--accent); font-weight: 600; }
.course-event-line[hidden] { display: none; }

/* Aid-station markers (map) + matching badges (plan table) */
.leaflet-div-icon.aid-marker-wrap { background: transparent; border: 0; }
.aid-marker { display: flex; align-items: center; justify-content: center; width: 26px; height: 26px;
    border-radius: 50%; color: #06140f; font-family: var(--display); font-weight: 800;
    font-size: 0.92rem; border: 2px solid rgba(255,255,255,0.92); box-shadow: 0 1px 5px rgba(0,0,0,0.5); }
.aid-badge { display: inline-flex; align-items: center; justify-content: center; width: 18px; height: 18px;
    border-radius: 50%; color: #06140f; font-family: var(--display); font-weight: 800; font-size: 0.72rem;
    margin-right: 8px; vertical-align: middle; }

/* isolation: Leaflet's panes (z 400) and controls (z 1000) otherwise escape into
   the page's stacking order and paint over anything sticky above the map — on
   the Run Planner the pane's section-chip row (z 7) vanished under the tiles as
   the plan scrolled. One stacking context per map keeps Leaflet's z-indexes
   internal; .map-expanded still lifts the whole wrap with its own z-index. */
.map-wrap { position: relative; isolation: isolate; border-radius: var(--radius); overflow: hidden; border: 1px solid var(--line); }
/* One frame around the map (TG-300). The Course panel drew three: the section
   card's border, then 24px of padding, then the map wrap's own 1px. The card's
   edge is the frame — the map loses its border and sits 12px inside, which is
   also 24px of width the profile and the stat tiles get back on a phone.
   Scoped to the panel rather than to .map-wrap: below 861px the pane merge
   moves this content into the rail, where .rd-merged has already stripped the
   card and the map's border becomes the only one there is. */
#course-view-panel { padding: 12px; }
#course-view-panel > summary { margin: -12px; padding: 10px 12px; }
/* The id selector above rewrites the shared band's whole margin shorthand, so
   the open/collapsed bottom margins from `.rd-section[open] > summary` lose on
   specificity and have to be restated at 12px here — without them the band
   keeps its -12px bottom and the map rides up over the header (and over its
   bottom rule). This card's band is the only two-row one on the page (the
   event byline sits under the title), so it also takes 10px of vertical
   padding rather than the shared 13px: two rows at 13px read as a slab. */
#course-view-panel:not([open]) > summary { margin-bottom: -12px; }
#course-view-panel[open] > summary { margin-bottom: 12px; }
#course-view-panel .map-wrap { border: none; }
#course-map { height: 425px; width: 100%; background: var(--bg-1); }

/* 3D terrain view (MapLibre) — overlays the 2D map within the same wrap.
   position:absolute !important beats MapLibre's own .maplibregl-map{position:relative}
   (its stylesheet loads later); z-index sits above Leaflet's panes/controls. */
.tg-3d { position: absolute !important; inset: 0; z-index: 1200; background: var(--bg-1); }
.tg-3d-badge { position: absolute; top: 10px; left: 50%; transform: translateX(-50%); z-index: 1300;
    font-family: var(--mono); font-weight: 700; font-size: 0.66rem; letter-spacing: 0.06em;
    text-transform: uppercase; color: var(--gold); background: rgba(7,18,15,0.85);
    border: 1px solid rgba(255,210,63,0.45); border-radius: var(--r-ctl); padding: 4px 10px; }
.tg-3d-exit { position: absolute; top: 10px; right: 10px; z-index: 1300; cursor: pointer;
    background: rgba(7,18,15,0.92); color: var(--ink); border: 1px solid var(--line); border-radius: 8px;
    padding: 7px 12px; font-family: var(--body); font-weight: 700; font-size: 0.82rem;
    box-shadow: 0 2px 8px rgba(0,0,0,0.45); }
.tg-3d-exit:hover { border-color: var(--edge-soft); color: var(--accent); }
.map-3d-btn { display: flex !important; align-items: center; justify-content: center; width: 32px; height: 32px;
    background: rgba(7,18,15,0.92); color: var(--ink); border: 1px solid var(--line); border-radius: 8px;
    font-family: var(--body); font-weight: 800; font-size: 0.72rem; line-height: 1; text-decoration: none;
    box-shadow: 0 2px 8px rgba(0,0,0,0.45); }
.map-3d-btn:hover { border-color: var(--edge-soft); color: var(--accent); }
/* Hide the 2D Leaflet controls while the 3D overlay is up (they drive the hidden map) */
.map-wrap.tg-3d-active .leaflet-control-container { display: none; }
/* 3D flythrough control (play/pause + speed), bottom-left of the 3D view */
.tg-3d-fly { position: absolute; left: 10px; bottom: 10px; z-index: 1300; display: flex; gap: 6px; }
.tg-3d-fly-btn, .tg-3d-fly-speed { cursor: pointer; background: rgba(7,18,15,0.92); color: var(--ink);
    border: 1px solid var(--line); border-radius: 8px; font-family: var(--body); font-weight: 700;
    box-shadow: 0 2px 8px rgba(0,0,0,0.45); line-height: 1; }
.tg-3d-fly-btn { padding: 9px 14px; font-size: 0.85rem; }
.tg-3d-fly-speed { padding: 9px 11px; font-size: 0.8rem; color: var(--muted); min-width: 36px; }
.tg-3d-fly-btn:hover { border-color: var(--edge-soft); color: var(--accent); }
.tg-3d-fly-speed:hover { border-color: var(--edge-soft); color: var(--ink); }

/* Locate-me button — same glass block as the 3D/expand controls beside it.
   No resting `color`: Leaflet's `.leaflet-container a` blue wins the cascade on
   those neighbours too, so omitting it here matches them by the same mechanism. */
.map-locate-btn { display: flex !important; align-items: center; justify-content: center;
    width: 32px; height: 32px; background: rgba(7,18,15,0.92);
    border: 1px solid var(--line); border-radius: 8px; line-height: 1;
    text-decoration: none; box-shadow: 0 2px 8px rgba(0,0,0,0.45); }
.map-locate-btn svg { width: 16px; height: 16px; display: block; }
.map-locate-btn:hover { border-color: var(--edge-soft); color: var(--accent); }

/* Expand-to-fullscreen button + state */
.map-expand-btn { display: flex !important; align-items: center; justify-content: center;
    width: 32px; height: 32px; background: rgba(7,18,15,0.92); color: var(--ink);
    border: 1px solid var(--line); border-radius: 8px; font-size: 1.05rem; line-height: 1;
    text-decoration: none; box-shadow: 0 2px 8px rgba(0,0,0,0.45); }
.map-expand-btn:hover { border-color: var(--edge-soft); color: var(--accent); }
/* Course playback control (play / pause + speed) */
.map-play { display: flex; gap: 6px; }
.map-play-btn, .map-speed-btn { display: flex !important; align-items: center; justify-content: center;
    height: 32px; background: rgba(7,18,15,0.92); color: var(--ink); border: 1px solid var(--line);
    border-radius: 8px; font-size: 0.95rem; line-height: 1; text-decoration: none;
    box-shadow: 0 2px 8px rgba(0,0,0,0.45); font-family: var(--body); font-weight: 700; }
.map-play-btn { width: 32px; }
.map-speed-btn { min-width: 34px; padding: 0 8px; font-size: 0.82rem; color: var(--muted); }
.map-play-btn:hover { border-color: var(--edge-soft); color: var(--accent); }
.map-speed-btn:hover { border-color: var(--edge-soft); color: var(--ink); }
.map-wrap.map-expanded { position: fixed; inset: 0; z-index: 9999; margin: 0;
    border: none; border-radius: 0; }
.map-wrap.map-expanded #course-map,
.map-wrap.map-expanded #lib-map,
.map-wrap.map-expanded #base-map { height: 100vh; height: 100dvh; }
body.map-modal-open { overflow: hidden; }
.leaflet-container { background: var(--bg-1); font-family: inherit; }

.elev-wrap { margin-top: 16px; }
.elev-head { color: var(--muted); font-size: 0.85rem; font-weight: 600; margin-bottom: 8px; }
.elev-head small { font-weight: 400; opacity: 0.8; }
/* Elevation-profile overlay toggles (Aid stations / Climbs / Descents) */
.tg-elev-toggles { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 10px; }
.tg-tog { display: inline-flex; align-items: center; gap: 7px; font-family: var(--body);
    font-weight: 600; font-size: 0.8rem; color: var(--muted); background: var(--glass-2);
    border: 1px solid var(--line); border-radius: var(--r-ctl); padding: 6px 12px; cursor: pointer;
    transition: color 0.15s ease, border-color 0.15s ease, background 0.15s ease; }
.tg-tog:hover { color: var(--ink); }
/* An explicit display beats the UA [hidden] rule — restate it (same trap as
   nav.css). Without this the Night/Altitude pills "hidden" for irrelevant
   courses still rendered, lit, on every low flat race. */
.tg-tog[hidden] { display: none; }
.tg-tog .tg-dot { width: 9px; height: 9px; border-radius: 50%; background: var(--tg, #888);
    opacity: 0.3; transition: opacity 0.15s ease; }
.tg-tog.on { color: var(--ink); border-color: var(--tg, #888);
    background: color-mix(in srgb, var(--tg, #888) 13%, transparent); }
.tg-tog.on .tg-dot { opacity: 1; }
/* Distance-marker pill: the dot mimics the numbered puck on the map. */
.tg-tog-mile .tg-dot { width: 11px; height: 11px; background: rgba(7, 18, 15, 0.88);
    border: 1.5px solid var(--gold); }
.elev-canvas-wrap { position: relative; height: 260px; }
.elev-empty { color: var(--muted); font-size: 0.9rem; line-height: 1.5; padding: 28px 22px;
    text-align: center; border: 1px dashed var(--line); border-radius: var(--radius);
    background: var(--glass-2); }

/* Leaflet layer switcher — match the dark UI */
.leaflet-control-layers { background: rgba(7,18,15,0.92) !important; color: var(--ink);
    border: 1px solid var(--line) !important; border-radius: var(--r-card) !important;
    box-shadow: 0 8px 24px rgba(0,0,0,0.5) !important; }
.leaflet-control-layers-expanded { padding: 8px 12px !important; }
.leaflet-control-layers label { color: var(--ink); font-size: 0.85rem; font-weight: 500; margin: 2px 0; }
.leaflet-control-layers-selector { accent-color: var(--accent); margin-right: 6px; }
.leaflet-bar a { background: rgba(7,18,15,0.92); color: var(--ink); border-color: var(--line); }
.leaflet-bar a:hover { background: var(--fill-hover); }

/* Fueling-plan per-section summary — its own card below the metric cards */
.plan-summary { margin-top: 18px; }
.plan-summary .col-head { border-bottom: none; padding-bottom: 0; margin-bottom: 10px; }
.summary-text { color: var(--muted); font-size: 0.95rem; line-height: 1.55; margin-bottom: 14px; max-width: 64ch; }
.summary-text strong { color: var(--gold); font-weight: 700; }
/* Biggest-carries strip — replaces the old duplicate per-leg table. */
.carry-head { font-family: var(--mono); font-size: 0.75rem; font-weight: 500; letter-spacing: 0.1em;
    text-transform: uppercase; color: var(--muted); margin: 0 0 8px; }
.carry-strip { list-style: none; margin: 0; padding: 0; display: grid;
    grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 10px; }
.carry-strip li { display: flex; flex-direction: column; gap: 2px; padding: 12px 14px;
    background: rgba(255, 255, 255, 0.03); border: 1px solid var(--line); border-radius: var(--r-card); }
.carry-name { font-weight: 700; font-size: 0.92rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.carry-dist { color: var(--muted); font-family: var(--mono); font-size: 0.78rem; }
.carry-load { font-family: var(--mono); font-size: 0.92rem; font-weight: 700; margin-top: 4px; }

/* Race plan table */
.plan-wrap { margin-top: 20px; }
.plan-scroll { overflow-x: auto; margin-top: 10px; }
.plan-table { width: 100%; border-collapse: collapse; font-size: 0.92rem; }
.plan-table th { text-align: left; color: var(--muted); font-family: var(--mono); font-weight: 500;
    font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.1em;
    padding: 6px 10px; border-bottom: 1px solid var(--line); }
/* Every column but the first holds a figure. The mono face here, plus the shared
   tabular-figures rule at the foot of this file, is what makes a split table read
   down a column instead of across a ragged edge (AC-4). */
.plan-table th:not(:first-child), .plan-table td:not(:first-child) { text-align: right; white-space: nowrap; }
.plan-table td:not(:first-child), .cmp-table td:not(:first-child) { font-family: var(--mono); }
/* td inherits --ink by default — don't set color here, or it would out-specify the
   .t-* value classes when they're applied directly to a <td> (they'd stay white). */
.plan-table td { padding: 9px 10px; border-bottom: 1px solid var(--line); }
/* Zebra striping + a warm hover so it's easy to track a single row across the table. */
.plan-table tbody tr:nth-child(even) { background: color-mix(in srgb, var(--ink) 3%, transparent); }   /* ink-based: a stripe on white too */
.plan-table tbody tr:hover { background: var(--fill-hover); }
.plan-table tbody tr:hover td { border-bottom-color: var(--edge-soft); }
.plan-table .aid-name { color: var(--ink); font-weight: 500; }
/* Semantic value colours (match the elevation profile: climbs orange, descents blue).
   Scoped under .plan-table so they reliably win on a <td> as well as a <span>. */
.plan-table .t-up { color: var(--orange); font-weight: 600; }
.plan-table .t-down { color: var(--aqua); font-weight: 600; }
/* OSM surface rows + aid-leg badges (KRI-35) — tokens, so themes restyle free */
.plan-table .srf-trail { color: var(--moss); font-weight: 600; }
.plan-table .srf-gravel { color: var(--gold); font-weight: 600; }
.plan-table .srf-paved { color: var(--muted); font-weight: 600; }
.srf-chip { display: inline-block; margin-left: 6px; padding: 1px 8px; border-radius: var(--r-ctl);
    font-size: 0.68rem; font-weight: 700; letter-spacing: 0.04em;
    border: 1px solid currentColor; opacity: 0.9; vertical-align: 1px; }
.plan-table .t-pace { color: var(--gold); font-weight: 700; }
.plan-table .t-water { color: var(--teal); font-weight: 600; }
.plan-table .t-carb { color: var(--gold); font-weight: 600; }
.plan-table .t-sep { color: var(--muted); font-weight: 400; }
.plan-table .t-flat { color: var(--muted); }
/* Aid table at a glance (gpx.js): the Temp cell's cool→hot wash and the
   Water cell's volume bar (--w, the leg's share of the biggest carry). Data
   hues, so each theme tints them its own way. */
.plan-table td.t-tint { border-radius: 6px; }
.plan-table td.t-hot  { background: color-mix(in srgb, var(--c-climb) 20%, transparent); }
.plan-table td.t-warm { background: color-mix(in srgb, var(--c-fuel) 17%, transparent); }
.plan-table td.t-mild { background: color-mix(in srgb, var(--gold) 12%, transparent); }
.plan-table td.t-cool { background: color-mix(in srgb, var(--c-aid) 14%, transparent); }
.plan-table td.w-carry { border-radius: 6px;
    background: linear-gradient(90deg, color-mix(in srgb, var(--c-course) 13%, transparent) var(--w, 0%), transparent var(--w, 0%)); }
/* Climb steepness scale — gentle gold → moderate orange → steep coral. */
.plan-table .grade-soft { color: var(--gold); font-weight: 600; }
.plan-table .grade-mid { color: var(--orange); font-weight: 600; }
.plan-table .grade-hard { color: var(--coral); font-weight: 700; }
.aid-actions { display: inline-flex; gap: 4px; margin-left: 8px; vertical-align: middle; }
/* Post-race plan vs actual (2026-08). */
.cmp-drop { margin: 4px 0 10px; }
.cmp-drop.over { border-color: var(--plum); }
.cmp-headline { font-size: 0.98rem; margin: 10px 0; }
.cmp-early { color: var(--ok); font-weight: 700; }
.cmp-late { color: var(--bad); font-weight: 700; }
.cmp-table { width: 100%; border-collapse: collapse; font-size: 0.88rem; }
.cmp-table th { text-align: left; color: var(--muted); font-family: var(--mono); font-weight: 500;
    font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.1em;
    padding: 6px 10px 6px 0; border-bottom: 1px solid var(--line); }
.cmp-table td { padding: 6px 10px 6px 0; border-bottom: 1px solid var(--line); }
.cmp-table .cmp-miss td { color: var(--muted); opacity: 0.7; }
.cmp-fade { color: var(--muted); font-size: 0.88rem; margin: 12px 0 2px; }
.cmp-fade b { color: var(--plum); }
.cmp-fade a { color: var(--fern); }

/* RD verification chip on the course event line — flat outline, fern check. */
.tg-verified { display: inline-flex; align-items: center; gap: 4px; padding: 1px 8px;
    font-size: 0.78rem; font-weight: 600; color: var(--fern);
    border: 1px solid color-mix(in srgb, var(--fern) 50%, transparent); border-radius: var(--r-ctl); }
.tg-verified svg { width: 11px; height: 11px; fill: none; stroke: currentColor; stroke-width: 2.5;
    stroke-linecap: round; stroke-linejoin: round; }

/* Night marker: a stop reached in darkness (and the headlamp note's glyph). */
.aid-night { display: inline-block; margin-left: 6px; vertical-align: -1px; color: var(--violet); }
.aid-night svg { width: 12px; height: 12px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linejoin: round; }
#night-note { color: var(--muted); }
#night-note .aid-night { margin-left: 0; }

/* Cutoff badge under a stop's arrival — flat outline pill, buffer-colored.
   ok ≥ 1h of margin · tight < 1h · missed = planned arrival is past it.
   Status tokens, the split table's margin column's thresholds and hues
   (raceday.css .pace-cut-m): a state, not a data series. */
.aid-cutoff { display: block; width: fit-content; margin-top: 3px; padding: 1px 7px;
    font-size: 0.72rem; font-family: var(--body); white-space: nowrap;
    border: 1px solid var(--line); border-radius: var(--r-ctl); color: var(--muted); cursor: pointer; }
.aid-cutoff.cut-ok { border-color: color-mix(in srgb, var(--ok) 45%, transparent); color: var(--ok); }
/* Time at this stop (TG-309) — the cutoff pill's quieter sibling: same outline,
   no grading, because a dwell is a choice rather than a threat. */
.aid-dwell { display: block; width: fit-content; margin-top: 3px; padding: 1px 7px;
    font: 500 .7rem/1.5 var(--mono); font-variant-numeric: tabular-nums; white-space: nowrap;
    border: 1px solid var(--line); border-radius: var(--r-ctl); color: var(--muted); cursor: pointer; }
.aid-dwell:hover, .aid-dwell:focus-visible { border-color: color-mix(in srgb, var(--accent) 55%, transparent); color: var(--ink); }
/* A stop too close to the start or finish to carry time (TG-314). Not
   editable, so it must not look or behave editable — the cursor and the hover
   response are what the row uses to say "this one is telling you something,
   not offering you something". */
.aid-dwell-off { cursor: default; border-style: dashed; opacity: 0.75; }
.aid-dwell-off:hover, .aid-dwell-off:focus-visible { border-color: var(--line); color: var(--muted); }
/* The 95% guard is binding: the number shown is scaled, not what was set. */
.aid-dwell-scaled { border-color: color-mix(in srgb, var(--gold) 55%, transparent); color: var(--gold); }
.aid-cutoff.cut-tight { border-color: color-mix(in srgb, var(--warn) 55%, transparent); color: var(--warn); }
.aid-cutoff.cut-missed { border-color: color-mix(in srgb, var(--bad) 60%, transparent); color: var(--bad); }
/* A pill that has something to say (TG-310): the demand goes on its own line
   inside the pill, so the buffer stays scannable and the required pace reads
   as the answer to it. nowrap comes off — the sentence is longer than the
   column, and clipping a number a runner needs is worse than a second line. */
.aid-cutoff.cut-warn { white-space: normal; max-width: 36ch; line-height: 1.35; }
.aid-cutoff .cut-need { display: block; font-weight: 600; }
.aid-rm, .aid-edit, .aid-cut { background: none; border: none; color: var(--muted); cursor: pointer;
    line-height: 1; padding: 2px 4px; border-radius: 6px; }
.aid-rm { font-size: 1.1rem; }
.aid-edit { font-size: 0.95rem; }
.aid-rm:hover { color: var(--ink); background: var(--fill-hover); }
.aid-edit:hover, .aid-cut:hover { color: var(--accent); background: var(--fill-hover); }
.aid-cut svg { width: 13px; height: 13px; fill: none; stroke: currentColor; stroke-width: 2; }
.aid-fixed { color: var(--muted); font-weight: 600; }
.plan-empty { color: var(--muted); font-size: 0.85rem; padding: 12px 2px; }
.plan-note { color: var(--muted); font-size: 0.8rem; margin-top: 8px; }
.plan-note strong { color: var(--ink); font-weight: 600; }

.aid-add { display: flex; gap: 8px; margin-top: 12px; flex-wrap: wrap; }
.aid-add #aid-dist { flex: 0 0 80px; }
.aid-add #aid-name { flex: 1 1 140px; }
.aid-add .estimate-btn { flex: 0 0 auto; }

.course-actions { margin-top: 18px; display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.course-actions .course-clear { margin-top: 0; }
.save-status { font-size: 0.85rem; color: var(--muted); }
.save-status.ok { color: var(--ok); }
.save-status.warn { color: var(--warn); }

.course-clear { margin-top: 16px; background: transparent; color: var(--muted);
    border: 1px solid var(--line); border-radius: var(--r-ctl); padding: 8px 16px; cursor: pointer;
    font-family: inherit; font-weight: 600; font-size: 0.85rem; transition: all 0.15s; }
.course-clear:hover { color: var(--ink); border-color: var(--edge-soft); background: var(--fill-hover); }

/* --- Course library page --- */
.topnav { display: flex; gap: 8px; flex-wrap: wrap; }
.back-link { color: var(--muted); text-decoration: none; font-weight: 600; font-size: 0.9rem;
    padding: 8px 16px; border: 1px solid var(--line); border-radius: var(--r-ctl); }
.back-link:hover { color: var(--ink); border-color: var(--edge-soft); background: var(--fill-hover); }
/* Primary in-app nav — mirrors the landing page (.nav from home.css). */
.nav { display: flex; align-items: center; gap: 4px; flex-wrap: wrap; }
.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); }
.nav .beta-tag { font-size: 0.62rem; font-weight: 700; color: var(--gold); margin-left: 4px;
    letter-spacing: 0.05em; vertical-align: super; }
.lib-toolbar { display: flex; gap: 10px; align-items: center; margin-bottom: 20px; flex-wrap: wrap; }
.lib-search { flex: 1 1 280px; max-width: 480px; }
.lib-manage { background: var(--glass-2); color: var(--muted); border: 1px solid var(--line);
    border-radius: var(--r-ctl); padding: 9px 16px; font-family: inherit; font-weight: 600; font-size: 0.85rem;
    cursor: pointer; white-space: nowrap; }
.lib-manage:hover { color: var(--ink); border-color: var(--edge-soft); background: var(--fill-hover); }
.lib-manage.active { color: var(--accent); border-color: var(--accent); background: var(--fill-selected); }
.lib-card { position: relative; }
.lib-del { position: absolute; top: 8px; right: 8px; width: 24px; height: 24px; border-radius: 50%;
    border: 1px solid var(--line); background: rgba(7,18,15,0.85); color: var(--muted); cursor: pointer;
    font-size: 1.1rem; line-height: 1; display: flex; align-items: center; justify-content: center; }
.lib-del:hover { color: var(--ink); border-color: var(--edge-soft); background: var(--fill-hover); }
.lib-count { color: var(--muted); font-size: 0.85rem; margin: 2px 2px 16px; }
.lib-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(230px, 1fr)); gap: 16px; }

.lib-card { position: relative; display: flex; flex-direction: column; gap: 8px; text-decoration: none;
    text-align: left; width: 100%; font-family: inherit; color: var(--ink); cursor: pointer;
    background: var(--glass-2); border: 1px solid var(--line); border-radius: var(--radius);
    padding: 18px; transition: border-color 0.15s, transform 0.15s, background 0.15s; }
.lib-card:hover { border-color: var(--edge-soft); transform: translateY(-2px);
    background: var(--fill-hover); }
.lib-card:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.lib-card-cta { color: var(--accent); font-weight: 600; }
.lib-card-name { font-family: var(--display); font-weight: 700; font-size: 1.02rem; color: var(--ink); }
.lib-card-stats { display: flex; flex-wrap: wrap; gap: 14px; color: var(--muted); font-size: 0.9rem; }
.lib-card-stats strong { color: var(--ink); font-family: var(--display); }
.lib-card-date { color: var(--teal); font-size: 0.85rem; }
.lib-card-foot { color: var(--muted); font-size: 0.78rem; margin-top: auto; opacity: 0.8; }
.lib-tag { color: var(--gold); font-size: 0.78rem; }
.lib-empty { color: var(--muted); font-size: 0.95rem; padding: 24px 4px; }

/* Course preview window (modal) */
.pv-modal-backdrop { position: fixed; inset: 0; z-index: 9000; display: flex; align-items: flex-start;
    justify-content: center; padding: 4vh 16px; overflow-y: auto;
    background: rgba(3, 9, 7, 0.82); }
.pv-modal-backdrop[hidden] { display: none; }
.pv-modal { position: relative; width: 100%; max-width: 800px; margin: auto; background: var(--bg-1);
    border: 1px solid var(--line); border-radius: var(--radius); box-shadow: var(--shadow-pop); padding: 26px; }
.pv-close { position: absolute; top: 16px; right: 16px; width: 34px; height: 34px; border-radius: 50%;
    border: 1px solid var(--line); background: var(--glass-2); color: var(--muted); cursor: pointer;
    font-size: 1rem; line-height: 1; display: flex; align-items: center; justify-content: center; z-index: 2; }
.pv-close:hover { color: var(--ink); border-color: var(--edge-soft); background: var(--fill-hover); }
.lib-preview-head { margin-bottom: 16px; padding-right: 40px; }
.pv-meta { color: var(--muted); font-size: 0.88rem; margin-top: 6px; }
#pv-map { height: 450px; width: 100%; background: var(--bg-1); }
.pv-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; margin-top: 20px;
    padding-top: 18px; border-top: 1px solid var(--line); }
.pv-actions-label { color: var(--muted); font-size: 0.9rem; margin-right: 2px; }
body.pv-open { overflow: hidden; }

/* "Open in…" chooser modal (library page) */
.tg-modal-backdrop { position: fixed; inset: 0; z-index: 10000; display: flex; align-items: center;
    justify-content: center; padding: 20px; background: rgba(3, 9, 7, 0.8); }
.tg-modal { width: 100%; max-width: 440px; background: var(--bg-1); border: 1px solid var(--line);
    border-radius: var(--radius); box-shadow: var(--shadow-pop); padding: 24px; }
.tg-modal-eyebrow { color: var(--accent); font-family: var(--mono); font-weight: 500;
    letter-spacing: 0.1em; text-transform: uppercase; font-size: 0.72rem; }
.tg-modal-title { font-family: var(--display); font-weight: 700; font-size: 1.2rem; margin-top: 6px; }
.tg-modal-sub { color: var(--muted); font-size: 0.9rem; margin-top: 4px; }
.tg-modal-choices { display: flex; flex-direction: column; gap: 10px; margin: 18px 0 14px; }
.tg-modal-choice { display: flex; flex-direction: column; gap: 3px; text-align: left; cursor: pointer;
    background: var(--glass-2); border: 1px solid var(--line); border-radius: var(--r-card); padding: 14px 16px;
    color: var(--ink); font-family: inherit; transition: border-color 0.15s, background 0.15s; }
.tg-modal-choice:hover { border-color: var(--edge-soft); background: var(--fill-hover); }
.tg-choice-title { font-family: var(--display); font-weight: 600; font-size: 1rem; }
.tg-choice-sub { color: var(--muted); font-size: 0.83rem; }
.tg-modal-cancel { background: transparent; color: var(--muted); border: 1px solid var(--line);
    border-radius: var(--r-ctl); padding: 8px 16px; cursor: pointer; font-family: inherit; font-weight: 600;
    font-size: 0.85rem; }
.tg-modal-cancel:hover { color: var(--ink); border-color: var(--muted); }

/* --- TrailGoat AI coach --- */
.coach { margin-bottom: 22px; }
.coach-messages { display: flex; flex-direction: column; gap: 10px; margin: 16px 0; }
/* Assistant bubbles render minimal markdown (bold + bullets) — see fuel-coach.js. */
.coach-msg ul { margin: 4px 0; padding-left: 18px; }
.coach-msg li { margin: 2px 0; }
.coach-msg strong { color: var(--gold); font-weight: 700; }
.coach-msg { max-width: 85%; padding: 10px 14px; border-radius: var(--r-card); font-size: 0.92rem; line-height: 1.5;
    white-space: pre-wrap; }
.coach-user { align-self: flex-end; background: var(--fill-selected); color: var(--ink); border-bottom-right-radius: 4px; }
.coach-assistant { align-self: flex-start; background: rgba(25,195,177,0.08); border: 1px solid rgba(25,195,177,0.28);
    border-left: 3px solid var(--teal); color: var(--ink); border-bottom-left-radius: 4px; }
.coach-assistant strong { color: var(--teal); }
.coach-thinking { color: var(--teal); font-style: italic; }
.coach-suggestions { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 14px; }
.coach-suggestions[hidden] { display: none; }   /* the flex above was beating [hidden] */
.coach-chip { background: var(--fill-idle); color: var(--ink); border: 1px solid var(--edge-soft);
    border-radius: var(--r-ctl); padding: 7px 14px; font-family: inherit; font-size: 0.85rem; cursor: pointer;
    transition: all 0.15s; }
.coach-chip:hover { border-color: var(--edge); background: var(--fill-hover); color: var(--ink); }
.coach-input { display: flex; gap: 10px; }
/* The runner's answer to "may we keep your questions?", with a way to change it. */
.coach-consent { margin: 8px 2px 0; font-size: .8rem; color: var(--muted); line-height: 1.45; }
.coach-consent[hidden] { display: none; }
.coach-consent-change { font: inherit; font-weight: 600; color: var(--accent); background: none; border: 0;
    padding: 0; cursor: pointer; text-decoration: underline; text-underline-offset: 3px; }
.coach-consent-change:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 2px; }
.coach-input input { flex: 1; }
/* Locked Capra (KRI-199): the panel stays fully readable — hiding it would make
   the feature look absent rather than paid — but the controls go inert and the
   whole row becomes one click target for the upgrade cards. */
.coach-chip:disabled { opacity: .55; pointer-events: none; }
.coach-suggestions.coach-locked { cursor: pointer; }
.coach-chip:disabled:hover { border-color: var(--edge-soft); background: var(--fill-idle); color: var(--ink); }
.coach-input.coach-locked { cursor: pointer; }
.coach-input.coach-locked input { opacity: .55; pointer-events: none; }
.coach-input.coach-locked .export-btn { opacity: .55; pointer-events: none; }
.coach-input .export-btn { flex: 0 0 auto; }

/* Streaming (the Capra idea): stage copy until the first token, then a caret
   while tokens arrive. The caret is the bubble's own ink, not a new hue. */
.coach-streaming::after { content: ''; display: inline-block; width: 0.5em; height: 1em; margin-left: 2px;
    vertical-align: -0.15em; background: currentColor; opacity: .55; border-radius: 1px;
    animation: capra-caret 1s steps(2, start) infinite; }
@keyframes capra-caret { to { visibility: hidden; } }
.coach-thinking::before { content: ''; display: inline-block; width: 7px; height: 7px; margin-right: 8px;
    border-radius: 50%; background: var(--gold); vertical-align: 0.05em; animation: capra-pulse 1.2s var(--ease-snap) infinite; }
@keyframes capra-pulse { 50% { opacity: .25; } }
@media (prefers-reduced-motion: reduce) {
    .coach-streaming::after, .coach-thinking::before { animation: none; }
}
/* Where the reply's suggestions went — one line under the answer. */
.capra-note { margin-top: 8px; padding-top: 8px; border-top: 1px solid var(--line); white-space: normal; }
.capra-note .inline-link, .capra-pop .inline-link { background: none; border: 0; padding: 0; font: inherit;
    font-size: 0.85rem; font-weight: 600; cursor: pointer; }
.capra-note .inline-link:focus-visible, .capra-pop .inline-link:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 2px; }

/* Capra's chip on an aid-table row: small, gold (Capra's brand hue — the one
   thing on the table that is an opinion rather than a number), and a button,
   so the printout's table clone drops it with the other controls. */
.capra-sug { display: inline-flex; align-items: center; gap: 5px; margin: 2px 0 2px 8px; padding: 2px 8px;
    min-height: 24px; max-width: 100%; border-radius: var(--r-ctl); font: inherit; font-size: 0.78rem; line-height: 1.2;
    color: var(--ink); background: color-mix(in srgb, var(--gold) 10%, transparent);
    border: 1px solid color-mix(in srgb, var(--gold) 45%, transparent); cursor: pointer; vertical-align: middle;
    text-align: left; transition: background var(--dur-ctl) var(--ease-snap), border-color var(--dur-ctl) var(--ease-snap); }
.capra-sug:hover, .capra-sug[aria-expanded="true"] { background: color-mix(in srgb, var(--gold) 18%, transparent);
    border-color: var(--gold); }
.capra-sug:focus-visible { outline: 2px solid var(--gold); outline-offset: 2px; }
.capra-sug-by { font-family: var(--mono); font-size: 0.64rem; font-weight: 700; letter-spacing: 0.08em;
    text-transform: uppercase; color: var(--gold); }
.capra-pop { position: fixed; z-index: 60; padding: 12px 14px; background: var(--bg-2); color: var(--ink);
    border: 1px solid color-mix(in srgb, var(--gold) 45%, transparent); border-left: 3px solid var(--gold);
    border-radius: var(--r-card); box-shadow: var(--shadow-pop); font-size: 0.88rem;
    animation: capra-pop-in var(--dur-layer) var(--ease-snap); }
@keyframes capra-pop-in { from { opacity: 0; transform: translateY(-4px); } }
.capra-pop-head { font-weight: 700; line-height: 1.35; }
.capra-pop-at { font-weight: 400; color: var(--muted); }
.capra-pop-why { margin: 6px 0 0; color: var(--ink); line-height: 1.45; }
.capra-pop-actions { display: flex; align-items: center; gap: 12px; margin-top: 10px; }
@media (prefers-reduced-motion: reduce) { .capra-pop { animation: none; } }
@media (max-width: 860px) {
    .capra-sug { min-height: 32px; margin-left: 0; }
    .capra-pop-actions .export-btn, .capra-pop-actions .inline-link { min-height: 44px; }
}

/* Footer */
.foot { text-align: center; color: var(--muted); font-size: 0.85rem; padding: 30px 24px 50px; }
.foot a { color: var(--accent); text-decoration: none; }
.foot a:hover { text-decoration: underline; }

/* Methodology */
.methodology p { color: var(--muted); font-size: 0.9rem; margin-top: 12px; }
.methodology .disclaimer { color: var(--muted); }
.methodology .disclaimer strong { color: var(--gold); }

/* =========================================================================
   Two-pane app shell (Planner + Fuel) — body.split-page only.
   Mobile keeps the natural stacked, page-scroll flow (the grid/scroll panes
   only kick in at >=861px). Left = setup/inputs, right = map + outputs.
   ========================================================================= */
/* Empty-state placeholder in a result pane — shown until a course loads.
   (Global so it works on mobile too; the sibling toggle hides it once the
   course view is un-hidden by the page JS.) */
.split-empty {
    display: flex; flex-direction: column; align-items: center; justify-content: center;
    gap: 12px; min-height: 280px; text-align: center; color: var(--muted); padding: 20px;
}
.split-empty-icon { font-size: 2.6rem; opacity: 0.7; }
.split-empty p { max-width: 26rem; }
.course-view:not([hidden]) ~ .split-empty { display: none; }   /* loaded → hide placeholder */

/* Empty state = a navigable base map of the user's area (before a course loads). */
.split-empty.has-basemap { display: block; min-height: 0; padding: 0; text-align: left; }
#base-map { height: clamp(450px, 72vh, 775px); width: 100%; background: var(--bg-1); }
#base-map[hidden] { display: none; }   /* explicit height above would beat [hidden] */
/* Waiting / "open a course" cards shown in the map slot (base-map.js states 1 & 3). */
.base-map-note {
    height: clamp(450px, 72vh, 775px);
    display: flex; flex-direction: column; align-items: center; justify-content: center;
    gap: 8px; text-align: center; padding: 24px;
    border: 1px dashed var(--line); border-radius: var(--radius);
    background: var(--glass); color: var(--ink); font-size: 0.95rem;
}
.base-map-note strong { color: var(--ink); font-size: 1.1rem; font-family: var(--display); }
.base-map-note span { max-width: 46ch; }
.base-map-note .bm-goat { display: block; width: 49px; height: 64px; margin: 0 auto;
    filter: drop-shadow(0 2px 3px rgba(0,0,0,0.55)); }
.base-map-note .bm-retry {
    margin-top: 10px; font: inherit; font-size: 0.86rem; font-weight: 600;
    color: var(--ink); background: none; border: 1px solid var(--edge);
    border-radius: var(--r-ctl); padding: 6px 14px; cursor: pointer;
}
.base-map-note .bm-retry:hover { background: var(--fill-hover); }
/* Featured-course cards (KRI-56) — the one-click wow moment. */
.bm-featured { display: flex; gap: 10px; flex-wrap: wrap; justify-content: center;
    margin-top: 14px; width: 100%; max-width: 640px; }
.bm-card { flex: 1 1 170px; max-width: 210px; display: grid; gap: 3px; text-align: left;
    text-decoration: none; border: 1px solid var(--line); border-radius: var(--r-card);
    padding: 12px 14px; background: var(--glass-2, rgba(255,255,255,0.03));
    transition: border-color 0.15s, background 0.15s, transform 0.15s; }
.bm-card:hover { border-color: var(--edge-soft); background: var(--fill-hover);
    transform: translateY(-1px); }
.bm-card-name { font-weight: 700; color: var(--ink); font-family: var(--display);
    font-size: 0.95rem; }
.bm-card-meta { color: var(--muted); font-size: 0.78rem; }
.bm-card-go { color: var(--accent); font-size: 0.78rem; font-weight: 600; margin-top: 4px; }
.bm-fail { color: var(--bad); font-size: 0.82rem; margin-top: 6px; }
@media (max-width: 560px) {
    .bm-featured { flex-direction: column; }
    .bm-card { max-width: none; }
}
.base-map-cap { color: var(--muted); font-size: 0.86rem; margin: 12px 2px 0; max-width: none;
    display: flex; align-items: center; gap: 6px 10px; flex-wrap: wrap; }
.base-map-cap strong { color: var(--ink); }
.base-map-sub { opacity: 0.8; }
/* "You are here" mascot marker on the base map (neutralise Leaflet's default div-icon box). */
.tg-you-goat { background: none; border: 0; text-align: center;
    filter: drop-shadow(0 2px 3px rgba(0,0,0,0.55)); }
.tg-you-goat img { display: block; width: 33px; height: 44px; }

@media (min-width: 861px) {
    /* Scoped to the split-page layout ON PURPOSE. An unscoped `html {height:100%}`
       clamps the background POSITIONING AREA to one viewport on every page that
       loads this sheet, so a body gradient tiles once per screen down a long
       page (the crew sheet showed a seam every ~900px). The planner hides that
       behind its fixed .backdrop; pages without one don't.
       Two rules, not one list: an unsupported :has() would invalidate the whole
       list and take the split-page rule down with it. */
    html:has(body.split-page) { height: 100%; }
    body.split-page { height: 100%; }
    body.split-page {
        display: flex; flex-direction: column;
        height: 100vh; height: 100dvh;
        overflow: hidden;                         /* the page itself never scrolls */
    }
    body.split-page .topbar { flex: 0 0 auto; }

    body.split-page .wrap {
        max-width: none; width: 100%;
        margin: 0; padding: 0 clamp(16px, 2vw, 30px);
        flex: 1 1 auto; min-height: 0;
        display: flex; flex-direction: column;
    }
    body.split-page .foot { display: none; }      /* reclaim vertical space */

    /* Hero now lives inside .split-left so the right-pane map rises to the top, inline
       with the hero text (reclaims the wasted top-right space). */
    body.split-page .hero { flex: 0 0 auto; margin: 0; }
    body.split-page .hero h1 { font-size: clamp(1.35rem, 2.2vw, 1.85rem); }
    body.split-page .hero-sub { display: none; }

    /* Two columns fill the remaining height; each scrolls on its own. */
    body.split-page .app-split {
        flex: 1 1 auto; min-height: 0;
        display: grid; grid-template-columns: minmax(320px, 400px) 1fr; gap: 18px;
        align-items: stretch; padding-bottom: 14px;
    }
    body.split-page .split-left,
    body.split-page .split-right {
        min-height: 0; overflow-y: auto; overscroll-behavior: contain;
        display: flex; flex-direction: column; gap: 16px; padding-right: 4px;
    }
    body.split-page .split-left::-webkit-scrollbar,
    body.split-page .split-right::-webkit-scrollbar { width: 9px; }
    body.split-page .split-left::-webkit-scrollbar-thumb,
    body.split-page .split-right::-webkit-scrollbar-thumb { background: color-mix(in srgb, var(--ink) 14%, transparent); border-radius: 8px; }

    /* Panels inside the panes shouldn't add their own outer margins. */
    body.split-page .split-left > .panel,
    body.split-page .split-right > .panel { margin: 0; }
}

/* ===== Build your pack (food database) ===== */
.fp { margin-top: 6px; }
.fp-empty { color: var(--muted); font-size: 0.9rem; line-height: 1.5; margin: 4px 0 0; }

/* Coverage bars: pack total vs the engine's race targets. Full-width card → lay the
   bars out in a responsive grid; glossy 3D pills with a moving sheen. */
.fp-cov-sub-head { font-family: var(--body); font-weight: 400; font-size: 0.78rem; color: var(--muted); }
.fp-coverage { display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 16px 26px; margin-top: 12px; }
.fp-coverage[hidden] { display: none; }
.fp-cov-row { display: flex; flex-direction: column; gap: 7px; }
.fp-cov-caf { --acc: var(--c-climb); }   /* caffeine: an informational total */
.fp-cov-top { display: flex; align-items: baseline; justify-content: space-between; }
.fp-cov-label { font-family: var(--display); font-weight: 600; font-size: 0.95rem; color: var(--ink); }
.fp-pct { font-family: var(--mono); font-weight: 700;
    font-size: 1.02rem; color: var(--acc, var(--teal));
    text-shadow: 0 0 12px color-mix(in srgb, var(--acc, var(--teal)) 55%, transparent); }
.fp-pct.over { color: var(--ok); text-shadow: 0 0 12px color-mix(in srgb, var(--ok) 55%, transparent); }

/* Track = a recessed channel (inset shadow reads as carved-in depth). */
.fp-cov-track {
    position: relative; height: 16px; border-radius: 999px; overflow: hidden;
    background: linear-gradient(180deg, rgba(0,0,0,0.45), rgba(255,255,255,0.03));
    border: 1px solid rgba(255,255,255,0.07);
    box-shadow: inset 0 2px 5px rgba(0,0,0,0.55), inset 0 -1px 0 rgba(255,255,255,0.04);
}
/* Fill = a glossy raised pill: vertical light→dark gradient + top highlight + glow. */
.fp-cov-fill {
    position: relative; height: 100%; border-radius: 999px; min-width: 6px;
    background: linear-gradient(180deg,
        color-mix(in srgb, var(--acc, var(--teal)) 58%, #fff) 0%,
        var(--acc, var(--teal)) 45%,
        color-mix(in srgb, var(--acc, var(--teal)) 70%, #000) 100%);
    box-shadow: 0 0 14px -2px var(--acc, var(--teal)),
        inset 0 1px 0 rgba(255,255,255,0.6),
        inset 0 -3px 6px rgba(0,0,0,0.28);
    transition: width 0.5s cubic-bezier(.2,.8,.3,1);
}
/* A fixed highlight across the fill — the gloss stays, the perpetual sweep is
   gone (TG-285). */
.fp-cov-fill::after {
    content: ""; position: absolute; inset: 0; border-radius: inherit;
    background: linear-gradient(115deg, transparent 30%, rgba(255,255,255,0.28) 48%, transparent 66%);
}
@media (prefers-reduced-motion: reduce) {
    .fp-cov-fill { transition: none; }
}
.fp-cov-sub { font-size: 0.84rem; color: var(--muted); }
.fp-cov-sub small { opacity: 0.8; }
.fp-cov-info { justify-content: flex-end; }
.fp-cov-info .fp-cov-sub { color: var(--ink); }

/* Your pack */
.fp-pack-head { display: flex; align-items: baseline; justify-content: space-between; margin: 18px 0 8px; }
.fp-pack-head h4 { font-family: var(--display); font-weight: 600; font-size: 0.95rem; color: var(--ink); margin: 0; }
.fp-count { color: var(--muted); font-weight: 500; font-size: 0.82rem; margin-left: 6px; }
.fp-clear { background: none; border: 1px solid var(--line); color: var(--muted); border-radius: var(--r-ctl);
    font: inherit; font-size: 0.78rem; padding: 3px 11px; cursor: pointer; transition: all 0.15s; }
.fp-clear:hover { color: var(--ink); border-color: var(--edge-soft); background: var(--fill-hover); }

/* Fixed-height scroll region, NOT free-growing: every added pill used to push
   the food grid down mid-tap, so the second tap landed on the wrong product
   (2026-08 review, worst on phones). The pack scrolls inside itself instead —
   nothing below it ever moves. */
.fp-pack { display: flex; flex-wrap: wrap; gap: 8px; align-content: flex-start;
    height: 84px; overflow-y: auto; padding-right: 6px;
    scrollbar-width: thin; scrollbar-color: color-mix(in srgb, var(--ink) 20%, transparent) transparent; }
.fp-pack::-webkit-scrollbar { width: 8px; }
.fp-pack::-webkit-scrollbar-thumb { background: color-mix(in srgb, var(--ink) 14%, transparent); border-radius: 8px; }
/* Slots (2026-09): the pack laid out as a vest — front L, front R, back —
   plus a card per drop bag. Still one fixed-height scroller, taller now it
   holds cards, for the same reason as above. */
.fp-pack { display: grid; grid-template-columns: repeat(auto-fill, minmax(190px, 1fr)); gap: 8px;
    height: 176px; flex-wrap: nowrap; }
.fp-slot { border: 1px solid var(--line); border-radius: var(--r-card); padding: 7px 8px 8px;
    background: var(--glass); min-width: 0; align-self: start; }
.fp-slot.is-bag { border-style: dashed; }
.fp-slot-h { display: flex; justify-content: space-between; align-items: baseline; gap: 6px; margin-bottom: 6px; }
.fp-slot-t { font-weight: 600; font-size: 0.8rem; color: var(--ink); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.fp-slot-s { font-size: 0.72rem; color: var(--muted); white-space: nowrap; }
.fp-slot-pills { display: flex; flex-wrap: wrap; gap: 6px; }
.fp-slot-pills:empty::before { content: "—"; color: var(--muted); font-size: 0.8rem; }

/* Carbs per hour: what the pack can feed each hour against the target. */
.fp-meter { margin: 2px 0 16px; }
.fp-meter[hidden] { display: none; }
.fp-meter-head { display: flex; justify-content: space-between; align-items: baseline; gap: 8px;
    font-size: 0.72rem; color: var(--muted); margin-bottom: 6px; }
.fp-meter-tgt { color: var(--ink); font-size: 0.8rem; white-space: nowrap; }
.fp-meter-chart { position: relative; display: flex; align-items: flex-end; gap: 2px; height: 72px;
    padding-top: 2px; border-bottom: 1px solid var(--line); }
.fp-meter-goal { position: absolute; left: 0; right: 0; top: 2px; border-top: 1px dashed var(--edge-soft, var(--line)); pointer-events: none; }
.fp-meter-bar { flex: 1 1 0; min-width: 2px; border-radius: 2px 2px 0 0; background: var(--ok); }
.fp-meter-bar.warn { background: var(--warn); }
.fp-meter-bar.bad { background: var(--bad); }
.fp-meter-ticks { display: flex; justify-content: space-between; font-size: 0.68rem; color: var(--muted); margin-top: 3px; }
.fp-short { margin: 10px 0 0; padding: 8px 10px; border-radius: var(--r-ctl); font-size: 0.85rem;
    border: 1px solid color-mix(in srgb, var(--ok) 40%, transparent); color: var(--ink); }
.fp-short p { margin: 0; }
.fp-short.bad { border-color: color-mix(in srgb, var(--bad) 45%, transparent); }
.fp-short.bad b { color: var(--bad); }
.fp-short.ok b { color: var(--ok); }
.fp-short-sub { color: var(--muted); }
.fp-short-fix { margin-top: 8px; min-height: 36px; text-align: left; }
@media (max-width: 860px) {
    .fp-pack { height: 220px; grid-template-columns: 1fr 1fr; }
    .fp-short-fix { min-height: 44px; width: 100%; justify-content: center; }
}
@media (max-width: 420px) {
    .fp-pack { grid-template-columns: 1fr; }
}
.fp-pack-empty { grid-column: 1 / -1; color: var(--muted); font-size: 0.88rem; font-style: italic; padding: 4px 2px; }
.fp-pill { display: inline-flex; align-items: center; gap: 4px; padding: 3px 4px 3px 4px;
    border: 1px solid color-mix(in srgb, var(--cat, var(--teal)) 55%, transparent);
    background: color-mix(in srgb, var(--cat, var(--teal)) 14%, transparent);
    border-radius: var(--r-ctl); font-family: var(--body); font-size: 0.85rem; color: var(--ink); }
.fp-pill .fp-name { padding: 0 2px; }
.fp-qty { font-family: var(--mono); font-weight: 700;
    min-width: 1.1em; text-align: center;
    color: var(--cat, var(--teal)); }
.fp-step { width: 22px; height: 22px; border-radius: 50%; border: none; cursor: pointer;
    background: var(--fill-hover); color: var(--ink); font-size: 1rem; line-height: 1;
    display: inline-flex; align-items: center; justify-content: center; transition: background 0.15s; }
.fp-step:hover { background: color-mix(in srgb, var(--cat, var(--teal)) 35%, transparent); }
.fp-rm { width: 20px; height: 20px; margin-left: 2px; border: none; cursor: pointer; background: none;
    color: var(--muted); font-size: 1.05rem; line-height: 1; border-radius: 50%; transition: all 0.15s; }
.fp-rm:hover { color: var(--ink); background: var(--fill-hover); }

/* Search + food bin */
.fp-search-wrap { display: block; margin: 16px 0 10px; }
.fp-search-wrap input { width: 100%; }
/* overscroll-behavior:auto so reaching the bin's scroll limit chains back to the
   page/pane scroll instead of dead-stopping. */
.fp-bin { display: flex; flex-direction: column; gap: 14px; max-height: 360px; overflow-y: auto;
    padding-right: 4px; overscroll-behavior: auto; }
.fp-bin::-webkit-scrollbar { width: 8px; }
.fp-bin::-webkit-scrollbar-thumb { background: color-mix(in srgb, var(--ink) 14%, transparent); border-radius: 8px; }
.fp-bin-cat { display: flex; align-items: baseline; gap: 8px; margin: 0 0 8px; flex-wrap: wrap;
    font-family: var(--mono); font-weight: 700; font-size: 0.78rem; letter-spacing: 0.1em;
    text-transform: uppercase; color: var(--cat, var(--muted)); }
.fp-bin-cat small { text-transform: none; letter-spacing: 0; font-weight: 400; color: var(--muted); opacity: 0.85; }
.fp-bin-pills { display: flex; flex-wrap: wrap; gap: 8px; }
.fp-bin-none { color: var(--muted); font-size: 0.88rem; font-style: italic; }
.fp-add-pill { display: inline-flex; align-items: center; gap: 6px; padding: 5px 12px 5px 9px;
    border: 1px solid var(--line); border-radius: var(--r-ctl); background: var(--glass); cursor: pointer;
    font-family: var(--body); font-size: 0.85rem; color: var(--muted); transition: all 0.15s; }
.fp-add-pill:hover { color: var(--ink); transform: translateY(-1px);
    border-color: var(--cat, var(--teal)); background: color-mix(in srgb, var(--cat, var(--teal)) 12%, transparent); }
.fp-add-pill:active { transform: translateY(0) scale(0.97); }
.fp-add-pill.added { color: var(--ink); border-color: color-mix(in srgb, var(--cat, var(--teal)) 55%, transparent);
    background: color-mix(in srgb, var(--cat, var(--teal)) 10%, transparent); }
.fp-add-ic { color: var(--cat, var(--teal)); font-weight: 700; line-height: 1; }
.fp-add-name { color: inherit; }
.fp-add-macros { color: var(--muted); font-size: 0.74rem; white-space: nowrap; }
.fp-add-macros small { opacity: 0.7; }

/* ===== Hour-by-hour weather strip (shared by /fuel and /raceday; logic in raceday-weather.js) ===== */
.rd-weather { margin-top: 18px; }
.rd-weather[hidden] { display: none; }
.rd-wx-strip { display: flex; align-items: stretch; gap: 8px; margin-top: 8px; }
.rd-wx-cells { flex: 1 1 auto; display: grid; grid-template-columns: repeat(12, 1fr); gap: 5px; min-width: 0; }
.rd-wx-cell {
    display: flex; flex-direction: column; align-items: center; gap: 3px;
    padding: 9px 2px; border-radius: var(--r-ctl); background: var(--glass); border: 1px solid var(--line);
    min-width: 0;
}
.rd-wx-cell.rd-wx-race { border-color: rgba(255,122,26,0.6); background: rgba(255,122,26,0.08); }
.rd-wx-cell.rd-wx-empty { opacity: 0.4; }
.rd-wx-cell.rd-wx-newday { box-shadow: inset 2px 0 0 rgba(255,210,63,0.55); }
.rd-wx-day { font-size: 0.58rem; font-weight: 700; color: var(--gold); min-height: 0.85em; white-space: nowrap; line-height: 1; }
.rd-wx-time { font-family: var(--mono); font-size: 0.64rem; color: var(--muted); white-space: nowrap; }
.rd-wx-ico { line-height: 1; color: var(--ink); display: flex; justify-content: center; }
/* Weather art is line-icon SVG (icons.js) — sized here, coloured by context. */
.rd-wx-ico .wx-ico { width: 22px; height: 22px; }
.rd-wx-race .rd-wx-ico { color: var(--orange); }
/* Small inline glyphs beside a stat (droplet, wind, sunrise, thermometer…). */
.wx-gl { width: 13px; height: 13px; vertical-align: -0.16em; opacity: .9; }
.wx-gl.aqua { color: var(--aqua); } .wx-gl.gold { color: var(--gold); } .wx-gl.orange { color: var(--orange); }
.rd-wx-temp { font-family: var(--mono); font-weight: 700; font-size: 0.9rem; }
/* Temperature steps, cool → hot (weather-explorer.js tempCls; its chart
   resolves the same tokens). Data hues: a temperature is data, not a state. */
.wx-tn { color: var(--muted); }
.wx-t0 { color: var(--c-aid); }
.wx-t1 { color: var(--c-course); }
.wx-t2 { color: var(--c-crew); }
.wx-t3 { color: var(--gold); }
.wx-t4 { color: var(--c-fuel); }
.wx-t5 { color: var(--c-climb); }
.rd-wx-pp { font-size: 0.6rem; color: var(--teal); min-height: 0.8em; white-space: nowrap; }
.rd-wx-arrow {
    flex: 0 0 auto; width: 34px; border-radius: var(--r-ctl); cursor: pointer;
    background: var(--glass-2); color: var(--ink); border: 1px solid var(--line);
    font-size: 1.2rem; line-height: 1; transition: all 0.15s ease;
}
.rd-wx-arrow:hover:not(:disabled) { border-color: var(--edge-soft); background: var(--fill-hover); }
.rd-wx-arrow:disabled { opacity: 0.3; cursor: default; }
.rd-wx-status { color: var(--muted); font-size: 0.8rem; margin-top: 8px; min-height: 1em; }
.rd-wx-status.warn { color: var(--warn); }

/* ===================== Weather explorer card (weather-explorer.js) ===================== */
.rd-wx-bar { display: flex; align-items: center; gap: 12px 18px; flex-wrap: wrap; margin-bottom: 12px; }
.rd-wx-datefield { display: inline-flex; align-items: center; gap: 8px; color: var(--muted); font-size: 0.86rem; }
.rd-wx-datefield input { background: var(--glass-2); color: var(--ink); border: 1px solid var(--line); border-radius: 10px; padding: 7px 10px; font: inherit; color-scheme: dark; }
.rd-wx-datefield input:focus { outline: none; border-color: var(--accent); }
.rd-wx-datefield input:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.rd-wx-summary { display: flex; flex-wrap: wrap; gap: 7px 16px; align-items: center; color: var(--muted); font-size: 0.86rem; }
.rd-wx-summary b:not([class]) { color: var(--ink); }   /* a .wx-t* high keeps its hue */
.rd-wx-sum-note { font-style: italic; opacity: 0.85; }

.rd-wx-days { display: grid; grid-auto-flow: column; grid-auto-columns: minmax(60px, 1fr); gap: 6px;
    overflow-x: auto; padding-bottom: 6px; margin-bottom: 14px; overscroll-behavior-x: contain; }
.rd-wx-day-chip { display: flex; flex-direction: column; align-items: center; gap: 3px; padding: 8px 4px;
    border-radius: var(--r-ctl); background: var(--glass); border: 1px solid var(--line); cursor: pointer; color: var(--ink); transition: all 0.15s ease; }
.rd-wx-day-chip:hover { border-color: var(--edge-soft); background: var(--fill-hover); }
.rd-wx-day-chip.active { border-color: var(--accent); background: var(--fill-selected); }
.rd-wx-dc-day { font-size: 0.7rem; font-weight: 700; color: var(--muted); }
.rd-wx-dc-ico { font-size: 1.1rem; line-height: 1; }
.rd-wx-dc-hl { font-family: var(--mono); font-size: 0.72rem; }

/* Two hourly views, switchable via a pill toggle: an Apple-style temperature curve
   (Chart.js, mirrors the elevation profile's hover/drag scrubber) and the classic
   hour-by-hour cells. */
.rd-wx-chart-head { display: flex; align-items: center; justify-content: space-between; gap: 10px;
    flex-wrap: wrap; margin: 16px 0 8px; }
.rd-wx-chart-title { color: var(--muted); font-size: 0.85rem; font-weight: 600; }
.rd-wx-view-toggle { display: inline-flex; gap: 4px; background: var(--glass-2);
    border: 1px solid var(--line); border-radius: var(--r-ctl); padding: 3px; }
.rd-wx-view-toggle button { background: transparent; color: var(--muted); border: none; cursor: pointer;
    border-radius: var(--r-ctl); padding: 5px 13px; font-family: var(--body); font-weight: 600; font-size: 0.78rem; }
.rd-wx-view-toggle button.active { background: var(--fill-selected); color: var(--accent); }
.rd-wx-chart-wrap { position: relative; height: 210px; }
/* City label in the Weather header band (reverse-geocoded from the course start). */
.rd-wx-place { color: var(--ink); font-weight: 600; }
/* .rd-wx-strip sets display:flex (fuel.css), which beats the UA [hidden] rule — re-assert it
   so toggling back to the graph actually hides the hour-by-hour cells. */
.rd-wx-strip[hidden], .rd-wx-chart-wrap[hidden] { display: none; }

/* Classic hour-by-hour cells (base .rd-wx-* come from fuel.css). */
#rd-weather-card .rd-wx-cells { grid-template-columns: repeat(12, minmax(72px, 1fr)); overflow-x: auto; overscroll-behavior-x: contain; }
.rd-wx-feels { font-size: 0.6rem; color: var(--muted); min-height: 0.8em; white-space: nowrap; }
.rd-wx-wind { font-size: 0.6rem; color: var(--muted); white-space: nowrap; }
.rd-wx-cell.rd-wx-night { background: rgba(60, 80, 140, 0.16); }
.rd-wx-none { color: var(--muted); font-style: italic; padding: 8px 2px; }

@media (max-width: 560px) {
    .rd-wx-cells { grid-template-columns: repeat(12, minmax(46px, 1fr)); overflow-x: auto; }
}

/* ===== Collapsible coloured left-pane sections (shared by /raceday, /fuel, /planner) =====
   A .panel that is a <details>; the <summary> is the header. Each section carries an accent
   colour via --acc (set inline) for a coloured left edge + icon chip + chevron, so the left
   pane reads as distinct, lively cards. */
/* The collapsible card look — ONE source of truth, shared by /fuel, /planner and
   /raceday (every `.panel.rd-section`). Treatment: a mono all-caps title, a
   filled accent header band, and an accent glow outline, all keyed off the card's
   per-card `--acc` colour. Panel content padding is 14px (top/bottom) × 24px
   (left/right), so the band uses matching negative margins to bleed to the edges.
   Change the card aesthetic HERE and all three pages stay in sync. */
.rd-section { padding-top: 14px; padding-bottom: 14px; }
/* When a card is collapsed, force-hide its body even where an author rule (the base map,
   the fuel-cards grid, etc.) would otherwise display:block/grid it. Shared so the result
   cards collapse correctly on /fuel and /planner, not just /raceday. */
.rd-section:not([open]) > :not(summary) { display: none !important; }

/* The section's identity stays in its 1px accent-tinted edge (iconography);
   the coloured glow it used to carry is gone (TG-282). */
.rd-section.panel {
    border: 1px solid color-mix(in srgb, var(--acc, var(--line)) 55%, var(--line));
    box-shadow: none;
}

.rd-section > summary {
    list-style: none; cursor: pointer; user-select: none;
    display: flex; align-items: center; gap: 9px;
    font-family: var(--mono); font-weight: 700; font-size: 1.02rem; color: var(--ink);
    text-transform: uppercase; letter-spacing: 0.02em;
    /* Filled header band — bleeds to the card edges. */
    margin: -14px -24px; padding: 13px 24px;
    background: linear-gradient(100deg, color-mix(in srgb, var(--acc, var(--muted)) 26%, transparent), transparent 92%);
    border-radius: calc(var(--radius) - 1px);
}
.rd-section > summary::-webkit-details-marker { display: none; }
/* The section title is a real <h2> (one outline for screen readers) that
   looks exactly like the summary text it wraps. */
.rd-sec-h { margin: 0; font: inherit; letter-spacing: inherit; text-transform: inherit; color: inherit; }
.rd-sec-ico {
    flex: 0 0 auto; width: 26px; height: 26px; border-radius: 8px; display: grid; place-items: center;
    font-size: 0.95rem; background: var(--acc, var(--muted));
    box-shadow: inset 0 1px 0 rgba(255,255,255,0.28), 0 2px 5px rgba(0,0,0,0.35);
}
/* Dark stroke on the accent tile — same raised-tile treatment as the landing cards. */
.rd-sec-ico .ico { width: 15px; height: 15px; color: #081410; }
.rd-section > summary::after {
    content: '⌄'; margin-left: auto; color: var(--acc, var(--muted)); font-size: 1.1rem; line-height: 1;
    transition: transform 0.18s ease;
}

/* The AI coach card wears its gold section tint like every other section.
   The three-hue gradient ring / band / tile it used to carry is retired: the
   wordmark and the goat are the only multicolour brand marks (TG-282). Its
   "TRAILGOAT AI" title still carries the wordmark gradient via .grad. */
#coach-panel { --acc: var(--gold); position: relative; }
.rd-section[open] > summary::after { transform: rotate(180deg); }
.rd-section > summary .rd-sec-sub {
    font-family: var(--body); font-weight: 400; font-size: 0.78rem; color: var(--muted);
    text-transform: none; letter-spacing: normal;
}
/* Collapsed: the band fills the whole card. Open: band caps the top, body sits below. */
.rd-section:not([open]) > summary { margin-bottom: -14px; }
.rd-section[open] > summary {
    margin-bottom: 14px; border-bottom: 1px solid var(--line);
    border-radius: calc(var(--radius) - 1px) calc(var(--radius) - 1px) 0 0;
}

/* The fuel form must stay one element (fuel.js delegates its 'input' listener on it),
   but it shouldn't draw its own box — let its <details> children be the panes. */
.rd-form { display: contents; }

/* Space the sections' controls a touch tighter in the narrow left pane. */
.rd-section .field-block + .field-block { margin-top: 14px; }
.rd-section .effort-col > * + * { margin-top: 14px; }

/* A small note under a section heading. */
.rd-note { color: var(--muted); font-size: 0.84rem; margin: 0 0 12px; }
.rd-note:last-child { margin-bottom: 0; }
/* Clear-course control inside the Course card (left pane). */
.course-card-actions { margin-top: 12px; display: flex; flex-wrap: wrap; gap: 8px; }
/* GPX download only means something once a course is loaded (KRI-112). */
#download-gpx { display: none; }
body.has-course #download-gpx { display: inline-flex; }

/* ===== Mobile ergonomics (shared by every tool page; fuel.css is the common sheet) ===== */
@media (max-width: 860px) {
    /* iOS Safari zooms the whole page when a focused field computes under 16px.
       Guard every text-entry control — the typed selector matches the specificity
       of the themed input rules above, and this block loads later, so it wins. */
    input[type], input:not([type]), select, textarea { font-size: max(1rem, 16px); }

    /* Touch targets: grow hit areas toward the 44px minimum without inflating
       the glyphs. Isolated map-corner controls and pill controls that sit in
       tight clusters get as close as their context allows. */
    .map-3d-btn, .map-expand-btn, .map-play-btn, .map-locate-btn { width: 42px; height: 42px; }
    .map-locate-btn svg { width: 20px; height: 20px; }
    .map-speed-btn { min-width: 42px; height: 42px; }

    /* Food-pack pills: bigger steppers, comfortable pill height. */
    .fp-pill { padding: 5px 6px; }
    .fp-step { width: 34px; height: 34px; }
    .fp-rm { width: 30px; height: 30px; }
    .fp-add-pill { min-height: 44px; padding: 8px 14px 8px 11px; }
    .fp-clear { min-height: 36px; padding: 5px 14px; }

    /* Aid-station row actions (✎ / ×): full-size touch targets. */
    .aid-actions { gap: 0; margin-left: 4px; }
    .aid-rm, .aid-edit {
        min-width: 42px; min-height: 42px;
        display: inline-flex; align-items: center; justify-content: center;
    }
}

/* ===== Mobile plan tables: stacked two-line rows =====
   Under 600px the 5–6 column tables don't fit; instead of clipping the payload
   columns (pace, splits, water, carbs) off-screen, each <tr> reflows into a
   card-like row: line 1 = the label + the answer, line 2 = muted supporting
   detail. Pure CSS — the row templates in JS are untouched. Line breaks are
   deterministic: a flex-grow:1 cell fills the rest of its line, so the next
   cell always wraps. */
@media (max-width: 600px) {
    /* Stat cards: distance is the headline (full width), the rest pair up 2×2 —
       no more lone orphan card stretched across the last row. */
    .course-stats { display: grid; grid-template-columns: 1fr 1fr; }
    .course-stats .cs-dist { grid-column: 1 / -1; }

    #pace-table thead, #climbs-table thead, #plan-summary .plan-table thead,
    #raceplan-table thead, #aid-table thead, #lib-aid-table thead { display: none; }

    #pace-table, #climbs-table, #plan-summary .plan-table,
    #raceplan-table, #aid-table, #lib-aid-table { display: block; }
    #pace-table tbody, #climbs-table tbody, #plan-summary .plan-table tbody,
    #raceplan-table tbody, #aid-table tbody, #lib-aid-table tbody { display: block; }

    #pace-table tr, #climbs-table tr, #plan-summary .plan-table tr,
    #raceplan-table tr, #aid-table tr, #lib-aid-table tr {
        display: flex; flex-wrap: wrap; align-items: baseline;
        column-gap: 14px; row-gap: 3px;
        padding: 11px 4px; border-bottom: 1px solid var(--line);
    }
    #pace-table td, #climbs-table td, #plan-summary .plan-table td,
    #raceplan-table td, #aid-table td, #lib-aid-table td {
        display: block; padding: 0; border: 0; text-align: left;
    }

    /* Pacing plan — line 1: segment + target pace; line 2: gain/loss · grade · elapsed.
       The rows' ::before is an invisible full-width flex item — a deterministic
       line break between the headline cells and the detail cells. */
    #pace-table tr::before { content: ""; flex: 1 1 100%; order: 3; }
    #pace-table td:nth-child(1) { flex: 1 1 auto; order: 1; font-weight: 600; }
    #pace-table td:nth-child(4) { order: 2; }
    #pace-table td:nth-child(2) { order: 4; font-size: 0.82rem; }
    #pace-table td:nth-child(3) { order: 5; font-size: 0.82rem; }
    #pace-table td:nth-child(5) { order: 6; margin-left: auto; font-size: 0.82rem; color: var(--muted); }
    #pace-table td:nth-child(5)::before { content: "elapsed "; }

    /* Major climbs — line 1: climb + gain; line 2: from–to · length · grade */
    #climbs-table tr::before { content: ""; flex: 1 1 100%; order: 3; }
    #climbs-table td:nth-child(1) { flex: 1 1 auto; order: 1; font-weight: 600; }
    #climbs-table td:nth-child(4) { order: 2; }
    #climbs-table td:nth-child(2) { order: 4; font-size: 0.82rem; color: var(--muted); }
    #climbs-table td:nth-child(3) { order: 5; font-size: 0.82rem; color: var(--muted); }
    #climbs-table td:nth-child(5) { order: 6; margin-left: auto; font-size: 0.82rem; }

    /* Fuel plan summary — line 1: section + water + carbs; line 2: distance · arrival */
    #plan-summary .plan-table tr::before { content: ""; flex: 1 1 100%; order: 4; }
    #plan-summary .plan-table td:nth-child(1) { flex: 1 1 auto; order: 1; font-weight: 600; }
    #plan-summary .plan-table td:nth-child(4) { order: 2; }
    #plan-summary .plan-table td:nth-child(5) { order: 3; }
    #plan-summary .plan-table td:nth-child(2) { order: 5; font-size: 0.82rem; color: var(--muted); }
    #plan-summary .plan-table td:nth-child(3) { order: 6; margin-left: auto; font-size: 0.82rem; color: var(--muted); }
    #plan-summary .plan-table td:nth-child(3)::before { content: "arrive "; }

    /* Race Day plan — a card per stop, one concern per line:
         1  name (wraps as it must) · drop bag           edit / cutoff / remove
         2  crew marks — only when someone meets or hands off here
         3  dist · arrive ETA · temp
         4  cutoff pill — only when the stop has one
         5  water + carbs to carry
       The name cell wraps so a long stop name never crushes the crew pills
       and the controls never run off the right edge (a three-pill Leadville
       row used to push the cutoff clock past the screen). The arrival cell
       is display:contents so its cutoff can leave the ETA's line and take
       its own — inside a block it sat indented under the ETA, mid-row. Two
       invisible full-width items (::before/::after) are the line breaks; they
       are zero-height lines, so the space between lines rides on the items'
       own top margins rather than a row-gap the breaks would double. */
    #raceplan-table tr { row-gap: 0; }
    #raceplan-table tr::before { content: ""; flex: 1 1 100%; order: 5; }
    #raceplan-table tr::after { content: ""; flex: 1 1 100%; order: 7; }
    #raceplan-table td:nth-child(2), #raceplan-table .aid-eta, #raceplan-table td:nth-child(4),
    #raceplan-table .aid-cutoff, #raceplan-table .aid-dwell, #raceplan-table td:nth-child(5), #raceplan-table td:nth-child(6) { margin-top: 6px; }
    #raceplan-table td:nth-child(1) { flex: 1 1 100%; order: 1; display: flex; flex-wrap: wrap;
        align-items: center; column-gap: 6px; row-gap: 6px; font-weight: 600; }
    #raceplan-table td:nth-child(1) .aid-nm { flex: 1 1 0; min-width: 0; }
    #raceplan-table td:nth-child(1) .aid-bag { margin-left: 0; }
    #raceplan-table td:nth-child(1) .aid-actions { flex: 0 0 auto; margin-left: auto; }
    /* Renaming and removing stops is desk work — on a phone the two buttons
       cost a third of the name line. The cutoff clock stays: it is the one
       control a runner reaches for on race week, and the only way to set a
       cutoff on a stop that has none. */
    #raceplan-table .aid-edit, #raceplan-table .aid-rm { display: none; }
    #raceplan-table td:nth-child(1) .aid-crewline { flex: 1 1 100%; order: 9;
        display: flex; flex-wrap: wrap; align-items: center; column-gap: 6px; row-gap: 6px; }
    #raceplan-table .aid-crewline .aid-crew, #raceplan-table .aid-crewline .crew-io { margin-left: 0; }
    #raceplan-table td:nth-child(2) { order: 2; font-size: 0.82rem; color: var(--muted); }
    #raceplan-table td:nth-child(3) { display: contents; }
    #raceplan-table .aid-eta { order: 3; font-size: 0.82rem; color: var(--muted); }
    #raceplan-table .aid-eta::before { content: "arrive "; }
    #raceplan-table td:nth-child(4) { order: 4; font-size: 0.82rem; color: var(--muted); }
    #raceplan-table .aid-cutoff { order: 6; margin-top: 0; }
    /* The card is the full width on a phone, so the demand can take it. */
    #raceplan-table .aid-cutoff.cut-warn { max-width: 100%; }
    #raceplan-table .aid-dwell { order: 7; margin-top: 0; }
    #raceplan-table td:nth-child(5) { order: 8; }
    #raceplan-table td:nth-child(6) { order: 9; }
    /* Nothing to say, nothing shown: the "—" cells (Start's carry, a stop
       with no forecast) are noise once the column header is gone. */
    #raceplan-table td.plan-none { display: none; }
    /* The pacer's bar runs the full card, not just the name line. */
    #raceplan-table tr.leg-paced { box-shadow: inset 3px 0 0 var(--pc, var(--teal)); padding-left: 10px; }
    #raceplan-table tr.leg-paced td:first-child { box-shadow: none; }

    /* Pacing aid stations — line 1: checkpoint (edit controls right); line 2: distance · elapsed */
    #aid-table td:nth-child(1) { flex: 1 1 100%; order: 1; display: flex; align-items: center; }
    #aid-table td:nth-child(1) .aid-actions { margin-left: auto; }
    #aid-table td:nth-child(2) { order: 2; font-size: 0.85rem; color: var(--muted); }
    #aid-table td:nth-child(3) { order: 3; margin-left: auto; font-size: 0.85rem; color: var(--muted); }
    #aid-table td:nth-child(3)::before { content: "elapsed "; }

    /* Course Creator aid list — line 1: name + edit controls; line 2: distance */
    #lib-aid-table td:nth-child(1) { flex: 1 1 auto; order: 1; display: flex; align-items: center; }
    #lib-aid-table td:nth-child(3) { order: 2; margin-left: auto; }
    #lib-aid-table td:nth-child(2) { order: 3; flex: 1 1 100%; font-size: 0.85rem; color: var(--muted); }
}

/* ===== Mobile header compaction =====
   The desktop app-shell (≥861px) already trades the tall hero for a compact
   heading; phones — the least screen — were still getting the full-size one.
   Mirror it: small h1, no lead paragraph, tighter margins, so the first tool
   card lands inside the first viewport. (Action buttons like Race Day's
   "Plan a race" stay.) */
@media (max-width: 560px) {
    .hero { margin: 10px 0 16px; }
    .hero h1 { font-size: 1.5rem; line-height: 1.15; }
    .eyebrow { font-size: 0.72rem; margin-bottom: 6px; }
    .hero-sub { display: none; }
    .hero-cta { margin-top: 12px; }

    /* Panels: a touch less padding buys real column width at 375px. The
       .rd-section header band bleeds edge-to-edge via negative margins that
       must mirror the panel padding — keep them in lockstep. */
    .panel { padding: 18px; }
    .rd-section { padding-top: 14px; padding-bottom: 14px; }
    .rd-section > summary { margin: -14px -18px; padding: 13px 18px; }
    .rd-section:not([open]) > summary { margin-bottom: -14px; }
    .rd-section[open] > summary { margin-bottom: 14px; }

    /* Card headers: let the subtitle take its own line under the title instead
       of squeezing beside it ("HOW THE PACING WORKS grade-adjusted splits"). */
    .rd-section > summary { flex-wrap: wrap; row-gap: 2px; }
    .rd-section > summary .rd-sec-sub { flex: 1 1 100%; order: 10; margin-left: 35px; }
}

/* ===== Food bin: collapsible categories (details/summary, all widths) ===== */
.fp-bin-group > summary::-webkit-details-marker { display: none; }
.fp-bin-cat { list-style: none; cursor: pointer; user-select: none; }
.fp-bin-cat::after {
    content: '⌄'; margin-left: auto; color: var(--muted);
    font-size: 1rem; line-height: 1; transition: transform 0.18s ease;
}
.fp-bin-group[open] > .fp-bin-cat::after { transform: rotate(180deg); }
.fp-bin-count {
    font-size: 0.7rem; font-weight: 600; color: var(--muted);
    background: var(--glass-2); border: 1px solid var(--line);
    border-radius: var(--r-ctl); padding: 1px 8px; margin-left: 2px;
}
@media (max-width: 860px) {
    /* No scroll region inside the page scroll — the categories collapse instead. */
    .fp-bin { max-height: none; overflow: visible; padding-right: 0; }
    .fp-bin-cat { align-items: center; min-height: 44px; }
}

/* ===== Mobile pane merge (pairs with the nav.js module) ===== */
.merged-away { display: none !important; }

@media (max-width: 860px) {
    /* Stacked cards need breathing room — desktop's app-split grid gap only
       exists ≥861px, so phones were rendering the cards edge-to-edge. */
    body.split-page .app-split,
    body.split-page .split-left,
    body.split-page .split-right { display: flex; flex-direction: column; gap: 14px; }
    /* The column's own gap is the only spacing between cards. Two panels carry
       a legacy margin-bottom from the standalone fuel page (.fueling-plan and
       .coach, fuel.css above), which lands ON TOP of that gap: Fueling sat 36px
       from Gear where every other pair sat 14px. Desktop already zeroes these
       the same way a few blocks up; this is the phone half of that rule. */
    body.split-page .split-left > .panel,
    body.split-page .split-right > .panel { margin: 0; }

    /* A display-card body merged into its input card: divider between the
       inputs above and the map / plan / table below. */
    .rd-merged { margin-top: 18px; padding-top: 16px; border-top: 1px solid var(--line); }

    /* The weather explorer card adopted into Conditions: flatten from a
       full panel to a sub-section of its host card. */
    .rd-merged .rd-section.panel {
        border: none; box-shadow: none; background: transparent;
        padding: 0; border-radius: 0;
    }
    .rd-merged .rd-section > summary {
        margin: 0; padding: 2px 0 10px; background: none; border-radius: 0;
    }
    .rd-merged .rd-section:not([open]) > summary { margin-bottom: 0; }
    .rd-merged .rd-section[open] > summary { margin-bottom: 8px; border-bottom: none; }
}

/* ===== Fullscreen elevation profile (trail-map.js createProfile expand) ===== */
.tg-elev-expand { margin-left: auto; }   /* right end of the overlay toggle bar */
.elev-canvas-wrap.elev-expanded {
    position: fixed; inset: 0; z-index: 9999;
    height: auto; max-height: none;
    background: var(--bg-1, #0a1a1c);
    padding: 58px 16px 22px;
}
body.elev-modal-open { overflow: hidden; }
.tg-elev-close {
    position: absolute; top: 10px; right: 10px; z-index: 2;
    width: 44px; height: 44px; border-radius: 50%;
    background: var(--glass-2); color: var(--ink); border: 1px solid var(--line);
    font-size: 1.1rem; line-height: 1; cursor: pointer;
    display: flex; align-items: center; justify-content: center;
}
.tg-elev-close:hover { border-color: var(--edge-soft); background: var(--fill-hover); }
.tg-elev-hint {
    position: absolute; top: 22px; left: 16px; right: 70px;
    color: var(--muted); font-size: 0.82rem; display: none;
}
/* The hint only matters on a phone held upright. */
@media (orientation: portrait) and (max-width: 860px) {
    .elev-canvas-wrap.elev-expanded .tg-elev-hint { display: block; }
}

/* ===== Map feel pass: new shared controls (trail-map.js) ===== */
/* In-map toast — gesture hint, locate status. */
.tg-map-toast {
    position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%);
    z-index: 900; pointer-events: none;
    background: rgba(4, 12, 10, 0.82); color: var(--ink);
    border: 1px solid var(--line); border-radius: var(--r-ctl);
    padding: 10px 18px; font-size: 0.88rem; font-weight: 600; white-space: nowrap;
    opacity: 0; transition: opacity 0.25s ease;
}
.tg-map-toast.show { opacity: 1; }

/* Distance markers along the route. */
.tg-mile-wrap { background: none; border: 0; }
.tg-mile {
    display: flex; align-items: center; justify-content: center;
    width: 22px; height: 22px; border-radius: 50%;
    background: rgba(7, 18, 15, 0.88); color: var(--gold);
    border: 1.5px solid var(--gold);
    font-family: var(--mono); font-weight: 700; font-size: 0.62rem;
}
/* Touch screens: at least 24×24 (WCAG 2.2 target size); trail-map.js gives
   the Leaflet icon the same box so the badge stays centred on its mile. */
@media (pointer: coarse) {
    .tg-mile { width: 24px; height: 24px; font-size: 0.66rem; }
}

/* Tap-to-inspect popup + scale bar: dark-theme the Leaflet chrome. */
.tg-inspect .leaflet-popup-content-wrapper {
    background: rgba(7, 18, 15, 0.94); color: var(--ink);
    border: 1px solid var(--line); border-radius: var(--r-card); box-shadow: var(--shadow-pop);
}
.tg-inspect .leaflet-popup-content { margin: 9px 14px; font: 600 0.88rem/1.45 var(--body); }
.tg-inspect .leaflet-popup-content b { color: var(--gold); }
.tg-inspect .leaflet-popup-tip { background: rgba(7, 18, 15, 0.94); border: 1px solid var(--line); }
.leaflet-control-scale-line {
    background: rgba(7, 18, 15, 0.75); color: var(--muted);
    border-color: var(--line); font-size: 0.66rem;
}

/* ===== Interaction grammar (audit PR 4, re-cut by TG-282) =====
   One hover language for the whole product: interactive surfaces lift a touch
   and step the neutral ladder — never a coloured glow. Hover-capable pointers
   only; reduced motion keeps color changes but drops movement. */
@media (hover: hover) {
    details.rd-section {
        transition: border-color 0.18s ease, box-shadow 0.18s ease;
    }
    /* Sections no longer lift or glow in their tint on hover (TG-282): the
       coloured glow is gone and hover never wears a feature tint. */
    .export-btn, .estimate-btn, .rd-plan-btn, .fp-clear, .course-clear, .lib-manage, .lib-create-route {
        transition: transform 0.15s ease, box-shadow 0.15s ease, border-color 0.15s ease,
            background 0.15s ease, color 0.15s ease;
    }
    /* Flat buttons lift, they don't glow — .export-btn/.rd-plan-btn keep their
       own outline hover (see the base rules). */
    .lib-create-route:hover { transform: translateY(-1px); }
    .export-btn.tonal:hover, .estimate-btn:hover, .lib-create-route:hover {
        box-shadow: var(--raise-2);
    }
    .export-btn:hover, .rd-plan-btn:hover { transform: translateY(-1px); }
}
@media (prefers-reduced-motion: reduce) {
    .export-btn:hover, .estimate-btn:hover, .rd-plan-btn:hover, .lib-create-route:hover { transform: none; }
}

/* Keyboard focus wears the one accent (TG-282). */
:where(a, button, summary, [role="button"], .fp-pill):focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}

/* Digits line up in every data surface (arrival times stop wobbling on rerender).
   One declaration site for the whole file — the mono pass (TG-284) sets the face
   on each value class and adds its selector here rather than repeating the
   property a dozen times. */
.plan-table td, .plan-table th, .cmp-table td, .cmp-table th,
.cs-val, .metric-num, .kit-num, .fp-cov-sub, .fp-pill, .fp-pct, .fp-qty,
.rd-wx-cell, .rd-wx-day-chip, .summary-text, .carry-dist, .carry-load,
.lib-item-meta, .estimate-out {
    font-variant-numeric: tabular-nums;
}

/* ===== Cross-highlighting (audit PR 5) =====
   Hovering a table row answers on the map and elevation profile — and tapping
   a map marker flashes its row. */
.aid-marker { transition: transform 0.15s ease, box-shadow 0.15s ease; }
.aid-marker-wrap { z-index: 600; }
.aid-marker-wrap.aid-hover { z-index: 900 !important; }
.aid-marker-wrap.aid-hover .aid-marker {
    transform: scale(1.45);
    /* Ink, not white: the glow has to read on First Light's light topo too. */
    box-shadow: 0 0 0 4px color-mix(in srgb, var(--ink) 25%, transparent), 0 0 18px 2px color-mix(in srgb, var(--ink) 35%, transparent);
}
@media (hover: hover) {
    .plan-table tbody tr { cursor: default; }
}
.plan-table tbody tr.row-flash {
    animation: tg-row-flash 1.6s ease;
}
@keyframes tg-row-flash {
    0%, 60% { background: rgba(255, 210, 63, 0.18); }
    100% { background: transparent; }
}
@media (prefers-reduced-motion: reduce) {
    .aid-marker { transition: none; }
    .plan-table tbody tr.row-flash { animation: none; background: rgba(255, 210, 63, 0.14); }
}

/* ===== 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; }
}

/* ===== Mini map in the left rail (course loaded, desktop) =====
   The right pane scrolls independently, so once you're down at the race plan
   the big map is gone — the mini map keeps every hover/tap answer in view.
   It lives behind the rail's "Show map" disclosure (#rd-sum-map, raceday.html)
   now that the rail's main job is the plan summary; gpx.js builds it only while
   the disclosure is open. Mobile keeps the merged full map instead (pane
   merge), so it's ≥861px only. */
.mini-map-wrap { display: none; margin-top: 4px; }

/* ===== The rail's three ways to start (TG-300, TG-320) =====
   Each way in gets a label and one line of hint, so the rail itself is the
   explanation the welcome popover used to give from on top of it. With a
   course loaded the whole block goes (TG-320): the rail is the pinned map
   then, the topbar search switches courses, and Clear course brings the
   three ways back. The head swaps from "Run setup" to the course name in the
   same stroke. display: contents keeps the rows as the rail's own flex items,
   so its gap spaces them exactly as before the wrapper existed. */
.rd-start { display: contents; }
.rd-start-row { display: flex; flex-direction: column; gap: 6px; }
.rd-start-hint { color: var(--muted); font-size: 0.82rem; line-height: 1.45; margin: 0; }
/* The browse link moved inside the search row, so it is spaced by that row's
   gap rather than by the rail's old negative-margin pull. */
.rd-start-row > .creator-note { margin: 0; }
.rd-start-wiz #rd-start-wizard { width: 100%; }
body.has-course .rd-start { display: none; }
.rd-rail-course { display: none; }
body.has-course .rd-rail-setup { display: none; }
body.has-course .rd-rail-course { display: flex; }
/* The browse button stays visible with a course loaded — switching is the point. */
.tg-browse-btn { background: none; border: none; padding: 0; font: inherit; cursor: pointer; }
@media (min-width: 861px) {
    body.has-course .mini-map-wrap { display: block; }
    /* Sized for a glance under the summary, not for the whole rail. */
    #mini-map { height: clamp(200px, 28vh, 360px); }
}
.map-wrap.map-expanded #mini-map { height: 100vh; height: 100dvh; }

/* ===== Mini-map aid dots =====
   Full 26px "A" badges bury the route at mini-map size — render them as small
   colored dots that grow into the badge when their row is hovered (.aid-hover
   comes from the cross-highlighting fan-out). */
.aid-marker-wrap.aid-mini { display: flex; align-items: center; justify-content: center; }
.aid-mini .aid-marker {
    width: 11px; height: 11px; font-size: 0; border-width: 1.5px;
    transition: width 0.15s ease, height 0.15s ease, font-size 0.1s ease 0.05s,
        transform 0.15s ease, box-shadow 0.15s ease;
}
.aid-mini.aid-hover .aid-marker {
    width: 26px; height: 26px; font-size: 0.92rem;
    transform: scale(1.1);   /* the base .aid-hover scale(1.45) is too much on top of the grow */
}
@media (prefers-reduced-motion: reduce) {
    .aid-mini .aid-marker { transition: none; }
}


/* ===== Full-height Course card (Race Day) =====
   The Course card is the ONLY left card, so the pane doesn't scroll — the
   card scales to the screen instead. With no course loaded the dropzone grows
   to fill it; with one loaded the plan summary (raceday.css) sits at natural
   height and the optional mini map below it is clamp-sized. overflow-y:auto
   on the pane remains as a safety net for very short windows only. */
@media (min-width: 861px) {
    body.split-page .split-left > #rd-course[open] {
        flex: 1 1 auto; min-height: 0;
        display: flex; flex-direction: column;
    }
    /* Chrome wraps a details' children in an internal ::details-content box —
       the flex above only splits [summary | content box], so the content box
       itself must flex-fill and lay its children out as a column too. Kept as
       its own rule: browsers without the pseudo drop just this block. */
    body.split-page .split-left > #rd-course[open]::details-content {
        display: flex; flex-direction: column; flex: 1 1 auto; min-height: 0;
    }
    body.has-course .mini-elev-wrap { display: block; }
    body.split-page:not(.has-course) .split-left #rd-course .dropzone { flex: 1 1 auto; }
}
/* Mini elevation profile under the mini map. */
.mini-elev-wrap { display: none; margin-top: 10px; }
.mini-elev-wrap[hidden] { display: none; }
.mini-elev-wrap .elev-canvas-wrap { height: 140px; }

/* Goal inputs at the top of the Pacing plan panel (the left card is retired). */
.pace-goal-row { display: flex; flex-wrap: wrap; gap: 12px 28px; align-items: flex-end;
    margin: 2px 0 10px; }
.pace-goal-row .field-block { margin: 0; }

/* ===== The rail slides away (TG-320, desktop) =====
   The drag handle that used to sit between the panes is gone: a runner who
   wants the plan full-width hides the rail instead. html.rd-rail-closed is
   set before first paint by the inline script in raceday.html's head and kept
   honest by raceday.js — never without a course loaded. The column animates
   between two minmax() tracks (the same shape at both ends is what lets the
   grid interpolate it) while the rail fades and slips left under it. The seam
   and its padding go too, or a 0px column would still show 19px of them. */
@media (min-width: 861px) {
    body.split-page .app-split { transition: grid-template-columns 0.2s ease, gap 0.2s ease; }
    html.rd-rail-closed body.split-page .app-split { grid-template-columns: minmax(0px, 0px) 1fr; gap: 0; }
    body.split-page .split-left {
        min-width: 0; overflow-x: hidden;
        transition: opacity 0.2s ease, transform 0.2s ease, padding-right 0.2s ease, border-color 0.2s ease, visibility 0s linear 0s;
    }
    html.rd-rail-closed body.split-page .split-left {
        opacity: 0; transform: translateX(-24px); visibility: hidden; pointer-events: none;
        padding-right: 0; border-right-color: transparent;
        transition-delay: 0s, 0s, 0s, 0s, 0.2s;
    }
}
@media (prefers-reduced-motion: reduce) {
    body.split-page .app-split, body.split-page .split-left { transition: none; }
}

/* ===== Compact conditions controls (Weather card) =====
   The adopted inputs read as one toolbar row, not a stacked form — start
   time + fetch, temp, humidity and acclimation wrap side by side. */
#wx-conditions .effort-col { flex-direction: row; flex-wrap: wrap; align-items: flex-end; gap: 12px 22px; }
#wx-conditions .effort-col > * + * { margin-top: 0; }
#wx-conditions .field-block { display: flex; flex-direction: column; gap: 6px; margin: 0; }
#wx-conditions .weather-row { flex-wrap: nowrap; gap: 8px; }
#wx-conditions .weather-row input { flex: 0 1 auto; width: 205px; padding: 8px 10px; }
#wx-conditions .estimate-btn { padding: 8px 12px; font-size: 0.84rem; }
#wx-conditions .field-grid { display: flex; gap: 12px; }
#wx-conditions .field-grid .field { gap: 6px; }
#wx-conditions .field-grid .field input { width: 88px; padding: 8px 10px; }
#wx-conditions .segmented button { flex: 0 0 auto; padding: 7px 14px; font-size: 0.8rem; }
/* The estimate line only earns space when it has something to say. */
#wx-conditions .weather-out { margin: 0; min-height: 0; }
#wx-conditions .weather-out:empty { display: none; }

/* Aid-station rename icon (icons.js). */
.aid-ico { width: 13px; height: 13px; vertical-align: -0.1em; }

/* ===== Upgrade dialog (billing) — flat outline cards, brand rules ===== */
.tg-upg-opts { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; margin: 14px 0 4px; }
.tg-upg-opt { display: flex; flex-direction: column; gap: 4px; align-items: flex-start; text-align: left;
    padding: 14px; background: transparent; color: var(--ink); font: inherit; cursor: pointer;
    border: 1px solid var(--line); border-radius: var(--r-card); transition: border-color .15s, box-shadow .15s; }
.tg-upg-opt:hover { border-color: var(--edge); background: var(--fill-hover); }
.tg-upg-opt b { font-family: var(--mono); letter-spacing: .02em; }
.tg-upg-price { color: var(--accent); font-weight: 700; font-size: 1.15rem; }
.tg-upg-opt small { color: var(--muted); line-height: 1.4; }
.tg-upg-note { color: var(--muted); font-size: .84rem; margin: 10px 2px 0; }
.tg-upg-note a { color: var(--accent); }
@media (max-width: 520px) { .tg-upg-opts { grid-template-columns: 1fr; } }

/* ===== Aid stations edited in place (planner-5) =====
   gpx.js: a grip to drag (or Alt+↑/↓), the name and the mile as click
   targets that open an inline field, and a type chip set per stop. The
   chips draw their word from data-t so the cell's text stays the name.
   One accent on duty: a chip that is on, the field that is open, the
   line a dragged row will land on. */
#raceplan-table .aid-grip {
    display: inline-flex; align-items: center; justify-content: center; vertical-align: middle;
    width: 20px; height: 28px; margin: 0 4px 0 -4px; border-radius: var(--r-ctl);
    color: var(--muted); cursor: grab; touch-action: none; opacity: 0.55;
    transition: opacity var(--dur-ctl) var(--ease-snap), background var(--dur-ctl) var(--ease-snap);
}
#raceplan-table .aid-grip svg { width: 16px; height: 16px; fill: currentColor; }
#raceplan-table tr:hover .aid-grip, #raceplan-table .aid-grip:focus-visible { opacity: 1; }
#raceplan-table .aid-grip:hover { background: var(--fill-hover); color: var(--ink); }
#raceplan-table .aid-grip:focus-visible,
#raceplan-table .aid-nm[role="button"]:focus-visible,
#raceplan-table .aid-mi:focus-visible,
#raceplan-table .aid-type:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
#raceplan-table .aid-nm[role="button"], #raceplan-table .aid-mi { cursor: text; border-radius: 4px; }
#raceplan-table .aid-nm[role="button"]:hover, #raceplan-table .aid-mi:hover {
    background: var(--fill-hover); box-shadow: 0 0 0 3px var(--fill-hover); }
#raceplan-table .aid-inline {
    font: inherit; color: var(--ink); background: var(--bg-well);
    border: 1px solid var(--accent); border-radius: var(--r-ctl); padding: 3px 7px; min-height: 32px;
    width: min(220px, 100%);
}
#raceplan-table .aid-inline-mile { width: 7ch; font-family: var(--mono); font-variant-numeric: tabular-nums; }
#raceplan-table .aid-inline:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
#raceplan-table tr.aid-dragging { opacity: 0.55; }
body.aid-drag-on { user-select: none; -webkit-user-select: none; cursor: grabbing; }
#raceplan-table tr.aid-drop-before td { box-shadow: inset 0 2px 0 var(--accent); }
#raceplan-table tr.aid-drop-after td { box-shadow: inset 0 -2px 0 var(--accent); }
#raceplan-table .aid-types { display: inline-flex; flex-wrap: wrap; gap: 3px; margin-left: 8px; vertical-align: middle; }
#raceplan-table .aid-type {
    min-height: 22px; padding: 0 7px; border-radius: var(--r-ctl);
    border: 1px dashed var(--line); background: none; color: var(--muted); cursor: pointer;
    font: 600 0.68rem/1 var(--body); letter-spacing: 0.02em;
    transition: background var(--dur-ctl) var(--ease-snap), color var(--dur-ctl) var(--ease-snap), border-color var(--dur-ctl) var(--ease-snap);
}
#raceplan-table .aid-type::before { content: attr(data-t); }
#raceplan-table .aid-type:hover { color: var(--ink); border-color: var(--edge-soft); }
#raceplan-table .aid-type.on {
    border-style: solid; border-color: color-mix(in srgb, var(--accent) 45%, transparent);
    color: var(--accent); background: color-mix(in srgb, var(--accent) 12%, transparent);
}
/* Quiet until wanted: the chips that are off show on the row's hover, while
   it is being edited or keyboard-focused, and always where there is no hover. */
@media (hover: hover) {
    #raceplan-table .aid-type:not(.on) { display: none; }
    #raceplan-table tr:hover .aid-type, #raceplan-table tr:focus-within .aid-type,
    #raceplan-table tr.aid-editing .aid-type { display: inline-block; }
}
.aid-paste-link { margin: 10px 0 0; font-size: 0.85rem; color: var(--muted); }
.aid-paste-link a { color: var(--accent); }
@media (max-width: 600px) {
    #raceplan-table td:nth-child(1) .aid-types { flex: 1 1 100%; order: 10; margin: 6px 0 0; }
    #raceplan-table .aid-type { min-height: 44px; min-width: 44px; padding: 0 10px; }
    #raceplan-table .aid-grip { width: 32px; height: 44px; opacity: 0.8; }
}
@media (prefers-reduced-motion: reduce) {
    #raceplan-table .aid-grip, #raceplan-table .aid-type { transition: none; }
}

/* /crew.css */
/* TrailGoat — the live crew sheet's stylesheet.
 *
 * Until 2026-09 this was a 475-line <style> block inside crew.html: the page a
 * paying customer looks at most, and the one furthest from tokens.css — 20px
 * corners against the site's 10, gold as the action colour, its own hex for
 * the meta line and the map, and a print restyle in crew-print.html that
 * could not reach any of it. It is a real stylesheet now, versioned through
 * app/assets.py like every other one, and it reads the tokens throughout:
 * --r-card / --r-ctl for corners, --ink / --bg-* / --glass / --line for the
 * chrome, the --c-* hues for data only, and the .export-btn ladder (fuel.css,
 * loaded on both sheets) for anything you press.
 *
 * Two audiences share it. The live sheet (crew.html) puts `cs-page` on its
 * <body>, and every rule that shapes THE PAGE — ground, wrap, headline, the
 * aid table's cells — is scoped under that class. The printable
 * (crew-print.html) loads this file too, for the component rules the shared
 * section builders in app/crew.py emit (aid marks, crew cards, checklists,
 * metric chips), and keeps only its paper-sized overrides. Nothing scoped to
 * .cs-page reaches paper; nothing unscoped assumes a dark ground.
 *
 * crew-sw.js precaches whatever the page loaded, so this file is part of the
 * offline sheet by construction (crew-pwa.js names the stylesheet links
 * explicitly, so a tile-flooded resource-timing buffer cannot drop it). */

:root {
    /* Status is tokens.css's --ok / --warn / --bad (#541); they grade a
       margin or a sync state and never paint a control.
       Head meta reads at arm's length in a dark van — a step below --ink,
       well above --muted (KRI-122), derived rather than a hex of its own. */
    --ink-soft: color-mix(in srgb, var(--ink) 82%, var(--muted));
}

.cs-page * { box-sizing:border-box; }
/* One smooth wash for the whole sheet, however long it gets. `fixed` pins the
   gradient to the viewport and `no-repeat` guarantees it can never tile —
   this page is thousands of pixels tall and any document-sized gradient
   banded across the content. --bg-1 underneath covers rubber-band overscroll. */
html:has(.cs-page) { height:auto; min-height:100%; }
body.cs-page { margin:0; min-height:100vh; color:var(--ink);
    background:linear-gradient(180deg,var(--bg-0),var(--bg-1) 60%) no-repeat fixed;
    background-color:var(--bg-1);
    font:16px/1.5 var(--body); -webkit-font-smoothing:antialiased; }
/* Phone-first, but not phone-only: 1060px matches the marketing pages
   (info-wrap--wide) and gives the aid table — which has grown crew chips,
   cutoff pills and per-stop temps — room to show every column on a
   desktop without the horizontal scroll the phone layout tolerates. */
.cs-page .wrap { max-width:1060px; margin:0 auto; padding:18px 14px 56px; }
.brand { display:flex; flex-wrap:wrap; gap:12px; justify-content:space-between; align-items:center;
    font:800 .82rem/1 var(--display); letter-spacing:.14em; color:var(--muted); padding:4px 2px 14px; }
.brand b { color:var(--ink); } .brand b i { font-style:normal; color:var(--gold); }
 /* The tag is a STATUS chip, not a button and not a brand mark: mono caps in
   the Crew hue, and a dot that lights up while the race clock is running
   (crew-live.js sets `.live` once the runner is marked through the start and
   until every stop is marked). Gold is off this header for good — it read as
   the action colour on a page whose actions are the ladder below. */
.crewtag { display:inline-flex; align-items:center; gap:7px; font-family:var(--mono);
    font-weight:500; font-size:max(.68rem, var(--mono-cap)); line-height:1; letter-spacing:.1em;
    text-transform:uppercase; color:var(--c-crew);
    border:1px solid color-mix(in srgb, var(--c-crew) 45%, transparent);
    border-radius:var(--r-ctl); padding:6px 10px; }
.crewtag .tag-dot { width:7px; height:7px; border-radius:50%; background:var(--muted); opacity:.5; }
.crewtag.live .tag-dot { background:var(--c-crew); opacity:1;
    box-shadow:0 0 0 3px color-mix(in srgb, var(--c-crew) 25%, transparent); }
.crewtag .tag-live { color:var(--c-crew); }
/* Print / PDF (TG-348) and Pace band (TG-349): ways out of the page, on the
   ladder's bordered neutral (.export-btn from fuel.css) at header size. */
.brand-r { display:flex; flex-wrap:wrap; max-width:100%; align-items:center; gap:8px; }
@media (max-width: 720px) { #wx-conditions .field-block { min-width:0; max-width:100%; } #wx-conditions .weather-row { flex-wrap:wrap; } .brand-r button, .brand-r a { min-height:44px; display:inline-flex; align-items:center; } }
.printbtn.export-btn { gap:6px; padding:6px 10px; font-size:.72rem; font-weight:600;
    letter-spacing:.06em; }
.printbtn svg { width:13px; height:13px; fill:none; stroke:currentColor; stroke-width:2;
    stroke-linecap:round; stroke-linejoin:round; }
.cs-page h1 { font:800 clamp(1.4rem,6vw,2rem)/1.15 var(--display); letter-spacing:-0.015em; margin:0 0 6px; }
/* Head meta reads at arm's length in a dark van — near-ink, not muted sage
   (KRI-122: the muted tone was hard to read under the title). */
.cs-page .meta { color:var(--ink-soft); margin:0 0 5px; font-size:.95rem; }
/* Each fact its own colour (KRI-126) — one flat wash read as a wall. */
.m-sep { color:var(--muted); font-style:normal; padding:0 7px; opacity:.7; }
.m-date { color:var(--ink); font-weight:600; }
.m-soft { color:var(--muted); }
.m-org, a.m-org { color:var(--accent); font-weight:600; text-decoration:underline;
    text-decoration-color:color-mix(in srgb, var(--accent) 45%, transparent);
    text-underline-offset:3px; }
.cs-page .m-org-t { color:var(--ink-soft); font-weight:600; }
.m-when { color:var(--teal); font-weight:600; }
.m-goal { color:var(--violet); font-weight:700; }
.wx { color:var(--teal); font-size:.92rem; margin:0 0 16px; }

/* ---- Cards: the planner's .rd-section treatment ---- */
.card { position:relative; background:var(--glass); border-radius:var(--r-card);
    padding:14px 18px 16px; margin-bottom:14px;
    border:1px solid color-mix(in srgb, var(--acc, var(--line)) 55%, var(--line));
    box-shadow:0 0 0 1px color-mix(in srgb, var(--acc, transparent) 12%, transparent),
               0 10px 34px color-mix(in srgb, var(--acc, transparent) 14%, transparent); }
.card-h, details.card summary {
    display:flex; align-items:center; gap:9px; margin:-14px -18px 14px; padding:12px 18px;
    font:700 .98rem/1.3 var(--mono); text-transform:uppercase; letter-spacing:.02em;
    color:var(--ink); border-radius:calc(var(--r-card) - 1px);
    background:linear-gradient(100deg, color-mix(in srgb, var(--acc, var(--muted)) 26%, transparent), transparent 92%); }
details.card summary { cursor:pointer; list-style:none; margin-bottom:0; }
details.card[open] summary { margin-bottom:14px; }
details.card summary::-webkit-details-marker { display:none; }
details.card summary::after, .card-h .chev { content:'⌄'; margin-left:auto;
    color:var(--acc, var(--muted)); font-size:1.1rem; line-height:1; transition:transform var(--dur-ctl) var(--ease-snap); }
details.card[open] summary::after { transform:rotate(180deg); }
.ico-tile { flex:0 0 auto; width:26px; height:26px; border-radius:var(--r-ctl); display:grid; place-items:center;
    background:var(--acc, var(--muted));
    box-shadow:inset 0 1px 0 var(--glass-2), 0 2px 5px rgba(0,0,0,.35); }
.ico-tile svg { width:15px; height:15px; fill:none; stroke:var(--accent-ink); stroke-width:2;
    stroke-linecap:round; stroke-linejoin:round; }
.card .sub { color:var(--muted); font-size:.85rem; margin:2px 0 10px; }
.card h3 { font:700 .85rem/1.3 var(--display); color:var(--acc, var(--ink)); margin:14px 0 6px; }

/* ---- Stat tiles (KRI-123): the planner's course-stat cards ---- */
.cs-grid { display:flex; flex-wrap:wrap; gap:10px; margin:0 0 14px; }
.cs-tile { background:var(--glass-2); border:1px solid var(--line); border-radius:var(--r-card);
    padding:9px 14px; min-width:96px; }
.cs-tile span { display:block; color:var(--muted); font:500 .72rem/1.4 var(--mono);
    text-transform:uppercase; letter-spacing:.1em; margin-bottom:1px; }
.cs-tile b { font:700 1.12rem/1.3 var(--mono); font-variant-numeric:tabular-nums; color:var(--sc,var(--ink)); }
.cs-tile small { color:var(--muted); font:500 .7rem/1 var(--mono); letter-spacing:.08em;
    text-transform:uppercase; margin-left:4px; }

/* ---- Conditions & weather: the Run Planner's card, dropped in whole.
   fuel.css already carries .panel/.rd-section and every .rd-wx-* rule, so
   only the box metrics need bridging — the planner pads its cards 24px and
   bleeds the header band out by that much; this page pads 18px. ---- */
.wx-typ { color:var(--ink); font-size:.92rem; margin:0 0 8px; }
#rd-weather-card { background:var(--glass); border-radius:var(--r-card);
    padding:14px 18px 16px; margin-bottom:14px; backdrop-filter:none; }
#rd-weather-card > summary { margin:-14px -18px; padding:12px 18px; font-size:.98rem; }
#rd-weather-card[open] > summary { margin-bottom:14px; }
#rd-weather-card .field-label { color:var(--muted); }
@media (max-width:480px) {
    #rd-weather-card { padding:12px 14px 14px; }
    #rd-weather-card > summary { margin:-12px -14px; padding:11px 14px; }
    #rd-weather-card[open] > summary { margin-bottom:12px; }
}

/* ---- Course: map + profile ---- */
#cs-map { height:min(52vh,360px); border-radius:var(--r-card); border:1px solid var(--line);
    background:var(--glass-2); margin-bottom:12px; }
/* Engine path (KRI-123): the map lives in a .map-wrap (expand/3D hang off
   it) and the profile panel matches the embed's. */
.card .map-wrap { height:min(56vh,400px); border-radius:var(--r-card); margin-bottom:12px; }
.card .map-wrap #cs-map { height:100%; margin:0; }
.cs-elev { background:var(--glass); border:1px solid var(--line);
    border-radius:var(--r-card); padding:8px 10px 4px; }
.cs-elev .elev-canvas-wrap { max-height:170px; overflow:hidden; }
.cs-elev .elev-canvas-wrap.elev-expanded { max-height:none; overflow:visible; }
@media (max-width:640px) {
    .cs-elev .tg-elev-toggles { flex-wrap:nowrap; overflow-x:auto; scrollbar-width:none; }
    .cs-elev .tg-elev-toggles::-webkit-scrollbar { display:none; }
    .cs-elev .tg-tog { white-space:nowrap; flex:0 0 auto; }
}
.cs-profile svg { display:block; width:100%; height:auto; border-radius:var(--r-card);
    background:var(--glass); border:1px solid var(--line); }
.surf-legend { color:var(--muted); font-size:.78rem; margin:8px 0 0; }
.surf-legend i { display:inline-block; width:11px; height:7px; border-radius:2px;
    vertical-align:1px; font-style:normal; }

/* ---- Tables ---- */
.cs-page .tblwrap { overflow-x:auto; -webkit-overflow-scrolling:touch; margin:6px -4px 0; }
.cs-page table { border-collapse:collapse; width:100%; font-size:.88rem; min-width:480px; }
.cs-page th { text-align:left; color:var(--muted); font:500 .7rem/1.4 var(--mono); text-transform:uppercase;
    letter-spacing:.1em; padding:6px 8px; border-bottom:1px solid var(--line); white-space:nowrap; }
.cs-page td { padding:7px 8px; border-bottom:1px solid var(--line-faint); white-space:nowrap; }
/* Every column but the aid-station name is a figure — mono and tabular so a
   crew reads the arrival times straight down the column (TG-284 AC-4). */
.cs-page td:not(:first-child) { font-family:var(--mono); font-variant-numeric:tabular-nums; }
.cs-page tbody tr:nth-child(even) { background:var(--glass); }
.aid-badge { display:inline-grid; place-items:center; width:17px; height:17px; border-radius:50%;
    margin-right:7px; font:700 .62rem/1 var(--display); color:var(--accent-ink); vertical-align:-3px; }
td.c-water { color:var(--teal); font-weight:600; }
td.c-carb { color:var(--gold); font-weight:600; }
/* Pace/climbs value tints — the Run Planner's plan-table classes, re-derived
   server-side from the snapshot text (the scrape drops the original spans). */
td.aid-name { color:var(--ink); font-weight:500; }
.t-up { color:var(--orange); font-weight:600; }
.t-down { color:var(--aqua); font-weight:600; }
.t-sep { color:var(--muted); font-weight:400; }
.t-flat { color:var(--muted); }
.t-pace { color:var(--gold); font-weight:700; }
/* Surface badges on the pacing rows. fuel.css carries the .srf-chip shape
   but scopes its colours to .plan-table, which this page's table isn't. */
.srf-trail { color:var(--moss); font-weight:600; }
.srf-gravel { color:var(--gold); font-weight:600; }
.srf-paved { color:var(--muted); font-weight:600; }
.srf-unknown { color:var(--muted); font-weight:600; }
.grade-soft { color:var(--gold); font-weight:600; }
.grade-mid { color:var(--orange); font-weight:600; }
.grade-hard { color:var(--coral); font-weight:700; }
/* Who & what waits at each stop (KRI-122) — same visual language as the
   planner's aid rows: gold bag icon, initial dots, ▶/◀ hand-off pills. */
.tbl-bag { display:inline-flex; align-items:center; gap:2px; margin-left:7px;
    vertical-align:-2px; color:var(--gold); }
.tbl-bag svg { width:14px; height:14px; fill:none; stroke:currentColor; stroke-width:2;
    stroke-linecap:round; stroke-linejoin:round; }
.tbl-bag i { font-style:normal; font-size:.68rem; font-weight:700; }
/* Two-letter initials, so two Amandines don't both read "A". Pill, not a
   circle, to fit them. */
.tbl-p { display:inline-grid; place-items:center; min-width:17px; height:17px;
    border-radius:var(--r-ctl); padding:0 5px; margin-left:6px;
    font:700 .62rem/1 var(--display); color:var(--accent-ink);
    background:var(--pc,var(--fern)); vertical-align:-3px; }
.tbl-io { display:inline-block; margin-left:6px; font-size:.68rem; font-weight:700;
    color:var(--pc,var(--fern)); border:1px solid currentColor; border-radius:var(--r-ctl);
    padding:1px 7px; vertical-align:1px; white-space:nowrap; }

/* ---- Metric tiles (planner's accent treatment) ---- */
.mts { display:grid; grid-template-columns:repeat(auto-fit,minmax(130px,1fr)); gap:10px; }
.mt { position:relative; overflow:hidden; background:var(--glass-2); border:1px solid var(--line);
    border-radius:var(--r-card); padding:10px 12px; }
.mt::before { content:''; position:absolute; top:0; left:0; width:100%; height:3px;
    background:var(--macc,var(--muted)); opacity:.9; }
.mt::after { content:''; position:absolute; top:-40px; right:-40px; width:96px; height:96px;
    border-radius:50%; background:var(--macc,var(--muted)); opacity:.12; filter:blur(8px); }
.mt span { display:block; color:var(--muted); font-size:.72rem; }
.mt b { font:700 1.05rem/1.3 var(--display); } .mt small { color:var(--muted); font-weight:500; }
.m-water { --macc:var(--teal); } .m-sodium { --macc:var(--violet); }
.m-carbs { --macc:var(--gold); } .m-cals { --macc:var(--orange); }

/* ---- Crew & pacers (the card this page exists for) ---- */
.crew-p { border:1px solid var(--line); border-left:3px solid var(--pc,var(--fern));
    border-radius:var(--r-card); background:var(--glass-2); padding:12px 14px; margin:0 0 10px; }
.crew-p-head { display:flex; align-items:center; gap:9px; flex-wrap:wrap; }
.crew-p-head .dot { width:10px; height:10px; border-radius:50%; background:var(--pc,var(--fern)); flex:0 0 auto; }
.crew-p-head b { font:700 1rem/1.3 var(--display); }
.role { font:600 .66rem/1 var(--body); letter-spacing:.1em; text-transform:uppercase;
    color:var(--pc,var(--fern)); border:1px solid color-mix(in srgb, var(--pc,var(--fern)) 55%, transparent);
    border-radius:var(--r-ctl); padding:4px 9px; }
.contacts { display:flex; gap:8px; flex-wrap:wrap; margin:9px 0 2px; }
.cbtn { display:inline-flex; align-items:center; gap:6px; text-decoration:none;
    color:var(--ink); font-size:.84rem; font-weight:600;
    border:1px solid var(--line); border-radius:var(--r-ctl); padding:7px 13px;
    background:var(--glass); }
.cbtn svg { width:13px; height:13px; fill:none; stroke:var(--pc,var(--fern)); stroke-width:2;
    stroke-linecap:round; stroke-linejoin:round; }
.cbtn:active { background:var(--glass-2); }
.crew-note { color:var(--muted); font-size:.85rem; font-style:italic; margin:7px 0 0; }
/* ---- Action lines (KRI-130). A whole line in one colour reads as a wall
   at 4am, so each piece gets its own weight: WHERE in bright ink (what a
   crew looks for first), WHEN in the action's colour, and the joining
   glyphs muted so they carry no weight at all. ---- */
.crewline { display:flex; flex-wrap:wrap; align-items:baseline; gap:4px 6px;
    margin:7px 0 0; font-size:.88rem; color:var(--ink); }
.crewline .lbl { flex:0 0 auto; display:inline-flex; align-items:center; gap:4px;
    color:var(--muted); font:500 .7rem/1 var(--mono); text-transform:uppercase;
    letter-spacing:.1em; }
.crewline .lbl svg { width:13px; height:13px; fill:none; stroke:currentColor;
    stroke-width:1.8; stroke-linecap:round; stroke-linejoin:round; }
.cl-at { color:var(--ink); font-weight:600; }
.cl-eta { color:var(--ak,var(--muted)); font-family:var(--mono); font-weight:700; font-variant-numeric:tabular-nums; }
.cl-arrow { color:var(--muted); }
.cl-sep { color:var(--muted); opacity:.6; }
.cl-dist { color:var(--muted); font-family:var(--mono); font-variant-numeric:tabular-nums; font-size:.78rem; }
/* The action's identity colour rides the times and the glyph, not the
   whole sentence: pacing takes the person's colour, driving is gold. */
.crewline.pace { --ak:var(--pc,var(--fern)); }
.crewline.drive { --ak:var(--gold); }
.crewline.pace .lbl svg, .crewline.drive .lbl svg { stroke:var(--ak); }
.tbl-io svg { width:12px; height:12px; fill:none; stroke:currentColor; stroke-width:1.8;
    stroke-linecap:round; stroke-linejoin:round; vertical-align:-2px; margin-right:2px; }
.bystop { display:flex; flex-wrap:wrap; gap:7px; margin-top:6px; }
.bystop span { border:1px solid var(--line); border-radius:var(--r-ctl); padding:6px 11px;
    font-size:.8rem; background:var(--glass); }
.bystop b { font-weight:700; }

ul.check { list-style:none; margin:4px 0 0; padding:0; columns:2; column-gap:18px; }
ul.check li { break-inside:avoid; padding:4px 0 4px 22px; position:relative; font-size:.9rem; }
ul.check li::before { content:""; position:absolute; left:0; top:9px; width:12px; height:12px;
    border:1.5px solid var(--muted); border-radius:3px; }
/* ---- Live checklist (KRI-124): items with an identity are tappable and
   the whole crew sees the tick. ---- */
ul.check li[data-k] { cursor:pointer; -webkit-tap-highlight-color:transparent;
    border-radius:var(--r-ctl); }
ul.check li[data-k]:active { background:var(--fill-hover); }
ul.check li.done::before { background:var(--acc,var(--ok)); border-color:var(--acc,var(--ok)); }
ul.check li.done::after { content:"✓"; position:absolute; left:1.5px; top:7px; width:12px;
    text-align:center; font:700 10px/14px var(--display); color:var(--accent-ink); }
ul.check li.done .t { color:var(--muted); text-decoration:line-through;
    text-decoration-color:var(--edge); }
ul.check li .by { font-style:normal; color:var(--muted); font-size:.72rem; margin-left:7px; }
.h3n { margin-left:8px; color:var(--muted); font:600 .72rem/1 var(--body);
    border:1px solid var(--line); border-radius:var(--r-ctl); padding:2px 8px; vertical-align:1px; }
.h3n.all { color:var(--ok); border-color:color-mix(in srgb, var(--ok) 55%, transparent); }

/* ---- "Who's viewing" chip + picker (KRI-124) ---- */
#cs-me { display:inline-flex; align-items:center; gap:7px; margin-left:auto; margin-right:10px;
    font:600 .78rem/1 var(--body); letter-spacing:0; text-transform:none; color:var(--ink);
    background:var(--fill-idle); border:1px solid var(--edge); border-radius:var(--r-ctl);
    padding:6px 12px; min-height:32px; cursor:pointer;
    transition:background var(--dur-ctl) var(--ease-snap); }
#cs-me:hover { background:var(--fill-hover); }
#cs-me .dot { width:9px; height:9px; border-radius:50%; background:var(--pc,var(--muted)); }
#cs-idm { position:fixed; inset:0; z-index:3000; background:color-mix(in srgb, var(--bg-0) 72%, transparent);
    display:grid; place-items:center; padding:18px; }
.idm-card { position:relative; width:min(430px,100%); background:var(--bg-1);
    border:1px solid var(--line); border-radius:var(--r-card); padding:20px 22px;
    box-shadow:var(--shadow-pop); }
.idm-card h2 { font:700 1.05rem/1.3 var(--display); margin:0 0 4px; }
.idm-card p { color:var(--muted); font-size:.85rem; margin:0 0 14px; }
.idm-list { display:flex; flex-direction:column; gap:8px; }
.idm-p { display:flex; align-items:center; gap:10px; width:100%; text-align:left;
    background:var(--glass-2); border:1px solid var(--line); border-radius:var(--r-card);
    padding:11px 13px; cursor:pointer; color:var(--ink); font:inherit; }
.idm-p:hover { border-color:var(--pc,var(--muted)); }
.idm-p .dot { width:11px; height:11px; border-radius:50%; background:var(--pc,var(--muted)); flex:0 0 auto; }
.idm-p b { font:600 .95rem/1.3 var(--body); }
.idm-p .role { margin-left:auto; }
.idm-else .dot { border:1.5px dashed var(--muted); background:transparent; }
.idm-other { display:flex; gap:8px; margin-top:10px; }
.idm-other input { flex:1; background:var(--fill-idle); border:1px solid var(--line);
    border-radius:var(--r-ctl); color:var(--ink); font:inherit; padding:10px 12px; }
.idm-other .export-btn { padding:8px 14px; }
.idm-x { position:absolute; top:10px; right:12px; background:none; border:0;
    color:var(--muted); font-size:1.3rem; cursor:pointer; padding:4px 8px; }

/* ---- Crew notes (KRI-124) ---- */
.notes-empty { color:var(--muted); font-size:.87rem; margin:2px 0 10px; }
.note { border:1px solid var(--line); border-left:3px solid var(--pc,var(--fern));
    border-radius:var(--r-card); background:var(--glass-2); padding:10px 13px; margin:0 0 9px; }
.note-pending { opacity:.6; }
.note-head { display:flex; align-items:center; gap:8px; flex-wrap:wrap; margin-bottom:4px; }
.note-head .dot { width:9px; height:9px; border-radius:50%; background:var(--pc,var(--fern)); flex:0 0 auto; }
.note-head b { font:600 .88rem/1.3 var(--body); }
.note-runner { font:600 .6rem/1 var(--body); letter-spacing:.1em; color:var(--orange);
    border:1px solid color-mix(in srgb, var(--orange) 55%, transparent); border-radius:var(--r-ctl); padding:3px 7px; }
.note-when { color:var(--muted); font-size:.72rem; margin-left:auto; }
.note-act { background:none; border:0; color:var(--muted); font:600 .72rem/1 var(--body);
    cursor:pointer; padding:3px 5px; }
.note-act:hover { color:var(--ink); }
.note-body { font-size:.9rem; white-space:pre-wrap; overflow-wrap:anywhere; }
#cs-note-form textarea { width:100%; box-sizing:border-box; resize:vertical; min-height:52px;
    background:var(--fill-idle); border:1px solid var(--line); border-radius:var(--r-card);
    color:var(--ink); font:inherit; font-size:.9rem; padding:10px 12px; }
#cs-note-form textarea:focus { outline:none; border-color:color-mix(in srgb, var(--accent) 55%, transparent); }
.note-actions { display:flex; align-items:center; gap:10px; margin-top:8px; }
.note-sync { color:var(--warn); font-size:.78rem; }
.note-post.export-btn { margin-left:auto; padding:8px 16px; }
/* ---- Race progress (KRI-132). The one card that answers "where is my
   runner right now", so it leads the sheet and states the delta in words
   a crew can act on rather than a number they have to subtract. ---- */
.prog-status { display:flex; flex-wrap:wrap; align-items:baseline; gap:6px 10px; margin:2px 0 12px; }
.prog-where { color:var(--ink); font:700 1.05rem/1.3 var(--display); }
.prog-at { color:var(--c-crew); font-family:var(--mono); font-weight:700; font-variant-numeric:tabular-nums; }
.prog-delta { font:600 .82rem/1 var(--body); border-radius:var(--r-ctl); padding:5px 10px;
    border:1px solid currentColor; }
.prog-delta.late { color:var(--bad); }
.prog-delta.early { color:var(--ok); }
.prog-by { color:var(--muted); font-size:.78rem; }
/* A held or postponed start is stated, never folded into "behind plan". */
.prog-delay { color:var(--warn); font:600 .78rem/1 var(--body);
    border:1px solid color-mix(in srgb, var(--warn) 45%, transparent);
    border-radius:var(--r-ctl); padding:5px 10px; }
.prog-hint { color:var(--warn); font-size:.82rem; margin:0 0 12px;
    border-left:2px solid var(--warn); padding-left:10px; }
.prog-idle { color:var(--muted); font-size:.88rem; }
.prog-mark { display:flex; flex-wrap:wrap; gap:8px; align-items:center; margin-bottom:12px; }
.prog-sel { flex:1 1 180px; min-width:150px; min-height:44px; background:var(--fill-idle); color:var(--ink);
    border:1px solid var(--edge); border-radius:var(--r-ctl); padding:9px 14px;
    font:inherit; font-size:.88rem; cursor:pointer; }
/* "Mark arrived now" is the sheet's one primary verb: the ladder's tonal step,
   not a coloured outline — the Crew hue is data, never a control (TG-282). */
.prog-btn.export-btn { flex:0 0 auto; min-height:44px; padding:9px 18px; font-size:.88rem; }
.prog-done { color:var(--ok); font-size:.88rem; font-weight:600; }
.prog-row { display:flex; flex-wrap:wrap; align-items:center; gap:8px; padding:7px 0;
    border-top:1px solid var(--line); }
.prog-row b { flex:1 1 120px; font:600 .9rem/1.3 var(--body); }
/* fuel.css stretches every datetime input to the row; a check-in time is
   one field on a line of four, so it sizes to its content here. */
.prog-time { width:auto; flex:0 1 240px; min-height:36px; background:var(--fill-idle); border:1px solid var(--line); border-radius:var(--r-ctl);
    color:var(--ink); font:inherit; font-size:.8rem; padding:5px 8px; }
.prog-row-d { font:700 .78rem/1 var(--mono); font-variant-numeric:tabular-nums; }
.prog-row-d.late { color:var(--bad); } .prog-row-d.early, .prog-row-d.on { color:var(--ok); }
.prog-row-d.bad { color:var(--warn); font-weight:600; }
.prog-undo { background:none; border:0; color:var(--muted); font:600 .76rem/1 var(--body);
    cursor:pointer; padding:4px 6px; }
.prog-undo:hover { color:var(--ink); }
.prog-foot { margin:10px 0 0; }
/* ---- One race bar (2026-09): runner, plan ghost, cutoff margins, on one
   course-length track. Data hues only on data: the fill and dot are the
   Crew hue, margins wear the same ok/warn/bad grade as the table's chips.
   Nothing pulses — motion announces a change and stops (TG-285). ---- */
.rb { position:relative; height:54px; margin:4px 0 2px; }
.rb[hidden] { display:none; }
.rb-track { position:absolute; left:0; right:0; top:24px; height:8px; overflow:hidden;
    border-radius:4px; background:var(--bg-well); border:1px solid var(--line-faint); }
.rb-fill { position:absolute; left:0; top:0; bottom:0; background:var(--c-crew);
    transition:width var(--dur-layer) var(--ease-snap); }
.rb-dot, .rb-ghost { position:absolute; top:21px; width:14px; height:14px; border-radius:50%;
    transform:translateX(-50%); }
.rb-dot { background:var(--c-crew); border:2px solid var(--bg-1); z-index:2;
    box-shadow:0 0 0 1px color-mix(in srgb, var(--c-crew) 55%, transparent); }
.rb-ghost { border:2px dashed var(--muted); background:var(--bg-1); z-index:1; }
.rb-cut { position:absolute; top:18px; width:2px; height:20px; transform:translateX(-50%);
    background:var(--muted); border-radius:1px; }
.rb-cut.cut-ok { background:var(--ok); } .rb-cut.cut-tight { background:var(--warn); }
.rb-cut.cut-missed { background:var(--bad); }
.rb-cut.past { opacity:.55; }
.rb-cut-l { position:absolute; bottom:22px; left:50%; transform:translateX(-50%);
    font-size:.68rem; line-height:1; white-space:nowrap; color:var(--muted); }
.rb-cut.cut-ok .rb-cut-l { color:var(--ok); } .rb-cut.cut-tight .rb-cut-l { color:var(--warn); }
.rb-cut.cut-missed .rb-cut-l { color:var(--bad); }
.rb-cut.next .rb-cut-l { font-weight:700; }
/* Ticks on the ends keep their labels on the bar rather than centred off it. */
.rb-cut.at-end .rb-cut-l { left:auto; right:0; transform:none; }
.rb-cut.at-start .rb-cut-l { left:0; transform:none; }
.rb-end { position:absolute; top:38px; font-size:.68rem; color:var(--faint); }
.rb-start { left:0; } .rb-finish { right:0; }
.rb-foot { display:flex; flex-wrap:wrap; align-items:baseline; gap:6px 16px; margin:2px 0 12px; }
.rb-foot .prog-cutoff { margin:0; flex:1 1 280px; }
.rb-legend { display:flex; flex-wrap:wrap; gap:4px 12px; color:var(--muted); font-size:.74rem; }
.rb-legend[hidden] { display:none; }
.rb-legend span { display:inline-flex; align-items:center; gap:5px; }
.rb-legend i { display:inline-block; }
.lg-dot { width:8px; height:8px; border-radius:50%; background:var(--c-crew); }
.lg-ghost { width:8px; height:8px; border-radius:50%; border:1.5px dashed var(--muted); }
.lg-cut { width:2px; height:9px; background:var(--muted); }
.prog-fin { color:var(--muted); font-size:.82rem; }
.prog-fin b { color:var(--ink); font-weight:700; }
/* On a phone only the next cutoff's margin is labelled; the rest keep their
   ticks, and every margin is still in the table below. */
@media (max-width:860px) { .rb-cut:not(.next) .rb-cut-l { display:none; } }
@media (prefers-reduced-motion:reduce) { .rb-fill { transition:none; } }

/* Re-timed arrivals: reached stops are fact, everything ahead is a forecast. */
td.c-eta.eta-actual { color:var(--ok); font-weight:700; }
td.c-eta.eta-live { color:var(--warn); font-weight:600; }
/* Departure beside the arrival (TG-309) — the crew's real question. */
.c-dep { margin-left:6px; color:var(--muted); font:500 .82em/1 var(--mono);
    font-variant-numeric:tabular-nums; white-space:nowrap; }
/* Cutoff chips: flat outline pills graded by the LIVE buffer. */
.cs-cutoff { display:block; width:fit-content; margin-top:3px; padding:1px 7px;
    font:500 .7rem/1.5 var(--mono); font-variant-numeric:tabular-nums;
    white-space:nowrap; border:1px solid var(--line);
    border-radius:var(--r-ctl); color:var(--muted); }
.cs-cutoff.cut-ok, .cs-cutbuf.cut-ok { border-color:color-mix(in srgb, var(--ok) 45%, transparent); color:var(--ok); }
.cs-cutoff.cut-tight, .cs-cutbuf.cut-tight { border-color:color-mix(in srgb, var(--warn) 55%, transparent); color:var(--warn); }
.cs-cutoff.cut-missed, .cs-cutbuf.cut-missed { border-color:color-mix(in srgb, var(--bad) 60%, transparent); color:var(--bad); }
.prog-cutoff { font-size:.88rem; margin:-6px 0 12px; color:var(--ink); }
.prog-cutoff .cs-cutbuf { font-weight:700; }
.prog-cutoff .sub-inline { color:var(--muted); font-size:.78rem; }
tr.cs-reached td { opacity:.72; }

/* ---- Next stop first (2026-09). The page leads with the one answer a crew
   opens it for — where next, when, when do we leave — then the feed of what
   has happened. Two columns where there is room, one stack on a phone. The
   forecast is data, so it wears the Crew hue; the buttons stay on the
   ladder. ---- */
#cs-next-mount { display:grid; gap:0 14px; }
#cs-next-mount:empty { display:none; }
@media (min-width:861px) { #cs-next-mount { grid-template-columns:minmax(0,5fr) minmax(0,6fr); align-items:start; } }
.cs-next { padding-top:16px; }
.nx-top { display:flex; align-items:baseline; justify-content:space-between; gap:10px; }
.nx-mile { color:var(--muted); font-size:.8rem; }
.nx-name { font:800 1.35rem/1.2 var(--display); letter-spacing:-0.01em; margin:4px 0 2px; color:var(--ink); }
.nx-clock { font:700 clamp(2.3rem, 10vw, 3rem)/1.05 var(--mono); font-variant-numeric:tabular-nums;
    color:var(--c-crew); margin:2px 0; border-radius:var(--r-ctl); width:fit-content; }
.nx-clock.late { color:var(--warn); }
.nx-note { color:var(--ink-soft); font-size:.9rem; margin:0; }
.nx-leave { color:var(--ink); font-size:.92rem; margin:6px 0 0; }
.nx-leave b { color:var(--ink); }
.nx-acts { display:flex; flex-wrap:wrap; gap:8px; margin-top:14px; }
.nx-acts:empty { display:none; }
.nx-btn.export-btn { flex:1 1 140px; min-height:48px; font-size:.92rem; text-decoration:none; }
.nx-btn svg { width:16px; height:16px; fill:none; stroke:currentColor; stroke-width:2;
    stroke-linecap:round; stroke-linejoin:round; }
.feed { list-style:none; margin:0; padding:0; }
.feed-i { display:grid; grid-template-columns:5.2em 1fr; gap:10px; align-items:baseline;
    padding:8px 0; border-top:1px solid var(--line-faint); font-size:.9rem; }
.feed-i:first-child { border-top:0; padding-top:0; }
.feed-t { color:var(--muted); font-size:.78rem; white-space:nowrap; }
.feed-x { overflow-wrap:anywhere; color:var(--ink-soft); }
.feed-x b { color:var(--ink); font-weight:600; }
.feed-note .feed-x > b:first-child { color:var(--pc, var(--ink)); }
.feed-pending { opacity:.6; }
.feed-empty { color:var(--muted); font-size:.87rem; }
.feed-more { margin:10px 0 0; font-size:.85rem; }
.feed-more a { color:var(--accent); }
/* A re-forecast announces itself once: changed times fade from a warn wash.
   Reduced motion gets the same signal standing still. */
@keyframes cs-flash { from { background:color-mix(in srgb, var(--warn) 34%, transparent); }
    to { background:transparent; } }
.cs-flash { animation:cs-flash 1.4s var(--ease-snap); }
@media (prefers-reduced-motion:reduce) {
    .cs-flash { animation:none; outline:1px solid var(--warn); outline-offset:-1px; }
}

/* ---- A card per crew stop (2026-09): tasks, what to have out, parking and
   a 48px "We're here" — gloves, dark, cold hands. Cards tile where there is
   room; a stop the runner has been through folds down to its arrival. ---- */
.stop-cards { display:grid; gap:12px; align-items:start; grid-template-columns:repeat(auto-fill, minmax(300px, 1fr)); }
.stop-card { background:var(--glass-2); border:1px solid var(--line); border-radius:var(--r-card);
    padding:12px 14px 14px; display:flex; flex-direction:column; gap:8px; min-width:0; }
.stop-card[hidden] { display:none; }
.stop-card .sc-name { font:800 1.2rem/1.2 var(--display); color:var(--ink); margin:2px 0 0; }
.sc-when { display:flex; flex-wrap:wrap; align-items:baseline; gap:4px 10px; margin-top:2px; }
.sc-eta { font:700 1.35rem/1.2 var(--mono); font-variant-numeric:tabular-nums; color:var(--c-crew);
    border-radius:var(--r-ctl); }
.sc-eta.late { color:var(--warn); }
.sc-note { color:var(--muted); font-size:.82rem; }
.sc-whos { display:flex; flex-wrap:wrap; gap:4px 12px; margin-top:6px; }
.sc-who { display:inline-flex; align-items:center; gap:6px; font-size:.82rem; color:var(--ink-soft); }
.sc-who .dot { width:8px; height:8px; border-radius:50%; background:var(--pc, var(--muted)); }
/* "Runner's in" is the big one and asks once (armed = the confirm step);
   "We're here" is crew presence, a quieter full-width step below it. */
.sc-runner.export-btn { width:100%; min-height:48px; font-size:1rem; font-weight:700; white-space:normal; }
.sc-runner.armed, .nt-runner.armed { outline:2px solid var(--accent); outline-offset:2px; }
.sc-crew-here.export-btn { width:100%; min-height:44px; font-size:.92rem; }
.sc-crew-here:disabled { opacity:.7; cursor:default; }
.sc-present { margin:0; color:var(--ink-soft); font-size:.86rem; }
.sc-present[hidden] { display:none; }
.sc-done-line { margin:0; color:var(--ok); font-size:.88rem; font-weight:600; }
.sc-done-line[hidden], .sc-runner[hidden] { display:none; }
.stop-card .sc-h { margin:6px 0 0; color:var(--muted); font:500 max(.7rem, var(--mono-cap))/1.3 var(--mono);
    text-transform:uppercase; letter-spacing:.1em; }
.stop-card ul.check { columns:1; margin:0; }
.stop-card ul.check li { padding-top:7px; padding-bottom:7px; min-height:36px; }
.stop-card ul.check li::before { top:12px; }
.stop-card ul.check li.done::after { top:10px; }
.sc-park { margin:0; font-size:.88rem; color:var(--ink); }
.sc-dir.export-btn { align-self:flex-start; min-height:44px; text-decoration:none; font-size:.86rem; }
/* Been and gone: the arrival stays, the lists fold away. */
.stop-card.sc-done { opacity:.8; }
.stop-card.sc-done .sc-h, .stop-card.sc-done ul.check, .stop-card.sc-done .sc-park,
.stop-card.sc-done .sc-dir { display:none; }

/* ---- Night mode (2026-09): huge type, red-safe, one thumb. ----
   The palette is tokens scoped to body.night, so every component below
   repaints without a rule of its own: warm ambers on near-black, almost no
   blue anywhere (blue light is what wrecks night vision and wakes the
   crew). It overrides both themes on purpose — at 2 AM the choice is the
   night, not Tokyo Night or First Light. Derived tokens are restated here
   because a color-mix resolves where it is declared (:root), not here. */
body.cs-page.night {
    --bg-0:#0a0806; --bg-1:#120d09; --bg-2:#1b140d;
    --bg-well:#060403;
    --ink:#e8c7a0; --ink-bright:#ffd9ad; --muted:#a8865f; --faint:#6e5638;
    --ink-soft:color-mix(in srgb, var(--ink) 82%, var(--muted));
    --accent:#ffb066; --accent-ink:#0a0806;
    --line:rgba(255, 176, 102, 0.16); --line-faint:rgba(255, 176, 102, 0.09);
    --glass:rgba(255, 176, 102, 0.04); --glass-2:rgba(255, 176, 102, 0.07);
    --fill-idle:rgba(232, 199, 160, 0.05); --fill-hover:rgba(232, 199, 160, 0.1);
    --fill-selected:rgba(232, 199, 160, 0.2); --edge:rgba(232, 199, 160, 0.4);
    --c-crew:#ffb066; --ok:#e0a860; --warn:#ff9a4d; --bad:#ff6a3d;
    background:var(--bg-0); }
/* Chrome drops away: only the night panel is left in the page column. */
body.night .wrap > :not(#cs-night) { display:none; }
#cs-night { display:flex; flex-direction:column; gap:6px; min-height:calc(100vh - 80px);
    padding:8px 4px 24px; color:var(--ink); }
#cs-night[hidden] { display:none; }
.nt-top { display:flex; justify-content:space-between; gap:10px; color:var(--muted);
    font-size:.8rem; letter-spacing:.1em; text-transform:uppercase; margin-bottom:14px; }
#cs-night .nt-label { color:var(--muted); margin-top:10px; }
.nt-name { font:800 clamp(2rem, 9vw, 3.2rem)/1.05 var(--display); color:var(--ink-bright); margin:2px 0 0; }
.nt-clock { font:700 clamp(3rem, 16vw, 5.5rem)/1 var(--mono); font-variant-numeric:tabular-nums;
    color:var(--accent); border-radius:var(--r-ctl); width:fit-content; }
.nt-note { font-size:1.05rem; margin:4px 0 0; color:var(--ink-soft); }
.nt-have { font-size:1.1rem; line-height:1.5; margin:14px 0 0; }
.nt-have b { color:var(--ink-bright); }
.nt-acts { margin-top:auto; padding-top:24px; display:flex; flex-direction:column; gap:12px; }
.nt-runner { width:100%; min-height:64px; border-radius:var(--r-card); cursor:pointer;
    border:2px solid var(--accent); background:transparent; color:var(--ink-bright);
    font:800 1.3rem/1.2 var(--display); letter-spacing:.04em; padding:10px 16px; }
.nt-runner:active { background:var(--fill-selected); }
.nt-runner[hidden] { display:none; }
.nt-row { display:flex; gap:10px; }
.nt-small { flex:1; min-height:48px; display:inline-flex; align-items:center; justify-content:center;
    border:1px solid var(--line); border-radius:var(--r-ctl); background:var(--glass);
    color:var(--ink); font:600 .95rem/1 var(--body); text-decoration:none; cursor:pointer; }
.nt-small[hidden] { display:none; }
.nt-small:disabled { opacity:.7; cursor:default; }
#cs-night button:focus-visible, #cs-night a:focus-visible { outline:2px solid var(--accent); outline-offset:2px; }
#cs-night-btn[aria-pressed="true"] { border-color:var(--accent); color:var(--accent); }
@media (min-width:861px) { #cs-night { max-width:560px; margin:0 auto; } }

/* ---- Roles and stops (2026-09): Everyone | Just mine, who you are on this
   sheet, and one line per person. The pressed segment is the accent's quiet
   fill — the only hue on a control; people keep their dots. ---- */
.cs-roles { display:flex; flex-wrap:wrap; align-items:center; gap:10px 14px; margin:0 0 14px; }
.cs-seg { display:inline-flex; border:1px solid var(--edge-soft); border-radius:var(--r-ctl); overflow:hidden; }
.cs-seg button { min-height:44px; padding:0 16px; border:0; background:transparent; color:var(--muted);
    font:600 .86rem/1 var(--body); cursor:pointer;
    transition:background var(--dur-ctl) var(--ease-snap), color var(--dur-ctl) var(--ease-snap); }
.cs-seg button + button { border-left:1px solid var(--edge-soft); }
.cs-seg button:hover { background:var(--fill-hover); color:var(--ink); }
.cs-seg button[aria-pressed="true"] { background:color-mix(in srgb, var(--accent) 18%, transparent); color:var(--ink); }
.cs-you { display:flex; flex-wrap:wrap; align-items:center; gap:8px 12px; margin-left:auto; }
.cs-you-t { font-size:.88rem; color:var(--ink-soft); }
.cs-you-t b { color:var(--ink); }
.cs-you-edit.export-btn { min-height:44px; padding:0 14px; font-size:.84rem; }
.cs-roles-hint { flex-basis:100%; margin:0; color:var(--warn); font-size:.84rem; }
.cs-who-list { flex-basis:100%; list-style:none; margin:0; padding:0; display:flex; flex-direction:column; gap:6px; }
.cs-who-list li { display:flex; flex-wrap:wrap; align-items:center; gap:4px 9px; font-size:.86rem; }
.cs-who-list .dot { width:9px; height:9px; border-radius:50%; background:var(--pc, var(--muted)); }
.cs-who-list .role { padding:3px 8px; }
.cs-who-stops { color:var(--muted); }
.cs-role-form .cs-seg { margin:0 0 14px; }
.cs-role-form .cs-seg button { text-transform:capitalize; }
.cs-role-stops { border:0; margin:0; padding:0; display:flex; flex-direction:column; max-height:46vh; overflow:auto; }
.cs-role-stops legend { margin-bottom:6px; }
.cs-role-stops label { display:flex; align-items:center; gap:10px; min-height:44px; padding:0 4px;
    border-top:1px solid var(--line-faint); cursor:pointer; font-size:.92rem; }
.cs-role-stops label .data { margin-left:auto; color:var(--muted); font-size:.78rem; }
.cs-role-stops input { width:18px; height:18px; accent-color:var(--accent); }
.cs-role-acts { display:flex; justify-content:flex-end; gap:8px; margin-top:14px; }
.cs-role-acts .export-btn { min-height:44px; padding:0 18px; }
.idm-other { flex-wrap:wrap; } .idm-other input { min-width:0; flex-basis:160px; }
.idm-other select { background:var(--fill-idle); border:1px solid var(--line); border-radius:var(--r-ctl);
    color:var(--ink); font:inherit; padding:0 10px; min-height:44px; }

/* ---- Install hint + offline badge (KRI-125) ---- */
.pwa-tip { display:flex; align-items:center; gap:10px; margin:0 0 14px;
    background:var(--glass); border:1px solid var(--line); border-radius:var(--r-card); padding:11px 13px; }
.pwa-ico { flex:0 0 auto; width:18px; height:18px; fill:none; stroke:var(--accent);
    stroke-width:1.8; stroke-linecap:round; stroke-linejoin:round; }
.pwa-txt { flex:1 1 auto; font-size:.85rem; color:var(--ink); }
.pwa-btn.export-btn { flex:0 0 auto; padding:7px 15px; font-size:.82rem; }
.pwa-x { flex:0 0 auto; background:none; border:0; color:var(--muted); font-size:1.15rem;
    line-height:1; cursor:pointer; padding:2px 4px; }
.pwa-x:hover { color:var(--ink); }
/* Offline, as a state (2026-09). A warn-tinted bar on the tokens, drawn by
   crew-live.js: what is saved, how many edits are queued, when it last
   synced. Ink stays ink — the tint says "needs a look", the words say why. */
.cs-offline { display:flex; flex-wrap:wrap; align-items:center; gap:6px 10px; margin:0 0 12px;
    padding:10px 13px; border-radius:var(--r-card); color:var(--ink); font-size:.86rem; line-height:1.45;
    background:color-mix(in srgb, var(--warn) 14%, transparent);
    border:1px solid color-mix(in srgb, var(--warn) 40%, transparent);
    animation:tg-pop-in var(--dur-layer) var(--ease-snap); }
.cs-offline[hidden] { display:none; }
.cs-off-ico { flex:0 0 auto; width:16px; height:16px; fill:none; stroke:var(--warn);
    stroke-width:2; stroke-linecap:round; stroke-linejoin:round; }
.cs-off-txt { flex:1 1 260px; }
.cs-off-txt b { font-weight:700; }
.cs-off-q { color:var(--ink-soft); }
.cs-off-q:not([data-n="0"]) { color:var(--ink); font-weight:600; }
.cs-off-when { flex:0 0 auto; margin-left:auto; color:var(--muted); font-size:.78rem; white-space:nowrap; }
@media (prefers-reduced-motion:reduce) { .cs-offline { animation:none; } }
/* Installed: no browser chrome, so the sheet owes itself the notch gap. */
@media (display-mode:standalone) {
    .wrap { padding-top:max(18px, env(safe-area-inset-top));
            padding-bottom:max(56px, env(safe-area-inset-bottom)); }
}
html.tg-standalone .wrap { padding-top:max(18px, env(safe-area-inset-top));
    padding-bottom:max(56px, env(safe-area-inset-bottom)); }

/* ---- Pull to refresh (KRI-131). Chrome's own swipe-to-refresh is turned
   off here on purpose: with it left on, the installed app would run two
   competing gestures with two indicators, and iOS would still have none.
   One gesture, one ring, same on both. ---- */
@media (display-mode:standalone) { html, body { overscroll-behavior-y:contain; } }
html.tg-standalone, html.tg-standalone body { overscroll-behavior-y:contain; }
#tg-ptr { position:fixed; top:max(6px, env(safe-area-inset-top)); left:0; right:0;
    display:flex; justify-content:center; z-index:2500; pointer-events:none;
    transform:translateY(-52px); will-change:transform; }
.ptr-ring { width:38px; height:38px; border-radius:50%; display:grid; place-items:center;
    background:var(--bg-1); border:1px solid var(--line);
    box-shadow:0 6px 20px rgba(0,0,0,.5); }
.ptr-ring svg { width:18px; height:18px; fill:none; stroke:var(--muted); stroke-width:2;
    stroke-linecap:round; stroke-linejoin:round; }
/* Past the threshold the ring commits to the action — the accent, the one
   hue on duty for "this will do something" (TG-282). */
#tg-ptr.armed .ptr-ring { border-color:color-mix(in srgb, var(--accent) 55%, transparent); }
#tg-ptr.armed .ptr-ring svg { stroke:var(--accent); }
#tg-ptr.spin .ptr-ring { border-color:color-mix(in srgb, var(--accent) 55%, transparent); }
#tg-ptr.spin .ptr-ring svg { stroke:var(--accent); animation:ptr-spin .8s linear infinite; }
@keyframes ptr-spin { to { transform:rotate(360deg); } }
@media (prefers-reduced-motion:reduce) { #tg-ptr.spin .ptr-ring svg { animation:none; } }
.cs-page .foot { margin-top:26px; text-align:center; color:var(--muted); font-size:.85rem; }
/* TG-334: the quietest thing on the page. A crew sheet belongs to the
   runner, not to us, so this is footer-weight text under the footer's own
   line — never a button, never above the fold. */
.foot .rd-ask { font-size:.78rem; opacity:.85; margin-top:2px; }
/* Course change notices: the race changed the course after this plan was shared. */
.cs-course-change { margin:10px 0 4px; padding:10px 12px; border:1px solid var(--line); border-left:3px solid var(--gold);
    border-radius:10px; font-size:.9rem; line-height:1.45; color:var(--ink); }
.cs-course-change b { color:var(--ink-bright); }
.foot .export-btn { margin:10px 0 4px; }
.foot a.plain { color:var(--accent); }
@media (max-width:480px) { ul.check { columns:1; } .card { padding:12px 14px 14px; }
    .card-h, details.card summary { margin:-12px -14px 12px; padding:11px 14px; }
    details.card summary { margin-bottom:0; } }
/* Print: LAYOUT ONLY (TG-347).

   This block used to hand-paint about 25 dark-to-light overrides — every
   one of them a second opinion about what the sheet looks like on a white
   ground, maintained separately from the theme that already answers that
   question. The script below applies First Light for the print pass
   instead, and because this page is token-driven throughout, that is the
   whole colour story. What is left here is what a palette cannot do: hide
   chrome, open collapsed cards, fit the table, and keep the map off
   page 1. */

/* ---- Focus (2026-09): every control on the sheet answers the keyboard the
   way the rest of the site does — the accent ring, outside the edge. ---- */
.cs-page button:focus-visible, .cs-page a:focus-visible, .cs-page select:focus-visible,
.cs-page input:focus-visible, .cs-page textarea:focus-visible, .cs-page summary:focus-visible,
.cs-page ul.check li[data-k]:focus-visible { outline:2px solid var(--accent); outline-offset:2px; }

/* Follow mode (2026-09): /plan/{followToken}/follow, the read-only page for
   the people at home. Same shell as the sheet; the eyebrow says whose day
   this is before the race name does. */
.cs-follow .fl-eyebrow { margin:14px 0 2px; }
.cs-follow .cs-follow-map { padding:10px; }
.cs-follow .cs-follow-map #cs-map { margin:0; }
.cs-follow .foot p { margin:4px 0; }

/* ---- Crewing two runners in one sheet (2026-09) ----------------------------
   A grouped sheet gets a tab per runner (name · current mile) and a Merged
   tab. Runner tabs are links to that runner's own sheet; Merged swaps the
   sheet's sections for #cs-merged. The tabs are a segmented control on the
   accent like .cs-seg; runner series dots (--rc) are data, never on a tab. */
#cs-group-mount:empty { display:none; }
.grp-tabs { display:flex; gap:0; margin:6px 0 14px; border:1px solid var(--edge-soft); border-radius:var(--r-ctl);
    overflow-x:auto; -webkit-overflow-scrolling:touch; scrollbar-width:none; width:fit-content; max-width:100%; }
.grp-tabs::-webkit-scrollbar { display:none; }
.grp-tab { flex:0 0 auto; display:inline-flex; align-items:center; gap:0; min-height:44px; padding:0 16px;
    border:0; background:transparent; color:var(--muted); font:500 .9rem/1 var(--body); text-decoration:none;
    white-space:nowrap; cursor:pointer; transition:background var(--dur-ctl) var(--ease-snap), color var(--dur-ctl) var(--ease-snap); }
.grp-tab + .grp-tab { border-left:1px solid var(--edge-soft); }
.grp-tab b { font-weight:600; color:inherit; }
.grp-tab .data { font-size:.84rem; }
.grp-tab-sep { opacity:.6; white-space:pre; }
.grp-tab:hover { background:var(--fill-hover); color:var(--ink); }
.grp-tab[aria-current="page"] { background:color-mix(in srgb, var(--accent) 18%, transparent); color:var(--ink); }
.grp-dot { display:inline-block; width:8px; height:8px; border-radius:50%; margin-right:7px; flex:0 0 auto;
    background:var(--rc, transparent); border:1.5px solid var(--rc, var(--muted)); vertical-align:middle; }

/* Merged: everything but the header, the tabs and the merged view steps aside. */
body.grp-merged-on .wrap > :not(.brand):not(h1):not(#cs-group-mount):not(#cs-merged):not(.foot) { display:none; }
#cs-merged[hidden] { display:none; }
.grp-note { margin:0 0 12px; padding:9px 12px; border-radius:var(--r-ctl); font-size:.88rem; color:var(--ink);
    background:color-mix(in srgb, var(--accent) 12%, transparent); }
.grp-nexts { display:grid; gap:0 14px; }
@media (min-width:861px) { .grp-nexts { grid-template-columns:repeat(auto-fit, minmax(0, 1fr)); align-items:start; } }
.grp-who { display:inline-flex; align-items:center; }
.grp-nx .nx-clock { font-size:clamp(2rem, 9vw, 2.6rem); }
.grp-in.armed { outline:2px solid var(--accent); outline-offset:2px; }
.grp-in.export-btn { white-space:normal; font-weight:700; }
.grp-stops .card-h { margin-bottom:6px; }
.cs-page .grp-table { min-width:420px; }
.grp-table th .grp-dot { margin-right:6px; }
.grp-table th.num, .grp-table td.num { text-align:right; }
.grp-stop b { display:block; font-weight:600; color:var(--ink); font-family:var(--body); }
.grp-mi { color:var(--muted); font-size:.74rem; }
.cs-page .grp-table td.grp-stop { font-family:var(--body); }
.grp-cell-in { color:var(--ok); font-weight:700; }
.grp-first { color:var(--ink); font-weight:700; }
.cs-page .grp-table td.grp-gap { font-family:var(--body); color:var(--ink-soft); white-space:normal; min-width:150px; }
.grp-gap .data { color:var(--ink); font-weight:600; }
.grp-gap.stay .data { color:var(--ink); }
.grp-gap.split { color:var(--warn); }
.grp-gap.split .data { color:var(--warn); }
.grp-table tr.grp-past td { opacity:.62; }
.grp-table tr.grp-now td { background:color-mix(in srgb, var(--accent) 9%, transparent); }
.grp-table tr.grp-now td:first-child { box-shadow:inset 3px 0 0 var(--accent); }
.grp-legend { margin:10px 0 0; color:var(--muted); font-size:.8rem; }
.grp-feed-r { display:inline-flex; align-items:center; margin-right:8px; padding:2px 7px 2px 6px;
    border:1px solid var(--line); border-radius:var(--r-ctl); font:600 .72rem/1.3 var(--body); color:var(--ink-soft); }
.grp-feed-r .grp-dot { width:7px; height:7px; margin-right:5px; }

/* The invite, and the card where a sheet joins or leaves a group. */
.grp-invite { padding-top:16px; }
.grp-invite-h { font:800 1.2rem/1.25 var(--display); margin:4px 0 6px; color:var(--ink); }
.grp-invite-p { margin:0; color:var(--ink-soft); font-size:.9rem; max-width:68ch; }
.grp-acts { display:flex; flex-wrap:wrap; gap:8px; margin-top:14px; }
.grp-acts .export-btn, .grp-row .export-btn, .grp-leave.export-btn { min-height:44px; padding:0 18px; }
.grp-msg { margin:8px 0 0; font-size:.86rem; color:var(--ink-soft); }
.grp-msg[hidden] { display:none; }
.grp-form { margin-top:4px; }
.grp-form[hidden] { display:none; }
.grp-form .data-label { display:block; margin-bottom:6px; }
.grp-row { display:flex; flex-wrap:wrap; gap:8px; }
.grp-row input { flex:1 1 240px; min-width:0; min-height:44px; padding:0 12px; font:500 .9rem/1 var(--body);
    color:var(--ink); background:var(--fill-idle); border:1px solid var(--line); border-radius:var(--r-ctl); }
.grp-row input:focus { outline:none; border-color:color-mix(in srgb, var(--accent) 55%, transparent); }
.grp-members { list-style:none; margin:0 0 12px; padding:0; display:flex; flex-direction:column; gap:8px; }
.grp-members li { display:flex; flex-wrap:wrap; align-items:center; gap:4px 10px; font-size:.92rem; }
.grp-members li .grp-dot { margin-right:0; }
.grp-m-sub { color:var(--muted); font-size:.82rem; }
.grp-leave.export-btn { margin-bottom:16px; }
@media print { #cs-group-mount, #cs-merged, #cs-group { display:none; } }
.grp-feed .feed-i { grid-template-columns:7.2em 1fr; }
@media (max-width:860px) {
    .cs-page .grp-table { min-width:0; font-size:.8rem; }
    .cs-page .grp-table th, .cs-page .grp-table td { padding:6px 5px; }
    .cs-page .grp-table td.num { white-space:normal; }
    .cs-page .grp-table td.grp-gap { min-width:0; }
    .grp-feed .feed-i { grid-template-columns:1fr; gap:2px; }
}

/* /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; }

