/* ==========================================================================
   /about — "The Stretch"
   The TTC's own visual language at poster scale: dark ground, white type,
   and line colour only where it means something.

   Modes (set on <html> before first paint, see the boot script):
     .js          scripts run
     .motion      motion allowed (no reduced-motion, no ?rm). Every pre-hidden
                  reveal state lives under .motion, so the no-JS / reduced-
                  motion page is simply this stylesheet without it.
     .gl-ok       WebGL looks possible; S2 uses the tall scroll stage
     .gl          three.js is up and the canvas has replaced the posters
     .intro       the hero copy is still waiting for its entrance (set by the
                  boot script, dropped after 600 ms unless main.js claims it)
     .motion-paused / .lite   decorative loops stop (Pause motion / Low Power)
     .failsafe    main.js never arrived: show everything
   Theme: data-theme="dark|light" on <html> (the boot script, before first
   paint). :root holds the dark tokens; html[data-theme="light"] overrides
   them at the end of this block. Paired pictures carry .t-dark / .t-light.
   Utilities: .js-only (controls and copy that only work with main.js) and
   .no-js-only (their fallback) swap on .js / .failsafe.
   ========================================================================== */

@font-face {
    font-family: "Archivo";
    src: url("/about/fonts/archivo-var.woff2?v=1") format("woff2");
    font-weight: 600 900;
    font-stretch: 62% 125%;
    font-display: swap;
}
@font-face {
    font-family: "Archivo Fallback";
    src: local("HelveticaNeue-CondensedBold"), local("Arial Narrow Bold"), local("Roboto Condensed Bold"), local("Arial Bold");
    size-adjust: 96%;
}

