/* modul-video-scenes.jsx — databites Modul-Video (insights · match · flow · daily) Reads timeline globals from window (set by animations.jsx). Exposes window.ModulVideo. */ (function () { const R = React; // ---- module accents -------------------------------------------------- const ACC = { insights: '#c3e48b', match: '#f87666', flow: '#ddb771', daily: '#5899e2' }; // ---- look presets (the "options") ------------------------------------ const LOOKS = { studio: { label: 'Studio', bg: '#223539', fg: '#f4efe6', muted: 'rgba(244,239,230,0.60)', frame: true, chrome: '#e7eeec', chromeDot: '#b8c6c4', glow: 0.24, logo: 'light', shotBg: '#e7eeec' }, kino: { label: 'Kino', bg: '#0b1315', fg: '#f4efe6', muted: 'rgba(244,239,230,0.58)', frame: false, chrome: '#0f1a1d', chromeDot: '#2a3b3f', glow: 0.34, logo: 'light', shotBg: '#dbe3e1' }, hell: { label: 'Hell', bg: '#f4efe6', fg: '#223539', muted: '#5b6b78', frame: true, chrome: '#ffffff', chromeDot: '#cdd6dd', glow: 0.16, logo: 'dark', shotBg: '#eef2f5' }, }; // ---- timeline (seconds) --------------------------------------------- const DUR = 240; const MODS = [ { key: 'insights', name: 'insights', acc: ACC.insights, seg: [12, 64], kicker: 'Klare Zahlen', claim: 'Alle Zahlen.
Ein Blick.', does: 'insights führt die Kennzahlen aller Filialen an einem Ort zusammen. In Echtzeit, ohne Suchen.', heute: 'Zahlen suchen', fix: 'Zahlen verstehen', payoff: 'Damit du entscheidest,
statt zu suchen.', }, { key: 'match', name: 'match', acc: ACC.match, seg: [64, 116], kicker: 'Starke Teams', claim: 'Menschen finden.
Menschen halten.', does: 'match begleitet den ganzen Weg. Vom ersten Bedarf über die Einarbeitung bis in die Personalakte.', heute: 'Aktenchaos', fix: 'Ein roter Faden', payoff: 'Damit dein Team
zusammenbleibt.', }, { key: 'flow', name: 'flow', acc: ACC.flow, seg: [116, 168], kicker: 'Runder Warenfluss', claim: 'Vom Rezept
zur Liefertreue.', does: 'flow bringt Sortiment, Produktion und Warenfluss in einen Takt. Sauber geplant, Tag für Tag.', heute: 'Ware fehlt', fix: 'Alles greift ineinander', payoff: 'Damit ankommt,
was gebacken wird.', }, { key: 'daily', name: 'daily', acc: ACC.daily, seg: [168, 220], kicker: 'Ruhiger Filialtag', claim: 'Der ganze Tag.
An einem Ort.', does: 'daily bündelt den Filialalltag. Aufgaben, Kommunikation und Tagesabschluss, klar an einer Stelle.', heute: 'Zettel und Chaos', fix: 'Ein klarer Rahmen', payoff: 'Damit abends
alle Bescheid wissen.', }, ]; const INTRO = [0, 12]; const OUTRO = [220, 240]; // ---- helpers --------------------------------------------------------- function ip(kf, vals, ease) { return window.interpolate(kf, vals, ease); } const easeOut = () => window.Easing.easeOutCubic; const easeBack = () => window.Easing.easeOutBack; function capLabel(c) { return { fontFamily: "'JetBrains Mono', monospace", fontSize: 18, fontWeight: 700, letterSpacing: '0.18em', textTransform: 'uppercase', color: c, }; } function Html({ html, style, className }) { return R.createElement('div', { className, style, dangerouslySetInnerHTML: { __html: html } }); } function Glow({ acc, alpha }) { return R.createElement('div', { style: { position: 'absolute', inset: 0, zIndex: 0, pointerEvents: 'none', background: 'radial-gradient(ellipse 62% 46% at 100% -6%, ' + hexA(acc, alpha) + ', transparent 60%)' + ', radial-gradient(ellipse 50% 40% at -8% 108%, ' + hexA(acc, alpha * 0.6) + ', transparent 62%)', }, }); } function BrandDots({ acc, size, gap }) { const d = { width: size, height: size, borderRadius: '50%', background: acc, display: 'block' }; const cell = { display: 'grid', gridTemplateColumns: size + 'px ' + size + 'px', gap: gap + 'px' }; return R.createElement('div', { style: cell }, R.createElement('span', { style: d }), R.createElement('span', { style: d }), R.createElement('span', { style: d }), R.createElement('span', { style: d })); } function hexA(hex, a) { const h = hex.replace('#', ''); const r = parseInt(h.slice(0, 2), 16), g = parseInt(h.slice(2, 4), 16), b = parseInt(h.slice(4, 6), 16); return 'rgba(' + r + ',' + g + ',' + b + ',' + a + ')'; } // ---- module pill ----------------------------------------------------- function Pill({ mod, L, o, dx }) { return R.createElement('div', { style: { display: 'inline-flex', alignItems: 'center', gap: 15, padding: '13px 28px 13px 22px', borderRadius: 999, fontSize: 30, fontWeight: 700, letterSpacing: '0.01em', color: L.logo === 'dark' ? '#223539' : mod.acc, background: hexA(mod.acc, L.logo === 'dark' ? 0.18 : 0.16), border: '1px solid ' + hexA(mod.acc, 0.5), opacity: o, transform: 'translateX(' + dx + 'px)', }, }, R.createElement('span', { style: { width: 16, height: 16, borderRadius: 999, background: mod.acc } }), mod.name); } // ---- native app mock (no screenshots) ------------------------------- const ICONS = { euro: 'M15 8a4 4 0 1 0 0 8M6 10h6M6 14h6', users: 'M8 11a3 3 0 1 0 0-6 3 3 0 0 0 0 6zM3 19a5 5 0 0 1 10 0M16 6a3 3 0 0 1 0 6M18 19a5 5 0 0 0-3-4.6', cart: 'M4 5h2l2 10h9l2-7H7M9 19a1 1 0 1 0 .01 0M17 19a1 1 0 1 0 .01 0', pulse: 'M3 12h4l2-6 4 12 2-6h6', briefcase: 'M4 8h16v11H4zM9 8V6a2 2 0 0 1 2-2h2a2 2 0 0 1 2 2v2', clock: 'M12 12l3 2M12 4a8 8 0 1 0 0 16 8 8 0 0 0 0-16z', check: 'M4 12l5 5L20 6', truck: 'M3 7h11v9H3zM14 10h4l3 3v3h-7M7 19a2 2 0 1 0 .01 0M17 19a2 2 0 1 0 .01 0', box: 'M12 3l8 4-8 4-8-4 8-4zM4 7v9l8 4 8-4V7', gauge: 'M5 18a8 8 0 1 1 14 0M12 14l4-4', list: 'M8 6h12M8 12h12M8 18h12M4 6h.01M4 12h.01M4 18h.01', doc: 'M6 3h8l4 4v14H6zM14 3v4h4', feed: 'M5 5a14 14 0 0 1 14 14M5 12a7 7 0 0 1 7 7M6 18a1 1 0 1 0 .01 0', }; function Icon({ name, color }) { return R.createElement('svg', { width: 22, height: 22, viewBox: '0 0 24 24', fill: 'none', stroke: color, strokeWidth: 2, strokeLinecap: 'round', strokeLinejoin: 'round' }, R.createElement('path', { d: ICONS[name] || ICONS.list })); } function sparkPath(vals, w, h) { const n = vals.length, pad = 3; const step = (w - pad * 2) / (n - 1); return vals.map((v, i) => { const x = pad + i * step, y = pad + (1 - v) * (h - pad * 2); return (i ? 'L' : 'M') + x.toFixed(1) + ' ' + y.toFixed(1); }).join(' '); } function fmtVal(v, tile) { if (tile.fmt === 'eur') return v.toLocaleString('de-DE', { minimumFractionDigits: 2, maximumFractionDigits: 2 }) + ' €'; if (tile.fmt === 'eurh') return v.toLocaleString('de-DE', { minimumFractionDigits: 2, maximumFractionDigits: 2 }) + ' €/h'; if (tile.fmt === 'pct') return v.toFixed(2).replace('.', ',') + ' %'; if (tile.fmt === 'dec') return v.toFixed(2).replace('.', ','); if (tile.unit) return Math.round(v).toLocaleString('de-DE') + ' ' + tile.unit; return Math.round(v).toLocaleString('de-DE'); } const MI = { bg: '#eef2f1', card: '#ffffff', fg: '#223539', sub: '#8a98a0', line: '#e6ecea', chipUpBg: '#e7f2d6', chipUpFg: '#5f7d33', chipDnBg: '#fbe2dd', chipDnFg: '#c0432f', iconBg: '#f1f5f3' }; function ScoreTile({ tile, acc, t, start }) { const cp = ip([start, start + 1.3], [0, 1], easeOut())(t); const val = tile.value * cp; const chipO = ip([start + 0.8, start + 1.5], [0, 1])(t); const draw = ip([start + 0.4, start + 1.6], [0, 1], easeOut())(t); const up = tile.up; return R.createElement('div', { style: { background: MI.card, border: '1px solid ' + MI.line, borderRadius: 14, padding: '16px 18px', position: 'relative', overflow: 'hidden' }, }, R.createElement('div', { style: { display: 'flex', alignItems: 'center', gap: 10, marginBottom: 10 } }, R.createElement('div', { style: { width: 34, height: 34, borderRadius: 9, background: MI.iconBg, display: 'flex', alignItems: 'center', justifyContent: 'center' } }, R.createElement(Icon, { name: tile.icon, color: '#5f6f74' })), R.createElement('div', { style: { fontSize: 17, fontWeight: 700, color: MI.fg } }, tile.label)), R.createElement('div', { style: { fontSize: 34, fontWeight: 800, color: MI.fg, letterSpacing: '-0.02em', fontVariantNumeric: 'tabular-nums' } }, fmtVal(val, tile)), tile.sub ? R.createElement('div', { style: { fontSize: 15, color: MI.sub, marginTop: 6, opacity: chipO } }, tile.sub) : null, R.createElement('div', { style: { marginTop: 10, opacity: chipO } }, R.createElement('span', { style: { fontSize: 13, fontWeight: 700, padding: '4px 10px', borderRadius: 999, background: up ? MI.chipUpBg : MI.chipDnBg, color: up ? MI.chipUpFg : MI.chipDnFg } }, tile.chip)), R.createElement('svg', { width: 96, height: 40, viewBox: '0 0 96 40', style: { position: 'absolute', right: 16, bottom: 16, overflow: 'visible' } }, R.createElement('path', { d: sparkPath(tile.spark, 96, 40), fill: 'none', stroke: acc, strokeWidth: 2.4, strokeLinecap: 'round', strokeLinejoin: 'round', pathLength: 1, strokeDasharray: 1, strokeDashoffset: 1 - draw }))); } // ---- score ring ------------------------------------------------------ function scoreColor(sc) { return sc >= 7 ? '#4a9d5b' : sc >= 4 ? '#e0a63a' : '#d9564a'; } const BAR = { g: '#7cc67c', y: '#e8b84b', r: '#e5654e' }; function ScoreRing({ score, size, draw }) { const r = size / 2 - 3, C = 2 * Math.PI * r, col = scoreColor(score); return R.createElement('svg', { width: size, height: size, viewBox: '0 0 ' + size + ' ' + size }, R.createElement('circle', { cx: size / 2, cy: size / 2, r, fill: 'none', stroke: '#e9efec', strokeWidth: 3 }), R.createElement('circle', { cx: size / 2, cy: size / 2, r, fill: 'none', stroke: col, strokeWidth: 3, strokeLinecap: 'round', strokeDasharray: C, strokeDashoffset: C * (1 - (score / 10) * draw), transform: 'rotate(-90 ' + size / 2 + ' ' + size / 2 + ')' }), R.createElement('text', { x: '50%', y: '50%', dy: '0.35em', textAnchor: 'middle', fontSize: size * 0.42, fontWeight: 800, fill: '#3a4a50', fontFamily: "'Montserrat', sans-serif" }, score)); } function ScoreView({ mod, t }) { const s = mod.seg[0]; const m = mod.mock; return R.createElement('div', { style: { flex: 1, display: 'grid', gridTemplateColumns: '1fr 1fr 1fr', gap: 16 } }, m.cols.map((col, ci) => R.createElement('div', { key: ci, style: { display: 'flex', flexDirection: 'column', gap: 10 } }, // column header (function () { const hs = s + 2.4 + ci * 0.3; const o = ip([hs, hs + 0.7], [0, 1], easeOut())(t); const draw = ip([hs + 0.2, hs + 1.1], [0, 1], easeOut())(t); return R.createElement('div', { key: 'h', style: { display: 'flex', alignItems: 'center', gap: 8, paddingBottom: 8, borderBottom: '1px solid ' + MI.line, opacity: o } }, R.createElement(Icon, { name: col.icon, color: '#5f6f74' }), R.createElement('div', { style: { fontSize: 16, fontWeight: 800, color: MI.fg, flex: 1 } }, col.name), R.createElement(ScoreRing, { score: col.score, size: 30, draw })); })(), // cards col.cards.map((card, ki) => { const cs = s + 3.0 + (ki * 3 + ci) * 0.16; const o = ip([cs, cs + 0.6], [0, 1], easeOut())(t); const y = ip([cs, cs + 0.6], [12, 0], easeOut())(t); const draw = ip([cs + 0.2, cs + 1.0], [0, 1], easeOut())(t); const bw = ip([cs + 0.3, cs + 0.9], [0, 1], easeOut())(t); return R.createElement('div', { key: ki, style: { background: MI.card, border: '1px solid ' + MI.line, borderRadius: 12, padding: '12px 14px', opacity: o, transform: 'translateY(' + y + 'px)' } }, R.createElement('div', { style: { display: 'flex', alignItems: 'center', gap: 8, marginBottom: 10 } }, R.createElement(Icon, { name: card.icon, color: '#839196' }), R.createElement('div', { style: { fontSize: 13, fontWeight: 800, letterSpacing: '0.03em', textTransform: 'uppercase', color: '#4a5a60', flex: 1 } }, card.label), R.createElement(ScoreRing, { score: card.score, size: 26, draw })), R.createElement('div', { style: { display: 'flex', gap: 6 } }, card.bars.map((b, bi) => R.createElement('div', { key: bi, style: { flex: 1, height: 8, borderRadius: 4, background: BAR[b], transformOrigin: 'left', transform: 'scaleX(' + bw + ')' } })))); })))); } // ---- pipeline view --------------------------------------------------- function PipelineView({ mod, t }) { const s = mod.seg[0]; const m = mod.mock; const trackW = ip([s + 3.2, s + 4.6], [0, 1], easeOut())(t); return R.createElement('div', { style: { flex: 1, display: 'flex', flexDirection: 'column' } }, // stat strip R.createElement('div', { style: { display: 'flex', gap: 30, marginBottom: 8 } }, m.stats.map((st, i) => { const o = ip([s + 2.4 + i * 0.25, s + 3.0 + i * 0.25], [0, 1], easeOut())(t); return R.createElement('div', { key: i, style: { display: 'flex', alignItems: 'baseline', gap: 8, opacity: o } }, R.createElement('span', { style: { fontSize: 30, fontWeight: 800, color: st.c, fontVariantNumeric: 'tabular-nums' } }, st.n), R.createElement('span', { style: { fontSize: 15, fontWeight: 600, color: MI.sub } }, st.label)); })), // pipeline R.createElement('div', { style: { flex: 1, position: 'relative', display: 'flex', alignItems: 'center' } }, R.createElement('div', { style: { position: 'absolute', left: '7%', right: '7%', top: '50%', height: 3, background: 'linear-gradient(90deg,#5899e2,#e0a63a,#3fae9a,#8b7bd8,#6fae5a,#6d78d6,#e08a3a)', borderRadius: 2, transformOrigin: 'left', transform: 'translateY(-14px) scaleX(' + trackW + ')' } }), R.createElement('div', { style: { position: 'relative', flex: 1, display: 'flex', justifyContent: 'space-between' } }, m.stages.map((st, i) => { const ns = s + 3.0 + i * 0.32; const o = ip([ns, ns + 0.5], [0, 1], easeOut())(t); const sc = ip([ns, ns + 0.5], [0.4, 1], easeBack())(t); const cnt = ip([ns + 0.2, ns + 1.0], [0, 1], easeOut())(t); return R.createElement('div', { key: i, style: { width: 96, display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 8, opacity: o } }, R.createElement('div', { style: { width: 52, height: 52, borderRadius: 999, background: MI.card, border: '3px solid ' + st.c, display: 'flex', alignItems: 'center', justifyContent: 'center', transform: 'scale(' + sc + ')', boxShadow: '0 4px 12px ' + hexA(st.c, 0.25), fontSize: 22, fontWeight: 800, color: st.c, fontVariantNumeric: 'tabular-nums' } }, Math.round(st.n * cnt)), R.createElement('div', { style: { fontSize: 12, fontWeight: 700, color: '#4a5a60', textAlign: 'center', lineHeight: 1.2, height: 30 } }, st.name)); })))); } // ---- feed view ------------------------------------------------------- function FeedView({ mod, t }) { const s = mod.seg[0]; const m = mod.mock; const p = m.post; const imgO = ip([s + 3.0, s + 4.0], [0, 1], easeOut())(t); const imgSc = ip([s + 3.0, s + 4.2], [0.94, 1], easeOut())(t); const titleO = ip([s + 3.4, s + 4.2], [0, 1], easeOut())(t); const metaO = ip([s + 3.8, s + 4.6], [0, 1], easeOut())(t); const textO = ip([s + 4.4, s + 5.4], [0, 1], easeOut())(t); return R.createElement('div', { style: { flex: 1, display: 'flex', flexDirection: 'column', minHeight: 0 } }, // category chips R.createElement('div', { style: { display: 'flex', gap: 8, marginBottom: 16, flexWrap: 'wrap' } }, m.chips.map((c, i) => { const o = ip([s + 2.4 + i * 0.12, s + 2.9 + i * 0.12], [0, 1], easeOut())(t); return R.createElement('span', { key: i, style: { fontSize: 13, fontWeight: 700, padding: '6px 14px', borderRadius: 999, opacity: o, color: c.active ? '#fff' : c.c, background: c.active ? c.c : hexA(c.c, 0.10), border: '1px solid ' + (c.active ? c.c : hexA(c.c, 0.35)) } }, c.name); })), // post card R.createElement('div', { style: { flex: 1, display: 'flex', gap: 20, background: MI.card, border: '1px solid ' + MI.line, borderRadius: 14, padding: 18, minHeight: 0 } }, R.createElement('div', { style: { flex: '0 0 320px', borderRadius: 10, overflow: 'hidden', opacity: imgO, transform: 'scale(' + imgSc + ')', background: '#e7ece9' } }, R.createElement('image-slot', { id: 'modvid-daily-feed', placeholder: 'Team-Foto (optional)', shape: 'rounded', radius: '10', style: { width: '100%', height: '100%', display: 'block' } })), R.createElement('div', { style: { flex: 1, display: 'flex', flexDirection: 'column', minWidth: 0 } }, R.createElement('div', { style: { display: 'flex', alignItems: 'flex-start', gap: 12, opacity: titleO } }, R.createElement('div', { style: { fontSize: 24, fontWeight: 800, color: MI.fg, lineHeight: 1.15, flex: 1, letterSpacing: '-0.01em' } }, p.title), R.createElement('span', { style: { flex: '0 0 auto', fontSize: 12, fontWeight: 700, color: '#5899e2', border: '1px solid ' + hexA('#5899e2', 0.4), borderRadius: 999, padding: '4px 12px' } }, p.tag)), R.createElement('div', { style: { fontSize: 14, color: MI.sub, marginTop: 10, opacity: metaO } }, 'Von ' + p.author + ' · ' + p.date), R.createElement('div', { style: { height: 1, background: MI.line, margin: '14px 0', opacity: metaO } }), R.createElement('div', { style: { fontSize: 17, lineHeight: 1.55, color: '#48585e', opacity: textO } }, p.text)))); } function AppMock({ mod, t }) { const s = mod.seg[0]; const m = mod.mock; let body; if (m.type === 'score') body = R.createElement(ScoreView, { mod, t }); else if (m.type === 'pipeline') body = R.createElement(PipelineView, { mod, t }); else if (m.type === 'feed') body = R.createElement(FeedView, { mod, t }); else body = R.createElement('div', { style: { flex: 1, display: 'grid', gridTemplateColumns: '1fr 1fr', gridTemplateRows: m.tiles.length <= 2 ? '1fr' : '1fr 1fr', gap: 16 } }, m.tiles.map((tile, i) => R.createElement(ScoreTile, { key: i, tile, acc: mod.acc, t, start: s + 2.6 + i * 0.5 }))); return R.createElement('div', { style: { position: 'absolute', inset: 0, background: MI.bg, padding: 26, display: 'flex', flexDirection: 'column' } }, R.createElement('div', { style: { fontFamily: "'JetBrains Mono', monospace", fontSize: 13, color: MI.sub, letterSpacing: '0.04em', marginBottom: 6 } }, m.crumb), R.createElement('div', { style: { display: 'flex', alignItems: 'center', gap: 14, marginBottom: 18 } }, R.createElement('div', { style: { fontSize: 26, fontWeight: 800, color: MI.fg, letterSpacing: '-0.02em' } }, m.title), R.createElement('span', { style: { fontSize: 13, fontWeight: 700, color: mod.acc, background: hexA(mod.acc, 0.16), padding: '5px 13px', borderRadius: 999 } }, m.pill)), body); } // ---- browser frame -------------------------------------------------- function Shot({ mod, L, kb, o, dx }) { const t = window.useTime(); const shotBox = R.createElement('div', { style: { position: 'relative', width: '100%', height: L.frame ? 'calc(100% - 52px)' : '100%', overflow: 'hidden', background: MI.bg, borderRadius: L.frame ? 0 : 18, }, }, R.createElement(AppMock, { mod, t })); const chrome = L.frame ? R.createElement('div', { style: { display: 'flex', alignItems: 'center', gap: 11, height: 52, padding: '0 22px', background: L.chrome }, }, R.createElement('span', { style: dotS(L) }), R.createElement('span', { style: dotS(L) }), R.createElement('span', { style: dotS(L) }), R.createElement('span', { style: { marginLeft: 18, fontFamily: "'JetBrains Mono', monospace", fontSize: 15, color: L.logo === 'dark' ? '#7f8b92' : '#8ea0a6', letterSpacing: '0.02em', }, }, 'app.databites.de / ' + mod.name)) : null; return R.createElement('div', { style: { position: 'absolute', left: 992, top: 244, width: 812, height: 592, borderRadius: 18, overflow: 'hidden', background: L.frame ? L.chrome : MI.bg, boxShadow: L.logo === 'dark' ? '0 40px 90px rgba(27,40,51,0.20)' : '0 44px 100px rgba(0,0,0,0.5)', border: L.frame ? '1px solid ' + hexA('#ffffff', L.logo === 'dark' ? 0 : 0.08) : 'none', opacity: o, transform: 'translateX(' + dx + 'px)', }, }, chrome, shotBox); } function dotS(L) { return { width: 15, height: 15, borderRadius: 999, background: L.chromeDot, display: 'block' }; } // ---- feature line ---------------------------------------------------- function Feat({ text, acc, L, o, dy, active }) { return R.createElement('div', { style: { display: 'flex', alignItems: 'flex-start', gap: 18, marginBottom: 20, opacity: o, transform: 'translateY(' + dy + 'px)', transition: 'none', }, }, R.createElement('span', { style: { marginTop: 10, flex: '0 0 auto', width: 17, height: 17, borderRadius: 999, background: active ? acc : 'transparent', border: '2px solid ' + acc, boxShadow: active ? '0 0 0 6px ' + hexA(acc, 0.18) : 'none', }, }), R.createElement('span', { style: { fontSize: 30, lineHeight: 1.3, fontWeight: 600, color: active ? L.fg : L.muted, maxWidth: 660, }, }, text)); } // ---- scene wrapper (full canvas + crossfade) ------------------------- function Scene({ seg, children }) { const t = window.useTime(); if (t < seg[0] - 0.05 || t > seg[1] + 0.05) return null; const fade = 0.7; const o = ip([seg[0], seg[0] + fade, seg[1] - fade, seg[1]], [0, 1, 1, 0])(t); return R.createElement('div', { style: { position: 'absolute', inset: 0, width: 1920, height: 1080, opacity: o }, }, children); } // ---- INTRO ----------------------------------------------------------- function Intro({ seg, L }) { const t = window.useTime(); const s = seg[0]; const dotsO = ip([s + 0.2, s + 1.1], [0, 1], easeOut())(t); const dotsSc = ip([s + 0.2, s + 1.1], [0.6, 1], easeBack())(t); const logoO = ip([s + 1.1, s + 2.1], [0, 1], easeOut())(t); const logoY = ip([s + 1.1, s + 2.1], [28, 0], easeOut())(t); const subO = ip([s + 2.6, s + 3.6], [0, 1], easeOut())(t); const rowO = ip([s + 4.4, s + 5.2], [0, 1], easeOut())(t); const logoSrc = (window.DB_LOGO ? (L.logo === 'dark' ? window.DB_LOGO.dark : window.DB_LOGO.light) : (L.logo === 'dark' ? 'assets/logo-dark.png' : 'assets/logo-light.png')); const chips = MODS.map((m, i) => { const co = ip([s + 4.6 + i * 0.45, s + 5.3 + i * 0.45], [0, 1], easeOut())(t); const cy = ip([s + 4.6 + i * 0.45, s + 5.3 + i * 0.45], [16, 0], easeOut())(t); return R.createElement('div', { key: m.key, style: { display: 'inline-flex', alignItems: 'center', gap: 13, padding: '12px 26px', borderRadius: 999, border: '1px solid ' + hexA(m.acc, 0.5), background: hexA(m.acc, 0.1), fontSize: 30, fontWeight: 700, color: L.logo === 'dark' ? '#223539' : L.fg, opacity: co, transform: 'translateY(' + cy + 'px)', }, }, R.createElement('span', { style: { width: 15, height: 15, borderRadius: 999, background: m.acc } }), m.name); }); return R.createElement(Scene, { seg }, R.createElement(Glow, { acc: ACC.insights, alpha: L.glow * 0.7 }), R.createElement('div', { style: { position: 'absolute', inset: 0, display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', gap: 34, zIndex: 1, }, }, R.createElement('img', { src: logoSrc, alt: 'databites', style: { height: 92, opacity: logoO, transform: 'translateY(' + logoY + 'px)' }, }), R.createElement('div', { style: { fontSize: 40, fontWeight: 500, color: L.muted, opacity: subO, textAlign: 'center' }, }, 'Vier Module. Eine Plattform für das Bäckerhandwerk.'), R.createElement('div', { style: { display: 'flex', gap: 20, marginTop: 14, opacity: rowO }, }, chips))); } // ---- MODULE ---------------------------------------------------------- function Module({ mod, L }) { const t = window.useTime(); const [s, e] = mod.seg; const lt = t - s; // local seconds const dur = e - s; const p = Math.max(0, Math.min(1, lt / dur)); const acc = mod.acc; // three beats: WAS (claim) → WANDEL (heute → fix) → WARUM (payoff) const aO = ip([s + 0.6, s + 1.8], [0, 1], easeOut())(t) * ip([s + 17, s + 18], [1, 0])(t); const claimY = ip([s + 0.6, s + 2.0], [30, 0], easeOut())(t); const eyebrowAO = ip([s + 0.3, s + 1.2], [0, 1], easeOut())(t); const doesO = ip([s + 2.4, s + 3.6], [0, 1], easeOut())(t); const bEnv = ip([s + 18.5, s + 19.7], [0, 1], easeOut())(t) * ip([s + 36, s + 37], [1, 0])(t); const heuteI = ip([s + 18.8, s + 20.0], [0, 1], easeOut())(t); const arrowI = ip([s + 21.4, s + 22.6], [0, 1], easeOut())(t); const fixI = ip([s + 22.2, s + 23.6], [0, 1], easeOut())(t); const cO = ip([s + 37.6, s + 39.0], [0, 1], easeOut())(t); const kickerCO = ip([s + 37.6, s + 38.6], [0, 1], easeOut())(t); const payY = ip([s + 38.2, s + 39.8], [30, 0], easeOut())(t); const drift = -Math.min(Math.max(lt, 0), dur) * 0.16; const layer = (o) => ({ position: 'absolute', inset: 0, display: 'flex', flexDirection: 'column', justifyContent: 'center', paddingLeft: 168, paddingRight: 140, opacity: o, zIndex: 1 }); const eyebrow = (o) => ({ fontFamily: "'JetBrains Mono', monospace", fontSize: 22, fontWeight: 700, letterSpacing: '0.22em', textTransform: 'uppercase', color: acc, opacity: o, marginBottom: 26 }); const miniLabel = (col) => ({ fontFamily: "'JetBrains Mono', monospace", fontSize: 20, fontWeight: 700, letterSpacing: '0.2em', textTransform: 'uppercase', color: col, marginBottom: 16 }); return R.createElement(Scene, { seg: mod.seg }, R.createElement('div', { style: { position: 'absolute', inset: 0, background: L.bg } }), R.createElement(Glow, { acc, alpha: L.glow }), // faint background wordmark R.createElement('div', { style: { position: 'absolute', right: -34, bottom: -78, zIndex: 0, fontFamily: "'Montserrat', sans-serif", fontWeight: 800, fontStyle: 'italic', fontSize: 380, lineHeight: 1, letterSpacing: '-0.04em', color: hexA(acc, 0.06), pointerEvents: 'none' } }, mod.name), // persistent module pill R.createElement('div', { style: { position: 'absolute', left: 168, top: 150, zIndex: 3 } }, R.createElement(Pill, { mod, L, o: ip([s + 0.3, s + 1.6], [0, 1], easeOut())(t), dx: 0 })), // BEAT A — was wir tun R.createElement('div', { style: layer(aO) }, R.createElement('div', { style: eyebrow(eyebrowAO) }, mod.kicker), R.createElement(Html, { html: mod.claim, className: 'mv-head', style: { fontFamily: "'Montserrat', sans-serif", fontWeight: 800, fontSize: 128, lineHeight: 0.96, letterSpacing: '-0.035em', color: L.fg, '--em': acc, transform: 'translateY(' + (claimY + drift) + 'px)' } }), R.createElement('div', { style: { marginTop: 34, maxWidth: 1000, fontSize: 36, lineHeight: 1.45, fontWeight: 500, color: L.muted, opacity: doesO } }, mod.does)), // BEAT B — von heute zu databites R.createElement('div', { style: layer(bEnv) }, R.createElement('div', { style: { display: 'flex', alignItems: 'flex-end', gap: 44, flexWrap: 'wrap', transform: 'translateY(' + drift + 'px)' } }, R.createElement('div', { style: { opacity: heuteI } }, R.createElement('div', { style: miniLabel(L.muted) }, 'heute'), R.createElement(Html, { html: mod.heute, className: 'mv-head', style: { fontFamily: "'Montserrat', sans-serif", fontWeight: 800, fontSize: 76, lineHeight: 1.0, letterSpacing: '-0.03em', color: L.muted, '--em': L.muted } })), R.createElement('div', { style: { fontSize: 76, fontWeight: 300, color: acc, opacity: arrowI, transform: 'translateY(-10px)', lineHeight: 1 } }, '\u2192'), R.createElement('div', { style: { opacity: fixI } }, R.createElement('div', { style: miniLabel(acc) }, 'mit databites'), R.createElement(Html, { html: mod.fix, className: 'mv-head', style: { fontFamily: "'Montserrat', sans-serif", fontWeight: 800, fontSize: 76, lineHeight: 1.0, letterSpacing: '-0.03em', color: L.fg, '--em': acc } })))), // BEAT C — warum wir es tun R.createElement('div', { style: layer(cO) }, R.createElement('div', { style: eyebrow(kickerCO) }, 'darum ' + mod.name), R.createElement(Html, { html: mod.payoff, className: 'mv-head', style: { fontFamily: "'Montserrat', sans-serif", fontWeight: 800, fontSize: 118, lineHeight: 1.0, letterSpacing: '-0.035em', color: L.fg, '--em': acc, transform: 'translateY(' + payY + 'px)' } }))); } // ---- OUTRO ----------------------------------------------------------- function Outro({ seg, L }) { const t = window.useTime(); const s = seg[0]; const logoO = ip([s + 0.4, s + 1.6], [0, 1], easeOut())(t); const logoY = ip([s + 0.4, s + 1.6], [22, 0], easeOut())(t); const rowO = ip([s + 1.6, s + 2.6], [0, 1], easeOut())(t); const ctaO = ip([s + 2.8, s + 3.8], [0, 1], easeOut())(t); const logoSrc = (window.DB_LOGO ? (L.logo === 'dark' ? window.DB_LOGO.dark : window.DB_LOGO.light) : (L.logo === 'dark' ? 'assets/logo-dark.png' : 'assets/logo-light.png')); const chips = MODS.map((m, i) => R.createElement('span', { key: m.key, style: { display: 'inline-flex', alignItems: 'center', gap: 11, fontSize: 30, fontWeight: 700, color: L.fg }, }, R.createElement('span', { style: { width: 14, height: 14, borderRadius: 999, background: m.acc } }), m.name)); const sep = () => R.createElement('span', { style: { color: L.muted, fontSize: 26, margin: '0 4px' } }, '·'); return R.createElement(Scene, { seg }, R.createElement(Glow, { acc: ACC.insights, alpha: L.glow * 0.7 }), R.createElement('div', { style: { position: 'absolute', inset: 0, display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', gap: 40, zIndex: 1, }, }, R.createElement('img', { src: logoSrc, alt: 'databites', style: { height: 96, opacity: logoO, transform: 'translateY(' + logoY + 'px)' } }), R.createElement('div', { style: { display: 'flex', alignItems: 'center', gap: 8, opacity: rowO } }, chips[0], sep(), chips[1], sep(), chips[2], sep(), chips[3]), R.createElement('div', { className: 'mv-cta', contentEditable: false, style: { marginTop: 8, fontSize: 34, fontWeight: 700, letterSpacing: '0.01em', color: L.logo === 'dark' ? '#223539' : ACC.insights, opacity: ctaO, padding: '16px 40px', borderRadius: 999, border: '1px solid ' + hexA(ACC.insights, 0.5), background: hexA(ACC.insights, 0.12), }, }, 'Jetzt kennenlernen'))); } // ---- APP ------------------------------------------------------------- function ModulVideo() { const L = LOOKS.studio; // Hide the Stage's playback bar → clean, chromeless, auto-looping video. R.useEffect(() => { const hide = () => { const svg = document.querySelector('[data-om-exportable-video-with-duration-secs]'); if (!svg) return false; const canvasArea = svg.parentElement; const stageRoot = canvasArea && canvasArea.parentElement; if (!stageRoot) return false; Array.prototype.forEach.call(stageRoot.children, (c) => { if (c !== canvasArea) c.style.display = 'none'; }); return true; }; if (!hide()) { const id = setInterval(() => { if (hide()) clearInterval(id); }, 120); setTimeout(() => clearInterval(id), 3000); } }, []); return R.createElement('div', { style: { position: 'fixed', inset: 0, background: L.bg } }, R.createElement(window.Stage, { width: 1920, height: 1080, duration: DUR, background: L.bg, loop: true, autoplay: true }, // music baked into the video export (muted in the live player) R.createElement(window.VideoSprite, { src: 'music-bed.wav', start: 0, end: 33.1, style: { position: 'absolute', width: 2, height: 2, opacity: 0, right: 0, bottom: 0, pointerEvents: 'none' } }), R.createElement(Intro, { seg: INTRO, L }), MODS.map((m) => R.createElement(Module, { key: m.key, mod: m, L })), R.createElement(Outro, { seg: OUTRO, L }))); } window.ModulVideo = ModulVideo; })();