
/* ---------------------------------------------------------------- fonts */
/* Self-hosted. A third-party font request on a docs site is a dependency
   nobody asked for, and Instrument is the right voice twice over: a display
   serif with real optical contrast, and a sans from the same drawing. */
@font-face{font-family:'Archivo';src:url('/assets/fonts/Archivo.woff2')format('woff2');
  font-weight:400 700;font-style:normal;font-display:swap}
@font-face{font-family:'JetBrains Mono';src:url('/assets/fonts/JetBrainsMono.woff2')format('woff2');
  font-weight:400 700;font-style:normal;font-display:swap}

/* --------------------------------------------------------------- tokens */
:root{
  /* Denim. A worn, slightly desaturated blue rather than a screen blue, on a
     blue-black valley - and every accent drawn from the 300-400 lightness band
     with saturation pulled back, because a full-strength blue BURNS on a dark
     surface and loses the scarcity that makes an accent work at all.

     Every pair below is measured against --night, not eyeballed:
       ink    16.6:1   ink-2  9.2:1   ink-3  5.7:1
       denim   7.8:1   signal 11.7:1  flash 17.1:1
       dark text on the denim button: 7.4:1
     The commonest failure in dev tooling is a slate-500 secondary at 4.0:1.
     --ink-3 is the floor here and it is 5.7:1. */
  --night:#080C12;          /* the valley */
  --dusk:#0D141C;           /* raised surfaces */
  --ridge:#18222E;          /* borders, edges */
  --slate:#243244;

  --brass:#4E7FB3;          /* the instrument, in shade */
  --gold:#7BA7D4;           /* the signal */
  --flash:#E6F1FB;          /* the light itself. The only near-white on the page. */
  --signal:#A8CCEC;

  --ink:#E7EDF4;
  --ink-2:#A3B4C6;
  --ink-3:#7A8CA0;          /* 5.2:1, captions only */

  --measure:68ch;
  --gutter:clamp(1rem,2.2vw,1.8rem);
  --ease:cubic-bezier(.16,1,.3,1);
}

/* ----------------------------------------------------------------- base */
*,*::before,*::after{box-sizing:border-box}
html{-webkit-text-size-adjust:100%}
body{
  margin:0;background:var(--night);color:var(--ink);
  font:400 17px/1.65 'Archivo',ui-sans-serif,system-ui,sans-serif;
  font-feature-settings:'kern' 1;
  -webkit-font-smoothing:antialiased;
  overflow-x:hidden;
}

/* The browser's own surfaces belong to the design too. These ship with
   defaults that belong to no design system, and theming them is the cheapest
   signal that a page was built rather than assembled. */
::selection{background:var(--gold);color:var(--night)}
::-webkit-scrollbar{width:11px;height:11px}
::-webkit-scrollbar-track{background:var(--night)}
::-webkit-scrollbar-thumb{background:var(--slate);border-radius:6px;border:3px solid var(--night)}
::-webkit-scrollbar-thumb:hover{background:var(--brass)}
html{scrollbar-color:var(--slate) var(--night);scrollbar-width:thin}
:focus-visible{outline:2px solid var(--gold);outline-offset:3px;border-radius:2px}
a{color:var(--gold);text-decoration-color:color-mix(in srgb,var(--gold) 42%,transparent);
  text-underline-offset:.22em;text-decoration-thickness:1px;
  transition:text-decoration-color .18s var(--ease),color .18s var(--ease)}
a:hover{color:var(--flash);text-decoration-color:var(--flash)}

/* --------------------------------------------------------------- header */
.site-header,.mobile-bar{
  position:sticky;top:0;z-index:50;
  display:flex;gap:2rem;align-items:center;flex-wrap:wrap;
  /* Full-bleed background and border, content aligned to the shell. Without
     the max() the brand sits a few pixels off the sidebar below it, which is
     the kind of misalignment nobody names and everybody sees. */
  --gutter:clamp(1rem,2.2vw,1.8rem);
  --edge:max(var(--gutter),calc((100vw - 90rem)/2 + var(--gutter)));
  padding:.85rem var(--edge);
  background:color-mix(in srgb,var(--night) 82%,transparent);
  backdrop-filter:blur(14px) saturate(1.3);
  border-bottom:1px solid var(--ridge);
}
.brand{display:flex;align-items:center;gap:.6rem;text-decoration:none;color:var(--ink);
  font-family:'Archivo',sans-serif;font-weight:600;font-size:1.12rem;letter-spacing:-.02em}
.brand:hover{color:var(--ink)}
.brand svg{width:26px;height:26px;color:var(--gold);flex:none}
/* SCOPED to the header's own nav with a child combinator, not to every nav on
   the page. The unscoped rules leaked into .side nav and .rail nav, which then
   had to undo them one at a time. The header carries three links on the home
   page and none anywhere else - the sidebar is the navigation once you are in
   the docs. */
.site-header>nav{display:flex;gap:1.35rem;flex-wrap:nowrap;margin-left:auto}
.site-header>nav a{color:var(--ink-2);text-decoration:none;font-size:.93rem;
  position:relative;padding:.15rem 0;white-space:nowrap}
.site-header>nav a::after{content:'';position:absolute;left:0;right:100%;bottom:-2px;height:1px;
  background:var(--gold);transition:right .3s var(--ease)}
.site-header>nav a:hover{color:var(--ink)}
.site-header>nav a:hover::after{right:0}
/* Below this the three links become a deliberate second row rather than
   wrapping mid-list. A link never breaks across lines. */
