/* ==========================================================================
   launch.css: the launching-soon page, and only that page.
   ==========================================================================

   SELF-CONTAINED by design. comingsoon.html links this file and neither
   styles.css nor site.css, because the `.cs-*` names here are the same names
   site.css uses (legal.html and the compare pages share `.cs-foot`, and the
   tests pin `.cs-h1` and `.cs-access__toggle`). Loading both sheets would put
   two rule sets in a specificity fight over every selector. Loading one means
   there is none. Everything shared is therefore redeclared below, COPIED from
   the real system rather than reinvented: the @font-face blocks and the token
   values are exactly what styles.css carries, so the page cannot drift into
   being its own brand.

   ---------------------------------------------------------------------------
   WHAT THIS IS
   ---------------------------------------------------------------------------

   A quiet page with four things on it: the mark, a headline, one sentence, and
   one field. That is the whole brief and the restraint is the design.

   It got here by subtraction. Three earlier versions were rejected, and the
   record is worth keeping because each failure had a different cause:

     - a flat white page with a blue button. It broke PRODUCT.md's "do not
       relitigate" visual law in four places, chiefly by replacing the night
       indigo and dropping Fraunces.
     - a purple-to-blue gradient with glass on every surface. impeccable.style,
       which this project already uses, names that combination as the current
       AI signature outright: "purple gradients, glassmorphism, neon glow."
     - a full-width catenary cable running through the page, drawn from the
       brand's own wire motif. It was distinctive and the owner did not want
       it: the graphic split the layout and earned nothing. Removed 2026-08-26.

   The lesson that survives all three: this page's job is to say almost nothing
   and take an email address. Anything that competes with those two jobs is
   wrong however well it is executed.

   WHAT IS TAKEN FROM APPLE: restraint, one accent, a type ramp whose
   line-height ratio falls as size rises, material used sparingly and on chrome
   only (HIG > Materials: "Don't use Liquid Glass in the content layer"), and
   contrast checked against the ground the text is actually painted on.

   WHAT IS TAKEN FROM WISPR FLOW: not the palette, the MOVE. Measured live, it
   runs EB Garamond at 96/91.2, a ratio of 0.95, with a true-italic second line
   ("Don't type, / just speak."). wireagent already owns exactly that pairing
   in Fraunces roman + Fraunces italic, both already subsetted in public/fonts.
*/

/* ------------------------------------------------------------------ faces */

/* Copied verbatim from styles.css. Same files, same axes, same weights. */
@font-face {
  font-family: "Fraunces";
  src: url("/fonts/fraunces.woff2") format("woff2");
  font-weight: 200 900; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "Fraunces";
  src: url("/fonts/fraunces-italic.woff2") format("woff2");
  font-weight: 200 900; font-style: italic; font-display: swap;
}
@font-face {
  font-family: "InterVar";
  src: url("/fonts/inter.woff2") format("woff2");
  font-weight: 100 900; font-style: normal; font-display: swap;
}

/* ----------------------------------------------------------------- tokens */

.ls {
  /* The night indigo family, verbatim from styles.css. Not a new palette. */
  --void:    #05070f;
  --bg:      #0a0f1e;
  --surface: #0f1730;
  --border:        #232f4e;
  --border-strong: #33436b;

  --text:  #eaf0ff;
  --muted: #9daecf;
  --faint: #808fb6;

  /*
   * WHITE is the single accent. That is the standing law, and it is also the
   * discipline Apple applies: one accent, spent in one place. Warn and error
   * are the only saturated colours in this system and neither appears at rest,
   * so there is no blue, no purple and no glow anywhere in this file.
   */
  --accent:     #ffffff;
  --accent-dim: #f2f5ff;
  --accent-ink: #0a1024;

  --bad:      #e76a6e;
  --bad-soft: #2b1416;
  --good:     #8fe0b0;
  --good-soft:#12301f;

  --glass:      rgba(130, 150, 225, .05);
  --glass-edge: rgba(140, 162, 230, .16);
  --blur: blur(22px) saturate(150%);

  --sans:  "InterVar", "Inter", ui-sans-serif, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  --serif: "Fraunces", Georgia, "Times New Roman", serif;

  /*
   * The line-height curve. The ratio only ever rises as the size falls, with
   * no exceptions: that is the single most transferable thing measured off
   * apple.com, and Wispr runs the same shape one notch tighter at the top.
   * 0.95 at display is Wispr's number and it is what stops a two-line serif
   * headline reading as two separate sentences.
   */
  --lh-display: 0.95;
  --lh-title:   1.06;
  --lh-lead:    1.24;
  --lh-body:    1.5;
  --lh-small:   1.55;
  --lh-fine:    1.6;

  --r-pill: 999px;
  --r:      10px;
  --bar:    44px;
  --gutter: clamp(20px, 5vw, 40px);
  --ease:   cubic-bezier(.22, 1, .36, 1);
}

