/**
 * CoinRush Coinflip - React Redesign
 * Modern UI matching jackpot and other game modes
 * Clean, premium design with consistent styling
 */

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

// ============================================
// CONFIGURATION
// ============================================
const CONFIG = {
  minBet: 100, // 1.00 coins in hundredths
  maxBet: 10000000,
  flipDuration: 3000, // 3 seconds for clean animation
  wsReconnectDelay: 3000,
  battleTimeout: 300 // 5 minutes in seconds
};

// ============================================
// SVG ICONS
// ============================================
const Icons = {
  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>
  ),
  swords: (
    <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
      <polyline points="14.5 17.5 3 6 3 3 6 3 17.5 14.5"/>
      <line x1="13" y1="19" x2="19" y2="13"/>
      <line x1="16" y1="16" x2="20" y2="20"/>
      <line x1="19" y1="21" x2="21" y2="19"/>
      <polyline points="14.5 6.5 18 3 21 3 21 6 17.5 9.5"/>
      <line x1="5" y1="14" x2="9" y2="18"/>
      <line x1="7" y1="17" x2="4" y2="20"/>
      <line x1="3" y1="19" x2="5" y2="21"/>
    </svg>
  ),
  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>
  ),
  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>
  ),
  plus: (
    <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
      <line x1="12" y1="5" x2="12" y2="19"/>
      <line x1="5" y1="12" x2="19" y2="12"/>
    </svg>
  ),
  play: (
    <svg viewBox="0 0 24 24" fill="currentColor">
      <polygon points="5 3 19 12 5 21 5 3"/>
    </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>
  ),
  crown: (
    <svg viewBox="0 0 24 24" fill="currentColor">
      <path d="M5 16L3 5l5.5 5L12 4l3.5 6L21 5l-2 11H5zm14 3H5v2h14v-2z"/>
    </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>
  ),
  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>
  ),
  bolt: (
    <svg viewBox="0 0 24 24" fill="currentColor">
      <polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2"/>
    </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>
  ),
  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>
  ),
  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>
  ),
  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>
  ),
  chevronDown: (
    <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
      <polyline points="6 9 12 15 18 9"/>
    </svg>
  ),
  skull: (
    <svg viewBox="0 0 24 24" fill="currentColor">
      <path d="M12 2C6.48 2 2 6.48 2 12c0 3.03 1.35 5.74 3.5 7.56V22h3v-2h3v2h3v-2h3v-2.44C20.65 17.74 22 15.03 22 12c0-5.52-4.48-10-10-10zm-3 12c-1.1 0-2-.9-2-2s.9-2 2-2 2 .9 2 2-.9 2-2 2zm6 0c-1.1 0-2-.9-2-2s.9-2 2-2 2 .9 2 2-.9 2-2 2z"/>
    </svg>
  ),
  sparkles: (
    <svg viewBox="0 0 24 24" fill="currentColor">
      <path d="M9.5 9.5L6 11l3.5 1.5L11 16l1.5-3.5L16 11l-3.5-1.5L11 6l-1.5 3.5zM17 3l-1 2-2 1 2 1 1 2 1-2 2-1-2-1-1-2zM6 18l-1 2-2 1 2 1 1 2 1-2 2-1-2-1-1-2z"/>
    </svg>
  )
};

// ============================================
// UTILITIES
// ============================================
const formatCoins = (cents) => {
  const coins = (cents || 0) / 100;
  return coins.toLocaleString('da-DK', { minimumFractionDigits: 2, maximumFractionDigits: 2 });
};

const parseCoinsInput = (input) => {
  if (typeof input === 'number') return Math.round(input * 100);
  const normalized = String(input).trim().replace(/\./g, '').replace(',', '.');
  const parsed = parseFloat(normalized);
  if (!isFinite(parsed)) return 0;
  return Math.round(parsed * 100);
};

const timeAgo = (dateStr) => {
  const date = new Date(dateStr);
  const now = new Date();
  const seconds = Math.floor((now - date) / 1000);
  if (seconds < 60) return 'Just now';
  if (seconds < 3600) return `${Math.floor(seconds / 60)}m ago`;
  if (seconds < 86400) return `${Math.floor(seconds / 3600)}h ago`;
  return `${Math.floor(seconds / 86400)}d ago`;
};

const formatTime = (seconds) => {
  const mins = Math.floor(seconds / 60);
  const secs = Math.floor(seconds % 60);
  return `${mins}:${secs.toString().padStart(2, '0')}`;
};

const clsx = (...classes) => classes.filter(Boolean).join(' ');

