/*
 * Yaseen waitlist landing page.
 *
 * Everything here reads from `tokens.css` — no literal colours, radii or type sizes
 * outside of the token file, so the site re-skins with the app. Layout is the one thing
 * tokens don't own, and it lives here.
 *
 * The page is fluid rather than stepped: type, gutters and section rhythm are `clamp()`
 * expressions and the grids are `auto-fit`/`minmax`, so a 1024px tablet and a 1280px
 * laptop are not two designs but two points on the same one. Breakpoints are kept for the
 * three places where the layout genuinely has to change shape, not to patch sizes.
 */

/* ---------------------------------------------------------------- reset */

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

* {
  margin: 0;
}

html {
  scroll-behavior: smooth;
  /* The sticky nav would otherwise cover the top of whatever an anchor lands on. */
  scroll-padding-top: 96px;
  -webkit-text-size-adjust: 100%;
}

body {
  background: var(--surface-page);
  color: var(--text-primary);
  font: var(--type-body);
  -webkit-font-smoothing: antialiased;
}

/*
 * A belt to the braces below: `clip` refuses the sideways scroll without creating a scroll
 * container, which `hidden` would — and a scroll container here would break the sticky nav.
 * The overflow it guards against is a bug either way; this only stops the page being
 * draggable while one goes unnoticed.
 */
html,
body {
  overflow-x: clip;
}

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

a {
  color: var(--text-link);
}

a:hover {
  color: var(--text-link-hover);
}

button,
input,
textarea,
select {
  font: inherit;
  color: inherit;
}

:focus-visible {
  outline: 2px solid var(--focus-ring);
  outline-offset: 2px;
  border-radius: var(--radius-xs);
}

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

  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}

.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
}

/* --------------------------------------------------------------- layout */

:root {
  --gutter: clamp(20px, 4vw, 32px);
  --section-y: clamp(56px, 8vw, 88px);
  --split-gap: clamp(36px, 5.5vw, 64px);
}

.wrap {
  max-width: var(--content-max);
  margin: 0 auto;
  padding-inline: var(--gutter);
}

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

.section--band {
  background: var(--surface-raised);
  border-block: var(--border-width) solid var(--border-hairline);
}

.split {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--split-gap);
  align-items: center;
}

.split--hero {
  grid-template-columns: 1.05fr 0.9fr;
}

.split--phone {
  grid-template-columns: 1fr minmax(260px, 360px);
}

/* ------------------------------------------------------------ typography */

.overline {
  display: block;
  font: var(--type-overline);
  letter-spacing: var(--tracking-caps);
  text-transform: uppercase;
  color: var(--text-muted);
}

.overline--inverse {
  color: rgba(253, 250, 246, 0.7);
}

.h1 {
  font: 500 clamp(34px, 6vw, 56px) / 1.06 var(--font-display);
  letter-spacing: var(--tracking-tight);
  max-width: 18ch;
  text-wrap: balance;
}

.h2 {
  font: 500 clamp(26px, 3.4vw, 32px) / 1.18 var(--font-display);
  letter-spacing: var(--tracking-tight);
  max-width: 24ch;
  text-wrap: balance;
}

.h2--display {
  font-size: clamp(30px, 4.4vw, 42px);
  line-height: 1.1;
  max-width: 18ch;
}

.h3 {
  font: var(--type-heading);
}

.lede {
  font: var(--type-body);
  font-size: clamp(16px, 1.1vw, 18px);
  color: var(--text-secondary);
  max-width: 50ch;
  text-wrap: pretty;
}

.lede--tight {
  margin-top: var(--space-4);
  max-width: 48ch;
}

.muted {
  color: var(--text-secondary);
}

.hint {
  font: var(--type-caption);
  color: var(--text-muted);
}

.error {
  font: var(--type-caption);
  color: var(--text-danger);
  margin-top: var(--space-1);
}

/* ----------------------------------------------------------- primitives */

.badge {
  display: inline-flex;
  align-items: center;
  gap: var(--space-15);
  padding: var(--space-1) var(--space-3);
  border-radius: var(--radius-pill);
  font: var(--type-label);
  background: var(--surface-brand-soft);
  color: var(--text-brand);
}

.badge--neutral {
  background: var(--surface-sunken);
  color: var(--text-secondary);
}