/* ------------------------------------------------------------- foundation */

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

body.ls {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: var(--sans);
  font-size: 17px;
  line-height: var(--lh-body);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

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

.ls a { color: var(--muted); text-decoration: none; }
.ls a:hover { color: var(--text); }

.ls-wrap {
  width: 100%;
  max-width: 1080px;
  margin-inline: auto;
  padding-inline: var(--gutter);
}

/* ------------------------------------------------------------------ shell */

.cs {
  position: relative;
  min-height: 100dvh;
  display: grid;
  grid-template-rows: auto 1fr auto;
  isolation: isolate;
  overflow: clip;
}

/*
 * A single, very low indigo light behind the page: enough to keep the bar's
 * glass from refracting nothing, and no more. PRODUCT.md calls for an aurora
 * behind the glass; what it does not call for is a coloured hero, which is
 * what the previous version turned it into.
 *
 * One hue, no second colour, no purple. If you find yourself adding a stop,
 * read impeccable.style's slop catalogue first.
 */
.cs::before {
  content: "";
  position: fixed;
  inset: 0;
  z-index: -2;
  background:
    radial-gradient(58rem 40rem at 50% 34%, rgba(70, 96, 190, .16) 0%, transparent 68%),
    radial-gradient(40rem 30rem at 50% 108%, rgba(70, 96, 190, .10) 0%, transparent 66%);
}

/*
 * Grain over everything. PRODUCT.md lists it as part of the identity, and it
 * earns its place technically too: a large smooth gradient banks into visible
 * bands on an 8-bit display, and noise dithers the steps away. You should not
 * be able to see it, only the absence of banding.
 */
.cs::after {
  content: "";
  position: fixed;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  opacity: .04;
  background-image: url("data:image/svg+xml;utf8,\
<svg xmlns='http://www.w3.org/2000/svg' width='140' height='140'>\
<filter id='n'><feTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='3'/></filter>\
<rect width='140' height='140' filter='url(%23n)'/></svg>");
}

/* -------------------------------------------------------------------- bar */

/*
 * Glass, and this is the ONLY glass on the page. PRODUCT.md: "liquid glass on
 * chrome only". Apple says the same thing from the other direction in HIG >
 * Materials: "Don't use Liquid Glass in the content layer... including it in
 * the content layer can result in unnecessary complexity and a confusing
 * visual hierarchy." The previous version put it on the waiting-list card and
 * broke both rules at once.
 */
.cs-top {
  position: sticky;
  top: 0;
  z-index: 20;
  height: var(--bar);
  display: flex;
  align-items: center;
  background: rgba(10, 15, 30, .55);
  -webkit-backdrop-filter: var(--blur);
  backdrop-filter: var(--blur);
  border-bottom: 1px solid var(--glass-edge);
}
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .cs-top { background: var(--bg); }
}

.cs-top .ls-wrap { display: flex; align-items: center; justify-content: center; }

.logo {
  --logo-ink: var(--void);
  display: inline-flex;
  align-items: center;
  gap: 9px;
  color: var(--text);
}
.logo:hover { color: var(--text); }
.logo__mark { display: block; }
.logo__word {
  font-size: 15px;
  font-weight: 600;
  letter-spacing: -.012em;
  line-height: 1;
}

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

.cs-main {
  position: relative;
  display: grid;
  align-content: center;
  padding: clamp(28px, 5vh, 56px) 0 clamp(36px, 7vh, 80px);
}