@media(max-width:420px){
  .site-header{flex-wrap:wrap;gap:.55rem}
  .site-header>nav{width:100%;margin-left:0;justify-content:space-between}
}

/* ----------------------------------------------------------------- hero */
/* The authored moment. One beam crosses the valley, lands, and the log comes
   back. It runs once on load and on a slow loop after, so a page that is being
   read is not competing with its own header. */
.hero{position:relative;overflow:hidden;border-bottom:1px solid var(--ridge);min-height:min(86vh,780px);display:flex;align-items:center}
.hero canvas{position:absolute;inset:0;width:100%;height:100%;display:block}
.hero-inner{
  position:relative;z-index:2;
  max-width:min(76rem,92vw);margin:0 auto;
  width:100%;
  padding:clamp(3rem,9vh,5.5rem) clamp(1.1rem,4vw,2.5rem) clamp(9rem,22vh,13rem);
}
.hero h1{
  font-family:'Archivo',sans-serif;font-weight:600;
  font-size:clamp(2.6rem,6.6vw,4.9rem);line-height:1.02;letter-spacing:-.035em;
  margin:0 0 1.15rem;max-width:17ch;text-wrap:balance;
}
.hero h1 em{font-style:normal;color:var(--gold)}
.hero .lede{
  font-size:clamp(1.08rem,1.9vw,1.34rem);line-height:1.55;color:var(--ink-2);
  max-width:46ch;margin:0 0 2.4rem;text-wrap:pretty;
}
.cta{display:flex;gap:.85rem;flex-wrap:wrap;align-items:center}
.btn{
  display:inline-flex;align-items:center;gap:.5rem;
  padding:.7rem 1.3rem;border-radius:7px;text-decoration:none;
  font-size:.97rem;font-weight:500;letter-spacing:.005em;
  transition:transform .2s var(--ease),box-shadow .2s var(--ease),background .2s var(--ease);
}
.btn-primary{background:var(--gold);color:#08131F;
  box-shadow:0 1px 2px rgba(0,0,0,.5),0 10px 26px -12px rgba(123,167,212,.5)}
.btn-primary:hover{background:var(--signal);color:#08131F;transform:translateY(-1px);
  box-shadow:0 2px 4px rgba(0,0,0,.5),0 16px 34px -12px rgba(123,167,212,.7)}
.btn-ghost{color:var(--ink);border:1px solid var(--slate);background:color-mix(in srgb,var(--dusk) 70%,transparent)}
.btn-ghost:hover{color:var(--flash);border-color:var(--brass);transform:translateY(-1px)}

/* ------------------------------------------------------------ the strip */
/* A real captured log, with a real gap in the timestamp column. The single
   most distinctive fact about this product, shown rather than described. */
.strip{border-bottom:1px solid var(--ridge);background:var(--dusk)}
.strip-inner{max-width:min(76rem,92vw);margin:0 auto;padding:2.6rem clamp(1.1rem,4vw,2.5rem)}
.strip h2{font-size:.78rem;letter-spacing:.14em;text-transform:uppercase;color:var(--ink-3);
  font-weight:500;margin:0 0 1.1rem}
.log{font-family:'JetBrains Mono',ui-monospace,monospace;font-size:.845rem;line-height:1.95;
  font-variant-numeric:tabular-nums;overflow-x:auto;margin:0}
.log .t{color:var(--ink-3)}
.log .gap{color:var(--gold);background:linear-gradient(90deg,
  color-mix(in srgb,var(--gold) 13%,transparent),transparent 62%);
  display:block;padding-left:.4rem;margin-left:-.4rem;border-radius:3px}

/* ---------------------------------------------------------------- shell */
/* Three columns on a docs page: navigation, the reading column, and the
   page's own headings.

   The layout this replaces was a full-bleed header above a centred 68ch
   column. The logo sat hard left, the first word of the body began a third of
   the way across, and nothing shared an edge with anything - which reads as
   broken even to a reader who could not say why, and wastes most of a wide
   window doing it.

   The gutters are deliberately small. Width here is not decoration: it is how
   much of a captured log fits on one line before it wraps, and a wrapped log
   line is harder to scan for the gap that matters. */
/* ------------------------------------------------------------- diagrams */
/* Coloured from the same variables as the prose, so there is no second
   palette to keep in step and they are right in whatever the theme becomes. */
.dgw{margin:2.4rem 0;padding:0}
.dg{width:100%;height:auto;display:block;color:var(--ink-2)}
.dgw figcaption{
  margin-top:.9rem;font-size:.88rem;color:var(--ink-2);line-height:1.5;
  text-wrap:pretty;
}
.dg-box rect{fill:var(--dusk);stroke:var(--ridge);stroke-width:1}
.dg-gate rect{fill:none;stroke:var(--ridge);stroke-width:1;stroke-dasharray:3 3}
.dg-chan rect{fill:var(--dusk);stroke:var(--ridge);stroke-width:1}
.dg-line path{stroke:var(--ridge);stroke-width:1.25;fill:none}
.dg-line.dg-no path{stroke-dasharray:3 3}
.dg text{font-family:'Instrument Sans',system-ui,sans-serif}
.dg-label,.dg text.dg-label{fill:var(--ink);font-size:13px;font-weight:500;text-anchor:middle}
.dg-sub,.dg text.dg-sub{fill:var(--ink-2);font-size:11px;text-anchor:middle;letter-spacing:.02em}
.dg-note text{fill:var(--ink-2);font-size:11px;text-anchor:middle}
.dg-foot{fill:var(--ink-2);font-size:11.5px;text-anchor:middle}
.dg-mono text,.dg .dg-mono text{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;
  font-size:12.5px;fill:var(--ink-2);text-anchor:start}
