/* ============================================================================
   U-MANAGER — "THE FRONT PANEL"

   A home server is headless. Its only face is POST text at boot and the LEDs
   in the closet. U-Manager gives it a face, so this page IS that face at full
   size: a warm-dark chassis you stand in front of, with each feature mounted
   as a labelled bay and the real screenshots shown on a service monitor.

   Doctrine: the chrome is 1987, the content is 2026. Everything this page
   DRAWS is chunky, bevelled and stepped. Everything the app SHOWS (the
   screenshots) stays perfectly crisp and modern. That contrast is the design.

   Rules that hold everywhere in this file:
     - border-radius: 0 on chrome. The single exception is the hero glass
       vignette, which depicts glass rather than UI.
     - No blurred shadows. Offsets are hard: `8px 8px 0`.
     - No radial glow. Light exists but is quantised into Bayer dither.
     - Nothing tweens. transition: none is the default; animation uses steps().
     - --orange is what you can press. --amber is what is currently on.
       They are never swapped.
   ========================================================================= */

/* ---------- fonts ------------------------------------------------------- */

/* Self-hosted rather than pulled from Google: the app makes a point of
   collecting nothing, and the site should not hand visitors' IPs to a third
   party either. VT323 is the app's own wordmark face (carbon_design
   BrandTitle), so the site and the product share one voice. */
