/* ============================================================================
   Reps — marketing site

   Structure and rhythm are measured from hevyapp.com (container 1140, h1 64/600,
   body 18/400, feature sections ~500-580px, art in a rounded panel). Palette,
   type colour and corner radii come from the Reps app's own tailwind.config.js,
   so the site and the product read as one thing.
   ========================================================================= */

:root {
  /* Colour — the app's palette */
  --ink: #0A0A0A;
  --ink-soft: #1A1A1A;
  --paper: #FAFAFA;
  --paper-card: #FFFFFF;

  /* #8E8E93 is the app's grey. At 3.12:1 on paper it clears the 3:1 bar for
     large or bold text only, so it stays on display type and non-text marks;
     body copy uses --muted-text at 4.86:1. */
  --muted: #8E8E93;
  --muted-text: #6E6E73;
  --line: #E5E5EA;

  /* The panel every feature image sits in */
  --panel: #F0F0F2;
  --surface: rgba(229, 229, 234, 0.3);

  /* Inverted pair, for the black CTA band */
  --on-ink: #FFFFFF;
  --on-ink-muted: rgba(255, 255, 255, 0.62);
  --on-ink-line: rgba(255, 255, 255, 0.16);

  /* Radii — cards hold panels, panels hold controls */
  --r-card: 24px;
  --r-panel: 16px;
  --r-control: 12px;
  --r-pill: 9999px;

  /* Tick: an opaque disc with the check cut out of it (see .checks). */
  --tick: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 22 22'><mask id='t'><rect width='22' height='22' fill='white'/><path d='M6.4 11.4l3.1 3.1 6.1-6.4' fill='none' stroke='black' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'/></mask><circle cx='11' cy='11' r='11' fill='black' mask='url(%23t)'/></svg>");

  /* Elevation */
  --sh-card: 0 1px 2px rgba(0,0,0,0.04), 0 4px 16px rgba(0,0,0,0.04);
  --sh-lift: 0 8px 24px rgba(0,0,0,0.18);
  --sh-lift-soft: 0 8px 24px rgba(0,0,0,0.08);

  /* Motion — the app's curves */
  --e-snap: cubic-bezier(0.23, 1, 0.32, 1);
  --t-press: 140ms;
  --t-pop: 180ms;

  /* Rhythm — measured off the reference */
  --shell: 1140px;
  --gutter: 24px;
  --section-y: clamp(72px, 8vw, 128px);
  --section-y-lg: clamp(80px, 9vw, 144px);

  --font: -apple-system, BlinkMacSystemFont, 'SF Pro Display', 'SF Pro Text',
          Inter, system-ui, sans-serif;

  /* Display face. Navigation and controls stay on the system stack: the app
     itself is SF Pro, and the two shouldn't disagree on a button. */
  --font-display: 'General Sans', var(--font);
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --ink: #F5F5F7;
    --ink-soft: #E4E4E7;
    --paper: #0A0A0A;
    --paper-card: #161617;
    --muted: #8E8E93;
    --muted-text: #9E9EA5;        /* 7.44:1 on #0A0A0A */
    --line: #2A2A2C;
    --panel: #141416;
    --surface: rgba(120, 120, 128, 0.14);
    --on-ink: #0A0A0A;
    --on-ink-muted: rgba(10, 10, 10, 0.64);
    --on-ink-line: rgba(10, 10, 10, 0.14);
    --sh-card: 0 1px 2px rgba(0,0,0,0.4), 0 4px 16px rgba(0,0,0,0.3);
    --sh-lift: 0 8px 28px rgba(0,0,0,0.6);
    --sh-lift-soft: 0 8px 24px rgba(0,0,0,0.4);
  }
}

:root[data-theme="dark"] {
  --ink: #F5F5F7;
  --ink-soft: #E4E4E7;
  --paper: #0A0A0A;
  --paper-card: #161617;
  --muted: #8E8E93;
  --muted-text: #9E9EA5;
  --line: #2A2A2C;
  --panel: #141416;
  --surface: rgba(120, 120, 128, 0.14);
  --on-ink: #0A0A0A;
  --on-ink-muted: rgba(10, 10, 10, 0.64);
  --on-ink-line: rgba(10, 10, 10, 0.14);
  --sh-card: 0 1px 2px rgba(0,0,0,0.4), 0 4px 16px rgba(0,0,0,0.3);
  --sh-lift: 0 8px 28px rgba(0,0,0,0.6);
  --sh-lift-soft: 0 8px 24px rgba(0,0,0,0.4);
}

/* ─────────────────────────────  base  ───────────────────────────── */

*, *::before, *::after { box-sizing: border-box; }

html {
  color-scheme: light dark;
  scroll-behavior: smooth;
  scroll-padding-top: 88px;
  -webkit-text-size-adjust: 100%;
}

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--font);
  font-size: 18px;              /* reference body size */
  line-height: 1.45;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  overflow-x: hidden;
}

img { max-width: 100%; height: auto; display: block; }

p, li { text-wrap: pretty; }
h1, h2, h3 { text-wrap: balance; }

h1, h2, h3 { margin: 0; font-weight: 600; line-height: 1.2; }

a { color: inherit; }

:where(a, button, summary, [tabindex]):focus-visible {
  outline: 2px solid currentColor;
  outline-offset: 3px;
  border-radius: 4px;
}

.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 100;
  padding: 12px 20px;
  background: var(--ink);
  color: var(--paper);
  border-radius: 0 0 var(--r-control) 0;
  font-weight: 600;
  text-decoration: none;
}
.skip-link:focus { left: 0; }

.shell {
  width: 100%;
  max-width: var(--shell);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

/* ─────────────────────────────  buttons  ───────────────────────────── */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 52px;
  padding: 0 28px;
  border: 1px solid transparent;
  border-radius: var(--r-pill);
  font: inherit;
  font-size: 16px;
  font-weight: 600;
  text-decoration: none;
  cursor: pointer;
  transition: transform var(--t-press) var(--e-snap),
              background-color var(--t-press) var(--e-snap),
              border-color var(--t-press) var(--e-snap),
              opacity var(--t-press) var(--e-snap);
}
.btn:active { transform: scale(0.97); }

.btn--primary { background: var(--ink); color: var(--paper); }
.btn--primary:hover { opacity: 0.86; }

.btn--ghost { background: transparent; color: var(--ink); border-color: var(--line); }
.btn--ghost:hover { background: var(--surface); border-color: var(--muted); }

.btn--invert { background: var(--on-ink); color: var(--ink); }
.btn--invert:hover { opacity: 0.88; }

.btn--outline-invert {
  background: transparent;
  color: var(--on-ink);
  border-color: var(--on-ink-line);
}
.btn--outline-invert:hover { border-color: var(--on-ink); }

