/* MindQot pre-launch landing page.
   Tokens transcribed from the app's DESIGN.md (the same system the Flutter app
   ships): three navy surface layers, 1px borders, no shadows, violet used like
   a stamp, teal reserved for completion. One self-hosted font, no third-party
   requests. `styles.css` still serves the legal pages untouched. */

@font-face {
  font-family: "Sora";
  src: url("/fonts/sora-variable.woff2") format("woff2");
  font-weight: 400 700;
  font-style: normal;
  font-display: swap;
}

:root {
  /* Surfaces — always step lighter going up, never reversed. */
  --bg: #07080d;
  --surface: #0f1219;
  --surface-2: #161b27;
  --border: #293044;
  --border-medium: #374158;
  --hairline: rgba(255, 255, 255, 0.05);

  /* Ink */
  --text: #f3f0ea;
  --text-muted: #a8b0c3;
  --text-dim: #737d94;

  /* Accents — violet = action, teal = completed. Never interchangeable. */
  --violet: #8b7cff;
  --violet-pressed: #7669e0;
  --violet-soft: rgba(139, 124, 255, 0.14);
  --teal: #63d6c5;
  --error: #e06c75;

  --display: "Sora", ui-sans-serif, system-ui, "Segoe UI", Roboto, sans-serif;
  --body: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto,
    Helvetica, Arial, sans-serif;

  --shell: 68rem;
  --gutter: 1.25rem; /* the app's 20px screen padding */
  --radius: 12px;
  --radius-lg: 16px;
}

*,
*::before,
*::after {
  box-sizing: border-box;
}

/* The `hidden` attribute is only a UA `display: none`, so any rule that sets
   its own `display` silently defeats it — which is how a price row with no
   price in it briefly rendered a lone "one time". Anything hidden here is
   hidden because showing it would be wrong, so this outranks layout. */
[hidden] {
  display: none !important;
}

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: var(--body);
  font-size: 1.0625rem;
  line-height: 1.647; /* 17/28, the app's Body Large */
  letter-spacing: 0;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-rendering: optimizeLegibility;
}

h1,
h2,
h3 {
  font-family: var(--display);
  font-weight: 600;
  margin: 0;
  text-wrap: balance;
}

p {
  text-wrap: pretty;
}

p {
  margin: 0;
}

a {
  color: var(--violet);
  text-decoration: none;
  text-underline-offset: 0.2em;
}
a:hover {
  text-decoration: underline;
}

ul,
ol {
  margin: 0;
  padding: 0;
  list-style: none;
}

:focus-visible {
  outline: 2px solid var(--violet);
  outline-offset: 3px;
  border-radius: 4px;
}

.shell {
  width: 100%;
  max-width: var(--shell);
  margin: 0 auto;
  padding: 0 var(--gutter);
}

.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

.skip-link {
  position: absolute;
  left: -999px;
  top: 0;
  z-index: 10;
  background: var(--surface-2);
  color: var(--text);
  padding: 0.75rem 1rem;
  border: 1px solid var(--border-medium);
  border-radius: var(--radius);
}
.skip-link:focus {
  left: 0.75rem;
  top: 0.75rem;
}

.brand {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  font-family: var(--display);
  font-weight: 700;
  font-size: 1.0625rem;
  letter-spacing: 0.01em;
  color: var(--text);
}
.brand:hover {
  text-decoration: none;
}
/* The supplied artwork is already a dark rounded square, so it needs no frame
   of its own here — width/height keep the box reserved before it decodes. */
.brand-mark {
  flex: none;
  width: 1.375rem;
  height: 1.375rem;
  margin-right: 0.5rem;
}
.brand-dot {
  color: var(--violet);
}
/* ── Waitlist form ────────────────────────────────────────────────────── */

.waitlist {
  margin-top: 1rem;
  max-width: 34rem;
}

.hp {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

.field-label {
  display: block;
  font-size: 0.75rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-dim);
  margin-bottom: 0.5rem;
}