// ============================================
// THREE.JS 3D COIN COMPONENT - CINEMA QUALITY EDITION
// Best-in-class coinflip animation with physics simulation
// ============================================
function ThreeCoin({ phase, winnerSide, onAnimationComplete, size = 280 }) {
  const containerRef = useRef(null);
  const rafRef = useRef(null);
  const apiRef = useRef(null);

  useEffect(() => {
    if (!containerRef.current || !window.THREE || !window.gsap) return;
    const root = containerRef.current;
    const THREE = window.THREE;
    const gsap = window.gsap;
    
    // ===== SCENE SETUP =====
    const scene = new THREE.Scene();
    scene.background = null;
    
    // Camera - more overhead angle to see coin face clearly
    const camera = new THREE.PerspectiveCamera(32, 1, 0.1, 100);
    camera.position.set(0, 6.5, 5.0); // Higher up, more overhead view
    camera.lookAt(0, 1.0, 0); // Look at mid-point of flip arc
    
    const renderer = new THREE.WebGLRenderer({ 
      antialias: true, 
      alpha: true,
      powerPreference: 'high-performance'
    });
    renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
    renderer.setSize(size, size);
    if (THREE.SRGBColorSpace) renderer.outputColorSpace = THREE.SRGBColorSpace;
    if (THREE.ACESFilmicToneMapping) {
      renderer.toneMapping = THREE.ACESFilmicToneMapping;
      renderer.toneMappingExposure = 1.5;
    }
    root.appendChild(renderer.domElement);

    // ===== PREMIUM LIGHTING =====
    scene.add(new THREE.AmbientLight(0xffffff, 0.5));
    
    // Key light - warm gold
    const keyLight = new THREE.DirectionalLight(0xfff0d4, 2.2);
    keyLight.position.set(5, 8, 4);
    scene.add(keyLight);
    
    // Fill light - cool accent
    const fillLight = new THREE.DirectionalLight(0x5bffb2, 0.6);
    fillLight.position.set(-5, 3, -3);
    scene.add(fillLight);
    
    // Rim light - dramatic edge highlight
    const rimLight = new THREE.DirectionalLight(0xffffff, 1.5);
    rimLight.position.set(0, -4, 6);
    scene.add(rimLight);
    
    // Top spotlight
    const spotLight = new THREE.SpotLight(0xffffff, 1.2, 15, Math.PI / 6, 0.5);
    spotLight.position.set(0, 10, 0);
    scene.add(spotLight);

    // ===== CLEAN MODERN COIN DESIGN =====
    const createCoinFace = (text, isRed) => {
      const size = 1024;
      const canvas = document.createElement('canvas');
      canvas.width = canvas.height = size;
      const ctx = canvas.getContext('2d');
      const cx = size / 2;
      const cy = size / 2;
      const r = size * 0.48; // Use full area
      
      // === FLAT COLOR WITH SUBTLE SHINE ===
      const baseColor = isRed ? '#dc2626' : '#22c55e';
      
      // === MAIN COLORED CIRCLE ===
      ctx.beginPath();
      ctx.arc(cx, cy, r, 0, Math.PI * 2);
      ctx.fillStyle = baseColor;
      ctx.fill();
      
      // === SUBTLE TOP SHINE ===
      const shineGrad = ctx.createRadialGradient(
        cx, cy - r * 0.35, 0,
        cx, cy - r * 0.2, r * 0.5
      );
      shineGrad.addColorStop(0, 'rgba(255,255,255,0.18)');
      shineGrad.addColorStop(1, 'rgba(255,255,255,0)');
      ctx.beginPath();
      ctx.arc(cx, cy, r, 0, Math.PI * 2);
      ctx.fillStyle = shineGrad;
      ctx.fill();
      
      // === WHITE LETTER - centered ===
      ctx.textAlign = 'center';
      ctx.textBaseline = 'middle';
      
      const letter = isRed ? 'R' : 'G';
      const fontSize = r * 1.1;
      ctx.font = `700 ${fontSize}px "Inter", -apple-system, sans-serif`;
      ctx.fillStyle = '#ffffff';
      ctx.fillText(letter, cx, cy + fontSize * 0.02);
      
      const texture = new THREE.CanvasTexture(canvas);
      texture.colorSpace = THREE.SRGBColorSpace;
      texture.anisotropy = 8;
      return texture;
    };
    
    const createEdgeTexture = () => {
      const canvas = document.createElement('canvas');
      canvas.width = 512;
      canvas.height = 64;
      const ctx = canvas.getContext('2d');
      
      // Simple dark edge - matches the flat style
      ctx.fillStyle = '#1f2937';
      ctx.fillRect(0, 0, canvas.width, canvas.height);
      
      const tex = new THREE.CanvasTexture(canvas);
      tex.wrapS = THREE.RepeatWrapping;
      tex.repeat.set(20, 1);
      return tex;
    };

    // ===== BUILD COIN =====
    const redTex = createCoinFace('CR', true);
    const greenTex = createCoinFace('CR', false);
    const edgeTex = createEdgeTexture();
    
    const edgeMat = new THREE.MeshStandardMaterial({ 
      map: edgeTex, 
      metalness: 0.85,
      roughness: 0.25
    });
    const redMat = new THREE.MeshStandardMaterial({ 
      map: redTex,
      metalness: 0.4,
      roughness: 0.35
    });
    const greenMat = new THREE.MeshStandardMaterial({ 
      map: greenTex,
      metalness: 0.4,
      roughness: 0.35
    });
    
    // Coin geometry
    const coinGeo = new THREE.CylinderGeometry(1, 1, 0.12, 72, 1, false);
    const coin = new THREE.Mesh(coinGeo, [edgeMat, redMat, greenMat]);
    scene.add(coin);

    // ===== GLOW RING =====
    const glowGeo = new THREE.RingGeometry(1.1, 1.4, 64);
    const glowMat = new THREE.MeshBasicMaterial({
      color: 0x5bffb2,
      transparent: true,
      opacity: 0,
      side: THREE.DoubleSide
    });
    const glowRing = new THREE.Mesh(glowGeo, glowMat);
    glowRing.rotation.x = Math.PI / 2;
    glowRing.position.y = -0.8;
    scene.add(glowRing);

    // ===== SHADOW =====
    const shadowCanvas = document.createElement('canvas');
    shadowCanvas.width = shadowCanvas.height = 256;
    const sCtx = shadowCanvas.getContext('2d');
    const sGrad = sCtx.createRadialGradient(128, 128, 0, 128, 128, 128);
    sGrad.addColorStop(0, 'rgba(0,0,0,0.6)');
    sGrad.addColorStop(0.5, 'rgba(0,0,0,0.3)');
    sGrad.addColorStop(1, 'rgba(0,0,0,0)');
    sCtx.fillStyle = sGrad;
    sCtx.fillRect(0, 0, 256, 256);
    const shadowTex = new THREE.CanvasTexture(shadowCanvas);
    const shadow = new THREE.Sprite(new THREE.SpriteMaterial({ 
      map: shadowTex, 
      transparent: true, 
      opacity: 0.7 
    }));
    shadow.scale.set(3, 3, 1);
    shadow.position.set(0, -0.85, 0);
    scene.add(shadow);

    // ===== AURA EFFECT =====
    const auraCanvas = document.createElement('canvas');
    auraCanvas.width = auraCanvas.height = 512;
    const aCtx = auraCanvas.getContext('2d');
    const aGrad = aCtx.createRadialGradient(256, 256, 0, 256, 256, 256);
    aGrad.addColorStop(0, 'rgba(255,255,255,1)');
    aGrad.addColorStop(0.3, 'rgba(255,255,255,0.6)');
    aGrad.addColorStop(1, 'rgba(255,255,255,0)');
    aCtx.fillStyle = aGrad;
    aCtx.fillRect(0, 0, 512, 512);
    const auraTex = new THREE.CanvasTexture(auraCanvas);
    const auraMat = new THREE.SpriteMaterial({ map: auraTex, transparent: true, opacity: 0 });
    const aura = new THREE.Sprite(auraMat);
    aura.scale.set(4, 4, 1);
    aura.position.set(0, -0.3, 0);
    scene.add(aura);
    
    // Winner celebration - disabled (no more pulse effect)
    const celebrate = (side) => {
      // No visual effects - clean landing
    };

    // ===== RENDER LOOP =====
    let time = 0;
    const render = () => {
      rafRef.current = requestAnimationFrame(render);
      time += 0.016;
      rimLight.intensity = 1.5 + Math.sin(time * 3) * 0.3;
      renderer.render(scene, camera);
    };
    render();

    // ===== ANIMATION API =====
    const TAU = Math.PI * 2;
    let finished = false;
    const restY = -0.3;
    
    const api = {
      reset() {
        finished = false;
        gsap.killTweensOf([coin.position, coin.rotation, shadow.scale, shadow.material, glowRing.material]);
        coin.position.set(0, restY, 0);
        coin.rotation.set(0, 0, 0);
        shadow.scale.set(3, 3, 1);
        shadow.material.opacity = 0.7;
        aura.material.opacity = 0;
        glowRing.material.opacity = 0;
        glowRing.scale.set(1, 1, 1);
      },
      
      idle() {
        this.reset();
        // Smooth floating with gentle breathing motion
        gsap.to(coin.position, { 
          y: restY + 0.25, 
          duration: 3, 
          yoyo: true, 
          repeat: -1, 
          ease: 'sine.inOut' 
        });
        // Slow elegant rotation
        gsap.to(coin.rotation, { 
          y: TAU, 
          duration: 10, 
          repeat: -1, 
          ease: 'none' 
        });
        // Subtle tilt for depth
        gsap.to(coin.rotation, { 
          x: 0.4, 
          duration: 3.5, 
          yoyo: true, 
          repeat: -1, 
          ease: 'sine.inOut' 
        });
        // Very subtle wobble
        gsap.to(coin.rotation, { 
          z: 0.06, 
          duration: 4.5, 
          yoyo: true, 
          repeat: -1, 
          ease: 'sine.inOut' 
        });
        // Shadow breathes with coin
        gsap.to(shadow.scale, { 
          x: 3.3, 
          y: 3.3, 
          duration: 3, 
          yoyo: true, 
          repeat: -1, 
          ease: 'sine.inOut' 
        });
      },
      
      // SMOOTH COINFLIP ANIMATION - Clean single arc
      toss(side) {
        gsap.killTweensOf([coin.position, coin.rotation, shadow.scale, shadow.material]);
        finished = false;
        
        // ROTATION LOGIC:
        // Cylinder: rotation.x = 0 shows RED (top), rotation.x = π shows GREEN (bottom)
        const landingRotation = side === 'green' ? Math.PI : 0;
        
        // Simple, clean animation parameters
        const peakY = 2.0;        // Contained peak height
        const totalDuration = 1.4; // Total flip time
        
        // Spin calculations - land on correct side
        const fullSpins = 5; // Fixed number of spins for consistency
        const finalX = (fullSpins * TAU) + landingRotation;
        
        const tl = gsap.timeline();
        
        // ─── SINGLE SMOOTH ARC - Up and down ───
        // Position: smooth parabolic arc using power easing
        tl.to(coin.position, { 
          y: peakY, 
          duration: totalDuration * 0.45, 
          ease: 'power2.out'
        }, 0);
        tl.to(coin.position, { 
          y: restY, 
          duration: totalDuration * 0.45, 
          ease: 'power2.in'
        }, totalDuration * 0.45);
        
        // Rotation: continuous smooth spin
        tl.to(coin.rotation, { 
          x: finalX,
          duration: totalDuration * 0.9,
          ease: 'power1.inOut'
        }, 0);
        
        // Shadow follows coin height
        tl.to(shadow.scale, { x: 1, y: 1, duration: totalDuration * 0.45, ease: 'power2.out' }, 0);
        tl.to(shadow.material, { opacity: 0.2, duration: totalDuration * 0.45 }, 0);
        tl.to(shadow.scale, { x: 3, y: 3, duration: totalDuration * 0.45, ease: 'power2.in' }, totalDuration * 0.45);
        tl.to(shadow.material, { opacity: 0.7, duration: totalDuration * 0.45 }, totalDuration * 0.45);
        
        // ─── SMALL BOUNCE ON LANDING ───
        const bounceStart = totalDuration * 0.9;
        tl.to(coin.position, { y: restY + 0.15, duration: 0.1, ease: 'power2.out' }, bounceStart);
        tl.to(coin.position, { y: restY, duration: 0.1, ease: 'power2.in' }, bounceStart + 0.1);
        
        // ─── SETTLE ───
        const settleTime = bounceStart + 0.25;
        tl.add(() => {
          coin.rotation.x = landingRotation;
          coin.rotation.y = 0;
          coin.rotation.z = 0;
          coin.position.y = restY;
          shadow.scale.set(3, 3, 1);
          shadow.material.opacity = 0.7;
          
          if (!finished && side) {
            finished = true;
            onAnimationComplete?.();
          }
        }, settleTime);
      },
      
      snapTo(side) {
        if (finished) return;
        gsap.killTweensOf([coin.position, coin.rotation]);
        const targetX = side === 'green' ? Math.PI : 0;
        gsap.to(coin.rotation, {
          x: targetX,
          y: 0,
          z: 0,
          duration: 0.3,
          ease: 'power2.out',
          onComplete: () => {
            if (!finished) {
              finished = true;
              onAnimationComplete?.();
            }
          }
        });
        coin.position.y = restY;
      }
    };
    
    apiRef.current = api;
    api.idle();

    // ===== CLEANUP =====
    return () => {
      cancelAnimationFrame(rafRef.current);
      gsap.killTweensOf([coin.position, coin.rotation, shadow.scale, shadow.material, aura.material, aura.scale, glowRing.material, glowRing.scale]);
      try { root.removeChild(renderer.domElement); } catch {}
      coinGeo.dispose();
      glowGeo.dispose();
      glowMat.dispose();
      edgeMat.dispose();
      redMat.dispose();
      greenMat.dispose();
      redTex.dispose();
      greenTex.dispose();
      edgeTex.dispose();
      renderer.dispose();
    };
  }, [size]);

  // ===== PHASE-DRIVEN ANIMATION =====
  useEffect(() => {
    const api = apiRef.current;
    if (!api) return;
    
    if (phase === 'waiting' || phase === 'ready' || phase === 'countdown') {
      api.idle();
    } else if (phase === 'flipping') {
      api.toss(winnerSide);
    }
    // Note: 'reveal' phase no longer calls snapTo since toss() already lands on correct side
  }, [phase, winnerSide]);

  return (
    <div
      ref={containerRef}
      style={{
        width: size,
        height: size,
        margin: '0 auto',
        position: 'relative',
        zIndex: 20,
        pointerEvents: 'none'
      }}
    />
  );
}

// API helper
const api = async (path, { method = 'GET', body } = {}) => {
  const res = await fetch(path, {
    method,
    headers: { 'Content-Type': 'application/json' },
    credentials: 'include',
    body: body ? JSON.stringify(body) : undefined
  });
  if (!res.ok) throw new Error(await res.text().catch(() => res.statusText));
  const ct = res.headers.get('content-type') || '';
  return ct.includes('application/json') ? res.json() : res.text();
};

