/* Generated by tools/site from docs/18-website.umsg. Do not edit; edit that document. */

:root {
  /* Instrument panel, not brochure. Dark, quiet, monospace, with exactly one warm accent so that the thing being pointed at is unambiguous. Futuristic by restraint rather than by chrome. */
  --void:      #05070c;   /* page and banner background */
  --surface:   #0b1018;   /* cards, code blocks */
  --rule:      #1b2430;   /* hairlines, table borders */
  --ink:       #c8d3e0;   /* body text */
  --ink-quiet: #7d8a9b;   /* captions, comments in code */
  --cyan:      #5fd7e8;   /* sigils, links, the far depth plane */
  --amber:     #e8b45f;   /* the single accent; strings, the focal glow */
  --violet:    #9d8ff0;   /* symbols and tags */
  --green:     #7fd88a;   /* byte output, hex, verified marks */
  --red:       #f0776f;   /* errors, and the weaknesses page's headings */
  --syntax-comment: #758193;   /* syntax legend: comments, cool neutral grey */
  --syntax-header: #d2d2f0;   /* syntax legend: headers */
  --syntax-string: #ffb45a;   /* syntax legend: strings */
  --syntax-symbol: #a587f0;   /* syntax legend: symbols */
  --syntax-extension: #d25af0;   /* syntax legend: extensions */
  --syntax-annotation: #b4c35a;   /* syntax legend: annotations */
  --syntax-identifier: #3cc3c3;   /* syntax legend: identifiers */
  --syntax-reference: #3c96ff;   /* syntax legend: references */
  --syntax-literal: #ff78c3;   /* syntax legend: reserved literals */
  --syntax-key: #f0f3f7;   /* syntax legend: keys, neutral white */
  --syntax-hash: #3cd269;   /* syntax legend: hashes */
  --syntax-bytes: #3cf0b4;   /* syntax legend: byte strings */
  --syntax-integer: #e1a5a5;   /* syntax legend: integers */
  --syntax-decimal: #d2873c;   /* syntax legend: exact decimals */
  --syntax-float: #e187ff;   /* syntax legend: binary floats */
  --syntax-timestamp: #3cf0ff;   /* syntax legend: temporal values */
  --syntax-array: #ff3c96;   /* syntax legend: typed arrays */
  --syntax-array-element: #a5f03c;   /* syntax legend: typed-array elements */
  --syntax-array-missing: #ff4b4b;   /* syntax legend: missing array positions */
  --syntax-block: #a5b4f0;   /* syntax legend: block delimiters */
  --syntax-sequence: #b487a5;   /* syntax legend: sequence delimiters */
  --syntax-table-row: #ffb4ff;   /* syntax legend: table-row markers */

  --body-face: ui-serif, Charter, "Bitstream Charter", "Iowan Old Style", Georgia, serif;
  --body-size: 17px;
  --body-lh: 1.62;
  --heading-face: ui-sans-serif, -apple-system, "Segoe UI", Inter, Helvetica, Arial, sans-serif;
  --heading-size: 28px;
  --heading-lh: 1.2;
  --code-face: ui-monospace, Menlo, "SF Mono", "Cascadia Mono", "Roboto Mono", monospace;
  --code-size: 14px;
  --code-lh: 1.55;
  --caption-face: var(--code-face);
  --caption-size: 12px;
  --caption-lh: 1.5;

  --space: 8px;
  --measure: 76ch;
  --radius: 4px;
  --hover-ms: 120ms;
  --hover-ease: linear;
  --disclosure-ms: 220ms;
  --disclosure-ease: cubic-bezier(0.2, 0, 0, 1);
  --page-transition-ms: 0ms;
  --page-transition-ease: none; navigation is navigation;

  --banner-size: 15px;
  --banner-lh: 1.55;
  --banner-mobile-lines: 4;
  --banner-min-height: 36rem;
  --banner-max-height: 70rem;
  --banner-canvas-height: 50vw;
  --banner-page-clearance: 7rem;
  --banner-mobile-min-height: 30rem;
  --banner-mobile-page-clearance: 11rem;
  --x-bg: #0b1018;
  --x-border: #1b2430;
  --x-accent: #5fd7e8;
  --x-bg-opacity: 0.82;
  --x-blur: 14px;
  --x-radius: 6px;
  --x-padding: 12px;
  --x-offset: 10px;
  --x-max-width: 48ch;
  --x-shadow: 0 8px 30px rgba(0, 0, 0, 0.55);
  --x-fade: 120ms;
  --x-hover-delay: 90ms;
  --x-hide-delay: 140ms;
}

