import { fetchSessionUser, fetchSlotState, postSlotSpin } from "../auth/session.js";
import { showToast } from "../components/toast.js";
import { t } from "../config/site.js";

const SYM_EMOJI = {
  cherry: "🍒",
  lemon: "🍋",
  bell: "🔔",
  diamond: "💎",
  seven: "7️⃣",
  jackpot: "🎰",
};

const REEL_IDS = Object.keys(SYM_EMOJI);
const JP_TIERS = ["mini", "minor", "major", "grand"];

const JP_LABEL_KEYS = {
  mini: "slotJpMini",
  minor: "slotJpMinor",
  major: "slotJpMajor",
  grand: "slotJpGrand",
};

function slotSymLabel(id) {
  const k = String(id);
  const map = {
    cherry: "slotSymCherry",
    lemon: "slotSymLemon",
    bell: "slotSymBell",
    diamond: "slotSymDiamond",
    seven: "slotSymSeven",
    jackpot: "slotSymJackpot",
  };
  return t(map[k] || "slotSymCherry");
}

function jpLabel(tier) {
  return t(JP_LABEL_KEYS[tier] || "slotJpMini");
}

function fmtCoins(n) {
  const v = Math.max(0, Math.floor(Number(n) || 0));
  try {
    return new Intl.NumberFormat(undefined, { maximumFractionDigits: 0 }).format(v);
  } catch {
    return String(v);
  }
}

const SLOT_MUTE_KEY = "slot_sound_muted";