.btn--sm { min-height: 44px; padding: 0 18px; font-size: 15px; }

/* ─────────────────────────────  nav  ───────────────────────────── */

.nav {
  position: sticky;
  top: 0;
  z-index: 50;
  background: color-mix(in srgb, var(--paper) 78%, transparent);
  backdrop-filter: saturate(180%) blur(20px);
  -webkit-backdrop-filter: saturate(180%) blur(20px);
  border-bottom: 1px solid transparent;
  transition: border-color var(--t-pop) var(--e-snap);
}
.nav[data-stuck="true"] { border-bottom-color: var(--line); }

.nav__inner { position: relative; display: flex; align-items: center; gap: 32px; height: 72px; }

.nav__brand {
  display: flex;
  align-items: center;
  flex: none;
  min-height: 44px;              /* the mark is 22px tall; the target is not */
}
.nav__brand img { width: auto; height: 22px; }

/* The wordmark ships as solid black, so it disappears on a dark page.
   Inverting is exact here — the mark is pure #000 on transparent. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .nav__brand img,
  :root:not([data-theme="light"]) .footer__brand img { filter: invert(1); }
}
:root[data-theme="dark"] .nav__brand img,
:root[data-theme="dark"] .footer__brand img { filter: invert(1); }

.nav__links {
  display: flex;
  align-items: center;
  gap: 28px;
  margin-inline: auto;
  font-size: 16px;
  font-weight: 500;
}

/* One rule that travels, rather than a border per link: it slides between
   items as the reader moves down the page. Width and offset come from the
   active link, measured in main.js — which is also why it stays invisible
   without scripting, where there is nothing to measure or follow.

   It hangs off .nav__inner, not the links row, so it lands on the bar's
   own bottom border rather than tucking under the text like an underline:
   -1px against a 2px rule straddles the 1px hairline. The hairline only
   appears once the bar is stuck, and by then the first section has passed
   under it, so the two turn up together. */
.nav__ink {
  position: absolute;
  left: 0;
  bottom: -1px;
  height: 2px;
  width: var(--ink-w, 0px);
  border-radius: 2px;
  background: var(--ink);
  transform: translateX(var(--ink-x, 0px));
  opacity: 0;
  pointer-events: none;
  transition: transform 380ms var(--e-snap),
              width 380ms var(--e-snap),
              opacity 200ms var(--e-snap);
}
.nav__inner[data-on] .nav__ink { opacity: 1; }
.nav__links a[aria-current] { color: var(--ink); }
.nav__links a {
  display: inline-flex;
  align-items: center;
  min-height: 44px;              /* HIG touch target, not just the glyph box */
  color: var(--muted-text);
  text-decoration: none;
  transition: color var(--t-press) var(--e-snap);
}
.nav__links a:hover { color: var(--ink); }

.nav__cta { flex: none; }

/* ─────────────────────────────  hero  ───────────────────────────── */

.hero { padding-block: clamp(48px, 7vw, 104px) clamp(40px, 6vw, 88px); overflow: hidden; }

.hero__inner {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  align-items: center;
  gap: 48px;
  min-height: clamp(600px, 72vh, 780px);
}

/* 64px / 600 / 1.2, tracking near zero — measured off the reference. The app's
   own display type is tighter, but this is the page's one borrowed proportion. */
.hero__title {
  font-family: var(--font-display);
  /* General Sans runs wider than SF Pro; 58px keeps “Never retype your”
     on one line so the hard break lands where it was written to. */
  font-size: clamp(38px, 4.2vw, 58px);
  font-weight: 600;                /* General Sans Semibold */
  line-height: 1.12;
  letter-spacing: -0.018em;
}
.hero__title-accent { color: var(--muted); }

.hero__sub {
  font-family: var(--font-display);
  font-weight: 400;
  max-width: 32ch;
  margin: 24px 0 0;
  font-size: 19px;
  line-height: 1.45;
  color: var(--muted-text);
}

.hero__actions { display: flex; flex-wrap: wrap; gap: 12px; margin-top: 28px; }

.hero__facts {
  display: flex;
  flex-wrap: wrap;
  gap: 8px 20px;
  margin: 58px 0 0;
  padding: 20px 0 0;
  border-top: 1px solid var(--line);
  list-style: none;
  font-size: 15px;
  color: var(--muted-text);
}
.hero__facts li { display: flex; align-items: center; gap: 8px; }
.hero__facts li::before {
  content: "";
  width: 5px; height: 5px;
  border-radius: 50%;
  background: var(--muted);
  flex: none;
}

/* Stacked phones, both upright — the second sits behind and slightly lower,
   the way Apple stages a pair. No rotation at any width.

   Every offset runs through --tx/--ty, so one pair of numbers drives the
   resting layout, the entry animation, the float and the reduced-motion
   fallback. A breakpoint that wants the pair staged differently sets those
   two and nothing else — before, it had to restate a transform that the
   animation then overrode. */
.hero__art { position: relative; display: grid; place-items: center; min-height: clamp(560px, 66vh, 720px); }
.hero__art .phone { position: absolute; transform: translate(var(--tx, 0), var(--ty, 0)); }

.phone--back {
  width: 59%;                    /* ~92% of the front phone, not a miniature */
  --tx: 38%;
  --ty: 7%;
  opacity: 0.92;
}
.phone--front {
  width: 64%;
  --tx: -14%;
  --ty: -2%;
  z-index: 2;
}

/* ─────────────────────────────  phone frame  ───────────────────────────── */