/* one-accent-rule: Amber is the only INTERFACE colour permitted to draw the eye. The syntax legend uses colour to identify constructs, never to establish visual hierarchy. */
/* serif-body-monospace-code: Prose is set in a serif and code in a monospace, with no third face. The contrast does the work a second sans face would otherwise be hired for. */
/* dark-only: There is no light theme in v0. A half-maintained second theme is worse than one theme, and the banner is designed against a near-black field that a light theme would have to abandon. */

/* Syntax colouring, from presentation.syntax-colouring. */
.c-annotation         { color: var(--syntax-annotation); }
.c-array              { color: var(--syntax-array); }
.c-array-element      { color: var(--syntax-array-element); }
.c-array-missing      { color: var(--syntax-array-missing); }
.c-block              { color: var(--syntax-block); }
.c-bytes              { color: var(--syntax-bytes); }
.c-comment            { color: var(--syntax-comment); }
.c-decimal            { color: var(--syntax-decimal); }
.c-extension          { color: var(--syntax-extension); }
.c-float              { color: var(--syntax-float); }
.c-hash               { color: var(--syntax-hash); }
.c-header             { color: var(--syntax-header); }
.c-identifier         { color: var(--syntax-identifier); }
.c-integer            { color: var(--syntax-integer); }
.c-key                { color: var(--syntax-key); }
.c-reference          { color: var(--syntax-reference); }
.c-reserved-literal   { color: var(--syntax-literal); }
.c-sequence           { color: var(--syntax-sequence); }
.c-string             { color: var(--syntax-string); }
.c-symbol             { color: var(--syntax-symbol); }
.c-table-row          { color: var(--syntax-table-row); }
.c-timestamp          { color: var(--syntax-timestamp); }
.t-annotation         { color: var(--syntax-annotation); }
.t-array              { color: var(--syntax-array); }
.t-array-bracket      { color: var(--syntax-array); }
.t-array-element      { color: var(--syntax-array-element); }
.t-array-missing      { color: var(--syntax-array-missing); }
.t-brace              { color: var(--syntax-block); }
.t-bracket            { color: var(--syntax-sequence); }
.t-bytes              { color: var(--syntax-bytes); }
.t-com                { color: var(--syntax-comment); }
.t-dec                { color: var(--syntax-decimal); }
.t-ext                { color: var(--syntax-extension); }
.t-float              { color: var(--syntax-float); }
.t-hash               { color: var(--syntax-hash); }
.t-hdr                { color: var(--syntax-header); }
.t-ident              { color: var(--syntax-identifier); }
.t-int                { color: var(--syntax-integer); }
.t-key                { color: var(--syntax-key); }
.t-kw                 { color: var(--syntax-literal); }
.t-pipe               { color: var(--syntax-table-row); }
.t-ref                { color: var(--syntax-reference); }
.t-str                { color: var(--syntax-string); }
.t-sym                { color: var(--syntax-symbol); }
.t-ts                 { color: var(--syntax-timestamp); }

/* The base stylesheet. Every colour, size and duration in it comes from a
   custom property emitted above from the presentation block of
   docs/18-website.umsg — nothing here restates a value that lives there.

   Dark only, by decision D: a half-maintained second theme is worse than one
   theme, and the banner is designed against a near-black field. */

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

html {
  background: var(--void);
  color: var(--ink);
  -webkit-text-size-adjust: 100%;
}

body {
  margin: 0;
  font-family: var(--body-face);
  font-size: var(--body-size);
  line-height: var(--body-lh);
  display: flex;
  flex-direction: column;
  min-height: 100vh;
}

/* ------------------------------------------------------------------ frame */

