/* global React, Stage, Sprite, useSprite, useTime, useTimeline, Easing, interpolate, clamp */

// ─────────────────────────────────────────────────────────────────
// CyberDebunk - Explainer v3 (GDPR / privacy review)
// Live site explainer (/explainer + landing embed).
// ~59s narrated arc. 10 scenes synced to explainer-3-voiceover.mp3
// ─────────────────────────────────────────────────────────────────

const STAGE_W = 1280;
const STAGE_H = 720;

const C = {
    cyan:   "#22D3EE",
    blue:   "#5A6FF0",
    purple: "#BC27E0",
    pink:   "#F472B6",
    ink:    "#050b16",
    deep:   "#0a1628",
    cream:  "#F8FAFC",
    mute:   "#94a3b8",
    sev:    "#F87171",
    ok:     "#86EFAC",
};

const fontDisplay = `"Raleway", "Space Grotesk", system-ui, sans-serif`;
const fontMono = `"JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, monospace`;

function spriteOpacity(localTime, duration, enterDur = 0.45, exitDur = 0.5) {
    const enter = Easing.easeOutCubic(clamp(localTime / enterDur, 0, 1));
    const exitStart = Math.max(0, duration - exitDur);
    const exit = duration > 0 && localTime > exitStart
        ? Easing.easeInQuad(clamp((localTime - exitStart) / exitDur, 0, 1))
        : 0;
    return enter * (1 - exit);
}

// ─── Sound engine ──────────────────────────────────────────────────────────
const SFX = (() => {
    let ctx = null;
    let master = null;
    let enabled = false;
    function ensure() {
        if (ctx) return ctx;
        try {
            const AC = window.AudioContext || window.webkitAudioContext;
            if (!AC) return null;
            ctx = new AC();
            master = ctx.createGain();
            master.gain.value = 0.0;
            master.connect(ctx.destination);
        } catch (e) { return null; }
        return ctx;
    }
    function setEnabled(on) {
        enabled = on;
        if (!ctx) ensure();
        if (!ctx) return;
        if (ctx.state === "suspended" && on) ctx.resume();
        if (master) {
            const target = on ? 0.55 : 0.0;
            master.gain.cancelScheduledValues(ctx.currentTime);
            master.gain.linearRampToValueAtTime(target, ctx.currentTime + 0.06);
        }
    }
    function tick({ freq = 1400, dur = 0.045, type = "square", vol = 0.18 } = {}) {
        if (!enabled || !ctx || !master) return;
        const t0 = ctx.currentTime;
        const o = ctx.createOscillator();
        const g = ctx.createGain();
        o.type = type;
        o.frequency.setValueAtTime(freq, t0);
        g.gain.setValueAtTime(0.0001, t0);
        g.gain.exponentialRampToValueAtTime(vol, t0 + 0.005);
        g.gain.exponentialRampToValueAtTime(0.0001, t0 + dur);
        o.connect(g); g.connect(master);
        o.start(t0); o.stop(t0 + dur + 0.02);
    }
    function thud({ freq = 80, dur = 0.4, vol = 0.5 } = {}) {
        if (!enabled || !ctx || !master) return;
        const t0 = ctx.currentTime;
        const o = ctx.createOscillator();
        const g = ctx.createGain();
        o.type = "sine";
        o.frequency.setValueAtTime(freq * 3, t0);
        o.frequency.exponentialRampToValueAtTime(freq, t0 + 0.08);
        g.gain.setValueAtTime(0.0001, t0);
        g.gain.exponentialRampToValueAtTime(vol, t0 + 0.01);
        g.gain.exponentialRampToValueAtTime(0.0001, t0 + dur);
        o.connect(g); g.connect(master);
        o.start(t0); o.stop(t0 + dur + 0.05);
    }
    function chime({ freq = 880, dur = 0.6, vol = 0.22 } = {}) {
        if (!enabled || !ctx || !master) return;
        const t0 = ctx.currentTime;
        const o = ctx.createOscillator();
        const g = ctx.createGain();
        o.type = "triangle";
        o.frequency.setValueAtTime(freq, t0);
        g.gain.setValueAtTime(0.0001, t0);
        g.gain.exponentialRampToValueAtTime(vol, t0 + 0.02);
        g.gain.exponentialRampToValueAtTime(0.0001, t0 + dur);
        o.connect(g); g.connect(master);
        o.start(t0); o.stop(t0 + dur + 0.05);
    }
    function sweep({ from = 240, to = 1600, dur = 0.7, vol = 0.12 } = {}) {
        if (!enabled || !ctx || !master) return;
        const t0 = ctx.currentTime;
        const o = ctx.createOscillator();
        const g = ctx.createGain();
        o.type = "sawtooth";
        o.frequency.setValueAtTime(from, t0);
        o.frequency.exponentialRampToValueAtTime(to, t0 + dur);
        g.gain.setValueAtTime(0.0001, t0);
        g.gain.exponentialRampToValueAtTime(vol, t0 + 0.05);
        g.gain.exponentialRampToValueAtTime(0.0001, t0 + dur);
        o.connect(g); g.connect(master);
        o.start(t0); o.stop(t0 + dur + 0.05);
    }
    function scratch({ dur = 0.32, vol = 0.32 } = {}) {
        if (!enabled || !ctx || !master) return;
        const t0 = ctx.currentTime;
        const sr = ctx.sampleRate;
        const len = Math.floor(sr * dur);
        const buf = ctx.createBuffer(1, len, sr);
        const data = buf.getChannelData(0);
        for (let i = 0; i < len; i++) {
            const n = (Math.random() * 2 - 1);
            const wob = 0.6 + 0.4 * Math.sin((i / sr) * 90);
            data[i] = n * wob;
        }
        const src = ctx.createBufferSource();
        src.buffer = buf;
        const bp = ctx.createBiquadFilter();
        bp.type = "bandpass";
        bp.frequency.setValueAtTime(2400, t0);
        bp.frequency.exponentialRampToValueAtTime(1500, t0 + dur);
        bp.Q.value = 0.9;
        const g = ctx.createGain();
        g.gain.setValueAtTime(0.0001, t0);
        g.gain.exponentialRampToValueAtTime(vol, t0 + 0.012);
        g.gain.exponentialRampToValueAtTime(0.0001, t0 + dur);
        src.connect(bp); bp.connect(g); g.connect(master);
        src.start(t0); src.stop(t0 + dur + 0.02);
    }
    return { ensure, setEnabled, tick, thud, chime, sweep, scratch, isEnabled: () => enabled };
})();