.cs-inner {
  position: relative;
  z-index: 2;
  display: grid;
  justify-items: center;
  text-align: center;
  gap: clamp(16px, 2.4vh, 24px);
  width: 100%;
  min-width: 0;
  max-width: 48rem;
  margin-inline: auto;
  padding-inline: var(--gutter);
}

/*
 * The headline. Fraunces at its display optical size, with the axes PRODUCT.md
 * names as the thing that makes an otherwise widely-used face ours:
 * opsz 144, SOFT 30, WONK 1.
 *
 * Roman first line, TRUE ITALIC second. That is Wispr Flow's move, measured
 * off their hero, and wireagent already owns the pairing: fraunces.woff2 and
 * fraunces-italic.woff2 are both already subsetted and shipping. A synthesised
 * oblique would not do it; this is a drawn italic with different letterforms.
 */
.cs-h1 {
  margin: 0;
  font-family: var(--serif);
  font-size: clamp(52px, 11vw, 116px);
  line-height: var(--lh-display);
  letter-spacing: -.03em;
  font-weight: 400;
  font-variation-settings: "opsz" 144, "SOFT" 30, "WONK" 1;
  color: var(--text);
  text-wrap: balance;
}

.cs-h1 em {
  display: block;
  font-style: italic;
  color: var(--accent);
  /*
   * Painted room below the baseline. Nothing here clips a gradient any more,
   * but Fraunces italic has long descenders and a 116px line at a 0.95 ratio
   * has no room to spare, so the box is given some and the negative margin
   * takes it back out of the layout.
   */
  padding-bottom: .08em;
  margin-bottom: -.08em;
}

.cs-lead {
  margin: 0;
  font-size: clamp(17px, 1.9vw, 20px);
  line-height: var(--lh-lead);
  color: var(--muted);
  max-width: 30rem;
  text-wrap: balance;
}

/* ------------------------------------------------------- waiting list */

/*
 * No card, no glass, no border box: the field stands on the page.
 *
 * PRODUCT.md's anti-references list "stat-tile soup" and "icon-in-rounded-square
 * chips" among the generic AI tells removed from this site by name. A rounded
 * translucent panel is the same family, and it is what an earlier version of
 * this page put here.
 */
.cs-wait-block {
  position: relative;
  display: grid;
  justify-items: center;
  /* Room between the pitch, the pill and the one optional question. It was
     14px, which packed them into one dense block once a second question was
     added; the second question is gone and the block breathes (2026-09-02). */
  gap: 22px;
  width: 100%;
  min-width: 0;
  max-width: 32rem;
  margin-top: clamp(14px, 3vh, 28px);
}

/*
 * --lh-body, not --lh-small. The probe caught this: the pitch is 15px and the
 * optional-question label below it is 14px, and both were on --lh-small, so
 * the ramp had two different sizes at an identical ratio and the curve
 * flattened where it should have kept rising. A ramp that is monotonic
 * everywhere except one pair is not a ramp.
 */
.cs-wait__pitch {
  margin: 0;
  font-size: 15px;
  line-height: var(--lh-body);
  color: var(--faint);
  max-width: 42ch;
  text-wrap: balance;
}

/*
 * One control, drawn as a single terminal on the cable: the field and the
 * button share one pill and one border, so it reads as a component the wire
 * runs into rather than as two things placed near each other.
 */
.cs-wait {
  display: flex;
  align-items: center;
  gap: 6px;
  width: 100%;
  min-width: 0;
  padding: 6px 6px 6px 8px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-pill);
  transition: border-color .25s var(--ease);
}
.cs-wait:focus-within { border-color: var(--border-strong); }

.cs-wait input {
  flex: 1 1 auto;
  min-width: 0;
  height: 40px;
  padding: 0 12px;
  font: inherit;
  font-size: 17px;
  color: var(--text);
  background: transparent;
  border: 0;
  border-radius: var(--r-pill);
}
.cs-wait input:focus { outline: none; }
.ls .cs-wait input::placeholder { color: var(--faint); }

.cs-wait .btn { flex: 0 0 auto; }

