/* ═══════════════════════════════════════════════════════════════
   ACCESS PANELS — the machine opens, it never navigates
   ═══════════════════════════════════════════════════════════ */

.panels {
  position: absolute; inset: 0;
  z-index: 60;
  pointer-events: none;
}
.panels.is-open { pointer-events: auto; }
.panels.is-open::before { /* shadow the board underneath */
  content: '';
  position: absolute; inset: 0;
  background: rgba(24, 20, 14, 0.34);
  animation: fade var(--t-quick) ease-out;
}
@keyframes fade { from { opacity: 0; } }

.panel {
  position: absolute; inset: 0;
  display: grid;
  grid-template-rows: auto minmax(0, 1fr);
  /* A hatch is its own piece of metal: brighter than the board it covers, with
     a machined edge and a hard shadow along the leading edge so you can see it
     sitting in front. Without those it reads as the board itself. */
  background-color: var(--enamel);
  background-image:
    radial-gradient(var(--perf-dot) 1.1px, transparent 1.2px),
    linear-gradient(180deg, var(--paper-hi) 0%, var(--enamel) 42%, var(--enamel-shade) 100%);
  background-size: var(--perf-size) var(--perf-size), 100% 100%;
  border-radius: var(--r-md);
  box-shadow:
    /* the machined edge */
    inset 0 0 0 1px rgba(70, 58, 34, 0.45),
    inset 0 1px 0 1px rgba(255, 255, 255, 0.85),
    /* the hatch standing off the board */
    0 30px 50px -14px rgba(28, 20, 8, 0.55),
    0 4px 0 -1px rgba(70, 58, 34, 0.28);
  transform: translateY(-101%);
  transition: transform var(--t-panel) var(--ease-mech), visibility var(--t-panel);
  visibility: hidden;
  will-change: transform;
}
/* Before JS runs, `hidden` keeps panels out of the document entirely.
   After boot the attribute is dropped and `visibility` does the hiding —
   it keeps them out of the a11y tree and out of the tab order while still
   letting the slide transition run. */
.panel.is-open { transform: translateY(0); visibility: visible; }

/* the archive slides out of the bottom like a drawer */
.panel--drawer { transform: translateY(101%); }

/* hinge at the top edge of a service panel */
.panel__hinge {
  position: absolute; top: 6px; left: 50%;
  transform: translateX(-50%);
  display: flex; gap: 34px;
}
.panel__hinge span {
  width: 26px; height: 6px;
  background: linear-gradient(180deg, var(--steel-light), var(--steel-dark));
  border-radius: 0 0 3px 3px;
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.4);
}
/* drawer rails at the bottom edge */
.panel__rails {
  position: absolute; bottom: 5px; left: 50%;
  transform: translateX(-50%);
  display: flex; gap: 120px;
}
.panel__rails span {
  width: 70px; height: 5px;
  background: linear-gradient(180deg, var(--steel-dark), var(--steel-light));
  border-radius: 3px;
}

.panel__head {
  display: flex; align-items: center; gap: var(--gap-lg); flex-wrap: wrap;
  padding: clamp(1rem, 2.2vw, 1.5rem) clamp(1rem, 2.6vw, 2rem) 0.85rem;
  border-bottom: 1px solid rgba(90, 80, 60, 0.26);
}
.panel__title {
  flex: 1 1 auto;
  font-family: var(--font-ui); font-weight: 700;
  font-size: clamp(1.35rem, 3.4vw, 2.15rem);
  line-height: 1.05;
  letter-spacing: -0.015em;
  color: var(--teal-ink);
  text-shadow: 0 1px 0 rgba(255, 255, 255, 0.6);
}
.panel__meta {
  font-size: 0.66rem; letter-spacing: var(--track-label); text-transform: uppercase;
  color: var(--engrave);
}
.panel__close {
  min-height: 44px;
  padding: 0.5rem 1.1rem;
  font-size: 0.66rem; letter-spacing: var(--track-label); text-transform: uppercase;
  color: var(--teal-ink);
  background: linear-gradient(180deg, var(--enamel), var(--enamel-deep));
  border: 1px solid rgba(60, 50, 30, 0.32);
  border-radius: var(--r-key);
  box-shadow: var(--raise-2);
  cursor: pointer;
}
.panel__close:active { transform: translateY(2px); box-shadow: var(--inset-2); }

/* READING SURFACES DROP THE TEXTURE.
   Dots behind body text raise visual noise, break saccades and cost reading
   speed — worst for ADHD, dyslexia and low vision, and worst of all in a long
   scrolling list. The hatch keeps its perforation because it is metal; the
   paper sitting in it does not. */
.panel__body,
.archive {
  background-color: var(--enamel);
  background-image: linear-gradient(180deg, var(--paper-hi) 0%, var(--enamel) 55%);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.9),
    inset 0 3px 8px -5px rgba(60, 44, 24, 0.4);
}

.panel__body {
  overflow-y: auto; overscroll-behavior: contain;
  padding: clamp(1.6rem, 3vw, 2.4rem) clamp(1rem, 2.6vw, 2rem) clamp(2rem, 4vw, 3rem);
}
.panel__lede, .panel__status {
  font-size: 0.78rem; letter-spacing: 0.05em; text-transform: uppercase;
  color: var(--ink-soft);
}
.panel__status { margin-top: 1rem; color: var(--teal-ink); min-height: 1.1em; }

/* ── PROSE ─────────────────────────────────────────────────── */
/* Scale contrast is what reads as premium: one unmistakably large thing per
   panel, then captions, nothing in between. Large type takes tight tracking,
   small caps take loose — that inversion is the whole trick.

   VERTICAL RHYTHM. A uniform gap is not typesetting — it gives a heading the
   same air as a paragraph break, so nothing looks like it belongs to anything.
   Space is proximity: a heading sits far from what came before and close to
   what it titles, and the lede gets its own tier. */
.prose { max-width: 64ch; }
.prose > * + * { margin-top: 1.1rem; }
.prose > .lede + * { margin-top: 1.75rem; }   /* the opening line is its own tier */
.prose > * + h3 { margin-top: 2.5rem; }       /* air above a heading */
.prose > h3 + * { margin-top: 0.65rem; }      /* bound tight to what it titles */
.prose p { font-size: 0.94rem; line-height: 1.7; letter-spacing: 0.005em; color: var(--ink); }
.prose .lede {
  font-size: clamp(1.1rem, 2.4vw, 1.45rem);
  line-height: 1.42;
  letter-spacing: -0.012em;
  color: var(--teal-ink);
}
.prose h3 {
  font-size: 0.74rem; letter-spacing: var(--track-label); text-transform: uppercase;
  color: var(--engrave); text-shadow: var(--engrave-text);
}
.steps { display: grid; gap: 0.7rem; counter-reset: step; }
.steps li {
  position: relative;
  padding-left: 2.2rem;
  font-size: 0.84rem; line-height: 1.7;
}
.steps li::before {
  counter-increment: step;
  content: counter(step, decimal-leading-zero);
  position: absolute; left: 0; top: 0.1rem;
  display: grid; place-items: center;
  width: 1.5rem; height: 1.5rem;
  font-family: var(--font-display); font-size: 0.95rem;
  color: var(--phosphor);
  background: var(--crt-bg);
  border-radius: 2px;
  box-shadow: var(--inset-2);
}