.skip {
  position: absolute;
  left: -9999px;
  top: var(--space);
  background: var(--surface);
  color: var(--amber);
  padding: var(--space) calc(var(--space) * 2);
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  z-index: 10;
}
.skip:focus { left: var(--space); }

.top {
  display: flex;
  align-items: baseline;
  gap: calc(var(--space) * 2);
  flex-wrap: wrap;
  padding: calc(var(--space) * 1.5) calc(var(--space) * 3);
  border-bottom: 1px solid var(--rule);
  font-family: var(--code-face);
  font-size: var(--caption-size);
  position: sticky;
  top: 0;
  background: color-mix(in srgb, var(--void) 92%, transparent);
  backdrop-filter: blur(6px);
  z-index: 5;
}

.wordmark {
  font-weight: 600;
  letter-spacing: 0.04em;
  color: var(--ink);
  text-decoration: none;
}
.wordmark::before { content: "%"; color: var(--cyan); margin-right: 0.15em; }

.top nav { flex: 1; }
.top nav ul {
  display: flex;
  flex-wrap: wrap;
  gap: calc(var(--space) * 2);
  list-style: none;
  margin: 0;
  padding: 0;
}
.top nav a { color: var(--ink-quiet); text-decoration: none; }
.top nav a:hover { color: var(--cyan); }
.top nav a[aria-current="page"] { color: var(--ink); border-bottom: 1px solid var(--cyan); }

.status {
  font-family: var(--code-face);
  font-size: var(--caption-size);
  color: var(--ink-quiet);
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  padding: 0 calc(var(--space) * 0.75);
}
.status::before { content: "status "; color: color-mix(in srgb, var(--ink-quiet) 60%, transparent); }

main {
  flex: 1;
  width: 100%;
  max-width: calc(var(--measure) + var(--space) * 6);
  margin: 0 auto;
  padding: calc(var(--space) * 5) calc(var(--space) * 3);
}
main.wide { max-width: 84rem; }
main.flush { max-width: none; padding: 0; }
main.flush > .page-body,
main.wide > .page-body { max-width: none; }
main.flush .page-body {
  max-width: calc(var(--measure) + var(--space) * 6);
  margin: 0 auto;
  padding: calc(var(--space) * 5) calc(var(--space) * 3);
}

.bottom {
  border-top: 1px solid var(--rule);
  padding: calc(var(--space) * 3);
  font-family: var(--code-face);
  font-size: var(--caption-size);
  line-height: var(--caption-lh);
  color: var(--ink-quiet);
}
.bottom p { margin: 0 0 calc(var(--space) * 0.5); }
.bottom .machine a { color: var(--cyan); }

/* ------------------------------------------------------------------- text */

h1, h2, h3, h4 {
  font-family: var(--heading-face);
  line-height: var(--heading-lh);
  font-weight: 600;
  letter-spacing: -0.01em;
  margin: calc(var(--space) * 5) 0 calc(var(--space) * 2);
}
h1 { font-size: calc(var(--heading-size) * 1.35); margin-top: 0; }
h2 { font-size: var(--heading-size); }
h3 { font-size: calc(var(--heading-size) * 0.66); }
h4 { font-size: calc(var(--heading-size) * 0.55); font-family: var(--code-face); font-weight: 500; }

p { margin: 0 0 calc(var(--space) * 2); max-width: var(--measure); }
a { color: var(--cyan); text-decoration-thickness: 1px; text-underline-offset: 2px; }
a:hover { color: var(--amber); transition: color var(--hover-ms) var(--hover-ease); }

code, pre, .mono { font-family: var(--code-face); font-size: var(--code-size); }
:not(pre) > code {
  background: var(--surface);
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  padding: 0 0.3em;
  color: var(--ink);
}

.lede {
  font-size: calc(var(--body-size) * 1.15);
  color: var(--ink);
  border-left: 2px solid var(--amber);
  padding-left: calc(var(--space) * 2);
}
.caption {
  font-family: var(--code-face);
  font-size: var(--caption-size);
  line-height: var(--caption-lh);
  color: var(--ink-quiet);
}
.empty { color: var(--ink-quiet); }

/* An aside is reasoning lifted out of a document's comments. Quiet, and
   present: the argument is most of what these documents are. */
