:root {
  /* Color — one primary (navy), one accent (cyan), one reserved secondary (amber), used
     deliberately rather than decoratively. --cyan itself fails text contrast on white/navy at
     small sizes, so it's a fill color only; --cyan-ink is the text-safe variant. */
  --navy: #102A3A;
  --navy-deep: #0A1D29;
  --navy-line: #24404F;
  --cyan: #00A6A6;
  --cyan-deep: #009494;
  --cyan-ink: #007373;
  --amber: #F59E0B;
  --ink: #111827;
  --ink-soft: #4B5563;
  --paper: #FFFFFF;
  --surface: #F4F8FA;
  --surface-line: #DCEAED;
  --max-width: 1160px;

  /* Type — an editorial serif for headings over a clean geometric sans for everything else.
     The serif carries the "finished" feel; a single italic word per heading, in the accent
     color, is the one flourish. Both load from Google Fonts in <head>, with metric-similar
     system fallbacks if that request fails. */
  --font-display: 'Playfair Display', Georgia, 'Times New Roman', serif;
  --font-body: 'Manrope', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  --text-xs: .75rem;
  --text-sm: .875rem;
  --text-base: clamp(1rem, .94rem + .3vw, 1.0625rem);
  --text-lg: clamp(1.125rem, 1.05rem + .4vw, 1.3125rem);
  --text-xl: clamp(1.375rem, 1.2rem + .9vw, 1.75rem);
  --text-3xl: clamp(2rem, 1.55rem + 2.3vw, 3.1rem);
  --text-hero: clamp(2.6rem, 1.5rem + 4.4vw, 4.6rem);

  /* Spacing — 8px base scale */
  --sp-1: 4px;
  --sp-2: 8px;
  --sp-3: 12px;
  --sp-4: 16px;
  --sp-5: 24px;
  --sp-6: 32px;
  --sp-7: 48px;
  --sp-8: 64px;
  --sp-9: 96px;
  --section-pad: clamp(4.5rem, 3rem + 5vw, 8rem);

  /* Radius */
  --radius-sm: 8px;
  --radius-md: 14px;
  --radius-lg: 22px;
  --radius-xl: 28px;
  --radius-pill: 999px;

  /* Shadow — two layers each: a tight contact shadow plus a wide, soft, offset one. A single
     blurred layer reads flat; the pair is what makes cards look lifted off the page. */
  --shadow-soft: 0 2px 8px rgba(16, 42, 58, .06), 0 16px 40px -18px rgba(16, 42, 58, .18);
  --shadow-lift: 0 4px 14px rgba(16, 42, 58, .08), 0 28px 60px -24px rgba(16, 42, 58, .28);
  --shadow-phone: 0 30px 60px -20px rgba(4, 14, 20, .65), 0 0 0 1px rgba(255, 255, 255, .06);

  /* Motion */
  --ease-out-expo: cubic-bezier(.16, 1, .3, 1);
  --ease-spring: cubic-bezier(.34, 1.4, .44, 1);
  --dur-fast: .16s;
  --dur-normal: .32s;
  --dur-slow: .7s;
}

* { box-sizing: border-box; }
html { scroll-behavior: smooth; }
body {
  margin: 0;
  font-family: var(--font-body);
  font-size: var(--text-base);
  color: var(--ink);
  background: var(--paper);
  line-height: 1.65;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}
img, svg { max-width: 100%; display: block; }
a { color: inherit; }
::selection { background: var(--cyan); color: var(--navy-deep); }
:focus-visible { outline: 2px solid var(--cyan); outline-offset: 3px; border-radius: 4px; }
.container {
  max-width: var(--max-width);
  margin: 0 auto;
  padding: 0 clamp(20px, 4vw, 40px);
}

/* Shared heading voice. font-weight 560 relies on Playfair being a variable font — it sits
   between Regular and Bold, which keeps large serif headlines elegant rather than heavy. */
h1, h2, h3, h4 {
  font-family: var(--font-display);
  font-weight: 560;
  line-height: 1.12;
  text-wrap: balance;
}
h1 em, h2 em, h3 em {
  font-style: italic;
  color: var(--cyan-ink);
}

/* Small tracked label above a heading, led by a short rule. Replaces the old centered
   "eyebrow" — the rule plus wide tracking is most of what makes section openings look
   deliberate. */
.kicker {
  display: inline-flex;
  align-items: center;
  gap: .7rem;
  font-size: var(--text-xs);
  font-weight: 700;
  letter-spacing: .22em;
  text-transform: uppercase;
  color: var(--cyan-ink);
}
.kicker::before {
  content: '';
  width: 26px;
  height: 2px;
  border-radius: 2px;
  background: currentColor;
}
.kicker.on-navy { color: var(--cyan); }

/* Faint road-sign texture for navy sections — masked so it fades out instead of ending in a
   hard edge. The parent must be position:relative with overflow hidden. */
