:root {
  --gold: #d4af37;
  --gold-light: #f4e2a1;
  --bg: #0b0b12;
  --panel: #17141c;
  --panel-2: #201a24;
  --wine: #4a1220;
  --red: #b3273a;
  --blue: #2a5fa5;
  --green: #2e7d4f;
  --text: #f1e6c8;
  --text-dim: #b9ab86;
  font-size: 16px;
}

* { box-sizing: border-box; }

body {
  margin: 0;
  min-height: 100vh;
  background: radial-gradient(ellipse at top, #201826 0%, #0b0b12 60%, #06060a 100%);
  color: var(--text);
  font-family: 'Trebuchet MS', 'Segoe UI', Georgia, serif;
  display: flex;
  justify-content: center;
  padding: 20px 12px 48px;
  /* Stops the iOS rubber-band bounce that made the whole page feel like it
     was being dragged around on touch. */
  overscroll-behavior-y: none;
}

.page {
  width: 100%;
  max-width: 700px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 14px;
}

.stage {
  position: relative;
  width: 100%;
  max-width: 640px;
  aspect-ratio: 2108 / 2475;
  /* Nothing in the game itself is a drag/swipe gesture (just taps), so this
     stops a touch on the reels from being interpreted as a page pan —
     the same "locked in place" feel the bonus modal already has by virtue
     of being a fixed full-viewport overlay. */
  touch-action: none;
}

#reelCanvas {
  width: 100%;
  height: 100%;
  display: block;
  border-radius: 10px;
}

/* Paylines/Pay Table info buttons — flank the title bar, top corners of the
   frame. Sized as a % of .stage (not fixed px) so they scale with the
   machine art at any screen size, same reasoning as the reel/console
   overlays. Each opens infoModal with its matching reference image. */
.info-icon-btn {
  position: absolute;
  top: 2.2%;
  width: 9%;
  z-index: 145;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  background: none;
  border: none;
  padding: 0;
  cursor: pointer;
}
.info-icon-left { left: 6.5%; }
.info-icon-right { right: 6.5%; }
.info-icon-btn img {
  width: 100%;
  display: block;
  filter: drop-shadow(0 2px 4px rgba(0,0,0,0.7));
  transition: transform 0.15s ease, filter 0.15s ease;
}
.info-icon-btn:hover img {
  transform: scale(1.08);
  filter: drop-shadow(0 2px 4px rgba(0,0,0,0.7)) drop-shadow(0 0 8px rgba(255,220,120,0.8));
}
.info-icon-btn span {
  font-size: 0.62rem;
  font-weight: bold;
  letter-spacing: 0.04em;
  color: var(--gold-light);
  text-shadow: 0 1px 3px #000;
}

/* Full-screen lightbox for the Paylines/Pay Table reference images — tap
   anywhere to close, same as tapping outside any other overlay here. */
.info-modal {
  position: fixed;
  inset: 0;
  z-index: 400;
  background: rgba(5,3,8,0.88);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 20px;
  cursor: pointer;
}
.info-modal img {
  max-width: 92vw;
  max-height: 92vh;
  border-radius: 8px;
  box-shadow: 0 0 40px rgba(0,0,0,0.8);
}

.overlay-banner {
  position: absolute;
  left: 50%;
  top: 20%;
  transform: translate(-50%, -50%);
  font-size: 1.6rem;
  font-weight: bold;
  color: var(--gold-light);
  text-shadow: 0 0 10px #000, 0 0 20px rgba(212,175,55,0.6);
  letter-spacing: 0.06em;
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.25s ease;
  text-align: center;
  width: 90%;
}
.overlay-banner.show { opacity: 1; }

/* Centered on the blue (Master Mason) obelisk, sized to read clearly at a
   glance during free spins — not a small status pill tucked up top. */
