/**
 * CoinRush Jackpot - Clean Modern Version
 * Full featured with navbar, WebSocket, footer, wheel
 * v2200 - Added UX improvements: sounds, confetti, toasts
 */

const { useState, useEffect, useRef, useCallback, useMemo, createContext, useContext } = React;

// ============================================
// CONFIG
// ============================================
const CONFIG = {
  minBet: 0.10,
  maxBet: 100000,
  spinDuration: 8000,    // 8 seconds spin
  revealDuration: 8000,  // 8 seconds reveal hold
  wsReconnectDelay: 3000,
  enableSounds: true,
  enableConfetti: true
};

// ============================================
// SOUND EFFECTS MANAGER
// ============================================
const SoundManager = {
  sounds: {},
  enabled: true,
  
  init() {
    // Pre-load sounds (using existing sound files or create simple beeps)
    this.sounds.tick = this.createTone(800, 0.05);
    this.sounds.spin = this.createTone(400, 0.1);
    this.sounds.win = this.createTone(600, 0.3);
  },
  
  createTone(freq, duration) {
    return { freq, duration };
  },
  
  play(soundName) {
    if (!this.enabled || !CONFIG.enableSounds) return;
    
    try {
      const AudioContext = window.AudioContext || window.webkitAudioContext;
      if (!AudioContext) return;
      
      const ctx = new AudioContext();
      const oscillator = ctx.createOscillator();
      const gainNode = ctx.createGain();
      
      oscillator.connect(gainNode);
      gainNode.connect(ctx.destination);
      
      const sound = this.sounds[soundName];
      if (!sound) return;
      
      oscillator.frequency.value = sound.freq;
      oscillator.type = 'sine';
      
      gainNode.gain.setValueAtTime(0.1, ctx.currentTime);
      gainNode.gain.exponentialRampToValueAtTime(0.01, ctx.currentTime + sound.duration);
      
      oscillator.start(ctx.currentTime);
      oscillator.stop(ctx.currentTime + sound.duration);
    } catch (e) {
      // Silently fail if audio not supported
    }
  },
  
  toggle() {
    this.enabled = !this.enabled;
    return this.enabled;
  }
};

SoundManager.init();

// ============================================
// CONFETTI COMPONENT
// ============================================
function Confetti({ active, duration = 5000 }) {
  const canvasRef = useRef(null);
  const animationRef = useRef(null);
  
  useEffect(() => {
    if (!active || !CONFIG.enableConfetti) return;
    
    const canvas = canvasRef.current;
    if (!canvas) return;
    
    const ctx = canvas.getContext('2d');
    canvas.width = window.innerWidth;
    canvas.height = window.innerHeight;
    
    const particles = [];
    const colors = ['#22c55e', '#ffd700', '#ff4081', '#00e5ff', '#aa00ff', '#ff6d00'];
    
    // Create particles
    for (let i = 0; i < 150; i++) {
      particles.push({
        x: Math.random() * canvas.width,
        y: Math.random() * canvas.height - canvas.height,
        size: Math.random() * 10 + 5,
        color: colors[Math.floor(Math.random() * colors.length)],
        speedY: Math.random() * 3 + 2,
        speedX: (Math.random() - 0.5) * 4,
        rotation: Math.random() * 360,
        rotationSpeed: (Math.random() - 0.5) * 10
      });
    }
    
    const startTime = Date.now();
    
    const animate = () => {
      const elapsed = Date.now() - startTime;
      if (elapsed > duration) {
        ctx.clearRect(0, 0, canvas.width, canvas.height);
        return;
      }
      
      ctx.clearRect(0, 0, canvas.width, canvas.height);
      
      const opacity = elapsed > duration - 1000 ? (duration - elapsed) / 1000 : 1;
      
      particles.forEach(p => {
        p.y += p.speedY;
        p.x += p.speedX;
        p.rotation += p.rotationSpeed;
        
        ctx.save();
        ctx.translate(p.x, p.y);
        ctx.rotate((p.rotation * Math.PI) / 180);
        ctx.globalAlpha = opacity;
        ctx.fillStyle = p.color;
        ctx.fillRect(-p.size / 2, -p.size / 2, p.size, p.size / 3);
        ctx.restore();
      });
      
      animationRef.current = requestAnimationFrame(animate);
    };
    
    animate();
    
    return () => {
      if (animationRef.current) {
        cancelAnimationFrame(animationRef.current);
      }
    };
  }, [active, duration]);
  
  if (!active) return null;
  
  return (
    <canvas
      ref={canvasRef}
      style={{
        position: 'fixed',
        top: 0,
        left: 0,
        width: '100%',
        height: '100%',
        pointerEvents: 'none',
        zIndex: 9999
      }}
    />
  );
}

// ============================================
// TOAST NOTIFICATION SYSTEM
// ============================================
const ToastContext = createContext(null);

function ToastProvider({ children }) {
  const [toasts, setToasts] = useState([]);
  
  const addToast = useCallback((message, type = 'info', duration = 4000) => {
    const id = Date.now() + Math.random();
    setToasts(prev => [...prev, { id, message, type }]);
    
    setTimeout(() => {
      setToasts(prev => prev.filter(t => t.id !== id));
    }, duration);
  }, []);
  
  return (
    <ToastContext.Provider value={addToast}>
      {children}
      <div className="toast-container">
        {toasts.map(toast => (
          <div key={toast.id} className={`toast toast-${toast.type}`}>
            <span className="toast-icon">
              {toast.type === 'success' && Icons.check}
              {toast.type === 'error' && Icons.close}
              {toast.type === 'info' && Icons.info}
              {toast.type === 'win' && Icons.crown}
            </span>
            <span className="toast-message">{toast.message}</span>
          </div>
        ))}
      </div>
    </ToastContext.Provider>
  );
}

const useToast = () => useContext(ToastContext);

// ============================================
// WINNER CELEBRATION OVERLAY - Subtle & Premium
// Only shows for the actual winner
// ============================================
function WinnerOverlay({ show, winner, amount, isCurrentUser, onClose }) {
  const [visible, setVisible] = useState(false);
  
  useEffect(() => {
    if (show && isCurrentUser) {
      SoundManager.play('win');
      // Slight delay for smooth entrance
      setTimeout(() => setVisible(true), 100);
    } else {
      setVisible(false);
    }
  }, [show, isCurrentUser]);
  
  // Only render for the actual winner
  if (!show || !isCurrentUser) return null;
  
  return (
    <div className={`winner-modal-backdrop ${visible ? 'visible' : ''}`} onClick={onClose}>
      <div className={`winner-modal ${visible ? 'visible' : ''}`} onClick={e => e.stopPropagation()}>
        {/* Close button */}
        <button className="winner-modal-close" onClick={onClose}>
          {Icons.close}
        </button>
        
        {/* Trophy icon */}
        <div className="winner-modal-trophy">
          {Icons.trophy}
        </div>
        
        {/* Victory text */}
        <h2 className="winner-modal-title">You Won!</h2>
        
        {/* Amount */}
        <div className="winner-modal-amount">
          <span className="amount-value">+{fmt(amount)}</span>
          <span className="amount-currency">coins</span>
        </div>
        
        {/* Collect button */}
        <button className="winner-modal-btn" onClick={onClose}>
          Collect Winnings
        </button>
      </div>
    </div>
  );
}

// Vibrant wheel colors
const COLORS = [
  { bg: 'linear-gradient(135deg, #FF3D3D 0%, #FF6B6B 100%)', solid: '#FF3D3D', glow: 'rgba(255, 61, 61, 0.6)' },
  { bg: 'linear-gradient(135deg, #00E5FF 0%, #00B8D4 100%)', solid: '#00E5FF', glow: 'rgba(0, 229, 255, 0.6)' },
  { bg: 'linear-gradient(135deg, #FFD600 0%, #FFAB00 100%)', solid: '#FFD600', glow: 'rgba(255, 214, 0, 0.6)' },
  { bg: 'linear-gradient(135deg, #AA00FF 0%, #D500F9 100%)', solid: '#AA00FF', glow: 'rgba(170, 0, 255, 0.6)' },
  { bg: 'linear-gradient(135deg, #00E676 0%, #1DE9B6 100%)', solid: '#00E676', glow: 'rgba(0, 230, 118, 0.6)' },
  { bg: 'linear-gradient(135deg, #FF6D00 0%, #FF9100 100%)', solid: '#FF6D00', glow: 'rgba(255, 109, 0, 0.6)' },
  { bg: 'linear-gradient(135deg, #2979FF 0%, #448AFF 100%)', solid: '#2979FF', glow: 'rgba(41, 121, 255, 0.6)' },
  { bg: 'linear-gradient(135deg, #FF4081 0%, #F50057 100%)', solid: '#FF4081', glow: 'rgba(255, 64, 129, 0.6)' },
  { bg: 'linear-gradient(135deg, #76FF03 0%, #B2FF59 100%)', solid: '#76FF03', glow: 'rgba(118, 255, 3, 0.6)' },
  { bg: 'linear-gradient(135deg, #E040FB 0%, #EA80FC 100%)', solid: '#E040FB', glow: 'rgba(224, 64, 251, 0.6)' }
];

