/*
 * knobs download page: layout tokens, the components and the bands. The
 * faces and the reset are in base.css; colours, type stacks, geometry and
 * motion are tokens in tokens.css.
 *
 * The bands alternate graphite and the deep ground: the hero (with the
 * header on it) on graphite, Download for Windows deep, the steps with it,
 * How it works on graphite, Benchmarks deep, Questions on graphite,
 * Get knobs deep, then the footer's band in the buttons' red. In light
 * they alternate a cool pale grey and a step deeper, with white rows and
 * panels.
 *
 * The red caps on a grey mixing desk: cap red (--action) is the action
 * colour and the colour of everything that is knobs, and grounds, text and
 * lines stay neutral, so the red has something to sing against.
 */

:root {
  /* Layout */
  --content: 77rem;        /* the content grid: 1232px at the default size */
  --narrow: 48rem;         /* Download for Windows' column */
  --gutter: clamp(16px, 5vw, 64px);
  --measure: 34rem;        /* about 68 characters of Radio Canada */
  --head-col: 20rem;       /* a row's term or claim, beside its text or chart */
  --col-gap: clamp(40px, 4.5vw, 64px);
  --band-top: clamp(64px, 7vw, 104px);
  --band-bottom: clamp(64px, 6vw, 96px);
  --head-gap: clamp(32px, 3.5vw, 56px);

  /* Type sizes. The headline is 84px at 1440. */
  --display: clamp(2.25rem, 5.84vw, 5.5rem);
  --title: clamp(2.125rem, 1.25rem + 2.2vw, 3.25rem);
  --lede: 1.125rem;
  --text-md: 0.9375rem;
  --text-sm: 0.875rem;
  --text-xs: 0.8125rem;

  /* The big button's height, in every release state. */
  --btn-hero: 64px;

  /* Which ground each band takes. The header sits on the hero's. */
  --hero-ground: var(--ground);
  --band-ground: var(--ground);
  --band-ground-deep: var(--ground-deep);

  /* A cap dot, the page's one graphic device: a red knob cap seen from
     above, marking the items of a list. An odd number of pixels, so it
     centres on a 1px rule, and it grows with the visitor's text. */
  --dot: 9px;
}
@supports (width: round(down, 1px, 2px)) {
  :root { --dot: max(9px, round(down, 0.5625rem, 2px) + 1px); }
}

/* ---------- Browser surfaces ---------- */

/* The header doesn't follow the page, so a link to a section lands on the
   section's own band. Links jump rather than glide: a smooth scroll on
   arrival would still be running when the release arrives and fills the
   rows, and would stop short of its target. */
html { scroll-padding-top: 0; }
body { background: var(--ground); caret-color: var(--action); }

/* A link is its words on a cap-red underline: red marks what can be
   pressed, and the underline, not the colour alone, says it's a link.
   Under the pointer the words turn red too. */
a {
  text-decoration-line: underline;
  text-decoration-color: var(--action);
  transition: text-decoration-color var(--duration-fast) var(--ease), color var(--duration-fast) var(--ease);
}
:is(.preview-strip, .hero-more, .hero-needs, .rs-line, .dl-meta, .about-block, .build-body, .step-links, .more, .sp-note, .footnote, .footer-legal) a:hover { color: var(--action-text); }
/* Pressed, a link or a disclosure dims a step, as a button sinks. */
a:active, summary:active { opacity: 0.72; }

code, .mono { font-size: 0.875em; }
p code, dd code, li > code { white-space: nowrap; }
b, strong { font-weight: 600; color: var(--text); }
.ui { font-weight: 600; color: var(--text); }
.figure { font-weight: 600; color: var(--text); font-variant-numeric: tabular-nums; }

/* A word too long for its line (very large text in a narrow window) breaks
   rather than running off the page. */
h1, h2, h3, h4 { font-weight: 600; text-wrap: balance; overflow-wrap: break-word; }
p, dd { text-wrap: pretty; }

.sprite { position: absolute; width: 0; height: 0; overflow: hidden; }
/* 16px beside text at the default size, growing with the visitor's text
   size like the words beside them. */
.icon {
  display: inline-block;
  flex: none;
  width: 1rem;
  height: 1rem;
  vertical-align: -0.1875rem;
}
/* The external arrow after a link's last word. Its drawing sits inside the
   16px box, so the box itself nearly touches the word. */
.icon-ext { margin-left: 1px; }

.wrap {
  width: 100%;
  max-width: calc(var(--content) + 2 * var(--gutter));
  margin-inline: auto;
  padding-inline: var(--gutter);
}
/* Download for Windows keeps its narrow measure, on the left edge every
   section's title starts on. */
.wrap-narrow > * { max-width: var(--narrow); }

/* Two skip links, to the content and to Download for Windows. Each waits
   above the top edge by its own height plus its offset, so it stays out of
   sight however large the visitor's text is. */
.skip-link {
  position: absolute;
  left: 12px;
  top: 12px;
  z-index: 10;
  padding: 10px 16px;
  border-radius: var(--radius);
  background: var(--action);
  color: var(--on-action);
  font-weight: 500;
  text-decoration: none;
  transform: translateY(calc(-100% - 16px));
}
.skip-link:focus { transform: none; }

.if-js { display: none; }
.js .if-js { display: inline; }
.js .if-nojs { display: none; }

/* ---------- Buttons ---------- */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  min-height: 44px;
  padding: 0 20px;
  border: 1px solid transparent;
  border-radius: var(--radius);
  font: 500 var(--text-md)/1.2 var(--font-sans);
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
  transition: background-color var(--duration-fast) var(--ease), border-color var(--duration-fast) var(--ease), color var(--duration-fast) var(--ease);
}
.btn[hidden] { display: none; }
/* Buttons are red caps. The primary one is a solid cap, its words graphite
   in dark and white in light; a secondary one is the cap's outline, and
   fills under the pointer. Neither is ever graphite or black on the light
   ground. */
.btn-primary {
  background: var(--action);
  border-color: var(--action);
  color: var(--on-action);
}
.btn-primary:hover { background: var(--action-hover); border-color: var(--action-hover); }
.btn-secondary {
  background: transparent;
  border-color: var(--action);
  color: var(--action-text);
}
.btn-secondary:hover { background: var(--action); border-color: var(--action); color: var(--on-action); }
.btn:active { opacity: 1; transform: translateY(1px); }

/* Buttons that share a row line up by their tops, so a taller one never
   stretches its neighbours. */
.actions {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  gap: 12px;
}

/* The hero's big button, 64px tall in every release state, so an answer
   arriving never moves what's under it. */
.btn-hero {
  min-height: var(--btn-hero);
  padding: 0 28px;
  font-size: 1.125rem;
}
.btn-hero.btn-primary { min-width: 17.5rem; }
.btn-download {
  justify-content: flex-start;
  gap: 14px;
  padding: 9px 32px 9px 24px;
  white-space: normal;
  text-align: left;
}
.btn-download .icon { width: 1.25rem; height: 1.25rem; }
.btn-download-text { display: grid; gap: 2px; min-width: 0; }
.btn-download-label { font-size: 1.125rem; font-weight: 600; line-height: 1.25; }
/* The second line steps down by size, not by fading: on the red a faded
   line would fall under 4.5:1. It keeps the button's 500, as every small
   word on a red button does: white on the light scheme's red is 4.5:1 with
   no margin, and a regular stroke that thin reads lighter than that. */
.btn-download-sub {
  font-size: var(--text-sm);
  font-weight: 500;
  line-height: 1.35;
  font-variant-numeric: tabular-nums;
}

.tag {
  display: inline-flex;
  align-items: center;
  min-height: 22px;
  padding: 0 7px;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius);
  color: var(--text-2);
  font: 500 0.75rem/1 var(--font-sans);
  letter-spacing: 0.01em;
  white-space: nowrap;
}
.tag[hidden] { display: none; }
/* "Recommended" marks knobs's own main download, so it takes the red. A
   pre-release stays a plain grey tag: red never warns. */
.file-title .tag { border-color: var(--action); color: var(--action-text); }

/* Copy: a quiet outlined button that turns red under the pointer, like
   every control. Copied, its icon turns to the check, in ink: red never
   stands for success or failure. */
.copy {
  display: inline-flex;
  flex: none;
  align-items: center;
  justify-content: center;
  gap: 6px;
  min-width: 5.75rem;
  min-height: 32px;
  padding: 0 10px;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius);
  background: transparent;
  color: var(--text-2);
  font: 500 var(--text-xs)/1 var(--font-sans);
  cursor: pointer;
  transition: color var(--duration-fast) var(--ease), border-color var(--duration-fast) var(--ease), background-color var(--duration-fast) var(--ease);
}
.copy:hover { color: var(--action-text); border-color: var(--action); }
.copy:active { transform: translateY(1px); }
.copy .i-done { display: none; }
.copy.is-copied { color: var(--text); border-color: var(--text-2); }
.copy.is-copied .i-copy { display: none; }
.copy.is-copied .i-done { display: inline-block; animation: settle var(--duration-fast) var(--ease); }

@keyframes settle {
  from { opacity: 0; transform: translateY(-4px); }
}

/* A chevron says a control opens something in place, and turns down while
   it's open. It stands centred on its words' first line, however many
   lines they take: its summary lines them up by their tops and pads a
   single line to the summary's height. */
.chev {
  color: var(--text-3);
  transition: transform var(--duration-fast) var(--ease), color var(--duration-fast) var(--ease);
}
summary > .chev {
  margin-top: calc((1.55em - 1rem) / 2);
  margin-top: calc((1lh - 1rem) / 2);
}
details[open] > summary .chev { transform: rotate(90deg); }
summary { list-style: none; }
summary::-webkit-details-marker { display: none; }

/* ---------- Preview strip ---------- */

.preview-strip {
  display: none;
  padding-block: 8px;
  border-bottom: 1px solid var(--line);
  background: var(--ground-deep);
  color: var(--text-2);
  font-size: var(--text-xs);
}
.is-preview .preview-strip { display: block; }
.preview-strip-inner {
  display: flex;
  align-items: center;
  gap: 10px;
  min-height: 24px;
}
.preview-strip .icon { color: var(--text); }
.preview-strip p { flex: 1; }
.preview-strip a { color: var(--text); white-space: nowrap; }

/* ---------- Header, on the hero's band ---------- */

