/* EIPME IT-Spot "Läuft einfach." — 1200×1200, ~14.4s, comic style. Gleiche Figur/Werkstatt wie KI- und Cyber-Spot. Reads Stage/Sprite/useTime/useSprite/Easing/interpolate/clamp from window (animations.jsx). */ const { Stage, Sprite, useTime, useSprite, Easing, interpolate, clamp } = window; const R = React; /* ---------- palette (EIPME tokens, literal values) ---------- */ 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 — shared comic head (identisch zu spot-ki.jsx). ============================================================ */ function Kopf({ expression = "grin", blink = 0, talkPhase = 0 }) { const shocked = expression === "shock"; let eyes; if (expression === "tired") { eyes = ( ); } else if (expression === "relaxed") { eyes = ( ); } else if (expression === "wink") { eyes = ( ); } else if (shocked) { eyes = ( ); } else { const closed = blink > 0.5; eyes = closed ? ( ) : ( ); } let mouth; if (expression === "tired") { mouth = ; } else 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 = expression === "tired" ? ( ) : shocked ? ( ) : ( ); return ( {!shocked && } {!shocked && } {brows} {eyes} {mouth} E ); } /* ---------- Sprechblase ---------- */ function SpeechBubble({ x, y, w, pop, text, tailX = 64, fontSize = 54, align = "left" }) { 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 ( ); } /* ---------- Cartoon-Stern (Benommenheit) ---------- */ function DizzyStar({ r = 12, color = C.orange }) { return ; } /* ============================================================ RAUM — Werkstatt-Kulisse für Szene 1 (Drucker statt Tisch). ============================================================ */ function Wanduhr({ clockH, clockM, x = 470, y = 204 }) { return ( ); } function Pegboard() { return ( ); } function NachtFenster() { return ( ); } /* ---------- Drucker ---------- */ function Drucker({ t, offline = true, shake = 0, printedP = 0 }) { const blink = Math.sin(t * 6) > 0 ? 1 : 0.25; const rot = shake * Math.sin(t * 42) * 0.9; return ( {/* eingeklemmtes Blatt oben */} {/* Korpus */} {/* Display */} {offline ? "OFFLINE" : "BEREIT"} {/* Knöpfe */} {/* Status-LED */} {/* Ausgabefach */} {printedP > 0 && ( )} ); } /* ---------- Stehende Figur, hämmert auf Drucker-Knöpfe ---------- */ function DruckerHandwerker({ expression, blink, jab, t }) { const bob = Math.sin(t * 2) * 3; const jabX = 14 * jab; return ( {/* linke Hand in die Hüfte, rechter Arm hämmert */} ); } /* ============================================================ SZENE 1 — Drucker offline, 18:30, "?"-Blase. 0 … 3.4 ============================================================ */ function SceneDrucker() { const { localTime: lt } = useSprite(); const sc = interpolate([0, 1.3, 3.35], [1.03, 1.1, 1.13], Easing.easeInOutCubic)(lt); const cy = interpolate([0, 1.3, 3.35], [4, 10, 12], Easing.easeInOutCubic)(lt); const jabRaw = Math.max(0, Math.sin(lt * 8.5)); const jab = lt > 0.25 ? Easing.easeOutCubic(jabRaw) : 0; const expression = lt < 0.9 ? "shock" : lt < 2.4 ? "shock" : "tired"; const blink = lt > 2.7 && lt < 2.88 ? 1 : 0; const bubblePop = clamp((lt - 0.7) / 0.45, 0, 1) * (lt < 3.0 ? 1 : clamp((3.3 - lt) / 0.3, 0, 1)); return (
{/* 18:30 */} {/* Sideboard + Drucker */} 0.7 ? 1 : 0.2} /> {/* Ärger-Strahlen am Drucker */} 0 ? 1 : 0)}> {/* Figur vor dem Drucker */}
{/* ?-Blase */}
); } /* ============================================================ SZENE 2 — Chaos-Dreierschnitt: WLAN / Update / Passwort-Zettel. 3.4 … 7.0 (je 1.2s) ============================================================ */ function PanelWlan({ p, t }) { const flick1 = Math.sin(t * 9) > -0.2; const flick2 = Math.sin(t * 7 + 1.7) > 0.55; return ( {/* Lager-Regale */} {/* Router auf dem Regal */} {/* WLAN-Bögen: 1 Balken fest, 2 flackern */} {/* kleines Ausrufe-Dreieck */} ); } function PanelUpdate({ p, t }) { const popS = Easing.easeOutBack(clamp((p - 0.18) / 0.2, 0, 1)); const prog = 0.07 + 0.05 * Easing.easeInOutCubic(clamp((p - 0.3) / 0.7, 0, 1)); return (
{/* Monitor */}
{/* Desktop-Reste */}
{/* Update-Fenster */}
Update 1 von 3
Bitte warten…
{Math.round(prog * 100)} % · Nicht ausschalten
); } function PanelZettel({ p, t }) { const fly = Easing.easeInOutCubic(clamp((p - 0.22) / 0.6, 0, 1)); const zx = 430 + 560 * fly; const zy = 480 - 240 * Math.sin(fly * Math.PI) + 60 * fly; const zr = -6 + 40 * fly + 9 * Math.sin(t * 10); const grabP = Easing.easeOutBack(clamp((p - 0.42) / 0.25, 0, 1)); return ( {/* Korkwand */} {/* andere Zettel bleiben hängen */} {/* leere Nadel, wo der Passwort-Zettel war */} {/* davonflatternder Zettel */} PA$$W0RT {/* Windlinien */} {/* greifende Hand von links unten — verfehlt */} ); } /* ---------- benommener Kopf mit kreisenden Sternen ---------- */ function DizzyBadge({ t, pop }) { const s = Easing.easeOutBack(clamp(pop, 0, 1)); return (
{/* kreisende Sterne */} {[0, 1, 2].map((i) => { const a = t * 3.2 + (i * Math.PI * 2) / 3; return ( ); })}
); } function SceneChaos({ showCaptions }) { const { localTime: lt } = useSprite(); const SEG = 4 / 3; const seg = Math.min(2, Math.floor(lt / SEG)); const segT = lt - seg * SEG; const p = clamp(segT / SEG, 0, 1); const enter = Easing.easeOutCubic(clamp(segT / 0.3, 0, 1)); const inP = clamp(lt / 0.35, 0, 1); const bgs = ["#E7EDFB", C.blue50, "#F6EEE2"]; const captions = ["WLAN · ein Balken", "Update · ausgerechnet jetzt", "Passwort · unauffindbar"]; const capPop = Easing.easeOutBack(clamp((segT - 0.12) / 0.3, 0, 1)); return (
{seg === 0 && } {seg === 1 && } {seg === 2 && }
{seg + 1} / 3
{showCaptions && (
{captions[seg]}
)}
{/* benommener Handwerker mit Cartoon-Sternen */}
); } /* ============================================================ SZENE 3 — Telefon + Anruf-Inset (Neffe), Stapel wächst, Uhr springt. 7.0 … 9.85 ============================================================ */ function Stapel({ x, w, h, p, t, seed }) { const n = Math.max(2, Math.round(h / 12)); const sheets = []; for (let i = 0; i < n; i++) { sheets.push( ); } const wob = Math.sin(t * 2 + seed) * 0.9 * Math.min(p, 1.2); return ( {sheets} ); } function SceneTelefon() { const { localTime: lt } = useSprite(); const sc = interpolate([0, 2.85], [1.05, 1.12], Easing.easeInOutCubic)(lt); const phoneUpP = clamp(lt / 0.4, 0, 1); const talking = (lt > 0.5 && lt < 1.3) || (lt > 1.8 && lt < 2.3); const expression = talking ? "talk" : lt < 1.6 ? "shock" : "tired"; const blink = lt > 1.62 && lt < 1.78 ? 1 : 0; const bob = Math.sin(lt * 1.9) * 3; const cardP = clamp((lt - 0.25) / 0.35, 0, 1); // Stapel wächst in Schüben, Uhr springt weiter const growP = Easing.easeInOutCubic(clamp((lt - 0.7) / 1.4, 0, 1)); const stackP = 1 + 0.35 * growP; const jumps = Math.min(3, Math.floor(Math.max(0, lt - 0.5) / 0.7)); const clockM = 180 + jumps * 96; const clockH = 195 + jumps * 8; return (
{/* Stuhllehne */} {/* Figur (Torso + Kopf) */} {/* Tisch */} {/* wachsende Papierstapel */} {/* Arme: links auf Tisch, rechts Telefon am Ohr */} 0.05 ? 1 : 0}> {/* Anruf-Inset: großes Handy-Display mit Kontakt „Neffe" */}
Anruf · 1:47
N
Neffe
{/* Antwort des Neffen — erst Nachdenken, dann Absage */} {lt < 0.85 ? ( ) : ( )}
{/* Tuut-Tuut Wellen am Telefon */} {talking && ( {[0, 1].map((i) => ( ))} )}
); } /* ============================================================ SZENE 4 — EIPME verbindet alles. Hell, ruhig, grün. 9.8 … 12.2 ============================================================ */ function TechBadge({ pop, t }) { const s = Easing.easeOutBack(clamp(pop, 0, 1)); if (s <= 0) return null; return (
{/* freundlicher Techniker: Helm + Gesicht */} E {/* Schraubenschlüssel-Orbit */}
); } function NetzIcon({ x, y, pop, litP, t, children }) { const s = Easing.easeOutBack(clamp(pop, 0, 1)); const lit = clamp(litP, 0, 1); return (
0.5 ? "#E7F3EA" : C.white, border: `8px solid ${lit > 0.5 ? C.green : INK}`, boxShadow: lit > 0.5 ? "0 0 0 10px rgba(14,107,39,0.14), 0 14px 0 rgba(12,14,19,0.10)" : "0 14px 0 rgba(12,14,19,0.10)", display: "flex", alignItems: "center", justifyContent: "center", transition: "none", transform: `scale(${1 + 0.08 * Math.sin(Math.min(1, lit) * Math.PI)})` }}> {children}
{/* grüner Mini-Haken */}
); } function Leitung({ d, p, len }) { const lp = Easing.easeInOutCubic(clamp(p, 0, 1)); return ( ); } function SceneVerbinden() { const { localTime: lt } = useSprite(); const badgePop = clamp((lt - 0.2) / 0.5, 0, 1); const pops = [clamp((lt - 0.1) / 0.35, 0, 1), clamp((lt - 0.25) / 0.35, 0, 1), clamp((lt - 0.4) / 0.35, 0, 1)]; const lines = [clamp((lt - 0.6) / 0.6, 0, 1), clamp((lt - 1.2) / 0.6, 0, 1), clamp((lt - 1.8) / 0.6, 0, 1)]; const lits = [clamp((lt - 1.1) / 0.3, 0, 1), clamp((lt - 1.7) / 0.3, 0, 1), clamp((lt - 2.3) / 0.3, 0, 1)]; const shiftP = Easing.easeInOutCubic(clamp((lt - 2.5) / 0.45, 0, 1)); const pp = clamp((lt - 2.75) / 0.45, 0, 1); const signScale = Easing.easeOutBack(pp); const signRot = 6 - 8 * Easing.easeOutCubic(pp); return (
{/* Netz-Struktur — schiebt hoch, wenn das Schild kommt */}
{/* Leitungen */} {/* Impuls-Punkte auf fertigen Leitungen */} {lits[0] > 0.9 && } {lits[2] > 0.9 && } {/* Geräte-Icons */} {/* EIPME-Techniker mittig unten */}
{/* Großes Orange-Schild nach dem grünen Häkchen-Trio */}
Jetzt EIPME PC-, Arbeitsplatz- & Mail-Support anfordern
{/* Funkeln ums Schild */}
); } /* ============================================================ SZENE 5 — Angebot in der Hand, "Läuft einfach." + Logo. 12.2 … 14.4 ============================================================ */ function LogoLockup({ size = 64 }) { return (
EIPME
EIPME
); } function StandMitAngebot({ blink = 0, bob = 0, liftP = 0, t = 0 }) { const lift = Easing.easeOutBack(clamp(liftP, 0, 1)); const armA = -20 * lift + 2.5 * Math.sin(t * 2.2); return ( {/* linker Arm entspannt, rechter hält Angebot hoch */} {/* frisch gedrucktes Angebot */} ANGEBOT ); } function SceneOutro({ headline }) { const { localTime: lt } = useSprite(); const wipe = Easing.easeOutCubic(clamp(lt / 0.5, 0, 1)); const walkP = Easing.easeOutCubic(clamp(lt / 0.5, 0, 1)); const x = -430 + 430 * walkP; const bob = Math.sin(lt * 13) * 7 * (1 - walkP); const liftP = clamp((lt - 0.35) / 0.35, 0, 1); const textIn = Easing.easeOutCubic(clamp((lt - 0.3) / 0.4, 0, 1)); const logoIn = Easing.easeOutCubic(clamp((lt - 0.55) / 0.4, 0, 1)); const blink = 0; const breathe = 1; return (
{headline}
); } /* ============================================================ */ function HandwerkerITSpot({ headline, showCaptions }) { const ref = R.useRef(null); const HL = headline || "Läuft einfach."; const caps = showCaptions !== false; 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.HandwerkerITSpot = HandwerkerITSpot;