.pattern {
  position: absolute;
  inset: 0;
  pointer-events: none;
  background-image: url(pattern-road.svg?v=20260923b);
  background-size: 96px;
  opacity: .05;
  -webkit-mask-image: linear-gradient(to bottom, #000 0%, transparent 80%);
  mask-image: linear-gradient(to bottom, #000 0%, transparent 80%);
}

/* Skip-to-content link — off-screen until focused, for keyboard users re-visiting the page
   past the sticky header's nav links. navy-deep-on-cyan matches the existing AA-safe CTA
   treatment, so it reads correctly in both light and dark mode without a separate override. */
.skip-link {
  position: absolute;
  top: -48px;
  left: 8px;
  z-index: 100;
  background: var(--cyan);
  color: var(--navy-deep);
  padding: 10px 16px;
  border-radius: var(--radius-sm);
  font-weight: 700;
  font-size: 14px;
  text-decoration: none;
  transition: top .15s ease;
}
.skip-link:focus {
  top: 8px;
}

/* Header */
.site-header {
  position: sticky;
  top: 0;
  z-index: 10;
  background: var(--navy);
  color: #fff;
  transition: background var(--dur-normal) ease, box-shadow var(--dur-normal) ease;
}
/* Added by assets/scroll-effects.js once the page scrolls past a small threshold; with JS
   disabled the header simply stays the flat navy bar it always was. */
.site-header.is-scrolled {
  background: rgba(16, 42, 58, 0.86);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  box-shadow: 0 1px 0 rgba(255, 255, 255, .06), 0 12px 30px -22px rgba(0, 0, 0, .6);
}
.site-header .container {
  display: flex;
  align-items: center;
  justify-content: space-between;
  height: 72px;
}
.brand {
  display: flex;
  align-items: center;
  gap: 12px;
  text-decoration: none;
  color: #fff;
}
.brand-text { display: flex; flex-direction: column; line-height: 1.1; }
.brand-name {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 21px;
  letter-spacing: -.005em;
}
.brand-tag {
  margin-top: 3px;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: .18em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, .6);
}
.nav-links {
  display: flex;
  gap: 30px;
  list-style: none;
  margin: 0;
  padding: 0;
}
.nav-links a {
  position: relative;
  text-decoration: none;
  color: rgba(255, 255, 255, 0.82);
  font-size: 14px;
  font-weight: 600;
  transition: color var(--dur-fast) ease;
}
/* Underline that draws in from the left on hover — desktop only (the mobile dropdown uses
   full-width rows instead, see the 860px media query). */
@media (min-width: 861px) {
  .nav-links a:not(.nav-cta)::after {
    content: '';
    position: absolute;
    left: 0;
    right: 0;
    bottom: -6px;
    height: 2px;
    border-radius: 2px;
    background: var(--cyan);
    transform: scaleX(0);
    transform-origin: left;
    transition: transform var(--dur-normal) var(--ease-out-expo);
  }
  .nav-links a:not(.nav-cta):hover::after { transform: scaleX(1); }
}
.nav-links a:hover { color: #fff; }
.nav-cta {
  background: var(--cyan);
  /* navy-deep-on-cyan ≈5.7:1; the previous white-on-cyan was ≈3.0:1, failing AA */
  color: var(--navy-deep) !important;
  padding: 10px 20px;
  font-size: 14px;
  box-shadow: 0 10px 24px -12px rgba(0, 166, 166, .7);
}
.nav-cta:hover { background: var(--cyan-deep); }
/* The in-menu CTA only exists for the mobile dropdown (see the 860px media query below) —
   hidden by default so it doesn't show as a stray extra item in the desktop nav list. */
.nav-cta-item { display: none; }

/* CSS-only mobile menu — no JS. Checkbox-driven, degrades to "no menu, scroll instead" if
   the browser is unusual enough not to support :checked, which is effectively never.
   display:none here (desktop default, where the hamburger is never shown — see
   .nav-toggle-label below) intentionally keeps the checkbox out of the desktop tab order;
   the mobile breakpoint below swaps this for a visually-hidden-but-focusable pattern once the
   checkbox is the mobile menu's real control. */
.nav-toggle-input { display: none; }
.nav-toggle-label {
  display: none;
  flex-direction: column;
  gap: 4px;
  cursor: pointer;
  padding: 8px;
}
.nav-toggle-label span {
  width: 20px;
  height: 2px;
  background: #fff;
  border-radius: 2px;
}
/* The label itself isn't focusable (the checkbox is) — reflect the checkbox's real keyboard
   focus onto its visible sibling label so keyboard users can see where focus is. */
.nav-toggle-input:focus-visible ~ .nav-toggle-label {
  outline: 2px solid #fff;
  outline-offset: 2px;
}

/* Buttons */
.btn {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  padding: 14px 26px;
  border-radius: var(--radius-pill);
  font-family: var(--font-body);
  font-weight: 700;
  font-size: var(--text-sm);
  letter-spacing: .02em;
  text-decoration: none;
  border: 1.5px solid transparent;
  transition:
    transform var(--dur-fast) var(--ease-out-expo),
    box-shadow var(--dur-fast) var(--ease-out-expo),
    background-color var(--dur-fast) ease,
    border-color var(--dur-fast) ease;
}
.btn svg { transition: transform var(--dur-normal) var(--ease-out-expo); }
.btn:hover { transform: translateY(-2px); }
.btn:hover svg { transform: translateX(3px); }
/* Tactile "press" micro-interaction on every button; disabled under reduced-motion below. */
.btn:active { transform: scale(0.97); }
/* navy-deep-on-cyan reads clearly (~5.7:1); white-on-cyan measured ~3.0:1, failing AA */
.btn-primary {
  background: var(--cyan);
  color: var(--navy-deep);
  box-shadow: 0 10px 24px -10px rgba(0, 166, 166, .65);
}
.btn-primary:hover {
  background: var(--cyan-deep);
  box-shadow: 0 16px 30px -10px rgba(0, 148, 148, .6);
}
.btn-secondary {
  background: transparent;
  color: #fff;
  border-color: rgba(255, 255, 255, 0.3);
}
.btn-secondary:hover { border-color: #fff; }
/* Modifier for using .btn-secondary on a light section — replaces an old inline-style hack
   that had no working hover state (it inherited the dark-bg hover, invisible on white). */
.btn-secondary.on-light { color: var(--navy); border-color: var(--surface-line); }
.btn-secondary.on-light:hover { border-color: var(--navy); background: var(--surface); }

/* Hero */
.hero {
  position: relative;
  overflow: hidden;
  background:
    radial-gradient(60% 70% at 85% 30%, rgba(0, 166, 166, .16), transparent 70%),
    linear-gradient(160deg, var(--navy) 0%, var(--navy-deep) 100%);
  color: #fff;
  padding: clamp(64px, 5vw + 32px, 112px) 0 clamp(80px, 6vw + 40px, 136px);
}
.hero .container {
  position: relative;
  display: grid;
  grid-template-columns: 1.15fr 0.85fr;
  gap: clamp(40px, 5vw, 80px);
  align-items: center;
}
.hero h1 {
  font-size: var(--text-hero);
  line-height: 1.04;
  letter-spacing: -.025em;
  margin: 22px 0 26px;
}
.hero h1 em { color: var(--cyan); }
.hero p.lede {
  font-size: var(--text-lg);
  line-height: 1.7;
  color: rgba(255, 255, 255, 0.78);
  max-width: 54ch;
  margin: 0 0 34px;
}
.hero-actions { display: flex; gap: 14px; flex-wrap: wrap; }
.hero-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  list-style: none;
  margin: 28px 0 0;
  padding: 0;
}
.hero-chips li {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 7px 14px;
  border-radius: var(--radius-pill);
  border: 1px solid rgba(255, 255, 255, .14);
  background: rgba(255, 255, 255, .04);
  font-size: var(--text-xs);
  font-weight: 700;
  letter-spacing: .02em;
  color: rgba(255, 255, 255, .85);
}
.hero-chips svg { color: var(--cyan); }
.hero-disclaimer {
  margin: 22px 0 0;
  font-size: 13px;
  line-height: 1.55;
  color: rgba(255, 255, 255, 0.5);
  max-width: 50ch;
}
.hero-visual {
  /* Explicit width (not just max-width) matters here: .hero-visual is a grid item with
     auto margins, and auto margins absorb stretch space per the box-alignment spec — so
     without a definite width it falls back to shrink-to-fit sizing from its content. That
     was invisible before (a single in-flow ~810px-intrinsic <img> made the shrink-to-fit
     width comfortably exceed the cap, so the cap always won) but broke once the hero became a
     carousel of position:absolute slides with zero in-flow content to size against —
     confirmed live: the phone frame collapsed to ~56px. width:100% gives it a real basis to
     clamp from instead of relying on stretch that was never actually happening. */
  width: 100%;
  max-width: 270px;
  margin: 0 auto;
  position: relative;
}
/* Soft accent glow behind the phone — adds depth without a new image asset. */
.hero-visual::before {
  content: '';
  position: absolute;
  inset: -20% -30%;
  background: radial-gradient(closest-side, rgba(0, 166, 166, 0.28), transparent 70%);
  filter: blur(24px);
  z-index: -1;
}
/* Floating summary cards that overlap the phone's edges — the white-on-navy contrast and the
   slight overlap are what give the hero its layered, composed look. */
.float-card {
  position: absolute;
  z-index: 3;
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 14px 18px;
  border-radius: var(--radius-md);
  background: #fff;
  color: var(--ink);
  border: 1px solid var(--surface-line);
  box-shadow: 0 4px 14px rgba(4, 14, 20, .18), 0 28px 50px -20px rgba(4, 14, 20, .55);
  white-space: nowrap;
}
.float-card strong {
  display: block;
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 17px;
  line-height: 1.2;
  color: var(--navy);
}
.float-card span { font-size: 12px; color: var(--ink-soft); }
.float-card > span:last-child > span { display: block; }
.float-card--top { top: 34%; left: -48%; flex-direction: column; align-items: flex-start; gap: 2px; }
.float-card--bottom { bottom: 16%; right: -42%; }
.float-card__icon {
  display: grid;
  place-items: center;
  width: 36px;
  height: 36px;
  border-radius: var(--radius-pill);
  background: rgba(0, 166, 166, .12);
  color: var(--cyan-ink) !important;
  flex-shrink: 0;
}
/* Gentle idle float, offset between the two cards so they never move in lockstep. */
@keyframes float-drift {
  0%, 100% { transform: translateY(0); }
  50% { transform: translateY(-6px); }
}
.float-card--top { animation: float-drift 6s ease-in-out infinite; }
.float-card--bottom { animation: float-drift 6s ease-in-out -3s infinite; }

/* Real device screenshots, framed in a phone bezel. All gallery source images share the
   same 9:20 crop, so the frame's aspect-ratio matches them exactly — no forced cropping. */
.phone-frame {
  background: var(--navy-deep);
  border-radius: 28px;
  padding: 9px;
  box-shadow: var(--shadow-phone);
  position: relative;
}
.phone-frame .screen {
  border-radius: 20px;
  overflow: hidden;
  aspect-ratio: 9 / 20;
  background: #16324a;
}
.phone-frame .screen img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: top;
  display: block;
}

/* Modifier for source images that already have a real Android device frame baked in
   (bezel, punch-hole camera, side buttons) — drawing our own bezel on top would double it up,
   so this strips the CSS bezel down to a plain shadow and lets the photo's own frame show. */
.phone-frame.native-frame {
  background: transparent;
  padding: 0;
  box-shadow: none;
}
.phone-frame.native-frame .screen {
  aspect-ratio: auto;
  background: transparent;
}
.phone-frame.native-frame .screen img {
  height: auto;
  object-fit: contain;
  box-shadow: var(--shadow-phone);
}

/* Hero carousel — replaces a single static screenshot with a small auto-advancing preview of
   4 real screens, cross-fading with a subtle "settle" scale so it reads as alive rather than
   a parked image. Falls back to a single static slide (hero.jpg) with JS off. */
.hero-carousel { position: relative; }
.hero-carousel .slide {
  position: absolute;
  inset: 0;
  opacity: 0;
  transform: scale(1.02);
  transition: opacity .6s ease, transform .6s ease;
  z-index: 1;
}
.hero-carousel .slide.is-active {
  opacity: 1;
  transform: scale(1);
  z-index: 2;
}

.hero-dots {
  display: flex;
  justify-content: center;
  gap: 8px;
  margin-top: var(--sp-5);
}
.hero-dot {
  position: relative;
  width: 8px;
  height: 8px;
  border-radius: var(--radius-pill);
  border: none;
  padding: 0;
  background: rgba(255, 255, 255, 0.4);
  cursor: pointer;
  transition: background .2s ease, width var(--dur-normal) var(--ease-out-expo);
}
/* Enlarges the actual hit area to ~24x24 without growing the visible dot or overlapping
   neighbors (each ::before is centered on its own dot, independent of the 8px gap between
   dots — a padding-based hit area would have overlapped here). */
.hero-dot::before {
  content: '';
  position: absolute;
  inset: -8px;
}
.hero-dot:hover { background: rgba(255, 255, 255, 0.7); }
/* Active dot stretches into a short pill rather than just scaling up. */
.hero-dot.is-active {
  background: var(--cyan);
  width: 22px;
}
.hero-dot:focus-visible {
  outline: 2px solid #fff;
  outline-offset: 2px;
}

/* Sections */
section { padding: var(--section-pad) 0; }
.section-heading {
  max-width: 760px;
  margin: 0 0 clamp(40px, 4vw, 64px);
}
.section-heading h2 {
  font-size: var(--text-3xl);
  letter-spacing: -.018em;
  margin: 18px 0 18px;
  color: var(--navy);
}
.section-heading p {
  color: var(--ink-soft);
  font-size: var(--text-lg);
  line-height: 1.65;
  margin: 0;
  max-width: 62ch;
}

/* Scroll-reveal (fade + rise) — .is-in is added by assets/scroll-effects.js once the element
   enters the viewport, with a per-sibling --reveal-delay so grids cascade in rather than
   popping as one block. Gated under the .js ancestor class (flipped from .no-js on <html> by
   an inline script in <head>, before first paint) so the pre-reveal hidden state can never
   apply with JS disabled or blocked — content just renders visible immediately instead of
   risking staying invisible forever. The prefers-reduced-motion override further down is what
   matters for JS-enabled users who've asked for less motion. */
:is(.section-heading, .feature-card, .screenshot-card, .step, .data-note) {
  transition:
    opacity var(--dur-slow) var(--ease-out-expo),
    transform var(--dur-slow) var(--ease-out-expo),
    box-shadow var(--dur-normal) var(--ease-out-expo),
    border-color var(--dur-normal) ease;
  transition-delay: var(--reveal-delay, 0s), var(--reveal-delay, 0s), 0s, 0s;
}
.js :is(.section-heading, .feature-card, .screenshot-card, .step, .data-note) { opacity: 0; transform: translateY(26px); }
.js :is(.section-heading, .feature-card, .screenshot-card, .step, .data-note).is-in { opacity: 1; transform: none; }

/* Features */
.features {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 20px;
}
.feature-card {
  position: relative;
  background: var(--paper);
  border: 1px solid var(--surface-line);
  border-radius: var(--radius-lg);
  padding: 30px 28px 28px;
  box-shadow: var(--shadow-soft);
  overflow: hidden;
}
/* Thin accent rule across the top edge, drawn inside the rounded corners. */
.feature-card::before {
  content: '';
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 3px;
  background: linear-gradient(90deg, var(--cyan), rgba(0, 166, 166, .25));
}
/* Repeated under .js so its specificity ties with .is-in's instead of losing to it outright —
   declared after .is-in, so the tie resolves to hover winning, which is what keeps the lift
   working when a revealed card is hovered. The unscoped copy covers the plain (no-JS) case. */
.feature-card:hover,
.js .feature-card.is-in:hover {
  transform: translateY(-5px);
  box-shadow: var(--shadow-lift);
  border-color: rgba(0, 166, 166, .45);
}
/* One deliberate icon treatment instead of arbitrary accent colors per card — the SVGs use
   stroke/fill="currentColor" so this is the single place their color is set. */
.feature-icon {
  width: 52px;
  height: 52px;
  border-radius: var(--radius-pill);
  display: flex;
  align-items: center;
  justify-content: center;
  margin-bottom: 20px;
  background: rgba(0, 166, 166, .1);
  border: 1px solid rgba(0, 166, 166, .18);
  color: var(--cyan-ink);
  transition: transform var(--dur-normal) var(--ease-spring);
}
.feature-card:hover .feature-icon { transform: translateY(-3px) scale(1.06); }
.feature-card h3 { margin: 0 0 10px; font-size: var(--text-lg); color: var(--navy); }
.feature-card p { margin: 0; color: var(--ink-soft); font-size: 15px; line-height: 1.65; }

/* Screenshot gallery — a horizontally scrollable strip. Fixed card width regardless of
   viewport, so it never balloons on wide screens and behaves like a familiar swipeable
   screenshot gallery on narrow ones. Shows every card in one row when they fit. */
.gallery { background: var(--surface); }
.screenshots-viewport {
  overflow-x: auto;
  scrollbar-width: thin;
  padding-bottom: 6px;
  -webkit-overflow-scrolling: touch;
  /* App-store-like swipe feel instead of drifting to an arbitrary scroll position. */
  scroll-snap-type: x mandatory;
}
.screenshots {
  display: flex;
  gap: 36px;
  width: fit-content;
  min-width: 100%;
  justify-content: center;
  margin: 0 auto;
  padding: 8px 8px 12px;
}
.screenshot-card { text-align: left; flex: 0 0 196px; width: 196px; scroll-snap-align: center; }
.screenshot-card .phone-frame { transition: transform var(--dur-normal) var(--ease-out-expo); }
.screenshot-card:hover .phone-frame { transform: translateY(-6px); }
.screenshot-index {
  display: block;
  margin: 22px 0 6px;
  font-size: var(--text-xs);
  font-weight: 800;
  letter-spacing: .18em;
  color: var(--cyan-ink);
  font-variant-numeric: tabular-nums;
}
.screenshot-card h4 { margin: 0 0 4px; font-size: 18px; color: var(--navy); }
.screenshot-card p { margin: 0; font-size: 14px; line-height: 1.55; color: var(--ink-soft); }

/* How it works — ringed serif numerals on a dashed track, each over its own card, so the
   four steps read as one ordered route rather than four unrelated tiles. */
.steps {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 20px;
  position: relative;
  list-style: none;
  margin: 0;
  padding: 0;
}
@media (min-width: 861px) {
  .steps::before {
    content: '';
    position: absolute;
    top: 27px;
    left: 4%;
    right: 4%;
    height: 2px;
    background: repeating-linear-gradient(to right, var(--surface-line) 0 10px, transparent 10px 18px);
    z-index: 0;
  }
}
.step {
  display: flex;
  flex-direction: column;
  gap: 18px;
}
.step-number {
  width: 56px;
  height: 56px;
  border-radius: 50%;
  background: var(--paper);
  border: 2px solid var(--cyan);
  color: var(--cyan-ink);
  display: grid;
  place-items: center;
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 1.4rem;
  position: relative;
  z-index: 1;
  box-shadow: 0 0 0 6px var(--paper);
  transition: background var(--dur-normal) ease, color var(--dur-normal) ease;
}
.step:hover .step-number { background: var(--cyan); color: var(--navy-deep); }
.step-body {
  flex: 1;
  background: var(--paper);
  border: 1px solid var(--surface-line);
  border-radius: var(--radius-md);
  padding: 22px 22px 24px;
  box-shadow: var(--shadow-soft);
  transition: transform var(--dur-normal) var(--ease-out-expo), border-color var(--dur-normal) ease;
}
.step:hover .step-body { transform: translateY(-5px); border-color: var(--cyan); }
.step-label {
  display: block;
  margin-bottom: 8px;
  font-size: var(--text-xs);
  font-weight: 800;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--cyan-ink);
}
.step h4 { margin: 0 0 8px; font-size: 19px; color: var(--navy); }
.step p { margin: 0; font-size: 14.5px; line-height: 1.6; color: var(--ink-soft); }

