/* ============================================================
   ONVAS — landing page
   Sections: hero · how it works (pinned, 4 stages) · getting started ·
             partners · for personal events · faq · beta · footer
   ============================================================ */

/* ---------- TOKENS ---------- */
:root{
  /* Every step is Derrick's brand blue #23366C (H224.4 S51%) held at a
     different lightness, so the whole navy family descends from one colour.
     --navy IS the brand value; the rest are its shades.
     --navy-950 is the dark ink on yellow buttons and pins — it reads 10.8:1
     on #ebd22a, so it can move but not much further up. */
  --navy-950:#131e3c;
  --navy-900:#18264b;
  --navy:#23366c;
  --navy-700:#2a4181;
  --navy-600:#324d9a;
  --navy-500:#3e60c1;

  --ink:#101527;
  --ink-soft:#4b5372;
  /* nudged darker (was #767d95) — at 13px the footer's legal line only made
     4.09:1 on white, under the 4.5 AA floor. Same hue, four points of
     lightness, now 4.62:1. */
  --ink-mute:#6d748d;

  --paper:#f8f9fd;
  --paper-dim:#eef1fa;
  --white:#fff;
  --line:#e2e6f2;

  --yellow:#ebd22a;
  --yellow-deep:#d4bc1e;
  --route:#e8352a;

  /* Type scale. Four steps, 2px apart at the bottom because at these sizes a
     half-pixel difference is below the threshold where anyone can name it and
     just reads as sloppiness. Display type (h1, section h2) keeps its own
     clamped sizes and is deliberately outside this scale.
       micro  meta text nobody reads in flow: captions, legal, eyebrows
       sm     supporting copy under a heading, dense UI, small buttons
       body   anything actually read in sentences, plus controls
       lede   section-opening paragraphs */
  --fs-micro:12px;
  --fs-sm:14px;
  --fs-body:16px;
  --fs-lede:18px;

  --radius-lg:26px;
  --radius-md:16px;
  --radius-sm:10px;

  --maxw:1240px;
  --gut:32px;
  /* header padding-top + nav-row padding + logo height — everything that
     needs to clear the fixed nav keys off this */
  --nav-h:106px;

  --shadow-soft:0 20px 60px -20px rgba(16,21,39,.28);

  /* driven by JS during the How It Works scroll */
  --map-scale:1.14;
  --veil-a:.9;
}

*,*::before,*::after{box-sizing:border-box;}
html{scroll-behavior:smooth;}
html.modal-open{overflow:hidden;}
body{
  margin:0;
  font-family:'DM Sans',system-ui,-apple-system,sans-serif;
  color:var(--ink);
  background:var(--paper);
  -webkit-font-smoothing:antialiased;
  overflow-x:hidden;
}
h1,h2,h3,h4{font-family:'Alexandria',system-ui,sans-serif;margin:0;line-height:1.08;letter-spacing:-.02em;}
p{margin:0;}
a{color:inherit;text-decoration:none;}
img{max-width:100%;display:block;}
ul,ol{margin:0;padding:0;list-style:none;}

.wrap{max-width:var(--maxw);margin:0 auto;padding:0 var(--gut);width:100%;}
section[id]{scroll-margin-top:calc(var(--nav-h) + 16px);}
.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0;}
/* Honeypot. Moved off-screen rather than display:none, because a bot that
   parses CSS skips a hidden input but happily fills a visible one it cannot
   see. aria-hidden + tabindex="-1" on the markup keep it away from real
   people and screen readers. */
.hp{position:absolute;left:-9999px;width:1px;height:1px;overflow:hidden;}
.skip-link{position:absolute;left:-999px;top:0;z-index:200;background:var(--yellow);color:var(--navy);padding:12px 20px;border-radius:0 0 10px 0;font-weight:700;}
.skip-link:focus{left:0;}
:focus-visible{outline:3px solid var(--yellow);outline-offset:3px;border-radius:6px;}

.hl{color:var(--yellow);}
.arw{display:inline-block;transition:transform .2s ease;}
.btn:hover .arw{transform:translateX(3px);}

.eyebrow{
  display:inline-block;
  font-weight:700;font-size:var(--fs-micro);letter-spacing:.16em;text-transform:uppercase;
}
.tag-navy{color:var(--navy);}
.tag-white{color:var(--white);}
.tag-yellow{color:var(--yellow);}