const SLOT_PAGE_STYLE = `
    .slot-page {
      --slot-highlight: #69b1ff;
      --slot-highlight-bright: #dbeafe;
      --slot-accent: #69b1ff;
      --slot-accent-dim: #1677ff;
      --slot-cabinet: #080c14;
      --slot-cabinet-mid: #121a28;
      --slot-bezel: #1e2a3d;
      max-width: 54rem;
      margin-left: auto;
      margin-right: auto;
    }
    @keyframes slotWinPulse {
      0%, 100% { box-shadow: inset 0 0 0 2px rgba(22,119,255,0.35), 0 0 0 0 rgba(22,119,255,0.25); }
      50% { box-shadow: inset 0 0 0 2px rgba(96,165,250,0.55), 0 0 32px 8px rgba(22,119,255,0.4); }
    }
    @keyframes slotMarqueeBlink {
      0%, 100% { opacity: 0.35; transform: scale(0.85); }
      50% { opacity: 1; transform: scale(1); }
    }
    @keyframes slotLedGlow {
      0%, 100% { text-shadow: 0 0 8px currentColor; opacity: 0.92; }
      50% { text-shadow: 0 0 16px currentColor, 0 0 28px currentColor; opacity: 1; }
    }
    @keyframes slotSpinBtnGlow {
      0%, 100% { box-shadow: 0 0 0 0 rgba(22,119,255,0.2), 0 12px 32px -8px rgba(22,119,255,0.45); }
      50% { box-shadow: 0 0 0 4px rgba(22,119,255,0.2), 0 16px 40px -6px rgba(22,119,255,0.55); }
    }
    @keyframes slotReelFaceSpin {
      0% { transform: translateY(-11px) scale(0.94) rotate(-2deg); filter: blur(1.2px); }
      25% { transform: translateY(9px) scale(1.06) rotate(1deg); filter: blur(0.4px); }
      50% { transform: translateY(-7px) scale(0.97) rotate(-1deg); filter: blur(1px); }
      75% { transform: translateY(10px) scale(1.04) rotate(1.5deg); filter: blur(0.35px); }
      100% { transform: translateY(-11px) scale(0.94) rotate(-2deg); filter: blur(1.2px); }
    }
    @keyframes slotReelLand {
      0% { transform: translateY(-14px) scale(1.18); filter: brightness(1.35) blur(0); }
      45% { transform: translateY(4px) scale(0.88); filter: brightness(1.05) blur(0); }
      72% { transform: translateY(-2px) scale(1.04); filter: brightness(1.12) blur(0); }
      100% { transform: translateY(0) scale(1); filter: brightness(1) blur(0); }
    }
    @keyframes slotWindowShine {
      0% { opacity: 0.35; transform: translateX(-100%); }
      100% { opacity: 0.15; transform: translateX(100%); }
    }
    .slot-toolbar {
      display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 0.75rem;
      margin-bottom: 1.25rem; font-size: 0.875rem;
    }
    .slot-toolbar a {
      color: var(--slot-accent); font-weight: 500;
      display: inline-flex; align-items: center; gap: 0.35rem;
    }
    .slot-toolbar a::before { content: "←"; opacity: 0.7; }
    .slot-toolbar a:hover { text-decoration: underline; color: var(--slot-highlight-bright); }
    .slot-toolbar-tools { display: flex; flex-wrap: wrap; align-items: center; gap: 0.65rem; }
    .slot-sound-toggle {
      font-size: 0.8125rem; padding: 0.4rem 0.75rem; border-radius: 999px;
      border: 1px solid rgba(22,119,255,0.28); background: rgba(0,0,0,0.35); color: #cbd5e1; cursor: pointer;
      transition: border-color 0.2s, color 0.2s, background 0.2s;
    }
    .slot-sound-toggle:hover { border-color: rgba(22,119,255,0.5); color: var(--slot-highlight-bright); background: rgba(22,119,255,0.1); }
    .slot-balance-pill {
      display: inline-flex; align-items: center; gap: 0.5rem; padding: 0.4rem 0.9rem 0.4rem 0.65rem;
      border-radius: 999px; font-size: 0.8125rem; color: #94a3b8;
      background: linear-gradient(135deg, rgba(30,42,61,0.9) 0%, rgba(8,12,20,0.95) 100%);
      border: 1px solid rgba(22,119,255,0.35);
      box-shadow: inset 0 1px 0 rgba(255,255,255,0.06), 0 4px 16px -6px rgba(0,0,0,0.5);
    }
    .slot-balance-pill__icon {
      width: 1.5rem; height: 1.5rem; border-radius: 50%;
      display: flex; align-items: center; justify-content: center; font-size: 0.75rem;
      background: linear-gradient(145deg, #b8d4f5 0%, var(--slot-accent-dim) 55%, #355a8a 100%);
      color: #0a0e14; font-weight: 800; box-shadow: 0 2px 6px rgba(0,0,0,0.35);
    }
    .slot-balance-pill strong { color: var(--slot-highlight-bright); font-variant-numeric: tabular-nums; font-weight: 800; font-size: 0.9375rem; }
    .slot-shell {
      position: relative; border-radius: 1.75rem; overflow: hidden;
      border: 2px solid rgba(22,119,255,0.35);
      background:
        radial-gradient(ellipse 120% 80% at 50% -20%, rgba(22,119,255,0.18), transparent 50%),
        linear-gradient(168deg, #1a2438 0%, var(--slot-cabinet-mid) 38%, var(--slot-cabinet) 100%);
      box-shadow:
        0 32px 64px -24px rgba(0,0,0,0.75),
        0 0 0 1px rgba(255,255,255,0.04) inset,
        inset 0 1px 0 rgba(255,255,255,0.08);
      padding: 0;
    }
    .slot-shell::before {
      content: ""; position: absolute; inset: 0; pointer-events: none; z-index: 0;
      background: repeating-linear-gradient(
        90deg,
        transparent 0,
        transparent 2px,
        rgba(255,255,255,0.012) 2px,
        rgba(255,255,255,0.012) 4px
      );
      opacity: 0.5;
    }
    .slot-shell__inner { position: relative; z-index: 1; padding: 1rem 1rem 1.35rem; }
    @media (min-width: 640px) { .slot-shell__inner { padding: 1.35rem 1.5rem 1.65rem; } }
    .slot-marquee {
      display: flex; align-items: center; justify-content: center; flex-wrap: wrap;
      gap: 0.45rem 0.65rem; padding: 0.85rem 1rem 0.8rem; margin: 0;
      min-height: 3rem; overflow: visible;
      background: linear-gradient(180deg, rgba(22,119,255,0.16) 0%, rgba(0,0,0,0.22) 100%);
      border-bottom: 1px solid rgba(22,119,255,0.28);
    }
    .slot-marquee__lights {
      display: flex; gap: 5px; flex-shrink: 0;
    }
    .slot-marquee__dot {
      width: 6px; height: 6px; border-radius: 50%;
      background: var(--slot-highlight);
      box-shadow: 0 0 6px var(--slot-highlight);
      animation: slotMarqueeBlink 1.2s ease-in-out infinite;
    }
    .slot-marquee__dot:nth-child(2) { animation-delay: 0.15s; }
    .slot-marquee__dot:nth-child(3) { animation-delay: 0.3s; }
    .slot-marquee__dot:nth-child(4) { animation-delay: 0.45s; }
    .slot-marquee__dot:nth-child(5) { animation-delay: 0.6s; }
    @media (max-width: 400px) {
      .slot-marquee__lights { display: none; }
      .slot-marquee__title { letter-spacing: 0.04em; font-size: 0.75rem; }
    }
    .slot-marquee__title {
      flex: 1 1 auto; min-width: 0; max-width: 100%;
      font-size: clamp(0.6875rem, 2.4vw, 0.8125rem); font-weight: 800; letter-spacing: 0.08em;
      line-height: 1.35; text-align: center;
      text-transform: uppercase; color: var(--slot-highlight-bright);
      text-shadow: 0 0 18px rgba(22,119,255,0.4);
      padding: 0.1rem 0.35rem;
      word-break: break-word;
    }
    .slot-machine-top {
      display: flex; align-items: center; justify-content: space-between; gap: 0.75rem;
      margin-bottom: 0.85rem;
    }
    .slot-badge {
      font-size: 9px; font-weight: 800; letter-spacing: 0.16em; text-transform: uppercase;
      color: var(--slot-accent); border: 1px solid rgba(22,119,255,0.4); border-radius: 999px;
      padding: 0.3rem 0.7rem; background: rgba(22,119,255,0.12);
    }
    .slot-limits-hint { font-size: 0.6875rem; color: #64748b; letter-spacing: 0.02em; }
    .slot-window-wrap {
      position: relative; padding: 0.65rem;
      border-radius: 1.35rem;
      background: linear-gradient(145deg, rgba(22,119,255,0.22) 0%, rgba(30,42,61,0.6) 40%, rgba(8,12,20,0.9) 100%);
      box-shadow:
        inset 0 2px 4px rgba(255,255,255,0.12),
        inset 0 -4px 12px rgba(0,0,0,0.4),
        0 8px 24px -8px rgba(0,0,0,0.5);
    }
    .slot-window {
      position: relative; border-radius: 1.1rem;
      background:
        radial-gradient(ellipse 100% 60% at 50% 0%, rgba(22,119,255,0.2), transparent 55%),
        linear-gradient(180deg, #0a1018 0%, #030508 100%);
      border: 2px solid rgba(0,0,0,0.6);
      padding: 1.15rem 0.85rem 1.35rem;
      overflow: hidden;
      box-shadow: inset 0 6px 28px rgba(0,0,0,0.65);
    }
    .slot-window::before {
      content: ""; position: absolute; inset: 0; pointer-events: none; z-index: 2;
      background: linear-gradient(180deg, rgba(255,255,255,0.07) 0%, transparent 32%, transparent 68%, rgba(0,0,0,0.45) 100%);
    }
    .slot-window__shine {
      position: absolute; top: 0; left: 0; right: 0; height: 45%; pointer-events: none; z-index: 3;
      background: linear-gradient(105deg, transparent 0%, rgba(255,255,255,0.07) 45%, transparent 70%);
      animation: slotWindowShine 2.8s ease-in-out infinite;
      opacity: 0.6;
    }
    .slot-reels {
      display: grid; grid-template-columns: repeat(3, 1fr); gap: 0.55rem; position: relative; z-index: 4;
    }
    @media (min-width: 640px) { .slot-reels { gap: 0.75rem; } }
    .slot-reel {
      position: relative; aspect-ratio: 1/1.08;
      border-radius: 0.9rem;
      background: linear-gradient(165deg, #2a3850 0%, #0d121c 45%, #1a2435 100%);
      border: 2px solid rgba(22,119,255,0.2);
      box-shadow:
        inset 0 3px 0 rgba(255,255,255,0.1),
        inset 0 -10px 28px rgba(0,0,0,0.55),
        0 6px 20px rgba(0,0,0,0.4);
      overflow: hidden;
    }
    .slot-reel::after {
      content: ""; position: absolute; inset: 0; border-radius: inherit; pointer-events: none; z-index: 2;
      background: linear-gradient(180deg, rgba(0,0,0,0.35) 0%, transparent 28%, transparent 72%, rgba(0,0,0,0.4) 100%);
    }
    .slot-reel-window {
      position: absolute; inset: 4px; border-radius: 0.65rem; z-index: 1;
      display: flex; align-items: center; justify-content: center;
      overflow: hidden;
      background:
        radial-gradient(ellipse 85% 65% at 50% 15%, rgba(22,119,255,0.12), transparent 50%),
        radial-gradient(ellipse 90% 70% at 50% 50%, #141c2a, #04060a);
      box-shadow: inset 0 6px 24px rgba(0,0,0,0.6);
    }
    .slot-reel-face {
      display: block; font-size: clamp(2.6rem, 11vw, 4rem); line-height: 1;
      filter: drop-shadow(0 4px 14px rgba(0,0,0,0.5));
      transform-origin: center center;
      will-change: transform, filter;
    }
    .slot-reel--spinning .slot-reel-face {
      animation: slotReelFaceSpin 0.11s linear infinite;
    }
    .slot-reel--land .slot-reel-face {
      animation: slotReelLand 0.52s cubic-bezier(0.28, 1.15, 0.42, 1) forwards;
    }
    .slot-reel--spinning {
      border-color: rgba(22,119,255,0.4);
      box-shadow:
        inset 0 2px 0 rgba(255,255,255,0.08),
        inset 0 -6px 20px rgba(22,119,255,0.12),
        0 0 20px -4px rgba(22,119,255,0.25);
    }
    .slot-reel--winner {
      animation: slotWinPulse 1.35s ease-in-out 2;
      border-color: rgba(22,119,255,0.55);
    }
    @media (prefers-reduced-motion: reduce) {
      .slot-reel--spinning .slot-reel-face { animation-duration: 0.2s; }
      .slot-reel--land .slot-reel-face { animation-duration: 0.2s; }
      .slot-window__shine { animation: none; opacity: 0.2; }
    }
    .slot-msg {
      margin-top: 1.1rem; text-align: center; font-size: 0.9375rem; color: #94a3b8; min-height: 1.6rem;
      padding: 0.35rem 0.5rem; border-radius: 0.5rem;
    }
    .slot-msg--win {
      color: var(--slot-highlight-bright); font-weight: 700;
      background: rgba(22,119,255,0.08);
      text-shadow: 0 0 20px rgba(22,119,255,0.25);
    }
    .slot-console {
      margin-top: 1.35rem; padding: 1rem 1rem 0.25rem;
      border-radius: 1.15rem;
      background: linear-gradient(180deg, rgba(0,0,0,0.35) 0%, rgba(0,0,0,0.15) 100%);
      border: 1px solid rgba(255,255,255,0.06);
      box-shadow: inset 0 1px 0 rgba(255,255,255,0.04);
    }
    .slot-controls { display: flex; flex-direction: column; gap: 1rem; }
    .slot-bet-row { display: flex; flex-wrap: wrap; align-items: stretch; gap: 0.65rem; }
    .slot-field { flex: 1; min-width: 7.5rem; }
    .slot-label {
      display: block; font-size: 10px; font-weight: 700; letter-spacing: 0.12em;
      text-transform: uppercase; color: #64748b; margin-bottom: 0.4rem;
    }
    .slot-input {
      width: 100%; border-radius: 0.75rem; border: 1px solid rgba(22,119,255,0.2);
      background: rgba(0,0,0,0.45); padding: 0.7rem 0.9rem; color: var(--slot-highlight-bright);
      font-size: 1.0625rem; font-weight: 700; font-variant-numeric: tabular-nums;
      outline: none; transition: border-color 0.2s, box-shadow 0.2s;
    }
    .slot-input:focus {
      border-color: rgba(22,119,255,0.5);
      box-shadow: 0 0 0 3px rgba(22,119,255,0.12), inset 0 0 20px rgba(22,119,255,0.05);
    }
    .slot-spin-btn {
      flex: 1; min-width: 10rem; min-height: 3rem;
      border-radius: 1rem; padding: 0.75rem 1.5rem; font-weight: 800; font-size: 1rem;
      letter-spacing: 0.06em; text-transform: uppercase;
      background: linear-gradient(180deg, #7eb8f0 0%, var(--slot-accent-dim) 45%, #355a8a 100%);
      color: #0a0e14; border: 2px solid rgba(22,119,255,0.45);
      box-shadow: 0 12px 32px -8px rgba(22,119,255,0.5);
      cursor: pointer; transition: transform 0.15s ease, filter 0.15s ease, box-shadow 0.2s;
      animation: slotSpinBtnGlow 2.5s ease-in-out infinite;
    }
    .slot-spin-btn:hover:not(:disabled) { filter: brightness(1.1); transform: translateY(-1px); }
    .slot-spin-btn:active:not(:disabled) { transform: scale(0.98) translateY(0); animation: none; }
    .slot-spin-btn:disabled { opacity: 0.4; cursor: not-allowed; animation: none; }
    .slot-action-group { display: flex; flex-direction: column; gap: 0.5rem; flex-shrink: 0; }
    .slot-chips { display: flex; flex-wrap: wrap; gap: 0.45rem; justify-content: center; }
    .slot-chip {
      min-width: 2.75rem; height: 2.75rem; border-radius: 50%; padding: 0;
      display: inline-flex; align-items: center; justify-content: center;
      font-size: 0.75rem; font-weight: 800; font-variant-numeric: tabular-nums;
      background: radial-gradient(circle at 35% 30%, #3d4f68 0%, #1a2435 55%, #0d121c 100%);
      border: 3px dashed rgba(22,119,255,0.35);
      color: var(--slot-highlight-bright);
      box-shadow: 0 4px 12px rgba(0,0,0,0.35), inset 0 1px 0 rgba(255,255,255,0.08);
      cursor: pointer; transition: transform 0.15s, border-color 0.2s, box-shadow 0.2s;
    }
    .slot-chip:hover:not(:disabled) {
      transform: translateY(-2px) scale(1.05);
      border-color: rgba(22,119,255,0.65);
      box-shadow: 0 6px 16px rgba(22,119,255,0.2), inset 0 0 12px rgba(22,119,255,0.08);
    }
    .slot-chip:disabled { opacity: 0.35; cursor: not-allowed; transform: none; }
    .slot-pay {
      margin-top: 1.35rem; padding: 1.15rem 1rem 0.5rem;
      border-radius: 1rem;
      background: rgba(0,0,0,0.2);
      border: 1px solid rgba(255,255,255,0.05);
    }
    .slot-pay h3 {
      font-size: 0.6875rem; font-weight: 800; letter-spacing: 0.18em; text-transform: uppercase;
      color: #64748b; margin-bottom: 0.85rem; text-align: center;
    }
    .slot-pay-grid { display: grid; gap: 0.45rem; grid-template-columns: repeat(2, 1fr); }
    @media (min-width: 520px) { .slot-pay-grid { grid-template-columns: repeat(4, 1fr); } }
    .slot-pay-item {
      display: grid; grid-template-columns: auto 1fr auto; align-items: center; gap: 0.4rem;
      padding: 0.55rem 0.6rem; border-radius: 0.65rem;
      background: rgba(0,0,0,0.3); border: 1px solid rgba(255,255,255,0.06);
      font-size: 0.75rem; color: #cbd5e1;
      transition: border-color 0.2s, background 0.2s;
    }
    .slot-pay-item:hover { border-color: rgba(22,119,255,0.2); background: rgba(22,119,255,0.04); }
    .slot-pay-item__sym { font-size: 1.125rem; line-height: 1; }
    .slot-pay-item__info { min-width: 0; }
    .slot-pay-item__mult {
      font-weight: 800; color: var(--slot-accent); font-variant-numeric: tabular-nums;
      font-size: 0.8125rem;
    }
    .slot-pay-footer { margin-top: 0.85rem; text-align: center; font-size: 0.6875rem; color: #64748b; line-height: 1.55; }
    .slot-jp-panel {
      margin-bottom: 1rem; padding: 0.75rem 0.65rem 0.65rem; border-radius: 1rem;
      background: linear-gradient(180deg, rgba(0,0,0,0.5) 0%, rgba(0,0,0,0.25) 100%);
      border: 1px solid rgba(22,119,255,0.15);
      box-shadow: inset 0 0 40px rgba(0,0,0,0.3);
    }
    .slot-jp-panel__title {
      font-size: 8px; font-weight: 800; letter-spacing: 0.24em; text-transform: uppercase;
      color: rgba(22,119,255,0.7); margin-bottom: 0.6rem; text-align: center;
    }
    .slot-jp-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 0.45rem; }
    @media (min-width: 520px) { .slot-jp-grid { grid-template-columns: repeat(4, 1fr); } }
    .slot-jp-cell {
      position: relative; border-radius: 0.65rem; padding: 0.45rem 0.4rem 0.5rem; text-align: center;
      border: 1px solid rgba(255,255,255,0.08);
      background: linear-gradient(180deg, #0a0e14 0%, #141c28 100%);
      box-shadow: inset 0 2px 8px rgba(0,0,0,0.5), 0 0 0 1px rgba(255,255,255,0.03);
      overflow: hidden;
    }
    .slot-jp-cell::before {
      content: ""; position: absolute; inset: 0; opacity: 0.4; pointer-events: none;
      background: repeating-linear-gradient(0deg, transparent, transparent 2px, rgba(255,255,255,0.03) 2px, rgba(255,255,255,0.03) 4px);
    }
    .slot-jp-cell__label {
      position: relative; font-size: 8px; font-weight: 800; letter-spacing: 0.12em;
      text-transform: uppercase; margin-bottom: 0.15rem;
    }
    .slot-jp-cell__val {
      position: relative; font-size: clamp(0.7rem, 2.6vw, 0.875rem); font-weight: 800;
      font-variant-numeric: tabular-nums; line-height: 1.2;
      animation: slotLedGlow 2s ease-in-out infinite;
    }
    .slot-jp-cell--mini { border-color: rgba(34,211,238,0.35); }
    .slot-jp-cell--mini .slot-jp-cell__label,
    .slot-jp-cell--mini .slot-jp-cell__val { color: #22d3ee; }
    .slot-jp-cell--minor { border-color: rgba(74,222,128,0.35); }
    .slot-jp-cell--minor .slot-jp-cell__label,
    .slot-jp-cell--minor .slot-jp-cell__val { color: #4ade80; }
    .slot-jp-cell--major { border-color: rgba(167,139,250,0.4); }
    .slot-jp-cell--major .slot-jp-cell__label,
    .slot-jp-cell--major .slot-jp-cell__val { color: #c4b5fd; }
    .slot-jp-cell--grand {
      border-color: rgba(147,197,253,0.5);
      background: linear-gradient(145deg, rgba(22,119,255,0.22) 0%, rgba(0,0,0,0.35) 100%);
      box-shadow: 0 0 24px -8px rgba(22,119,255,0.4), inset 0 1px 0 rgba(255,255,255,0.08);
    }
    .slot-jp-cell--grand .slot-jp-cell__label,
    .slot-jp-cell--grand .slot-jp-cell__val { color: #bfdbfe; }
    .slot-jp-cell--pulse { animation: slotJpPulse 0.9s ease-in-out 2; }
    @keyframes slotJpPulse {
      0%, 100% { transform: scale(1); filter: brightness(1); }
      50% { transform: scale(1.04); filter: brightness(1.35); }
    }
    .slot-meta-row {
      display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 0.5rem;
      margin-top: 0.75rem; font-size: 0.75rem; color: #64748b;
    }
    .slot-last-win strong { color: #69b1ff; font-variant-numeric: tabular-nums; }
    .slot-max-btn {
      border-radius: 0.75rem; padding: 0 0.85rem; min-height: 3rem; font-size: 0.75rem; font-weight: 800;
      letter-spacing: 0.08em; text-transform: uppercase;
      background: linear-gradient(180deg, rgba(22,119,255,0.2) 0%, rgba(22,119,255,0.06) 100%);
      border: 1px solid rgba(22,119,255,0.4); color: var(--slot-highlight-bright); cursor: pointer;
      transition: background 0.2s, transform 0.15s;
    }
    .slot-max-btn:hover:not(:disabled) { background: rgba(22,119,255,0.28); transform: translateY(-1px); }
    .slot-max-btn:disabled { opacity: 0.35; cursor: not-allowed; }
    .slot-reel--jackpot-win {
      animation: slotWinPulse 1s ease-in-out 3;
      border-color: rgba(96,165,250,0.7);
      box-shadow: 0 0 28px -4px rgba(22,119,255,0.5);
    }
    .slot-jp-overlay {
      position: fixed; inset: 0; z-index: 120; display: flex; align-items: center; justify-content: center;
      padding: 1.5rem; background: rgba(0,0,0,0.72); backdrop-filter: blur(6px);
      opacity: 0; pointer-events: none; transition: opacity 0.35s ease;
    }
    .slot-jp-overlay--show { opacity: 1; pointer-events: auto; }
    .slot-jp-overlay__card {
      text-align: center; padding: 2.25rem 2.5rem; border-radius: 1.75rem; max-width: 24rem; width: 100%;
      background: linear-gradient(168deg, #1e2a3d 0%, #0a0e14 100%);
      border: 3px solid rgba(22,119,255,0.55);
      box-shadow: 0 0 80px -10px rgba(22,119,255,0.5), 0 24px 48px rgba(0,0,0,0.5);
    }
    .slot-jp-overlay__tier {
      font-size: 0.8125rem; font-weight: 800; letter-spacing: 0.32em; text-transform: uppercase;
      color: var(--slot-highlight-bright); margin-bottom: 0.65rem;
      text-shadow: 0 0 24px rgba(22,119,255,0.5);
    }
    .slot-jp-overlay__amt {
      font-size: 2.35rem; font-weight: 800; color: #fff; font-variant-numeric: tabular-nums;
      text-shadow: 0 0 30px rgba(22,119,255,0.4);
    }
    .slot-jp-overlay__sub { margin-top: 0.85rem; font-size: 0.875rem; color: #94a3b8; }
    .slot-pay-note { font-size: 0.65rem; color: #64748b; margin-top: 0.1rem; line-height: 1.3; }
    @media (prefers-reduced-motion: reduce) {
      .slot-marquee__dot, .slot-spin-btn, .slot-jp-cell__val { animation: none; }
    }

    /* Light theme */
    [data-theme="light"] .slot-page {
      --slot-highlight: #1677ff;
      --slot-highlight-bright: #0958d8;
      --slot-cabinet: #f8fafc;
      --slot-cabinet-mid: #fff;
    }
    [data-theme="light"] .slot-toolbar a { color: #1677ff; }
    [data-theme="light"] .slot-toolbar a:hover { color: #0958d8; }
    [data-theme="light"] .slot-shell {
      border-color: rgba(22, 119, 255, 0.35);
      background: linear-gradient(168deg, #fff 0%, #f1f5f9 50%, #e8eef5 100%);
      box-shadow: 0 20px 50px -20px rgba(15, 23, 42, 0.15);
    }
    [data-theme="light"] .slot-marquee {
      background: linear-gradient(180deg, rgba(22, 119, 255, 0.12) 0%, rgba(241, 245, 249, 0.9) 100%);
      border-bottom-color: rgba(22, 119, 255, 0.22);
    }
    [data-theme="light"] .slot-marquee__title { color: #0958d8; text-shadow: none; }
    [data-theme="light"] .slot-balance-pill {
      background: #fff; border-color: rgba(22, 119, 255, 0.28); color: #64748b;
    }
    [data-theme="light"] .slot-balance-pill strong { color: #0958d8; }
    [data-theme="light"] .slot-badge {
      color: #0958d8;
      border-color: rgba(22, 119, 255, 0.35);
      background: rgba(22, 119, 255, 0.1);
    }
    [data-theme="light"] .slot-limits-hint { color: #64748b; }
    [data-theme="light"] .slot-window-wrap {
      background: linear-gradient(145deg, rgba(22, 119, 255, 0.12) 0%, #e2e8f0 100%);
      box-shadow: inset 0 2px 4px rgba(255,255,255,0.8), 0 8px 20px rgba(15,23,42,0.08);
    }
    [data-theme="light"] .slot-sound-toggle {
      color: #334155;
      background: #fff;
      border-color: rgba(22, 119, 255, 0.18);
    }
    [data-theme="light"] .slot-sound-toggle:hover {
      color: #0f172a;
      border-color: rgba(22, 119, 255, 0.32);
    }
    [data-theme="light"] .slot-window {
      border-color: rgba(22, 119, 255, 0.22);
      background: radial-gradient(120% 80% at 50% -10%, rgba(22, 119, 255, 0.1), transparent 45%),
        linear-gradient(180deg, #e2e8f0 0%, #cbd5e1 100%);
      box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.8), 0 12px 32px -16px rgba(15, 23, 42, 0.15);
    }
    [data-theme="light"] .slot-window::before {
      background: linear-gradient(180deg, rgba(255, 255, 255, 0.35) 0%, transparent 32%, transparent 68%, rgba(15, 23, 42, 0.08) 100%);
    }
    [data-theme="light"] .slot-reel {
      background: linear-gradient(145deg, #f8fafc 0%, #e2e8f0 50%, #f1f5f9 100%);
      border-color: rgba(22, 119, 255, 0.2);
      box-shadow: inset 0 2px 0 rgba(255, 255, 255, 0.9), inset 0 -6px 16px rgba(22, 119, 255, 0.08), 0 4px 12px rgba(15, 23, 42, 0.08);
    }
    [data-theme="light"] .slot-reel-window {
      background: radial-gradient(ellipse 90% 70% at 50% 20%, rgba(255, 255, 255, 0.9), rgba(226, 232, 240, 0.95));
      box-shadow: inset 0 4px 16px rgba(22, 119, 255, 0.12);
    }
    [data-theme="light"] .slot-msg { color: #334155; }
    [data-theme="light"] .slot-msg--win { color: #0958d8; background: rgba(22,119,255,0.1); }
    [data-theme="light"] .slot-console { background: #f1f5f9; border-color: rgba(22,119,255,0.12); }
    [data-theme="light"] .slot-label { color: #64748b; }
    [data-theme="light"] .slot-input {
      color: #0f172a;
      background: #fff;
      border-color: rgba(22,119,255,0.25);
    }
    [data-theme="light"] .slot-chip {
      color: #0958d8;
      background: radial-gradient(circle at 35% 30%, #fff 0%, #f1f5f9 100%);
      border-color: rgba(22,119,255,0.35);
    }
    [data-theme="light"] .slot-chip:hover:not(:disabled) {
      border-color: rgba(22,119,255,0.55);
      box-shadow: 0 4px 12px rgba(22,119,255,0.15);
    }
    [data-theme="light"] .slot-pay { background: #f8fafc; border-color: rgba(22,119,255,0.1); }
    [data-theme="light"] .slot-pay h3 { color: #64748b; }
    [data-theme="light"] .slot-pay-item {
      color: #334155;
      background: #fff;
      border-color: rgba(22, 119, 255, 0.1);
    }
    [data-theme="light"] .slot-pay-item__mult { color: #1677ff; }
    [data-theme="light"] .slot-pay-footer { color: #64748b; }
    [data-theme="light"] .slot-guest-panel { color: #334155; }
    [data-theme="light"] .slot-guest-panel .slot-guest-hint { color: #64748b; }
    [data-theme="light"] .slot-guest-panel .slot-guest-title { color: #0f172a; }
    [data-theme="light"] .slot-error-panel {
      color: #334155;
      background: linear-gradient(180deg, #fff 0%, #f8fafc 100%);
      border-color: rgba(22, 119, 255, 0.2);
    }
    [data-theme="light"] .slot-jp-panel {
      background: linear-gradient(180deg, #f1f5f9 0%, #e2e8f0 100%);
      border-color: rgba(22, 119, 255, 0.14);
    }
    [data-theme="light"] .slot-jp-cell { background: #fff; }
    [data-theme="light"] .slot-jp-cell__val { color: #0f172a; }
    [data-theme="light"] .slot-jp-cell--grand .slot-jp-cell__val { color: #0958d8; }
    [data-theme="light"] .slot-last-win strong { color: #1677ff; }
    [data-theme="light"] .slot-jp-overlay__card {
      background: linear-gradient(165deg, #fff 0%, #f8fafc 100%);
      border-color: rgba(22, 119, 255, 0.45);
    }
`;

