/* install.sinlu.app: the installer in the website's look (apps/web/src/
   styles.css): the app's black, one warm light at the top, Inter at 800
   with tight tracking, white pill buttons, amber for what is happening now
   and green for what is done. Dark only, like the site. Layout follows
   Round 3 · C of the "Sinlú Flasher" canvas (owner pick, 2026-11-07). */

@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 400 800;
  font-display: swap;
  src: url(assets/fonts/inter-latin.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

:root {
  --bg: #0A0B0D;
  --bg-1: #101114;
  --bg-2: #16171B;
  --ink: #EEF0F3;
  --ink-hi: #F7F8FA;
  --ink-2: #C7C9CF;
  --muted: #8E939B;
  --muted-2: #5F6570;
  --border: rgba(255, 255, 255, 0.07);
  --border-2: rgba(255, 255, 255, 0.12);
  --border-3: rgba(255, 255, 255, 0.2);
  --accent: #FF9E24;
  --accent-soft: #FFB861;
  --accent-on: #16171B;
  --green: #30D158;
  --max: 1240px;
  --gutter: clamp(20px, 4.4vw, 48px);
  --ease-out: cubic-bezier(.16, 1, .3, 1);
  --font: "Inter", system-ui, -apple-system, "Segoe UI", sans-serif;
  color-scheme: dark;
}

*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; text-size-adjust: 100%; background: var(--bg); }
html, body { margin: 0; padding: 0; }
body {
  min-height: 100vh;
  background: var(--bg);
  color: var(--ink);
  font-family: var(--font);
  font-size: 17px;
  line-height: 1.6;
  letter-spacing: -0.006em;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  overflow-x: clip;
}
/* The site's film grain: large dark gradients band without it. */
body::after {
  content: "";
  position: fixed;
  inset: 0;
  z-index: 50;
  pointer-events: none;
  opacity: 0.05;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix values='0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0.55 0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
}

h1, h2, p { margin: 0; }
p { text-wrap: pretty; }
img, svg { display: block; max-width: 100%; }
button { font: inherit; color: inherit; background: none; border: 0; padding: 0; cursor: pointer; -webkit-tap-highlight-color: transparent; }
a { color: inherit; text-decoration: none; }
::selection { background: var(--accent); color: var(--accent-on); }
:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; border-radius: 6px; }
[tabindex="-1"]:focus { outline: none; }

.skip-link {
  position: fixed; top: 12px; left: 12px; z-index: 100;
  padding: 10px 14px; border-radius: 10px; background: var(--bg-2); color: var(--ink);
  transform: translateY(-180%);
}
.skip-link:focus { transform: none; }
.noscript { padding: 48px var(--gutter); color: var(--ink-2); }
.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
}
/* A hyphenated name part (ESP32-C6) never breaks at its hyphen. */
.nobr { white-space: nowrap; }
.ok-mark { display: flex; flex: none; color: var(--green); }

.page { position: relative; min-height: 100vh; overflow: hidden; }
.page::before {
  content: "";
  position: absolute;
  left: 50%; top: -420px;
  width: 1400px; height: 800px;
  transform: translateX(-50%);
  border-radius: 50%;
  background: radial-gradient(closest-side, rgba(255, 158, 36, 0.16), rgba(255, 158, 36, 0.05) 55%, rgba(255, 158, 36, 0) 100%);
  pointer-events: none;
}

/* The bar across the top when this browser can't install (supportBar in
   app.js): the page's amber as a tint over the black, not a warning yellow,
   with the one fact and the one thing to do. */
.support-bar {
  position: relative;
  background: rgba(255, 158, 36, 0.13);
  border-bottom: 1px solid rgba(255, 158, 36, 0.3);
}
.support-bar-in {
  display: grid; grid-template-columns: auto minmax(0, auto) auto; justify-content: center; align-items: center; gap: 10px 12px;
  max-width: var(--max); margin: 0 auto;
  padding: 12px var(--gutter);
}
.support-bar-icon { display: flex; color: var(--accent); }
.support-bar-icon svg { width: 20px; height: 20px; }
.support-bar-text { font-size: 15px; line-height: 1.45; color: var(--ink-2); letter-spacing: -0.008em; }
.support-bar-text strong { font-weight: 600; color: var(--ink-hi); }
.support-bar-copy {
  height: 34px; padding: 0 14px; border-radius: 999px; white-space: nowrap;
  border: 1px solid rgba(255, 158, 36, 0.5); color: var(--ink-hi);
  font-size: 13px; font-weight: 600; letter-spacing: -0.005em;
  transition: background-color .3s var(--ease-out), color .3s var(--ease-out), border-color .3s var(--ease-out);
}
@media (hover: hover) { .support-bar-copy:hover { background: var(--accent); border-color: var(--accent); color: var(--accent-on); } }

/* Header */
.top {
  position: relative;
  display: flex; align-items: center; justify-content: space-between; gap: 16px;
  padding: 22px var(--gutter);
}
.brand { display: flex; align-items: center; gap: 14px; min-height: 44px; }
.brand img { height: 24px; width: auto; }
.brand-sep { flex: none; width: 1px; height: 18px; background: var(--border-3); }
.brand-tool { font-size: 15px; font-weight: 600; color: var(--ink-2); letter-spacing: -0.01em; }
.top-end { display: flex; align-items: center; gap: 12px; }
/* The way back to the website, beside the language switch: the logo goes
   there too, but nothing says so (owner 2026-11-11). */
.home-link {
  display: inline-flex; align-items: center; gap: 3px; min-height: 44px; padding: 0 6px;
  color: var(--ink-2); font-size: 14px; font-weight: 600; white-space: nowrap;
  transition: color .3s var(--ease-out);
}
.home-link .out { width: 16px; height: 16px; color: var(--muted); transition: transform .35s var(--ease-out), color .3s var(--ease-out); }
.home-link .house { display: none; }
@media (hover: hover) {
  .home-link:hover { color: var(--ink-hi); }
  .home-link:hover .out { transform: translate(2px, -2px); color: var(--ink-2); }
}
/* On a phone the words sat against "Installer" and read as one name, so
   the link is a round house button, the language switch's size, and the
   words stay for screen readers. */
@media (max-width: 520px) {
  .home-link {
    justify-content: center; width: 46px; height: 46px; padding: 0; flex: none;
    border-radius: 999px; border: 1px solid var(--border-2); background: rgba(255, 255, 255, 0.03);
  }
  .home-link .out { display: none; }
  .home-link .house { display: block; }
  .home-text {
    position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
    overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
  }
}
/* Under 360 px it would sit on "Installer": the footer's sinlu.app and the
   logo still go home. */
