// video.jsx — "ИИ для бизнеса" social animation (1:1) // Scenes: ДО (routine pile-up) → обработка (Alakris AI clears) → ПОСЛЕ (time saved) → CTA // Uses the timeline engine globals registered by animations.jsx (window.Stage, etc). const NAVY = '#0B0F19'; const SURFACE = '#141B2E'; const SURF2 = '#1A2238'; const ROW = '#10172A'; const ACCENT = '#4F8CFF'; const BORDER = 'rgba(255,255,255,0.08)'; const BORDER2 = 'rgba(255,255,255,0.14)'; const TEXT = '#F8FAFC'; const TEXT2 = '#94A3B8'; const TEXT3 = '#5B6B86'; const SUCCESS = '#22C55E'; const DANGER = '#EF4444'; const WARN = '#F59E0B'; const SANS = "Inter, system-ui, -apple-system, sans-serif"; const MONO = "'JetBrains Mono', ui-monospace, monospace"; const TASKS = [ { t: 'Ответы клиентам', meta: 'поддержка · 47 в очереди', tag: '12 мин' }, { t: 'Отчёт по продажам', meta: 'аналитика · еженедельно', tag: '40 мин' }, { t: 'Резюме встреч', meta: '6 созвонов сегодня', tag: '25 мин' }, { t: 'Обновить CRM', meta: 'синхронизация лидов', tag: '18 мин' }, { t: 'Разбор входящих', meta: '210 непрочитанных', tag: '35 мин' }, { t: 'Выставить счета', meta: 'финансы · просрочено', tag: '22 мин' }, ]; // doneAt[i] — when the AI marks each row complete during processing const DONE_AT = TASKS.map((_, i) => 5.05 + i * 0.30); // ── tiny helpers (read engine off window at call time) ────────────────────── function clampN(v, a, b) { return Math.max(a, Math.min(b, v)); } function ClockIcon({ size = 26, color = TEXT2, angle = 0 }) { return ( React.createElement('svg', { width: size, height: size, viewBox: '0 0 24 24', fill: 'none' }, React.createElement('circle', { cx: 12, cy: 12, r: 9, stroke: color, strokeWidth: 1.6 }), React.createElement('line', { x1: 12, y1: 12, x2: 12, y2: 6.5, stroke: color, strokeWidth: 1.6, strokeLinecap: 'round', transform: `rotate(${angle} 12 12)`, }), React.createElement('line', { x1: 12, y1: 12, x2: 15.6, y2: 12, stroke: color, strokeWidth: 1.6, strokeLinecap: 'round', transform: `rotate(${angle * 12} 12 12)`, }), ) ); } function Check({ size = 16, color = '#fff', w = 2.4 }) { return React.createElement('svg', { width: size, height: size, viewBox: '0 0 24 24', fill: 'none' }, React.createElement('path', { d: 'M5 12.5l4.5 4.5L19 6.5', stroke: color, strokeWidth: w, strokeLinecap: 'round', strokeLinejoin: 'round' })); } // ── Task row ──────────────────────────────────────────────────────────────── function TaskRow({ data, index }) { const useTime = window.useTime; const t = useTime(); // entry: rows drop in fast, stacking, with a slight chaotic jitter const appear = 0.7 + index * 0.26; const ent = clampN((t - appear) / 0.4, 0, 1); const e = window.Easing.easeOutCubic(ent); const jitter = ent < 1 ? (1 - e) : 0; // done state const done = clampN((t - DONE_AT[index]) / 0.32, 0, 1); const de = window.Easing.easeOutCubic(done); const opacity = e; const ty = (1 - e) * 26; const bg = done > 0 ? `rgba(34,197,94,${0.05 + 0.05 * de})` : ROW; const bd = done > 0 ? `rgba(34,197,94,${0.25 + 0.25 * de})` : BORDER; return React.createElement('div', { style: { display: 'flex', alignItems: 'center', gap: 16, padding: '0 22px', height: 76, background: bg, border: `1px solid ${bd}`, borderRadius: 10, opacity, transform: `translateY(${ty}px) translateX(${jitter * (index % 2 ? 8 : -8)}px)`, willChange: 'transform, opacity', } }, // status square React.createElement('div', { style: { width: 38, height: 38, borderRadius: 8, flexShrink: 0, display: 'flex', alignItems: 'center', justifyContent: 'center', background: done > 0 ? `rgba(34,197,94,${0.18 + de * 0.1})` : 'rgba(245,158,11,0.14)', border: `1px solid ${done > 0 ? `rgba(34,197,94,${0.4})` : 'rgba(245,158,11,0.35)'}`, transition: 'none', } }, done > 0.5 ? React.createElement(Check, { size: 20, color: SUCCESS }) : React.createElement('div', { style: { width: 8, height: 8, borderRadius: 9, background: WARN } }) ), // text React.createElement('div', { style: { flex: 1, minWidth: 0 } }, React.createElement('div', { style: { fontFamily: SANS, fontSize: 21, fontWeight: 600, letterSpacing: '-0.01em', color: done > 0.5 ? TEXT3 : TEXT, textDecoration: done > 0.5 ? 'line-through' : 'none', textDecorationColor: 'rgba(91,107,134,0.7)', whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis', } }, data.t), React.createElement('div', { style: { fontFamily: MONO, fontSize: 12.5, color: TEXT3, marginTop: 3, letterSpacing: '0.01em' } }, data.meta), ), // tag React.createElement('div', { style: { fontFamily: MONO, fontSize: 13, fontWeight: 500, color: done > 0.5 ? SUCCESS : WARN, padding: '5px 10px', borderRadius: 6, flexShrink: 0, background: done > 0.5 ? 'rgba(34,197,94,0.12)' : 'rgba(245,158,11,0.12)', } }, done > 0.5 ? 'готово' : data.tag), ); } // ── BEFORE view (rows + scan line) ────────────────────────────────────────── function BeforeView() { const t = window.useTime(); // fade the whole before-view out as we transition to calm const out = clampN((t - 7.4) / 0.45, 0, 1); const opacity = 1 - window.Easing.easeInCubic(out); const scale = 1 - 0.04 * out; if (opacity <= 0) return null; // scan line sweep 4.9 → 6.9 const scanP = clampN((t - 4.9) / 2.0, 0, 1); const showScan = t > 4.85 && t < 7.1; const scanTop = scanP * 100; return React.createElement('div', { style: { position: 'absolute', inset: 0, padding: 26, display: 'flex', flexDirection: 'column', gap: 12, opacity, transform: `scale(${scale})`, transformOrigin: '50% 40%', } }, TASKS.map((d, i) => React.createElement(TaskRow, { key: i, data: d, index: i })), // scan line showScan && React.createElement('div', { style: { position: 'absolute', left: 14, right: 14, top: `${scanTop}%`, height: 2, background: ACCENT, boxShadow: `0 0 22px 4px rgba(79,140,255,0.65)`, borderRadius: 2, } }), ); } // ── CALM view (after) ─────────────────────────────────────────────────────── function CalmView() { const t = window.useTime(); const ent = clampN((t - 7.7) / 0.5, 0, 1); if (ent <= 0) return null; const e = window.Easing.easeOutCubic(ent); // fade out for CTA const out = clampN((t - 10.7) / 0.4, 0, 1); const opacity = e * (1 - out); // count-up hours const hrs = Math.round(clampN((t - 7.9) / 1.4, 0, 1) ** 0.85 * 14); const pctIn = clampN((t - 8.9) / 0.4, 0, 1); return React.createElement('div', { style: { position: 'absolute', inset: 0, display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', gap: 4, opacity, transform: `translateY(${(1 - e) * 16}px)`, } }, // big check React.createElement('div', { style: { width: 76, height: 76, borderRadius: 20, marginBottom: 18, display: 'flex', alignItems: 'center', justifyContent: 'center', background: 'rgba(34,197,94,0.14)', border: '1px solid rgba(34,197,94,0.4)', transform: `scale(${0.7 + 0.3 * window.Easing.easeOutBack(ent)})`, } }, React.createElement(Check, { size: 40, color: SUCCESS, w: 2.6 })), React.createElement('div', { style: { fontFamily: MONO, fontSize: 14, color: SUCCESS, letterSpacing: '0.04em', marginBottom: 26 } }, 'ОЧЕРЕДЬ ПУСТА · АВТОМАТИЗИРОВАНО'), // hero stat React.createElement('div', { style: { display: 'flex', alignItems: 'baseline', gap: 14 } }, React.createElement('div', { style: { fontFamily: SANS, fontSize: 150, fontWeight: 700, color: TEXT, letterSpacing: '-0.04em', lineHeight: 1 } }, `−${hrs}`), React.createElement('div', { style: { textAlign: 'left' } }, React.createElement('div', { style: { fontFamily: SANS, fontSize: 30, fontWeight: 600, color: TEXT, lineHeight: 1.1 } }, 'часов'), React.createElement('div', { style: { fontFamily: SANS, fontSize: 30, fontWeight: 600, color: ACCENT, lineHeight: 1.1 } }, 'в неделю'), ), ), React.createElement('div', { style: { fontFamily: SANS, fontSize: 20, color: TEXT2, marginTop: 12 } }, 'высвобождено на каждого сотрудника'), // productivity chip React.createElement('div', { style: { marginTop: 30, opacity: pctIn, transform: `translateY(${(1 - pctIn) * 10}px)`, display: 'flex', alignItems: 'center', gap: 10, padding: '12px 20px', borderRadius: 999, background: 'rgba(79,140,255,0.12)', border: '1px solid rgba(79,140,255,0.35)', } }, React.createElement('div', { style: { width: 8, height: 8, borderRadius: 9, background: ACCENT } }), React.createElement('span', { style: { fontFamily: SANS, fontSize: 19, fontWeight: 600, color: TEXT } }, '+38% к продуктивности команды'), ), ); } // ── Alakris agent pill (bottom of card during processing) ─────────────────── function AgentPill() { const t = window.useTime(); const ent = clampN((t - 4.5) / 0.4, 0, 1); const out = clampN((t - 7.5) / 0.4, 0, 1); if (ent <= 0 || out >= 1) return null; const opacity = ent * (1 - out); const working = t < 7.0; const dotPulse = 0.4 + 0.6 * Math.abs(Math.sin(t * 4)); return React.createElement('div', { style: { position: 'absolute', left: '50%', bottom: 20, transform: `translateX(-50%) translateY(${(1 - ent) * 12}px)`, opacity, display: 'flex', alignItems: 'center', gap: 12, padding: '13px 22px', borderRadius: 12, background: 'rgba(11,15,25,0.9)', border: `1px solid ${working ? 'rgba(79,140,255,0.5)' : 'rgba(34,197,94,0.5)'}`, boxShadow: '0 12px 40px rgba(0,0,0,0.5)', } }, working ? React.createElement('div', { style: { width: 18, height: 18, borderRadius: 99, border: '2px solid rgba(79,140,255,0.3)', borderTopColor: ACCENT, transform: `rotate(${t * 720}deg)`, } }) : React.createElement('div', { style: { width: 22, height: 22, borderRadius: 99, background: 'rgba(34,197,94,0.2)', display: 'flex', alignItems: 'center', justifyContent: 'center' } }, React.createElement(Check, { size: 14, color: SUCCESS })), React.createElement('span', { style: { fontFamily: SANS, fontSize: 17, fontWeight: 600, color: TEXT } }, working ? 'Alakris AI обрабатывает очередь' : 'Готово — рутина закрыта'), working && React.createElement('div', { style: { width: 7, height: 7, borderRadius: 9, background: ACCENT, opacity: dotPulse } }), ); } // ── The work-surface card ─────────────────────────────────────────────────── function WorkCard() { const t = window.useTime(); // card present 0.3 → 11.0, then recedes for CTA const ent = clampN((t - 0.3) / 0.5, 0, 1); const e = window.Easing.easeOutCubic(ent); const out = clampN((t - 10.9) / 0.5, 0, 1); const oe = window.Easing.easeInCubic(out); const opacity = e * (1 - oe); if (opacity <= 0) return null; const scale = (0.96 + 0.04 * e) * (1 - 0.06 * oe); // header counter let q; if (t < 4.8) q = window.interpolate([0.8, 4.2], [0, 248], window.Easing.easeOutQuad)(t); else q = window.interpolate([4.8, 7.0], [248, 0], window.Easing.easeInOutCubic)(t); const queue = Math.max(0, Math.round(q)); const overloaded = t > 1.6 && t < 7.2 && queue > 0; const clear = t >= 7.0; // clock angle — fast during ДО, freezes const angle = (t < 6.9 ? t : 6.9) * 230 % 360; return React.createElement('div', { style: { position: 'absolute', left: 90, top: 232, width: 900, height: 744, background: SURFACE, border: `1px solid ${BORDER2}`, borderRadius: 18, boxShadow: '0 30px 80px rgba(0,0,0,0.55)', opacity, transform: `scale(${scale})`, transformOrigin: '50% 45%', display: 'flex', flexDirection: 'column', overflow: 'hidden', } }, // header React.createElement('div', { style: { height: 84, flexShrink: 0, padding: '0 26px', display: 'flex', alignItems: 'center', gap: 16, borderBottom: `1px solid ${BORDER}`, background: SURF2, } }, // app dot React.createElement('div', { style: { display: 'flex', gap: 7 } }, React.createElement('div', { style: { width: 11, height: 11, borderRadius: 9, background: 'rgba(255,255,255,0.18)' } }), React.createElement('div', { style: { width: 11, height: 11, borderRadius: 9, background: 'rgba(255,255,255,0.18)' } }), React.createElement('div', { style: { width: 11, height: 11, borderRadius: 9, background: 'rgba(255,255,255,0.18)' } }), ), React.createElement('div', { style: { fontFamily: SANS, fontSize: 20, fontWeight: 600, color: TEXT, marginLeft: 6 } }, 'Очередь задач'), React.createElement('div', { style: { flex: 1 } }), // counter badge React.createElement('div', { style: { display: 'flex', alignItems: 'center', gap: 8, padding: '7px 14px', borderRadius: 8, background: clear ? 'rgba(34,197,94,0.14)' : (overloaded ? 'rgba(239,68,68,0.14)' : 'rgba(245,158,11,0.14)'), border: `1px solid ${clear ? 'rgba(34,197,94,0.4)' : (overloaded ? 'rgba(239,68,68,0.4)' : 'rgba(245,158,11,0.35)')}`, } }, React.createElement('span', { style: { fontFamily: MONO, fontSize: 22, fontWeight: 600, fontVariantNumeric: 'tabular-nums', color: clear ? SUCCESS : (overloaded ? DANGER : WARN) } }, String(queue)), React.createElement('span', { style: { fontFamily: SANS, fontSize: 14, color: TEXT2 } }, 'в очереди'), ), // clock / check clear ? React.createElement('div', { style: { width: 28, display: 'flex', justifyContent: 'center' } }, React.createElement(Check, { size: 24, color: SUCCESS })) : React.createElement(ClockIcon, { size: 28, color: overloaded ? DANGER : TEXT2, angle }), ), // body React.createElement('div', { style: { flex: 1, position: 'relative' } }, React.createElement(BeforeView, null), React.createElement(CalmView, null), React.createElement(AgentPill, null), ), ); } // ── Headline + scene chip above the card ──────────────────────────────────── function TopBlock() { const t = window.useTime(); const before = t < 7.35; // chip const chipEnt = clampN((t - 0.9) / 0.4, 0, 1); const ctaOut = clampN((t - 10.8) / 0.4, 0, 1); const blockOp = (1 - ctaOut); if (blockOp <= 0) return null; // headline crossfade const beforeOp = clampN(Math.min((t - 0.7) / 0.5, (7.35 - t) / 0.4), 0, 1); const afterOp = clampN(Math.min((t - 7.6) / 0.45, (10.8 - t) / 0.4), 0, 1); const chipColor = before ? DANGER : SUCCESS; const chipBg = before ? 'rgba(239,68,68,0.13)' : 'rgba(34,197,94,0.13)'; const chipText = before ? 'ДО' : 'ПОСЛЕ'; return React.createElement('div', { style: { position: 'absolute', left: 90, right: 90, top: 96, display: 'flex', flexDirection: 'column', alignItems: 'flex-start', gap: 16, opacity: blockOp, } }, React.createElement('div', { style: { opacity: chipEnt, fontFamily: MONO, fontSize: 14, fontWeight: 600, letterSpacing: '0.1em', color: chipColor, background: chipBg, border: `1px solid ${before ? 'rgba(239,68,68,0.35)' : 'rgba(34,197,94,0.35)'}`, padding: '6px 14px', borderRadius: 7, } }, chipText), // headlines stacked, crossfaded React.createElement('div', { style: { position: 'relative', height: 56, alignSelf: 'stretch' } }, React.createElement('div', { style: { position: 'absolute', left: 0, top: 0, opacity: beforeOp, fontFamily: SANS, fontSize: 44, fontWeight: 700, letterSpacing: '-0.025em', color: TEXT } }, 'Рутина съедает часы каждый день'), React.createElement('div', { style: { position: 'absolute', left: 0, top: 0, opacity: afterOp, fontFamily: SANS, fontSize: 44, fontWeight: 700, letterSpacing: '-0.025em', color: TEXT } }, 'Alakris ', React.createElement('span', { style: { color: ACCENT } }, 'забирает рутину на себя'), ), ), ); } // ── CTA scene ─────────────────────────────────────────────────────────────── function CTAScene() { const t = window.useTime(); const ent = clampN((t - 11.1) / 0.6, 0, 1); if (ent <= 0) return null; const e = window.Easing.easeOutCubic(ent); const lineW = clampN((t - 11.35) / 0.5, 0, 1); const tagOp = clampN((t - 11.5) / 0.5, 0, 1); const btnEnt = clampN((t - 11.9) / 0.5, 0, 1); const btnE = window.Easing.easeOutBack(btnEnt); return React.createElement('div', { style: { position: 'absolute', inset: 0, display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', gap: 0, } }, React.createElement('img', { src: 'assets/logo-white.png', alt: 'Alakris', style: { width: 460, opacity: e, transform: `translateY(${(1 - e) * 14}px)` } }), React.createElement('div', { style: { width: lineW * 120, height: 2, background: ACCENT, marginTop: 36, borderRadius: 2 } }), React.createElement('div', { style: { marginTop: 30, opacity: tagOp, textAlign: 'center', fontFamily: SANS, fontSize: 30, fontWeight: 500, color: TEXT2, letterSpacing: '-0.01em', } }, 'ИИ-платформа, которая берёт', React.createElement('br', null), React.createElement('span', { style: { color: TEXT } }, 'рутину бизнеса на себя'), ), React.createElement('div', { style: { marginTop: 46, opacity: clampN(btnEnt * 1.3, 0, 1), transform: `scale(${0.85 + 0.15 * btnE})`, display: 'flex', alignItems: 'center', gap: 12, padding: '20px 40px', borderRadius: 12, background: ACCENT, boxShadow: '0 16px 50px rgba(79,140,255,0.4)', fontFamily: SANS, fontSize: 26, fontWeight: 600, color: '#fff', } }, 'Начните с Alakris', React.createElement('svg', { width: 24, height: 24, viewBox: '0 0 24 24', fill: 'none' }, React.createElement('path', { d: 'M5 12h14M13 6l6 6-6 6', stroke: '#fff', strokeWidth: 2.2, strokeLinecap: 'round', strokeLinejoin: 'round' })), ), ); } // ── Background: navy + dot grid + faint /// motif ─────────────────────────── function Background() { const t = window.useTime(); const drift = Math.sin(t * 0.3) * 6; return React.createElement('div', { style: { position: 'absolute', inset: 0, background: NAVY, overflow: 'hidden', } }, React.createElement('div', { style: { position: 'absolute', inset: -40, backgroundImage: 'radial-gradient(rgba(255,255,255,0.05) 1.4px, transparent 1.4px)', backgroundSize: '34px 34px', transform: `translate(${drift}px, ${-drift}px)`, } }), React.createElement('div', { style: { position: 'absolute', inset: 0, background: 'radial-gradient(120% 80% at 50% 30%, rgba(79,140,255,0.10), transparent 60%)', } }), ); } function AIVideo(props) { const [ready, setReady] = React.useState(!!window.Stage); React.useEffect(() => { if (window.Stage) { setReady(true); return; } const id = setInterval(() => { if (window.Stage) { setReady(true); clearInterval(id); } }, 30); return () => clearInterval(id); }, []); if (!ready) return null; const { Stage } = window; return React.createElement(Stage, { width: 1080, height: 1080, duration: 13.5, background: NAVY, persistKey: 'alakris-ai-video', controls: (props && props.controls === false) ? false : true, }, React.createElement(Background, null), React.createElement(TopBlock, null), React.createElement(WorkCard, null), React.createElement(CTAScene, null), ); } module.exports = { AIVideo }; if (typeof window !== 'undefined') window.AIVideo = AIVideo;