window.__cdSfx = SFX;

function Vignette({ from = "#0a1628", to = "#050b16" }) {
    return (
        <div style={{
            position: "absolute", inset: 0,
            background: `radial-gradient(ellipse at center, ${from} 0%, ${to} 75%)`,
        }}/>
    );
}

function GridBg({ opacity = 0.07, color = "#22D3EE" }) {
    return (
        <svg width={STAGE_W} height={STAGE_H} style={{ position: "absolute", inset: 0, opacity, pointerEvents: "none" }}>
            <defs>
                <pattern id="g3-grid" width="48" height="48" patternUnits="userSpaceOnUse">
                    <path d="M 48 0 L 0 0 0 48" stroke={color} strokeWidth="1" fill="none" opacity="0.6"/>
                </pattern>
            </defs>
            <rect width={STAGE_W} height={STAGE_H} fill="url(#g3-grid)"/>
        </svg>
    );
}

function Aurora({ time, hueA = 195, hueB = 280 }) {
    const x = 640 + Math.sin(time * 0.4) * 180;
    const y = 360 + Math.cos(time * 0.3) * 80;
    const x2 = 640 - Math.sin(time * 0.5) * 220;
    const y2 = 360 - Math.cos(time * 0.4) * 120;
    return (
        <div style={{ position: "absolute", inset: 0, overflow: "hidden", pointerEvents: "none" }}>
            <div style={{
                position: "absolute", left: x - 320, top: y - 320,
                width: 640, height: 640, borderRadius: "50%",
                background: `radial-gradient(circle, hsla(${hueA},90%,60%,0.28), transparent 65%)`,
                filter: "blur(40px)",
            }}/>
            <div style={{
                position: "absolute", left: x2 - 280, top: y2 - 280,
                width: 560, height: 560, borderRadius: "50%",
                background: `radial-gradient(circle, hsla(${hueB},80%,60%,0.22), transparent 65%)`,
                filter: "blur(40px)",
            }}/>
        </div>
    );
}

const CAPTIONS = [
    { t: 0.2,  text: "Buyer paused the deal for GDPR proof." },
    { t: 6.4,  text: "Policies. Evidence. A pack security can open." },
    { t: 13.3, text: "Five tools. Weeks later, still open." },
    { t: 19.0, text: "Map controls. Fix code. Collect signatures." },
    { t: 25.6, text: "Connect GitHub. Score against named articles." },
    { t: 32.1, text: "Missing control? We open a pull request." },
    { t: 38.4, text: "Drafted. Sent. Signed to match the code." },
    { t: 44.4, text: "One Trust Center link for the buyer." },
    { t: 50.4, text: "Privacy review stops being a fire drill." },
    { t: 56.2, text: "Book a demo. See it on your repo." },
];

function CaptionLine({ time }) {
    let active = CAPTIONS[0];
    for (const c of CAPTIONS) if (time >= c.t) active = c;
    if (!active.text) return null;
    return (
        <div style={{
            position: "absolute", left: 0, right: 0, bottom: 56,
            textAlign: "center", pointerEvents: "none", zIndex: 5,
        }}>
            <span style={{
                display: "inline-block",
                background: "rgba(5,11,22,0.62)",
                backdropFilter: "blur(10px)",
                WebkitBackdropFilter: "blur(10px)",
                color: C.cream, fontFamily: fontDisplay, fontWeight: 600,
                fontSize: 18, letterSpacing: "0.005em",
                padding: "11px 20px", borderRadius: 12,
                border: "1px solid rgba(255,255,255,0.06)",
            }}>{active.text}</span>
        </div>
    );
}

// ─── Scene 1 · Cold open ─────────────────────────────────────────────────
function Scene1() {
    const { localTime, duration } = useSprite();
    const fade = spriteOpacity(localTime, duration, 0.4, 0.55);
    const titleP = Easing.easeOutBack(clamp(localTime / 0.55, 0, 1));
    const subP = Easing.easeOutCubic(clamp((localTime - 1.4) / 0.55, 0, 1));
    const brandP = clamp((localTime - 1.5) / 0.5, 0, 1);
    return (
        <div style={{ position: "absolute", inset: 0, opacity: fade }}>
            <Vignette from="#12182a" to="#050b16"/>
            <GridBg opacity={0.05}/>
            <Aurora time={localTime} hueA={200} hueB={300}/>
            <div style={{ position: "absolute", inset: 0, display: "grid", placeItems: "center" }}>
                <div style={{ textAlign: "center" }}>
                    <div style={{
                        fontFamily: fontDisplay, fontWeight: 900,
                        fontSize: 96, lineHeight: 1.0, letterSpacing: "-0.03em",
                        color: C.cream,
                        opacity: titleP,
                        transform: `scale(${0.86 + titleP * 0.14}) translateY(${(1 - titleP) * 28}px)`,
                    }}>Privacy review.</div>
                    <div style={{
                        marginTop: 22,
                        fontFamily: fontDisplay, fontWeight: 700,
                        fontSize: 42, letterSpacing: "-0.01em",
                        color: C.sev,
                        opacity: subP,
                        transform: `translateY(${(1 - subP) * 16}px)`,
                    }}>Deal paused.</div>
                </div>
            </div>
            <img
                src="/design-system/assets/logo-lockup-white.png"
                alt=""
                style={{
                    position: "absolute", left: 36, bottom: 36,
                    height: 28, opacity: brandP * 0.85, pointerEvents: "none",
                }}
            />
        </div>
    );
}

