/* ---------------------------------------------------------------------------
   docs.size.fun

   The same system the app is drawn in, applied to a reading surface: black as
   a material, one green used as punctuation, hairlines instead of grey panels,
   depth as a hard offset with no blur, everything on an 8px grid.

   Two things change for a documentation page and only two. The measure is
   capped at 72ch, because the app's job is to show a grid of art and this
   page's job is to be read. And the green marks structure — the caret before a
   section, the bar beside the active nav row, the rule under a page title —
   rather than marking a live figure, because nothing here moves.

   Values are copied from web/src/styles.css rather than imported: this service
   builds on its own and shares no bundle with the app. Anything changed there
   has to be changed here too, which is the cost of the two being separable at
   all.
   --------------------------------------------------------------------------- */

@font-face {
  font-family: 'Chakra Petch';
  src: url('/fonts/chakrapetch-400.woff2') format('woff2');
  font-weight: 400;
  font-display: swap;
}
@font-face {
  font-family: 'Chakra Petch';
  src: url('/fonts/chakrapetch-600.woff2') format('woff2');
  font-weight: 600;
  font-display: swap;
}
@font-face {
  font-family: 'Chakra Petch';
  src: url('/fonts/chakrapetch-700.woff2') format('woff2');
  font-weight: 700;
  font-display: swap;
}
@font-face {
  font-family: 'Atkinson Hyperlegible';
  src: url('/fonts/atkinson-400.woff2') format('woff2');
  font-weight: 400;
  font-display: swap;
}
@font-face {
  font-family: 'Atkinson Hyperlegible';
  src: url('/fonts/atkinson-700.woff2') format('woff2');
  font-weight: 700;
  font-display: swap;
}

/* Dark is the only theme this site serves: every page stamps data-theme="dark"
   on the tag and there is no control to change it, because the identity is
   drawn from those values and a documentation page is not the place to offer a
   preference the product does not.

   The light values still sit on the bare :root, and are not dead. They are the
   base the dark block overrides, so a token added to one and forgotten in the
   other resolves to something legible instead of to nothing — and they keep
   this file the same shape as web/src/styles.css, which is where they came
   from. */
:root {
  --font: 'Atkinson Hyperlegible', system-ui, sans-serif;
  --font-title: 'Chakra Petch', system-ui, sans-serif;
  /* The one monospace in the system. Code is the only place a fixed advance
     buys anything, and it buys a lot: indentation, alignment, and the ability
     to point at a column. */
  --font-code: ui-monospace, 'SF Mono', 'Cascadia Mono', 'JetBrains Mono', Menlo, Consolas, monospace;

  --step: polygon(
    4px 0, calc(100% - 4px) 0, 100% 4px, 100% calc(100% - 4px),
    calc(100% - 4px) 100%, 4px 100%, 0 calc(100% - 4px), 0 4px);
  --step-sm: polygon(
    2px 0, calc(100% - 2px) 0, 100% 2px, 100% calc(100% - 2px),
    calc(100% - 2px) 100%, 2px 100%, 0 calc(100% - 2px), 0 2px);

  --u: 8px;
  --sidebar-w: 264px;
  --toc-w: 216px;
  --topbar-h: 72px;
  --measure: 72ch;
  --gutter: 24px;
  /* The width the Markdown button occupies at the end of the title row. Held
     as a token because two rules depend on it agreeing: the button is given
     exactly this width, and the lede reserves exactly this much so its lines
     break where the button begins rather than running underneath it. */
  --copyw: 186px;

  --bg: #dfe3ea;
  --surface: #fff;
  --surface-2: #f1f2f5;
  --surface-3: #e4e6ec;

  --line: rgba(26, 26, 30, 0.13);
  --line-hi: rgba(26, 26, 30, 0.30);

  --ink: #1a1a1e;
  --dim: #61616c;
  --dimmer: #9a9aa3;
  --invert: #fff;

  --green: #0d9a48;
  --green-hi: #12b855;
  --green-dim: #0a6e34;
  --green-wash: rgba(13, 154, 72, 0.11);

  --up: #12a150;
  --down: #dc2b32;
  --warn: #b45309;

  --lift: drop-shadow(4px 4px 0 var(--green-dim));
  --lift-press: drop-shadow(1px 1px 0 var(--green-dim));
  --inset-top: none;

  --track: rgba(26, 26, 30, 0.10);

  /* Code tokens. Four hues and no more: the green the system already owns for
     strings, one blue for keywords, one warm for numbers, and the dim grey for
     comments. A twelve-colour theme in a black interface reads as a different
     product. */
  --tok-key: #7c3aed;
  --tok-str: var(--green);
  --tok-num: #b45309;
  --tok-com: var(--dimmer);
  --tok-fn: #1a1a1e;
}

