:root {
  --cherry: #d7263d;
  --teal: #1b998b;
  --gold: #f4c22b;
  --plum: #5e2b97;
  --brand: #2e1f66;
  --success: #0e7a6e;
  --track: #3b4256;
  --bg: #fff8e7;
  --panel: #ffffff;
  --ink: #1d2340;
  --muted: #69708c;
  --line: #e7e1cf;
  --shadow: 0 10px 30px rgba(46, 31, 102, 0.12);

  /* Board geometry; mini boards override these. */
  --rh: 26px;
  --rw: min(66px, calc((100vw - 64px) / 6 - 8px));
  --gap: 10px;
}

* {
  box-sizing: border-box;
}

html,
body {
  margin: 0;
}

/* No double-tap zoom anywhere: quick taps are game input. Pinch zoom still works. */
html {
  touch-action: manipulation;
  -webkit-text-size-adjust: 100%;
}

body {
  min-height: 100dvh;
  background:
    radial-gradient(circle at 10% 0%, #ffe3e6 0, transparent 35%),
    radial-gradient(circle at 100% 30%, #def5f1 0, transparent 40%),
    var(--bg);
  color: var(--ink);
  font-family: 'Fredoka', ui-rounded, 'SF Pro Rounded', system-ui, sans-serif;
  font-weight: 500;
  -webkit-tap-highlight-color: transparent;
}

[hidden] {
  display: none !important;
}

.c0 { color: var(--cherry); }
.c1 { color: var(--teal); }
.c2 { color: #e0a800; }
.c3 { color: var(--plum); }

/* ---------- Top bar ---------- */

.topbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 14px 16px;
  max-width: 1200px;
  margin: 0 auto;
}

.logo {
  margin: 0;
  font-size: clamp(22px, 5vw, 32px);
  font-weight: 700;
  letter-spacing: 0.02em;
  line-height: 1;
}

.logo {
  display: flex;
  align-items: center;
  gap: 0.04em;
}

.logo-towers {
  margin-left: 0.3em;
  color: var(--brand);
}

/* A buddy peeking over the wordmark: the Zip art, set by client.js. */
.logo-mark {
  width: 1.5em;
  height: 0.9em;
  margin-right: 0.25em;
  background: var(--logo-art) center / contain no-repeat;
}

.room-badge {
  background: var(--panel);
  border: 2px solid var(--line);
  border-radius: 999px;
  padding: 6px 12px;
  font-weight: 600;
  letter-spacing: 0.15em;
}

/* ---------- Panels & controls ---------- */

.screen {
  padding: 8px 16px 32px;
}

.panel {
  max-width: 440px;
  margin: 4vh auto 0;
  background: var(--panel);
  border-radius: 24px;
  padding: 24px;
  box-shadow: var(--shadow);
  display: flex;
  flex-direction: column;
  gap: 14px;
}

.tagline {
  margin: 0;
  font-size: 18px;
  text-align: center;
}

.label {
  margin: 0;
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--muted);
}

.field {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.field span {
  font-size: 14px;
  color: var(--muted);
}

input {
  font: inherit;
  font-size: 18px;
  padding: 12px 14px;
  border: 2px solid var(--line);
  border-radius: 14px;
  background: #fffdf7;
  color: var(--ink);
  min-width: 0;
}

input:focus {
  outline: none;
  border-color: var(--teal);
}

.btn {
  font: inherit;
  font-size: 18px;
  font-weight: 600;
  border: 0;
  border-radius: 14px;
  padding: 12px 18px;
  cursor: pointer;
  background: #efebfa;
  color: var(--brand);
  transition: transform 0.08s, background 0.15s;
}

.btn:active {
  transform: translateY(1px) scale(0.99);
}

.btn:disabled {
  opacity: 0.5;
  cursor: default;
}

.btn-primary {
  background: var(--brand);
  color: #fff;
  box-shadow: 0 4px 0 #170d3d;
}

.btn-primary.is-on {
  background: var(--teal);
  box-shadow: 0 4px 0 #0e5f56;
}

.btn-small {
  font-size: 14px;
  padding: 8px 12px;
}

.btn-link {
  background: none;
  color: var(--muted);
  font-size: 15px;
  padding: 4px;
}

.divider {
  display: flex;
  align-items: center;
  gap: 10px;
  color: var(--muted);
  font-size: 14px;
}

.divider::before,
.divider::after {
  content: '';
  flex: 1;
  height: 2px;
  background: var(--line);
}

.join-row {
  display: flex;
  gap: 10px;
}

.join-row input {
  flex: 1;
  text-transform: uppercase;
  letter-spacing: 0.3em;
  text-align: center;
  font-weight: 600;
}

.error {
  margin: 0;
  min-height: 1.2em;
  color: #c4214f;
  text-align: center;
}

.hint {
  margin: 0;
  color: var(--muted);
  font-size: 14px;
}

.center {
  text-align: center;
}

.rules summary {
  cursor: pointer;
  color: var(--brand);
  font-weight: 600;
}

.rules ul {
  padding-left: 18px;
  margin: 8px 0 0;
  color: var(--muted);
  line-height: 1.5;
}

kbd {
  font: inherit;
  font-size: 0.85em;
  border: 1px solid var(--line);
  border-bottom-width: 2px;
  border-radius: 6px;
  padding: 0 5px;
  background: #fffdf7;
}

.code-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
}

.invite-actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  gap: 6px;
}