// ─── Scene 2 · The ask ───────────────────────────────────────────────────
const ASKS = [
    "Policies signed",
    "Technical controls evidenced",
    "Something we can share with security",
];

function Scene2() {
    const { localTime, duration } = useSprite();
    const fade = spriteOpacity(localTime, duration);
    return (
        <div style={{ position: "absolute", inset: 0, opacity: fade }}>
            <Vignette/>
            <GridBg opacity={0.04}/>
            <div style={{
                position: "absolute", left: 0, right: 0, top: 88,
                textAlign: "center",
                fontFamily: fontMono, fontSize: 13, letterSpacing: "0.16em",
                textTransform: "uppercase", color: C.cyan, fontWeight: 600,
                opacity: clamp(localTime / 0.4, 0, 1),
            }}>What the buyer asks for</div>
            <div style={{
                position: "absolute", inset: 0, display: "grid", placeItems: "center",
                paddingTop: 40,
            }}>
                <div style={{ display: "grid", gap: 18, width: 720 }}>
                    {ASKS.map((label, i) => {
                        const p = Easing.easeOutCubic(clamp((localTime - 0.35 - i * 0.55) / 0.45, 0, 1));
                        const hot = i === 2 && localTime > 3.2;
                        return (
                            <div key={label} style={{
                                padding: "22px 28px",
                                borderRadius: 16,
                                background: hot
                                    ? "linear-gradient(90deg, rgba(34,211,238,0.14), rgba(188,39,224,0.10))"
                                    : "rgba(255,255,255,0.03)",
                                border: hot
                                    ? `1px solid ${C.cyan}66`
                                    : "1px solid rgba(255,255,255,0.08)",
                                opacity: p,
                                transform: `translateY(${(1 - p) * 24}px)`,
                                boxShadow: hot ? `0 0 40px ${C.cyan}22` : "none",
                            }}>
                                <div style={{
                                    fontFamily: fontDisplay, fontWeight: 700, fontSize: 26,
                                    color: C.cream, display: "flex", alignItems: "center", gap: 16,
                                }}>
                                    <span style={{
                                        fontFamily: fontMono, fontSize: 13, color: C.cyan,
                                        width: 28,
                                    }}>0{i + 1}</span>
                                    {label}
                                </div>
                            </div>
                        );
                    })}
                </div>
            </div>
        </div>
    );
}

// ─── Scene 3 · Tool sprawl ───────────────────────────────────────────────
const TOOLS = ["Scanner", "GRC wiki", "Doc template", "E-sign", "Spreadsheet"];

function Scene3() {
    const { localTime, duration } = useSprite();
    const fade = spriteOpacity(localTime, duration);
    const crack = clamp((localTime - 2.4) / 1.2, 0, 1);
    return (
        <div style={{ position: "absolute", inset: 0, opacity: fade }}>
            <Vignette from="#1a1020" to="#050b16"/>
            <GridBg opacity={0.04} color="#F472B6"/>
            <div style={{ position: "absolute", inset: 0 }}>
                {TOOLS.map((label, i) => {
                    const p = Easing.easeOutCubic(clamp((localTime - i * 0.18) / 0.4, 0, 1));
                    const x = 160 + (i % 5) * 210;
                    const y = 180 + (i % 2) * 90 + Math.sin(localTime * 3 + i) * (6 + crack * 10);
                    const rot = (i - 2) * 3 + Math.sin(localTime * 5 + i) * crack * 8;
                    return (
                        <div key={label} style={{
                            position: "absolute", left: x, top: y,
                            padding: "14px 20px", borderRadius: 12,
                            background: "rgba(255,255,255,0.04)",
                            border: "1px solid rgba(255,255,255,0.10)",
                            fontFamily: fontDisplay, fontWeight: 700, fontSize: 20,
                            color: C.cream,
                            opacity: p * (1 - crack * 0.45),
                            filter: `grayscale(${crack})`,
                            transform: `rotate(${rot}deg) scale(${0.92 + p * 0.08})`,
                        }}>{label}</div>
                    );
                })}
            </div>
            <div style={{
                position: "absolute", inset: 0, display: "grid", placeItems: "center",
                paddingTop: 220,
            }}>
                <div style={{
                    fontFamily: fontDisplay, fontWeight: 900, fontSize: 48,
                    color: C.cream, textAlign: "center",
                    opacity: Easing.easeOutCubic(clamp((localTime - 2.6) / 0.5, 0, 1)),
                }}>
                    Five tools. Still not done.
                </div>
            </div>
        </div>
    );
}

// ─── Scene 4 · Product enter ─────────────────────────────────────────────
function Scene4() {
    const { localTime, duration } = useSprite();
    const fade = spriteOpacity(localTime, duration, 0.5, 0.45);
    const titleP = Easing.easeOutCubic(clamp(localTime / 0.7, 0, 1));
    const subP = Easing.easeOutCubic(clamp((localTime - 1.1) / 0.55, 0, 1));
    const lineP = Easing.easeOutCubic(clamp((localTime - 1.6) / 0.5, 0, 1));
    return (
        <div style={{ position: "absolute", inset: 0, opacity: fade }}>
            <Vignette/>
            <Aurora time={localTime} hueA={185} hueB={275}/>
            <GridBg opacity={0.05}/>
            <div style={{ position: "absolute", inset: 0, display: "grid", placeItems: "center" }}>
                <div style={{ textAlign: "center" }}>
                    <div style={{
                        fontFamily: fontDisplay, fontWeight: 900,
                        fontSize: 84, letterSpacing: "-0.03em",
                        background: `linear-gradient(135deg, ${C.cyan}, ${C.blue} 50%, ${C.purple})`,
                        WebkitBackgroundClip: "text", backgroundClip: "text",
                        color: "transparent",
                        opacity: titleP,
                        transform: `translateY(${(1 - titleP) * 20}px)`,
                    }}>CyberDebunk</div>
                    <div style={{
                        width: 280, height: 3, margin: "22px auto 0",
                        borderRadius: 99,
                        background: `linear-gradient(90deg, ${C.cyan}, ${C.purple})`,
                        transform: `scaleX(${lineP})`,
                        transformOrigin: "center",
                    }}/>
                    <div style={{
                        marginTop: 28,
                        fontFamily: fontDisplay, fontWeight: 700, fontSize: 28,
                        color: C.cream,
                        opacity: subP,
                    }}>GDPR program. Code fixes. Signatures.</div>
                </div>
            </div>
        </div>
    );
}

