/* The site navigation. Generated into every page by scripts/gen_nav.py.
 *
 * No JavaScript. Each group is a <details>, so it opens on click and on
 * Enter, it is in the tab order for free, and it still works with scripting
 * disabled. The previous bar was seven different hand-edited bars that between
 * them reached 13 of 142 routes; the point of generating it was to stop that,
 * and adding a script to drive it would put the behaviour back in each page.
 *
 * Everything here reads from /tokens.css. No colour or size is written twice.
 */

.sitebar {
  border-bottom: 1px solid var(--rule);
  background: var(--paper-2);
  position: sticky;
  top: 0;
  z-index: 60;
  backdrop-filter: blur(6px);
}
.sitebar-in {
  display: flex;
  align-items: stretch;
  gap: 0;
  max-width:var(--w-page);
  margin: 0 auto;
  padding: 0 var(--pad-x);
  font-family: var(--mono);
  font-size: var(--t-xs);
}
.sitebar-gap { flex: 1 }

.sitebar .brand {
  display: flex;
  align-items: center;
  gap: .45rem;
  font-weight: 700;
  color: var(--ink);
  text-decoration: none;
  padding-right: var(--s-4);
}
.sitebar .brand img { width: 20px; height: 20px; object-fit: contain; display: block }

.navplain {
  display: flex;
  align-items: center;
  padding: var(--s-3) var(--s-3);
  color: var(--ink-2);
  text-decoration: none;
  opacity: .62;
}
.navplain:hover { opacity: 1; color: var(--ink) }
.navcta {
  align-self: center;
  display: inline-flex;
  align-items: center;
  margin: 0 var(--s-2) 0 var(--s-3);
  padding: .42rem .9rem;
  border: 1.5px solid var(--ink);
  border-radius: 999px;
  color: var(--paper);
  background: var(--ink);
  text-decoration: none;
  font-weight: 600;
  line-height: 1;
}
.navcta:hover, .navcta[aria-current] { background: var(--accent); border-color: var(--accent); color: var(--paper) }
.navcta:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px }

.navgrp { position: relative }
.navgrp > summary {
  list-style: none;
  cursor: pointer;
  display: flex;
  align-items: center;
  gap: .35rem;
  height: 100%;
  min-height: 24px;
  padding: var(--s-3) var(--s-3);
  color: var(--ink-2);
  opacity: .62;
  white-space: nowrap;
  user-select: none;
}
.navgrp > summary::-webkit-details-marker { display: none }
.navgrp > summary::after {
  content: "";
  width: 0; height: 0;
  border-left: 3.5px solid transparent;
  border-right: 3.5px solid transparent;
  border-top: 4px solid currentColor;
  opacity: .5;
}
.navgrp > summary:hover { opacity: 1; color: var(--ink) }
.navgrp[open] > summary { opacity: 1; color: var(--ink); background: var(--paper-3) }
.navgrp.on > summary { opacity: 1; color: var(--accent) }

/* Keyboard users get the same outline everything else on the site gets. */
.navgrp > summary:focus-visible,
.navplain:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px }

.navmenu {
  position: absolute;
  top: 100%;
  left: 0;
  /* 21rem is a floor, and a floor wider than the viewport is an overflow. It
     was released only below one breakpoint, so at 320px the menu ran past the
     right edge with nothing clipping it and the notes under each link were
     cut. min() keeps the comfortable width wherever there is room and gives it
     up where there is not, at every width rather than at one. */
  min-width: min(21rem, calc(100vw - 2rem));
  max-width: calc(100vw - 1.5rem);
  background: var(--paper);
  border: 1.6px solid var(--ink);
  border-top: 0;
  padding: var(--s-2);
  display: grid;
  gap: 1px;
  box-shadow: 0 10px 28px rgb(0 0 0 / .10);
}
.navmenu a {
  display: block;
  min-height: 24px;
  padding: var(--s-2) var(--s-3);
  text-decoration: none;
  color: var(--ink);
  border: 1px solid transparent;
}
.navmenu a:hover,
.navmenu a:focus-visible {
  background: var(--accent-bg);
  border-color: var(--accent);
  outline: none;
}
.navmenu a[aria-current="page"] { border-color: var(--accent); background: var(--accent-bg) }
.navmenu b { display: block; font-weight: 600; font-size: var(--t-xs) }
/* The note is the whole reason this is a menu and not a row of words: a
   reader who does not know what /a2a is should not have to click to find out. */