.fs-hud {
  position: absolute;
  top: 29%;
  left: 50%;
  transform: translate(-50%, -50%);
  z-index: 140;
  width: 64%;
  background: linear-gradient(180deg, #241a0c, #0e0a05);
  border: 1px solid var(--gold);
  color: var(--gold-light);
  padding: 9px 14px;
  border-radius: 16px;
  text-align: center;
  letter-spacing: 0.02em;
  text-shadow: 0 1px 3px #000;
  box-shadow: 0 0 16px rgba(212,175,55,0.4), 0 0 10px rgba(0,0,0,0.6);
}
#fsCount {
  font-size: 0.86rem;
  font-weight: bold;
  white-space: nowrap;
}
/* Running total for the round, distinct enough from the count line above
   that the two don't blur together, but still small — this isn't the final
   TOTAL WIN moment, just a live running figure. */
#fsCumWin {
  font-size: 0.74rem;
  font-weight: bold;
  color: #ffe9a8;
  margin-top: 3px;
  white-space: nowrap;
}
.hidden { display: none !important; }

/* Wild-reel explosion (free spins only, see runFreeSpins in math.js and
   explodeReel in main.js) — a brief burst over the whole reel column, fixed
   viewport coordinates like the bonus fireball/coin-shower, timed so the
   flash is near peak brightness right when the reel's other symbols
   actually swap to wild. */
.reel-explosion {
  position: fixed;
  pointer-events: none;
  z-index: 210;
  border-radius: 8px;
  background: radial-gradient(circle, rgba(255,230,150,0.95) 0%, rgba(255,150,30,0.85) 35%, rgba(255,70,10,0.5) 65%, rgba(255,60,0,0) 100%);
  box-shadow: 0 0 40px 14px rgba(255,140,30,0.7);
  opacity: 0;
  transform: scale(0.3);
  transition: transform 0.45s cubic-bezier(.2,.8,.3,1.4), opacity 0.3s ease;
}
.reel-explosion.burst { opacity: 1; transform: scale(1); }
.reel-explosion.fade { opacity: 0; }

/* Meter fills sit directly inside the empty oval slots already printed in
   machine1.png under each obelisk name — no separate label, the art has one.
   Bounds hand-measured against the 2108x2475 image (re-measured when the
   background moved from machine.png to machine1.png). */
