/* ==========================================================================
   PokeSim — "Bench Instrument", live page.

   tokens.css loads first and carries every colour, size and space used here.
   Only index.html loads this sheet, so its reset and base cannot leak onto the
   pages that have not been converted yet.

   Contents
     1  reset and base
     2  primitives ............ panel, legend, readout, lamp, meter, key, tag, plate
     3  chrome ................ top rail, identity strip, footer plate
     4  live page ............. bezel, deck, gamepad, plan, party bays
     5  overlays .............. partner dialog, toast
     6  responsive ............ 1100px and 640px, plus container queries on a bay
     7  motion and contrast preferences

   Spacing rhythm: 8px inside a control, 16px inside a panel, 24px between
   panels, 32px between page regions. Nothing uses a value off the --s scale.
   ========================================================================== */


/* ==========================================================================
   1  RESET AND BASE
   ========================================================================== */

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

/* Reserve the scrollbar's track on every page, so a page that grows past the
   window once its data arrives, or a short page next to a long one, keeps the
   same width and nothing slides sideways. */
html { -webkit-text-size-adjust: 100%; scrollbar-gutter: stable; }

body {
  margin: 0;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  background: var(--chassis);
  color: var(--ink);
  font: var(--f-body) / 1.55 var(--font-text);
  /* A fine machined stipple over the chassis. One repeating gradient on a
     3px cell, so it survives any zoom and costs nothing. */
  background-image: radial-gradient(var(--stipple) 0.5px, transparent 0.5px);
  background-size: 3px 3px;
}

h1, h2, h3, p, figure, ul, ol, dl, dd { margin: 0; padding: 0; }
ul, ol { list-style: none; }
img, svg { display: block; max-width: 100%; }
button, input, select { font: inherit; color: inherit; }
a { color: var(--link); text-decoration: underline; text-underline-offset: 3px; }
a:hover { text-decoration-thickness: 2px; }
[hidden] { display: none !important; }

:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 2px;
  box-shadow: 0 0 0 4px var(--focus-halo);
}

.skip-link {
  position: absolute;
  left: -9999px;
  top: var(--s2);
  padding: var(--s2) var(--s4);
  background: var(--panel);
  border: var(--hair) solid var(--rule);
  z-index: 40;
}
.skip-link:focus { left: var(--s4); }

/* One measure for the rail, the page and the footer, so their edges line up. */
.shell {
  width: 100%;
  max-width: 1360px;
  margin: 0 auto;
  padding-inline: var(--s6);
}
/* At least a window tall, so the footer starts below the fold instead of
   painting at the bottom of the empty page and being pushed away by the data. */
main.shell { flex: 1 0 auto; min-height: 100vh; padding-bottom: var(--s6); }

.vh {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}


/* ==========================================================================
   2  PRIMITIVES
   ========================================================================== */

.panel {
  background: var(--panel);
  border: var(--hair) solid var(--rule);
  box-shadow: var(--key-inset);
}

/* Silkscreen: the display face, letterspaced, in caps. */
.legend {
  font: var(--f-legend) / 1 var(--font-panel);
  letter-spacing: var(--track-legend);
  text-transform: uppercase;
  color: var(--ink-3);
}
.legend--ink { color: var(--ink); }

/* The small printed label that sits above every reading. */
.micro {
  font: var(--f-micro) / 1.4 var(--font-mono);
  letter-spacing: var(--track-micro);
  text-transform: uppercase;
  color: var(--ink-3);
}

.readout {
  font: var(--f-read) / 1 var(--font-panel);
  color: var(--ink);
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.02em;
  white-space: nowrap;
}
.unit { color: var(--ink-3); }

/* ---- lamp: a square indicator. Never round, never the only signal. ------ */
.lamp {
  display: inline-block;
  width: 8px;
  height: 8px;
  flex: none;
  background: var(--cell-off);
  box-shadow: inset 0 0 0 1px var(--rule);
}
.lamp[data-on="ok"] { background: var(--ok-lamp); box-shadow: inset 0 0 0 1px var(--rule), 0 0 6px -1px var(--ok-lamp); }
.lamp[data-on="signal"] { background: var(--signal-lamp); box-shadow: inset 0 0 0 1px var(--rule), 0 0 6px -1px var(--signal-lamp); }
.lamp[data-on="warn"] { background: var(--warn-lamp); box-shadow: inset 0 0 0 1px var(--rule), 0 0 6px -1px var(--warn-lamp); }
.lamp[data-on="crit"] { background: var(--crit-lamp); box-shadow: inset 0 0 0 1px var(--rule), 0 0 6px -1px var(--crit-lamp); }

/* The only self-starting motion in the system. It steps rather than fades,
   and the word beside it carries the same meaning for anyone who cannot see
   it blink. */
.lamp--blink { animation: blink 1.6s steps(1, end) infinite; }
@keyframes blink { 0%, 60% { opacity: 1; } 60.01%, 100% { opacity: 0.32; } }

/* ---- meter: discrete cells, never a smooth bar -------------------------- */
.meter {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: 1fr;
  gap: 1px;
  height: 10px;
  padding: 1px;
  background: var(--rule);
  border: var(--hair) solid var(--rule);
}
.meter i { background: var(--cell-off); }
.meter[data-level="ok"] i.on { background: var(--ok-lamp); }
.meter[data-level="warn"] i.on { background: var(--warn-lamp); }
.meter[data-level="crit"] i.on { background: var(--crit-lamp); }
.meter[data-level="signal"] i.on { background: var(--signal-lamp); }

