/* Wisp docs. A light near-white wash and a deep slate dark, both tinted
   toward the violet accent, following the system until a visitor picks one.
   OKLCH throughout. Motion is a quick ease-out on transform and opacity,
   and off for prefers-reduced-motion.

   Breakpoints (rem; a media query cannot read a variable):
     36   pager stacks              40   header drops the keys and the label
     48   docs sidebar appears      60   two-column home sections
     62   header links fold         64   blog contents column
     84   docs contents column */

:root {
  color-scheme: light dark;

  --bg: light-dark(oklch(0.99 0.003 290), oklch(0.215 0.014 285));
  --wash: light-dark(oklch(0.968 0.007 290), oklch(0.19 0.013 285));
  --panel: light-dark(oklch(1 0 0), oklch(0.245 0.015 285));
  --panel-2: light-dark(oklch(0.95 0.009 290), oklch(0.28 0.016 285));
  --line: light-dark(oklch(0.91 0.01 290), oklch(0.32 0.016 285));
  --line-strong: light-dark(oklch(0.82 0.014 290), oklch(0.42 0.018 285));
  --ink: light-dark(oklch(0.21 0.012 285), oklch(0.96 0.005 285));
  --slate: light-dark(oklch(0.42 0.015 285), oklch(0.8 0.012 285));
  --ash: light-dark(oklch(0.5 0.015 285), oklch(0.7 0.014 285));
  --accent: light-dark(oklch(0.52 0.2 295), #a17ff5);
  --accent-hover: light-dark(oklch(0.45 0.2 295), oklch(0.76 0.14 295));
  --on-accent: light-dark(oklch(0.99 0.005 295), oklch(0.17 0.02 295));
  --accent-soft: light-dark(oklch(0.95 0.025 295), oklch(0.3 0.05 295));
  --accent-ink: light-dark(oklch(0.45 0.19 295), oklch(0.82 0.1 295));
  --code: light-dark(oklch(0.975 0.005 290), oklch(0.18 0.012 285));
  --warn: light-dark(oklch(0.5 0.16 35), oklch(0.8 0.11 45));
  --warn-soft: light-dark(oklch(0.965 0.02 45), oklch(0.27 0.035 40));
  --warn-line: light-dark(oklch(0.88 0.05 45), oklch(0.4 0.06 40));
  --good: light-dark(oklch(0.48 0.12 155), oklch(0.8 0.12 155));

  --hl-k: light-dark(oklch(0.48 0.2 300), oklch(0.78 0.14 303));
  --hl-s: light-dark(oklch(0.45 0.13 155), oklch(0.82 0.12 155));
  --hl-c: light-dark(oklch(0.52 0.03 285), oklch(0.7 0.035 285));
  --hl-n: light-dark(oklch(0.5 0.14 60), oklch(0.82 0.12 70));
  --hl-t: light-dark(oklch(0.46 0.13 235), oklch(0.8 0.1 235));
  --hl-a: light-dark(oklch(0.46 0.14 20), oklch(0.82 0.1 20));

  --sans: var(--font-dm-sans), "Segoe UI Variable", "Segoe UI", system-ui, -apple-system, sans-serif;
  --mono: var(--font-jetbrains-mono), "Cascadia Code", ui-monospace, SFMono-Regular, Menlo, monospace;
  --radius: 0.5rem;
  --radius-lg: 1rem;
  --ease: cubic-bezier(0.16, 1, 0.3, 1);
  --gutter: clamp(1rem, 4vw, 2rem);
  --head: 4rem;
  --pill: 999px;
  --t: 150ms var(--ease);
  --fs-lede: 1.1875rem;
  --fs-sm: 0.9375rem;
  --measure: 50rem;
  --scrim: oklch(0.2 0.02 285 / 0.5);

  /* A slim violet thumb on every scroller. */
  --sb-track-color: transparent;
  --sb-thumb-color: #7544cd;
  --sb-size: 7px;

  /* Line icons drawn as masks, so they take the text color. */
  --i-right: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5 12h14M13 6l6 6-6 6'/%3E%3C/svg%3E");
  --i-left: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M19 12H5M11 6l-6 6 6 6'/%3E%3C/svg%3E");
  --i-down: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
  --i-link: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M10 13a5 5 0 0 0 7.5.5l3-3a5 5 0 0 0-7-7l-1.7 1.7'/%3E%3Cpath d='M14 11a5 5 0 0 0-7.5-.5l-3 3a5 5 0 0 0 7 7l1.7-1.7'/%3E%3C/svg%3E");
  --i-check: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5 12.5l4.5 4.5L19 7.5'/%3E%3C/svg%3E");
  --i-note: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='9'/%3E%3Cpath d='M12 11v5M12 7.5v.5'/%3E%3C/svg%3E");
  --i-warn: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 3.5L2.5 20h19z'/%3E%3Cpath d='M12 10v4.5M12 17.5v.5'/%3E%3C/svg%3E");
  --i-book: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4 5a2 2 0 0 1 2-2h14v15H6a2 2 0 0 0-2 2z'/%3E%3Cpath d='M4 20a2 2 0 0 0 2 2h14v-4'/%3E%3C/svg%3E");
}

:root[data-theme="light"] {
  color-scheme: light;
}

:root[data-theme="dark"] {
  color-scheme: dark;
}

* {
  box-sizing: border-box;
}

html {
  scroll-padding-top: calc(var(--head) + 1rem);
  -webkit-text-size-adjust: 100%;
}

@media (prefers-reduced-motion: no-preference) {
  html {
    scroll-behavior: smooth;
  }
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font: 400 1.0625rem/1.75 var(--sans);
  text-rendering: optimizeLegibility;
  overflow-wrap: break-word;
  /* The footer sits at the bottom of the screen on a short page. */
  display: flex;
  flex-direction: column;
  min-height: 100vh;
  min-height: 100dvh;
}

body > main {
  flex: 1 0 auto;
}

img,
svg {
  max-width: 100%;
}

a {
  color: var(--accent-ink);
  text-underline-offset: 0.2em;
  text-decoration-thickness: 1px;
  transition:
    color var(--t),
    text-underline-offset 150ms ease-out; /* not the long expo tail: the offset snaps to whole pixels, so a tail shows as a late second step */
}

a:hover {
  color: var(--accent-hover);
  text-underline-offset: 0.3em;
}

:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* One look for every summary: its own marker, drawn or left out per component. */
summary {
  list-style: none;
}

summary::-webkit-details-marker {
  display: none;
}

::selection {
  background: var(--accent-soft);
}

h1,
h2,
h3,
h4 {
  line-height: 1.25;
  letter-spacing: -0.02em;
  text-wrap: balance;
}

.sr {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

.skip {
  position: absolute;
  left: 1rem;
  top: -4rem;
  z-index: 30;
  padding: 0.5rem 0.75rem;
  background: var(--accent);
  color: var(--on-accent);
  border-radius: var(--radius);
}

.skip:focus {
  top: 0.75rem;
}

/* With JavaScript off: what is hidden, and how to get it back. */
.nojs {
  margin: 0;
  padding: 0.625rem var(--gutter);
  border-bottom: 1px solid var(--line);
  background: var(--accent-soft);
  color: var(--ink);
  font-size: 0.875rem;
  line-height: 1.5;
  text-align: center;
}

.wrap {
  width: min(100%, 76rem);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

.muted {
  color: var(--ash);
}

/* ---- Buttons ----------------------------------------------------------- */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 2.75rem;
  padding: 0.5rem 1.375rem;
  border: 1px solid var(--line-strong);
  border-radius: var(--pill);
  background: var(--panel);
  color: var(--ink);
  font: 600 1rem/1.2 var(--sans);
  text-decoration: none;
  cursor: pointer;
  transition:
    background-color var(--t),
    border-color var(--t),
    box-shadow 120ms var(--ease),
    translate 120ms var(--ease);
}

/* A raised key: a solid edge underneath, pushed flat when pressed. */
.btn {
  --edge: var(--line-strong);
  box-shadow: 0 3px 0 var(--edge);
}

.btn:active {
  box-shadow: 0 0 0 var(--edge);
  translate: 0 3px;
}

.btn:hover {
  color: var(--ink);
  border-color: var(--ash);
}

.btn.primary {
  border-color: var(--accent);
  background: var(--accent);
  color: var(--on-accent);
  --edge: color-mix(in oklab, var(--accent) 62%, black);
}

.btn.primary:hover {
  border-color: var(--accent-hover);
  background: var(--accent-hover);
  color: var(--on-accent);
}

.icon:active {
  scale: 0.96;
}

.cta {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 1rem 0.75rem;
  padding-top: 1rem;
}

.cta .btn.primary::after {
  width: 1em;
  height: 1em;
  margin-left: 0.45em;
  --ico: var(--i-right);
}


/* ---- Scrollbars -------------------------------------------------------- */

*::-webkit-scrollbar {
  width: var(--sb-size);
  height: var(--sb-size);
}

*::-webkit-scrollbar-track {
  background: var(--sb-track-color);
  border-radius: 6px;
}

*::-webkit-scrollbar-thumb {
  background: var(--sb-thumb-color);
  border-radius: 6px;
}

@supports not selector(::-webkit-scrollbar) {
  * {
    scrollbar-color: var(--sb-thumb-color) var(--sb-track-color);
    scrollbar-width: thin;
  }
}

/* ---- Shared looks ------------------------------------------------------ */

/* Round icon buttons: the header icons, the menu button, the copy button.
   A component sets --size for another width and --fg for a quieter color. */
.icon,
.mnav summary,
.copy-icon {
  display: grid;
  place-items: center;
  flex: none;
  width: var(--size, 2.5rem);
  height: var(--size, 2.5rem);
  border: 0;
  border-radius: var(--pill);
  background: none;
  color: var(--fg, var(--ink));
  cursor: pointer;
  transition:
    color var(--t),
    background-color var(--t),
    scale 120ms var(--ease);
}

.icon:hover,
.mnav summary:hover,
.copy-icon:hover {
  background: var(--panel-2);
  color: var(--ink);
}

/* Keycaps. */
.key,
.find-bar .esc,
kbd {
  padding: 0 0.375rem;
  border: 1px solid var(--line-strong);
  border-radius: 0.3125rem;
  color: var(--ash);
  font: 500 0.75rem/1.5 var(--mono);
}

/* The card shell: a hairline and a large radius. */
.file,
.result,
.demo .code,
.finder,
.found li,
.pager a,
.doc blockquote,
.callout,
.deep-dive,
.learn,
.recap,
.challenges,
pre {
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
}

/* Line icons drawn as masks: set --ico on the element, size it in the rule.
   Forced colors repaint them (see 9-print.css). */
.cta .btn.primary::after,
.menu > summary::after,
.grp > summary::after,
.crumbs > * + *::before,
.doc .anchor::before,
.callout > p:first-child:has(> strong:only-child)::before,
.pager .prev small::before,
.pager .next small::after {
  content: "";
  background: var(--mask-ink, currentColor);
  mask: var(--ico) center / contain no-repeat;
  forced-color-adjust: none;
}

/* Nav links: a hover wash, the current page in the accent. */
.side li a:hover,
:is(.toc, .post-toc) a:hover {
  background: var(--panel-2);
  color: var(--ink);
}

.side li a[aria-current],
:is(.toc, .post-toc) a[aria-current] {
  color: var(--accent-ink);
  font-weight: 600;
}

.side li a[aria-current],
:is(.toc, .post-toc) a[aria-current],
.hits [aria-selected="true"] a,
.hits a:hover {
  background: var(--accent-soft);
}

/* Windows High Contrast: system colors for the icons, a real outline for focus. */
@media (forced-colors: active) {
  :focus-visible {
    outline: 2px solid Highlight;
  }

  svg :is(path, circle, ellipse, rect) {
    stroke: CanvasText;
  }

  svg .fill,
  .ghost .shape {
    fill: CanvasText;
  }

  .ghost :is(.eye, .mouth) {
    fill: Canvas;
  }
}

/* ---- Header ------------------------------------------------------------ */

.top {
  position: sticky;
  top: 0;
  z-index: 20;
  height: var(--head);
  border-bottom: 1px solid var(--line);
  background: var(--bg);
}

/* The header: brand left, search centered, links right. */
.bar {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: 1rem;
  height: 100%;
  padding-inline: var(--gutter);
}

.top .brand {
  justify-self: start;
}

.end {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 0.5rem;
}

.brand {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  color: var(--ink);
  font-weight: 700;
  font-size: 1.25rem;
  letter-spacing: -0.03em;
  text-decoration: none;
}

.brand:hover {
  color: var(--ink);
}

/* As wide as fits with room for the links on both sides, so it stays centered. */
.search {
  position: relative;
  width: clamp(16rem, 100vw - 2 * (var(--gutter) + 28.5rem), 34rem);
  min-width: 0;
}

.search .i,
.find-bar .i {
  position: absolute;
  left: 0.875rem;
  top: 50%;
  width: 1.125rem;
  height: 1.125rem;
  translate: 0 -50%;
  fill: none;
  stroke: var(--ash);
  stroke-width: 2;
  stroke-linecap: round;
  pointer-events: none;
}

.search input {
  width: 100%;
  height: 2.5rem;
  padding: 0 4.5rem 0 2.625rem;
  border: 0;
  border-radius: var(--pill);
  background: var(--panel-2);
  color: var(--ink);
  font: inherit;
  font-size: var(--fs-sm);
  cursor: text;
}

/* The browser's own clear button, drawn to match the icons. */
.search input::-webkit-search-cancel-button,
.find-bar input::-webkit-search-cancel-button {
  appearance: none;
  width: 0.875rem;
  height: 0.875rem;
  margin: 0 0.125rem 0 0.5rem;
  background: var(--ash);
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.5' stroke-linecap='round'%3E%3Cpath d='M6 6l12 12M18 6L6 18'/%3E%3C/svg%3E") center / contain no-repeat;
  cursor: pointer;
  transition: background-color var(--t);
}

.search input::-webkit-search-cancel-button:hover,
.find-bar input::-webkit-search-cancel-button:hover {
  background: var(--ink);
}

/* The shortcut hint hides while typing, so the clear button takes its place at the edge. */
.search input:is(:focus, :not(:placeholder-shown)) {
  padding-right: 0.875rem;
}

.search input::placeholder {
  color: var(--ash);
}

.search input:hover {
  background: color-mix(in oklch, var(--panel-2), var(--ink) 4%);
}

.key {
  position: absolute;
  right: 0.75rem;
  top: 50%;
  translate: 0 -50%;
  pointer-events: none;
}

.links {
  display: flex;
  align-items: center;
  gap: 0.125rem;
}

.links a,
.mnav nav a {
  display: inline-flex;
  align-items: center;
  height: 2.25rem;
  padding: 0 0.875rem;
  border-radius: var(--pill);
  line-height: 1;
  color: var(--ink);
  font-weight: 500;
  font-size: 0.9375rem;
  text-decoration: none;
  white-space: nowrap;
  transition: background-color 150ms var(--ease);
}

.links a:hover,
.mnav nav a:hover {
  background: var(--panel-2);
}

.links a[aria-current],
.mnav nav a[aria-current] {
  background: var(--accent-soft);
  color: var(--accent-ink);
}

.icon svg,
.mnav summary svg {
  width: 1.375rem;
  height: 1.375rem;
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.icon svg .fill {
  fill: currentColor;
  stroke: none;
}

/* Both icons share the button; the one for the other theme turns and fades in. */
.theme svg {
  grid-area: 1 / 1;
  transition:
    opacity 250ms var(--ease),
    rotate 400ms var(--ease),
    scale 400ms var(--ease);
}

.theme .sun,
:root[data-theme="dark"] .theme .moon {
  opacity: 0;
  rotate: -90deg;
  scale: 0.5;
}

:root[data-theme="dark"] .theme .sun {
  opacity: 1;
  rotate: 0deg;
  scale: 1;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .theme .moon {
    opacity: 0;
    rotate: 90deg;
    scale: 0.5;
  }

  :root:not([data-theme="light"]) .theme .sun {
    opacity: 1;
    rotate: 0deg;
    scale: 1;
  }
}

.mnav {
  display: none;
  position: relative;
}

.mnav nav {
  position: absolute;
  right: 0;
  top: calc(100% + 1rem);
  display: grid;
  min-width: 12rem;
  gap: 0.125rem;
  padding: 0.5rem;
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  background: var(--panel);
}

@media (max-width: 62rem) {
  .links {
    display: none;
  }

  .mnav {
    display: block;
  }
}

@media (max-width: 40rem) {
  .bar {
    grid-template-columns: auto 1fr auto;
    gap: 0.5rem;
  }

  .search {
    width: auto;
  }

  .gh,
  .key {
    display: none;
  }

  .brand > span:last-child {
    display: none;
  }

  .search input {
    padding-right: 0.75rem;
  }
}

/* ---- Search dialog ----------------------------------------------------- */

.finder {
  width: min(100% - 2rem, 40rem);
  max-height: min(80vh, 36rem);
  margin: 10vh auto auto;
  padding: 0;
  background: var(--panel);
  color: var(--ink);
  overflow: hidden;
}

.finder[open] {
  display: flex;
  flex-direction: column;
}

/* From 48rem the results drop below the header field instead of opening a
   modal; search.js sets where. */
.finder.drop {
  position: fixed;
  inset: var(--drop-top, 4rem) auto auto var(--drop-left, 0);
  z-index: 30;
  width: var(--drop-w, 34rem);
  max-height: min(70dvh, 30rem);
  margin: 0;
}

.finder.drop .find-bar {
  display: none;
}

.search:is(:focus-within, :has(input:not(:placeholder-shown))) .key {
  opacity: 0;
}

.key {
  transition: opacity var(--t);
}

.finder::backdrop {
  background: var(--scrim);
}

.find-bar {
  position: relative;
  display: flex;
  align-items: center;
  border-bottom: 1px solid var(--line);
}

.find-bar input {
  flex: 1;
  min-width: 0;
  height: 3.5rem;
  padding: 0 0.75rem 0 2.75rem;
  border: 0;
  background: none;
  color: var(--ink);
  font: inherit;
  font-size: 1.0625rem;
}

.find-bar input:focus-visible {
  outline: 0;
}

.find-bar .esc {
  margin-right: 0.75rem;
  background: none;
  cursor: pointer;
}

.hits {
  margin: 0;
  padding: 0.5rem;
  overflow-y: auto;
  list-style: none;
}

.hits .page {
  padding: 0.75rem 0.75rem 0.25rem;
  color: var(--ash);
  font-size: 0.8125rem;
  font-weight: 600;
}

.hits a {
  display: grid;
  padding: 0.5rem 0.75rem;
  border-radius: var(--radius);
  color: var(--ink);
  text-decoration: none;
}

.hits .h {
  font-weight: 600;
}

.hits .t {
  color: var(--slate);
  font-size: 0.875rem;
  line-height: 1.5;
}

.hits b {
  color: var(--accent-ink);
}

.hits .none,
.find-tip {
  margin: 0;
  padding: 1.5rem;
  color: var(--ash);
  text-align: center;
}

/* ---- Footer ------------------------------------------------------------ */

.foot {
  border-top: 1px solid var(--line);
  background: var(--bg);
  font-size: var(--fs-sm);
}

.cols {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(10rem, 1fr));
  gap: 2rem;
  padding-block: 3.5rem;
}

.foot .mark p {
  margin: 0.5rem 0 0;
  color: var(--ash);
}

.foot .mark .maker {
  margin-top: 1rem;
}

.foot .maker a {
  color: var(--slate);
}

.foot .maker a:hover {
  color: var(--accent-ink);
}

.foot h2 {
  margin: 0 0 0.75rem;
  color: var(--ink);
  font-size: var(--fs-sm);
}

.foot nav {
  display: grid;
  align-content: start;
  gap: 0.375rem;
}

.foot nav a {
  color: var(--slate);
  text-decoration: none;
}

.foot nav a:hover {
  color: var(--accent-ink);
}


/* ---- Home: sections, tabs, comparison ----------------------------------
   The hero, demo, hosts and band styles live in their components. */

/* Sections alternate between the page and a soft wash, full width. */
.sec {
  padding-block: clamp(3.5rem, 8vw, 6rem);
  border-top: 1px solid var(--line);
}

.home > section:nth-of-type(even) {
  background: var(--wash);
}

.sec h2 {
  margin: 0 0 1rem;
  font-size: clamp(1.875rem, 4.5vw, 2.75rem);
  font-weight: 700;
}

/* Home sections read as one centered column with left-aligned text. */
.sec-head,
.claim.wide {
  max-width: 44rem;
  margin: 0 auto 3rem;
}

.sec-head p,
.claim p {
  margin: 0;
  color: var(--slate);
  font-size: var(--fs-lede);
}

.claim.wide p + p {
  margin-top: 1rem;
}

.claim.wide:last-child {
  margin-bottom: 0;
}

.claim.wide p.more {
  margin-top: 1.25rem;
}

.split {
  display: grid;
  gap: 2.5rem;
  align-items: center;
}

.split > * {
  min-width: 0;
}

@media (min-width: 60rem) {
  .split {
    grid-template-columns: 5fr 7fr;
    gap: 4rem;
  }

  .split.flip .claim {
    order: 2;
  }
}

/* Code beside its result: the file frame, a tabbed pair of panes. */
.file {
  min-width: 0;
  background: var(--code);
  overflow: hidden;
}

.file pre {
  margin: 0;
  border: 0;
  border-radius: 0;
}

.tabs {
  display: flex;
  gap: 0.25rem;
  padding: 0 0.75rem;
  border-bottom: 1px solid var(--line);
  position: relative; /* holds the hidden radios, so focusing one never scrolls the row */
  overflow-x: auto;
  overflow-y: hidden;
}

.tabs :is(label, .tab) {
  position: relative;
  padding: 0.625rem 0.5rem;
  color: var(--ash);
  font: 500 0.8125rem/1.4 var(--mono);
  white-space: nowrap;
  cursor: pointer;
}

.tabs :is(.tab, input:checked + label) {
  color: var(--ink);
}

.tabs input:focus-visible + label {
  outline: 2px solid var(--accent);
  outline-offset: -4px;
}

/* Tabs: the underline grows from the middle, a hover hints at it. */
.tabs :is(label, .tab)::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 2px;
  border-radius: 2px 2px 0 0;
  background: var(--line-strong);
  scale: 0 1;
  transition:
    scale 250ms var(--ease),
    background-color var(--t);
}

.tabs label:hover::after {
  scale: 0.4 1;
}

.tabs :is(.tab, input:checked + label)::after {
  background: var(--accent);
  scale: 1 1;
}

.tabbed .pane-2,
.tabbed:has(.tabs input:nth-of-type(2):checked) .pane-1 {
  display: none;
}

.tabbed:has(.tabs input:nth-of-type(2):checked) .pane-2 {
  display: block;
}

/* Shared by the demo form, the search page and the blog filter. */
.view input,
.plain-find input,
.blog-bar input {
  flex: 1;
  min-width: 0;
  height: 2.75rem;
  padding: 0 1.125rem;
  border: 1px solid var(--line-strong);
  border-radius: var(--pill);
  background: var(--bg);
  color: var(--ink);
  font: inherit;
}

/* The token comparison. */
.pair {
  display: grid;
  gap: 1.5rem;
  margin-bottom: 3rem;
}

@media (min-width: 60rem) {
  .pair {
    grid-template-columns: 1fr 1fr;
    align-items: start;
  }
}

.cmp {
  min-width: 0;
  margin: 0;
}

.cmp figcaption {
  margin-bottom: 0.625rem;
  color: var(--slate);
  font-size: var(--fs-sm);
  font-weight: 500;
}

.cmp .scroll {
  max-height: 24rem;
  overflow: auto;
}

/* Launch sections rise into place as they scroll in (CSS only, where supported). */
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    .sec-head,
    .band .claim,
    .band .sample,
    .demo,
    .pair .cmp,
    .hosts {
      animation: rise var(--ease) both;
      animation-timeline: view();
      animation-range: entry 0% entry 60%;
    }

    .pair .cmp + .cmp {
      animation-range: entry 10% entry 70%;
    }
  }
}