@font-face {
  font-family: "VT323";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("../fonts/vt323-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6,
    U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}

/* Both of these ship as single variable files, so one request covers every
   weight the page uses. Ranges are the fonts' real wght axes. */
@font-face {
  font-family: "JetBrains Mono";
  font-style: normal;
  font-weight: 100 800;
  font-display: swap;
  src: url("../fonts/jetbrains-mono-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+2074, U+20AC,
    U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: "IBM Plex Sans";
  font-style: normal;
  font-weight: 100 700;
  font-display: swap;
  src: url("../fonts/ibm-plex-sans-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+2074, U+20AC,
    U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* ---------- tokens ------------------------------------------------------ */

:root {
  /* Chassis. Warm, matching the app's own screenshots — the previous site
     used a cold neutral #08080a that quietly fought every shot on the page. */
  --ink: #0d0a07;
  --panel: #1a120a;
  --panel-2: #241809;
  --bevel-hi: #3a2c1c;
  --bevel-lo: #050302;

  /* Silkscreen. Contrast on --ink: paper 16.4:1, paper-dim 8.5:1 (both AAA). */
  --paper: #f2e9dc;
  --paper-dim: #b8a892;

  /* Brand. --orange 7.0:1 on ink; --amber 11.0:1. The gradient is used in
     exactly two places (wordmark, hero h1) — scarcity is what keeps it a mark
     rather than decoration. */
  --orange: #ff6c2c;
  --amber: #ffb300;
  --brand-from: #ff6d00;
  --brand-to: #ffab40;
  --on-orange: #1a0c04; /* 6.8:1 on --orange */

  /* Status LEDs, same semantics the app uses. These appear only as lamps,
     badges and POST values — never as decorative fields. */
  --led-green: #4cd97b;
  --led-red: #ff5449;
  --led-cyan: #4cc9f0;
  --led-amber: #ffb300;

  --max: 1360px;
  --pad: 40px;
  --header: 60px;
  --bayrail: 44px;
  /* Everything sticky at the top. Snapped sections subtract it so their last
     row — the pager controls — never lands under the fold. */
  --chrome: calc(var(--header) + var(--bayrail));
  --px: 2px; /* bevel thickness */

  /* Screenshot heights.

     These are definite values derived from the viewport rather than a
     percentage-height flex chain, because engines do not agree on that chain:
     measured in WebKit, the hero bezel kept 164px of dead frame beside the
     phone and the field-ops mini monitors collapsed to 20px wide. Give an
     <img> a definite height and `width: auto` and it derives its own width
     from its intrinsic ratio — which every engine does agree on.

     The subtracted figure is everything else stacked in that section: padding,
     bezel, caption and pager controls. clamp() floors it so a short window
     degrades instead of collapsing. */
  --hero-pad: 52px;
  --shot-hero: clamp(200px, calc(100svh - var(--chrome) - var(--hero-pad) - 52px), 640px);
  --shot-bay: clamp(180px, calc(100svh - var(--chrome) - 212px), 560px);
  --shot-mini: clamp(120px, calc(100svh - var(--chrome) - 450px), 360px);

  --font-display: "VT323", ui-monospace, monospace;
  --font-hud: "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
  --font-body: "IBM Plex Sans", system-ui, -apple-system, sans-serif;

  /* Ordered 4x4 Bayer tiles. These replace every glow on the site: light
     still exists, it is just quantised. Rendered at 8px with
     image-rendering: pixelated so the dots read as chunky pixels. */
  --d25: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='4' height='4'%3E%3Crect x='0' y='0' width='1' height='1' fill='%23241809'/%3E%3Crect x='2' y='2' width='1' height='1' fill='%23241809'/%3E%3C/svg%3E");
  --d50: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='4' height='4'%3E%3Crect x='0' y='0' width='1' height='1' fill='%23241809'/%3E%3Crect x='2' y='0' width='1' height='1' fill='%23241809'/%3E%3Crect x='1' y='2' width='1' height='1' fill='%23241809'/%3E%3Crect x='3' y='2' width='1' height='1' fill='%23241809'/%3E%3C/svg%3E");
  --d75: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='4' height='4'%3E%3Crect width='4' height='4' fill='%23241809'/%3E%3Crect x='0' y='0' width='1' height='1' fill='%230d0a07'/%3E%3Crect x='2' y='2' width='1' height='1' fill='%230d0a07'/%3E%3C/svg%3E");
  /* The one "glow" on the page, behind the hero monitor — made of pixels. */
  --d-orange: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='4' height='4'%3E%3Crect x='0' y='0' width='1' height='1' fill='%23ff6c2c'/%3E%3Crect x='2' y='2' width='1' height='1' fill='%23ff6c2c'/%3E%3C/svg%3E");
}

/* ---------- reset ------------------------------------------------------- */

* {
  box-sizing: border-box;
}

html {
  /* Deliberately NOT smooth. Smooth scrolling is tweening, and on this page
     nothing tweens. Anchor jumps land instantly, like changing channel.

     Each section settles into place as a page, so the site reads as a pager
     rather than one long scroll. `mandatory`, not `proximity`: Safari
     implements proximity so loosely it effectively never snaps while Chrome
     does, and mandatory is the only setting both honour. It is safe here
     because EVERY full-width section below declares a snap point, so there is
     never a long stretch you are forbidden to rest in. */
  scroll-padding-top: var(--chrome);
}

/* Opt-in, and only the two landing pages opt in. Applied globally it also hit
   the service manual, which has exactly one snap point (its footer) — so every
   scroll there jumped straight to the bottom and the guide was unreadable. */
html.paged {
  scroll-snap-type: y mandatory;
}

/* No bay rail on the manual, so nothing to subtract for it. */
html:not(.paged) {
  --chrome: var(--header);
}

body {
  margin: 0;
  overflow-x: clip;
  background: var(--ink);
  color: var(--paper);
  font-family: var(--font-body);
  font-size: 17px;
  line-height: 1.65;
  -webkit-font-smoothing: antialiased;
}

img {
  max-width: 100%;
  display: block;
  /* Without this the width/height attributes win and shots render at a
     literal 1800px tall. Keeping the attributes is still worth it: the
     browser reserves the box before the file loads, so nothing jumps. */
  height: auto;
}

a {
  color: var(--orange);
}

h1,
h2,
h3 {
  margin: 0;
}

/* Focus is louder than hover, as it should be. Amber on ink is 11:1. */
:focus-visible {
  outline: 2px solid var(--amber);
  outline-offset: 3px;
}

.wrap {
  width: 100%;
  max-width: var(--max);
  margin: 0 auto;
  padding: 0 var(--pad);
}

/* Clipped rather than pushed off-canvas: a negative offset widens the
   scrollable area and leaves the page scrolling sideways on a phone. */
.skip {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

.skip:focus {
  position: fixed;
  left: 16px;
  top: 10px;
  z-index: 60;
  width: auto;
  height: auto;
  margin: 0;
  overflow: visible;
  clip-path: none;
  background: var(--amber);
  color: var(--ink);
  padding: 10px 16px;
  font: 700 13px/1 var(--font-hud);
  letter-spacing: 0.1em;
  text-transform: uppercase;
}

/* ---------- primitives -------------------------------------------------- */

/* The universal surface. One box-shadow stack does the whole bevel plus the
   hard drop shadow: light edge top/left, dark edge bottom/right. */
.panel {
  background: var(--panel);
  border: none;
  border-radius: 0;
  box-shadow:
    inset 0 var(--px) 0 var(--bevel-hi),
    inset var(--px) 0 0 var(--bevel-hi),
    inset 0 calc(var(--px) * -1) 0 var(--bevel-lo),
    inset calc(var(--px) * -1) 0 0 var(--bevel-lo),
    8px 8px 0 rgba(0, 0, 0, 0.85);
}

/* Bevel inverted: wells, unlit cells, screen cavities. No drop shadow — a
   sunken thing does not cast one. */
.panel--sunken {
  background: var(--ink);
  box-shadow:
    inset 0 var(--px) 0 var(--bevel-lo),
    inset var(--px) 0 0 var(--bevel-lo),
    inset 0 calc(var(--px) * -1) 0 var(--bevel-hi),
    inset calc(var(--px) * -1) 0 0 var(--bevel-hi);
}

.panel--flat {
  box-shadow:
    inset 0 var(--px) 0 var(--bevel-hi),
    inset var(--px) 0 0 var(--bevel-hi),
    inset 0 calc(var(--px) * -1) 0 var(--bevel-lo),
    inset calc(var(--px) * -1) 0 0 var(--bevel-lo);
}

/* Keycap. Hover LIGHTS UP rather than lifting; press travels 4px into its own
   shadow and eats it. It feels mechanical precisely because nothing eases. */
.btn {
  display: inline-block;
  font: 700 15px/1 var(--font-hud);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  padding: 16px 28px;
  background: var(--orange);
  color: var(--on-orange);
  border: 2px solid var(--bevel-lo);
  border-radius: 0;
  box-shadow: 4px 4px 0 var(--bevel-lo);
  text-decoration: none;
  cursor: pointer;
  transition: none;
}

.btn:hover {
  background: var(--amber);
}

.btn:active {
  transform: translate(4px, 4px);
  box-shadow: 0 0 0 var(--bevel-lo);
}

.btn--ghost {
  background: var(--panel);
  color: var(--paper);
}

.btn--ghost:hover {
  background: var(--panel-2);
  color: var(--amber);
}

.btn--sm {
  padding: 11px 18px;
  font-size: 13px;
}

.btn--lg {
  padding: 22px 44px;
  font-size: 18px;
  box-shadow: 6px 6px 0 var(--bevel-lo);
}

.btn--lg:active {
  transform: translate(6px, 6px);
}

/* Silkscreen label — the bracketed mono voice of the machine. */
.silk {
  font: 400 12px/1.5 var(--font-hud);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--paper-dim);
}

.silk--tight {
  letter-spacing: 0.1em;
}

.label {
  font: 700 12px/1 var(--font-hud);
  letter-spacing: 0.14em;
  text-transform: uppercase;
}

/* Status lamp. The one place a glow is allowed, because LEDs actually do. */
.lamp {
  display: inline-block;
  width: 8px;
  height: 8px;
  background: var(--led-green);
  box-shadow: 0 0 6px var(--led-green);
  flex: none;
}

.lamp--dead {
  background: var(--bevel-hi);
  box-shadow: none;
}

.lamp--amber {
  background: var(--led-amber);
  box-shadow: 0 0 6px var(--led-amber);
}

/* Staggered periods so the chassis reads as idling hardware rather than a
   metronome. steps(2, jump-none): lamps are on or off, never in between. */
@keyframes lamp {
  50% {
    opacity: 0.25;
    box-shadow: none;
  }
}

.lamp--blink {
  animation: lamp 1.6s steps(2, jump-none) infinite;
}

.lamp--blink-b {
  animation: lamp 2.1s steps(2, jump-none) infinite;
}

.lamp--blink-c {
  animation: lamp 2.7s steps(2, jump-none) infinite;
}

/* Scanlines. 3px period avoids moire at 1x. Used ONLY on the POST plate and
   the back panel — never over screenshots or reading text. */
.crt::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 3;
  background: repeating-linear-gradient(
    to bottom,
    transparent 0 2px,
    rgba(0, 0, 0, 0.18) 2px 3px
  );
}

/* The CRT switch drives a full-viewport overlay. Scoped to the POST block and
   the footer it was invisible in practice, which made the control look broken.

   Opt-in, and off by default, deliberately: an overlay strong enough to be
   worth a switch darkens every third pixel row, which costs real contrast on
   body text. Off by default the measured AA ratios hold for everyone; whoever
   wants the tube can have it properly, not homeopathically.

   pointer-events: none so it never eats a click. It sits above the header and
   rail — a tube covers the whole screen — but below the skip link, and the
   screenshot viewer renders in the top layer so it stays crisp. */
body.crt-on::after {
  content: "";
  position: fixed;
  inset: 0;
  z-index: 56;
  pointer-events: none;
  background: repeating-linear-gradient(
    to bottom,
    transparent 0 2px,
    rgba(0, 0, 0, 0.32) 2px 3px
  );
  box-shadow: inset 0 0 200px rgba(0, 0, 0, 0.7);
}

/* The local plates keep their texture either way: they depict screens inside
   the page, not the monitor you are sitting at, and they carry no body copy. */

/* Dithered ambient field — what used to be a radial glow. */
.dither {
  background-image: var(--d25);
  background-size: 8px 8px;
  image-rendering: pixelated;
}

/* With snapping on, an <hr> BETWEEN two viewport-tall sections is never
   actually seen — you snap straight past it. So the rack rail belongs inside
   each section, drawn at its top edge, out of the layout flow. */
.select,
.bay,
.plain,
.closer {
  position: relative;
}

.select::before,
.bay::before,
.plain::before,
.closer::before {
  content: "";
  position: absolute;
  inset: 0 0 auto 0;
  height: 14px;
  z-index: 1;
  background:
    radial-gradient(circle 3px at 9px 7px, var(--bevel-lo) 97%, transparent) 0 0 /
      48px 14px repeat-x,
    linear-gradient(180deg, var(--bevel-hi), var(--panel) 35%, var(--bevel-lo));
}

/* Stepped ramp divider: three densities stacked into a 24px fade. */
.bay-divider {
  height: 24px;
  border: none;
  margin: 0;
  background-image: var(--d75), var(--d50), var(--d25);
  background-repeat: repeat-x;
  background-size: 8px 8px;
  background-position: 0 0, 0 8px, 0 16px;
  image-rendering: pixelated;
}

/* Rack rail with mounting holes. Alternated with .bay-divider so the chassis
   never repeats mechanically. */
.rail {
  height: 14px;
  border: none;
  margin: 0;
  background:
    radial-gradient(circle 3px at 9px 7px, var(--bevel-lo) 97%, transparent) 0 0 /
      48px 14px repeat-x,
    linear-gradient(180deg, var(--bevel-hi), var(--panel) 35%, var(--bevel-lo));
}

/* ---------- parity check (scroll progress) ------------------------------ */

/* Reading the whole pitch IS the parity check. Segmented, quantised to 2%
   steps in JS, aria-hidden because it only mirrors scroll position. */
.parity {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  height: 6px;
  z-index: 50;
  background: var(--bevel-lo);
  pointer-events: none;
}

.parity span {
  display: block;
  height: 100%;
  width: 0;
  background-image: linear-gradient(
    90deg,
    var(--amber) 0 6px,
    transparent 6px 8px
  );
  background-size: 8px 100%;
  background-color: transparent;
}

/* Bottom left, not top right: up there it sat on top of the Install button
   (the readout outranks the header on z-index) and covered the primary CTA
   every time the reader scrolled. Down here nothing else lives. */
.parity-readout {
  position: fixed;
  bottom: 14px;
  left: 14px;
  z-index: 50;
  font: 400 10px/1 var(--font-hud);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--paper-dim);
  background: var(--ink);
  padding: 4px 7px;
  opacity: 0;
  pointer-events: none;
}

.parity-readout.on {
  opacity: 1;
}

/* ---------- header / top rail ------------------------------------------- */

/* Solid ink, no backdrop blur. The bottom edge is a machined two-tone line. */
header {
  position: sticky;
  top: 0;
  z-index: 40;
  background: var(--ink);
  box-shadow:
    inset 0 -2px 0 var(--bevel-lo),
    0 2px 0 var(--bevel-hi);
}

.bar {
  display: flex;
  align-items: center;
  gap: 16px;
  height: var(--header);
}

.brand {
  display: flex;
  align-items: center;
  gap: 10px;
  text-decoration: none;
  white-space: nowrap;
}

.brand img {
  width: 30px;
  height: 30px;
  image-rendering: auto;
}

/* Same recipe as the app: VT323, uppercase, tracked out, brand gradient
   painted through the glyphs. One of only two gradients on the page. */
.brand span {
  font-family: var(--font-display);
  font-size: 30px;
  line-height: 1;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  background: linear-gradient(90deg, var(--brand-from), var(--brand-to));
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}

.power {
  display: flex;
  align-items: center;
  gap: 7px;
  font: 400 11px/1 var(--font-hud);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--paper-dim);
}

.bar nav {
  margin-left: auto;
  display: flex;
  align-items: center;
  gap: 22px;
}

.bar nav a:not(.btn):not(.dip a) {
  font: 700 12px/1 var(--font-hud);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--paper-dim);
  text-decoration: none;
}

