/* 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 (
);
}
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 (
);
}
/* ============================================================
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 (
{/* 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 (
);
}
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 */}
{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 (
);
}
/* ---------- benommener Kopf mit kreisenden Sternen ---------- */
function DizzyBadge({ t, pop }) {
const s = Easing.easeOutBack(clamp(pop, 0, 1));
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 && (
)}
{/* 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 (
{/* Anruf-Inset: großes Handy-Display mit Kontakt „Neffe" */}
{/* Antwort des Neffen — erst Nachdenken, dann Absage */}
{lt < 0.85 ? (
) : (
)}
{/* Tuut-Tuut Wellen am Telefon */}
{talking && (
)}
);
}
/* ============================================================
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 (
);
}
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 */}
{/* 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
);
}
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 (
);
}
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 (
);
}
/* ============================================================ */
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;