/* demoman.io, after docs/superpowers/specs/2026-10-18-site-redesign-design.md:
   near-black, hairline grid, Geist, lime as a rare accent (§3). The landing
   page, /privacy and the Worker's answer page all share this sheet.
   test/look.test.mjs holds the tokens, fonts, accent and focus ring to the
   design. */
@font-face {
  font-family: "Geist";
  src: url("/assets/fonts/Geist-Variable.woff2") format("woff2");
  font-weight: 100 900;
  font-display: swap;
}
@font-face {
  font-family: "Geist Mono";
  src: url("/assets/fonts/GeistMono-Variable.woff2") format("woff2");
  font-weight: 100 900;
  font-display: swap;
}
/* The header's wordmark (spec §3.5). */
@font-face {
  font-family: "Outfit";
  src: url("/assets/fonts/outfit-latin-variable.woff2") format("woff2");
  font-weight: 100 900;
  font-display: swap;
}

:root {
  --bg: #0a0a0a;
  --bg-1: #101010;
  --bg-2: #151515;
  --bg-3: #1b1b1b;
  --line: rgba(255,255,255,.08);
  --line-2: rgba(255,255,255,.13);
  --ink: #f4f4f5;
  --ink-2: #a1a1aa;
  /* --ink-3 and --ink-4 were raised from the mockup's first values so small
     text clears AA on --bg and --bg-1 (spec §3.1). */
  --ink-3: #8f8f98;
  --ink-4: #7b7b84;
  --accent: #cdeb45;
  /* The drawings' main line (launch copy spec §4.1). */
  --draw: #c4c4cc;
  /* The terminal's "done" teal: colour lives in the product (the terminal),
     as Linear keeps its own inside its product mock, and nowhere else. */
  --ok: #5eead4;
  --sans: "Geist", system-ui, -apple-system, "Segoe UI", sans-serif;
  --mono: "Geist Mono", ui-monospace, "SF Mono", Menlo, monospace;
  --w: 1340px;
  --pad: 32px;
}

*,
*::before,
*::after {
  box-sizing: border-box;
}
/* The header is sticky and 60px tall: an in-page link (#changelog) lands
   below it, with room to breathe. */
html {
  -webkit-text-size-adjust: 100%;
  background: var(--bg);
  scroll-padding-top: 76px;
}
@supports (overflow: clip) {
  html,
  body {
    overflow-x: clip;
  }
}
body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font-family: var(--sans);
  font-size: 15px;
  line-height: 1.6;
  letter-spacing: -0.006em;
  -webkit-font-smoothing: antialiased;
}
a {
  color: inherit;
  text-decoration: none;
}
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
}

/* Keyboard focus: one lime ring for every control (spec §8). */
/* An outline, not a box-shadow: buttons and the pill draw their rings with
   box-shadow, which would win the cascade and erase this (final review,
   2026-10-18). An outline follows each control's own corners. */
:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* The header. */
.site {
  position: sticky;
  top: 0;
  z-index: 10;
  height: 60px;
  background: rgba(10,10,10,.78);
  -webkit-backdrop-filter: blur(16px);
  backdrop-filter: blur(16px);
  border-bottom: 1px solid var(--line);
}
.site .row {
  position: relative;
  max-width: var(--w);
  height: 100%;
  margin: 0 auto;
  padding: 0 var(--pad);
  display: flex;
  align-items: center;
  gap: 32px;
}
/* The home link is the figure and the wordmark (the author, 2026-10-25).
   The figure is 24px, as it was standing alone at the bar's centre, and
   sits on the content's left edge with no box around it. */
.home {
  display: flex;
  align-items: center;
  gap: 18px;
  color: var(--ink);
  transition: gap 520ms cubic-bezier(0.65, 0, 0.35, 1);
}
.home .lm {
  display: flex;
}
.home .lm .figure {
  height: 24px;
}
.home + nav.main {
  margin-left: 8px;
}
.home .word {
  display: inline-block;
  max-width: 6em;
  overflow: hidden;
  white-space: nowrap;
  font-family: "Outfit", var(--sans);
  font-size: 19px;
  font-weight: 600;
  letter-spacing: -0.015em;
  line-height: 1;
  transition:
    max-width 520ms cubic-bezier(0.65, 0, 0.35, 1),
    opacity 260ms ease-out,
    filter 260ms ease-out;
}
/* Past the hero (wordmark.js): the word fades and its width closes, so the
   nav glides in beside the figure. It plays under reduced motion too, as
   the author asked (2026-10-25); it is one short move the reader set off. */
