import React, { useState, useEffect, useCallback } 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 ACCENT   = '#4FD1C5';
const C_OK     = '#34D399';
const C_WARN   = '#F59E0B';
const C_ERROR  = '#EF4444';
const C_INFO   = '#60A5FA';
// const C_CRIT   = '#F43F5E'; // reserved for future use

/* ── Mesh palettes per slide tint ──────────────────────────── */
const MESH_PAL = {
    mint:    ['#0a3d3a','#0d4f48','#082b29','#0b1f1d'],
    red:     ['#3d0a1a','#4f0d22','#2b0810','#1f0b0e'],
    blue:    ['#0a1f3d','#0d2a4f','#08152b','#0b121f'],
    violet:  ['#22093d','#2d0c4f','#16062b','#10071f'],
    neutral: ['#0a1014','#0e1419','#070b0e','#04070a'],
};

/* ════════════════════════════════════════════════════════════
   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
════════════════════════════════════════════════════════════ */
function MeshBackground({ tint = 'neutral', intensity = 1 }) {
    const p = MESH_PAL[tint] || MESH_PAL.neutral;
    const k = intensity;
    return (
        <div style={{ position: 'absolute', inset: 0, overflow: 'hidden', pointerEvents: 'none' }}>
            <div style={{ position: 'absolute', inset: 0, background: '#000' }} />
            <div className="tv-blob tv-b1" style={{ background: p[0], opacity: 0.85 * k }} />
            <div className="tv-blob tv-b2" style={{ background: p[1], opacity: 0.7 * k }} />
            <div className="tv-blob tv-b3" style={{ background: p[2], opacity: 0.6 * k }} />
            <div className="tv-blob tv-b4" style={{ background: p[3], opacity: 0.5 * k }} />
            <div style={{ position: 'absolute', inset: 0, opacity: 0.06, mixBlendMode: 'overlay',
                backgroundImage: `url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='160' height='160'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/></filter><rect width='100%25' height='100%25' filter='url(%23n)'/></svg>")` }} />
            <div style={{ position: 'absolute', inset: 0, background: 'radial-gradient(ellipse at center, transparent 40%, rgba(0,0,0,0.55) 100%)' }} />
        </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: 56, right: 80, textAlign: 'right', fontFeatureSettings: '"tnum" 1', zIndex: 5 }}>
            <div style={{ fontSize: 76, fontWeight: 600, letterSpacing: -2.5, lineHeight: 1, display: 'inline-flex', alignItems: 'baseline' }}>
                <span>{hh}</span>
                <span style={{ opacity: 0.32, margin: '0 2px', fontWeight: 300 }}>:</span>
                <span>{mm}</span>
                <span style={{ opacity: 0.32, margin: '0 2px', fontWeight: 300, fontSize: 56 }}>:</span>
                <span style={{ fontSize: 44, opacity: 0.4, fontWeight: 400, letterSpacing: -1 }}>{ss}</span>
            </div>
            <div style={{ fontSize: 16, color: 'rgba(244,245,247,0.62)', marginTop: 10, letterSpacing: 0.4, fontWeight: 500, textTransform: 'capitalize' }}>{dateStr}</div>
        </div>
    );
}

function BrandChip({ section }) {
    return (
        <div style={{ position: 'absolute', top: 64, left: 80, display: 'inline-flex', alignItems: 'center', gap: 16, fontSize: 14, fontWeight: 700, letterSpacing: 2.2, zIndex: 5 }}>
            <span className="tv-brand-dot" style={{ background: ACCENT }} />
            <span style={{ color: '#F4F5F7' }}>IT NEXUS</span>
            {section && <>
                <span style={{ width: 1, height: 16, background: 'rgba(255,255,255,0.14)' }} />
                <span style={{ color: 'rgba(244,245,247,0.62)', letterSpacing: 1.6, fontWeight: 500, textTransform: 'uppercase' }}>{section}</span>
            </>}
        </div>
    );
}

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