.site-header { background: var(--hero-ground); }
.site-header-inner {
  display: flex;
  align-items: center;
  gap: 12px 40px;
  min-height: 64px;
}
/* The lockup at 32px tall (140px wide), its clear space kept by the
   header's padding and the gap to the links. Both colour lockups are in
   the markup; the one for the scheme on screen shows (see "Light and
   dark" below). */
.brand { display: block; flex: none; }
.brand img { width: auto; height: 32px; }

.site-nav ul { display: flex; gap: 4px; margin: 0 -12px; padding: 0; list-style: none; }
.site-nav a {
  display: block;
  padding: 8px 12px;
  color: var(--text-2);
  font-size: var(--text-md);
  text-decoration: none;
  white-space: nowrap;
  transition: color var(--duration-fast) var(--ease);
}
.site-nav a:hover { color: var(--action-text); }

/* Source and the light/dark toggle, together at the header's end, 38px
   tall (44px on a touch screen). With large text they grow with it, so the
   toggle's icon always keeps room around it. */
.header-tools {
  --tool: max(38px, 1.125rem + 12px);
  display: flex;
  flex: none;
  align-items: center;
  gap: 8px;
  margin-left: auto;
}
.source-link {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  min-height: var(--tool);
  padding: 0 14px;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius);
  color: var(--text);
  font: 500 var(--text-sm)/1 var(--font-sans);
  text-decoration: none;
  transition: border-color var(--duration-fast) var(--ease), color var(--duration-fast) var(--ease);
}
.source-link:hover { border-color: var(--action); color: var(--action-text); }
/* GitHub's mark keeps GitHub's colours: it never turns red. */
.source-link .icon { color: var(--text); }
.source-link:active { opacity: 1; transform: translateY(1px); }

/* The light/dark toggle: a square icon button the height of Source, its
   sun or moon in ink, red under the pointer like every control. It needs
   theme.js, so without JavaScript it isn't there and the page stays dark.
   In forced colours the visitor's own colours rule, so it goes. */
.theme-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--tool);
  height: var(--tool);
  padding: 0;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius);
  background: transparent;
  color: var(--text);
  cursor: pointer;
  transition: border-color var(--duration-fast) var(--ease), color var(--duration-fast) var(--ease);
}
.theme-toggle:hover { border-color: var(--action); color: var(--action-text); }
.theme-toggle:active { transform: translateY(1px); }
.theme-toggle .icon { width: 1.125rem; height: 1.125rem; }
.no-js .theme-toggle { display: none; }

/* ---------- Light and dark ---------- */

/* Each scheme shows its own lockup in the header and its own icon and name
   on the toggle: the sun and "Switch to light mode" while dark is showing,
   the moon and "Switch to dark mode" while light is. Light shows only when
   the visitor chose it: each page's <html> carries data-theme="dark" until
   then, so the system's scheme never picks tokens.css's light block. */
.lockup-on-light,
.tt-moon,
.tt-to-dark { display: none; }
:root[data-theme="light"] .lockup-on-dark,
:root[data-theme="light"] .tt-sun,
:root[data-theme="light"] .tt-to-light { display: none; }
:root[data-theme="light"] .lockup-on-light { display: block; }
:root[data-theme="light"] .tt-moon { display: inline-block; }
:root[data-theme="light"] .tt-to-dark { display: inline; }

/* ---------- Hero ---------- */

/* The pitch on the left, the video on the right, centred on each other. */
.hero {
  padding-block: clamp(24px, 2vw, 28px) clamp(48px, 3.4vw, 52px);
  background: var(--hero-ground);
}
.hero-grid {
  display: grid;
  grid-template-columns: minmax(0, 33fr) minmax(0, 40fr);
  column-gap: var(--col-gap);
  align-items: center;
}
.pitch { min-width: 0; }
.pitch h1 {
  color: var(--text);
  font: 700 var(--display)/0.9 var(--font-display);
  letter-spacing: -0.01em;
  text-wrap: wrap;
}
/* Each piece of the headline breaks as a whole, so the lines always end at
   "OBS", at the comma or at the full stop. A piece too wide for its line
   (very large text) wraps inside itself rather than running off. */
.hl { display: inline-block; }
/* In forced colours every line gets its own backplate, and at a line-height
   under 1 the next line's covers the tail of the comma above it, so the
   comma reads as a full stop. The extra class outranks the narrower
   screens' line-height below. */
@media (forced-colors: active) {
  .hero .pitch h1 { line-height: 1; }
}
.lede {
  max-width: var(--measure);
  margin-top: 22px;
  color: var(--text-2);
  font-size: var(--lede);
  line-height: 1.5;
}

/* The release's states. Only the active one takes room, so a stack is as
   tall as what it holds. An answer from GitHub settles in, and page.js
   moves the stack from the old height to the new one on the same curve,
   clipped below so the arriving state never spills. Without JavaScript only
   the static state shows. */
[data-release] { container-type: inline-size; }
[data-release] > .rs:not(.is-active) { display: none; }
[data-release].is-sizing {
  clip-path: inset(-12px -12px 0 -12px);
  transition: height var(--duration-slow) var(--ease);
}
[data-release-state="ready"] [data-release] > .rs.is-active,
[data-release-state="error"] [data-release] > .rs.is-active {
  animation: settle var(--duration-slow) var(--ease);
}

.get { margin-top: 30px; }
/* Where the stack can't hold Releases on GitHub and Try again side by side
   (on phones, and in a narrow desktop column), Try again goes under it at a
   plain button's height and type, as wide as the big one, so the big one
   stays the one to press. */
@container (max-width: 26.5rem) {
  .get .actions { display: grid; justify-content: start; }
  .get .btn-hero.btn-secondary {
    min-height: 44px;
    padding: 0 20px;
    font-size: var(--text-md);
  }
}
/* One quiet line with the big button in every state: where the file comes
   from, or that GitHub is being asked. It stands beside the button where
   the stack holds both, so every state is the button's own height, and
   goes under it where it doesn't. The source's address is never broken:
   the whole line moves rather than wrap inside it. Why there's no release
   goes under the buttons, as far under them as the line goes under the
   button in the other states. */
.rs-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px 20px;
}
.rs-line {
  max-width: var(--measure);
  margin-top: 10px;
  color: var(--text-3);
  font-size: var(--text-sm);
  line-height: 1.5;
}
.rs-row > .rs-line {
  flex: 1 1 auto;
  margin: 0;
}
/* Where the stack can't hold the download and its whole source line side
   by side (the hero's column never can), the line goes under the button in
   every state, so the states stay level with each other. */
@container (max-width: 41rem) {
  .rs-row > .rs-line { flex-basis: 100%; }
}
.rs-line .rs-head { color: var(--text-2); }
.rs-line [hidden] { display: none; }
/* The source line, here and in Download for Windows: GitHub's mark stays
   on the first line when a narrow column puts the address under "From". */
.dest, .dl-from {
  display: flex;
  align-items: flex-start;
  gap: 8px;
}
.dest .icon, .dl-from .icon { margin-top: 0.125rem; }
/* A pre-release tag keeps 8px to "From", the space after it included (a
   space is a quarter of the line's size). */
.dest .tag { margin-right: calc(8px - var(--text-sm) / 4); vertical-align: 1px; }
.dest .mono, .dl-from .mono { color: var(--text-2); font-size: 0.9286em; white-space: nowrap; }
/* A <wbr> breaks even inside nowrap, so the address's own break points
   wait until its line can't hold it whole (see Responsive). */
.dest wbr, .dl-from wbr { display: none; }

.hero-more {
  margin-top: 18px;
  font-size: 1rem;
  font-weight: 500;
}
.hero-more a { color: var(--text); }
/* In the error states the rows aren't there and the hero already offers
   the releases page, so the link would only lead back to the same error.
   The Needs line then keeps the link's distance from the state above it. */
[data-release-state="error"] .hero-more { display: none; }
.hero-needs {
  max-width: var(--measure);
  margin-top: 6px;
  color: var(--text-2);
  font-size: var(--text-sm);
  line-height: 1.5;
}
[data-release-state="error"] .hero-needs,
.hero-more[hidden] + .hero-needs { margin-top: 18px; }
.hero-needs a { color: var(--text); }

/* The reel: 16:9, square, with no frame. Its edges are graphite, so on the
   dark ground it has no edge at all, and on the light one it stands as a
   graphite panel of its own. Nothing sits over it: its controls are in a
   row under it, on the right. */
.video {
  display: grid;
  gap: 14px;
  margin: 0;
  min-width: 0;
}
.video > .video-controls { justify-self: end; }
.video-frame {
  position: relative;
  aspect-ratio: 16 / 9;
  overflow: hidden;
  border-radius: var(--radius);
  background: var(--brand-graphite);
}
/* While the reel is away in the expanded view, its empty place takes the
   page's ground, so no bare panel shows past it on the dimmed page. */
.video-frame.is-away { background: none; }
.video-frame > video {
  position: absolute;
  inset: 0;
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
/* The video's own controls, drawn by page.js: Pause and Play (WCAG 2.2.2),
   the sound, and Expand. Square and outlined in label type, as the
   header's tools are, on the page's own ground; under the pointer the edge
   and the words light red, as a cap does. Pause and Play say their word;
   the sound and Expand are their icons alone, as the header's light and
   dark toggle is. */
.video-controls {
  display: flex;
  gap: 6px;
}
.video-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  min-width: var(--video-btn);
  min-height: var(--video-btn);
  padding: 0 12px 0 10px;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius);
  background: var(--ground);
  color: var(--text);
  font: 500 var(--text-xs)/1 var(--font-sans);
  cursor: pointer;
  transition: border-color var(--duration-fast) var(--ease), color var(--duration-fast) var(--ease);
}
.video-btn.is-icon { padding: 0; }
.video-btn[hidden] { display: none; }
button.video-btn:hover { border-color: var(--action); color: var(--action-text); }
button.video-btn:active { transform: translateY(1px); }
:root { --video-btn: 36px; }

/* Expand: the video large, in a modal dialog page.js builds, with the
   page still there behind it, dimmed under graphite in both schemes (the
   brand's own dark, as a room's lights go down), and a margin all round.
   The same <video> moves into it, so it plays on without a break, and
   moves back on Minimize, Esc or a click on the dimmed page. Its controls
   go with it, under it on the right, as they sit under the reel, Expand
   turned into Minimize in the same place. On the dimmed page they take the
   dark scheme's roles, in both schemes. The page behind stays where it
   was. */
