/* Burxt — white, quiet, and out of the way.
 *
 * The brief was "clean simple minimalist yet sophisticated apple looking, white only, so the burxt
 * logo stands out". So: one background colour, one text colour, one accent that appears only where
 * something is actually interactive, and hairlines instead of shadows. Nothing here glows.
 *
 * The logo is the only strong colour on any page. That is the whole design decision — everything
 * else is restraint in service of it.
 *
 * No dark mode, on purpose and per the brief. A white-only site that is genuinely white beats one
 * that is two mediocre themes.
 *
 * ---------------------------------------------------------------------------------------------
 * There is no grey text here, and that is enforced rather than remembered.
 *
 * This file used to carry `--ink-soft: #6e6e73` and hand it the navigation, the hero subtitle,
 * every table header, every caption and the whole footer. At 5.1:1 that passes a contrast checker
 * and still reads as washed out at 13px, which is where most of it was used — so the page looked
 * faint everywhere it was explaining itself.
 *
 * The variable is DELETED rather than darkened, so nothing can quietly keep using it. Text that a
 * reader reads is `--ink`. `--ink-2` exists for chrome only — an idle nav item, a table header, a
 * line number — and is dark enough (10.4:1) to be a weight, not a fade.
 * `the_site_text_is_readable` in tests/runner.rs computes the ratios out of this file and fails
 * below 4.5:1, because faint text is the kind of regression only a person looking at the page
 * catches. */

:root {
  /* ---- ink ---------------------------------------------------------------------------------- */
  --ink: #1d1d1f;              /* Apple's near-black: softer than #000, easier over long reading */
  --ink-2: #3a3a3e;            /* CHROME ONLY — 10.4:1. Never a sentence. */
  --accent: #0071e3;           /* 4.7:1 on white: passes as link text, which is what it is */
  --accent-deep: #0058b8;      /* hover, and white-on-accent that needs more room */

  /* ---- surfaces ----------------------------------------------------------------------------- */
  --paper: #ffffff;
  --wash: #f5f5f7;             /* Apple's own light surface: panels, code, quiet rows */
  --wash-2: #fbfbfd;           /* one step quieter, for a panel inside a panel */
  --hair: #e3e3e8;
  --hair-2: #d2d2d7;           /* a hairline that has to be seen: a panel edge, a divider */

  /* ---- refusal ------------------------------------------------------------------------------ */
  /* The language's whole pitch is what it declines to compile, so a refusal gets a real colour
   * rather than an apologetic one. 5.9:1 — quotable at 13px. */
  --refuse: #c8102e;
  --refuse-wash: #fff5f6;

  /* ---- type --------------------------------------------------------------------------------- */
  --mono: ui-monospace, "SF Mono", SFMono-Regular, "JetBrains Mono", Menlo, Consolas, monospace;
  --sans: -apple-system, BlinkMacSystemFont, "Segoe UI", "Helvetica Neue", Arial, sans-serif;

  /* ---- metrics ------------------------------------------------------------------------------ */
  --measure: 44rem;            /* ~72 characters: the line length prose is actually readable at */
  --bar-h: 52px;
  --side-w: 17rem;
  --toc-w: 13rem;
  --gap: 1.5rem;

  /* ---- motion ------------------------------------------------------------------------------- */
  /* macOS's own easing. Everything that moves uses this one curve, so nothing feels borrowed. */
  --ease: cubic-bezier(.4, 0, .22, 1);
  --quick: .18s var(--ease);
  --calm: .32s var(--ease);

  --radius: 12px;
  --radius-sm: 6px;
  --pill: 980px;
}

* { box-sizing: border-box; }

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  /* A heading linked from the sidebar must not land under the sticky bar. */
  scroll-padding-top: calc(var(--bar-h) + 1rem);
  /* On `html` as well as on `body`. The page must never scroll sideways, and it was `html` that
   * did: `overflow-x: hidden` on body alone does not stop the document scrolling when something
   * inside it is wider than the viewport. Every wide thing on this site has its own scroll
   * container; this is the guard for the one that gets missed. */
  overflow-x: hidden;
}

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font: 400 clamp(16px, 0.95rem + 0.15vw, 17px)/1.6 var(--sans);
  letter-spacing: -0.011em;   /* the small negative tracking that makes system fonts look set */
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  overflow-x: hidden;         /* the belt to the braces on every wide-content scroll container */
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: .01ms !important;
    transition-duration: .01ms !important;
  }
}

/* A visible focus ring, once, for everything — and only for keyboards, so a mouse click on a pill
 * does not leave a halo behind it. */
:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: var(--radius-sm);
}
:focus:not(:focus-visible) { outline: none; }

/* For a control whose label is a picture, or a skip link that only exists for a keyboard. */
.vh {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}
.skip {
  position: absolute;
  left: 50%; top: 0;
  transform: translate(-50%, -120%);
  z-index: 60;
  padding: .6rem 1.1rem;
  background: var(--paper);
  border: 1px solid var(--hair-2);
  border-radius: 0 0 var(--radius) var(--radius);
  transition: transform var(--quick);
}
.skip:focus { transform: translate(-50%, 0); }

/* ---- the frame --------------------------------------------------------------------------------
 *
 * Translucent and blurred, like every macOS toolbar. The fallback matters: without
 * backdrop-filter the bar must be opaque, or text scrolls through it. */