/* GSAP (src/lib/motion.js) takes over the rise once it has loaded. */
.gsap :is(.sec-head, .band .claim, .band .sample, .demo, .pair .cmp, .hosts) {
  animation: none;
}

/* A code block after a claim's text has room above it. */
.claim p + pre {
  margin-top: 1.75rem;
}

.hosts-title {
  margin: 2rem 0 0.875rem;
  font-size: 1.0625rem;
}

/* ---- Docs: sidebar, article, contents ---------------------------------- */

.docs {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
}

@media (min-width: 48rem) {
  .docs {
    grid-template-columns: 17rem minmax(0, 1fr);
  }
}

@media (min-width: 84rem) {
  .docs {
    grid-template-columns: 19rem minmax(0, 1fr) 16rem;
  }
}

.side {
  border-bottom: 1px solid var(--line);
}

@media (min-width: 48rem) {
  .side {
    position: sticky;
    top: var(--head);
    height: calc(100dvh - var(--head));
    overflow-y: auto;
    border-bottom: 0;
    border-right: 1px solid var(--line);
  }

  .menu::details-content {
    display: block;
    content-visibility: visible;
  }
}

.menu > summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  min-height: 3rem;
  padding: 0 var(--gutter);
  font-weight: 600;
  cursor: pointer;
}