.video-focus {
  color-scheme: dark;
  --ground: var(--brand-graphite);
  --text: var(--brand-ink);
  --line-strong: var(--neutral-dark-375);
  --action: var(--brand-cap-red);
  --action-text: var(--brand-cap-red);
  --focus: var(--brand-cap-red);
  --pad: clamp(24px, 5vw, 80px);
  --row: calc(var(--video-btn) + 12px);
  position: fixed;
  inset: 0;
  width: 100%;
  height: 100%;
  max-width: none;
  max-height: none;
  margin: 0;
  padding: var(--pad);
  border: 0;
  background: var(--brand-graphite);
  background: color-mix(in srgb, var(--brand-graphite) 80%, transparent);
  color: var(--text);
  overflow: hidden;
}
.video-focus[open] {
  display: grid;
  place-items: center;
  animation: video-fade var(--duration-fast) var(--ease);
}
.video-focus::backdrop { background: transparent; }
@keyframes video-fade {
  from { opacity: 0; }
}
/* As wide as the window allows at 16:9 inside the margin, with a row for
   the controls below. */
.video-focus-body {
  display: grid;
  row-gap: 12px;
  width: min(100%, calc((100vh - 2 * var(--pad) - var(--row)) * 16 / 9));
  width: min(100%, calc((100dvh - 2 * var(--pad) - var(--row)) * 16 / 9));
  animation: settle var(--duration-fast) var(--ease);
}
.video-focus-body > .video-frame { width: 100%; }
.video-focus-body > .video-controls { justify-self: end; }
html:has(.video-focus[open]) { overflow: hidden; }

/* ---------- Bands ---------- */

.band {
  --band-bg: var(--band-ground);
  padding-block: var(--band-top) var(--band-bottom);
  background: var(--band-bg);
}
.band-deep { --band-bg: var(--band-ground-deep); }

/* Section titles in the condensed display face, each set left on the
   left edge of what it heads. On the full content grid that's the edge
   the hero's headline starts on, so the eye follows one line down the
   page. The face sets a straight stem 0.07em in from its edge and a round
   letter about 0.03em in, where the headline's first letter starts at
   none, so a title hangs 0.05em into the margin: its stems then stand on
   the edge and its rounds just over it, as round letters should. */
.title {
  margin-left: -0.05em;
  color: var(--text);
  font: 700 var(--title)/1.02 var(--font-display);
  letter-spacing: -0.005em;
}
.section-head { margin-bottom: var(--head-gap); }
.section-head .more {
  margin-top: 14px;
  color: var(--text-2);
  font-size: var(--text-md);
}
.more a { color: inherit; }
.more a:hover { color: var(--action-text); }

/* ---------- Download for Windows ---------- */

.dl { margin-top: 16px; container-name: dl; }
/* "Current version: 0.3.0 · October 13, 2026 | Release notes" */
.dl-meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 4px 14px;
  color: var(--text-3);
  font-size: var(--text-md);
}
/* 10px from the version, the space before the tag included. */
.dl-version .tag { margin-left: calc(10px - var(--text-md) / 4); vertical-align: 1px; }
.dl-version [hidden], .dl-meta > [hidden] { display: none; }
.dl-date { color: var(--text-2); font-variant-numeric: tabular-nums; }
.dl-dot { color: var(--text-3); }
.dl-bar { align-self: stretch; width: 1px; margin-block: 3px; background: var(--line-strong); }
.dl-meta a { color: var(--text); }
/* Where the line can't hold the version, the date and the notes, the notes
   take a line of their own and the bar between them goes. */
@container dl (max-width: 28rem) {
  .dl-bar { display: none; }
  .dl-meta a { flex-basis: 100%; }
}
.dl-from {
  margin-top: 6px;
  color: var(--text-3);
  font-size: var(--text-sm);
  line-height: 1.5;
}

/* A row per file. The recommended download takes a 2px cap-red edge, the
   others a hairline edge. Every file that runs knobs has an outlined red
   Download; the checksums file, a quiet link. The rows stand a step
   lighter than their band (white on the grey in light). */
/* The rows' container queries ask the stack (dl), not the list: a size
   container shown as an answer arrives stops the browser's scroll
   anchoring, and a visitor who arrived at a later section would be pushed
   down the page. */
.files {
  display: grid;
  gap: 16px;
  margin-top: 28px;
  scroll-margin-top: 24px;
}
.files[hidden] { display: none; }
.file {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  column-gap: 32px;
  align-items: center;
  padding: 24px 28px 18px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--raised);
}
.file.is-primary {
  border-color: var(--action);
  /* The edge's second pixel, inside the first: the row keeps its size. */
  box-shadow: inset 0 0 0 1px var(--action);
}
.file-main { grid-column: 1; grid-row: 1; min-width: 0; }
.file-dl { grid-column: 2; grid-row: 1; min-width: 9rem; }
.file-check { grid-column: 1 / -1; grid-row: 2; margin-top: 10px; }

.file-title {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 4px 12px;
  color: var(--text);
  font-size: 1.25rem;
  line-height: 1.3;
  letter-spacing: -0.005em;
}
.file-desc {
  max-width: var(--measure);
  margin-top: 6px;
  color: var(--text-2);
  font-size: var(--text-md);
  line-height: 1.5;
}
.file-desc code { color: var(--text); }
.file-meta {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  margin-top: 10px;
  color: var(--text-3);
  font-size: var(--text-sm);
}
.file-name { color: var(--text-2); font-size: 0.9286em; overflow-wrap: anywhere; }
/* The size keeps its dot when it wraps under a long name. */
.file-size { white-space: nowrap; font-variant-numeric: tabular-nums; }
.file-size .sep { margin: 0 8px; }

/* The checksums file: a slimmer row, with no checksum of its own. It's
   there to check the others, so its Download is a quiet link in the
   buttons' place rather than a third red button. */
.file.is-checksums { padding-block: 18px; }
.file.is-checksums .file-title { font-size: 1rem; }
.file.is-checksums .file-desc { margin-top: 2px; }
.file.is-checksums .file-meta { margin-top: 6px; }
.file-dl.file-link {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  justify-self: end;
  min-width: 0;
  color: var(--text-2);
  font-size: var(--text-md);
  font-weight: 500;
}
.file-link:hover { color: var(--action-text); }

/* "Checksum (SHA-256)", one disclosure per file: its chevron turns down while
   it's open, and the SHA-256 and the PowerShell check open under it. */
.file-check > summary {
  --summary-h: 32px;
  display: inline-flex;
  align-items: flex-start;
  gap: 6px;
  min-height: var(--summary-h);
  margin-left: -3px;
  padding: 0 6px 0 0;
  padding-block: calc((var(--summary-h) - 1.55em) / 2);
  padding-block: calc((var(--summary-h) - 1lh) / 2);
  border-radius: var(--radius);
  color: var(--text-2);
  font-size: var(--text-sm);
  cursor: pointer;
  transition: color var(--duration-fast) var(--ease);
}
.file-check > summary:hover { color: var(--action-text); }
.file-check > summary:hover .chev { color: var(--action); }
.file-check[open] > .check { animation: settle var(--duration-fast) var(--ease); }
.check { margin-top: 10px; }
.check + .check { margin-top: 14px; }
.check-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  min-height: 32px;
  color: var(--text-3);
  font-size: var(--text-xs);
  line-height: 1.45;
}
.check-head > span { text-wrap: balance; }
.check-value {
  display: block;
  margin-top: 6px;
  padding: 9px 12px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--sunken);
  color: var(--text);
  font-size: var(--text-xs);
  line-height: 1.55;
  overflow-wrap: anywhere;
}
/* A hash runs on one line where the row has room, and otherwise wraps in
   even rows of 32 characters, or 16 on the narrowest, so it can be
   compared in chunks. */
.hash-value { display: block; max-width: 32.5ch; }
@container dl (min-width: 38em) {
  .hash-value { max-width: none; }
}
@container dl (max-width: 17.5em) {
  .hash-value { max-width: 16.5ch; }
}
/* In the command the hash runs on from "-eq", as a terminal would wrap it. */
.cmd-hash { word-break: break-all; }
/* After the command: what its answer means, and what to do about a False. */
.check-after {
  max-width: var(--measure);
  margin-top: 8px;
  color: var(--text-3);
  font-size: var(--text-xs);
  line-height: 1.45;
}
.check-after a { color: var(--text-2); }
.check-after a:hover { color: var(--action-text); }

/* Before GitHub answers, without JavaScript, or when there's no release to
   show: a row-like panel with the state's words and its buttons. */
.dl-note {
  padding: 24px 28px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--raised);
}
.dl-note[hidden] { display: none; }
[data-when="ready"] > .dl-note { margin-top: 28px; }
/* Before an answer, or in its place, the panel stands 28px under the
   title, as the rows stand under the release's lines. */
.dl > .rs > .dl-note:first-child { margin-top: 12px; }
.dl-note-head { color: var(--text); font-weight: 600; }
.dl-note-text { max-width: var(--measure); color: var(--text-2); }
.dl-note-head + .dl-note-text { margin-top: 4px; }
.dl-note-text [hidden] { display: none; }
.dl-note .actions { margin-top: 18px; }

/* About these downloads, under the rows. */
.about { margin-top: clamp(48px, 5vw, 64px); }
.about-title {
  color: var(--text);
  font-size: 1.375rem;
  line-height: 1.3;
}
.about-block { margin-top: 24px; }
.about-block h4 {
  color: var(--text);
  font-size: 1rem;
  line-height: 1.4;
}
.about-block > p { margin-top: 8px; color: var(--text-2); }
.about-block > p a { color: var(--text); }
.reqs {
  margin-top: 10px;
  border-top: 1px solid var(--line);
}
.reqs > div {
  display: grid;
  grid-template-columns: 8.5rem minmax(0, 1fr);
  column-gap: 16px;
  padding: 10px 0;
  border-bottom: 1px solid var(--line);
}
.reqs dt { color: var(--text-3); }
/* What knobs needs, each marked by a cap dot. */
.reqs dt::before {
  content: "";
  display: inline-block;
  width: var(--dot);
  height: var(--dot);
  margin-right: 10px;
  border-radius: 50%;
  background: var(--action);
  vertical-align: 0.0625em;
}
.reqs dd { margin: 0; color: var(--text); }
.reqs dd a { color: var(--text); }
/* The requirements already say Windows, so the line that says knobs runs
   on nothing else is only for visitors not on Windows. */
.about-block > .about-note { display: none; margin-top: 12px; color: var(--text-2); }
.not-windows .about-block > .about-note { display: block; }