function Sparkline({ data = [], width = 400, height = 80, color = ACCENT, 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 = ACCENT, 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 style={{ position: 'relative', display: 'inline-block', 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 style={{ position: 'absolute', inset: 0, display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', textAlign: 'center' }}>
                <div style={{ fontSize: size < 260 ? 44 : 56, fontWeight: 700, letterSpacing: -2, lineHeight: 1, fontFeatureSettings: '"tnum" 1' }}>
                    {Math.round(animated)}<span style={{ fontSize: size < 260 ? 24 : 32, opacity: 0.6, marginLeft: 3 }}>%</span>
                </div>
                {label && <div style={{ fontSize: 12, color: 'rgba(244,245,247,0.62)', marginTop: 5, letterSpacing: 1.5, textTransform: 'uppercase' }}>{label}</div>}
                {sublabel && <div style={{ fontSize: 11, color: 'rgba(244,245,247,0.38)', marginTop: 2 }}>{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="tv-number-punch" style={{ fontFeatureSettings: '"tnum" 1' }}>{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, info: C_INFO };
    const c = colors[status] || '#94A3B8';
    const pulse = status === 'ok' || status === 'online';
    return (
        <span className={pulse ? 'tv-dot-pulse' : ''} style={{
            display: 'inline-block', width: size, height: size, borderRadius: '50%', flexShrink: 0,
            background: c, boxShadow: `0 0 ${size}px ${c}88`,
        }} />
    );
}

/* ── Bento tile ─────────────────────────────────────────────── */
const TILE_GLOWS = {
    hero:    'rgba(79,209,197,0.22)',
    danger:  'rgba(244,63,94,0.22)',
    warn:    'rgba(245,158,11,0.20)',
    info:    'rgba(96,165,250,0.20)',
    success: 'rgba(52,211,153,0.22)',
};
const TILE_BG = {
    hero:    'linear-gradient(165deg,rgba(79,209,197,0.16),rgba(79,209,197,0.025) 60%,rgba(255,255,255,0.012))',
    danger:  'linear-gradient(165deg,rgba(244,63,94,0.16),rgba(244,63,94,0.025) 60%,rgba(255,255,255,0.012))',
    warn:    'linear-gradient(165deg,rgba(245,158,11,0.14),rgba(245,158,11,0.025) 60%,rgba(255,255,255,0.012))',
    info:    'linear-gradient(165deg,rgba(96,165,250,0.14),rgba(96,165,250,0.025) 60%,rgba(255,255,255,0.012))',
    success: 'linear-gradient(165deg,rgba(52,211,153,0.14),rgba(52,211,153,0.025) 60%,rgba(255,255,255,0.012))',
    '':      'linear-gradient(180deg,rgba(255,255,255,0.045),rgba(255,255,255,0.012))',
};
const TILE_BORDER = {
    hero: 'rgba(79,209,197,0.22)', danger: 'rgba(244,63,94,0.22)',
    warn: 'rgba(245,158,11,0.22)', info: 'rgba(96,165,250,0.22)',
    success: 'rgba(52,211,153,0.22)', '': 'rgba(255,255,255,0.07)',
};

function BTile({ children, style, variant = '', animDelay = 0, cornerGlyph }) {
    return (
        <div style={{
            borderRadius: 18, padding: 20,
            background: TILE_BG[variant] || TILE_BG[''],
            border: `1px solid ${TILE_BORDER[variant] || TILE_BORDER['']}`,
            display: 'flex', flexDirection: 'column',
            position: 'relative', overflow: 'hidden', isolation: 'isolate',
            boxShadow: `inset 0 1px 0 rgba(255,255,255,0.05), 0 1px 0 rgba(255,255,255,0.02), 0 20px 60px rgba(0,0,0,0.35)${TILE_GLOWS[variant] ? `, 0 0 0 1px transparent` : ''}`,
            animation: `tvTileIn 800ms ${animDelay}ms both cubic-bezier(.2,.7,.2,1)`,
            ...style,
        }}>
            {/* hairline */}
            <div style={{ content: '""', position: 'absolute', top: 0, left: '12%', right: '12%', height: 1, background: 'linear-gradient(90deg, transparent, rgba(255,255,255,0.22), transparent)', pointerEvents: 'none' }} />
            {/* glow */}
            {TILE_GLOWS[variant] && (
                <div style={{ position: 'absolute', inset: -1, borderRadius: 18, pointerEvents: 'none', zIndex: -1, background: `radial-gradient(ellipse at 50% 0%, ${TILE_GLOWS[variant]}, transparent 60%)`, opacity: 0.6 }} />
            )}
            {cornerGlyph && (
                <div style={{ position: 'absolute', top: 14, right: 16, opacity: 0.6, fontSize: 10, letterSpacing: 2, textTransform: 'uppercase', color: 'rgba(244,245,247,0.38)', fontFeatureSettings: '"tnum" 1' }}>
                    {cornerGlyph}
                </div>
            )}
            {children}
        </div>
    );
}

function BLabel({ children, style }) {
    return <div style={{ fontSize: 11, letterSpacing: 2.2, textTransform: 'uppercase', color: 'rgba(244,245,247,0.38)', fontWeight: 600, ...style }}>{children}</div>;
}

function SlideHeader({ eyebrow, title, accentSpan, right }) {
    return (
        <div style={{ display: 'flex', alignItems: 'flex-end', justifyContent: 'space-between', marginBottom: 14 }}>
            <div>
                <div style={{ fontSize: 11, letterSpacing: 3.2, textTransform: 'uppercase', color: 'rgba(244,245,247,0.38)', fontWeight: 600, marginBottom: 8 }}>{eyebrow}</div>
                <div style={{ fontSize: 62, fontWeight: 600, letterSpacing: -1.8, lineHeight: 1 }}>
                    {title}{accentSpan && <> <span style={{
                        background: `linear-gradient(180deg, ${ACCENT} 0%, color-mix(in srgb, ${ACCENT}, white 18%) 100%)`,
                        WebkitBackgroundClip: 'text', backgroundClip: 'text', color: 'transparent',
                        filter: `drop-shadow(0 0 24px color-mix(in srgb, ${ACCENT}, transparent 60%))`,
                    }}>{accentSpan}</span></>}
                </div>
            </div>
            {right}
        </div>
    );
}

function BentoGrid({ children, style }) {
    return (
        <div style={{
            display: 'grid', gap: 14,
            gridTemplateColumns: 'repeat(12, 1fr)',
            gridAutoRows: 'minmax(0, 1fr)',
            flex: 1, minHeight: 0,
            ...style,
        }}>
            {children}
        </div>
    );
}

function SlideWrap({ k, tint, children }) {
    return (
        <div key={k} style={{ position: 'absolute', inset: 0, animation: 'tvSlideUp 900ms cubic-bezier(.2,.7,.2,1)' }}>
            <MeshBackground tint={tint} />
            <div style={{ position: 'absolute', inset: 0, padding: '168px 60px 62px', display: 'flex', flexDirection: 'column' }}>
                {children}
            </div>
        </div>
    );
}

/* ════════════════════════════════════════════════════════════
   DATA PROCESSING
════════════════════════════════════════════════════════════ */
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;
    // Synthetic 24h CPU trend
    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;
    // Synthetic 14-day login failure trend
    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 || data.ticketMetrics?.daily?.slice(-7)?.map(d => d.count) || [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') : '—',
            risk: (loginFailures > 5000 || noEncrypt > 5) ? 'HOCH' : (loginFailures > 1000 || noEncrypt > 1) ? 'MITTEL' : 'NIEDRIG',
        },
        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,
        },
        // Static: network, backup, CVE (no backend endpoint yet)
        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],
        },
        cves: [
            { id: 'CVE-2026-13421', sev: 'critical', cvss: 9.8, title: 'Apache Tomcat — Pre-Auth RCE via AJP-Connector', vendor: 'Apache', published: '2 Std.' },
            { id: 'CVE-2026-13380', sev: 'critical', cvss: 9.6, title: 'Microsoft Outlook — Zero-Click via Preview Pane', vendor: 'Microsoft', published: '5 Std.' },
            { id: 'CVE-2026-13298', sev: 'high',     cvss: 8.4, title: 'Fortinet FortiGate — Auth Bypass', vendor: 'Fortinet', published: '11 Std.' },
            { id: 'CVE-2026-13211', sev: 'high',     cvss: 7.9, title: 'VMware ESXi — Privilege Escalation', vendor: 'VMware', published: '18 Std.' },
            { id: 'CVE-2026-13104', sev: 'medium',   cvss: 6.5, title: 'Chrome V8 — Type Confusion', vendor: 'Google', published: '1 Tag' },
        ],
    };
}

/* ════════════════════════════════════════════════════════════
   SLIDES
════════════════════════════════════════════════════════════ */

/* 1 – Overview */
function SlideOverview({ d, k }) {
    const onlinePct = d.agents.total > 0 ? Math.round((d.agents.online / d.agents.total) * 100) : 0;
    return (
        <SlideWrap k={k} tint="neutral">
            <SlideHeader eyebrow="Cereda Systems GmbH · Echtzeit-Systemübersicht" title="Heute im" accentSpan="Überblick" />
            <BentoGrid>
                {/* Hero — Agents */}
                <BTile variant="hero" style={{ gridColumn: 'span 7', gridRow: 'span 4' }} animDelay={0} cornerGlyph="01 · AGENTS">
                    <BLabel>Agents online</BLabel>
                    <div style={{ display: 'flex', alignItems: 'baseline', gap: 14, marginTop: 10 }}>
                        <span style={{ fontSize: 160, fontWeight: 700, letterSpacing: -5, lineHeight: 0.92, color: ACCENT, filter: `drop-shadow(0 0 32px ${ACCENT}60)`, fontFeatureSettings: '"tnum" 1' }}>
                            <BigNumber value={d.agents.online} animateKey={k} />
                        </span>
                        <span style={{ fontSize: 52, color: 'rgba(244,245,247,0.18)', fontWeight: 500, letterSpacing: -1.5, fontFeatureSettings: '"tnum" 1' }}>/ {d.agents.total}</span>
                    </div>
                    {/* Fleet strip */}
                    <div style={{ display: 'grid', gridTemplateColumns: `repeat(${Math.max(d.agentList.length, 1)}, 1fr)`, gap: 4, marginTop: 12 }}>
                        {d.agentList.map((a, i) => (
                            <div key={a.name} style={{
                                aspectRatio: 1, borderRadius: 6,
                                border: '1px solid rgba(255,255,255,0.04)',
                                background: a.status === 'online'
                                    ? `rgba(79,209,197,${0.3 + (a.cpu / 100) * 0.6})`
                                    : 'rgba(239,68,68,0.4)',
                                animation: `tvHeatIn 600ms ${300 + i * 14}ms both cubic-bezier(.2,.7,.2,1)`,
                            }} />
                        ))}
                    </div>
                    <div style={{ marginTop: 'auto', display: 'flex', justifyContent: 'space-between', alignItems: 'flex-end' }}>
                        <div style={{ display: 'flex', gap: 24 }}>
                            <span style={{ display: 'flex', alignItems: 'center', gap: 8 }}><StatusDot status="online" size={9} /><span style={{ color: 'rgba(244,245,247,0.62)' }}>{d.agents.online} online</span></span>
                            <span style={{ display: 'flex', alignItems: 'center', gap: 8 }}><StatusDot status="offline" size={9} /><span style={{ color: 'rgba(244,245,247,0.62)' }}>{d.agents.offline} offline</span></span>
                        </div>
                        <div style={{ fontSize: 28, fontWeight: 600, color: ACCENT, letterSpacing: -0.5, fontFeatureSettings: '"tnum" 1' }}>{onlinePct} %</div>
                    </div>
                </BTile>

                {/* Tickets */}
                <BTile variant="success" style={{ gridColumn: 'span 5', gridRow: 'span 2' }} animDelay={140} cornerGlyph="02 · HELPDESK">
                    <BLabel>Offene Tickets</BLabel>
                    <div style={{ display: 'flex', alignItems: 'baseline', justifyContent: 'space-between', marginTop: 8 }}>
                        <span style={{ fontSize: 80, fontWeight: 700, letterSpacing: -2.5, lineHeight: 1, color: C_OK }}><BigNumber value={d.tickets.open} animateKey={k} /></span>
                        <div style={{ width: 180, height: 40 }}><Sparkline data={d.tickets.perDay7} color={C_OK} width={180} height={40} strokeW={1.5} /></div>
                    </div>
                    <div style={{ marginTop: 'auto', fontSize: 14, color: 'rgba(244,245,247,0.62)' }}>
                        {d.tickets.byPriority.critical} kritisch · {d.tickets.byPriority.high} hoch
                    </div>
                </BTile>

                {/* Security */}
                <BTile variant="danger" style={{ gridColumn: 'span 5', gridRow: 'span 2' }} animDelay={200} cornerGlyph="03 · SECURITY">
                    <BLabel>Login-Fehler · 30 Tage</BLabel>
                    <div style={{ display: 'flex', alignItems: 'baseline', justifyContent: 'space-between', marginTop: 8 }}>
                        <span style={{ fontSize: 64, fontWeight: 700, letterSpacing: -2, lineHeight: 1, color: C_ERROR }}><BigNumber value={d.security.loginFailures} animateKey={k} /></span>
                        <div style={{ width: 180, height: 40 }}><Sparkline data={d.security.failuresTrend14d} color={C_ERROR} width={180} height={40} strokeW={1.5} /></div>
                    </div>
                    <div style={{ marginTop: 'auto', fontSize: 14, color: 'rgba(244,245,247,0.62)' }}><span style={{ color: C_ERROR }}>●</span> Risiko: {d.security.risk}</div>
                </BTile>

                {/* Updates */}
                <BTile variant="info" style={{ gridColumn: 'span 4', gridRow: 'span 2' }} animDelay={260} cornerGlyph="04 · UPDATES">
                    <BLabel>Updates ausstehend</BLabel>
                    <div style={{ fontSize: 80, fontWeight: 700, letterSpacing: -2.5, lineHeight: 1, color: C_INFO, marginTop: 8 }}><BigNumber value={d.patches.pending} animateKey={k} /></div>
                    <div style={{ height: 8, borderRadius: 4, background: 'rgba(255,255,255,0.06)', overflow: 'hidden', marginTop: 'auto' }}>
                        <div style={{ height: '100%', borderRadius: 4, background: C_INFO, transition: 'width 1.4s cubic-bezier(.2,.7,.2,1)', width: `${d.patches.compliancePct}%` }} />
                    </div>
                    <div style={{ marginTop: 6, fontSize: 12, color: 'rgba(244,245,247,0.62)' }}>{d.patches.compliancePct} % konform</div>
                </BTile>

                {/* Backup */}
                <BTile variant="success" style={{ gridColumn: 'span 4', gridRow: 'span 2' }} animDelay={320} cornerGlyph="05 · BACKUP">
                    <BLabel>Backup Coverage</BLabel>
                    <div style={{ display: 'flex', alignItems: 'baseline', gap: 8, marginTop: 8 }}>
                        <span style={{ fontSize: 80, fontWeight: 700, letterSpacing: -2.5, lineHeight: 1, color: C_OK }}><BigNumber value={d.backups.coverage} animateKey={k} /></span>
                        <span style={{ fontSize: 36, color: 'rgba(244,245,247,0.18)' }}>%</span>
                    </div>
                    <div style={{ marginTop: 'auto', fontSize: 13, color: 'rgba(244,245,247,0.62)' }}>Letzter Lauf {d.backups.lastSuccess} Uhr</div>
                </BTile>

                {/* CVE */}
                <BTile variant="warn" style={{ gridColumn: 'span 4', gridRow: 'span 2' }} animDelay={380} cornerGlyph="06 · CVE">
                    <BLabel>Kritische CVEs</BLabel>
                    <div style={{ fontSize: 80, fontWeight: 700, letterSpacing: -2.5, lineHeight: 1, color: C_WARN, marginTop: 8 }}>
                        {d.cves.filter(c => c.sev === 'critical').length}
                    </div>
                    <div style={{ marginTop: 'auto', fontSize: 13, color: 'rgba(244,245,247,0.62)', lineHeight: 1.3 }}>
                        {d.cves[0].vendor} · {d.cves[0].id}
                    </div>
                </BTile>
            </BentoGrid>
        </SlideWrap>
    );
}

/* 2 – Agents */
function SlideAgents({ 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 avgCpu = onlineAgents.length > 0 ? Math.round(onlineAgents.reduce((s, a) => s + a.cpu, 0) / onlineAgents.length) : 0;
    return (
        <SlideWrap k={k} tint="mint">
            <SlideHeader eyebrow="Windows Agents · Live-Telemetrie" title="Flotte" accentSpan="im Blick" />
            <BentoGrid>
                <BTile variant="hero" style={{ gridColumn: 'span 4', gridRow: 'span 5' }} animDelay={0} cornerGlyph="VERFÜGBARKEIT">
                    <div style={{ display: 'flex', flex: 1, alignItems: 'center', justifyContent: 'center', marginTop: 8 }}>
                        <Donut value={pct} size={240} stroke={16} color={ACCENT} animateKey={k} label="Online" sublabel={`${d.agents.online} / ${d.agents.total}`} />
                    </div>
                    <div style={{ display: 'flex', justifyContent: 'space-between', fontSize: 14 }}>
                        <span style={{ display: 'flex', alignItems: 'center', gap: 8 }}><StatusDot status="online" size={9} /><span style={{ color: 'rgba(244,245,247,0.62)' }}>{d.agents.online} online</span></span>
                        <span style={{ display: 'flex', alignItems: 'center', gap: 8 }}><StatusDot status="offline" size={9} /><span style={{ color: 'rgba(244,245,247,0.62)' }}>{d.agents.offline} offline</span></span>
                    </div>
                </BTile>

                <BTile style={{ gridColumn: 'span 8', gridRow: 'span 3' }} animDelay={120} cornerGlyph="CPU · 24 STD">
                    <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline' }}>
                        <BLabel>Cluster-CPU-Last</BLabel>
                        <div style={{ fontSize: 14, color: 'rgba(244,245,247,0.62)', fontFeatureSettings: '"tnum" 1' }}>
                            Ø <span style={{ color: ACCENT, fontWeight: 600 }}>{avgCpu} %</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={ACCENT} width={900} height={200} />
                    </div>
                    <div style={{ display: 'flex', justifyContent: 'space-between', fontSize: 12, color: 'rgba(244,245,247,0.38)', marginTop: 8 }}>
                        <span>vor 24 Std</span><span>jetzt</span>
                    </div>
                </BTile>

                <BTile style={{ gridColumn: 'span 8', gridRow: 'span 2' }} animDelay={200} cornerGlyph="TOP 6 · CPU">
                    <BLabel>Top Auslastung</BLabel>
                    <div style={{ display: 'grid', gridTemplateColumns: 'repeat(6, 1fr)', gap: 16, marginTop: 12, flex: 1 }}>
                        {(top.length > 0 ? top : Array(6).fill({ name: '—', cpu: 0 })).map((a, i) => (
                            <div key={a.name + i} style={{ display: 'flex', flexDirection: 'column', justifyContent: 'flex-end', gap: 6,
                                animation: `tvTileIn 600ms ${260 + i * 60}ms both cubic-bezier(.2,.7,.2,1)` }}>
                                <div style={{ fontSize: 12, fontWeight: 600, opacity: 0.85, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>{a.name}</div>
                                <div style={{ fontSize: 24, fontWeight: 700, letterSpacing: -0.5, color: a.cpu > 50 ? C_WARN : ACCENT, fontFeatureSettings: '"tnum" 1' }}>{a.cpu.toFixed(1)} %</div>
                                <div style={{ height: 4, borderRadius: 2, background: 'rgba(255,255,255,0.06)', overflow: 'hidden' }}>
                                    <div style={{ height: '100%', borderRadius: 2, transition: 'width 1.4s cubic-bezier(.2,.7,.2,1)', width: `${a.cpu}%`, background: a.cpu > 50 ? C_WARN : ACCENT }} />
                                </div>
                            </div>
                        ))}
                    </div>
                </BTile>
            </BentoGrid>
        </SlideWrap>
    );
}

/* 3 – Patch Compliance */
function SlideCompliance({ d, k }) {
    const pct = d.patches.compliancePct;
    const color = pct < 50 ? C_ERROR : pct < 80 ? C_WARN : C_OK;
    return (
        <SlideWrap k={k} tint="red">
            <SlideHeader eyebrow="Patch Compliance" title="Update-" accentSpan="Zustand"
                right={<div style={{
                    display: 'inline-flex', alignItems: 'center', gap: 10, padding: '10px 20px', borderRadius: 999,
                    background: 'rgba(245,158,11,0.12)', border: '1px solid rgba(245,158,11,0.35)', color: '#FCD34D',
                    fontSize: 18, fontWeight: 600, letterSpacing: 0.5,
                }}>⚠ unter Ziel · 95 %</div>}
            />
            <BentoGrid>
                <BTile variant="danger" style={{ gridColumn: 'span 5', gridRow: 'span 4' }} animDelay={0} cornerGlyph="KONFORM">
                    <div style={{ flex: 1, display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
                        <Donut value={pct} size={270} stroke={20} color={color} animateKey={k} label="konform" sublabel={`${d.patches.current} von ${d.patches.total} Geräten`} />
                    </div>
                    <div style={{ display: 'flex', justifyContent: 'space-between', fontSize: 13, color: 'rgba(244,245,247,0.38)' }}>
                        <span>Ziel: 95 %</span>
                        <span style={{ color: C_ERROR }}>Abweichung −{Math.max(0, 95 - pct)} %</span>
                    </div>
                </BTile>

                <BTile variant="success" style={{ gridColumn: 'span 7', gridRow: 'span 2' }} animDelay={120} cornerGlyph="AKTUELL">
                    <BLabel>Aktuell · konform</BLabel>
                    <div style={{ display: 'flex', alignItems: 'baseline', justifyContent: 'space-between', marginTop: 8 }}>
                        <span style={{ fontSize: 48, fontWeight: 700, letterSpacing: -1.2, color: C_OK }}><BigNumber value={d.patches.current} animateKey={k} /></span>
                        <div style={{ fontSize: 14, color: 'rgba(244,245,247,0.62)', fontFeatureSettings: '"tnum" 1' }}>{d.patches.total > 0 ? Math.round((d.patches.current / d.patches.total) * 100) : 0} %</div>
                    </div>
                    <div style={{ height: 8, borderRadius: 4, background: 'rgba(255,255,255,0.06)', overflow: 'hidden', marginTop: 'auto' }}>
                        <div style={{ height: '100%', borderRadius: 4, background: C_OK, transition: 'width 1.4s cubic-bezier(.2,.7,.2,1)', width: `${d.patches.total > 0 ? (d.patches.current / d.patches.total) * 100 : 0}%` }} />
                    </div>
                </BTile>

                <BTile variant="warn" style={{ gridColumn: 'span 4', gridRow: 'span 2' }} animDelay={180} cornerGlyph="FEHLEN">
                    <BLabel>Updates fehlen</BLabel>
                    <div style={{ fontSize: 48, fontWeight: 700, letterSpacing: -1.2, color: C_WARN, marginTop: 8 }}><BigNumber value={d.patches.warning} animateKey={k} /></div>
                    <div style={{ height: 8, borderRadius: 4, background: 'rgba(255,255,255,0.06)', overflow: 'hidden', marginTop: 'auto' }}>
                        <div style={{ height: '100%', borderRadius: 4, background: C_WARN, transition: 'width 1.4s cubic-bezier(.2,.7,.2,1)', width: `${d.patches.total > 0 ? (d.patches.warning / d.patches.total) * 100 : 0}%` }} />
                    </div>
                </BTile>

                <BTile variant="danger" style={{ gridColumn: 'span 3', gridRow: 'span 2' }} animDelay={240} cornerGlyph="OFFLINE">
                    <BLabel>Offline</BLabel>
                    <div style={{ fontSize: 48, fontWeight: 700, letterSpacing: -1.2, color: C_ERROR, marginTop: 8 }}><BigNumber value={d.patches.offline} animateKey={k} /></div>
                    <div style={{ height: 8, borderRadius: 4, background: 'rgba(255,255,255,0.06)', overflow: 'hidden', marginTop: 'auto' }}>
                        <div style={{ height: '100%', borderRadius: 4, background: C_ERROR, transition: 'width 1.4s cubic-bezier(.2,.7,.2,1)', width: `${d.patches.total > 0 ? (d.patches.offline / d.patches.total) * 100 : 0}%` }} />
                    </div>
                </BTile>
            </BentoGrid>
        </SlideWrap>
    );
}

/* 4 – Security */
function SlideSecurity({ d, k }) {
    return (
        <SlideWrap k={k} tint="red">
            <SlideHeader eyebrow="Sicherheit" title="Risiko" accentSpan="Übersicht"
                right={<div style={{ display: 'inline-flex', alignItems: 'center', gap: 10, padding: '8px 16px', borderRadius: 999, background: 'rgba(244,63,94,0.12)', border: '1px solid rgba(244,63,94,0.35)', color: '#FCA5A5', fontSize: 15, fontWeight: 600 }}>⚠ Risiko {d.security.risk}</div>}
            />
            <BentoGrid>
                <BTile variant="danger" style={{ gridColumn: 'span 7', gridRow: 'span 4' }} animDelay={0} cornerGlyph="M365 · 30 TAGE">
                    <BLabel>Login-Fehler</BLabel>
                    <div style={{ fontSize: 130, fontWeight: 700, color: C_ERROR, letterSpacing: -4, lineHeight: 0.95, marginTop: 8,
                        filter: 'drop-shadow(0 0 32px rgba(244,63,94,0.4))', fontFeatureSettings: '"tnum" 1' }}>
                        <BigNumber value={d.security.loginFailures} animateKey={k} />
                    </div>
                    <div style={{ fontSize: 16, color: 'rgba(244,245,247,0.62)', marginTop: 4 }}>fehlgeschlagene Anmeldeversuche · Microsoft 365</div>
                    <div style={{ flex: 1, marginTop: 20, minHeight: 0 }}>
                        <Sparkline data={d.security.failuresTrend14d} color={C_ERROR} width={900} height={140} />
                    </div>
                    <div style={{ display: 'flex', justifyContent: 'space-between', fontSize: 12, color: 'rgba(244,245,247,0.38)' }}>
                        <span>vor 14 Tagen</span><span>heute · {d.security.failuresTrend14d[d.security.failuresTrend14d.length - 1]}/Tag</span>
                    </div>
                </BTile>

                <BTile variant="warn" style={{ gridColumn: 'span 5', gridRow: 'span 2' }} animDelay={120} cornerGlyph="BITLOCKER">
                    <BLabel>Verschlüsselungs-Abdeckung</BLabel>
                    <div style={{ display: 'flex', alignItems: 'baseline', justifyContent: 'space-between', marginTop: 8 }}>
                        <span style={{ fontSize: 48, fontWeight: 700, letterSpacing: -1.2, color: d.security.bitlockerPct >= 90 ? C_OK : C_WARN }}><BigNumber value={d.security.bitlockerPct} suffix=" %" animateKey={k} /></span>
                        <span style={{ fontSize: 14, color: 'rgba(244,245,247,0.62)', fontFeatureSettings: '"tnum" 1' }}>{d.security.encrypted}/{d.agents.total}</span>
                    </div>
                    <div style={{ height: 8, borderRadius: 4, background: 'rgba(255,255,255,0.06)', overflow: 'hidden', marginTop: 'auto' }}>
                        <div style={{ height: '100%', borderRadius: 4, background: d.security.bitlockerPct >= 90 ? C_OK : C_WARN, transition: 'width 1.4s cubic-bezier(.2,.7,.2,1)', width: `${d.security.bitlockerPct}%` }} />
                    </div>
                </BTile>

                <BTile variant="danger" style={{ gridColumn: 'span 3', gridRow: 'span 2' }} animDelay={180} cornerGlyph="OFFEN">
                    <BLabel>Unverschlüsselt</BLabel>
                    <div style={{ fontSize: 48, fontWeight: 700, letterSpacing: -1.2, color: C_ERROR, marginTop: 8 }}><BigNumber value={d.security.unencrypted} animateKey={k} /></div>
                    <div style={{ marginTop: 'auto', fontSize: 12, color: 'rgba(244,245,247,0.62)' }}>BitLocker deaktiviert</div>
                </BTile>

                <BTile style={{ gridColumn: 'span 2', gridRow: 'span 2' }} animDelay={240} cornerGlyph="LETZTER">
                    <BLabel>Report</BLabel>
                    <div style={{ fontSize: 22, fontWeight: 700, marginTop: 8, letterSpacing: -0.5, fontFeatureSettings: '"tnum" 1' }}>{d.security.lastReport}</div>
                    <div style={{ marginTop: 'auto', fontSize: 12, color: 'rgba(244,245,247,0.62)' }}>SIEM · auto</div>
                </BTile>
            </BentoGrid>
        </SlideWrap>
    );
}

/* 5 – Helpdesk */
function SlideHelpdesk({ d, k }) {
    const prios = [
        { label: 'Kritisch', v: d.tickets.byPriority.critical, c: C_ERROR },
        { label: 'Hoch',     v: d.tickets.byPriority.high,     c: C_WARN },
        { label: 'Mittel',   v: d.tickets.byPriority.medium,   c: C_INFO },
        { label: 'Niedrig',  v: d.tickets.byPriority.low,      c: 'rgba(244,245,247,0.62)' },
    ];
    return (
        <SlideWrap k={k} tint="mint">
            <SlideHeader eyebrow="Helpdesk" title="Saubere" accentSpan="Inbox" />
            <BentoGrid>
                <BTile variant="success" style={{ gridColumn: 'span 7', gridRow: 'span 4' }} animDelay={0} cornerGlyph="OFFEN · GESAMT">
                    <div style={{ flex: 1, display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', textAlign: 'center' }}>
                        <div style={{ fontSize: 200, fontWeight: 700, color: C_OK, lineHeight: 0.92, letterSpacing: -7,
                            filter: 'drop-shadow(0 0 40px rgba(52,211,153,0.35))', fontFeatureSettings: '"tnum" 1' }}>
                            <BigNumber value={d.tickets.open} animateKey={k} />
                        </div>
                        <div style={{ marginTop: 16, fontSize: 18, color: 'rgba(244,245,247,0.62)' }}>offene Tickets · {d.tickets.byPriority.critical === 0 ? 'keine Eskalationen' : `${d.tickets.byPriority.critical} kritisch`}</div>
                    </div>
                </BTile>

                <BTile style={{ gridColumn: 'span 5', gridRow: 'span 2' }} animDelay={120} cornerGlyph="PRIORITÄT">
                    <BLabel>Verteilung</BLabel>
                    <div style={{ display: 'flex', flexDirection: 'column', gap: 12, marginTop: 12, flex: 1, justifyContent: 'space-around' }}>
                        {prios.map(p => (
                            <div key={p.label} style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
                                <span style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
                                    <span style={{ width: 8, height: 8, borderRadius: 99, background: p.c, boxShadow: `0 0 8px ${p.c}88` }} />
                                    <span style={{ fontSize: 16, fontWeight: 500 }}>{p.label}</span>
                                </span>
                                <span style={{ fontSize: 22, fontWeight: 700, color: p.c, fontFeatureSettings: '"tnum" 1' }}><BigNumber value={p.v} animateKey={k} /></span>
                            </div>
                        ))}
                    </div>
                </BTile>

                <BTile style={{ gridColumn: 'span 5', gridRow: 'span 2' }} animDelay={180} cornerGlyph="VERLAUF · 7 T">
                    <BLabel>Tickets pro Tag</BLabel>
                    <div style={{ flex: 1, marginTop: 12 }}>
                        <Sparkline data={d.tickets.perDay7.length > 0 ? d.tickets.perDay7 : [0, 0, 0, 0, 0, 0, 0]} color={ACCENT} width={500} height={130} />
                    </div>
                    <div style={{ display: 'flex', justifyContent: 'space-between', fontSize: 11, color: 'rgba(244,245,247,0.38)' }}>
                        {['Mo','Di','Mi','Do','Fr','Sa','So'].map(d => <span key={d}>{d}</span>)}
                    </div>
                </BTile>
            </BentoGrid>
        </SlideWrap>
    );
}

/* 6 – Network */
function SlideNetwork({ 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;
    const warnCount = d.network.filter(s => s.status === 'warn').length;
    return (
        <SlideWrap k={k} tint="blue">
            <SlideHeader eyebrow="Netzwerk & Services" title="Konnek-" accentSpan="tivität"
                right={<div style={{ fontSize: 14, color: 'rgba(244,245,247,0.62)', textAlign: 'right' }}>
                    <div><span style={{ color: C_OK }}>● </span>{okCount} OK</div>
                    {warnCount > 0 && <div><span style={{ color: C_WARN }}>● </span>{warnCount} Warnung</div>}
                </div>}
            />
            <BentoGrid>
                {d.network.map((s, i) => {
                    const color = s.status === 'ok' ? C_OK : s.status === 'warn' ? C_WARN : C_ERROR;
                    const variant = s.status === 'warn' ? 'warn' : s.status === 'error' ? 'danger' : '';
                    return (
                        <BTile key={s.name} variant={variant} style={{ gridColumn: 'span 3', gridRow: 'span 2' }} animDelay={i * 60}>
                            <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', marginBottom: 6 }}>
                                <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
                                    <StatusDot status={s.status} size={10} />
                                    <div style={{ fontSize: 17, fontWeight: 600 }}>{s.name}</div>
                                </div>
                                <div style={{ width: 70, height: 22, opacity: 0.7 }}>
                                    <Sparkline data={trends[i]} color={color} width={70} height={22} fill={false} strokeW={1.5} />
                                </div>
                            </div>
                            <div style={{ fontSize: 30, fontWeight: 700, color, letterSpacing: -0.5, fontFeatureSettings: '"tnum" 1' }}>{s.latency}</div>
                            <div style={{ marginTop: 'auto', fontSize: 12, color: 'rgba(244,245,247,0.62)' }}>{s.detail}</div>
                        </BTile>
                    );
                })}
            </BentoGrid>
        </SlideWrap>
    );
}

/* 7 – Backup */
function SlideBackup({ d, k }) {
    return (
        <SlideWrap k={k} tint="mint">
            <SlideHeader eyebrow="Backup · Datenschutz im Hintergrund" title="24 h" accentSpan="im Grünen" />
            <BentoGrid>
                <BTile variant="hero" style={{ gridColumn: 'span 5', gridRow: 'span 4' }} animDelay={0} cornerGlyph="ABDECKUNG">
                    <div style={{ flex: 1, display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
                        <Donut value={d.backups.coverage} size={240} stroke={18} color={C_OK} animateKey={k} label="Erfolgreich" />
                    </div>
                    <div style={{ textAlign: 'center', fontSize: 14, color: 'rgba(244,245,247,0.62)' }}>Letzte Sicherung um {d.backups.lastSuccess} Uhr</div>
                </BTile>

                <BTile style={{ gridColumn: 'span 7', gridRow: 'span 2' }} animDelay={120} cornerGlyph="14 TAGE">
                    <BLabel>Verlauf</BLabel>
                    <div style={{ flex: 1, marginTop: 8 }}>
                        <Sparkline data={d.backups.trend14d} color={C_OK} width={800} height={150} />
                    </div>
                    <div style={{ display: 'flex', justifyContent: 'space-between', fontSize: 12, color: 'rgba(244,245,247,0.38)' }}>
                        <span>vor 14 Tagen</span>
                        <span>Min {Math.min(...d.backups.trend14d)} % · jetzt {d.backups.trend14d[d.backups.trend14d.length - 1]} %</span>
                    </div>
                </BTile>

                <BTile variant="success" style={{ gridColumn: 'span 3', gridRow: 'span 2' }} animDelay={200} cornerGlyph="OK">
                    <BLabel>Erfolgreich</BLabel>
                    <div style={{ fontSize: 48, fontWeight: 700, letterSpacing: -1.2, color: C_OK, marginTop: 8 }}><BigNumber value={d.backups.successful} animateKey={k} /></div>
                    <div style={{ marginTop: 'auto', fontSize: 12, color: 'rgba(244,245,247,0.62)' }}>24 Std</div>
                </BTile>

                <BTile variant="info" style={{ gridColumn: 'span 2', gridRow: 'span 2' }} animDelay={260} cornerGlyph="LIVE">
                    <BLabel>Laufend</BLabel>
                    <div style={{ fontSize: 48, fontWeight: 700, letterSpacing: -1.2, color: C_INFO, marginTop: 8 }}><BigNumber value={d.backups.running} animateKey={k} /></div>
                    <div style={{ marginTop: 'auto', fontSize: 12, color: 'rgba(244,245,247,0.62)' }}>Jobs aktiv</div>
                </BTile>

                <BTile variant={d.backups.failed > 0 ? 'warn' : 'success'} style={{ gridColumn: 'span 2', gridRow: 'span 2' }} animDelay={320} cornerGlyph="FAIL">
                    <BLabel>Fehler</BLabel>
                    <div style={{ fontSize: 48, fontWeight: 700, letterSpacing: -1.2, color: d.backups.failed > 0 ? C_WARN : C_OK, marginTop: 8 }}><BigNumber value={d.backups.failed} animateKey={k} /></div>
                    <div style={{ marginTop: 'auto', fontSize: 11, lineHeight: 1.3, color: 'rgba(244,245,247,0.62)' }}>{d.backups.lastFailJob}</div>
                </BTile>
            </BentoGrid>
        </SlideWrap>
    );
}

/* 8 – CVE Watch */
function SlideCVE({ d, k }) {
    const [top, ...rest] = d.cves;
    const sevCount = { critical: 0, high: 0, medium: 0, low: 0 };
    d.cves.forEach(c => sevCount[c.sev] = (sevCount[c.sev] || 0) + 1);
    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 (
        <SlideWrap k={k} tint="violet">
            <SlideHeader eyebrow="CVE Watch · Vendor Advisories" title="Schwach-" accentSpan="stellen"
                right={<div style={{ display: 'flex', gap: 16, fontSize: 13, color: 'rgba(244,245,247,0.62)' }}>
                    <span><span style={{ color: '#FCA5A5' }}>● </span>{sevCount.critical || 0} critical</span>
                    <span><span style={{ color: '#FCD34D' }}>● </span>{sevCount.high || 0} high</span>
                    <span><span style={{ color: '#93C5FD' }}>● </span>{sevCount.medium || 0} medium</span>
                </div>}
            />
            <BentoGrid>
                <BTile variant="danger" style={{ gridColumn: 'span 12', gridRow: 'span 3' }} animDelay={0} cornerGlyph={`#1 · ${top.id}`}>
                    <div style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
                        <SevPill sev={top.sev} cvss={top.cvss} />
                        <span style={{ fontSize: 13, color: 'rgba(244,245,247,0.62)' }}>vor {top.published}</span>
                    </div>
                    <div style={{ fontSize: 38, fontWeight: 600, marginTop: 12, letterSpacing: -0.8, lineHeight: 1.1 }}>
                        {top.title}
                    </div>
                    <div style={{ marginTop: 'auto', fontSize: 15, color: 'rgba(244,245,247,0.62)' }}>{top.vendor} · Sicherheitshinweis</div>
                </BTile>
                {rest.map((c, i) => (
                    <BTile key={c.id} variant={c.sev === 'high' ? 'warn' : 'info'} style={{ gridColumn: 'span 6', gridRow: 'span 2' }} animDelay={120 + i * 80} cornerGlyph={c.id}>
                        <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start' }}>
                            <SevPill sev={c.sev} cvss={c.cvss} />
                            <span style={{ fontSize: 12, color: 'rgba(244,245,247,0.62)' }}>vor {c.published}</span>
                        </div>
                        <div style={{ fontSize: 22, fontWeight: 600, marginTop: 12, letterSpacing: -0.4, lineHeight: 1.2 }}>{c.title}</div>
                        <div style={{ marginTop: 'auto', fontSize: 13, color: 'rgba(244,245,247,0.62)' }}>{c.vendor}</div>
                    </BTile>
                ))}
            </BentoGrid>
        </SlideWrap>
    );
}

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

const SLIDES = [
    { id: 'overview',   title: 'Übersicht',       Component: SlideOverview },
    { id: 'agents',     title: 'Windows Agents',  Component: SlideAgents },
    { id: 'compliance', title: 'Patch Compliance', Component: SlideCompliance },
    { id: 'security',   title: 'Sicherheit',       Component: SlideSecurity },
    { id: 'helpdesk',   title: 'Helpdesk',         Component: SlideHelpdesk },
    { id: 'network',    title: 'Netzwerk',         Component: SlideNetwork },
    { id: 'backup',     title: 'Backup',           Component: SlideBackup },
    { id: 'cve',        title: 'CVE Watch',        Component: SlideCVE },
];

export default function TVDashboardPage() {
    const [rawData, setRawData] = useState({});
    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 d = r.data;
        const agents = d.agentList || [];
        const noEncrypt = agents.filter(a => a.bitlocker_status && !['encrypted', 'on'].includes(String(a.bitlocker_status).toLowerCase())).length;
        setRawData({
            monitoring: { ...d.monitoring, noEncrypt },
            agentList: agents,
            tickets: d.tickets || {},
            patch: d.patch || {},
            lastSecReport: d.lastSecReport || null,
            ticketMetrics: d.ticketMetrics || {},
        });
    }, []);

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

    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);
    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' }}>

            {/* ── 1920×1080 scaled stage ── */}
            <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} />

                {/* Controls overlay (bottom) */}
                <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.2)', 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');

                /* ── Mesh blobs ── */
                .tv-blob { position: absolute; border-radius: 50%; filter: blur(140px); will-change: transform; }
                .tv-b1 { width: 1200px; height: 1200px; left: -200px; top: -300px; animation: tvBlob1 22s ease-in-out infinite alternate; }
                .tv-b2 { width: 1000px; height: 1000px; right: -200px; top: -100px; animation: tvBlob2 28s ease-in-out infinite alternate; }
                .tv-b3 { width: 900px; height: 900px; left: 20%; bottom: -300px; animation: tvBlob3 32s ease-in-out infinite alternate; }
                .tv-b4 { width: 800px; height: 800px; right: 10%; bottom: -200px; animation: tvBlob4 26s ease-in-out infinite alternate; }
                @keyframes tvBlob1 { from { transform: translate(0,0) scale(1); } to { transform: translate(180px,120px) scale(1.1); } }
                @keyframes tvBlob2 { from { transform: translate(0,0) scale(1.05); } to { transform: translate(-120px,200px) scale(0.95); } }
                @keyframes tvBlob3 { from { transform: translate(0,0) scale(0.95); } to { transform: translate(220px,-160px) scale(1.1); } }
                @keyframes tvBlob4 { from { transform: translate(0,0) scale(1.1); } to { transform: translate(-180px,-100px) scale(0.9); } }

                /* ── Brand dot pulse ── */
                .tv-brand-dot {
                    display: inline-block; width: 9px; height: 9px; border-radius: 50%;
                    box-shadow: 0 0 14px currentColor, 0 0 4px currentColor;
                    animation: tvBrandPulse 2.4s ease-in-out infinite;
                }
                @keyframes tvBrandPulse {
                    0%,100% { opacity:1; box-shadow:0 0 14px currentColor,0 0 4px currentColor; }
                    50% { opacity:0.55; box-shadow:0 0 8px currentColor,0 0 3px currentColor; }
                }

                /* ── Status dot pulse ── */
                .tv-dot-pulse { animation: tvDotPulse 2.4s ease-in-out infinite; }
                @keyframes tvDotPulse {
                    0%,100% { transform:scale(1); opacity:1; }
                    50% { transform:scale(1.25); opacity:0.7; }
                }

                /* ── Number punch ── */
                .tv-number-punch { display:inline-block; animation: tvNumberPunch 480ms cubic-bezier(.2,.7,.2,1) 1400ms both; }
                @keyframes tvNumberPunch {
                    0% { transform:scale(1); } 60% { transform:scale(1.025); } 100% { transform:scale(1); }
                }

                /* ── Slide entry ── */
                @keyframes tvSlideUp {
                    from { transform: translateY(28px); }
                    to   { transform: translateY(0); }
                }

                /* ── Tile entry ── */
                @keyframes tvTileIn {
                    from { transform: translateY(24px) scale(0.985); }
                    to   { transform: translateY(0) scale(1); }
                }

                /* ── Heat cell entry ── */
                @keyframes tvHeatIn {
                    from { transform: scale(0.4); }
                    to   { transform: scale(1); }
                }

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

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