/*
 * The site wears the product's own tokens.
 *
 * Lifted from packages/ui/src/panel/panel-styles.ts rather than re-picked, so
 * the page and the panel in its screenshots are visibly one object. Mono-led,
 * because the product is an instrument and a geometric sans would make it look
 * like a SaaS landing page instead.
 */

:root {
  --bg: #ffffff;
  --raised: #f4f6f7;
  --sunk: #eaedef;
  --ink: #14181c;
  --ink-soft: #3d474e;
  --ink-mute: #646d73;
  --ink-mute-on-tint: #60696f;
  --rule: #d5dbde;
  --rule-strong: #8b8f91;
  --accent: #b8451f;
  --accent-wash: rgba(184, 69, 31, 0.09);
  --good: #3f7d58;
  /*
   * The same green, darkened enough to clear AA on its own tint.
   *
   * --good was picked against white, where it measures 4.90:1. Painted on
   * --good-wash it drops to 4.21 and on --accent-wash to 4.31 — both under.
   * Text on a tinted surface gets this one; borders and fills keep --good.
   */
  --good-ink: #376b4c;
  --good-wash: rgba(63, 125, 88, 0.14);
  --risk: #a8352b;
  --risk-wash: rgba(168, 53, 43, 0.14);
  --shadow: 0 1px 2px rgba(20, 24, 28, 0.05), 0 20px 50px -30px rgba(20, 24, 28, 0.45);

  --mono: ui-monospace, 'SF Mono', SFMono-Regular, Menlo, Consolas, monospace;
  --sans: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;

  --t-1: 0.8rem;
  --t0: 1rem;
  --t1: 1.25rem;
  --t2: 1.5625rem;
  --t3: 1.953rem;
  --t4: 2.441rem;

  --s1: 8px;
  --s2: 16px;
  --s3: 24px;
  --s4: 32px;
  --s5: 48px;
  --s6: 64px;
  --s7: 96px;

  /* Measured from the bar's own padding and content; anchors offset by it. */
  --topbar-h: 75px;

  color-scheme: light;
}

@media (prefers-color-scheme: dark) {
  :root {
    --bg: #14181c;
    --raised: #1b2126;
    --sunk: #0f1316;
    --ink: #e6eaec;
    --ink-soft: #b3bcc2;
    --ink-mute: #838d94;
    /* Muted text on a tinted panel; #838d94 measures 4.23:1 there. */
    --ink-mute-on-tint: #8d979e;
    --rule: #2b343a;
    --rule-strong: #656b70;
    --accent: #e4743f;
    --accent-wash: rgba(228, 116, 63, 0.13);
    --good: #6aab84;
    --good-ink: #6aab84;
    --good-wash: rgba(106, 171, 132, 0.15);
    --risk: #d9705f;
    --risk-wash: rgba(217, 112, 95, 0.15);
    --shadow: 0 1px 2px rgba(0, 0, 0, 0.5), 0 20px 50px -30px rgba(0, 0, 0, 0.9);
    color-scheme: dark;
  }
}

* { box-sizing: border-box; }

/*
 * Anchor targets clear the sticky bar.
 *
 * Without this every in-page link lands with its heading tucked underneath the
 * header, which reads as the link having missed.
 */
html { scroll-padding-top: calc(var(--topbar-h) + var(--s3)); }

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

/*
 * The container grows with the display, within limits.
 *
 * A fixed 1000px looked like a ribbon down the middle of a 1680px screen,
 * which is where most of this is read. It does not grow without bound either:
 * past about 1280 the measure stops helping and only the tables and
 * screenshots benefit, so the ceiling is set where those stop gaining.
 *
 * Prose is capped separately, in ch, so a wider container never lengthens a
 * line past reading — it gives the room to the things that want it.
 */
.wrap {
  width: 100%;
  max-width: 1000px;
  margin: 0 auto;
  padding: 0 var(--s4);
}

@media (min-width: 1200px) {
  .wrap { max-width: 1120px; padding: 0 var(--s5); }
}

@media (min-width: 1440px) {
  .wrap { max-width: 1240px; }
}

@media (min-width: 1680px) {
  .wrap { max-width: 1320px; padding: 0 var(--s6); }
}

a { color: var(--accent); text-underline-offset: 3px; }
a:focus-visible, button:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: 3px;
}

