:root {
  color-scheme: light;
  --sky-top: #6bd5ff;
  --sky-mid: #f4fbff;
  --sunset: #f7cf87;
  --deep: #240733;
  --panel: #32104a;
  --panel-soft: rgba(34, 13, 45, 0.78);
  --text: #fff8df;
  --muted: #ffd9fa;
  --gold: #f5c34c;
  --gold-deep: #9a6120;
  --teal: #31d6ca;
  --coral: #f45f6f;
  --win: #fff2a1;
  font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
}

* {
  box-sizing: border-box;
}

body {
  margin: 0;
  min-height: 100vh;
  min-height: 100dvh; /* mobile: account for dynamic browser chrome */
  background:
    linear-gradient(180deg, #240733 0%, #3a0a4f 50%, #19031f 100%);
  color: var(--text);
  overflow-x: hidden;
}

body::after {
  content: "";
  position: fixed;
  inset: 0;
  z-index: 50;
  pointer-events: none;
  background: rgba(0, 0, 0, 0);
  transition: background 280ms ease;
}

/* FB⑧: heat bleeds into the viewport margins behind the game shell, so on
   wider screens the whole backdrop warms up with the held multiplier. */
body::before {
  content: "";
  position: fixed;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  opacity: 0;
  background: radial-gradient(ellipse 70% 60% at 50% 46%,
    var(--bg-heat, transparent), transparent 72%);
  transition: opacity 560ms ease;
}

body.is-bank-heat-1::before,
body.is-bank-heat-2::before,
body.is-bank-heat-3::before {
  opacity: 1;
}

body.is-bank-heat-1 { --bg-heat: rgba(196, 96, 26, 0.6); }
body.is-bank-heat-2 { --bg-heat: rgba(220, 140, 22, 0.68); }
body.is-bank-heat-3 { --bg-heat: rgba(168, 96, 210, 0.66); }

body.is-climax::after {
  background: rgba(0, 0, 0, 0.32);
  transition: background 200ms ease;
}

/* Tension warnings as remaining spins run low.
   The animation runs on body::after (fixed full-viewport overlay) so the red
   glow wraps the entire screen, not just the inner game area. */
body.is-spins-warning::after {
  animation: spinsAmberFrame 2.4s ease-in-out infinite;
}

body.is-spins-last::after {
  animation: spinsLastFrame 1.0s ease-in-out infinite;
}

@keyframes spinsAmberFrame {
  0%, 100% {
    box-shadow: inset 0 0 0 0 rgba(255, 180, 70, 0);
  }
  50% {
    box-shadow:
      inset 0 0 60px 12px rgba(255, 180, 70, 0.4),
      inset 0 0 160px 40px rgba(255, 160, 60, 0.22);
  }
}

@keyframes spinsLastFrame {
  0%, 100% {
    box-shadow: inset 0 0 0 0 rgba(255, 60, 60, 0);
  }
  50% {
    box-shadow:
      inset 0 0 90px 30px rgba(255, 60, 60, 0.62),
      inset 0 0 220px 80px rgba(255, 40, 40, 0.34);
  }
}

/* Spotlight: dim the non-active areas only.
   Active = winning / clearing / collected cells, plus the relevant HUD cards. */
body.is-focus .cell:not(.is-win):not(.is-clearing):not(.is-collected):not(.is-absorbing):not(.is-scatter-trigger):not(.is-scatter-vanishing) {
  filter: brightness(0.42) saturate(0.55);
  transition: filter 280ms ease;
}

body.is-focus .action-row,
body.is-focus .info-button,
body.is-focus .spins-strip {
  filter: brightness(0.52);
  transition: filter 280ms ease;
}

body.is-climax .cell:not(.is-scatter-trigger):not(.is-scatter-vanishing) {
  filter: brightness(0.32) saturate(0.42);
  transition: filter 200ms ease;
}

body.is-climax .action-row,
body.is-climax .info-button,
body.is-climax .calc-strip,
body.is-climax .hud-card.multiplier-card,
body.is-climax .hud-card.win-card {
  filter: brightness(0.36);
  transition: filter 200ms ease;
}

/* Multiplier spotlight: dim everything except the multiplier cells
   and the multiplier HUD card, so the collection beat is unambiguous.
   No overlay layer — pure contrast keeps the bright cells from being masked. */
body.is-multiplier-spotlight .cell:not(.is-mult):not(.is-collected):not(.is-absorbing) {
  filter: brightness(0.48) saturate(0.6);
  transition: filter 220ms ease;
}

body.is-multiplier-spotlight .cell.is-mult,
body.is-multiplier-spotlight .cell.is-collected,
body.is-multiplier-spotlight .cell.is-absorbing {
  filter: brightness(1.12) saturate(1.08);
  transition: filter 220ms ease;
  z-index: 5;
}

body.is-multiplier-spotlight .action-row,
body.is-multiplier-spotlight .info-button,
body.is-multiplier-spotlight .calc-strip,
body.is-multiplier-spotlight .hud-card.win-card,
body.is-multiplier-spotlight .spins-strip {
  filter: brightness(0.55);
  transition: filter 220ms ease;
}

.cell,
.action-row,
.info-button,
.hud-card,
.calc-strip {
  transition: filter 280ms ease;
}

button {
  font: inherit;
}

.app-shell {
  min-height: 100vh;
  min-height: 100dvh; /* mobile: account for dynamic browser chrome */
  display: grid;
  place-items: center;
  padding: 10px;
}

.game-shell {
  width: min(430px, 100%);
  min-height: min(720px, calc(100vh - 20px));
  min-height: min(720px, calc(100dvh - 20px)); /* mobile-safe height */
  display: grid;
  /* Three rows now: title image (auto) / game-screen (flex) / action row.
     The title sits at the very top of the shell, pushing everything else
     down. */
  grid-template-rows: auto minmax(0, 1fr) auto;
  overflow: hidden;
  border: 1px solid rgba(255, 236, 166, 0.62);
  border-radius: 12px;
  background:
    linear-gradient(180deg, rgba(255, 255, 255, 0.24), rgba(255, 255, 255, 0.02) 24%, rgba(38, 8, 50, 0.34) 100%),
    url("../image/background/background.png") center / cover no-repeat,
    linear-gradient(180deg, var(--sky-top) 0%, var(--sky-mid) 34%, var(--sunset) 62%, #8d63c9 100%);
  box-shadow: 0 0 40px rgba(245, 91, 182, 0.24), 0 24px 80px rgba(0, 0, 0, 0.28);
}

/* Game title artwork sitting above the play area. Source PNG is 8192×2026
   (~4:1 aspect). We let it scale by width, cap the height so it never
   dominates the play area, and disable interaction so it can't be
   dragged/selected accidentally.

   The title overlaps the top of the game-screen by a few px and skips its
   own drop-shadow — combined with removing the white top-highlight band
   from .game-screen::before in the title-present layout, the boundary
   between the title and the HUD becomes invisible. */
.game-title {
  display: block;
  width: 100%;
  height: auto;
  max-height: 96px;
  object-fit: contain;
  object-position: center;
  pointer-events: none;
  user-select: none;
  -webkit-user-drag: none;
  padding: 6px 10px 0;
  /* Pull the next row up by 8px so the title and HUD area visually merge
     instead of leaving a faint divider line. */
  margin-bottom: -8px;
  position: relative;
  z-index: 3;
  box-sizing: border-box;
}

.loading-screen {
  position: fixed;
  inset: 0;
  z-index: 100;
  display: grid;
  place-items: center;
  padding: 18px;
  background:
    radial-gradient(circle at 50% 40%, rgba(78, 27, 122, 0.78), transparent 38%),
    linear-gradient(180deg, rgba(36, 7, 51, 0.96), rgba(18, 2, 24, 0.98));
  transition: opacity 320ms ease, visibility 320ms ease;
}

.loading-screen.is-complete {
  opacity: 0;
  visibility: hidden;
}

.loading-panel {
  width: min(430px, 100%);
  display: grid;
  gap: 14px;
  padding: 16px;
  border: 1px solid rgba(255, 236, 166, 0.68);
  border-radius: 12px;
  background:
    linear-gradient(180deg, rgba(255, 255, 255, 0.1), transparent 42%),
    rgba(28, 8, 43, 0.9);
  box-shadow:
    0 0 36px rgba(245, 195, 76, 0.18),
    0 24px 70px rgba(0, 0, 0, 0.44);
}

.loading-guide {
  width: 100%;
  display: block;
  border-radius: 10px;
  box-shadow: 0 14px 28px rgba(0, 0, 0, 0.24);
}

.loading-progress {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
  gap: 10px;
}

.loading-progress-bar {
  height: 18px;
  overflow: hidden;
  border: 1px solid rgba(255, 242, 161, 0.72);
  border-radius: 999px;
  background: rgba(15, 3, 28, 0.82);
  box-shadow: inset 0 0 12px rgba(0, 0, 0, 0.42);
}

.loading-progress-bar span {
  display: block;
  width: 0%;
  height: 100%;
  border-radius: inherit;
  background:
    linear-gradient(90deg, #31d6ca, #fff2a1 48%, #f45f6f);
  box-shadow: 0 0 18px rgba(255, 242, 161, 0.48);
  transition: width 180ms ease;
}

.loading-progress strong {
  min-width: 48px;
  color: #fff2a1;
  font-size: 16px;
  font-weight: 950;
  text-align: right;
}

.loading-panel p {
  margin: 0;
  color: #fff8df;
  font-size: 13px;
  font-weight: 800;
  text-align: center;
}

.loading-screen.is-tap-ready {
  cursor: pointer;
}

.loading-screen.is-tap-ready .loading-progress,
.loading-screen.is-tap-ready #loadingStatusText {
  display: none;
}

.loading-tap {
  margin: 4px auto 0;
  padding: 12px 28px;
  border: 2px solid rgba(255, 236, 166, 0.85);
  border-radius: 999px;
  background:
    linear-gradient(180deg, rgba(255, 236, 166, 0.22), rgba(245, 195, 76, 0.08)),
    rgba(28, 8, 43, 0.6);
  color: #fff8df;
  font-family: inherit;
  font-size: 16px;
  font-weight: 900;
  letter-spacing: 0.08em;
  text-align: center;
  cursor: pointer;
  box-shadow:
    0 0 22px rgba(255, 220, 130, 0.32),
    inset 0 0 12px rgba(255, 236, 166, 0.18);
  animation: tapPulse 1.4s ease-in-out infinite;
  transition: transform 160ms ease, box-shadow 160ms ease;
}

.loading-tap:hover,
.loading-tap:focus-visible {
  outline: none;
  transform: translateY(-1px);
  box-shadow:
    0 0 28px rgba(255, 220, 130, 0.48),
    inset 0 0 14px rgba(255, 236, 166, 0.28);
}

.loading-tap:active {
  transform: translateY(1px) scale(0.98);
}

@keyframes tapPulse {
  0%, 100% {
    transform: scale(1);
    box-shadow:
      0 0 18px rgba(255, 220, 130, 0.28),
      inset 0 0 12px rgba(255, 236, 166, 0.18);
  }
  50% {
    transform: scale(1.04);
    box-shadow:
      0 0 32px rgba(255, 220, 130, 0.56),
      inset 0 0 16px rgba(255, 236, 166, 0.32);
  }
}

.game-screen {
  position: relative;
  display: grid;
  /* Two rows now: HUD (with combined win+spins card) and the board.
     Previously three rows including a standalone spins strip. */
  grid-template-rows: auto minmax(0, 1fr);
  gap: 8px;
  min-height: 0;
  overflow: hidden;
  /* Reduced bottom padding so the symbol area sits closer to the action row. */
  padding: 12px 8px 2px;
}

.game-screen::before {
  content: "";
  position: absolute;
  inset: 0;
  /* Removed the top white highlight (linear-gradient(180deg, rgba(255,255,255,0.26), transparent 19%))
     because, with the title image now sitting above the game-screen, that
     highlight created a visible "ceiling band" just below the title.
     Kept the side vignettes and bottom darkening which still help frame
     the play area. */
  background:
    linear-gradient(90deg, rgba(26, 6, 38, 0.16), transparent 12% 88%, rgba(26, 6, 38, 0.18)),
    linear-gradient(180deg, transparent 0 57%, rgba(42, 13, 71, 0.46) 100%);
  pointer-events: none;
}

/* FB⑧: high-multiplier background heat.
   Conveys rising EXCITEMENT/HEAT — deliberately NOT a danger cue.
   The remaining-spins warning uses a pulsing red INSET edge frame, so the
   heat must avoid that idiom entirely: instead of an edge border it is a
   warm bloom that radiates from the board outward, screen-blended so it
   glows (lightens) rather than tints dark. Colour runs amber -> molten
   gold -> prismatic, steering clear of alarm red. Driven by
   body.is-bank-heat-N. */
.game-screen::after {
  content: "";
  position: absolute;
  /* Oversized so the slow rotation never exposes an uncovered edge —
     game-screen's overflow:hidden clips it back to the play area. */
  inset: -52%;
  z-index: 6;
  pointer-events: none;
  opacity: 0;
  background: var(--heat-wash, transparent);
  mix-blend-mode: screen;
  transition: opacity 620ms ease;
  will-change: transform, filter;
}

body.is-bank-heat-1 .game-screen::after,
body.is-bank-heat-2 .game-screen::after,
body.is-bank-heat-3 .game-screen::after {
  opacity: 1;
  /* Slow rotation drifts the asymmetric multi-bloom field organically,
     instead of one flat blob pulsing in place. */
  animation: bankHeatFlow 26s linear infinite;
}

body.is-bank-heat-1 .game-screen::after {
  --heat-wash:
    radial-gradient(circle at 42% 44%, rgba(255, 152, 56, 0.52) 0%, transparent 17%),
    radial-gradient(circle at 59% 58%, rgba(255, 114, 38, 0.46) 0%, transparent 19%),
    radial-gradient(circle at 53% 38%, rgba(255, 182, 86, 0.36) 0%, transparent 14%),
    radial-gradient(ellipse 60% 56% at 50% 50%, rgba(255, 128, 46, 0.2) 0%, transparent 58%);
}

body.is-bank-heat-2 .game-screen::after {
  --heat-wash:
    radial-gradient(circle at 42% 44%, rgba(255, 198, 78, 0.62) 0%, transparent 17%),
    radial-gradient(circle at 59% 58%, rgba(255, 138, 36, 0.52) 0%, transparent 19%),
    radial-gradient(circle at 53% 38%, rgba(255, 224, 116, 0.42) 0%, transparent 14%),
    radial-gradient(ellipse 60% 56% at 50% 50%, rgba(255, 150, 44, 0.26) 0%, transparent 60%);
}

body.is-bank-heat-3 .game-screen::after {
  --heat-wash:
    radial-gradient(circle at 42% 44%, rgba(255, 150, 222, 0.58) 0%, transparent 17%),
    radial-gradient(circle at 59% 58%, rgba(146, 138, 255, 0.52) 0%, transparent 19%),
    radial-gradient(circle at 53% 38%, rgba(118, 212, 255, 0.46) 0%, transparent 14%),
    radial-gradient(ellipse 60% 56% at 50% 50%, rgba(200, 120, 255, 0.22) 0%, transparent 62%);
  animation: bankHeatFlowPrism 22s linear infinite;
}

/* Rotation = organic drift; the slow brightness/saturation wobble is a
   gentle breath layered on top, kept subtle so it never feels mechanical. */
@keyframes bankHeatFlow {
  0%   { transform: rotate(0deg);   filter: brightness(0.9) saturate(1.05); }
  50%  { transform: rotate(180deg); filter: brightness(1.24) saturate(1.2); }
  100% { transform: rotate(360deg); filter: brightness(0.9) saturate(1.05); }
}

@keyframes bankHeatFlowPrism {
  0%   { transform: rotate(0deg);   filter: hue-rotate(0deg) brightness(0.95) saturate(1.2); }
  50%  { transform: rotate(180deg); filter: hue-rotate(180deg) brightness(1.3) saturate(1.4); }
  100% { transform: rotate(360deg); filter: hue-rotate(360deg) brightness(0.95) saturate(1.2); }
}

.hud,
.temple-frame,
.calc-strip {
  position: relative;
  z-index: 1;
}

.hud {
  position: relative;
  display: flex;
  justify-content: center;
  align-items: center;
  min-height: clamp(60px, 16vw, 84px);
  padding: 2px 8px;
}

.spins-strip {
  position: relative;
  display: flex;
  justify-content: center;
  align-items: center;
  width: fit-content;
  margin: 10px auto 0;
  padding: 10px 28px 12px;
  border-radius: 999px;
  border: 2px solid rgba(255, 214, 102, 0.82);
  background:
    linear-gradient(180deg, rgba(255, 255, 255, 0.1), transparent 40%),
    linear-gradient(180deg, rgba(57, 12, 74, 0.92), rgba(28, 6, 44, 0.95));
  box-shadow:
    inset 0 0 0 1px rgba(255, 255, 255, 0.08),
    inset 0 -4px 10px rgba(0, 0, 0, 0.36),
    0 6px 18px rgba(47, 10, 64, 0.34);
  transition:
    border-color 240ms ease,
    box-shadow 240ms ease,
    background 240ms ease,
    filter 280ms ease;
}

.spins-strip-label {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

.spins-strip .spins-pips {
  margin-top: 0;
  justify-content: center;
  gap: 16px;
}

.spins-strip .spins-pip {
  width: 24px;
  height: 24px;
}

.hud-card {
  min-width: 0;
  display: grid;
  align-content: center;
  gap: 3px;
  border-radius: 8px;
  border: 1px solid rgba(255, 214, 102, 0.68);
  background:
    linear-gradient(180deg, rgba(255, 255, 255, 0.12), transparent 36%),
    rgba(57, 12, 74, 0.88);
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.08), 0 10px 26px rgba(47, 10, 64, 0.26);
}

.hud-card span {
  color: var(--muted);
  font-size: 11px;
  font-weight: 850;
  letter-spacing: 0;
}

/* Header row inside the frameless win-card — label is hidden (Sweet Rush
   style minimalism), so just the pip row sits centred at the top. */
.win-card-header {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  min-width: 0;
}

/* Hide the "獲得PT" text label */
.win-card-label {
  display: none;
}

/* Remaining-spins pips sat frameless and were easy to miss. A pill frame
   groups the three pips into one clearly recognisable indicator and gives
   the lit lightning icons a dark backdrop to pop against. Enlarged so the
   gauge is readable at a glance and the recovery animation reads. */
.win-card .spins-pips {
  margin-top: 0;
  gap: 7px;
  flex: 0 0 auto;
  padding: 6px 14px;
  border: 1.5px solid rgba(255, 226, 152, 0.78);
  border-radius: 999px;
  background:
    linear-gradient(180deg, rgba(64, 26, 96, 0.6), rgba(26, 8, 42, 0.78));
  box-shadow:
    inset 0 1px 3px rgba(0, 0, 0, 0.5),
    0 2px 10px rgba(0, 0, 0, 0.42),
    0 0 16px rgba(255, 206, 110, 0.26);
}

/* (Final-spin tension alert moved to .win-card itself further down,
   since the pip pill no longer has its own border/background. The
   former rule body.is-spins-last .win-card .spins-pips is intentionally
   left as a no-op to avoid affecting the now-frameless pips row.) */
body.is-spins-last .win-card .spins-pips {
  /* no-op — see body.is-spins-last .win-card below */
}

.win-card .spins-pip {
  width: 22px;
  height: 22px;
}

.hud-card strong {
  color: #ffffff;
  font-size: clamp(24px, 8vw, 40px);
  line-height: 1;
  letter-spacing: 0;
  text-shadow: 0 2px 0 rgba(57, 17, 74, 0.6), 0 0 18px rgba(255, 215, 0, 0.48);
}

.hud-card strong.is-absorbing-target {
  animation: multiplierTargetPulse 520ms ease-out;
}

/* Frameless multiplier — gem floats on the left without any card chrome
   so all the visual identity comes from the gem PNG itself.
   IMPORTANT: vertical centering is done with flex (top:0/bottom:0 +
   align-items:center) rather than `transform: translateY(-50%)`. Several
   heat-tier and kick animations apply `transform: scale(...)` which
   would otherwise wipe out the translateY and make the card visibly
   shift downward each time the multiplier accumulates. */
.multiplier-card {
  position: absolute;
  left: 4px;
  top: 0;
  bottom: 0;
  display: flex;
  align-items: center;
  padding: 0;
  background: none !important;
  border: none !important;
  box-shadow: none !important;
  outline: none !important;
  overflow: visible;
  isolation: auto;
  transition: filter 240ms ease;
}

/* Hide the "マルチプライヤー倍率" text label */
.multiplier-card > span {
  display: none;
}

.multiplier-card > span,
.multiplier-card > strong,
.multiplier-card > .multiplier-gem {
  position: relative;
  z-index: 2;
}

/* The HUD multiplier display uses dedicated gem ornament PNGs — one per
   heat tier — with transparent backgrounds. Each tier swaps the
   background-image; the bank value text overlays the central gem of
   each ornament. */
.multiplier-gem {
  position: relative;
  width: clamp(96px, 24vw, 128px);
  aspect-ratio: 512 / 373;
  margin: 0 auto;
  display: grid;
  place-items: center;
  background-image: var(--gem-image, url("../image/game/multipliers/hud_green_t.png"));
  background-size: contain;
  background-position: center;
  background-repeat: no-repeat;
  filter:
    drop-shadow(0 0 8px var(--gem-glow, rgba(120, 230, 140, 0.65)))
    drop-shadow(0 0 16px var(--gem-glow-far, rgba(120, 230, 140, 0.35)));
  transition: filter 320ms ease, transform 240ms ease;
}

/* Facet element is no longer used as a hard scrim — keep the slot for
   future polish but render nothing so the PNG art is unobstructed. */
.multiplier-gem-facet {
  display: none;
}

/* Shine element also unused for now — keep clean PNG without overlays */
.multiplier-gem-shine {
  display: none;
}

/* ---------------------------------------------------------------------------
   A案 (Gem-Fill): treats the gem's inner area as a vessel that fills with
   light. Driven by JS-set CSS variables on the .multiplier-card:
     --gem-fill-ratio:  0..1   how much of the inner gem area is filled
     --gem-fill-on:     0 or 1 master toggle (fades the whole fill on/off)

   The fill is layered light, not a literal solid bar:
     ::before  =  soft radial body from the bottom upward (blurred)
     ::after   =  bright meniscus highlight line at the top of the fill

   Position constants (14%/32%/18%/32%) match the inner gem-ellipse area
   of the four hud_*_t.png ornaments. The wings on the left/right and the
   gold frame are NOT covered so the gem art reads cleanly.

   Default values ensure the element is invisible when JS hasn't set the
   variables — so flipping FB_GEM_FILL_ENABLED off has no visual side
   effect (the JS simply never updates --gem-fill-ratio, so the fill stays
   at 0 and the meniscus stays invisible).
--------------------------------------------------------------------------- */
.multiplier-gem-fill {
  position: absolute;
  top: 14%;
  right: 32%;
  bottom: 18%;
  left: 32%;
  border-radius: 50% / 40%;
  overflow: hidden;
  pointer-events: none;
  opacity: var(--gem-fill-on, 0);
  transition: opacity 280ms ease;
  mix-blend-mode: screen;
  z-index: 1;
}

.multiplier-gem-fill::before {
  /* Light body of the fill — a soft radial that's brightest at the
     centre-bottom and fades toward the meniscus. Multi-stop gradient
     gives depth (deep / mid / bright). */
  content: "";
  position: absolute;
  left: -6%;
  right: -6%;
  bottom: 0;
  height: calc(var(--gem-fill-ratio, 0) * 100%);
  background:
    radial-gradient(ellipse 100% 130% at 50% 110%,
      var(--gem-fill-bright, rgba(255, 255, 220, 0.85)) 0%,
      var(--gem-fill-mid,    rgba(255, 230, 140, 0.55)) 38%,
      var(--gem-fill-deep,   rgba(255, 200, 80,  0.22)) 70%,
      transparent 100%);
  filter: blur(1.2px);
  transition:
    height 320ms cubic-bezier(0.3, 0.05, 0.2, 1),
    background 460ms ease;
}

.multiplier-gem-fill::after {
  /* Meniscus — thin bright line riding the top of the fill, tinted by
     tier. Fades out when fill is essentially empty. */
  content: "";
  position: absolute;
  left: 6%;
  right: 6%;
  bottom: calc(var(--gem-fill-ratio, 0) * 100% - 1.5px);
  height: 3px;
  border-radius: 999px;
  background: linear-gradient(90deg,
    transparent 0%,
    var(--gem-fill-meniscus, rgba(255, 255, 245, 0.92)) 50%,
    transparent 100%);
  filter: blur(0.5px);
  opacity: clamp(0, calc(var(--gem-fill-ratio, 0) * 5), 1);
  transition:
    bottom 320ms cubic-bezier(0.3, 0.05, 0.2, 1),
    opacity 280ms ease,
    background 460ms ease;
}

/* Per-tier fill colours — sync with the tier ornament PNGs so the inner
   light reads as the same energy as the gem itself. */
.multiplier-card.is-heat-0 {
  --gem-fill-bright:   rgba(245, 255, 220, 0.85);
  --gem-fill-mid:      rgba(190, 250, 200, 0.55);
  --gem-fill-deep:     rgba(120, 230, 140, 0.28);
  --gem-fill-meniscus: rgba(220, 255, 230, 0.95);
}
.multiplier-card.is-heat-1 {
  --gem-fill-bright:   rgba(255, 235, 210, 0.9);
  --gem-fill-mid:      rgba(255, 165, 130, 0.6);
  --gem-fill-deep:     rgba(255, 100, 80, 0.34);
  --gem-fill-meniscus: rgba(255, 235, 220, 0.96);
}
.multiplier-card.is-heat-2 {
  --gem-fill-bright:   rgba(255, 250, 220, 0.95);
  --gem-fill-mid:      rgba(255, 225, 130, 0.7);
  --gem-fill-deep:     rgba(255, 175, 60, 0.4);
  --gem-fill-meniscus: rgba(255, 250, 220, 0.98);
}
.multiplier-card.is-heat-3 {
  --gem-fill-bright:   rgba(255, 255, 255, 1);
  --gem-fill-mid:      rgba(225, 215, 255, 0.78);
  --gem-fill-deep:     rgba(190, 160, 235, 0.48);
  --gem-fill-meniscus: rgba(255, 255, 255, 1);
}

@media (prefers-reduced-motion: reduce) {
  .multiplier-gem-fill,
  .multiplier-gem-fill::before,
  .multiplier-gem-fill::after {
    transition: none;
  }
}

/* The multiplier value sits in the centre of the gem. It must visually
   dominate the PNG's embedded number without needing an ugly scrim — so
   we use a chunky font with a hard outline (multi-direction stroke) plus
   a soft outer glow. The outline makes the text crisp against the gem
   art, the glow integrates it with the tier colour. */
.multiplier-card strong.multiplier-gem-value {
  position: relative;
  z-index: 2;
  color: var(--gem-text, #ffffff);
  font-family: inherit;
  /* Base font-size is moderate; sprite digits scale to 1.5em of it. */
  font-size: clamp(15px, 4.0vw, 20px);
  font-weight: 950;
  line-height: 1;
  letter-spacing: 0.02em;
  /* Hard outline via stacked text-shadows (8-direction stroke) — fully
     overrides the PNG's embedded number which sits at the same spot. */
  text-shadow:
    /* 8-direction crisp dark outline */
    -1.5px -1.5px 0 rgba(0, 0, 0, 0.95),
     1.5px -1.5px 0 rgba(0, 0, 0, 0.95),
    -1.5px  1.5px 0 rgba(0, 0, 0, 0.95),
     1.5px  1.5px 0 rgba(0, 0, 0, 0.95),
     0    -1.5px 0 rgba(0, 0, 0, 0.95),
     0     1.5px 0 rgba(0, 0, 0, 0.95),
    -1.5px  0    0 rgba(0, 0, 0, 0.95),
     1.5px  0    0 rgba(0, 0, 0, 0.95),
    /* tier-tinted soft glow */
    0 0 8px  var(--gem-text-glow, rgba(255, 255, 255, 0.85)),
    0 0 16px var(--gem-text-glow-far, rgba(255, 255, 255, 0.55));
}

/* === Heat tier — swap to the matching pre-coloured gem ornament PNG.
   Each PNG already carries its tier colour, so no hue-rotate is needed. */

/* Tier 0 — GREEN (default, x1-x9) */
.multiplier-card.is-heat-0 {
  --gem-image: url("../image/game/multipliers/hud_green_t.png");
  --gem-glow: rgba(120, 230, 140, 0.7);
  --gem-glow-far: rgba(120, 230, 140, 0.4);
  --gem-text: #ffffff;
  --gem-text-glow: rgba(180, 255, 200, 0.78);
  --gem-text-glow-far: rgba(140, 230, 160, 0.45);
}

/* Tier 1 — RED (x10-x24) */
.multiplier-card.is-heat-1 {
  --gem-image: url("../image/game/multipliers/hud_red_t.png");
  --gem-glow: rgba(255, 90, 80, 0.85);
  --gem-glow-far: rgba(255, 90, 80, 0.5);
  --gem-text: #ffffff;
  --gem-text-glow: rgba(255, 220, 200, 0.85);
  --gem-text-glow-far: rgba(255, 180, 160, 0.45);
}

/* Tier 2 — GOLD (x25-x49) */
.multiplier-card.is-heat-2 {
  --gem-image: url("../image/game/multipliers/hud_gold_t.png");
  --gem-glow: rgba(255, 210, 80, 0.9);
  --gem-glow-far: rgba(255, 210, 80, 0.55);
  --gem-text: #fffae0;
  --gem-text-glow: rgba(255, 240, 180, 0.9);
  --gem-text-glow-far: rgba(255, 220, 130, 0.55);
}

/* Tier 3 — RAINBOW (x50+) — image already has full spectrum, we add a
   gentle pulse + glow cycle to keep the climax tier alive. */
.multiplier-card.is-heat-3 {
  --gem-image: url("../image/game/multipliers/hud_rainbow_t.png");
  --gem-glow: rgba(255, 255, 255, 0.95);
  --gem-glow-far: rgba(255, 255, 255, 0.6);
  --gem-text: #ffffff;
  --gem-text-glow: rgba(255, 255, 255, 0.95);
  --gem-text-glow-far: rgba(255, 255, 255, 0.6);
}

.multiplier-card.is-heat-3 .multiplier-gem {
  animation: gemRainbowGlow 1.6s ease-in-out infinite alternate, gemRainbowPulse 1.6s ease-in-out infinite;
}

@keyframes gemRainbowGlow {
  0%   { filter: drop-shadow(0 0 10px hsla(0,   95%, 65%, 0.85)) drop-shadow(0 0 22px hsla(0,   95%, 65%, 0.55)); }
  20%  { filter: drop-shadow(0 0 10px hsla(60,  95%, 65%, 0.85)) drop-shadow(0 0 22px hsla(60,  95%, 65%, 0.55)); }
  40%  { filter: drop-shadow(0 0 10px hsla(120, 90%, 60%, 0.85)) drop-shadow(0 0 22px hsla(120, 90%, 60%, 0.55)); }
  60%  { filter: drop-shadow(0 0 10px hsla(200, 95%, 65%, 0.85)) drop-shadow(0 0 22px hsla(200, 95%, 65%, 0.55)); }
  80%  { filter: drop-shadow(0 0 10px hsla(280, 90%, 70%, 0.85)) drop-shadow(0 0 22px hsla(280, 90%, 70%, 0.55)); }
  100% { filter: drop-shadow(0 0 10px hsla(330, 95%, 65%, 0.85)) drop-shadow(0 0 22px hsla(330, 95%, 65%, 0.55)); }
}

@keyframes gemRainbowPulse {
  0%, 100% { transform: scale(1); }
  50%      { transform: scale(1.04); }
}

/* Pulse the gem in time with heat-pulse */
.multiplier-card.is-heat-pulse .multiplier-gem {
  animation: gemHeatPulse 720ms cubic-bezier(0.16, 0.86, 0.24, 1);
}

/* Rainbow tier keeps its idle glow cycle + pulse even during heat-pulse */
.multiplier-card.is-heat-3.is-heat-pulse .multiplier-gem {
  animation:
    gemRainbowGlow 1.6s ease-in-out infinite alternate,
    gemHeatPulse 720ms cubic-bezier(0.16, 0.86, 0.24, 1);
}

@keyframes gemHeatPulse {
  0%   { transform: scale(1); filter: brightness(1); }
  20%  { transform: scale(1.55); filter: brightness(1.8) drop-shadow(0 0 14px rgba(255, 238, 140, 0.95)); }
  46%  { transform: scale(0.9); filter: brightness(1.25); }
  72%  { transform: scale(1.08); filter: brightness(1.05); }
  100% { transform: scale(1); filter: brightness(1); }
}

/* FB①: tier-crossing celebration. When the bank tier rises (x10 / x25 /
   x50), the card gets `is-tier-up` for ~1.1s on top of the usual heat-pulse,
   spawning a gold flash-burst halo around the gem. */
.multiplier-card.is-tier-up::after {
  content: "" !important;
  position: absolute;
  inset: -24% !important;
  border-radius: 50% !important;
  background: radial-gradient(circle at 50% 50%,
    rgba(255, 255, 255, 0.95) 0%,
    rgba(255, 234, 140, 0.62) 24%,
    rgba(255, 184, 80, 0.22) 52%,
    transparent 76%) !important;
  box-shadow:
    0 0 22px rgba(255, 232, 140, 0.85),
    0 0 48px rgba(255, 196, 100, 0.5) !important;
  pointer-events: none !important;
  z-index: -1 !important;
  transform: none !important;
  mix-blend-mode: screen !important;
  animation: gemTierUpBurst 1000ms cubic-bezier(0.16, 0.86, 0.24, 1) forwards !important;
}

@keyframes gemTierUpBurst {
  0%   { opacity: 0; transform: scale(0.35); }
  18%  { opacity: 1; transform: scale(1.04); filter: brightness(1.3); }
  100% { opacity: 0; transform: scale(1.7); filter: brightness(1); }
}

/* Card-level decorative auras are disabled — the gem PNG carries its
   own glow & aura via filter on .multiplier-gem. */
.multiplier-card::before,
.multiplier-card::after {
  content: none;
}

.multiplier-card::before {
  inset: -18%;
  background:
    radial-gradient(ellipse at 50% 52%, rgba(91, 221, 255, 0.38), transparent 54%),
    radial-gradient(circle at 21% 34%, rgba(255, 242, 158, 0.2), transparent 18%),
    radial-gradient(circle at 78% 68%, rgba(112, 232, 255, 0.22), transparent 20%);
  filter: blur(9px);
  animation: multiplierAuraDrift 4.8s ease-in-out infinite alternate;
}

.multiplier-card::after {
  inset: 6px;
  border-radius: 6px;
  background:
    linear-gradient(90deg, transparent 0 9%, rgba(120, 231, 255, 0.12) 24%, rgba(155, 242, 255, 0.78) 46%, rgba(255, 236, 126, 0.62) 54%, rgba(120, 231, 255, 0.1) 72%, transparent 90%) 50% 3px / 115% 2px no-repeat,
    linear-gradient(90deg, transparent 0 14%, rgba(255, 236, 126, 0.48) 38%, rgba(135, 235, 255, 0.72) 52%, transparent 86%) 50% calc(100% - 3px) / 112% 2px no-repeat,
    radial-gradient(circle at 18% 30%, rgba(180, 246, 255, 0.92) 0 1px, transparent 2px),
    radial-gradient(circle at 82% 68%, rgba(255, 239, 151, 0.82) 0 1px, transparent 2px);
  mix-blend-mode: screen;
  transform: translateX(-18%);
  animation: multiplierSparkDrift 2.8s ease-in-out infinite;
}

/* Card-level border/glow per heat tier — wraps the gem with an ambient
   environment that matches its colour. The gem itself carries the main
   colour identity; these rules add a subtle frame echo. */

/* Tier-specific card chrome is fully suppressed — the gem PNG itself
   carries the tier identity (image + filter glow). Per-tier scale
   pulses keep the card feeling hotter at higher tiers without adding
   any visible frame. */
.multiplier-card.is-heat-0,
.multiplier-card.is-heat-1,
.multiplier-card.is-heat-2,
.multiplier-card.is-heat-3 {
  background: none !important;
  border: none !important;
  box-shadow: none !important;
  outline: none !important;
}

/* Tier idle pulse — heartbeat only. The old multiplierHeatInferno carried
   small 1〜2px translate jitter that read as nervous shaking; we keep the
   scale/brightness beat (鼓動) but no positional motion. */
.multiplier-card.is-heat-1 { animation: multiplierHeatPulseFast 2.4s ease-in-out infinite; }
.multiplier-card.is-heat-2 { animation: multiplierHeatBeat 1.7s ease-in-out infinite; }
.multiplier-card.is-heat-3 { animation: multiplierHeatBeat 1.2s ease-in-out infinite; }

.multiplier-card.is-absorbing,
.multiplier-card.is-heat-pulse,
.multiplier-card.is-formula-bolt {
  animation: multiplierCardKick 620ms cubic-bezier(0.16, 0.86, 0.24, 1) both;
}

.multiplier-card.is-absorbing::after,
.multiplier-card.is-heat-pulse::after,
.multiplier-card.is-formula-bolt::after {
  opacity: 0.92;
  animation: multiplierLightningFlash 620ms cubic-bezier(0.16, 0.86, 0.24, 1) both;
}

.win-readout {
  min-width: 0;
  position: relative;
  /* min-height は最小限。数字のレイアウト高さ（1.85em）がそのまま読み出されるため、
     この値より大きい場合は数字側で決まる。 */
  min-height: 0;
  display: grid;
  place-items: center;
  /* padding-right: 20px was reserving space for the absolute .win-unit
     "PT" label, which has been removed from the HUD. Number now centres
     properly without the reserved gutter. */
  overflow: visible;
}

.win-readout #winValue {
  min-width: 0;
  max-width: 100%;
  /* ベース font-size を大幅に縮小（72 → 30）。スプライト digit は 1.85em で
     30 × 1.85 ≈ 56px の見た目高さ。コンパクトな HUD カード仕様に追従するため
     のサイズダウン。 */
  font-size: clamp(22px, 6.0vw, 30px);
  text-align: center;
  white-space: nowrap;
  line-height: 1;
}

/* Digit-count adaptive sizing so longer wins still fit in the narrow
   win-card. is-long covers 5-6 digit values (still showing big), and
   is-very-long takes over at 7+ digits. */
.win-readout #winValue.is-long {
  font-size: clamp(14px, 3.8vw, 18px);
}
.win-readout #winValue.is-very-long {
  font-size: clamp(9px, 2.4vw, 13px);
}

