@import url("./bubble-reveal.css");
/* styles/replay.css -- Jackpot Replay Viewer */

.replay-panel {
  max-width: 600px;
  margin: 0 auto;
}

.replay-panel .panel-header h2 {
  font-size: clamp(1rem, 3vw, 1.3rem);
  font-weight: 800;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-primary);
  margin: 0;
}

/* --- Controls bar --- */

.replay-controls {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: clamp(0.5rem, 2vw, 0.75rem);
  margin-bottom: clamp(0.75rem, 3vw, 1.25rem);
}

.replay-control-group {
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
  flex: 1;
  min-width: 140px;
}

.replay-label {
  font-size: 0.72rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--text-dim);
}

.replay-select {
  background: var(--bg-tertiary);
  border: 1px solid var(--border-color);
  border-radius: 8px;
  padding: 0.5rem 0.75rem;
  color: var(--text-primary);
  font-family: inherit;
  font-size: 0.85rem;
  font-weight: 500;
  cursor: pointer;
  transition: border-color 0.15s;
  -webkit-appearance: none;
  appearance: none;
  background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%236b6b8d' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 8l4 4 4-4'/%3e%3c/svg%3e");
  background-position: right 0.5rem center;
  background-repeat: no-repeat;
  background-size: 1.25em;
  padding-right: 2rem;
}

.replay-select:focus {
  outline: none;
  border-color: var(--accent-primary);
}

.replay-select:hover {
  border-color: var(--accent-secondary);
}

.replay-reveal-btn {
  white-space: nowrap;
  align-self: flex-end;
  min-height: 38px;
}

/* --- Ticket info bar --- */

.replay-ticket-bar {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  padding: 0.5rem 0.75rem;
  background: var(--bg-tertiary);
  border-radius: 8px;
  margin-bottom: clamp(0.75rem, 3vw, 1rem);
  font-size: 0.85rem;
}

.replay-ticket-count {
  font-weight: 700;
  color: var(--text-primary);
  font-variant-numeric: tabular-nums;
}

.replay-ticket-detail {
  color: var(--text-dim);
}

.replay-winner-text {
  color: var(--success) !important;
}

/* --- Ticket token (whitepaper style: 2x2 grid with flame center) --- */

.replay-ticket {
  width: clamp(240px, 50vw, 300px);
  aspect-ratio: 1;
  margin: 0 auto clamp(0.75rem, 3vw, 1.25rem);
  display: grid;
  grid-template-columns: 1fr 1fr;
  grid-template-rows: 1fr 1fr;
  position: relative;
  background: #d9d9d9;
  border: 3px solid #8a8d8f;
  border-radius: 14px;
  overflow: hidden;
  transition: border-color 0.2s ease, box-shadow 0.2s ease, background 0.2s ease;
}

.replay-ticket.replay-ticket--has-owned-gold {
  border-color: #d4af37;
  box-shadow: 0 0 0 2px rgba(255, 233, 163, 0.22), 0 0 30px rgba(212, 175, 55, 0.42);
}

/* Cross lines */
.replay-ticket::before {
  content: '';
  position: absolute;
  left: 50%;
  top: 0;
  width: 2px;
  height: 100%;
  background: #8a8d8f;
  transform: translateX(-50%);
  z-index: 5;
  pointer-events: none;
}
.replay-ticket::after {
  content: '';
  position: absolute;
  top: 50%;
  left: 0;
  width: 100%;
  height: 2px;
  background: #8a8d8f;
  transform: translateY(-50%);
  z-index: 5;
  pointer-events: none;
}

.replay-tq {
  display: flex;
  align-items: center;
  justify-content: center;
  aspect-ratio: 1;
  width: 100%;
  height: 100%;
  position: relative;
  overflow: hidden;
}

/* A reward callout can extend beyond its own quadrant. Raise its quadrant over
   neighboring scratch canvases, but keep it below the center seal (z20). The
   old z30 promotion briefly painted the revealed quadrant's green paper over
   the center diamond before the callout settled. */
.replay-tq.q-reward-pop-active {
  z-index: var(--replay-quadrant-reward-stack, 1);
  overflow: visible;
}

.replay-tq .badge-img {
  object-fit: contain;
  object-position: 50% 50%;
  flex: 0 0 auto;
  width: 122%;
  max-width: none;
  height: 122%;
  max-height: none;
  padding: 0;
  display: block;
  position: relative;
  z-index: 1;
  opacity: 0;
  transition: none;
}

.replay-tq.spinning .badge-img,
.replay-tq.q-has-trait .badge-img,
.replay-tq.q-no-tickets .badge-img,
.replay-tq.q-scratchable .badge-img,
.replay-tq.q-has-tickets .badge-img {
  opacity: 1;
}

/* Center diamond */
.replay-ticket-center {
  position: absolute;
  top: 50%; left: 50%;
  transform: translate(-50%, -50%);
  z-index: 20;
  width: 28%;
  height: 28%;
  display: flex;
  align-items: center;
  justify-content: center;
  pointer-events: auto;
  cursor: pointer;
}

.replay-ticket-center::before {
  content: '';
  position: absolute;
  top: 50%; left: 50%;
  width: calc(100% / 1.4142);
  height: calc(100% / 1.4142);
  background: #fff;
  border: 2px solid #8a8d8f;
  box-sizing: border-box;
  transform: translate(-50%, -50%) rotate(45deg);
  transition: background 0.4s ease, border-color 0.4s ease;
}