function reelsAreWinning(reels) {
  if (!Array.isArray(reels) || reels.length !== 3) return false;
  if (reels.every((s) => s === "jackpot")) return true;
  return reels[0] === reels[1] && reels[1] === reels[2] && reels[0] !== "jackpot";
}

function jackpotsBarHtml(pools, pulseTier = null) {
  return `
    <div class="slot-jp-panel" aria-label="${t("slotJackpotsTitle")}">
      <p class="slot-jp-panel__title">◆ ${t("slotJackpotsTitle")} ◆</p>
      <div class="slot-jp-grid">
        ${JP_TIERS.map((tier) => {
          const val = Number(pools?.[tier]) || 0;
          const pulse = pulseTier === tier ? " slot-jp-cell--pulse" : "";
          return `<div class="slot-jp-cell slot-jp-cell--${tier}${pulse}" data-jp-tier="${tier}">
            <div class="slot-jp-cell__label">${jpLabel(tier)}</div>
            <div class="slot-jp-cell__val" data-jp-val="${tier}">${fmtCoins(val)}</div>
          </div>`;
        }).join("")}
      </div>
    </div>`;
}

function updateJackpotDisplays(root, pools, pulseTier = null) {
  if (!root || !pools) return;
  JP_TIERS.forEach((tier) => {
    const el = root.querySelector(`[data-jp-val="${tier}"]`);
    if (el) el.textContent = fmtCoins(pools[tier]);
    const cell = root.querySelector(`[data-jp-tier="${tier}"]`);
    if (cell) {
      cell.classList.toggle("slot-jp-cell--pulse", pulseTier === tier);
    }
  });
}