// ─── Scene 5 · Connect and score ─────────────────────────────────────────
function Scene5() {
    const { localTime, duration } = useSprite();
    const fade = spriteOpacity(localTime, duration);
    const connected = localTime > 1.1;
    const scoreP = Easing.easeOutCubic(clamp((localTime - 1.6) / 1.4, 0, 1));
    const score = Math.round(34 * scoreP);
    const rows = [
        { name: "Right to erasure", bad: true },
        { name: "Retention / purge", bad: false },
        { name: "Access logging", bad: false },
    ];
    return (
        <div style={{ position: "absolute", inset: 0, opacity: fade }}>
            <Vignette/>
            <GridBg opacity={0.04}/>
            <div style={{ position: "absolute", inset: 0, display: "grid", placeItems: "center" }}>
                <div style={{
                    width: 860, borderRadius: 20,
                    background: "linear-gradient(180deg, rgba(255,255,255,0.04), rgba(255,255,255,0.015))",
                    border: "1px solid rgba(255,255,255,0.08)",
                    padding: 28,
                    boxShadow: "0 40px 100px -40px rgba(0,0,0,0.7)",
                }}>
                    <div style={{ display: "flex", alignItems: "center", gap: 14, marginBottom: 22 }}>
                        <div style={{
                            fontFamily: fontMono, fontSize: 13, color: C.cream,
                            padding: "8px 14px", borderRadius: 999,
                            background: connected ? "rgba(34,211,238,0.12)" : "rgba(255,255,255,0.04)",
                            border: connected ? `1px solid ${C.cyan}55` : "1px solid rgba(255,255,255,0.08)",
                        }}>
                            acme/api
                            <span style={{
                                marginLeft: 10, color: connected ? C.ok : C.mute, fontSize: 11,
                            }}>{connected ? "connected" : "not connected"}</span>
                        </div>
                        <div style={{ marginLeft: "auto", textAlign: "right" }}>
                            <div style={{ fontFamily: fontMono, fontSize: 11, color: C.mute, letterSpacing: "0.12em" }}>SCORE</div>
                            <div style={{
                                fontFamily: fontDisplay, fontWeight: 900, fontSize: 36,
                                color: scoreP < 0.05 ? C.mute : C.cream,
                            }}>
                                {scoreP < 0.05 ? "not scanned" : `${score}%`}
                            </div>
                        </div>
                    </div>
                    <div style={{ display: "grid", gap: 10 }}>
                        {rows.map((r, i) => {
                            const p = Easing.easeOutCubic(clamp((localTime - 2.0 - i * 0.25) / 0.4, 0, 1));
                            const pulse = r.bad && localTime > 3.2 ? (0.5 + 0.5 * Math.sin(localTime * 6)) : 0;
                            return (
                                <div key={r.name} style={{
                                    display: "flex", alignItems: "center", gap: 14,
                                    padding: "14px 16px", borderRadius: 12,
                                    background: r.bad ? `rgba(248,113,113,${0.06 + pulse * 0.08})` : "rgba(255,255,255,0.025)",
                                    border: r.bad ? `1px solid rgba(248,113,113,${0.35 + pulse * 0.35})` : "1px solid rgba(255,255,255,0.06)",
                                    opacity: p,
                                    transform: `translateX(${(1 - p) * -12}px)`,
                                }}>
                                    <span style={{
                                        width: 10, height: 10, borderRadius: 99,
                                        background: r.bad ? C.sev : C.ok,
                                    }}/>
                                    <span style={{ fontFamily: fontDisplay, fontWeight: 600, fontSize: 18, color: C.cream }}>{r.name}</span>
                                    <span style={{
                                        marginLeft: "auto", fontFamily: fontMono, fontSize: 11,
                                        color: r.bad ? C.sev : C.ok, letterSpacing: "0.08em",
                                    }}>{r.bad ? "GAP" : "OK"}</span>
                                </div>
                            );
                        })}
                    </div>
                </div>
            </div>
        </div>
    );
}