/* "Build from source": a disclosure on its own ruled row, opened in place.
   Without JavaScript it works the same. */
.build {
  margin-top: 32px;
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
}
.build > summary {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  min-height: 56px;
  padding-block: 16px;
  padding-block: calc((56px - 1lh) / 2);
  color: var(--text);
  font-weight: 600;
  cursor: pointer;
  transition: color var(--duration-fast) var(--ease);
}
.build > summary:hover { color: var(--action-text); }
.build > summary:hover .chev { color: var(--action); }
.build-cost { color: var(--text-3); font-weight: 400; }
.build-body {
  padding-bottom: 24px;
  /* A link to #build lands here; the room keeps "Build from source" in
     view above the steps it opened. */
  scroll-margin-top: 80px;
  color: var(--text-2);
  font-size: var(--text-md);
  line-height: 1.5;
}
.build[open] > .build-body { animation: settle var(--duration-fast) var(--ease); }
.build-body > p { max-width: var(--measure); }
.build-body > * + * { margin-top: 12px; }
.build-body a, .build-body code { color: var(--text); }
/* The exe's path wraps only after its backslashes, as a narrow column or
   large text needs. */
.build-body .exe-path { white-space: normal; }

/* The build steps are the commands' container, so in a narrow column the
   well itself can change: one column, with Copy under the commands at the
   right. */
.build-body { container: code / inline-size; }

/* The copy button has a column of its own, so no command ever runs under
   it. Commands wrap in theirs: between words and after the URL's slashes,
   continued under a hanging indent. The commands sit on the band itself,
   as the file rows do, so they take the rows' tone (white in light). */
.code {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: start;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--raised);
}
.code pre {
  margin: 0;
  padding: 14px 0 14px 16px;
  overflow-x: auto;
  color: var(--text);
  font-size: var(--text-xs);
  line-height: 1.6;
  white-space: pre-wrap;
}
/* A grid drops the line breaks between the commands from the layout, so
   each command's own block makes its line. They stay in the text, which is
   what Copy copies. */
.code pre code { display: grid; font-size: 1em; }
.code .cmd { padding-left: 2ch; text-indent: -2ch; }
.code .copy { margin: 10px 10px 10px 12px; }
@container code (max-width: 30em) {
  .code { grid-template-columns: minmax(0, 1fr); }
  .code pre { padding: 14px 16px 12px; }
  .code .copy { justify-self: end; margin: 0 10px 10px; }
}
/* With very large text in a narrow column, the URL also breaks after
   "https://", an option's value may go under it, and the indent narrows,
   so no line runs out of the well. */
.code .wbr-tight { display: none; }
@container code (max-width: 12.5em) {
  .code .wbr-tight { display: inline; }
  .code .pair { white-space: normal; }
  .code .cmd { padding-left: 1ch; text-indent: -1ch; }
}
/* Copy needs page.js, so without JavaScript there's no button and the
   commands take the whole well, to be selected by hand. */
.no-js .code { grid-template-columns: minmax(0, 1fr); }
.no-js .code pre { padding: 14px 16px; }
.no-js .code .copy { display: none; }

/* On a touch screen the quiet Download keeps a 44px target, and the issue
   link gets the one every link inside a sentence does (see Touch below). */
@media (pointer: coarse) {
  .file-dl.file-link { min-height: 44px; }
}

/* ---------- Get knobs, where the page ends ---------- */

/* The hero's stack once more, after Questions, for a reader who got this
   far: a short title over the same button, on a band of the deep ground,
   the downloads' ground, between Questions and the footer, and on the
   left edge every section's title starts on. Its title stands a step
   under the sections' own, so it reads as the page's close rather than a
   second hero. It keeps the page's buttons and the hero's rule off
   Windows: the note above the button. */
.get-end { padding-block: clamp(56px, 6vw, 88px); }
.get-end-title {
  margin-left: -0.05em;  /* hung as the section titles are */
  color: var(--text);
  font: 700 clamp(1.875rem, 1.25rem + 1.4vw, 2.5rem)/1.02 var(--font-display);
  letter-spacing: -0.005em;
}
.get-end .get { margin-top: 24px; }

/* ---------- Set up in four steps ---------- */

/* Four steps in four columns, each opening on a numbered circle on one
   grey rule. Step 3, getting knobs running, is the one red cap: the others
   are quiet outlined circles, so the eye goes to the download step. The
   band follows Download for Windows on the same ground, so it opens a
   little closer under it.
   Every 1px line that meets a node runs through its centre: the node is
   an odd number of pixels, so its centre is a pixel's centre. */
.setup { padding-top: clamp(32px, 3.5vw, 56px); }
.steps {
  --node: max(33px, 2.0625rem);
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  column-gap: var(--col-gap);
  margin: 0;
  padding: 0;
  list-style: none;
}
@supports (width: round(down, 1px, 2px)) {
  .steps { --node: max(33px, round(down, 2.0625rem, 2px) + 1px); }
}
.step { position: relative; min-width: 0; scroll-margin-top: 32px; }
/* The rule, from each node's centre across its column and the gap to the
   next node, which covers the line's end. */
.step:not(:last-child)::before {
  content: "";
  position: absolute;
  top: calc((var(--node) - 1px) / 2);
  left: calc(var(--node) / 2);
  right: calc(var(--col-gap) * -1);
  border-top: 1px solid var(--line-strong);
}
/* The figure is centred by its own height, from the baseline to the cap
   height, where text-box is supported; elsewhere the line box fills the
   circle and centres it. */
.node {
  position: relative;
  z-index: 1;
  display: block;
  align-content: center;
  width: var(--node);
  height: var(--node);
  border: 1px solid var(--line-strong);
  border-radius: 50%;
  background: var(--band-bg, var(--ground));
  color: var(--text-2);
  font: 600 0.875rem/calc(var(--node) - 2px) var(--font-sans);
  font-variant-numeric: tabular-nums;
  text-align: center;
  text-box: trim-both cap alphabetic;
}
.step-get > .node {
  border-color: var(--action);
  background: var(--action);
  color: var(--on-action);
}
.step h3 {
  margin-top: 18px;
  color: var(--text);
  font-size: 1.1875rem;
  line-height: 1.3;
  letter-spacing: -0.005em;
}
.step p {
  max-width: var(--measure);
  margin-top: 8px;
  color: var(--text-2);
  font-size: var(--text-md);
  line-height: 1.5;
}
.step-links {
  display: flex;
  flex-wrap: wrap;
  gap: 4px 16px;
  margin: 12px 0 0;
  padding: 0;
  list-style: none;
  font-size: var(--text-sm);
}
.step-links a { color: var(--text); }
/* Where the steps stand side by side, each one's node, title and text
   take the same three rows as its neighbours', so every step's text
   starts on one line however many lines its title takes. A shorter title
   sits down on its text, and the room it leaves goes above it. */
@supports (grid-template-rows: subgrid) {
  @media (min-width: 56.25em) {
    .step { display: grid; grid-row: span 3; grid-template-rows: subgrid; }
    .step h3 { align-self: end; }
  }
}

/* ---------- How it works ---------- */

/* The signal path: one 1px wire through four stages, left to right (top to
   bottom below 900px). Each stage is marked by its icon on the wire, and
   each stretch of wire ends in an arrowhead just short of the next icon.
   knobs's stage is the one panel, hung under its icon. Every 1px line that
   meets an icon runs through its centre exactly: the icon's box is an odd
   number of pixels, so its centre is a pixel's centre, and each line sits
   at (size - 1px) / 2 across it. */
.signal {
  --kp: 20px;
  --kp-top: 14px;
  --kp-drop: 10px;
  --sp-cols: minmax(0, 1fr) minmax(0, 1.6fr) minmax(0, 1fr) minmax(0, 1fr);
  --sp-gap: 40px;
  --sp-icon: max(21px, 1.25rem + 1px);
  --sp-clear: 6px;
  --sp-row: 28px;
  --sp-dash: 44px;         /* the running signal's trail, at full speed */
  --pulse-w: 3px;          /* its weight, three times the wire's */
  --pulse-box: 7px;        /* its head, a small cap: odd, so it centres on the 1px wire */
  --pulse: var(--text);    /* ink from the mic; cap red from knobs on */
  --wire: var(--text-3);
  position: relative;
  margin: 0;
}
/* Kept odd at any text size where the browser can round. */
@supports (width: round(down, 1px, 2px)) {
  .signal { --sp-icon: max(21px, round(down, 1.25rem, 2px) + 1px); }
}
.signal-path {
  display: grid;
  grid-template-columns: var(--sp-cols);
  column-gap: var(--sp-gap);
  align-items: start;
  margin: 0;
  padding: 0;
  list-style: none;
}
/* Every stage's name sits on one line with the knobs panel's. */
.sp {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding-top: calc(var(--sp-icon) + var(--kp-drop) + 1px + var(--kp-top));
}
.sp-name { color: var(--text); font-weight: 600; line-height: 1.4; }
.sp-note { color: var(--text-3); font-size: var(--text-sm); line-height: 1.45; }
.sp-note a { color: var(--text-2); }
.sp-note a:hover { color: var(--action-text); }
/* A stage's icon, in the sprite's cut: 1.5px strokes, butt caps and miter
   joins, round only where the thing is. Graphite like the wire, and
   knobs's in cap red. */
.sp-icon {
  position: absolute;
  top: 0;
  left: 0;
  width: var(--sp-icon);
  height: var(--sp-icon);
  overflow: visible;
  color: var(--wire);
  fill: none;
  stroke: currentColor;
  stroke-width: 1.5;
  stroke-linecap: butt;
  stroke-linejoin: miter;
}
.sp-knobs > .sp-icon { color: var(--action); }
/* The wire to the next stage, and its arrowhead. The signal that runs
   along a wire (.sp-pulse, added by page.js) takes the wire's place. */
.sp:not(:last-child)::after { content: ""; }
.sp:not(:last-child)::after,
.sp > .sp-pulse {
  position: absolute;
  top: calc((var(--sp-icon) - 1px) / 2);
  left: calc(var(--sp-icon) + var(--sp-clear));
  right: calc(var(--sp-clear) + 1px - var(--sp-gap));
  border-top: 1px solid var(--wire);
}
/* The arrowhead's 7px box turned 45 degrees: its tip is level with the
   box's centre, so the box sits at (size - 7px) / 2. */