// Wheel constants
const SEGMENT_WIDTH = 110;
const VIEWPORT_WIDTH = 660; // Show ~6 segments
const MIN_SEGMENTS = 50; // Minimum segments to ensure smooth infinite scroll

// ============================================
// SVG ICONS
// ============================================
const Icons = {
  trophy: <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><path d="M6 9V2h12v7a6 6 0 01-12 0z"/><path d="M6 4H4a2 2 0 000 4h2M18 4h2a2 2 0 010 4h-2"/><path d="M9 22h6M12 15v7"/></svg>,
  coin: <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><circle cx="12" cy="12" r="10"/><path d="M12 6v12M8 10h8M8 14h8"/></svg>,
  users: <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><path d="M17 21v-2a4 4 0 00-4-4H5a4 4 0 00-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M23 21v-2a4 4 0 00-3-3.87M16 3.13a4 4 0 010 7.75"/></svg>,
  clock: <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/></svg>,
  spinner: <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><path d="M21 12a9 9 0 11-6.219-8.56"/></svg>,
  lock: <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><rect x="3" y="11" width="18" height="11" rx="2" ry="2"/><path d="M7 11V7a5 5 0 0110 0v4"/></svg>,
  send: <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><line x1="22" y1="2" x2="11" y2="13"/><polygon points="22 2 15 22 11 13 2 9 22 2"/></svg>,
  crown: <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><path d="M2 4l3 12h14l3-12-6 7-4-7-4 7-6-7z"/><path d="M3 20h18"/></svg>,
  history: <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/></svg>,
  chevronDown: <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><polyline points="6 9 12 15 18 9"/></svg>,
  info: <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><circle cx="12" cy="12" r="10"/><line x1="12" y1="16" x2="12" y2="12"/><line x1="12" y1="8" x2="12.01" y2="8"/></svg>,
  close: <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>,
  check: <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><polyline points="20 6 9 17 4 12"/></svg>,
  percent: <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><line x1="19" y1="5" x2="5" y2="19"/><circle cx="6.5" cy="6.5" r="2.5"/><circle cx="17.5" cy="17.5" r="2.5"/></svg>,
  shield: <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/><polyline points="9 12 11 14 15 10"/></svg>,
  copy: <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg>,
  refresh: <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><polyline points="23 4 23 10 17 10"/><path d="M20.49 15a9 9 0 1 1-2.12-9.36L23 10"/></svg>,
  gift: <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><rect x="3" y="8" width="18" height="4" rx="1"/><rect x="5" y="12" width="14" height="10" rx="1"/><line x1="12" y1="8" x2="12" y2="22"/><path d="M12 8c-2-4-6-4-6 0s4 4 6 0"/><path d="M12 8c2-4 6-4 6 0s-4 4-6 0"/></svg>,
  eye: <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/><circle cx="12" cy="12" r="3"/></svg>,
  user: <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><path d="M20 21v-2a4 4 0 00-4-4H8a4 4 0 00-4 4v2"/><circle cx="12" cy="7" r="4"/></svg>,
  twitter: <svg viewBox="0 0 24 24" fill="currentColor"><path d="M23.953 4.57a10 10 0 01-2.825.775 4.958 4.958 0 002.163-2.723c-.951.555-2.005.959-3.127 1.184a4.92 4.92 0 00-8.384 4.482C7.69 8.095 4.067 6.13 1.64 3.162a4.822 4.822 0 00-.666 2.475c0 1.71.87 3.213 2.188 4.096a4.904 4.904 0 01-2.228-.616v.06a4.923 4.923 0 003.946 4.827 4.996 4.996 0 01-2.212.085 4.936 4.936 0 004.604 3.417 9.867 9.867 0 01-6.102 2.105c-.39 0-.779-.023-1.17-.067a13.995 13.995 0 007.557 2.209c9.053 0 13.998-7.496 13.998-13.985 0-.21 0-.42-.015-.63A9.935 9.935 0 0024 4.59z"/></svg>,
  telegram: <svg viewBox="0 0 24 24" fill="currentColor"><path d="M12 0C5.374 0 0 5.373 0 12s5.374 12 12 12c6.627 0 12-5.373 12-12S18.627 0 12 0zm5.894 8.221l-1.97 9.28c-.145.658-.537.818-1.084.508l-3-2.21-1.446 1.394c-.14.18-.357.295-.6.295-.002 0-.003 0-.005 0l.213-3.054 5.56-5.022c.24-.213-.054-.334-.373-.121l-6.869 4.326-2.96-.924c-.64-.203-.658-.64.135-.954l11.566-4.458c.538-.196 1.006.128.832.941z"/></svg>
};

// ============================================
// UTILITIES
// ============================================
const fmt = (coins) => {
  return (coins || 0).toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 });
};

const fmtCents = (cents) => {
  return fmt((cents || 0) / 100);
};

// ============================================
// TOOLTIP COMPONENT
// ============================================
function Tooltip({ children, text }) {
  return (
    <span className="tooltip-wrapper">
      <span className="tooltip-trigger">{children}</span>
      <span className="tooltip-content">{text}</span>
    </span>
  );
}

// ============================================
// LIVE PLAYER BADGE COMPONENT
// ============================================
function LiveBadge({ count }) {
  return (
    <span className="live-badge">
      <span>{count} player{count !== 1 ? 's' : ''}</span>
    </span>
  );
}

const parseDate = (dateStr) => {
  if (!dateStr) return null;
  try {
    // If no timezone specified, assume UTC by appending Z
    let str = dateStr;
    if (!str.endsWith('Z') && !str.includes('+') && !str.includes('-', 10)) {
      str = str + 'Z';
    }
    return new Date(str);
  } catch (e) {
    return null;
  }
};

// Helper to parse date as UTC
const parseUTCDate = (dateStr) => {
  if (!dateStr) return null;
  try {
    // If no timezone specified, assume UTC by appending Z
    let str = dateStr;
    if (!str.endsWith('Z') && !str.includes('+') && !str.includes('-', 10)) {
      str = str + 'Z';
    }
    return new Date(str);
  } catch (e) {
    return null;
  }
};

const getColor = (idx) => COLORS[idx % COLORS.length];

// ============================================
// WHEEL COMPONENT WITH JS-DRIVEN ANIMATION
// Uses requestAnimationFrame for reliable animation even in background tabs
// ============================================

// Easing function: cubic-bezier(0.1, 0.7, 0.1, 1) approximation
const easeOutQuart = (t) => 1 - Math.pow(1 - t, 4);

