/*
  site.css: the rateapp.co.uk marketing site stylesheet.

  Design system carried over 1:1 from the Flare landing page
  (web/flare/src/app/globals.css, the .ld- block + tokens), per Nat's brief:
  "very simple, design wise very close to the Flare landing page".
  Dark glass on a near-black ground, ambient green wash, Apple system type,
  green #34c759 accent. Colour values, radii, blur and easing are the Flare
  values verbatim; anything new (legal prose, quote strip, feature grid)
  is built from the same tokens.

  The site is deliberately dark-only (color-scheme: dark), matching Flare
  and the app itself. No frameworks, no build step: this file is served
  as-is next to plain HTML pages.
*/

/* ---------- Design tokens (Flare :root, verbatim) ---------- */
:root {
  --bg: #030303;
  --elev: #1c1c1e;
  --sep: rgba(255, 255, 255, 0.11);
  --hair: rgba(255, 255, 255, 0.06);
  --label: rgba(255, 255, 255, 0.96);
  --label2: rgba(235, 235, 245, 0.78);
  --label3: rgba(235, 235, 245, 0.62);
  --green: #34c759;
  --green2: #3ddb64;
  --red: #ff5c52;
  --fill: rgba(120, 120, 128, 0.24);
  --fill2: rgba(120, 120, 128, 0.16);
  --glass: rgba(255, 255, 255, 0.09);
  --glass2: rgba(255, 255, 255, 0.12);
  --edge: rgba(255, 255, 255, 0.135);
  --edge2: rgba(255, 255, 255, 0.22);
  --panelshadow: 0 2px 10px rgba(0, 0, 0, 0.14);
  --ease: cubic-bezier(0.32, 0.72, 0, 1);
}

/* ---------- Base ---------- */
* {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}
html {
  -webkit-text-size-adjust: 100%;
  color-scheme: dark;
  /* Anchor jumps (#flare) land clear of the sticky sitebar (68px bar
     + 20px gutter + breathing room). */
  scroll-padding-top: 96px;
}
@media (prefers-reduced-motion: no-preference) {
  html {
    scroll-behavior: smooth;
  }
}
body {
  font-family: -apple-system, "SF Pro Display", "SF Pro Text", system-ui,
    Helvetica, Arial, sans-serif;
  background: var(--bg);
  color: var(--label);
  -webkit-font-smoothing: antialiased;
  min-height: 100vh;
  letter-spacing: -0.01em;
}
/* Ambient backdrop: the light source every glass surface samples */
body::before {
  content: "";
  position: fixed;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background: radial-gradient(
      46% 40% at 84% -6%,
      rgba(52, 199, 89, 0.21),
      transparent 64%
    ),
    radial-gradient(38% 34% at 10% 26%, rgba(90, 200, 250, 0.085), transparent 62%),
    radial-gradient(30% 26% at 55% 58%, rgba(52, 199, 89, 0.055), transparent 65%),
    radial-gradient(44% 42% at -8% 112%, rgba(52, 199, 89, 0.13), transparent 62%),
    radial-gradient(30% 30% at 68% 115%, rgba(255, 255, 255, 0.035), transparent 65%),
    #050506;
}
::selection {
  background: rgba(52, 199, 89, 0.3);
}
:focus-visible {
  outline: 2px solid var(--green);
  outline-offset: 2px;
  border-radius: 8px;
}
a {
  color: var(--green2);
}

/* ---------- Buttons (Flare .btn, pill-radiused for the marketing site) ----------
   980px radius is Apple's own capsule value (apple.com CTAs); everything
   else stays Flare. */
.btn {
  background: var(--green);
  color: #04210d;
  font-weight: 600;
  font-size: 15px;
  font-family: inherit;
  padding: 10px 19px;
  border-radius: 980px;
  cursor: pointer;
  border: none;
  display: inline-flex;
  align-items: center;
  gap: 7px;
  transition: transform 0.12s var(--ease), filter 0.15s;
  text-decoration: none;
}
.btn .ic {
  width: 17px;
  height: 17px;
}
.btn:hover {
  filter: brightness(1.08);
}
.btn:active {
  transform: scale(0.97);
}
.btn.gray {
  background: rgba(255, 255, 255, 0.08);
  color: var(--label);
  backdrop-filter: blur(20px);
  -webkit-backdrop-filter: blur(20px);
}
.btn.gray:hover {
  background: rgba(255, 255, 255, 0.3);
  filter: none;
}
.btn.sm {
  padding: 7px 15px;
  font-size: 13.5px;
}

/* ---------- Page shell + nav ---------- */
/* Legal/support/404 pages: single constrained column. The homepage instead
   uses the full-bleed .band zone system below. */
.ld-page {
  max-width: 1060px;
  margin: 0 auto;
  padding: 0 32px 72px;
}

/* ---------- Master cards (bento evolution of the apple.com zoning) ----------
   Apple's separation mechanics (per-unit background + 12px gutters) applied
   as rounded MASTER CARDS floating on the site background, which stays the
   darkest layer of all: each section is a large panel object on the ambient
   ground, exactly the Flare surface philosophy at page scale. Tone ladder
   varies per card; hairline edge + panelshadow give the object read. */
