/* GlastoGroups — a Solari split-flap departure board.
   The board is a physical object: a dark case, per-character flap modules,
   a hairline seam across every character where the flap splits, and warm-white
   printing. Amber means in motion, green means banked, grey means not yet.
   There is no light mode: the object is dark, and it gets glanced at for hours
   next to a blinding queue page. */

@font-face {
  font-family: 'Martian Mono';
  src: url('/fonts/martian-mono-latin.woff2') format('woff2');
  font-weight: 400 800;
  font-display: swap;
  unicode-range: U+0000-00FF, U+2013-2014, U+2018-201D, U+2026, U+00B7;
}
@font-face {
  font-family: 'Archivo';
  src: url('/fonts/archivo-latin.woff2') format('woff2');
  font-weight: 400 800;
  font-display: swap;
  unicode-range: U+0000-00FF, U+2013-2014, U+2018-201D, U+2026, U+00B7;
}

:root {
  --mono: 'Martian Mono', ui-monospace, 'SF Mono', Menlo, monospace;
  --sans: 'Archivo', ui-sans-serif, system-ui, sans-serif;

  /* the case */
  --case:      #0b0d11;
  --case-up:   #14181f;
  --case-lip:  #1d222b;
  --rule:      #21262f;

  /* a flap module, lit from above */
  --flap-hi:   #262b34;
  --flap-mid:  #1c2129;
  --flap-lo:   #12161c;
  --flap-foot: #1a1f27;
  --seam:      #04060a;

  /* printing */
  --ink:       #f4eee2;
  --ink-soft:  #b9bfca;
  --ink-dim:   #8a92a1;
  --amber:     #ffb020;
  --amber-lo:  #7a5205;
  --green:     #57e06a;
  --green-lo:  #0f3b17;
  --green-ink: #05230c;
  --red:       #ff6a52;

  --gutter: clamp(0.75rem, 3.2vw, 1.5rem);
  --col-code: 3.6rem;
  --ease: cubic-bezier(0.16, 1, 0.3, 1);
}

