/* FailEcho — live failure intelligence for autonomous software.
   Graphite ground, warm off-white type, brand red used sparingly: logo,
   primary action, and genuine failure signal. The live data is the visual
   interest; nothing else competes with it.
   System fonts only, no build step, no framework. */

:root {
  --bg:        #000000;   /* black, all the way down */
  --bg-deep:   #000000;   /* header and footer are the same ground */
  --surface:   #0c1017;   /* cards lift off it rather than sit in it */
  --surface-2: #141922;
  --line:      #1e2430;
  --line-2:    #2c3440;

  --ink:   #edeae3;   /* warm off-white against the cool ground */
  --ink-2: #9aa2af;
  --ink-3: #8a94a3;

  --red:      #f83030;   /* sampled from the mark */
  --red-deep: #c00010;
  --red-dim:  rgba(248, 48, 48, 0.12);

  --healthy:  #edeae3;   /* was green: the one colour on the page that
                            belonged to no other part of it */
  --accent:   #a78bfa;   /* the terminal's successes, where red is
                            already taken by the failure above them */
  --degraded: #e0a63f;
  --major:    #f8564a;
  --unknown:  #8a8680;

  --serif: "Iowan Old Style", "Palatino Linotype", Palatino, "Book Antiqua",
    Georgia, serif;
  --sans: system-ui, -apple-system, "Segoe UI", Roboto, Arial, sans-serif;
  --mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas,
    "Liberation Mono", monospace;

  --gutter: 24px;
  --max: 1320px;
  --band: 120px;
  --r: 0px;   /* every panel is a rectangle; the corners were the only curve */
}

* { box-sizing: border-box; }

/* A class that sets `display` outranks the UA's [hidden] rule, so anything
   toggled from JS (the demo banner, the empty states) would stay visible.
   This is load-bearing: without it a production instance shows the DEMO MODE
   banner. */
[hidden] { display: none !important; }

html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font-family: var(--sans);
  font-size: 16.5px;
  line-height: 1.62;
  -webkit-font-smoothing: antialiased;
}

a { color: var(--ink); text-decoration: none; }
a:hover { color: var(--red); }

/* A link in running text has to look like one before you hover it. Underline
   anything inside a paragraph, list item or definition, then take it back off
   the places that are already obviously interactive -- navigation, buttons,
   tabs, the footer, reference rows. Underlining those makes a page of rules. */
p a, li a, dd a { text-decoration: underline; text-decoration-color: var(--line-2);
                  text-underline-offset: 3px; text-decoration-thickness: 1px; }
p a:hover, li a:hover, dd a:hover { text-decoration-color: var(--red); }
.nav a, .navpanel a, .foot-links a, .setup-jump a, .links a, .btn,
.legend a, .way-facts a { text-decoration: none; }

/* Anything leaving the site says so, once, at the end of the link text.
   Marked in the markup with rel="external" rather than matched on a hostname,
   because the origin is configuration and belongs in one place. */
a[rel~="external"]::after { content: " \2197"; font-size: .85em; color: var(--ink-3); }
a[rel~="external"]:hover::after { color: var(--red); }

:focus-visible { outline: 2px solid var(--red); outline-offset: 3px; }

.shell { max-width: var(--max); margin: 0 auto; padding-inline: var(--gutter); }

/* Full-width surface, content still on the measure. No wrapper divs. */
.band--bleed {
  max-width: none;
  padding-inline: max(var(--gutter), calc(50% - var(--max) / 2 + var(--gutter)));
  background: var(--surface);
}

.visually-hidden {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

.skip-link {
  position: absolute; left: -9999px; top: 0; z-index: 20;
  background: var(--surface); border: 1px solid var(--line-2); padding: 10px 16px;
}
.skip-link:focus { left: 14px; top: 14px; }

.muted { color: var(--ink-2); }

/* ---- labels ------------------------------------------------------------ */
.label {
  margin: 0;
  font-family: var(--serif);
  font-size: clamp(24px, 2.9vw, 32px);
  font-weight: 400;
  letter-spacing: -0.015em;
  color: var(--ink);
}
.label--quiet { font-size: 15px; font-weight: 550; color: var(--ink-2); letter-spacing: 0; }

.fineprint {
  margin: 18px 0 0;
  color: var(--ink-3);
  font-size: 13px;
  max-width: 72ch;
}
.fineprint code { color: var(--ink-2); }

/* ---- top bar ----------------------------------------------------------- */
/* Black glass, and one hairline under it. At the top of the page the line is
   not there -- the bar is the page. Once you scroll, a sharp white rule
   separates the two, and the brand gives way from the mark to the wordmark,
   which is what has to say who this is when the headline is gone. */
.topbar {
  position: sticky; top: 0; z-index: 12;
  background: rgba(0, 0, 0, 0.72);
  backdrop-filter: saturate(160%) blur(16px);
  -webkit-backdrop-filter: saturate(160%) blur(16px);
  /* margin-bottom moves with the rest of it, or the page jolts up 25px
     the instant a menu opens and then eases back down. */
  transition: background 0.22s ease, margin-bottom 0.22s ease;
}
/* No rule under the bar. The glass and the page are the same black, and the
   line was the only thing insisting they were two surfaces. */
.topbar.is-stuck { background: rgba(0, 0, 0, 0.82); }

.topbar-row {
  display: flex; align-items: center; gap: 14px 20px; flex-wrap: wrap;
  padding-block: 18px;
}

.brand { display: inline-flex; align-items: center; gap: 11px; }
.brand:hover { color: inherit; }
/* The mark and the name, side by side, at every scroll position. They used
   to trade places -- mark at the top of the page, wordmark once you had
   scrolled past the headline -- which meant the site never said its own name
   above the fold. */
.brand-mark, .brand-word { display: block; width: auto; }
.brand-mark { height: 28px; }
.brand-word { height: 19px; }

.nav {
  display: flex; align-items: center; gap: 4px;
  margin-left: auto;
  font-size: 14.5px;
}
.nav > a, .navitem > a {
  padding: 8px 12px; color: var(--ink-2);
}
.nav > a:hover, .navitem > a:hover { color: var(--ink); background: var(--surface-2); }
.nav .btn { margin-left: 10px; }
.nav-narrow { display: none; }

/* Hover reveals what each area holds, so the nav explains itself. Opens on
   focus too, so it is reachable from the keyboard. */
/* An inline span is as tall as its line box, not as tall as the padded
   link inside it, so top: 100% put the panel ten pixels up over the word
   it belongs to. Match the link's box and the panel starts below it. */
.navitem { position: relative; display: flex; align-items: center; }
.navitem > a::after {
  content: ""; display: inline-block; width: 5px; height: 5px; margin-left: 7px;
  border-right: 1.5px solid currentColor; border-bottom: 1.5px solid currentColor;
  transform: translateY(-2px) rotate(45deg);
}
/* Hovering a section pulls the whole bar down: the panel is the width of the
   window, carries the bar's own glass, and takes the white rule with it, so
   the bar reads as having grown rather than as having sprouted a dropdown. */
.navpanel {
  position: fixed; left: 0; right: 0; top: var(--bar-h, 76px); z-index: 11;
  display: grid; gap: 2px;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  width: auto; max-width: none;
  padding: 26px max(var(--gutter), calc(50% - var(--max) / 2 + var(--gutter))) 34px;
  background: rgba(0, 0, 0, 0.82);
  backdrop-filter: saturate(160%) blur(16px);
  -webkit-backdrop-filter: saturate(160%) blur(16px);
  border: 0;
  opacity: 0; visibility: hidden; transform: translateY(-10px);
  transition: opacity 0.2s ease, transform 0.2s ease, visibility 0.2s,
              top 0.22s ease;
}
/* Leaves the bar's edge as the bar starts moving, not after it stops. */
.topbar.is-open .navpanel { top: var(--bar-h-open, 101px); }
/* The bar's bottom padding is dead ground: the pointer leaves the nav item
   there, the CSS :hover that shows the panel drops, and the panel is gone
   before you have reached it. Moving fast beat it; moving slowly did not.
   This bridges that strip. It belongs to the panel, which belongs to the
   item, so the pointer never leaves the item on the way down -- and because
   a hidden panel is not hit-tested, only the open one's bridge is live.
   Sized to sit below the links, so it cannot swallow a click on one. */
.navpanel::before {
  content: ""; position: absolute; left: 0; right: 0; top: -30px; height: 30px;
}

.navpanel b { font-size: 16px; }
.navpanel span { font-size: 13.5px; }
.navitem:hover .navpanel, .navitem:focus-within .navpanel {
  opacity: 1; visibility: visible; transform: none;
}
/* Measuring both bar heights means briefly wearing the open state; that
   must not be a thing anyone sees move. */
.topbar.is-measuring, .topbar.is-measuring * { transition: none !important; }
/* The bar itself gives way: more air above and below, so the whole thing
   comes down rather than only sprouting a panel. */
.topbar-row { transition: padding 0.22s ease; }
.topbar.is-open .topbar-row { padding-block: 28px; }

/* The page does not move. The bar grew the page downward at the top of the
   document and nowhere else -- a sticky element keeps its slot in the flow
   where it started -- and moving the page deliberately, to make that
   consistent, was worse than the inconsistency: it read as motion sickness
   rather than as polish. The growth is kept out of the flow entirely, so the
   bar opens over the page and nothing under it shifts. */
.topbar.is-open { margin-bottom: calc(var(--bar-h, 76px) - var(--bar-h-open, 101px)); }
/* The mark grows into the taller bar rather than sitting in the middle of it. */
/* The brand grows by transform, not by height. Height changed the bar's
   content box, and the content box only grows once the mark passes the nav
   links -- a clamped curve, while the margin that compensates for the bar's
   growth eases linearly. The two did not cancel, and the page drifted up and
   back during every open. Scaling leaves the layout alone, so the whole of
   the bar's growth is its padding, which the margin matches exactly. */
.brand { transition: transform 0.22s ease; transform-origin: left center; }
.brand-mark, .brand-word { transition: opacity 0.22s ease; }
.topbar.is-open .brand { transform: scale(1.28); }

/* Everything below the bar goes soft while a menu is open, so the menu is
   the only thing in focus. Fixed and behind the bar, never over it. */
.scrim {
  position: fixed; inset: 0; z-index: 9;
  background: rgba(0, 0, 0, 0.45);
  backdrop-filter: blur(7px); -webkit-backdrop-filter: blur(7px);
  opacity: 0; visibility: hidden;
  transition: opacity 0.2s ease, visibility 0.2s;
}
.scrim.is-open { opacity: 1; visibility: visible; }
.navpanel a {
  display: grid; gap: 2px; padding: 10px 12px; color: var(--ink);
}
.navpanel a:hover { background: var(--surface-2); }
.navpanel b { font-weight: 550; font-size: 14px; }
.navpanel span { color: var(--ink-2); font-size: 12.5px; line-height: 1.45; }
.nav a { color: var(--ink-2); }
.nav a:hover { color: var(--ink); }
.nav .btn--red { color: #fff; }

/* ---- buttons ----------------------------------------------------------- */
.btn {
  display: inline-block;
  font-family: var(--sans);
  font-size: 14px;
  font-weight: 500;
  padding: 12px 22px;
  border: 1px solid var(--line-2);
  background: transparent;
  color: var(--ink);
  cursor: pointer;
  border-radius: 0;
  transition: background 0.15s ease, border-color 0.15s ease,
              box-shadow 0.15s ease, transform 0.15s ease;
}
.btn:hover { background: var(--surface-2); border-color: var(--ink-3); color: var(--ink); }

.btn--red {
  background: var(--red-deep);
  border-color: var(--red-deep);
  color: #fff;
  font-weight: 600;
}
/* #c00010 to #f83030 is a real change and nobody could see it, and neither a
   soft glow nor a wider one fixed that. Four cues at once do: it lightens, it
   rises, it takes a light ring, and it throws the red past its own edge. */
.btn--red:hover {
  background: var(--red); border-color: #ff7a6a; color: #fff;
  transform: translateY(-2px);
  box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.5),
              0 10px 30px rgba(248, 48, 48, 0.45);
}
.btn:not(.btn--red):hover {
  transform: translateY(-2px);
  box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.28), 0 10px 26px rgba(0, 0, 0, 0.5);
}
.btn:active { transform: translateY(0); }