@media (min-width: 48rem) {
  .menu > summary {
    display: none;
  }
}

.menu > summary::after,
.grp > summary::after {
  --ico: var(--i-down);
  width: 1.125rem;
  height: 1.125rem;
  rotate: -90deg;
  transition: rotate 200ms var(--ease);
}

.menu[open] > summary::after,
.grp[open] > summary::after {
  rotate: 0deg;
}

.side nav {
  padding: 0.75rem 0.75rem 1.5rem;
}

/* Learn, Reference and Hosting as a segmented control: one soft pill marks the
   current side. The tabs are equal and fill the row. */
.switch {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  margin: 0.75rem 0.75rem 0;
  padding: 0.25rem;
  border: 1px solid var(--line);
  border-radius: var(--pill);
  background: var(--wash);
}

.switch a {
  padding: 0.4375rem 0.625rem;
  line-height: 1.3;
  border-radius: var(--pill);
  background: none;
  color: var(--slate);
  font-size: var(--fs-sm);
  font-weight: 600;
  text-align: center;
  text-decoration: none;
  transition: color var(--t), background-color var(--t);
}

.switch a:focus-visible {
  outline-offset: -2px;
}

.switch a:hover:not([aria-current]) {
  background: none;
  color: var(--ink);
}

.switch a[aria-current] {
  background: var(--accent-soft);
  color: var(--accent-ink);
}

