/* Terminal Intelligence — landing
   Aesthetic: a terminal that has calmed down. Warm dark, one accent,
   real block grammar, generous space. Nothing glows, nothing scrolls by itself. */

@font-face { font-family: "Geist Mono"; src: url(GeistMono-Regular.otf) format("opentype"); font-weight: 400; font-display: swap; }
@font-face { font-family: "Geist Mono"; src: url(GeistMono-Bold.otf) format("opentype"); font-weight: 700; font-display: swap; }

:root {
  --bg: #0e1013;
  --bg-2: #14171c;
  --panel: #171a20;
  --line: #262a33;
  --fg: #e8e6e1;
  --fg-2: #a8a49c;
  --fg-3: #88837b; /* >=4.5:1 on --bg, --bg-2, and --panel (WCAG AA body text) */
  --accent: #7fd1b9;           /* the one accent: calm seafoam, not neon */
  --accent-ink: #0b1a15;
  --safe: #4ecb8d;
  --caution: #e8c25a;
  --danger: #ef6b6b;
  --sans: -apple-system, BlinkMacSystemFont, "SF Pro Text", "Inter", system-ui, sans-serif;
  --mono: "Geist Mono", "SF Mono", ui-monospace, Menlo, monospace;
  --radius: 14px;
  --max: 1080px;
}

* { box-sizing: border-box; }
html { scroll-behavior: smooth; }
body {
  margin: 0; background: var(--bg); color: var(--fg);
  font: 17px/1.55 var(--sans); -webkit-font-smoothing: antialiased;
  background-image: radial-gradient(1200px 600px at 70% -10%, rgba(127,209,185,.07), transparent 60%);
}
a { color: inherit; }
h1, h2, h3 { margin: 0; font-weight: 600; letter-spacing: -0.02em; }
p { margin: 0; }
kbd { font: 600 .9em var(--mono); background: var(--bg-2); border: 1px solid var(--line); border-bottom-width: 2px; border-radius: 6px; padding: .05em .45em; }
.k { font: 700 .95em var(--mono); color: var(--fg-2); letter-spacing: .04em; }
.k.warn { color: var(--danger); }
.eyebrow { font: 500 13px/1.4 var(--mono); text-transform: uppercase; letter-spacing: .14em; color: var(--accent); margin-bottom: 18px; }
.eyebrow.center { text-align: center; }
.center { text-align: center; }
.lede { font-size: 20px; color: var(--fg-2); max-width: 46ch; }

/* ---------- buttons ---------- */
.btn {
  display: inline-block; text-decoration: none; font-weight: 600; font-size: 16px;
  padding: 12px 20px; border-radius: 10px; border: 1px solid var(--line); color: var(--fg);
  transition: transform .12s ease, background .12s ease;
}
.btn:hover { transform: translateY(-1px); }
.btn-primary { background: var(--accent); color: var(--accent-ink); border-color: transparent; }
.btn-primary:hover { background: #93dcc6; }
.btn-sm { padding: 8px 14px; font-size: 14px; }
.btn-lg { padding: 16px 28px; font-size: 18px; max-width: 100%; overflow-wrap: break-word; }

/* ---------- top bar ---------- */
.top {
  max-width: var(--max); margin: 0 auto; padding: 22px 24px;
  display: flex; align-items: center; justify-content: space-between; gap: 24px;
}
.brand { display: flex; align-items: center; gap: 10px; text-decoration: none; font: 500 15px var(--mono); color: var(--fg); }
.brand img { border-radius: 7px; }
nav { display: flex; align-items: center; gap: 26px; font-size: 15px; color: var(--fg-2); }
nav a { text-decoration: none; }
nav a:not(.btn):hover { color: var(--fg); }

/* ---------- sections ---------- */
main > section { max-width: var(--max); margin: 0 auto; padding: 96px 24px; }

/* ---------- hero ---------- */
.hero { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1.1fr); gap: 56px; align-items: center; padding-top: 72px; }
.hero > * { min-width: 0; }
.hero h1 { font-size: clamp(40px, 5.2vw, 60px); line-height: 1.05; margin-bottom: 22px; }
.hero h1 em { font-style: normal; color: var(--accent); }
.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
}