.win-unit {
  position: absolute;
  right: 1px;
  bottom: 1px;
  color: #fff2a1;
  font-size: clamp(9px, 2.2vw, 13px);
  font-weight: 950;
  line-height: 1;
  text-shadow:
    0 1px 0 rgba(57, 17, 74, 0.7),
    0 0 10px rgba(255, 215, 0, 0.46);
}

.win-add-badge {
  position: absolute;
  right: 0;
  top: -7px;
  padding: 2px 7px 4px;
  border: 1px solid rgba(255, 242, 161, 0.88);
  border-radius: 999px;
  background:
    radial-gradient(circle at 50% 25%, rgba(255, 255, 255, 0.36), transparent 48%),
    linear-gradient(180deg, rgba(70, 184, 236, 0.88), rgba(26, 75, 188, 0.92));
  box-shadow:
    inset 0 0 0 1px rgba(255, 255, 255, 0.14),
    0 0 14px rgba(87, 204, 255, 0.5),
    0 5px 10px rgba(18, 4, 39, 0.28);
  color: #ffffff;
  font-size: clamp(11px, 3vw, 16px);
  font-style: normal;
  font-weight: 950;
  line-height: 1;
  text-shadow:
    0 1px 0 rgba(19, 45, 125, 0.78),
    0 0 12px rgba(255, 242, 161, 0.78);
  white-space: nowrap;
  transform-origin: left center;
}

.win-add-badge[hidden] {
  display: none;
}

.win-add-badge.is-active {
  animation: winAddBadgePop 980ms cubic-bezier(0.16, 0.86, 0.24, 1) both;
}

.win-card.is-counting #winValue {
  animation: winValueCountPulse 720ms cubic-bezier(0.16, 0.86, 0.24, 1) both;
}

/* Framed win+pips card at HUD centre — the same dark-gradient + gold
   border pill that previously wrapped only the spin pips is now extended
   to wrap the 獲得PT readout too, so the PT value pops against a dark
   backdrop (it was hard to read on the bright temple background). */
.win-card {
  /* The multiplier-card is absolutely positioned at left:4px and is not
     a flex item, so flex centering would put the win-card on top of it.
     Anchor win-card to the right side instead so the two cards are
     physically separated, then constrain its width so it can't grow into
     the multiplier-card region.

     Geometry: shell max 430px → HUD inner width ≈ 414px. Multiplier-card
     (with gem) extends to roughly left: 4 + 132 = 136. Leave 8px gap →
     win-card occupies right: 4 → left: 144. Available width ≈ 414 - 144 = 270. */
  position: absolute;
  top: 50%;
  /* Shifted left by setting right offset to 30px (was 4) — pulls the
     win-card inward from the HUD right edge, reducing the visual gap
     between the two cards. */
  right: 30px;
  transform: translateY(-50%);
  /* Narrowed to 220 max so the multiplier-card on the left has a
     comfortable visual buffer (its drop-shadow halo extends beyond the
     132px gem bounding box). */
  width: clamp(160px, calc(100vw - 200px), 220px);
  /* 縦幅は「ピップ＋数字レイアウト」にぴったり追従させる。
     - min-height はピップ+数字が確実に収まる最小値
     - padding 上 3px：ピップが枠上端に張り付かない最小マージン
     - padding 下 2px：枠下端と数字の間も詰める
     - gap 2px：ピップと数字の間にわずかな視覚的距離を入れる
     - align-content: center：内容を縦中央に配置 */
  min-height: 0;
  padding: 3px 12px 2px;
  display: grid;
  gap: 2px;
  align-content: center;
  justify-items: center;
  border: 1.5px solid rgba(255, 226, 152, 0.78);
  border-radius: 18px;
  background:
    linear-gradient(180deg, rgba(64, 26, 96, 0.6), rgba(26, 8, 42, 0.78));
  box-shadow:
    inset 0 1px 3px rgba(0, 0, 0, 0.5),
    0 2px 10px rgba(0, 0, 0, 0.42),
    0 0 16px rgba(255, 206, 110, 0.26);
  box-sizing: border-box;
}

/* The pip row no longer needs its own pill — the win-card frame above
   provides the container. Strip the previous dedicated background /
   border / shadow so the pips sit naked inside the unified frame. */
.win-card .spins-pips {
  background: none;
  border: none;
  box-shadow: none;
  /* 縦パディングを 2px → 0 にして、カードの縦幅をさらに圧縮 */
  padding: 0;
}

/* When the player is on the final spin, the WHOLE win-card flashes red
   (previously only the spin-pip pill did). Echoes the new unified card. */
body.is-spins-last .win-card {
  border-color: rgba(255, 96, 74, 0.92);
  box-shadow:
    inset 0 1px 3px rgba(0, 0, 0, 0.45),
    0 2px 9px rgba(0, 0, 0, 0.4),
    0 0 16px rgba(255, 78, 54, 0.6);
}

/* Multiplier card chrome already stripped above; keep its sizing tidy
   without inheriting the shared chrome. */
.multiplier-card {
  min-height: 72px;
  padding: 0;
}

.spins-card {
  min-height: 72px;
  padding: 12px 14px;
}

.spins-card strong {
  color: #fff2a1;
}

.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

.spins-pips {
  display: flex;
  gap: 6px;
  justify-content: center;
  align-items: center;
  margin-top: 2px;
}

.spins-pip {
  position: relative;
  width: 14px;
  height: 14px;
  border-radius: 0;
  background: none;
  border: 0;
  box-shadow: none;
  /* ⑥: golden 3D lightning bolt sprite — matches THUNDERHEART branding.
     Sourced from image/game/symbols/thunder.png; the asset is taller
     than wide (~0.7:1) so background-size: contain keeps the bolt
     visible at its full vertical extent inside the square pip box. */
  background-image: url("../image/game/symbols/thunder.png");
  background-size: contain;
  background-position: center;
  background-repeat: no-repeat;
  filter: saturate(0.05) brightness(0.45) contrast(0.9);
  transition:
    transform 280ms cubic-bezier(0.16, 0.86, 0.24, 1),
    filter 280ms ease;
}

.spins-pip.is-lit {
  filter:
    saturate(1.1) brightness(1.05)
    drop-shadow(0 0 4px rgba(255, 200, 80, 0.85))
    drop-shadow(0 0 10px rgba(255, 200, 80, 0.45));
}

.spins-pip.is-just-recovered {
  animation: spinsPipRecover 720ms cubic-bezier(0.16, 0.86, 0.24, 1);
}

.spins-pip.is-just-drained {
  animation: spinsPipDrain 380ms cubic-bezier(0.32, 0, 0.66, 1);
}

@keyframes spinsPipRecover {
  /* box-shadow was being drawn against the pip's rectangular bounding
     box, producing a visible square halo around the transparent PNG.
     Switched to filter: drop-shadow which respects the alpha channel
     and hugs the bolt silhouette. */
  0% {
    transform: scale(0.38) rotate(-12deg);
    opacity: 0.3;
    filter:
      brightness(3.2)
      drop-shadow(0 0 6px rgba(255, 240, 150, 1))
      drop-shadow(0 0 12px rgba(255, 200, 80, 0.9));
  }
  46% {
    transform: scale(1.6) rotate(6deg);
    opacity: 1;
    filter:
      brightness(1.95)
      drop-shadow(0 0 8px rgba(255, 244, 160, 1))
      drop-shadow(0 0 16px rgba(255, 200, 80, 0.78));
  }
  76% {
    transform: scale(0.92) rotate(0deg);
    opacity: 1;
    filter:
      brightness(1.35)
      drop-shadow(0 0 5px rgba(255, 240, 150, 0.6));
  }
  100% {
    transform: scale(1) rotate(0deg);
    opacity: 1;
    filter: brightness(1);
  }
}

@keyframes spinsPipDrain {
  0% {
    transform: scale(1);
    filter: brightness(1);
  }
  40% {
    transform: scale(1.18);
    filter: brightness(2.2);
  }
  100% {
    transform: scale(0.86);
    filter: brightness(0.4);
  }
}

/* When tension warning kicks in, recolor lit pips */
body.is-spins-warning .spins-pip.is-lit {
  filter:
    saturate(1.3) brightness(1.0) hue-rotate(-18deg)
    drop-shadow(0 0 5px rgba(255, 170, 70, 0.9))
    drop-shadow(0 0 12px rgba(255, 170, 70, 0.5));
}

body.is-spins-last .spins-pip.is-lit {
  filter:
    saturate(1.5) brightness(0.95) hue-rotate(-50deg)
    drop-shadow(0 0 6px rgba(255, 70, 50, 0.95))
    drop-shadow(0 0 14px rgba(255, 70, 50, 0.6));
}

.temple-frame {
  align-self: start;
  margin-top: 4px;
  /* Tightened so the bottom of the symbol area sits closer to the buttons. */
  padding: 24px 4px 6px;
}

.temple-frame::before {
  content: "";
  position: absolute;
  left: 12%;
  right: 12%;
  top: 4px;
  height: 18px;
  clip-path: polygon(12% 0, 88% 0, 100% 100%, 0 100%);
  background:
    linear-gradient(180deg, rgba(255, 255, 255, 0.86), transparent 42%),
    linear-gradient(180deg, #fff0ac, #d99d32 72%, #8f581f);
  box-shadow: 0 6px 0 rgba(103, 61, 26, 0.25);
}

.temple-frame::after {
  content: "";
  position: absolute;
  left: 18px;
  right: 18px;
  bottom: 2px;
  height: 10px;
  border-radius: 6px;
  background: linear-gradient(90deg, #916028, #fff0b2 18%, #d69f3a 50%, #fff0b2 82%, #916028);
  opacity: 0.82;
}

.board {
  position: relative;
  aspect-ratio: 6 / 5.7;
  display: grid;
  grid-template-columns: repeat(6, 1fr);
  grid-template-rows: repeat(5, 1fr);
  gap: 0;
  overflow: hidden;
  contain: layout paint;
  isolation: isolate;
  border: 4px solid #d9a13a;
  border-radius: 8px;
  background:
    linear-gradient(90deg,
      transparent 0,
      transparent calc(16.6667% - 1px),
      rgba(255, 217, 120, 0.28) calc(16.6667% - 1px),
      rgba(255, 217, 120, 0.28) 16.6667%,
      transparent 16.6667%,
      transparent calc(33.3333% - 1px),
      rgba(255, 217, 120, 0.28) calc(33.3333% - 1px),
      rgba(255, 217, 120, 0.28) 33.3333%,
      transparent 33.3333%,
      transparent calc(50% - 1px),
      rgba(255, 217, 120, 0.28) calc(50% - 1px),
      rgba(255, 217, 120, 0.28) 50%,
      transparent 50%,
      transparent calc(66.6667% - 1px),
      rgba(255, 217, 120, 0.28) calc(66.6667% - 1px),
      rgba(255, 217, 120, 0.28) 66.6667%,
      transparent 66.6667%,
      transparent calc(83.3333% - 1px),
      rgba(255, 217, 120, 0.28) calc(83.3333% - 1px),
      rgba(255, 217, 120, 0.28) 83.3333%,
      transparent 83.3333%,
      transparent 100%),
    linear-gradient(180deg, rgba(255, 255, 255, 0.1), transparent 36%, rgba(0, 0, 0, 0.28)),
    linear-gradient(180deg, #5b2766, #38184c 56%, #240733);
  background-size: 100% 100%, auto, auto;
  background-repeat: no-repeat, no-repeat, no-repeat;
  box-shadow:
    inset 0 0 0 2px rgba(255, 247, 193, 0.38),
    inset 0 16px 34px rgba(255, 255, 255, 0.08),
    inset 0 -18px 36px rgba(0, 0, 0, 0.34),
    0 18px 26px rgba(47, 12, 76, 0.24);
  transform: translateZ(0);
}

.calc-strip {
  min-height: 42px;
  display: grid;
  place-items: center;
  margin: 0 8px;
  padding: 7px 12px 8px;
  overflow: hidden;
  border: 1px solid rgba(255, 214, 102, 0.68);
  border-radius: 8px;
  background:
    linear-gradient(180deg, rgba(255, 255, 255, 0.12), transparent 42%),
    rgba(39, 9, 56, 0.82);
  box-shadow:
    inset 0 0 0 1px rgba(255, 255, 255, 0.08),
    0 8px 18px rgba(47, 10, 64, 0.2);
}

.calc-strip::before {
  content: "";
  position: absolute;
  inset: 8px 12px;
  z-index: 0;
  pointer-events: none;
  border-radius: 7px;
  background:
    linear-gradient(90deg, transparent 0 16%, rgba(122, 232, 255, 0.08) 28%, rgba(138, 237, 255, 0.68) 44%, rgba(255, 235, 126, 0.56) 53%, transparent 78%) 50% 58% / 118% 2px no-repeat,
    radial-gradient(ellipse at 50% 52%, rgba(82, 213, 255, 0.2), transparent 62%);
  opacity: 0;
  transform: translateX(-52%);
}

.calc-strip #calcFormula {
  position: relative;
  z-index: 1;
  max-width: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  color: #fff8df;
  font-weight: 900;
  line-height: 1.2;
  text-align: center;
  text-shadow: 0 2px 0 rgba(57, 17, 74, 0.62), 0 0 14px rgba(255, 215, 0, 0.36);
  white-space: nowrap;
  overflow: hidden;
}

.formula-term {
  min-width: 0;
  display: inline-grid;
  grid-template-rows: auto auto;
  gap: 1px;
}

.formula-label {
  color: rgba(255, 217, 250, 0.82);
  font-size: 10px;
  font-weight: 800;
  line-height: 1;
}

.formula-value {
  color: #fff8df;
  font-size: 15px;
  font-weight: 950;
  line-height: 1;
}

.formula-term.is-subtotal {
  min-width: 86px;
  padding: 3px 9px 5px;
  border: 1px solid rgba(255, 242, 161, 0.92);
  border-radius: 7px;
  background:
    radial-gradient(circle at 50% 20%, rgba(255, 255, 255, 0.34), transparent 46%),
    linear-gradient(180deg, rgba(47, 158, 217, 0.78), rgba(30, 73, 173, 0.88));
  box-shadow:
    inset 0 0 0 1px rgba(255, 255, 255, 0.16),
    0 0 15px rgba(87, 204, 255, 0.42),
    0 5px 12px rgba(21, 6, 43, 0.28);
}

.formula-term.is-subtotal .formula-label {
  color: #fff3aa;
}

.formula-term.is-subtotal .formula-value {
  color: #ffffff;
  font-size: 17px;
  text-shadow:
    0 2px 0 rgba(27, 40, 122, 0.74),
    0 0 14px rgba(255, 242, 161, 0.76);
}

.formula-term.is-multiplier {
  padding: 1px 5px 2px;
  border-radius: 5px;
}

.calc-strip.is-active .formula-term.is-multiplier {
  color: #e7fbff;
}

.calc-strip.is-multiplier-bolt::before {
  animation: multiplierBoltSweep 820ms cubic-bezier(0.16, 0.86, 0.24, 1) both;
}

.calc-strip.is-multiplier-bolt .formula-term.is-multiplier {
  animation: formulaMultiplierZap 760ms cubic-bezier(0.16, 0.86, 0.24, 1) both;
}

.formula-operator {
  color: #fff2a1;
  font-size: 17px;
  font-weight: 950;
  line-height: 1;
}

.calc-strip.is-active {
  border-color: rgba(255, 242, 161, 0.92);
  box-shadow:
    inset 0 0 0 1px rgba(255, 255, 255, 0.1),
    0 0 22px rgba(255, 226, 90, 0.32),
    0 8px 18px rgba(47, 10, 64, 0.2);
}

.calc-strip.is-active #calcFormula {
  color: #ffffff;
}

.calc-strip.is-active .formula-value {
  color: #ffffff;
  text-shadow:
    0 2px 0 rgba(57, 17, 74, 0.72),
    0 0 14px rgba(255, 242, 161, 0.48);
}

.calc-strip.is-focus-pulse {
  animation: formulaStripFocus 780ms cubic-bezier(0.16, 0.86, 0.24, 1) both;
}

.calc-strip.is-focus-pulse .formula-term.is-subtotal {
  animation: formulaSubtotalPop 780ms cubic-bezier(0.16, 0.86, 0.24, 1) both;
}

/* --- Staged formula reveal ------------------------------------------------ */
.calc-strip.is-revealing .formula-term,
.calc-strip.is-revealing .formula-operator {
  opacity: 0.18;
  filter: saturate(0.55) brightness(0.7);
  transform: translateY(3px) scale(0.96);
  transition:
    opacity 220ms ease,
    filter 240ms ease,
    transform 260ms cubic-bezier(0.16, 0.86, 0.24, 1);
}

.calc-strip.is-revealing .formula-term.is-revealed,
.calc-strip.is-revealing .formula-operator.is-revealed {
  opacity: 1;
  filter: none;
  transform: none;
}

.calc-strip.is-revealing .formula-term[data-reveal-step="1"].is-revealed {
  animation: formulaBaseReveal 420ms cubic-bezier(0.16, 0.86, 0.24, 1) both;
}

.calc-strip.is-revealing .formula-term.is-multiplier.is-revealed {
  animation: formulaMultiplierReveal 460ms cubic-bezier(0.16, 0.86, 0.24, 1) both;
}

.calc-strip.is-revealing .formula-term.is-subtotal.is-revealed {
  animation: formulaSubtotalReveal 540ms cubic-bezier(0.16, 0.86, 0.24, 1) both;
}

.cell {
  position: relative;
  display: grid;
  place-items: center;
  min-width: 0;
  min-height: 0;
  overflow: visible;
  border: 0;
  background: transparent;
  color: #ffffff;
  font-size: clamp(20px, 8.8vw, 38px);
  font-weight: 950;
  text-shadow: 0 2px 0 rgba(60, 28, 76, 0.48), 0 0 12px rgba(255, 255, 255, 0.34);
  transform-origin: center bottom;
  backface-visibility: hidden;
  contain: layout paint style;
  transform: translate3d(0, 0, 0);
}

.cell::before {
  content: "";
  position: absolute;
  inset: 15% 18%;
  clip-path: polygon(50% 0, 86% 22%, 100% 58%, 50% 100%, 0 58%, 14% 22%);
  border-radius: 9px;
  background:
    linear-gradient(145deg, rgba(255, 255, 255, 0.94), transparent 22%),
    radial-gradient(circle at 50% 38%, rgba(255, 255, 255, 0.4), transparent 32%),
    linear-gradient(145deg, var(--tone), rgba(48, 24, 110, 0.36));
  box-shadow:
    0 0 0 2px rgba(241, 174, 58, 0.84),
    inset 0 -8px 14px rgba(0, 0, 0, 0.2),
    0 7px 13px rgba(10, 3, 30, 0.34);
}

.cell::after {
  content: "";
  position: absolute;
  top: 18%;
  left: 26%;
  width: 28%;
  height: 15%;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.48);
  transform: rotate(-22deg);
}