.card {
  background: var(--surface-card);
  border: var(--border-width) solid var(--border-hairline);
  border-radius: var(--radius-card);
  padding: var(--card-pad-lg);
}

.card--raised {
  box-shadow: var(--shadow-2);
}

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  min-height: var(--tap-min);
  padding: 0 var(--space-5);
  border: var(--border-width) solid transparent;
  border-radius: var(--radius-pill);
  background: var(--brand-primary);
  color: var(--on-brand);
  font: 500 var(--text-base) / 1 var(--font-ui);
  cursor: pointer;
  text-decoration: none;
  white-space: nowrap;
  transition: var(--transition-control);
}

.btn:hover {
  background: var(--brand-primary-hover);
  color: var(--on-brand);
}

/* The system's press: one step darker and a small physical give. */
.btn:active {
  background: var(--brand-primary-press);
  transform: scale(0.98);
}

.btn[disabled] {
  cursor: progress;
}

.btn--sm {
  min-height: 36px;
  font-size: var(--text-sm);
  padding: 0 var(--space-4);
}

.btn--lg {
  min-height: 52px;
  font-size: var(--text-md);
  padding: 0 var(--space-6);
}

.btn--accent {
  background: var(--brand-accent);
  color: var(--on-accent);
}

.btn--accent:hover {
  background: var(--brand-accent-hover);
  color: var(--on-accent);
}

/* The secondary of a pair: present, clearly not the primary. */
.btn--ghost {
  background: transparent;
  border-color: var(--border-subtle);
  color: var(--text-primary);
}

.btn--ghost:hover,
.btn--ghost:active {
  background: var(--surface-sunken);
  border-color: var(--border-strong);
  color: var(--text-primary);
}

.btn--block {
  width: 100%;
}

.btn .arrow {
  transition: transform var(--dur-fast) var(--ease-out);
  flex: 0 0 auto;
}

.btn:hover .arrow {
  transform: translateX(2px);
}

.btn:hover .arrow--down {
  transform: translateY(2px);
}

/*
 * Busy state. No spinner: the design system allows exactly one looping animation, "a
 * single 1.2s opacity breath on a syncing badge", and this is that one rather than a
 * second exception invented for a submit button.
 */
.btn[data-busy] .arrow {
  display: none;
}

.btn[data-busy] > span {
  animation: breath 1.2s var(--ease-in-out) infinite;
}

@keyframes breath {
  0%,
  100% {
    opacity: 1;
  }
  50% {
    opacity: 0.55;
  }
}

.icon-btn {
  width: var(--tap-min);
  height: var(--tap-min);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-1);
  border: var(--border-width) solid var(--border-subtle);
  border-radius: var(--radius-pill);
  background: var(--surface-card);
  color: var(--text-primary);
  cursor: pointer;
  transition: var(--transition-control);
}

.icon-btn:hover:not([disabled]) {
  background: var(--surface-sunken);
  border-color: var(--border-strong);
}

/* The system's press for an icon button is a deeper give than a text button's. */
.icon-btn:active:not([disabled]) {
  transform: scale(0.94);
}

.icon-btn[disabled] {
  color: var(--clay-400);
  border-color: var(--border-hairline);
  background: transparent;
  cursor: not-allowed;
}

.icon-btn--ghost {
  background: transparent;
  border-color: transparent;
  color: var(--text-secondary);
  width: auto;
  min-width: var(--tap-min);
  padding-inline: var(--space-3);
}

.icon-btn--ghost:hover:not([disabled]) {
  background: var(--surface-sunken);
  border-color: transparent;
  color: var(--text-primary);
}

.icon-btn-label {
  font: 500 var(--text-sm) / 1 var(--font-mono);
  letter-spacing: var(--tracking-wide);
}

.field {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

.field > label {
  font: var(--type-label);
  color: var(--text-secondary);
}

.fieldset {
  border: none;
  padding: 0;
  min-width: 0;
}

.fieldset > legend {
  font: var(--type-label);
  color: var(--text-secondary);
  margin-bottom: var(--space-2);
  padding: 0;
}

.input {
  width: 100%;
  min-height: var(--tap-min);
  padding: var(--space-3) var(--space-4);
  background: var(--surface-sunken);
  border: var(--border-width) solid transparent;
  border-radius: var(--radius-control);
  transition: var(--transition-control);
}

.input::placeholder {
  color: var(--text-muted);
}

.input:focus {
  outline: none;
  background: var(--surface-card);
  border-color: var(--border-brand);
  box-shadow: var(--shadow-focus);
}

.input[aria-invalid='true'] {
  border-color: var(--danger-500);
}

textarea.input {
  resize: vertical;
  min-height: 76px;
}

/* Stat pairs: a mono figure over a caption. */
.stats {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-6) var(--space-8);
}

