@import url('https://fonts.googleapis.com/css2?family=Press+Start+2P&display=swap');

/* ---------------------------------------------------------------
   Flat arcade shell.

   The menu and the game canvas are meant to read as one object, so
   anything added here follows the same rules:
     - the shell is dark, in the same color family as the canvas
     - square corners only (no border-radius, anywhere)
     - no box-shadow, no blur, no gradient fills
     - depth comes from borders and flat fills, never from lighting
     - selection = solid block of the accent color
   --------------------------------------------------------------- */

:root {
  --bg: #06080f;          /* shell, one shade under the arena */
  --arena: #0b0e18;       /* canvas background, mirrored by panels */
  --panel: #0e1220;
  --panel-alt: #121728;
  --fg: #e8eefc;
  --dim: #6e7c9c;
  --line: #232c44;        /* resting outline */
  --line-strong: #35415f; /* hover / active outline */
  /* Both accents are overwritten at runtime by theme.js from the player's
     chosen side colors, so the menu always matches the arena. The values
     here are the defaults and the fallback if scripting is unavailable. */
  --accent: #3fa294;      /* your side, matching the right paddle */
  --accent2: #e2603f;     /* their side, matching the left paddle */
  --on-accent: #06080f;
}

* { margin: 0; padding: 0; box-sizing: border-box; }

::-webkit-scrollbar { width: 6px; height: 6px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb { background: var(--line); }
::-webkit-scrollbar-thumb:hover { background: var(--line-strong); }
::-webkit-scrollbar-corner { background: transparent; }
* { scrollbar-width: thin; scrollbar-color: var(--line) transparent; }

html, body {
  height: 100%;
  background: var(--bg);
  font-family: 'Press Start 2P', monospace;
  color: var(--fg);
  overflow: hidden;
}

body {
  display: flex;
  align-items: center;
  justify-content: center;
}

#game-wrapper {
  position: relative;
  width: min(96vw, calc(96vh * 900 / 560));
  aspect-ratio: 900 / 560;
  transition: width 300ms ease, margin-bottom 300ms ease;
}

#game {
  width: 100%;
  height: 100%;
  display: block;
  border: 2px solid var(--line-strong);
  background: var(--arena);
  cursor: default;
}

/* Spectator tools sit outside the arena, never over the match. */
body.spectating #game-wrapper {
  width: min(96vw, calc((96vh - 130px) * 900 / 560));
  margin-bottom: 130px;
}
#spectator-panel {
  position: absolute;
  top: calc(100% + 20px);
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-wrap: wrap;
  gap: 10px;
  padding: 12px;
  border: 1px solid var(--line);
  background: var(--panel);
  font-size: 8px;
}
#spectator-panel:not([hidden]) {
  animation: spectator-panel-in 300ms ease-out both;
}
@keyframes spectator-panel-in {
  from { opacity: 0; transform: translateY(10px); }
  to { opacity: 1; transform: translateY(0); }
}
#spectator-panel[hidden] { display: none; }
@media (prefers-reduced-motion: reduce) {
  #game-wrapper { transition: none; }
  #spectator-panel:not([hidden]) { animation: none; }
}
#spawn-choice {
  min-width: 0;
  max-width: 100%;
  padding: 8px;
  color: var(--fg);
  background: var(--arena);
  border: 1px solid var(--line);
  border-radius: 0;
  font: inherit;
}
#spawn-choice:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
#spawn-status { width: 100%; text-align: center; color: var(--dim); line-height: 1.6; }

#spawn-btn:disabled { opacity: 0.35; cursor: not-allowed; }

/* ---------- overlays ---------- */
.overlay {
  position: absolute;
  inset: 0;
  display: none;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 16px;
  /* A near-opaque flat wash. The arena stays readable underneath as a
     silhouette, and nothing is softened or blurred. */
  background: rgba(6, 8, 15, 0.94);
  padding: 12px;
  overflow-y: auto;
  text-align: center;
  z-index: 10;
}
.overlay.visible { display: flex; }

/* Pause covers the whole window, including spectator controls below the arena. */
#pause {
  position: fixed;
  inset: 0;
  z-index: 50;
}

