<?php
require_once __DIR__ . '/auth.php';
require_login();
$user = current_user();
?>
<!DOCTYPE html>
<html lang="pt-BR">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Painel - Gerador de Banners</title>
<link href="https://cdn.jsdelivr.net/npm/tailwindcss@2.2.19/dist/tailwind.min.css" rel="stylesheet">
<link href="https://fonts.googleapis.com/css2?family=Montserrat:wght@400;600;700&display=swap" rel="stylesheet">
<style>
:root{--bg:#0b1220;--card:#0f1724;--muted:#9fb3d8}
body{font-family:'Montserrat',Arial,sans-serif;background:linear-gradient(180deg,#071022 0%,#081226 100%);color:#ecf2ff}
.sidebar{background:linear-gradient(180deg,#0d1220,#0c1522);box-shadow:inset -2px 0 0 rgba(255,255,255,0.02)}
.nav-item{color:rgba(255,255,255,0.9)}
.glass{background:rgba(255,255,255,0.03);border:1px solid rgba(255,255,255,0.04);backdrop-filter:blur(6px)}
.preview-img{width:220px;height:auto;border-radius:10px;box-shadow:0 10px 30px rgba(0,0,0,0.6)}
.small{font-size:.9rem;color:#b6c8e6}
.muted{color:#9fb3d8}
.active { background: linear-gradient(90deg, rgba(255,255,255,0.02), rgba(255,255,255,0.01)); }
.tmdb-item img{border-radius:6px}

/* Mobile sidebar */
#sidebar.mobile-open { 
  position: fixed;
  left: 0;
  top: 0;
  bottom: 0;
  transform: translateX(0);
  transition: transform .28s ease;
  z-index: 50;
  width: 80%;
  max-width: 320px;
}
#sidebar.mobile-closed {
  transform: translateX(-110%);
  transition: transform .28s ease;
}
@media (min-width: 768px) {
  #sidebar { transform: none !important; position: static; }
  #mobileOverlay { display: none !important; }
}
/* previews e canvases responsivos */
.preview-img { width: 100%; max-width: 220px; height: auto; border-radius:10px; box-shadow:0 10px 30px rgba(0,0,0,0.6); }
.preview-box { display:flex; flex-direction:column; align-items:center; gap:8px; }
.responsive-canvas { width:100%; height:auto; display:block; max-width:420px; } /* controle visual */
@media (min-width:1024px){ .responsive-canvas { max-width:360px; } }
</style>
</head>
<body class="min-h-screen">
  <div class="flex">
    <!-- Sidebar -->
    <aside id="sidebar" class="w-72 min-h-screen sidebar p-6 text-sm hidden md:block">
    <div class="mb-8 flex items-center gap-3">
    <div class="w-10 h-10 rounded-lg bg-indigo-600 flex items-center justify-center font-bold">LOB</div>
    <div>
    <div class="font-bold text-lg">LOB Conteúdos</div>
    <div class="text-xs muted">Gerador de Banners</div>
    </div>
    </div>

    <nav class="space-y-1">
    <button class="w-full flex items-center gap-3 p-3 rounded nav-item active" data-tab="dashboard" onclick="switchTab('dashboard', this)">
    <svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5 text-indigo-300" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 7h18M3 12h18M3 17h18"/></svg>
    <span class="label font-medium">Início</span>
    </button>

    <div class="mt-4 border-t border-white/5 pt-4">
    <div class="text-xs text-slate-400 uppercase mb-2">Banners</div>
    <button class="w-full flex items-center gap-3 p-3 rounded nav-item" data-tab="futebol" onclick="switchTab('futebol', this)">
    <svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5 text-yellow-400" viewBox="0 0 24 24" fill="currentColor"><path d="M12 2l2.39 4.85 5.36.78-3.88 3.78.92 5.35L12 14.77 6.21 17.76l.92-5.35L3.25 8.63l5.36-.78L12 2z"/></svg>
    <span class="label">Gerar Banners Futebol</span>
    </button>
    <button class="w-full flex items-center gap-3 p-3 rounded nav-item" data-tab="filmes" onclick="switchTab('filmes', this)">
    <svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5 text-cyan-400" viewBox="0 0 24 24" fill="currentColor"><path d="M4 6h16v12H4z" opacity=".9"/></svg>
    <span class="label">Gerar Banners Filmes e Séries</span>
    </button>

    <?php if (is_master()): ?>
    <a href="manage_users.php" class="block mt-3 p-3 rounded nav-item"><svg class="w-5 h-5 inline mr-2" viewBox="0 0 24 24" fill="currentColor"><path d="M12 12c2.21 0 4-1.79 4-4S14.21 4 12 4 8 5.79 8 8s1.79 4 4 4zm0 2c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z"/></svg>Gerenciar Revendedores</a>
    <?php endif; ?>
    </div>

    <div class="mt-6 border-t border-white/5 pt-4">
    <a href="logout.php" class="flex items-center gap-3 p-3 rounded nav-item">
    <svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5 text-red-400" viewBox="0 0 24 24" fill="currentColor"><path d="M16 13v-2H7V8l-5 4 5 4v-3zM20 3h-8v2h8v14h-8v2h8a2 2 0 0 0 2-2V5a2 2 0 0 0-2-2z"/></svg>
    <span class="label">Sair</span>
    </a>
    </div>
    </nav>

    <div class="mt-8 border-t border-white/5 pt-4 text-xs text-slate-400">
    <div>Usuário: <strong><?php echo htmlspecialchars($user['username']); ?></strong></div>
    <div class="mt-3 small">Salvar em <code>/uploads/</code></div>
    </div>
    </aside>

    <!-- Overlay para mobile -->
    <div id="mobileOverlay" class="fixed inset-0 bg-black/50 z-40 hidden"></div>

    <!-- Main -->
    <main class="flex-1 p-4 md:p-8">
    <header class="flex items-center justify-between mb-6">
    <div>
    <h1 id="pageTitle" class="text-2xl font-bold">Início</h1>
    <p class="small muted">Painel de controle — escolha uma ação no menu à esquerda</p>
    </div>
    <div class="flex items-center gap-4">
    <button id="mobileToggleBtn" class="md:hidden p-2 rounded bg-white/6 hover:bg-white/8" aria-label="Abrir menu">
    <svg xmlns="http://www.w3.org/2000/svg" class="w-6 h-6 text-white" fill="none" viewBox="0 0 24 24" stroke="currentColor">
    <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16"/>
    </svg>
    </button>
    <div class="text-right small muted hidden md:block">Conectado como <strong><?php echo htmlspecialchars($user['email']); ?></strong></div>
    </div>
    </header>

    <section id="content" class="space-y-6">
    <div id="dashboard" class="tab-section glass p-6 rounded-lg">
    <div class="grid grid-cols-1 md:grid-cols-3 gap-6">
    <div class="p-5 bg-gradient-to-b from-slate-800 to-slate-900 rounded-lg">
    <div class="text-sm muted">REVENDAS TOTAL</div>
    <div class="text-3xl font-bold mt-3">2</div>
    </div>
    <div class="p-5 bg-gradient-to-b from-slate-800 to-slate-900 rounded-lg">
    <div class="text-sm muted">MEUS TESTES GERADOS</div>
    <div class="text-3xl font-bold mt-3">2</div>
    </div>
    <div class="p-5 bg-gradient-to-b from-slate-800 to-slate-900 rounded-lg">
    <div class="text-sm muted">NOVOS USUÁRIOS</div>
    <div class="text-3xl font-bold mt-3">0</div>
    </div>
    </div>
    </div>

    <!-- FUTEBOL -->
    <div id="futebol" class="tab-section hidden glass p-6 rounded-lg">
    <div class="flex items-center justify-between mb-4">
    <div>
    <h2 class="text-xl font-semibold">Gerador de Banners - Futebol</h2>
    <p class="small muted">Puxe jogos da API (via proxy), gere banners verticais 1080×1920</p>
    </div>
    <div class="small muted">5 jogos por imagem</div>
    </div>

    <div class="grid grid-cols-1 lg:grid-cols-3 gap-6">
    <div class="lg:col-span-2 space-y-4">
    <label class="block text-sm">🔗 Fonte de dados: API protegida no servidor</label>
    <div class="grid grid-cols-1 md:grid-cols-2 gap-2">
    <input id="bannerTextFut" class="p-3 rounded bg-gray-900 text-white" placeholder="TEXTO DO BANNER" value="JOGOS DE HOJE" />
    <input id="accentFut" type="color" class="h-12 p-0 rounded" value="#00ff88" />
    </div>

    <div class="flex flex-col sm:flex-row gap-3 mt-2">
    <div class="flex-1">
    <input id="logoFut" type="file" accept="image/*" class="w-full" />
    <div id="logoFutInfo" class="small muted mt-1">Logo: nenhum arquivo</div>
    </div>
    <div class="flex-1">
    <input id="bgFut" type="file" accept="image/*" class="w-full" />
    <div id="bgFutInfo" class="small muted mt-1">Background: nenhum arquivo</div>
    </div>
    </div>

    <div class="flex flex-wrap gap-3 mt-3">
    <button id="btnFetchFut" class="bg-blue-600 hover:bg-blue-700 py-2 px-4 rounded">Buscar jogos (API)</button>
    <button id="btnPreviewFut" class="bg-green-600 hover:bg-green-700 py-2 px-4 rounded">Gerar Preview</button>
    <button id="btnSaveFut" class="bg-purple-600 hover:bg-purple-700 py-2 px-4 rounded">Gerar & Salvar</button>
    </div>

    <p id="infoFut" class="small mt-2 muted"></p>

    <div id="gamesListFut" class="mt-4 space-y-2 max-h-52 overflow-auto p-2 bg-gray-900 rounded"></div>
    </div>

    <div class="lg:col-span-1">
    <h3 class="font-semibold mb-2">Previews</h3>
    <div id="previewsFut" class="space-y-4"></div>
    </div>
    </div>
    </div>

    <!-- FILMES & SERIES -->
    <div id="filmes" class="tab-section hidden glass p-6 rounded-lg">
    <div class="flex items-center justify-between mb-4">
    <div>
    <h2 class="text-xl font-semibold">Gerador de Banners - Filmes & Séries</h2>
    <p class="small muted">Buscar no TMDB, selecionar o item e gerar um banner vertical com capa, título, sinopse e data.</p>
    </div>
    <div class="small muted">Busque por nome → selecione → gere</div>
    </div>

    <div class="grid grid-cols-1 lg:grid-cols-3 gap-6">
    <div class="lg:col-span-2 space-y-4">
    <!-- TMDB Search -->
    <label class="block text-sm">Pesquisar no TMDB</label>
    <div class="flex flex-col sm:flex-row gap-2">
    <input id="tmdbQuery" class="p-3 rounded bg-gray-900 text-white flex-1" placeholder="Digite o nome do filme ou série..." />
    <select id="tmdbType" class="p-3 rounded bg-gray-900 text-white">
    <option value="movie">Filme</option>
    <option value="tv">Série</option>
    </select>
    <button id="tmdbSearchBtn" class="bg-blue-600 hover:bg-blue-700 py-2 px-4 rounded">Buscar</button>
    </div>

    <div id="tmdbResults" class="mt-4 max-h-64 overflow-auto p-2 bg-gray-900 rounded"></div>

    <!-- Selected -->
    <div id="tmdbSelected" style="display:none;" class="mt-4">
    <h3 id="tmdbSelectedTitle" class="text-lg font-bold"></h3>
    <div class="small muted" id="tmdbSelectedMeta"></div>
    <!-- canvas configurado para 720x1280 por padrão; se quiser 1080x1920, altere width/height aqui e nos estilos/fontes -->
    <canvas id="tmdbCanvas" width="720" height="1280" class="responsive-canvas" style="margin-top:12px; width:100%; height:auto;"></canvas>
    <div class="flex flex-wrap gap-3 mt-3">
    <button id="tmdbGenerateBtn" class="bg-green-600 hover:bg-green-700 py-2 px-4 rounded">Gerar / Baixar</button>
    <button id="tmdbSaveBtn" class="bg-purple-600 hover:bg-purple-700 py-2 px-4 rounded">Salvar no servidor</button>
    </div>
    <p id="tmdbInfo" class="small mt-2 muted"></p>
    </div>

    <!-- Fallback / antigo: lista de títulos manual (mantido como opção) -->
    <label class="block text-sm mt-6">Ou: Lista de títulos (um por linha) — formato: Título - URL_da_capa (opcional)</label>
    <textarea id="listFilmes" rows="6" class="w-full p-3 rounded bg-gray-900 text-white" placeholder="Ex:\nThe Last Dance - https://.../cover1.jpg"></textarea>

    <div class="flex flex-col sm:flex-row gap-3 mt-2">
    <div class="flex-1">
    <input id="logoFilmes" type="file" accept="image/*" class="w-full" />
    <div id="logoFilmesInfo" class="small muted mt-1">Logo: nenhum arquivo</div>
    </div>
    <div class="flex-1">
    <input id="bgFilmes" type="file" accept="image/*" class="w-full" />
    <div id="bgFilmesInfo" class="small muted mt-1">Background: nenhum arquivo</div>
    </div>
    </div>

    <div class="flex flex-wrap gap-3 mt-3">
    <button id="btnPreviewFilmes" class="bg-green-600 hover:bg-green-700 py-2 px-4 rounded">Gerar Preview (Lista)</button>
    <button id="btnSaveFilmes" class="bg-purple-600 hover:bg-purple-700 py-2 px-4 rounded">Gerar & Salvar (Lista)</button>
    </div>

    <p id="infoFilmes" class="small mt-2 muted"></p>
    </div>

    <div class="lg:col-span-1">
    <h3 class="font-semibold mb-2">Previews</h3>
    <div id="previewsFilmes" class="space-y-4"></div>
    </div>
    </div>
    </div>

    </section>

    <footer class="mt-8 text-center small muted">Powered by LOB Conteúdos • Estrutura moderna</footer>
    </main>
  </div>

<script>
// --- Helpers & ajustes ---
// GLOBAL para logo/bg de filmes (usado pelo fluxo TMDB)
let logoFilmesData = null, bgFilmesData = null;

function switchTab(tabId, btn) {
  document.querySelectorAll('.tab-section').forEach(el => el.classList.add('hidden'));
  const el = document.getElementById(tabId);
  if (el) el.classList.remove('hidden');
  document.getElementById('pageTitle').textContent = (tabId === 'dashboard') ? 'Início' : (tabId === 'futebol' ? 'Gerar Banners - Futebol' : 'Gerar Banners - Filmes e Séries');
  document.querySelectorAll('aside nav button').forEach(b => b.classList.remove('active'));
  if (btn) btn.classList.add('active');
}
switchTab('dashboard');

function readFileAsDataURL(file) {
  return new Promise((resolve) => {
    if (!file) return resolve(null);
    const r = new FileReader();
    r.onload = e => resolve(e.target.result);
    r.readAsDataURL(file);
  });
}
function chunkArray(arr, size) { const out=[]; for (let i=0;i<arr.length;i+=size) out.push(arr.slice(i,i+size)); return out; }

// Melhor loadImageWithProxy: aceita data: URLs, URLs normais e proxy fallback
function loadImageWithProxy(url) {
  return new Promise((resolve) => {
    if (!url) return resolve(null);
    const img = new Image();
    // se for data URL, não setamos crossOrigin
    if (!url.startsWith('data:')) img.crossOrigin = 'anonymous';
    img.onload = () => resolve(img);
    img.onerror = () => {
    // fallback para proxy público (só se não for data URL)
    if (url.startsWith('data:')) return resolve(null);
    const proxy = 'https://corsproxy.io/?' + encodeURIComponent(url);
    const img2 = new Image();
    img2.crossOrigin = 'anonymous';
    img2.onload = () => resolve(img2);
    img2.onerror = () => resolve(null);
    img2.src = proxy;
    };
    img.src = url;
  });
}

function roundRect(ctx,x,y,w,h,r){ctx.beginPath();ctx.moveTo(x+r,y);ctx.arcTo(x+w,y,x+w,y+h,r);ctx.arcTo(x+w,y+h,x,y+h,r);ctx.arcTo(x,y+h,x,y,r);ctx.arcTo(x,y,x+w,y,r);ctx.closePath();ctx.fill();}
function drawCoverImage(ctx,img,x,y,w,h){const ratio=Math.max(w/img.width,h/img.height);const nw=img.width*ratio,nh=img.height*ratio;const dx=x-(nw-w)/2,dy=y-(nh-h)/2;ctx.drawImage(img,dx,dy,nw,nh);}

// ---- FUTEBOL (mantido) ----
let allGamesFut = []; let logoDataFut = null; let bgDataFut = null;
const bannerTextFut = document.getElementById('bannerTextFut');
const accentFut = document.getElementById('accentFut');
const logoFut = document.getElementById('logoFut');
const bgFut = document.getElementById('bgFut');
const btnFetchFut = document.getElementById('btnFetchFut');
const btnPreviewFut = document.getElementById('btnPreviewFut');
const btnSaveFut = document.getElementById('btnSaveFut');
const gamesListFut = document.getElementById('gamesListFut');
const previewsFut = document.getElementById('previewsFut');
const infoFut = document.getElementById('infoFut');

// Atualiza informação visual ao selecionar arquivos
document.getElementById('logoFut').addEventListener('change', async (e) => {
  const f = e.target.files[0];
  if (!f) {
    document.getElementById('logoFutInfo').textContent = 'Logo: nenhum arquivo';
    logoDataFut = null;
    return;
  }
  logoDataFut = await readFileAsDataURL(f);
  document.getElementById('logoFutInfo').textContent = `Logo: ${f.name}`;
});

document.getElementById('bgFut').addEventListener('change', async (e) => {
  const f = e.target.files[0];
  if (!f) {
    document.getElementById('bgFutInfo').textContent = 'Background: nenhum arquivo';
    bgDataFut = null;
    return;
  }
  bgDataFut = await readFileAsDataURL(f);
  document.getElementById('bgFutInfo').textContent = `Background: ${f.name}`;
});

// FIX: agora api_proxy.php encaminha query params — mantemos fetch simples
btnFetchFut.addEventListener('click', async () => {
  infoFut.textContent = '⏳ Buscando jogos (via servidor)...';
  gamesListFut.innerHTML = ''; previewsFut.innerHTML = ''; allGamesFut = [];
  try {
    const res = await fetch('api_proxy.php'); // proxy agora repassa parâmetros e token
    if (!res.ok) throw new Error('Erro: ' + res.status);
    const data = await res.json();
    console.log('API jogos =>', data);
    const arr = Array.isArray(data) ? data : (Array.isArray(data.jogos) ? data.jogos : []);
    // mantenho filtro "hoje" como antes
    const today = arr.filter(j => j.data_jogo && j.data_jogo.toLowerCase() === 'hoje');
    today.sort((a,b) => { const toM = s => { if(!s) return 0; const [hh,mm] = s.split(':'); return (parseInt(hh)||0)*60 + (parseInt(mm)||0); }; return toM(a.horario) - toM(b.horario); });
    allGamesFut = today;
    renderGamesListFut();
    infoFut.textContent = `✅ ${allGamesFut.length} jogo(s) encontrado(s).`;
  } catch (err) {
    console.error(err);
    infoFut.textContent = '❌ Erro: ' + err.message;
  }
});

function renderGamesListFut() {
  gamesListFut.innerHTML = '';
  if (!allGamesFut || allGamesFut.length === 0) { gamesListFut.innerHTML = '<div class="text-gray-400">Nenhum jogo</div>'; return; }
  allGamesFut.forEach((g, idx) => {
    const d = document.createElement('div'); d.className = 'p-3 bg-gray-900 rounded flex items-center gap-3';
    d.innerHTML = `
      <div class="w-12 h-12 flex-shrink-0">
        <img src="${g.img_time1_url||''}" style="width:48px;height:48px;object-fit:cover;border-radius:6px" onerror="this.style.display='none'">
      </div>
      <div class="flex-1">
        <div class="font-bold">${g.time1} <span class="text-gray-400">vs</span> ${g.time2}</div>
        <div class="small text-gray-400">${g.competicao} • ${g.horario}</div>
      </div>
      <div style="width:84px;display:flex;flex-direction:column;align-items:center;gap:6px;justify-content:center">
        ${g.img_canal_url ? `<img src="${g.img_canal_url}" style="width:64px;height:24px;object-fit:contain;border-radius:4px" onerror="this.style.display='none'">` : ''}
        <div class="small text-gray-400" style="text-align:center;max-width:76px;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;">${(g.canal||'')}</div>
      </div>
      <div class="w-12 h-12 flex-shrink-0">
        <img src="${g.img_time2_url||''}" style="width:48px;height:48px;object-fit:cover;border-radius:6px" onerror="this.style.display='none'">
      </div>
    `;
    gamesListFut.appendChild(d);
  });
}

// Resto do fluxo de preview/save futebol (mantido)
btnPreviewFut.addEventListener('click', async () => {
  previewsFut.innerHTML = '';
  if (!allGamesFut || allGamesFut.length === 0) { infoFut.textContent = 'Nenhum jogo para gerar preview.'; return; }
  infoFut.textContent = '⏳ Gerando preview(s)...';
  const groups = chunkArray(allGamesFut, 5);
  for (let i=0;i<groups.length;i++) {
    const canvas = await createCanvasForGames(groups[i], { logoData: logoDataFut, bgData: bgDataFut, bannerText: bannerTextFut.value, accent: accentFut.value });
    const dataUrl = canvas.toDataURL('image/png');
    const box = document.createElement('div'); box.className = 'p-3 bg-gray-900 rounded preview-box';
    box.innerHTML = `<div class="mb-2 font-bold">Imagem ${i+1} — ${groups[i].length} jogo(s)</div><img src="${dataUrl}" class="preview-img"><div class="mt-2"><a download="banner_jogos_${i+1}.png" href="${dataUrl}" class="inline-block mt-2 bg-blue-600 hover:bg-blue-700 py-1 px-3 rounded">⬇️ Baixar</a></div>`;
    previewsFut.appendChild(box);
  }
  infoFut.textContent = `✅ Preview(s) gerado(s): ${groups.length}`;
});

btnSaveFut.addEventListener('click', async () => {
  if (!allGamesFut || allGamesFut.length === 0) { infoFut.textContent = 'Nenhum jogo para gerar.'; return; }
  infoFut.textContent = '⏳ Gerando imagens e salvando no servidor...';
  const groups = chunkArray(allGamesFut, 5);
  previewsFut.innerHTML = '';
  try {
    for (let i=0;i<groups.length;i++) {
      const canvas = await createCanvasForGames(groups[i], { logoData: logoDataFut, bgData: bgDataFut, bannerText: bannerTextFut.value, accent: accentFut.value });
      const dataUrl = canvas.toDataURL('image/png');
      infoFut.textContent = `⏳ Salvando imagem ${i+1}/${groups.length}...`;
      const res = await fetch('save_image.php', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ image: dataUrl, name: `banner_jogos_${Date.now()}_${i+1}.png` })
      });
      const j = await res.json();
      if (!j.success) throw new Error(j.message || 'Erro salvar imagem');
      const box = document.createElement('div'); box.className = 'p-3 bg-gray-900 rounded preview-box';
      box.innerHTML = `<div class="mb-2 font-bold">Imagem ${i+1} salva</div><img src="${j.url}" class="preview-img"><div class="mt-2"><a class="bg-blue-600 inline-block py-1 px-3 rounded" href="${j.url}" target="_blank">🔗 Abrir</a></div>`;
      previewsFut.appendChild(box);
      await new Promise(r => setTimeout(r, 200));
    }
    infoFut.textContent = `✅ Concluído: ${groups.length} arquivo(s) salvos.`;
  } catch (err) { console.error(err); infoFut.textContent = '❌ Erro: ' + err.message; }
});

// Canvas generator para futebol (mantido)
async function createCanvasForGames(gamesChunk, options={}) {
  const W = 1080, H = 1920;
  const canvas = document.createElement('canvas'); canvas.width=W; canvas.height=H;
  const ctx = canvas.getContext('2d');

  // background
  if (options.bgData) {
    const bgImg = await loadImageWithProxy(options.bgData);
    if (bgImg) {
      const ratio = Math.max(W/bgImg.width, H/bgImg.height);
      ctx.drawImage(bgImg, (W-bgImg.width*ratio)/2, (H-bgImg.height*ratio)/2, bgImg.width*ratio, bgImg.height*ratio);
      ctx.fillStyle = 'rgba(0,0,0,0.35)'; ctx.fillRect(0,0,W,H);
    } else { ctx.fillStyle = '#0b1220'; ctx.fillRect(0,0,W,H); }
  } else { const g = ctx.createLinearGradient(0,0,0,H); g.addColorStop(0,'#101520'); g.addColorStop(1,'#0b1220'); ctx.fillStyle = g; ctx.fillRect(0,0,W,H); }

  // top
  const padding=80, logoW=140, logoH=140;
  if (options.logoData) {
    const limg = await loadImageWithProxy(options.logoData);
    if (limg) {
      // desenhar logo centralizado verticalmente num bloco à esquerda (como antes)
      ctx.drawImage(limg, padding, 48, logoW, logoH);
    }
  } else { ctx.fillStyle='rgba(255,255,255,0.04)'; ctx.fillRect(padding,48,logoW,logoH); }

  ctx.textAlign='left'; ctx.fillStyle='#fff'; ctx.font='700 48px Montserrat, Arial';
  const titleX = padding + (options.logoData ? (logoW + 24) : 0);
  ctx.fillText((options.bannerText||'').toUpperCase(), titleX, 120);

  const startY=220; const availableHeight = H - startY - 140;
  const perGameH = Math.floor(availableHeight / Math.max(1, gamesChunk.length));
  const accent = options.accent || '#00ff88';

  for (let i=0;i<gamesChunk.length;i++) {
    const g = gamesChunk[i];
    const y = startY + i*perGameH + 12;
    ctx.fillStyle='rgba(0,0,0,0.42)'; roundRect(ctx,60,y,W-120,perGameH-18,14);
    ctx.fillStyle = accent; ctx.fillRect(72, y + 12, 260, 6);

    const logoSize = Math.min(140, perGameH - 64);
    const leftX = 100, rightX = W - 100 - logoSize;
    const img1 = await loadImageWithProxy(g.img_time1_url); if (img1) drawCoverImage(ctx,img1,leftX,y+28,logoSize,logoSize); else { ctx.fillStyle='rgba(255,255,255,0.04)'; ctx.fillRect(leftX,y+28,logoSize,logoSize); }
    const img2 = await loadImageWithProxy(g.img_time2_url); if (img2) drawCoverImage(ctx,img2,rightX,y+28,logoSize,logoSize); else { ctx.fillStyle='rgba(255,255,255,0.04)'; ctx.fillRect(rightX,y+28,logoSize,logoSize); }

    ctx.fillStyle='#fff'; ctx.font='700 28px Montserrat'; ctx.textAlign='left';
    const nameLeftX = leftX + logoSize + 20; ctx.fillText((g.time1||'').toUpperCase(), nameLeftX, y + 64);
    ctx.textAlign='right'; const nameRightX = rightX - 20; ctx.fillText((g.time2||'').toUpperCase(), nameRightX, y + 64);

    ctx.textAlign='center'; ctx.fillStyle=accent; ctx.font='700 36px Montserrat';
    ctx.fillText(g.horario || '', W/2, y + perGameH/2 - 6);

    // --- novo: desenhar canal (logo ou texto) logo abaixo/à frente do horário ---
    try {
      if (g.img_canal_url) {
        const chImg = await loadImageWithProxy(g.img_canal_url);
        if (chImg) {
          // ajusta largura/altura do ícone do canal conforme o espaço
          const chW = Math.min(220, Math.floor(perGameH * 0.3)); // largura do canal
          const chH = Math.floor(chW * (chImg.height / chImg.width));
          const chX = Math.floor((W - chW) / 2);
          const chY = y + perGameH - chH - 18; // perto da base do bloco do jogo
          ctx.drawImage(chImg, chX, chY, chW, chH);
        } else if (g.canal) {
          ctx.fillStyle = '#cfe6ff';
          ctx.font = '600 20px Montserrat';
          ctx.textAlign = 'center';
          ctx.fillText((g.canal||''), W/2, y + perGameH - 34);
        }
      } else if (g.canal) {
        ctx.fillStyle = '#cfe6ff';
        ctx.font = '600 20px Montserrat';
        ctx.textAlign = 'center';
        ctx.fillText((g.canal||''), W/2, y + perGameH - 34);
      }
    } catch (e) {
      // não interrompe a renderização se imagem falhar
      console.warn('Erro ao carregar imagem do canal:', e);
    }
  }

  ctx.font='14px Montserrat'; ctx.fillStyle='rgba(255,255,255,0.7)'; ctx.textAlign='right'; ctx.fillText('Gerado por LOB Conteúdos', W-72, H-44);
  return canvas;
}

// ---- TMDB / FILMES & SÉRIES ----
// Helpers TMDB proxy
function posterUrlProxy(path, size='w780') {
  return `tmdb_proxy.php?action=poster&path=${encodeURIComponent(path)}&size=${encodeURIComponent(size)}`;
}

async function tmdbSearch(query, type='movie') {
  const url = `tmdb_proxy.php?action=search&type=${encodeURIComponent(type)}&q=${encodeURIComponent(query)}`;
  const res = await fetch(url);
  if (!res.ok) throw new Error('Erro na busca TMDB: ' + res.status);
  return res.json();
}

async function tmdbDetails(id, type='movie') {
  const url = `tmdb_proxy.php?action=details&type=${encodeURIComponent(type)}&id=${encodeURIComponent(id)}`;
  const res = await fetch(url);
  if (!res.ok) throw new Error('Erro nos detalhes TMDB: ' + res.status);
  return res.json();
}

function escapeHtml(s){ return (s+'').replace(/[&<>"']/g, c => ({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[c])); }

const tmdbQuery = document.getElementById('tmdbQuery');
const tmdbType = document.getElementById('tmdbType');
const tmdbSearchBtn = document.getElementById('tmdbSearchBtn');
const tmdbResults = document.getElementById('tmdbResults');
const tmdbSelected = document.getElementById('tmdbSelected');
const tmdbSelectedTitle = document.getElementById('tmdbSelectedTitle');
const tmdbSelectedMeta = document.getElementById('tmdbSelectedMeta');
const tmdbCanvas = document.getElementById('tmdbCanvas');
const tmdbGenerateBtn = document.getElementById('tmdbGenerateBtn');
const tmdbSaveBtn = document.getElementById('tmdbSaveBtn');
const tmdbInfo = document.getElementById('tmdbInfo');

const tmdbSearchLimit = 12;
let selectedTmdbDetails = null;

// Attaches uploaded logo/bg files for TMDB flow
document.getElementById('logoFilmes').addEventListener('change', async (e) => {
  const f = e.target.files[0];
  if (!f) {
    document.getElementById('logoFilmesInfo').textContent = 'Logo: nenhum arquivo';
    logoFilmesData = null;
    return;
  }
  logoFilmesData = await readFileAsDataURL(f);
  document.getElementById('logoFilmesInfo').textContent = `Logo: ${f.name}`;
});

document.getElementById('bgFilmes').addEventListener('change', async (e) => {
  const f = e.target.files[0];
  if (!f) {
    document.getElementById('bgFilmesInfo').textContent = 'Background: nenhum arquivo';
    bgFilmesData = null;
    return;
  }
  bgFilmesData = await readFileAsDataURL(f);
  document.getElementById('bgFilmesInfo').textContent = `Background: ${f.name}`;
});

tmdbSearchBtn.addEventListener('click', async () => {
  const q = tmdbQuery.value.trim();
  const type = tmdbType.value;
  tmdbResults.innerHTML = '';
  tmdbSelected.style.display = 'none';
  selectedTmdbDetails = null;
  if (!q) { tmdbResults.innerText = 'Digite algo para buscar'; return; }
  tmdbInfo.textContent = '⏳ Buscando...';
  try {
    const r = await tmdbSearch(q, type);
    renderTmdbResults(r, type);
    tmdbInfo.textContent = '';
  } catch (err) {
    console.error(err);
    tmdbInfo.textContent = '❌ ' + err.message;
  }
});

function renderTmdbResults(data, type) {
  tmdbResults.innerHTML = '';
  const list = data.results || [];
  if (!list.length) { tmdbResults.innerText = 'Nenhum resultado'; return; }
  list.slice(0, tmdbSearchLimit).forEach(item => {
    const title = item.title || item.name || '';
    const date = item.release_date || item.first_air_date || '';
    const poster = item.poster_path ? posterUrlProxy(item.poster_path, 'w200') : '';
    const div = document.createElement('div');
    div.className = 'tmdb-item p-2 rounded flex items-center gap-3 mb-2';
    div.style.background = 'rgba(255,255,255,0.02)';
    div.innerHTML = `
    <div style="width:64px;height:96px;flex-shrink:0">
    ${poster ? `<img src="${poster}" style="width:100%;height:100%;object-fit:cover" />` : `<div style="width:100%;height:100%;background:#333;border-radius:6px"></div>`}
    </div>
    <div style="flex:1">
    <div class="font-bold">${escapeHtml(title)}</div>
    <div class="small muted">${escapeHtml(date)}</div>
    </div>
    <div style="flex-shrink:0">
    <button class="tmdb-select-btn bg-indigo-600 hover:bg-indigo-700 py-1 px-3 rounded text-sm" data-id="${item.id}" data-type="${type}">Selecionar</button>
    </div>
    `;
    tmdbResults.appendChild(div);
  });
}

document.addEventListener('click', async function(e){
  if (e.target && e.target.classList.contains('tmdb-select-btn')) {
    const id = e.target.getAttribute('data-id');
    const type = e.target.getAttribute('data-type');
    tmdbInfo.textContent = '⏳ Carregando detalhes...';
    try {
      const details = await tmdbDetails(id, type);
      selectedTmdbDetails = { ...details, _type: type };
      showSelectedTmdb(details, type);
      tmdbInfo.textContent = '';
    } catch (err) {
      console.error(err);
      tmdbInfo.textContent = '❌ ' + err.message;
    }
  }
});

async function showSelectedTmdb(details, type) {
  tmdbSelected.style.display = 'block';
  tmdbSelectedTitle.innerText = details.title || details.name || '';
  const date = details.release_date || details.first_air_date || '';
  tmdbSelectedMeta.innerText = (details.vote_average ? ('⭐ ' + details.vote_average + ' • ') : '') + (date ? ('Lançamento: ' + date) : '');
  // passa logo/bg atuais (podem ser dataURLs) para o draw
  await drawMovieBanner(details, { logoData: logoFilmesData, bgData: bgFilmesData });
}

// Draw banner vertical para o item TMDB (agora usa logo/bg)
async function drawMovieBanner(details, opts={}) {
  const canvas = tmdbCanvas;
  const ctx = canvas.getContext('2d');
  const W = canvas.width, H = canvas.height;

  // background: se usuário enviou bg, usar; senão gradiente
  ctx.clearRect(0,0,W,H);
  if (opts.bgData) {
    const bgImg = await loadImageWithProxy(opts.bgData);
    if (bgImg) {
      const ratio = Math.max(W/bgImg.width, H/bgImg.height);
      ctx.drawImage(bgImg, (W-bgImg.width*ratio)/2, (H-bgImg.height*ratio)/2, bgImg.width*ratio, bgImg.height*ratio);
      ctx.fillStyle = 'rgba(0,0,0,0.35)'; ctx.fillRect(0,0,W,H);
    } else {
      const grad = ctx.createLinearGradient(0,0,0,H); grad.addColorStop(0,'#0f1724'); grad.addColorStop(1,'#071022'); ctx.fillStyle = grad; ctx.fillRect(0,0,W,H);
    }
  } else {
    const grad = ctx.createLinearGradient(0,0,0,H); grad.addColorStop(0,'#0f1724'); grad.addColorStop(1,'#071022'); ctx.fillStyle = grad; ctx.fillRect(0,0,W,H);
  }

  // Poster (ocupar top ~55%)
  const posterPath = details.poster_path;
  let posterImg = null;
  if (posterPath) {
    try {
      posterImg = await loadImageWithProxy(posterUrlProxy(posterPath, 'w780'));
    } catch(e){ posterImg = null; }
  }

  const padding = 36;
  const posterH = Math.floor(H * 0.55);
  if (posterImg) {
    const ratio = posterImg.width / posterImg.height;
    let dw = W - padding*2;
    let dh = Math.floor(dw / ratio);
    if (dh > posterH) {
      dh = posterH;
      dw = Math.floor(dh * ratio);
    }
    const dx = Math.floor((W - dw)/2);
    const dy = padding;
    ctx.drawImage(posterImg, dx, dy, dw, dh);
  } else {
    ctx.fillStyle = '#222';
    roundRect(ctx, padding, padding, W - padding*2, posterH, 12);
  }

  // Se houver logo enviado, centralizar logo no topo (sobre o poster, pequeno)
  if (opts.logoData) {
    try {
      const logoImg = await loadImageWithProxy(opts.logoData);
      if (logoImg) {
        const logoW = Math.min(240, Math.floor(W * 0.35));
        const logoH = Math.floor(logoW * (logoImg.height / logoImg.width));
        const lx = Math.floor((W - logoW)/2);
        const ly = padding + 12;
        // desenha com sombra leve
        ctx.save();
        ctx.globalAlpha = 0.95;
        ctx.drawImage(logoImg, lx, ly, logoW, logoH);
        ctx.restore();
      }
    } catch(e) { /* ignora */ }
  }

  // Title
  ctx.fillStyle = '#fff';
  ctx.font = 'bold 36px Montserrat, Arial';
  ctx.textAlign = 'center';
  const title = details.title || details.name || '';
  wrapTextCentered(ctx, title, W/2, posterH + padding + 36, W - padding*4, 40);

  // Release date
  ctx.fillStyle = '#cfe6ff';
  ctx.font = '16px Montserrat';
  ctx.textAlign = 'center';
  const date = details.release_date || details.first_air_date || '';
  if (date) ctx.fillText(`Lançamento: ${date}`, W/2, posterH + padding + 120);

  // Sinopse (overview)
  ctx.fillStyle = '#e9f2ff';
  ctx.font = '16px Montserrat';
  ctx.textAlign = 'left';
  const overview = details.overview || '';
  const synopsisX = padding;
  const synopsisY = posterH + padding + 140;
  const synopsisW = W - padding*2;
  const lineHeight = 22;
  const availableH = H - synopsisY - 120;
  wrapText(ctx, overview, synopsisX, synopsisY, synopsisW, lineHeight, availableH);

  // Texto fixo
  ctx.fillStyle = '#ffdd00';
  ctx.font = 'bold 22px Montserrat';
  ctx.textAlign = 'center';
  ctx.fillText('O melhor do stream aqui', W/2, H - 60);

  // small credit
  ctx.fillStyle = 'rgba(255,255,255,0.6)';
  ctx.font = '12px Montserrat';
  ctx.textAlign = 'right';
  ctx.fillText('Gerado por LOB Conteúdos', W - 24, H - 24);
}

function wrapText(ctx, text, x, y, maxWidth, lineHeight, maxHeight) {
  const words = text.split(' ');
  let line = '';
  let curY = y;
  for (let n = 0; n < words.length; n++) {
    const testLine = line + words[n] + ' ';
    const metrics = ctx.measureText(testLine);
    if (metrics.width > maxWidth && n > 0) {
      ctx.fillText(line.trim(), x, curY);
      line = words[n] + ' ';
      curY += lineHeight;
      if (maxHeight && (curY - y) > maxHeight) {
        ctx.fillText('...', x, curY);
        return;
      }
    } else {
      line = testLine;
    }
  }
  if (line) ctx.fillText(line.trim(), x, curY);
}

function wrapTextCentered(ctx, text, cx, y, maxWidth, lineHeight) {
  const words = text.split(' ');
  let line = '';
  let curY = y;
  const lines = [];
  for (let n=0;n<words.length;n++){
    const testLine = line + words[n] + ' ';
    if (ctx.measureText(testLine).width > maxWidth && line !== '') {
      lines.push(line.trim());
      line = words[n] + ' ';
    } else {
      line = testLine;
    }
  }
  if (line) lines.push(line.trim());
  ctx.textAlign = 'center';
  lines.forEach((ln, idx) => {
    ctx.fillText(ln, cx, curY + (idx * lineHeight));
  });
}

// Download / salvar TMDB
tmdbGenerateBtn.addEventListener('click', () => {
  if (!selectedTmdbDetails) return alert('Selecione um item primeiro.');
  const canvas = tmdbCanvas;
  const dataUrl = canvas.toDataURL('image/png');
  const a = document.createElement('a');
  const safeName = (selectedTmdbDetails.title || selectedTmdbDetails.name || 'banner').replace(/[^\w\-]+/g,'_');
  a.href = dataUrl;
  a.download = `${safeName}.png`;
  document.body.appendChild(a);
  a.click();
  a.remove();
});

tmdbSaveBtn.addEventListener('click', async () => {
  if (!selectedTmdbDetails) return alert('Selecione um item primeiro.');
  tmdbInfo.textContent = '⏳ Salvando imagem...';
  try {
    const canvas = tmdbCanvas;
    const dataUrl = canvas.toDataURL('image/png');
    const res = await fetch('save_image.php', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ image: dataUrl, name: `tmdb_${Date.now()}.png` })
    });
    const j = await res.json();
    if (!j.success) throw new Error(j.message || 'Erro ao salvar');
    tmdbInfo.innerHTML = `✅ Salvo: <a href="${j.url}" target="_blank" class="underline">Abrir imagem</a>`;
  } catch (err) {
    console.error(err);
    tmdbInfo.textContent = '❌ ' + err.message;
  }
});

// ---- Fluxo antigo de lista de filmes (mantido) ----
const btnPreviewFilmes = document.getElementById('btnPreviewFilmes');
const btnSaveFilmes = document.getElementById('btnSaveFilmes');
const listFilmes = document.getElementById('listFilmes');
const previewsFilmes = document.getElementById('previewsFilmes');
const infoFilmes = document.getElementById('infoFilmes');

function parseListItems(text) {
  const lines = text.split('\n').map(l => l.trim()).filter(Boolean);
  return lines.map(l => {
    const parts = l.split(' - ');
    return { title: parts[0].trim(), cover: parts[1] ? parts[1].trim() : null };
  });
}

async function createCanvasForMovies(items, options={}) {
  const W=1080,H=1920,canvas=document.createElement('canvas');canvas.width=W;canvas.height=H;const ctx=canvas.getContext('2d');
  if (options.bgData) { const bg = await loadImageWithProxy(options.bgData); if (bg){ const ratio=Math.max(W/bg.width,H/bg.height); ctx.drawImage(bg,(W-bg.width*ratio)/2,(H-bg.height*ratio)/2,bg.width*ratio,bg.height*ratio); ctx.fillStyle='rgba(0,0,0,0.35)'; ctx.fillRect(0,0,W,H);} else { ctx.fillStyle='#0b1220'; ctx.fillRect(0,0,W,H); } }
  else { const g=ctx.createLinearGradient(0,0,0,H); g.addColorStop(0,'#0f1724'); g.addColorStop(1,'#071022'); ctx.fillStyle=g; ctx.fillRect(0,0,W,H); }
  ctx.fillStyle='#fff'; ctx.font='700 48px Montserrat'; ctx.fillText((options.bannerText||'').toUpperCase(),80,120);
  const hero = items[0];
  if (hero) { if (hero.cover) { const img = await loadImageWithProxy(hero.cover); if (img) drawCoverImage(ctx,img,80,160,W-160,720); } ctx.fillStyle='#fff'; ctx.font='700 36px Montserrat'; ctx.fillText(hero.title||'',100,940); }
  const thumbY=1000, thumbW=(W-200)/2-10, thumbH=260;
  for (let i=1;i<Math.min(items.length,5);i++){ const it=items[i]; const col=(i-1)%2; const row=Math.floor((i-1)/2); const x=100+col*(thumbW+20); const y=thumbY+row*(thumbH+20); ctx.fillStyle='rgba(0,0,0,0.45)'; roundRect(ctx,x,y,thumbW,thumbH,10); if (it.cover){ const img = await loadImageWithProxy(it.cover); if (img) drawCoverImage(ctx,img,x+10,y+10,thumbW-20,thumbH-20); } ctx.fillStyle='#fff'; ctx.font='700 20px Montserrat'; ctx.fillText(it.title||'', x+16, y+thumbH-16); }
  ctx.font='14px Montserrat'; ctx.fillStyle='rgba(255,255,255,0.7)'; ctx.textAlign='right'; ctx.fillText('Gerado por LOB Conteúdos', W-72, H-44);
  return canvas;
}

btnPreviewFilmes.addEventListener('click', async () => {
  previewsFilmes.innerHTML=''; infoFilmes.textContent='';
  const items = parseListItems(listFilmes.value);
  if (items.length===0){ infoFilmes.textContent='Informe ao menos 1 título'; return; }
  infoFilmes.textContent='⏳ Gerando preview...';
  const canvas = await createCanvasForMovies(items, { bgData: bgFilmesData, logoData: logoFilmesData, bannerText: 'DESTAQUES' });
  const dataUrl = canvas.toDataURL('image/png');
  const box = document.createElement('div'); box.className='p-3 bg-gray-900 rounded preview-box'; box.innerHTML=`<img src='${dataUrl}' class='preview-img'><div class='mt-2'><a download='banner_filmes.png' href='${dataUrl}' class='inline-block mt-2 bg-blue-600 hover:bg-blue-700 py-1 px-3 rounded'>⬇️ Baixar</a></div>`;
  previewsFilmes.appendChild(box);
  infoFilmes.textContent='✅ Preview gerado.';
});

btnSaveFilmes.addEventListener('click', async () => {
  const items = parseListItems(listFilmes.value);
  if (items.length===0){ infoFilmes.textContent='Informe ao menos 1 título'; return; }
  infoFilmes.textContent='⏳ Gerando e salvando...';
  try {
    const canvas = await createCanvasForMovies(items, { bgData: bgFilmesData, logoData: logoFilmesData, bannerText: 'DESTAQUES' });
    const dataUrl = canvas.toDataURL('image/png');
    const res = await fetch('save_image.php', { method:'POST', headers:{'Content-Type':'application/json'}, body:JSON.stringify({ image:dataUrl, name:`banner_filmes_${Date.now()}.png` }) });
    const j = await res.json();
    if (!j.success) throw new Error(j.message||'Erro salvar');
    const box = document.createElement('div'); box.className='p-3 bg-gray-900 rounded preview-box'; box.innerHTML=`<div class='mb-2 font-bold'>Imagem salva</div><img src='${j.url}' class='preview-img'><div class='mt-2'><a class='bg-blue-600 inline-block py-1 px-3 rounded' href='${j.url}' target='_blank'>🔗 Abrir</a></div>`;
    previewsFilmes.appendChild(box); infoFilmes.textContent='✅ Salvo com sucesso.';
  } catch (err) { console.error(err); infoFilmes.textContent='❌ Erro: ' + err.message; }
});

// SIDEBAR MOBILE TOGGLE
const sidebar = document.getElementById('sidebar');
const mobileToggleBtn = document.getElementById('mobileToggleBtn');
const mobileOverlay = document.getElementById('mobileOverlay');

function openMobileSidebar() {
  sidebar.classList.remove('hidden');
  sidebar.classList.remove('mobile-closed');
  sidebar.classList.add('mobile-open');
  mobileOverlay.classList.remove('hidden');
  document.body.style.overflow = 'hidden'; // trava scroll do body
}

function closeMobileSidebar() {
  sidebar.classList.add('mobile-closed');
  sidebar.classList.remove('mobile-open');
  mobileOverlay.classList.add('hidden');
  // no desktop, não esconder o sidebar
  if (window.innerWidth >= 768) {
    sidebar.classList.remove('hidden');
    document.body.style.overflow = '';
    return;
  }
  // pequena espera para animação antes de esconder completamente
  setTimeout(() => {
    if (!sidebar.classList.contains('mobile-open')) sidebar.classList.add('hidden');
    document.body.style.overflow = '';
  }, 300);
}

mobileToggleBtn?.addEventListener('click', () => {
  if (sidebar.classList.contains('mobile-open')) closeMobileSidebar();
  else openMobileSidebar();
});
mobileOverlay?.addEventListener('click', closeMobileSidebar);

// opcional: fechar sidebar quando o usuário clica numa opção (útil em mobile)
document.querySelectorAll('aside nav button, aside nav a').forEach(el => {
  el.addEventListener('click', () => {
    if (window.innerWidth < 768) closeMobileSidebar();
  });
});

// Ajusta canvases exibidos para serem responsivos (aplica estilo após geração)
// Função utilitária usada quando você insere/mostra canvases dinamicamente:
function makeCanvasResponsive(canvasEl, maxWidthPx = 420) {
  if (!canvasEl) return;
  canvasEl.classList.add('responsive-canvas');
  canvasEl.style.maxWidth = maxWidthPx + 'px';
  canvasEl.style.width = '100%';
  canvasEl.style.height = 'auto';
}

// chama essa função sempre que mostrar um canvas gerado (ex.: no preview)
</script>
</body>
</html>