/* Data note */
#data { padding-bottom: calc(var(--section-pad) * .5); }
.data-note {
  position: relative;
  overflow: hidden;
  background:
    radial-gradient(70% 90% at 100% 0%, rgba(0, 166, 166, .18), transparent 65%),
    var(--navy);
  color: #fff;
  border-radius: var(--radius-xl);
  padding: clamp(32px, 5vw, 64px);
  display: grid;
  grid-template-columns: 1.2fr 0.8fr;
  gap: clamp(28px, 4vw, 56px);
  align-items: center;
  box-shadow: var(--shadow-lift);
  border-top: 3px solid var(--cyan);
}
.data-note > div:not(.pattern) { position: relative; }
.data-note h3 { margin: 16px 0 14px; font-size: var(--text-xl); font-weight: 560; }
.data-note h3 em { color: var(--cyan); }
.data-note p { margin: 0; color: rgba(255,255,255,0.76); font-size: 15px; line-height: 1.7; }
.data-note .badges { display: flex; gap: 10px; flex-wrap: wrap; }
.badge {
  font-size: 12px;
  font-weight: 700;
  letter-spacing: .02em;
  background: rgba(255,255,255,0.06);
  border: 1px solid rgba(255,255,255,0.18);
  padding: 7px 14px;
  border-radius: 999px;
}

