/* AcreliaVideo.jsx — Video corporativo de producto (versión dinámica)
   La plataforma es la protagonista: marco de navegador grande y persistente,
   cámara que recorre la UI, etiquetas ancladas, cortes y montaje. Poco texto.
   Sincronizado al audio de la voz en off. 1920×1080, auto-escalado. */

const { useState, useRef, useEffect, useCallback } = React;

/* ----------------------------------------------------------- paleta */
const C = {
  blue:'#1992C9', blueLt:'#46AEDC', blueDeep:'#0F6E97', blueDark:'#0B4A66',
  ink:'#16242E', ink2:'#24333D', navy:'#0B1B25', navy2:'#0E2533',
  mist:'#EAF4FA', mist2:'#F3F9FC', white:'#FFFFFF', soft:'#5C6E78', softLt:'rgba(255,255,255,0.66)',
};
const FONT = "'Manrope', system-ui, sans-serif";
const MONO = "ui-monospace, 'SF Mono', Menlo, monospace";

/* --------------------------------------------------------- easing */
const E = {
  outCubic: x => 1 - Math.pow(1 - x, 3),
  inOutCubic: x => x < 0.5 ? 4*x*x*x : 1 - Math.pow(-2*x+2, 3)/2,
  outExpo: x => x >= 1 ? 1 : 1 - Math.pow(2, -10*x),
  outQuint: x => 1 - Math.pow(1 - x, 5),
  outBack: x => { const c1=1.70158, c3=c1+1; return 1 + c3*Math.pow(x-1,3) + c1*Math.pow(x-1,2); },
};
const clamp = (v,a,b) => Math.max(a, Math.min(b, v));
const lerp  = (a,b,p) => a + (b-a)*p;
function P(lt, delay, dur, ease){ return (ease||E.outCubic)(clamp((lt-(delay||0))/(dur||0.6), 0, 1)); }
function rv(lt, delay, dur, ease, fromY){
  const e = P(lt, delay, dur||0.7, ease||E.outCubic);
  return { opacity: e, transform: `translateY(${(1-e)*(fromY==null?44:fromY)}px)` };
}

/* ------------------------------------------------------- timeline */
const TL = [
  { key:'intro', s:0.0, e:5.62 }, { key:'plataforma', s:5.62, e:12.12 }, { key:'usable', s:12.12, e:17.52 },
  { key:'crm', s:17.52, e:32.94 }, { key:'editor', s:32.94, e:48.10 }, { key:'ia', s:48.10, e:56.08 },
  { key:'imagenes', s:56.08, e:67.0 }, { key:'home', s:67.0, e:83.82 }, { key:'topbar', s:83.82, e:92.34 },
  { key:'reputacion', s:92.34, e:103.4 }, { key:'idiomas', s:103.4, e:110.7 },
  { key:'cierre', s:110.7, e:125.00 }, { key:'tagline', s:125.00, e:129.18 },
];
const DUR = 129.18;
const AUDIO_SRC = 'uploads/audio_video_acrelia.mp3';
const MUSIC_SRC = 'uploads/music_acrelia.mp3';

/* capturas disponibles (añade aquí cada archivo cuando lo subas a assets/).
   Las que no estén en el set muestran placeholder automáticamente.
   Para el vídeo del CRM (assets/s4-crm.mp4), poner CLIP_READY = true. */
const READY_FILES = new Set([
  'assets/s2-overview.png',
  'assets/s4-crm.mp4',
  'assets/s2-overview-2.png',
  'assets/s2-overview-3.png',
  'assets/s5-editor.png',
  'assets/s5-editor.mp4',
  'assets/s6-ia.mp4',
  'assets/s7-imagenes.png',
  'assets/s7-imagenes-2.png',
  'assets/s7-imagenes-3.png',
  'assets/s9-topbar.png',
  'assets/s9-topbar.mp4',
  'assets/s10-reputacion.png',
  'assets/s10-reputacion.mp4',
  'assets/s8-home.png',
  'assets/s8-home.mp4',
  'assets/final-2.png',
  'assets/final-1.png',
  'assets/final-3.png',
  'assets/final-4.png',
  'assets/final-5.png',
  'assets/final-6.png',
  'assets/s2-usable.png',
  'assets/s2-usable-dark.png',
]);
const has = (f) => !!f && READY_FILES.has(f);
const shot = (file) => has(file) ? file : null;
const MEDIA = { playing: false };   // estado compartido para el vídeo embebido
const BLOB_CACHE = {};               // path -> objectURL (vídeo descargado en memoria)
const vsrc = (p) => (p && BLOB_CACHE[p]) || p;
const ALL_VIDEOS = ['assets/s4-crm.mp4','assets/s5-editor.mp4','assets/s6-ia.mp4','assets/s8-home.mp4','assets/s9-topbar.mp4','assets/s10-reputacion.mp4'];

/* marco de navegador grande y persistente */
const FR = { x:210, y:128, w:1500, h:892, chrome:60 };
const FRC = FR.h - FR.chrome;   // altura del lienzo interior

/* ===================================================== primitivos */

function Chrome() {
  return (
    <div style={{ height:FR.chrome, background:'#F4F8FB', borderBottom:'1px solid #E4ECF1',
      display:'flex', alignItems:'center', padding:'0 24px', gap:11 }}>
      <span style={{ width:13, height:13, borderRadius:7, background:'#CBD7DE' }}></span>
      <span style={{ width:13, height:13, borderRadius:7, background:'#CBD7DE' }}></span>
      <span style={{ width:13, height:13, borderRadius:7, background:'#CBD7DE' }}></span>
      <div style={{ flex:1, display:'flex', justifyContent:'center' }}>
        <div style={{ background:'#fff', border:'1px solid #E4ECF1', borderRadius:9, height:34, minWidth:340,
          display:'flex', alignItems:'center', justifyContent:'center', gap:10, padding:'0 20px',
          fontFamily:MONO, fontSize:17, color:'#8497A1' }}>
          <span style={{ width:9, height:12, border:'2px solid #B6C5CD', borderRadius:2, display:'inline-block' }}></span>
          manager.acrelia.com
        </div>
      </div>
      <span style={{ width:54 }}></span>
    </div>
  );
}

function Placeholder({ title, file, video }) {
  return (
    <div style={{ position:'absolute', inset:0, display:'flex', flexDirection:'column',
      alignItems:'center', justifyContent:'center', gap:18, backgroundColor:'#EBF3F8',
      backgroundImage:'repeating-linear-gradient(135deg, rgba(25,146,201,0.05) 0 16px, rgba(25,146,201,0.10) 16px 32px)' }}>
      <div style={{ display:'flex', flexDirection:'column', alignItems:'center', gap:10, padding:'28px 46px', borderRadius:16,
        background:'rgba(255,255,255,0.92)', border:'1px solid rgba(25,146,201,0.22)', boxShadow:'0 14px 34px -12px rgba(11,27,37,0.25)' }}>
        {video
          ? <span style={{ width:0, height:0, borderStyle:'solid', borderWidth:'13px 0 13px 22px', borderColor:'transparent transparent transparent '+C.blue }}></span>
          : <span style={{ width:30, height:30, borderRadius:7, border:'2.5px solid '+C.blue, display:'inline-block' }}></span>}
        <div style={{ fontFamily:FONT, fontWeight:800, fontSize:27, color:C.ink, lineHeight:1.1, whiteSpace:'nowrap' }}>{title}</div>
        <div style={{ fontFamily:MONO, fontSize:17, color:C.blueDeep }}>{file}</div>
      </div>
      <div style={{ fontFamily:MONO, fontSize:15, color:'rgba(11,27,37,0.4)', letterSpacing:'0.08em' }}>// arrastra aquí {video ? 'el vídeo' : 'la captura'}</div>
    </div>
  );
}