.stats--wl {
  margin-top: var(--space-8);
}

.stat {
  display: flex;
  flex-direction: column;
}

.stat b {
  font: 500 var(--text-lg) / 1.2 var(--font-mono);
  color: var(--text-brand);
}

.stat span {
  font: var(--type-caption);
  color: var(--text-muted);
}

.check-list {
  list-style: none;
  padding: 0;
  margin-top: var(--space-6);
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}

.check-list li {
  display: flex;
  gap: var(--space-3);
  align-items: flex-start;
}

.check-list svg {
  color: var(--text-brand);
  flex: 0 0 auto;
  margin-top: 2px;
}

.check-list p {
  color: var(--text-secondary);
}

.check-list b {
  font-weight: var(--weight-medium);
  color: var(--text-primary);
}

/* ----------------------------------------------------------- device frame */

/*
 * The phone body is part of the image, composited onto the capture at full resolution
 * rather than drawn in CSS around a bare screen — so the enclosure survives being saved,
 * shared or dropped into a deck, and the page does not have to keep a fake bezel in step
 * with a real screenshot.
 *
 * The files are RGBA with a transparent surround, which is why the shadow is a
 * `drop-shadow` filter: a box-shadow would trace the image's rectangle instead of the
 * phone's rounded silhouette.
 */
.device {
  width: min(100%, var(--dw, clamp(248px, 24vw, 320px)));
  margin-inline: auto;
}

/*
 * One drop-shadow, not two. Each is a separate blur pass over the whole image, and the
 * second only ever added a contact shadow nobody was going to notice at this size — on a
 * phone it was paying for eight extra blurs to say almost nothing.
 */
.device img {
  display: block;
  width: 100%;
  height: auto;
  filter: drop-shadow(0 14px 22px rgba(28, 39, 35, 0.2));
}

/*
 * Night mode adds a hairline rim. The enclosure is ink-900 and the band behind it is
 * #222B28 — ten values apart — so without an edge the phone dissolves into its own
 * background and the whole slide reads as a smudge. A zero-blur drop-shadow traces the
 * image's alpha silhouette, which is the only way to outline a transparent PNG's actual
 * shape rather than its bounding box. At half a pixel it costs almost nothing next to the
 * 26px pass above it.
 */
[data-theme='dark'] .device img {
  filter: drop-shadow(0 14px 26px rgba(0, 0, 0, 0.5))
    drop-shadow(0 0 0.5px rgba(231, 227, 218, 0.45));
}

/* In the carousel the slide sets the width, so the device takes all of it. */
.device--sm {
  --dw: 100%;
}

.device-note {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  margin-top: var(--space-5);
  font: var(--type-caption);
  color: var(--text-muted);
  text-align: center;
}

.device-note svg {
  flex: 0 0 auto;
}

/* --------------------------------------------------------------- the nav */

/*
 * Glass, in both themes.
 *
 * The bar is the page's own surface at partial opacity over a blur, so what scrolls under
 * it shows through rather than disappearing behind a slab. Because the colour comes from
 * `--surface-page`, night mode gets dark glass for free: the same rule resolves to sand at
 * 74% in day and #121816 at 74% at night, which is the whole point of putting chrome on
 * tokens instead of hex.
 *
 * A solid background is declared first as the fallback for anything without
 * `backdrop-filter` — translucency with no blur behind it is unreadable, not merely plain.
 */
.nav {
  position: sticky;
  top: 0;
  z-index: 40;
  background: var(--surface-page);
  border-bottom: var(--border-width) solid transparent;
  transition: background-color var(--dur-base) var(--ease-out),
    border-color var(--dur-base) var(--ease-out), box-shadow var(--dur-base) var(--ease-out);
}