/* The bar's primary button hovers exactly like the hero's -- same lift, same
   ring, same red -- because it is the same control.
   This rule exists only to win a specificity argument: `.nav > a:hover` is
   (0,2,1) and `.btn--red:hover` is (0,2,0), so without it the nav rule's grey
   `--surface-2` background beats the red one no matter which is written
   later. The lift and the ring are uncontested and come from .btn--red:hover
   itself. */
.nav .btn--red:hover, .nav .btn--red:focus-visible {
  background: var(--red); border-color: #ff7a6a; color: #fff;
}
/* The bar's primary button stays exactly the colour it is. It sits on glass
   at 13px, where the hero button's ring and lift read as a different control
   rather than as the same one. */

.btn--sm { padding: 8px 15px; font-size: 13px; }


/* ---- demo banner ------------------------------------------------------- */
.stamp {
  font-family: var(--sans);
  font-size: 10.5px; font-weight: 700; letter-spacing: 0.12em;
  color: var(--red); border: 1px solid var(--red-deep);
  padding: 3px 8px; white-space: nowrap;
}

.banner {
  display: flex; align-items: center; gap: 14px; flex-wrap: wrap;
  margin: 20px 0 0; padding: 13px 16px;
  background: var(--red-dim);
  border: 1px solid var(--red-deep);
  color: var(--ink-2); font-size: 14px;
}

/* ---- hero -------------------------------------------------------------- */
.hero { padding-block: 96px 80px; }
h1 {
  margin: 0 0 18px;
  font-family: var(--serif);
  font-weight: 400;
  font-size: clamp(40px, 5.6vw, 84px);
  line-height: 1.02;
  letter-spacing: -0.02em;
  text-wrap: balance;
}


.hero-support {
  margin: 0 0 36px;
  color: var(--ink-2);
  font-size: 16.5px;
  line-height: 1.65;
  max-width: 56ch;
}

.hero-actions { display: flex; flex-wrap: wrap; gap: 14px; margin: 0 0 22px; }

.hero-tagline {
  margin: 0 0 10px;
  padding-left: 16px;
  border-left: 2px solid var(--red);
  font-family: var(--sans);
  font-size: clamp(17px, 2.1vw, 21px);
  color: var(--ink);
}

/* Distribution metadata, not an endorsement row: text, no logos, no border.
   Being in a registry says where to install from, not who uses it. */
.hero-listed { margin: 10px 0 0; font-size: 13px; color: var(--ink-3); }
.hero-listed a { color: var(--ink-2); }

.hero-meta {
  position: relative;
  margin: 32px 0 0; padding-top: 24px;
  font-family: var(--mono);
  font-size: 12.5px;
  color: var(--ink-3);
  letter-spacing: 0.02em;
}
/* The same horizon the bar wears, fading out before the measure ends. */
.hero-meta::before {
  content: ""; position: absolute; top: 0; left: 0; right: 0; height: 1px;
  background: linear-gradient(90deg, rgba(255, 255, 255, 0.42),
    rgba(255, 255, 255, 0.42) 44%, transparent);
}

/* ---- bands ------------------------------------------------------------- */
.band { padding-block: var(--band); border-top: 1px solid var(--line); }
.band--bleed { border-top-color: var(--line); }

.band-head {
  display: grid; grid-template-columns: 1fr auto; align-items: baseline;
  gap: 10px 24px; margin-bottom: 26px;
}
.band-head > .band-sub { grid-column: 1 / -1; }
.band-sub { margin: 0; color: var(--ink-2); font-size: 15px; max-width: 64ch; }
.band-sub code { font-size: 13px; color: var(--ink); }
.band-meta {
  margin: 0 0 0 auto;
  font-family: var(--mono); font-size: 12px; color: var(--ink-3);
  white-space: nowrap;
}

