import React, { useState, useEffect, useCallback, useMemo } from 'react';

const API = process.env.REACT_APP_API_URL || '/api';
const apiFetch = (url) => fetch(url).then(r => r.ok ? r.json() : null).catch(() => null);

/* ── Design tokens ──────────────────────────────────────────── */
const LIME    = '#7CF53E';
const C_OK    = '#34D399';
const C_WARN  = '#F59E0B';
const C_ERROR = '#EF4444';

/* ════════════════════════════════════════════════════════════
   HOOKS
════════════════════════════════════════════════════════════ */
function useNow(interval = 1000) {
    const [now, setNow] = useState(() => new Date());
    useEffect(() => {
        const id = setInterval(() => setNow(new Date()), interval);
        return () => clearInterval(id);
    }, [interval]);
    return now;
}

function useCountUp(target, durationMs = 1400, deps = []) {
    const [val, setVal] = useState(target);
    useEffect(() => {
        if (document.visibilityState !== 'visible') { setVal(target); return; }
        let raf;
        const start = performance.now();
        const to = Number(target) || 0;
        setVal(0);
        const tick = (t) => {
            const p = Math.min(1, (t - start) / durationMs);
            const eased = 1 - Math.pow(1 - p, 3);
            setVal(to * eased);
            if (p < 1) raf = requestAnimationFrame(tick);
        };
        raf = requestAnimationFrame(tick);
        const safety = setTimeout(() => setVal(to), 200);
        return () => { cancelAnimationFrame(raf); clearTimeout(safety); };
        // eslint-disable-next-line
    }, deps);
    return val;
}

function useStageScale(w = 1920, h = 1080) {
    const [scale, setScale] = useState(1);
    useEffect(() => {
        const fit = () => {
            const vw = window.innerWidth, vh = window.innerHeight;
            if (vw < 4 || vh < 4) return;
            setScale(Math.min(vw / w, vh / h));
        };
        fit();
        window.addEventListener('resize', fit);
        const retries = [50, 150, 400].map(t => setTimeout(fit, t));
        return () => { window.removeEventListener('resize', fit); retries.forEach(clearTimeout); };
    }, [w, h]);
    return scale;
}

/* ════════════════════════════════════════════════════════════
   ATOMS — shared
════════════════════════════════════════════════════════════ */
function Sparkline({ data = [], width = 400, height = 80, color = LIME, fill = true, strokeW = 2 }) {
    if (!data || data.length < 2) return null;
    const min = Math.min(...data), max = Math.max(...data);
    const range = max - min || 1;
    const step = width / (data.length - 1);
    const pts = data.map((v, i) => [i * step, height - ((v - min) / range) * (height - 8) - 4]);
    const path = pts.map((p, i) => `${i === 0 ? 'M' : 'L'}${p[0].toFixed(1)},${p[1].toFixed(1)}`).join(' ');
    const area = `${path} L${width},${height} L0,${height} Z`;
    const id = `sg-${color.replace(/[^a-z0-9]/gi, '')}-${width}`;
    return (
        <svg viewBox={`0 0 ${width} ${height}`} preserveAspectRatio="none" style={{ width: '100%', height: '100%' }}>
            <defs>
                <linearGradient id={id} x1="0" y1="0" x2="0" y2="1">
                    <stop offset="0%" stopColor={color} stopOpacity="0.35" />
                    <stop offset="100%" stopColor={color} stopOpacity="0" />
                </linearGradient>
            </defs>
            {fill && <path d={area} fill={`url(#${id})`} />}
            <path d={path} fill="none" stroke={color} strokeWidth={strokeW} strokeLinecap="round" strokeLinejoin="round" />
            <circle cx={pts[pts.length - 1][0]} cy={pts[pts.length - 1][1]} r={strokeW + 1} fill={color} />
        </svg>
    );
}

function Donut({ value, size = 220, stroke = 16, color = LIME, label, sublabel, animateKey }) {
    const animated = useCountUp(value, 1400, [animateKey, value]);
    const r = (size - stroke) / 2;
    const c = 2 * Math.PI * r;
    const off = c - (animated / 100) * c;
    return (
        <div className="donut" style={{ width: size, height: size }}>
            <svg width={size} height={size}>
                <circle cx={size/2} cy={size/2} r={r} fill="none" stroke="rgba(255,255,255,0.08)" strokeWidth={stroke} />
                <circle cx={size/2} cy={size/2} r={r} fill="none" stroke={color} strokeWidth={stroke}
                    strokeDasharray={c} strokeDashoffset={off} strokeLinecap="round"
                    transform={`rotate(-90 ${size/2} ${size/2})`}
                    style={{ filter: `drop-shadow(0 0 12px ${color}55)` }} />
            </svg>
            <div className="donut-center">
                <div className="donut-value">{Math.round(animated)}<span className="donut-pct">%</span></div>
                {label && <div className="donut-label">{label}</div>}
                {sublabel && <div className="donut-sublabel">{sublabel}</div>}
            </div>
        </div>
    );
}

function BigNumber({ value, suffix = '', decimals = 0, animateKey }) {
    const animated = useCountUp(value, 1400, [animateKey, value]);
    const display = decimals === 0
        ? Math.round(animated).toLocaleString('de-DE')
        : animated.toFixed(decimals).replace('.', ',');
    return <span key={animateKey} className="number-punch numeric">{display}{suffix}</span>;
}

function StatusDot({ status, size = 10 }) {
    const colors = { ok: C_OK, online: C_OK, warn: C_WARN, error: C_ERROR, offline: C_ERROR };
    const c = colors[status] || '#94A3B8';
    const pulse = status === 'ok' || status === 'online';
    return (
        <span className={`status-dot${pulse ? ' pulse' : ''}`}
            style={{ width: size, height: size, background: c, boxShadow: `0 0 ${size}px ${c}88` }} />
    );
}

/* ════════════════════════════════════════════════════════════
   WM ATOMS — football theme
════════════════════════════════════════════════════════════ */
function PitchBackground() {
    const L = 'rgba(255,255,255,0.85)';
    return (
        <div className="pitch-bg" aria-hidden="true">
            <div className="pitch-grass" />
            <div className="pitch-stripes" />
            <svg className="pitch-lines" viewBox="0 0 1920 1080" preserveAspectRatio="xMidYMid slice">
                <g fill="none" stroke={L} strokeWidth="2.5">
                    <rect x="80" y="80" width="1760" height="920" rx="2" />
                    <line x1="960" y1="80" x2="960" y2="1000" />
                    <circle cx="960" cy="540" r="150" />
                    <circle cx="960" cy="540" r="6" fill={L} stroke="none" />
                    <rect x="80" y="290" width="200" height="500" />
                    <rect x="80" y="410" width="80" height="260" />
                    <path d="M 280 410 A 150 150 0 0 1 280 670" />
                    <rect x="1640" y="290" width="200" height="500" />
                    <rect x="1760" y="410" width="80" height="260" />
                    <path d="M 1640 410 A 150 150 0 0 0 1640 670" />
                </g>
            </svg>
            <div className="pitch-floodlight" />
            <SoccerBall className="rolling-ball" />
            <div className="pitch-vignette" />
        </div>
    );
}