function ensureSlotStyles() {
  if (typeof document === "undefined") return;
  const id = "slot-page-styles";
  let el = document.getElementById(id);
  if (!el) {
    el = document.createElement("style");
    el.id = id;
    document.head.appendChild(el);
  }
  if (el.textContent !== SLOT_PAGE_STYLE) el.textContent = SLOT_PAGE_STYLE;
}

function symEmoji(id) {
  const key = String(id) === "wild" ? "cherry" : String(id);
  return SYM_EMOJI[key] ?? "❔";
}

function errMsg(data) {
  const e = data?.error;
  if (e === "coins") return t("bjErrCoins");
  if (e === "bet_range")
    return t("slotErrBetRange")
      .replace("{min}", String(data?.min ?? 1))
      .replace("{max}", String(data?.max ?? 1000));
  return data?.message || t("slotErrGeneric");
}

function randomSymbolId() {
  return REEL_IDS[Math.floor(Math.random() * REEL_IDS.length)];
}

let audioCtx = null;

function isSoundMuted() {
  try {
    return globalThis.localStorage?.getItem(SLOT_MUTE_KEY) === "1";
  } catch {
    return false;
  }
}

function setSoundMuted(muted) {
  try {
    globalThis.localStorage?.setItem(SLOT_MUTE_KEY, muted ? "1" : "0");
  } catch {

  }
}