/*
 * Explicitly inked, never inherited.
 *
 * Inside a muted list item `code` inherited --ink-mute and landed on --sunk at
 * 4.49:1. A literal is the most precise thing in a sentence; it should be the
 * most legible part of it, not the least.
 */
code {
  font-family: var(--mono);
  font-size: 0.88em;
  background: var(--sunk);
  color: var(--ink);
  padding: 0.12em 0.36em;
  border-radius: 3px;
}

/* ---------- masthead ---------- */

/*
 * Translucent rather than solid.
 *
 * The page has real content running under it, and a flat opaque strip makes
 * the bar look pasted on. A blurred backdrop keeps the sense that the page
 * continues underneath, and the hairline gives it an edge to sit on.
 */
.topbar {
  position: sticky;
  top: 0;
  z-index: 10;
  background: color-mix(in srgb, var(--bg) 86%, transparent);
  backdrop-filter: saturate(180%) blur(12px);
  -webkit-backdrop-filter: saturate(180%) blur(12px);
  border-bottom: 1px solid transparent;
  transition: border-color 200ms cubic-bezier(0.22, 1, 0.36, 1);
}

/* The rule appears only once there is something scrolled underneath it. */
.topbar[data-scrolled='true'] { border-bottom-color: var(--rule); }

/* A browser without backdrop-filter gets an opaque bar rather than a smear. */
@supports not (backdrop-filter: blur(1px)) {
  .topbar { background: var(--bg); }
}

@media (prefers-reduced-motion: reduce) {
  .topbar { transition: none; }
}

/*
 * Not sticky on a phone.
 *
 * The nav wraps to a second line below 640px, which makes the bar 136px tall.
 * Pinning that costs a fifth of the screen on every scroll, permanently, to
 * keep six links visible — a bad trade on the device with the least room. It
 * scrolls away instead, and the anchor offset goes with it.
 */
@media (max-width: 640px) {
  .topbar { position: static; }
  html { scroll-padding-top: var(--s3); }
}

.top {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s2);
  padding: var(--s3) var(--s4);
  flex-wrap: wrap;
}

@media (min-width: 1200px) { .top { padding: var(--s3) var(--s5); } }
@media (min-width: 1680px) { .top { padding: var(--s3) var(--s6); } }

.brand {
  display: flex;
  align-items: center;
  gap: 10px;
  font-family: var(--mono);
  font-size: var(--t0);
  font-weight: 600;
  letter-spacing: -0.02em;
  color: var(--ink);
  text-decoration: none;
}

.top nav {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s2) var(--s3);
  font-family: var(--mono);
  font-size: var(--t-1);
}

/* Five links plus the wordmark run to the edge of a 390px phone. */
@media (max-width: 480px) {
  .top { gap: 10px; }
  .top nav { gap: 10px var(--s2); }
}
.top nav a { color: var(--ink-mute); text-decoration: none; }
.top nav a:hover { color: var(--accent); }

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

.hero {
  padding: var(--s6) 0 var(--s7);
  display: grid;
  grid-template-columns: minmax(0, 1fr) clamp(320px, 30vw, 420px);
  gap: var(--s5);
  align-items: center;
}

.hero-text { min-width: 0; }

.hero-shot {
  margin: 0;
  position: relative;
}

.hero-shot img {
  display: block;
  /*
   * Source is 1224px wide at 3x, so this stays above 2x out to about 600 CSS px.
   */
  width: min(100%, 480px);
  /*
   * Load-bearing.
   *
   * The `height` attribute on the tag is a presentational hint, and it is used
   * as the real height unless CSS says otherwise. Setting only `width` left the
   * height pinned at 470px while the width grew to 420 — the panel came out
   * visibly stretched. `auto` restores the ratio and still lets the width and
   * height attributes reserve the right space before the image loads.
   */
  height: auto;
  border: 1px solid var(--rule);
  /* Square at the bottom, where the panel runs off the image rather than ending. */
  border-radius: 10px 10px 0 0;
  border-bottom: 0;
}

/* Below this the panel would be a smudge above the copy that matters. */
@media (max-width: 940px) {
  .hero { grid-template-columns: 1fr; }
  .hero-shot { display: none; }
}

h2.plain { margin-bottom: var(--s2); }

h1 {
  margin: 0 0 var(--s3);
  font-family: var(--mono);
  font-size: clamp(2rem, 6vw, 3.4rem);
  font-weight: 600;
  letter-spacing: -0.035em;
  line-height: 1.08;
  /*
   * The break between the two sentences is authored, not balanced.
   *
   * Left to `text-wrap: balance` the line broke at "page. No", splitting one
   * sentence across two lines and starting the next mid-line — which reads as
   * a rendering accident rather than a statement.
   */
  max-width: 22ch;
}