@supports (backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px)) {
  .nav {
    background: color-mix(in srgb, var(--surface-page) 74%, transparent);
    /*
     * Halved from 18px, and the saturate pass dropped. A backdrop filter re-runs on every
     * scroll frame, and on a phone two chained passes over a full-width bar is the single
     * most expensive thing on this page. At 10px the material still reads as glass.
     */
    -webkit-backdrop-filter: blur(10px);
    backdrop-filter: blur(10px);
  }

  .nav[data-scrolled='true'] {
    background: color-mix(in srgb, var(--surface-page) 88%, transparent);
  }
}

.nav[data-scrolled='true'] {
  border-bottom-color: var(--border-hairline);
  box-shadow: var(--shadow-1);
}

/*
 * Night mode adds the one thing the day bar does not need: a lit top edge. Dark glass on a
 * dark page has no value difference to separate it, and the hairline that reads as a
 * divider in daylight disappears at night.
 */
[data-theme='dark'] .nav {
  box-shadow: inset 0 1px 0 rgba(231, 227, 218, 0.07);
}

[data-theme='dark'] .nav[data-scrolled='true'] {
  box-shadow: inset 0 1px 0 rgba(231, 227, 218, 0.1), var(--shadow-2);
}

.nav .wrap {
  min-height: 72px;
  display: flex;
  align-items: center;
  gap: clamp(var(--space-4), 2.5vw, var(--space-8));
}

.wordmark {
  font: 500 var(--text-xl) / 1 var(--font-display);
  letter-spacing: var(--tracking-tight);
  color: var(--text-brand);
  text-decoration: none;
  flex: 0 0 auto;
}

.nav nav {
  display: flex;
  gap: clamp(var(--space-4), 2vw, var(--space-6));
  flex: 1;
  min-width: 0;
}

.nav nav a {
  font: var(--type-body);
  color: var(--text-secondary);
  text-decoration: none;
  padding: var(--space-1) 0;
  white-space: nowrap;
  border-bottom: 2px solid transparent;
  transition: var(--transition-control);
}

.nav nav a:hover {
  color: var(--text-primary);
}

/* Where you are, not just where you could go. */
.nav nav a[aria-current='true'] {
  color: var(--text-brand);
  border-bottom-color: var(--border-brand);
}

.nav .privacy {
  display: inline-flex;
  align-items: center;
  gap: var(--space-15);
  font: var(--type-caption);
  color: var(--text-muted);
  white-space: nowrap;
}

.nav-controls {
  display: flex;
  align-items: center;
  gap: var(--space-1);
  flex: 0 0 auto;
}

/*
 * The nav's call to action has two labels. French runs about 40% longer than English —
 * "Rejoindre la liste d'attente" is 222px against "Join the waitlist" at 159px — and with
 * `white-space: nowrap` the long one pushed the whole row past a phone's 393px, making the
 * page scroll sideways. The short label is swapped in below the breakpoint where that
 * starts to bite.
 */
.cta-short {
  display: none;
}

/* -------------------------------------------------------------- the hero */

.hero {
  padding-block: clamp(48px, 7vw, 80px) clamp(48px, 6vw, 72px);
}

.split--hero .h1 {
  margin-top: var(--space-5);
}

.split--hero .lede {
  margin-top: var(--space-4);
}

.cta-row {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
  margin-top: var(--space-6);
}

.cta-hint {
  margin-top: var(--space-3);
}

.hero-proof {
  margin-top: var(--space-8);
  padding-top: var(--space-6);
  border-top: var(--border-width) solid var(--border-hairline);
}

/* --------------------------------------------------------------- reveal */

/*
 * Opacity and eight pixels of travel, once, on the way in. The design system allows a
 * small translate on a decelerating ease and forbids anything springy, so that is the
 * whole effect. The hiding rule is scoped to `:root[data-reveal="on"]`, which the script
 * sets only when motion is welcome — so a reduced-motion visitor, or one with no script,
 * never has content hidden from them in the first place.
 */
:root[data-reveal='on'] [data-reveal] {
  opacity: 0;
  transform: translateY(8px);
  /* 480ms on the entrance curve: the 320ms control speed made blocks appear to snap in. */
  transition: opacity 480ms var(--ease-entrance), transform 480ms var(--ease-entrance);
}

:root[data-reveal='on'] [data-reveal='in'] {
  opacity: 1;
  transform: none;
}

/* --------------------------------------------------------- the 3am loop */

.loop-points {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(220px, 100%), 1fr));
  gap: var(--space-5);
  margin-top: var(--space-8);
}