.note {
  border-left: 1px solid var(--rule);
  padding-left: calc(var(--space) * 2);
  margin: 0 0 calc(var(--space) * 2);
  color: var(--ink-quiet);
  font-size: calc(var(--body-size) * 0.94);
}
.note p { margin-bottom: calc(var(--space) * 1.5); }
.note-head {
  font-family: var(--code-face);
  font-size: var(--caption-size);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--cyan);
}

/* Red is for errors and for the weaknesses page's headings — the one place
   the accent rule bends, and the palette says so. */
main.weaknesses h1,
main.weaknesses h2 { color: var(--red); }
main.weaknesses .block > h2 { border-bottom-color: color-mix(in srgb, var(--red) 35%, transparent); }

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

figure { margin: 0 0 calc(var(--space) * 3); }

.code pre, .bytes pre, .doc-source pre {
  background: var(--surface);
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  padding: calc(var(--space) * 2);
  overflow-x: auto;
  margin: 0;
  line-height: var(--code-lh);
}
.bytes pre { color: var(--green); word-break: break-all; }
figcaption {
  font-family: var(--code-face);
  font-size: var(--caption-size);
  color: var(--ink-quiet);
  padding-top: calc(var(--space) * 0.75);
}
figcaption .count { color: var(--green); }

/* The colours themselves are emitted above from presentation.syntax-colouring.
   What is left here is the one thing that is not a colour. */
.t-com { font-style: italic; }

/* The one-accent rule: amber draws the eye, and only amber. Strings are amber
   in code because a string is the thing a reader looks for in a document. */

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

.scroll { overflow-x: auto; margin: 0 0 calc(var(--space) * 3); }
table {
  border-collapse: collapse;
  font-family: var(--code-face);
  font-size: var(--code-size);
  line-height: var(--code-lh);
  min-width: 100%;
}
th, td {
  text-align: left;
  vertical-align: top;
  padding: calc(var(--space) * 0.75) calc(var(--space) * 1.5);
  border-bottom: 1px solid var(--rule);
}
thead th {
  color: var(--ink-quiet);
  font-weight: 500;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  font-size: var(--caption-size);
  border-bottom: 1px solid var(--rule);
  white-space: nowrap;
}
tbody th { font-weight: 500; color: var(--ink); white-space: nowrap; }
tbody tr:hover { background: var(--surface); }

/* Verdict cells. Structure gives no way to know a cell is a verdict, so the
   colouring keys off the symbol the document actually wrote. */
.v-yes { color: var(--green); }
.v-no { color: var(--red); }
.v-partial, .v-tag, .v-poor { color: var(--amber); }
.v-n-a, .v-unchecked { color: var(--ink-quiet); }

/* ------------------------------------------------------- rendered document */

.block { margin: 0 0 calc(var(--space) * 6); }
.block > h2 { border-bottom: 1px solid var(--rule); padding-bottom: calc(var(--space)); }
.anchor { color: inherit; text-decoration: none; }
.anchor:hover { color: var(--amber); }
.anchor::after {
  content: " #";
  color: var(--rule);
  font-family: var(--code-face);
  font-size: 0.7em;
}

.pairs { margin: 0; }
.pair { margin: 0 0 calc(var(--space) * 2); }
.pairs dt { margin-bottom: calc(var(--space) * 0.5); }
.pairs dd { margin: 0 0 0 calc(var(--space) * 2); border-left: 1px solid var(--rule); padding-left: calc(var(--space) * 2); }
.key {
  font-family: var(--code-face);
  font-size: var(--code-size);
  color: var(--cyan);
}
.qual {
  font-family: var(--code-face);
  font-size: var(--caption-size);
  margin-left: calc(var(--space) * 0.75);
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  padding: 0 0.35em;
}

