@charset "utf-8";
/* ═══════════════════════════════════════════════════════════════════════════
   VelvetAbyss / Else — one stylesheet, no build step, no external requests.

   The whole site is one metaphor: PAPER and INTERIOR.

   Cream paper + terracotta ink is the surface — what a reader of the story
   sees. Deep plum is the interior — what the character is actually thinking,
   which the engine keeps and the reader never gets handed. Every dark band on
   the page is literally a look inside the machine, so the theme is doing
   argument work, not decoration.

   Colours are lifted from the app itself (frontend/src/index.css), where the
   contrast ratios were already tuned against real 11px labels.
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── Tokens ──────────────────────────────────────────────────────────────── */
:root {
  color-scheme: light;

  --paper:       #fdf8f5;
  --paper-soft:  #fbf1ec;
  --paper-card:  #f6e8e2;
  --paper-sunk:  #f2ded7;

  --rule:        #e9d3cc;
  --rule-soft:   #f3e4de;
  --rule-ember:  #e0b4a8;

  --ink:         #3a2a2e;
  --ink-2:       #6b5157;
  --ink-3:       #7a5a61;

  --ember:       #b04a35;  /* solid fills; carries white text at 6.6:1 */
  --ember-emph:  #a03c2b;  /* accent TEXT on cream — deeper than the fill */
  --ember-deep:  #822e22;
  --ember-soft:  #e28a76;
  --on-ember:    #fff7f4;

  --plum:        #2b1620;
  --plum-deep:   #22121a;

  --ok:          #236644;

  --backdrop: radial-gradient(125% 125% at 12% 0%, #f0cec6 0%, #e0aca6 45%, #c98d8d 100%);

  /* Shadows are tinted plum, never neutral black, or cards read as pasted on. */
  --lift-1: 0 1px 2px rgba(74, 34, 44, .06), 0 8px 28px -14px rgba(74, 34, 44, .18);
  --lift-2: 0 2px 6px rgba(74, 34, 44, .08), 0 22px 60px -22px rgba(74, 34, 44, .30);

  --serif: "Iowan Old Style", "Hoefler Text", "Palatino Linotype", "Book Antiqua",
           Constantia, Palatino, Georgia, "Times New Roman", serif;
  /* "Segoe UI Variable Text" is deliberately NOT first: it is a Windows 11-only
     variable font whose flex base size and painted width can disagree, which
     spills a nowrap button label outside its own pill. Plain Segoe UI has
     stable metrics and is on every Windows. */
  --sans:  "Segoe UI", -apple-system, BlinkMacSystemFont, "Helvetica Neue",
           system-ui, sans-serif;
  --mono:  "Cascadia Mono", "Cascadia Code", ui-monospace, "SF Mono", "Menlo",
           Consolas, monospace;

  --measure: 62ch;
  --page: 1180px;
  --gutter: clamp(20px, 5vw, 56px);
  --ease: cubic-bezier(.22, 1, .36, 1);

  /* Paper grain. One data URI, no request, no build. */
  --grain: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='180' height='180' filter='url(%23n)'/%3E%3C/svg%3E");
  --grain-opacity: .045;
  --grain-blend: multiply;
}

/* The plum interior. Used both as the dark theme and, via .interior, as a band
   inside the light page — same tokens, so every component follows without a
   single override. */
:root[data-theme="dark"], .interior {
  color-scheme: dark;

  --paper:       #2b1620;
  --paper-soft:  #331b26;
  --paper-card:  #381e2a;
  --paper-sunk:  #442633;

  --rule:        #4c2c3a;
  --rule-soft:   #3e232f;
  --rule-ember:  #7a3f44;

  --ink:         #faeeea;
  --ink-2:       #d6b8ba;
  --ink-3:       #bca1a5;

  --ember:       #f6c5b8;  /* on plum the fill inverts: light chip, plum label */
  --ember-emph:  #e99e8e;
  --ember-deep:  #d26c56;
  --ember-soft:  #d26c56;
  --on-ember:    #35191f;

  --ok:          #8bd0a4;

  --backdrop: linear-gradient(168deg, #3a1f2c 0%, #2b1620 55%, #22121a 100%);

  --lift-1: 0 1px 2px rgba(0, 0, 0, .35), 0 8px 28px -14px rgba(0, 0, 0, .55);
  --lift-2: 0 2px 8px rgba(0, 0, 0, .40), 0 24px 64px -24px rgba(0, 0, 0, .70);

  --grain-opacity: .07;
  --grain-blend: overlay;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    color-scheme: dark;

    --paper: #2b1620;  --paper-soft: #331b26;  --paper-card: #381e2a;  --paper-sunk: #442633;
    --rule: #4c2c3a;   --rule-soft: #3e232f;   --rule-ember: #7a3f44;
    --ink: #faeeea;    --ink-2: #d6b8ba;       --ink-3: #bca1a5;
    --ember: #f6c5b8;  --ember-emph: #e99e8e;  --ember-deep: #d26c56;  --ember-soft: #d26c56;
    --on-ember: #35191f;
    --ok: #8bd0a4;
    --backdrop: linear-gradient(168deg, #3a1f2c 0%, #2b1620 55%, #22121a 100%);
    --lift-1: 0 1px 2px rgba(0, 0, 0, .35), 0 8px 28px -14px rgba(0, 0, 0, .55);
    --lift-2: 0 2px 8px rgba(0, 0, 0, .40), 0 24px 64px -24px rgba(0, 0, 0, .70);
    --grain-opacity: .07;
    --grain-blend: overlay;
  }
  /* A light band dropped into the dark page — the mirror of .interior. */
  :root:not([data-theme="light"]) .surface {
    color-scheme: light;
    --paper: #fdf8f5;  --paper-soft: #fbf1ec;  --paper-card: #f6e8e2;  --paper-sunk: #f2ded7;
    --rule: #e9d3cc;   --rule-soft: #f3e4de;   --rule-ember: #e0b4a8;
    --ink: #3a2a2e;    --ink-2: #6b5157;       --ink-3: #7a5a61;
    --ember: #b04a35;  --ember-emph: #a03c2b;  --ember-deep: #822e22;  --ember-soft: #e28a76;
    --on-ember: #fff7f4;
    --ok: #236644;
  }
}

:root[data-theme="dark"] .surface {
  color-scheme: light;
  --paper: #fdf8f5;  --paper-soft: #fbf1ec;  --paper-card: #f6e8e2;  --paper-sunk: #f2ded7;
  --rule: #e9d3cc;   --rule-soft: #f3e4de;   --rule-ember: #e0b4a8;
  --ink: #3a2a2e;    --ink-2: #6b5157;       --ink-3: #7a5a61;
  --ember: #b04a35;  --ember-emph: #a03c2b;  --ember-deep: #822e22;  --ember-soft: #e28a76;
  --on-ember: #fff7f4;
  --ok: #236644;
}

/* ═══════════════════════════════════════════════════════════════════════════
   VELVET — the publisher's own skin, and nothing to do with Else's.

   VelvetAbyss is velvet: violet and black, with a sheen. Else is warm rose and
   terracotta. They are two brands that happen to share a stylesheet, so the
   split is a scope swap on <html data-brand="velvet"> and not one component
   override — the same trick .interior already uses.

   Velvet commits to the dark. There is no light velvet: a violet-and-black
   identity rendered on white is a different, worse brand. So these blocks sit
   AFTER every data-theme rule and win on source order at equal specificity,
   which makes the theme toggle irrelevant on velvet pages — they do not ship
   one.
   ═══════════════════════════════════════════════════════════════════════════ */
:root[data-brand="velvet"] {
  color-scheme: dark;

  --paper:       #140a20;
  --paper-soft:  #1b0e2b;
  --paper-card:  #221336;
  --paper-sunk:  #2c1943;

  --rule:        #3a2258;
  --rule-soft:   #2a1740;
  --rule-ember:  #5b3689;

  --ink:         #f3ecfb;
  --ink-2:       #cabbe2;
  --ink-3:       #ab9bc7;

  /* The accent is a lilac with light behind it — velvet catching a lamp. */
  --ember:       #c9a6ff;
  --ember-emph:  #c4a0fb;
  --ember-deep:  #9a6cf0;
  --ember-soft:  #8b5cf6;
  --on-ember:    #1a0d2b;

  --ok:          #86e0b0;

  --backdrop: radial-gradient(125% 125% at 15% 0%, #55287f 0%, #2a1247 44%, #0c0614 100%);

  --lift-1: 0 1px 2px rgba(0, 0, 0, .45), 0 8px 30px -14px rgba(0, 0, 0, .70);
  --lift-2: 0 2px 10px rgba(0, 0, 0, .50), 0 26px 70px -26px rgba(0, 0, 0, .85);

  --grain-opacity: .075;
  --grain-blend: overlay;
}

/* Deeper into the abyss, not lighter — the velvet equivalent of a dark band. */
:root[data-brand="velvet"] .interior {
  --paper:       #0a0512;
  --paper-soft:  #0e0718;
  --paper-card:  #150c22;
  --paper-sunk:  #1d1231;
  --rule:        #2a1a40;
  --rule-soft:   #1d1230;
  --rule-ember:  #4a2c72;
  --ink:         #f3ecfb;
  --ink-2:       #cabbe2;
  --ink-3:       #ab9bc7;
}

:root[data-brand="velvet"] .foot { background: #0a0512; }

/* Velvet's hero gradient is the brand, so it carries more of the frame than
   Else's does — where the wash is a warm hint behind cream paper. */
:root[data-brand="velvet"] .hero::before { opacity: .62; }

/* ── Reset ───────────────────────────────────────────────────────────────── */
*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
@media (prefers-reduced-motion: no-preference) { html { scroll-behavior: smooth; } }

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font: 400 clamp(17px, .48vw + 15.6px, 19px)/1.62 var(--serif);
  text-rendering: optimizeLegibility;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

/* Paper grain over everything, ignored by the pointer. */
body::after {
  content: "";
  position: fixed;
  inset: 0;
  z-index: 9999;
  pointer-events: none;
  background-image: var(--grain);
  opacity: var(--grain-opacity);
  mix-blend-mode: var(--grain-blend);
}

img, svg { max-width: 100%; height: auto; display: block; }
h1, h2, h3, h4 { font-weight: 600; line-height: 1.1; letter-spacing: -.018em; text-wrap: balance; margin: 0; }
p { margin: 0 0 1.05em; text-wrap: pretty; }
p:last-child { margin-bottom: 0; }
ul, ol { margin: 0; padding: 0; }
a { color: var(--ember-emph); text-underline-offset: .22em; text-decoration-thickness: from-font; }
a:hover { color: var(--ember-deep); }
strong { font-weight: 600; color: var(--ink); }
hr { border: 0; border-top: 1px solid var(--rule); margin: 0; }

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

::selection { background: var(--ember-soft); color: var(--plum-deep); }

.skip {
  position: absolute;
  left: 12px; top: -100px;
  z-index: 10000;
  padding: 12px 18px;
  background: var(--ember);
  color: var(--on-ember);
  font: 600 14px/1 var(--sans);
  border-radius: 6px;
  text-decoration: none;
  transition: top .18s var(--ease);
}
.skip:focus { top: 12px; color: var(--on-ember); }

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

/* ── Layout ──────────────────────────────────────────────────────────────── */
.wrap { width: 100%; max-width: var(--page); margin-inline: auto; padding-inline: var(--gutter); }
.band { background: var(--paper); color: var(--ink); position: relative; }
.band + .band { border-top: 1px solid var(--rule-soft); }
.band-soft { background: var(--paper-soft); }
.band > .wrap { padding-block: clamp(64px, 9vw, 116px); }

.interior, .surface { background: var(--paper); color: var(--ink); }
.interior { border-top: 0; }

.prose { max-width: var(--measure); }
.center { margin-inline: auto; text-align: center; }
.center .prose { margin-inline: auto; }

/* ── Editorial furniture ─────────────────────────────────────────────────── */
.eyebrow {
  display: flex;
  align-items: center;
  gap: 12px;
  margin: 0 0 20px;
  font: 500 11.5px/1 var(--mono);
  letter-spacing: .18em;
  text-transform: uppercase;
  color: var(--ember-emph);
}
.eyebrow::after {
  content: "";
  flex: 1;
  height: 1px;
  background: linear-gradient(90deg, var(--rule-ember), transparent);
}
.center .eyebrow { justify-content: center; }
.center .eyebrow::after { display: none; }
.center .eyebrow::before {
  content: "";
  width: 26px; height: 1px;
  background: var(--rule-ember);
}

/* Chapter numeral — the page reads as a book, not a feature list. */
.chapter {
  font: 500 11.5px/1 var(--mono);
  letter-spacing: .2em;
  color: var(--ink-3);
}

.h1 { font-family: var(--serif); font-size: clamp(2.6rem, 6.4vw, 4.4rem); line-height: 1.02; letter-spacing: -.03em; }
.h2 { font-family: var(--serif); font-size: clamp(1.95rem, 3.6vw, 2.9rem); letter-spacing: -.025em; }
.h3 { font-family: var(--serif); font-size: clamp(1.22rem, 1.5vw, 1.42rem); letter-spacing: -.015em; }
.h4 { font: 600 15px/1.3 var(--sans); letter-spacing: -.005em; }

.h1 em, .h2 em { font-style: italic; color: var(--ember-emph); }

.lede { font-size: clamp(1.08rem, .5vw + 1rem, 1.28rem); color: var(--ink-2); max-width: 54ch; }
.small { font-size: .86rem; color: var(--ink-2); }
.note { font: 400 13.5px/1.55 var(--sans); color: var(--ink-3); }
.mono { font-family: var(--mono); font-size: .82em; letter-spacing: .01em; }

/* Marginalia — a printed-book aside, folded inline on small screens. */
.margin-note {
  font: 400 13px/1.5 var(--sans);
  color: var(--ink-3);
  border-left: 2px solid var(--rule-ember);
  padding-left: 14px;
  margin-block: 22px;
}

/* ── Buttons ─────────────────────────────────────────────────────────────── */
.btn {
  --btn-bg: var(--ember);
  --btn-fg: var(--on-ember);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 9px;
  padding: 14px 26px;
  border: 1px solid transparent;
  border-radius: 8px;
  background: var(--btn-bg);
  color: var(--btn-fg);
  font: 600 14.5px/1 var(--sans);
  letter-spacing: -.005em;
  text-decoration: none;
  cursor: pointer;
  white-space: nowrap;
  /* A nowrap label in a flex row must never be squeezed narrower than itself,
     or the text spills outside the pill and reads as clipped. */
  flex-shrink: 0;
  transition: transform .16s var(--ease), box-shadow .16s var(--ease), background .16s var(--ease), border-color .16s var(--ease);
  box-shadow: var(--lift-1);
}
.btn:hover { color: var(--btn-fg); transform: translateY(-1px); box-shadow: var(--lift-2); }
.btn:active { transform: translateY(0); }
.btn-ghost {
  --btn-bg: transparent;
  --btn-fg: var(--ink);
  border-color: var(--rule);
  box-shadow: none;
}
.btn-ghost:hover { border-color: var(--rule-ember); background: var(--paper-card); box-shadow: none; }
.btn-sm { padding: 10px 16px; font-size: 13.5px; border-radius: 7px; }

/* ── Header ──────────────────────────────────────────────────────────────── */
.masthead {
  position: sticky;
  top: 0;
  z-index: 100;
  background: color-mix(in srgb, var(--paper) 86%, transparent);
  backdrop-filter: blur(14px) saturate(1.3);
  border-bottom: 1px solid var(--rule-soft);
}
.masthead > .wrap {
  display: flex;
  align-items: center;
  gap: clamp(14px, 3vw, 34px);
  min-height: 66px;
}
.brand {
  display: inline-flex;
  align-items: center;
  gap: 11px;
  text-decoration: none;
  color: var(--ink);
  flex-shrink: 0;
}
.brand:hover { color: var(--ink); }
.brand-mark { width: 30px; height: 30px; flex-shrink: 0; }
.brand-name {
  font: 600 18px/1 var(--serif);
  letter-spacing: .02em;
}
.brand-by {
  font: 400 10.5px/1.25 var(--mono);
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--ink-3);
  display: block;
  margin-top: 3px;
}

.nav { display: flex; align-items: center; gap: clamp(12px, 2vw, 26px); margin-left: auto; }
/* Scoped to .nav-links, never bare `.nav a` — the nav also holds the CTA, and
   `.nav a` (0,1,1) outranks `.btn` (0,1,0), which silently repainted the button
   label in body-text colour and crushed its padding to 2px. */
.nav-links a {
  font: 500 14px/1 var(--sans);
  color: var(--ink-2);
  text-decoration: none;
  padding: 6px 2px;
}
.nav-links a:hover { color: var(--ember-emph); }
.nav-links { display: flex; align-items: center; gap: clamp(12px, 2vw, 26px); }
@media (max-width: 860px) { .nav-links { display: none; } }
/* Below this the header cannot hold brand + language + theme + CTA. The hero's
   own signup sits immediately underneath, so the header CTA is the one to go. */
@media (max-width: 560px) { .masthead .btn { display: none; } }

.icon-btn {
  display: inline-grid;
  place-items: center;
  width: 36px; height: 36px;
  padding: 0;
  border: 1px solid var(--rule);
  border-radius: 8px;
  background: transparent;
  color: var(--ink-2);
  cursor: pointer;
  transition: border-color .16s var(--ease), color .16s var(--ease), background .16s var(--ease);
}
.icon-btn:hover { border-color: var(--rule-ember); color: var(--ember-emph); background: var(--paper-card); }
.icon-btn svg { width: 17px; height: 17px; }
/* One button, two glyphs: the moon shows in light mode, the sun in dark. */
:root[data-theme="dark"] .i-moon, .interior .i-moon { display: none; }
:root:not([data-theme="dark"]) .i-sun { display: none; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .i-moon { display: none; }
  :root:not([data-theme="light"]) .i-sun  { display: block; }
  :root[data-theme="light"] .i-sun { display: none; }
  :root[data-theme="light"] .i-moon { display: block; }
}

.lang {
  display: inline-flex;
  border: 1px solid var(--rule);
  border-radius: 8px;
  overflow: hidden;
}
.lang a {
  padding: 8px 11px;
  font: 500 12px/1 var(--mono);
  letter-spacing: .06em;
  color: var(--ink-3);
  text-decoration: none;
}
.lang a[aria-current="true"] { background: var(--paper-card); color: var(--ember-emph); }
.lang a:hover { color: var(--ember-emph); }

/* ── Hero ────────────────────────────────────────────────────────────────── */
.hero {
  position: relative;
  overflow: hidden;
  background: var(--paper);
  display: flex;
  align-items: center;
  min-height: 94vh;
}
.hero::before {
  content: "";
  position: absolute;
  inset: -30% -10% auto -10%;
  height: 90%;
  background: var(--backdrop);
  opacity: .34;
  filter: blur(38px);
  pointer-events: none;
}
:root[data-theme="dark"] .hero::before { opacity: .6; }
@media (prefers-color-scheme: dark) { :root:not([data-theme="light"]) .hero::before { opacity: .6; } }

.hero > .wrap {
  position: relative;
  display: grid;
  grid-template-columns: minmax(0, 1.02fr) minmax(0, .98fr);
  align-items: center;
  gap: clamp(32px, 5vw, 66px);
  padding-block: clamp(52px, 8vw, 96px) clamp(56px, 8vw, 100px);
}
@media (max-width: 940px) {
  .hero > .wrap { grid-template-columns: 1fr; }
}

.hero h1 { margin-bottom: 22px; }
.hero .lede { margin-bottom: 30px; }

.trust {
  display: flex;
  flex-wrap: wrap;
  gap: 8px 20px;
  margin: 26px 0 0;
  padding: 0;
  list-style: none;
  font: 400 12.5px/1.4 var(--mono);
  letter-spacing: .02em;
  color: var(--ink-3);
}
.trust li { display: flex; align-items: center; gap: 7px; }
.trust li::before {
  content: "";
  width: 5px; height: 5px;
  border-radius: 50%;
  background: var(--ember-soft);
  flex-shrink: 0;
}

/* ── Price cue ───────────────────────────────────────────────────────────
   The full price table is thousands of pixels down the page, which is far too
   late for the visitor whose second question is "how much". This says it in
   the hero, in one line, and jumps them there. */
.price-cue {
  display: inline-flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 6px 12px;
  margin: 22px 0 0;
  padding: 11px 16px;
  border: 1px solid var(--rule);
  border-radius: 10px;
  background: var(--paper-card);
  font: 400 13px/1.4 var(--sans);
  color: var(--ink-2);
  text-decoration: none;
  transition: border-color .16s var(--ease), transform .16s var(--ease);
}
.price-cue:hover { border-color: var(--rule-ember); color: var(--ink-2); transform: translateY(-1px); }
.price-cue b { color: var(--ink); font-weight: 600; }
.price-cue .cue-flag {
  padding: 3px 8px;
  border-radius: 999px;
  background: var(--ember);
  color: var(--on-ember);
  font: 600 11px/1.4 var(--mono);
  letter-spacing: .04em;
}
.price-cue .cue-go { color: var(--ember-emph); font-weight: 600; }

/* ── Waitlist form ───────────────────────────────────────────────────────── */
.signup { max-width: 470px; }
.signup-row { display: flex; gap: 10px; flex-wrap: wrap; }
.signup label { display: block; margin-bottom: 8px; font: 500 13px/1 var(--sans); color: var(--ink-2); }
.signup input[type="email"] {
  flex: 1 1 220px;
  min-width: 0;
  padding: 14px 16px;
  border: 1px solid var(--rule);
  border-radius: 8px;
  background: var(--paper-soft);
  color: var(--ink);
  font: 400 15.5px/1.2 var(--sans);
  transition: border-color .16s var(--ease), box-shadow .16s var(--ease);
}
.signup input[type="email"]::placeholder { color: var(--ink-3); opacity: .8; }
.signup input[type="email"]:focus-visible {
  outline: none;
  border-color: var(--ember-emph);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--ember-soft) 40%, transparent);
}
.signup input[type="email"]:user-invalid {
  border-color: var(--ember-deep);
}
.signup .btn { flex: 0 0 auto; }
.signup-note { margin: 12px 0 0; font: 400 12.5px/1.5 var(--sans); color: var(--ink-3); }
.signup-note a { color: var(--ink-2); }

/* Honeypot: a real field for a bot, off-screen for everyone else. */
.hp { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }

.form-status {
  margin: 12px 0 0;
  font: 500 13.5px/1.5 var(--sans);
  min-height: 1.5em;
}
.form-status[data-state="ok"]   { color: var(--ok); }
.form-status[data-state="err"]  { color: var(--ember-deep); }
.form-status[data-state="busy"] { color: var(--ink-3); }

/* ── App mockup ──────────────────────────────────────────────────────────── */
.mock {
  --mock-radius: 14px;
  position: relative;
  border-radius: var(--mock-radius);
  border: 1px solid var(--rule);
  background: var(--paper-soft);
  box-shadow: var(--lift-2);
  overflow: hidden;
  font-family: var(--sans);
}
.mock-bar {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 10px 14px;
  border-bottom: 1px solid var(--rule-soft);
  background: var(--paper-card);
}
.mock-dot { width: 9px; height: 9px; border-radius: 50%; background: var(--rule); }
.mock-dot:first-child { background: var(--ember-soft); }
.mock-title {
  margin-left: 6px;
  font: 400 11px/1 var(--mono);
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--ink-3);
}
.mock-body { display: grid; grid-template-columns: minmax(0, 1fr) 178px; }
@media (max-width: 520px) { .mock-body { grid-template-columns: 1fr; } }

