// === Escena 03 — "El mismo gesto, en todas partes" ===  [cierre + ECO de La Única Cosa]
// Tres fichas distintas (cliente, pedido, artículo) y el MISMO clic derecho en el MISMO sitio
// relativo de cada una: la repetición exacta ES el argumento. Después, las dos cosas que NO
// existen —«Pantalla de auditoría» e «Informe de cambios»— entran y se tachan en GRIS (no en rojo:
// no son un error, es que no están). Y el remate: de cada etiqueta BAJA un conector hasta su
// mini-diálogo, porque el historial vive debajo del dato. Cierra en eco, pantalla fija.
//
// COLUMNA SEGURA 4:5 (restricción propia de esta pieza): todo lo que porta información vive entre
// x=568 y x=1352. Aquí: fichas 576..1000 · mini-diálogos 1020..1348 · fantasmas 650..1270 · eco
// centrado. Fuera de x=528..1392 no hay ni un dato: solo el degradado del SlideCanvas.
//
// El diálogo NO se repinta: es <DialogoHistorial/> de scenes/historial.jsx, a escala 0.42 y sin
// ningún beat interno (sale en estado final). Lo único que la escena le anima es su aparición.
function EscenaHC03({ t }){
  // LOCUCIÓN (audio/escena-03.mp3): el mismo clic derecho, en la ficha de un cliente → el mismo, en
  // un pedido → el mismo, en el artículo de antes → no hay una pantalla de auditoría que abrir, ni
  // un informe que encargar → el historial vive debajo del dato, donde ocurrió → la próxima vez que
  // un precio no cuadre, no preguntes a nadie → ¿quién ha tocado esto? → clic derecho en el nombre
  // del campo.
  const T = {
    cliente:   0.3,  // "El mismo clic derecho" → ficha CLIENTE entra + anillo + mini-menú + mini-diálogo
    pedido:    3.3,  // "El mismo, en un pedido" → ficha PEDIDO, mismo gesto y mismo sitio; la 1 al 0.45
    articulo:  6.5,  // "en el artículo de antes" → ficha ARTÍCULO; su mini-diálogo es el ya visto
    auditoria: 9.3,  // "pantalla de auditoría" → tarjeta fantasma 1, tachada con línea GRIS que se dibuja
    informe:   11.3,  // "un informe que encargar" → tarjeta fantasma 2, tachada igual (una por frase)
    debajo:   15.0,  // "debajo del dato" → de cada etiqueta baja un conector, cascada +0.15 s
    centro:   20.3,  // "no preguntes a nadie" → fichas 1 y 2 al 0.35; el artículo al centro, grande
    eco1:     21.9,  // "¿Quién ha tocado esto" → todo al 0.2; ECO mitad 1
    eco2:     23.8,  // "Clic derecho en el nombre del campo" → ECO mitad 2 + logo; PANTALLA FIJA
  };

  // ── Geometría de la columna segura ──────────────────────────────────────────────────────────
  const FICHA_X = 576, FICHA_W = 424, FICHA_H = 156;   // ficha 576..1000
  const FILA_Y  = [118, 348, 578];                     // tres unidades escalonadas
  const DLG_X   = 1020, DLG_ESC = 0.42, DLG_DY = 82;   // 780*0.42 = 328 → 1020..1348
  const CODO_DY = 96;                                  // el conector entra por el flanco del diálogo
  const ETQ_DX  = 34, ETQ_DY = 130;                    // punto del que arranca el conector (bajo la etiqueta)
  const PUNT_DX = 70, PUNT_DY = 112;                   // punto del puntero: encima de la ETIQUETA

  // Datos: tres registros distintos, tres campos personalizados distintos. Los valores van CRUDOS
  // (sin € y sin separadores) porque el diálogo pinta el valor tal como está guardado; en los campos
  // de relación lo que se ve es el NOMBRE del registro relacionado, que es justo lo que traduce el
  // servidor. La ficha 3 es la de la escena 1 y 2: mismo registro, mismo campo, mismas 3 entradas.
  // El REPARTO no cambia entre escenas: Marta Ruiz y Javier Sanz son las mismas dos personas que la
  // escena 01 enseña con cursor, cara y color, y que la 02 firma en el historial. Misma foto local y
  // mismo anillo (PRES_RUEDA, la constante que tiñe sus cursores) también aquí, aunque a escala 0,42
  // la cara sea un botón de 17 px: lo que el ojo tiene que registrar no es el rostro, es que es LA
  // MISMA persona de antes. Un avatar con inicial aquí y con foto allí rompería eso sin ganar nada.
  const AVATAR = {
    marta:  '/_engine/assets/avatares/marta.jpg',
    javier: '/_engine/assets/avatares/javier.jpg',
  };
  const UNIDADES = [
    { seccion:'CLIENTES', registro:'Talleres Marbán, S.L.', campo:'Tarifa aplicada', valor:'Tarifa mayorista',
      valorSize:21, callback:false,
      entradas:[
        { grupo:'Hoy', inicial:'J', autor:'Javier Sanz', fecha:'16:20', antes:'Tarifa general', despues:'Tarifa mayorista',
          foto:AVATAR.javier, borde:PRES_RUEDA.verde },
      ] },
    { seccion:'PEDIDOS', registro:'P-2026-000418', campo:'Transportista', valor:'Envíos Bardají',
      valorSize:21, callback:false,
      entradas:[
        { grupo:'Hoy', inicial:'M', autor:'Marta Ruiz', fecha:'17:35', antes:'Transportes Nadal', despues:'Envíos Bardají',
          foto:AVATAR.marta, borde:PRES_RUEDA.azul },
      ] },
    // PERSONA CONTINUA: es la MISMA ficha de las escenas 01 y 02, así que la sección se llama igual
    // que allí — «Productos», que es el nombre que pinta <FichaProducto/> en su cabecera. Decía
    // «ARTÍCULOS» y el mismo registro no puede vivir en dos secciones distintas dentro de la pieza.
    { seccion:'PRODUCTOS', registro:'Compresor 50 litros', campo:'Precio de venta', valor:'990',
      valorSize:30, callback:true,
      entradas:[
        { grupo:'Hoy',         inicial:'M', autor:'Marta Ruiz',  fecha:'18:02',        antes:'1290', despues:'990',
          foto:AVATAR.marta,  borde:PRES_RUEDA.azul  },
        { grupo:'Ayer',        inicial:'J', autor:'Javier Sanz', fecha:'12 ago 09:41', antes:'1350', despues:'1290',
          foto:AVATAR.javier, borde:PRES_RUEDA.verde },
        { grupo:'Esta semana', inicial:'M', autor:'Marta Ruiz',  fecha:'8 ago 11:15',  antes:'1200', despues:'1350',
          foto:AVATAR.marta,  borde:PRES_RUEDA.azul  },
      ] },
  ];
  const ANCLA = [T.cliente, T.pedido, T.articulo];

  // ── Curvas globales ─────────────────────────────────────────────────────────────────────────
  // Velo mientras están las fantasmas: lo que no existe pasa delante y lo real se atenúa; al decir
  // «debajo del dato» la composición vuelve entera.
  const veloFantasma = lerp(
    lerp(1, 0.55, Easing.easeOutCubic(clamp((t - T.auditoria)/0.5, 0, 1))),
    1, Easing.easeOutCubic(clamp((t - T.debajo)/0.4, 0, 1))
  );
  const dimEco = Easing.easeOutCubic(clamp((t - T.eco1)/0.5, 0, 1));   // todo al 0.2 en el eco

  // Opacidad de cada unidad (ficha + conector + mini-diálogo)
  const opUnidad = (i) => {
    if (t < ANCLA[i]) return 0;
    let op = 1;
    for (let j = i + 1; j < ANCLA.length; j++){          // entra una posterior → esta se atenúa
      if (t >= ANCLA[j]) op = Math.min(op, lerp(1, 0.45, Easing.easeOutCubic(clamp((t - ANCLA[j])/0.4, 0, 1))));
    }
    op = lerp(op, 1, Easing.easeOutCubic(clamp((t - T.debajo)/0.4, 0, 1)));   // las tres, iguales
    if (i < 2) op = lerp(op, 0.35, Easing.easeOutCubic(clamp((t - T.centro)/0.5, 0, 1)));
    op = lerp(op, 0.2, dimEco);
    return op * veloFantasma;
  };

  // El artículo vuelve al centro y grande (solo la ficha: el 990 es lo que tiene que leerse).
  // 424×156 a escala 1.5 = 636×234, centrado en x=960 → left 642 (dentro de 568..1352).
  const heroP  = Easing.easeOutCubic(clamp((t - T.centro)/0.7, 0, 1));
  const heroS  = lerp(1, 1.5, heroP);
  const heroDX = lerp(0, 66, heroP);
  const heroDY = lerp(0, -225, heroP);
  const trans = (i) => (i === 2 ? { dx:heroDX, dy:heroDY, s:heroS } : { dx:0, dy:0, s:1 });

  // Puntero: cae sobre la ETIQUETA de cada ficha, en el mismo sitio relativo, y se retira antes de
  // las fantasmas. El anillo de clic derecho lo pone <AnilloClicDerecho/>, no el cursor.
  const cursorFrames = [
    { t:0,                  x:1150, y:120 },
    { t:T.cliente  + 0.45,  x:FICHA_X+PUNT_DX, y:FILA_Y[0]+PUNT_DY },
    { t:T.pedido   - 0.35,  x:FICHA_X+PUNT_DX, y:FILA_Y[0]+PUNT_DY },
    { t:T.pedido   + 0.45,  x:FICHA_X+PUNT_DX, y:FILA_Y[1]+PUNT_DY },
    { t:T.articulo - 0.35,  x:FICHA_X+PUNT_DX, y:FILA_Y[1]+PUNT_DY },
    { t:T.articulo + 0.45,  x:FICHA_X+PUNT_DX, y:FILA_Y[2]+PUNT_DY },
    { t:T.auditoria- 0.40,  x:FICHA_X+PUNT_DX, y:FILA_Y[2]+PUNT_DY },
    { t:T.auditoria+ 0.50,  x:1150, y:1010 },
  ];
  const opCursor = clamp((t - 0.15)/0.3, 0, 1) * (1 - clamp((t - T.auditoria)/0.4, 0, 1));

  // ── Ficha en miniatura: sección · registro, y debajo ETIQUETA + cuadro con el valor ─────────
  const FichaMini = ({ u, i }) => {
    const p = clamp((t - ANCLA[i])/0.5, 0, 1);
    const e = Easing.easeOutBack(p);
    const g = trans(i);
    return (
      <div style={{
        position:'absolute', left:FICHA_X, top:FILA_Y[i], width:FICHA_W, height:FICHA_H,
        transform:`translate(${g.dx}px, ${g.dy}px) scale(${g.s})`, transformOrigin:'0% 0%',
      }}>
        <div style={{
          width:'100%', height:'100%', position:'relative',
          transform:`translateY(${(1-e)*18}px) scale(${lerp(0.9, 1, e)})`, transformOrigin:'50% 50%',
          opacity:clamp(p*1.6, 0, 1),
          background:'#fff', borderRadius:14,
          border:`${u.callback ? 2 : 1}px solid ${u.callback ? '#0085FF' : '#E2E8F0'}`,
          boxShadow: u.callback
            ? '0 22px 52px rgba(0,133,255,0.18), 0 0 0 4px rgba(0,133,255,0.08)'
            : '0 18px 44px rgba(15,23,42,0.10)',
        }}>
          <div className="f-plex" style={{position:'absolute', left:20, top:16, fontSize:15, fontWeight:600,
                                          letterSpacing:'0.14em', color:'#94A3B8'}}>{u.seccion}</div>
          <div className="f-plex" style={{position:'absolute', left:20, top:38, fontSize:25, fontWeight:600,
                                          color:'#0F172A', whiteSpace:'nowrap'}}>{u.registro}</div>

          {/* LA ETIQUETA: de aquí cuelga el menú contextual en el producto, y de aquí baja el conector */}
          <div className="f-plex" style={{position:'absolute', left:20, top:98, fontSize:23, fontWeight:500,
                                          color:'#334155', whiteSpace:'nowrap'}}>{u.campo}</div>

          {/* el cuadro del valor: es el sitio donde NO va el clic derecho */}
          <div style={{position:'absolute', left:196, top:90, width:208, height:50, borderRadius:9,
                       background:'#F8FAFC', border:'1px solid #CBD5E1',
                       display:'flex', alignItems:'center', paddingLeft:14, boxSizing:'border-box'}}>
            <span className="f-plex" style={{fontSize:u.valorSize, fontWeight:600, color:'#0F172A', whiteSpace:'nowrap'}}>
              {u.valor}
            </span>
          </div>
        </div>
      </div>
    );
  };

  // ── Conector etiqueta → mini-diálogo: baja del dato y entra por el flanco del historial ─────
  const Conector = ({ i }) => {
    const p = Easing.easeOutCubic(clamp((t - T.debajo - i*0.15)/0.5, 0, 1));
    if (p <= 0) return null;
    const g = trans(i);
    const ax = FICHA_X + g.dx + ETQ_DX*g.s;
    const ay = FILA_Y[i] + g.dy + ETQ_DY*g.s;
    const y1 = FILA_Y[i] + DLG_DY + CODO_DY;
    const r  = 16;
    const d  = `M ${ax} ${ay} L ${ax} ${y1-r} Q ${ax} ${y1} ${ax+r} ${y1} L ${DLG_X} ${y1}`;
    return (
      <svg viewBox="0 0 1920 1080" width={1920} height={1080}
           style={{position:'absolute', left:0, top:0, zIndex:15, pointerEvents:'none'}}>
        <path d={d} pathLength={1} fill="none" stroke="#0085FF" strokeWidth={3}
              strokeLinecap="round" strokeLinejoin="round"
              strokeDasharray="1" strokeDashoffset={1-p}/>
        <circle cx={DLG_X} cy={y1} r={6} fill="#0085FF" opacity={clamp((p-0.82)/0.18, 0, 1)}/>
      </svg>
    );
  };

  // ── Tarjeta fantasma: lo que NO existe. Se tacha en GRIS, no en rojo. ───────────────────────
  const Fantasma = ({ texto, beat, top, icono }) => {
    const p = clamp((t - beat)/0.45, 0, 1);
    const e = Easing.easeOutBack(p);
    const tacha = Easing.easeOutCubic(clamp((t - beat - 0.45)/0.4, 0, 1));
    const salida = 1 - clamp((t - T.debajo)/0.3, 0, 1);
    if (p <= 0 || salida <= 0) return null;
    return (
      <div style={{
        position:'absolute', left:650, top, width:620, height:112, zIndex:40,
        display:'flex', alignItems:'center', gap:20, padding:'0 26px', boxSizing:'border-box',
        background:'#F8FAFC', border:'2px dashed #CBD5E1', borderRadius:14,
        boxShadow:'0 18px 44px rgba(15,23,42,0.12)',
        opacity:clamp(p*1.5, 0, 1) * salida,
        transform:`translateY(${(1-e)*14}px) scale(${lerp(0.93, 1, e)})`, transformOrigin:'50% 50%',
      }}>
        {icono}
        <span className="f-plex" style={{fontSize:34, fontWeight:600, color:'#94A3B8', whiteSpace:'nowrap'}}>{texto}</span>
        <span style={{position:'absolute', left:26, right:26, top:'52%', height:5, borderRadius:3,
                      background:'#94A3B8', transform:`rotate(-1.2deg) scaleX(${tacha})`, transformOrigin:'left center'}}/>
      </div>
    );
  };

  const IcoPantalla = (
    <svg width="40" height="40" viewBox="0 0 24 24" fill="none" stroke="#94A3B8" strokeWidth="1.8"
         strokeLinecap="round" strokeLinejoin="round" style={{flexShrink:0}}>
      <rect x="2.5" y="4" width="19" height="13" rx="2"/><path d="M9 21 H15"/><path d="M12 17 V21"/>
    </svg>
  );
  const IcoInforme = (
    <svg width="40" height="40" viewBox="0 0 24 24" fill="none" stroke="#94A3B8" strokeWidth="1.8"
         strokeLinecap="round" strokeLinejoin="round" style={{flexShrink:0}}>
      <path d="M6 2.5 H14 L18.5 7 V21.5 H6 Z"/><path d="M14 2.5 V7 H18.5"/>
      <path d="M9 12 H15.5"/><path d="M9 15.5 H15.5"/><path d="M9 19 H13"/>
    </svg>
  );

  // ── Eco de La Única Cosa. 68 px, mitad 2 en dos líneas para no salirse de los 784 px útiles ──
  // Mitad 2 entra CON la voz, igual que en las escenas 01 y 02: el «+0.5 s» del plan era la
  // estimación previa al audio; sobre la toma real T.eco2 YA es el instante en que se dice la
  // frase, y retrasarla medio segundo rompía la ley de sincronía en el único plano fijo de la pieza.
  const ecoP1 = Easing.easeOutCubic(clamp((t - T.eco1)/0.6, 0, 1));
  const ecoP2 = Easing.easeOutCubic(clamp((t - T.eco2)/0.6, 0, 1));
  const logoP = Easing.easeOutCubic(clamp((t - T.eco2 - 0.8)/0.5, 0, 1));  // cierra en 22.59 < 23.58

  return (
    <SlideCanvas variant="light">
      {/* firma de la pieza, ambiente: la misma de la escena 01 */}
      <div className="f-plex" style={{position:'absolute', top:58, left:568, width:784, textAlign:'center',
                                      fontSize:15, fontWeight:600, letterSpacing:'3px', color:'#94A3B8',
                                      opacity:lerp(0.6*clamp(t/0.6, 0, 1), 0.2, dimEco), zIndex:4}}>
        HISTÓRICO POR CAMPO
      </div>

      {/* Tres veces lo mismo: ficha → clic derecho en su etiqueta → una sola opción → historial */}
      {UNIDADES.map((u, i) => {
        const op = opUnidad(i);
        if (op <= 0) return null;
        return (
          <div key={u.seccion} style={{position:'absolute', inset:0, opacity:op, zIndex:10+i}}>
            <FichaMini u={u} i={i}/>
            <Conector i={i}/>
            <DialogoHistorial
              t={t}
              campo={u.campo}
              valorActual={u.valor}
              entradas={u.entradas}
              escala={DLG_ESC}
              x={DLG_X} y={FILA_Y[i] + DLG_DY}
              ancho={780} alto={420}
              transformOrigin="0% 0%"
              escalaEntrada={0.82}
              aparicion={ANCLA[i] + 1.05}
              zIndex={18}
            />
            <MenuContextualUnItem
              t={t}
              x={FICHA_X+PUNT_DX} y={FILA_Y[i]+PUNT_DY}
              escala={0.72}
              start={ANCLA[i] + 0.62}
              resalte={ANCLA[i] + 1.00}
              salida={ANCLA[i] + 1.05}
              zIndex={30}
            />
            <AnilloClicDerecho
              t={t}
              x={FICHA_X+PUNT_DX} y={FILA_Y[i]+PUNT_DY}
              start={ANCLA[i] + 0.52}
              tamano={96}
              zIndex={32}
            />
          </div>
        );
      })}

      {/* Lo que no existe: una por frase (la sincronía manda) */}
      <Fantasma texto="Pantalla de auditoría" beat={T.auditoria} top={372} icono={IcoPantalla}/>
      <Fantasma texto="Informe de cambios"   beat={T.informe}   top={508} icono={IcoInforme}/>

      {/* Puntero: se retira antes de las fantasmas y no vuelve */}
      {opCursor > 0 && (
        <div style={{position:'absolute', inset:0, zIndex:34, opacity:opCursor, pointerEvents:'none'}}>
          <ScriptedCursor frames={cursorFrames} t={t}/>
        </div>
      )}

      {/* ECO — las dos mitades de La Única Cosa, y pantalla fija hasta el corte.
          Debajo, un difuminado blanco muy suave: la composición se queda al 0.2 (lo que manda el
          plan) pero las palabras no compiten con el 990 que tienen detrás. */}
      {ecoP1 > 0 && (
        <div style={{position:'absolute', inset:0, zIndex:55, pointerEvents:'none', opacity:ecoP1,
                     background:'radial-gradient(ellipse 46% 34% at 50% 46%, rgba(255,255,255,0.94) 0%, rgba(255,255,255,0.80) 45%, rgba(255,255,255,0) 100%)'}}/>
      )}
      {ecoP1 > 0 && (
        <div className="f-plex" style={{position:'absolute', left:568, width:784, top:322, textAlign:'center',
                                        fontSize:68, fontWeight:700, letterSpacing:'-0.03em', lineHeight:1.12,
                                        color:'#0F172A', zIndex:60,
                                        opacity:ecoP1, transform:`translateY(${(1-ecoP1)*14}px)`}}>
          ¿Quién ha tocado esto?
        </div>
      )}
      {ecoP2 > 0 && (
        <div className="f-plex" style={{position:'absolute', left:568, width:784, top:436, textAlign:'center',
                                        fontSize:68, fontWeight:700, letterSpacing:'-0.03em', lineHeight:1.12,
                                        zIndex:60,
                                        opacity:ecoP2, transform:`translateY(${(1-ecoP2)*14}px)`}}>
          <span className="grad-text">Clic derecho<br/>en el nombre del campo.</span>
        </div>
      )}
      {logoP > 0 && (
        <div style={{position:'absolute', left:568, width:784, top:686, display:'flex', justifyContent:'center',
                     zIndex:60, opacity:logoP, transform:`translateY(${(1-logoP)*10}px)`}}>
          <DinaupLogo size={46}/>
        </div>
      )}
    </SlideCanvas>
  );
}
Object.assign(window, { EscenaHC03 });