/* ---- key: a button, as a physical key on the panel ---------------------- */
.key {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--s2);
  min-height: 40px;
  padding: 0 var(--s4);
  background: var(--panel-2);
  border: var(--hair) solid var(--rule);
  box-shadow: var(--key-inset);
  font: var(--f-legend) / 1 var(--font-panel);
  letter-spacing: var(--track-legend);
  text-transform: uppercase;
  color: var(--ink);
  white-space: nowrap;
  cursor: pointer;
  transition: background var(--t);
  text-decoration: none;
}
.key:hover { background: var(--panel); text-decoration: none; }
.key:active { box-shadow: var(--well-inset); background: var(--well); }
.key--primary {
  background: var(--signal);
  border-color: var(--signal);
  color: var(--focus-halo);
  box-shadow: inset 1px 1px 0 rgba(255, 255, 255, 0.22), inset -1px -1px 0 rgba(0, 0, 0, 0.30);
}
.key--primary:hover { background: var(--signal); filter: brightness(1.12); }
.key:disabled { opacity: 0.45; cursor: not-allowed; }

/* ---- tag: a type or state, stamped rather than pilled ------------------- */
.tag {
  display: inline-block;
  padding: 2px 6px 1px;
  border: var(--hair) solid var(--rule-faint);
  font: var(--f-micro) / 1.3 var(--font-mono);
  letter-spacing: var(--track-micro);
  text-transform: uppercase;
  color: var(--ink-2);
  white-space: nowrap;
}
.tag--warn { color: var(--warn); border-color: var(--warn); }
.tag--crit { color: var(--crit); border-color: var(--crit); }

/* ---- plate: the portrait well ------------------------------------------
   A backlit slide in a milled frame. Cartridge portraits are 2bpp and at most
   56x56; app.js scales each one by the largest whole number that fits, so a
   sprite is never resampled to a fractional multiple and blurred. */
.plate {
  position: relative;
  display: grid;
  /* One track the size of the plate, so a portrait bigger than it (a 96px
     pack canvas at 2x) overflows evenly and its art stays centred. */
  grid-template-columns: minmax(0, 1fr);
  grid-template-rows: minmax(0, 1fr);
  place-items: center;
  background: var(--plate);
  border: var(--hair) solid var(--rule);
  box-shadow: var(--well-inset);
  background-image: radial-gradient(var(--plate-grid) 0.5px, transparent 0.5px);
  background-size: 4px 4px;
  overflow: hidden;
}
.plate img {
  max-width: 100%;
  max-height: 100%;
  object-fit: contain;
  image-rendering: pixelated;
}
[data-theme="dark"] .plate img { filter: brightness(0.88); }
@media (prefers-color-scheme: dark) {
  [data-theme="auto"] .plate img { filter: brightness(0.88); }
}
/* No portrait installed: the well keeps its size and states the number. */
.plate-num {
  font: var(--f-read) / 1 var(--font-panel);
  color: var(--plate-ink);
  letter-spacing: 0.04em;
}
/* The inside of the frame is exactly 112px: two screen pixels per sprite
   pixel for a cartridge portrait. */
.plate--bay { width: calc(var(--sprite-2x) + 2px); height: calc(var(--sprite-2x) + 2px); }


/* ==========================================================================
   3  CHROME
   ========================================================================== */

/* ---- top rail ----------------------------------------------------------- */
.rail {
  position: sticky;
  top: var(--rail-top, 0);
  z-index: 20;
  background: var(--panel-2);
  border-bottom: var(--hair) solid var(--rule);
  /* A machined lip rather than a soft elevation: hairline, 3px of bare
     chassis, hairline. Content passes behind a hard edge. */
  box-shadow: inset 0 -2px 0 var(--bevel-lo), 0 3px 0 var(--chassis), 0 4px 0 var(--rule);
}
.rail-inner {
  display: flex;
  align-items: stretch;
  gap: var(--s5);
  min-height: 56px;
}

.brandplate {
  display: flex;
  align-items: center;
  gap: var(--s3);
  flex: none;
  color: var(--ink);
  text-decoration: none;
}
.brandplate:hover { text-decoration: none; }
.brandplate .mark {
  position: relative;
  width: 24px;
  height: 24px;
  flex: none;
  background: var(--signal);
  box-shadow: var(--key-inset);
}
/* The handheld mark, squared off: a screen and one button. */
.brandplate .mark::before {
  content: "";
  position: absolute;
  inset: 4px 4px 11px;
  background: var(--focus-halo);
}
.brandplate .mark::after {
  content: "";
  position: absolute;
  left: 9px;
  bottom: 4px;
  width: 6px;
  height: 4px;
  background: var(--focus-halo);
}
.brandplate .wordmark {
  display: block;
  font: var(--f-legend) / 1 var(--font-panel);
  letter-spacing: 0.14em;
  text-transform: uppercase;
}
.brandplate .model {
  display: block;
  margin-top: var(--s1);
  font: var(--f-micro) / 1 var(--font-mono);
  letter-spacing: var(--track-micro);
  color: var(--ink-3);
}
.brandplate .model:empty { display: none; }

/* The adventure selector sits beside the brand, which returns to the Library.
   Absent in standalone mode. */