.bar {
  position: sticky;
  top: 0;
  z-index: 30;
  display: flex;
  align-items: center;
  gap: 1.75rem;
  padding: 0 var(--gap);
  height: var(--bar-h);
  background: var(--paper);
  border-bottom: 1px solid var(--hair);
}
@supports (backdrop-filter: blur(20px)) or (-webkit-backdrop-filter: blur(20px)) {
  .bar {
    background: rgba(255, 255, 255, 0.72);
    -webkit-backdrop-filter: saturate(180%) blur(20px);
    backdrop-filter: saturate(180%) blur(20px);
  }
}

.bar a {
  color: var(--ink-2);
  text-decoration: none;
  font-size: 14px;
  white-space: nowrap;
  transition: color var(--quick);
}
.bar a:hover { color: var(--ink); text-decoration: none; }

/* The version picker, beside the mark.
   A real <select> rather than a styled menu, and that is the accessible choice as much as the
   cheap one: it is keyboard-navigable, it works before any JavaScript runs, and on a phone it
   opens the platform's own picker instead of a list somebody has to scroll inside a sticky bar.
   The `gap` on `.bar` already spaces it; only the chrome needs removing so it reads as part of
   the bar rather than as a form. */
.bar .ver {
  appearance: none;
  -webkit-appearance: none;
  font: inherit;
  font-size: 13px;
  color: var(--ink-2);
  background: transparent;
  border: 1px solid var(--hair);
  border-radius: 999px;
  padding: 2px 1.5rem 2px 0.6rem;
  margin-left: -1.1rem;
  cursor: pointer;
  transition: color var(--quick), border-color var(--quick);
  /* The chevron, drawn rather than fetched — the site ships no icon font and a background-image
     would be a request for eleven pixels. */
  background-image: linear-gradient(45deg, transparent 50%, currentColor 50%),
                    linear-gradient(135deg, currentColor 50%, transparent 50%);
  background-position: right 0.72rem center, right 0.45rem center;
  background-size: 5px 5px, 5px 5px;
  background-repeat: no-repeat;
}
.bar .ver:hover { color: var(--ink); border-color: var(--ink-2); }
.bar .ver:focus-visible { outline: 2px solid var(--ink); outline-offset: 2px; }
/* The native dropdown list is the platform's, so it needs real colours rather than inherited ones. */
.bar .ver option { color: var(--ink); background: var(--paper); }

.bar a.here { color: var(--ink); font-weight: 500; }

/* The logo is a link, so it is a tap target: 44px tall whatever the mark's own height is. */
.bar .mark {
  display: flex;
  align-items: center;
  margin-right: auto;
  min-height: 44px;
  padding: 0 .25rem 0 0;
}
.bar .mark img { height: 22px; width: auto; display: block; }

.bar .links { display: flex; align-items: center; gap: 1.75rem; }

/* Search, at EVERY width.
 *
 * This was a phone-only icon, on the reasoning that a desktop reader has ⌘K. They do — and they have
 * no way to find that out, because a keyboard shortcut with nothing on screen to advertise it is a
 * feature only its author knows about. A reference with a search index and no visible way in is a
 * reference you Cmd-F instead, which is the thing it was built to replace.
 *
 * So on a wide screen it is a labelled control that says what it does and what the shortcut is, and
 * on a narrow one it collapses to the icon. */
.bar .find {
  display: inline-flex;
  align-items: center;
  gap: .45rem;
  height: 32px;
  padding: 0 .5rem 0 .6rem;
  border: 1px solid var(--hair-2);
  border-radius: var(--pill);
  background: var(--paper);
  color: var(--ink-2);
  font: inherit;
  font-size: 13px;
  cursor: pointer;
  transition: color var(--quick), background var(--quick), border-color var(--quick);
}
.bar .find:hover { color: var(--ink); background: var(--wash); border-color: var(--ink-2); }
.bar .find .what { white-space: nowrap; }
.bar .find kbd { border: 0; border-bottom: 0; padding: 0 .1rem; font-size: 11.5px; }

.bar .burger {
  display: none;
  width: 44px; height: 44px;
  align-items: center;
  justify-content: center;
  padding: 0;
  border: 0;
  background: none;
  color: var(--ink);
  cursor: pointer;
  border-radius: var(--radius-sm);
}
.bar .burger:hover { background: var(--wash); }
.bar .burger svg, .bar .find svg { width: 17px; height: 17px; display: block; flex: 0 0 auto; }

/* The site's own links, carried inside the drawer.
 *
 * Hidden on a wide screen because the bar already shows them. Below 900px they are the ONLY copy —
 * see the note on the breakpoints at the bottom of this file for what shipped without them. */
.side .site { display: none; }

main { padding: 0 var(--gap) 6rem; }
.wrap { max-width: var(--measure); margin: 0 auto; }
.wide { max-width: 60rem; margin: 0 auto; }

/* ---- the hero: mostly empty, which is the point --------------------------------------------- */

.hero { text-align: center; padding: clamp(3.5rem, 9vw, 7rem) 0 4rem; }
.hero img.lockup { width: min(340px, 72vw); height: auto; }