// ─── Scene 6 · Check then PR ─────────────────────────────────────────────
function Scene6() {
    const { localTime, duration } = useSprite();
    const fade = spriteOpacity(localTime, duration);
    const chips = ["Why it matters", "Check first", "Create PR"];
    const activeChip = localTime < 1.4 ? 0 : localTime < 2.8 ? 1 : 2;
    const diffLines = [
        { kind: "ctx", text: "  router.get('/me', auth, getProfile);" },
        { kind: "add", text: "+ router.delete('/me', auth, eraseUser);" },
        { kind: "add", text: "+ await hardDeletePersonalData(userId);" },
    ];
    const prP = Easing.easeOutCubic(clamp((localTime - 3.6) / 0.5, 0, 1));
    return (
        <div style={{ position: "absolute", inset: 0, opacity: fade }}>
            <Vignette/>
            <GridBg opacity={0.04}/>
            <div style={{ position: "absolute", inset: 0, display: "grid", placeItems: "center" }}>
                <div style={{ width: 880 }}>
                    <div style={{ display: "flex", gap: 10, marginBottom: 18 }}>
                        {chips.map((c, i) => (
                            <div key={c} style={{
                                padding: "8px 14px", borderRadius: 999,
                                fontFamily: fontMono, fontSize: 12, letterSpacing: "0.06em",
                                color: i === activeChip ? C.cyan : C.mute,
                                background: i === activeChip ? "rgba(34,211,238,0.12)" : "rgba(255,255,255,0.03)",
                                border: `1px solid ${i === activeChip ? "rgba(34,211,238,0.35)" : "rgba(255,255,255,0.08)"}`,
                            }}>{c}</div>
                        ))}
                    </div>
                    <div style={{
                        borderRadius: 16, overflow: "hidden",
                        border: "1px solid rgba(255,255,255,0.08)",
                        background: "#07101c",
                        fontFamily: fontMono, fontSize: 15, lineHeight: 1.7,
                        padding: "18px 22px",
                    }}>
                        <div style={{ color: C.mute, marginBottom: 8, fontSize: 12 }}>routes/users.js</div>
                        {diffLines.map((l, i) => {
                            const p = Easing.easeOutCubic(clamp((localTime - 1.6 - i * 0.35) / 0.35, 0, 1));
                            return (
                                <div key={i} style={{
                                    opacity: p,
                                    color: l.kind === "add" ? C.ok : C.mute,
                                    background: l.kind === "add" ? "rgba(134,239,172,0.08)" : "transparent",
                                    whiteSpace: "pre",
                                }}>{l.text}</div>
                            );
                        })}
                    </div>
                    <div style={{
                        marginTop: 16, display: "inline-flex", alignItems: "center", gap: 12,
                        padding: "14px 20px", borderRadius: 14,
                        background: `linear-gradient(90deg, ${C.cyan}, ${C.blue})`,
                        color: C.ink, fontFamily: fontDisplay, fontWeight: 800, fontSize: 18,
                        opacity: prP,
                        transform: `translateY(${(1 - prP) * 12}px) scale(${0.96 + prP * 0.04})`,
                        boxShadow: localTime > 4.2
                            ? `0 0 0 ${4 + Math.sin(localTime * 8) * 3}px rgba(34,211,238,0.25)`
                            : "none",
                    }}>
                        Open pull request
                    </div>
                </div>
            </div>
        </div>
    );
}

// ─── Scene 7 · Policies and signatures ───────────────────────────────────
const POLICY_DOCS = [
    { title: "Privacy policy", art: "Art. 13 / 14", signer: "DPO", at: 2.9 },
    { title: "Records of processing", art: "Art. 30", signer: "Legal", at: 3.45 },
    { title: "Sub-processor list", art: "Art. 28", signer: "CTO", at: 4.0 },
];

function Scene7() {
    const { localTime, duration } = useSprite();
    const fade = spriteOpacity(localTime, duration);
    const headP = Easing.easeOutCubic(clamp(localTime / 0.45, 0, 1));
    const sendP = Easing.easeOutCubic(clamp((localTime - 1.7) / 0.4, 0, 1));
    const sent = localTime > 2.35;
    return (
        <div style={{ position: "absolute", inset: 0, opacity: fade }}>
            <Vignette from="#0c1524" to="#050b16"/>
            <GridBg opacity={0.045}/>
            <div style={{
                position: "absolute", left: 0, right: 0, top: 72, textAlign: "center",
                opacity: headP, transform: `translateY(${(1 - headP) * 14}px)`,
            }}>
                <div style={{
                    fontFamily: fontMono, fontSize: 12, letterSpacing: "0.16em",
                    textTransform: "uppercase", color: C.cyan, fontWeight: 600, marginBottom: 10,
                }}>Policies · law-mapped</div>
                <div style={{
                    fontFamily: fontDisplay, fontWeight: 900, fontSize: 40,
                    letterSpacing: "-0.02em", color: C.cream,
                }}>Drafted. Sent. Signed.</div>
            </div>
            <div style={{
                position: "absolute", left: "50%", top: 168, transform: "translateX(-50%)",
                width: 860,
            }}>
                <div style={{
                    borderRadius: 20, overflow: "hidden",
                    border: "1px solid rgba(255,255,255,0.08)",
                    background: "linear-gradient(180deg, rgba(255,255,255,0.04), rgba(255,255,255,0.015))",
                    boxShadow: "0 36px 90px -40px rgba(0,0,0,0.75)",
                }}>
                    <div style={{
                        display: "flex", alignItems: "center", gap: 12,
                        padding: "14px 22px",
                        borderBottom: "1px solid rgba(255,255,255,0.06)",
                        background: "rgba(0,0,0,0.18)",
                    }}>
                        <span style={{ width: 8, height: 8, borderRadius: 99, background: C.sev, opacity: 0.8 }}/>
                        <span style={{ width: 8, height: 8, borderRadius: 99, background: "#FBBF24", opacity: 0.8 }}/>
                        <span style={{ width: 8, height: 8, borderRadius: 99, background: C.ok, opacity: 0.8 }}/>
                        <span style={{
                            marginLeft: 8, fontFamily: fontMono, fontSize: 12, color: C.mute,
                        }}>signature request · acme-gdpr-pack.pdf</span>
                        <span style={{
                            marginLeft: "auto",
                            fontFamily: fontMono, fontSize: 11, letterSpacing: "0.08em",
                            color: sent ? C.ok : C.cyan,
                            opacity: sendP,
                        }}>{sent ? "SENT" : "READY TO SEND"}</span>
                    </div>
                    {POLICY_DOCS.map((d, i) => {
                        const p = Easing.easeOutCubic(clamp((localTime - 0.35 - i * 0.22) / 0.4, 0, 1));
                        const signedP = Easing.easeOutBack(clamp((localTime - d.at) / 0.35, 0, 1));
                        const signed = signedP > 0.2;
                        return (
                            <div key={d.title} style={{
                                display: "grid",
                                gridTemplateColumns: "1fr 140px 150px",
                                gap: 16, alignItems: "center",
                                padding: "18px 22px",
                                borderTop: i ? "1px solid rgba(255,255,255,0.05)" : "none",
                                opacity: p,
                                transform: `translateY(${(1 - p) * 12}px)`,
                                background: signed ? "rgba(134,239,172,0.04)" : "transparent",
                            }}>
                                <div>
                                    <div style={{
                                        fontFamily: fontDisplay, fontWeight: 700, fontSize: 20, color: C.cream,
                                    }}>{d.title}</div>
                                    <div style={{
                                        marginTop: 4, fontFamily: fontMono, fontSize: 12, color: C.mute,
                                    }}>{d.art}</div>
                                </div>
                                <div style={{
                                    fontFamily: fontMono, fontSize: 12, color: C.mute, textAlign: "right",
                                }}>Signer: {d.signer}</div>
                                <div style={{
                                    justifySelf: "end",
                                    minWidth: 118, textAlign: "center",
                                    padding: "8px 12px", borderRadius: 999,
                                    fontFamily: fontMono, fontSize: 12, fontWeight: 700,
                                    letterSpacing: "0.08em",
                                    color: signed ? C.ink : C.mute,
                                    background: signed
                                        ? `linear-gradient(90deg, ${C.ok}, #4ADE80)`
                                        : "rgba(255,255,255,0.04)",
                                    border: signed ? "none" : "1px solid rgba(255,255,255,0.08)",
                                    transform: `scale(${0.9 + signedP * 0.1})`,
                                    boxShadow: signed ? `0 0 24px ${C.ok}33` : "none",
                                }}>
                                    {signed ? "SIGNED ✓" : "PENDING"}
                                </div>
                            </div>
                        );
                    })}
                </div>
                <div style={{
                    marginTop: 18, display: "flex", justifyContent: "center",
                    opacity: sendP,
                    transform: `translateY(${(1 - sendP) * 10}px)`,
                }}>
                    <div style={{
                        padding: "12px 22px", borderRadius: 12,
                        fontFamily: fontDisplay, fontWeight: 800, fontSize: 16,
                        color: sent ? C.ok : C.ink,
                        background: sent
                            ? "rgba(134,239,172,0.10)"
                            : `linear-gradient(90deg, ${C.cyan}, ${C.blue})`,
                        border: sent ? `1px solid ${C.ok}55` : "none",
                        boxShadow: sent ? "none" : `0 12px 36px -14px ${C.cyan}88`,
                    }}>
                        {sent ? "Signatures collecting…" : "Send for signature"}
                    </div>
                </div>
            </div>
        </div>
    );
}