.grp > summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0.5rem 0.75rem;
  border-radius: var(--radius);
  color: var(--ink);
  font-weight: 700;
  font-size: var(--fs-sm);
  cursor: pointer;
}

.grp > summary::after {
  color: var(--ash);
}

.grp > summary:hover {
  background: var(--panel-2);
}

.side ul {
  margin: 0 0 0.5rem;
  padding: 0;
  list-style: none;
}

.side li a {
  display: block;
  padding: 0.375rem 0.75rem 0.375rem 1.25rem;
  border-radius: var(--radius);
  color: var(--slate);
  font-size: var(--fs-sm);
  line-height: 1.45;
  text-decoration: none;
}

.doc {
  min-width: 0;
  width: min(100%, var(--measure));
  margin-inline: auto;
  padding: 2.5rem var(--pad) 4rem;
  --pad: var(--gutter);
}

@media (min-width: 62rem) {
  .doc {
    --pad: 3rem;
  }
}

.crumbs {
  display: flex;
  flex-wrap: wrap;
  gap: 0.375rem;
  color: var(--accent-ink);
  font-size: 0.875rem;
  font-weight: 600;
}

.crumbs a {
  text-decoration: none;
}

.crumbs > * + *::before {
  --ico: var(--i-down);
  display: inline-block;
  width: 0.875rem;
  height: 0.875rem;
  margin-right: 0.375rem;
  color: var(--ash);
  rotate: -90deg;
  vertical-align: -0.125em;
}

.crumbs span {
  color: var(--ash);
}

.doc-title {
  margin: 0.5rem 0 1.5rem;
  font-size: clamp(2.25rem, 5vw, 2.75rem);
  font-weight: 700;
  letter-spacing: -0.035em;
}

.doc > p:first-of-type,
.lede {
  color: var(--slate);
  font-size: var(--fs-lede);
}

.doc h2 {
  margin: 3rem 0 1rem;
  font-size: 1.75rem;
  font-weight: 700;
}

.doc h3 {
  margin: 2.25rem 0 0.75rem;
  font-size: 1.3125rem;
  font-weight: 700;
}

.doc h4 {
  margin: 1.75rem 0 0.5rem;
  font-size: 1.0625rem;
}

.doc p,
.doc ul,
.doc ol {
  margin: 0 0 1.25rem;
}

.doc li + li {
  margin-top: 0.25rem;
}

.doc ul,
.doc ol {
  padding-left: 1.5rem;
}

.doc img {
  height: auto;
  border-radius: var(--radius-lg);
}

.doc hr {
  margin: 2.5rem 0;
  border: 0;
  border-top: 1px solid var(--line);
}

.doc blockquote {
  margin: 0 0 1.25rem;
  padding: 1rem 1.25rem;
  background: var(--wash);
  color: var(--slate);
}

.doc blockquote > :last-child {
  margin-bottom: 0;
}

/* Heading links: a link icon on hover, a check once the address is copied. */
.doc :is(h2, h3) {
  position: relative;
}

/* The link sits left of the heading: hanging in the margin where there is room, inline before it on a phone. */
.doc .anchor {
  display: inline-flex;
  align-items: center;
  margin-right: 0.5rem;
  color: var(--ash);
  font-size: 0.7em;
  opacity: 0;
  vertical-align: middle;
  --ico: var(--i-link);
  transition: opacity var(--t);
}

.doc .anchor::before {
  width: 1em;
  height: 1em;
}

.doc :hover > .anchor,
.doc .anchor:focus-visible,
.doc .anchor.done {
  opacity: 1;
}

.doc .anchor.done {
  --ico: var(--i-check);
  color: var(--good);
  animation: pop 300ms var(--ease);
}

@media (min-width: 48rem) {
  .doc .anchor {
    position: absolute;
    right: 100%;
    inset-block: 0;
    height: 1em;
    margin-block: auto;
    margin-right: 0.375rem;
  }
}

@media (hover: none) {
  .doc .anchor {
    opacity: 0.6;
  }
}


/* Inline code, blocks and tables. */
code,
kbd {
  font-family: var(--mono);
  font-size: 0.875em;
}

:not(pre) > code {
  padding: 0.125em 0.375em;
  border-radius: 0.375rem;
  background: var(--accent-soft);
  color: var(--ink);
  overflow-wrap: anywhere;
}

pre {
  position: relative;
  margin: 0 0 1.5rem;
  padding: 1rem 1.25rem;
  background: var(--code);
  overflow-x: auto;
  font-size: 0.875rem;
  line-height: 1.7;
  tab-size: 2;
  white-space: pre-wrap;
  overflow-wrap: break-word;
}

pre code {
  font-size: inherit;
}

/* A block in an article or a sample gets a header bar with its language
   (CSS only), and the copy button sits in that bar. */
:is(.doc, .sample) pre {
  padding-top: 3.25rem;

  &::before {
    content: "Code";
    position: absolute;
    inset: 0 0 auto;
    height: 2.5rem;
    padding: 0 1.25rem;
    border-bottom: 1px solid var(--line);
    color: var(--ash);
    font: 500 0.8125rem/2.5rem var(--sans);
  }

  &:has(> .language-rust)::before { content: "Rust"; }
  &:has(> .language-html)::before { content: "Template"; }
  &:has(> .language-wisp)::before { content: "Wisp"; }
  &:has(> :is(.language-bash, .language-sh, .language-shell, .language-console))::before { content: "Terminal"; }
  &:has(> :is(.language-js, .language-javascript))::before { content: "JavaScript"; }
  &:has(> :is(.language-ts, .language-typescript))::before { content: "TypeScript"; }
  &:has(> .language-toml)::before { content: "TOML"; }
  &:has(> .language-json)::before { content: "JSON"; }
  &:has(> .language-css)::before { content: "CSS"; }
  &:has(> :is(.language-yaml, .language-yml))::before { content: "YAML"; }
  &:has(> .language-sql)::before { content: "SQL"; }
  &:has(> .language-text)::before { content: "Text"; }
}

.copy-code {
  position: absolute;
  top: 0.4375rem;
  right: 0.5rem;
  padding: 0.125rem 0.625rem;
  border: 1px solid var(--line);
  border-radius: var(--pill);
  background: var(--panel);
  color: var(--slate);
  font: 500 0.75rem/1.6 var(--sans);
  cursor: pointer;
  opacity: 0;
  transition:
    opacity var(--t),
    border-color var(--t);
}

pre:hover .copy-code,
pre:focus-within .copy-code,
.copy-code:focus-visible,
.copy-code.done {
  opacity: 1;
}

.copy-code.done {
  border-color: var(--accent);
  color: var(--accent-ink);
  animation: pop 300ms var(--ease);
}

@media (hover: none) {
  .copy-code {
    opacity: 1;
  }
}

.hl-k { color: var(--hl-k); }
.hl-s { color: var(--hl-s); }
.hl-c { color: var(--hl-c); font-style: italic; }
.hl-n { color: var(--hl-n); }
.hl-t { color: var(--hl-t); }
.hl-a { color: var(--hl-a); }

/* Article tables fill the column; a wide one scrolls inside its wrapper
   (added by the layout script) instead of widening the page. */
.doc table {
  width: 100%;
  margin-bottom: 1.5rem;
  border-collapse: collapse;
  font-size: var(--fs-sm);
}

.table-wrap {
  margin-bottom: 1.5rem;
  overflow-x: auto;
}

.table-wrap > table {
  margin-bottom: 0;
}

@media (max-width: 40rem) {
  .doc :is(th, td) {
    padding-right: 0.625rem;
  }
}

/* The token tally: a bar per stack, Wisp in the accent. */
.tally {
  width: 100%;
  margin: 2rem 0;
  border-collapse: collapse;
  font-size: 1rem;
  font-variant-numeric: tabular-nums;
}

.tally caption {
  padding-bottom: 0.5rem;
  color: var(--ink);
  font-weight: 700;
  text-align: left;
}

.tally th,
.tally td {
  padding: 0.75rem 0 0.75rem 1rem;
  vertical-align: middle;
}

.tally :is(th, td):first-child {
  padding-left: 0;
  white-space: nowrap;
}