.site.compact .home {
  gap: 0;
}
.site.compact .home .word {
  max-width: 0;
  opacity: 0;
  filter: blur(4px);
}
/* Centred one by one, the 19px word and the 13.5px links land 2px apart at
   the baseline; 1px each way puts them on one (measured on the painted
   glyphs, 2026-10-19). Phones show no links, so the word stays centred. */
.home .word,
nav.main {
  position: relative;
}
.home .word {
  top: -1px;
}
nav.main {
  top: 1px;
}
.figure {
  height: 22px;
  width: auto;
  fill: currentColor;
}
nav.main {
  display: flex;
  gap: 24px;
  font-size: 13.5px;
  color: var(--ink-2);
}
nav.main a:hover,
.gh:hover {
  color: var(--ink);
}
.spacer {
  flex: 1;
}
/* GitHub with its name; phones show the mark alone (aria-label names it). */
.gh {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 13.5px;
  color: var(--ink-2);
}
.gh svg {
  width: 16px;
  height: 16px;
  fill: currentColor;
}

/* Buttons: ghosts with a ring (the header's filled Get started went,
   2026-10-18). */
.btn {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  height: 32px;
  padding: 0 14px;
  border-radius: 999px;
  font-size: 13.5px;
  font-weight: 500;
  letter-spacing: -0.01em;
  white-space: nowrap;
}
.btn.lg {
  height: 42px;
  padding: 0 20px;
  font-size: 14.5px;
}
.btn.ghost {
  color: var(--ink);
  background: rgba(255,255,255,.02);
  box-shadow: inset 0 0 0 1px var(--line-2);
}
.btn.ghost:hover {
  box-shadow: inset 0 0 0 1px rgba(255,255,255,.24);
}


/* Page width and section positioning. Dividers stay within their content
   groups rather than forming a grid across the page. */
.rails {
  max-width: var(--w);
  margin: 0 auto;
}
.band {
  position: relative;
}

/* The hero (the author, 2026-10-19, replacing option B of spec §5.2):
   one centred column of pill, headline, a one-sentence intro and the
   install row, over the stage. With no second column there is nothing
   beside the headline to balance, and the column shares the stage's
   centre line. */
.hero {
  position: relative;
  overflow: hidden; /* keeps the stage's light inside the rails */
  padding: 72px var(--pad) 0;
}
.pill {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  max-width: 100%;
  margin-bottom: 24px;
  padding: 3px 12px 3px 10px;
  border-radius: 999px;
  font-size: 12.5px;
  line-height: 22px;
  color: var(--ink-2);
  white-space: nowrap;
  box-shadow: inset 0 0 0 1px var(--line-2);
}
/* The launch dot: lime, with a faint ring of itself. */
.pill .ld {
  flex: none;
  width: 7px;
  height: 7px;
  margin-left: 2px;
  border-radius: 50%;
  background: var(--accent);
  box-shadow: 0 0 0 4px rgba(205, 235, 69, 0.15);
}
.pill .pl {
  color: var(--ink);
  font-weight: 500;
}
/* The release that is out and its month, held to the newest release
   commit and the changelog by test/look.test.mjs. */
.pill .ver {
  margin-left: -4px;
  font-family: var(--mono);
  font-size: 11.5px;
  color: var(--ink-3);
}
/* The agents, under the install row: the marks and names the pill
   carried until 2026-10-22. */
.works {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: center;
  gap: 18px;
  margin-top: 28px;
  font-size: 13.5px;
  color: var(--ink-2);
}
.works .wl {
  font-family: var(--mono);
  font-size: 11.5px;
  letter-spacing: 0.02em;
  color: var(--ink-4);
}
.works .ag {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  white-space: nowrap;
}
.works .mk {
  flex: none;
  width: 15px;
  height: 15px;
  fill: currentColor;
}
h1 {
  margin: 0;
  font-size: 68px;
  line-height: 1.02;
  font-weight: 500;
  letter-spacing: -0.045em;
}
.q {
  color: var(--ink-3);
}
.hero-head {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
}
/* Centred lines read best evened out, so the intro's two are balanced.
   Quiet beside the headline, after Linear's hero (the author, 2026-10-19),
   and since 2026-10-25 a step larger and lighter: it is what tells a first
   visitor what Demoman is. */