.cell span {
  position: relative;
  z-index: 1;
  line-height: 1;
  max-width: 94%;
  font-family: Georgia, "Times New Roman", serif;
}

.symbol-crown::before {
  clip-path: polygon(8% 76%, 16% 34%, 34% 58%, 50% 12%, 66% 58%, 84% 34%, 92% 76%, 86% 92%, 14% 92%);
}

/* Visual pay-tier hierarchy via cell-art size:
   - crown  : 1.16× (premium apex)
   - yellow : 1.00× (default size — sits below crown)
   - others : 0.80× (smaller, signalling lower pay tier)
   Applied UNCONDITIONALLY (no :not() guards) so the relative sizes hold
   throughout drops, clears, and spins. The parent .cell drives those
   state animations on its own transform; these child transforms compose
   safely on top. */
.cell.symbol-crown .cell-art {
  transform: translateZ(0) scale(1.16);
}

.cell.symbol-red .cell-art,
.cell.symbol-green .cell-art,
.cell.symbol-blue .cell-art,
.cell.symbol-white .cell-art {
  transform: translateZ(0) scale(0.8);
}

/* Rock — the non-paying "blocker" filler. Sized smaller than tier
   symbols so it visually recedes (it's not a winning element). Also
   slightly desaturated to avoid competing with paying symbols. */
.cell.symbol-scatter .cell-art {
  transform: translateZ(0) scale(0.78);
  filter: saturate(0.78) brightness(0.92);
}

/* ============================================================ */
/* CROWN GOLD GLOW — REMOVABLE BLOCK                            */
/* To disable: comment out or delete the entire block below.    */
/* Adds a soft circular golden halo behind the crown image so   */
/* the apex tier visually shines on the board.                  */
/* ============================================================ */
.cell.has-art.symbol-crown::before {
  inset: 4%;
  clip-path: none;
  border-radius: 50%;
  background: radial-gradient(
    circle at 50% 50%,
    rgba(255, 240, 150, 0.78) 0%,
    rgba(255, 215, 90, 0.55) 28%,
    rgba(255, 190, 55, 0.28) 55%,
    rgba(255, 170, 40, 0.10) 78%,
    transparent 100%
  );
  box-shadow: none;
  filter: blur(2px);
  opacity: 0.9;
  animation: crownGoldGlow 2.4s ease-in-out infinite;
  z-index: 0;
}

@keyframes crownGoldGlow {
  0%, 100% {
    opacity: 0.78;
    transform: scale(1);
    filter: blur(2px) brightness(1);
  }
  50% {
    opacity: 1;
    transform: scale(1.08);
    filter: blur(2.5px) brightness(1.18);
  }
}
/* ============================================================ */
/* END CROWN GOLD GLOW                                          */
/* ============================================================ */

.symbol-sun::before {
  clip-path: polygon(16% 20%, 84% 20%, 98% 50%, 84% 80%, 16% 80%, 2% 50%);
}

.symbol-chalice::before,
.symbol-drop::before {
  clip-path: polygon(50% 1%, 94% 50%, 50% 99%, 6% 50%);
}

.symbol-lyre::before,
.symbol-gem::before {
  clip-path: polygon(50% 0, 96% 92%, 4% 92%);
}

.symbol-leaf::before {
  clip-path: polygon(50% 0, 98% 38%, 76% 100%, 24% 100%, 2% 38%);
}

.symbol-stone::before,
.symbol-spark::before {
  clip-path: polygon(26% 6%, 74% 6%, 96% 50%, 74% 94%, 26% 94%, 4% 50%);
}

.cell.has-art::before {
  inset: 10% 8%;
  clip-path: none;
  border-radius: 999px;
  background: none;
  box-shadow: none;
  transform: none;
}

.cell.has-art::after {
  content: none;
}

.cell-art {
  position: absolute;
  inset: 2%;
  z-index: 1;
  display: block;
  width: 96%;
  height: 96%;
  border: 0;
  border-radius: 0;
  object-fit: contain;
  pointer-events: none;
  backface-visibility: hidden;
  transform: translateZ(0);
  will-change: transform, opacity;
}

.cell:not(.is-dropping):not(.is-spinning) .cell-art {
  filter: none;
}

.cell.has-art:not(.is-art-loaded)::before {
  inset: 14% 16%;
  clip-path: polygon(50% 0, 88% 24%, 100% 58%, 50% 100%, 0 58%, 12% 24%);
  border-radius: 10px;
  background:
    linear-gradient(145deg, rgba(255, 255, 255, 0.86), transparent 24%),
    radial-gradient(circle at 50% 38%, rgba(255, 255, 255, 0.34), transparent 34%),
    linear-gradient(145deg, var(--tone), rgba(48, 24, 110, 0.46));
  box-shadow:
    0 0 0 2px rgba(241, 174, 58, 0.82),
    inset 0 -8px 14px rgba(0, 0, 0, 0.22),
    0 7px 13px rgba(10, 3, 30, 0.34);
  transform: none;
}

.cell.has-art:not(.is-art-loaded) .cell-art {
  opacity: 0;
}

.cell.has-art.is-art-loaded span {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
}

.cell.has-art:not(.is-art-loaded) span {
  position: relative;
  z-index: 2;
  display: grid;
  place-items: center;
  width: 100%;
  height: 100%;
  color: #ffffff;
  font-size: clamp(20px, 8.8vw, 38px);
  line-height: 1;
  text-shadow:
    0 2px 0 rgba(60, 28, 76, 0.7),
    0 0 12px rgba(255, 255, 255, 0.42);
}

.cell.is-drop-prep {
  opacity: 0.01;
  transform: translate3d(0, var(--drop-distance, -180%), 0) scale(1);
  will-change: transform, opacity;
}

.cell.is-drop-prep .cell-art {
  filter: none;
}

.cell.is-dropping {
  animation: symbolDrop var(--drop-duration, 700ms) cubic-bezier(0.2, 0.82, 0.28, 1) both;
  animation-delay: var(--drop-delay, 0ms);
  will-change: transform, opacity;
}

.cell.is-dropping.is-reel-drop {
  animation-name: reelSymbolDrop;
  /* Per-keyframe timing functions inside reelSymbolDrop govern each
     segment; the rule-level value is just a fallback for browsers that
     don't honour per-keyframe timing. */
  animation-timing-function: linear;
}

.cell.is-dropping.is-survivor-drop {
  animation-name: symbolFall;
  animation-timing-function: cubic-bezier(0.18, 0.88, 0.24, 1);
}

.cell.is-dropping.is-reel-drop.is-survivor-drop {
  animation-name: reelSymbolDrop;
  animation-timing-function: cubic-bezier(0.12, 0.78, 0.18, 1);
}

.board.is-spinning::after,
.board.is-tumbling::after {
  content: none;
  position: absolute;
  inset: 0;
  z-index: 6;
  pointer-events: none;
  background:
    linear-gradient(180deg, rgba(255, 248, 202, 0.14), transparent 26%, rgba(17, 2, 30, 0.18)),
    linear-gradient(90deg, rgba(255, 255, 255, 0.08), transparent 18%, transparent 82%, rgba(255, 255, 255, 0.08));
  opacity: 0;
}

.board.is-spinning::after {
  animation: reelSpinSheen var(--spin-duration, 680ms) linear both;
}

.board.is-tumbling::after {
  animation: reelTumbleShade var(--drop-duration, 700ms) ease-out both;
}

.cell.is-spinning {
  animation: reelSpinRoll var(--spin-duration, 220ms) cubic-bezier(0.36, 0, 0.66, 1) both;
  animation-delay: var(--spin-delay, 0ms);
  will-change: transform, opacity;
}

.cell.is-win:not(.is-clearing) {
  z-index: 2;
  animation: winPulse 620ms ease-out both;
}

/* Role-establishment frame — a rotating, bluish-white particle ring that
   briefly wraps each winning cell at the moment a winning role is
   detected (fires with role_hit SE). The dashed border reads as
   discrete particles; the spin + glow gives the confirmation flash. */
.win-frame {
  position: absolute;
  inset: -6%;
  z-index: 5;
  pointer-events: none;
  border: 3px dashed rgba(170, 230, 255, 0.92);
  border-radius: 6px;
  box-shadow:
    0 0 18px rgba(140, 220, 255, 0.85),
    0 0 38px rgba(140, 220, 255, 0.42),
    inset 0 0 12px rgba(200, 240, 255, 0.55);
  opacity: 0;
  animation: winFrameSpin 880ms cubic-bezier(0.2, 0.7, 0.4, 1) forwards;
  will-change: transform, opacity;
}

/* Outer ring of brighter particle-dots, drawn via radial-gradient on a
   second layered pseudo so it rotates slightly out of sync with the frame. */
.win-frame::before {
  content: "";
  position: absolute;
  inset: -8%;
  border-radius: 50%;
  background:
    radial-gradient(circle 3px at 50% 0%,    rgba(255, 255, 255, 0.95), transparent 70%),
    radial-gradient(circle 3px at 100% 50%,  rgba(255, 255, 255, 0.95), transparent 70%),
    radial-gradient(circle 3px at 50% 100%,  rgba(255, 255, 255, 0.95), transparent 70%),
    radial-gradient(circle 3px at 0% 50%,    rgba(255, 255, 255, 0.95), transparent 70%),
    radial-gradient(circle 2.5px at 15% 15%, rgba(180, 230, 255, 0.85), transparent 70%),
    radial-gradient(circle 2.5px at 85% 15%, rgba(180, 230, 255, 0.85), transparent 70%),
    radial-gradient(circle 2.5px at 85% 85%, rgba(180, 230, 255, 0.85), transparent 70%),
    radial-gradient(circle 2.5px at 15% 85%, rgba(180, 230, 255, 0.85), transparent 70%);
  animation: winFrameParticles 880ms cubic-bezier(0.2, 0.7, 0.4, 1) forwards;
}

@keyframes winFrameSpin {
  0% {
    opacity: 0;
    transform: rotate(0deg) scale(0.6);
    filter: blur(2px);
  }
  14% {
    opacity: 1;
    transform: rotate(22deg) scale(1.06);
    filter: blur(0);
  }
  40% {
    opacity: 1;
    transform: rotate(72deg) scale(1.02);
  }
  70% {
    opacity: 0.92;
    transform: rotate(135deg) scale(1);
  }
  88% {
    opacity: 0.68;
    transform: rotate(175deg) scale(1.04);
    filter: blur(0);
  }
  100% {
    opacity: 0;
    transform: rotate(210deg) scale(1.14);
    filter: blur(1.2px);
  }
}

@keyframes winFrameParticles {
  0% {
    opacity: 0;
    transform: rotate(0deg) scale(0.72);
  }
  16% {
    opacity: 1;
    transform: rotate(-18deg) scale(1.08);
  }
  44% {
    opacity: 1;
    transform: rotate(-55deg) scale(1.02);
  }
  72% {
    opacity: 0.9;
    transform: rotate(-110deg) scale(1);
  }
  88% {
    opacity: 0.66;
    transform: rotate(-140deg) scale(1.04);
  }
  100% {
    opacity: 0;
    transform: rotate(-170deg) scale(1.18);
  }
}

.cell.is-win:not(.is-clearing)::before {
  box-shadow:
    0 0 0 3px rgba(255, 242, 161, 0.9),
    0 0 22px rgba(255, 226, 90, 0.72),
    inset 0 -8px 14px rgba(0, 0, 0, 0.18);
}

.cell.has-art.is-win:not(.is-clearing)::before {
  box-shadow: 0 0 24px rgba(255, 229, 102, 0.72);
}

.cell.is-clearing {
  z-index: 4;
  animation:
    symbolBurst 700ms cubic-bezier(0.16, 0.88, 0.24, 1) both,
    symbolDebris 700ms cubic-bezier(0.18, 0.62, 0.32, 1) both;
  animation-delay: calc(var(--col, 0) * 13ms + var(--row, 0) * 7ms);
}

.cell.is-clearing::before {
  background:
    radial-gradient(circle at 50% 50%, rgba(255, 255, 255, 0.95), rgba(255, 248, 200, 0.78) 28%, rgba(255, 216, 100, 0.42) 54%, rgba(255, 180, 60, 0.12) 76%, transparent 92%),
    radial-gradient(circle at 50% 52%, rgba(255, 255, 255, 0.32), transparent 72%);
  box-shadow:
    0 0 18px rgba(255, 244, 166, 0.82),
    0 0 42px rgba(255, 218, 84, 0.5);
}

.cell.has-art.is-clearing::before {
  inset: -10%;
  background:
    radial-gradient(circle at 50% 50%, rgba(255, 255, 255, 0.92), rgba(255, 248, 200, 0.72) 22%, rgba(255, 220, 110, 0.48) 44%, rgba(255, 180, 70, 0.18) 64%, transparent 86%);
  box-shadow:
    0 0 22px rgba(255, 244, 166, 0.6),
    0 0 42px rgba(255, 200, 70, 0.32);
  animation: clearingAura 700ms cubic-bezier(0.16, 0.88, 0.24, 1) both;
  animation-delay: calc(var(--col, 0) * 13ms + var(--row, 0) * 7ms);
}

.cell.has-art.is-clearing .cell-art {
  animation: symbolArtVanish 700ms cubic-bezier(0.16, 0.88, 0.24, 1) both;
  animation-delay: calc(var(--col, 0) * 13ms + var(--row, 0) * 7ms);
}

.cell.is-clearing::after,
.cell.has-art.is-clearing::after {
  content: "";
  position: absolute;
  z-index: 3;
  top: -32%;
  left: 50%;
  width: 86%;
  height: 144%;
  transform: translateX(-50%);
  border-radius: 0;
  background: linear-gradient(180deg,
    rgba(255, 255, 255, 1) 0%,
    rgba(255, 253, 232, 1) 35%,
    rgba(255, 240, 170, 1) 68%,
    rgba(255, 220, 110, 1) 88%,
    rgba(255, 196, 70, 0.7) 100%);
  -webkit-mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 200' preserveAspectRatio='none'><g fill='none' stroke='white' stroke-linecap='round' stroke-linejoin='miter' stroke-miterlimit='4'><path d='M 52 0 L 42 24 L 60 36 L 32 62 L 54 80 L 26 104 L 48 124 L 30 144 L 44 160' stroke-width='4.5'/><path d='M 56 4 L 64 26 L 48 42 L 70 56 L 52 74 L 70 98 L 56 120 L 68 144 L 54 160' stroke-width='3'/><path d='M 32 62 L 12 78 L 22 96 L 4 116' stroke-width='2.5'/><path d='M 70 56 L 88 70 L 76 90 L 92 108' stroke-width='2.5'/><path d='M 26 104 L 14 124 L 6 140' stroke-width='1.6'/><path d='M 88 70 L 96 86' stroke-width='1.6'/><path d='M 44 160 L 12 184' stroke-width='2.4'/><path d='M 44 160 L 26 196' stroke-width='2'/><path d='M 44 160 L 40 200' stroke-width='1.8'/><path d='M 54 160 L 86 184' stroke-width='2.4'/><path d='M 54 160 L 72 196' stroke-width='2'/><path d='M 54 160 L 60 200' stroke-width='1.8'/><path d='M 50 160 L 50 200' stroke-width='1.5'/><path d='M 44 160 L 4 170' stroke-width='1.8'/><path d='M 54 160 L 96 172' stroke-width='1.8'/></g></svg>") center/100% 100% no-repeat;
          mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 200' preserveAspectRatio='none'><g fill='none' stroke='white' stroke-linecap='round' stroke-linejoin='miter' stroke-miterlimit='4'><path d='M 52 0 L 42 24 L 60 36 L 32 62 L 54 80 L 26 104 L 48 124 L 30 144 L 44 160' stroke-width='4.5'/><path d='M 56 4 L 64 26 L 48 42 L 70 56 L 52 74 L 70 98 L 56 120 L 68 144 L 54 160' stroke-width='3'/><path d='M 32 62 L 12 78 L 22 96 L 4 116' stroke-width='2.5'/><path d='M 70 56 L 88 70 L 76 90 L 92 108' stroke-width='2.5'/><path d='M 26 104 L 14 124 L 6 140' stroke-width='1.6'/><path d='M 88 70 L 96 86' stroke-width='1.6'/><path d='M 44 160 L 12 184' stroke-width='2.4'/><path d='M 44 160 L 26 196' stroke-width='2'/><path d='M 44 160 L 40 200' stroke-width='1.8'/><path d='M 54 160 L 86 184' stroke-width='2.4'/><path d='M 54 160 L 72 196' stroke-width='2'/><path d='M 54 160 L 60 200' stroke-width='1.8'/><path d='M 50 160 L 50 200' stroke-width='1.5'/><path d='M 44 160 L 4 170' stroke-width='1.8'/><path d='M 54 160 L 96 172' stroke-width='1.8'/></g></svg>") center/100% 100% no-repeat;
  filter:
    drop-shadow(0 0 5px rgba(255, 255, 220, 1))
    drop-shadow(0 0 14px rgba(255, 232, 130, 0.85))
    drop-shadow(0 0 28px rgba(255, 196, 70, 0.55));
  pointer-events: none;
  animation: lightningStrike 700ms cubic-bezier(0.14, 0.88, 0.22, 1) both;
  animation-delay: calc(var(--col, 0) * 13ms + var(--row, 0) * 7ms);
}

/* ── Symbol-clear effect v2 (Gates of Olympus-style snappy burst) ──────────
   Gated by body.clearfx-v2 (JS flag CLEAR_FX_V2). A clean, fast 400ms burst:
   the symbol flashes white-hot, pops up, then implodes — with a halo flash
   and an expanding ring. Overrides the original lightning-bolt clear.
   Set CLEAR_FX_V2 = false to revert. */
body.clearfx-v2 .cell.is-clearing {
  animation: none !important;
}

body.clearfx-v2 .cell.has-art.is-clearing .cell-art {
  animation: clearV2Art 580ms cubic-bezier(0.2, 0.7, 0.3, 1) both !important;
  animation-delay: calc(var(--col, 0) * 13ms + var(--row, 0) * 7ms) !important;
}

body.clearfx-v2 .cell.is-clearing::before,
body.clearfx-v2 .cell.has-art.is-clearing::before {
  inset: -18% !important;
  background: radial-gradient(circle at 50% 50%,
    rgba(255, 255, 255, 0.96) 0%,
    rgba(255, 240, 178, 0.62) 32%,
    rgba(255, 206, 96, 0.2) 56%,
    transparent 74%) !important;
  box-shadow: none !important;
  animation: clearV2Halo 580ms cubic-bezier(0.2, 0.7, 0.3, 1) both !important;
  animation-delay: calc(var(--col, 0) * 13ms + var(--row, 0) * 7ms) !important;
}

body.clearfx-v2 .cell.is-clearing::after,
body.clearfx-v2 .cell.has-art.is-clearing::after {
  -webkit-mask: none !important;
  mask: none !important;
  background: none !important;
  top: 50% !important;
  left: 50% !important;
  width: 122% !important;
  height: 122% !important;
  border: 3px solid rgba(255, 246, 206, 0.92) !important;
  border-radius: 50% !important;
  filter: drop-shadow(0 0 8px rgba(255, 238, 170, 0.9)) !important;
  animation: clearV2Ring 580ms cubic-bezier(0.22, 0.66, 0.3, 1) both !important;
  animation-delay: calc(var(--col, 0) * 13ms + var(--row, 0) * 7ms) !important;
}

@keyframes clearV2Art {
  0%   { transform: scale(1); filter: brightness(1); opacity: 1; }
  22%  { transform: scale(1.32);
         filter: brightness(2.5) drop-shadow(0 0 14px rgba(255, 255, 255, 0.95));
         opacity: 1; }
  100% { transform: scale(0.1) rotate(16deg); filter: brightness(1.7); opacity: 0; }
}

@keyframes clearV2Halo {
  0%   { opacity: 0; transform: scale(0.45); }
  24%  { opacity: 1; transform: scale(1.08); }
  100% { opacity: 0; transform: scale(1.55); }
}

@keyframes clearV2Ring {
  0%   { opacity: 0; transform: translate(-50%, -50%) scale(0.3); }
  30%  { opacity: 1; transform: translate(-50%, -50%) scale(0.62); }
  100% { opacity: 0; transform: translate(-50%, -50%) scale(1.5); }
}

