/* kit/marca.jsx — as peças de marca e de lançamento.
 *
 * O repertório dos vídeos de referência (brand reveal, reel de motion
 * designer, lançamento de SaaS), cada um virado peça:
 *
 *   orbe       o brilho no escuro, anéis e poeira; a marca nasce dele
 *   vetor      as letras em contorno, com âncoras, alças de bezier e o cursor
 *   manchete   a frase cinética: carimbo, desfoque, digitação ou subida; sai em pontos
 *   cromo      a marca cromada (ou bordada) com a luz de estúdio passando
 *   cores      os cartões de cor flutuando em 3D, com borrão de movimento
 *   padrao     a marca ladrilhada (ou o truchet) com a onda passando
 *   particulas a explosão de partículas
 *   estrela    o asterisco que explode no anel com texto em volta
 *   cartoes    cartões de interface: voo em 3D, colunas de calendário, mural
 *   painel     o cartão do produto: o marca-texto, o selo, as barras enchendo
 *   esfera     a esfera de pontos girando em 3D
 *   grade      o chão em perspectiva
 *   site       a janela do site com a manchete entrando
 *
 * Coordenadas da cena em px; tempo do elemento em segundos. */
import React from 'react';
import { Img, staticFile } from 'remotion';
import {
  mola, entre, lerp, acaso, Traco, suave, familia, MONO, CURVAS_KIT, mistura, alfa, sobre, Marca,
} from './base';

const ED = CURVAS_KIT.editorial;

// ── as palavras que entram (manchete e site dividem) ────────────────────
function entradaPalavra(modo, t, t0, fps, i) {
  if (t < t0) return { opacity: 0 };
  const k = ED(entre(t, t0, t0 + 10 / fps));
  if (modo === 'desfoque') {
    return { opacity: Math.min(1, k * 1.6), filter: k < 1 ? 'blur(' + ((1 - k) * 18).toFixed(1) + 'px)' : undefined,
      transform: 'translateY(' + ((1 - k) * 0.22).toFixed(3) + 'em)' };
  }
  if (modo === 'subir') return { transform: 'translateY(' + ((1 - k) * 105).toFixed(1) + '%)' };
  // carimbo: rotação e escala que assentam; nunca fade.
  const m = mola(t, t0, fps);
  const r0 = (acaso(i + 5) - 0.5) * 6;
  return { opacity: 1, transform: 'rotate(' + (r0 * (1 - m)).toFixed(2) + 'deg) scale(' + (1 + 0.22 * (1 - m)).toFixed(3) + ')' };
}

// ── manchete cinética ────────────────────────────────────────────────────
export function Manchete({ el, t, fps }) {
  const texto = String(el.texto || '');
  const modo = el.modo || 'desfoque';
  const t0 = el.t || 0;
  const passo = (el.passo || 4) / fps;
  const tam = el.tamanho || 140;
  const cor = el.cor || '#111';
  const acento = el.corAcento || cor;
  const alvos = new Set(String(el.destaque || '').toLowerCase().split(/\s+/).filter(Boolean).map((w) => w.replace(/[^\p{L}\p{N}]/gu, '')));
  const centro = el.alinhamento === 'center';
  const caixa = { position: 'absolute', left: el.x, top: el.y, width: el.largura, transform: centro ? 'translateX(-50%)' : (el.ancora === 'meio' ? 'translateY(-50%)' : undefined),
    textAlign: centro ? 'center' : 'left', fontFamily: familia(el.fonte), fontWeight: el.peso || 900, fontSize: tam, lineHeight: el.entrelinha || 0.98,
    letterSpacing: el.espacamento ?? '-0.035em', color: cor, textTransform: el.caixaAlta ? 'uppercase' : undefined };

  // O NÚMERO que conta: sobe de 0 até o valor na curva editorial.
  if (el.contar) {
    const k = ED(entre(t, t0, t0 + (el.contar.dur || 0.9)));
    const v = (Number(el.contar.ate) * k).toLocaleString('pt-BR', { maximumFractionDigits: el.contar.casas || 0, minimumFractionDigits: el.contar.casas || 0 });
    return <div style={{ ...caixa, fontVariantNumeric: 'tabular-nums', ...entradaPalavra('carimbo', t, t0, fps, 1) }}>{(el.contar.antes || '') + v + (el.contar.depois || '')}</div>;
  }

  // A DIGITAÇÃO: letra a letra, com o cursor piscando (o wordmark que se escreve).
  if (modo === 'digitar') {
    const n = Math.max(0, Math.min(texto.length, Math.floor((t - t0) * fps / 1.6) + 1));
    const pisca = Math.floor(t * fps / 9) % 2 === 0 && t < t0 + texto.length * 1.6 / fps + 0.8;
    return (
      <div style={{ ...caixa, whiteSpace: 'nowrap', opacity: t >= t0 ? 1 : 0 }}>
        {texto.slice(0, n)}<span style={{ opacity: pisca ? 1 : 0, fontWeight: 300, marginLeft: '0.02em' }}>|</span>
      </div>
    );
  }

  const ws = texto.split(/\s+/).filter(Boolean);
  const saida = el.saida && t >= el.saida.t ? el.saida : null;
  let letra = 0;
  return (
    <div style={caixa}>
      {ws.map((w, i) => {
        const tw = el.tempos && el.tempos[i] != null ? el.tempos[i] : t0 + i * passo;
        const ehAlvo = alvos.has(w.toLowerCase().replace(/[^\p{L}\p{N}]/gu, ''));
        const quebra = el.quebras && el.quebras.includes(i);
        const letras = w.split('');
        const conteudo = saida && saida.modo === 'pontos'
          ? letras.map((c, j) => {
            // As letras DESMANCHAM em pontos, uma depois da outra, e os pontos somem.
            const n = letra++;
            const tl = saida.t + n * 1.2 / fps;
            const k = ED(entre(t, tl, tl + 9 / fps));
            const some = entre(t, tl + 14 / fps, tl + 22 / fps);
            const r = (acaso(n + 9) - 0.5) * 70;
            return (
              <span key={j} style={{ position: 'relative', display: 'inline-block' }}>
                <span style={{ display: 'inline-block', transform: 'scale(' + (1 - k * 0.92).toFixed(3) + ')', opacity: 1 - k }}>{c}</span>
                <span style={{ position: 'absolute', left: '50%', top: '55%', width: '0.26em', height: '0.2em', borderRadius: '50%', background: ehAlvo ? acento : cor,
                  transform: 'translate(-50%,-50%) rotate(' + r + 'deg) scale(' + (k * (1 - some)).toFixed(3) + ')' }} />
              </span>
            );
          })
          : w;
        const estilo = saida && saida.modo === 'desfoque'
          ? { filter: 'blur(' + (entre(t, saida.t, saida.t + 8 / fps) * 20).toFixed(1) + 'px)', opacity: 1 - entre(t, saida.t, saida.t + 8 / fps) }
          : entradaPalavra(modo, t, tw, fps, i);
        return (
          <React.Fragment key={i}>
            {quebra ? <br /> : null}
            <span style={{ display: 'inline-block', overflow: modo === 'subir' ? 'hidden' : undefined, verticalAlign: 'top', paddingBottom: modo === 'subir' ? '0.08em' : undefined }}>
              <span style={{ display: 'inline-block', color: ehAlvo ? acento : undefined, transformOrigin: '50% 80%', ...estilo }}>{conteudo}</span>
            </span>
            {i < ws.length - 1 ? ' ' : null}
          </React.Fragment>
        );
      })}
    </div>
  );
}

