/* bikechat.app
   Tokens mirror lib/core/theme/app_colors.dart ("Midnight Electric") and app_spacing.dart, except the one bright blue
   and the hero's navy, which are the founder's references' (ledger R63, R64).
   No colour or size outside :root. Motion: transform and opacity only, gated by prefers-reduced-motion. */

/* 1. The browser's own hiding stays sticky, whatever a component sets later. */
[hidden] { display: none !important; }
dialog:not([open]) { display: none; }

/* 2. Tokens */
:root {
  color-scheme: dark;
  --black: #0B0C10;
  --deep-navy: #0C0D12;
  --card-fill: #121419;
  --surface: #14161B;
  --surface-alt: #1A1C22;
  --surface-high: #22252D;
  --card-hairline: #23262E;
  --border: #2A2D36;
  --border-strong: #3A3E49;
  --white: #F3F4F6;
  --white-dim: #DDE0E6;
  --grey-text: #A7ABB4;
  --grey-muted: #6A6F7A;
  /* The one bright blue (R64): the founder's references' blue, sampled from the hero's Download button, used wherever blue
     appears. White on it reads 4.7:1. Light: the far end of the highlighted words, and blue text on the dark (8:1). */
  --blue: #235EFA;
  --blue-pressed: #1E50D5;
  --blue-light: #62ACFB;
  --blue-soft: rgba(35, 94, 250, 0.16);
  --blue-border: rgba(35, 94, 250, 0.32);
  --blue-faint: rgba(35, 94, 250, 0.38);
  /* The hero's ground and inks, sampled from the founder's hero reference (R63). */
  --navy: #0C1118;
  --glass: #121821;
  --field: #1F272F;
  --steel: #6F93C7;
  --mist: #A2AFC9;
  --mist-dim: #818EA0;
  --line: rgba(160, 190, 230, 0.13);
  --line-strong: rgba(160, 190, 230, 0.21);
  --contour: rgba(150, 180, 230, 0.07);
  --online: #4FE24E;
  --teal: #24C2AE;
  --teal-soft: rgba(36, 194, 174, 0.14);
  --amber: #F5A623;
  --amber-soft: rgba(245, 166, 35, 0.12);
  --danger: #F2565B;
  --danger-soft: rgba(242, 86, 91, 0.12);
  --cream: #F4F1EA;
  --on-accent: #F3F4F6;
  --on-accent-dark: #062B26;
  --shade: rgba(11, 12, 16, 0.92);
  --shade-mid: rgba(11, 12, 16, 0.40);
  --shade-none: rgba(11, 12, 16, 0);
  --highlight: rgba(255, 255, 255, 0.06);
  --hairline: rgba(255, 255, 255, 0.09);
  --scrim: rgba(11, 12, 16, 0.72);
  --ink: #000000;
  --blue-shadow: rgba(35, 94, 250, 0.30);   /* the tint under the primary button: a shadow, not a glow (R44) */
  --light-stage: rgba(98, 172, 251, 0.16);  /* the one soft light behind the hero phone: the reference lifts its navy by 7, 13, 19 */
  --dot: color-mix(in srgb, var(--blue-light) 32%, var(--navy));       /* the hero's location dots */
  --dot-ring: color-mix(in srgb, var(--blue-light) 26%, var(--navy));
  --drop: rgba(0, 0, 0, 0.75);
  /* The device (R69): black titanium, as both of the founder's references draw every phone. A graphite frame, a thin
     specular line on its outer edge where the light falls (top left, bottom right), a faint chamfer against the bezel. */
  --ti-0: #0A0B0E;
  --ti-1: #191B20;
  --ti-2: #2C2F36;
  --ti-3: #4B4F58;
  --ti-spec: rgba(240, 244, 252, 0.92);
  --ti-spec-dim: rgba(236, 241, 250, 0.14);
  --ti-inner: rgba(255, 255, 255, 0.07);
  --lens: #16245A;
  --lens-glint: rgba(125, 160, 255, 0.75);
  --status-shade: rgba(10, 14, 20, 0.8);   /* the hero screen's top: near-black navy with the map faint under it */

  --r-sm: 8px; --r-md: 12px; --r-lg: 16px; --r-xl: 24px; --r-pill: 999px;
  --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;

  /* Headings in the app's own Inter, bold, as both of the founder's references set them (R64; Barlow left the site). */
  --font-display: "Inter", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  --font-body: "Inter", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  --size-body: 17px;
  --size-caption: 13px;
  --size-small: 14px;

  /* Motion language (ledger R47): two curves, six durations, two distances, one stagger. Every animation and transition uses these. */
  --ease: cubic-bezier(0.32, 0.72, 0, 1);          /* arrivals: fast out, long settle */
  --ease-standard: cubic-bezier(0.4, 0, 0.2, 1);   /* symmetric state swaps */
  --t-press: 120ms;    /* immediate feedback */
  --t-hover: 180ms;    /* hover and focus colour */
  --t-exit: 250ms;     /* anything leaving */
  --t-hold: 400ms;     /* the beat before a state changes */
  --t-swap: 450ms;     /* one screen state to the next */
  --t-enter: 650ms;    /* an element arriving */
  --t-focal: 900ms;    /* the one authored entrance of a section */
  --d-rise: 16px;      /* how far text rises in */
  --d-rise-lg: 32px;   /* how far a phone or a stage rises in */
  --stagger: 70ms;
  --t-beat: 1200ms;    /* the gap between the states of a product sequence */
  --t-ping: 1000ms;    /* the hero's one ping, from your position out past the farthest rider (R71) */
  --t-type: 900ms;     /* how long Ivan is seen typing before his reply */
  /* The ping's curve: the quadratic ease-out, cubic-bezier(1/3, 2/3, 2/3, 1), so the ring's radius after a share x of
     its run is 1 - (1 - x)^2 of its reach, and each rider's delay is solved from its distance (section 8). */
  --ease-ping: cubic-bezier(0.333, 0.667, 0.667, 1);
  --btn-lit: rgba(255, 255, 255, 0.18);   /* the primary button's lit top edge */

  --content: 1160px;
  --content-wide: 1400px;
  --gutter: 20px;
  --measure: 32em;
  --section-pad: clamp(56px, 8vw, 112px);
  --phone-w: clamp(240px, 68vw, 320px);
  --tap: 44px;
  --nav-h: 64px;
  --bar: rgba(11, 12, 16, 0.74);
  /* The founder's hero reference at 1440 (R63): its content runs 1179 px wide, its bar 1115. */
  --hero-inner: 1179px;
  --bar-inner: 1115px;
}
@media (min-width: 900px) {
  :root { --gutter: 48px; --size-body: 18px; --phone-w: clamp(280px, 26vw, 360px); --nav-h: 78px; }
}

/* 3. Fonts (self-hosted subsets; licences in /fonts/web/LICENSES.txt) */
@font-face { font-family: "Inter"; font-style: normal; font-weight: 400; font-display: swap; src: url(/fonts/web/inter-400.woff2) format("woff2"); }
@font-face { font-family: "Inter"; font-style: normal; font-weight: 500; font-display: swap; src: url(/fonts/web/inter-500.woff2) format("woff2"); }
@font-face { font-family: "Inter"; font-style: normal; font-weight: 600; font-display: swap; src: url(/fonts/web/inter-600.woff2) format("woff2"); }
@font-face { font-family: "Inter"; font-style: normal; font-weight: 700; font-display: swap; src: url(/fonts/web/inter-700.woff2) format("woff2"); }
/* Cyrillic capitals and small letters only, for the map's place names; fetched only where such a letter appears. */
@font-face { font-family: "Inter"; font-style: normal; font-weight: 500; font-display: swap; src: url(/fonts/web/inter-500-cyr.woff2) format("woff2"); unicode-range: U+0401, U+0410-044F, U+0451; }

/* 4. Base */
*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; text-size-adjust: 100%; scroll-behavior: smooth; }
body {
  margin: 0;
  background: var(--black);
  color: var(--white);
  font: 400 var(--size-body) / 1.6 var(--font-body);
  -webkit-font-smoothing: antialiased;
  -webkit-tap-highlight-color: transparent;
  touch-action: manipulation;
  overflow-x: hidden;
  padding-left: env(safe-area-inset-left);
  padding-right: env(safe-area-inset-right);
}
img, video { display: block; max-width: 100%; height: auto; }
a { color: var(--blue-light); text-decoration: none; }
a:active { color: var(--blue); }
/* The ring follows each element's own shape: a focused button keeps its corners. */
:focus-visible { outline: 2px solid var(--blue-light); outline-offset: 3px; }
h1, h2, h3 { font-family: var(--font-display); margin: 0; text-wrap: balance; }
h1 { font-size: clamp(2.9rem, 1.4rem + 5.6vw, 5.4rem); line-height: 0.98; font-weight: 700; letter-spacing: -0.025em; }
h2 { font-size: clamp(2rem, 1rem + 2.8vw, 3.2rem); line-height: 1.08; font-weight: 700; letter-spacing: -0.025em; }
h3 { font-size: 1.5rem; line-height: 1.15; font-weight: 600; letter-spacing: -0.01em; }
p { margin: 0; }
ol, ul { margin: 0; padding: 0; }
svg { display: block; }
.wrap { width: 100%; max-width: var(--content); margin-inline: auto; padding-inline: var(--gutter); }
.section { padding-block: var(--section-pad); }
section[id] { scroll-margin-top: calc(var(--nav-h) + var(--s-4)); }
/* Muted copy uses --grey-text: --grey-muted reads 3.9:1 on the canvas, under the 4.5:1 floor, so it never carries words. */
.eyebrow { font: 500 var(--size-caption) / 1 var(--font-body); letter-spacing: 0.1em; text-transform: uppercase; color: var(--teal); }
.lede { color: var(--grey-text); max-width: var(--measure); text-wrap: pretty; font-size: 1.06em; }
.caption { font-size: var(--size-caption); color: var(--grey-text); line-height: 1.5; }
/* Highlighted words in headings (R64, superseding R41): the one blue, lightening across the words as both of the founder's
   references draw it. Sized to the words, so the gradient runs from their first letter to their last. The solid light
   blue stays underneath for browsers without background-clip, and forced colours get plain text. */
.hl { color: var(--blue-light); }
@supports ((-webkit-background-clip: text) or (background-clip: text)) {
  .hl { background: linear-gradient(90deg, color-mix(in srgb, var(--blue) 60%, var(--blue-light)), var(--blue-light)); -webkit-background-clip: text; background-clip: text; -webkit-text-fill-color: transparent; }
}
@media (forced-colors: active) { .hl { background: none; -webkit-text-fill-color: currentColor; } }
.skip { position: absolute; left: var(--gutter); top: -200px; z-index: 10; padding: var(--s-3) var(--s-4); background: var(--surface-high); color: var(--white); border-radius: var(--r-md); }
.skip:focus { top: var(--s-4); }