.pulse {
  display: inline-block; width: 6px; height: 6px; border-radius: 50%;
  background: var(--red); margin-right: 8px; vertical-align: middle;
  animation: beat 2.6s ease-in-out infinite;
}
@keyframes beat { 0%, 100% { opacity: 1; } 50% { opacity: 0.25; } }
@keyframes blink { 0%, 45% { opacity: 1; } 55%, 100% { opacity: 0; } }
.caret {
  display: inline-block; width: 8px; height: 1em; margin-left: 3px;
  vertical-align: text-bottom; background: var(--red);
  animation: blink 1.1s steps(1, end) infinite;
}
@keyframes tick { 0% { color: var(--red); } 100% { color: inherit; } }
.ticked { animation: tick 1.8s ease-out; }
@media (prefers-reduced-motion: reduce) {
  .pulse, .ticked, .caret, .loop-node, .loop-node .loop-n,
  .loop-return::before { animation: none; }
  .ways.is-shifting .way .way-face { animation: none; }
  /* Transitions, which the rule above does not reach. */
  .way-face, .way-code, .way::before, .ways {
    transition-duration: 0s; transition-delay: 0s;
  }
  .way-face, .way-code { filter: none; transform: none; }
}

/* ---- metrics ----------------------------------------------------------- */
.metrics {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 1px;
  background: var(--line);
  border: 1px solid var(--line);
  border-radius: var(--r);
  overflow: hidden;
}

.metric { background: var(--surface); padding: 28px 24px; }

.metric-value {
  display: block;
  font-family: var(--sans);
  font-weight: 600;
  font-size: clamp(34px, 5vw, 46px);
  line-height: 1;
  letter-spacing: -0.03em;
  font-variant-numeric: tabular-nums;
}

.metric-label {
  display: block; margin-top: 10px;
  font-size: 13px; color: var(--ink-2);
}

.metrics--quiet .metric { background: var(--surface-2); padding: 16px 20px; }
.metrics--quiet .metric-value { font-size: 20px; color: var(--ink-2); }
.metrics--quiet .metric-label { font-size: 12.5px; color: var(--ink-3); margin-top: 6px; }
.metrics--quiet { grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); }



/* ---- table ------------------------------------------------------------- */
.table-wrap {
  overflow-x: auto;
  border: 1px solid var(--line);
  background: var(--surface);
}

table { width: 100%; border-collapse: collapse; min-width: 620px; font-size: 14px; }

th {
  text-align: left;
  font-size: 11px; font-weight: 600; letter-spacing: 0.12em; text-transform: uppercase;
  color: var(--ink-3);
  padding: 13px 18px;
  background: var(--surface-2);
  border-bottom: 1px solid var(--line);
  white-space: nowrap;
}

td { padding: 15px 18px; border-bottom: 1px solid var(--line); }
tbody tr:last-child td { border-bottom: 0; }
th.num, td.num { text-align: right; font-variant-numeric: tabular-nums; }
td.num { font-family: var(--mono); }

.svc { font-family: var(--mono); white-space: nowrap; }
.op  { font-family: var(--mono); color: var(--ink-2); }

.status { display: inline-flex; align-items: center; gap: 9px; white-space: nowrap;
          font-size: 12.5px; font-weight: 600; letter-spacing: 0.04em; }
.status-mark { width: 9px; height: 9px; border: 1.5px solid currentColor; flex: none; }
.status--HEALTHY { color: var(--healthy); }
.status--DEGRADED { color: var(--degraded); }
.status--DEGRADED .status-mark { background: linear-gradient(to top, currentColor 50%, transparent 50%); }
.status--MAJOR { color: var(--major); }
.status--MAJOR .status-mark { background: currentColor; }
.status--INSUFFICIENT_DATA { color: var(--unknown); }
.status--INSUFFICIENT_DATA .status-mark { border-style: dashed; }

.tag {
  font-size: 10px; font-weight: 700; letter-spacing: 0.1em;
  border: 1px solid var(--line-2); color: var(--ink-3);
  padding: 2px 6px; margin-left: 9px; vertical-align: 1px;
}

/* ---- recovery echoes --------------------------------------------------- */
.echo {
  border: 1px solid var(--line);
  border-left: 2px solid var(--red-deep);
  background: var(--surface);
  padding: 22px 24px;
  margin-bottom: 14px;
}

.echo-head { display: flex; align-items: baseline; justify-content: space-between;
             gap: 12px; flex-wrap: wrap; }
.echo-target { font-family: var(--mono); font-size: 15px; }
.echo-kind { font-family: var(--mono); font-size: 12.5px; color: var(--major); }
.echo-msg { margin: 8px 0 20px; font-family: var(--mono); font-size: 12.5px;
            color: var(--ink-3); word-break: break-word; }

/* ---- chain ------------------------------------------------------------- */
.chain { list-style: none; margin: 0; padding: 0; max-width: 62ch; }
.chain li {
  position: relative; padding: 0 0 20px 32px; margin-left: 5px;
  border-left: 1px solid var(--line-2); color: var(--ink-2); font-size: 16px;
}
.chain li:last-child { border-left-color: transparent; padding-bottom: 0; }
.chain li::before {
  content: ""; position: absolute; left: -4px; top: 10px;
  width: 7px; height: 7px; border-radius: 50%; background: var(--line-2);
}
.chain li.actor::before { background: var(--red); }
.chain b { color: var(--ink); font-weight: 600; }

.statement {
  margin: 30px 0 0;
  font-family: var(--sans);
  font-size: clamp(20px, 2.4vw, 27px);
  line-height: 1.25;
  letter-spacing: -0.015em;
  max-width: 22ch;
  padding-left: 20px;
  border-left: 2px solid var(--red);
}