.field-input {
  width: 100%;
  height: 52px;
  padding: 0 1rem;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
  color: var(--text);
  font-family: inherit;
  font-size: 1.0625rem;
  transition: border-color 140ms ease, background-color 140ms ease;
}
.field-input::placeholder {
  color: var(--text-dim);
}
.field-input:hover {
  border-color: var(--border-medium);
}
.field-input:focus {
  outline: none;
  border-color: var(--violet);
  background: var(--surface-2);
}
.field-input:focus-visible {
  outline: 2px solid var(--violet);
  outline-offset: 2px;
}
.field-input[aria-invalid="true"] {
  border-color: var(--error);
}

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  height: 52px;
  padding: 0 1.5rem;
  border: 1px solid transparent;
  border-radius: var(--radius);
  font-family: inherit;
  font-size: 0.9375rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  cursor: pointer;
  transition: background-color 140ms ease, scale 140ms ease;
}
.btn-primary {
  background: var(--violet);
  color: #07080d;
}
.btn-primary:hover {
  background: var(--violet-pressed);
}
.btn-primary:active {
  background: var(--violet-pressed);
  scale: 0.96;
}
.btn-primary[disabled] {
  opacity: 0.62;
  cursor: default;
  scale: 1;
}

.field-error {
  /* Reserved from first paint: an error message must never push the device
     question or the button down as the user types.

     The reservation is exactly one line — 15px at 1.33 gives 20px, which is
     what `min-height` holds. They used to disagree (20px reserved, 24px
     rendered), so an error still nudged everything below it by 4px, which is
     the one thing this block exists to prevent. No top margin: the reserved
     strip is already the gap under the field. */
  min-height: 1.25rem;
  margin-top: 0;
  color: var(--error);
  font-size: 0.9375rem;
  line-height: 1.33;
}

.consent {
  margin-top: 0.75rem;
  color: var(--text-muted);
  font-size: 0.8125rem;
  line-height: 1.538; /* 13/20 */
}
.consent a {
  color: var(--text-muted);
  text-decoration: underline;
  text-decoration-color: var(--border-medium);
}
.consent a:hover {
  color: var(--text);
}

/* Success state */

.joined {
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--surface);
  padding: 1.25rem;
}
.joined-head {
  display: flex;
  align-items: flex-start;
  gap: 0.75rem;
  font-size: 1.0625rem;
  line-height: 1.647;
  color: var(--text-muted);
}
.joined-head strong {
  color: var(--text);
  font-weight: 600;
}
.joined-check {
  flex: none;
  width: 22px;
  height: 22px;
  margin-top: 0.15rem;
  border-radius: 50%;
  border: 1px solid var(--teal);
  background: rgba(99, 214, 197, 0.12);
  position: relative;
}
.joined-check::after {
  content: "";
  position: absolute;
  left: 7px;
  top: 4px;
  width: 5px;
  height: 9px;
  border: solid var(--teal);
  border-width: 0 1.5px 1.5px 0;
  transform: rotate(45deg);
}


/* The Founding invitation inside the success card.
   Violet because it is an action, but text rather than a button: the signup
   that just succeeded is the conversion, and a second filled CTA here would
   compete with the calm it earned. Separated by a hairline, never a card. */
.joined-founding {
  margin: 0.9375rem 0 0;
  padding-top: 0.625rem;
  border-top: 1px solid var(--hairline);
}
.founding-invite {
  color: var(--violet);
  font-weight: 600;
  font-size: 1.0625rem;
  text-decoration: none;
  /* The arrow is part of the link, so the tap target is one whole phrase.
     44px because everything else you can press in this system is at least
     that: a link this important should not be the one thumb-sized exception. */
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  gap: 0.375rem;
}
.founding-invite:hover {
  color: var(--text);
}
.founding-invite-arrow {
  transition: transform 160ms cubic-bezier(0.22, 0.61, 0.36, 1);
}
.founding-invite:hover .founding-invite-arrow {
  transform: translateX(3px);
}
.founding-invite-note {
  display: block;
  margin-top: 0.25rem;
  color: var(--text-dim);
  font-size: 0.9375rem;
  line-height: 1.5;
}

/* The optional device answer, sitting under the email field. */
.device {
  /* One step down from 1rem on the same 0.25rem scale the rest of the form
     uses. The 24px above this is already spoken for by `.field-error`, which
     reserves its height so a validation message cannot shove this question
     down — so the visible gap was 40px and read as a break between two
     sections rather than two parts of one form. */
  margin: 0.5rem 0 0;
  padding: 0;
  border: 0;
}
.device-legend {
  padding: 0;
  font-size: 0.8125rem;
  color: var(--text-muted);
}
.ask-optional {
  color: var(--text-dim);
  font-size: 0.75rem;
  letter-spacing: 0.04em;
}
.chip {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  padding: 0 1.125rem;
  border: 1px solid var(--border);
  border-radius: 100px;
  background: var(--surface);
  color: var(--text-muted);
  font-size: 0.9375rem;
  font-weight: 500;
  cursor: pointer;
  transition: border-color 140ms ease, color 140ms ease,
    background-color 140ms ease, scale 140ms ease;
}
.chip:hover {
  border-color: var(--border-medium);
  color: var(--text);
}
.chip:active {
  scale: 0.96;
}
[data-platform]:checked + .chip {
  border-color: var(--violet);
  background: var(--violet-soft);
  color: var(--text);
}
/* The radio itself is visually hidden, so its focus ring has to live here. */
[data-platform]:focus-visible + .chip {
  outline: 2px solid var(--violet);
  outline-offset: 3px;
}