:root[data-theme='dark'] {
  --bg: #000;
  --surface: #0b0b0b;
  --surface-2: #161616;
  --surface-3: #202020;

  --line: #1f1f1f;
  --line-hi: #333;

  --ink: #fff;
  --dim: #a3a3a3;
  --dimmer: #4d4d4d;
  --invert: #000;

  --green: #12e85c;
  --green-hi: #3bff7e;
  --green-dim: #0a8c38;
  --green-wash: rgba(18, 232, 92, 0.10);

  --up: var(--green);
  --down: #ff3b30;
  --warn: #fbbf24;

  --inset-top: inset 0 1px 0 rgba(255, 255, 255, 0.06);
  --track: #161616;

  --tok-key: #c084fc;
  --tok-str: var(--green);
  --tok-num: #fbbf24;
  --tok-com: #4d4d4d;
  --tok-fn: #fff;
}

*, *::before, *::after { box-sizing: border-box; }

html { scroll-behavior: smooth; }
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  * { transition: none !important; animation: none !important; }
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font: 17px/1.6 var(--font);
  font-weight: 400;
  font-synthesis-weight: none;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  min-height: 100vh;
}

a { color: inherit; text-decoration: none; }

/* Prose links are the one place an underline earns its keep: a docs page is
   dense enough that colour alone would have the reader hunting. */
.prose a {
  color: var(--green);
  text-decoration: underline;
  text-underline-offset: 3px;
  text-decoration-thickness: 1px;
  text-decoration-color: var(--green-dim);
}
.prose a:hover { color: var(--green-hi); text-decoration-color: var(--green-hi); }

::selection { background: var(--green); color: var(--invert); }

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

/* --- shell ---------------------------------------------------------------- */

.shell {
  display: grid;
  grid-template-columns: var(--sidebar-w) minmax(0, 1fr);
  max-width: 1600px;
  margin: 0 auto;
}

.sidebar {
  position: sticky; top: 0; z-index: 40;
  height: 100vh;
  display: flex; flex-direction: column;
  padding: 0 var(--gutter) calc(var(--u) * 3);
  border-right: 1px solid var(--line);
  overflow-y: auto;
  overscroll-behavior: contain;
  scrollbar-width: thin;
  scrollbar-color: var(--line-hi) transparent;
}

.brand {
  display: flex; align-items: center; gap: 10px;
  height: var(--topbar-h); flex: none;
  position: sticky; top: 0; z-index: 1;
  background: var(--bg);
}
/* The mark ships in two cuts, one per ground. Only the dark one is served,
   because only the dark ground is. */
.brand img { height: 34px; width: auto; display: block; }
.brand .tag {
  font-family: var(--font-title); font-weight: 700;
  font-size: 13px; letter-spacing: 0.06em; text-transform: uppercase;
  color: var(--green);
  padding: 3px 7px; border: 1px solid var(--green-dim);
  clip-path: var(--step-sm);
}

.sidebar__nav { display: flex; flex-direction: column; gap: calc(var(--u) * 3); padding-top: var(--u); }
.navgroup h4 {
  margin: 0 0 6px; padding: 0 12px;
  font-family: var(--font-title); font-size: 12px; font-weight: 700;
  letter-spacing: 0.1em; text-transform: uppercase;
  color: var(--dimmer);
}
.navrow {
  display: flex; align-items: center; gap: 10px;
  min-height: 40px; padding: 8px 12px; width: 100%;
  background: transparent; color: var(--dim);
  font-size: 16px; line-height: 1.3;
  transition: color 120ms steps(2), background 120ms steps(2);
}
.navrow:hover { color: var(--ink); background: var(--surface); }
.navrow.on { color: var(--ink); background: var(--surface); position: relative; }
.navrow.on::before {
  content: ''; position: absolute; left: 0; top: 0; bottom: 0;
  width: 4px; background: var(--green);
}

