/* Squad Film · Golden Hour · Manifesto
   8 slides 1080×1350 (4:5) — carrossel Instagram.
   Estrutura: capa + 2 manifestos + 4 fotos full-bleed + fechamento.
   Tipografia: Britti Sans · só Light (300) e Regular (400), sem itálico.
   Acento: Squad Blue #00AEEF (ajustável via Tweaks). */

const { useState } = React;

const SLIDE_W = 1080;
const SLIDE_H = 1350;
const PAD = 80;
const TOTAL = 8;

/* --------------------------------------------------------------
   Atomics
-------------------------------------------------------------- */

function MonoTag({ children, color = 'var(--fg-muted)', size = 11, style }) {
  return (
    <div style={{
      fontFamily: 'var(--font-sans)',
      fontWeight: 400,
      fontSize: size,
      letterSpacing: '0.14em',
      textTransform: 'uppercase',
      color,
      lineHeight: 1.45,
      ...style,
    }}>{children}</div>
  );
}

function Monogram({ size = 28, variant = 'icon', style }) {
  const src = variant === 'mark'
    ? 'assets/squad-mark.png'
    : variant === 'light'
    ? 'assets/squad-mark.png'
    : 'assets/squad-mark-icon.png';
  const filter = variant === 'light' ? 'invert(1) hue-rotate(180deg)' : 'none';
  return (
    <img src={src} alt="Squad monogram"
      style={{height: size, width: 'auto', display:'block', filter, ...style}}/>
  );
}

function Wordmark({ size = 42, variant = 'light', style }) {
  const src = variant === 'color'
    ? 'assets/squad-wordmark-color.png'
    : variant === 'dark'
    ? 'assets/squad-wordmark-dark.png'
    : 'assets/squad-wordmark-light.png';
  return (
    <img src={src} alt="SQUAD /// FILM"
      style={{height: size, width: 'auto', display:'block', ...style}}/>
  );
}

/* Chrome: tl/tr/bl/br + contador central */
function Chrome({ tl, tr, bl, br, slide, total = TOTAL, accent, invert = false }) {
  const ink = invert ? 'rgba(0,0,0,0.55)' : 'var(--fg-muted)';
  return (
    <>
      {tl && (
        <div style={{position:'absolute', top:PAD, left:PAD, zIndex:3, maxWidth:320}}>
          <MonoTag color={ink}>{tl}</MonoTag>
        </div>
      )}
      {tr && (
        <div style={{position:'absolute', top:PAD, right:PAD, zIndex:3, textAlign:'right'}}>
          <MonoTag color={ink}>{tr}</MonoTag>
        </div>
      )}
      {bl && (
        <div style={{position:'absolute', bottom:PAD, left:PAD, zIndex:3, maxWidth:420}}>
          <MonoTag color={ink}>{bl}</MonoTag>
        </div>
      )}
      {br && (
        <div style={{position:'absolute', bottom:PAD, right:PAD, zIndex:3,
          display:'flex', alignItems:'center', gap:16}}>
          <MonoTag color={ink}>{br}</MonoTag>
          <Monogram size={24} variant={invert ? 'mark' : 'icon'}/>
        </div>
      )}
      {/* contador topo centro */}
      <div style={{
        position:'absolute', top:PAD, left:'50%', transform:'translateX(-50%)',
        zIndex:3, display:'flex', gap:10, alignItems:'center'
      }}>
        {Array.from({length: total}).map((_, i) => (
          <div key={i} style={{
            width: i === slide ? 28 : 8, height: 2,
            background: i === slide ? (invert ? '#000' : '#fff')
              : (invert ? 'rgba(0,0,0,0.22)' : 'rgba(255,255,255,0.22)'),
            transition: 'width 400ms var(--ease-expo)'
          }}/>
        ))}
      </div>
    </>
  );
}

/* Imagem full-bleed com fallback amber se faltar */
function Photo({ src, style, objectPosition = 'center' }) {
  return (
    <img src={src} alt=""
      style={{
        position:'absolute', inset:0, width:'100%', height:'100%',
        objectFit:'cover', objectPosition,
        ...style,
      }}/>
  );
}