.sp:not(:last-child)::before {
  content: "";
  position: absolute;
  top: calc((var(--sp-icon) - 7px) / 2);
  right: calc(var(--sp-clear) + 1.5px - var(--sp-gap));
  width: 7px;
  height: 7px;
  border-top: 1px solid var(--wire);
  border-right: 1px solid var(--wire);
  transform: rotate(45deg);
}

/* knobs: the wire runs straight through its icon, as it does through every
   other, and the panel hangs under the icon, clear of the wire, with a
   caret in its top edge pointing up at it. The panel reaches into the gaps
   by its padding, so its icon, name and note stay on the column like every
   other stage's. A 1px cap-red edge, square, and no shadow: depth from
   tone. */
.sp-knobs {
  margin: calc(var(--sp-icon) + var(--kp-drop)) calc((var(--kp) + 1px) * -1) 0;
  padding: var(--kp-top) var(--kp) var(--kp);
  border: 1px solid var(--action);
  border-radius: var(--radius);
  background: var(--raised);
}
.sp-knobs > .sp-icon {
  top: calc((var(--sp-icon) + var(--kp-drop) + 1px) * -1);
  left: var(--kp);
}
.sp-knobs:not(:last-child)::after,
.sp-knobs > .sp-pulse {
  top: calc((var(--sp-icon) + 1px) / -2 - var(--kp-drop) - 1px);
  left: calc(var(--kp) + var(--sp-icon) + var(--sp-clear));
  right: calc(var(--sp-clear) + 1px + var(--kp) - var(--sp-gap));
}
.sp-knobs:not(:last-child)::before {
  top: calc((var(--sp-icon) + 7px) / -2 - var(--kp-drop) - 1px);
  right: calc(var(--sp-clear) + 1.5px + var(--kp) - var(--sp-gap));
}
/* The caret is one 1px stroke: along the top edge, up to a point 6px above
   it under the icon's centre, and back down. Its fill covers the edge
   between its sides, so it reads as part of the border. It points: the
   45 degrees here mean direction. */
.sp-caret {
  position: absolute;
  top: -8px;
  left: calc(var(--kp) + (var(--sp-icon) - 21px) / 2);
  width: 21px;
  height: 8px;
  overflow: visible;
  fill: var(--raised);
  stroke: var(--action);
  stroke-width: 1;
  stroke-linejoin: miter;
}
.sp-caret path:first-child { stroke: none; }
.sp-caret path:last-child { fill: none; }

/* The chain inside knobs is the visitor's own, so it's drawn, not named: a
   row for each filter, in the order they run, with a red dot on a short
   wire down the list and a setting drawn as a fader at its own value, its
   handle a red cap. Screen readers get one image, named by its label. Each
   row grows with the visitor's text size. */
.sp-drawn { margin: 16px 0 0; }
.sp-chain {
  --row: calc(14px + 1.4em);
  position: relative;
  margin: 0;
  padding: 0;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--sunken);
  list-style: none;
  font-size: var(--text-sm);
}
.sp-chain li {
  position: relative;
  height: var(--row);
}
.sp-chain li + li { border-top: 1px solid var(--line); }
.sp-chain li::before {
  content: "";
  position: absolute;
  top: calc(50% - 2.5px);
  left: 14px;
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: var(--action);
}
.sp-chain li:not(:last-of-type)::after {
  content: "";
  position: absolute;
  top: calc(50% + 2.5px);
  left: 16px;
  height: calc(100% - 4px);
  border-left: 1px solid var(--wire);
}
/* A filter's setting: the track, the stretch up to its value, and the
   handle, a red cap, a true circle. */
.sp-setting {
  position: absolute;
  top: 50%;
  left: 32px;
  right: 18px;
  border-top: 1px solid var(--line-strong);
}
.sp-setting::before {
  content: "";
  position: absolute;
  top: -1px;
  left: 0;
  width: calc(var(--v) * 100%);
  border-top: 1px solid var(--text-3);
}
.sp-setting::after {
  content: "";
  position: absolute;
  top: -5px;
  left: calc(var(--v) * 100% - 4.5px);
  width: 9px;
  height: 9px;
  border: 1px solid var(--action);
  border-radius: 50%;
  background: var(--action);
}

/* One signal through the path, once. It sets off from the mic in ink and
   glides into knobs, where the filters take it in order: each fader runs
   up to its setting in red, its dot and cap turning red as it does, and
   settles back to grey; then the signal leaves knobs in cap red, as knobs's sound, through the cable to the apps,
   which light as it reaches them. The whole path holds lit for a moment
   and settles to the drawing as it always is. Before the pass the drawing
   waits whole, knobs red as ever, with only its filters unlit: a grey dot,
   an empty ring and no run up to the setting, each. page.js moves the signal and times each light
   (--at, and --off for what goes out again). With reduced motion, without
   script, in forced colours and in print, nothing moves and nothing waits:
   the lit drawing is simply there. */
.sp-pulse {
  z-index: 1;
  overflow: hidden;
  border: 0 !important;
  pointer-events: none;
}
/* Along a wire, the pulse's box is its head's size, centred on the wire,
   and it clips the signal to the wire. */
.sp-pulse:not(.is-y) {
  width: auto;
  height: var(--pulse-box);
  margin-top: calc((var(--pulse-box) - 1px) / -2);
}
.sp-pulse.is-y {
  width: var(--pulse-box);
  height: auto;
  margin-left: calc((var(--pulse-box) - 1px) / -2);
}
/* The signal: its head, a small cap, and a trail that ends under the head
   and is as long as the signal is fast, so it streaks at speed and is only
   its head at rest. page.js moves both, by transform alone; until then
   they wait off the wire. */
.sp-trail,
.sp-head {
  position: absolute;
  top: 0;
  left: 0;
  background: var(--pulse);
}
.sp-head {
  width: var(--pulse-box);
  height: var(--pulse-box);
  border-radius: 50%;
  transform: translateX(calc(-1 * var(--pulse-box)));
}
.sp-trail {
  top: calc((var(--pulse-box) - var(--pulse-w)) / 2);
  width: var(--sp-dash);
  height: var(--pulse-w);
  transform: scaleX(0);
  transform-origin: 100% 50%;
}
.is-y > .sp-head { transform: translateY(calc(-1 * var(--pulse-box))); }
.is-y > .sp-trail {
  top: 0;
  left: calc((var(--pulse-box) - var(--pulse-w)) / 2);
  width: var(--pulse-w);
  height: var(--sp-dash);
  transform: scaleY(0);
  transform-origin: 50% 100%;
}
.sp-knobs .sp-pulse,
.sp-knobs ~ .sp > .sp-pulse { --pulse: var(--action); }

/* A stage lights as the signal reaches it: an ink copy of the mic's icon,
   cap-red copies of the cable's and the apps'. knobs's is red already. */
.sp-icon.is-lit { color: var(--text); opacity: 0; }
.sp-knobs ~ .sp > .sp-icon.is-lit { color: var(--action); }

@media screen and (prefers-reduced-motion: no-preference) {
  /* Waiting: only the filters are unlit. */
  .signal.is-armed .sp-chain li::before { background-color: var(--wire); }
  .signal.is-armed .sp-setting::before { transform: scaleX(0); }
  .signal.is-armed .sp-setting::after { background-color: var(--sunken); }

  /* The pass. Each part waits for its moment (--at) as it was and stays
     as it ends up (fill both), unless it goes out again at --off. Things
     light at the feedback pace and go out at the structure pace. */
  .signal.is-playing .sp-icon.is-lit {
    animation: sp-on var(--duration-fast) var(--ease) var(--at) both,
      sp-off var(--duration-slow) var(--ease) var(--off) forwards;
  }
  .signal.is-playing .sp-chain li::before { animation: sp-dot var(--duration-fast) var(--ease) var(--at) both; }
  .signal.is-playing .sp-setting::before {
    transform-origin: 0 50%;
    animation: sp-level 1100ms var(--ease) var(--at) both;
  }
  /* The cap fills as the run reaches it. */
  .signal.is-playing .sp-setting::after { animation: sp-cap var(--duration-fast) var(--ease) calc(var(--at) + 180ms) both; }
}
@keyframes sp-on {
  from { opacity: 0; }
  to { opacity: 1; }
}
@keyframes sp-off {
  from { opacity: 1; }
  to { opacity: 0; }
}
/* A filter as the signal passes through it: its fader runs up to its
   setting in red, like a level, in about 220ms; holds; and settles back to
   the track's grey by 1.1s. Its dot and cap turn red and stay, at the
   size they always are. */
@keyframes sp-level {
  0% { transform: scaleX(0); border-top-color: var(--action); }
  20% { transform: scaleX(1); border-top-color: var(--action); }
  55% { border-top-color: var(--action); }
  100% { transform: scaleX(1); border-top-color: var(--text-3); }
}
@keyframes sp-dot {
  from { background-color: var(--wire); }
}
@keyframes sp-cap {
  from { background-color: var(--sunken); }
}

/* Three facts in a row of ruled columns, with no cards: each opens on a
   strong hairline, with a cap dot at its start,
   the way the steps sit on theirs. */
.facts {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 40px var(--col-gap);
  margin: clamp(64px, 7vw, 96px) 0 0;
}
.facts > div {
  position: relative;
  padding-top: 22px;
  border-top: 1px solid var(--line-strong);
}
.facts > div::before {
  content: "";
  position: absolute;
  top: calc((var(--dot) + 1px) / -2);
  left: 0;
  width: var(--dot);
  height: var(--dot);
  border-radius: 50%;
  background: var(--action);
}
.facts dt {
  color: var(--text);
  font: 600 1.1875rem/1.3 var(--font-sans);
  letter-spacing: -0.005em;
}
.facts dd {
  max-width: var(--measure);
  margin: 10px 0 0;
  color: var(--text-2);
}

/* ---------- Measured ---------- */

/* Each measurement is a row: the claim left, its evidence right, the rows
   ruled. The evidence keeps a structure screen readers read as data, and
   CSS draws it as a chart. */