.mock-chat { padding: 16px 16px 18px; display: grid; gap: 12px; }
.turn { display: grid; gap: 5px; }
.turn-who {
  font: 500 10.5px/1 var(--mono);
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--ink-3);
}
.turn-char .turn-who { color: var(--ember-emph); }
.turn-text {
  font: 400 14.2px/1.55 var(--serif);
  color: var(--ink);
  background: var(--paper-card);
  border: 1px solid var(--rule-soft);
  border-radius: 10px;
  padding: 10px 13px;
}
.turn-user .turn-text { background: transparent; border-style: dashed; color: var(--ink-2); }
.turn-text em { color: var(--ink-2); font-style: italic; }

/* The Mind rail — the plum interior, inside the light mockup. */
.mock-mind {
  border-left: 1px solid var(--rule);
  padding: 14px 13px;
  display: grid;
  gap: 13px;
  align-content: start;
  background: var(--plum);
  color: #faeeea;
}
/* Not a heading: the whole mock is role="img" with its own label, so this is
   caption text inside a picture. Marking it <h4> put an h1→h4 jump in the
   document outline for the sake of 10px of styling. */
.mock-mind .rail-title {
  font: 500 10px/1 var(--mono);
  letter-spacing: .16em;
  text-transform: uppercase;
  color: #e99e8e;
  margin: 0;
}
.axis { display: grid; gap: 4px; }
.axis-top {
  display: flex;
  justify-content: space-between;
  font: 400 10.5px/1 var(--mono);
  color: #d6b8ba;
}
.axis-track { height: 4px; border-radius: 2px; background: rgba(250, 238, 234, .13); overflow: hidden; }
.axis-fill { height: 100%; border-radius: 2px; background: linear-gradient(90deg, #d26c56, #f6c5b8); }
.axis-fill.cool { background: linear-gradient(90deg, #6b4a72, #b58fbd); }
.mind-note {
  font: 400 10.5px/1.45 var(--sans);
  color: #bca1a5;
  border-top: 1px solid rgba(250, 238, 234, .12);
  padding-top: 10px;
}
.mind-note b { color: #f6c5b8; font-weight: 600; }

/* ── Cards & grids ───────────────────────────────────────────────────────── */
.grid { display: grid; gap: clamp(14px, 1.6vw, 20px); }
.g2 { grid-template-columns: repeat(auto-fit, minmax(min(100%, 300px), 1fr)); }
.g3 { grid-template-columns: repeat(auto-fit, minmax(min(100%, 260px), 1fr)); }
.g4 { grid-template-columns: repeat(auto-fit, minmax(min(100%, 200px), 1fr)); }

.card {
  background: var(--paper-card);
  border: 1px solid var(--rule-soft);
  border-radius: 12px;
  padding: clamp(20px, 2.2vw, 28px);
  transition: transform .2s var(--ease), box-shadow .2s var(--ease), border-color .2s var(--ease);
}
.card > :last-child { margin-bottom: 0; }
.card-lift:hover { transform: translateY(-3px); border-color: var(--rule-ember); box-shadow: var(--lift-1); }
.card h3 { margin-bottom: 9px; }
.card p { font-size: .93rem; color: var(--ink-2); }

/* The engine features: a numbered, ruled list rather than a card soup. */
.feature {
  display: grid;
  grid-template-columns: 96px minmax(0, 1fr) minmax(0, 1fr);
  gap: clamp(20px, 3vw, 46px);
  align-items: start;
  padding-block: clamp(34px, 4.4vw, 56px);
  border-top: 1px solid var(--rule);
}
.feature:first-of-type { border-top: 0; }
@media (max-width: 900px) {
  .feature { grid-template-columns: 1fr; gap: 20px; padding-block: 34px; }
  .feature-num { order: -1; }
}
.feature-num {
  font: 500 12px/1 var(--mono);
  letter-spacing: .16em;
  color: var(--ember-emph);
  padding-top: .5em;
}
.feature-body h3 { margin-bottom: 12px; }
.feature-body p { color: var(--ink-2); font-size: .96rem; }
.feature-art { align-self: center; }

.tags { display: flex; flex-wrap: wrap; gap: 7px; margin: 16px 0 0; padding: 0; list-style: none; }
.tag {
  padding: 5px 10px;
  border: 1px solid var(--rule);
  border-radius: 999px;
  font: 400 11.5px/1.3 var(--mono);
  letter-spacing: .02em;
  color: var(--ink-3);
  background: var(--paper);
}
.tag-on { border-color: var(--rule-ember); color: var(--ember-emph); }

/* ── Scene card ──────────────────────────────────────────────────────────
   A moment out of a story, used where a diagram would have been. It shows the
   same thing a chart of the internals would — and a reader who has never
   thought about how any of this works can feel it in one line. */
.scene {
  border: 1px solid var(--rule-ember);
  border-radius: 12px;
  background: var(--paper-card);
  padding: clamp(20px, 2.2vw, 26px);
  display: grid;
  gap: 14px;
  box-shadow: var(--lift-1);
}
.scene-line {
  font: italic 400 1.06rem/1.62 var(--serif);
  color: var(--ink);
  margin: 0;
}
.scene-line b { font-style: normal; font-weight: 600; }
.scene-who {
  font: 400 11.5px/1.5 var(--mono);
  letter-spacing: .05em;
  color: var(--ink-3);
  margin: 0;
  padding-top: 12px;
  border-top: 1px dashed var(--rule);
}
.scene-who b { color: var(--ember-emph); font-weight: 500; }

/* ── Price recall ────────────────────────────────────────────────────────
   The table now sits high on the page, which means a visitor reaches the
   arguments that justify it AFTER seeing the number. This strip closes that
   loop: it sits at the end of the section that earns the price and sends them
   back up. Same job as the plan strip a pro site puts near its footer. */
.recall {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 18px 26px;
  margin-top: clamp(28px, 3.4vw, 44px);
  padding: clamp(20px, 2.4vw, 28px);
  border: 1px solid var(--rule-ember);
  border-radius: 12px;
  background: var(--paper-card);
}
.recall h3 { margin: 0; }
.recall p { margin: 8px 0 0; }

/* ── Compare (the money section) ─────────────────────────────────────────── */
.compare { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 320px), 1fr)); gap: 18px; }
.compare-col {
  border: 1px solid var(--rule);
  border-radius: 12px;
  padding: clamp(20px, 2.2vw, 28px);
  background: var(--paper);
  display: grid;
  gap: 14px;
  align-content: start;
}
.compare-col.win { border-color: var(--rule-ember); box-shadow: var(--lift-1); background: var(--paper-card); }
.compare-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  font: 500 11px/1 var(--mono);
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--ink-3);
}
.compare-col.win .compare-head { color: var(--ember-emph); }