.navmenu span {
  display: block;
  margin-top: .12rem;
  color: var(--mute);
  font-size: var(--t-2xs);
  line-height: 1.35;
}

@media (max-width: 860px) {
  .sitebar-in { flex-wrap: wrap; font-size: var(--t-2xs); padding: 0 var(--s-3) }
  .sitebar .brand { padding-right: var(--s-3) }
  .navgrp { position: static }
  /* width:auto with both offsets set, or the menu keeps its content width and
     sits wherever the summary left it: at 320 it was 111px wide starting at
     x=94, and its links ran to x=331 with nothing clipping them. */
  .navmenu { left: 0; right: 0; width: auto; min-width: 0; max-width: none }
  /* A link's min-content is its longest word. `min-width:auto` on a grid item
     lets that push past the menu, so the label overflowed the box that was
     supposed to contain it. */
  .navmenu a { min-width: 0; overflow-wrap: anywhere }
}

/* The phone menu. On wide screens the panel is display:contents, so its
   children sit in the bar's own flex row and the desktop layout is the one it
   always was; the button does not render. */
.navpanel { display: contents }
.navtoggle { display: none }

@media (max-width: 720px) {
  /* Only once the nav script has run and marked the bar .navjs. Without it the
     button would open nothing, so the bar stays unfolded as it was. */
  /* The panel positions against .sitebar (sticky, so already a containing
     block) rather than this row, so it spans the viewport edge to edge instead
     of stopping at the row's side padding. */
  .sitebar.navjs .sitebar-in { flex-wrap: nowrap; align-items: center; padding-top: var(--s-1); padding-bottom: var(--s-1) }
  .sitebar.navjs .brand { min-height: 44px }
  .sitebar.navjs .navtoggle {
    display: inline-flex;
    align-items: center;
    gap: var(--s-2);
    margin-left: auto;
    min-height: 44px;
    min-width: 44px;
    padding: 0 var(--s-3);
    font: inherit;
    font-size: var(--t-xs);
    color: var(--ink);
    background: transparent;
    border: 1px solid var(--rule-strong);
    border-radius: 999px;
    cursor: pointer;
  }
  .navtoggle svg { width: 16px; height: 16px; fill: none; stroke: currentColor; stroke-width: 1.6; stroke-linecap: round }
  .navtoggle[aria-expanded="true"] { background: var(--paper-3); border-color: var(--ink-2) }
  .navtoggle:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px }

  .sitebar.navjs .navpanel {
    display: none;
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    flex-direction: column;
    align-items: stretch;
    max-height: calc(100dvh - 60px);
    overflow-y: auto;
    padding: var(--s-2) var(--s-3) var(--s-3);
    background: var(--paper);
    border-bottom: 1.6px solid var(--ink);
    box-shadow: 0 10px 28px rgb(0 0 0 / .10);
  }
  .sitebar.navjs.navopen .navpanel { display: flex }
  .sitebar.navjs .navgrp > summary { min-height: 44px; font-size: var(--t-xs); opacity: 1 }
  .sitebar.navjs .navmenu { position: static; border: 0; box-shadow: none; padding: 0 0 var(--s-2) var(--s-3); background: transparent }
  .sitebar.navjs .navmenu a { min-height: 44px }
  .sitebar.navjs .sitebar-gap { display: none }
  .sitebar.navjs .navplain { min-height: 44px; font-size: var(--t-xs); opacity: 1 }
  .sitebar.navjs .navcta { align-self: flex-start; min-height: 44px; margin: var(--s-2) 0 0; padding: 0 1.1rem }
}

@media (prefers-reduced-motion: reduce) {
  .navgrp > summary { transition: none }
}