/* ── FIELDS ────────────────────────────────────────────────── */
.field { display: grid; gap: 0.3rem; }
.field__label {
  font-size: 0.62rem; letter-spacing: var(--track-label); text-transform: uppercase;
  color: var(--engrave); text-shadow: var(--engrave-text);
}
.field input, .field textarea, .field select {
  width: 100%;
  min-height: var(--tap-min);
  padding: 0.5rem 0.7rem;
  font-size: 0.78rem; letter-spacing: 0.04em;
  color: var(--teal-ink);
  background: linear-gradient(180deg, #FBF8F1, #EFEAE0);
  border: 1px solid rgba(60, 50, 30, 0.3);
  border-radius: var(--r-xs);
  box-shadow: var(--inset-1);
}
.field textarea { min-height: 4.5rem; resize: vertical; line-height: 1.6; }
.field input::placeholder, .field textarea::placeholder { color: var(--placeholder); }
.field--wide { grid-column: 1 / -1; }

/* ── ARCHIVE DRAWER ────────────────────────────────────────── */
.archive {
  display: grid;
  grid-template-rows: auto minmax(0, 1fr);
  min-height: 0;
  padding: clamp(0.9rem, 2vw, 1.4rem) clamp(1rem, 2.6vw, 2rem) 1.6rem;
  gap: var(--gap-lg);
}
.archive__filters {
  display: grid;
  grid-template-columns: minmax(180px, 280px) minmax(0, 1fr);
  gap: var(--gap-lg);
  align-items: start;
}
.archive__tag-list { display: flex; flex-wrap: wrap; gap: 0.4rem; margin-top: 0.3rem; }

/* physical latching checkbox */
.tag {
  display: inline-flex; align-items: center; gap: 0.45rem;
  min-height: 34px;
  padding: 0.3rem 0.7rem 0.3rem 0.4rem;
  font-size: 0.64rem; letter-spacing: var(--track-label); text-transform: uppercase;
  color: var(--teal-ink);
  background: linear-gradient(180deg, var(--enamel), var(--enamel-deep));
  border: 1px solid rgba(60, 50, 30, 0.3);
  border-radius: var(--r-key);
  box-shadow: var(--raise-1);
  cursor: pointer;
}
.tag__box {
  width: 13px; height: 13px;
  background: var(--enamel-recess);
  border: 1px solid rgba(60, 50, 30, 0.45);
  box-shadow: var(--inset-1);
}
.tag[aria-pressed='true'] { box-shadow: var(--inset-2); background: var(--enamel-deep); }
.tag[aria-pressed='true'] .tag__box {
  background: var(--teal);
  box-shadow: inset 0 0 0 2px var(--enamel-deep), 0 0 5px rgba(30, 77, 74, 0.5);
}
.tag__count { color: var(--engrave); font-size: 0.92em; }

.archive__list {
  min-height: 0;
  overflow-y: auto; overscroll-behavior: contain;
  display: grid; gap: 0;
  border-top: 1px solid rgba(90, 80, 60, 0.24);
}
.archive__row {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto auto;
  align-items: center;
  gap: var(--gap-md);
  width: 100%;
  padding: 0.7rem 0.5rem;
  text-align: left;
  background: none; border: 0;
  border-bottom: 1px solid rgba(90, 80, 60, 0.18);
  cursor: pointer;
  transition: background var(--t-quick) ease-out;
}
.archive__row:hover { background: rgba(255, 255, 255, 0.5); }
.archive__row.is-loaded { background: rgba(30, 77, 74, 0.09); }
.archive__reel {
  font-family: var(--font-display); font-size: 1rem;
  letter-spacing: 0.06em;
  color: var(--phosphor);
  padding: 1px 7px;
  background: var(--crt-bg);
  border-radius: 2px;
  box-shadow: var(--inset-2);
}
.archive__name {
  font-size: 0.8rem; letter-spacing: 0.06em; text-transform: uppercase;
  color: var(--teal-ink);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.archive__sub {
  display: block;
  font-size: 0.62rem; letter-spacing: var(--track-label);
  color: var(--engrave);
}
.archive__len {
  font-family: var(--font-display); font-size: 1rem;
  color: var(--teal-dark);
}
.archive__load {
  font-size: 0.62rem; letter-spacing: var(--track-label); text-transform: uppercase;
  color: var(--teal-ink);
}
.archive__row.is-loaded .archive__load { color: var(--teal-ink); font-weight: 700; }
.archive__empty {
  padding: 2rem 0.5rem;
  font-size: 0.76rem; letter-spacing: var(--track-label); text-transform: uppercase;
  color: var(--engrave);
}

/* ── LOCATOR ───────────────────────────────────────────────── */
.locator {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: var(--gap-bay);
  align-items: start;
}
.locator__display {
  display: grid; gap: 0.5rem; justify-items: start;
  padding: clamp(1rem, 2.4vw, 1.5rem);
  border-radius: var(--r-xs);
  background-color: var(--crt-bg);
  background-image: radial-gradient(120% 130% at 50% 20%, #0B2422, var(--crt-bg) 70%);
  box-shadow: var(--inset-3);
}
.locator__eyebrow {
  font-family: var(--font-display); font-size: 0.78rem;
  letter-spacing: var(--track-display); text-transform: uppercase;
  color: var(--phosphor-dim);
}
.locator__place {
  font-family: var(--font-display);
  font-size: clamp(1.6rem, 4vw, 2.6rem); line-height: 1;
  letter-spacing: var(--track-display); text-transform: uppercase;
  color: var(--phosphor);
  text-shadow: 0 0 12px rgba(143, 227, 224, 0.5);
}
.locator__when {
  font-family: var(--font-display); font-size: 1.1rem;
  letter-spacing: var(--track-display); text-transform: uppercase;
  color: var(--phosphor);
  opacity: 0.85;
}
.locator__map { margin-top: var(--gap-lg); display: grid; gap: 0.6rem; justify-items: start; }
.locator__map-note { font-size: 0.74rem; letter-spacing: 0.05em; color: var(--ink-soft); }

.ticket-list { display: grid; gap: 0.55rem; }
.ticket-list li {
  display: grid; gap: 2px;
  padding: 0.55rem 0.7rem;
  background: rgba(255, 255, 255, 0.5);
  border-left: 3px solid var(--teal);
  border-radius: 0 var(--r-xs) var(--r-xs) 0;
  box-shadow: var(--raise-1);
}
.ticket-list b {
  font-size: 0.72rem; letter-spacing: var(--track-label); text-transform: uppercase;
  color: var(--teal-ink);
}
.ticket-list span { font-size: 0.74rem; letter-spacing: 0.03em; color: var(--ink-soft); }

/* ── SERVICE REQUEST TICKET ────────────────────────────────── */
.ticket {
  max-width: 700px;
  display: grid; gap: var(--gap-lg);
  padding: clamp(1rem, 2.4vw, 1.6rem);
  background:
    repeating-linear-gradient(180deg, transparent 0 27px, rgba(30, 77, 74, 0.07) 27px 28px),
    linear-gradient(180deg, #FBF6E9, #F1E9D6);
  border: 1px solid rgba(120, 100, 60, 0.4);
  border-radius: var(--r-xs);
  box-shadow: var(--raise-3);
}
.ticket__head {
  display: flex; align-items: baseline; justify-content: space-between; gap: var(--gap-md);
  flex-wrap: wrap;
  padding-bottom: 0.6rem;
  border-bottom: 2px solid var(--teal);
}
.ticket__form-no, .ticket__no {
  font-size: 0.62rem; letter-spacing: var(--track-label); text-transform: uppercase;
  color: var(--engrave);
}
.ticket__title {
  font-weight: 700; font-size: 0.85rem;
  letter-spacing: var(--track-label); text-transform: uppercase;
  color: var(--teal-ink);
}
.ticket__grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: var(--gap-md);
}
.ticket__foot {
  display: flex; align-items: center; justify-content: space-between; gap: var(--gap-lg);
  flex-wrap: wrap;
  padding-top: 0.4rem;
  border-top: 1px dashed rgba(120, 100, 60, 0.5);
}
.ticket__stamp {
  padding: 0.3rem 0.7rem;
  font-size: 0.62rem; letter-spacing: 0.2em; text-transform: uppercase;
  color: rgba(160, 50, 40, 0.62);
  border: 2px solid rgba(160, 50, 40, 0.45);
  border-radius: 2px;
  transform: rotate(-6deg);
}
.ticket__status {
  font-size: 0.74rem; letter-spacing: 0.05em; color: var(--teal-ink);
  min-height: 1.1em;
}
.field.is-invalid input, .field.is-invalid textarea { border-color: var(--lamp-red); box-shadow: var(--inset-1), 0 0 0 1px var(--lamp-red); }

/* ── SUPPORT / COIN MECH ───────────────────────────────────── */
.support {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: var(--gap-bay);
  align-items: start;
}
.coin-mech {
  display: grid; gap: var(--gap-md); justify-items: start;
  padding: clamp(1rem, 2.4vw, 1.5rem);
  border-radius: var(--r-md);
  background: linear-gradient(180deg, #D9D3C6, #BFB8A9);
  box-shadow: var(--raise-3), inset 0 1px 0 rgba(255, 255, 255, 0.7);
}
.coin-slot {
  position: relative;
  width: 100%; height: 60px;
  display: grid; place-items: center;
}
/* brass escutcheon around the slot, screwed to the panel */
.coin-slot::before {
  content: '';
  position: absolute; top: 50%; left: 50%;
  width: 138px; height: 46px;
  transform: translate(-50%, -50%);
  background: linear-gradient(160deg, #E4D9A6, var(--brass) 45%, var(--brass-dark));
  border-radius: 5px;
  box-shadow: var(--raise-2), inset 0 1px 0 rgba(255, 255, 255, 0.55);
}
.coin-slot::after { /* two slot screws */
  content: '';
  position: absolute; top: 50%; left: 50%;
  width: 118px; height: 7px;
  transform: translate(-50%, -50%);
  background:
    radial-gradient(circle at 3px 50%, #6B5F1E 0 3px, transparent 3px),
    radial-gradient(circle at calc(100% - 3px) 50%, #6B5F1E 0 3px, transparent 3px);
}
.coin-slot__mouth {
  position: relative; z-index: 1;
  width: 84px; height: 12px;
  background: linear-gradient(180deg, #0A0908, #26221C);
  border-radius: 6px;
  box-shadow: var(--inset-3), 0 1px 0 rgba(255, 255, 255, 0.45);
}
.coin {
  position: absolute; z-index: 2; top: -4px; left: 50%;
  width: 30px; height: 30px; margin-left: -15px;
  border-radius: 50%;
  background: radial-gradient(circle at 36% 30%, #F3E6A8, var(--brass) 52%, var(--brass-dark));
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.45), inset 0 -2px 3px rgba(0, 0, 0, 0.3);
  opacity: 0;
}
.coin.is-dropping { animation: coin-drop 620ms var(--ease-mech); }
@keyframes coin-drop {
  0%   { opacity: 1; transform: translateY(-34px) rotate(0deg) scaleX(1); }
  62%  { opacity: 1; transform: translateY(24px) rotate(220deg) scaleX(0.9); }
  100% { opacity: 0; transform: translateY(34px) rotate(320deg) scaleX(0.15); }
}
.support__amounts { display: flex; flex-wrap: wrap; gap: 0.4rem; }
.chip {
  min-height: 38px; min-width: 62px;
  padding: 0.4rem 0.8rem;
  font-size: 0.72rem; letter-spacing: var(--track-label);
  color: var(--teal-ink);
  background: linear-gradient(180deg, var(--enamel), var(--enamel-deep));
  border: 1px solid rgba(60, 50, 30, 0.32);
  border-radius: var(--r-key);
  box-shadow: var(--raise-1);
  cursor: pointer;
}
.chip.is-on {
  /* teal-ink flips per theme, so this pair passes APCA in both:
     dark ink-well in daylight, light tint at night */
  background: var(--teal-ink);
  color: var(--enamel);
  box-shadow: var(--inset-2);
}
.support__note { font-size: 0.72rem; letter-spacing: 0.04em; color: var(--ink-soft); line-height: 1.6; }
.support__note code {
  font-size: 0.95em; padding: 1px 4px;
  background: rgba(30, 77, 74, 0.1); border-radius: 2px;
}
.support__other { display: grid; gap: 0.6rem; }

.port-button {
  display: inline-flex; align-items: center; gap: 0.5rem;
  min-height: var(--tap-min);
  padding: 0.6rem 1.2rem;
  font-size: 0.68rem; letter-spacing: var(--track-label); text-transform: uppercase;
  color: var(--teal-ink);
  background: linear-gradient(180deg, var(--enamel), var(--enamel-deep));
  border: 1px solid rgba(60, 50, 30, 0.34);
  border-radius: var(--r-key);
  box-shadow: var(--raise-2);
  cursor: pointer;
  text-decoration: none;
}
.port-button:active { transform: translateY(2px); box-shadow: var(--inset-2); }
.port-button--primary {
  color: var(--enamel);
  background: linear-gradient(180deg, var(--teal-light), var(--teal-dark));
  border-color: var(--teal-ink);
  text-shadow: 0 1px 0 rgba(0, 0, 0, 0.3);
}

/* ── PATCH BAY ─────────────────────────────────────────────── */
.patchbay {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(170px, 1fr));
  gap: var(--gap-md);
  margin-top: var(--gap-lg);
  max-width: 760px;
}
.jack {
  display: flex; align-items: center; gap: 0.7rem;
  width: 100%;
  min-height: 56px;
  padding: 0.7rem 1rem;
  font-size: 0.7rem; letter-spacing: var(--track-label); text-transform: uppercase;
  color: var(--teal-ink);
  text-decoration: none;
  background: linear-gradient(180deg, #D9D3C6, #C2BBAC);
  border: 1px solid rgba(60, 50, 30, 0.32);
  border-radius: var(--r-key);
  box-shadow: var(--raise-2);
  cursor: pointer;
}
.jack__socket {
  width: 20px; height: 20px; border-radius: 50%;
  background: radial-gradient(circle at 50% 50%, #0B0908 0 32%, #6E6A63 33% 62%, #C8C2B4 63%);
  box-shadow: var(--inset-2);
  transition: transform var(--t-quick) var(--ease-settle);
}
.jack:hover .jack__socket { transform: scale(1.12); }
.jack:active { transform: translateY(2px); box-shadow: var(--inset-2); }

/* ── PANEL RESPONSIVE ──────────────────────────────────────── */
@media (max-width: 860px) {
  .archive__filters { grid-template-columns: 1fr; }
  .locator, .support { grid-template-columns: 1fr; }
  .archive__row { grid-template-columns: auto minmax(0, 1fr) auto; }
  .archive__load { display: none; }
}

/* ── REEL NOTES ────────────────────────────────────────────────
   Four fields, four jobs. The summary is for browsing, the chips are
   the vibe, the takeaway is explicitly the Line's voice, and the
   transcript is the only part that is *equivalent* to the audio. */
.notes { max-width: 64ch; display: grid; gap: 2.4rem; }
.notes__block { display: grid; gap: 0.65rem; }
.notes__block p + p { margin-top: 0.45rem; }
.notes__label {
  font-size: 0.68rem; letter-spacing: var(--track-label); text-transform: uppercase;
  color: var(--engrave); text-shadow: var(--engrave-text);
}
.notes__block p { font-size: 0.94rem; line-height: 1.7; color: var(--ink); }
.notes__summary {
  font-size: clamp(1.1rem, 2.4vw, 1.45rem) !important;
  line-height: 1.42; letter-spacing: -0.012em;
  color: var(--teal-ink);
}
.notes__chips { display: flex; flex-wrap: wrap; gap: 0.4rem; }
.notes__chips li {
  padding: 0.35rem 0.8rem;
  font-size: 0.66rem; letter-spacing: var(--track-label); text-transform: uppercase;
  color: var(--teal-deep);           /* 4.5:1 over the teal wash; teal-ink missed at 3.6 */
  background: rgba(111, 178, 176, 0.16);
  border: 1px solid rgba(46, 115, 117, 0.4);
  border-radius: 999px;
}
/* the takeaway is the curator writing, and it says so */
.notes__attrib {
  font-size: 0.72rem !important;
  letter-spacing: 0.04em;
  color: var(--ink-soft) !important;
  padding-left: 0.8rem;
  border-left: 2px solid rgba(46, 115, 117, 0.45);
}

/* ── TEXT SHAPE ────────────────────────────────────────────────
   `balance` stops headings and ledes ending on a one-word line;
   `pretty` avoids orphans in running text. Progressive — browsers
   without them simply wrap as before. */
.panel__title, .plate__title, .notes__summary, .prose .lede { text-wrap: balance; }
.prose p, .notes__block p, .panel__lede, .archive__sub { text-wrap: pretty; }

/* durations and shelf numbers line up as columns */
.archive__len, .archive__reel { font-variant-numeric: tabular-nums; }

/* the plate foot stacks cleanly on a phone instead of squeezing */
@media (max-width: 520px) {
  .bay--plate .plate__foot { flex-direction: column; align-items: stretch; }
  .bay--plate .plate__transcript { width: 100%; text-align: center; }
}


/* ── EDITORIAL ARCHIVE ─────────────────────────────────────────
   The rule that makes panels read as designed rather than themed:
   HARDWARE styling belongs to the machine; PAPER follows editorial
   rules. No bevels, no pills, no uppercase titles inside a panel. */

.archive__filters .field--search input {
  background: none;
  border: 0;
  border-bottom: 1px solid rgba(90, 80, 60, 0.35);
  border-radius: 0;
  box-shadow: none;
  padding: 0.4rem 0.1rem;
  font-size: 1.05rem;
  letter-spacing: 0;
}
.archive__filters .field--search input:focus-visible {
  outline: none;
  border-bottom: 2px solid var(--teal-dark);
}

/* filters: quiet text, not latching hardware */
.archive__tag-list { gap: 0.15rem 1.1rem; }
.archive .tag {
  background: none;
  border: 0;
  box-shadow: none;
  border-radius: 0;
  padding: 0.55rem 0;
  font-size: 0.74rem;
  letter-spacing: 0.02em;
  text-transform: capitalize;
  color: var(--ink-soft);
  border-bottom: 2px solid transparent;
}
.archive .tag .tag__box { display: none; }
.archive .tag .tag__count {
  font-size: 0.85em;
  color: var(--ink-soft);
}
.archive .tag:hover { color: var(--teal-ink); }
.archive .tag[aria-pressed='true'] {
  background: none;
  box-shadow: none;
  color: var(--teal-ink);
  font-weight: 700;
  border-bottom-color: var(--teal-dark);
}

/* rows: sentence-case titles at reading size — this is a library, not a menu */
.archive__row { padding: 0.85rem 0.5rem; }
.archive__name {
  font-size: 1rem;
  font-weight: 500;
  letter-spacing: 0;
  text-transform: none;
  color: var(--ink);
}
.archive__sub { margin-top: 2px; }
.archive__len { color: var(--ink-soft); font-size: 0.92rem; }

/* ── WIDE SCREENS: CONTENT HOLDS THE CENTRE ────────────────────
   A hatch on a big laptop is wider than any comfortable measure.
   Content blocks keep their own max width and sit centred, like a
   document on a desk — never smeared into the top-left corner with
   a field of empty perforation to the right. */
.panel__body > .prose,
.panel__body > .notes {
  margin-inline: auto;
}
.panel__body > .locator,
.panel__body > .support {
  max-width: 1100px;
  margin-inline: auto;
}
.panel__body > .ticket { margin-inline: auto; }
.panel__body > .patchbay { margin-inline: auto; }
.panel__lede, .panel__status {
  max-width: 760px;
  margin-inline: auto;
}
.archive {
  max-width: 1100px;
  margin-inline: auto;
  width: 100%;
}
/* the locator's two halves share the width evenly at any size */
@media (min-width: 861px) {
  .locator { grid-template-columns: minmax(0, 1.1fr) minmax(0, 1fr); align-items: start; }
}

/* ── PHONE · PAPER STILL HAS TO BE PRESSABLE ─────────────────
   The tag filters are paper, not hardware — quiet text with an underline
   when active — and that stays exactly as it is. But paper still has to
   be a target: they were 34px tall against a 44px floor, in the one panel
   the QR visitor is most likely to open.

   Height comes from padding, so the editorial look survives. Nothing here
   adds a bevel, a pill or a border; that is the hardware vocabulary and it
   stops at the hatch. Appended at the end of the file deliberately — this
   has to win over the base `.archive .tag` rule above, and a media query
   adds no specificity of its own. */
@media (max-width: 620px) {
  .archive .tag {
    display: inline-flex;
    align-items: center;
    min-height: 44px;
    padding: 0.7rem 0;
  }
  .archive__tag-list { gap: 0.1rem 1.15rem; }

  /* the rows are the reels themselves — the most-pressed target in here */
  .archive__row { min-height: 56px; }
}

/* Lives here, at the end of the last stylesheet loaded, because these rules
   have to beat `.plate__transcript` in modules.css and `.port-button` in
   this file — all at equal specificity, where source order decides. */
/* ═══════════════════════════════════════════════════════════════
   ACTION BUTTONS — MODERN GEOMETRY, MACHINE MATERIAL
   ═══════════════════════════════════════════════════════════════

   A deliberate compromise, and worth stating so nobody "fixes" it back.

   `CLAUDE.md` bans pills inside panels, and it was right about why: a pill
   is the house style of every SaaS dashboard, and a machine that adopts it
   wholesale stops being a machine. But the ban was aimed at *flat* pills —
   the tinted rectangle with no edge, no light and no weight, which reads as
   a sticker laid on the panel.

   What actually dates the old buttons is not their shape, it is their
   TYPOGRAPHY: 0.66rem, uppercase, wide tracking. That is a legend engraved
   on metal. Correct for a control on the board, wrong for a sentence a
   person is being asked to press.

   So the split is by JOB, not by looks:

     hardware  transport keys, chassis toggles, rail keys — these ARE the
               machine. Unchanged. Engraved legends, square-ish corners.
     actions   "What they said", "Donate", "Send request", "Close" — these
               are things a visitor DOES. Modern geometry: full-round ends,
               sentence case, reading size, real hierarchy.

   And the material stays. Every button below still has an edge that catches
   light and a shadow it sits in — that is the blend, and it is what keeps
   this from looking like a template. Apple's own buttons are not flat
   either; they have a fill, a border and a weight. We just do it in enamel
   and teal instead of blue.
   ═══════════════════════════════════════════════════════════ */

.plate__transcript,
.panel__close,
.chip,
.port-button {
  /* full-round ends. `--tap-min` guarantees this never goes below the
     44px floor no matter how the padding is later adjusted. */
  border-radius: 999px;
  min-height: var(--tap-min);
  padding: 0.7rem 1.4rem;

  /* The real modernisation. Sentence case at reading size, normal width,
     almost no tracking — a phrase, not a stamped legend. */
  font-stretch: 100%;
  font-size: 0.95rem;
  font-weight: 500;
  letter-spacing: 0.005em;
  text-transform: none;
  line-height: 1.2;

  display: inline-flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  transition:
    transform var(--t-tap) var(--ease-mech),
    box-shadow var(--t-tap) var(--ease-mech);
}

/* ── SECONDARY · the outlined one ────────────────────────────
   Apple's "Shop iPhone" to the filled "Learn more". Enamel face, teal
   ink, a real border — it reads as a raised key, just a quiet one. */
.plate__transcript,
.panel__close,
.chip {
  color: var(--teal-ink);
  background-color: var(--enamel);
  background-image: linear-gradient(180deg, var(--enamel), var(--enamel-deep));
  border: 1.5px solid rgba(60, 50, 30, 0.3);
  box-shadow: var(--raise-1);
}

.plate__transcript:hover,
.panel__close:hover,
.chip:hover { box-shadow: var(--raise-2); }

.plate__transcript:active,
.panel__close:active,
.chip:active { transform: translateY(1.5px); box-shadow: var(--inset-2); }

/* a selected amount is the machine agreeing with you — it fills in */
.chip.is-on {
  color: var(--enamel);
  background-color: var(--teal-dark);
  background-image: linear-gradient(180deg, var(--teal), var(--teal-dark));
  border-color: rgba(0, 0, 0, 0.28);
}

/* ── PRIMARY · the filled one ────────────────────────────────
   One per surface. The thing we actually want pressed. */
.port-button--primary {
  color: #FBF8F1;
  background-color: var(--teal-dark);
  background-image: linear-gradient(180deg, var(--teal), var(--teal-dark));
  border: 1.5px solid rgba(0, 0, 0, 0.26);
  box-shadow: var(--raise-2);
  font-weight: 600;
}
.port-button--primary:hover { box-shadow: var(--raise-3); }
.port-button--primary:active { transform: translateY(1.5px); box-shadow: var(--inset-2); }

/* an absent transcript is stated, not hidden — keep that, restyled */
.plate__transcript.is-empty {
  color: var(--ink-soft);
  border-style: dashed;
  box-shadow: none;
}

/* On a phone the primary action deserves the full width of its card, the
   way every mobile app lays out its one obvious next step. */
@media (max-width: 620px) {
  .plate__transcript,
  .port-button--primary { width: 100%; }
}

/* ═══════════════════════════════════════════════════════════════
   THE CONTROLS GET THE SAME GEOMETRY AS THE ACTIONS
   ═══════════════════════════════════════════════════════════════

   The action buttons went full-round and the controls did not, which left
   two vocabularies on one screen — boxy 10px keys next to 999px pills. That
   reads as unfinished, not as a distinction. One radius language now, on
   everything a finger touches.

   The material is still doing the work: the toggle keeps its track, its
   travel and its shadow; the rail key keeps its raised face on the dark
   housing. Only the OUTLINE changed. A rounded control is not a flat one.
   ═══════════════════════════════════════════════════════════ */

/* ── ROCKER SWITCHES ─────────────────────────────────────────
   A real rocker is a rounded lozenge with a rounded actuator in it, so the
   square-cornered version was the least accurate thing on the chassis, not
   the most.

   LABEL LEFT, SWITCH RIGHT. This is the settings row every phone has used
   for fifteen years, and it is the fix for the dead space: with the switch
   pinned to the far edge there is no leftover gap to look wrong, however
   wide the control gets. The markup is lever-then-label, so `row-reverse`
   flips the render order without touching the HTML — and the label still
   comes first for a screen reader, which is the order it should be read. */
.toggle {
  flex-direction: row-reverse;
  justify-content: space-between;
  border-radius: 999px;
  padding: 0.3rem 0.35rem 0.3rem 0.85rem;
  gap: 0.75rem;
}
.toggle__lever {
  flex: 0 0 auto;
  width: 40px;
  height: 23px;
  border-radius: 999px;
}
.toggle__lever::after {
  border-radius: 999px;
  inset: 2.5px 2.5px 2.5px 47%;
}
.toggle[aria-checked='false'] .toggle__lever::after { inset: 2.5px 47% 2.5px 2.5px; }
.toggle__label { font-stretch: 100%; letter-spacing: 0.06em; }

/* ── THE DOCKED HOUSING, ON PHONES ───────────────────────────
   Rail keys and transport keys share one radius so the two tiers read as
   one instrument. `--tap-min` on the rail key matches the transport cap's
   height, so the two rows have the same proportion instead of one looking
   squashed under the other. */
/* ── THE RAIL KEYS, EVERYWHERE ───────────────────────────────
   Rounded on desktop too. Keeping one vocabulary matters more than the
   chip-on-a-board conceit did, and the solder legs go with it — a leg is
   for something square. */
.rail__key { border-radius: 999px; letter-spacing: 0.05em; font-stretch: 100%; }
.rail__key::after { display: none; }

/* ── SIZE: FIRM, NOT BULBOUS ─────────────────────────────────
   The first pass at full-round made everything look inflated. A pill reads
   as heavy the taller it gets, because the corner radius grows with the
   height and the straight edge disappears. The fix is not a smaller radius
   — it is less height and less air inside.

   44px stays the floor on every one of these; nothing here goes under it. */
@media (max-width: 620px) {
  .bay--deck .key__cap,
  .bay--deck .key--wide .key__cap { border-radius: 999px; height: 44px; }
  .bay--deck .key__label { font-stretch: 100%; letter-spacing: 0.05em; font-size: 0.55rem; }

  .bay--deck .transport__buttons { padding: 0.5rem 0.9rem 0.45rem; gap: 6px; }

  .rail__key { min-height: 44px; font-size: 0.75rem; padding: 0.35rem 0.2rem; }
  .rail { padding: 0.45rem 0.9rem 0.6rem; gap: 5px; }
}

/* ── PHONE · THE SWITCHES SIZE TO THEIR CONTENT ──────────────
   The two-column grid fixed the mid-word label breaks and created a worse
   problem: every switch was stretched to 177px whether it needed it or
   not, so the label sat at one end and the actuator at the other with a
   canyon between them. Stretching is what made them look huge — the
   controls were never too big, the boxes around them were.

   A wrapped row of content-width chips instead. Each control is exactly as
   wide as its own label plus its own switch, the gap between them is a
   fixed 0.5rem rather than whatever is left over, and the row centres and
   wraps on its own. FLAT stops being a lonely full-width bar for the same
   reason — nothing is being stretched to fill a column any more.

   `min-width: 0` stays: without it a flex item refuses to shrink below its
   content and pushes the chassis sideways, which is the usual cause of a
   page that scrolls horizontally on a phone and looks fine on a laptop. */
@media (max-width: 620px) {
  .nameplate__switches {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: 0.4rem;
    width: 100%;
  }

  .toggle {
    flex: 0 1 auto;
    min-width: 0;
    padding: 0.25rem 0.5rem 0.25rem 0.7rem;
    gap: 0.5rem;
  }
  .toggle--mode { padding: 0.25rem 0.7rem 0.25rem 0.55rem; }

  /* smaller travel to match — the switch was sized for a 177px box */
  .toggle__lever { flex: 0 0 auto; width: 32px; height: 19px; }
  .toggle__lever::after { inset: 2px 2px 2px 47%; }
  .toggle[aria-checked='false'] .toggle__lever::after { inset: 2px 47% 2px 2px; }

  .toggle__icon { width: 16px; height: 16px; }

  .toggle__label {
    min-width: 0;
    font-size: 0.58rem;
    letter-spacing: 0.05em;
    white-space: nowrap;
  }
}

/* RESTORED. This block was deleted by accident: a later edit replaced the
   switches section by cutting from one comment marker to the next, and the
   whole flat finish happened to be sitting between them. The symptom was
   the FLAT switch flipping its own state and changing nothing else.

   The lesson is in the method, not the block. Editing CSS by slicing
   between two landmarks assumes nothing else lives in the gap, and nothing
   checks that assumption. Verify by what a rule DOES after an edit, not by
   whether the line you meant to remove is gone. */
/* ═══════════════════════════════════════════════════════════════
   FINISH · FLAT — THE COMPARISON BUILD
   ═══════════════════════════════════════════════════════════════

   Everything the flat-design brief asks for: no drop shadows, no inner
   shadows, no bevels, no gradients, no metallic texture, no glow. Solid
   fills, hairline borders, pill geometry, plenty of air.

   It exists to be LOOKED AT next to the machine, on a phone, so the choice
   gets made by eye instead of by argument. One of these two blocks is going
   to be deleted; this one is written to lose or win honestly, not to be a
   straw man — it is a genuinely competent flat UI.

   The heavy lifting is six tokens. All depth in this project runs through
   --raise-1..3 and --inset-1..3, so neutralising those flattens the whole
   console in one move, and everything below is only mopping up gradients
   and textures that were declared literally.
   ═══════════════════════════════════════════════════════════ */

:root[data-finish='flat'] {
  /* a hairline instead of a shadow — the flat way to say "edge" */
  --raise-1: 0 0 0 1px var(--flat-line);
  --raise-2: 0 0 0 1px var(--flat-line);
  --raise-3: 0 0 0 1px var(--flat-line);
  --inset-1: none;
  --inset-2: none;
  --inset-3: none;
  --flat-line: rgba(20, 30, 30, 0.16);
  --perf-dot: transparent;          /* the enamel loses its perforation */
}

:root[data-finish='flat'][data-theme='dark'] {
  --flat-line: rgba(255, 255, 255, 0.14);
}

/* ── SURFACES · solid fills, one hairline, nothing else ────── */
[data-finish='flat'] .console,
[data-finish='flat'] .board,
[data-finish='flat'] .bay--deck,
[data-finish='flat'] .plate,
[data-finish='flat'] .panel,
[data-finish='flat'] .panel__body,
[data-finish='flat'] .archive,
[data-finish='flat'] .ticket,
[data-finish='flat'] .coin-mech,
[data-finish='flat'] .jack,
[data-finish='flat'] .readout,
[data-finish='flat'] .vu-bezel,
[data-finish='flat'] .transport__buttons,
[data-finish='flat'] .rail {
  background-image: none;
  box-shadow: 0 0 0 1px var(--flat-line);
  text-shadow: none;
}

[data-finish='flat'] .console { background-color: var(--enamel-shade); }
[data-finish='flat'] .board   { background-color: var(--enamel-shade); }
[data-finish='flat'] .bay--deck,
[data-finish='flat'] .plate,
[data-finish='flat'] .panel   { background-color: var(--enamel); }

/* the docked housing keeps its own darker fill so the keys stay legible */
[data-finish='flat'] .transport__buttons,
[data-finish='flat'] .rail { background-color: var(--bakelite); }

/* ── PURE ORNAMENT · gone ──────────────────────────────────── */
[data-finish='flat'] .grain,
[data-finish='flat'] .traces,
[data-finish='flat'] .screw,
[data-finish='flat'] .edge-connector,
[data-finish='flat'] .transport__buttons::before,
[data-finish='flat'] .transport__buttons::after,
[data-finish='flat'] .rail::before { display: none; }

/* ── CONTROLS · solid fill, flat state change ──────────────── */
[data-finish='flat'] .key__cap {
  background-image: none;
  background-color: var(--teal);
  border: 0;
  box-shadow: none;
  border-radius: 999px;
}
[data-finish='flat'] .key:active .key__cap,
[data-finish='flat'] .key__cap.is-latched { background-color: var(--teal-dark); transform: none; }
[data-finish='flat'] .key__label { text-shadow: none; }

[data-finish='flat'] .toggle {
  background-image: none;
  background-color: transparent;
  border: 1px solid var(--flat-line);
  box-shadow: none;
}
[data-finish='flat'] .toggle__lever {
  background-image: none;
  background-color: var(--enamel-recess);
  box-shadow: none;
}
[data-finish='flat'] .toggle[aria-checked='true'] .toggle__lever { background-color: var(--teal-dark); }
[data-finish='flat'] .toggle__lever::after {
  background-image: none;
  background-color: #FFFFFF;
  box-shadow: none;
}

[data-finish='flat'] .rail__key {
  background-image: none;
  background-color: transparent;
  border: 1px solid var(--flat-line);
  box-shadow: none;
}
[data-finish='flat'] .rail__key[aria-expanded='true'] { background-color: rgba(255, 255, 255, 0.08); }

/* ── BUTTONS · the brief's own filled / outlined pair ──────── */
[data-finish='flat'] .port-button--primary {
  background-image: none;
  background-color: var(--teal-dark);
  border: 0;
  box-shadow: none;
  text-shadow: none;
}
[data-finish='flat'] .plate__transcript,
[data-finish='flat'] .panel__close,
[data-finish='flat'] .chip,
[data-finish='flat'] .port-button {
  background-image: none;
  background-color: transparent;
  border: 1px solid currentColor;
  box-shadow: none;
}
[data-finish='flat'] .chip.is-on {
  background-color: var(--teal-dark);
  color: var(--enamel);
  border-color: transparent;
}

/* ── LIGHT · no glow, per the brief ────────────────────────── */
[data-finish='flat'] .lamp__glass,
[data-finish='flat'] .lamp__glass::after,
[data-finish='flat'] .seg__bar.is-lit { box-shadow: none; filter: none; }

/* ── NOTHING MOVES ON PRESS ────────────────────────────────── */
[data-finish='flat'] .key:active,
[data-finish='flat'] .plate__transcript:active,
[data-finish='flat'] .panel__close:active,
[data-finish='flat'] .chip:active,
[data-finish='flat'] .port-button:active { transform: none; box-shadow: none; }

/* the knob is turned metal — there is no flat version of a spindle, so it
   becomes a plain disc and loses the conic sweep */
[data-finish='flat'] .knob__face {
  background-image: none;
  background-color: var(--brass);
  box-shadow: none;
}

/* ── THE DRAWN PARTS · so the test is honest ─────────────────
   The reels and the screen bezel are SVG, drawn rather than styled, and
   they are also the most skeuomorphic things on the board. Leaving them
   lit and shadowed while flattening everything around them would rig the
   comparison in the machine's favour — flat would be judged against a
   half-flat page. Their cast shadows, sheens and glows come off too.

   What stays is geometry and colour, which is all a flat UI ever has. */
[data-finish='flat'] .reel,
[data-finish='flat'] .deck__reels.is-scrubbing .reel,
[data-finish='flat'] .key__icon,
[data-finish='flat'] .key.is-latched .key__icon { filter: none; }

[data-finish='flat'] .reel__hub-sheen { display: none; }

[data-finish='flat'] .scope,
[data-finish='flat'] .waveform { box-shadow: 0 0 0 1px var(--flat-line); }

/* ── MODE KEYS · icon + word, and it lights when it is on ────
   NIGHT and FLAT change how the machine looks; AUTO, SOUND and HI-CON
   change what it does. Two jobs, two controls — a rocker for behaviour, a
   lit key for appearance.

   It keeps `role="switch"` and `aria-checked`, so a screen reader still
   announces "Night, switch, off" rather than a button of unknown state.
   The look changed; what it IS did not.

   Both the icon and the word are always present. A symbol alone is a
   guess — a crescent could be sleep, mute or dim — and a guess costs most
   from the people an icon is meant to help. The word carries the meaning;
   the icon just makes it findable faster. */
.toggle--mode {
  justify-content: flex-start;
  flex-direction: row;
  gap: 0.55rem;
  padding: 0.3rem 1rem 0.3rem 0.8rem;
}
.toggle--mode .toggle__lever { display: none; }

.toggle__icon {
  flex: 0 0 auto;
  width: 19px;
  height: 19px;
  color: currentColor;
}

/* ON is not a subtle state change. The key fills, the way a lit button on
   a real panel tells you across the room that something is engaged. */
.toggle--mode[aria-checked='true'] {
  background-image: none;
  background-color: var(--teal-dark);
  border-color: transparent;
  color: #FBF8F1;
}
.toggle--mode[aria-checked='true'] .toggle__label { color: inherit; }

[data-theme='dark'] .toggle--mode[aria-checked='true'] {
  background-color: var(--teal-dark);
  color: #10201F;
}
[data-theme='dark'] .toggle--mode[aria-checked='true'] .toggle__label { color: inherit; }

/* the flat finish already strips fills — give the lit state its border back
   so the on/off difference survives with no shadow to carry it */
[data-finish='flat'] .toggle--mode[aria-checked='true'] {
  background-color: var(--teal-dark);
  color: var(--enamel);
}

/* ── THE TRACK HAS TO SAY WHICH WAY IT IS ────────────────────
   The actuator moved and nothing else did, so the track sat black in both
   states and the only thing carrying on-or-off was the knob's position.
   That is a real failure and not only a cosmetic one: position alone is
   the weakest signal a switch can give. You have to already know which
   end means yes.

   So the well lights. Off it is the dark recess it always was; on it
   fills teal — the colour the machine is painted, doing the job a lamp
   behind a switch does on a real panel.

   Colour is the SECOND signal, never the only one: the knob still travels,
   and `aria-checked` still carries the truth for anyone who cannot see
   either. Nobody here is depending on hue alone.

   It does not pulse. A lamp may strike, breathe or sit steady, and a
   switch that is merely on is steady. */
.toggle[aria-checked='true'] .toggle__lever {
  background-color: var(--teal);
  background-image: none;
}

/* At night the enamel goes to shadow but the paint stays lit, so the track
   holds the same teal — that is the whole rule of the dark theme, and a
   switch is not an exception to it. A touch deeper so the cream actuator
   still separates from it. */
[data-theme='dark'] .toggle[aria-checked='true'] .toggle__lever {
  background-color: var(--teal-mid);
}

/* the flat finish has no inset well to read as a recess, so the off state
   needs a visible fill of its own rather than a hole */
[data-finish='flat'] .toggle[aria-checked='true'] .toggle__lever {
  background-color: var(--teal);
}

/* The lit track and the light actuator are close in tone — measured 2.1:1,
   under the 3:1 a UI boundary needs. Darkening the track would fix that and
   spend the on/off difference doing it, which is the thing we just bought.

   So the edge does the work instead of the fill. A hairline ring separates
   the actuator from any colour behind it at any brightness, which a
   luminance gap only manages against one. Same trick a real switch uses:
   the cap has a shadow line around it, not a different paint. */
.toggle[aria-checked='true'] .toggle__lever::after {
  box-shadow:
    0 0 0 1px rgba(0, 0, 0, 0.45),
    0 1px 2px rgba(0, 0, 0, 0.5);
}

/* flat has no shadows anywhere, so the ring is all there is — and it has to
   be, or the actuator dissolves into the teal it sits on */
[data-finish='flat'] .toggle__lever::after {
  box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.4);
}

/* ═══════════════════════════════════════════════════════════════
   PHONE · A PANEL IS A SHEET, NOT A HATCH IN THE BOARD
   ═══════════════════════════════════════════════════════════════

   `.panels` is `position: absolute; inset: 0` inside `.board`, so an open
   panel is exactly as tall as the board is. On a laptop that is right —
   the board is one screenful and the hatch covers it.

   On a phone the board is the whole console stacked vertically, well over
   a thousand pixels. So a panel with a short form in it — Bring It is a
   handful of fields — was inheriting that entire height and leaving
   hundreds of pixels of blank paper underneath. It read as the page being
   broken, and no amount of overscroll tuning was ever going to touch it,
   because the empty space was not overscroll. It was the panel.

   Fixed to the viewport instead: the panel becomes a sheet exactly one
   screen tall, scrolling its own content, the way every phone app presents
   this. The blank tail cannot exist because there is no extra height to
   inherit.

   That also fixes the top. Content was sliding under the clock because the
   sheet was scrolling with the page; anchored to the viewport it can hold
   its own safe-area inset and the heading stops where the glass does. */
@media (max-width: 620px) {
  .panels {
    position: fixed;
    inset: 0;
    z-index: 200;               /* above the docked transport, which hides anyway */
  }

  .panel {
    border-radius: 0;
    /* the sheet owns the screen — no rounded hatch corners to give away
       that this was ever a rectangle sitting on a board */
    box-shadow: none;
  }

  /* the notch and the home indicator are the sheet's problem now */
  .panel__head {
    padding-top: calc(clamp(1rem, 2.2vw, 1.5rem) + env(safe-area-inset-top, 0px));
  }
  .panel__body,
  .archive {
    padding-bottom: calc(1.5rem + env(safe-area-inset-bottom, 0px));
  }

  /* The hinge and the drawer rails are hatch hardware — they exist to say
     "this lid opened out of the board". A sheet did not open out of
     anything, it covers the screen, so the fittings have nothing to fit
     to. The hinge was also sitting in the safe area, three metal nubs
     under the clock. */
  .panel__rails,
  .panel__hinge { display: none; }
}

/* ═══════════════════════════════════════════════════════════════
   PHONE · ONE SCROLLER PER SHEET
   ═══════════════════════════════════════════════════════════════

   The archive had two scrollers inside each other: the sheet body, and
   the reel list nested within it. On a mouse that is merely untidy. Under
   a thumb it is the thing that feels broken — you drag, the inner list
   moves, it reaches its end, and the drag stops dead while a second
   surface underneath it takes over. Every one of those handovers is a
   jolt, and neither surface ever bounces the way a phone has taught you
   to expect.

   A sheet gets one scroller, always. The list stops being a window with
   its own scrollbar and simply flows; the sheet moves as a single piece
   from the first reel to the last.

   The filter bar stays put while it does. That is the one thing you want
   still reachable after scrolling a shelf of forty-eight, and pinning it
   costs nothing now that there is only one thing moving. */
@media (max-width: 620px) {
  /* The scrolling MOVES rather than switching off. `.archive__list` was
     the archive panel's only scroller — `.archive` around it never had
     one — so simply disabling the list left forty-eight reels clipped at
     the first screenful with no way down. Verified that the hard way.

     The list stops scrolling and `.archive` starts, which puts the single
     scroller at the sheet level where the filters can stick to the top of
     it. One surface moves, from the first reel to the last. */
  .archive {
    /* `display: block`, not the desktop grid. A sticky item inside a grid
       is stuck to its own grid AREA, and the filter row's area is exactly
       as tall as the filters — so there is nowhere for it to travel and it
       never sticks. Letting the children flow normally gives it the whole
       scroll length to stay pinned across. */
    display: block;
    overflow-y: auto;
    min-height: 0;
  }
  .archive__list {
    overflow: visible;
    max-height: none;
    min-height: 0;
  }

  /* ── THE STICKY BAR HAS TO REACH THE EDGES ────────────────
     A pinned bar only hides what is directly behind it. This one sat
     inside the sheet's 16px side padding and 14px top padding, so three
     uncovered strips were left around it — and reels slid up through
     them as you scrolled. On a dark background that reads as the bar
     being half transparent, which is exactly what it looked like.

     The bar cancels the parent's padding with a negative margin and
     puts the same amount back as its own padding. Same position on
     screen, but the painted box now runs corner to corner, so there is
     no gap left for anything to show through. */
  .archive {
    --sheet-pad: 16px;
    padding: 0 var(--sheet-pad) 1.5rem;
  }

  .archive__filters {
    position: sticky;
    top: 0;
    z-index: 5;
    margin-inline: calc(var(--sheet-pad) * -1);
    padding-inline: var(--sheet-pad);
    padding-top: 0.9rem;
    padding-bottom: 0.6rem;
    /* opaque, not translucent. A blur would still let shapes read
       through, and the point is that nothing behind it is visible. */
    background-color: var(--enamel);
    box-shadow: 0 1px 0 rgba(90, 80, 60, 0.24);
  }
}

/* ── MOMENTUM, AND WHERE THE BOUNCE STOPS ────────────────────
   `-webkit-overflow-scrolling: touch` is what gives an inner scroller the
   weighted, coasting feel instead of stopping the instant a finger lifts.
   Modern iOS does it by default; older iOS does not, and this is the one
   line that separates a panel that feels native from one that feels like
   a web page. Harmless everywhere else.

   `overscroll-behavior: contain` keeps the bounce inside the sheet: pull
   past the end of a panel and the panel gives, but the page behind it
   does not move. Without it a hard flick inside a panel scrolls the
   console underneath and the sheet appears to come loose. */
.panel__body,
.archive,
.archive__list {
  -webkit-overflow-scrolling: touch;
  overscroll-behavior: contain;
}

/* ═══════════════════════════════════════════════════════════════
   THE COLLABORATION FORM
   ═══════════════════════════════════════════════════════════════

   Written for the two readers most forms quietly exclude.

   DYSLEXIA. The old labels were 0.62rem, letter-spaced and UPPERCASE.
   Capitals remove the ascenders and descenders that give a word its
   outline, and that outline is what a dyslexic reader recognises before
   decoding the letters — so all-caps costs the exact cue that helps
   most. Sentence case at reading size, generous line height, left
   aligned, never justified, never italic for anything that matters.

   ADHD / WORKING MEMORY. A placeholder disappears the moment you type,
   which asks you to hold the question in your head while answering it.
   Labels are permanent and sit above the field, hints are real text
   rather than ghost text, and the form is cut into four numbered steps
   so there is always somewhere to come back to after a distraction.

   Neither of these is a special case. Bigger targets, visible labels
   and plain wording are better for everyone who has ever filled in a
   form on a phone in the sun.
   ═══════════════════════════════════════════════════════════ */

.form__purpose {
  margin-bottom: 1.6rem;
  font-size: 1rem;
  line-height: 1.6;
  color: var(--ink);
  max-width: 52ch;             /* ~60 characters: the comfortable line */
}

.form__step {
  border: 0;
  padding: 0 0 1.6rem;
  margin: 0 0 1.6rem;
  border-bottom: 1px solid rgba(90, 80, 60, 0.2);
}
.form__step:last-of-type { border-bottom: 0; }

.form__legend {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  margin-bottom: 0.4rem;
  padding: 0;
  font-size: 1.1rem;
  font-weight: 700;
  line-height: 1.35;
  letter-spacing: 0;
  text-transform: none;        /* sentence case, deliberately */
  color: var(--ink);
}
/* the step number is a landmark, not decoration — it is what someone
   returning to the form after a minute away looks for first */
.form__step-no {
  display: grid;
  place-items: center;
  flex: 0 0 auto;
  width: 26px; height: 26px;
  border-radius: 999px;
  background-color: var(--teal-dark);
  color: var(--enamel);
  font-size: 0.82rem;
  font-weight: 700;
}
[data-theme='dark'] .form__step-no { color: #10201F; }

.form__hint,
.field__hint {
  margin: 0 0 0.55rem;
  /* 15.2px. Hints were 14.7 — under the 16px that dyslexia guidance asks
     for body text, and this text is not decoration: it is where "roughly
     is fine" and "we will never add you to a list" live. Supporting text
     that nobody can read is just clutter with good intentions. */
  font-size: 0.95rem;
  line-height: 1.55;
  letter-spacing: 0;
  text-transform: none;
  color: var(--ink-soft);
  max-width: 52ch;
}

/* ── FIELDS ──────────────────────────────────────────────────
   The label is a real label at reading size. This overrides the
   engraved-legend treatment on purpose: engraving is hardware, and a
   form is paper. */
.form .field { display: block; margin-bottom: 1.3rem; }
.form .field:last-child { margin-bottom: 0; }

.form .field__label {
  display: block;
  margin-bottom: 0.3rem;
  font-size: 1rem;
  font-weight: 700;
  line-height: 1.4;
  letter-spacing: 0;
  text-transform: none;
  color: var(--ink);
  text-shadow: none;
}

/* "Required" and "Optional" as WORDS. An asterisk is a convention you
   have to already know, and it is announced as "star" by some screen
   readers. Optional is marked too — otherwise its absence is ambiguous. */
.field__req,
.field__opt {
  display: inline-block;
  margin-left: 0.4rem;
  padding: 0.1rem 0.45rem;
  border-radius: 999px;
  font-size: 0.72rem;
  font-weight: 400;
  letter-spacing: 0.02em;
  vertical-align: middle;
}
.field__req { background-color: var(--teal-dark); color: var(--enamel); }
.field__opt { background-color: transparent; color: var(--ink-soft); border: 1px solid rgba(90, 80, 60, 0.35); }
[data-theme='dark'] .field__req { color: #10201F; }

.form .field input,
.form .field textarea {
  font-size: 1rem;            /* 16px+ — anything less makes iOS zoom on focus */
  line-height: 1.5;
  letter-spacing: 0;
  padding: 0.7rem 0.8rem;
  min-height: 48px;
}
.form .field textarea { min-height: 110px; }

/* ── ERRORS · say what to do, never colour alone ─────────────
   A red border is invisible to a red-green colourblind reader and
   meaningless to anyone who cannot see it at all. Every error carries a
   symbol and a sentence, and the sentence names the fix rather than the
   fault: "Add a neighbourhood", not "Invalid input". */
.field__error {
  display: flex;
  align-items: flex-start;
  gap: 0.4rem;
  margin: 0.4rem 0 0;
  font-size: 0.92rem;
  line-height: 1.5;
  color: var(--lamp-red);
  font-weight: 700;
}
.field__error::before {
  content: '!';
  flex: 0 0 auto;
  display: grid; place-items: center;
  width: 18px; height: 18px;
  margin-top: 0.1rem;
  border-radius: 999px;
  background-color: var(--lamp-red);
  color: #FFF;
  font-size: 0.72rem;
}
[data-theme='dark'] .field__error { color: #FF9B93; }
[data-theme='dark'] .field__error::before { background-color: #FF9B93; color: #2A0906; }

/* ── THE CHOICE LIST ─────────────────────────────────────────
   Radio buttons as full-width rows. The whole row is the target, not a
   14px circle — and the description sits with the option instead of in
   a legend you have to remember. */
.choices { display: grid; gap: 0.55rem; }

.choice {
  display: flex;
  align-items: flex-start;
  gap: 0.75rem;
  padding: 0.85rem 1rem;
  min-height: 56px;
  border: 1px solid rgba(90, 80, 60, 0.3);
  border-radius: var(--r-md);
  background-color: var(--enamel);
  cursor: pointer;
}
.choice input {
  flex: 0 0 auto;
  width: 22px; height: 22px;
  margin: 0.15rem 0 0;
  accent-color: var(--teal-dark);
}
.choice__body { display: grid; gap: 0.15rem; }
.choice__title {
  font-size: 1rem; font-weight: 700; line-height: 1.4;
  letter-spacing: 0; color: var(--ink);
}
.choice__note {
  font-size: 0.95rem; line-height: 1.5;
  letter-spacing: 0; color: var(--ink-soft);
}

/* Selected state carries a border AND a fill AND the radio dot — three
   signals, so losing any one of them still leaves the answer readable. */
.choice:has(input:checked) {
  border-color: var(--teal-dark);
  box-shadow: inset 0 0 0 1px var(--teal-dark);
  background-color: color-mix(in srgb, var(--teal) 12%, var(--enamel));
}
.choice:has(input:focus-visible) {
  outline: var(--focus-ring);
  outline-offset: 2px;
}
.choices.is-invalid .choice { border-color: var(--lamp-red); }

/* ── PHONE ───────────────────────────────────────────────────
   Never side by side. Two fields on one row halves the width of each
   and doubles the chance of answering the wrong one. */
@media (max-width: 620px) {
  .ticket__grid { grid-template-columns: 1fr; }
  .form__legend { font-size: 1.05rem; }
  .form .field input,
  .form .field textarea { font-size: 1rem; }
}

/* ── THE RECEIPT ─────────────────────────────────────────────
   What replaces the form once it has been sent.

   Not a toast: a toast fades on a timer, and the one moment you must not
   put on a timer is the one where somebody is asking themselves whether
   their message got through. Not a dialog either, because there is
   nothing left to decide. The form is finished, so the form goes and the
   answer takes its place.

   The tick is drawn, not a character. A glyph that fails to load leaves
   an empty box exactly where reassurance was supposed to be. */
.sent {
  display: grid;
  justify-items: center;
  gap: 0.9rem;
  padding: 2.6rem 1.5rem 3rem;
  text-align: center;
}
.sent:focus { outline: none; }
.sent:focus-visible { outline: var(--focus-ring); outline-offset: 4px; }

.sent__mark {
  width: 58px; height: 58px;
  color: var(--teal-dark);
}

.sent__title {
  margin: 0;
  font-size: clamp(1.5rem, 5vw, 1.9rem);
  font-weight: 700;
  line-height: 1.25;
  letter-spacing: 0;
  text-transform: none;
  color: var(--ink);
}

.sent__body {
  margin: 0;
  max-width: 34ch;
  font-size: 1rem;
  line-height: 1.6;
  color: var(--ink);
}

/* Reads back what they sent, including the address. Somebody who mistyped
   it can see that here, while they still remember typing it. */
.sent__meta {
  margin: 0;
  max-width: 40ch;
  font-size: 0.95rem;
  line-height: 1.6;
  color: var(--ink-soft);
}

.sent .port-button { margin-top: 0.6rem; min-height: 48px; }

/* The tick draws itself once. A checkmark that simply appears is a state
   change; one that is drawn is an event, and this is an event. Static for
   anyone who has asked for less motion. */
@media (prefers-reduced-motion: no-preference) {
  .sent:not([hidden]) .sent__mark path {
    stroke-dasharray: 34;
    stroke-dashoffset: 34;
    animation: sent-tick 420ms var(--ease-settle) 120ms forwards;
  }
  .sent:not([hidden]) .sent__mark circle {
    transform-origin: 50% 50%;
    animation: sent-ring 320ms var(--ease-settle) both;
  }
}
@keyframes sent-tick { to { stroke-dashoffset: 0; } }
@keyframes sent-ring { from { transform: scale(0.86); opacity: 0; } to { transform: scale(1); opacity: 1; } }

/* ── THE EMAIL SUGGESTION ────────────────────────────────────
   Deliberately not styled as an error. Nothing is wrong yet — the address
   is valid, it is just probably not the one they meant — so red would be
   both inaccurate and a small accusation. It reads as an offer.

   The correction is a real button, not a link and not a click handler on
   a span: it does something on this page rather than going anywhere, it
   has to be reachable by keyboard, and it has to be announced as
   pressable. */
.field__suggest {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.3rem;
  margin: 0.45rem 0 0;
  font-size: 0.95rem;
  line-height: 1.5;
  color: var(--ink-soft);
}

.field__suggest-btn {
  padding: 0.15rem 0.1rem;
  border: 0;
  background: none;
  font: inherit;
  font-weight: 700;
  color: var(--teal-ink);
  text-decoration: underline;
  text-underline-offset: 3px;
  cursor: pointer;
}
.field__suggest-btn:hover { color: var(--teal-dark); }
.field__suggest-btn:focus-visible {
  outline: var(--focus-ring);
  outline-offset: 2px;
  border-radius: var(--r-xs);
}

/* the whole line is a target on a phone, not just the underlined words */
@media (max-width: 620px) {
  .field__suggest { font-size: 1rem; }
  .field__suggest-btn { min-height: 44px; display: inline-flex; align-items: center; }
}

/* ── THE TIP JAR ─────────────────────────────────────────────
   Ko-fi's page, framed inside the coin mechanism. It is somebody else's
   surface, so it does not get to pretend to be ours: it sits in a plain
   recess with a visible edge, the way the scope and the VU sit in theirs.
   Dressing a third-party payment form up as part of the machine would be
   the one place on this site where looking native is dishonest. */
.support__lede {
  margin: 0 0 1.1rem;
  max-width: 44ch;
  font-size: 1rem;
  line-height: 1.6;
  color: var(--ink);
}

.kofi {
  width: 100%;
  min-height: 420px;
  border-radius: var(--r-md);
  overflow: hidden;
  background-color: var(--enamel-recess);
  box-shadow: var(--inset-1);
}
.kofi iframe {
  display: block;
  width: 100%;
  height: 480px;
  border: 0;
}

/* Ko-fi renders its own light page. The frame keeps its own background at
   night rather than being tinted to match, because a payment form that has
   been recoloured by the site around it is a payment form people are right
   to distrust. */
[data-theme='dark'] .kofi { background-color: var(--n2); }

.support__fallback {
  margin: 0.9rem 0 0;
  font-size: 0.95rem;
  line-height: 1.6;
  color: var(--ink-soft);
}

/* Content blockers and strict privacy modes stop third-party frames, which
   is a reasonable thing for a person to have switched on. When that happens
   the recess is empty and the link underneath is the whole route — so it is
   always present, never revealed by a failure handler that may not run. */
@media (max-width: 620px) {
  .kofi { min-height: 460px; }
  .kofi iframe { height: 520px; }
  .support__lede { font-size: 1rem; }
}

/* The honeypot. Off-screen rather than display:none, because some bots
   skip hidden fields on purpose — this one is in the layout, reachable to
   a script, and invisible and unfocusable to a person. */
.honeypot {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

/* ── PHONE · FIVE CONTROLS, ONE ROW ──────────────────────────
   The arithmetic first, because it is the whole reason for this block.
   The chassis has 329px on a 375px screen. A switch with its label
   BESIDE it needs about 92px, so five of them want 480px. They wrapped
   because they genuinely did not fit — no amount of tightening was going
   to change that.

   Stacking the label UNDER the icon roughly halves the width each one
   needs, and 61px is plenty for a small symbol and a short word. That is
   the move: not a smaller control, a differently shaped one.

   THE LABELS STAY. Round icon-only buttons would fit easily, and the
   reason not to is that no icon means "high contrast" to anyone who has
   not been told. A moon could be night, sleep or mute. An accessible
   name would satisfy a screen reader and leave every sighted visitor
   guessing, which is the wrong trade when a word costs twelve pixels of
   height. */
@media (max-width: 620px) {
  .nameplate__switches {
    display: flex;
    flex-wrap: nowrap;
    gap: 4px;
    width: 100%;
  }

  .toggle--mode {
    flex: 1 1 0;
    min-width: 0;                 /* or a long label forces the row wider */
    flex-direction: column;
    justify-content: center;
    gap: 0.2rem;
    padding: 0.45rem 0.2rem 0.4rem;
    border-radius: var(--r-md);
    min-height: 52px;             /* still over the 44px floor, stacked */
  }

  .toggle--mode .toggle__icon { width: 20px; height: 20px; }

  .toggle--mode .toggle__label {
    font-size: 0.52rem;
    letter-spacing: 0.03em;
    line-height: 1.1;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    max-width: 100%;
  }

  /* the lever is gone on every one of them now — the fill IS the state */
  .toggle--mode .toggle__lever { display: none; }
}

/* ═══════════════════════════════════════════════════════════════
   PHONE · THE DOCK STOPS SHOUTING
   ═══════════════════════════════════════════════════════════════

   It was 134px — sixteen percent of the screen — and it read as a slab
   laid over the machine rather than part of it. Three things caused that,
   and only one of them was size.

   1 · EVERY KEY WAS EQUALLY LOUD. Three filled teal blocks side by side,
       so nothing was primary and the eye had nowhere to land. Play is
       what people came for; prev and next are for after. Giving them one
       weight was the single biggest cause of the block.

   2 · THE LABELS UNDER THE TRANSPORT. This is the exception to the rule
       I applied upstairs. A word is mandatory beside HI-CON because no
       symbol means high contrast to anyone. A triangle means play to
       everyone who has touched a machine in sixty years — it is the most
       settled icon in existence. Keeping the words there was consistency
       for its own sake, and it cost a whole row of height. They stay in
       `aria-label`, so nothing is lost to a screen reader.

   3 · TOO MUCH AIR INSIDE. Padding sized for a full board, not a strip.
   ═══════════════════════════════════════════════════════════ */
@media (max-width: 620px) {

  /* ── ONE PRIMARY, TWO SECONDARY ────────────────────────────
     Play keeps the paint. Prev and next become quiet keys on the same
     housing — still 44px targets, still obviously pressable, no longer
     competing with the thing you actually came to press. */
  /* Superseded by the glyph block at the end of this file. The quiet fill
     was the right idea while these were still buttons; once the chrome
     came off it was the only box left in the row, which looked like an
     oversight rather than a choice. Hierarchy is carried by glyph size
     and brightness now. */
  .bay--deck #key-prev .key__icon,
  .bay--deck #key-next .key__icon { fill: #DFE8E7; }

  /* ── THE TRANSPORT LEGENDS COME OFF ────────────────────────
     Only here. The symbols are universal; the words were a row of
     height buying nothing a stranger did not already know. */
  .bay--deck .key__label { display: none; }

  /* ── LESS AIR ──────────────────────────────────────────────── */
  .bay--deck .transport__buttons { padding: 0.45rem 0.9rem; gap: 7px; }
  .rail { padding: 0.4rem 0.9rem 0.45rem; gap: 5px; }
  /* 44 is the floor and trimming below it is not a saving, it is a
     regression — caught by measuring rather than by looking. */
  .rail__key { min-height: 44px; font-size: 0.72rem; }

  :root { --dock-h: 122px; }
}

/* ── THE CHASSIS KEYS GET A PROPER CORNER ────────────────────
   Spotted from the page, and a fair catch: the chassis keys sat at 5px
   while every other control on the machine was fully round. That was not
   a decision, it was a leftover.

   They are not pills for a real reason — a pill is for something wider
   than it is tall, and these are stacked, so full rounding turns a 68x52
   key into a lozenge with no straight edge left. The rule that holds
   both is that RADIUS FOLLOWS PROPORTION: wide and short takes a pill,
   square-ish takes a generous rounded corner. It is why a phone's
   control centre uses rounded squares for stacked toggles and pills for
   single-line buttons — same family, different proportion.

   5px was simply too tight for the size, which is what made it read as
   cheap rather than deliberate. */
.toggle--mode { border-radius: 15px; }
@media (max-width: 620px) { .toggle--mode { border-radius: 15px; } }

/* ── PHONE · THE DOCK GETS ITS PROPORTIONS RIGHT ─────────────
   Three faults, all reported from the page.

   THE BRASS WAS COLLIDING, not merely close: the fitting occupies 5–15px
   from the housing edge and the first key started at 14. They overlapped
   by a pixel and crowded each other for a good deal more.

   A fixing exists to say "this housing is bolted to something". The
   docked strip is not bolted to anything — it floats above the board on
   its own shadow — so the fittings were decorating a joint that does not
   exist, and taking the edge space from the keys to do it. They come off
   here and stay on the desktop bed, where the housing really does sit on
   the board.

   PLAY WAS TWICE THE WIDTH of its neighbours. Primary does not mean
   double: it already has the only filled cap on a dark strip, which is
   the whole of the hierarchy. The extra width was emphasis on top of
   emphasis, and it is what made the middle look oversized. 1.4x carries
   the same meaning without the shouting.

   ONE SHAPE FAMILY. The transport was fully round while the chassis keys
   are 15px. Same reasoning as up there: these caps are no longer twice
   as wide as they are tall, so a pill is no longer the honest shape for
   them. Matching the chassis makes the two rows read as one machine. */
@media (max-width: 620px) {
  .bay--deck .transport__buttons {
    grid-template-columns: 1fr 1.4fr 1fr;
    grid-template-areas: 'prev play next';
    padding-inline: 1.35rem;
    gap: 8px;
  }

  /* the fittings come off — nothing here is bolted down */
  .bay--deck .transport__buttons::before,
  .bay--deck .transport__buttons::after { display: none; }

  .bay--deck .key__cap,
  .bay--deck .key--wide .key__cap { border-radius: 15px; }

  /* The three that are not on the strip stay out of the grid entirely.
     PAUSE IS NOT ONE OF THEM, and listing it here was a real bug: play
     and pause share the same cell and swap on `is-rolling`, so hiding
     pause unconditionally meant that starting a reel hid PLAY and put
     nothing in its place. A hole where the button was, mid-playback,
     with no way to stop. This file loads after console.css, so it won
     silently over the rule that had it right. */
  .bay--deck #key-rew,
  .bay--deck #key-stop,
  .bay--deck #key-ff { display: none; }
}

/* One housing, one corner. With the transport at 15px the rail keys
   underneath were the only fully round thing left in the dock, which put
   two shapes in a single strip — the exact mismatch that started this.
   Proportion would defend a pill at 105x44, but consistency inside one
   piece of hardware outranks it: they sit on the same housing, so they
   are the same key. */
@media (max-width: 620px) {
  .rail__key { border-radius: 15px; }
}

/* ── PHONE · SIX FUNCTIONS, ONE ROW, NOTHING HIDDEN ──────────
   Expanded, the rail was 156px and the whole dock 214 — thirty percent
   of the screen. The keys were already at the 44px floor, so trimming
   was not available: the expansion itself was the cost.

   Six keys fit one row once each is a symbol above a word, exactly like
   the chassis. 51px each is plenty for an icon and a short label. So the
   fold-out goes, and MORE with it — there is nothing left to hide behind
   it, and a control that exists only to reveal other controls is a tax
   on every visit.

   That also deleted a piece of state: no expanded flag, no height that
   changes under the content, no rail to fold away when a panel opens.
   The dock is one height, always. */
@media (max-width: 620px) {
  .rail {
    display: flex;
    flex-wrap: nowrap;
    gap: 4px;
    padding: 0.4rem 0.8rem 0.45rem;
  }

  .rail__key {
    flex: 1 1 0;
    min-width: 0;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 0.15rem;
    min-height: 46px;
    padding: 0.35rem 0.15rem 0.3rem;
    border-radius: 13px;
  }

  /* the lamp was the state dot beside the word; the key's own fill says
     the same thing now and the row has no width to spare */
  .rail__key .rail__lamp { display: none; }

  .rail__icon { width: 18px; height: 18px; flex: 0 0 auto; }

  .rail__word {
    font-size: 0.5rem;
    letter-spacing: 0.02em;
    line-height: 1.1;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    max-width: 100%;
  }
}

/* Desktop keeps the word beside the lamp as before — the icon is a phone
   affordance, where the row has to carry six things across 375px. */
@media (min-width: 621px) {
  .rail__icon { display: none; }
  .rail__word { font: inherit; letter-spacing: inherit; }
}

/* ── THE POSITION HAIRLINE ───────────────────────────────────
   Sits on the top edge of the docked housing, so it reads as the seam
   where the strip meets the machine rather than as a widget added to it.

   `scaleX` on a child rather than an animated width: a transform is
   composited and costs nothing at sixty frames a second, where animating
   width forces the browser to lay the element out again on every one.
   Same reason the reels rotate rather than redraw. */
.dock-progress {
  display: none;
  height: 3px;
  background-color: rgba(255, 255, 255, 0.1);
  overflow: hidden;
}
.dock-progress span {
  display: block;
  height: 100%;
  background-color: var(--teal);
  transform: scaleX(0);
  transform-origin: left center;
}

@media (max-width: 620px) {
  .bay--deck .dock-progress {
    display: block;
    position: fixed;
    left: max(10px, env(safe-area-inset-left));
    right: max(10px, env(safe-area-inset-right));
    /* rides exactly on top of the transport tier, which is itself fixed
       above the rail — one number keeps the two welded together */
    bottom: calc(var(--rail-h, 56px) + var(--transport-h, 64px) + 10px + env(safe-area-inset-bottom, 0px));
    z-index: 61;
    border-radius: 3px 3px 0 0;
  }
}

/* ═══════════════════════════════════════════════════════════════
   PHONE · GLYPHS, NOT BUTTONS
   ═══════════════════════════════════════════════════════════════

   The reference has no buttons in it. Open any phone player and the
   transport is bare symbols sitting on the panel — no fill, no border, no
   container, nothing drawn around them. The panel is the object; the
   glyphs are printed on it.

   Ours drew a rounded rectangle around every symbol, which is what read
   as heavy and as amateur, and it is why shrinking them never helped:
   the chrome was the problem, not the size. Three boxes in a row is
   three boxes in a row at any scale.

   The touch target does not shrink with the chrome. Each control keeps
   its 44px through padding, so the finger has exactly as much to aim at
   as before — there is simply nothing painted around it. That is the
   whole trick, and it is why phone players look uncluttered while
   staying easy to hit.
   ═══════════════════════════════════════════════════════════ */
@media (max-width: 620px) {

  /* ── THE TRANSPORT ─────────────────────────────────────────── */
  .bay--deck .transport__buttons {
    grid-template-columns: 1fr 1fr 1fr;
    justify-items: center;
    align-items: center;
    padding: 0.55rem 1.5rem 0.6rem;
    gap: 0;
  }

  .bay--deck .key__cap,
  .bay--deck .key--wide .key__cap {
    width: 56px;
    height: 46px;
    background: none;
    background-color: transparent;
    border: 0;
    border-radius: 0;
    box-shadow: none;
    transform: none;
  }

  .bay--deck .key__icon { width: 23px; height: 23px; fill: #E6EDEC; }

  /* Play is the one that carries weight, and it does it by being bigger
     rather than by being boxed — the same way the reference does it. */
  .bay--deck #key-play .key__icon,
  .bay--deck #key-pause .key__icon { width: 30px; height: 30px; fill: #FFFFFF; }

  /* pressed state, since there is no cap left to depress */
  .bay--deck .key:active .key__icon { opacity: 0.55; }
  .bay--deck .key:active .key__cap { transform: none; box-shadow: none; }

  /* ── THE RAIL ──────────────────────────────────────────────── */
  .rail__key {
    background: none;
    background-color: transparent;
    background-image: none;
    border: 0;
    box-shadow: none;
    border-radius: 0;
    color: #C9D3D2;
  }
  .rail__key[aria-expanded='true'] { background-image: none; color: #FFFFFF; }
  .rail__key[aria-expanded='true'] .rail__icon { opacity: 1; }
  .rail__icon { opacity: 0.85; }
  .rail__key:active { opacity: 0.55; }
}

/* ═══════════════════════════════════════════════════════════════
   PHONE · THE DOCK GOES QUIET
   ═══════════════════════════════════════════════════════════════

   Still reading as heavy after the chrome came off, and the remaining
   reason is that it is a solid slab. The reference is not solid — the
   artwork behind it is visible through the panel. That translucency is
   most of what separates a control surface that floats from a bar that
   is bolted on, and no amount of trimming substitutes for it.

   Two tiers with a hard seam between them was the other half: it read as
   two bars stacked, not one control surface. One panel now, one blur,
   one border, with the rail merely a quieter region inside it.

   This is a deliberate departure from the no-glassmorphism rule, asked
   for directly and repeatedly. It applies to the docked strip on phones
   and nowhere else — the board, the panels and the whole desktop console
   keep their materials. A carried machine's control face catching the
   light through it is not the same claim as a fake frosted card.
   ═══════════════════════════════════════════════════════════ */
@media (max-width: 620px) {

  /* one continuous surface: the transport draws it, the rail sits inside */
  .bay--deck .transport__buttons {
    padding: 0.35rem 1.6rem 0.3rem;
    background-color: rgba(22, 20, 18, 0.94);
    background-image: none;
    -webkit-backdrop-filter: blur(34px) saturate(140%);
    backdrop-filter: blur(34px) saturate(140%);
    border-radius: 17px 17px 0 0;
    box-shadow:
      0 0 0 0.5px rgba(255, 255, 255, 0.1),
      0 12px 30px -8px rgba(15, 12, 8, 0.5);
  }

  .rail {
    padding: 0.15rem 0.7rem 0.35rem;
    background-color: rgba(22, 20, 18, 0.94);
    background-image: none;
    -webkit-backdrop-filter: blur(34px) saturate(140%);
    backdrop-filter: blur(34px) saturate(140%);
    border-radius: 0 0 17px 17px;
    /* a hairline, not a seam — one surface with two regions in it */
    box-shadow:
      inset 0 0.5px 0 rgba(255, 255, 255, 0.07),
      0 0 0 0.5px rgba(255, 255, 255, 0.1);
  }

  /* ── SMALLER ───────────────────────────────────────────────
     Every control still clears 44px; the height comes out of padding,
     which was sized for a board rather than a strip. */
  .bay--deck .key__cap,
  .bay--deck .key--wide .key__cap { width: 52px; height: 44px; }
  .bay--deck .key__icon { width: 20px; height: 20px; }
  .bay--deck #key-play .key__icon,
  .bay--deck #key-pause .key__icon { width: 26px; height: 26px; }

  .rail__key { min-height: 44px; gap: 0.1rem; padding: 0.25rem 0.1rem 0.2rem; }
  .rail__icon { width: 16px; height: 16px; }
  .rail__word { font-size: 0.46rem; letter-spacing: 0.04em; }

  .dock-progress {
    height: 2px;
    background-color: rgba(255, 255, 255, 0.14);
    border-radius: 0;
  }

  /* A square outline around a glyph with no box was the only rectangle
     left in the strip, which looked like a bug. Rounded, and inset so it
     never clips against the housing edge. */
  .bay--deck .key:focus-visible,
  .rail__key:focus-visible {
    outline: 2px solid var(--focus-ring-colour, #E9C35A);
    outline-offset: -3px;
    border-radius: 12px;
  }
}

/* WITHOUT BLUR, TRANSLUCENCY IS JUST A HOLE.
   The panel is legible at 0.8 because the blur smears what is behind it
   into a wash. On a browser that cannot blur, the same value leaves the
   board's own labels reading straight through the glyphs at full detail
   — COUNTER and VOLUME competing with PLAY. Contrast maths would still
   pass and it would still be unreadable, because the problem is
   busyness, not contrast. So where there is no blur there is no
   transparency either. */
@supports not ((backdrop-filter: blur(2px)) or (-webkit-backdrop-filter: blur(2px))) {
  @media (max-width: 620px) {
    .bay--deck .transport__buttons,
    .rail { background-color: rgb(26, 24, 22); }
  }
}

/* ═══════════════════════════════════════════════════════════════
   THE CHASSIS CONTROLS BECOME PUSH BUTTONS
   ═══════════════════════════════════════════════════════════════

   A knob you cannot turn is a lie the hand catches immediately: round
   says rotate, and a thumb that tries and fails learns not to trust the
   panel. What a machine actually uses for a two-state control is a round
   PUSH button — a cap you press, with the legend engraved on the panel
   underneath it rather than on the cap itself.

   So the control splits in two. The cap is the round face: raised metal
   when off, lit when on. The word sits below it on the panel, where a
   silk-screened legend belongs. The button is still the whole thing —
   cap and legend together — so the target is larger than the circle it
   appears to be, which is how real panels work too.

   Rotation is never implied and never offered. It presses.
   ═══════════════════════════════════════════════════════════ */

.toggle--mode {
  flex-direction: column;
  gap: 0.3rem;
  padding: 0.3rem 0.35rem 0.25rem;
  background: none;
  background-color: transparent;
  border: 0;
  box-shadow: none;
  border-radius: 12px;
}

/* the cap — a pressable face, not a dial */
.toggle__cap {
  display: grid;
  place-items: center;
  width: 46px;
  height: 46px;
  border-radius: 50%;
  background-image: linear-gradient(180deg, #E7E2D8, #C6C0B4);
  background-color: #D8D2C6;
  border: 1px solid rgba(0, 0, 0, 0.22);
  /* seated in the panel: a shadow beneath, a highlight along the top rim */
  box-shadow:
    0 2px 3px rgba(60, 50, 35, 0.28),
    inset 0 1px 0 rgba(255, 255, 255, 0.85),
    inset 0 -2px 3px rgba(0, 0, 0, 0.12);
  transition: transform var(--t-tap) var(--ease-mech),
              box-shadow var(--t-tap) var(--ease-mech),
              background-color var(--t-quick) var(--ease-mech);
}

.toggle--mode .toggle__icon { width: 21px; height: 21px; color: #37413E; }

/* ON — the cap lights. Same signal the lamps use: the machine is painted
   teal, and the parts that are live are the parts that are lit. */
.toggle--mode[aria-checked='true'] {
  background: none;
  background-color: transparent;
  border-color: transparent;
}
.toggle--mode[aria-checked='true'] .toggle__cap {
  background-image: linear-gradient(180deg, var(--teal-light), var(--teal-mid));
  background-color: var(--teal);
  border-color: rgba(20, 60, 58, 0.5);
  box-shadow:
    0 2px 3px rgba(20, 50, 48, 0.32),
    inset 0 1px 0 rgba(255, 255, 255, 0.5),
    inset 0 -2px 3px rgba(0, 0, 0, 0.16);
}
.toggle--mode[aria-checked='true'] .toggle__icon { color: #10322F; }

/* pressed — the cap travels down into the panel, the way a real one does */
.toggle--mode:active .toggle__cap {
  transform: translateY(1px);
  box-shadow:
    0 1px 1px rgba(60, 50, 35, 0.3),
    inset 0 2px 4px rgba(0, 0, 0, 0.28);
}

/* the legend is engraved on the panel, not printed on the cap */
.toggle--mode .toggle__label {
  font-size: 0.55rem;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--engrave);
  text-shadow: var(--engrave-text);
}
.toggle--mode[aria-checked='true'] .toggle__label { color: var(--ink); }

/* focus belongs on the whole control, not the circle inside it */
.toggle--mode:focus-visible { outline: var(--focus-ring); outline-offset: 2px; }

@media (max-width: 620px) {
  .toggle--mode { padding: 0.25rem 0.15rem 0.2rem; gap: 0.25rem; }
  .toggle__cap { width: 42px; height: 42px; }
  .toggle--mode .toggle__icon { width: 19px; height: 19px; }
  .toggle--mode .toggle__label { font-size: 0.5rem; letter-spacing: 0.04em; }
}

[data-theme='dark'] .toggle__cap {
  background-image: linear-gradient(180deg, var(--n4), var(--n2));
  background-color: var(--n3);
  border-color: rgba(255, 255, 255, 0.12);
  box-shadow:
    0 2px 4px rgba(0, 0, 0, 0.55),
    inset 0 1px 0 rgba(255, 255, 255, 0.08);
}
[data-theme='dark'] .toggle--mode .toggle__icon { color: #C9D3D2; }
[data-theme='dark'] .toggle--mode[aria-checked='true'] .toggle__cap {
  background-image: linear-gradient(180deg, var(--teal), var(--teal-mid));
  background-color: var(--teal-mid);
}
[data-theme='dark'] .toggle--mode[aria-checked='true'] .toggle__icon { color: #08211F; }

/* The lit state still carried `--raise-1` plus a brass ring from the
   original rocker rule, which drew a faint raised plate around the whole
   control. On a rocker that plate WAS the switch; here the cap is the
   switch and the plate is a second object behind it, so AUTO and SOUND
   looked mounted on something the other three were not. The shell is
   flat panel in every state — only the cap is hardware. */
.toggle--mode[aria-checked='true'] { box-shadow: none; }

/* ── ONLY THE CAP IS THE BUTTON ──────────────────────────────
   Three older rules still filled the shell when a control was on — one
   for dark, one for flat, one for flat's lit state. They were correct
   when the whole control WAS the button and the fill was how it lit up.

   Now the cap is the button, so those fills draw a coloured box behind a
   circle that is already lit: two objects reporting one state, and the
   box is the louder of the two. Exactly the "covered thing" reported.

   The shell is panel in every theme, every finish, every state. It exists
   to hold a cap and a legend and to be the target — never to be seen. */
.toggle--mode,
.toggle--mode[aria-checked='true'],
[data-theme='dark'] .toggle--mode,
[data-theme='dark'] .toggle--mode[aria-checked='true'],
[data-finish='flat'] .toggle--mode,
[data-finish='flat'] .toggle--mode[aria-checked='true'] {
  background: none;
  background-color: transparent;
  background-image: none;
  border-color: transparent;
  box-shadow: none;
  color: inherit;
}

/* the legend still has to change when the cap lights, since it is the
   only part of the control that carries a word */
[data-theme='dark'] .toggle--mode .toggle__label { color: var(--engrave); }
[data-theme='dark'] .toggle--mode[aria-checked='true'] .toggle__label { color: var(--ink); }

/* ── STICKY HOVER ────────────────────────────────────────────
   `.rail__key:hover` added a shadow. On a touchscreen there is no such
   thing as leaving an element, so the hover state latches on after a tap
   and stays — which is the glow left sitting on SUPPORT after pressing
   it. It was never a design decision, it is a mouse rule applied to a
   thumb.

   `hover: hover` restricts it to pointers that can genuinely hover. */
.rail__key:hover { box-shadow: none; }

@media (hover: hover) and (pointer: fine) {
  .rail__key:hover { box-shadow: var(--raise-3); }
}

@media (max-width: 620px) {
  /* the docked rail has no chrome at all, so not even a real mouse
     should paint a box on it — brightness carries the hover instead */
  .rail__key:hover { box-shadow: none; }
  @media (hover: hover) and (pointer: fine) {
    .rail__key:hover { box-shadow: none; color: #FFFFFF; }
  }
}

/* THE FROST HAD TO GET STRONGER, AND THE REASON IS TYPOGRAPHY.
   A big bold heading behind a light frost stays legible — THE GREETING
   was reading straight through the panel and colliding with the play
   glyph. Small text and textures blur away at 22px; display type does
   not, because the strokes are wide enough to survive it.

   So blur goes to 34 and opacity to 0.88. Still visibly translucent —
   the reels and the knob move behind it as you scroll — but nothing
   behind it can be read as words any more, which is the actual
   requirement. Translucency is for depth, not for reading two things at
   once. */

/* 0.94, NOT 0.88, AND THE REASON IS THAT I CANNOT SEE THE BLUR.
   Screenshot capture in this environment does not composite
   backdrop-filter, so every check of the frost came back showing the
   plate title reading straight through the panel. The computed style
   says the blur is applied; the picture cannot confirm it.

   Guessing in that situation is how a legibility bug ships. So the panel
   is opaque enough that reading it never DEPENDS on the blur landing —
   if the frost works, it adds depth on top; if it silently fails on some
   browser, nothing behind it is legible anyway. The blur became a
   finish rather than a load-bearing part. */

/* ── THE SUGGESTION LIST ─────────────────────────────────────
   Paper, not hardware: it belongs to the form it sits in, so no bevel,
   no housing, no key caps. A raised sheet with a hairline, the way a
   dropdown behaves on paper.

   Positioned relative to the field rather than the panel, so it follows
   the input when the sheet scrolls instead of detaching from it. */
.field { position: relative; }

.suggest {
  position: absolute;
  left: 0; right: 0;
  z-index: 20;
  margin: 0.2rem 0 0;
  padding: 0.25rem;
  max-height: 232px;
  overflow-y: auto;
  overscroll-behavior: contain;
  list-style: none;
  background-color: var(--paper-hi);
  border: 1px solid rgba(90, 80, 60, 0.3);
  border-radius: 12px;
  box-shadow: 0 12px 28px -10px rgba(40, 32, 18, 0.4);
}

.suggest__option {
  padding: 0.7rem 0.75rem;
  min-height: 44px;
  display: flex;
  align-items: center;
  border-radius: 8px;
  font-size: 1rem;
  line-height: 1.4;
  color: var(--ink-soft);
  cursor: pointer;
}
/* the completion is what is being offered, so it is the part that is bold */
.suggest__option b { color: var(--ink); font-weight: 700; }

/* Highlight follows aria-selected, not :hover — the keyboard and the
   pointer then agree on which option is current, instead of the mouse
   showing one thing and the arrow keys another. */
.suggest__option[aria-selected='true'] {
  background-color: color-mix(in srgb, var(--teal) 18%, transparent);
}
@media (hover: hover) {
  .suggest__option:hover { background-color: color-mix(in srgb, var(--teal) 12%, transparent); }
}

[data-theme='dark'] .suggest {
  background-color: var(--n4);
  border-color: rgba(255, 255, 255, 0.14);
  box-shadow: 0 14px 30px -10px rgba(0, 0, 0, 0.7);
}

/* ── THE DERIVED ROW ─────────────────────────────────────────
   City and state, filled from the zip. One field visually, because they
   are one answer — asking for them separately after promising to supply
   them was the contradiction this replaces.

   No "Required" pill on either. The zip above carries the requirement;
   restating it here would be the same broken promise in a smaller font. */
.field--derived .derived {
  display: grid;
  grid-template-columns: 1fr 4.5rem;
  gap: 0.5rem;
}
.derived__part { display: block; min-width: 0; }
.derived__part input { width: 100%; text-align: left; }
.derived__part--state input { text-align: center; text-transform: uppercase; }

/* Filled by the machine rather than typed, so it reads as an answer
   already given — not another empty box. */
.field--derived input {
  background-image: none;
  background-color: color-mix(in srgb, var(--teal) 9%, var(--enamel));
}

/* ── INPUTS THE EYE CAN FIND ─────────────────────────────────
   Flagged in review: cream fields on cream paper with very light borders
   sit under the 3:1 that WCAG asks for on a control's boundary, so the
   edge of a field was a guess. The glossy inset gradient was doing the
   same damage from the other side — a sheen reads as a surface, and a
   surface reads as something you look at rather than type into.

   Flat fill, and a border dark enough to be the thing that says "here". */
.form .field input,
.form .field textarea {
  background-image: none;
  background-color: var(--paper-hi);
  border: 1px solid rgba(60, 52, 34, 0.42);
  box-shadow: none;
}
[data-theme='dark'] .form .field input,
[data-theme='dark'] .form .field textarea {
  background-color: var(--n2);
  border-color: rgba(255, 255, 255, 0.26);
}
.form .field input:focus-visible,
.form .field textarea:focus-visible {
  outline: var(--focus-ring);
  outline-offset: 1px;
}

/* The ruled paper runs behind the labels and the helper text, which is
   noise for exactly the readers the rest of this form was written for.
   The lines stay on the ticket around it; they come off the part being
   read and filled. */
.form .field,
.form__legend,
.form__hint { background-image: none; }