/* Pattern /// sutil para áreas pretas */
function SlashPattern({ opacity = 0.05 }) {
  return (
    <div aria-hidden="true" style={{
      position:'absolute', inset:0, zIndex:0,
      backgroundImage:'url(assets/squad-pattern-slashes.png)',
      backgroundSize:'920px auto',
      backgroundPosition:'center',
      backgroundRepeat:'repeat',
      opacity,
      mixBlendMode:'screen',
      pointerEvents:'none',
    }}/>
  );
}

/* --------------------------------------------------------------
   SLIDE 01 — Capa · título
-------------------------------------------------------------- */
function Slide01({ state }) {
  const { accentColor: accent, year } = state;
  return (
    <div className="slide" data-screen-label="01 Capa">
      {/* foto de fundo totalmente visível */}
      <Photo src="assets/golden-01-tree-sea.png"
        objectPosition="center 40%"/>
      {/* gradient leve apenas nas extremidades para legibilidade */}
      <div aria-hidden="true" style={{
        position:'absolute', inset:0,
        background:'linear-gradient(180deg, rgba(0,0,0,0.35) 0%, rgba(0,0,0,0) 22%, rgba(0,0,0,0) 60%, rgba(0,0,0,0.55) 100%)',
        zIndex:1,
      }}/>

      <Chrome
        tl="SQUAD FILM · MANIFESTO"
        tr={`01 / EDIÇÃO ${year}`}
        bl="CINEMA DE PERFORMANCE"
        br="ARRASTE →"
        slide={0} accent={accent}
      />

      {/* Título central */}
      <div style={{
        position:'absolute', left: PAD, right: PAD, top: '38%',
        zIndex: 2,
      }}>
        <MonoTag color="rgba(255,255,255,0.55)" style={{marginBottom: 28}}>
          ◉ UM ENSAIO SOBRE A LUZ
        </MonoTag>
        <h1 style={{
          fontFamily:'var(--font-sans)',
          fontWeight: 300,
          fontSize: 168,
          lineHeight: 0.88,
          letterSpacing: '-0.045em',
          color:'#fff', margin:0,
        }}>
          Golden
          <br/>
          <span style={{color: accent, fontWeight: 400}}>Hour.</span>
        </h1>
        <div style={{
          marginTop: 32,
          fontFamily:'var(--font-sans)',
          fontWeight: 300,
          fontSize: 22,
          lineHeight: 1.45,
          color:'rgba(255,255,255,0.78)',
          maxWidth: 580,
        }}>
          A hora em que a luz deixa de ser iluminação <br/>
          e passa a ser <span style={{color:'#fff', fontWeight: 400}}>narrativa</span>.
        </div>
      </div>
    </div>
  );
}

/* --------------------------------------------------------------
   SLIDE 02 — Manifesto · abertura
-------------------------------------------------------------- */
function Slide02({ state }) {
  const { accentColor: accent } = state;
  return (
    <div className="slide" data-screen-label="02 Manifesto">
      <Chrome
        tl="§01 · A PREMISSA"
        tr="GOLDEN HOUR"
        bl=""
        br=""
        slide={1} accent={accent}
      />

      <div style={{
        position:'absolute', left: PAD, right: PAD, top: 260, bottom: PAD + 100,
        display:'flex', flexDirection:'column', justifyContent:'space-between',
        zIndex: 2,
      }}>
        <h2 style={{
          fontFamily:'var(--font-sans)',
          fontWeight: 300,
          fontSize: 78,
          lineHeight: 1.02,
          letterSpacing: '-0.025em',
          color:'#fff', margin:0,
          maxWidth: 900,
          textWrap:'pretty',
        }}>
          <span style={{color:'rgba(255,255,255,0.45)'}}>Todo dia ela volta,</span>
          <br/>
          <span style={{color:'rgba(255,255,255,0.45)'}}>entre dois silêncios —</span>
          <br/>
          <span style={{fontWeight: 400}}>quarenta e sete</span>
          <br/>
          <span style={{fontWeight: 400}}>minutos de </span>
          <span style={{fontWeight: 400, color: accent}}>luz.</span>
        </h2>

        <div style={{
          borderTop: '1px solid rgba(255,255,255,0.10)',
          paddingTop: 24,
          display:'flex', justifyContent:'space-between', alignItems:'flex-end', gap: 48,
        }}>
          <p style={{
            fontFamily:'var(--font-sans)',
            fontWeight: 300,
            fontSize: 16,
            lineHeight: 1.6,
            color:'var(--fg-muted)',
            margin: 0,
            maxWidth: 460,
          }}>
            Ela não é filtro, não é preset,
            não está no Lightroom.
            Ela acontece — ou não acontece —
            e a câmera só pode obedecer.
          </p>
          <MonoTag size={10} color="rgba(255,255,255,0.35)">
            OBSERVAÇÃO · CAMPO
          </MonoTag>
        </div>
      </div>
    </div>
  );
}