/* ---------- BUTTONS ---------- */
.btn{
  display:inline-flex;align-items:center;justify-content:center;gap:9px;
  padding:14px 26px;border-radius:999px;font-weight:700;font-size:var(--fs-body);
  font-family:'DM Sans',sans-serif;border:1.5px solid transparent;cursor:pointer;
  transition:transform .18s ease, box-shadow .18s ease, background .18s ease, border-color .18s ease;
  white-space:nowrap;
}
.btn:hover{transform:translateY(-2px);}
.btn-sm{padding:11px 20px;font-size:var(--fs-sm);}
.btn-lg{padding:16px 30px;font-size:var(--fs-body);}
.btn-yellow{background:var(--yellow);color:var(--navy-950);box-shadow:0 10px 24px -12px rgba(212,188,30,.8);}
.btn-yellow:hover{background:#f2dc45;box-shadow:0 16px 30px -12px rgba(212,188,30,.9);}
.btn-ghost{background:rgba(255,255,255,.06);color:var(--white);border-color:rgba(255,255,255,.55);}
.btn-ghost:hover{background:rgba(255,255,255,.16);}
/* .btn-ghost inverted, for the secondary action on a light interior page.
   The hero's ghost button only works on navy. */
.btn-outline{background:transparent;color:var(--navy);border-color:rgba(35,54,108,.38);}
.btn-outline:hover{background:rgba(35,54,108,.06);border-color:var(--navy);}

/* ============================================================
   HEADER
   ============================================================ */
header.site{position:fixed;top:0;left:0;right:0;z-index:80;padding:18px var(--gut) 0;}
.nav-shell{
  max-width:var(--maxw);margin:0 auto;
  background:rgba(255,255,255,.92);
  backdrop-filter:blur(18px) saturate(160%);
  -webkit-backdrop-filter:blur(18px) saturate(160%);
  border:1px solid rgba(16,21,39,.06);
  border-radius:999px;
  box-shadow:0 12px 34px -18px rgba(15,23,48,.4);
  transition:box-shadow .25s ease, background .25s ease;
}
body.scrolled .nav-shell{box-shadow:0 16px 40px -18px rgba(15,23,48,.5);}
/* right padding is set so the gap from the CTA to the pill edge matches the
   gap above and below it — recheck it if the logo height changes */
.nav-row{display:flex;align-items:center;justify-content:space-between;gap:18px;padding:8px 23px 8px 24px;}
.brand img{height:70px;width:auto;}
.nav-links{display:flex;align-items:center;gap:30px;}
/* The mobile CTA is a <button> (it opens a dialog, it does not navigate),
   so it needs the type styling the anchors get plus a reset for the chrome
   a button brings with it. */
.nav-links a,.nav-cta-mobile{color:var(--ink-soft);font-weight:600;font-size:var(--fs-sm);white-space:nowrap;transition:color .15s ease;}
.nav-cta-mobile{font-family:inherit;border:0;cursor:pointer;width:100%;}
.nav-links a:hover,.nav-links a.active{color:var(--navy);}
.nav-actions{display:flex;align-items:center;gap:10px;}
.nav-cta-mobile{display:none;}

.nav-toggle{display:none;background:none;border:0;width:42px;height:42px;padding:9px;cursor:pointer;}
.nav-toggle span{display:block;height:2px;background:var(--navy);border-radius:2px;margin:5px 0;transition:transform .25s ease,opacity .2s ease;}
.nav-toggle[aria-expanded="true"] span:nth-child(1){transform:translateY(7px) rotate(45deg);}
.nav-toggle[aria-expanded="true"] span:nth-child(2){opacity:0;}
.nav-toggle[aria-expanded="true"] span:nth-child(3){transform:translateY(-7px) rotate(-45deg);}

/* ============================================================
   HERO — full screen on any device
   ============================================================ */
.hero{
  position:relative;
  min-height:100vh;
  min-height:100svh;
  min-height:100dvh;
  /* stretch, not center: the device column bottom-aligns against the hero's
     content box, and a centred .wrap leaves slack underneath that the device's
     negative margin then cannot reach across. */
  display:flex;align-items:stretch;
  --hero-pb:90px;
  padding:calc(var(--nav-h) + 40px) 0 var(--hero-pb);
  overflow:hidden;
  color:var(--white);
  isolation:isolate;
}
/* Hero backdrop, three layers back to front:
     .hero-fallback  brand navy, the ground everything sits on
     .hero-video     the bokeh clip, screened over that navy
     .hero-scrim     navy gradients that push it into the far background
   isolation:isolate keeps the video's blend mode inside this box so it
   composites against the navy below it and nothing further back. */
.hero-media{position:absolute;inset:0;z-index:-1;isolation:isolate;}

/* Shows before the video decodes, and stays visible through it — the navy
   in the comp is the brand blue, not the video's own colour. */
.hero-fallback{
  position:absolute;inset:0;
  background:
    radial-gradient(1100px 640px at 74% 18%, #2e468d 0%, transparent 62%),
    linear-gradient(160deg,#1f2f5e 0%, var(--navy) 46%, #1a274f 100%);
}

/* screen, not plain opacity: the clip's dark half drops out entirely and only
   the bokeh highlights survive, so the lights read as glowing out of the navy
   rather than as a grey video sitting on top of it. */
.hero-video{
  position:absolute;inset:0;width:100%;height:100%;object-fit:cover;
  mix-blend-mode:screen;
  opacity:.62;
}

/* Brand navy (#23366C = 35,54,108) carried across the whole scrim. Heaviest
   at the left where the headline sits, lightest at the right so the bokeh
   still has somewhere to breathe behind the phone. */
.hero-scrim{
  position:absolute;inset:0;
  background:
    linear-gradient(90deg, rgba(35,54,108,.86) 0%, rgba(35,54,108,.68) 42%, rgba(35,54,108,.28) 100%),
    linear-gradient(180deg, rgba(35,54,108,.62) 0%, rgba(35,54,108,.10) 32%, rgba(35,54,108,.55) 100%);
}
.hero-inner{display:grid;grid-template-columns:minmax(0,1.05fr) minmax(0,.95fr);gap:48px;align-items:center;}
.hero-copy{max-width:660px;}
/* Status pill. Deliberately no Google Play logo — naming it in text is fine,
   redrawing their mark is not. The dot reads as a live-status light instead.
   Base is the on-navy treatment; .badge-light is the same pill for a white
   panel. One rule so the two instances can't drift apart. */
.badge{
  display:inline-flex;align-items:center;gap:8px;
  padding:6px 15px 6px 12px;
  border-radius:999px;
  font-size:var(--fs-micro);font-weight:600;line-height:1.35;
  background:rgba(255,255,255,.10);
  backdrop-filter:blur(6px);
  -webkit-backdrop-filter:blur(6px);
  color:rgba(255,255,255,.94);
}
.badge-dot{flex:none;width:7px;height:7px;border-radius:50%;background:var(--yellow);}
.badge-light{
  background:rgba(35,54,108,.07);
  backdrop-filter:none;-webkit-backdrop-filter:none;
  color:var(--ink-soft);
}
/* the bright yellow disappears on a near-white pill; the deep tone holds */
.badge-light .badge-dot{background:var(--yellow-deep);}
.hero-copy .badge{margin-bottom:20px;}
.hero h1{font-size:clamp(42px,5.2vw,72px);font-weight:800;letter-spacing:-.03em;}
.hero .lede{margin-top:22px;font-size:clamp(var(--fs-body),1.5vw,var(--fs-lede));line-height:1.6;color:rgba(255,255,255,.86);max-width:30em;}
.hero-cta-row{display:flex;flex-wrap:wrap;gap:12px;margin-top:32px;}
.beta-cta{margin-top:28px;display:flex;flex-direction:column;align-items:flex-start;gap:16px;}

.hero-split{display:grid;grid-template-columns:1fr 1fr;gap:22px;margin-top:46px;max-width:560px;}
.hero-split-col{border-top:1px solid rgba(255,255,255,.28);padding-top:14px;}
.hero-split-col h2{font-size:var(--fs-sm);font-family:'DM Sans',sans-serif;font-weight:700;letter-spacing:.06em;color:var(--white);text-transform:none;}
.hero-split-col p{margin-top:4px;font-size:var(--fs-micro);line-height:1;color:rgba(255,255,255,.72);}

/* ---------- HERO DEVICE ------------------------------------------------
   Layer stack, back to front:
     .hero-media (video + scrim, z-index -1)
       -> .hero-screen  (the live map, z-index 1)
       -> .hero-hand    (the photograph, z-index 2)
       -> .hero-copy    (z-index 2 in its own column)

   The photograph's screen is a hole in its alpha channel, so the map is not
   masked into the picture — it sits underneath it, which is what keeps the
   fingers crossing the glass. Every value below was measured off the PNG with
   numpy rather than eyeballed; tools/measure-hole.py reprints them if the
   photograph is ever re-exported.

   The hole values carry a 12px bleed on each side. The bleed hides any
   sub-pixel seam along the glass edge, and it stops at 12 because the phone is
   shot at a slight angle: its silhouette narrows toward the top corners, and a
   larger square overlay pokes out past the bezel up there. --hero-hole-r is
   the same guard from the other side — deliberately tighter than the glass's
   own corner radius, enough to stay inside the silhouette, not enough to clip
   the screen. */
.hero-copy{position:relative;z-index:2;}

/* Exists only to be the container .hero-device measures its own height
   against. The device cannot query itself, and the cap has to come from the
   column's width. */
.hero-stage{
  display:flex;justify-content:center;align-items:flex-end;
  align-self:end;
  container-type:inline-size;
}

.hero-device{
  position:relative;z-index:1;
  /* Height drives the size. The photo is 900x1148, so its natural width at
     height H is H x 0.7843 and 127.5cqw is the tallest H whose width still
     fits this column — without that cap the WIDTH gets clamped by the global
     img{max-width:100%} instead and the phone renders squeezed.
     The dvh term keeps the hero exactly one screen tall: the device hangs
     --hero-pb past the content box, so that padding is its headroom. */
  height:min(calc(100dvh - var(--nav-h) - 40px), 127.5cqw);
  width:auto;
  /* a negative MARGIN, never a transform: .hero-device carries .r and
     .r.in{transform:none} outranks anything set here */
  margin-bottom:calc(-1 * var(--hero-pb));

  --hero-hole-x:10.264%;
  --hero-hole-y:1.970%;
  --hero-hole-w:49.762%;
  --hero-hole-h:78.465%;
  --hero-hole-r:6%/3%;
}
/* the <picture> is only a source switch — take it out of the layout */
.hero-device picture{display:contents;}
.hero-hand{
  position:relative;z-index:2;
  /* max-width:none is load-bearing: the global img rule would otherwise hold
     the photo to the column width and squeeze it against the set height */
  height:100%;width:auto;max-width:none;
  filter:drop-shadow(-18px 28px 44px rgba(6,11,26,.5));
  pointer-events:none;-webkit-user-select:none;user-select:none;
}

.hero-screen{
  position:absolute;z-index:1;
  left:var(--hero-hole-x);top:var(--hero-hole-y);
  width:var(--hero-hole-w);height:var(--hero-hole-h);
  overflow:hidden;border-radius:var(--hero-hole-r);
  background:#dfe3e0;
  /* everything inside is sized in cqi against the glass, so the chrome holds
     its proportions whether the device renders at 150px across or 260px */
  container-type:inline-size;
}
.hero-map{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;}
/* opacity is driven per frame by the loop in main.js — no CSS transition to
   fight it, or the fade never actually reaches zero at the wrap */
.hero-overlay{position:absolute;inset:0;width:100%;height:100%;}

/* The phone is not merely rotated, it is keystoned: shot slightly from above,
   so the glass's top edge runs at about -0.4deg and its bottom edge at about
   -1.7deg. Chrome takes its angle from where it sits, or it reads as a sticker
   laid on the photo rather than as pixels on that screen. */
.hero-status,.hero-mapui{transform:rotate(-0.55deg);}
.hero-chat{transform:rotate(-1.7deg);}

.hero-status{
  position:absolute;top:2.2%;left:0;right:0;z-index:4;
  display:flex;justify-content:space-between;align-items:center;
  padding:0 8%;
  font-family:'DM Sans',sans-serif;font-weight:600;line-height:1;
  font-size:clamp(6.5px,4.4cqi,11px);color:#11172a;
}
.hero-status-right{display:flex;align-items:center;gap:1.8cqi;}
.hero-bars{display:flex;align-items:flex-end;gap:.7cqi;}
.hero-bars i{display:block;width:.95cqi;background:#11172a;border-radius:1px;}
.hero-bars i:nth-child(1){height:1.5cqi;}
.hero-bars i:nth-child(2){height:2.3cqi;}
.hero-bars i:nth-child(3){height:3.1cqi;}
.hero-bars i:nth-child(4){height:3.9cqi;opacity:.35;}
.hero-batt{position:relative;width:6.8cqi;height:3.6cqi;border:.5cqi solid #11172a;border-radius:1.1cqi;}
.hero-batt::after{content:"";position:absolute;inset:.5cqi;right:1.7cqi;background:#11172a;border-radius:1px;}

/* the app's own controls: menu left, zoom in / zoom out / locate stacked right */
.hero-mapui{
  position:absolute;top:8.6%;left:0;right:0;z-index:3;
  display:flex;justify-content:space-between;padding:0 5.2%;pointer-events:none;
}
.hero-mapui-stack{display:flex;flex-direction:column;gap:3cqi;}
.hero-mapui b{
  display:flex;align-items:center;justify-content:center;
  width:11.2cqi;height:11.2cqi;border-radius:3.9cqi;
  background:#fff;color:#5a5a5a;
  box-shadow:0 1px 3px rgba(16,21,39,.2),0 4px 9px -3px rgba(16,21,39,.2);
}
.hero-mapui b svg{display:block;width:64%;height:64%;}

/* Full width, at the bottom, where the app puts it. The thumb crosses its left
   end — that occlusion is the composite working, so the bar keeps its width and
   the CONTENT is inset past the thumb instead, which keeps the badge readable. */
.hero-chat{
  position:absolute;left:6%;right:6%;bottom:2.6%;z-index:4;
  display:flex;align-items:center;gap:2.6cqi;
  padding:3.6cqi 4.2cqi 3.6cqi 27cqi;
  border-radius:5cqi;background:rgba(255,255,255,.96);
  box-shadow:0 8px 22px -10px rgba(16,21,39,.55);
  font-size:clamp(7px,4.5cqi,11.5px);color:var(--ink-soft);
}
.hero-chat-n{
  flex:none;display:flex;align-items:center;justify-content:center;
  width:6.6cqi;height:6.6cqi;border-radius:50%;
  background:var(--route);color:#fff;
  font-family:'DM Sans',sans-serif;font-weight:700;font-size:clamp(6px,3.7cqi,9.5px);
}
.hero-chat-msg{flex:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}
.hero-chat-msg b{color:var(--ink);}
.hero-chat-chev{
  flex:none;width:2.6cqi;height:2.6cqi;
  border-left:.6cqi solid var(--ink-mute);border-top:.6cqi solid var(--ink-mute);
  transform:rotate(45deg);
}

/* the pin engine, same values as How It Works but a shorter tail: at this size
   the 294-unit tail runs most of the way down the visible route */
.hero-overlay .trail{fill:none;stroke:var(--route);stroke-width:8;stroke-linecap:round;stroke-linejoin:round;opacity:.2;}
.hero-overlay .comet{fill:none;stroke-width:8;stroke-linecap:round;stroke-linejoin:round;}
.hero-overlay .rider use,.hero-overlay .pack use{fill:var(--navy);}
.hero-overlay .packnum{font-family:'Alexandria',sans-serif;font-size:66px;font-weight:700;fill:#fff;}

.hero-scroll-cue{position:absolute;left:50%;bottom:26px;transform:translateX(-50%);z-index:3;}
.cue-arrow{display:block;width:14px;height:30px;position:relative;}
.cue-arrow::before,.cue-arrow::after{content:"";position:absolute;background:currentColor;}
.cue-arrow::before{left:6px;top:0;width:2px;height:24px;}
.cue-arrow::after{left:1px;top:16px;width:12px;height:2px;transform:rotate(0);
  clip-path:polygon(0 0,100% 0,50% 100%);height:9px;background:currentColor;
  -webkit-mask:none;}
.hero-scroll-cue{color:rgba(255,255,255,.8);animation:bob 2.2s ease-in-out infinite;}
@keyframes bob{0%,100%{transform:translate(-50%,0);}50%{transform:translate(-50%,8px);}}

/* ============================================================
   HOW IT WORKS — pinned scroll
   ============================================================ */
.hiw{background:#f1efec;}
/* four stages: 125vh of scroll each */
.hiw-track{position:relative;height:500vh;}
.hiw-stage{
  /* shared by the map chrome and the caption so their insets cannot drift */
  --mapui-size:44px;
  --mapui-gap:17px;
  --mapui-inset:24px;
  position:sticky;top:0;
  height:100vh;height:100svh;height:100dvh;
  overflow:hidden;
  background:#f1efec;
}

/* Map: three stacked copies of the same image. Cross-fading opacity is far
   cheaper than animating filter:blur() on a full-screen image every frame,
   and the small blurred files upscale into a smooth blur for free. */
.hiw-map{
  /* --map-pan widens this box past the right edge of the stage, which moves
     the cover crop's centre rightwards and so reveals map further LEFT. It
     has to work this way: translating the element would just slide it off
     its own background. Both the <img> layers and the SVG overlay size to
     this box, so they crop identically and the route stays on the streets. */
  position:absolute;top:0;bottom:0;left:0;
  right:calc(-2 * var(--map-pan,0px));
  transform:scale(var(--map-scale)) translateZ(0);
  transform-origin:58% 46%;
  will-change:transform;
}
.map-layer{
  position:absolute;inset:0;width:100%;height:100%;
  object-fit:cover;object-position:center;
}
.map-mid{opacity:var(--mid-a,0);transition:opacity .14s linear;}
.map-heavy{opacity:var(--heavy-a,1);transition:opacity .14s linear;}
.map-overlay{
  position:absolute;inset:0;width:100%;height:100%;
  filter:blur(var(--pin-blur,9px));
  opacity:var(--pin-a,.75);
  transition:opacity .14s linear;
}
/* Route and trails share geometry once a rider merges, so these stack exactly
   on the corridor and read as a single line however many riders are on it.

   Two layers make the movement read. The base path is the full history at a
   flat low opacity; a "comet" clone on top draws only the last stretch behind
   the pin, stroked with a gradient that goes transparent at its tail. Numbers
   measured off Derrick's comp: stroke 18 units, comet fade 294 units, history
   settling at 0.17. The comet paths and their gradients are built in main.js —
   nothing to maintain here when riders are added or removed. */
.route,.trail{
  fill:none;stroke:#e8352a;stroke-width:18;
  stroke-linecap:round;stroke-linejoin:round;opacity:.17;
}
/* no stroke colour here on purpose: it comes from the per-rider gradient, and
   a CSS stroke would outrank that presentation attribute */
.comet{fill:none;stroke-width:18;stroke-linecap:round;stroke-linejoin:round;}
.rider use{fill:var(--navy);}
#cluster use{fill:var(--navy);}
.cluster-num{font-family:'Alexandria',sans-serif;font-size:66px;font-weight:700;fill:#fff;}
.rider,#cluster{will-change:transform;}

.hiw-veil{
  position:absolute;inset:0;
  background:
    radial-gradient(1000px 620px at 55% 26%, rgba(174,205,236,.55), transparent 62%),
    linear-gradient(180deg, rgba(250,250,249,.96) 0%, rgba(250,250,249,.9) 55%, rgba(250,250,249,.96) 100%);
  opacity:var(--veil-a);
  pointer-events:none;
}

.hiw-panel{
  position:absolute;inset:0;
  display:flex;flex-direction:column;justify-content:flex-start;
  padding-top:calc(var(--nav-h) + clamp(22px,5vh,52px));
  opacity:0;visibility:hidden;
  transform:translateY(26px);
  transition:opacity .5s ease, transform .6s cubic-bezier(.2,.7,.3,1), visibility .5s;
  pointer-events:none;
}
.hiw-panel.is-on{opacity:1;visibility:visible;transform:none;pointer-events:auto;}

/* Type comes from .eyebrow. This holds only what is actually different here:
   it stacks rather than sitting inline, and it is dark on the light map. */
.hiw-eyebrow{display:block;color:var(--ink);}
.hiw-h2{margin-top:clamp(28px,6vh,72px);font-size:clamp(30px,4.2vw,54px);font-weight:800;max-width:19em;}
.hiw-panel[data-stage="2"] .hiw-h2{margin-top:clamp(24px,5vh,60px);}
.hiw-lede{margin-top:14px;font-size:clamp(var(--fs-body),1.35vw,var(--fs-lede));line-height:1.6;color:var(--ink);max-width:60ch;}

/* stage 4 feature grid. It was six centred columns on stage 1; inside the
   stage-4 panel it reads better as icon-beside-text, and auto-fit lands it at
   three across on a desktop card, two on a tablet and one on a phone without
   needing a column count at every breakpoint. */
.feature-row{
  display:grid;grid-template-columns:repeat(auto-fit,minmax(280px,1fr));
  gap:clamp(18px,2.4vh,26px) 34px;
  margin-top:clamp(22px,3.4vh,34px);
}
.feature-row li{display:flex;gap:16px;align-items:flex-start;}
/* Navy tile, white glyph. Yellow is the call-to-action colour on this page,
   so it is kept for buttons and accents rather than spent on decoration. */
.ft-icon{
  flex:none;
  display:inline-flex;align-items:center;justify-content:center;
  width:52px;height:52px;border-radius:15px;background:var(--navy);color:var(--white);
  box-shadow:0 10px 22px -12px rgba(35,54,108,.75);
}
.ft-icon svg{width:26px;height:26px;}
.feature-row h3{font-size:var(--fs-body);font-weight:700;line-height:1.25;}
.feature-row p{margin-top:6px;font-size:var(--fs-sm);line-height:1.45;color:var(--ink-soft);}

/* ---------- stage 4 — the feature panel ----------
   The map has stopped moving by now, so this stage's move is the scrim: navy
   washes back over a sharp street map so six light cards have something to sit
   on. Without it they fight the map for attention and lose. */
.hiw-scrim{
  position:absolute;inset:0;pointer-events:none;
  background:linear-gradient(180deg, rgba(35,54,108,.32) 0%, rgba(35,54,108,.52) 100%);
}
.hiw-panel-4{
  /* centred rather than top-aligned, with the nav's height reserved so the
     card can never slide under the pill on a short viewport */
  justify-content:center;
  padding-top:calc(var(--nav-h) + 12px);
  padding-bottom:24px;
}
.hiw-feature-card{
  position:relative;z-index:1;
  width:calc(100% - 2 * clamp(16px,3.2vw,58px));
  max-width:var(--maxw);
  margin:0 auto;
  background:rgba(250,250,249,.9);
  backdrop-filter:blur(4px);
  -webkit-backdrop-filter:blur(4px);
  border-radius:var(--radius-md);
  padding:clamp(26px,4vh,44px) 0 clamp(28px,4.4vh,48px);
}
/* smaller than the story stages: this panel carries a heading and six cards,
   and at the full size the heading ate the top third of it */
/* 16em, so it breaks after the first sentence instead of orphaning "it." */
.hiw-feature-card .hiw-h2{margin-top:12px;font-size:clamp(26px,3.1vw,40px);max-width:16em;}

/* stage 2 — numbered steps */
.step-row{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:34px;margin-top:clamp(28px,4.5vh,48px);max-width:1000px;}
.step-row li{display:flex;gap:16px;align-items:flex-start;}
.step-pin{
  flex:none;width:46px;height:60px;position:relative;display:flex;align-items:flex-start;justify-content:center;
  background:var(--navy);
  clip-path:polygon(50% 100%, 0 46%, 0 0, 100% 0, 100% 46%);
  border-radius:8px 8px 0 0;
}
.step-pin span{font-family:'Alexandria',sans-serif;font-weight:800;font-size:24px;color:var(--white);line-height:1;padding-top:11px;}
.step-row h3{font-size:var(--fs-lede);font-weight:700;}
.step-row p{margin-top:6px;font-size:var(--fs-body);line-height:1.5;color:var(--ink);}

/* stage 3 — payoff card */
.hiw-panel-3{padding-top:0;justify-content:flex-start;}
.hiw-eyebrow.floating{position:absolute;top:calc(var(--nav-h) + clamp(22px,5vh,52px));left:0;right:0;max-width:var(--maxw);margin:0 auto;padding:0 calc(var(--gut) + 8px);z-index:2;}
/* A floating rounded box, not a full-bleed band. Measured off Derrick's comp:
   the card is inset ~5.1% of the viewport each side with a ~1% corner radius.
   --card-inset is tuned so the inner .wrap still lands the headline where it
   already sat on desktop; the card just gains map either side of it.
   z-index:1 puts it over the map chrome, which is pinned to the same corners. */
.hiw-card{
  --card-inset:clamp(16px,3.2vw,58px);
  position:relative;z-index:1;
  /* Capped at --maxw, the same ceiling the nav pill uses, so on a wide monitor
     the card stops growing and lines up with the nav instead of running out to
     the window edges far past the text inside it. Below that ceiling
     --card-inset still governs, which is the proportion measured off the comp. */
  width:calc(100% - 2 * var(--card-inset));
  max-width:var(--maxw);
  margin:calc(var(--nav-h) + clamp(46px,9vh,104px)) auto 0;
  background:rgba(250,250,249,.82);
  backdrop-filter:blur(3px);
  -webkit-backdrop-filter:blur(3px);
  border-radius:var(--radius-md);
  padding:34px 0 38px;
}
.hiw-card .hiw-h2{margin-top:0;}

/* Map chrome — menu top-left, zoom/locate stack top-right, sized off Derrick's
   comp. Everything scales from --mapui-size, which is the button edge: the
   corner radius is 0.35 of it and the icons are drawn in a 44-unit viewBox that
   fills the button, so changing the one number rescales the whole set.
   The gap and edge inset are the comp's own 17px and ~22px at that 43px size. */
/* Pinned to the stage's own top corners, because the whole frame is the map.
   No z-index on purpose: the fixed nav is z-index 80 at the root, so at widths
   where the nav pill reaches these corners it passes cleanly over them. */
.map-ui{
  position:absolute;top:var(--mapui-inset);
  left:var(--mapui-inset);right:var(--mapui-inset);
  display:flex;justify-content:space-between;align-items:flex-start;
  pointer-events:none;
}
.map-ui-stack{display:flex;flex-direction:column;gap:var(--mapui-gap);}
.map-ui-btn{
  flex:none;display:block;
  width:var(--mapui-size);height:var(--mapui-size);
  border-radius:calc(var(--mapui-size) * .35);
  background:#fff;
  color:#404040;
  box-shadow:0 1px 3px rgba(16,21,39,.2), 0 4px 10px -2px rgba(16,21,39,.18);
}
.map-ui-btn svg{display:block;width:100%;height:100%;}
/* the menu bars are drawn a shade lighter than the control glyphs in the comp */
.map-ui-menu{color:#707070;}

/* Caption on the artwork, bottom-left — where a credit line belongs relative
   to the thing it describes. Clear of the card above, the chat bar (centred)
   and the dots (right). Shares --mapui-inset with the map chrome so the two
   pieces of furniture line up on the same left edge. */
.hiw-note{
  position:absolute;left:var(--mapui-inset);bottom:20px;
  max-width:calc(100% - 2 * var(--mapui-inset));
  padding:6px 13px;border-radius:999px;
  /* .88 let a tan patch of map through and dropped this to 3.11:1 at 12px.
     This is a claim about the product, not decoration, so it has to be
     readable wherever the crop happens to put it. .96 reads 7.4:1. */
  background:rgba(250,250,249,.96);
  backdrop-filter:blur(4px);
  -webkit-backdrop-filter:blur(4px);
  font-size:var(--fs-micro);font-weight:600;line-height:1.35;color:var(--ink-soft);
}
.hiw-chatbar{
  position:absolute;left:50%;bottom:0;transform:translateX(-50%);
  display:flex;align-items:center;gap:12px;
  width:min(420px,72vw);padding:14px 20px;
  background:rgba(255,255,255,.95);border-radius:14px 14px 0 0;
  box-shadow:0 -8px 26px -14px rgba(16,21,39,.5);
}
.chat-badge{
  width:22px;height:22px;border-radius:50%;background:var(--route);color:#fff;
  font-size:var(--fs-micro);font-weight:700;display:flex;align-items:center;justify-content:center;
}
/* 600, not 500: this was the only 500 on the page, and one decorative label
   is not worth a whole extra weight download. 600 also matches the other
   map furniture (.hiw-note, .badge). */
.chat-name{flex:none;font-size:var(--fs-sm);font-weight:700;color:var(--ink);}
.chat-msg{
  flex:1;min-width:0;font-size:var(--fs-sm);color:var(--ink-soft);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
}
.chat-chev{width:10px;height:10px;border-left:2px solid var(--ink-mute);border-top:2px solid var(--ink-mute);transform:rotate(45deg);}

/* cue + dots */
.hiw-cue{position:absolute;left:50%;bottom:64px;transform:translateX(-50%);color:var(--ink);animation:bob 2.2s ease-in-out infinite;transition:opacity .4s ease;}
.hiw-dots{position:absolute;right:calc(var(--gut) + 6px);top:50%;transform:translateY(-50%);display:flex;flex-direction:column;gap:10px;}
.hiw-dots span{width:8px;height:8px;border-radius:50%;background:rgba(35,54,108,.22);transition:background .3s ease,transform .3s ease;}
.hiw-dots span.on{background:var(--navy);transform:scale(1.35);}

/* ============================================================
   SHARED PANELS
   ============================================================ */
.panel{position:relative;padding:clamp(84px,11vh,132px) 0;overflow:hidden;}
.panel-inner{position:relative;z-index:2;}
.panel-inner.narrow{max-width:900px;}
.panel-h2{margin-top:16px;font-size:clamp(28px,3.6vw,46px);font-weight:800;max-width:20em;}
.panel-lede{margin-top:18px;font-size:clamp(var(--fs-body),1.35vw,var(--fs-lede));line-height:1.65;max-width:72ch;}

/* The setup steps, out of the pinned scroll and standing on their own.
   Slimmer than a full .panel: it is a strip between two big sections, not a
   destination. */
.panel-start{padding:clamp(56px,8vh,92px) 0;background:var(--white);border-bottom:1px solid var(--line);}
.panel-start .panel-h2{max-width:24em;}
.panel-start .step-row{max-width:1100px;}

.panel-navy{background:var(--navy);color:var(--white);min-height:100svh;display:flex;align-items:center;}
.panel-navy .panel-lede{color:rgba(255,255,255,.78);}
.panel-light{background:var(--white);color:var(--ink);}
.panel-light .panel-lede{color:var(--ink-soft);}
.panel-paper{background:var(--paper);color:var(--ink);}
.panel-paper .panel-lede{color:var(--ink-soft);}

/* Partners photo backdrop.
   The asset already has the navy gradient baked into its left side, so the
   linear-gradient below is only the top-up that keeps white text safe once
   the crop shifts at other viewport sizes — keep it light or the left half
   goes muddy. To swap the image, change --partner-photo and nothing else;
   when this becomes a set of graphics for different group types, each one
   is a value for that var and they can crossfade between stacked copies. */
.has-photo .panel-photo{
  position:absolute;inset:-12% 0;z-index:1;
  --partner-photo:url('../assets/partners-bg.jpg');
  background-image:
    linear-gradient(90deg, rgba(35,54,108,.55) 0%, rgba(35,54,108,.22) 42%, rgba(35,54,108,0) 68%),
    var(--partner-photo),
    linear-gradient(180deg,#1d2d5a,#172447);
  background-size:cover,cover,cover;
  background-position:center,right center,center;
  background-repeat:no-repeat;
  will-change:transform;
}

/* ---------- partners flow ---------- */
.flow{
  display:grid;grid-template-columns:1fr auto 1fr auto 1fr;align-items:center;gap:16px;
  margin-top:clamp(34px,5vh,54px);max-width:1000px;
}
.flow-box{
  background:rgba(255,255,255,.07);border:1px solid rgba(255,255,255,.16);
  border-radius:var(--radius-md);padding:20px 22px;
}
.flow-box h3{font-size:var(--fs-body);font-weight:700;}
.flow-box p{margin-top:7px;font-size:var(--fs-sm);line-height:1.5;color:rgba(255,255,255,.7);}
.flow-arrow{color:var(--yellow);font-size:26px;line-height:1;}
.flow-core{text-align:center;}
.flow-core-label{display:block;font-size:var(--fs-micro);letter-spacing:.1em;text-transform:uppercase;color:rgba(255,255,255,.62);margin-bottom:8px;}
/* --navy-950 rather than --navy: the panel behind it is already --navy, so
   the box needs the darkest step of the ramp to read as a separate block. */
.flow-core-mark{
  background:var(--navy-950);border-radius:var(--radius-md);padding:18px 20px;
  display:flex;align-items:center;justify-content:center;
  box-shadow:0 18px 36px -18px rgba(9,14,32,.9);
}
.flow-core-mark img{height:34px;width:auto;}

.partner-marks{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:22px;margin-top:clamp(34px,5vh,54px);max-width:1000px;}
.partner-marks li{text-align:center;}
.pm-icon{display:inline-flex;align-items:center;justify-content:center;width:40px;height:40px;border-radius:50%;border:1.5px solid rgba(255,255,255,.4);color:var(--yellow);}
.pm-icon svg{width:20px;height:20px;}
.partner-marks h3{margin-top:11px;font-size:var(--fs-body);font-weight:700;}
.partner-marks p{margin-top:5px;font-size:var(--fs-sm);color:rgba(255,255,255,.62);}

.panel-cta{margin-top:clamp(34px,5vh,52px);display:flex;flex-wrap:wrap;align-items:center;gap:18px;}
.panel-cta p{font-size:var(--fs-sm);color:rgba(255,255,255,.68);max-width:38ch;}
.panel-light .panel-cta p{color:var(--ink-soft);}

/* ---------- use cases ---------- */
.use-grid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:36px;margin-top:clamp(34px,5vh,52px);}
.use-card h3{font-size:var(--fs-lede);font-weight:700;}
.use-card p{margin-top:10px;font-size:var(--fs-body);line-height:1.6;color:var(--ink-soft);}
.use-closer{margin-top:clamp(36px,5vh,56px);display:flex;flex-direction:column;align-items:center;gap:20px;text-align:center;}
/* :not(.badge) so the status pill keeps its own type — a bare `.use-closer p`
   outranks the single-class .badge rule and was blowing it up to 15.5px. */
.use-closer p:not(.badge){max-width:62ch;font-size:var(--fs-body);line-height:1.6;color:var(--ink);}

/* Use-cases photo backdrop.
   The graphic already fades to white across its top two-thirds, so the
   scrim below is only a light top-up that keeps ink-on-white copy safe
   once the crop shifts at other viewport sizes — keep it light or the
   sky goes flat and the riders stop reading as a photograph. The extra
   padding-bottom is what reserves the empty road for the riders and the
   pins to sit in, clear of the CTA. To swap the image, change
   --usecase-photo and nothing else. */
.panel-usecases{
  --usecase-photo:url('../assets/usecases-bg.jpg');
  padding-bottom:clamp(300px,36vh,440px);
}
/* The 2x art (2798px, 367 KB) only earns its weight where the 1x would
   actually look soft. A 390px phone at 3dppx needs 1170 device pixels and the
   1399px 1x already covers that, so phones keep the 129 KB file; the swap
   starts at the width where 2x displays out-resolve it. */
@media (min-resolution:2dppx) and (min-width:800px){
  .panel-usecases{--usecase-photo:url('../assets/usecases-bg@2x.jpg');}
}
.panel-usecases .panel-photo{
  position:absolute;inset:0;z-index:1;
  background-image:
    linear-gradient(180deg,
      rgba(255,255,255,.62) 0%,
      rgba(255,255,255,.46) 38%,
      rgba(255,255,255,.20) 58%,
      rgba(255,255,255,0) 76%),
    var(--usecase-photo);
  /* 100% auto, not cover: the photo sits at natural proportions across the
     full width and hangs off the bottom, so both pin clusters stay in frame
     at every width and a tall section just shows more white above it —
     which costs nothing, because the graphic is already white up top. */
  background-size:cover,100% auto;
  background-position:center,center bottom;
  background-repeat:no-repeat;
  will-change:transform;
}

/* ---------- FAQ ---------- */
.faq-list{margin-top:clamp(28px,4vh,44px);display:flex;flex-direction:column;gap:10px;}
.faq-item{background:var(--white);border:1px solid var(--line);border-radius:var(--radius-sm);overflow:hidden;}
.faq-item summary{
  list-style:none;cursor:pointer;padding:18px 22px;
  display:flex;align-items:center;justify-content:space-between;gap:16px;
  font-weight:700;font-size:var(--fs-body);
}
.faq-item summary::-webkit-details-marker{display:none;}
.chev{flex:none;width:11px;height:11px;border-right:2px solid var(--ink-mute);border-bottom:2px solid var(--ink-mute);transform:rotate(45deg);transition:transform .25s ease;}
.faq-item[open] .chev{transform:rotate(225deg);}
.faq-body{padding:0 22px 20px;}
.faq-body p{font-size:var(--fs-body);line-height:1.65;color:var(--ink-soft);}

/* ---------- beta ---------- */
/* No background art on the section itself any more. The route and pin live
   in their own grid column (.beta-art), which is what keeps them off the
   copy at every width: a background image has no relationship to the text
   box, so at some sizes the old raster pin landed right against it. */
.panel-beta{
  text-align:left;
  min-height:auto;
  padding:clamp(76px,10vh,110px) 0;
  background-color:var(--navy);
}
.beta-grid{
  display:grid;
  grid-template-columns:minmax(0,1fr) minmax(0,clamp(250px,34%,460px));
  gap:clamp(28px,4vw,72px);
  align-items:center;
}

/* The art box. Fixed ratio so it scales as one piece, clipped corners so
   the faded map reads as a card rather than a bleed. */
.beta-art{
  position:relative;
  aspect-ratio:5 / 4;
  border-radius:var(--radius-md);
  overflow:hidden;
  background:var(--navy-950);
  box-shadow:0 30px 60px -34px rgba(6,11,26,.9), 0 0 0 1px rgba(255,255,255,.06);
}
/* The same map the pinned section already loads, so this costs no new
   request on a 1x display. Faded well back: it is texture, not information. */
.beta-map{
  position:absolute;inset:0;
  /* zoomed past cover so streets read as streets at this size. At plain cover
     the crop is so small it turns into noise. */
  background:url('../assets/hiw-map@1x.jpg') 48% 42% / 185% auto no-repeat;
  opacity:.5;
  filter:grayscale(.4);
}
/* Heavy navy over the top, so the box sits in the same blue as the section
   around it and the yellow pin is the only bright thing in the frame. */
.beta-wash{
  position:absolute;inset:0;
  background:
    radial-gradient(120% 95% at 72% 18%, rgba(62,96,193,.45) 0%, transparent 62%),
    linear-gradient(170deg, rgba(35,54,108,.62) 0%, rgba(19,30,60,.86) 100%);
}
.beta-svg{position:absolute;inset:0;width:100%;height:100%;}

/* ---------- footer ---------- */
.site-footer{background:var(--white);padding:70px 0 32px;border-top:1px solid var(--line);}
.footer-top{display:grid;grid-template-columns:2fr 1fr 1fr;gap:40px;}
/* The logo PNG carries transparent padding — 153px of its 423px art height
   sits left of the mark — so the img box's left edge is NOT the logo's left
   edge, and a plain block image lands visually inboard of the copy below it.
   The negative margin (153/423 = 0.362 of rendered height) pulls the mark
   back onto the text's left edge and stays correct if the height changes.
   Re-measure that ratio if the logo file is ever re-exported. */
.footer-brand img{
  --footer-logo-h:68px;
  height:var(--footer-logo-h);width:auto;
  margin-left:calc(var(--footer-logo-h) * -0.362);
}
.footer-brand p{margin-top:16px;font-size:var(--fs-sm);line-height:1.6;color:var(--ink-soft);max-width:38ch;}
.footer-col h4{font-size:var(--fs-micro);letter-spacing:.14em;text-transform:uppercase;color:var(--ink-mute);font-family:'DM Sans',sans-serif;font-weight:700;}
.footer-col ul{margin-top:14px;display:flex;flex-direction:column;gap:9px;}
.footer-col a{font-size:var(--fs-sm);color:var(--ink-soft);}
.footer-col a:hover{color:var(--navy);}
.footer-bottom{margin-top:48px;padding-top:20px;border-top:1px solid var(--line);font-size:var(--fs-micro);color:var(--ink-mute);}

/* ============================================================
   MODAL (partner enquiry)
   ============================================================ */
.modal-overlay{
  position:fixed;inset:0;z-index:120;
  display:flex;align-items:center;justify-content:center;
  padding:24px;
  background:rgba(64,70,88,.55);
  backdrop-filter:blur(7px) saturate(120%);
  -webkit-backdrop-filter:blur(7px) saturate(120%);
  opacity:0;transition:opacity .22s ease;
}
.modal-overlay[hidden]{display:none;}
.modal-overlay.is-open{opacity:1;}

.modal{
  position:relative;
  width:min(520px,100%);
  max-height:calc(100dvh - 48px);
  overflow-y:auto;
  background:var(--white);
  /* the nav pill is fully rounded; at this size that reads as an oval, so
     this takes the nav's soft-corner feel at a radius a panel can wear */
  border-radius:var(--radius-lg);
  padding:44px 44px 38px;
  text-align:center;
  box-shadow:0 40px 90px -28px rgba(15,23,48,.55), 0 0 0 1px rgba(16,21,39,.05);
  transform:translateY(14px) scale(.98);
  transition:transform .26s cubic-bezier(.2,.7,.3,1);
}
.modal-overlay.is-open .modal{transform:none;}

.modal-close{
  position:absolute;top:14px;right:14px;
  width:38px;height:38px;display:flex;align-items:center;justify-content:center;
  background:none;border:0;border-radius:50%;cursor:pointer;color:var(--ink-mute);
  transition:background .15s ease,color .15s ease;
}
.modal-close:hover{background:var(--paper-dim);color:var(--ink);}
.modal-close svg{width:20px;height:20px;}

.modal-logo{height:56px;width:auto;margin:0 auto 22px;}
.modal h2{
  font-size:clamp(19px,2.1vw,23px);font-weight:700;line-height:1.32;
  color:var(--ink);max-width:26em;margin:0 auto;
}

.modal-form{margin-top:28px;text-align:left;display:flex;flex-direction:column;gap:16px;}
.modal-form .field{display:flex;flex-direction:column;gap:7px;}
.modal-form label{font-size:var(--fs-sm);font-weight:700;color:var(--ink-soft);letter-spacing:.02em;}
.modal-form input{
  width:100%;padding:14px 18px;
  border:1.5px solid var(--line);border-radius:var(--radius-sm);
  background:var(--paper);color:var(--ink);
  font-family:inherit;font-size:var(--fs-body);
  transition:border-color .15s ease,background .15s ease;
}
.modal-form input::placeholder{color:var(--ink-mute);}
.modal-form input:focus{background:var(--white);border-color:var(--navy-500);}
.modal-send{margin-top:8px;width:100%;}

/* step swap inside a modal — beat any display set on the step itself */
.modal [hidden]{display:none !important;}
.modal-error{
  margin-top:-4px;font-size:var(--fs-sm);font-weight:600;color:var(--route);
  min-height:1.1em;
}
.modal-error:empty{min-height:0;}
.modal-done{padding:8px 0 6px;}
.modal-done p{
  font-size:clamp(var(--fs-body),2vw,var(--fs-lede));font-weight:600;line-height:1.45;
  color:var(--ink);max-width:24em;margin:0 auto;
}
.modal-done:focus{outline:none;}

/* ============================================================
   INTERIOR PAGES  (privacy.html, terms, etc.)
   A navy band under the nav, then one column of prose with a
   sticky contents rail. Everything here uses the shared tokens,
   so a colour or type-scale change on the landing page carries
   through without a second edit.
   ============================================================ */

/* The nav pill is translucent white. Over the landing hero it sits on navy,
   so an interior page needs a dark band of its own or the pill floats on
   near-white and loses its edge. Same gradient recipe as .hero-fallback,
   flattened, with no video behind it. */
.page-head{
  position:relative;
  background:
    radial-gradient(900px 420px at 78% 8%, var(--navy-600) 0%, transparent 64%),
    linear-gradient(160deg, var(--navy-900) 0%, var(--navy) 58%, var(--navy-950) 100%);
  color:var(--white);
  padding:calc(var(--nav-h) + clamp(44px,7vh,84px)) 0 clamp(48px,7vh,76px);
}
/* Same expression as .hero h1, deliberately. At 58px the title was smaller
   than the 70px logo beside it and the nav pill read oversized on this page
   while looking normal on the home page. Display type has to stay on one
   scale across pages for the chrome to hold its apparent size. */
.page-head h1{
  margin-top:14px;
  font-size:clamp(42px,5.2vw,72px);font-weight:800;letter-spacing:-.03em;
}
/* Wider than the 46ch it started at, and wider than the 660px column the
   whole banner used to sit in. Both caps cost the band height for no reading
   benefit: a long title wrapped that did not need to, and the standfirst ran
   three lines where two will do. 68ch is the measure the body copy uses. */
.page-head-lede{
  margin-top:18px;max-width:68ch;
  font-size:var(--fs-lede);line-height:1.55;
  color:rgba(255,255,255,.82);
}

.doc{padding:clamp(46px,7vh,84px) 0 clamp(64px,10vh,112px);}
.doc-grid{
  display:grid;
  grid-template-columns:224px minmax(0,1fr);
  gap:clamp(30px,5vw,80px);
  align-items:start;
}

/* ---------- contents rail ---------- */
/* The rail column. It holds one nav on a content page and two on a legal
   page (this page's sections, then its sibling policies), so the wrapper is
   what sticks and what becomes a card on a phone, not the nav inside it. */
.doc-rail{position:sticky;top:calc(var(--nav-h) + 26px);}
/* a second list under the first, separated rather than merged into it */
.doc-nav-sub{margin-top:26px;padding-top:22px;border-top:1px solid var(--line);}
.doc-toc-title{
  font-family:'DM Sans',sans-serif;font-weight:700;
  font-size:var(--fs-micro);letter-spacing:.14em;text-transform:uppercase;
  /* --ink-mute is the usual label colour, but it only makes 4.40:1 on --paper
     (it clears 4.5 on pure white, which is where the footer uses it). One step
     darker keeps the label quiet and passes at 7.2:1. */
  color:var(--ink-soft);
}
/* Two counters, one here and one on .doc-body, both stepping in document
   order. Section numbers then cannot drift from the list, and adding a
   section needs no renumbering anywhere. */
.doc-toc ol{counter-reset:toc;margin-top:15px;display:flex;flex-direction:column;gap:11px;}
.doc-toc li{counter-increment:toc;}
.doc-toc a{
  display:flex;gap:9px;
  font-size:var(--fs-sm);line-height:1.4;font-weight:600;color:var(--ink-soft);
  transition:color .15s ease;
}
.doc-toc a::before{
  content:counter(toc) ".";
  /* wide enough for "12." so the labels stay in one column once the list
     runs past nine items */
  flex:none;min-width:1.9em;
  /* recedes by weight rather than by lightness. --ink-mute would be 4.40:1
     on --paper, and these numerals are content, not decoration. */
  font-weight:400;color:var(--ink-soft);font-variant-numeric:tabular-nums;
}
.doc-toc a:hover{color:var(--navy);}
.doc-toc a:hover::before{color:var(--navy-500);}

/* ---------- prose ---------- */
.doc-body{counter-reset:sect;max-width:68ch;}
/* "Last updated" used to sit in the navy band. It is reference detail, not a
   headline, so it moved down here to the top of the document, set right so it
   reads as a stamp on the text rather than a line of it.
   --ink-mute would be lighter still, but it only makes 4.40:1 on --paper. */
.doc-meta{
  margin-bottom:26px;text-align:right;
  font-size:var(--fs-micro);line-height:1.4;color:var(--ink-soft);
}
.doc-meta-sep{padding:0 6px;}
.doc-lede{font-size:var(--fs-lede);line-height:1.6;color:var(--ink);}
.doc-section{
  counter-increment:sect;
  margin-top:clamp(34px,4.4vw,48px);
  padding-top:clamp(30px,4vw,44px);
  border-top:1px solid var(--line);
}
.doc-body > .doc-section:first-of-type{margin-top:clamp(28px,3.6vw,40px);padding-top:0;border-top:0;}
.doc-section h2{font-size:clamp(22px,2.5vw,30px);font-weight:800;}
.doc-section h2::before{
  content:counter(sect) ".";
  margin-right:.42em;
  /* the number reads as part of the heading, not as an accent, so it takes
     the heading's own colour */
  color:inherit;font-variant-numeric:tabular-nums;
}
.doc-section h3{
  margin-top:clamp(24px,2.6vw,32px);
  font-size:var(--fs-lede);font-weight:700;line-height:1.25;color:var(--ink);
}
.doc-section p{margin-top:14px;font-size:var(--fs-body);line-height:1.7;color:var(--ink-soft);}
.doc-section strong{color:var(--ink);font-weight:700;}
.doc-section a:not(.btn){color:var(--navy-600);text-decoration:underline;text-underline-offset:2px;}
.doc-section a:not(.btn):hover{color:var(--navy);}

.doc-list{margin-top:16px;display:flex;flex-direction:column;gap:11px;}
.doc-list li{
  position:relative;padding-left:22px;
  font-size:var(--fs-body);line-height:1.65;color:var(--ink-soft);
}
.doc-list li::before{
  content:"";position:absolute;left:4px;top:.62em;
  width:6px;height:6px;border-radius:50%;background:var(--ink);
}

/* plain-language summary sitting inside a section */
.doc-note{
  margin-top:22px;
  background:var(--paper-dim);
  border-left:3px solid var(--yellow-deep);
  border-radius:0 var(--radius-sm) var(--radius-sm) 0;
  padding:18px 22px;
}
.doc-note p{margin-top:0;font-size:var(--fs-sm);line-height:1.6;}

.doc-address{
  margin-top:18px;font-style:normal;
  font-size:var(--fs-body);line-height:1.7;color:var(--ink-soft);
}

/* ---------- content pages (about, beta, contact, press) ----------
   A contents rail earns its place on a policy nobody reads end to end.
   On a page you do read end to end it is clutter, so these drop it and
   run one column. The prose then starts at the gutter, in line with the
   title in the band above it. */
/* ---------- content pages (about, beta, contact, press) ----------
   Same two-column grid as a legal page. What changes is what the rail holds:
   a legal page lists its own sections, a content page lists the other three
   company pages. Both keep the prose at one measure. Running the body
   edge to edge instead put paragraphs at about 120 characters, which is past
   the point where the eye reliably finds the start of the next line. */
.doc-nav ul{margin-top:15px;display:flex;flex-direction:column;gap:12px;}
.doc-nav li{position:relative;}
/* .doc-nav carries .doc-toc for the shared rail styling, which numbers its
   items. A list of pages is not an ordered list of anything, so the counter
   comes off here. The accent bar goes on the li, because the anchor's
   ::before is where that counter lives. */
.doc-toc.doc-nav a::before{content:none;}
.doc-nav a{
  display:block;padding-left:14px;
  font-size:var(--fs-sm);line-height:1.4;font-weight:600;color:var(--ink-soft);
  transition:color .15s ease;
}
.doc-nav a:hover{color:var(--navy);}
.doc-nav a[aria-current="page"]{color:var(--navy);}
.doc-nav li:has(> a[aria-current="page"])::before{
  content:"";position:absolute;left:0;top:.15em;bottom:.15em;
  width:3px;border-radius:2px;background:var(--yellow-deep);
}

/* Section numbers are a legal convention: they exist so a clause can be
   cited. "1. Where to send what" on a contact page is just noise. */
.doc-pages .doc-section h2::before{content:none;}
/* The body fills its column so card grids can use it; the text inside holds
   the measure. */
.doc-pages .doc-body{max-width:none;}
.doc-pages .doc-lede,
.doc-pages .doc-section > *{max-width:68ch;}
.doc-pages .doc-section > .doc-cards,
.doc-pages .doc-section > .doc-facts,
.doc-pages .doc-section > .doc-table-wrap{max-width:none;}

/* a row of buttons closing a section */
.doc-actions{
  margin-top:26px;
  display:flex;flex-wrap:wrap;gap:12px;align-items:center;
}

/* contact routes, brand assets: anything that is a short list of things
   with a heading and a line of explanation each */
.doc-cards{
  margin-top:22px;
  display:grid;grid-template-columns:repeat(auto-fit,minmax(240px,1fr));
  gap:14px;
}
/* Four cards read better as two rows of two than as three and an orphan.
   :has() counts them, so the rule follows the content rather than the page.
   A browser without :has() just gets the three-up default. */
.doc-cards:has(> :nth-child(4)){grid-template-columns:repeat(auto-fit,minmax(300px,1fr));}
.doc-card{
  background:var(--white);
  border:1px solid var(--line);
  border-radius:var(--radius-md);
  padding:20px 22px 22px;
}
.doc-card h3{margin-top:0;font-size:var(--fs-body);font-weight:700;line-height:1.25;}
.doc-card p{margin-top:8px;font-size:var(--fs-sm);line-height:1.6;color:var(--ink-soft);}
.doc-card .doc-actions{margin-top:14px;}

/* press fast facts */
.doc-facts{
  margin-top:18px;
  display:grid;grid-template-columns:max-content minmax(0,1fr);
  gap:10px 20px;
  font-size:var(--fs-body);line-height:1.6;
}
.doc-facts dt{font-weight:700;color:var(--ink);}
.doc-facts dd{margin:0;color:var(--ink-soft);}

/* A table is the one thing on these pages allowed to be wider than the
   column, so it gets its own scroller rather than pushing the page
   sideways. */
.doc-table-wrap{margin-top:20px;overflow-x:auto;-webkit-overflow-scrolling:touch;}
.doc-table{
  width:100%;border-collapse:collapse;
  font-size:var(--fs-sm);line-height:1.55;
  min-width:520px;
}
.doc-table th,.doc-table td{
  text-align:left;vertical-align:top;
  padding:11px 16px 11px 0;
  border-bottom:1px solid var(--line);
}
.doc-table th{
  font-family:'DM Sans',sans-serif;font-weight:700;
  font-size:var(--fs-micro);letter-spacing:.08em;text-transform:uppercase;
  color:var(--ink);
}
.doc-table td{color:var(--ink-soft);}
.doc-table td:first-child{color:var(--ink);font-weight:600;white-space:nowrap;}

/* ============================================================
   REVEAL ANIMATION
   ============================================================ */
.r{opacity:0;transform:translateY(20px);transition:opacity .55s ease, transform .65s cubic-bezier(.2,.7,.3,1);transition-delay:calc(var(--rd,0) * 1ms);}
.r.in{opacity:1;transform:none;}

/* ============================================================
   RESPONSIVE
   ============================================================ */
@media (max-width:1080px){
  /* The chat bar is centred at min(420px,72vw), so below roughly 990px its
     left edge crosses the caption. Lift the caption above it — 50px bar plus
     a 14px gap — rather than letting the two touch. */
  .hiw-note{bottom:64px;}

  :root{--nav-h:90px;}
  .brand img{height:54px;}
  .nav-row{padding-right:15px;}
  /* tighter link spacing buys the room the four labels need down to 900px */
  .nav-links{gap:22px;}
  /* the copy column no longer sits clear of the bride once the crop
     narrows, so the horizontal fade gives way to an overall scrim */
  .has-photo .panel-photo{
    background-image:
      linear-gradient(180deg, rgba(35,54,108,.34) 0%, rgba(35,54,108,.24) 45%, rgba(35,54,108,.46) 100%),
      var(--partner-photo),
      linear-gradient(180deg,#1d2d5a,#172447);
    background-position:center,right center,center;
  }
  .flow{grid-template-columns:1fr;justify-items:stretch;}
  .flow-arrow{transform:rotate(90deg);justify-self:center;}
  .partner-marks{grid-template-columns:repeat(2,minmax(0,1fr));}
  .use-grid{grid-template-columns:1fr;gap:28px;}
  /* the crop loses the right of the frame first, so the photo slides
     toward the pins and the scrim carries further down the copy */
  .panel-usecases{padding-bottom:clamp(260px,32vh,360px);}
  .panel-usecases .panel-photo{
    background-image:
      linear-gradient(180deg,
        rgba(255,255,255,.72) 0%,
        rgba(255,255,255,.60) 44%,
        rgba(255,255,255,.26) 64%,
        rgba(255,255,255,0) 82%),
      var(--usecase-photo);
  }
  .step-row{grid-template-columns:1fr;gap:22px;}
  .footer-top{grid-template-columns:1fr 1fr;}
}

/* The nav collapses to a menu well before the four links would start to
   wrap — that happens around 900px, so it hands off here rather than at
   the layout breakpoint below. */
@media (max-width:900px){
  .nav-shell{position:relative;}
  .nav-links{
    position:absolute;top:calc(100% + 10px);left:0;right:0;
    flex-direction:column;align-items:stretch;gap:0;
    background:rgba(255,255,255,.98);border:1px solid rgba(16,21,39,.07);
    border-radius:22px;padding:8px;box-shadow:var(--shadow-soft);
    display:none;
  }
  .nav-links.open{display:flex;}
  .nav-links a,.nav-cta-mobile{padding:13px 16px;border-radius:14px;}
  .nav-cta-mobile{display:block;margin:6px 4px 4px;width:calc(100% - 8px);text-align:center;background:var(--yellow);color:var(--navy-950);font-weight:700;}
  .nav-toggle{display:block;}
  .nav-actions > .btn{display:none;}
  .nav-row{padding-right:14px;}

  /* Interior pages: the rail has nowhere to sit beside the prose, so it
     becomes a card above it. Sticky comes off with it, otherwise it would
     pin a full-width box over the reading column. */
  .doc-grid{grid-template-columns:minmax(0,1fr);gap:0;}
  .doc-rail{
    position:static;
    background:var(--white);
    border:1px solid var(--line);
    border-radius:var(--radius-md);
    padding:20px 22px 22px;
    margin-bottom:clamp(30px,5vw,42px);
  }
  .doc-nav-sub{margin-top:20px;padding-top:18px;}
  .doc-toc ol,.doc-nav ul{margin-top:13px;gap:9px;}
  .doc-body{max-width:none;}
  .doc-pages .doc-lede,.doc-pages .doc-section > *{max-width:none;}
  .doc-facts{grid-template-columns:minmax(0,1fr);gap:4px 0;}
  .doc-facts dd{margin-bottom:10px;}
}

@media (max-width:760px){
  :root{--gut:20px;--nav-h:80px;}
  .brand img{height:44px;}
  .hiw-stage{--mapui-size:38px;--mapui-gap:13px;--mapui-inset:16px;}
  .nav-row{padding:8px 8px 8px 18px;}

  /* ---- stacked hero ------------------------------------------------
     Above this width the two-column hero still has room for the device
     beside the copy, so the layout switch happens here, at the same width
     the nav collapses.

     The hand keeps its full shape on a phone rather than being rotated and
     corner-anchored the way the old static graphic was: the screen is live
     now, and tilting it costs legibility on the one layout where the glass
     is smallest. It stays bottom-anchored, so the cut wrist still lands on
     the section edge.

     Two things this depends on, unchanged from before:
     1. The offset is a negative margin, NOT a transform. .hero-device
        carries the .r reveal class and .r.in{transform:none} outranks
        anything set here — a transform offset silently does nothing.
     2. Any transform that has to survive lives on the <img>, which is not
        a .r element.

     The two supporting columns are hidden: stacked, they push the device
     clean off the first screen, and the first screen would then carry no
     product at all. Partners and For your event make that case further
     down the page. */
  /* A column, not a block. The hero is still min-height:100dvh here, so on a
     tall-but-narrow screen the stacked copy and device do not fill it, and a
     plain block flow leaves that slack UNDER the device — the hand floats up
     off the section edge by as much as 134px at 760x1100. As a flex column the
     stage can take margin-top:auto, which eats the slack above it instead. On
     a screen where the content already overflows, the auto margin is zero and
     nothing moves. */
  .hero-inner{display:flex;flex-direction:column;}
  .hero-copy{position:relative;z-index:2;max-width:620px;}
  .hero-split{display:none;}
  .hero h1{font-size:clamp(42px,5vw,56px);}
  .hero .lede{margin-top:16px;}
  .hero-cta-row{margin-top:24px;}
  .hero{--hero-pb:64px;padding:calc(var(--nav-h) + 16px) 0 var(--hero-pb);}
  /* align-self has to be reset, not inherited from the desktop rule: the base
     .hero-stage sits in a GRID, where the cross axis is vertical and end means
     bottom. Here the parent is a flex COLUMN, so the cross axis is horizontal
     and that same end means right-aligned and shrink-to-fit — which, with
     container-type:inline-size on this element, collapses it to zero width and
     takes the device with it. stretch gives the container a width to resolve
     127.5cqw against.
     margin-top:auto takes the slack so the wrist stays welded to the section
     edge; the padding is the gap to the copy, which the auto margin loses. */
  .hero-stage{align-self:stretch;margin-top:auto;padding-top:26px;}
  .hero-device{
    /* 46vh rather than the desktop dvh expression: here the copy is stacked
       above the device instead of beside it, so the height left over is not
       what the viewport minus the nav gives. 127.5cqw still caps the width. */
    height:min(clamp(320px,46vh,470px), 127.5cqw);
  }
  .hero-cta-row .btn{flex:1 1 100%;}
  /* the phone now passes behind these, and the ghost button's 6% white fill
     let the handset read straight through it — this makes it opaque enough
     to sit in front on purpose rather than look like a mistake */
  .hero-cta-row .btn-ghost{
    background:rgba(19,30,60,.62);
    backdrop-filter:blur(8px);
    -webkit-backdrop-filter:blur(8px);
    border-color:rgba(255,255,255,.5);
  }

  .hiw-track{height:430vh;}
  /* portrait crops to a narrow slice of a 1.76:1 map, and the slice that
     lands by default is empty streets — this frames it on the group */
  .hiw-map{--map-pan:150px;}
  .hiw-h2{font-size:clamp(25px,6.4vw,34px);margin-top:22px;}
  .feature-row{gap:16px 20px;margin-top:22px;}
  .feature-row li{gap:12px;}
  .ft-icon{width:42px;height:42px;border-radius:12px;}
  .ft-icon svg{width:21px;height:21px;}
  .feature-row h3{font-size:var(--fs-sm);}
  .feature-row p{font-size:var(--fs-sm);}
  .hiw-feature-card{padding:24px 0 26px;}
  .hiw-feature-card .hiw-h2{margin-top:10px;}
  .step-pin{width:40px;height:52px;}
  .step-pin span{font-size:21px;padding-top:9px;}
  .hiw-dots{right:12px;}
  .hiw-cue{bottom:78px;}
  /* Keyed off the eyebrow's own top rather than a flat number. The eyebrow sits
     at nav-h + clamp(22px,5vh,52px), so on a short phone it rides up and on a
     tall one it drops — a fixed card offset let it collide at some heights and
     not others. + 34px is the eyebrow's ~15px line box plus a 19px gap. */
  .hiw-card{margin-top:calc(var(--nav-h) + clamp(22px,5vh,52px) + 34px);padding:24px 0 26px;}

  .panel-navy{min-height:auto;}
  .has-photo .panel-photo{
    background-image:
      linear-gradient(180deg, rgba(35,54,108,.62) 0%, rgba(35,54,108,.54) 45%, rgba(35,54,108,.72) 100%),
      var(--partner-photo),
      linear-gradient(180deg,#1d2d5a,#172447);
  }
  .partner-marks{grid-template-columns:repeat(2,minmax(0,1fr));gap:18px;}
  /* one column: the art follows the CTA in the DOM, so it simply falls
     below it and there is still nothing for the pin to collide with */
  .beta-grid{grid-template-columns:minmax(0,1fr);gap:34px;}
  .beta-art{aspect-ratio:16 / 10;}
  .modal{padding:36px 24px 28px;}
  .modal-logo{height:46px;margin-bottom:18px;}
  .footer-top{grid-template-columns:1fr;gap:30px;}
    /* Sizing off the height letterboxes the photo the moment the section
     gets short, so size off the WIDTH instead: never narrower than the
     column, and on a phone it scales past it to 620px so the riders and
     the pins stay legible — the overflow crops off the left, which is
     empty road, and the position keeps all four pins in frame. */
  .panel-usecases{padding-bottom:clamp(210px,30vh,290px);}
  .panel-usecases .panel-photo{
    background-size:cover,max(100%,620px) auto;
    background-position:center,84% bottom;
    background-repeat:no-repeat,no-repeat;
    background-image:
      linear-gradient(180deg,
        rgba(255,255,255,.80) 0%,
        rgba(255,255,255,.66) 46%,
        rgba(255,255,255,.22) 66%,
        rgba(255,255,255,0) 82%),
      var(--usecase-photo);
  }

  /* Interior pages on a phone: the band tightens up, and the title takes the
     same reduced ceiling the hero title takes here. */
  .page-head{padding:calc(var(--nav-h) + 30px) 0 36px;}
  .page-head h1{font-size:clamp(42px,5vw,56px);}
  .page-head-lede{font-size:var(--fs-body);}
  .doc-rail{padding:18px 18px 20px;}
}

/* short-and-wide screens: keep the pinned stage from crowding */
@media (max-height:760px) and (min-width:761px){
  :root{--nav-h:90px;}
  .brand img{height:54px;}
  .hiw-h2{margin-top:18px;font-size:clamp(26px,3.2vw,38px);}
  .hiw-lede{margin-top:10px;}
  .feature-row{margin-top:18px;gap:14px 26px;}
  .ft-icon{width:46px;height:46px;}
  .hiw-feature-card{padding:22px 0 24px;}
  .step-row{margin-top:24px;}
  /* same reasoning as the ≤760 rule — the eyebrow moves with 5vh, so the card
     has to move with it or it swallows the eyebrow on short viewports */
  .hiw-card{margin-top:calc(var(--nav-h) + clamp(22px,5vh,52px) + 34px);padding:24px 0 26px;}
}

/* ============================================================
   REDUCED MOTION — unpin everything, show all three stages
   ============================================================ */
@media (prefers-reduced-motion:reduce){
  html{scroll-behavior:auto;}
  .r{opacity:1;transform:none;transition:none;}
  .hero-scroll-cue,.hiw-cue{animation:none;}
  .modal-overlay,.modal{transition:none;}
  .modal{transform:none;}

  /* Drop the hero clip entirely rather than freezing it — the poster is the
     same first frame, and the navy fallback is doing most of the work anyway.
     main.js also calls pause() so the file stops decoding. */
  .hero-video{display:none;}

  .hiw-track{height:auto;}
  .hiw-stage{position:static;height:auto;padding:60px 0;}
  .hiw-map{position:absolute;inset:0;opacity:.18;transform:none;}
  .map-heavy,.map-mid{opacity:0;}
  .map-overlay{filter:none;opacity:1;}
  .hiw-veil{display:none;}
  .hiw-panel{
    position:relative;inset:auto;opacity:1;visibility:visible;transform:none;
    pointer-events:auto;padding:44px 0;
  }
  .hiw-card{margin-top:0;background:transparent;backdrop-filter:none;}
  /* unpinned, the map is already at .18 opacity, so the scrim would only
     muddy the cards sitting on it */
  .hiw-scrim{display:none;}
  .hiw-panel-4{padding:44px 0;}
  .hiw-feature-card{background:transparent;backdrop-filter:none;width:100%;padding:0;}
  .hiw-chatbar,.hiw-dots,.map-ui{display:none;}
  .hiw-eyebrow.floating{position:static;padding:0 var(--gut);}
}

/* ============================================================
   PRINT
   Legal pages get printed and saved as PDF, so give them a
   readable sheet: no fixed nav floating over the text, no
   contents rail (page numbers are what a printout navigates
   by), and ink on white.
   ============================================================ */
@media print{
  header.site,.site-footer,.modal-overlay,.doc-toc,.skip-link{display:none !important;}
  body{background:#fff;color:#000;}
  .r{opacity:1;transform:none;}
  .page-head{
    background:none;color:#000;
    padding:0 0 18px;border-bottom:1px solid #999;
  }
  .page-head h1{font-size:26pt;}
  .page-head-lede{color:#333;}
  .doc-meta{color:#555;}
  .doc{padding:18px 0 0;}
  .doc-grid{display:block;}
  .doc-body{max-width:none;}
  .doc-section{
    padding-top:16px;border-top:1px solid #ddd;
    break-inside:avoid;page-break-inside:avoid;
  }
  .doc-section h2,.doc-section h3{break-after:avoid;page-break-after:avoid;}
  .doc-card{border:1px solid #bbb;}
  .doc-table-wrap{overflow:visible;}
  .doc-table{min-width:0;}
  .doc-actions{display:none;}
  .doc-section p,.doc-list li,.doc-address{color:#222;}
  .doc-section h2::before,.doc-list li::before{color:#000;}
  .doc-list li::before{background:#000;}
  .doc-note{background:none;border-left:2px solid #000;}
  /* a printed page cannot be clicked, so spell the destination out */
  .doc-section a[href^="http"]::after,.doc-section a[href^="mailto"]::after{
    content:" (" attr(href) ")";font-size:9pt;color:#555;
  }
}