.sidebar__foot {
  margin-top: auto; padding-top: calc(var(--u) * 3);
  display: flex; flex-direction: column; gap: 4px;
}
.sidebar__foot a { color: var(--dimmer); font-size: 15px; padding: 4px 12px; }
.sidebar__foot a:hover { color: var(--ink); }

/* --- main column ---------------------------------------------------------- */

.main {
  min-width: 0;
  display: grid;
  grid-template-columns: minmax(0, 1fr) var(--toc-w);
  gap: 0 calc(var(--u) * 5);
  padding: 0 calc(var(--u) * 5);
}

.topbar {
  grid-column: 1 / -1;
  position: sticky; top: 0; z-index: 30;
  display: flex; align-items: center; gap: 16px;
  height: var(--topbar-h);
  background: var(--bg);
  border-bottom: 1px solid var(--line);
  margin: 0 calc(var(--u) * -5);
  padding: 0 calc(var(--u) * 5);
}
.topbar .spacer { flex: 1; }
.brandsm { display: none; align-items: center; gap: 8px; }
.brandsm img { height: 26px; width: auto; display: block; }

.article { min-width: 0; padding: calc(var(--u) * 6) 0 calc(var(--u) * 10); }
.prose { max-width: var(--measure); }

/* --- search --------------------------------------------------------------- */

.search { position: relative; width: min(420px, 100%); }
.search input {
  width: 100%; height: 40px; padding: 0 12px 0 38px;
  background: var(--surface); color: var(--ink);
  border: 1px solid var(--line); clip-path: var(--step-sm);
  font: 16px/1 var(--font);
}
.search input::placeholder { color: var(--dimmer); }
.search input:focus { border-color: var(--green); outline: none; }
.search .glyph {
  position: absolute; left: 13px; top: 50%; transform: translateY(-50%);
  z-index: 1; color: var(--dim); pointer-events: none; line-height: 0;
}
.search kbd {
  position: absolute; right: 10px; top: 50%; transform: translateY(-50%);
  font: 12px/1 var(--font-code); color: var(--dimmer);
  border: 1px solid var(--line); padding: 3px 5px; pointer-events: none;
}
.search input:focus ~ kbd { display: none; }

.results {
  position: absolute; top: calc(100% + 6px); left: 0; right: 0; z-index: 60;
  background: var(--surface); border: 1px solid var(--line-hi);
  clip-path: var(--step); max-height: 60vh; overflow-y: auto;
  padding: 4px;
}
.results:empty { display: none; }
.results a { display: block; padding: 9px 12px; }
.results a:hover, .results a.on { background: var(--surface-2); }
.results .t { font-family: var(--font-title); font-weight: 600; }
.results .c { color: var(--dim); font-size: 14px; }
.results mark { background: var(--green-wash); color: var(--green); }

/* --- table of contents ---------------------------------------------------- */

.toc {
  position: sticky; top: var(--topbar-h); align-self: start;
  max-height: calc(100vh - var(--topbar-h)); overflow-y: auto;
  padding: calc(var(--u) * 6) 0 calc(var(--u) * 6);
  font-size: 15px;
  scrollbar-width: thin;
  scrollbar-color: var(--line-hi) transparent;
}
.toc h4 {
  margin: 0 0 10px;
  font-family: var(--font-title); font-size: 12px; font-weight: 700;
  letter-spacing: 0.1em; text-transform: uppercase; color: var(--dimmer);
}
.toc a {
  display: block; padding: 5px 0 5px 12px;
  color: var(--dim); border-left: 1px solid var(--line);
  transition: color 120ms steps(2), border-color 120ms steps(2);
}
.toc a:hover { color: var(--ink); }
.toc a.on { color: var(--ink); border-left-color: var(--green); }
.toc a.lvl3 { padding-left: 24px; font-size: 14px; }
.toc:empty { display: none; }

/* --- typography ----------------------------------------------------------- */

.eyebrow {
  font-family: var(--font-title); font-size: 13px; font-weight: 700;
  letter-spacing: 0.1em; text-transform: uppercase; color: var(--green);
  margin: 0 0 10px;
}

/* Eyebrow, title, button and lede are one block, and the rule belongs to that
   block rather than to the paragraph above it.
   
   That is the whole fix for a rule that used to overshoot: `--measure` is 72ch,
   and `ch` resolves against the font-size of whatever element reads it — so the
   same token on the 20px lede drew a line a fifth wider than the 17px prose
   column beside it. Held here, at the column's own size, the rule ends exactly
   where the button does and exactly where the body text does. */