/* Proof pin — the stage and card exist on every engine; only the pinning,
   the runway and the timeline live inside the motion fence below, so engines
   without scroll timelines get the section as an ordinary block. */
.pin-card { padding-block: clamp(64px, 9vw, 116px); }
.compare-line {
  font: 400 1rem/1.6 var(--serif);
  color: var(--ink);
  margin: 0;
}
.compare-line em { color: var(--ink-2); }
.compare-why {
  font: 400 12.5px/1.55 var(--sans);
  color: var(--ink-3);
  border-top: 1px dashed var(--rule);
  padding-top: 12px;
  margin: 0;
}

/* ── Pricing ─────────────────────────────────────────────────────────────── */
.plans { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 235px), 1fr)); gap: 16px; align-items: stretch; }
.plan {
  position: relative;
  display: grid;
  align-content: start;
  gap: 14px;
  padding: 26px 22px;
  border: 1px solid var(--rule);
  border-radius: 12px;
  background: var(--paper);
}
.plan-best {
  border-color: var(--ember);
  background: var(--paper-card);
  box-shadow: var(--lift-1);
}
.plan-flag {
  position: absolute;
  top: -11px; left: 22px;
  padding: 4px 10px;
  border-radius: 999px;
  background: var(--ember);
  color: var(--on-ember);
  font: 600 10.5px/1.4 var(--mono);
  letter-spacing: .1em;
  text-transform: uppercase;
}
.plan-name { font: 600 1.28rem/1 var(--serif); letter-spacing: -.01em; }
.plan-claim { font: 400 13px/1.4 var(--sans); color: var(--ember-emph); margin: -6px 0 0; }
.plan-price { display: flex; align-items: baseline; gap: 6px; }
.plan-amount { font: 600 2.05rem/1 var(--serif); letter-spacing: -.03em; }
.plan-per { font: 400 12.5px/1 var(--mono); color: var(--ink-3); }
.plan ul { list-style: none; display: grid; gap: 8px; font: 400 13.5px/1.45 var(--sans); color: var(--ink-2); }
/* Bullet is absolutely positioned, NOT a grid/flex column: these items contain
   inline <strong>, and every element child of a grid item becomes its own grid
   item — which scatters "<strong>Layered memory</strong> — episodic, world"
   across separate cells. Absolute positioning keeps the text one inline flow. */