.phone {
  margin: 0;
  padding: 10px;
  overflow: hidden;              /* keeps the sweep inside the screen */
  background: linear-gradient(160deg, #48484A 0%, #2C2C2E 16%, #1C1C1E 46%, #050505 100%);
  /* A hardware corner is a ratio, not a measurement: Apple's is about 13.8% of
     the device's width, which a fixed 46px only matched at the hero's size. On
     a 187px panel phone the same 46px read as 25% — a lozenge, not a phone.
     The second value is that radius as a share of the height, so the corner
     stays circular; the aspect ratio below is fixed, so the two track. */
  border-radius: 13.8% / 6.6%;
  /* A rail catches light along its top edge and loses it underneath, and it
     rests on two shadows rather than one: a tight contact shadow where it
     meets the page, and the soft ambient one it had on its own. A single
     mid-range shadow is most of what makes a mockup read as a rectangle. */
  box-shadow:
    inset 0 1px 0 rgba(255,255,255,0.24),
    inset 0 -1px 0 rgba(255,255,255,0.06),
    0 2px 4px rgba(0,0,0,0.16),
    var(--sh-lift),
    0 0 0 1px rgba(0,0,0,0.55);
}

/* The glass sits under the rail instead of flush with it: its own hairline,
   and the bezel casting a little shade onto the top of the screen. */
.phone::after {
  content: "";
  position: absolute;
  inset: 10px;
  border-radius: calc(13.8% - 7px) / calc(6.38% - 7px);
  pointer-events: none;
  box-shadow:
    inset 0 0 0 1px rgba(0,0,0,0.5),
    inset 0 2px 3px rgba(0,0,0,0.28);
}
.phone img,
.phone__film {
  width: 100%;
  /* The glass is the shape of the screens it holds, not of a borrowed device.
     Every capture is 1080/2185 (Android, both system bars removed) or cropped
     to match, so nothing is scaled off the sides and each screen keeps the
     margins the app draws. Guards against an odd-sized capture too. */
  aspect-ratio: 1080 / 2185;
  object-fit: cover;
  object-position: top;
  /* The frame's radius less its 10px bezel, so the glass sits concentric
     inside the shell at every size. */
  border-radius: calc(13.8% - 7px) / calc(6.38% - 7px);
  background: var(--paper-card);
}

/* The hero's recording of the app takes that same glass — same corner, same
   fit — at its own ratio: it is the raw 1080x2125 of app, with none of the
   bottom padding the stills carry. */
.phone__film {
  display: block;
  /* A video's width/height attributes land as CSS width/height, unlike an
     image's — without this the intrinsic 1062px wins over the ratio and the
     phone grows to the height of the file. */
  height: auto;
  aspect-ratio: 540 / 1062;
}

.phone--static { width: min(276px, 100%); }

/* The app is light-only, so its screens are near-white. Dropped straight into a
   dark page they glow. dark-mode.md › Dark Mode colors: "Soften the color of
   white backgrounds ... to prevent the background from glowing". */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .phone img { filter: brightness(0.86); }
}
:root[data-theme="dark"] .phone img { filter: brightness(0.86); }

/* ─────────────────────────────  banner  ───────────────────────────── */

.banner { padding-block: clamp(64px, 7vw, 104px) 0; text-align: center; }

.banner__title {
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--muted-text);
}
.banner__claim {
  font-family: var(--font-display);
  max-width: 23ch;
  margin: 20px auto 0;
  font-size: clamp(32px, 4.6vw, 62px);
  font-weight: 600;
  line-height: 1.08;
  letter-spacing: -0.028em;
  /* Display type, so it gets balance rather than the paragraph default:
     at this size an orphaned last line reads as a mistake. */
  text-wrap: balance;
}

/* ─────────────────────────────  features  ───────────────────────────── */

.feature { padding-block: var(--section-y); }

.feature__inner {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  align-items: center;
  gap: clamp(32px, 4.5vw, 72px);
}

.feature--reverse .panel { order: 2; }

/* Every feature image sits in a rounded panel — the reference's most
   characteristic structural move. */
.panel {
  display: grid;
  place-items: center;
  margin: 0;
  padding: clamp(32px, 4vw, 56px);
  background: var(--panel);
  border-radius: var(--r-card);
  min-height: 520px;
}

.feature__copy h2 {
  font-size: clamp(30px, 3.4vw, 44px);
  font-weight: 600;
  letter-spacing: -0.02em;
}

.feature__lead {
  margin: 16px 0 0;
  font-size: 18px;
  line-height: 1.45;
  color: var(--muted-text);
  max-width: 44ch;
}

.checks {
  margin: 28px 0 0;
  padding: 0;
  list-style: none;
  display: grid;
  gap: 13px;
}
.checks li {
  display: grid;
  grid-template-columns: 22px 1fr;
  gap: 14px;
  align-items: start;
  font-size: 17px;
}
.checks li::before {
  content: "";
  width: 22px;
  height: 22px;
  margin-top: 3px;
  background: var(--ink);
  /* The tick has to be a hole: a CSS mask reads alpha, so a white stroke would
     be just as opaque as the disc and we'd get a filled circle. */
  -webkit-mask: var(--tick) center / contain no-repeat;
  mask: var(--tick) center / contain no-repeat;
  flex: none;
}

/* the calculator's three totals, echoing the app's own control */
.readout {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 10px;
  margin-top: 28px;
}
.readout__cell {
  padding: 14px 12px;
  background: var(--paper-card);
  border: 1px solid var(--line);
  border-radius: var(--r-panel);
}
.readout__cell--on { background: var(--ink); border-color: var(--ink); color: var(--paper); }
.readout__label {
  display: block;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--muted-text);
}
.readout__cell--on .readout__label { color: rgba(255,255,255,0.7); }
:root[data-theme="dark"] .readout__cell--on .readout__label { color: rgba(10,10,10,0.6); }

.readout__value {
  display: block;
  margin-top: 5px;
  font-size: 26px;
  font-weight: 700;
  letter-spacing: -0.03em;
}
.readout__unit { font-size: 16px; font-weight: 600; }

/* offline illustration — the sync plays through on a loop, so the state line
   holds both readings stacked and cross-fades between them. */
.offline-card {
  width: min(340px, 100%);
  padding: 24px;
  background: var(--paper-card);
  border-radius: var(--r-card);
  box-shadow: var(--sh-card);
}
.offline-card__row,
.offline-card__foot {
  display: flex;
  align-items: center;
  gap: 10px;
  font-size: 13px;
  font-weight: 600;
  color: var(--muted-text);
}
.offline-card__state { display: grid; }
.offline-card__state-txt { grid-area: 1 / 1; }

.offline-card__big {
  margin: 16px 0 14px;
  font-size: 24px;
  font-weight: 700;
  letter-spacing: -0.03em;
  color: var(--ink);
}
.offline-card__list {
  margin: 0 0 18px;
  padding: 16px;
  list-style: none;
  display: grid;
  gap: 12px;
  background: var(--surface);
  border-radius: var(--r-panel);
  font-size: 15px;
}
.offline-card__list li { display: flex; gap: 10px; align-items: center; }
.offline-card__list span { color: var(--muted-text); font-weight: 700; }
.offline-card__foot { padding-top: 16px; border-top: 1px solid var(--line); }

.dot { width: 8px; height: 8px; border-radius: 50%; flex: none; }
.dot--off { background: var(--muted); }
.dot--on { background: var(--ink); }

/* progress stats */
.stat-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 12px;
  width: min(400px, 100%);
}
.stat {
  padding: 20px;
  background: var(--paper-card);
  border-radius: var(--r-panel);
  box-shadow: var(--sh-card);
}
.stat--wide { grid-column: 1 / -1; }
.stat__label {
  display: block;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--muted-text);
}
.stat__value {
  display: block;
  margin: 8px 0 5px;
  font-size: 36px;
  font-weight: 700;
  letter-spacing: -0.04em;
  line-height: 1;
}
.stat__unit { font-size: 18px; color: var(--muted-text); }
.stat__note { font-size: 13px; color: var(--muted-text); }