.band {
  width: min(1120px, 100% - 24px);
  /* 20px vertical gutters between cards (Nat, 2026-07-22: larger gaps
     read nicer than the original 12px). The viewport-fill calcs below
     budget 88px (68px sitebar + this gutter). */
  margin: 0 auto 20px;
  border-radius: 28px;
  border: 0.5px solid var(--hair);
  box-shadow: var(--panelshadow);
}
/* First card clears the sitebar by the same 20px gutter. */
.sitebar + .band {
  margin-top: 20px;
}
/* :last-child, not :last-of-type — bands mix header/section/footer tags,
   and last-of-type would drop the gap after the LAST OF EACH TAG. */
.band:last-child {
  margin-bottom: 0;
}
.band .inner {
  max-width: 1060px;
  margin: 0 auto;
  padding-left: 32px;
  padding-right: 32px;
}
/* The tone ladder: every card slightly different, all lighter than the
   ground. Ladder raised +0.025 over the original 0.02-0.05 range (Nat,
   2026-07-22: the first +0.05 raise made the cards pop but read a bit
   much; dialled back by half). Ladder spacing preserved; component-level
   --glass panels keep their pop because their overlay stacks on top of
   the band tone. */
.band.tone-1 {
  background: rgba(255, 255, 255, 0.045);
}
.band.tone-2 {
  background: rgba(255, 255, 255, 0.06);
}
.band.tone-3 {
  background: rgba(255, 255, 255, 0.075);
}
/* Flare's own territory: its tone plus a local green wash, the dark-theme
   cousin of Apple's theme-dark product tiles. */
.band.zone-flare {
  position: relative;
  overflow: hidden;
  background: rgba(255, 255, 255, 0.055);
}
.band.zone-flare::before {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: radial-gradient(52% 46% at 50% 0%, rgba(52, 199, 89, 0.1), transparent 70%);
}
.band.zone-flare .inner {
  position: relative;
}
/* Short utility pages (support, 404, redirect): the card alone fills the
   viewport (sitebar 68px + top gutter 20px accounted for), content centred
   in it; the footer sits below the fold. */
body.page-fill main.band {
  min-height: calc(100svh - 88px);
  display: flex;
}
body.page-fill main.band .inner {
  width: 100%;
  display: flex;
  flex-direction: column;
  justify-content: center;
}

/* The footer is page chrome, not a card: bare on the darkest ground. */
.band.bare {
  width: 100%;
  border: 0;
  border-radius: 0;
  box-shadow: none;
  background: none;
  margin: 20px 0 0;
}
/* Sticky translucent sitebar, the apple.com treatment: saturate(1.8)
   blur(20px) (their exact computed filter), hairline base. 68px tall
   (Nat, 2026-07-22: 30% up from the original 52px slim bar); the
   viewport-fill calcs below budget 80px (bar + 12px gutter) for it.
   position: sticky keeps it in flow, so no body offset is needed. */
.sitebar {
  position: sticky;
  top: 0;
  z-index: 50;
  background: rgba(5, 5, 6, 0.72);
  backdrop-filter: saturate(1.8) blur(20px);
  -webkit-backdrop-filter: saturate(1.8) blur(20px);
  border-bottom: 0.5px solid var(--hair);
}
.sitebar .ld-nav {
  max-width: 1060px;
  margin: 0 auto;
  min-height: 68px;
  padding: 0 32px;
}
.ld-nav {
  display: flex;
  align-items: center;
  justify-content: space-between;
}
/* Brand: the real RATE wordmark (traced from the brand SVG, same asset as
   Flare's footer stamp). All caps, so its height IS its cap height. */
.ld-brand {
  display: flex;
  align-items: center;
  color: var(--label);
  text-decoration: none;
}
.ld-brand svg {
  width: auto;
  height: 19px;
  display: block;
}
.ld-nav-actions {
  display: flex;
  gap: 10px;
}

/* ---------- Hero ----------
   The hero card owns the first viewport (sitebar 68px + top gutter 20px
   accounted for), content centred within; the next card waits below the
   fold. Under 900px the forcing relaxes and the hero flows naturally. */