function getAudioCtx() {
  if (typeof globalThis.AudioContext === "undefined" && typeof globalThis.webkitAudioContext === "undefined") {
    return null;
  }
  if (!audioCtx) {
    const Ctx = globalThis.AudioContext || globalThis.webkitAudioContext;
    audioCtx = new Ctx();
  }
  return audioCtx;
}

async function unlockAudio() {
  const ctx = getAudioCtx();
  if (!ctx) return;
  try {
    if (ctx.state === "suspended") await ctx.resume();
  } catch {

  }
}

function playTone(freq, dur, vol = 0.06, type = "sine", when = 0) {
  const ctx = getAudioCtx();
  if (!ctx || isSoundMuted()) return;
  const t0 = ctx.currentTime + when;
  const osc = ctx.createOscillator();
  const g = ctx.createGain();
  osc.type = type;
  osc.frequency.setValueAtTime(freq, t0);
  g.gain.setValueAtTime(0, t0);
  g.gain.linearRampToValueAtTime(vol, t0 + 0.01);
  g.gain.exponentialRampToValueAtTime(0.0001, t0 + dur);
  osc.connect(g);
  g.connect(ctx.destination);
  osc.start(t0);
  osc.stop(t0 + dur + 0.02);
}

function playNoiseBurst(dur, vol = 0.04, when = 0) {
  const ctx = getAudioCtx();
  if (!ctx || isSoundMuted()) return;
  const t0 = ctx.currentTime + when;
  const len = Math.max(1, Math.floor(ctx.sampleRate * dur));
  const buf = ctx.createBuffer(1, len, ctx.sampleRate);
  const data = buf.getChannelData(0);
  for (let i = 0; i < len; i++) data[i] = (Math.random() * 2 - 1) * (1 - i / len);
  const src = ctx.createBufferSource();
  src.buffer = buf;
  const bp = ctx.createBiquadFilter();
  bp.type = "bandpass";
  bp.frequency.setValueAtTime(2400, t0);
  bp.Q.setValueAtTime(0.6, t0);
  const g = ctx.createGain();
  g.gain.setValueAtTime(0, t0);
  g.gain.linearRampToValueAtTime(vol, t0 + 0.004);
  g.gain.exponentialRampToValueAtTime(0.0001, t0 + dur);
  src.connect(bp);
  bp.connect(g);
  g.connect(ctx.destination);
  src.start(t0);
  src.stop(t0 + dur);
}