.btn-share {
  background: var(--cherry);
  color: #fff;
}

.bests {
  width: 100%;
  border-collapse: collapse;
  font-variant-numeric: tabular-nums;
}

.bests td {
  padding: 8px 4px;
  border-top: 2px solid var(--line);
}

.bests td:first-child {
  font-weight: 600;
}

.bests td:not(:first-child) {
  text-align: right;
  color: var(--muted);
}

.bests td.best-time {
  color: var(--ink);
  font-weight: 600;
}

/* Solo: the side panel holds your best time instead of opponents. */
.opp-list.solo .opp-board {
  display: none;
}

.opp.ahead .opp-status {
  color: var(--success);
}

.opp.behind .opp-status {
  color: #c4214f;
}

.big-code {
  font-size: 40px;
  font-weight: 700;
  letter-spacing: 0.2em;
  color: var(--brand);
}

.player-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.player-list li {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 10px 14px;
  border: 2px solid var(--line);
  border-radius: 14px;
}

.player-list li.waiting {
  border-style: dashed;
  color: var(--muted);
}

.player-name {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  min-width: 0;
}

/* ---------- Buddies outside the board ---------- */

/* A buddy drawn from the skin art (client.js sets --art per color). */
.avatar {
  display: inline-block;
  width: 33px;
  height: 20px;
  flex: none;
  background: var(--art) center / contain no-repeat;
  vertical-align: middle;
}

.hero-crew {
  display: flex;
  justify-content: center;
  gap: 14px;
  padding: 10px 0 2px;
}

.hero-buddy {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  font-size: 13px;
  font-weight: 600;
  color: var(--muted);
}

.hero-buddy .avatar {
  width: 66px;
  height: 40px;
  animation: bob 2.4s ease-in-out infinite;
}

.hero-buddy:nth-child(2) .avatar { animation-delay: 0.3s; }
.hero-buddy:nth-child(3) .avatar { animation-delay: 0.6s; }
.hero-buddy:nth-child(4) .avatar { animation-delay: 0.9s; }

.crew-picker {
  display: flex;
  justify-content: center;
  flex-wrap: wrap;
  gap: 8px;
  margin: 6px 0 2px;
}

.crew-option {
  font: inherit;
  font-size: 14px;
  font-weight: 500;
  padding: 6px 14px;
  border-radius: 999px;
  border: 2px solid var(--line);
  background: #fff;
  color: var(--ink);
  cursor: pointer;
}