/* --------------------------------------------------------------
   SLIDE 03 — Manifesto · tese
-------------------------------------------------------------- */
function Slide03({ state }) {
  const { accentColor: accent } = state;
  return (
    <div className="slide" data-screen-label="03 Tese">
      <Chrome
        tl="§02 · A TESE"
        tr="GOLDEN HOUR"
        bl=""
        br=""
        slide={2} accent={accent}
      />

      <div style={{
        position:'absolute', left: PAD, right: PAD, top: 240,
        zIndex: 2, maxWidth: 920,
      }}>
        <MonoTag color={accent} style={{marginBottom: 36}}>
          ◉ POR QUE FILMAMOS NELA
        </MonoTag>

        <h2 style={{
          fontFamily:'var(--font-sans)',
          fontWeight: 300,
          fontSize: 68,
          lineHeight: 1.05,
          letterSpacing: '-0.02em',
          color:'#fff', margin:0,
        }}>
          <span style={{color:'rgba(255,255,255,0.48)'}}>A luz dura </span>
          <span style={{fontWeight: 400}}>diz onde</span>
          <br/>
          <span style={{color:'rgba(255,255,255,0.48)'}}>algo está.</span>
          <br/><br/>
          <span style={{color:'rgba(255,255,255,0.48)'}}>A luz da golden hour </span>
          <br/>
          <span style={{fontWeight: 400}}>diz </span>
          <span style={{fontWeight: 400, color: accent}}>por quê</span>
          <span style={{color:'rgba(255,255,255,0.48)'}}> você</span>
          <br/>
          <span style={{color:'rgba(255,255,255,0.48)'}}>está ali.</span>
        </h2>
      </div>

      <div style={{
        position:'absolute', left: PAD, right: PAD, bottom: 180,
        display:'grid', gridTemplateColumns:'1fr 1fr 1fr', gap: 40,
        zIndex: 2,
      }}>
        {[
          { t: 'INFORMA', v: 'A luz dura' },
          { t: 'REVELA',  v: 'A luz lateral' },
          { t: 'EMOCIONA', v: 'A golden hour' },
        ].map((d, i) => (
          <div key={i} style={{
            borderTop: i === 2 ? `1px solid ${accent}` : '1px solid rgba(255,255,255,0.15)',
            paddingTop: 16,
          }}>
            <MonoTag size={10} color={i === 2 ? accent : 'rgba(255,255,255,0.45)'}>
              {d.t}
            </MonoTag>
            <div style={{
              fontFamily:'var(--font-sans)',
              fontWeight: i === 2 ? 400 : 300,
              fontSize: 22, color: i === 2 ? '#fff' : 'rgba(255,255,255,0.68)',
              marginTop: 8, letterSpacing:'-0.005em',
            }}>{d.v}</div>
          </div>
        ))}
      </div>
    </div>
  );
}

