/* ============================================================================
   GearGenius marketing site
   ---------------------------------------------------------------------------
   The design system is not invented here. Every token below is lifted from the
   app itself (lib/theme/app_colors.dart, lib/theme/app_theme.dart) or measured
   off the same prototypes the app was built against, so the site and the
   product read as one thing. See DESIGN.md for the provenance of each value.
   ========================================================================= */

/* ── Typeface ───────────────────────────────────────────────────────────────
   Inter, self-hosted from the app's own assets/fonts. Inter Display is the
   tighter optical cut and is used above ~32px, exactly as AppTheme does.     */

@font-face {
  font-family: 'Inter';
  src: url('../fonts/Inter-Regular.ttf') format('truetype');
  font-weight: 400;
  font-display: swap;
}
@font-face {
  font-family: 'Inter';
  src: url('../fonts/Inter-Medium.ttf') format('truetype');
  font-weight: 500;
  font-display: swap;
}
@font-face {
  font-family: 'Inter';
  src: url('../fonts/Inter-SemiBold.ttf') format('truetype');
  font-weight: 600;
  font-display: swap;
}
@font-face {
  font-family: 'Inter';
  src: url('../fonts/Inter-Bold.ttf') format('truetype');
  font-weight: 700;
  font-display: swap;
}
@font-face {
  font-family: 'InterDisplay';
  src: url('../fonts/InterDisplay-SemiBold.ttf') format('truetype');
  font-weight: 600;
  font-display: swap;
}

/* ── Tokens ─────────────────────────────────────────────────────────────── */

:root {
  color-scheme: dark;

  /* Surfaces, sampled off 05_Home(FREE).PNG */
  --ink: #080808;          /* scaffold            AppColors.darkBg    */
  --card-top: #0a0d11;     /* card gradient top   AppColors.cardTop   */
  --card-bottom: #05080d;  /* card gradient base  AppColors.cardBottom*/
  --panel: #111217;        /* nav tiles, rows     AppColors.navCardBg */
  --chip: #222323;         /* icon chips          AppColors.iconChipBg*/
  --hairline: rgba(255, 255, 255, 0.055); /*      AppColors.cardBorder*/
  --hairline-strong: rgba(255, 255, 255, 0.11);
  --divider: #222222;

  /* Accent. One green, everywhere. */
  --accent: #6dde26;
  --accent-soft: rgba(109, 222, 38, 0.15);
  --accent-line: rgba(109, 222, 38, 0.32);

  /* Health buckets. Semantic only: these appear where a score is being
     explained, never as decoration. */
  --good: #6dde26;
  --fair: #ffb020;
  --poor: #ff3b30;
  --gold: #e8b93b;         /* reserved for the Go Pro affordance      */

  /* Text */
  --text: #ffffff;
  --text-2: rgba(255, 255, 255, 0.66);
  --text-3: #888888;       /* AppColors.subtitleGray                  */
  --text-caps: rgba(255, 255, 255, 0.42);

  /* Radii. Documented scale: page chrome uses 20 / 14 / 10 / pill.
     The dashboard replica keeps the app's own 14 / 13 / pill. */
  --r-card: 20px;
  --r-inner: 14px;
  --r-chip: 10px;
  --r-pill: 999px;

  /* Motion. One curve for structural movement, one for entrances. */
  --ease: cubic-bezier(0.32, 0.72, 0, 1);
  --ease-out: cubic-bezier(0.16, 1, 0.3, 1);

  /* Rhythm */
  --gutter: clamp(1.25rem, 4vw, 3rem);
  --measure: 65ch;
  --section-y: clamp(6rem, 14vh, 11rem);
  --shell: 1320px;
}

/* ── Reset and base ─────────────────────────────────────────────────────── */

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

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  scroll-padding-top: 6rem;
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
}

body {
  margin: 0;
  background: var(--ink);
  color: var(--text);
  font-family: 'Inter', system-ui, -apple-system, sans-serif;
  font-size: clamp(1rem, 0.96rem + 0.2vw, 1.125rem);
  line-height: 1.62;
  font-synthesis: none;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  overflow-x: hidden;
}

img { max-width: 100%; height: auto; display: block; }
a { color: inherit; text-decoration: none; }
button { font: inherit; color: inherit; }

::selection { background: var(--accent); color: #000; }

:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
  border-radius: 4px;
}

/* The prototypes are colour graded with a fine grain. A fixed, non-interactive
   overlay reproduces it without ever repainting a scrolling layer. */
.grain {
  position: fixed;
  inset: 0;
  z-index: 5;
  pointer-events: none;
  opacity: 0.032;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
}

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

.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 100;
  padding: 0.75rem 1.25rem;
  background: var(--accent);
  color: #000;
  font-weight: 600;
  border-radius: 0 0 var(--r-chip) 0;
}
.skip-link:focus { left: 0; }

/* ── Type ───────────────────────────────────────────────────────────────── */

/* Tracking is size specific. Large type reads too loose at its natural fit and
   is tightened; body sits at zero; small caps open up. */

.display {
  font-family: 'InterDisplay', 'Inter', sans-serif;
  font-weight: 600;
  font-size: clamp(2.9rem, 6.6vw, 5.4rem);
  line-height: 0.98;
  letter-spacing: -0.035em;
  margin: 0;
}

.h2 {
  font-family: 'InterDisplay', 'Inter', sans-serif;
  font-weight: 600;
  font-size: clamp(2rem, 4.2vw, 3.4rem);
  line-height: 1.04;
  letter-spacing: -0.03em;
  margin: 0;
  text-wrap: balance;
}

.h3 {
  font-weight: 600;
  font-size: clamp(1.15rem, 1.4vw, 1.375rem);
  line-height: 1.3;
  letter-spacing: -0.018em;
  margin: 0;
}

.lede {
  font-size: clamp(1.0625rem, 1.05rem + 0.35vw, 1.375rem);
  line-height: 1.55;
  color: var(--text-2);
  max-width: 46ch;
  margin: 0;
}

.body { color: var(--text-2); max-width: var(--measure); margin: 0; }
.body + .body { margin-top: 1rem; }

.caps {
  font-size: 0.6875rem;
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--text-caps);
  margin: 0;
}

.accent { color: var(--accent); }

/* ── Icons ──────────────────────────────────────────────────────────────── */

.ico {
  width: 1em;
  height: 1em;
  fill: currentColor;
  flex: none;
  display: block;
}

.ico-chip {
  width: 44px;
  height: 44px;
  border-radius: 13px;
  background: var(--chip);
  display: grid;
  place-items: center;
  flex: none;
  color: rgba(255, 255, 255, 0.78);
}
.ico-chip .ico { width: 22px; height: 22px; }
.ico-chip--accent { background: var(--accent-soft); color: var(--accent); }