// ============================================
// AVATAR COMPONENT - Uses shared auth styling with PNG borders
// ============================================
function Avatar({ username, size = 'md', showBorder = true, level = 1, showLevel = false }) {
  // Size classes for cf-avatar base styles
  const sizeClasses = {
    xs: 'cf-avatar--xs',
    sm: 'cf-avatar--sm',
    md: 'cf-avatar--md',
    lg: 'cf-avatar--lg',
    xl: 'cf-avatar--xl'
  };

  // Border frame sizes (approximately 1.8x the avatar size)
  const frameSizes = {
    xs: 44,
    sm: 72,
    md: 86,
    lg: 100,
    xl: 120
  };

  // Use colorFrom from shared-auth.js if available, fallback to hue calculation
  const getBackground = () => {
    if (typeof window.colorFrom === 'function') {
      return window.colorFrom(username || 'X');
    }
    const hue = [...(username || 'X')].reduce((a, c) => a + c.charCodeAt(0), 0) % 360;
    return `linear-gradient(135deg, hsl(${hue} 70% 50%), hsl(${hue} 70% 35%))`;
  };

  // Get border frame image based on level
  const getBorderFrame = () => {
    if (level >= 40) return '/static/img/borders/Challengerborder.png'; // Mythic & Legendary
    if (level >= 30) return '/static/img/borders/Diamondborder.png';
    if (level >= 20) return '/static/img/borders/Rubyborder.png'; // Gold Elite & Platinum
    if (level >= 15) return '/static/img/borders/Goldborder.png';
    if (level >= 10) return '/static/img/borders/Silverborder.png';
    if (level >= 5) return '/static/img/borders/Bronzeborder.png';
    return null; // Rookie - no frame
  };

  // Use getAvatarBorderClass from shared-auth.js if available
  const getBorderClass = () => {
    if (typeof window.getAvatarBorderClass === 'function') {
      return window.getAvatarBorderClass(level);
    }
    return '';
  };

  const borderClass = showBorder ? getBorderClass() : '';
  const frameUrl = showBorder ? getBorderFrame() : null;
  const frameSize = frameSizes[size] || 86;

  return (
    <div
      className={clsx(
        'cf-avatar',
        'player-avatar',
        sizeClasses[size],
        borderClass
      )}
      style={{ background: getBackground() }}
    >
      <span className="cf-avatar__initials">
        {(username || 'X').slice(0, 2).toUpperCase()}
      </span>
      {showLevel && level > 0 && (
        <span className="cf-avatar__level">{level}</span>
      )}
      {frameUrl && (
        <img
          src={frameUrl}
          alt=""
          className="cf-avatar__frame"
          style={{
            position: 'absolute',
            top: '50%',
            left: '50%',
            transform: 'translate(-50%, -50%)',
            width: frameSize,
            height: frameSize,
            pointerEvents: 'none',
            zIndex: 10
          }}
        />
      )}
    </div>
  );
}

// ============================================
// SIDE BADGE COMPONENT
// ============================================
function SideBadge({ side, size = 'md' }) {
  const isRed = side === 'red';
  const sizes = {
    sm: 'px-2 py-0.5 text-xs',
    md: 'px-3 py-1 text-sm',
    lg: 'px-4 py-1.5 text-base'
  };

  return (
    <span className={clsx(
      sizes[size],
      'font-bold uppercase rounded-lg border-2',
      isRed
        ? 'bg-red-500/20 border-red-500/50 text-red-400'
        : 'bg-green-500/20 border-green-500/50 text-green-400'
    )}>
      {side}
    </span>
  );
}

// ============================================
// BATTLE CARD COMPONENT - PREMIUM REDESIGN
// ============================================
function BattleCard({ battle, user, onJoin, onView, isJoining }) {
  const [timeLeft, setTimeLeft] = useState(CONFIG.battleTimeout);
  const isOwn = user && battle.creator === user.username;
  const canJoin = user && battle.status === 'open' && !isOwn;
  const isResolved = battle.status === 'resolved';
  const isCountdown = battle.status === 'countdown';
  const creatorSide = battle.creator_side;
  const joinerSide = creatorSide === 'red' ? 'green' : 'red';

  useEffect(() => {
    if (isResolved) return;

    // Parse created_at - handle both ISO with 'Z' and without
    let createdAtStr = battle.created_at;
    if (createdAtStr && !createdAtStr.endsWith('Z') && !createdAtStr.includes('+')) {
      createdAtStr += 'Z'; // Assume UTC if no timezone specified
    }
    const createdAt = new Date(createdAtStr).getTime();
    if (!Number.isFinite(createdAt)) return;

    const updateTimer = () => {
      const elapsed = (Date.now() - createdAt) / 1000;
      const remaining = Math.max(0, CONFIG.battleTimeout - elapsed);
      setTimeLeft(remaining);
    };

    updateTimer();
    const interval = setInterval(updateTimer, 1000);
    return () => clearInterval(interval);
  }, [battle.created_at, isResolved]);

  const isUrgent = timeLeft < 60;
  const isWarning = timeLeft < 120 && timeLeft >= 60;

  // Determine winner/loser for resolved battles
  const winnerIsCreator = isResolved && battle.winner === battle.creator;

  return (
    <div className={clsx(
      'bc',
      isResolved && 'bc--resolved',
      isCountdown && 'bc--countdown',
      isOwn && 'bc--own'
    )}>
      {/* Compact row layout */}
      <div className="bc__row">
        {/* Creator */}
        <div className={clsx('bc__player', isResolved && winnerIsCreator && 'bc__player--winner')}>
          <div className="bc__avatar-wrap">
            <Avatar username={battle.creator} size="sm" level={battle.creator_level || 1} showLevel={true} />
            <span className={clsx('bc__side', `bc__side--${creatorSide}`)}>
              <span className={`bc__side-dot bc__side-dot--${creatorSide}`}></span>
            </span>
          </div>
          <div className="bc__info">
            <span className="bc__name">{battle.creator}</span>
            <span className="bc__bet">{formatCoins(battle.amount)}</span>
          </div>
          {isResolved && winnerIsCreator && <span className="bc__crown">{Icons.crown}</span>}
        </div>

        {/* VS */}
        <div className="bc__vs">VS</div>

        {/* Joiner or waiting */}
        <div className={clsx('bc__player bc__player--right', isResolved && !winnerIsCreator && 'bc__player--winner')}>
          {battle.joiner ? (
            <>
              {isResolved && !winnerIsCreator && <span className="bc__crown">{Icons.crown}</span>}
              <div className="bc__info bc__info--right">
                <span className="bc__name">{battle.joiner}</span>
                <span className="bc__bet">{formatCoins(battle.join_amount || battle.amount)}</span>
              </div>
              <div className="bc__avatar-wrap">
                <Avatar username={battle.joiner} size="sm" level={battle.joiner_level || 1} showLevel={true} />
                <span className={clsx('bc__side', `bc__side--${joinerSide}`)}>
                  <span className={`bc__side-dot bc__side-dot--${joinerSide}`}></span>
                </span>
              </div>
            </>
          ) : (
            <div className="bc__waiting">
              <span className="bc__waiting-icon">?</span>
              <span className="bc__waiting-text">Waiting...</span>
            </div>
          )}
        </div>
      </div>

      {/* Footer with bet amount, timer, actions */}
      <div className="bc__footer">
        <div className="bc__pot">
          <img src="/static/coinrushlogos.png" alt="" className="bc__coin" />
          <span>{formatCoins(battle.amount)}</span>
        </div>

        {/* Deviation badge */}
        {battle.join_tolerance_pct > 0 && (
          <div className="bc__deviation" title={`Bet deviation allowed: ±${battle.join_tolerance_pct}%`}>
            ±{battle.join_tolerance_pct}%
          </div>
        )}

        {/* Status badge with timer urgency */}
        <div className={clsx(
          'bc__status',
          isResolved ? 'bc__status--resolved' : isCountdown ? 'bc__status--countdown' : 'bc__status--open',
          !isResolved && !isCountdown && isUrgent && 'bc__status--urgent',
          !isResolved && !isCountdown && isWarning && 'bc__status--warning'
        )}>
          {isResolved ? (
            <>{battle.winner_side?.toUpperCase()} WINS</>
          ) : isCountdown ? (
            <>FLIPPING</>
          ) : (
            <><span className="bc__timer-icon">{Icons.clock}</span> {formatTime(timeLeft)}</>
          )}
        </div>

        <div className="bc__actions">
          <button className="bc__btn bc__btn--view" onClick={() => onView(battle)}>
            <span className="bc__btn-icon">{isResolved ? Icons.refresh : Icons.eye}</span>
          </button>
          {canJoin && (
            <button className="bc__btn bc__btn--join" onClick={() => onJoin(battle)} disabled={isJoining}>
              {isJoining ? '...' : 'Join'}
            </button>
          )}
        </div>
      </div>
    </div>
  );
}

// ============================================
// CREATE BATTLE PANEL
// ============================================
function CreateBattlePanel({ user, onRefresh }) {
  const [amount, setAmount] = useState('10,00');
  const [side, setSide] = useState('red');
  const [tolerance, setTolerance] = useState(0);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState('');

  const amountCents = parseCoinsInput(amount);
  const tolFraction = Math.max(0, tolerance) / 100;
  const minJoin = Math.max(100, Math.floor(amountCents * (1 - tolFraction)));
  const maxJoin = Math.max(minJoin, Math.ceil(amountCents * (1 + tolFraction)));

  const handleCreate = async (isFree = false) => {
    if (!user) {
      window.openAuthModal?.('login');
      return;
    }

    if (amountCents < CONFIG.minBet) {
      setError('Minimum bet is 1.00 coins');
      return;
    }

    if (amountCents > (user.balance || 0)) {
      setError('Insufficient balance');
      return;
    }

    setLoading(true);
    setError('');

    try {
      const endpoint = isFree ? '/coinflip/create-free' : '/coinflip/create';
      await api(endpoint, {
        method: 'POST',
        body: { amount: amountCents, side, tolerance_pct: tolerance }
      });
      onRefresh?.();
    } catch (err) {
      setError(err.message || 'Failed to create battle');
    } finally {
      setLoading(false);
    }
  };

  const [customTolerance, setCustomTolerance] = useState('');

  const presets = ['5,00', '10,00', '25,00', '50,00', '100,00', '250,00'];

  const handleCustomTolerance = (val) => {
    const num = parseInt(val, 10);
    setCustomTolerance(val);
    if (!isNaN(num) && num >= 0 && num <= 100) {
      setTolerance(num);
    }
  };

  return (
    <div className="create-panel">
      {/* Header */}
      <div className="create-panel__header">
        <div className="create-panel__title">
          <span className="create-panel__icon">{Icons.bolt}</span>
          Create Battle
        </div>
      </div>

      {/* Content */}
      <div className="create-panel__content">
        {/* Side Selection */}
        <div className="create-panel__sides">
          <button
            type="button"
            className={clsx('side-selector side-selector--red', side === 'red' && 'side-selector--active')}
            onClick={() => setSide('red')}
          >
            <div className="side-selector__coin">R</div>
            <div className="side-selector__label">
              <span className="side-selector__name">RED</span>
              <span className="side-selector__subtitle">Ruby Side</span>
            </div>
          </button>
          <div className="side-divider">
            <span className="side-divider__text">VS</span>
          </div>
          <button
            type="button"
            className={clsx('side-selector side-selector--green', side === 'green' && 'side-selector--active')}
            onClick={() => setSide('green')}
          >
            <div className="side-selector__coin">G</div>
            <div className="side-selector__label">
              <span className="side-selector__name">GREEN</span>
              <span className="side-selector__subtitle">Emerald Side</span>
            </div>
          </button>
        </div>

        {/* Amount Row */}
        <div className="create-panel__amount-row">
          <div className="amount-input">
            <input
              type="text"
              value={amount}
              onChange={(e) => setAmount(e.target.value)}
              placeholder="10,00"
            />
            <img src="/static/coinrushlogos.png" alt="" />
          </div>
          <div className="amount-presets">
            {['5', '10', '25', '50', '100', '250', '500'].map(p => (
              <button key={p} onClick={() => setAmount(p + ',00')} className="preset-btn">{p}</button>
            ))}
            {user?.balance > 0 && (
              <button onClick={() => setAmount(formatCoins(user.balance))} className="preset-btn preset-btn--max">MAX</button>
            )}
          </div>
        </div>

        {/* Tolerance - With Custom Input */}
        <div className="create-panel__tolerance">
          <span className="tolerance-label">Deviation</span>
          <div className="tolerance-chips">
            {[0, 5, 10, 25].map(t => (
              <button
                key={t}
                onClick={() => { setTolerance(t); setCustomTolerance(''); }}
                className={clsx('tol-chip', tolerance === t && !customTolerance && 'tol-chip--active')}
              >
                ±{t}%
              </button>
            ))}
            <div className="tol-custom">
              <input
                type="number"
                min="0"
                max="100"
                placeholder="Custom"
                value={customTolerance}
                onChange={(e) => handleCustomTolerance(e.target.value)}
                className={clsx('tol-custom__input', customTolerance && 'tol-custom__input--active')}
              />
              <span className="tol-custom__suffix">%</span>
            </div>
          </div>
        </div>

        {/* Error */}
        {error && <div className="create-panel__error">{error}</div>}

        {/* CTA */}
        <div className="create-panel__cta">
          {user ? (
            user.free_battles_available > 0 ? (
              <div className="cta-row">
                <button onClick={() => handleCreate(false)} disabled={loading} className="cta-btn cta-btn--primary">
                  {loading ? 'Creating...' : 'Create'}
                </button>
                <button onClick={() => handleCreate(true)} disabled={loading} className="cta-btn cta-btn--free">
                  <span className="cta-icon">{Icons.gift}</span> Free ({user.free_battles_available})
                </button>
              </div>
            ) : (
              <button onClick={() => handleCreate(false)} disabled={loading} className="cta-btn cta-btn--primary cta-btn--full">
                <span className="cta-icon">{Icons.bolt}</span>
                {loading ? 'Creating...' : 'Create Battle'}
              </button>
            )
          ) : (
            <button onClick={() => window.openAuthModal?.('login')} className="cta-btn cta-btn--login">
              <span className="cta-icon">{Icons.users}</span>
              Login to Play
            </button>
          )}
        </div>
      </div>
    </div>
  );
}