.intro {
  max-width: 34em;
  margin: 20px 0 0;
  font-size: 18px;
  line-height: 1.6;
  letter-spacing: -0.011em;
  color: var(--ink-2);
  text-wrap: balance;
}
/* The product's name stands out in white (the author, 2026-10-23). */
.intro b {
  font-weight: inherit;
  color: var(--ink);
}
.cta {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: flex-start;
  gap: 14px;
  margin-top: 32px;
}
.plat {
  margin: 0 0 0 6px;
  line-height: 42px;
  font-size: 13px;
  color: var(--ink-3);
}

/* An install command (install.js binds each .install on the page). */
/* The note under a command is absolutely placed; the block keeps room for
   it so it never lands on the control below (a wrapped "Read the docs"). */
.install {
  position: relative;
  margin-bottom: 28px;
}
.cmd {
  display: flex;
  align-items: center;
  gap: 12px;
  height: 42px;
  padding: 0 5px 0 16px;
  border-radius: 10px;
  /* A command reads from its prompt, even in a centred column where the
     box runs the column's width (the hero on phones). */
  text-align: left;
  background: var(--bg-1);
  box-shadow: inset 0 0 0 1px var(--line-2);
}
.dollar {
  color: var(--ink-4);
  font: 400 14px/20px var(--mono);
  user-select: none;
}
.install-cmd {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
  color: var(--ink);
  font: 400 14px/20px var(--mono);
  user-select: all;
}
/* Copying: the button gives under the press, the copy icon shrinks and
   blurs away while the tick draws itself in its place, and both run in
   reverse when the tick times out (install.js). The two icons share one
   grid cell so neither moves the other. */
.copy {
  flex: none;
  display: grid;
  place-items: center;
  width: 32px;
  height: 32px;
  border: 0;
  border-radius: 7px;
  background: var(--bg-3);
  color: var(--ink-2);
  cursor: pointer;
  transition: transform 160ms cubic-bezier(0.16, 1, 0.3, 1), color 150ms ease-out;
}
.copy:hover {
  color: var(--ink);
}
.copy:active {
  transform: scale(0.92);
}
.copy[hidden] {
  display: none;
}
.copy svg {
  grid-area: 1 / 1;
  width: 15px;
  height: 15px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.6;
  stroke-linecap: round;
  stroke-linejoin: round;
  transition:
    opacity 180ms ease-out,
    transform 320ms cubic-bezier(0.34, 1.56, 0.64, 1),
    filter 180ms ease-out;
}
.copy .i-done,
.copy.done .i-copy {
  opacity: 0;
  transform: scale(0.5);
  filter: blur(2px);
}
.copy.done .i-done {
  opacity: 1;
  transform: none;
  filter: none;
}
.copy.done {
  color: var(--ink);
}
/* The tick draws from its short arm to its tip (pathLength="1"). */
.copy .i-done path {
  stroke-dasharray: 1;
  stroke-dashoffset: 1;
  transition: stroke-dashoffset 280ms cubic-bezier(0.65, 0, 0.35, 1) 60ms;
}
.copy.done .i-done path {
  stroke-dashoffset: 0;
}
/* What happened, under the command: "Copied." or the refusal. */
.install-note {
  position: absolute;
  top: calc(100% + 8px);
  left: 2px;
  margin: 0;
  font-size: 13px;
  white-space: nowrap;
  color: var(--ink-2);
}
.install-note.err {
  color: var(--ink);
}
.install-note:not(:empty) {
  animation: rise 240ms cubic-bezier(0.16, 1, 0.3, 1) both;
}
@keyframes rise {
  from {
    opacity: 0;
    transform: translateY(4px);
  }
}

/* The stage: the agent's terminal and the video it made, in one frame.
   film.js adds .live when it takes over. */