/* etiquetas apiladas (opción G): bloques sólidos pegados a un lado, acumulativos.
   side: 'l' (izquierda) | 'r' (derecha). top: separación desde arriba. */
function TagStack({ lt, items, side, top, gap }) {
  const isR = side === 'r';
  let active = -1; items.forEach((it,i)=>{ if (lt >= (it.at||0)) active = i; });
  return (
    <div style={{ position:'absolute', top: top==null?150:top, ...(isR?{right:0}:{left:0}),
      display:'flex', flexDirection:'column', gap: gap==null?12:gap, alignItems: isR?'flex-end':'flex-start' }}>
      {items.map((it,i) => {
        const e = clamp(P(lt, it.at||0, 0.5, E.outBack), 0, 1);
        const on = i === active;
        return (
          <div key={it.t} style={{ opacity:e, transform:`translateX(${(1-e)*(isR?26:-26)}px)`,
            padding:'12px 26px', borderRadius: isR ? '9px 0 0 9px' : '0 9px 9px 0',
            background: on ? C.blue : 'rgba(11,30,42,0.86)', color: on ? '#fff' : 'rgba(255,255,255,0.6)',
            fontFamily:FONT, fontWeight: on?700:600, fontSize:30, lineHeight:1.12, whiteSpace:'nowrap',
            backdropFilter:'blur(3px)',
            boxShadow: on ? '0 16px 36px -14px rgba(25,146,201,0.7)' : '0 14px 30px -16px rgba(0,0,0,0.5)',
            transition:'background .4s ease, color .4s ease, box-shadow .4s ease' }}>{it.t}</div>
        );
      })}
    </div>
  );
}

/* caption inferior (lower-third) sobre scrim */
function Caption({ lt, kicker, title, sub }) {
  return (
    <div style={{ position:'absolute', left:48, bottom:42, ...rv(lt, 0.22, 0.7, E.outCubic, 30), zIndex:4 }}>
      {kicker ? (
        <div style={{ display:'inline-flex', alignItems:'center', gap:12, marginBottom:14,
          padding:'9px 17px 9px 15px', borderRadius:9, background:'rgba(7,18,26,0.7)',
          WebkitBackdropFilter:'blur(6px)', backdropFilter:'blur(6px)', boxShadow:'0 8px 24px -12px rgba(0,0,0,0.6)' }}>
          <span style={{ width:34, height:3, borderRadius:2, background:'#7FD0F0', boxShadow:'0 0 10px rgba(127,208,240,0.7)' }}></span>
          <span style={{ fontFamily:FONT, fontWeight:800, fontSize:22, letterSpacing:'0.2em',
            textTransform:'uppercase', color:'#FFFFFF', textShadow:'0 1px 8px rgba(0,0,0,0.55)' }}>{kicker}</span>
        </div>
      ) : null}
      <div style={{ fontFamily:FONT, fontWeight:800, fontSize:52, lineHeight:1.05, letterSpacing:'-0.02em',
        color:'#fff', maxWidth:1000, textShadow:'0 2px 30px rgba(7,22,31,0.5)' }}>{title}</div>
      {sub ? <div style={{ marginTop:14, fontFamily:FONT, fontWeight:600, fontSize:31, lineHeight:1.2,
        color:'rgba(255,255,255,0.84)', maxWidth:1040, textShadow:'0 2px 22px rgba(7,22,31,0.55)' }}>{sub}</div> : null}
    </div>
  );
}

/* MARCO PRODUCTO — el héroe de casi todas las escenas */
function VideoSlot({ src, lt, fit='contain', fitDur }) {
  const ref = useRef(null);
  // precarga en cuanto se monta para que no entre "en frío" al cortar la escena
  useEffect(() => { const v = ref.current; if (!v) return;
    try { v.load(); } catch(e){}
    if (MEDIA.playing) v.play().catch(()=>{}); }, []);
  useEffect(() => { const v = ref.current; if (!v) return;
    if (MEDIA.playing) { if (v.paused) v.play().catch(()=>{}); } else v.pause(); }, [lt]);
  // solo corrige la sincronía si la desviación es grande (no reescribir currentTime cada frame: congela el vídeo)
  useEffect(() => { const v = ref.current; if (!v || !v.duration) return;
    if (fitDur) {
      // encaja el vídeo ENTERO dentro de la duración de la escena (acelera/ralentiza un poco)
      v.playbackRate = Math.max(0.5, Math.min(2, v.duration / fitDur));
      const want = Math.min((clamp(lt, 0, fitDur) / fitDur) * v.duration, v.duration - 0.05);
      if (Math.abs(v.currentTime - want) > 0.7) { try { v.currentTime = want; } catch(e){} }
      return;
    }
    const want = lt % v.duration; if (Math.abs(v.currentTime - want) > 0.7) { try { v.currentTime = want; } catch(e){} } }, [lt]);
  return <video ref={ref} src={vsrc(src)} muted loop={!fitDur} autoPlay playsInline preload="auto"
    style={{ width:'100%', height:'100%', objectFit:fit, objectPosition:'center top', display:'block' }} />;
}

/* contenido alto simulado para que se vea el scroll en el preview */
function TallPlaceholder({ title, file }) {
  const rows = [0,1,2,3,4,5,6];
  return (
    <div style={{ width:'100%', background:'#EBF3F8',
      backgroundImage:'repeating-linear-gradient(135deg, rgba(25,146,201,0.05) 0 16px, rgba(25,146,201,0.10) 16px 32px)', padding:'56px 0 70px' }}>
      <div style={{ display:'flex', flexDirection:'column', alignItems:'center', gap:10, margin:'0 auto 46px', padding:'26px 44px', borderRadius:16, width:'fit-content',
        background:'rgba(255,255,255,0.92)', border:'1px solid rgba(25,146,201,0.22)' }}>
        <span style={{ width:28, height:28, borderRadius:7, border:'2.5px solid '+C.blue }}></span>
        <div style={{ fontFamily:FONT, fontWeight:800, fontSize:27, color:C.ink, whiteSpace:'nowrap' }}>{title || 'Captura'}</div>
        <div style={{ fontFamily:MONO, fontSize:17, color:C.blueDeep }}>{file}</div>
      </div>
      {rows.map(i => (
        <div key={i} style={{ height: i%3===0?160:120, margin:'0 90px 38px', borderRadius:14,
          background:'rgba(255,255,255,0.72)', border:'1px solid rgba(25,146,201,0.12)' }}></div>
      ))}
      <div style={{ textAlign:'center', fontFamily:MONO, fontSize:15, color:'rgba(11,27,37,0.4)' }}>// scroll · {file}</div>
    </div>
  );
}