h1 em { font-style: normal; color: var(--accent); }

.lede {
  margin: 0 0 var(--s4);
  max-width: 56ch;
  font-size: clamp(1.05rem, 2.2vw, var(--t1));
  line-height: 1.55;
  color: var(--ink-soft);
  text-wrap: pretty;
}

.actions { display: flex; flex-wrap: wrap; gap: var(--s2); align-items: center; }

.btn {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  font-family: var(--mono);
  font-size: var(--t-1);
  font-weight: 600;
  letter-spacing: 0.02em;
  padding: 11px 20px;
  border-radius: 5px;
  border: 1px solid var(--accent);
  background: var(--accent);
  color: #fff;
  text-decoration: none;
}

/* The Chrome mark is full colour, so it never inherits the button's ink. */
.btn-mark { display: block; flex: none; }
@media (prefers-color-scheme: dark) { .btn { color: #14181c; } }
.btn:hover { filter: brightness(1.08); }

.btn.ghost { background: transparent; color: var(--accent); }
.btn.ghost:hover { background: var(--accent-wash); filter: none; }

.note { font-family: var(--mono); font-size: var(--t-1); color: var(--ink-mute); }

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

section { padding: var(--s7) 0; border-top: 1px solid var(--rule); }

h2 {
  margin: 0 0 var(--s2);
  font-family: var(--mono);
  font-size: var(--t2);
  font-weight: 600;
  letter-spacing: -0.02em;
  text-wrap: balance;
}

.sub { margin: 0 0 var(--s5); max-width: 62ch; color: var(--ink-soft); text-wrap: pretty; }

/* ---------- the permission comparison ---------- */

.asks { display: grid; grid-template-columns: 1fr 1fr; gap: var(--s3); }
@media (max-width: 720px) { .asks { grid-template-columns: 1fr; } }

/*
 * The two cards are the argument, so they are coloured like a verdict.
 *
 * Neutral card versus neutral card with red text made the reader work out
 * which was which. Green for what this asks for, red for what the others do:
 * the comparison should land before the paragraphs are read.
 */
.ask {
  padding: var(--s4);
  border: 1px solid var(--rule);
  border-radius: 8px;
  background: var(--raised);
  display: flex;
  flex-direction: column;
  gap: var(--s2);
}

.ask[data-ours='true'] { border-color: var(--good); background: var(--good-wash); }
.ask[data-ours='false'] { border-color: var(--risk); background: var(--risk-wash); }

.ask-who {
  font-family: var(--mono);
  font-size: var(--t-1);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink-mute-on-tint);
}

.ask-what { font-size: var(--t1); line-height: 1.35; font-weight: 600; text-wrap: balance; }
.ask[data-ours='true'] .ask-what { color: var(--good-ink); }
.ask[data-ours='false'] .ask-what { color: var(--risk); }

.ask-why { margin: 0; font-size: 0.92rem; color: var(--ink-soft); }

/* ---------- feature rows ---------- */

.feature {
  display: grid;
  grid-template-columns: 1fr 1.15fr;
  gap: var(--s5);
  align-items: center;
  margin-bottom: var(--s7);
}
.feature:last-child { margin-bottom: 0; }
.feature:nth-child(even) .feature-text { order: 2; }

@media (max-width: 820px) {
  .feature { grid-template-columns: 1fr; gap: var(--s3); }
  .feature:nth-child(even) .feature-text { order: 0; }
}

.feature h3 {
  margin: 0 0 10px;
  font-family: var(--mono);
  font-size: var(--t1);
  font-weight: 600;
  letter-spacing: -0.01em;
}

.feature p { margin: 0; color: var(--ink-soft); max-width: 46ch; }

.feature figure { margin: 0; }

.feature img {
  display: block;
  width: 100%;
  height: auto;
  border: 1px solid var(--rule);
  /* Square at the bottom, where the panel runs off the image rather than ending. */
  border-radius: 10px 10px 0 0;
  border-bottom: 0;
}

/* ---------- panel list ---------- */

.panels { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: 1px;
  background: var(--rule); border: 1px solid var(--rule); border-radius: 8px; overflow: hidden; }

.panel-item { background: var(--bg); padding: var(--s3); display: flex; flex-direction: column; gap: 6px; }
.panel-item h3 { margin: 0; font-family: var(--mono); font-size: var(--t-1); letter-spacing: 0.06em;
  text-transform: uppercase; color: var(--accent); }
.panel-item p { margin: 0; font-size: 0.92rem; color: var(--ink-soft); }

/* ---------- verify ---------- */

.checks { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: var(--s3); }

.check {
  border: 1px solid var(--rule);
  border-radius: 8px;
  padding: var(--s3);
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.check-value {
  font-family: var(--mono);
  font-size: var(--t3);
  font-weight: 600;
  letter-spacing: -0.03em;
  color: var(--good-ink);
  line-height: 1;
}

.check-label { font-family: var(--mono); font-size: var(--t-1); color: var(--ink); }
.check-how { font-size: 0.86rem; color: var(--ink-mute); margin: 0; }

/* ---------- limits ---------- */

.limits { display: flex; flex-direction: column; gap: var(--s2); max-width: 72ch; }

.limit {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: var(--s2);
  align-items: baseline;
  padding-bottom: var(--s2);
  border-bottom: 1px solid var(--rule);
}
.limit:last-child { border-bottom: 0; }

.limit-tag {
  font-family: var(--mono);
  font-size: 0.7rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  padding: 3px 8px;
  border-radius: 3px;
  white-space: nowrap;
  color: var(--ink-mute);
  border: 1px solid var(--rule-strong);
}
.limit-tag[data-kind='choice'] { color: var(--accent); border-color: var(--accent); }

.limit p { margin: 0; color: var(--ink-soft); }
.limit b { color: var(--ink); font-weight: 600; }

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

footer {
  border-top: 1px solid var(--rule);
  padding: var(--s4) 0 var(--s6);
  font-family: var(--mono);
  font-size: var(--t-1);
  color: var(--ink-mute);
  display: flex;
  justify-content: space-between;
  gap: var(--s3);
  flex-wrap: wrap;
}

footer a { color: var(--ink-mute); text-decoration: none; }
footer a:hover { color: var(--accent); }
footer nav { display: flex; gap: var(--s3); flex-wrap: wrap; }

/* ---------- prose, for the privacy page ---------- */

.prose { padding: var(--s5) 0 var(--s6); max-width: 68ch; }
.prose h1 { font-size: clamp(1.7rem, 4vw, var(--t3)); max-width: none; margin-bottom: var(--s2); }
.prose h2 { font-size: var(--t1); margin: var(--s5) 0 var(--s2); }
.prose p { color: var(--ink-soft); text-wrap: pretty; }
.prose ul { color: var(--ink-soft); padding-left: 1.2em; }
.prose li { margin-bottom: 6px; }
.prose table { width: 100%; border-collapse: collapse; font-size: 0.92rem; margin: var(--s2) 0; }
.prose th, .prose td { text-align: left; padding: 10px var(--s2); border-bottom: 1px solid var(--rule); vertical-align: top; }
.prose th { font-family: var(--mono); font-size: var(--t-1); color: var(--ink-mute); font-weight: 600; }
.prose td { color: var(--ink-soft); }
.prose .updated { font-family: var(--mono); font-size: var(--t-1); color: var(--ink-mute); }
/*
 * A bordered panel, not a coloured left rule.
 *
 * The stripe was decoration standing in for emphasis. The statement is the
 * most load-bearing sentence on the page, so it gets a surface of its own.
 */
.prose .big {
  font-size: var(--t1);
  line-height: 1.5;
  color: var(--ink);
  background: var(--good-wash);
  border: 1px solid var(--good);
  border-radius: 8px;
  padding: var(--s3);
  margin: var(--s4) 0;
}


/* ==========================================================================
   The comprehensive page
   ========================================================================== */

/*
 * Section rhythm is varied, not uniform.
 *
 * A page where every band has identical padding reads as a template. The
 * argument sections breathe; the reference sections (keyboard, comparison)
 * are tighter, because they are consulted rather than read.
 */
section.tight { padding: var(--s6) 0; }

.section-head { margin-bottom: var(--s5); }
.section-head h2 { margin-bottom: 10px; }
.section-head .sub { margin: 0; }

/* ---------- panel index ---------- */

/*
 * Seven panels as a numbered sequence, because they genuinely are one: the
 * tab strip runs left to right in this order, and the numbers are how you
 * find the tab rather than decoration.
 */
/*
 * Bordered cards with a real gap, not hairlines over a coloured container.
 *
 * The container-background trick draws a seamless grid only when the cards
 * fill every cell. There are seven panels, so on a three-column layout the
 * eighth cell is empty — and it rendered as a large slab of rule colour, which
 * reads as a broken image rather than a deliberate gap.
 */
.tabs-index {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: var(--s2);
}

.tab-card {
  background: var(--bg);
  border: 1px solid var(--rule);
  border-radius: 8px;
  padding: var(--s3);
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.tab-card-head { display: flex; align-items: baseline; gap: 10px; }

.tab-num {
  font-family: var(--mono);
  font-size: 0.7rem;
  color: var(--ink-mute);
  font-variant-numeric: tabular-nums;
}

.tab-card h3 {
  margin: 0;
  font-family: var(--mono);
  font-size: var(--t0);
  font-weight: 600;
  letter-spacing: -0.01em;
  color: var(--ink);
}

.tab-card p { margin: 0; font-size: 0.92rem; color: var(--ink-soft); }

.tab-card ul {
  margin: 4px 0 0;
  padding: 0;
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: 5px;
}

.tab-card li {
  font-size: 0.86rem;
  color: var(--ink-mute);
  padding-left: 14px;
  position: relative;
}
.tab-card li::before {
  content: '';
  position: absolute;
  left: 0;
  top: 0.62em;
  width: 6px;
  height: 1px;
  background: var(--rule-strong);
}

/* ---------- keyboard reference ---------- */

.keys {
  border: 1px solid var(--rule);
  border-radius: 8px;
  overflow: hidden;
}

.key-row {
  display: grid;
  grid-template-columns: 190px 1fr;
  gap: var(--s3);
  padding: 12px var(--s3);
  border-bottom: 1px solid var(--rule);
  align-items: baseline;
}
.key-row:last-child { border-bottom: 0; }
.key-row:nth-child(odd) { background: var(--raised); }

@media (max-width: 620px) {
  .key-row { grid-template-columns: 1fr; gap: 6px; }
}

.key-combo { display: flex; gap: 5px; flex-wrap: wrap; align-items: center; }

kbd {
  font-family: var(--mono);
  font-size: 0.76rem;
  line-height: 1;
  padding: 5px 7px;
  border: 1px solid var(--rule-strong);
  border-bottom-width: 2px;
  border-radius: 4px;
  background: var(--bg);
  color: var(--ink);
  white-space: nowrap;
}

.key-plus { font-size: 0.72rem; color: var(--ink-mute); }
.key-what { font-size: 0.92rem; color: var(--ink-soft); }
.key-what b { color: var(--ink); font-weight: 600; }

/* ---------- comparison ---------- */

.compare-wrap {
  overflow-x: auto;
  border: 1px solid var(--rule);
  border-radius: 8px;
}

.compare { width: 100%; min-width: 620px; border-collapse: collapse; font-size: 0.9rem; }

.compare thead th {
  background: var(--raised);
  text-align: center;
  font-family: var(--mono);
  font-size: 0.74rem;
  font-weight: 600;
  padding: 12px 10px;
  border-bottom: 1px solid var(--rule-strong);
  white-space: nowrap;
  color: var(--ink);
}
.compare thead th:first-child { text-align: left; padding-left: var(--s3); width: 38%; }
/*
 * Green, not accent.
 *
 * Orange is this page's "you changed something" colour and it was doing a
 * second job here it was never meant to do. The column is a verdict, and the
 * two permission cards above already taught the reader that green means "this
 * is the good answer".
 */
.compare thead th[data-ours='true'] { color: var(--good-ink); background: var(--good-wash); }

.compare tbody th {
  text-align: left;
  font-weight: 400;
  padding: 11px var(--s3);
  border-bottom: 1px solid var(--rule);
  color: var(--ink);
}

.compare tbody td {
  text-align: center;
  padding: 11px 10px;
  border-bottom: 1px solid var(--rule);
  border-left: 1px solid var(--rule);
  color: var(--ink-soft);
  font-family: var(--mono);
  font-size: 0.8rem;
}
.compare tbody td[data-ours='true'] { background: var(--good-wash); color: var(--ink); }
.compare tbody tr:last-child th, .compare tbody tr:last-child td { border-bottom: 0; }

.yes { color: var(--good-ink); font-weight: 600; }
.no { color: var(--ink-mute); }

.compare-note { margin: var(--s2) 0 0; font-size: 0.84rem; color: var(--ink-mute); max-width: 70ch; }

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

.faq { max-width: 74ch; border-top: 1px solid var(--rule); }

.faq details {
  border-bottom: 1px solid var(--rule);
}

.faq summary {
  cursor: pointer;
  padding: var(--s3) 0;
  font-family: var(--mono);
  font-size: var(--t0);
  font-weight: 600;
  letter-spacing: -0.01em;
  list-style: none;
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--s2);
}
.faq summary::-webkit-details-marker { display: none; }

.faq summary::after {
  content: '+';
  font-family: var(--mono);
  font-size: var(--t1);
  color: var(--ink-mute);
  line-height: 1;
  flex: none;
  transition: transform 220ms cubic-bezier(0.22, 1, 0.36, 1);
}
.faq details[open] summary::after { transform: rotate(45deg); color: var(--accent); }
.faq summary:hover { color: var(--accent); }

.faq .answer { padding: 0 0 var(--s3); }
.faq .answer p { margin: 0 0 12px; color: var(--ink-soft); }
.faq .answer p:last-child { margin-bottom: 0; }

@media (prefers-reduced-motion: reduce) {
  .faq details[open] summary::after { transition: none; }
}

/* ---------- install ---------- */

.installs { display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: var(--s3); }

.install {
  border: 1px solid var(--rule);
  border-radius: 8px;
  padding: var(--s3);
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.install[data-state='ready'] { border-color: var(--accent); }

.install h3 {
  margin: 0;
  font-family: var(--mono);
  font-size: var(--t0);
  font-weight: 600;
}

.install p { margin: 0; font-size: 0.9rem; color: var(--ink-soft); flex: 1; }

/* The one card with a call to action keeps it at the foot, aligned with the
   code block in the card beside it. */
.install .btn { align-self: flex-start; margin-top: 4px; }

.install-state {
  font-family: var(--mono);
  font-size: 0.7rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink-mute);
}
.install[data-state='ready'] .install-state { color: var(--accent); }

pre {
  margin: 0;
  padding: 12px 14px;
  background: var(--sunk);
  border-radius: 5px;
  overflow-x: auto;
  font-family: var(--mono);
  font-size: 0.78rem;
  line-height: 1.6;
  color: var(--ink);
}

/* ---------- final call ---------- */

.closer {
  border: 1px solid var(--rule);
  border-radius: 10px;
  padding: var(--s5);
  background: var(--raised);
  text-align: center;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--s3);
}

.closer h2 { margin: 0; max-width: 22ch; }
.closer p { margin: 0; max-width: 54ch; color: var(--ink-soft); }


/* ==========================================================================
   Story sections
   ========================================================================== */

/*
 * The page is a sequence, so the sections are numbered.
 *
 * Not decoration: the order carries the argument. What it is, why it exists,
 * how you use it, what it shows, and whether to believe any of it. Reading
 * them out of order loses the thread, which is the test for whether numbering
 * is earned.
 */
.chapter {
  display: flex;
  align-items: baseline;
  gap: 12px;
  margin-bottom: var(--s2);
}

.chapter-num {
  font-family: var(--mono);
  font-size: var(--t-1);
  color: var(--accent);
  font-variant-numeric: tabular-nums;
  flex: none;
}

.chapter h2 { margin: 0; }

/* ---------- how it works ---------- */

.steps {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: var(--s3);
  counter-reset: step;
}

.step { display: flex; flex-direction: column; gap: 10px; }

.step-rule {
  height: 2px;
  background: var(--rule);
  position: relative;
  overflow: hidden;
}
.step-rule::after {
  content: '';
  position: absolute;
  inset: 0 auto 0 0;
  width: 34px;
  background: var(--accent);
}

.step h3 {
  margin: 0;
  font-family: var(--mono);
  font-size: var(--t0);
  font-weight: 600;
  letter-spacing: -0.01em;
}

.step p { margin: 0; font-size: 0.92rem; color: var(--ink-soft); }

/* ---------- bento ---------- */

/*
 * Not every panel deserves the same box.
 *
 * A uniform grid of seven identical cards says all seven matter equally, which
 * is false: Styles is where most sessions start, and Layout is the one nothing
 * else in the category does. Those two get the room and the screenshots; the
 * rest support the story at the size they deserve.
 */
.bento {
  display: grid;
  grid-template-columns: repeat(6, 1fr);
  gap: var(--s2);
}

.cell {
  grid-column: span 2;
  border: 1px solid var(--rule);
  border-radius: 10px;
  background: var(--bg);
  padding: var(--s3);
  display: flex;
  flex-direction: column;
  gap: 10px;
  overflow: hidden;
  position: relative;
}

.cell[data-span='3'] { grid-column: span 3; }
.cell[data-span='4'] { grid-column: span 4; }
.cell[data-span='6'] { grid-column: span 6; }

@media (max-width: 900px) {
  .cell, .cell[data-span='3'], .cell[data-span='4'] { grid-column: span 3; }
}
@media (max-width: 620px) {
  .cell, .cell[data-span='3'], .cell[data-span='4'] { grid-column: span 6; }
}

.cell-tag {
  font-family: var(--mono);
  font-size: 0.68rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-mute);
}