/* ── Buttons ────────────────────────────────────────────────────────────── */

.btn {
  --btn-bg: var(--accent);
  --btn-fg: #041003;
  display: inline-flex;
  align-items: center;
  gap: 0.75rem;
  padding: 0.5rem 0.5rem 0.5rem 1.375rem;
  border: 0;
  border-radius: var(--r-pill);
  background: var(--btn-bg);
  color: var(--btn-fg);
  font-size: 0.9375rem;
  font-weight: 600;
  letter-spacing: -0.01em;
  white-space: nowrap;
  cursor: pointer;
  transition: transform 0.5s var(--ease), box-shadow 0.5s var(--ease),
    background-color 0.5s var(--ease);
  box-shadow: 0 1px 0 rgba(255, 255, 255, 0.28) inset,
    0 10px 30px -12px rgba(109, 222, 38, 0.55);
}

/* The trailing glyph lives in its own well rather than floating beside the
   label, so the button reads as one machined object. */
.btn__well {
  width: 30px;
  height: 30px;
  border-radius: var(--r-pill);
  background: rgba(0, 0, 0, 0.16);
  display: grid;
  place-items: center;
  transition: transform 0.5s var(--ease), background-color 0.5s var(--ease);
}
.btn__well .ico { width: 15px; height: 15px; }

.btn:hover { background: #7cea33; }
.btn:hover .btn__well {
  transform: translate3d(2px, -1px, 0) scale(1.06);
  background: rgba(0, 0, 0, 0.24);
}
.btn:active { transform: scale(0.978); }

.btn--ghost {
  --btn-bg: transparent;
  --btn-fg: var(--text);
  padding: 0.6875rem 1.375rem;
  border: 1px solid var(--hairline-strong);
  box-shadow: none;
}
.btn--ghost:hover {
  background: rgba(255, 255, 255, 0.05);
  border-color: rgba(255, 255, 255, 0.2);
}

.btn--sm { padding: 0.375rem 0.375rem 0.375rem 1rem; font-size: 0.875rem; }
.btn--sm .btn__well { width: 26px; height: 26px; }

/* ── Navigation ─────────────────────────────────────────────────────────── */

/* A floating translucent layer with the page running underneath it, rather
   than an opaque bar that permanently consumes a strip of the viewport. */

.nav-wrap {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 40;
  padding: 1rem var(--gutter);
  display: flex;
  justify-content: center;
  pointer-events: none;
}

.nav {
  pointer-events: auto;
  width: 100%;
  max-width: var(--shell);
  height: 64px;
  padding: 0 0.5rem 0 1.25rem;
  display: flex;
  align-items: center;
  gap: 1.5rem;
  border-radius: var(--r-pill);
  border: 1px solid transparent;
  background: transparent;
  transition: background-color 0.6s var(--ease), border-color 0.6s var(--ease),
    backdrop-filter 0.6s var(--ease), max-width 0.6s var(--ease);
}

.nav-wrap[data-stuck='true'] .nav {
  max-width: 980px;
  background: rgba(12, 14, 16, 0.72);
  border-color: var(--hairline);
  backdrop-filter: blur(22px) saturate(180%);
  -webkit-backdrop-filter: blur(22px) saturate(180%);
  box-shadow: 0 1px 0 rgba(255, 255, 255, 0.06) inset,
    0 20px 50px -30px rgba(0, 0, 0, 0.9);
}

.brand {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  font-weight: 600;
  font-size: 1.0625rem;
  letter-spacing: -0.025em;
  margin-right: auto;
}
/* The app's own mark, lifted from the 1024px app icon. It is carried as a mask
   rather than a picture so it inherits colour like any other glyph and stays
   correct if the surface behind it ever changes. */
.brand__mark {
  /* 20 against a 17px wordmark. At 25 it stood 1.5x the cap height beside it
     and read as a logo with a word after it rather than one lockup. */
  width: 20px;
  height: 20px;
  flex: none;

  /* A real image rather than a mask, and that is the whole change.
     `logo-mark.png` draws the glyph EDGE TO EDGE with a transparent counter,
     so masked at 20px it read as a chunky bordered box with the nav's blurred
     backdrop showing through the middle of the letter. `logo-tile.png` is the
     app icon's arrangement instead: the glyph at 80% on a filled #080808
     plate, so the counter is a known colour and the letter has air around it.

     80% and not the icon's own 27%. iOS can afford that much padding because
     its tile is 60px and up; at 20px the strokes thin out and the G stops
     being legible somewhere around 72%.

     The cost of dropping the mask is that the mark no longer follows
     `currentColor`. Both places it appears — the nav and the footer — draw it
     white, so nothing changes today, but a coloured brand lockup would now
     need a second file. */
  background: url('../img/logo-tile.png') center / contain no-repeat;
}

.nav__links {
  display: flex;
  align-items: center;
  gap: 1.75rem;
  font-size: 0.9375rem;
  color: var(--text-2);
}
.nav__links a {
  white-space: nowrap;
  transition: color 0.35s var(--ease);
}
.nav__links a:hover { color: var(--text); }

.nav__toggle { display: none; }

/* ── Hero ───────────────────────────────────────────────────────────────── */

.hero {
  position: relative;
  min-height: 100dvh;
  padding-top: clamp(7rem, 13vh, 9.5rem);
  padding-bottom: 0;
  display: flex;
  align-items: flex-start;
  overflow: hidden;
}

/* The pool of light the app casts under a healthy vehicle, scaled up to hold
   the whole hero. Same colour, same falloff. */
.hero::before {
  content: '';
  position: absolute;
  top: -14%;
  right: -6%;
  width: min(1100px, 92vw);
  aspect-ratio: 1.6;
  background: radial-gradient(
    ellipse at center,
    rgba(109, 222, 38, 0.14),
    rgba(109, 222, 38, 0.05) 42%,
    transparent 70%
  );
  pointer-events: none;
}

.hero__grid {
  position: relative;
  display: grid;
  grid-template-columns: minmax(0, 1.02fr) minmax(0, 0.98fr);
  align-items: center;
  gap: clamp(2rem, 4vw, 4rem);
}

.hero__copy { max-width: 40rem; }

.hero__cta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.875rem;
  margin-top: clamp(1.75rem, 3.5vh, 2.75rem);
}

.hero .lede { margin-top: 1.5rem; }

.hero__stage {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  /* The whole screen, not the top of it. This used to clip the phone at the
     stage's bottom edge and fade the cut, which read as the top of a device
     and showed maybe half the home screen. It is a showcase: the tiles, the
     SOS row and the dock are the point, so the stage sizes the phone rather
     than cropping it. */
  height: calc(100dvh - clamp(7rem, 13vh, 9.5rem));
  min-height: 520px;
}