function Wheel({ participants, pot, winner, status, secondsSinceResolved, isInitialLoad }) {
  const [segments, setSegments] = useState([]);
  const [currentOffset, setCurrentOffset] = useState(0);
  const [winnerIdx, setWinnerIdx] = useState(null);
  const [isAnimating, setIsAnimating] = useState(false);
  
  const animationRef = useRef(null);
  const spinStartTimeRef = useRef(null);
  const targetOffsetRef = useRef(0);
  const hasTriggeredSpinRef = useRef(false);
  const lastStatusRef = useRef(null);
  
  const TOTAL_SEGMENTS = 60;
  
  // Compute isSpinning from status
  const isSpinning = status === 'spinning';
  
  // Cleanup animation on unmount
  useEffect(() => {
    return () => {
      if (animationRef.current) {
        cancelAnimationFrame(animationRef.current);
      }
    };
  }, []);
  
  // JS-driven animation loop
  const startAnimation = useCallback((startOffset, endOffset, duration, startTime = null) => {
    // Cancel any existing animation
    if (animationRef.current) {
      cancelAnimationFrame(animationRef.current);
    }
    
    const animStartTime = startTime || performance.now();
    setIsAnimating(true);
    
    const animate = (currentTime) => {
      const elapsed = currentTime - animStartTime;
      const progress = Math.min(elapsed / duration, 1);
      const easedProgress = easeOutQuart(progress);
      
      const newOffset = startOffset + (endOffset - startOffset) * easedProgress;
      setCurrentOffset(newOffset);
      
      if (progress < 1) {
        animationRef.current = requestAnimationFrame(animate);
      } else {
        setCurrentOffset(endOffset);
        setIsAnimating(false);
        animationRef.current = null;
      }
    };
    
    animationRef.current = requestAnimationFrame(animate);
  }, []);
  
  // Build segments when participants change
  useEffect(() => {
    if (participants.length === 0) {
      const placeholders = Array(TOTAL_SEGMENTS).fill(null).map((_, i) => ({
        key: `empty-${i}`,
        isEmpty: true,
        username: '',
        percentage: 0,
        color: { bg: 'rgba(255,255,255,0.03)', solid: '#333', glow: 'transparent' }
      }));
      setSegments(placeholders);
      return;
    }
    
    const withColors = participants.map((p, i) => ({
      ...p,
      color: getColor(i),
      percentage: pot > 0 ? (p.amount / pot) * 100 : 0
    }));
    
    // Fill segments by repeating players
    const allSegments = [];
    for (let i = 0; i < TOTAL_SEGMENTS; i++) {
      const p = withColors[i % withColors.length];
      allSegments.push({
        ...p,
        key: `seg-${i}`,
        segmentIndex: i
      });
    }
    
    setSegments(allSegments);
  }, [participants, pot]);
  
  // Handle spinning state from server
  useEffect(() => {
    const prevStatus = lastStatusRef.current;
    lastStatusRef.current = status;
    
    console.log('[Wheel] Status update:', { status, prevStatus, winner, hasTriggered: hasTriggeredSpinRef.current });
    
    // NEW ROUND: Reset everything when we go back to open/countdown
    if ((status === 'open' || status === 'countdown') && 
        (prevStatus === 'spinning' || prevStatus === 'reveal' || prevStatus === 'intermission' || !prevStatus)) {
      console.log('[Wheel] Resetting for new round');
      if (animationRef.current) {
        cancelAnimationFrame(animationRef.current);
        animationRef.current = null;
      }
      setCurrentOffset(0);
      setWinnerIdx(null);
      setIsAnimating(false);
      targetOffsetRef.current = 0;
      spinStartTimeRef.current = null;
      hasTriggeredSpinRef.current = false;
      return;
    }
    
    // Skip if no winner or no segments yet
    if (!winner || segments.length === 0 || segments[0]?.isEmpty) {
      if (status === 'spinning' && !winner) {
        console.log('[Wheel] Spinning but no winner yet, waiting...');
      }
      return;
    }
    
    // Calculate target position for winner
    const calculateWinnerPosition = () => {
      const allWinnerSegments = segments
        .map((s, i) => ({ ...s, idx: i }))
        .filter(s => s.username === winner);
      
      if (allWinnerSegments.length === 0) {
        console.log('[Wheel] ERROR: No winner segments found for:', winner);
        return null;
      }
      
      // Prefer target zone (60-85% of wheel) for visual effect
      let winnerSegments = allWinnerSegments.filter(s => s.idx > TOTAL_SEGMENTS * 0.6 && s.idx < TOTAL_SEGMENTS * 0.85);
      if (winnerSegments.length === 0) {
        winnerSegments = allWinnerSegments.filter(s => s.idx > TOTAL_SEGMENTS * 0.3);
      }
      if (winnerSegments.length === 0) {
        winnerSegments = allWinnerSegments;
      }
      
      // Use consistent target based on winner name (so all clients land same place)
      const hash = winner.split('').reduce((a, c) => a + c.charCodeAt(0), 0);
      const targetSeg = winnerSegments[hash % winnerSegments.length];
      
      const viewportCenter = VIEWPORT_WIDTH / 2;
      const segmentCenter = SEGMENT_WIDTH / 2;
      const offset = (targetSeg.idx * SEGMENT_WIDTH) - viewportCenter + segmentCenter;
      
      return { offset, segIdx: targetSeg.idx };
    };
    
    // SPINNING: User joined mid-spin or spin just started
    if (status === 'spinning' && winner) {
      const position = calculateWinnerPosition();
      if (!position) return;
      
      // Check if this is a mid-spin join (from fetchServerState, not WebSocket):
      const isMidSpinJoin = isInitialLoad && secondsSinceResolved && secondsSinceResolved > 0;
      
      // For mid-spin joins, always allow animation (user switched tabs)
      if (isMidSpinJoin) {
        hasTriggeredSpinRef.current = false;
      }
      
      // Skip if we already handled this spin (for WebSocket events)
      if (hasTriggeredSpinRef.current) return;
      
      console.log('[Wheel] Spinning state:', { isInitialLoad, secondsSinceResolved, isMidSpinJoin });
      
      targetOffsetRef.current = position.offset;
      setWinnerIdx(position.segIdx);
      hasTriggeredSpinRef.current = true;
      
      if (isMidSpinJoin) {
        // Mid-spin join: calculate where we should be based on server time
        const elapsedMs = secondsSinceResolved * 1000;
        const remainingMs = Math.max(0, CONFIG.spinDuration - elapsedMs);
        
        console.log('[Wheel] Mid-spin join, elapsed:', elapsedMs, 'remaining:', remainingMs);
        
        if (remainingMs > 500) {
          // Calculate current position based on elapsed time
          const progress = elapsedMs / CONFIG.spinDuration;
          const easedProgress = easeOutQuart(progress);
          const currentPos = position.offset * easedProgress;
          
          console.log('[Wheel] Mid-spin: starting from', currentPos, 'to', position.offset, 'over', remainingMs, 'ms');
          
          // Start animation from current position to final position
          setCurrentOffset(currentPos);
          startAnimation(currentPos, position.offset, remainingMs);
        } else {
          // Less than 0.5s left - just show final position
          console.log('[Wheel] Mid-spin: too little time, showing final position');
          setCurrentOffset(position.offset);
        }
      } else {
        // Fresh spin from WebSocket - animate fully from start
        console.log('[Wheel] Fresh spin, starting full animation to:', winner);
        spinStartTimeRef.current = performance.now();
        setCurrentOffset(0);
        startAnimation(0, position.offset, CONFIG.spinDuration);
      }
    }
    
    // REVEAL/INTERMISSION: Show winner at final position
    if ((status === 'reveal' || status === 'intermission') && winner) {
      const position = calculateWinnerPosition();
      if (position) {
        console.log('[Wheel] Reveal/intermission - showing winner at position');
        if (animationRef.current) {
          cancelAnimationFrame(animationRef.current);
          animationRef.current = null;
        }
        setCurrentOffset(position.offset);
        setWinnerIdx(position.segIdx);
        setIsAnimating(false);
        hasTriggeredSpinRef.current = true;
      }
    }
  }, [status, winner, segments, secondsSinceResolved, isInitialLoad, startAnimation]);
  
  const hasPlayers = segments.length > 0 && !segments[0]?.isEmpty;
  const showWinnerHighlight = (status === 'reveal' || status === 'intermission') && winnerIdx !== null;
  
  return (
    <div className={`wheel-container ${isSpinning ? 'spinning' : ''} ${showWinnerHighlight ? 'revealed' : ''}`}>
      {/* Center pointer */}
      <div className="wheel-pointer-wrapper">
        <div className="wheel-pointer">
          <svg viewBox="0 0 40 50" fill="none" xmlns="http://www.w3.org/2000/svg">
            <path d="M20 45L5 15h30L20 45z" fill="#22c55e"/>
            <path d="M20 40L8 18h24L20 40z" fill="#16a34a"/>
            <path d="M20 0v15" stroke="#22c55e" strokeWidth="3"/>
          </svg>
        </div>
      </div>
      
      <div className="wheel-viewport" style={{ width: VIEWPORT_WIDTH }}>
        <div className="wheel-fade wheel-fade-left" />
        <div className="wheel-fade wheel-fade-right" />
        
        <div
          className={`wheel-track ${isAnimating ? 'animating' : ''}`}
          style={{
            transform: `translateX(-${currentOffset}px)`,
            WebkitTransform: `translateX(-${currentOffset}px)`,
            MozTransform: `translateX(-${currentOffset}px)`,
            msTransform: `translateX(-${currentOffset}px)`
          }}
        >
          {segments.map((seg, idx) => {
            const isWinnerSeg = showWinnerHighlight && idx === winnerIdx;
            return (
              <div
                key={seg.key}
                className={`wheel-segment ${isWinnerSeg ? 'winner' : ''} ${seg.isEmpty ? 'empty' : ''}`}
                style={{ 
                  width: SEGMENT_WIDTH,
                  background: seg.color.bg,
                }}
              >
                {!seg.isEmpty && (
                  <div className="segment-content">
                    <div 
                      className="segment-avatar"
                      style={{ 
                        background: seg.color.solid,
                        boxShadow: isWinnerSeg ? `0 0 25px ${seg.color.glow}` : '0 4px 12px rgba(0,0,0,0.3)'
                      }}
                    >
                      {seg.username.slice(0, 2).toUpperCase()}
                    </div>
                    <div className="segment-name">{seg.username}</div>
                    <div className="segment-chance">{seg.percentage.toFixed(1)}%</div>
                  </div>
                )}
                {seg.isEmpty && (
                  <div className="segment-content empty">
                    <div className="segment-avatar empty-avatar">?</div>
                  </div>
                )}
                <div className="segment-shine" />
              </div>
            );
          })}
        </div>
      </div>
      
      {/* Waiting overlay */}
      {!hasPlayers && (
        <div className="wheel-empty-overlay">
          <span className="empty-text">Waiting for players...</span>
        </div>
      )}
      
      {/* Winner flash */}
      {showWinnerHighlight && <div className="wheel-winner-flash" />}
    </div>
  );
}

