// hub-components.jsx β Shared constants, algorithms, SpiderChart, RevealAnimation const ATTRS = [ { key: 'hiz', label: 'HΔ±z', icon: 'β‘' }, { key: 'sut', label: 'Εut', icon: 'π―' }, { key: 'pas', label: 'Pas', icon: 'π' }, { key: 'dripling', label: 'Dripling', icon: 'π' }, { key: 'defans', label: 'Defans', icon: 'π‘' }, { key: 'fizik', label: 'Fizik', icon: 'πͺ' }, ]; const POSITIONS = ['KALECΔ°', 'DEFANS', 'ORTA SAHA', 'KANAT', 'FORVET']; const POS_SHORT = { 'KALECΔ°':'GK', 'DEFANS':'DEF', 'ORTA SAHA':'MID', 'KANAT':'WNG', 'FORVET':'FWD' }; const DEFAULT_SCORES = { hiz:5, sut:5, pas:5, dripling:5, defans:5, fizik:5 }; function calcOverall(scores) { if (!scores) return 0; return parseFloat((ATTRS.reduce((s, a) => s + (scores[a.key] || 0), 0) / ATTRS.length).toFixed(1)); } function getTitle(scores) { const { hiz, sut, pas, dripling, defans, fizik } = scores; const avg = calcOverall(scores); if (hiz >= 8.5 && dripling >= 8) return { title:'FΔ°ΕEK', sub:'Lightning Winger', color:'#FFD700' }; if (defans >= 8.5 && fizik >= 8) return { title:'TANK', sub:'Defensive Wall', color:'#CC6600' }; if (pas >= 8.5 && hiz >= 7) return { title:'MAESTRO', sub:'Playmaker', color:'#9B59B6' }; if (sut >= 8.5 && hiz >= 7) return { title:'AVCI', sub:'Clinical Finisher', color:'#E74C3C' }; if (avg >= 8) return { title:'EFSANE', sub:'Complete Player', color:'#39FF14' }; if (fizik >= 8.5) return { title:'DUVAR', sub:'Physical Beast', color:'#7F8C8D' }; if (hiz >= 8.5) return { title:'FIRTINA', sub:'Speed Demon', color:'#3498DB' }; if (pas >= 8) return { title:'VΔ°ZYONER', sub:'Vision Master', color:'#1ABC9C' }; if (avg >= 7) return { title:'KΔ°LΔ°T', sub:'Key Player', color:'#F39C12' }; if (avg >= 5.5) return { title:'KAPTAN', sub:'Solid Performer', color:'#BDC3C7' }; return { title:'TOPAR', sub:'Room to Grow', color:'#555' }; } // βββ SPIDER CHART ββββββββββββββββββββββββββββββββββββββββββββββββββββ function SpiderChart({ scores, size = 200, animated = false, accentColor = '#39FF14' }) { const cx = size/2, cy = size/2, r = size*0.36, n = ATTRS.length; const polar = (val, i, rad) => { const a = (Math.PI*2*i/n) - Math.PI/2; const d = (val/10)*rad; return { x: cx + d*Math.cos(a), y: cy + d*Math.sin(a) }; }; const polyPts = (vals, rad) => ATTRS.map((a,i) => { const v = vals[a.key] ?? 5; const p = polar(v, i, rad); return `${p.x},${p.y}`; }).join(' '); return ( ); } // βββ MERGE REVEAL ββββββββββββββββββββββββββββββββββββββββββββββββββββ function RevealAnimation({ player, votes, onDone }) { const [phase, setPhase] = React.useState('orbs'); React.useEffect(() => { const t1 = setTimeout(() => setPhase('flash'), 2000); const t2 = setTimeout(() => setPhase('card'), 2700); return () => { clearTimeout(t1); clearTimeout(t2); }; }, []); const avg = ATTRS.reduce((acc,a) => ({ ...acc, [a.key]: votes.reduce((s,v)=>s+(v[a.key]||0),0)/votes.length }), {}); const titleData = getTitle(avg); const overall = calcOverall(avg); if (phase === 'orbs') { return (