/* 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;
})();