.cell h3 {
  margin: 0;
  font-family: var(--mono);
  font-size: var(--t1);
  font-weight: 600;
  letter-spacing: -0.02em;
}

.cell[data-span='2'] h3 { font-size: var(--t0); }

.cell p { margin: 0; font-size: 0.92rem; color: var(--ink-soft); max-width: 42ch; }

.cell ul { margin: 2px 0 0; padding: 0; list-style: none; display: flex; flex-direction: column; gap: 5px; }
.cell li { font-size: 0.85rem; color: var(--ink-mute); padding-left: 13px; position: relative; }
.cell li::before {
  content: '';
  position: absolute;
  left: 0;
  top: 0.62em;
  width: 6px;
  height: 1px;
  background: var(--rule-strong);
}

/*
 * The screenshot bleeds off the bottom-right.
 *
 * A shot floated inside its padding reads as a thumbnail; one that runs past
 * the edge reads as a window onto something larger, which is what it is.
 */
.cell-shot {
  margin: 6px -1px -1px 0;
  border-top: 1px solid var(--rule);
  border-left: 1px solid var(--rule);
  border-radius: 8px 0 0 0;
  overflow: hidden;
  margin-left: auto;
  width: min(348px, calc(100% - var(--s3)));
  min-height: 132px;
}