/*
 * The optional name sits inside the pill, ahead of the email. One control,
 * one border: it briefly had a pill of its own above this one, and three
 * centred blocks of three different widths (name, email, select) read as a
 * stack of mismatched parts rather than a form.
 *
 * In the row the name takes a fixed slice and the email keeps the flex, so a
 * long address still has the room. A hairline on the email's left edge is the
 * only seam between the two fields; both keep the 17px floor that stops iOS
 * zooming on focus. The column layout below 560px resets the basis, because
 * in a column flex-basis is the HEIGHT and 7.5rem of input is not a field.
 */
.cs-wait .cs-wait__name { flex: 0 0 7.5rem; }
.cs-wait .cs-wait__email {
  border-left: 1px solid var(--border);
  border-radius: 0;
}

.cs-wait__why {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  width: 100%;
  min-width: 0;
  gap: 10px 14px;
  margin-top: 4px;
  font-size: 14px;
  line-height: var(--lh-small);
  color: var(--faint);
}

/*
 * 17px, not 14px: iOS zooms the whole page when a form control under 16px
 * takes focus, and `test/width-sweep` asserts the floor.
 *
 * `min-width: 0` on a flex item is the other half, and it is the one that is
 * easy to miss. A <select>'s intrinsic width is the width of its LONGEST
 * OPTION, and a flex item's automatic minimum size is its min-content size, so
 * without this the select dragged the whole document to 349px at a 320px
 * viewport. `max-width` alone cannot fix it: the MINIMUM propagates, not the
 * width.
 */
.cs-wait__why select {
  flex: 0 1 auto;
  min-width: 0;
  max-width: 100%;
  font: inherit;
  font-size: 17px;
  color: var(--muted);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r);
  padding: 0 12px;
  min-height: 40px;
  cursor: pointer;
  transition: border-color .25s var(--ease);
}
.cs-wait__why select:hover,
.cs-wait__why select:focus { border-color: var(--border-strong); }
/* The popup list is painted by the OS and inherits nothing, so it needs real
   colours or the options render dark on dark. */
.ls .cs-wait__why select option { background: var(--surface); color: var(--text); }

.cs-wait__note { width: 100%; text-align: center; }

.lp-proof {
  margin: 0;
  font-size: 14px;
  line-height: var(--lh-small);
  color: var(--faint);
}

/* ---------------------------------------------------------------- buttons */

/*
 * White fill, indigo ink. The single accent, spent on the single action.
 * 40px rather than 43px because it sits inside the field's own pill and the
 * 6px of padding around it carries the rest to a 52px target.
 */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  min-height: 40px;
  padding: 0 20px;
  font: inherit;
  font-size: 15px;
  font-weight: 600;
  letter-spacing: -.006em;
  line-height: 1;
  border: 1px solid transparent;
  border-radius: var(--r-pill);
  cursor: pointer;
  white-space: nowrap;
  transition: background .2s var(--ease), color .2s var(--ease), opacity .2s var(--ease);
}
.btn:disabled { opacity: .45; cursor: default; }

.btn--primary { background: var(--accent); color: var(--accent-ink); }
.btn--primary:hover:not(:disabled) { background: var(--accent-dim); }

.btn--ghost {
  background: transparent;
  color: var(--text);
  border-color: var(--border-strong);
}
.btn--ghost:hover:not(:disabled) { background: var(--glass); }

/* ------------------------------------------------------------------ notes */

.note {
  margin: 0;
  font-size: 14px;
  line-height: var(--lh-small);
  padding: 9px 13px;
  border-radius: var(--r);
}
/* Never colour alone: the HIG's Vision guidance is explicit that some people
   cannot tell red from green, so each state carries a glyph as well as a hue. */
.note--bad  { color: var(--bad);  background: var(--bad-soft); }
.note--good { color: var(--good); background: var(--good-soft); }
.note--bad::before  { content: "\26A0\FE0E  "; }
.note--good::before { content: "\2713  "; }

.muted { color: var(--faint); }
.small { font-size: 14px; line-height: var(--lh-small); }

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

.cs-foot {
  position: relative;
  z-index: 2;
  border-top: 1px solid var(--glass-edge);
  padding-block: 8px 16px;
  font-size: 12px;
  line-height: var(--lh-fine);
  color: var(--faint);
  background: rgba(5, 7, 15, .5);
}
.cs-foot__row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 0 24px;
}
.cs-foot__meta { color: var(--faint); }
.cs-foot__nav {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0 22px;
}
/* 12px of text inside a 40px target: the project's tap floor is 40px and
   `test/width-sweep` asserts it, so the hit area grows and the type does not. */