/* Sized for the full "PONGETITIVE" wordmark: tighter tracking than a 4-letter
   title needs, and it wraps instead of overflowing on narrow screens.

   Titles carry your own side color, so the wordmark and PAUSED match the
   paddle you play. The game-over title is the one exception: endMatch() sets
   its color inline to the WINNER's side, which overrides this. */
.title {
  font-size: clamp(18px, 3.4vw, 34px);
  letter-spacing: 5px;
  color: var(--accent);
  max-width: 100%;
  overflow-wrap: anywhere;
}
.title.small { font-size: clamp(20px, 4vw, 36px); letter-spacing: 6px; }

.subtitle {
  font-size: clamp(8px, 1.4vw, 12px);
  color: var(--dim);
  line-height: 1.8;
}

.tabs {
  display: flex;
  gap: 28px;
  width: 70%;
  justify-content: center;
  border-bottom: 1px solid var(--line);
}

.tab {
  font-family: inherit;
  font-size: 11px;
  padding: 10px 26px 12px;
  background: transparent;
  color: var(--dim);
  border: none;
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;                /* underline sits on the divider */
  cursor: pointer;
  letter-spacing: 2px;
  transition: color 0.12s linear, border-color 0.12s linear;
}
.tab:hover { color: var(--fg); }
.tab.selected {
  color: var(--accent);
  border-bottom-color: var(--accent);
}
.tab:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }

.tab-page {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 16px;
  width: min(820px, 90%);
  max-height: 64%;
  overflow-y: auto;
  /* No bottom padding: the sticky launch strip is the last child and must be
     able to sit flush with the bottom edge. A sticky element cannot move past
     its container's padding box, so any padding here shows as a gap under it. */
  padding: 2px 10px 0;
}

/* Play setup has three clear layers: match basics, difficulty details, launch. */
#tab-play { gap: 18px; }
#tab-play > .subtitle {
  max-width: 620px;
  padding: 0 12px 8px;
}
.match-choice {
  width: 100%;
  gap: 10px;
  padding: 13px 16px;
  border: 1px solid var(--line);
  background: var(--panel);
}
.room-panel {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 9px;
  width: 100%;
  padding-top: 11px;
  border-top: 1px solid var(--line);
}
.room-panel[hidden], .room-actions[hidden], .room-join-form[hidden], .room-host-code[hidden] { display: none; }
.room-actions, .room-join-form {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-wrap: wrap;
  gap: 8px;
}
.room-host-code {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
}
.room-host-code strong {
  padding: 10px 15px;
  color: var(--on-accent);
  background: var(--accent);
  border: 1px solid var(--accent);
  letter-spacing: 3px;
}
#room-code-input {
  width: 128px;
  max-width: 100%;
  padding: 8px;
  color: var(--fg);
  background: var(--arena);
  border: 1px solid var(--line);
  border-radius: 0;
  font: inherit;
  font-size: 8px;
  text-align: center;
  text-transform: uppercase;
}
#room-code-input::placeholder { color: var(--dim); }
#room-code-input:focus { outline: none; border-color: var(--accent); }
.room-status {
  min-height: 12px;
  font-size: 8px;
  color: var(--dim);
  line-height: 1.6;
}
.room-permissions {
  max-width: 500px;
  font-size: 8px;
  color: var(--accent);
  line-height: 1.7;
}
/* A waiting guest cannot change the MATCH settings, which the host owns, so
   they are dimmed. Side colors are exempt: they are a purely local cosmetic
   preference that stays live for both players, and dimming a control that
   still works would misrepresent it as locked. */
#tab-play.pvp-guest-locked > :not(#opponent-group):not(#color-group):not(.launch-zone) {
  opacity: 0.3;
}
#tab-play.pvp-guest-locked > .launch-zone {
  opacity: 0.5;
}
/* Sticky launch strip: a flat plate with a hard top border, so scrolling
   content passes under a visible edge.

   `bottom: 0` pins it flush to the container's bottom edge. Its own bottom
   padding provides the breathing room under the button, so the plate itself
   still reaches the edge and no background shows through beneath it. */
.launch-zone {
  position: sticky;
  bottom: 0;
  z-index: 2;
  width: 100%;
  display: flex;
  justify-content: center;
  padding: 18px 0 14px;
  border-top: 1px solid var(--line);
  background: var(--bg);
}