.bar nav a:not(.btn):hover {
  color: var(--amber);
}

/* Language selector as a two-cell DIP switch: the lit cell is the one you
   are in. Reads as hardware, and is a clearer state than a bare "ES" link. */
.dip {
  display: flex;
  border: 2px solid var(--bevel-lo);
}

.dip a,
.dip span {
  font: 700 11px/1 var(--font-hud);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  padding: 7px 9px;
  text-decoration: none;
}

.dip .on {
  background: var(--amber);
  color: var(--ink);
}

.dip .off {
  background: var(--ink);
  color: var(--paper-dim);
  box-shadow:
    inset 0 2px 0 var(--bevel-lo),
    inset 2px 0 0 var(--bevel-lo);
}

.dip .off:hover {
  color: var(--amber);
}

/* Sound + CRT toggles live in the rail as machine switches. */
.switch {
  font: 700 11px/1 var(--font-hud);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  background: var(--ink);
  color: var(--paper-dim);
  border: 2px solid var(--bevel-lo);
  padding: 7px 9px;
  cursor: pointer;
  transition: none;
}

.switch:hover {
  color: var(--amber);
}

.switch[aria-pressed="true"] {
  background: var(--amber);
  color: var(--ink);
}

@media (max-width: 1000px) {
  .bar nav a:not(.btn):not(.dip a) {
    display: none;
  }
}

@media (max-width: 720px) {
  :root {
    --pad: 20px;
    --header: 54px;
  }

  .bar {
    gap: 10px;
  }

  .brand span {
    font-size: 24px;
  }

  .brand img {
    width: 26px;
    height: 26px;
  }

  .power,
  .bar .switch {
    display: none;
  }

  .bar nav {
    gap: 8px;
  }
}

/* ---------- persistent bay rail ----------------------------------------- *
   The full SELECT BAY screen is a nice moment but you only ever see it once;
   after the first jump it is a long scroll away. This is the same selector
   reduced to a strip that never leaves: it stays under the header, lights the
   bay you are currently in, and gets you to any other in one click. */

.bayrail {
  position: sticky;
  top: var(--header);
  z-index: 35;
  height: var(--bayrail);
  background: var(--panel);
  box-shadow:
    inset 0 1px 0 var(--bevel-hi),
    inset 0 -2px 0 var(--bevel-lo);
  /* The nav inside scrolls horizontally. WebKit still let ~8px of its content
     count toward the document's scroll width, so the whole page could be
     panned sideways on a phone; clipping at the strip stops that. */
  overflow-x: clip;
}

.bayrail .wrap {
  height: 100%;
  min-width: 0;
  display: flex;
  align-items: center;
  gap: 14px;
}

.bayrail .lbl {
  flex: none;
  font: 700 10px/1 var(--font-hud);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--amber);
}

.bayrail nav {
  display: flex;
  align-items: center;
  gap: 4px;
  overflow-x: auto;
  scrollbar-width: none;
  min-width: 0;
}

.bayrail nav::-webkit-scrollbar {
  display: none;
}

.bayrail a {
  flex: none;
  display: flex;
  align-items: center;
  gap: 7px;
  padding: 7px 10px;
  font: 700 11px/1 var(--font-hud);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--paper-dim);
  text-decoration: none;
  background: var(--ink);
  box-shadow:
    inset 0 1px 0 var(--bevel-lo),
    inset 1px 0 0 var(--bevel-lo),
    inset 0 -1px 0 var(--bevel-hi),
    inset -1px 0 0 var(--bevel-hi);
  transition: none;
}

/* 5.3:1 on --ink. The dimmer grey this started as measured 3.6:1 — supplementary
   to the bay name beside it, but still text someone has to read. */
.bayrail a .n {
  color: #93826f;
}

.bayrail a .lamp {
  width: 6px;
  height: 6px;
  background: var(--bevel-hi);
  box-shadow: none;
}

.bayrail a:hover {
  color: var(--paper);
}

/* Lit: this is the bay you are looking at. Amber = currently on. */
.bayrail a[aria-current="true"] {
  background: var(--panel-2);
  color: var(--amber);
  box-shadow:
    inset 0 2px 0 var(--bevel-hi),
    inset 2px 0 0 var(--bevel-hi),
    inset 0 -2px 0 var(--bevel-lo),
    inset -2px 0 0 var(--bevel-lo);
}

.bayrail a[aria-current="true"] .n {
  color: var(--amber);
}

.bayrail a[aria-current="true"] .lamp {
  background: var(--c, var(--amber));
  box-shadow: 0 0 6px var(--c, var(--amber));
}

/* The label eats room the bay cells need before the cells start scrolling. */
@media (max-width: 900px) {
  .bayrail .lbl {
    display: none;
  }
}

/* ---------- hero / POST screen ------------------------------------------ */

/* A fixed height, not a minimum. With `min-height` the screenshot hits its cap
   and pushes the section past the viewport, taking the caption and the pager
   controls with it. A definite height gives the flex chain below something
   real to divide up, so the phone shrinks to fit instead. */
.hero {
  position: relative;
  height: calc(100svh - var(--chrome));
  display: flex;
  /* stretch, NOT center: centring here leaves .wrap with a content-derived
     height, so the `height: 100%` further down the monitor chain has nothing
     definite to resolve against and the screenshot renders at full size. The
     grid inside does the vertical centring instead. */
  align-items: stretch;
  overflow: hidden;
  padding: 24px 0 28px;
  scroll-snap-align: start;
}

.hero > .wrap {
  width: 100%;
  height: 100%;
  min-height: 0;
}

.hero .wrap {
  display: grid;
  /* The headline is the widest thing on the page and VT323 is not narrow —
     the text column has to be able to hold "Your Unraid server," on one line
     at display size, or the h1 breaks into three. */
  grid-template-columns: minmax(0, 1.16fr) minmax(0, 0.84fr);
  gap: 64px;
  align-items: center;
}

.eyebrow {
  display: inline-block;
  font: 400 12px/1 var(--font-hud);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--amber);
  margin-bottom: 24px;
}

/* VT323 has a small x-height and one weight; it needs size and POSITIVE
   tracking. Negative letter-spacing (the generic-landing-page reflex) turns
   it to mush. Minimum 28px anywhere on the site. */
h1 {
  font-family: var(--font-display);
  /* Capped so the first line fits the column at 1440px and up. VT323 runs
     about 7.6px of width per 1px of font-size for this string. */
  font-size: clamp(2.9rem, 5.6vw, 5.4rem);
  /* VT323 carries a lot of internal leading, so a "normal" 1.0 reads as a
     gappy 1.3 would in a text face. 0.9 is what looks like solid setting. */
  line-height: 0.9;
  letter-spacing: 0.02em;
  margin: 0 0 22px;
  background: linear-gradient(90deg, var(--brand-from), var(--brand-to));
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}

/* Both lines are blocks so the break is deliberate rather than whatever the
   column width happens to produce. */
h1 .l1 {
  display: block;
}

h1 .soft {
  display: block;
  color: var(--paper);
  /* The gradient above paints through descendants too, so opt this line out. */
  -webkit-text-fill-color: var(--paper);
}

.lede {
  color: var(--paper-dim);
  margin: 0 0 22px;
  max-width: 34em;
  font-size: 1.02rem;
}

/* The POST block. This is where the privacy claim gets told in the machine's
   own voice: TELEMETRY ... NOT FOUND says more than a paragraph could. */
.post {
  position: relative;
  padding: 16px 20px;
  margin: 0 0 24px;
  max-width: 30em;
  font: 400 13px/1.85 var(--font-hud);
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--paper-dim);
  overflow: hidden;
}

.post b {
  color: var(--paper);
  font-weight: 400;
}

.post .ok {
  color: var(--led-green);
}

.post .warn {
  color: var(--led-amber);
}

.post .none {
  color: var(--led-cyan);
}

.post-line {
  display: block;
  white-space: nowrap;
}

/* Typed-on reveal is opt-in from JS; without JS every line is simply visible. */
.post.typing .post-line {
  visibility: hidden;
}

.post.typing .post-line.shown {
  visibility: visible;
}