.cell-shot img {
  display: block;
  width: 100%;
  height: auto;
}

/* A number that is the point of the cell, not an ornament. */
.cell-figure {
  font-family: var(--mono);
  font-size: var(--t3);
  font-weight: 600;
  letter-spacing: -0.03em;
  color: var(--accent);
  line-height: 1;
  margin-top: auto;
}
.cell-figure small {
  display: block;
  font-size: 0.72rem;
  font-weight: 400;
  letter-spacing: 0.04em;
  color: var(--ink-mute);
  margin-top: 6px;
}


/* ==========================================================================
   Panel walkthrough
   ========================================================================== */

/*
 * A sticky rail beside a column of sections, not a tab switcher.
 *
 * The difference matters: tabs hide six panels behind a click and let a reader
 * leave having seen one. Scrolling shows all seven in order, and the rail is a
 * position indicator that happens to also be a shortcut.
 */
.walk {
  display: grid;
  grid-template-columns: 168px minmax(0, 1fr);
  gap: var(--s5);
  margin-top: var(--s6);
}

@media (min-width: 1440px) {
  .walk { grid-template-columns: 200px minmax(0, 1fr); gap: var(--s6); }
}

.walk-rail {
  position: sticky;
  top: calc(var(--topbar-h) + var(--s3));
  align-self: start;
  height: fit-content;
}