/* ─────────────────────────────  cards grid  ───────────────────────────── */

.grid-section { padding-block: var(--section-y-lg); }

.grid-section__title {
  font-size: clamp(28px, 3.2vw, 40px);
  font-weight: 600;
  letter-spacing: -0.02em;
  margin-bottom: 32px;
}

.cards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
  gap: 12px;
  margin: 0;
  padding: 0;
  list-style: none;
}

.card {
  padding: 26px;
  background: var(--paper-card);
  border-radius: var(--r-card);
  box-shadow: var(--sh-card);
  transition: transform var(--t-pop) var(--e-snap),
              box-shadow var(--t-pop) var(--e-snap);
}
.card:hover { transform: translateY(-3px); box-shadow: var(--sh-lift-soft); }

/* Staggered on the way in; the row index keeps the wave diagonal rather than
   running the whole grid one card at a time. */
.js .card { opacity: 0; transform: translateY(18px); }
.card.is-in {
  opacity: 1;
  transform: none;
  transition: opacity 520ms var(--e-snap), transform 520ms var(--e-snap),
              box-shadow var(--t-pop) var(--e-snap);
  transition-delay: calc(var(--i, 0) * 70ms);
}
.card h3 { font-size: 18px; font-weight: 600; letter-spacing: -0.015em; }
.card p { margin: 8px 0 0; font-size: 15px; line-height: 1.5; color: var(--muted-text); }

/* ─────────────────────────────  CTA  ───────────────────────────── */

.cta {
  margin-inline: var(--gutter);
  padding-block: clamp(64px, 7vw, 100px);
  background: var(--ink);
  color: var(--on-ink);
  border-radius: 40px;
}
.cta__inner { text-align: center; }
.cta__title { font-size: clamp(32px, 4.2vw, 52px); font-weight: 600; letter-spacing: -0.02em; }
.cta__sub {
  max-width: 50ch;
  margin: 20px auto 0;
  font-size: 18px;
  color: var(--on-ink-muted);
}
.cta__actions {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  justify-content: center;
  margin-top: 32px;
}
.cta__fine { margin: 18px 0 0; font-size: 14px; color: var(--on-ink-muted); }

/* ─────────────────────────────  footer  ───────────────────────────── */

.footer { padding-block: clamp(56px, 6vw, 80px) 28px; }

.footer__inner {
  display: grid;
  grid-template-columns: 2fr 1fr;
  gap: 40px;
  padding-bottom: 40px;
}
.footer__brand img { width: auto; height: 22px; }
.footer__brand p { margin: 14px 0 0; max-width: 28ch; font-size: 15px; color: var(--muted-text); }

.footer__heading {
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--muted-text);
  margin-bottom: 8px;
}
.footer__links { display: flex; flex-direction: column; gap: 2px; }
.footer__links a {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  font-size: 16px;
  color: var(--ink);
  text-decoration: none;
  transition: color var(--t-press) var(--e-snap);
}
.footer__links a:hover { color: var(--muted-text); }

.footer__base { padding-top: 24px; border-top: 1px solid var(--line); }
.footer__base p { margin: 0; font-size: 14px; color: var(--muted-text); }

/* ─────────────────────────────  reveal  ───────────────────────────── */

.js .reveal {
  opacity: 0;
  transform: translateY(16px);
  transition: opacity 560ms var(--e-snap), transform 560ms var(--e-snap);
}
.reveal.is-in { opacity: 1; transform: none; }

/* ─────────────────────────────  responsive  ───────────────────────────── */

@media (max-width: 1000px) {
  .nav__links { display: none; }
  .nav__cta { margin-inline-start: auto; }

  .hero__inner,
  .feature__inner,
  .help__inner { grid-template-columns: minmax(0, 1fr); }

  .hero__inner { gap: 8px; min-height: 0; }
  .hero__art { min-height: 460px; order: 2; margin-top: 16px; }
  .hero__sub { max-width: none; }

  .feature--reverse .panel { order: 0; }
  .feature__copy { order: 1; }

  .footer__inner { grid-template-columns: 1fr; }
}

/* Narrow: once the hero is one column the phones size off its full width, so
   they are capped in pixels here and the copy above them is centred. */
@media (max-width: 1000px) {
  /* Both phones, upright and stacked: the front one drops into flow so it sets
     the container's height, the back stays absolute, smaller and behind.

     One column is far narrower than the hero's art half, so the desktop stage
     runs the back phone off the edge. Pull it in to where the pair's outer
     edges sit the same distance from the centre line: with the back at 92% of
     the front, that is (0.14 + (1 - 0.92) / 2) / 0.92 ≈ 19.6% of its own
     width, and it leaves ~30% of the back phone showing. */
  .hero__art {
    min-height: 0;
    margin-top: 36px;
  }
  .hero__art .phone--front {
    position: relative;            /* in flow: it sets the container's height */
    width: min(236px, 64%);
  }
  .hero__art .phone--back {
    width: min(217px, 58.9%);      /* 92% of the front phone */
    --tx: 19.6%;
  }

  /* One column reads better centred, and it balances the phone below it. */
  .hero__copy { text-align: center; }
  .hero__sub { margin-inline: auto; }
  .hero__actions { justify-content: center; }
  .hero__facts { justify-content: center; }
}