/* 5. Buttons and links */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--s-2);
  min-height: 48px; padding: 0 var(--s-5); border: 1px solid transparent; border-radius: var(--r-md);
  background: var(--blue); color: var(--on-accent);
  font: 500 16px / 1 var(--font-body); white-space: nowrap; cursor: pointer;
  box-shadow: inset 0 1px 0 var(--btn-lit), 0 10px 30px -12px var(--blue-shadow);
  transition: background-color var(--t-hover) var(--ease-standard), border-color var(--t-hover) var(--ease-standard), box-shadow var(--t-hover) var(--ease-standard), transform var(--t-press) var(--ease);
}
.btn:active { background: var(--blue-pressed); color: var(--on-accent); transform: scale(0.98); }
/* The icon a button carries (download, play), as the founder's reference draws them: line or solid, in the text's colour. */
.btn__icon { width: 1.1em; height: 1.1em; flex: none; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; overflow: visible; }
.btn__icon--solid { fill: currentColor; stroke: none; width: 0.9em; height: 0.9em; }
/* Under the pointer the icon says what the button does (R71): the download arrow dips toward its tray, the play glyph
   leans forward. A few pixels, on the hover time; a touch screen gets the press instead. */
.btn__arrow, .btn__icon--solid { transition: transform var(--t-hover) var(--ease); }
.btn--lg { min-height: 64px; padding: 0 36px; gap: 14px; font-size: 18px; border-radius: var(--r-md); }
.btn--sm { min-height: 40px; padding: 0 16px; gap: 8px; font-size: 14px; border-radius: var(--r-sm); box-shadow: none; }
.btn--ghost { background: transparent; color: var(--white); border-color: var(--line-strong); box-shadow: none; }
.btn--sm { box-shadow: inset 0 1px 0 var(--btn-lit); }
.btn--ghost.btn--lg { padding: 0 40px; }
.btn--ghost:active { background: var(--surface-high); color: var(--white); }
@media (hover: hover) and (pointer: fine) {
  .btn:hover { background: var(--blue-pressed); }
  .btn:hover .btn__arrow { transform: translateY(1.5px); }
  .btn:hover .btn__icon--solid { transform: translateX(2px); }
  .btn--ghost:hover { background: var(--surface-high); border-color: var(--line-strong); }
  a:hover { text-decoration: underline; text-underline-offset: 0.2em; }
  .btn:hover, .lockup:hover, .nav__link:hover, .footer__col a:hover { text-decoration: none; }
}

/* 6. Top bar (sticky, translucent) and lockup.
   R48: on the home page the bar is clear over the top of the hero and takes its fill, hairline and blur once the page
   has scrolled past the sentinel (24 px). Every other page keeps the filled bar: the clear state needs .home. */
.topbar { position: sticky; top: 0; z-index: 20; background-color: var(--bar); -webkit-backdrop-filter: blur(14px); backdrop-filter: blur(14px); border-bottom: 1px solid var(--card-hairline);
  transition: background-color var(--t-swap) var(--ease-standard), border-color var(--t-swap) var(--ease-standard), -webkit-backdrop-filter var(--t-swap) var(--ease-standard), backdrop-filter var(--t-swap) var(--ease-standard); }
.js .home:not(.is-scrolled) .topbar { background-color: transparent; border-bottom-color: transparent; -webkit-backdrop-filter: blur(0); backdrop-filter: blur(0); }
.bar-sentinel { position: absolute; top: var(--s-5); left: 0; width: 1px; height: 1px; pointer-events: none; }
.nav { display: flex; align-items: center; justify-content: space-between; gap: var(--s-4); min-height: var(--nav-h); }
/* The bar's measure is the founder's reference's (R63): narrower than the hero's, centred. */
.topbar > .wrap { max-width: calc(var(--bar-inner) + var(--gutter) * 2); }
.lockup { display: inline-flex; align-items: center; gap: var(--s-3); min-height: var(--tap); color: var(--white); }
.lockup__mark { width: 28px; height: 38px; }
.lockup__name { font: 600 19px / 1 var(--font-body); letter-spacing: 0.04em; color: var(--white); }
/* In-page links from tablet width up; phones scroll. */
.nav__links { display: none; list-style: none; }
.nav__link { --link-pad: var(--s-3); position: relative; display: inline-flex; align-items: center; min-height: var(--tap); padding: 0 var(--link-pad); color: var(--mist); font-size: 15px; font-weight: 400; border-radius: var(--r-sm); transition: color var(--t-hover) var(--ease-standard); }
/* A 1 px rule under the label grows from the left on hover, and stays under the section in view (aria-current). */
.nav__link::after { content: ""; position: absolute; left: var(--link-pad); right: var(--link-pad); bottom: var(--s-2); height: 1px; background: currentColor; transform: scaleX(0); transform-origin: left; transition: transform var(--t-hover) var(--ease-standard); }
.nav__link[aria-current="true"] { color: var(--white); }
/* One rule slides from link to link under the section in view (R71), rather than each link drawing its own: site.js
   sets its place and width. It fades in where it first lands, slides from then on, and fades out over the hero. */
.nav__links { position: relative; }
.nav__links::after { content: ""; position: absolute; left: 0; bottom: var(--s-2); width: 1px; height: 1px; background: var(--white); transform-origin: 0 0; transform: translateX(var(--ind-x, 0px)) scaleX(var(--ind-w, 0)); opacity: 0; pointer-events: none; transition: transform var(--t-swap) var(--ease), opacity var(--t-hover) var(--ease-standard); }
.nav__links.has-current::after { opacity: 1; }
.nav__links.is-landing::after { transition: opacity var(--t-hover) var(--ease-standard); }
@media (min-width: 720px) { .nav__links { display: flex; gap: var(--s-1); } }
/* Desktop: the reference's larger pin and name, and its looser links. */
@media (min-width: 900px) {
  .lockup { gap: 14px; }
  .lockup__mark { width: 34px; height: 46px; }
  .lockup__name { font-size: 21px; letter-spacing: 0.12em; }
  .nav__link { --link-pad: 20px; }
  .topbar .btn--sm { min-height: 46px; padding: 0 18px 0 20px; gap: 10px; font-size: 14.5px; }
}
@media (hover: hover) and (pointer: fine) { .nav__link:hover { color: var(--white); } .nav__link:hover::after { transform: scaleX(1); } }

/* 7. The iPhone. Every size derives from the width (--phone-w): rim, bezel, corner radii, island, home indicator, buttons. */
.iphone {
  --w: var(--phone-w);
  --rim: calc(var(--w) * 0.012);
  --bezel: calc(var(--w) * 0.024);
  width: var(--w); position: relative; z-index: 1;
  border: var(--rim) solid transparent; padding: var(--bezel);
  border-radius: calc(var(--w) * 0.165);
  background: linear-gradient(var(--ink), var(--ink)) padding-box, linear-gradient(150deg, var(--ti-2) 0%, var(--ti-1) 22%, var(--ti-0) 50%, var(--ti-1) 78%, var(--ti-2) 100%) border-box;
  box-shadow: inset 0 0 0 1px var(--ti-inner), 0 50px 90px -30px var(--drop), 0 0 0 1px var(--ink);
}
/* The polished outer half of the frame (R69): a ring about half the frame wide, brightest where the light falls, so
   the edge reads as a curved metal band (bright outside, graphite inside) the way the references light it. */
.iphone::before {
  content: ""; position: absolute; inset: calc(var(--rim) * -1); z-index: 2; pointer-events: none;
  border-radius: inherit; padding: max(1px, calc(var(--w) * 0.0055));
  background: linear-gradient(150deg, var(--ti-spec) 0%, var(--ti-spec-dim) 22%, var(--ti-spec-dim) 74%, var(--ti-spec) 100%);
  -webkit-mask: linear-gradient(var(--ink) 0 0) content-box, linear-gradient(var(--ink) 0 0); -webkit-mask-composite: xor;
  mask: linear-gradient(var(--ink) 0 0) content-box, linear-gradient(var(--ink) 0 0); mask-composite: exclude;
}
.iphone__screen { position: relative; aspect-ratio: 1080 / 2340; border-radius: calc(var(--w) * 0.13); overflow: hidden; background: var(--black); }
/* The screens are 1080x2200 boxes without a status bar; they sit at the bottom and the top strip holds the island. */
.iphone__screen > img, .iphone__screen > video { position: absolute; left: 0; right: 0; bottom: 0; width: 100%; height: auto; }
/* Island, status row and home indicator at a real iPhone 15 Pro's places (R75): the island 11 pt under the screen's top
   edge, the indicator 8 pt above its bottom edge and 134 x 5 pt (in the 393 pt screen, 0.928 of the phone's width).
   They are measured from the padding box, which already starts inside the rim, so the rim is not counted twice (the
   fault R62 found and left in the Ugly log; the riding and hero phones carry their own fitted values). */
.iphone__island { position: absolute; left: 50%; top: calc(var(--bezel) + var(--w) * 0.026); transform: translateX(-50%); width: calc(var(--w) * 0.30); height: calc(var(--w) * 0.088); border-radius: var(--r-pill); background: var(--ink); z-index: 3; }
/* The front camera at the island's right end: a dark lens with a blue glint, as both references draw it. */
.iphone__island::after { content: ""; position: absolute; right: 14%; top: 50%; width: calc(var(--w) * 0.028); height: calc(var(--w) * 0.028); transform: translateY(-50%); border-radius: 50%; background: radial-gradient(circle at 36% 32%, var(--lens-glint) 0 7%, transparent 12%), radial-gradient(circle, var(--ink) 0 30%, var(--lens) 46% 60%, var(--ink) 76%); }
.iphone__home { position: absolute; left: 50%; bottom: calc(var(--bezel) + var(--w) * 0.019); transform: translateX(-50%); width: calc(var(--w) * 0.316); height: calc(var(--w) * 0.012); border-radius: var(--r-pill); background: var(--white); opacity: 0.9; z-index: 3; }
.iphone__btn { position: absolute; width: calc(var(--w) * 0.012); border-radius: 2px; background: linear-gradient(90deg, var(--ti-3), var(--ti-1) 45%, var(--ti-0)); }
.iphone__btn--action { left: calc(var(--w) * -0.012 - var(--rim)); top: 14%; height: 3.4%; }
.iphone__btn--up { left: calc(var(--w) * -0.012 - var(--rim)); top: 20.5%; height: 6.2%; }
.iphone__btn--down { left: calc(var(--w) * -0.012 - var(--rim)); top: 28.5%; height: 6.2%; }
.iphone__btn--power { right: calc(var(--w) * -0.012 - var(--rim)); top: 21%; height: 10.5%; background: linear-gradient(270deg, var(--ti-2), var(--ti-1) 45%, var(--ti-0)); }
.iphone--sm { --phone-w: clamp(220px, 24vw, 300px); }
.iphone--xs { --phone-w: clamp(150px, 16vw, 220px); }
/* The status strip on the island row (R46): 9:41, signal, Wi-Fi, battery. Decorative, drawn once per phone, sized from the width like the rest. */
.iphone__status { position: absolute; left: calc(var(--rim) + var(--bezel)); right: calc(var(--rim) + var(--bezel)); top: calc(var(--bezel) + var(--w) * 0.026); height: calc(var(--w) * 0.088); display: flex; align-items: center; justify-content: space-between; padding-inline: calc(var(--w) * 0.09) calc(var(--w) * 0.075); z-index: 3; color: var(--white); pointer-events: none; }
.iphone__time { font: 500 calc(var(--w) * 0.047) / 1 var(--font-body); letter-spacing: -0.01em; }
.iphone__glyphs { width: calc(var(--w) * 0.165); height: auto; fill: currentColor; }
/* A screen with two real states (R45): the first frame shows; the second fades in once the screen is in view, after a beat.
   Without script the second state simply shows, which is what the screen's label describes. */
