/* Airtype: the download page.
   Palette and type are lifted from Sources/AirType/Theme.swift so the site and
   the app read as the same product. */

@font-face {
  font-family: "Instrument Sans";
  src: url("assets/fonts/InstrumentSans.woff2") format("woff2-variations");
  font-weight: 400 700;
  font-display: swap;
}
@font-face {
  font-family: "Cormorant Garamond";
  src: url("assets/fonts/CormorantGaramond.woff2") format("woff2-variations");
  font-weight: 300 700;
  font-display: swap;
}

:root {
  --ink: #121a12;
  --secondary: #4f5e4f;
  --tertiary: #798a79;
  --accent: #5c7a5c;
  --accent-ink: #ffffff;
  --live: #33b859;
  --surface: #ffffff;
  --canvas: #f5f7f5;
  --hairline: rgba(23, 28, 33, 0.09);
  --wash: rgba(92, 122, 92, 0.09);
  --shadow: 0 1px 2px rgba(18, 26, 18, 0.04), 0 8px 28px rgba(18, 26, 18, 0.06);
  --shadow-lift: 0 2px 4px rgba(18, 26, 18, 0.05), 0 18px 48px rgba(18, 26, 18, 0.1);

  --sans: "Instrument Sans", ui-sans-serif, -apple-system, "Helvetica Neue", sans-serif;
  --serif: "Cormorant Garamond", ui-serif, Georgia, serif;

  --gutter: clamp(20px, 5vw, 48px);
  --measure: 1080px;
  --radius: 12px;
  --radius-lg: 22px;
}

@media (prefers-color-scheme: dark) {
  :root {
    --ink: #f5f5f4;
    --secondary: #a3a7a0;
    --tertiary: #8b908a;
    --accent: #93b193;
    --accent-ink: #10160f;
    --surface: #2e2f2e;
    --canvas: #1e201e;
    --hairline: rgba(235, 237, 232, 0.1);
    --wash: rgba(147, 177, 147, 0.12);
    --shadow: 0 1px 2px rgba(0, 0, 0, 0.3), 0 8px 28px rgba(0, 0, 0, 0.28);
    --shadow-lift: 0 2px 4px rgba(0, 0, 0, 0.35), 0 18px 48px rgba(0, 0, 0, 0.4);
  }
}

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

html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }

body {
  margin: 0;
  background: var(--canvas);
  color: var(--ink);
  font-family: var(--sans);
  font-size: 16px;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

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

a { color: inherit; }

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

/* ---------------------------------------------------------------- headings */

h1, h2, h3 {
  font-family: var(--serif);
  font-weight: 700;
  line-height: 1.05;
  letter-spacing: -0.01em;
  margin: 0;
}

h1 { font-size: clamp(44px, 7.5vw, 86px); }
h2 { font-size: clamp(30px, 4.4vw, 46px); }
h3 { font-size: clamp(21px, 2.4vw, 25px); line-height: 1.15; }

.eyebrow {
  font-family: var(--sans);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--accent);
  margin: 0 0 20px;
}

p { margin: 0 0 16px; color: var(--secondary); }
p:last-child { margin-bottom: 0; }

.lede {
  font-size: clamp(17px, 1.9vw, 20px);
  line-height: 1.55;
  max-width: 56ch;
}

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

.hero-band {
  position: relative;
  height: clamp(200px, 30vw, 330px);
  overflow: hidden;
  background: #cfd9e4;
}
.hero-band img {
  width: 100%; height: 100%;
  object-fit: cover;
  object-position: center 58%;
}
/* The painting fades into the page rather than stopping at an edge. */
.hero-band::after {
  content: "";
  position: absolute; inset: 0;
  background: linear-gradient(to bottom, transparent 45%, var(--canvas) 99%);
}

.hero {
  padding: clamp(40px, 6vw, 72px) 0 clamp(56px, 8vw, 96px);
  text-align: center;
}
.hero h1 { margin-bottom: 24px; }
.hero h1 .quiet { color: var(--tertiary); }
.hero .lede { margin-inline: auto; }

.cta {
  margin-top: 36px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 14px;
}