.cell.is-mult::before {
  inset: 12% 16%;
  clip-path: none;
  border-radius: 999px;
  background:
    linear-gradient(145deg, rgba(255, 255, 255, 0.92), transparent 26%),
    linear-gradient(145deg, #fff3a7, #40e1d2 58%, #2870bd);
}

.cell.is-mult.has-art::before {
  content: "";
  inset: 18%;
  z-index: 0;
  clip-path: none;
  border-radius: 999px;
  background: transparent;
  box-shadow:
    0 0 7px rgba(86, 240, 224, 0.4),
    0 0 15px rgba(255, 240, 110, 0.24);
  transform: none;
  animation: multiplierIdleGlow 2.6s ease-in-out infinite;
}

.cell.is-mult.has-art .cell-art {
  inset: -3%;
  z-index: 2;
  width: 106%;
  height: 106%;
  transform: translateZ(0);
  filter: none;
  animation: multiplierIdlePulse 2.6s ease-in-out infinite;
  transition: filter 280ms ease;
}

/* Normalise digit sizes: the 2-digit multiplier PNG assets (x10, x15, x20)
   render the digits visibly smaller than 1-digit assets to fit the same
   canvas. Scale those images up so the digit reads at the same size as
   single-digit multipliers. */
.cell.is-mult[data-mult-value="10"] .cell-art,
.cell.is-mult[data-mult-value="15"] .cell-art,
.cell.is-mult[data-mult-value="20"] .cell-art {
  transform: translateZ(0) scale(1.18);
}

/* Locked multiplier: desaturated and dim until a winning cluster activates it. */
.cell.is-mult.is-locked.has-art .cell-art {
  filter: saturate(0.18) brightness(0.55) contrast(0.92);
  animation: none;
}

.cell.is-mult.is-locked::before {
  opacity: 0.25 !important;
  background: radial-gradient(circle at 50% 50%,
    rgba(140, 140, 150, 0.32),
    rgba(80, 80, 90, 0.18) 50%,
    transparent 80%) !important;
  box-shadow: 0 0 6px rgba(80, 80, 90, 0.4) !important;
  animation: none !important;
}

/* Brief flash when the lock is released (CSS transitions handle most of it,
   this just adds a kick to the moment the photon lands). */
.cell.is-mult.is-just-unlocked.has-art .cell-art {
  animation: multiplierUnlockKick 480ms cubic-bezier(0.18, 0.88, 0.24, 1) 0s 1;
}

@keyframes multiplierUnlockKick {
  0%   { filter: saturate(0.18) brightness(0.55); transform: translateZ(0) scale(1); }
  35%  { filter: saturate(1.8) brightness(2.2); transform: translateZ(0) scale(1.18); }
  100% { filter: none; transform: translateZ(0) scale(1); }
}

/* ---------------------------------------------------------------------------
   ② Chain-Lock (Lightning Shackle 案A) — MP cells are bound by an X-shaped
   electric shackle while `.is-locked`. The shackle "shorts out" the moment
   a winning cluster releases the MP (.is-just-unlocked), reading as the
   cause-effect "your win broke the binding". Uses lightning instead of
   iron chains so the visual language stays in the THUNDERHEART palette
   alongside the existing electric bolts.

   - Asset: image/effects/chain.svg (X-shaped jagged electric arcs)
   - Gating: body.fb-chain-lock — toggled by FB_CHAIN_LOCK_ENABLED in JS.
   - Tempo: snap completes in 240ms (under the 220ms gap before the next
     renderBoard, with a touch of overshoot for the lingering spark).
     Locked-state pulse uses opacity only at 2s period so the GPU cost
     is negligible.
   - Specificity: selectors include `.has-art` to outrank the base rule
     `.cell.has-art::after { content: none; }` which would otherwise
     suppress the pseudo.
--------------------------------------------------------------------------- */
body.fb-chain-lock .cell.is-mult.has-art.is-locked::after,
body.fb-chain-lock .cell.is-mult.has-art.is-just-unlocked::after {
  content: "" !important;
  position: absolute !important;
  /* Reset base .cell::after's positioning FIRST so inset wins on the
     final value. Declaration order matters within a rule. */
  transform: none !important;
  clip-path: none !important;
  border-radius: 0 !important;
  top: auto;
  right: auto;
  bottom: auto;
  left: auto;
  width: auto;
  height: auto;
  /* User-supplied chain.png at image/game/multipliers/ — heavy iron chains
     wrapping a central gold padlock with gem accents. The art has its own
     baked-in shadows / metal shading, so we don't add extra drop-shadow
     here; we keep just a faint underlay shadow for separation from bright
     gem backgrounds. */
  inset: 2% !important;
  background: url("../image/game/multipliers/chain.png") center / contain no-repeat !important;
  pointer-events: none;
  z-index: 6 !important;
  filter: drop-shadow(0 2px 3px rgba(0, 0, 0, 0.55));
  opacity: 0.98;
  transform-origin: center;
  /* No screen-blend: the chain art is intentionally dark metal and
     should overlay opaquely on the gem, not get washed out. */
}

/* The chain art is heavy iron metal — it should look static, not buzz.
   No locked-state animation: the chain just hangs there as a clear
   "this is sealed" cue. The desaturated gem underneath already conveys
   inactivity; the chain adds the binding metaphor. */

/* Release: the gold padlock catches the light, the chain shakes once
   and breaks apart with rotate + scale + fade. 240ms total — fits in
   the 220ms gap before the next renderBoard swaps to clearing state. */
body.fb-chain-lock .cell.is-mult.has-art.is-just-unlocked::after {
  animation: chainBreak 260ms cubic-bezier(0.32, 0, 0.22, 1) forwards;
}

@keyframes chainBreak {
  0%   {
    opacity: 1;
    transform: scale(1) rotate(0deg);
    filter: drop-shadow(0 2px 3px rgba(0, 0, 0, 0.55));
  }
  30%  {
    /* The padlock catches a sharp gold glint as the lock breaks. */
    opacity: 1;
    transform: scale(1.08) rotate(-3deg);
    filter:
      drop-shadow(0 2px 3px rgba(0, 0, 0, 0.55))
      drop-shadow(0 0 14px rgba(255, 220, 130, 0.92))
      brightness(1.25);
  }
  60%  {
    opacity: 0.85;
    transform: scale(1.22) rotate(5deg);
    filter:
      drop-shadow(0 0 18px rgba(255, 200, 100, 0.7))
      brightness(1.15);
  }
  100% {
    opacity: 0;
    transform: scale(1.5) rotate(10deg);
    filter:
      drop-shadow(0 0 20px rgba(255, 200, 100, 0));
  }
}

@media (prefers-reduced-motion: reduce) {
  body.fb-chain-lock .cell.is-mult.has-art.is-just-unlocked::after {
    animation: none;
    opacity: 0;
  }
}

.cell.is-mult:not(.is-clearing):not(.is-dropping):not(.is-spinning) {
  z-index: 3;
}

.cell.is-mult {
  contain: layout style;
  overflow: visible;
}

.cell.is-mult span {
  color: #15335e;
  font-family: Inter, ui-sans-serif, system-ui, sans-serif;
  font-size: clamp(12px, 5.2vw, 22px);
  text-shadow: 0 1px 0 rgba(255, 255, 255, 0.86);
  white-space: nowrap;
}

.cell.is-collected:not(.is-clearing) {
  filter: drop-shadow(0 0 16px rgba(49, 214, 202, 0.62));
}

.cell.is-mult.is-absorbing {
  z-index: 5;
}

/* FB①: when a multiplier symbol lands, the whole board frame (the symbol
   display area) becomes electrically charged. A fixed-position overlay is
   sized to the board by JS (getBoundingClientRect); the border crackles with
   an electric glow. The symbol art itself is left untouched. */
.board-charge {
  position: fixed;
  z-index: 40;
  pointer-events: none;
  border-radius: 13px;
  border: 5px solid rgba(212, 241, 255, 1);
  box-shadow:
    0 0 28px 6px rgba(150, 215, 255, 1),
    0 0 72px 16px rgba(110, 195, 255, 0.85),
    0 0 130px 34px rgba(86, 165, 255, 0.5),
    inset 0 0 46px rgba(170, 225, 255, 0.72),
    inset 0 0 96px rgba(120, 200, 255, 0.4);
  animation: boardCharge 1200ms ease-out forwards;
}

/* Inner electric wash — the whole board area briefly tints electric blue. */
.board-charge::before {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background: radial-gradient(ellipse at 50% 45%,
    rgba(160, 215, 255, 0.34), rgba(120, 190, 255, 0.12) 55%, transparent 78%);
  animation: boardChargeWash 1200ms ease-out forwards;
}

/* Expanding shockwave ring — a second frame blasts outward and fades. */
.board-charge::after {
  content: "";
  position: absolute;
  inset: -5px;
  border-radius: 16px;
  border: 3px solid rgba(205, 238, 255, 0.95);
  box-shadow: 0 0 24px rgba(140, 205, 255, 0.8);
  animation: boardChargeWave 1200ms cubic-bezier(0.2, 0.7, 0.3, 1) forwards;
}

/* Violent flicker = a strong electric crackle; opens with a white-hot flash. */
@keyframes boardCharge {
  0%   { opacity: 0; transform: scale(1.09); filter: brightness(3.2); }
  6%   { opacity: 1; transform: scale(1); filter: brightness(2.7); }
  15%  { opacity: 0.45; filter: brightness(1); }
  25%  { opacity: 1; filter: brightness(2.5); }
  36%  { opacity: 0.5; filter: brightness(1.05); }
  48%  { opacity: 1; filter: brightness(2.3); }
  60%  { opacity: 0.55; filter: brightness(1.1); }
  72%  { opacity: 1; filter: brightness(2); }
  85%  { opacity: 0.62; filter: brightness(1.2); }
  100% { opacity: 0; filter: brightness(0.85); }
}

@keyframes boardChargeWash {
  0%   { opacity: 0; }
  10%  { opacity: 1; }
  55%  { opacity: 0.5; }
  100% { opacity: 0; }
}

@keyframes boardChargeWave {
  0%   { opacity: 1; transform: scale(1); }
  100% { opacity: 0; transform: scale(1.22); }
}

/* FB④: spark on the MP source cell — fired simultaneously with the board
   charge so the causality "this MP symbol → frame electrified" reads. The
   cell uses `contain: paint`, so the spark is clipped to the cell box. */
.mult-source-spark {
  position: absolute;
  inset: -22%;
  pointer-events: none;
  z-index: 8;
  border-radius: 50%;
  background: radial-gradient(circle at 50% 50%,
    rgba(255, 255, 255, 0.92) 0%,
    rgba(186, 226, 255, 0.66) 22%,
    rgba(140, 200, 255, 0.32) 46%,
    transparent 72%);
  box-shadow:
    0 0 20px rgba(170, 220, 255, 0.85),
    0 0 38px rgba(120, 200, 255, 0.55);
  mix-blend-mode: screen;
  animation: multSourceSpark 880ms cubic-bezier(0.16, 0.86, 0.24, 1) forwards;
}

@keyframes multSourceSpark {
  0%   { opacity: 0; transform: scale(0.3); }
  16%  { opacity: 1; transform: scale(1); }
  100% { opacity: 0; transform: scale(1.5); }
}

/* FB④ (trial): multiplier-impact punch. A decaying screen shake on the game
   shell + a gold vignette flash, both scaled by multiplier tier (amplitude
   set inline by JS). Disabled at source via FB4_ENABLED. */
.game-shell.is-fb4-shake {
  animation: fb4Shake 440ms cubic-bezier(0.36, 0.07, 0.19, 0.97) both;
}

@keyframes fb4Shake {
  0%, 100% { transform: translate(0, 0); }
  10% { transform: translate(calc(var(--fb4-shake-amp, 6px) * -0.9), calc(var(--fb4-shake-amp, 6px) * 0.5)); }
  22% { transform: translate(calc(var(--fb4-shake-amp, 6px) * 0.85), calc(var(--fb4-shake-amp, 6px) * -0.7)); }
  35% { transform: translate(calc(var(--fb4-shake-amp, 6px) * -0.68), calc(var(--fb4-shake-amp, 6px) * -0.38)); }
  48% { transform: translate(calc(var(--fb4-shake-amp, 6px) * 0.52), calc(var(--fb4-shake-amp, 6px) * 0.46)); }
  62% { transform: translate(calc(var(--fb4-shake-amp, 6px) * -0.36), calc(var(--fb4-shake-amp, 6px) * -0.26)); }
  78% { transform: translate(calc(var(--fb4-shake-amp, 6px) * 0.2), calc(var(--fb4-shake-amp, 6px) * 0.15)); }
  90% { transform: translate(calc(var(--fb4-shake-amp, 6px) * -0.09), calc(var(--fb4-shake-amp, 6px) * -0.06)); }
}

.fb4-impact-flash {
  position: fixed;
  inset: 0;
  z-index: 46;
  pointer-events: none;
  background: radial-gradient(ellipse 78% 62% at 50% 52%,
    transparent 50%, rgba(255, 214, 120, calc(var(--fb4-flash, 0.5) * 0.9)) 100%);
  animation: fb4Flash 380ms ease-out forwards;
}

@keyframes fb4Flash {
  0%   { opacity: 0; }
  20%  { opacity: 1; }
  100% { opacity: 0; }
}

.cell.is-hidden {
  visibility: hidden;
  opacity: 0;
}

.multiplier-fly {
  position: fixed;
  left: 0;
  top: 0;
  z-index: 20;
  pointer-events: none;
  color: #fff5ad;
  font-family: Inter, ui-sans-serif, system-ui, sans-serif;
  font-size: 32px;
  font-weight: 950;
  line-height: 1;
  padding: 4px 8px 6px;
  border-radius: 999px;
  border: 1px solid rgba(255, 242, 161, 0.8);
  background:
    radial-gradient(circle at 50% 44%, rgba(255, 255, 255, 0.34), transparent 58%),
    linear-gradient(180deg, rgba(82, 30, 98, 0.88), rgba(30, 7, 48, 0.82));
  text-shadow:
    0 2px 0 rgba(49, 11, 76, 0.62),
    0 0 12px rgba(255, 245, 173, 0.95),
    0 0 24px rgba(49, 214, 202, 0.58);
  animation: multiplierAbsorb 760ms cubic-bezier(0.16, 0.82, 0.22, 1) var(--fly-delay, 0ms) forwards;
  transform: translate(-50%, -50%) scale(1);
}

/* Multiplier tag flying from the HUD multiplier card to the win card,
   showing the bank that's about to be applied to the upcoming win. */
.mult-to-win-fly {
  position: fixed;
  left: 0;
  top: 0;
  z-index: 22;
  pointer-events: none;
  color: #fff5ad;
  font-family: Inter, ui-sans-serif, system-ui, sans-serif;
  font-size: 28px;
  font-weight: 950;
  line-height: 1;
  padding: 5px 12px 7px;
  border-radius: 999px;
  border: 2px solid var(--fly-rim, rgba(255, 242, 161, 0.9));
  background:
    radial-gradient(circle at 50% 36%, rgba(255, 255, 255, 0.42), transparent 60%),
    linear-gradient(180deg, var(--fly-bg-top, rgba(82, 30, 98, 0.92)), var(--fly-bg-bottom, rgba(30, 7, 48, 0.88)));
  text-shadow:
    0 2px 0 rgba(49, 11, 76, 0.7),
    0 0 14px var(--fly-glow, rgba(255, 245, 173, 0.95)),
    0 0 26px var(--fly-glow, rgba(255, 220, 100, 0.65));
  box-shadow:
    0 0 18px var(--fly-glow, rgba(255, 220, 100, 0.55)),
    0 6px 16px rgba(0, 0, 0, 0.4);
  transform: translate(-50%, -50%) scale(1);
  animation: multToWinFly 760ms cubic-bezier(0.22, 0.7, 0.3, 1) forwards;
}

/* Tier 0 — GREEN (x1-x9) */
.mult-to-win-fly.is-tier-0 {
  --fly-rim: rgba(110, 230, 140, 0.95);
  --fly-glow: rgba(110, 230, 140, 0.78);
}
/* Tier 1 — RED (x10-x24) */
.mult-to-win-fly.is-tier-1 {
  --fly-rim: rgba(255, 108, 90, 0.97);
  --fly-glow: rgba(255, 82, 64, 0.92);
}
/* Tier 2 — GOLD (x25-x49) */
.mult-to-win-fly.is-tier-2 {
  --fly-rim: rgba(255, 212, 90, 1);
  --fly-glow: rgba(255, 212, 80, 1);
}
/* Tier 3 — RAINBOW (x50+) — opt to flash with a multi-stop ring */
.mult-to-win-fly.is-tier-3 {
  --fly-rim: rgba(255, 255, 255, 1);
  --fly-glow: rgba(255, 255, 255, 0.95);
  animation:
    multToWinFly 760ms cubic-bezier(0.22, 0.7, 0.3, 1) forwards,
    flyRainbow 1.2s linear infinite;
}

@keyframes flyRainbow {
  0%   { filter: hue-rotate(0deg); }
  100% { filter: hue-rotate(360deg); }
}

@keyframes multToWinFly {
  0% {
    transform: translate(-50%, -50%) translate3d(0, 0, 0) scale(0.6);
    opacity: 0;
  }
  18% {
    transform: translate(-50%, -50%) translate3d(0, 0, 0) scale(1.18);
    opacity: 1;
  }
  64% {
    transform: translate(-50%, -50%) translate3d(var(--fly-x, 0), var(--fly-y, 0), 0) scale(1.06);
    opacity: 1;
  }
  88% {
    transform: translate(-50%, -50%) translate3d(var(--fly-x, 0), var(--fly-y, 0), 0) scale(0.8);
    opacity: 0.9;
  }
  100% {
    transform: translate(-50%, -50%) translate3d(var(--fly-x, 0), var(--fly-y, 0), 0) scale(0.5);
    opacity: 0;
  }
}

/* Pulse the win card the instant the multiplier tag lands */
.win-card.is-mult-boost {
  animation: winCardMultBoost 520ms cubic-bezier(0.18, 0.82, 0.3, 1);
}

@keyframes winCardMultBoost {
  0% { transform: scale(1); box-shadow: var(--win-card-shadow, 0 0 0 transparent); }
  30% {
    transform: scale(1.06);
    box-shadow:
      0 0 22px rgba(255, 220, 100, 0.75),
      0 0 12px rgba(255, 245, 173, 0.55),
      0 10px 26px rgba(47, 10, 64, 0.32);
  }
  100% { transform: scale(1); }
}

/* FB⑥: subtotal labels are centred on the board (vertically + horizontally)
   in the Sweet Rush style, rather than pinned to the top edge. */
.win-payout-stack {
  position: absolute;
  left: 8px;
  right: 8px;
  top: 0;
  bottom: 0;
  z-index: 12;
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-content: center;
  align-items: center;
  gap: 6px 18px;
  pointer-events: none;
}

/* FB⑦: the chip frame (border / background / box-shadow) is removed — the
   subtotal now reads as floating glowing text. Strong text shadows (a dark
   halo + the gold/red glow) keep it legible over the board symbols. */
.win-payout-label {
  position: relative;
  display: grid;
  place-items: center;
  gap: 2px;
  flex: 0 1 auto;
  padding: 2px 6px;
  color: #ffffff;
  pointer-events: none;
  transform: translateY(0) scale(0.88);
  animation: winPayoutFloat 900ms cubic-bezier(0.17, 0.86, 0.23, 1) forwards;
}

.win-payout-label span {
  color: #fff2a1;
  font-size: 11px;
  font-weight: 900;
  line-height: 1;
  text-shadow:
    0 0 3px rgba(18, 3, 30, 1),
    0 1px 2px rgba(18, 3, 30, 0.96);
}

.win-payout-label strong {
  color: #ffffff;
  /* 盤面に浮かぶ配当サブトータルは視認性が重要なので、HUD の小さい #winValue
     ではなく旧 #winValue 相当の大きさ（clamp(48, 13vw, 72)）に設定する。
     スプライト digit は 1.85em で 72 × 1.85 ≈ 133px の見た目高さ。 */
  font-size: clamp(48px, 13vw, 72px);
  font-weight: 950;
  line-height: 1;
  text-shadow:
    0 0 3px rgba(18, 3, 30, 1),
    0 2px 6px rgba(18, 3, 30, 0.95),
    0 0 16px rgba(255, 242, 161, 0.95),
    0 0 30px rgba(244, 95, 111, 0.55);
}

/* Amount row hosts the spin-recovery icons + the amount value side by
   side so the player reads "icon + win" as a single reward chip. */
.win-payout-amount-row {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}

.win-payout-spin-icons {
  display: inline-flex;
  align-items: center;
  gap: 3px;
}

/* Lit spin pip — uses the same THUNDERHEART thunder.png art as the HUD
   spin pips for visual continuity (the in-board recovery indicator
   should feel like the same icon flying up to the HUD strip). */
.win-payout-spin-icon {
  /* Scaled up to read alongside the now-larger amount text. */
  width: 30px;
  height: 30px;
  background-image: url("../image/game/symbols/thunder.png");
  background-size: contain;
  background-position: center;
  background-repeat: no-repeat;
  filter:
    saturate(1.2) brightness(1.12)
    drop-shadow(0 0 5px rgba(255, 200, 80, 0.95))
    drop-shadow(0 0 12px rgba(255, 200, 80, 0.55));
  animation: winPayoutSpinIconPop 480ms cubic-bezier(0.16, 0.86, 0.24, 1) both;
}

@keyframes winPayoutSpinIconPop {
  0%   { transform: scale(0.4) rotate(-12deg); opacity: 0; }
  55%  { transform: scale(1.18) rotate(4deg); opacity: 1; }
  100% { transform: scale(1) rotate(0deg); opacity: 1; }
}

/* Flying amount text — duplicates the strong amount and rides up to the
   HUD win-card. Fades out as it lands so the HUD counter pickup feels
   like a continuation rather than a duplicate display. */
.win-payout-fly-amount {
  position: fixed;
  left: 0;
  top: 0;
  z-index: 22;
  pointer-events: none;
  color: #ffffff;
  font-family: inherit;
  font-size: 22px;
  font-weight: 950;
  line-height: 1;
  text-shadow:
    0 2px 0 rgba(49, 11, 76, 0.72),
    0 0 14px rgba(255, 242, 161, 0.95),
    0 0 24px rgba(244, 95, 111, 0.52);
  transform: translate(-50%, -50%);
  animation: winPayoutFlyAmount 700ms cubic-bezier(0.28, 0.55, 0.32, 1) var(--fly-delay, 0ms) forwards;
  will-change: transform, opacity;
}

@keyframes winPayoutFlyAmount {
  0%   {
    transform: translate(-50%, -50%) translate3d(0, 0, 0) scale(1);
    opacity: 0;
  }
  12%  {
    transform: translate(-50%, -50%) translate3d(0, 0, 0) scale(1.12);
    opacity: 1;
  }
  72%  {
    transform: translate(-50%, -50%) translate3d(calc(var(--fly-x, 0) * 0.82), calc(var(--fly-y, 0) * 0.82), 0) scale(1);
    opacity: 1;
  }
  100% {
    transform: translate(-50%, -50%) translate3d(var(--fly-x, 0), var(--fly-y, 0), 0) scale(0.62);
    opacity: 0;
  }
}

/* Flying spin pip — duplicates the on-label spin icon and rides up to
   a specific HUD pip slot. Lights that pip up on impact (via JS class). */
.win-payout-fly-pip {
  position: fixed;
  left: 0;
  top: 0;
  z-index: 23;
  pointer-events: none;
  width: 22px;
  height: 22px;
  background-image: url("../image/effects/lightning_pip.png");
  background-size: contain;
  background-position: center;
  background-repeat: no-repeat;
  filter:
    saturate(1.2) brightness(1.18)
    drop-shadow(0 0 6px rgba(255, 220, 110, 0.95))
    drop-shadow(0 0 14px rgba(255, 200, 80, 0.62));
  transform: translate(-50%, -50%);
  animation: winPayoutFlyPip 700ms cubic-bezier(0.28, 0.55, 0.32, 1) var(--fly-delay, 0ms) forwards;
  will-change: transform, opacity;
}

@keyframes winPayoutFlyPip {
  0%   {
    transform: translate(-50%, -50%) translate3d(0, 0, 0) scale(1) rotate(0deg);
    opacity: 0;
  }
  14%  {
    transform: translate(-50%, -50%) translate3d(0, 0, 0) scale(1.25) rotate(-8deg);
    opacity: 1;
  }
  78%  {
    transform: translate(-50%, -50%) translate3d(calc(var(--fly-x, 0) * 0.85), calc(var(--fly-y, 0) * 0.85), 0) scale(1) rotate(8deg);
    opacity: 1;
  }
  100% {
    transform: translate(-50%, -50%) translate3d(var(--fly-x, 0), var(--fly-y, 0), 0) scale(0.5) rotate(20deg);
    opacity: 0;
  }
}

/* Impact pulse when the multiplier badge lands on a subtotal label —
   the label briefly enlarges with a gold flash so the player sees the
   "multiplier has been applied to this subtotal" moment clearly. */
.win-payout-label.is-mult-boost {
  animation: winPayoutMultBoost 560ms cubic-bezier(0.18, 0.82, 0.3, 1);
}

@keyframes winPayoutMultBoost {
  0%   { transform: scale(1); box-shadow:
           0 0 0 1px rgba(116, 48, 128, 0.5),
           0 0 18px rgba(255, 238, 140, 0.68),
           0 8px 18px rgba(24, 4, 38, 0.38); }
  30%  { transform: scale(1.18);
         box-shadow:
           0 0 0 2px rgba(255, 220, 80, 0.85),
           0 0 26px rgba(255, 220, 80, 0.95),
           0 0 14px rgba(255, 255, 200, 0.75),
           0 8px 20px rgba(24, 4, 38, 0.42);
         filter: brightness(1.15);
  }
  100% { transform: scale(1); }
}

.cell.is-scatter-trigger {
  z-index: 3;
}

.cell.is-scatter-trigger::before {
  box-shadow: 0 0 26px rgba(255, 242, 161, 0.88);
}

.cell.is-scatter-trigger .cell-art {
  animation: scatterSymbolGlow 760ms ease-in-out infinite alternate;
  filter:
    drop-shadow(0 0 8px rgba(255, 242, 161, 0.9));
}

.cell.is-scatter-vanishing {
  z-index: 6;
  animation: scatterCellVanish 720ms cubic-bezier(0.16, 0.88, 0.24, 1) forwards;
}

.cell.has-art.is-scatter-vanishing::before {
  inset: -14%;
  background:
    radial-gradient(circle at 50% 50%, rgba(255, 255, 255, 0.96), rgba(255, 243, 155, 0.58) 26%, rgba(86, 236, 204, 0.3) 48%, transparent 72%);
  box-shadow: none;
  animation: scatterLightBloom 720ms ease-out forwards;
}

.cell.has-art.is-scatter-vanishing .cell-art {
  animation: scatterSymbolVanish 720ms cubic-bezier(0.16, 0.88, 0.24, 1) forwards;
}

.asset-cache {
  position: fixed;
  left: -9999px;
  top: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
  opacity: 0.001;
  pointer-events: none;
}

.asset-cache img {
  display: block;
  width: 64px;
  height: 64px;
  object-fit: contain;
}

.action-row {
  position: relative;
  z-index: 2;
  display: grid;
  grid-template-columns: 42px minmax(0, 1fr) 100px;
  gap: 10px;
  align-items: center;
  min-height: 86px;
  padding: 10px 12px;
  border-top: 1px solid rgba(255, 236, 166, 0.35);
  background:
    linear-gradient(180deg, rgba(255, 255, 255, 0.08), transparent),
    rgba(34, 13, 45, 0.86);
  box-shadow: 0 -18px 34px rgba(36, 10, 50, 0.24);
}

.info-button {
  width: 36px;
  height: 36px;
  justify-self: center;
  border: 1px solid rgba(255, 255, 255, 0.62);
  border-radius: 999px;
  color: #fff8df;
  cursor: pointer;
  font-size: 22px;
  font-weight: 950;
  background:
    linear-gradient(180deg, rgba(255, 255, 255, 0.34), transparent 55%),
    rgba(46, 15, 61, 0.82);
}

.bet-card {
  min-height: 54px;
  padding: 7px 9px 8px;
  text-align: center;
}

.bet-stepper {
  display: grid;
  grid-template-columns: 30px minmax(42px, 1fr) 30px;
  gap: 7px;
  align-items: center;
}

.bet-card strong {
  font-size: 26px;
  line-height: 1;
}

.bet-button {
  width: 30px;
  height: 30px;
  display: grid;
  place-items: center;
  border: 1px solid rgba(255, 233, 159, 0.7);
  border-radius: 999px;
  background:
    linear-gradient(180deg, rgba(255, 255, 255, 0.22), transparent 55%),
    rgba(49, 16, 72, 0.92);
  color: #fff8df;
  cursor: pointer;
  font-size: 21px;
  font-weight: 950;
  line-height: 1;
  box-shadow: inset 0 -2px 0 rgba(18, 4, 35, 0.22);
}

.bet-button:disabled {
  cursor: not-allowed;
  opacity: 0.45;
}

.play-button {
  width: 94px;
  height: 64px;
  border: 2px solid rgba(255, 255, 255, 0.72);
  border-radius: 999px;
  background:
    linear-gradient(180deg, rgba(255, 255, 255, 0.82), transparent 42%),
    linear-gradient(135deg, #ff72b7, #df177d 58%, #9a0e5c);
  color: #ffffff;
  cursor: pointer;
  font-size: 16px;
  font-weight: 950;
  letter-spacing: 0;
  box-shadow: 0 10px 18px rgba(54, 10, 64, 0.36), inset 0 -5px 0 rgba(85, 5, 55, 0.34);
}

.play-button:disabled {
  cursor: wait;
  opacity: 0.66;
}

.scatter-award {
  position: fixed;
  inset: 0;
  z-index: 60;
  display: grid;
  place-items: center;
  padding: 24px;
  pointer-events: none;
  opacity: 0;
  visibility: hidden;
  background:
    radial-gradient(circle at 50% 42%, rgba(255, 240, 161, 0.2), transparent 28%),
    rgba(8, 3, 18, 0.72);
  transition: opacity 180ms ease, visibility 180ms ease;
}

.scatter-award.is-active {
  opacity: 1;
  visibility: visible;
}

.scatter-award-panel {
  position: relative;
  display: grid;
  justify-items: center;
  gap: 8px;
  min-width: min(360px, calc(100vw - 48px));
  padding: 34px 28px 38px;
  color: #fff8df;
  text-align: center;
  opacity: 0;
  transform: scale(0.82) translateY(18px);
}

.scatter-award.is-active .scatter-award-panel {
  animation: scatterAwardAppear 720ms cubic-bezier(0.17, 0.9, 0.24, 1) both;
}

.scatter-award-panel::before,
.scatter-award-panel::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: 999px;
  pointer-events: none;
}

.scatter-award-panel::before {
  background:
    radial-gradient(circle at 50% 50%, rgba(255, 255, 255, 0.34), rgba(255, 230, 108, 0.22) 36%, rgba(108, 255, 182, 0.08) 62%, transparent 74%);
  filter: blur(1px);
}

.scatter-award.is-active .scatter-award-panel::before {
  animation: scatterAwardHalo 1250ms ease-out both;
}

.scatter-award-panel::after {
  inset: 18px -20px;
  border-top: 2px solid rgba(255, 243, 168, 0.78);
  border-bottom: 2px solid rgba(255, 243, 168, 0.58);
  box-shadow:
    0 0 24px rgba(255, 223, 92, 0.72),
    inset 0 0 22px rgba(255, 255, 255, 0.12);
  opacity: 0.9;
}

.scatter-award-panel span {
  position: relative;
  z-index: 1;
  color: #fff2a1;
  font-size: 13px;
  font-weight: 950;
  letter-spacing: 0.08em;
  text-shadow: 0 0 12px rgba(255, 229, 102, 0.75);
}

.scatter-award-panel strong {
  position: relative;
  z-index: 1;
  color: #ffffff;
  font-size: clamp(52px, 13vw, 92px);
  line-height: 1;
  letter-spacing: 0;
  text-shadow:
    0 3px 0 rgba(111, 56, 9, 0.76),
    0 0 12px rgba(255, 255, 255, 0.95),
    0 0 30px rgba(255, 221, 91, 0.9),
    0 0 54px rgba(108, 255, 182, 0.54);
}

.info-modal,
.result-modal {
  width: min(680px, calc(100vw - 28px));
  padding: 0;
  border: 2px solid rgba(245, 195, 76, 0.78);
  border-radius: 8px;
  background: transparent;
  color: var(--text);
}

.result-modal {
  width: min(760px, calc(100vw - 20px));
  overflow: visible;
  border: 0;
}

.info-modal::backdrop,
.result-modal::backdrop {
  background: rgba(12, 10, 34, 0.7);
}

.modal-panel {
  max-height: min(720px, calc(100vh - 40px));
  overflow: auto;
  border-radius: 6px;
  background:
    linear-gradient(180deg, rgba(255, 255, 255, 0.08), transparent 28%),
    #131846;
  box-shadow: 0 28px 90px rgba(32, 20, 76, 0.5);
}

.modal-header {
  position: sticky;
  top: 0;
  z-index: 2;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 14px;
  padding: 16px 18px;
  border-bottom: 1px solid rgba(245, 195, 76, 0.34);
  background: #131846;
}

.modal-header h2 {
  margin: 0;
  font-size: 22px;
  letter-spacing: 0;
}

.modal-close {
  width: 40px;
  height: 40px;
  border: 1px solid rgba(245, 195, 76, 0.52);
  border-radius: 8px;
  background: #27318a;
  color: var(--text);
  cursor: pointer;
  font-size: 24px;
  line-height: 1;
}

.rule-list {
  display: grid;
  gap: 10px;
  padding: 16px 18px 18px;
}

.rule-list section {
  border: 1px solid rgba(245, 195, 76, 0.34);
  border-radius: 8px;
  background: rgba(255, 255, 255, 0.06);
  padding: 12px;
}

.rule-list h3 {
  margin: 0 0 5px;
  font-size: 15px;
}

.rule-list p {
  margin: 0;
  color: var(--muted);
  line-height: 1.62;
}

.rule-list .payout-note {
  font-size: 11px;
  margin: 0 0 8px;
  color: rgba(255, 255, 255, 0.55);
}

.payout-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 8px 10px;
}