// ============================================
// HISTORY TICKER
// ============================================
function HistoryTicker({ history }) {
  if (!history?.length) return null;

  return (
    <div className="cf-ticker">
      <div className="cf-ticker-track">
        {history.slice(0, 12).map((h, i) => (
          <div key={i} className="cf-ticker-item">
            <span className="cf-ticker-id">#{h.id}</span>
            <span className="cf-ticker-winner">{h.winner}</span>
            <span className="cf-ticker-text">won</span>
            <span className={clsx(
              'cf-ticker-amount',
              h.winner_side === 'red' ? 'cf-ticker-amount--red' : 'cf-ticker-amount--green'
            )}>
              {formatCoins(h.amount)}
            </span>
            <span className="cf-ticker-text">on</span>
            <SideBadge side={h.winner_side} size="sm" />
          </div>
        ))}
      </div>
    </div>
  );
}

// ============================================
// LEADERBOARD PANEL
// ============================================
function LeaderboardPanel({ leaderboard }) {
  return (
    <div className="cf-panel cf-leaderboard-panel">
      <div className="cf-panel-header">
        <div className="cf-panel-title">
          <span className="cf-panel-icon">{Icons.trophy}</span>
          <span>Weekly Champions</span>
        </div>
      </div>

      <div className="cf-panel-body">
        {leaderboard.length === 0 ? (
          <div className="cf-empty-state">
            <span className="cf-empty-icon">💤</span>
            <span>No battles this week yet</span>
          </div>
        ) : (
          <div className="cf-leaderboard-list">
            {leaderboard.slice(0, 10).map((player, index) => (
              <div
                key={player.username}
                className={clsx(
                  'cf-leaderboard-item',
                  index === 0 && 'cf-leaderboard-item--gold',
                  index === 1 && 'cf-leaderboard-item--silver',
                  index === 2 && 'cf-leaderboard-item--bronze'
                )}
              >
                <div className="cf-leaderboard-rank">
                  {index < 3 ? (
                    <span className="cf-leaderboard-medal">
                      {index === 0 ? '👑' : index === 1 ? '🥈' : '🥉'}
                    </span>
                  ) : (
                    <span className="cf-leaderboard-number">#{index + 1}</span>
                  )}
                </div>
                <Avatar username={player.username} size="sm" />
                <div className="cf-leaderboard-info">
                  <span className="cf-leaderboard-name">{player.username}</span>
                  <span className="cf-leaderboard-stats">
                    {player.battles_played} battle{player.battles_played !== 1 ? 's' : ''}
                  </span>
                </div>
                <div className={clsx(
                  'cf-leaderboard-profit',
                  player.net_profit > 0 ? 'cf-leaderboard-profit--positive' : 'cf-leaderboard-profit--negative'
                )}>
                  {player.net_profit > 0 ? '+' : ''}{formatCoins(player.net_profit)}
                </div>
              </div>
            ))}
          </div>
        )}
      </div>
    </div>
  );
}