.walk-rail-label {
  font-family: var(--mono);
  font-size: 0.7rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-mute);
  padding-left: 12px;
  margin-bottom: 12px;
}

.walk-rail nav { display: flex; flex-direction: column; }

.walk-rail a {
  font-family: var(--mono);
  font-size: 0.85rem;
  color: var(--ink-mute);
  text-decoration: none;
  padding: 8px 12px;
  border-left: 2px solid var(--rule);
  transition: color 180ms cubic-bezier(0.22, 1, 0.36, 1),
              border-color 180ms cubic-bezier(0.22, 1, 0.36, 1);
}
.walk-rail a:hover { color: var(--ink); }
.walk-rail a[aria-current='true'] { color: var(--ink); border-left-color: var(--accent); }

.walk-stack { display: flex; flex-direction: column; gap: var(--s7); min-width: 0; }

.walk-block {
  display: grid;
  grid-template-columns: minmax(0, 2fr) minmax(0, 3fr);
  gap: var(--s4);
  scroll-margin-top: calc(var(--topbar-h) + var(--s4));
  align-items: start;
}

.walk-copy { display: flex; flex-direction: column; }

.walk-copy h3 {
  margin: 0 0 var(--s2);
  font-family: var(--mono);
  font-size: 0.82rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  color: var(--ink-mute);
}