/* ── The app itself ─────────────────────────────────────────────────────────
   prototype.png, cut to the phone it was drawn for. This replaced a replica of
   the home screen built in markup, which was more faithful in principle -- it
   stayed crisp at any size and every measurement in home_screen.dart transferred
   -- and less faithful in practice, because keeping it true meant re-measuring
   it against the app by hand every time a tile or a stat changed. It had drifted
   three ways by the time it was replaced. A render cannot drift.

   The rounded corners are in the file's alpha, not in `border-radius`: baked at
   the source's full 1170px and resampled down, the curve is antialiased once
   rather than re-cut at whatever width it lands on.                           */
/* The <picture> is a wrapper with no box of its own, and it has to stay that
   way: as a real flex item it becomes the parent that `.hero__phone`'s
   `max-height: 100%` resolves against, and its own height is auto, so the cap
   silently stops applying. The phone then sizes to the column and overflows
   the stage — 1201px of image in 748px of box, no error, just a phone running
   through the section below it. */
.hero__stage picture { display: contents; }

.hero__phone {
  /* Both maxima, and `auto` on both axes: the image shrinks to whichever
     constraint binds first and keeps its ratio doing it. Setting `height: 100%`
     instead would let a narrow column bind the width without touching the
     height, which is a stretched phone. */
  max-height: 100%;
  max-width: 100%;
  width: auto;
  height: auto;
  /* The pool of light the app's own card throws, carried out onto the page so
     the device sits in the section rather than on top of it. */
  filter: drop-shadow(0 30px 60px rgba(0, 0, 0, 0.65))
          drop-shadow(0 0 90px rgba(109, 222, 38, 0.09));
}

/* ── Section furniture ──────────────────────────────────────────────────── */

/* Several sections carry a glow that is deliberately wider than its column.
   `clip` contains it horizontally without creating a scroll container, so the
   glow still bleeds across the layout but never past the viewport. */
.section {
  padding-block: var(--section-y);
  position: relative;
  overflow-x: clip;
}
.section--tight { padding-block: clamp(3.5rem, 8vh, 6rem); }

.section__head { max-width: 34rem; }
.section__head .body { margin-top: 1.25rem; }

/* ── Getting started rail ───────────────────────────────────────────────── */

.rail {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: minmax(248px, 1fr);
  gap: 1rem;
  margin-top: 3rem;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  padding-bottom: 1rem;
  scrollbar-width: thin;
  scrollbar-color: var(--chip) transparent;
}
.rail::-webkit-scrollbar { height: 6px; }
.rail::-webkit-scrollbar-thumb { background: var(--chip); border-radius: 99px; }

.step {
  scroll-snap-align: start;
  padding: 1.5rem;
  border-radius: var(--r-card);
  border: 1px solid var(--hairline);
  background: linear-gradient(180deg, var(--card-top), var(--card-bottom));
  display: flex;
  flex-direction: column;
  gap: 0.875rem;
  transition: border-color 0.5s var(--ease), transform 0.5s var(--ease);
}
.step:hover { border-color: var(--hairline-strong); transform: translateY(-3px); }
.step__title { font-size: 1.0625rem; font-weight: 600; letter-spacing: -0.015em; }
.step__body { font-size: 0.9375rem; color: var(--text-2); margin: 0; line-height: 1.5; }

/* ── Health score studio ────────────────────────────────────────────────── */

.studio {
  margin-top: 3.5rem;
  display: grid;
  grid-template-columns: minmax(0, 0.85fr) minmax(0, 1.15fr);
  gap: 1rem;
}

/* Outer shell and inner core, so the panel reads as a plate seated in a tray
   rather than a flat rectangle. */
.plate {
  padding: 8px;
  border-radius: calc(var(--r-card) + 8px);
  background: rgba(255, 255, 255, 0.022);
  border: 1px solid var(--hairline);
}
.plate__core {
  border-radius: var(--r-card);
  border: 1px solid var(--hairline);
  background: linear-gradient(180deg, var(--card-top), var(--card-bottom));
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.05);
  height: 100%;
}

.dial {
  padding: clamp(1.5rem, 3vw, 2.5rem);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 1rem;
  position: relative;
  overflow: hidden;
  min-height: 100%;
}
.dial__glow {
  position: absolute;
  left: 50%;
  top: 46%;
  width: 420px;
  height: 420px;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  background: radial-gradient(
    circle,
    color-mix(in srgb, var(--bucket, var(--good)) 17%, transparent),
    transparent 62%
  );
  transition: background 0.7s var(--ease);
  pointer-events: none;
}
.dial__ring { position: relative; width: min(260px, 62vw); aspect-ratio: 1; }
.dial__ring svg { width: 100%; height: 100%; transform: rotate(-90deg); }
.dial__track { fill: none; stroke: #1e2126; stroke-width: 10; }
.dial__arc {
  fill: none;
  stroke: var(--bucket, var(--good));
  stroke-width: 10;
  stroke-linecap: round;
  transition: stroke-dashoffset 1s var(--ease), stroke 0.6s var(--ease);
}
.dial__center {
  position: absolute;
  inset: 0;
  display: grid;
  place-content: center;
  justify-items: center;
  text-align: center;
}
.dial__score {
  font-family: 'InterDisplay', sans-serif;
  font-size: clamp(3.75rem, 8vw, 5rem);
  font-weight: 600;
  line-height: 1;
  letter-spacing: -0.04em;
  font-variant-numeric: tabular-nums;
}
.dial__outof { font-size: 0.8125rem; color: var(--text-3); margin-top: 0.35rem; }
.dial__bucket { margin-top: 0.9rem; font-size: 0.75rem; padding: 6px 12px; }
.dial__note {
  font-size: 0.875rem;
  color: var(--text-3);
  text-align: center;
  max-width: 26ch;
  margin: 0;
}

.controls { padding: clamp(1.5rem, 2.6vw, 2rem); display: grid; gap: 1.5rem; }

.field { display: grid; gap: 0.5rem; }
.field__label {
  font-size: 0.8125rem;
  font-weight: 500;
  color: var(--text-2);
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 1rem;
}
.field__value { color: var(--text); font-weight: 600; font-variant-numeric: tabular-nums; }
/* One field carries a note, because one field needs defending: a visitor is
   entitled to ask whether four buttons labelled None through Kept up are
   scoring anything or just picking a number. */
.field__hint {
  margin-top: 0.625rem;
  font-size: 0.75rem;
  line-height: 1.5;
  color: var(--text-3);
}

select.input {
  appearance: none;
  width: 100%;
  padding: 0.75rem 2.5rem 0.75rem 0.875rem;
  border-radius: var(--r-chip);
  border: 1px solid var(--hairline-strong);
  background-color: #111111;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23888' stroke-width='2'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 0.75rem center;
  background-size: 18px;
  color: var(--text);
  font-size: 0.9375rem;
  cursor: pointer;
  transition: border-color 0.4s var(--ease);
}
select.input:hover { border-color: rgba(255, 255, 255, 0.24); }

input[type='range'] {
  appearance: none;
  width: 100%;
  height: 24px;
  background: transparent;
  cursor: grab;
}
input[type='range']:active { cursor: grabbing; }
input[type='range']::-webkit-slider-runnable-track {
  height: 6px;
  border-radius: var(--r-pill);
  background: linear-gradient(
    to right,
    var(--accent) var(--pct, 50%),
    #232323 var(--pct, 50%)
  );
}
input[type='range']::-moz-range-track {
  height: 6px;
  border-radius: var(--r-pill);
  background: #232323;
}
input[type='range']::-moz-range-progress {
  height: 6px;
  border-radius: var(--r-pill);
  background: var(--accent);
}
input[type='range']::-webkit-slider-thumb {
  appearance: none;
  width: 20px;
  height: 20px;
  margin-top: -7px;
  border-radius: 50%;
  background: #fff;
  border: 0;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.6);
  transition: transform 0.3s var(--ease);
}
input[type='range']:active::-webkit-slider-thumb { transform: scale(1.14); }
input[type='range']::-moz-range-thumb {
  width: 20px;
  height: 20px;
  border-radius: 50%;
  background: #fff;
  border: 0;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.6);
}

