/* ══ RAIL SHOOTER ══════════════════════════════════════════════
   The cabinet: pixel type, phosphor glow, scanlines, and buttons that
   snap rather than ease. Nothing here transitions smoothly on purpose —
   an arcade menu is instant, and easing is what makes it feel like a web
   page instead of a machine.
   ══════════════════════════════════════════════════════════════ */
* { margin: 0; padding: 0; box-sizing: border-box; }

:root {
  --amber: #ffd447;
  --cyan: #00e5ff;
  --magenta: #ff2fb9;
  --danger: #ff3b5c;
  --dim: #45455c;
  --arcade: 'Press Start 2P', 'Courier New', ui-monospace, monospace;
}

html, body {
  background: #05050a;
  overflow: hidden;
  height: 100%;
  /* The game owns every gesture: no pull-to-refresh, no pinch zoom, no
     double-tap zoom stealing a dodge. */
  touch-action: none;
  overscroll-behavior: none;
  font-family: var(--arcade);
  color: #fff;
  -webkit-font-smoothing: none;
}

canvas {
  display: block;
  width: 100vw;
  height: 100vh;
  touch-action: none;
}

.hidden { display: none !important; }

/* ── Cabinet overlay ──────────────────────────────────────────
   pointer-events:none on the shell so a tap on bare background still
   reaches the canvas; only the panel itself catches clicks. */
.ui {
  position: fixed;
  inset: 0;
  z-index: 3;
  display: grid;
  place-items: center;
  pointer-events: none;
  padding: 20px;
}

.panel {
  pointer-events: auto;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 22px;
  text-align: center;
  padding: clamp(20px, 4vw, 40px);
  max-width: 560px;
  width: 100%;
  background: rgba(2, 2, 8, 0.82);
  border: 4px solid var(--dim);
  /* The double box is pure cabinet: an outer rule, a gap, then the glow. */
  box-shadow: 0 0 0 4px #05050a, 0 0 0 6px #1b1b2a, 0 0 40px rgba(0, 229, 255, 0.12);
}

/* ── Title ── */
.logo {
  font-size: clamp(22px, 6vw, 42px);
  line-height: 1.5;
  letter-spacing: 0.06em;
  /* Offset colour fringes: a cheap, very period-correct chromatic split. */
  text-shadow: 3px 0 var(--magenta), -3px 0 var(--cyan), 0 0 24px rgba(255, 255, 255, 0.45);
}
.logo span { color: var(--amber); text-shadow: 3px 0 var(--magenta), -3px 0 var(--cyan); }

.tagline { font-size: clamp(7px, 1.6vw, 10px); color: var(--cyan); line-height: 2; letter-spacing: 0.1em; }
/* The title's three doors, stacked: solo first and brightest, the two party
   buttons as ghosts under it so a solo player's thumb lands where it always has. */
.title-actions { display: flex; flex-direction: column; gap: 12px; align-items: stretch; width: min(100%, 320px); }
.title-actions .arcade-btn:disabled { opacity: 0.35; cursor: not-allowed; }
.title-actions .arcade-btn:disabled:hover { background: transparent; color: var(--dim); box-shadow: none; }
.credits { font-size: clamp(7px, 1.6vw, 9px); color: var(--dim); letter-spacing: 0.12em; }
.small { font-size: clamp(7px, 1.7vw, 10px); letter-spacing: 0.1em; }
.hint { color: var(--dim); line-height: 1.8; }

.heading {
  font-size: clamp(13px, 3.2vw, 20px);
  letter-spacing: 0.12em;
  color: var(--cyan);
  text-shadow: 0 0 18px rgba(0, 229, 255, 0.6);
}
.heading.danger { color: var(--danger); text-shadow: 0 0 18px rgba(255, 59, 92, 0.6); }

/* Two frames a second, hard cut — no fading. */
.blink { animation: blink 1s steps(1, end) infinite; color: var(--amber); }
@keyframes blink { 0%, 50% { opacity: 1; } 50.01%, 100% { opacity: 0; } }