@media (max-width: 560px) {
  :root { --gutter: 22px; }
  body { font-size: 17px; }

  .hero__title { font-size: clamp(34px, 9.5vw, 46px); }
  .hero__actions .btn { flex: 1 1 100%; }

  /* One height for every section image. 10/9 is another 10% off the square,
     so the page does not feel like a column of tall grey boxes. Everything
     that sits in a panel is sized below to fit this height — only the phones
     are meant to run past it. */
  .panel {
    aspect-ratio: 10 / 9;
    min-height: 0;
    overflow: hidden;
  }
  /* A phone runs off the bottom edge rather than leaving a gap under it.
     --shift slides the screen up so the part worth seeing lands in the window
     instead of the navigation bar at the top of it. */
  .panel:has(.phone) { place-items: start center; padding: 40px 20px 0; }
  /* Narrower than on desktop, so a shallower window still reaches past the
     navigation bar to the part worth seeing — and the top of the phone stays
     in frame, which is what makes it read as a phone at all. */
  .panel .phone--static { width: min(214px, 62%); }
  .panel:has(.phone) .phone { margin-top: var(--shift, 0px); }

  #plan .panel { --shift: 0px; }         /* the day cards start at the top */
  /* The calculator needs the bar and the plate buttons in frame, which used to
     mean cutting 110px off the top — and a phone with no top reads as a
     cropped picture, not a phone. It now sits at the same height as the day
     list and runs off the bottom of the panel instead, which is the one crop
     that looks deliberate. */
  #calculator .panel { --shift: 0px; }
  /* A card that outgrows the panel is pinned to the top and clipped at the
     bottom rather than centred, so these three are measured to clear the
     shorter panel at 360px — the narrowest width the layout targets. */
  .panel:has(.offline-card),
  .panel:has(.stat-grid),
  .panel:has(.pegs) { place-items: center; padding: 16px; }

  .readout { grid-template-columns: 1fr; }
  .readout__cell { display: flex; align-items: baseline; justify-content: space-between; }
  .readout__value { margin-top: 0; }

  /* Two tiles side by side with the third across the bottom — one column
     stacked three deep could not fit the panel. The type comes down with the
     column width so the pair still breathes. */
  /* One line per label, and the number is the point: the note only earns its
     place on the full-width tile, where there is room for it on one line. With
     the other two gone the tiles can carry their desktop weight and still
     clear the panel. */
  .stat-grid { gap: 10px; }
  .stat { padding: 16px; }
  .stat__label { font-size: 10.5px; letter-spacing: 0.07em; }
  .stat:not(.stat--wide) .stat__note { display: none; }
  .stat__value { font-size: 30px; margin: 8px 0 4px; }
  .stat__unit { font-size: 16px; }
  .stat__note { font-size: 12.5px; }
  .help__list { padding-inline: 18px; }
  .cta { border-radius: 30px; }

  /* The hero carries more air than the sections below it. */
  .hero .shell { padding-inline: 30px; }

  /* Internal padding: the page gutter alone left text close to the card edge,
     which is tight on a narrow iPhone and worse on a wide Android. */
  .cta .shell { padding-inline: 28px; }
  .cta__title { font-size: clamp(28px, 8vw, 38px); }

  /* .pegs, .how__card and .soon__inner are all declared further down the
     sheet than this breakpoint, so a bare class here loses on source order
     however narrow the screen is. Each one is scoped to its parent to win it
     back — hence the padding below never applying before. */
  .shell .soon__inner { padding: 34px 26px; }
  .card { padding: 24px 22px; }
  .how__step .how__card { padding: 22px; }
  /* Sized to the shorter panel: the card has to clear it whole, since a
     clipped card would read as a mistake rather than a crop. */
  .offline-card { padding: 18px; }
  .offline-card__row,
  .offline-card__foot { font-size: 12.5px; }
  .offline-card__big { margin: 10px 0 9px; font-size: 19px; }
  .offline-card__list { margin-bottom: 0; padding: 12px; gap: 8px; font-size: 13.5px; }
  .offline-card__foot { padding-top: 11px; margin-top: 11px; }
  /* With the padding finally applying, the diagram has room to keep most of
     its desktop spacing inside the shorter panel. */
  .panel .pegs { padding: 20px; }
  .panel .pegs__list { gap: 12px; }
  .panel .pegs__total { margin-top: 18px; padding-top: 15px; }
  .panel .pegs__total-value { font-size: 28px; }
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    transition-duration: 0.01ms !important;
  }
  .reveal { opacity: 1; transform: none; }
}

/* ── stagger indices ──────────────────────────────────────────────────────
   These live here rather than as style="--i:n" in the markup so the site can
   ship a Content-Security-Policy that forbids inline style attributes. */
.how__step:nth-child(1) { --i: 0; }
.how__step:nth-child(2) { --i: 1; }
.how__step:nth-child(3) { --i: 2; }

.peg:nth-child(1) { --i: 0; }
.peg:nth-child(2) { --i: 1; }
.peg:nth-child(3) { --i: 2; }

.offline-card__list li:nth-child(1) { --i: 0; }
.offline-card__list li:nth-child(2) { --i: 1; }
.offline-card__list li:nth-child(3) { --i: 2; }

.stat:nth-child(1) { --i: 0; }
.stat:nth-child(2) { --i: 1; }
.stat:nth-child(3) { --i: 2; }

.cards .card:nth-child(1) { --i: 0; }
.cards .card:nth-child(2) { --i: 1; }
.cards .card:nth-child(3) { --i: 2; }
.cards .card:nth-child(4) { --i: 3; }
.cards .card:nth-child(5) { --i: 4; }
.cards .card:nth-child(6) { --i: 5; }
.cards .card:nth-child(7) { --i: 6; }
.cards .card:nth-child(8) { --i: 7; }

/* ═══════════════════════════  IN-SECTION MOTION  ═══════════════════════════
   Each figure plays once, when it is reached. Everything here is inert until
   JS adds .is-in, and the reduced-motion block at the end of the file switches
   the lot off — the static state is always the readable one. */

/* ── the eyebrow, restored above each feature heading ── */
.eyebrow {
  margin: 0 0 14px;
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--muted-text);
}

/* ── phones and panels rise in ── */
/* Scoped to .js: without scripting nothing ever adds .is-in, and an
   unscoped opacity:0 would hide every figure on the page. */
.js .panel { opacity: 0; transform: translateY(28px) scale(0.985); }
.panel.is-in {
  opacity: 1;
  transform: none;
  transition: opacity 700ms var(--e-snap), transform 700ms var(--e-snap);
}

/* ── hero phones drift in, then breathe ── */
.js .hero__art .phone {
  opacity: 0;
  animation: phone-in 900ms var(--e-snap) forwards;
}
.phone--front { animation-delay: 120ms; }
.phone--back  { animation-delay: 280ms; }

@keyframes phone-in {
  from { opacity: 0; transform: translate(var(--tx, 0), calc(var(--ty, 0) + 40px)) rotate(var(--rot, 0deg)) scale(0.96); }
  to   { opacity: var(--end-opacity, 1); transform: translate(var(--tx, 0), var(--ty, 0)) rotate(var(--rot, 0deg)); }
}

/* --tx / --ty are set with the layout above; the entry animation only needs
   to know where the pair comes to rest. */
.phone--back { --end-opacity: 0.92; }

/* A slow float, so the hero is never quite still. */
.hero__art.is-float .phone--front { animation: phone-in 900ms var(--e-snap) forwards, float-phone 7s ease-in-out 1.1s infinite; }
.hero__art.is-float .phone--back  { animation: phone-in 900ms var(--e-snap) 280ms forwards, float-phone 9s ease-in-out 1.4s infinite; }

/* Each phone breathes around wherever it rests, so the float never undoes the
   centring. The two differ by duration, which is enough to read as two. */
@keyframes float-phone {
  0%, 100% { transform: translate(var(--tx, 0), var(--ty, 0)); }
  50%      { transform: translate(var(--tx, 0), calc(var(--ty, 0) - 1.4%)); }
}