/* ---------- headline: three fixed block lines, one sliding tool-name slot ----------
   "Told to use" / [sliding: Claude Code, Codex, OpenCode, Cursor] / "Start here."
   Only the middle line's content moves; the first and third lines and every
   surrounding block on the page stay put — the slot's height is fixed to one
   line box, so nothing above or below it ever shifts width or height. */
.tl-headline { font-size: clamp(40px, 5.2vw, 60px); line-height: 1.05; margin-bottom: 22px; }
.tl-visual { display: block; }
.tl-row { display: block; }
/* Word pitch (line box height for each sliding word, and the fixed slot
   height that clips to exactly one word) is 1.25em — comfortably clear of
   descenders/ascenders (>= the 1.2-1.3em floor) at the hero's clamp(40px,
   5.2vw, 60px) headline size, so no glyph in any tool name is clipped
   top or bottom while sliding. The three 1.25em values below (.tl-row-slot
   height, .tl-slot height, .tl-word height/line-height) and the
   translateY step (-1.25em per index, further down) are literal, not a
   shared custom property — if this pitch ever changes, all four spots
   must be updated together. */
.tl-row-slot { overflow: hidden; height: 1.25em; }
.tl-slot { display: block; height: 1.25em; overflow: hidden; }
.tl-track {
  display: flex; flex-direction: column;
}
.tl-word { display: flex; align-items: center; height: 1.25em; line-height: 1.25; white-space: nowrap; }

/* JS drives the slide by setting a --tl-y custom property (in multiples of
   one word's pitch) and toggling a transition class; see the script. With
   JS disabled the track never moves, so the first word ("Claude Code") is
   what a no-JS visitor sees — same text as the sr-only accessible name. */
.tl-track { transform: translateY(calc(var(--tl-y, 0) * -1.25em)); }
.tl-track.tl-animate { transition: transform .46s cubic-bezier(0.22, 0.61, 0.36, 1); will-change: transform; }

@media (prefers-reduced-motion: reduce) {
  .tl-track { transform: none !important; transition: none !important; will-change: auto !important; }
  .tl-word { display: none; }
  .tl-word.tl-word-active { display: flex; }
}
.cta { margin-top: 32px; display: flex; align-items: center; gap: 18px; flex-wrap: wrap; }
.cta-note { color: var(--fg-3); font-size: 14px; }