function sfxPull() {
  playNoiseBurst(0.05, 0.06, 0);
  playTone(340, 0.08, 0.08, "triangle", 0.02);
  playTone(180, 0.1, 0.06, "sine", 0.06);
}

function sfxTick(phase = 0) {
  const f = 900 + (phase % 5) * 40;
  playNoiseBurst(0.018, 0.028, 0);
  playTone(f, 0.022, 0.045, "triangle", 0.003);
}

function sfxReelLock(reelIndex) {
  const w = reelIndex * 0.012;
  playNoiseBurst(0.032, 0.05, w);
  playTone(1650 - reelIndex * 120, 0.035, 0.055, "sine", w + 0.002);
  playTone(420, 0.05, 0.04, "triangle", w + 0.025);
}

function sfxWinSmall(when = 0) {
  playTone(523, 0.1, 0.065, "sine", when);
  playTone(659, 0.12, 0.055, "sine", when + 0.08);
  playTone(784, 0.14, 0.05, "sine", when + 0.16);
}

function sfxWinBig(when = 0) {
  playTone(392, 0.1, 0.06, "sine", when);
  playTone(523, 0.12, 0.065, "sine", when + 0.08);
  playTone(659, 0.14, 0.06, "sine", when + 0.18);
  playTone(784, 0.18, 0.065, "sine", when + 0.3);
  playTone(1046, 0.22, 0.055, "sine", when + 0.45);
  playNoiseBurst(0.06, 0.04, when + 0.55);
}

function sfxLose(when = 0) {
  playTone(200, 0.14, 0.065, "sawtooth", when);
  playTone(165, 0.18, 0.05, "sawtooth", when + 0.1);
}

function sfxJackpot(tier = "mini", when = 0) {
  const base = tier === "grand" ? 330 : tier === "major" ? 392 : tier === "minor" ? 440 : 494;
  playTone(base, 0.12, 0.07, "sine", when);
  playTone(base * 1.25, 0.14, 0.065, "sine", when + 0.1);
  playTone(base * 1.5, 0.16, 0.06, "sine", when + 0.22);
  playTone(base * 2, 0.2, 0.055, "triangle", when + 0.38);
  playNoiseBurst(0.08, 0.05, when + 0.5);
}

function showJackpotOverlay(tier, amount) {
  const existing = document.getElementById("slot-jp-overlay");
  if (existing) existing.remove();
  const wrap = document.createElement("div");
  wrap.id = "slot-jp-overlay";
  wrap.className = "slot-jp-overlay";
  wrap.innerHTML = `
    <div class="slot-jp-overlay__card" role="dialog" aria-modal="true">
      <p class="slot-jp-overlay__tier">${jpLabel(tier).toUpperCase()} JACKPOT</p>
      <p class="slot-jp-overlay__amt">+${fmtCoins(amount)}</p>
      <p class="slot-jp-overlay__sub">${t("marketCoinWord")}</p>
    </div>`;
  document.body.appendChild(wrap);
  requestAnimationFrame(() => wrap.classList.add("slot-jp-overlay--show"));
  const close = () => {
    wrap.classList.remove("slot-jp-overlay--show");
    setTimeout(() => wrap.remove(), 400);
  };
  wrap.addEventListener("click", close, { once: true });
  setTimeout(close, 4200);
}