.loop-point {
  display: flex;
  gap: var(--space-3);
  align-items: flex-start;
}

.loop-point .glyph {
  width: 34px;
  height: 34px;
  border-radius: var(--radius-sm);
  background: var(--surface-brand-soft);
  color: var(--text-brand);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
}

.loop-point b {
  font: 500 var(--text-base) / 1.3 var(--font-ui);
}

.loop-point p {
  font: var(--type-caption);
  color: var(--text-secondary);
  margin-top: 3px;
}

/* ------------------------------------------------------- what you track */

.track-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(260px, 100%), 1fr));
  gap: var(--space-4);
  margin-top: var(--space-8);
}

/*
 * The accent rule carries the category hue, so a card and its log row agree at a glance.
 * It sits on the top edge at 3px: a coloured left border is explicitly not a Yaseen
 * pattern, and the system calls that out by name.
 */
.track-card {
  position: relative;
  overflow: hidden;
}

.track-card::before {
  content: '';
  position: absolute;
  inset: 0 0 auto 0;
  height: 3px;
  background: var(--accent, var(--brand-primary));
}

.track-card .glyph {
  width: 40px;
  height: 40px;
  border-radius: var(--radius-sm);
  background: var(--accent-soft, var(--surface-brand-soft));
  color: var(--accent, var(--brand-primary));
  display: inline-flex;
  align-items: center;
  justify-content: center;
}

.track-card h3 {
  margin-top: var(--space-4);
}

.track-card p {
  font: var(--type-body);
  color: var(--text-secondary);
  margin-top: var(--space-2);
}

/* ------------------------------------------------------------ carousel */

/*
 * The app gallery. A horizontal scroller rather than a grid, because the screens are
 * portrait phones: a grid of five wastes the whole right half of the page on whitespace.
 *
 * Scrolling is the browser's — `scroll-snap-type` on the track, `scroll-snap-align` on the
 * slides. Touch, trackpad, shift-wheel, keyboard and the arrow buttons all drive the same
 * native scroll, so there is no transform to keep in sync and nothing to break if the
 * script fails to load. The script only marks the centred slide and lights the arrows and
 * the rail.
 */

.carousel-head {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: var(--space-6);
}

.carousel-controls {
  display: flex;
  gap: var(--space-2);
  flex: 0 0 auto;
}

.carousel {
  margin-top: var(--space-8);
}

/*
 * Centre stage. The track's inline padding is half the scrollport less half a slide, which
 * is what lets the first and last slides reach the middle — without it the ends could
 * never become the focus. Snapping is centre-aligned to match.
 */
.carousel-track {
  --slide-w: clamp(200px, 46vw, 260px);
  --edge: max(var(--gutter), calc(50% - var(--slide-w) / 2));
  display: flex;
  gap: var(--space-6);
  margin: 0;
  padding: var(--space-4) var(--edge) var(--space-5);
  list-style: none;
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scroll-snap-type: x mandatory;
  scroll-behavior: smooth;
  /* The rail below is the scroll affordance; a second one under the phones is noise. */
  scrollbar-width: none;
}

.carousel-track::-webkit-scrollbar {
  display: none;
}

.carousel-track:focus-visible {
  outline: 2px solid var(--focus-ring);
  outline-offset: 4px;
  border-radius: var(--radius-card);
}

.carousel-slide {
  flex: 0 0 auto;
  width: var(--slide-w);
  scroll-snap-align: center;
}

.carousel-slide figure {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}

.carousel-slide figcaption {
  font: var(--type-caption);
  color: var(--text-secondary);
}

.carousel-slide figcaption b {
  display: block;
  font: 500 var(--text-base) / 1.3 var(--font-ui);
  color: var(--text-primary);
  margin-bottom: 2px;
}

.carousel-slide .device {
  /*
   * Slower than the 220ms control speed, and on the entrance curve. A slide coming into
   * focus is a change of subject, not a button responding — at the control speed it read
   * as a jump cut.
   *
   * No `will-change` here. It is the obvious next optimisation — promote the frame so the
   * scale composites without re-running the image's drop-shadow — and it was tried and
   * pulled back out. `will-change` paired with a filter has a history of dropping content
   * in WebKit, and WebKit is what most of this page's readers will be using; the filter
   * already promotes the element, so the remaining gain did not look worth a bug that can
   * only be reproduced on the device. Reach for it only with an iPhone in hand to check.
   */
  transition: transform 420ms var(--ease-entrance), opacity 420ms var(--ease-entrance);
}