// ============================================
// TIMER COMPONENT
// ============================================
function Timer({ endsAt, status }) {
  const [seconds, setSeconds] = useState(0);
  
  useEffect(() => {
    if (!endsAt || status !== 'countdown') {
      setSeconds(0);
      return;
    }
    
    const endDate = parseDate(endsAt);
    if (!endDate) return;
    
    const tick = () => {
      const remaining = Math.max(0, Math.ceil((endDate.getTime() - Date.now()) / 1000));
      setSeconds(remaining);
    };
    
    tick();
    const interval = setInterval(tick, 100);
    return () => clearInterval(interval);
  }, [endsAt, status]);
  
  if (status === 'spinning') {
    return (
      <div className="timer timer-spinning">
        <span className="timer-icon spinning">{Icons.spinner}</span>
        <span>Drawing winner...</span>
      </div>
    );
  }
  
  if (status === 'reveal' || status === 'intermission') {
    return (
      <div className="timer timer-winner">
        <span className="timer-icon">{Icons.crown}</span>
        <span>Winner!</span>
      </div>
    );
  }
  
  if (status === 'open') {
    return (
      <div className="timer timer-waiting">
        <span className="timer-icon">{Icons.clock}</span>
        <span>Waiting for bets</span>
      </div>
    );
  }
  
  if (seconds <= 0) return null;
  
  const mins = Math.floor(seconds / 60);
  const secs = seconds % 60;
  const isUrgent = seconds <= 10;
  
  return (
    <div className={`timer ${isUrgent ? 'timer-urgent' : ''}`}>
      <span className="timer-icon">{Icons.clock}</span>
      <span>{mins}:{secs.toString().padStart(2, '0')}</span>
    </div>
  );
}

// ============================================
// BET PANEL
// ============================================
function BetPanel({ user, pot, onDeposit, isLocked, countdownSeconds }) {
  const [amount, setAmount] = useState('');
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState('');
  const [selectedPreset, setSelectedPreset] = useState(null);
  
  const presets = [1, 5, 10, 50, 100];
  
  const handlePresetClick = (presetCoins) => {
    setSelectedPreset(presetCoins);
    setAmount(presetCoins.toString());
    setError('');
  };
  
  const handleInputChange = (e) => {
    const val = e.target.value;
    setAmount(val);
    setSelectedPreset(null);
    setError('');
  };
  
  const handleDeposit = async (amountCoins) => {
    if (isLocked || loading) return;
    if (!user) {
      window.openAuthModal?.('login');
      return;
    }
    if (amountCoins < CONFIG.minBet) {
      setError(`Minimum bet is ${CONFIG.minBet} coins`);
      return;
    }
    const balanceCoins = (user?.balance || 0) / 100;
    if (amountCoins > balanceCoins) {
      setError('Insufficient balance');
      return;
    }
    
    setLoading(true);
    setError('');
    
    try {
      const res = await fetch('/jackpot/deposit', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        credentials: 'include',
        body: JSON.stringify({ amount: amountCoins })
      });
      
      const data = await res.json();
      if (!res.ok) throw new Error(data.detail || 'Deposit failed');
      
      setAmount('');
      setSelectedPreset(null);
      onDeposit?.({ ...data, amount: Math.round(amountCoins * 100) });
    } catch (err) {
      setError(err.message);
    } finally {
      setLoading(false);
    }
  };
  
  const handleMaxBet = () => {
    if (!user) {
      window.openAuthModal?.('login');
      return;
    }
    const balanceCoins = (user?.balance || 0) / 100;
    setAmount(balanceCoins.toFixed(2));
    setSelectedPreset(null);
    setError('');
  };
  
  const handleLoginClick = () => {
    window.openAuthModal?.('login');
  };
  
  const customAmountCoins = parseFloat(amount || 0);
  const currentAmount = customAmountCoins > 0 ? customAmountCoins : 0;
  const balanceCoins = (user?.balance || 0) / 100;
  const chance = pot > 0 && currentAmount > 0 
    ? ((currentAmount / (pot + currentAmount)) * 100).toFixed(1) 
    : null;
  
  return (
    <div className="jp-create-panel">
      <div className="jp-create-panel__header">
        <div className="jp-create-panel__title">
          <span className="jp-create-panel__icon">{Icons.coin}</span>
          <span>Place Bet</span>
        </div>
        {user && (
          <div className="jp-create-panel__balance">
            <span className="jp-balance-value">{fmtCents(user.balance)}</span>
            <span className="jp-balance-label">coins</span>
          </div>
        )}
      </div>
      
      <div className="jp-create-panel__content">
        {isLocked && (
          <div className="jp-panel-locked">
            <span className="jp-lock-icon">{Icons.lock}</span>
            <span className="jp-lock-text">
              {countdownSeconds > 0 ? `Next round in ${countdownSeconds}s` : 'Round in progress...'}
            </span>
          </div>
        )}
        
        {error && <div className="jp-create-panel__error">{error}</div>}
        
        {/* Amount Input Section */}
        <div className="jp-amount-section">
          <div className={`jp-amount-input ${currentAmount > 0 ? 'has-value' : ''}`}>
            <input
              type="number"
              placeholder="0.00"
              value={amount}
              onChange={handleInputChange}
              disabled={isLocked || loading}
              step="0.01"
              min="0.10"
            />
            <div className="jp-amount-suffix">
              <span>coins</span>
            </div>
          </div>
        </div>
        
        {/* Preset Buttons */}
        <div className="jp-amount-presets">
          {presets.map(presetCoins => (
            <button
              key={presetCoins}
              className={`jp-preset-btn ${parseFloat(amount) === presetCoins ? 'active' : ''}`}
              onClick={() => handlePresetClick(presetCoins)}
              disabled={isLocked || loading}
            >
              {presetCoins}
            </button>
          ))}
          <button
            className="jp-preset-btn jp-preset-btn--max"
            onClick={handleMaxBet}
            disabled={isLocked || loading}
          >
            MAX
          </button>
        </div>
        
        {/* Stats Row */}
        <div className="jp-stats-row">
          <div className="jp-stat-box">
            <span className="jp-stat-label">Current Pot</span>
            <span className="jp-stat-value">{fmt(pot)}</span>
          </div>
          <div className="jp-stat-box">
            <span className="jp-stat-label">Your Chance</span>
            <span className="jp-stat-value jp-stat-value--chance">{chance ? `${chance}%` : '—'}</span>
          </div>
        </div>
        
        {/* CTA Button */}
        <div className="jp-create-panel__cta">
          <button
            className={`jp-cta-btn jp-cta-btn--primary ${loading ? 'loading' : ''}`}
            onClick={() => handleDeposit(currentAmount)}
            disabled={isLocked || loading || currentAmount < CONFIG.minBet}
          >
            {loading ? (
              <span className="jp-cta-loading">{Icons.spinner}</span>
            ) : (
              <>
                <span className="jp-cta-icon">{Icons.send}</span>
                <span>{currentAmount >= CONFIG.minBet ? `Place ${fmt(currentAmount)} Bet` : 'Enter Amount'}</span>
              </>
            )}
          </button>
        </div>
      </div>
    </div>
  );
}

// ============================================
// PARTICIPANTS LIST
// ============================================
function ParticipantsList({ participants, pot, currentUser }) {
  const sorted = [...participants].sort((a, b) => b.amount - a.amount);
  
  return (
    <div className="panel participants-panel">
      <div className="panel-header">
        <div className="panel-title">
          <span className="panel-icon">{Icons.users}</span>
          <span>Players</span>
        </div>
        <span className="panel-badge">{participants.length}</span>
      </div>
      
      <div className="panel-body participants-list">
        {sorted.length === 0 ? (
          <div className="empty-state">
            <span className="empty-icon">{Icons.users}</span>
            <span>No players yet</span>
          </div>
        ) : (
          sorted.map((p, idx) => {
            const chance = pot > 0 ? (p.amount / pot) * 100 : 0;
            const isCurrentUser = currentUser && p.username === currentUser.username;
            const color = getColor(participants.findIndex(x => x.username === p.username));
            
            return (
              <div key={p.username} className={`participant-row ${isCurrentUser ? 'current-user' : ''}`}>
                <div className="participant-rank">#{idx + 1}</div>
                <div 
                  className="participant-avatar" 
                  style={{ background: color.bg }}
                >
                  {p.username.slice(0, 2).toUpperCase()}
                </div>
                <div className="participant-info">
                  <div className="participant-name">{p.username}</div>
                  <div className="participant-bet">{fmt(p.amount)} coins</div>
                </div>
                <div className="participant-chance" style={{ color: color.solid }}>
                  {chance.toFixed(1)}%
                </div>
              </div>
            );
          })
        )}
      </div>
    </div>
  );
}

// ============================================
// HISTORY PANEL - Coinflip Style
// ============================================
function HistoryPanel({ history }) {
  return (
    <div className="jp-history-panel">
      <div className="jp-history-panel__header">
        <div className="jp-history-panel__title">
          <span className="jp-history-panel__icon">{Icons.history}</span>
          <span>Recent Rounds</span>
        </div>
        <span className="jp-history-panel__count">{history.length}</span>
      </div>
      
      <div className="jp-history-panel__content">
        {history.length === 0 ? (
          <div className="jp-history-empty">
            <span className="jp-history-empty__icon">{Icons.history}</span>
            <span className="jp-history-empty__text">No rounds yet</span>
          </div>
        ) : (
          <div className="jp-history-list">
            {history.map((round, idx) => (
              <div key={round.id} className="jp-history-item">
                <div className="jp-history-item__winner">
                  <span className="jp-history-item__crown">{Icons.crown}</span>
                  <span className="jp-history-item__name">{round.winner}</span>
                </div>
                <div className="jp-history-item__stats">
                  <span className="jp-history-item__amount">{fmt(round.total)}</span>
                  <span className="jp-history-item__chance">{round.chance?.toFixed(1) || '?'}%</span>
                </div>
              </div>
            ))}
          </div>
        )}
      </div>
    </div>
  );
}

