/* ============================================================
   Bubble Planet — a bubble shooter in orbit
   ============================================================ */

/* The deep-space wash behind the whole page. The two ellipses give the void a
   direction — a lit nebula shoulder up on the left, a darker well down on the
   right — so the board does not sit on a flat rectangle of purple. */
body {
  background-color: #1A0A2E;
  background-image:
    radial-gradient(ellipse at 30% 20%, #2D1B69 0%, transparent 50%),
    radial-gradient(ellipse at 70% 80%, #0D0521 0%, transparent 40%);
  background-attachment: fixed;
}

#canvas-wrap {
  background: transparent;
  padding: 8px;
}

#game-canvas {
  border-radius: 10px;
  image-rendering: auto;
  box-shadow: 0 10px 44px rgba(0, 0, 0, 0.6);
  cursor: crosshair;
  touch-action: none;
}

/* Aiming is a press-and-drag held over the board, which iOS and Android both
   read as an attempt to select something: a long press raises the callout menu
   and the blue selection wash, and the drag that follows aims nothing. Nothing
   in the play area is text worth selecting, so none of it is selectable. */
canvas,
#canvas-wrap,
#bp-start,
#bp-toast {
  user-select: none;
  -webkit-user-select: none;
  -webkit-touch-callout: none;
  -webkit-tap-highlight-color: transparent;
}

/* ── Splash ── */
/* The three rows are logo / artwork / Play, and Play must survive any viewport.
   On a short window (landscape phone, small laptop, an embedded frame) the stack
   used to overflow its container and push the Play button down behind the footer
   — unreachable, with no way to start the game. The middle row is the only one
   allowed to give: the picture shrinks, so the logo and the buttons always hold
   their place. */
#bp-start {
  position: absolute;
  inset: 0;
  z-index: 60;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 18px;
  padding: 20px 24px;
  background: rgba(26, 10, 46, 0.88);
  backdrop-filter: blur(3px);
  -webkit-backdrop-filter: blur(3px);
  text-align: center;
  overflow: hidden;
}
#bp-start.bp-hide { display: none; }

#bp-start > .bp-splash-logo,
#bp-start > .bp-splash-actions { flex: 0 0 auto; }

/* The logo ships black, like every other logo here. Masking rather than
   filtering it gets the fluorescent yellow exactly, instead of chasing it
   through a hue-rotate chain that lands near-but-not-on the brand colour. */
.bp-splash-logo {
  display: block;
  width: min(300px, 70vw);
  aspect-ratio: 71.22 / 27.409;
  background-color: #E8FF00;
  -webkit-mask-image: url('../logos/bubbleplanet_logo.svg');
          mask-image: url('../logos/bubbleplanet_logo.svg');
  -webkit-mask-size: contain;
          mask-size: contain;
  -webkit-mask-repeat: no-repeat;
          mask-repeat: no-repeat;
  -webkit-mask-position: center;
          mask-position: center;
  filter: drop-shadow(0 0 18px rgba(232, 255, 0, 0.35));
}

/* The planet and its cluster, cut from the same artwork the share card carries,
   so the picture on the start screen is the one that brought the player here.
   The rules used to be printed in this slot; they have their own walkthrough
   now, and a wall of text was a poor thing to meet a game with.

   min-height:0 is what actually lets a flex item shrink below its content —
   without it the picture refuses to give and the buttons are pushed out. */
.bp-splash-art {
  display: block;
  width: min(290px, 60vw);
  max-height: 44vh;
  object-fit: contain;
  flex: 0 1 auto;
  min-height: 0;
  filter: drop-shadow(0 12px 34px rgba(124, 58, 237, 0.42));
}

/* Short windows: give the logo and the picture less room so the buttons keep
   their place rather than the stack growing past the frame. */
@media (max-height: 620px) {
  #bp-start { gap: 12px; padding: 14px 20px; }
  .bp-splash-logo { width: min(210px, 46vw); }
  .bp-splash-art { width: min(200px, 40vw); max-height: 36vh; }
  .bp-btn { padding: 10px 32px; }
}

/* Very short — a landscape phone. Both yield; the buttons are the one thing on
   this screen that cannot. */
@media (max-height: 440px) {
  #bp-start { gap: 9px; padding: 10px 18px; }
  .bp-splash-logo { width: min(158px, 34vw); }
  .bp-splash-art { width: min(132px, 26vw); max-height: 30vh; }
  .bp-btn { padding: 9px 28px; font-size: 0.88rem; }
}

/* Continue sits above New Game, both centred. Stacked rather than side by side
   so the wider "Continue (Board 3 · 4,210 pts)" label never has to compete for
   width with anything on a phone. */
.bp-splash-actions {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  flex: 0 0 auto;
}