.tally thead th {
  white-space: nowrap;
  color: var(--ash);
  font-size: 0.8125rem;
  font-weight: 600;
}

.tally tbody th {
  color: var(--ink);
  font-weight: 500;
}

.tally .num {
  width: 4.5rem;
  color: var(--slate);
  text-align: right;
}

.tally .stack {
  color: var(--slate);
  white-space: nowrap;
}

/* On a phone the bars give way to the numbers. */
@media (max-width: 30rem) {
  .bench :is(.meter, thead th:nth-child(3)) {
    display: none;
  }
}

.tally .meter {
  width: 100%;
}

.tally .meter span {
  display: block;
  width: calc(var(--v) * 100%);
  height: 0.5rem;
  border-radius: var(--pill);
  background: var(--line-strong);
}

.tally .us :is(th, td) {
  color: var(--accent-ink);
  font-weight: 700;
}

.tally .us .meter span {
  background: var(--accent);
}

th,
td {
  padding: 0.5rem 1rem 0.5rem 0;
  border-bottom: 1px solid var(--line);
  vertical-align: top;
}

th {
  color: var(--ink);
  font-weight: 600;
  text-align: left;
}

th:last-child,
td:last-child {
  padding-right: 0;
}

/* Is This Page Useful, prev and next, edit. */
.useful {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.75rem;
  margin-top: 3.5rem;
  padding-top: 1.5rem;
  border-top: 1px solid var(--line);
}

.useful p {
  margin: 0 0.5rem 0 0;
  font-weight: 600;
}

.useful .btn {
  min-height: 2.25rem;
  padding-inline: 1.125rem;
  font-size: var(--fs-sm);
}

.useful .thanks {
  color: var(--accent-ink);
  animation: fade 300ms var(--ease);
}

.pager {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 1rem;
  margin-top: 2rem;
}

.pager a {
  display: grid;
  gap: 0.125rem;
  padding: 1rem 1.25rem;
  color: var(--ink);
  font-weight: 700;
  text-decoration: none;
  transition: border-color var(--t), background-color var(--t);
}

.pager a:hover {
  border-color: var(--accent);
  background: var(--wash);
}

.pager .next {
  grid-column: 2;
  text-align: right;
}

.pager small {
  display: inline-flex;
  align-items: center;
  gap: 0.375rem;
  color: var(--ash);
  font-size: 0.8125rem;
  font-weight: 500;
}

.pager .next small {
  justify-content: end;
}

/* An arrow points at the page it leads to: left for prev, right for next. */
.pager .prev small::before,
.pager .next small::after {
  width: 1em;
  height: 1em;
  transition: translate 200ms var(--ease);
}

.pager .prev small::before {
  --ico: var(--i-left);
}

.pager .next small::after {
  --ico: var(--i-right);
}

.pager .prev:hover small::before {
  translate: -3px 0;
}

.pager .next:hover small::after {
  translate: 3px 0;
}

@media (max-width: 36rem) {
  .pager {
    grid-template-columns: 1fr;
  }

  .pager .next {
    grid-column: 1;
  }
}

.edit {
  margin: 2rem 0 0;
  font-size: var(--fs-sm);
}

.toc {
  display: none;
}

@media (min-width: 84rem) {
  .toc {
    display: block;
    position: sticky;
    top: var(--head);
    max-height: calc(100dvh - var(--head));
    overflow-y: auto;
    padding: 2.5rem 1.5rem 2rem 0.5rem;
    font-size: 0.875rem;
  }
}

:is(.toc, .post-toc) .toc-head {
  margin: 0 0 0.75rem;
  color: var(--ash);
  font-size: 0.8125rem;
  font-weight: 700;
  letter-spacing: 0.02em;
}

:is(.toc, .post-toc) ul {
  margin: 0;
  padding: 0;
  list-style: none;
}

:is(.toc, .post-toc) a {
  display: block;
  padding: 0.3125rem 0.75rem;
  border-radius: var(--radius);
  color: var(--slate);
  line-height: 1.4;
  text-decoration: none;
}

:is(.toc, .post-toc) .h3 a {
  padding-left: 1.5rem;
}

:is(.toc, .post-toc) a:hover {
  color: var(--ink);
}

:is(.toc, .post-toc) a[aria-current] {
  background: var(--accent-soft);
  color: var(--accent-ink);
  font-weight: 600;
}

@media (prefers-reduced-motion: no-preference) {
  .doc {
    animation: fade 400ms var(--ease) backwards;
  }
}


/* The speed tables sit side by side, with tight rows; a narrow screen stacks
   them and each scrolls inside a short box. */
.benches {
  display: grid;
  gap: 0 2rem;
  margin: 1.5rem 0;
}

.bench {
  max-height: 18rem;
  overflow-y: auto;
}

.bench .tally {
  margin: 0;
}

.bench .tally .num {
  width: auto;
  white-space: nowrap;
}

.bench .tally :is(th, td) {
  padding-block: 0.4375rem;
  font-size: var(--fs-sm);
  line-height: 1.4;
}

@media (min-width: 48rem) {
  .benches {
    grid-template-columns: 1fr 1fr;
  }

  .bench {
    max-height: none;
    overflow: visible;
  }
}

/* ---- Teaching blocks, written as raw HTML in Markdown ------------------ */

.callout,
.deep-dive,
.learn,
.recap,
.challenges {
  margin: 2rem 0;
  padding: 1.25rem 1.5rem;
}

.callout > :last-child,
.deep-dive > :last-child,
.learn > :last-child,
.recap > :last-child {
  margin-bottom: 0;
}

/* The first line, when it is only bold, is the callout's title. */
.callout > p:first-child:has(> strong:only-child) {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin-bottom: 0.5rem;
}

.callout > p:first-child:has(> strong:only-child)::before {
  --ico: var(--i-note);
  flex: none;
  width: 1.25rem;
  height: 1.25rem;
}

/* A callout sets its tone; the title, icon, code, links and blocks take it. */
.callout {
  --tone: var(--accent-ink);
  --tint: color-mix(in oklch, var(--accent-soft) 60%, var(--bg));
  --edge: color-mix(in oklch, var(--accent) 30%, var(--line));
  border-color: var(--edge);
  background: var(--tint);
  color: color-mix(in oklab, var(--tone) 14%, var(--ink));
}

.callout.pitfall {
  --tone: var(--warn);
  --tint: var(--warn-soft);
  --edge: var(--warn-line);
}

.callout > p:first-child:has(> strong:only-child) {
  color: var(--tone);
}

.callout.pitfall > p:first-child:has(> strong:only-child)::before {
  --ico: var(--i-warn);
}

.callout > p {
  margin-block: 0 0.75rem;
}

.callout :not(pre) > code {
  background: color-mix(in oklab, var(--tone) 16%, transparent);
  color: color-mix(in oklab, var(--tone) 55%, var(--ink));
}

.callout a {
  color: var(--tone);
  text-decoration-color: color-mix(in oklab, var(--tone) 45%, transparent);
}

.callout pre {
  margin: 0.75rem 0 0;
  border-color: var(--edge);
  background: color-mix(in oklab, var(--tone) 8%, var(--code));
}

.callout pre::before {
  border-color: var(--edge);
}

.deep-dive {
  padding: 0;
  background: var(--wash);
}

.deep-dive > summary {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.25rem 0.75rem;
  padding: 1.125rem 1.5rem;
  font-size: var(--fs-lede);
  font-weight: 700;
  cursor: pointer;
}

.deep-dive > summary::before {
  content: "Deep Dive";
  flex-basis: 100%;
  color: var(--accent-ink);
  font-size: 0.8125rem;
  font-weight: 700;
}

.deep-dive > summary::after {
  content: "Show Details";
  margin-left: auto;
  padding: 0.25rem 0.875rem;
  border: 1px solid var(--line-strong);
  border-radius: var(--pill);
  font-size: 0.875rem;
  font-weight: 600;
}

.deep-dive[open] > summary::after {
  content: "Hide Details";
}

.deep-dive[open] {
  padding-bottom: 1.25rem;
}

.deep-dive > :not(summary) {
  margin-inline: 1.5rem;
}

:is(.learn, .recap) {
  background: var(--wash);
}

:is(.learn, .recap)::before {
  display: block;
  margin-bottom: 0.75rem;
  color: var(--accent-ink);
  font-size: var(--fs-lede);
  font-weight: 700;
}

.learn::before {
  content: "You Will Learn";
}

.recap::before {
  content: "Recap";
}

/* The box's own title, a heading or a bold first line, replaces the default. */
:is(.learn, .recap):has(> :first-child:is(h2, h3, h4), > p:first-child > strong:only-child)::before {
  content: none;
}

:is(.learn, .recap) > p:first-child > strong:only-child {
  color: var(--accent-ink);
  font-size: var(--fs-lede);
}