.stage {
  position: relative;
  display: grid;
  grid-template-columns: 360px minmax(0, 1fr);
  grid-template-areas: "term screen";
  gap: 8px;
  margin-top: 48px;
  padding: 8px;
  border-radius: 14px;
  background: var(--bg-1);
  box-shadow: inset 0 0 0 1px var(--line-2), 0 40px 80px -20px rgba(0,0,0,.6);
}
/* One soft light from below (spec §3.4). */
.stage::before {
  content: "";
  position: absolute;
  z-index: -1;
  inset: 30% -6% -18%;
  background: radial-gradient(50% 60% at 50% 100%, rgba(228,228,231,.13), transparent 70%);
  pointer-events: none;
}
.term {
  grid-area: term;
  display: flex;
  flex-direction: column;
  min-height: 0;
  overflow: hidden;
  /* The terminal takes the video's height and adds none of its own. */
  contain: size;
  min-height: 100%;
  border-radius: 8px;
  background: #0c0c0c;
  box-shadow: inset 0 0 0 1px var(--line);
  font: 400 12px/20px var(--mono);
  color: var(--ink-2);
}
.term-bar {
  flex: none;
  display: flex;
  gap: 10px;
  align-items: center;
  height: 34px;
  padding: 0 14px;
  border-bottom: 1px solid var(--line);
  font-size: 11.5px;
  color: var(--ink-3);
}
.term-bar b {
  font-weight: 500;
  color: var(--ink-2);
}
/* Lines run down from the top, as a terminal's do; once the panel is full
   film.js keeps it scrolled to the newest line. */