// ============================================
// DUEL OVERLAY - LANDING PAGE STYLE
// Premium dark theme with green/gold accents
// ============================================
function DuelOverlay({ duel, reveal, phase, timeLeft, user, onClose }) {
  const [localPhase, setLocalPhase] = useState(() => {
    // Initialize based on phase prop if available
    if (phase === 'countdown') return 'countdown';
    if (phase === 'flipping') return 'flipping';
    if (phase === 'reveal') return 'landed';
    return 'waiting';
  });
  const [showResult, setShowResult] = useState(false);
  const [showConfetti, setShowConfetti] = useState(false);
  const [coinLanded, setCoinLanded] = useState(false);
  const resultTimerRef = useRef(null);

  if (!duel) return null;

  // ===== COMPUTED VALUES =====
  const playerRed = duel.creator_side === 'red' ? duel.creator : duel.joiner;
  const playerGreen = duel.creator_side === 'green' ? duel.creator : duel.joiner;
  const redLevel = duel.creator_side === 'red' ? (duel.creator_level || 1) : (duel.joiner_level || 1);
  const greenLevel = duel.creator_side === 'green' ? (duel.creator_level || 1) : (duel.joiner_level || 1);
  const creatorAmt = duel.amount || 0;
  const joinAmt = duel.join_amount || creatorAmt;
  const totalPot = creatorAmt + joinAmt;
  const redAmt = duel.creator_side === 'red' ? creatorAmt : joinAmt;
  const greenAmt = duel.creator_side === 'green' ? creatorAmt : joinAmt;
  const redPct = totalPot > 0 ? (redAmt / totalPot) * 100 : 50;
  const greenPct = 100 - redPct;

  // Winner info
  const winnerSide = reveal?.winner_side;
  const winnerName = reveal?.winner;
  const winnerPrize = reveal?.winner_prize || Math.floor(totalPot * 0.98);
  const houseFee = reveal?.house_fee || Math.floor(totalPot * 0.02);
  const secondsLeft = Math.max(0, Math.ceil((timeLeft || 0) / 1000));

  // User state
  const userInGame = user && (duel.creator === user.username || duel.joiner === user.username);
  const normalizedUser = user?.username?.toString().trim().toLowerCase();
  const normalizedWinner = winnerName?.toString().trim().toLowerCase();
  const userWon = userInGame && normalizedWinner && normalizedUser === normalizedWinner;
  const userSide = user?.username === playerRed ? 'red' : (user?.username === playerGreen ? 'green' : null);

  // ===== PHASE MANAGEMENT WITH 1.5s DELAY =====
  useEffect(() => {
    console.log('[DuelOverlay Phase Effect]', { phase, localPhase, joiner: duel.joiner, duelId: duel.id });
    
    if (resultTimerRef.current) clearTimeout(resultTimerRef.current);

    if (!phase || phase === 'waiting') {
      setLocalPhase(duel.joiner ? 'ready' : 'waiting');
      setShowResult(false);
      setShowConfetti(false);
      setCoinLanded(false);
    } else if (phase === 'countdown') {
      console.log('[DuelOverlay] Setting localPhase to countdown');
      setLocalPhase('countdown');
      setShowResult(false);
      setShowConfetti(false);
      setCoinLanded(false);
    } else if (phase === 'flipping') {
      setLocalPhase('flipping');
      setShowResult(false);
      setCoinLanded(false);
    } else if (phase === 'reveal') {
      // Coin has landed, but wait 1.5 seconds before showing result
      setCoinLanded(true);
      setLocalPhase('landed'); // New phase: coin landed but result not shown yet
      
      resultTimerRef.current = setTimeout(() => {
        setLocalPhase('result');
        setShowResult(true);
        if (userWon || !userInGame) setShowConfetti(true);
      }, 1500); // 1.5 second delay
    }

    return () => { if (resultTimerRef.current) clearTimeout(resultTimerRef.current); };
  }, [phase, duel.joiner, userWon, userInGame]);

  // Reset state when duel changes, but respect incoming phase
  useEffect(() => {
    setShowResult(false);
    setShowConfetti(false);
    setCoinLanded(false);
    // Don't reset localPhase here - let the phase management effect handle it
  }, [duel.id]);

  // Floating particles (like landing page)
  const particles = useMemo(() => 
    [...Array(20)].map((_, i) => ({
      id: i,
      x: Math.random() * 100,
      y: Math.random() * 100,
      size: 2 + Math.random() * 2,
      duration: 4 + Math.random() * 4,
      delay: Math.random() * 2
    })), [duel.id]
  );

  // Confetti for winners
  const confetti = useMemo(() => 
    [...Array(80)].map((_, i) => ({
      id: i,
      x: Math.random() * 100,
      delay: Math.random() * 0.5,
      duration: 2 + Math.random() * 2,
      color: ['#ffd700', '#5bffb2', '#ff6b6b', '#3b82f6', '#fbbf24'][i % 5],
      size: 6 + Math.random() * 8
    })), [duel.id]
  );

  // ===== RENDER =====
  return (
    <div className="battle-popup" onClick={onClose}>
      {/* Dark backdrop */}
      <div className="battle-popup__backdrop" />

      {/* Confetti */}
      {showConfetti && (
        <div className="battle-popup__confetti">
          {confetti.map(c => (
            <div key={c.id} className="battle-popup__confetti-piece" style={{
              left: `${c.x}%`,
              backgroundColor: c.color,
              width: c.size,
              height: c.size * 0.5,
              animationDelay: `${c.delay}s`,
              animationDuration: `${c.duration}s`
            }} />
          ))}
        </div>
      )}

      {/* Modal Card */}
      <div className={`battle-popup__card battle-popup__card--${localPhase}`} onClick={e => e.stopPropagation()}>
        
        {/* Close button */}
        <button className="battle-popup__close" onClick={onClose}>
          <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" width="18" height="18">
            <path d="M18 6L6 18M6 6l12 12"/>
          </svg>
        </button>

        {/* Header */}
        <div className="battle-popup__header">
          <span className="battle-popup__id">Battle #{duel.id}</span>
          <div className="battle-popup__pot">
            <img src="/static/coinrushlogos.png" alt="" />
            <span>{formatCoins(winnerPrize)}</span>
          </div>
        </div>

        {/* Players Row */}
        <div className="battle-popup__players">
          
          {/* Red Player */}
          <div className={`battle-popup__player battle-popup__player--red ${
            localPhase === 'result' ? (winnerSide === 'red' ? 'battle-popup__player--winner' : 'battle-popup__player--loser') : ''
          }`}>
            <div className="battle-popup__avatar">
              <Avatar username={playerRed || '?'} size="lg" level={redLevel} showLevel={true} />
            </div>
            <div className="battle-popup__info">
              <span className="battle-popup__name">
                {playerRed || 'Waiting...'}
                {userSide === 'red' && <span className="battle-popup__you">YOU</span>}
              </span>
              <span className="battle-popup__bet">{formatCoins(redAmt)}</span>
            </div>
            <span className="battle-popup__odds">{redPct.toFixed(0)}%</span>
            {localPhase === 'result' && winnerSide === 'red' && (
              <div className="battle-popup__crown">
                <svg viewBox="0 0 24 24" fill="currentColor" width="24" height="24">
                  <path d="M5 16L3 5l5.5 5L12 4l3.5 6L21 5l-2 11H5zm14 3c0 .6-.4 1-1 1H6c-.6 0-1-.4-1-1v-1h14v1z"/>
                </svg>
              </div>
            )}
          </div>

          {/* VS / Coin Center */}
          <div className="battle-popup__center">
            {/* Status indicator */}
            {localPhase === 'waiting' && (
              <div className="battle-popup__status battle-popup__status--waiting">
                <div className="battle-popup__spinner"></div>
                <span>Waiting...</span>
              </div>
            )}
            {localPhase === 'ready' && (
              <div className="battle-popup__status battle-popup__status--ready">
                <svg viewBox="0 0 24 24" fill="currentColor" width="16" height="16">
                  <path d="M13 10V3L4 14h7v7l9-11h-7z"/>
                </svg>
                <span>Ready!</span>
              </div>
            )}
            {localPhase === 'countdown' && (
              <div className="battle-popup__countdown" key={secondsLeft}>
                <span className="battle-popup__countdown-num">{secondsLeft}</span>
              </div>
            )}
            {localPhase === 'flipping' && (
              <div className="battle-popup__status battle-popup__status--flipping">
                <div className="battle-popup__dots"><span></span><span></span><span></span></div>
              </div>
            )}
            {localPhase === 'result' && (
              <div className={`battle-popup__result battle-popup__result--${winnerSide}`}>
                {winnerSide?.toUpperCase()}
              </div>
            )}
            
            {/* Coin */}
            <div className="battle-popup__coin">
              <ThreeCoin 
                phase={localPhase === 'flipping' ? 'flipping' : ((localPhase === 'result' || localPhase === 'landed') ? 'reveal' : localPhase)}
                winnerSide={winnerSide}
                size={160}
                onAnimationComplete={() => {}}
              />
            </div>
            
            <span className="battle-popup__vs">VS</span>
          </div>

          {/* Green Player */}
          <div className={`battle-popup__player battle-popup__player--green ${
            localPhase === 'result' ? (winnerSide === 'green' ? 'battle-popup__player--winner' : 'battle-popup__player--loser') : ''
          }`}>
            <div className="battle-popup__avatar">
              <Avatar username={playerGreen || '?'} size="lg" level={greenLevel} showLevel={true} />
            </div>
            <div className="battle-popup__info">
              <span className="battle-popup__name">
                {playerGreen || 'Waiting...'}
                {userSide === 'green' && <span className="battle-popup__you">YOU</span>}
              </span>
              <span className="battle-popup__bet">{formatCoins(greenAmt)}</span>
            </div>
            <span className="battle-popup__odds">{greenPct.toFixed(0)}%</span>
            {localPhase === 'result' && winnerSide === 'green' && (
              <div className="battle-popup__crown">
                <svg viewBox="0 0 24 24" fill="currentColor" width="24" height="24">
                  <path d="M5 16L3 5l5.5 5L12 4l3.5 6L21 5l-2 11H5zm14 3c0 .6-.4 1-1 1H6c-.6 0-1-.4-1-1v-1h14v1z"/>
                </svg>
              </div>
            )}
          </div>
        </div>

        {/* Result ticket info */}
        {localPhase === 'result' && reveal?.ticket != null && (
          <div className="battle-popup__ticket">
            <span className="battle-popup__ticket-label">Roll:</span>
            <span className={`battle-popup__ticket-value battle-popup__ticket-value--${winnerSide}`}>
              {reveal.ticket.toFixed(4).replace('.', ',')}
            </span>
          </div>
        )}

        {/* User result banner */}
        {userInGame && showResult && (
          <div className={`battle-popup__banner ${userWon ? 'battle-popup__banner--won' : 'battle-popup__banner--lost'}`}>
            {userWon ? (
              <svg className="battle-popup__banner-icon" viewBox="0 0 24 24" fill="currentColor" width="20" height="20">
                <path d="M5 16L3 5l5.5 5L12 4l3.5 6L21 5l-2 11H5zm14 3c0 .6-.4 1-1 1H6c-.6 0-1-.4-1-1v-1h14v1z"/>
              </svg>
            ) : (
              <svg className="battle-popup__banner-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" width="20" height="20">
                <circle cx="12" cy="12" r="10"/>
                <path d="M15 9l-6 6M9 9l6 6"/>
              </svg>
            )}
            <span className="battle-popup__banner-text">
              {userWon ? 'YOU WON!' : 'YOU LOST'}
            </span>
            {userWon && (
              <span className="battle-popup__banner-prize">
                +{formatCoins(winnerPrize)}
              </span>
            )}
          </div>
        )}

        {/* Footer */}
        <div className="battle-popup__footer">
          <span className="battle-popup__fair">
            <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" width="12" height="12">
              <path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/>
            </svg>
            Provably Fair
          </span>
          {localPhase === 'waiting' && !duel.joiner && (
            <button className="battle-popup__btn battle-popup__btn--cancel" onClick={onClose}>Cancel</button>
          )}
          {showResult && (
            <button className="battle-popup__btn battle-popup__btn--close" onClick={onClose}>
              {userWon ? 'Collect' : 'Close'}
            </button>
          )}
        </div>
      </div>
    </div>
  );
}
// ============================================
// JOIN DIALOG
// ============================================
function JoinDialog({ battle, user, onConfirm, onClose }) {
  const [amount, setAmount] = useState('');
  const [loading, setLoading] = useState(false);

  const minJoin = Math.max(100, Math.floor(battle.amount * (1 - (battle.join_tolerance_pct || 0) / 100)));
  const maxJoin = Math.ceil(battle.amount * (1 + (battle.join_tolerance_pct || 0) / 100));
  const maxAllowed = Math.min(maxJoin, user?.balance || maxJoin);

  useEffect(() => {
    const defaultAmt = Math.min(Math.max(battle.amount, minJoin), maxAllowed);
    setAmount(formatCoins(defaultAmt));
  }, [battle.amount, minJoin, maxAllowed]);

  const amountCents = parseCoinsInput(amount);
  const joiningSide = battle.creator_side === 'red' ? 'green' : 'red';

  // Calculate odds
  const totalPot = battle.amount + amountCents;
  const yourChance = totalPot > 0 ? (amountCents / totalPot) * 100 : 50;
  const creatorChance = 100 - yourChance;

  const handleJoin = async () => {
    if (amountCents < minJoin || amountCents > maxAllowed) return;
    
    setLoading(true);
    try {
      await onConfirm(battle, amountCents);
      onClose();
    } catch (err) {
      console.error('Join failed:', err);
    } finally {
      setLoading(false);
    }
  };

  return (
    <div className="cf-dialog-overlay" onClick={onClose}>
      <div className="cf-dialog" onClick={(e) => e.stopPropagation()}>
        <button className="cf-dialog-close" onClick={onClose}>
          {Icons.close}
        </button>

        <div className="cf-dialog-header">
          <div className="cf-dialog-badge">Battle #{battle.id}</div>
          <h3 className="cf-dialog-title">Join Battle</h3>
        </div>

        <div className="cf-dialog-body">
          {/* Battle Info */}
          <div className="cf-dialog-info">
            <div className="cf-dialog-info-row">
              <span className="cf-dialog-label">Creator</span>
              <span className="cf-dialog-value">{battle.creator}</span>
            </div>
            <div className="cf-dialog-info-row">
              <span className="cf-dialog-label">Wager</span>
              <div className="cf-dialog-value cf-dialog-value--coins">
                {formatCoins(battle.amount)}
                <img src="/static/coinrushlogos.png" alt="" className="cf-coin-icon-sm" />
              </div>
            </div>
            <div className="cf-dialog-info-row">
              <span className="cf-dialog-label">Their Side</span>
              <SideBadge side={battle.creator_side} size="sm" />
            </div>
            <div className="cf-dialog-info-row">
              <span className="cf-dialog-label">Your Side</span>
              <SideBadge side={joiningSide} size="sm" />
            </div>
          </div>

          {/* Amount Input */}
          {(battle.join_tolerance_pct || 0) > 0 && (
            <div className="cf-dialog-amount">
              <label className="cf-label">Your Bet Amount</label>
              <div className="cf-dialog-amount-row">
                <input
                  type="text"
                  value={amount}
                  onChange={(e) => setAmount(e.target.value)}
                  className="cf-input"
                />
                <input
                  type="range"
                  min={minJoin}
                  max={maxAllowed}
                  step="100"
                  value={amountCents}
                  onChange={(e) => setAmount(formatCoins(Number(e.target.value)))}
                  className="cf-slider"
                />
              </div>
              <div className="cf-dialog-range">
                {formatCoins(minJoin)} – {formatCoins(maxAllowed)}
              </div>
            </div>
          )}

          {/* Odds Display */}
          <div className="cf-dialog-odds">
            <div className="cf-dialog-odds-item">
              <span className="cf-dialog-odds-label">Your Chance</span>
              <span className={clsx(
                'cf-dialog-odds-value',
                joiningSide === 'green' ? 'cf-text-green' : 'cf-text-red'
              )}>
                {yourChance.toFixed(1)}%
              </span>
            </div>
            <div className="cf-dialog-odds-pot">
              <span className="cf-dialog-odds-label">Total Pot</span>
              <div className="cf-dialog-odds-pot-value">
                {formatCoins(totalPot)}
                <img src="/static/coinrushlogos.png" alt="" className="cf-coin-icon-sm" />
              </div>
            </div>
            <div className="cf-dialog-odds-item">
              <span className="cf-dialog-odds-label">Their Chance</span>
              <span className={clsx(
                'cf-dialog-odds-value',
                battle.creator_side === 'green' ? 'cf-text-green' : 'cf-text-red'
              )}>
                {creatorChance.toFixed(1)}%
              </span>
            </div>
          </div>
        </div>

        <div className="cf-dialog-footer">
          <button className="cf-btn cf-btn--ghost" onClick={onClose}>Cancel</button>
          <button
            className="cf-btn cf-btn--primary"
            onClick={handleJoin}
            disabled={loading || amountCents < minJoin || amountCents > maxAllowed}
          >
            {loading ? 'Joining...' : 'Join Battle'}
          </button>
        </div>
      </div>
    </div>
  );
}