.crew-option[aria-pressed='true'] {
  font-weight: 700;
  border-color: var(--brand);
  background: var(--brand);
  color: #fff;
}

.crew-option.locked {
  color: var(--muted);
  border-style: dashed;
}

/* A padlock before a pack sold in the iOS app. */
.crew-option.locked::before {
  content: '';
  display: inline-block;
  width: 10px;
  height: 12px;
  margin-right: 6px;
  vertical-align: -1px;
  background: currentColor;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 10 12'%3E%3Cpath d='M2 5V3.5a3 3 0 0 1 6 0V5h.5A1.5 1.5 0 0 1 10 6.5v4A1.5 1.5 0 0 1 8.5 12h-7A1.5 1.5 0 0 1 0 10.5v-4A1.5 1.5 0 0 1 1.5 5zm1.5 0h3V3.5a1.5 1.5 0 0 0-3 0z'/%3E%3C/svg%3E") center / contain no-repeat;
}

.crew-note {
  margin: 0;
  text-align: center;
  font-size: 13px;
  color: var(--muted);
}

.crew-option:focus-visible {
  outline: 3px solid var(--teal);
  outline-offset: 2px;
}

@keyframes bob {
  0%, 100% { transform: translateY(0); }
  50% { transform: translateY(-4px); }
}

@media (prefers-reduced-motion: reduce) {
  .hero-buddy .avatar { animation: none; }
}

.pill {
  font-size: 13px;
  font-weight: 600;
  border-radius: 999px;
  padding: 3px 10px;
  background: #f1efe7;
  color: var(--muted);
}

.pill.ready {
  background: #d9f3ef;
  color: var(--success);
}

.segmented {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 6px;
  background: #f4f1e6;
  padding: 5px;
  border-radius: 14px;
}

.segmented button {
  font: inherit;
  font-weight: 600;
  border: 0;
  border-radius: 10px;
  padding: 10px 0;
  background: transparent;
  color: var(--muted);
  cursor: pointer;
}

.segmented button[aria-checked='true'] {
  background: var(--panel);
  color: var(--brand);
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.08);
}

/* ---------- Game layout ---------- */

.game-grid {
  max-width: 1200px;
  margin: 0 auto;
  display: grid;
  grid-template-columns: 220px 1fr 220px;
  gap: 20px;
  align-items: start;
}

.my-side {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12px;
}

.hud {
  display: flex;
  gap: 22px;
  background: var(--panel);
  border-radius: 16px;
  padding: 10px 18px;
  box-shadow: var(--shadow);
}

.hud div {
  display: flex;
  flex-direction: column;
  align-items: center;
}

.hud b {
  font-size: 22px;
  font-variant-numeric: tabular-nums;
}

.target-card {
  background: #fff;
  border-radius: 14px;
  padding: 10px 12px 12px;
  box-shadow: var(--shadow);
  border-top: 10px solid var(--cherry);
}

.card-head {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  font-weight: 700;
  color: var(--brand);
  font-size: 13px;
  margin-bottom: 8px;
}

.card-diff {
  color: var(--cherry);
  font-size: 18px;
  text-transform: uppercase;
}