.breadcrumb {
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: var(--s1);
  flex: none;
  min-width: 0;
  padding-left: var(--s5);
  border-left: var(--hair) solid var(--rule-faint);
}
.breadcrumb > a {
  align-self: flex-start;
  font: var(--f-micro) / 1 var(--font-mono);
  letter-spacing: var(--track-micro);
  text-transform: uppercase;
  color: var(--ink-3);
  text-decoration: none;
}
.breadcrumb > a::before { content: "\2039\00a0"; }
.breadcrumb > a:hover { color: var(--ink); text-decoration: none; }
.breadcrumb-mark { display: none; }
.adventure-switch {
  position: relative;
  display: flex;
  align-items: center;
  min-width: 0;
}
.adventure-switch select {
  appearance: none;
  min-width: 0;
  /* A fixed width: a select sizes to its longest option, which would widen it
     once routes.js lists the other adventures. */
  width: 13rem;
  height: 22px;
  margin-left: calc(-1 * var(--s2));
  padding: 0 var(--s5) 0 var(--s2);
  background: none;
  border: var(--hair) solid transparent;
  font: var(--f-legend) / 1 var(--font-panel);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  text-overflow: ellipsis;
  white-space: nowrap;
  color: var(--ink);
  cursor: pointer;
  transition: background var(--t), border-color var(--t);
}
.adventure-switch select:hover { background: var(--panel); border-color: var(--rule-faint); }
.adventure-switch select option { font: var(--f-label) / 1.4 var(--font-mono); text-transform: none; }
.adventure-switch::after {
  content: "\25BC";
  position: absolute;
  right: var(--s2);
  font: 8px / 1 var(--font-panel);
  color: var(--ink-3);
  pointer-events: none;
}

.rail nav:not(.breadcrumb) {
  display: flex;
  align-items: stretch;
  margin-left: auto;
  min-width: 0;
  overflow-x: auto;
  scrollbar-width: none;
}
.rail nav:not(.breadcrumb)::-webkit-scrollbar { display: none; }
.rail nav:not(.breadcrumb) a {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  padding: 0 var(--s4);
  border-left: var(--hair) solid var(--rule-faint);
  font: var(--f-legend) / 1 var(--font-panel);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink-2);
  text-decoration: none;
  transition: background var(--t), color var(--t);
}
.rail nav:not(.breadcrumb) a:last-child { border-right: var(--hair) solid var(--rule-faint); }
.rail nav:not(.breadcrumb) a:hover { background: var(--panel); color: var(--ink); }
/* The current tab is a depressed key with a lit strip under it: inset as well
   as coloured, and it carries aria-current. */
.rail nav:not(.breadcrumb) a[aria-current] {
  background: var(--well);
  color: var(--ink);
  box-shadow: inset 0 -3px 0 var(--signal), inset 1px 1px 0 var(--bevel-lo);
}

.connection {
  display: flex;
  align-items: center;
  gap: var(--s2);
  flex: none;
  font: var(--f-micro) / 1 var(--font-mono);
  letter-spacing: var(--track-micro);
  text-transform: uppercase;
  color: var(--ink-2);
  white-space: nowrap;
  /* One slot for every state, from Live to Reconnecting…, so the tabs beside it
     never move when the status changes. */
  width: 120px;
  justify-content: flex-end;
}
/* app.js only toggles a class; the lamp colour follows it. */
.connection .lamp { background: var(--ok-lamp); box-shadow: inset 0 0 0 1px var(--rule), 0 0 6px -1px var(--ok-lamp); }
.connection.is-paused .lamp { background: var(--warn-lamp); box-shadow: inset 0 0 0 1px var(--rule), 0 0 6px -1px var(--warn-lamp); animation: none; }
.connection.is-offline .lamp { background: var(--crit-lamp); box-shadow: inset 0 0 0 1px var(--rule), 0 0 6px -1px var(--crit-lamp); }

/* ---- identity strip ---------------------------------------------------
   Who, then the run's totals. Every stat is label over reading with the same
   two line boxes, so all six readings share one baseline whatever they hold. */
.identity {
  display: flex;
  align-items: flex-end;
  flex-wrap: wrap;
  gap: var(--s4) var(--s6);
  padding: var(--s6) 0 var(--s5);
}
.identity .who { min-width: 0; }
.identity h1 {
  margin-top: var(--s2);
  /* A touch of leading, so a title that wraps on a phone does not touch itself. */
  font: var(--f-title) / 1.1 var(--font-panel);
  letter-spacing: 0.04em;
  text-transform: uppercase;
  overflow-wrap: anywhere;
}
.identity .stats {
  display: flex;
  margin-left: auto;
}
.stat {
  display: flex;
  flex-direction: column;
  gap: var(--s2);
  padding: 0 var(--s5);
  border-left: var(--hair) solid var(--rule-faint);
  min-width: 0;
}
.stat:first-child { padding-left: 0; border-left: 0; }
.stat:last-child { padding-right: 0; }
.stat .micro { white-space: nowrap; }
.stat .micro small { font-size: inherit; color: var(--ink-3); }

/* Eight gym badges as eight tall lamps, exactly the height of a reading. */
.badge-bank { display: flex; gap: var(--s1); height: 24px; }
.badge-bank .lamp { width: 10px; height: 24px; }