/* The audience strip.
 *
 * The site mixes three readerships and marked none of them. /tools is 14,000
 * words and 321 code blocks, /reference is nine tables, and /llms.txt is a
 * machine format a person can open by accident. A leader who lands on a wall
 * of JSON concludes the project is not for them. Neither page is bad at its
 * job; nobody was told whose job it was.
 *
 * One line, quiet, directly under the bar. It names the reader first because
 * that is the question being answered, then what the page is, then what to
 * expect from it.
 */
.audience {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--s-3);
  max-width: var(--w-page);
  margin: 0 auto;
  padding: var(--s-2) var(--pad-x);
  font-family: var(--mono);
  font-size: var(--t-2xs);
  color: var(--mute);
  border-bottom: 1px solid var(--rule);
}
.aud-for {
  font-weight: 600;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--ink-2);
  border-left: 2px solid var(--rule-strong);
  padding-left: var(--s-2);
}
.aud-what { color: var(--ink-2) }
.aud-note { color: var(--mute-2) }

/* Colour carries the same distinction the words do, so the strip is
   recognisable before it is read. Agents get the machine hue the rest of the
   site already uses for machine-produced things. */
.aud-agents     .aud-for { border-left-color: var(--machine); color: var(--machine) }
.aud-developers .aud-for { border-left-color: var(--accent);  color: var(--accent) }
.aud-leaders    .aud-for { border-left-color: var(--warn);    color: var(--warn) }

@media (max-width: 860px) {
  .audience { gap: var(--s-2); padding: var(--s-2) var(--s-3) }
  .aud-note { display: none }
}

/* ── The ports ──────────────────────────────────────────────────────────────
   Where emem is listed, at the end of the bar, because they are the one part
   of it a reader is likely to click on the way OUT.

   Drawn as sockets rather than as vendor badges. The homepage is built around
   a jack plug; these are the sockets it goes into, so the site says the same
   thing in the bar that it says in the painting. A hand-redrawn brand mark
   would read as a cheap copy of the brand, and at 20px an unfamiliar glyph is
   a mystery-meat link, so the recognisable part is the word and the word is
   what is shown.

   Each is a full 44px tall so it clears the tap bound on a phone; the visible
   pill is smaller, and the padding makes up the rest. */
.ports {
  display: flex;
  align-items: center;
  /* Wrapping was set only below 860px. Between that breakpoint and the width
     where the bar has room for four, the row overflowed: at 900px the last
     port sat at x=972 in a 900px viewport, unclipped, so it could not be
     clicked or seen. A row of links must wrap at every width or not be a row. */
  flex-wrap: wrap;
  justify-content: flex-end;
  gap: var(--s-1);
  margin: 0;
  padding: 0;
  list-style: none;
  min-width: 0;
}
.ports li { display: flex }
.port {
  display: inline-flex;
  align-items: center;
  gap: .38em;
  min-height: 2.75rem;            /* the tap bound, not a rounded-up guess */
  padding: .3rem .62rem;
  font-family: var(--mono);
  font-size: var(--t-3xs);
  letter-spacing: .02em;
  color: var(--mute);
  text-decoration: none;
  white-space: nowrap;
  border: 1px solid var(--rule);
  border-radius: 999px;
  transition: color .18s ease, border-color .18s ease;
}
/* The socket: a ring with a dark centre, the shape the plug on the homepage
   is drawn to enter. It is the only ornament here and it carries no meaning a
   screen reader needs, so it is aria-hidden in the markup. */
.port-pin {
  width: .58em;
  height: .58em;
  border-radius: 50%;
  border: 1px solid currentColor;
  background:
    radial-gradient(circle at 50% 50%, currentColor 0 38%, transparent 39%);
  opacity: .75;
  flex: none;
}
.port:hover, .port:focus-visible {
  color: var(--ink);
  border-color: var(--ink-2);
}
.port:focus-visible { outline: 2px solid var(--ink); outline-offset: 2px }

@media (max-width: 860px) {
  /* The bar already wraps to two rows here. Keep every port, drop the gaps and
     the tracking rather than dropping a destination: these are the links that
     take a reader to where emem is listed, and hiding them on a phone hides
     them from most readers. */
  .ports { gap: .25rem; flex-wrap: wrap }
  .port { padding: .3rem .5rem; letter-spacing: 0 }
}