/* pantalla con borde luminoso (glow), con scroll opcional del contenido */
function GlowScreen({ w, h, file, videoFile, title, lt, scrollFrac, dim=0, chrome=true, radius=22, contentScale=1, videoFit='contain', imgFit='contain', fitDur }) {
  const ready = videoFile ? has(videoFile) : has(file);
  const ch = chrome ? FR.chrome : 0;
  const contentH = h - ch;
  const scRef = useRef(null);
  const isScroll = (scrollFrac != null) && !videoFile;
  let ty = 0;
  if (isScroll && scRef.current) ty = -clamp(scrollFrac,0,1) * Math.max(0, scRef.current.scrollHeight - contentH);
  return (
    <div style={{ width:w, height:h, borderRadius:radius, background:'#0B1E2A', position:'relative', overflow:'hidden',
      boxShadow:'0 0 0 1.5px rgba(70,174,220,0.55), 0 0 80px rgba(25,146,201,0.5), 0 0 170px rgba(25,146,201,0.26), 0 50px 110px -24px rgba(0,0,0,0.72)' }}>
      {chrome ? <Chrome /> : null}
      <div style={{ position:'relative', width:'100%', height:contentH, overflow:'hidden', background:'#fff' }}>
        {isScroll ? (
          <div ref={scRef} style={{ position:'absolute', left:0, top:0, width:'100%', transform:`translateY(${ty}px) scale(${contentScale})`, transformOrigin:'top center' }}>
            {ready ? <img src={file} alt={title} style={{ width:'100%', height:'auto', display:'block' }} />
                   : <TallPlaceholder title={title} file={file} />}
          </div>
        ) : (
          <div style={{ position:'absolute', inset:0, transform:`scale(${contentScale})`, transformOrigin:'center center' }}>
            {ready ? (videoFile ? <VideoSlot src={videoFile} lt={lt} fit={videoFit} fitDur={fitDur} />
                : <img src={file} alt={title} style={{ width:'100%', height:'100%', objectFit:imgFit, objectPosition:'center', display:'block' }} />)
              : <Placeholder title={title} file={videoFile || file} video={!!videoFile} />}
          </div>
        )}
      </div>
      {dim > 0 ? <div style={{ position:'absolute', inset:0, background:`rgba(5,16,24,${dim})` }}></div> : null}
    </div>
  );
}

const rigStyle = (t, op) => ({ position:'absolute', left:'50%', top:'50%', width:0, height:0, transformStyle:'preserve-3d', transform:t, opacity:op });

/* HÉROE 3D — variantes: 'solo' (una pantalla grande con scroll+zoom),
   'wall' (central + 2 laterales) y 'pan' (fila de pantallas que recorre la cámara).
   Corta entre varias capturas (files) y hace scroll del contenido. */
function Hero({ lt, dur, file, files, videoFile, title, kicker, capTitle, capSub, cam, dir, variant='solo', scroll=false, videoFit='contain', fitVideo=false, soloH=850, children }) {
  const ent = E.outCubic(clamp(lt / 0.95, 0, 1));
  const d = dir === 'l' ? -1 : 1;
  const p = E.inOutCubic(clamp(lt / (dur || 8), 0, 1));
  const float = Math.sin(lt*0.7)*9;
  const entZ = (1-ent)*-640, entRot = (1-ent)*20*d;
  const list = (files && files.length ? files : [videoFile || file]).filter(Boolean);
  const n = Math.max(list.length, 1);
  const isV = (f) => typeof f === 'string' && f.endsWith('.mp4');
  const cutDur = (dur || 8) / n;
  const idx = clamp(Math.floor(lt / cutDur), 0, n-1);
  const cutLocal = lt - idx*cutDur;
  const cutPop = E.outCubic(clamp(cutLocal/0.5, 0, 1));
  const scrollFrac = E.inOutCubic(clamp((cutLocal-0.4)/Math.max(cutDur-0.9,0.5), 0, 1));
  const cur = list[idx] || list[0];
  const G = (f, w, h, o={}) => <GlowScreen w={w} h={h} file={isV(f)?undefined:f} videoFile={isV(f)?f:undefined}
    title={o.t!=null?o.t:title} lt={lt} dim={o.dim||0} chrome={o.chrome!==false} contentScale={o.cs||1} videoFit={videoFit} imgFit={o.imgFit||'contain'} fitDur={fitVideo?(dur||8):undefined}
    scrollFrac={o.scroll ? (o.sf!=null?o.sf:scrollFrac) : undefined} />;

  let rig;
  if (variant === 'wall') {
    const camZ = lerp(cam?.z0 ?? -150, cam?.z1 ?? 160, p);
    const camRot = lerp(cam?.r0 ?? (6*d), cam?.r1 ?? (-3*d), p) + Math.sin(lt*0.4)*0.6;
    const sideA = list[(idx+1)%n] || cur, sideB = list[(idx+2)%n] || cur;
    rig = (
      <div style={rigStyle(`translateZ(${camZ+entZ}px) translateY(${float}px) rotateY(${camRot+entRot}deg)`, ent)}>
        <div style={{ position:'absolute', transform:'translate(-50%,-50%) translateX(-1030px) translateZ(-470px) rotateY(35deg)' }}>{G(sideA,1040,580,{dim:0.58,chrome:false,t:''})}</div>
        <div style={{ position:'absolute', transform:'translate(-50%,-50%) translateX(1030px) translateZ(-470px) rotateY(-35deg)' }}>{G(sideB,1040,580,{dim:0.58,chrome:false,t:''})}</div>
        <div style={{ position:'absolute', transform:`translate(-50%,-50%) scale(${lerp(0.985,1,cutPop)})` }}>{G(cur,1420,790,{scroll:scroll,cs:lerp(1.04,1.0,cutPop),imgFit:'fill'})}</div>
      </div>
    );
  } else if (variant === 'pan') {
    const SP = 1480;
    // paneo con keyframes: dwell largo en la 2ª captura (-SP) y luego avanza a la 3ª
    const camStops = [[0,180],[0.16,0],[0.34,-SP],[0.66,-SP],[0.85,-(n-1)*SP],[1,-(n-1)*SP]];
    let camX = camStops[0][1];
    for (let k=1;k<camStops.length;k++){
      const [p0,v0]=camStops[k-1], [p1,v1]=camStops[k];
      if (p<=p1){ const f=E.inOutCubic(clamp((p-p0)/(p1-p0),0,1)); camX=lerp(v0,v1,f); break; }
      camX=v1;
    }
    const camRot = Math.sin(lt*0.3)*0.6;
    const panEntZ = (1-ent)*-150; // entrada suave, sin el zoom brusco
    rig = (
      <div style={rigStyle(`translateZ(${-160+panEntZ}px) translateX(${camX}px) translateY(${float}px) rotateY(${camRot}deg)`, ent)}>
        {list.map((f,i)=>(
          <div key={i} style={{ position:'absolute', transform:`translate(-50%,-50%) translateX(${i*SP}px) rotateY(-4deg)` }}>
            {G(f,1340,730,{scroll:scroll && i>0, sf: clamp(p*0.8 - i*0.22, 0, 1)})}
          </div>
        ))}
      </div>
    );
  } else { // solo
    const camZ = lerp(cam?.z0 ?? -110, cam?.z1 ?? 250, p);
    const camRot = lerp(cam?.r0 ?? (7*d), cam?.r1 ?? (-5*d), p) + Math.sin(lt*0.35)*0.7;
    rig = (
      <div style={rigStyle(`translateZ(${camZ+entZ}px) translateY(${float}px) rotateY(${camRot+entRot}deg)`, ent)}>
        <div style={{ position:'absolute', transform:`translate(-50%,-50%) scale(${lerp(0.985,1,cutPop)})` }}>{G(cur,1540,soloH,{scroll:scroll,cs:lerp(1.03,1.0,cutPop)})}</div>
      </div>
    );
  }
  return (
    <>
      <div style={{ position:'absolute', inset:0, perspective:1950, perspectiveOrigin:'50% 46%' }}>{rig}</div>
      <div style={{ position:'absolute', left:0, right:0, bottom:0, height:340, background:'linear-gradient(transparent, rgba(4,13,20,0.0) 25%, rgba(4,13,20,0.6))', pointerEvents:'none' }}></div>
      <div style={{ position:'absolute', left:FR.x, top:FR.y, width:FR.w, height:FR.h, pointerEvents:'none' }}>
        <Caption lt={lt} kicker={kicker} title={capTitle} sub={capSub} />
        {children}
      </div>
    </>
  );
}