// ── o orbe ───────────────────────────────────────────────────────────────
export function Orbe({ el, t, fps }) {
  const R = el.tamanho || 260;
  const cor = el.cor || '#ff5a1f';
  const t0 = el.t || 0;
  const tM = el.tMarca;
  const nasce = mola(t, t0, fps);
  const vira = tM != null ? ED(entre(t, tM, tM + 12 / fps)) : 0;
  const respira = 1 + 0.04 * Math.sin(t * 2.2);
  const pts = [];
  for (let i = 0; i < (el.particulas ?? 46); i++) {
    const a = acaso(i + 1) * Math.PI * 2;
    const d0 = R * (0.9 + acaso(i + 50) * 3.2);
    const d = d0 + t * (8 + acaso(i + 90) * 22);
    const s = 1.5 + acaso(i + 130) * 2.6;
    const pisca = 0.35 + 0.65 * Math.abs(Math.sin(t * (1 + acaso(i + 7) * 2) + i));
    pts.push(<circle key={i} cx={el.x + Math.cos(a) * d} cy={el.y + Math.sin(a) * d * 0.8} r={s} fill={i % 5 === 0 ? cor : '#ffffff'} opacity={pisca * 0.55 * nasce} />);
  }
  return (
    <>
      <svg style={{ position: 'absolute', inset: 0, width: '100%', height: '100%', overflow: 'visible' }}>
        {el.aneis !== false && [1.35, 2.1, 3.0].map((k, i) => (
          <circle key={i} cx={el.x} cy={el.y} r={R * k * (0.9 + 0.1 * nasce) + t * 6 * (i + 1)} fill="none" stroke={alfa(cor, 0.22 - i * 0.05)} strokeWidth={1.4} />
        ))}
        {pts}
      </svg>
      <div style={{ position: 'absolute', left: el.x - R * 2, top: el.y - R * 2, width: R * 4, height: R * 4, borderRadius: '50%',
        background: 'radial-gradient(circle, ' + alfa(cor, 0.55) + ' 0%, ' + alfa(cor, 0.16) + ' 30%, transparent 62%)',
        transform: 'scale(' + (nasce * respira * (1 + vira * 0.25)) + ')', opacity: 1 - vira * 0.35 }} />
      <div style={{ position: 'absolute', left: el.x - R * 0.2, top: el.y - R * 0.2, width: R * 0.4, height: R * 0.4, borderRadius: '50%',
        background: 'radial-gradient(circle at 40% 35%, ' + mistura(cor, '#ffffff', 0.5) + ', ' + cor + ' 60%)', boxShadow: '0 0 ' + R * 0.3 + 'px ' + alfa(cor, 0.8),
        transform: 'scale(' + (nasce * (1 - vira)) + ')' }} />
      {tM != null && t >= tM ? (
        <div style={{ position: 'absolute', left: el.x, top: el.y, transform: 'translate(-50%,-50%) scale(' + (0.55 + 0.45 * mola(t, tM, fps)) + ')',
          filter: 'drop-shadow(0 0 ' + R * 0.18 + 'px ' + alfa(el.corMarca || cor, 0.85) + ')' }}>
          <Marca logo={el.logo} tamanho={R * 0.62} cor={el.corMarca || cor} tinta={el.logo ? undefined : 'linear-gradient(160deg, #ffffff, ' + mistura(el.corMarca || cor, '#ffffff', 0.4) + ')'} />
        </div>
      ) : null}
    </>
  );
}

// ── as letras em vetor ───────────────────────────────────────────────────
/* "glifos" vem do PrimoCode (glifos.js leu a fonte TrueType): o caminho de
   cada letra e os pontos de verdade — os que ficam na curva viram âncora
   (quadrado), os de controle viram alça (bolinha com a haste). Sem glifos
   (sem rede na hora de montar), o contorno sai do próprio texto, sem alças. */
export function Vetor({ el, t, fps }) {
  const g = el.glifos;
  const t0 = el.t || 0;
  const durTraco = el.durTraco || 1.1;
  const pT = CURVAS_KIT.arrasto(entre(t, t0, t0 + durTraco));
  const pFill = ED(entre(t, t0 + durTraco * 0.75, t0 + durTraco + 0.5));
  const H = el.altura || 600;
  const cor = el.cor || '#ffffff';
  const azul = el.corPonto || '#2f8cff';
  const pan = el.pan ? CURVAS_KIT.camera(entre(t, t0, t0 + (el.pan.dur || 3))) : 0;
  const dx = el.pan ? lerp(el.pan.de || 0, el.pan.ate || 0, pan) : 0;
  if (!g) {
    return (
      <svg style={{ position: 'absolute', inset: 0, width: '100%', height: '100%', overflow: 'visible' }}>
        <text x={el.x + dx} y={el.y} fontFamily={familia(el.fonte)} fontWeight={el.peso || 800} fontSize={H} textAnchor="middle" dominantBaseline="central"
          fill={alfa('#2a2a30', pFill)} stroke={cor} strokeWidth={Math.max(2, H * 0.006)} strokeDasharray={H * 30} strokeDashoffset={H * 30 * (1 - pT)}>{el.texto}</text>
      </svg>
    );
  }
  const [bx, by, bw, bh] = g.caixa;
  const esc = H / bh;
  const x0 = el.x - (bw * esc) / 2 + dx, y0 = el.y - (bh * esc) / 2;
  const P = (p) => [x0 + (p[0] - bx) * esc, y0 + (p[1] - by) * esc];
  const pontos = g.pontos || [];
  const N = pontos.length;
  const q = Math.max(6, H * 0.022);
  const itens = [];
  pontos.forEach((p, i) => {
    const tp = t0 + (i / Math.max(1, N)) * durTraco * 0.95;
    if (t < tp) return;
    const m = mola(t, tp, fps);
    const [x, y] = P(p);
    if (p[2]) {
      itens.push(<rect key={'a' + i} x={x - q / 2} y={y - q / 2} width={q} height={q} fill={azul} stroke="#ffffff" strokeWidth={1.2}
        transform={'rotate(0) translate(0 0)'} opacity={m} style={{ transformBox: 'fill-box', transformOrigin: 'center', transform: 'scale(' + (0.4 + 0.6 * m) + ')' }} />);
    } else {
      // A alça: a haste até as âncoras vizinhas, e a bolinha na ponta.
      const viz = [pontos[(i - 1 + N) % N], pontos[(i + 1) % N]].filter((v) => v && v[3] === p[3]);
      for (const [k, v] of viz.entries()) {
        const [vx, vy] = P(v);
        itens.push(<line key={'h' + i + k} x1={x} y1={y} x2={vx} y2={vy} stroke={azul} strokeWidth={1.3} opacity={0.9 * m} />);
      }
      itens.push(<circle key={'c' + i} cx={x} cy={y} r={q * 0.36 * (0.4 + 0.6 * m)} fill="#ffffff" stroke={azul} strokeWidth={1.4} />);
    }
  });
  // O cursor passeia pelas âncoras, como quem ajusta a curva.
  const ancoras = pontos.filter((p) => p[2]);
  let cursor = null;
  if (el.cursor !== false && ancoras.length && t >= t0 + 0.3) {
    const seg = 0.55;
    const k = (t - t0 - 0.3) / seg;
    const i = Math.floor(k);
    const a = P(ancoras[(i * 7) % ancoras.length]), b = P(ancoras[((i + 1) * 7) % ancoras.length]);
    const e = ED(Math.min(1, (k - i) * 1.4));
    const cx = lerp(a[0], b[0], e) + q, cy = lerp(a[1], b[1], e) + q;
    const s = Math.max(22, H * 0.06);
    cursor = <path d={'M ' + cx + ' ' + cy + ' l 0 ' + s + ' l ' + s * 0.28 + ' ' + -s * 0.26 + ' l ' + s * 0.42 + ' ' + -s * 0.02 + ' Z'}
      fill="#d9dde6" stroke="#1b1d22" strokeWidth={1.5} strokeLinejoin="round" />;
  }
  const dGlifos = 'translate(' + x0 + ' ' + y0 + ') scale(' + esc + ') translate(' + -bx + ' ' + -by + ')';
  return (
    <svg style={{ position: 'absolute', inset: 0, width: '100%', height: '100%', overflow: 'visible' }}>
      <defs>
        <linearGradient id="vetor-preenche" x1="0" y1="0" x2="0" y2="1">
          <stop offset="0" stopColor={el.corTopo || '#3a3b42'} />
          <stop offset="1" stopColor={el.corBase || '#101114'} />
        </linearGradient>
      </defs>
      <g transform={dGlifos}>
        <path d={g.d} fill="url(#vetor-preenche)" fillOpacity={pFill} fillRule="nonzero" />
        <path d={g.d} fill="none" stroke={cor} strokeWidth={Math.max(1.6, H * 0.004) / esc} pathLength={1} strokeDasharray="1 1" strokeDashoffset={1 - pT} />
      </g>
      {itens}
      {cursor}
    </svg>
  );
}