/* Inside the phone menu panel. 2.75rem is 44px only where the root font is
   16px; the demo and verify pages set a smaller root, so the bound is in px. */
@media (max-width: 720px) {
  .sitebar.navjs .ports { justify-content: flex-start; padding-top: var(--s-2); border-top: 1px solid var(--rule) }
  .sitebar.navjs .port { min-height: 44px }
}

/* ── The footer edge ────────────────────────────────────────────────────────
   The footer read like a different website from the page above it: six dense
   columns of links under a page that says one thing at a time. Rather than
   thin it out, which would cost the links that make it useful, it gets the
   page's own edge: a hairline that is the guessing bank on the left and the
   measuring bank on the right, meeting in the middle where the channel runs.

   Two colour stops and nothing else, so this adds one line to the page and no
   markup at all. The two hues are the same ones the homepage draws with. */
.page.foot {
  position: relative;
  border-top: 0;
}
.page.foot::before {
  content: "";
  position: absolute;
  inset: 0 0 auto 0;
  height: 1px;
  background: linear-gradient(90deg,
    color-mix(in oklab, var(--slate-edge, #4e678a) 70%, transparent) 0%,
    color-mix(in oklab, var(--slate-edge, #4e678a) 70%, transparent) 46%,
    var(--rule) 50%,
    color-mix(in oklab, var(--ochre-edge, #9d7420) 70%, transparent) 54%,
    color-mix(in oklab, var(--ochre-edge, #9d7420) 70%, transparent) 100%);
  opacity: 1;
  height: 2px;
}
:root:not([data-theme="light"]) .page.foot::before {
  --slate-edge: #93a9c6;
  --ochre-edge: #dcb161;
}
@media (prefers-color-scheme: light) {
  :root:not([data-theme="dark"]) .page.foot::before {
    --slate-edge: #4e678a;
    --ochre-edge: #9d7420;
  }
}
:root[data-theme="dark"] .page.foot::before {
  --slate-edge: #93a9c6;
  --ochre-edge: #dcb161;
}

/* The "Listed on" column reads as the same sockets the bar carries, so the two
   places emem publishes its destinations look like one decision. */
.foot-col a[rel="noopener"][title] {
  display: inline-flex;
  align-items: center;
  gap: .4em;
}
.foot-col a[rel="noopener"][title]::before {
  content: "";
  width: .5em;
  height: .5em;
  border-radius: 50%;
  border: 1px solid currentColor;
  background: radial-gradient(circle at 50% 50%, currentColor 0 38%, transparent 39%);
  opacity: .6;
  flex: none;
}

/* ---- The developer path, flat in the bar ---------------------------------
   Every step is one visible link; only the rest of the site sits behind
   "More". Generated from SITE / BAR in scripts/gen_nav.py. */
.navlink {
  display: flex;
  align-items: center;
  padding: var(--s-3) var(--s-2);
  color: var(--ink-2);
  text-decoration: none;
  white-space: nowrap;
  opacity: .72;
}
.navlink:hover { opacity: 1; color: var(--ink) }
.navlink.on { opacity: 1; color: var(--accent); box-shadow: inset 0 -2px 0 var(--accent) }
.navlink:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px }
.navmenu.navmenu-cols {
  left: auto;
  right: 0;
  width: min(46rem, calc(100vw - 2rem));
  grid-template-columns: repeat(auto-fit, minmax(10.5rem, 1fr));
  gap: var(--s-2) var(--s-3);
}
.navsec h6 {
  margin: var(--s-1) var(--s-3) var(--s-1);
  font: 600 var(--t-3xs)/1 var(--mono);
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--mute);
}
.labtag {
  display: inline-block;
  margin-left: .4em;
  padding: 0 .38em;
  font: 600 var(--t-3xs)/1.5 var(--mono);
  font-style: normal;
  letter-spacing: .04em;
  color: var(--warn);
  border: 1px solid currentColor;
  border-radius: 999px;
  vertical-align: .1em;
}
.aud-lab .aud-for { border-left-color: var(--warn); color: var(--warn) }
.aud-lab .aud-note { color: var(--accent); display: inline }
@media (max-width: 720px) {
  .sitebar.navjs .navlink { min-height: 44px; font-size: var(--t-xs); opacity: 1 }
  .sitebar.navjs .navmenu.navmenu-cols { width: auto; grid-template-columns: 1fr }
}

/* ---- Shared layout guards -------------------------------------------------
   One gutter for every page, at every width. Template pages set the gutter
   on .page and then a padding shorthand on the same element (.hero-lean:
   padding 3rem 0) that wiped it, so hero text ran to the screen edge on a
   phone. These longhands out-rank a single class, so the gutter survives. */
:root { --gutter: clamp(1rem, 4vw, 2.5rem) }
/* :root body adds a pseudo-class and a type to out-rank the pages' own
   two-class rules (.hero.hero-lean { padding: 3rem 0 }), which come later. */
:root body .page,
:root body .wrap,
:root body main.app { padding-left: var(--gutter); padding-right: var(--gutter); box-sizing: border-box }
/* A fixed-width image or a long token inside a grid track widened the whole
   page past a phone's viewport. */
.page img, .wrap img, main.app img { max-width: 100%; height: auto }
.page > *, .hero > *, .wrap > * { min-width: 0 }
.page pre, .wrap pre { max-width: 100%; overflow-x: auto }
@media (max-width: 720px) {
  .page table, .wrap table { display: block; max-width: 100%; overflow-x: auto }
}

/* ---- Folded prose ---------------------------------------------------------
   A long paragraph shows its first sentence; the rest is one click away. */
details.fold { margin: calc(-1 * var(--s-2)) 0 var(--s-3) }
details.fold > summary {
  display: inline-block;
  cursor: pointer;
  list-style: none;
  font: var(--t-2xs)/1.6 var(--mono);
  color: var(--accent);
  border-bottom: 1px dotted currentColor;
}
details.fold > summary::-webkit-details-marker { display: none }
details.fold[open] > summary { display: none }
details.fold > .rest { margin-top: 0 }

/* ---- Where this page sits on the developer path --------------------------- */
.pathbar {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: var(--s-3);
  max-width: var(--w-page);
  margin: var(--s-6) auto 0;
  padding: var(--s-3) var(--gutter);
  border-top: 1px solid var(--rule);
  font-family: var(--mono);
  font-size: var(--t-xs);
  box-sizing: border-box;
}
.pathbar a { color: var(--ink); text-decoration: none }
.pb-prev, .pb-next { display: flex; align-items: center; gap: var(--s-2); min-height: 44px }
.pb-next { justify-content: flex-end; text-align: right }
.pb-next span { color: var(--mute); font-size: var(--t-2xs) }
.pb-prev:hover b, .pb-next:hover b { color: var(--accent) }
.pathbar i { font-style: normal; color: var(--accent) }
.pb-steps { display: flex; gap: var(--s-1); margin: 0; padding: 0; list-style: none }
.pb-steps a {
  display: grid;
  place-items: center;
  width: 1.75rem;
  height: 1.75rem;
  border-radius: 50%;
  border: 1px solid var(--rule-strong);
  font-size: var(--t-3xs);
  color: var(--mute);
}
.pb-steps a[aria-current="step"] { background: var(--accent); border-color: var(--accent); color: var(--paper) }
.pb-steps a:hover { border-color: var(--accent); color: var(--accent) }
@media (max-width: 720px) {
  .pathbar { grid-template-columns: 1fr 1fr }
  .pb-steps { display: none }
  .pb-next span { display: none }
}

/* ---- The footer, generated -------------------------------------------------
   Every page's footer is computed from the same SITE table as the bar. The
   selector out-ranks the per-page .foot / .page rules the old footers used. */
footer.page.foot.sitefoot {
  display: block;
  max-width: var(--w-page);
  margin: 0 auto;
  padding: var(--s-4) var(--gutter) var(--s-4);
  background: transparent;
  color: var(--ink);
  border-top: 1px solid var(--rule);
  box-sizing: border-box;
}
.sf-top { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--s-3); margin-bottom: var(--s-4) }
.sf-brand { display: flex; align-items: center; gap: var(--s-2); font: 700 var(--t-sm) var(--mono); color: var(--ink); text-decoration: none }
.sf-brand img { width: 20px; height: 20px }
.sf-proof { margin: 0; font: var(--t-2xs)/1.5 var(--mono); color: var(--mute) }
.sf-proof a { color: var(--ink-2) }
.sf-proof .ok { color: var(--accent) }
.sf-proof .no { color: var(--warn) }
.sf-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr)); gap: var(--s-4) var(--s-3) }
.sitefoot .foot-col h4 { margin: 0 0 var(--s-2); font: 600 var(--t-3xs)/1 var(--mono); letter-spacing: .08em; text-transform: uppercase; color: var(--mute) }
.sitefoot .foot-col ul { margin: 0; padding: 0; list-style: none; display: grid; gap: var(--s-1) }
.sitefoot .foot-col a { font: var(--t-xs)/1.5 var(--mono); color: var(--ink-2); text-decoration: none }
.sitefoot .foot-col a:hover { color: var(--accent) }
.sf-bottom { display: flex; flex-wrap: wrap; justify-content: space-between; gap: var(--s-2); margin-top: var(--s-4); padding-top: var(--s-3); border-top: 1px solid var(--rule); font: var(--t-2xs) var(--mono); color: var(--mute) }
.sf-bottom a { color: var(--ink-2) }

/* A lab page says so above its own stage: full-bleed pages (the worlds canvas)
   paint over the strip, and the channel hides the audience strip it inherits. */
.audience.aud-lab {
  display: flex;
  position: relative;
  z-index: 59;
  background: var(--paper-2);
}

/* ---- Inner pages -----------------------------------------------------------
   Every page except the landing page carries body.doc. The landing page is a
   composed poster at --w-page; inner pages are working documents and use the
   width of the screen, up to --w-doc, with a small gutter. Prose is capped at
   --measure so a paragraph stays readable; tables, code and figures are not.

   Measured before this block, at 1440px: the section grid (220px meta column
   plus 68ch, centred) left 263px empty on each side of /how-it-works, /guard
   and /a2a, and the demo pages left 295px. */
body.doc { --w-page: var(--w-doc); --gutter: var(--gutter-doc); --pad-x: var(--gutter-doc) }
body.doc .section { grid-template-columns: var(--col-meta) minmax(0, 1fr); justify-content: stretch }
body.doc :is(.section-body, .doc-body) > :is(p, ul, ol, blockquote, h2, h3, h4, .lede, details.fold) { max-width: var(--measure) }
/* Prose is the human claim, so it is set in the serif, as tokens.css says the
   split should be. It had inherited the body's mono, which put every paragraph
   of every inner page in a terminal face at reading size. Only sentences move:
   code, tokens, figures, tables, chips and the margin notes stay mono. 66ch
   because a serif at 18px carries more characters per inch than mono at 16. */
body.doc :is(.section-body, .doc-body) > :is(p:not([class], [id]), p.lede, blockquote),
body.doc :is(.section-body, .doc-body) > :is(ul, ol):not([class]) > li,
body.doc :is(.hw-list, .hw-steps, .ops, .uc-list) > li,
body.doc :is(.section-body, .doc-body) > details.fold > :is(p:not([class], [id]), ul:not([class]) > li, ol:not([class]) > li) {
  font-family: var(--display);
  font-size: var(--t-md);
  line-height: 1.6;
  font-weight: 400;
  font-variation-settings: 'opsz' 18;
  letter-spacing: 0;
}
body.doc :is(.section-body, .doc-body) > :is(p:not([class], [id]), p.lede, blockquote) { max-width: 66ch }
/* A list item's bold lead-in is a label, so it keeps the label face. */
body.doc :is(.hw-list, .hw-steps, .ops, .uc-list) > li > b:first-child { font-family: var(--mono); font-size: .84em; font-weight: 600 }
body.doc :is(.section-body, .doc-body) :is(p, .lede, blockquote, li) :is(code, kbd, samp, .tok, .mono, .num) {
  font-family: var(--mono);
  font-size: .86em;
  letter-spacing: 0;
}
@media (max-width: 900px) {
  body.doc .section { grid-template-columns: minmax(0, 1fr) }
}

/* The page header: where you are, what this page is for, what is on it. */
.doc-head {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 26rem);
  gap: var(--s-4) var(--s-6);
  align-items: end;
  max-width: var(--w-page);
  margin: 0 auto;
  padding: var(--s-5) var(--gutter) var(--s-4);
  border-bottom: 1px solid var(--rule);
  box-sizing: border-box;
}
.doc-head > * { min-width: 0 }
.doc-head .eyebrow {
  margin: 0 0 var(--s-2);
  font: 600 var(--t-2xs)/1.4 var(--mono);
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--mute);
}
.doc-head h1 {
  margin: 0 0 var(--s-3);
  font-family: var(--display);
  font-weight: 400;
  font-variation-settings: 'opsz' 60;
  font-size: clamp(var(--t-3xl), 4vw, var(--t-5xl));
  line-height: 1.02;
  letter-spacing: -.02em;
  color: var(--ink);
  text-wrap: balance;
}
.doc-head .purpose {
  margin: 0;
  max-width: 62ch;
  font-family: var(--display);
  font-size: var(--t-lg);
  line-height: 1.4;
  color: var(--ink-2);
}
.doc-head .facts {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-2) var(--s-4);
  margin: var(--s-4) 0 0;
  padding: 0;
  font: var(--t-2xs)/1.4 var(--mono);
  color: var(--mute);
}
.doc-head .facts div { display: flex; gap: var(--s-2) }
.doc-head .facts dt { color: var(--mute) }
.doc-head .facts dd { margin: 0; color: var(--ink-2) }
.doc-head .actions { display: flex; flex-wrap: wrap; gap: var(--s-2); margin-top: var(--s-4) }

