// KarlaPay — Scenes part 2: Carla call + Result + Tagline // ───────────────────────────────────────────────────────────────────────────── // SCENE 3 — Carla calls (10.5 → 19.0s) // Abstract orange orb pulses while a voice waveform animates; live transcript. function Waveform({ localTime, width = 520, height = 120, bars = 48, active = true }) { const barW = 4; const gap = (width - bars * barW) / (bars - 1); return (
{Array.from({ length: bars }).map((_, i) => { // Organic waveform using layered sines const phase = i * 0.42; const envelope = Math.sin((i / bars) * Math.PI); // peak in middle const base = 0.15; const variance = active ? (0.5 + 0.5 * Math.sin(localTime * 6 + phase)) * (0.6 + 0.4 * Math.sin(localTime * 2.3 + phase * 0.5)) : 0.1; const h = (base + variance * envelope * 0.85) * height; const color = ORANGE; return (
); })}
); } function CarlaOrb({ localTime, size = 280 }) { const pulse1 = 1 + 0.08 * Math.sin(localTime * 2.2); const pulse2 = 1 + 0.12 * Math.sin(localTime * 3.1 + 1.2); const pulse3 = 1 + 0.06 * Math.sin(localTime * 1.5 + 2.4); return (
{/* Outer rings */}
{/* Glow */}
{/* Core */}
{/* Inner highlight */}
{/* Headset suggestion - thin arc */}
); } function TranscriptLine({ speaker, text, visible, enter, color }) { const opacity = clamp(enter, 0, 1); const dy = (1 - opacity) * 12; return (
{speaker}
{text}
); } function SceneCarla() { const { localTime, duration } = useSprite(); // Phone ring establishing (0 → 1.2s) const ringT = clamp(localTime / 1.0, 0, 1); const ringEased = Easing.easeOutCubic(ringT); // Orb zooms in (1.0s) const orbAppearT = clamp((localTime - 0.6) / 0.9, 0, 1); const orbEased = Easing.easeOutBack(orbAppearT); // Connection label const connectStart = 0.3; const connectT = clamp((localTime - connectStart) / 0.5, 0, 1); // Transcript timings — after connection, lines appear const transcriptStart = 1.8; const lines = [ { at: 0.0, speaker: 'CARLA', color: ORANGE, text: '"Hola Mario, soy Carla de idme SL."' }, { at: 1.8, speaker: 'MARIO', color: MUTED, text: '"Sí, dígame."' }, { at: 3.0, speaker: 'CARLA', color: ORANGE, text: '"Le llamo por su factura de € 1.250, con vencimiento el 3 de mayo."' }, { at: 5.0, speaker: 'MARIO', color: MUTED, text: '"Ah, sí… ¿puedo pagar ahora por Bizum?"' }, ]; // Which line is "active" (driving waveform) const activeSpeaker = (() => { const t = localTime - transcriptStart; let current = 'CARLA'; for (const l of lines) { if (t >= l.at) current = l.speaker; } return current; })(); // Exit const exitStart = duration - 0.7; const exitT = clamp((localTime - exitStart) / 0.7, 0, 1); const exitOpacity = 1 - Easing.easeInCubic(exitT); return (
{/* Top bar: call status */}
LLAMADA EN CURSO · {formatCallTime(Math.max(0, localTime - 0.3))}
{/* Left: Orb */}
Carla
AGENTE DE IA
{/* Center-right: Transcript + waveform */}
{/* Waveform at top of transcript area */}
{/* Lines */}
{lines.map((l, i) => { const lineLocal = localTime - transcriptStart - l.at; const enter = clamp(lineLocal / 0.5, 0, 1); const visible = lineLocal >= 0; return ( ); })}
); } function formatCallTime(t) { const m = Math.floor(t / 60); const s = Math.floor(t % 60); return `${String(m).padStart(2, '0')}:${String(s).padStart(2, '0')}`; } Object.assign(window, { Waveform, CarlaOrb, TranscriptLine, SceneCarla, formatCallTime, });