/* =====================================================================
   PRIZE WHEEL
   ===================================================================== */
.wheel-overlay {
  position: fixed;
  inset: 0;
  z-index: 11600;
  display: none;
  align-items: center;
  justify-content: center;
  padding: 1.2rem;
  background: rgba(0, 0, 0, 0.88);
}
.wheel-overlay.open { display: flex; }

.wheel-panel {
  width: min(34rem, 100%);
  max-height: 92vh;
  overflow-y: auto;
  padding: 1.4rem;
  text-align: center;
  border-radius: var(--radius-md);
  background: linear-gradient(180deg, #1a0f2e, #080410);
  border: 2px solid var(--gold);
  box-shadow: 0 0 46px rgba(255, 217, 77, 0.32);
}
.wheel-title {
  font-family: 'Orbitron', sans-serif;
  color: var(--gold);
  letter-spacing: 0.1em;
}
.wheel-sub { color: var(--text-dim); font-size: 0.82rem; margin-bottom: 1rem; }

.wheel-stage {
  position: relative;
  width: min(20rem, 74vw);
  aspect-ratio: 1;
  margin: 0 auto 1rem;
}

/* The pointer sits at the top and does not move; the disc rotates
   underneath it, which is how a real wheel reads. */
.wheel-pointer {
  position: absolute;
  top: -10px;
  left: 50%;
  transform: translateX(-50%);
  width: 0;
  height: 0;
  border-left: 14px solid transparent;
  border-right: 14px solid transparent;
  border-top: 26px solid var(--gold);
  filter: drop-shadow(0 3px 5px rgba(0, 0, 0, 0.8));
  z-index: 3;
}

.wheel-disc {
  position: relative;
  width: 100%;
  height: 100%;
  border-radius: 50%;
  border: 6px solid var(--gold);
  box-shadow: 0 0 30px rgba(255, 217, 77, 0.35), inset 0 0 40px rgba(0, 0, 0, 0.55);
  /* Set from JS; the transition is added at spin time so the initial
     paint does not animate from zero. */
  transform: rotate(0deg);
}

/* THE SPOKE-AND-TOKEN LAYOUT LIVED HERE, AND IS GONE.

   126 lines used to place twelve prizes by rotating a full-size absolute
   layer per seat, riding a token at a fixed percentage down it and
   counter-rotating that token to keep it upright. It was the best you
   can do over a conic gradient, which has no geometry to attach to — but
   it could only ever place things by proportion, never by measurement,
   so a 5.4deg slice got the same treatment as a 48deg one and its
   caption came out wider than its own wedge.

   The face is an <svg> now (see wheel-fx.css). Wedges are explicit arc
   paths, captions are measured against the room they have, and the
   geometry lives in viewBox units in wheel.js rather than being split
   across two stylesheets that had to agree.

   Removed: .wheel-labels, .wheel-slice-label, .wheel-slice-inner,
   .wheel-slice-upright, .wheel-slice-token, .wheel-slice-token img,
   .wheel-slice-text. Nothing builds those elements any more. */

.wheel-hub {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 27%;
  aspect-ratio: 1;
  border-radius: 50%;
  background: #0b0714;
  border: 3px solid var(--gold);
  display: grid;
  place-items: center;
  z-index: 2;
}
.wheel-hub img { width: 74%; height: 74%; object-fit: contain; }

.wheel-result {
  display: flex;
  flex-direction: column;
  gap: 2px;
  margin-bottom: 0.9rem;
}
.wheel-result-label {
  font-family: 'Orbitron', sans-serif;
  font-size: 1.5rem;
  font-weight: bold;
}
.wheel-result-detail { font-size: 0.84rem; color: var(--text-dim); }
.wheel-result[hidden] { display: none !important; }

.wheel-actions {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.6rem;
  margin-bottom: 1rem;
}
.btn.wheel-spin-btn {
  background: linear-gradient(180deg, #ffd94d, #ffb800 55%, #a37200);
  border-color: #fff2b8;
  color: #2a1c00;
  font-size: 0.95rem;
}
.wheel-close {
  background: none;
  border: none;
  color: var(--text-dim);
  cursor: pointer;
  font-family: 'Orbitron', sans-serif;
  font-size: 0.8rem;
}
.wheel-close:hover { color: #fff; }

/* The odds table. The whole point of the wheel is that it publishes
   these, so they are part of the component rather than a tooltip. */
.wheel-odds {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 0.35rem 0.8rem;
  font-size: 0.7rem;
  color: var(--text-dim);
}
.wheel-odd { display: inline-flex; align-items: center; gap: 5px; }
.wheel-odd i { width: 10px; height: 10px; border-radius: 3px; display: inline-block; }
.wheel-odd b { color: #fff; }

/* =====================================================================
   jk1x MODE
   ===================================================================== */
.jk1x-banner {
  position: fixed;
  top: 0.6rem;
  left: 50%;
  transform: translateX(-50%);
  z-index: 960;
  display: flex;
  align-items: center;
  gap: 0.6rem;
  padding: 6px 16px 6px 6px;
  border-radius: var(--radius-pill);
  background: linear-gradient(90deg, rgba(10,6,20,0.95), rgba(30,10,50,0.95));
  border: 2px solid var(--gold);
  box-shadow: 0 0 26px rgba(255, 217, 77, 0.5);
  pointer-events: none;
}
.jk1x-banner[hidden] { display: none !important; }
.jk1x-banner-logo { width: 34px; height: 34px; object-fit: contain; }
.jk1x-banner-text { display: flex; flex-direction: column; line-height: 1.1; }
.jk1x-banner-title {
  font-family: 'Orbitron', sans-serif;
  font-size: 0.62rem;
  letter-spacing: 0.14em;
  color: var(--gold);
}
.jk1x-banner-time {
  font-family: 'Orbitron', sans-serif;
  font-size: 1rem;
  font-weight: bold;
  color: #fff;
}

/* While the mode runs the machine itself is lit differently, so the
   takeover is visible on the reels and not only in the banner. */
@keyframes jk1xPulse {
  0%, 100% { box-shadow: 0 0 24px 2px rgba(255, 217, 77, 0.35); }
  50%      { box-shadow: 0 0 46px 10px rgba(255, 217, 77, 0.65); }
}
body.jk1x-active .machine-frame {
  animation: jk1xPulse 2.4s ease-in-out infinite;
  border-color: var(--gold);
}

.jk1x-intro {
  position: fixed;
  inset: 0;
  z-index: 12000;
  display: none;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 1rem;
  background: radial-gradient(circle at 50% 45%, rgba(60, 20, 90, 0.96), rgba(0, 0, 0, 0.98));
}
.jk1x-intro.open { display: flex; }

@keyframes jk1xLogoLand {
  0%   { opacity: 0; transform: scale(2.6) rotate(-18deg); filter: blur(8px); }
  55%  { opacity: 1; transform: scale(0.92) rotate(3deg); filter: blur(0); }
  75%  { transform: scale(1.06) rotate(-1deg); }
  100% { transform: scale(1) rotate(0deg); }
}
.jk1x-intro-logo {
  width: min(15rem, 44vw);
  animation: jk1xLogoLand 900ms cubic-bezier(0.2, 0.9, 0.3, 1) both;
  filter: drop-shadow(0 0 30px rgba(255, 217, 77, 0.7));
}

.jk1x-intro-title {
  font-family: 'Orbitron', sans-serif;
  font-size: clamp(1rem, 4vw, 1.8rem);
  letter-spacing: 0.14em;
  color: var(--gold);
  text-shadow: 0 0 18px rgba(255, 217, 77, 0.8);
  animation: jk1xLogoLand 700ms 500ms cubic-bezier(0.2, 0.9, 0.3, 1) both;
}

/* Every symbol in the game streaming into the machine, one after
   another — the visual statement of what the mode actually does. */
.jk1x-intro-symbols {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 0.5rem;
  max-width: min(40rem, 92vw);
}
@keyframes jk1xSymbolIn {
  0%   { opacity: 0; transform: translateY(60px) scale(0.4) rotate(-30deg); }
  70%  { opacity: 1; transform: translateY(-6px) scale(1.15) rotate(4deg); }
  100% { opacity: 1; transform: translateY(0) scale(1) rotate(0deg); }
}
.jk1x-intro-symbol {
  width: 52px;
  height: 52px;
  object-fit: contain;
  opacity: 0;
  animation: jk1xSymbolIn 520ms cubic-bezier(0.2, 0.9, 0.3, 1) both;
  filter: drop-shadow(0 4px 8px rgba(0, 0, 0, 0.6));
}

/* Shop icons for the new items. */
.shop-icon-reel {
  display: inline-flex;
  gap: 3px;
  align-items: center;
}
.shop-icon-reel i {
  width: 12px;
  height: 34px;
  border-radius: 3px;
  background: linear-gradient(180deg, #22e3ff, #0e6d88);
  display: inline-block;
}
.shop-icon-reel i:nth-child(2) { height: 42px; }

.shop-icon-wheel {
  display: inline-block;
  width: 40px;
  height: 40px;
  border-radius: 50%;
  border: 3px solid var(--gold);
  background: conic-gradient(#ffd94d 0 25%, #7bf3ff 0 50%, #4be07a 0 75%, #ff4081 0 100%);
}

@media (prefers-reduced-motion: reduce) {
  body.jk1x-active .machine-frame { animation: none; }
  .jk1x-intro-logo, .jk1x-intro-title, .jk1x-intro-symbol { animation-duration: 1ms; }
  .wheel-disc { transition-duration: 600ms !important; }
}

/* The WHEEL button matches the shop's gold rather than the utility row's
   colours: what it opens is a prize, not a panel. */
.btn.wheel {
  background: linear-gradient(180deg, #ffd94d, #ffb800 55%, #a37200);
  border-color: #fff2b8;
  color: #2a1c00;
}
.btn.wheel[hidden] { display: none !important; }


/* =====================================================================
   SHOP ICON TWEAKS for the new artwork
   ===================================================================== */
/* Symbol Swap draws a small sentence — guitar, arrow, terror — so its
   three parts need to sit on one line at a readable size. */
.shop-icon-trade {
  display: inline-flex;
  align-items: center;
  gap: 3px;
}
.shop-icon-trade img { width: 32px; height: 32px; object-fit: contain; }
.shop-icon-trade .shop-icon-arrow { font-size: 1.25rem; line-height: 1; color: var(--gold-bright); }

/* =====================================================================
   PREMIUM CHEST CARDS — Mythic and jk1x
   Bloom, orbiting motes and a moving sheen, so the two rarest things in
   the shop look unlike everything around them. jk1x gets more of
   everything plus a colour cycle, because it is the top of the shop.
   ===================================================================== */
.shop-card[data-chest="mythic"],
.shop-card[data-chest="jk1x"] {
  position: relative;
  overflow: hidden;
  isolation: isolate;
}

/* --- the bloom sitting behind the artwork --- */
@keyframes chestBloom {
  0%, 100% { opacity: 0.55; transform: translate(-50%, -50%) scale(1); }
  50%      { opacity: 1;    transform: translate(-50%, -50%) scale(1.25); }
}
.shop-card[data-chest="mythic"]::before,
.shop-card[data-chest="jk1x"]::before {
  content: '';
  position: absolute;
  top: 42px;
  left: 34px;
  width: 130px;
  height: 130px;
  border-radius: 50%;
  transform: translate(-50%, -50%);
  pointer-events: none;
  z-index: 0;
  animation: chestBloom 3.4s ease-in-out infinite;
}
.shop-card[data-chest="mythic"]::before {
  background: radial-gradient(circle, rgba(var(--violet-mid-rgb), 0.55) 0%, rgba(126, 34, 206, 0.22) 45%, transparent 70%);
}
.shop-card[data-chest="jk1x"]::before {
  background: radial-gradient(circle, rgba(255, 217, 77, 0.6) 0%, rgba(34, 227, 255, 0.28) 40%, transparent 72%);
  animation-duration: 2.4s;
}

/* --- a sheen that sweeps across the card --- */
@keyframes chestSheen {
  0%   { transform: translateX(-120%) rotate(18deg); }
  55%  { transform: translateX(240%) rotate(18deg); }
  100% { transform: translateX(240%) rotate(18deg); }
}
.shop-card[data-chest="mythic"]::after,
.shop-card[data-chest="jk1x"]::after {
  content: '';
  position: absolute;
  top: -30%;
  left: 0;
  width: 40%;
  height: 160%;
  pointer-events: none;
  z-index: 3;
  background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.22), transparent);
  animation: chestSheen 4.2s ease-in-out infinite;
}
.shop-card[data-chest="jk1x"]::after {
  background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.4), transparent);
  animation-duration: 2.8s;
}

/* --- orbiting motes, added by JS so the count can differ per tier --- */
.chest-mote {
  position: absolute;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  pointer-events: none;
  z-index: 1;
  opacity: 0;
  animation: moteFloat var(--mote-dur, 4s) linear infinite;
  animation-delay: var(--mote-delay, 0s);
}
@keyframes moteFloat {
  0%   { opacity: 0; transform: translate(var(--mote-x, 0), 0) scale(0.4); }
  15%  { opacity: 1; }
  85%  { opacity: 1; }
  100% { opacity: 0; transform: translate(calc(var(--mote-x, 0) + var(--mote-drift, 10px)), -120px) scale(1.1); }
}
.shop-card[data-chest="mythic"] .chest-mote {
  background: #d8b4fe;
  box-shadow: 0 0 8px 2px rgba(var(--violet-mid-rgb), 0.9);
}
.shop-card[data-chest="jk1x"] .chest-mote {
  background: #fff2b8;
  box-shadow: 0 0 10px 3px rgba(255, 217, 77, 0.95);
  width: 7px;
  height: 7px;
}

/* --- jk1x alone gets a cycling border --- */
@property --jk1x-hue { syntax: '<angle>'; inherits: false; initial-value: 0deg; }
@keyframes jk1xBorderCycle {
  to { --jk1x-hue: 360deg; }
}
.shop-card[data-chest="jk1x"] {
  border-width: 2px;
  border-color: transparent;
  background:
    linear-gradient(180deg, rgba(30, 12, 40, 0.95), rgba(10, 4, 16, 0.98)) padding-box,
    conic-gradient(from var(--jk1x-hue), #ffd94d, #22e3ff, #ff4081, #4be07a, #ffd94d) border-box;
  animation: jk1xBorderCycle 4s linear infinite;
  box-shadow: 0 6px 26px rgba(0, 0, 0, 0.75), 0 0 26px rgba(255, 217, 77, 0.35);
}

/* Everything in these two cards must sit above the bloom.

   Scoped to the children that are laid out IN FLOW. As `> *` it also
   caught the corner furniture -- the info button, the lock badge, the
   purchase flash -- all of which are `position: absolute` and anchored
   to a corner. Turning them relative dropped them into the column, so
   the info button on these two cards alone sat inline above the chest
   instead of in the top-right corner where it sits on every other card.
   They are lifted with z-index instead, which is all they needed. */
/* .chest-mote joins that exclusion list, and it was the expensive one to
   miss. The motes are appended AFTER the buy button, so turning them
   relative did not merely move them — it dropped five 6px sparks into
   the bottom of the column and made these two cards about 90px taller
   than every other card in the shop. That is where the "buttons are not
   at the same level" complaint was coming from: the Mythic and jk1x buy
   buttons were pushed a whole block up their own cards to make room for
   particles that were supposed to be floating over them. */
.shop-card[data-chest="mythic"] > *:not(.shop-card-info):not(.shop-card-lock):not(.shop-card-flash):not(.shop-card-ribbon):not(.chest-mote),
.shop-card[data-chest="jk1x"] > *:not(.shop-card-info):not(.shop-card-lock):not(.shop-card-flash):not(.shop-card-ribbon):not(.chest-mote) { position: relative; z-index: 2; }
.shop-card[data-chest="mythic"] > .shop-card-info,
.shop-card[data-chest="mythic"] > .shop-card-lock,
.shop-card[data-chest="mythic"] > .shop-card-flash,
.shop-card[data-chest="mythic"] > .shop-card-ribbon,
.shop-card[data-chest="jk1x"] > .shop-card-info,
.shop-card[data-chest="jk1x"] > .shop-card-lock,
.shop-card[data-chest="jk1x"] > .shop-card-flash,
.shop-card[data-chest="jk1x"] > .shop-card-ribbon { z-index: 3; }

/* =====================================================================
   SHOP CATEGORY TRANSITIONS
   Cards leave and arrive rather than being swapped instantly, and each
   one lands a beat after the last so a category reads as dealing out.
   ===================================================================== */
@keyframes shopCardIn {
  0%   { opacity: 0; transform: translateY(18px) scale(0.94) rotateX(12deg); }
  100% { opacity: 1; transform: translateY(0) scale(1) rotateX(0); }
}
.shop-grid { perspective: 900px; }
/* `backwards`, NOT `both`. A forwards fill keeps the last keyframe
   applied for as long as the element lives, and that keyframe ends on
   `opacity: 1` -- which silently outranked every declared opacity on the
   card. An unaffordable card was supposed to sit at 0.72 and a
   level-locked one at 0.4; both were pinned to fully opaque the moment
   they finished dealing in, so neither state was ever visible. Backwards
   still holds the 0% frame through the stagger delay, which is the only
   part of the fill this animation actually needed. */
.shop-card {
  animation: shopCardIn 380ms cubic-bezier(0.2, 0.9, 0.3, 1) backwards;
  animation-delay: var(--card-delay, 0ms);
}

/* The outgoing set, held for one frame while it fades. */
@keyframes shopCardOut {
  0%   { opacity: 1; transform: translateY(0) scale(1); }
  100% { opacity: 0; transform: translateY(-14px) scale(0.96); }
}
.shop-grid.leaving .shop-card {
  animation: shopCardOut 150ms ease-in both;
}

/* --- 3D shop title --- */
.shop-title {
  text-shadow:
    0 1px 0 #b8860b, 0 2px 0 #a5790a, 0 3px 0 #926c09,
    0 4px 0 #7f5f08, 0 5px 0 #6c5207, 0 6px 8px rgba(0, 0, 0, 0.75),
    0 0 26px rgba(255, 217, 77, 0.55);
  letter-spacing: 0.04em;
}

/* --- tabs get depth and a lift on selection --- */
.shop-tab {
  text-shadow: 0 1px 0 rgba(0, 0, 0, 0.6);
  transform-style: preserve-3d;
}
@keyframes tabPop {
  0%   { transform: translateY(-2px) scale(1); }
  45%  { transform: translateY(-5px) scale(1.07); }
  100% { transform: translateY(-2px) scale(1); }
}
.shop-tab.active { animation: tabPop 320ms cubic-bezier(0.2, 0.9, 0.3, 1); }

.shop-tab-blurb {
  animation: shopCardIn 300ms ease-out both;
}

/* --- ambient sparkles drifting up the shop --- */
.shop-sparkles {
  position: absolute;
  inset: 0;
  overflow: hidden;
  pointer-events: none;
  z-index: 0;
}
.shop-sparkle {
  position: absolute;
  bottom: -14px;
  border-radius: 50%;
  background: radial-gradient(circle, #fff 0%, var(--spark-tone, #ffd94d) 45%, transparent 70%);
  animation: sparkleRise var(--spark-dur, 9s) linear infinite;
  animation-delay: var(--spark-delay, 0s);
  opacity: 0;
}
@keyframes sparkleRise {
  0%   { opacity: 0; transform: translateY(0) scale(0.5); }
  10%  { opacity: var(--spark-peak, 0.85); }
  90%  { opacity: var(--spark-peak, 0.85); }
  100% { opacity: 0; transform: translateY(-104vh) translateX(var(--spark-drift, 0)) scale(1.15); }
}
/* The shop's own content sits above the sparkle layer. */
.shop-inner { position: relative; z-index: 1; }

/* The logo now closes the page rather than heading it. */
.shop-logo {
  margin: 1.6rem auto 0.5rem;
  display: block;
}

@media (prefers-reduced-motion: reduce) {
  .shop-card,
  .shop-grid.leaving .shop-card,
  .shop-tab.active,
  .shop-tab-blurb,
  .shop-sparkle,
  .chest-mote,
  .shop-card[data-chest="mythic"]::before,
  .shop-card[data-chest="jk1x"]::before,
  .shop-card[data-chest="mythic"]::after,
  .shop-card[data-chest="jk1x"]::after,
  .shop-card[data-chest="jk1x"] { animation: none; }
}