.opp-side {
  background: var(--panel);
  border-radius: 16px;
  padding: 12px;
  box-shadow: var(--shadow);
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.opp-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.opp-list:empty::before {
  content: 'Nobody else is here.';
  color: var(--muted);
  font-size: 14px;
}

.opp {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.opp + .opp {
  border-top: 2px solid var(--line);
  padding-top: 10px;
}

.opp-head {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 6px;
  font-size: 15px;
  min-width: 0;
}

.opp-name {
  font-weight: 600;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.opp-status {
  color: var(--muted);
  font-size: 13px;
  white-space: nowrap;
}

.opp.finished .opp-status {
  color: var(--success);
  font-weight: 600;
}

.opp-stats {
  margin: 0;
  font-size: 13px;
  color: var(--muted);
}

/* Status line above your board: finished, watching, time left in the round. */
.banner {
  background: var(--brand);
  color: #fff;
  border-radius: 12px;
  padding: 8px 14px;
  font-weight: 600;
  text-align: center;
}

.progress {
  height: 10px;
  background: #f1efe7;
  border-radius: 999px;
  overflow: hidden;
}

.progress div {
  height: 100%;
  width: 0;
  background: linear-gradient(90deg, var(--teal), var(--gold));
  transition: width 0.2s;
}

/* Each rival's bar takes their buddy's color from the skin (client.js). */

.opp-name {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-width: 0;
}

.opp-name .avatar {
  width: 26px;
  height: 16px;
}

.opp-name-text {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* ---------- Board ---------- */

.board {
  --lift: calc(var(--rh) * 1.6);
  display: flex;
  justify-content: center;
  align-items: flex-end;
  gap: var(--gap);
  padding: 0 calc(var(--gap) * 1.2) calc(var(--rh) * 0.6);
  position: relative;
  user-select: none;
  -webkit-user-select: none;
  -webkit-touch-callout: none;
  touch-action: none;
}

/* The ground the landing pads sit on. Scene art comes from the skin (client.js). */
.board::after {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: calc(var(--rh) * 1.1);
  border-radius: 0 0 calc(var(--rh) * 0.5) calc(var(--rh) * 0.5);
  background: var(--ground) center / 100% 100% no-repeat;
  z-index: 0;
}

/* The main board is a night sky over the landing zone. */
.board.main {
  --lift: calc(var(--rh) * 2.6);
  background: var(--sky) right top / cover no-repeat;
  border-radius: 24px;
  padding-top: 8px;
  padding-bottom: calc(var(--rh) * 1.4);
  overflow: hidden;
}

.board.main::after {
  height: calc(var(--rh) * 1.6);
}

.board.mini {
  --rh: 9px;
  --rw: 24px;
  --gap: 5px;
  --lift: 0px;
  padding-bottom: calc(var(--rh) * 0.9);
}

.board.mini.opp-board {
  --rh: 10px;
  --rw: 26px;
}

/* Three or more opponents: smaller boards so the column fits. */
.opp-list:not([data-count='1']):not([data-count='2']) .board.mini.opp-board {
  --rh: 6px;
  --rw: 18px;
  --gap: 4px;
}

/* A tower: the whole column is the touch target. */
.pole {
  position: relative;
  width: var(--rw);
  height: calc(var(--rh) * 10 + var(--lift) + var(--rh) * 0.4);
  display: flex;
  flex-direction: column-reverse;
  align-items: center;
  z-index: 1;
  cursor: pointer;
}

.board.mini .pole {
  cursor: default;
}

/* The landing pad under each tower. On Easy, pads a lifted buddy can land on glow
   (.target); a finished crew lights its pad (.done). */
.pole::before {
  content: '';
  position: absolute;
  bottom: calc(var(--rh) * -0.5);
  left: -14%;
  right: -14%;
  height: calc(var(--rh) * 0.95);
  background: var(--pad-idle) center / 100% 100% no-repeat;
  z-index: -1;
}

.pole.target::before {
  background-image: var(--pad-target);
  filter: drop-shadow(0 0 calc(var(--rh) * 0.3) var(--hint-glow)) brightness(1.15);
}

.pole.invalid::before {
  opacity: 0.4;
}

.board.main .pole.done::before {
  background-image: var(--pad-done);
}

/* On the ground, below the pad. */
.pole-num {
  position: absolute;
  top: calc(100% + var(--rh) * 0.55);
  font-size: 12px;
  font-weight: 600;
  color: var(--ground-ink);
  opacity: 0.75;
  z-index: 2;
  pointer-events: none;
}

.board.mini .pole-num {
  display: none;
}

/* A buddy. The slot is one stack step tall; the art overflows it upward (ears)
   and downward (feet), so each buddy stands on the head of the one below. The art
   for each color and mood is set by client.js from public/shared/skins.js. */
.piece {
  pointer-events: none; /* the tower is always the touch target */
  width: 100%;
  height: var(--rh);
  flex: none;
  position: relative;
  transition: transform 0.12s ease-out;
}

.piece::before {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  top: -50%;
  bottom: -16.67%;
  background: var(--art) center bottom / 100% 100% no-repeat;
}

/* A finished tower: full and one color. */
.board.main .pole.done .pole-num {
  opacity: 1;
  font-weight: 700;
}

.board.main .pole.done .pole-num::after {
  content: ' ✓';
}

.piece.lifted {
  position: absolute;
  top: calc(var(--rh) * 1.6);
  z-index: 3;
  animation: bob 1s ease-in-out infinite;
}

/* The saucer holding a beamed-up buddy, with its tractor beam. */
.piece.lifted::after {
  content: '';
  position: absolute;
  left: -20%;
  right: -20%;
  top: calc(var(--rh) * -1.4);
  height: calc(var(--rh) * 2.9);
  background: var(--beam) center top / 100% 100% no-repeat;
  z-index: -1;
}

.piece.lifted::before {
  filter: drop-shadow(0 8px 6px rgba(0, 0, 0, 0.2));
}

@keyframes bob {
  50% {
    transform: translateY(-3px);
  }
}

.board.shake {
  animation: shake 0.25s;
}

@keyframes shake {
  25% { transform: translateX(-4px); }
  75% { transform: translateX(4px); }
}

/* ---------- Overlay ---------- */

.overlay {
  touch-action: manipulation;
  position: fixed;
  inset: 0;
  display: grid;
  place-items: center;
  background: rgba(255, 248, 231, 0.6);
  backdrop-filter: blur(3px);
  z-index: 10;
  padding: 16px;
}

.overlay-box {
  background: var(--panel);
  border-radius: 24px;
  padding: 24px 28px;
  box-shadow: var(--shadow);
  text-align: center;
  min-width: min(360px, 100%);
}

.overlay-box h2 {
  margin: 0 0 12px;
  font-size: 30px;
  color: var(--brand);
}

.countdown {
  font-size: 96px;
  font-weight: 700;
  color: var(--cherry);
  line-height: 1;
}

.countdown:empty {
  display: none;
}

.result-table {
  width: 100%;
  border-collapse: collapse;
  margin-bottom: 10px;
  font-variant-numeric: tabular-nums;
}

.result-table th {
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--muted);
  font-weight: 600;
  padding: 4px 8px;
}

.result-table td {
  padding: 8px;
  border-top: 2px solid var(--line);
}

.result-table td:first-child {
  color: var(--muted);
  font-weight: 600;
  width: 1.5em;
}

.result-table td:nth-child(2) {
  text-align: left;
  font-weight: 600;
}

.result-table tr.win td:nth-child(2)::before {
  content: '★ ';
  color: #e5b800;
}

.result-table td:nth-child(2) .avatar {
  margin-right: 6px;
}

#result {
  position: relative;
}

/* Cheering buddies above the result title. */
.result-crew {
  display: flex;
  justify-content: center;
  gap: 6px;
  margin-bottom: 4px;
}

.result-crew .avatar {
  width: 50px;
  height: 30px;
}

.result-crew.big .avatar {
  width: 86px;
  height: 52px;
}

/* Confetti: client.js scatters a few dozen bits over the card for a win. */
.confetti {
  position: absolute;
  inset: -24px -28px auto;
  height: 100%;
  overflow: hidden;
  pointer-events: none;
  border-radius: 24px;
}

.confetti i {
  position: absolute;
  top: -12px;
  width: 8px;
  height: 12px;
  border-radius: 2px;
  opacity: 0.9;
  animation: confetti-fall 2.4s ease-in forwards;
}

@keyframes confetti-fall {
  to { transform: translateY(420px) rotate(540deg); opacity: 0; }
}

@media (prefers-reduced-motion: reduce) {
  .confetti { display: none; }
}

#result .btn {
  width: 100%;
  margin-top: 6px;
}

#rematchHint {
  margin-top: 8px;
}