.post .caret {
  display: inline-block;
  width: 8px;
  height: 13px;
  background: var(--led-green);
  vertical-align: -2px;
  animation: lamp 1s steps(2, jump-none) infinite;
}

.cta {
  display: flex;
  flex-wrap: wrap;
  gap: 14px;
  align-items: center;
}

/* PRESS START — the arcade ritual, kept because it is the most legible
   "this is free, just take it" signal there is. */
.start {
  display: inline-flex;
  align-items: center;
  gap: 12px;
  font: 700 17px/1 var(--font-hud);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  padding: 18px 26px;
  background: var(--orange);
  color: var(--on-orange);
  border: 2px solid var(--bevel-lo);
  box-shadow: 5px 5px 0 var(--bevel-lo);
  text-decoration: none;
  transition: none;
}

.start:hover {
  background: var(--amber);
}

.start:active {
  transform: translate(5px, 5px);
  box-shadow: 0 0 0 var(--bevel-lo);
}

.start .blink {
  animation: lamp 1.1s steps(2, jump-none) infinite;
}

/* Silkscreen is for short stamped phrases. A whole sentence set in uppercase
   mono is a chore to read, so the sentence next to it stays in Plex. */
.freeplay {
  margin: 18px 0 0;
  font: 700 12px/1.6 var(--font-hud);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--amber);
}

.note {
  margin: 8px 0 0;
  font-size: 0.88rem;
  color: var(--paper-dim);
  max-width: 34em;
}

/* ---------- monitor (screenshot housing) -------------------------------- */

/* The central craft problem: real 808x1800 screenshots against chunky pixel
   chrome. The answer is not to pixelate them — it is to mount them as live
   video on a service monitor. The screenshot stays the only smooth,
   antialiased object in view, which is exactly why the product reads as the
   modern thing you are being offered. */
.monitor {
  position: relative;
  width: fit-content;
  margin-inline: auto;
  background: var(--panel-2);
  padding: 12px 12px 40px;
  box-shadow:
    inset 0 2px 0 var(--bevel-hi),
    inset 2px 0 0 var(--bevel-hi),
    inset 0 -2px 0 var(--bevel-lo),
    inset -2px 0 0 var(--bevel-lo),
    8px 8px 0 rgba(0, 0, 0, 0.85);
}

/* Portrait phone shots next to short copy always leave vertical slack. Capping
   the height keeps each bay from towering over its own paragraph while staying
   well above the size at which the UI in the shot stops being readable. */
.monitor img {
  border: 2px solid #000;
  border-radius: 0;
  width: auto;
  max-width: 100%;
  max-height: 520px;
  object-fit: contain;
}

.monitor .hud {
  position: absolute;
  left: 14px;
  right: 14px;
  bottom: 12px;
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 10px;
  font: 400 11px/1 var(--font-hud);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--paper-dim);
}

/* Hero monitor only: corner darkening on the glass. This is the one
   border-radius on the site, and it depicts glass rather than UI chrome.
   Feature-bay shots get nothing — product legibility outranks fiction. */
/* The vignette hangs off the monitor rather than off a wrapper around the
   image. As a real box that wrapper broke the flex chain: the bezel was then
   sized by the image's intrinsic width instead of its aspect ratio, leaving
   ~170px of dead frame beside the phone. `inset` here is exactly the bezel, so
   it still lands only on the screen area. */
.monitor--hero::after {
  content: "";
  position: absolute;
  inset: 12px 12px 40px;
  z-index: 2;
  pointer-events: none;
  background: radial-gradient(
    115% 115% at 50% 48%,
    transparent 64%,
    rgba(0, 0, 0, 0.3) 100%
  );
  border-radius: 6px;
}

/* The dithered field the hero monitor sits on — the only "glow" here. */
.hero-field {
  position: relative;
  display: flex;
  justify-content: center;
  align-items: center;
  min-height: 0;
}

/* The image carries a definite height, so the bezel hugs it without any
   percentage-height plumbing. */
.hero-field .monitor--hero {
  width: fit-content;
  max-width: 100%;
  align-self: center;
}

/* No box of its own — the vignette lives on the monitor now. */
.hero-field .glass {
  display: contents;
}

.hero-field .monitor--hero img {
  height: var(--shot-hero);
  width: auto;
  max-width: 100%;
  max-height: none;
}

.hero-field::before {
  content: "";
  position: absolute;
  inset: -6% -8%;
  z-index: 0;
  background-image: var(--d-orange);
  background-size: 8px 8px;
  image-rendering: pixelated;
  opacity: 0.32;
  -webkit-mask-image: radial-gradient(closest-side, #000 26%, transparent 80%);
  mask-image: radial-gradient(closest-side, #000 26%, transparent 80%);
}

.hero-field > * {
  position: relative;
  z-index: 1;
}

@media (max-width: 1000px) {
  .hero .wrap {
    grid-template-columns: minmax(0, 1fr);
    gap: 48px;
  }

  .monitor--hero img {
    max-height: 520px;
  }
}

/* ---------- SELECT BAY grid --------------------------------------------- */

/* The stage-select. It is also, plainly, a table of contents — which is why
   it earns its place on a page whose job is conversion. */
/* Full screen, because it is a screen: the stage select. */
.select {
  height: calc(100svh - var(--chrome));
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding: 32px 0;
  scroll-snap-align: start;
  overflow: hidden;
}

.select h2 {
  font: 700 13px/1 var(--font-hud);
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--amber);
  margin: 0 0 8px;
}

.select .sub {
  margin: 0 0 28px;
}

.bays {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 14px;
}

.bays a {
  display: block;
  padding: 18px 16px 16px;
  text-decoration: none;
  color: var(--paper-dim);
  background: var(--panel);
  box-shadow:
    inset 0 2px 0 var(--bevel-hi),
    inset 2px 0 0 var(--bevel-hi),
    inset 0 -2px 0 var(--bevel-lo),
    inset -2px 0 0 var(--bevel-lo),
    4px 4px 0 rgba(0, 0, 0, 0.8);
  transition: none;
}

.bays a:hover,
.bays a:focus-visible {
  background: var(--panel-2);
  color: var(--paper);
}

.bays a:active {
  transform: translate(4px, 4px);
  box-shadow:
    inset 0 2px 0 var(--bevel-hi),
    inset 2px 0 0 var(--bevel-hi),
    inset 0 -2px 0 var(--bevel-lo),
    inset -2px 0 0 var(--bevel-lo);
}

.bays .n {
  display: flex;
  align-items: center;
  justify-content: space-between;
  font: 700 12px/1 var(--font-hud);
  letter-spacing: 0.1em;
  color: var(--paper-dim);
  margin-bottom: 10px;
}

.bays .t {
  display: block;
  font: 700 13px/1.25 var(--font-hud);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: inherit;
}

/* One line of what is actually in the bay. Without it the tiles are pretty
   but tell you nothing you could not guess from the name. */
.bays .d {
  display: block;
  margin-top: 8px;
  font-size: 0.86rem;
  line-height: 1.45;
  color: var(--paper-dim);
}

/* Which bays need something bought. Stated here so nobody clicks through
   expecting the whole list to be included. */
.bays .paid {
  display: inline-block;
  margin-top: 8px;
  padding: 3px 6px;
  background: var(--panel-2);
  color: var(--amber);
  font: 700 9px/1 var(--font-hud);
  letter-spacing: 0.1em;
  text-transform: uppercase;
}

@media (max-width: 900px) {
  .bays {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

/* ---------- feature bays ------------------------------------------------ */

/* Normal document flow. The old site pinned every section to 100svh with
   mandatory scroll-snap and then spent ~80 lines undoing it on small screens.
   A machine is one object; you pan across it. */
.bay {
  height: calc(100svh - var(--chrome));
  display: flex;
  align-items: stretch;
  overflow: hidden;
  padding: 24px 0 28px;
  scroll-snap-align: start;
}

.bay > .wrap {
  width: 100%;
  min-height: 0;
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: 64px;
  align-items: center;
}

/* The screenshot stack sizes itself from --shot-bay and is simply centred in
   the column. It used to stretch and divide leftover height with flex, which
   is exactly the plumbing WebKit disagreed about. */
.bay .shot {
  align-self: center;
  min-width: 0;
}

/* Odd bays put the shot on the left without touching source order, so the
   reading order stays text-then-image for screen readers. */
.bay.flip .shot {
  order: -1;
}

.bay .copy {
  max-width: 32em;
}

/* The bay tag carries this section's status colour on its left edge — the
   app's own semantics, homeopathically dosed. */
.bay-tag {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  padding: 7px 12px 7px 10px;
  margin-bottom: 18px;
  background: var(--panel);
  border-left: 4px solid var(--c, var(--orange));
  font: 700 11px/1 var(--font-hud);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--paper-dim);
  box-shadow:
    inset 0 2px 0 var(--bevel-hi),
    inset 0 -2px 0 var(--bevel-lo),
    inset -2px 0 0 var(--bevel-lo);
}

.bay-tag .lamp {
  background: var(--c, var(--orange));
  box-shadow: 0 0 6px var(--c, var(--orange));
}

.bay-tag em {
  font-style: normal;
  color: var(--paper);
}

.chip {
  display: inline-block;
  margin-left: 10px;
  padding: 4px 8px;
  background: var(--amber);
  color: var(--ink);
  font: 700 10px/1 var(--font-hud);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  vertical-align: 2px;
}

.bay h2,
.plain h2 {
  font-family: var(--font-display);
  font-size: clamp(2.2rem, 3.6vw, 3.2rem);
  line-height: 0.92;
  letter-spacing: 0.02em;
  color: var(--paper);
  margin: 0 0 18px;
}

.bay p {
  color: var(--paper-dim);
  margin: 0 0 22px;
}

/* Spec readout. The marker is a flat 6px square — the pixel — while the words
   stay in Plex, because these are sentences a human reads, not machine data. */
.bay ul {
  margin: 0;
  padding: 0;
  list-style: none;
  display: grid;
  gap: 11px;
}

.bay ul li {
  display: flex;
  gap: 13px;
  align-items: flex-start;
  color: var(--paper-dim);
  font-size: 0.98rem;
}

.bay ul li::before {
  content: "";
  flex: none;
  width: 6px;
  height: 6px;
  margin-top: 11px;
  background: var(--c, var(--orange));
}

/* Inline marker on the individual bullets that need an add-on bought. The
   bay-level chip says "some of this is paid"; this says exactly which line. */
.req {
  display: inline-block;
  margin-left: 7px;
  padding: 2px 6px;
  background: var(--panel-2);
  color: var(--amber);
  font: 700 9px/1.5 var(--font-hud);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  vertical-align: 1px;
  white-space: nowrap;
}

/* ---------- expansion slots (add-ons) ----------------------------------- */

.slots {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 14px;
  margin-bottom: 26px;
}

.slot {
  display: flex;
  flex-direction: column;
  padding: 20px 18px;
  background: var(--panel);
  box-shadow:
    inset 0 2px 0 var(--bevel-hi),
    inset 2px 0 0 var(--bevel-hi),
    inset 0 -2px 0 var(--bevel-lo),
    inset -2px 0 0 var(--bevel-lo),
    6px 6px 0 rgba(0, 0, 0, 0.85);
}

.slot h3 {
  font: 700 13px/1.3 var(--font-hud);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--amber);
  margin: 0 0 10px;
}

.slot p {
  margin: 0;
  color: var(--paper-dim);
  font-size: 0.92rem;
}

.slot .slot-n {
  font: 400 10px/1 var(--font-hud);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  /* 4.9:1 on --panel. The dimmer grey used elsewhere measured 3.4:1 here. */
  color: #93826f;
  margin-bottom: 10px;
}

.slot .needs {
  margin-top: 12px;
  font: 400 10px/1.4 var(--font-hud);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--paper-dim);
}