@media (max-width: 359px) { .home-link { display: none; } }
.sim-tag {
  display: inline-block; margin-bottom: 18px; white-space: nowrap;
  font-size: 12px; font-weight: 600; letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--accent); padding: 6px 10px; border-radius: 999px; border: 1px solid rgba(255, 158, 36, 0.4);
}
.lang {
  display: flex; gap: 2px; padding: 2px;
  border-radius: 999px; border: 1px solid var(--border-2); background: rgba(255, 255, 255, 0.03);
}
.lang button {
  height: 40px; min-width: 44px; padding: 0 10px; border-radius: 999px;
  color: var(--muted); font-size: 13px; font-weight: 600; letter-spacing: 0.04em;
  transition: background-color .3s var(--ease-out), color .3s var(--ease-out);
}
.lang button[aria-pressed="true"] { background: var(--ink-hi); color: var(--bg); }
@media (hover: hover) { .lang button[aria-pressed="false"]:hover { color: var(--ink-hi); } }

/* Layout */
.wrap {
  position: relative;
  width: 100%; max-width: var(--max); margin: 0 auto;
  padding: 44px var(--gutter) 72px;
}
.h1 {
  font-size: clamp(2.4rem, 1rem + 3.4vw, 3.8rem);
  line-height: 0.98; font-weight: 800; letter-spacing: -0.045em;
  color: var(--ink-hi); text-wrap: balance;
}
.h1 span { display: block; }
.h1 .accent { color: var(--accent); }
.h2 {
  font-size: clamp(1.6rem, 1.1rem + 1.2vw, 2.15rem);
  line-height: 1.08; font-weight: 800; letter-spacing: -0.04em;
  color: var(--ink-hi); text-wrap: balance;
}
.h2-sm { font-size: 28px; letter-spacing: -0.035em; line-height: 1.15; }
.body { font-size: 17px; line-height: 1.5; color: var(--ink-2); }
.muted { color: var(--muted); }

/* Stepper */
/* One line on every screen: from 561 px it's dot, label, line; narrower,
   each label sits under its dot (the phone rules below). It never wraps
   onto a second row, which read as broken (owner 2026-11-07). */
.stepper {
  list-style: none; margin: 36px 0 0; padding: 0;
  display: flex; align-items: center; gap: 14px;
}
.st { display: flex; align-items: center; gap: 12px; min-width: 0; flex: 1 1 auto; }
.st:last-child { flex: none; }
.st-dot {
  flex: none; width: 34px; height: 34px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  border: 2px solid var(--border-3); color: var(--muted);
  font-size: 13px; font-weight: 600; font-variant-numeric: tabular-nums;
}
.st-label { flex: none; font-size: 15px; font-weight: 600; color: var(--muted); letter-spacing: -0.01em; }
.st-line { flex: 1; min-width: 24px; height: 2px; border-radius: 1px; background: var(--border-2); }
.st:last-child .st-line { display: none; }
.st-back {
  display: flex; align-items: center; gap: 12px; min-height: 44px; margin: -5px 0; padding: 0;
  border-radius: 999px;
}
.st-back .st-label {
  text-decoration: underline; text-decoration-color: var(--border-3); text-underline-offset: 4px;
  transition: text-decoration-color .25s var(--ease-out);
}
@media (hover: hover) { .st-back:hover .st-label { text-decoration-color: currentColor; } }
.st.is-now .st-dot { border-color: var(--accent); color: var(--accent); background: rgba(255, 158, 36, 0.08); }
.st.is-now .st-label, .st.is-done .st-label { color: var(--ink-hi); }
.st.is-done .st-dot { border-color: var(--green); color: var(--green); }
.st.is-done .st-line { background: var(--green); }

.stage { margin-top: 44px; }

/* Step 1: cards */
.cards {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); gap: 16px;
  margin-top: 20px;
}
/* Verified or Beta: a pill in each card's top left, and on the board's
   page beside what it means. Green is the page's "done" colour; Beta
   stays grey, since amber means what is happening now. */
.tag {
  display: inline-flex; align-items: center; gap: 4px; flex: none;
  padding: 3px 9px; border-radius: 999px; border: 1px solid var(--border-2);
  background: rgba(255, 255, 255, 0.05); color: var(--ink-2);
  font-size: 12px; font-weight: 600; line-height: 1.4; letter-spacing: 0;
}
.tag.is-verified { color: var(--green); background: rgba(48, 209, 88, 0.1); border-color: rgba(48, 209, 88, 0.28); }
.tag svg { margin-left: -2px; }
.card-tag { position: absolute; top: 14px; left: 14px; z-index: 1; }
.card {
  position: relative;
  display: flex; flex-direction: column; overflow: hidden; text-align: left;
  border-radius: 24px; background: var(--bg-1); border: 1px solid var(--border);
  box-shadow: 0 1px 0 rgba(255, 255, 255, 0.04) inset;
  transition: border-color .35s var(--ease-out), transform .2s var(--ease-out);
}
@media (hover: hover) { .card:hover { border-color: var(--border-3); } }
.card:active { transform: scale(0.985); }
.card-photo {
  display: flex; align-items: center; justify-content: center; width: 100%; height: 190px;
  background: radial-gradient(closest-side, rgba(255, 158, 36, 0.13), rgba(255, 158, 36, 0)) center / 220px 190px no-repeat;
}
.card-photo img { height: 140px; width: auto; }
/* The XIAOs and the C3 SuperMini are ~21 mm boards beside the 51 mm Pico:
   shown larger than true scale so their parts read, smaller than the Pico. */
.card-photo.is-small img { height: 124px; }
.chosen-tile.is-small img { height: 42px; }
.illus-xiao img { height: 236px; width: auto; }
/* The DevKit V1 (51 mm, like the Pico) at the Pico's card scale; on the
   connect step a little larger than the small boards, not true scale. */
.card-photo.is-long img { height: calc(136px * var(--k, 1)); }
.chosen-tile.is-long img { height: calc(44px * var(--k, 1)); }
.illus-xiao.is-long img { height: calc(280px * var(--k, 1)); }
/* A card for two boards (the DevKit V1 and DevKitC) shows both side by side.
   The renders share one scale (482 px across a 28 mm board), so the DevKitC,
   809 px tall to the V1's 780, draws that much taller. A rule, not a style
   attribute: the page's CSP has no 'unsafe-inline' for styles. */
.is-pair { gap: 14px; }
.is-pair img + img { --k: 1.0372; }
.chosen-tile.is-pair { width: 64px; gap: 3px; }
.illus-xiao.is-pair { gap: 28px; }
.illus-xiao.is-pair img { height: calc(240px * var(--k, 1)); }
.test-error-detail { margin-top: 14px; font-size: 13px; color: var(--muted); overflow-wrap: anywhere; }
.card-row { display: flex; align-items: center; gap: 12px; padding: 14px 18px 18px; }
.card-text { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 3px; }
.card-name { font-size: 17px; font-weight: 600; line-height: 1.25; color: var(--ink-hi); letter-spacing: -0.015em; }
.card-sub { font-size: 14px; color: var(--muted); }
.card .chev { flex: none; color: var(--muted-2); transition: transform .35s var(--ease-out), color .35s var(--ease-out); }
@media (hover: hover) { .card:hover .chev { transform: translateX(3px); color: var(--ink-2); } }