.plan li { position: relative; padding-left: 22px; }
.plan li::before {
  content: "";
  position: absolute;
  left: 0; top: .42em;
  width: 7px; height: 7px;
  border-radius: 50%;
  border: 1.5px solid var(--ember-soft);
}
.plan li.off { color: var(--ink-3); opacity: .72; }
.plan li.off::before { border-color: var(--rule); background: var(--rule-soft); }
.plan-foot { margin-top: 4px; font: 400 11.5px/1.45 var(--mono); color: var(--ink-3); }

.seats {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  padding: 7px 12px;
  border: 1px solid var(--rule-ember);
  border-radius: 999px;
  font: 500 11.5px/1 var(--mono);
  letter-spacing: .04em;
  white-space: nowrap;
  color: var(--ember-emph);
  background: var(--paper);
}
.seats::before {
  content: "";
  width: 6px; height: 6px;
  border-radius: 50%;
  background: var(--ember);
}
@media (prefers-reduced-motion: no-preference) {
  .seats::before { animation: pulse 2.6s var(--ease) infinite; }
}
@keyframes pulse {
  0%, 100% { opacity: 1; transform: scale(1); }
  50%      { opacity: .45; transform: scale(.8); }
}

/* ── Spec table ──────────────────────────────────────────────────────────── */
.specs { display: grid; gap: 0; }
.spec {
  display: grid;
  grid-template-columns: minmax(150px, .34fr) minmax(0, 1fr);
  gap: 14px 26px;
  padding-block: 15px;
  border-top: 1px solid var(--rule-soft);
  align-items: baseline;
}
.spec:first-child { border-top: 0; }
.spec dt {
  font: 500 11.5px/1.4 var(--mono);
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--ink-3);
  margin: 0;
}
.spec dd { margin: 0; font-size: .94rem; color: var(--ink-2); }
.spec dd b { color: var(--ink); font-weight: 600; }
@media (max-width: 620px) { .spec { grid-template-columns: 1fr; gap: 5px; } }