/* ── a light sweep passes over each screen once, when it arrives ── */
.phone { position: relative; }
.phone__sweep {
  position: absolute;
  inset: 10px;
  border-radius: calc(13.8% - 7px) / calc(6.38% - 7px);
  pointer-events: none;
  background: linear-gradient(105deg, transparent 38%, rgba(255,255,255,0.55) 50%, transparent 62%);
  transform: translateX(-120%);
  opacity: 0;                    /* only exists while it is sweeping */
}

/* Feature phones sweep as their panel is reached. */
.panel.is-in .phone__sweep { animation: sweep 1400ms var(--e-snap) 420ms 1; }

/* The hero pair sweep once the entry animation has settled, one after the
   other so it reads as two screens, not one flash. */
.hero__art.is-float .phone--front .phone__sweep { animation: sweep 1400ms var(--e-snap) 1 backwards; }
.hero__art.is-float .phone--back .phone__sweep { animation: sweep 1400ms var(--e-snap) 260ms 1 backwards; }

@keyframes sweep {
  from { opacity: 1; transform: translateX(-120%); }
  to   { opacity: 1; transform: translateX(120%); }
}

/* ── calculator: cross-fade through three real loads ── */
.phone--stack { display: grid; }
/* Without scripting the first frame stays visible and the rest never
   appear, which is exactly the static fallback we want. */
.phone--stack img { grid-area: 1 / 1; opacity: 0; transition: opacity 340ms var(--e-snap); }
/* The hero is a walk through the app rather than a demonstration of one
   control, so it changes more slowly and fades for longer. */
.hero__art .phone--stack img { transition-duration: 620ms; }
:root:not(.js) .phone--stack img:first-child { opacity: 1; }
.phone--stack img.is-on { opacity: 1; }

/* ── weight profile diagram ── */
.pegs {
  width: min(360px, 100%);
  padding: 26px;
  background: var(--paper-card);
  border-radius: var(--r-card);
  box-shadow: var(--sh-card);
}
.pegs__title {
  margin: 0 0 20px;
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--muted-text);
}
.pegs__list { margin: 0; padding: 0; list-style: none; display: grid; gap: 14px; }
.js .peg {
  display: grid;
  grid-template-columns: 54px 1fr auto;
  align-items: center;
  gap: 12px;
  opacity: 0;
  transform: translateX(-10px);
}
[data-anim="pegs"].is-in .peg {
  opacity: 1;
  transform: none;
  transition: opacity 500ms var(--e-snap), transform 500ms var(--e-snap);
  transition-delay: calc(var(--i) * 180ms + 250ms);
}
.peg__no { font-size: 13px; font-weight: 600; color: var(--muted-text); }
.peg__bar {
  height: 10px;
  border-radius: var(--r-pill);
  background: var(--surface);
  overflow: hidden;
}
.peg__fill {
  display: block;
  height: 100%;
  width: 0;
  border-radius: var(--r-pill);
  background: var(--ink);
}
[data-anim="pegs"].is-in .peg:nth-child(1) .peg__fill { animation: fill 700ms var(--e-snap) 420ms forwards; --w: 100%; }
[data-anim="pegs"].is-in .peg:nth-child(2) .peg__fill { animation: fill 700ms var(--e-snap) 600ms forwards; --w: 100%; }
[data-anim="pegs"].is-in .peg:nth-child(3) .peg__fill { animation: fill 700ms var(--e-snap) 780ms forwards; --w: 50%; }
@keyframes fill { to { width: var(--w); } }

.peg__kg { font-size: 16px; font-weight: 700; letter-spacing: -0.02em; }
.peg__kg span { font-size: 12px; font-weight: 600; color: var(--muted-text); }

.pegs__total {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  margin-top: 22px;
  padding-top: 18px;
  border-top: 1px solid var(--line);
}
.pegs__total-label {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--muted-text);
}
.pegs__total-value { font-size: 32px; font-weight: 700; letter-spacing: -0.04em; }
.pegs__total-value span { font-size: 17px; font-weight: 600; color: var(--muted-text); }

/* ── offline card: the whole story runs on a loop ──────────────────────────
   Three sets get logged with no signal (ticks appear, but grey, the way the
   app greys an unsent set), the connection comes back, the ticks check through
   in order as each set goes up, and the footer counts them. Then it drops the
   connection and does it again. JS only toggles .is-online; every step below
   is a transition off that one class, so the loop cannot fall out of sync with
   itself. */
.js .offline-card__tick {
  width: 17px; height: 17px;
  flex: none;
  background: var(--muted);
  -webkit-mask: var(--tick) center / contain no-repeat;
  mask: var(--tick) center / contain no-repeat;
  opacity: 0;
  transform: scale(0.5);
}
/* logged locally: they appear in order, still grey */
[data-anim="sync"].is-in .offline-card__tick {
  opacity: 1;
  transform: none;
  transition: opacity 320ms var(--e-snap) calc(var(--i) * 260ms + 500ms),
              transform 320ms var(--e-snap) calc(var(--i) * 260ms + 500ms),
              background-color 260ms linear;
}
/* signal back: each one goes solid as its set lands */
.offline-card.is-online .offline-card__tick {
  background: var(--ink);
  transition: background-color 260ms linear calc(var(--i) * 190ms);
}

/* the state line swaps over, and the dot with it. The two readings share one
   grid cell, so exactly one of them has to be visible at any time — including
   with no scripting at all, where the card rests on the end of the story. */
.offline-card__state-txt--off { opacity: 0; }
.offline-card__state-txt--on  { opacity: 1; }
.js .offline-card__state-txt--off { opacity: 1; }
.js .offline-card__state-txt--on  { opacity: 0; }
.js .offline-card__state-txt { transition: opacity 320ms var(--e-snap); }
.js .offline-card.is-online .offline-card__state-txt--off { opacity: 0; }
.js .offline-card.is-online .offline-card__state-txt--on  { opacity: 1; }

.offline-card__row .dot { transition: background-color 320ms var(--e-snap); }
.offline-card.is-online .offline-card__row .dot {
  background: var(--ink);
  animation: pulse 1.8s ease-out 2;
}

.js .offline-card__foot > * {
  opacity: 0;
  transform: translateY(6px);
  transition: opacity 260ms var(--e-snap), transform 260ms var(--e-snap);
}
.offline-card.is-online .offline-card__foot > * {
  opacity: 1;
  transform: none;
  transition: opacity 420ms var(--e-snap) 640ms, transform 420ms var(--e-snap) 640ms;
}
@keyframes pulse {
  0%   { box-shadow: 0 0 0 0 rgba(128,128,128,0.55); }
  70%  { box-shadow: 0 0 0 9px rgba(128,128,128,0); }
  100% { box-shadow: 0 0 0 0 rgba(128,128,128,0); }
}