details { border-left: 1px solid var(--rule); padding-left: calc(var(--space) * 2); }
details > summary {
  cursor: pointer;
  list-style: none;
  margin-left: calc(var(--space) * -2);
  padding-left: calc(var(--space) * 2);
  position: relative;
  transition: color var(--disclosure-ms) var(--disclosure-ease);
}
summary::-webkit-details-marker { display: none; }
summary::before {
  content: "+";
  position: absolute;
  left: calc(var(--space) * 0.25);
  color: var(--ink-quiet);
  font-family: var(--code-face);
}
details[open] > summary::before { content: "\2212"; }
summary:hover .key { color: var(--amber); }
.hint {
  font-family: var(--code-face);
  font-size: var(--caption-size);
  color: var(--ink-quiet);
  margin-left: calc(var(--space) * 1);
}
.hint::before { content: "("; }
.hint::after { content: ")"; }

.prose-list { padding-left: calc(var(--space) * 2); }
.prose-list li { margin-bottom: calc(var(--space) * 1.5); }
.prose-list li > p:last-child { margin-bottom: 0; }
.value-list { list-style: none; padding: 0; margin: 0; }
.value-list > li { margin-bottom: calc(var(--space) * 1.5); }
.inline-block, .scalar { font-family: var(--code-face); font-size: var(--code-size); }
.scalar { margin-bottom: calc(var(--space) * 1.5); }

/* --------------------------------------------------------------- the cards */

.cards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
  gap: calc(var(--space) * 2);
  padding: 0;
  margin: 0 0 calc(var(--space) * 4);
  list-style: none;
}
.card {
  display: block;
  background: var(--surface);
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  padding: calc(var(--space) * 2);
  text-decoration: none;
  color: var(--ink);
  transition: border-color var(--hover-ms) var(--hover-ease);
}
.card:hover { border-color: var(--amber); color: var(--ink); }
.card .card-path {
  font-family: var(--code-face);
  font-size: var(--caption-size);
  color: var(--cyan);
  display: block;
}
.card .card-head {
  font-family: var(--heading-face);
  font-size: calc(var(--heading-size) * 0.6);
  display: block;
  margin: calc(var(--space) * 0.5) 0;
}
.card p { font-size: calc(var(--body-size) * 0.9); color: var(--ink-quiet); margin: 0; }

.claim { border-top: 1px solid var(--rule); padding-top: calc(var(--space) * 2); margin-bottom: calc(var(--space) * 3); }
.claim h3 { margin: 0 0 calc(var(--space)); font-family: var(--code-face); font-size: var(--code-size); color: var(--violet); }

/* ------------------------------------------------------------- the applets

   A stage that is a button until it is pressed, and a frame afterwards. The
   stage keeps its height across the swap so that pressing the button does not
   throw the rest of the page down the screen — an applet is between 66 kB and
   580 kB and arrives a moment later, and a layout that jumps in that moment
   is a layout that loses the reader's place. */
.applet-stage {
  margin: calc(var(--space) * 1.5) 0 0;
  min-height: 3rem;
  display: flex;
  align-items: center;
}
.applet-stage .button {
  display: inline-block;
  font-family: var(--code-face);
  font-size: var(--code-size);
  color: var(--ink);
  background: var(--surface);
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  padding: calc(var(--space) * 0.75) calc(var(--space) * 2);
  cursor: pointer;
  transition: border-color var(--hover-ms) var(--hover-ease),
              color var(--hover-ms) var(--hover-ease);
}
.applet-stage .button:hover { border-color: var(--amber); color: var(--amber); }
.applet-stage .button:focus-visible { outline: 1px solid var(--amber); outline-offset: 2px; }
.applet-frame {
  width: 100%;
  height: min(34rem, 80vh);
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  background: var(--surface);
}

/* --------------------------------------------------------------- the banner

   Layered. The canvas carries the board; the markup carries the document.
   Without scripting, WebGL or with reduced motion the markup is the banner —
   which is why the fragment is styled as the hero here and demoted only once
   the board is actually running. */

