// Act 2 — Solution pipeline (5–20s): URL → analyze → clip → publish adapted function Act2Solution() { return ( ); } function Act2Inner() { const { localTime: t } = useSprite(); // Phases (act-local, total 15s): // 0.0 - 1.5 Logo reveal // 1.5 - 4.5 Paste URL (1 typed, 1 instant) // 4.5 - 7.5 AI analysis // 7.5 - 11.0 Clip generation // 11.0 - 15.0 Publish adapted to each network return (
{t < 1.5 && } {t >= 1.5 && t < 4.5 && } {t >= 4.5 && t < 7.5 && } {t >= 7.5 && t < 11.0 && } {t >= 11.0 && t < 15.0 && }
); } function LogoReveal({ t }) { // Logo reveal compressed to 1.5s total: fade-in 0.0-0.4s, hold 0.4-1.1s, fade-out 1.1-1.5s. // Slogan moved to Act 5 (closer to CTA). if (t >= 1.5) return null; const fadeIn = Easing.easeOutCubic(clamp(t / 0.4, 0, 1)); const fadeOut = t > 1.1 ? Easing.easeInCubic(clamp((1.5 - t) / 0.4, 0, 1)) : 1; const opacity = Math.min(fadeIn, fadeOut); const scaleK = Easing.easeOutBack(clamp(t / 0.55, 0, 1)); const scale = 0.6 + scaleK * 0.4; return (
Chispa
); } function BoltLogo({ size = 80 }) { return ( ); } function PhaseURL({ t }) { // 3.0s window (1.5-4.5 absolute). One URL typed, the other appears instantly. const opacity = t < 0.3 ? Easing.easeOutCubic(t / 0.3) : t > 2.6 ? Easing.easeInCubic(Math.max(0, (3.0 - t) / 0.4)) : 1; const url1 = 'twitch.tv/streamer/v/2847391'; const url2 = 'youtube.com/watch?v=Hk9oG7tT4nQ'; // Twitch typed in 0.0–1.4s. YouTube appears pre-filled at 1.4s with scale-in. const typed1 = t < 1.4 ? url1.slice(0, Math.min(url1.length, Math.floor(t * 26))) : url1; const url1Active = t < 1.4; const url1Done = t >= 1.4; const url2In = clamp((t - 1.4) / 0.35, 0, 1); const url2Eased = Easing.easeOutBack(url2In); const url2Done = t >= 1.6; const showCursor = Math.floor(t * 4) % 2 === 0; const buttonProgress = Easing.easeOutCubic(clamp((t - 1.8) * 4, 0, 1)); // CLICK animation centered around t=2.2 (well before phase end at 3.0) let btnScale = 1; let btnBrightness = 1; let pressed = false; if (t >= 2.05 && t < 2.2) { const k = (t - 2.05) / 0.15; btnScale = 1 + k * 0.05; } else if (t >= 2.2 && t < 2.35) { const k = (t - 2.2) / 0.15; btnScale = 1.05 - k * 0.13; btnBrightness = 1 + k * 0.3; pressed = true; } else if (t >= 2.35 && t < 2.5) { const k = (t - 2.35) / 0.15; btnScale = 0.92 + k * 0.08; btnBrightness = 1.3 - k * 0.3; pressed = true; } else if (t >= 2.5) { pressed = true; } const rippleStart = 2.3; const rippleProgress = clamp((t - rippleStart) / 0.4, 0, 1); const showRipple = rippleProgress > 0 && rippleProgress < 1; const showClickCursor = t >= 1.9 && t < 2.35; const cursorIn = clamp((t - 1.85) / 0.15, 0, 1); return (
paso 1 · pega tu directo o vídeo
} typed={typed1} active={url1Active} done={url1Done} cursor={showCursor} />
} typed={url2} active={false} done={url2Done} cursor={false} />
{/* Ripple */} {showRipple && (
)} {/* In-product CTA — neutral so it doesn't compete with Act 5 hero CTA */}
Detectar la chispa →
{/* Click cursor — appears, hovers, taps */} {showClickCursor && buttonProgress > 0.5 && ( )}
); } function URLRow({ label, labelColor, icon, typed, active, done, cursor }) { const borderColor = active ? '#EA580C' : done ? 'rgba(34,197,94,0.5)' : 'rgba(255,255,255,0.08)'; const glow = active ? '0 0 32px rgba(234,88,12,0.3)' : 'none'; return (
{label}
{icon}
{typed}
{done && ( )}
); } function TwitchIcon() { return ( ); } function YouTubeIcon() { return ( ); } function PhaseAnalysis({ t }) { // 3s phase (4.5-7.5 absolute). Hold until 2.5, fade 2.5-3.0. const opacity = t < 0.3 ? Easing.easeOutCubic(t / 0.3) : t > 2.5 ? Easing.easeInCubic(clamp((3.0 - t) / 0.5, 0, 1)) : 1; // Animated waveform with detection markers const bars = []; for (let i = 0; i < 80; i++) { const phase = i * 0.4 + t * 3; const h = 10 + Math.abs(Math.sin(phase)) * 50 + Math.abs(Math.sin(phase * 1.3)) * 30; bars.push(h); } // Detection markers at specific positions; appear progressively const markers = [ { pos: 18, label: 'grito', appear: 0.6 }, { pos: 36, label: 'reacción', appear: 1.2 }, { pos: 52, label: 'giro', appear: 1.8 }, { pos: 68, label: 'climax', appear: 2.4 }, ]; // Scanning line position — scaled to 3s phase const scanX = (t / 3.0) * 100; return (
paso 2 · IA analiza tu directo
{/* Waveform */}
{bars.map((h, i) => { const isNearMarker = markers.some(m => Math.abs(m.pos - i) < 3 && t > m.appear); return (
); })}
{/* Scanning line */}
{/* Markers */} {markers.map((m, i) => { if (t < m.appear) return null; const opacity = Math.min(1, (t - m.appear) * 3); return (
{m.label}
); })} {/* Bottom label */}
analizando audio + reacciones · 03:24:18
{markers.filter(m => t > m.appear).length} momentos detectados
); } function PhaseClip({ t }) { // 3.5s phase (7.5-11.0 absolute). Hold until 3.0, fade 3.0-3.5. const opacity = t < 0.3 ? Easing.easeOutCubic(t / 0.3) : t > 3.0 ? Easing.easeInCubic(clamp((3.5 - t) / 0.5, 0, 1)) : 1; // Subtitle bake-in const subProgress = Math.min(1, Math.max(0, (t - 0.6) / 0.5)); // Caption rotation cycles through 3 subtitle styles to show variety const cap = Math.min(2, Math.floor(Math.max(0, t - 0.7) / 0.65)); const captions = ['¡NO J*DAS!', 'NO ME LO CREO', 'ESTO ES BRUTAL']; const captionText = captions[cap]; // Subtle "now generating" pulse on the clip frame const framePulse = 0.5 + Math.sin(t * 3.2) * 0.5; return (
paso 3 · clip vertical listo
{/* Left: phone-mockup-style vertical clip */}
{/* Fake video frame — a centered "face" silhouette + waveform feet */}
{/* Top badge */}
9:16 · 28s
renderizado
{/* Burned subtitle */} {subProgress > 0 && (
{captionText}
)} {/* Progress dots at bottom */}
{[0, 1, 2].map(i => (
))}
{/* Right: feature checklist */}
); } function ClipFeature({ delay, t, icon, label, desc }) { const local = t - delay; const ease = Easing.easeOutCubic(clamp(local * 4, 0, 1)); return (
{icon}
{label}
{desc}
); } // ── Phase 4 · Publica adaptado a cada red (4s, merged adapt+publish) ──────── function PhaseAdaptPublish({ t }) { const opacity = t < 0.3 ? Easing.easeOutCubic(t / 0.3) : t > 3.5 ? Easing.easeInCubic(clamp((4.0 - t) / 0.5, 0, 1)) : 1; const headlineProgress = Easing.easeOutCubic(clamp(t / 0.5, 0, 1)); const cards = [ { label: 'YouTube Shorts', sub: 'estética limpia', accent: '#FF0033', icon: , delay: 0.4, renderClip: () => (
que pasó después...
), }, { label: 'TikTok', sub: 'bold · alto contraste', accent: '#25F4EE', icon: , delay: 0.65, renderClip: () => (
¿QUÉ PASÓ?!
), }, { label: 'Instagram Reels', sub: 'hook visual fijo', accent: '#E1306C', icon: , delay: 0.9, renderClip: () => (
¿QUÉ PASÓ AQUÍ?
), }, ]; return (
paso 4 · publica adaptado a cada red
publica mientras duermes.
{cards.map((c, i) => { const local = t - c.delay; const slide = Easing.easeOutCubic(clamp(local * 2.4, 0, 1)); const done = local > 0.8; const doneIn = Easing.easeOutBack(clamp((local - 0.8) / 0.3, 0, 1)); return (
{c.label}
{done && (
)}
{c.icon}
{c.renderClip()}
{c.sub}
); })}
); } function PubYouTube() { return ( ); } function PubTikTok() { return ( ); } function PubInstagram() { return ( ); } function Subtitles({ t }) { // Cues aligned with new 15s act-local timeline const cues = [ { t: 1.5, end: 4.5, es: 'Pega tu URL · Twitch o YouTube.', en: 'Paste your URL · Twitch or YouTube.' }, { t: 4.5, end: 7.5, es: 'La IA detecta los momentos virales.', en: 'AI spots the viral moments.' }, { t: 7.5, end: 11.0, es: 'Corta, encuadra y quema subtítulos.', en: 'Cuts, frames and burns subtitles.' }, { t: 11.0, end: 15.0, es: 'Publica adaptado a cada red.', en: 'Publishes adapted to each network.' }, ]; return ; } window.Act2Solution = Act2Solution;