/* ── Buttons ── */
.arcade-btn {
  font-family: var(--arcade);
  font-size: clamp(10px, 2.4vw, 15px);
  letter-spacing: 0.14em;
  color: var(--amber);
  background: transparent;
  border: 4px solid var(--amber);
  padding: 16px 22px;
  cursor: pointer;
  text-transform: uppercase;
}
.arcade-btn:hover,
.arcade-btn:focus-visible {
  background: var(--amber);
  color: #05050a;
  outline: none;
  box-shadow: 0 0 26px rgba(255, 212, 71, 0.65);
}
.arcade-btn:active { transform: translateY(2px); }
.arcade-btn.ghost { color: var(--dim); border-color: var(--dim); }
.arcade-btn.ghost:hover,
.arcade-btn.ghost:focus-visible { background: var(--dim); color: #05050a; box-shadow: none; }

/* ── Pilot roster ── */
.roster { display: flex; gap: 14px; flex-wrap: wrap; justify-content: center; }

.pilot {
  font-family: var(--arcade);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12px;
  width: 132px;
  padding: 16px 10px;
  background: rgba(0, 0, 0, 0.6);
  border: 3px solid #23233a;
  color: var(--cyan);
  cursor: pointer;
}
.pilot:hover:not(.locked) { border-color: var(--cyan); }
.pilot.selected {
  border-color: var(--cyan);
  background: rgba(0, 229, 255, 0.1);
  box-shadow: 0 0 22px rgba(0, 229, 255, 0.4);
}
.pilot-name { font-size: 11px; letter-spacing: 0.1em; }
.pilot-stats { font-size: 7px; line-height: 2; color: var(--dim); letter-spacing: 0.08em; }

/* The Erebus itself — the same arrowhead the renderer draws, as a clip path,
   so the card and the ship in flight can never drift apart by accident. */
.ship {
  width: 46px;
  height: 54px;
  background: #3aa0ff;
  clip-path: polygon(50% 0%, 100% 100%, 50% 81%, 0% 100%);
  filter: drop-shadow(0 0 8px #3aa0ff);
}

.pilot.locked { border-style: dashed; border-color: #1c1c2c; color: var(--dim); cursor: not-allowed; }
.lock { font-size: 30px; width: 46px; height: 54px; display: grid; place-items: center; color: #23233a; }

/* The ship's one line, or — on a locked card — what would unlock it. Dimmer
   than the stats so the numbers stay the thing you compare cards by. */
.pilot-tag {
  font-size: 6px;
  line-height: 1.8;
  letter-spacing: 0.08em;
  color: #6a6a84;
  min-height: 2.2em;
}
.pilot.locked .pilot-tag { color: var(--amber); opacity: 0.75; }

/* ── Unlock toasts ──
   Across the top, over everything but the scanlines. Each line slides in,
   holds, and fades; the shell removes it. */
.toasts {
  position: fixed;
  top: 18px;
  left: 0;
  right: 0;
  z-index: 3;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  pointer-events: none;
}
.toast {
  font-family: var(--arcade);
  font-size: clamp(8px, 1.8vw, 11px);
  letter-spacing: 0.12em;
  color: var(--amber);
  background: rgba(5, 5, 10, 0.92);
  border: 3px solid var(--amber);
  padding: 10px 16px;
  box-shadow: 0 0 22px rgba(255, 212, 71, 0.45);
  animation: toast-in 0.2s steps(3, end);
}
.toast.leaving { opacity: 0; transition: opacity 0.4s steps(4, end); }
@keyframes toast-in { from { transform: translateY(-12px); opacity: 0; } to { transform: none; opacity: 1; } }

/* ── Party mode switches ──
   main.js sets body.party for the life of a party. Buttons that would start
   a solo run under a live party go away; party-only lines appear. */
.party-only { display: none; }
body.party .party-only { display: block; }
body.party .solo-only { display: none; }
/* The lobby's two halves by intent: a host does not need the code box, a
   joiner does not need HOST. Both show when the panel is opened by hand. */
#ui-party.intent-host .code-row,
#ui-party.intent-join [data-action="party-host"] { display: none; }
/* Watch mode on the map (a guest while the host is choosing): the planets
   are there to be looked at, not pressed, and the title says so. */
.map-panel.watching .map-node { cursor: default; pointer-events: none; }
.map-panel.watching .map-node.open .planet { animation: none; }
/* SYNCING… — centred over the corridor, under every panel. */
.sync {
  position: fixed; inset: 0; z-index: 2;
  display: grid; place-items: center;
  pointer-events: none;
  font-size: clamp(11px, 2.6vw, 16px);
  letter-spacing: 0.16em;
  color: var(--cyan);
  text-shadow: 0 0 18px rgba(0, 229, 255, 0.7);
}
.sync span { background: rgba(2, 2, 8, 0.8); padding: 14px 22px; border: 3px solid var(--dim); }

/* ── Score table ── */
.score-table {
  display: grid;
  grid-template-columns: auto auto;
  gap: 10px 26px;
  font-size: clamp(9px, 2vw, 12px);
  color: var(--dim);
  letter-spacing: 0.1em;
}
.score-table b { color: #fff; text-align: right; }

/* ── CRT scanlines ───────────────────────────────────────────
   Over everything, including the game — it is the glass of the cabinet,
   not a menu decoration. Kept faint: heavy lines look great in a
   screenshot and are tiring to actually play behind. */
.scanlines {
  position: fixed;
  inset: 0;
  z-index: 4;
  pointer-events: none;
  background: repeating-linear-gradient(
    to bottom,
    rgba(0, 0, 0, 0.22) 0px,
    rgba(0, 0, 0, 0.22) 1px,
    transparent 1px,
    transparent 3px
  );
}

/* ── Hub link ── */
.back-link {
  position: fixed;
  left: 12px;
  bottom: 10px;
  z-index: 5;
  color: #55556a;
  font-family: var(--arcade);
  font-size: 8px;
  letter-spacing: 0.1em;
  text-decoration: none;
}
.back-link:hover { color: var(--amber); }
/* Out of the way while flying — a thumb on the stick sits right on top of it. */
body.playing .back-link { opacity: 0; pointer-events: none; }

@media (max-width: 420px) {
  .panel { gap: 16px; border-width: 3px; }
  .roster { gap: 8px; }
  .pilot { width: 104px; padding: 12px 6px; }
  .ship { width: 36px; height: 42px; }
}

/* ── Cutscene dialogue ────────────────────────────────────────
   Two boxes pinned to opposite halves of the screen, the way every console
   game of this era did it: the voice with authority talks down from the top,
   you answer from the bottom. Both share one frame style with the cabinet
   panels, so a briefing looks like part of the same machine.

   One deliberate exception to the no-easing rule at the top of this file: the
   boxes DO fade. A cutscene is not a menu — a menu should snap because you are
   operating it, and a scene should breathe because you are watching it. */
.dlg {
  position: fixed;
  inset: 0;
  z-index: 3;
  pointer-events: none;                 /* never eats a tap meant for the canvas */
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  padding: clamp(14px, 3vh, 34px) clamp(12px, 3vw, 28px);
}

.dlg-box {
  position: relative;
  display: flex;
  align-items: stretch;
  gap: 14px;
  width: min(720px, 100%);
  margin: 0 auto;
  padding: 14px;
  background: rgba(2, 2, 8, 0.92);
  border: 4px solid var(--dim);
  box-shadow: 0 0 0 4px #05050a, 0 0 0 6px #1b1b2a, 0 0 40px rgba(0, 229, 255, 0.12);
  opacity: 1;
  transition: opacity 260ms linear;
}
/* .hidden is display:none and wins, so fading out is a two-step: drop the
   opacity, then hide once the fade has run. main.js owns that timing. */
.dlg-box.fading { opacity: 0; }

/* The portrait square. Fixed size and square on purpose — character art can be
   dropped straight in at one known aspect without redesigning the box. */
.dlg-portrait {
  flex: 0 0 auto;
  width: clamp(56px, 13vw, 88px);
  height: clamp(56px, 13vw, 88px);
  border: 3px solid #23233a;
  /* main.js sets background-image per speaker from assets/portraits/<key>
     (see paintPortrait); the slot stays this dark box when there is no file.
     Pixel art, so no smoothing when the 96px file is stretched to the slot. */
  background: rgba(0, 0, 0, 0.55) center / cover no-repeat;
  image-rendering: pixelated;
  display: grid;
  place-items: center;
  overflow: hidden;
}
.dlg-portrait img { width: 100%; height: 100%; object-fit: cover; image-rendering: pixelated; }

.dlg-body { flex: 1 1 auto; display: flex; flex-direction: column; gap: 10px; min-width: 0; }

.dlg-name {
  font-size: clamp(9px, 2vw, 12px);
  letter-spacing: 0.14em;
  color: var(--cyan);
  text-shadow: 0 0 14px rgba(0, 229, 255, 0.55);
}
.dlg-text {
  font-size: clamp(8px, 1.8vw, 11px);
  line-height: 2;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  /* The typed text grows a character at a time; reserving the height stops the
     box from jittering taller as a long line fills in. */
  min-height: 4.2em;
}

/* The "press to continue" chevron, bottom right of its box. */
.dlg-more {
  position: absolute;
  right: 12px;
  bottom: 8px;
  font-size: 11px;
  color: var(--amber);
}
.dlg-box.typing .dlg-more { visibility: hidden; }   /* only once the line is done */

/* Per-speaker accents. The name plate and the frame take the colour, so who is
   talking is readable before a word of it has been read. */
.dlg-box.accent-cyan   { border-color: #1e4d57; }
.dlg-box.accent-blue   { border-color: #24405e; }
.dlg-box.accent-blue   .dlg-name { color: #3aa0ff; text-shadow: 0 0 14px rgba(58, 160, 255, 0.55); }
.dlg-box.accent-danger { border-color: #5a1b26; box-shadow: 0 0 0 4px #05050a, 0 0 0 6px #2a0d14, 0 0 44px rgba(255, 59, 92, 0.22); }
.dlg-box.accent-danger .dlg-name { color: var(--danger); text-shadow: 0 0 14px rgba(255, 59, 92, 0.6); }
.dlg-box.accent-danger .dlg-text { color: #ffd9e0; }

/* H.A.L. — one red eye, no art. Stepped rather than eased, so it pulses like a
   machine indicator instead of breathing like something alive. */
.dlg-portrait .dlg-face { display: none; }
.dlg-box.portrait-dot .dlg-portrait { border-color: #5a1b26; background: radial-gradient(circle, #1a0509 0%, #000 70%); }
.dlg-box.portrait-dot .dlg-face {
  display: block;
  width: 38%;
  aspect-ratio: 1;
  border-radius: 50%;
  background: var(--danger);
  box-shadow: 0 0 18px var(--danger), 0 0 46px rgba(255, 59, 92, 0.75);
  animation: hal-eye 1.2s steps(1, end) infinite;
}
@keyframes hal-eye { 0%, 70% { opacity: 1; } 70.01%, 100% { opacity: 0.45; } }

/* ── Mission card ─────────────────────────────────────────────
   The beat between the briefing and the first enemy: the ship sits at its mark
   and the title hangs over it. Same chromatic split as the logo. */
.mission-card {
  position: fixed;
  inset: 0;
  z-index: 3;
  display: grid;
  place-items: center;
  pointer-events: none;
}
.mission-card span {
  font-size: clamp(16px, 4.6vw, 32px);
  letter-spacing: 0.2em;
  color: var(--amber);
  text-shadow: 3px 0 var(--magenta), -3px 0 var(--cyan), 0 0 30px rgba(255, 212, 71, 0.5);
  opacity: 1;
  transition: opacity 260ms linear;
}
.mission-card.fading span { opacity: 0; }

@media (max-width: 420px) {
  .dlg-box { gap: 10px; padding: 10px; border-width: 3px; }
  .dlg-text { min-height: 5em; }
}

/* ─── The overworld ──────────────────────────────────────────
   A Star Fox style route map: five columns of planets and the lines between
   them. Where you have been is drawn solid, where you can go pulses, the rest
   sits dim. The panel is wider than the others because a map needs the room,
   and it darkens while a briefing plays over it so the map reads as the
   backdrop to the conversation rather than a thing still asking for a click. */
.map-panel { max-width: 900px; width: min(96vw, 900px); gap: 14px; }
.map-panel.briefing { pointer-events: none; }
.map-panel.briefing .map { opacity: 0.35; }

.map {
  position: relative;
  width: 100%;
  aspect-ratio: 5 / 2.6;
  /* Room for the labels hanging under the bottom row. */
  margin-bottom: 18px;
}
.map svg { position: absolute; inset: 0; width: 100%; height: 100%; overflow: visible; }
.map .lane { stroke: #1b1b2a; stroke-width: 4; fill: none; }
.map .lane.flown { stroke: var(--cyan); }
.map .lane.live { stroke: var(--amber); stroke-dasharray: 6 8; animation: crawl 0.6s linear infinite; }
@keyframes crawl { to { stroke-dashoffset: -14; } }

/* A node is a planet and its name. Positioned by main.js in percent, so the
   same layout fits every width without the CSS knowing how many there are. */
.map-node {
  position: absolute;
  transform: translate(-50%, -50%);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  background: none;
  border: 0;
  padding: 0;
  cursor: default;
  font-family: var(--arcade);
  color: var(--dim);
}
.map-node .planet {
  width: clamp(18px, 3.6vw, 30px);
  height: clamp(18px, 3.6vw, 30px);
  border-radius: 50%;
  border: 4px solid var(--dim);
  background: #05050a;
  box-shadow: 0 0 0 3px #05050a;
}
.map-node .planet-name {
  font-size: clamp(6px, 1.3vw, 9px);
  letter-spacing: 0.1em;
  white-space: nowrap;
}
/* The sun is the only node that is not a dot. */
.map-node.sun .planet {
  width: clamp(30px, 6vw, 52px);
  height: clamp(30px, 6vw, 52px);
  border-color: #6b4a10;
  background: radial-gradient(circle, #3a2a08 0%, #05050a 70%);
}

.map-node.flown { color: #8fd9e6; }
.map-node.flown .planet { border-color: var(--cyan); background: var(--cyan); }
.map-node.here { color: #fff; }
.map-node.here .planet { border-color: var(--cyan); background: var(--cyan); box-shadow: 0 0 0 3px #05050a, 0 0 22px rgba(0, 229, 255, 0.7); }

.map-node.open { color: var(--amber); cursor: pointer; }
.map-node.open .planet { border-color: var(--amber); animation: blink 1s steps(1, end) infinite; }
.map-node.open:hover .planet,
.map-node.open:focus-visible .planet { background: var(--amber); animation: none; box-shadow: 0 0 0 3px #05050a, 0 0 26px rgba(255, 212, 71, 0.75); }
.map-node.open:focus-visible { outline: none; }
.map-node.open:focus-visible .planet-name,
.map-node.open:hover .planet-name { color: #fff; }
.map-node.chosen { color: #fff; }
.map-node.chosen .planet { border-color: var(--amber); background: var(--amber); box-shadow: 0 0 0 3px #05050a, 0 0 26px rgba(255, 212, 71, 0.75); }
.map-node.open.sun .planet { background: radial-gradient(circle, #7a5210 0%, #05050a 70%); }
.map-node.open.sun:hover .planet,
.map-node.open.sun:focus-visible .planet { background: var(--amber); }
.map-node.sun.here .planet,
.map-node.sun.flown .planet { border-color: var(--amber); background: var(--amber); }

/* ─── Pause and the console ──────────────────────────────────
   Form fields in the cabinet's own voice: square, dim border, amber when
   focused, no browser chrome. */
.arcade-input,
.arcade-select {
  font-family: var(--arcade);
  font-size: clamp(9px, 1.8vw, 12px);
  letter-spacing: 0.08em;
  color: #fff;
  background: #05050a;
  border: 3px solid var(--dim);
  padding: 10px 12px;
  text-transform: uppercase;
  min-width: 0;
  width: 100%;
}
.arcade-input:focus, .arcade-select:focus { outline: none; border-color: var(--amber); }
.arcade-input::placeholder { color: var(--dim); }
.arcade-select option { background: #05050a; color: #fff; }
.arcade-btn.small-btn { padding: 10px 14px; font-size: clamp(8px, 1.8vw, 11px); }

.code-row { display: flex; gap: 10px; width: 100%; max-width: 420px; }

/* The sound row on the pause panel: the mute switch beside a volume slider
   drawn in the cabinet's own voice — a square amber thumb on a dim track, no
   browser chrome. The mute button's label IS its state (SOUND ON / OFF). */
.sound-row { display: flex; gap: 12px; align-items: center; flex-wrap: wrap; justify-content: center; width: 100%; max-width: 420px; }
.arcade-btn[aria-pressed="true"] { color: var(--danger); border-color: var(--danger); }
.arcade-btn[aria-pressed="true"]:hover,
.arcade-btn[aria-pressed="true"]:focus-visible { background: var(--danger); color: #05050a; }
.volume-field { display: flex; align-items: center; gap: 10px; flex: 1 1 160px; }
.volume-field > span { font-size: clamp(7px, 1.4vw, 9px); letter-spacing: 0.12em; color: var(--dim); }
.arcade-range { -webkit-appearance: none; appearance: none; flex: 1 1 auto; height: 4px; background: var(--dim); outline: none; cursor: pointer; }
.arcade-range::-webkit-slider-thumb { -webkit-appearance: none; appearance: none; width: 16px; height: 16px; background: var(--amber); border: 0; border-radius: 0; }
.arcade-range::-moz-range-thumb { width: 16px; height: 16px; background: var(--amber); border: 0; border-radius: 0; }
.arcade-range:focus-visible::-webkit-slider-thumb { box-shadow: 0 0 14px rgba(255, 212, 71, 0.75); }
.code-row.rejected .arcade-input { border-color: var(--danger); animation: shake 0.25s steps(2, end) 2; }
@keyframes shake { 0%, 100% { transform: translateX(0); } 50% { transform: translateX(-6px); } }

.console {
  width: 100%;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: 12px 14px;
  text-align: left;
  max-height: 70vh;
  overflow-y: auto;
  padding-right: 4px;
}
.console .field { display: flex; flex-direction: column; gap: 6px; }
.console .field > span { font-size: clamp(6px, 1.3vw, 8px); letter-spacing: 0.12em; color: var(--cyan); }
.console .group { grid-column: 1 / -1; font-size: clamp(7px, 1.4vw, 9px); letter-spacing: 0.14em; color: var(--amber); margin-top: 6px; }
.console .upgrades {
  grid-column: 1 / -1;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
  gap: 8px 10px;
}
.console .upgrades .field > span { color: #c8c8d8; }
.console .upgrades .arcade-input { padding: 6px 8px; }
.console-actions { display: flex; gap: 12px; flex-wrap: wrap; justify-content: center; }

/* A cleared run gets the amber of the logo rather than the red of a death. */
.heading.win { color: var(--amber); text-shadow: 0 0 18px rgba(255, 212, 71, 0.6); }

/* ─── The shop ───────────────────────────────────────────────
   Between stages. Four lines you can mostly not afford, and the whole
   readability job is making "what does this cost / can I have it" answerable
   at a glance, because the interesting part is the arithmetic and not the
   chrome. */
.shop-items {
  display: flex;
  flex-direction: column;
  gap: 8px;
  width: 100%;
  max-width: 460px;
  margin: 10px 0 4px;
}

.shop-item {
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: center;
  gap: 4px 12px;
  padding: 10px 14px;
  border: 1px solid #2c3a4d;
  background: #0b1018;
  color: inherit;
  font: inherit;
  text-align: left;
  cursor: pointer;
}

.shop-item:hover:not(:disabled),
.shop-item:focus-visible:not(:disabled) { border-color: #6ee7ff; background: #101a26; }

/* Unaffordable and unavailable look the SAME on purpose. Both mean "not this
   visit", and splitting them into two greys would be a distinction the player
   has to learn for no decision it changes. The reason still prints. */
.shop-item:disabled { opacity: 0.4; cursor: default; }

.shop-item .shop-name { font-size: 13px; letter-spacing: 1px; }
.shop-item .shop-blurb { grid-column: 1; font-size: 10px; opacity: 0.6; letter-spacing: 0.5px; }
.shop-item .shop-price { grid-row: 1 / span 2; font-size: 16px; letter-spacing: 1px; }
.shop-item.affordable .shop-price { color: #7dffa8; }
.shop-item:disabled .shop-price { color: #ff8a8a; }
/* The free SWAP line, when a spare was carried in: its blurb carries the same
   warning, in the same red, as a weapon card that burns the build. */
.shop-item.burns .shop-blurb { color: #ff8a8a; opacity: 0.95; }

/* The card row. Shown over the menu while a paid-for crate is open. */
.shop-choice { width: 100%; max-width: 560px; }
.shop-cards { display: flex; gap: 10px; justify-content: center; flex-wrap: wrap; margin-top: 8px; }

.shop-card {
  flex: 1 1 150px;
  max-width: 180px;
  padding: 14px 10px;
  border: 1px solid #2c3a4d;
  border-top-width: 3px;
  background: #0b1018;
  color: inherit;
  font: inherit;
  cursor: pointer;
}

.shop-card:hover, .shop-card:focus-visible { background: #101a26; }
.shop-card .card-name { display: block; font-size: 13px; letter-spacing: 1px; }
.shop-card .card-blurb { display: block; margin-top: 6px; font-size: 10px; opacity: 0.65; line-height: 1.5; }
/* A card that will destroy the build you are carrying says so in the colour
   the rest of the cabinet uses for damage. */
.shop-card.burns .card-blurb { color: #ff8a8a; opacity: 0.95; }

/* ─── The party lobby ────────────────────────────────────────
   Up to four devices before a run. Two halves of one panel: HOST / JOIN until
   you are in a party, then the code and the seats. The code is the biggest
   thing on the screen because it is read out loud across a room, and the seat
   list is a table rather than cards: four rows of name · ship · ready is a
   thing you scan, not a thing you choose from. */
.party-entry { display: flex; flex-direction: column; gap: 14px; width: 100%; max-width: 420px; align-items: stretch; }
.party-entry .arcade-btn { width: 100%; }
/* Typing a code: wide letters, centred, so the four of them read as a word. */
.party-code-input { text-align: center; letter-spacing: 0.4em; }

.party-lobby { display: flex; flex-direction: column; gap: 12px; width: 100%; max-width: 460px; align-items: center; }
.party-code {
  font-size: clamp(26px, 7vw, 44px);
  letter-spacing: 0.3em;
  padding-left: 0.3em;                /* balances the trailing letter-spacing */
  color: var(--amber);
  text-shadow: 3px 0 var(--magenta), -3px 0 var(--cyan), 0 0 24px rgba(255, 212, 71, 0.5);
}

.party-seats { list-style: none; width: 100%; display: flex; flex-direction: column; gap: 6px; }
.party-seat {
  display: grid;
  grid-template-columns: 1.4em 1fr auto 1.6em;
  align-items: center;
  gap: 10px;
  padding: 10px 12px;
  border: 1px solid #2c3a4d;
  background: #0b1018;
  font-size: clamp(8px, 1.8vw, 11px);
  letter-spacing: 0.1em;
  text-align: left;
  color: #c8c8d8;
}
.party-seat.me { border-color: var(--cyan); }
/* The row's left edge is the seat's ship colour in flight: COLOURS.players
   [seat index] from tuning.js, which the shell writes onto the row as
   --seat-colour (renderPartySeats) so the two cannot drift. The fallbacks
   are that palette as of today, for a row the shell has not painted. */
.party-seat[data-seat="0"] { box-shadow: inset 4px 0 0 var(--seat-colour, #3aa0ff); }
.party-seat[data-seat="1"] { box-shadow: inset 4px 0 0 var(--seat-colour, #f0506e); }
.party-seat[data-seat="2"] { box-shadow: inset 4px 0 0 var(--seat-colour, #2ec27e); }
.party-seat[data-seat="3"] { box-shadow: inset 4px 0 0 var(--seat-colour, #e3b341); }
.party-seat .seat-crown { color: var(--amber); font-size: 1.1em; visibility: hidden; }
.party-seat.host .seat-crown { visibility: visible; }
.party-seat .seat-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.party-seat .seat-ship { color: var(--dim); font-size: 0.85em; }
.party-seat .seat-ready { color: var(--dim); text-align: center; }
.party-seat.ready .seat-ready { color: #7dffa8; }
/* An empty seat is drawn so the host can see how many more can come. */
.party-seat.empty { border-style: dashed; color: var(--dim); }

/* Ship pick: the same cards as the roster, smaller, one row. */
.party-ships { display: flex; gap: 10px; flex-wrap: wrap; justify-content: center; }
.party-ships .pilot { width: 96px; padding: 10px 6px; gap: 8px; }
.party-ships .ship { width: 30px; height: 36px; }

.party-actions { display: flex; gap: 12px; flex-wrap: wrap; justify-content: center; }
.arcade-btn[data-action="party-ready"][aria-pressed="true"] { color: #7dffa8; border-color: #7dffa8; }
.arcade-btn[data-action="party-ready"][aria-pressed="true"]:hover,
.arcade-btn[data-action="party-ready"][aria-pressed="true"]:focus-visible { background: #7dffa8; color: #05050a; }

/* The status line: dim when it is only informing, red when it is an error,
   amber while a connection is being made. main.js sets the class. */
.party-status { color: var(--dim); line-height: 1.8; min-height: 1.8em; max-width: 460px; }
.party-status.error { color: var(--danger); }
.party-status.busy { color: var(--amber); }