.term-log {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  scrollbar-width: thin;
  scrollbar-color: var(--line) transparent;
  /* Lines scrolled past fade under the bar, so a cut line reads as
     scrollback rather than as a fragment. */
  mask-image: linear-gradient(transparent, #000 18px);
  padding: 12px 14px 14px;
}
/* A wrapped line hangs under its text, not under its marker. */
.ln {
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  padding-left: 4ch;
  text-indent: -4ch;
}
.stage.live .ln {
  animation: line-in 280ms cubic-bezier(0.16, 1, 0.3, 1) both;
}
@keyframes line-in {
  from {
    opacity: 0.2;
    transform: translateY(4px);
  }
}
/* The terminal keeps the colours it had (the author, 2026-10-18): lime for
   the prompt and the agent's calls, teal for what finished. */
.ln .chev,
.ln .tool {
  color: var(--accent);
}
.ln .you,
.ln .ink {
  color: var(--ink);
}
.ln .ok {
  color: var(--ok);
}
/* A clause of the report that wraps as one piece. */
.ln .nb {
  white-space: nowrap;
}
/* The step the video last reached: the one lime mark in the terminal. */
.ln.now .ok {
  color: var(--accent);
}
.ln .dim {
  color: var(--ink-4);
}
.ln.typing .you::after {
  content: "";
  display: inline-block;
  width: 7px;
  height: 14px;
  margin-left: 1px;
  vertical-align: -3px;
  background: var(--accent);
  animation: caret 1.1s step-end infinite;
}
/* The caret blinks while the terminal waits for input. */
@keyframes caret {
  50% {
    opacity: 0;
  }
}
/* The screen is the desktop take's frame; each take's box is drawn inside
   it at one height and its own shape, so a switch changes only the box's
   width (spec 2026-10-24-hero-device-carousel, option B). */
.screen {
  grid-area: screen;
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 0;
  aspect-ratio: 1440 / 988;
}
/* Each take fits its screen at its own shape: the portrait ones by its
   height, the desktop one by its width, or by its height where the screen
   is short (stacked pages). */
.screen video {
  display: block;
  flex: none;
  width: auto;
  height: auto;
  max-width: 100%;
  max-height: 100%;
  object-fit: cover;
  border-radius: 8px;
  /* The take's own padding colour, behind the poster while it loads. */
  background: #e8e8eb;
  box-shadow: 0 0 0 1px var(--line);
}
/* One take shows at a time; display: block would otherwise beat [hidden]. */
.screen video[hidden],
.devices[hidden] {
  display: none;
}
/* A button per take under the stage: its device's icon and name, lit while
   it plays, with a bar that fills as it plays. */
.devices {
  display: flex;
  justify-content: center;
  gap: 28px;
  margin-top: 20px;
}
.devices button {
  display: grid;
  justify-items: center;
  gap: 8px;
  padding: 6px 4px;
  border: 0;
  border-radius: 6px;
  background: none;
  color: var(--ink-4);
  font: 400 11.5px/1 var(--mono);
  cursor: pointer;
}
.devices button:hover {
  color: var(--ink-2);
}
.devices button[aria-pressed="true"] {
  color: var(--ink);
}
.devices button:disabled {
  opacity: .4;
  cursor: not-allowed;
}
.devices .dev-icon {
  width: 20px;
  height: 20px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.5;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.devices .bar {
  display: block;
  width: 44px;
  height: 2px;
  overflow: hidden;
  border-radius: 1px;
  background: var(--line-2);
}
.devices .fill {
  display: block;
  width: 0;
  height: 100%;
  background: var(--accent);
}
/* The quiet pause button in the video's corner (WCAG 2.2.2), as Apple's
   looping videos have: no timeline, just pause and play. It sits above the
   frame's fade. */
.pause {
  position: absolute;
  z-index: 1;
  right: 12px;
  bottom: 12px;
  display: grid;
  place-items: center;
  width: 36px;
  height: 36px;
  padding: 0;
  border: 0;
  border-radius: 50%;
  /* No backdrop blur: over a playing video it costs frames (measured
     2026-10-13). A denser fill keeps the glyph legible instead. */
  background: rgba(10,10,10,.72);
  color: var(--ink);
  cursor: pointer;
  transition: background-color 160ms ease-out;
}
.pause:hover {
  background: rgba(10,10,10,.85);
}
.pause svg {
  width: 14px;
  height: 14px;
  fill: currentColor;
}
.pause .i-play,
.pause.paused .i-pause {
  display: none;
}
.pause.paused .i-play {
  display: block;
}
.pause[hidden] {
  display: none;
}

/* Labels: mono, small, uppercase (spec §3.2). */
.eyebrow {
  font-family: var(--mono);
  font-size: 11.5px;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--ink-3);
}

/* The statement: two tones at one size (spec §5.3). */
.statement {
  max-width: 1040px;
  margin: 0 auto;
  padding: 136px var(--pad) 120px;
  text-align: center;
  text-wrap: balance;
  font-size: 38px;
  line-height: 1.18;
  font-weight: 500;
  letter-spacing: -0.035em;
}

/* Three pillars in hairline cells (spec §5.4). */
.pillars {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
}
.pillar {
  padding: 28px var(--pad) 40px;
  border-right: 1px solid var(--line);
}
.pillar:last-child {
  border-right: 0;
}
.pillar .iso {
  display: block;
  width: 100%;
  height: 200px;
  margin: 32px 0 40px;
}
h3 {
  margin: 0;
  font-size: 15px;
  font-weight: 500;
  letter-spacing: -0.01em;
}
.pillar p {
  max-width: 28em;
  margin: 6px 0 0;
  font-size: 14.5px;
  color: var(--ink-2);
}

/* Section headings (spec §3.2). */
h2 {
  margin: 0;
  font-size: 44px;
  line-height: 1.05;
  font-weight: 500;
  letter-spacing: -0.04em;
}

/* The chapters (launch copy spec §3.4): a numbered head, a framed panel
   holding the chapter's drawing, and a hairline row of its features. */
.chapter {
  padding-top: 112px;
}
.ch-head {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 48px;
  padding: 0 var(--pad);
}
.ch-head .num {
  display: block;
  margin-bottom: 18px;
  font-family: var(--mono);
  font-size: 12px;
  color: var(--ink-4);
}
.ch-head .r {
  padding-top: 34px;
}
.ch-head p {
  max-width: 30em;
  margin: 0 0 18px;
  font-size: 17px;
  line-height: 1.55;
  letter-spacing: -0.012em;
  color: var(--ink-2);
}
.media {
  position: relative;
  margin: 64px var(--pad) 0;
  padding-bottom: 32px;
  overflow: hidden;
  border-radius: 12px;
  background: var(--bg-1);
  box-shadow: inset 0 0 0 1px var(--line);
}
.media.drawing {
  padding: 24px 24px 28px;
}
.media.drawing .fn {
  font-family: var(--mono);
  font-size: 11px;
  color: var(--ink-4);
}
.media.drawing .iso {
  display: block;
  width: 100%;
  height: 420px;
}
/* The drawings (launch copy spec §4): hairlines that keep their width at
   any size, faces filled with the surface they sit on, one lime idea each. */
.iso path {
  fill: none;
  stroke: var(--draw);
  stroke-width: 1;
  vector-effect: non-scaling-stroke;
  stroke-linejoin: round;
  stroke-linecap: round;
}
.iso path.f {
  fill: var(--bg);
}
.media .iso path.f {
  fill: var(--bg-1);
}
.iso path.m,
.iso .lead {
  stroke: var(--ink-4);
}
.iso path.a {
  stroke: var(--accent);
}
.iso path.d {
  stroke-dasharray: 3 3;
}
.iso .dot {
  fill: var(--ink-2);
}
.iso text {
  font-family: var(--mono);
  font-size: 9px;
  fill: var(--ink-3);
}
.iso text.at {
  fill: var(--accent);
}
.iso text.cl {
  font-size: 10.5px;
  fill: var(--ink-2);
}
/* Each drawing acts out its one lime idea once, each time it scrolls into
   view (public/figures.js sets .play), reduced motion too, and ends on the
   drawing as it stands; nothing else moves, and every play is under
   WCAG 2.2.2's 5 s. The cursors
   ride on SVG animateMotion (scripts/drawings.mjs's ride) in 2 s: act-press
   and act-arrive land after them. Dimming uses stroke-opacity so a filled
   face never shows what is behind it, and moving uses translate, which
   composes with a label's own transform attribute. */
.iso .act-press,
.iso .act-pulse {
  transform-box: fill-box;
  transform-origin: center;
}
.iso .ride,
.iso.play .still {
  display: none;
}
.iso.play .ride {
  display: inline;
}
.iso.play .act-press {
  animation: act-press 2.6s ease-in-out;
}
@keyframes act-press {
  0%, 76%, 100% { transform: scale(1); }
  86% { transform: scale(0.9); }
}
.iso.play .act-arrive {
  animation: act-arrive 2.6s ease-in-out;
}
@keyframes act-arrive {
  0%, 60% { stroke-opacity: 0.35; }
  85%, 100% { stroke-opacity: 1; }
}
.iso.play .act-beat {
  animation: act-beat 1.6s ease-in-out;
}
@keyframes act-beat {
  0%, 100% { stroke-opacity: 1; }
  50% { stroke-opacity: 0.25; }
}
.iso.play .act-march {
  animation: act-march 3s ease-in-out;
}
@keyframes act-march {
  to { stroke-dashoffset: -24; }
}
.iso.play .act-draw {
  stroke-dasharray: 1 1;
  animation: act-draw 3s ease-in-out;
}
@keyframes act-draw {
  0% { stroke-dashoffset: 1; }
  55%, 100% { stroke-dashoffset: 0; }
}
.iso.play .act-head {
  animation: act-head 3s ease-in-out;
}
@keyframes act-head {
  0%, 50% { stroke-opacity: 0; }
  60%, 100% { stroke-opacity: 1; }
}
.iso.play .act-blink {
  animation: act-blink 3s ease-in-out;
}
@keyframes act-blink {
  0%, 62%, 74%, 86%, 100% { stroke-opacity: 1; }
  68%, 80% { stroke-opacity: 0.2; }
}
/* Along the timeline's axis, (cos 30°, sin 30°) per unit: 90 units right
   of the playhead, then 18 left, inside the zoom pill's handles. */
.iso.play .act-sweep {
  animation: act-sweep 3.6s ease-in-out;
}
@keyframes act-sweep {
  0%, 100% { translate: 0 0; }
  45% { translate: 77.94px 45px; }
  85% { translate: -15.59px -9px; }
}
.iso.play .act-lift {
  animation: act-lift 1.6s ease-in-out;
}
@keyframes act-lift {
  0%, 100% { translate: 0 0; }
  50% { translate: 0 -7px; }
}
.iso.play .act-pulse {
  animation: act-pulse 1.2s ease-in-out;
}
@keyframes act-pulse {
  0%, 100% { transform: scale(1); }
  50% { transform: scale(1.3); }
}
.subfeat {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  margin-top: 64px;
}
.subfeat.n3 {
  grid-template-columns: repeat(3, 1fr);
}
.sf {
  padding: 22px var(--pad) 26px;
  border-right: 1px solid var(--line);
  font-size: 13.5px;
  color: var(--ink-2);
}
.sf:last-child {
  border-right: 0;
}
.sf b {
  display: block;
  margin-bottom: 4px;
  font-weight: 500;
  color: var(--ink);
}

/* The changelog row (spec §5.6): hairline cells, the newest dot lime. */
.changelog {
  padding-top: 112px;
}
.changelog .head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  padding: 0 var(--pad);
}
.cl-row {
  position: relative;
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  margin-top: 48px;
}
.cl-row .entry {
  position: relative;
  padding: 24px var(--pad) 32px;
  border-right: 1px solid var(--line);
}
.cl-row .entry:last-child {
  border-right: 0;
}
.cl-row .entry::before {
  content: "";
  position: absolute;
  top: -4px;
  left: var(--pad);
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--ink-4);
}
.cl-row .entry:first-child::before {
  background: var(--accent);
}
.cl-row h3 {
  margin-bottom: 6px;
}
.cl-row p {
  margin: 0 0 14px;
  font-size: 13.5px;
  line-height: 1.5;
  color: var(--ink-2);
}
.cl-row time {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--ink-4);
}