/* ---- footer plate ------------------------------------------------------- */
.plate-foot {
  margin-top: auto;
  background: var(--panel-2);
  border-top: var(--hair) solid var(--rule);
  box-shadow: inset 0 1px 0 var(--bevel-hi);
}
.plate-foot .shell {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--s3) var(--s5);
  min-height: 56px;
  padding-block: var(--s3);
}
.plate-foot p { color: var(--ink-2); }
.plate-foot .links { display: flex; flex-wrap: wrap; gap: var(--s2) var(--s5); margin-left: auto; }
.plate-foot a,
.plate-foot .restart-link {
  font: var(--f-micro) / 1.4 var(--font-mono);
  letter-spacing: var(--track-micro);
  text-transform: uppercase;
}
.restart-link {
  padding: 0;
  background: none;
  border: 0;
  color: var(--crit);
  text-decoration: underline;
  text-underline-offset: 3px;
  cursor: pointer;
}

/* Theme control: three states, so "auto" is reachable. It is set once and
   forgotten, so it lives on the footer plate rather than on the rail. */
.theme {
  display: flex;
  background: var(--well);
  border: var(--hair) solid var(--rule);
  box-shadow: var(--well-inset);
}
.theme button {
  min-height: 32px;
  padding: 0 var(--s3);
  background: none;
  border: 0;
  border-left: var(--hair) solid var(--rule-faint);
  font: var(--f-micro) / 1 var(--font-mono);
  letter-spacing: var(--track-micro);
  text-transform: uppercase;
  color: var(--ink-2);
  cursor: pointer;
}
.theme button:first-child { border-left: 0; }
.theme button[aria-pressed="true"] { background: var(--panel); color: var(--signal); box-shadow: var(--key-inset); }


/* ==========================================================================
   4  LIVE PAGE
   ========================================================================== */

/* The screen is the thing you watch and the party is the thing you glance at,
   so the stage gets the wider column. The two columns start and end together:
   the ratio is set so six bays and the stage are the same height on a desktop,
   and whichever is shorter stretches the rest of the way. A short party spreads
   its bays; a short stage grows the dark well around the picture. */
.live-grid {
  display: grid;
  grid-template-columns: minmax(0, var(--stage-w)) minmax(0, 1fr);
  gap: var(--s5);
  align-items: stretch;
  /* Six 132px bays and their header make the party about 895px tall. The
     stage spends 268px on its header, bezel, deck and gamepad strip, which
     leaves 627px of 10:9 screen, 697px wide, plus 26px of bezel. */
  --stage-w: 723px;
}
.live-grid > * { min-width: 0; }
.watch-stage, .watch-companions, .party-section { display: flex; flex-direction: column; }
.watch-stage > .game-card, .watch-companions > .party-section { flex: 1; }

/* ---- the stage -------------------------------------------------------- */
.game-card { display: flex; flex-direction: column; min-width: 0; }
.game-card > .bezel { flex: 1; display: flex; flex-direction: column; }