/* --------------------------------------------------------------
   Foto full-bleed — slides 04/05/06/07
-------------------------------------------------------------- */
function PhotoSlide({
  idx, label, slideLabel, src, objectPosition = 'center',
  kicker, caption, state, invert = false, darken = 0.55,
}) {
  const { accentColor: accent, year } = state;
  return (
    <div className="slide" data-screen-label={slideLabel}>
      <Photo src={src} objectPosition={objectPosition}/>
      {/* gradient legibilidade — bottom apenas */}
      <div aria-hidden="true" style={{
        position:'absolute', inset:0,
        background: invert
          ? 'linear-gradient(180deg, rgba(255,240,210,0.0) 45%, rgba(0,0,0,0.55) 100%)'
          : `linear-gradient(180deg, rgba(0,0,0,${darken*0.35}) 0%, rgba(0,0,0,0.05) 35%, rgba(0,0,0,${darken}) 100%)`,
        zIndex: 1,
      }}/>

      <Chrome
        tl={label}
        tr={`${String(idx).padStart(2,'0')} / LUZ`}
        bl=""
        br=""
        slide={idx - 1} accent={accent}
      />

      {/* Legenda canto inferior esquerdo */}
      <div style={{
        position:'absolute', left: PAD, right: PAD, bottom: PAD + 40,
        zIndex: 2, display:'flex', justifyContent:'space-between',
        alignItems:'flex-end', gap: 48,
      }}>
        <div style={{maxWidth: 620}}>
          {kicker && (
            <MonoTag color={accent} style={{marginBottom: 14}}>
              {kicker}
            </MonoTag>
          )}
          <div style={{
            fontFamily:'var(--font-sans)',
            fontWeight: 300,
            fontSize: 34,
            lineHeight: 1.18,
            color:'#fff',
            letterSpacing:'-0.012em',
            textShadow:'0 2px 20px rgba(0,0,0,0.6)',
            textWrap:'pretty',
          }}>{caption}</div>
        </div>
        <div style={{textAlign:'right', flexShrink: 0, minWidth: 140}}>
          <MonoTag color="rgba(255,255,255,0.5)">FRAME · {String(idx - 3).padStart(2,'0')}/04</MonoTag>
          <MonoTag color="rgba(255,255,255,0.3)" size={10} style={{marginTop: 4}}>
            F/2.8 · 1/500 · ISO 200
          </MonoTag>
        </div>
      </div>
    </div>
  );
}

function Slide04({ state }) {
  return (
    <PhotoSlide
      idx={4}
      slideLabel="04 Frame · Árvore"
      label="FRAME 01 · ABERTURA"
      src="assets/golden-01-tree-sea.png"
      objectPosition="center 40%"
      kicker="17H42 · O LITORAL"
      caption={
        <>
          <span style={{color:'rgba(255,255,255,0.55)', fontWeight: 300}}>A árvore espera </span>
          <span>o dia inteiro </span>
          <span style={{color:'rgba(255,255,255,0.55)', fontWeight: 300}}>por este minuto.</span>
        </>
      }
      state={state}
    />
  );
}

function Slide05({ state }) {
  return (
    <PhotoSlide
      idx={5}
      slideLabel="05 Frame · Figura"
      label="FRAME 02 · CONTEMPLAÇÃO"
      src="assets/golden-02-figure-dusk.png"
      objectPosition="center center"
      kicker="18H09 · CERRADO"
      caption={
        <>
          <span style={{color:'rgba(255,255,255,0.55)', fontWeight: 300}}>Entre a última luz </span>
          <span>e a primeira sombra, </span>
          <span style={{color:'rgba(255,255,255,0.55)', fontWeight: 300}}>cabe uma pessoa.</span>
        </>
      }
      state={state}
    />
  );
}

function Slide06({ state }) {
  return (
    <PhotoSlide
      idx={6}
      slideLabel="06 Frame · Caminhada"
      label="FRAME 03 · MOVIMENTO"
      src="assets/golden-03-man-trees.png"
      objectPosition="center center"
      kicker="18H21 · ENTRE AS ÁRVORES"
      caption={
        <>
          <span>Quando ele atravessa,</span>
          <br/>
          <span style={{color:'rgba(255,255,255,0.55)', fontWeight: 300}}>a luz se lembra dele.</span>
        </>
      }
      state={state}
    />
  );
}