/*
 * Two-tone: the claim in ink, the qualification muted.
 *
 * It lets one paragraph carry both the headline and the detail without a
 * second type size, which is what keeps this section quiet at seven repeats.
 */
.walk-lead {
  margin: 0;
  font-size: var(--t1);
  line-height: 1.45;
  font-weight: 500;
  color: var(--ink-mute);
  text-wrap: pretty;
}
.walk-lead b { color: var(--ink); font-weight: 600; }

.walk-list {
  margin: var(--s3) 0 0;
  padding: 0;
  list-style: none;
  border-top: 1px solid var(--rule);
}

.walk-list li {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  padding: 11px 0;
  border-bottom: 1px solid var(--rule);
  font-size: 0.88rem;
  line-height: 1.5;
  color: var(--ink-soft);
}

/*
 * The label is its own flex child.
 *
 * Left as bare text beside the icon, an item containing inline <code> broke
 * into three flex items and wrapped into a ragged column instead of a
 * sentence.
 */
.walk-list li > span { min-width: 0; }

.walk-list svg { flex: none; color: var(--ink-mute); margin-top: 2px; }

/*
 * The screenshot sits in a square frame with the panel bled off the bottom.
 *
 * A panel is much taller than it is wide, so a frame that contained the whole
 * thing would either be enormous or shrink the type past reading. Cropping it
 * at the frame says there is more panel below, which there is.
 */