// ============================================
// JACKPOT DISPLAY
// ============================================
function JackpotDisplay({ pot, participants, status, countdown, showCountdown, endsAt, winner, serverSeedHash, serverSeed, clientSeed, ticket }) {
  const [countdownSecs, setCountdownSecs] = useState(0);
  
  // Calculate countdown from endsAt - runs on mount and when props change
  useEffect(() => {
    console.log('[JackpotDisplay] useEffect - status:', status, 'endsAt:', endsAt, 'countdownSecs:', countdownSecs);
    
    if (!endsAt || status !== 'countdown') {
      console.log('[JackpotDisplay] Not in countdown mode, setting countdownSecs to 0. Reason:', !endsAt ? 'no endsAt' : 'status is ' + status);
      setCountdownSecs(0);
      return;
    }
    
    // Parse as UTC (backend sends dates without timezone, they are UTC)
    const endDate = parseUTCDate(endsAt);
    if (!endDate) {
      console.log('[JackpotDisplay] Failed to parse endsAt:', endsAt);
      setCountdownSecs(0);
      return;
    }
    console.log('[JackpotDisplay] Setting up countdown timer. endsAt:', endsAt, 'endDate:', endDate, 'now:', new Date());
    
    const tick = () => {
      const remaining = Math.max(0, Math.ceil((endDate.getTime() - Date.now()) / 1000));
      setCountdownSecs(remaining);
    };
    
    // Run immediately
    tick();
    
    // Then run every 100ms
    const interval = setInterval(tick, 100);
    return () => clearInterval(interval);
  }, [endsAt, status]);
  
  const isEnded = status === 'reveal' || status === 'intermission';
  const showWinner = winner && isEnded;
  // Always show next round counter during intermission (no condition on showCountdown)
  const showNextRound = status === 'intermission' && countdown > 0;
  const isSpinning = status === 'spinning';
  const isCountdown = status === 'countdown' && countdownSecs > 0;
  
  // Format time as M:SS
  const formatTime = (secs) => {
    const mins = Math.floor(secs / 60);
    const s = secs % 60;
    return `${mins}:${s.toString().padStart(2, '0')}`;
  };

  // Calculate countdown progress (assuming max 60 seconds)
  const countdownProgress = Math.min(100, (countdownSecs / 60) * 100);
  
  return (
    <div className={`jackpot-display ${status}`}>
      <div className="jackpot-bg">
        <div className="jackpot-glow" />
        {isSpinning && <div className="jackpot-pulse-ring" />}
        {isCountdown && countdownSecs <= 10 && <div className="jackpot-urgent-pulse" />}
      </div>
      
      <div className="jackpot-content">
        {showWinner ? (
          /* Simple inline winner display - doesn't push content */
          <div className="winner-compact">
            <div className="winner-compact-left">
              <div className="winner-trophy-small">{Icons.trophy}</div>
              <div className="winner-details">
                <span className="winner-label">Winner</span>
                <span className="winner-name-compact">{winner}</span>
              </div>
            </div>
            <div className="winner-compact-center">
              <span className="winner-amount-compact">+{fmt(pot)}</span>
            </div>
            <div className="winner-compact-right">
              {showNextRound ? (
                <div className="next-round-inline">
                  <span className="next-round-timer">{countdown}s</span>
                  <span className="next-round-label">next</span>
                </div>
              ) : (
                <div className="next-round-inline">
                  <span className="next-round-timer">...</span>
                  <span className="next-round-label">next</span>
                </div>
              )}
            </div>
          </div>
        ) : (
          <>
            {/* Main jackpot display */}
            <div className={`jackpot-main ${isSpinning ? 'spinning' : ''} ${isCountdown ? 'countdown-active' : ''}`}>
              <div className="jackpot-trophy-wrap">
                <div className="jackpot-trophy">{Icons.trophy}</div>
              </div>
              
              <div className="jackpot-pot-section">
                <span className="jackpot-label-mini">Jackpot</span>
                <div className="jackpot-amount">{fmt(pot)}</div>
              </div>
            </div>
            
            {/* Status section */}
            <div className="jackpot-status-section">
              {isSpinning ? (
                <div className="status-spinning">
                  <div className="spinning-indicator">
                    <span className="spin-dot" />
                    <span className="spin-dot" />
                    <span className="spin-dot" />
                  </div>
                  <span className="status-text">Drawing winner</span>
                </div>
              ) : isCountdown ? (
                <div className={`status-countdown ${countdownSecs <= 10 ? 'urgent' : ''}`}>
                  <div className="countdown-timer">
                    <div className="countdown-ring">
                      <svg viewBox="0 0 100 100">
                        <circle className="ring-bg" cx="50" cy="50" r="45" />
                        <circle 
                          className="ring-progress" 
                          cx="50" 
                          cy="50" 
                          r="45"
                          style={{ 
                            strokeDasharray: `${countdownProgress * 2.83} 283`
                          }}
                        />
                      </svg>
                      <span className="countdown-number">{countdownSecs}</span>
                    </div>
                    <span className="countdown-label">seconds</span>
                  </div>
                </div>
              ) : (
                <div className="status-waiting">
                  <div className="waiting-indicator">
                    <span className="waiting-dot" />
                  </div>
                  <span className="status-text">Waiting for players</span>
                </div>
              )}
              
              {/* Player count */}
              <div className="player-count">
                {Icons.users}
                <span>{participants.length} player{participants.length !== 1 ? 's' : ''}</span>
              </div>
            </div>
          </>
        )}
      </div>
      
      {/* Provably Fair - Compact */}
      {isEnded && serverSeedHash && (
        <FairnessDisplay
          serverSeedHash={serverSeedHash}
          serverSeed={serverSeed}
          clientSeed={clientSeed}
          ticket={ticket}
        />
      )}
    </div>
  );
}

// ============================================
// FAIRNESS DISPLAY
// ============================================
function FairnessDisplay({ serverSeedHash, serverSeed, clientSeed, ticket }) {
  const [expanded, setExpanded] = useState(false);
  const [copied, setCopied] = useState(null);
  
  const copyToClipboard = (text, field) => {
    navigator.clipboard.writeText(text).then(() => {
      setCopied(field);
      setTimeout(() => setCopied(null), 2000);
    });
  };
  
  const truncate = (str, len = 12) => {
    if (!str) return 'N/A';
    if (str.length <= len) return str;
    return str.slice(0, len) + '...';
  };
  
  const ticketFormatted = ticket !== null ? ticket.toFixed(4) : null;
  
  return (
    <div className={`fairness-compact ${expanded ? 'expanded' : ''}`}>
      <button className="fairness-toggle" onClick={() => setExpanded(!expanded)}>
        <span className="fairness-shield">{Icons.shield}</span>
        <span className="fairness-toggle-text">Provably Fair</span>
        {ticketFormatted && <span className="fairness-ticket-badge">{ticketFormatted}</span>}
        <span className={`fairness-chevron ${expanded ? 'up' : 'down'}`}>
          {Icons.chevronDown}
        </span>
      </button>
      
      {expanded && (
        <div className="fairness-details">
          <div className="fairness-row">
            <span className="fairness-row-label">Server Hash</span>
            <div className="fairness-row-value">
              <code>{truncate(serverSeedHash, 16)}</code>
              <button 
                className={`fairness-copy-sm ${copied === 'hash' ? 'copied' : ''}`}
                onClick={() => copyToClipboard(serverSeedHash, 'hash')}
              >
                {copied === 'hash' ? Icons.check : Icons.copy}
              </button>
            </div>
          </div>
          
          {serverSeed && (
            <div className="fairness-row">
              <span className="fairness-row-label">Server Seed</span>
              <div className="fairness-row-value">
                <code>{truncate(serverSeed, 16)}</code>
                <button 
                  className={`fairness-copy-sm ${copied === 'seed' ? 'copied' : ''}`}
                  onClick={() => copyToClipboard(serverSeed, 'seed')}
                >
                  {copied === 'seed' ? Icons.check : Icons.copy}
                </button>
              </div>
            </div>
          )}
          
          {clientSeed && (
            <div className="fairness-row">
              <span className="fairness-row-label">Client Seed</span>
              <div className="fairness-row-value">
                <code>{truncate(clientSeed, 16)}</code>
                <button 
                  className={`fairness-copy-sm ${copied === 'client' ? 'copied' : ''}`}
                  onClick={() => copyToClipboard(clientSeed, 'client')}
                >
                  {copied === 'client' ? Icons.check : Icons.copy}
                </button>
              </div>
            </div>
          )}
          
          {ticketFormatted && (
            <div className="fairness-row ticket-row">
              <span className="fairness-row-label">Winning Ticket</span>
              <span className="fairness-ticket-num">{ticketFormatted}</span>
            </div>
          )}
        </div>
      )}
    </div>
  );
}