function Slide07({ state }) {
  return (
    <PhotoSlide
      idx={7}
      slideLabel="07 Frame · Contraluz"
      label="FRAME 04 · ENCERRAMENTO"
      src="assets/golden-04-backlit-field.png"
      objectPosition="center 55%"
      kicker="18H34 · CONTRA O SOL"
      caption={
        <>
          <span style={{color:'rgba(255,255,255,0.55)', fontWeight: 300}}>O contraluz não</span>
          <br/>
          <span style={{color:'rgba(255,255,255,0.55)', fontWeight: 300}}>mostra o corpo. </span>
          <span>Mostra o peso dele.</span>
        </>
      }
      state={state}
      darken={0.7}
    />
  );
}

/* --------------------------------------------------------------
   SLIDE 08 — Fechamento · assinatura
-------------------------------------------------------------- */
function Slide08({ state }) {
  const { accentColor: accent, year } = state;
  return (
    <div className="slide" data-screen-label="08 Assinatura">
      <Chrome
        tl="§03 · ASSINATURA"
        tr={`EDIÇÃO ${year}`}
        bl=""
        br=""
        slide={7} accent={accent}
      />

      {/* Monograma grande como marca d'água — logo novo */}
      <div style={{
        position:'absolute', top: 200, left: PAD, zIndex: 2,
        opacity: 0.18,
      }}>
        <img src="assets/squad-monogram-new.png" alt=""
          style={{height: 260, width: 'auto', display:'block'}}/>
      </div>

      <div style={{
        position:'absolute', left: PAD, right: PAD, top: 480,
        zIndex: 3, maxWidth: 920,
      }}>
        <h2 style={{
          fontFamily:'var(--font-sans)',
          fontWeight: 300,
          fontSize: 86,
          lineHeight: 0.98,
          letterSpacing: '-0.028em',
          color:'#fff', margin:0,
          textWrap:'pretty',
        }}>
          <span style={{color:'rgba(255,255,255,0.45)'}}>Quem filma </span>
          <br/>
          <span style={{fontWeight: 400}}>na hora certa</span>
          <br/>
          <span style={{color:'rgba(255,255,255,0.45)'}}>não </span>
          <span style={{fontWeight: 400}}>ilustra </span>
          <span style={{color:'rgba(255,255,255,0.45)'}}>—</span>
          <br/>
          <span style={{fontWeight: 400, color: accent}}>lembra.</span>
        </h2>
      </div>

      {/* Rodapé de assinatura */}
      <div style={{
        position:'absolute', left: PAD, right: PAD, bottom: PAD + 40,
        zIndex: 3, display:'flex', justifyContent:'space-between', alignItems:'flex-end',
        gap: 48, borderTop:'1px solid rgba(255,255,255,0.12)', paddingTop: 28,
      }}>
        <div>
          <img src="assets/squad-wordmark-new.png" alt="SQUAD FILM"
            style={{height: 44, width: 'auto', display:'block'}}/>
          <MonoTag color="rgba(255,255,255,0.5)" style={{marginTop: 14}}>
            @SQUAD.FILM · CINEMA DE PERFORMANCE
          </MonoTag>
        </div>
        <div style={{textAlign:'right'}}>
          <MonoTag color="rgba(255,255,255,0.55)">◉ AGENDAR DIREÇÃO</MonoTag>
          <MonoTag color="rgba(255,255,255,0.3)" size={10} style={{marginTop: 4}}>
            LINK NA BIO
          </MonoTag>
        </div>
      </div>
    </div>
  );
}