.pagehead {
  max-width: var(--measure);
  margin-bottom: calc(var(--u) * 5);
  padding-bottom: calc(var(--u) * 4);
  border-bottom: 1px solid var(--line);
}

/* flex-start rather than centre because the heading runs to two and three
   lines at narrow widths, and a button centred against a three-line title
   floats in the middle of nothing. The nudge lands it on the first line's cap
   height. Never wraps: the button belongs at the end of the title's line, and
   below the title it reads as the start of the page's content instead. */
.titlerow {
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: 20px; flex-wrap: nowrap;
  margin-bottom: 12px;
}
.titlerow .copyfile { margin-top: 6px; width: var(--copyw); }

h1 {
  font-family: var(--font-title); font-weight: 700;
  font-size: clamp(34px, 4.4vw, 46px); line-height: 1.1;
  margin: 0; letter-spacing: -0.01em;
  min-width: 0;
}
/* The lede stops where the button starts, so its second line does not run on
   underneath a control. Expressed against the block's own width rather than
   against `--measure`, for the ch reason above. */
.lede {
  margin: 0;
  font-size: 20px; line-height: 1.55; color: var(--dim);
  max-width: calc(100% - var(--copyw) - 20px);
}

/* The caret. Green marks where a section starts, drawn as the same 8px block
   the rest of the grid is built from rather than as a glyph. */
.prose h2 {
  font-family: var(--font-title); font-weight: 700;
  font-size: 27px; line-height: 1.2;
  margin: calc(var(--u) * 7) 0 var(--u);
  scroll-margin-top: calc(var(--topbar-h) + 16px);
  position: relative; padding-left: 20px;
}
.prose h2::before {
  content: ''; position: absolute; left: 0; top: 0.32em;
  width: 8px; height: 8px; background: var(--green);
}
.prose h3 {
  font-family: var(--font-title); font-weight: 600;
  font-size: 20px; line-height: 1.3;
  margin: calc(var(--u) * 5) 0 var(--u);
  scroll-margin-top: calc(var(--topbar-h) + 16px);
}
.prose h4 {
  font-family: var(--font-title); font-weight: 600; font-size: 17px;
  margin: calc(var(--u) * 4) 0 4px;
}

.prose p { margin: 0 0 var(--u); }
.prose ul, .prose ol { margin: 0 0 var(--u); padding-left: 22px; }
.prose li { margin-bottom: 6px; }
.prose li::marker { color: var(--green); }
.prose strong { font-weight: 700; }
.prose hr {
  border: 0; border-top: 1px solid var(--line);
  margin: calc(var(--u) * 6) 0;
}

/* A heading you can point at. Hidden until the heading is hovered so the
   measure is not littered with hashes. */
.anchor {
  margin-left: 8px; color: var(--dimmer); opacity: 0;
  font-family: var(--font-code); font-size: 0.72em;
  transition: opacity 120ms steps(2);
}
h2:hover .anchor, h3:hover .anchor, .anchor:focus-visible { opacity: 1; }

/* --- inline code ---------------------------------------------------------- */

.prose :not(pre) > code {
  font-family: var(--font-code); font-size: 0.87em;
  background: var(--surface-2); color: var(--ink);
  border: 1px solid var(--line);
  padding: 1px 5px; clip-path: var(--step-sm);
  white-space: nowrap;
}
.prose a > code { color: var(--green); }

/* --- code blocks ---------------------------------------------------------- */