.js .moment__frame--end { opacity: 0; transition: opacity var(--t-swap) var(--ease-standard); }
.js .moment.is-on .moment__frame--end { opacity: 1; transition-delay: var(--t-hold); }
.phone__pause {
  position: absolute; right: calc(var(--bezel) + var(--rim) + 12px); top: calc(var(--bezel) + var(--rim) + var(--w) * 0.15); z-index: 4;
  width: var(--tap); height: var(--tap); border-radius: 50%; border: 1px solid var(--hairline);
  background: var(--scrim); color: var(--white); cursor: pointer; display: grid; place-items: center;
  transition: background-color var(--t-hover) var(--ease-standard), border-color var(--t-hover) var(--ease-standard), transform var(--t-press) var(--ease);
}
.phone__pause:active { transform: scale(0.94); }
@media (hover: hover) and (pointer: fine) { .phone__pause:hover { background-color: var(--black); border-color: var(--border-strong); } }
.phone__pause svg { width: 18px; height: 18px; fill: currentColor; }
.phone__pause .ico-play { display: none; }
.phone__pause[data-state="paused"] .ico-play { display: block; }
.phone__pause[data-state="paused"] .ico-pause { display: none; }

/* 8. Hero (R63): the founder's hero reference rebuilt in HTML and CSS. Measured at 1440 x 810, the view the reference
   shows at 1.161x (review-2026-10-16/hero-ref-*.cjs): the copy from 131 px, the phone's flat box 330.7 px wide from
   879 px, turned 14.02 degrees about its upright under a perspective of 5.618 phone widths (hero-ref-phone.cjs fits it to
   the reference's outline within 0.7 px), and the cards and dots placed in phone widths around it, so the stage scales
   as one piece. The ground is the reference's navy, returning to the page's black at its foot. */
/* It runs up under the clear bar (R48), so the navy, the contours and the light continue behind it, as in the reference. */
.hero { position: relative; display: flex; flex-direction: column; overflow: hidden; margin-top: calc(-1px - var(--nav-h)); padding-top: calc(var(--nav-h) + 1px); background: linear-gradient(180deg, var(--navy) 0%, var(--navy) 94%, var(--black) 100%); }
/* Faint contour lines, as faint as the reference: the lines are a mask, their colour a token. */
.hero::after { content: ""; position: absolute; inset: 0; background: var(--contour); -webkit-mask: url(/img/hero/contours.svg) center / cover no-repeat; mask: url(/img/hero/contours.svg) center / cover no-repeat; pointer-events: none; }
.hero__grid { flex: 1; display: grid; gap: var(--s-7); align-items: center; padding-block: var(--s-6) var(--s-8); position: relative; z-index: 1; }
.hero__copy { display: grid; justify-items: start; }
.hero .eyebrow { color: var(--steel); font-size: 12px; letter-spacing: 0.1em; }
.hero h1 { margin-top: 22px; font-family: var(--font-body); font-weight: 700; font-size: clamp(2.6rem, 1.2rem + 6.4vw, 3.6rem); line-height: 0.9; letter-spacing: -0.035em; }
/* Each line is sized to its words; its words reach below the descenders, so the clipped fills cover every glyph. The
   line is also the mask its words rise through on load (R71): it clips only below, just past the descenders, and
   reaches well beyond the letters above and to the sides, so nothing is cut at rest. */
.hero h1 .line { display: block; width: fit-content; clip-path: inset(-0.4em -0.3em -0.16em -0.3em); }
.hero h1 .line__in { display: block; padding-bottom: 0.16em; margin-bottom: -0.16em; }
@supports ((-webkit-background-clip: text) or (background-clip: text)) {
  .hero h1 .line:first-child .line__in { background: linear-gradient(180deg, var(--white) 25%, var(--white-dim)); -webkit-background-clip: text; background-clip: text; -webkit-text-fill-color: transparent; }
}
@media (forced-colors: active) { .hero h1 .line:first-child .line__in { background: none; -webkit-text-fill-color: currentColor; } }
.hero__sub { margin-top: 20px; max-width: 28.6em; color: var(--mist); font-size: 17px; line-height: 1.45; text-wrap: pretty; }
.hero__cta { margin-top: 30px; display: flex; flex-wrap: wrap; align-items: center; gap: 16px; justify-self: stretch; }
/* On a phone the two buttons are one width, one above the other; side by side once they fit. */
.hero__cta .btn { flex: 1 1 100%; }
@media (min-width: 520px) { .hero__cta .btn { flex: 0 0 auto; } }
.hero__soon { margin-top: 10px; color: var(--mist-dim); font-size: var(--size-small); }
/* The five things the app does, each a way to its own section below. */
.chips { margin-top: 36px; display: flex; flex-wrap: wrap; gap: 12px; list-style: none; }
.chip { display: inline-flex; align-items: center; gap: 11px; min-height: var(--tap); padding: 0 15px 0 14px; border: 1px solid var(--line); border-radius: var(--r-pill); color: var(--white-dim); font-size: 12.5px; line-height: 1; transition: border-color var(--t-hover) var(--ease-standard), color var(--t-hover) var(--ease-standard), background-color var(--t-hover) var(--ease-standard); }
.chip svg { width: 19px; height: 19px; flex: none; fill: none; stroke: currentColor; stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round; color: var(--white); }
.chip:active { background: var(--surface-alt); }
@media (hover: hover) and (pointer: fine) { .chip:hover { border-color: var(--line-strong); color: var(--white); text-decoration: none; } }
/* A long press on a control does not select its label (a phone's tell); content text stays selectable. */
.btn, .chip { -webkit-user-select: none; user-select: none; }
/* On a phone the five chips are one row that scrolls sideways with the page's own touch physics (R72c), not three rows
   of pills between the buttons and the phone: the row bleeds to the screen's edges, starts on the copy's edge, and
   fades out at the far end so the next chip says there is more. Its padding keeps a focused chip's ring in view. */
@media (max-width: 599px) {
  .chips { justify-self: stretch; flex-wrap: nowrap; gap: var(--s-2); overflow-x: auto; overscroll-behavior-x: contain; scrollbar-width: none; scroll-snap-type: x proximity; margin: 30px calc(var(--gutter) * -1) 0; padding: var(--s-1) var(--gutter); scroll-padding-inline: var(--gutter);
    /* The fade is only the last 16px. A 30px fade hid the next chip entirely on a 360px phone, so the row looked finished at two. */
    -webkit-mask: linear-gradient(90deg, var(--ink) calc(100% - var(--s-4)), transparent); mask: linear-gradient(90deg, var(--ink) calc(100% - var(--s-4)), transparent); }
  .chips::-webkit-scrollbar { display: none; }
  .chips li { flex: none; scroll-snap-align: start; }
  .chip { padding: 0 var(--s-3); gap: var(--s-2); }
  .btn--lg { min-height: 56px; font-size: 17px; }
}

/* The stage is the phone's flat box (W wide); every card, dot and light is placed in W from its top-left corner. */
.hero__stage { --w: var(--phone-w); --phone-w: clamp(220px, 62vw, 300px); position: relative; width: var(--w); justify-self: center; left: calc(var(--w) * -0.068); }
/* One soft light behind the phone: the reference lifts its navy around the phone and lets it fall off to the edges. */
.hero__stage::before { content: ""; position: absolute; left: calc(var(--w) * -0.85); right: calc(var(--w) * -0.85); top: calc(var(--w) * -0.6); bottom: calc(var(--w) * -0.6); background: radial-gradient(closest-side, var(--light-stage), transparent); pointer-events: none; z-index: -1; }
.hero__dot { position: absolute; width: calc(var(--w) * 0.05); height: calc(var(--w) * 0.05); margin: calc(var(--w) * -0.025) 0 0 calc(var(--w) * -0.025); border: 1px solid var(--dot-ring); border-radius: 50%; }
.hero__dot::after { content: ""; position: absolute; inset: 27%; border-radius: 50%; background: var(--dot); }
.hero__dot--1 { left: calc(var(--w) * -0.261); top: calc(var(--w) * 0.629); }
.hero__dot--2 { left: calc(var(--w) * -0.331); top: calc(var(--w) * 1.316); }
.hero__dot--3 { left: calc(var(--w) * 1.477); top: calc(var(--w) * 0.293); }

/* The phone, turned in depth: its right edge comes toward the viewer and its side face shows (R63). Its frame is the
   shared black titanium (R69); only its shadow is its own, sized for the stage. */
.iphone--hero { transform-origin: 50% 52.82%; transform: perspective(calc(var(--w) * 5.618)) rotateY(-14.02deg); transform-style: preserve-3d;
  box-shadow: inset 0 0 0 1px var(--ti-inner), calc(var(--w) * 0.007) calc(var(--w) * 0.006) 0 0 var(--ti-1), calc(var(--w) * 0.007) calc(var(--w) * 0.006) 0 1px var(--ink), 0 calc(var(--w) * 0.12) calc(var(--w) * 0.22) calc(var(--w) * -0.08) var(--drop); }
.iphone--hero .iphone__screen { aspect-ratio: 912 / 1902; }
.iphone--hero .iphone__btn { display: none; }
/* Two classes deep, so they win over the riding variant's rules (11a), which this phone also wears. */
.iphone.iphone--hero .iphone__island { width: calc(var(--u) * 305); height: calc(var(--u) * 90); }
.iphone.iphone--hero .iphone__status { top: calc(var(--bezel) + var(--u) * 28); padding-inline: calc(var(--u) * 76) calc(var(--u) * 14); }
.iphone.iphone--hero .iphone__glyphs { width: calc(var(--u) * 177); }
.iphone--hero .iphone__home { bottom: calc(var(--u) * 54); width: calc(var(--u) * 348); height: calc(var(--u) * 13); }
/* The side face in the frame's graphite: a lit line at its front edge (the frame's curve catching the light), dark
   toward the back, and the side button standing on it with its own lit edge. */
.iphone__side { position: absolute; left: calc(100% + var(--rim)); top: calc(var(--w) * 0.13); bottom: calc(var(--w) * 0.13); width: calc(var(--w) * 0.15); transform-origin: 0 50%; transform: rotateY(90deg); border-radius: calc(var(--w) * 0.06); background: linear-gradient(90deg, var(--ti-3) 0%, var(--ti-1) 14%, var(--ti-0) 52%, var(--ti-1) 86%, var(--ti-2) 100%); }
.iphone__side::after { content: ""; position: absolute; left: 26%; right: 26%; top: 14%; height: 12%; border-radius: var(--r-pill); background: linear-gradient(90deg, var(--ti-1), var(--ti-3) 50%, var(--ti-1)); box-shadow: 0 0 0 1px var(--ti-0); }