/* ── stat tiles stagger ── */
.js .stat { opacity: 0; transform: translateY(14px); }
[data-anim="stats"].is-in .stat {
  opacity: 1;
  transform: none;
  transition: opacity 520ms var(--e-snap), transform 520ms var(--e-snap);
  transition-delay: calc(var(--i) * 140ms + 200ms);
}

/* ── coming soon ── */
.soon { padding-block: var(--section-y); }
.soon__inner {
  max-width: 640px;
  margin-inline: auto;
  padding: clamp(40px, 5vw, 64px);
  text-align: center;
  background: var(--panel);
  border: 1px dashed var(--line);
  border-radius: var(--r-card);
}
.soon__tag {
  margin: 0;
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--muted-text);
}
/* ── phase two: the form is behind its own button ───────────────────────── */
.notify { margin-top: 28px; }

/* .btn already sets inline-flex, which is enough to drop the marker in most
   engines; the other two lines are for the ones where it is not. */
.notify__open {
  list-style: none;
  cursor: pointer;
}
.notify__open::-webkit-details-marker { display: none; }
.notify__open::marker { content: ""; }

/* Once it is open the button has done its job, so it steps back to an outline
   and keeps working as the way to close it again. */
.notify[open] .notify__open {
  background: transparent;
  color: var(--ink);
  border-color: var(--line);
}
.notify[open] .notify__open:hover { background: var(--surface); border-color: var(--muted); }

.notify__form { margin-top: 24px; }
.notify[open] .notify__form { animation: faq-open 260ms var(--e-snap); }

.soon__title {
  margin: 12px 0 0;
  font-size: clamp(26px, 3vw, 38px);
  font-weight: 600;
  letter-spacing: -0.02em;
}
.soon__sub {
  margin: 14px auto 0;
  max-width: 42ch;
  font-size: 17px;
  color: var(--muted-text);
}


/* ── nothing above moves when the reader has asked for stillness ── */
@media (prefers-reduced-motion: reduce) {
  .panel,
  .hero__art .phone,
  .peg,
  .stat,
  .offline-card__tick,
  .offline-card__foot > * {
    opacity: 1 !important;
    transform: none !important;
    animation: none !important;
  }
  .peg__fill { width: var(--w, 100%) !important; animation: none !important; }
  .phone__sweep { display: none; }
  /* The sync loop never starts, so the card rests on the end of its story
     rather than on "no connection" with the sets already sent. */
  .offline-card__state-txt--off { opacity: 0 !important; }
  .offline-card__state-txt--on  { opacity: 1 !important; }
  .offline-card__tick { background: var(--ink) !important; }
  /* Still, but still staged: the pair keeps its resting offsets. */
  .hero__art .phone { transform: translate(var(--tx, 0), var(--ty, 0)) !important; }
  .hero__art .phone--back { opacity: 0.92 !important; }
}

/* ─────────────────────  problem → fix connector  ───────────────────── */

/* One movement, not two sections: the statement, a line drawn down out of it,
   then the answer. */
.connector {
  display: grid;
  place-items: center;
  padding-block: clamp(28px, 3.5vw, 48px);
}
.connector span {
  position: relative;
  display: block;
  width: 2px;
  height: clamp(56px, 7vw, 104px);
  background: var(--line);
  border-radius: 2px;
}
.connector span::after {
  content: "";
  position: absolute;
  inset: 0;
  background: var(--ink);
  border-radius: 2px;
  transform: scaleY(0);
  transform-origin: top center;
}
.connector.is-in span::after {
  transform: scaleY(1);
  transition: transform 760ms var(--e-glide) 120ms;
}

/* ═══════════════════════════  HOW IT WORKS  ═══════════════════════════ */

.how { padding-block: clamp(32px, 4vw, 52px) var(--section-y-lg); }

.how__head { max-width: 46ch; margin: 0 auto clamp(48px, 5vw, 72px); text-align: center; }
.how__head .eyebrow { margin-bottom: 12px; }

.how__title {
  font-size: clamp(30px, 3.4vw, 44px);
  font-weight: 600;
  letter-spacing: -0.025em;
}
.how__lead { margin: 16px 0 0; font-size: 18px; color: var(--muted-text); }

/* The three steps hang off one track, so the section reads as a sequence
   rather than three unrelated columns. */
.how__list {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: clamp(20px, 2.5vw, 32px);
  margin: 0;
  padding: 0;
  list-style: none;
}

.how__step { display: grid; }

.how__card {
  display: grid;
  align-content: start;
  gap: 16px;
  width: 100%;
  height: 100%;                 /* all three match, whatever the copy does */
  padding: 30px;
  background: var(--panel);
  border-radius: var(--r-card);
}
.how__no {
  display: grid;
  place-items: center;
  width: 34px;
  height: 34px;
  border-radius: 50%;
  background: var(--ink);
  color: var(--paper);
  font-size: 15px;
  font-weight: 700;
  letter-spacing: -0.02em;
}
.how__card h3 {
  margin: 0;
  font-size: 21px;
  font-weight: 600;
  letter-spacing: -0.02em;
}
.how__card p {
  margin: 0;
  font-size: 16px;
  line-height: 1.5;
  color: var(--muted-text);
}

/* Cards arrive one after another. */
.js .how__step { opacity: 0; transform: translateY(20px); }
.how__step.is-in {
  opacity: 1;
  transform: none;
  transition: opacity 560ms var(--e-snap), transform 560ms var(--e-snap);
  transition-delay: calc(var(--i) * 150ms + 220ms);
}

@media (max-width: 1000px) {
  .how__list { grid-template-columns: 1fr; gap: 12px; }
}

/* Mobile: the steps are read one after another, so they don't each need a
   full card's worth of air. */
@media (max-width: 700px) {
  .how { padding-block: clamp(28px, 6vw, 44px) clamp(44px, 8vw, 64px); }
  .how__head { margin-bottom: 28px; }
  .how__card { padding: 20px; gap: 12px; }
  .how__card h3 { font-size: 19px; }
  .how__card p { font-size: 15px; }
  .how__no { width: 30px; height: 30px; font-size: 14px; }
}

/* ═══════════════════════════  OBJECTIONS  ═══════════════════════════ */

.faq { padding-block: var(--section-y-lg); }

.faq__inner {
  display: grid;
  grid-template-columns: minmax(0, 0.85fr) minmax(0, 1.15fr);
  gap: clamp(32px, 5vw, 80px);
  align-items: start;
}

.faq__copy h2 {
  font-size: clamp(28px, 3.2vw, 40px);
  font-weight: 600;
  letter-spacing: -0.02em;
}
.faq__copy p {
  margin: 16px 0 0;
  font-size: 17px;
  color: var(--muted-text);
  max-width: 34ch;
}
.faq__copy a,
.faq__list a { color: var(--ink); text-decoration: underline; text-underline-offset: 3px; }
.faq__copy a:hover,
.faq__list a:hover { color: var(--muted-text); }