// ============================================
// RULES MODAL
// ============================================
function RulesModal({ isOpen, onClose }) {
  if (!isOpen) return null;

  return (
    <div className="cf-modal-overlay" onClick={onClose}>
      <div className="cf-modal cf-rules-modal" onClick={(e) => e.stopPropagation()}>
        <button className="cf-modal-close" onClick={onClose}>
          {Icons.close}
        </button>

        <div className="cf-modal-header">
          <div className="cf-modal-badge">Game Rules</div>
          <h2 className="cf-modal-title">How Coinflip Works</h2>
          <p className="cf-modal-subtitle">
            A fast-paced 1v1 betting game where two players compete with a coin flip.
          </p>
        </div>

        <div className="cf-modal-body">
          <div className="cf-rules-grid">
            <div className="cf-rules-card">
              <div className="cf-rules-step">1</div>
              <h3>Creating a Battle</h3>
              <ul>
                <li>Choose your bet amount and select Red or Green</li>
                <li>Click "Create Battle" to start your game</li>
                <li>Wait for an opponent to join</li>
              </ul>
            </div>

            <div className="cf-rules-card">
              <div className="cf-rules-step">2</div>
              <h3>Joining a Battle</h3>
              <ul>
                <li>Browse available battles in the lobby</li>
                <li>Click "Join" on any open battle</li>
                <li>You'll be assigned the opposite color</li>
              </ul>
            </div>

            <div className="cf-rules-card">
              <div className="cf-rules-step">3</div>
              <h3>The Coin Flip</h3>
              <ul>
                <li>Once both players are ready, the coin flips</li>
                <li>Watch the 3D coin animation</li>
                <li>The coin lands on Red or Green</li>
              </ul>
            </div>

            <div className="cf-rules-card">
              <div className="cf-rules-step">4</div>
              <h3>Winning</h3>
              <ul>
                <li>If the coin lands on your color, you win!</li>
                <li>Winner receives the pot minus 2% house fee</li>
                <li>Winnings are instantly credited</li>
              </ul>
            </div>
          </div>

          <div className="cf-rules-info">
            <div className="cf-rules-info-item">
              <span className="cf-rules-info-icon">{Icons.shield}</span>
              <div>
                <h4>Provably Fair</h4>
                <p>Every flip uses cryptographically secure randomization</p>
              </div>
            </div>
            <div className="cf-rules-info-item">
              <span className="cf-rules-info-icon">{Icons.bolt}</span>
              <div>
                <h4>Leverage Mode</h4>
                <p>Enable leverage to amplify your potential winnings</p>
              </div>
            </div>
          </div>
        </div>

        <div className="cf-modal-footer">
          <button className="cf-btn cf-btn--primary" onClick={onClose}>
            Got it, let's play!
          </button>
        </div>
      </div>
    </div>
  );
}

// ============================================
// DAILY REWARDS MODAL
// ============================================

// Helper to get SVG icon for reward type
const getRewardIcon = (type) => {
  const typeIcons = {
    'xp_boost': Icons.bolt,
    'coinflip_discount': Icons.swords,
    'barbarian_discount': Icons.shield,
    'jackpot_discount': Icons.trophy,
    'combo': Icons.crown
  };
  return typeIcons[type] || Icons.gift;
};

// Get reward color based on type
const getRewardColor = (type) => {
  const colors = {
    'xp_boost': { primary: '#ffd700', secondary: '#ff9500', gradient: 'linear-gradient(135deg, #ffd700, #ff9500)' },
    'coinflip_discount': { primary: '#3b82f6', secondary: '#1d4ed8', gradient: 'linear-gradient(135deg, #3b82f6, #1d4ed8)' },
    'barbarian_discount': { primary: '#ef4444', secondary: '#dc2626', gradient: 'linear-gradient(135deg, #ef4444, #dc2626)' },
    'jackpot_discount': { primary: '#a855f7', secondary: '#7c3aed', gradient: 'linear-gradient(135deg, #a855f7, #7c3aed)' },
    'combo': { primary: '#4fffb0', secondary: '#2dd490', gradient: 'linear-gradient(135deg, #4fffb0, #2dd490)' }
  };
  return colors[type] || { primary: '#4fffb0', secondary: '#2dd490', gradient: 'linear-gradient(135deg, #4fffb0, #2dd490)' };
};

function DailyRewardModal({ isOpen, onClose, user, onClaim }) {
  const [status, setStatus] = useState(null);
  const [loading, setLoading] = useState(true);
  const [claiming, setClaiming] = useState(false);
  const [claimResult, setClaimResult] = useState(null);
  const [showConfetti, setShowConfetti] = useState(false);
  
  useEffect(() => {
    if (isOpen && user) {
      setLoading(true);
      setClaimResult(null);
      fetch('/api/daily-reward/status', { credentials: 'include' })
        .then(res => res.ok ? res.json() : null)
        .then(data => {
          setStatus(data);
          setLoading(false);
        })
        .catch(() => setLoading(false));
    }
  }, [isOpen, user]);
  
  const handleClaim = async () => {
    if (claiming || !status || status.daily_reward_claimed) return;
    
    setClaiming(true);
    try {
      const res = await fetch('/api/daily-reward/claim', {
        method: 'POST',
        credentials: 'include'
      });
      const data = await res.json();
      
      if (res.ok) {
        setClaimResult(data);
        setShowConfetti(true);
        setStatus(prev => ({ ...prev, daily_reward_claimed: true }));
        onClaim?.(data);
        setTimeout(() => setShowConfetti(false), 3000);
      } else {
        setClaimResult({ error: data.detail || 'Failed to claim reward' });
      }
    } catch (e) {
      setClaimResult({ error: 'Network error' });
    }
    setClaiming(false);
  };
  
  if (!isOpen) return null;
  
  const streak = status?.login_streak || 1;
  const reward = status?.today_reward || {};
  const claimed = status?.daily_reward_claimed || false;
  const rewardColor = getRewardColor(reward.type);
  
  const allRewards = status?.all_rewards || {};
  const visibleDays = [];
  const startDay = Math.max(1, streak - 3);
  const endDay = Math.min(30, startDay + 6);
  for (let i = startDay; i <= endDay; i++) {
    visibleDays.push({
      day: i,
      reward: allRewards[i] || {},
      isCurrent: i === streak,
      isPast: i < streak,
      isFuture: i > streak
    });
  }
  
  const hasActiveBonuses = status?.active_bonuses && (
    status.active_bonuses.xp_boost > 0 ||
    status.active_bonuses.coinflip_discounts > 0 ||
    status.active_bonuses.barbarian_discounts > 0 ||
    status.active_bonuses.jackpot_discounts > 0
  );
  
  return (
    <div className="cf-modal-backdrop" onClick={onClose}>
      <div className="cf-modal cf-modal--daily" onClick={(e) => e.stopPropagation()}>
        {showConfetti && (
          <div className="cf-confetti">
            {[...Array(60)].map((_, i) => (
              <div 
                key={i} 
                className="cf-confetti-piece"
                style={{
                  left: `${Math.random() * 100}%`,
                  animationDelay: `${Math.random() * 0.5}s`,
                  animationDuration: `${2 + Math.random() * 2}s`,
                  backgroundColor: ['#4fff9f', '#ffd700', '#ff6b6b', '#3b82f6', '#a855f7'][Math.floor(Math.random() * 5)]
                }}
              />
            ))}
          </div>
        )}
        
        <button className="cf-modal-close" onClick={onClose}>
          {Icons.close}
        </button>
        
        <div className="cf-modal-body">
          {loading ? (
            <div className="cf-loading">
              <div className="cf-spinner" />
              <span>Loading rewards...</span>
            </div>
          ) : !user ? (
            <div className="cf-login-prompt">
              <span className="cf-login-icon">{Icons.shield}</span>
              <h3>Login Required</h3>
              <p>Please login to claim your daily rewards!</p>
              <button 
                className="cf-btn cf-btn--primary"
                onClick={() => { onClose(); window.openAuthModal?.('login'); }}
              >
                Log In
              </button>
            </div>
          ) : (
            <>
              <div className="cf-daily-hero">
                <div className="cf-daily-icon" style={{ background: rewardColor.gradient }}>
                  {getRewardIcon(reward.type)}
                </div>
                <div className="cf-daily-streak">
                  <span className="cf-streak-flame">{Icons.bolt}</span>
                  <span className="cf-streak-count">{streak}</span>
                  <span className="cf-streak-text">Day Streak</span>
                </div>
                <h2 className="cf-modal-title">
                  {claimed ? 'Reward Claimed!' : 'Daily Reward'}
                </h2>
                <p className="cf-modal-subtitle">
                  {claimed 
                    ? 'Come back tomorrow to continue your streak!'
                    : 'Claim your reward and keep your streak alive!'
                  }
                </p>
              </div>
              
              <div className={`cf-reward-card ${claimed ? 'cf-reward-card--claimed' : ''}`} style={{ '--reward-color': rewardColor.primary }}>
                <div className="cf-reward-badge">Day {streak}</div>
                <div className="cf-reward-icon" style={{ background: rewardColor.gradient }}>
                  {getRewardIcon(reward.type)}
                </div>
                <div className="cf-reward-info">
                  <h3 className="cf-reward-name">{reward.name || 'Daily Reward'}</h3>
                  <p className="cf-reward-desc">{reward.description || 'Claim your daily reward!'}</p>
                </div>
                {claimed && (
                  <div className="cf-reward-check">
                    {Icons.check}
                  </div>
                )}
              </div>
              
              <div className="cf-timeline">
                <div className="cf-timeline-label">Reward Calendar</div>
                <div className="cf-timeline-track">
                  {visibleDays.map(({ day, reward: dayReward, isCurrent, isPast, isFuture }) => {
                    const dayColor = getRewardColor(dayReward.type);
                    return (
                      <div 
                        key={day}
                        className={clsx('cf-timeline-day', isCurrent && 'cf-timeline-day--current', isPast && 'cf-timeline-day--past', isFuture && 'cf-timeline-day--future')}
                      >
                        <div className="cf-timeline-day-num">Day {day}</div>
                        <div 
                          className="cf-timeline-day-icon" 
                          style={{ 
                            background: isCurrent ? dayColor.gradient : 'rgba(255,255,255,0.05)',
                            color: isCurrent ? '#000' : dayColor.primary
                          }}
                        >
                          {getRewardIcon(dayReward.type)}
                        </div>
                        {isPast && <div className="cf-timeline-check">{Icons.check}</div>}
                      </div>
                    );
                  })}
                </div>
              </div>
              
              {hasActiveBonuses && (
                <div className="cf-bonuses">
                  <div className="cf-bonuses-label">Active Power-Ups</div>
                  <div className="cf-bonuses-row">
                    {status.active_bonuses.xp_boost > 0 && (
                      <div className="cf-powerup cf-powerup--xp">
                        {Icons.bolt}
                        <span>+{status.active_bonuses.xp_boost}% XP</span>
                      </div>
                    )}
                    {status.active_bonuses.coinflip_discounts > 0 && (
                      <div className="cf-powerup cf-powerup--cf">
                        {Icons.swords}
                        <span>{status.active_bonuses.coinflip_discounts}x Flip</span>
                      </div>
                    )}
                    {status.active_bonuses.barbarian_discounts > 0 && (
                      <div className="cf-powerup cf-powerup--bd">
                        {Icons.shield}
                        <span>{status.active_bonuses.barbarian_discounts}x Duel</span>
                      </div>
                    )}
                    {status.active_bonuses.jackpot_discounts > 0 && (
                      <div className="cf-powerup cf-powerup--jp">
                        {Icons.trophy}
                        <span>{status.active_bonuses.jackpot_discounts}x Jackpot</span>
                      </div>
                    )}
                  </div>
                </div>
              )}
              
              <div className="cf-daily-action">
                {claimed ? (
                  <div className="cf-claimed-notice">
                    {Icons.clock}
                    <span>Next reward in <strong>Tomorrow</strong></span>
                  </div>
                ) : (
                  <button 
                    className={`cf-btn cf-btn--primary cf-btn--lg ${claiming ? 'cf-btn--loading' : ''}`}
                    onClick={handleClaim}
                    disabled={claiming}
                  >
                    {claiming ? (
                      <>
                        <span className="cf-spinner cf-spinner--sm" />
                        Claiming...
                      </>
                    ) : (
                      <>
                        {Icons.gift}
                        Claim Reward
                      </>
                    )}
                  </button>
                )}
                
                {claimResult?.error && (
                  <div className="cf-error-msg">{claimResult.error}</div>
                )}
              </div>
            </>
          )}
        </div>
      </div>
    </div>
  );
}