/* The mascot — the mark, alive.
 *
 * It replaces the static lockup in the hero rather than sitting beside it, because the animation
 * CONTAINS the `b`: two of them on one screen is not a flourish, it is a mistake. Nothing is lost by
 * dropping the wordmark here, because the sticky bar carries the lockup on every page including this
 * one, two centimetres above.
 *
 * And it plays ONCE. The choreography starts and ends with the ember hidden inside the bowl of the
 * b, so a single play leaves a clean, still logo — a moment of warmth on arrival and then quiet,
 * which is the only way a mascot belongs on a page whose stylesheet says "nothing here glows". The
 * loop count is set in the GIF itself; `the_mascot_plays_once` holds it there.
 *
 * A reader who has asked for less motion gets a still frame instead, chosen so the ember is out and
 * waving — the poster has to show the mascot, or that reader never learns there is one. */
/* The animated lockup: the mark, alive, beside the real wordmark.
 *
 * Two elements rather than one composited GIF, for two reasons. Compositing the wordmark into all 46
 * frames took the file from 110 KB to 440 KB, because a GIF encoder re-encodes the static pixels on
 * every frame. And the wordmark stays a crisp PNG at any pixel density this way, where a composited
 * copy would be rasterised once at one size.
 *
 * The cost is that the alignment is arithmetic rather than a picture, so here is where every number
 * comes from. The GIF is 174x222 and its `b` ink occupies x 6..113, y 40..183 — measured across all
 * 46 frames, because the ember hops above the letter and the frame has to hold that. The wordmark is
 * cropped to its own ink, 823x217, from the official 1311px lockup, so the typeface is the brand's
 * and not a system font pretending.
 *
 * The GIF is the WHITE-background original, not the transparent one. GIF alpha is one bit, so a
 * transparent export has to binarise every anti-aliased edge and the ember came out visibly
 * stair-stepped. Against a white background the anti-aliasing survives, and this site is white.
 *
 * Everything is expressed against `--mark`, the GIF's rendered height, so one number scales the pair:
 *   the wordmark's height   135/222 = 0.608   (135px beside a 144px-tall b, the lockup's own ratio)
 *   the lift off the bottom  38/222 + 5/222 = 0.194
 *                            38px of the GIF sits below the b's baseline, and the wordmark's baseline
 *                            is 5px above the b's at this scale — both from the lockup's geometry
 *   the gap                  48/222 = 0.216
 *                            the brand gap is 108px in these units and the GIF already carries 60px
 *                            of transparent space to the right of the b, so only 48 is added */
.hero .lockup-live {
  /* The lockup's whole width is about 3.3x this number (the mark, the gap, and the wordmark at
   * 3.79:1), so 108px puts it at ~357px — where the static lockup used to sit. The first version
   * clamped the MINIMUM at 104px, which meant it stopped scaling below ~430px of viewport and the
   * wordmark ran to 90% of a phone's width. The minimum is what has to be small. */
  --mark: clamp(76px, 22vw, 108px);
  display: flex;
  align-items: flex-end;
  justify-content: center;
  gap: calc(var(--mark) * 0.216);
}
.hero .lockup-live img.mark { height: var(--mark); width: auto; max-width: none; }
.hero .lockup-live img.word {
  height: calc(var(--mark) * 0.608);
  width: auto;
  margin-bottom: calc(var(--mark) * 0.194);
}

.hero .mascot { display: block; }
.hero .mascot img {
  /* On the 404 page the mascot stands alone, with no wordmark to size against. The delivered frame
   * was 480x360 with the artwork filling 34% of its width, so a 300px element showed a 100px mark —
   * cropped to its own bounding box, the number here is the size of the thing you see. */
  width: min(190px, 44vw);
  height: auto;
  margin: 0 auto;
  display: block;
  /* Tapping or clicking replays it. The cursor says so; site.js does it. */
  cursor: pointer;
}
@media (prefers-reduced-motion: reduce) {
  .hero .mascot img { cursor: default; }
}
.hero p.line {
  margin: 2rem auto 0;
  max-width: 34rem;
  font-size: clamp(19px, 1.1rem + 0.4vw, 21px);
  line-height: 1.45;
  color: var(--ink);
}
.hero .cta {
  margin-top: 2.5rem;
  display: flex; gap: .75rem;
  justify-content: center;
  flex-wrap: wrap;
}

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 44px;                /* an iOS tap target, everywhere, not only on a phone */
  padding: .65rem 1.4rem;
  border-radius: var(--pill);
  background: var(--accent);
  color: #fff;
  text-decoration: none;
  font-size: 15px;
  line-height: 1.2;
  transition: background var(--quick), transform var(--quick);
}
.btn:hover { background: var(--accent-deep); text-decoration: none; }
.btn:active { transform: scale(.98); }
.btn.ghost { background: transparent; color: var(--accent); box-shadow: inset 0 0 0 1px var(--hair-2); }
.btn.ghost:hover { background: var(--wash); color: var(--accent-deep); }

/* ---- type ------------------------------------------------------------------------------------ */

h1, h2, h3, h4 { letter-spacing: -0.021em; line-height: 1.15; font-weight: 600; color: var(--ink); }
h1 { font-size: clamp(30px, 1.6rem + 1.8vw, 40px); margin: 3rem 0 1rem; }
h2 { font-size: clamp(23px, 1.3rem + 0.9vw, 27px); margin: 3.5rem 0 1rem; }
h3 { font-size: clamp(18px, 1.05rem + 0.3vw, 20px); margin: 2.5rem 0 .75rem; }
h1:first-child, h2:first-child { margin-top: 2rem; }