.banner {
  position: relative;
  overflow: hidden;
  background: var(--void);
  border-bottom: 1px solid var(--rule);
  min-height: 30rem;
  display: flex;
  align-items: center;
}
.banner canvas {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
  /* So the text underneath stays selectable where the board is drawn over it. */
  pointer-events: none;
}
.banner-stage {
  position: relative;
  z-index: 1;
  padding: calc(var(--space) * 4) calc(var(--space) * 3);
  width: 100%;
  max-width: 72rem;
  margin: 0 auto;
}
.banner-hook {
  font-family: var(--code-face);
  font-size: var(--caption-size);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--amber);
  margin: 0 0 calc(var(--space) * 1.5);
}
.banner-fragment {
  margin: 0;
  font-family: var(--code-face);
  font-size: var(--banner-size);
  line-height: var(--banner-lh);
  color: var(--ink);
  overflow-x: auto;
  min-height: 11em;
  /* 7:1 against the banner field at its brightest, which is what constrains
     the palette. The plate keeps that true while the board turns behind it. */
  background: color-mix(in srgb, var(--void) 78%, transparent);
  border-left: 1px solid var(--rule);
  padding: calc(var(--space)) calc(var(--space) * 2);
}
.banner-bytes {
  margin: calc(var(--space) * 1.5) 0 0;
  font-family: var(--code-face);
  font-size: var(--banner-size);
  line-height: var(--banner-lh);
  color: var(--green);
  word-break: break-all;
  min-height: 3em;
  padding: 0 calc(var(--space) * 2);
  max-width: 100%;
}
.banner-source {
  margin: calc(var(--space) * 1.5) 0 0;
  padding: 0 calc(var(--space) * 2);
  font-family: var(--code-face);
  font-size: var(--caption-size);
  color: var(--ink-quiet);
  max-width: 68ch;
}
/* One line where it fits: the credit sits under the board and every line it
   wraps to is a line the board has to give up. */
.banner.has-board .banner-source { max-width: none; text-align: center; }
.banner-source a { color: var(--ink-quiet); }
.banner-source a:hover { color: var(--cyan); }

/* With the board running, the markup becomes the readout under it: the
   document is on the board, and this is the copyable form of the same thing,
   which is the guarantee the whole banner rests on. */
/* With the readout hidden the banner holds the board, a protected floor below
   its canvas, and the one credit line. At ordinary desktop widths the canvas
   remains the same two-to-one size the artwork was composed for; the clamp
   stops very wide screens turning the banner into an entire page. */
.banner.has-board {
  min-height: clamp(
    var(--banner-min-height),
    calc(var(--banner-canvas-height) + var(--banner-page-clearance)),
    var(--banner-max-height)
  );
  align-items: flex-end;
}
/* A canvas is a replaced element: in Chrome height:auto keeps its intrinsic
   aspect ratio and ignores the intended bottom constraint. Give it the exact
   height the banner owns, leaving the protected floor as real layout space. */
.banner.has-board canvas {
  bottom: auto;
  height: calc(100% - var(--banner-page-clearance));
}
.banner.has-board .banner-stage {
  position: static;
  padding: 0 calc(var(--space) * 3) calc(var(--space) * 2);
  pointer-events: none;
}

/* The board is showing this text already, larger, with its title and with
   commentary attached. Repeating it underneath was duplication that made the
   banner look unfinished. It stays in the accessibility tree, and the source
   line below links to a page that displays the whole document. */
.banner.has-board .banner-readout {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}
/* The hook labels the board from above rather than sitting across it. */
/* The hook and what it shows are drawn on the board itself. The markup keeps
   them for a reader with no WebGL and for a screen reader, where they are the
   banner rather than a duplicate of it. */
.banner.has-board .banner-hook { display: none; }
.banner.has-board .banner-source { pointer-events: auto; margin: 0; padding: 0; }

/* The still path — reduced motion, no WebGL2, a shader that would not
   compile. It cross-fades on the same cycle rather than sitting on one topic. */
.banner.is-still .banner-fragment,
.banner.is-still .banner-bytes,
.banner.is-still .banner-hook { transition: opacity 200ms linear; }
.banner.is-fading .banner-fragment,
.banner.is-fading .banner-bytes,
.banner.is-fading .banner-hook { opacity: 0; }

/* Narrow viewport: the board square to the reader with no swivel, and four
   lines of fragment. The line count is a value from the degradation table. */