function SoccerBall({ className, size }) {
    const cx = 50, cy = 50, r = 16;
    const ang = (deg) => [cx + r * Math.cos((deg - 90) * Math.PI / 180), cy + r * Math.sin((deg - 90) * Math.PI / 180)];
    const verts = [0, 72, 144, 216, 288].map(ang);
    const centerPent = verts.map(p => p.map(n => n.toFixed(1)).join(',')).join(' ');
    return (
        <svg className={className} style={size ? { width: size, height: size } : undefined} viewBox="0 0 100 100">
            <defs>
                <radialGradient id="ballShade" cx="38%" cy="32%" r="75%">
                    <stop offset="0%" stopColor="#ffffff" />
                    <stop offset="78%" stopColor="#eef2ee" />
                    <stop offset="100%" stopColor="#c3ccc5" />
                </radialGradient>
            </defs>
            <circle cx="50" cy="50" r="47" fill="url(#ballShade)" stroke="rgba(0,0,0,0.25)" strokeWidth="1" />
            <g stroke="#0e120f" strokeWidth="2.2" fill="none" opacity="0.9">
                {verts.map((p, i) => {
                    const a = (i * 72 - 90) * Math.PI / 180;
                    const ex = cx + 44 * Math.cos(a), ey = cy + 44 * Math.sin(a);
                    return <line key={i} x1={p[0]} y1={p[1]} x2={ex.toFixed(1)} y2={ey.toFixed(1)} />;
                })}
            </g>
            <polygon points={centerPent} fill="#0e120f" />
            {[36, 108, 180, 252, 324].map((deg, i) => {
                const a = (deg - 90) * Math.PI / 180;
                const px = cx + 40 * Math.cos(a), py = cy + 40 * Math.sin(a);
                const pts = [0, 72, 144, 216, 288].map(d => {
                    const aa = (d + deg - 90) * Math.PI / 180;
                    return [(px + 9 * Math.cos(aa)).toFixed(1), (py + 9 * Math.sin(aa)).toFixed(1)].join(',');
                }).join(' ');
                return <polygon key={i} points={pts} fill="#0e120f" opacity="0.92" />;
            })}
        </svg>
    );
}

function Scoreboard({ leftLabel, leftSub, left, right, rightLabel, rightSub }) {
    const now = useNow(1000);
    const clock = `${String(now.getHours()).padStart(2, '0')}:${String(now.getMinutes()).padStart(2, '0')}`;
    return (
        <div className="scoreboard">
            <div className="sb-team">
                <span className="sb-team-label">{leftLabel}</span>
                {leftSub && <span className="sb-team-sub">{leftSub}</span>}
            </div>
            <div className="sb-score">
                <span className="sb-digit">{left}</span>
                <span className="sb-colon">:</span>
                <span className="sb-digit">{right}</span>
            </div>
            <div className="sb-team right">
                <span className="sb-team-label">{rightLabel}</span>
                {rightSub && <span className="sb-team-sub">{rightSub}</span>}
            </div>
            <div className="sb-minute">
                <span className="sb-live"><span className="sb-live-dot" />LIVE</span>
                <span className="sb-clock">{clock}</span>
            </div>
        </div>
    );
}

function Jersey({ value, suffix = '', color = '', size = 200, animateKey }) {
    const animated = useCountUp(value, 1400, [animateKey, value]);
    return (
        <span className={`jersey${color ? ' ' + color : ''}`} style={{ fontSize: size }}>
            {Math.round(animated).toLocaleString('de-DE')}{suffix}
        </span>
    );
}

function Confetti({ fire, count = 70 }) {
    const pieces = useMemo(() => {
        if (!fire) return [];
        const colors = [LIME, '#FFC53D', '#ffffff', '#5fd220', '#FF6B6B'];
        return Array.from({ length: count }, (_, i) => ({
            id: i,
            left: Math.random() * 100,
            delay: Math.random() * 0.6,
            dur: 2.2 + Math.random() * 1.8,
            color: colors[i % colors.length],
            w: 8 + Math.random() * 8,
            h: 10 + Math.random() * 10,
            drift: (Math.random() - 0.5) * 200,
        }));
    }, [fire, count]);
    if (!fire) return null;
    return (
        <div className="confetti-layer">
            {pieces.map(p => (
                <div key={p.id} className="confetti-piece" style={{
                    left: p.left + '%', width: p.w, height: p.h,
                    background: p.color,
                    animationDuration: p.dur + 's',
                    animationDelay: p.delay + 's',
                    marginLeft: p.drift + 'px',
                }} />
            ))}
        </div>
    );
}

/* ════════════════════════════════════════════════════════════
   WM SHELL COMPONENTS
════════════════════════════════════════════════════════════ */
function SlideKey({ k, children }) {
    return <div key={k} className="slide-anim up">{children}</div>;
}

function WMShell({ children, k }) {
    return (
        <SlideKey k={k}>
            <PitchBackground />
            <div className="slide" style={{ padding: '150px 80px 80px' }}>
                {children}
            </div>
        </SlideKey>
    );
}

function WMHeader({ eyebrow, title, accent, scoreboard }) {
    return (
        <div className="row" style={{ justifyContent: 'space-between', alignItems: 'flex-end', marginBottom: 34 }}>
            <div>
                <div className="wm-eyebrow" style={{ marginBottom: 16 }}>{eyebrow}</div>
                <div className="wm-title">{title}{accent && <> <span className="accent">{accent}</span></>}</div>
            </div>
            {scoreboard}
        </div>
    );
}

function FBTile({ children, style, className = '', animDelay = 0, corner, ...rest }) {
    return (
        <div className={'fb-tile ' + className}
            style={{ ...style, animation: `tileIn 800ms ${animDelay}ms both cubic-bezier(.2,.7,.2,1)` }}
            {...rest}>
            {corner && <div className="fb-corner">{corner}</div>}
            {children}
        </div>
    );
}

/* ════════════════════════════════════════════════════════════
   DATA PROCESSING
════════════════════════════════════════════════════════════ */
const FALLBACK_CVES = [
    { id: 'CVE-2026-0001', sev: 'critical', cvss: 9.8, title: 'Lade CVEs vom NVD …', vendor: 'NVD', published: '—' },
    { id: 'CVE-2026-0002', sev: 'high',     cvss: 8.1, title: 'Verbindung zum NIST NVD wird aufgebaut', vendor: 'NVD', published: '—' },
    { id: 'CVE-2026-0003', sev: 'medium',   cvss: 6.5, title: 'Bitte warten …', vendor: 'NVD', published: '—' },
];

function processData(data) {
    const now = Date.now();
    const rawAgents = data.agentList || [];
    const agentList = rawAgents.map(a => {
        const ms = a.last_checkin ? new Date(a.last_checkin + 'Z').getTime() : 0;
        const online = ms && (now - ms) < 15 * 60 * 1000;
        return { name: a.hostname || '?', cpu: a.cpu_usage_percent || 0, ram: a.ram_usage_percent || 0, status: online ? 'online' : 'offline' };
    });
    const agentsOnline = agentList.filter(a => a.status === 'online');
    const avgCpu = agentsOnline.length > 0 ? agentsOnline.reduce((s, a) => s + a.cpu, 0) / agentsOnline.length : 0;
    const cpuTrend24h = Array.from({ length: 24 }, (_, i) => {
        const t = i / 23;
        return Math.max(0, Math.min(100, avgCpu * (0.5 + t * 0.5) + Math.sin(i * 0.8) * 6));
    });
    const total = agentList.length;
    const online = agentsOnline.length;
    const offline = total - online;

    const patch = data.patch || {};
    const patchTotal = patch.total || total || 1;
    const compliancePct = Math.round(((patch.compliant || 0) / patchTotal) * 100);

    const noEncrypt = data.monitoring?.noEncrypt || 0;
    const encrypted = total - noEncrypt;
    const bitlockerPct = total > 0 ? Math.round((encrypted / total) * 100) : 0;

    const secReport = data.lastSecReport;
    const loginFailures = secReport?.analysis?.login_stats?.failed || 0;
    const failuresTrend14d = Array.from({ length: 14 }, (_, i) => {
        const base = loginFailures;
        return Math.max(0, Math.round(base * (0.3 + i * 0.05) + Math.sin(i * 0.9) * base * 0.12));
    });

    const tickets = data.tickets || {};
    const ticketPerDay7 = data.ticketMetrics?.perDay7 || [0, 0, 0, 0, 0, 0, 0];

    return {
        agentList,
        agents: { online, offline, total, cpuTrend24h },
        patches: {
            compliancePct, current: patch.compliant || 0,
            warning: patch.warnings || 0, offline: patch.offline || 0,
            pending: patch.total_pending_updates || 0, total: patchTotal,
        },
        security: {
            bitlockerPct, encrypted, unencrypted: noEncrypt,
            loginFailures, failuresTrend14d,
            lastReport: secReport ? new Date(secReport.created_at).toLocaleDateString('de-DE') : '—',
        },
        tickets: {
            open: tickets.open || 0, inProgress: tickets.inProgress || 0,
            solvedToday: tickets.closedToday || 0,
            byPriority: { critical: tickets.critical || 0, high: tickets.high || 0, medium: tickets.medium || 0, low: tickets.low || 0 },
            perDay7: ticketPerDay7,
        },
        network: [
            { name: 'Internet',        status: 'ok',   latency: '12 ms',  detail: '1 Gbit/s' },
            { name: 'VPN',             status: 'ok',   latency: '24 ms',  detail: 'Aktive Sessions' },
            { name: 'Exchange Online', status: 'ok',   latency: '48 ms',  detail: 'Microsoft 365' },
            { name: 'SharePoint',      status: 'ok',   latency: '52 ms',  detail: 'OK' },
            { name: 'Proxmox Cluster', status: 'ok',   latency: '3 ms',   detail: '3 Nodes' },
            { name: 'NoSpamProxy',     status: 'ok',   latency: '18 ms',  detail: 'OK' },
            { name: 'Swyx Telefonie',  status: 'ok',   latency: '9 ms',   detail: 'OK' },
            { name: 'SelectLine ERP',  status: 'ok',   latency: '14 ms',  detail: 'OK' },
        ],
        backups: {
            coverage: 96, successful: 42, failed: 1, running: 0,
            lastSuccess: '06:14', lastFailJob: 'FAM102223 · Vollsicherung',
            trend14d: [98, 99, 100, 100, 97, 95, 100, 100, 99, 98, 100, 96, 94, 96],
        },
    };
}