/* The closing (spec §5.7). */
.closing {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 30px;
  padding: 168px var(--pad) 160px;
  text-align: center;
}
.closing h2 {
  font-size: 60px;
}
/* Centred without a transform: the note's rise animation owns transform. */
.closing .install-note {
  left: 0;
  right: 0;
  text-align: center;
}

/* The footer. */
/* Horizontal separators reach the viewport edges; their content stays
   aligned with the rest of the page. */
.cl-row::before,
footer.site-foot::before,
footer.site-foot .base::before {
  content: "";
  position: absolute;
  top: 0;
  left: calc(50% - 50vw);
  width: 100vw;
  border-top: 1px solid var(--line);
  pointer-events: none;
}
footer.site-foot .cols {
  display: grid;
  grid-template-columns: 2fr 1fr 1fr 1fr;
  gap: 24px;
  padding: 56px var(--pad) 72px;
  font-size: 13.5px;
}
footer.site-foot .figure {
  height: 30px;
}
footer.site-foot h2 {
  margin: 0 0 14px;
  font-size: 13.5px;
  font-weight: 500;
  letter-spacing: -0.01em;
}
footer.site-foot ul {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 9px;
  color: var(--ink-3);
}
footer.site-foot ul a:hover {
  color: var(--ink);
}
footer.site-foot .base {
  position: relative;
  display: flex;
  justify-content: space-between;
  padding: 20px var(--pad);
  color: var(--ink-4);
  font-size: 12.5px;
}
footer.site-foot .base a:hover {
  color: var(--ink);
}