// ── a marca cromada ──────────────────────────────────────────────────────
export function Cromo({ el, t, fps }) {
  const s = el.tamanho || 320;
  const t0 = el.t || 0;
  const m = mola(t, t0, fps, { damping: 30, stiffness: 120, mass: 1 });
  const giro = (el.giro ?? 16) * Math.sin((t - t0) * 0.9);
  const ang = 115 + 70 * Math.sin((t - t0) * 0.7);
  const passa = ((t - t0) * 0.45) % 1.6 - 0.3;           // a luz de estúdio que atravessa
  const modo = el.modo || 'cromo';
  const tom = el.tom || '#9aa0aa';
  const tinta = modo === 'bordado'
    ? 'repeating-linear-gradient(' + (45 + giro * 0.2) + 'deg, ' + mistura(tom, '#ffffff', 0.25) + ' 0px, ' + tom + ' 2px, ' + mistura(tom, '#000000', 0.35) + ' 4px, ' + tom + ' 6px)'
    : modo === 'solido'
      ? tom
      : 'linear-gradient(' + ang + 'deg, #1c1d21 0%, #f4f5f7 14%, #7d8088 27%, #ffffff 40%, #2e3036 52%, #c9ccd3 66%, #5f626a 80%, #eef0f3 92%, #3a3c42 100%)';
  const brilho = 'linear-gradient(105deg, transparent ' + (passa * 100 - 12) + '%, rgba(255,255,255,0.95) ' + passa * 100 + '%, transparent ' + (passa * 100 + 12) + '%)';
  const sombra = modo === 'bordado'
    ? 'drop-shadow(0 2px 0 rgba(0,0,0,0.5)) drop-shadow(0 -1px 0 rgba(255,255,255,0.18)) drop-shadow(0 16px 26px rgba(0,0,0,0.5))'
    : 'drop-shadow(0 1px 0 rgba(255,255,255,0.55)) drop-shadow(0 -1px 1px rgba(0,0,0,0.6)) drop-shadow(0 22px 34px rgba(0,0,0,0.45))';
  return (
    <div style={{ position: 'absolute', left: el.x, top: el.y, width: s, height: s, transform: 'translate(-50%,-50%)', perspective: 1200, opacity: t >= t0 ? 1 : 0 }}>
      <div style={{ position: 'absolute', inset: 0, transformStyle: 'preserve-3d',
        transform: 'rotateY(' + (lerp(-50, 0, m) + giro) + 'deg) rotateX(' + (6 * Math.sin((t - t0) * 0.6)) + 'deg) scale(' + (0.72 + 0.28 * m) + ')' }}>
        <div style={{ position: 'absolute', inset: 0, filter: sombra }}><Marca logo={el.logo} tamanho={s} tinta={tinta} /></div>
        {modo !== 'bordado' ? <div style={{ position: 'absolute', inset: 0, mixBlendMode: 'screen' }}><Marca logo={el.logo} tamanho={s} tinta={brilho} /></div> : null}
      </div>
      {el.reflexo !== false && modo !== 'bordado' ? (
        <div style={{ position: 'absolute', left: 0, top: s * 1.04, width: s, height: s, transform: 'scaleY(-1) rotateY(' + (lerp(-50, 0, m) + giro) + 'deg)',
          opacity: 0.14 * m, WebkitMaskImage: 'linear-gradient(to top, rgba(0,0,0,0.9), transparent 55%)', maskImage: 'linear-gradient(to top, rgba(0,0,0,0.9), transparent 55%)' }}>
          <Marca logo={el.logo} tamanho={s} tinta={tinta} />
        </div>
      ) : null}
    </div>
  );
}

// ── os cartões de cor ────────────────────────────────────────────────────
export function Cores({ el, t, fps }) {
  const cores = el.cores || [];
  const n = cores.length;
  const w = el.largura || 300, h = w * 0.82;
  const Rx = el.raioX || 560, Ry = el.raioY || 300;
  const t0 = el.t || 0;
  return (
    <div style={{ position: 'absolute', inset: 0, perspective: 1600 }}>
      {cores.map((c, i) => {
        const a = -Math.PI * 0.8 + (i / Math.max(1, n - 1)) * Math.PI * 1.6 * (n > 1 ? 1 : 0) + (acaso(i + 2) - 0.5) * 0.35;
        const alvoX = el.x + Math.cos(a) * Rx * (0.8 + acaso(i + 4) * 0.3);
        const alvoY = el.y + Math.sin(a) * Ry * (0.8 + acaso(i + 6) * 0.35);
        const de = t0 + i * 5 / fps;
        const k = mola(t, de, fps, { damping: 22, stiffness: 110, mass: 0.8 });
        const kAntes = mola(t - 1 / fps, de, fps, { damping: 22, stiffness: 110, mass: 0.8 });
        // O borrão de movimento é a velocidade do quadro: rápido borra, parado fica nítido.
        const vel = Math.abs(k - kAntes);
        const fora = 1.9;
        const x = lerp(el.x + Math.cos(a) * Rx * fora * 1.6, alvoX, k);
        const y = lerp(el.y + Math.sin(a) * Ry * fora * 1.6, alvoY, k) + Math.sin(t * 1.3 + i * 1.7) * 8;
        const z = -120 + acaso(i + 8) * 260;
        const ry = (Math.cos(a) * -22) + Math.sin(t * 0.8 + i) * 4;
        const rx = (Math.sin(a) * 14);
        const rz = (acaso(i + 10) - 0.5) * 14;
        const txt = sobre(c.hex || '#888');
        return (
          <div key={i} style={{ position: 'absolute', left: x - w / 2, top: y - h / 2, width: w, height: h, borderRadius: w * 0.07, background: c.hex,
            transform: 'translateZ(' + z + 'px) rotateX(' + rx + 'deg) rotateY(' + ry + 'deg) rotate(' + rz + 'deg) scaleX(' + (1 + vel * 2.2) + ')',
            filter: vel > 0.004 ? 'blur(' + Math.min(22, vel * 520).toFixed(1) + 'px)' : undefined, opacity: t >= de ? 1 : 0,
            boxShadow: '0 30px 60px -20px rgba(0,0,0,0.35), 0 2px 4px rgba(0,0,0,0.12)', border: luzBorda(c.hex) }}>
            <div style={{ position: 'absolute', left: w * 0.08, top: w * 0.07, fontFamily: familia(el.fonte), fontWeight: 600, fontSize: w * 0.085, color: txt }}>{c.nome}</div>
            <div style={{ position: 'absolute', left: w * 0.08, bottom: w * 0.07, fontFamily: MONO(el.fonteMono), fontSize: w * 0.052, color: alfa(txt, 0.7), letterSpacing: '0.04em' }}>{String(c.hex || '').toUpperCase()}</div>
          </div>
        );
      })}
    </div>
  );
}
const luzBorda = (hex) => (sobre(hex) === '#111114' ? '1px solid rgba(0,0,0,0.06)' : '1px solid rgba(255,255,255,0.08)');