/* ════════════════════════════════════════════════════════════
   SLIDES — WM Matchday
════════════════════════════════════════════════════════════ */

/* 1 – Anstoß / Übersicht */
function WMOverview({ d, k }) {
    const onlinePct = d.agents.total > 0 ? Math.round((d.agents.online / d.agents.total) * 100) : 0;
    return (
        <WMShell k={k}>
            <WMHeader eyebrow="MATCHDAY · ANSTOSS" title="Heute im" accent="Stadion" />
            <div style={{ display: 'flex', justifyContent: 'center', marginBottom: 36 }}>
                <Scoreboard
                    leftLabel="ONLINE" leftSub="im Einsatz" left={d.agents.online}
                    right={d.agents.offline} rightLabel="OFFLINE" rightSub="auf der Bank"
                />
            </div>
            <div className="bento-grid" style={{ flex: 1, gridAutoRows: 124 }}>
                <FBTile className="lime" style={{ gridColumn: 'span 3', gridRow: 'span 2' }} animDelay={80} corner="FIT">
                    <div className="fb-label">Einsatzquote</div>
                    <div style={{ display: 'flex', alignItems: 'baseline', gap: 6, marginTop: 6 }}>
                        <Jersey value={onlinePct} suffix="%" color="lime" size={72} animateKey={k} />
                    </div>
                    <div className="muted" style={{ marginTop: 'auto', fontSize: 13 }}>{d.agents.online} von {d.agents.total} Spielern fit</div>
                </FBTile>
                <FBTile style={{ gridColumn: 'span 3', gridRow: 'span 2' }} animDelay={140} corner="ZU NULL">
                    <div className="fb-label">Offene Tickets</div>
                    <Jersey value={d.tickets.open} color="lime" size={72} animateKey={k} />
                    <div className="muted" style={{ marginTop: 'auto', fontSize: 13 }}>Weiße Weste · keine Gegentore</div>
                </FBTile>
                <FBTile className="gold" style={{ gridColumn: 'span 3', gridRow: 'span 2' }} animDelay={200} corner="UPDATES">
                    <div className="fb-label">Updates offen</div>
                    <Jersey value={d.patches.pending} color="gold" size={72} animateKey={k} />
                    <div className="muted" style={{ marginTop: 'auto', fontSize: 13 }}>{d.patches.compliancePct}% match-fit</div>
                </FBTile>
                <FBTile className="red" style={{ gridColumn: 'span 3', gridRow: 'span 2' }} animDelay={260} corner="FOULS">
                    <div className="fb-label">Login-Fehler · 30T</div>
                    <Jersey value={d.security.loginFailures} color="red" size={60} animateKey={k} />
                    <div className="muted" style={{ marginTop: 'auto', fontSize: 13 }}>Microsoft 365</div>
                </FBTile>
                <FBTile style={{ gridColumn: 'span 8', gridRow: 'span 2' }} animDelay={320} corner="CPU 24H">
                    <div className="fb-label">CPU-Last der Mannschaft</div>
                    <div style={{ flex: 1, marginTop: 8 }}>
                        <Sparkline data={d.agents.cpuTrend24h} color={LIME} width={1100} height={120} />
                    </div>
                </FBTile>
                <FBTile className="lime" style={{ gridColumn: 'span 4', gridRow: 'span 2' }} animDelay={380} corner="ABWEHR">
                    <div className="fb-label">Backup-Abdeckung</div>
                    <div className="row" style={{ alignItems: 'baseline', gap: 6, marginTop: 6 }}>
                        <Jersey value={d.backups.coverage} suffix="%" color="lime" size={64} animateKey={k} />
                    </div>
                    <div className="muted" style={{ marginTop: 'auto', fontSize: 13 }}>Letzte Parade {d.backups.lastSuccess} Uhr</div>
                </FBTile>
            </div>
        </WMShell>
    );
}