/* chip (intro / idiomas) */
function Chip({ children, lt, delay, dark, accent }) {
  const e = P(lt, delay||0, 0.55, E.outBack);
  return (
    <div style={{ opacity: clamp(e,0,1), transform:`translateY(${(1-e)*26}px) scale(${lerp(0.9,1,clamp(e,0,1))})`,
      display:'inline-flex', alignItems:'center', gap:14, padding:'18px 30px', borderRadius:100,
      fontFamily:FONT, fontWeight:700, fontSize:30, whiteSpace:'nowrap',
      background: accent ? C.blue : (dark ? 'rgba(255,255,255,0.07)' : C.white),
      color: accent ? C.white : (dark ? C.white : C.ink),
      border: accent ? '1px solid '+C.blue : (dark ? '1px solid rgba(255,255,255,0.16)' : '1px solid rgba(11,27,37,0.08)'),
      transition:'background .35s ease, color .35s ease, border-color .35s ease',
      boxShadow: dark ? 'none' : '0 14px 30px -14px rgba(11,27,37,0.22)' }}>
      <span style={{ width:11, height:11, borderRadius:6, background: accent ? '#fff' : C.blue,
        transition:'background .35s ease, box-shadow .35s ease',
        boxShadow: accent ? 'none':'0 0 0 5px rgba(25,146,201,0.16)' }}></span>
      {children}
    </div>
  );
}

/* KPI compacto (overlay home) */
function StatPin({ lt, at, x, y, label, value, suffix, accent }) {
  const e = P(lt, at, 1.3, E.outQuint);
  const enter = P(lt, at, 0.55, E.outCubic);
  const disp = (value*e).toFixed(1).replace('.', ',');
  return (
    <div style={{ position:'absolute', left:x, top:y, opacity:enter, transform:`translateY(${(1-enter)*24}px)`,
      background:'rgba(255,255,255,0.96)', borderRadius:16, padding:'20px 26px', minWidth:190,
      boxShadow:'0 22px 48px -18px rgba(8,28,40,0.5)', border:'1px solid rgba(255,255,255,0.6)' }}>
      <div style={{ fontFamily:FONT, fontWeight:700, fontSize:18, letterSpacing:'0.05em', textTransform:'uppercase', color:C.soft }}>{label}</div>
      <div style={{ fontFamily:FONT, fontWeight:800, fontSize:52, lineHeight:1, marginTop:8, color:accent?C.blue:C.ink, letterSpacing:'-0.02em' }}>
        {disp}<span style={{ fontSize:32 }}>{suffix}</span>
      </div>
    </div>
  );
}

/* anillo de reputación (overlay) */
function Gauge({ lt, at, score, x, y }) {
  const e = P(lt, at, 1.5, E.outQuint);
  const enter = P(lt, at, 0.6, E.outCubic);
  const R=120, CIRC=2*Math.PI*R, frac=(score/100)*e, shown=Math.round(score*e);
  return (
    <div style={{ position:'absolute', left:x, top:y, width:300, height:300, opacity:enter,
      transform:`scale(${lerp(0.9,1,enter)})` }}>
      <div style={{ position:'absolute', inset:-18, borderRadius:'50%', background:'rgba(11,27,37,0.55)',
        backdropFilter:'blur(4px)' }}></div>
      <svg width="300" height="300" viewBox="0 0 300 300" style={{ position:'relative', transform:'rotate(-90deg)' }}>
        <circle cx="150" cy="150" r={R} fill="none" stroke="rgba(255,255,255,0.16)" strokeWidth="22" />
        <circle cx="150" cy="150" r={R} fill="none" stroke={C.blueLt} strokeWidth="22" strokeLinecap="round"
          strokeDasharray={CIRC} strokeDashoffset={CIRC*(1-frac)} />
      </svg>
      <div style={{ position:'absolute', inset:0, display:'flex', flexDirection:'column', alignItems:'center', justifyContent:'center' }}>
        <div style={{ fontFamily:FONT, fontWeight:800, fontSize:76, color:'#fff', lineHeight:1 }}>{shown}</div>
        <div style={{ fontFamily:FONT, fontWeight:700, fontSize:20, color:C.softLt, marginTop:4 }}>/ 100</div>
      </div>
    </div>
  );
}

function DriftMarks({ light }) {
  return (
    <div style={{ position:'absolute', inset:0, overflow:'hidden', pointerEvents:'none' }}>
      <img src="assets/acrelia-imago.png" alt="" style={{ position:'absolute', width:540, height:540, top:-170, right:-130, opacity: light ? 0.06 : 0.08 }} />
      <img src="assets/acrelia-imago.png" alt="" style={{ position:'absolute', width:380, height:380, bottom:-150, left:-120, opacity: light ? 0.05 : 0.06 }} />
    </div>
  );
}
function SparkIcon(){
  return (<svg width="26" height="26" viewBox="0 0 24 24" fill="none">
    <path d="M12 2l1.8 5.6L19.5 9.4 13.8 11.2 12 17 10.2 11.2 4.5 9.4 10.2 7.6z" fill="#fff"/>
    <circle cx="19" cy="18" r="2.2" fill="#fff"/></svg>);
}

/* ====================================================== ESCENAS */

function Intro({ lt }) {
  const logoE = P(lt, 0.15, 1.0, E.outBack);
  const appear = [1.6, 2.0, 2.4];
  const hi = [2.9, 3.7, 4.4];
  let active = -1; hi.forEach((s,i)=>{ if (lt >= s) active = i; });
  return (
    <StageBG dark>
      <DriftMarks />
      <div style={{ position:'absolute', inset:0, display:'flex', flexDirection:'column', alignItems:'center', justifyContent:'center', textAlign:'center' }}>
        <img src="assets/acrelia-logo-white.png" alt="Acrelia" style={{ height:120, marginBottom:54,
          opacity:clamp(logoE,0,1), transform:`scale(${lerp(0.6,1,clamp(logoE,0,1))}) translateY(${(1-clamp(logoE,0,1))*20}px)` }} />
        <h1 style={{ ...rv(lt, 0.7, 0.9, E.outCubic, 44), margin:0, fontFamily:FONT, fontWeight:800,
          fontSize:84, lineHeight:1.05, letterSpacing:'-0.03em', color:'#fff' }}>Nueva versión</h1>
        <div style={{ display:'flex', gap:20, marginTop:54, minHeight:82 }}>
          {['Más intuitiva','Más potente','Más rápida'].map((t,i)=>(
            <Chip key={t} lt={lt} delay={appear[i]} dark accent={i===active}>{t}</Chip>
          ))}
        </div>
      </div>
    </StageBG>
  );
}

const STAR_CSS = (() => {
  let parts = [], seed = 20260619;
  const rnd = () => { seed = (seed*1103515245 + 12345) & 0x7fffffff; return seed/0x7fffffff; };
  for (let i=0;i<64;i++){ const x=(rnd()*100).toFixed(1), y=(rnd()*100).toFixed(1),
    s=(rnd()*1.5+0.5).toFixed(1), a=(rnd()*0.45+0.18).toFixed(2);
    parts.push(`radial-gradient(${s}px ${s}px at ${x}% ${y}%, rgba(255,255,255,${a}), transparent)`); }
  return parts.join(',');
})();

function StageBG({ children, dark }) {
  if (dark) return (
    <div style={{ position:'absolute', inset:0, overflow:'hidden',
      background:'radial-gradient(125% 100% at 50% 34%, #12526F 0%, #0A2C40 46%, #051420 100%)' }}>
      <div style={{ position:'absolute', inset:0, backgroundImage:STAR_CSS, opacity:0.55 }}></div>
      <div style={{ position:'absolute', width:1500, height:1500, left:'50%', top:'38%', transform:'translate(-50%,-50%)',
        background:'radial-gradient(circle, rgba(25,146,201,0.34) 0%, rgba(25,146,201,0.0) 60%)' }}></div>
      <div style={{ position:'absolute', inset:0, pointerEvents:'none',
        background:'radial-gradient(130% 120% at 50% 50%, transparent 55%, rgba(4,12,18,0.65) 100%)' }}></div>
      {children}
    </div>
  );
  return (
    <div style={{ position:'absolute', inset:0, background:`linear-gradient(150deg, ${C.mist2} 0%, ${C.mist} 100%)` }}>{children}</div>
  );
}