// ─── Scene 8 · Trust Center ──────────────────────────────────────────────
function Scene8() {
    const { localTime, duration } = useSprite();
    const fade = spriteOpacity(localTime, duration);
    const linkP = Easing.easeOutCubic(clamp(localTime / 0.55, 0, 1));
    const panelP = Easing.easeOutCubic(clamp((localTime - 1.4) / 0.55, 0, 1));
    const items = ["Controls mapped", "Policies signed", "Security posture"];
    return (
        <div style={{ position: "absolute", inset: 0, opacity: fade }}>
            <Vignette/>
            <GridBg opacity={0.04}/>
            <div style={{
                position: "absolute", inset: 0, display: "grid",
                gridTemplateColumns: "1fr 1fr", placeItems: "center", gap: 24, padding: "0 64px",
            }}>
                <div style={{
                    width: "100%", maxWidth: 420, padding: 24, borderRadius: 18,
                    background: "rgba(255,255,255,0.03)",
                    border: "1px solid rgba(34,211,238,0.28)",
                    opacity: linkP,
                    transform: `translateX(${(1 - linkP) * -20}px)`,
                    boxShadow: localTime > 1.0 ? `0 0 36px ${C.cyan}18` : "none",
                }}>
                    <div style={{ fontFamily: fontMono, fontSize: 11, color: C.cyan, letterSpacing: "0.14em", marginBottom: 10 }}>SHAREABLE LINK</div>
                    <div style={{ fontFamily: fontMono, fontSize: 16, color: C.cream }}>trust.cyberdebunk.com/acme</div>
                    <div style={{ marginTop: 14, fontFamily: fontDisplay, fontSize: 15, color: C.mute }}>
                        One link. Same evidence your team just produced.
                    </div>
                </div>
                <div style={{
                    width: "100%", maxWidth: 400, padding: 24, borderRadius: 18,
                    background: "linear-gradient(180deg, rgba(255,255,255,0.05), rgba(255,255,255,0.02))",
                    border: "1px solid rgba(255,255,255,0.08)",
                    opacity: panelP,
                    transform: `translateX(${(1 - panelP) * 24}px)`,
                }}>
                    <div style={{ display: "flex", alignItems: "center", gap: 12, marginBottom: 18 }}>
                        <div style={{
                            width: 36, height: 36, borderRadius: "50%",
                            background: `linear-gradient(135deg, ${C.cyan}, ${C.purple})`,
                        }}/>
                        <div>
                            <div style={{ fontFamily: fontDisplay, fontWeight: 700, color: C.cream, fontSize: 16 }}>Buyer security</div>
                            <div style={{ fontFamily: fontMono, fontSize: 11, color: C.mute }}>opened Trust Center</div>
                        </div>
                    </div>
                    {items.map((item, i) => {
                        const p = Easing.easeOutCubic(clamp((localTime - 1.8 - i * 0.3) / 0.35, 0, 1));
                        return (
                            <div key={item} style={{
                                display: "flex", alignItems: "center", gap: 10,
                                padding: "10px 0",
                                borderTop: i ? "1px solid rgba(255,255,255,0.06)" : "none",
                                opacity: p,
                            }}>
                                <span style={{ color: C.ok, fontWeight: 800 }}>✓</span>
                                <span style={{ fontFamily: fontDisplay, fontSize: 16, color: C.cream }}>{item}</span>
                            </div>
                        );
                    })}
                </div>
            </div>
        </div>
    );
}