/* The floating cards: the app's own UI, flat in front of the turned phone. */
.float { position: absolute; z-index: 4; margin: 0; background: var(--glass); border: 1px solid var(--line); box-shadow: 0 calc(var(--w) * 0.07) calc(var(--w) * 0.15) calc(var(--w) * -0.05) var(--drop); color: var(--white); }
.float--card { left: calc(var(--w) * -0.12); top: calc(var(--w) * 0.94); width: calc(var(--w) * 0.758); height: calc(var(--w) * 0.2865); border-radius: calc(var(--w) * 0.052); transform-origin: 90% 0; }
.float__avatar { position: absolute; left: calc(var(--w) * 0.0438); top: calc(var(--w) * 0.0339); width: calc(var(--w) * 0.198); height: calc(var(--w) * 0.198); border-radius: 50%; object-fit: cover; }
.float__online { position: absolute; left: calc(var(--w) * 0.1892); top: calc(var(--w) * 0.1866); width: calc(var(--w) * 0.055); height: calc(var(--w) * 0.055); border: calc(var(--w) * 0.008) solid var(--glass); border-radius: 50%; background: var(--online); }
.float__body { position: absolute; left: calc(var(--w) * 0.289); top: calc(var(--w) * 0.0404); display: grid; justify-items: start; }
.float__name { font: 600 calc(var(--w) * 0.0556) / calc(var(--w) * 0.078) var(--font-body); white-space: nowrap; }
.float__line { font: 400 calc(var(--w) * 0.0433) / calc(var(--w) * 0.066) var(--font-body); color: var(--mist); white-space: nowrap; }
/* On a phone Request sent stands above the Benkovski label and below the locate button (0.32 W), so it covers no word
   of the map; from 600 px it takes the reference's place (below). */
.float--sent { left: calc(var(--w) * 0.62); top: calc(var(--w) * 0.32); width: calc(var(--w) * 0.586); height: calc(var(--w) * 0.156); border-radius: calc(var(--w) * 0.042); display: flex; align-items: center; gap: calc(var(--w) * 0.0446); padding: calc(var(--w) * 0.008) 0 0 calc(var(--w) * 0.0724); }
.float--sent .float__check { margin-top: calc(var(--w) * -0.008); }
.float__check { flex: none; width: calc(var(--w) * 0.086); height: calc(var(--w) * 0.086); border-radius: 50%; background: var(--blue); display: grid; place-items: center; }
.float__check svg { width: 56%; height: 56%; fill: none; stroke: var(--white); stroke-width: 3; stroke-linecap: round; stroke-linejoin: round; }
.float--sent .float__text { font: 500 calc(var(--w) * 0.047) / 1 var(--font-body); white-space: nowrap; }
.float--msg { left: calc(var(--w) * 0.55); top: calc(var(--w) * 1.30); width: calc(var(--w) * 0.706); height: calc(var(--w) * 0.2266); border-radius: calc(var(--w) * 0.052); padding: calc(var(--w) * 0.0396) calc(var(--w) * 0.065) 0; display: grid; align-content: start; justify-items: start; transform-origin: 0 0; }
/* Ivan typing (R71): a small bubble where his reply will stand, seen only on its beat; the reply grows out of its corner. */
.float--typing { left: calc(var(--w) * 0.55); top: calc(var(--w) * 1.30); width: calc(var(--w) * 0.19); height: calc(var(--w) * 0.11); border-radius: calc(var(--w) * 0.055); display: flex; align-items: center; justify-content: center; gap: calc(var(--w) * 0.017); opacity: 0; transform-origin: 0 0; }
.float--typing span { width: calc(var(--w) * 0.022); height: calc(var(--w) * 0.022); border-radius: 50%; background: var(--mist); opacity: 0.35; }
.float--msg .float__text { font: 400 calc(var(--w) * 0.0515) / calc(var(--w) * 0.08) var(--font-body); white-space: nowrap; }
.float__time { margin-top: calc(var(--w) * 0.012); font: 400 calc(var(--w) * 0.0396) / calc(var(--w) * 0.06) var(--font-body); color: var(--mist-dim); }

/* From a large phone up the cards stand where the reference puts them, around the phone; the composition centred. */
@media (min-width: 600px) {
  .hero__stage { left: calc(var(--w) * -0.143); }
  .float--card { left: calc(var(--w) * -0.245); top: calc(var(--w) * 0.936); }
  .float--sent { left: calc(var(--w) * 0.945); top: calc(var(--w) * 0.4957); }
  .float--msg, .float--typing { left: calc(var(--w) * 0.7705); top: calc(var(--w) * 1.269); }
}
@media (min-width: 900px) {
  .hero { min-height: 100svh; }
  .hero > .wrap { max-width: calc(var(--hero-inner) + var(--gutter) * 2); }
  /* Three columns at the reference's proportions: the copy, the phone's flat box (28.05% of the measure), and the room
     the cards reach into (8.54%), widened where the page's margin is too narrow to hold the Request sent card. The phone
     also fits the first viewport: its width follows the height under the bar. */
  .hero__grid { --hero-w: min(var(--hero-inner), 100vw - var(--gutter) * 2); --phone-w: min(calc(var(--hero-w) * 0.2805), calc((100svh - 138px) * 0.4918)); grid-template-columns: minmax(0, 1fr) var(--phone-w) max(calc(var(--hero-w) * 0.0854), calc(var(--phone-w) * 0.531 - (100vw - var(--hero-w)) / 2 + 12px)); gap: 0; padding-block: var(--s-5); }
  .hero__copy { grid-column: 1; padding-top: 7px; }
  .hero__stage { grid-column: 2; --phone-w: inherit; justify-self: start; left: 0; top: calc(var(--w) * -0.05); }
  .hero .eyebrow { font-size: 12.5px; letter-spacing: 0.16em; }
  /* The headline hangs its first letters' side bearing, so the ink of F and R stands on the copy's edge. */
  .hero h1 { margin: 21px 0 0 -0.05em; font-size: calc(var(--hero-w) * 0.0776); line-height: 0.957; }
  .hero__sub { margin-top: 20px; font-size: 19.25px; line-height: 1.4; }
  .hero__cta { margin-top: 31px; }
  /* The reference's button icons are bold: a 29 px download glyph and a wide play triangle. */
  .btn--lg { padding: 0 39px 0 31px; gap: 10px; }
  .btn--lg .btn__icon { width: 1.6em; height: 1.6em; }
  .btn--lg .btn__icon--solid { width: 1.5em; height: 1.5em; }
  .btn--ghost.btn--lg { padding: 0 34px 0 32px; gap: 12px; }
  .hero__soon { margin-top: 9px; font-size: 13px; }
  .chips { margin-top: 37px; }
  .chip { min-height: 41px; padding: 0 14px 0 13px; font-size: 12.3px; }
}

/* Load choreography (R47, R67, R71): the connection loop as discovery, told once, on one timeline for every width.
   The copy rises, the headline's two lines out of their masks; on a desktop the phone settles into its turn and its
   screen wakes. Then, from the moment the phone is seen (site.js adds .is-on): your position appears and sends one
   ping, and each rider appears as the ring reaches them; Ivan's pin is picked, his card arrives, Request sent draws its
   check, his typing gives way to his reply, and the page rests, about five seconds in. Nothing loops (R44). Reduced
   motion keeps every beat as opacity (section 16): nothing travels, the headline focuses in. */
@keyframes rise { from { opacity: 0; transform: translateY(var(--d-rise)); } to { opacity: 1; transform: none; } }
@keyframes fade { from { opacity: 0; } to { opacity: 1; } }
/* 118%: the line box is shorter than the letters (line-height 0.957), so the tallest marks (the i's dots) need more
   than one box height to clear the mask's foot; measured, 108% left both dots showing at the start. */
@keyframes line-up { from { transform: translateY(118%); } to { transform: none; } }
@keyframes focus-in { from { opacity: 0; filter: blur(8px); } to { opacity: 1; filter: blur(0); } }
@keyframes phone-settle {
  from { opacity: 0; transform: perspective(calc(var(--w) * 5.618)) translateY(var(--d-rise-lg)) rotateY(-24deg); }
  to { opacity: 1; transform: perspective(calc(var(--w) * 5.618)) translateY(0) rotateY(-14.02deg); }
}
@keyframes wake { from { opacity: 0; transform: scale(1.05); } to { opacity: 1; transform: none; } }
@keyframes ping { from { opacity: 0.6; transform: scale(1); } to { opacity: 0; transform: scale(11); } }
@keyframes pin-in { from { opacity: 0; transform: scale(0.4); } to { opacity: 1; transform: none; } }
@keyframes picked { from { opacity: 0; transform: scale(0.7); } to { opacity: 1; transform: none; } }
@keyframes card-in { from { opacity: 0; transform: translateY(var(--d-rise)) scale(0.97); } to { opacity: 1; transform: none; } }
@keyframes sent-in { from { opacity: 0; transform: translateX(calc(var(--d-rise) * -1)); } to { opacity: 1; transform: none; } }
@keyframes check-in { from { transform: scale(0.4); } 60% { transform: scale(1.08); } to { transform: none; } }
@keyframes draw { from { stroke-dashoffset: 1; } to { stroke-dashoffset: 0; } }
@keyframes typing-life { 0% { opacity: 0; transform: scale(0.8); } 18%, 78% { opacity: 1; transform: none; } 100% { opacity: 0; transform: scale(0.94); } }
@keyframes typing-fade { 0% { opacity: 0; } 18%, 78% { opacity: 1; } 100% { opacity: 0; } }
@keyframes dot { 0%, 100% { opacity: 0.35; transform: none; } 45% { opacity: 1; transform: translateY(-35%); } }
@keyframes dot-fade { 0%, 100% { opacity: 0.35; } 45% { opacity: 1; } }
@keyframes bubble-in { from { opacity: 0; transform: scale(0.86); } to { opacity: 1; transform: none; } }
.js .hero .eyebrow, .js .hero__sub, .js .hero__cta, .js .hero__soon, .js .chips { animation: rise var(--t-enter) var(--ease) both; }
.js .hero h1 .line__in { animation: line-up var(--t-focal) var(--ease) both; }
.js .hero h1 .line:nth-child(1) .line__in { animation-delay: var(--stagger); }
.js .hero h1 .line:nth-child(2) .line__in { animation-delay: calc(var(--stagger) * 3); }
.js .hero__sub { animation-delay: calc(var(--stagger) * 5); }
.js .hero__cta { animation-delay: calc(var(--stagger) * 6); }
.js .hero__soon { animation-delay: calc(var(--stagger) * 7); }
.js .chips { animation-delay: calc(var(--stagger) * 8); }
/* The stage's timeline, counted from .is-on: the ping, Ivan picked, his card, the request, his typing, his reply. On a
   phone the screen is lit before it is seen, so the ping comes a moment after; a desktop waits for the screen to wake. */
.js .hero__stage { --t0: var(--t-enter); --pick: calc(var(--t0) + var(--t-ping)); --card: calc(var(--pick) + var(--stagger)); --sent: calc(var(--card) + var(--t-focal)); --typing: calc(var(--sent) + var(--t-enter)); --reply: calc(var(--typing) + var(--t-type)); }
.js .hero__stage:not(.is-on) :is(.ride--hero .me, .ride--hero .pin, .float), .js .hero__stage:not(.is-on) > .hero__dot, .js .hero__stage:not(.is-on) .ride--hero .pin--h2::before { opacity: 0; }
.js .hero__stage.is-on > .hero__dot { animation: fade var(--t-focal) var(--ease-standard) var(--t-swap) both; }
/* Your position, then the ping: one thin ring from you, out past the farthest rider, fading as it goes. */
.js .hero__stage.is-on .ride--hero .me { animation: pin-in var(--t-swap) var(--ease) calc(var(--t0) - var(--t-swap)) both; }
.js .hero__stage.is-on .ride--hero .me::after { animation: ping var(--t-ping) var(--ease-ping) var(--t0) forwards; }
/* Each rider appears as the ring reaches them. The ring's reach is 330 u (11 times your 60 u dot, halved); a rider d u
   away is reached at the share x of the run where 1 - (1 - x)^2 = d / 330: Ivan at 216 u (x 0.411), the upper rider
   at 255 u (0.525), the lower one at 305 u (0.726). */