.segment {
  display: flex;
  gap: 4px;
  padding: 4px;
  border-radius: var(--r-pill);
  background: #111111;
  border: 1px solid var(--hairline-strong);
}
.segment button {
  flex: 1;
  padding: 0.5rem 0.25rem;
  border: 0;
  border-radius: var(--r-pill);
  background: transparent;
  color: var(--text-2);
  font-size: 0.8125rem;
  font-weight: 500;
  cursor: pointer;
  transition: background-color 0.4s var(--ease), color 0.4s var(--ease);
}
.segment button:hover { color: var(--text); }
.segment button[aria-pressed='true'] {
  background: var(--accent-soft);
  color: var(--accent);
  font-weight: 600;
}

.factors { display: grid; gap: 0.875rem; margin: 0; padding: 0; list-style: none; }
.factor__row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1rem;
  font-size: 0.875rem;
}
.factor__name { font-weight: 500; }
.factor__pts { color: var(--text-3); font-variant-numeric: tabular-nums; flex: none; }
.factor__meter {
  height: 4px;
  margin-top: 0.5rem;
  border-radius: var(--r-pill);
  background: #1c1f22;
  overflow: hidden;
}
.factor__meter i {
  display: block;
  height: 100%;
  border-radius: var(--r-pill);
  background: var(--accent);
  transform-origin: left center;
  transform: scaleX(var(--f, 0));
  transition: transform 0.7s var(--ease);
}
.factor__why { margin: 0.4rem 0 0; font-size: 0.8125rem; color: var(--text-3); }

.buckets {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem 1.5rem;
  margin-top: 1.75rem;
  padding-top: 1.5rem;
  border-top: 1px solid var(--hairline);
  font-size: 0.8125rem;
  color: var(--text-3);
}
.bucket-key { display: inline-flex; align-items: center; gap: 0.5rem; }
.bucket-key b { color: var(--text-2); font-weight: 500; }
.bucket-key i {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--k);
  flex: none;
}

/* ── Split (maintenance) ────────────────────────────────────────────────── */

.split {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  align-items: center;
  gap: clamp(2.5rem, 6vw, 6rem);
}

.device {
  position: relative;
  justify-self: center;
  max-width: 420px;
}
.device::before {
  content: '';
  position: absolute;
  left: 50%;
  top: 20%;
  width: 130%;
  aspect-ratio: 1;
  transform: translate(-50%, -20%);
  background: radial-gradient(
    circle,
    rgba(109, 222, 38, 0.12),
    transparent 62%
  );
  pointer-events: none;
}
.device img { position: relative; filter: drop-shadow(0 40px 60px rgba(0, 0, 0, 0.6)); }

.chips { display: flex; flex-wrap: wrap; gap: 0.5rem; margin-top: 2rem; }
.chip {
  display: inline-flex;
  align-items: baseline;
  gap: 0.5rem;
  padding: 0.5rem 0.875rem;
  border-radius: var(--r-pill);
  border: 1px solid var(--hairline);
  background: var(--panel);
  font-size: 0.8125rem;
  color: var(--text-2);
}
.chip b { color: var(--text); font-weight: 600; font-variant-numeric: tabular-nums; }

/* ── Feature cards ──────────────────────────────────────────────────────────
   The paid grid and the free grid are the same object with the accent swapped.
   Gold is the app's own paywall colour and is used nowhere else on this page,
   exactly as it is used nowhere else in the app — DESIGN.md, "One accent".    */

.features {
  margin-top: 3.5rem;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: clamp(1rem, 2vw, 1.5rem);
}
.features--free { grid-template-columns: repeat(3, 1fr); }

.feature {
  position: relative;
  padding: 1.75rem 1.5rem 1.5rem;
  border-radius: var(--r-inner);
  border: 1px solid var(--hairline);
  background: var(--panel);
}
/* The paid cards carry the gold hairline and a lit top edge, so the two grids
   are told apart before a word of either is read. */
.feature--pro {
  border-color: rgba(232, 185, 59, 0.28);
  background: linear-gradient(180deg, rgba(232, 185, 59, 0.045), transparent 42%),
              var(--panel);
}

.feature__tag {
  display: inline-block;
  margin-bottom: 0.875rem;
  padding: 0.25rem 0.5rem;
  border-radius: var(--r-pill);
  border: 1px solid rgba(232, 185, 59, 0.5);
  color: var(--gold);
  font-size: 0.625rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  line-height: 1;
}

.feature__name {
  margin: 0;
  font-size: 1.0625rem;
  font-weight: 600;
  letter-spacing: -0.015em;
}
.feature--pro .feature__name { color: var(--gold); }

/* The line from the app's own paywall, carried across verbatim. */
.feature__lede {
  margin: 0.5rem 0 0;
  font-size: 0.9375rem;
  color: var(--text);
  line-height: 1.5;
}
.feature__body {
  margin: 0.75rem 0 0;
  font-size: 0.875rem;
  color: var(--text-2);
  line-height: 1.6;
}
/* What the feature cannot do, on the same card that sells it. Every one of
   these is a limit the app states in its own words somewhere. */
/* A single closing statement under a grid. Was `.track-note`, which went with
   the pipeline section it was written for. */