/* An anchor a reader can actually take, without a link icon cluttering the heading until wanted. */
h2 > a.anchor, h3 > a.anchor {
  opacity: 0;
  margin-left: .4rem;
  font-weight: 400;
  color: var(--ink-2);
  transition: opacity var(--quick);
}
h2:hover > a.anchor, h3:hover > a.anchor, a.anchor:focus-visible { opacity: 1; }

/* On a touch screen there is no hover, so this would be an invisible 29px target sitting beside
 * every heading — a thing you can hit and cannot see. Better absent than phantom. */
@media (hover: none) {
  h2 > a.anchor, h3 > a.anchor { display: none; }
}

p { margin: 1.15rem 0; }

/* A long unbreakable token — a URL, a generated identifier, a `Decimal<2, RoundHalfEven>` in a
 * table cell — is the one thing prose cannot reflow around, and on a 390px screen it pushes the
 * whole column wide. Breaking it is the lesser evil, and the only visible one on a phone. */
p, li, td, th, dd, figcaption, blockquote { overflow-wrap: break-word; }

a { color: var(--accent); text-decoration: none; }
a:hover { text-decoration: underline; text-underline-offset: 2px; }
strong { font-weight: 600; }
hr { border: 0; border-top: 1px solid var(--hair); margin: 3rem 0; }

blockquote {
  margin: 1.5rem 0;
  padding: .25rem 0 .25rem 1.25rem;
  border-left: 2px solid var(--hair-2);
  color: var(--ink);
}

ul, ol { padding-left: 1.35rem; }
li { margin: .45rem 0; }

/* An analogy picture. Illustrative, not schematic — and it scales on a phone. */
main svg { max-width: 100%; height: auto; display: block; margin: 1.75rem auto; }
figure { margin: 1.75rem 0; }
figcaption { font-size: 14px; color: var(--ink); text-align: center; margin-top: .6rem; }

/* ---- code -------------------------------------------------------------------------------------
 *
 * Two shapes. `code` is inline. `pre` is the plain, pre-enhancement block — what a reader sees for
 * the instant before burxt-editor.js turns a Burxt one into a real panel, and what they keep if
 * scripting is off. It has to be presentable on its own. */

code {
  font-family: var(--mono);
  font-size: .89em;
  background: var(--wash);
  padding: .12em .38em;
  border-radius: 5px;
  overflow-wrap: break-word;
}

pre {
  background: var(--wash);
  border: 1px solid var(--hair);
  border-radius: var(--radius);
  padding: 1.1rem 1.25rem;
  overflow-x: auto;        /* wide code scrolls in its own box; the page never scrolls sideways */
  font-size: clamp(13px, 0.8rem + 0.15vw, 14.5px);
  line-height: 1.55;
  -webkit-overflow-scrolling: touch;
}
pre code { background: none; padding: 0; font-size: inherit; }

/* The one place the hero shows code: bigger, centred, nothing around it. */
.hero pre {
  text-align: left;
  max-width: 30rem;
  margin: 2.5rem auto 0;
  font-size: clamp(13px, 0.78rem + 0.35vw, 16px);
}

/* ---- the code panel ---------------------------------------------------------------------------
 *
 * Built by docs/assets/burxt-editor.js over the plain <pre> above. The shape is Xcode's editor: a
 * title bar naming the file, a line-number gutter, and — where the guide shows a refusal — the
 * compiler's own caret rendering turned into a squiggle under exactly the columns it marked.
 *
 * No traffic-light dots. Xcode's editor pane does not have them either, and three decorative
 * circles on every code block in a twelve-page guide is a joke that stops being funny on page two. */

.code {
  margin: 1.75rem 0;
  background: var(--wash);
  border: 1px solid var(--hair-2);
  border-radius: var(--radius);
  overflow: hidden;                /* so the bar's corners follow the panel's */
}
.hero .code { max-width: 34rem; margin: 2.5rem auto 0; text-align: left; }