.btn-block {
  width: 100%;
  margin-top: 1rem;
}

/* ── Founding Member offer ────────────────────────────────────────────────
   Shown only *after* someone has joined, so it can never compete with the
   email field for the first fold. It is a separate block below the "You're
   in" card rather than a section inside it: joining is finished, and this is
   a different, entirely optional decision.

   Deliberately unlike a pricing page — one price, one sentence of terms, one
   button. No badge row, no countdown, no feature checklist, no second tier to
   compare against. Violet stays the action colour; the teal check above is the
   only confirmed thing on screen, and this offer is not confirmed yet. */

.offer {
  margin-top: 0.75rem;
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--surface);
  padding: 1.25rem;
}

.offer-label {
  font-size: 0.6875rem;
  font-weight: 600;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: var(--violet);
}

/* Baseline-aligned rather than centred: the amount is the anchor and the two
   smaller parts sit on its line, so the row reads as one price and not as
   three stacked labels. */
.offer-price {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.5rem;
  margin-top: 1rem;
}
.offer-amount {
  font-family: var(--display);
  font-size: 2rem;
  font-weight: 600;
  line-height: 1.1;
  letter-spacing: -0.02em;
  color: var(--text);
  /* Same-width digits, so a price that changes never shifts what follows it. */
  font-variant-numeric: tabular-nums;
}
.offer-once {
  font-size: 0.9375rem;
  color: var(--text-muted);
}

.offer-terms {
  margin-top: 0.75rem;
  font-size: 0.9375rem;
  line-height: 1.55;
  color: var(--text-muted);
}

/* The CTA is its own block below the price card, so the price reads as
   information and the button reads as the single thing to do. */
[data-founding-solo] {
  margin-top: 1.25rem;
}

/* Sits under the price: before it resolves, the graceful line; after, the
   plain statement of what the price is for. Never a struck-through second
   number — no regular price has ever been charged, because nothing has
   launched, and a localised comparison would need a second Paddle price that
   does not exist. */
.offer-regular {
  margin-top: 0.5rem;
  font-size: 0.9375rem;
  line-height: 1.55;
  color: var(--text-dim);
}

/* What the purchase includes. A list, not a grid of badges. */
.offer-points {
  margin: 1rem 0 0;
  padding: 0;
  list-style: none;
}
.offer-points li {
  position: relative;
  padding-left: 1.375rem;
  margin-top: 0.5rem;
  font-size: 0.9375rem;
  line-height: 1.55;
  color: var(--text-muted);
}
.offer-points li::before {
  content: "";
  position: absolute;
  left: 0.25rem;
  top: 0.6875rem;
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: var(--violet);
}

/* The one emphasised phrase in the closed state. */
.solo-soon {
  display: block;
  color: var(--text);
  font-weight: 600;
}

.offer-foot {
  margin-top: 0.75rem;
  font-size: 0.8125rem;
  line-height: 1.5;
  color: var(--text-dim);
}

.offer-error {
  margin-top: 0.75rem;
  font-size: 0.9375rem;
  color: var(--error);
}
.offer-error:empty {
  display: none;
}

/* ── Founding standalone pages (/founding, /founding-success) ───────────── */

.solo {
  min-height: 100svh;
  display: flex;
  align-items: center;
  padding: 2rem 0 3rem;
}
.solo-inner {
  width: 100%;
  max-width: 27rem;
  margin: 0 auto;
}
.solo .offer {
  animation: none;
  margin-top: 1.5rem;
}
.solo-brand {
  display: inline-flex;
  align-items: center;
  font-family: var(--display);
  font-size: 1.0625rem;
  font-weight: 600;
  letter-spacing: -0.01em;
  color: var(--text);
}
.solo-title {
  margin-top: 1.5rem;
  font-size: 1.5rem;
  line-height: 1.25;
  letter-spacing: -0.02em;
}
.solo-lede {
  margin-top: 0.75rem;
  color: var(--text-muted);
}
/* The "Try again" recovery is an anchor, not a button element. */
a.btn-block {
  margin-top: 1.5rem;
  text-decoration: none;
}
a.btn-primary {
  color: #07080d;
}