.payout-item {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 10px;
  border-radius: 8px;
  border: 1px solid rgba(255, 214, 102, 0.32);
  background:
    linear-gradient(180deg, rgba(255, 255, 255, 0.06), transparent 50%),
    rgba(28, 8, 43, 0.55);
}

.payout-symbol {
  flex: 0 0 auto;
  width: 48px;
  height: 48px;
  object-fit: contain;
  filter: drop-shadow(0 2px 6px rgba(0, 0, 0, 0.5));
}

.payout-amounts {
  flex: 1 1 auto;
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}

.payout-row {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 8px;
  font-size: 12px;
  font-weight: 800;
  line-height: 1.2;
}

.payout-row .cluster {
  color: #fff2a1;
  white-space: nowrap;
  font-size: 11px;
  letter-spacing: 0.02em;
}

.payout-row .amount {
  color: #ffffff;
  text-align: right;
  white-space: nowrap;
  text-shadow: 0 1px 0 rgba(49, 11, 76, 0.7), 0 0 8px rgba(255, 215, 0, 0.25);
}

/* Blocker row — visually distinct so players never mistake it for a
   paying symbol. Dashed border + muted palette + greyed icon. */
.payout-item.is-blocker {
  border-style: dashed;
  border-color: rgba(180, 180, 195, 0.45);
  background:
    linear-gradient(180deg, rgba(255, 255, 255, 0.025), transparent 50%),
    rgba(20, 18, 30, 0.5);
  opacity: 0.88;
}

.payout-item.is-blocker .payout-symbol {
  filter: drop-shadow(0 2px 6px rgba(0, 0, 0, 0.6)) saturate(0.6) brightness(0.92);
}

.payout-row--blocker {
  font-size: 12px;
}

.payout-row--blocker .cluster {
  color: #c8c9cf;  /* neutral grey, distinct from gold-yellow paying rows */
  letter-spacing: 0.04em;
}

.payout-row--blocker .amount {
  color: #c8c9cf;
  font-size: 18px;
  font-weight: 950;
  text-shadow: none;
  line-height: 1;
  letter-spacing: 0.02em;
}

.payout-row--note {
  margin-top: 1px;
}

.payout-row--note .cluster-note {
  color: rgba(200, 201, 207, 0.7);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.02em;
  white-space: nowrap;
}

/* Single-column fallback for very narrow screens */
@media (max-width: 380px) {
  .payout-grid {
    grid-template-columns: 1fr;
  }
}

.result-modal .result-panel {
  position: relative;
  max-height: min(720px, calc(100vh - 36px));
  overflow: visible;
  border: 3px solid #f5c34c;
  border-radius: 8px;
  background:
    radial-gradient(circle at 50% 0, rgba(61, 204, 255, 0.2), transparent 26%),
    radial-gradient(circle at 18% 28%, rgba(108, 255, 182, 0.11), transparent 30%),
    radial-gradient(circle at 82% 32%, rgba(178, 77, 255, 0.18), transparent 34%),
    linear-gradient(180deg, rgba(27, 63, 79, 0.96), rgba(12, 30, 38, 0.98) 58%, rgba(9, 24, 30, 0.98));
  text-align: center;
  box-shadow:
    0 0 0 1px rgba(255, 252, 190, 0.72),
    0 0 42px rgba(255, 204, 67, 0.42),
    0 32px 110px rgba(0, 0, 0, 0.6);
}