// ─── Scene 9 · Outcome ───────────────────────────────────────────────────
function Scene9() {
    const { localTime, duration } = useSprite();
    const fade = spriteOpacity(localTime, duration, 0.45, 0.45);
    const titleP = Easing.easeOutCubic(clamp(localTime / 0.55, 0, 1));
    const subP = Easing.easeOutCubic(clamp((localTime - 1.0) / 0.5, 0, 1));
    const checkP = Easing.easeOutBack(clamp((localTime - 1.6) / 0.5, 0, 1));
    return (
        <div style={{ position: "absolute", inset: 0, opacity: fade }}>
            <Vignette from="#0c1a22" to="#050b16"/>
            <GridBg opacity={0.06}/>
            <div style={{ position: "absolute", inset: 0, display: "grid", placeItems: "center" }}>
                <div style={{ textAlign: "center" }}>
                    <div style={{
                        width: 72, height: 72, borderRadius: "50%", margin: "0 auto 22px",
                        background: "rgba(134,239,172,0.12)", border: `2px solid ${C.ok}`,
                        display: "grid", placeItems: "center",
                        color: C.ok, fontSize: 36, fontWeight: 800,
                        opacity: checkP,
                        transform: `scale(${0.7 + checkP * 0.3})`,
                    }}>✓</div>
                    <div style={{
                        fontFamily: fontDisplay, fontWeight: 900, fontSize: 64,
                        letterSpacing: "-0.025em", color: C.cream,
                        opacity: titleP,
                    }}>Review unblocked.</div>
                    <div style={{
                        marginTop: 18,
                        fontFamily: fontDisplay, fontWeight: 600, fontSize: 24,
                        color: C.mute,
                        opacity: subP,
                    }}>Policies. PRs. Proof. One place.</div>
                </div>
            </div>
        </div>
    );
}

// ─── Scene 10 · CTA ──────────────────────────────────────────────────────
function Scene10() {
    const { localTime, duration } = useSprite();
    // Short end card (~2.7s): snap elements in fast so VO and visuals finish together
    const fade = spriteOpacity(localTime, duration, 0.25, 0.12);
    const logoP = Easing.easeOutCubic(clamp(localTime / 0.28, 0, 1));
    const titleP = Easing.easeOutCubic(clamp((localTime - 0.15) / 0.3, 0, 1));
    const subP = Easing.easeOutCubic(clamp((localTime - 0.4) / 0.28, 0, 1));
    const ctaP = Easing.easeOutCubic(clamp((localTime - 0.7) / 0.28, 0, 1));
    const footP = Easing.easeOutCubic(clamp((localTime - 1.05) / 0.25, 0, 1));
    return (
        <div style={{ position: "absolute", inset: 0, opacity: fade }}>
            <Vignette from="#0a1628" to="#050b16"/>
            <Aurora time={localTime} hueA={190} hueB={275}/>
            <div style={{ position: "absolute", inset: 0, display: "grid", placeItems: "center" }}>
                <div style={{ textAlign: "center", padding: "0 56px", maxWidth: 980 }}>
                    <img
                        src="/design-system/assets/logo-lockup-white.png"
                        alt="CyberDebunk"
                        style={{
                            height: 72, width: "auto", display: "block", margin: "0 auto 28px",
                            opacity: logoP,
                            transform: `scale(${0.9 + logoP * 0.1})`,
                            filter: `drop-shadow(0 0 28px ${C.cyan}44)`,
                        }}
                    />
                    <div style={{
                        fontFamily: fontDisplay, fontWeight: 900,
                        fontSize: 72, lineHeight: 1.05, letterSpacing: "-0.03em",
                        color: C.cream,
                        opacity: titleP,
                        transform: `translateY(${(1 - titleP) * 18}px)`,
                    }}>
                        Book a demo.
                    </div>
                    <div style={{
                        marginTop: 16,
                        fontFamily: fontDisplay, fontWeight: 700,
                        fontSize: 32, lineHeight: 1.25, letterSpacing: "-0.015em",
                        opacity: subP,
                        transform: `translateY(${(1 - subP) * 12}px)`,
                        background: `linear-gradient(135deg, ${C.cyan}, ${C.blue} 55%, ${C.purple})`,
                        WebkitBackgroundClip: "text", backgroundClip: "text",
                        color: "transparent",
                    }}>
                        See it on your repo.
                    </div>
                    <div style={{
                        marginTop: 34,
                        display: "inline-flex", alignItems: "center", gap: 12,
                        padding: "16px 28px", borderRadius: 14,
                        background: `linear-gradient(90deg, ${C.cyan}, ${C.blue})`,
                        color: C.ink,
                        fontFamily: fontDisplay, fontWeight: 800, fontSize: 20,
                        opacity: ctaP,
                        transform: `translateY(${(1 - ctaP) * 12}px) scale(${0.96 + ctaP * 0.04})`,
                        boxShadow: `0 16px 48px -16px ${C.cyan}99`,
                    }}>
                        cyberdebunk.com
                        <span style={{ fontSize: 22, lineHeight: 1 }}>→</span>
                    </div>
                    <div style={{
                        marginTop: 22,
                        fontFamily: fontMono, fontSize: 12, letterSpacing: "0.12em",
                        textTransform: "uppercase", color: C.mute,
                        opacity: footP,
                    }}>
                        Made in Europe · Hosted in the EU
                    </div>
                </div>
            </div>
        </div>
    );
}