/* The confirmed state. Teal appears here and nowhere else on these pages,
   because this is the one thing that is genuinely, finally done. */

.solo-note {
  margin-top: 1.5rem;
  color: var(--text-muted);
  font-size: 0.9375rem;
  line-height: 1.6;
}
.solo-support {
  margin-top: 1rem;
  color: var(--text-dim);
  font-size: 0.8125rem;
}

/* A working state must look like work, not like failure. */
.solo-pending {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  margin-top: 1.5rem;
  color: var(--text-muted);
}
.spinner {
  flex: none;
  width: 18px;
  height: 18px;
  border-radius: 50%;
  border: 2px solid var(--border-medium);
  border-top-color: var(--violet);
  animation: spin 700ms linear infinite;
}
@keyframes spin {
  to {
    transform: rotate(360deg);
  }
}

@media (min-width: 40rem) {
  .offer {
    padding: 1.5rem;
  }
  .solo-title {
    font-size: 1.75rem;
  }
}

@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }
  *,
  *::before,
  *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
  }
}

/* ── The one screen ───────────────────────────────────────────────────────
   One idea, one action, inside one viewport. `min-height` and `svh` rather
   than `height`/`vh`: the composition fills a phone without ever locking the
   page, so a short screen, landscape, 200% zoom, larger text or an open
   keyboard all just scroll. Nothing here is `overflow: hidden`. */

.screen {
  /* The drawing's height is the one number the hero composition is built from:
     the SVG's width follows it (viewBox 300x200), and `.moment` is sized as a
     multiple of it so the thought text keeps the same relationship to the
     perception marks at every breakpoint instead of drifting as the column
     changes width. */
  --fig-h: clamp(7.5rem, 20svh, 12rem);
  min-height: 100svh;
  max-width: 34rem;
  margin: 0 auto;
  display: flex;
  flex-direction: column;
  /* `safe` matters: plain centring pushes the overflow off BOTH ends when the
     content is taller than the screen, and the top half becomes unreachable.
     With `safe`, a short screen, big text or an open keyboard just scrolls. */
  justify-content: safe center;
  gap: clamp(0.6rem, 1.7svh, 1.15rem);
  padding: max(1.25rem, env(safe-area-inset-top)) max(1.25rem, env(safe-area-inset-right))
    max(1.5rem, env(safe-area-inset-bottom)) max(1.25rem, env(safe-area-inset-left));
}

.brand-line {
  margin-bottom: clamp(0.25rem, 1.4svh, 1rem);
}

/* ── The moment ───────────────────────────────────────────────────────────
   A person who has just finished a conversation and is quietly re-reading it.
   Line art, no face, no infographic: the thought is the subject and the
   drawing is only the room it happens in. */

.moment {
  position: relative;
  /* Sized from the drawing, not from the column. The perception marks end at a
     fixed fraction of the drawing, so anchoring the box to the drawing is what
     keeps the thought text the same distance from them on a 320px phone and a
     1920px monitor — 2.08 is the ratio the phone layout was drawn at. */
  width: min(100%, calc(var(--fig-h) * 2.08));
  margin-bottom: clamp(0.25rem, 1.2svh, 0.85rem);
}

.figure {
  display: block;
  /* Height-led rather than width-led: on a phone the scarce axis is vertical,
     so the drawing gives space back to the words as the screen gets shorter. */
  height: var(--fig-h);
  width: auto;
  max-width: 100%;
  color: var(--text-muted);
  opacity: 0.85;
}

/* The head rotates about its own chin rather than its centre, so the neck stays
   attached through the gesture. `fill-box` keeps the pivot on the head's own
   bounding box, which means the SVG can be re-drawn without re-deriving a
   coordinate here. */
.figure-head-pivot {
  transform-box: fill-box;
  transform-origin: 50% 100%;
}

/* Violet, but at half strength: these are marks a mind makes, not a highlight. */
.marks {
  opacity: 0.55;
}