.button {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  background: var(--accent);
  color: var(--accent-ink);
  font-family: var(--sans);
  font-size: 16px;
  font-weight: 600;
  text-decoration: none;
  padding: 15px 30px;
  border-radius: 999px;
  box-shadow: var(--shadow);
  transition: transform 0.18s ease, box-shadow 0.18s ease, filter 0.18s ease;
}
.button:hover { transform: translateY(-2px); box-shadow: var(--shadow-lift); filter: brightness(1.06); }
.button:active { transform: translateY(0); }
.button svg { width: 17px; height: 17px; }

.meta {
  font-size: 13px;
  color: var(--tertiary);
  margin: 0;
}
.meta b { font-weight: 600; color: var(--secondary); }

/* The terms sit under the button, never on day fourteen. The long form wraps
   to three lines below about 560px, so a shorter one that still says both
   things takes over. */
.terms-short { display: none; }
@media (max-width: 560px) {
  .terms-long { display: none; }
  .terms-short { display: block; margin-top: 4px; }
}

/* -------------------------------------------------------------------- demo */

.demo-section { padding-bottom: clamp(56px, 8vw, 100px); }

.demo {
  position: relative;
  max-width: 760px;
  margin-inline: auto;
}

.win {
  background: var(--surface);
  border: 1px solid var(--hairline);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lift);
  overflow: hidden;
}