/* Where to buy: the board's page has a quiet "Don't have one yet? Where to
   buy" line; it opens a modal. A centred panel on wide screens, a sheet from
   the bottom on phones, in the page's own dark surface: the board's render
   with its amber glow, the title, then one hairline row out to the store. */
html:has(.modal[open]) { overflow: hidden; }
.modal {
  width: min(440px, calc(100% - 32px)); max-width: none; max-height: calc(100dvh - 48px);
  margin: auto; padding: 0; overflow: auto;
  border-radius: 28px; background: var(--bg-1); border: 1px solid var(--border-2); color: var(--ink);
  box-shadow: 0 1px 0 rgba(255, 255, 255, 0.05) inset, 0 30px 80px rgba(0, 0, 0, 0.6);
}
.modal::backdrop { background: rgba(5, 6, 8, 0.72); backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px); }
.modal[open] { animation: modal-in .35s var(--ease-out); }
.modal[open]::backdrop { animation: fade-in .3s var(--ease-out); }
@keyframes modal-in { from { opacity: 0; transform: translateY(12px) scale(0.985); } }
@keyframes fade-in { from { opacity: 0; } }
.modal-in { position: relative; padding: 8px 24px 26px; }
.modal-close {
  position: absolute; top: 12px; right: 12px; z-index: 1;
  display: flex; align-items: center; justify-content: center; width: 44px; height: 44px;
  border-radius: 999px; color: var(--ink-2); background: rgba(255, 255, 255, 0.05);
}
@media (hover: hover) { .modal-close:hover { color: var(--ink-hi); background: rgba(255, 255, 255, 0.1); } }
.modal-photo { height: 200px; }
.modal-title { margin-top: 4px; font-size: 24px; font-weight: 700; line-height: 1.2; letter-spacing: -0.03em; color: var(--ink-hi); }
.buy-row {
  display: flex; align-items: center; gap: 14px; min-height: 68px; margin-top: 16px; padding: 8px 2px;
  border-top: 1px solid var(--border); border-bottom: 1px solid var(--border);
}
.buy-text { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.buy-name { font-size: 16px; font-weight: 600; color: var(--ink-hi); letter-spacing: -0.015em; }
.buy-sub { font-size: 14px; color: var(--muted); }
.buy-row .out { flex: none; color: var(--muted-2); transition: transform .35s var(--ease-out), color .35s var(--ease-out); }
@media (hover: hover) {
  .buy-row:hover .out { transform: translate(2px, -2px); color: var(--ink-2); }
  .buy-row:hover .buy-name { text-decoration: underline; text-decoration-color: var(--border-3); text-underline-offset: 4px; }
}
.buy-note { margin-top: 12px; font-size: 13px; line-height: 1.45; color: var(--muted); }
.buy-ask { margin-top: 22px; }
@media (max-width: 560px) {
  .modal {
    width: 100%; max-height: calc(100dvh - 24px); margin: auto 0 0;
    border-radius: 28px 28px 0 0; border-bottom: 0;
    padding-bottom: env(safe-area-inset-bottom);
  }
  @keyframes modal-in { from { transform: translateY(40px); opacity: 0; } }
  .modal-in { padding: 4px 20px 24px; }
  .modal-photo { height: 176px; }
}

/* Step 2: connect */
.connect { display: flex; flex-wrap: wrap; align-items: center; gap: 32px 56px; }
.connect-copy { flex: 1 1 380px; max-width: 540px; min-width: 0; }
.chosen {
  display: flex; align-items: center; gap: 14px; padding: 6px;
  border-radius: 18px; background: var(--bg-1); border: 1px solid var(--border);
}
.chosen-tile {
  flex: none; width: 44px; height: 56px; border-radius: 12px; background: var(--bg);
  display: flex; align-items: center; justify-content: center;
}
.chosen-tile img { height: 46px; width: auto; }
/* A board found by its chip: a chip in place of a render that may not look like it. */
.chosen-tile.is-chip { color: var(--muted); }
.chosen-text { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; padding-right: 10px; }
.chosen-name { font-size: 16px; font-weight: 600; color: var(--ink-hi); letter-spacing: -0.015em; }
.chosen-sub { font-size: 13px; color: var(--muted); }
.chosen-meta {
  flex: none; display: flex; flex-direction: column; align-items: flex-end; gap: 2px;
  padding: 0 12px 0 0; text-align: right;
}
.chosen-meta-label { font-size: 13px; color: var(--muted); }
.chosen-meta-value { font-size: 16px; font-weight: 600; color: var(--ink-hi); font-variant-numeric: tabular-nums; letter-spacing: -0.01em; }
/* The board's tag and what it means, across the top of its picture
   (.illus.is-board). */
.board-status {
  display: flex; align-items: center; gap: 10px; margin: 14px 0 0 6px;
  font-size: 14px; line-height: 1.4; color: var(--muted);
}
.connected {
  display: flex; align-items: center; gap: 8px; margin-top: 28px;
  font-size: 15px; font-weight: 600; color: var(--green);
}
.connected + .h2 { margin-top: 10px; }
.connect-copy > .connected:first-child { margin-top: 0; }
.connect .h2 { margin-top: 28px; }
.connect .body { margin-top: 14px; }
.fw-line {
  display: flex; align-items: center; gap: 8px; margin-top: 14px;
  font-size: 14px; line-height: 1.45; color: var(--muted);
}
.fw-line[hidden] { display: none; }
.fw-line svg { flex: none; }
.fw-line.is-error { color: var(--ink-2); }
.fw-retry, .buy-open {
  position: relative;
  color: var(--ink-hi); font-weight: 600; text-decoration: underline;
  text-decoration-color: var(--border-3); text-underline-offset: 4px;
}
/* The only way back from a failed download: a finger-sized hit area. */
.fw-retry::after, .buy-open::after { content: ""; position: absolute; inset: -12px -8px; }
.actions { display: flex; flex-wrap: wrap; align-items: center; gap: 12px 18px; margin-top: 26px; }
.hint { margin-top: 14px; font-size: 14px; line-height: 1.5; color: var(--muted); max-width: 46ch; }
.hint-more { color: var(--ink-2); }

.illus {
  flex: 1 1 380px; min-width: 0; height: 320px;
  border-radius: 26px; background: var(--bg-1); border: 1px solid var(--border);
  display: flex; align-items: center; justify-content: center; overflow: hidden;
}
/* The board's picture: its tag in the top left, as on its card a step
   back, and the render centred in the room under it. */
.illus.is-board { flex-direction: column; align-items: stretch; height: 344px; }
/* Beside it, the board's row lines up with its top edge (owner 2026-11-11:
   centred, the row with the firmware version sat lower than the box). */
@media (min-width: 641px) { .connect:has(> .illus.is-board) { align-items: flex-start; } }
.illus.is-board .board-status { flex: none; margin: 0; padding: 18px 20px 0; }
.illus-art { flex: 1; min-height: 0; display: flex; align-items: center; justify-content: center; }
/* The board on its side with a micro USB cable coming in from the left,
   a white ring on its button: one render, assets/pico-2w-plugged.webp
   (scripts/make-boards.py). The box is the plug and the board (from the
   end of the plug's strain relief, 54.381% into the picture, to its right
   edge) and stays centred; the picture's cable hangs out of it to the left
   and runs off the frame. Everything inside is in % of that box, so it
   scales as one piece. */
.illus-board { position: relative; width: min(88%, 470px); aspect-ratio: 4.049; }
.illus-board img { position: absolute; left: -119.21%; top: 0; width: 219.21%; height: 100%; max-width: none; }
/* Centred on the render's button (75.883%, 59.963% of the picture). */
.illus-board .ring {
  position: absolute; left: 47.13%; top: 59.96%; width: 7.7%; aspect-ratio: 1; border-radius: 50%;
  transform: translate(-50%, -50%);
  border: 2.5px solid #fff;
  box-shadow: 0 0 0 6px rgba(255, 255, 255, 0.14), 0 0 22px rgba(255, 255, 255, 0.4);
  animation: ring 2.4s var(--ease-out) infinite;
}
@keyframes ring {
  0%, 100% { box-shadow: 0 0 0 6px rgba(255, 255, 255, 0.14), 0 0 22px rgba(255, 255, 255, 0.4); }
  50% { box-shadow: 0 0 0 10px rgba(255, 255, 255, 0.06), 0 0 30px rgba(255, 255, 255, 0.55); }
}
/* The done screen's picture (app.js nextPicture): what comes next. The
   board, plugged in, and a phone lying beside it, two renders from one
   scene at 8 px per mm (scripts/make-boards.py done-*, phone); here at
   --u px per mm, the long boards at the phone's scale and the small ones
   1.4 times it so their light reads. The phone runs off the box's bottom
   and fades there; the board's cable runs off its top. */
.illus.next { position: relative; display: block; height: 340px; --u: 2.75px; --cycle: 9s; --delay: 1s; }
.next-scene {
  position: absolute; inset: 0;
  -webkit-mask-image: linear-gradient(#000 calc(100% - 52px), transparent);
  mask-image: linear-gradient(#000 calc(100% - 52px), transparent);
}
.next-board {
  position: absolute; left: calc(50% - 37 * var(--u)); top: 74%; translate: -50% -100%;
  --mm: 21.38; --s: 1;
  animation: next-in 560ms var(--ease-out) backwards;
}
.next-board img { display: block; width: calc(var(--mm) * var(--u) * var(--s)); height: auto; }
.next-board.is-esp32-devkit-v1 { --mm: 28.75; }
.next-board.is-small { --mm: 18.25; --s: 1.4; top: 70%; }
.next-board.is-esp32c3-supermini { --mm: 18.38; }
.next-phone {
  position: absolute; left: calc(50% - 6 * var(--u)); top: 16px; width: calc(73.75 * var(--u));
  animation: next-phone-in 720ms var(--ease-out) 160ms backwards;
}
.next-phone > img { display: block; width: 100%; height: auto; }
.is-phone-only .next-phone { left: 50%; translate: -50% 0; }
@keyframes next-in { from { opacity: 0; } }
@keyframes next-phone-in { from { opacity: 0; transform: translateY(12px); } }

/* The board's own light, on the render's LED (make-boards.py prints where):
   the firmware's quick pairing blink, 150 ms on, 150 ms off, with a short
   ramp so it reads as a light, the way the app draws it (DeviceCaseBlink
   useQuickBlink); steady once the phone shows Online. A near-white core in
   a strong halo of the light's colour: a green glow alone vanishes into
   the green board. Reduced motion ends on the still: blinking, lit. */
.next-led {
  position: absolute; width: max(5px, calc(1.05 * var(--u) * var(--s))); aspect-ratio: 1; translate: -50% -50%;
  --core: #F2FFF5; --in: 120 255 160; --glow: 48 209 88;
}
.next-led.is-yellow { --core: #FFFBEA; --in: 255 232 120; --glow: 255 204 0; }
.next-led.is-orange { --core: #FFF5EA; --in: 255 196 120; --glow: 255 149 0; }
.next-led.is-blue { --core: #EEF5FF; --in: 120 180 255; --glow: 10 132 255; }
.next-led > span { position: absolute; inset: 0; }
.next-led > span::before {
  content: ""; position: absolute; inset: 0; border-radius: 50%;
  background: var(--core);
  box-shadow:
    0 0 3px 2px rgb(var(--in)),
    0 0 10px 5px rgb(var(--glow) / 0.85),
    0 0 26px 12px rgb(var(--glow) / 0.45);
}
.next-led-blink::before { animation: quick-blink 300ms linear infinite; }
.next-led-blink { animation: next-led-blink var(--cycle) ease-in-out var(--delay) infinite backwards; }
.next-led-steady { opacity: 0; animation: next-led-steady var(--cycle) ease-in-out var(--delay) infinite backwards; }
.is-pico2w .next-led { left: 23.622%; top: 70.388%; }
.is-xiao-esp32c6 .next-led { left: 82.214%; top: 86.42%; }
.is-xiao-esp32s3 .next-led { left: 81.392%; top: 85.988%; }
.is-esp32c3-supermini .next-led { left: 72.53%; top: 90.471%; }
.is-esp32-devkit-v1 .next-led { left: 33.901%; top: 80.502%; }
@keyframes quick-blink {
  0%, 42% { opacity: 1; }
  50%, 92% { opacity: 0.06; }
  100% { opacity: 1; }
}
@keyframes next-led-blink { 0%, 54% { opacity: 1; } 56%, 94% { opacity: 0; } 96%, 100% { opacity: 1; } }
@keyframes next-led-steady { 0%, 54% { opacity: 0; } 56%, 94% { opacity: 1; } 96%, 100% { opacity: 0; } }

/* A board's other lights (devices.js done), on the render's LEDs (their
   positions follow from the user light's: 8 px per mm, the camera 56°
   up). bootLight: the XIAOs' charge LED, across the USB from the user
   light, red as the board powers up and then out for good; shown once, as
   the board appears, not on every turn of the 9 s clock (the board boots
   once), and reduced motion skips it (it ends dark at once). powerLight:
   the SuperMini's and the DevKit's red power LED, on as long as there is
   power. */
.next-boot, .next-power {
  position: absolute; width: max(5px, calc(1.05 * var(--u) * var(--s))); aspect-ratio: 1; translate: -50% -50%;
  border-radius: 50%; background: var(--core);
  box-shadow:
    0 0 3px 2px rgb(var(--in)),
    0 0 10px 5px rgb(var(--glow) / 0.85),
    0 0 26px 12px rgb(var(--glow) / 0.45);
}
.next-boot { opacity: 0; animation: next-boot 3.2s linear both; }
.next-boot.is-red, .next-power.is-red { --core: #FFF1EF; --in: 255 150 140; --glow: 255 59 48; }
.is-xiao-esp32c6 .next-boot { left: 17.556%; top: 86.42%; }
.is-xiao-esp32s3 .next-boot { left: 18.104%; top: 85.988%; }
.is-esp32c3-supermini .next-power { left: 21.918%; top: 87.579%; }
.is-esp32-devkit-v1 .next-power { left: 65.901%; top: 80.502%; }
@keyframes next-boot { 0%, 96% { opacity: 1; } 100% { opacity: 0; } }

/* The phone's screen, over the render's black glass (the display's corners
   at 4.847%, 1.664% and 95.153%, 94.602% of the picture). Laid out flat at
   the iPhone 17 Pro's 402 x 874 pt (--pt), then squashed to the camera's
   56°, one transform, so it lies in the phone. The app's Bluetooth setup
   sheet by night, at its own measures (SensorSetupFrame, SensorMapHero,
   BLESetupSheet): the sheet 72 pt down over black, the map 371 pt tall,
   the panel rising over it from 339 pt on its 32 pt curve, all below in pt
   from the sheet's top. */
.next-screen { position: absolute; left: 4.847%; top: 1.664%; width: 90.306%; container-type: inline-size; }
.ns {
  --pt: calc(100cqw / 402);
  position: relative; width: 100%; aspect-ratio: 402 / 874; overflow: hidden;
  transform: scaleY(0.829); transform-origin: top;
  border-radius: calc(60 * var(--pt));
  background: #000; color: #EEF0F3; line-height: 1.3; letter-spacing: 0;
}
.ns svg { display: block; }
.ns p { margin: 0; }
.ns-time { position: absolute; left: 0; width: calc(138 * var(--pt)); top: calc(18.5 * var(--pt)); text-align: center; font-size: calc(17 * var(--pt)); line-height: calc(22 * var(--pt)); font-weight: 600; color: #fff; }
.ns-island { position: absolute; left: 50%; top: calc(11 * var(--pt)); width: calc(126 * var(--pt)); height: calc(37 * var(--pt)); translate: -50% 0; border-radius: 999px; background: #000; }
.ns-status { position: absolute; left: calc(264 * var(--pt)); width: calc(138 * var(--pt)); top: calc(21 * var(--pt)); display: flex; justify-content: center; align-items: center; gap: calc(6 * var(--pt)); color: #fff; }
.ns-status-wifi { width: calc(17 * var(--pt)); height: calc(17 * var(--pt)); }
.ns-battery { position: relative; width: calc(26 * var(--pt)); height: calc(12.5 * var(--pt)); border: calc(1.3 * var(--pt)) solid rgba(255, 255, 255, 0.4); border-radius: calc(4 * var(--pt)); }
.ns-battery::after { content: ""; position: absolute; inset: calc(1.5 * var(--pt)); right: 30%; border-radius: calc(2 * var(--pt)); background: #fff; }
.ns-sheet { position: absolute; left: 0; right: 0; top: calc(72 * var(--pt)); bottom: 0; overflow: hidden; background: #0A0B0D; border-radius: calc(38 * var(--pt)) calc(38 * var(--pt)) 0 0; }
.ns-sheet > * { position: absolute; }
.ns-map { left: 0; top: 0; width: 100%; height: calc(371 * var(--pt)); overflow: hidden; background: #3E434C; }
.ns-map img { display: block; width: 100%; height: calc(389 * var(--pt)); }
.ns-map.is-illustrated {
  background-image:
    linear-gradient(152deg, transparent 35%, #292d35 35% 37%, transparent 37% 65%, #323740 65% 66%, transparent 66%),
    repeating-linear-gradient(27deg, transparent 0 19%, #333842 19% 20%, transparent 20% 34%);
}
/* The area's circle, 50.65 pt round its centre at (201, 194.5): a fill at
   0.2 and a 2 pt edge at 0.6 of its colour, grey while it waits and green
   once online, with the online ring growing out of it every 2 s. */
.ns-area {
  left: calc(150.35 * var(--pt)); top: calc(143.85 * var(--pt)); width: calc(101.3 * var(--pt)); height: calc(101.3 * var(--pt));
  border-radius: 50%; border: calc(2 * var(--pt)) solid rgba(142, 142, 147, 0.6); background: rgba(142, 142, 147, 0.2);
  animation: ns-page-a var(--cycle) ease-in-out var(--delay) infinite backwards;
}
.ns-area.is-online { border-color: rgba(48, 209, 88, 0.6); background: rgba(48, 209, 88, 0.2); opacity: 0; animation-name: ns-page-b; }
.ns-area-pulse {
  position: absolute; inset: calc(-2 * var(--pt)); border-radius: 50%;
  border: calc(2 * var(--pt)) solid rgba(48, 209, 88, 0.6); background: rgba(48, 209, 88, 0.3);
  animation: ns-ring 2s cubic-bezier(.33, 1, .68, 1) infinite;
}
.ns-pin { left: calc(177 * var(--pt)); top: calc(137.5 * var(--pt)); width: calc(48 * var(--pt)); height: calc(57 * var(--pt)); filter: drop-shadow(0 calc(3 * var(--pt)) calc(7 * var(--pt)) rgba(0, 0, 0, 0.28)); }
.ns-pin > svg:first-child { width: 100%; height: 100%; }
.ns-pin-glyph { position: absolute; left: calc(12 * var(--pt)); top: calc(12 * var(--pt)); width: calc(24 * var(--pt)); height: calc(24 * var(--pt)); color: #EEF0F3; }
.ns-pin-dot {
  position: absolute; left: calc(33 * var(--pt)); top: calc(-7 * var(--pt)); width: calc(22 * var(--pt)); height: calc(22 * var(--pt)); border-radius: 50%; background: #14161B;
  opacity: 0; animation: ns-page-b var(--cycle) ease-in-out var(--delay) infinite backwards;
}
.ns-pin-dot::after { content: ""; position: absolute; inset: calc(4 * var(--pt)); border-radius: 50%; background: #30D158; }
.ns-grabber { left: 50%; top: calc(8 * var(--pt)); width: calc(40 * var(--pt)); height: calc(5 * var(--pt)); translate: -50% 0; border-radius: calc(3 * var(--pt)); background: #5F6570; }
/* The back button: Liquid Glass over the map, a grey disc with a lighter rim. */
.ns-back {
  left: calc(16 * var(--pt)); top: calc(30 * var(--pt)); width: calc(40 * var(--pt)); height: calc(40 * var(--pt)); border-radius: 50%;
  display: grid; place-items: center; color: #EEF0F3;
  background: rgba(58, 62, 70, 0.62); box-shadow: inset 0 0 0 calc(1 * var(--pt)) rgba(255, 255, 255, 0.16), 0 calc(2 * var(--pt)) calc(8 * var(--pt)) rgba(0, 0, 0, 0.25);
  animation: ns-page-a var(--cycle) ease-in-out var(--delay) infinite backwards;
}
.ns-back svg { width: calc(22 * var(--pt)); height: calc(22 * var(--pt)); }
.ns-panel { left: 0; right: 0; top: calc(339 * var(--pt)); bottom: 0; background: #0A0B0D; border-radius: calc(32 * var(--pt)) calc(32 * var(--pt)) 0 0; }
.ns-title { left: calc(34 * var(--pt)); top: calc(367 * var(--pt)); font-size: calc(32 * var(--pt)); line-height: calc(38 * var(--pt)); font-weight: 800; letter-spacing: calc(-0.8 * var(--pt)); }
.ns-find, .ns-done { inset: 0; }
.ns-find { animation: ns-page-a var(--cycle) ease-in-out var(--delay) infinite backwards; }
.ns-done { opacity: 0; animation: ns-page-b var(--cycle) ease-in-out var(--delay) infinite backwards; }
.ns-find > *, .ns-done > * { position: absolute; }
/* Under the name (SetupSteps): a 16 x 5 bar per step, the one it's on in
   ink and the ones ahead in the quieter grey, then "Step 1 of 3 · Device",
   the count muted, the dot quieter, the step's name in ink. */
.ns-line { left: calc(34 * var(--pt)); top: calc(407 * var(--pt)); height: calc(24 * var(--pt)); display: flex; align-items: center; gap: calc(10 * var(--pt)); font-size: calc(17 * var(--pt)); line-height: calc(22 * var(--pt)); color: #8E939B; white-space: nowrap; }
.ns-bars { display: flex; gap: calc(4 * var(--pt)); }
.ns-bars > span { width: calc(16 * var(--pt)); height: calc(5 * var(--pt)); border-radius: calc(2.5 * var(--pt)); background: #5F6570; }
.ns-bars > .is-on { background: #EEF0F3; }
.ns-sep { color: #5F6570; white-space: pre; }
.ns-count b { color: #EEF0F3; font-weight: 600; }
.ns-line.is-done { gap: calc(6 * var(--pt)); color: #30D158; font-weight: 700; }
.ns-done-glyph { width: calc(18 * var(--pt)); height: calc(18 * var(--pt)); }
.ns-sentence { left: calc(34 * var(--pt)); top: calc(445 * var(--pt)); width: calc(334 * var(--pt)); font-size: calc(16 * var(--pt)); line-height: calc(23 * var(--pt)); color: #8E939B; }
.ns-list { left: calc(34 * var(--pt)); top: calc(490 * var(--pt)); width: calc(334 * var(--pt)); height: calc(60 * var(--pt)); }
/* The rows are words alone: the name and code at the list's edge, the state
   at the end (the search's pulse, the chevron, the spinner while it
   connects), the chevron and spinner flush with the list's right edge. */
.ns-row { position: absolute; inset: 0; display: flex; align-items: center; gap: calc(14 * var(--pt)); }
.ns-trail { flex: none; width: calc(15 * var(--pt)); display: flex; align-items: center; justify-content: center; }
.ns-dot { position: relative; width: calc(8 * var(--pt)); height: calc(8 * var(--pt)); border-radius: 50%; background: #0A84FF; }
.ns-dot::after { content: ""; position: absolute; inset: 0; border-radius: 50%; border: calc(2 * var(--pt)) solid #0A84FF; animation: ns-scan 1.5s cubic-bezier(.33, 1, .68, 1) infinite; }
.ns-muted { flex: 1; font-size: calc(17 * var(--pt)); line-height: calc(22 * var(--pt)); color: #8E939B; }
.ns-looking { opacity: 0; animation: ns-looking var(--cycle) ease-in-out var(--delay) infinite backwards; }
.ns-device { animation: ns-device var(--cycle) ease-in-out var(--delay) infinite backwards; }
.ns-device > * { animation: ns-press var(--cycle) ease-in-out var(--delay) infinite backwards; }
/* The name and its code, then Connecting… and the code in their place, so
   the code always follows the word showing. */
.ns-id { flex: 1; min-width: 0; display: grid; }
.ns-id > span { display: flex; align-items: baseline; gap: calc(8 * var(--pt)); }
.ns-id > span, .ns-end > * { grid-area: 1 / 1; }
.ns-name { font-size: calc(17 * var(--pt)); line-height: calc(22 * var(--pt)); font-weight: 600; white-space: nowrap; }
.ns-code { font-size: calc(15 * var(--pt)); color: #8E939B; font-variant-numeric: tabular-nums; }
.ns-end { flex: none; display: grid; align-items: center; justify-items: end; width: calc(20 * var(--pt)); }
.ns-chev { width: calc(15 * var(--pt)); height: calc(15 * var(--pt)); color: #5F6570; }
.ns-spin { width: calc(20 * var(--pt)); height: calc(20 * var(--pt)); color: #8E939B; }
.ns-n1, .ns-chev { animation: ns-before var(--cycle) ease-in-out var(--delay) infinite backwards; }
.ns-n2, .ns-note { opacity: 0; animation: ns-after var(--cycle) ease-in-out var(--delay) infinite backwards; }
.ns-spin { opacity: 0; animation: ns-turn 1s steps(8) infinite, ns-after var(--cycle) ease-in-out var(--delay) infinite backwards; }
.ns-note { left: calc(34 * var(--pt)); top: calc(568 * var(--pt)); display: flex; align-items: center; gap: calc(6 * var(--pt)); font-size: calc(13 * var(--pt)); line-height: calc(18 * var(--pt)); color: #8E939B; white-space: nowrap; }
.ns-islot { flex: none; width: calc(20 * var(--pt)); display: flex; align-items: center; justify-content: center; }
.ns-note-glyph { width: calc(15 * var(--pt)); height: calc(15 * var(--pt)); }
/* The sheet's Online end: the WiFi and Area rows, a hairline between (the
   Device row stays with the app's first-step pages). */
.ns-info { left: calc(34 * var(--pt)); top: calc(490 * var(--pt)); width: calc(334 * var(--pt)); }
.ns-irow { height: calc(44 * var(--pt)); display: flex; align-items: center; justify-content: space-between; font-size: calc(15 * var(--pt)); line-height: calc(20 * var(--pt)); }
.ns-irow + .ns-irow { box-shadow: inset 0 calc(0.6 * var(--pt)) rgba(255, 255, 255, 0.07); }
.ns-ilabel { display: flex; align-items: center; gap: calc(6 * var(--pt)); color: #8E939B; }
.ns-iglyph { width: calc(19 * var(--pt)); height: calc(19 * var(--pt)); }
.ns-iinfo { width: calc(16 * var(--pt)); height: calc(16 * var(--pt)); }
.ns-ival { color: #EEF0F3; white-space: nowrap; }
.ns-fade { left: 0; right: 0; top: calc(691 * var(--pt)); height: calc(77 * var(--pt)); background: linear-gradient(to top, #0A0B0D 40%, rgba(10, 11, 13, 0.5) 70%, rgba(10, 11, 13, 0)); }
.ns-pill {
  left: calc(16 * var(--pt)); top: calc(702 * var(--pt)); width: calc(370 * var(--pt)); height: calc(50 * var(--pt)); border-radius: calc(25 * var(--pt));
  display: flex; align-items: center; justify-content: center; gap: calc(8 * var(--pt));
  background: rgba(238, 240, 243, 0.9); color: #292B32; font-size: calc(17 * var(--pt)); font-weight: 600;
}
.ns-pill-glyph { width: calc(20 * var(--pt)); height: calc(20 * var(--pt)); }
.ns-home { left: 50%; bottom: calc(8 * var(--pt)); width: calc(134 * var(--pt)); height: calc(5 * var(--pt)); translate: -50% 0; border-radius: calc(3 * var(--pt)); background: rgba(255, 255, 255, 0.8); }
/* One clock of --cycle for every part. The find page: Looking nearby…, the
   row comes in at 17% and is tapped at 33%, Connecting… and the pairing
   hint at 36%; the Online page from 50% to 94%, the board's light steady
   from 56%; then the find page again (its rows reset while it's hidden). */
@keyframes ns-page-a { 0%, 50% { opacity: 1; } 54%, 94% { opacity: 0; } 98%, 100% { opacity: 1; } }
@keyframes ns-page-b { 0%, 50% { opacity: 0; } 55%, 92% { opacity: 1; } 96%, 100% { opacity: 0; } }
@keyframes ns-looking { 0%, 17% { opacity: 1; } 21%, 58% { opacity: 0; } 60%, 100% { opacity: 1; } }
@keyframes ns-device { 0%, 17% { opacity: 0; transform: translateY(calc(8 * var(--pt))); } 22%, 58% { opacity: 1; transform: none; } 60%, 100% { opacity: 0; transform: translateY(calc(8 * var(--pt))); } }
@keyframes ns-press { 0%, 33% { opacity: 1; } 34.5% { opacity: 0.4; } 37.5%, 100% { opacity: 1; } }
@keyframes ns-before { 0%, 35.5% { opacity: 1; } 36.5%, 58% { opacity: 0; } 60%, 100% { opacity: 1; } }
@keyframes ns-after { 0%, 35.5% { opacity: 0; } 36.5%, 58% { opacity: 1; } 60%, 100% { opacity: 0; } }
@keyframes ns-scan { from { transform: scale(1); opacity: 1; } to { transform: scale(2.4); opacity: 0; } }
@keyframes ns-ring { from { transform: scale(0); opacity: 1; } to { transform: scale(1); opacity: 0; } }
@keyframes ns-turn { to { rotate: 360deg; } }

/* Buttons */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 10px;
  height: 52px; padding: 0 30px; border-radius: 999px; border: 1px solid transparent;
  font-size: 16px; font-weight: 600; letter-spacing: -0.01em; white-space: nowrap;
  transition: background-color .35s var(--ease-out), color .35s var(--ease-out), border-color .35s var(--ease-out), box-shadow .45s var(--ease-out), transform .2s var(--ease-out), opacity .3s;
}
.btn:active { transform: scale(0.97); }
.btn-primary {
  background: var(--ink-hi); color: var(--bg);
  box-shadow: 0 1px 0 rgba(255, 255, 255, 0.4) inset, 0 10px 30px -12px rgba(255, 255, 255, 0.25);
}
@media (hover: hover) {
  .btn-primary:hover:not(:disabled) {
    background: var(--accent); color: var(--accent-on);
    box-shadow: 0 1px 0 rgba(255, 255, 255, 0.45) inset, 0 12px 40px -10px rgba(255, 158, 36, 0.7);
  }
}
.btn-primary:disabled { opacity: 0.35; cursor: default; box-shadow: none; }
.btn-ghost { color: var(--ink); border-color: var(--border-3); background: rgba(255, 255, 255, 0.02); height: 48px; padding: 0 22px; font-size: 15px; }
@media (hover: hover) { .btn-ghost:hover { background: rgba(255, 255, 255, 0.07); border-color: rgba(255, 255, 255, 0.32); } }
.link {
  height: 44px; padding: 0 14px; border-radius: 999px; color: var(--ink-2);
  font-size: 15px; font-weight: 600;
  text-decoration: underline; text-decoration-color: var(--border-3); text-underline-offset: 4px;
}
@media (hover: hover) { .link:hover { color: var(--ink-hi); text-decoration-color: currentColor; } }

/* Step 3: installing */
.install { max-width: 560px; }
.install .sub { margin-top: 6px; font-size: 15px; color: var(--muted); }
.pct {
  margin-top: 28px; font-size: 72px; line-height: 1; font-weight: 800; letter-spacing: -0.045em;
  color: var(--ink-hi); font-variant-numeric: tabular-nums;
}
.bar { position: relative; height: 8px; margin-top: 18px; border-radius: 4px; background: rgba(255, 255, 255, 0.08); overflow: hidden; }
.bar-fill {
  position: absolute; left: 0; top: 0; bottom: 0; width: 0;
  border-radius: 4px; background: var(--accent); box-shadow: 0 0 18px rgba(255, 158, 36, 0.6);
  transition: width .25s linear;
}
.install .body { margin-top: 16px; }

/* Done, problems and messages: one card */
.band {
  display: flex; flex-wrap: wrap; align-items: center; gap: 20px 24px;
  padding: 26px 28px; border-radius: 26px; background: var(--bg-1); border: 1px solid var(--border);
  box-shadow: 0 1px 0 rgba(255, 255, 255, 0.04) inset;
}
.band-mark {
  flex: none; display: flex; align-items: center; justify-content: center;
  width: 56px; height: 56px; border-radius: 50%; background: rgba(48, 209, 88, 0.14); color: var(--green);
}
.band-mark.is-problem { background: rgba(255, 158, 36, 0.12); color: var(--accent); }
.band-text { flex: 999 1 320px; min-width: 0; }
.band-text .h2 { font-size: 26px; letter-spacing: -0.03em; line-height: 1.15; }
.band-text .body { margin-top: 6px; font-size: 16px; }

.message { max-width: 560px; margin-top: 44px; }
.stage > .message { margin-top: 0; }
.message .h2 { margin-top: 22px; }
.message .body { margin-top: 12px; }

/* Footer: the website's quiet legal row (Terms, Privacy, Licenses), then
   the site's sign-off in sentence case, both sides matching. The trademark
   lines live on licenses.html, so the footer stays one size. */
.foot {
  position: relative;
  max-width: var(--max); margin: 0 auto;
  padding: 28px var(--gutter) 40px;
  border-top: 1px solid var(--border);
  font-size: 13px; line-height: 1.5; color: var(--muted);
}
.foot-links { display: flex; flex-wrap: wrap; gap: 4px 20px; }
.foot-links a {
  display: inline-flex; align-items: center; min-height: 44px;
  color: var(--ink-2); font-weight: 600;
  text-decoration: underline; text-decoration-color: var(--border-3); text-underline-offset: 4px;
}
@media (hover: hover) { .foot-links a:hover { color: var(--ink-hi); text-decoration-color: currentColor; } }
.foot-base { display: flex; flex-wrap: wrap; justify-content: space-between; gap: 6px 24px; margin-top: 14px; }
.foot-base span { text-wrap: balance; }

/* licenses.html */
.licenses { max-width: 880px; }
.lic-intro { margin-top: 18px; max-width: 62ch; }
.lic-intro + .lic-intro { margin-top: 10px; color: var(--muted); }
.lic-group { margin-top: 48px; }
.lic-list { list-style: none; margin: 16px 0 0; padding: 0; border-top: 1px solid var(--border); }
.lic-item { border-bottom: 1px solid var(--border); }
.lic-item summary {
  position: relative;
  display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: 4px 16px;
  padding: 14px 34px 14px 4px; cursor: pointer; list-style: none; -webkit-tap-highlight-color: transparent;
}
.lic-item summary::after {
  content: ""; position: absolute; right: 10px; top: 50%; width: 8px; height: 8px;
  border-right: 2px solid var(--muted); border-bottom: 2px solid var(--muted);
  transform: translateY(-70%) rotate(45deg); transition: transform .3s var(--ease-out);
}
.lic-item details[open] > summary::after { transform: translateY(-25%) rotate(-135deg); }
.lic-item summary::-webkit-details-marker { display: none; }
.lic-name { font-size: 16px; font-weight: 600; color: var(--ink-hi); letter-spacing: -0.015em; }
.lic-meta { font-size: 13px; color: var(--muted); }
.lic-note { margin: 0 4px 10px; font-size: 14px; color: var(--ink-2); }
.lic-link { display: inline-block; padding: 10px 0; margin: -10px 0; color: var(--ink-2); text-decoration: underline; text-decoration-color: var(--border-3); text-underline-offset: 4px; word-break: break-all; }
/* License texts are printed matter: the one place a monospace belongs. */
.lic-text {
  margin: 0 4px 16px; padding: 16px; max-height: 420px; overflow: auto;
  border-radius: 14px; background: var(--bg-1); border: 1px solid var(--border);
  font: 12px/1.55 ui-monospace, "SF Mono", Menlo, Consolas, monospace; color: var(--ink-2);
  white-space: pre-wrap; word-break: break-word;
}
.lic-own { margin-top: 40px; font-size: 14px; color: var(--muted); }
.lic-mark { margin-top: 10px; max-width: 70ch; font-size: 14px; color: var(--muted); }

@media (max-width: 560px) {
  /* Three columns: each dot with its label under it, the line running from
     the dot to the next one. The finished first step stays a button, now
     dot over label. */
  .stepper { display: grid; grid-template-columns: 1fr 1fr auto; align-items: start; gap: 0; }
  .st { position: relative; flex-direction: column; align-items: flex-start; gap: 8px; }
  .st-back { flex-direction: column; align-items: flex-start; gap: 8px; margin: 0; min-height: 0; padding-right: 10px; text-align: left; }
  .st-label { font-size: 14px; line-height: 1.3; padding-right: 10px; }
  .st-back .st-label { padding-right: 0; }
  .st:last-child .st-label { padding-right: 0; }
  .st-line { position: absolute; top: 16px; left: 42px; right: 8px; min-width: 0; }
}

@media (max-width: 520px) {
  /* The version moves under the board's name instead of squeezing it. */
  .chosen { flex-wrap: wrap; row-gap: 0; }
  .chosen-meta {
    flex: 1 0 100%; flex-direction: row; align-items: baseline; justify-content: space-between;
    padding: 8px 10px 4px; margin-top: 6px; border-top: 1px solid var(--border); text-align: left;
  }
}

@media (max-width: 640px) {
  /* Icon beside the sentence, the button under it. */
  .support-bar-in { grid-template-columns: auto minmax(0, 1fr); justify-content: stretch; align-items: start; }
  .support-bar-icon { margin-top: 1px; }
  .support-bar-copy { grid-column: 2; justify-self: start; }
  .wrap { padding-top: 28px; }
  .stepper { margin-top: 28px; }
  .stage { margin-top: 32px; }
  .h2-sm { font-size: 1.6rem; }
  /* The picture goes between the instruction and the buttons: "Hold the
     button" needs the picture of the button, and under the buttons it was
     past the bottom of the screen. The copy box steps aside so its pieces
     can be ordered; the render is aria-hidden, and the board's tag on it
     reads after the buttons, a footnote to them. */
  .connect { flex-direction: column; flex-wrap: nowrap; align-items: stretch; gap: 0; }
  .connect-copy { display: contents; }
  .connect .illus { order: 1; flex: none; margin-top: 24px; }
  .connect .actions, .connect .fw-line, .connect .hint { order: 2; }
  .illus { height: 200px; }
  .illus.is-board { height: auto; }
  /* A Verified board's line stays one line on a 375 px phone. */
  .illus.is-board .board-status { gap: 8px; padding: 14px 12px 0; font-size: 13px; }
  .illus.is-board .illus-art { flex: none; height: 196px; }
  .illus-xiao img { height: 156px; }
  .illus-xiao.is-long img { height: calc(184px * var(--k, 1)); }
  .illus-xiao.is-pair { gap: 18px; }
  .illus-xiao.is-pair img { height: calc(164px * var(--k, 1)); }
  .illus.next { height: 290px; --u: 2.25px; }
  .next-phone { top: 12px; }
  .next-scene { -webkit-mask-image: linear-gradient(#000 calc(100% - 44px), transparent); mask-image: linear-gradient(#000 calc(100% - 44px), transparent); }
  .next-led > span::before, .next-boot, .next-power { box-shadow: 0 0 2px 1.5px rgb(var(--in)), 0 0 7px 3.5px rgb(var(--glow) / 0.85), 0 0 18px 8px rgb(var(--glow) / 0.45); }
  .support-bar-copy { height: 44px; padding: 0 18px; font-size: 14px; }
  /* License texts keep their own line breaks and scroll sideways: wrapped
     into ~33 characters, 80-column texts and file trees fell apart. */
  .lic-text { white-space: pre; word-break: normal; }
  .pct { font-size: 56px; }
  .band { padding: 22px; }
}

@media (hover: none) {
  .st-back .st-label, .fw-retry, .buy-open, .link, .foot-links a, .lic-link { text-decoration-color: var(--muted); }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: 0.001ms !important; animation-iteration-count: 1 !important; transition-duration: 0.001ms !important; }
  /* The done picture holds its still at once: the device's row found, the
     light lit. */
  .next *, .next *::before { animation-delay: 0s !important; }
}