/* ---------- side colors ---------- */
.color-sides {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 10px;
  width: 100%;
}
.color-side {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 9px;
  flex: 1 1 0;
  min-width: 0;
  padding: 11px 12px;
  border: 1px solid var(--line);
  background: var(--panel-alt);
}
.color-side-label {
  font-size: 8px;
  letter-spacing: 2px;
  color: var(--dim);
}
/* Two columns of named chips. Fixed columns keep every chip the same width, so
   the longest name (VIOLET) sets the size and none of them jump on selection. */
.swatches {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 5px;
  width: 100%;
}
/* The button IS the swatch: it wears its own color as an outline, and fills
   with it once selected. Same language as the power-up and curse toggles.
   Unselected chips are held at 0.55 so the selected one clearly leads. */
.swatch {
  display: flex;
  align-items: center;
  justify-content: center;
  min-width: 0;
  padding: 7px 6px;
  border: 1px solid var(--sw);
  border-radius: 0;
  background: transparent;
  color: var(--sw);
  opacity: 0.55;
  font-family: inherit;
  font-size: 7px;
  letter-spacing: 1px;
  text-align: center;
  cursor: pointer;
  transition: color 0.12s linear, background-color 0.12s linear, opacity 0.12s linear;
}
.swatch-name {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.swatch:hover:not(:disabled) { opacity: 1; }
/* Selected fills with the color itself. Every palette entry clears 5.3:1
   against --on-accent, so the name stays readable on all of them. */
.swatch.selected {
  opacity: 1;
  color: var(--on-accent);
  background: var(--sw);
}
.swatch:focus-visible { outline: 2px solid var(--fg); outline-offset: 2px; }
/* Owned by the other side. Disabled in the DOM as well, so it cannot be clicked
   or tabbed to, because the two sides must never share a color. */
.swatch:disabled {
  opacity: 0.3;
  cursor: not-allowed;
}
/* A line through the name says "spoken for" rather than merely "faint", which
   on its own could be mistaken for a rendering artifact. */
.swatch:disabled .swatch-name { text-decoration: line-through; }
.color-hint {
  font-size: 7px;
  color: var(--dim);
  line-height: 1.7;
}

/* ---------- difficulty ---------- */
/* Difficulty choices and their settings are one unit: presets show a muted,
   read-only summary; Custom unlocks the same controls in place. */
.difficulty-panel {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 14px;
  flex-shrink: 0;
  width: 100%;
  box-sizing: border-box;
  border: 1px solid var(--line);
  padding: 16px 18px 18px;
  background: var(--panel);
}
.difficulty-tabs {
  display: flex;
  gap: 6px;
  flex-wrap: wrap;
  justify-content: center;
  padding: 0 0 14px;
  border-bottom: 1px solid var(--line);
  width: 100%;
}
.difficulty-tabs .opt { padding: 8px 10px; font-size: 8px; }
/* Fixed, centered three-column card matrix. A grid row is always as tall as
   its tallest card and `stretch` gives every card in that row the same bottom
   edge; cards therefore never drift vertically based on their own content.
   Extra controls add rows inside a card, not a different card position. */
.difficulty-settings {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  grid-auto-flow: row;
  align-items: stretch;
  justify-content: center;
  gap: 12px;
  width: 100%;
  transition: opacity 0.18s ease;
}
/* Seven cards leaves one final card; put it on the grid's center line. */
.difficulty-settings .option-group:last-child { grid-column: 2; }
/* ...but in PvP the AI SKILL card is hidden, and the remaining six fill all
   three columns exactly. `display: none` does not change which element is
   :last-child, so without this the final card is still dragged onto the center
   line - which pushes it into a row of its own and leaves a hole beside the
   card before it. With no orphan to center, ordinary flow is already right. */
.difficulty-settings.no-ai .option-group:last-child { grid-column: auto; }
.difficulty-settings .option-group {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: flex-start;
  min-width: 0;
  gap: 9px;
  padding: 12px 14px;
  border: 1px solid var(--line);
  background: var(--panel-alt);
}
.difficulty-settings .option-label {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  color: var(--dim);
  letter-spacing: 2px;
  /* Compensate for letter-spacing's trailing measured space, so titles land
     on the actual center line. */
  margin-right: -2px;
}
/* A single two-column control grid is shared by every card. Button count can
   add rows only; it cannot alter a card's width or its horizontal center. */
.difficulty-settings .options {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 6px;
  width: 100%;
}
/* Do not let an odd final control become a full-width strip. It occupies one
   regular button track and is centered on the card. */
#c-ball-options .opt:last-child:nth-child(odd),
#c-curse-options .opt:last-child:nth-child(odd),
#c-field-options .opt:last-child:nth-child(odd) {
  grid-column: 1 / -1;
  justify-self: center;
  width: calc((100% - 6px) / 2);
}
/* Preserve equal card widths as space tightens: 3 → 2 → 1 columns. The
   unpaired final card remains exactly one normal card wide and centered. */