/*
 * One screen at a time is the subject; the rest are context. Scaling is a transform, so a
 * slide occupies the same box whether or not it is the focus and the captions never shift.
 * Only the device dims — the captions stay at full contrast, because fading text to make a
 * visual point is how a carousel fails a contrast check.
 *
 * Gated on `data-enhanced`, which the script sets once it is running: without it nothing
 * marks a slide active, and the row would otherwise sit permanently dimmed.
 */
[data-enhanced] .carousel-slide .device {
  opacity: 0.62;
  transform: scale(0.92);
}

[data-enhanced] .carousel-slide.is-active .device {
  opacity: 1;
  transform: none;
}

@media (prefers-reduced-motion: reduce) {
  [data-enhanced] .carousel-slide .device {
    transform: none;
  }

  .carousel-track {
    scroll-behavior: auto;
  }
}

/* Progress rail: how far along the row you are, and how much of it fits at once. */
.carousel-rail {
  position: relative;
  height: 3px;
  margin-top: var(--space-5);
  border-radius: var(--radius-pill);
  background: var(--border-subtle);
  overflow: hidden;
}

.carousel-thumb {
  position: absolute;
  inset-block: 0;
  left: 0;
  width: var(--thumb-width, 25%);
  transform: translateX(var(--thumb-offset, 0));
  border-radius: var(--radius-pill);
  background: var(--brand-primary);
  transition: transform var(--dur-base) var(--ease-out), width var(--dur-base) var(--ease-out);
}

/* ----------------------------------------------------------- quote band */

/*
 * `--text-inverse` flips with the theme, and on the brand surface that is right in day
 * mode and wrong at night: night's inverse is the near-black ground, which would land dark
 * text on a dark green band. The band names its own foreground so it holds in both.
 */
.quote-band {
  --on-band: var(--text-inverse);
  background: var(--surface-brand);
  color: var(--on-band);
}

[data-theme='dark'] .quote-band {
  --on-band: var(--text-primary);
}

.quote-band .wrap {
  padding-block: clamp(64px, 9vw, 96px);
  display: grid;
  grid-template-columns: 1.25fr 0.75fr;
  gap: var(--split-gap);
  align-items: center;
}

.quote-band blockquote {
  font: 300 clamp(28px, 4.4vw, 42px) / 1.2 var(--font-editorial);
  letter-spacing: var(--tracking-snug);
  max-width: 26ch;
  text-wrap: balance;
}

.quote-band .overline--inverse {
  margin-bottom: var(--space-4);
}

.quote-band p {
  font: var(--type-body);
  font-size: clamp(16px, 1.1vw, 18px);
  color: rgba(253, 250, 246, 0.86);
  margin-bottom: var(--space-4);
}

.quote-band .quote-body {
  margin-top: var(--space-6);
  max-width: 44ch;
}

/* ----------------------------------------------------------------- FAQ */

.faq {
  margin-top: var(--space-6);
  max-width: 780px;
}

.faq details {
  border-top: var(--border-width) solid var(--border-subtle);
}

.faq summary {
  min-height: 64px;
  display: flex;
  align-items: center;
  gap: var(--space-4);
  padding-block: var(--space-5);
  cursor: pointer;
  font: 500 var(--text-lg) / 1.35 var(--font-ui);
  list-style: none;
}

.faq summary::-webkit-details-marker {
  display: none;
}

.faq summary .q {
  flex: 1;
}

.faq summary .sign {
  color: var(--text-muted);
  flex: 0 0 auto;
  transition: transform var(--dur-base) var(--ease-out);
}

.faq details[open] summary .sign {
  transform: rotate(45deg);
}

.faq details p {
  color: var(--text-secondary);
  max-width: 68ch;
  padding-bottom: var(--space-5);
}

/* -------------------------------------------------------- waitlist panel */

.panel {
  max-width: 560px;
  width: 100%;
}

.panel form {
  display: flex;
  flex-direction: column;
  gap: var(--space-5);
}