.thought {
  position: absolute;
  top: 6%;
  /* 52% of the moment box: the marks stop at about 45% of it, so this is a
     constant optical gap rather than a number that drifts every time the
     viewport changes shape. `max()` keeps the line readable when the drawing
     is at its smallest and the percentage alone would squeeze it. */
  left: 52%;
  right: auto;
  /* Capped as well as floored: given a wide box the line fits on one row, and
     the two-line shape is part of how the thought reads as a thought. */
  width: clamp(7.5rem, 44%, 11rem);
  font-family: var(--display);
  font-size: clamp(0.9375rem, 4vw, 1.0625rem);
  font-weight: 400;
  line-height: 1.35;
  color: var(--text-muted);
  text-wrap: balance;
}

/* ── The promise ──────────────────────────────────────────────────────── */

.promise {
  /* The floor is 1.75rem, not 2rem: at 200% browser zoom a 390px phone is a
     203px viewport, and "Psychology" set at 32px is wider than that — the page
     scrolled sideways. Nothing here may ever need a horizontal scrollbar. */
  font-size: clamp(1.75rem, 8.4vw, 2.625rem);
  overflow-wrap: break-word;
  line-height: 1.08;
  letter-spacing: -0.022em;
}

.support {
  max-width: 42ch;
  color: var(--text-muted);
  font-size: 1.0625rem;
  line-height: 1.5;
}

/* ── The action ───────────────────────────────────────────────────────── */

.screen .waitlist {
  margin-top: clamp(0.25rem, 1.4svh, 0.9rem);
  max-width: none;
}

/* 0.985 rather than the 0.96 the offer pages use: a press, not a squash. Kept
   in CSS rather than driven by Motion so it can never be left mid-press when a
   touch turns into a scroll, and so it still works if the module fails. */
.screen .btn-primary {
  transition: background-color 140ms ease, scale 120ms ease;
}
.screen .btn-primary:active {
  scale: 0.985;
}

/* The device answer as a real segmented control: one track, one indicator.
   The controls underneath are still native radios, so this works by keyboard,
   posts with the form when JavaScript is off, and can still be un-picked. */

.seg {
  position: relative;
  display: grid;
  grid-template-columns: 1fr 1fr;
  margin-top: 0.625rem;
  max-width: 20rem;
  border: 1px solid var(--border);
  border-radius: 100px;
  background: var(--surface);
  overflow: hidden;
}
.seg-indicator {
  position: absolute;
  inset: 0 50% 0 0;
  border-radius: 100px;
  background: var(--violet-soft);
  opacity: 0;
  transform: translateX(0);
  transition: opacity 140ms ease,
    transform 200ms cubic-bezier(0.22, 0.61, 0.36, 1);
}
/* `:has` gives the indicator its resting position without a line of JavaScript;
   Motion only takes over the same transform to add the spring. */
.seg:has(#platform-ios:checked) .seg-indicator {
  opacity: 1;
  transform: translateX(0);
}
.seg:has(#platform-android:checked) .seg-indicator {
  opacity: 1;
  transform: translateX(100%);
}
.seg .chip {
  position: relative;
  z-index: 1;
  justify-content: center;
  border: 0;
  border-radius: 100px;
  background: none;
  min-height: 46px;
  /* The track already gives each answer exactly half, so the chip's own side
     padding only stops the grid shrinking below its content — which is what
     pushed the control past the viewport at 200% zoom. */
  padding: 0 0.5rem;
  min-width: 0;
}
.seg .chip:active {
  scale: 1;
}
/* Inside the track the fill belongs to the indicator, so a checked chip only
   brightens its own label — otherwise two violets stack on the same pill. */
.seg [data-platform]:checked + .chip {
  background: none;
  border-color: transparent;
  color: var(--text);
}

/* Full column width, not a shorter measure: this sentence sits directly under
   a full-width button, and a ragged edge 28px short of it reads as a mistake. */
.screen .consent {
  margin-top: 0.875rem;
  max-width: none;
}
/* The pair travels together: letting the line break between them left a lone
   "· Terms" hanging under the sentence. */
.consent-links {
  white-space: nowrap;
}

/* ── The opening curtain ──────────────────────────────────────────────────
   Present in the HTML so it is on screen at first paint, painted in the page's
   own background colour so it reads as the page rather than as a cover, and
   `pointer-events: none` so it can never take a tap.

   The failsafe animation is the point of this block: if `intro.js` never loads,
   never parses, or the vendored Motion module 404s, the cover still clears on
   its own. JavaScript opts out of it by adding `.curtain--js`. */