.meter-bar {
  position: absolute;
  top: 51.15%;
  height: 1.70%;
  border-radius: 3px;
  overflow: visible;
  pointer-events: none;
}
.meter-red   { left: 14.56%; width: 18.55%; }
.meter-blue  { left: 41.13%; width: 18.22%; }
.meter-green { left: 67.55%; width: 18.41%; }
.meter-bar i {
  display: block;
  height: 100%;
  width: 0%;
  border-radius: 3px;
  transition: width 0.4s ease, box-shadow 0.35s ease, filter 0.35s ease;
}
.meter-red i { background: linear-gradient(90deg, #7a0f1e, var(--red)); }
.meter-blue i { background: linear-gradient(90deg, #123a66, var(--blue)); }
.meter-green i { background: linear-gradient(90deg, #12522f, var(--green)); }

/* Pulses while a meter advances, then settles back — makes progress feel dramatic. */
.meter-bar i.glowing { filter: brightness(1.35); }
.meter-red i.glowing { box-shadow: 0 0 10px 3px var(--red), 0 0 18px 7px rgba(179,39,58,0.7); }
.meter-blue i.glowing { box-shadow: 0 0 10px 3px var(--blue), 0 0 18px 7px rgba(42,95,165,0.7); }
.meter-green i.glowing { box-shadow: 0 0 10px 3px var(--green), 0 0 18px 7px rgba(46,125,79,0.7); }

/* Sustained pulse for a meter that just won its bonus — filled to 100% and
   held glowing/pulsing (not the brief settle-back above) until the player
   taps the bonus symbol and the round actually starts. Needs to read as
   unmistakably different from a bar that's merely full from normal play, so
   this swings much brighter (near-white at the peak) rather than a subtle
   brightness bump — otherwise a full-but-unarmed bar and an armed one look
   the same at a glance, which is exactly the problem this replaces. */
@keyframes meterArmedPulse {
  0%, 100% { filter: brightness(1.5) saturate(1.3); }
  50% { filter: brightness(2.6) saturate(1.7); }
}
.meter-bar i.armed { animation: meterArmedPulse 0.75s ease-in-out infinite; }
.meter-red i.armed { box-shadow: 0 0 20px 8px var(--red), 0 0 40px 16px rgba(255,85,85,0.95); }
.meter-blue i.armed { box-shadow: 0 0 20px 8px var(--blue), 0 0 40px 16px rgba(90,170,255,0.95); }
.meter-green i.armed { box-shadow: 0 0 20px 8px var(--green), 0 0 40px 16px rgba(90,255,150,0.95); }

/* Console bar — overlaid directly on the wood console area baked into
   machine1.png (below the reel window), not a separate panel. Each control
   is individually hand-positioned (percentages against this box) rather
   than flexed, so BET can sit under reel 1 and MAX BET/SPIN can share the
   "+" button's vertical center without disturbing each other. Re-measured
   against the wood panel's actual edges when the background moved from
   machine.png to machine1.png — top/bottom barely moved (the reel window's
   own vertical position didn't change), left/right did. */
.console-bar {
  position: absolute;
  left: 12%;
  right: 12%;
  top: 90.6%;
  bottom: 0.8%;
}

.stat, .bet-control {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 1px;
}
/* BET's own center — measured against GRID.left/colW in render.js — lands
   under reel 1's horizontal center. */
.bet-control {
  position: absolute;
  left: 12.15%;
  top: 50%;
  transform: translate(-50%, -50%);
}
/* Unchanged from where flex layout put it — only BET and MAX BET/SPIN were
   asked to move. */
.stat {
  position: absolute;
  left: 92.15%;
  top: 50%;
  transform: translate(-50%, -50%);
}
.stat label, .bet-control label {
  font-size: 0.6rem;
  letter-spacing: 0.08em;
  color: var(--gold-light);
  text-shadow: 0 1px 3px #000;
}
.stat-val {
  font-size: 1rem;
  font-weight: bold;
  color: var(--gold-light);
  text-shadow: 0 1px 3px #000;
}
.bet-row { display: flex; align-items: center; gap: 5px; }
.bet-row .stat-val { min-width: 40px; text-align: center; }
.bet-row button {
  width: 20px; height: 20px; border-radius: 50%;
  border: 1px solid var(--gold);
  background: rgba(20,12,4,0.75);
  color: var(--gold-light);
  cursor: pointer;
  font-size: 0.85rem;
  line-height: 1;
}
.bet-row button:hover { background: rgba(50,30,10,0.85); }

button {
  font-family: inherit;
}

.btn-spin, .btn-max {
  border: 1px solid var(--gold);
  border-radius: 6px;
  padding: 8px 10px;
  background: linear-gradient(180deg, rgba(58,44,16,0.85), rgba(20,15,6,0.85));
  color: var(--gold-light);
  font-weight: bold;
  letter-spacing: 0.03em;
  font-size: 0.72rem;
  cursor: pointer;
  transition: transform 0.08s ease, box-shadow 0.15s ease;
  white-space: nowrap;
}
/* Positioning (vertical center matching the BET +/- circle's center) only
   applies to the main console's own SPIN/MAX BET — .btn-spin is reused by
   the bonus modal's own button (.bonus-spin-btn), which must stay in normal
   flow, not inherit this absolute placement meant for the console bar. */
.console-bar .btn-max  { position: absolute; top: 59.9%; left: 39.58%; transform: translate(-50%, -50%); }
.console-bar .btn-spin { position: absolute; top: 59.9%; left: 66.31%; transform: translate(-50%, -50%); }
.btn-spin:hover:not(:disabled), .btn-max:hover:not(:disabled) {
  box-shadow: 0 0 14px rgba(212,175,55,0.55);
  transform: translateY(-1px);
}
.console-bar .btn-spin:hover:not(:disabled), .console-bar .btn-max:hover:not(:disabled) {
  transform: translate(-50%, -50%) translateY(-1px);
}
/* No transform:none override here — the console buttons rely on their
   translate(-50%,-50%) staying in place even while disabled, or they jump
   to their unTRANSLATED top-left anchor the instant SPIN is pressed. Hover
   is already excluded via :not(:disabled) above, so this was only ever
   guarding against a transform that no longer needs cancelling. */
button:disabled { opacity: 0.45; cursor: not-allowed; box-shadow: none !important; }

.btn-spin { font-size: 0.95rem; padding: 8px 18px; background: linear-gradient(180deg, rgba(90,61,16,0.9), rgba(38,25,10,0.9)); }

/* Overall-win popup — shown once the winning paylines have had their moment
   in the reel area, styled like the AUTOPLAY button (same border/gradient)
   but scaled up so it reads as the headline result. Stays up until SPIN is
   pressed again. */
.win-card {
  position: absolute;
  left: 50%;
  top: 74.6%;
  transform: translate(-50%, -50%);
  border: 2px solid var(--gold);
  border-radius: 12px;
  padding: 16px 40px;
  background: linear-gradient(180deg, rgba(58,44,16,0.94), rgba(20,15,6,0.94));
  color: var(--gold-light);
  font-weight: bold;
  font-size: 1.8rem;
  letter-spacing: 0.04em;
  text-shadow: 0 1px 3px #000;
  box-shadow: 0 0 24px rgba(212,175,55,0.5), 0 4px 18px rgba(0,0,0,0.6);
  pointer-events: none;
  white-space: nowrap;
}

/* Free-spins award/win cards — same "centered card over the reel area"
   language as .win-card (same top:74.6%, the reel window's true vertical
   center), just with a button so the player controls when the round
   starts and ends instead of it auto-playing straight through. */
.fs-award-card {
  position: absolute;
  left: 50%;
  top: 74.6%;
  transform: translate(-50%, -50%);
  z-index: 150;
  width: min(78vw, 360px);
  background: linear-gradient(180deg, #3a2c10, #1c1408);
  border: 2px solid var(--gold);
  border-radius: 14px;
  padding: 22px 22px 26px;
  text-align: center;
  box-shadow: 0 0 40px rgba(212,175,55,0.4), 0 10px 40px rgba(0,0,0,0.7);
}
.fs-award-title {
  color: var(--gold-light);
  font-weight: bold;
  font-size: 1.25rem;
  letter-spacing: 0.04em;
  text-shadow: 0 1px 3px #000;
  margin-bottom: 18px;
}
.fs-award-card .btn-spin { width: 100%; }

/* TEMP DEBUG BUTTONS — remove before shipping. Styled visibly off from the
   real UI (dashed red) so it reads as scaffolding, not a real feature. */
.debug-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 8px;
  width: 100%;
  max-width: 640px;
  margin-top: 10px;
}
.debug-btn {
  padding: 10px 8px;
  background: rgba(120,0,0,0.25);
  border: 1px dashed #ff5555;
  border-radius: 10px;
  color: #ff9d9d;
  font-size: 0.68rem;
  font-weight: bold;
  letter-spacing: 0.03em;
  line-height: 1.3;
  text-align: center;
  cursor: pointer;
}
.debug-btn:hover:not(:disabled) { background: rgba(160,0,0,0.35); }
.debug-btn:disabled { opacity: 0.4; cursor: not-allowed; }

/* Hold-and-Spin — a card popped up over the main screen (never a navigation),
   themed to match the cabinet (wood/gold) with a reel-style board so it reads
   as part of the same game rather than a generic dialog. */
.bonus-modal {
  position: fixed;
  inset: 0;
  background: rgba(5,3,8,0.78);
  backdrop-filter: blur(2px);
  display: flex;
  /* Centered, not bottom-anchored: the badge figure floats above the panel
     via position:absolute, which means it never counts toward this
     container's scrollable area — bottom-anchoring in a shorter/regular
     browser window pushed it off the top of the screen with no way to
     reach it (confirmed: modal scrollHeight stayed equal to clientHeight
     even with the figure clipped, since absolutely-positioned overflow
     isn't included in that calculation). Centering shares any overflow
     between top and bottom instead of concentrating it all at the top. */
  align-items: center;
  justify-content: center;
  overflow-y: auto;
  z-index: 50;
}
.bonus-panel {
  position: relative;
  width: min(92vw, 560px);
  background: linear-gradient(180deg, #3a2c10, #1c1408);
  border: 2px solid var(--gold);
  border-radius: 14px;
  padding: 18px 20px 22px;
  box-shadow: 0 0 40px rgba(212,175,55,0.35), 0 10px 40px rgba(0,0,0,0.7);
  text-align: center;
}

/* Which obelisk(s) were won, shown as figures instead of text, standing on
   a lodge-interior backdrop (bonusback.png) — a full-bleed header section
   at the top of the card, not a small accent. Each color gets a fixed
   third-width column (red/blue/green, left-to-right) so its figure stands
   directly above that same color's meter bar below (.bonus-meters uses the
   same left-to-right color order) — a color with no figure just leaves its
   column empty rather than the group re-centering, unlike the old
   dynamically-ordered layout this replaced. */
.bonus-badges {
  position: relative;
  /* Cancel only the panel's left/right padding so the backdrop still runs
     edge-to-edge — .bonus-sub sits above this now (see index.html), so the
     top no longer needs to cancel the panel's own top padding too. */
  margin: 0 -20px 14px;
  padding: 14px 14px 0;
  min-height: 230px;
  background:
    linear-gradient(rgba(0,0,0,0.1), rgba(0,0,0,0.4)),
    url('../assets/bonusback.png') center 28% / cover no-repeat;
  border-radius: 12px;
  pointer-events: none;
}
.bonus-badge-slot {
  position: absolute;
  top: 14px;
  bottom: 0;
  width: 33.333%;
  display: flex;
  align-items: flex-end;
  justify-content: center;
}
.bonus-badge-slot[data-color="red"]   { left: 0; }
.bonus-badge-slot[data-color="blue"]  { left: 33.333%; }
.bonus-badge-slot[data-color="green"] { left: 66.666%; }
.bonus-badges img {
  /* These figures ARE the bonus — sized as the visual focus, not a small
     accent — but capped lower than before so badges + a fully-grown 5-row
     board still has a realistic chance of fitting without scrolling. */
  height: clamp(110px, 20vh, 220px);
  width: auto;
  filter: drop-shadow(0 6px 14px rgba(0,0,0,0.6));
}
.bonus-sub {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  font-size: 0.85rem;
  text-transform: uppercase;
  color: var(--gold-light);
  font-weight: bold;
  margin-bottom: 12px;
  min-height: 1.2em;
  text-shadow: 0 1px 3px #000;
}

/* Red/blue/green obelisk progress, same behavior as the main game's own
   meter-bar (glow-then-settle bump, sustained "armed" pulse once a color is
   actually active for the round) — just re-skinned onto bar-trim.png (which
   already bakes in the three obelisk-name labels) instead of the main
   game's machine-art cutouts. bar-trim.png is bar.png with its huge
   transparent top/bottom margin (the original is 1280x426 but the actual
   art is only ~150px tall) cropped off — using the untrimmed source sized
   the container to the full 426px height, leaving a big dead gap above and
   below the visible bars. Fill-track bounds hand-measured against the
   1280x150 trimmed image, same technique as the main meter-bar's
   coordinates against machine.png. */
.bonus-meters {
  position: relative;
  width: 100%;
  aspect-ratio: 1280 / 150;
  background: url('../assets/bar-trim.png') center / contain no-repeat;
  margin: 0 0 14px;
}
.bonus-meter-track {
  position: absolute;
  /* Re-measured by sampling actual pixel colors down/across bar-trim.png
     (not eyeballing a zoomed screenshot) to find where each track's dark
     fill area truly starts/ends — the ornate end-cap scrollwork has its own
     internal light/dark oscillation that made earlier visual-crop estimates
     land short both vertically and horizontally. */
  top: 52%;
  height: 23.3%;
  border-radius: 3px;
  overflow: visible;
}
.bonus-meter-track[data-color="red"]   { left: 4.69%;  width: 24.41%; }
/* Blue's width was previously measured too wide — a blue-tinted decorative
   gem sits just past the right gold cap and got mistaken for more track
   during pixel sampling. Re-verified against the actual dark-to-gold
   transition (x=783->784 in bar-trim.png): true width is 22.38%, not
   24.69%. */
.bonus-meter-track[data-color="blue"]  { left: 38.83%; width: 22.38%; }
.bonus-meter-track[data-color="green"] { left: 71.72%; width: 23.4%; }
/* The fill doesn't run edge-to-edge vertically in the track — matching the
   main game's own meter-bar, which reads as inset with a little dark
   background showing above and below. Flush left/right, though: an inset
   there read as too big a gap. Fill percentage (0-100, set in main.js) is
   relative to this frame's own box, not the outer track. */
.bonus-meter-fill-frame {
  position: absolute;
  left: 0;
  right: 0;
  top: 18%;
  bottom: 18%;
}
.bonus-meter-track i {
  display: block;
  height: 100%;
  width: 0%;
  border-radius: 3px;
  transition: width 0.4s ease, box-shadow 0.35s ease, filter 0.35s ease;
}
.bonus-meter-track[data-color="red"] i   { background: linear-gradient(90deg, #7a0f1e, var(--red)); }
.bonus-meter-track[data-color="blue"] i  { background: linear-gradient(90deg, #123a66, var(--blue)); }
.bonus-meter-track[data-color="green"] i { background: linear-gradient(90deg, #12522f, var(--green)); }

.bonus-meter-track i.glowing { filter: brightness(1.35); }
.bonus-meter-track[data-color="red"] i.glowing   { box-shadow: 0 0 10px 3px var(--red), 0 0 18px 7px rgba(179,39,58,0.7); }
.bonus-meter-track[data-color="blue"] i.glowing  { box-shadow: 0 0 10px 3px var(--blue), 0 0 18px 7px rgba(42,95,165,0.7); }
.bonus-meter-track[data-color="green"] i.glowing { box-shadow: 0 0 10px 3px var(--green), 0 0 18px 7px rgba(46,125,79,0.7); }

/* meterArmedPulse keyframe is already defined above for the main game's
   meter-bar — reused here as-is. */
.bonus-meter-track i.armed { animation: meterArmedPulse 0.75s ease-in-out infinite; }
.bonus-meter-track[data-color="red"] i.armed   { box-shadow: 0 0 20px 8px var(--red), 0 0 40px 16px rgba(255,85,85,0.95); }
.bonus-meter-track[data-color="blue"] i.armed  { box-shadow: 0 0 20px 8px var(--blue), 0 0 40px 16px rgba(90,170,255,0.95); }
.bonus-meter-track[data-color="green"] i.armed { box-shadow: 0 0 20px 8px var(--green), 0 0 40px 16px rgba(90,255,150,0.95); }

/* Gold-trimmed dark window, echoing the main reel frame. The board itself is
   a canvas rendered by SlotRender.BonusReel — same scroll/easing/stagger
   technique as the main reels, just with a different symbol set (dimmed
   filler cards + coin faces). */
.bonus-reel-frame {
  position: relative;
  background: linear-gradient(180deg, #cfa233, #8a6a1e);
  border: 2px solid var(--gold);
  border-radius: 8px;
  padding: 4px;
  margin: 0 auto 14px;
  display: flex;
  justify-content: center;
}

/* Appears the instant COLLECT is pressed, right over the bonus board —
   disappears along with the rest of the modal a beat later (see playBonus
   in main.js), not something the player dismisses separately. */
.bonus-win-popup {
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  z-index: 30;
  width: 84%;
  background: linear-gradient(180deg, #3a2c10, #1c1408);
  border: 2px solid var(--gold);
  border-radius: 12px;
  padding: 16px 18px;
  text-align: center;
  box-shadow: 0 0 30px rgba(212,175,55,0.5), 0 6px 24px rgba(0,0,0,0.7);
  color: var(--gold-light);
  font-weight: bold;
  font-size: 1.15rem;
  letter-spacing: 0.03em;
  text-shadow: 0 1px 3px #000;
}
#bonusCanvas {
  display: block;
  max-width: 100%;
  height: auto;
  border-radius: 4px;
}

.bonus-spin-btn { width: 100%; }

/* Collect-time fireball — a coin's win shooting up to the WIN: total.
   Positioned via left/top in JS (fixed, viewport coordinates), animated by
   toggling .fly which transitions those same properties to the target. */
.bonus-fireball {
  position: fixed;
  width: 46px;
  height: 46px;
  border-radius: 50%;
  background: radial-gradient(circle, #fff6d0 0%, #ffcf5c 30%, #ff8a2b 60%, rgba(255,90,20,0) 100%);
  box-shadow: 0 0 26px 10px rgba(255,140,40,0.9), 0 0 50px 20px rgba(255,90,20,0.55);
  pointer-events: none;
  z-index: 200;
  transform: translate(-50%, -50%) scale(1);
  transition: left 0.5s cubic-bezier(.3,-0.2,.7,1.1), top 0.5s cubic-bezier(.3,-0.2,.7,1.1), opacity 0.5s ease, transform 0.5s ease;
  opacity: 1;
}
.bonus-fireball.fly { transform: translate(-50%, -50%) scale(0.4); opacity: 0.6; }

/* Red's event: the same particle, recolored — flies from ea2.png's hand to
   the targeted coin instead of from a coin up to the WIN: total. */
.bonus-fireball.red {
  background: radial-gradient(circle, #fff0e8 0%, #ff8a5c 25%, #ff2d1f 55%, rgba(180,10,0,0) 100%);
  box-shadow: 0 0 26px 10px rgba(255,60,30,0.9), 0 0 50px 20px rgba(200,20,10,0.55);
}

/* Blue's event: a cosmetic shower of coins raining from both of mm3.png's
   hands (see spawnCoinShower in main.js) — same fixed-position + transition
   technique as .bonus-fireball, not @keyframes: unregistered custom
   properties (--drift/--fall/--spin) don't interpolate smoothly inside
   @keyframes, they just snap partway through, so left/top/transform are
   transitioned directly instead, with the per-particle duration set inline
   via transition-duration. */
.bonus-coin-shower {
  position: fixed;
  pointer-events: none;
  z-index: 190;
  filter: drop-shadow(0 3px 6px rgba(0,0,0,0.55));
  transform: translate(-50%, -50%) rotate(0deg);
  transition-property: left, top, transform, opacity;
  transition-timing-function: cubic-bezier(.4,0,.8,.4), cubic-bezier(.4,0,.8,.4), linear, linear;
  opacity: 1;
}
.bonus-coin-shower.fall { opacity: 0.95; }

@media (max-width: 480px) {
  :root { font-size: 13px; }
}

/* Phones held sideways get a full-screen "please rotate" block instead of
   the game — real orientation locking (screen.orientation.lock) only works
   in fullscreen on some Android browsers and not at all on iOS, so this
   CSS block is the actual cross-platform enforcement; the JS lock attempt
   in main.js's init() is just a bonus on top where it's supported.
   max-height keeps this from ever firing on a landscape desktop/laptop. */
.rotate-lock {
  display: none;
  position: fixed;
  inset: 0;
  z-index: 9999;
  background: var(--bg);
  color: var(--gold-light);
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  gap: 18px;
  padding: 24px;
}
.rotate-lock-icon {
  font-size: 3.4rem;
  animation: rotateLockSpin 1.8s ease-in-out infinite;
}
.rotate-lock p {
  max-width: 320px;
  font-size: 1.05rem;
  margin: 0;
}
@keyframes rotateLockSpin {
  0%, 100% { transform: rotate(0deg); }
  50% { transform: rotate(-90deg); }
}
@media (orientation: landscape) and (max-height: 500px) {
  .rotate-lock { display: flex; }
}

/* TV/large-landscape-display fix: on a phone or normal PC browser window,
   capping .page/.stage at 640-700px reads as a nice compact widget. On a
   TV (always a wide landscape screen, viewed from across a room) that same
   small fixed width leaves most of the screen as empty black space around
   a tiny portrait card. min-width 1200px + landscape targets exactly that
   class of big screen without touching phone/tablet/normal-desktop-window
   sizing at all. Scales by HEIGHT (not width) since the game's own aspect
   ratio is portrait - letting height drive width via aspect-ratio (already
   set on .stage) fills the screen vertically without distortion, and since
   the source canvas is a high-resolution 2108x2475, scaling up this much
   stays sharp (nowhere near upscaling past native resolution). */
@media (orientation: landscape) and (min-width: 1200px) {
  .page {
    max-width: none;
  }
  .stage {
    width: auto;
    height: 92vh;
    max-width: none;
  }
}