.dg-ok{fill:var(--gold);font-size:13px;font-weight:500;text-anchor:middle}
.dg-flag rect{fill:color-mix(in srgb,var(--gold) 14%,transparent);stroke:var(--gold);stroke-width:1}
.dg-flagtext{fill:var(--ink);font-size:11.5px;text-anchor:start}
.missing{color:#e06c75;font-weight:600}

/* A diagram is scanned, so it may use the whole column rather than the
   reading measure. Below that it is smaller than its own labels. */
.docs-shell main .dgw{max-width:none;width:100%}
@media(max-width:560px){.dg text{font-size:14px}.dg-sub,.dg-foot{font-size:12px}}

/* ----------------------------------------------------------------- main */
/* Inside the shell the column already sets the width, so main only sets the
   measure it will not exceed. --measure is the line length prose is readable
   at; the column is wider than that so tables and code can use the room. */
main{max-width:var(--measure);margin:0;padding:2.6rem 0 6rem}
main.wide{max-width:min(76rem,92vw);margin:0 auto;padding:3.4rem clamp(1.1rem,4vw,2.5rem) 6rem}

/* A table or a code block may use the whole column: they are scanned, not
   read, and a 68ch table wraps cells that were meant to line up. */
.docs-shell main table,.docs-shell main pre{max-width:none;width:100%}

/* And a table WIDER than the column scrolls rather than being cut off. The
   host-by-transport matrix is five columns and cannot shrink to a phone; with
   body overflow-x hidden, the columns on the right simply vanished, which is
   the worst possible failure for a table whose whole content is the right-hand
   columns. A wrapper rather than display:block on the table itself, so the
   table keeps its own layout. */
.tw{overflow-x:auto;max-width:100%}
.tw table{min-width:32rem}
@media(min-width:64rem){.tw table{min-width:0}}

/* ------------------------------------------------- the documentation shell */
/* Mobile first, and that is not a style preference. The old rules built three
   columns and then took them apart twice; below 820px the sidebar became a
   wrapped row of every page with the group headings hidden, which is not
   navigation - it is a sitemap dumped above the article. Position in that row
   changed with the viewport, so there was nothing to remember, and a keyboard
   user tabbed through twenty-three links to reach the first word. */

.skip-link{
  position:fixed;top:.6rem;left:.6rem;z-index:200;
  padding:.65rem .9rem;border-radius:6px;
  background:var(--flash);color:var(--night);
  font-weight:600;text-decoration:none;
  transform:translateY(calc(-100% - 1.5rem))
}
.skip-link:focus{transform:none}
main:focus{outline:none}

.mobile-bar{position:sticky;top:0;z-index:50;justify-content:space-between}
.menu-button,.menu-close{
  display:inline-flex;align-items:center;justify-content:center;gap:.45rem;
  min-width:2.75rem;min-height:2.75rem;padding:.45rem .65rem;
  border:1px solid var(--slate,var(--ridge));border-radius:7px;
  background:var(--dusk);color:var(--ink);
  font:600 .9rem/1 'Archivo',ui-sans-serif,system-ui,sans-serif;cursor:pointer
}
.menu-button:hover,.menu-close:hover{border-color:var(--gold);color:var(--flash)}
.menu-button svg,.menu-close svg{
  width:1.25rem;height:1.25rem;fill:none;stroke:currentColor;
  stroke-width:1.8;stroke-linecap:round
}
/* Never offer a control that cannot work. */
.no-js .menu-button{display:none}

.nav-dialog{
  position:fixed;inset:0;
  width:100vw;max-width:none;height:100dvh;max-height:none;
  margin:0;padding:0;border:0;background:transparent;color:var(--ink);overflow:hidden
}
.nav-dialog::backdrop{background:rgba(4,7,11,.72)}
.nav-dialog-panel{
  width:min(20rem,calc(100vw - 2rem));height:100dvh;
  overflow-y:auto;scrollbar-gutter:stable;
  background:var(--night);border-right:1px solid var(--ridge);
  box-shadow:1rem 0 3rem rgba(0,0,0,.45)
}
.nav-dialog-head{
  position:sticky;top:0;z-index:1;
  display:flex;align-items:center;justify-content:space-between;
  min-height:3.5rem;padding:.6rem .8rem .6rem 1rem;
  background:var(--night);border-bottom:1px solid var(--ridge)
}
.nav-dialog-head h2{
  margin:0;padding:0;border:0;
  font:600 1rem/1.2 'Archivo',ui-sans-serif,system-ui,sans-serif;letter-spacing:0
}
.nav-dialog .side-nav{padding:1rem .75rem 2rem}
/* A browser with no native dialog has no UA rule hiding a closed one, so
   without this the whole drawer paints over the page for ever - and the
   no-JS sidebar appears underneath it, giving two copies of the navigation.
   Belt and braces, because the failure is total. */
.no-js .nav-dialog{display:none}
.nav-dialog:not([open]){display:none}
html:has(.nav-dialog[open]),body:has(.nav-dialog[open]){overflow:hidden}

.docs-shell{display:block;width:100%;max-width:90rem;margin:0 auto}

/* Below the desktop breakpoint the sidebar is the no-JS fallback: a grouped,
   vertical list above the article. Plainer than the drawer and still readable,
   which is the right way for a fallback to differ. */
.side{position:static;padding:1rem;border-bottom:1px solid var(--ridge)}
.js .side{display:none}
.side-brand{display:none}

.side-nav{margin:0}
.side-group+.side-group{margin-top:1.35rem}
.side-nav .grp{
  margin:0 0 .4rem;padding:0 .7rem;color:var(--ink-3,var(--ink-2));
  font-size:.72rem;font-weight:600;letter-spacing:.12em;text-transform:uppercase
}
.side-nav ul{margin:0;padding:0;list-style:none}
.side-nav li{margin:0}
.side-nav a{
  display:flex;align-items:center;min-height:2.75rem;padding:.45rem .7rem;
  border-radius:6px;color:var(--ink-2);text-decoration:none;
  font-size:.92rem;line-height:1.35;
  transition:color .15s var(--ease),background .15s var(--ease)
}
.side-nav a::after{display:none}
.side-nav a:hover{color:var(--ink);background:var(--dusk)}
.side-nav a.here{color:var(--flash);background:var(--dusk);font-weight:600;
  box-shadow:inset 2px 0 var(--gold)}

.col{min-width:0;padding:0 1.1rem}
.docs-shell main{max-width:80ch;margin:0;padding:2rem 0 5rem}
.rail{display:none}

/* Without this a heading jumped to from the rail lands under the sticky bar. */
main :where(h1,h2,h3,h4){scroll-margin-top:4.75rem}

/* 64rem: room for a permanent sidebar beside a readable column. */
@media(min-width:64rem){
  .mobile-bar{display:none}
  .docs-shell{display:grid;grid-template-columns:15rem minmax(0,1fr);align-items:start}
  .side,.js .side{
    display:block;position:sticky;top:0;align-self:start;
    height:100dvh;max-height:none;padding:0 0 2rem;
    overflow-y:auto;scrollbar-gutter:stable;
    border-right:1px solid var(--ridge);border-bottom:0
  }
  .side-brand{display:flex;min-height:3.5rem;margin:0;padding:.6rem 1rem;
    border-bottom:1px solid var(--ridge)}
  .side .side-nav{padding:1.25rem .75rem 2rem}
  .side-nav a{min-height:2.1rem;padding:.35rem .7rem}
  .col{padding:0 3rem}
  .docs-shell main{padding-top:2.75rem}
  main :where(h1,h2,h3,h4){scroll-margin-top:2rem}
}

/* 80rem: and now the rail as well. Contiguous tracks, no grid gap - the old
   layout spent two explicit gaps plus per-link margins, so a one-pixel resize
   at the old 1180 breakpoint took about 300px off the article at once. */
@media(min-width:80rem){
  .docs-shell{grid-template-columns:15rem minmax(0,1fr) 15rem}
  .rail{
    display:block;position:sticky;top:0;align-self:start;
    height:100dvh;padding:2.9rem 1rem 2rem;
    overflow-y:auto;scrollbar-gutter:stable
  }
  .rail .grp{
    margin:0 0 .6rem;color:var(--ink-2);
    font-size:.72rem;font-weight:600;letter-spacing:.12em;text-transform:uppercase
  }
  .rail nav{display:flex;flex-direction:column;gap:.05rem;margin:0}
  .rail nav a{
    padding:.3rem 0 .3rem .7rem;border-left:1px solid var(--ridge);
    color:var(--ink-2);text-decoration:none;font-size:.85rem;line-height:1.4
  }
  .rail nav a::after{display:none}
  .rail nav a:hover{color:var(--ink);border-left-color:var(--gold)}
}

main ol{margin:1.1rem 0;padding-left:1.35rem}
main ol li{margin:.35rem 0;padding-left:.25rem}
main ol li::marker{color:var(--ink-2)}

h1,h2,h3,h4{text-wrap:balance}

/* --------------------------------------------------------------- printing */
/* A page box is narrower than the desktop breakpoint, so without this every
   printed page carried the mobile bar and a Menu button that does nothing on
   paper, while the sidebar stayed hidden. Print the article and nothing else. */
@media print{
  .mobile-bar,.nav-dialog,.side,.rail,.skip-link,.site-header,footer{display:none}
  .docs-shell{display:block;max-width:none}
  .col{padding:0}
  .docs-shell main{max-width:none;padding:0}
  a[href^="/"]::after{content:" (" attr(href) ")";font-size:.85em;color:#555}
}
main h1{font-family:'Archivo',sans-serif;font-weight:600;
  font-size:clamp(2.1rem,4.4vw,3rem);line-height:1.08;letter-spacing:-.032em;margin:.2em 0 .5em}
main h2{font-family:'Archivo',sans-serif;font-weight:600;
  font-size:clamp(1.4rem,2.6vw,1.8rem);line-height:1.22;letter-spacing:-.022em;
  margin:2.6em 0 .6em;padding-top:1.4rem;border-top:1px solid var(--ridge)}
main h3{font-size:1.1rem;font-weight:600;margin:2.1em 0 .45em;letter-spacing:-.005em}
main h4{font-size:.98rem;font-weight:600;color:var(--ink-2);margin:1.7em 0 .35em}
p,li{margin:.85em 0;text-wrap:pretty}
strong{font-weight:600;color:var(--flash)}
em{color:var(--ink)}
ul{padding-left:1.15rem}
li::marker{color:var(--brass)}

code{font-family:'JetBrains Mono',ui-monospace,monospace;font-size:.88em;
  background:var(--dusk);border:1px solid var(--ridge);padding:.1em .38em;border-radius:4px;
  color:var(--gold)}
pre{background:var(--dusk);border:1px solid var(--ridge);border-radius:9px;
  padding:1.05rem 1.25rem;overflow-x:auto;
  box-shadow:0 1px 2px rgba(0,0,0,.4),0 14px 34px -22px rgba(0,0,0,.9)}
pre code{background:none;border:0;padding:0;color:var(--ink);font-size:.855rem;line-height:1.62}

table{border-collapse:collapse;width:100%;margin:1.5em 0;font-size:.945rem}
th,td{text-align:left;padding:.62rem .8rem;border-bottom:1px solid var(--ridge);vertical-align:top}
th{font-weight:600;color:var(--ink-2);font-size:.82rem;letter-spacing:.06em;text-transform:uppercase}
tbody tr{transition:background .16s var(--ease)}
tbody tr:hover{background:color-mix(in srgb,var(--gold) 4%,transparent)}

/* --------------------------------------------------------------- footer */
footer{border-top:1px solid var(--ridge);background:var(--dusk);
  padding:2.6rem clamp(1.1rem,4vw,2.5rem);color:var(--ink-3);font-size:.9rem}
footer .inner{max-width:min(76rem,92vw);margin:0 auto;display:flex;gap:1.6rem;
  flex-wrap:wrap;align-items:center;justify-content:space-between}
footer p{margin:0}

/* ---------------------------------------------------------------- motion */
@media (prefers-reduced-motion:reduce){
  *,*::before,*::after{animation-duration:.001ms!important;animation-iteration-count:1!important;
    transition-duration:.001ms!important}
}
/* The consent banner. Fixed to the bottom corner and never modal: the page
   stays readable, and a reader who ignores it is a reader who declined. */
.consent{position:fixed;top:auto;left:auto;right:1rem;bottom:1rem;margin:0;z-index:50;
  width:min(26rem,calc(100vw - 2rem));box-sizing:border-box;
  background:var(--dusk);color:var(--ink);border:1px solid var(--ridge);border-radius:12px;
  padding:1rem 1.1rem;font-size:.92rem;line-height:1.5;
  box-shadow:0 12px 40px rgba(4,7,11,.65)}
.consent:not([open]){display:none}
.consent p{margin:0 0 .85rem}
.consent a{color:var(--gold)}
.consent-actions{display:flex;gap:.6rem;justify-content:flex-end}
.consent .btn{padding:.5rem .95rem;font-size:.9rem;cursor:pointer;
  font-family:inherit;border:1px solid transparent}
.consent .btn-ghost{border-color:var(--slate)}

/* The copy button on a code block. Hidden without JS, because a control that
   cannot work is worse than no control, and most of this site's readers are
   agents that run none of it. */
.code{position:relative}
/* Room for the button, so it never sits on the first line. The commands here
   are long enough to scroll, and the one most worth copying is the one that
   was underneath it. */
.code pre{padding-right:4.6rem}
.code .copy{
  position:absolute;top:.55rem;right:.55rem;
  display:inline-flex;align-items:center;gap:.35rem;
  padding:.3rem .6rem;border-radius:6px;cursor:pointer;
  background:var(--slate);color:var(--ink-2);border:1px solid var(--ridge);
  font:500 .74rem/1 'Archivo',ui-sans-serif,system-ui,sans-serif;letter-spacing:.02em;
  opacity:0;transition:opacity .18s var(--ease),color .18s var(--ease),border-color .18s var(--ease)}
.code:hover .copy,.code .copy:focus-visible{opacity:1}
.code .copy:hover{color:var(--flash);border-color:var(--brass)}
.code .copy[data-done]{opacity:1;color:var(--gold);border-color:var(--brass)}
.no-js .copy,.no-js .page-actions{display:none}
/* Touch has no hover, so the button would never appear. */
@media (hover:none){.code .copy{opacity:1}}

/* The breadcrumb and the two markdown controls, above the title. The JSON-LD
   has claimed a breadcrumb since the SEO work; this is the reader's copy. */
.crumbs{font-size:.82rem;color:var(--ink-3);margin:0 0 .45rem;
  display:flex;align-items:center;gap:.4rem;flex-wrap:wrap}
.crumbs a{color:var(--ink-3);text-decoration:none}
.crumbs a:hover{color:var(--gold)}
.crumbs span[aria-hidden]{opacity:.55}
.crumbs .here{color:var(--ink-2)}
.page-actions{display:flex;gap:.5rem;flex-wrap:wrap;margin:0 0 1.6rem}
.page-actions a,.page-actions button{
  display:inline-flex;align-items:center;gap:.4rem;
  padding:.36rem .7rem;border-radius:6px;cursor:pointer;text-decoration:none;
  background:transparent;color:var(--ink-2);border:1px solid var(--ridge);
  font:500 .8rem/1.1 'Archivo',ui-sans-serif,system-ui,sans-serif;
  transition:color .18s var(--ease),border-color .18s var(--ease)}
.page-actions a:hover,.page-actions button:hover{color:var(--flash);border-color:var(--brass)}
.page-actions button[data-done]{color:var(--gold);border-color:var(--brass)}
.page-actions svg{width:14px;height:14px;flex:none}

/* The GitHub mark, wherever a link points at the repository. */
.gh{width:1em;height:1em;vertical-align:-.14em;flex:none}
.site-header nav a .gh,footer .gh{margin-right:.32em}

/* The rail marks where you are. Without it the column is a list of links
   that never changes while the page moves under it. */
.rail nav a.here{color:var(--flash)}
.rail nav a.here::before{background:var(--gold)}

/* The DBHQ menu in the home header. A <details>, so it opens with no
   JavaScript; the panel is absolutely positioned so opening it does not
   push the header's own links sideways. */
.org-menu{position:relative;display:inline-block}
.org-menu>summary{
  display:inline-flex;align-items:center;gap:.28rem;cursor:pointer;list-style:none;
  color:var(--ink-2);transition:color .2s var(--ease)}
.org-menu>summary::-webkit-details-marker{display:none}
.org-menu>summary:hover,.org-menu[open]>summary{color:var(--flash)}
.org-menu>summary svg{width:13px;height:13px;transition:transform .2s var(--ease)}
.org-menu[open]>summary svg{transform:rotate(180deg)}
.org-panel{
  position:absolute;right:0;top:calc(100% + .7rem);z-index:40;
  width:min(20rem,calc(100vw - 2rem));
  display:flex;flex-direction:column;
  background:var(--dusk);border:1px solid var(--ridge);border-radius:10px;
  padding:.4rem;box-shadow:0 16px 44px rgba(4,7,11,.7)}
.org-panel a{
  display:block;padding:.55rem .65rem;border-radius:7px;text-decoration:none;
  color:var(--ink);line-height:1.35}
.org-panel a:hover{background:var(--slate)}
.org-panel a b{display:block;font-weight:600;font-size:.94rem}
.org-panel a span{display:block;color:var(--ink-3);font-size:.82rem;margin-top:.1rem}
.org-panel .org-all{
  margin-top:.25rem;border-top:1px solid var(--ridge);border-radius:0 0 7px 7px;
  color:var(--gold);font-size:.88rem;font-weight:500}
/* The panel stays anchored to the menu's RIGHT edge at every width. An
   earlier left:0 override for narrow screens ran it off the side of a phone:
   the menu sits near the right edge, so a panel growing rightwards from there
   has nowhere to go. */


/* ------------------------------------------------------------ the matrix */
:root{
  --mx-proven:#6FD08C;
  --mx-works:#7BA7D4;
  --mx-needs:#E8B93F;
  --mx-untested:#93A1B5;
  --mx-none:#2A1F27;
}
.vh{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;
  clip:rect(0 0 0 0);white-space:nowrap;border:0}

/* THIS PAGE IS AN APPLICATION, not a document with a widget in it. The prose
   above the grid was cut to three sentences and the grid takes the room that
   freed: full width of the shell, and the height of the viewport under it. */
.docs-shell--wide{max-width:none;padding:0 clamp(.6rem,2vw,1.6rem)}
.docs-shell--wide main{max-width:none;padding-top:1.2rem;padding-bottom:1.5rem}
.docs-shell--wide .rail{display:none}
.docs-shell--wide main h1{font-size:clamp(1.5rem,3.2vw,2.1rem);margin-bottom:.3rem}
.docs-shell--wide main h1+p{margin:0 0 .9rem;color:var(--ink-3);font-size:.92rem}
.mxa{margin:0 0 1.4rem;display:flex;flex-direction:column;gap:.8rem}

/* -- the toolbar -- */
.mxa-bar{display:flex;flex-wrap:wrap;gap:.8rem 1.4rem;align-items:flex-end;
  padding:.75rem .9rem;border:1px solid var(--ridge);border-radius:12px;background:var(--dusk)}
.mxa-fg{min-width:0}
.mxa-fl{display:block;font-size:.66rem;letter-spacing:.09em;text-transform:uppercase;
  color:var(--ink-3);margin-bottom:.35rem}
.mxa-chips{display:flex;gap:.3rem;flex-wrap:wrap}
.mxa-chip{border:1px solid var(--ridge);background:none;color:var(--ink-2);font:inherit;
  font-size:.8rem;border-radius:999px;padding:.24rem .7rem;cursor:pointer;white-space:nowrap;
  transition:border-color .15s,color .15s,background .15s}
.mxa-chip:hover{border-color:var(--brass);color:var(--ink)}
.mxa-chip:focus-visible{outline:2px solid var(--gold);outline-offset:2px}
.mxa-chip[aria-checked="true"]{background:var(--gold);border-color:var(--gold);color:#06213A;font-weight:600}
.mxa-count{margin:0 0 .1rem auto;font-size:.8rem;color:var(--ink-3);white-space:nowrap}
.mxa-count b{color:var(--flash);font-size:1.45rem;font-weight:600;line-height:1;
  font-variant-numeric:tabular-nums;margin-right:.15rem}

/* One line, and only while that shape is chosen. */
.mxa-def{display:none;margin:0;padding:.55rem .8rem;border-left:3px solid var(--brass);
  background:var(--dusk);font-size:.85rem;color:var(--ink-2);border-radius:0 8px 8px 0}
.mxa-def b{color:var(--flash)}
[data-kindfilter="beacon"] .mxa-def[data-def="beacon"],
[data-kindfilter="flare"] .mxa-def[data-def="flare"]{display:block}

/* -- grid beside panel -- */
.mxa-body{display:grid;grid-template-columns:minmax(0,1fr) 19rem;gap:1rem;align-items:stretch}
.mxa-gridwrap{overflow:auto;border:1px solid var(--ridge);border-radius:12px;background:var(--dusk)}

.mxa-grid{border-collapse:separate;border-spacing:0;width:100%;min-width:38rem;margin:0;
  font-size:.9rem;table-layout:fixed}
.mxa-grid td,.mxa-grid th{border:0;padding:0;vertical-align:middle}
.mxa-corner{width:10.5rem;min-width:10.5rem;padding:.45rem .6rem;line-height:1.15;
  position:sticky;left:0;top:0;z-index:4;background:var(--dusk);overflow:hidden}
.mxa-corner span{display:block;font-size:.6rem;letter-spacing:.08em;text-transform:uppercase;color:var(--ink-3)}
.mxa-corner span:last-child{text-align:right;color:var(--brass)}

/* The head row sticks too: scrolling ten rows should not cost you the column
   names, and this grid is meant to be scrolled. */
.mxa-grid thead th{padding:.45rem .25rem;text-align:center;font-size:.72rem;font-weight:600;
  color:var(--ink-2);border-bottom:1px solid var(--ridge);background:var(--dusk);
  position:sticky;top:0;z-index:3;transition:color .15s,background .15s}
.mxa-grid thead th a{color:inherit;text-decoration:none}
.mxa-grid thead th a:hover{text-decoration:underline}
.mxa-grid thead th em{display:block;font-style:normal;font-size:.56rem;letter-spacing:.08em;
  text-transform:uppercase;color:var(--ink-3);margin-top:.1rem}

.mxa-grid tbody th{text-align:left;padding:.4rem .6rem;font-size:.8rem;font-weight:500;
  color:var(--ink-2);text-transform:none;letter-spacing:0;border-right:1px solid var(--ridge);
  transition:color .15s,background .15s;position:sticky;left:0;z-index:2;background:var(--dusk)}
.mxa-grid tbody th a{color:inherit;text-decoration:none}
.mxa-grid tbody th a:hover{text-decoration:underline}
.mxa-grid tbody th em{display:block;font-style:normal;font-size:.6rem;color:var(--ink-3)}
.mxa-grid tbody tr:nth-child(even) td{background:rgba(255,255,255,.012)}

/* -- the cells -- */
.mxa-grid tbody td{text-align:center}
.mxa-grid tbody td button{display:block;width:100%;border:0;background:none;
  padding:.5rem .2rem;cursor:pointer;color:inherit;min-height:2.6rem}
.mxa-grid tbody td button:focus{outline:none}
.mxa-grid tbody td button:focus-visible{outline:2px solid var(--flash);outline-offset:-2px;border-radius:6px}

/* EVERY STATE HAS ITS OWN SHAPE as well as its own hue, because five states
   in one hue differ only by brightness and that is the distinction a
   colour-blind reader loses first. */
.mxa-dot{display:inline-block;width:1rem;height:1rem;border-radius:4px;
  background:var(--mx-none);transition:transform .18s var(--ease),box-shadow .18s}
[data-state="proven"] .mxa-dot{background:var(--mx-proven);border-radius:4px}
[data-state="works"] .mxa-dot{background:var(--mx-works);border-radius:50%}
[data-state="needs"] .mxa-dot{background:none;border:2px solid var(--mx-needs);border-radius:50%}
[data-state="untested"] .mxa-dot{background:none;border:2px dashed var(--mx-untested);border-radius:4px}
[data-state="none"] .mxa-dot{background:none;position:relative;opacity:.5}
[data-state="none"] .mxa-dot:before,[data-state="none"] .mxa-dot:after{
  content:"";position:absolute;inset:45% 12%;background:var(--slate);border-radius:1px}
[data-state="none"] .mxa-dot:before{transform:rotate(45deg)}
[data-state="none"] .mxa-dot:after{transform:rotate(-45deg)}
@media(hover:hover){.mxa-grid tbody td:hover .mxa-dot{transform:scale(1.22)}}

/* -- crosshair and flash -- */
/* A STICKY CELL MUST STAY OPAQUE. This rule set a translucent tint with the
   background shorthand, which threw away the opaque var(--dusk) underneath -
   so on the lit row the label column became see-through and the cells
   scrolling beneath it showed straight through the label. The tint goes on as
   an image layer over an opaque colour instead, which looks identical and
   hides what is behind it. */
.mxa-grid tbody tr[data-lit] th,.mxa-grid thead th[data-lit]{color:var(--flash);
  background-color:var(--dusk);
  background-image:linear-gradient(rgba(123,167,212,.14),rgba(123,167,212,.14))}
.mxa-grid td[data-lit]{background:rgba(123,167,212,.06)}
.mxa-grid td[data-on] .mxa-dot{box-shadow:0 0 0 3px rgba(230,241,251,.22),0 0 16px 2px currentColor;transform:scale(1.3)}
.mxa-grid td[data-on]{background:rgba(123,167,212,.14);box-shadow:inset 0 0 0 1px var(--gold)}

/* The heliograph is a mirror flashing light across a valley. */
@keyframes mx-beam{from{background-position:-40% 0}to{background-position:140% 0}}
.mxa-grid tbody tr[data-lit]{background-image:linear-gradient(90deg,
  transparent 0%,rgba(230,241,251,.10) 45%,rgba(230,241,251,.16) 50%,rgba(230,241,251,.10) 55%,transparent 100%);
  background-size:60% 100%;background-repeat:no-repeat;animation:mx-beam .55s var(--ease) 1}
@media(prefers-reduced-motion:reduce){
  .mxa-grid tbody tr[data-lit]{animation:none;background-image:none}
  .mxa-dot,.mxa-panel{transition:none}
}

/* -- the panel -- */
.mxa-panel{border:1px solid var(--ridge);border-radius:12px;background:var(--dusk);
  padding:.9rem 1rem 1rem;align-self:start;position:sticky;top:1rem}
.mxa-state{margin:0 0 .4rem;font-size:.66rem;letter-spacing:.1em;text-transform:uppercase;
  font-weight:700;display:inline-block;padding:.16rem .5rem;border-radius:5px;color:#06213A}
.mxa-state[data-state="proven"]{background:var(--mx-proven)}
.mxa-state[data-state="works"]{background:var(--mx-works)}
.mxa-state[data-state="needs"]{background:var(--mx-needs)}
.mxa-state[data-state="untested"]{background:var(--mx-untested)}
.mxa-state[data-state="none"]{background:var(--slate);color:var(--ink-2)}
.mxa-panel h3{margin:0 0 .45rem;font-size:1rem;line-height:1.3;color:var(--flash)}
.mxa-panel h3 span{color:var(--ink-3);font-weight:400}
.mxa-panel p[data-panel-note]{margin:0 0 .7rem;font-size:.86rem;color:var(--ink-2);line-height:1.5}
.mxa-meta{margin:0 0 .7rem;display:grid;gap:.28rem;font-size:.76rem}
.mxa-meta div{display:flex;gap:.5rem;justify-content:space-between;border-bottom:1px solid var(--ridge);padding-bottom:.22rem}
.mxa-meta dt{color:var(--ink-3);margin:0}
.mxa-meta dd{margin:0;color:var(--ink-2);text-align:right}
.mxa-links{margin:0 0 .4rem;display:flex;gap:.8rem;flex-wrap:wrap;font-size:.8rem}
.mxa-hint{margin:0;font-size:.72rem;color:var(--ink-3)}
.mxa-close{display:none}

/* -- legend, folded tables, filter feedback -- */
.mxa-legend{list-style:none;display:flex;flex-wrap:wrap;gap:.3rem 1rem;margin:0;padding:0;
  font-size:.76rem;color:var(--ink-3)}
.mxa-legend li{display:flex;align-items:center;gap:.35rem}
.mxa-legend .mxa-dot{width:.75rem;height:.75rem}
.mxa-tables{margin:.4rem 0 0;border:1px solid var(--ridge);border-radius:10px;background:var(--dusk)}
.mxa-tables summary{cursor:pointer;padding:.55rem .9rem;font-size:.86rem;color:var(--ink-2)}
.mxa-tables summary:hover{color:var(--flash)}
.mxa-tables[open] summary{border-bottom:1px solid var(--ridge)}
.mxa-tables .tw{padding:0 .9rem .4rem}
.mxa-grid [data-dim]{opacity:.22}
.mxa-grid [data-dim] .mxa-dot{filter:grayscale(1)}
.mxa-panel[data-hidden]{border-style:dashed;opacity:.75}
.mxa-panel[data-hidden]:before{content:"Filtered out of the grid";display:block;
  font-size:.64rem;letter-spacing:.08em;text-transform:uppercase;color:var(--mx-needs);margin-bottom:.45rem}
.mxa-empty{margin:0;padding:.65rem .9rem;border:1px dashed var(--mx-needs);border-radius:8px;
  color:var(--ink-2);font-size:.86rem}
.mxa-empty[hidden]{display:none}

/* ---------------------------------------------------------------- mobile */
/* A phone gets a genuinely different layout rather than the desktop one made
   small: the grid keeps the full width and the panel becomes a sheet that
   rises when a cell is tapped, because a 19rem column beside a 7-column grid
   on a 380px screen is two things neither of which is usable. */
@media(max-width:62rem){
  .mxa-body{grid-template-columns:1fr}
  .mxa-bar{gap:.6rem 1rem;padding:.6rem .7rem}
  .mxa-fg{flex:1 1 100%}
  /* One scrolling line per group. Wrapping three groups of chips is a
     toolbar taller than the grid it controls. */
  .mxa-chips{flex-wrap:nowrap;overflow-x:auto;padding-bottom:.15rem;
    scrollbar-width:none;-ms-overflow-style:none}
  .mxa-chips::-webkit-scrollbar{display:none}
  .mxa-count{margin:0;order:-1;flex:1 1 100%}
  .mxa-gridwrap{max-height:none}
  .mxa-grid{min-width:34rem;font-size:.84rem}
  .mxa-corner{width:7.5rem;min-width:7.5rem}
  /* "TRANSPORT" does not fit 7.5rem and was spilling over the first column. */
  .mxa-corner span{font-size:.52rem;letter-spacing:.04em}
  .mxa-grid tbody th{padding:.36rem .45rem;font-size:.74rem}
  .mxa-grid tbody th em{display:none}
  .mxa-grid thead th em{display:none}

  .mxa-panel{position:fixed;left:0;right:0;bottom:0;top:auto;z-index:40;
    border-radius:14px 14px 0 0;border-bottom:0;max-height:62vh;overflow-y:auto;
    box-shadow:0 -18px 40px rgba(0,0,0,.55);
    transform:translateY(105%);transition:transform .22s var(--ease);
    padding-bottom:calc(1rem + env(safe-area-inset-bottom))}
  .mxa-panel[data-open]{transform:translateY(0)}
  .mxa-hint{display:none}
  .mxa-close{display:block;width:100%;margin-top:.6rem;border:1px solid var(--ridge);
    background:none;color:var(--ink-2);font:inherit;font-size:.85rem;border-radius:8px;
    padding:.55rem;cursor:pointer}
  .mxa-close:hover{border-color:var(--gold);color:var(--flash)}
  /* A 2.6rem tap target is below what a thumb can hit reliably. */
  .mxa-grid tbody td button{min-height:2.9rem;padding:.55rem .2rem}
}
@media(max-width:26rem){
  .mxa-grid{min-width:30rem}
  .mxa-corner{width:6.2rem;min-width:6.2rem}
  .mxa-corner span:last-child{display:none}
}