.note-line {
  margin-top: clamp(2.5rem, 5vh, 3.5rem);
  padding: 1.25rem 1.5rem;
  border-radius: var(--r-inner);
  border: 1px solid var(--accent-line);
  background: rgba(109, 222, 38, 0.05);
  display: flex;
  align-items: center;
  gap: 1rem;
  color: var(--text-2);
  font-size: 0.9375rem;
  line-height: 1.55;
}
/* The text is one <span> on purpose. Bare text nodes beside an element in a
   flex container each become their own anonymous flex item, so the sentence
   split around its own link and the full stop drifted a gap-width away. */
.note-line .ico { width: 20px; height: 20px; color: var(--accent); flex: none; }
.note-line a { color: var(--accent); text-underline-offset: 3px; }

.feature__caveat {
  margin: 0.875rem 0 0;
  padding-top: 0.875rem;
  border-top: 1px solid var(--hairline);
  font-size: 0.8125rem;
  color: var(--text-3);
  line-height: 1.55;
}

/* ── Privacy cards ──────────────────────────────────────────────────────── */

/* ── Tech line ──────────────────────────────────────────────────────────── */

/* ── Waitlist ───────────────────────────────────────────────────────────── */

.finale { text-align: center; display: grid; justify-items: center; gap: 1.5rem; }
.finale .body { text-align: center; max-width: 44ch; }

/* Which phone this is for, at the point somebody decides to hand over an
   address. It used to be buried in a "built with" strip beside the framework
   name, where the only fact a visitor needed was the only one they would not
   have read. No margin: `.finale` is a grid and its gap already spaces this. */
.platforms {
  margin: 0;
  display: flex;
  align-items: center;
  gap: 0.625rem;
  font-size: 0.875rem;
  color: var(--text-3);
}
.platforms .ico { width: 17px; height: 17px; }

.waitlist { width: min(460px, 100%); display: grid; gap: 0.75rem; text-align: left; }
.waitlist__row { display: flex; gap: 0.5rem; }

input[type='email'],
input[type='password'] {
  flex: 1;
  min-width: 0;
  padding: 0.875rem 1rem;
  border-radius: var(--r-pill);
  border: 1px solid var(--hairline-strong);
  background: #111111;
  color: var(--text);
  font-size: 0.9375rem;
  transition: border-color 0.4s var(--ease);
}
input[type='email']::placeholder,
input[type='password']::placeholder { color: #7a7a7a; }
input[type='email']:hover,
input[type='password']:hover { border-color: rgba(255, 255, 255, 0.24); }
input[type='email']:focus-visible,
input[type='password']:focus-visible { border-color: var(--accent); outline-offset: 1px; }
input[type='email'][aria-invalid='true'],
input[type='password'][aria-invalid='true'] { border-color: var(--poor); }

.waitlist__msg {
  min-height: 1.25rem;
  font-size: 0.8125rem;
  color: var(--text-3);
  display: flex;
  align-items: center;
  gap: 0.4rem;
  padding-left: 1rem;
}
.waitlist__msg .ico { width: 15px; height: 15px; }
.waitlist__msg[data-state='error'] { color: var(--poor); }
.waitlist__msg[data-state='ok'] { color: var(--accent); }

.btn[data-busy='true'] { pointer-events: none; opacity: 0.75; }
.btn[data-busy='true'] .btn__well .ico { animation: spin 0.9s linear infinite; }

/* ── The waitlist, once it has worked ──────────────────────────────────────
   The arrow becomes a tick and the control stays where it was. `disabled`
   normally means "you cannot do this yet", which would be the wrong reading
   here, so the button keeps its full colour rather than fading: this is a
   finished thing, not an unavailable one. */
.waitlist[data-done='true'] .btn[disabled] {
  opacity: 1;
  cursor: default;
}
.waitlist[data-done='true'] .btn[disabled]:hover { background: var(--accent); }
.waitlist[data-done='true'] .btn__well {
  background: rgba(8, 8, 8, 0.18);
  transform: none;
}

/* The tick lands rather than appears. One short scale, on the curve the rest
   of the page uses for entrances, so the swap reads as an answer to the press
   instead of a repaint. Flattened by the reduced-motion block at the end of
   this file, like everything else here. */
.waitlist[data-done='true'] .btn__well .ico {
  animation: tick 0.42s var(--ease-out) both;
}
@keyframes tick {
  from { transform: scale(0.3); opacity: 0; }
  60%  { transform: scale(1.12); opacity: 1; }
  to   { transform: scale(1); opacity: 1; }
}

/* The address is kept on screen and kept readable, but no longer editable:
   what it says is now a claim about what was stored. */
.waitlist[data-done='true'] input[type='email'] {
  border-color: var(--accent-line);
  color: var(--text-2);
}
@keyframes spin { to { transform: rotate(360deg); } }

/* ── Footer ─────────────────────────────────────────────────────────────── */

/* The band is the element with the rule on it, and `.shell` moved inside, so
   the hairline runs the full width of the viewport instead of stopping at the
   content column. A footer rule that stops short reads as a broken card. */
.footer {
  border-top: 1px solid var(--hairline);
  margin-top: clamp(4rem, 9vh, 7rem);
  padding-block: clamp(2.5rem, 5vh, 4rem) 0;
  color: var(--text-3);
  font-size: 0.875rem;
}

.footer__inner {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: 2.5rem 4rem;
}

.footer__brand { display: grid; gap: 0.75rem; align-content: start; }
.footer__brand .brand { color: var(--text); margin-right: 0; }
.footer__line { margin: 0; max-width: 26ch; }

.footer__nav { display: flex; flex-wrap: wrap; gap: 2.5rem 4rem; }
.footer__col { display: grid; gap: 0.625rem; align-content: start; }

.footer__heading {
  margin: 0 0 0.25rem;
  font-size: 0.6875rem;
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--text-caps);
}

.footer__col a { transition: color 0.35s var(--ease); width: fit-content; }
.footer__col a:hover { color: var(--text); }
/* The page you are already on is stated, not offered. */
.footer__col a[aria-current='page'] { color: var(--text-2); }

/* One item now, not two, so `space-between` has nothing to space and the line
   simply sits at the start. Left as flex because the wrap and the gap still
   apply if a second item ever comes back. */
.footer__base {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: 0.5rem 2rem;
  margin-top: clamp(2.5rem, 5vh, 3.5rem);
  padding-block: 1.5rem 2.5rem;
  border-top: 1px solid var(--hairline);
  font-size: 0.8125rem;
  color: var(--text-caps);
}

/* ── Document pages (privacy.html) ───────────────────────────────────────────
   The policy screen in the app, widened. It carries the same furniture in the
   same roles: a grey small-caps label over each block, one green-tinted card
   for the opening statement, grey prose inside panel-coloured cards, and the
   contact row in the outlined green style the app uses for SOS and Sign Out.
   A reader who has seen the app's Privacy Policy screen should recognise this
   as the same document rather than a marketing page about it.               */