/* ---- terminal ---------------------------------------------------------- */
.terminal { border: 1px solid var(--line); background: #0a0b0c; overflow: hidden; }
.terminal-bar {
  padding: 9px 16px; border-bottom: 1px solid var(--line);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  background: var(--surface-2);
  font-family: var(--mono); font-size: 11.5px; color: var(--ink-3);
}
.terminal pre { margin: 0; padding: 18px 16px; overflow-x: auto;
                font-size: 12.5px; line-height: 1.65; color: var(--ink-2); }
.t-red { color: var(--major); }
/* Two kinds of success, two places. In the story the brand red carries
   it; in the transcript red is already spoken for by the failure three
   lines above, so it takes the accent instead. */
.t-brand { color: var(--red); }
.t-ok { color: var(--accent); }
.t-dim { color: var(--ink-3); }

/* ---- endpoint ---------------------------------------------------------- */
.endpoint {
  display: block;
  border: 1px solid var(--line-2);
  background: var(--surface);
  padding: 22px 24px;
}
.endpoint-label, .endpoint-note {
  display: block; font-size: 11px; letter-spacing: 0.12em;
  text-transform: uppercase; color: var(--ink-3);
}
.endpoint-row { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; margin: 12px 0 10px; }
.endpoint-row code {
  font-family: var(--mono);
  font-size: clamp(15px, 2.6vw, 22px);
  word-break: break-all;
}
.endpoint-note { text-transform: none; letter-spacing: 0; font-size: 12.5px; }

/* ---- troubleshooting --------------------------------------------------- */
.trouble {
  margin: 0;
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 1px; background: var(--line);
  border: 1px solid var(--line); border-radius: var(--r); overflow: hidden;
}
.trouble > div { background: var(--surface); padding: 24px 26px 26px; }
.trouble dt { margin: 0 0 10px; font-size: 15.5px; font-weight: 600; color: var(--ink); }
.trouble dd { margin: 0; color: var(--ink-2); font-size: 14.5px; line-height: 1.6; }
.trouble code { font-size: 12.5px; color: var(--ink); overflow-wrap: anywhere; }

@media (max-width: 760px) { .trouble { grid-template-columns: minmax(0, 1fr); } }

/* ---- snippets ---------------------------------------------------------- */
.snip { margin: 0; border: 1px solid var(--line);
        background: var(--surface); overflow: hidden; }
.snip figcaption {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  padding: 9px 12px 9px 18px; background: var(--surface-2);
  border-bottom: 1px solid var(--line);
  font-size: 11px; letter-spacing: 0.12em; text-transform: uppercase; color: var(--ink-3);
}
pre { margin: 0; padding: 18px; overflow-x: auto; font-size: 12.5px; line-height: 1.6; }
code { font-family: var(--mono); }

.setup { margin: 0 0 26px; padding-left: 20px; max-width: 68ch;
  color: var(--ink-2); font-size: 15px; }
.setup li { margin-bottom: 12px; }
.setup .snip { margin: 10px 0 0; }

.links { list-style: none; margin: 24px 0 0; padding: 0; }
.links li { display: flex; gap: 18px; flex-wrap: wrap; align-items: baseline;
            padding: 11px 0; border-bottom: 1px solid var(--line); }
.links a { font-family: var(--mono); font-size: 14px; min-width: 150px; color: var(--ink); }
.links a:hover { color: var(--red); }
.links span { color: var(--ink-3); font-size: 13.5px; }

/* ---- privacy ----------------------------------------------------------- */
.no-list, .yes-list { list-style: none; margin: 0; padding: 0; font-size: 15px; }
.no-list li, .yes-list li {
  position: relative; padding: 7px 0 7px 26px; color: var(--ink-2);
  border-bottom: 1px solid var(--line); font-family: var(--mono); font-size: 13.5px;
}
.no-list li::before {
  content: ""; position: absolute; left: 0; top: 17px;
  width: 12px; height: 1.5px; background: var(--red);
}
.yes-list li::before {
  content: ""; position: absolute; left: 3px; top: 14px;
  width: 5px; height: 9px; border: 1.5px solid var(--healthy);
  border-top: 0; border-left: 0; transform: rotate(42deg);
}

/* ---- footer ------------------------------------------------------------ */
footer.shell {
  max-width: none;
  padding-inline: max(var(--gutter), calc(50% - var(--max) / 2 + var(--gutter)));
  padding-block: 52px 68px;
  margin-top: 0;
  background: var(--bg-deep);
  border-top: 1px solid var(--line-2);
}
.foot-top { display: flex; align-items: baseline; justify-content: space-between; gap: 18px; flex-wrap: wrap; }
.foot-brand { margin: 0; }
.foot-brand img { display: block; width: auto; height: 22px; }
.foot-line { margin: 0; color: var(--ink-2); font-size: 14.5px; }
.foot-links { list-style: none; display: flex; flex-wrap: wrap; gap: 20px;
              margin: 20px 0 14px; padding: 0; font-size: 14px; }
.foot-links a { color: var(--ink-2); }
.foot-links a:hover { color: var(--red); }

/* ---- responsive -------------------------------------------------------- */
@media (max-width: 900px) {
  .metrics { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .principles { grid-template-columns: minmax(0, 1fr); }
  .two-col { grid-template-columns: minmax(0, 1fr); gap: 22px; }
}

@media (max-width: 620px) {
  .nav { gap: 6px; font-size: 13.5px; }
  .nav .btn--sm { margin-left: auto; }
  .band-meta { margin-left: 0; }
}

@media (max-width: 480px) {
  :root { --gutter: 18px; }
  body { font-size: 15px; }
  .hero { padding-block: 56px 48px; }
  .band { padding-block: 40px; }
  .metrics, .metrics--quiet { grid-template-columns: minmax(0, 1fr); }
  .metric { padding: 18px; }
  .two-up { grid-template-columns: minmax(0, 1fr); gap: 24px; }
  .hero-actions { flex-direction: column; align-items: stretch; }
  .hero-actions .btn { text-align: center; }
  .btn-quiet { text-align: center; padding-top: 6px; }
  .echo { padding: 18px; }
  .endpoint { padding: 18px; }
}

/* ---- FAQ --------------------------------------------------------------- */
.faq { margin: 0; display: grid; gap: 1px; background: var(--line);
       border: 1px solid var(--line); overflow: hidden; }
.faq > div { background: var(--surface); padding: 20px 22px; }
.faq dt { font-size: 15.5px; font-weight: 600; margin-bottom: 7px; }
.faq dd { margin: 0; color: var(--ink-2); font-size: 14.5px; max-width: 78ch; }
.faq code { font-size: 13px; color: var(--ink); }

/* Brand line under the wordmark: the phrase search engines should attach to
   the name, in normal readable text rather than metadata. */

/* ---- content pages ----------------------------------------------------- */
.hero--page { padding-block: 68px 44px; }

.hero--page h1 {
  font-size: clamp(34px, 5.4vw, 54px);
  margin-bottom: 20px;
}

.prose { max-width: 68ch; }
.prose p { margin: 0 0 16px; color: var(--ink-2); font-size: 16.5px; }
.prose p:last-child { margin-bottom: 0; }
.prose code { font-size: 13.5px; color: var(--ink); }

.band .yes-list, .band .no-list { max-width: 68ch; }


/* ---- bootstrap / empty states ------------------------------------------ */
.bootstrap {
  border: 1px solid var(--line);
  background: var(--surface);
  border-radius: var(--r);
  padding: 26px 28px;
  margin-bottom: 26px;
  max-width: 62ch;
}
.bootstrap p { margin: 0 0 12px; color: var(--ink-2); font-size: 15px; }
.bootstrap p:last-child { margin-bottom: 0; }
.bootstrap-lead {
  font-family: var(--sans);
  font-size: clamp(19px, 2.4vw, 24px) !important;
  color: var(--ink) !important;
  line-height: 1.3;
}

/* ---- sub-sections inside a band ---------------------------------------- */
.subband { margin-top: 40px; padding-top: 28px; border-top: 1px solid var(--line); }
.subband .label { margin-bottom: 16px; }


/* ---- status legend ------------------------------------------------------ */
.legend {
  display: flex; flex-wrap: wrap; gap: 10px 32px;
  margin: 18px 0 0;
}
.legend > div { display: flex; align-items: baseline; gap: 10px; }
.legend dt { margin: 0; }
.legend dd { margin: 0; color: var(--ink-3); font-size: 13px; }

/* ---- the demo story ----------------------------------------------------- */
.story {
  list-style: none; margin: 0 0 32px; padding: 0;
  display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 1px;
  background: var(--line); border: 1px solid var(--line);
 overflow: hidden;
}
.story li { background: var(--surface); padding: 22px 20px; display: flex; gap: 14px; }
.story-step {
  font-family: var(--mono); font-size: 12px; color: var(--red);
  border: 1px solid var(--line-2);
  width: 24px; height: 24px; flex: none;
  display: inline-flex; align-items: center; justify-content: center;
}
.story-head { margin: 0 0 10px; font-size: 14.5px; font-weight: 600; }
.story-body { margin: 0 0 5px; font-size: 13.5px; color: var(--ink-2); }
.story-body code { font-size: 13px; color: var(--ink); }

/* ---- how it works flow --------------------------------------------------- */
.flow {
  list-style: none; margin: 0 0 28px; padding: 0;
  display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 20px;
  counter-reset: flow;
}
.flow li { position: relative; padding-top: 20px; border-top: 2px solid var(--line-2); }
.flow li:first-child { border-top-color: var(--red); }
.flow-name {
  margin: 0 0 8px;
  font-size: 16px; font-weight: 600; letter-spacing: -0.01em; color: var(--ink);
}
.flow-body { margin: 0; color: var(--ink-2); font-size: 14.5px; }


@media (max-width: 900px) {
  .story { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .flow { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 24px; }
}

@media (max-width: 560px) {
  .story { grid-template-columns: minmax(0, 1fr); }
  .flow { grid-template-columns: minmax(0, 1fr); gap: 22px; }
  .legend { gap: 8px 20px; }
  .bootstrap { padding: 20px; }
}

/* Touch has no hover, so the panels are pointless there; the nav collapses to
   one compact scrollable row instead of wrapping into three. */
@media (max-width: 760px) {
  .topbar-row { gap: 10px; flex-wrap: nowrap; }
  .nav {
    flex-wrap: nowrap; overflow-x: auto; gap: 2px;
    scrollbar-width: none; -webkit-overflow-scrolling: touch;
  }
  .nav::-webkit-scrollbar { display: none; }
  .nav > a, .navitem > a { padding: 6px 8px; font-size: 13.5px; white-space: nowrap; }
  /* No hover on touch: drop the menus, keep the one link that matters. */
  .navitem, .nav-wide { display: none; }
  .nav-narrow { display: inline-flex; }
  .nav .btn { margin-left: auto; flex: 0 0 auto; padding: 7px 11px; font-size: 12px; }
  .brand-word { width: 90px; height: auto; }
}

/* ---- the demo terminal, with a button --------------------------------- */
.terminal-bar {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
}
/* Says what the terminal is before the button invites you to press it. */
.terminal-tag {
  flex: 1 1 auto; text-align: right; margin-right: 2px;
  font-family: var(--sans); font-size: 11px; letter-spacing: 0.04em;
  color: var(--ink-3); white-space: nowrap; overflow: hidden;
  text-overflow: ellipsis;
}
@media (max-width: 560px) { .terminal-tag { display: none; } }

.run-btn {
  appearance: none; cursor: pointer; flex: none;
  font-family: var(--sans); font-size: 11.5px; letter-spacing: 0.04em;
  text-transform: uppercase; color: var(--ink-2);
  background: transparent; border: 1px solid var(--line-2); padding: 4px 12px;
  transition: color 0.15s ease, border-color 0.15s ease, background 0.15s ease;
}
.run-btn:hover:not(:disabled) {
  color: var(--ink); border-color: var(--red); background: rgba(248, 48, 48, 0.1);
}
.run-btn:disabled { cursor: default; color: var(--ink-3); }
/* The transcript is as tall as it is; reserving that height stops the page
   growing line by line under the reader while it plays. */
#run-output { display: block; min-height: 46em; }
#run-output.is-running::after {
  content: "\258c"; color: var(--red);
  animation: blink 1.1s steps(1, end) infinite;
}

/* ---- charts ------------------------------------------------------------ */
/* Bars, not lines. Raw observations are kept for 48 hours and there is no
   endpoint that would make a line over time honest, so the chart shows the
   split it can stand behind: what was asked, and where the evidence came
   from. A real but tiny share still gets a visible mark. */
.chart { display: grid; gap: 14px; }
.chart-row {
  display: grid; grid-template-columns: minmax(0, 200px) minmax(0, 1fr) 72px;
  align-items: center; gap: 18px;
}
.chart-key { font-size: 14px; color: var(--ink-2); }
.chart-track { height: 12px; background: var(--surface-2); display: block; }
.chart-fill {
  display: block; height: 100%; width: 0;
  background: var(--ink-3);
  transition: width 0.5s cubic-bezier(0.22, 0.61, 0.36, 1);
}
.chart-fill--known { background: var(--red); }
.chart-fill--own { background: var(--ink-3); }
.chart-fill--demo { background: var(--line-2); }
.chart-num {
  font-family: var(--mono); font-size: 14px; color: var(--ink); text-align: right;
}
@media (max-width: 640px) {
  .chart-row { grid-template-columns: minmax(0, 1fr) 64px; gap: 6px 12px; }
  .chart-track { grid-column: 1 / -1; }
}

/* ---- cards ------------------------------------------------------------- */
/* Three ways on, each with a piece of the artwork. One file, three crops:
   the same 7.5KB image positioned differently reads as three, and there is
   only one request. Swap a crop for its own file when there is one. */
.cards {
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 0 28px; margin-top: 8px;
}
.card { display: grid; gap: 0; align-content: start; }
.card-art {
  display: block; aspect-ratio: 4 / 3; width: 100%;
  background-image: url("/static/card-abstract1.webp");
  background-size: 190% auto; background-repeat: no-repeat;
  filter: saturate(0.9);
  transition: transform 0.3s ease, filter 0.3s ease;
}
.card-art--a { background-position: 8% 22%; }
.card-art--b { background-position: 52% 74%; }
.card-art--c { background-position: 96% 34%; }
.card:hover .card-art { transform: scale(1.02); filter: saturate(1.15); }
.card-title {
  margin-top: 18px; font-size: 18px; line-height: 1.3; color: var(--ink);
}
.card:hover .card-title { color: var(--red); }
.card-meta { margin-top: 8px; font-size: 13px; color: var(--ink-3); }
@media (max-width: 880px) {
  .cards { grid-template-columns: minmax(0, 1fr); gap: 34px 0; }
}

/* Copy controls added by the script sit over the top-right of their box. */
.has-copy { position: relative; }
.has-copy > pre { padding-right: 82px; }
.copy-btn { position: absolute; top: 10px; right: 10px; z-index: 1; }
.terminal.has-copy > .copy-btn { top: 5px; right: 8px; padding: 5px 12px; font-size: 12px; }
.terminal.has-copy > .terminal-bar { padding-right: 86px; }
@media (max-width: 560px) {
  .has-copy > pre { padding-right: 18px; padding-top: 48px; }
  .copy-btn { left: 12px; right: auto; }
  /* A terminal keeps its control in the title bar at every width, so its
     body does not need the room a floating control would have taken. */
  .terminal.has-copy > pre { padding-top: 18px; }
}

/* No button and no title bar: the code is the control. A pre cannot be a
   button element without wrapping every line in it, so it takes the role,
   the tab stop and the keyboard handling instead. */
.copyable { cursor: pointer; position: relative; transition: background 0.15s ease; }
.copyable:hover, .copyable:focus-visible { background: var(--surface-2); }
.copyable::after {
  content: "click to copy";
  position: absolute; top: 14px; right: 0;
  font: 10.5px var(--sans); letter-spacing: .04em; text-transform: uppercase;
  color: var(--ink-3); opacity: 0; transition: opacity 0.15s ease;
}
.copyable:hover::after, .copyable:focus-visible::after { opacity: 1; }
.copyable.is-copied::after { content: "copied"; color: var(--healthy); opacity: 1; }
/* "copied" fades out still reading "copied" -- the word never changes back in
   view -- and stays gone until the pointer leaves. Both classes to outrank
   .is-copied's own opacity. */
.copyable.is-quiet::after,
.copyable.is-copied.is-quiet::after { opacity: 0; }

/* ---- hero: one column, the ways underneath ------------------------------ */
/* Spacing down the hero, top to bottom, all of it in this block:
     .hero--lead padding-block  air above the headline, and below the last line
     h1 margin-bottom           headline to lede
     .hero-lede                 its own size; the gap under it is the next rule
     .hero-actions margin-top   lede to the two buttons
     .hero-meta margin/padding  buttons to the rule, and rule to the stack line
     .hero-listed margin-top    stack line to the distribution line
   The rule above the stack line costs 44px (26 margin + 17 padding + 1px).
   If you add to it, take the same amount off .hero--lead's first number and
   the page below the rule will not move. */
/* The four ways in are four cards below the hero rather than four tabs beside
   it. All four are visible at once, which is the point: the question a first
   visitor has is "is there a path for me", and a tab strip answers it one
   quarter at a time. */
.hero--lead { padding-block: 64px 52px; max-width: 1040px; }
.hero--lead h1 { margin: 0 0 24px; max-width: none; }
.hero--lead .hero-lede { max-width: 46ch; color: var(--ink); font-size: 20px; }
.hero-def {
  margin: 30px 0 0; max-width: 68ch;
  font-size: 16.5px; line-height: 1.7; color: var(--ink-2);
}
.hero--lead .hero-actions { margin-top: 64px; }
/* The endpoint an agent needs, at the far end of the buttons' row. It is a
   button because copying is what it is for. */
.hero-endpoint {
  margin-left: auto; display: inline-flex; align-items: center; gap: 10px;
}
/* Beside the button, not under it: the row keeps the button's own height. */
.hero-endpoint-note {
  font-size: 11.5px; line-height: 1.35; color: var(--ink-3);
  font-family: var(--sans); text-align: right; max-width: 15ch;
}
.hero-endpoint-note a { color: var(--ink-2); }
.hero-endpoint-note a:hover { color: var(--ink); }
/* Dressed like the command blocks below it: same kind of thing, something to
   copy. */
.hero-endpoint-copy {
  appearance: none; cursor: pointer; text-align: left;
  background: rgba(255, 255, 255, 0.04);
  border: 0; border-left: 2px solid var(--red-deep);
  padding: 13px 18px 13px 16px;
  transition: background 0.15s ease;
}
.hero-endpoint-copy code {
  font-family: var(--mono); font-size: 13px; color: var(--ink-2);
}
.hero-endpoint-copy:hover { background: rgba(255, 255, 255, 0.07); }
.hero-endpoint-copy:hover code { color: var(--ink); }
.hero-endpoint-copy::after { top: -19px; right: 0; }
.hero-endpoint-copy.is-copied code { color: var(--healthy); }
@media (max-width: 760px) {
  .hero-endpoint { margin-left: 0; width: 100%; }
}

/* ---- the four ways ------------------------------------------------------ */
.install { padding-bottom: 72px; }
/* The four cards answer one question, and this is the question. Centred over
   them at the size of a section's second line. */
.ways-lead {
  margin: 0 auto 30px; text-align: center;
  font-family: var(--serif); font-weight: 400;
  font-size: clamp(21px, 2.2vw, 29px); letter-spacing: -0.012em;
  color: var(--ink-2);
}
/* The card you point at takes the room: a command needs more width than three
   facts do. Grid tracks interpolate while the count holds, so this is one
   transition on one property. */
.ways {
  display: grid;
  /* Written out rather than repeat(4, ...) so the rest state and the four
     hovered states are the same shape to the letter. */
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr)
                         minmax(0, 1fr) minmax(0, 1fr);
  gap: 20px; align-items: stretch;
  /* --shift times the columns AND delays the arriving content. Content shown
     while a column still moves re-wraps in view. Never split these two. */
  --shift: 0.30s;
  transition: grid-template-columns var(--shift) cubic-bezier(0.22, 0.61, 0.36, 1);
}
.ways:has(.way--a:hover), .ways:has(.way--a:focus-within) {
  grid-template-columns: minmax(0, 2.05fr) minmax(0, 1fr)
                         minmax(0, 1fr) minmax(0, 1fr);
}
.ways:has(.way--b:hover), .ways:has(.way--b:focus-within) {
  grid-template-columns: minmax(0, 1fr) minmax(0, 2.05fr)
                         minmax(0, 1fr) minmax(0, 1fr);
}
.ways:has(.way--c:hover), .ways:has(.way--c:focus-within) {
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr)
                         minmax(0, 2.05fr) minmax(0, 1fr);
}
.ways:has(.way--d:hover), .ways:has(.way--d:focus-within) {
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr)
                         minmax(0, 1fr) minmax(0, 2.05fr);
}
@media (max-width: 1080px) {
  .ways:has(.way:hover), .ways:has(.way:focus-within) {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}
/* At rest a card is what this way in is: the artwork, the facts, one line
   about it. Point at it and the artwork goes, the facts go with it, and the
   command you came for is what is left. The art is its own layer so it can
   fade -- a background image cannot be transitioned. */
.way {
  position: relative; overflow: hidden; min-width: 0;
  display: flex; flex-direction: column;
  padding: 22px 20px 20px;
  border: 1px solid var(--line); background: #000;
  transition: border-color 0.22s ease;
}
/* The row rearranges in two phases, and the point of the second one is that
   nobody watches a paragraph re-wrap.
   
   When a card widens the other three narrow, and narrowing re-flows their
   text: line breaks move, a two-line fact becomes three, and the eye follows
   the words jumping rather than the card that opened. Blurring it, softening
   it, snapping it -- all of that was treating the motion as the problem. The
   reflow is the problem, and the fix is to not be looking at the text while
   it happens.

   So, on every card in the row at once:

     phase 1  0 -> 0.20s   the content fades up and out
              0 -> 0.42s   the widths move, behind nothing
     phase 2  0.44 -> 0.74s  the content fades up and back in, already at its
                             new width, already re-wrapped, never seen mid-wrap

   The hovered card does the same thing with different content on the other
   side of it: facts out, code in. The other three come back to the same
   facts, narrower. Same timing, so the row moves as one thing. */
@keyframes settle {
  0%   { opacity: 1; transform: none;             filter: blur(0); }
  25%  { opacity: 0; transform: translateY(-12px); filter: blur(7px); }
  58%  { opacity: 0; transform: translateY(12px);  filter: blur(7px); }
  100% { opacity: 1; transform: none;             filter: blur(0); }
}
/* Driven by a class rather than :hover, because it has to run when the
   pointer *leaves* the row as well -- the widths change back then too, and
   CSS cannot start an animation on a selector that has stopped matching. */
.ways.is-shifting .way:not(:hover):not(:focus-within) .way-face {
  animation: settle 0.48s cubic-bezier(0.22, 0.61, 0.36, 1) both;
}
.way::before {
  content: ""; position: absolute; inset: 0; z-index: 0;
  background-image:
    linear-gradient(rgba(0, 0, 0, 0.52), rgba(0, 0, 0, 0.82)),
    url("/static/card-abstract1.webp");
  background-size: auto, 260% auto;
  background-repeat: no-repeat, no-repeat;
  transition: opacity 0.32s ease;
}
.way > * { position: relative; z-index: 1; }
.way:hover, .way:focus-within { border-color: var(--line-2); }
.way:hover::before, .way:focus-within::before { opacity: 0; }
.way--a::before { background-position: 0 0, 6% 18%; }
.way--b::before { background-position: 0 0, 38% 72%; }
.way--c::before { background-position: 0 0, 66% 26%; }
.way--d::before { background-position: 0 0, 98% 62%; }

/* Two faces, one box: the facts and the command occupy the same space, so
   the card does not resize when they trade places. Both start the same
   distance down that space rather than at the top of it.

   Centring them was the obvious way to do that and it was wrong: the four
   faces are different heights, so centring each one in its own card started
   each one somewhere different -- the last card, which says the least, began
   16px below the first. One offset, shared, puts all four on the same line.

   They hand over rather than cross-fade. Overlapping them at all was the
   mistake: while both are partly visible the card holds two sets of words on
   top of each other, and no amount of blur stops that reading as mashed
   text. "A beat later" was not late enough -- the arriving face began at
   0.10s while the leaving one still had until 0.20s to go, so the worst
   frame was still in there.

   Now they never share one. Whichever face is leaving gets the first 0.26s
   to itself, rising and softening as it goes; the arriving one starts at
   0.28s, after the other is completely gone, and takes 0.34s to come up from
   below and into focus. About 0.62s end to end.

   Symmetrical in both directions: going in, the facts leave and the command
   arrives; coming out, the command leaves and the facts arrive. So the
   resting rules are the *leaving* half for the code and the *arriving* half
   for the facts, which is why their delays look reversed. */
.way-body {
  position: relative; flex: 1 1 auto; min-height: 252px;
  display: flex; flex-direction: column; justify-content: flex-start;
}
.way-face {
  margin-top: 52px;
  transform: none; filter: blur(0);
  transition: opacity 0.18s ease var(--shift),
              transform 0.22s cubic-bezier(0.22, 0.61, 0.36, 1) var(--shift),
              filter 0.18s ease var(--shift), visibility 0s linear var(--shift);
}
.way-code {
  position: absolute; inset: 0; display: flex; flex-direction: column;
  justify-content: flex-start; padding-top: 52px;
  opacity: 0; visibility: hidden;
  transform: translateY(12px); filter: blur(7px);
  transition: opacity 0.12s ease, transform 0.18s cubic-bezier(0.22, 0.61, 0.36, 1),
              filter 0.12s ease, visibility 0s linear 0.12s;
}
.way:hover .way-face, .way:focus-within .way-face {
  opacity: 0; visibility: hidden;
  transform: translateY(-12px); filter: blur(7px);
  transition: opacity 0.12s ease, transform 0.18s cubic-bezier(0.22, 0.61, 0.36, 1),
              filter 0.12s ease, visibility 0s linear 0.12s;
}
.way:hover .way-code, .way:focus-within .way-code {
  opacity: 1; visibility: visible;
  transform: none; filter: blur(0);
  transition: opacity 0.18s ease var(--shift),
              transform 0.22s cubic-bezier(0.22, 0.61, 0.36, 1) var(--shift),
              filter 0.18s ease var(--shift), visibility 0s linear var(--shift);
}

/* Moving card to card is what the handover above does not cover: the card you
   left performs its own swap while shrinking, beside the one you are reading.
   Two cards mid-swap at once is the same mashing, spread across the row.

   So only the card under the pointer animates. Any other card in a row that
   has one snaps back: 0.12s, no delay, including its artwork. You are not
   looking at it, and a card quietly returning to rest is not something that
   needs a performance. */
.ways:hover .way:not(:hover):not(:focus-within) .way-face,
.ways:hover .way:not(:hover):not(:focus-within) .way-code,
.ways:hover .way:not(:hover):not(:focus-within)::before,
.ways:focus-within .way:not(:hover):not(:focus-within) .way-face,
.ways:focus-within .way:not(:hover):not(:focus-within) .way-code,
.ways:focus-within .way:not(:hover):not(:focus-within)::before {
  transition-duration: 0.12s;
  transition-delay: 0s;
}

/* No pointer, no hover: a touch screen gets both at once rather than a card
   whose whole point never appears. */
@media (hover: none) {
  .way-body { min-height: 0; }
  .way-code { position: static; opacity: 1; visibility: visible; margin-top: 18px; }
  .way::before { opacity: 1 !important; }
}

/* "Cursor, Desktop, any MCP client" is two lines and the other three are
   one, so everything below them sat at four different heights. Every title
   reserves both lines. */
.way-name {
  margin: 0 0 20px; min-height: 2.28em;
  font-family: var(--serif); font-weight: 400;
  /* Longest name is two lines; at a fixed 25px it became three when narrow,
     pushing that one card's body below the others. */
  font-size: clamp(19px, 1.88vw, 25px); line-height: 1.14; letter-spacing: -0.015em; color: var(--ink);
}
.way-facts { margin: 0 0 18px; font-size: 13px; line-height: 1.5; }
.way-facts > div { display: flex; gap: 8px; padding: 4px 0; }
.way-facts dt { color: var(--ink-3); flex: 0 0 76px; }
.way-facts dd { margin: 0; color: var(--ink-2); }
.way-facts code { font-size: 12px; color: var(--ink-2); }

/* The command wraps rather than scrolling: a card column cannot hold a curl
   line, and a sideways scrollbar inside a card is worse than a wrapped
   command. Without min-width: 0 above, a wide <pre> widens its own grid
   track instead and the whole document scrolls sideways. */
.way pre {
  /* A flex item shrinks below its content by default, and this one is in a
     column that is only as tall as the card: the command was being squeezed
     and clipped rather than filling the space it was given. */
  flex: 0 0 auto;
  display: flex; align-items: center; min-height: 132px;
  margin: 0; padding: 14px 14px 14px 16px; max-width: 100%;
  background: rgba(255, 255, 255, 0.04);
  border-left: 2px solid var(--red-deep);
  white-space: pre-wrap; overflow-wrap: anywhere;
  /* pre carries overflow-x: auto everywhere else on the site. Here the text
     wraps, so the only thing that rule can produce is a scrollbar with
     nothing to scroll. */
  overflow: visible;
}
.way code { font-family: var(--mono); font-size: 12.5px; line-height: 1.75; }
.way .copyable:hover { background: rgba(255, 255, 255, 0.07); }
/* The Copy control is a control now, so the block does not need to explain
   that it is one too. */
.way .copyable::after { content: none; }

/* Copy on the left, where the full steps are on the right. */
.way-actions {
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px; margin-top: 14px;
}
.way-copy {
  appearance: none; cursor: pointer; flex: none;
  font-family: var(--sans); font-size: 12px; letter-spacing: 0.02em;
  color: var(--ink-2); background: transparent;
  border: 1px solid var(--line-2); padding: 6px 14px;
  transition: color 0.15s ease, border-color 0.15s ease, background 0.15s ease;
}
.way-copy:hover { color: var(--ink); border-color: var(--ink-3); background: var(--surface-2); }
.way-copy.is-copied { color: var(--healthy); border-color: var(--healthy); }
.way-foot { margin: 14px 0 0; font-size: 12.5px; color: var(--ink-3); }
.way .copyable::after { top: -20px; bottom: auto; right: 0; }
/* Where this way in is written out in full. */
.way-more {
  font-size: 12.5px; line-height: 1.3; color: var(--ink-2); text-align: right;
}
.way-more::after { content: " \2197"; color: var(--ink-3); }
.way-more:hover { color: var(--ink); border-bottom-color: var(--red); }
.way-more:hover::after { color: var(--red); }
.install-note { margin: 26px 0 0; font-size: 14px; color: var(--ink-2); }

@media (max-width: 1080px) {
  .ways { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 620px) {
  .ways { grid-template-columns: minmax(0, 1fr); }
  /* One card to a row: nothing to line up with, and the offset is only
     empty space. */
  .way-body { min-height: 0; }
  .way-face { margin-top: 18px; }
  .way-code { position: static; padding-top: 18px; }
}

/* ---- centred bands ----------------------------------------------------- */
.band--center { text-align: center; }
.band--center .band-head { display: block; margin-bottom: 40px; }
.band--center .label {
  font-family: var(--serif); font-weight: 400;
  font-size: clamp(32px, 4.4vw, 56px); letter-spacing: -0.018em;
  line-height: 1.08; text-transform: none; color: var(--ink);
}
.band--center .band-sub {
  margin: 18px auto 0; max-width: 62ch; text-align: left; font-size: 16.5px;
  line-height: 1.65;
}
.band--center .band-meta { margin: 14px auto 0; }
.band--center .statement {
  margin: 44px auto 0; max-width: 44ch; text-align: center;
  padding-left: 0; border-left: 0;
}
.band--center .fineprint { margin: 26px auto 0; }

/* ---- the loop ---------------------------------------------------------- */
/* The product is a cycle, not a funnel: evidence leaves an agent, and comes
   back to a different one. The return path is the whole point, so it is drawn
   rather than described. */
/* One size for the circle, in one place, because the connectors and the
   return path are measured from it. Four fixed 282px circles plus their gaps
   are 1254px wide and did not shrink until 760px, so every window between
   those two scrolled sideways -- on the whole document, not just the
   diagram. */
.loop { display: inline-block; margin: 8px 0 0; --node: clamp(168px, 18.5vw, 282px); }
.loop-track {
  list-style: none; margin: 0; padding: 0;
  display: flex; align-items: center; justify-content: center;
  gap: clamp(10px, 2.4vw, 34px);
}
.loop-node {
  flex: none; width: var(--node); height: var(--node); border-radius: 50%;
  border: 1px dashed var(--line-2); background: transparent;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 5px; padding: 10px 20px; text-align: center;
}
/* The light goes round: one step at a time, then the return path, then
   again. An eight-second lap, slow enough to read as a diagram rather than
   as a warning. */
@keyframes steplit {
  0%, 3%    { border-color: var(--line-2); background: transparent; }
  8%, 16%   { border-color: var(--red); background: var(--red-dim); }
  24%, 100% { border-color: var(--line-2); background: transparent; }
}
@keyframes numberlit {
  0%, 3%    { color: var(--ink-3); }
  8%, 16%   { color: var(--red); }
  24%, 100% { color: var(--ink-3); }
}
.loop-node { animation: steplit 8s ease-in-out infinite; }
.loop-node .loop-n { animation: numberlit 8s ease-in-out infinite; }
.loop-node:nth-child(2), .loop-node:nth-child(2) .loop-n { animation-delay: 1.6s; }
.loop-node:nth-child(3), .loop-node:nth-child(3) .loop-n { animation-delay: 3.2s; }
.loop-node:nth-child(4), .loop-node:nth-child(4) .loop-n { animation-delay: 4.8s; }
.loop-n {
  font-family: var(--mono); font-size: 12px; color: var(--ink-3);
  letter-spacing: 0.08em;
}
.loop-node .flow-name {
  font-family: var(--sans); font-size: 20px; font-weight: 550; color: var(--ink);
}
.loop-node span:last-child {
  font-size: 14.5px; line-height: 1.4; color: var(--ink-2); min-height: 2.8em;
}
.loop-track li + li { position: relative; }
.loop-track li + li::before {
  content: ""; position: absolute; top: 50%; right: 100%;
  width: 30px; margin-right: 7px;
  border-top: 1px dashed var(--line-2);
}
.loop-track li + li::after {
  content: ""; position: absolute; top: 50%; right: 100%;
  width: 5px; height: 5px; margin: -3px 6px 0 0;
  border-top: 1px solid var(--ink-3); border-right: 1px solid var(--ink-3);
  transform: rotate(45deg);
}
/* The path is drawn as a path again, and one circle walks it -- once a lap,
   in the gap after Recover lights and before Fail does, because that is when
   the evidence actually goes back. A dot moving the whole time would say the
   loop never stops to do anything. */
@keyframes carry {
  0%          { left: 100%; opacity: 0; }
  4%          { opacity: 1; }
  16%         { opacity: 1; }
  20%, 100%   { left: 0; opacity: 0; }
}
.loop-return {
  position: relative; margin: 0 auto 30px; height: 46px;
  width: calc(100% - var(--node));
  border: 1px dashed var(--line-2); border-top: 0;
}
.loop-return::before {
  content: ""; position: absolute; bottom: -4px; left: 100%;
  width: 8px; height: 8px; border-radius: 50%; background: var(--red);
  box-shadow: 0 0 12px rgba(248, 48, 48, 0.65);
  opacity: 0;
  animation: carry 8s linear 6.4s infinite;
}
.loop-return::after {
  content: ""; position: absolute; left: -4px; top: -1px;
  width: 6px; height: 6px;
  border-top: 1px solid var(--ink-3); border-left: 1px solid var(--ink-3);
  transform: rotate(45deg);
}
.loop-return span {
  position: absolute; left: 50%; top: 100%; transform: translateX(-50%);
  padding-top: 10px; white-space: nowrap;
  font-size: 13px; color: var(--ink-3);
}

/* ---- stat cards -------------------------------------------------------- */
.statcards {
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 20px; text-align: left;
}
.statcard {
  background: var(--surface); border-radius: var(--r);
  padding: 30px 32px 34px;
}
.statcard-label { margin: 0; font-size: 13.5px; color: var(--ink-2); }
.statcard-value {
  margin: 20px 0 14px;
  font-size: clamp(44px, 6vw, 68px); font-weight: 500; line-height: 1;
  letter-spacing: -0.03em; color: var(--ink);
  font-variant-numeric: tabular-nums;
}
.statcard-body { margin: 0; font-size: 14.5px; line-height: 1.6; color: var(--ink-2); }

@media (max-width: 960px) {
  .loop { display: block; }
  .loop-track { flex-wrap: wrap; gap: 28px 44px; }
  .loop-track li + li::before, .loop-track li + li::after { display: none; }
  .loop-return { height: auto; width: auto; border: 0; margin: 6px 0 0; }
  .loop-return::before, .loop-return::after { display: none; }
  .loop-return span {
    position: static; transform: none; display: block; padding: 0;
    text-align: center; white-space: normal;
  }
  .loop { --node: 176px; }
}

@media (max-width: 640px) {
  .hero--lead { padding-block: 52px 32px; }
  .statcards { grid-template-columns: minmax(0, 1fr); }
  /* 13.5px mono put the REST and agent snippets 40px over a 435px box, so
     both scrolled sideways on a phone. Smaller type fits them instead, and
     the two short panels lose their desktop bump for the same reason: the
     agent line is the longest one on the card. */
  .install code, #panel-plugin code, #panel-agent code { font-size: 12px; }
  .install pre { padding: 18px 16px 8px; }
}

/* A CTA closing a band sits under its heading, not against it. */
.band .hero-actions { margin-top: 4px; }

/* Quiet navigation and readable developer documentation. */
section[id], h2[id] { scroll-margin-top: 100px; }
.eyebrow { margin: 0 0 20px; font: 500 11px/1.5 var(--sans); letter-spacing: .12em; color: var(--ink-3); }
.hero--page .hero-lede { font-size: 18px; max-width: 58ch; }
.setup-jump { display: grid; grid-template-columns: repeat(4,minmax(0,1fr)); gap: 14px; padding-bottom: 40px; }
.setup-jump a { border: 1px solid var(--line-2); padding: 20px; border-radius: var(--r); font-size: 14px; }
.setup-jump a:hover { background: var(--surface); color: var(--ink); }
.setup-jump span { color: var(--ink-3); font: 11px var(--mono); margin-right: 12px; }
.band > .band-sub + .snip,.endpoint + .band-sub,.snip + .band-sub { margin-top: 20px; }
.snip,.terminal,.faq,.story,.principles,.endpoint,.table-wrap { border-radius: var(--r); }
@media(max-width:760px) {
  .setup-jump { grid-template-columns: minmax(0,1fr); gap: 8px; padding-bottom: 32px; }
  .setup-jump a { padding: 15px 18px; }
  .band-head { grid-template-columns: minmax(0,1fr); }
  .band { padding-block: 60px; }
  .foot-links { gap: 15px 22px; }
}

/* ---- on-this-page rail --------------------------------------------------
   /setup and /about grew past ten sections each, which is more than a reader
   will scroll through hoping. A sticky rail beside the content is the cheapest
   way to make a long page navigable without cutting it into more pages.

   The sections keep .shell so nothing else about them changes; the rule below
   neutralises it inside the grid, which is the one place it must not apply. */
.pagelayout {
  max-width: var(--max); margin: 0 auto; padding-inline: var(--gutter);
  display: grid; grid-template-columns: 190px minmax(0, 1fr); gap: 0 56px;
  align-items: start;
}
.pagelayout .shell { max-width: none; margin-inline: 0; padding-inline: 0; }

.pagenav { position: sticky; top: 92px; padding-block: 96px 0; }
.pagenav-title {
  margin: 0 0 12px; font-size: 11px; letter-spacing: .1em;
  text-transform: uppercase; color: var(--ink-3);
}
.pagenav-links { display: flex; flex-direction: column; gap: 2px; }
.pagenav a {
  font-size: 13.5px; line-height: 1.35; color: var(--ink-3);
  padding: 6px 10px; border-left: 2px solid var(--line);
  text-decoration: none;
  transition: color .15s ease, border-color .15s ease, background .15s ease;
}
.pagenav a:hover { color: var(--ink); border-left-color: var(--red); background: var(--surface); }
.pagenav a:target, .pagenav a.here { color: var(--ink); border-left-color: var(--red); }

/* Below this the rail has nowhere to live: one column, and /setup falls back
   to the chip nav it already had. */
@media (max-width: 1100px) {
  .pagelayout { display: block; padding-inline: 0; max-width: none; }
  .pagelayout .shell {
    max-width: var(--max); margin-inline: auto; padding-inline: var(--gutter);
  }
  .pagenav { display: none; }
}

/* ---- lab instance ------------------------------------------------------- */
/* Only rendered when FIN_LAB_LABEL is set. Sits above the bar so every
   screenshot of a lab page carries what it is. */
.lab-banner {
  padding: 7px 16px; text-align: center;
  font: 12px var(--sans); letter-spacing: .04em; text-transform: uppercase;
  color: #000; background: var(--accent);
}
.lab-banner a { color: #000; text-decoration: underline; }
/* Survives scrolling so no screenshot of a lab page is unlabelled, without
   pinning a whole strip to the top. */
.lab-tag {
  display: inline-block; margin-left: 10px; padding: 2px 7px; vertical-align: middle;
  font: 10.5px var(--sans); letter-spacing: .08em; text-transform: uppercase;
  color: #000; background: var(--accent);
}