/* How it is actually sold. Stated plainly, in the same plate as the rest. */
.pricing {
  display: flex;
  flex-wrap: wrap;
  gap: 14px;
}

.pricing div {
  flex: 1 1 260px;
  padding: 18px 20px;
  background: var(--panel-2);
  box-shadow:
    inset 0 2px 0 var(--bevel-hi),
    inset 2px 0 0 var(--bevel-hi),
    inset 0 -2px 0 var(--bevel-lo),
    inset -2px 0 0 var(--bevel-lo);
}

.pricing h3 {
  font: 700 12px/1 var(--font-hud);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--amber);
  margin: 0 0 9px;
}

.pricing p {
  margin: 0;
  color: var(--paper-dim);
  font-size: 0.94rem;
}

@media (max-width: 1000px) {
  .slots {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

@media (max-width: 600px) {
  .slots {
    grid-template-columns: 1fr;
  }
}

/* ---------- channel selector (was: carousel) ---------------------------- */

.pager {
  width: 100%;
  max-width: 440px;
  margin-inline: auto;
}

.pager .track {
  display: flex;
  overflow-x: auto;
  /* Setting one axis alone turns the other into `auto`, and the monitor
     shadow sticks out far enough to make that axis genuinely scrollable —
     the wheel would then move the shot instead of the page. */
  overflow-y: clip;
  scroll-snap-type: x mandatory;
  /* Channels switch. They do not ease. */
  scroll-behavior: auto;
  scrollbar-width: none;
}

.pager .track::-webkit-scrollbar {
  display: none;
}

.pager .slide {
  flex: 0 0 100%;
  scroll-snap-align: center;
  margin: 0;
  padding: 0 10px;
}

.pager .slide .monitor {
  width: fit-content;
  max-width: 100%;
  margin-inline: auto;
}

.pager .slide .monitor img {
  height: var(--shot-bay);
  width: auto;
  max-width: 100%;
  max-height: none;
}

.pager .slide figcaption {
  margin-top: 14px;
  text-align: center;
  font-size: 0.9rem;
  color: var(--paper-dim);
  min-height: 3.2em;
}

.pager-nav {
  flex: none;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 14px;
  margin-top: 8px;
}

/* Square bevel keys, not round buttons. */
.pager-btn {
  width: 40px;
  height: 40px;
  background: var(--panel);
  color: var(--paper);
  border: 2px solid var(--bevel-lo);
  box-shadow: 3px 3px 0 var(--bevel-lo);
  font: 700 14px/1 var(--font-hud);
  cursor: pointer;
  display: grid;
  place-items: center;
  padding: 0;
  transition: none;
}

.pager-btn:hover:not(:disabled) {
  background: var(--amber);
  color: var(--ink);
}

.pager-btn:active:not(:disabled) {
  transform: translate(3px, 3px);
  box-shadow: 0 0 0 var(--bevel-lo);
}

.pager-btn:disabled {
  opacity: 0.3;
  cursor: default;
}

.dots {
  display: flex;
  gap: 7px;
}

/* Rectangular LED lamps rather than round dots. */
.dot {
  width: 14px;
  height: 8px;
  padding: 0;
  border: 0;
  background: var(--bevel-hi);
  cursor: pointer;
  transition: none;
}

.dot[aria-current="true"] {
  background: var(--amber);
  box-shadow: 0 0 6px var(--amber);
}

/* The mono readout says the state out loud — better than dots ever did. */
.ch {
  font: 400 11px/1 var(--font-hud);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--paper-dim);
  min-width: 5.5em;
  text-align: center;
}

@media (max-width: 1000px) {
  .bay .wrap {
    grid-template-columns: minmax(0, 1fr);
    gap: 40px;
  }

  .bay.flip .shot {
    order: 0;
  }
}

/* ---------- generic section --------------------------------------------- */

/* Every snap point has to be exactly one screen tall. Shorter and the snap
   leaves a void below it; taller and mandatory snapping puts its own bottom
   out of reach, since it will not rest between two snap points. */
.plain {
  min-height: calc(100svh - var(--chrome));
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding: 32px 0;
  scroll-snap-align: start;
}

.sub {
  color: var(--paper-dim);
  margin: 0 0 32px;
  max-width: 46em;
}

/* The gallery is the one block that would otherwise outgrow its screen, so its
   phones are sized from --shot-mini. This used to divide leftover height with
   a four-deep flex chain, which collapsed the minis to 20px wide in WebKit. */
#manage {
  height: calc(100svh - var(--chrome));
  overflow: hidden;
}

#manage .trio .monitor--mini {
  width: fit-content;
  max-width: 100%;
  margin-inline: auto;
  margin-bottom: 18px;
}

#manage .trio .monitor--mini img {
  height: var(--shot-mini);
  width: auto;
  max-width: 100%;
  max-height: none;
}

/* ---------- field operations gallery ------------------------------------ */

.trio {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 28px;
}

.trio figure {
  margin: 0;
}

/* Mini monitors: same housing, thinner bezel, shorter shadow. */
.monitor--mini {
  padding: 8px 8px 30px;
  box-shadow:
    inset 0 2px 0 var(--bevel-hi),
    inset 2px 0 0 var(--bevel-hi),
    inset 0 -2px 0 var(--bevel-lo),
    inset -2px 0 0 var(--bevel-lo),
    4px 4px 0 rgba(0, 0, 0, 0.85);
  margin-bottom: 18px;
}

.monitor--mini img {
  max-height: none;
  width: 100%;
}

.monitor--mini .hud {
  left: 10px;
  right: 10px;
  bottom: 9px;
  font-size: 10px;
}