// ── o padrão ─────────────────────────────────────────────────────────────
export function Padrao({ el, t, fps }) {
  const W = el.largura || 1920, H = el.altura || 1080;
  const c = el.celula || 180;
  const cols = Math.ceil(W / c) + 2, lins = Math.ceil(H / c) + 2;
  const t0 = el.t || 0;
  const cor = el.cor || '#ffffff';
  const onda = el.corOnda || cor;
  const cx = cols / 2, cy = lins / 2;
  const deriva = (t - t0) * (el.deriva ?? 24);
  const cel = [];
  for (let j = 0; j < lins; j++) {
    for (let i = 0; i < cols; i++) {
      const dist = Math.hypot(i - cx, j - cy);
      const de = t0 + dist * 1.3 / fps;
      if (t < de) continue;
      const m = mola(t, de, fps);
      const fase = (t - t0) * 3.2 - dist * 0.55;
      const w = Math.sin(fase);
      const x = (i - 1) * c + ((j % 2) * c) / 2 - (deriva % c), y = (j - 1) * c;
      if (el.modo === 'truchet') {
        const gira = acaso(i * 31 + j * 17) > 0.5 ? 90 : 0;
        const aceso = w > 0.55;
        const traco = aceso ? mistura(cor, onda, Math.min(1, (w - 0.55) * 3)) : cor;
        cel.push(
          <svg key={i + '-' + j} viewBox="0 0 100 100" width={c} height={c} style={{ position: 'absolute', left: i * c - c - (deriva % c), top: y,
            transform: 'rotate(' + gira + 'deg) scale(' + m + ')' }}>
            <path d="M 50 0 A 50 50 0 0 1 0 50 M 100 50 A 50 50 0 0 0 50 100" fill="none" stroke={traco} strokeWidth={el.espessura || 16} strokeLinecap="round" />
          </svg>,
        );
      } else {
        const s = c * (el.escala || 0.52) * m * (0.86 + 0.14 * w);
        cel.push(
          <div key={i + '-' + j} style={{ position: 'absolute', left: x + c / 2 - s / 2, top: y + c / 2 - s / 2, width: s, height: s,
            transform: 'rotate(' + (w * 10) + 'deg)', opacity: 0.9 + 0.1 * w }}>
            <Marca logo={el.logo} tamanho={s} tinta={w > 0.8 ? onda : cor} />
          </div>,
        );
      }
    }
  }
  return <div style={{ position: 'absolute', left: el.x || 0, top: el.y || 0, width: W, height: H, overflow: 'hidden' }}>{cel}</div>;
}

// ── a explosão de partículas ─────────────────────────────────────────────
export function Particulas({ el, t, fps }) {
  const t0 = el.t || 0;
  if (t < t0) return null;
  const n = el.n || 42;
  const R = el.raio || 420;
  const cores = el.cores || ['#ffffff'];
  const dt = t - t0;
  const k = ED(entre(t, t0, t0 + 1.0));
  const pts = [];
  for (let i = 0; i < n; i++) {
    const a = acaso(i + 3) * Math.PI * 2;
    const v = 0.35 + acaso(i + 40) * 0.65;
    const d = R * v * k;
    const x = el.x + Math.cos(a) * d;
    const y = el.y + Math.sin(a) * d + dt * dt * 60 * v;
    const s = (el.tamanho || 7) * (0.5 + acaso(i + 80)) * (1 - entre(t, t0 + 0.5, t0 + 1.4) * 0.8);
    const op = 1 - entre(t, t0 + 0.7 + acaso(i) * 0.5, t0 + 1.5);
    const cor = cores[i % cores.length];
    pts.push(i % 4 === 3 && el.forma !== 'circulo'
      ? <path key={i} d={'M ' + x + ' ' + (y - s) + ' L ' + (x + s) + ' ' + (y + s) + ' L ' + (x - s) + ' ' + (y + s) + ' Z'} fill={cor} opacity={op} transform={'rotate(' + a * 57 + ' ' + x + ' ' + y + ')'} />
      : <circle key={i} cx={x} cy={y} r={s / 2 + 0.5} fill={cor} opacity={op} />);
  }
  return <svg style={{ position: 'absolute', inset: 0, width: '100%', height: '100%', overflow: 'visible' }}>{pts}</svg>;
}

// ── o asterisco e o anel ─────────────────────────────────────────────────
export function Estrela({ el, t, fps }) {
  const R = el.tamanho || 200;
  const t0 = el.t || 0;
  const cor = el.cor || '#ff5a1f';
  const n = el.raios || 14;
  const tb = t0 + (el.espera ?? 0.45);
  const gira = (t - t0) * 12;
  const raios = [];
  for (let i = 0; i < n; i++) {
    const m = mola(t, tb + (i % 3) / fps, fps);
    const a = (i / n) * 360 + gira;
    const L = R * (0.72 + acaso(i + 1) * 0.35) * m;
    raios.push(<line key={i} x1={0} y1={0} x2={0} y2={-L} stroke={cor} strokeWidth={R * 0.13} strokeLinecap="round" transform={'rotate(' + a + ')'} />);
  }
  const ponto = t < tb ? mola(t, t0, fps) : 1 - mola(t, tb, fps);
  const pAnel = CURVAS_KIT.arrasto(entre(t, t0, t0 + 0.8));
  const texto = String(el.texto || '');
  return (
    <svg style={{ position: 'absolute', inset: 0, width: '100%', height: '100%', overflow: 'visible' }}>
      <defs><path id="estrela-anel" d={'M ' + (el.x - R * 1.6) + ' ' + el.y + ' a ' + R * 1.6 + ' ' + R * 1.6 + ' 0 1 1 ' + R * 3.2 + ' 0 a ' + R * 1.6 + ' ' + R * 1.6 + ' 0 1 1 ' + -R * 3.2 + ' 0'} /></defs>
      <Traco d={'M ' + (el.x - R * 1.95) + ' ' + el.y + ' a ' + R * 1.95 + ' ' + R * 1.95 + ' 0 1 1 ' + R * 3.9 + ' 0 a ' + R * 1.95 + ' ' + R * 1.95 + ' 0 1 1 ' + -R * 3.9 + ' 0'}
        p={pAnel} cor={alfa(el.corAnel || '#ffffff', 0.35)} espessura={1.4} />
      {texto ? (
        <g transform={'rotate(' + (-gira * 0.6) + ' ' + el.x + ' ' + el.y + ')'} opacity={entre(t, t0 + 0.3, t0 + 0.7)}>
          <text fontFamily={MONO(el.fonteMono)} fontSize={R * 0.12} letterSpacing={R * 0.05} fill={alfa(el.corAnel || '#ffffff', 0.7)}>
            <textPath href="#estrela-anel">{(texto.toUpperCase() + '  ·  ').repeat(4)}</textPath>
          </text>
        </g>
      ) : null}
      <g transform={'translate(' + el.x + ' ' + el.y + ')'}>
        {raios}
        {ponto > 0.01 ? <circle r={R * 0.16 * ponto} fill={cor} /> : null}
      </g>
    </svg>
  );
}