.js .hero__stage.is-on .ride--hero .pin { animation: pin-in var(--t-swap) var(--ease) both; }
.js .hero__stage.is-on .ride--hero .pin--h2 { animation-delay: calc(var(--t0) + var(--t-ping) * 0.411); }
.js .hero__stage.is-on .ride--hero .pin--h1 { animation-delay: calc(var(--t0) + var(--t-ping) * 0.525); }
.js .hero__stage.is-on .ride--hero .pin--h3 { animation-delay: calc(var(--t0) + var(--t-ping) * 0.726); }
/* Ivan picked, then his card, the request with its check drawn, his typing (two waves of the dots), his reply. */
.js .hero__stage.is-on .ride--hero .pin--h2::before { animation: picked var(--t-swap) var(--ease) var(--pick) both; }
.js .hero__stage.is-on .float--card { animation: card-in var(--t-enter) var(--ease) var(--card) both; }
.js .hero__stage.is-on .float--sent { animation: sent-in var(--t-enter) var(--ease) var(--sent) both; }
.js .hero__stage.is-on .float--sent .float__check { animation: check-in var(--t-swap) var(--ease) calc(var(--sent) + var(--stagger) * 2) both; }
.js .hero__stage.is-on .float--sent .float__check path { animation: draw var(--t-swap) var(--ease-standard) calc(var(--sent) + var(--stagger) * 4) both; }
.js .hero__stage.is-on .float--typing { animation: typing-life calc(var(--t-type) + var(--t-exit)) var(--ease-standard) var(--typing) both; }
.js .hero__stage.is-on .float--typing span { animation: dot var(--t-swap) var(--ease-standard) calc(var(--typing) + var(--t-exit)) 2 both; }
.js .hero__stage.is-on .float--typing span:nth-child(2) { animation-delay: calc(var(--typing) + var(--t-exit) + var(--stagger) * 2); }
.js .hero__stage.is-on .float--typing span:nth-child(3) { animation-delay: calc(var(--typing) + var(--t-exit) + var(--stagger) * 4); }
.js .hero__stage.is-on .float--msg { animation: bubble-in var(--t-swap) var(--ease) var(--reply) both; }
/* On a desktop the phone is the first view: it settles into its turn as the copy rises, and its screen wakes, the map
   first and the app's controls a moment later; the ping follows the lit screen. */
@media (min-width: 900px) {
  .js .iphone--hero { animation: phone-settle var(--t-focal) var(--ease) calc(var(--stagger) * 2) both; }
  .js .hero__stage { --t0: calc(var(--t-swap) + var(--t-focal)); }
  .js .hero__stage:not(.is-on) .ride--hero :is(.ride__map, .map-shield, .map-label, .map-ctl, .map-report, .map-search, .map-start) { opacity: 0; }
  .js .hero__stage.is-on .ride--hero :is(.ride__map, .map-shield, .map-label) { animation: wake var(--t-focal) var(--ease) var(--t-swap) both; }
  .js .hero__stage.is-on .ride--hero :is(.map-ctl, .map-report, .map-search, .map-start) { animation: fade var(--t-enter) var(--ease-standard) calc(var(--t-swap) + var(--t-hold)) both; }
}

/* Reveal on enter, below the fold. */
.js .reveal { opacity: 0; transform: translateY(var(--d-rise)); transition: opacity var(--t-enter) var(--ease), transform var(--t-enter) var(--ease); transition-delay: calc(var(--i, 0) * var(--stagger)); }
.js .reveal.is-in { opacity: 1; transform: none; }
.reveal.i1 { --i: 1; } .reveal.i2 { --i: 2; } .reveal.i3 { --i: 3; } .reveal.i4 { --i: 4; }

/* 9. How it works: sticky phone, stepping text */
.how__head { display: grid; gap: var(--s-4); margin-bottom: var(--s-7); max-width: 36rem; }
.how__grid { display: grid; gap: var(--s-6); }
.how__sticky { position: sticky; top: var(--nav-h); z-index: 1; display: flex; justify-content: center; }
.how__steps { list-style: none; position: relative; counter-reset: step; }
.how__steps::before, .how__steps::after { content: ""; position: absolute; left: 0; top: 0; bottom: 0; width: 2px; border-radius: var(--r-pill); }
.how__steps::before { background: var(--card-hairline); }
.how__steps::after { background: var(--teal); transform-origin: top; transform: scaleY(var(--progress, 0)); transition: transform var(--t-enter) var(--ease); }
.how__steps[data-active="1"] { --progress: 0.25; }
.how__steps[data-active="2"] { --progress: 0.5; }
.how__steps[data-active="3"] { --progress: 0.75; }
.how__steps[data-active="4"] { --progress: 1; }
.how__step { min-height: 56svh; display: grid; align-content: center; gap: var(--s-3); padding-left: var(--s-6); }
.how__num { font: 700 var(--size-small) / 1 var(--font-display); letter-spacing: 0.1em; color: var(--grey-text); transition: color var(--t-exit) var(--ease-standard); }
.how__step h3 { color: var(--grey-text); font-size: 1.75rem; transition: color var(--t-exit) var(--ease-standard); }
.how__step p { color: var(--grey-text); max-width: 26em; transition: color var(--t-exit) var(--ease-standard); }
.how__step.is-active .how__num { color: var(--teal); }
.how__step.is-active h3 { color: var(--white); }
.how__step.is-active p { color: var(--white-dim); }
.how__screen { opacity: 0; transform: scale(1.03); transition: opacity var(--t-swap) var(--ease), transform var(--t-swap) var(--ease); }
.how__screen:not(.is-active) { transition-duration: var(--t-exit); }
.how__screen.is-active { opacity: 1; transform: none; z-index: 1; }
@media (max-width: 719px) {
  /* The box stays opaque and only the phone fades into its foot (R72a): a mask on the box made its lower third
     see-through, so each step's text scrolled across the phone's screen. Now the steps pass behind it. */
  /* The whole phone stands in the box (R76): the steps' stories sit at the screens' feet (Ivan's card, the Connect
     sheet, the chat's messages), and a box that cut the phone at 46svh hid them on a real 783 px tall phone. The box
     takes 62% of the room under the bar and the phone is sized to it (its height is 2.083 of its width), top-aligned
     rather than stretched. */
  .how { --room: calc(100svh - var(--nav-h)); }
  .how__sticky { height: calc(var(--room) * 0.62); align-items: flex-start; overflow: hidden; padding-top: var(--s-3); background: var(--black); box-shadow: 0 var(--s-6) var(--s-5) var(--black); }
  /* That shadow is a soft band under the box: words fade as they pass beneath it instead of being sliced by its edge.
     The first step starts below the band, so it is read whole before it goes. */
  .how__sticky .iphone { --phone-w: min(60vw, (var(--room) * 0.62 - var(--s-3) * 2) / 2.083); }
  /* Each step fills the room under the phone, plus the next step's number. A shorter step let that number
     arrive alone and, on a 783px phone, get cut by the screen edge. */
  .how__step { min-height: calc(var(--room) * 0.38 + var(--s-5)); }
  .how__step:first-child { margin-top: var(--s-7); }
  /* The last step ends soon after its words, so the phone lets go as they pass instead of standing over empty room.
     Its number starts below the box's shadow (the shadow offset is --s-6), so "04" is not born inside the blur. */
  .how__step:last-child { min-height: 0; align-content: start; padding-block: calc(var(--s-6) + var(--s-5)) var(--s-5); }
}
/* Tablets and up: the phone sits beside the steps and stays centred in the viewport while they pass. */
@media (min-width: 720px) {
  .how__grid { grid-template-columns: 5fr 6fr; gap: var(--s-7); align-items: start; }
  .how__sticky { top: calc(var(--nav-h) + (100vh - var(--nav-h) - var(--phone-w) * 2.243) / 2); }
  .how__step { min-height: 70vh; }
  .how__step:last-child { min-height: 42vh; align-content: center; }
  .how { padding-bottom: var(--s-8); }
}
@media (min-width: 720px) and (max-width: 899px) {
  .how__sticky { --phone-w: clamp(220px, 32vw, 280px); }
}
@media (min-width: 900px) { .how__grid { gap: var(--s-9); } }
/* Short landscape phones: the sticky phone cannot fit, so the steps simply scroll past a smaller one. */
@media (min-width: 720px) and (max-height: 520px) {
  .how__sticky { position: static; }
  .how__sticky .iphone { --phone-w: 200px; }
  .how__step { min-height: 0; padding-block: var(--s-5); }
}

/* 10. Meetups (R38, R50): the riders photo as a full-bleed band cut low, the heading over its shade, the Meetups phone
   whole and standing proud of the band's bottom edge at the right, in front of the photo and never inside it; the three
   points below. Under 720 px: the band, the points, then the phone. The riders sit at the frame's centre, clear of both
   the heading (below them) and the phone (right of them) at every width. */