.bezel {
  background: var(--bezel);
  border: var(--hair) solid var(--rule);
  padding: var(--s3);
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.06);
}
.bezel-head {
  display: flex;
  align-items: center;
  gap: var(--s3);
  min-height: 32px;
  padding: 0 var(--s1) var(--s3);
}
.bezel .on-air {
  display: flex;
  align-items: center;
  gap: var(--s2);
  flex: none;
  font: var(--f-micro) / 1 var(--font-mono);
  letter-spacing: var(--track-micro);
  color: var(--bezel-ink);
}
.bezel-head h2 {
  flex: 1;
  min-width: 0;
  font: var(--f-read) / 1 var(--font-panel);
  letter-spacing: 0.03em;
  text-transform: uppercase;
  color: #D6DCC6;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.bezel-head .coords {
  flex: none;
  font: var(--f-label) / 1 var(--font-mono);
  letter-spacing: 0.06em;
  color: var(--bezel-ink);
  font-variant-numeric: tabular-nums;
}
.bezel-key {
  display: grid;
  place-items: center;
  width: 32px;
  height: 32px;
  flex: none;
  padding: 0;
  background: none;
  border: var(--hair) solid #2B3222;
  color: var(--bezel-ink);
  font: var(--f-legend) / 1 var(--font-panel);
  cursor: pointer;
}
.bezel-key:hover { color: #D6DCC6; border-color: var(--bezel-ink); }

/* 160x144 is 10:9. The well holds that ratio at every width, so the
   connecting state and the live picture occupy the same rectangle. */
.screen-well {
  position: relative;
  display: grid;
  place-items: center;
  aspect-ratio: 10 / 9;
  flex: none;
  width: 100%;
  background: #0A0C07;
  box-shadow: inset 0 0 0 2px #2B3222;
  overflow: hidden;
}
.screen-well::before {
  content: "";
  position: absolute;
  inset: 0;
  background-image:
    repeating-linear-gradient(to right, rgba(142, 154, 107, 0.08) 0 1px, transparent 1px 4px),
    repeating-linear-gradient(to bottom, rgba(142, 154, 107, 0.08) 0 1px, transparent 1px 4px);
  pointer-events: none;
}
.screen-well img {
  position: relative;
  width: 100%;
  height: 100%;
  object-fit: contain;
  image-rendering: pixelated;
}
.screen-well img:not([src]) { visibility: hidden; }
html:has(.screen-well:fullscreen) { scrollbar-gutter: auto }
.screen-well:fullscreen { aspect-ratio: auto }
.screen-well:fullscreen { display: flex }
.screen-well:fullscreen { align-items: center }
.screen-well:fullscreen { justify-content: center }
.screen-well:fullscreen { width: 100vw }
.screen-well:fullscreen { height: 100vh }
.screen-well:fullscreen { max-width: none }
.screen-well:fullscreen { max-height: none }
.screen-well:fullscreen { background: #000 }
.screen-well:fullscreen::before { display: none }
.screen-well:fullscreen img { min-width: 0 }
.screen-well:fullscreen img { min-height: 0 }
/* screen.js appends this to the well while the stream connects. */
.screen-status {
  position: absolute;
  inset: auto 0 50%;
  transform: translateY(50%);
  text-align: center;
  font: var(--f-legend) / 1.6 var(--font-panel);
  letter-spacing: var(--track-legend);
  text-transform: uppercase;
  color: var(--bezel-ink);
}

/* If the stage is ever the shorter column, the surplus is dark bezel under
   the caption; the picture itself always keeps its 10:9. */
.bezel-foot {
  flex: 1;
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: var(--s1) var(--s4);
  padding: var(--s3) var(--s1) 0;
  font: var(--f-micro) / 1.4 var(--font-mono);
  letter-spacing: var(--track-micro);
  text-transform: uppercase;
  color: var(--bezel-ink);
}
.bezel-foot .activity { color: #B9C29F; }

/* In manual play the housing carries a signal ring, so there is no doubt at
   a glance about who is holding the controls. */
.game-card.is-manual .bezel { outline: 2px solid var(--signal-lamp); outline-offset: -1px; }
.game-card.is-manual .on-air .lamp { background: var(--signal-lamp); }

/* ---- the deck: operator controls below the housing -------------------- */
.deck {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--s2);
  padding: var(--s3);
  background: var(--panel);
  border: var(--hair) solid var(--rule);
  border-top: 0;
  box-shadow: var(--key-inset);
}
.deck .mode {
  display: flex;
  align-items: center;
  gap: var(--s2);
  margin-left: auto;
  padding-right: var(--s1);
  font: var(--f-micro) / 1.4 var(--font-mono);
  letter-spacing: var(--track-micro);
  text-transform: uppercase;
  color: var(--ink-2);
}
.game-card.is-manual .deck .mode { color: var(--signal); }
.game-card.is-manual .deck .mode .lamp { background: var(--signal-lamp); }

.gamepad {
  background: var(--panel-2);
  border: var(--hair) solid var(--rule);
  border-top: 0;
}
.gamepad > summary {
  display: flex;
  align-items: center;
  gap: var(--s2);
  min-height: 40px;
  padding: 0 var(--s3);
  font: var(--f-micro) / 1.4 var(--font-mono);
  letter-spacing: var(--track-micro);
  text-transform: uppercase;
  color: var(--ink-2);
  cursor: pointer;
  list-style: none;
}
.gamepad > summary::-webkit-details-marker { display: none; }
.gamepad > summary::before { content: "\25B6"; font: 8px / 1 var(--font-panel); color: var(--ink-3); }
.gamepad[open] > summary::before { content: "\25BC"; }
.gamepad > summary:hover { color: var(--ink); }
.pad-body {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--s5);
  padding: var(--s4);
  border-top: var(--hair) solid var(--rule-faint);
}
.dpad { display: grid; grid-template-columns: repeat(3, 40px); grid-auto-rows: 40px; gap: 2px; }
.abxy { display: grid; grid-template-columns: repeat(2, auto); gap: var(--s2); }
.dpad button,
.abxy button {
  display: grid;
  place-items: center;
  min-width: 40px;
  height: 40px;
  padding: 0 var(--s3);
  background: var(--panel);
  border: var(--hair) solid var(--rule);
  box-shadow: var(--key-inset);
  font: var(--f-legend) / 1 var(--font-panel);
  text-transform: uppercase;
  color: var(--ink);
  cursor: pointer;
}
.dpad button:active,
.abxy button:active { background: var(--well); box-shadow: var(--well-inset); }
.pad-help { flex: 1 1 200px; display: grid; gap: var(--s2); }
.pad-help p { font-size: 14px; line-height: 1.45; color: var(--ink-2); }

/* ---- the plan ----------------------------------------------------------
   The machine's statement of intent, directly under the picture it
   describes. Headline across the top; state and next step side by side. */
.plan {
  display: grid;
  grid-template-columns: minmax(0, 2fr) minmax(0, 1fr) minmax(0, 1fr);
  margin-top: var(--s5);
  background: var(--panel);
  border: var(--hair) solid var(--rule);
  border-left: 3px solid var(--signal);
  box-shadow: var(--key-inset);
}
.plan > div { display: grid; align-content: start; gap: var(--s2); padding: var(--s4); min-width: 0; }
.plan .plan-state, .plan .plan-next { border-left: var(--hair) solid var(--rule-faint); }
.plan .headline {
  font: var(--f-read) / 1.2 var(--font-panel);
  letter-spacing: 0.03em;
  text-transform: uppercase;
}
.plan .body {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  overflow: hidden;
  max-width: 64ch;
  font-size: 14px;
  line-height: 1.5;
  color: var(--ink-2);
}
.plan .body:empty { display: none; }
.plan .value {
  display: flex;
  align-items: center;
  gap: var(--s2);
  font: var(--f-legend) / 1.25 var(--font-panel);
  letter-spacing: 0.05em;
  text-transform: uppercase;
}
.plan .note { font-size: 13px; line-height: 1.45; color: var(--ink-3); }

/* ---- party ------------------------------------------------------------ */
.column-head {
  position: sticky;
  top: var(--rail-h);
  z-index: 5;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s3);
  min-height: 57px;
  margin-bottom: var(--s2);
  border-bottom: var(--hair) solid var(--rule);
  background: var(--chassis);
  background-image: radial-gradient(var(--stipple) 0.5px, transparent 0.5px);
  background-size: 3px 3px;
}
/* The stage's header is the same height but does not stick: the screen is
   always in view on its own. */