.nav--doc { gap: 1rem; }
.nav__back {
  margin-right: auto;
  /* One word, and it must stay one line. "Back to the site" wrapped to three
     of them inside a 40px pill on a narrow phone. */
  white-space: nowrap;
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.5rem 0.875rem;
  border-radius: var(--r-pill);
  font-size: 0.875rem;
  color: var(--text-2);
  transition: color 0.35s var(--ease), background-color 0.35s var(--ease);
}
.nav__back:hover { color: var(--text); background: rgba(255, 255, 255, 0.05); }
.nav__back .ico { width: 15px; height: 15px; }

.doc-head {
  position: relative;
  padding-top: clamp(7.5rem, 15vh, 10rem);
  padding-bottom: clamp(2.5rem, 5vh, 3.5rem);
  overflow-x: clip;
}
/* The same pool of light the hero uses, at a third of the strength. This is a
   document, so the glow acknowledges the brand and then gets out of the way. */
.doc-head::before {
  content: '';
  position: absolute;
  top: -30%;
  left: 50%;
  width: min(900px, 96vw);
  aspect-ratio: 1.7;
  transform: translateX(-50%);
  background: radial-gradient(
    ellipse at center,
    rgba(109, 222, 38, 0.075),
    transparent 68%
  );
  pointer-events: none;
}
.doc-head .shell { position: relative; }

.doc-head__title {
  font-family: 'InterDisplay', 'Inter', sans-serif;
  font-weight: 600;
  font-size: clamp(2.4rem, 5.2vw, 3.9rem);
  line-height: 1.02;
  letter-spacing: -0.032em;
  margin: 0.875rem 0 0;
}
.doc-head__sub {
  margin: 0.75rem 0 0;
  font-size: clamp(1rem, 0.98rem + 0.3vw, 1.25rem);
  color: var(--text-2);
}
.doc-head__stamp {
  margin: 1.5rem 0 0;
  font-size: 0.8125rem;
  color: var(--text-caps);
  font-variant-numeric: tabular-nums;
}

.doc-layout {
  display: grid;
  grid-template-columns: 232px minmax(0, 1fr);
  gap: clamp(2rem, 5vw, 5rem);
  align-items: start;
  padding-bottom: clamp(3rem, 7vh, 5rem);
}

/* ── Contents rail ─────────────────────────────────────────────────────── */

.toc { position: sticky; top: 6.5rem; }
.toc__wrap { border-radius: var(--r-inner); }
.toc__summary {
  font-size: 0.6875rem;
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--text-caps);
  cursor: pointer;
  padding-bottom: 0.875rem;
  list-style: none;
}
.toc__summary::-webkit-details-marker { display: none; }

.toc__list {
  margin: 0;
  padding: 0 0 0 0.875rem;
  list-style: none;
  border-left: 1px solid var(--hairline);
  display: grid;
  gap: 0.125rem;
  /* The rail runs to two dozen entries on the policy. On a short laptop it has
     to scroll on its own rather than push the sticky column past the bottom of
     the viewport. */
  max-height: calc(100dvh - 11rem);
  overflow-y: auto;
  scrollbar-width: thin;
  scrollbar-color: var(--chip) transparent;
}
.toc__list a {
  display: block;
  padding: 0.3125rem 0;
  font-size: 0.8125rem;
  line-height: 1.35;
  color: var(--text-3);
  transition: color 0.3s var(--ease);
}
.toc__list a:hover { color: var(--text-2); }
/* The marker sits on the rule itself, so the rail reads as a progress line
   through the document rather than as a list with one item highlighted. */
.toc__list a[data-current='true'] {
  color: var(--accent);
  position: relative;
}
.toc__list a[data-current='true']::before {
  content: '';
  position: absolute;
  left: calc(-0.875rem - 1px);
  top: 0.375rem;
  bottom: 0.375rem;
  width: 2px;
  border-radius: var(--r-pill);
  background: var(--accent);
}

/* ── The document ──────────────────────────────────────────────────────── */

.doc { max-width: 68ch; }

/* The one accent in the document, in the app's own green-tinted card. */
.doc__preamble {
  padding: clamp(1.25rem, 2.5vw, 1.75rem);
  border-radius: var(--r-inner);
  border: 1px solid var(--sos-border, #557641);
  background: #131d12;
}
.doc__preamble-title {
  display: flex;
  align-items: center;
  gap: 0.625rem;
  margin: 0 0 1rem;
  font-size: 1.0625rem;
  font-weight: 600;
  letter-spacing: -0.015em;
  color: var(--text);
}
.doc__preamble-title .ico { width: 20px; height: 20px; color: var(--accent); }
.doc__preamble p {
  margin: 0;
  color: rgba(255, 255, 255, 0.87);
  font-size: 0.9375rem;
  line-height: 1.62;
}
.doc__preamble p + p { margin-top: 0.875rem; }

.doc__section { margin-top: clamp(2.25rem, 4vh, 3rem); }
/* Clears the floating nav when a contents link jumps to a section. */
.doc__section { scroll-margin-top: 6.5rem; }

.doc__label {
  margin: 0 0 0.75rem;
  font-size: 0.6875rem;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--text-caps);
}

.doc__card {
  padding: clamp(1.25rem, 2.5vw, 1.75rem);
  border-radius: var(--r-inner);
  border: 1px solid var(--hairline);
  background: var(--panel);
}
/* Flow spacing is set on the children as a group rather than per element. A
   `.doc__card p` rule carrying `margin: 0` outranks `.doc__card > * + *` on
   specificity and silently flattens the whole document into one block, which
   is exactly what it did the first time. Both selectors here weigh the same,
   so the later one wins on order, which is what the cascade is for. */
/* The one block on this page that is not finished. Amber rather than the
   accent, because it is a warning to whoever publishes the page and not a
   feature of the policy: the controller's identity is a GDPR Art. 13(1)(a)
   requirement and it cannot be invented here. Delete the modifier once the
   fields below it are filled in. */
.doc__card--review {
  border-color: rgba(255, 176, 32, 0.35);
  background: linear-gradient(180deg, rgba(255, 176, 32, 0.05), transparent 60%),
              var(--panel);
}
.doc__fill {
  padding: 0.05em 0.35em;
  border-radius: 4px;
  background: rgba(255, 176, 32, 0.13);
  color: var(--fair);
  font-weight: 600;
  font-size: 0.8125rem;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  white-space: nowrap;
}

.doc__card > *,
.qa__a > * { margin: 0; }
.doc__card > * + *,
.qa__a > * + * { margin-top: 0.875rem; }

.doc__card p,
.qa__a p {
  color: var(--text-2);
  font-size: 0.9375rem;
  line-height: 1.65;
}
.doc__card b,
.qa__a b,
.doc__card em,
.qa__a em { color: var(--text); font-weight: 600; font-style: normal; }