/* ── FAQ (native disclosure, zero JS) ────────────────────────────────────── */
.faq { max-width: 74ch; }
.faq details {
  border-top: 1px solid var(--rule);
  padding: 0;
}
.faq details:last-of-type { border-bottom: 1px solid var(--rule); }
.faq summary {
  display: flex;
  align-items: center;
  gap: 16px;
  padding: 20px 2px;
  cursor: pointer;
  font: 600 1.03rem/1.4 var(--serif);
  color: var(--ink);
  list-style: none;
}
.faq summary::-webkit-details-marker { display: none; }
.faq summary::after {
  content: "";
  margin-left: auto;
  width: 11px; height: 11px;
  border-right: 1.5px solid var(--ember-emph);
  border-bottom: 1.5px solid var(--ember-emph);
  transform: rotate(45deg) translate(-2px, -2px);
  transition: transform .2s var(--ease);
  flex-shrink: 0;
}
.faq details[open] summary::after { transform: rotate(-135deg) translate(-2px, -2px); }
.faq summary:hover { color: var(--ember-emph); }
.faq .answer { padding: 0 2px 22px; color: var(--ink-2); font-size: .96rem; max-width: 66ch; }

/* ── Final call ──────────────────────────────────────────────────────────── */
.finale { text-align: center; }
.finale .signup { margin-inline: auto; text-align: left; }
.finale-mark { width: 54px; height: 54px; margin: 0 auto 24px; }