.meetups { position: relative; background: var(--deep-navy); padding-bottom: var(--section-pad); }
.meetups__band { position: relative; overflow: hidden; height: min(125vw, 72svh); background: var(--surface); }
.meetups__photo img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.meetups__shade { position: absolute; inset: 0; background: linear-gradient(to top, var(--shade) 0%, var(--shade-mid) 34%, var(--shade-none) 60%); }
.meetups__head { position: absolute; left: 0; right: 0; bottom: 0; padding-bottom: var(--s-6); display: grid; gap: var(--s-3); justify-items: start; }
.meetups__head h2 { max-width: 12em; }
.meetups__body { display: grid; gap: var(--s-7); padding-top: var(--s-7); }
.meetups__phone { --phone-w: clamp(200px, 60vw, 260px); display: flex; justify-content: center; }
.points { list-style: none; display: grid; gap: var(--s-4); margin-top: var(--s-2); }
.point { display: grid; grid-template-columns: auto 1fr; gap: var(--s-4); align-items: start; }
.point h3 { font-size: 1.25rem; }
.point p { color: var(--grey-text); text-wrap: pretty; }
.point__icon, .promise__icon { display: grid; place-items: center; width: 44px; height: 44px; border-radius: var(--r-md); background: var(--teal-soft); color: var(--teal); flex: none; }
.point__icon svg, .promise__icon svg { width: 24px; height: 24px; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
/* From 720 px the band is 2.4:1 (never under 420 px) and the phone stands at the content's right edge, its bottom
   64 px below the band: its top is placed from the body's top, so it needs no knowledge of the band's height. */
@media (min-width: 720px) {
  .meetups__band { height: max(420px, 41.667vw); }
  .meetups__head { padding-bottom: var(--s-7); }
  .meetups__body { position: relative; }
  .meetups .points { max-width: 30rem; }
  .meetups__phone { --phone-w: clamp(220px, 19vw, 280px); position: absolute; right: var(--gutter); top: calc(var(--s-8) - var(--phone-w) * 2.083); z-index: 2; }
}
@supports (animation-timeline: view()) {
  .js .meetups__photo img { animation: band-zoom linear both; animation-timeline: view(); animation-range: entry 0% exit 100%; }
  @keyframes band-zoom { from { transform: scale(1.1); } to { transform: scale(1); } }
}

/* 11. Made for riding (R37, R49, R51 to R59): three cards, each an icon, a title, a line, and the top of an iPhone whose
   screen is drawn in HTML and CSS to the founder's reference, cut by the card's bottom edge just under its bottom panel. */
.riding__head { display: grid; gap: var(--s-4); justify-items: center; text-align: center; margin: 0 auto var(--s-7); max-width: 60rem; }
/* The reference's faint contours behind the section's head, the hero's lines as a mask in the contour token. */
.riding { position: relative; }
.riding::before { content: ""; position: absolute; inset: 0 0 45%; background: var(--contour); -webkit-mask: url(/img/hero/contours.svg) center top / cover no-repeat; mask: url(/img/hero/contours.svg) center top / cover no-repeat; pointer-events: none; }
.riding > .wrap { position: relative; }
/* The row is wider than the page's text measure (R60): the founder's reference gives each card about 1.5 times its text
   size in width, so the titles run two lines, the lines three, and the phones stand large in their cards. */
.riding > .wrap { max-width: var(--content-wide); }
/* The row is the size container (not the cards: containment on a card would stop it sharing the row's head height). */
.cards { display: grid; gap: var(--s-4); container-type: inline-size; }
.card { position: relative; background: var(--card-fill); border: 1px solid var(--card-hairline); border-radius: var(--r-xl); overflow: hidden; display: grid; grid-template-rows: auto 1fr; }
/* Cards in one row share their head height, so the three phones stand at one level whatever their text runs to. */
@supports (grid-template-rows: subgrid) {
  .card { grid-row: span 2; grid-template-rows: subgrid; row-gap: 0; }
}
.card__head { display: grid; grid-template-columns: auto 1fr; gap: var(--s-4); align-items: start; padding: var(--s-5) var(--s-5) var(--s-4); }
.card h3 { font-size: 1.35rem; line-height: 1.18; }
.card p { color: var(--mist); text-wrap: pretty; margin-top: var(--s-2); font-size: 17px; line-height: 1.5; }
/* The card heads to their reference (R64): a larger icon square on a dark fill with a faint edge, each icon in its own
   colour (the route's blue, the alert's amber, the map's teal). */
.card__icon { display: grid; place-items: center; flex: none; width: 52px; height: 52px; border-radius: var(--r-md); background: var(--surface); border: 1px solid var(--line); }
.card__icon svg { width: 24px; height: 24px; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
.card__icon--solid svg { fill: currentColor; stroke: none; }
.card__icon--blue { color: color-mix(in srgb, var(--blue) 50%, var(--blue-light)); }
.card__icon--amber { color: var(--amber); }
.card__icon--teal { color: var(--teal); }
@media (min-width: 900px) {
  .card__head { gap: 20px; padding: 26px 16px 16px 26px; }
  .card__icon { width: 61px; height: 61px; }
  .card__icon svg { width: 27px; height: 27px; }
  /* Filled lines without a lone last word, as the reference breaks them ("Navigate on the app's / own map"),
     not balanced halves. */
  .card h3 { font-size: 1.375rem; text-wrap: pretty; }
  .card p { font-size: 16.5px; }
}
/* The phone is 78% of the card's width, as in the founder's section reference, and the card's edge cuts it 1.20 widths
   down (R53): 58% of the phone, just under the screen's bottom panel, never through it. */
.card__device { --phone-w: clamp(196px, 22vw, 330px); position: relative; height: calc(var(--phone-w) * 1.2 + var(--s-2)); padding-top: var(--s-2); display: flex; justify-content: center; align-items: flex-start; overflow: hidden; }
/* 78% of one card's width, from the row's width: one column, two (the third card spanning both keeps the same phone),
   or three with their gaps. */
@supports (width: 1cqw) {
  .card__device { --phone-w: min(78cqw, 320px); }
  @media (min-width: 720px) { .card__device { --phone-w: calc((100cqw - var(--s-4)) * 0.39); } }
  @media (min-width: 900px) { .card__device { --phone-w: calc((100cqw - var(--s-5) * 2) * 0.26); } }
}
.card__device .iphone { box-shadow: 0 30px 60px -30px var(--drop), 0 0 0 1px var(--ink); }
/* The phones rise into their cards when the row enters; the cards themselves stay put. */
.js .cards .iphone { opacity: 0; transform: translateY(var(--d-rise-lg)); transition: opacity var(--t-enter) var(--ease), transform var(--t-enter) var(--ease); }
.js .cards.is-in .iphone { opacity: 1; transform: none; }
.js .cards .card:nth-child(2) .iphone { transition-delay: var(--stagger); }
.js .cards .card:nth-child(3) .iphone { transition-delay: calc(var(--stagger) * 2); }
@media (min-width: 720px) { .cards { grid-template-columns: 1fr 1fr; } .card:last-child { grid-column: 1 / -1; } }
@media (min-width: 900px) { .cards { grid-template-columns: repeat(3, 1fr); gap: var(--s-5); } .card:last-child { grid-column: auto; } }

/* 11a. The riding screens (R51): the founder's reference drawn in HTML and CSS over the app's own night map (R54).
   Every length is in thousandths of the phone's width (--u), measured off the reference in phone widths from the screen's
   top-left corner (ledger R52). The phone variant matches the reference's bezel, island and status row (R57). */
.iphone--ride { --bezel: calc(var(--w) * 0.032); --u: calc(var(--w) * 0.001); }
/* Positioned from the phone's padding box, which already starts inside the rim: the island's top at 0.060 W from the
   phone's outer edge, the status row centred at 0.098 W, 9:41 from 0.128 W, the glyphs ending at 0.915 W. */
.iphone--ride .iphone__island { top: calc(var(--bezel) + var(--u) * 16); width: calc(var(--u) * 310); }
.iphone--ride .iphone__status { top: calc(var(--bezel) + var(--u) * 12); padding-inline: calc(var(--u) * 76) calc(var(--u) * 29); }
.iphone--ride .iphone__time { font: 600 calc(var(--u) * 39) / 1 var(--font-body); letter-spacing: 0; }
.iphone--ride .iphone__glyphs { width: calc(var(--u) * 174); }
.ride > * { position: absolute; }
.ride svg { fill: currentColor; }
.ride > .ride__map { top: 0; bottom: auto; left: 0; width: 100%; height: auto; }
.ride__route { top: 0; left: 0; width: 100%; height: auto; aspect-ratio: 412 / 521; overflow: visible; }
.ride .ride__route path { fill: none; stroke: var(--blue); stroke-width: 9.5; stroke-linejoin: round; stroke-dasharray: 1; }
.ride .ride__route polygon { fill: var(--blue); }
/* The section's one product moment (R67): as the phones stand up, the route draws in ahead of the puck, and its
   arrowhead follows. The path's length is normalised (pathLength="1"), so one dash is the whole route. */
.js .cards .ride__route path { stroke-dashoffset: 1; transition: stroke-dashoffset var(--t-focal) var(--ease-standard); }
.js .cards.is-in .ride__route path { stroke-dashoffset: 0; transition-delay: calc(var(--t-enter) + var(--stagger)); }
.js .cards .ride__route polygon { opacity: 0; transition: opacity var(--t-swap) var(--ease-standard); }
.js .cards.is-in .ride__route polygon { opacity: 1; transition-delay: calc(var(--t-enter) + var(--t-focal)); }
.ride__shade { left: 0; right: 0; top: 0; height: calc(var(--u) * 160); background: linear-gradient(var(--shade), var(--shade-none)); }
.ride .ico-line { fill: none; stroke: currentColor; stroke-linecap: round; stroke-linejoin: round; }

/* Navigation: the turn card, three round buttons, the puck on the route, the bottom panel with End. */
.nav-turn { left: calc(var(--u) * 31); right: calc(var(--u) * 33); top: calc(var(--u) * 140); height: calc(var(--u) * 259); border-radius: calc(var(--u) * 34); background: var(--deep-navy); color: var(--white); }
.nav-turn > * { position: absolute; }
.nav-turn__icon { left: calc(var(--u) * 39); top: calc(var(--u) * 22); width: calc(var(--u) * 170); height: calc(var(--u) * 170); stroke-width: 2.6; }
.nav-turn__dist { left: calc(var(--u) * 245); top: calc(var(--u) * 52); font: 600 calc(var(--u) * 66) / 1 var(--font-body); letter-spacing: -0.01em; white-space: nowrap; }
.nav-turn__street { left: calc(var(--u) * 245); top: calc(var(--u) * 125); font: 400 calc(var(--u) * 37) / calc(var(--u) * 52) var(--font-body); white-space: nowrap; }
.nav-btn { left: calc(var(--u) * 746); width: calc(var(--u) * 123); height: calc(var(--u) * 123); border-radius: 50%; background: var(--black); box-shadow: 0 calc(var(--u) * 8) calc(var(--u) * 24) var(--drop); color: var(--white); display: grid; place-items: center; }
.nav-btn svg { width: calc(var(--u) * 68); height: calc(var(--u) * 68); }
.nav-btn--1 { top: calc(var(--u) * 446); }
.nav-btn--2 { top: calc(var(--u) * 592); }
.nav-btn--3 { top: calc(var(--u) * 737); }
.nav-btn .ico-line { stroke-width: 2.3; }
.nav-puck { left: calc(var(--u) * 334); top: calc(var(--u) * 711); width: calc(var(--u) * 119); height: calc(var(--u) * 119); border-radius: 50%; border: calc(var(--u) * 7) solid var(--white); background: var(--blue); color: var(--white); display: grid; place-items: center; }
.nav-puck svg { width: calc(var(--u) * 58); height: calc(var(--u) * 58); }
.nav-panel { left: calc(var(--u) * 5); right: calc(var(--u) * 5); top: calc(var(--u) * 914); height: calc(var(--u) * 400); border-radius: calc(var(--u) * 45) calc(var(--u) * 45) 0 0; background: var(--deep-navy); color: var(--white); }
.nav-panel > * { position: absolute; }
.nav-panel__time { left: calc(var(--u) * 70); top: calc(var(--u) * 50); font: 600 calc(var(--u) * 60) / 1 var(--font-body); white-space: nowrap; }
.nav-panel__meta { left: calc(var(--u) * 70); top: calc(var(--u) * 133); font: 400 calc(var(--u) * 41) / 1 var(--font-body); color: var(--grey-text); white-space: nowrap; }
.nav-panel__dot { margin-inline: calc(var(--u) * 25); }
.nav-panel__end { left: calc(var(--u) * 618); top: calc(var(--u) * 45); width: calc(var(--u) * 239); height: calc(var(--u) * 126); border-radius: var(--r-pill); background: var(--blue); font: 600 calc(var(--u) * 47) / calc(var(--u) * 126) var(--font-body); text-align: center; }

/* Road alerts: the report sheet over the black screen, its handle, heading, helper line and the 3 x 2 tiles. */
.ride--alerts { background: var(--black); }
.alert-sheet { left: 0; right: 0; top: calc(var(--u) * 187); height: calc(var(--u) * 1200); border-radius: calc(var(--u) * 36) calc(var(--u) * 36) 0 0; background: var(--card-fill); color: var(--white); }
.alert-sheet > * { position: absolute; }
.alert-sheet__handle { left: 50%; top: calc(var(--u) * 30); width: calc(var(--u) * 98); height: calc(var(--u) * 12); margin-left: calc(var(--u) * -49); border-radius: var(--r-pill); background: var(--grey-muted); }
.alert-sheet__title { left: calc(var(--u) * 49); top: calc(var(--u) * 100); font: 600 calc(var(--u) * 54) / 1 var(--font-body); white-space: nowrap; }
.alert-sheet__help { left: calc(var(--u) * 49); top: calc(var(--u) * 177); font: 400 calc(var(--u) * 37) / calc(var(--u) * 47) var(--font-body); color: var(--grey-text); white-space: nowrap; }
.alert-tile { width: calc(var(--u) * 189); height: calc(var(--u) * 260); }
.alert-tile > * { position: absolute; }
.alert-tile__disc { left: 0; top: 0; width: 100%; height: calc(var(--u) * 189); border-radius: 50%; display: grid; place-items: center; }
.alert-tile__disc svg { width: calc(var(--u) * 92); height: calc(var(--u) * 92); }
.alert-tile__label { left: calc(var(--u) * -40); right: calc(var(--u) * -40); top: calc(var(--u) * 215); font: 400 calc(var(--u) * 35) / 1 var(--font-body); text-align: center; white-space: nowrap; }
.alert-tile--c1 { left: calc(var(--u) * 83); }
.alert-tile--c2 { left: calc(var(--u) * 360); }
.alert-tile--c3 { left: calc(var(--u) * 637); }
.alert-tile--r1 { top: calc(var(--u) * 328); }
.alert-tile--r2 { top: calc(var(--u) * 620); }
.alert-tile--amber .alert-tile__disc { background: var(--amber-soft); color: var(--amber); }
.alert-tile--danger .alert-tile__disc { background: var(--danger-soft); color: var(--danger); }
.alert-tile--brand .alert-tile__disc { background: var(--blue-soft); color: var(--blue); }
.alert-tile--plain .alert-tile__disc { background: var(--surface-alt); color: var(--white); }
.alert-tile .ico-line { stroke-width: 2; }
.ride .ico-cut { fill: var(--white); }

/* The map: four controls, five riders as photo pins, one road alert, your position. */
.map-ctl { width: calc(var(--u) * 105); height: calc(var(--u) * 105); border-radius: calc(var(--u) * 23); background: var(--white); color: var(--black); box-shadow: 0 calc(var(--u) * 6) calc(var(--u) * 18) var(--drop); display: grid; place-items: center; }
.map-ctl svg { width: calc(var(--u) * 64); height: calc(var(--u) * 64); }
/* The reference's place names and its A6 shield, drawn over the map image (which carries no labels, R54). */
.map-label { width: calc(var(--u) * 400); margin-left: calc(var(--u) * -200); font: 500 calc(var(--u) * 28) / calc(var(--u) * 38) var(--font-body); letter-spacing: 0.05em; text-align: center; text-transform: uppercase; color: var(--white-dim); text-shadow: 0 0 calc(var(--u) * 6) var(--black), 0 0 calc(var(--u) * 3) var(--black); white-space: nowrap; }
.map-label--1 { left: calc(var(--u) * 414); top: calc(var(--u) * 415); }
.map-label--2 { left: calc(var(--u) * 767); top: calc(var(--u) * 627); }
.map-label--3 { left: calc(var(--u) * 158); top: calc(var(--u) * 884); }
.map-shield { left: calc(var(--u) * 229); top: calc(var(--u) * 213); width: calc(var(--u) * 51); height: calc(var(--u) * 36); border-radius: calc(var(--u) * 6); background: var(--blue); color: var(--white); font: 600 calc(var(--u) * 23) / calc(var(--u) * 36) var(--font-body); text-align: center; }
.map-ctl .ico-line { stroke-width: 2.2; }
.map-ctl--dark { background: var(--black); color: var(--white); box-shadow: 0 0 0 1px var(--hairline), 0 calc(var(--u) * 6) calc(var(--u) * 18) var(--drop); }
.map-ctl--l { left: calc(var(--u) * 34); }
.map-ctl--r { left: calc(var(--u) * 768); }
.map-ctl--t1 { top: calc(var(--u) * 170); }
.map-ctl--t2 { top: calc(var(--u) * 310); }
.pin { --d: calc(var(--u) * 100); width: var(--d); height: var(--d); margin: calc(var(--d) / -2) 0 0 calc(var(--d) / -2); border-radius: 50%; border: calc(var(--u) * 7) solid var(--blue); background: var(--surface-high); }
.pin img { width: 100%; height: 100%; border-radius: 50%; object-fit: cover; }
.pin::after { content: ""; position: absolute; left: 50%; top: calc(100% + var(--u) * 4); margin-left: calc(var(--u) * -12); border: calc(var(--u) * 12) solid transparent; border-bottom: 0; border-top: calc(var(--u) * 22) solid var(--blue); }
.pin--1 { left: calc(var(--u) * 617); top: calc(var(--u) * 571); }
.pin--2 { left: calc(var(--u) * 296); top: calc(var(--u) * 650); }
.pin--3 { left: calc(var(--u) * 206); top: calc(var(--u) * 768); }
.pin--4 { left: calc(var(--u) * 773); top: calc(var(--u) * 820); --d: calc(var(--u) * 112); }
.pin--5 { left: calc(var(--u) * 527); top: calc(var(--u) * 939); }
.map-alert { --d: calc(var(--u) * 66); left: calc(var(--u) * 515); top: calc(var(--u) * 744); width: var(--d); height: var(--d); margin: calc(var(--d) / -2) 0 0 calc(var(--d) / -2); border-radius: 50%; background: var(--danger); color: var(--white); display: grid; place-items: center; }
.map-alert svg { width: calc(var(--u) * 38); height: calc(var(--u) * 38); }
.map-alert::after { content: ""; position: absolute; left: 50%; top: calc(100% - var(--u) * 3); margin-left: calc(var(--u) * -9); border: calc(var(--u) * 9) solid transparent; border-bottom: 0; border-top: calc(var(--u) * 14) solid var(--danger); }
.map-alert .ico-cut { fill: var(--danger); }
.me { --d: calc(var(--u) * 174); left: calc(var(--u) * 364); top: calc(var(--u) * 855); width: var(--d); height: var(--d); margin: calc(var(--d) / -2) 0 0 calc(var(--d) / -2); border-radius: 50%; background: var(--blue-border); }
.me::before, .me::after { content: ""; position: absolute; left: 50%; top: 50%; }
.me::before { width: calc(var(--u) * 80); height: calc(var(--u) * 106); margin: calc(var(--u) * -50) 0 0 calc(var(--u) * -40); border-radius: 46% 46% 50% 50% / 40% 40% 60% 60%; background: var(--blue); opacity: 0.55; }
.me::after { width: calc(var(--u) * 52); height: calc(var(--u) * 52); margin: calc(var(--u) * -26) 0 0 calc(var(--u) * -26); border-radius: 50%; border: calc(var(--u) * 6) solid var(--white); background: var(--blue); }

/* 11b. The hero's screen (R63): the founder's hero reference drawn on the riding screens' system (11a), over the app's
   own night map of Sofia (hero-map-capture.js: zoom 11.78 about 42.7112, 23.3190, no labels). Lengths in thousandths of
   the phone's width from the screen's top-left corner, read off the reference unwarped to a flat phone
   (hero-ref-phone.cjs). Its labels are partly garbled: the three it gets right stand where it puts them, within 31 u of
   the map's own, and a fourth real one, Kriva reka, stands where the map has it. */
.ride--hero { background: var(--ink); }
/* The reference darkens the map under the status bar to a near-black navy, not to black (R70): so the bezel and the
   island, which are black, read against it as they do on a real screen, and the map stays faint behind 9:41. */
.ride--hero .ride__shade { height: calc(var(--u) * 190); background: linear-gradient(var(--status-shade) 45%, var(--shade-none)); }
.ride__fade { left: 0; right: 0; top: calc(var(--u) * 1290); height: calc(var(--u) * 290); background: linear-gradient(var(--shade-none), var(--ink)); }
.ride--hero .map-shield { left: calc(var(--u) * 192); top: calc(var(--u) * 134); width: calc(var(--u) * 52); height: calc(var(--u) * 27); border-radius: calc(var(--u) * 5); background: var(--white-dim); color: var(--ink); font: 600 calc(var(--u) * 20) / calc(var(--u) * 27) var(--font-body); }
.map-label--h1 { left: calc(var(--u) * 424); top: calc(var(--u) * 324); }
.map-label--h2 { left: calc(var(--u) * 756); top: calc(var(--u) * 504); }
.map-label--h3 { left: calc(var(--u) * 176); top: calc(var(--u) * 707); }
.map-label--h4 { left: calc(var(--u) * 302); top: calc(var(--u) * 1274); }
.ride--hero .map-ctl { width: calc(var(--u) * 103); height: calc(var(--u) * 96); border-radius: calc(var(--u) * 22); }
.map-ctl--h1 { left: calc(var(--u) * 36); top: calc(var(--u) * 163); }
.map-ctl--h2 { left: calc(var(--u) * 36); top: calc(var(--u) * 277); }
.ride--hero .map-ctl--h3 { left: calc(var(--u) * 779); top: calc(var(--u) * 161); height: calc(var(--u) * 101); }
/* The reference's pins: a photo in a blue ring with a blue dot under it, not the riding map's tail. */
.ride--hero .pin { --d: calc(var(--u) * 93); }
.ride--hero .pin::after { top: 50%; width: calc(var(--u) * 15); height: calc(var(--u) * 15); margin: calc(var(--u) * 50.5) 0 0 calc(var(--u) * -7.5); border: 0; border-radius: 50%; background: var(--blue); }
.pin--h1 { left: calc(var(--u) * 336); top: calc(var(--u) * 631); }
.pin--h2 { left: calc(var(--u) * 574); top: calc(var(--u) * 772); }
.pin--h3 { left: calc(var(--u) * 691); top: calc(var(--u) * 918); }
.ride--hero .me { --d: calc(var(--u) * 60); left: calc(var(--u) * 388); top: calc(var(--u) * 881); background: transparent; border: calc(var(--u) * 10) solid var(--blue); }
.ride--hero .me::before { display: none; }
/* The ping's ring (R71): thin, on your position, unseen at rest; section 8 sends it out once. */
.ride--hero .me::after { width: var(--d); height: var(--d); margin: calc(var(--d) / -2) 0 0 calc(var(--d) / -2); border: calc(var(--u) * 2.5) solid var(--blue-light); background: none; opacity: 0; }
/* Ivan's pin, picked (R70, R71): a white ring around his photo and a soft band of the blue, as a map marks the rider
   whose card is open, so the card beside the phone belongs to a rider on it. */
.ride--hero .pin--h2::before { content: ""; position: absolute; inset: calc(var(--u) * -13); border-radius: 50%; border: calc(var(--u) * 5) solid var(--white); box-shadow: 0 0 0 calc(var(--u) * 9) var(--blue-soft); pointer-events: none; }
.map-report { left: calc(var(--u) * 786); top: calc(var(--u) * 1041); width: calc(var(--u) * 118); height: calc(var(--u) * 128); border-radius: calc(var(--u) * 22); background: var(--white); color: var(--ink); box-shadow: 0 calc(var(--u) * 6) calc(var(--u) * 18) var(--drop); display: grid; justify-items: center; align-content: center; gap: calc(var(--u) * 8); font: 500 calc(var(--u) * 22) / 1 var(--font-body); }
.map-report svg { width: calc(var(--u) * 48); height: calc(var(--u) * 48); color: var(--amber); }
.ride .map-report .ico-cut { fill: var(--ink); }
.map-search { left: calc(var(--u) * 36); top: calc(var(--u) * 1519); width: calc(var(--u) * 845); height: calc(var(--u) * 127); border-radius: calc(var(--u) * 28); background: var(--field); border: 1px solid var(--line); color: var(--mist-dim); display: flex; align-items: center; gap: calc(var(--u) * 40); padding: calc(var(--u) * 26) 0 0 calc(var(--u) * 40); font: 400 calc(var(--u) * 38) / 1 var(--font-body); }
.map-search svg { width: calc(var(--u) * 50); height: calc(var(--u) * 50); color: var(--white-dim); stroke-width: 2.2; }
.map-start { left: calc(var(--u) * 36); top: calc(var(--u) * 1679); width: calc(var(--u) * 845); height: calc(var(--u) * 127); border-radius: calc(var(--u) * 28); background: var(--blue); color: var(--white); display: flex; align-items: center; justify-content: center; gap: calc(var(--u) * 42); padding-top: calc(var(--u) * 16); font: 500 calc(var(--u) * 40) / 1 var(--font-body); }
.map-start svg { width: calc(var(--u) * 46); height: calc(var(--u) * 46); }

/* 12. You're in control: four promises at a glance, the screen beside them */
.control__grid { display: grid; gap: var(--s-7); align-items: center; }
.control__copy { display: grid; gap: var(--s-4); align-content: start; }
.control__head { display: grid; gap: var(--s-4); }
.promises { list-style: none; display: grid; gap: var(--s-3); margin-top: var(--s-3); }
/* The promises are statements, not controls: they do not move or light up under the pointer (R43). They are the
   page's one staggered list, each one stagger after the last as they enter. */
.promise { position: relative; display: grid; gap: var(--s-3); padding: var(--s-5); background: var(--card-fill); border: 1px solid var(--card-hairline); border-radius: var(--r-xl); }
.promise h3 { font-size: 1.2rem; }
.promise p { color: var(--grey-text); text-wrap: pretty; font-size: var(--size-body); }
.control__visual { display: flex; justify-content: center; }
/* A switch flips fast: the two states cross in a hover's time, so the rows that move under it barely double. */
.js .moment--switch .moment__frame--end { transition-duration: var(--t-hover); }
@media (min-width: 600px) { .promises { grid-template-columns: 1fr 1fr; gap: var(--s-4); } }
@media (min-width: 900px) {
  .control__grid { grid-template-columns: 7fr 4fr; gap: var(--s-9); }
  .control__visual { justify-content: end; }
}

/* 13. Download */
.download { position: relative; overflow: hidden; background: var(--deep-navy); }
.download__bg { position: absolute; inset: 0; }
.download__bg img { width: 100%; height: 100%; object-fit: cover; object-position: center 62%; opacity: 0.55; }
.download__shade { position: absolute; inset: 0; background: linear-gradient(90deg, var(--shade) 0%, var(--shade) 50%, var(--shade-mid) 100%); }
.download__inner { position: relative; display: grid; gap: var(--s-7); align-items: center; }
.download__copy { display: grid; gap: var(--s-4); justify-items: start; }
.stores { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-3); margin-top: var(--s-2); }
.store { --badge-h: 48px; display: inline-flex; align-items: center; height: var(--badge-h); }
.store img { height: var(--badge-h); width: auto; }
/* Google's PNG carries a transparent margin of 41 px around its 564 x 168 badge on a 646 x 250 canvas (R72b): drawn at
   one height with Apple's, its badge read a third smaller. The picture is scaled so the badge itself is the shared
   height, and the margin is cancelled so the two sit on one line with one gap. */
.store--play img { height: calc(var(--badge-h) * 250 / 168); margin: calc(var(--badge-h) * -41 / 168); }
/* Until the store pages exist the badges are pictures, not links: nothing on the page is a dead control. */
.stores[data-stores="soon"] .store { opacity: 0.92; }
.download__legal { color: var(--grey-text); font-size: var(--size-small); }
.download__legal a { display: inline-flex; align-items: center; min-height: var(--tap); color: var(--grey-text); text-decoration: underline; text-underline-offset: 0.2em; }
.download__visual { position: relative; display: flex; justify-content: center; align-items: flex-end; gap: 0; min-height: calc(var(--phone-w) * 1.2); }
.download__phone { --phone-w: clamp(150px, 36vw, 230px); }
.download__phone--back { transform: translateX(14%) rotate(-7deg); }
.download__phone--front { transform: translateX(-14%) translateY(10%) rotate(7deg); }
/* The two phones settle into their fan once, as the section enters: from 3 degrees further out and lower. */
.js .settle .download__phone { transition: transform var(--t-focal) var(--ease), opacity var(--t-enter) var(--ease); }
.js .settle .download__phone--front { transition-delay: var(--stagger); }
.js .settle:not(.is-in) .download__phone { opacity: 0; }
.js .settle:not(.is-in) .download__phone--back { transform: translateX(14%) translateY(var(--d-rise-lg)) rotate(-10deg); }
.js .settle:not(.is-in) .download__phone--front { transform: translateX(-14%) translateY(calc(10% + var(--d-rise-lg))) rotate(10deg); }
@media (min-width: 900px) {
  .download__inner { grid-template-columns: 6fr 5fr; gap: var(--s-9); }
  .download__phone { --phone-w: clamp(180px, 16vw, 230px); }
  .download__visual { min-height: 0; padding-top: var(--s-6); }
}

/* 14. Footer */
.footer { border-top: 1px solid var(--card-hairline); background: var(--deep-navy); padding-block: var(--s-8) calc(var(--s-7) + env(safe-area-inset-bottom)); color: var(--grey-text); font-size: var(--size-small); }
.footer__grid { display: grid; gap: var(--s-7) var(--s-6); margin-bottom: var(--s-7); }
.footer__brand { display: grid; gap: var(--s-4); justify-items: start; max-width: 22em; }
.footer__brand p { color: var(--grey-text); text-wrap: pretty; }
.footer__col { display: grid; align-content: start; }
.footer__title { font: 500 var(--size-caption) / 1 var(--font-body); letter-spacing: 0.08em; text-transform: uppercase; color: var(--grey-text); margin-bottom: var(--s-2); }
.footer__col ul { list-style: none; display: grid; }
.footer__col a { display: inline-flex; align-items: center; min-height: var(--tap); color: var(--white-dim); transition: color var(--t-hover) var(--ease-standard); }
.footer__small { display: grid; gap: var(--s-1); padding-top: var(--s-5); border-top: 1px solid var(--card-hairline); color: var(--grey-text); font-size: var(--size-caption); }
@media (min-width: 720px) {
  .footer__grid { grid-template-columns: 1.6fr 1fr 1fr 1fr; align-items: start; }
  .footer__small { grid-template-columns: 1fr auto; }
}
@media (hover: hover) and (pointer: fine) {
  .footer__col a:hover { color: var(--white); }
}

/* 15. Policy pages share the shell */
strong, b { font-weight: 500; color: var(--white-dim); }
.policy main { max-width: 720px; margin-inline: auto; padding: var(--s-6) var(--gutter) var(--s-9); }
.policy main h1 { font-size: clamp(2rem, 1rem + 3vw, 3rem); margin-bottom: var(--s-3); }
.policy main h2 { font-size: 1.35rem; margin-top: var(--s-7); margin-bottom: var(--s-3); }
.policy main p { color: var(--grey-text); margin-bottom: 1em; text-wrap: pretty; }
.policy main ul { padding-left: 1.2em; margin-bottom: 1em; }
.policy main li { color: var(--grey-text); margin-bottom: 0.5em; text-wrap: pretty; }
.policy main .updated { color: var(--grey-text); font-size: var(--size-small); margin-bottom: var(--s-6); }
.policy main .brand { color: var(--blue-light); font-weight: 600; }
.policy main em { color: var(--white); font-style: normal; }

/* 16. Reduced motion (R65, R67): nothing moves in space and nothing autoplays, but the product moments still happen,
   as opacity crossfades on the same beats: the hero's riders and cards, the riding phones and their route, the
   safety switch, the stepper's screens, the download phones. Text shows at once. Colour changes stay (the bar, links,
   buttons), because they carry state rather than movement. */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  /* The hero (R71): the copy fades in on its stagger and the headline focuses in; the phone fades into its turn; the
     screen wakes, the riders appear on their beats and the cards cross in, all as opacity. No ping (a ring that
     travels), no drawn check, and his typing dots pulse in place. */
  .js .hero .eyebrow, .js .hero__sub, .js .hero__cta, .js .hero__soon, .js .chips { animation-name: fade; }
  .js .hero h1 .line__in { animation-name: focus-in; }
  .js .hero__stage.is-on .ride--hero .me, .js .hero__stage.is-on .ride--hero .pin, .js .hero__stage.is-on .ride--hero .pin--h2::before,
  .js .hero__stage.is-on .float, .js .hero__stage.is-on .ride--hero :is(.ride__map, .map-shield, .map-label) { animation-name: fade; }
  .js .hero__stage.is-on .ride--hero .me::after, .js .hero__stage.is-on .float--sent .float__check, .js .hero__stage.is-on .float--sent .float__check path { animation: none; }
  .js .hero__stage.is-on .float--typing { animation-name: typing-fade; }
  .js .hero__stage.is-on .float--typing span { animation-name: dot-fade; }
  .js .iphone--hero { animation-name: fade; }
  .js .reveal { opacity: 1; transform: none; transition: none; }
  .how__steps::after, .nav__link::after { transition: none; }
  .nav__links::after { transition: opacity var(--t-hover) var(--ease-standard); }
  .how__screen { transform: none; transition-property: opacity; }
  .btn:active, .phone__pause:active { transform: none; }
  .js .meetups__photo img { animation: none; }
  .js .cards .iphone { transform: none; transition: opacity var(--t-enter) var(--ease-standard); }
  .js .cards .ride__route path { stroke-dashoffset: 0; opacity: 0; transition: opacity var(--t-swap) var(--ease-standard); }
  .js .cards.is-in .ride__route path { opacity: 1; }
  .js .settle .download__phone { transition: opacity var(--t-enter) var(--ease-standard); }
  .js .settle:not(.is-in) .download__phone--back { transform: translateX(14%) rotate(-7deg); }
  .js .settle:not(.is-in) .download__phone--front { transform: translateX(-14%) translateY(10%) rotate(7deg); }
}