// ============================================
// RULES MODAL
// ============================================
function RulesModal({ isOpen, onClose }) {
  if (!isOpen) return null;
  
  return (
    <div className="modal-overlay" onClick={onClose}>
      <div className="modal-content rules-modal" onClick={(e) => e.stopPropagation()}>
        <button className="modal-close" onClick={onClose}>{Icons.close}</button>
        
        <div className="modal-header">
          <span className="modal-icon">{Icons.info}</span>
          <h2>Jackpot Rules</h2>
        </div>
        
        <div className="modal-body">
          <section className="rules-section">
            <h3><span className="section-icon">{Icons.gift}</span> How It Works</h3>
            <ul className="rules-list">
              <li>Deposit coins to join the jackpot round</li>
              <li>Your chance to win = Your bet ÷ Total pot</li>
              <li>When countdown ends, the wheel spins to pick a winner</li>
              <li>Winner takes the entire pot!</li>
            </ul>
          </section>
          
          <section className="rules-section">
            <h3><span className="section-icon">{Icons.percent}</span> House Edge</h3>
            <div className="house-edge-display">
              <span className="house-edge-value">4%</span>
              <span className="house-edge-label">House Edge</span>
            </div>
            <p>A 4% fee is deducted from the pot. Winner receives 96%.</p>
          </section>
          
          <section className="rules-section">
            <h3><span className="section-icon">{Icons.refresh}</span> The Wheel</h3>
            <ul className="rules-list">
              <li>Each player gets a segment proportional to their bet</li>
              <li>Bigger bets = larger segments = better odds</li>
              <li>The pointer indicates the winner when wheel stops</li>
            </ul>
          </section>
          
          <section className="rules-section">
            <h3><span className="section-icon">{Icons.clock}</span> Game States</h3>
            <div className="states-grid">
              <div className="state-item">
                <span className="state-badge state-open">Waiting</span>
                <span>Accepting bets</span>
              </div>
              <div className="state-item">
                <span className="state-badge state-countdown">Countdown</span>
                <span>30s until spin</span>
              </div>
              <div className="state-item">
                <span className="state-badge state-spinning">Spinning</span>
                <span>8 second spin</span>
              </div>
              <div className="state-item">
                <span className="state-badge state-reveal">Winner</span>
                <span>Winner announced!</span>
              </div>
            </div>
          </section>
        </div>
        
        <div className="modal-footer">
          <button className="modal-btn" onClick={onClose}>Got it!</button>
        </div>
      </div>
    </div>
  );
}

// ============================================
// FOOTER
// ============================================
function Footer() {
  return (
    <footer className="cr-premium-footer">
      <div className="cr-footer-container">
        <div className="cr-footer-grid">
          {/* Brand Section */}
          <div className="cr-footer-brand">
            <div className="cr-footer-logo">
              <div className="cr-footer-logo-icon">CR</div>
              <div className="cr-footer-logo-text">
                <span className="cr-footer-logo-name">CoinRush</span>
                <span className="cr-footer-logo-tag">CRYPTO CASINO</span>
              </div>
            </div>
            <p className="cr-footer-description">
              The most transparent and provably fair crypto casino. Instant payouts, zero hidden fees, 100% verified fairness on every game.
            </p>
            <div className="cr-footer-socials">
              <a href="#" className="cr-social-btn" aria-label="Twitter">
                <svg width="17" height="17" fill="currentColor" viewBox="0 0 24 24"><path d="M23.953 4.57a10 10 0 01-2.825.775 4.958 4.958 0 002.163-2.723c-.951.555-2.005.959-3.127 1.184a4.92 4.92 0 00-8.384 4.482C7.69 8.095 4.067 6.13 1.64 3.162a4.822 4.822 0 00-.666 2.475c0 1.71.87 3.213 2.188 4.096a4.904 4.904 0 01-2.228-.616v.06a4.923 4.923 0 003.946 4.827 4.996 4.996 0 01-2.212.085 4.936 4.936 0 004.604 3.417 9.867 9.867 0 01-6.102 2.105c-.39 0-.779-.023-1.17-.067a13.995 13.995 0 007.557 2.209c9.053 0 13.998-7.496 13.998-13.985 0-.21 0-.42-.015-.63A9.935 9.935 0 0024 4.59z"/></svg>
              </a>
              <a href="#" className="cr-social-btn" aria-label="Telegram">
                <svg width="17" height="17" fill="currentColor" viewBox="0 0 24 24"><path d="M12 0C5.374 0 0 5.373 0 12s5.374 12 12 12c6.627 0 12-5.373 12-12S18.627 0 12 0zm5.894 8.221l-1.97 9.28c-.145.658-.537.818-1.084.508l-3-2.21-1.446 1.394c-.14.18-.357.295-.6.295-.002 0-.003 0-.005 0l.213-3.054 5.56-5.022c.24-.213-.054-.334-.373-.121l-6.869 4.326-2.96-.924c-.64-.203-.658-.64.135-.954l11.566-4.458c.538-.196 1.006.128.832.941z"/></svg>
              </a>
              <a href="#" className="cr-social-btn" aria-label="Discord">
                <svg width="17" height="17" fill="currentColor" viewBox="0 0 24 24"><path d="M20.317 4.37a19.791 19.791 0 00-4.885-1.515.074.074 0 00-.079.037c-.21.375-.444.864-.608 1.25a18.27 18.27 0 00-5.487 0 12.64 12.64 0 00-.617-1.25.077.077 0 00-.079-.037A19.736 19.736 0 003.677 4.37a.07.07 0 00-.032.027C.533 9.046-.32 13.58.099 18.057a.082.082 0 00.031.057 19.9 19.9 0 005.993 3.03.078.078 0 00.084-.028c.462-.63.874-1.295 1.226-1.994a.076.076 0 00-.041-.106 13.107 13.107 0 01-1.872-.892.077.077 0 01-.008-.128 10.2 10.2 0 00.372-.292.074.074 0 01.077-.01c3.928 1.793 8.18 1.793 12.062 0a.074.074 0 01.078.01c.12.098.246.198.373.292a.077.077 0 01-.006.127 12.299 12.299 0 01-1.873.892.077.077 0 00-.041.107c.36.698.772 1.362 1.225 1.993a.076.076 0 00.084.028 19.839 19.839 0 006.002-3.03.077.077 0 00.032-.054c.5-5.177-.838-9.674-3.549-13.66a.061.061 0 00-.031-.03zM8.02 15.33c-1.183 0-2.157-1.085-2.157-2.419 0-1.333.956-2.419 2.157-2.419 1.21 0 2.176 1.096 2.157 2.42 0 1.333-.956 2.418-2.157 2.418zm7.975 0c-1.183 0-2.157-1.085-2.157-2.419 0-1.333.955-2.419 2.157-2.419 1.21 0 2.176 1.096 2.157 2.42 0 1.333-.946 2.418-2.157 2.418z"/></svg>
              </a>
            </div>
          </div>
          
          {/* Platform Links */}
          <div className="cr-footer-section">
            <h3 className="cr-footer-section-title">Platform</h3>
            <div className="cr-footer-links">
              <button onClick={() => window.showProvablyFairModal?.()} className="cr-footer-link">
                <span className="cr-footer-icon">PF</span>
                <span>Provably Fair</span>
              </button>
              <a href="/terms" className="cr-footer-link">
                <span className="cr-footer-icon">TC</span>
                <span>Terms</span>
              </a>
              <a href="/privacy" className="cr-footer-link">
                <span className="cr-footer-icon">PP</span>
                <span>Privacy</span>
              </a>
              <a href="/about" className="cr-footer-link">
                <span className="cr-footer-icon">AB</span>
                <span>About Us</span>
              </a>
            </div>
          </div>
          
          {/* Games Links */}
          <div className="cr-footer-section">
            <h3 className="cr-footer-section-title">Games</h3>
            <div className="cr-footer-links">
              <a href="/" className="cr-footer-link">
                <span className="cr-footer-icon">CF</span>
                <span>Coinflip</span>
              </a>
              <a href="/jackpot" className="cr-footer-link">
                <span className="cr-footer-icon">JP</span>
                <span>Jackpot</span>
              </a>
              <a href="/premium-jackpot" className="cr-footer-link">
                <span className="cr-footer-icon">PJ</span>
                <span>Premium Jackpot</span>
              </a>
              <a href="/barbarian" className="cr-footer-link">
                <span className="cr-footer-icon">BD</span>
                <span>Barbarian Duel</span>
              </a>
              <a href="/crash" className="cr-footer-link">
                <span className="cr-footer-icon">CD</span>
                <span>Crash</span>
              </a>
            </div>
          </div>
          
          {/* Support Links */}
          <div className="cr-footer-section">
            <h3 className="cr-footer-section-title">Support</h3>
            <div className="cr-footer-links">
              <a href="/faq" className="cr-footer-link">
                <span className="cr-footer-icon">FQ</span>
                <span>FAQ</span>
              </a>
              <a href="/live-support" className="cr-footer-link">
                <span className="cr-footer-icon">LS</span>
                <span>Live Support</span>
              </a>
              <a href="/support" className="cr-footer-link">
                <span className="cr-footer-icon">SP</span>
                <span>Support</span>
              </a>
              <a href="/contact" className="cr-footer-link">
                <span className="cr-footer-icon">CT</span>
                <span>Contact</span>
              </a>
            </div>
          </div>
        </div>
        
        {/* Bottom Bar */}
        <div className="cr-footer-bottom">
          <p className="cr-footer-copyright">
            © 2025 <span className="cr-footer-copyright-brand">CoinRush</span>. All rights reserved.
          </p>
          <div className="cr-footer-badges">
            <span className="cr-footer-badge cr-footer-badge--age">18+</span>
            <span className="cr-footer-badge cr-footer-badge--responsible">PLAY RESPONSIBLY</span>
          </div>
        </div>
      </div>
    </footer>
  );
}