/* ── Footer ──────────────────────────────────────────────────────────────── */
.foot { background: var(--paper-soft); border-top: 1px solid var(--rule); }
.foot > .wrap { padding-block: clamp(40px, 5vw, 62px) 34px; }
.foot-grid {
  display: grid;
  grid-template-columns: minmax(220px, 1.4fr) repeat(auto-fit, minmax(130px, 1fr));
  gap: 30px 26px;
}
/* The explicit 220px first track sits outside the auto-fit repeat, so auto-fit
   cannot shed a column to make room for it: 220 + 130 + gap outgrows a phone
   and drags the whole document sideways. Below this width every track joins the
   repeat, which then picks its own column count. */
@media (max-width: 720px) {
  .foot-grid { grid-template-columns: repeat(auto-fit, minmax(min(100%, 140px), 1fr)); }
}
.foot h2 { font: 500 11px/1 var(--mono); letter-spacing: .16em; text-transform: uppercase; color: var(--ink-3); margin: 0 0 14px; }
.foot ul { list-style: none; display: grid; gap: 9px; }
.foot a { font: 400 14px/1.4 var(--sans); color: var(--ink-2); text-decoration: none; }
.foot a:hover { color: var(--ember-emph); text-decoration: underline; }
.foot-legal {
  margin-top: 34px;
  padding-top: 22px;
  border-top: 1px solid var(--rule);
  display: flex;
  flex-wrap: wrap;
  gap: 10px 24px;
  align-items: center;
  font: 400 12.5px/1.5 var(--sans);
  color: var(--ink-3);
}
.foot-legal p { margin: 0; }