.doc__card ul,
.qa__a ul {
  padding: 0;
  list-style: none;
  display: grid;
  gap: 0.625rem;
}
.doc__card li,
.qa__a li {
  position: relative;
  padding-left: 1.125rem;
  color: var(--text-2);
  font-size: 0.9375rem;
  line-height: 1.65;
}
/* Grey rather than green, and sized to sit on the first line's baseline. A
   green marker would turn every list in the document into an accent. */
.doc__card li::before,
.qa__a li::before {
  content: '';
  position: absolute;
  left: 0.1875rem;
  top: 0.6875em;
  width: 4px;
  height: 4px;
  border-radius: 50%;
  background: var(--text-3);
}

.doc__card a,
.qa__a a {
  color: var(--accent);
  text-decoration: underline;
  text-underline-offset: 0.2em;
  text-decoration-thickness: 1px;
  text-decoration-color: var(--accent-line);
  transition: text-decoration-color 0.3s var(--ease);
}
.doc__card a:hover,
.qa__a a:hover { text-decoration-color: var(--accent); }

/* ── Questions ─────────────────────────────────────────────────────────────
   faq.html reuses the document page whole: same header, same contents rail,
   same footer, same prose rules. The one thing it adds is the question.

   Each is a <details> and each starts CLOSED, which is the opposite of the
   rail above and right for the same reason. A contents list is scanned by
   reading all of it; an answer is read one at a time, and forty open answers
   is a forty-screen page nobody reaches the bottom of. Closed, the whole set
   of questions fits on two screens and the rail still works.

   No JavaScript. <details> already does open, close, keyboard focus, and
   find-in-page opening the section it matched, and all four are things a
   hand-built accordion gets wrong.                                          */
.qa-list { display: grid; gap: 0.625rem; }

.qa {
  border-radius: var(--r-inner);
  border: 1px solid var(--hairline);
  background: var(--panel);
  transition: border-color 0.4s var(--ease);
}
.qa[open] { border-color: var(--accent-line); }

.qa__q {
  display: flex;
  align-items: baseline;
  gap: 1rem;
  padding: clamp(0.9375rem, 2vw, 1.125rem) clamp(1rem, 2.5vw, 1.375rem);
  cursor: pointer;
  list-style: none;
  color: var(--text);
  font-size: 0.9375rem;
  font-weight: 600;
  line-height: 1.5;
}
.qa__q::-webkit-details-marker { display: none; }
.qa__q:hover { color: var(--text); }

/* The marker is two borders on a rotated square rather than a glyph, so it
   turns through the open state instead of being swapped for a different
   character. The reduced-motion block at the end of this file already flattens
   the turn, so there is nothing to gate here. */
.qa__q::after {
  content: '';
  flex: none;
  margin-left: auto;
  align-self: center;
  width: 7px;
  height: 7px;
  border-right: 1.5px solid var(--text-3);
  border-bottom: 1.5px solid var(--text-3);
  transform: translateY(-2px) rotate(45deg);
  transition: transform 0.4s var(--ease), border-color 0.4s var(--ease);
}
.qa[open] .qa__q::after {
  transform: translateY(1px) rotate(-135deg);
  border-color: var(--accent);
}

/* The answer carries no top padding: the question above it already has
   bottom padding, and doubling the two opens a gap wider than the gap
   between two questions. */
.qa__a { padding: 0 clamp(1rem, 2.5vw, 1.375rem) clamp(1.125rem, 2.5vw, 1.375rem); }