function Plataforma({ lt }) {
  return (
    <StageBG dark>
      <DriftMarks />
      <Hero lt={lt} dur={6.5} dir="r" variant="pan" scroll files={['assets/s2-overview.png','assets/s2-overview-3.png','assets/s2-overview-2.png']} title="Vista general"
        kicker="Acrelia evoluciona" capTitle="Más intuitiva, potente y rápida" />
    </StageBG>
  );
}

function Usable({ lt }) {
  return (
    <StageBG dark>
      <DriftMarks />
      <Hero lt={lt} dur={5.4} dir="l" variant="solo" files={['assets/s2-usable.png','assets/s2-usable-dark.png']} title="Vista general"
        kicker="Usable y accesible" capTitle="Se adapta a tu forma de trabajar"
        cam={{ z0:230, z1:-150 }} />
    </StageBG>
  );
}

function Crm({ lt }) {
  const tags = [
    { t:'Ordena columnas', at:5.3 },
    { t:'Vistas personalizadas', at:7.3 },
    { t:'Filtros avanzados', at:9.4 },
    { t:'Búsquedas guardadas', at:11.2 },
  ];
  return (
    <StageBG dark>
      <DriftMarks />
      <Hero lt={lt} dur={15.4} dir="r" variant="solo" videoFile="assets/s4-crm.mp4" fitVideo videoFit="cover" title="Listado de contactos"
        kicker="Datos bajo control" capTitle="Gestiona tus contactos como en un CRM"
        cam={{ z0:-140, z1:240 }}>
        <TagStack lt={lt} side="l" top={150} items={tags} />
      </Hero>
    </StageBG>
  );
}

/* editor de campañas */
function Editor({ lt }) {
  const tags = [
    { t:'Más intuitivo', at:4.5 },
    { t:'Más cómodo', at:7.0 },
    { t:'Pantalla completa', at:10.0 },
  ];
  return (
    <StageBG dark>
      <DriftMarks />
      <Hero lt={lt} dur={15.2} dir="l" variant="solo" videoFile="assets/s5-editor.mp4" videoFit="cover" title="Editor de campañas"
        kicker="Editor renovado" capTitle="Crea campañas con más comodidad"
        cam={{ z0:-160, z1:150 }}>
        <TagStack lt={lt} side="l" top={150} items={tags} />
      </Hero>
    </StageBG>
  );
}

function Ia({ lt }) {
  return (
    <StageBG dark>
      <DriftMarks />
      <Hero lt={lt} dur={8.0} dir="l" variant="solo" videoFile="assets/s6-ia.mp4" fitVideo soloH={819} title="Editor con IA"
        kicker="IA integrada" capTitle="Asuntos, textos y traducciones en segundos"
        cam={{ z0:-120, z1:230 }}>
        <TagStack lt={lt} side="r" top={180} items={[
          { t:'Genera asuntos y preencabezados', at:1.6 },
          { t:'Mejora tus textos', at:3.6 },
          { t:'Traduce desde el editor', at:5.6 },
        ]} />
      </Hero>
    </StageBG>
  );
}

function Imagenes({ lt }) {
  return (
    <StageBG dark>
      <DriftMarks />
      <Hero lt={lt} dur={12.4} dir="r" variant="wall" files={['assets/s7-imagenes.png','assets/s7-imagenes-2.png','assets/s7-imagenes-3.png']} title="Editor de imágenes"
        kicker="Editor de imágenes" capTitle="Ajusta tus creatividades de forma sencilla"
        cam={{ z0:150, z1:-120 }}>
      </Hero>
    </StageBG>
  );
}

function Home({ lt }) {
  return (
    <StageBG dark>
      <DriftMarks />
      <Hero lt={lt} dur={15.4} dir="l" variant="solo" videoFile="assets/s8-home.mp4" videoFit="cover" title="Home · estadísticas"
        kicker="Nuevo dashboard" capTitle="Tus métricas clave, siempre a la vista"
        cam={{ z0:-100, z1:210 }}>
        <TagStack lt={lt} side="l" top={150} items={[
          { t:'Principales indicadores', at:1.6 },
          { t:'Evolución por periodos', at:4.0 },
          { t:'Top aperturas y clics', at:9.5 },
        ]} />
      </Hero>
    </StageBG>
  );
}

function Topbar({ lt }) {
  return (
    <StageBG dark>
      <DriftMarks />
      <Hero lt={lt} dur={8.5} dir="r" variant="solo" videoFile="assets/s9-topbar.mp4" videoFit="cover" title="Barra superior"
        kicker="Navegación más eficiente" capTitle="Accede rápido a tus contactos y acciones habituales"
        cam={{ z0:-60, z1:340 }}>
      </Hero>
    </StageBG>
  );
}

function Reputacion({ lt }) {
  return (
    <StageBG dark>
      <DriftMarks />
      <Hero lt={lt} dur={11.06} dir="l" variant="solo" videoFile="assets/s10-reputacion.mp4" fitVideo title="Panel de reputación"
        kicker="Mejora tu reputación" capTitle="Conoce tu puntuación y cómo optimizarla"
        cam={{ z0:-130, z1:200 }}>
        <TagStack lt={lt} side="r" top={250} items={[
          { t:'Puntuación de remitente', at:3.5 },
          { t:'Factores de reputación', at:6.0 },
          { t:'Áreas de mejora', at:8.5 },
        ]} />
      </Hero>
    </StageBG>
  );
}

function Idiomas({ lt }) {
  const nuevos = ['Galego','Euskera','Valencià','Português','Italiano'];
  const previos = ['Español','English','Català','Français'];
  const nt = [2.0, 2.9, 3.8, 4.7, 5.6];
  return (
    <StageBG dark>
      <DriftMarks />
      <div style={{ position:'absolute', left:'50%', top:'46%', width:1300, height:1300, transform:'translate(-50%,-50%)',
        background:'radial-gradient(circle, rgba(25,146,201,0.32), rgba(25,146,201,0) 60%)', pointerEvents:'none' }}></div>
      <div style={{ position:'absolute', inset:0, display:'flex', flexDirection:'column', alignItems:'center', justifyContent:'center', textAlign:'center', padding:'0 110px' }}>
        <div style={{ ...rv(lt,0.1,0.6,E.outCubic,18), display:'flex', alignItems:'center', gap:14, marginBottom:16 }}>
          <span style={{ width:42, height:3, borderRadius:2, background:C.blueLt }}></span>
          <span style={{ fontFamily:FONT, fontWeight:700, fontSize:23, letterSpacing:'0.24em', textTransform:'uppercase', color:C.blueLt }}>Acrelia habla tu idioma</span>
          <span style={{ width:42, height:3, borderRadius:2, background:C.blueLt }}></span>
        </div>
        <h1 style={{ ...rv(lt,0.25,0.7,E.outCubic,30), fontFamily:FONT, fontWeight:800, fontSize:72, letterSpacing:'-0.02em', color:'#fff', margin:'0 0 50px', whiteSpace:'nowrap' }}>
          Ahora en <span style={{ color:C.blueLt }}>5 idiomas más</span>
        </h1>
        <div style={{ display:'flex', flexWrap:'nowrap', gap:18, justifyContent:'center', maxWidth:1820 }}>
          {nuevos.map((t,i)=>{
            const e = clamp(P(lt, nt[i], 0.55, E.outBack), 0, 1);
            const flash = clamp(1 - (lt-nt[i])/0.55, 0, 1);
            const fl = Math.sin((lt+i)*0.9)*4;
            return (
              <div key={t} style={{ opacity:e, transform:`translateY(${(1-e)*32 + fl}px) scale(${lerp(0.7,1,e)})`,
                padding:'20px 34px', borderRadius:100, fontFamily:FONT, fontWeight:800, fontSize:46, color:'#fff', whiteSpace:'nowrap',
                background:`linear-gradient(135deg, ${C.blueLt}, ${C.blueDeep})`,
                boxShadow:`0 0 ${28+flash*70}px rgba(25,146,201,${0.45+flash*0.55}), 0 20px 44px -14px rgba(0,0,0,0.6), inset 0 0 0 1.5px rgba(255,255,255,0.28)` }}>{t}</div>
            );
          })}
        </div>
        <div style={{ ...rv(lt,0.5,0.6,E.outCubic,16), marginTop:56, display:'flex', alignItems:'center', gap:14, flexWrap:'wrap', justifyContent:'center' }}>
          {previos.map(t=>(
            <span key={t} style={{ padding:'11px 24px', borderRadius:100, fontFamily:FONT, fontWeight:700, fontSize:25,
              background:'rgba(255,255,255,0.09)', color:'rgba(255,255,255,0.82)', border:'1px solid rgba(255,255,255,0.2)' }}>{t}</span>
          ))}
        </div>
      </div>
    </StageBG>
  );
}