@media (max-width: 760px) {
  .difficulty-settings {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    align-items: stretch;
  }
  .difficulty-settings .option-group:last-child {
    grid-column: 1 / -1;
    justify-self: center;
    width: calc((100% - 12px) / 2);
    box-sizing: border-box;
  }
  /* Six cards divide evenly across two columns as well, so the final card is
     again a normal cell rather than a centered orphan. */
  .difficulty-settings.no-ai .option-group:last-child {
    grid-column: auto;
    justify-self: stretch;
    width: auto;
  }
}
@media (max-width: 500px) {
  .difficulty-settings { grid-template-columns: 1fr; }
  .difficulty-settings .option-group:last-child {
    grid-column: auto;
    justify-self: stretch;
    width: auto;
  }
}
.difficulty-settings .opt {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 5px;
  min-width: 0;
  padding: 7px 8px;
  font-size: 7px;
}
.difficulty-settings .opt [aria-hidden="true"] {
  font-family: sans-serif;
  font-size: 10px;
  line-height: 1;
}
.difficulty-panel.preset-locked .difficulty-settings { opacity: 0.3; }

/* ---------- info ---------- */
/* INFO has no sticky footer, so it restores the bottom padding that #tab-play
   drops for the launch strip. Without it the last section ends flush against
   the overlay edge. */
#tab-info { width: 88%; gap: 12px; padding-bottom: 14px; }
.info-section {
  width: 100%;
  flex-shrink: 0;
  text-align: left;
  padding: 12px 14px;
  border: 1px solid var(--line);
  background: var(--panel);
}
.info-section summary {
  cursor: pointer;
  color: var(--fg);
  font-size: 9px;
  line-height: 1.8;
}
.info-section summary:hover { color: var(--accent); }
.info-section summary:focus-visible { outline: 2px solid var(--accent); outline-offset: 4px; }
.info-section .controls-hint { margin-top: 12px; color: var(--dim); }
.info-section ul, .info-section ol { padding-left: 22px; }
.info-section li + li { margin-top: 8px; }
.info-section strong { color: var(--fg); font-weight: normal; }

/* Two-column term/value lists. The label column sizes to its content so every
   row in a section shares one gutter, which is what makes them scannable. */
.keybinds, .statlist {
  display: grid;
  grid-template-columns: max-content 1fr;
  gap: 9px 16px;
  align-items: baseline;
  margin-top: 12px;
  font-size: 8px;
  line-height: 1.9;
}
.keybinds dt, .statlist dt { color: var(--fg); white-space: nowrap; }
.keybinds dd, .statlist dd { color: var(--dim); }
.statlist dd b { color: var(--accent); font-weight: normal; }
/* Keys sit in a row that can wrap on narrow screens without breaking a chord. */
.keybinds dd { display: flex; flex-wrap: wrap; align-items: center; gap: 4px; }
.keybinds .note, .statlist .note { color: var(--dim); opacity: 0.75; }

/* Icon chip echoing the pickup's arena color, so the list reads like the game. */
.pill {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 16px;
  padding: 2px 4px;
  margin-right: 5px;
  border: 1px solid var(--c);
  color: var(--c);
  font-size: 7px;
  line-height: 1;
  vertical-align: middle;
}

.info-section .steps { margin-top: 12px; }
.info-section .steps + .statlist { margin-top: 14px; }