.measures { border-top: 1px solid var(--line-strong); }
.measure {
  display: grid;
  grid-template-columns: var(--head-col) minmax(0, 1fr);
  column-gap: var(--col-gap);
  padding-block: 40px 44px;
  border-bottom: 1px solid var(--line);
}
.measure:last-child { padding-bottom: 0; border-bottom: 0; }
.measure h3 {
  color: var(--text);
  font-size: 1.25rem;
  line-height: 1.3;
  letter-spacing: -0.005em;
}
.measure-text p {
  max-width: var(--measure);
  margin-top: 10px;
  color: var(--text-2);
  font-size: var(--text-md);
}
.measure-fig {
  min-width: 0;
  margin: 0;
  container-type: inline-size;
}
.footnote {
  color: var(--text-3);
  font-size: var(--text-xs);
  line-height: 1.5;
}
.measure-fig > .footnote { max-width: var(--measure); margin-top: 18px; }
.footnote a { color: var(--text-2); }

.table {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--text-md);
}
.table th, .table td {
  padding: 10px 16px 10px 0;
  border-bottom: 1px solid var(--line);
  text-align: left;
  vertical-align: top;
}
.table thead th {
  border-bottom-color: var(--line-strong);
  color: var(--text-3);
  font-size: var(--text-xs);
  font-weight: 500;
}

/* Same sound: the result for each input, read like a test report. */
.match th, .match td { padding-block: 13px; }
.match thead th { padding-top: 0; }
.match thead th:last-child,
.match td { padding-right: 0; text-align: right; white-space: nowrap; }
.match tbody th { color: var(--text); font-weight: 400; }
/* A module's name moves to the next line whole, never split at its hyphen. */
.match th code { white-space: nowrap; }
.match td { color: var(--text); font-weight: 600; }
.match td .icon { width: 1em; height: 1em; margin-right: 0.5em; vertical-align: -0.15em; }

/* Charts. Every bar's length is its value on a linear scale from zero:
   --f, set beside the printed value, is its share of the scale's width.
   Bars leave --room at the end of the scale for the longest label, in rem
   so it grows with the visitor's text size. knobs is cap red, lit; OBS an
   unlit grey: they differ in hue (in dark in lightness too), and by order and label,
   so colour is never the only thing that tells them apart. Bar ends are
   cut square, as every rectangle. */
.chart {
  --bar: 10px;
  --room: 5.25rem;
  --key: 4.5rem;
  --c-knobs: var(--action);
  --c-obs: var(--chart-obs);
}

/* Same delay: two bars on one scale, 0 to 100 ms */
.chart-delay { --bar: 12px; }
.plot { position: relative; padding-bottom: 26px; }
.ticks {
  position: absolute;
  top: -8px;
  bottom: 0;
  left: var(--key);
  right: var(--room);
  color: var(--text-3);
  font-size: var(--text-xs);
  font-variant-numeric: tabular-nums;
  line-height: 1;
  white-space: nowrap;
}
.ticks span {
  position: absolute;
  top: 0;
  bottom: 0;
  left: calc(var(--t) * 100%);
  display: flex;
  align-items: flex-end;
  transform: translateX(-50%);
}
.ticks span:first-child { transform: none; }
.ticks span::before {
  content: "";
  position: absolute;
  top: 0;
  bottom: 20px;
  left: 50%;
  border-left: 1px solid var(--line);
}
.ticks span:first-child::before { left: 0; border-left-color: var(--line-strong); }
.bars {
  position: relative;
  display: grid;
  gap: 6px;
  margin: 0;
}
.bar-row {
  display: grid;
  grid-template-columns: var(--key) minmax(0, 1fr);
  align-items: center;
}
.bar-row dt { padding-right: 12px; color: var(--text-2); font-size: var(--text-md); }
.bar-row dd {
  display: flex;
  align-items: center;
  min-height: 30px;
  margin: 0;
}
.bar-row dd::before {
  content: "";
  flex: none;
  width: calc((100% - var(--room)) * var(--f));
  height: var(--bar);
  margin-right: 10px;
  border-radius: 0 var(--radius) var(--radius) 0;
  background: var(--c-obs);
}
/* The label masks any gridline it crosses, on a narrow chart. */
.val {
  padding-right: 4px;
  background: var(--band-bg, var(--ground));
  color: var(--text-2);
  font-size: var(--text-md);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.val .num { font-size: 1rem; font-weight: 600; letter-spacing: -0.01em; }
.bar-row.is-knobs dt,
.bar-row.is-knobs .val { color: var(--text); font-weight: 600; }
.bar-row.is-knobs dd::before { background: var(--c-knobs); }

/* "Lower is better", once for the footprint's four measures, which all
   run the same way. It ends the legend's line, quiet, in label type, and
   takes a line of its own when the legend fills the row. */
.chart-note {
  color: var(--text-3);
  font-size: var(--text-xs);
  font-weight: 500;
  line-height: 1.4;
}

/* A fraction of the footprint: the table, laid out as four small charts.
   The header row is the legend; each body row is a measure, with knobs's
   bar over OBS's on that measure's own scale. */
.chart-footprint { --metric: 11.5rem; }
.footprint {
  display: block;
  width: 100%;
  border-collapse: collapse;
  font-size: var(--text-md);
}
.footprint thead,
.footprint tbody { display: block; }
.footprint thead tr {
  display: flex;
  flex-wrap: wrap;
  column-gap: 20px;
  row-gap: 4px;
  padding: 0 0 14px var(--metric);
}
.footprint thead td { padding: 0; margin-right: -20px; }
.footprint thead .chart-note { align-self: center; margin: 0 0 0 auto; }
.footprint thead th {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 0;
  color: var(--text-2);
  font-size: var(--text-xs);
  font-weight: 500;
  text-align: left;
}
.footprint thead th::before {
  content: "";
  flex: none;
  width: 14px;
  height: 10px;
  border-radius: var(--radius);
  background: var(--c-obs);
}
.footprint thead th.is-knobs::before { background: var(--c-knobs); }
.footprint tbody tr {
  display: grid;
  grid-template-columns: var(--metric) minmax(0, 1fr);
  padding: 12px 0;
  border-top: 1px solid var(--line);
}
.footprint tbody tr:last-child { border-bottom: 1px solid var(--line); }
.footprint tbody th {
  grid-row: 1 / span 2;
  align-self: center;
  padding: 0 16px 0 0;
  color: var(--text);
  font-weight: 600;
  text-align: left;
}
.footprint tbody td {
  grid-column: 2;
  display: flex;
  align-items: center;
  min-height: 26px;
  padding: 0;
  border-left: 1px solid var(--line-strong);
  color: var(--text-2);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.footprint tbody td::before,
.footprint tbody td.is-range::after {
  content: "";
  flex: none;
  order: -2;
  width: calc((100% - var(--room)) * var(--f));
  height: var(--bar);
  margin-right: 10px;
  border-radius: 0 var(--radius) var(--radius) 0;
  background: var(--c-obs);
}
.footprint tbody td.is-knobs { color: var(--text); font-weight: 600; }
.footprint tbody td.is-knobs::before,
.footprint tbody td.is-knobs::after { background: var(--c-knobs); }
/* A range: its low end solid, the rest of it pale */
.footprint tbody td.is-range::before { margin-right: 0; border-radius: 0; }
.footprint tbody td.is-range::after {
  order: -1;
  width: calc((100% - var(--room)) * (var(--f2) - var(--f)));
  opacity: 0.32;
}
/* GPU, said in words: with no bars there's no zero line either, and each
   value is followed, more quietly, by whose it is. */
.footprint tbody tr.is-words td { gap: 0.3em; border-left: 0; }
.footprint tbody tr.is-words td::before { content: none; }
.footprint .who { color: var(--text-3); font-weight: 400; }

/* A narrow chart puts each measure's name over its bars. */
@container (max-width: 34em) {
  .footprint thead tr { padding-left: 0; }
  .footprint tbody tr { grid-template-columns: minmax(0, 1fr); padding: 14px 0 12px; }
  .footprint tbody th { grid-row: auto; grid-column: 1; padding: 0 0 6px; }
  .footprint tbody td { grid-column: 1; }
}
/* With large text, the delay chart does the same and keeps three ticks. */
@container (max-width: 22em) {
  .ticks span:nth-child(even) { color: transparent; }
}
@container (max-width: 18em) {
  .plot { --key: 0px; }
  .bar-row { grid-template-columns: minmax(0, 1fr); }
  .bar-row dt { justify-self: start; padding: 4px 6px 0 0; background: var(--band-bg, var(--ground)); }
}
/* With large text, each input's result goes under it. The header row stays
   for screen readers. */
@container (max-width: 20em) {
  .match,
  .match tbody,
  .match tr,
  .match th,
  .match td { display: block; }
  .match thead {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
  }
  .match tbody { border-top: 1px solid var(--line-strong); }
  .match tr { padding: 12px 0; border-bottom: 1px solid var(--line); }
  .match th, .match td { padding: 0; border: 0; }
  .match td { margin-top: 2px; text-align: left; }
}

/* The charts' one motion: the first time a chart comes into view, one
   sweep draws its bars from zero, at the same pace on every line. page.js
   arms only charts still below the fold; without script, with reduced
   motion and in print (see Print) the bars are simply there. */
.chart.is-armed .bar-row dd,
.chart.is-armed .footprint tbody td { clip-path: inset(0 100% 0 0); }
.chart.is-armed.is-swept .bar-row dd,
.chart.is-armed.is-swept .footprint tbody td {
  clip-path: inset(0);
  transition: clip-path 720ms var(--ease);
}

/* ---------- Questions ---------- */

/* The questions in three groups, each a ruled row as the benchmarks are:
   the group's name on the left, quiet, on the line of its first
   question, and its questions and answers on the right, all open. Each
   question is marked by a cap dot, its answer hung under the question's
   words. */
.qa { border-top: 1px solid var(--line-strong); }
.qa-group {
  display: grid;
  grid-template-columns: var(--head-col) minmax(0, 1fr);
  column-gap: var(--col-gap);
  align-items: baseline;
  padding-block: 40px 44px;
  border-bottom: 1px solid var(--line);
}
.qa-group:last-child { padding-bottom: 0; border-bottom: 0; }
.qa-group > h3 {
  color: var(--text-2);
  font-size: var(--text-md);
  line-height: 1.4;
}
.qa-item { position: relative; padding-left: calc(var(--dot) + 12px); }
.qa-item + .qa-item { margin-top: 32px; }
.qa-item h4 {
  color: var(--text);
  font-size: 1.1875rem;
  line-height: 1.35;
  letter-spacing: -0.005em;
}
/* Centred on the question's first line, which is 1.35em tall. */
.qa-item h4::before {
  content: "";
  position: absolute;
  top: calc(0.675em - var(--dot) / 2);
  left: 0;
  width: var(--dot);
  height: var(--dot);
  border-radius: 50%;
  background: var(--action);
}
.qa-item p {
  max-width: var(--measure);
  margin-top: 8px;
  color: var(--text-2);
}
.qa-item p a { color: var(--text); }
.qa-item p a:hover { color: var(--action-text); }

/* ---------- Footer ---------- */

/* Big and calm, on a band of the buttons' red, cap red in dark and cap
   red deep in light: the one-colour lockup at 44px tall with the line, the
   link columns under a hairline, each headed in the condensed face the
   section titles use, then the legal lines. Inside it the page's roles
   take the band's own colours, so every rule above reads on the red
   without one of its own: every word, link and underline in the colour of
   a red button's words (graphite in dark, white in light), a thicker
   underline under the pointer, the band's own hairlines, a focus ring in
   the words' colour, and a red of its own for selected text. White on cap
   red deep is 4.5:1 with no margin, so the smallest words take 500, as
   they do on a red button. The band is the screen's: on paper the footer
   keeps the page's own colours (see Print). */
.site-footer {
  padding-block: clamp(64px, 6vw, 88px) 40px;
  color: var(--text-2);
}
@media screen {
  .site-footer {
    --line: var(--band-line);
    --line-strong: var(--band-line-strong);
    --text: var(--on-band);
    --text-2: var(--on-band);
    --text-3: var(--on-band);
    --action: var(--on-band);
    --action-text: var(--on-band);
    --selection: var(--band-selection);
    --focus: var(--band-focus);
    background: var(--band);
  }
}
.footer-top {
  display: grid;
  grid-template-columns: minmax(0, 5fr) minmax(0, 7fr);
  gap: 48px var(--col-gap);
  align-items: start;
}
.footer-id img { width: auto; height: 44px; }
/* The lockup that reads on the band: black on cap red, white on cap red
   deep. In forced colours and on paper it follows the page's own colours
   instead (see there). */
.footer-id .lockup-white { display: none; }
@media screen {
  :root[data-theme="light"] .footer-id .lockup-white { display: block; }
  :root[data-theme="light"] .footer-id .lockup-black { display: none; }
}
.footer-id p { margin-top: 16px; color: var(--text-3); font-size: var(--text-md); }
.footer-nav {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 15rem));
  gap: 32px var(--col-gap);
  padding-top: 28px;
  border-top: 1px solid var(--line);
}
.footer-h {
  margin-left: -0.05em;  /* hung as the section titles are */
  color: var(--text);
  font: 700 1.375rem/1.1 var(--font-display);
  letter-spacing: -0.005em;
}
.footer-nav ul { margin: 14px 0 0; padding: 0; list-style: none; }
.footer-nav li + li { margin-top: 8px; }
.footer-nav a {
  color: var(--text-2);
  font-size: var(--text-md);
  text-decoration: none;
}
.footer-nav a:hover { color: var(--action-text); text-decoration: underline; text-decoration-color: var(--action); text-decoration-thickness: 2px; }
.footer-legal {
  margin-top: clamp(48px, 5vw, 72px);
  padding-top: 24px;
  border-top: 1px solid var(--line);
  color: var(--text-3);
  font-size: var(--text-xs);
  font-weight: 500;
}
.footer-legal p + p { margin-top: 4px; }
.footer-legal a { color: var(--text-2); }
.footer-legal a:hover { color: var(--action-text); text-decoration-thickness: 2px; }