/* /privacy: the policy as one readable column. */
.doc {
  max-width: 720px;
  padding: 96px var(--pad) 112px;
  font-size: 15px;
  line-height: 1.7;
  color: var(--ink-2);
}
.doc h1 {
  margin: 0 0 24px;
  color: var(--ink);
  font-size: 44px;
  line-height: 1.05;
  font-weight: 500;
  letter-spacing: -0.04em;
}
.doc h2 {
  margin: 48px 0 12px;
  color: var(--ink);
  font-size: 20px;
  font-weight: 500;
  letter-spacing: -0.02em;
}
.doc p,
.doc ul {
  margin: 0 0 16px;
}
.doc ul {
  padding-left: 20px;
}
.doc li + li {
  margin-top: 6px;
}
.doc strong {
  color: var(--ink);
  font-weight: 500;
}
.doc a {
  color: var(--ink);
  text-decoration: underline;
  text-decoration-color: var(--line-2);
  text-underline-offset: 3px;
}
.doc a:hover {
  text-decoration-color: var(--ink);
}
.doc code {
  font-family: var(--mono);
  font-size: 13px;
  padding: 2px 6px;
  border-radius: 4px;
  background: var(--bg-1);
  box-shadow: inset 0 0 0 1px var(--line);
}

/* The Worker's page for a form sent without script (src/worker.js). */
.answer {
  max-width: 640px;
  margin: 0 auto;
  padding: 120px var(--pad);
  font-size: 17px;
}
.answer .said {
  margin: 0 0 16px;
  color: var(--ink);
}
.answer .said.err {
  color: var(--ink-2);
}
.answer a {
  color: var(--ink);
  text-decoration: underline;
  text-underline-offset: 3px;
}

@media (max-width: 860px) {
  :root {
    --pad: 20px;
  }
  nav.main,
  .gh span {
    display: none;
  }
  .home .word {
    top: 0;
  }
  footer.site-foot .cols {
    grid-template-columns: 1fr 1fr;
  }
  .doc h1 {
    font-size: 34px;
  }
}

/* Phones and tablets: the video, then the terminal at its last lines. The
   terminal adds no height of its own past 152px, so the stage stays one
   video tall plus a few lines of the run (the phone story in spec §8). */