@media (max-width: 640px) {
  .banner { min-height: 30rem; }
  .banner.has-board { min-height: var(--banner-mobile-min-height); }
  .banner.has-board canvas {
    height: calc(100% - var(--banner-mobile-page-clearance));
  }
  .banner.has-board .banner-stage { padding-top: calc(var(--space) * 26); }
  /* A seven-item nav stacked one per line is most of a phone screen. It
     scrolls sideways instead, inside its own box, so the page does not. */
  .top nav ul {
    flex-wrap: nowrap;
    overflow-x: auto;
    gap: calc(var(--space) * 1.5);
  }
  .top nav a { white-space: nowrap; }
  .banner.has-board .banner-readout { grid-template-columns: minmax(0, 1fr); }
  .banner-fragment {
    min-height: calc(var(--banner-mobile-lines) * var(--banner-size) * var(--banner-lh));
    max-height: calc(var(--banner-mobile-lines) * var(--banner-size) * var(--banner-lh));
    overflow-y: hidden;
  }
  main { padding: calc(var(--space) * 3) calc(var(--space) * 2); }
}

@media (prefers-reduced-motion: reduce) {
  .banner canvas { display: none; }
  * { animation: none !important; transition: none !important; }
}

/* --------------------------------------------------------- the playground */

.play {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(22rem, 1fr));
  gap: calc(var(--space) * 2);
}
.play textarea, .play pre {
  width: 100%;
  min-height: 24rem;
  background: var(--surface);
  color: var(--ink);
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  padding: calc(var(--space) * 2);
  font-family: var(--code-face);
  font-size: var(--code-size);
  line-height: var(--code-lh);
  resize: vertical;
  overflow: auto;
}
.play textarea:focus { outline: 1px solid var(--amber); }

/* The coloured editor: a <pre> of coloured text with a transparent <textarea>
   exactly on top of it. Every metric that affects where a glyph lands has to
   match in both, or the caret drifts from the ink — which is the one way this
   technique goes wrong, and it goes wrong slowly, a character at a time. */
.editor { position: relative; }
.editor > .editor-ink,
.editor > textarea {
  margin: 0;
  white-space: pre;
  word-break: normal;
  overflow-wrap: normal;
  tab-size: 4;
  letter-spacing: normal;
}
.editor > .editor-ink {
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  overflow: hidden;
}
.editor > textarea {
  position: relative;
  z-index: 1;
  background: transparent;
}
/* Transparent glyphs, visible caret and selection: the reader edits real text
   and reads the coloured copy underneath it. */
.editor > textarea.is-overlaid {
  color: transparent;
  caret-color: var(--amber);
}
.editor > textarea.is-overlaid::selection {
  background: color-mix(in srgb, var(--cyan) 30%, transparent);
  color: transparent;
}
.play h3 { margin: 0 0 var(--space); }
.play .out-bytes { color: var(--green); word-break: break-all; }
.play-status {
  font-family: var(--code-face);
  font-size: var(--caption-size);
  padding: var(--space) 0;
  min-height: 2.5em;
}
.play-status.bad { color: var(--red); }
.play-status.good { color: var(--green); }
.noscript {
  border: 1px solid var(--amber);
  border-radius: var(--radius);
  padding: calc(var(--space) * 2);
  margin-bottom: calc(var(--space) * 3);
}

/* ------------------------------------------------- the document viewer

   The card is positioned by script; everything it looks like is here, from
   the constants in the document-viewer block. */

.doc-source pre { max-height: none; }
.doc-source code { display: block; }

/* An explainable run. Underlined faintly on hover only, so a page of source
   is not a page of dotted lines. */
.x { cursor: help; border-radius: 2px; outline-offset: 2px; }
.x:hover, .x:focus-visible {
  background: color-mix(in srgb, var(--x-accent) 16%, transparent);
  outline: none;
}
.x:focus-visible { box-shadow: 0 0 0 1px var(--x-accent); }
.x-punct { color: var(--ink-quiet); }