.hero-band {
  display: flex;
  min-height: calc(100svh - 88px);
}
.hero-band > .inner {
  flex: 1;
}
.ld-hero {
  display: grid;
  grid-template-columns: 1.1fr 0.9fr;
  align-items: center;
  align-content: center;
  height: 100%;
  gap: 48px;
  padding: clamp(40px, 5vw, 56px) 0;
}
.ld-h1 {
  font-size: clamp(38px, 5.4vw, 60px);
  font-weight: 750;
  letter-spacing: -0.038em;
  line-height: 1.04;
  background: linear-gradient(180deg, #fff 40%, rgba(255, 255, 255, 0.66));
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
  margin-bottom: 20px;
}
.ld-sub {
  font-size: 17.5px;
  line-height: 1.55;
  color: var(--label2);
  max-width: 30em;
  margin-bottom: 30px;
}
.ld-ctas {
  display: flex;
  gap: 12px;
  flex-wrap: wrap;
}
.ld-btn-lg {
  padding: 13px 26px;
  font-size: 16px;
}

/* ---------- How it works: the luminous step rail (Flare signature) ---------- */
.ld-howhead {
  font-size: 13.5px;
  font-weight: 650;
  letter-spacing: 0.02em;
  color: var(--green2);
  margin-bottom: 16px;
}
.ld-steps {
  list-style: none;
  margin: 0 0 34px;
  padding: 0;
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 20px;
  max-width: 30em;
}
.ld-steps::before {
  content: "";
  position: absolute;
  left: 13px;
  top: 10px;
  bottom: 14px;
  width: 1.5px;
  background: linear-gradient(180deg, rgba(52, 199, 89, 0.55), rgba(52, 199, 89, 0.32) 55%, rgba(52, 199, 89, 0.08));
}
.ld-step {
  display: flex;
  gap: 15px;
  align-items: flex-start;
  animation: ld-rise 0.55s var(--ease) both;
}
.ld-step:nth-child(1) { animation-delay: 0.2s; }
.ld-step:nth-child(2) { animation-delay: 0.28s; }
.ld-step:nth-child(3) { animation-delay: 0.36s; }
.ld-step:nth-child(4) { animation-delay: 0.44s; }
.ld-stepnum {
  position: relative;
  z-index: 1;
  flex: none;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 27px;
  height: 27px;
  border-radius: 9px;
  background: #101511;
  border: 1px solid rgba(52, 199, 89, 0.38);
  box-shadow: 0 0 0 3px var(--bg, #000);
  color: var(--green2);
  font-size: 12.5px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}
.ld-step:last-child .ld-stepnum {
  background: var(--green);
  border-color: var(--green);
  color: #04210d;
}
.ld-steptext {
  font-size: 15px;
  line-height: 1.5;
  color: var(--label2);
  padding-top: 3px;
}
.ld-steptext b {
  color: var(--label);
  font-weight: 650;
  white-space: nowrap;
}

/* ---------- Hero mock: a shoot summary card ----------
   Rate's equivalent of Flare's respondents-table mock: the payoff, concretely.
   Framed like a phone-width app card in a browser window (same chrome as the
   Flare tablecard: dots bar, dark gradient, green ambient shadow). */
.ld-tablecard {
  background: linear-gradient(165deg, rgba(20, 22, 24, 0.92), rgba(9, 10, 11, 0.96));
  border: 0.5px solid var(--edge2);
  border-radius: 20px;
  padding: 10px;
  box-shadow:
    0 40px 90px rgba(0, 0, 0, 0.5),
    0 0 80px rgba(52, 199, 89, 0.08),
    inset 0 0 0 0.5px rgba(255, 255, 255, 0.06);
  max-width: 400px;
  justify-self: center;
  width: 100%;
}
.ld-tablecard-bar {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 6px 8px 14px;
}
.ld-tablecard-bar .dots {
  display: flex;
  gap: 5px;
  flex: none;
}
.ld-tablecard-bar .dots i {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.14);
}
.ld-tablecard-bar .ttl {
  font-size: 12px;
  font-weight: 600;
  color: var(--label3);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* The day breakdown inside the mock: label/value line items, exactly the
   shape the app's calculation screen produces. */
.mock-summary {
  background: rgba(255, 255, 255, 0.04);
  border: 0.5px solid var(--hair);
  border-radius: 14px;
  padding: 16px 16px 14px;
  font-variant-numeric: tabular-nums;
}
.mock-day {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.06em;
  color: var(--green2);
  margin: 14px 0 8px;
}
.mock-day:first-child {
  margin-top: 0;
}
.mock-row {
  display: flex;
  justify-content: space-between;
  gap: 16px;
  font-size: 13px;
  line-height: 1.4;
  color: var(--label2);
  padding: 3.5px 0;
}
.mock-row .v {
  color: var(--label);
  white-space: nowrap;
}
.mock-row.sub {
  border-top: 0.5px solid var(--hair);
  margin-top: 6px;
  padding-top: 8px;
  font-weight: 650;
  color: var(--label);
}
.mock-total {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  margin-top: 14px;
  padding: 12px 2px 2px;
  border-top: 1px solid var(--sep);
  font-weight: 750;
}
.mock-total .k {
  font-size: 14px;
  color: var(--label);
}
.mock-total .v {
  font-size: 20px;
  letter-spacing: -0.02em;
  color: var(--green2);
}

/* ---------- Sections ---------- */
/* Apple-scale chapter rhythm: each section is its own room. */
.ld-section {
  padding: clamp(60px, 8vw, 100px) 0;
}
.ld-h2 {
  font-size: clamp(28px, 4vw, 44px);
  font-weight: 720;
  letter-spacing: -0.03em;
  color: var(--label);
}
.ld-seclead {
  font-size: 16.5px;
  line-height: 1.55;
  color: var(--label2);
  max-width: 36em;
  margin-top: 12px;
}

/* ---------- Feature grid (ld-card glass panels) ---------- */
.ld-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 16px;
  margin-top: 28px;
}
.ld-card {
  background: var(--glass);
  box-shadow: var(--panelshadow);
  border: 0.5px solid var(--edge);
  border-radius: 18px;
  padding: 24px 22px;
  backdrop-filter: blur(28px) saturate(170%);
  -webkit-backdrop-filter: blur(28px) saturate(170%);
  transition: transform 0.16s var(--ease), border-color 0.18s;
}
.ld-card:hover {
  transform: translateY(-2px);
  border-color: var(--edge2);
}
.ld-card h3 {
  font-size: 16.5px;
  font-weight: 700;
  letter-spacing: -0.015em;
  margin-bottom: 8px;
}
.ld-card p {
  font-size: 14.5px;
  line-height: 1.55;
  color: var(--label2);
}

/* ---------- Footer ---------- */
.ld-foot {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 18px;
  /* Zone tone draws the footer boundary now (band system); no hairline. */
  padding: 44px 0 56px;
}
.ld-foot-rate {
  width: auto;
  height: 13px;
  color: var(--label);
}
.ld-foot-links {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 8px 22px;
  font-size: 13.5px;
}
.ld-foot-links a {
  color: var(--label2);
  text-decoration: none;
}
.ld-foot-links a:hover {
  color: var(--label);
  text-decoration: underline;
}
.ld-foot-legal {
  font-size: 12.5px;
  color: var(--label3);
}

/* ---------- Legal + support prose pages ---------- */
/* Prose lives inside a master card now (band tone-1), so the padding is
   the card's interior rhythm. */
.legal {
  max-width: 680px;
  margin: 0 auto;
  padding: clamp(44px, 6vw, 64px) 0 clamp(48px, 6vw, 64px);
}
/* The simplified support card: one short message, centred. */
.support-simple {
  text-align: center;
  max-width: 34em;
}
.support-simple .support-cta {
  margin-top: 28px;
}
.legal h1 {
  font-size: clamp(30px, 4vw, 40px);
  font-weight: 750;
  letter-spacing: -0.03em;
  margin-bottom: 10px;
}
.legal .updated {
  font-size: 14px;
  color: var(--label3);
  margin-bottom: 34px;
}
.legal h2 {
  font-size: 19px;
  font-weight: 700;
  letter-spacing: -0.015em;
  margin: 34px 0 10px;
}
.legal h3 {
  font-size: 16px;
  font-weight: 650;
  margin: 24px 0 8px;
}
.legal p {
  font-size: 15.5px;
  line-height: 1.65;
  color: var(--label2);
  margin: 0 0 14px;
}
.legal ul {
  margin: 0 0 14px;
  padding-left: 22px;
}
.legal li {
  font-size: 15.5px;
  line-height: 1.65;
  color: var(--label2);
  margin-bottom: 8px;
}
/* Prose links only: a button inside the prose keeps its own label colour
   (this rule otherwise outranks .btn and renders green-on-green). */
.legal a:not(.btn) {
  color: var(--green2);
}

/* ---------- Page-load reveal (one orchestrated stagger) ---------- */
@keyframes ld-rise {
  from {
    opacity: 0;
    transform: translateY(14px);
  }
  to {
    opacity: 1;
    transform: none;
  }
}
.ld-reveal {
  animation: ld-rise 0.6s var(--ease) both;
  animation-delay: var(--ld-delay, 0s);
}

/* ---------- Small screens ---------- */
@media (max-width: 900px) {
  /* Stacked hero flows naturally: no viewport forcing, phone capped
     shorter so text + render share the screen. */
  .hero-band {
    display: block;
    min-height: 0;
  }
  /* Outranks the base .phone-img-hero rule, which sits later in the
     file (source order would otherwise kill this cap). 84svh (Nat,
     2026-07-22, twice: renders read too small on a phone; 56 -> 70 ->
     84svh). In practice the column width (max-width 100%, ~0.49 aspect)
     caps the render near 91svh on tall phones, so this sits close to
     the width-bound ceiling; the hero flows on mobile (no viewport
     forcing), so the extra height extends the card, nothing clips. */
  .ld-hero .phone-img-hero {
    max-height: 84svh;
  }
  .ld-hero {
    grid-template-columns: 1fr;
    gap: 40px;
    padding: 56px 0 64px;
  }
  .ld-page {
    padding: 0 18px 56px;
  }
  .band .inner {
    padding-left: 18px;
    padding-right: 18px;
  }
  .band {
    width: calc(100% - 16px);
  }
  .band.bare {
    width: 100%;
  }
  .ld-tablecard {
    max-width: 100%;
  }
  .ld-grid {
    grid-template-columns: 1fr;
  }
}

/* ---------- Community stat tiles ----------
   One unified trust band: the trusted-by claim as the headline, ON11's
   stats as its evidence. Values wear primary ink and proportional figures
   (display-size numbers read loose in tabular-nums); the green stays on
   the lead line, matching the app's hierarchy. */
.stats-band {
  text-align: center;
}
.stats-band .ld-howhead {
  margin: 14px 0 22px;
}
.stat-tiles {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 16px;
}
.stat-tiles .stat {
  padding: 22px 20px;
}
.stat-v {
  font-size: clamp(26px, 3.2vw, 38px);
  font-weight: 700;
  letter-spacing: -0.02em;
  color: var(--label);
}
.stat-k {
  font-size: 13.5px;
  color: var(--label2);
  margin-top: 6px;
}
@media (max-width: 900px) {
  .stat-tiles {
    grid-template-columns: repeat(2, 1fr);
  }
}
@media (max-width: 480px) {
  .stat-v {
    font-size: 24px;
  }
}

/* ---------- Promo images ----------
   Nat's Rotato device renders (transparent surround, frame + shadow baked
   in), so no chrome here: just sizing. Served as LOSSLESS WebP (~60%
   smaller, pixel-identical) via <picture> with the PNG original as the
   fallback; display: contents makes the <picture> wrapper invisible to
   layout so the img stays the real flex/grid child everywhere. */
.phone-pic {
  display: contents;
}
/* display: contents promotes BOTH picture children into the parent
   grid/flex — including the invisible <source>, which then auto-places
   into the first grid cell and shoves the img/caption a cell over
   (live regression, 2026-07-23: step phones jumped to column 2, captions
   to a second row). Hiding it is safe: <source> renders nothing by
   definition, and resource selection happens at the element level, so
   display: none does not affect which file the img loads. */
.phone-pic source {
  display: none;
}
.phone-img {
  width: 100%;
  height: auto;
  display: block;
}
/* Reactive hero render: sized by viewport height with caps, constrained
   by its column; aspect stays true because only max-* are set. */
.phone-img-hero {
  width: auto;
  height: auto;
  max-height: min(76svh, 760px);
  max-width: 100%;
  justify-self: center;
}
/* Chapter title for the scrolly card: a centered heading that scrolls
   away as the stage pins beneath it. */
.scrolly-title {
  text-align: center;
  padding: clamp(40px, 5vw, 56px) 0 0;
}

/* ---------- Promo scrollytelling (Apple product-page pattern) ----------
   A tall runway with a sticky stage: scroll progress drives which
   phone + caption pair is showing. Everything scrolly-specific is gated
   behind .armed (set by the driver script), so the unarmed page renders
   the steps as three stacked, fully-visible blocks in normal flow. */
.scrolly-step {
  margin: 0;
}
.scrolly-step .phone-img {
  width: min(320px, 76vw);
  margin: 0 auto;
}
/* Unarmed: steps stack with comfortable rhythm. */
.scrolly:not(.armed) .scrolly-step + .scrolly-step {
  margin-top: 56px;
}
.scrolly:not(.armed) .car-dots {
  display: none;
}
/* Armed: the runway (one viewport of travel per step, plus the stage) and
   the pinned stage. svh keeps mobile URL-bar resizing honest. */
.scrolly.armed {
  position: relative;
  height: 320vh;
  /* Pull the runway up under the title: the pinned stage centres its
     content in a full viewport, so its top whitespace would otherwise
     stack on the title's padding as a huge gap at entry. */
  margin-top: -7svh;
}
/* Mobile-first stage composition: the view flexes to fill the viewport
   between the stage paddings and the dots, and (below) the phone flexes
   to fill the view minus its caption. No fixed svh guesses: the render
   takes exactly the space the text leaves over, on every device height
   (Nat, 2026-07-22: make positioning and padding use the space). The
   desktop block restores its own hugged-centre composition. */
.scrolly.armed .scrolly-stage {
  position: sticky;
  top: 0;
  height: 100vh;
  height: 100svh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 2px;
  /* The pinned stage sits at viewport top, so the sticky sitebar (68px)
     overlays it: budget the bar plus the 12px gutter so the render never
     rides beneath the blur. Bottom budget clears the absolute indicator
     rail (31px block + its 8px offset). */
  padding-top: 80px;
  padding-bottom: max(44px, env(safe-area-inset-bottom));
}
/* All steps share the stage cell; the active one is visible. */
.scrolly.armed .scrolly-view {
  display: grid;
  width: 100%;
  flex: 1 1 auto;
  min-height: 0;
}
/* NO TRANSITIONS on the steps, deliberately (Safari, live site,
   2026-07-22, twice): desktop Safari can skip the class-driven restyle
   of a de-activated step mid-scroll, leaving TWO steps painted at full
   strength. Transitions made that unrecoverable (nothing re-applies a
   missed restyle). The mechanism is now wedge-proof by construction:
   non-active steps are visibility: hidden (CSS here + inline style
   enforced by the driver on EVERY scroll event, which self-heals any
   missed restyle within one tick), and the active step plays one-shot
   entrance ANIMATIONS: an animation never reverses, so there is no
   state machine to wedge, and its end state is the element's natural
   resting style. The outgoing step vanishes instantly, which also kills
   the two-phones-superimposed mid-crossfade look. */
.scrolly.armed .scrolly-step {
  grid-area: 1 / 1;
  height: 100%;
  min-height: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 16px;
  visibility: hidden;
  pointer-events: none;
}
.scrolly.armed .scrolly-step.on {
  visibility: visible;
  pointer-events: auto;
  animation: scrolly-in 0.55s var(--ease) backwards;
}
@keyframes scrolly-in {
  from {
    opacity: 0;
    transform: scale(0.96) translateY(10px);
  }
}
/* The stagger: the caption rises in a beat after the phone. backwards
   fill keeps it invisible through its 0.12s delay. */
.scrolly.armed .scrolly-step.on .car-cap {
  animation: scrolly-cap-up 0.45s var(--ease) 0.12s backwards;
}
@keyframes scrolly-cap-up {
  from {
    opacity: 0;
    transform: translateY(10px);
  }
}
/* The stage phone fills whatever the caption leaves over: shrink-only
   flex (never stretched past its natural size, so no phantom letterbox
   air), floored at nothing, capped by width for narrow screens. The
   renders are ink-cropped (2026-07-22: the Rotato exports carried ~13%
   invisible alpha margin per axis, which read as phantom padding), so
   the box IS the visible phone. Desktop overrides below. */
.scrolly.armed .phone-img {
  flex: 0 1 auto;
  min-height: 0;
  height: auto;
  width: auto;
  max-width: min(88vw, 460px);
  object-fit: contain;
}
.scrolly.armed .car-cap {
  margin-top: 0;
}

/* ---------- Scrolly, desktop: alternating side-by-side steps ----------
   Text sits beside the phone (sides alternate per step), freeing the full
   stage height for the render, and the in/out motion turns directional:
   the phone glides in from its own side, the caption counter-enters from
   the other, a beat behind. Mobile keeps the stacked composition above. */
@media (min-width: 900px) {
  .scrolly-step {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: clamp(40px, 5vw, 72px);
    align-items: center;
    width: 100%;
  }
  .scrolly-step .phone-img {
    justify-self: end;
    margin: 0;
  }
  .scrolly-step .car-cap {
    justify-self: start;
    text-align: left;
    margin-top: 0;
  }
  /* Even steps mirror: phone right, text left. */
  .scrolly-step:nth-child(even) .phone-img {
    grid-column: 2;
    grid-row: 1;
    justify-self: start;
  }
  /* Mirrored position, but body text stays left-aligned for readability
     (ragged-left paragraphs read badly; Apple keeps text left-aligned on
     either side of the visual). */
  .scrolly-step:nth-child(even) .car-cap {
    grid-column: 1;
    grid-row: 1;
    justify-self: end;
  }
  .scrolly-step .car-cap h3 {
    font-size: clamp(26px, 3vw, 36px);
    letter-spacing: -0.025em;
  }
  .scrolly-step .car-cap p {
    font-size: 16.5px;
    margin-top: 12px;
    max-width: 24em;
    margin-left: 0;
    margin-right: 0;
  }
  /* Desktop restores the hugged-centre composition: fixed-height render,
     no flex fill (the mobile flex-fill rules above outrank the plain
     .scrolly-step desktop rules, so each gets an explicit reset here). */
  .scrolly.armed .scrolly-stage {
    /* 8px + the dots' 12px internal padding = the former 20px gap. */
    gap: 8px;
    padding-top: 40px;
    padding-bottom: 0;
  }
  .scrolly.armed .scrolly-view {
    flex: 0 0 auto;
  }
  .scrolly.armed .phone-img {
    flex: none;
    max-width: none;
    height: min(76svh, 780px);
  }
  /* Directional entrance choreography: the phone glides in from its own
     side, the caption counter-enters a beat behind. One-shot ANIMATIONS
     only, same rationale as the armed base block (transitions on these
     parts are what desktop Safari wedged). The even-step rules override
     animation-name alone; timing carries over from the shorthand. */
  .scrolly.armed .scrolly-step {
    display: grid;
    height: auto;
    gap: clamp(40px, 5vw, 72px);
  }
  .scrolly.armed .scrolly-step.on .phone-img {
    animation: scrolly-phone-side 0.6s var(--ease);
  }
  .scrolly.armed .scrolly-step.on:nth-child(even) .phone-img {
    animation-name: scrolly-phone-side-mirror;
  }
  .scrolly.armed .scrolly-step.on .car-cap {
    animation: scrolly-cap-side 0.5s var(--ease) 0.12s backwards;
  }
  .scrolly.armed .scrolly-step.on:nth-child(even) .car-cap {
    animation-name: scrolly-cap-side-mirror;
  }
}
@keyframes scrolly-phone-side {
  from {
    transform: translateX(-36px) scale(0.97);
  }
}
@keyframes scrolly-phone-side-mirror {
  from {
    transform: translateX(36px) scale(0.97);
  }
}
@keyframes scrolly-cap-side {
  from {
    opacity: 0;
    transform: translateX(32px);
  }
}
@keyframes scrolly-cap-side-mirror {
  from {
    opacity: 0;
    transform: translateX(-32px);
  }
}
.car-cap {
  text-align: center;
  margin-top: 22px;
}
.car-cap h3 {
  font-size: 20px;
  font-weight: 650;
  letter-spacing: -0.02em;
}
.car-cap p {
  margin-top: 8px;
  font-size: 14.5px;
  line-height: 1.55;
  color: var(--label2);
  max-width: 28em;
  margin-left: auto;
  margin-right: auto;
}
/* ---------- Scrolly progress indicator ----------
   Apple's gallery indicator: inactive steps are quiet 7px dots; the
   ACTIVE step's track stretches into a 36px capsule whose fill tracks
   scroll progress through that step.
   Orientation is composition-led: DESKTOP gets the vertical right-edge
   rail (there's clear air beside the alternating layout); MOBILE keeps
   the bottom-centred horizontal form, because a 390px screen has no
   side room next to a render deliberately sized to fill it (a side
   rail overlays the phone bezel and reads accidental: measured -27px
   clearance) — the same reason Apple's phone galleries indicate at the
   bottom.
   The fill is driven the proper platform way where available: CSS
   scroll-driven animations (named view() timeline on the runway,
   per-dot animation-range segments), compositor-run with no JS
   listener (Chromium 115+, Safari 26+). The native block is
   @supports-gated: engines without it (Firefox, older iOS) must never
   see the 1ms animation (fill-mode both would pin fills at 100%), so
   they fall back to the driver writing the fill inline; the driver
   skips those writes when the native path is live.
   Each button stays a 44pt-class touch block, non-overlapping by
   construction (the rail is absolute, so the stage pads its bottom on
   mobile to keep the caption clear of it). */
.car-dots {
  position: absolute;
  left: 50%;
  bottom: max(8px, env(safe-area-inset-bottom));
  transform: translateX(-50%);
  display: flex;
  gap: 0;
}
.car-dot {
  width: 31px;
  height: 31px;
  background: none;
  border: 0;
  padding: 12px;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: width 0.35s var(--ease), height 0.35s var(--ease);
}
.car-dot .track {
  display: block;
  width: 7px;
  height: 7px;
  border-radius: 980px;
  background: rgba(255, 255, 255, 0.25);
  overflow: hidden;
  transition: width 0.35s var(--ease), height 0.35s var(--ease);
}
.car-dot .fill {
  display: block;
  width: 0%;
  height: 100%;
  border-radius: inherit;
  background: var(--green);
  /* Only the active capsule shows its fill: passed steps return to
     quiet dots (the timeline still tracks them; opacity gates). */
  opacity: 0;
}
.car-dot.on .fill {
  opacity: 1;
}
.car-dot.on {
  width: 60px;
}
.car-dot.on .track {
  width: 36px;
}
/* Desktop: the vertical rail, right of the stage, centred. */
@media (min-width: 900px) {
  .car-dots {
    left: auto;
    bottom: auto;
    right: 4px;
    top: 50%;
    transform: translateY(-50%);
    flex-direction: column;
  }
  .car-dot.on {
    width: 31px;
    height: 60px;
  }
  .car-dot.on .track {
    width: 7px;
    height: 36px;
  }
  .car-dot .fill {
    width: 100%;
    height: 0%;
  }
}
/* The native scroll-driven path. The runway carries a named view
   timeline; each dot's fill maps one third of it. Range constants: the
   driver activates on the viewport CENTRE crossing the runway (progress
   p = (ih/2 - top)/h with h = 320vh = 3.2ih), which in view-timeline
   cover terms is q = (p*h + ih/2)/(h + ih): p=0 -> 11.905%, thirds
   every 25.397%. animation THEN animation-timeline, strictly: the
   shorthand resets the timeline (order is load-bearing), and the
   nth-child ranges sit after the shorthand for the same reason. The
   desktop override swaps animation-NAME only (width-growth to
   height-growth), which preserves timeline and ranges. */
@supports (animation-timeline: view()) {
  .scrolly {
    view-timeline: --scrolly-tl block;
    /* Explicit zero: the initial view-timeline-inset is auto, which
       adopts the scroller's scroll-padding (96px here for anchor
       jumps) and silently shifts every range constant. */
    view-timeline-inset: 0;
  }
  .car-dot .fill {
    animation: scrolly-fill-w 1ms linear both;
    animation-timeline: --scrolly-tl;
  }
  .car-dot:nth-child(1) .fill {
    animation-range: cover 11.905% cover 37.302%;
  }
  .car-dot:nth-child(2) .fill {
    animation-range: cover 37.302% cover 62.698%;
  }
  .car-dot:nth-child(3) .fill {
    animation-range: cover 62.698% cover 88.095%;
  }
  @media (min-width: 900px) {
    .car-dot .fill {
      animation-name: scrolly-fill-h;
    }
  }
}
@keyframes scrolly-fill-w {
  from {
    width: 0%;
  }
  to {
    width: 100%;
  }
}
@keyframes scrolly-fill-h {
  from {
    height: 0%;
  }
  to {
    height: 100%;
  }
}

/* ---------- Flare section ---------- */
/* Nav pills: both capsules share one 32px height and one width, content
   centred within (paddings below act as minimums). The invisible ::after
   pad grows each pill's touch target to the 44pt-class Apple expects
   (32px visual + 2x8px halo) without changing the drawn capsule. */
.sitebar .btn.sm {
  position: relative;
  height: 32px;
  padding: 0 14px;
  min-width: 88px;
  justify-content: center;
}
.sitebar .btn.sm::after {
  content: "";
  position: absolute;
  /* -5px sides: exactly half the 10px pill gap, so the two halos meet
     without overlapping (an overlap would hand the seam to the later
     pill in DOM order). */
  inset: -8px -5px;
  border-radius: 980px;
}
/* Flare pill spacing. The glyph's viewBox is cropped to the ink (the
   full symbol box is ~44% empty margin), so 16px here means 16px of
   visible mark and the gap/padding numbers are honest. */
.flare-navlink {
  gap: 6px;
}
/* Scoped past .sitebar .btn.sm, whose shorthand otherwise outranks the
   longhands. Mark + label sit left; the equal-width capsule's spare room
   becomes trailing air. */
.sitebar .btn.sm.flare-navlink {
  justify-content: flex-start;
  padding-left: 12px;
  padding-right: 16px;
}
.flare-navlink .ic {
  width: 16px;
  height: 16px;
  color: var(--green);
}
.flare-head {
  text-align: center;
  max-width: 620px;
  margin: 0 auto;
}
/* The Flare brand lockup heading the section: glyph + name at the same
   optical weight the product's own landing uses. */
.flare-brand {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 12px;
}
.flare-brand .glyph {
  width: 38px;
  height: 38px;
  color: var(--green);
}
.flare-brand .name {
  font-size: 31px;
  font-weight: 700;
  letter-spacing: -0.02em;
  color: var(--label);
}
.flare-head .ld-seclead {
  margin: 12px auto 0;
}
.flare-grid {
  margin-top: 32px;
}

/* ---------- Flare audience switch (production default) ----------
   A two-segment pill; panels swap with a small rise. Everything
   switch-specific is gated behind .armed so the no-JS page shows both
   pitches stacked, production first. */
.aud-switch {
  display: none;
  background: var(--fill2);
  border: 0.5px solid var(--hair);
  border-radius: 980px;
  padding: 3px;
  gap: 3px;
  width: max-content;
  margin: 20px auto 0;
}
.flare-section.armed .aud-switch {
  display: flex;
}
.aud-tab {
  border: 0;
  background: transparent;
  color: var(--label2);
  font-family: inherit;
  font-size: 14px;
  font-weight: 600;
  padding: 7px 18px;
  border-radius: 980px;
  cursor: pointer;
  transition: background 0.2s var(--ease), color 0.2s;
}
.aud-tab:hover {
  color: var(--label);
}
.aud-tab[aria-selected="true"] {
  background: var(--glass2);
  color: var(--label);
  box-shadow: inset 0 0 0 0.5px var(--edge);
}
.aud-panel {
  margin-top: 34px;
}
.flare-section.armed .aud-panel {
  display: none;
}
/* Transform-only entrance, deliberately: the crew panel is an ancestor of
   four backdrop-filter cards, and an opacity animation on it would flatten
   their glass mid-fade then snap (same artifact class as the community
   stats reveal, worst on iOS WebKit). A rise with no fade stays clean. */
.flare-section.armed .aud-panel.on {
  display: block;
  animation: aud-rise 0.35s var(--ease);
}
@keyframes aud-rise {
  from {
    transform: translateY(8px);
  }
}
/* The production pitch: the W1 rail centred, CTA beneath. */
.aud-steps {
  max-width: 30em;
  margin: 30px auto 0;
}
.aud-cta {
  text-align: center;
  margin-top: 30px;
}

@media (max-width: 640px) {
  .band {
    border-radius: 22px;
  }
  .band.bare {
    border-radius: 0;
  }
}

/* ---------- On-scroll reveals (apple.com chapter entrances) ----------
   Armed from <head> only when IntersectionObserver exists and motion is
   allowed; unarmed, every section renders visible with no dependency on JS.
   Driven by the shared ld-rise ANIMATION, not a transition: a transition
   would take over the element's transition property and slow unrelated
   interactions (the ld-card hover lift) to reveal speed. backwards fill
   covers the stagger delay (--rv-delay per element); once the animation
   completes it stops applying entirely, handing transform and opacity back
   to the stylesheet (a forwards fill would pin transform: none and kill
   hover transforms for good).
   RULE: never put data-reveal on an ANCESTOR of a backdrop-filter element
   (ld-card glass). While an ancestor fades, it becomes the backdrop root:
   the glass flattens, then visibly snaps live when opacity lands at 1
   (worst on iOS WebKit). Reveal the glass element itself, or its non-glass
   siblings. That artifact was the community-stats first-scroll glitch
   (2026-07-22). */
.reveals-armed [data-reveal] {
  opacity: 0;
}
.reveals-armed [data-reveal].in {
  opacity: 1;
  animation: ld-rise 0.7s var(--ease) backwards;
  animation-delay: var(--rv-delay, 0s);
}

/* ---------- Reduced motion: land everything instantly ---------- */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-delay: 0s !important;
    transition-duration: 0.01ms !important;
  }
}