/* ── Long-form pages (privacy, legal) ────────────────────────────────────── */
.doc { max-width: 70ch; }
.doc h2 { font-size: clamp(1.3rem, 2vw, 1.6rem); margin: 2.2em 0 .6em; }
.doc h2:first-of-type { margin-top: 1.4em; }
.doc ul { list-style: none; display: grid; gap: 9px; margin: 0 0 1.05em; }
.doc ul li { position: relative; padding-left: 26px; }
.doc ul li::before { content: "—"; position: absolute; left: 0; color: var(--ember-emph); }
.doc dl { margin: 0 0 1.05em; }

/* ── Reveal on scroll — progressive enhancement only ─────────────────────── */
@media (prefers-reduced-motion: no-preference) {
  .js .reveal {
    opacity: 0;
    transform: translateY(14px);
    transition: opacity .6s var(--ease), transform .6s var(--ease);
  }
  .js .reveal.seen { opacity: 1; transform: none; }

  /* Hero comes in on load, staggered, without waiting for the observer. */
  .js .hero .stagger > * {
    opacity: 0;
    animation: rise .7s var(--ease) forwards;
  }
  .js .hero .stagger > *:nth-child(1) { animation-delay: .05s; }
  .js .hero .stagger > *:nth-child(2) { animation-delay: .13s; }
  .js .hero .stagger > *:nth-child(3) { animation-delay: .21s; }
  .js .hero .stagger > *:nth-child(4) { animation-delay: .29s; }
  .js .hero .stagger > *:nth-child(5) { animation-delay: .37s; }
  .js .hero .stagger > *:nth-child(6) { animation-delay: .45s; }
  .js .hero .mock { opacity: 0; animation: rise .8s var(--ease) .22s forwards; }
}
@keyframes rise {
  from { opacity: 0; transform: translateY(16px); }
  to   { opacity: 1; transform: none; }
}

/* ═══════════════════════════════════════════════════════════════════════════
   MOTION — parallax and scroll choreography, entirely in CSS.

   Every animation below is driven by `animation-timeline: scroll()` or
   `view()`. That matters for three reasons, and they are the reasons there is
   no library here:

     · It runs off the main thread. A scroll event listener recalculating
       transforms is the classic way to make a long page feel cheap on a phone;
       this cannot jank, because the browser is doing the interpolation itself.
     · It is reversible and position-accurate. Scroll back up and it plays back.
     · It costs nothing to ship. No GSAP, no Lenis, no AOS, no 40KB of
       scrolljacking that fights the reader's own scroll.

   The whole block is fenced twice on purpose:

     @media (prefers-reduced-motion: no-preference)  — someone who gets sick
        from parallax sees a completely static page, not a politer parallax.
     @supports (animation-timeline: view())          — older engines ignore
        scroll timelines but would happily run the same keyframes on a wall
        clock, so the progress bar would fill in one second and the charts
        would draw themselves before you reached them. Gating on support means
        those browsers get the plain page, which is a fine page.

   Initial states (a hidden stroke, a bar at scaleX(0)) live INSIDE the gate,
   never outside it. Anything else risks a browser that paints the "before"
   state and never animates out of it — content invisible, permanently.
   ═══════════════════════════════════════════════════════════════════════════ */