.code {
  position: relative;
  margin: calc(var(--u) * 2) 0 calc(var(--u) * 3);
  background: var(--surface);
  border: 1px solid var(--line);
  clip-path: var(--step);
  /* The block runs the full column rather than the measure: a line of code
     is not prose and wrapping one costs more than the ragged edge saves. */
  max-width: 100%;
}
.code__bar {
  display: flex; align-items: center; gap: 10px;
  height: 38px; padding: 0 12px;
  border-bottom: 1px solid var(--line);
  background: var(--surface-2);
}
.code__lang {
  font-family: var(--font-title); font-size: 12px; font-weight: 700;
  letter-spacing: 0.08em; text-transform: uppercase; color: var(--dim);
}
.code__file { font-family: var(--font-code); font-size: 13px; color: var(--dimmer); }
.code__bar .spacer { flex: 1; }
.copy {
  font: 12px/1 var(--font-title); font-weight: 700; letter-spacing: 0.06em;
  text-transform: uppercase;
  background: transparent; color: var(--dim);
  border: 1px solid var(--line-hi); padding: 5px 8px;
  cursor: pointer; clip-path: var(--step-sm);
  transition: color 120ms steps(2), border-color 120ms steps(2);
}
.copy:hover { color: var(--ink); border-color: var(--ink); }
.copy.done { color: var(--green); border-color: var(--green); }
.code pre {
  margin: 0; padding: 16px;
  overflow-x: auto;
  font-family: var(--font-code); font-size: 14px; line-height: 1.65;
  tab-size: 2;
}
.code pre::-webkit-scrollbar { height: 8px; }
.code pre::-webkit-scrollbar-thumb { background: var(--line-hi); }

.tok-key { color: var(--tok-key); }
.tok-str { color: var(--tok-str); }
.tok-num { color: var(--tok-num); }
.tok-com { color: var(--tok-com); font-style: italic; }
.tok-fn  { color: var(--tok-fn); }

/* --- panels and callouts -------------------------------------------------- */

.panel {
  background: var(--surface); border: 1px solid var(--line);
  clip-path: var(--step); padding: 18px 20px;
  margin: calc(var(--u) * 2) 0 calc(var(--u) * 3);
  box-shadow: var(--inset-top);
}
.panel > :last-child { margin-bottom: 0; }

.note {
  border: 1px solid var(--line); border-left: 4px solid var(--green);
  background: var(--surface); padding: 14px 18px;
  margin: calc(var(--u) * 2) 0 calc(var(--u) * 3);
}
.note > :last-child { margin-bottom: 0; }
.note__t {
  font-family: var(--font-title); font-weight: 700; font-size: 13px;
  letter-spacing: 0.08em; text-transform: uppercase; color: var(--green);
  margin: 0 0 6px;
}
.note.warn { border-left-color: var(--warn); }
.note.warn .note__t { color: var(--warn); }
.note.bad { border-left-color: var(--down); }
.note.bad .note__t { color: var(--down); }

/* --- tables --------------------------------------------------------------- */

.tablewrap { overflow-x: auto; margin: calc(var(--u) * 2) 0 calc(var(--u) * 3); }
table { width: 100%; border-collapse: collapse; font-size: 16px; }
th {
  text-align: left; color: var(--dim); font-weight: 400;
  font-family: var(--font-title);
  padding: 0 16px 9px 0; border-bottom: 1px solid var(--line);
  white-space: nowrap;
}
td {
  padding: 11px 16px 11px 0; border-bottom: 1px solid var(--line);
  vertical-align: top;
}
tr:last-child td { border-bottom: 0; }
td:last-child, th:last-child { padding-right: 0; }
/* Figures line up on the decimal even in a proportional face. */
td.num { font-variant-numeric: tabular-nums; white-space: nowrap; }
td code, th code {
  font-family: var(--font-code); font-size: 13px;
  background: var(--surface-2); border: 1px solid var(--line);
  padding: 1px 5px; white-space: nowrap;
}

/* An address is the one string on this site nobody reads and everybody
   copies, so it gets the fixed advance and a copy control of its own. */
.addr { display: flex; align-items: center; gap: 8px; }
.addr code {
  font-family: var(--font-code); font-size: 13px;
  background: var(--surface-2); border: 1px solid var(--line);
  padding: 3px 6px; word-break: break-all; white-space: normal;
}
.addr .copy { flex: none; }

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

.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  min-height: 44px; padding: 9px 18px;
  background: var(--green); color: var(--invert); border: 0;
  font: 700 16px/1 var(--font); cursor: pointer;
  clip-path: var(--step); filter: var(--lift);
  transition: transform 60ms steps(2), filter 60ms steps(2), background 120ms steps(2);
}
.btn:hover { background: var(--green-hi); }
.btn:active { transform: translate(3px, 3px); filter: var(--lift-press); }
.btn.ghost {
  background: var(--surface); color: var(--dim);
  border: 1px solid var(--line-hi); filter: none;
}
.btn.ghost:hover { background: var(--surface-2); color: var(--ink); }
.btn.ghost:active { transform: translate(2px, 2px); background: var(--surface-3); }
.btn.sm { min-height: 36px; padding: 6px 12px; font-size: 14px; }
/* The two copy-a-file controls: the page's Markdown beside its title, and
   llms.txt in the header. Green only while confirming, so the one filled
   control in the chrome stays the app link and the confirmation is the thing
   that changes.

   Full-strength ink rather than the ghost button's --dim. Dim grey next to a
   46px heading is the colour this interface uses for things that are switched
   off, and a control that reads as disabled is one nobody presses. */