.roles {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

.roles label,
.consent {
  display: flex;
  gap: var(--space-3);
  align-items: flex-start;
  padding: var(--space-3);
  border: var(--border-width) solid var(--border-subtle);
  border-radius: var(--radius-control);
  cursor: pointer;
  transition: var(--transition-control);
}

.roles label:hover,
.consent:hover {
  border-color: var(--border-brand);
}

.roles input,
.consent input {
  margin-top: 3px;
  accent-color: var(--brand-primary);
  width: 18px;
  height: 18px;
  flex: 0 0 auto;
}

.roles label:has(input:checked) {
  border-color: var(--border-brand);
  background: var(--surface-brand-soft);
}

.roles b,
.consent b {
  display: block;
  font: 500 var(--text-base) / 1.3 var(--font-ui);
}

.roles span span,
.consent span span {
  font: var(--type-caption);
  color: var(--text-muted);
}

.assurances {
  display: flex;
  gap: var(--space-2);
  flex-wrap: wrap;
}

.success-card .icon-circle {
  width: 44px;
  height: 44px;
  border-radius: 50%;
  background: var(--surface-brand-soft);
  color: var(--text-brand);
  display: inline-flex;
  align-items: center;
  justify-content: center;
}

.success-card h3 {
  font: 500 var(--text-2xl) / 1.2 var(--font-display);
  letter-spacing: var(--tracking-snug);
  margin-top: var(--space-4);
}

.success-card p {
  margin-top: var(--space-2);
}

.success-card .stats {
  margin-top: var(--space-5);
  padding-top: var(--space-5);
  border-top: var(--border-width) solid var(--border-hairline);
  gap: var(--space-4) var(--space-6);
}

/* -------------------------------------------------------------- footer */

.footer {
  background: var(--surface-page);
  border-top: var(--border-width) solid var(--border-hairline);
}

.footer .cols {
  padding-block: var(--space-16) var(--space-10);
  display: grid;
  grid-template-columns: 1.5fr repeat(3, 1fr);
  gap: var(--space-10);
}

.footer .cols p {
  font: var(--type-caption);
  color: var(--text-muted);
  margin-top: var(--space-3);
  max-width: 34ch;
}

.footer .residency {
  display: flex;
  gap: var(--space-2);
  margin-top: var(--space-4);
  flex-wrap: wrap;
}

.footer .col-links {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  margin-top: var(--space-3);
}

.footer .col-links a {
  font: var(--type-body);
  text-decoration: none;
}

.footer .col-links a:hover {
  text-decoration: underline;
}

.footer .baseline {
  padding-bottom: var(--space-10);
  display: flex;
  justify-content: space-between;
  gap: var(--space-4);
  flex-wrap: wrap;
  font: var(--type-caption);
  color: var(--text-muted);
}

/* --------------------------------------------------------- responsive */

/*
 * Three shape changes, not a ladder of size patches — sizes are fluid above.
 *
 *   1080  the nav runs out of room for its section links
 *    900  two columns beside a phone stop being two columns
 *    600  rows of stats and footer columns become stacks
 */

@media (max-width: 1080px) {
  .nav nav,
  .nav .privacy {
    display: none;
  }

  .nav .wrap {
    justify-content: space-between;
  }

  .nav-controls {
    margin-left: auto;
  }
}

@media (max-width: 900px) {
  .split,
  .split--hero,
  .split--phone,
  .quote-band .wrap,
  .footer .cols {
    grid-template-columns: 1fr;
  }

  /* The phone follows the copy it illustrates rather than sitting beside nothing. */
  .split > :last-child,
  .split--hero > :last-child,
  .split--phone > :last-child {
    max-width: 420px;
    margin-inline: auto;
    width: 100%;
  }

  .device {
    --dw: min(300px, 78vw);
  }

  .footer .cols {
    grid-template-columns: repeat(auto-fit, minmax(min(200px, 100%), 1fr));
    gap: var(--space-8);
  }
}

@media (max-width: 600px) {
  .cta-full {
    display: none;
  }

  .cta-short {
    display: inline;
  }

  .carousel-head {
    flex-direction: column;
    align-items: flex-start;
    gap: var(--space-5);
  }

  .cta-row .btn {
    width: 100%;
  }

  .stats {
    gap: var(--space-5);
  }

  .footer .baseline {
    flex-direction: column;
    gap: var(--space-2);
  }
}

/*
 * Coarse pointers get the full 44px target everywhere, including the small nav button a
 * mouse can hit at 36.
 */
@media (pointer: coarse) {
  .btn--sm {
    min-height: var(--tap-min);
  }
}
