@font-face {
  font-family: "Silkscreen";
  src: url("fonts/silkscreen-latin.woff2") format("woff2");
  font-display: swap;
}

:root {
  --bg: #faf9f5;
  --surface: #f0eee6;
  --surface-2: #e8e6dc;
  --ink: #141413;
  --muted: #5e5d59;
  --line: #d9d6ca;
  --accent: #d97757;
  --accent-ink: #b85c3d;
  --shelf: #c9c4b3;
  --code-bg: #1f1e1d;
  --code-ink: #f0eee6;
  --pixel: "Silkscreen", ui-monospace, monospace;
  --sans: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  --mono: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  color-scheme: light;
}

/* The page follows the system until someone presses the theme button, which pins a choice in
   `data-theme`. The dark palette is written out twice because a media query cannot be combined with
   an attribute selector into one rule, and both routes have to arrive at the same colours. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --bg: #1a1918;
    --surface: #242321;
    --surface-2: #2e2d2a;
    --ink: #f0eee6;
    --muted: #a8a59b;
    --line: #3a3935;
    --accent: #e08a6c;
    --accent-ink: #f0a488;
    --shelf: #4a4843;
    --code-bg: #0f0f0e;
    --code-ink: #f0eee6;
    color-scheme: dark;
  }
}

:root[data-theme="dark"] {
  --bg: #1a1918;
  --surface: #242321;
  --surface-2: #2e2d2a;
  --ink: #f0eee6;
  --muted: #a8a59b;
  --line: #3a3935;
  --accent: #e08a6c;
  --accent-ink: #f0a488;
  --shelf: #4a4843;
  --code-bg: #0f0f0e;
  --code-ink: #f0eee6;
  color-scheme: dark;
}

* { box-sizing: border-box; }

/* Jumping to a section leaves its heading clear of the sticky bar rather than under it. */
html { scroll-behavior: smooth; scroll-padding-top: 64px; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font: 17px/1.6 var(--sans);
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

a { color: inherit; }

code { font-family: var(--mono); font-size: 0.88em; }
p code, li code { background: var(--surface-2); padding: 0.1em 0.35em; border-radius: 4px; }

canvas.crawler { image-rendering: pixelated; display: block; }

h1, h2, h3 { line-height: 1.1; margin: 0; }
h1, h2 { font-family: var(--pixel); font-weight: 400; letter-spacing: 0.01em; }
h1 { font-size: clamp(2.6rem, 7vw, 4.6rem); }
h2 { font-size: clamp(1.7rem, 4vw, 2.4rem); }
h3 { font-size: 1.05rem; font-weight: 650; }

/* ---------- nav ---------- */

/* The bar sits on the page's own column, so the brand lines up with the headings under it. It is
   flat at the top of the page and only takes its rule once something has scrolled beneath it. */
.nav {
  position: sticky;
  top: 0;
  z-index: 10;
  background: color-mix(in srgb, var(--bg) 88%, transparent);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  border-bottom: 1px solid transparent;
  transition: border-color 0.2s ease;
}
.nav.scrolled, .nav.open { border-bottom-color: var(--line); }
.nav-inner {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: 24px;
  max-width: 1120px;
  height: 64px;
  margin: 0 auto;
  padding: 0 16px;
}
.brand {
  display: flex;
  align-items: center;
  gap: 10px;
  justify-self: start;
  font-family: var(--pixel);
  font-size: 0.95rem;
  text-decoration: none;
  white-space: nowrap;
}
.nav-links { display: flex; align-items: center; gap: 4px; font-size: 0.95rem; }
.nav-links a {
  position: relative;
  padding: 8px 12px;
  color: var(--muted);
  text-decoration: none;
  white-space: nowrap;
  transition: color 0.15s ease;
}
.nav-links a:hover, .nav-links a[aria-current="true"] { color: var(--ink); }
.nav-links a.nav-pro { color: var(--accent-ink); font-weight: 600; }
/* The section in view is marked with a short square bar, like the pixel font, rather than a pill. */
.nav-links a::after {
  content: "";
  position: absolute;
  left: 12px;
  right: 12px;
  bottom: 0;
  height: 2px;
  background: var(--accent);
  transform: scaleX(0);
  transition: transform 0.2s ease;
}
.nav-links a[aria-current="true"]::after { transform: scaleX(1); }
.nav-actions { display: flex; align-items: center; justify-content: flex-end; gap: 8px; }
.icon-button {
  display: grid;
  place-items: center;
  width: 36px;
  height: 36px;
  padding: 0;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: none;
  color: var(--muted);
  cursor: pointer;
}
.icon-button:hover { color: var(--ink); border-color: var(--muted); }
.icon-button svg { width: 18px; height: 18px; }
/* Show the theme the button switches to, not the one already on screen. */
.theme-toggle .to-light { display: none; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .theme-toggle .to-light { display: block; }
  :root:not([data-theme="light"]) .theme-toggle .to-dark { display: none; }
}
:root[data-theme="dark"] .theme-toggle .to-light { display: block; }
:root[data-theme="dark"] .theme-toggle .to-dark { display: none; }
.menu-toggle { display: none; }
.menu-toggle .cross, .nav.open .menu-toggle .bars { display: none; }
.nav.open .menu-toggle .cross { display: inline; }

/* Too narrow for the links in a row: they fold into a panel under the bar, opened by the menu
   button, and the download button stays where it is. */
@media (max-width: 900px) {
  .nav-inner { grid-template-columns: 1fr auto; }
  .menu-toggle { display: grid; }
  .nav-links {
    display: none;
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    flex-direction: column;
    align-items: stretch;
    gap: 0;
    padding: 4px max(16px, calc((100vw - 1120px) / 2 + 16px)) 12px;
    background: var(--bg);
    border-bottom: 1px solid var(--line);
    font-size: 1.05rem;
  }
  .nav.open .nav-links { display: flex; }
  .nav-links a { padding: 14px 0; border-top: 1px solid var(--line); }
  .nav-links a:first-child { border-top: 0; }
  .nav-links a::after { left: auto; right: 0; top: 50%; bottom: auto; width: 8px; height: 8px; transform: translateY(-50%) scale(0); }
  .nav-links a[aria-current="true"]::after { transform: translateY(-50%) scale(1); }
}
@media (max-width: 440px) {
  .nav-inner { gap: 8px; }
  .brand { font-size: 0.78rem; gap: 8px; }
  .nav-actions { gap: 6px; }
}
@media (max-width: 360px) {
  .brand span { display: none; }
}

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

.button {
  display: inline-block;
  padding: 12px 20px;
  border-radius: 8px;
  background: var(--ink);
  color: var(--bg);
  text-decoration: none;
  font-weight: 600;
  border: 1px solid var(--ink);
  transition: transform 0.15s ease, background 0.15s ease;
}
.button:hover { transform: translateY(-2px); }
.button.ghost { background: transparent; color: var(--ink); border-color: var(--line); }
.button.ghost:hover { border-color: var(--ink); }
.button.small { padding: 6px 14px; font-size: 0.9rem; }

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

.hero {
  padding: clamp(48px, 9vw, 96px) 0 0;
}
.hero-copy {
  max-width: 1120px;
  margin: 0 auto;
  padding: 0 16px;
}
.eyebrow {
  font-family: var(--pixel);
  color: var(--accent-ink);
  margin: 0 0 16px;
  font-size: 0.9rem;
}
/* The headline's character, between the arrows that step through the cast. Everything here is set
   in the headline's pixel face, so the controls read as part of the word rather than sat on it. */
.character-pick { display: inline-flex; align-items: baseline; white-space: nowrap; }
.character-name { color: var(--accent-ink); }
/* A slot holds every word it can show stacked in one grid cell, so it is always as wide as the
   longest of them and the arrows around the name never move. Only the current word is visible; the
   one it replaces slides out sideways, clipped to the slot, while the new one slides in. `clip`
   rather than `hidden` keeps the slot on the headline's baseline. */
.word-slot { display: inline-grid; justify-items: center; overflow-x: clip; }
.article-slot { justify-items: start; }
.word-slot > .word { grid-area: 1 / 1; visibility: hidden; }
.word-slot > .word.current, .word-slot > .word.leaving { visibility: visible; }
.pick-arrow {
  font: inherit;
  padding: 0 0.12em;
  border: 0;
  background: none;
  color: var(--muted);
  cursor: pointer;
}
.pick-arrow:hover { color: var(--accent-ink); }
.character-pick.single .pick-arrow, .character-pick.single ~ .shuffle { display: none; }
.shuffle {
  margin-top: 20px;
  padding: 8px 12px;
  border: 2px solid var(--line);
  border-radius: 0;
  background: transparent;
  color: var(--muted);
  font: 400 0.85rem var(--pixel);
  cursor: pointer;
}
.shuffle:hover { color: var(--ink); border-color: var(--ink); }
.pick-arrow:focus-visible, .shuffle:focus-visible, .character-tile:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

.character-row { display: flex; gap: 8px; flex-wrap: wrap; }
.character-tile {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  padding: 6px 8px;
  border: 2px solid var(--line);
  border-radius: 10px;
  background: transparent;
  color: var(--muted);
  font: 600 0.8rem var(--sans);
  text-transform: capitalize;
  cursor: pointer;
}
.character-tile[aria-pressed="true"] { border-color: var(--ink); color: var(--ink); background: var(--surface-2); }

.lede { font-size: 1.2rem; color: var(--muted); max-width: 40em; margin: 24px 0 32px; }
.actions { display: flex; gap: 12px; flex-wrap: wrap; }
.platforms { color: var(--muted); font-size: 0.9rem; margin-top: 20px; }

.shelf-wrap { margin: 24px 0 0; }
.shelf {
  position: relative;
  /* Tall enough for a crawler's question and its buttons over another crawler's bubble. */
  height: 300px;
  border-bottom: 6px solid var(--shelf);
  background: linear-gradient(to bottom, transparent 0%, var(--surface) 100%);
  overflow: hidden;
  cursor: pointer;
}
#shelf-canvas { position: absolute; inset: 0; width: 100%; height: 100%; image-rendering: pixelated; }
#shelf-labels { position: absolute; inset: 0; pointer-events: none; }
.shelf-wrap figcaption {
  text-align: center;
  font-size: 0.85rem;
  color: var(--muted);
  padding: 12px 16px 0;
}

/* Bubbles are the app's own (`src/lib/overlay/Bubble.svelte`): paper for a crawler at work, its own
   colour when it needs you, red for a destructive command. No tail: a bubble that had to move out of
   another's way draws a stem back to its crawler instead, on the canvas. `site/bubbles.js` sizes each
   box for its whole text, so nothing grows while the letters type in. */
.bubble-box { position: absolute; }
.bubble {
  --crawlers-mono: ui-monospace, "SF Mono", SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace;
  display: block;
  width: 100%;
  padding: 6px 10px;
  border-radius: 10px;
  font: 500 12px/1.3 var(--crawlers-mono);
  text-align: left;
  box-shadow: 0 2px 8px rgba(20, 20, 19, 0.25);
}
.bubble-header {
  display: flex;
  justify-content: space-between;
  gap: 8px;
  font-size: 9px;
  line-height: 12px;
  font-weight: 700;
  text-transform: uppercase;
  white-space: nowrap;
  overflow: hidden;
}
.bubble-header > span { opacity: 0.8; overflow: hidden; text-overflow: ellipsis; }
.bubble-tokens { flex: none; font-variant-numeric: tabular-nums; }
/* `.bubble-tokens-alarm` in `Bubble.svelte`: a context 80% full trembles until it is compacted. */
.bubble-header > .bubble-tokens-measured { opacity: 1; }
.bubble-tokens-alarm { animation: bubble-tokens-shake 280ms linear infinite; }
@keyframes bubble-tokens-shake {
  0%, 100% { transform: translate(0, 0); }
  25% { transform: translate(0.7px, -0.5px); }
  50% { transform: translate(-0.6px, 0.5px); }
  75% { transform: translate(0.5px, 0.6px); }
}
.bubble-text {
  display: -webkit-box;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  text-overflow: ellipsis;
  overflow-wrap: anywhere;
}
.bubble-command {
  display: block;
  font-size: 9px;
  line-height: 12px;
  opacity: 0.7;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.bubble-stats {
  display: flex;
  gap: 8px;
  font-size: 10px;
  line-height: 13px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  overflow: hidden;
}
/* A sleeping crawler's dream (`.bubble-dream` in `Bubble.svelte`): the box goes clear and a comic-book
   cloud drawn to its size (`cloudPath`) sits behind the words, its puffs bulging a few pixels past it. */
.bubble-dream { padding: 10px 16px; position: relative; isolation: isolate; background: transparent; box-shadow: none; }
.bubble-dream .bubble-text { font-style: italic; white-space: nowrap; }
.bubble-cloud {
  position: absolute;
  left: 0;
  top: 0;
  z-index: -1;
  overflow: visible;
  filter: drop-shadow(0 2px 3px rgba(20, 20, 19, 0.22));
}
.bubble-cloud path, .bubble-cloud circle { stroke-width: 1.1; stroke-opacity: 0.5; stroke-linejoin: round; }
.bubble-trail { animation: bubble-drift 2.6s ease-in-out infinite; }
@keyframes bubble-drift { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-1px); } }
@media (prefers-reduced-motion: reduce) {
  .bubble-trail, .bubble-tokens-alarm { animation: none; }
}
.bubble-small { padding: 5px 9px; font-size: 11px; }
/* `.bubble-ringed` in `Bubble.svelte`: a subagent's edge in its crawler's colour, inset so the box stays sized. */
.bubble-ringed { box-shadow: inset 0 0 0 2px var(--bubble-ring), 0 2px 8px rgba(20, 20, 19, 0.25); }
.bubble-danger {
  position: relative;
  box-shadow: 0 0 0 1.5px #f2c94c, 0 2px 10px rgba(181, 57, 47, 0.55);
  animation: bubble-shake 420ms ease-in-out;
}
.bubble-warning {
  position: absolute;
  top: -7px;
  left: -7px;
  width: 14px;
  height: 14px;
  border-radius: 50%;
  background: #f2c94c;
  color: #141413;
  font: 800 10px/14px var(--crawlers-mono);
  text-align: center;
}
@keyframes bubble-shake {
  0%, 100% { transform: translateX(0); }
  15%, 55% { transform: translateX(-3px) rotate(-1deg); }
  35%, 75% { transform: translateX(3px) rotate(1deg); }
}
@media (prefers-reduced-motion: reduce) {
  .bubble-danger { animation: none; }
}