.game-card > .column-head { position: static; }
.column-head h2 { font-size: var(--f-legend); }

.party { flex: 1; display: grid; grid-auto-rows: 1fr; gap: var(--s2); }

/* ---- a bay -------------------------------------------------------------
   Portrait, then a body of three rows: who, what, how they are doing.
   The whole bay opens the partner's details; the arrow only marks it. */
.mon {
  position: relative;
  container: bay / inline-size;
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  background: var(--panel);
  border: var(--hair) solid var(--rule);
  box-shadow: var(--key-inset);
  transition: background var(--t);
}
.mon:has(.mon-open):hover { background: color-mix(in srgb, var(--panel) 70%, var(--panel-2)); }
.mon-plate { padding: var(--s2); border-right: var(--hair) solid var(--rule-faint); }
.mon-body { display: grid; grid-template-columns: minmax(0, 1fr); grid-template-rows: auto auto 1fr; min-width: 0; }

.mon-head {
  display: flex;
  align-items: center;
  gap: var(--s2);
  min-height: 40px;
  padding: 0 var(--s2) 0 var(--s3);
  border-bottom: var(--hair) solid var(--rule-faint);
}
.mon-head .slotno { font: var(--f-micro) / 1 var(--font-mono); letter-spacing: var(--track-micro); color: var(--ink-3); }
.mon-head .name {
  min-width: 0;
  font: var(--f-legend) / 1 var(--font-panel);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.mon-head .spacer { flex: 1; }
.mon-head .lv {
  font: var(--f-read) / 1 var(--font-panel);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.mon-head .lv em {
  margin-right: 2px;
  font: var(--f-micro) / 1 var(--font-mono);
  font-style: normal;
  letter-spacing: var(--track-micro);
  color: var(--ink-3);
}
.dv { display: flex; gap: 3px; }
.dv .lamp { width: 7px; height: 7px; }

/* The key stretches over the whole bay, so the hit target is the bay. */
.mon-open {
  display: grid;
  place-items: center;
  width: 28px;
  height: 28px;
  flex: none;
  padding: 0;
  background: none;
  border: var(--hair) solid transparent;
  font: var(--f-legend) / 1 var(--font-panel);
  color: var(--ink-3);
  cursor: pointer;
}
.mon-open::after { content: ""; position: absolute; inset: 0; }
.mon-open:focus-visible { box-shadow: none; outline: 0; }
.mon-open:focus-visible::after { outline: 2px solid var(--focus); outline-offset: 2px; }
.mon:hover .mon-open { color: var(--signal); border-color: var(--rule-faint); background: var(--panel-2); }

.mon-id {
  display: flex;
  align-items: center;
  gap: var(--s2);
  min-width: 0;
  flex-wrap: wrap;
  min-height: 32px;
  padding: var(--s1) var(--s3);
  border-bottom: var(--hair) solid var(--rule-faint);
}
.mon-id .micro { color: var(--ink-2); white-space: nowrap; }

.mon-lower { display: grid; grid-template-columns: minmax(0, 1fr); }
/* One grid for both rows, so the HP and XP meters start and end together
   whatever width their readings take. */
.mon-meters {
  display: grid;
  grid-template-columns: auto minmax(32px, 1fr) auto;
  align-content: center;
  align-items: center;
  gap: var(--s2);
  padding: var(--s2) var(--s3);
}
.meter-row { display: contents; }
.meter-row .micro { color: var(--ink-2); }
.meter-row .value {
  font: var(--f-legend) / 1 var(--font-panel);
  font-variant-numeric: tabular-nums;
  text-align: right;
  white-space: nowrap;
}

/* Moves are hidden until the bay is wide enough to hold them whole; the
   container queries in section 6 decide how they sit. */
.mon-moves { display: none; align-content: center; }
.move {
  display: flex;
  align-items: baseline;
  gap: var(--s2);
  min-width: 0;
  padding: 3px var(--s3);
  font: var(--f-micro) / 1.4 var(--font-mono);
  letter-spacing: 0.03em;
  text-transform: uppercase;
  color: var(--ink-2);
}
.move .nm { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.move .pp { margin-left: auto; color: var(--ink-3); font-variant-numeric: tabular-nums; }
.move .pp.empty { color: var(--crit); }
.no-moves { padding: 3px var(--s3); font: var(--f-micro) / 1.4 var(--font-mono); color: var(--ink-3); }

.mon--fainted { background: var(--panel-2); }
.mon--fainted .name,
.mon--fainted .lv { color: var(--ink-3); }
.mon--fainted .plate img { filter: grayscale(1) opacity(0.6); }

/* An open slot is a recess with a dashed tray, not a card. */
.mon--empty { background: var(--panel-2); box-shadow: var(--well-inset); border-color: var(--rule-faint); }
.mon--empty .plate { background: transparent; border-style: dashed; box-shadow: none; }
.mon--empty .plate-num { color: var(--ink-3); }
.mon--empty .mon-body { display: flex; flex-direction: column; justify-content: center; gap: var(--s1); padding: 0 var(--s4); }
.mon--empty .mon-body p:last-child { font-size: 14px; color: var(--ink-2); }


/* ==========================================================================
   5  OVERLAYS
   ========================================================================== */

.partner-detail {
  width: min(560px, calc(100% - 2 * var(--s4)));
  max-height: calc(100vh - 2 * var(--s6));
  padding: 0;
  background: var(--panel);
  color: var(--ink);
  border: var(--hair) solid var(--rule);
  box-shadow: var(--key-inset), 6px 6px 0 var(--bevel-lo);
}
.partner-detail::backdrop { background: rgba(10, 12, 10, 0.55); }
.partner-detail form { position: absolute; top: var(--s3); right: var(--s3); }
.partner-close { min-height: 32px; padding: 0 var(--s3); }
.partner-detail-head {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: var(--s1) var(--s4);
  align-items: center;
  padding: var(--s4);
  padding-right: 120px;
  border-bottom: var(--hair) solid var(--rule);
  background: var(--panel-2);
}
.partner-detail-head .plate { grid-row: span 3; }
.partner-detail-head h2 { font: var(--f-read) / 1.1 var(--font-panel); letter-spacing: 0.04em; text-transform: uppercase; }
.partner-detail-head p:last-child { font-size: 14px; color: var(--ink-2); }
.partner-detail section { padding: var(--s4); border-bottom: var(--hair) solid var(--rule-faint); }
.partner-detail section:last-child { border-bottom: 0; }
.partner-detail h3 { margin-bottom: var(--s3); }
.partner-detail .moves { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); border: var(--hair) solid var(--rule-faint); }
.partner-detail .moves .move { padding: var(--s2) var(--s3); font-size: var(--f-label); border-bottom: var(--hair) solid var(--rule-faint); }
.partner-detail .moves .move:nth-child(odd) { border-right: var(--hair) solid var(--rule-faint); }
.partner-detail .moves .move:nth-last-child(-n + 2):nth-child(odd),
.partner-detail .moves .move:last-child { border-bottom: 0; }
.battle-stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(84px, 1fr)); gap: var(--s2); }
.battle-stats > div { display: grid; gap: var(--s2); padding: var(--s3); background: var(--panel-2); border: var(--hair) solid var(--rule-faint); }
.battle-stats dt { font: var(--f-micro) / 1 var(--font-mono); letter-spacing: var(--track-micro); text-transform: uppercase; color: var(--ink-3); }
.battle-stats dd { font: var(--f-read) / 1 var(--font-panel); font-variant-numeric: tabular-nums; }
.total-xp { margin-top: var(--s3); font-size: 13px; color: var(--ink-3); }

