/* EIPME NIS2-Spot "Wissen, ob NIS2 Sie betrifft." — 1200×1200, 15s, comic style. Schwesterfigur zum Handwerker: Chef eines Zulieferbetriebs (Sakko, offener Kragen, Sneaker), gleiche Farbwelt. Reads Stage/Sprite/useTime/useSprite/Easing/interpolate/clamp from window (animations.jsx). */ const { Stage, Sprite, useTime, useSprite, Easing, interpolate, clamp } = window; const R = React; const C = { blue: "#1F5EFF", blue600: "#0A47E0", blue300: "#8AA8FF", blue100: "#D9E4FF", blue50: "#EEF3FF", orange: "#FF7A1A", orange300: "#FFA15C", orange100: "#FFDEC2", red: "#FF3B47", red600: "#E51F2D", red100: "#FFD6DA", green: "#0E6B27", greenTint: "rgba(14,107,39,0.14)", ink: "#0C0E13", ink700: "#1C202A", ink500: "#3D434F", ink300: "#858B98", ink100: "#D6D9DF", paper: "#FAFBFC", white: "#FFFFFF", sky: "#EEF3FF", skin: "#FFD9B8", skinShade: "#F2BE93", cheek: "#FFB08A", }; const INK = C.ink; const GEIST = "'Geist', system-ui, sans-serif"; const MONO = "'Geist Mono', ui-monospace, monospace"; /* ============================================================ KOPF CHEF — Comic-Kopf mit Kurzhaar (statt Mütze), gleiche Mimik. ============================================================ */ function KopfChef({ expression = "smile", blink = 0, talkPhase = 0 }) { const shocked = expression === "shock"; let eyes; if (expression === "relaxed") { eyes = ( ); } else if (shocked) { eyes = ( ); } else { const closed = blink > 0.5; eyes = closed ? ( ) : ( ); } let mouth; if (expression === "talk") { const open = 5 + 11 * Math.abs(Math.sin(talkPhase)); mouth = ; } else if (expression === "smile" || expression === "relaxed") { mouth = ; } else if (shocked) { mouth = ; } else { mouth = ; } const brows = shocked ? ( ) : ( ); return ( {!shocked && } {!shocked && } {brows} {eyes} {/* Brille */} {mouth} {/* kurzes, dunkles Haar mit Stirntolle — jünger, kein Grau */} ); } /* ---------- Sprechblase ---------- */ function SpeechBubble({ x, y, w, pop, text, tailX = 64, fontSize = 54, align = "left", accent = null }) { const s = Easing.easeOutBack(clamp(pop, 0, 1)); return (
{text}
); } function Sparkle({ x, y, p, size = 26, color = C.orange }) { const lp = clamp(p, 0, 1); if (lp <= 0) return null; const s = Easing.easeOutBack(Math.min(1, lp * 1.6)); const o = 1 - Math.max(0, lp - 0.6) / 0.4; return ( ); } function LogoLockup({ size = 64 }) { return (
EIPME
EIPME
); } /* ============================================================ BÜRO-KULISSE — kleines Büro eines Zulieferbetriebs, Tag. ============================================================ */ function BueroRaum() { return ( {/* Fenster mit Tageslicht */} {/* Regal mit Kartons */} {/* Firmenschild (oben, frei sichtbar) */} Zulieferbetrieb SEIT 2004 ); } /* ---------- Chef-Figur, ganze Figur: Sakko, offenes Hemd, Sneaker ---------- */ function ChefBueste({ expression = "smile", blink = 0, pose = "crossed", t = 0, sweat = 0 }) { const bob = Math.sin(t * 1.7) * 2.4; return ( {/* Hose */} {/* Sneaker */} {/* Hals — statisch, überbrückt das Kopf-Bobbing hinter dem Hemd */} {/* Hemd */} {/* Gürtelschnalle im Sakko-Spalt */} {/* Sakko, offen */} {/* Revers */} {/* Hemdkragen offen */} {/* Einstecktuch */} {/* Sakko-Knopf */} {pose === "crossed" && ( )} {pose === "desk" && ( )} {pose === "receive" && ( )} {/* Cartoon-Schweißtropfen */} {sweat > 0 && ( )} ); } /* ============================================================ SZENE 1 — Chef entspannt, "NIS2? Betrifft uns nicht." 0 … 3.3 ============================================================ */ function SceneRelaxed() { const { localTime: lt } = useSprite(); const sc = interpolate([0, 3.3], [1.04, 1.1], Easing.easeInOutCubic)(lt); const blink = (lt > 1.7 && lt < 1.86) || (lt > 2.7 && lt < 2.86) ? 1 : 0; const bubblePop = clamp((lt - 0.35) / 0.4, 0, 1); return (
{/* Schreibtisch */} {/* Deko auf dem Tisch */} {/* Chef steht vor dem Tisch, Arme verschränkt — ganze Figur */}
{/* Sprechblase am Kopf verankert, Spitze zeigt auf den Mund */} NIS2?
Betrifft uns nicht.} />
); } /* ============================================================ SZENE 2 — Mail ploppt auf, Chef erschrickt. 3.0 … 6.3 ============================================================ */ function SceneMail() { const { localTime: lt } = useSprite(); const sc = interpolate([0, 4.45], [1.05, 1.12], Easing.easeInOutCubic)(lt); const mailPop = Easing.easeOutBack(clamp((lt - 0.55) / 0.4, 0, 1)); const shock = lt > 0.95; const sweat = clamp((lt - 1.25) / 0.4, 0, 1); const blink = 0; const shake = shock && lt < 1.7 ? Math.sin(lt * 40) * 2.2 : 0; return (
{/* Schreibtischplatte */} {/* Monitor */} {/* Mail-Karte auf dem Bildschirm — visueller Fokus */}
Neue Nachricht
G
Absender
Ihr Großkunde
Betreff
Sicherheitsnachweis gemäß NIS2 erforderlich
{/* Chef rechts, erschrickt — hinter dem Tisch, unten sauber abgeschnitten */}
{/* Ausrufe-Funken beim Schreck */} {shock && ( )}
); } /* ============================================================ SZENE 3 — Zahlenkarte, still. 6.0 … 9.3 ============================================================ */ function SceneStat() { const { localTime: lt } = useSprite(); const cardPop = Easing.easeOutBack(clamp((lt - 0.15) / 0.45, 0, 1)); const numGrow = interpolate([0.2, 0.9], [0.8, 1], Easing.easeOutBack)(clamp(lt, 0, 3)); const barP = Easing.easeInOutCubic(clamp((lt - 0.5) / 0.7, 0, 1)); return (
NIS2 · Betroffenheit
48 %
schätzen ihre Betroffenheit falsch ein
29.500 Betriebe direkt betroffen · Quelle: Cyber Security Report 2026 / BSI
); } /* ============================================================ SZENE 4 — EIPME überreicht NIS2-Check. 9.0 … 12.3 ============================================================ */ function EipmeBadge({ pop, t }) { const s = Easing.easeOutBack(clamp(pop, 0, 1)); if (s <= 0) return null; return (
EIPME
); } function SceneResolve() { const { localTime: lt } = useSprite(); const badgePop = clamp((lt - 0.2) / 0.45, 0, 1); const docPop = Easing.easeOutBack(clamp((lt - 0.7) / 0.5, 0, 1)); const check1 = clamp((lt - 1.2) / 0.4, 0, 1); const check2 = clamp((lt - 1.7) / 0.4, 0, 1); const smile = lt > 1.0; const blink = (lt > 2.2 && lt < 2.36) ? 1 : 0; return (
{/* Chef links, lächelt — ganze Figur */}
{/* EIPME-Badge rechts */}
{/* Dokument NIS2-Check in der Mitte */}
Dokument
NIS2-Check
{/* Zwei Häkchen-Zeilen unten */}
{[["Betroffenheit geprüft", check1], ["Nachweis für Auftraggeber", check2]].map(([label, cp], i) => (
{label}
))}
); } /* ============================================================ SZENE 5 — Schlussbild, komplett still. 12.0 … 15.0 ============================================================ */ function SceneOutro({ headline }) { const { localTime: lt } = useSprite(); const wipe = Easing.easeOutCubic(clamp(lt / 0.5, 0, 1)); const textIn = Easing.easeOutCubic(clamp((lt - 0.3) / 0.45, 0, 1)); const logoIn = Easing.easeOutCubic(clamp((lt - 0.6) / 0.45, 0, 1)); return (
{/* Chef ruhig rechts — ganze Figur */}
{headline}
); } /* ============================================================ */ function HandwerkerNIS2Spot({ headline }) { const ref = R.useRef(null); const HL = headline || "Wissen, ob NIS2 Sie betrifft."; return (
); } function TimestampLabel({ rootRef }) { const t = useTime(); const sec = Math.floor(t); R.useEffect(() => { const el = rootRef.current && rootRef.current.querySelector("[data-om-exportable-video-with-duration-secs]"); (el || rootRef.current) && (el || rootRef.current).setAttribute("data-screen-label", "t=" + sec + "s"); }, [sec]); return null; } window.HandwerkerNIS2Spot = HandwerkerNIS2Spot;