/* A bubble's letters (`src/lib/overlay/Glyphs.svelte`): each fades, grows and rises into place, then
   keeps its bubble's mood. The mood loops on the outer box so it composes with the entrance on the
   inner one, and a negative delay by position sends it along the line. */
.glyphs-heard { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
.word { white-space: nowrap; }
.glyph, .glyph-in { display: inline-block; }
.glyph-in { transform-origin: 50% 100%; animation: glyph-enter 180ms cubic-bezier(0.34, 1.56, 0.64, 1) var(--delay) both; }
@keyframes glyph-enter {
  from { opacity: 0; transform: translateY(4px) scale(0.6); }
  to { opacity: 1; transform: none; }
}
.glyph-wave { animation: glyph-wave 2s ease-in-out calc(var(--i) * -140ms) infinite; }
@keyframes glyph-wave { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-2px); } }
.glyph-shake { animation: glyph-shake 280ms linear calc(var(--i) * -71ms) infinite; }
@keyframes glyph-shake {
  0%, 100% { transform: translate(0, 0); }
  25% { transform: translate(0.6px, -0.4px); }
  50% { transform: translate(-0.5px, 0.5px); }
  75% { transform: translate(0.4px, 0.6px); }
}
.glyph-bounce { animation: glyph-bounce 1.4s ease-in-out calc(var(--i) * -60ms) infinite; }
@keyframes glyph-bounce { 0%, 50%, 100% { transform: translateY(0); } 25% { transform: translateY(-2.5px); } }
/* A dangerous command's damage (`dangerRuns`): the program trembles in bold, the flag is bold, and what
   it hits flickers between yellow and amber. */