.toast {
  position: fixed;
  left: 50%;
  bottom: var(--s5);
  z-index: 50;
  transform: translateX(-50%);
  max-width: calc(100% - 2 * var(--s4));
  padding: var(--s3) var(--s4);
  background: var(--panel);
  border: var(--hair) solid var(--rule);
  border-left: 3px solid var(--ok-lamp);
  box-shadow: var(--key-inset), 4px 4px 0 var(--bevel-lo);
  font-size: 14px;
}
.toast.error { border-left-color: var(--crit-lamp); color: var(--crit); }


/* ==========================================================================
   6  RESPONSIVE
     1100px  the rail's tabs take a row of their own.
      860px  the stage and the party stack. The party goes two bays abreast
             so it does not become a column of very wide, empty strips.
      640px  the phone. The rail stops being sticky and hands that job to the
             tab strip alone; everything goes single file.
   A bay also answers to its own width, so it is right in either layout.
   ========================================================================== */

/* Moves join the bay only where they fit as a 2x2 beside the meters, so a
   bay is the same height at every width and six of them stack predictably. */
@container bay (min-width: 560px) {
  .mon-lower { grid-template-columns: minmax(0, 0.9fr) minmax(0, 1.1fr); }
  .mon-meters { border-right: var(--hair) solid var(--rule-faint); }
  .mon-moves { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
  .move { padding-block: 3px; }
  .move:nth-child(odd) { border-right: var(--hair) solid var(--rule-faint); }
}
@container bay (max-width: 500px) {
  .mon-head .slotno, .mon-head .dv, .mon-id .dexno { display: none; }
  .mon-head .lv { font-size: var(--f-legend); }
}
/* A phone-width bay fits the longest Gen 1 names (Blastoise, Charmeleon)
   once the name's tracking comes in and the level loses its prefix gap. */
@container bay (max-width: 400px) {
  .mon-head { gap: var(--s1); }
  .mon-head .name { letter-spacing: 0.01em; }
}
/* On the narrowest phones the name gets its own line rather than two letters. */
@container bay (max-width: 300px) {
  .mon-head { flex-wrap: wrap; row-gap: var(--s1); }
  .mon-head .name { flex: 1 0 100%; }
}

@media (max-width: 1100px) {
  .shell { padding-inline: var(--s5); }

  .rail-inner { flex-wrap: wrap; column-gap: var(--s4); }
  .rail nav:not(.breadcrumb) {
    order: 3;
    width: 100%;
    margin-left: 0;
    border-top: var(--hair) solid var(--rule-faint);
  }
  .rail nav:not(.breadcrumb) a { flex: 1 0 auto; justify-content: center; min-height: 44px; }
  .rail nav:not(.breadcrumb) a:first-child { border-left: 0; }
  .rail nav:not(.breadcrumb) a:last-child { border-right: 0; }
  .connection { margin-left: auto; }

  .brandplate { min-height: 56px; }
  .identity .stats { margin-left: 0; width: 100%; }
}

/* Side by side needs the fixed stage plus a party column wide enough for a
   bay to stay one line per row, so below 1280px the stage and the party
   stack, and the party goes two abreast while it can. Every row still spans
   the full shell, so edges line up down the page. */
@media (max-width: 1279px) {
  .live-grid { grid-template-columns: minmax(0, 1fr); gap: var(--s6); }
  .game-card .bezel { align-items: center; }
  .bezel-head, .bezel-foot { width: 100%; }
  .screen-well { max-width: 640px; }
  .party { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .plan { margin-top: var(--s6); }
}
@media (max-width: 860px) {
  .screen-well { max-width: 560px; }
  /* Narrow, the plan's headline takes the full row above state and next. */
  .plan { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
  .plan .plan-now { grid-column: 1 / -1; border-bottom: var(--hair) solid var(--rule-faint); }
  .plan .plan-state { border-left: 0; }
}

@media (max-width: 760px) {
  .party { grid-template-columns: minmax(0, 1fr); }
}

@media (max-width: 640px) {
  .shell { padding-inline: var(--s4); }

  /* panel.js pins the rail at a negative offset here, so the brand and the
     adventure slot scroll away and only the tab strip stays on screen. */
  .rail-inner { min-height: 0; row-gap: 0; }
  .brandplate { min-height: 52px; }
  /* One line on a phone: the way back, then the switcher filling the rest as
     a full-size touch target. */
  .breadcrumb { order: 2; flex-direction: row; align-items: center; gap: var(--s3); width: 100%; padding: 0 0 var(--s2); border-left: 0; }
  .breadcrumb > a { align-self: center; padding: var(--s3) 0; }
  .adventure-switch { flex: 1; }
  .adventure-switch select { width: 100%; height: 40px; margin-left: 0; background: var(--well); border-color: var(--rule); box-shadow: var(--well-inset); }
  .rail nav:not(.breadcrumb) {
    margin-inline: calc(-1 * var(--s4));
    width: calc(100% + 2 * var(--s4));
  }
  /* Five tabs in the display face need 380px; in the mono voice they fit a
     320px phone with room to spare, still as a full-height key each. */
  .rail nav:not(.breadcrumb) a {
    flex: 1 1 0;
    padding: 0 var(--s1);
    font: var(--f-label) / 1 var(--font-mono);
    letter-spacing: 0.06em;
  }

  .identity { padding: var(--s5) 0 var(--s4); gap: var(--s4); }
  .identity h1 { font-size: var(--f-read); }
  /* The totals become a milled bank of cells rather than a wrapping row. */
  .identity .stats {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    background: var(--panel);
    border: var(--hair) solid var(--rule);
    box-shadow: var(--key-inset);
  }
  .stat { padding: var(--s3); border-left: var(--hair) solid var(--rule-faint); border-top: var(--hair) solid var(--rule-faint); }
  .stat:first-child { grid-column: 1 / -1; border-top: 0; padding: var(--s3); }
  .stat:nth-child(even) { border-left: 0; }
  .stat:last-child { padding-right: var(--s3); }
  .stat .readout { font-size: var(--f-legend); }
  .badge-bank { height: 16px; }
  .badge-bank .lamp { flex: 1; max-width: 24px; height: 16px; }

  .live-grid { gap: var(--s5); }
  .bezel { padding: var(--s2); }
  .bezel-head { padding-bottom: var(--s2); }
  .bezel-head h2 { font-size: var(--f-legend); }
  .bezel-foot { padding-top: var(--s2); }
  .deck .key { flex: 1 1 auto; min-height: 44px; }
  .deck .key--primary { flex-basis: 100%; }
  .deck .mode { width: 100%; justify-content: center; margin-left: 0; }

  .plan { grid-template-columns: minmax(0, 1fr); }
  .plan .plan-next { border-left: 0; border-top: var(--hair) solid var(--rule-faint); }
  .plan .headline { font-size: var(--f-legend); line-height: 1.35; }

  .column-head { min-height: 48px; }

  .partner-detail-head { grid-template-columns: minmax(0, 1fr); padding-right: var(--s4); padding-top: var(--s7); }
  .partner-detail-head .plate { grid-row: auto; }
  .partner-detail .moves { grid-template-columns: minmax(0, 1fr); }
  .partner-detail .moves .move:nth-child(odd) { border-right: 0; }
  .partner-detail .moves .move:nth-last-child(-n + 2):nth-child(odd) { border-bottom: var(--hair) solid var(--rule-faint); }

  .plate-foot .links { margin-left: 0; }
}


/* ==========================================================================
   7  MOTION AND CONTRAST PREFERENCES
   ========================================================================== */

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0s !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0s !important;
    scroll-behavior: auto !important;
  }
  .lamp--blink { opacity: 1; }
}

@media (prefers-contrast: more) {
  :root,
  [data-theme] {
    --rule: var(--ink);
    --rule-faint: var(--ink-2);
    --ink-3: var(--ink-2);
  }
}

@media (forced-colors: active) {
  .panel, .key, .mon, .plan, .deck, .gamepad {
    border: 1px solid CanvasText;
    box-shadow: none;
  }
  .lamp { border: 1px solid CanvasText; background: Canvas; }
  .lamp[data-on]:not([data-on=""]) { background: Highlight; }
  .meter i.on { background: Highlight; }
  .meter i { background: Canvas; }
  .rail nav:not(.breadcrumb) a[aria-current] { box-shadow: none; outline: 2px solid Highlight; outline-offset: -2px; }
}

/* A 320px phone: tighten the tab type so "Journal" is not cut off. */
@media (max-width: 360px) {
  .rail nav:not(.breadcrumb) a { padding: 0 2px; letter-spacing: 0.01em; }
}