/* Accordion, built on <details> so it opens without JavaScript and keeps
   keyboard and screen-reader behaviour for free. */
.faq__list { display: grid; gap: 10px; }

.faq__item {
  background: var(--panel);
  border-radius: var(--r-panel);
  overflow: hidden;
}

.faq__item summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  min-height: 60px;
  padding: 16px 20px;
  font-size: 17px;
  font-weight: 600;
  letter-spacing: -0.01em;
  cursor: pointer;
  list-style: none;                /* the default marker, replaced by our own */
  transition: color var(--t-press) var(--e-snap);
}
.faq__item summary::-webkit-details-marker { display: none; }
.faq__item summary:hover { color: var(--muted-text); }
.faq__item summary { -webkit-tap-highlight-color: transparent; }
/* No ring for a mouse click; :focus-visible still covers the keyboard. */
.faq__item summary:focus { outline: none; }
.faq__item summary:focus-visible {
  outline: 2px solid currentColor;
  outline-offset: -3px;
  border-radius: var(--r-panel);
}

/* A plus drawn from two bars; the vertical one rotates away when open. */
.faq__icon {
  position: relative;
  flex: none;
  width: 18px;
  height: 18px;
}
.faq__icon::before,
.faq__icon::after {
  content: "";
  position: absolute;
  inset: 50% 0 auto 0;
  height: 2px;
  margin-top: -1px;
  border-radius: 2px;
  background: var(--ink);
  transition: transform var(--t-pop) var(--e-snap);
}
.faq__icon::after { transform: rotate(90deg); }
.faq__item[open] .faq__icon::after { transform: rotate(0deg); }

.faq__answer { padding: 0 20px 20px; }
.faq__answer p {
  margin: 0;
  font-size: 16px;
  line-height: 1.5;
  color: var(--muted-text);
  max-width: 52ch;
}
.faq__item[open] .faq__answer { animation: faq-open 260ms var(--e-snap); }
@keyframes faq-open {
  from { opacity: 0; transform: translateY(-4px); }
  to   { opacity: 1; transform: none; }
}

@media (max-width: 1000px) {
  .faq__inner { grid-template-columns: minmax(0, 1fr); }
  .faq__copy p { max-width: none; }
}

@media (prefers-reduced-motion: reduce) {
  .how__step { opacity: 1 !important; transform: none !important; }
  .connector span::after { transform: scaleY(1) !important; }
  .card { opacity: 1 !important; transform: none !important; }
}

/* ═══════════════════════════  WHY  ═══════════════════════════ */

.why { padding-block: var(--section-y-lg); }

.why__inner {
  /* 44ch put the body on four short lines. Wider lets it settle into two or
     three without running past a comfortable measure. */
  max-width: 62ch;
  margin-inline: auto;
  text-align: center;
}

.why__title {
  font-family: var(--font-display);
  font-size: clamp(30px, 3.8vw, 50px);
  font-weight: 600;
  line-height: 1.1;
  letter-spacing: -0.028em;
}

.why__body {
  text-wrap: balance;
  margin: 20px 0 0;
  font-size: 18px;
  line-height: 1.5;
  color: var(--muted-text);
}


/* ── late overrides ───────────────────────────────────────────────────────
   These sit at the end because the rules they override are declared later in
   the file than the breakpoint blocks above, and a media query carries no
   extra specificity. */
@media (max-width: 560px) {
  .stat__label,
  .pegs__total-label { font-size: 12px; }
}

/* ═══════════════════════════  REQUEST FORM  ═══════════════════════════
   Posts to Netlify Forms. Submissions land in the Netlify UI; turn on the
   email notification under Forms → Settings, or nothing will reach an inbox. */

/* The form is used twice — on the black CTA band and on the light phase-two
   panel — so its colours come from three local properties rather than the
   inverted palette directly. The band re-points them just below. */
.request {
  --form-ink: var(--ink);
  --form-muted: var(--muted-text);
  --form-line: var(--line);

  display: grid;
  gap: 14px;
  width: min(420px, 100%);
  margin: 36px auto 0;
  text-align: left;
}
.cta .request {
  --form-ink: var(--on-ink);
  --form-muted: var(--on-ink-muted);
  --form-line: var(--on-ink-line);
}

/* The honeypot: a real field, hidden from people, catching bots that fill
   everything. Not display:none — some bots skip those. */
.request__trap {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

.request__field { display: grid; gap: 7px; }
.request__field label {
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--form-muted);
}
.request__field input {
  width: 100%;
  min-height: 52px;
  padding: 0 16px;
  font: inherit;
  font-size: 16px;                /* 16px or iOS zooms the page on focus */
  color: var(--form-ink);
  background: transparent;
  border: 1px solid var(--form-line);
  border-radius: var(--r-control);
  transition: border-color var(--t-press) var(--e-snap);
}
.request__field input:hover { border-color: var(--form-muted); }
.request__field input:focus {
  outline: none;
  border-color: var(--form-ink);
}
.request__field input:focus-visible { outline: 2px solid var(--form-ink); outline-offset: 2px; }

.request__check {
  display: grid;
  grid-template-columns: 22px 1fr;
  gap: 12px;
  align-items: start;
  min-height: 44px;
  padding: 4px 0;
  font-size: 15px;
  line-height: 1.4;
  color: var(--form-muted);
  cursor: pointer;
}
.request__check input {
  width: 22px;
  height: 22px;
  margin: 0;
  accent-color: var(--form-ink);
  cursor: pointer;
}
.request__check:hover { color: var(--form-ink); }

.request__submit { width: 100%; margin-top: 8px; }

.request__fine {
  margin: 0;
  font-size: 13px;
  line-height: 1.5;
  color: var(--form-muted);
  text-align: center;
}

/* ── thank-you page ── */
.thanks {
  display: grid;
  place-items: center;
  min-height: 100vh;
  min-height: 100svh;
  padding: 40px var(--gutter);
  text-align: center;
}
.thanks__inner { max-width: 44ch; }
.thanks__mark { width: 128px; height: auto; margin: 0 auto 36px; }
.thanks h1 {
  font-family: var(--font-display);
  font-size: clamp(30px, 5vw, 46px);
  font-weight: 600;
  letter-spacing: -0.03em;
}
.thanks p { margin: 18px 0 0; font-size: 18px; color: var(--muted-text); }
.thanks .btn { margin-top: 34px; }

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .thanks__mark { filter: invert(1); }
}
:root[data-theme="dark"] .thanks__mark { filter: invert(1); }