.ls .cs-foot__nav a {
  display: inline-flex;
  align-items: center;
  min-height: 40px;
  color: var(--faint);
}
.ls .cs-foot__nav a:hover { color: var(--text); }

/* ------------------------------------------------------ team sign-in link */

/*
 * A GET form whose button reads as one more footer link. The popover this
 * used to open (a <details> with the password form inside) is gone as of
 * 2026-09-02: the sign-in lives on /login now, with Clerk's card on a page of
 * its own. A form rather than an <a>, because robots.txt withholds /login and
 * test/markup.mjs refuses an indexable page that links a withheld path; the
 * form submits without JavaScript exactly as a link would.
 */
.cs-access { display: inline-flex; margin: 0; }

.cs-access__toggle {
  appearance: none;
  background: none;
  border: 0;
  padding: 0;
  margin: 0;
  font: inherit;
  cursor: pointer;
  color: var(--faint);
  display: inline-flex;
  align-items: center;
  min-height: 40px;
  transition: color .2s var(--ease);
}
.cs-access__toggle:hover { color: var(--text); }

/*
 * Load-bearing, and copied from styles.css for the same reason it is there:
 * the UA's `[hidden] { display: none }` loses to any author `display`. This
 * page sets display on several things it also toggles with `hidden`, and on
 * 2026-09-02 that made a hidden form render anyway. test/markup.mjs pins it.
 */
.ls [hidden] { display: none !important; }

/* --------------------------------------------------------------- entrance */

/*
 * The cable draws itself in, then the current starts, then the words arrive.
 * One sequence, in the order the page is meant to be read.
 *
 * Guarded TWICE. The media query covers this stylesheet; the `html.no-motion`
 * class is set by the page's own inline script from the same preference, which
 * is the only version a script can read. PRODUCT.md is explicit that
 * prefers-reduced-motion stills everything.
 */
@keyframes ls-rise {
  from { opacity: 0; transform: translate3d(0, 14px, 0); }
  to   { opacity: 1; transform: none; }
}

.cs-inner > * { animation: ls-rise .7s var(--ease) both; }
.cs-inner > *:nth-child(2) { animation-delay: .09s; }
.cs-inner > *:nth-child(3) { animation-delay: .18s; }

html.no-motion .cs-inner > *,
html.no-motion .cs-inner > *:nth-child(n) {
  animation: none;
  opacity: unset;
  transform: none;
}

@media (prefers-reduced-motion: reduce) {
  .ls *,
  .ls *::before,
  .ls *::after {
    animation-duration: .001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .001ms !important;
  }
  .cs-inner > * { opacity: 1; transform: none; }
}

/* ------------------------------------------------------------------ small */

@media (max-width: 560px) {
  /* Below this the shared pill cannot hold a field and a button on one line
     without the button's label wrapping, so they separate. */
  .cs-wait {
    flex-direction: column;
    align-items: stretch;
    gap: 8px;
    padding: 8px;
    border-radius: 22px;
  }
  .cs-wait input { flex: 0 0 auto; height: 44px; text-align: center; }
  /* Stacked, the name's row basis would become its HEIGHT, and the seam
     between name and email is a row rule, not a column one. Both selectors
     match the specificity of the row rules they undo. */
  .cs-wait .cs-wait__name { flex: 0 0 auto; }
  .cs-wait .cs-wait__email { border-left: 0; border-radius: var(--r-pill); }
  .cs-wait .btn { width: 100%; min-height: 44px; }
  .cs-foot__row { justify-content: center; text-align: center; }
  /* No .cs-signin override here any more: it is centred at every width by the
     base rule, so this was correcting that rule rather than adapting it. */
}

/* --------------------------------------------------------- forced colours */

/* Windows High Contrast strips backgrounds and filters, so the field's pill
   and the bar would lose every edge that defines them. Borders survive. */
@media (forced-colors: active) {
  .btn { border: 1px solid currentColor; }
  .cs-wait, .cs-signin, .cs-top { border: 1px solid currentColor; }
}