.privacy-strip { padding-top: calc(var(--section-pad) * .5); }
.privacy-strip .container {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 32px;
  flex-wrap: wrap;
}
.privacy-strip .section-heading { margin-bottom: 0; }
.privacy-strip .section-heading p {
  border-left: 3px solid var(--cyan);
  padding-left: 20px;
}

/* Closing CTA — bookends the hero (dark → light → dark rhythm) instead of ending the page
   on a bare heading and a static badge. */
.closing {
  position: relative;
  overflow: hidden;
  background:
    radial-gradient(50% 80% at 50% 0%, rgba(0, 166, 166, .18), transparent 70%),
    linear-gradient(160deg, var(--navy) 0%, var(--navy-deep) 100%);
  color: #fff;
  text-align: center;
}
.closing .container { position: relative; }
.closing-mark { margin: 0 auto var(--sp-5); }
.closing h2 {
  font-size: var(--text-3xl);
  letter-spacing: -.018em;
  color: #fff;
  margin: 18px 0 18px;
}
.closing h2 em { color: var(--cyan); }
.closing p {
  color: rgba(255, 255, 255, 0.74);
  font-size: var(--text-lg);
  max-width: 50ch;
  margin: 0 auto var(--sp-6);
}
.badge-store {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  color: var(--navy-deep);
  background: var(--amber);
  border: none;
  padding: 12px 22px;
  font-size: 14px;
  box-shadow: 0 12px 28px -12px rgba(245, 158, 11, .7);
}