.trio h3 {
  font: 700 13px/1.3 var(--font-hud);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--paper);
  margin: 0 0 8px;
}

.trio p {
  margin: 0;
  color: var(--paper-dim);
  font-size: 0.94rem;
}

@media (max-width: 1100px) {
  .trio {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 36px;
  }
}

@media (max-width: 620px) {
  .trio {
    grid-template-columns: 1fr;
    gap: 40px;
  }
}

/* ---------- indicator cluster (was: three cards) ------------------------ */

/* One housing with three cells, divided by machined lines — a single object
   rather than three floating cards. */
.cluster {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  background: var(--panel);
  box-shadow:
    inset 0 2px 0 var(--bevel-hi),
    inset 2px 0 0 var(--bevel-hi),
    inset 0 -2px 0 var(--bevel-lo),
    inset -2px 0 0 var(--bevel-lo),
    8px 8px 0 rgba(0, 0, 0, 0.85);
}

.cluster .cell {
  padding: 28px 26px;
  border-left: 2px solid var(--bevel-lo);
  box-shadow: inset 2px 0 0 var(--bevel-hi);
}

.cluster .cell:first-child {
  border-left: 0;
  box-shadow: none;
}

.cluster .well {
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  margin-bottom: 16px;
  background: var(--ink);
  box-shadow:
    inset 0 2px 0 var(--bevel-lo),
    inset 2px 0 0 var(--bevel-lo),
    inset 0 -2px 0 var(--bevel-hi),
    inset -2px 0 0 var(--bevel-hi);
}

/* Hand-placed 16x16 pixel glyphs. crispEdges is what keeps them pixels. */
.cluster svg {
  width: 32px;
  height: 32px;
  shape-rendering: crispEdges;
  fill: var(--orange);
}

.cluster h3 {
  font: 700 13px/1.3 var(--font-hud);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--paper);
  margin: 0 0 9px;
}

.cluster p {
  margin: 0;
  color: var(--paper-dim);
  font-size: 0.94rem;
}

@media (max-width: 860px) {
  .cluster {
    grid-template-columns: 1fr;
  }

  .cluster .cell {
    border-left: 0;
    border-top: 2px solid var(--bevel-lo);
    box-shadow: inset 0 2px 0 var(--bevel-hi);
  }

  .cluster .cell:first-child {
    border-top: 0;
    box-shadow: none;
  }
}

/* ---------- quick start card -------------------------------------------- */

/* The printed instruction card riveted to real hardware, screws and all. */
.qs {
  position: relative;
  background: var(--panel-2);
  padding: 0;
  box-shadow:
    inset 0 2px 0 var(--bevel-hi),
    inset 2px 0 0 var(--bevel-hi),
    inset 0 -2px 0 var(--bevel-lo),
    inset -2px 0 0 var(--bevel-lo),
    8px 8px 0 rgba(0, 0, 0, 0.85);
}

.qs::before,
.qs::after {
  content: "";
  position: absolute;
  top: 10px;
  bottom: 10px;
  width: 8px;
  background-image: radial-gradient(
    circle 4px at 4px 4px,
    var(--bevel-lo) 60%,
    var(--bevel-hi) 61% 100%
  );
  background-size: 8px 8px;
  background-repeat: no-repeat;
  background-position: top left, bottom left;
  /* two screws per side: top and bottom */
  background-image:
    radial-gradient(circle 4px at 4px 4px, var(--bevel-lo) 55%, var(--bevel-hi) 56%),
    radial-gradient(circle 4px at 4px 4px, var(--bevel-lo) 55%, var(--bevel-hi) 56%);
  background-position: 0 0, 0 100%;
}

.qs::before {
  left: 10px;
}

.qs::after {
  right: 10px;
}

.qs-head {
  padding: 12px 26px;
  background: var(--panel);
  font: 700 11px/1 var(--font-hud);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--amber);
  box-shadow: inset 0 -2px 0 var(--bevel-lo);
}

.steps {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 0;
  margin: 0;
  padding: 0;
  list-style: none;
}

.steps li {
  padding: 26px;
  border-left: 2px solid var(--bevel-lo);
  box-shadow: inset 2px 0 0 var(--bevel-hi);
}

.steps li:first-child {
  border-left: 0;
  box-shadow: none;
}

/* Same keycap geometry as .btn, but inert — it is a printed number. */
.steps .key {
  display: grid;
  place-items: center;
  width: 34px;
  height: 34px;
  margin-bottom: 16px;
  background: var(--ink);
  color: var(--amber);
  border: 2px solid var(--bevel-lo);
  box-shadow: inset -2px -2px 0 var(--bevel-hi);
  font: 700 15px/1 var(--font-hud);
}

.steps h3 {
  font: 700 13px/1.3 var(--font-hud);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--paper);
  margin: 0 0 9px;
}

.steps p {
  margin: 0;
  color: var(--paper-dim);
  font-size: 0.94rem;
}

@media (max-width: 860px) {
  .steps {
    grid-template-columns: 1fr;
  }

  .steps li {
    border-left: 0;
    border-top: 2px solid var(--bevel-lo);
    box-shadow: inset 0 2px 0 var(--bevel-hi);
  }

  .steps li:first-child {
    border-top: 0;
    box-shadow: none;
  }

  .qs::before,
  .qs::after {
    display: none;
  }
}

/* ---------- spec plate + caution sticker -------------------------------- */

.split {
  display: grid;
  grid-template-columns: minmax(0, 1.25fr) minmax(0, 1fr);
  gap: 40px;
  align-items: start;
}

/* The serial-number label. Two columns of mono with hairline rules. */
.spec {
  width: 100%;
  border-collapse: collapse;
  font: 400 13px/1.5 var(--font-hud);
}

.spec caption {
  text-align: left;
  padding: 12px 16px;
  background: var(--panel);
  color: var(--amber);
  font-weight: 700;
  font-size: 11px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  box-shadow:
    inset 0 2px 0 var(--bevel-hi),
    inset 2px 0 0 var(--bevel-hi),
    inset -2px 0 0 var(--bevel-lo);
}

.spec th,
.spec td {
  text-align: left;
  padding: 13px 16px;
  border-bottom: 1px solid var(--bevel-hi);
  vertical-align: top;
}

.spec th {
  width: 38%;
  color: var(--paper-dim);
  font-weight: 400;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  white-space: nowrap;
}

.spec td {
  color: var(--paper);
}

.spec tr:last-child th,
.spec tr:last-child td {
  border-bottom: 0;
}

.spec tbody {
  background: var(--panel-2);
  box-shadow:
    inset 2px 0 0 var(--bevel-hi),
    inset 0 -2px 0 var(--bevel-lo),
    inset -2px 0 0 var(--bevel-lo);
}

/* Every real PSU has one of these, and every homelabber has peeled one off. */
.caution {
  background: var(--amber);
  color: var(--ink);
  padding: 20px 22px;
  box-shadow: 8px 8px 0 rgba(0, 0, 0, 0.85);
}

.caution h3 {
  font: 700 12px/1 var(--font-hud);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  margin: 0 0 12px;
}

.caution p {
  margin: 0;
  font-size: 0.95rem;
  line-height: 1.6;
}

.caution a {
  color: var(--ink);
  text-decoration: underline;
  text-underline-offset: 2px;
  font-weight: 600;
}

@media (max-width: 860px) {
  .split {
    grid-template-columns: 1fr;
    gap: 32px;
  }
}

/* ---------- closer / power switch --------------------------------------- */

.closer {
  min-height: calc(100svh - var(--chrome));
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding: 64px 0;
  scroll-snap-align: start;
  text-align: center;
  background-image: var(--d25);
  background-size: 8px 8px;
  image-rendering: pixelated;
}

.closer > .wrap > * {
  image-rendering: auto;
}

.closer h2 {
  font-family: var(--font-display);
  font-size: clamp(2.4rem, 4.4vw, 3.8rem);
  line-height: 0.92;
  letter-spacing: 0.02em;
  margin: 0 0 16px;
  color: var(--paper);
}

.closer p {
  color: var(--paper-dim);
  margin: 0 0 30px;
}

/* Flips to green + "COMPLETE" once the reader actually reaches this point. */
.parity-final {
  font: 400 12px/1 var(--font-hud);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--paper-dim);
  margin: 0 0 18px;
}

.parity-final.done {
  color: var(--led-green);
}

.closer .freeplay {
  margin-top: 24px;
}

/* ---------- footer / back panel ----------------------------------------- */

/* The footer needs a snap point of its own. Without one, mandatory snapping
   bounces back to the section above and the bottom of the page simply cannot
   be reached. */