.result-links {
  display: flex;
  justify-content: center;
  gap: 18px;
}

#result .btn-link {
  width: auto;
  margin-top: 4px;
}

.result-table td:nth-child(2) {
  max-width: 9em;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.result-table td {
  white-space: nowrap;
}

/* ---------- Toast ---------- */

.toast {
  position: fixed;
  left: 50%;
  bottom: 24px;
  transform: translateX(-50%);
  background: var(--ink);
  color: #fff;
  padding: 10px 16px;
  border-radius: 12px;
  z-index: 20;
  max-width: calc(100vw - 32px);
}

/* ---------- Responsive ---------- */

/* Large screens: scale the board up to use the space. */
@media (min-width: 1100px) and (min-height: 760px) {
  :root {
    --rh: clamp(26px, calc((100vh - 330px) / 12), 36px);
    --rw: calc(var(--rh) * 2.6);
    --gap: 14px;
  }
}

@media (max-width: 900px) {
  .game-grid {
    grid-template-columns: 1fr 1fr;
    grid-template-areas:
      'card opp'
      'me me';
    gap: 12px;
  }

  .card-wrap {
    grid-area: card;
  }

  /* Never taller than the challenge card beside it: the zero height keeps this
     panel from sizing the row, and min-height stretches it to the card's height. */
  .opp-side {
    grid-area: opp;
    height: 0;
    min-height: 100%;
    overflow: hidden;
  }

  /* A single opponent keeps a mini board, sized to fit beside the card. */
  .opp-list[data-count='1'] .board.mini.opp-board {
    --rh: 6px;
  }

  .opp-list[data-count='1'] .opp-stats {
    display: none;
  }

  .opp-list[data-count='1'] .progress {
    height: 6px;
  }

  .opp-list.solo .opp-stats {
    display: block;
  }

  /* Standings order: show the top 3 only. */
  .opp-list .opp:nth-child(n + 4) {
    display: none;
  }

  .my-side {
    grid-area: me;
  }

  .board.mini {
    --rh: 7px;
    --rw: min(22px, calc((50vw - 60px) / 6 - 4px));
    --gap: 3px;
  }

  .board.mini.opp-board {
    --rh: 7px;
    --rw: min(22px, calc((50vw - 60px) / 6 - 4px));
  }

  /* Phones with several opponents: a compact standings list, no boards. */
  .opp-list:not([data-count='1']) {
    gap: 6px;
  }

  .opp-list:not([data-count='1']) .opp-board,
  .opp-list:not([data-count='1']) .opp-stats {
    display: none;
  }

  .opp-list:not([data-count='1']) .opp + .opp {
    padding-top: 6px;
  }

  .opp-list:not([data-count='1']) .progress {
    height: 6px;
  }

  .opp-side > .label {
    display: none;
  }

  .target-card,
  .opp-side {
    padding: 8px;
  }

  .card-head {
    font-size: 11px;
  }

  .card-diff {
    font-size: 14px;
  }
}

@media (max-width: 520px) {
  :root {
    --rh: clamp(18px, calc((100dvh - 400px) / 12), 26px);
    --gap: 6px;
  }

  .hud {
    gap: 14px;
    padding: 8px 14px;
  }

  .hud b {
    font-size: 18px;
  }

  .opp-stats {
    font-size: 12px;
  }

  .overlay-box {
    padding: 20px 14px;
  }

  .result-table th,
  .result-table td {
    padding: 6px 4px;
    font-size: 15px;
  }
}