/* --------------------------------------------------------------
   Export bar — PDF / PNG / ZIP PNG
-------------------------------------------------------------- */
function ExportBar({ idx, total }) {
  const [status, setStatus] = useState('');
  const [busy, setBusy] = useState(false);

  /* Cache de data URLs: converte cada asset referenciado (img src / background-image)
     em data:image para que o html-to-image embuta tudo no PNG final. Sem isso,
     o canvas fica "tainted" ou recursos externos somem do ZIP. */
  const dataUrlCacheRef = React.useRef(new Map());

  async function urlToDataUrl(url) {
    const cache = dataUrlCacheRef.current;
    if (cache.has(url)) return cache.get(url);
    const resp = await fetch(url, {cache:'force-cache'});
    const blob = await resp.blob();
    const dataUrl = await new Promise((res, rej) => {
      const r = new FileReader();
      r.onload = () => res(r.result);
      r.onerror = rej;
      r.readAsDataURL(blob);
    });
    cache.set(url, dataUrl);
    return dataUrl;
  }

  async function inlineAssets(root) {
    // <img src="assets/..."> → converte para background-image div (mais estável no html-to-image)
    const imgs = root.querySelectorAll('img');
    await Promise.all([...imgs].map(async (img) => {
      const src = img.getAttribute('src');
      if (!src || src.startsWith('data:')) return;
      try {
        const dataUrl = await urlToDataUrl(src);
        // substitui <img> por <div> com background-image inline — contorna bugs de img cross-origin no html-to-image
        const cs = window.getComputedStyle(img);
        const replacement = document.createElement('div');
        replacement.style.cssText = img.style.cssText;
        replacement.style.backgroundImage = `url("${dataUrl}")`;
        replacement.style.backgroundSize = cs.objectFit === 'cover' ? 'cover' : (cs.objectFit === 'contain' ? 'contain' : '100% 100%');
        replacement.style.backgroundPosition = cs.objectPosition || 'center';
        replacement.style.backgroundRepeat = 'no-repeat';
        // Manter dimensões explícitas
        if (!replacement.style.width)  replacement.style.width  = cs.width;
        if (!replacement.style.height) replacement.style.height = cs.height;
        img.parentNode.replaceChild(replacement, img);
      } catch (e) { console.warn('inline img falhou', src, e); }
    }));
    // background-image: url(...) inline
    const all = root.querySelectorAll('*');
    await Promise.all([...all].map(async (el) => {
      const bg = el.style && el.style.backgroundImage;
      if (!bg || !bg.includes('url(')) return;
      const m = bg.match(/url\(["']?([^"')]+)["']?\)/);
      if (!m || m[1].startsWith('data:')) return;
      try {
        const dataUrl = await urlToDataUrl(m[1]);
        el.style.backgroundImage = `url("${dataUrl}")`;
      } catch (e) { console.warn('inline bg falhou', m[1], e); }
    }));
  }

  /* Captura canvas-based: renderiza html-to-image sobre um canvas que já tem
     as fotos de fundo desenhadas. Isso elimina o bug de <img> sumindo. */
  async function captureSlideAsBlob(slideIdx, pxRatio = 2) {
    const wraps = document.querySelectorAll('.slide-wrap');
    const target = wraps[slideIdx];
    if (!target) throw new Error('slide não encontrado');

    // 1) Clonar em container off-screen SEM SCALE — medições corretas 1080×1350
    const holder = document.createElement('div');
    holder.style.cssText = 'position:fixed;left:-99999px;top:0;width:1080px;height:1350px;background:#000;';
    const clone = target.cloneNode(true);
    clone.style.cssText = 'width:1080px;height:1350px;position:relative;transform:none;';
    holder.appendChild(clone);
    document.body.appendChild(holder);
    await new Promise(r => setTimeout(r, 50));

    // 2) Coletar <img> full-bleed (as que fazem cover do slide inteiro) e desenhá-las no canvas
    const holderRect = holder.getBoundingClientRect();
    const photoImgs = [];
    clone.querySelectorAll('img').forEach(img => {
      const src = img.getAttribute('src');
      if (!src) return;
      const cs = window.getComputedStyle(img);
      // Só fotos full-bleed: position:absolute + objectFit:cover + tamanho >= 500px
      if (cs.position !== 'absolute') return;
      if (cs.objectFit !== 'cover') return;
      const r = img.getBoundingClientRect();
      if (r.width < 500) return;
      photoImgs.push({
        src,
        x: r.left - holderRect.left,
        y: r.top - holderRect.top,
        w: r.width,
        h: r.height,
        pos: cs.objectPosition,
        el: img,
      });
    });

    // 3) Montar canvas 1080×1350 * pxRatio
    const W = 1080, H = 1350;
    const canvas = document.createElement('canvas');
    canvas.width  = W * pxRatio;
    canvas.height = H * pxRatio;
    const ctx = canvas.getContext('2d');
    ctx.fillStyle = '#000';
    ctx.fillRect(0, 0, canvas.width, canvas.height);

    // 4) Desenhar fotos no canvas (com cover + object-position)
    for (const p of photoImgs) {
      try {
        const dataUrl = await urlToDataUrl(p.src);
        const img = await new Promise((res, rej) => {
          const im = new Image();
          im.onload = () => res(im);
          im.onerror = rej;
          im.src = dataUrl;
        });
        const scale = Math.max(p.w / img.naturalWidth, p.h / img.naturalHeight);
        const drawW = img.naturalWidth * scale;
        const drawH = img.naturalHeight * scale;
        let px = 0.5, py = 0.5;
        const posParts = (p.pos || '50% 50%').split(/\s+/);
        if (posParts[0]) px = parsePos(posParts[0]);
        if (posParts[1]) py = parsePos(posParts[1]);
        const dx = p.x - (drawW - p.w) * px;
        const dy = p.y - (drawH - p.h) * py;
        ctx.drawImage(img, dx * pxRatio, dy * pxRatio, drawW * pxRatio, drawH * pxRatio);
        // Esconder do clone pra não duplicar no overlay
        p.el.style.visibility = 'hidden';
      } catch (e) { console.warn('fundo falhou', p.src, e); }
    }

    // 5) Tornar slide transparente e inlinar logos/monogramas restantes
    clone.querySelectorAll('.slide').forEach(s => s.style.background = 'transparent');
    const logos = clone.querySelectorAll('img');
    await Promise.all([...logos].map(async (img) => {
      const src = img.getAttribute('src');
      if (!src || src.startsWith('data:')) return;
      try { img.setAttribute('src', await urlToDataUrl(src)); } catch {}
    }));
    await new Promise(r => setTimeout(r, 100));

    // 6) Render overlay (texto + logos + gradients) por cima
    try {
      const overlayBlob = await htmlToImage.toBlob(clone, {
        width: 1080, height: 1350, pixelRatio: pxRatio,
        backgroundColor: null, cacheBust: false,
      });
      const overlayImg = await blobToImage(overlayBlob);
      ctx.drawImage(overlayImg, 0, 0, canvas.width, canvas.height);
    } finally {
      document.body.removeChild(holder);
    }

    return await new Promise(res => canvas.toBlob(res, 'image/png'));
  }

  function parsePos(v) {
    if (v === 'left' || v === 'top') return 0;
    if (v === 'right' || v === 'bottom') return 1;
    if (v === 'center') return 0.5;
    const m = v.match(/([\d.]+)%/);
    if (m) return parseFloat(m[1]) / 100;
    return 0.5;
  }

  function blobToImage(blob) {
    return new Promise((res, rej) => {
      const url = URL.createObjectURL(blob);
      const img = new Image();
      img.onload = () => { URL.revokeObjectURL(url); res(img); };
      img.onerror = rej;
      img.src = url;
    });
  }

  async function exportPNG() {
    if (busy) return;
    setBusy(true); setStatus(`Capturando slide ${String(idx+1).padStart(2,'0')}…`);
    try {
      const blob = await captureSlideAsBlob(idx);
      downloadBlob(blob, `squad-golden-hour-${String(idx+1).padStart(2,'0')}.png`);
      setStatus('PNG baixado.');
    } catch (e) {
      console.error(e);
      setStatus('Erro no PNG.');
    } finally {
      setBusy(false);
      setTimeout(() => setStatus(''), 2400);
    }
  }

  async function exportZIP(pxRatio = 2, label = 'ZIP') {
    if (busy) return;
    setBusy(true);
    try {
      const zip = new JSZip();
      for (let i = 0; i < total; i++) {
        setStatus(`${label} · ${String(i+1).padStart(2,'0')}/${String(total).padStart(2,'0')}…`);
        const blob = await captureSlideAsBlob(i, pxRatio);
        zip.file(`squad-golden-hour-${String(i+1).padStart(2,'0')}.png`, blob);
      }
      setStatus('Compactando ZIP…');
      const zipBlob = await zip.generateAsync({type:'blob'});
      const suffix = pxRatio >= 4 ? '-4k' : '';
      downloadBlob(zipBlob, `squad-golden-hour${suffix}.zip`);
      setStatus(`${label} baixado.`);
    } catch (e) {
      console.error(e);
      setStatus(`Erro no ${label}.`);
    } finally {
      setBusy(false);
      setTimeout(() => setStatus(''), 2400);
    }
  }

  function exportPDF() {
    setStatus('Abra a caixa de impressão → Salvar como PDF.');
    setTimeout(() => window.print(), 120);
    setTimeout(() => setStatus(''), 4200);
  }

  return (
    <div className="export-bar">
      <button className="export-btn" onClick={exportPDF} disabled={busy}>
        <span className="dot-accent"/> PDF · Todos
      </button>
      <button className="export-btn" onClick={exportPNG} disabled={busy}>
        <span className="dot-accent"/> PNG · Atual
      </button>
      <button className="export-btn" onClick={() => exportZIP(2, 'ZIP')} disabled={busy}>
        <span className="dot-accent"/> ZIP · {total} PNG
      </button>
      <button className="export-btn" onClick={() => exportZIP(4, 'ZIP 4K')} disabled={busy}
        style={{borderColor:'#00AEEF', color:'#00AEEF'}}>
        <span className="dot-accent"/> BAIXAR TUDO · 4K
      </button>
      {status && <div className="export-status">{status}</div>}
    </div>
  );
}