/* New Game is dealt in by the splash only when there is a saved board to throw
   away; on a first visit there is nothing for it to do that Play does not. It
   ships with .hidden and nothing on either stylesheet ever matched that — the
   generic rule other games carry does not exist here — so the button stood on
   every fresh splash next to a Play that did the same thing. Scoped to .bp-btn
   so it outranks the button's own display rather than depending on which line
   the cascade happens to read last. */
.bp-btn.hidden { display: none; }

.bp-btn {
  background: #7C3AED;
  border: none;
  color: #fff;
  padding: 13px 40px;
  border-radius: 10px;
  font-size: 0.95rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  cursor: pointer;
  transition: background 0.12s;
  -webkit-tap-highlight-color: transparent;
}
.bp-btn:hover  { background: #8B5CF6; }
.bp-btn:active { background: #6D28D9; }

/* The secondary choice reads as secondary. Starting a new game throws away a
   run in progress, so it should not be the loudest thing on the screen. */
.bp-btn-ghost {
  background: transparent;
  border: 1px solid rgba(232, 255, 0, 0.35);
  color: rgba(232, 255, 0, 0.85);
  padding: 9px 26px;
  font-size: 0.85rem;
}
.bp-btn-ghost:hover  { background: rgba(232, 255, 0, 0.08); }
.bp-btn-ghost:active { background: rgba(232, 255, 0, 0.16); }

/* Placed after .bp-btn deliberately: both are single-class selectors, so source
   order is the only thing deciding which background wins, and the ghost has to
   come second or it renders as a second solid button. */

/* ── Mute ──────────────────────────────────────────────────────────────────
   Drawn rather than set in an emoji. A speaker glyph renders monochrome — or as
   a hollow box — on any system without a colour emoji font, which is a poor
   showing for the one control a player reaches for in a hurry. Inline SVG looks
   the same everywhere and takes the button's own colour.

   Sound on: the speaker keeps the game's yellow, waves showing. Muted: the
   waves go, a slash appears, and the whole thing dims — three signals, so the
   state is never ambiguous at 20px. */
#mute-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  min-width: 2.6em;
  color: #E8FF00;
}
#mute-btn svg {
  width: 20px;
  height: 20px;
  display: block;
}
#mute-btn .mute-slash { opacity: 0; }

#mute-btn[aria-pressed="true"] {
  color: #b6a8d8;
  opacity: 0.7;
}
#mute-btn[aria-pressed="true"] .mute-waves { opacity: 0; }
#mute-btn[aria-pressed="true"] .mute-slash { opacity: 1; }

/* ── Walkthrough ───────────────────────────────────────────────────────────
   Sits over the board like the splash does, so the first thing a new player
   sees is taught inside the game's own frame rather than in a dialog floating
   over it. Screens are stacked and cross-faded, which keeps the panel a fixed
   size — sliding a shorter screen in would make the buttons jump. */
#bp-tour {
  position: absolute;
  inset: 0;
  z-index: 90;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 14px;
  padding: 18px 20px;
  background: rgba(13, 5, 33, 0.96);
  backdrop-filter: blur(4px);
  -webkit-backdrop-filter: blur(4px);
  text-align: center;
  user-select: none;
  -webkit-user-select: none;
}
#bp-tour.hidden { display: none; }

#bp-tour-skip {
  position: absolute;
  top: 12px;
  right: 14px;
  background: none;
  border: none;
  color: #b6a8d8;
  font-family: inherit;
  font-size: 0.78rem;
  font-weight: 700;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  cursor: pointer;
  padding: 6px 8px;
  -webkit-tap-highlight-color: transparent;
}
#bp-tour-skip:hover { color: #E8FF00; }

/* The stage holds its tallest screen so nothing below it moves between steps. */
.bp-tour-stage {
  position: relative;
  width: 100%;
  max-width: 360px;
  flex: 0 1 auto;
  min-height: 0;
  display: grid;
}
.bp-tour-screen {
  grid-area: 1 / 1;
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.22s ease;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  min-height: 0;
}
.bp-tour-screen.is-on { opacity: 1; visibility: visible; }

.bp-tour-screen h3 {
  margin: 0;
  color: #E8FF00;
  font-size: 1.05rem;
  font-weight: 700;
  letter-spacing: 0.01em;
}
.bp-tour-art {
  width: 100%;
  background: rgba(26, 10, 46, 0.85);
  border: 1px solid #4c3a7a;
  border-radius: 10px;
  padding: 8px;
}
/* The diorama's frame. Fixed ratio so the slice of board each scene shows is
   the one it was composed for, whatever the panel's width. */
.bp-tour-canvas {
  display: block;
  width: 100%;
  aspect-ratio: 16 / 10;
  border-radius: 6px;
}
.bp-tour-screen p {
  margin: 0;
  color: #b6a8d8;
  font-size: 0.84rem;
  line-height: 1.55;
  max-width: 42ch;
  overflow-y: auto;
  min-height: 0;
}