/* ---------- Forced colours ---------- */

/* Forced colours drop fills, so the bars, dots and caret keep theirs in the
   visitor's own colours: text colour for knobs, the disabled-text colour
   for OBS. Step 3's cap stays filled, in the visitor's text colour. */
@media (forced-colors: active) {
  .chart { --c-knobs: CanvasText; --c-obs: GrayText; }
  .bar-row dd::before,
  .footprint thead th::before,
  .footprint tbody td::before,
  .footprint tbody td::after,
  .sp-chain li::before,
  .reqs dt::before,
  .facts > div::before,
  .qa-item h4::before { forced-color-adjust: none; }
  .sp-chain li::before,
  .reqs dt::before,
  .facts > div::before,
  .qa-item h4::before { background: CanvasText; }
  .signal .sp-icon { color: CanvasText; }
  .sp-caret { forced-color-adjust: none; fill: Canvas; stroke: CanvasText; }
  /* A filter's cap stays filled, as it is when lit: an empty ring is a
     filter the signal hasn't reached. */
  .sp-setting::after { forced-color-adjust: none; border-color: CanvasText; background: CanvasText; }
  .file.is-primary { border-color: CanvasText; border-width: 2px; }
  .step-get > .node {
    forced-color-adjust: none;
    border-color: CanvasText;
    background: CanvasText;
    color: Canvas;
  }
  /* The visitor's own colours rule here, so the toggle has nothing to do,
     and the signal doesn't run. */
  .theme-toggle { display: none !important; }
}
/* In forced colours the canvas is the visitor's, whatever the toggle says:
   the header takes the colour lockup that reads on it, and the footer, its
   red gone, the one-colour lockup that does (white on a dark canvas, black
   on a light one). */
@media (forced-colors: active) and (prefers-color-scheme: dark) {
  .brand .lockup-on-dark { display: block !important; }
  .brand .lockup-on-light { display: none !important; }
  .footer-id .lockup-white { display: block !important; }
  .footer-id .lockup-black { display: none !important; }
}
@media (forced-colors: active) and (prefers-color-scheme: light) {
  .brand .lockup-on-dark { display: none !important; }
  .brand .lockup-on-light { display: block !important; }
  .footer-id .lockup-white { display: none !important; }
  .footer-id .lockup-black { display: block !important; }
}

/* ---------- Print ---------- */

/* On paper the page is the light scheme (theme.js switches to it for the
   print and back after), ink on white whether or not the browser prints
   backgrounds. Its marks print in their own colours: the bars, the
   swatches, the cap dots, the fader caps and step 3's cap are fills, which
   a browser leaves out by default. The buttons print as the red outline
   of a cap with red words, since a solid one would print its words white
   on white. The footer drops its band for the black one-colour lockup on
   the page's own colours (see Footer). Controls that only work on a
   screen, and the video's frame, stay off the paper. */
@media print {
  .bar-row dd::before,
  .footprint thead th::before,
  .footprint tbody td::before,
  .footprint tbody td::after,
  .sp-chain li::before,
  .sp-setting::after,
  .reqs dt::before,
  .facts > div::before,
  .qa-item h4::before,
  .dl-bar,
  .step-get > .node {
    -webkit-print-color-adjust: exact;
    print-color-adjust: exact;
  }
  .chart .bar-row dd,
  .chart .footprint tbody td { clip-path: none !important; }

  .btn-primary,
  .btn-primary:hover {
    background: transparent;
    border-color: var(--action);
    color: var(--action-text);
  }
  .site-footer { border-top: 1px solid var(--line); }
  .footer-id .lockup-white { display: none; }
  .footer-id .lockup-black { display: block; }

  .hero-grid { grid-template-columns: minmax(0, 1fr); }
  .skip-link,
  .preview-strip,
  .header-tools,
  .video,
  .video-controls,
  .video-focus,
  [data-retry],
  .copy { display: none !important; }
}

/* ---------- Responsive ---------- */

/* Breakpoints are in em, so they follow the text size set in the browser:
   at the default 16px they fall at 1024, 900, 864, 768, 640, 480, 375 and
   360px, and with larger text the page takes the layout of a narrower
   window instead of squeezing its columns. Short windows have their own
   steps, by height. */

/* Short desktop windows (a 1280x720 laptop, or 1366x768 at 100%) keep the
   headline a step smaller, so the button sits well inside the first
   screen. */
@media (min-width: 56.25em) and (max-height: 50em) {
  :root { --display: clamp(2.25rem, 5.84vw, 4.5rem); }
}
/* Shorter still (a 1366x768 laptop at 125%, about 1093x520, or a 1280x720
   one under the browser's bars, about 1280x610): the header, the headline
   and the lede give back room, so the requirements under the big button,
   and off Windows the link to the checksums, stay in the first screen with
   it. Where the hero's column is narrower than its full width (below
   1360px) the lede and the requirements take more lines, so this step
   reaches windows up to 680px tall; at full width, up to 632px. */
@media (min-width: 56.25em) and (max-height: 39.5em), (min-width: 56.25em) and (max-width: 84.9375em) and (max-height: 42.5em) {
  :root { --display: 3.25rem; --lede: 1rem; }
  .site-header-inner { min-height: 56px; }
  .hero { padding-block: 16px 40px; }
  .lede { margin-top: 16px; }
  .get { margin-top: 24px; }
  .hero-more { margin-top: 14px; }
}

@media (max-width: 63.9375em) {
  /* Below 1024px the measurement rows stack: the claim over its chart.
     The question groups do the same, each name over its questions. */
  .measure { display: block; padding-block: 32px 36px; }
  .measure-fig { margin-top: 24px; }
  .qa-group { display: block; padding-block: 32px 36px; }
  .qa-items { margin-top: 20px; }
}

/* Below 900px the hero stacks and centres: the headline, the lede, the full-width button, the link, then the
   video. */