.x-card {
  position: absolute;
  z-index: 40;
  max-width: var(--x-max-width);
  padding: var(--x-padding);
  border: 1px solid var(--x-border);
  border-radius: var(--x-radius);
  box-shadow: var(--x-shadow);
  background: color-mix(in srgb, var(--x-bg) calc(var(--x-bg-opacity) * 100%), transparent);
  backdrop-filter: blur(var(--x-blur));
  -webkit-backdrop-filter: blur(var(--x-blur));
  opacity: 0;
  transform: translateY(-2px);
  transition: opacity var(--x-fade) ease, transform var(--x-fade) ease;
  pointer-events: none;
}
.x-card.is-open { opacity: 1; transform: none; }
.x-card p { margin: 0 0 calc(var(--space) * 0.75); max-width: none; }
.x-card p:last-child { margin-bottom: 0; }
.x-token {
  font-family: var(--code-face);
  font-size: var(--code-size);
  display: flex;
  align-items: baseline;
  gap: calc(var(--space));
}
.x-glyph {
  color: var(--x-accent);
  border: 1px solid var(--x-border);
  border-radius: var(--radius);
  padding: 0 .4em;
}
.x-gloss { font-size: calc(var(--body-size) * 0.92); line-height: 1.5; color: var(--ink); }
.x-formal {
  font-family: var(--code-face);
  font-size: var(--caption-size);
  color: var(--ink-quiet);
}
/* No colour here: the example wears the construct's own class, so a symbol's
   example is not set in the string colour. */
.x-formal code { background: none; border: none; padding: 0; }
.x-gloss code { background: none; border: 1px solid var(--x-border); padding: 0 .25em; color: var(--ink); }
.x-label { color: var(--x-accent); }
.x-source {
  font-family: var(--code-face);
  font-size: calc(var(--caption-size) * 0.92);
  color: color-mix(in srgb, var(--ink-quiet) 75%, transparent);
  border-top: 1px solid var(--x-border);
  padding-top: calc(var(--space) * 0.6);
}

/* Legibility before effect: without backdrop-filter the card is solid. */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .x-card { background: var(--x-bg); }
}

@media (max-width: 640px) {
  .x-card { max-width: calc(100vw - 2rem); }
}

/* A "raw" chip beside a document link: the page the site can display, and the
   bytes the repository holds, distinguishable at a glance. */
a.raw {
  font-family: var(--code-face);
  font-size: calc(var(--caption-size) * 0.92);
  color: var(--ink-quiet);
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  padding: 0 .35em;
  text-decoration: none;
  margin-left: .4em;
}
a.raw:hover { color: var(--cyan); border-color: var(--cyan); }

main.viewer { max-width: 84rem; }

/* --------------------------------------------------- loading a document

   A real file input with its label styled as a button. The input itself is
   hidden from sight and not from the accessibility tree, so the control keeps
   its keyboard behaviour and its announcement. */

.play-load {
  display: flex;
  align-items: baseline;
  gap: calc(var(--space) * 2);
  flex-wrap: wrap;
  margin: calc(var(--space) * 2) 0 0;
}
.play-load input[type="file"] {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
}
.play-load .button {
  display: inline-block;
  font-family: var(--code-face);
  font-size: var(--code-size);
  color: var(--ink);
  background: var(--surface);
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  padding: calc(var(--space) * 0.75) calc(var(--space) * 2);
  cursor: pointer;
  transition: border-color var(--hover-ms) var(--hover-ease),
              color var(--hover-ms) var(--hover-ease);
}
.play-load .button:hover { border-color: var(--amber); color: var(--amber); }
.play-load input[type="file"]:focus-visible + .button,
.play-load .button:focus-visible { outline: 1px solid var(--amber); outline-offset: 2px; }
.play-load .caption { margin: 0; flex: 1; min-width: 20ch; }

/* The load row in the artefact list, and the empty state of the local viewer. */
tr.uses-load th { color: var(--amber); }
tr.uses-load input[type="file"] {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
}
tr.uses-load .button {
  display: inline-block;
  font-family: var(--code-face);
  font-size: var(--caption-size);
  color: var(--ink);
  background: var(--surface);
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  padding: calc(var(--space) * 0.4) calc(var(--space) * 1.5);
  cursor: pointer;
  white-space: nowrap;
}
tr.uses-load .button:hover { border-color: var(--amber); color: var(--amber); }
tr.uses-load input[type="file"]:focus-visible + .button {
  outline: 1px solid var(--amber);
  outline-offset: 2px;
}