.result-modal .result-panel::before,
.result-modal .result-panel::after {
  content: "";
  position: absolute;
  left: 34px;
  right: 34px;
  height: 18px;
  z-index: 3;
  pointer-events: none;
  border-radius: 999px;
  background:
    linear-gradient(90deg, #9d5b12, #fff4a3 14%, #f5ad2c 34%, #fff7b8 50%, #f5ad2c 66%, #fff4a3 86%, #9d5b12);
  box-shadow:
    0 0 0 1px rgba(255, 249, 188, 0.88),
    0 0 22px rgba(255, 202, 62, 0.78);
}

.result-modal .result-panel::before {
  top: -10px;
}

.result-modal .result-panel::after {
  bottom: -10px;
}

.result-crest {
  position: absolute;
  left: 50%;
  top: -42px;
  z-index: 5;
  width: min(360px, 68vw);
  height: 88px;
  transform: translateX(-50%);
  pointer-events: none;
}

.result-crest::before {
  content: "";
  position: absolute;
  inset: 26px 0 0;
  clip-path: polygon(0 64%, 11% 36%, 28% 46%, 41% 20%, 50% 58%, 59% 20%, 72% 46%, 89% 36%, 100% 64%, 86% 78%, 64% 70%, 50% 88%, 36% 70%, 14% 78%);
  background:
    linear-gradient(180deg, rgba(255, 255, 255, 0.72), transparent 26%),
    linear-gradient(90deg, #a15e12, #ffd24f 22%, #fff5a6 50%, #ffd24f 78%, #a15e12);
  filter: drop-shadow(0 8px 12px rgba(0, 0, 0, 0.36));
}

.result-crest::after {
  content: "";
  position: absolute;
  left: 50%;
  top: 16px;
  width: 78px;
  height: 78px;
  transform: translateX(-50%) rotate(45deg);
  border: 4px solid #ffe38a;
  border-radius: 12px;
  background:
    radial-gradient(circle at 35% 28%, rgba(255, 255, 255, 0.78), transparent 18%),
    linear-gradient(135deg, #ecff98, #40e36c 58%, #138942);
  box-shadow:
    inset 0 0 0 4px rgba(28, 84, 35, 0.32),
    0 0 0 4px rgba(153, 84, 13, 0.92),
    0 0 28px rgba(127, 255, 125, 0.64);
}

.result-header {
  position: relative;
  z-index: 4;
  min-height: 104px;
  display: grid;
  place-items: end center;
  padding: 48px 58px 8px;
}

.result-header h2 {
  margin: 0;
  color: #31c8ff;
  font-family: Georgia, "Times New Roman", serif;
  font-size: clamp(28px, 6.8vw, 66px);
  font-weight: 950;
  line-height: 0.92;
  letter-spacing: 0;
  white-space: nowrap;
  text-shadow:
    0 4px 0 #9b4a12,
    0 -2px 0 #fff2a1,
    3px 0 0 #ffd34d,
    -3px 0 0 #ffd34d,
    0 0 22px rgba(62, 210, 255, 0.55),
    0 10px 18px rgba(0, 0, 0, 0.48);
}

.result-modal .modal-close {
  position: absolute;
  right: 16px;
  top: 16px;
  z-index: 7;
  border-color: rgba(255, 231, 124, 0.9);
  background: rgba(25, 47, 101, 0.9);
  box-shadow: 0 0 16px rgba(255, 204, 67, 0.32);
}

.result-body {
  position: relative;
  z-index: 2;
  display: grid;
  justify-items: center;
  gap: 14px;
  padding: 4px 18px 26px;
}

.result-body::before {
  content: "";
  position: absolute;
  left: 52px;
  right: 52px;
  top: -86px;
  bottom: 14px;
  z-index: -1;
  border-radius: 10px;
  background:
    linear-gradient(180deg, rgba(255, 255, 255, 0.05), transparent 20%),
    repeating-linear-gradient(90deg, rgba(255, 255, 255, 0.04) 0 1px, transparent 1px 52px),
    linear-gradient(180deg, rgba(11, 26, 34, 0.42), rgba(5, 17, 23, 0.34));
  box-shadow: inset 0 0 40px rgba(0, 0, 0, 0.42);
}

.result-won-label,
.result-spins-line {
  color: #fff2a1;
  font-family: "Yu Gothic", "Hiragino Sans", Meiryo, system-ui, sans-serif;
  font-weight: 950;
  line-height: 1;
  text-shadow:
    0 2px 0 rgba(0, 0, 0, 0.72),
    0 0 14px rgba(255, 217, 77, 0.56);
}

.result-won-label {
  font-size: clamp(24px, 5.4vw, 42px);
}

.result-win-plate {
  width: min(460px, calc(100vw - 88px));
  min-height: 70px;
  display: grid;
  place-items: center;
  overflow: hidden;
  padding: 0 8%;
  border: 2px solid #ffcf57;
  border-radius: 8px;
  background:
    radial-gradient(circle at 50% 0, rgba(255, 255, 255, 0.32), transparent 38%),
    linear-gradient(180deg, #3157d5, #143cba 54%, #092a86);
  box-shadow:
    inset 0 0 0 2px rgba(255, 255, 255, 0.24),
    0 0 0 1px rgba(96, 45, 4, 0.88),
    0 0 24px rgba(43, 115, 255, 0.72),
    0 0 24px rgba(255, 207, 87, 0.34);
}

.result-win-plate strong {
  color: #fff0a3;
  font-family: Georgia, "Times New Roman", serif;
  font-size: clamp(44px, 12vw, 78px);
  font-weight: 950;
  line-height: 1;
  letter-spacing: 0;
  text-shadow:
    0 3px 0 rgba(80, 45, 7, 0.88),
    0 0 10px rgba(255, 255, 255, 0.9),
    0 0 24px rgba(255, 220, 84, 0.72);
}

.result-spins-line {
  font-size: clamp(22px, 5.6vw, 38px);
}

.result-spins-line strong {
  color: #ffffff;
  font-size: 1.12em;
}

.result-action {
  width: auto;
  min-height: 32px;
  margin: 2px 0 0;
  padding: 4px 12px;
  border: 0;
  border-radius: 0;
  background: transparent;
  color: #ffffff;
  cursor: pointer;
  font-size: 15px;
  font-weight: 900;
  text-shadow: 0 2px 3px rgba(0, 0, 0, 0.78);
  box-shadow: none;
}

@media (max-width: 520px) {
  .result-modal {
    width: calc(100vw - 14px);
  }

  .result-modal .result-panel::before,
  .result-modal .result-panel::after {
    left: 22px;
    right: 22px;
  }

  .result-header {
    min-height: 88px;
    padding: 42px 46px 6px;
  }

  .result-body {
    gap: 11px;
    padding: 2px 12px 22px;
  }

  .result-body::before {
    left: 24px;
    right: 24px;
    top: -72px;
  }
}

.result-modal {
  width: min(430px, calc(100vw - 20px));
  max-width: none;
}

.result-modal::backdrop {
  background:
    radial-gradient(circle at 50% 42%, rgba(116, 41, 219, 0.34), transparent 38%),
    rgba(6, 3, 22, 0.78);
}

.result-modal .result-panel {
  position: relative;
  aspect-ratio: 430 / 255;
  max-height: min(66vh, 300px);
  width: min(430px, calc(100vw - 20px));
  overflow: visible;
  border: 0;
  border-radius: 0;
  background: transparent;
  box-shadow: 0 28px 90px rgba(0, 0, 0, 0.54);
}

.result-art {
  position: absolute;
  inset: 0;
  z-index: 1;
  width: 100%;
  height: 100%;
  object-fit: fill;
  pointer-events: none;
  user-select: none;
  filter: drop-shadow(0 18px 36px rgba(0, 0, 0, 0.42));
}

.result-crest,
.result-crest::before,
.result-crest::after,
.result-modal .result-panel::before,
.result-modal .result-panel::after {
  content: none;
  display: none;
}

.result-header {
  position: absolute;
  inset: 0;
  z-index: 4;
  min-height: 0;
  display: block;
  padding: 0;
  pointer-events: none;
}

.result-header h2 {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
}

.result-modal .modal-close {
  display: none;
}

.result-body {
  position: absolute;
  inset: 0;
  z-index: 3;
  display: block;
  padding: 0;
}

.result-body::before {
  content: "";
  position: absolute;
  left: 31%;
  right: 31%;
  top: 44.4%;
  z-index: 3;
  height: 8.2%;
  border-radius: 999px;
  background:
    radial-gradient(circle at 50% 50%, rgba(28, 45, 52, 0.98), rgba(5, 12, 16, 0.98) 72%);
  box-shadow: 0 0 20px rgba(0, 0, 0, 0.72);
}

.result-body::after {
  content: "";
  position: absolute;
  left: 29%;
  right: 29%;
  top: 73.2%;
  z-index: 3;
  height: 18.2%;
  border-radius: 999px;
  background:
    linear-gradient(180deg, rgba(10, 20, 24, 0.98), rgba(5, 12, 16, 0.98));
  box-shadow: 0 0 22px rgba(0, 0, 0, 0.7);
}

.result-won-label,
.result-spins-line,
.result-action {
  position: absolute;
  left: 50%;
  z-index: 4;
  transform: translateX(-50%);
  width: max-content;
  max-width: 60%;
  margin: 0;
  border: 0;
  border-radius: 999px;
  background: rgba(5, 12, 16, 0.98);
  color: #fff0a3;
  font-family: "Yu Gothic", "Hiragino Sans", Meiryo, system-ui, sans-serif;
  font-weight: 950;
  line-height: 1;
  letter-spacing: 0;
  text-align: center;
  text-shadow:
    0 2px 0 rgba(0, 0, 0, 0.84),
    0 0 12px rgba(255, 226, 90, 0.72);
  box-shadow: 0 0 18px rgba(0, 0, 0, 0.46);
}

.result-won-label {
  top: 46.1%;
  min-width: 30%;
  padding: 0.36em 1.7em 0.42em;
  font-size: clamp(18px, 3.1vw, 34px);
}

.result-win-plate {
  position: absolute;
  left: 50%;
  top: 57.2%;
  z-index: 4;
  width: 43.5%;
  min-height: 0;
  aspect-ratio: 5.4 / 1;
  display: grid;
  place-items: center;
  transform: translateX(-50%);
  border: 2px solid #ffe18a;
  border-radius: 5px;
  background:
    linear-gradient(135deg, rgba(255, 255, 255, 0.72) 0 4%, transparent 4% 96%, rgba(255, 255, 255, 0.42) 96%),
    radial-gradient(circle at 50% 0, rgba(255, 255, 255, 0.4), transparent 36%),
    linear-gradient(180deg, #3672ff 0%, #164dcf 52%, #092f99 100%);
  box-shadow:
    inset 0 0 0 2px rgba(255, 255, 255, 0.24),
    0 0 0 1px rgba(117, 59, 9, 0.88),
    0 0 26px rgba(38, 128, 255, 0.9),
    0 0 18px rgba(255, 216, 92, 0.46);
}

.result-win-plate strong {
  max-width: 100%;
  color: #fff0a3;
  font-family: Georgia, "Times New Roman", serif;
  /* Reduced to ×0.1 of the previous values per request (11→1.1). At this
     scale the sprite digits render at roughly 2px tall — essentially
     invisible on standard displays. Bump up if visibility is needed. */
  font-size: clamp(1px, 0.2vw, 1.1px);
  font-weight: 950;
  line-height: 0.94;
  letter-spacing: 0;
  white-space: nowrap;
  text-shadow:
    0 3px 0 rgba(82, 44, 6, 0.92),
    0 0 8px rgba(255, 255, 255, 0.88),
    0 0 20px rgba(255, 212, 75, 0.8);
}

.result-win-plate strong.is-long {
  font-size: clamp(1px, 0.15vw, 1px);
}

.result-win-plate strong.is-very-long {
  font-size: clamp(1px, 0.12vw, 1px);
}

/* When the result modal's number is rendered with sprite digits, the
   system-font text-shadow (drop + outer glow) no longer applies (the
   digits are background images, not text). Re-apply the same character
   via filter: drop-shadow so the bespoke gold numbers still get the
   plate's gold halo treatment. */
.result-win-plate strong .num-glyph,
.result-spins-line strong .num-glyph {
  filter:
    drop-shadow(0 2px 0 rgba(82, 44, 6, 0.85))
    drop-shadow(0 0 8px rgba(255, 255, 255, 0.55))
    drop-shadow(0 0 18px rgba(255, 212, 75, 0.65));
}

/* スプライト digit (.num-glyph) は vertical-align: middle で欧文 x-height 基準
   に整列されるため、和文「総回転数」と並べた際に視覚的に少し下にずれる。
   transform: translateY で digit を上に持ち上げて、和文と中心を合わせる。 */
.result-spins-line strong .num-glyph {
  transform: translateY(-0.18em);
}

.result-spins-line {
  top: 75.5%;
  min-width: 40%;
  padding: 0.32em 1.4em 0.4em;
  /* Reduced to ×0.1 of previous (9→0.9, rounded to 1px) per request. */
  font-size: clamp(1px, 0.1vw, 1px);
}

.result-spins-line strong {
  color: #ffffff;
  /* スプライト数字は 1.85em の高さで描画されるため、親 font-size に対して
     0.6em にすると digit ≒ 1.11em で「総回転数」テキストと釣り合うサイズになる。 */
  font-size: 0.6em;
}

.result-action {
  top: calc(100% + 8px);
  min-width: 34%;
  padding: 0.48em 1.45em 0.56em;
  background:
    linear-gradient(180deg, rgba(31, 49, 57, 0.98), rgba(5, 12, 16, 0.98));
  border: 2px solid rgba(255, 213, 83, 0.86);
  color: #ffffff;
  cursor: pointer;
  font-size: clamp(12px, 1.8vw, 20px);
  text-shadow: 0 2px 4px rgba(0, 0, 0, 0.9);
  box-shadow:
    0 0 0 1px rgba(255, 248, 188, 0.22),
    0 0 18px rgba(255, 200, 62, 0.34),
    0 12px 24px rgba(0, 0, 0, 0.36);
}

@media (max-width: 520px) {
  .result-modal,
  .result-modal .result-panel {
    width: calc(100vw - 20px);
  }

  .result-won-label {
    font-size: clamp(13px, 4.1vw, 20px);
  }

  /* Reduced ×0.1 from the previous compact size per request — sprite
     digits at 1.85em give ~2px effective digit, essentially invisible. */
  .result-win-plate strong {
    font-size: clamp(1px, 0.2vw, 1.1px);
  }

  .result-win-plate strong.is-long {
    font-size: clamp(1px, 0.15vw, 1px);
  }

  .result-win-plate strong.is-very-long {
    font-size: clamp(1px, 0.12vw, 1px);
  }

  .result-spins-line {
    font-size: clamp(1px, 0.1vw, 1px);
  }

  .result-action {
    top: calc(100% + 7px);
    min-width: 42%;
    font-size: clamp(10px, 3.2vw, 15px);
  }
}

.result-modal {
  width: min(430px, calc(100vw - 18px));
  overflow: visible;
}

.result-modal::backdrop {
  background:
    radial-gradient(circle at 50% 44%, rgba(91, 32, 173, 0.32), transparent 42%),
    rgba(3, 1, 12, 0.78);
}

.result-modal .result-panel {
  aspect-ratio: 4704 / 3238;
  width: min(430px, calc(100vw - 18px));
  max-height: min(70vh, 332px);
  border: 0;
  border-radius: 0;
  background: transparent;
  box-shadow: none;
}

.result-art {
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: contain;
  filter:
    drop-shadow(0 18px 34px rgba(0, 0, 0, 0.58))
    drop-shadow(0 0 22px rgba(255, 204, 67, 0.16));
}

.result-body::before,
.result-body::after,
.result-crest,
.result-crest::before,
.result-crest::after,
.result-modal .result-panel::before,
.result-modal .result-panel::after {
  content: none;
  display: none;
}

.result-won-label,
.result-spins-line,
.result-action {
  background: transparent;
  box-shadow: none;
  border: 0;
  border-radius: 0;
  color: #fff1a8;
  max-width: 82%;
  white-space: nowrap;
  text-shadow:
    0 3px 0 rgba(0, 0, 0, 0.88),
    0 0 12px rgba(255, 226, 90, 0.8),
    0 0 24px rgba(44, 164, 255, 0.32);
}

.result-won-label {
  top: 42.6%;
  min-width: 0;
  padding: 0;
  font-size: clamp(20px, 4.9vw, 36px);
}

.result-win-plate {
  top: 54.2%;
  width: 66%;
  aspect-ratio: 5.15 / 1;
  overflow: hidden;
  padding: 0 4%;
  border: 2px solid #ffdd76;
  border-radius: 5px;
  background:
    linear-gradient(135deg, rgba(255, 255, 255, 0.65) 0 4%, transparent 4% 96%, rgba(255, 255, 255, 0.34) 96%),
    radial-gradient(circle at 50% 0, rgba(255, 255, 255, 0.42), transparent 38%),
    linear-gradient(180deg, #3678ff 0%, #1749c7 54%, #082a88 100%);
  box-shadow:
    inset 0 0 0 2px rgba(255, 255, 255, 0.22),
    0 0 0 1px rgba(94, 45, 6, 0.9),
    0 0 23px rgba(38, 128, 255, 0.82),
    0 0 18px rgba(255, 216, 92, 0.42);
}

.result-win-plate strong {
  position: absolute;
  left: 50%;
  top: 50%;
  max-width: 90%;
  transform: translate(-50%, -50%);
  /* プレート (aspect 5.15:1) の高さは画面幅により 46〜55px。スプライト数字は
     1.85em なので、最大 font-size を 22px に抑えれば digit ≈ 41px となり、
     プレート内に余裕を持って収まる。 */
  font-size: clamp(14px, 3.4vw, 22px);
  line-height: 0.95;
}

.result-win-plate strong.is-long {
  font-size: clamp(12px, 2.8vw, 18px);
}

.result-win-plate strong.is-very-long {
  font-size: clamp(10px, 2.3vw, 15px);
}

.result-win-unit {
  position: absolute;
  right: 7.5%;
  top: 51%;
  transform: translateY(-50%);
  color: #fff0a3;
  font-family: Georgia, "Times New Roman", serif;
  font-size: clamp(14px, 3.3vw, 24px);
  font-weight: 950;
  line-height: 1;
  text-shadow:
    0 2px 0 rgba(80, 45, 7, 0.9),
    0 0 12px rgba(255, 241, 174, 0.78),
    0 0 18px rgba(18, 104, 255, 0.48);
}

.result-spins-line {
  top: 75.8%;
  min-width: 0;
  padding: 0;
  font-size: clamp(18px, 4.5vw, 32px);
}

.result-action {
  top: calc(100% + 10px);
  min-width: 0;
  padding: 0.62em 1.58em 0.7em;
  border: 1px solid rgba(255, 213, 83, 0.74);
  border-radius: 999px;
  background: rgba(5, 12, 16, 0.82);
  color: #ffffff;
  font-size: clamp(12px, 3.5vw, 18px);
  box-shadow:
    0 0 0 1px rgba(255, 248, 188, 0.14),
    0 0 18px rgba(255, 200, 62, 0.3),
    0 10px 20px rgba(0, 0, 0, 0.36);
}

@keyframes symbolDrop {
  0% {
    opacity: 1;
    transform: translate3d(0, var(--drop-distance, -180%), 0) scale(0.98, 1.05);
  }

  18% {
    opacity: 1;
  }

  64% {
    opacity: 1;
    transform: translate3d(0, 8.5%, 0) scale(0.99, 1.04);
  }

  78% {
    transform: translate3d(0, 2.5%, 0) scale(1.07, 0.94);
  }

  100% {
    opacity: 1;
    transform: translate3d(0, 0, 0) scale(1);
  }
}

@keyframes symbolFall {
  0% {
    opacity: 1;
    transform: translate3d(0, var(--drop-distance, -104%), 0) scale(1);
  }

  68% {
    opacity: 1;
    transform: translate3d(0, 7%, 0) scale(0.99, 1.03);
  }

  82% {
    transform: translate3d(0, 2%, 0) scale(1.04, 0.96);
  }

  100% {
    opacity: 1;
    transform: translate3d(0, 0, 0) scale(1);
  }
}

/* Gates of Olympus reel landing — FAST and SNAPPY.
   The previous longer/ease-out version felt floaty because the cell took
   too long to descend through the visible area. Real PP reels:
   - Slide quickly at LINEAR speed (no acceleration, no deceleration)
   - Total fall is ~540ms; visible portion is ~300ms
   - Bounce-back is SMALL and FAST (settles in ~200ms)
   With dropDuration shortened to ~540ms, this same structure reads as a
   decisive mechanical drop instead of a slow descent.

   Sequence:
   - 0%–60%   : single linear slide (constant velocity — sharp & crisp)
   - 60%      : impact (small overshoot + squash)
   - 60%–100% : tight quick bounce settle */
@keyframes reelSymbolDrop {
  0% {
    opacity: 1;
    transform: translate3d(0, var(--drop-distance, -180%), 0) scale(1);
    /* Pure linear fall — same speed throughout, like a motorised reel
       strip. No acceleration ramp-up, no deceleration into landing. */
    animation-timing-function: linear;
  }

  /* IMPACT — short overshoot with sharp squash. Smaller magnitudes than
     before because at this faster duration the eye reads smaller
     deformations as plenty of weight. */
  60% {
    opacity: 1;
    transform: translate3d(0, 3%, 0) scale(1.11, 0.88);
    animation-timing-function: cubic-bezier(0.3, 0.5, 0.5, 1);
  }

  /* SPRING-BACK — small quick upward rebound */
  74% {
    opacity: 1;
    transform: translate3d(0.25%, -1.4%, 0) scale(0.96, 1.045);
    animation-timing-function: cubic-bezier(0.4, 0.5, 0.55, 1);
  }

  /* Quick damped return — single tiny downward then settle */
  87% {
    opacity: 1;
    transform: translate3d(-0.1%, 0.35%, 0) scale(1.012, 0.988);
    animation-timing-function: ease-out;
  }

  /* Settle */
  100% {
    opacity: 1;
    transform: translate3d(0, 0, 0) scale(1);
  }
}

@keyframes reelSpinRoll {
  0% {
    opacity: 1;
    transform: translateY(0) scale(1);
  }

  58% {
    opacity: 0.62;
    transform: translateY(24%) scale(0.99, 1.04);
  }

  100% {
    opacity: 0;
    transform: translateY(68%) scale(0.98, 1.05);
  }
}

@keyframes reelSpinSheen {
  0% {
    opacity: 0;
    transform: translateY(-8%);
  }

  42% {
    opacity: 0.42;
  }

  100% {
    opacity: 0;
    transform: translateY(10%);
  }
}

@keyframes reelTumbleShade {
  0% {
    opacity: 0.35;
  }

  62% {
    opacity: 0.18;
  }

  100% {
    opacity: 0;
  }
}

@keyframes formulaStripFocus {
  0% {
    transform: translateY(0) scale(1);
    box-shadow:
      inset 0 0 0 1px rgba(255, 255, 255, 0.1),
      0 0 16px rgba(255, 226, 90, 0.2),
      0 8px 18px rgba(47, 10, 64, 0.2);
  }

  36% {
    transform: translateY(-2px) scale(1.018);
    box-shadow:
      inset 0 0 0 1px rgba(255, 255, 255, 0.18),
      0 0 34px rgba(255, 242, 161, 0.66),
      0 0 28px rgba(87, 204, 255, 0.28),
      0 12px 22px rgba(47, 10, 64, 0.26);
  }

  100% {
    transform: translateY(0) scale(1);
    box-shadow:
      inset 0 0 0 1px rgba(255, 255, 255, 0.1),
      0 0 22px rgba(255, 226, 90, 0.32),
      0 8px 18px rgba(47, 10, 64, 0.2);
  }
}

@keyframes formulaSubtotalPop {
  0% {
    transform: scale(0.94);
  }

  40% {
    transform: scale(1.08);
    box-shadow:
      inset 0 0 0 1px rgba(255, 255, 255, 0.22),
      0 0 25px rgba(87, 204, 255, 0.7),
      0 0 28px rgba(255, 242, 161, 0.5),
      0 8px 16px rgba(21, 6, 43, 0.32);
  }

  100% {
    transform: scale(1);
  }
}

@keyframes formulaBaseReveal {
  0% {
    opacity: 0.18;
    transform: translateY(4px) scale(0.92);
    filter: saturate(0.55) brightness(0.7);
  }
  55% {
    opacity: 1;
    transform: translateY(-2px) scale(1.1);
    filter: brightness(1.2);
  }
  100% {
    opacity: 1;
    transform: none;
    filter: none;
  }
}

@keyframes formulaMultiplierReveal {
  0% {
    opacity: 0.18;
    transform: translateY(5px) scale(0.84);
    filter: saturate(0.6) brightness(0.7);
  }
  42% {
    opacity: 1;
    transform: translateY(-3px) scale(1.22);
    filter: brightness(1.45);
  }
  72% {
    transform: translateY(0) scale(1.06);
    filter: brightness(1.18);
  }
  100% {
    opacity: 1;
    transform: none;
    filter: none;
  }
}

@keyframes formulaSubtotalReveal {
  0% {
    opacity: 0.18;
    transform: translateY(8px) scale(0.82);
    filter: brightness(0.78);
    box-shadow:
      inset 0 0 0 1px rgba(255, 255, 255, 0.12),
      0 0 0 rgba(87, 204, 255, 0),
      0 4px 10px rgba(21, 6, 43, 0.2);
  }
  38% {
    opacity: 1;
    transform: translateY(-4px) scale(1.2);
    filter: brightness(1.4);
    box-shadow:
      inset 0 0 0 1px rgba(255, 255, 255, 0.28),
      0 0 32px rgba(87, 204, 255, 0.78),
      0 0 30px rgba(255, 242, 161, 0.58),
      0 10px 18px rgba(21, 6, 43, 0.34);
  }
  68% {
    transform: translateY(0) scale(1.06);
    filter: brightness(1.18);
  }
  100% {
    opacity: 1;
    transform: none;
    filter: none;
    box-shadow:
      inset 0 0 0 1px rgba(255, 255, 255, 0.16),
      0 0 15px rgba(87, 204, 255, 0.42),
      0 5px 12px rgba(21, 6, 43, 0.28);
  }
}

@keyframes multiplierAuraDrift {
  0% {
    transform: scale(0.94);
    filter: blur(10px) brightness(0.95);
  }

  100% {
    transform: scale(1.06);
    filter: blur(9px) brightness(1.12);
  }
}

@keyframes multiplierIdlePulse {
  0%, 100% {
    transform: translateZ(0) scale(1);
  }

  50% {
    transform: translateZ(0) scale(1.045);
  }
}

@keyframes multiplierIdleGlow {
  0%, 100% {
    opacity: 0.85;
    transform: scale(1);
    box-shadow:
      0 0 6px rgba(86, 240, 224, 0.32),
      0 0 12px rgba(255, 240, 110, 0.2);
  }

  50% {
    opacity: 1;
    transform: scale(1.04);
    box-shadow:
      0 0 10px rgba(86, 240, 224, 0.55),
      0 0 18px rgba(255, 240, 110, 0.36);
  }
}

@keyframes multiplierHeatGentle {
  0%, 100% { transform: scale(1); filter: brightness(1); }
  50% { transform: scale(1.025); filter: brightness(1.1); }
}

@keyframes multiplierHeatBreath {
  0%, 100% { transform: scale(1); filter: brightness(1); }
  50% { transform: scale(1.05); filter: brightness(1.22); }
}

@keyframes multiplierHeatPulseFast {
  0%, 100% { transform: scale(1); filter: brightness(1); }
  50% { transform: scale(1.08); filter: brightness(1.32); }
}

/* Translate-jitter free heartbeat for the higher tiers — same dynamic feel
   as the old multiplierHeatInferno without the 1〜2px positional shake. */
@keyframes multiplierHeatBeat {
  0%   { transform: scale(1);    filter: brightness(1); }
  18%  { transform: scale(1.12); filter: brightness(1.42); }
  38%  { transform: scale(1.06); filter: brightness(1.24); }
  58%  { transform: scale(1.1);  filter: brightness(1.34); }
  78%  { transform: scale(1.04); filter: brightness(1.18); }
  100% { transform: scale(1);    filter: brightness(1); }
}

@keyframes multiplierHeatInferno {
  0%   { transform: scale(1); filter: brightness(1); }
  18%  { transform: translate(-1.6px, 1.2px) scale(1.1);  filter: brightness(1.42); }
  38%  { transform: translate(1.6px, -1px) scale(1.06);   filter: brightness(1.24); }
  58%  { transform: translate(-1.2px, 1.4px) scale(1.08); filter: brightness(1.34); }
  78%  { transform: translate(1.2px, -1.4px) scale(1.05); filter: brightness(1.18); }
  100% { transform: scale(1); filter: brightness(1); }
}

@keyframes multiplierSparkDrift {
  0%,
  100% {
    transform: translateX(-18%);
    filter: brightness(0.98);
  }

  50% {
    transform: translateX(18%);
    filter: brightness(1.22);
  }
}

@keyframes multiplierCardKick {
  0% {
    transform: scale(1);
  }

  42% {
    transform: scale(1.045);
  }

  100% {
    transform: scale(1);
  }
}

@keyframes multiplierLightningFlash {
  0% {
    opacity: 0;
    transform: translateX(-34%) scaleX(0.78);
  }

  26% {
    opacity: 0.94;
  }

  66% {
    opacity: 0.78;
    transform: translateX(14%) scaleX(1.05);
  }

  100% {
    opacity: 0;
    transform: translateX(34%) scaleX(0.92);
  }
}

@keyframes multiplierBoltSweep {
  0% {
    opacity: 0;
    transform: translateX(-58%);
  }

  22% {
    opacity: 0.72;
  }

  70% {
    opacity: 0.52;
    transform: translateX(18%);
  }

  100% {
    opacity: 0;
    transform: translateX(58%);
  }
}

@keyframes formulaMultiplierZap {
  0% {
    transform: scale(1);
    background: rgba(83, 213, 255, 0);
    box-shadow: none;
  }

  34% {
    transform: scale(1.045);
    background: rgba(83, 213, 255, 0.12);
    box-shadow:
      inset 0 -1px 0 rgba(166, 244, 255, 0.72),
      0 0 10px rgba(83, 213, 255, 0.42),
      0 0 14px rgba(255, 235, 120, 0.28);
  }

  100% {
    transform: scale(1);
    background: rgba(83, 213, 255, 0);
    box-shadow: none;
  }
}

@keyframes symbolBurst {
  /* Phase 1: anticipation — slight pulse forward */
  0%  { opacity: 1; transform: scale(1.02); filter: brightness(1.05); }
  25% { opacity: 1; transform: scale(1.06); filter: brightness(1.2); }

  /* Phase 2: compress before impact */
  42% { opacity: 1; transform: scale(0.94); filter: brightness(1.3); }

  /* Phase 3: IMPACT — synced with lightning peak */
  50% { opacity: 1; transform: scale(1.32); filter: brightness(2.6) saturate(0.4); }
  58% { opacity: 0.96; transform: scale(1.42); filter: brightness(2.1) saturate(1.18); }

  /* Phase 4: shatter & dissipate */
  78% { opacity: 0.7; transform: scale(1.58); filter: brightness(1.7) blur(1px); }
  100% { opacity: 0; transform: scale(1.78); filter: brightness(1.4) blur(3px); }
}

@keyframes clearingAura {
  0%  { opacity: 0; transform: scale(0.6); }
  30% { opacity: 0.42; transform: scale(0.96); filter: brightness(1.15); }

  /* IMPACT — huge bright bloom */
  50% { opacity: 1; transform: scale(1.7); filter: brightness(1.55); }
  62% { opacity: 0.78; transform: scale(1.5); filter: brightness(1.22); }

  100% { opacity: 0; transform: scale(2.1); filter: brightness(0.9); }
}

@keyframes symbolArtVanish {
  /* Phase 1: anticipation glow */
  0%  { filter: brightness(1.06) saturate(1); }
  25% { filter: brightness(1.45) saturate(0.85)
                 drop-shadow(0 0 4px rgba(255, 245, 180, 0.6)); }

  /* Phase 2: compress before impact */
  42% { filter: brightness(1.3) saturate(1.05)
                 drop-shadow(0 0 6px rgba(255, 232, 130, 0.5)); }

  /* Phase 3: WHITE FLASH IMPACT — synced with lightning peak */
  50% {
    filter:
      brightness(4.4) saturate(0.05)
      drop-shadow(0 0 18px rgba(255, 255, 255, 1))
      drop-shadow(0 0 38px rgba(255, 248, 210, 0.95));
  }

  /* Phase 4: golden recoil */
  60% {
    filter:
      brightness(2.5) saturate(1.2)
      drop-shadow(0 0 18px rgba(255, 232, 130, 0.9))
      drop-shadow(0 0 32px rgba(255, 196, 70, 0.65));
  }

  /* Phase 5: dissipate with blur and color drain */
  80% {
    filter:
      brightness(1.8) saturate(0.9) blur(1.5px)
      drop-shadow(0 0 14px rgba(255, 200, 110, 0.55));
  }
  100% {
    opacity: 0;
    filter: brightness(1.4) saturate(0.8) blur(4px);
  }
}

/* Debris particles flying out from the cell at the impact moment.
   Animated via multi-value box-shadow keyframes. */
@keyframes symbolDebris {
  /* Particles start invisible at center */
  0%, 42% {
    box-shadow:
      0 0 0 0 rgba(255, 248, 210, 0),
      0 0 0 0 rgba(255, 232, 130, 0),
      0 0 0 0 rgba(255, 248, 210, 0),
      0 0 0 0 rgba(255, 232, 130, 0),
      0 0 0 0 rgba(255, 248, 210, 0),
      0 0 0 0 rgba(255, 232, 130, 0),
      0 0 0 0 rgba(255, 248, 210, 0),
      0 0 0 0 rgba(255, 232, 130, 0);
  }
  /* IMPACT — particles burst out brightly */
  52% {
    box-shadow:
      -24px -22px 0 1.5px rgba(255, 252, 220, 0.95),
       26px -28px 0 2px   rgba(255, 240, 160, 0.92),
      -32px  10px 0 1.8px rgba(255, 232, 130, 0.92),
       30px  18px 0 1.5px rgba(255, 248, 210, 0.95),
      -18px  30px 0 1.6px rgba(255, 220, 110, 0.88),
       22px -10px 0 1.4px rgba(255, 200, 90, 0.88),
       -8px -36px 0 1.3px rgba(255, 252, 220, 0.9),
       12px  34px 0 1.4px rgba(255, 232, 130, 0.88);
  }
  /* Particles fly further and fade */
  100% {
    box-shadow:
      -58px -52px 0 0 rgba(255, 252, 220, 0),
       62px -64px 0 0 rgba(255, 240, 160, 0),
      -72px  24px 0 0 rgba(255, 232, 130, 0),
       68px  42px 0 0 rgba(255, 248, 210, 0),
      -42px  68px 0 0 rgba(255, 220, 110, 0),
       52px -28px 0 0 rgba(255, 200, 90, 0),
      -22px -78px 0 0 rgba(255, 252, 220, 0),
       30px  72px 0 0 rgba(255, 232, 130, 0);
  }
}

@keyframes lightningStrike {
  /* Build-up: the bolt drops from above and grows */
  0% {
    opacity: 0;
    transform: translateX(-50%) translateY(-36%) scaleY(0.5) scaleX(0.8);
    filter: brightness(1.2);
  }
  20% {
    opacity: 0.7;
    transform: translateX(-50%) translateY(-6%) scaleY(0.84) scaleX(0.92);
    filter: brightness(1.8)
      drop-shadow(0 0 8px rgba(255, 240, 180, 0.7));
  }
  35% {
    opacity: 0.9;
    transform: translateX(-50.5%) translateY(0) scaleY(1) scaleX(0.98);
    filter: brightness(2.4)
      drop-shadow(0 0 10px rgba(255, 248, 200, 0.9))
      drop-shadow(0 0 22px rgba(255, 222, 130, 0.7));
  }
  44% {
    /* Slight flicker right before the impact */
    opacity: 0.55;
    transform: translateX(-49%) translateY(0) scaleY(0.96) scaleX(0.96);
    filter: brightness(1.8)
      drop-shadow(0 0 6px rgba(255, 240, 180, 0.55));
  }

  /* IMPACT PEAK — synced with symbol's IMPACT phase at 50% */
  50% {
    opacity: 1;
    transform: translateX(-50%) translateY(0) scaleY(1.14) scaleX(1.06);
    filter:
      brightness(3.4)
      drop-shadow(0 0 14px rgba(255, 255, 220, 1))
      drop-shadow(0 0 28px rgba(255, 240, 150, 0.95))
      drop-shadow(0 0 48px rgba(255, 196, 70, 0.7));
  }

  /* Post-impact glow continues to feel the strike */
  60% {
    opacity: 0.92;
    transform: translateX(-50%) translateY(0) scaleY(1.06) scaleX(1.02);
    filter:
      brightness(2.6)
      drop-shadow(0 0 10px rgba(255, 248, 210, 0.85))
      drop-shadow(0 0 22px rgba(255, 222, 130, 0.7))
      drop-shadow(0 0 36px rgba(255, 196, 80, 0.5));
  }

  /* Fade away */
  78% {
    opacity: 0.45;
    transform: translateX(-50%) translateY(0) scaleY(1) scaleX(1);
    filter: brightness(1.8)
      drop-shadow(0 0 8px rgba(255, 220, 120, 0.5));
  }
  100% {
    opacity: 0;
    transform: translateX(-50%) translateY(0) scaleY(0.94) scaleX(0.92);
    filter: brightness(1.2);
  }
}

@keyframes multiplierAbsorb {
  0% {
    opacity: 0;
    transform: translate(-50%, -50%) scale(0.72);
  }

  18% {
    opacity: 1;
    transform: translate(-50%, -50%) scale(1.22);
  }

  72% {
    opacity: 1;
  }

  100% {
    opacity: 0;
    transform: translate(calc(-50% + var(--fly-x)), calc(-50% + var(--fly-y))) scale(0.68);
  }
}

/* Photon orb. Two phases:
   - is-phase-in: travels from a winning cluster cell to the board centre
                  (convergence — multiple photons collapse to a focal point)
   - is-phase-out: radiates from the board centre out to each locked
                   multiplier (release — energy disperses to its targets) */
.multiplier-unlock-photon {
  position: fixed;
  left: 0;
  top: 0;
  z-index: 21;
  width: 12px;
  height: 12px;
  border-radius: 999px;
  background:
    radial-gradient(circle at 50% 35%, rgba(255, 255, 255, 0.98), rgba(140, 240, 220, 0.95) 50%, rgba(60, 200, 200, 1) 100%);
  box-shadow:
    0 0 9px rgba(140, 240, 220, 0.95),
    0 0 20px rgba(80, 220, 220, 0.6),
    inset 0 1px 0 rgba(255, 255, 255, 0.55);
  pointer-events: none;
  opacity: 0;
}

.multiplier-unlock-photon.is-phase-in {
  animation: multiplierUnlockConverge var(--fly-duration, 420ms) cubic-bezier(0.5, 0.05, 0.6, 1) var(--fly-delay, 0ms) both;
}

.multiplier-unlock-photon.is-phase-out {
  animation: multiplierUnlockRadiate var(--fly-duration, 460ms) cubic-bezier(0.3, 0.05, 0.4, 1) var(--fly-delay, 0ms) both;
}

@keyframes multiplierUnlockConverge {
  0% {
    opacity: 0;
    transform: translate(-50%, -50%) scale(0.5);
    filter: brightness(1.4);
  }
  20% {
    opacity: 1;
    transform: translate(-50%, -50%) scale(1.3);
    filter: brightness(1.7);
  }
  88% {
    opacity: 1;
    transform: translate(calc(-50% + var(--fly-x)), calc(-50% + var(--fly-y))) scale(0.9);
    filter: brightness(1.5);
  }
  100% {
    opacity: 0;
    transform: translate(calc(-50% + var(--fly-x)), calc(-50% + var(--fly-y))) scale(0.4);
    filter: brightness(1.2);
  }
}

@keyframes multiplierUnlockRadiate {
  0% {
    opacity: 0;
    transform: translate(-50%, -50%) scale(0.6);
    filter: brightness(1.9);
  }
  18% {
    opacity: 1;
    transform: translate(-50%, -50%) scale(1.5);
    filter: brightness(2);
  }
  82% {
    opacity: 1;
    transform: translate(calc(-50% + var(--fly-x)), calc(-50% + var(--fly-y))) scale(1);
    filter: brightness(1.3);
  }
  100% {
    opacity: 0;
    transform: translate(calc(-50% + var(--fly-x)), calc(-50% + var(--fly-y))) scale(0.5);
    filter: brightness(1);
  }
}

/* Burst flash at the board centre between phases — the focal point where
   the converged energy is released back out to the multipliers. */
.multiplier-unlock-burst {
  position: fixed;
  left: 0;
  top: 0;
  z-index: 22;
  width: 80px;
  height: 80px;
  border-radius: 50%;
  pointer-events: none;
  transform: translate(-50%, -50%) scale(0);
  background:
    radial-gradient(circle at 50% 50%,
      rgba(255, 255, 255, 0.95) 0%,
      rgba(180, 250, 240, 0.85) 28%,
      rgba(100, 220, 220, 0.6) 55%,
      rgba(60, 180, 220, 0.3) 75%,
      transparent 100%);
  box-shadow:
    0 0 30px rgba(180, 250, 240, 0.9),
    0 0 60px rgba(100, 220, 220, 0.55);
  animation: multiplierUnlockBurst 360ms cubic-bezier(0.3, 0.7, 0.5, 1) forwards;
}

@keyframes multiplierUnlockBurst {
  0% {
    opacity: 0;
    transform: translate(-50%, -50%) scale(0.15);
    filter: brightness(1.5);
  }
  40% {
    opacity: 1;
    transform: translate(-50%, -50%) scale(1.4);
    filter: brightness(2);
  }
  100% {
    opacity: 0;
    transform: translate(-50%, -50%) scale(2.2);
    filter: brightness(1);
  }
}

/* Flying spin-charge orb: spawns from a cleared cell and lands on a pip. */
.spin-charge-fly {
  position: fixed;
  left: 0;
  top: 0;
  z-index: 22;
  width: 14px;
  height: 14px;
  border-radius: 999px;
  background:
    radial-gradient(circle at 50% 32%, rgba(255, 255, 255, 0.95), rgba(255, 232, 130, 0.95) 55%, rgba(255, 196, 70, 1) 100%);
  box-shadow:
    0 0 10px rgba(255, 232, 130, 0.95),
    0 0 22px rgba(255, 200, 80, 0.7),
    inset 0 1px 0 rgba(255, 255, 255, 0.6);
  pointer-events: none;
  opacity: 0;
  animation: spinChargeFly 760ms cubic-bezier(0.32, 0.4, 0.4, 1) var(--fly-delay, 0ms) both;
}

@keyframes spinChargeFly {
  0% {
    opacity: 0;
    transform: translate(-50%, -50%) scale(0.4);
    filter: brightness(1.4);
  }
  18% {
    opacity: 1;
    transform: translate(-50%, -50%) scale(1.55);
    filter: brightness(1.7);
  }
  82% {
    opacity: 1;
    transform: translate(calc(-50% + var(--fly-x)), calc(-50% + var(--fly-y))) scale(1);
    filter: brightness(1.2);
  }
  100% {
    opacity: 0;
    transform: translate(calc(-50% + var(--fly-x)), calc(-50% + var(--fly-y))) scale(0.6);
    filter: brightness(1);
  }
}

/* Entrance only — the label rises into view and STAYS visible.
   The previous version faded out at 100%, but the JS now controls the
   lifecycle (labels persist until clearWinAmountLabels runs after the
   multiplier badge has landed). */
@keyframes winPayoutFloat {
  0% {
    opacity: 0;
    transform: translateY(10px) scale(0.72);
    filter: brightness(1.45) blur(1px);
  }

  35% {
    opacity: 1;
    transform: translateY(0) scale(1.08);
    filter: brightness(1.35) blur(0);
  }

  100% {
    opacity: 1;
    transform: translateY(-2px) scale(1);
    filter: brightness(1.1);
  }
}

@keyframes scatterSymbolGlow {
  0% {
    filter:
      brightness(1.08)
      drop-shadow(0 0 7px rgba(255, 242, 161, 0.75));
    transform: translateZ(0) scale(1);
  }

  100% {
    filter:
      brightness(1.42)
      drop-shadow(0 0 14px rgba(255, 242, 161, 0.98))
      drop-shadow(0 0 24px rgba(86, 236, 204, 0.52));
    transform: translateZ(0) scale(1.08);
  }
}

@keyframes scatterCellVanish {
  0% {
    opacity: 1;
    transform: scale(1);
  }

  46% {
    opacity: 1;
    transform: scale(1.18);
  }

  100% {
    opacity: 0;
    transform: scale(1.54);
  }
}

@keyframes scatterLightBloom {
  0% {
    opacity: 0;
    transform: scale(0.72);
  }

  34% {
    opacity: 1;
    transform: scale(1.08);
  }

  100% {
    opacity: 0;
    transform: scale(1.68);
  }
}

@keyframes scatterSymbolVanish {
  0% {
    opacity: 1;
    filter:
      brightness(1.55)
      saturate(1.16)
      drop-shadow(0 0 12px rgba(255, 249, 198, 0.95))
      drop-shadow(0 0 22px rgba(86, 236, 204, 0.54));
  }

  58% {
    opacity: 0.9;
    filter:
      brightness(2.3)
      saturate(0.8)
      blur(0.5px)
      drop-shadow(0 0 18px rgba(255, 249, 198, 0.9));
  }

  100% {
    opacity: 0;
    filter:
      brightness(2.7)
      saturate(0.45)
      blur(3px)
      drop-shadow(0 0 28px rgba(255, 249, 198, 0.8));
  }
}

@keyframes multiplierTargetPulse {
  0% {
    transform: scale(1);
    filter: brightness(1);
  }

  48% {
    transform: scale(1.12);
    filter: brightness(1.55);
  }

  100% {
    transform: scale(1);
    filter: brightness(1.08);
  }
}

@keyframes winAddBadgePop {
  0% {
    opacity: 0;
    transform: translateX(-6px) scale(0.72);
  }

  24% {
    opacity: 1;
    transform: translateX(0) scale(1.12);
  }

  68% {
    opacity: 1;
    transform: translateX(0) scale(1);
  }

  100% {
    opacity: 0;
    transform: translateX(4px) scale(0.9);
  }
}

@keyframes winValueCountPulse {
  0% {
    transform: scale(1);
    text-shadow:
      0 2px 0 rgba(57, 17, 74, 0.6),
      0 0 18px rgba(255, 215, 0, 0.48);
  }

  42% {
    transform: scale(1.08);
    text-shadow:
      0 2px 0 rgba(57, 17, 74, 0.68),
      0 0 18px rgba(255, 242, 161, 0.88),
      0 0 28px rgba(87, 204, 255, 0.44);
  }

  100% {
    transform: scale(1);
    text-shadow:
      0 2px 0 rgba(57, 17, 74, 0.6),
      0 0 18px rgba(255, 215, 0, 0.48);
  }
}

@keyframes scatterAwardAppear {
  0% {
    opacity: 0;
    transform: scale(0.7) translateY(18px);
    filter: brightness(1.35) blur(1px);
  }

  24% {
    opacity: 1;
    transform: scale(1.08) translateY(0);
    filter: brightness(1.55) blur(0);
  }

  72% {
    opacity: 1;
    transform: scale(1);
  }

  100% {
    opacity: 1;
    transform: scale(1) translateY(0);
    filter: brightness(1.42);
  }
}

@keyframes scatterAwardHalo {
  0% {
    opacity: 0;
    transform: scale(0.62);
  }

  32% {
    opacity: 1;
    transform: scale(1.06);
  }

  100% {
    opacity: 0;
    transform: scale(1.42);
  }
}

@keyframes winPulse {
  0% {
    transform: scale(1);
    filter: brightness(1);
  }

  54% {
    transform: scale(1.08);
    filter: brightness(1.18);
  }

  100% {
    transform: scale(1);
    filter: brightness(1.08);
  }
}

@media (min-height: 850px) {
  .game-shell {
    min-height: 760px;
  }
}

@media (max-width: 420px) {
  .app-shell {
    padding: 0;
  }

  .game-shell {
    width: 100%;
    min-height: 100vh;
    border-radius: 0;
    border-left: 0;
    border-right: 0;
  }

  .game-screen {
    padding: 14px 12px 8px;
  }

  .temple-frame {
    margin-top: 12px;
    padding: 32px 2px 14px;
  }

  .calc-strip {
    min-height: 38px;
    margin: 0 2px;
    padding: 6px 8px 7px;
  }

  .calc-strip #calcFormula {
    gap: 4px;
  }

  .formula-label {
    font-size: 9px;
  }

  .formula-value {
    font-size: 13px;
  }

  .formula-operator {
    font-size: 14px;
  }

  .action-row {
    grid-template-columns: 40px minmax(0, 1fr) 96px;
    gap: 8px;
    padding: 10px;
  }

  .cell::before {
    box-shadow:
      0 0 0 2px rgba(241, 174, 58, 0.84),
      inset 0 -6px 10px rgba(0, 0, 0, 0.18),
      0 5px 9px rgba(10, 3, 30, 0.28);
  }
}

/* ---------------------------------------------------------------------------
   Ambient golden motes — soft cream/gold light particles rising from the
   bottom of the game-screen. Continuous, multi-layered, glow-bloom effect
   designed to read as divine/sacred ambience rather than confetti.

   Depth illusion comes from 5 size buckets, each with its own opacity peak
   and animation duration: small + dim + slow particles read as "far away",
   large + bright + faster ones read as "close". JS picks a bucket per
   particle weighted toward the dimmer/farther buckets so the foreground
   isn't crowded.

   Each particle uses translateX + translateY in vh (viewport-height) so
   the rise traverses the full screen height even when the layer is short.
   Three sway path variants give organic horizontal drift.
--------------------------------------------------------------------------- */
.mote-layer {
  position: absolute;
  inset: 0;
  overflow: hidden;
  pointer-events: none;
  z-index: 0;
}

.mote {
  position: absolute;
  bottom: -16px;
  left: var(--mote-x, 50%);
  width: var(--mote-size, 8px);
  height: var(--mote-size, 8px);
  margin-left: calc(var(--mote-size, 8px) * -0.5);
  pointer-events: none;
  will-change: transform, opacity;
  mix-blend-mode: screen;
  opacity: 0;
}

/* Halo body — soft round glow that forms the visible particle. */
.mote::before {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: 50%;
  background: radial-gradient(circle,
    rgba(var(--mote-rgb, 255, 245, 200), 1) 0%,
    rgba(var(--mote-rgb, 255, 230, 160), 0.78) 18%,
    rgba(var(--mote-rgb, 255, 215, 120), 0.32) 52%,
    rgba(var(--mote-rgb, 255, 200, 90), 0) 78%);
  filter:
    blur(var(--mote-blur, 0.3px))
    drop-shadow(0 0 var(--mote-glow, 8px) rgba(var(--mote-rgb, 255, 220, 130), 0.78));
}

/* 4-point sparkle cross — only applied to .has-cross particles (the larger
   foreground motes) so they read as defined points of divine light rather
   than ambient haze. Beams scale with --mote-size for size-consistent shine. */
.mote.has-cross::after {
  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
  width: calc(var(--mote-size, 8px) * 4.2);
  height: calc(var(--mote-size, 8px) * 4.2);
  transform: translate(-50%, -50%);
  background:
    /* horizontal ray */
    linear-gradient(90deg,
      transparent 0%,
      rgba(var(--mote-rgb, 255, 245, 200), 0) 30%,
      rgba(var(--mote-rgb, 255, 250, 215), 0.85) 50%,
      rgba(var(--mote-rgb, 255, 245, 200), 0) 70%,
      transparent 100%),
    /* vertical ray */
    linear-gradient(180deg,
      transparent 0%,
      rgba(var(--mote-rgb, 255, 245, 200), 0) 30%,
      rgba(var(--mote-rgb, 255, 250, 215), 0.85) 50%,
      rgba(var(--mote-rgb, 255, 245, 200), 0) 70%,
      transparent 100%);
  background-size:
    100% calc(var(--mote-size, 8px) * 0.18),
    calc(var(--mote-size, 8px) * 0.18) 100%;
  background-position: center;
  background-repeat: no-repeat;
  filter: blur(0.4px);
}

.mote.is-rising.path-a { animation: mote-rise-a var(--mote-duration, 12s) linear forwards; }
.mote.is-rising.path-b { animation: mote-rise-b var(--mote-duration, 12s) linear forwards; }
.mote.is-rising.path-c { animation: mote-rise-c var(--mote-duration, 12s) linear forwards; }

/* Optional slow twinkle on top of the rise — only added to bigger particles
   via JS for emphasis. */
.mote.is-rising.twinkle { animation: mote-rise-a var(--mote-duration, 12s) linear forwards,
                                     mote-twinkle 2.4s ease-in-out infinite; }

@keyframes mote-rise-a {
  0%   { transform: translate3d(0, 0, 0);              opacity: 0; }
  8%   { opacity: var(--mote-peak, 0.7); }
  25%  { transform: translate3d(8px,  -28vh, 0); }
  50%  { transform: translate3d(-6px, -56vh, 0); }
  75%  { transform: translate3d(10px, -83vh, 0); }
  92%  { opacity: var(--mote-peak, 0.7); }
  100% { transform: translate3d(2px,  -112vh, 0);      opacity: 0; }
}

@keyframes mote-rise-b {
  0%   { transform: translate3d(0, 0, 0);              opacity: 0; }
  8%   { opacity: var(--mote-peak, 0.7); }
  25%  { transform: translate3d(-10px, -28vh, 0); }
  50%  { transform: translate3d(8px,   -56vh, 0); }
  75%  { transform: translate3d(-12px, -83vh, 0); }
  92%  { opacity: var(--mote-peak, 0.7); }
  100% { transform: translate3d(-4px,  -112vh, 0);     opacity: 0; }
}

@keyframes mote-rise-c {
  0%   { transform: translate3d(0, 0, 0);              opacity: 0; }
  8%   { opacity: var(--mote-peak, 0.7); }
  25%  { transform: translate3d(6px,  -28vh, 0); }
  50%  { transform: translate3d(-9px, -56vh, 0); }
  75%  { transform: translate3d(6px,  -83vh, 0); }
  92%  { opacity: var(--mote-peak, 0.7); }
  100% { transform: translate3d(-2px, -112vh, 0);      opacity: 0; }
}

@keyframes mote-twinkle {
  0%, 100% { filter:
    blur(var(--mote-blur, 0.4px))
    drop-shadow(0 0 var(--mote-glow, 5px) rgba(var(--mote-rgb, 255, 225, 150), 0.55)); }
  50%      { filter:
    blur(calc(var(--mote-blur, 0.4px) * 1.4))
    drop-shadow(0 0 calc(var(--mote-glow, 5px) * 1.6) rgba(var(--mote-rgb, 255, 225, 150), 0.85)); }
}

@media (prefers-reduced-motion: reduce) {
  .mote.is-rising,
  .mote.is-rising.path-a,
  .mote.is-rising.path-b,
  .mote.is-rising.path-c,
  .mote.is-rising.twinkle {
    animation: none;
    opacity: 0;
  }
}

/* ---------------------------------------------------------------------------
   FB⑥: Light-trail effect for multiplier transfers.

   Replaces the previous "+N" / "xN" flying badges (.multiplier-fly,
   .mult-to-win-fly) with a glowing bezier-curved light trail and a moving
   bright head particle. Same visual vocabulary will be reused for FB② to
   bridge the centre-calculation count and the HUD total.

   Components:
   - `.light-trail-svg`: full-viewport SVG overlay containing the curved path.
     The path uses stroke-dashoffset animation to draw in from source to
     target. Drop-shadow gives the glow.
   - `.light-trail-head`: separate DOM element that rides the same bezier
     using CSS `offset-path`. Renders a bright nucleus + halo via radial
     gradient and blends additively (mix-blend-mode: screen).

   Tier colour variables match the existing multiplier heat tiers
   (0=green / 1=red / 2=gold / 3=rainbow) for the HUD→subtotal trail; the
   MP→HUD trail uses a single warm-gold palette.
--------------------------------------------------------------------------- */
.light-trail-svg {
  position: fixed;
  inset: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
  z-index: 20;
  overflow: visible;
}

.light-trail-svg path {
  fill: none;
  stroke-width: var(--trail-width, 3);
  stroke-linecap: round;
  stroke: var(--trail-stroke, rgba(255, 232, 158, 0.95));
  filter:
    drop-shadow(0 0 6px var(--trail-glow, rgba(255, 210, 110, 0.85)))
    drop-shadow(0 0 14px var(--trail-glow-outer, rgba(255, 190, 80, 0.55)));
  opacity: 0;
}

.light-trail-svg.is-drawing path {
  animation: trailDraw var(--trail-duration, 720ms) cubic-bezier(0.4, 0.05, 0.2, 1) forwards;
}

@keyframes trailDraw {
  0%   { stroke-dashoffset: var(--trail-length, 600); opacity: 0; }
  10%  { opacity: 0.95; }
  72%  { opacity: 0.9; }
  100% { stroke-dashoffset: 0; opacity: 0; }
}

.light-trail-head {
  position: fixed;
  left: 0;
  top: 0;
  width: var(--head-size, 14px);
  height: var(--head-size, 14px);
  border-radius: 50%;
  pointer-events: none;
  z-index: 21;
  background: radial-gradient(circle,
    rgba(255, 255, 248, 1) 0%,
    var(--head-core, rgba(255, 240, 180, 0.95)) 22%,
    var(--head-halo, rgba(255, 200, 90, 0.6)) 55%,
    rgba(255, 200, 90, 0) 90%);
  filter:
    blur(0.4px)
    drop-shadow(0 0 8px var(--head-glow, rgba(255, 210, 100, 0.85)))
    drop-shadow(0 0 18px var(--head-glow-outer, rgba(255, 180, 60, 0.55)));
  mix-blend-mode: screen;
  opacity: 0;
  transform: translate(-50%, -50%);
  will-change: offset-distance, opacity, transform;
  offset-rotate: 0deg;
  offset-anchor: center;
}

.light-trail-head.is-flying {
  animation: trailHeadRide var(--trail-duration, 720ms) cubic-bezier(0.4, 0.05, 0.2, 1) forwards;
}

@keyframes trailHeadRide {
  0%   { offset-distance: 0%;   opacity: 0;   transform: translate(-50%, -50%) scale(0.4); }
  10%  { offset-distance: 8%;   opacity: 1;   transform: translate(-50%, -50%) scale(1); }
  60%  { offset-distance: 60%;  opacity: 1;   transform: translate(-50%, -50%) scale(1.05); }
  90%  { offset-distance: 92%;  opacity: 1;   transform: translate(-50%, -50%) scale(1.2); }
  100% { offset-distance: 100%; opacity: 0;   transform: translate(-50%, -50%) scale(0.5); }
}

/* Tier colour palettes for the HUD→subtotal trail. */
.light-trail-svg.is-tier-0 { /* green */
  --trail-stroke: rgba(170, 245, 175, 0.95);
  --trail-glow: rgba(110, 220, 130, 0.85);
  --trail-glow-outer: rgba(80, 200, 110, 0.5);
}
.light-trail-svg.is-tier-1 { /* red */
  --trail-stroke: rgba(255, 180, 160, 0.95);
  --trail-glow: rgba(255, 110, 90, 0.92);
  --trail-glow-outer: rgba(255, 70, 50, 0.55);
}
.light-trail-svg.is-tier-2 { /* gold */
  --trail-stroke: rgba(255, 232, 158, 0.95);
  --trail-glow: rgba(255, 210, 90, 0.95);
  --trail-glow-outer: rgba(255, 190, 60, 0.6);
}
.light-trail-svg.is-tier-3 { /* rainbow */
  --trail-stroke: rgba(255, 255, 255, 0.96);
  --trail-glow: rgba(255, 255, 255, 0.9);
  --trail-glow-outer: rgba(220, 220, 255, 0.6);
}
.light-trail-svg.is-tier-3 path {
  animation: trailDraw var(--trail-duration, 720ms) cubic-bezier(0.4, 0.05, 0.2, 1) forwards,
             trailRainbowHue 1.2s linear infinite;
}
@keyframes trailRainbowHue {
  0%   { filter: drop-shadow(0 0 6px rgba(255, 110, 110, 0.85)) drop-shadow(0 0 14px rgba(255, 110, 110, 0.5)); }
  33%  { filter: drop-shadow(0 0 6px rgba(110, 255, 130, 0.85)) drop-shadow(0 0 14px rgba(110, 255, 130, 0.5)); }
  66%  { filter: drop-shadow(0 0 6px rgba(110, 180, 255, 0.85)) drop-shadow(0 0 14px rgba(110, 180, 255, 0.5)); }
  100% { filter: drop-shadow(0 0 6px rgba(255, 110, 110, 0.85)) drop-shadow(0 0 14px rgba(255, 110, 110, 0.5)); }
}

.light-trail-head.is-tier-0 { --head-core: rgba(200, 250, 200, 0.95); --head-halo: rgba(110, 220, 130, 0.7); --head-glow: rgba(120, 230, 140, 0.9); --head-glow-outer: rgba(80, 200, 110, 0.55); }
.light-trail-head.is-tier-1 { --head-core: rgba(255, 220, 200, 0.95); --head-halo: rgba(255, 120, 100, 0.75); --head-glow: rgba(255, 110, 90, 0.95); --head-glow-outer: rgba(255, 70, 50, 0.6); }
.light-trail-head.is-tier-2 { --head-core: rgba(255, 240, 180, 0.95); --head-halo: rgba(255, 210, 90, 0.78); --head-glow: rgba(255, 210, 90, 0.95); --head-glow-outer: rgba(255, 190, 60, 0.6); }
.light-trail-head.is-tier-3 { --head-core: rgba(255, 255, 255, 1);     --head-halo: rgba(255, 255, 255, 0.85); --head-glow: rgba(255, 255, 255, 0.9);  --head-glow-outer: rgba(220, 220, 255, 0.65); animation-name: trailHeadRide, trailHeadHueRotate; animation-duration: var(--trail-duration, 720ms), 1.2s; animation-iteration-count: 1, infinite; animation-timing-function: cubic-bezier(0.4, 0.05, 0.2, 1), linear; }
@keyframes trailHeadHueRotate { 0% { filter: blur(0.4px) drop-shadow(0 0 8px rgba(255, 110, 110, 0.9)) drop-shadow(0 0 18px rgba(255, 110, 110, 0.55)); } 33% { filter: blur(0.4px) drop-shadow(0 0 8px rgba(110, 255, 130, 0.9)) drop-shadow(0 0 18px rgba(110, 255, 130, 0.55)); } 66% { filter: blur(0.4px) drop-shadow(0 0 8px rgba(110, 180, 255, 0.9)) drop-shadow(0 0 18px rgba(110, 180, 255, 0.55)); } 100% { filter: blur(0.4px) drop-shadow(0 0 8px rgba(255, 110, 110, 0.9)) drop-shadow(0 0 18px rgba(255, 110, 110, 0.55)); } }

@media (prefers-reduced-motion: reduce) {
  .light-trail-svg.is-drawing path,
  .light-trail-head.is-flying { animation: none; opacity: 0; }
}

/* ---------------------------------------------------------------------------
   FB⑥-v2: Electric/lightning bolt for multiplier transfers. The impact SE
   is a tier-scaled thunder family, so the visual switches from a smooth
   light trail to a multi-stroke jagged bolt with branches, high-frequency
   flicker, and a spark burst at impact.

   Three coplanar SVG paths share the same `d` attribute (built in JS):
   - .bolt-glow: thickest, blurred, plasma-tinted (outer halo)
   - .bolt-mid: medium, lightly blurred, brighter (mid plasma)
   - .bolt-core: thin, sharp, white-hot (inner core)

   The core animation includes a steps()-based flicker so the bolt
   visibly stutters during the strike, reading as electrical not laser.
--------------------------------------------------------------------------- */
.elec-bolt-svg {
  position: fixed;
  inset: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
  z-index: 20;
  overflow: visible;
}

.elec-bolt-svg .bolt-glow {
  fill: none;
  stroke: var(--bolt-glow-color, rgba(255, 180, 70, 0.6));
  /* Per-tier stroke widths via CSS variables — see .is-tier-N below.
     --bolt-width-scale is an additional multiplier set inline by the JS
     helper so specific contexts (e.g. the climactic multiplier-apply bolt)
     can boost their widths beyond the tier base without affecting other
     bolts of the same tier. */
  stroke-width: calc(var(--bolt-glow-w, 7) * var(--bolt-width-scale, 1));
  stroke-linecap: round;
  stroke-linejoin: round;
  filter: blur(calc(var(--bolt-glow-blur, 3px) * var(--bolt-width-scale, 1)));
  opacity: 0;
}

.elec-bolt-svg .bolt-mid {
  fill: none;
  stroke: var(--bolt-mid-color, rgba(255, 230, 150, 0.92));
  stroke-width: calc(var(--bolt-mid-w, 3) * var(--bolt-width-scale, 1));
  stroke-linecap: round;
  stroke-linejoin: round;
  filter: blur(0.6px);
  opacity: 0;
}

.elec-bolt-svg .bolt-core {
  fill: none;
  stroke: rgba(255, 255, 250, 1);
  stroke-width: calc(var(--bolt-core-w, 1.4) * var(--bolt-width-scale, 1));
  stroke-linecap: round;
  stroke-linejoin: round;
  opacity: 0;
}

.elec-bolt-svg.is-striking .bolt-glow {
  animation: boltGlowFade var(--bolt-duration, 460ms) cubic-bezier(0.2, 0.0, 0.1, 1) forwards;
}
.elec-bolt-svg.is-striking .bolt-mid {
  animation: boltMidFade  var(--bolt-duration, 460ms) cubic-bezier(0.2, 0.0, 0.1, 1) forwards;
}
.elec-bolt-svg.is-striking .bolt-core {
  /* Core uses a fast flicker overlayed on the fade-out envelope. */
  animation: boltCoreFade var(--bolt-duration, 460ms) cubic-bezier(0.2, 0.0, 0.1, 1) forwards,
             boltFlicker  70ms steps(2, jump-none) 5;
}

@keyframes boltGlowFade {
  0%   { opacity: 0; }
  6%   { opacity: 1; }
  35%  { opacity: 0.9; }
  100% { opacity: 0; }
}
@keyframes boltMidFade {
  0%   { opacity: 0; }
  4%   { opacity: 1; }
  40%  { opacity: 0.7; }
  100% { opacity: 0; }
}
@keyframes boltCoreFade {
  0%   { opacity: 0; }
  4%   { opacity: 1; }
  28%  { opacity: 0.92; }
  100% { opacity: 0; }
}
@keyframes boltFlicker {
  0%, 100% { opacity: 1; }
  50%      { opacity: 0.45; }
}

/* Tier colour palettes — mirror the multiplier heat tiers.
   Stroke widths also scale with tier so higher heat produces a visibly
   chunkier, more violent bolt. Glow blur scales mildly so a thicker
   bolt still reads as plasma rather than a fat solid line. */
.elec-bolt-svg.is-tier-0 { /* GREEN — calm */
  --bolt-glow-color: rgba(110, 230, 140, 0.62);
  --bolt-mid-color: rgba(190, 250, 200, 0.92);
  --bolt-glow-w: 6;
  --bolt-mid-w: 3;
  --bolt-core-w: 1.4;
  --bolt-glow-blur: 2.8px;
}
.elec-bolt-svg.is-tier-1 { /* RED — hot */
  --bolt-glow-color: rgba(255, 110, 90, 0.68);
  --bolt-mid-color: rgba(255, 200, 180, 0.94);
  --bolt-glow-w: 11;
  --bolt-mid-w: 5.2;
  --bolt-core-w: 2.3;
  --bolt-glow-blur: 3.6px;
}
.elec-bolt-svg.is-tier-2 { /* GOLD — blazing (also MP→HUD default) */
  --bolt-glow-color: rgba(255, 180, 60, 0.7);
  --bolt-mid-color: rgba(255, 235, 150, 0.95);
  --bolt-glow-w: 18;
  --bolt-mid-w: 8;
  --bolt-core-w: 3.4;
  --bolt-glow-blur: 4.8px;
}
.elec-bolt-svg.is-tier-3 { /* RAINBOW — ultimate */
  --bolt-glow-color: rgba(255, 255, 255, 0.82);
  --bolt-mid-color: rgba(255, 255, 255, 0.96);
  --bolt-glow-w: 26;
  --bolt-mid-w: 11;
  --bolt-core-w: 4.6;
  --bolt-glow-blur: 6.2px;
}
.elec-bolt-svg.is-tier-3 .bolt-glow {
  animation: boltGlowFade var(--bolt-duration, 460ms) cubic-bezier(0.2, 0.0, 0.1, 1) forwards,
             boltRainbowHue 0.7s linear infinite;
}
@keyframes boltRainbowHue {
  /* Reference the per-tier blur AND the --bolt-width-scale multiplier so
     this animation cooperates with the inline scaling done by the JS
     helper (specifically the climactic multiplier-apply bolt). */
  0%   { filter: blur(calc(var(--bolt-glow-blur, 6.2px) * var(--bolt-width-scale, 1))) hue-rotate(0deg); }
  100% { filter: blur(calc(var(--bolt-glow-blur, 6.2px) * var(--bolt-width-scale, 1))) hue-rotate(360deg); }
}

/* Impact sparks — small bright particles bursting outward at the target. */
.elec-spark {
  position: fixed;
  left: 0;
  top: 0;
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: radial-gradient(circle,
    rgba(255, 255, 250, 1) 0%,
    rgba(255, 215, 120, 0.9) 35%,
    rgba(255, 180, 60, 0) 80%);
  filter: drop-shadow(0 0 5px var(--spark-glow, rgba(255, 200, 80, 0.95)));
  pointer-events: none;
  z-index: 22;
  mix-blend-mode: screen;
  opacity: 0;
  transform: translate(-50%, -50%);
  will-change: transform, opacity;
}

.elec-spark.is-bursting {
  animation: sparkBurst 380ms cubic-bezier(0.2, 0.55, 0.4, 1) forwards;
}

@keyframes sparkBurst {
  0%   { transform: translate(-50%, -50%) scale(0.6); opacity: 0; }
  10%  { opacity: 1; }
  100% {
    transform:
      translate(calc(-50% + var(--spark-x, 0px)),
                calc(-50% + var(--spark-y, 0px)))
      scale(0.2);
    opacity: 0;
  }
}

/* Spark tier tints — match the bolt that produced them. */
.elec-spark.is-tier-0 { --spark-glow: rgba(120, 230, 140, 0.95); background: radial-gradient(circle, rgba(245, 255, 250, 1) 0%, rgba(180, 245, 200, 0.9) 35%, rgba(110, 220, 130, 0) 80%); }
.elec-spark.is-tier-1 { --spark-glow: rgba(255, 110, 90, 0.95);  background: radial-gradient(circle, rgba(255, 250, 240, 1) 0%, rgba(255, 200, 180, 0.9) 35%, rgba(255, 110, 90, 0) 80%); }
.elec-spark.is-tier-2 { --spark-glow: rgba(255, 200, 80, 0.95);  /* default gold styling above */ }
.elec-spark.is-tier-3 { --spark-glow: rgba(255, 255, 255, 0.95); background: radial-gradient(circle, rgba(255, 255, 255, 1) 0%, rgba(255, 255, 255, 0.9) 35%, rgba(255, 255, 255, 0) 80%); }

@media (prefers-reduced-motion: reduce) {
  .elec-bolt-svg.is-striking .bolt-glow,
  .elec-bolt-svg.is-striking .bolt-mid,
  .elec-bolt-svg.is-striking .bolt-core,
  .elec-spark.is-bursting { animation: none; opacity: 0; }
}

/* ---------------------------------------------------------------------------
   ④ MP-photon — visual vocabulary for the MP→HUD collection moment.
   Lightning travels top→bottom by nature, so an upward-going bolt felt
   physically wrong. We swap to a golden photon orb that arcs up from
   each collected MP cell to the HUD multiplier card.

   Visual: small bright nucleus (radial gradient) with a layered glow,
   blended additively (screen) so it adds light to whatever's behind it.
   The orb rides a quadratic-bezier path via CSS `offset-path`, which is
   computed in JS per-photon to curve upward in a natural sweep.

   At the start of the animation the orb fades in and grows from a small
   seed, then briefly inflates as it lands at the HUD card (mirroring
   the existing card-absorbing flash that fires in parallel).

   Toggle via FB_MP_PHOTON_ENABLED at the top of js/game.js.
--------------------------------------------------------------------------- */
.mp-photon {
  position: fixed;
  left: 0;
  top: 0;
  width: var(--photon-size, 16px);
  height: var(--photon-size, 16px);
  border-radius: 50%;
  background: radial-gradient(circle,
    rgba(255, 255, 250, 1) 0%,
    rgba(255, 240, 178, 0.94) 20%,
    rgba(255, 215, 120, 0.55) 50%,
    rgba(255, 190, 80, 0) 82%);
  filter:
    blur(0.35px)
    drop-shadow(0 0 10px rgba(255, 220, 130, 0.92))
    drop-shadow(0 0 22px rgba(255, 190, 80, 0.55));
  mix-blend-mode: screen;
  pointer-events: none;
  z-index: 21;
  opacity: 0;
  transform: translate(-50%, -50%) scale(0.5);
  will-change: offset-distance, transform, opacity;
  offset-rotate: 0deg;
  offset-anchor: center;
}

.mp-photon.is-collecting {
  animation: mpPhotonRide var(--photon-duration, 520ms) cubic-bezier(0.42, 0, 0.45, 1) forwards;
}

@keyframes mpPhotonRide {
  0%   {
    offset-distance: 0%;
    opacity: 0;
    transform: translate(-50%, -50%) scale(0.45);
  }
  12%  {
    offset-distance: 8%;
    opacity: 1;
    transform: translate(-50%, -50%) scale(1);
  }
  62%  {
    offset-distance: 60%;
    opacity: 1;
    transform: translate(-50%, -50%) scale(1.08);
  }
  92%  {
    offset-distance: 94%;
    opacity: 1;
    transform: translate(-50%, -50%) scale(1.25);
  }
  100% {
    offset-distance: 100%;
    opacity: 0;
    /* Land state: a soft inflate-then-vanish that visually merges into
       the gem's absorbing flash. */
    transform: translate(-50%, -50%) scale(1.7);
  }
}

@media (prefers-reduced-motion: reduce) {
  .mp-photon.is-collecting { animation: none; opacity: 0; }
}

/* ---------------------------------------------------------------------------
   B案: Apply-Tame — anticipation phase before the multiplier-apply bolt
   strikes. Toggle from JS via FB_APPLY_TAME_ENABLED. Only tier 1+ adds
   this beat; tier 0 stays snappy.

   Restraint is the point. Premium "tame" looks subtle, not flashy:
   - Gem inhales (scale 1 → 1.06) and brightens softly. NO whole-screen
     vignette overlay (that reads as cheap "now charging" UI).
   - End-frame scale eases back to ~1.02 so the heat-pulse animation
     fired by the strike can hand off cleanly without a visible snap.
   - Audio carries the rest (layered low-pitched rumble in JS).

   To revert: set FB_APPLY_TAME_ENABLED = false in js/game.js (top of file).
   This block of CSS becomes dead code with no side effects.
--------------------------------------------------------------------------- */
.multiplier-card.is-charging .multiplier-gem {
  /* `forwards` holds the brightened end-state until the strike replaces
     it with .is-heat-pulse / .is-tier-up animations. The cubic-bezier
     accelerates slightly toward the end so the inhale feels like a draw
     of breath rather than a uniform ramp. */
  animation: gemCharging var(--tame-duration, 280ms) cubic-bezier(0.35, 0, 0.65, 1) forwards;
}

/* Rainbow tier keeps its idle rainbow shimmer ALONGSIDE the charging draw
   so the gem doesn't lose its colour cycle during the buildup. */
.multiplier-card.is-heat-3.is-charging .multiplier-gem {
  animation:
    gemRainbowGlow 1.6s ease-in-out infinite alternate,
    gemCharging var(--tame-duration, 280ms) cubic-bezier(0.35, 0, 0.65, 1) forwards;
}

@keyframes gemCharging {
  0%   {
    transform: scale(1);
    filter: brightness(1);
  }
  72%  {
    transform: scale(1.07);
    filter: brightness(1.32) drop-shadow(0 0 18px rgba(255, 224, 132, 0.78));
  }
  100% {
    /* End-state: held just slightly higher than baseline so the gem feels
       "poised" rather than back-at-rest. The strike's heat-pulse anim will
       lift this further. */
    transform: scale(1.03);
    filter: brightness(1.18) drop-shadow(0 0 12px rgba(255, 218, 120, 0.6));
  }
}

@media (prefers-reduced-motion: reduce) {
  .multiplier-card.is-charging .multiplier-gem,
  .multiplier-card.is-heat-3.is-charging .multiplier-gem {
    animation: none;
  }
}

/* ---------------------------------------------------------------------------
   ⑤ Custom Number Font — sprite sheet at image/game/symbols/number.png

   Source layout (2048×1536 sprite):
     Top row    (y=328-653):  digits 1 2 3 4 5  — "1" is narrower (144px)
     Bottom row (y=846-1165): digits 6 7 8 9 0

   Approach: each rendered digit is an inline-block element with
   height: 1em and width proportional to its source bounding box. The
   sprite is set as background-image with background-size scaled so
   that 1em on screen corresponds to the digit's source row height.

   Per-digit bg-position is the negative of the digit's top-left corner
   in the source, scaled by the same factor. Common unit: digit row
   height = 326 source-px ≡ 1em on screen.

   JS helper `renderCustomNumber(str)` emits one <span> per character;
   digits get .num-d-N classes, commas/other characters fall through
   to a small system-font glyph that visually sits at the digit baseline.

   Toggle via FB_CUSTOM_NUMBER_FONT in js/game.js. When false, JS just
   uses textContent so this CSS becomes dead weight (no side effects).
--------------------------------------------------------------------------- */
.num-glyph {
  display: inline-block;
  vertical-align: middle;
  /* Sprite digits are scaled up to 1.85em — bumped from 1.5em so the
     bespoke 3D gold artwork actually dominates the HUD. All width and
     bg-position values below use this same 1.85× source-derived ratio. */
  height: 1.85em;
  background-image: url("../image/game/symbols/number.png");
  background-repeat: no-repeat;
  /* Sprite native size 2048×1536, row height 326. Scaled so 326 src-px
     = 1.85em on screen:
       width  = 2048 / 326 × 1.85 em ≈ 11.620em
       height = 1536 / 326 × 1.85 em ≈ 8.717em                         */
  background-size: 11.620em 8.717em;
  pointer-events: none;
  user-select: none;
  /* Tight kerning so adjacent sprite glyphs visually connect. */
  margin: 0 -0.02em;
  /* Sharpness — the sprite source is large (2048px-wide) and gets
     downscaled to ~30-40px in the HUD. For DOWNSCALING, smooth bicubic
     filtering (the default `auto`) actually looks better than crisp-edges
     (which is nearest-neighbour and would produce blocky results).
     The GPU layer hints make sure the sprite is rasterised once per
     scale and cached, rather than recomputed per repaint. */
  image-rendering: auto;
  transform: translateZ(0);
  backface-visibility: hidden;
  will-change: transform;
}

/* Digit positions — each entry sets .num-d-N's width and bg-position
   based on its source bbox, multiplied by the 1.85em scaling factor.
     width                 = (source_width / 326) × 1.85 em
     background-position-x = -(source_left / 326) × 1.85 em
     background-position-y = -(source_top  / 326) × 1.85 em            */

.num-d-0 { width: 1.430em; background-position: -9.466em -4.812em; }
.num-d-1 { width: 0.817em; background-position: -1.089em -1.884em; }
.num-d-2 { width: 1.350em; background-position: -3.019em -1.861em; }
.num-d-3 { width: 1.316em; background-position: -5.152em -1.884em; }
.num-d-4 { width: 1.458em; background-position: -7.314em -1.884em; }
.num-d-5 { width: 1.339em; background-position: -9.510em -1.884em; }
.num-d-6 { width: 1.418em; background-position: -0.828em -4.812em; }
.num-d-7 { width: 1.396em; background-position: -2.951em -4.823em; }
.num-d-8 { width: 1.418em; background-position: -5.073em -4.834em; }
.num-d-9 { width: 1.384em; background-position: -7.298em -4.801em; }

/* ---------------------------------------------------------------------------
   獲得PT (#winValue) スプライト数字のオーバーライド。
   コンパクトな HUD カード仕様に合わせて、ベース font-size を直接縮小したため
   transform: scale は不要。視覚サイズ＝レイアウトサイズで一致しているので、
   カード枠は数字＋ピップにぴったり追従する。
--------------------------------------------------------------------------- */

/* "x" multiplication glyph — bespoke 3D gold from image/game/symbols/x.png.
   Source 633×474, content bbox (168-466, 77-402) = 299×326 src-px.

   The "x" is the multiplication operator (not a digit). In the "Nx"
   format used by the MP HUD and the MP symbol art, the "x" reads as a
   small unit suffix at the digit's bottom baseline — so it's both
   smaller AND bottom-aligned with the digits.
   - height: 1.0em (≈54% of the digit's 1.85em)
   - vertical-align: bottom so its bottom edge lines up with the digit row's
     bottom (digits use vertical-align: middle on a 1.85em line box; the
     line box bottom = the digit bottom in this layout).
   - All width/bg values rescaled by 1.0/1.85 ≈ 0.541 from the
     source-derived defaults. */
.num-x {
  background-image: url("../image/game/symbols/x.png");
  height: 1.0em;
  vertical-align: bottom;
  background-size: 1.942em 1.453em;
  background-position: -0.515em -0.236em;
  width: 0.917em;
  /* Subtle nudge: pull the x slightly closer to the trailing digit. */
  margin-left: -0.06em;
}

/* "+" sign glyph — bespoke 3D gold from image/game/symbols/+.png.
   Source 633×570, content bbox (127-503, 90-481) = 377×392 src-px.

   Used in subtotal labels like "+250 PT". Rendered at 1.6em (slightly
   smaller than digits' 1.85em so it reads as a sign, not a leading
   digit). Anchored to the content bbox so positioning is exact.
   Scale factor relative to content height: 1.6 / 392 (em per src-px). */
.num-plus {
  background-image: url("../image/game/symbols/+.png");
  height: 1.6em;
  background-size: 2.583em 2.327em;
  background-position: -0.518em -0.367em;
  width: 1.539em;
  margin-right: -0.04em;
}

/* Comma and non-digit characters — keep readable in system font, sized
   to sit at the visual baseline of the gold digits. */
.num-other {
  display: inline-block;
  vertical-align: middle;
  font-family: inherit;
  font-weight: 950;
  /* Slightly smaller than 1em so it doesn't tower above the digits. */
  font-size: 0.92em;
  line-height: 1;
  /* Inherit the text colour so commas / 'x' tone with the surrounding
     UI styling rather than picking up the gold of the sprite. */
  color: currentColor;
}
.num-other.is-comma {
  /* Pull the comma down so it sits below the digit baseline like a
     real punctuation comma. */
  vertical-align: bottom;
  padding: 0 0.04em;
}

@media (prefers-reduced-motion: reduce) {
  .num-glyph { background-image: none; }
}

/* ===========================================================================
   初回プレイ時オーバーレイ：マルチプライヤー（累計倍率）の仕組みを3ステップで解説。
   localStorage で1回限り表示制御（thunderheart_intro_seen_v1）。
   βテスターから「ゲーツオブオリンポス経験者でも仕組みが理解できなかった」という
   フィードバックを受けて追加。ローディングガイドだけでは伝わらない倍率の累積
   メカニクスを、独立した強調オーバーレイで明示する。
   =========================================================================== */
.intro-overlay {
  position: fixed;
  inset: 0;
  z-index: 90;
  display: grid;
  place-items: center;
  padding: 16px;
  background:
    radial-gradient(circle at 50% 38%, rgba(91, 32, 173, 0.32), transparent 44%),
    rgba(3, 1, 12, 0.82);
  opacity: 0;
  transition: opacity 320ms ease;
  pointer-events: none;
}

.intro-overlay.is-visible {
  opacity: 1;
  pointer-events: auto;
}

.intro-overlay[hidden] {
  display: none;
}

.intro-panel {
  width: min(420px, 100%);
  max-height: calc(100dvh - 32px);
  overflow-y: auto;
  padding: 20px 18px 22px;
  border: 2px solid #ffe18a;
  border-radius: 16px;
  background:
    linear-gradient(180deg, rgba(78, 28, 122, 0.94), rgba(28, 8, 50, 0.96));
  box-shadow:
    0 0 32px rgba(255, 216, 92, 0.45),
    0 24px 60px rgba(0, 0, 0, 0.5);
  text-align: center;
  color: #fff8df;
  transform: translateY(6px) scale(0.98);
  transition: transform 360ms cubic-bezier(0.16, 0.86, 0.24, 1);
}

.intro-overlay.is-visible .intro-panel {
  transform: translateY(0) scale(1);
}

.intro-title {
  margin: 0 0 4px;
  color: #fff2a1;
  font-size: 22px;
  font-weight: 950;
  text-shadow:
    0 2px 0 rgba(57, 17, 74, 0.6),
    0 0 16px rgba(255, 215, 0, 0.55);
}

.intro-subtitle {
  margin: 0 0 18px;
  color: #ffd9fa;
  font-size: 13px;
  font-weight: 700;
}

.intro-steps {
  display: grid;
  gap: 14px;
  margin-bottom: 20px;
}

.intro-step {
  position: relative;
  padding: 14px 12px 12px;
  border: 1px solid rgba(255, 226, 152, 0.55);
  border-radius: 12px;
  background:
    linear-gradient(180deg, rgba(255, 255, 255, 0.08), transparent 40%),
    rgba(20, 6, 36, 0.6);
}

.intro-step-num {
  position: absolute;
  top: -11px;
  left: 12px;
  width: 24px;
  height: 24px;
  border-radius: 50%;
  background: linear-gradient(180deg, #fff2a1, #f5c34c);
  color: #2c0a3e;
  font-size: 14px;
  font-weight: 950;
  line-height: 24px;
  text-align: center;
  box-shadow:
    0 0 12px rgba(255, 215, 0, 0.7),
    inset 0 0 0 1.5px rgba(154, 97, 32, 0.4);
}

.intro-step-art {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-direction: column;
  gap: 4px;
  margin-bottom: 8px;
  min-height: 58px;
}

.intro-mult {
  display: inline-block;
  color: #67aef6;
  font-family: Georgia, "Times New Roman", serif;
  font-size: 20px;
  font-weight: 900;
  text-shadow:
    0 1px 0 rgba(0, 0, 0, 0.4),
    0 0 8px rgba(103, 174, 246, 0.6);
}

.intro-mult--single {
  font-size: 40px;
  color: #ffe06b;
  text-shadow:
    0 2px 0 rgba(57, 17, 74, 0.6),
    0 0 12px rgba(255, 224, 107, 0.9),
    0 0 24px rgba(255, 215, 0, 0.55);
}

.intro-mult--total {
  font-size: 30px;
  color: #ffe06b;
  text-shadow:
    0 2px 0 rgba(57, 17, 74, 0.6),
    0 0 12px rgba(255, 224, 107, 0.9),
    0 0 24px rgba(255, 215, 0, 0.55);
}

.intro-mult-stack {
  display: inline-flex;
  align-items: center;
  gap: 4px;
}

.intro-mult-plus {
  color: #fff2a1;
  font-size: 16px;
  font-weight: 900;
  padding: 0 1px;
}

.intro-arrow {
  color: #fff2a1;
  font-size: 12px;
  line-height: 1;
  opacity: 0.85;
}

.intro-payout {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  padding: 6px 14px;
  border-radius: 999px;
  background:
    linear-gradient(180deg, rgba(47, 158, 217, 0.4), rgba(30, 73, 173, 0.5));
  border: 1px solid rgba(70, 184, 236, 0.55);
}

.intro-payout-base {
  color: #ffffff;
  font-size: 16px;
  font-weight: 900;
}

.intro-payout-x {
  color: #fff2a1;
  font-size: 18px;
  font-weight: 900;
}

.intro-step-title {
  color: #fff2a1;
  font-size: 14px;
  font-weight: 900;
  margin-bottom: 3px;
  text-shadow: 0 1px 0 rgba(0, 0, 0, 0.4);
}

.intro-step-text {
  color: #ffd9fa;
  font-size: 11px;
  font-weight: 700;
  line-height: 1.5;
}

.intro-cta {
  margin: 0 auto;
  padding: 12px 28px;
  border: 2px solid rgba(255, 213, 83, 0.86);
  border-radius: 999px;
  background:
    linear-gradient(180deg, rgba(31, 49, 57, 0.98), rgba(5, 12, 16, 0.98));
  color: #ffffff;
  font-family: inherit;
  font-size: 15px;
  font-weight: 900;
  letter-spacing: 0.04em;
  cursor: pointer;
  box-shadow:
    0 0 0 1px rgba(255, 248, 188, 0.22),
    0 0 18px rgba(255, 200, 62, 0.4),
    0 8px 18px rgba(0, 0, 0, 0.36);
  transition: transform 160ms ease, box-shadow 160ms ease;
}

.intro-cta:hover,
.intro-cta:focus-visible {
  outline: none;
  transform: translateY(-1px);
  box-shadow:
    0 0 0 1px rgba(255, 248, 188, 0.4),
    0 0 24px rgba(255, 200, 62, 0.6),
    0 10px 20px rgba(0, 0, 0, 0.42);
}

.intro-cta:active {
  transform: translateY(1px) scale(0.98);
}

/* イメージベースのマルチプライヤー表示（intro オーバーレイで使用）。
   テキストの "x2" "x10" よりも、実際のゲーム内 MP シンボル PNG を表示することで
   プレイヤーに「盤面で見るあのアイコン」と即座に紐付けてもらう。 */
.intro-mult-img {
  width: 42px;
  height: 42px;
  object-fit: contain;
  display: inline-block;
  vertical-align: middle;
  filter:
    drop-shadow(0 2px 0 rgba(0, 0, 0, 0.55))
    drop-shadow(0 0 6px rgba(255, 215, 0, 0.4));
}

.intro-mult-img--single {
  width: 92px;
  height: 92px;
  filter:
    drop-shadow(0 3px 0 rgba(0, 0, 0, 0.55))
    drop-shadow(0 0 14px rgba(255, 215, 0, 0.65))
    drop-shadow(0 0 26px rgba(255, 215, 0, 0.4));
  animation: introMultPulse 2.4s ease-in-out infinite;
}

.intro-mult-img--total {
  width: 74px;
  height: 74px;
  filter:
    drop-shadow(0 3px 0 rgba(0, 0, 0, 0.55))
    drop-shadow(0 0 14px rgba(255, 235, 100, 0.7));
  animation: introMultGlow 2.4s ease-in-out infinite alternate;
}

.intro-mult-img--inline {
  width: 52px;
  height: 52px;
}

/* 画像を入れたので stack の gap を少し広げて、シンボル同士が密接しないように */
.intro-mult-stack {
  gap: 8px;
}
.intro-mult-plus {
  font-size: 22px;
  padding: 0 2px;
}
.intro-step-art {
  min-height: 92px;
}

@keyframes introMultPulse {
  0%, 100% { transform: scale(1); }
  50%      { transform: scale(1.06); }
}

@keyframes introMultGlow {
  0% {
    filter:
      drop-shadow(0 3px 0 rgba(0, 0, 0, 0.55))
      drop-shadow(0 0 12px rgba(255, 215, 0, 0.5));
  }
  100% {
    filter:
      drop-shadow(0 3px 0 rgba(0, 0, 0, 0.55))
      drop-shadow(0 0 22px rgba(255, 235, 100, 0.9))
      drop-shadow(0 0 36px rgba(255, 215, 0, 0.45));
  }
}

@media (prefers-reduced-motion: reduce) {
  .intro-mult-img--single,
  .intro-mult-img--total {
    animation: none;
  }
}

/* ===========================================================================
   intro オーバーレイ：1メッセージ＋CSS合成シーン版（4秒ナラティブ）。
   βテスターの「Gates of Olympus 既プレイでも仕組みが分からなかった」FBを
   受けて、3ステップ版から「クラスター成立→消滅→倍率マスがロック解除→
   HUD累計倍率に吸収」を1シーンのループで見せる構成に刷新。
   旧 .intro-steps / .intro-step* / .intro-mult-* / .intro-subtitle 系は
   未使用だが、CSS としては残置（影響なし）。

   タイムライン (4s ループ):
     0-15%   静止：クラスター候補が並ぶ。倍率マスは「ロック状態」(グレースケール+暗)
     15-30%  クラスター発光（赤グロー強）
     30-45%  クラスター消滅（フェードアウト + スケール拡大）
     35-50%  倍率マス「ロック解除」(グレースケール→フルカラー、明るくフラッシュ)
     50-70%  倍率マスが浮き上がる（吸い込まれる気配）
     55-80%  光のトレイルが盤面→HUDジェムへ昇る
     75-88%  HUDジェムが受け取って一瞬大きく光る ＋ 値が x1 → x15 に切り替わる
     88-100% settle して次ループへ
   =========================================================================== */
.intro-caption {
  margin: 14px 4px 18px;
  color: #ffd9fa;
  font-size: 13px;
  font-weight: 700;
  line-height: 1.55;
  text-align: center;
}
.intro-caption strong {
  color: #fff2a1;
  font-weight: 950;
}

.intro-scene {
  position: relative;
  margin: 8px 0 4px;
  padding: 12px 12px 12px;
  border: 1px solid rgba(255, 226, 152, 0.4);
  border-radius: 12px;
  background:
    radial-gradient(ellipse at 50% 100%, rgba(255, 170, 60, 0.12), transparent 65%),
    linear-gradient(180deg, rgba(255, 255, 255, 0.06), transparent 35%),
    rgba(20, 6, 36, 0.65);
  overflow: hidden;
}

/* === HUD ジェムミニチュア === */
.intro-scene-hud {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  margin-bottom: 8px;
  position: relative;
  z-index: 3;
}
.intro-scene-gem {
  position: relative;
  width: 110px;
  display: inline-block;
}
.intro-scene-gem-art {
  display: block;
  width: 100%;
  height: auto;
  filter:
    drop-shadow(0 0 10px rgba(255, 210, 80, 0.7))
    drop-shadow(0 0 22px rgba(255, 210, 80, 0.4));
  animation: introGemPulse 4s ease-in-out infinite;
}
.intro-scene-gem-value {
  position: absolute;
  top: 48%;
  left: 50%;
  transform: translate(-50%, -50%);
  color: #fffae0;
  font-family: Georgia, "Times New Roman", serif;
  font-size: 17px;
  font-weight: 950;
  letter-spacing: 0.02em;
  text-shadow:
    -1.2px -1.2px 0 rgba(0, 0, 0, 0.95),
     1.2px -1.2px 0 rgba(0, 0, 0, 0.95),
    -1.2px  1.2px 0 rgba(0, 0, 0, 0.95),
     1.2px  1.2px 0 rgba(0, 0, 0, 0.95),
    0 0 10px rgba(255, 245, 200, 0.9),
    0 0 18px rgba(255, 215, 80, 0.55);
  z-index: 2;
  pointer-events: none;
}
.intro-scene-gem-value--before {
  animation: introGemValueOut 4s ease-in-out infinite;
}
.intro-scene-gem-value--after {
  opacity: 0;
  animation: introGemValueIn 4s ease-in-out infinite;
}
.intro-scene-hud-label {
  color: #ffd9fa;
  font-size: 11px;
  font-weight: 900;
  letter-spacing: 0.06em;
  margin-top: 2px;
}

/* === 盤面→HUD 吸収トレイル === */
.intro-scene-trails {
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 2;
}
.intro-scene-trail {
  position: absolute;
  width: 3px;
  background: linear-gradient(180deg,
    rgba(255, 235, 130, 0) 0%,
    rgba(255, 235, 130, 0.95) 35%,
    rgba(255, 235, 130, 0) 100%);
  filter: drop-shadow(0 0 6px rgba(255, 215, 60, 0.95));
  border-radius: 3px;
  opacity: 0;
}
.intro-scene-trail--a {
  /* x5 マス → HUD ジェム下端 */
  top: 92px;
  height: 60px;
  left: calc(50% + 36px);
  animation: introTrailFlow 4s ease-in-out infinite;
  animation-delay: 0.05s;
}
.intro-scene-trail--b {
  /* x10 マス → HUD ジェム下端 */
  top: 92px;
  height: 90px;
  left: calc(50% + 100px);
  animation: introTrailFlow 4s ease-in-out infinite;
  animation-delay: 0.35s;
}

/* === ミニ盤面 === */
.intro-scene-board {
  position: relative;
  z-index: 1;
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  gap: 3px;
  padding: 5px;
  border: 2px solid #d9a13a;
  border-radius: 6px;
  background:
    linear-gradient(180deg, rgba(255, 255, 255, 0.06), transparent 30%, rgba(0, 0, 0, 0.22)),
    linear-gradient(180deg, #5b2766, #38184c 56%, #240733);
  box-shadow:
    inset 0 0 0 1px rgba(255, 247, 193, 0.3),
    inset 0 -8px 18px rgba(0, 0, 0, 0.36);
}

.intro-scene-cell {
  position: relative;
  aspect-ratio: 1 / 1;
  display: grid;
  place-items: center;
  border-radius: 4px;
}
.intro-scene-cell img {
  width: 86%;
  height: 86%;
  object-fit: contain;
  filter: drop-shadow(0 2px 3px rgba(0, 0, 0, 0.55));
}

/* === クラスター（同色8個）の発光 → 消滅 === */
.intro-scene-cell.is-cluster {
  animation: introClusterBgGlow 4s ease-in-out infinite;
}
.intro-scene-cell.is-cluster img {
  animation: introClusterClear 4s ease-in-out infinite;
}

/* === 倍率マス：ロック状態 → 解除 → 上昇吸収 === */
.intro-scene-cell.is-mult {
  position: relative;
  animation: introMultBgGlow 4s ease-in-out infinite;
}
/* ロック表現：image/game/multipliers/chain.png を倍率マスにオーバーレイ。
   解除タイミングで鎖が砕ける（スケール拡大＋回転＋フェード）。 */
.intro-scene-cell.is-mult::after {
  content: "";
  position: absolute;
  inset: 4%;
  background:
    url("../image/game/multipliers/chain.png") center / contain no-repeat,
    radial-gradient(circle at 50% 50%, rgba(0, 0, 0, 0.4) 0%, rgba(0, 0, 0, 0.15) 70%, transparent 100%);
  pointer-events: none;
  filter:
    drop-shadow(0 1px 2px rgba(0, 0, 0, 0.85))
    drop-shadow(0 0 3px rgba(0, 0, 0, 0.6));
  animation: introMultLockBadge 4s ease-in-out infinite;
  transform-origin: center center;
}
.intro-scene-cell.is-mult img {
  animation: introMultUnlock 4s ease-in-out infinite;
}

/* === アニメ === */

/* クラスター背景の赤グロー（光って消える） */
@keyframes introClusterBgGlow {
  0%, 12%, 100% {
    background: radial-gradient(circle at 50% 50%, rgba(255, 90, 80, 0.1) 0%, transparent 70%);
  }
  20%, 28% {
    background: radial-gradient(circle at 50% 50%, rgba(255, 140, 110, 0.7) 0%, transparent 70%);
  }
  40% {
    background: radial-gradient(circle at 50% 50%, rgba(255, 180, 130, 0.5) 0%, transparent 80%);
  }
  50%, 92% {
    background: radial-gradient(circle at 50% 50%, rgba(255, 90, 80, 0) 0%, transparent 70%);
  }
}

/* クラスターのシンボル本体：膨らんで弾けて消える */
@keyframes introClusterClear {
  0%, 12% {
    opacity: 1;
    transform: scale(1);
    filter: drop-shadow(0 2px 3px rgba(0, 0, 0, 0.55));
  }
  22% {
    opacity: 1;
    transform: scale(1.08);
    filter:
      drop-shadow(0 0 6px rgba(255, 130, 100, 0.95))
      drop-shadow(0 0 12px rgba(255, 130, 100, 0.6));
  }
  35% {
    opacity: 0.6;
    transform: scale(1.25);
    filter:
      drop-shadow(0 0 8px rgba(255, 180, 130, 0.85));
  }
  45%, 88% {
    opacity: 0;
    transform: scale(1.4);
  }
  95% {
    opacity: 0;
    transform: scale(0.92);
  }
  100% {
    opacity: 1;
    transform: scale(1);
  }
}

/* 倍率マス背景：ロック中暗め → 解除後明るく → 消える */
@keyframes introMultBgGlow {
  0%, 30%, 100% {
    background: radial-gradient(circle at 50% 50%, rgba(80, 80, 80, 0.25) 0%, transparent 70%);
  }
  38%, 50% {
    background: radial-gradient(circle at 50% 50%, rgba(255, 215, 60, 0.55) 0%, transparent 70%);
  }
  70%, 88% {
    background: radial-gradient(circle at 50% 50%, rgba(255, 215, 60, 0) 0%, transparent 70%);
  }
}

/* 倍率マスの鎖（chain.png）：解除タイミングで揺れて → 砕けて飛び散る感じ */
@keyframes introMultLockBadge {
  0%, 30% {
    opacity: 0.95;
    transform: scale(1) rotate(0deg);
    filter:
      drop-shadow(0 1px 2px rgba(0, 0, 0, 0.85))
      drop-shadow(0 0 3px rgba(0, 0, 0, 0.6));
  }
  34% {
    opacity: 1;
    transform: scale(1.04) rotate(-3deg);
    filter:
      drop-shadow(0 1px 2px rgba(0, 0, 0, 0.85))
      drop-shadow(0 0 10px rgba(255, 235, 130, 0.7));
  }
  40% {
    opacity: 0.7;
    transform: scale(1.18) rotate(6deg);
    filter:
      drop-shadow(0 0 14px rgba(255, 240, 140, 0.9))
      drop-shadow(0 0 22px rgba(255, 215, 80, 0.6));
  }
  46% {
    opacity: 0.3;
    transform: scale(1.4) rotate(14deg);
    filter:
      drop-shadow(0 0 18px rgba(255, 240, 140, 0.7));
  }
  52%, 100% {
    opacity: 0;
    transform: scale(1.6) rotate(22deg);
  }
}

/* 倍率シンボル：ロック状態(グレースケール暗) → 解除フラッシュ → 上昇 → 消失 */
@keyframes introMultUnlock {
  0%, 30% {
    opacity: 1;
    transform: scale(0.92) translateY(0);
    filter:
      grayscale(0.7) brightness(0.55)
      drop-shadow(0 2px 3px rgba(0, 0, 0, 0.55));
  }
  38% {
    opacity: 1;
    transform: scale(1.15) translateY(0);
    filter:
      grayscale(0) brightness(1.5)
      drop-shadow(0 0 10px rgba(255, 235, 130, 1))
      drop-shadow(0 0 20px rgba(255, 215, 60, 0.7));
  }
  50% {
    opacity: 1;
    transform: scale(1) translateY(0);
    filter:
      grayscale(0) brightness(1.1)
      drop-shadow(0 0 6px rgba(255, 215, 60, 0.9));
  }
  65% {
    opacity: 0.95;
    transform: scale(1.04) translateY(-8px);
    filter:
      grayscale(0) brightness(1.15)
      drop-shadow(0 0 8px rgba(255, 215, 60, 0.9));
  }
  78% {
    opacity: 0.5;
    transform: scale(0.95) translateY(-22px);
    filter:
      grayscale(0) brightness(1.1)
      drop-shadow(0 0 10px rgba(255, 215, 60, 0.7));
  }
  88%, 100% {
    opacity: 0;
    transform: scale(0.85) translateY(-30px);
    filter:
      grayscale(0.5) brightness(0.6)
      drop-shadow(0 2px 3px rgba(0, 0, 0, 0.55));
  }
}

/* トレイル：倍率解除のタイミングで盤面から HUD ジェムへ昇る */
@keyframes introTrailFlow {
  0%, 50% {
    opacity: 0;
    transform: translateY(20px) scaleY(0.3);
  }
  60% {
    opacity: 1;
    transform: translateY(0) scaleY(1);
  }
  78% {
    opacity: 1;
    transform: translateY(-32px) scaleY(0.8);
  }
  90%, 100% {
    opacity: 0;
    transform: translateY(-65px) scaleY(0.2);
  }
}

/* HUD ジェム：トレイル到達時に大きく光る */
@keyframes introGemPulse {
  0%, 78%, 100% {
    filter:
      drop-shadow(0 0 10px rgba(255, 210, 80, 0.7))
      drop-shadow(0 0 22px rgba(255, 210, 80, 0.4));
    transform: scale(1);
  }
  86% {
    filter:
      drop-shadow(0 0 22px rgba(255, 245, 150, 1))
      drop-shadow(0 0 40px rgba(255, 220, 90, 0.85));
    transform: scale(1.1);
  }
  92% {
    filter:
      drop-shadow(0 0 16px rgba(255, 230, 120, 0.9))
      drop-shadow(0 0 30px rgba(255, 220, 90, 0.6));
    transform: scale(1.04);
  }
}

/* HUD ジェムの値：x1 → x15 に切り替わる */
@keyframes introGemValueOut {
  0%, 80% { opacity: 1; }
  84%     { opacity: 0; }
  100%    { opacity: 1; }
}
@keyframes introGemValueIn {
  0%, 80% {
    opacity: 0;
    transform: translate(-50%, -50%) scale(0.9);
  }
  86% {
    opacity: 1;
    transform: translate(-50%, -50%) scale(1.25);
  }
  93% {
    opacity: 1;
    transform: translate(-50%, -50%) scale(1);
  }
  100% {
    opacity: 0;
    transform: translate(-50%, -50%) scale(0.9);
  }
}

@media (prefers-reduced-motion: reduce) {
  .intro-scene-cell.is-cluster,
  .intro-scene-cell.is-cluster img,
  .intro-scene-cell.is-mult,
  .intro-scene-cell.is-mult::after,
  .intro-scene-cell.is-mult img,
  .intro-scene-trail,
  .intro-scene-gem-art,
  .intro-scene-gem-value--before,
  .intro-scene-gem-value--after {
    animation: none !important;
  }
  .intro-scene-gem-value--after {
    opacity: 1;
  }
  .intro-scene-gem-value--before {
    opacity: 0;
  }
}