.curtain {
  position: fixed;
  inset: 0;
  z-index: 50;
  background: var(--bg);
  pointer-events: none;
  display: grid;
  grid-template-columns: repeat(var(--cols, 6), 1fr);
  grid-template-rows: repeat(var(--rows, 10), 1fr);
  /* 1100ms, and `intro.js` shares the number: it is the moment the stylesheet
     takes the opening back from a module that never arrived. Generous on
     purpose — the cover is the page's own colour, so waiting longer costs a
     visitor nothing, while cutting it short costs a slow connection the
     entrance entirely. */
  animation: curtain-failsafe 240ms linear 1100ms both;
}
@keyframes curtain-failsafe {
  to {
    opacity: 0;
    visibility: hidden;
  }
}
/* Once the tiles exist they carry the colour, so the sheet behind them stops
   painting — otherwise clearing every tile would still leave the cover. */
.curtain--js {
  background: none;
  animation: none;
}
.curtain-tile {
  background: var(--bg);
}
.curtain-tile--surface {
  background: var(--surface);
}
.curtain-tile--surface-2 {
  background: var(--surface-2);
}
/* A little stronger than --violet-soft: at 14% over near-black a violet tile
   is invisible, and the point of these is that you catch two or three. */
.curtain-tile--violet {
  background: rgba(139, 124, 255, 0.2);
}

@media (prefers-reduced-motion: reduce) {
  /* No curtain at all, rather than a curtain resolved quickly. */
  .curtain {
    display: none;
  }
}

/* ── Short screens ────────────────────────────────────────────────────────
   An iPhone SE is 667px tall and the composition needs about 740. Rather than
   shrink type — which is the one thing that must stay readable — the density
   comes out of the spaces between things and out of the drawing, which is the
   element that can afford it. The type scale is untouched below 1.75rem.

   Height-based, not width-based: a 390px-wide phone in landscape has the same
   problem as a narrow one in portrait, and a width query would miss it. */

@media (max-height: 46rem) {
  .screen {
    gap: 0.45rem;
    padding: max(0.75rem, env(safe-area-inset-top))
      max(1.25rem, env(safe-area-inset-right))
      max(0.875rem, env(safe-area-inset-bottom))
      max(1.25rem, env(safe-area-inset-left));
  }
  .brand-line {
    margin-bottom: 0.125rem;
  }
  .screen {
    --fig-h: clamp(5.5rem, 17svh, 9rem);
  }
  .moment {
    margin-bottom: 0.125rem;
  }
  .promise {
    font-size: clamp(1.625rem, 7.6vw, 2rem);
  }
  .screen .waitlist {
    margin-top: 0.25rem;
  }
  .field-label {
    margin-bottom: 0.3125rem;
  }
  .device {
    margin-top: 0.25rem;
  }
  .seg {
    margin-top: 0.4375rem;
  }
  .btn-block {
    margin-top: 0.625rem;
  }
  .screen .consent {
    margin-top: 0.5rem;
    font-size: 0.75rem;
    line-height: 1.5;
  }
}

/* ── Desktop ──────────────────────────────────────────────────────────────
   The same composition, expanded: the moment moves beside the words instead of
   above them, and nothing changes order for a screen reader. */

@media (min-width: 60rem) {
  .screen {
    max-width: 68rem;
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1.05fr);
    grid-template-rows: auto auto auto auto;
    align-content: center;
    column-gap: 3.5rem;
    row-gap: clamp(0.75rem, 1.6svh, 1.25rem);
  }
  .brand-line,
  .promise,
  .support,
  .screen .waitlist {
    grid-column: 1;
  }
  .moment {
    grid-column: 2;
    grid-row: 1 / span 4;
    align-self: center;
    margin-bottom: 0;
  }
  .screen {
    /* Capped at 16rem: at 34svh the drawing reached 304px on a 1080p screen and
       the composition read as a zoomed-in phone layout rather than a desktop one. */
    --fig-h: clamp(11rem, 26svh, 16rem);
  }
  .moment {
    margin-left: auto;
  }
  .thought {
    top: 0;
    font-size: 1.0625rem;
  }
  .promise {
    /* Fluid rather than a flat 3rem. A fixed 48px headline is tuned to exactly
       one laptop and looks oversized on every other, which is what "excessively
       zoomed" was describing. */
    font-size: clamp(2.25rem, 3vw, 3rem);
  }
  .support {
    font-size: clamp(1rem, 1.15vw, 1.125rem);
  }
}
