/* ============================================================================
   Packs (gacha) — Market → Packs.
   Modelled on the Discord shop: one big featured pack up top, then a grid of
   smaller cards, each showing its real odds. Built on the same variables as
   the Cosmetics storefront so it recolours with an equipped UI theme.
   ========================================================================== */

.gx {
  --gx-shard: #7dd3fc;
  --gx-prism: #a78bfa;
  --gx-nova: #fbbf24;
  --gx-token: #ffcc4d;
  display: flex;
  flex-direction: column;
  gap: 22px;
}

/* ---------------------------------------------------------------- headings -- */
.gx-top {
  display: flex;
  justify-content: space-between;
  align-items: flex-end;
  gap: 14px;
  flex-wrap: wrap;
}
.gx-top h3 { margin: 2px 0 4px; font-size: 30px; line-height: 1.1; }
.gx-top p { margin: 0; color: var(--text-dim, #8a94a6); font-size: 14px; max-width: 560px; }
.gx-top-r { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; }

/* --------------------------------------------------------- wallet / buy bar --
   The StarKen wallet and the buy control live together, the way Discord puts a
   price and its Buy button in one row. Buying StarKens is the first thing a new
   player needs, so it is never more than one click from the packs.            */
.gx-wallet {
  display: flex;
  align-items: center;
  gap: 18px;
  flex-wrap: wrap;
  padding: 16px 20px;
  border-radius: 20px;
  border: 1px solid rgba(255, 204, 77, 0.28);
  background:
    radial-gradient(120% 160% at 0% 0%, rgba(255, 204, 77, 0.12), transparent 60%),
    var(--panel, rgba(255, 255, 255, 0.03));
}
.gx-wallet-bal { display: flex; align-items: center; gap: 12px; min-width: 0; }
.gx-wallet-bal img { width: 40px; height: 40px; flex: none; filter: drop-shadow(0 0 12px rgba(255, 204, 77, 0.45)); }
.gx-wallet-bal div { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.gx-wallet-bal b { font: 700 22px/1 'JetBrains Mono', monospace; color: var(--gx-token); }
.gx-wallet-bal span { font-size: 11px; letter-spacing: 0.14em; text-transform: uppercase; color: var(--text-faint, #6b7789); }

.gx-buy { display: flex; align-items: flex-end; gap: 10px; flex-wrap: wrap; margin-left: auto; }
.gx-buy-field { display: flex; flex-direction: column; gap: 5px; }
.gx-buy-field label { font-size: 10px; letter-spacing: 0.14em; text-transform: uppercase; color: var(--text-faint, #6b7789); }
.gx-buy-field input {
  width: 104px; padding: 10px 12px; border-radius: 12px; text-align: right;
  border: 1px solid var(--line, rgba(255, 255, 255, 0.12)); background: rgba(0, 0, 0, 0.28);
  color: var(--text, #e6edf7); font: 700 14px 'JetBrains Mono', monospace;
}
.gx-buy-field input:focus { outline: none; border-color: rgba(255, 204, 77, 0.6); }
.gx-cost {
  display: flex; flex-direction: column; gap: 3px; padding-bottom: 9px;
  font: 700 15px 'JetBrains Mono', monospace; color: var(--text, #e6edf7); white-space: nowrap;
}
.gx-cost small { font-size: 11px; font-weight: 600; color: var(--text-faint, #6b7789); letter-spacing: 0; }

.gx-btn {
  padding: 11px 20px; border-radius: 999px; cursor: pointer; white-space: nowrap;
  background: rgba(255, 204, 77, 0.16); border: 1px solid rgba(255, 204, 77, 0.55);
  color: #ffe9a8; font: 700 13px 'Sora', system-ui, sans-serif;
  transition: transform 0.15s ease, box-shadow 0.15s ease, background 0.15s ease;
}
.gx-btn:hover:not(:disabled) { background: rgba(255, 204, 77, 0.26); transform: translateY(-1px); box-shadow: 0 10px 26px rgba(255, 204, 77, 0.18); }
.gx-btn:disabled { opacity: 0.45; cursor: not-allowed; }

.gx-rate { font-size: 12px; color: var(--text-faint, #6b7789); }
.gx-rate b { color: var(--text, #e6edf7); font-family: 'JetBrains Mono', monospace; }

/* ------------------------------------------------------------------- hero ---
   Discord's shop leads with one oversized card for the thing it wants you to
   open first. Same idea: art on the left, the pitch and the odds on the right. */
.gx-hero {
  position: relative; display: grid; grid-template-columns: minmax(0, 1.05fr) minmax(0, 1fr);
  border-radius: 26px; overflow: hidden; min-height: 300px; background: #080c14;
  border: 1px var(--line, rgba(255, 255, 255, 0.1));
}
.gx-hero-art { position: relative; min-height: 240px; overflow: hidden; background: radial-gradient(120% 100% at 50% 0%, rgba(255, 204, 77, 0.16), #070b12 72%); }
.gx-hero-art img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.gx-hero-art::after { content: ''; position: absolute; inset: 0; background: linear-gradient(90deg, rgba(8, 12, 20, 0), #080c14 86%); }
.gx-hero-fallback { position: absolute; inset: 0; display: grid; place-items: center; font-size: 74px; }
/* A generated cover is inline SVG, so it has to fill its box the same way an <img> does. */
.gx-cover{position:absolute;inset:0;width:100%;height:100%;display:block}
.gx-hero-copy {
  position: relative; padding: 30px 30px 28px; display: flex; flex-direction: column;
  justify-content: center; gap: 12px;
  background: linear-gradient(90deg, rgba(8, 12, 20, 0), #080c14 24%);
}
.gx-hero-copy h2 { margin: 0; font-size: 34px; line-height: 1.08; text-shadow: 0 2px 20px rgba(0, 0, 0, 0.5); }
.gx-hero-copy p { margin: 0; color: #b8c3d6; font-size: 14px; line-height: 1.55; max-width: 430px; }
.gx-hero-cta { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; margin-top: 4px; }
.gx-hero-odds { display: flex; gap: 8px; flex-wrap: wrap; }

/* ------------------------------------------------------------- tier chips --- */
.gx-tier-chip {
  display: inline-flex; align-items: center; gap: 6px; padding: 5px 11px;
  border-radius: 999px; border: 1px solid currentColor; background: rgba(0, 0, 0, 0.3);
  font: 700 11px 'Sora', system-ui, sans-serif; white-space: nowrap;
}
.gx-tier-chip img { width: 15px; height: 15px; flex: none; }

/* --------------------------------------------------------------- odds bar --- */
.gx-odds { display: flex; flex-direction: column; gap: 7px; }
.gx-odds-label { display: flex; justify-content: space-between; font-size: 10px; letter-spacing: 0.14em; text-transform: uppercase; color: var(--text-faint, #6b7789); }
.gx-odds-bar { display: flex; height: 7px; border-radius: 999px; overflow: hidden; background: rgba(255, 255, 255, 0.07); }
.gx-odds-bar i { display: block; height: 100%; }
.gx-odds-legend { display: flex; gap: 14px; flex-wrap: wrap; font-size: 12px; color: var(--text-dim, #8a94a6); }
.gx-odds-legend span { display: inline-flex; align-items: center; gap: 6px; }
.gx-odds-legend i { width: 9px; height: 9px; border-radius: 3px; flex: none; }
.gx-odds-legend b { color: var(--text, #e6edf7); font-family: 'JetBrains Mono', monospace; }

/* ------------------------------------------------------------------- grid --- */
.gx-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(272px, 1fr)); gap: 18px; }

.gx-pack {
  position: relative; display: flex; flex-direction: column; border-radius: 22px; overflow: hidden;
  background: var(--panel, rgba(255, 255, 255, 0.03)); border: 1px var(--line, rgba(255, 255, 255, 0.1));
  transition: transform 0.18s ease, box-shadow 0.18s ease, border-color 0.18s ease;
}
.gx-pack:hover { transform: translateY(-5px); border-color: rgba(255, 204, 77, 0.45); box-shadow: 0 20px 44px rgba(0, 0, 0, 0.5), 0 0 30px rgba(255, 204, 77, 0.14); }

.gx-pack-art { position: relative; aspect-ratio: 16 / 10; overflow: hidden; background: radial-gradient(120% 100% at 50% 0%, rgba(255, 204, 77, 0.14), #070b12 72%); }
.gx-pack-art img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.gx-pack-fallback { position: absolute; inset: 0; display: grid; place-items: center; font-size: 56px; }
.gx-pack-tiers { position: absolute; left: 12px; right: 12px; bottom: 12px; display: flex; gap: 6px; flex-wrap: wrap; }
.gx-pack-tiers .gx-tier-chip { background: rgba(8, 12, 20, 0.82); backdrop-filter: blur(6px); }

.gx-pack-body { display: flex; flex-direction: column; gap: 12px; padding: 16px 18px 18px; flex: 1; }
.gx-pack-body h4 { margin: 0; font-size: 18px; line-height: 1.2; }
.gx-pack-body > p { margin: 0; color: var(--text-dim, #8a94a6); font-size: 13px; line-height: 1.5; }

.gx-pack-meta { display: flex; gap: 12px; flex-wrap: wrap; font-size: 12px; color: var(--text-faint, #6b7789); }
.gx-pack-meta b { color: var(--text, #e6edf7); font-family: 'JetBrains Mono', monospace; }

.gx-pack-actions { display: flex; gap: 8px; margin-top: auto; }
.gx-spin {
  flex: 1; display: flex; flex-direction: column; align-items: center; gap: 2px; padding: 11px 10px;
  border-radius: 14px; cursor: pointer; border: 1px solid rgba(255, 204, 77, 0.45);
  background: rgba(255, 204, 77, 0.14); color: #ffe9a8; font: 700 14px 'Sora', system-ui, sans-serif;
  transition: transform 0.15s ease, background 0.15s ease;
}
.gx-spin small { font-size: 10px; font-weight: 600; color: #ffcc4d; letter-spacing: 0.04em; }
.gx-spin:hover:not(:disabled) { background: rgba(255, 204, 77, 0.24); transform: translateY(-1px); }
.gx-spin:disabled { opacity: 0.45; cursor: not-allowed; }

.gx-empty { color: var(--text-faint, #6b7789); font-size: 14px; padding: 8px 0; }

/* ---------------------------------------------------------------- reveal ---- */
/* LANDSCAPE. One prize is a wide card -- art on the left, details on the right --
   because a single drop is the one worth looking at. Several prizes become a
   landscape strip so all of them stay legible at once instead of a vertical list
   you have to scroll to the bottom of. */
.gx-veil {
  position: fixed; inset: 0; z-index: 200; display: grid; place-items: center; padding: 20px;
  background: rgba(4, 6, 11, 0.82); backdrop-filter: blur(8px);
  opacity: 0; transition: opacity 0.25s ease;
}
.gx-veil.on { opacity: 1; }
.gx-reveal {
  width: min(880px, 100%); max-height: 88vh; overflow: auto; border-radius: 24px; padding: 26px;
  background:
    radial-gradient(120% 90% at 50% -10%, color-mix(in srgb, var(--gx-accent) 16%, transparent), transparent 60%),
    linear-gradient(180deg, #101827, #080c14);
  border: 1px color-mix(in srgb, var(--gx-accent) 34%, transparent);
  box-shadow: 0 30px 90px rgba(0, 0, 0, 0.7);
  transform: scale(0.94); transition: transform 0.25s cubic-bezier(0.2, 0.9, 0.3, 1.2);
}
.gx-veil.on .gx-reveal { transform: scale(1); }
.gx-reveal-head { display: flex; justify-content: space-between; align-items: flex-start; gap: 14px; margin-bottom: 20px; }
.gx-reveal-kicker {
  display: inline-flex; align-items: center; gap: 6px; margin-bottom: 6px;
  font: 700 10px 'JetBrains Mono', monospace; letter-spacing: 0.2em; text-transform: uppercase;
  color: var(--gx-accent);
}
.gx-reveal-kicker img { width: 14px; height: 14px; }
.gx-reveal-head h3 { margin: 0; font-size: 25px; line-height: 1.15; }
.gx-reveal-close { background: none; border: 0; color: var(--text-dim, #8a94a6); font-size: 26px; cursor: pointer; line-height: 1; }

  /* the stage: a horizontal row of prize cards, left to right, scrolling down */

/* the wide card for a single drop */
.gx-prize-art {
  position: relative; min-height: 132px; overflow: hidden;
  background: radial-gradient(120% 100% at 50% 0%, color-mix(in srgb, var(--gx-accent) 24%, transparent), #070b12 74%);
  display: grid; place-items: center;
}
.gx-prize-art img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.gx-prize-fallback { font-size: 52px; line-height: 1; }
.gx-prize-art::after {
  content: ''; position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(180deg, rgba(8, 12, 20, 0) 45%, rgba(8, 12, 20, 0.7) 100%);
}

.gx-prize-body { padding: 16px 18px; display: flex; flex-direction: column; gap: 7px; justify-content: center; min-width: 0; }
.gx-prize-tier {
  display: inline-flex; align-items: center; gap: 6px; align-self: flex-start;
  padding: 4px 10px; border-radius: 999px; border: 1px solid currentColor;
  font: 700 10px 'Sora', system-ui, sans-serif; letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--gx-accent);
}
.gx-prize-tier img { width: 13px; height: 13px; }
.gx-prize-body h4 { margin: 0; font-size: 19px; line-height: 1.2; word-break: break-word; }
.gx-prize-body p { margin: 0; font-size: 12.5px; color: var(--text-faint, #6b7789); }
.gx-prize-notes { display: flex; gap: 7px; flex-wrap: wrap; margin-top: 2px; }
.gx-win-note {
  font: 700 11px 'JetBrains Mono', monospace; padding: 4px 9px; border-radius: 999px;
  color: #25e88f; background: rgba(37, 232, 143, 0.12); border: 1px solid rgba(37, 232, 143, 0.32);
}
.gx-win-note.stack { color: #8ef0c0; }
.gx-win-note.dupe { color: #9fb0c8; background: rgba(255, 255, 255, 0.06); border-color: rgba(255, 255, 255, 0.16); }
.gx-win-note.lock { color: #a78bfa; background: rgba(167, 139, 250, 0.12); border-color: rgba(167, 139, 250, 0.32); }

/* a wasted roll reads as a wasted roll */
.gx-prize.is-dupe { opacity: 0.82; border-style: dashed; }
.gx-prize.is-dupe .gx-prize-art { filter: saturate(0.45); }

.gx-reveal-foot { display: flex; justify-content: space-between; align-items: center; gap: 12px; margin-top: 20px; flex-wrap: wrap; }

@keyframes gxPop { from { opacity: 0; transform: translateY(10px) scale(0.97); } to { opacity: 1; transform: none; } }

/* ------------------------------------------------------------- responsive --- */
@media (max-width: 860px) {
  .gx-hero { grid-template-columns: 1fr; min-height: 0; }
  .gx-hero-art { min-height: 190px; }
  .gx-hero-art::after { background: linear-gradient(180deg, rgba(8, 12, 20, 0), #080c14 88%); }
  .gx-hero-copy { background: none; padding: 22px; }
  .gx-hero-copy h2 { font-size: 27px; }
  .gx-buy { margin-left: 0; width: 100%; }
  .gx-buy-field input { width: 100%; }
  .gx-buy-field { flex: 1 1 120px; }
  .gx-reveal { padding: 20px; }
  .gx-reveal-head h3 { font-size: 21px; }
}
@media (prefers-reduced-motion: reduce) {
  .gx-pack, .gx-btn, .gx-spin, .gx-reveal, .gx-veil { transition: none; }
  .gx-prize { animation: none; }
}

/* ------------------------------------------------------- spin reel + FX ---- */
/* A full-screen veil holding a CRATE. It runs across the network request, so the wait is
   never dead time; the sound is synthesised, so there is nothing to download. The crate
   wobbles and builds light in the pack's tier colour, then the lid comes off and the
   prize is thrown up inside the burst. */
.gx-fx {
  position: fixed; inset: 0; z-index: 210; display: grid; place-items: center; padding: 20px;
  background: rgba(4, 6, 11, 0.78); backdrop-filter: blur(10px);
  opacity: 0; transition: opacity 0.2s ease;
}
.gx-fx.on { opacity: 1; }
.gx-fx-inner { display: flex; flex-direction: column; align-items: center; gap: 26px; }

.gx-crate {
  position: relative; width: 190px; height: 190px; display: grid; place-items: center;
  --gx-accent: #ffcc4d;
}
/* the light the crate gives off while it is being wound up */
.gx-crate-aura {
  position: absolute; inset: -46%; border-radius: 50%; pointer-events: none;
  background: radial-gradient(circle at 50% 50%, color-mix(in srgb, var(--gx-accent) 42%, transparent), transparent 62%);
  opacity: 0.5; animation: gxAura 1.5s ease-in-out infinite;
}

.gx-crate-box {
  position: relative; width: 128px; height: 116px;
  animation: gxWobble 0.72s ease-in-out infinite;
  transform-origin: 50% 92%;
}
/* the lid: three straps, thrown upward when the crate opens */
.gx-crate-lid {
  position: absolute; top: 0; left: -7px; right: -7px; height: 34px; z-index: 3;
  border-radius: 10px 10px 5px 5px;
  background: linear-gradient(180deg, color-mix(in srgb, var(--gx-accent) 62%, #101827), #131c2c);
  border: 1px solid color-mix(in srgb, var(--gx-accent) 55%, transparent);
  box-shadow: 0 8px 22px rgba(0, 0, 0, 0.5), inset 0 1px 0 rgba(255, 255, 255, 0.22);
  transform-origin: 50% 100%;
  transition: transform 0.34s cubic-bezier(0.2, 0.9, 0.3, 1.5), opacity 0.34s ease;
}
.gx-crate-lid i {
  position: absolute; top: 6px; bottom: 6px; width: 7px; border-radius: 3px;
  background: color-mix(in srgb, var(--gx-accent) 78%, #fff 10%);
  box-shadow: 0 0 12px color-mix(in srgb, var(--gx-accent) 70%, transparent);
}
.gx-crate-lid i:nth-child(1) { left: 16px; }
.gx-crate-lid i:nth-child(2) { left: 50%; margin-left: -3.5px; }
.gx-crate-lid i:nth-child(3) { right: 16px; }

.gx-crate-front {
  position: absolute; top: 30px; bottom: 0; left: 0; right: 0; z-index: 2;
  border-radius: 6px 6px 12px 12px; overflow: hidden;
  background: linear-gradient(180deg, #16202f, #0a0f18);
  border: 1px solid rgba(255, 255, 255, 0.1);
  box-shadow: 0 22px 48px rgba(0, 0, 0, 0.6), inset 0 1px 0 rgba(255, 255, 255, 0.07);
  display: grid; place-items: center;
}
.gx-crate-front b {
  font: 800 46px 'Sora', system-ui, sans-serif; color: color-mix(in srgb, var(--gx-accent) 80%, #fff);
  text-shadow: 0 0 26px color-mix(in srgb, var(--gx-accent) 65%, transparent);
  transition: opacity 0.2s ease;
}

/* the burst: a ring of light plus the prize itself.
   z-index 5 puts the whole burst -- and therefore the prize -- IN FRONT of the
   crate: the lid is z-index 3 and the front face z-index 2, so without this the
   reveal came out *behind* the box it had just been thrown out of. The box stays
   visible around and behind the prize, which is the point: you see the cosmetic
   bursting out of the crate, not pasted over a hole where the crate was. */
.gx-crate-burst {
  position: absolute; inset: 0; display: grid; place-items: center; pointer-events: none;
  z-index: 5;
  opacity: 0;
}
.gx-crate-burst::before {
  content: ''; position: absolute; width: 24px; height: 24px; border-radius: 50%;
  background: radial-gradient(circle, #fff 0%, color-mix(in srgb, var(--gx-accent) 80%, transparent) 34%, transparent 70%);
}
.gx-crate-prize {
  position: relative; width: 150px; height: 150px; display: grid; place-items: center;
  border-radius: 22px; overflow: hidden;
  background: linear-gradient(160deg, color-mix(in srgb, var(--gx-accent) 26%, #101827), #080c14);
  border: 1px solid color-mix(in srgb, var(--gx-accent) 60%, transparent);
  box-shadow: 0 0 0 1px color-mix(in srgb, var(--gx-accent) 40%, transparent), 0 20px 60px rgba(0, 0, 0, 0.66);
}
.gx-crate-prize img { width: 100%; height: 100%; object-fit: cover; }
.gx-crate-emoji { font-size: 62px; line-height: 1; }

/* ---- the moment it opens ---- */
.gx-fx.open .gx-crate-box { animation: none; }
.gx-fx.open .gx-crate-lid {
  transform: translateY(-128px) rotate(-13deg); opacity: 0;
}
.gx-fx.open .gx-crate-front b { opacity: 0; }
.gx-fx.open .gx-crate-front {
  box-shadow: 0 0 0 1px color-mix(in srgb, var(--gx-accent) 70%, transparent), 0 0 70px color-mix(in srgb, var(--gx-accent) 55%, transparent);
}
.gx-fx.open .gx-crate-burst { opacity: 1; }
.gx-fx.open .gx-crate-burst::before { animation: gxBurst 0.62s cubic-bezier(0.2, 0.8, 0.3, 1) both; }
.gx-fx.open .gx-crate-prize { animation: gxPrize 0.5s cubic-bezier(0.2, 0.9, 0.3, 1.45) both; }
.gx-fx.open .gx-crate-aura { opacity: 0.95; animation-duration: 0.5s; }

.gx-fx-label {
  margin: 0; font: 700 13px 'JetBrains Mono', monospace; letter-spacing: 0.22em;
  text-transform: uppercase; color: #9fb0c8; min-height: 1em; transition: color 0.2s ease;
}

@keyframes gxWobble {
  0%, 100% { transform: rotate(-2.4deg) translateY(0); }
  25% { transform: rotate(2.8deg) translateY(-3px); }
  50% { transform: rotate(-1.8deg) translateY(0); }
  75% { transform: rotate(3.2deg) translateY(-2px); }
}
@keyframes gxAura { 0%, 100% { opacity: 0.38; transform: scale(0.94); } 50% { opacity: 0.68; transform: scale(1.06); } }
@keyframes gxBurst {
  0% { transform: scale(0.3); opacity: 0.2; }
  45% { transform: scale(5.6); opacity: 1; }
  100% { transform: scale(7.4); opacity: 0; }
}
@keyframes gxPrize {
  0% { transform: scale(0.2) translateY(26px); opacity: 0; }
  55% { transform: scale(1.12) translateY(-8px); opacity: 1; }
  100% { transform: scale(1) translateY(0); opacity: 1; }
}

/* ---------------------------------------------- pack cards, rebuilt look ---- */
/* The old card was a flat panel with a picture and two buttons. This one gives each tier a
   colour identity, a live odds bar and a shimmer on the spin control, so a pack reads as a
   thing you open rather than a row in a list. */
.gx-pack { --gx-accent: #ffcc4d; }
.gx-pack[data-best='shard'] { --gx-accent: #7dd3fc; }
.gx-pack[data-best='prism'] { --gx-accent: #a78bfa; }
.gx-pack[data-best='nova']  { --gx-accent: #fbbf24; }

.gx-pack::before {
  /* accent wash along the top edge, tinted by the best tier the pack holds */
  content: ''; position: absolute; inset: 0 0 auto; height: 3px; z-index: 2;
  background: linear-gradient(90deg, transparent, var(--gx-accent), transparent);
  opacity: 0.75;
}
.gx-pack-art::after {
  /* deeper vignette so the tier chips always stay readable over bright art */
  content: ''; position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(180deg, rgba(8, 12, 20, 0) 40%, rgba(8, 12, 20, 0.82) 100%);
}
.gx-pack-art img, .gx-pack-art .gx-cover { transition: transform 0.5s cubic-bezier(0.2, 0.8, 0.3, 1); }
.gx-pack:hover .gx-pack-art img, .gx-pack:hover .gx-pack-art .gx-cover { transform: scale(1.05); }

/* odds bar, per tier, with a soft glow on the rarest one present */
.gx-pack-odds { display: flex; flex-direction: column; gap: 6px; }
.gx-pack-odds .gx-odds-bar { height: 9px; background: rgba(255, 255, 255, 0.06); }
.gx-pack-odds .gx-odds-bar i { box-shadow: 0 0 12px currentColor; transition: width 0.4s ease; }
.gx-pack-odds .gx-odds-legend { font-size: 11.5px; }

/* the spin control: a slow shimmer so it invites the click */
.gx-spin { position: relative; overflow: hidden; isolation: isolate; }
.gx-spin::after {
  content: ''; position: absolute; inset: 0; z-index: -1;
  background: linear-gradient(100deg, transparent 38%, rgba(255, 255, 255, 0.22) 50%, transparent 62%);
  transform: translateX(-100%);
}
.gx-spin:hover:not(:disabled)::after { animation: gxBtnSheen 0.85s ease-out; }
.gx-spin:active:not(:disabled) { transform: translateY(1px) scale(0.99); }
@keyframes gxBtnSheen { to { transform: translateX(100%); } }

/* timed/permanent badges on the pack body */
.gx-pack-flags { display: flex; gap: 6px; flex-wrap: wrap; }
.gx-flag {
  display: inline-flex; align-items: center; gap: 5px; padding: 3px 9px; border-radius: 999px;
  border: 1px solid rgba(255, 255, 255, 0.14); background: rgba(255, 255, 255, 0.05);
  font: 700 10px 'JetBrains Mono', monospace; letter-spacing: 0.06em; color: #9fb0c8;
}
.gx-flag.is-timed { color: #ffb020; border-color: rgba(255, 176, 32, 0.4); background: rgba(255, 176, 32, 0.1); }
.gx-flag.is-perm { color: #19e88f; border-color: rgba(25, 232, 143, 0.4); background: rgba(25, 232, 143, 0.1); }

/* the hero gets the same accent treatment */
.gx-hero { --gx-accent: #ffcc4d; }
.gx-hero[data-best='shard'] { --gx-accent: #7dd3fc; }
.gx-hero[data-best='prism'] { --gx-accent: #a78bfa; }
.gx-hero[data-best='nova']  { --gx-accent: #fbbf24; }
.gx-hero { border-color: color-mix(in srgb, var(--gx-accent) 34%, transparent); }
.gx-hero-art { background: radial-gradient(120% 100% at 50% 0%, color-mix(in srgb, var(--gx-accent) 20%, transparent), #070b12 72%); }

@media (prefers-reduced-motion: reduce) {
  .gx-spin::after { animation: none; }
  .gx-crate-box, .gx-crate-aura, .gx-crate-burst::before, .gx-crate-prize { animation: none !important; transition: none !important; }
  .gx-pack:hover .gx-pack-art img, .gx-pack:hover .gx-pack-art .gx-cover { transform: none; }
}

/* ==========================================================================
   Round 23 — pack contents preview, and a horizontal spin reveal.
   ========================================================================== */

/* ---- the Contents button, beside the spins on every card ---- */
.gx-preview-btn {
  display: inline-flex; align-items: center; gap: 7px;
  padding: 11px 16px; border-radius: 14px; cursor: pointer;
  background: rgba(125, 211, 252, 0.1); border: 1px solid rgba(125, 211, 252, 0.34);
  color: #bfe8fb; font: 600 13px/1 inherit; letter-spacing: 0.01em;
  transition: background 0.16s ease, transform 0.16s ease, border-color 0.16s ease;
}
.gx-preview-btn:hover { background: rgba(125, 211, 252, 0.2); transform: translateY(-1px); border-color: rgba(125, 211, 252, 0.6); }
.gx-preview-btn i {
  font-style: normal; font-size: 11px; font-weight: 700; padding: 2px 7px; border-radius: 999px;
  background: rgba(125, 211, 252, 0.18); color: #e4f6ff;
}
.gx-card-cta { display: flex; gap: 8px; flex-wrap: wrap; margin-top: auto; }
.gx-card-cta .gx-pack-actions { margin-top: 0; }

/* ---- the pack-contents modal ---- */
.gx-pv-veil {
  position: fixed; inset: 0; z-index: 205;
  display: grid; place-items: start center; padding: 24px 16px;
  background: rgba(4, 7, 12, 0.82); backdrop-filter: blur(7px);
  opacity: 0; transition: opacity 0.22s ease; overflow-y: auto; overscroll-behavior: contain;
}
.gx-pv-veil.on { opacity: 1; }
.gx-pv {
  position: relative; width: min(880px, 100%); margin: auto;
  border-radius: 24px; padding: 24px;
  background: linear-gradient(180deg, #101825, #0a0f16);
  border: 1px solid color-mix(in srgb, var(--gx-accent) 30%, transparent);
  box-shadow: 0 30px 90px rgba(0, 0, 0, 0.66);
  transform: translateY(14px) scale(0.985); transition: transform 0.24s cubic-bezier(0.2, 0.9, 0.3, 1);
}
.gx-pv-veil.on .gx-pv { transform: none; }
.gx-pv-close {
  position: absolute; top: 14px; right: 16px; z-index: 4;
  background: rgba(8, 12, 20, 0.7); border: 1px solid rgba(255, 255, 255, 0.12);
  color: var(--text-dim, #8a94a6); font-size: 24px; line-height: 1;
  width: 36px; height: 36px; border-radius: 50%; cursor: pointer;
}
.gx-pv-close:hover { color: #fff; border-color: rgba(255, 255, 255, 0.3); }

.gx-pv-head { display: grid; grid-template-columns: minmax(0, 190px) minmax(0, 1fr); gap: 20px; align-items: start; }
.gx-pv-cover {
  aspect-ratio: 16 / 10; border-radius: 16px; overflow: hidden; background: #070b12;
  border: 1px solid rgba(255, 255, 255, 0.08);
}
.gx-pv-cover img { width: 100%; height: 100%; object-fit: cover; display: block; }
.gx-pv-title h3 { margin: 4px 0 6px; font-size: 25px; line-height: 1.15; }
.gx-pv-title p { margin: 0 0 12px; color: var(--text-dim, #8a94a6); font-size: 13px; line-height: 1.55; max-width: 54ch; }
.gx-pv-odds { display: flex; gap: 7px; flex-wrap: wrap; margin-bottom: 10px; }
.gx-pv-count { margin: 10px 0 14px; font-size: 12.5px; color: var(--text-faint, #6b7789); }
.gx-pv-count b { color: var(--text, #e8eef7); }
.gx-pv-cta { display: flex; gap: 8px; flex-wrap: wrap; }
.gx-pv-cta .gx-pack-actions { margin-top: 0; }
.gx-pv-note {
  margin: 20px 0 4px; padding: 12px 14px; border-radius: 14px;
  background: rgba(125, 211, 252, 0.07); border: 1px solid rgba(125, 211, 252, 0.18);
  font-size: 12.5px; line-height: 1.6; color: var(--text-dim, #8a94a6);
}
.gx-pv-note b { color: #bfe8fb; }

.gx-pv-list{display:grid;grid-template-columns:repeat(auto-fill,minmax(168px,1fr));gap:12px;margin-top:16px}
.gx-pv-tile{,  display:flex;flex-direction:column;padding:0;overflow:hidden;text-align:left;,  border-radius:16px;cursor:zoom-in;color:inherit;font:inherit;,  background:rgba(255,255,255,.032);border:1px solid rgba(255,255,255,.08);,  transition:transform .16s ease,border-color .16s ease,background .16s ease;,},.gx-pv-tile:hover{transform:translateY(-2px);border-color:color-mix(in srgb,var(--gx-accent) 45%,transparent);background:color-mix(in srgb,var(--gx-accent) 8%,transparent)},.gx-pv-tile:focus-visible{outline:2px solid var(--gx-accent);outline-offset:2px},.gx-pv-tile.is-owned{border-color:color-mix(in srgb,var(--gx-accent) 34%,transparent)},.gx-pv-tile.is-perm{box-shadow:0 0 0 1px color-mix(in srgb,var(--gx-accent) 22%,transparent)},.gx-pv-tile-art{position:relative;display:block;aspect-ratio:16/10;overflow:hidden;background:#070b12},.gx-pv-tile-art .gx-pv-art{position:absolute;inset:0;width:100%;height:100%},.gx-pv-tile-body{display:flex;flex-direction:column;gap:5px;padding:11px 12px 12px;min-width:0},.gx-pv-tile-name{font:700 13.5px 'Sora',system-ui,sans-serif;line-height:1.25;word-break:break-word},.gx-pv-tile-kind{font-size:10px;letter-spacing:.08em;text-transform:uppercase;color:var(--text-faint,#6b7789)}
.gx-pv-art {
  position: relative; aspect-ratio: 16 / 10; border-radius: 11px; overflow: hidden;
  background: radial-gradient(120% 100% at 50% 0%, rgba(255, 255, 255, 0.07), #070b12 74%);
  background-size: cover; background-position: center;
}
.gx-pv-art.cx-prev { border: 0; min-height: 0; margin: 0; }
.gx-pv-art .mc-mock { position: absolute; inset: 0; }
.gx-pv-tags { display: flex; gap: 6px; flex-wrap: wrap; }
.gx-pv-tag {
  display: inline-flex; align-items: center; gap: 5px;
  font-size: 10.5px; font-weight: 700; letter-spacing: 0.05em; text-transform: uppercase;
  padding: 3px 8px; border-radius: 999px;
  background: rgba(255, 255, 255, 0.06); color: var(--text-dim, #9aa6b8);
  border: 1px solid rgba(255, 255, 255, 0.08);
}
.gx-pv-tag img { width: 11px; height: 11px; }
.gx-pv-tag.t-shard { color: #7dd3fc; border-color: rgba(125, 211, 252, 0.34); }
.gx-pv-tag.t-prism { color: #a78bfa; border-color: rgba(167, 139, 250, 0.34); }
.gx-pv-tag.t-nova  { color: #fbbf24; border-color: rgba(251, 191, 36, 0.34); }
.gx-pv-tag.is-timed { color: #7dd3fc; }
.gx-pv-tag.is-perm  { color: #19e88f; border-color: rgba(25, 232, 143, 0.3); }
.gx-pv-tag.is-owned { color: #ffcc4d; border-color: rgba(255, 204, 77, 0.34); background: rgba(255, 204, 77, 0.1); }
.gx-pv-tag.is-anim  { color: #d6ccff; }

/* ---- the pack-contents modal: one section per tier, drawn like the Market ---- */
/* Live progress. The old modal told you what you owned once, when it opened, and
   then lied until you reloaded the page. These three numbers are rewritten in
   place after every spin, so "spins taken" and "permanents obtained" are true
   the instant the reveal closes. */
.gx-live {
  display: flex; flex-wrap: wrap; gap: 8px; margin: 12px 0 2px;
}
.gx-live-i {
  display: inline-flex; align-items: baseline; gap: 6px;
  padding: 7px 12px; border-radius: 999px;
  background: rgba(255, 255, 255, 0.05); border: 1px solid rgba(255, 255, 255, 0.1);
  font-size: 11.5px; color: var(--text-dim, #8a94a6);
}
.gx-live-i b {
  font: 800 14px 'Sora', system-ui, sans-serif; color: var(--text, #e8eef7);
  font-variant-numeric: tabular-nums;
}
.gx-live-i.is-perm b { color: #19e88f; }
.gx-live-i.is-spin b { color: var(--gx-accent); }

/* Each tier is its own collapsible section, hardest first. */
.gx-pv-secs { display: flex; flex-direction: column; gap: 12px; margin-top: 16px; }
.gx-sec {
  border-radius: 20px; overflow: hidden;
  background: rgba(255, 255, 255, 0.022); border: 1px solid rgba(255, 255, 255, 0.08);
}
.gx-sec[open] {
  background: color-mix(in srgb, var(--gx-accent) 4%, transparent);
  border-color: color-mix(in srgb, var(--gx-accent) 26%, transparent);
}
.gx-sec-sum {
  display: flex; align-items: center; gap: 11px;
  padding: 12px 14px; cursor: pointer; list-style: none; min-width: 0;
}
.gx-sec-sum::-webkit-details-marker { display: none; }
.gx-sec-ico { width: 22px; height: 22px; flex: 0 0 22px; }
.gx-sec-txt { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.gx-sec-name {
  font: 800 12.5px 'Sora', system-ui, sans-serif; letter-spacing: 0.16em;
  text-transform: uppercase; color: var(--gx-accent);
}
.gx-sec-sub { font-size: 11.5px; color: var(--text-faint, #6b7789); }
.gx-sec-n {
  margin-left: auto; flex: 0 0 auto;
  font: 700 11px 'JetBrains Mono', monospace; color: var(--text-dim, #9aa6b8);
  background: rgba(255, 255, 255, 0.06); border: 1px solid rgba(255, 255, 255, 0.1);
  border-radius: 999px; padding: 3px 9px; font-variant-numeric: tabular-nums;
}
.gx-sec-chev {
  flex: 0 0 auto; width: 9px; height: 9px;
  border-right: 2px solid var(--text-faint, #6b7789); border-bottom: 2px solid var(--text-faint, #6b7789);
  transform: rotate(45deg); transition: transform 0.18s ease;
}
.gx-sec[open] .gx-sec-chev { transform: rotate(-135deg); }
.gx-sec-body { padding: 0 14px 14px; }

/* The grid itself IS the Market's: same tile frame, same .cxs-art stage, same
   chips in the corner, same .cxs-meta name + kind, same .cxs-foot price pill. */
.gx-pv-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(196px, 1fr)); gap: 14px; }
.gx-pv-grid .cxs-tile { cursor: zoom-in; }
/* a reward you already hold reads as owned, exactly like a Market tile */
.gx-pv-grid .cxs-tile.is-owned { border-color: rgba(25, 232, 143, 0.34); }
.gx-pv-grid .cxs-tile.is-perm { box-shadow: 0 0 0 1px color-mix(in srgb, var(--gx-accent) 24%, transparent); }

/* pack-only chips, in the Market's chip language */
.cxs-chip.t-shard { color: #7dd3fc; border-color: rgba(125, 211, 252, 0.55); }
.cxs-chip.t-prism { color: #a78bfa; border-color: rgba(167, 139, 250, 0.55); }
.cxs-chip.t-nova  { color: #fbbf24; border-color: rgba(251, 191, 36, 0.55); }
.cxs-chip.is-perm  { color: #19e88f; border-color: rgba(25, 232, 143, 0.5); }
.cxs-chip.is-timed { color: #7dd3fc; border-color: rgba(125, 211, 252, 0.5); }
.cxs-chip.is-own   { color: #ffcc4d; border-color: rgba(255, 204, 77, 0.5); background: rgba(255, 204, 77, 0.12); }
/* and the foot pill that replaces a price */
.cxs-price.is-perm  { color: #19e88f; border-color: rgba(25, 232, 143, 0.5); background: rgba(25, 232, 143, 0.1); }
.cxs-price.is-timed { color: #7dd3fc; border-color: rgba(125, 211, 252, 0.5); background: rgba(125, 211, 252, 0.1); }
.cxs-price.is-new   { color: #9aa6b8; }
/* In the pack sheet the chips sit in flow, directly above the eyebrow, exactly as
   the Market's own preview shows them. .cxs-chips is absolutely positioned so it
   can sit in a tile's corner, and would otherwise float over the title here. */
.gx-cos-sheet .cxs-chips.is-inline {
  position: static; left: auto; top: auto; right: auto; margin-bottom: 2px;
}

/* ---- the spin reveal: horizontal, left to right, scrolling down ---- */
.gx-reveal-stage {
  display: flex; flex-wrap: wrap; gap: 12px;
  max-height: 52vh; overflow-y: auto; overflow-x: hidden;
  overscroll-behavior: contain; padding-right: 4px;
  scrollbar-width: thin; scrollbar-color: rgba(255, 255, 255, 0.22) transparent;
}
.gx-reveal-stage::-webkit-scrollbar { width: 8px; }
.gx-reveal-stage::-webkit-scrollbar-thumb { background: rgba(255, 255, 255, 0.18); border-radius: 99px; }
.gx-reveal-stage.is-one { max-height: none; overflow: visible; }
.gx-prize {
    animation: gxPop 0.36s cubic-bezier(0.2, 0.9, 0.3, 1.25) both;
    box-shadow: 0 0 26px color-mix(in srgb, var(--gx-accent) 12%, transparent);
  flex: 1 1 268px; min-width: 250px; max-width: 100%;
  display: grid; grid-template-columns: minmax(0, 104px) minmax(0, 1fr);
  border-radius: 18px; overflow: hidden;
  background: linear-gradient(180deg, rgba(255, 255, 255, 0.05), rgba(255, 255, 255, 0.015));
  border: 1px solid color-mix(in srgb, var(--gx-accent) 26%, transparent);
}
.gx-reveal-stage.is-one .gx-prize { flex: 1 1 100%; grid-template-columns: minmax(0, 40%) minmax(0, 1fr); }
.gx-prize-art { position: relative; min-height: 104px; background: #070b12; }
.gx-reveal-stage.is-one .gx-prize-art { min-height: 210px; }
.gx-prize-body { padding: 13px 15px; gap: 5px; }
.gx-reveal-stage.is-one .gx-prize-body { padding: 18px 20px; }
.gx-prize-body h4 { font-size: 16px; }
.gx-reveal-stage.is-one .gx-prize-body h4 { font-size: 21px; }
.gx-prize-fallback { font-size: 40px; }

@media (max-width: 760px) {
  .gx-pv { padding: 18px; border-radius: 20px; }
  .gx-pv-head { grid-template-columns: minmax(0, 1fr); gap: 14px; }
  .gx-pv-cover { aspect-ratio: 16 / 9; }
  .gx-pv-title h3 { font-size: 21px; }
  .gx-reveal-stage { max-height: 46vh; }
  .gx-prize { flex: 1 1 100%; }
}
@media (prefers-reduced-motion: reduce) {
  .gx-pv, .gx-pv-veil { transition: none; }
  .gx-pv-veil.on .gx-pv { transform: none; }
}

/* ==========================================================================
   Round 24 — a cosmetic kept for good stands out from a rental.
   Shard cyan, Prism violet, Nova amber — the same three colours the tier chip
   and the crate already use, so a permanent win is unmistakable at a glance.
   ========================================================================== */
.gx-prize.is-perm{outline:2px solid var(--gx-accent);outline-offset:-1px;box-shadow:0 0 0 1px color-mix(in srgb,var(--gx-accent) 30%,transparent),0 0 34px color-mix(in srgb,var(--gx-accent) 26%,transparent)}
.gx-prize.is-perm .gx-prize-tier{background:color-mix(in srgb,var(--gx-accent) 16%,transparent)}
.gx-prize.is-perm .gx-prize-art{background:radial-gradient(120% 100% at 50% 0%,color-mix(in srgb,var(--gx-accent) 34%,transparent),#070b12 74%)}
/* an extended rental is still a rental — the outline never appears on it */
.gx-prize.is-dupe.is-perm{outline-style:dashed}

/* ==========================================================================
   Round 24 — a banner is a wide strip, so show it as a strip.
   The Market insets its banners on a dark backdrop with a rounded border and a
   glow; at thumbnail size a full-bleed banner reads as a broken image. These
   are the shop's own numbers, so it looks the same in both places.
   ========================================================================== */
.gx-pv-art.k-banner{inset:auto 7% 0 7%;top:50%;bottom:auto;transform:translateY(-50%);height:42%;width:86%;border-radius:12px;border:1px solid rgba(255,255,255,.28);align-items:center;box-shadow:0 8px 22px rgba(0,0,0,.5);background-color:#070b12;background-size:cover;background-position:center}
.gx-pv-tile:hover .gx-pv-art.k-banner{transform:translateY(-50%) scale(1.03)}

/* ==========================================================================
   Round 27 — one output at a time, and a permanent arrives in style.
   ========================================================================== */
.gx-prize{position:relative}
.gx-prize-flare,.gx-prize-ring{position:absolute;inset:0;z-index:3;pointer-events:none;border-radius:18px;opacity:0;mix-blend-mode:screen}
.gx-prize-ring{border:2px solid var(--gx-accent)}
/* a timed drop: the plain pop it always had, just later
   (gxPop is the original keyframe, kept) */
.gx-prize{animation-name:gxPop;animation-duration:.36s;animation-delay:calc(var(--d,0) * 1ms);animation-timing-function:cubic-bezier(.2,.9,.3,1.25);animation-fill-mode:both}
/* Shard — a glint sweeps across and it is over quickly */
.gx-prize.is-perm.t-shard{animation-name:gxPopShard,gxHoldShard;animation-duration:.34s,calc(var(--hold,0) * 1ms);animation-delay:calc(var(--d,0) * 1ms),calc(var(--d,0) * 1ms + 340ms);animation-timing-function:cubic-bezier(.2,.9,.3,1.3),ease-in-out;animation-fill-mode:both,both}
.gx-prize.is-perm.t-shard .gx-prize-flare{background:linear-gradient(115deg,transparent 34%,color-mix(in srgb,var(--gx-accent) 72%,#fff) 50%,transparent 66%);animation:gxGlint .5s ease-out calc(var(--d,0) * 1ms) both}
/* Prism — wipes in from the left behind a vertical bar of light */
.gx-prize.is-perm.t-prism{animation-name:gxPopPrism,gxHoldPrism;animation-duration:.4s,calc(var(--hold,0) * 1ms);animation-delay:calc(var(--d,0) * 1ms),calc(var(--d,0) * 1ms + 400ms);animation-timing-function:cubic-bezier(.2,.9,.3,1.25),ease-in-out;animation-fill-mode:both,both}
.gx-prize.is-perm.t-prism .gx-prize-flare{background:linear-gradient(180deg,transparent 30%,color-mix(in srgb,var(--gx-accent) 62%,#fff) 50%,transparent 70%);animation:gxWipe .55s ease-out calc(var(--d,0) * 1ms) both}
/* Nova — lands big, settles, and throws a ring out through the card */
.gx-prize.is-perm.t-nova{animation-name:gxPopNova,gxHoldNova;animation-duration:.52s,calc(var(--hold,0) * 1ms);animation-delay:calc(var(--d,0) * 1ms),calc(var(--d,0) * 1ms + 520ms);animation-timing-function:cubic-bezier(.2,.9,.3,1.3),ease-in-out;animation-fill-mode:both,both}
.gx-prize.is-perm.t-nova .gx-prize-flare{background:radial-gradient(120% 100% at 50% 0%,color-mix(in srgb,var(--gx-accent) 60%,transparent),transparent 72%);animation:gxFlare .7s ease-out calc(var(--d,0) * 1ms) both}
.gx-prize.is-perm.t-nova .gx-prize-ring{animation:gxRing .8s cubic-bezier(.2,.8,.3,1) calc(var(--d,0) * 1ms) both}
/* the three entrances */
@keyframes gxPopShard{0%{opacity:0;transform:translateY(14px) scale(.9)}60%{opacity:1;transform:translateY(0) scale(1.03)}100%{opacity:1;transform:none}}
@keyframes gxPopPrism{0%{opacity:0;transform:translateX(-28px) scale(.94)}55%{opacity:1;transform:translateX(7px) scale(1.02)}100%{opacity:1;transform:none}}
@keyframes gxPopNova{0%{opacity:0;transform:scale(.4) rotate(-4deg)}55%{opacity:1;transform:scale(1.14) rotate(1.5deg)}76%{transform:scale(.97) rotate(0)}100%{opacity:1;transform:none}}
/* the three light shows */
@keyframes gxGlint{0%{opacity:.95;transform:translateX(-130%)}100%{opacity:0;transform:translateX(130%)}}
@keyframes gxWipe{0%{opacity:.95;transform:translateY(-130%)}100%{opacity:0;transform:translateY(130%)}}
@keyframes gxFlare{0%{opacity:0;transform:scale(.35)}30%{opacity:1;transform:scale(1)}100%{opacity:0;transform:scale(1.45)}}
@keyframes gxRing{0%{opacity:.9;transform:scale(.55)}100%{opacity:0;transform:scale(1.7)}}
/* the hold: a slow tier-coloured pulse, so a Nova visibly stays */
@keyframes gxHoldShard{0%,100%{box-shadow:0 0 0 1px color-mix(in srgb,var(--gx-accent) 30%,transparent),0 0 22px color-mix(in srgb,var(--gx-accent) 18%,transparent)}50%{box-shadow:0 0 0 2px color-mix(in srgb,var(--gx-accent) 55%,transparent),0 0 34px color-mix(in srgb,var(--gx-accent) 30%,transparent)}}
@keyframes gxHoldPrism{0%,100%{box-shadow:0 0 0 1px color-mix(in srgb,var(--gx-accent) 34%,transparent),0 0 26px color-mix(in srgb,var(--gx-accent) 20%,transparent)}50%{box-shadow:0 0 0 2px color-mix(in srgb,var(--gx-accent) 60%,transparent),0 0 44px color-mix(in srgb,var(--gx-accent) 34%,transparent)}}
@keyframes gxHoldNova{0%,100%{box-shadow:0 0 0 1px color-mix(in srgb,var(--gx-accent) 38%,transparent),0 0 30px color-mix(in srgb,var(--gx-accent) 22%,transparent)}50%{box-shadow:0 0 0 2px color-mix(in srgb,var(--gx-accent) 70%,transparent),0 0 62px color-mix(in srgb,var(--gx-accent) 42%,transparent)}}
/* Reduced motion: the cards are simply there, all at once. The sounds still stagger,
   because audio is not motion and a silent reveal reads as broken. This has to come
   last — the tier rules above are more specific than the old `.gx-prize{animation:none}`
   and would otherwise win under reduced motion. */
@media (prefers-reduced-motion: reduce) {
  .gx-prize,.gx-prize.is-perm.t-shard,.gx-prize.is-perm.t-prism,.gx-prize.is-perm.t-nova{animation:none}
  .gx-prize-flare,.gx-prize-ring{animation:none;opacity:0}
}

/* a pack reached from a Discord link: a brief accent ring so the player sees
   which pack the announcement was about */
.gx-pack-target,.gx-hero.gx-pack-target{animation:gxPackTarget 2.6s ease-out 1}
@keyframes gxPackTarget{0%,60%{box-shadow:0 0 0 2px #fbbf24,0 0 34px rgba(251,191,36,.45)}100%{box-shadow:0 0 0 0 rgba(251,191,36,0)}}