.titlebar {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 13px 16px;
  border-bottom: 1px solid var(--hairline);
  background: var(--wash);
}
.dot { width: 11px; height: 11px; border-radius: 50%; }
.dot.r { background: #ff5f57; }
.dot.a { background: #febc2e; }
.dot.g { background: #28c840; }
.titlebar span {
  margin-left: auto; margin-right: auto;
  padding-right: 45px;
  font-size: 12px; font-weight: 500;
  color: var(--tertiary);
}

.win-body { padding: 20px clamp(18px, 3vw, 28px) 30px; }

.field-row {
  display: flex;
  gap: 10px;
  font-size: 13px;
  padding-bottom: 11px;
  border-bottom: 1px solid var(--hairline);
  color: var(--tertiary);
}
.field-row b { font-weight: 500; color: var(--tertiary); min-width: 58px; }
.field-row span { color: var(--secondary); }

/* The two states stack rather than sit side by side, so the one that is
   invisible does not push the other around. */
.compose {
  position: relative;
  margin: 22px 0 0;
  min-height: 3.6em;
  font-size: clamp(15px, 1.9vw, 17px);
  line-height: 1.7;
  color: var(--ink);
}
.compose > p { margin: 0; }
.compose .placeholder {
  position: absolute;
  inset: 0 0 auto 0;
  color: var(--tertiary);
}
.pasted { opacity: 0; color: var(--ink); }
.caret {
  display: inline-block;
  width: 1.5px; height: 1.05em;
  background: var(--accent);
  vertical-align: text-bottom;
  margin-left: 2px;
}
.pasted .caret { margin-left: 3px; }

.stage { position: relative; }

/* The floating HUD pill, matched to HUDView in Sources/AirType/HUD.swift.
   Every measurement is the Figma value multiplied by 1.2, which is exactly
   what HUDMetrics.s() does, so these numbers can be read straight off the
   frame: a 120x30 capsule, a 10pt dot, eleven 3pt bars 2pt apart across 53pt,
   and 10pt semibold text.

   Two things are deliberately not symmetrical between the modes, and both
   come from the Swift: light has no border at all, because the design carries
   the pill on its shadow alone, and dark adds a rim because without one the
   pill dissolves into a dark window behind it. */
:root {
  --pill-bg: rgba(255, 255, 255, 0.95);
  --pill-border: transparent;
  --pill-ink: #121a12;
  --pill-bar: #4f5e4f;
  --pill-glyph: #5c7a5c;
  --pill-shadow: 0 9.6px 28.8px rgba(0, 0, 0, 0.2);
}
@media (prefers-color-scheme: dark) {
  :root {
    --pill-bg: rgba(46, 47, 46, 0.96);
    --pill-border: rgba(255, 255, 255, 0.1);
    --pill-ink: #f5f5f4;
    --pill-bar: #a3a7a0;
    --pill-glyph: #5c7a5c;
    --pill-shadow: 0 9.6px 28.8px rgba(0, 0, 0, 0.4);
  }
}

.pill {
  position: absolute;
  left: 50%;
  bottom: -26px;
  transform: translate(-50%, 12px);
  opacity: 0;
  width: 144px;
  height: 36px;
  border-radius: 999px;
  background: var(--pill-bg);
  border: 1px solid var(--pill-border);
  box-shadow: var(--pill-shadow);
  white-space: nowrap;
}

/* Both faces fill the pill and cross-fade in place, so the shape never
   resizes between states. */
.pill-face {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
}
.pill-face.listening { gap: 7.2px; }
.pill-face.transcribing { gap: 4.8px; opacity: 0; }
.pill-face.transcribing span {
  font-size: 12px;
  font-weight: 600;
  color: var(--pill-ink);
}
/* A three-quarter arc, 10x10 in a 16x16 box, turning once every 0.9s. */
.pill-face.transcribing svg {
  width: 16px; height: 16px;
  fill: none;
  color: var(--pill-glyph);
  animation: spin 0.9s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }

.rec-dot {
  flex: none;
  width: 12px; height: 12px;
  border-radius: 50%;
  background: #e53836;
}

.pill-time {
  flex: none;
  font-size: 12px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  color: var(--pill-ink);
}

/* Eleven bars, the count HUDModel.levels keeps. 11 x 3.6 + 10 x 2.4 = 63.6,
   which is the 53pt meter width in the frame, scaled. */
.meter {
  flex: none;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 2.4px;
  width: 63.6px;
  height: 24px;
}
.meter i {
  display: block;
  width: 3.6px;
  height: 24px;
  border-radius: 2.4px;
  background: var(--pill-bar);
  transform: scaleY(0.1);
  transform-origin: center;
}

.said {
  margin: 56px auto 0;
  max-width: 60ch;
  text-align: center;
  font-size: 14px;
  color: var(--tertiary);
  opacity: 0;
}
.said em { font-style: italic; color: var(--secondary); }

/* The loop. Nine seconds: silence, listening, transcribing, paste, hold. */
@keyframes pill-in {
  0%, 6%    { opacity: 0; transform: translate(-50%, 14px); }
  12%, 66%  { opacity: 1; transform: translate(-50%, 0); }
  74%, 100% { opacity: 0; transform: translate(-50%, 14px); }
}
@keyframes bar {
  from { transform: scaleY(0.1); }
  to   { transform: scaleY(1); }
}
/* The pill swells with the loudest recent sound, but only while it is
   listening, which is why this is on the same 9s clock rather than its own.
   `scale` is its own property, so it composes with the translate that brings
   the pill in without either fighting the other. */
@keyframes pill-swell {
  0%, 12%   { scale: 1; }
  17%       { scale: 1.035; }
  23%       { scale: 1; }
  30%       { scale: 1.04; }
  36%       { scale: 1; }
  43%       { scale: 1.028; }
  49%, 100% { scale: 1; }
}
@keyframes face-listening {
  0%, 53%   { opacity: 1; }
  57%, 100% { opacity: 0; }
}
@keyframes face-transcribing {
  0%, 53%   { opacity: 0; }
  57%, 100% { opacity: 1; }
}
/* Opacity only. The redraw dropped the glow the dot used to carry. */
@keyframes dot-breathe {
  from { opacity: 0.55; }
  to   { opacity: 1; }
}
@keyframes paste-in {
  0%, 62%   { opacity: 0; transform: translateY(4px); }
  70%, 100% { opacity: 1; transform: translateY(0); }
}
@keyframes placeholder-out {
  0%, 60%   { opacity: 1; }
  66%, 100% { opacity: 0; }
}
@keyframes said-in {
  0%, 8%    { opacity: 0; }
  16%, 58%  { opacity: 1; }
  68%, 100% { opacity: 0; }
}
@keyframes caret-blink {
  0%, 49%   { opacity: 1; }
  50%, 100% { opacity: 0; }
}
/* Four seconds of timecode, stepped through a stacked column of digits. */
@keyframes reel {
  0%, 12%   { transform: translateY(0); }
  22%       { transform: translateY(-1.6em); }
  34%       { transform: translateY(-3.2em); }
  46%, 100% { transform: translateY(-4.8em); }
}
.demo .pill {
  animation: pill-in 9s cubic-bezier(0.32, 0.72, 0, 1) infinite,
             pill-swell 9s ease-in-out infinite;
}
.demo .pill-face.listening { animation: face-listening 9s ease infinite; }
.demo .pill-face.transcribing { animation: face-transcribing 9s ease infinite; }
.demo .rec-dot { animation: dot-breathe 0.85s ease-in-out infinite alternate; }

/* Each bar carries its own amplitude (--a) and its own phase (--i), so the
   level travels across the meter instead of every bar snapping in unison,
   the same idea as the staggered springs in HUDView. The amplitudes are the
   app's own curve, pow(ratio, 1.35), which is what gives the meter its
   spread: a flat row of bars is the thing that exponent exists to prevent. */
.demo .meter i {
  height: calc(24px * var(--a));
  animation: bar 1.05s ease-in-out infinite alternate;
  animation-delay: calc(var(--i) * -0.115s);
}
.demo .meter i:nth-child(3n)   { animation-duration: 0.86s; }
.demo .meter i:nth-child(3n+1) { animation-duration: 1.28s; }

.demo .pasted { animation: paste-in 9s cubic-bezier(0.32, 0.72, 0, 1) infinite; }
.demo .placeholder { animation: placeholder-out 9s ease infinite; }
.demo .said { animation: said-in 9s ease infinite; }
.demo .caret { animation: caret-blink 1.1s steps(1) infinite; }

.reel { display: inline-block; height: 1.6em; overflow: hidden; vertical-align: bottom; }
.reel > span { display: block; height: 1.6em; line-height: 1.6em; }
.pill-time .reel > span { animation: reel 9s cubic-bezier(0.4, 0, 0.2, 1) infinite; }

/* ---------------------------------------------------------------- sections */

section { padding: clamp(56px, 8vw, 104px) 0; }
section + section { border-top: 1px solid var(--hairline); }

/* The measure belongs on the paragraph, not the block. A `ch` cap here is
   computed against this element's 16px sans, about 534px, which silently
   wrapped a 46px display heading at two thirds of the page. */
.section-head {
  margin-bottom: clamp(36px, 5vw, 56px);
  text-align: center;
}
.section-head h2 { margin-bottom: 16px; }
.section-head .lede { max-width: 58ch; margin-inline: auto; }

/* ---------------------------------------------------------------- features */

/* No cards. A card has a surface, a border and a shadow, and all three say
   "press me", and these are statements, not controls. The only thing separating
   one feature from the next is a hairline, and on a narrow screen not even
   that. Column counts are fixed per breakpoint rather than auto-fit, because
   the dividers below depend on knowing which cell ends a row. */

.features {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
}

.feature {
  padding: clamp(28px, 3.4vw, 44px) clamp(18px, 2.4vw, 32px);
  text-align: center;
  border-left: 1px solid var(--hairline);
  border-top: 1px solid var(--hairline);
}
.feature:nth-child(3n + 1) { border-left: 0; }
.feature:nth-child(-n + 3) { border-top: 0; }

.feature h3 {
  margin-bottom: 10px;
  font-size: clamp(19px, 2vw, 23px);
}
.feature p {
  font-size: 14.5px;
  line-height: 1.62;
  margin: 0 auto;
  max-width: 34ch;
}

@media (max-width: 980px) {
  .features { grid-template-columns: repeat(2, 1fr); }
  .feature:nth-child(n)      { border-left: 1px solid var(--hairline); border-top: 1px solid var(--hairline); }
  .feature:nth-child(2n + 1) { border-left: 0; }
  .feature:nth-child(-n + 2) { border-top: 0; }
}

@media (max-width: 600px) {
  .features { grid-template-columns: 1fr; }
  .feature:nth-child(n) { border-left: 0; border-top: 1px solid var(--hairline); }
  .feature:first-child  { border-top: 0; }
}

/* ─────────────────────────────────────────────────────── feature glyphs ──
   Each card opens with a line drawing that animates the point it is making.
   Deliberately glyphs rather than miniature interfaces: a small mock window
   with fields and chips invites a click it cannot answer. These are drawings,
   and read as drawings.

   Every path carries pathLength="100", so a stroke can be drawn on with
   dashoffset without anyone measuring its real length. */

/* The drawing sits on the page, not in a tinted band. No background, no
   border, no container of any kind. */
.glyph {
  display: grid;
  place-items: center;
  margin-bottom: clamp(18px, 2vw, 26px);
}
.glyph svg {
  width: 100%;
  max-width: 190px;
  height: auto;
  overflow: visible;
  stroke: var(--accent);
  stroke-width: 2.2;
}
/* Only the caret and the meter bars are solid; every other rect is an
   outline, so this must not be widened to `rect`. */
.glyph .wave rect { fill: var(--accent); stroke: none; }
.glyph .beam { stroke: var(--accent); }
.glyph .ln, .glyph .dash, .glyph .cloud { stroke: var(--secondary); opacity: 0.55; }

/* Paths that draw themselves in. */
.g-lands .l2, .g-meant .messy, .g-meant .arrow, .g-meant .clean,
.g-learn .word, .g-learn .squiggle, .g-learn .settled, .g-learn .tick,
.g-short .stub, .g-short .out {
  stroke-dasharray: 100;
  stroke-dashoffset: 100;
}

.g-local .probe { fill: var(--accent); stroke: none; opacity: 0; }
.g-local .cross { stroke: #c0554f; opacity: 0; }
.g-local .dash { stroke-width: 2; }

.g-meant .messy { stroke: var(--secondary); opacity: 0.5; }
.g-meant .arrow { stroke: var(--tertiary); }

.g-tone .ring { stroke: var(--accent); stroke-width: 2; opacity: 0.9; }
.g-tone .gl { stroke: var(--secondary); opacity: 0.4; }

.g-learn .squiggle { stroke: #c0554f; }
.g-learn .settled { stroke: var(--accent); opacity: 0; }
.g-learn .tick { stroke: var(--accent); }

.g-short .bolt { fill: var(--accent); stroke: none; opacity: 0; }
.g-short .out { stroke: var(--secondary); opacity: 0.65; }

/* ── glyph motion ───────────────────────────────────────────────────────
   Six loops on six different clocks, so a grid of cards never pulses in
   unison. Nothing runs until its card has been scrolled into view. */

.glyph * { animation-play-state: paused; }
.feature.seen .glyph *,
.capture .reveal.seen .glyph * { animation-play-state: running; }

@keyframes draw   { to { stroke-dashoffset: 0; } }
@keyframes undraw { to { stroke-dashoffset: -100; } }

/* 1 ── a line of text typing itself, the caret keeping pace */
@keyframes type-line {
  0%, 6%    { stroke-dashoffset: 100; }
  46%, 88%  { stroke-dashoffset: 0; }
  97%, 100% { stroke-dashoffset: 100; }
}
@keyframes type-caret {
  0%, 6%    { transform: translateX(0); }
  46%, 88%  { transform: translateX(74px); }
  97%, 100% { transform: translateX(0); }
}
@keyframes caret-flicker { 50% { opacity: 0.25; } }
.g-lands .l2  { animation: type-line 6s cubic-bezier(0.5, 0, 0.2, 1) infinite; }
.g-lands .beam {
  animation: type-caret 6s cubic-bezier(0.5, 0, 0.2, 1) infinite,
             caret-flicker 1s steps(1) infinite;
}

/* 2 ── sound goes in, and the one thing that tries to go out is turned back */
@keyframes wave-bar {
  from { transform: scaleY(0.22); }
  to   { transform: scaleY(1); }
}
@keyframes probe-run {
  0%, 40%   { transform: translateX(0); opacity: 0; }
  47%       { transform: translateX(0); opacity: 1; }
  60%       { transform: translateX(9px); opacity: 1; }
  74%       { transform: translateX(0); opacity: 1; }
  84%, 100% { transform: translateX(0); opacity: 0; }
}
@keyframes cross-flash {
  0%, 56%   { opacity: 0; transform: scale(0.6); }
  64%, 76%  { opacity: 1; transform: scale(1); }
  86%, 100% { opacity: 0; transform: scale(1); }
}
.g-local .wave rect {
  transform-box: fill-box;
  transform-origin: center;
  animation: wave-bar 0.85s ease-in-out infinite alternate;
}
.g-local .wave rect:nth-child(2) { animation-duration: 1.15s; }
.g-local .wave rect:nth-child(3) { animation-duration: 0.7s; }
.g-local .wave rect:nth-child(4) { animation-duration: 1.3s; }
.g-local .wave rect:nth-child(5) { animation-duration: 0.9s; }
.g-local .wave rect:nth-child(6) { animation-duration: 1.05s; }
.g-local .probe { animation: probe-run 7s cubic-bezier(0.5, 0, 0.4, 1) infinite; }
.g-local .cross {
  transform-box: fill-box;
  transform-origin: center;
  animation: cross-flash 7s ease infinite;
}

/* 3 ── the scribble is drawn, then resolves into two clean lines */
@keyframes messy-in {
  0%, 4%    { stroke-dashoffset: 100; opacity: 0.5; }
  28%, 52%  { stroke-dashoffset: 0; opacity: 0.5; }
  64%, 94%  { stroke-dashoffset: 0; opacity: 0.12; }
  99%, 100% { stroke-dashoffset: 100; opacity: 0.5; }
}
@keyframes step-in {
  0%, 34%   { stroke-dashoffset: 100; }
  50%, 94%  { stroke-dashoffset: 0; }
  99%, 100% { stroke-dashoffset: 100; }
}
@keyframes clean-draw {
  0%, 52%   { stroke-dashoffset: 100; }
  68%, 94%  { stroke-dashoffset: 0; }
  99%, 100% { stroke-dashoffset: 100; }
}
.g-meant .messy { animation: messy-in 7.5s cubic-bezier(0.4, 0, 0.2, 1) infinite; }
.g-meant .arrow { animation: step-in 7.5s ease infinite; }
.g-meant .clean { animation: clean-draw 7.5s cubic-bezier(0.4, 0, 0.2, 1) infinite; }
.g-meant .c2    { animation-delay: 0.28s; }

/* 4 ── attention moves between a terminal, a message and a letter */
@keyframes ring-hop {
  0%, 26%   { transform: translateX(0); }
  33%, 59%  { transform: translateX(48px); }
  66%, 92%  { transform: translateX(96px); }
  99%, 100% { transform: translateX(0); }
}
@keyframes gl-lit {
  0%, 26%   { opacity: 1; }
  33%, 100% { opacity: 0.4; }
}
.g-tone .ring { animation: ring-hop 9s cubic-bezier(0.32, 0.72, 0, 1) infinite; }
.g-tone .t1 { animation: gl-lit 9s ease infinite; }
.g-tone .t2 { animation: gl-lit 9s ease infinite -3s; }
.g-tone .t3 { animation: gl-lit 9s ease infinite -6s; }

/* 5 ── a wavy correction underline settles into a straight one, and sticks */
@keyframes squiggle-life {
  0%, 8%    { stroke-dashoffset: 100; opacity: 1; }
  26%, 44%  { stroke-dashoffset: 0; opacity: 1; }
  56%, 94%  { stroke-dashoffset: 0; opacity: 0; }
  99%, 100% { stroke-dashoffset: 100; opacity: 1; }
}
@keyframes settle {
  0%, 46%   { stroke-dashoffset: 100; opacity: 0; }
  62%, 94%  { stroke-dashoffset: 0; opacity: 1; }
  99%, 100% { stroke-dashoffset: 100; opacity: 0; }
}
@keyframes tick-draw {
  0%, 62%   { stroke-dashoffset: 100; }
  76%, 94%  { stroke-dashoffset: 0; }
  99%, 100% { stroke-dashoffset: 100; }
}
.g-learn .word     { animation: step-in 8s ease infinite; }
.g-learn .squiggle { animation: squiggle-life 8s cubic-bezier(0.4, 0, 0.2, 1) infinite; }
.g-learn .settled  { animation: settle 8s cubic-bezier(0.4, 0, 0.2, 1) infinite; }
.g-learn .tick     { animation: tick-draw 8s cubic-bezier(0.4, 0, 0.2, 1) infinite; }

/* 6 ── a short phrase becomes the three lines it stood for */
@keyframes stub-draw {
  0%, 6%    { stroke-dashoffset: 100; }
  22%, 88%  { stroke-dashoffset: 0; }
  97%, 100% { stroke-dashoffset: 100; }
}
@keyframes bolt-pop {
  0%, 24%   { opacity: 0.18; transform: scale(0.86); }
  34%, 88%  { opacity: 1;    transform: scale(1); }
  97%, 100% { opacity: 0.18; transform: scale(0.86); }
}
@keyframes out-draw {
  0%, 40%   { stroke-dashoffset: 100; }
  58%, 88%  { stroke-dashoffset: 0; }
  97%, 100% { stroke-dashoffset: 100; }
}
.g-short .stub { animation: stub-draw 8.5s cubic-bezier(0.4, 0, 0.2, 1) infinite; }
.g-short .bolt {
  transform-box: fill-box;
  transform-origin: center;
  animation: bolt-pop 8.5s cubic-bezier(0.32, 0.72, 0, 1) infinite;
}
.g-short .out  { animation: out-draw 8.5s cubic-bezier(0.4, 0, 0.2, 1) infinite; }
.g-short .o2   { animation-delay: 0.16s; }
.g-short .o3   { animation-delay: 0.32s; }

/* ── the capture section ────────────────────────────────────────────────
   Given its own moment rather than a seventh feature column: six cells fill a
   three-wide grid exactly, and a seventh would sit alone on a third row. */

.capture { text-align: center; }
.capture .glyph {
  margin-bottom: clamp(18px, 2vw, 26px);
}
.capture .glyph svg {
  max-width: 84px;
  stroke: var(--accent);
}
.capture h2 { margin-bottom: 16px; }
.capture .lede { margin-inline: auto; margin-bottom: 20px; max-width: 46ch; }
/* The supporting paragraph drops to body size, so the section reads as
   heading, line, detail rather than three sizes competing. */
.capture .detail {
  margin-inline: auto;
  max-width: 62ch;
  font-size: 15.5px;
  line-height: 1.65;
}

/* The notes fill the board one after another and stay there, which is the
   claim: nothing is thrown away except the audio. */
@keyframes note-in {
  0%, 8%    { stroke-dashoffset: 100; }
  26%, 90%  { stroke-dashoffset: 0; }
  98%, 100% { stroke-dashoffset: 100; }
}
.g-keep .note { stroke-dasharray: 100; stroke-dashoffset: 100; }
.g-keep .n1 { animation: note-in 7s cubic-bezier(0.4, 0, 0.2, 1) infinite; }
.g-keep .n2 { animation: note-in 7s cubic-bezier(0.4, 0, 0.2, 1) infinite 0.5s; }
.g-keep .n3 { animation: note-in 7s cubic-bezier(0.4, 0, 0.2, 1) infinite 1s; }

/* ------------------------------------------------------------------- steps */

.steps {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: clamp(24px, 3vw, 44px);
  counter-reset: step;
}
.step { counter-increment: step; }
.step::before {
  content: counter(step);
  display: flex;
  align-items: center;
  justify-content: center;
  width: 34px; height: 34px;
  margin-bottom: 16px;
  border-radius: 50%;
  background: var(--wash);
  color: var(--accent);
  font-size: 14px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}
.step h3 { font-size: 20px; margin-bottom: 8px; }
.step p { font-size: 15px; }

.pipeline {
  margin-top: clamp(40px, 5vw, 64px);
  padding: 26px clamp(20px, 3vw, 32px);
  border: 1px solid var(--hairline);
  border-radius: var(--radius);
  background: var(--surface);
  font-size: 13.5px;
  line-height: 2.1;
  color: var(--secondary);
  overflow-x: auto;
}
.pipeline b { color: var(--ink); font-weight: 600; }
.pipeline .arrow { color: var(--accent); padding: 0 8px; }

/* ------------------------------------------------------------------- needs */

.needs {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
  gap: clamp(24px, 3vw, 40px);
}
.need h3 { font-size: 20px; margin-bottom: 16px; }

.rows { list-style: none; margin: 0; padding: 0; }
.rows li {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 16px;
  padding: 13px 0;
  border-bottom: 1px solid var(--hairline);
  font-size: 15px;
}
.rows li:first-child { padding-top: 0; }
.rows li:last-child { border-bottom: 0; }
.rows .name { color: var(--ink); font-weight: 500; }
.rows .name small {
  display: block;
  font-size: 13px;
  font-weight: 400;
  color: var(--tertiary);
  margin-top: 3px;
  max-width: 42ch;
}
.rows .value {
  font-size: 13.5px;
  font-variant-numeric: tabular-nums;
  color: var(--secondary);
  white-space: nowrap;
}
.tag {
  display: inline-block;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--accent);
  background: var(--wash);
  padding: 2px 8px;
  border-radius: 999px;
  margin-left: 6px;
  vertical-align: 1px;
}

.note {
  margin-top: 24px;
  padding: 16px 18px;
  border-radius: var(--radius);
  background: var(--wash);
  font-size: 14px;
  color: var(--secondary);
}
.note b { color: var(--ink); }

/* --------------------------------------------------------------------- faq */

/* The block is centred under the heading; the questions and answers inside
   stay left-aligned, because centred body copy is harder to read and a
   centred summary would leave the chevron floating. */
.faq { max-width: 68ch; margin-inline: auto; }
.faq details {
  border-bottom: 1px solid var(--hairline);
}
.faq summary {
  list-style: none;
  cursor: pointer;
  padding: 20px 34px 20px 0;
  position: relative;
  font-size: 17px;
  font-weight: 500;
  color: var(--ink);
}
.faq summary::-webkit-details-marker { display: none; }
.faq summary::after {
  content: "";
  position: absolute;
  right: 6px; top: 50%;
  width: 9px; height: 9px;
  border-right: 1.5px solid var(--tertiary);
  border-bottom: 1.5px solid var(--tertiary);
  transform: translateY(-70%) rotate(45deg);
  transition: transform 0.22s ease;
}
.faq details[open] summary::after { transform: translateY(-25%) rotate(-135deg); }
.faq details p { padding-bottom: 20px; font-size: 15px; max-width: 62ch; }

/* ------------------------------------------------------------------ closing */

.closing {
  text-align: center;
  border-top: 1px solid var(--hairline);
}
.closing h2 { margin-bottom: 20px; }
.closing .lede { margin-inline: auto; }

/* The footer carries the painting itself. The image is a real <img> for
   loading and aspect reasons, pinned behind the content; two gradients sit
   over it. One eases up out of the canvas so there is no seam where the
   footer starts. The other darkens the bottom so the credits stay legible over
   a bright sky. Because the bottom always resolves dark, the footer keeps the
   same treatment in light and dark mode. */
footer {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  padding: clamp(150px, 20vw, 260px) 0 clamp(40px, 5vw, 60px);
  font-size: 13.5px;
  color: rgba(244, 246, 242, 0.82);
}
.footer-bg {
  position: absolute;
  inset: 0;
  z-index: -2;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center 40%;
}
footer::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background:
    linear-gradient(to bottom, var(--canvas) 0%, transparent 40%),
    linear-gradient(to bottom, transparent 34%, rgba(16, 21, 16, 0.68) 70%, rgba(16, 21, 16, 0.92) 100%);
}

.footer-grid {
  display: flex;
  flex-wrap: wrap;
  gap: 24px 44px;
  align-items: center;
  justify-content: space-between;
}
.footer-mark {
  flex: none;
  height: clamp(44px, 5vw, 58px);
  width: auto;
}
footer p {
  font-size: 13.5px;
  color: inherit;
  max-width: 46ch;
  margin: 0;
}
footer a {
  color: #d3e3d3;
  text-decoration-color: rgba(211, 227, 211, 0.35);
  text-underline-offset: 3px;
}
footer a:hover { text-decoration-color: currentColor; }

@media (max-width: 620px) {
  .footer-grid { flex-direction: column; align-items: flex-start; gap: 20px; }
}

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

.reveal {
  opacity: 0;
  transform: translateY(14px);
  transition: opacity 0.7s cubic-bezier(0.32, 0.72, 0, 1), transform 0.7s cubic-bezier(0.32, 0.72, 0, 1);
}
.reveal.seen { opacity: 1; transform: none; }

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  .reveal { opacity: 1; transform: none; transition: none; }
  /* Hold the demo on its finished state rather than looping. */
  .demo *, .demo { animation: none !important; }
  .demo .pill { opacity: 0; }
  .demo .pasted { opacity: 1; }
  .demo .placeholder { display: none; }
  .demo .said { opacity: 1; }
  /* Same for the six glyphs: hold each on the state it is arguing for,
     fully drawn. */
  .glyph, .glyph * { animation: none !important; }
  .glyph [class*="qq"] { stroke-dashoffset: 0 !important; }
  .g-local .cross, .g-local .probe { opacity: 1; }
  .g-meant .messy { opacity: 0.12; }
  .g-tone .gl { opacity: 0.4; }
  .g-tone .t1 { opacity: 1; }
  .g-learn .squiggle { opacity: 0; }
  .g-learn .settled { opacity: 1; }
  .g-short .bolt { opacity: 1; }
  .g-keep .note { stroke-dashoffset: 0 !important; }
  * { transition-duration: 0.01ms !important; }
}

@media (max-width: 620px) {
  .titlebar span { display: none; }
  .hero { text-align: left; }
  .hero .cta { align-items: flex-start; }
  .rows li { flex-direction: column; gap: 6px; }
  .rows .value { white-space: normal; }
}