/*
 * A clipping frame, and nothing else.
 *
 * The backdrop, the inset and the panel's shadow are all baked into the image
 * now (see scripts/frame-site-shots.mjs), which is the only way to get one
 * surface instead of two: a tinted tile *and* a tinted image inside it read as a
 * rectangle sitting on a rectangle, and no amount of matching the two colours
 * hides the seam at the edges. So the tile keeps the geometry — the square, the
 * radius, the crop — and gives up the paint.
 */
.walk-visual {
  margin: 0;
  aspect-ratio: 1;
  border: 1px solid var(--rule);
  border-radius: 16px;
  padding: 0;
  overflow: hidden;
}

/*
 * Anchored to the top of the frame, so the panel's own header survives.
 *
 * Bottom-anchored, the crop ate the selector, the Inspect button and the
 * breadcrumb — the parts that identify what you are looking at — and kept the
 * least distinctive middle. The bleed belongs at the bottom, where the panel
 * genuinely continues.
 */
/*
 * The <picture> has to be sized, not just the <img> inside it.
 *
 * `justify-content: center` centres the flex item, and the flex item is the
 * picture, not the image. Left to itself the picture stretches to fill the tile:
 * working out its intrinsic width means resolving the image's `min(100%, 420px)`
 * with the percentage treated as auto, which leaves the image's *natural* width
 * — over a thousand pixels — as the max-content contribution. So the picture
 * took the whole tile and the image sat at its left edge, with the entire
 * remainder as empty space on the right. Only visible below 940px, where the
 * tile is wider than the image; above it the two are the same width and the bug
 * has nowhere to show.
 */
.walk-visual picture { display: block; }

/* Edge to edge: the image is the surface, so any gap would show the page through. */
.walk-visual img {
  display: block;
  width: 100%;
  height: auto;
}

@media (max-width: 940px) {
  .walk { grid-template-columns: 1fr; gap: var(--s4); }
  .walk-rail { display: none; }
  .walk-stack { gap: var(--s6); }
  .walk-block { grid-template-columns: 1fr; gap: var(--s3); }
  /*
   * Capped, because the tile no longer has a paint of its own to stretch.
   *
   * One column means the tile would otherwise run the full width of the page,
   * and the image cannot follow it: 1224px of capture stops being a 2x image
   * somewhere around 600 CSS px, and past that the panel goes soft again.
   */
  .walk-visual { aspect-ratio: 4 / 3; max-width: 520px; margin-inline: auto; }
}

@media (prefers-reduced-motion: reduce) {
  .walk-rail a { transition: none; }
}