.glyph-tremble { animation: glyph-tremble 170ms linear calc(var(--i) * -43ms) infinite; }
@keyframes glyph-tremble {
  0%, 100% { transform: translate(0, 0) rotate(0); }
  25% { transform: translate(1.2px, -0.8px) rotate(4deg); }
  50% { transform: translate(-1px, 0.6px) rotate(-3deg); }
  75% { transform: translate(0.8px, 1px) rotate(2deg); }
}
.glyph-bold { font-weight: 800; }
.glyph-alarm { color: #ffd84d; animation: glyph-flicker 2.2s steps(1) calc(var(--i) * -310ms) infinite; }
.glyph-in.glyph-alarm {
  animation: glyph-enter 180ms cubic-bezier(0.34, 1.56, 0.64, 1) var(--delay) both, glyph-flicker 2.2s steps(1) calc(var(--i) * -310ms) infinite;
}
@keyframes glyph-flicker {
  0%, 42%, 50%, 100% { color: #ffd84d; }
  44%, 48% { color: #ffb347; }
}
@media (prefers-reduced-motion: reduce) {
  .glyph, .glyph-in, .glyph-alarm, .glyph-in.glyph-alarm { animation: none; }
}

/* An asking crawler's bubble (`src/lib/overlay/AskBubble.svelte`): a crawler bubble a size up, with its
   buttons in the bubble's own ink so they read on whatever colour the crawler wears. `askBox` in
   `site/bubbles.js` sizes it, so the two are read together. */
.ask {
  --crawlers-mono: ui-monospace, "SF Mono", SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace;
  display: block;
  width: 100%;
  padding: 10px 14px;
  border-radius: 12px;
  font: 500 13px/1.3 var(--crawlers-mono);
  text-align: left;
  box-shadow: 0 2px 8px rgba(20, 20, 19, 0.25);
  pointer-events: auto;
  cursor: default;
}
.ask-header {
  display: flex;
  justify-content: space-between;
  gap: 8px;
  font-size: 9px;
  line-height: 12px;
  font-weight: 700;
  text-transform: uppercase;
  opacity: 0.8;
  white-space: nowrap;
  overflow: hidden;
}
.ask-header > span { overflow: hidden; text-overflow: ellipsis; }
.ask-text {
  display: -webkit-box;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  text-overflow: ellipsis;
  overflow-wrap: anywhere;
}
.ask-row { display: flex; gap: 8px; margin-top: 6px; }
.ask-button {
  flex: 1 1 auto;
  min-width: 0;
  height: 27px;
  padding: 5px 12px;
  border: 1.5px solid var(--ink);
  border-radius: 7px;
  background: var(--ink);
  color: var(--fill);
  font: 700 13px/17px var(--crawlers-mono);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  cursor: pointer;
}
.ask-button.ask-quiet { background: transparent; color: var(--ink); }
.ask-button:focus-visible { outline: 2px solid #f2c94c; outline-offset: 1px; }
.ask-danger {
  position: relative;
  box-shadow: 0 0 0 1.5px #f2c94c, 0 2px 10px rgba(181, 57, 47, 0.55);
  animation: bubble-shake 420ms ease-in-out;
}
.ask-warning {
  position: absolute;
  top: -7px;
  left: -7px;
  width: 14px;
  height: 14px;
  border-radius: 50%;
  background: #f2c94c;
  color: #141413;
  font: 800 10px/14px var(--crawlers-mono);
  text-align: center;
}
@media (prefers-reduced-motion: reduce) {
  .ask-danger { animation: none; }
}

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

/* Each section opens on a rule the full width of the window, with its name above the heading, so
   it is plain where one part of the page ends and the next begins. */
.section {
  position: relative;
  max-width: 1120px;
  margin: clamp(64px, 10vw, 112px) auto 0;
  padding: clamp(48px, 7vw, 80px) 16px 0;
}
.section::before {
  content: "";
  position: absolute;
  top: 0;
  left: 50%;
  width: 100vw;
  transform: translateX(-50%);
  border-top: 1px solid var(--line);
}

/* The Pro feature sits on a band of its own, tinted with the accent and edged in it, so it cannot be
   read as part of the free app. The band replaces the rule above it and the one after it. */
.section.pro-band { isolation: isolate; padding-bottom: clamp(48px, 7vw, 80px); }
.section.pro-band::before {
  z-index: -1;
  bottom: 0;
  background: color-mix(in srgb, var(--accent) 7%, var(--bg));
  border-top: 2px solid var(--accent);
  border-bottom: 2px solid var(--accent);
}
.pro-band + .section::before { display: none; }
#settings .eyebrow, #settings .pro-note, #speech .eyebrow, #speech .pro-note { color: var(--accent-ink); }
.pro-note { font-weight: 600; margin: 0 0 8px; }
.pro-actions { margin-top: 28px; }
.button.pro { background: var(--accent-ink); border-color: var(--accent-ink); color: var(--bg); }
.config { position: relative; }
.config-badge { position: absolute; top: 16px; right: 16px; }
.section-lede { color: var(--muted); font-size: 1.1rem; max-width: 42em; margin: 16px 0 40px; }

.cards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
  gap: 16px;
}
.card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 14px;
  padding: 24px;
}
.card canvas.crawler, .leash-demo { height: 100px; margin-bottom: 20px; }
.card canvas.crawler { width: auto; }
.card p { color: var(--muted); font-size: 0.95rem; margin: 8px 0 0; }
/* The whole cast gets a row of its own under the four moods, the tiles beside what they are for. */
.card-wide { grid-column: 1 / -1; display: flex; align-items: center; gap: 24px; flex-wrap: wrap; }
.card-wide > div:last-child { flex: 1 1 260px; }
.card .character-row { align-items: flex-end; }
.card .character-row canvas.crawler { height: auto; margin: 0; }
.leash-demo { display: flex; align-items: flex-end; gap: 18px; position: relative; }
.leash-demo canvas.crawler { margin: 0; height: auto; }
.leash-demo::before {
  content: "";
  position: absolute;
  left: 60px;
  width: 70px;
  bottom: 30px;
  height: 20px;
  border-bottom: 2px dashed var(--muted);
  border-radius: 0 0 50% 50%;
  opacity: 0.6;
}

.pipeline {
  list-style: none;
  padding: 0;
  margin: 0 0 40px;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
}
.pipeline li { display: flex; align-items: center; gap: 8px; }
.pipeline .node {
  display: flex;
  flex-direction: column;
  padding: 14px 18px;
  border-radius: 10px;
  background: var(--code-bg);
  color: var(--code-ink);
  font: 0.95rem var(--mono);
}
.pipeline .node code { color: var(--accent); background: none; padding: 0; }
.pipeline small { color: #a8a59b; font-size: 0.75rem; }
.pipeline .edge {
  font: 0.75rem var(--pixel);
  color: var(--muted);
  display: flex;
  align-items: center;
  gap: 6px;
}
.pipeline .edge::after { content: "→"; font: 1.3rem var(--sans); color: var(--accent); }

.facts { display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); gap: 32px; }
.facts p { color: var(--muted); font-size: 0.95rem; margin: 8px 0 0; }

/* The speech section's own crawler, saying one of a few tagged lines above its head: the bubble is
   the site's own (`site/bubbles.js`), just placed in normal flow instead of packed onto a shelf. */
.speech-demo { display: flex; flex-direction: column; align-items: center; gap: 18px; margin: 8px 0 40px; }
.speech-bubble { width: 100%; max-width: 340px; }

.split { display: grid; grid-template-columns: 1.1fr 1fr; gap: clamp(24px, 5vw, 64px); align-items: start; }
@media (max-width: 800px) { .split { grid-template-columns: 1fr; } }
#settings p { color: var(--muted); }
#settings h2 { margin-bottom: 16px; }
#start h2 { margin-bottom: 40px; }

.swatches { display: flex; gap: 10px; flex-wrap: wrap; margin-top: 24px; }
.swatch {
  width: 36px;
  height: 36px;
  border-radius: 8px;
  border: 2px solid transparent;
  outline: 2px solid transparent;
  cursor: pointer;
  padding: 0;
}
.swatch[aria-checked="true"] { outline-color: var(--ink); outline-offset: 2px; }

.config {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 14px;
  padding: 28px;
  display: flex;
  flex-direction: column;
  align-items: center;
}
.config canvas.crawler { margin-bottom: 20px; }
.config pre {
  margin: 0;
  width: 100%;
  background: var(--code-bg);
  color: var(--code-ink);
  border-radius: 10px;
  padding: 18px 20px;
  overflow-x: auto;
  font-size: 0.95rem;
  line-height: 1.55;
}
.config .file-name { font: 0.8rem var(--pixel); color: var(--muted); margin: 10px 0 0; }
.json-key { color: #9a86c6; }
.json-str { color: #e08a6c; }

.steps { margin: 0; padding-left: 1.3em; }
.steps li { margin-bottom: 12px; color: var(--muted); }
.steps b { color: var(--ink); }
/* One row per system in a card, like the project file beside it. The row for the visitor's own
   system, if `main.js` can tell, moves to the top in the solid button style; the rest stay quiet. */
.downloads {
  display: flex;
  flex-direction: column;
  gap: 10px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 14px;
  padding: 28px;
}
.downloads-note { order: -2; font: 0.8rem var(--pixel); color: var(--muted); margin: 0 0 6px; }
.download {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 14px 18px;
  border: 1px solid var(--line);
  border-radius: 8px;
  color: var(--ink);
  text-decoration: none;
  font-weight: 600;
  transition: transform 0.15s ease, border-color 0.15s ease;
}
.download:hover { transform: translateY(-2px); border-color: var(--ink); }
.download-os { font-family: var(--pixel); font-weight: 400; }
.download-go { font-size: 0.9rem; color: var(--muted); }
.download-go::after { content: " ↓"; }
.download.recommended { order: -1; padding: 18px 20px; background: var(--ink); border-color: var(--ink); color: var(--bg); }
.download.recommended .download-go { color: inherit; }

/* ---------- pricing ---------- */

.pro-badge {
  display: inline-block;
  padding: 2px 8px;
  border-radius: 999px;
  background: var(--accent);
  color: #fff;
  font: 0.7rem/1.5 var(--pixel);
  vertical-align: middle;
}
.plans {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: 16px;
  max-width: 820px;
}
.plan {
  display: flex;
  flex-direction: column;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 14px;
  padding: 28px;
}
.plan.featured { border: 2px solid var(--accent); }
.plan h3 { display: flex; align-items: center; gap: 10px; }
.price { font: 2.4rem/1.1 var(--pixel); margin: 16px 0; }
.price small { display: block; font: 0.85rem/1.4 var(--sans); color: var(--muted); margin-top: 6px; }
.plan ul { margin: 0 0 24px; padding-left: 1.2em; color: var(--muted); flex: 1; }
.plan li { margin-bottom: 8px; }
.plan .button { text-align: center; }

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

.footer {
  margin-top: clamp(80px, 12vw, 140px);
  padding: 0 16px 48px;
  text-align: center;
  color: var(--muted);
  font-size: 0.9rem;
}
.footer-shelf { height: 6px; background: var(--shelf); margin: 0 -16px 32px; }
.footer p { margin: 4px 0; }

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  .button, .download { transition: none; }
}