/* ---------- terminal window ---------- */
.term {
  background: var(--panel); border: 1px solid var(--line); border-radius: var(--radius);
  box-shadow: 0 30px 80px -30px rgba(0,0,0,.7), 0 0 0 1px rgba(255,255,255,.02) inset;
  overflow: hidden;
}
.term-bar {
  display: flex; align-items: center; gap: 7px; padding: 11px 14px;
  border-bottom: 1px solid var(--line); background: var(--bg-2);
  font: 12px var(--mono); color: var(--fg-3);
}
.term-bar i { width: 11px; height: 11px; border-radius: 50%; background: #3a3f49; }
.term-bar span { margin-left: 8px; }
.term-body {
  margin: 0; padding: 18px 18px; font: 13.5px/1.6 var(--mono); color: var(--fg);
  min-width: 0;
}
.term-stack .term-body + .term-body { border-top: 1px solid var(--line); }
.term-body-gap { margin-top: 18px; }
.term-line { margin: 0 0 10px; white-space: pre-wrap; word-break: break-word; }
.p { color: var(--fg-3); }
.err { color: var(--danger); }

/* The block (spec "The block, in Terminal.app"): a bordered card instead
   of literal box-drawing characters, so it reflows at any width instead
   of clipping a fixed 64-column ASCII frame. Labels dim, the command
   bold, the risk a word inside a filled badge — ink is near-black so it
   reads on all three badge backgrounds. Every child has min-width: 0 so
   long commands/consequence text wrap inside the card instead of forcing
   the card (or its grid ancestor) wider than its container. */
.ti-block {
  min-width: 0; margin: 14px 0; border: 1px solid var(--accent); border-radius: 8px;
  overflow: hidden;
}
.ti-head {
  display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between;
  gap: 4px 12px; padding: 8px 12px; border-bottom: 1px solid var(--line);
  min-width: 0;
}
.ti-head b { color: var(--fg); font-weight: 700; }
.ti-rows {
  margin: 0; padding: 10px 12px; min-width: 0;
  display: grid; grid-template-columns: 5ch minmax(0, 1fr); gap: 4px 12px;
}
.ti-rows dt { grid-column: 1; min-width: 0; }
.ti-rows dd {
  grid-column: 2; margin: 0; color: var(--fg); min-width: 0;
  overflow-wrap: break-word; word-break: break-word;
}
.ti-fix {
  display: flex; flex-wrap: wrap; align-items: center; gap: 8px 12px;
  padding: 10px 12px; border-top: 1px solid var(--line); border-bottom: 1px solid var(--line);
  min-width: 0;
}
.ti-fix .keys { min-width: 0; overflow-wrap: break-word; word-break: break-word; flex-basis: 100%; }
.ti-foot { padding: 8px 12px; min-width: 0; }

.ti-block .k { color: var(--fg-2); }
.cmd {
  font: inherit; font-weight: 700; color: var(--fg); min-width: 0;
  overflow-wrap: break-word; word-break: break-word; background: none;
}
.keys { color: var(--fg-3); }
.badge {
  font: 700 .85em var(--mono); color: #101010; letter-spacing: .04em;
  padding: 2px 8px; border-radius: 4px; white-space: nowrap; flex-shrink: 0;
}
.badge.safe { background: var(--safe); }
.badge.caution { background: var(--caution); }
.badge.danger { background: var(--danger); }
.ti-block .k.warn { color: var(--danger); }
.typed { color: var(--fg); }
.cursor { display: inline-block; width: 8px; height: 1.1em; background: var(--fg); vertical-align: text-bottom; animation: blink 1.1s steps(1) infinite; }
@keyframes blink { 50% { opacity: 0; } }
@media (prefers-reduced-motion: reduce) { .cursor { animation: none; } }

/* Mobile: stack the badge above the command instead of wrapping them
   inline mid-word once the row gets tight. */
@media (max-width: 420px) {
  .ti-fix { flex-direction: column; align-items: flex-start; }
}

/* ---------- hero demo (typing -> error -> explanation -> insert) ----------
   Every step is revealed with visibility/opacity, never by changing layout
   flow (display:none/block or adding/removing nodes), so the .term card
   never changes height across the sequence and nothing below it (or the
   headline slide beside it) ever shifts. The error line and the ti-block
   each reserve their final rendered height from first paint via
   visibility: hidden, then fade in with opacity only. */
.hero-media { display: flex; flex-direction: column; gap: 12px; min-width: 0; }
/* Default (no-JS, or JS hasn't decided to animate yet): every step of the
   example is fully visible and static, so a no-JS or pre-hydration
   visitor sees the complete worked example (typed command, error,
   explanation block, and the final "git status" on the last prompt) —
   not a mid-sequence snapshot. The script only hides steps to animate
   the reveal, and only after confirming prefers-reduced-motion allows
   motion, by adding .demo-animating to the wrapper below. */
.demo-fade { transition: opacity .35s ease; }
.hero-media.demo-animating .demo-fade {
  opacity: 0; visibility: hidden;
}
.hero-media.demo-animating .demo-fade.demo-visible { opacity: 1; visibility: visible; }
@media (prefers-reduced-motion: reduce) {
  .demo-fade { transition: none; }
  .hero-media.demo-animating .demo-fade { opacity: 1; visibility: visible; }
}

/* The ⌃Y key hint gets a brief highlight pulse when the demo reaches the
   insert step — color/background only, never a size or position change. */
.demo-hl { border-radius: 4px; padding: 1px 4px; margin: 0 -4px; transition: background-color .25s ease, color .25s ease; }
.demo-hl.demo-hl-active { background: var(--accent); color: var(--accent-ink); }
@media (prefers-reduced-motion: reduce) {
  .demo-hl { transition: none; }
}

.demo-caption {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  flex-wrap: wrap; font-size: 13px; color: var(--fg-3);
}
.demo-caption-label { font: 500 12px var(--mono); text-transform: uppercase; letter-spacing: .08em; }
.demo-pause-btn {
  font: 600 13px var(--sans); color: var(--fg-2); background: var(--bg-2);
  border: 1px solid var(--line); border-radius: 8px; padding: 6px 12px; cursor: pointer;
}
.demo-pause-btn:hover { color: var(--fg); border-color: var(--fg-3); }
.demo-pause-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* The button ships with the `hidden` attribute in the raw markup: with
   no JS, nothing animates and the button has no click handler, so a
   control that does nothing is worse than no control — it stays hidden.
   The motion coordinator's updatePauseButton() runs once at load and
   unconditionally clears `hidden` unless prefers-reduced-motion already
   matches (in which case nothing auto-plays, so there is nothing to
   pause) — it does not wait on player registration. It re-applies the
   same check live if the OS-level setting changes mid-visit. `[hidden]`
   is used (not a media query alone) because the button's visibility
   depends on this JS check, not on the media query by itself — the
   query is also asserted directly in JS at runtime, not just here. */
/* The typing-phase cursor is JS-controlled and only relevant while the
   demo is actively animating; default hidden so a no-JS or reduced-motion
   visitor never sees a stray blinking cursor after already-resolved
   output. */
.demo-cursor-typing { display: none; }

/* ---------- hero demo: loading -> result stage ----------
   .ti-stage is a CSS grid with a single implicit cell; both the loading
   card and the result card are placed at grid-area: 1 / 1, so both
   occupy the exact same cell simultaneously and the grid's own auto row
   sizing is naturally the taller of the two cards' intrinsic heights —
   no JS measurement or explicit min-height is needed to reserve space,
   and nothing below the stage ever shifts as loading swaps for the
   result. Only one of the two cards is "visible" (opacity 1, the other
   opacity 0 and inert) at a time; swapping between them is an opacity
   cross-fade, never a layout reflow, because both are always present
   and already sized. */
.ti-stage { position: relative; display: grid; min-width: 0; }
.ti-stage > .ti-block {
  grid-area: 1 / 1; min-width: 0;
  opacity: 0; visibility: hidden; pointer-events: none;
  transition: opacity .2s ease;
}
.ti-stage > .ti-block.ti-stage-visible { opacity: 1; visibility: visible; pointer-events: auto; }
/* The real app's "working it out…" block (Sources/Shared/BlockRenderer.swift
   thinking(), frame: .quiet) is a short, quiet frame — one dim row, no
   header rows/fix/footer — deliberately much shorter than a resolved
   answer block. By default both cards stretch to fill the grid cell
   (align-self: stretch), which the auto-sized row sizes to the TALLER
   card (the result), so the loading card's border was being stretched
   to match — a tall bordered box the real app never draws for a plain
   spinner line. align-self: start makes the loading card size to its
   own intrinsic (compact) height and sit at the top of the cell instead;
   the reserved space below it stays part of the (already fully
   transparent, unbordered) .ti-stage grid cell, so the surrounding
   hero/card/page layout height is untouched — only the loading card's
   own border shrinks to match its real content. */
.ti-stage > .ti-loading { align-self: start; }
/* No-JS / pre-hydration default: the result card is the complete worked
   example a visitor with no script should see, so it renders visible
   without needing any class toggled by JS. The loading card stays
   hidden by default (aria-hidden="true" is set in markup too). Scoped to
   .hero-media:not(.demo-animating) — once the JS controller adds
   .demo-animating (see play()), this default no longer applies and the
   .ti-stage-visible class above becomes the sole source of truth, so
   toggling classes during the sequence is never overridden by this
   higher-specificity ID rule. */
.hero-media:not(.demo-animating) .ti-stage > #demo-block { opacity: 1; visibility: visible; pointer-events: auto; }
@media (prefers-reduced-motion: reduce) {
  .ti-stage > .ti-block { transition: none; }
}

.ti-loading-row { display: flex; align-items: center; gap: 10px; padding: 12px; min-width: 0; }
/* The spinner's motion is driven entirely by JS (setInterval swapping the
   glyph in .ti-spinner's textContent, see startSpinner()/stopSpinner()),
   not a CSS animation/keyframes — there is nothing here for a
   prefers-reduced-motion media query to disable. Reduced-motion users
   never see the spinner at all: the motion coordinator's goStatic() path
   is what they get (see the hero demo player script), and it calls
   showResult(), which calls stopSpinner(), before startSpinner() is ever
   reached. */
.ti-spinner { font: 700 16px var(--mono); color: var(--accent); display: inline-block; width: 1.2em; text-align: center; }
.ti-loading-text { color: var(--fg-2); font: 13.5px var(--mono); }

/* ---------- loop ---------- */
.loop { border-top: 1px solid var(--line); }
.steps { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: repeat(3, 1fr); gap: 32px; }
.steps li { padding: 28px; background: var(--bg-2); border: 1px solid var(--line); border-radius: var(--radius); }
.steps .n { display: inline-grid; place-items: center; width: 30px; height: 30px; border-radius: 50%; background: var(--accent); color: var(--accent-ink); font: 700 14px var(--mono); margin-bottom: 18px; }
.steps h3 { font-size: 20px; margin-bottom: 10px; }
.steps p { color: var(--fg-2); font-size: 16px; }

/* ---------- safety ---------- */
.safety { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1.1fr); gap: 56px; align-items: center; border-top: 1px solid var(--line); }
.safety > * { min-width: 0; }
.safety h2 { font-size: clamp(30px, 3.6vw, 40px); line-height: 1.15; margin-bottom: 18px; }
.safety > .safety-copy > p { color: var(--fg-2); font-size: 18px; }
.legend { list-style: none; margin: 28px 0 0; padding: 0; display: grid; gap: 14px; font-size: 16px; color: var(--fg-2); }
.legend .badge { font-size: 14px; margin-right: 12px; }