/* The row the app closes this screen on, at the same colours. */
.doc__contact {
  margin-top: clamp(2rem, 4vh, 2.75rem);
  display: flex;
  align-items: center;
  gap: 0.875rem;
  padding: 1rem 1.25rem;
  border-radius: var(--r-inner);
  border: 1px solid #557641;
  background: #131d12;
  color: var(--accent);
  font-size: 1rem;
  font-weight: 600;
  transition: background-color 0.4s var(--ease), border-color 0.4s var(--ease);
}
.doc__contact:hover { background: #172415; border-color: #6a8f52; }
.doc__contact-lead { width: 22px; height: 22px; }
.doc__contact-tail { width: 17px; height: 17px; margin-left: auto; }
.doc__contact span { min-width: 0; overflow-wrap: anywhere; }

/* ── The account action page ───────────────────────────────────────────────
   auth.html is one page wearing four faces: checking, set-a-password, done,
   and something-went-wrong. Every panel ships in the markup and `data-state`
   on the container picks one, so nothing is built out of strings at runtime
   and the CSP needs no exception.                                           */
.auth { max-width: 34rem; padding-bottom: clamp(3rem, 7vh, 5rem); }

.auth__panel { display: none; }
.auth[data-state='loading'] [data-for='loading'],
.auth[data-state='password'] [data-for='password'],
.auth[data-state='done'] [data-for='done'],
.auth[data-state='error'] [data-for='error'] { display: block; }

/* Amber, matching the review cards in the legal pages: this is a warning to
   the reader rather than a failure of the page. An expired link is the
   ordinary case here, not an error state to paint red. */
.doc__card--bad {
  border-color: rgba(255, 176, 32, 0.35);
  background: linear-gradient(180deg, rgba(255, 176, 32, 0.05), transparent 60%),
              var(--panel);
}
.doc__card--bad .doc__preamble-title .ico { color: var(--fair); }

.auth__field { display: grid; gap: 0.5rem; margin-top: 1.125rem; }
.auth__label {
  font-size: 0.8125rem;
  font-weight: 600;
  color: var(--text-2);
  padding-left: 1rem;
}

/* The three rules, ticked live as they are met. Grey until satisfied and
   green after, so the form answers "why is the button not working" before
   anybody asks it. The marker is drawn rather than a glyph, for the reason
   every marker on this site is: there is one icon sprite and it is inlined. */
.auth__rules {
  margin: 0.875rem 0 0;
  padding: 0 0 0 1rem;
  list-style: none;
  display: grid;
  gap: 0.375rem;
}
.auth__rules li {
  position: relative;
  padding-left: 1.375rem;
  font-size: 0.8125rem;
  line-height: 1.5;
  color: var(--text-3);
  transition: color 0.3s var(--ease);
}
.auth__rules li::before {
  content: '';
  position: absolute;
  left: 0.1875rem;
  top: 0.45em;
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: var(--text-3);
  transition: background-color 0.3s var(--ease), transform 0.3s var(--ease);
}
.auth__rules li[data-met='true'] { color: var(--accent); }
.auth__rules li[data-met='true']::before {
  background: var(--accent);
  transform: scale(1.25);
}

.auth #pwForm .btn { margin-top: 1.25rem; width: 100%; justify-content: center; }
.auth .doc__contact { justify-content: flex-start; }

/* The way to reach a person, on the two states where somebody might want one.
   Hidden on `done`, where offering help to somebody who just succeeded reads
   as though something went wrong after all, and on `loading`, which lasts a
   second and should not flash a support link. */
.auth[data-state='done'] .doc__contact,
.auth[data-state='loading'] .doc__contact { display: none; }

/* ── Entrances ──────────────────────────────────────────────────────────────
   Elements arrive rather than appearing. Driven by IntersectionObserver, never
   by a scroll listener.                                                      */

/* No `will-change` here on purpose. There are close to thirty of these on the
   page, and promoting every one of them to its own layer for the whole session
   costs far more than the entrance is worth. The transition promotes what it
   needs, when it needs it. */
.reveal {
  opacity: 0;
  transform: translate3d(0, 22px, 0);
  transition: opacity 0.8s var(--ease-out), transform 0.8s var(--ease-out);
  transition-delay: var(--d, 0ms);
}
.reveal.is-in { opacity: 1; transform: none; }

/* ── Responsive ─────────────────────────────────────────────────────────── */

@media (max-width: 1180px) {
  .features, .features--free { grid-template-columns: repeat(2, 1fr); }
}

/* The contents rail stops being a rail. doc.js closes the <details> at this
   same width, so on a phone it is a labelled disclosure above the document
   rather than a 19-item column between the reader and the first section. */
@media (max-width: 1000px) {
  .doc-layout { grid-template-columns: minmax(0, 1fr); gap: 2rem; }
  .toc { position: static; }
  .toc__wrap {
    padding: 1rem 1.25rem;
    border: 1px solid var(--hairline);
    background: var(--panel);
  }
  .toc__summary { padding-bottom: 0; }
  .toc__wrap[open] .toc__summary { padding-bottom: 1rem; }
  .toc__list {
    max-height: none;
    overflow: visible;
    /* Two columns, because 19 single-file rows is a scroll in its own right. */
    grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
  }
  .doc { max-width: none; }
}

/* The nav collapses before the rest of the page does, at its own threshold.
   It shared the 900px breakpoint while there were four links; the fifth broke
   that, wrapping two labels onto a second line inside a 64px pill at 901px.

   960 rather than 940 because the labels then grew: "Questions" became "FAQs"
   and "Privacy" became "Privacy Policy", a net +15px, and 940 left only 14px
   of slack at the boundary. Font metrics differ by more than that between
   platforms. Measured at 941px: 852px of content in an 866px bar.

   Between 901 and 960 this shows the Menu button on a page otherwise still in
   its desktop layout, which is right. The nav is the only thing out of room. */
@media (max-width: 960px) {
  .nav__links { display: none; }
  .nav__toggle {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.5rem 0.875rem;
    border-radius: var(--r-pill);
    border: 1px solid var(--hairline-strong);
    background: transparent;
    font-size: 0.875rem;
    cursor: pointer;
  }
  .nav-wrap[data-open='true'] .nav__links {
    display: flex;
    position: absolute;
    top: calc(100% + 0.5rem);
    left: var(--gutter);
    right: var(--gutter);
    flex-direction: column;
    align-items: flex-start;
    gap: 0.25rem;
    padding: 1rem 1.25rem;
    border-radius: var(--r-card);
    border: 1px solid var(--hairline-strong);
    /* Near solid. At 0.92 the headline behind it read straight through the
       panel and both sets of words became unreadable. */
    background: rgba(10, 12, 14, 0.98);
    backdrop-filter: blur(22px);
    -webkit-backdrop-filter: blur(22px);
    box-shadow: 0 24px 60px -20px rgba(0, 0, 0, 0.9);
  }
  .nav-wrap[data-open='true'] .nav__links a { padding: 0.625rem 0; width: 100%; }
}

@media (max-width: 900px) {
  .hero { min-height: auto; padding-bottom: 0; }
  .hero__grid { grid-template-columns: 1fr; }
  .hero__copy { max-width: none; }
  .hero__stage { height: 660px; min-height: 0; margin-top: 2rem; }
  .studio { grid-template-columns: 1fr; }
  .split { grid-template-columns: 1fr; }
  .split .device { order: -1; max-width: 340px; }
}

/* The phone was dropped entirely here once, which left the most visited
   viewport with a headline on an empty black field. It stays, whole, and the
   stage height is what makes it fit -- the same rule as the desktop, at
   smaller numbers. Below 360px it is about 230px across, which is small but
   still legibly a phone, and the alternative is showing a third of one.       */
@media (max-width: 768px) {
  /* Section rhythm was drawn for a desktop column and read as a hole on a
     phone: 14vh top and bottom put 163px of nothing between the hero's last
     button and the next heading, on a page you scroll rather than glance at.
     Roughly half of that, and the sections still separate. */
  :root { --section-y: clamp(3rem, 7vh, 5rem); }

  .hero { min-height: auto; padding-bottom: clamp(1rem, 3vh, 2rem); }
  .hero__copy { padding-top: clamp(0.5rem, 3vh, 1.5rem); }

  /* No app screens on a phone. Every one of them is a picture of a phone
     rendered inside a phone, which is always smaller than life size — the
     whole home screen put its vehicle card at 66% of it, and cropping to the
     card alone only bought back a single legible object at the cost of the
     rest of the screen. The page says what the app does; the studio below
     lets a visitor work the actual scoring. Neither needs a screenshot. */
  .hero__stage,
  .device--drops { display: none; }
}

@media (max-width: 560px) {
  /* The wordmark, the menu button and the call to action do not all fit on one
     line on a phone. The mark alone still identifies the brand, and keeping the
     call to action beats keeping the word. */
  .brand__word { display: none; }
  .nav { gap: 0.75rem; padding-right: 0.375rem; }
  .features, .features--free { grid-template-columns: 1fr; }
  .waitlist__row { flex-direction: column; }
  .waitlist__row .btn { justify-content: center; }
  .footer__links { margin-left: 0; }
}

/* ── Accessibility preferences ──────────────────────────────────────────── */

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
  }
  .reveal { opacity: 1; transform: none; filter: none; }
  .btn:hover .btn__well { transform: none; }
}

@media (prefers-reduced-transparency: reduce) {
  .nav-wrap[data-stuck='true'] .nav {
    background: #0d0f11;
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
  }
  .grain { display: none; }
}

@media (prefers-contrast: more) {
  :root {
    --hairline: rgba(255, 255, 255, 0.22);
    --hairline-strong: rgba(255, 255, 255, 0.4);
    --text-2: rgba(255, 255, 255, 0.86);
    --text-3: #b4b4b4;
    --text-caps: rgba(255, 255, 255, 0.7);
  }
  .grain { display: none; }
}