/* ---------- menu footer ---------- */
.menu-footer {
  flex-shrink: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 7px;
  font-size: 7px;
  line-height: 1;
}
.menu-footer small { font-size: inherit; color: var(--dim); opacity: 0.65; }
.menu-footer a { color: var(--dim); text-decoration: none; }
.menu-footer a:hover { color: var(--accent); }
.menu-footer a:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
.menu-footer #build-id::before { content: '\00b7'; margin: 0 4px; }

/* ---------- controls ---------- */
.option-group {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12px;
}

.option-label {
  font-size: 9px;
  color: var(--dim);
  letter-spacing: 3px;
}

.options { display: flex; gap: 10px; flex-wrap: wrap; justify-content: center; }

.opt {
  /* --c = this button's accent color. It defaults to the shared accent, and
     buttons that carry their own identity color (IMPOSSIBLE, CUSTOM, the
     curse and power-up toggles) set --c inline so that their selected block,
     hover border and focus ring all use that color. */
  --c: var(--accent);
  font-family: inherit;
  font-size: 10px;
  padding: 10px 14px;
  background: transparent;
  color: var(--dim);
  border: 1px solid var(--line);
  border-radius: 0;
  cursor: pointer;
  transition: color 0.12s linear, border-color 0.12s linear, background-color 0.12s linear;
}
.opt[style*="--c"]:not(.selected) { color: var(--c); border-color: var(--line-strong); }
.opt:hover { color: var(--fg); border-color: var(--c); background: var(--panel-alt); }
.opt:focus-visible { outline: 2px solid var(--c); outline-offset: 2px; }
/* Selected state is a solid block of the button's own color. */
.opt.selected {
  color: var(--on-accent);
  background: var(--c);
  border-color: var(--c);
}
.opt.selected:hover { color: var(--on-accent); background: var(--c); }

.opt:disabled { cursor: default; }
.opt:disabled:hover { color: var(--dim); border-color: var(--line); background: transparent; }
.opt.toggle:disabled:hover { color: var(--c); border-color: var(--line-strong); background: transparent; }
.opt.selected:disabled:hover { color: var(--on-accent); background: var(--c); border-color: var(--c); }

/* Toggles keep their own accent color: off = outline only, on = filled. */
.opt.toggle { opacity: 0.45; }
.opt.toggle,
.opt.toggle:hover { color: var(--c); }
.opt.toggle.selected {
  opacity: 1;
  color: var(--on-accent);
  background: var(--c);
  border-color: var(--c);
}

.big-btn {
  font-family: inherit;
  font-size: 13px;
  padding: 16px 34px;
  color: var(--on-accent);
  background: var(--accent);
  border: 1px solid var(--accent);
  border-radius: 0;
  cursor: pointer;
  letter-spacing: 2px;
  transition: color 0.12s linear, background-color 0.12s linear, border-color 0.12s linear;
}
/* Hover inverts the block (filled becomes outlined) rather than raising it. */
.big-btn:hover { color: var(--accent); background: transparent; }
.big-btn:active { color: var(--on-accent); background: var(--accent); }
.big-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }

/* A disabled action must look disabled: dimmed, with no hover reaction and a
   not-allowed cursor. Otherwise it still reads as clickable and doing nothing
   looks like a bug rather than a rule.
   Applies to START MATCH and REMATCH while a guest waits on the host. */
.big-btn:disabled {
  opacity: 0.35;
  cursor: not-allowed;
}
.big-btn:disabled:hover,
.big-btn:disabled:active { color: var(--on-accent); background: var(--accent); }

.big-btn.secondary {
  background: transparent;
  color: var(--dim);
  border-color: var(--line-strong);
}
.big-btn.secondary:hover { color: var(--fg); border-color: var(--fg); background: transparent; }
.big-btn.secondary:disabled:hover,
.big-btn.secondary:disabled:active { color: var(--dim); background: transparent; }

.controls-hint {
  font-size: 8px;
  color: var(--dim);
  line-height: 2;
}

kbd {
  font-family: inherit;
  background: var(--panel-alt);
  border: 1px solid var(--line);
  border-bottom-width: 2px;
  padding: 3px 6px;
  color: var(--fg);
  font-size: 8px;
  line-height: 1;
  white-space: nowrap;
}
/* Adjacent keys are alternatives (W/S), so they read as one cluster. */
kbd + kbd { margin-left: 2px; }