/* ---------- principles ---------- */
.principles { display: grid; grid-template-columns: repeat(2, 1fr); gap: 24px; border-top: 1px solid var(--line); }
.card { padding: 30px; border: 1px solid var(--line); border-radius: var(--radius); background: linear-gradient(180deg, var(--bg-2), transparent); }
.card h3 { font-size: 20px; margin-bottom: 10px; }
.card p { color: var(--fg-2); font-size: 16px; }

/* ---------- pricing ---------- */
.pricing { border-top: 1px solid var(--line); text-align: center; }
.price-card {
  margin: 0 auto; max-width: 520px; padding: 40px; text-align: left;
  border: 1px solid var(--line); border-radius: var(--radius); background: var(--bg-2);
}
.price-main { display: flex; align-items: baseline; gap: 6px; flex-wrap: wrap; margin-bottom: 24px; }
.amount { font-size: 56px; font-weight: 600; letter-spacing: -0.03em; }
.per { color: var(--fg-2); font-size: 20px; }
.price-sub { flex-basis: 100%; color: var(--fg-3); font-size: 15px; }
.price-features { list-style: none; margin: 0 0 28px; padding: 0; display: grid; gap: 12px; color: var(--fg-2); }
.price-features li::before { content: "✓"; color: var(--accent); font-family: var(--mono); margin-right: 12px; }