:is(.learn, .recap) > :is(h2, h3, h4):first-child {
  margin: 0 0 0.75rem;
  color: var(--accent-ink);
  font-size: var(--fs-lede);
}

:is(.learn, .recap) ul {
  margin-bottom: 0;
}

.challenges {
  counter-reset: challenge;
  padding: 0;
  overflow: hidden;
}

.challenges::before {
  content: "Try Out Some Challenges";
  display: block;
  padding: 1.25rem 1.5rem 0.75rem;
  font-size: 1.3125rem;
  font-weight: 700;
}

.challenges > :is(h2, h3):first-child {
  margin: 0;
  padding: 1.25rem 1.5rem 0.75rem;
}

.challenges:has(> :is(h2, h3):first-child)::before {
  content: none;
}

.challenge {
  counter-increment: challenge;
  border-top: 1px solid var(--line);
}

.challenge > summary {
  padding: 1rem 1.5rem;
  font-weight: 700;
  cursor: pointer;
}

.challenge > summary::before {
  content: "Challenge " counter(challenge) ": ";
  color: var(--accent-ink);
}

.challenge > summary:hover {
  background: var(--wash);
}

.challenge[open] {
  padding-bottom: 1.25rem;
  background: var(--wash);
}

.challenge > :not(summary) {
  margin-inline: 1.5rem;
}

.solution > summary {
  display: inline-flex;
  margin-bottom: 1rem;
  padding: 0.375rem 1rem;
  border: 1px solid var(--line-strong);
  border-radius: var(--pill);
  background: var(--panel);
  font-size: var(--fs-sm);
  font-weight: 600;
  cursor: pointer;
}

.solution[open] > summary {
  border-color: var(--accent);
  color: var(--accent-ink);
}

.solution > :last-child,
.challenge > :last-child {
  margin-bottom: 0;
}


/* ---- Blog, community, search ------------------------------------------- */

.page {
  width: min(100%, var(--measure));
  margin-inline: auto;
  padding: 3rem var(--gutter) 5rem;
}

.page.doc {
  padding-top: 2.5rem;
}

.page > h1 {
  margin: 0 0 0.75rem;
  font-size: clamp(2.25rem, 5vw, 3rem);
  letter-spacing: -0.035em;
}

.blog-head {
  margin-bottom: 1rem;
}

.blog-bar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem 1.25rem;
  margin-top: 1.5rem;
}

.blog-bar input {
  flex: 1 1 14rem;
}

.blog-count,
.blog-tag,
.meta,
.tag-list span {
  margin: 0;
  color: var(--ash);
  font-size: var(--fs-sm);
}

.blog-tag {
  margin-top: 1rem;
}

.posts {
  margin: 1rem 0 0;
  padding: 0;
  list-style: none;
}

.posts li {
  padding: 1.5rem 0;
  border-bottom: 1px solid var(--line);
}

.posts h2 {
  margin: 0 0 0.375rem;
  font-size: 1.3125rem;
  line-height: 1.3;
}

.posts h2 a {
  color: var(--ink);
  text-decoration: none;
}

.posts h2 a:hover {
  color: var(--accent-ink);
}

.posts p {
  margin: 0 0 0.375rem;
  color: var(--slate);
}

.eyebrow {
  margin: 0 0 0.75rem;
  font-size: 0.75rem;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

.eyebrow a {
  color: var(--ash);
  text-decoration: none;
}

.post .lede {
  margin: 0 0 1rem;
}

.chips {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin: 1rem 0 2rem;
}

.chips a {
  padding: 0.25rem 0.625rem;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius);
  color: var(--ink);
  font-size: 0.875rem;
  text-decoration: none;
}

.chips a:hover {
  border-color: var(--accent);
}

.tag-list {
  display: grid;
  gap: 0.75rem;
  padding: 0;
  list-style: none;
}

.tag-list a {
  margin-right: 0.5rem;
  font-weight: 600;
}

.post-toc {
  display: none;
}

@media (min-width: 64rem) {
  .page:has(.post-toc) {
    width: min(100%, 68rem);
  }

  .post-wrap:has(.post-toc) {
    display: grid;
    grid-template-columns: 12rem minmax(0, 1fr);
    gap: 3rem;
  }

  .post-toc {
    display: block;
    position: sticky;
    top: calc(var(--head) + 2rem);
    align-self: start;
    font-size: 0.875rem;
  }
}

.plain-find {
  display: flex;
  gap: 0.5rem;
  margin-bottom: 1.5rem;
}

.found {
  display: grid;
  gap: 0.5rem;
  padding: 0;
  list-style: none;
}

.found li {
  display: grid;
  padding: 0.75rem 1rem;
}

.found span {
  color: var(--ash);
  font-size: var(--fs-sm);
}


/* ---- Error page: the ghost floats over its own shadow ------------------ */

.error {
  padding: clamp(4rem, 12vw, 8rem) var(--gutter);
  text-align: center;
}

.error .float {
  display: block;
  margin-inline: auto;
}

.error .shade {
  display: block;
  width: 2.75rem;
  height: 0.375rem;
  margin: 0.5rem auto 0;
  border-radius: 50%;
  background: var(--line);
}

.error h1 {
  margin: 1.5rem 0 0.5rem;
  font-size: 3.5rem;
}

.error p {
  color: var(--slate);
}

@media (prefers-reduced-motion: no-preference) {
  .error .float {
    animation: float 3.2s ease-in-out infinite;
  }

  .error .shade {
    animation: shade 3.2s ease-in-out infinite;
  }
}

/* ---- The ghost --------------------------------------------------------- */
/* Eyes and body lean toward the pointer; a hover or focus wakes it, a page
   arrival makes it hop, typing boo startles it. */

.ghost {
  display: block;
  flex: none;
  width: 1.875rem;
  height: 1.875rem;
}

.ghost svg {
  display: block;
  width: 100%;
  overflow: visible;
  transform-origin: 50% 90%;
  rotate: calc(var(--look-x, 0) * 6deg);
  translate: calc(var(--look-x, 0) * 1.5px) 0;
  transition:
    rotate 300ms var(--ease),
    translate 300ms var(--ease);
}

.ghost .shape {
  fill: var(--accent);
  transition: fill var(--t);
}

.ghost .eye,
.ghost .mouth {
  fill: var(--bg);
  transform-box: fill-box;
  transform-origin: center;
}

.ghost .eye {
  translate: calc(var(--look-x, 0) * 1.2px) calc(var(--look-y, 0) * 1.2px);
  transition:
    translate var(--t),
    scale var(--t);
}

.ghost .mouth {
  translate: calc(var(--look-x, 0) * 0.7px) calc(var(--look-y, 0) * 0.7px);
  transition:
    translate var(--t),
    scale var(--t);
}

.brand:is(:hover, :focus-visible) .ghost svg,
.ghost.hop svg {
  animation: hop 450ms var(--ease);
}

.brand:is(:hover, :focus-visible) .ghost .shape {
  fill: var(--accent-hover);
}

.brand:is(:hover, :focus-visible) .ghost .eye {
  scale: 1.2;
}

.brand:is(:hover, :focus-visible) .ghost .mouth {
  scale: 1.5 1.9;
}

/* The eyes blink now and then. */
@media (prefers-reduced-motion: no-preference) {
  .ghost .eye {
    animation: blink 5s infinite;
  }
}

.ghost.boo svg {
  animation: boo 800ms var(--ease);
}

.ghost.boo .eye {
  animation: wide 800ms var(--ease);
}

.ghost.boo .mouth {
  animation: gasp 800ms var(--ease);
}

/* ---- Motion ------------------------------------------------------------ */

@keyframes fade {
  from {
    opacity: 0;
    translate: 0 6px;
  }
}

@keyframes rise {
  from {
    opacity: 0;
    translate: 0 1.5rem;
  }
}

@keyframes draw {
  from {
    clip-path: inset(0 100% 0 0);
  }
}

@keyframes bob {
  from {
    transform: translate3d(0, 0, 0);
  }
  to {
    transform: translate3d(0, -0.5rem, 0);
  }
}

@keyframes check {
  from {
    stroke-dashoffset: 20;
  }
}

@keyframes pop {
  from {
    scale: 0.6;
  }
}

@keyframes shake {
  20% { translate: -4px 0; }
  40% { translate: 4px 0; }
  60% { translate: -2px 0; }
  80% { translate: 2px 0; }
}

@keyframes hop {
  30% {
    transform: translateY(-3px) scale(0.94, 1.08);
  }
  60% {
    transform: translateY(0) scale(1.08, 0.9);
  }
}

@keyframes blink {
  0%,
  92%,
  100% {
    transform: scaleY(1);
  }
  95% {
    transform: scaleY(0.1);
  }
}

@keyframes boo {
  25% {
    transform: translateY(-4px) scale(1.3) rotate(-10deg);
  }
  55% {
    transform: translateY(-3px) scale(1.25) rotate(8deg);
  }
}