footer {
  position: relative;
  padding: 56px 0 64px;
  background: var(--bevel-lo);
  color: var(--paper-dim);
  box-shadow: inset 0 2px 0 var(--bevel-hi);
  scroll-snap-align: end;
}

.flinks {
  display: flex;
  flex-wrap: wrap;
  gap: 26px;
  margin-bottom: 28px;
}

/* Port labels: the brackets appear on hover, like a terminal selection. */
.flinks a {
  font: 700 12px/1 var(--font-hud);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--paper);
  text-decoration: none;
}

.flinks a::before {
  content: "[ ";
  color: transparent;
}

.flinks a::after {
  content: " ]";
  color: transparent;
}

.flinks a:hover {
  color: var(--amber);
}

.flinks a:hover::before,
.flinks a:hover::after {
  color: var(--amber);
}

/* The brand promise stated as a hardware fact: a lamp that never lights. */
/* The back panel carries its own pair of switches: the ones in the top rail
   are hidden on phones, and the CRT switch must stay reachable everywhere. */
.panel-switches {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 12px;
  margin-bottom: 26px;
}

.fswitches {
  display: flex;
  gap: 8px;
}

.telemetry {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  padding: 9px 13px;
  margin: 0;
  background: var(--ink);
  font: 400 11px/1 var(--font-hud);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--paper-dim);
  box-shadow:
    inset 0 2px 0 var(--bevel-lo),
    inset 2px 0 0 var(--bevel-lo),
    inset 0 -2px 0 var(--bevel-hi),
    inset -2px 0 0 var(--bevel-hi);
}

.disclaimer {
  max-width: 64em;
  margin: 0 0 14px;
  font: 400 12px/1.7 var(--font-hud);
  color: var(--paper-dim);
}

.disclaimer a {
  color: var(--paper);
}

/* Recessive, but still real text: --bevel-hi on the back panel measured
   1.53:1, which is decoration pretending to be content. This is 4.9:1. */
.serial {
  margin: 0;
  font: 400 11px/1 var(--font-hud);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: #8a7a68;
}

/* ---------- service manual (how-to page) -------------------------------- *
   The guide used to be a raw .md file linked from the footer, which GitHub
   Pages serves as plain text. Same chassis, laid out as the manual that ships
   taped to the inside of the lid. */

.doc {
  display: grid;
  grid-template-columns: 250px minmax(0, 1fr);
  gap: 56px;
  padding: 44px 0 88px;
  align-items: start;
}

.doc-head {
  padding: 40px 0 28px;
}

.doc-head h1 {
  font-size: clamp(2.4rem, 4.4vw, 3.6rem);
  margin-bottom: 12px;
}

.doc-head p {
  color: var(--paper-dim);
  margin: 0;
  max-width: 44em;
}

.toc {
  position: sticky;
  top: calc(var(--header) + 20px);
  padding: 18px 18px 20px;
  background: var(--panel);
  box-shadow:
    inset 0 2px 0 var(--bevel-hi),
    inset 2px 0 0 var(--bevel-hi),
    inset 0 -2px 0 var(--bevel-lo),
    inset -2px 0 0 var(--bevel-lo),
    6px 6px 0 rgba(0, 0, 0, 0.85);
}

.toc h2 {
  font: 700 11px/1 var(--font-hud);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--amber);
  margin: 0 0 14px;
}

.toc ol {
  margin: 0;
  padding: 0;
  list-style: none;
  counter-reset: toc;
  display: grid;
  gap: 9px;
}

.toc li {
  counter-increment: toc;
}

.toc a {
  display: flex;
  gap: 9px;
  font: 400 12.5px/1.35 var(--font-hud);
  color: var(--paper-dim);
  text-decoration: none;
}

.toc a::before {
  content: counter(toc, decimal-leading-zero);
  color: #7a6650;
  flex: none;
}

.toc a:hover {
  color: var(--amber);
}

.doc-body {
  max-width: 46em;
  /* Grid item: without this it refuses to shrink below the min-content of the
     long plugin URLs further down, and widens the whole document. */
  min-width: 0;
}

.doc-body > section {
  padding: 0 0 44px;
}

.doc-body h2 {
  font-family: var(--font-display);
  font-size: clamp(1.9rem, 2.8vw, 2.5rem);
  line-height: 0.92;
  letter-spacing: 0.02em;
  color: var(--paper);
  margin: 0 0 18px;
}

.doc-body h3 {
  font: 700 13px/1.3 var(--font-hud);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--amber);
  margin: 30px 0 12px;
}

.doc-body p {
  color: var(--paper-dim);
  margin: 0 0 16px;
}

.doc-body ul,
.doc-body ol {
  color: var(--paper-dim);
  margin: 0 0 18px;
  padding-left: 0;
  list-style: none;
  display: grid;
  gap: 10px;
}

.doc-body ol {
  counter-reset: dstep;
}

/* The marker is absolutely positioned rather than a flex sibling. Making the
   <li> a flex container would promote every inline <strong> and <code> inside
   the sentence to a flex item, and the row `gap` would then open up around
   them mid-sentence. */
/* These lists are grids (for the row gap), so their <li> are grid items and
   default to min-width: auto. A step containing a <pre> with an unbreakable
   plugin URL would then stretch the item — and the page — to the width of that
   URL. min-width: 0 lets the <pre> keep the overflow to itself. */
.doc-body ol > li,
.doc-body ul > li {
  min-width: 0;
}

.doc-body ol > li {
  counter-increment: dstep;
  position: relative;
  padding-left: 35px;
}

.doc-body ol > li::before {
  content: counter(dstep);
  position: absolute;
  left: 0;
  top: 2px;
  width: 22px;
  height: 22px;
  display: grid;
  place-items: center;
  background: var(--panel-2);
  color: var(--amber);
  font: 700 12px/1 var(--font-hud);
  box-shadow: inset -1px -1px 0 var(--bevel-hi);
}

.doc-body ul > li {
  position: relative;
  padding-left: 19px;
}

.doc-body ul > li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 11px;
  width: 6px;
  height: 6px;
  background: var(--orange);
}

/* Nested lists sit inside a step, so they get no counter of their own. */
.doc-body li ul,
.doc-body li ol {
  margin: 10px 0 0;
}

.doc-body strong {
  color: var(--paper);
  font-weight: 600;
}

.doc-body code {
  font: 400 0.88em/1.4 var(--font-hud);
  background: var(--panel-2);
  color: var(--amber);
  padding: 2px 6px;
  word-break: break-word;
}

.doc-body pre {
  margin: 0 0 18px;
  padding: 16px 18px;
  background: var(--ink);
  overflow-x: auto;
  max-width: 100%;
  min-width: 0;
  box-shadow:
    inset 0 2px 0 var(--bevel-lo),
    inset 2px 0 0 var(--bevel-lo),
    inset 0 -2px 0 var(--bevel-hi),
    inset -2px 0 0 var(--bevel-hi);
}

.doc-body pre code {
  background: none;
  padding: 0;
  color: var(--led-green);
  font-size: 13px;
  white-space: pre;
  word-break: normal;
}

/* Blockquotes in the source are all advisories, so they become panel notices
   rather than the usual indented grey bar. */
.notice {
  margin: 0 0 18px;
  padding: 15px 18px;
  background: var(--panel);
  border-left: 4px solid var(--amber);
  color: var(--paper-dim);
  box-shadow:
    inset 0 2px 0 var(--bevel-hi),
    inset 0 -2px 0 var(--bevel-lo),
    inset -2px 0 0 var(--bevel-lo);
}

.notice p:last-child {
  margin-bottom: 0;
}

.notice--hot {
  border-left-color: var(--led-red);
}

.doc-body figure {
  margin: 0 0 20px;
}

.doc-body figure img {
  width: 100%;
  border: 2px solid #000;
  background: var(--panel-2);
  padding: 8px;
}

.doc-body figcaption {
  margin-top: 8px;
  font: 400 11px/1.4 var(--font-hud);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--paper-dim);
}

@media (max-width: 1000px) {
  .doc {
    grid-template-columns: minmax(0, 1fr);
    gap: 32px;
  }

  .toc {
    position: static;
  }
}

/* ---------- screenshot viewer ------------------------------------------- *
   The screenshots are small by necessity — a phone next to a column of text —
   and on the field-ops row they are smaller still. Clicking any frame blows it
   up. Built on <dialog> so Esc, the focus trap and the backdrop come from the
   browser rather than from hand-rolled JS.

   The affordance is attached to .monitor, which is already the correctly-sized
   box; no wrapper element is added around the <img>, because the sizing chain
   through these frames is what took cross-engine work to settle. */

.is-zoomable {
  cursor: zoom-in;
}