.copyfile { flex: none; white-space: nowrap; color: var(--ink); }
.copyfile:hover { border-color: var(--green); color: var(--green); }
.copyfile svg { flex: none; }
.copyfile.done { color: var(--green); border-color: var(--green); }
/* Past the point the top bar gets crowded the label goes and the mark carries
   it, which is the same trade the nav makes one breakpoint later. The one in
   the title row keeps its label — there is room in the column, and beside a
   heading a bare icon does not say what it copies. */
@media (max-width: 1180px) {
  .topbar .copyfile span { display: none; }
  .topbar .copyfile { padding: 6px 10px; }
}

.iconbtn {
  display: inline-flex; align-items: center; justify-content: center;
  width: 40px; height: 40px; flex: none;
  background: var(--surface); color: var(--dim);
  border: 1px solid var(--line); cursor: pointer; clip-path: var(--step-sm);
  transition: color 120ms steps(2), border-color 120ms steps(2);
}
.iconbtn:hover { color: var(--ink); border-color: var(--line-hi); }

.actions { display: flex; flex-wrap: wrap; gap: 12px; margin: calc(var(--u) * 3) 0; }

/* The add-the-network control, under the network panel on the quickstart. */
.addchain { margin: calc(var(--u) * 2) 0 calc(var(--u) * 4); }
.addchain__note {
  margin: 10px 0 0; font-size: 15px; color: var(--dim);
  max-width: var(--measure);
}
.addchain__note.ok { color: var(--green); }
.addchain__note.bad { color: var(--down); }
.addchain .btn:disabled { opacity: 0.6; cursor: default; }

/* --- card grid ------------------------------------------------------------ */

.cards {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(232px, 1fr));
  gap: 16px; margin: calc(var(--u) * 3) 0 calc(var(--u) * 4);
  max-width: var(--measure);
}
.card {
  display: block; padding: 18px;
  background: var(--surface); border: 1px solid var(--line);
  clip-path: var(--step);
  transition: border-color 120ms steps(2), transform 60ms steps(2);
}
.card:hover { border-color: var(--line-hi); }
.card:active { transform: translate(2px, 2px); }
.card h3 {
  margin: 0 0 4px; font-family: var(--font-title); font-weight: 600; font-size: 18px;
}
.card p { margin: 0; color: var(--dim); font-size: 15px; line-height: 1.5; }
.card .n {
  font-family: var(--font-title); font-size: 12px; font-weight: 700;
  letter-spacing: 0.1em; color: var(--green); display: block; margin-bottom: 8px;
}

/* --- key/value rows ------------------------------------------------------- */

.kv {
  display: flex; justify-content: space-between; align-items: baseline; gap: 16px;
  padding: 11px 0; border-bottom: 1px solid var(--line);
}
.kv:last-child { border-bottom: 0; }
.kv .k { color: var(--dim); }
.kv .v { font-variant-numeric: tabular-nums; text-align: right; }
.kv .v.green { color: var(--green); }

/* --- lifecycle diagram ---------------------------------------------------- */

/* Four states and the transitions between them. Drawn in boxes and rules
   rather than in an image so it stays legible at any width and in both
   themes, and so the labels are real text. */
.flow {
  display: grid; gap: 0;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  margin: calc(var(--u) * 3) 0 calc(var(--u) * 4);
  border: 1px solid var(--line); clip-path: var(--step);
  background: var(--surface);
}
.flow__step {
  padding: 16px; border-right: 1px solid var(--line);
  position: relative;
}
.flow__step:last-child { border-right: 0; }
.flow__n {
  font-family: var(--font-title); font-size: 12px; font-weight: 700;
  letter-spacing: 0.1em; color: var(--green); display: block; margin-bottom: 6px;
}
.flow__t { font-family: var(--font-title); font-weight: 600; font-size: 16px; margin-bottom: 4px; }
.flow__d { color: var(--dim); font-size: 14px; line-height: 1.45; }
@media (max-width: 900px) {
  .flow__step { border-right: 0; border-bottom: 1px solid var(--line); }
  .flow__step:last-child { border-bottom: 0; }
}