/* cierre: montaje rápido de capturas */
function Cierre({ lt }) {
  const shots = [
    { f:'assets/final-1.png', t:'Captura final 1' }, { f:'assets/final-2.png', t:'Captura final 2' },
    { f:'assets/final-3.png', t:'Captura final 3' }, { f:'assets/final-4.png', t:'Captura final 4' },
    { f:'assets/final-5.png', t:'Captura final 5' }, { f:'assets/final-6.png', t:'Captura final 6' },
  ];
  const CUT = 1.7, START = 0.3, SLIDE = 0.66;
  const N = shots.length;
  const idx = clamp(Math.floor((lt - START) / CUT), 0, N - 1);
  const localCut = (lt - START) - idx*CUT;
  const slideP = E.inOutCubic(clamp((localCut - (CUT - SLIDE)) / SLIDE, 0, 1));
  const pos = clamp(idx + (idx < N-1 ? slideP : 0), 0, N-1);
  const endMontage = N*CUT + START;
  const showText = lt > endMontage - 0.3;
  const AR = 2.02, CW = 1720, CH = Math.round(CW/AR);
  return (
    <div style={{ position:'absolute', inset:0, background:'radial-gradient(125% 100% at 50% 40%, #114B66 0%, #0A2A3C 48%, #051420 100%)', display:'flex', alignItems:'center', justifyContent:'center' }}>
      <DriftMarks />
      {/* montaje — carrete deslizante continuo */}
      <div style={{ position:'absolute', inset:0,
        display:'flex', alignItems:'center', justifyContent:'center',
        opacity: showText ? clamp(1-(lt-endMontage)*1.6,0,1) : 1 }}>
        <div style={{ width:CW, height:CH, borderRadius:18, overflow:'hidden', background:'#0B1E2A',
          boxShadow:'0 0 0 1.5px rgba(70,174,220,0.5), 0 0 90px rgba(25,146,201,0.42), 0 60px 120px -30px rgba(0,0,0,0.62)' }}>
          <div style={{ display:'flex', width:CW*N, height:'100%', transform:`translateX(${-pos*CW}px)` }}>
            {shots.map((sh,i)=>(
              <div key={i} style={{ flex:'none', width:CW, height:'100%', overflow:'hidden', background:'#0B1E2A' }}>
                {shot(sh.f)
                  ? <img src={shot(sh.f)} alt="" style={{ width:'100%', height:'100%', objectFit:'cover', objectPosition:'center top', display:'block' }} />
                  : <div style={{ width:'100%', height:'100%', display:'flex', alignItems:'center', justifyContent:'center' }}><Placeholder title={sh.t} file={sh.f} /></div>}
              </div>
            ))}
          </div>
        </div>
      </div>
      {/* texto final */}
      {showText ? (
        <div style={{ position:'absolute', inset:0, display:'flex', flexDirection:'column', alignItems:'center', justifyContent:'center',
          textAlign:'center', background:'radial-gradient(circle at 50% 50%, rgba(11,27,37,0.55), rgba(11,27,37,0.92))' }}>
          <div style={{ ...rv(lt, endMontage+0.15, 0.8, E.outBack, 26), marginBottom:48, display:'inline-flex', alignItems:'center', gap:16,
            padding:'18px 44px', borderRadius:100, background:'linear-gradient(120deg, #1992C9, #0F6E97)',
            boxShadow:'0 0 0 1.5px rgba(127,208,240,0.4), 0 22px 50px -16px rgba(25,146,201,0.8)' }}>
            <span style={{ fontFamily:FONT, fontWeight:800, fontSize:44, letterSpacing:'0.01em', color:'#fff', whiteSpace:'nowrap' }}>Lanzamiento en <span style={{ color:'#EAF7FE' }}>Julio</span></span>
          </div>
          <h1 style={{ ...rv(lt, endMontage+0.5, 0.8, E.outCubic, 36), margin:0, fontFamily:FONT, fontWeight:800,
            fontSize:96, lineHeight:1.02, letterSpacing:'-0.025em', color:'#fff' }}>Muchas más mejoras<br/>te esperan</h1>
        </div>
      ) : null}
    </div>
  );
}

function Tagline({ lt }) {
  const e = P(lt, 0.15, 1.0, E.outCubic);
  return (
    <div style={{ position:'absolute', inset:0, background:`radial-gradient(120% 130% at 50% 42%, #FFFFFF 0%, ${C.mist} 82%)`,
      display:'flex', flexDirection:'column', alignItems:'center', justifyContent:'center', textAlign:'center' }}>
      <DriftMarks light />
      <img src="assets/acrelia-logo.png" alt="Acrelia" style={{ height:96, marginBottom:48, opacity:e, transform:`translateY(${(1-e)*26}px)` }} />
      <h1 style={{ ...rv(lt,0.5,0.9,E.outCubic,32), margin:0, fontFamily:FONT, fontWeight:800, fontSize:72,
        lineHeight:1.1, letterSpacing:'-0.02em', color:C.ink, maxWidth:1200, textWrap:'balance' }}>
        El email marketing que <span style={{ color:C.blue }}>evoluciona contigo</span>
      </h1>
    </div>
  );
}

const SCENES = {
  intro:Intro, plataforma:Plataforma, usable:Usable, crm:Crm, editor:Editor, ia:Ia,
  imagenes:Imagenes, home:Home, topbar:Topbar, reputacion:Reputacion, idiomas:Idiomas, cierre:Cierre, tagline:Tagline,
};

/* ========================================== contenedor + reloj */