// ── os cartões de interface ──────────────────────────────────────────────
function Cartao({ c, w, el, destaque }) {
  const acento = el.corAcento || '#ff5a1f';
  const fonte = familia(el.fonte);
  return (
    <div style={{ width: w, background: el.cor || '#ffffff', borderRadius: w * 0.045, padding: w * 0.055, boxSizing: 'border-box',
      boxShadow: '0 1px 2px rgba(0,0,0,0.08), 0 24px 48px -18px rgba(0,0,0,0.35)', fontFamily: fonte, color: el.corTexto || '#16171b',
      border: destaque ? '2px solid ' + alfa(acento, 0.8) : '1px solid rgba(0,0,0,0.05)' }}>
      <div style={{ display: 'flex', alignItems: 'center', gap: w * 0.03, marginBottom: w * 0.035 }}>
        <div style={{ width: w * 0.085, height: w * 0.085, borderRadius: '50%', background: c.cor || mistura(acento, '#ffffff', 0.55) }} />
        <div style={{ fontWeight: 700, fontSize: w * 0.05 }}>{c.titulo || ''}</div>
        {c.rotulo ? <div style={{ marginLeft: 'auto', fontFamily: MONO(el.fonteMono), fontSize: w * 0.034, color: '#8a8d96' }}>{c.rotulo}</div> : null}
      </div>
      {c.texto ? <div style={{ fontSize: w * (String(c.texto).length < 22 ? 0.085 : 0.055), lineHeight: 1.25, fontWeight: String(c.texto).length < 22 ? 700 : 500, letterSpacing: '-0.01em' }}>{c.texto}</div> : null}
      {!c.texto ? [0.92, 0.7, 0.45].map((k, i) => <div key={i} style={{ height: w * 0.028, width: k * 100 + '%', borderRadius: 99, background: '#e8e9ed', marginTop: w * 0.028 }} />) : null}
    </div>
  );
}

export function Cartoes({ el, t, fps }) {
  const lista = el.cartoes || [];
  const modo = el.modo || 'voo';
  const t0 = el.t || 0;
  const W = el.largura || 1920, H = el.altura || 1080;
  const X = el.x || 0, Y = el.y || 0;
  const acento = el.corAcento || '#ff5a1f';

  if (modo === 'colunas') {
    const n = Math.max(1, lista.length);
    const gap = W * 0.018;
    const cw = (W - gap * (n - 1)) / n;
    return (
      <div style={{ position: 'absolute', left: X, top: Y, width: W, height: H, display: 'flex', gap }}>
        {lista.map((c, i) => {
          const de = t0 + i * 4 / fps;
          const m = mola(t, de, fps);
          const itens = c.itens || [];
          /* A coluna da VEZ é a que está sendo falada ("t" dela): sobe um pouco e
             ganha a borda do acento; as outras ficam como estão. É o que dá vida
             a um calendário que fica muitos segundos na tela. */
          let vez = -1;
          lista.forEach((q, j) => { if (q.t != null && t >= q.t) vez = j; });
          const ativa = vez === i ? mola(t, c.t || 0, fps) : (vez === i + 1 ? 1 - mola(t, lista[vez].t, fps) : 0);   // a anterior desce, não some
          return (
            <div key={i} style={{ width: cw, height: H, background: el.cor || '#ffffff', borderRadius: cw * 0.06, padding: cw * 0.08, boxSizing: 'border-box',
              boxShadow: '0 1px 2px rgba(0,0,0,0.06), 0 30px 60px -28px rgba(0,0,0,' + (0.35 + 0.2 * ativa) + ')', fontFamily: familia(el.fonte), color: el.corTexto || '#16171b',
              outline: ativa > 0.05 ? (2 * ativa).toFixed(1) + 'px solid ' + alfa(acento, 0.9) : undefined,
              opacity: t >= de ? 1 : 0, transform: 'translateY(' + ((1 - m) * 120 - ativa * 18) + 'px)' }}>
              <div style={{ display: 'flex', alignItems: 'center', gap: cw * 0.05, marginBottom: cw * 0.1 }}>
                <div style={{ minWidth: cw * 0.16, height: cw * 0.16, borderRadius: 99, background: i === 0 ? acento : '#f0f0f3', color: i === 0 ? sobre(acento) : '#16171b',
                  display: 'flex', alignItems: 'center', justifyContent: 'center', fontWeight: 800, fontSize: cw * 0.075 }}>{c.numero ?? i + 1}</div>
                <div style={{ fontFamily: MONO(el.fonteMono), fontSize: cw * 0.06, letterSpacing: '0.1em', color: '#6d7079', textTransform: 'uppercase' }}>{c.rotulo || ''}</div>
              </div>
              <div style={{ fontWeight: 800, fontSize: cw * (c.titulo && c.titulo.length > 30 ? 0.095 : 0.115), lineHeight: 1.12, letterSpacing: '-0.015em', marginBottom: cw * 0.08 }}>{c.titulo}</div>
              {itens.map((it, j) => {
                const dj = de + (j + 2) * 3 / fps;
                return (
                  <div key={j} style={{ fontSize: cw * 0.062, lineHeight: 1.3, padding: cw * 0.05 + 'px 0', borderTop: '1px solid #ececf0', opacity: t >= dj ? 1 : 0,
                    background: c.marcado === j ? alfa(acento, 0.12) : undefined }}>{it}</div>
                );
              })}
            </div>
          );
        })}
      </div>
    );
  }

  if (modo === 'mural') {
    const cols = el.colunas || 7, lins = el.linhas || 5;
    const cw = W / cols * 0.88;
    const k = CURVAS_KIT.camera(entre(t, t0, t0 + (el.dur || 3.2)));
    const cells = [];
    for (let j = 0; j < lins; j++) {
      for (let i = 0; i < cols; i++) {
        const n = j * cols + i;
        const de = t0 + (acaso(n + 2) * 0.6);
        const c = lista[n % Math.max(1, lista.length)] || {};
        const barras = 1 + Math.floor(acaso(n + 9) * 3);
        cells.push(
          <div key={n} style={{ position: 'absolute', left: i * (W / cols), top: j * (H / lins), width: cw, height: H / lins * 0.88, background: '#ffffff', borderRadius: 10,
            boxShadow: '0 10px 24px -12px rgba(0,0,0,0.35)', padding: 10, boxSizing: 'border-box', opacity: t >= de ? 1 : 0,
            transform: 'translateZ(' + (acaso(n + 5) * 40) + 'px)' }}>
            <div style={{ fontFamily: MONO(el.fonteMono), fontSize: 13, color: '#8a8d96' }}>{c.rotulo || String(n + 1).padStart(2, '0')}</div>
            {Array.from({ length: barras }).map((_, b) => (
              <div key={b} style={{ height: 10, marginTop: 8, borderRadius: 3, width: (40 + acaso(n * 3 + b) * 55) + '%', background: b === 0 && acaso(n + 1) > 0.45 ? acento : '#e3e4e8' }} />
            ))}
          </div>,
        );
      }
    }
    return (
      <div style={{ position: 'absolute', left: X, top: Y, width: W, height: H, perspective: 1800 }}>
        <div style={{ position: 'absolute', inset: 0, transformStyle: 'preserve-3d', transformOrigin: '50% 50%',
          transform: 'scale(' + lerp(el.de ?? 2.4, el.ate ?? 0.78, k) + ') rotateX(' + lerp(0, 26, k) + 'deg) rotateZ(' + lerp(0, -14, k) + 'deg)' }}>
          {cells}
        </div>
      </div>
    );
  }

  // voo: os cartões chegam do fundo do espaço e ficam flutuando, cada um inclinado.
  const w = el.larguraCartao || Math.min(W, H) * 0.36;
  return (
    <div style={{ position: 'absolute', left: X, top: Y, width: W, height: H, perspective: 1400 }}>
      {lista.map((c, i) => {
        const px = c.x != null ? c.x : (0.08 + acaso(i + 1) * 0.84) * W;
        const py = c.y != null ? c.y : (0.08 + acaso(i + 20) * 0.84) * H;
        const de = t0 + (c.t != null ? c.t : i * 3 / fps);
        const m = mola(t, de, fps, { damping: 24, stiffness: 150, mass: 0.8 });
        const flutua = Math.sin(t * 1.1 + i * 1.9) * 10;
        const rx = 14 + acaso(i + 3) * 18, ry = (acaso(i + 4) - 0.5) * 40, rz = (acaso(i + 5) - 0.5) * 18;
        const z = -200 + acaso(i + 6) * 320;
        return (
          <div key={i} style={{ position: 'absolute', left: px - w / 2, top: py, opacity: t >= de ? Math.min(1, m * 3) : 0,
            transform: 'translateZ(' + lerp(z - 900, z, m) + 'px) translateY(' + (flutua + (1 - m) * 160) + 'px) rotateX(' + rx + 'deg) rotateY(' + ry + 'deg) rotate(' + rz + 'deg)',
            filter: z < -80 ? 'blur(' + ((-80 - z) / 60).toFixed(1) + 'px)' : undefined }}>
            <Cartao c={c} w={w * (c.escala || 1)} el={el} destaque={c.destaque} />
          </div>
        );
      })}
    </div>
  );
}