/* Footer */
footer {
  background: var(--navy-deep);
  color: rgba(255,255,255,0.6);
  padding: clamp(56px, 6vw, 80px) 0 32px;
  font-size: 14px;
  border-top: 1px solid rgba(255, 255, 255, .06);
}
.footer-grid {
  display: grid;
  grid-template-columns: 2fr 1fr 1fr;
  gap: 40px;
}
.footer-brand p { margin: 18px 0 0; max-width: 38ch; line-height: 1.65; }
.footer-title {
  margin: 6px 0 18px;
  font-family: var(--font-body);
  font-size: var(--text-xs);
  font-weight: 700;
  letter-spacing: .2em;
  text-transform: uppercase;
  color: var(--cyan);
}
footer a { color: rgba(255,255,255,0.75); text-decoration: none; transition: color var(--dur-fast) ease; }
footer a:hover { color: #fff; }
.footer-links { display: flex; flex-direction: column; gap: 12px; list-style: none; padding: 0; margin: 0; }
.footer-bottom {
  margin-top: 48px;
  padding-top: 24px;
  border-top: 1px solid rgba(255, 255, 255, .08);
  font-size: 13px;
  color: rgba(255,255,255,0.5);
}
/* privacy.html still uses the original single-row footer (text + one link) — keep it laid
   out as a row there rather than inheriting the landing page's column grid. */
footer .container.footer-row {
  display: flex;
  justify-content: space-between;
  align-items: center;
  flex-wrap: wrap;
  gap: 16px;
}
footer .footer-row .footer-links { flex-direction: row; gap: 20px; }

/* Privacy page */
.doc {
  max-width: 740px;
  margin: 0 auto;
  padding: 72px 24px 96px;
}
.doc h1 {
  font-size: clamp(2.2rem, 1.6rem + 2.6vw, 3.2rem);
  letter-spacing: -.02em;
  color: var(--navy);
  margin: 18px 0 28px;
}
.doc h2 { color: var(--navy); font-size: var(--text-xl); margin-top: 44px; margin-bottom: 12px; }
.doc p { color: var(--ink-soft); line-height: 1.75; }
/* Same cyan-as-text-on-white contrast issue fixed elsewhere on the kicker label */
.doc .updated {
  color: var(--cyan-ink);
  font-weight: 700;
  font-size: var(--text-xs);
  text-transform: uppercase;
  letter-spacing: .22em;
  display: inline-flex;
  align-items: center;
  gap: .7rem;
}
.doc .updated::before { content: ''; width: 26px; height: 2px; border-radius: 2px; background: currentColor; }
/* Was a hardcoded inline style using tokens that never repaint for dark mode -- confirmed
   live via headless render, it turned into a near-illegible near-white-on-near-white box in
   dark mode. Given its own class with a dark-mode override below instead. */
.doc-note {
  margin-top: 40px;
  padding: 20px 22px;
  background: var(--surface);
  border: 1px solid var(--surface-line);
  border-left: 3px solid var(--cyan);
  border-radius: var(--radius-md);
  color: var(--ink-soft);
}

/* Desktop hero fits the first screen — kicker, headline, lede, buttons, chips, disclaimer, and
   the phone all visible without scrolling. Width-only sizing overflowed every common laptop
   window (measured: 74px/4-line headline, content ending ~1040px down — 50px past a
   1920x1000 window and 314px past 1280x720), so type, spacing, and the phone also scale
   with viewport *height* here, and whichever of the two limits is tighter wins. svh (with a
   vh fallback) keeps mobile-style browser toolbars from being counted as usable height. */
@media (min-width: 861px) {
  .hero {
    display: flex;
    align-items: center;
    min-height: calc(100vh - 72px);
    min-height: calc(100svh - 72px);
    padding: clamp(28px, 6vh, 80px) 0;
  }
  .hero .container {
    width: 100%;
    grid-template-columns: 1.4fr 0.6fr;
  }
  .hero h1 {
    font-size: min(clamp(2.5rem, 1.3rem + 3.4vw, 4.25rem), 8.2vh);
    font-size: min(clamp(2.5rem, 1.3rem + 3.4vw, 4.25rem), 8.2svh);
    margin: clamp(12px, 2.2vh, 22px) 0 clamp(14px, 2.6vh, 24px);
  }
  .hero p.lede {
    font-size: clamp(1rem, .45rem + 1.35vh, 1.25rem);
    margin-bottom: clamp(18px, 3.4vh, 32px);
  }
  .hero-chips { margin-top: clamp(14px, 2.6vh, 24px); }
  .hero-disclaimer { margin-top: clamp(10px, 2vh, 18px); }
  /* The phone is ~2.25x as tall as it is wide (9:20 screen + bezel) plus the dots row, so this
     caps its width at whatever keeps the whole thing inside the padded hero height. */
  .hero-visual {
    max-width: min(270px, calc((100vh - 72px - 12vh - 56px) * .44));
    max-width: min(270px, calc((100svh - 72px - 12svh - 56px) * .44));
  }
  .hero-dots { margin-top: clamp(12px, 2vh, 24px); }
}

@media (max-width: 1100px) {
  /* Not enough room beside the phone for both cards to hang off its edges — tuck them in so
     they overlap the screen instead of running into the copy column. */
  .float-card--top { left: -30%; }
  .float-card--bottom { right: -26%; }
}

@media (max-width: 860px) {
  .hero .container { grid-template-columns: 1fr; }
  .hero-visual { order: -1; max-width: 220px; }
  .float-card--top { left: -46%; top: 34%; }
  .float-card--bottom { right: -46%; bottom: 20%; }
  .features { grid-template-columns: 1fr 1fr; }
  .steps { grid-template-columns: 1fr 1fr; }
  .data-note { grid-template-columns: 1fr; }
  .footer-grid { grid-template-columns: 1fr 1fr; }
  .footer-brand { grid-column: 1 / -1; }

  .nav-toggle-label { display: flex; }
  /* Below 860px the checkbox is the mobile menu's real, only control — visually hidden
     rather than display:none so it stays focusable. display:none removes an element from the
     tab order and accessibility tree in every browser, so keyboard/screen-reader users could
     never reach or toggle it. */
  .nav-toggle-input {
    display: block;
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
  }
  .nav-links {
    display: flex !important;
    position: absolute;
    top: 72px;
    left: 0;
    right: 0;
    flex-direction: column;
    gap: 0;
    background: var(--navy-deep);
    max-height: 0;
    overflow: hidden;
    transition: max-height .25s ease;
    padding: 0 24px;
  }
  .nav-links li { border-top: 1px solid rgba(255, 255, 255, 0.1); }
  .nav-links a { display: block; padding: 14px 0; }
  .nav-toggle-input:checked ~ nav .nav-links { max-height: 400px; padding: 8px 24px 16px; }

  /* The header CTA and the hamburger button were both competing for space in the same row
     as the brand mark, and the CTA (having the least room to shrink into) wrapped its text
     and overflowed — confirmed live via headless render. Below 860px the header keeps only
     the brand and the hamburger; "Get RoadAware" moves into the dropdown as a highlighted
     item instead. */
  .nav-cta-header { display: none; }
  .nav-cta-item {
    display: block;
    padding: 16px 0 4px;
  }
  .nav-cta-item a.nav-cta {
    display: inline-flex;
    width: 100%;
    justify-content: center;
    padding: 11px 18px;
  }
}
@media (max-width: 560px) {
  .features { grid-template-columns: 1fr; }
  .steps { grid-template-columns: 1fr; }
  .screenshots { justify-content: flex-start; gap: 24px; }
  .screenshot-card { flex-basis: 168px; width: 168px; }
  /* On a phone-width screen the hero phone is small and centered — the cards would cover most
     of it, so they're dropped; the same facts are in the chips and features below. */
  .float-card { display: none; }
  .brand-tag { display: none; }
  .footer-grid { grid-template-columns: 1fr; }
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  .nav-links { transition: none; }
  .hero-carousel .slide { transition: none; transform: none; }
  .float-card { animation: none; }
  /* Scroll-reveal is opt-in JS (assets/scroll-effects.js checks this same media query before
     even attaching its IntersectionObserver) — this override is what actually guarantees
     content stays visible for reduced-motion users even if that script never runs. */
  .js :is(.section-heading, .feature-card, .screenshot-card, .step, .data-note), :is(.section-heading, .feature-card, .screenshot-card, .step, .data-note) {
    opacity: 1;
    transform: none;
    transition: none;
  }
  .feature-card:hover, .js .feature-card.is-in:hover,
  .step:hover .step-body, .screenshot-card:hover .phone-frame,
  .feature-card:hover .feature-icon { transform: none; }
  .site-header { transition: none; }
  .btn, .btn svg { transition: none; }
  .btn:hover, .btn:active, .btn:hover svg { transform: none; }
}

@media (prefers-color-scheme: dark) {
  body { background: #0B1A24; color: #EAF2F6; }
  h1 em, h2 em, h3 em { color: var(--cyan); }
  .kicker, .screenshot-index, .step-label, .doc .updated { color: var(--cyan); }
  .section-heading h2, .feature-card h3, .step h4, .screenshot-card h4,
  .doc h1, .doc h2 { color: #fff; }
  .section-heading p, .feature-card p, .step p, .screenshot-card p, .doc p { color: #A9BCC6; }
  .feature-card, .step-body {
    background: #12222E;
    border-color: #1D3546;
    box-shadow: 0 2px 8px rgba(0, 0, 0, .2), 0 16px 40px -18px rgba(0, 0, 0, .5);
  }
  .feature-icon { background: rgba(0, 166, 166, 0.14); border-color: rgba(0, 166, 166, .25); color: var(--cyan); }
  .gallery { background: #0E1E29; }
  .steps::before { background: repeating-linear-gradient(to right, #1D3546 0 10px, transparent 10px 18px); }
  .step-number { background: #0B1A24; color: var(--cyan); box-shadow: 0 0 0 6px #0B1A24; }
  .step:hover .step-number { color: var(--navy-deep); }
  .data-note { box-shadow: none; }
  .doc-note {
    background: #12222E;
    border-color: #1D3546;
    border-left-color: var(--cyan);
    color: #A9BCC6;
  }
  .doc-note strong { color: #fff; }
  /* .on-light assumes a light section background; .privacy-strip has none of its own, so in
     dark mode it inherits the page's dark background and navy-on-near-black is unreadable
     (confirmed live via headless render). Swap to the same light-on-dark treatment used
     everywhere else in dark mode. */
  .btn-secondary.on-light { color: #fff; border-color: rgba(255, 255, 255, 0.35); }
  .btn-secondary.on-light:hover { border-color: #fff; background: rgba(255, 255, 255, 0.08); }
}