function downloadBlob(blob, filename) {
  const url = URL.createObjectURL(blob);
  const a = document.createElement('a');
  a.href = url; a.download = filename;
  document.body.appendChild(a); a.click();
  document.body.removeChild(a);
  setTimeout(() => URL.revokeObjectURL(url), 2000);
}

/* --------------------------------------------------------------
   Carousel shell
-------------------------------------------------------------- */
function Carousel({ state }) {
  const [idx, setIdx] = useState(0);
  const SLIDES = [Slide01, Slide02, Slide03, Slide04, Slide05, Slide06, Slide07, Slide08];
  const total = SLIDES.length;

  const go = (i) => setIdx(Math.max(0, Math.min(total - 1, i)));
  const prev = () => go(idx - 1);
  const next = () => go(idx + 1);

  React.useEffect(() => {
    const onKey = (e) => {
      if (e.key === 'ArrowLeft')  prev();
      if (e.key === 'ArrowRight') next();
    };
    window.addEventListener('keydown', onKey);
    return () => window.removeEventListener('keydown', onKey);
  }, [idx]);

  return (
    <div className="stage">
      <div className="slide-frame">
        <div className="slide-inner">
          <div className="slide-track" style={{
            width: total * SLIDE_W,
            transform: `translateX(${-idx * SLIDE_W}px)`
          }}>
            {SLIDES.map((S, i) => (
              <div key={i} className="slide-wrap">
                <S state={state}/>
              </div>
            ))}
          </div>
        </div>

        {idx > 0 && (
          <button className="nav nav-left" onClick={prev} aria-label="Anterior">‹</button>
        )}
        {idx < total - 1 && (
          <button className="nav nav-right" onClick={next} aria-label="Próximo">›</button>
        )}
      </div>

      <div className="dots">
        {SLIDES.map((_, i) => (
          <button key={i} className={'dot' + (i === idx ? ' active' : '')}
            onClick={() => go(i)} aria-label={`Ir para slide ${i+1}`}/>
        ))}
      </div>

      <div className="legend">
        <div className="mono-dna">SQUAD FILM · GOLDEN HOUR · 4:5 · 1080×1350</div>
        <div style={{fontFamily:'var(--font-sans)', fontWeight:400, fontSize: 11,
          letterSpacing:'0.12em', textTransform:'uppercase',
          color:'var(--fg-subtle)'}}>
          {String(idx+1).padStart(2,'0')} / {String(total).padStart(2,'0')}
        </div>
      </div>

      <ExportBar idx={idx} total={total}/>
    </div>
  );
}

Object.assign(window, { Carousel });