function AcreliaVideo() {
  const W = 1920, H = 1080;
  const audioRef = useRef(null), musicRef = useRef(null), lastT = useRef(0), wrapRef = useRef(null), hideTimer = useRef(null);
  const posterRef = useRef(false);
  const clockRef = useRef({ base: 0, anchor: 0 }), playingRef = useRef(false);
  const [t, setT] = useState(() => { let init = 0;
    try {
      const sp = new URLSearchParams(location.search);
      const qp = parseFloat(sp.get('start'));
      if (isFinite(qp)) { init = qp; posterRef.current = true; }
      else { const v = parseFloat(localStorage.getItem('acrelia_video_t') || '0'); if (isFinite(v)) init = v; }
    } catch(e){}
    lastT.current = init; clockRef.current.base = init; return init; });
  const [playing, setPlaying] = useState(false);
  const [scale, setScale] = useState(1);
  const [blobN, setBlobN] = useState(() => ALL_VIDEOS.filter(p => BLOB_CACHE[p]).length);
  const [warmDone, setWarmDone] = useState(false);
  // descarga los vídeos a memoria (blob) una sola vez: reproducción sin tirones ni pantallas en blanco
  useEffect(() => {
    let alive = true;
    (async () => {
      for (const p of ALL_VIDEOS) {
        if (BLOB_CACHE[p]) continue;
        try {
          const r = await fetch(p); if (!r.ok) continue;
          const url = URL.createObjectURL(await r.blob());
          if (!alive) { URL.revokeObjectURL(url); return; }
          BLOB_CACHE[p] = url; setBlobN(n => n + 1);
        } catch (e) { /* sigue con el resto */ }
      }
      if (alive) setWarmDone(true);   // se han intentado todos (aunque alguno falle)
    })();
    return () => { alive = false; };
  }, []);
  const [showUI, setShowUI] = useState(true);
  const [musicOn, setMusicOn] = useState(true);
  const [recMode, setRecMode] = useState(false);
  const [recArmed, setRecArmed] = useState(false);
  const [recCount, setRecCount] = useState(0);
  const recPlayTimer = useRef(null);
  const recScheduled = useRef(false);
  const recIv = useRef(null);

  useEffect(() => { MEDIA.playing = playing; playingRef.current = playing; }, [playing]);

  useEffect(() => {
    const fit = () => { const el = wrapRef.current; if (!el) return; setScale(Math.min(el.clientWidth / W, el.clientHeight / H)); };
    fit(); const ro = new ResizeObserver(fit); if (wrapRef.current) ro.observe(wrapRef.current);
    window.addEventListener('resize', fit); return () => { ro.disconnect(); window.removeEventListener('resize', fit); };
  }, []);

  // El audio es la fuente del reloj mientras avanza (sonido continuo, nunca se reescribe currentTime).
  // Solo si el audio se queda bloqueado, el reloj real mueve la imagen y se reintenta reproducir.
  useEffect(() => {
    let raf, prevAudio = -1, stuck = 0, lastRetry = 0;
    const stop = (nt) => { playingRef.current = false; MEDIA.playing = false; setPlaying(false);
      const a = audioRef.current; if (a) a.pause(); musicRef.current && musicRef.current.pause(); lastT.current = nt; setT(nt); };
    const loop = () => {
      if (playingRef.current) {
        const a = audioRef.current, c = clockRef.current;
        const wallT = c.base + (performance.now() - c.anchor) / 1000;
        if (a && !a.paused) {
          const at = a.currentTime;
          if (Math.abs(at - prevAudio) > 0.004) stuck = 0; else stuck++;
          prevAudio = at;
          if (stuck < 24) { // audio sano -> manda el audio, sin tocar currentTime
            clockRef.current = { base: at, anchor: performance.now() };
            if (at >= DUR - 0.04) { stop(DUR); } else { lastT.current = at; setT(at); }
            raf = requestAnimationFrame(loop); return;
          }
        }
        // fallback: audio ausente/bloqueado -> avanza la imagen y reintenta el sonido (sin reescribir currentTime)
        if (wallT >= DUR) { stop(DUR); } else { lastT.current = wallT; setT(wallT);
          if (a && a.paused) { const now = performance.now(); if (now - lastRetry > 700) { lastRetry = now;
            const p = a.play(); if (p && p.catch) p.catch(()=>{}); } } }
      } else { prevAudio = -1; stuck = 0; }
      raf = requestAnimationFrame(loop);
    };
    raf = requestAnimationFrame(loop); return () => cancelAnimationFrame(raf);
  }, []);

  useEffect(() => {
    const a = audioRef.current; if (!a) return;
    const onMeta = () => { const v = parseFloat(localStorage.getItem('acrelia_video_t') || '0');
      if (isFinite(v) && v > 0 && v < a.duration) { a.currentTime = v; if (musicRef.current) musicRef.current.currentTime = v; } };
    if (a.readyState >= 1) onMeta(); else a.addEventListener('loadedmetadata', onMeta);
    return () => a.removeEventListener('loadedmetadata', onMeta);
  }, []);

  useEffect(() => { const id = setInterval(() => { localStorage.setItem('acrelia_video_t', String(lastT.current)); }, 500); return () => clearInterval(id); }, []);

  const startAudio = (el, to) => { if (!el) return; const needSeek = Math.abs(el.currentTime - to) > 0.3;
    const p = el.play();
    if (p && p.then) p.then(() => { if (needSeek) { try { el.currentTime = to; } catch(e){} } }).catch(()=>{});
    else if (needSeek) { try { el.currentTime = to; } catch(e){} } };
  const play = useCallback(() => {
    let start = lastT.current >= DUR ? 0 : lastT.current;
    if (posterRef.current) { start = 0; posterRef.current = false; }
    clockRef.current = { base: start, anchor: performance.now() };
    lastT.current = start; setT(start);
    playingRef.current = true; MEDIA.playing = true; setPlaying(true);
    startAudio(audioRef.current, start);
    if (musicRef.current && musicOn) { musicRef.current.volume = 0.16; startAudio(musicRef.current, start); }
  }, [musicOn]);
  const pause = useCallback(() => { audioRef.current && audioRef.current.pause(); musicRef.current && musicRef.current.pause();
    playingRef.current = false; MEDIA.playing = false; setPlaying(false); }, []);
  const toggle = useCallback(() => { if (playingRef.current) pause(); else play(); }, [play, pause]);
  const seek = useCallback((time) => { const tt = clamp(time, 0, DUR); posterRef.current = false;
    clockRef.current = { base: tt, anchor: performance.now() }; lastT.current = tt; setT(tt);
    const a = audioRef.current; if (a) { try { a.currentTime = tt; } catch(e){} }
    if (musicRef.current) { try { musicRef.current.currentTime = tt; } catch(e){} } }, []);

  useEffect(() => { const a = audioRef.current; if (!a) return;
    const onEnd = () => { setPlaying(false); musicRef.current && musicRef.current.pause(); };
    a.addEventListener('ended', onEnd); return () => a.removeEventListener('ended', onEnd); }, []);

  // REC: muestra el preloader y, tras cargar, hace cuenta atrás de 10 s antes de arrancar (para iniciar la captura)
  useEffect(() => {
    if (!recArmed) return;
    const ready = blobN >= ALL_VIDEOS.length || warmDone;
    if (ready) {
      if (recScheduled.current) return;
      recScheduled.current = true;
      setRecCount(-1);   // fase "Preparando grabación" (sin cuenta atrás todavía)
      recPlayTimer.current = setTimeout(() => {
        setRecCount(10);   // empieza la cuenta atrás tras 3 s
        recIv.current = setInterval(() => setRecCount(c => (c > 1 ? c - 1 : 0)), 1000);
        recPlayTimer.current = setTimeout(() => {
          clearInterval(recIv.current); recScheduled.current = false;
          setRecArmed(false);   // oculta el preloader (fade 0.6 s)
          // arranca el vídeo SOLO cuando el preloader ya se ha desvanecido del todo
          recPlayTimer.current = setTimeout(() => { if (!playingRef.current) play(); }, 800);
        }, 10000);
      }, 3000);
      return;
    }
    const fallback = setTimeout(() => { recScheduled.current = false; setRecArmed(false); if (!playingRef.current) play(); }, 60000);
    return () => clearTimeout(fallback);
  }, [recArmed, blobN, warmDone, play]);

  useEffect(() => {
    const onKey = (e) => { if (e.code === 'Space') { e.preventDefault(); toggle(); }
      else if (e.code === 'ArrowRight') seek((audioRef.current?.currentTime||0) + 5);
      else if (e.code === 'ArrowLeft') seek((audioRef.current?.currentTime||0) - 5);
      else if (e.key === '0') seek(0);
      else if (e.key === 'Escape') { clearTimeout(recPlayTimer.current); clearInterval(recIv.current); recScheduled.current = false; setRecArmed(false); setRecMode(false); setShowUI(true); } };
    window.addEventListener('keydown', onKey); return () => window.removeEventListener('keydown', onKey);
  }, [toggle, seek]);

  const bumpUI = useCallback(() => { setShowUI(true); clearTimeout(hideTimer.current);
    if (playing) hideTimer.current = setTimeout(() => setShowUI(false), 2600); }, [playing]);
  useEffect(() => { bumpUI(); }, [playing, bumpUI]);

  const fmt = (x) => { x = Math.max(0, x); const m = Math.floor(x/60), s = Math.floor(x%60); return m + ':' + String(s).padStart(2,'0'); };
  const fmtMs = (x) => { x = Math.max(0, x); const m = Math.floor(x/60), s = Math.floor(x%60), d = Math.floor((x*10)%10); return m + ':' + String(s).padStart(2,'0') + '.' + d; };

  const dispT = posterRef.current ? 0 : t;
  return (
    <div ref={wrapRef} onMouseMove={bumpUI} data-screen-label={'t=' + fmt(t)}
      style={{ position:'fixed', inset:0, background:'#050d12', overflow:'hidden', display:'flex', alignItems:'center', justifyContent:'center',
        cursor: (recMode || (playing && !showUI)) ? 'none' : 'default' }}>
      <div style={{ width:W, height:H, flex:'none', position:'relative', transform:`scale(${scale})`, transformOrigin:'center center', background:C.navy, overflow:'hidden' }}>
        {TL.map(({ key, s, e }) => {
          const fade = 0.42;
          if (t < s - fade - 0.9 || t > e + 0.25) return null;
          const op = Math.min(clamp((t - (s - fade)) / fade, 0, 1), clamp((e - t) / fade, 0, 1));
          const Comp = SCENES[key];
          return (
            <div key={key} style={{ position:'absolute', inset:0, opacity:op, transform:`scale(${lerp(1.012,1,op)})`, willChange:'opacity, transform' }}>
              <Comp lt={t - s} />
            </div>
          );
        })}
        <div style={{ position:'absolute', inset:0, pointerEvents:'none', boxShadow:'inset 0 0 220px rgba(5,13,18,0.22)' }}></div>
      </div>

      {(!playing && t < 0.4 && !recMode) && (
        <button onClick={play} aria-label="Reproducir"
          style={{ position:'absolute', width:118, height:118, borderRadius:60, border:'none', background:'rgba(25,146,201,0.92)',
            cursor:'pointer', display:'flex', alignItems:'center', justifyContent:'center', boxShadow:'0 24px 60px -16px rgba(0,0,0,0.6)', backdropFilter:'blur(4px)' }}>
          <span style={{ marginLeft:8, borderStyle:'solid', borderWidth:'26px 0 26px 42px', borderColor:'transparent transparent transparent #fff' }}></span>
        </button>
      )}

      <div style={{ position:'absolute', left:0, right:0, bottom:0, padding:'30px 40px 26px', background:'linear-gradient(transparent, rgba(5,13,18,0.82))',
        opacity: (showUI && !recMode) ? 1 : 0, transition:'opacity .4s', pointerEvents: (showUI && !recMode) ? 'auto' : 'none', display:'flex', alignItems:'center', gap:20 }}>
        <button onClick={toggle} style={ctrlBtn}>
          {playing ? <span style={{ display:'flex', gap:6 }}><i style={bar}></i><i style={bar}></i></span>
            : <span style={{ marginLeft:4, borderStyle:'solid', borderWidth:'9px 0 9px 15px', borderColor:'transparent transparent transparent #fff' }}></span>}
        </button>
        <span style={{ fontFamily:MONO, fontSize:16, color:'#fff', minWidth:66 }}>{fmtMs(dispT)}</span>
        <input type="range" min={0} max={DUR} step={0.05} value={dispT} onChange={(ev)=>seek(parseFloat(ev.target.value))}
          style={{ flex:1, accentColor:C.blue, height:6, cursor:'pointer' }} />
        <span style={{ fontFamily:MONO, fontSize:16, color:'rgba(255,255,255,0.6)', minWidth:52 }}>{fmt(DUR)}</span>
        <button onClick={()=>{ setMusicOn(m=>{ const nv=!m; if(musicRef.current){ if(nv && playing){musicRef.current.volume=0.16; musicRef.current.play().catch(()=>{});} else musicRef.current.pause(); } return nv; }); }}
          style={{ ...ctrlBtn, width:44, height:44, fontFamily:FONT, fontSize:15, fontWeight:700 }}>{musicOn ? '♪' : '✕'}</button>
        <button onClick={()=>{ const el = wrapRef.current;
            if (window.self !== window.top) { try { window.parent.postMessage({ type:'acrelia-fs-toggle' }, '*'); } catch(e){} return; }
            if (document.fullscreenElement) { document.exitFullscreen().catch(()=>{}); }
            else if (el && el.requestFullscreen) { el.requestFullscreen().catch(()=>{}); } }}
          aria-label="Pantalla completa" title="Pantalla completa"
          style={{ ...ctrlBtn, width:44, height:44 }}>
          <span style={{ fontSize:20, lineHeight:1 }}>⛶</span>
        </button>
      </div>

      <audio ref={audioRef} src={AUDIO_SRC} preload="auto" />
      <audio ref={musicRef} src={MUSIC_SRC} loop preload="auto" />

      {/* preloader: descarga de vídeos a memoria */}
      <div style={{ position:'absolute', inset:0, background:'#050d12', zIndex:50,
        display:'flex', flexDirection:'column', alignItems:'center', justifyContent:'center', gap:30,
        opacity: 0, pointerEvents: 'none',
        transition:'opacity .6s ease' }}>
        <div style={{ width:54, height:54, borderRadius:'50%', border:'4px solid rgba(70,174,220,0.18)',
          borderTopColor:'#1992C9', animation:'acrelia-spin 0.85s linear infinite' }}></div>
        <div style={{ fontFamily:'Manrope, sans-serif', fontWeight:700, fontSize:26, color:'#EAF4FA', letterSpacing:'-0.01em' }}>
          {recArmed ? 'Preparando grabación' : 'Preparando vídeo'}
        </div>
        <div style={{ fontFamily:'Manrope, sans-serif', fontWeight:600, fontSize:16, color:'rgba(234,244,250,0.55)' }}>
          {(recArmed && (blobN >= ALL_VIDEOS.length || warmDone))
            ? (recCount < 0 ? 'Listo' : recCount > 0 ? 'Empieza en ' + recCount + ' s…' : 'Arrancando…')
            : <span>Cargando clips&nbsp;&nbsp;{blobN} / {ALL_VIDEOS.length}</span>}
        </div>
        <div style={{ width:260, height:5, borderRadius:3, background:'rgba(70,174,220,0.16)', overflow:'hidden' }}>
          <div style={{ width:((recArmed && (blobN >= ALL_VIDEOS.length || warmDone)) ? (recCount < 0 ? 0 : (10-recCount)/10) : blobN/ALL_VIDEOS.length)*100+'%', height:'100%', background:'#1992C9', borderRadius:3, transition:'width .4s ease' }}></div>
        </div>
      </div>
    </div>
  );
}

const ctrlBtn = { width:50, height:50, borderRadius:25, border:'1px solid rgba(255,255,255,0.18)', background:'rgba(255,255,255,0.10)',
  cursor:'pointer', display:'flex', alignItems:'center', justifyContent:'center', color:'#fff', flex:'none' };
const bar = { width:5, height:18, background:'#fff', borderRadius:1, display:'block' };

if (typeof module !== 'undefined' && module.exports) module.exports = { AcreliaVideo };
window.AcreliaVideo = AcreliaVideo;