*, *::before, *::after { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  background: var(--case);
  color: var(--ink);
  font-family: var(--sans);
  font-size: 16px;
  line-height: 1.45;
  font-synthesis-weight: none;
  -webkit-font-smoothing: antialiased;
  /* the case sits under a soft overhead light */
  background-image: radial-gradient(120% 60% at 50% -10%, #171c24 0%, var(--case) 62%);
  background-attachment: fixed;
  min-height: 100dvh;
}

/* Browser surfaces belong to the board too. */
::selection { background: var(--amber); color: #1a1200; }
:focus-visible {
  outline: 2px solid var(--amber);
  outline-offset: 2px;
  border-radius: 1px;
}
* { scrollbar-color: var(--case-lip) var(--case); scrollbar-width: thin; }
::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-track { background: var(--case); }
::-webkit-scrollbar-thumb { background: var(--case-lip); border: 2px solid var(--case); }
::-webkit-scrollbar-thumb:hover { background: #2b323d; }

[hidden] { display: none !important; }

/* ───────────────────────────────────────────── the flap module itself ── */

.flaps {
  display: inline-flex;
  gap: 0.13em;
  font-family: var(--mono);
  font-weight: 700;
  letter-spacing: 0;
  vertical-align: baseline;
}

.flap {
  position: relative;
  display: grid;
  place-items: center;
  inline-size: 1.02em;
  block-size: 1.42em;
  overflow: hidden;
  border-radius: 0.07em;
  color: var(--ink);
  background: linear-gradient(
    180deg,
    var(--flap-hi) 0%, var(--flap-mid) 49.2%,
    var(--flap-lo) 50.8%, var(--flap-foot) 100%);
  box-shadow:
    inset 0 0.045em 0 rgb(255 255 255 / 0.08),
    inset 0 -0.045em 0 rgb(0 0 0 / 0.5),
    0 0.05em 0.12em rgb(0 0 0 / 0.55);
}

/* the seam: where the flap splits. the whole illusion lives here. */
.flap::after {
  content: "";
  position: absolute;
  inset-inline: 0;
  top: 50%;
  block-size: 1px;
  background: var(--seam);
  box-shadow: 0 1px 0 rgb(255 255 255 / 0.05);
}

.flap__c {
  display: block;
  font-size: 0.82em;
  line-height: 1;
  transform: translateZ(0);
}

/* a gap in the printing — a blank module, not a missing one */
.flap--void { color: transparent; }

/* riffling through to the next character */
.flap--turn .flap__c { animation: riffle 80ms linear; }
@keyframes riffle {
  from { transform: translateY(-46%) scaleY(0.3); opacity: 0.5; }
  to   { transform: none; opacity: 1; }
}

.flaps--amber .flap__c  { color: var(--amber); }
.flaps--dim   .flap__c  { color: var(--ink-dim); }
.flaps--red   .flap__c  { color: var(--red); }
.flaps--grey  .flap__c  { color: var(--ink-soft); }

/* banked: the flap turns over to its reversed face */
.flaps--banked .flap {
  background: linear-gradient(
    180deg, #6bea7c 0%, #4fd463 49.2%, #34b449 50.8%, #45c95a 100%);
  box-shadow:
    inset 0 0.045em 0 rgb(255 255 255 / 0.3),
    inset 0 -0.045em 0 rgb(0 0 0 / 0.25),
    0 0.05em 0.12em rgb(0 0 0 / 0.5);
}
.flaps--banked .flap::after { background: rgb(0 0 0 / 0.45); box-shadow: none; }
.flaps--banked .flap__c { color: var(--green-ink); }

.flaps--xl { font-size: clamp(1.65rem, 7vw, 2.5rem); }

/* ────────────────────────────────────────────────────────────── gate ── */

.gate {
  min-height: 100dvh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 1.5rem;
  padding: 2rem var(--gutter) calc(2rem + env(safe-area-inset-bottom));
}

.gate__board {
  inline-size: min(26rem, 100%);
  background: linear-gradient(180deg, var(--case-up), var(--case));
  border: 1px solid var(--rule);
  padding: clamp(1.25rem, 5vw, 2rem);
}

.gate__title {
  margin: 0;
  display: flex;
  justify-content: center;
  font-size: clamp(0.95rem, 4.4vw, 1.3rem);
}
.gate__title .flaps { flex-wrap: wrap; justify-content: center; }

.gate__sub {
  margin: 0.9rem 0 0;
  text-align: center;
  font-family: var(--mono);
  font-size: 0.7rem;
  letter-spacing: 0.18em;
  color: var(--amber);
}

.gate__form { margin-top: 1.9rem; display: grid; gap: 1rem; }

.gate__sent {
  margin: 0;
  font-size: 0.875rem;
  color: var(--ink-soft);
}
.gate__sent strong { color: var(--ink); font-weight: 600; }

.gate__foot {
  margin: 0;
  font-size: 0.75rem;
  color: var(--ink-dim);
  text-align: center;
}

/* ───────────────────────────────────────────────── fields & buttons ── */

.field { display: grid; gap: 0.4rem; }

.field__label {
  font-family: var(--mono);
  font-size: 0.625rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink-dim);
}

.field__input {
  inline-size: 100%;
  min-block-size: 3rem;
  padding: 0.6rem 0.75rem;
  font-family: var(--mono);
  font-size: 1rem;
  font-weight: 500;
  color: var(--ink);
  caret-color: var(--amber);
  background: #070a0d;
  border: 1px solid var(--rule);
  border-radius: 0;
}
.field__input::placeholder { color: #7a828f; }
/* one ring, not a border and an outline stacked on top of each other */
.field__input:focus-visible {
  outline: none;
  border-color: var(--amber);
  box-shadow: inset 0 0 0 1px var(--amber);
}

.field__input--code {
  font-size: 1.85rem;
  letter-spacing: 0.42em;
  text-indent: 0.42em;
  text-align: center;
  min-block-size: 3.75rem;
  font-weight: 700;
}

.btn {
  min-block-size: 3rem;
  padding: 0.7rem 1.1rem;
  font-family: var(--mono);
  font-size: 0.75rem;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--green-ink);
  background: var(--amber);
  border: 0;
  border-radius: 0;
  cursor: pointer;
  transition: background 140ms var(--ease), transform 140ms var(--ease);
}
.btn:hover:not(:disabled) { background: #ffc352; }
.btn:active:not(:disabled) { transform: translateY(1px); }
/* The primary key while it is sending your code. Muted, but you still have
   to be able to read what the thing you just pressed says: this was 2.62:1.
   (#9a927f on #5a5343 would have been 2.47 - worse.) */
.btn:disabled { background: #4a4437; color: #c6bea9; cursor: progress; }

.linkish {
  background: none;
  border: 0;
  /* Was 19px tall. A link-looking button is still a button you have to hit. */
  min-block-size: 2.75rem;
  display: inline-flex;
  align-items: center;
  padding: 0;
  font: inherit;
  font-size: 0.8125rem;
  color: var(--amber);
  text-decoration: underline;
  text-underline-offset: 0.22em;
  text-decoration-thickness: 1px;
  cursor: pointer;
}
.linkish:hover { color: #ffc352; }
.linkish--block { justify-self: center; }

.notice {
  margin: 1rem 0 0;
  padding: 0.65rem 0.8rem;
  font-size: 0.875rem;
  line-height: 1.4;
  color: var(--ink);
  background: #2a1512;
  border: 1px solid #6b2a20;
}
.notice[data-tone="ok"] { background: #0f2415; border-color: #2c5c33; }

/* ───────────────────────────────────────────────────── board: case ── */

.board, .admin { padding-bottom: calc(4.5rem + env(safe-area-inset-bottom)); }

.case {
  position: sticky;
  top: 0;
  z-index: 20;
  background: linear-gradient(180deg, var(--case-up) 0%, var(--case) 100%);
  border-bottom: 1px solid var(--rule);
  padding: 0 var(--gutter);
}

.case__bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  /* Three things on one line - the name, which sale is running, the clock -
     and at 320 they do not fit. Wrapping keeps each of them whole and costs a
     second line only where it has to. */
  flex-wrap: wrap;
  row-gap: 0.1rem;
  gap: 1rem;
  block-size: 2.75rem;
  border-bottom: 1px solid var(--rule);
}

.case__name {
  /* An h1 now, for a landmark that had no heading at all. */
  margin: 0;
  font-family: var(--mono);
  font-size: 0.6875rem;
  font-weight: 700;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--ink-soft);
}

.case__clock {
  font-family: var(--mono);
  font-size: 0.75rem;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.06em;
  color: var(--amber);
}

.case__back {
  background: none; border: 0;
  /* The only way out of Admin, and it was 13px tall. */
  min-block-size: 2.75rem;
  display: inline-flex;
  align-items: center;
  padding: 0;
  font-family: var(--mono);
  font-size: 0.6875rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--amber);
  cursor: pointer;
}

.case--slim { position: static; }

/* the tally, printed large */
.tally {
  display: flex;
  flex-wrap: wrap;
  gap: 0.8rem clamp(1.5rem, 7vw, 3.5rem);
  padding: 0.9rem 0 0.7rem;
}

.tally__item { display: grid; gap: 0.35rem; min-inline-size: 0; }

.tally__label {
  font-family: var(--mono);
  font-size: 0.5625rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink-dim);
}

.tally__figure { display: flex; align-items: baseline; gap: 0.3rem; }

/* the denominator is context, not news: plain print, not a flap */
.tally__of {
  font-family: var(--mono);
  font-size: clamp(0.8rem, 3vw, 1rem);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  color: #78818f;
}

.tally__left {
  margin: 0 0 0.7rem;
  font-family: var(--mono);
  font-size: 0.6875rem;
  letter-spacing: 0.04em;
  color: var(--amber);
}

/* the case's own LED strip */
.led {
  block-size: 3px;
  background: #0a0d11;
  box-shadow: inset 0 0 0 1px #191e26;
  overflow: hidden;
}
.led__fill {
  display: block;
  block-size: 100%;
  inline-size: 0;
  background: var(--green);
  box-shadow: 0 0 6px rgb(87 224 106 / 0.6);
  transition: inline-size 700ms var(--ease);
}

.cols {
  display: grid;
  grid-template-columns: var(--col-code) minmax(0, 1fr) auto;
  gap: 0.75rem;
  padding: 0.55rem 0 0.45rem;
  font-family: var(--mono);
  font-size: 0.5625rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: #7b8492;
}
.cols span:last-child { text-align: right; }
.cols__for { display: none; }

/* ───────────────────────────────────────────────────── board: rows ── */

.rows { list-style: none; margin: 0; padding: 0 var(--gutter); }

/* One row is one line, the way a board row is. Everything else is in the
   sheet you get when you tap it. */
.row {
  position: relative;
  display: grid;
  grid-template-columns: var(--col-code) minmax(0, 1fr) auto;
  grid-template-areas: "code slot right" "says says says";
  align-items: center;
  gap: 0.3rem 0.75rem;
  inline-size: 100%;
  min-block-size: 3rem;
  padding: 0.5rem 0;
  text-align: left;
  background: none;
  border: 0;
  border-bottom: 1px solid var(--rule);
  color: inherit;
  font: inherit;
  cursor: pointer;
  transition: background 160ms var(--ease);
}
.row:hover { background: #12161c; }
.row:focus-visible { outline-offset: -2px; }

.row__code { grid-area: code; font-size: 0.75rem; }

.row__slot {
  grid-area: slot;
  display: flex;
  align-items: center;
  gap: 0.5rem;
  min-inline-size: 0;
  overflow: hidden;
}

.row__status { font-size: 0.6875rem; flex: none; }

/* who this syndicate is currently buying for */
.row__for {
  flex: none;
  font-family: var(--mono);
  font-size: 0.625rem;
  font-weight: 700;
  color: var(--amber);
  white-space: nowrap;
}

.row__tickets {
  flex: none;
  font-size: 0.8125rem;
  font-weight: 700;
  color: var(--green);
  font-variant-numeric: tabular-nums;
}

.row__right {
  grid-area: right;
  display: flex;
  align-items: baseline;
  gap: 0.45rem;
  justify-content: end;
  font-family: var(--mono);
  font-size: 0.625rem;
  color: var(--ink-dim);
  white-space: nowrap;
}
.row__who {
  max-inline-size: 5.5rem;
  overflow: hidden;
  text-overflow: ellipsis;
  color: var(--ink-soft);
}
.row__when { font-variant-numeric: tabular-nums; }

/* a syndicate carrying an admin's instruction — rare, so it gets its own line */
.row__says {
  grid-area: says;
  justify-self: start;
  font-family: var(--mono);
  font-size: 0.5625rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: #1c1300;
  background: var(--amber);
  padding: 0.12rem 0.35rem;
}

.row--banked { background: rgb(87 224 106 / 0.05); }
.row--banked .row__right { color: #7d9a84; }   /* was #5f7a66, 4.13:1 */
.row--banked .row__who { color: #7d9a84; }

/* the queue: how far through the green bars.
   For a queued syndicate the bars ARE the status — no label needed, because
   "on 7 green bars" is what the group actually says to each other. */
.bars {
  display: flex;
  gap: 2px;
  align-items: center;
  flex: none;
}
.bars__cell {
  inline-size: 0.5rem;
  block-size: 1.05rem;
  background: #181d24;
  box-shadow: inset 0 0 0 1px #242b34;
}
.bars__cell--on {
  background: var(--green);
  box-shadow: inset 0 -2px 0 rgb(0 0 0 / 0.25), 0 0 5px rgb(87 224 106 / 0.45);
}
.bars__n {
  margin-inline-start: 0.4rem;
  font-family: var(--mono);
  font-size: 0.625rem;
  font-weight: 700;
  color: var(--green);
  font-variant-numeric: tabular-nums;
}

/* the one authored moment: a row that just took an update */
.row--took { animation: took 900ms var(--ease); }
@keyframes took {
  0%   { background: rgb(255 176 32 / 0.22); }
  100% { background: transparent; }
}

.board__foot {
  margin: 0;
  padding: 1rem var(--gutter) 0;
  font-family: var(--mono);
  font-size: 0.625rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: #7b8492;
}

.rows--empty { padding: 3rem var(--gutter); color: var(--ink-dim); text-align: center; }

/* On the smallest phones there is not room for both the status and who posted
   it. The status wins; the name is one tap away in the sheet. */
@media (max-width: 24rem) {
  :root { --col-code: 3.4rem; }
  .row { gap: 0.3rem 0.55rem; }
  .row__who { display: none; }
}

/* ─────────────────────────────────────────── fastext (bottom rail) ── */

.fastex {
  position: fixed;
  inset-inline: 0;
  bottom: 0;
  z-index: 30;
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: 1fr;
  background: #05070a;
  border-top: 1px solid var(--rule);
  padding-bottom: env(safe-area-inset-bottom);
}

.fastex__btn {
  min-block-size: 3.25rem;
  padding: 0.5rem 0.25rem;
  font-family: var(--mono);
  font-size: 0.625rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-dim);
  background: none;
  border: 0;
  border-top: 2px solid transparent;
  cursor: pointer;
}
.fastex__btn[aria-current="true"] { color: var(--amber); border-top-color: var(--amber); }
.fastex__btn:hover { color: var(--ink); }

/* ─────────────────────────────────────────────────────────── sheet ── */

.sheet { position: fixed; inset: 0; z-index: 40; }

.sheet__scrim {
  position: absolute;
  inset: 0;
  background: rgb(2 3 5 / 0.72);
  animation: fade 200ms var(--ease);
}

.sheet__panel {
  position: absolute;
  inset-inline: 0;
  bottom: 0;
  max-block-size: 92dvh;
  overflow-y: auto;
  overscroll-behavior: contain;
  background: var(--case-up);
  border-top: 1px solid var(--rule);
  padding: 0.5rem var(--gutter) calc(1.5rem + env(safe-area-inset-bottom));
  animation: rise 280ms var(--ease);
}

@keyframes fade { from { opacity: 0; } }
@keyframes rise { from { transform: translateY(14%); opacity: 0; } }

.sheet__grab {
  inline-size: 2.5rem;
  block-size: 3px;
  margin: 0.25rem auto 0.9rem;
  background: #333b47;
}

.sheet__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
}

.sheet__title {
  margin: 0;
  font-family: var(--mono);
  font-size: 1.35rem;
  font-weight: 700;
  letter-spacing: 0.04em;
}

.sheet__close {
  display: grid;
  place-items: center;
  inline-size: 2.75rem;
  block-size: 2.75rem;
  margin-inline-end: -0.6rem;
  color: var(--ink-dim);
  background: none;
  border: 0;
  cursor: pointer;
}
.sheet__close:hover { color: var(--ink); }

.sheet__state {
  margin: 0.35rem 0 0;
  font-size: 0.875rem;
  color: var(--ink-soft);
}
.sheet__state b { color: var(--ink); font-weight: 600; }

.sheet__h {
  margin: 1.75rem 0 0.6rem;
  font-family: var(--mono);
  font-size: 0.5625rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink-dim);
  font-weight: 700;
}

/* The actions are flap keys: the same face, the same seam. Pressing the green
   one is what turns your row over. */
.acts { display: grid; gap: 0.5rem; margin-top: 1.25rem; }

.act {
  position: relative;
  display: flex;
  align-items: center;
  gap: 0.7rem;
  min-block-size: 3.25rem;
  padding: 0.65rem 0.9rem;
  font-family: var(--mono);
  font-size: 0.75rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  text-align: left;
  color: var(--ink);
  background: linear-gradient(
    180deg, var(--flap-hi) 0%, var(--flap-mid) 49.4%,
    var(--flap-lo) 50.6%, var(--flap-foot) 100%);
  border: 1px solid #262c36;
  border-radius: 0;
  cursor: pointer;
  transition: filter 140ms var(--ease), transform 120ms var(--ease);
}
/* No seam on a key: at button width, across left-aligned text, it reads as a
   strikethrough rather than a flap. The face carries it on its own. */
.act:hover { filter: brightness(1.28); }
.act:active { transform: translateY(1px); }
/* Dimming the whole key with opacity took its text to 3.07:1 and the amber
   Admin tag to 2.61. Stating the disabled colours keeps it obviously
   disabled and still readable. */
.act:disabled { cursor: progress; color: #8f9099; }
.act:disabled .tag { background: #c0922e; color: #1c1300; }
.act--done:disabled { background: #2f4a35; border-color: #3c5c43; color: #cfe6d4; }

.act--queue  { color: var(--green); }
.act--buying { color: var(--amber); }
.act--target { color: var(--ink-soft); }
.act--kicked { color: var(--red); }
.act--ask    { color: var(--ink-dim); }
.act--admin  { color: var(--amber); }
.act--undo   { color: var(--red); }

/* the win: a key that has already turned over */
.act--done {
  color: var(--green-ink);
  border-color: #3ba351;
  background: linear-gradient(
    180deg, #6bea7c 0%, #4fd463 49.4%, #34b449 50.6%, #45c95a 100%);
}
.act__text { color: currentColor; }
.act__tag { margin-inline-start: auto; }

/* Choosing how many green bars. The queue's scale is not known until the sale
   starts and can be thirty, so the keys wrap rather than squeezing into one
   row - and every key stays thumb-sized however many there are. */
.act__bars {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(2.75rem, 1fr));
  gap: 3px;
  margin-block-start: 0.5rem;
}
.act__bar {
  min-block-size: 2.75rem;
  display: grid;
  place-items: center;
  font-family: var(--mono);
  font-size: 0.6875rem;
  font-weight: 700;
  color: var(--green);
  background: #0d1a10;
  border: 1px solid #235c2d;
  cursor: pointer;
  padding: 0;
}
.act__bar:hover { background: var(--green); color: var(--green-ink); }

.picker {
  display: flex;
  flex-wrap: wrap;
  gap: 0.35rem;
  padding: 0.75rem 0 0.25rem;
}
.picker__opt {
  min-inline-size: 3.1rem;
  min-block-size: 2.75rem;
  padding: 0.35rem 0.5rem;
  font-family: var(--mono);
  font-size: 0.6875rem;
  font-weight: 700;
  color: var(--ink);
  background: #171c23;
  border: 1px solid var(--rule);
  cursor: pointer;
}
.picker__opt:hover { border-color: var(--amber); color: var(--amber); }
.picker__opt--self { color: var(--amber); }

/* the log */
.log { list-style: none; margin: 0; padding: 0; display: grid; gap: 0; }

.log__item {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 0.7rem;
  padding: 0.7rem 0;
  border-bottom: 1px solid var(--rule);
  font-size: 0.875rem;
}
.log__item:last-child { border-bottom: 0; }

.log__time {
  font-family: var(--mono);
  font-size: 0.6875rem;
  font-variant-numeric: tabular-nums;
  color: var(--ink-dim);
  padding-top: 0.12rem;
}

.log__what { margin: 0; color: var(--ink); }
.log__what b { font-weight: 700; }
.log__who { color: var(--ink-dim); font-size: 0.8125rem; }

.log--empty { color: var(--ink-dim); font-size: 0.875rem; padding: 0.5rem 0; }

/* ──────────────────────────────────────────────────────────── admin ── */

.panel {
  padding: 1.5rem var(--gutter) 0;
  max-inline-size: 46rem;
}

.panel__h {
  margin: 0 0 0.35rem;
  font-family: var(--mono);
  font-size: 0.8125rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--amber);
}

.panel__note { margin: 0 0 1rem; font-size: 0.875rem; color: var(--ink-dim); }

.panel__form { display: grid; gap: 0.9rem; margin-top: 1.25rem; }

.people, .targets { list-style: none; margin: 0; padding: 0; }

.person, .target {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  padding: 0.7rem 0;
  border-bottom: 1px solid var(--rule);
}
/* Two numbers per syndicate now, so at 320 the note drops to its own line
   rather than squeezing the fields. */
.target { flex-wrap: wrap; }

.person__id { min-inline-size: 0; flex: 1; }
.person__name { display: block; font-weight: 600; }
.person__mail {
  display: block;
  font-family: var(--mono);
  font-size: 0.75rem;
  color: var(--ink-dim);
  overflow: hidden;
  text-overflow: ellipsis;
}

.tag {
  font-family: var(--mono);
  font-size: 0.5625rem;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: #1c1300;
  background: var(--amber);
  padding: 0.15rem 0.35rem;
}

.mini {
  min-block-size: 2.75rem;
  padding: 0.3rem 0.6rem;
  font-family: var(--mono);
  font-size: 0.625rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-soft);
  background: #171c23;
  border: 1px solid var(--rule);
  cursor: pointer;
}
.mini:hover { color: var(--ink); border-color: #38414e; }
.mini--danger:hover { color: var(--red); border-color: #6b2a20; }

.target__code {
  font-family: var(--mono);
  font-size: 0.8125rem;
  font-weight: 700;
  min-inline-size: 3.5rem;
}
.target__field {
  inline-size: 4.5rem;
  min-block-size: 2.75rem;
  padding: 0.3rem 0.5rem;
  font-family: var(--mono);
  font-size: 0.875rem;
  font-weight: 700;
  text-align: center;
  color: var(--ink);
  caret-color: var(--amber);
  background: #070a0d;
  border: 1px solid var(--rule);
}
.target__field:focus-visible {
  outline: none;
  border-color: var(--amber);
  box-shadow: inset 0 0 0 1px var(--amber);
}
.target__note { flex: 1 1 7rem; font-size: 0.8125rem; color: var(--ink-dim); }

.target--off .target__code { color: var(--ink-dim); }
.target--off .target__note { color: var(--amber); }

.check {
  display: flex;
  align-items: center;
  /* The whole label toggles the box, so the label is the target. */
  min-block-size: 2.75rem;
  gap: 0.6rem;
  font-size: 0.875rem;
  color: var(--ink-soft);
  cursor: pointer;
}
.check input {
  appearance: none;
  -webkit-appearance: none;
  inline-size: 1.2rem;
  block-size: 1.2rem;
  margin: 0.08rem 0 0;
  flex: none;
  display: grid;
  place-items: center;
  background: #070a0d;
  border: 1px solid var(--rule);
  cursor: pointer;
}
.check input:checked { background: var(--amber); border-color: var(--amber); }
.check input:checked::after {
  content: "";
  inline-size: 0.32rem;
  block-size: 0.62rem;
  margin-block-start: -0.14rem;
  border: solid #1c1300;
  border-width: 0 2px 2px 0;
  transform: rotate(45deg);
}

/* ───────────────────────────────────────────────────────────── boot ── */

.boot {
  position: fixed;
  inset: 0;
  display: grid;
  place-items: center;
  background: var(--case);
  z-index: 50;
}
.boot__flap {
  inline-size: 2.2rem;
  block-size: 3rem;
  background: linear-gradient(
    180deg, var(--flap-hi) 0%, var(--flap-mid) 49.2%,
    var(--flap-lo) 50.8%, var(--flap-foot) 100%);
  box-shadow: inset 0 1px 0 rgb(255 255 255 / 0.08);
  animation: bootflap 1.1s var(--ease) infinite;
}
@keyframes bootflap {
  0%, 100% { transform: scaleY(1); }
  50%      { transform: scaleY(0.08); }
}

#app[data-view="loading"] .boot { display: grid; }
#app:not([data-view="loading"]) .boot { display: none; }

/* ─────────────────────────────────────────────────────────── wider ── */

@media (min-width: 40rem) {
  :root { --col-code: 5rem; }
  .cols, .row {
    grid-template-columns: var(--col-code) 15rem minmax(0, 1fr) auto;
    gap: 0.3rem 1.25rem;
  }
  .cols { grid-template-areas: none; }
  .cols__for { display: block; }
  .row { grid-template-areas: "code slot says right"; }
  .row__says { justify-self: start; align-self: center; }
  .row__code { font-size: 0.9375rem; }
  .row__status { font-size: 0.75rem; }
  .row__right { font-size: 0.6875rem; }
  .row__who { max-inline-size: 10rem; }
  .bars__cell { inline-size: 0.6rem; block-size: 1.2rem; }
  .tally { padding: 1.2rem 0 0.85rem; }

  .board, .admin { padding-bottom: 3rem; }

  /* The rail is the last thing in the board's markup, because on a phone it
     is fixed to the bottom of the screen and belongs at the end for anyone
     reading the document in order. Made sticky in place here it stuck to
     nothing - a sticky element never rises above where it already sits, so it
     rendered under all twenty-one rows and there was no navigation on desktop
     at all. Ordering the board's children puts it at the top, where it can
     stick - and the case header, which is sticky too, docks underneath it
     rather than both fighting over the same nought. */
  .board {
    display: flex;
    flex-direction: column;
  }
  :root { --rail-h: calc(2.75rem + 1px); }
  .fastex      { order: 0; }
  .case        { order: 1; top: var(--rail-h); }   /* docks under the rail */
  .rows        { order: 2; }
  .board__foot { order: 3; }

  .fastex {
    position: sticky;
    top: 0;
    bottom: auto;
    inline-size: 100%;
    grid-auto-columns: max-content;
    justify-content: start;
    border-top: 0;
    border-bottom: 1px solid var(--rule);
  }
  .fastex__btn { padding-inline: 1.5rem; min-block-size: 2.75rem; }
  .sheet__panel {
    inset: 50% auto auto 50%;
    transform: translate(-50%, -50%);
    inline-size: min(34rem, calc(100% - 2rem));
    max-block-size: 84dvh;
    border: 1px solid var(--rule);
  }
  @keyframes rise { from { transform: translate(-50%, -46%); opacity: 0; } }
  .sheet__grab { display: none; }
}

@media (min-width: 64rem) {
  /* The case is centred by its parent, not by itself. Centring it with its
     own auto margins made it shrink to fit its contents once .board became a
     flex column - auto margins beat align-self: stretch - and the header's
     background stopped reaching the ends of the rows under it. */
  .board, .admin { max-inline-size: 64rem; margin-inline: auto; }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}

/* The queue scale sits next to its label rather than above it: it is one
   small number, not a form. */
.field--inline {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  flex-wrap: wrap;
}
.field--inline .field__label { margin: 0; }
.field__input--num { inline-size: 6rem; flex: none; }

/* The only destructive thing in the app, kept visually apart from the rest
   of admin so it is never the button you meant to press. */
.panel--danger { border-block-start: 1px solid #3a1f1a; }
.btn--danger {
  color: var(--red);
  border-color: #6b2a20;
  background: #1a0f0d;
}
.btn--danger:hover { background: #2a1512; }

/* how many people are in a queue for this syndicate - quiet, it is context
   for the bars rather than news in itself */
.row__on {
  flex: none;
  margin-inline-start: 0.45rem;
  font-family: var(--mono);
  font-size: 0.5625rem;
  letter-spacing: 0.08em;
  color: var(--ink-dim);
  white-space: nowrap;
}

/* adding the whole list at once, tucked away until it is needed */
.bulk { margin-block-start: 1.25rem; border-block-start: 1px solid var(--rule); padding-block-start: 1rem; }
.bulk__open {
  font-family: var(--mono);
  font-size: 0.6875rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-soft);
  cursor: pointer;
  min-block-size: 2.75rem;
  display: flex;
  align-items: center;
}
.bulk__open:hover { color: var(--amber); }
.field__input--area { resize: vertical; min-block-size: 8rem; line-height: 1.5; }

/* Which sale is running, named in the case header beside the clock. */
.case__sale {
  margin-inline-start: 0.6rem;
  margin-inline-end: auto;
  font-family: var(--mono);
  font-size: 0.5625rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--amber);
  white-space: nowrap;
}

/* Choosing between the Thursday and Sunday sales. Two keys, the running one
   lit, because this is the switch that changes what the whole board means. */
.sale { display: flex; gap: 0.5rem; flex-wrap: wrap; }
.sale__opt {
  flex: 1 1 10rem;
  min-block-size: 3rem;
  padding: 0.6rem 0.8rem;
  font-family: var(--mono);
  font-size: 0.6875rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-soft);
  background: #171c23;
  border: 1px solid var(--rule);
  cursor: pointer;
}
.sale__opt:hover { border-color: var(--amber); color: var(--amber); }
.sale__opt[aria-current="true"] {
  color: var(--amber);
  border-color: var(--amber);
  background: #241c0c;
}

/* The admin targets list carries two numbers per syndicate, so it needs a
   header row to say which is which. */
.target--head { border-block-end-color: transparent; }
.target__cap {
  inline-size: 4.5rem;
  font-family: var(--mono);
  font-size: 0.5625rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  text-align: center;
  color: var(--ink-dim);
}

/* `act__open` carries no style: it marks which panel is showing so the DOM,
   rather than a closure, is the one true answer. */

/* Visually hidden, still read out. */
.vh {
  position: absolute;
  inline-size: 1px;
  block-size: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* The board has stopped being live. Loud, and in the first viewport next to
   the clock, because the clock is the thing that makes it look live. */
.case__stale {
  margin-inline-start: 0.6rem;
  padding: 0.15rem 0.4rem;
  font-family: var(--mono);
  font-size: 0.5625rem;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: #2a0b05;
  background: var(--red);
  white-space: nowrap;
}

/* Handing out codes */
.codes__do { display: flex; gap: 0.5rem; flex-wrap: wrap; margin-block-end: 1rem; }
.codes__do .btn { flex: 1 1 12rem; }
.tag--quiet { color: var(--ink-soft); background: #2b3340; }