function runSlotSpinAnimation(outlet, finalReels, isActive) {
  const reels = [...outlet.querySelectorAll(".slot-reel")];
  const faces = reels.map((r) => r.querySelector(".slot-reel-face")).filter(Boolean);
  if (reels.length !== 3 || faces.length !== 3) return Promise.resolve();

  const stopped = [false, false, false];
  let tickN = 0;
  const intervalMs = 38;
  const stopTimes = [920, 1680, 2480];

  let iv = null;

  const timeoutIds = [];

  function cleanupTimers() {
    if (iv != null) {
      clearInterval(iv);
      iv = null;
    }
    timeoutIds.forEach((tid) => clearTimeout(tid));
    timeoutIds.length = 0;
  }

  function cellsStillMounted() {
    return reels.every((c) => c.isConnected && outlet.contains(c));
  }

  return new Promise((resolve) => {
    iv = setInterval(() => {
      if (!isActive() || !cellsStillMounted()) {
        cleanupTimers();
        resolve();
        return;
      }
      tickN += 1;
      for (let i = 0; i < 3; i++) {
        if (!stopped[i]) faces[i].textContent = symEmoji(randomSymbolId());
      }
      if (!isSoundMuted() && tickN % 2 === 0) sfxTick(tickN);
    }, intervalMs);

    for (let i = 0; i < 3; i++) {
      const tid = setTimeout(() => {
        if (!isActive() || !cellsStillMounted()) {
          cleanupTimers();
          resolve();
          return;
        }
        stopped[i] = true;
        const id = finalReels[i] || "cherry";
        faces[i].textContent = symEmoji(id);
        reels[i].classList.remove("slot-reel--spinning");
        reels[i].classList.add("slot-reel--land");
        void reels[i].offsetWidth;
        sfxReelLock(i);
        if (i === 2) {
          cleanupTimers();
          resolve();
        }
      }, stopTimes[i]);
      timeoutIds.push(tid);
    }
  });
}

function paytableRowsHtml(paytable) {
  const order = ["seven", "diamond", "bell", "lemon", "cherry", "jackpot", "pair"];
  return order
    .map((key) => {
      const row = paytable[key];
      if (!row) return "";
      const mult = Number(row.mult) || 0;
      const label = String(row.label || "").trim() || slotSymLabel(key);
      const emoji = key === "pair" ? "◆◆" : symEmoji(key);
      const multTxt =
        key === "jackpot"
          ? "JP"
          : key === "pair"
            ? "½×"
            : mult > 0
              ? `${mult}×`
              : "—";
      const note = row.note ? `<div class="slot-pay-note">${row.note}</div>` : "";
      return `<div class="slot-pay-item">
        <span class="slot-pay-item__sym" aria-hidden="true">${emoji}</span>
        <span class="slot-pay-item__info">${label}${note}</span>
        <span class="slot-pay-item__mult">${multTxt}</span>
      </div>`;
    })
    .join("");
}