function ExplainerScene() {
    const { time } = useTimeline();
    return (
        <React.Fragment>
            {/* Windows locked to ElevenLabs Hamza take (~58.83s) */}
            <Sprite start={0.0} end={6.4}><Scene1/></Sprite>
            <Sprite start={6.4} end={13.3}><Scene2/></Sprite>
            <Sprite start={13.3} end={19.0}><Scene3/></Sprite>
            <Sprite start={19.0} end={25.6}><Scene4/></Sprite>
            <Sprite start={25.6} end={32.1}><Scene5/></Sprite>
            <Sprite start={32.1} end={38.4}><Scene6/></Sprite>
            <Sprite start={38.4} end={44.4}><Scene7/></Sprite>
            <Sprite start={44.4} end={50.4}><Scene8/></Sprite>
            <Sprite start={50.4} end={56.2}><Scene9/></Sprite>
            <Sprite start={56.2} end={58.9}><Scene10/></Sprite>
            <CaptionLine time={time}/>
            {time < 56.4 && (
                <img
                    src="/design-system/assets/logo-lockup-white.png"
                    alt="CyberDebunk"
                    style={{
                        position: "absolute", top: 24, right: 32,
                        height: 32, width: "auto", display: "block",
                        opacity: 0.8, pointerEvents: "none",
                    }}
                />
            )}
        </React.Fragment>
    );
}

const SOUND_CUES = [
    { t: 2.5,  fn: () => SFX.thud({ freq: 85, dur: 0.35, vol: 0.42 }) },
    { t: 6.6,  fn: () => SFX.tick({ freq: 1100, vol: 0.12, type: "triangle" }) },
    { t: 8.2,  fn: () => SFX.tick({ freq: 1240, vol: 0.12, type: "triangle" }) },
    { t: 11.1, fn: () => SFX.tick({ freq: 1380, vol: 0.14, type: "triangle" }) },
    { t: 13.5, fn: () => SFX.sweep({ from: 220, to: 900, dur: 0.5, vol: 0.09 }) },
    { t: 15.8, fn: () => SFX.scratch({ dur: 0.28, vol: 0.22 }) },
    { t: 17.2, fn: () => SFX.scratch({ dur: 0.26, vol: 0.20 }) },
    { t: 19.3, fn: () => SFX.sweep({ from: 240, to: 1400, dur: 0.8, vol: 0.11 }) },
    { t: 20.0, fn: () => SFX.chime({ freq: 880, dur: 0.65, vol: 0.16 }) },
    { t: 26.0, fn: () => SFX.tick({ freq: 1500, vol: 0.14, type: "triangle" }) },
    { t: 27.2, fn: () => SFX.thud({ freq: 100, dur: 0.28, vol: 0.28 }) },
    { t: 32.4, fn: () => SFX.tick({ freq: 1200, vol: 0.10 }) },
    { t: 34.0, fn: () => SFX.tick({ freq: 1400, vol: 0.10 }) },
    { t: 36.5, fn: () => SFX.chime({ freq: 1100, dur: 0.4, vol: 0.14 }) },
    { t: 39.0, fn: () => SFX.tick({ freq: 1400, vol: 0.12, type: "triangle" }) },
    { t: 40.8, fn: () => SFX.thud({ freq: 95, dur: 0.22, vol: 0.28 }) },
    { t: 41.5, fn: () => SFX.tick({ freq: 1600, vol: 0.14 }) },
    { t: 42.2, fn: () => SFX.tick({ freq: 1700, vol: 0.14 }) },
    { t: 43.0, fn: () => SFX.chime({ freq: 990, dur: 0.45, vol: 0.14 }) },
    { t: 44.7, fn: () => SFX.sweep({ from: 300, to: 1200, dur: 0.45, vol: 0.11 }) },
    { t: 47.2, fn: () => SFX.tick({ freq: 1700, vol: 0.10 }) },
    { t: 51.0, fn: () => SFX.chime({ freq: 760, dur: 0.7, vol: 0.16 }) },
    { t: 56.4, fn: () => SFX.sweep({ from: 220, to: 1100, dur: 0.7, vol: 0.11 }) },
    { t: 56.7, fn: () => SFX.chime({ freq: 660, dur: 1.0, vol: 0.18 }) },
    { t: 56.8, fn: () => SFX.chime({ freq: 990, dur: 1.0, vol: 0.13 }) },
];

function SoundtrackCues() {
    const { time } = useTimeline();
    const lastTimeRef = React.useRef(0);
    React.useEffect(() => {
        const prev = lastTimeRef.current;
        const now = time;
        if (now < prev - 0.2) { lastTimeRef.current = now; return; }
        for (const cue of SOUND_CUES) {
            if (cue.t > prev && cue.t <= now) {
                try { cue.fn(); } catch (e) {}
            }
        }
        lastTimeRef.current = now;
    }, [time]);
    return null;
}

function VoiceoverSync({ src, offset = 0 }) {
    const { time, playing } = useTimeline();
    const audioRef = React.useRef(null);
    const [ready, setReady] = React.useState(false);

    React.useEffect(() => {
        if (!playing) return;
        SFX.ensure();
        SFX.setEnabled(true);
        const a = audioRef.current;
        if (!a || !ready) return;
        const p = a.play();
        if (p && p.catch) p.catch(() => {});
    }, [playing, ready]);

    React.useEffect(() => {
        const a = audioRef.current;
        if (!a || !ready) return;
        if (!playing) a.pause();
    }, [playing, ready]);

    React.useEffect(() => {
        const a = audioRef.current;
        if (!a || !ready) return;
        const target = Math.max(0, time - offset);
        const drift = Math.abs(a.currentTime - target);
        if (drift > 0.30) {
            try { a.currentTime = Math.min(target, (a.duration || target) - 0.01); } catch (e) {}
        }
    }, [time, ready, offset]);

    return (
        <audio
            ref={audioRef}
            src={src}
            preload="auto"
            onLoadedMetadata={() => setReady(true)}
        />
    );
}

function App() {
    return (
        <Stage
            width={STAGE_W}
            height={STAGE_H}
            duration={58.9}
            background="#050b16"
            persistKey="cd-explainer-3-hamza"
            autoplay={false}
        >
            <ExplainerScene/>
            <SoundtrackCues/>
            <VoiceoverSync src="/design-system/explainer-3-voiceover.mp3" offset={0}/>
        </Stage>
    );
}

ReactDOM.createRoot(document.getElementById("root")).render(<App/>);