@media (prefers-reduced-motion: no-preference) {
  @supports (animation-timeline: view()) {

    /* ── Reading progress ────────────────────────────────────────────────
       On the bottom edge of the sticky masthead, so it needs no markup and
       exists on every page. Long pages ask for patience; showing how much is
       left is the cheapest way to earn it. */
    .masthead::after {
      content: "";
      position: absolute;
      left: 0;
      bottom: -1px;
      height: 2px;
      width: 100%;
      transform: scaleX(0);
      transform-origin: 0 50%;
      background: linear-gradient(90deg, var(--ember-soft), var(--ember));
      animation: progress linear both;
      animation-timeline: scroll(root block);
    }
    @keyframes progress { to { transform: scaleX(1); } }

    /* ── Hero parallax ───────────────────────────────────────────────────
       Two planes at different rates. The wash sinks and fades; the app window
       rises against it. Deliberately small numbers — the point is depth, not
       a fairground ride, and a hero that slides half a screen is a hero you
       have to wait for. */
    .hero::before {
      animation: wash-sink linear both;
      animation-timeline: scroll(root block);
      animation-range: 0 100vh;
    }
    @keyframes wash-sink {
      to { transform: translateY(14%) scale(1.12); opacity: .1; }
    }

    .js .hero .mock,
    .hero .mock {
      animation: rise .8s var(--ease) .22s both, mock-lift linear both;
      animation-timeline: auto, scroll(root block);
      animation-range: normal, 0 100vh;
    }
    @keyframes mock-lift { to { translate: 0 -46px; } }

    /* ── Section reveals ─────────────────────────────────────────────────
       Replaces the IntersectionObserver class toggle where supported (site.js
       skips the observer then). Tied to scroll position rather than fired
       once, so scrubbing back up replays it instead of leaving a gap. */
    .reveal {
      animation: rise-in linear both;
      animation-timeline: view();
      animation-range: entry 0% cover 72%;
    }
    @keyframes rise-in {
      from { opacity: 0; transform: translateY(26px); }
      to   { opacity: 1; transform: none; }
    }

    /* ── The charts draw themselves ──────────────────────────────────────
       The one flourish worth the most on this page: the trust curve falling
       and the memory thread crossing 300 messages are arguments, and watching
       them happen lands harder than finding them already finished.

       Each path carries pathLength="1", so one rule drives every chart no
       matter its real geometry. */
    .draw {
      stroke-dasharray: 1;
      stroke-dashoffset: 1;
      animation: draw linear both;
      animation-timeline: view();
      animation-range: entry 12% cover 80%;
    }
    @keyframes draw { to { stroke-dashoffset: 0; } }

    /* A dashed path cannot be drawn this way without losing its dashes, so
       the off-screen plan travels along its arc instead. */
    .travel {
      stroke-dashoffset: 60;
      animation: travel linear both;
      animation-timeline: view();
      animation-range: entry 12% cover 80%;
    }
    @keyframes travel { to { stroke-dashoffset: 0; } }

    /* Annotations arrive after the line they annotate. */
    .fade-late {
      opacity: 0;
      animation: fade-late linear both;
      animation-timeline: view();
      animation-range: entry 70% cover 60%;
    }
    @keyframes fade-late { to { opacity: 1; } }

    /* ── Plan cards ──────────────────────────────────────────────────────
       Staggered by shifting each card's range rather than by animation-delay:
       a delay is measured in seconds and means nothing on a scroll timeline.
       Entry and exit are both offset, so the cascade reads the whole way
       through the section, not just in its last few pixels. */
    .plan {
      animation: card-in linear both;
      animation-timeline: view();
      animation-range: entry 2% cover 60%;
    }
    .plan:nth-child(2) { animation-range: entry 6% cover 65%; }
    .plan:nth-child(3) { animation-range: entry 10% cover 70%; }
    .plan:nth-child(4) { animation-range: entry 14% cover 75%; }
    @keyframes card-in {
      from { opacity: 0; transform: translateY(30px) scale(.985); }
      to   { opacity: 1; transform: none; }
    }

    /* ── Scene cards ─────────────────────────────────────────────────────── */
    .scene {
      animation: scene-in linear both;
      animation-timeline: view();
      animation-range: entry 5% cover 65%;
    }

    /* ── The proof holds still ─────────────────────────────────────────────
       The one pinned moment on the page: the section holds while the two
       messages arrive in sequence. The stage is tall, the card sticky; the
       animations ride the stage's own view timeline, so nothing here needs
       JavaScript and nothing fights the reader's scroll — the page still
       moves one scroll notch per notch, it simply has more to show. */
    @media (min-width: 941px) {
      .pin-stage {
        height: 230vh;
        view-timeline-name: --pin;
        view-timeline-axis: block;
      }
      .pin-card {
        position: sticky;
        top: 96px;
        height: calc(100vh - 96px);
        display: flex;
        flex-direction: column;
        justify-content: center;
      }
      /* cover 22%, not 15%: WebKit 26 mis-resolves small cover ends on named
         view timelines (the animation stays frozen at 0). 22% renders fine
         in every engine and lands visually in the same place. */
      .pin-card .center {
        animation: pin-in linear both;
        animation-timeline: --pin;
        animation-range: entry 55% cover 22%;
      }
      .pin-card .compare-col {
        animation: pin-in linear both;
        animation-timeline: --pin;
        animation-range: entry 78% cover 38%;
      }
      .pin-card .compare-col.win {
        animation-range: cover 10% cover 62%;
      }
      .pin-card .note {
        animation: pin-in linear both;
        animation-timeline: --pin;
        animation-range: cover 30% cover 75%;
      }
      @keyframes pin-in {
        from { opacity: 0; transform: translateY(28px); }
        to   { opacity: 1; transform: none; }
      }
    }
    @keyframes scene-in {
      from { opacity: 0; transform: translateY(20px); }
      to   { opacity: 1; transform: none; }
    }

    /* ── Chapter numerals hold their place ───────────────────────────────
       The numeral pins while its feature scrolls past — a printed-page habit,
       and it keeps the reader oriented in a section with seven entries.
       Only where the three-column feature layout exists. */
    @media (min-width: 901px) {
      .feature-num {
        position: sticky;
        top: 96px;
      }
    }
  }

  /* The mock's bars fill on load rather than on view: they sit above the fold,
     where a view() timeline is already past its range and would show them
     finished. Width stays inline; scaleX is what animates. */
  .axis-fill {
    transform-origin: 0 50%;
    animation: bar-fill .9s var(--ease) .55s both;
  }
  @keyframes bar-fill {
    from { transform: scaleX(0); }
    to   { transform: scaleX(1); }
  }
}

@media print {
  .masthead, .signup, .skip, body::after { display: none !important; }
  body { background: #fff; color: #000; }
}