/* --- page footer nav ------------------------------------------------------ */

.pagenav {
  display: flex; gap: 16px; flex-wrap: wrap;
  margin-top: calc(var(--u) * 8); padding-top: calc(var(--u) * 3);
  border-top: 1px solid var(--line);
  max-width: var(--measure);
}
.pagenav a {
  flex: 1 1 220px; padding: 14px 18px;
  background: var(--surface); border: 1px solid var(--line); clip-path: var(--step);
  transition: border-color 120ms steps(2);
}
.pagenav a:hover { border-color: var(--green); }
.pagenav .d {
  font-family: var(--font-title); font-size: 12px; font-weight: 700;
  letter-spacing: 0.1em; text-transform: uppercase; color: var(--dimmer);
  display: block; margin-bottom: 4px;
}
.pagenav .t { font-family: var(--font-title); font-weight: 600; font-size: 17px; }
.pagenav a.next { text-align: right; }

/* --- responsive ----------------------------------------------------------- */

@media (max-width: 1180px) {
  :root { --toc-w: 0px; }
  .main { grid-template-columns: minmax(0, 1fr); }
  .toc { display: none; }
}

/* Narrow enough that a 186px control beside a heading is most of the row. The
   label goes and the mark carries it, exactly as the header's does — and
   because both the button's width and the lede's reservation are the same
   token, redefining it here moves both. */
@media (max-width: 720px) {
  :root { --copyw: 44px; }
  .titlerow .copyfile span { display: none; }
  .titlerow .copyfile { padding: 6px; }
}

@media (max-width: 900px) {
  .shell { grid-template-columns: minmax(0, 1fr); }
  .main { padding: 0 var(--gutter); }
  .topbar { margin: 0 calc(var(--gutter) * -1); padding: 0 var(--gutter); }
  .brandsm { display: flex; }

  /* The sidebar folds into a sheet the menu button opens. It stays in the
     document rather than being duplicated into a drawer, so there is one nav
     to keep correct. */
  .sidebar {
    position: fixed; inset: 0 auto 0 0; width: min(300px, 86vw);
    background: var(--bg); border-right: 1px solid var(--line-hi);
    transform: translateX(-100%);
    transition: transform 160ms steps(4);
    z-index: 70;
  }
  .sidebar.open { transform: none; }
  .scrim {
    position: fixed; inset: 0; z-index: 60;
    background: rgba(0, 0, 0, 0.6);
  }
  .scrim[hidden] { display: none; }
  .article { padding-top: calc(var(--u) * 4); }
}

@media (min-width: 901px) {
  .menubtn { display: none; }
  .scrim { display: none; }
}

/* --- view transitions ------------------------------------------------------ */

/* Only the article animates. The sidebar, the search and the top bar are
   identical on every page, so cross-fading the whole viewport spends frames
   showing nothing and — worse — leaves the article's own furniture looking
   like it did not move. Naming the article makes it its own layer: the title,
   the Markdown button and the prose are all replaced together and visibly
   redraw as one thing, which is what they are.

   Two frames and out. A dissolve between pages is the one moment this
   interface would stop being drawn in whole pixels, so the transition is held
   to the same shape as every other movement here: fast enough to read as a
   cut, and stepped so what happens in between is a frame rather than a blur. */
.article { view-transition-name: article; }

::view-transition-old(article),
::view-transition-new(article) {
  animation-duration: 70ms;
  animation-timing-function: steps(2, end);
}
/* The chrome swaps without animating, since nothing in it changed. */
::view-transition-old(root),
::view-transition-new(root) { animation: none; }

@media (prefers-reduced-motion: reduce) {
  ::view-transition-old(article),
  ::view-transition-new(article) { animation: none; }
}

/* --- print ---------------------------------------------------------------- */

@media print {
  .sidebar, .toc, .topbar, .pagenav, .copy { display: none; }
  .main { grid-template-columns: 1fr; padding: 0; }
  .code { break-inside: avoid; }
}