/* ---------- download ---------- */
.download { border-top: 1px solid var(--line); text-align: center; }
.download h2 { font-size: clamp(30px, 3.6vw, 40px); margin-bottom: 14px; }
.download .lede { margin: 0 auto 30px; }
.dl-meta { color: var(--fg-3); font-size: 14px; margin-top: 16px; }
.faq { max-width: 640px; margin: 20px auto 0; text-align: left; border: 1px solid var(--line); border-radius: 12px; background: var(--bg-2); }
.faq summary { cursor: pointer; padding: 16px 20px; font-weight: 600; list-style: none; }
.faq summary::-webkit-details-marker { display: none; }
.faq summary::before { content: "›"; display: inline-block; margin-right: 12px; color: var(--accent); transition: transform .15s; }
.faq[open] summary::before { transform: rotate(90deg); }
.faq p { padding: 0 20px 18px; color: var(--fg-2); }
.faq p + p { padding-top: 0; }
.events { margin: 0 20px 16px; padding: 12px 14px; font: 13px var(--mono); color: var(--accent); background: var(--bg); border: 1px solid var(--line); border-radius: 8px; white-space: pre-wrap; }
.download:first-of-type { margin-top: 40px; }

/* ---------- footer ---------- */
footer { max-width: var(--max); margin: 0 auto; padding: 32px 24px 48px; display: flex; gap: 24px; color: var(--fg-3); font-size: 14px; border-top: 1px solid var(--line); }
footer a { text-decoration: none; } footer a:hover { color: var(--fg); }

/* ---------- responsive ---------- */
@media (max-width: 900px) {
  .hero, .safety { grid-template-columns: 1fr; gap: 36px; }
  .steps, .principles { grid-template-columns: 1fr; }
  main > section { padding: 64px 20px; }
  nav a:not(.btn) { display: none; }
}

/* Mobile: the block is a responsive card, not a fixed-width ASCII frame —
   content wraps instead of clipping, so type never needs to shrink below
   the accessible floor (13.5px) to keep glyphs aligned. */
@media (max-width: 600px) {
  .term-body { padding: 14px 12px; }
}