.bp-tour-dots { display: flex; gap: 8px; flex: 0 0 auto; }
.bp-tour-dots button {
  width: 9px;
  height: 9px;
  padding: 0;
  border-radius: 50%;
  border: 1px solid rgba(232, 255, 0, 0.55);
  background: transparent;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.bp-tour-dots button.is-on { background: #E8FF00; }

.bp-tour-nav {
  display: flex;
  gap: 10px;
  align-items: center;
  flex: 0 0 auto;
}
.bp-tour-nav .bp-btn { padding: 10px 30px; font-size: 0.88rem; }
#bp-tour-prev.is-hidden { visibility: hidden; }

/* Short windows: the picture yields first, then the copy, so the buttons and
   dots always stay on screen. */
@media (max-height: 620px) {
  #bp-tour { gap: 10px; padding: 12px 16px; }
  .bp-tour-screen h3 { font-size: 0.95rem; }
  .bp-tour-screen p { font-size: 0.78rem; line-height: 1.45; }
  .bp-tour-art { padding: 4px; }
}
@media (max-height: 440px) {
  .bp-tour-art { display: none; }
  .bp-tour-screen p { max-width: 54ch; }
}

/* ── Sound panel ───────────────────────────────────────────────────────────
   Hangs under the header button rather than taking over the screen: it is a
   two-line adjustment, not a page, and a full modal would make turning the
   music down feel like leaving the game. */
#bp-audio-panel {
  position: fixed;
  top: 58px;
  right: 12px;
  z-index: 200;
  width: min(280px, calc(100vw - 24px));
  padding: 14px 16px;
  border-radius: 12px;
  background: rgba(26, 10, 46, 0.97);
  border: 1px solid #4c3a7a;
  box-shadow: 0 14px 40px rgba(0, 0, 0, 0.55);
  display: flex;
  flex-direction: column;
  gap: 14px;
}
#bp-audio-panel.hidden { display: none; }

.bp-audio-row {
  display: grid;
  grid-template-columns: 1fr auto;
  grid-template-areas: "head val" "slider slider";
  gap: 8px 10px;
  align-items: center;
}
.bp-audio-head {
  grid-area: head;
  display: flex;
  align-items: center;
  gap: 8px;
  color: #E8FF00;
  font-size: 0.82rem;
  font-weight: 700;
  letter-spacing: 0.03em;
  cursor: pointer;
}
.bp-audio-head input { accent-color: #7C3AED; width: 15px; height: 15px; cursor: pointer; }
.bp-audio-val {
  grid-area: val;
  color: #b6a8d8;
  font-size: 0.74rem;
  font-weight: 700;
  min-width: 3.2em;
  text-align: right;
}
.bp-audio-row input[type="range"] {
  grid-area: slider;
  width: 100%;
  accent-color: #E8FF00;
  cursor: pointer;
}

/* A row whose sound is switched off says so by fading, so the slider beneath it
   does not read as live. */
.bp-audio-row.bp-off .bp-audio-head span,
.bp-audio-row.bp-off .bp-audio-val,
.bp-audio-row.bp-off input[type="range"] { opacity: 0.4; }

/* ── Toast ── */
#bp-toast {
  position: absolute;
  left: 50%;
  bottom: 22px;
  transform: translate(-50%, 12px);
  z-index: 50;
  max-width: min(320px, 86%);
  padding: 9px 16px;
  border-radius: 999px;
  background: rgba(26, 10, 46, 0.94);
  border: 1px solid #4c3a7a;
  color: #d9cff2;
  font-size: 0.76rem;
  font-weight: 600;
  text-align: center;
  letter-spacing: 0.01em;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.22s, transform 0.22s;
}
#bp-toast.bp-toast-on {
  opacity: 1;
  transform: translate(-50%, 0);
}

/* The score is drawn large and centred on the board itself, so the stats bar
   carries only Board / Bubbles / Best. */
#val-best {
  color: #d9cff2;
  font-weight: 700;
}

/* ── End-of-run breakdown rows carry a label/value pair ── */
#modal-breakdown .bd-row span { color: #b6a8d8; }

/* The modal's own score line, matched to the bar so the number the player has
   been watching all run is the same number they land on. */
#modal-score {
  color: #E8FF00;
  font-size: 2rem;
  font-weight: 700;
  text-shadow: 0 0 18px rgba(232, 255, 0, 0.4);
}

/* Four stats do not fit a 375px phone at the shared bar's spacing — Best clips
   off the right edge. Tighten the gutters rather than drop a stat. */
@media (max-width: 460px) {
  #stats { padding: 0 10px; }
  #stats .stat-item {
    padding-right: 9px;
    margin-right: 9px;
  }
  #stats .stat-label { font-size: 0.6rem; letter-spacing: 0.04em; }
  #stats .stat-value { font-size: 0.8rem; }
}