// ============================================
// AUTH MODAL - Use shared component from auth-react.jsx
// ============================================
// The AuthModal is now loaded from /static/js/auth-react.jsx
// which includes OAuth buttons (Discord, Google, X)
// Access it via window.AuthModal

// ============================================
// MAIN APP
// ============================================
function JackpotApp() {
  const [user, setUser] = useState(null);
  const [gameState, setGameState] = useState({
    status: 'open',
    pot: 0,
    participants: [],
    endsAt: null,
    winner: null,
    roundId: null,
    intermissionEndsAt: null,
    serverSeedHash: null,
    serverSeed: null,
    clientSeed: null,
    ticket: null,
    secondsSinceResolved: null,
    isInitialLoad: true  // Flag to track if this is from initial fetch vs WebSocket
  });
  const [history, setHistory] = useState([]);
  const [isConnected, setIsConnected] = useState(false);
  const [intermissionCountdown, setIntermissionCountdown] = useState(0);
  const [showCountdownUI, setShowCountdownUI] = useState(false);
  const [showRulesModal, setShowRulesModal] = useState(false);
  const [showAuthModal, setShowAuthModal] = useState(false);
  const [authModalMode, setAuthModalMode] = useState('login');
  
  // UX Enhancement States
  const [showConfetti, setShowConfetti] = useState(false);
  const [showWinnerOverlay, setShowWinnerOverlay] = useState(false);
  const [winnerData, setWinnerData] = useState(null);
  const [soundEnabled, setSoundEnabled] = useState(true);
  
  const wsRef = useRef(null);
  const userRef = useRef(null);
  const gameStateRef = useRef(null);
  const lastWinnerRef = useRef(null);
  
  // Get toast function from context
  const addToast = useToast();
  
  useEffect(() => { userRef.current = user; }, [user]);
  useEffect(() => { gameStateRef.current = gameState; }, [gameState]);
  
  // Handle winner celebration
  useEffect(() => {
    if (gameState.status === 'reveal' && gameState.winner && gameState.winner !== lastWinnerRef.current) {
      lastWinnerRef.current = gameState.winner;
      
      const isCurrentUserWinner = user && gameState.winner === user.username;
      const winAmount = gameState.pot / 100;
      
      // Show confetti ONLY for winner
      if (isCurrentUserWinner) {
        setShowConfetti(true);
        setTimeout(() => setShowConfetti(false), 5000);
        
        // Show winner overlay ONLY for the actual winner
        setWinnerData({
          winner: gameState.winner,
          amount: winAmount,
          isCurrentUser: true
        });
        setShowWinnerOverlay(true);
        
        // Winner toast
        if (addToast) {
          addToast(`You won ${fmt(winAmount)} coins!`, 'win', 6000);
        }
      } else {
        // Non-winner just gets a simple toast notification
        if (addToast) {
          addToast(`${gameState.winner} won ${fmt(winAmount)} coins`, 'info', 4000);
        }
      }
    }
    
    // Reset winner ref on new round
    if (gameState.status === 'open' || gameState.status === 'countdown') {
      lastWinnerRef.current = null;
    }
  }, [gameState.status, gameState.winner, gameState.pot, user, addToast]);
  
  // Sound toggle
  const toggleSound = useCallback(() => {
    const newState = SoundManager.toggle();
    setSoundEnabled(newState);
  }, []);
  
  // Expose functions to window for navbar
  useEffect(() => {
    window.showJackpotRules = () => setShowRulesModal(true);
    window.openAuthModal = (mode = 'login') => {
      setAuthModalMode(mode);
      setShowAuthModal(true);
    };
    window.dailyRewardsModal = {
      open: () => window.dispatchEvent(new CustomEvent('openDailyRewards')),
      close: () => {}
    };
    return () => {
      delete window.showJackpotRules;
      delete window.openAuthModal;
      delete window.dailyRewardsModal;
    };
  }, []);
  
  // Fetch user
  useEffect(() => {
    const fetchUser = () => {
      fetch('/me', { credentials: 'include' })
        .then(res => res.ok ? res.json() : null)
        .then(data => setUser(data))
        .catch(() => {});
    };
    fetchUser();
    
    const handleBalanceRefresh = () => fetchUser();
    window.addEventListener('balanceUpdate', handleBalanceRefresh);
    return () => window.removeEventListener('balanceUpdate', handleBalanceRefresh);
  }, []);
  
  // Fetch history
  useEffect(() => {
    fetch('/jackpot/history', { credentials: 'include' })
      .then(res => res.ok ? res.json() : [])
      .then(rounds => {
        const historyData = rounds.slice(0, 10).map(round => ({
          id: round.id,
          winner: round.winner,
          total: round.total,
          chance: round.participants?.find(p => p.username === round.winner)?.share_pct || 0
        }));
        setHistory(historyData);
      })
      .catch(() => {});
  }, []);
  
  // Fetch server state
  const fetchServerState = useCallback(async () => {
    try {
      const res = await fetch('/jackpot/state', { credentials: 'include' });
      const data = await res.json();
      
      console.log('[Jackpot] Raw /jackpot/state response:', data);
      
      let status = data.state || 'open';
      let pot = 0, participants = [], endsAt = null, winner = null, roundId = null;
      let serverSeedHash = null, serverSeed = null, clientSeed = null, ticket = null;
      
      if (data.current_round) {
        const round = data.current_round;
        pot = round.total || 0;
        participants = (round.participants || []).map(p => ({
          username: p.username,
          amount: p.amount || 0
        }));
        endsAt = round.ends_at;
        roundId = round.id;
        serverSeedHash = round.server_seed_hash || null;
        console.log('[Jackpot] Parsed current_round:', { status, pot, endsAt, roundId });
      }
      
      if (['spinning', 'reveal', 'intermission'].includes(status) && data.last_round) {
        const round = data.last_round;
        pot = round.total || 0;
        participants = (round.participants || []).map(p => ({
          username: p.username,
          amount: p.amount || 0
        }));
        winner = data.last_winner;
        roundId = round.id;
        serverSeedHash = round.server_seed_hash || null;
        serverSeed = round.revealed_server_seed || null;
        clientSeed = round.client_seed_used || null;
        ticket = round.ticket || null;
      }
      
      console.log('[Jackpot] fetchServerState result:', { status, pot, endsAt, winner, secondsSinceResolved: data.seconds_since_resolved });
      
      const newState = {
        status,
        pot,
        participants,
        endsAt,
        winner,
        roundId,
        intermissionEndsAt: data.intermission_ends_at,
        serverSeedHash,
        serverSeed,
        clientSeed,
        ticket,
        secondsSinceResolved: data.seconds_since_resolved || null,
        isInitialLoad: true  // This came from initial fetch, not WebSocket event
      };
      
      console.log('[Jackpot] Setting gameState to:', newState);
      setGameState(newState);
    } catch (e) {
      console.error('Failed to fetch state:', e);
    }
  }, []);
  
  // Re-fetch state when tab becomes visible (handles switching browsers/tabs mid-spin)
  useEffect(() => {
    const handleVisibilityChange = () => {
      if (document.visibilityState === 'visible') {
        console.log('[Jackpot] Tab became visible, re-fetching state...');
        fetchServerState();
      }
    };
    document.addEventListener('visibilitychange', handleVisibilityChange);
    return () => document.removeEventListener('visibilitychange', handleVisibilityChange);
  }, [fetchServerState]);
  
  // WebSocket
  useEffect(() => {
    const connect = () => {
      const protocol = location.protocol === 'https:' ? 'wss:' : 'ws:';
      const ws = new WebSocket(`${protocol}//${location.host}/ws/lobby`);
      wsRef.current = ws;
      
      ws.onopen = () => {
        setIsConnected(true);
        fetchServerState();
      };
      
      ws.onclose = () => {
        setIsConnected(false);
        setTimeout(connect, CONFIG.wsReconnectDelay);
      };
      
      ws.onerror = () => {};
      
      ws.onmessage = (event) => {
        try {
          handleMessage(JSON.parse(event.data));
        } catch (e) {}
      };
    };
    
    connect();
    return () => wsRef.current?.close();
  }, [fetchServerState]);
  
  // Handle WebSocket messages
  const handleMessage = useCallback((msg) => {
    switch (msg.type) {
      case 'jp_countdown':
        setGameState(prev => ({
          ...prev,
          status: 'countdown',
          pot: msg.total || 0,
          participants: (msg.participants || []).map(p => ({
            username: p.username,
            amount: p.amount || 0
          })),
          endsAt: msg.ends_at,
          winner: null,
          roundId: msg.round_id || prev.roundId,
          isInitialLoad: false  // This is from WebSocket, not initial fetch
        }));
        break;
        
      case 'jp_deposit':
        setGameState(prev => ({
          ...prev,
          pot: msg.total || 0,
          participants: (msg.participants || []).map(p => ({
            username: p.username,
            amount: p.amount || 0
          })),
          isInitialLoad: false
        }));
        break;
        
      case 'jp_resolved':
        console.log('[Jackpot] jp_resolved received:', msg);
        const resolvedData = {
          roundId: msg.round_id,
          winner: msg.winner,
          total: msg.total,
          chance: msg.participants?.find(p => p.username === msg.winner)?.percentage || 0
        };
        
        setGameState(prev => ({
          ...prev,
          status: 'spinning',
          winner: msg.winner,
          pot: msg.total || 0,
          participants: (msg.participants || []).map(p => ({
            username: p.username,
            amount: p.amount || 0
          })),
          serverSeedHash: msg.server_seed_hash || null,
          serverSeed: msg.server_seed || null,
          clientSeed: msg.client_seed || null,
          ticket: msg.ticket || null,
          isInitialLoad: false,  // WebSocket event, not initial load
          secondsSinceResolved: 0  // Fresh spin, just started
        }));
        
        // After spin completes, transition to reveal state
        setTimeout(() => {
          console.log('[Jackpot] Spin complete, transitioning to reveal');
          setGameState(prev => ({
            ...prev,
            status: 'reveal'
          }));
          
          setHistory(prev => [{
            id: resolvedData.roundId,
            winner: resolvedData.winner,
            total: resolvedData.total,
            chance: resolvedData.chance
          }, ...prev.slice(0, 9)]);
          
          // Show pending balance animation now that spin is complete
          if (window.showPendingBalanceAnimation) {
            window.showPendingBalanceAnimation();
          }
          
          fetch('/me', { credentials: 'include' })
            .then(res => res.ok ? res.json() : null)
            .then(data => { if (data) setUser(data); })
            .catch(() => {});
        }, CONFIG.spinDuration);
        break;
        
      case 'jp_reset':
        fetchServerState();
        break;
        
      case 'balance_update':
        const currentUser = userRef.current;
        const currentGameState = gameStateRef.current;
        if (currentUser && msg.username === currentUser.username) {
          const oldBalance = currentUser.balance || 0;
          const newBalance = typeof msg.balance === 'number' ? msg.balance : oldBalance;
          const delta = newBalance - oldBalance;
          setUser(prev => prev ? { ...prev, balance: newBalance } : prev);
          // Queue animation if spinning, show immediately if revealed
          if (delta > 0 && window.updateCoinRushBalance) {
            const isSpinning = currentGameState?.status === 'spinning';
            window.updateCoinRushBalance(newBalance, delta, !isSpinning);
          }
        }
        break;
    }
  }, [fetchServerState]);
  
  // Poll during spinning/reveal
  useEffect(() => {
    if (gameState.status === 'spinning') {
      const timer = setTimeout(() => fetchServerState(), CONFIG.spinDuration + 500);
      return () => clearTimeout(timer);
    }
    if (gameState.status === 'reveal') {
      const timer = setInterval(() => fetchServerState(), 2000);
      return () => clearInterval(timer);
    }
  }, [gameState.status, fetchServerState]);
  
  // Intermission countdown - starts immediately when winner is shown
  useEffect(() => {
    if (gameState.status !== 'intermission' || !gameState.intermissionEndsAt) {
      setIntermissionCountdown(0);
      setShowCountdownUI(false);
      return;
    }
    
    // Show countdown immediately - no delay
    setShowCountdownUI(true);
    
    const updateCountdown = () => {
      const endsAt = new Date(gameState.intermissionEndsAt);
      const remaining = Math.max(0, Math.ceil((endsAt - new Date()) / 1000));
      setIntermissionCountdown(remaining);
      if (remaining <= 0) fetchServerState();
    };
    
    updateCountdown();
    const timer = setInterval(updateCountdown, 1000);
    
    return () => {
      clearInterval(timer);
    };
  }, [gameState.status, gameState.intermissionEndsAt, fetchServerState]);
  
  // Handlers
  const handleDeposit = (data) => {
    if (data?.amount) {
      const newBalance = Math.max(0, (user?.balance || 0) - data.amount);
      setUser(prev => prev ? { ...prev, balance: newBalance } : prev);
      // Show floating -X animation when betting
      if (window.updateCoinRushBalance) {
        window.updateCoinRushBalance(newBalance, -data.amount);
      }
    }
  };
  
  const handleLogout = async () => {
    try {
      await fetch('/auth/logout', { method: 'POST', credentials: 'include' });
      setUser(null);
      window.location.href = '/';
    } catch (e) {}
  };
  
  const isLocked = ['spinning', 'reveal', 'intermission'].includes(gameState.status);
  
  return (
    <div className="jackpot-app">
      {/* Floating Orbs Background */}
      <div className="jp-orbs">
        <div className="jp-orb jp-orb-1"></div>
        <div className="jp-orb jp-orb-2"></div>
        <div className="jp-orb jp-orb-3"></div>
        <div className="jp-orb jp-orb-4"></div>
        <div className="jp-orb jp-orb-5"></div>
        <div className="jp-orb jp-orb-6"></div>
      </div>
      
      {/* Animated Particles */}
      <div className="jp-particles">
        {[...Array(30)].map((_, i) => (
          <div 
            key={i} 
            className="jp-particle" 
            style={{
              '--delay': `${i * 0.2}s`,
              '--x': `${Math.random() * 100}%`,
              '--duration': `${4 + Math.random() * 5}s`,
              '--size': `${2 + Math.random() * 4}px`
            }}
          />
        ))}
      </div>
      
      {/* Navbar */}
      <CoinRushNavbar 
        user={user} 
        isConnected={isConnected} 
        activePage="jackpot"
        onLogout={handleLogout} 
      />
      
      {/* Rules Modal */}
      <RulesModal 
        isOpen={showRulesModal} 
        onClose={() => setShowRulesModal(false)} 
      />
      
      {/* Main Content */}
      <main className="app-main">
        <div className="app-layout">
          <aside className="sidebar sidebar-left">
            <BetPanel 
              user={user}
              pot={gameState.pot}
              onDeposit={handleDeposit}
              isLocked={isLocked}
              countdownSeconds={intermissionCountdown}
            />
            <HistoryPanel history={history} />
          </aside>
          
          <section className="main-content">
            <JackpotDisplay 
              pot={gameState.pot}
              participants={gameState.participants}
              status={gameState.status}
              countdown={intermissionCountdown}
              showCountdown={showCountdownUI}
              endsAt={gameState.endsAt}
              winner={gameState.winner}
              serverSeedHash={gameState.serverSeedHash}
              serverSeed={gameState.serverSeed}
              clientSeed={gameState.clientSeed}
              ticket={gameState.ticket}
            />
            
            <Wheel
              participants={gameState.participants}
              pot={gameState.pot}
              winner={gameState.winner}
              status={gameState.status}
              secondsSinceResolved={gameState.secondsSinceResolved}
              isInitialLoad={gameState.isInitialLoad}
            />
            
            {/* Participants list moved below wheel */}
            <ParticipantsList 
              participants={gameState.participants}
              pot={gameState.pot}
              currentUser={user}
            />
          </section>
          
          <aside className="sidebar sidebar-right">
            {/* Shared Chat Component */}
            {window.CoinRushChatApp && (
              <window.CoinRushChatApp currentUser={user} theme="green" />
            )}
          </aside>
        </div>
      </main>
      
      {/* Footer */}
      <Footer />
      
      {/* Auth Modal (uses shared component from auth-react.jsx with OAuth buttons) */}
      {window.AuthModal && (
        <window.AuthModal
          isOpen={showAuthModal}
          initialMode={authModalMode}
          onClose={() => setShowAuthModal(false)}
          onSuccess={() => {
            setShowAuthModal(false);
            window.location.reload();
          }}
        />
      )}
      
      {/* UX Enhancements */}
      <Confetti active={showConfetti} />
      
      <WinnerOverlay
        show={showWinnerOverlay}
        winner={winnerData?.winner}
        amount={winnerData?.amount}
        isCurrentUser={winnerData?.isCurrentUser}
        onClose={() => setShowWinnerOverlay(false)}
      />
      
      {/* Sound Toggle Button */}
      <button 
        className={`sound-toggle ${!soundEnabled ? 'muted' : ''}`}
        onClick={toggleSound}
        title={soundEnabled ? 'Mute sounds' : 'Unmute sounds'}
      >
        {soundEnabled ? (
          <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
            <polygon points="11 5 6 9 2 9 2 15 6 15 11 19 11 5"/>
            <path d="M19.07 4.93a10 10 0 0 1 0 14.14M15.54 8.46a5 5 0 0 1 0 7.07"/>
          </svg>
        ) : (
          <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
            <polygon points="11 5 6 9 2 9 2 15 6 15 11 19 11 5"/>
            <line x1="23" y1="9" x2="17" y2="15"/>
            <line x1="17" y1="9" x2="23" y2="15"/>
          </svg>
        )}
      </button>
    </div>
  );
}

// ============================================
// APP WITH TOAST PROVIDER
// ============================================
function JackpotAppWithProviders() {
  return (
    <ToastProvider>
      <JackpotApp />
    </ToastProvider>
  );
}

// ============================================
// RENDER
// ============================================
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<JackpotAppWithProviders />);