/* In-page contents. Sits in the header's right column on a wide screen and
   under the title on a narrow one. */
.doc-toc { font-family: var(--mono); font-size: var(--t-xs); border-left: 1px solid var(--rule-strong); padding-left: var(--s-3) }
.doc-toc h2 {
  margin: 0 0 var(--s-2);
  font: 600 var(--t-3xs)/1 var(--mono);
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--mute);
}
.doc-toc ol { margin: 0; padding: 0; list-style: none; columns: 2 12rem; column-gap: var(--s-4) }
.doc-toc li { break-inside: avoid }
.doc-toc a {
  display: flex;
  gap: var(--s-2);
  min-height: 24px;
  padding: 2px 0;
  color: var(--ink-2);
  text-decoration: none;
  line-height: 1.35;
}
.doc-toc a span { color: var(--mute-2); font-variant-numeric: tabular-nums; flex: none }
.doc-toc a:hover { color: var(--accent) }
@media (max-width: 900px) {
  .doc-head { grid-template-columns: minmax(0, 1fr); padding-top: var(--s-4) }
  .doc-toc ol { columns: 1 }
}
/* On a phone the list was a column of twelve rows, a full screen between the
   title and the first sentence. It becomes one row of chips that scrolls
   sideways inside itself; the page does not scroll sideways. */