// ============================================
// 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 COMPONENT
// ============================================
function CoinflipApp() {
  const [user, setUser] = useState(null);
  const [battles, setBattles] = useState([]);
  const [history, setHistory] = useState([]);
  const [leaderboard, setLeaderboard] = useState([]);
  const [ws, setWs] = useState(null);
  const [wsConnected, setWsConnected] = useState(false);


  // Duel overlay state
  const [activeDuel, setActiveDuel] = useState(null);
  const [duelReveal, setDuelReveal] = useState(null);
  const [duelPhase, setDuelPhase] = useState(null);
  const [duelTimeLeft, setDuelTimeLeft] = useState(0);
  
  // Refs to track current values for WebSocket handler (avoid stale closures)
  const activeDuelRef = useRef(null);
  const userRef = useRef(null);
  const duelPhaseRef = useRef(null);
  
  // Keep refs in sync with state
  useEffect(() => { activeDuelRef.current = activeDuel; }, [activeDuel]);
  useEffect(() => { userRef.current = user; }, [user]);
  useEffect(() => { duelPhaseRef.current = duelPhase; }, [duelPhase]);

  // Join dialog state
  const [joinBattle, setJoinBattle] = useState(null);

  // Rules modal
  const [showRules, setShowRules] = useState(false);

  // Auth modal state
  const [showAuthModal, setShowAuthModal] = useState(false);
  const [authModalMode, setAuthModalMode] = useState('login');

  // Daily rewards modal
  const [showDailyModal, setShowDailyModal] = useState(false);

  // Filter
  const [filter, setFilter] = useState('all');

  // Expose functions for navbar
  useEffect(() => {
    window.showCoinflipRules = () => setShowRules(true);
    window.dailyRewardsModal = {
      open: () => setShowDailyModal(true),
      close: () => setShowDailyModal(false)
    };
    window.openAuthModal = (mode = 'login') => {
      setAuthModalMode(mode);
      setShowAuthModal(true);
    };
    return () => {
      delete window.showCoinflipRules;
      delete window.dailyRewardsModal;
      delete window.openAuthModal;
    };
  }, []);

  // Handle logout
  const handleLogout = async () => {
    try {
      await fetch('/auth/logout', { method: 'POST', credentials: 'include' });
      setUser(null);
      window.location.reload();
    } catch (err) {
      console.error('Logout failed:', err);
    }
  };

  // Handle auth success
  const handleAuthSuccess = (userData) => {
    setUser(userData);
    setShowAuthModal(false);
  };

  // Load user
  const loadUser = useCallback(async () => {
    try {
      const data = await api('/me');
      setUser(data);
    } catch {
      setUser(null);
    }
  }, []);

  // Load battles
  const loadBattles = useCallback(async () => {
    try {
      const [live, recent] = await Promise.all([
        api('/coinflip/live', { auth: false }),
        api('/coinflip/recent-resolved', { auth: false })
      ]);
      setBattles([...live, ...recent]);
    } catch {
      // Fallback
      try {
        const data = await api('/coinflip/live', { auth: false });
        setBattles(data);
      } catch {}
    }
  }, []);

  // Load history
  const loadHistory = useCallback(async () => {
    try {
      const data = await api('/coinflip/history', { auth: false });
      setHistory(data);
    } catch {}
  }, []);

  // Load leaderboard
  const loadLeaderboard = useCallback(async () => {
    try {
      const data = await api('/coinflip/weekly-leaderboard', { auth: false });
      setLeaderboard(data);
    } catch {}
  }, []);

  // Initial load
  useEffect(() => {
    loadUser();
    loadBattles();
    loadHistory();
    loadLeaderboard();
  }, [loadUser, loadBattles, loadHistory, loadLeaderboard]);

  // Auto-cleanup expired battles (5 minute timeout)
  useEffect(() => {
    const cleanupInterval = setInterval(() => {
      setBattles(prev => prev.filter(battle => {
        // Keep resolved battles (they have their own cleanup timer)
        if (battle.status === 'resolved' || battle.status === 'countdown') {
          return true;
        }
        
        // Check if open battle has expired (older than 5 minutes)
        if (battle.status === 'open' && battle.created_at) {
          let createdAtStr = battle.created_at;
          if (createdAtStr && !createdAtStr.endsWith('Z') && !createdAtStr.includes('+')) {
            createdAtStr += 'Z';
          }
          const createdAt = new Date(createdAtStr).getTime();
          if (Number.isFinite(createdAt)) {
            const elapsed = (Date.now() - createdAt) / 1000;
            // Remove if older than battle timeout (300 seconds = 5 minutes)
            if (elapsed > CONFIG.battleTimeout) {
              console.log('[Auto-cleanup] Removing expired battle:', battle.id);
              return false;
            }
          }
        }
        return true;
      }));
    }, 5000); // Check every 5 seconds

    return () => clearInterval(cleanupInterval);
  }, []);

  // WebSocket connection
  useEffect(() => {
    const wsUrl = (location.protocol === 'https:' ? 'wss://' : 'ws://') + location.host + '/ws/lobby';
    const socket = new WebSocket(wsUrl);
    setWs(socket);

    socket.onopen = () => setWsConnected(true);
    socket.onclose = () => setWsConnected(false);
    socket.onerror = () => setWsConnected(false);

    socket.onmessage = (ev) => {
      try {
        const data = JSON.parse(ev.data);

        if (data.type === 'battle_created') {
          setBattles(prev => [{
            id: data.id,
            created_at: data.created_at,
            amount: data.amount,
            creator: data.creator,
            creator_side: data.creator_side,
            status: 'open',
            join_tolerance_pct: data.join_tolerance_pct || 0,
            watchers_count: data.watchers_count || 0,
            creator_level: data.creator_level || 1
          }, ...prev]);
        }

        if (data.type === 'battle_filled') {
          console.log('[WebSocket battle_filled]', data);
          setDuelReveal(null);
          setActiveDuel({
            id: data.id,
            amount: data.amount,
            join_amount: data.join_amount || data.amount,
            creator: data.creator,
            joiner: data.joiner,
            creator_side: data.creator_side,
            creator_level: data.creator_level || 1,
            joiner_level: data.joiner_level || 1,
            status: 'countdown',
            start_flip_at: data.start_flip_at
          });
          console.log('[WebSocket] Setting duelPhase to countdown');
          setDuelPhase('countdown');

          // Update the battles list with countdown status
          setBattles(prev => prev.map(b =>
            b.id === data.id ? { 
              ...b, 
              status: 'countdown', 
              joiner: data.joiner,
              join_amount: data.join_amount,
              start_flip_at: data.start_flip_at,
              joiner_level: data.joiner_level || 1
            } : b
          ));

          const startMs = new Date(data.start_flip_at).getTime();
          const now = Date.now();
          const countdown = Math.max(0, startMs - now);

          setDuelTimeLeft(countdown);

          // Only update countdown timer - DON'T start flip here!
          // Wait for battle_resolved from server with winner_side
          const countdownInterval = setInterval(() => {
            const remaining = Math.max(0, startMs - Date.now());
            setDuelTimeLeft(remaining);
            if (remaining <= 0) {
              clearInterval(countdownInterval);
              // Keep countdown phase until server sends battle_resolved
              // The phase will change when we receive the result
            }
          }, 100);
        }

        if (data.type === 'battle_resolved') {
          const currentActiveDuel = activeDuelRef.current;
          const currentUser = userRef.current;
          
          console.log('[WebSocket Battle Resolved]', {
            messageData: data,
            winner: data.winner,
            winnerSide: data.winner_side,
            currentUser: currentUser?.username,
            activeDuelId: currentActiveDuel?.id,
            dataId: data.id,
            willSetReveal: currentActiveDuel && currentActiveDuel.id === data.id
          });
          
          setHistory(prev => [{
            id: data.id,
            amount: data.amount,
            winner: data.winner,
            winner_side: data.winner_side
          }, ...prev].slice(0, 100));

          setBattles(prev => prev.map(b =>
            b.id === data.id ? { ...b, status: 'resolved', winner: data.winner, winner_side: data.winner_side } : b
          ));

          if (currentActiveDuel && currentActiveDuel.id === data.id) {
            console.log('[Setting Duel Reveal & Starting Flip]', {
              revealData: data,
              currentUsername: currentUser?.username,
              willUserWin: data.winner === currentUser?.username
            });
            // IMPORTANT: Set reveal data FIRST (so coin knows where to land)
            // Then set phase to 'flipping' to trigger animation
            setDuelReveal(data);
            setDuelPhase('flipping');
            
            // After coin animation (~2.5s), show the result
            setTimeout(() => {
              setDuelPhase('reveal');
              // Show any pending balance animation now that flip is complete
              if (window.showPendingBalanceAnimation) {
                window.showPendingBalanceAnimation();
              }
            }, 2500);
          }

          loadLeaderboard();

          // Remove after 60s
          setTimeout(() => {
            setBattles(prev => prev.filter(b => b.id !== data.id));
          }, 60000);
        }

        if (data.type === 'watchers') {
          setBattles(prev => prev.map(b =>
            b.id === data.id ? { ...b, watchers_count: data.count } : b
          ));
        }

        // Live balance update - update user balance without full reload
        if (data.type === 'balance_update') {
          const currentUser = userRef.current;
          if (currentUser && data.username === currentUser.username) {
            const oldBalance = currentUser.balance || 0;
            const newBalance = data.balance;
            const delta = newBalance - oldBalance;
            setUser(prev => prev ? { ...prev, balance: newBalance } : prev);
            // Queue animation if mid-flip, show immediately otherwise
            if (delta !== 0 && window.updateCoinRushBalance) {
              const isFlipping = duelPhaseRef.current === 'flipping' || duelPhaseRef.current === 'countdown';
              window.updateCoinRushBalance(newBalance, delta, !isFlipping);
            }
          }
        }
      } catch {}
    };

    return () => socket.close();
  }, []);

  // Join battle handler
  const handleJoin = async (battle, amount) => {
    const joinAmount = amount || battle.amount;
    await api('/coinflip/join', {
      method: 'POST',
      body: { battle_id: battle.id, amount: joinAmount, client_seed: '' }
    });
    // Balance will be updated via WebSocket balance_update event
  };

  // Filter battles
  const filteredBattles = battles.filter(b => {
    if (filter === 'all') return true;
    if (filter === 'red') return b.creator_side === 'red';
    if (filter === 'green') return b.creator_side === 'green';
    if (filter === 'high') return b.amount >= 10000;
    return true;
  });

  // Get navbar component (from navbar-react.jsx)
  const NavbarComponent = window.CoinRushNavbar;
  
  // Get chat component (from chat-react.jsx)
  const SidebarChatComponent = window.CoinRushChatApp;

  return (
    <div className="cf-app">
      {/* Floating Glowing Orbs - Background Animation */}
      <div className="cf-orbs">
        <div className="cf-orb cf-orb--1"></div>
        <div className="cf-orb cf-orb--2"></div>
        <div className="cf-orb cf-orb--3"></div>
        <div className="cf-orb cf-orb--4"></div>
        <div className="cf-orb cf-orb--5"></div>
        <div className="cf-orb cf-orb--6"></div>
      </div>

      {/* Floating Particles - Small Dots */}
      <div className="cf-particles">
        {[...Array(35)].map((_, i) => {
          // Use seeded pseudo-random for consistent but scattered positions
          const seed = i * 17 + 7;
          const x = ((seed * 13) % 90) + 5;
          const delay = i * 0.2;
          const duration = 4 + ((seed * 3) % 5);
          const size = 2 + (i % 4);
          return (
            <div 
              key={i} 
              className="cf-particle"
              style={{
                '--x': `${x}%`,
                '--delay': `${delay}s`,
                '--duration': `${duration}s`,
                '--size': `${size}px`,
                '--color': i % 2 === 0 ? 'rgba(239, 68, 68, 0.9)' : 'rgba(34, 197, 94, 0.9)'
              }}
            />
          );
        })}
      </div>

      {/* React Navbar - Fixed at top */}
      {NavbarComponent && (
        <NavbarComponent
          user={user}
          isConnected={wsConnected}
          activePage="coinflip"
          onLogout={handleLogout}
        />
      )}

      {/* Auth Modal (uses shared component from auth-react.jsx with OAuth buttons) */}
      {window.AuthModal && (
        <window.AuthModal
          isOpen={showAuthModal}
          initialMode={authModalMode}
          onClose={() => setShowAuthModal(false)}
          onSuccess={handleAuthSuccess}
        />
      )}

      {/* Daily Rewards Modal */}
      <DailyRewardModal
        isOpen={showDailyModal}
        onClose={() => setShowDailyModal(false)}
        user={user}
        onClaim={() => loadUser()}
      />

      {/* Connection Warning */}
      {!wsConnected && (
        <div className="cf-connection-warning">
          <span className="cf-connection-icon">⚠️</span>
          <span>Connection lost. Please refresh the page.</span>
        </div>
      )}

      {/* History Ticker */}
      <HistoryTicker history={history} />

      {/* Main Layout */}
      <div className="cf-layout">
        {/* Left: Create Panel */}
        <div className="cf-sidebar cf-sidebar--left">
          <CreateBattlePanel user={user} onRefresh={loadBattles} />
        </div>

        {/* Center: Battles */}
        <div className="cf-main">
          {/* Header */}
          <div className="cf-header">
            <div className="cf-header-title">
              <h1>Live Battles</h1>
              <div className="cf-header-status">
                <span className="cf-status-dot" />
                <span>{filteredBattles.filter(b => b.status !== 'resolved').length} Active</span>
              </div>
            </div>

            <div className="cf-header-actions">
              <div className="cf-filter-tabs">
                {['all', 'red', 'green', 'high'].map(f => (
                  <button
                    key={f}
                    onClick={() => setFilter(f)}
                    className={clsx('cf-filter-tab', filter === f && 'cf-filter-tab--active')}
                  >
                    {f === 'all' ? 'All' : f === 'high' ? (
                      <><span className="cf-filter-icon">{Icons.crown}</span> High</>
                    ) : f.charAt(0).toUpperCase() + f.slice(1)}
                  </button>
                ))}
              </div>

              <button className="cf-btn cf-btn--ghost cf-btn--sm" onClick={() => setShowRules(true)}>
                <span className="cf-btn-icon">{Icons.info}</span>
                Rules
              </button>
            </div>
          </div>

          {/* Battle List */}
          <div className="cf-battle-list">
            {filteredBattles.length > 0 ? (
              filteredBattles.map(battle => (
                <BattleCard
                  key={battle.id}
                  battle={battle}
                  user={user}
                  onJoin={(b) => {
                    if ((b.join_tolerance_pct || 0) > 0) {
                      setJoinBattle(b);
                    } else {
                      handleJoin(b);
                    }
                  }}
                  onView={(b) => {
                    setActiveDuel(b);
                    if (b.status === 'resolved') {
                      setDuelPhase('reveal');
                      setDuelReveal({ winner_side: b.winner_side, winner: b.winner });
                    } else if (b.status === 'countdown' && b.start_flip_at) {
                      // Calculate remaining countdown time
                      const startMs = new Date(b.start_flip_at).getTime();
                      const now = Date.now();
                      const remaining = Math.max(0, startMs - now);
                      setDuelTimeLeft(remaining);
                      setDuelPhase('countdown');
                      
                      // Start countdown interval
                      const countdownInterval = setInterval(() => {
                        const timeLeft = Math.max(0, startMs - Date.now());
                        setDuelTimeLeft(timeLeft);
                        if (timeLeft <= 0) {
                          clearInterval(countdownInterval);
                        }
                      }, 100);
                    } else {
                      setDuelPhase(b.joiner ? 'ready' : 'waiting');
                    }
                  }}
                />
              ))
            ) : (
              <div className="cf-empty-state cf-empty-state--large">
                <span className="cf-empty-icon">{Icons.swords}</span>
                <h3>No Active Battles</h3>
                <p>Create the first battle to get started!</p>
              </div>
            )}
          </div>
        </div>

        {/* Right: Chat */}
        <div className="cf-sidebar cf-sidebar--right">
          {SidebarChatComponent && (
            <SidebarChatComponent currentUser={user} theme="green" />
          )}
        </div>
      </div>

      {/* Duel Overlay */}
      {activeDuel && (
        <DuelOverlay
          duel={activeDuel}
          reveal={duelReveal}
          phase={duelPhase}
          timeLeft={duelTimeLeft}
          user={user}
          onClose={() => {
            setActiveDuel(null);
            setDuelReveal(null);
            setDuelPhase(null);
          }}
        />
      )}

      {/* Join Dialog */}
      {joinBattle && (
        <JoinDialog
          battle={joinBattle}
          user={user}
          onConfirm={handleJoin}
          onClose={() => setJoinBattle(null)}
        />
      )}

      {/* Rules Modal */}
      <RulesModal isOpen={showRules} onClose={() => setShowRules(false)} />
    </div>
  );
}

// ============================================
// RENDER
// ============================================
const rootEl = document.getElementById('coinflip-root');
if (rootEl) {
  const root = ReactDOM.createRoot(rootEl);
  root.render(<CoinflipApp />);
}