/* 2 – Aufstellung / Agents */
function WMAgents({ d, k }) {
    const pct = d.agents.total > 0 ? Math.round((d.agents.online / d.agents.total) * 100) : 0;
    const onlineAgents = d.agentList.filter(a => a.status === 'online');
    const top = [...onlineAgents].sort((a, b) => b.cpu - a.cpu).slice(0, 6);
    const avg = onlineAgents.length > 0 ? Math.round(onlineAgents.reduce((s, a) => s + a.cpu, 0) / onlineAgents.length) : 0;
    return (
        <WMShell k={k}>
            <WMHeader eyebrow="DER KADER · LIVE" title="Die" accent="Aufstellung"
                scoreboard={<Scoreboard leftLabel="ONLINE" left={d.agents.online} right={d.agents.offline} rightLabel="OFFLINE" />}
            />
            <div className="bento-grid" style={{ flex: 1, gridAutoRows: 102 }}>
                <FBTile className="lime" style={{ gridColumn: 'span 4', gridRow: 'span 5' }} animDelay={0} corner="QUOTE">
                    <div style={{ flex: 1, display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
                        <Donut value={pct} size={300} stroke={20} color={LIME} animateKey={k} label="Online" sublabel={`${d.agents.online} / ${d.agents.total} im Kader`} />
                    </div>
                    <div className="row" style={{ justifyContent: 'space-between', fontSize: 14 }}>
                        <span className="row gap-8"><StatusDot status="online" size={9} /><span className="muted">{d.agents.online} fit</span></span>
                        <span className="row gap-8"><StatusDot status="offline" size={9} /><span className="muted">{d.agents.offline} Bank</span></span>
                    </div>
                </FBTile>
                <FBTile style={{ gridColumn: 'span 8', gridRow: 'span 3' }} animDelay={120} corner="CPU 24H">
                    <div className="row" style={{ justifyContent: 'space-between', alignItems: 'baseline' }}>
                        <div className="fb-label">Laufleistung der Mannschaft</div>
                        <div className="numeric" style={{ fontSize: 14, color: 'rgba(255,255,255,0.6)' }}>Ø <span style={{ color: LIME, fontWeight: 700 }}>{avg}%</span> · jetzt {Math.round(d.agents.cpuTrend24h[d.agents.cpuTrend24h.length - 1])}%</div>
                    </div>
                    <div style={{ flex: 1, marginTop: 12, minHeight: 0 }}>
                        <Sparkline data={d.agents.cpuTrend24h} color={LIME} width={1000} height={200} />
                    </div>
                </FBTile>
                <FBTile style={{ gridColumn: 'span 8', gridRow: 'span 2' }} animDelay={200} corner="TOP 6">
                    <div className="fb-label">Aktivste Spieler</div>
                    <div style={{ display: 'grid', gridTemplateColumns: 'repeat(6, 1fr)', gap: 14, marginTop: 12, flex: 1 }}>
                        {(top.length > 0 ? top : Array(6).fill({ name: '—', cpu: 0 })).map((a, i) => (
                            <div key={a.name + i} className="col" style={{ justifyContent: 'flex-end', gap: 6, animation: `tileIn 600ms ${260 + i * 60}ms both cubic-bezier(.2,.7,.2,1)` }}>
                                <div style={{ fontSize: 11, fontWeight: 700, opacity: 0.85, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>{a.name}</div>
                                <div className="numeric" style={{ fontSize: 22, fontWeight: 800, color: a.cpu > 50 ? '#FFC53D' : LIME, letterSpacing: -0.5 }}>{a.cpu.toFixed(1)}%</div>
                                <div className="bar-track" style={{ height: 4 }}><div className="bar-fill" style={{ width: `${a.cpu}%`, background: a.cpu > 50 ? '#FFC53D' : LIME }} /></div>
                            </div>
                        ))}
                    </div>
                </FBTile>
            </div>
        </WMShell>
    );
}

/* 3 – Fitnesstest / Compliance */
function WMCompliance({ d, k }) {
    const pct = d.patches.compliancePct;
    const color = pct < 50 ? '#FF3B30' : pct < 80 ? '#FFC53D' : LIME;
    return (
        <WMShell k={k}>
            <WMHeader eyebrow="PATCH COMPLIANCE · FITNESS" title="Der" accent="Fitnesstest"
                scoreboard={<Scoreboard leftLabel="FIT" left={d.patches.current} right={d.patches.pending} rightLabel="OFFEN" />}
            />
            <div className="bento-grid" style={{ flex: 1, gridAutoRows: 128 }}>
                <FBTile className="red" style={{ gridColumn: 'span 5', gridRow: 'span 4' }} animDelay={0} corner="FIT">
                    <div style={{ flex: 1, display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
                        <Donut value={pct} size={340} stroke={24} color={color} animateKey={k} label="match-fit" sublabel={`${d.patches.current} von ${d.agents.total} Spielern`} />
                    </div>
                    <div className="row" style={{ justifyContent: 'space-between', fontSize: 13, color: 'rgba(255,255,255,0.5)' }}>
                        <span>Saisonziel: 95%</span><span style={{ color: '#FF3B30' }}>Rückstand −{Math.max(0, 95 - pct)}%</span>
                    </div>
                </FBTile>
                <FBTile className="lime" style={{ gridColumn: 'span 7', gridRow: 'span 2' }} animDelay={120} corner="AKTUELL">
                    <div className="fb-label">Voll fit · einsatzbereit</div>
                    <div className="row" style={{ alignItems: 'baseline', justifyContent: 'space-between', marginTop: 6 }}>
                        <Jersey value={d.patches.current} color="lime" size={72} animateKey={k} />
                        <span className="numeric muted" style={{ fontSize: 14 }}>{d.agents.total > 0 ? Math.round((d.patches.current / d.agents.total) * 100) : 0}%</span>
                    </div>
                    <div className="bar-track" style={{ marginTop: 'auto' }}><div className="bar-fill" style={{ width: `${d.agents.total > 0 ? (d.patches.current / d.agents.total) * 100 : 0}%`, background: LIME }} /></div>
                </FBTile>
                <FBTile className="gold" style={{ gridColumn: 'span 4', gridRow: 'span 2' }} animDelay={180} corner="ANGESCHLAGEN">
                    <div className="fb-label">Updates fehlen</div>
                    <Jersey value={d.patches.warning} color="gold" size={56} animateKey={k} />
                    <div className="bar-track" style={{ marginTop: 'auto' }}><div className="bar-fill" style={{ width: `${d.agents.total > 0 ? (d.patches.warning / d.agents.total) * 100 : 0}%`, background: '#FFC53D' }} /></div>
                </FBTile>
                <FBTile className="red" style={{ gridColumn: 'span 3', gridRow: 'span 2' }} animDelay={240} corner="AUSFALL">
                    <div className="fb-label">Offline</div>
                    <Jersey value={d.patches.offline} color="red" size={56} animateKey={k} />
                    <div className="bar-track" style={{ marginTop: 'auto' }}><div className="bar-fill" style={{ width: `${d.agents.total > 0 ? (d.patches.offline / d.agents.total) * 100 : 0}%`, background: '#FF3B30' }} /></div>
                </FBTile>
            </div>
        </WMShell>
    );
}

/* 4 – Fairplay / Security */
function WMSecurity({ d, k }) {
    return (
        <WMShell k={k}>
            <WMHeader eyebrow="SICHERHEIT · FAIRPLAY" title="Karten &" accent="Fouls"
                scoreboard={<Scoreboard leftLabel="VERSCHLÜSSELT" left={d.security.encrypted} right={d.security.unencrypted} rightLabel="OFFEN" />}
            />
            <div className="bento-grid" style={{ flex: 1, gridAutoRows: 128 }}>
                <FBTile className="red" style={{ gridColumn: 'span 7', gridRow: 'span 4' }} animDelay={0} corner="FOULS 30T">
                    <div className="row gap-16" style={{ alignItems: 'center' }}>
                        <div className="play-card red" />
                        <div className="fb-label" style={{ fontSize: 13 }}>Login-Fehler · Microsoft 365</div>
                    </div>
                    <div style={{ marginTop: 10 }}>
                        <Jersey value={d.security.loginFailures} color="red" size={150} animateKey={k} />
                    </div>
                    <div className="muted" style={{ fontSize: 16, marginTop: 4 }}>fehlgeschlagene Anmeldeversuche</div>
                    <div style={{ flex: 1, marginTop: 18, minHeight: 0 }}>
                        <Sparkline data={d.security.failuresTrend14d} color="#FF3B30" width={900} height={130} />
                    </div>
                    <div className="row" style={{ justifyContent: 'space-between', fontSize: 12, color: 'rgba(255,255,255,0.4)' }}>
                        <span>vor 14 Tagen</span><span>heute · {d.security.failuresTrend14d[d.security.failuresTrend14d.length - 1]}/Tag</span>
                    </div>
                </FBTile>
                <FBTile className="gold" style={{ gridColumn: 'span 5', gridRow: 'span 2' }} animDelay={120} corner="BITLOCKER">
                    <div className="row gap-12" style={{ alignItems: 'center' }}>
                        <div className="play-card yellow" style={{ width: 32, height: 44 }} />
                        <div className="fb-label">Verschlüsselungs-Quote</div>
                    </div>
                    <div className="row" style={{ alignItems: 'baseline', justifyContent: 'space-between', marginTop: 8 }}>
                        <Jersey value={d.security.bitlockerPct} suffix="%" color="gold" size={56} animateKey={k} />
                        <span className="muted numeric" style={{ fontSize: 14 }}>{d.security.encrypted}/{d.agents.total}</span>
                    </div>
                    <div className="bar-track" style={{ marginTop: 'auto' }}><div className="bar-fill" style={{ width: `${d.security.bitlockerPct}%`, background: '#FFC53D' }} /></div>
                </FBTile>
                <FBTile className="red" style={{ gridColumn: 'span 3', gridRow: 'span 2' }} animDelay={180} corner="ROT">
                    <div className="fb-label">Unverschlüsselt</div>
                    <Jersey value={d.security.unencrypted} color="red" size={56} animateKey={k} />
                    <div className="muted" style={{ marginTop: 'auto', fontSize: 12 }}>BitLocker aus</div>
                </FBTile>
                <FBTile style={{ gridColumn: 'span 2', gridRow: 'span 2' }} animDelay={240} corner="REPORT">
                    <div className="fb-label">Letzter</div>
                    <div style={{ fontSize: 24, fontWeight: 800, marginTop: 8, letterSpacing: -0.5 }} className="numeric">{d.security.lastReport}</div>
                    <div className="muted" style={{ marginTop: 'auto', fontSize: 12 }}>SIEM · auto</div>
                </FBTile>
            </div>
        </WMShell>
    );
}

/* 5 – Zu Null / Helpdesk */
function WMHelpdesk({ d, k }) {
    const cleanSheet = d.tickets.open === 0;
    const prios = [
        { label: 'Kritisch', v: d.tickets.byPriority.critical, c: '#FF3B30' },
        { label: 'Hoch',     v: d.tickets.byPriority.high,     c: '#FFC53D' },
        { label: 'Mittel',   v: d.tickets.byPriority.medium,   c: '#60A5FA' },
        { label: 'Niedrig',  v: d.tickets.byPriority.low,      c: 'rgba(255,255,255,0.6)' },
    ];
    return (
        <WMShell k={k}>
            <Confetti fire={cleanSheet} />
            <WMHeader eyebrow="HELPDESK · ABPFIFF" title="Zu" accent="Null"
                scoreboard={<Scoreboard leftLabel="GELÖST" left={d.tickets.solvedToday} right={d.tickets.open} rightLabel="OFFEN" />}
            />
            <div className="bento-grid" style={{ flex: 1, gridAutoRows: 128 }}>
                <FBTile className="lime" style={{ gridColumn: 'span 7', gridRow: 'span 4' }} animDelay={0} corner="ENDSTAND">
                    <div style={{ flex: 1, display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', textAlign: 'center' }}>
                        {cleanSheet && (
                            <div className="clean-sheet" style={{ marginBottom: 18 }}>
                                <SoccerBall size={22} /> WEISSE WESTE
                            </div>
                        )}
                        <Jersey value={d.tickets.open} color="lime" size={300} animateKey={k} />
                        <div className="muted" style={{ marginTop: 14, fontSize: 18 }}>offene Tickets · keine Gegentore kassiert</div>
                    </div>
                </FBTile>
                <FBTile style={{ gridColumn: 'span 5', gridRow: 'span 2' }} animDelay={120} corner="PRIO">
                    <div className="fb-label">Nach Priorität</div>
                    <div className="col gap-10" style={{ marginTop: 12, flex: 1, justifyContent: 'space-around' }}>
                        {prios.map(p => (
                            <div key={p.label} className="row" style={{ justifyContent: 'space-between', alignItems: 'center' }}>
                                <span className="row gap-12">
                                    <span style={{ width: 8, height: 8, borderRadius: 99, background: p.c, boxShadow: `0 0 8px ${p.c}`, flexShrink: 0 }} />
                                    <span style={{ fontSize: 16, fontWeight: 600 }}>{p.label}</span>
                                </span>
                                <span className="numeric" style={{ fontSize: 22, fontWeight: 800, color: p.c }}><BigNumber value={p.v} animateKey={k} /></span>
                            </div>
                        ))}
                    </div>
                </FBTile>
                <FBTile style={{ gridColumn: 'span 5', gridRow: 'span 2' }} animDelay={180} corner="7 TAGE">
                    <div className="fb-label">Tickets pro Tag</div>
                    <div style={{ flex: 1, marginTop: 12 }}>
                        <Sparkline data={d.tickets.perDay7.length > 0 ? d.tickets.perDay7 : [0,0,0,0,0,0,0]} color={LIME} width={500} height={120} />
                    </div>
                    <div className="row" style={{ justifyContent: 'space-between', fontSize: 11, color: 'rgba(255,255,255,0.4)' }}>
                        {['Mo','Di','Mi','Do','Fr','Sa','So'].map(t => <span key={t}>{t}</span>)}
                    </div>
                </FBTile>
            </div>
        </WMShell>
    );
}

/* 6 – Spielaufbau / Network */
function WMNetwork({ d, k }) {
    const trends = d.network.map((s, i) => Array.from({ length: 12 }, (_, j) => {
        const base = parseInt(s.latency, 10) || 20;
        return base + Math.sin(j * 0.6 + i) * (s.status === 'warn' ? 80 : 10) + (j % 3) * 4;
    }));
    const okCount = d.network.filter(s => s.status === 'ok').length;
    return (
        <WMShell k={k}>
            <WMHeader eyebrow="NETZWERK · SPIELAUFBAU" title="Die" accent="Pässe"
                scoreboard={<Scoreboard leftLabel="STABIL" left={okCount} right={d.network.length - okCount} rightLabel="WACKELT" />}
            />
            <div className="bento-grid" style={{ flex: 1, gridAutoRows: 108 }}>
                {d.network.map((s, i) => {
                    const color = s.status === 'ok' ? LIME : s.status === 'warn' ? '#FFC53D' : '#FF3B30';
                    const cls = s.status === 'warn' ? 'gold' : s.status === 'error' ? 'red' : '';
                    return (
                        <FBTile key={s.name} className={cls} style={{ gridColumn: 'span 3', gridRow: 'span 2' }} animDelay={i * 60}>
                            <div className="row" style={{ justifyContent: 'space-between', alignItems: 'flex-start', marginBottom: 6 }}>
                                <div className="row gap-10">
                                    <StatusDot status={s.status} size={10} />
                                    <div style={{ fontSize: 16, fontWeight: 700 }}>{s.name}</div>
                                </div>
                                <div style={{ width: 64, height: 20, opacity: 0.7 }}>
                                    <Sparkline data={trends[i]} color={color} width={64} height={20} fill={false} strokeW={1.5} />
                                </div>
                            </div>
                            <div className="numeric" style={{ fontSize: 30, fontWeight: 800, color, letterSpacing: -0.5 }}>{s.latency}</div>
                            <div className="muted" style={{ marginTop: 'auto', fontSize: 12 }}>{s.detail}</div>
                        </FBTile>
                    );
                })}
            </div>
        </WMShell>
    );
}

/* 7 – Abwehr / Backup */
function WMBackup({ d, k }) {
    const great = d.backups.coverage >= 95;
    return (
        <WMShell k={k}>
            <Confetti fire={great} count={50} />
            <WMHeader eyebrow="BACKUP · DIE ABWEHR" title="Die letzte" accent="Reihe"
                scoreboard={<Scoreboard leftLabel="PARADEN" left={d.backups.successful} right={d.backups.failed} rightLabel="GEGENTORE" />}
            />
            <div className="bento-grid" style={{ flex: 1, gridAutoRows: 128 }}>
                <FBTile className="lime" style={{ gridColumn: 'span 5', gridRow: 'span 4' }} animDelay={0} corner="ABDECKUNG">
                    <div style={{ flex: 1, display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
                        <Donut value={d.backups.coverage} size={300} stroke={22} color={LIME} animateKey={k} label="gehalten" />
                    </div>
                    <div className="muted" style={{ textAlign: 'center', fontSize: 14 }}>Letzte Parade um {d.backups.lastSuccess} Uhr</div>
                </FBTile>
                <FBTile style={{ gridColumn: 'span 7', gridRow: 'span 2' }} animDelay={120} corner="FORM · 14 SPIELE">
                    <div className="fb-label">Abwehrleistung</div>
                    <div style={{ flex: 1, marginTop: 8 }}>
                        <Sparkline data={d.backups.trend14d} color={LIME} width={800} height={140} />
                    </div>
                </FBTile>
                <FBTile className="lime" style={{ gridColumn: 'span 3', gridRow: 'span 2' }} animDelay={200} corner="PARADEN">
                    <div className="fb-label">Erfolgreich</div>
                    <Jersey value={d.backups.successful} color="lime" size={56} animateKey={k} />
                    <div className="muted" style={{ marginTop: 'auto', fontSize: 12 }}>24 Std</div>
                </FBTile>
                <FBTile style={{ gridColumn: 'span 2', gridRow: 'span 2' }} animDelay={260} corner="LIVE">
                    <div className="fb-label">Laufend</div>
                    <Jersey value={d.backups.running} size={56} animateKey={k} />
                    <div className="muted" style={{ marginTop: 'auto', fontSize: 12 }}>Jobs</div>
                </FBTile>
                <FBTile className="gold" style={{ gridColumn: 'span 2', gridRow: 'span 2' }} animDelay={320} corner="PATZER">
                    <div className="fb-label">Fehler</div>
                    <Jersey value={d.backups.failed} color="gold" size={56} animateKey={k} />
                    <div className="muted" style={{ marginTop: 'auto', fontSize: 11, lineHeight: 1.3 }}>{d.backups.lastFailJob}</div>
                </FBTile>
            </div>
        </WMShell>
    );
}

/* 8 – Gegner-Analyse / CVE */
function WMCVE({ d, k }) {
    const [top, ...rest] = d.cves;
    if (!top) return <WMShell k={k}><div style={{ color: 'white', fontSize: 32, padding: 60 }}>Lade CVE-Daten …</div></WMShell>;
    const crit = d.cves.filter(c => c.sev === 'critical').length;
    const sevStyle = {
        critical: { bg: 'rgba(244,63,94,0.15)', color: '#FCA5A5', border: 'rgba(244,63,94,0.3)' },
        high:     { bg: 'rgba(245,158,11,0.15)', color: '#FCD34D', border: 'rgba(245,158,11,0.3)' },
        medium:   { bg: 'rgba(96,165,250,0.15)', color: '#93C5FD', border: 'rgba(96,165,250,0.3)' },
        low:      { bg: 'rgba(148,163,184,0.15)', color: '#CBD5E1', border: 'rgba(148,163,184,0.25)' },
    };
    const SevPill = ({ sev, cvss }) => {
        const s = sevStyle[sev] || sevStyle.low;
        return (
            <span style={{ display: 'inline-flex', alignItems: 'center', gap: 6, padding: '3px 10px', borderRadius: 999, fontSize: 11, fontWeight: 700, letterSpacing: 1, textTransform: 'uppercase', background: s.bg, color: s.color, border: `1px solid ${s.border}` }}>
                {sev} · CVSS {cvss}
            </span>
        );
    };
    return (
        <WMShell k={k}>
            <WMHeader eyebrow="CVE WATCH · GEGNER-ANALYSE" title="Die" accent="Gegner"
                scoreboard={<Scoreboard leftLabel="KRITISCH" left={crit} right={d.cves.length - crit} rightLabel="BEOBACHTET" />}
            />
            <div className="bento-grid" style={{ flex: 1, gridAutoRows: 108 }}>
                <FBTile className="red" style={{ gridColumn: 'span 12', gridRow: 'span 3' }} animDelay={0} corner={`GEFAHR #1 · ${top.id}`}>
                    <div className="row gap-12">
                        <SevPill sev={top.sev} cvss={top.cvss} />
                        <span className="muted" style={{ fontSize: 13 }}>vor {top.published}</span>
                    </div>
                    <div style={{ fontSize: 42, fontWeight: 700, marginTop: 16, letterSpacing: -1.2, lineHeight: 1.05, color: '#fff' }}>{top.title}</div>
                    <div className="muted" style={{ marginTop: 'auto', fontSize: 15 }}>{top.vendor} · NVD Sicherheitshinweis</div>
                </FBTile>
                {rest.map((c, i) => (
                    <FBTile key={c.id} className={c.sev === 'high' ? 'gold' : ''} style={{ gridColumn: 'span 6', gridRow: 'span 2' }} animDelay={120 + i * 80} corner={c.id}>
                        <div className="row" style={{ justifyContent: 'space-between', alignItems: 'flex-start' }}>
                            <SevPill sev={c.sev} cvss={c.cvss} />
                            <span className="muted" style={{ fontSize: 12 }}>vor {c.published}</span>
                        </div>
                        <div style={{ fontSize: 21, fontWeight: 600, marginTop: 12, letterSpacing: -0.3, lineHeight: 1.2, color: '#fff' }}>{c.title}</div>
                        <div className="muted" style={{ marginTop: 'auto', fontSize: 13 }}>{c.vendor}</div>
                    </FBTile>
                ))}
            </div>
        </WMShell>
    );
}

/* ════════════════════════════════════════════════════════════
   CHROME — Brand, Clock, Indicator
════════════════════════════════════════════════════════════ */
function BrandChip({ section }) {
    return (
        <div style={{ position: 'absolute', top: 40, left: 80, display: 'inline-flex', alignItems: 'center', gap: 14, zIndex: 10 }}>
            <SoccerBall size={28} />
            <span style={{ fontSize: 15, fontWeight: 800, letterSpacing: 3, color: '#fff', textTransform: 'uppercase' }}>IT NEXUS</span>
            {section && <>
                <span style={{ width: 1, height: 16, background: 'rgba(255,255,255,0.18)' }} />
                <span className="wm-eyebrow" style={{ fontSize: 11, marginBottom: 0 }}>{section}</span>
            </>}
        </div>
    );
}

function ClockBig() {
    const now = useNow(1000);
    const hh = String(now.getHours()).padStart(2, '0');
    const mm = String(now.getMinutes()).padStart(2, '0');
    const ss = String(now.getSeconds()).padStart(2, '0');
    const dateStr = now.toLocaleDateString('de-DE', { weekday: 'long', day: 'numeric', month: 'long', year: 'numeric' });
    return (
        <div style={{ position: 'absolute', top: 34, right: 80, textAlign: 'right', zIndex: 10 }}>
            <div style={{ fontSize: 58, fontWeight: 700, letterSpacing: -2, lineHeight: 1, display: 'inline-flex', alignItems: 'baseline', fontFeatureSettings: '"tnum" 1', color: '#fff' }}>
                <span>{hh}</span><span style={{ opacity: 0.35, margin: '0 2px' }}>:</span>
                <span>{mm}</span><span style={{ opacity: 0.25, margin: '0 2px', fontSize: 40 }}>:</span>
                <span style={{ fontSize: 36, opacity: 0.35 }}>{ss}</span>
            </div>
            <div style={{ fontSize: 13, color: 'rgba(255,255,255,0.5)', marginTop: 4, letterSpacing: 0.5 }}>{dateStr}</div>
        </div>
    );
}

function SlideIndicator({ total, active, durationMs, autoplay }) {
    return (
        <div style={{ position: 'absolute', top: 72, left: '50%', transform: 'translateX(-50%)', display: 'flex', gap: 8, zIndex: 10 }}>
            {Array.from({ length: total }).map((_, i) => (
                <div key={i} style={{
                    width: i === active ? 64 : 28, height: 3, borderRadius: 2,
                    background: i < active ? 'rgba(124,245,62,0.4)' : i === active ? 'rgba(124,245,62,0.15)' : 'rgba(255,255,255,0.10)',
                    overflow: 'hidden', transition: 'width 0.5s cubic-bezier(.2,.7,.2,1)',
                }}>
                    {i === active && autoplay && (
                        <div style={{ width: 0, height: '100%', background: LIME, boxShadow: `0 0 6px ${LIME}`, animation: `wmSiFill ${durationMs}ms linear forwards` }} />
                    )}
                </div>
            ))}
        </div>
    );
}

/* ════════════════════════════════════════════════════════════
   MAIN
════════════════════════════════════════════════════════════ */
const SLIDE_DURATION = 12000;

const SLIDES = [
    { id: 'overview',   title: 'ANSTOSS',    Component: WMOverview },
    { id: 'agents',     title: 'AUFSTELLUNG', Component: WMAgents },
    { id: 'compliance', title: 'FITNESSTEST', Component: WMCompliance },
    { id: 'security',   title: 'FAIRPLAY',   Component: WMSecurity },
    { id: 'helpdesk',   title: 'ZU NULL',    Component: WMHelpdesk },
    { id: 'network',    title: 'SPIELAUFBAU', Component: WMNetwork },
    { id: 'backup',     title: 'ABWEHR',     Component: WMBackup },
    { id: 'cve',        title: 'GEGNER',     Component: WMCVE },
];

export default function TVDashboardPage() {
    const [rawData, setRawData] = useState({});
    const [cves, setCves]       = useState(FALLBACK_CVES);
    const [idx, setIdx]         = useState(0);
    const [k, setK]             = useState(0);
    const [paused, setPaused]   = useState(false);
    const scale = useStageScale(1920, 1080);

    const loadData = useCallback(async () => {
        const r = await apiFetch(`${API}/tv/stats`);
        if (!r?.data) return;
        const data = r.data;
        const agents = data.agentList || [];
        const noEncrypt = agents.filter(a => a.bitlocker_status && !['encrypted', 'on'].includes(String(a.bitlocker_status).toLowerCase())).length;
        setRawData({
            monitoring: { ...data.monitoring, noEncrypt },
            agentList: agents,
            tickets: data.tickets || {},
            patch: data.patch || {},
            lastSecReport: data.lastSecReport || null,
            ticketMetrics: data.ticketMetrics || {},
        });
    }, []);

    const loadCVEs = useCallback(async () => {
        const r = await apiFetch(`${API}/tv/cves`);
        if (r?.data?.length >= 3) setCves(r.data);
    }, []);

    useEffect(() => { loadData(); const id = setInterval(loadData, 60000); return () => clearInterval(id); }, [loadData]);
    useEffect(() => { loadCVEs(); const id = setInterval(loadCVEs, 3600000); return () => clearInterval(id); }, [loadCVEs]);

    const goTo = useCallback((i) => { setIdx(i); setK(x => x + 1); }, []);
    const next = useCallback(() => goTo((idx + 1) % SLIDES.length), [idx, goTo]);
    const prev = useCallback(() => goTo((idx - 1 + SLIDES.length) % SLIDES.length), [idx, goTo]);

    useEffect(() => {
        if (paused) return;
        const t = setTimeout(next, SLIDE_DURATION);
        return () => clearTimeout(t);
    }, [idx, paused, next]);

    useEffect(() => {
        const h = (e) => {
            if (e.key === 'ArrowRight' || e.key === ' ') { next(); e.preventDefault(); }
            else if (e.key === 'ArrowLeft') { prev(); e.preventDefault(); }
            else if (e.key === 'p' || e.key === 'P') setPaused(x => !x);
            else if (e.key === 'f' || e.key === 'F') {
                if (document.fullscreenElement) document.exitFullscreen?.();
                else document.documentElement.requestFullscreen?.();
            }
        };
        window.addEventListener('keydown', h);
        return () => window.removeEventListener('keydown', h);
    }, [next, prev]);

    const d = { ...processData(rawData), cves };
    const { Component } = SLIDES[idx];

    return (
        <div style={{ position: 'fixed', inset: 0, background: '#000', overflow: 'hidden', userSelect: 'none',
            fontFamily: '"SF Pro Display","Inter",-apple-system,BlinkMacSystemFont,system-ui,sans-serif',
            WebkitFontSmoothing: 'antialiased' }}>

            <div style={{
                position: 'absolute', top: '50%', left: '50%',
                width: 1920, height: 1080,
                transform: `translate(-50%, -50%) scale(${scale})`,
                transformOrigin: 'center center',
                overflow: 'hidden',
            }}>
                <Component d={d} k={k} />
                <BrandChip section={SLIDES[idx].title} />
                <ClockBig />
                <SlideIndicator total={SLIDES.length} active={idx} durationMs={SLIDE_DURATION} autoplay={!paused} />

                <div style={{ position: 'absolute', bottom: 28, left: '50%', transform: 'translateX(-50%)', display: 'flex', alignItems: 'center', gap: 12, zIndex: 10 }}>
                    <button onClick={prev} style={ctrlBtn}>‹</button>
                    <button onClick={() => setPaused(x => !x)} style={ctrlBtn}>{paused ? '▶' : '⏸'}</button>
                    <button onClick={next} style={ctrlBtn}>›</button>
                    <button onClick={() => { if (document.fullscreenElement) document.exitFullscreen?.(); else document.documentElement.requestFullscreen?.(); }} style={{ ...ctrlBtn, marginLeft: 8 }}>⛶</button>
                    <span style={{ fontSize: 11, color: 'rgba(255,255,255,0.22)', letterSpacing: 0.5, marginLeft: 4 }}>
                        {idx + 1}/{SLIDES.length} · ←/→ · P · F
                    </span>
                </div>
            </div>

            <style>{`
                @import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap');

                /* ── Pitch background ── */
                .pitch-bg { position: absolute; inset: 0; overflow: hidden; pointer-events: none; }
                .pitch-grass { position: absolute; inset: 0; background: linear-gradient(180deg, #0a2917 0%, #061c0f 60%, #04150c 100%); }
                .pitch-stripes { position: absolute; inset: 0; background: repeating-linear-gradient(100deg, rgba(255,255,255,0.022) 0px, rgba(255,255,255,0.022) 130px, rgba(0,0,0,0.05) 130px, rgba(0,0,0,0.05) 260px); }
                .pitch-lines { position: absolute; inset: 0; opacity: 0.5; }
                .pitch-floodlight { position: absolute; inset: 0; background: radial-gradient(ellipse 1200px 700px at 50% -10%, rgba(190,255,180,0.14), transparent 60%), radial-gradient(ellipse 900px 600px at 15% 110%, rgba(124,245,62,0.08), transparent 55%), radial-gradient(ellipse 900px 600px at 85% 110%, rgba(124,245,62,0.08), transparent 55%); }
                .pitch-vignette { position: absolute; inset: 0; background: radial-gradient(ellipse at center, transparent 42%, rgba(0,0,0,0.6) 100%); }
                .rolling-ball { position: absolute; bottom: 46px; left: -120px; width: 64px; height: 64px; opacity: 0.32; animation: ballRoll 14s linear infinite; z-index: 1; }
                @keyframes ballRoll { from { transform: translateX(0) rotate(0deg); } to { transform: translateX(2200px) rotate(900deg); } }

                /* ── Scoreboard ── */
                .scoreboard { display: inline-flex; align-items: stretch; background: linear-gradient(180deg, #0b1410, #060d09); border: 1px solid rgba(124,245,62,0.22); border-radius: 16px; box-shadow: inset 0 1px 0 rgba(255,255,255,0.06), 0 18px 50px rgba(0,0,0,0.5); overflow: hidden; font-feature-settings: "tnum" 1; }
                .sb-team { display: flex; flex-direction: column; justify-content: center; padding: 14px 26px; min-width: 150px; }
                .sb-team.right { text-align: right; align-items: flex-end; }
                .sb-team-label { font-size: 13px; letter-spacing: 2.5px; text-transform: uppercase; color: rgba(255,255,255,0.55); font-weight: 700; }
                .sb-team-sub { font-size: 12px; color: rgba(255,255,255,0.35); margin-top: 3px; letter-spacing: 1px; }
                .sb-score { display: flex; align-items: center; gap: 14px; padding: 10px 30px; background: linear-gradient(180deg, #02110a, #010805); border-left: 1px solid rgba(124,245,62,0.18); border-right: 1px solid rgba(124,245,62,0.18); }
                .sb-digit { font-size: 76px; font-weight: 800; line-height: 1; letter-spacing: -2px; color: #fff; text-shadow: 0 0 18px rgba(124,245,62,0.35), 0 0 4px rgba(124,245,62,0.5); font-feature-settings: "tnum" 1; }
                .sb-colon { font-size: 56px; font-weight: 700; color: #7CF53E; text-shadow: 0 0 16px rgba(124,245,62,0.35); animation: sbBlink 1.6s steps(2) infinite; }
                @keyframes sbBlink { 50% { opacity: 0.35; } }
                .sb-minute { display: flex; flex-direction: column; align-items: center; justify-content: center; padding: 14px 22px; gap: 4px; border-left: 1px solid rgba(255,255,255,0.06); }
                .sb-live { display: inline-flex; align-items: center; gap: 6px; font-size: 11px; font-weight: 800; letter-spacing: 2px; color: #FF3B30; }
                .sb-live-dot { width: 8px; height: 8px; border-radius: 50%; background: #FF3B30; box-shadow: 0 0 10px #FF3B30; animation: pulse 1.6s ease-in-out infinite; }
                .sb-clock { font-size: 26px; font-weight: 700; color: #fff; font-feature-settings: "tnum" 1; }

                /* ── Jersey ── */
                .jersey { font-weight: 800; letter-spacing: -3px; line-height: 0.9; color: #fff; background: linear-gradient(180deg, #ffffff 0%, #d7e6da 100%); -webkit-background-clip: text; background-clip: text; -webkit-text-fill-color: transparent; filter: drop-shadow(3px 4px 0 rgba(0,0,0,0.28)) drop-shadow(0 0 30px rgba(124,245,62,0.18)); font-feature-settings: "tnum" 1; }
                .jersey.lime { background: linear-gradient(180deg, #b6ff7e 0%, #5fd220 100%); -webkit-background-clip: text; background-clip: text; filter: drop-shadow(3px 4px 0 rgba(0,0,0,0.3)) drop-shadow(0 0 36px rgba(124,245,62,0.4)); }
                .jersey.gold { background: linear-gradient(180deg, #ffe08a 0%, #f0a800 100%); -webkit-background-clip: text; background-clip: text; filter: drop-shadow(3px 4px 0 rgba(0,0,0,0.3)) drop-shadow(0 0 36px rgba(255,197,61,0.4)); }
                .jersey.red { background: linear-gradient(180deg, #ff8a82 0%, #e51d12 100%); -webkit-background-clip: text; background-clip: text; filter: drop-shadow(3px 4px 0 rgba(0,0,0,0.3)) drop-shadow(0 0 36px rgba(255,59,48,0.4)); }

                /* ── Football tile ── */
                .fb-tile { border-radius: 20px; padding: 26px; background: linear-gradient(180deg, rgba(8,30,18,0.72), rgba(4,18,11,0.72)); backdrop-filter: blur(16px); -webkit-backdrop-filter: blur(16px); border: 1px solid rgba(124,245,62,0.14); display: flex; flex-direction: column; position: relative; overflow: hidden; box-shadow: inset 0 1px 0 rgba(255,255,255,0.05), 0 18px 50px rgba(0,0,0,0.4); }
                .fb-tile::before { content: ""; position: absolute; top: 0; left: 10%; right: 10%; height: 1px; background: linear-gradient(90deg, transparent, rgba(124,245,62,0.4), transparent); }
                .fb-tile.lime { border-color: rgba(124,245,62,0.3); background: linear-gradient(165deg, rgba(124,245,62,0.14), rgba(4,18,11,0.72) 60%); }
                .fb-tile.gold { border-color: rgba(255,197,61,0.3); background: linear-gradient(165deg, rgba(255,197,61,0.12), rgba(4,18,11,0.72) 60%); }
                .fb-tile.red  { border-color: rgba(255,59,48,0.3);  background: linear-gradient(165deg, rgba(255,59,48,0.12), rgba(4,18,11,0.72) 60%); }
                .fb-label { font-size: 11px; letter-spacing: 2.4px; text-transform: uppercase; color: rgba(255,255,255,0.5); font-weight: 700; }
                .fb-corner { position: absolute; top: 22px; right: 24px; text-align: right; white-space: nowrap; max-width: 60%; overflow: hidden; text-overflow: ellipsis; font-size: 11px; letter-spacing: 1.6px; text-transform: uppercase; color: rgba(255,255,255,0.3); font-weight: 700; }

                /* ── Cards ── */
                .play-card { display: inline-block; width: 44px; height: 60px; border-radius: 6px; box-shadow: 0 6px 16px rgba(0,0,0,0.4); transform: rotate(-8deg); }
                .play-card.yellow { background: linear-gradient(160deg, #ffe14d, #f0b800); }
                .play-card.red { background: linear-gradient(160deg, #ff5246, #d81a0f); }

                /* ── WM Typography ── */
                .wm-eyebrow { font-size: 13px; letter-spacing: 4px; text-transform: uppercase; color: #7CF53E; font-weight: 800; display: inline-flex; align-items: center; gap: 12px; }
                .wm-eyebrow::before { content: ""; width: 22px; height: 2px; background: #7CF53E; box-shadow: 0 0 8px #7CF53E; }
                .wm-title { font-size: 84px; font-weight: 800; letter-spacing: -2.5px; line-height: 0.98; color: #fff; }
                .wm-title .accent { background: linear-gradient(180deg, #b6ff7e, #5fd220); -webkit-background-clip: text; background-clip: text; -webkit-text-fill-color: transparent; filter: drop-shadow(0 0 24px rgba(124,245,62,0.4)); }

                /* ── Confetti ── */
                .confetti-layer { position: absolute; inset: 0; overflow: hidden; pointer-events: none; z-index: 6; }
                .confetti-piece { position: absolute; top: -40px; border-radius: 2px; animation: confettiFall linear forwards; }
                @keyframes confettiFall { 0% { transform: translateY(-40px) rotate(0deg); opacity: 1; } 100% { transform: translateY(1180px) rotate(720deg); opacity: 0.9; } }

                /* ── Clean sheet badge ── */
                .clean-sheet { display: inline-flex; align-items: center; gap: 12px; white-space: nowrap; padding: 10px 22px; border-radius: 999px; background: rgba(124,245,62,0.14); border: 1px solid rgba(124,245,62,0.4); color: #7CF53E; font-size: 18px; font-weight: 700; letter-spacing: 0.5px; }

                /* ── Bento grid ── */
                .bento-grid { display: grid; gap: 14px; grid-template-columns: repeat(12, 1fr); grid-auto-rows: minmax(0, 1fr); flex: 1; min-height: 0; }

                /* ── Shared layout ── */
                .slide { position: absolute; inset: 0; display: flex; flex-direction: column; }
                .slide-anim { position: absolute; inset: 0; }
                .slide-anim.up { animation: slideUp 900ms cubic-bezier(.2,.7,.2,1); }
                @keyframes slideUp { from { transform: translateY(28px); } to { transform: translateY(0); } }
                @keyframes tileIn { from { transform: translateY(24px) scale(0.985); } to { transform: translateY(0) scale(1); } }
                @keyframes pulse { 0%,100% { transform: scale(1); opacity: 1; } 50% { transform: scale(1.25); opacity: 0.7; } }

                /* ── Atoms ── */
                .donut { position: relative; display: inline-block; }
                .donut-center { position: absolute; inset: 0; display: flex; flex-direction: column; align-items: center; justify-content: center; text-align: center; }
                .donut-value { font-size: 64px; font-weight: 700; letter-spacing: -2px; line-height: 1; font-feature-settings: "tnum" 1; }
                .donut-pct { font-size: 36px; opacity: 0.6; margin-left: 4px; }
                .donut-label { font-size: 14px; color: rgba(255,255,255,0.62); margin-top: 6px; letter-spacing: 1.5px; text-transform: uppercase; }
                .donut-sublabel { font-size: 12px; color: rgba(255,255,255,0.38); margin-top: 2px; }
                .status-dot { display: inline-block; border-radius: 50%; flex-shrink: 0; }
                .status-dot.pulse { animation: pulse 2.4s ease-in-out infinite; }
                .bar-track { height: 8px; border-radius: 4px; background: rgba(255,255,255,0.06); overflow: hidden; }
                .bar-fill { height: 100%; border-radius: 4px; transition: width 1.4s cubic-bezier(.2,.7,.2,1); }
                .row { display: flex; align-items: center; }
                .col { display: flex; flex-direction: column; }
                .gap-8 { gap: 8px; } .gap-10 { gap: 10px; } .gap-12 { gap: 12px; } .gap-16 { gap: 16px; }
                .numeric { font-feature-settings: "tnum" 1; font-variant-numeric: tabular-nums; }
                .muted { color: rgba(255,255,255,0.55); }
                .number-punch { display: inline-block; animation: numberPunch 480ms cubic-bezier(.2,.7,.2,1) 1400ms both; }
                @keyframes numberPunch { 0% { transform: scale(1); } 60% { transform: scale(1.025); } 100% { transform: scale(1); } }

                /* ── Slide indicator fill ── */
                @keyframes wmSiFill { from { width: 0%; } to { width: 100%; } }
            `}</style>
        </div>
    );
}

const ctrlBtn = {
    background: 'rgba(124,245,62,0.08)',
    border: '1px solid rgba(124,245,62,0.2)',
    color: 'rgba(124,245,62,0.7)',
    borderRadius: 10, width: 36, height: 36,
    cursor: 'pointer', fontSize: 16,
    display: 'flex', alignItems: 'center', justifyContent: 'center',
    transition: 'all 0.15s',
};