@keyframes wide {
  20%,
  70% {
    transform: scale(1.45);
  }
}

@keyframes gasp {
  20%,
  70% {
    transform: scale(1.7, 2.3);
  }
}

@keyframes float {
  50% {
    translate: 0 -0.625rem;
  }
}

@keyframes shade {
  50% {
    scale: 0.7 1;
    opacity: 0.6;
  }
}

@media (prefers-reduced-motion: reduce) {
  *,
  ::before,
  ::after {
    animation: none !important;
    transition: none !important;
  }

  @view-transition {
    navigation: none;
  }
}

/* ---- Delight: tactile presses, page fades, small touches ---------------- */
/* Flat and quiet. Transform and opacity only, ease-out exponential, and none
   of it under prefers-reduced-motion (8-ghost.css switches every animation and
   transition off there too). */

@media (prefers-reduced-motion: no-preference) {
  /* Page transitions: the header and the sidebar stay put, the page fades. */
  @view-transition {
    navigation: auto;
  }

  .top {
    view-transition-name: top;
  }

  .side {
    view-transition-name: side;
  }

  ::view-transition-old(root),
  ::view-transition-new(root) {
    animation-duration: 150ms;
    animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1);
  }

  /* Presses: a quick lift on hover, a crisp press on :active. */
  :is(
    .icon,
    .mnav summary,
    .copy-icon,
    .copy-code,
    .links a,
    .mnav nav a,
    .tabs label,
    .side li a,
    .switch a,
    .grp > summary,
    .pager a,
    .hosts span,
    .find-bar .esc,
    .hits a,
    :is(.toc, .post-toc) a
  ) {
    transition-property: color, background-color, border-color, opacity, scale, translate;
    transition-duration: 140ms;
    transition-timing-function: var(--ease);
  }

  :is(.icon, .mnav summary, .copy-icon, .copy-code, .links a, .mnav nav a, .tabs label, .side li a, .switch a,
    .grp > summary, .pager a, .hosts span, .find-bar .esc, .hits a):active {
    scale: 0.97;
    translate: 0 1px;
    transition-duration: 80ms;
  }

  .icon:active,
  .mnav summary:active {
    scale: 0.92;
    translate: 0;
  }

  @media (hover: hover) {
    .hosts span:hover {
      border-color: var(--accent);
      color: var(--ink);
    }
  }

  /* Heading links slide in. */
  .doc .anchor {
    translate: -0.25rem 0;
    transition:
      opacity var(--t),
      translate 200ms var(--ease);
  }

  .doc :hover > .anchor,
  .doc .anchor:focus-visible,
  .doc .anchor.done {
    translate: 0;
  }

  /* The search dialog opens from the search field, the backdrop fades. */
  .finder[open] {
    transform-origin: 50% 0;
    animation: open 180ms var(--ease);
  }

  .finder[open]::backdrop {
    animation: veil 180ms var(--ease);
  }

  /* The ghost's happy hop and the Konami spin. */
  .brand .ghost.happy svg,
  .brand:is(:hover, :focus-visible) .ghost.happy svg {
    animation: happy 500ms var(--ease);
  }

  .brand .ghost.spin svg,
  .brand:is(:hover, :focus-visible) .ghost.spin svg {
    animation: spin 700ms var(--ease);
  }

  .hero .logo.happy {
    animation: happy 500ms var(--ease);
  }

  /* A copied code block washes once. */
  pre:has(> .copy-code.done)::after {
    animation: wash 400ms var(--ease);
  }

  /* The copy button's check draws in with a tiny pop. */
  .copy-code.done::before {
    animation:
      draw 260ms var(--ease),
      pop 300ms var(--ease);
  }

  /* Callout icons tilt once as they scroll into view. */
  @supports (animation-timeline: view()) {
    .callout > p:first-child:has(> strong:only-child)::before {
      animation: tilt linear both;
      animation-timeline: view();
      animation-range: entry 10% entry 90%;
    }
  }
}

/* The sidebar's current marker. */
.side li a {
  position: relative;
}

/* The copy wash: a flat accent tint that fades out, over the block. */
pre::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background: color-mix(in oklch, var(--accent) 14%, transparent);
  opacity: 0;
  pointer-events: none;
}

.copy-code.done::before {
  content: "";
  display: inline-block;
  width: 0.9em;
  height: 0.9em;
  margin-right: 0.3em;
  vertical-align: -0.14em;
  background: currentColor;
  mask: var(--i-check) center / contain no-repeat;
}

@keyframes open {
  from {
    opacity: 0;
    scale: 0.97;
    translate: 0 -0.5rem;
  }
}

@keyframes veil {
  from {
    opacity: 0;
  }
}

@keyframes wash {
  from {
    opacity: 1;
  }
}

@keyframes tilt {
  from {
    rotate: -18deg;
    scale: 0.85;
  }
}

@keyframes happy {
  35% {
    transform: translateY(-0.4rem) scale(0.94, 1.1) rotate(-6deg);
  }
  65% {
    transform: translateY(0) scale(1.08, 0.9) rotate(3deg);
  }
}

@keyframes spin {
  to {
    transform: rotate(360deg);
  }
}

/* ---- Print and forced colors ------------------------------------------- */

@media print {
  :root {
    color-scheme: light;
  }

  body {
    display: block;
    background: #fff;
    color: #000;
  }

  .top,
  .side,
  .toc,
  .post-toc,
  .foot,
  .copy-code,
  .useful,
  .pager,
  .skip,
  .nojs,
  .doc .anchor {
    display: none;
  }

  .docs,
  .post-wrap {
    display: block;
  }

  .doc,
  .page {
    width: 100%;
    padding: 0;
    animation: none;
  }

  .doc a[href^="http"]::after {
    content: " (" attr(href) ")";
    color: #000;
    font-size: 0.8em;
    overflow-wrap: anywhere;
  }

  pre {
    break-inside: avoid;
    white-space: pre-wrap;
    overflow: visible;
  }

  h1,
  h2,
  h3,
  h4 {
    break-after: avoid;
  }
}

@media (forced-colors: active) {
  /* Mask icons paint with a color, which forced colors would reset to the page. */
  :root {
    --mask-ink: CanvasText;
  }

  .top:has(~ main .docs)::after {
    forced-color-adjust: none;
    background: Highlight;
  }

  .ghost .shape {
    fill: CanvasText;
  }

  .ghost .eye,
  .ghost .mouth {
    fill: Canvas;
  }

  .btn {
    border: 1px solid ButtonText;
  }

  .tally .meter span {
    forced-color-adjust: none;
    background: CanvasText;
  }

  .tally .us .meter span {
    background: Highlight;
  }
}

@font-face{font-family:"DM Sans";src:url(/fonts/dm-sans-400.woff2) format("woff2");font-weight:400;font-style:normal;font-display:swap}
@font-face{font-family:"DM Sans";src:url(/fonts/dm-sans-500.woff2) format("woff2");font-weight:500;font-style:normal;font-display:swap}
@font-face{font-family:"DM Sans";src:url(/fonts/dm-sans-600.woff2) format("woff2");font-weight:600;font-style:normal;font-display:swap}
@font-face{font-family:"DM Sans";src:url(/fonts/dm-sans-700.woff2) format("woff2");font-weight:700;font-style:normal;font-display:swap}
@font-face{font-family:"JetBrains Mono";src:url(/fonts/jetbrains-mono-400.woff2) format("woff2");font-weight:400;font-style:normal;font-display:swap}
@font-face{font-family:"JetBrains Mono";src:url(/fonts/jetbrains-mono-500.woff2) format("woff2");font-weight:500;font-style:normal;font-display:swap}
@font-face{font-family:"DM Sans Fallback";src:local("Arial");size-adjust:125.05%;ascent-override:79.33%;descent-override:24.79%;line-gap-override:0.00%}
:root{--font-dm-sans:"DM Sans","DM Sans Fallback",sans-serif}
@font-face{font-family:"JetBrains Mono Fallback";src:local("Courier New");size-adjust:100.32%;ascent-override:101.68%;descent-override:29.91%;line-gap-override:0.00%}
:root{--font-jetbrains-mono:"JetBrains Mono","JetBrains Mono Fallback",monospace}

.band .claim h2.w-lkvhqc {
    font-size: clamp(1.75rem, 3.6vw, 2.375rem);
  }