export async function renderSlotPage(outlet) {
  ensureSlotStyles();
  const user = await fetchSessionUser();
  if (!user) {
    outlet.innerHTML = `
      <section class="slot-guest-panel slot-page mx-auto max-w-2xl space-y-6 px-1">
        <a href="#/oyunlar" class="text-sm text-accent hover:underline">${t("bjBackToGames")}</a>
        <header class="space-y-2">
          <h1 class="slot-guest-title text-2xl font-bold sm:text-3xl">${t("slotGuestTitle")}</h1>
          <p class="slot-guest-hint text-sm">${t("slotGuestHint")}</p>
        </header>
        <button type="button" id="slot-open-login" class="k-btn k-btn--primary btn-site-primary rounded-xl px-5 py-2.5 text-sm font-semibold">${t("bjOpenLogin")}</button>
      </section>`;
    outlet.querySelector("#slot-open-login")?.addEventListener("click", () => globalThis.__siteOpenLogin?.());
    return;
  }

  const res = await fetchSlotState();
  if (!res.ok) {
    outlet.innerHTML = `
      <div class="slot-error-panel mx-auto max-w-lg rounded-2xl border border-sky-500/25 bg-sky-950/20 px-6 py-8 text-center text-sm text-sky-100">
        ${t("slotLoadErrorBeforeLink")} <a href="#/oyunlar" class="font-semibold text-accent underline">${t("gamesNavigateBack")}</a> ${t("slotLoadErrorAfterLink")}
      </div>`;
    return;
  }

  const d = res.data || {};
  const limits = { min: Number(d.minBet) || 1, max: Number(d.maxBet) || 1000 };

  const paytable = typeof d.paytable === "object" && d.paytable !== null ? d.paytable : {};
  let coins = typeof d.coins === "number" ? d.coins : user.coins ?? 0;
  let jackpots =
    typeof d.jackpots === "object" && d.jackpots !== null
      ? { ...d.jackpots }
      : { mini: 120, minor: 650, major: 3200, grand: 18000 };

  let displayReels = ["cherry", "lemon", "bell"];
  let animGeneration = 0;
  let lastWinText = "—";

  function paint() {
    const payHtml = paytableRowsHtml(paytable);
    const betDefault = Math.min(25, Math.max(limits.min, Math.min(limits.max, coins)));
    const maxBetVal = Math.max(limits.min, Math.min(limits.max, coins));
    const muted = isSoundMuted();
    const jpHtml = jackpotsBarHtml(jackpots);

    outlet.innerHTML = `
      <div class="slot-page px-1 pb-6">
        <div class="slot-toolbar">
          <a href="#/oyunlar">${t("bjBackToGames")}</a>
          <div class="slot-toolbar-tools">
            <button type="button" id="slot-toggle-sound" class="slot-sound-toggle" title="${t("bjSoundToggle")}">
              ${muted ? t("bjSoundOff") : t("bjSoundOn")}
            </button>
            <div class="slot-balance-pill">
              <span class="slot-balance-pill__icon" aria-hidden="true">₵</span>
              <span>${t("bjBalance")} <strong id="slot-coins-disp">${fmtCoins(coins)}</strong></span>
            </div>
          </div>
        </div>

        <div class="slot-shell">
          <div class="slot-marquee">
            <div class="slot-marquee__lights">${[1, 2, 3, 4, 5].map(() => `<span class="slot-marquee__dot"></span>`).join("")}</div>
            <span class="slot-marquee__title">${t("slotGuestTitle")}</span>
            <div class="slot-marquee__lights">${[1, 2, 3, 4, 5].map(() => `<span class="slot-marquee__dot"></span>`).join("")}</div>
          </div>
          <div class="slot-shell__inner">
          <div class="slot-machine-top">
            <span class="slot-badge">${t("slotBadge3")}</span>
            <span class="slot-limits-hint">${t("slotLimitsHint").replace("{min}", String(limits.min)).replace("{max}", String(limits.max))}</span>
          </div>

          <div id="slot-jp-mount">${jpHtml}</div>

          <div class="slot-window-wrap">
          <div class="slot-window">
            <div class="slot-window__shine" aria-hidden="true"></div>
            <div class="slot-reels" aria-live="polite">
              ${[0, 1, 2]
                .map((i) => {
                  const sym = displayReels[i] || "cherry";
                  const win = reelsAreWinning(displayReels);
                  return `<div class="slot-reel ${win ? "slot-reel--winner" : ""}" data-reel="${i}" aria-label="${slotSymLabel(sym)}">
                    <div class="slot-reel-window">
                      <span class="slot-reel-face">${symEmoji(sym)}</span>
                    </div>
                  </div>`;
                })
                .join("")}
            </div>
            <p class="slot-msg" id="slot-msg"></p>
          </div>
          </div>

          <div class="slot-console">
          <div class="slot-controls">
            <div class="slot-bet-row">
              <div class="slot-field">
                <label for="slot-bet" class="slot-label">${t("slotBetLabel")}</label>
                <input type="number" id="slot-bet" class="slot-input" min="${limits.min}" max="${limits.max}" step="1" value="${betDefault}" />
              </div>
              <div class="slot-action-group">
                <button type="button" class="slot-max-btn" id="slot-max-bet" title="${t("slotMaxBet")}">${t("slotMaxBet")}</button>
                <button type="button" class="slot-spin-btn" id="slot-spin">${t("slotSpin")}</button>
              </div>
            </div>
            <div class="slot-chips">
              ${[10, 25, 50, 100, 250, 500]
                .filter((x) => x <= limits.max)
                .map((x) => `<button type="button" class="slot-chip" data-bet="${x}">${x}</button>`)
                .join("")}
            </div>
            <div class="slot-meta-row">
              <span class="slot-last-win">${t("slotLastWin")}: <strong id="slot-last-win">${lastWinText}</strong></span>
            </div>
          </div>
          </div>

          <div class="slot-pay">
            <h3>${t("slotPayTitle")}</h3>
            <div class="slot-pay-grid">${payHtml}</div>
            <p class="slot-pay-footer">${t("slotPayFooter")}</p>
          </div>
          </div>
        </div>
      </div>`;

    outlet.querySelector("#slot-toggle-sound")?.addEventListener("click", () => {
      setSoundMuted(!isSoundMuted());
      paint();
    });

    outlet.querySelectorAll(".slot-chip").forEach((btn) => {
      btn.addEventListener("click", () => {
        const v = Number(btn.getAttribute("data-bet"));
        const el = outlet.querySelector("#slot-bet");
        if (el) el.value = String(Math.min(v, coins, limits.max));
      });
    });

    outlet.querySelector("#slot-max-bet")?.addEventListener("click", () => {
      const el = outlet.querySelector("#slot-bet");
      if (el) el.value = String(maxBetVal);
    });

    outlet.querySelector("#slot-spin")?.addEventListener("click", async () => {
      const inp = outlet.querySelector("#slot-bet");
      const raw = inp?.value ?? "10";
      const bet = Math.max(limits.min, Math.min(limits.max, parseInt(String(raw), 10) || 0));
      if (bet < limits.min || bet > limits.max) {
        showToast(t("slotToastBet").replace("{min}", String(limits.min)).replace("{max}", String(limits.max)), "error");
        return;
      }
      if (bet > coins) {
        showToast(t("bjErrCoins"), "error");
        return;
      }

      const gen = ++animGeneration;
      const spinBtn = outlet.querySelector("#slot-spin");
      const betInput = outlet.querySelector("#slot-bet");
      const chips = outlet.querySelectorAll(".slot-chip");
      const msgEl = outlet.querySelector("#slot-msg");
      const soundBtn = outlet.querySelector("#slot-toggle-sound");

      spinBtn?.setAttribute("disabled", "disabled");
      betInput?.setAttribute("disabled", "disabled");
      chips.forEach((c) => c.setAttribute("disabled", "disabled"));
      soundBtn?.setAttribute("disabled", "disabled");
      if (msgEl) {
        msgEl.textContent = t("slotSpinning");
        msgEl.classList.remove("slot-msg--win");
      }

      const out = await postSlotSpin({ action: "spin", bet });
      if (!out.ok || gen !== animGeneration) {
        spinBtn?.removeAttribute("disabled");
        betInput?.removeAttribute("disabled");
        chips.forEach((c) => c.removeAttribute("disabled"));
        soundBtn?.removeAttribute("disabled");
        if (msgEl) msgEl.textContent = "";
        showToast(errMsg(out.data || {}), "error");
        return;
      }

      const data = out.data || {};
      const finalReels = Array.isArray(data.reels) ? data.reels.map(String) : ["cherry", "cherry", "cherry"];
      if (typeof data.coins === "number") coins = data.coins;

      const reelEls = outlet.querySelectorAll(".slot-reel");
      reelEls.forEach((reel) => {
        reel.classList.add("slot-reel--spinning");
        reel.classList.remove("slot-reel--winner", "slot-reel--land");
      });

      await unlockAudio();
      sfxPull();

      await runSlotSpinAnimation(outlet, finalReels, () => gen === animGeneration);

      if (gen !== animGeneration) return;

      displayReels = finalReels;
      const mult = Number(data.multiplier) || 0;
      const payout = Number(data.payout) || 0;
      const net = payout - bet;
      const jpWin = data.jackpot && typeof data.jackpot === "object" ? data.jackpot : null;
      if (data.jackpots && typeof data.jackpots === "object") {
        jackpots = { ...data.jackpots };
      }

      const reelsNow = outlet.querySelectorAll(".slot-reel");
      const coinDisp = outlet.querySelector("#slot-coins-disp");
      if (coinDisp) coinDisp.textContent = fmtCoins(coins);

      const jpMount = outlet.querySelector("#slot-jp-mount");
      if (jpMount) {
        jpMount.innerHTML = jackpotsBarHtml(jackpots, jpWin?.tier || null);
      } else {
        updateJackpotDisplays(outlet, jackpots, jpWin?.tier || null);
      }

      reelsNow.forEach((reel) => {
        reel.classList.remove("slot-reel--spinning", "slot-reel--land", "slot-reel--jackpot-win");
      });
      const lineWin = mult > 0 || reelsAreWinning(finalReels);
      if (lineWin) {
        reelsNow.forEach((reel) => reel.classList.add("slot-reel--winner"));
      }
      if (jpWin) {
        reelsNow.forEach((reel) => reel.classList.add("slot-reel--jackpot-win"));
      }

      const wonAnything = payout > 0;
      if (msgEl) {
        if (wonAnything) {
          const msg = data.message || t("slotWinExclaim");
          const netStr = net >= 0 ? `+${fmtCoins(net)}` : fmtCoins(net);
          msgEl.textContent = t("slotWinMsg")
            .replace("{msg}", msg)
            .replace("{payout}", fmtCoins(payout))
            .replace("{net}", netStr);
          msgEl.classList.add("slot-msg--win");
        } else {
          msgEl.textContent = data.message || t("slotTryAgain");
          msgEl.classList.remove("slot-msg--win");
        }
      }

      const lastEl = outlet.querySelector("#slot-last-win");
      if (wonAnything) {
        lastWinText = `+${fmtCoins(payout)}`;
        if (lastEl) lastEl.textContent = lastWinText;
      }

      if (!isSoundMuted()) {
        if (jpWin) sfxJackpot(jpWin.tier || "mini", 0);
        else if (mult >= 25) sfxWinBig(0);
        else if (mult > 0 || payout > 0) sfxWinSmall(0);
        else sfxLose(0);
      }

      if (jpWin) {
        showJackpotOverlay(jpWin.tier || "mini", Number(jpWin.amount) || 0);
        showToast(
          `${jpLabel(jpWin.tier)}: +${fmtCoins(jpWin.amount)} ${t("marketCoinWord")}`,
          "success"
        );
      } else if (payout > 0) {
        showToast(t("slotToastWin").replace("{payout}", fmtCoins(payout)), "success");
      }

      spinBtn?.removeAttribute("disabled");
      betInput?.removeAttribute("disabled");
      chips.forEach((c) => c.removeAttribute("disabled"));
      soundBtn?.removeAttribute("disabled");

      void globalThis.__siteRefreshUser?.();
    });
  }

  paint();
}