.code-bar {
  display: flex;
  align-items: center;
  gap: .6rem;
  padding: .45rem .5rem .45rem .9rem;
  background: rgba(255, 255, 255, 0.66);
  border-bottom: 1px solid var(--hair);
  font-size: 12px;
}
@supports (backdrop-filter: blur(12px)) {
  .code-bar { -webkit-backdrop-filter: blur(12px); backdrop-filter: blur(12px); }
}
.code-file {
  font-family: var(--mono);
  font-size: 11.5px;
  color: var(--ink-2);
  letter-spacing: 0;
}
.code-tag {
  margin-left: .1rem;
  padding: .1rem .45rem;
  border-radius: var(--pill);
  font-size: 10.5px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .05em;
}
.code-tag.refused { background: var(--refuse-wash); color: var(--refuse); box-shadow: inset 0 0 0 1px #f3ccd2; }
.code-tag.runtime { background: #fff7ed; color: #8c4b00; box-shadow: inset 0 0 0 1px #f0dcc0; }

.code-copy {
  margin-left: auto;
  min-height: 28px;
  padding: .25rem .7rem;
  font: 500 12px/1 var(--sans);
  color: var(--ink-2);
  background: none;
  border: 1px solid var(--hair-2);
  border-radius: var(--pill);
  cursor: pointer;
  transition: color var(--quick), background var(--quick);
}
.code-copy:hover { color: var(--ink); background: var(--paper); }
.code-copy[data-done="1"] { color: var(--accent); border-color: var(--accent); }

/* The scroller. Wide code moves inside here and nowhere else. */
.code-scroll {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  overscroll-behavior-x: contain;
}
.code-lines {
  min-width: max-content;          /* so the gutter has something to stick against */
  font-family: var(--mono);
  font-size: clamp(12.5px, 0.78rem + 0.15vw, 14px);
  line-height: 1.62;
  padding: .7rem 0;
  tab-size: 4;
}

.cl { display: flex; align-items: flex-start; }
.cl:hover { background: rgba(0, 0, 0, 0.022); }

/* The gutter rides the horizontal scroll so the numbers are always readable, and never gets
 * picked up by a selection — copying code must give you code. */
.lnum {
  position: sticky;
  left: 0;
  z-index: 1;
  flex: 0 0 auto;
  min-width: 3.1rem;
  padding: 0 .85rem 0 0;
  text-align: right;
  color: var(--ink-2);
  font-variant-numeric: tabular-nums;
  background: var(--wash);
  user-select: none;
  -webkit-user-select: none;
}
.cl.bad .lnum { color: var(--refuse); font-weight: 600; }

.ltext { flex: 1 1 auto; padding-right: 1.25rem; white-space: pre; background: none; }

/* The squiggle. A native wavy underline rather than a background image, so it survives any zoom
 * and lands on exactly the characters the compiler's carets covered. */
.sq {
  text-decoration: underline wavy var(--refuse);
  text-decoration-skip-ink: none;
  text-underline-offset: 3px;
  text-decoration-thickness: 1px;
}

/* The message, on its own row under the line it belongs to — where an editor puts it. */
.diag {
  display: flex;
  gap: .5rem;
  margin: .15rem 0 .3rem;
  padding: .45rem .9rem .5rem calc(3.1rem + .1rem);
  background: var(--refuse-wash);
  border-top: 1px solid #f3ccd2;
  border-bottom: 1px solid #f3ccd2;
  font-family: var(--sans);
  font-size: 13px;
  line-height: 1.5;
  color: var(--ink);
  white-space: normal;
  max-width: min(46rem, 100%);
}
.diag-kind { flex: 0 0 auto; font-weight: 600; color: var(--refuse); }

/* A message is the last thing in the panel often enough that its own margin plus the block's
 * padding read as an empty band under it. */
.code-lines > .diag:last-child { margin-bottom: 0; }

/* The compiler's raw output, kept and quotable, collapsed so it does not say the same thing twice. */
.code-out {
  border-top: 1px solid var(--hair);
  background: var(--wash-2);
  font-size: 13px;
}
.code-out > summary {
  padding: .55rem .9rem;
  color: var(--ink-2);
  cursor: pointer;
  list-style: none;
  user-select: none;
}
.code-out > summary::-webkit-details-marker { display: none; }
.code-out > summary::before { content: "▸ "; }
.code-out[open] > summary::before { content: "▾ "; }
.code-out > summary:hover { color: var(--ink); }
.code-out pre {
  margin: 0;
  border: 0;
  border-top: 1px solid var(--hair);
  border-radius: 0;
  background: var(--paper);
  color: var(--ink);
  font-size: 12.5px;
}

/* ---- syntax ------------------------------------------------------------------------------------
 *
 * Xcode's light default, so a snippet on this page and the same snippet in the editor extension are
 * the same colours. The class names follow the TextMate scopes in
 * editors/vscode/syntaxes/burxt.tmLanguage.json rather than inventing a second vocabulary.
 *
 * Every one of these clears 4.5:1 on --wash. A syntax theme is text, and a pretty pale keyword is
 * still unreadable text. */

.t-kw       { color: #9b2393; }                          /* keyword, storage modifier    6.7:1 */
.t-contract { color: #8c4b00; font-weight: 500; }        /* requires / ensures / decreases 6.6:1 */
.t-type     { color: #0b4f79; }                          /* Int, Decimal, a class name    8.0:1 */
.t-str      { color: #c41a16; }                          /* string literal                5.7:1 */
.t-num      { color: #1c00cf; }                          /* Int and Decimal literals      9.9:1 */
.t-money    { color: #0f6f3c; font-weight: 500; }        /* $19.99 and 8.25% — its own    6.1:1 */
.t-fn       { color: #266d70; }                          /* a declared or called name     5.0:1 */
.t-com      { color: #5d6c79; font-style: italic; }      /* comment                       5.3:1 */
.t-lang     { color: #9b2393; font-style: italic; }      /* self, result, it                     */
.t-punc     { color: var(--ink-2); }
.t-bad {                                                  /* `fn`, `struct` — the old spellings */
  color: var(--refuse);
  text-decoration: underline wavy var(--refuse);
  text-decoration-skip-ink: none;
  text-underline-offset: 3px;
}

/* ---- the roaming ember -------------------------------------------------------------------------
 *
 * A second mascot, and a different job from the one in the hero. This one enters from off the right
 * edge, hops across making stops, and leaves the same way — the loop is empty-to-empty, so the
 * corner it lives in is empty for part of every cycle and completely empty once it is done.
 *
 * Four decisions, and the reasons matter more than the values.
 *
 * **It runs three times, not forever.** The delivered file loops endlessly, and something moving in
 * the corner of your eye every few seconds for as long as you read is the opposite of a stylesheet
 * that says "nothing here glows". Three traverses is a visit; endless is a companion nobody asked
 * for. Because the animation ends empty, stopping leaves no half-hopped ember frozen in the corner —
 * it leaves nothing. One byte in the GIF's loop count, patched rather than re-encoded.
 *
 * **It is transparent, not white-backed.** The opposite of the hero's choice, and for the opposite
 * reason: this one floats over whatever is beneath it — a code panel is #f5f5f7, the drawer is white
 * — so a white rectangle would be a visible box. GIF's one-bit alpha does leave a harder edge, but
 * the frame is shown at 300px from a 520px source and the downscale absorbs it.
 *
 * **It cannot be interacted with.** `pointer-events: none`, so it never intercepts a click, and a
 * z-index below the scrim so an open drawer or search palette covers it rather than the reverse.
 * The README that came with the asset suggested z-index 50, which would have put a decoration on
 * top of the search dialog.
 *
 * **It is not on a phone.** See the note at the breakpoint. */
.roam {
  position: fixed;
  right: 0;
  bottom: 0;
  width: clamp(220px, 24vw, 320px);
  height: auto;
  z-index: 15;                 /* above the page, below the scrim (20), drawer (25) and bar (30) */
  pointer-events: none;
  user-select: none;
}

/* Decoration with no informational content, so a reader who asked for less motion gets nothing
 * rather than a still frame of a mascot standing in the corner for no reason. */
@media (prefers-reduced-motion: reduce) {
  .roam { display: none; }
}

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

.tablewrap {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  margin: 1.5rem 0;
}
table { border-collapse: collapse; width: 100%; font-size: 15px; }
th, td { text-align: left; padding: .6rem .8rem; border-bottom: 1px solid var(--hair); }
th {
  font-weight: 600;
  color: var(--ink-2);
  font-size: 13px;
  text-transform: uppercase;
  letter-spacing: .04em;
}
tbody tr:hover { background: var(--wash-2); }

/* ---- the documentation shell ------------------------------------------------------------------
 *
 * Sidebar, content, and an "on this page" rail. Below 1100px the rail goes; below 900px the
 * sidebar becomes a drawer. Both are laid out here and driven by docs/assets/site.js. */

.docs {
  display: grid;
  grid-template-columns: var(--side-w) minmax(0, 1fr) var(--toc-w);
  gap: clamp(1.5rem, 3vw, 3.5rem);
  max-width: 82rem;
  margin: 0 auto;
  align-items: start;
}
.docs > .doc-body { min-width: 0; }        /* without this a wide code panel widens the grid */

.side, .toc {
  position: sticky;
  top: var(--bar-h);
  max-height: calc(100dvh - var(--bar-h));
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: 1.75rem 0 3rem;
  font-size: 14px;
}
.side { border-right: 1px solid var(--hair); padding-right: 1rem; }

.side h2, .toc h2 {
  margin: 0 0 .75rem;
  font-size: 11.5px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .06em;
  color: var(--ink-2);
}
.side ol, .side ul, .toc ul { list-style: none; margin: 0; padding: 0; }

.side .topic > a {
  display: flex;
  gap: .55rem;
  align-items: baseline;
  padding: .4rem .5rem;
  border-radius: var(--radius-sm);
  color: var(--ink);
  line-height: 1.35;
}
.side .topic > a:hover { background: var(--wash); text-decoration: none; }
.side .topic > a.here { background: var(--wash); font-weight: 600; }
.side .topic .n {
  flex: 0 0 1.15rem;
  color: var(--ink-2);
  font-variant-numeric: tabular-nums;
  font-size: 12.5px;
}

/* The eight steps of the open topic. Collapsed on every other topic, so the sidebar stays a list
 * of twelve things rather than a wall of ninety-six. */
.side .steps { margin: .1rem 0 .5rem 1.7rem; display: none; }
.side .topic.open .steps { display: block; }
.side .steps a {
  display: block;
  padding: .22rem .5rem;
  border-left: 2px solid var(--hair);
  color: var(--ink);
  font-size: 13.5px;
  line-height: 1.4;
}
.side .steps a:hover { border-left-color: var(--ink-2); background: var(--wash); text-decoration: none; }
.side .steps a.here { border-left-color: var(--accent); color: var(--accent); font-weight: 500; }

.side .group + .group { margin-top: 1.5rem; padding-top: 1.25rem; border-top: 1px solid var(--hair); }

.toc a {
  display: block;
  padding: .22rem 0 .22rem .7rem;
  border-left: 2px solid var(--hair);
  color: var(--ink);
  font-size: 13.5px;
  line-height: 1.4;
}
.toc a:hover { border-left-color: var(--ink-2); text-decoration: none; }
.toc a.here { border-left-color: var(--accent); color: var(--accent); }

/* Previous / next, because a guide is read in order. */
.seq { display: flex; gap: 1rem; margin: 4rem 0 0; padding-top: 1.5rem; border-top: 1px solid var(--hair); }
.seq a {
  flex: 1 1 0;
  min-height: 44px;
  padding: .8rem 1rem;
  border: 1px solid var(--hair-2);
  border-radius: var(--radius);
  color: var(--ink);
  line-height: 1.35;
}
.seq a:hover { background: var(--wash); text-decoration: none; }
.seq a.next { text-align: right; }
.seq .rel { display: block; font-size: 12px; color: var(--ink-2); margin-bottom: .15rem; }

/* ---- the drawer, and the scrim behind it ------------------------------------------------------ */

.scrim {
  position: fixed;
  inset: 0;
  z-index: 20;
  background: rgba(0, 0, 0, 0.28);
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--calm);
}
/* The search palette is modal at every width. The drawer only exists below 900px, so its scrim and
 * its scroll lock are scoped there — otherwise a `drawer-open` class left behind by a resize dims a
 * desktop page that has its sidebar inline and nothing overlaying it. */
body.find-open .scrim { opacity: 1; pointer-events: auto; }
body.find-open { overflow: hidden; }

@media (max-width: 900px) {
  body.drawer-open .scrim { opacity: 1; pointer-events: auto; }
  body.drawer-open { overflow: hidden; }
}

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

.find-panel {
  position: fixed;
  z-index: 50;
  top: max(6vh, 3rem);
  left: 50%;
  transform: translate(-50%, -8px);
  width: min(38rem, calc(100vw - 2rem));
  max-height: min(70dvh, 34rem);
  display: none;
  flex-direction: column;
  background: var(--paper);
  border: 1px solid var(--hair-2);
  border-radius: 14px;
  box-shadow: 0 18px 60px rgba(0, 0, 0, 0.18);   /* the one shadow on the site: it floats */
  overflow: hidden;
  opacity: 0;
  transition: opacity var(--quick), transform var(--quick);
}
body.find-open .find-panel { display: flex; opacity: 1; transform: translate(-50%, 0); }

.find-panel input {
  font: 400 17px/1.4 var(--sans);
  color: var(--ink);
  padding: 1rem 1.1rem;
  border: 0;
  border-bottom: 1px solid var(--hair);
  background: none;
  width: 100%;
}
.find-panel input::placeholder { color: var(--ink-2); }
.find-panel input:focus { outline: none; }

.find-results { overflow-y: auto; padding: .4rem; overscroll-behavior: contain; }
/* Direct children only. A `display: block` on every descendant link turned the "no index yet"
 * message's inline link into three stacked lines of prose. */
.find-results > a {
  display: block;
  padding: .55rem .7rem;
  border-radius: var(--radius-sm);
  color: var(--ink);
  line-height: 1.35;
}
.find-results > a:hover, .find-results > a.on { background: var(--wash); text-decoration: none; }
.find-results .name { font-family: var(--mono); font-size: 14px; }
.find-results .kind {
  float: right;
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: .05em;
  color: var(--ink-2);
  padding-top: .2rem;
}
.find-results .where { display: block; font-size: 12.5px; color: var(--ink-2); }
.find-empty { padding: 1.25rem; color: var(--ink); font-size: 14px; }

kbd {
  font: 500 11.5px/1 var(--sans);
  padding: .22rem .38rem;
  border: 1px solid var(--hair-2);
  border-bottom-width: 2px;
  border-radius: 5px;
  color: var(--ink-2);
  background: var(--paper);
}

/* ---- the examples page ------------------------------------------------------------------------ */

.picker { display: flex; flex-wrap: wrap; gap: .4rem; margin: 2rem 0 1.5rem; }
.picker button {
  font: inherit;
  font-size: 14px;
  min-height: 36px;
  padding: .4rem .9rem;
  border-radius: var(--pill);
  border: 1px solid var(--hair-2);
  background: var(--paper);
  color: var(--ink);
  cursor: pointer;
  transition: background var(--quick), color var(--quick), border-color var(--quick);
}
.picker button:hover { background: var(--wash); }
.picker button[aria-selected="true"] { background: var(--ink); border-color: var(--ink); color: #fff; }

.pane { display: grid; grid-template-columns: minmax(0, 1fr) 17rem; gap: 1rem; align-items: start; }
.pane > * { min-width: 0; }

.out { border: 1px solid var(--hair-2); border-radius: var(--radius); overflow: hidden; }
.out h4 {
  margin: 0;
  padding: .6rem .9rem;
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: .05em;
  color: var(--ink-2);
  font-weight: 600;
  border-bottom: 1px solid var(--hair);
}
.out h4.stale { color: var(--refuse); }
.out pre { border: 0; border-radius: 0; margin: 0; background: var(--paper); }
.note { font-size: 13px; color: var(--ink); padding: .7rem .9rem; border-top: 1px solid var(--hair); }
.stale { color: var(--refuse); }

/* ---- guide navigation ------------------------------------------------------------------------- */

.pages { list-style: none; padding: 0; margin: 2rem 0; }
.pages li { margin: 0; border-bottom: 1px solid var(--hair); }
.pages a {
  display: flex;
  gap: 1rem;
  align-items: baseline;
  min-height: 44px;
  padding: .9rem .5rem;
  border-radius: var(--radius-sm);
  color: var(--ink);
}
.pages a:hover { text-decoration: none; background: var(--wash); }
.pages .n { color: var(--ink-2); font-variant-numeric: tabular-nums; min-width: 1.5rem; }
.pages .what { color: var(--ink-2); margin-left: auto; font-size: 14px; text-align: right; }

footer {
  border-top: 1px solid var(--hair);
  padding: 2.5rem var(--gap);
  color: var(--ink);
  font-size: 13px;
  text-align: center;
}
footer a { color: var(--accent); }

/* ---- responsive -------------------------------------------------------------------------------
 *
 * Four steps, each with a reason. 1100: the "on this page" rail runs out of room before the prose
 * does. 900: the sidebar becomes a drawer, AND the hamburger appears to open it. 768: the link row
 * collapses into that drawer. 520: the two-column examples pane and the prev/next pair stack.
 *
 * The hamburger moved from 768 to 900 to close a gap that shipped. Between those two widths a doc
 * page's sidebar was already a drawer and there was no button anywhere that opened it, so the
 * contents were simply gone for 130px of viewport.
 *
 * And the drawer now carries the site's own links, which is the bug behind that one. The link row
 * was hidden below 768 on EVERY page, but the hamburger only appeared where there was a sidebar to
 * open — so on the landing page, the refusals, the examples and the install page there was no
 * navigation at all on a phone. Not "hard to find": absent. The reasoning that produced it — "a
 * hamburger that opens nothing is worse than no hamburger" — was right about the hamburger and
 * wrong about what to do next, which was to give it something to open. */

/* `.side-menu` is the drawer on a page that has no sidebar. Above 900px the bar's link row is the
 * navigation, so this must not draw an empty rail beside the content. */
@media (min-width: 901px) {
  .side-menu { display: none; }
}

@media (max-width: 1100px) {
  .docs { grid-template-columns: var(--side-w) minmax(0, 1fr); }
  .toc { display: none; }
}

@media (max-width: 900px) {
  .docs { display: block; }

  .side {
    position: fixed;
    top: 0; bottom: 0; left: 0;
    /* Under the bar (30), over the scrim (20). The drawer used to sit at 45, above everything, so
     * opening it hid the logo and the button that opened it — and the only way out was the scrim.
     * Sliding it beneath the bar keeps the hamburger visible, which is what closes it. */
    z-index: 25;
    width: min(20rem, 84vw);
    max-height: none;
    height: 100dvh;
    padding: calc(var(--bar-h) + 1rem) 1rem 3rem;
    background: var(--paper);
    border-right: 1px solid var(--hair-2);
    transform: translateX(-101%);
    transition: transform var(--calm);
    visibility: hidden;
  }
  body.drawer-open .side { transform: translateX(0); visibility: visible; }

  /* Every topic's steps open in the drawer: it is the only navigation left, so hiding
   * ninety-six links behind a topic nobody can see the state of would be worse than a long list. */
  .side .topic .steps { display: block; }

  /* The site's links live here now, and on a page with no sidebar this is the whole drawer. */
  .side .site { display: block; }
  .bar .burger { display: inline-flex; }
}

@media (max-width: 768px) {
  :root { --gap: 1.15rem; }
  .bar { gap: .5rem; }
  .bar .links { display: none; }
  /* The search control drops its label and becomes the 44px icon a thumb wants. */
  .bar .find { width: 44px; height: 44px; padding: 0; justify-content: center; border-color: transparent; }
  .bar .find .what, .bar .find kbd { display: none; }
  .bar .find svg { width: 19px; height: 19px; }
  .bar .burger { display: inline-flex; }
  .bar .mark img { height: 20px; }
  h1 { margin-top: 2rem; }
  main { padding-bottom: 4rem; }

  /* A finger, not a cursor. 28px is fine beside a mouse and a miss beside a thumb, so the panel's
   * title bar grows to give its one control the 44px iOS asks for. */
  .code-bar { padding: .3rem .35rem .3rem .9rem; }
  .code-copy { min-height: 40px; min-width: 64px; }
  .picker button { min-height: 44px; }
}

@media (max-width: 620px) {
  .pages .what { display: none; }

  /* No roaming ember on a phone. At any size it can be read at it covers a tenth of the screen
   * permanently while you scroll, it traverses most of the width, and it sits exactly where a thumb
   * rests. `pointer-events: none` means it cannot block a tap — but a small screen has no corner to
   * spare, and a decoration that is in the way is not a decoration. */
  .roam { display: none; }
}

@media (max-width: 520px) {
  .pane { grid-template-columns: minmax(0, 1fr); }
  .seq { flex-direction: column; }
  .seq a.next { text-align: left; }
  .hero { padding-top: 2.5rem; }
  .lnum { min-width: 2.5rem; padding-right: .6rem; }
  .diag { padding-left: calc(2.5rem + .1rem); }
  .code-file { max-width: 9rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
}

/* A short window: the sticky rails would eat the screen, so they scroll with the page instead.
 *
 * `min-width: 901px` is load-bearing and was missing. Below that the sidebar is a fixed DRAWER, and
 * this rule — later in the file, same specificity — was turning it back into a static block that
 * pushed the whole page down by its own height. A phone in landscape is 844x390, which is exactly a
 * short viewport, so the menu appeared as a 400px slab above the content on every page.
 *
 * Found by taking a 390x420 screenshot, which is the only reason the height ever went under 560. */
@media (min-width: 901px) and (max-height: 560px) {
  .side, .toc { position: static; max-height: none; }
}

@media print {
  .bar, .side, .toc, .scrim, .find-panel, .code-copy, .seq { display: none !important; }
  .docs { display: block; }
  .code, pre { break-inside: avoid; border-color: #999; }
  a { color: var(--ink); }
}