@media (max-width: 1079px) {
  .stage {
    grid-template-columns: minmax(0, 1fr);
    grid-template-areas: "screen" "term";
    grid-template-rows: auto 152px;
  }
  .term {
    min-height: 152px;
  }
  /* One height for every take: the phone take at 55% of the stage's width
     (the stage is the viewport less the page's gutters; 728/1440 is the
     phone take's shape), but never taller than 60% of the window. */
  .screen {
    aspect-ratio: auto;
    height: min(calc((100vw - 2 * var(--pad)) * .55 * 1440 / 728), 60vh);
  }
}
@media (max-width: 860px) {
  .hero {
    padding-top: 56px;
  }
  .statement {
    padding: 88px var(--pad) 80px;
    font-size: 26px;
  }
  .pillars {
    grid-template-columns: 1fr;
  }
  .pillar {
    border-right: 0;
    border-bottom: 1px solid var(--line);
  }
  .pillar:last-child {
    border-bottom: 0;
  }
  h2 {
    font-size: 34px;
  }
  .closing h2 {
    font-size: 40px;
  }
  .closing {
    padding: 112px var(--pad) 104px;
  }
  .changelog,
  .chapter {
    padding-top: 80px;
  }
  .ch-head {
    grid-template-columns: 1fr;
    gap: 16px;
  }
  .ch-head .r {
    padding-top: 0;
  }
  .media {
    margin-top: 40px;
    padding-bottom: 16px;
  }
  .media.drawing .iso {
    height: auto;
    max-height: 420px;
  }
  .subfeat {
    grid-template-columns: 1fr 1fr;
  }
  .sf:nth-child(2) {
    border-right: 0;
  }
  .sf:nth-child(-n + 2) {
    border-bottom: 1px solid var(--line);
  }
  .subfeat.n3 {
    grid-template-columns: 1fr;
  }
  .subfeat.n3 .sf {
    border-right: 0;
  }
  .subfeat.n3 .sf:not(:last-child) {
    border-bottom: 1px solid var(--line);
  }
  .changelog .head {
    flex-direction: column;
    gap: 12px;
  }
  .cl-row {
    grid-template-columns: 1fr;
  }
  .cl-row .entry {
    border-right: 0;
    border-bottom: 1px solid var(--line);
  }
  h1 {
    font-size: 44px;
  }
}
/* Below 680px a chapter's drawing shrinks until its labels would draw
   under 9px (Studio's at 8.43px at 641px); the feature row names the same
   things. */
@media (max-width: 680px) {
  .iso .callout {
    display: none;
  }
}
/* Phones: the works-with row drops its label and never wraps a name onto a
   second line. */
@media (max-width: 640px) {
  .works {
    flex-wrap: nowrap;
    gap: 14px;
  }
  .works .wl {
    display: none;
  }
  .cmd {
    width: 100%;
  }
  .install {
    flex: 1 1 100%;
  }
  .plat {
    margin-left: 2px;
  }
}
/* Under 360px the four names no longer fit beside their marks: they stay
   for screen readers, and the marks alone show. */
@media (max-width: 359px) {
  .works .nm {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
  }
}
@media (prefers-reduced-motion: reduce) {
  .stage.live .ln,
  .install-note:not(:empty) {
    animation: none;
  }
  /* The drawings play on: each action is small, plays once and is over in
     under 5 s (the author's call, 2026-10-22). */
  /* The icons still cross-fade; nothing scales, blurs or draws. */
  .copy,
  .copy:active {
    transform: none;
  }
  .copy svg,
  .copy .i-done,
  .copy.done .i-copy {
    transform: none;
    filter: none;
  }
  .copy .i-done path {
    stroke-dashoffset: 0;
    transition: none;
  }
}

/* Studio: two real editor clips above the static feature row. */
.editor-showcase {
  margin: 48px var(--pad) 0;
  border: 1px solid var(--line-2);
  border-radius: 12px;
  overflow: hidden;
  background: var(--bg-1);
}
.editor-screen {
  position: relative;
  aspect-ratio: 8 / 5;
  background: var(--bg);
}
.editor-screen video {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: contain;
}
.editor-screen video[hidden],
.editor-showcase [hidden] {
  display: none;
}
@media (max-width: 700px) {
  .editor-showcase {
    margin-top: 32px;
  }
}