:root {
    color-scheme: dark;
    /* ground */
    --bg: #0F1014; --bg-sunk: #0B0C0F; --bg-raise: #16181D; --bg-raise-2: #1C1F26; --navy: #0C1421;
    /* ink (smallest text tone is --ink-3) */
    --ink: #FFFFFF; --ink-2: rgba(255,255,255,.66); --ink-3: rgba(255,255,255,.52); --ink-4: rgba(255,255,255,.18);
    --hair: rgba(255,255,255,.08); --hair-strong: rgba(255,255,255,.14);
    /* the ground and the ink as channels, for scrims and washes */
    --bg-rgb: 15,16,20; --ink-rgb: 255,255,255;
    /* brand */
    --coral: #EC6964; --coral-hi: #F07A75; --coral-ink: #1A0C0B; --peach: #EAC4B5; --halo: #FFCD00; --halo-hi: #FFD633;
    /* status */
    --red: #EF4444; --amber: #FF9F1C; --shuttle: #3B82F6; --ok: #34C759; --access: #528CCB;
    --red-text: color-mix(in srgb, #EF4444 80%, #fff);   /* red as small text: AA on raised surfaces (the board lifts it the same way) */
    --amber-text: var(--amber); --coral-text: var(--coral);   /* as text; light mode darkens them to AA */
    --halo-ink: var(--halo);   /* the halo yellow as a small glyph on the ground */
    --shuttle-aa: color-mix(in srgb, #3B82F6 78%, #0F1014);   /* shuttle fill that carries white 12px text at AA */
    --hv-delay: #00E5FF; --hv-susp: #FF00FF; --quiet: #3A3B40;
    --red-rgb: 239,68,68; --amber-rgb: 255,159,28;
    /* surfaces */
    --fill-1: rgba(255,255,255,.06); --fill-2: rgba(255,255,255,.1);
    --glass-bg: rgba(24,26,32,.55); --glass-edge: rgba(255,255,255,.10); --glass-sheen: rgba(255,255,255,.14); --glass-solid: rgba(22,24,29,.96);
    --card-glass: rgba(22,24,29,.72); --nav-bg: rgba(15,16,20,.62); --screen-bg: #000;
    --dot-grid: rgba(255,255,255,.035); --glow-halo: rgba(255,205,0,.06); --visual-glow: rgba(255,255,255,.05);
    --widget-bg: linear-gradient(160deg, rgba(40,43,52,.95), rgba(24,26,32,.95));
    --deck-dim: brightness(.72); --floor-shadow: rgba(0,0,0,.5); --hv-border: #fff;
    --focus: var(--halo);
    /* line colours as text (.f-num): lifted toward the ink until they read */
    --tick-mix: #fff; --tick-p: 70%;
    /* TTC signage */
    --l1: #FFCD00; --l2: #00A651; --l4: #8B288F; --l5: #F58220; --l6: #808285;
    /* TTC map (schema hexes, as the app draws them) */
    --m1: #fdc90b; --m2: #31a650; --m4: #b70174; --m5: #f8831d; --m6: #959597;
    /* GO */
    --go-lw: #98002E; --go-le: #ED1B24; --go-le-aa: #D11920; --go-br: #005C8A; --go-ki: #00853F;
    --go-st: #7A5030; --go-rh: #00A4E4; --go-mi: #F58220; --go-up: #40BFD9;
    /* type */
    --f-display: "Archivo", "Archivo Fallback", -apple-system, BlinkMacSystemFont, system-ui, sans-serif;
    --f-text: -apple-system, BlinkMacSystemFont, "SF Pro Text", "Inter", system-ui, "Segoe UI", Roboto, sans-serif;
    --f-rounded: ui-rounded, "SF Pro Rounded", -apple-system, system-ui, sans-serif;
    --f-mono: ui-monospace, "SF Mono", Menlo, monospace;
    --t-hero: clamp(2.75rem, .59rem + 9.9vw, 9.5rem);
    --t-beat: clamp(2.5rem, 1.1rem + 6.9vw, 7.5rem);
    --t-h2: clamp(2.25rem, 1.4rem + 4.6vw, 5rem);
    --t-h3: clamp(1.25rem, 1rem + .9vw, 1.625rem);
    --t-lede: clamp(1.125rem, 1rem + .55vw, 1.4375rem);
    --t-body: 1.0625rem; --t-small: .9375rem; --t-micro: .8125rem; --t-eyebrow: .8125rem; --t-btn: 1.0625rem;
    --t-board: clamp(.9375rem, .5rem + 1.3vw, 1.375rem);
    --t-station: clamp(.6875rem, .55rem + .4vw, .8125rem);
    /* space & layout */
    --s-1: 4px; --s-2: 8px; --s-3: 12px; --s-4: 16px; --s-5: 24px; --s-6: 32px; --s-7: 48px; --s-8: 64px; --s-9: 96px; --s-10: 128px; --s-11: 192px;
    --gutter: max(clamp(16px, 4vw, 48px), env(safe-area-inset-left, 0px), env(safe-area-inset-right, 0px)); --container: 1360px; --section-y: clamp(96px, 12vw, 176px);
    --nav-h: 64px;
    /* radii */
    --r-tile: 3px; --r-badge-go: 6px; --r-chip: 12px; --r-frame: 14px; --r-card-sm: 20px; --r-card: 24px; --r-panel: 32px; --r-pill: 999px;
    /* shadows */
    --sh-card: inset 0 1px 0 rgba(255,255,255,.05);
    --sh-glass: inset 0 1px 0 rgba(255,255,255,.12), inset 0 -1px 0 rgba(0,0,0,.25), 0 10px 30px rgba(0,0,0,.35);
    --sh-phone: 0 0 0 1px rgba(255,255,255,.12), 0 40px 120px rgba(0,0,0,.6);
    --sh-lift: 0 60px 120px -20px rgba(0,0,0,.7);
    --sh-browser: 0 30px 80px rgba(0,0,0,.55); --sh-widget: 0 20px 50px rgba(0,0,0,.45);
    --sh-btn-halo: 0 8px 24px rgba(255,205,0,.28);
    --sh-btn-coral: 0 8px 24px rgba(236,105,100,.35);
    /* motion */
    --ease-out: cubic-bezier(.16,1,.3,1);
    --ease-io: cubic-bezier(.65,0,.35,1);
    --ease-ui: cubic-bezier(.25,.1,.25,1);
    --ease-spring: cubic-bezier(.34,1.56,.64,1);
    --d-micro: 150ms; --d-ui: 250ms; --d-swap: 450ms; --d-reveal: 700ms; --d-hero: 900ms;
    --d-breathe: 3s; --d-march: 2.4s;
    /* layers */
    --z-grid: 0; --z-gl: 1; --z-content: 2; --z-rail: 40; --z-nav: 50; --z-skip: 100;
}
@media (max-width: 899px) { :root { --nav-h: 56px; } }

/* Light: the same page on the iOS light ground. Ink is the brand's near-black;
   every text tone clears 4.5:1 on the page, the cards and the sunk panels
   (ink-3 .62 is 5.0:1 on --bg-sunk). Hues that were lifted toward white for
   the dark ground step down toward the ink instead (the web app's light
   text ramp: amber #B45309, red #B91C1C). Glows that add light are useless
   on white, so the washes become soft greys and the shadows real shadows. */
html[data-theme="light"] {
    color-scheme: light;
    --bg: #F5F5F7; --bg-sunk: #EBEBEF; --bg-raise: #FFFFFF; --bg-raise-2: #FFFFFF; --navy: #FFFFFF;
    --ink: #0F1014; --ink-2: rgba(15,16,20,.72); --ink-3: rgba(15,16,20,.62); --ink-4: rgba(15,16,20,.18);
    --hair: rgba(15,16,20,.09); --hair-strong: rgba(15,16,20,.15);
    --bg-rgb: 245,245,247; --ink-rgb: 15,16,20;
    --red-text: #B91C1C; --amber-text: #B45309; --coral-text: #A8352F; --halo-ink: #C79A00;
    --quiet: #CFD0D6;
    --fill-1: rgba(15,16,20,.05); --fill-2: rgba(15,16,20,.08);
    --glass-bg: rgba(255,255,255,.68); --glass-edge: rgba(15,16,20,.08); --glass-sheen: rgba(255,255,255,.8); --glass-solid: rgba(255,255,255,.96);
    --card-glass: rgba(255,255,255,.82); --nav-bg: rgba(245,245,247,.76); --screen-bg: #FFFFFF;
    --dot-grid: rgba(15,16,20,.075); --glow-halo: rgba(255,184,0,.10); --visual-glow: rgba(15,16,20,.035);
    --widget-bg: linear-gradient(160deg, #FFFFFF, #F1F1F5);
    --deck-dim: brightness(.94) saturate(.8); --floor-shadow: rgba(15,16,20,.16); --hv-border: #0F1014;
    --focus: #0F1014;
    --tick-mix: #0F1014; --tick-p: 48%;
    --sh-card: 0 1px 2px rgba(15,16,20,.04), 0 14px 36px -18px rgba(15,16,20,.16);
    --sh-glass: inset 0 1px 0 rgba(255,255,255,.9), 0 1px 2px rgba(15,16,20,.06), 0 10px 30px rgba(15,16,20,.10);
    --sh-phone: 0 0 0 1px rgba(15,16,20,.10), 0 40px 100px -12px rgba(15,16,20,.30);
    --sh-lift: 0 50px 100px -24px rgba(15,16,20,.42);
    --sh-browser: 0 30px 80px -12px rgba(15,16,20,.22); --sh-widget: 0 20px 50px -12px rgba(15,16,20,.20);
    --sh-btn-halo: 0 8px 24px rgba(214,160,0,.32);
}

/* ------------------------------------------------------------------ base */
*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; text-size-adjust: 100%; scroll-padding-top: calc(var(--nav-h) + 8px); overflow-x: clip; }
html:not(.motion) { scroll-behavior: smooth; }
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }
body {
    margin: 0; background: var(--bg); color: var(--ink);
    font: 400 var(--t-body)/1.55 var(--f-text);
    -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
    overflow-x: clip;
}
img, video, svg { display: block; max-width: 100%; }
img, video { height: auto; }
a { color: inherit; }
button { font: inherit; color: inherit; background: none; border: 0; padding: 0; cursor: pointer; }
h1, h2, h3, p, ol, ul, figure { margin: 0; }
ol, ul { padding: 0; list-style: none; }
h1, h2, h3 { text-wrap: balance; }
p { text-wrap: pretty; }
[hidden] { display: none !important; }
/* Paired pictures: only the current theme's is shown, and a lazy image that
   isn't shown is never fetched. No theme at all (no JS) is dark. */
html[data-theme="light"] .t-dark, html:not([data-theme="light"]) .t-light { display: none !important; }
html:not(.js) .js-only, html.failsafe .js-only { display: none !important; }
html.js:not(.failsafe) .no-js-only { display: none !important; }

.sr-only {
    position: absolute !important; width: 1px; height: 1px; padding: 0; margin: -1px;
    overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; border: 0;
}
.skip {
    position: fixed; left: 12px; top: 12px; z-index: var(--z-skip);
    padding: 12px 18px; border-radius: var(--r-pill); background: var(--halo); color: #0F1014;
    font-weight: 700; text-decoration: none; transform: translateY(-160%); transition: transform var(--d-ui) var(--ease-ui);
}
.skip:focus { transform: none; }

:focus-visible { outline: 3px solid var(--focus); outline-offset: 3px; }
.btn--web:focus-visible, .skip:focus-visible { outline-color: var(--ink); }

.container { width: min(100% - 2 * var(--gutter), var(--container)); margin-inline: auto; }
.section { position: relative; padding-block: var(--section-y); background: var(--bg); z-index: var(--z-content); }

/* ------------------------------------------------------------------ type */
h2, .beat {
    font-family: var(--f-display); font-weight: 780; font-synthesis: none;
    font-size: var(--t-h2); line-height: .95; letter-spacing: -.028em;
    font-variation-settings: "wdth" 82; font-stretch: 82%;
}
h3 { font-size: var(--t-h3); line-height: 1.2; font-weight: 650; letter-spacing: -.012em; }
.lede { font-size: var(--t-lede); line-height: 1.42; color: var(--ink-2); letter-spacing: -.005em; max-width: 34ch; }
.small, .micro { font-size: var(--t-small); line-height: 1.45; color: var(--ink-3); }
.eyebrow {
    display: flex; align-items: center; gap: 10px; margin-bottom: 20px;
    font: 700 var(--t-eyebrow)/1 var(--f-display); font-synthesis: none;
    text-transform: uppercase; letter-spacing: .14em; color: var(--ink-2);
    font-variation-settings: "wdth" 100;
}
.eyebrow::before { content: ""; width: 10px; height: 10px; background: var(--tick, var(--ink-3)); flex: none; }
.eyebrow .ticks { display: flex; gap: 4px; }
.eyebrow .ticks i { width: 10px; height: 10px; background: var(--c); }
.hero-eyebrow::before { display: none; }
/* The GO eyebrow's last place name goes on narrow phones, so it keeps one line. */
@media (max-width: 479px) { .hero-eyebrow .eb-more { display: none; } }
h2 + .lede { margin-top: 24px; }
.wordmark { font: 800 18px/1 var(--f-rounded); letter-spacing: -.02em; }

/* ---------------------------------------------------------------- buttons */
.btn {
    display: inline-flex; align-items: center; justify-content: center; gap: 10px;
    height: 52px; padding: 0 22px; border-radius: var(--r-pill);
    font: 600 var(--t-btn)/1 var(--f-text); text-decoration: none; white-space: nowrap;
    transition: transform var(--d-micro) var(--ease-ui), background-color var(--d-micro) var(--ease-ui), box-shadow var(--d-ui) var(--ease-ui);
}
.btn:hover { transform: translateY(-1px); }
.btn:active { transform: scale(.98); }
.btn .arr { transition: transform var(--d-micro) var(--ease-ui); }
.btn:hover .arr { transform: translateX(3px); }
.btn--web { background: var(--halo); color: #0F1014; }
.btn--web:hover { background: var(--halo-hi); box-shadow: var(--sh-btn-halo); }
.btn--ios { background: var(--coral); color: var(--coral-ink); }
.btn--ios:hover { background: var(--coral-hi); box-shadow: var(--sh-btn-coral); }
.btn--store { background: #000; color: #fff; box-shadow: inset 0 0 0 1px #A6A6A6; }
.btn--glass { color: var(--ink); }
.btn--sm { height: 40px; padding: 0 16px; font-size: .9375rem; }
@media (max-width: 599px) { .btn { height: 48px; } .btn--sm { height: 40px; } }
/* Touch: every target reaches 44px without changing how it looks with a mouse. */
@media (pointer: coarse) { .btn--sm { height: 44px; } }
.icon-btn { width: 44px; height: 44px; border-radius: 50%; display: grid; place-items: center; color: var(--ink); }
.text-btn { font-size: var(--t-small); color: var(--ink-2); text-decoration: underline; text-underline-offset: 3px; min-height: 44px; }
.text-btn:hover { color: var(--ink); }
.link-arrow { color: var(--ink); font-weight: 600; text-decoration: none; min-height: 44px; display: inline-flex; align-items: center; gap: 6px; }
.link-arrow:hover { text-decoration: underline; text-underline-offset: 3px; }

/* The first CTA is filled, the second glass; iPhones see the iOS one first. */
.ctas { display: flex; flex-wrap: wrap; gap: 12px; }
.ctas .cta--ios.btn--ios { background: var(--glass-bg); color: var(--ink); box-shadow: var(--sh-glass); -webkit-backdrop-filter: blur(24px) saturate(170%); backdrop-filter: blur(24px) saturate(170%); }
html.is-iphone .ctas .cta--ios { order: -1; }
html.is-iphone .ctas .cta--ios.btn--ios { background: var(--coral); color: var(--coral-ink); box-shadow: none; -webkit-backdrop-filter: none; backdrop-filter: none; }
html.is-iphone .ctas .cta--web { background: var(--glass-bg); color: var(--ink); box-shadow: var(--sh-glass); -webkit-backdrop-filter: blur(24px) saturate(170%); backdrop-filter: blur(24px) saturate(170%); }
.lbl-iphone { display: none; }
html.is-iphone .lbl-iphone { display: inline; }
html.is-iphone .lbl-other { display: none; }

/* Launch switch */
html[data-ios="beta"] .ios-appstore, html[data-ios="appstore"] .ios-beta { display: none !important; }

/* ------------------------------------------------------------------ glass */
.glass {
    position: relative; background: var(--glass-bg);
    -webkit-backdrop-filter: blur(24px) saturate(170%); backdrop-filter: blur(24px) saturate(170%);
    border: 1px solid var(--glass-edge); box-shadow: var(--sh-glass);
}
.glass::before {
    content: ""; position: absolute; inset: 0; border-radius: inherit; pointer-events: none;
    background: linear-gradient(135deg, var(--glass-sheen), transparent 42%); opacity: .5;
}
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) { .glass { background: var(--glass-solid); } }
.card { background: var(--bg-raise); border: 1px solid var(--hair); border-radius: var(--r-card); box-shadow: var(--sh-card); }
@media (max-width: 599px) { .card { border-radius: var(--r-card-sm); } }

/* ------------------------------------------------------------ phone frame */
.phone {
    --w: 300px; position: relative; width: var(--w); flex: none;
    padding: calc(var(--w) * .03); border-radius: calc(var(--w) * .14);
    background: #050608; box-shadow: var(--sh-phone);
}
.phone .screen {
    position: relative; aspect-ratio: 780 / 1695; overflow: hidden;
    border-radius: calc(var(--w) * .115); background: var(--screen-bg);
    -webkit-mask-image: -webkit-radial-gradient(white, black);
}
.phone .screen > img, .phone .screen > video { width: 100%; height: 100%; object-fit: cover; }
.phone .screen::after { /* Dynamic Island, for captures that don't have one baked in */
    content: ""; position: absolute; left: 50%; top: 1.25%; width: 31%; height: 3.6%;
    transform: translateX(-50%); border-radius: var(--r-pill); background: #000; pointer-events: none;
}
.phone.no-island .screen::after { display: none; }

/* ------------------------------------------------------------------- nav */
.nav {
    position: fixed; inset: 0 0 auto; z-index: var(--z-nav);
    padding-top: env(safe-area-inset-top);
    transition: transform 240ms var(--ease-io), background-color var(--d-ui), border-color var(--d-ui);
    border-bottom: 1px solid transparent;
}
/* Without main.js (no JS, or the failsafe fired) nothing adds .is-solid, and
   a transparent fixed bar would print over the content it scrolls past. */
.nav.is-solid, html:not(.js) .nav, html.failsafe .nav {
    background: var(--nav-bg);
    -webkit-backdrop-filter: blur(24px) saturate(170%); backdrop-filter: blur(24px) saturate(170%);
    border-bottom-color: var(--hair);
}
/* Past its own height too: the glass pill's shadow would otherwise hang
   below the hidden bar (a grey smudge on the light page). */
.nav.is-hidden { transform: translateY(calc(-100% - 48px)); }
.nav-inner {
    height: var(--nav-h); display: flex; align-items: center; gap: 24px;
    width: min(100% - 2 * var(--gutter), var(--container)); margin-inline: auto;
}
.nav-brand { display: flex; flex: none; align-items: center; gap: 10px; text-decoration: none; min-height: 44px; }
.nav-brand img { width: 28px; height: 28px; border-radius: 22.5%; }
/* TTC | GO: the app's network switcher as two links, one per page (the
   current one carries aria-current). A sunk track with the current page as
   a raised segment, the web app's switcher at nav size. */
.net-switch {
    display: flex; flex: none; padding: 3px; gap: 2px; border-radius: var(--r-pill);
    background: var(--fill-1); box-shadow: inset 0 0 0 1px var(--hair);
}
.net-switch a {
    position: relative; min-width: 44px; height: 30px; padding: 0 12px; border-radius: var(--r-pill);
    display: grid; place-items: center; text-decoration: none; color: var(--ink-3);
    font: 700 .8125rem/1 var(--f-display); font-synthesis: none; letter-spacing: .06em; font-variation-settings: "wdth" 100;
    transition: color var(--d-micro) var(--ease-ui), background-color var(--d-micro) var(--ease-ui);
}
.net-switch a::after { content: ""; position: absolute; inset: -7px -1px; }   /* a 44px target at a 30px segment */
.net-switch a:hover { color: var(--ink); }
.net-switch a[aria-current="page"] { background: var(--bg-raise-2); color: var(--ink); box-shadow: 0 1px 3px rgba(0,0,0,.28), inset 0 0 0 1px var(--hair-strong); }
html[data-theme="light"] .net-switch a[aria-current="page"] { box-shadow: 0 1px 3px rgba(15,16,20,.14), inset 0 0 0 1px var(--hair); }
.nav-links { display: flex; gap: 4px; margin-inline: auto; }
.nav-links a { padding: 10px 14px; border-radius: var(--r-pill); color: var(--ink-2); text-decoration: none; font-size: .9375rem; font-weight: 500; white-space: nowrap; }
@media (pointer: coarse) { .nav-links a { padding-block: 12px; } }
.nav-links a:hover { color: var(--ink); background: var(--fill-1); }
.nav-actions { display: flex; align-items: center; gap: 8px; }
.live-pill {
    height: 32px; padding: 0 12px; border-radius: var(--r-pill); display: inline-flex; align-items: center; justify-content: center; gap: 8px;
    min-width: 100px; white-space: nowrap;
    font: 600 .875rem/1 var(--f-text); text-decoration: none; font-variant-numeric: tabular-nums;
}
/* With scripts on, the pill keeps its place while it waits for data, so its
   arrival doesn't shift the nav (layout shift). */
html.js .live-pill[hidden] { display: inline-flex !important; visibility: hidden; }
@media (min-width: 900px) { .live-pill { min-width: 112px; } }   /* room for "99 active" */
.live-pill::after { content: ""; position: absolute; inset: -6px 0; }
.live-pill .dot { width: 8px; height: 8px; border-radius: 50%; background: var(--dot, var(--ink-3)); box-shadow: 0 0 0 3px color-mix(in srgb, var(--dot, transparent) 25%, transparent); }
.lbl-short { display: none; }
/* Theme toggle: shows where it goes (a sun in dark, a moon in light), which
   is also what its label says. 44px target, a quiet glyph. */
.theme-toggle {
    width: 44px; height: 44px; border-radius: 50%; display: grid; place-items: center; flex: none;
    color: var(--ink-2); transition: color var(--d-micro) var(--ease-ui), background-color var(--d-micro) var(--ease-ui);
}
.theme-toggle:hover { color: var(--ink); background: var(--fill-1); }
.theme-toggle svg { transition: transform var(--d-swap) var(--ease-out); }
.theme-toggle:active svg { transform: rotate(-30deg) scale(.9); }
html[data-theme="light"] .theme-toggle .i-sun, html:not([data-theme="light"]) .theme-toggle .i-moon { display: none; }
html:not(.js) .theme-toggle { display: none; }
.nav-progress { position: absolute; left: 0; right: 0; bottom: -1px; height: 2px; background: var(--halo); transform-origin: 0 50%; transform: scaleX(var(--progress, 0)); }
@media (min-width: 1100px) { .nav-progress { display: none; } }
@media (max-width: 899px) {
    .nav-links { display: none; }
    .nav-actions { margin-left: auto; }
    .nav-map, .nav-ios { display: none; }
    html:not(.is-iphone) .nav-map { display: inline-flex; background: var(--halo); color: #0F1014; }
    html.is-iphone .nav-ios.ios-beta { display: inline-flex; }
    html.is-iphone[data-ios="appstore"] .nav-ios.ios-appstore { display: inline-flex; }
    .lbl-long { display: none; } .lbl-short { display: inline; }
    /* Room for the theme toggle beside the pill and the button, with the
       whole wordmark still showing at 390px. */
    .nav-inner { gap: 8px; }
    .nav-actions { gap: 4px; }
    .nav .btn--sm { padding: 0 12px; }
    .live-pill { padding: 0 10px; min-width: 88px; }
    .theme-toggle { width: 36px; position: relative; }
    .theme-toggle::after { content: ""; position: absolute; inset: 0 -4px; }   /* still a 44px target */
    .net-switch a { padding: 0 10px; }
}
/* Phones: the icon stands for the brand, so the network switch, the pill,
   the theme toggle and the map button all fit on one row at 375px. */
@media (max-width: 599px) { .nav .wordmark { display: none; } }
@media (max-width: 399px) {
    .nav-inner { gap: 6px; }
    .net-switch a { min-width: 36px; padding: 0 8px; font-size: .75rem; }
    .live-pill { min-width: 78px; padding: 0 9px; gap: 6px; }
    .nav .btn--sm { padding: 0 10px; }
    .nav-actions { gap: 3px; }
}
/* The smallest phones (360): the switch takes the brand's place at the left. */
@media (max-width: 374px) { .nav-brand { display: none; } }
/* 320: the theme follows the system (the toggle is a convenience). */
@media (max-width: 339px) { .nav .theme-toggle { display: none; } }
/* 900–1199: with the network switch beside it, the wordmark no longer fits
   next to the links, the pill and the buttons; the icon carries the brand. */
@media (min-width: 900px) and (max-width: 1199px) { .nav .wordmark { display: none; } }
/* 900–1023: the links, pill and two buttons don't fit on one row; the map
   link is already the hero's first button. */
@media (min-width: 900px) and (max-width: 1023px) {
    .nav-map { display: none; }
    .nav-inner { gap: 16px; }
    .nav-links a { padding-inline: 10px; }
}

/* ------------------------------------------------------------------ rail */
.rail { display: none; }
@media (min-width: 1100px) {
    /* Only once main.js has placed the stops (.is-laid): without it all nine
       would sit on one point. */
    .rail.is-laid {
        display: block; position: fixed; right: 14px; top: 28vh; height: 44vh; width: 24px; z-index: var(--z-rail);
        transition: opacity var(--d-ui) var(--ease-ui), visibility 0s;
    }
    /* Muted where it would cross another line: S2's pinned strip and the
       footer's colour bands. It stays while a stop has keyboard focus. */
    .rail.is-muted:not(:focus-within) {
        opacity: 0; visibility: hidden;
        transition: opacity var(--d-ui) var(--ease-ui), visibility 0s var(--d-ui);
    }
    .rail-track { position: absolute; left: 10px; top: 0; bottom: 0; width: 4px; border-radius: 2px; background: var(--quiet); overflow: hidden; }
    .rail-fill { position: absolute; inset: 0; background: var(--halo); transform-origin: 50% 0; transform: scaleY(var(--progress, 0)); }
    .rail ol { position: absolute; inset: 0; }
    .rail li { position: absolute; left: 0; top: var(--at, 0); transform: translateY(-50%); }
    .rail a { display: grid; place-items: center; width: 24px; height: 24px; position: relative; }
    .rail a::before {
        content: ""; width: 12px; height: 12px; border-radius: 50%; background: var(--bg); border: 3px solid var(--ink);
        transition: width var(--d-ui) var(--ease-ui), height var(--d-ui) var(--ease-ui), box-shadow var(--d-ui);
    }
    .rail a[aria-current="step"]::before { width: 20px; height: 20px; box-shadow: 0 0 0 6px rgba(255,205,0,.18); }
    .rail a span {
        position: absolute; right: 34px; top: 50%; transform: translate(6px, -50%); opacity: 0; pointer-events: none;
        padding: 7px 12px; border-radius: var(--r-pill); white-space: nowrap; font-size: .8125rem; font-weight: 600;
        background: var(--glass-solid); -webkit-backdrop-filter: blur(20px); backdrop-filter: blur(20px); border: 1px solid var(--glass-edge); box-shadow: var(--sh-glass);
        transition: opacity var(--d-ui), transform var(--d-ui) var(--ease-ui);
    }
    /* The label can be hovered (the bridge spans the gap back to the dot) and
       dismissed with Escape (main.js adds .labels-off). */
    .rail a span::after { content: ""; position: absolute; left: 100%; top: 0; bottom: 0; width: 12px; }
    .rail a:hover span, .rail a:focus-visible span { opacity: 1; transform: translate(0, -50%); pointer-events: auto; }
    .rail.labels-off a span { opacity: 0 !important; pointer-events: none !important; }
}
@media (min-width: 1100px) and (pointer: coarse) { .rail a::after { content: ""; position: absolute; inset: -10px; } }

/* ============================================================ GL track */
.gl-track { position: relative; }
/* The layer keeps its real 100svh margin box (the hero is pulled up over it
   instead), so it lets go exactly when S2's stage does. A negative bottom
   margin would keep it pinned for one more screen after S2. */
.gl-layer {
    position: sticky; top: 0; height: 100svh; z-index: var(--z-gl);
    overflow: hidden; pointer-events: none;
    background:
        radial-gradient(ellipse at 50% 110%, var(--glow-halo), transparent 60%),
        radial-gradient(var(--dot-grid) 1px, transparent 1.3px) 0 0 / 24px 24px;
    -webkit-mask-image: linear-gradient(#000 88%, transparent); mask-image: linear-gradient(#000 88%, transparent);
}
.gl-layer .gl-canvas { position: absolute; inset: 0; width: 100%; height: 100%; opacity: 0; transition: opacity 400ms var(--ease-ui); }
html.gl .gl-layer .gl-canvas { opacity: 1; }
.gl-track > .hero { margin-top: -100svh; }
/* S2's scrim sits on this pinned layer, not the scrolling stage, so no hard
   edge sweeps up through the map during the hero exit. main.js fades it in
   over the end of the exit (--stage-scrim). */
html.motion.gl-ok .gl-layer::after {
    content: ""; position: absolute; inset: 0 0 auto; height: 50%; pointer-events: none;
    background: linear-gradient(180deg, rgba(var(--bg-rgb), .88) 0%, rgba(var(--bg-rgb), .62) 55%, transparent 100%);
    opacity: var(--stage-scrim, 0);
}
/* Phones: the strip band starts at 40%, so the scrim stops above Line 1. */
@media (max-width: 899px) { html.motion.gl-ok .gl-layer::after { height: 37%; } }
/* GO's eight strips start higher (36%, choreo.js stripLayout). */
@media (max-width: 899px) { html[data-network="go"].motion.gl-ok .gl-layer::after { height: 33%; } }
.poster { position: absolute; inset: 0; perspective: 1200px; transition: opacity 400ms var(--ease-ui); }
/* With WebGL coming, the poster waits: it only shows if GL is late or fails
   (main.js adds .poster-in), so the draw-on intro doesn't replace a map
   that was already there. */
html.gl-ok:not(.poster-in) .poster { opacity: 0; }
html.gl .gl-layer .poster { opacity: 0; }
.poster-tilt {
    position: absolute; left: 62%; top: 44%; width: min(1500px, 118vw); aspect-ratio: 3230 / 2460;
    transform: translate(-54%, -46%) rotateX(var(--rx, 56deg)) rotateZ(-12deg); transform-style: preserve-3d;
}
.poster-map, .poster-overlay { position: absolute; inset: 0; width: 100%; height: 100%; }
.poster-overlay { overflow: visible; }
@media (max-width: 899px) {
    .poster-tilt { left: 55%; top: 30%; width: 190vw; transform: translate(-55%, -40%) rotateX(var(--rx, 52deg)) rotateZ(-28deg); }
}
/* The SVG hero's stand-in for the WebGL pulse: alerts breathe, the halo bobs
   (poster.js writes --bob-x/--bob-y as "4px up the screen" in map units). */
@keyframes poster-breathe { 0%, 100% { opacity: 1; } 50% { opacity: .75; } }
@keyframes poster-bob {
    0%, 100% { transform: translate(calc(-1 * var(--bob-x, 0px)), calc(-1 * var(--bob-y, -4px))); }
    50% { transform: translate(var(--bob-x, 0px), var(--bob-y, -4px)); }
}
html.motion:not(.motion-paused):not(.lite):not(.gl) .poster-alert,
html.motion:not(.motion-paused):not(.lite):not(.gl) .poster-ring { animation: poster-breathe var(--d-breathe) ease-in-out infinite; }
html.motion:not(.motion-paused):not(.lite):not(.gl) .poster-halo { animation: poster-bob var(--d-breathe) ease-in-out infinite; }

/* The label tethered to the halo: names the alert under it, in the style of
   S2's phone callout. main.js places it from the ribbon's (or the poster's)
   haloScreen() every frame, above the ring, or below it when the copy is in
   the way, and fades it with the halo; it shows only where a map is showing. */
.halo-label {
    --hl-rgb: var(--amber-rgb); --hl-text: var(--amber-text);
    position: absolute; left: 0; top: 0; z-index: 2; pointer-events: none;
    display: none; align-items: center; gap: 7px; white-space: nowrap;
    padding: 6px 11px 6px 9px; border-radius: var(--r-pill);
    background: rgba(var(--bg-rgb), .88); border: 1px solid rgba(var(--hl-rgb), .5);
    box-shadow: 0 6px 18px rgba(0,0,0,.18);
    font: 700 .75rem/1 var(--f-display); font-synthesis: none; font-variation-settings: "wdth" 90; font-stretch: 90%;
    letter-spacing: .06em; text-transform: uppercase; color: var(--hl-text);
    opacity: 0; transition: opacity 220ms var(--ease-ui); will-change: transform, opacity;
}
.halo-label.sev-suspended { --hl-rgb: var(--red-rgb); --hl-text: var(--red-text); }
.hl-dot { width: 7px; height: 7px; border-radius: 50%; background: rgb(var(--hl-rgb)); flex: none; }
/* The tether: a hairline to the ring, leaning when the pill has slid past
   it (main.js sets where it leaves the pill, its length and its angle). */
.halo-label::after {
    content: ""; position: absolute; left: var(--tether-x, 50%); top: 100%; width: 1.5px; height: var(--tether-l, 9px); margin-left: -.75px;
    transform: rotate(var(--tether-a, 0deg)); transform-origin: 50% 0;
    background: linear-gradient(rgba(var(--hl-rgb), .75), rgba(var(--hl-rgb), .2));
}
/* Hung below the stretch (phones, mostly): the tether rises to the ring. */
.halo-label.is-below::after { top: auto; bottom: 100%; transform-origin: 50% 100%; background: linear-gradient(0deg, rgba(var(--hl-rgb), .75), rgba(var(--hl-rgb), .2)); }
html.gl .halo-label, html.poster-in:not(.gl) .halo-label { display: inline-flex; }
@media (max-width: 899px) { .halo-label { font-size: .6875rem; padding: 5px 9px 5px 8px; } }

/* In the stacked (no-GL) S2, the layer only sits behind the hero. */
html:not(.gl-ok) .stretch, html:not(.motion) .stretch { background: var(--bg); }

/* ================================================================ hero */
.hero { position: relative; z-index: var(--z-content); min-height: max(100svh, 640px); display: flex; }
/* Scrims: soft gradients only, never a box behind the text (the old
   blurred panel smeared as it slid up the map). The top corners' shade is
   pinned on the layer, under the halo label. The headline and lede carry
   theirs with the hero as it scrolls, in a box that runs half a screen past
   the hero so every gradient has faded out inside it: nothing has an edge
   to see. Both fade with the exit (main.js: opacity 1 − h). Positions are
   in the copy scrim's box: the hero is its top two thirds. */
.hero-scrim { position: absolute; pointer-events: none; }
.hero-scrim--top {
    inset: 0; z-index: 1;
    background:
        /* the live caption, top right */
        radial-gradient(ellipse 34% 20% at 88% 15%, rgba(var(--bg-rgb), .9) 0%, rgba(var(--bg-rgb), .64) 50%, rgba(var(--bg-rgb), 0) 100%),
        /* the eyebrow, top left */
        radial-gradient(ellipse 30% 16% at 12% 14%, rgba(var(--bg-rgb), .7) 0%, rgba(var(--bg-rgb), 0) 100%);
}
.hero-scrim--copy {
    left: 0; right: 0; top: 0; height: 150%;
    background:
        /* the lede and buttons: held above .75 across the block, so body text
           over a bright line still clears 4.5:1, then gone well before the loop */
        radial-gradient(ellipse 34% 21.5% at 82% 46%, rgba(var(--bg-rgb), .93) 0%, rgba(var(--bg-rgb), .86) 50%, rgba(var(--bg-rgb), 0) 100%),
        /* the headline, bottom left: display type, it needs less */
        radial-gradient(ellipse 58% 29% at 18% 58%, rgba(var(--bg-rgb), .86) 0%, rgba(var(--bg-rgb), .55) 48%, rgba(var(--bg-rgb), 0) 100%);
}
.hero-grid {
    position: relative; display: grid; grid-template-columns: repeat(12, 1fr); column-gap: 24px;
    grid-template-rows: auto 1fr auto; padding-top: 120px; padding-bottom: 16vh; width: min(100% - 2 * var(--gutter), var(--container));
}
.hero-eyebrow { grid-column: 1 / 7; grid-row: 1; align-self: start; }
.hero-caption {
    grid-column: 9 / 13; grid-row: 1; justify-self: end; text-align: right; font-size: var(--t-small); color: var(--ink-2); max-width: 36ch;
    position: relative;
}
.hero-caption p + p { margin-top: 4px; }
.cap-seg { white-space: nowrap; }
.cap-2 { color: var(--ink-2); }
.halo-glyph { color: var(--halo-ink); display: inline; vertical-align: .12em; }
.hero-h {
    grid-column: 1 / 11; grid-row: 3; align-self: end;
    font-family: var(--f-display); font-weight: 800; font-synthesis: none;
    font-size: var(--t-hero); line-height: .86; letter-spacing: -.035em; padding-bottom: .08em;
    font-variation-settings: "wdth" 75; font-stretch: 75%;
}
/* Masked words wait at 135% (html.intro, under "reveals"): the clip reaches
   .14em below the line box, and at 105% the tops of tall glyphs showed in it. */
.w { display: inline-block; overflow: clip; overflow-clip-margin: .08em; vertical-align: top; padding-bottom: .06em; margin-bottom: -.06em; }
.w > span { display: inline-block; }
.hero-side { grid-column: 9 / 13; grid-row: 3; align-self: end; display: grid; gap: 20px; padding-bottom: .9em; position: relative; }
.hero-side .lede { font-size: var(--t-lede); max-width: 32ch; color: var(--ink-2); }
/* Both edge controls line up with the content column, not the window edge;
   from 1100px the pause button also stays clear of the rail (right 14–38px). */
.hero-pause { position: absolute; right: max(24px, (100% - var(--container)) / 2); bottom: 24px; }
@media (min-width: 1100px) { .hero-pause { right: max(54px, (100% - var(--container)) / 2); } }
.scroll-cue { position: absolute; left: max(var(--gutter), (100% - var(--container)) / 2); bottom: 28px; display: flex; align-items: center; gap: 12px; font: 700 .75rem/1 var(--f-display); letter-spacing: .14em; text-transform: uppercase; color: var(--ink-3); }
.scroll-cue i { width: 1px; height: 24px; background: linear-gradient(var(--ink-2), transparent); transform-origin: 50% 0; }
html.motion .scroll-cue i { animation: cue 2s var(--ease-io) infinite; }
@keyframes cue { 0% { transform: scaleY(0); } 50% { transform: scaleY(1); transform-origin: 50% 0; } 51% { transform-origin: 50% 100%; } 100% { transform: scaleY(0); transform-origin: 50% 100%; } }
html:not(.motion) .scroll-cue, html.motion-paused .scroll-cue, html.lite .scroll-cue { display: none; }
.motion-toggle .i-play, html.motion-paused .motion-toggle .i-pause { display: none; }
html.motion-paused .motion-toggle .i-play { display: block; }
html:not(.motion) .motion-toggle { display: none; }

@media (max-width: 899px) {
    /* Phones: the copy (caption included) stacks up from the bottom of the
       hero (the top two thirds of the box), so its shade does too, easing
       out through the half screen below. */
    .hero-scrim--top { background: radial-gradient(ellipse 60% 12% at 18% 12%, rgba(var(--bg-rgb), .7) 0%, rgba(var(--bg-rgb), 0) 100%); }
    .hero-scrim--copy {
        background: linear-gradient(180deg, rgba(var(--bg-rgb), 0) 14%, rgba(var(--bg-rgb), .62) 26.7%, rgba(var(--bg-rgb), .92) 36%,
            rgba(var(--bg-rgb), 1) 58%, rgba(var(--bg-rgb), .9) 70%, rgba(var(--bg-rgb), 0) 97%);
    }
    .hero-grid {
        grid-template-columns: 1fr; grid-template-rows: auto 1fr auto auto auto;
        padding-top: calc(var(--nav-h) + env(safe-area-inset-top) + 16px);
        padding-bottom: calc(24px + env(safe-area-inset-bottom));
    }
    .hero-eyebrow { grid-column: 1; grid-row: 1; }
    .hero-caption { grid-column: 1; grid-row: 3; justify-self: start; text-align: left; margin-bottom: 14px; }
    .hero-h { grid-column: 1; grid-row: 4; max-width: none; }
    .hero-side { grid-column: 1; grid-row: 5; padding: 18px 0 0; gap: 16px; }
    .hero-side .ctas { display: grid; }
    .hero-side .btn { width: 100%; }
    .hero-pause { top: calc(var(--nav-h) + env(safe-area-inset-top) + 48px); right: var(--gutter); bottom: auto; }
    .scroll-cue { display: none; }
}
/* Short phones (Safari with its toolbars): the stack reaches the pause
   button, so the caption keeps clear of it and drops its second line. */
@media (max-width: 899px) and (max-height: 740px) {
    .hero-caption { padding-right: 52px; }
    .hero-caption .cap-2 { display: none !important; }
}
/* Tablets: buttons at their own width, side by side. */
@media (min-width: 600px) and (max-width: 899px) {
    .hero-side .ctas { display: flex; }
    .hero-side .btn { width: auto; min-width: 260px; }
}
/* Desktop: the buttons share the side column's width, so when it can't hold
   both on one row (1280 and so on) each wraps to a full-width pill of the
   same size, not two unequal ones. */
@media (min-width: 900px) { .hero-side .ctas > .btn { flex: 1 1 auto; } }

/* ============================================================ S2 stretch */
.stretch { position: relative; z-index: var(--z-content); }
.beat { font-size: var(--t-beat); line-height: .9; letter-spacing: -.03em; font-variation-settings: "wdth" 78; font-stretch: 78%; font-weight: 800; }
.beat-c { font-size: var(--t-h2); line-height: .95; }
.nowrap { white-space: nowrap; }
/* main.js holds the word-and-comma wrapper at its width at wdth 125, so the
   heading wraps at its final width from the start and never re-wraps mid-scrub. */
.beat-c .nowrap { display: inline-block; text-align: left; }
.stretch-word { color: var(--amber-text); display: inline-block; text-align: left; font-variation-settings: "wdth" var(--wdth, 125); font-stretch: calc(var(--wdth, 125) * 1%); }
.strip-labels, .strip-bullets { display: none; }
.kicker { font-size: var(--t-lede); line-height: 1.42; color: var(--ink-2); max-width: 30ch; margin-top: 24px; }
.stretch-body { color: var(--ink-2); font-size: var(--t-body); box-sizing: content-box; max-width: 60ch; padding-left: max(var(--gutter), (100% - var(--container)) / 2); padding-right: var(--gutter); }

/* Stacked layout: static, reduced motion, or no WebGL */
html:not(.motion) .stretch, html:not(.gl-ok) .stretch { padding-block: var(--section-y); }
html:not(.motion) .stretch .stage, html:not(.gl-ok) .stretch .stage { display: flex; flex-direction: column; gap: 48px; }
html:not(.motion) .stretch .beat-a, html:not(.motion) .stretch .beat-b,
html:not(.gl-ok) .stretch .beat-a, html:not(.gl-ok) .stretch .beat-b { display: none; }
html:not(.motion) .stretch .beat-c, html:not(.gl-ok) .stretch .beat-c { order: 2; margin-top: 24px; }
html:not(.motion) .stage-copy, html:not(.gl-ok) .stage-copy { display: flex; flex-direction: column; }
html:not(.motion) .stage-copy .kicker, html:not(.gl-ok) .stage-copy .kicker { order: 1; margin-top: 0; }
html:not(.motion) .stage-copy .eyebrow, html:not(.gl-ok) .stage-copy .eyebrow { order: 0; }
html:not(.motion) .beats, html:not(.gl-ok) .beats { order: 2; }
html:not(.motion) .strip, html:not(.gl-ok) .strip { width: min(100% - 2 * var(--gutter), var(--container)); margin-inline: auto; }
/* A block box: clipping an inline <picture> (the no-WebGL wipe) kept its
   image from loading or painting at all. */
.strip-static { display: block; }
.strip-static img { width: 100%; height: auto; }
html:not(.motion) .stretch-side, html:not(.gl-ok) .stretch-side { width: min(100% - 2 * var(--gutter), 520px); margin-inline: var(--gutter) auto; margin-left: max(var(--gutter), (100% - var(--container)) / 2); }

/* Scroll stage: motion + WebGL */
html.motion.gl-ok .stretch { height: 360svh; }
html.motion.gl-ok .stretch .stage { position: sticky; top: 0; height: 100svh; overflow: hidden; }
/* (The scrim that keeps the beats legible over the ribbons is .gl-layer::after.) */
html.motion.gl-ok .stage-copy { position: absolute; left: 0; right: 0; top: 10%; margin-inline: auto; }
/* The beats stand in for the kicker on screen; screen readers still get it. */
html.motion.gl-ok .stage-copy .kicker {
    position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
    overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; border: 0;
}
html.motion.gl-ok .beats { position: relative; margin-top: 2.5vh; height: 26svh; }
html.motion.gl-ok .beat { position: absolute; left: 0; top: 0; max-width: 11ch; }
html.motion.gl-ok .beat-c { max-width: none; }
html.motion.gl-ok .beat-a, html.motion.gl-ok .beat-b { font-size: var(--t-beat); }
.bw { display: inline-block; overflow: clip; overflow-clip-margin: .1em; vertical-align: top; padding-bottom: .08em; margin-bottom: -.08em; }
.bw > span { display: inline-block; }
/* Waiting words sit at 135% and are hidden outright: the clip reaches
   ~.18em below the line box, and at 105% the tops of tall glyphs showed
   as rows of dashes. main.js shows each word only while it's moving. */
html.motion.gl-ok .beat-a .bw > span, html.motion.gl-ok .beat-b .bw > span { transform: translateY(135%); visibility: hidden; }
html.motion.gl-ok .beat-c { opacity: 0; }
html.motion.gl-ok .strip { position: absolute; inset: 0; pointer-events: none; }
html.motion.gl-ok .strip-static { position: absolute; left: 7%; right: 7%; top: 50%; display: none; }
html.motion.gl-ok:not(.gl) .strip-static { display: block; clip-path: inset(0 calc(100% - var(--wipe, 0%)) 0 0); }
html.motion.gl-ok:not(.gl) .strip-static img { width: 100%; height: auto; }
@media (max-width: 599px) { html.motion.gl-ok .strip-static { left: 16px; right: 16px; top: 44%; } }
html.motion.gl-ok.gl .strip-labels, html.motion.gl-ok.gl .strip-bullets { display: block; }
/* Placed by transform (px from main.js), so moving them costs no layout. */
.strip-bullets li {
    position: absolute; left: 0; top: 0; transform: translate(calc(var(--bx, 0px) - 50%), calc(var(--by, 0px) - 50%));
    width: 28px; height: 28px; border-radius: 50%; display: grid; place-items: center;
    background: var(--c); color: var(--ink); font: 800 17px/1 var(--f-display); font-variation-settings: "wdth" 100; opacity: 0;
}
/* Phones: smaller bullets, so they sit left of each strip's first station
   (7% in) instead of on it. */
@media (max-width: 899px) {
    .strip-bullets li { width: 20px; height: 20px; font-size: 12px; }
}
/* GO's bullets are its signage: a rounded rectangle with the two-letter
   code (the board's .badge--go). Eight strips share the band, so they're a
   little shorter than the TTC's circles. */
html[data-network="go"] .strip-bullets li { width: 38px; height: 24px; border-radius: var(--r-badge-go); font-size: 13px; letter-spacing: .02em; color: var(--ink); }
@media (max-width: 899px) {
    html[data-network="go"] .strip-bullets li { width: 24px; height: 16px; border-radius: 4px; font-size: 9px; }
}
/* The GO page's static strip, drawn inline by main.js from the model (the
   TTC page's are pictures). Page tokens, so it follows the theme. */
.strip-static--live svg { display: block; width: 100%; height: auto; overflow: visible; }
.strip-static--live .ss-track { stroke: var(--quiet); fill: none; }
.strip-static--live .ss-track-stop { stop-color: var(--quiet); }
.strip-static--live .ss-lit { stroke: var(--amber); fill: none; }
.strip-static--live .ss-lit-fill { fill: var(--amber); }
.strip-static--live text { font-family: var(--f-display); font-weight: 700; font-synthesis: none; font-stretch: 90%; font-variation-settings: "wdth" 90; letter-spacing: .04em; }
.strip-static--live .ss-names text { fill: var(--ink-3); }
.strip-static--live .ss-names text.on { fill: var(--ink); }
.strip-static--live .ss-badge { font-weight: 800; text-anchor: middle; letter-spacing: 0; font-variation-settings: "wdth" 100; font-stretch: 100%; }
.strip-static--live .ss-hint { font-size: 11px; fill: var(--ink-3); letter-spacing: .08em; }
.strip-labels li {
    position: absolute; left: 0; top: 0; white-space: nowrap; will-change: transform, opacity;
    font: 650 var(--t-station)/1 var(--f-display); font-variation-settings: "wdth" 90; font-stretch: 90%;
    letter-spacing: .04em; text-transform: uppercase; color: rgba(var(--ink-rgb), .72);
    transform-origin: 0 50%; opacity: 0;
}
.strip-labels li.is-focus { color: var(--ink); }
/* A name too long for its room goes on two lines (main.js planLabels picks
   the break). Only that break shows, and the label turns about the middle
   of its second line, where a one-line label turns, so the first line
   stacks above it. */
.strip-labels li:not(.is-wrap) br, .strip-labels br:not(.is-on) { display: none; }
.strip-labels li.is-wrap { line-height: 1.15; transform-origin: 0 calc(100% - .575em); }
/* ...and when a name on the lit stretch needs the room under the card, the
   note there steps aside (the card still says what it shows). */
@media (min-width: 900px) {
    html.motion.gl-ok.gl .card-note.is-yielding { visibility: hidden; }
}
/* Desktop end state: the example card sits beside the heading, the five
   strips hold the middle (Line 1's labels rising above them), and the body
   copy runs underneath the band. */
html.motion.gl-ok .stretch-side { position: absolute; right: max(var(--gutter), (100% - var(--container)) / 2); top: 12%; width: min(30vw, 420px); }
html.motion.gl-ok .stretch-card, html.motion.gl-ok .card-note { opacity: 0; transform: translateY(40px); }
html.motion.gl-ok .stretch-body {
    position: absolute; top: 84%; left: max(var(--gutter), (100% - var(--container)) / 2); right: auto;
    width: min(52vw, 680px); max-width: none; margin: 0; padding: 0; box-sizing: border-box; opacity: 0; font-size: var(--t-lede); line-height: 1.42;
}
@media (min-width: 900px) and (max-height: 820px) {
    html.motion.gl-ok .stretch-body { top: 85%; font-size: var(--t-body); }
}
/* Phone: one callout over the lit stretch instead of 38 rotated labels. */
.strip-callout { display: none; }
@media (max-width: 899px) {
    html.motion.gl-ok.gl .strip-callout {
        display: block; position: absolute; left: 0; top: 0; margin: 0; opacity: 0; white-space: nowrap;
        transform: translate(calc(var(--cx, 0px) - 50%), calc(var(--cy, 0px) - 100%));
        padding: 5px 10px; border-radius: var(--r-pill); background: rgba(var(--bg-rgb), .86); border: 1px solid rgba(var(--amber-rgb), .45);
        font: 700 .75rem/1 var(--f-display); font-variation-settings: "wdth" 90; letter-spacing: .06em; text-transform: uppercase; color: var(--amber-text);
    }
}
@media (max-width: 899px) {
    html.motion.gl-ok .stretch { height: 320svh; }
    html.motion.gl-ok .stage-copy { top: calc(var(--nav-h) + env(safe-area-inset-top) + 16px); }
    html.motion.gl-ok .beats { height: 22svh; }
    /* Anchored to the bottom, so the card and its "Illustration" note always
       fit, whatever height Safari's toolbars leave. */
    html.motion.gl-ok .stretch-side {
        left: var(--gutter); right: auto; top: auto; bottom: calc(12px + env(safe-area-inset-bottom));
        width: min(100% - 2 * var(--gutter), 440px);
    }
    html.motion.gl-ok .stage .stretch-body { display: none; }
    html.motion.gl-ok .stretch-body-flow { display: block; }
}
/* Short phones: a compact card keeps clear of the strip row (56%). */
@media (max-width: 899px) and (max-height: 720px) {
    html.motion.gl-ok .stretch-card { padding: 14px; }
    html.motion.gl-ok .stretch-card .ac-chips { display: none; }
    html.motion.gl-ok .stretch-card .ac-route { margin: 12px 0 0; padding-top: 12px; }
}
.stretch-body-flow { display: none; position: relative; z-index: var(--z-content); background: var(--bg); padding: 8px 0 0; color: var(--ink-2); }
.stretch-body-flow p { max-width: 60ch; }

/* Example alert card (the app's card, at rest) */
.alert-card {
    --sev-rgb: var(--amber-rgb); --sev: var(--amber); --sev-text: var(--amber-text); --sev-wash: .07;
    border-radius: var(--r-card); padding: 18px 18px 16px;
    background: linear-gradient(rgba(var(--sev-rgb), var(--sev-wash)), rgba(var(--sev-rgb), var(--sev-wash))), var(--card-glass);
    border: 1px solid rgba(var(--sev-rgb), .28);
}
.alert-card.sev-suspended { --sev-rgb: var(--red-rgb); --sev: var(--red); --sev-text: var(--red-text); }
/* The border breathes .18 ↔ .34 by fading a second border over it: opacity
   runs on the compositor, where animating border-color restyled the page
   every frame. It only runs on screen (main.js marks .is-onscreen). */
html.motion .alert-card { border-color: rgba(var(--sev-rgb), .18); }
html.motion .alert-card::after {
    content: ""; position: absolute; inset: -1px; border-radius: inherit; pointer-events: none;
    border: 1px solid rgba(var(--sev-rgb), .2); opacity: 0;
    animation: breathe var(--d-breathe) ease-in-out infinite;
}
@keyframes breathe { 50% { opacity: 1; } }
html.motion .alert-card:not(.is-onscreen)::after { animation-play-state: paused; }
html.motion-paused .alert-card::after, html.lite .alert-card::after { animation: none; opacity: .6; }
.ac-head { display: flex; align-items: center; gap: 12px; }
.ac-icon { width: 36px; height: 36px; border-radius: 10px; background: var(--sev); display: grid; place-items: center; flex: none; }
.ac-title { display: grid; gap: 2px; flex: 1; }
.ac-title b { color: var(--sev-text); font-weight: 650; font-size: 1.0625rem; }
.ac-title span { color: var(--ink-3); font-size: .875rem; }
.stretch-card { --lc: var(--l1); --lc-ink: #0F1014; }
.ac-line { background: var(--lc, var(--l1)); color: var(--lc-ink, #0F1014); font: 700 .8125rem/1 var(--f-text); padding: 6px 10px; border-radius: var(--r-pill); }
.ac-route { display: flex; align-items: center; gap: 12px; margin: 16px 0 12px; padding-top: 14px; border-top: 1px solid var(--hair); font-weight: 650; font-size: 1.0625rem; }
.ac-route .ac-stop { display: inline-flex; align-items: center; gap: 8px; }
.ac-route .ac-stop:first-child::before, .ac-route .ac-stop:last-child::after { content: ""; width: 10px; height: 10px; border-radius: 50%; border: 2px solid var(--lc, var(--l1)); }
.ac-rule { flex: 1; height: 3px; border-radius: 2px; background: var(--lc, var(--l1)); }
/* A single-station alert: one stop, no rule. */
.ac-route.is-single .ac-rule, .ac-route.is-single .ac-stop + .ac-stop { display: none; }
.ac-route.is-single .ac-stop:first-child::after { content: none; }
.ac-chips { display: flex; flex-wrap: wrap; gap: 6px; }
.ac-chip.ac-shuttle { background: var(--shuttle-aa); color: #fff; }
/* S2's headline word takes the lit stretch's colour: red when it's a closure. */
.stretch[data-sev="suspended"] .stretch-word { color: var(--red-text, var(--red)); }
html.motion.gl-ok.gl .stretch[data-sev="suspended"] .strip-callout { color: var(--red-text, var(--red)); border-color: rgba(var(--red-rgb), .45); }
.ac-chip { display: inline-flex; align-items: center; gap: 6px; padding: 6px 10px; border-radius: var(--r-pill); background: var(--fill-1); color: var(--ink-3); font-size: .8125rem; }
.card-note { margin-top: 12px; font-size: var(--t-small); color: var(--ink-3); }

/* ======================================================= S3 live (frame) */
.section#live h2 { max-width: 16ch; }

/* ========================================================== S4 watch */
.watch-grid { display: grid; grid-template-columns: repeat(12, 1fr); column-gap: 24px; align-items: start; }
.watch-copy { grid-column: 1 / 6; padding-top: 8vh; }
.watch-copy h2 { max-width: 11ch; }
.chapters { margin-top: 56px; display: grid; gap: 4px; counter-reset: ch; }
.chapters button {
    display: grid; gap: 4px; width: 100%; text-align: left; padding: 16px 0 18px; position: relative;
    color: var(--ink-3); transition: color var(--d-ui);
}
.chapters button:hover, .chapters button.is-active { color: var(--ink); }
.ch-h { font-size: var(--t-h3); font-weight: 650; letter-spacing: -.012em; line-height: 1.2; }
.ch-sub { font-size: var(--t-small); color: var(--ink-3); }
.ch-bar { position: absolute; left: 0; right: 0; bottom: 0; height: 2px; background: var(--hair-strong); overflow: hidden; }
.ch-bar b { position: absolute; inset: 0; background: var(--c); transform-origin: 0 50%; transform: scaleX(var(--p, 0)); }
.watch-stage { grid-column: 7 / 12; position: sticky; top: calc(50svh - (min(24vw, 340px) * 2.26) / 2); justify-self: center; perspective: 1400px; }
.watch-phone { --w: clamp(260px, 24vw, 340px); }
/* The poster is an image under the video (lazy, unlike a poster attribute);
   the video paints over it once it has a frame. */
.watch-phone .screen > .video-poster { position: absolute; inset: 0; }
.watch-phone .screen video { position: relative; width: 100%; height: 100%; object-fit: cover; }
html:not(.js) .watch-phone .js-video { display: none; }
/* Beside the phone, not on it: over the screen it read as the app's own UI. */
.video-toggle { position: absolute; right: -64px; bottom: 16px; }
.video-toggle .i-play { display: none; }
.video-toggle.is-paused .i-play { display: block; }
.video-toggle.is-paused .i-pause { display: none; }
.video-big { position: absolute; left: 50%; top: 50%; width: 84px; height: 84px; border-radius: 50%; transform: translate(-50%, -50%); display: grid; place-items: center; color: var(--ink); }
html.motion .watch-section-in .chapters li { opacity: 0; transform: translateY(16px); }
@media (min-width: 900px) { .watch { min-height: 140svh; } }
@media (max-width: 899px) {
    .watch-grid { display: flex; flex-direction: column; align-items: stretch; }
    .watch-grid > *, .chapters { min-width: 0; }
    .watch-copy { padding-top: 0; display: contents; }
    .watch-copy > .eyebrow, .watch-copy > h2 { order: 0; }
    .watch-stage { order: 1; position: relative; top: 0; margin: 40px auto 0; align-self: center; }
    .watch-phone { --w: min(72vw, 300px); }
    .chapters { order: 2; display: flex; gap: 10px; overflow-x: auto; scroll-snap-type: x mandatory; scroll-padding-inline: var(--gutter); margin: 28px calc(-1 * var(--gutter)) 0; padding: 0 var(--gutter) 6px; scrollbar-width: none; }
    .chapters::-webkit-scrollbar { display: none; }
    .chapters li { flex: none; scroll-snap-align: start; }
    .chapters button { width: auto; padding: 12px 16px 14px; border-radius: var(--r-chip); background: var(--bg-raise); border: 1px solid var(--hair); overflow: hidden; }
    .chapters .ch-h { font-size: 1rem; }
    .chapters .ch-sub { display: none; }
    .video-toggle { right: -6px; bottom: 16px; }
}

/* ========================================================= S5 features */
.features { position: relative; z-index: var(--z-content); background: var(--bg); }
.f-num { font: 700 1rem/1 var(--f-display); font-variant-numeric: tabular-nums; color: var(--ink-3); font-variation-settings: "wdth" 100; }
/* Line colours as text: lifted toward white so the darker ones (Kitchener
   green, Lakeshore East red) still reach 4.5:1 on the ground. */
.f-num { --tick-text: color-mix(in srgb, var(--tick, var(--ink-3)) var(--tick-p), var(--tick-mix)); }
.pro-tag { display: inline-block; vertical-align: middle; margin-left: 8px; padding: 4px 8px; border-radius: var(--r-pill); background: rgba(236,105,100,.14); color: var(--coral-text); font: 700 11px/1 var(--f-text); letter-spacing: .08em; text-transform: uppercase; }
.carousel, .carousel-dots { display: none; }
/* A link inside a feature's copy (the GO page's "The TTC page"). A closed
   step's copy is clipped, not gone, so its link leaves the tab order too. */
.f-link { color: var(--ink); font-weight: 600; text-decoration: underline; text-underline-offset: 3px; white-space: nowrap; }
.f-link:hover { text-decoration-thickness: 2px; }
@media (min-width: 900px) { html.motion .f-item:not(.is-active) .f-link { visibility: hidden; } }

@media (min-width: 900px) {
    .features { padding-block: 0; }
    html.motion .features { height: 485svh; }   /* 55svh a step for seven, plus the pinned screen */
    html.motion .features .stage { position: sticky; top: 0; height: 100svh; display: flex; align-items: center; overflow: clip; }
    html:not(.motion) .features { padding-block: var(--section-y); }
    .features-grid { display: grid; grid-template-columns: repeat(12, 1fr); column-gap: 24px; align-items: center; }
    .features-copy { grid-column: 1 / 6; }
    /* Pinned: the grid is exactly the deck's height and the copy hangs from a
       fixed top (main.js centres its resting height: --f-copy-top), so the
       open/close of the index, even several steps at once on a fast scroll,
       never moves the headline or re-centres the stage (layout shift). */
    html.motion .features-grid { height: min(78svh, 760px); grid-template-rows: 100%; align-items: start; }
    html.motion .features-copy { margin-top: var(--f-copy-top, 0px); }
    /* Short screens (iPad landscape): a smaller headline keeps the whole
       index inside the pinned screen. */
    @media (max-height: 780px) {
        html.motion .features-copy h2 { font-size: min(var(--t-h2), 6svh); }
        html.motion .f-index { margin-top: 24px; }
    }
    .features-copy h2 { max-width: 12ch; }
    .f-index { margin-top: 40px; display: grid; gap: 2px; }
    .f-item h3 { font-size: var(--t-h3); }
    .f-item button { display: flex; align-items: baseline; gap: 16px; width: 100%; text-align: left; padding: 10px 0; color: var(--ink-3); transition: color var(--d-ui); min-height: 44px; }
    .f-item button:hover { color: var(--ink-2); }
    .f-item.is-active button { color: var(--ink); }
    .f-item.is-active .f-num { color: var(--tick-text); }
    .f-body { display: grid; grid-template-rows: 0fr; transition: grid-template-rows var(--d-swap) var(--ease-ui); }
    .f-body > div { overflow: hidden; }
    /* Every open body is as tall as the tallest (main.js measures it), so the
       column keeps one height and doesn't bob as the steps change. */
    .f-body p { padding: 0 0 14px 38px; color: var(--ink-2); max-width: 46ch; min-height: var(--f-body-h, 0px); }
    .f-item.is-active .f-body { grid-template-rows: 1fr; }
    .deck { grid-column: 6 / 13; height: min(78svh, 760px); perspective: 1800px; display: grid; place-items: center; }
    /* Sized from the viewport height: lifted (×1.27 at 380px under a 1800px
       perspective) the active screen still clears the nav and the fold. */
    .deck-3d {
        --dw: min(300px, calc((100svh - 150px) / 2.76));
        position: relative; width: var(--dw); aspect-ratio: 780 / 1695; transform-style: preserve-3d; transform: rotateX(58deg) rotateZ(var(--deck-rz, -32deg));
    }
    /* The stack sits 260px back along the deck's normal, so no tilted card
       reaches the lifted one's plane (Chrome would slice them into it). */
    .deck-card {
        position: absolute; inset: 0; border-radius: calc(var(--dw) * .14); border: 1px solid var(--hair-strong); background: #050608; padding: calc(var(--dw) * .027);
        transform: translateZ(calc(var(--i) * 28px - 260px)) translateX(calc(var(--i) * 6px));
        -webkit-backface-visibility: hidden; backface-visibility: hidden;
        transition: transform 800ms var(--ease-io), box-shadow 800ms var(--ease-io), filter 800ms var(--ease-io);
        filter: var(--deck-dim);
    }
    .deck-card .screen { position: relative; height: 100%; border-radius: calc(var(--dw) * .113); overflow: hidden; -webkit-mask-image: -webkit-radial-gradient(white, black); }
    .deck-card img { width: 100%; height: 100%; object-fit: cover; }
    .deck-card.is-active {
        transform: rotateZ(calc(-1 * var(--deck-rz, -32deg))) rotateX(-58deg) translateZ(380px) translateY(-16px);
        box-shadow: var(--sh-lift); filter: none;
    }
    /* Static: the phone slides become a plain three-column grid with captions */
    html:not(.motion) .deck, html:not(.motion) .f-index { display: none; }
    html:not(.motion) .features-copy { grid-column: 1 / -1; }
    /* Seven phones: rows of four (three where that's too tight), the last row centred. */
    html:not(.motion) .carousel {
        display: flex; flex-wrap: wrap; justify-content: center; gap: 64px 24px;
        width: min(100% - 2 * var(--gutter), var(--container)); margin: 64px auto 0; padding: 0; overflow: visible;
    }
    html:not(.motion) .slide { width: auto; flex: 0 1 calc((100% - 3 * 24px) / 4); min-width: 240px; }
    html:not(.motion) .slide-phone { --w: 240px; margin-inline: auto; }
    html:not(.motion) .slide-cap { padding: 24px 0 0; display: grid; gap: 8px; max-width: 36ch; margin-inline: auto; }
    html:not(.motion) .slide-cap .f-num { color: var(--tick-text); }
    html:not(.motion) .slide-cap p { color: var(--ink-2); font-size: var(--t-small); }
}
@media (max-width: 899px) {
    .features { padding-block: var(--section-y); }
    .features-grid .deck, .f-index { display: none; }
    .carousel {
        display: flex; gap: 16px; overflow-x: auto; scroll-snap-type: x mandatory; padding: 40px 14vw 8px; margin-top: 8px;
        scrollbar-width: none; overscroll-behavior-x: contain;
    }
    .carousel::-webkit-scrollbar { display: none; }
    .slide { flex: none; width: min(72vw, 300px); scroll-snap-align: center; }
    .slide-phone { --w: min(72vw, 300px); }
    .slide-cap { padding: 24px 4px 0; display: grid; gap: 8px; }
    .slide-cap .f-num { color: var(--tick-text); }
    .carousel:focus-visible { outline: 3px solid var(--focus); outline-offset: -3px; }
    .slide-cap p { color: var(--ink-2); font-size: var(--t-small); }
    @supports (animation-timeline: view()) {
        html.motion .slide { animation: slide-3d linear both; animation-timeline: view(inline); }
        @keyframes slide-3d {
            0% { transform: perspective(900px) rotateY(22deg) translateZ(-60px) scale(.94); opacity: .65; }
            50% { transform: none; opacity: 1; }
            100% { transform: perspective(900px) rotateY(-22deg) translateZ(-60px) scale(.94); opacity: .65; }
        }
    }
    .carousel-dots { display: flex; justify-content: center; gap: 6px; margin-top: 20px; }
    .carousel-dots i { width: 6px; height: 6px; border-radius: 3px; background: var(--ink-4); transition: width var(--d-ui) var(--ease-ui), background-color var(--d-ui); }
    .carousel-dots i.is-on { width: 18px; background: var(--ink); }
}
/* Scroll-linked, not decorative: Pause motion and Low Power must not freeze
   a slide half-turned (this outranks the blanket pause at the end). */
html.motion-paused .slide, html.lite .slide { animation-play-state: running !important; }

/* ========================================================== S6 glance */
.glance h2 { max-width: 14ch; }
.glance-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 24px; margin-top: 64px; }
.g-card { padding: 20px 20px 28px; display: flex; flex-direction: column; gap: 10px; }
.g-card p { color: var(--ink-2); font-size: var(--t-small); }
.g-visual { position: relative; height: 220px; margin-bottom: 14px; border-radius: 16px; background: var(--bg-sunk); display: grid; place-items: center; overflow: hidden; }
.g-tag { position: absolute; left: 12px; bottom: 10px; font-size: .75rem; color: var(--ink-3); }
/* Room for the tag under the illustration, so the two never touch. */
.g-visual--island, .g-visual--widget { padding-bottom: 26px; }
.badge-mini { width: 22px; height: 22px; border-radius: 50%; display: inline-grid; place-items: center; background: var(--c); color: #0F1014; font: 800 13px/1 var(--f-display); font-style: normal; font-variation-settings: "wdth" 100; flex: none; }
/* GO's rounded rectangle, ink per line as the board's badges (inline --ink). */
.badge-mini--go { width: 30px; height: 20px; border-radius: 5px; color: var(--ink); font-size: 11px; letter-spacing: .02em; }
.island-cap {
    height: 37px; width: 252px; border-radius: 999px; background: #000; color: #fff; box-shadow: 0 0 0 1px var(--hair-strong);
    display: flex; align-items: center; justify-content: space-between; padding: 0 16px 0 8px; overflow: hidden;
}
.ic-left { display: flex; align-items: center; gap: 10px; min-width: 0; }
.ic-expand { display: grid; line-height: 1.15; font-size: .8125rem; white-space: nowrap; }
.ic-expand span { color: rgba(255,255,255,.6); font-size: .75rem; }   /* the island is black in both themes */
.ic-eta { font: 800 1.125rem/1 var(--f-rounded); color: var(--l1); }
.widget { width: 172px; height: 172px; border-radius: 22%; padding: 16px; background: var(--widget-bg); border: 1px solid var(--hair-strong); box-shadow: var(--sh-widget); display: flex; flex-direction: column; gap: 12px; }
.widget-h { font: 700 .8125rem/1 var(--f-text); color: var(--ink-2); }
.widget-rows { display: grid; gap: 10px; }
.widget-rows li { display: flex; gap: 8px; align-items: flex-start; font-size: .75rem; line-height: 1.25; }
.wr-text { display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden; }
.wr-text b { color: var(--sev, var(--amber-text)); font-weight: 650; }
.wr-more { color: var(--ink-3); white-space: nowrap; }
/* Desktop keeps the 220px tile row: the window opens on the Disruption
   Alerts switches and fades out at the bottom rather than ending mid-line. */
.g-visual--shot img {
    position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; object-position: 50% 17%;
    -webkit-mask-image: linear-gradient(#000 80%, transparent); mask-image: linear-gradient(#000 80%, transparent);
}
@media (max-width: 899px) {
    .glance-grid { grid-template-columns: 1fr; gap: 16px; margin-top: 40px; }
    /* One column: a framed crop from the top of the screen down to the end of
       its footnote, at no more than 420px so it doesn't turn into a status bar. */
    .g-visual--shot { display: block; height: auto; width: 100%; max-width: 420px; margin-inline: auto; }
    .g-visual--shot img { position: static; height: auto; aspect-ratio: 780 / 740; object-position: 50% 0; -webkit-mask-image: none; mask-image: none; }
}

/* ========================================================== S7a access */
.access-grid { display: grid; grid-template-columns: repeat(12, 1fr); column-gap: 24px; row-gap: 24px; }
.access-copy { grid-column: 1 / 6; }
.access-copy h2 { max-width: 11ch; }
.access-list { margin-top: 40px; display: grid; gap: 22px; }
.access-list h3 { font-size: 1.125rem; }
.access-list p { color: var(--ink-2); font-size: var(--t-small); margin-top: 4px; max-width: 46ch; }
.access-live { margin-top: 28px; padding: 14px 16px; border-radius: var(--r-chip); background: rgba(82,140,203,.1); border: 1px solid rgba(82,140,203,.3); color: var(--access-text, #cfe0f3); font-size: var(--t-small); }
.access-demos { grid-column: 7 / 13; display: grid; gap: 24px; }
.demo-card { padding: 28px; }
.demo-card > p { color: var(--ink-2); margin-top: 6px; }
.hv-strip { width: 100%; height: auto; margin: 24px 0 8px; }
.hv-strip text { fill: var(--ink-3); font: 600 15px var(--f-text); }
@media (max-width: 599px) { .hv-strip text { font-size: 24px; } }   /* ≈14px once the strip scales down */
.hv-delay { stroke: var(--amber); transition: stroke var(--d-ui) var(--ease-ui); }
.hv-susp { stroke: var(--red); transition: stroke var(--d-ui) var(--ease-ui); }
.hv-borders { opacity: 0; transition: opacity var(--d-ui) var(--ease-ui); }
.hv-borders line { stroke: var(--hv-border); }
.demo-card.is-hv .hv-delay { stroke: var(--hv-delay); }
.demo-card.is-hv .hv-susp { stroke: var(--hv-susp); }
.demo-card.is-borders .hv-borders { opacity: 1; }
.switches { display: grid; border-radius: 14px; background: var(--bg-sunk); border: 1px solid var(--hair); }
.switch-row { display: flex; justify-content: space-between; align-items: center; padding: 10px 14px; min-height: 52px; cursor: pointer; }
.switch-row + .switch-row { border-top: 1px solid var(--hair); }
.switch { position: relative; width: 51px; height: 31px; border-radius: 999px; background: rgba(120,120,128,.32); transition: background-color var(--d-ui) var(--ease-ui); }
.switch::after { content: ""; position: absolute; inset: -7px -4px; }
.switch i { position: absolute; left: 2px; top: 2px; width: 27px; height: 27px; border-radius: 50%; background: #fff; box-shadow: 0 3px 8px rgba(0,0,0,.25); transition: transform var(--d-ui) var(--ease-spring); }
.switch[aria-checked="true"] { background: var(--ok); }
.switch[aria-checked="true"] i { transform: translateX(20px); }
.proof { margin-top: 20px; }
/* Framed on the Accessibility group only (legend and both switches), row
   edge to row edge, and no wider than 440px so its type stays under ours. */
.proof img { width: 100%; max-width: 440px; margin-inline: auto; aspect-ratio: 780 / 347; object-fit: cover; object-position: 50% 86.8%; border-radius: 14px; }
.proof figcaption { text-align: center; }
.proof figcaption { margin-top: 8px; font-size: var(--t-small); color: var(--ink-3); }
.compare-card { display: grid; grid-template-columns: 1fr auto; column-gap: 24px; align-items: center; }
.compare-card h3, .compare-card > p { grid-column: 1; }
.compare { --w: 240px; grid-column: 2; grid-row: 1 / span 3; }
.compare .screen img { position: absolute; inset: 0; }
.cmp-light { clip-path: inset(0 calc(100% - var(--x)) 0 0); }
.cmp-divider { position: absolute; top: 0; bottom: 0; left: var(--x); width: 2px; margin-left: -1px; background: #fff; pointer-events: none; }
.cmp-knob { position: absolute; left: 50%; top: 50%; width: 28px; height: 28px; border-radius: 50%; transform: translate(-50%, -50%); background: rgba(40,43,52,.95); border: 1px solid rgba(255,255,255,.4); box-shadow: 0 4px 12px rgba(0,0,0,.4); }
.cmp-knob::before { content: "‹ ›"; position: absolute; inset: 0; display: grid; place-items: center; font-size: 12px; color: #fff; letter-spacing: 2px; }
.cmp-range { position: absolute; inset: 0; width: 100%; height: 100%; margin: 0; opacity: 0; cursor: ew-resize; touch-action: pan-y; }
.compare:has(.cmp-range:focus-visible) { outline: 3px solid var(--focus); outline-offset: 4px; }
@media (min-width: 900px) and (max-width: 1199px) {
    .compare-card { grid-template-columns: 1fr; }
    .compare { grid-column: 1; grid-row: auto; margin: 24px auto 0; }
}
@media (max-width: 899px) {
    .access-copy, .access-demos { grid-column: 1 / -1; }
    .demo-card { padding: 20px; }
    .compare-card { grid-template-columns: 1fr; }
    .compare { --w: min(62vw, 280px); grid-column: 1; grid-row: auto; margin: 24px auto 0; }
}

/* ============================================================ S7b pro */
.pro-panel {
    display: grid; grid-template-columns: repeat(12, 1fr); column-gap: 24px; align-items: center;
    background: radial-gradient(ellipse at 85% 30%, rgba(236,105,100,.16), transparent 55%), var(--navy);
    border-radius: var(--r-panel); padding: clamp(32px, 5vw, 72px); border: 1px solid var(--hair);
}
.pro-copy { grid-column: 1 / 8; grid-row: 1; }
.pro-visual { grid-column: 9 / 13; grid-row: 1; justify-self: center; position: relative; perspective: 1200px; }
.pro-icon { width: 200px; height: 200px; border-radius: 22.5%; box-shadow: 0 30px 80px rgba(236,105,100,.35), 0 0 0 1px rgba(255,255,255,.1); }
.pro-chip { position: absolute; left: -28px; bottom: -14px; padding: 10px 14px; border-radius: var(--r-pill); background: #fff; color: #0F1014; font: 700 .875rem/1 var(--f-text); white-space: nowrap; box-shadow: 0 10px 30px rgba(0,0,0,.35); }
.pro-body { margin-top: 24px; color: var(--ink-2); font-size: var(--t-lede); line-height: 1.42; max-width: 44ch; }
.checks { margin: 28px 0 20px; display: grid; gap: 12px; }
.checks li { display: flex; gap: 12px; align-items: flex-start; font-weight: 500; }
.checks svg { color: var(--coral); flex: none; margin-top: 2px; }
@media (max-width: 899px) {
    .pro-panel { grid-template-columns: 1fr; row-gap: 32px; }
    .pro-copy, .pro-visual { grid-column: 1; grid-row: auto; }
    .pro-visual { justify-self: start; }
    .pro-icon { width: 120px; height: 120px; }
    .pro-chip { left: 64px; bottom: -12px; }
}

/* ============================================================== S8 get */
.get { background: var(--bg); isolation: isolate; }
.get::before {
    content: ""; position: absolute; inset: 0; z-index: -1; pointer-events: none;
    background:
        /* A fixed-size glow centred on the logo: a percentage ellipse follows the
           section's shape and becomes a tall column on phones. */
        radial-gradient(min(760px, 130vw) 460px at 50% calc(var(--section-y) + 160px), var(--glow-halo), transparent 70%),
        radial-gradient(var(--dot-grid) 1px, transparent 1.3px) 0 0 / 24px 24px;
    -webkit-mask-image: linear-gradient(transparent, #000 18%, #000 82%, transparent);
    mask-image: linear-gradient(transparent, #000 18%, #000 82%, transparent);
}
.logo-slot { position: relative; width: 360px; height: 320px; margin: 0 auto 8px; }
.logo-slot::after { content: ""; position: absolute; left: 20%; right: 20%; bottom: 6px; height: 24px; background: radial-gradient(ellipse, var(--floor-shadow), transparent 70%); z-index: -1; }
/* The no-WebGL mark matches the 3D one: coral shield, peach wings, gold halo. */
.logo-slot .mark-svg { --logo-body: var(--coral); --logo-wings: var(--peach); --logo-halo: var(--halo); }
.logo-slot .mark-svg { position: absolute; inset: 8% 6%; width: 88%; height: auto; }
html.gl .logo-slot.has-canvas .mark-svg { opacity: 0; }
.logo-slot .gl-canvas { position: absolute; inset: 0; width: 100%; height: 100%; }
html:not(.gl) .logo-slot .gl-canvas { opacity: 0; }   /* a lost context leaves the SVG mark, not a hole */
.get-head { text-align: center; display: grid; justify-items: center; }
.get-head .eyebrow { justify-content: center; }
.get-head .lede { margin-top: 18px; }
.get-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 24px; margin-top: 64px; }
.get-card { display: flex; flex-direction: column; overflow: hidden; }
.get-visual { position: relative; height: 360px; overflow: hidden; background: radial-gradient(ellipse at 50% 100%, var(--visual-glow), transparent 70%); border-bottom: 1px solid var(--hair); }
.get-copy { padding: 28px 32px 32px; display: grid; grid-template-columns: minmax(0, 1fr); gap: 12px; justify-items: start; }
.get-copy h3 { font-family: var(--f-display); font-size: clamp(1.75rem, 1.2rem + 1.4vw, 2.5rem); font-weight: 800; letter-spacing: -.02em; font-variation-settings: "wdth" 85; line-height: 1; overflow-wrap: anywhere; }
.get-copy > p:not(.get-label) { color: var(--ink-2); }
.get-cta { display: grid; grid-template-columns: minmax(0, 1fr); gap: 12px; justify-items: start; margin-top: 8px; }
/* Side by side, the two cards share their copy rows (subgrid), so both
   primary buttons start on the same line whatever the body copy's length. */
@supports (grid-template-rows: subgrid) {
    @media (min-width: 900px) {
        .get-card { display: grid; grid-row: span 5; grid-template-rows: subgrid; row-gap: 0; }
        .get-copy { grid-row: span 4; grid-template-rows: subgrid; align-content: start; }
    }
}
.get-label { font: 700 var(--t-eyebrow)/1 var(--f-display); letter-spacing: .14em; text-transform: uppercase; color: var(--ink-3); font-variation-settings: "wdth" 100; }
.soon { color: var(--ink-3); font-weight: 600; }
.browser { position: absolute; left: 32px; right: -40px; top: 36px; border-radius: var(--r-frame); overflow: hidden; background: var(--bg-raise-2); box-shadow: var(--sh-browser), 0 0 0 1px var(--hair-strong); }
.browser-bar { height: 28px; display: flex; align-items: center; gap: 6px; padding: 0 10px; position: relative; }
.browser-bar i { width: 9px; height: 9px; border-radius: 50%; background: var(--ink-4); }
.browser-bar span { position: absolute; left: 50%; transform: translateX(-50%); padding: 3px 14px; border-radius: 6px; background: var(--fill-1); font-size: 12px; color: var(--ink-3); }
.browser-tabs { display: flex; gap: 4px; padding: 6px 10px; border-top: 1px solid var(--hair); }
.browser-tabs button { position: relative; padding: 6px 12px; border-radius: 8px; font-size: .8125rem; font-weight: 600; color: var(--ink-3); min-height: 32px; }
.browser-tabs button::after { content: ""; position: absolute; inset: -6px -2px; }
.browser-tabs button[aria-selected="true"] { background: var(--fill-2); color: var(--ink); }
.browser-view { position: relative; aspect-ratio: 1800 / 1125; }
.bv-panel { position: absolute; inset: 0; opacity: 0; transition: opacity 300ms var(--ease-ui); }
.bv-panel { overflow: hidden; }
.bv-panel img { width: 100%; height: 100%; object-fit: cover; }
/* The arrivals panel sits in the capture's bottom right, but the card only
   ever shows the top of the browser: the 360px visual less the bar and tabs
   is 251px of the view, ±24px of parallax (main.js). This tab frames that
   corner instead, a little closer: the picture's bottom edge sits at 272px
   (or the view's own bottom, if that comes first), and it moves left past
   the 40px the card's edge cuts off the browser. The ground behind it is
   the capture's own dimmed map, for the odd pixel at the far end of the
   parallax. */
#wp-arrivals, #wp-departures { background: #0A0A0C; }
html[data-theme="light"] #wp-arrivals, html[data-theme="light"] #wp-departures { background: #B3B3B3; }
#wp-arrivals img { transform-origin: 100% 100%; transform: translate(calc(2% - 48px), min(0px, calc(272px - 100%))) scale(1.25); }
/* The GO page's Departures tab: the same corner, framed on the board's top
   rows (the panel is taller than the TTC's, so it sits lower). */
#wp-departures img { transform-origin: 100% 100%; transform: translate(calc(2% - 48px), min(0px, calc(300px - 100%))) scale(1.25); }
/* ...and its Alerts tab: the open alert sits at the capture's right edge,
   which the card cuts off (the browser runs 40px past it), so the picture
   moves left by that much. */
html[data-network="go"] #wp-alerts img { transform-origin: 100% 0; transform: translateX(-40px) scale(1.08); }
.bv-panel.is-on { opacity: 1; }
/* Hidden panels stay laid out (for the crossfade) but out of reach. */
.bv-panel[hidden] { display: block !important; visibility: hidden; }
.bv-panel.is-on[hidden] { visibility: visible; }
.bv-panel:focus-visible { outline-offset: -3px; }
.web-phone { display: none; }
.ios-phone { --w: 230px; position: absolute; left: 0; right: 0; margin-inline: auto; top: 40px; transform: rotateY(-12deg) rotateX(6deg); }
.card--ios .get-visual { perspective: 1200px; }
@media (max-width: 899px) {
    .logo-slot { width: 260px; height: 232px; }
    .get-grid { grid-template-columns: 1fr; gap: 16px; margin-top: 40px; }
    html.is-iphone .card--ios { order: -1; }
    .browser { display: none; }
    .web-phone { display: block; --w: min(58vw, 300px); margin: 32px auto 0; }
    .get-visual { height: auto; min-height: 300px; max-height: 420px; }
    .ios-phone { --w: min(58vw, 300px); position: relative; left: auto; right: auto; top: auto; margin: 32px auto 0; }
    .get-copy { padding: 24px 20px 28px; }
}
/* Narrow phones with large text spacing: buttons wrap instead of clipping. */
@media (max-width: 599px) {
    .get-cta .btn { max-width: 100%; white-space: normal; height: auto; min-height: 48px; padding-block: 12px; text-align: center; }
}

/* ============================================================== footer */
.footer { position: relative; z-index: var(--z-content); background: var(--bg-sunk); font-size: var(--t-small); color: var(--ink-2); padding-bottom: calc(32px + env(safe-area-inset-bottom)); }
.footer-bands { display: grid; }
.footer-bands i { height: 6px; background: var(--c); transform-origin: 0 50%; }
/* Link columns take their content's width, so the email never breaks mid-word. */
.footer-grid { display: grid; grid-template-columns: minmax(0, 2fr) repeat(3, max-content); gap: 32px 48px; padding-top: 64px; }
.footer-brand { display: grid; gap: 10px; justify-items: start; }
.footer-brand .wordmark { color: var(--ink); }
.footer-mark { width: 56px; height: auto; --logo-body: var(--peach); --logo-wings: var(--peach); }
.footer-h { font: 700 var(--t-eyebrow)/1 var(--f-display); letter-spacing: .14em; text-transform: uppercase; color: var(--ink-3); margin-bottom: 14px; font-variation-settings: "wdth" 100; }
.footer ul { display: grid; gap: 2px; }
.footer ul a { color: var(--ink); text-decoration: none; display: inline-flex; min-height: 44px; align-items: center; overflow-wrap: anywhere; }
.footer ul a:hover, .footer ul a:focus-visible { text-decoration: underline; text-underline-offset: 3px; }
.muted { color: var(--ink-3); display: inline-flex; min-height: 44px; align-items: center; }
.footer-bottom { margin-top: 48px; padding-top: 24px; border-top: 1px solid var(--hair); display: grid; gap: 8px; }
.footer-bottom a { color: var(--ink); }
/* Three items that wrap between each other, never inside one. */
.footer-meta { display: flex; flex-wrap: wrap; align-items: center; column-gap: 16px; }
.footer-meta a { display: inline-flex; align-items: center; min-height: 44px; }
.disclaimer { font-size: var(--t-micro); color: var(--ink-3); }
@media (max-width: 899px) { .footer-grid { grid-template-columns: 1fr 1fr; gap: 32px; } .footer-brand { grid-column: 1 / -1; } }
@media (max-width: 599px) { .footer-grid { grid-template-columns: 1fr; gap: 32px; } }

/* ======================================================= light extras
   What the tokens can't say: surfaces that flip from light-on-dark to
   dark-on-light, and washes that need more weight on white. */
html[data-theme="light"] .btn--web { box-shadow: 0 1px 2px rgba(15,16,20,.14); }
html[data-theme="light"] .btn--web:hover { box-shadow: var(--sh-btn-halo); }
html[data-theme="light"] .alert-card { --sev-wash: .1; }
html[data-theme="light"] .switch { background: rgba(120,120,128,.2); }
html[data-theme="light"] .switch[aria-checked="true"] { background: var(--ok); }
html[data-theme="light"] .access-live { --access-text: #1E4E86; background: rgba(82,140,203,.12); border-color: rgba(82,140,203,.36); }
html[data-theme="light"] .pro-panel { background: radial-gradient(ellipse at 85% 30%, rgba(236,105,100,.13), transparent 55%), var(--navy); box-shadow: var(--sh-card); }
html[data-theme="light"] .pro-chip { background: var(--ink); color: #fff; box-shadow: 0 10px 30px rgba(15,16,20,.2); }
html[data-theme="light"] .pro-icon { box-shadow: 0 30px 80px rgba(236,105,100,.3), 0 0 0 1px rgba(15,16,20,.06); }
/* The app's light icon is white on coral; on a light ground the mark takes the coral. */
html[data-theme="light"] .footer-mark { --logo-body: var(--coral); --logo-wings: var(--coral); }
html[data-theme="light"] .halo-label { box-shadow: 0 6px 18px rgba(15,16,20,.12); }

/* ============================================================ reveals */
html.motion .reveal { opacity: 0; transform: translateY(32px); }
/* The hero waits for its entrance only under .intro (see the boot script):
   the H1's words below their masks, the lede merely lowered, never faded,
   since it's the largest text in the first viewport. */
html.intro .hero-h .w > span { transform: translateY(135%); }
html.intro .hero-side .lede { transform: translateY(16px); }
html.intro .hero-eyebrow, html.intro .hero-side .ctas > *, html.intro .hero-side .micro, html.intro .nav { opacity: 0; }
html.motion .footer-bands i { transform: scaleX(0); }
html.failsafe .reveal, html.failsafe .hero-eyebrow, html.failsafe .hero-side .lede, html.failsafe .hero-side .ctas > *, html.failsafe .hero-side .micro { opacity: 1 !important; transform: none !important; }
html.failsafe .hero-h .w > span, html.failsafe .footer-bands i { transform: none !important; }
html.failsafe.motion.gl-ok .stretch { height: auto; }

/* The theme switch's crossfade (SS.setTheme, where the browser has one). */
::view-transition-old(root), ::view-transition-new(root) { animation-duration: 280ms; animation-timing-function: var(--ease-ui); }

/* Decorative loops stop when paused or in Low Power Mode */
html.motion-paused *, html.lite * { animation-play-state: paused !important; }
/* ...and while they're off screen (main.js marks .is-onscreen). */
html .board-updated:not(.is-onscreen) .upd-dot { animation-play-state: paused !important; }