.demo.w-57mieq {
    display: grid;
    gap: 1.5rem;
  }

  @media (min-width: 60rem) {
    .demo.w-57mieq {
      grid-template-columns: 1.15fr 1fr;
    }
  }

  .demo .code.w-57mieq,
  .result.w-57mieq {
    min-width: 0;
    background: var(--code);
    overflow: hidden;
  }

  .demo.w-57mieq pre {
    margin: 0;
    border: 0;
    border-radius: 0;
  }

  .chrome.w-57mieq {
    padding: 0.625rem 1rem;
    border-bottom: 1px solid var(--line);
    color: var(--ash);
    font: 0.8125rem/1.4 var(--mono);
  }

  .result.w-57mieq {
    background: var(--panel);
  }

  .view.w-57mieq {
    padding: 1.25rem 1.5rem 1.5rem;
  }

  .view h3.w-57mieq {
    margin: 0 0 1rem;
    font-size: 1.375rem;
  }

  .view label.w-57mieq {
    display: block;
    margin-bottom: 0.25rem;
    font-size: 0.875rem;
    font-weight: 600;
  }

  .view .row.w-57mieq {
    display: flex;
    gap: 0.5rem;
  }

  .view input[aria-invalid="true"].w-57mieq {
    border-color: var(--warn);
    animation: shake 340ms var(--ease);
  }

  .view form button.w-57mieq,
  .view .remove.w-57mieq {
    padding: 0 1rem;
    border: 1px solid var(--line-strong);
    border-radius: var(--pill);
    background: var(--panel-2);
    color: var(--ink);
    font: 500 var(--fs-sm) var(--sans);
    cursor: pointer;
  }

  .view .remove.w-57mieq {
    height: 2rem;
    padding: 0 0.625rem;
    font-size: 0.8125rem;
  }

  .view .problem.w-57mieq {
    display: block;
    min-height: 1.5em;
    color: var(--warn);
    font-size: 0.875rem;
  }

  .view ul.w-57mieq {
    display: grid;
    gap: 0.5rem;
    margin: 0.5rem 0 0;
    padding: 0;
    list-style: none;
  }

  .view li.w-57mieq {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    padding: 0.5rem 0.5rem 0.5rem 0.875rem;
    border: 1px solid var(--line);
    border-radius: var(--radius);
    transition:
      opacity 300ms var(--ease),
      translate 300ms var(--ease);
  }

  .view li[data-leaving].w-57mieq {
    opacity: 0;
    translate: 0 0.375rem;
  }

  @starting-style {
    .view li.w-57mieq {
      opacity: 0;
      translate: 0 -0.375rem;
    }
  }
.hero.w-ghtvqd {
    padding-block: clamp(3.5rem, 9vw, 6.5rem) 4rem;
    text-align: center;
  }

   
  .hero .stage.w-ghtvqd {
    position: relative;
    width: clamp(5.5rem, 14vw, 8.25rem);
    margin: 0 auto 1.5rem;
    padding-bottom: 1.25rem;
  }

  .hero .stage.w-ghtvqd::after {
    content: "";
    position: absolute;
    inset: auto 18% 0;
    height: 0.5rem;
    border-radius: 50%;
    background: var(--ink);
    opacity: 0.16;
  }

  .hero .logo.w-ghtvqd {
    display: block;
    width: 100%;
    height: auto;
    user-select: none;
    -webkit-user-drag: none;
  }

  .hero h1.w-ghtvqd {
    margin: 0;
    font-size: clamp(3.25rem, 9vw, 4.75rem);
    font-weight: 700;
    letter-spacing: -0.045em;
    line-height: 1;
  }

  .hero .sub.w-ghtvqd {
    margin: 1.25rem 0 0;
    font-size: clamp(1.375rem, 3.4vw, 2rem);
    font-weight: 600;
    line-height: 1.3;
    letter-spacing: -0.02em;
  }

  .hero .lede.w-ghtvqd {
    margin: 0.75rem auto 2.25rem;
    max-width: 36rem;
    color: var(--slate);
    font-size: var(--fs-lede);
  }

  .install.w-ghtvqd {
    display: inline-flex;
    align-items: center;
    gap: 0.25rem;
    max-width: 100%;
    margin-top: 2rem;
    padding: 0.5rem 1.5rem;
    border: 1px solid var(--line);
    border-radius: var(--pill);
    background: var(--panel);
    text-align: left;
  }

  .install code.w-ghtvqd {
    padding: 0;
    background: none;
    overflow-x: auto;
    font: 0.875rem/1.6 var(--mono);
    white-space: nowrap;
    scrollbar-width: none;
  }

  .install .prompt.w-ghtvqd {
    color: var(--ash);
  }

   
  @media (max-width: 30rem) {
    .install.w-ghtvqd {
      border-radius: var(--radius-lg);
      padding-inline: 1rem;
    }

    .install code.w-ghtvqd {
      overflow-wrap: anywhere;
      white-space: normal;
    }
  }

   
  .copy-icon.w-ghtvqd {
    --size: 2.25rem;
    position: relative;
    --fg: var(--ash);
    margin-right: -1rem;
  }

  .copy-icon.w-ghtvqd:active {
    scale: 0.85;
  }

  .copy-icon svg.w-ghtvqd {
    grid-area: 1 / 1;
    width: 1.125rem;
    height: 1.125rem;
    fill: none;
    stroke: currentColor;
    stroke-width: 2;
    stroke-linecap: round;
    stroke-linejoin: round;
    transition:
      opacity var(--t),
      scale 300ms var(--ease);
  }

  .copy-icon .i-done.w-ghtvqd,
  .copy-icon.done .i-copy.w-ghtvqd {
    opacity: 0;
    scale: 0.5;
  }

  .copy-icon.done.w-ghtvqd {
    color: var(--good);
  }

  .copy-icon.done .i-done.w-ghtvqd {
    opacity: 1;
    scale: 1;
  }

  .copy-icon.done .i-done path.w-ghtvqd {
    stroke-dasharray: 20;
    animation: check 350ms var(--ease) 80ms backwards;
  }

   
  .hero em.w-ghtvqd {
    position: relative;
    font-style: normal;
    color: var(--accent-ink);
    white-space: nowrap;
  }

  .hero .scribble.w-ghtvqd {
    position: absolute;
    left: -0.06em;
    bottom: -0.34em;
    width: calc(100% + 0.12em);
    height: auto;
    overflow: visible;
    fill: none;
    stroke: currentColor;
    stroke-width: 4.5;
    stroke-linecap: round;
    stroke-linejoin: round;
    stroke-dasharray: 1;
    pointer-events: none;
  }

  .hero .word > span.w-ghtvqd {
    display: inline-block;
  }

  @media (prefers-reduced-motion: no-preference) {
    .hero .wrap > *.w-ghtvqd {
      animation: fade 500ms var(--ease) backwards;
    }

    .hero .wrap > .w-ghtvqd:nth-child(2) { animation-delay: 60ms; }
    .hero .wrap > .w-ghtvqd:nth-child(3) { animation-delay: 120ms; }
    .hero .wrap > .w-ghtvqd:nth-child(4) { animation-delay: 180ms; }
    .hero .wrap > .w-ghtvqd:nth-child(5) { animation-delay: 240ms; }
    .hero .wrap > .w-ghtvqd:nth-child(6) { animation-delay: 300ms; }

     
    .hero .logo.w-ghtvqd {
      animation:
        fade 500ms var(--ease) backwards,
        hop 1.4s cubic-bezier(0.45, 0, 0.55, 1) 600ms infinite alternate;
    }

    .hero .stage.w-ghtvqd::after {
      animation: shade 1.4s cubic-bezier(0.45, 0, 0.55, 1) 600ms infinite alternate;
    }

     
    .hero h1.w-ghtvqd {
      animation: none;
    }

    .hero .word > span.w-ghtvqd {
      animation: letter 600ms var(--ease) backwards;
    }

    .hero .word > .w-ghtvqd:nth-child(2) { animation-delay: 50ms; }
    .hero .word > .w-ghtvqd:nth-child(3) { animation-delay: 100ms; }
    .hero .word > .w-ghtvqd:nth-child(4) { animation-delay: 150ms; }

     
    .hero .scribble path.w-ghtvqd {
      animation: pen 900ms cubic-bezier(0.65, 0, 0.35, 1) 550ms backwards;
    }
  }

  @keyframes hop {
    to {
      translate: 0 -0.875rem;
    }
  }

  @keyframes shade {
    to {
      scale: 0.7 1;
      opacity: 0.08;
    }
  }

  @keyframes letter {
    from {
      opacity: 0;
      translate: 0 0.4em;
      rotate: -8deg;
    }
  }

  @keyframes pen {
    from {
      stroke-dashoffset: 1;
    }
  }
.hosts.w-b86tfm {
    display: flex;
    flex-wrap: wrap;
    gap: 0.625rem;
  }

  .hosts a.w-b86tfm {
    padding: 0.375rem 1rem;
    border: 1px solid var(--line);
    border-radius: var(--pill);
    background: var(--panel);
    color: var(--slate);
    font-size: var(--fs-sm);
    text-decoration: none;
    transition: color var(--t), border-color var(--t);
  }

  .hosts a.w-b86tfm:hover,
  .hosts a.w-b86tfm:focus-visible {
    border-color: var(--accent);
    color: var(--ink);
  }