.replay-ticket-center.revealed::before {
  background:
    radial-gradient(circle at 32% 24%, rgba(45, 212, 191, 0.32), transparent 42%),
    linear-gradient(145deg, #071a2c 0%, #172554 48%, #2e1065 100%);
  border-color: #67e8f9;
  box-shadow:
    inset 0 0 0 1px rgba(196, 181, 253, 0.34),
    0 0 16px rgba(99, 102, 241, 0.58);
}

/* Once main + bonus are both uncovered, the center becomes a quiet two-state
   switch. The ring is enough affordance without adding another button row. */
.replay-ticket-center.replay-ticket-center--draw-toggle::before {
  border-color: #f5a623;
  box-shadow: 0 0 0 2px rgba(245, 166, 35, 0.2), 0 0 14px rgba(245, 166, 35, 0.42);
}
.replay-ticket-center.replay-ticket-center--draw-toggle:hover::before,
.replay-ticket-center.replay-ticket-center--draw-toggle:focus-visible::before {
  filter: brightness(1.12);
  box-shadow: 0 0 0 3px rgba(245, 166, 35, 0.3), 0 0 18px rgba(245, 166, 35, 0.58);
}

/* Purchase-day fill-draw craps battle (audit 5790a946). Only for a viewed wallet that was drawn
   into the day's battle: the diamond is glowing craps felt with a 5 and a 2 on it, nothing else,
   and it opens the battle on that wallet's run. The draw toggle's amber ring wins if both apply. */
.replay-ticket-center.replay-ticket-center--craps:not(.replay-ticket-center--draw-toggle)::before {
  background:
    radial-gradient(circle at 50% 50%, rgba(110, 231, 183, 0.34), transparent 64%),
    linear-gradient(145deg, #0d5a41 0%, #0a7a53 52%, #07402d 100%);
  border-color: #6ee7b7;
  box-shadow: 0 0 0 2px rgba(52, 211, 153, 0.3), 0 0 18px rgba(16, 185, 129, 0.62);
  animation: replay-craps-glow 2.2s ease-in-out infinite;
}
.replay-ticket-center.replay-ticket-center--craps:hover::before,
.replay-ticket-center.replay-ticket-center--craps:focus-visible::before {
  filter: brightness(1.16);
}
.replay-ticket-center.replay-ticket-center--craps:focus-visible { outline: none; }
.replay-ticket-center.replay-ticket-center--craps.is-working { cursor: progress; }
.replay-ticket-center.replay-ticket-center--craps.is-error:not(.replay-ticket-center--draw-toggle)::before {
  border-color: #fca5a5;
  box-shadow: 0 0 0 2px rgba(248, 113, 113, 0.32), 0 0 16px rgba(239, 68, 68, 0.5);
  animation: none;
}

.replay-center-dice {
  position: absolute;
  inset: 0;
  z-index: 4;
  display: none;
  pointer-events: none;
}
.replay-ticket-center--craps:not(.replay-ticket-center--draw-toggle) .replay-center-dice { display: block; }
/* The dice are the dice trait badges (see CRAPS_CENTRE_DICE), tossed along the diamond's axis. The
   badge disc is 69% of its image, so a 44% image is a 30% die. Centred at (37.5%, 40.5%) and
   (62.5%, 59.5%), each disc reaches 43% of the way to the diamond's edge (50%), so neither the
   rim nor its float ever touches the border or its glow, and the two dice never overlap. */
.replay-center-dice img {
  position: absolute;
  width: 44%;
  aspect-ratio: 1;
  filter: drop-shadow(0 2px 3px rgba(0, 0, 0, 0.55)) drop-shadow(0 0 6px rgba(110, 231, 183, 0.55));
  animation: replay-craps-dice-float 2.2s ease-in-out infinite;
}
.replay-center-dice img:first-child { top: 18.5%; left: 15.5%; --die-tilt: -12deg; }
.replay-center-dice img:last-child { top: 37.5%; left: 40.5%; --die-tilt: 10deg; animation-delay: -1.1s; }
.replay-ticket-center--craps:hover .replay-center-dice img,
.replay-ticket-center--craps:focus-visible .replay-center-dice img { filter: drop-shadow(0 2px 3px rgba(0, 0, 0, 0.55)) drop-shadow(0 0 9px rgba(167, 243, 208, 0.85)); }
.replay-ticket-center--craps.is-working .replay-center-dice img { animation: replay-craps-dice-spin 0.7s linear infinite; }
/* The dice replace the flame while the battle owns the centre. */
.replay-ticket-center--craps:not(.replay-ticket-center--draw-toggle) .replay-flame { opacity: 0; }

@keyframes replay-craps-glow {
  0%, 100% { box-shadow: 0 0 0 2px rgba(52, 211, 153, 0.26), 0 0 12px rgba(16, 185, 129, 0.5); }
  50% { box-shadow: 0 0 0 4px rgba(52, 211, 153, 0.4), 0 0 28px rgba(16, 185, 129, 0.85); }
}

@keyframes replay-craps-dice-float {
  0%, 100% { transform: rotate(var(--die-tilt, 0deg)) translateY(0); }
  50% { transform: rotate(var(--die-tilt, 0deg)) translateY(-2%); }
}

@keyframes replay-craps-dice-spin {
  from { transform: rotate(var(--die-tilt, 0deg)); }
  to { transform: rotate(calc(var(--die-tilt, 0deg) + 360deg)); }
}

@media (prefers-reduced-motion: reduce) {
  .replay-ticket-center.replay-ticket-center--craps::before { animation: none; }
  .replay-center-dice img { animation: none; transform: rotate(var(--die-tilt, 0deg)); }
}

.replay-flame {
  position: relative;
  width: 66%;
  height: 66%;
  object-fit: contain;
  z-index: 1;
}

/* --- Center diamond scratch canvas --- */

.replay-center-canvas {
  position: absolute;
  top: 0; left: 0;
  width: 100%; height: 100%;
  z-index: 5;
  display: none;
  cursor: url('/app/assets/scratch-coin-cursor.svg') 20 20, crosshair;
  clip-path: polygon(50% 0%, 100% 50%, 50% 100%, 0% 50%);
  touch-action: none;
}

/* --- Center prize overlay --- */

.replay-center-prize {
  position: absolute;
  top: 50%; left: 50%;
  transform: translate(-50%, -50%);
  z-index: 3;
  text-align: center;
  font-weight: 700;
  line-height: 1.2;
  display: none;
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.4s ease;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.08rem;
  box-sizing: border-box;
  width: 80%;
  min-width: 0;
  padding-inline: 0.08rem;
}

.replay-center-prize.visible {
  opacity: 1;
}

.replay-center-prize .ff-logo {
  display: block;
  width: 1.24rem;
  height: 1.24rem;
  object-fit: contain;
  filter: drop-shadow(0 0 5px rgba(255, 255, 255, 0.32));
}

.replay-center-prize .ff-amount {
  width: 100%;
  font-size: clamp(0.64rem, 2.35vw, 0.78rem);
  display: block;
  max-width: 100%;
  color: #ecfeff;
  text-shadow: 0 0 9px rgba(103, 232, 249, 0.62), 0 1px 2px rgba(0, 0, 0, 0.72);
  letter-spacing: -0.01em;
  line-height: 0.96;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.replay-center-prize .ff-label {
  font-size: 0.44rem;
  color: #fde68a;
  display: block;
  margin-top: 0.05rem;
  letter-spacing: 0.14em;
  line-height: 1;
  text-transform: uppercase;
  text-shadow: 0 0 7px rgba(250, 204, 21, 0.42);
}

/* --- Spin + Scratch quadrant states (ported from jackpot-demo.html) --- */

.replay-tq.q-has-trait {
  /* Opaque by design: an owned gold quadrant may turn the outer border gold,
     but must never tint another quadrant's ordinary possible-win blue. */
  background: #b8d4e8;
}

.replay-tq.q-no-tickets {
  background: rgba(239, 120, 120, 0.5);
}

/* The new day/player is selected before its level-scoped holdings request can
   finish. Unknown is not a four-quadrant loss: keep the attract reel on neutral
   cabinet paper until that request proves the ordinary pink/blue state. */
.replay-tq.q-traits-pending {
  background: #bfc2c7;
  box-shadow: none;
}

.replay-tq.q-scratchable {
  background: #7ca8c4;
}

.replay-tq.q-has-tickets {
  background: rgba(22, 163, 74, 0.6);
}

.replay-tq.q-owned-miss {
  /* The removable canvas is the blue "you held this trait" front. Once it is
     scratched, a loss uses the same pink result surface as every other miss. */
  background: rgba(239, 120, 120, 0.5);
}

.replay-tq.q-player-win {
  background: rgba(22, 163, 74, 0.72);
  box-shadow: inset 0 0 0 2px rgba(134, 239, 172, 0.55);
}

.replay-tq.q-win-impossible {
  /* Lighter losing paper revealed beneath the darker removable cover. */
  background: rgba(239, 120, 120, 0.5);
  transition: background-color 0.22s ease, box-shadow 0.22s ease;
}

.replay-tq.q-win-impossible-lock {
  /* The darker face appears the instant an impossible target locks. The
     scratch canvas is painted this exact color before this class is removed,
     so lock → scratch remains visually continuous. */
  background: rgb(218, 104, 104);
  transition: background-color 0.22s ease, box-shadow 0.22s ease;
}

.replay-tq.q-gold-trait {
  background:
    linear-gradient(135deg, #8a6a12 0%, #d4af37 30%, #fff0ad 50%, #d4af37 70%, #8a6a12 100%);
  box-shadow: inset 0 0 0 2px rgba(255, 244, 176, 0.62);
}

/* The removable canvas above this layer carries the pale-blue, darker-pink, or
   gold settled face. During a partial scratch the paper underneath stays
   neutral; hitting a paid badge removes this class for its win color immediately,
   and crossing the reveal threshold exposes the final loss/win result. */
.replay-tq.q-scratch-underlay,
.replay-tq.q-result-pending {
  background: #bfc2c5;
  box-shadow: none;
}

.replay-tq.q-result-revealed.q-has-tickets {
  background: rgba(22, 163, 74, 0.72);
  box-shadow: inset 0 0 0 2px rgba(134, 239, 172, 0.55);
}

/* The main draw's one-wallet ETH bucket is the day's marquee prize. Keep
   ordinary winning buckets green, while the solo winner resolves to blue. */
.replay-tq.q-solo-eth-win.q-has-tickets {
  background: rgba(37, 99, 235, 0.72);
  box-shadow: inset 0 0 0 2px rgba(147, 197, 253, 0.68);
}

.replay-tq.q-result-revealed.q-no-tickets {
  background: rgba(239, 120, 120, 0.5);
  box-shadow: none;
}

/* Comps use teal once revealed; the covered surface keeps its eligibility color. */
.replay-tq.q-craps-comp-win.q-has-tickets:not(.q-scratch-underlay):not(.q-result-pending) {
  background: rgba(8, 145, 178, 0.72);
  box-shadow: inset 0 0 0 2px rgba(103, 232, 249, 0.65);
}

/* --- Scratch canvas --- */

.replay-scratch-canvas {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  z-index: 2;
  cursor: url('/app/assets/scratch-coin-cursor.svg') 20 20, crosshair;
  touch-action: none;
}

/* --- Prize reveal overlay --- */

.replay-prize-reveal {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 3;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  align-items: stretch;
  opacity: 0;
  transition: opacity 0.4s ease;
  pointer-events: none;
}

.replay-prize-reveal.visible {
  opacity: 1;
}

/* Compact per-quadrant receipt. A player win reserves the quadrant's outside
   edge for this copy: top on the upper pair, bottom on the lower pair. Badge
   placement uses the complementary art band, so neither layer has to win a
   z-index fight to remain legible. */
.replay-prize-reveal.replay-player-win-reveal[data-pos="tl"],
.replay-prize-reveal.replay-player-win-reveal[data-pos="tr"] {
  justify-content: flex-start;
}

.replay-prize-reveal.replay-player-win-reveal[data-pos="bl"],
.replay-prize-reveal.replay-player-win-reveal[data-pos="br"] {
  justify-content: flex-end;
}

.replay-win-description {
  box-sizing: border-box;
  width: 94%;
  align-self: center;
  max-width: 94%;
  margin: 0;
  padding: 0.34rem 0.48rem;
  border: 1px solid rgba(187, 247, 208, 0.62);
  border-radius: 10px;
  background: linear-gradient(135deg, rgba(5, 46, 22, 0.97), rgba(6, 78, 59, 0.94));
  box-shadow: 0 3px 14px rgba(5, 46, 22, 0.42), inset 0 1px 0 rgba(240, 253, 244, 0.12);
  color: #f0fdf4;
  opacity: 1;
  text-align: center;
  transform: translateY(0) scale(1);
  transition: opacity 180ms ease, transform 220ms cubic-bezier(0.2, 0.78, 0.24, 1);
}

.replay-win-description--craps-comp {
  border-color: rgba(103, 232, 249, 0.72);
  background: linear-gradient(135deg, rgba(8, 51, 68, 0.97), rgba(14, 86, 101, 0.96));
  box-shadow: 0 3px 14px rgba(8, 51, 68, 0.42), inset 0 1px 0 rgba(207, 250, 254, 0.16);
  color: #ecfeff;
}

.replay-win-description--craps-comp .replay-win-description__title,
.replay-win-description__line--craps-comp {
  color: #a5f3fc;
}

.replay-win-description.is-waiting-for-reward-popups {
  visibility: hidden;
  opacity: 0;
  transform: translateY(3px) scale(0.97);
}

.replay-player-win-reveal[data-pos="tl"] .replay-win-description,
.replay-player-win-reveal[data-pos="tr"] .replay-win-description {
  margin-top: 4%;
}

.replay-player-win-reveal[data-pos="bl"] .replay-win-description,
.replay-player-win-reveal[data-pos="br"] .replay-win-description {
  margin-bottom: 4%;
}

.replay-win-description__title {
  display: block;
  margin-bottom: 0.18rem;
  color: #bbf7d0;
  font-size: clamp(0.46rem, 1.4vw, 0.62rem);
  font-weight: 900;
  letter-spacing: 0.14em;
}

.replay-win-description__lines {
  display: grid;
  justify-items: center;
  gap: 0.14rem;
}

.replay-win-description__line {
  display: inline-flex;
  align-items: center;
  gap: 0.16em;
  font-size: clamp(0.58rem, 1.8vw, 0.78rem);
  font-weight: 850;
  font-variant-numeric: tabular-nums;
  line-height: 1.1;
  white-space: nowrap;
}

.replay-win-description__currency-icon {
  display: block;
  width: 1.05em;
  height: 1.05em;
  flex: 0 0 auto;
  object-fit: contain;
}

/* --- Scattered win badges --- */

.replay-badge-wrap {
  --replay-badge-rotation: 0deg;
  --replay-badge-layer: 1;
  position: absolute;
  pointer-events: none;
  text-align: center;
}

/* Hitting one badge exposes the prize paper before the quadrant reaches its
   completion threshold. Keep every badge below the live scratch canvas
   until #revealQuadrant promotes it to tabindex=0; otherwise a multi-win
   quadrant exposes all badges at once and they steal subsequent scratches. */
.replay-tq.q-result-revealed .replay-badge-wrap[tabindex="0"] {
  z-index: calc(5 + var(--replay-badge-layer));
  pointer-events: auto;
  cursor: default;
}

.replay-badge-wrap:focus-visible {
  border-radius: 50%;
  outline: 2px solid rgba(255, 255, 255, 0.88);
  outline-offset: 1px;
}

.replay-scattered-badge {
  position: relative;
  z-index: 1;
  width: 100%;
  display: block;
  opacity: 0.85;
  filter: drop-shadow(0 1px 3px rgba(0, 0, 0, 0.25));
  object-fit: contain;
  transform: rotate(var(--replay-badge-rotation));
  transform-origin: 50% 50%;
  transition: transform 150ms ease, filter 150ms ease, opacity 150ms ease;
}

/* The solo bucket's marquee badge is presentation art, not a faded member of
   the ordinary scattered-badge field. Keep it fully opaque at rest. */
.replay-badge-wrap--solo .replay-scattered-badge {
  opacity: 1;
}

/* Its 92% badge intentionally reaches into the receipt lane. The quadrant's
   YOU WON label must remain the foreground layer once that receipt appears. */
.replay-tq.q-solo-eth-win
  .replay-prize-reveal.replay-player-win-reveal {
  z-index: 7;
}

.replay-tq.q-result-revealed .replay-badge-wrap[tabindex="0"]:hover .replay-scattered-badge,
.replay-tq.q-result-revealed .replay-badge-wrap[tabindex="0"]:focus-visible .replay-scattered-badge {
  opacity: 1;
  filter: drop-shadow(0 3px 5px rgba(0, 0, 0, 0.4));
  transform: rotate(var(--replay-badge-rotation)) scale(1.055);
}

.replay-badge-reward-pop {
  --replay-reward-base-x: -50%;
  --replay-reward-base-y: 0%;
  --replay-reward-enter-x: 0px;
  --replay-reward-enter-y: 4px;
  --replay-reward-exit-x: 0px;
  --replay-reward-exit-y: -8px;
  position: absolute;
  z-index: 12;
  left: 50%;
  bottom: calc(100% + 4px);
  display: grid;
  min-width: 4.25rem;
  gap: 0.14rem;
  padding: 0.34rem 0.5rem;
  visibility: hidden;
  overflow: hidden;
  border: 1px solid rgba(255, 214, 105, 0.62);
  border-radius: 9px;
  background:
    linear-gradient(180deg, rgba(255, 224, 133, 0.14), transparent 38%),
    linear-gradient(145deg, rgba(46, 25, 9, 0.93), rgba(8, 12, 25, 0.94));
  box-shadow:
    0 5px 14px rgba(0, 0, 0, 0.42),
    0 0 10px rgba(250, 190, 58, 0.25),
    inset 0 0 0 1px rgba(255, 247, 211, 0.07);
  opacity: 0;
  pointer-events: none;
  transform:
    translate(var(--replay-reward-base-x), var(--replay-reward-base-y))
    translate(var(--replay-reward-enter-x), var(--replay-reward-enter-y))
    scale(0.76);
  transform-origin: 50% 100%;
  will-change: opacity, transform;
}

.replay-badge-reward-pop::before {
  position: absolute;
  inset: 0 12% auto;
  height: 1px;
  background: linear-gradient(90deg, transparent, rgba(255, 239, 174, 0.9), transparent);
  content: '';
}

.replay-badge-wrap[data-reward-direction="below"] .replay-badge-reward-pop {
  --replay-reward-enter-y: -4px;
  --replay-reward-exit-y: 8px;
  top: calc(100% + 4px);
  bottom: auto;
  transform-origin: 50% 0;
}

.replay-badge-wrap[data-reward-direction="right"] .replay-badge-reward-pop {
  --replay-reward-base-x: 0%;
  --replay-reward-base-y: -50%;
  --replay-reward-enter-x: -4px;
  --replay-reward-enter-y: 0px;
  --replay-reward-exit-x: 8px;
  --replay-reward-exit-y: 0px;
  inset: 50% auto auto calc(100% + 4px);
  transform-origin: 0 50%;
}

.replay-badge-wrap[data-reward-direction="left"] .replay-badge-reward-pop {
  --replay-reward-base-x: 0%;
  --replay-reward-base-y: -50%;
  --replay-reward-enter-x: 4px;
  --replay-reward-enter-y: 0px;
  --replay-reward-exit-x: -8px;
  --replay-reward-exit-y: 0px;
  inset: 50% calc(100% + 4px) auto auto;
  transform-origin: 100% 50%;
}

/* A solo-winner badge fills almost the entire quadrant, leaving no outside
   lane for its amount plate. Center that plate over the badge so the cabinet's
   clipped outer edge cannot eat the prize amount. */
.replay-badge-wrap[data-reward-direction="inside"] .replay-badge-reward-pop {
  --replay-reward-base-x: -50%;
  --replay-reward-base-y: -50%;
  --replay-reward-enter-y: 4px;
  --replay-reward-exit-y: -4px;
  top: 50%;
  bottom: auto;
  transform-origin: 50% 50%;
}
.replay-tq.q-result-revealed .replay-badge-wrap.is-reward-pop[tabindex="0"] {
  z-index: calc(15 + var(--replay-reward-stack, 1));
}
.replay-badge-wrap.is-reward-pop .replay-badge-reward-pop {
  visibility: visible;
  animation: replay-badge-reward-pop 0.78s cubic-bezier(0.2, 0.78, 0.24, 1) both;
}

.replay-badge-wrap.is-reward-pop .replay-scattered-badge {
  animation: replay-badge-reward-hit 0.3s cubic-bezier(0.18, 0.86, 0.3, 1) both;
}

.replay-badge-reward-pop__row {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.26rem;
  color: #fff4c9;
  white-space: nowrap;
}

.replay-badge-reward-pop__row b {
  color: inherit;
  font: 1000 clamp(0.68rem, 2vw, 0.84rem)/1 "Roboto Mono", ui-monospace, monospace;
  font-variant-numeric: tabular-nums;
  text-shadow: 0 0 8px currentColor;
}

.replay-badge-reward-pop__row--flip { color: #ffcf64; }
.replay-badge-reward-pop__row--eth { color: #dcfce7; }
.replay-badge-reward-pop__row--tickets { color: #dbeafe; }
.replay-badge-reward-pop__row--craps-pass,
.tip-row.tip-row--craps-comp { color: #67e8f9; }

.replay-badge-reward-pop__icon,
.replay-badge-reward-pop__ticket {
  width: 1.1rem;
  height: 1.1rem;
  flex: 0 0 auto;
  object-fit: contain;
}

@keyframes replay-badge-reward-pop {
  0% {
    opacity: 0;
    transform:
      translate(var(--replay-reward-base-x), var(--replay-reward-base-y))
      translate(var(--replay-reward-enter-x), var(--replay-reward-enter-y))
      scale(0.76);
  }
  18% {
    opacity: 1;
    transform: translate(var(--replay-reward-base-x), var(--replay-reward-base-y)) translate(0, 0) scale(1.08);
  }
  31% {
    opacity: 1;
    transform: translate(var(--replay-reward-base-x), var(--replay-reward-base-y)) translate(0, 0) scale(0.98);
  }
  44%, 68% {
    opacity: 0.96;
    transform: translate(var(--replay-reward-base-x), var(--replay-reward-base-y)) translate(0, 0) scale(1);
  }
  100% {
    opacity: 0;
    transform:
      translate(var(--replay-reward-base-x), var(--replay-reward-base-y))
      translate(var(--replay-reward-exit-x), var(--replay-reward-exit-y))
      scale(1.04);
  }
}

@keyframes replay-badge-reward-hit {
  0% { transform: rotate(var(--replay-badge-rotation)) scale(1); }
  46% { transform: rotate(var(--replay-badge-rotation)) scale(1.11); }
  100% { transform: rotate(var(--replay-badge-rotation)) scale(1.055); }
}

@media (prefers-reduced-motion: reduce) {
  .replay-badge-wrap.is-reward-pop .replay-badge-reward-pop { animation-duration: 0.3s; }
  .replay-badge-wrap.is-reward-pop .replay-scattered-badge { animation: none; }
}

/* "+N more" label shown when win count exceeds the 20-badge visual cap */
.replay-badge-overflow-label {
  position: absolute;
  bottom: 4%;
  right: 4%;
  background: rgba(0, 0, 0, 0.65);
  color: #fff;
  font-size: 0.65rem;
  font-weight: 600;
  padding: 2px 6px;
  border-radius: 4px;
  pointer-events: none;
  z-index: 2;
  letter-spacing: 0.02em;
}

/* Keep the overflow count in the art band as well. */
.replay-tq[data-pos="tl"] .replay-badge-overflow-label,
.replay-tq[data-pos="tr"] .replay-badge-overflow-label {
  top: auto;
  bottom: 4%;
}

.replay-tq[data-pos="bl"] .replay-badge-overflow-label,
.replay-tq[data-pos="br"] .replay-badge-overflow-label {
  top: 4%;
  bottom: auto;
}

.replay-tq[data-pos="tl"] .replay-badge-overflow-label,
.replay-tq[data-pos="bl"] .replay-badge-overflow-label {
  left: 4%;
  right: auto;
}

/* --- Hint text --- */

.replay-hint {
  text-align: center;
  font-size: 0.72rem;
  color: var(--text-dim);
  letter-spacing: 0.04em;
  min-height: 1.2em;
  margin-top: 0.5rem;
  margin-bottom: 0.5rem;
}

/* --- Spin animation --- */

.replay-ticket-center.spinning .replay-flame {
  animation: replay-flame-pulse 0.6s ease-in-out infinite;
}

@keyframes replay-flame-pulse {
  0%, 100% { filter: brightness(1) sepia(0) saturate(1) hue-rotate(0deg); }
  50% { filter: brightness(1.4) sepia(1) saturate(5) hue-rotate(-30deg); }
}

/* --- Distributions --- */

.replay-distributions {
  margin-top: clamp(0.5rem, 2vw, 0.75rem);
}

.replay-dist-title {
  font-size: 0.8rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--text-dim);
  margin: 0 0 0.5rem;
}

.replay-dist-list {
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
}

.replay-dist-item {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  padding: 0.4rem 0.6rem;
  background: var(--bg-tertiary);
  border-radius: 6px;
  font-size: 0.82rem;
}

.replay-dist-winner {
  font-weight: 600;
  color: var(--accent-secondary);
  font-family: monospace;
}

.replay-dist-mine {
  background: rgba(22, 163, 74, 0.15);
  border: 1px solid rgba(22, 163, 74, 0.3);
}

.replay-dist-mine .replay-dist-winner {
  color: var(--success);
}

.replay-dist-amount {
  font-weight: 700;
  color: var(--success);
  font-variant-numeric: tabular-nums;
  margin-left: auto;
}

.replay-dist-type {
  color: var(--text-dim);
  font-size: 0.75rem;
  text-transform: uppercase;
  letter-spacing: 0.05em;
}

/* --- Winner hover tooltip (D1) --- */

.replay-dist-item {
  cursor: default;
}

.winner-tip {
  display: none;
  position: absolute;
  left: 0;
  top: calc(100% + 4px);
  min-width: 200px;
  max-width: 300px;
  background: var(--bg-panel, #1a1a2e);
  border: 1px solid var(--border-color, #3a3a5c);
  border-radius: 8px;
  padding: 0.6rem 0.75rem;
  z-index: 100;
  box-shadow: 0 4px 16px rgba(0,0,0,0.5);
  font-size: 0.78rem;
  color: var(--text-primary);
  pointer-events: none;
}

.replay-dist-item:hover .winner-tip {
  display: block;
}

.tip-trait-group {
  margin-bottom: 0.5rem;
}

.tip-trait-group:last-child {
  margin-bottom: 0;
}

.tip-trait-name {
  display: block;
  font-weight: 700;
  font-size: 0.75rem;
  color: var(--accent-secondary, #7c6af7);
  text-transform: capitalize;
  letter-spacing: 0.04em;
  margin-bottom: 0.2rem;
}

.tip-row {
  display: block;
  padding-left: 0.6rem;
  color: var(--text-secondary, #c0c0d8);
  line-height: 1.5;
}

.tip-phase-header {
  display: block;
  font-size: 0.65rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--text-muted, #888);
  border-bottom: 1px solid var(--border-color, #3a3a5c);
  margin: 0.4rem 0 0.35rem;
  padding-bottom: 0.2rem;
}

.tip-phase-header:first-child {
  margin-top: 0;
}

.replay-dist-bonus-badge {
  font-size: 0.65rem;
  font-weight: 700;
  padding: 0.1rem 0.35rem;
  background: rgba(124, 106, 247, 0.25);
  border: 1px solid rgba(124, 106, 247, 0.5);
  border-radius: 4px;
  color: #a08cf8;
  letter-spacing: 0.05em;
  white-space: nowrap;
  margin-left: auto;
}

/* --- Bonus roll section (D2) --- */

.replay-bonus-section {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.5rem;
  margin: clamp(0.75rem, 3vw, 1rem) 0;
}

.replay-bonus-btn {
  background: linear-gradient(135deg, #4c1d95, #6d28d9);
  border-color: #7c3aed;
  color: #fff;
  font-weight: 700;
  letter-spacing: 0.05em;
  transition: background 0.2s, box-shadow 0.2s;
}

.replay-bonus-btn:hover:not(:disabled) {
  background: linear-gradient(135deg, #5b21b6, #7c3aed);
  box-shadow: 0 0 16px rgba(124, 58, 237, 0.5);
}

.replay-no-bonus {
  font-size: 0.8rem;
  color: var(--text-dim);
  font-style: italic;
  margin: 0;
}

/* --- Empty state --- */

.replay-empty {
  text-align: center;
  padding: clamp(2rem, 6vw, 3rem) 1rem;
  color: var(--text-dim);
  font-size: 0.9rem;
}

/* --- Mobile --- */

@media (max-width: 640px) {
  .replay-controls {
    flex-direction: column;
  }

  .replay-control-group {
    min-width: unset;
    width: 100%;
  }

  .replay-reveal-btn {
    width: 100%;
  }
}

/* --- Public result when the viewed player has no winning entry ----------------
   Every main-draw quadrant stays scratchable. Its red cover communicates that
   the player cannot win there; scratching reveals the bucket's actual badge,
   ETH amount, ticket amount, and one combined winning-entry count. */
.replay-prize-reveal.replay-bucket-reveal {
  box-sizing: border-box;
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  grid-template-rows: minmax(0, 1fr) clamp(2.2rem, 9vw, 2.5rem);
  align-items: center;
  justify-items: center;
  gap: 0.22rem;
  text-align: center;
  padding: 0.1rem;
}

/* The center diamond occupies every quadrant's inner corner. Reserve that
   corner instead of allowing it to sit over payout copy; the modifier follows
   display order (TL, TR, BL, BR) and leaves the outer corner for the receipt. */
.replay-prize-reveal.replay-bucket-reveal--q0 { padding: 4% 14% 14% 4%; }
.replay-prize-reveal.replay-bucket-reveal--q1 { padding: 4% 4% 14% 14%; }
.replay-prize-reveal.replay-bucket-reveal--q2 { padding: 14% 14% 4% 4%; }
.replay-prize-reveal.replay-bucket-reveal--q3 { padding: 14% 4% 4% 14%; }

/* Ordinary public results use one badge scale in both draws. The main draw's
   loss tint and the bonus draw's neutral surface must not change its size. */
.replay-bucket-badge-stage {
  display: grid;
  width: 100%;
  height: 100%;
  min-width: 0;
  min-height: 0;
  place-items: center;
}

.replay-bucket-badge {
  width: 68%;
  aspect-ratio: 1;
  object-fit: contain;
  grid-area: 1 / 1;
}

/* Preserve the main draw's pink loss language after the cover is gone without
   moving or muting the public winning badge and payout underneath. */
.replay-prize-reveal.replay-bucket-reveal--main-miss {
  background:
    repeating-linear-gradient(135deg, rgba(105, 20, 31, 0.045) 0 1px, transparent 1px 8px),
    rgba(239, 120, 120, 0.16);
  box-shadow: inset 0 0 0 2px rgba(111, 25, 36, 0.22);
}

/* The solo ETH result is only one step larger than an ordinary public result.
   Its auto-height receipt leaves the rest of the quadrant to a top-anchored
   badge, so the payout can never cover the artwork. */
.replay-prize-reveal.replay-bucket-reveal--solo-eth {
  grid-template-rows: minmax(0, 1fr) auto;
  gap: 0.42rem;
}

.replay-bucket-reveal--solo-eth .replay-bucket-badge-stage {
  place-items: start center;
}

.replay-bucket-reveal--main-miss.replay-bucket-reveal--solo-eth .replay-bucket-badge {
  width: 76%;
  height: 100%;
  aspect-ratio: auto;
  object-position: 50% 0;
  transform: translateY(-0.18rem);
}

/* The fixed receipt lane keeps badge centers identical even when a bucket has
   no ticket award or the solo ETH capsule uses larger type. */
.replay-bucket-receipt {
  display: flex;
  width: 100%;
  height: 100%;
  min-width: 0;
  flex-direction: column;
  align-items: center;
  justify-content: flex-start;
  gap: 0.22rem;
}

/* A solo receipt remains one compact row when the bucket also has tickets.
   This preserves badge height instead of stacking rewards into its lane. */
.replay-bucket-reveal--solo-eth .replay-bucket-receipt {
  height: auto;
  flex-direction: row;
  justify-content: center;
  gap: 0.3rem;
  transform: translateY(-0.28rem);
}

/* The lower quadrants have more open paper beneath the center diamond. Raise
   their rewards slightly in both the main and bonus draws. */
.replay-bucket-reveal--q2 .replay-bucket-amount,
.replay-bucket-reveal--q2 .replay-bucket-tickets,
.replay-bucket-reveal--q3 .replay-bucket-amount,
.replay-bucket-reveal--q3 .replay-bucket-tickets {
  transform: translateY(-0.16rem);
}

/* Protect the intentionally separated solo badge/receipt lanes. */
.replay-bucket-reveal--solo-eth.replay-bucket-reveal--q2 .replay-bucket-amount,
.replay-bucket-reveal--solo-eth.replay-bucket-reveal--q2 .replay-bucket-tickets,
.replay-bucket-reveal--solo-eth.replay-bucket-reveal--q3 .replay-bucket-amount,
.replay-bucket-reveal--solo-eth.replay-bucket-reveal--q3 .replay-bucket-tickets {
  transform: none;
}

.replay-bucket-reveal--solo-eth .replay-bucket-amount {
  gap: 0.22em;
  font-size: clamp(0.82rem, 2.55vw, 1.18rem);
}

/* Keep the solo payout in the same muted loss-result language as every other
   amount. Its slightly larger type, not a win-like plate, provides emphasis. */
.replay-bucket-amount--solo-eth {
  background: transparent;
  color: #5c1616;
  box-shadow: none;
}

.replay-bucket-reveal--solo-eth .replay-bucket-eth {
  height: 1.28em;
}

.replay-bucket-amount {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.18em;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
  font-size: clamp(0.72rem, 2.35vw, 1.08rem);
  font-weight: 900;
  line-height: 1.05;
  color: #5c1616;
  text-shadow: 0 1px 0 rgba(255, 255, 255, 0.35);
}

.replay-bucket-eth {
  height: 1.26em;
  width: auto;
  flex: 0 0 auto;
}

.replay-bucket-tickets {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.28rem;
  color: #1f4f7a;
  font-size: clamp(0.62rem, 1.9vw, 0.82rem);
  font-variant-numeric: tabular-nums;
  font-weight: 950;
  letter-spacing: 0.04em;
  line-height: 1;
  white-space: nowrap;
}

.replay-bucket-ticket-count {
  line-height: 1;
}

.replay-bucket-craps {
  gap: 0.2em;
  color: #155e75;
}

.replay-bucket-craps__unit {
  font-size: 0.85em;
  line-height: 1.05;
}

.replay-bucket-row-count {
  flex: 0 0 auto;
  margin-left: 0.08em;
  font-size: 0.78em;
  font-weight: 950;
  letter-spacing: 0.02em;
  line-height: 1;
  white-space: nowrap;
}
.replay-bucket-row-count--currency { color: rgba(92, 22, 22, 0.78); }
.replay-bucket-row-count--tickets { color: rgba(31, 79, 122, 0.82); }

/* A miniature version of the actual Degenerus ticket: four paper quadrants,
   crossed seams, and the same centered flame used by inventory tickets. */
.replay-bucket-ticket-icon {
  position: relative;
  display: inline-grid;
  place-items: center;
  width: 1.72em;
  height: 1.72em;
  flex: 0 0 auto;
  overflow: hidden;
  border: 1px solid #777b7e;
  border-radius: 0.22em;
  background:
    linear-gradient(90deg, transparent calc(50% - 0.5px), #85898c calc(50% - 0.5px), #85898c calc(50% + 0.5px), transparent calc(50% + 0.5px)),
    linear-gradient(0deg, transparent calc(50% - 0.5px), #85898c calc(50% - 0.5px), #85898c calc(50% + 0.5px), transparent calc(50% + 0.5px)),
    linear-gradient(135deg, #e8e8e8, #c9ccce);
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.36);
}

.replay-bucket-ticket-icon::before {
  position: absolute;
  inset: 50% auto auto 50%;
  z-index: 2;
  width: 44%;
  aspect-ratio: 1;
  border: 1px solid #777b7e;
  border-radius: 0.08em;
  background: #fff;
  content: '';
  transform: translate(-50%, -50%) rotate(45deg);
}

.replay-bucket-ticket-badge {
  position: absolute;
  z-index: 1;
  width: 36%;
  height: 36%;
  object-fit: contain;
}
.replay-bucket-ticket-badge--q0 { top: 4%; left: 4%; }
.replay-bucket-ticket-badge--q1 { top: 4%; right: 4%; }
.replay-bucket-ticket-badge--q2 { bottom: 4%; left: 4%; }
.replay-bucket-ticket-badge--q3 { right: 4%; bottom: 4%; }

.replay-bucket-ticket-flame {
  position: relative;
  z-index: 3;
  width: 30%;
  height: 42%;
  object-fit: contain;
}

/* Shared air-pocket reveal; legacy canvas nodes remain inert for saved tutorial state. */
.replay-scratch-canvas, .replay-center-canvas { display: none !important; pointer-events: none !important; }
.replay-tq { isolation: isolate; }
.replay-bubble-trigger {
  position: absolute; inset: 0; z-index: 100; display: block; width: 100%; height: 100%;
  padding: 0; border: 0; border-radius: inherit; background: none; cursor: pointer;
  touch-action: manipulation; -webkit-tap-highlight-color: transparent;
}
.replay-bubble-trigger:disabled { opacity: 1; cursor: default; }
.replay-bubble-trigger:focus-visible { outline: 3px solid #fff1a5; outline-offset: -5px; }
.replay-tq .replay-bubble-trigger .bubble-reveal__badge {
  /* The cover's resting 1.025 scale brings this to the spinning badge's 122%. */
  flex-shrink: 0;
  width: 119%;
  height: 119%;
}
.replay-bubble-trigger--center { clip-path: polygon(50% 0,100% 50%,50% 100%,0 50%); z-index: 2; }
.replay-bubble-trigger--center .bubble-reveal { border-radius: 0; }
.replay-tq.q-result-revealed.q-no-tickets {
  background: repeating-linear-gradient(135deg,#69141f0b 0 1px,transparent 1px 8px),#dfaeae;
}
.replay-tq.q-result-revealed .replay-badge-wrap[tabindex="0"] { cursor: pointer; }
.replay-win-flight {
  position: absolute; z-index: 99; display: grid; gap: 2px; box-sizing: border-box;
  pointer-events: none; white-space: nowrap; padding: 4px 6px;
  border: 1px solid #f2d797a3; border-radius: 6px; background: #192c25f0;
  box-shadow: 0 3px 7px #0004; color: #fff1c9;
  font: 780 clamp(9px,1.6vw,11px)/1.1 system-ui; opacity: 0;
}
.replay-win-flight .replay-badge-reward-pop__row { font-size: inherit; }
.replay-win-flight.is-flying { animation: replay-prize-flight 1250ms var(--flight-delay) both; }
.replay-win-flight-source .replay-scattered-badge { animation: replay-prize-kick 280ms var(--flight-delay) ease-out both; }
@keyframes replay-prize-flight {
  0% { opacity: 0; transform: translate(var(--flight-x),var(--flight-y)) scale(.3); }
  12% { opacity: 1; }
  28%,76% { opacity: 1; transform: translate(0,0) scale(1); }
  100% { opacity: 0; transform: translate(0,0) scale(.96); }
}
@keyframes replay-prize-kick { 0%,100% { scale: 1; } 40% { scale: 1.13; } }
@media (max-width: 650px) { .replay-win-flight { padding: 3px 4px; } }
@media (prefers-reduced-motion: reduce) {
  .replay-win-flight { display: none; }
  .replay-win-flight-source .replay-scattered-badge { animation: none; }
}

.replay-badge-reward-pop { box-sizing: border-box; }
/* These packed badge targets use the layout's explicit size; shell button padding
   must not enlarge them into adjacent badges or clip the right edge on phones. */
.replay-badge-wrap[role="button"] { box-sizing: border-box; min-width: 0; min-height: 0; padding: 0; }