@media (max-width: 720px) {
  .doc-toc { border-left: 0; padding-left: 0; min-width: 0 }
  .doc-toc ol {
    display: flex; gap: var(--s-2); columns: auto;
    overflow-x: auto; overscroll-behavior-x: contain; scrollbar-width: none;
    margin: 0 calc(-1 * var(--pad-x)); padding: 2px var(--pad-x) var(--s-2);
    scroll-padding-inline: var(--pad-x);
  }
  .doc-toc ol::-webkit-scrollbar { display: none }
  .doc-toc li { flex: none }
  .doc-toc a {
    align-items: center; min-height: 36px; padding: 0 .8rem;
    border: 1px solid var(--rule-strong); border-radius: 999px;
    background: var(--paper); white-space: nowrap;
  }
}

/* Figures. One stroke weight, one palette, labels at 12.6-14px at 1x. An SVG
   carries width= equal to its viewBox width so it is never scaled UP past the
   size its labels were set at; max-width lets it shrink on a phone, where a
   page should offer a tall variant rather than a shrunken wide one. */
.fig { margin: var(--s-4) 0; padding: var(--s-3); border: 1px solid var(--rule); background: var(--paper-2) }
.fig svg { display: block; max-width: 100%; height: auto; margin: 0 auto }
.fig figcaption {
  max-width: var(--measure);
  margin-top: var(--s-3);
  padding-top: var(--s-2);
  border-top: 1px solid var(--rule);
  font: var(--t-xs)/1.55 var(--mono);
  color: var(--ink-2);
}
.fig figcaption b { color: var(--ink); font-weight: 600 }
.fig .fig-tall { display: none; max-width: 360px }
@media (max-width: 640px) {
  .fig .fig-wide { display: none }
  .fig .fig-tall { display: block }
}
.dg { font-family: var(--mono); overflow: visible }
.dg text { fill: var(--ink); font-size: var(--t-xs) }
.dg .t-sub { fill: var(--mute); font-size: var(--t-2xs) }
.dg .t-key { fill: var(--accent); font-weight: 600 }
.dg .t-absent { fill: var(--absent) }
.dg .t-strong { font-weight: 600 }
.dg .n { fill: var(--paper); stroke: var(--rule-strong); stroke-width: 1.25 }
.dg .n-key { fill: var(--accent-bg); stroke: var(--accent); stroke-width: 1.25 }
.dg .n-absent { fill: var(--absent-bg); stroke: var(--absent); stroke-width: 1.25 }
.dg .n-machine { fill: var(--paper); stroke: var(--machine); stroke-width: 1.25 }
.dg .n-zone { fill: none; stroke: var(--rule-strong); stroke-width: 1; stroke-dasharray: 3 4 }
.dg .e { fill: none; stroke: var(--ink-2); stroke-width: 1.25 }
.dg .e-dash { fill: none; stroke: var(--mute); stroke-width: 1.25; stroke-dasharray: 4 4 }
.dg .e-key { fill: none; stroke: var(--accent); stroke-width: 1.5 }
.dg .ah { fill: var(--ink-2) }
.dg .ah-key { fill: var(--accent) }
.dg .ah-dash { fill: var(--mute) }
.dg .step { fill: var(--ink); font-weight: 700; font-size: var(--t-2xs) }