// ── o painel do produto ──────────────────────────────────────────────────
export function Painel({ el, t, fps }) {
  const W = el.largura || 900;
  const t0 = el.t || 0;
  const m = mola(t, t0, fps, { damping: 26, stiffness: 160, mass: 0.8 });
  const acento = el.corAcento || '#ff5a1f';
  const esc = el.tema === 'escuro';
  const bg = esc ? '#17181c' : '#ffffff';
  const tinta = esc ? '#f3f3f5' : '#16171b';
  const suaveCor = esc ? '#8d9099' : '#8a8d96';
  // "escala" engorda tudo por dentro (letra, margem, botão) sem mudar a largura: é o painel de quem está em pé.
  const u = (W / 100) * (el.escala || 1);
  const texto = String(el.texto || '');
  const dest = String(el.destaque || '');
  const iD = dest ? texto.indexOf(dest) : -1;
  const tMarca = t0 + (el.tMarca ?? 0.55);
  const pMarca = CURVAS_KIT.arrasto(entre(t, tMarca, tMarca + 14 / fps));
  const tSelo = t0 + (el.tSelo ?? 1.05);
  const tBarras = t0 + (el.tBarras ?? 1.25);
  const barras = el.barras || [];
  const temLado = barras.length > 0;
  // Empilhado (vídeo em pé), as barras vão embaixo do cartão, na largura toda.
  const Wp = temLado && !el.empilhar ? W * 0.66 : W;
  return (
    <div style={{ position: 'absolute', left: el.x, top: el.y, width: W, perspective: 1600, opacity: t >= t0 ? 1 : 0 }}>
      <div style={{ display: 'flex', flexDirection: el.empilhar ? 'column' : 'row', gap: u * 2.4, alignItems: 'flex-start', transformOrigin: '50% 100%',
        transform: 'translateY(' + (1 - m) * 90 + 'px) rotateX(' + (1 - m) * 22 + 'deg)' }}>
        <div style={{ position: 'relative', width: Wp, background: bg, borderRadius: u * 2.2, padding: u * 3, boxSizing: 'border-box', fontFamily: familia(el.fonte), color: tinta,
          boxShadow: '0 2px 4px rgba(0,0,0,0.08), 0 40px 80px -30px rgba(0,0,0,0.4)' }}>
          <div style={{ display: 'flex', alignItems: 'center', gap: u * 1.2, marginBottom: u * 2 }}>
            <div style={{ width: u * 4, height: u * 4, borderRadius: '50%', background: tinta, color: bg, fontWeight: 800, fontSize: u * 2, display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
              {(String(el.titulo || '?').trim()[0] || '?').toUpperCase()}
            </div>
            <div style={{ fontWeight: 700, fontSize: u * 2.2 }}>{el.titulo || ''}</div>
            <div style={{ marginLeft: 'auto', display: 'flex', gap: u * 1.4, fontSize: u * 1.5, color: suaveCor }}>{(el.abas || ['Prévia', 'Nota', 'Melhorar']).map((a) => <span key={a}>{a}</span>)}</div>
          </div>
          <div style={{ fontSize: u * (texto.length > 110 ? 2.5 : texto.length > 60 ? 3 : 4), lineHeight: 1.36, fontWeight: texto.length > 60 ? 500 : 600, minHeight: u * 10, letterSpacing: '-0.01em' }}>
            {iD >= 0 ? (
              <>
                {texto.slice(0, iD)}
                <span style={{ position: 'relative' }}>
                  <span style={{ position: 'absolute', left: '-0.08em', right: '-0.08em', top: '0.05em', bottom: '-0.02em', background: alfa(acento, 0.22), borderRadius: 4,
                    transform: 'scaleX(' + pMarca + ')', transformOrigin: '0% 50%' }} />
                  <span style={{ position: 'relative', color: pMarca > 0.5 ? mistura(tinta, acento, 0.55) : undefined }}>{dest}</span>
                </span>
                {texto.slice(iD + dest.length)}
              </>
            ) : texto}
          </div>
          <div style={{ display: 'flex', alignItems: 'center', gap: u * 1.2, marginTop: u * 3, fontSize: u * 1.6 }}>
            <div style={{ color: suaveCor }}>Salvar</div>
            <div style={{ marginLeft: 'auto', color: suaveCor }}>Rascunho</div>
            <div style={{ background: tinta, color: bg, borderRadius: 99, padding: u * 0.7 + 'px ' + u * 1.6 + 'px', fontWeight: 700 }}>{el.botao || 'Agendar'}</div>
          </div>
          {el.selo ? (
            <div style={{ position: 'absolute', right: -u * 2, top: -u * 2.6, background: acento, color: sobre(acento), borderRadius: 99, padding: u * 0.9 + 'px ' + u * 1.8 + 'px',
              fontWeight: 800, fontSize: u * 1.8, boxShadow: '0 12px 24px -8px ' + alfa(acento, 0.7), whiteSpace: 'nowrap',
              transform: 'scale(' + mola(t, tSelo, fps) + ') rotate(' + (1 - mola(t, tSelo, fps)) * -8 + 'deg)', transformOrigin: '80% 80%' }}>{'✓  ' + el.selo}</div>
          ) : null}
        </div>
        {temLado ? (
          <div style={{ width: el.empilhar ? W : W - Wp - u * 2.4, background: bg, borderRadius: u * 2, padding: u * 2.2, boxSizing: 'border-box', fontFamily: familia(el.fonte), color: tinta,
            boxShadow: '0 2px 4px rgba(0,0,0,0.08), 0 30px 60px -28px rgba(0,0,0,0.4)', marginTop: el.empilhar ? 0 : u * 4 }}>
            <div style={{ fontFamily: MONO(el.fonteMono), fontSize: u * 1.2, letterSpacing: '0.12em', color: suaveCor, marginBottom: u * 1.6, textTransform: 'uppercase' }}>{el.tituloBarras || 'Nota'}</div>
            {barras.map((b, i) => {
              const k = ED(entre(t, tBarras + i * 4 / fps, tBarras + i * 4 / fps + 0.7));
              const v = Math.max(0, Math.min(100, Number(b.valor) || 0));
              return (
                <div key={i} style={{ marginBottom: u * 1.3 }}>
                  <div style={{ display: 'flex', fontSize: u * 1.35, marginBottom: u * 0.5 }}>
                    <span>{b.rotulo}</span><span style={{ marginLeft: 'auto', fontVariantNumeric: 'tabular-nums', color: suaveCor }}>{Math.round(v * k)}%</span>
                  </div>
                  <div style={{ height: u * 0.9, borderRadius: 99, background: esc ? '#2a2c32' : '#eeeff2', overflow: 'hidden' }}>
                    <div style={{ height: '100%', width: v * k + '%', background: acento, borderRadius: 99 }} />
                  </div>
                </div>
              );
            })}
          </div>
        ) : null}
      </div>
    </div>
  );
}

// ── a esfera de pontos ───────────────────────────────────────────────────
export function Esfera({ el, t, fps }) {
  const R = el.raio || 320;
  const t0 = el.t || 0;
  const nasce = mola(t, t0, fps, { damping: 28, stiffness: 90, mass: 1 });
  const lat = el.latitudes || 16, lon = el.longitudes || 28;
  const gy = (t - t0) * 0.55, gx = 0.38;
  const cor = el.cor || '#ffffff';
  const acento = el.corAcento || '#ff5a1f';
  const pts = [];
  for (let i = 1; i < lat; i++) {
    const th = (i / lat) * Math.PI;
    for (let j = 0; j < lon; j++) {
      const ph = (j / lon) * Math.PI * 2 + gy;
      let x = Math.sin(th) * Math.cos(ph), y = Math.cos(th), z = Math.sin(th) * Math.sin(ph);
      // inclina em X
      const y2 = y * Math.cos(gx) - z * Math.sin(gx), z2 = y * Math.sin(gx) + z * Math.cos(gx);
      const p = 1 / (1.6 - z2 * 0.45);
      const k = i * lon + j;
      const ac = acaso(k + 3) > 0.93;
      pts.push(<circle key={k} cx={el.x + x * R * p * nasce} cy={el.y + y2 * R * p * nasce} r={(ac ? 3.2 : 2) * p * (z2 > 0 ? 1.15 : 0.8)}
        fill={ac ? acento : cor} opacity={(0.25 + 0.75 * (z2 + 1) / 2) * nasce} />);
    }
  }
  return (
    <svg style={{ position: 'absolute', inset: 0, width: '100%', height: '100%', overflow: 'visible' }}>
      <ellipse cx={el.x} cy={el.y} rx={R * 0.62 * nasce} ry={R * 0.62 * 0.36 * nasce} fill="none" stroke={alfa(cor, 0.12)} />
      {pts}
    </svg>
  );
}

// ── o chão em perspectiva ────────────────────────────────────────────────
export function Grade({ el, t }) {
  const W = el.largura || 1920, H = el.altura || 1080;
  const hz = el.horizonte ?? H * 0.42;
  const cor = el.cor || 'rgba(60,80,200,0.35)';
  const linhas = [];
  const cx = W / 2;
  for (let i = -18; i <= 18; i++) linhas.push(<line key={'v' + i} x1={cx + i * 12} y1={hz} x2={cx + i * W * 0.16} y2={H * 1.4} stroke={cor} strokeWidth={1.2} />);
  const off = (t * (el.velocidade ?? 0.35)) % 1;
  for (let j = 0; j < 16; j++) {
    const k = (j + off) / 16;
    const y = hz + (H * 1.4 - hz) * k * k;
    linhas.push(<line key={'h' + j} x1={0} y1={y} x2={W} y2={y} stroke={cor} strokeWidth={1.2} opacity={k} />);
  }
  return (
    <svg style={{ position: 'absolute', left: el.x || 0, top: el.y || 0, width: W, height: H, overflow: 'hidden',
      WebkitMaskImage: 'linear-gradient(to bottom, transparent ' + (hz / H * 100) + '%, #000 ' + (hz / H * 100 + 30) + '%)',
      maskImage: 'linear-gradient(to bottom, transparent ' + (hz / H * 100) + '%, #000 ' + (hz / H * 100 + 30) + '%)' }}>
      {linhas}
    </svg>
  );
}

// ── a janela do site ─────────────────────────────────────────────────────
export function Site({ el, t, fps }) {
  const W = el.largura || 1400, H = el.altura || 820;
  const t0 = el.t || 0;
  const m = mola(t, t0, fps, { damping: 28, stiffness: 120, mass: 0.9 });
  const acento = el.corAcento || '#1f3cff';
  const emPe = H > W;                          // a janela do celular: o herói vai embaixo da manchete
  const u = (emPe ? W * 1.6 : W) / 100;
  const heroi = emPe ? { left: u * 3, right: u * 3, top: H * 0.5, height: H * 0.44 } : { right: u * 3, top: H * 0.2, width: W * 0.36, height: H * 0.7 };
  const texto = emPe ? { left: u * 3, top: H * 0.13, width: W - u * 6 } : { left: u * 4, top: H * 0.26, width: W * 0.5 };
  return (
    <div style={{ position: 'absolute', left: el.x, top: el.y, width: W, height: H, perspective: 1800, opacity: t >= t0 ? 1 : 0 }}>
      <div style={{ position: 'absolute', inset: 0, background: '#ffffff', borderRadius: u * 2, overflow: 'hidden', fontFamily: familia(el.fonte), color: '#111216',
        boxShadow: '0 2px 6px rgba(0,0,0,0.06), 0 60px 120px -40px rgba(0,0,0,0.35)', transformOrigin: '0% 50%',
        transform: 'translateX(' + (1 - m) * 260 + 'px) rotateY(' + (1 - m) * -24 + 'deg)' }}>
        <div style={{ display: 'flex', alignItems: 'center', padding: u * 2.6 + 'px ' + u * 4 + 'px', gap: u * 1.2 }}>
          <Marca logo={el.logo} tamanho={u * 3.2} tinta={el.logo ? undefined : '#111216'} />
          <div style={{ fontWeight: 800, fontSize: u * 2.4, letterSpacing: '-0.02em' }}>{el.marca || ''}</div>
          <div style={{ marginLeft: 'auto', display: 'flex', gap: u * 2.6, fontSize: u * 1.35, color: '#3b3d45' }}>
            {(el.menu || ['Início', 'Sobre', 'Serviços', 'Contato']).map((x) => <span key={x}>{x}</span>)}
          </div>
        </div>
        {/* O herói: a manchete à esquerda, a marca grande à direita num bloco da cor. */}
        <div style={{ position: 'absolute', ...texto, height: H * 0.6 }}>
          <Manchete el={{ texto: el.titulo, x: 0, y: 0, largura: texto.width, tamanho: u * (String(el.titulo || '').length > 34 ? 4.6 : 5.6), fonte: el.fonte, peso: 800,
            modo: 'desfoque', t: t0 + 0.45, passo: 3, cor: '#111216', corAcento: acento, destaque: el.destaque, entrelinha: 1.04 }} t={t} fps={fps} />
          {el.sub ? <div style={{ position: 'absolute', top: emPe ? H * 0.2 : H * 0.34, fontSize: u * 1.6, lineHeight: 1.4, color: '#5a5d66', width: texto.width * 0.8, opacity: entre(t, t0 + 1.0, t0 + 1.3) }}>{el.sub}</div> : null}
          <div style={{ position: 'absolute', top: emPe ? H * 0.27 : H * 0.46, display: 'inline-block', background: '#111216', color: '#fff', borderRadius: 99, padding: u * 1 + 'px ' + u * 2.2 + 'px',
            fontWeight: 700, fontSize: u * 1.5, transform: 'scale(' + mola(t, t0 + 1.2, fps) + ')', transformOrigin: '0% 50%' }}>{el.botao || 'Fale com a gente'}</div>
        </div>
        <div style={{ position: 'absolute', ...heroi, borderRadius: u * 1.6, overflow: 'hidden',
          background: 'radial-gradient(circle at 30% 25%, ' + mistura(acento, '#ffffff', 0.25) + ', ' + acento + ' 60%, ' + mistura(acento, '#000000', 0.25) + ')',
          transform: 'translateY(' + (1 - mola(t, t0 + 0.25, fps)) * 60 + 'px)', opacity: t >= t0 + 0.25 ? 1 : 0 }}>
          <div style={{ position: 'absolute', left: '50%', top: '50%', transform: 'translate(-50%,-50%) rotate(' + (-8 + 4 * Math.sin(t)) + 'deg)' }}>
            <Marca logo={el.logo} tamanho={W * (emPe ? 0.36 : 0.16)} tinta={'linear-gradient(160deg, #ffffff, ' + mistura(acento, '#ffffff', 0.6) + ')'} />
          </div>
        </div>
      </div>
    </div>
  );
}

// ── o print do site de verdade, em 3D ───────────────────────────────────
/* A peça `site` acima DESENHA um site genérico; esta mostra o print real
   (print-site.js), numa janela de navegador ou num celular, inclinados em
   perspectiva, com espessura, reflexo e a página rolando devagar.
   "proporcao" é altura/largura do print: é ela que diz quanto há para rolar.
   Só CSS 3D — o three.js entraria aqui só para o mesmo plano com textura. */
export function PrintSite({ el, t, fps }) {
  const W = el.largura || 1280, H = el.altura || (el.celular ? 900 : 820);
  const t0 = el.t || 0;
  const cel = !!el.celular;
  const m = mola(t, t0, fps, { damping: 30, stiffness: 90, mass: 1 });
  const inc = el.inclinacao ?? (cel ? 14 : 18);           // graus de giro em Y no repouso
  const deriva = Math.sin((t - t0) * 0.6) * 3;             // a câmera que respira, sem parar
  const rotY = lerp(-48, -inc, m) + deriva;
  const rotX = lerp(22, cel ? 6 : 9, m);
  const z = lerp(-700, 0, m);
  const barra = cel ? 0 : Math.round(H * 0.06);            // a barra do navegador
  const borda = cel ? Math.round(W * 0.045) : 0;           // a moldura do celular
  const telaW = W - borda * 2, telaH = H - barra - borda * 2;
  // A página rola de cima até o fim, com respiro parado nas duas pontas.
  const total = Math.max(0, telaW * (el.proporcao || 0) - telaH);
  const dur = el.duracao || 6;
  const k = el.rolar === false ? 0 : CURVAS_KIT.camera(entre(t, t0 + 1.0, t0 + dur - 0.6));
  const brilho = 30 + 50 * entre(t, t0, t0 + dur);
  const raio = cel ? Math.round(W * 0.14) : Math.round(H * 0.018);
  return (
    <div style={{ position: 'absolute', left: el.x || 0, top: el.y || 0, width: W, height: H, perspective: 2400, opacity: t >= t0 ? Math.min(1, m * 2) : 0 }}>
      <div style={{ position: 'absolute', inset: 0, transformStyle: 'preserve-3d',
        transform: 'translateZ(' + z + 'px) rotateX(' + rotX + 'deg) rotateY(' + rotY + 'deg)' }}>
        {/* A espessura: duas placas atrás, mais escuras — o aparelho tem corpo. */}
        {[18, 9].map((d) => (
          <div key={d} style={{ position: 'absolute', inset: 0, borderRadius: raio, background: d > 10 ? '#0b0c10' : '#1d1f26', transform: 'translateZ(' + -d + 'px)' }} />
        ))}
        <div style={{ position: 'absolute', inset: 0, borderRadius: raio, overflow: 'hidden', background: cel ? '#0e0f13' : '#f4f4f6',
          boxShadow: '0 80px 160px -40px rgba(0,0,0,0.55), 0 30px 60px -30px rgba(0,0,0,0.4)' }}>
          {cel ? null : (
            <div style={{ height: barra, display: 'flex', alignItems: 'center', gap: barra * 0.18, padding: '0 ' + barra * 0.4 + 'px', background: '#e9e9ee' }}>
              {['#ff5f57', '#febc2e', '#28c840'].map((c) => <div key={c} style={{ width: barra * 0.24, height: barra * 0.24, borderRadius: 99, background: c }} />)}
              <div style={{ marginLeft: barra * 0.4, flex: 1, maxWidth: W * 0.5, height: barra * 0.5, borderRadius: 99, background: '#fff', display: 'flex', alignItems: 'center',
                padding: '0 ' + barra * 0.35 + 'px', fontFamily: MONO(), fontSize: barra * 0.26, color: '#55585f', whiteSpace: 'nowrap', overflow: 'hidden' }}>
                {String(el.url || '').replace(/^https?:\/\//, '').replace(/\/$/, '')}
              </div>
            </div>
          )}
          <div style={{ position: 'absolute', left: borda, top: barra + borda, width: telaW, height: telaH, overflow: 'hidden', borderRadius: cel ? raio * 0.75 : 0, background: '#fff' }}>
            <Img src={staticFile(el.src)} style={{ position: 'absolute', left: 0, top: 0, width: telaW, transform: 'translateY(' + (-total * k).toFixed(1) + 'px)' }} />
          </div>
          {cel ? <div style={{ position: 'absolute', left: '50%', top: borda * 1.3, width: W * 0.28, height: borda * 1.1, marginLeft: -W * 0.14, borderRadius: 99, background: '#0e0f13' }} /> : null}
          {/* O reflexo que atravessa o vidro enquanto o aparelho gira. */}
          <div style={{ position: 'absolute', inset: 0, pointerEvents: 'none',
            background: 'linear-gradient(115deg, rgba(255,255,255,0) ' + (brilho - 18) + '%, rgba(255,255,255,0.16) ' + brilho + '%, rgba(255,255,255,0) ' + (brilho + 18) + '%)' }} />
        </div>
      </div>
    </div>
  );
}

export const MARCA = {
  orbe: Orbe, vetor: Vetor, manchete: Manchete, cromo: Cromo, cores: Cores, padrao: Padrao, particulas: Particulas,
  estrela: Estrela, cartoes: Cartoes, painel: Painel, esfera: Esfera, grade: Grade, site: Site, printsite: PrintSite,
};