@media (max-width: 56.1875em) {
  .hero { padding-block: 32px 48px; }
  .hero-grid {
    grid-template-columns: minmax(0, 1fr);
    justify-items: center;
    text-align: center;
  }
  .pitch {
    display: flex;
    flex-direction: column;
    align-items: center;
    width: 100%;
    max-width: 36rem;
  }
  .pitch h1 {
    font-size: clamp(2.125rem, 1.2rem + 5.2vw, 3.75rem);
    line-height: 0.96;
  }
  .lede { margin-top: 18px; }
  /* Centred, the lede and the Needs line run in even lines, so neither
     ends on a word or a link alone. */
  .lede, .hero-needs { text-wrap: balance; }
  .get { width: 100%; max-width: 26rem; margin-top: 28px; }
  .get .rs-row { justify-content: center; }
  /* Both buttons take the stack's full width, Try again under the big one
     (see the hero). */
  .get .actions { justify-content: stretch; }
  .btn-hero.btn-primary,
  .btn-hero.btn-download { width: 100%; min-width: 0; }
  .btn-download { justify-content: center; padding-inline: 20px; }
  .rs-line, .hero-needs { margin-inline: auto; }
  /* Centred, the source line runs as one sentence with its mark. */
  .dest { display: block; }
  .dest .icon { margin: 0 8px 0 0; vertical-align: -0.1875rem; }
  .hero-more { margin-top: 18px; }
  .video { width: 100%; max-width: 40rem; margin-top: 40px; }
  /* Its controls centre under it, like the rest of the stack. */
  .video > .video-controls { justify-self: center; }

  /* The signal path runs down the left: the same icons, wire and
     arrowheads, turned, each icon level with its stage name's first line.
     The knobs panel sits right of the rail, its caret turned onto its left
     edge to point across at knobs's icon. The column starts on the
     section title's edge, as every section's content does. */
  .signal {
    --kp-top: 12px;
    --sp-indent: calc(var(--sp-icon) + 22px);
    --sp-top: calc(0.7rem - var(--sp-icon) / 2);
  }
  .signal-path {
    grid-template-columns: minmax(0, 1fr);
    row-gap: var(--sp-row);
    max-width: 30rem;
    margin-inline: 0;
  }
  .sp { padding: 0 0 0 var(--sp-indent); }
  .sp-icon { top: var(--sp-top); }
  .sp:not(:last-child)::after,
  .sp > .sp-pulse {
    top: calc(var(--sp-top) + var(--sp-icon) + var(--sp-clear));
    bottom: calc((var(--sp-row) + var(--sp-top) - var(--sp-clear) - 1px) * -1);
    left: calc((var(--sp-icon) - 1px) / 2);
    right: auto;
    border-top: 0;
    border-left: 1px solid var(--wire);
  }
  .sp:not(:last-child)::before {
    top: auto;
    bottom: calc((var(--sp-row) + var(--sp-top) - var(--sp-clear) - 1.5px) * -1);
    left: calc((var(--sp-icon) - 7px) / 2);
    right: auto;
    transform: rotate(135deg);
  }
  .sp-knobs {
    margin: 0 0 0 calc(var(--sp-icon) + 9px);
    padding: var(--kp-top) 18px 18px 12px;
  }
  .sp-knobs > .sp-icon {
    top: calc(var(--kp-top) + var(--sp-top));
    left: calc((var(--sp-icon) + 10px) * -1);
  }
  .sp:has(+ .sp-knobs)::after,
  .sp:has(+ .sp-knobs) > .sp-pulse {
    bottom: calc((var(--sp-row) + var(--kp-top) + var(--sp-top) - var(--sp-clear)) * -1);
  }
  .sp:has(+ .sp-knobs)::before {
    bottom: calc((var(--sp-row) + var(--kp-top) + var(--sp-top) - var(--sp-clear) - 0.5px) * -1);
  }
  .sp-knobs:not(:last-child)::after,
  .sp-knobs > .sp-pulse {
    top: calc(var(--kp-top) + var(--sp-top) + var(--sp-icon) + var(--sp-clear));
    bottom: calc((var(--sp-row) + var(--sp-top) - var(--sp-clear)) * -1);
    left: calc((var(--sp-icon) + 1px) / -2 - 10px);
    right: auto;
  }
  .sp-knobs:not(:last-child)::before {
    top: auto;
    bottom: calc((var(--sp-row) + var(--sp-top) - var(--sp-clear) - 0.5px) * -1);
    left: calc((var(--sp-icon) + 7px) / -2 - 10px);
    right: auto;
  }
  .sp-caret {
    top: calc(var(--kp-top) + var(--sp-top) + (var(--sp-icon) - 7px) / 2);
    left: -14px;
    transform: rotate(-90deg);
    transform-origin: 10px 4px;
  }
}

/* Below 900px the four steps run down one rule on the left, from the
   section title's edge, each title level with its node. */
@media (max-width: 56.1875em) {
  .steps {
    grid-template-columns: minmax(0, 1fr);
    row-gap: 28px;
    max-width: 34rem;
  }
  .step { padding-left: calc(var(--node) + 18px); }
  .step > .node { position: absolute; top: 0; left: 0; }
  .step h3 { margin-top: calc((var(--node) - 1.3em) / 2); }
  .step:not(:last-child)::before {
    top: calc(var(--node) / 2);
    bottom: -28px;
    left: calc((var(--node) - 1px) / 2);
    right: auto;
    border-top: 0;
    border-left: 1px solid var(--line-strong);
  }
}

/* Below 768px the facts and the footer each take one column. */
@media (max-width: 47.9375em) {
  .facts { grid-template-columns: minmax(0, 1fr); gap: 32px; }
  .footer-top { grid-template-columns: minmax(0, 1fr); }
}

/* Below 864px the lockup, the four links, Source and the light/dark
   toggle no longer share a row: the links take a row of their own under
   the lockup, the first one's words on the gutter. Where even that row is
   too narrow (large text) the links wrap onto another row. */
@media (max-width: 53.9375em) {
  .site-header-inner {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    grid-template-rows: 60px auto;
    align-items: center;
    gap: 0 16px;
  }
  .brand { justify-self: start; }
  .header-tools { grid-area: 1 / 2; margin-left: 0; }
  .site-nav { grid-area: 2 / 1 / 3 / -1; }
  .site-nav ul { flex-wrap: wrap; gap: 0; margin: 0 -10px; }
  .site-nav a {
    display: flex;
    align-items: center;
    min-height: 44px;
    padding: 0 10px;
    font-size: var(--text-sm);
  }
}
/* On a phone the links spread across the whole row, the first one's words
   on the left gutter and the last one's on the right, under the toggle's
   edge, so the row sits between even margins. Below 375px four of them
   would crowd at that size, so they take the next size down. */
@media (max-width: 29.9375em) {
  .site-nav ul { justify-content: space-between; margin-inline: -6px; }
  .site-nav a { padding-inline: 6px; }
}
@media (max-width: 23.375em) {
  .site-nav a { font-size: var(--text-xs); }
}

/* Below 640px each file's Download goes under its text, full width. */
@media (max-width: 39.9375em) {
  .hero { padding-top: 20px; }
  .lede { font-size: 1rem; }

  .file {
    grid-template-columns: minmax(0, 1fr);
    padding: 20px 20px 14px;
  }
  .file-dl { grid-column: 1; grid-row: 2; width: 100%; min-width: 0; margin-top: 16px; }
  .file-dl.file-link { justify-self: start; width: auto; margin-top: 10px; }
  .file-check { grid-row: 3; }
  .file.is-checksums { padding-bottom: 20px; }
  .dl-note { padding: 20px; }
  .dl-note .btn { width: 100%; }
  .reqs > div { grid-template-columns: minmax(0, 1fr); row-gap: 2px; }
  /* Two columns where each has 8rem, one with very large text. */
  .footer-nav { grid-template-columns: repeat(auto-fit, minmax(8rem, 1fr)); column-gap: 24px; }
}

/* The size takes its own line under the button's label on narrow phones,
   without the dot. */
@container (max-width: 21em) {
  .btn-download-sub .sub-size { display: block; }
  .btn-download-sub .sub-dot { display: none; }
}
/* With very large text in a narrow column (200% at 390px), what can't fit
   on one line wraps rather than run off the page. */
@container (max-width: 12.5em) {
  .actions .btn { white-space: normal; text-align: left; }
}
/* The source's address breaks after its slashes only once its line can't
   hold it whole: about 15.1em of address, which under the big button has a
   line of its own, and in Download for Windows shares one with GitHub's
   mark. */
@container (max-width: 15.25em) {
  .dest .mono { white-space: normal; overflow-wrap: anywhere; }
  .dest wbr { display: inline; }
}
@container (max-width: 17em) {
  .dl-from .mono { white-space: normal; overflow-wrap: anywhere; }
  .dl-from wbr { display: inline; }
}
/* In a narrow row the PowerShell line's help would squeeze into a column
   beside its Copy, so it takes the row's width and Copy goes under it, at
   the right, over the line it copies. */
@container dl (max-width: 32em) {
  .check-head { flex-wrap: wrap; }
  .check-head .copy { margin-left: auto; }
}
@container dl (max-width: 12.5em) {
  .check-value .nowrap { display: inline-block; white-space: normal; }
}

/* Below 360px the headline steps down so "Your OBS mic chain," still fits
   on its line at 320px, and the section links drop "Download": the hero's
   own link to the downloads is on screen, and the other three then fit
   without scrolling. */
@media (max-width: 22.4375em) {
  .pitch h1 { font-size: 2rem; }
  .nav-download { display: none; }
  /* Source becomes its mark alone, square like the toggle beside it, so
     the lockup keeps its full 140px. Its name stays for screen readers. */
  .source-link { width: var(--tool); padding: 0; justify-content: center; }
  .source-label {
    position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0;
    overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
  }
}

/* ---------- Touch ---------- */

/* On a touch screen every target is at least 44px tall. Controls and the
   links in the header and footer lists grow in the flow. Every other link
   keeps its line and gets a larger invisible hit area, 44px tall and
   centred on the link. For that the link is one box: it moves to the next
   line whole rather than breaking inside, unless it's wider than the line,
   and its words, spaces and underline stay as they are. */
@media (pointer: coarse) {
  /* The 32px lockup grows to a 44px target inside the header row. */
  .brand { padding-block: 6px; }
  .btn, .copy { min-height: 44px; }
  .check-head { min-height: 44px; }
  .file-check > summary { --summary-h: 44px; }
  .site-nav a { display: flex; align-items: center; min-height: 44px; }
  .header-tools { --tool: max(44px, 1.125rem + 12px); }
  :root { --video-btn: 44px; }

  .hero-more a,
  .hero-needs a,
  .rs-line a,
  .dl-meta a,
  .reqs a,
  .about-block > p a,
  .build-body a,
  .step-links a,
  .more a,
  .sp-note a,
  .footnote a,
  .qa-item p a,
  .check-after a,
  .footer-legal a { position: relative; display: inline-block; }
  .hero-more a::after,
  .hero-needs a::after,
  .rs-line a::after,
  .dl-meta a::after,
  .reqs a::after,
  .about-block > p a::after,
  .build-body a::after,
  .step-links a::after,
  .more a::after,
  .sp-note a::after,
  .footnote a::after,
  .qa-item p a::after,
  .check-after a::after,
  .footer-legal a::after { content: ""; position: absolute; inset: calc(50% - 22px) -4px; }
  /* A step's links, side by side, keep their areas apart if they wrap. */
  .step-links { row-gap: calc(44px - 1.55em); }

  .footer-nav ul { margin-top: 4px; }
  .footer-nav li + li { margin-top: 0; }
  .footer-nav a { display: block; padding-block: max(0px, (44px - 1.55em) / 2); }
}