/* What to read next, at the end of a page. */
.next-steps { max-width: var(--w-page); margin: var(--s-5) auto 0; padding: 0 var(--gutter); box-sizing: border-box }
.next-steps h2 { margin: 0 0 var(--s-3); font: 600 var(--t-3xs)/1 var(--mono); letter-spacing: .08em; text-transform: uppercase; color: var(--mute) }
.next-steps ul { display: grid; grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr)); gap: var(--s-3); margin: 0; padding: 0; list-style: none }
.next-steps a {
  display: block;
  height: 100%;
  padding: var(--s-3);
  border: 1px solid var(--rule-strong);
  background: var(--paper);
  color: var(--ink);
  text-decoration: none;
  box-sizing: border-box;
}
.next-steps a:hover { border-color: var(--accent); background: var(--accent-bg) }
.next-steps b { display: block; font: 600 var(--t-sm)/1.3 var(--mono) }
.next-steps span { display: block; margin-top: var(--s-1); font: var(--t-xs)/1.45 var(--mono); color: var(--mute) }

.doc-head .purpose code { font-size: .82em }
/* Several page templates set word-break:break-all on the side-column values,
   which split "Ed25519-signed" mid-word. */
body.doc .section-meta dd, body.doc .hero-meta .kv .v { word-break: normal; overflow-wrap: anywhere }
/* Pages with a global dotted a{border-bottom} underlined the generated chrome. */
.crumbs a, .pathbar a, .sitefoot a, .doc-toc a { border-bottom: 0 }

/* Breadcrumb, generated into the audience strip. */
.crumbs { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--s-1); margin: 0; padding: 0; list-style: none; color: var(--mute) }
.crumbs li { display: inline-flex; gap: var(--s-1) }
.crumbs li + li::before { content: "/"; color: var(--mute-2) }
.crumbs a { color: var(--ink-2); text-decoration: none }
.crumbs a:hover { color: var(--accent); text-decoration: underline }
.crumbs [aria-current] { color: var(--ink); font-weight: 600 }
