// Floating background suits + confetti + sound-free FX const { useEffect, useState, useRef } = React; function SuitsBG({ enabled = true }) { if (!enabled) return null; const suits = ["♠","♥","♦","♣"]; const pieces = React.useMemo(() => Array.from({ length: 14 }).map((_, i) => { const suit = suits[i % 4]; const isRed = suit === "♥" || suit === "♦"; return { suit, isRed, size: 38 + Math.random() * 90, left: Math.random() * 100, delay: -Math.random() * 28, dur: 22 + Math.random() * 20, startTop: 100 + Math.random() * 40, }; }), []); return (
{pieces.map((p, i) => ( {p.suit} ))}
); } function Confetti({ fire, onDone }) { const [pieces, setPieces] = useState([]); useEffect(() => { if (!fire) return; const colors = ["#f5c542","#e63946","#22d3ee","#ff4fa3","#d9f635","#4ade80","#f6efd9"]; const shapes = [ { w: 14, h: 18, br: "2px" }, { w: 10, h: 10, br: "50%" }, { w: 6, h: 16, br: "2px" }, { w: 18, h: 6, br: "3px" }, ]; const arr = Array.from({ length: 90 }).map(() => { const angle = Math.random() * Math.PI * 2; const dist = 260 + Math.random() * 320; const shape = shapes[Math.floor(Math.random() * shapes.length)]; return { color: colors[Math.floor(Math.random() * colors.length)], tx: Math.cos(angle) * dist + "px", ty: Math.sin(angle) * dist + window.innerHeight * 0.15 + "px", tr: (Math.random() * 720 - 360) + "deg", delay: Math.random() * 120, ...shape, }; }); setPieces(arr); const t = setTimeout(() => { setPieces([]); onDone && onDone(); }, 2200); return () => clearTimeout(t); }, [fire]); if (!pieces.length) return null; return (
{pieces.map((p, i) => ( ))}
); } Object.assign(window, { SuitsBG, Confetti });