/* Corner badge so it is visibly clickable without hovering first. */
.is-zoomable::before {
  content: "\2922";
  position: absolute;
  top: 14px;
  right: 14px;
  z-index: 4;
  width: 24px;
  height: 24px;
  display: grid;
  place-items: center;
  background: var(--ink);
  color: var(--amber);
  font: 700 13px/1 var(--font-hud);
  box-shadow:
    inset 0 1px 0 var(--bevel-hi),
    inset 0 -1px 0 var(--bevel-lo);
}

.is-zoomable:hover::before,
.is-zoomable:focus-visible::before {
  background: var(--amber);
  color: var(--ink);
}

.monitor--mini.is-zoomable::before {
  top: 10px;
  right: 10px;
  width: 20px;
  height: 20px;
  font-size: 11px;
}

.viewer {
  border: none;
  padding: 0;
  margin: auto;
  max-width: 96vw;
  max-height: 96vh;
  overflow: visible;
  background: transparent;
  color: var(--paper);
}

.viewer::backdrop {
  background: rgba(5, 3, 2, 0.94);
}

.viewer-frame {
  position: relative;
  background: var(--panel-2);
  padding: 14px 14px 46px;
  box-shadow:
    inset 0 2px 0 var(--bevel-hi),
    inset 2px 0 0 var(--bevel-hi),
    inset 0 -2px 0 var(--bevel-lo),
    inset -2px 0 0 var(--bevel-lo),
    10px 10px 0 rgba(0, 0, 0, 0.9);
}

/* Two modes. "Fit" shows the whole screen at once; "1:1" renders the file at
   its own 808px width, which is more than twice as wide as fit gives you on a
   laptop, and the dialog scrolls. The choice persists — someone who needs the
   bigger one needs it every time. */
.viewer img {
  display: block;
  height: min(86vh, 1400px);
  width: auto;
  max-width: 88vw;
  border: 2px solid #000;
}

.viewer.is-actual img {
  height: auto;
  width: min(808px, 88vw);
  max-width: 88vw;
}

.viewer.is-actual {
  max-height: 96vh;
  overflow-y: auto;
  scrollbar-width: none;
}

.viewer.is-actual::-webkit-scrollbar {
  display: none;
}

/* In 1:1 the frame is taller than the window, so the readout has to travel
   with the content rather than pin to a bottom that is off-screen. */
.viewer.is-actual .viewer-hud {
  position: static;
  margin-top: 12px;
}

.viewer.is-actual .viewer-frame {
  padding-bottom: 14px;
}

.viewer .viewer-hud {
  position: absolute;
  left: 16px;
  right: 16px;
  bottom: 14px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 14px;
  font: 400 12px/1.3 var(--font-hud);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--paper-dim);
}

.viewer-close {
  flex: none;
  font: 700 12px/1 var(--font-hud);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  padding: 9px 13px;
  background: var(--orange);
  color: var(--on-orange);
  border: 2px solid var(--bevel-lo);
  box-shadow: 3px 3px 0 var(--bevel-lo);
  cursor: pointer;
  transition: none;
}

.viewer-close:hover {
  background: var(--amber);
}

.viewer-close:active {
  transform: translate(3px, 3px);
  box-shadow: 0 0 0 var(--bevel-lo);
}

@media (max-width: 720px) {
  .viewer img {
    height: auto;
    width: min(84vw, 420px);
    max-width: 84vw;
  }

  .viewer-frame {
    padding: 10px 10px 42px;
  }
}

/* ---------- konami ------------------------------------------------------ */

/* Earned, not automatic. Inverts the chassis into "service mode" amber. */
body.service {
  --ink: #0a0700;
  --panel: #1c1400;
  --panel-2: #2a1e00;
  --orange: var(--amber);
}

.service-banner {
  position: fixed;
  left: 50%;
  bottom: 22px;
  transform: translateX(-50%);
  z-index: 55;
  padding: 12px 20px;
  background: var(--amber);
  color: var(--ink);
  font: 700 12px/1 var(--font-hud);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  box-shadow: 5px 5px 0 rgba(0, 0, 0, 0.85);
}

/* ---------- short windows ----------------------------------------------- *
   Snapping is still on here, and the hero stack is the tallest thing on the
   page, so tighten it before the section starts clipping its own CTA. Applies
   only above the phone breakpoint, where the paging layout is still in play. */

@media (min-width: 861px) and (max-height: 900px) {
  /* Keep --shot-hero in step with the tighter padding below. */
  :root {
    --hero-pad: 36px;
  }

  .hero {
    padding: 16px 0 20px;
  }

  .eyebrow {
    margin-bottom: 16px;
  }

  h1 {
    font-size: clamp(2.4rem, 4.2vw, 3.6rem);
    margin-bottom: 16px;
  }

  .lede {
    font-size: 0.96rem;
    margin-bottom: 16px;
  }

  .post {
    font-size: 12px;
    line-height: 1.7;
    padding: 12px 16px;
    margin-bottom: 18px;
  }

  .freeplay {
    margin-top: 14px;
  }
}

/* ---------- phones ------------------------------------------------------ *
   Below this a full-height section stops helping: the copy alone fills the
   screen, so the page flows normally instead of being clipped. Everything the
   snapping system sets has to be undone here, which is why this block and the
   reduced-motion block are the last two things in the file — every rule they
   fight is a plain `.bay` or `.hero` selector of equal weight, so if they sat
   any earlier the base rules would win on source order. */

@media (max-width: 860px), (max-height: 640px) {
  /* Must out-specify html.paged, not just html, or the opt-in rule wins. */
  html.paged {
    scroll-snap-type: none;
  }

  .hero,
  .select,
  .bay,
  .plain,
  .closer,
  footer {
    scroll-snap-align: none;
    height: auto;
    min-height: 0;
    overflow: visible;
  }

  .hero,
  .bay {
    display: block;
    padding: 44px 0;
  }

  .select {
    display: block;
    padding: 36px 0;
  }

  .plain,
  .closer {
    display: block;
    padding: 44px 0;
  }

  #manage {
    height: auto;
    overflow: visible;
  }

  #manage > .wrap,
  #manage .trio figure {
    display: block;
  }

  /* The dithered field bleeds 8% past the monitor on each side. Up above,
     `overflow: hidden` on the section swallows that; down here the section
     flows, so the bleed is wider than the page gutter and drags the whole
     document sideways — 8px of horizontal wobble on a phone. Pull it in. */
  .hero-field::before {
    inset: -4% -2%;
  }

  /* Below the paging breakpoint the viewport-derived heights stop making
     sense — the page flows, so the screenshots are sized by width instead.

     The width is absolute (vw, never %) on purpose: the frame around it is
     `width: fit-content`, so a percentage width on the image would be
     resolving against a container that is itself sized by the image. Chromium
     breaks that cycle using the image's intrinsic size; WebKit collapses it,
     which rendered every screenshot at 4x4px on iPhone. */
  .hero-field .monitor--hero img,
  .pager .slide .monitor img,
  #manage .trio .monitor--mini img,
  .monitor img {
    width: min(280px, 74vw);
    height: auto;
    max-width: none;
    max-height: none;
  }

  .hero-field .monitor--hero,
  .pager .slide .monitor,
  #manage .trio .monitor--mini {
    width: fit-content;
    margin-inline: auto;
  }
}

@media (max-width: 720px) {
  body {
    font-size: 16px;
  }

  .hero {
    padding: 40px 0 48px;
  }

  /* The bracketed silkscreen is one stamped phrase; letting it wrap so the
     closing bracket lands alone on the next line reads as a bug. */
  .eyebrow {
    font-size: 10px;
    letter-spacing: 0.08em;
  }

  .bay,
  .plain {
    padding: 44px 0;
  }

  .closer {
    padding: 56px 0 64px;
  }

  .post {
    font-size: 11.5px;
    padding: 14px 14px;
  }

  .start {
    font-size: 15px;
    padding: 16px 20px;
  }

  .btn--lg {
    padding: 18px 28px;
    font-size: 16px;
  }

  .parity-readout {
    display: none;
  }
}

/* ---------- reduced motion ---------------------------------------------- *
   The aesthetic already rejects easing, so honouring this costs the design
   almost nothing — which is a good sign the direction is structurally sound.
   Lamps burn steady, the POST renders complete, the parity bar still tracks
   scroll position (that is the user moving, not an animation). */

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

/* Viewer action pair, kept together at the right of the readout. */
.viewer-actions {
  flex: none;
  display: flex;
  gap: 8px;
}

.viewer-zoom {
  background: var(--panel);
  color: var(--paper);
}

.viewer-zoom:hover {
  background: var(--panel-2);
  color: var(--amber);
}

.viewer-zoom[aria-pressed="true"] {
  background: var(--amber);
  color: var(--ink);
}
