const charts={};
let currentSEOTool='';
let currentPeriod='30d';
const contexts={};

// ——— i18n ———
const _i18n = {};
let _lang = localStorage.getItem('dm_lang') || 'es';

function _t(key) {
  return (_i18n[_lang] || _i18n['es'] || {})[key] || key;
}

async function initI18n() {
  try {
    const d = await fetch('api/i18n.php').then(r => r.json());
    Object.assign(_i18n, d);
    _applyLangActive();
    applyTranslations();
  } catch(e) { /* silencioso */ }
}

function gtLang(lang) {
  _lang = lang;
  localStorage.setItem('dm_lang', lang);
  _applyLangActive();
  applyTranslations();
}

function _applyLangActive() {
  ['es','en','pt','fr','de'].forEach(l => {
    const b = document.getElementById('gl-' + l);
    if (b) b.style.opacity = l === _lang ? '1' : '.45';
  });
}

function applyTranslations() {
  document.querySelectorAll('[data-i18n]').forEach(el => {
    const key = el.getAttribute('data-i18n');
    const attr = el.getAttribute('data-i18n-attr');
    const val = _t(key);
    if (attr) el.setAttribute(attr, val);
    else el.textContent = val;
  });
}

// ——— Utilidad fetch con CSRF ———
function fetchJSON(url, data) {
  const token = document.querySelector('meta[name="dm-csrf"]')?.content || '';
  return fetch(url, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json', 'X-CSRF-Token': token },
    body: JSON.stringify(data),
  }).then(r => r.json());
}

// Auto-cargar cliente al inicio (usuarios no-admin o cliente único)
(function(){
  initI18n();
  if(currentClient && currentClient!=='all'){
    setTimeout(()=>selectClient(currentClient), 50);
  } else {
    setTimeout(()=>loadDashboardSemaforo(), 200);
  }
})();

// ——— PAGE NAV ———
function showPage(id){
  document.querySelectorAll('.page').forEach(p=>p.classList.remove('active'));
  const pg=document.getElementById('page-'+id);
  if(pg) pg.classList.add('active');
  document.querySelectorAll('.nt[data-page],.si[data-page]').forEach(t=>t.classList.toggle('active', t.getAttribute('data-page')===id));
  if(id==='meta'&&!charts.meta) initChart('meta');
  if(id==='google'&&!charts.google) initChart('google');
  if(id==='seo'&&!charts.seo) initChart('seo');
  if(id==='seo') { aviAgentSEOPanelUpdate(); seoAdvancedInit(); if(currentClient&&currentClient!=='all') loadGSCData(currentClient); }
  if(id==='google'&&currentClient&&currentClient!=='all') loadGoogleData(currentClient, currentGPeriod);
  if(id==='reportes') { loadRptServices(); loadReportHistory(); if(currentClient&&currentClient!=='all'){loadRptClientSettings(currentClient);document.getElementById('rpt-settings-card')&&(document.getElementById('rpt-settings-card').style.display='block');} }
  if(id==='tiktok'&&currentClient&&currentClient!=='all') loadTikTokData(currentClient, ttCurrentPeriod);
  if(id==='email'&&currentClient&&currentClient!=='all') loadEmailData();
  if(id==='web'&&currentClient&&currentClient!=='all') loadWebOverview(currentClient);
  if(id==='overview') loadDashboardSemaforo();
  if(id==='reportes'){const nb=document.getElementById('rpt-no-client');if(nb)nb.style.display=(!currentClient||currentClient==='all')?'block':'none';}
  aviDrawerSetContext(id);
}

// ——— CLIENT SELECT ———
function selectClient(id){
  currentClient=id;
 document.querySelectorAll('.cchip,.call').forEach(c=>c.classList.remove('active'));
document.getElementById('chip-all')?.classList.remove('active');
  const aSec=document.getElementById('all-sec');
  const sSec=document.getElementById('single-sec');

  // Limpiar contextos en memoria al cambiar de cliente
  Object.keys(contexts).forEach(k=>delete contexts[k]);
  brandAssets = { logo:'', colors:'', fonts:'', manual:'', description:'', styles:[] };
  // Limpiar textareas
  ['meta','google','seo','web','videos','imagenes'].forEach(s=>{
    const ta=document.getElementById(s+'-ctx');
    if(ta) ta.value='';
  });
  ['g-neg-permanent','g-neg-daily'].forEach(id=>{
    const ta=document.getElementById(id);
    if(ta) ta.value='';
  });

  const banner   = document.getElementById('ov-select-banner');
  const dataWrap = document.getElementById('ov-data-wrap');
  const periodBtns = document.getElementById('ov-period-btns');

  if(id==='all'){
    if(aSec) aSec.style.display='block';
    if(sSec) sSec.style.display='none';
    if(banner) banner.style.display='flex';
    if(dataWrap) dataWrap.style.display='none';
    if(periodBtns) periodBtns.style.display='none';
    document.getElementById('ov-title').textContent='Super Cerebro';
    document.getElementById('ov-sub').textContent='Panel general · selecciona un cliente';
    document.documentElement.style.removeProperty('--client-color');
    document.documentElement.style.removeProperty('--client-color-bg');
  } else {
    const c=CLIENTS.find(x=>x.id==id);
    if(!c) return;
    document.documentElement.style.setProperty('--client-color', c.color);
    document.documentElement.style.setProperty('--client-color-bg', c.color + '18');
    document.getElementById('chip-'+id)?.classList.add('active');
    if(aSec) aSec.style.display='none';
    if(sSec){ sSec.style.display='block'; renderClientHeader(c); }
    if(banner) banner.style.display='none';
    if(dataWrap){ dataWrap.style.display='flex'; }
    if(periodBtns) periodBtns.style.display='flex';
    document.getElementById('ov-title').textContent=c.name;
    document.getElementById('ov-sub').textContent='Panel general · '+new Date().toLocaleDateString('es-CO',{month:'long',year:'numeric'});
    document.getElementById('avi-ctx').textContent='Analizando '+c.name;
    // Reset métricas a loading
    ['ov-spend','ov-roas','ov-organic'].forEach(i=>{const e=document.getElementById(i);if(e)e.textContent='...';});
    ['ov-spend-d','ov-roas-d','ov-organic-d'].forEach(i=>{const e=document.getElementById(i);if(e)e.textContent='';});
    // Limpiar snapshot hasta que lleguen los datos del cliente nuevo
    metaCampsAll=[]; gCampaignsList=[]; gscKeywordsList=[];
    typeof emResetCache !== 'undefined' && emResetCache();
    const snapCard=document.getElementById('ov-snapshot-card');
    if(snapCard) snapCard.style.display='none';
    loadClientContexts(id);
    loadMetaData(id);
    loadBrandAssets(id);
    loadClientActivity(id);
    loadRptServices();
    if(document.getElementById('page-reportes')?.classList.contains('active')){loadReportHistory();loadRptClientSettings(id);document.getElementById('rpt-settings-card')&&(document.getElementById('rpt-settings-card').style.display='block');}
    loadOverviewGoogleMetrics(id);
    aviDrawerClientUpdate();
    if(document.getElementById('page-google')?.classList.contains('active')) {
      _gDailyLoaded='';
      if(currentGTab==='overview') loadGoogleData(id, currentGPeriod);
      else reloadGTab();
    }
    loadGoogleCustomerId(id);
    loadGSCData(id);
    loadShopifyData(id);
    if(document.getElementById('page-tiktok')?.classList.contains('active')) loadTikTokData(id);
    if(document.getElementById('page-email')?.classList.contains('active')) loadEmailData();
    if(document.getElementById('page-web')?.classList.contains('active')) loadWebOverview(id);
    const rptNoBanner=document.getElementById('rpt-no-client');if(rptNoBanner)rptNoBanner.style.display='none';
  }
}

// ——— OVERVIEW: métricas reales de Google Ads ———
let ovGPeriod = '30d';
async function loadOverviewGoogleMetrics(clientId, period) {
  if (!clientId || clientId==='all') return;
  if (period) ovGPeriod = period;
  try {
    const d = await fetch(`api/google-data.php?client_id=${clientId}&period=${ovGPeriod}`).then(r=>r.json());
    if (!d.ok || !d.data) { setOvEl('ov-roas','Sin Google'); setOvEl('ov-roas-d','Configura Google Ads'); return; }
    const m = d.data.metrics;
    setOvEl('ov-roas', m.spend ? (m.spend_fmt || '$'+m.spend.toLocaleString('es-CO')) : 'Sin datos');
    setOvEl('ov-roas-d', m.roas ? 'ROAS: '+m.roas+'×' : (m.spend ? 'Sin conversiones aún' : ''));
    // Tabla campañas Google en overview
    const camps = (d.data.campaigns||[]).slice(0,4);
    const el = document.getElementById('ov-google-camps');
    if (el) {
      el.innerHTML = camps.length ? '<table class="tbl" style="margin-top:4px"><thead><tr><th>Campaña</th><th>Gasto</th><th>ROAS</th><th>Estado</th></tr></thead><tbody>'+
        camps.map(c=>`<tr>
          <td style="max-width:130px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">${escapeHtml(c.name)}</td>
          <td>$${(c.spend||0).toLocaleString('es-CO')}</td>
          <td style="color:${(c.roas||0)>=2?'var(--g)':(c.roas||0)>=1?'var(--y)':'var(--red)'}">${c.roas||'—'}×</td>
          <td><span class="pill ${c.status==='enabled'?'pill-ok':'pill-pause'}">${c.status==='enabled'?'Activa':'Pausada'}</span></td>
        </tr>`).join('')+'</tbody></table>'
        : '<div style="color:var(--m);font-size:12px;padding:8px 0">Sin campañas en este período</div>';
    }
  } catch(e) { setOvEl('ov-roas','Sin Google'); setOvEl('ov-roas-d',''); }
}
function setOvEl(id,val){ const e=document.getElementById(id); if(e) e.textContent=val; }

// ——— DASHBOARD SEMÁFORO — todos los clientes ———
let _semaforoData = [];

async function loadDashboardSemaforo() {
  try {
    const d = await fetch('api/dashboard.php?action=health_check').then(r => r.json());
    if (!d.ok || !d.clients) return;
    _semaforoData = d.clients;
    const colorMap = { green:'#22c97a', yellow:'#f5c543', red:'#E8272A', no_data:'rgba(240,240,240,.2)' };
    const svcLabel = { meta:'Meta', google:'Google', seo:'SEO' };
    d.clients.forEach(client => {
      const el = document.getElementById('semaforo-' + client.id);
      if (!el) return;
      const svcs = client.services || {};
      const dots = Object.entries(svcs).map(([svc, info]) => {
        const col   = colorMap[info.status] || colorMap.no_data;
        const tip   = `${svcLabel[svc]||svc}: ${info.label} — ${info.detail}`;
        const glow  = info.status === 'green' ? `box-shadow:0 0 5px ${col}55` : info.status === 'red' ? `box-shadow:0 0 5px ${col}66` : '';
        return `<span title="${escapeHtml(tip)}" style="width:8px;height:8px;border-radius:50%;background:${col};display:inline-block;flex-shrink:0;${glow}"></span>`;
      }).join('');
      el.innerHTML = dots || '<span style="font-size:10px;color:var(--m)">—</span>';
    });
    // Actualizar timestamp
    const ts = document.getElementById('semaforo-ts');
    if (ts && d.as_of) ts.textContent = 'Semáforo al ' + d.as_of;

    // Badge + alertas proactivas enriquecidas con delta_pct
    let redCount = 0;
    const alertItems = [];
    d.clients.forEach(client => {
      const svcs = client.services || {};
      Object.entries(svcs).forEach(([svc, info]) => {
        if (info.status === 'red') {
          redCount++;
          const pct = (info.delta_pct !== null && info.delta_pct !== undefined)
            ? info.delta_pct : null;
          const badge = pct !== null
            ? `<span style="font-size:10px;font-weight:700;color:${pct<=-30?'var(--red)':'var(--y)'};background:${pct<=-30?'rgba(232,39,42,.12)':'rgba(245,197,67,.12)'};padding:2px 6px;border-radius:4px;margin-left:6px">${pct}% semana</span>`
            : '';
          alertItems.push({ client: client.name, svc: svcLabel[svc], detail: info.detail, badge, pct });
        }
      });
    });

    // También cargar weekly_deltas para alertas de caída ≥20% en canales amarillos
    let weeklyExtra = [];
    try {
      const wd = await fetch('api/dashboard.php?action=weekly_deltas').then(r=>r.json());
      if (wd.ok) {
        wd.clients.forEach(wc => {
          (wc.alerts||[]).forEach(a => {
            const alreadyRed = alertItems.some(ai => ai.client===wc.name && ai.svc===svcLabel[a.channel]);
            if (!alreadyRed) {
              weeklyExtra.push({ client: wc.name, svc: a.label, detail: `Caída ${a.pct}% vs semana anterior`, badge: `<span style="font-size:10px;font-weight:700;color:var(--y);background:rgba(245,197,67,.12);padding:2px 6px;border-radius:4px;margin-left:6px">${a.pct}% semana</span>`, pct: a.pct });
            }
          });
        });
      }
    } catch(e2) { /* silencioso */ }

    const allAlerts = [...alertItems, ...weeklyExtra];
    const badge = document.getElementById('ov-alert-badge');
    if (badge) {
      badge.textContent = allAlerts.length;
      badge.style.display = allAlerts.length > 0 ? '' : 'none';
    }

    const alertsEl = document.getElementById('ov-proactive-alerts');
    if (alertsEl) {
      if (allAlerts.length > 0) {
        const rows = allAlerts.map(a =>
          `<div style="display:flex;align-items:flex-start;gap:8px;padding:7px 10px;background:rgba(232,39,42,.05);border-left:2px solid ${(a.pct!==null&&a.pct<=-30)?'var(--red)':'var(--y)'};border-radius:0 6px 6px 0">
            <div style="flex:1;min-width:0">
              <div style="font-size:12px;font-weight:600;color:var(--t)">${escapeHtml(a.client)} <span style="font-weight:400;color:var(--m)">— ${escapeHtml(a.svc)}</span>${a.badge}</div>
              <div style="font-size:11px;color:var(--m);margin-top:2px">${escapeHtml(a.detail)}</div>
            </div>
          </div>`
        ).join('');
        alertsEl.innerHTML = `<div class="avi" style="padding:14px 16px">
          <div class="avi-hd" style="margin-bottom:10px">
            <div class="avi-orb" style="background:rgba(232,39,42,.15);color:var(--red)">⚠</div>
            <div><div class="avi-name">AVI · ${allAlerts.length} alerta${allAlerts.length>1?'s':''} detectada${allAlerts.length>1?'s':''}</div>
            <div class="avi-sub">Caídas y canales que requieren atención</div></div>
          </div>
          <div style="display:flex;flex-direction:column;gap:5px">${rows}</div>
          <button onclick="document.getElementById('ov-proactive-alerts').style.display='none'"
            style="margin-top:10px;padding:3px 10px;border-radius:6px;font-size:11px;background:var(--bg3);border:1px solid var(--br2);color:var(--m);cursor:pointer;font-family:var(--fb)">Descartar</button>
        </div>`;
        alertsEl.style.display = 'block';
      } else {
        alertsEl.style.display = 'none';
      }
    }
  } catch(e) { /* silencioso */ }
}

function filterSemaforo(canal) {
  // Update active button
  document.querySelectorAll('.sf-btn').forEach(b => {
    b.style.border = '1px solid transparent';
    b.style.opacity = '0.65';
  });
  const active = document.getElementById('sf-btn-' + canal);
  if (active) { active.style.border = '1px solid var(--br2)'; active.style.opacity = '1'; }

  const rows = document.querySelectorAll('#clist .crow');
  rows.forEach(row => {
    if (canal === 'all') { row.style.display = ''; return; }
    const cid = row.id.replace('crow-', '');
    const sfEl = document.getElementById('semaforo-' + cid);
    if (!sfEl) { row.style.display = ''; return; }
    // Check if client has a red or yellow dot for this canal
    const clientData = _semaforoData.find(c => String(c.id) === cid);
    if (!clientData) { row.style.display = 'none'; return; }
    const svcInfo = (clientData.services || {})[canal];
    if (!svcInfo) { row.style.display = 'none'; return; }
    const st = svcInfo.status;
    row.style.display = (st === 'red' || st === 'yellow') ? '' : 'none';
  });
}

function exportSemaforoCSV() {
  const svcLabel = { meta:'Meta', google:'Google', seo:'SEO' };
  const rows = [['Cliente', 'Canal', 'Estado', 'Detalle', 'Delta %', 'Fecha']];
  const date = new Date().toISOString().slice(0, 10);
  _semaforoData.forEach(client => {
    const svcs = client.services || {};
    Object.entries(svcs).forEach(([svc, info]) => {
      rows.push([
        client.name,
        svcLabel[svc] || svc,
        info.status,
        info.detail || '',
        info.delta_pct !== null && info.delta_pct !== undefined ? info.delta_pct : '',
        date
      ]);
    });
  });
  if (rows.length <= 1) return;
  const csv = rows.map(r => r.map(v => '"' + String(v).replace(/"/g,'""') + '"').join(',')).join('\n');
  const a = document.createElement('a');
  a.href = 'data:text/csv;charset=utf-8,' + encodeURIComponent(csv);
  a.download = 'semaforo-' + date + '.csv';
  a.click();
}

function loadOverviewMetrics(cid, period) {
  if(!cid||cid==='all') return;
  // Actualizar botón activo
  document.querySelectorAll('#ov-period-btns .db').forEach(b=>b.classList.remove('active'));
  const map={'7d':'7D','30d':'30D','90d':'90D'};
  document.querySelectorAll('#ov-period-btns .db').forEach(b=>{ if(b.textContent.trim()===map[period]) b.classList.add('active'); });
  ['ov-spend','ov-roas','ov-organic'].forEach(i=>{const e=document.getElementById(i);if(e)e.textContent='...';});
  loadMetaData(cid, period);
  loadOverviewGoogleMetrics(cid, period);
  loadGSCData(cid, period==='7d'?'7d':period==='90d'?'90d':'30d');
}

function renderClientHeader(c){
  const el=document.getElementById('chc-wrap');
  const svcs=(c.services||'').split(',').filter(Boolean);
  const sm={meta:['rgba(74,158,248,.15)','var(--b)','Meta Ads'],google:['rgba(232,39,42,.15)','#f87171','Google Ads'],seo:['rgba(34,201,122,.15)','var(--g)','SEO'],wp:['rgba(245,197,67,.15)','var(--y)','WordPress'],shopify:['rgba(150,191,77,.15)','#96bf4d','Shopify']};
  el.innerHTML=`<div class="chc">
    <div class="chc-av" style="background:${c.color}18;color:${c.color}">${c.name.substring(0,2).toUpperCase()}</div>
    <div class="chc-info">
      <div class="chc-name">${c.name}</div>
      <div class="chc-meta">${c.industry} · <a href="https://${c.url}" target="_blank" style="color:var(--b)">${c.url}</a></div>
      <div class="chc-svcs">${svcs.map(s=>{const[bg,col,lbl]=sm[s]||[];return`<span style="font-size:11px;padding:2px 8px;border-radius:4px;background:${bg};color:${col}">${lbl}</span>`;}).join('')}</div>
    </div>
    <div class="chc-acts">
      <button class="chc-btn" onclick="showPage('meta')">Meta Ads</button>
      <button class="chc-btn" onclick="showPage('google')">Google Ads</button>
      <button class="chc-btn" onclick="showPage('seo')">SEO</button>
      <button class="chc-btn pr" onclick="document.getElementById('avi-q').value='Dame análisis completo de ${c.name}';askAVI(\"main\")">AVI →</button>
    </div>
  </div>`;
}

// ——— CONTEXT SAVE / LOAD (BD por cliente) ———
function saveCtx(section){
  const val=document.getElementById(section+'-ctx').value;
  const el=document.getElementById('ctx-saved-'+section);
  contexts[section]=val;

  // Si hay cliente activo, guardar en BD
  if(currentClient!=='all'){
    el.textContent='Guardando...';
    el.style.display='inline';
    fetch('api/context.php',{
      method:'POST',
      headers:{'Content-Type':'application/json'},
      body:JSON.stringify({action:'save',client_id:currentClient,section,context:val})
    })
    .then(r=>r.json())
    .then(d=>{
      el.textContent = d.ok ? '✓ Guardado para este cliente' : '✗ Error al guardar';
      setTimeout(()=>el.style.display='none',2500);
    })
    .catch(()=>{ el.textContent='✗ Error'; setTimeout(()=>el.style.display='none',2000); });
  } else {
    // Sin cliente seleccionado — guardar solo en memoria temporal
    el.textContent='⚠ Selecciona un cliente para guardar permanentemente';
    el.style.display='inline';
    setTimeout(()=>el.style.display='none',3000);
  }
}

function clearCtx(section){
  if(!confirm('¿Limpiar el contexto de '+section+'? Se borrará de la base de datos del cliente actual.')) return;
  const ta=document.getElementById(section+'-ctx');
  if(ta) ta.value='';
  contexts[section]='';
  if(currentClient&&currentClient!=='all'){
    fetch('api/context.php',{
      method:'POST',
      headers:{'Content-Type':'application/json'},
      body:JSON.stringify({action:'save',client_id:currentClient,section,context:''})
    })
    .then(r=>r.json())
    .then(d=>{
      const el=document.getElementById('ctx-saved-'+section);
      if(el){ el.textContent=d.ok?'✓ Limpiado':'✗ Error'; el.style.display='inline'; setTimeout(()=>el.style.display='none',2000); }
    });
  }
}

// ——— META ADS TABS ———
function switchMetaTab(id, el) {
  document.querySelectorAll('.meta-panel').forEach(p => p.style.display = 'none');
  document.querySelectorAll('.meta-tab').forEach(t => {
    t.style.color = 'var(--m)';
    t.style.borderBottomColor = 'transparent';
  });
  const panel = document.getElementById('mpanel-' + id);
  if (panel) panel.style.display = 'flex';
  if (el) { el.style.color = 'var(--t)'; el.style.borderBottomColor = 'var(--red)'; }
  if (id === 'conjuntos') loadMetaAdSets();
  else if (id === 'anuncios') loadMetaAdsTab();
  else if (id === 'publicos') loadMetaBreakdown('publisher_platform');
  else if (id === 'creatividades') loadMetaCreativeRanking();
}

// ——— CARGA DATOS REALES META ADS ———
let metaCurrentData = null;
let metaCampsAll = [];
let metaCampsFilter = 'active';
let gscKeywordsList = [];

function onMetaPeriodChange() {
  const sel = document.getElementById('meta-period-sel');
  const customWrap = document.getElementById('meta-date-custom');
  if (sel.value === 'custom') {
    customWrap.style.display = 'flex';
  } else {
    customWrap.style.display = 'none';
    loadMetaData(currentClient, sel.value);
  }
}

function filterCamps(filter, btn) {
  metaCampsFilter = filter;
  document.querySelectorAll('[id^="filter-"]').forEach(b => {
    b.style.background = 'var(--bg3)';
    b.style.borderColor = 'var(--br2)';
    b.style.color = 'var(--m)';
  });
  if (btn) { btn.style.background = 'var(--rl)'; btn.style.borderColor = 'var(--rb)'; btn.style.color = 'var(--red)'; }
  let filtered;
  if (filter === 'all') filtered = metaCampsAll;
  else if (filter === 'conv') filtered = metaCampsAll.filter(c => (c.conversations || 0) > 0);
  else filtered = metaCampsAll.filter(c => c.status === filter);
  renderMetaCampaigns(filtered);
}

function loadMetaData(clientId, period) {
  if (!clientId || clientId === 'all') return;
  period = period || document.getElementById('meta-period-sel')?.value || '30d';
  currentPeriod = period;

  // Loading state
  ['meta-spend','meta-roas','meta-ctr','meta-cpa','meta-reach','meta-convs','meta-cpconv','meta-cpm'].forEach(id => {
    const el = document.getElementById(id);
    if (el) el.textContent = '...';
  });
  const campLoading = document.getElementById('meta-camp-loading');
  if (campLoading) campLoading.textContent = 'Cargando...';

  const _attr = localStorage.getItem('meta_attribution') || '7d_click';
  const _attrSel = document.getElementById('meta-attribution-sel');
  if (_attrSel && _attrSel.value !== _attr) _attrSel.value = _attr;

  let url = `api/meta-data.php?client_id=${clientId}&period=${period}&attribution=${_attr}`;
  if (period === 'custom') {
    const since = document.getElementById('meta-date-from')?.value;
    const until = document.getElementById('meta-date-to')?.value;
    if (!since || !until) return;
    url = `api/meta-data.php?client_id=${clientId}&period=custom&since=${since}&until=${until}&attribution=${_attr}`;
  }

  fetch(url)
    .then(r => r.json())
    .then(data => {
      if (campLoading) campLoading.textContent = '';
      if (!data.ok) {
        document.getElementById('meta-spend').textContent = 'Sin datos';
        return;
      }
      metaCurrentData = data.data;
      metaCampsAll = data.data.campaigns || [];
      renderOvSnapshot();
      const m = data.data.metrics;

      // Métricas dashboard
      document.getElementById('meta-spend').textContent = m.spend_fmt || '$0';
      document.getElementById('meta-spend-d').textContent = m.clicks ? m.clicks.toLocaleString() + ' clics' : '';
      document.getElementById('meta-roas').textContent = m.roas ? m.roas + '×' : 'N/A';
      document.getElementById('meta-roas-d').textContent = m.roas_target ? `Objetivo: ${m.roas_target}×` : 'No aplica';
      document.getElementById('meta-ctr').textContent = m.ctr + '%';
      document.getElementById('meta-ctr-d').textContent = 'Frec: ' + (m.frequency > 3 ? '⚠ ' : '') + m.frequency;
      if (m.frequency > 3) document.getElementById('meta-ctr-d').style.color = 'var(--y)';
      document.getElementById('meta-cpa').textContent = m.cpa_fmt || (m.cpl_fmt ? m.cpl_fmt + ' (CPL)' : 'N/A');
      document.getElementById('meta-cpa-d').textContent = m.cpa_target ? `Obj: $${(m.cpa_target/1000).toFixed(0)}K` : 'No aplica';
      document.getElementById('meta-reach').textContent = m.reach ? m.reach.toLocaleString() : '0';
      document.getElementById('meta-reach-d').textContent = m.impressions ? m.impressions.toLocaleString() + ' imp.' : '';
      document.getElementById('meta-convs').textContent = m.conversations || '0';
      document.getElementById('meta-convs-d').textContent = m.conversations > 0 ? m.conversations + ' conversaciones' : 'Sin datos';
      document.getElementById('meta-cpconv').textContent = m.cpconv_fmt || 'N/A';
      document.getElementById('meta-cpconv-d').textContent = m.cpconv ? 'por conversación' : '';
      document.getElementById('meta-cpm').textContent = m.cpm_fmt || '$0';

      // Actualizar resumen overview
      setOvEl('ov-spend', m.spend_fmt || '—');
      setOvEl('ov-spend-d', m.clicks ? m.clicks.toLocaleString()+' clics' : '');
      // Campañas Meta en overview
      const ovCamps = document.getElementById('ov-camps-table');
      if (ovCamps) {
        const top = (metaCampsAll||[]).filter(c=>c.status==='active').slice(0,4);
        ovCamps.innerHTML = top.length ? '<table class="tbl" style="margin-top:4px"><thead><tr><th>Campaña</th><th>Gasto</th><th>ROAS</th><th>Estado</th></tr></thead><tbody>'+
          top.map(c=>`<tr>
            <td style="max-width:130px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">${escapeHtml(c.name||'')}</td>
            <td>${c.spend_fmt||'—'}</td>
            <td style="color:${(c.roas||0)>=2?'var(--g)':(c.roas||0)>=1?'var(--y)':'var(--red)'}">${c.roas||'—'}×</td>
            <td><span class="pill pill-ok">Activa</span></td>
          </tr>`).join('')+'</tbody></table>'
          : '<div style="color:var(--m);font-size:12px;padding:6px 0">Sin campañas activas</div>';
      }

      // Alertas
      renderMetaAlerts(data.data.alerts || [], data.data.account);

      // Campañas respetando filtro activo
      let filteredCamps;
      if (metaCampsFilter === 'all') filteredCamps = metaCampsAll;
      else filteredCamps = metaCampsAll.filter(c => c.status === metaCampsFilter);
      renderMetaCampaigns(filteredCamps);

      // Recomendaciones AVI
      if (metaCampsAll.length > 0) generateMetaRecommendations(data.data);

      // Semáforo de optimización
      renderMetaOptimIndicators(m);

      // Pacing + Placement dashboard
      const ppWrap = document.getElementById('meta-pacing-placement');
      if (ppWrap) {
        ppWrap.style.display = 'block';
        renderMetaPacing(metaCampsAll, m);
        loadMetaPlacementDashboard(clientId, period);
      }

      // Poblar filtros de campaña en tabs Conjuntos y Anuncios
      const campOpts = metaCampsAll.map(c => `<option value="${c.id}">${escapeHtml(c.name)}</option>`).join('');
      ['meta-as-camp-filter','meta-ads-camp-filter'].forEach(fid => {
        const sel = document.getElementById(fid);
        if (sel) sel.innerHTML = '<option value="">Todas las campañas</option>' + campOpts;
      });

      // AVI agente panel
      aviAgentMetaPanelUpdate();
    })
    .catch(() => {
      document.getElementById('meta-spend').textContent = 'Error';
      if (campLoading) campLoading.textContent = 'Error de conexión';
    });
}

function onMetaAttributionChange() {
  const sel = document.getElementById('meta-attribution-sel');
  if (!sel) return;
  localStorage.setItem('meta_attribution', sel.value);
  if (currentClient && currentClient !== 'all') loadMetaData(currentClient, currentPeriod);
}

function renderMetaPacing(camps, metrics) {
  const el = document.getElementById('meta-pacing-content');
  if (!el) return;
  const now = new Date();
  const dayOfMonth = now.getDate();
  const daysInMonth = new Date(now.getFullYear(), now.getMonth() + 1, 0).getDate();
  const daysLeft = daysInMonth - dayOfMonth;

  // Total daily budget from active campaigns
  const totalDailyBudget = (camps || [])
    .filter(c => c.status === 'active' && c.daily_budget > 0)
    .reduce((s, c) => s + (c.daily_budget || 0), 0);
  const monthlyBudget = totalDailyBudget * daysInMonth;
  const spend = metrics?.spend || 0;
  const projected = dayOfMonth > 0 ? Math.round((spend / dayOfMonth) * daysInMonth) : 0;
  const pacingPct = monthlyBudget > 0 ? Math.round((projected / monthlyBudget) * 100) : null;
  const spentPct  = monthlyBudget > 0 ? Math.round((spend / monthlyBudget) * 100) : null;

  const fmt = v => '$' + Math.round(v).toLocaleString('es-CO');
  const color = pacingPct === null ? 'var(--m)' : pacingPct > 110 ? 'var(--red)' : pacingPct < 80 ? 'var(--y)' : 'var(--g)';
  const label = pacingPct === null ? '—' : pacingPct > 110 ? 'Sobre presupuesto' : pacingPct < 80 ? 'Por debajo' : 'En ritmo';

  el.innerHTML = `
    <div style="display:grid;grid-template-columns:1fr 1fr;gap:8px;margin-bottom:10px">
      <div style="background:var(--bg3);border-radius:8px;padding:10px 12px">
        <div style="font-size:10px;color:var(--m);margin-bottom:3px">Gasto del período</div>
        <div style="font-family:var(--fh);font-size:17px;font-weight:700">${metrics?.spend_fmt || '—'}</div>
        <div style="font-size:11px;color:var(--m)">Día ${dayOfMonth} de ${daysInMonth}</div>
      </div>
      <div style="background:var(--bg3);border-radius:8px;padding:10px 12px">
        <div style="font-size:10px;color:var(--m);margin-bottom:3px">Proyección mes</div>
        <div style="font-family:var(--fh);font-size:17px;font-weight:700;color:${color}">${projected > 0 ? fmt(projected) : '—'}</div>
        <div style="font-size:11px;color:${color};font-weight:600">${label}</div>
      </div>
    </div>
    ${monthlyBudget > 0 ? `
    <div style="margin-bottom:6px">
      <div style="display:flex;justify-content:space-between;font-size:11px;color:var(--m);margin-bottom:4px">
        <span>Presupuesto mensual estimado</span><span>${fmt(monthlyBudget)}</span>
      </div>
      <div style="height:6px;background:var(--bg3);border-radius:3px;overflow:hidden">
        <div style="height:100%;width:${Math.min(spentPct,100)}%;background:${color};border-radius:3px;transition:width .5s"></div>
      </div>
      <div style="font-size:10px;color:var(--m);margin-top:3px">${spentPct}% ejecutado · ${daysLeft} días restantes</div>
    </div>` : '<div style="font-size:11px;color:var(--m)">Sin presupuesto diario configurado</div>'}`;
}

async function loadMetaPlacementDashboard(clientId, period) {
  const el = document.getElementById('meta-placement-content');
  if (!el || !clientId || clientId === 'all') return;
  try {
    const d = await fetch(`api/meta-data.php?action=breakdown&client_id=${clientId}&period=${period}&breakdown=publisher_platform`).then(r => r.json());
    if (!d.ok || !d.breakdown?.length) { el.innerHTML = '<div style="font-size:12px;color:var(--m);text-align:center;padding:8px">Sin datos de placement</div>'; return; }
    const total = d.breakdown.reduce((s, r) => s + (r.spend || 0), 0);
    const plColors = { facebook:'var(--b)', instagram:'var(--p)', audience_network:'var(--y)', messenger:'var(--g)' };
    const rows = d.breakdown.sort((a,b) => b.spend - a.spend).map(r => {
      const pct = total > 0 ? Math.round(r.spend / total * 100) : 0;
      const col = plColors[r.label] || 'var(--m)';
      const name = (r.label || '').replace(/_/g, ' ');
      return `<div style="margin-bottom:8px">
        <div style="display:flex;justify-content:space-between;font-size:11px;margin-bottom:3px">
          <span style="color:var(--t);text-transform:capitalize">${name}</span>
          <span style="color:var(--m)">${pct}% · $${Math.round(r.spend).toLocaleString()}</span>
        </div>
        <div style="height:5px;background:var(--bg3);border-radius:3px;overflow:hidden">
          <div style="height:100%;width:${pct}%;background:${col};border-radius:3px"></div>
        </div>
        <div style="font-size:10px;color:var(--m);margin-top:2px">${(r.impressions||0).toLocaleString()} imp · CTR ${r.ctr||'0'}%</div>
      </div>`;
    }).join('');
    el.innerHTML = rows;
  } catch(e) { el.innerHTML = '<div style="font-size:12px;color:var(--m);text-align:center;padding:8px">Error cargando placement</div>'; }
}

async function loadMetaCreativeRanking() {
  const el = document.getElementById('meta-creative-ranking');
  if (!el || !currentClient || currentClient === 'all') { if(el) el.innerHTML = '<div style="font-size:12px;color:var(--m);text-align:center;padding:12px">Selecciona un cliente</div>'; return; }
  el.innerHTML = '<div style="font-size:12px;color:var(--m);text-align:center;padding:12px">Cargando anuncios...</div>';
  try {
    const _attr = localStorage.getItem('meta_attribution') || '7d_click';
    const d = await fetch(`api/meta-data.php?action=ads&client_id=${currentClient}&period=${currentPeriod}&attribution=${_attr}`).then(r => r.json());
    if (!d.ok || !d.ads?.length) { el.innerHTML = '<div style="font-size:12px;color:var(--m);text-align:center;padding:12px">Sin anuncios con datos suficientes</div>'; return; }
    const ads = d.ads.filter(a => (a.spend || 0) > 0).sort((a, b) => {
      const scoreA = (parseFloat(a.ctr) || 0) * 2 + (a.conversions || 0) * 0.5 - (a.frequency || 0) * 0.5;
      const scoreB = (parseFloat(b.ctr) || 0) * 2 + (b.conversions || 0) * 0.5 - (b.frequency || 0) * 0.5;
      return scoreB - scoreA;
    });
    if (!ads.length) { el.innerHTML = '<div style="font-size:12px;color:var(--m);text-align:center;padding:12px">Sin gasto registrado en anuncios activos</div>'; return; }
    const top = ads.slice(0, 3);
    const bot = ads.length > 3 ? ads.slice(-Math.min(3, ads.length - top.length)) : [];

    const freqBadge = f => {
      const v = parseFloat(f) || 0;
      const col = v > 3 ? 'var(--red)' : v > 2 ? 'var(--y)' : 'var(--g)';
      return `<span style="font-size:10px;font-weight:700;color:${col};background:${col}18;padding:1px 5px;border-radius:4px">Frec ${v.toFixed(1)}</span>`;
    };
    const adRow = (a, label, labelCol) => `
      <div style="display:flex;align-items:center;gap:10px;padding:8px 10px;background:var(--bg3);border-radius:8px;margin-bottom:6px">
        <div style="font-size:10px;font-weight:700;color:${labelCol};min-width:36px">${label}</div>
        <div style="flex:1;min-width:0">
          <div style="font-size:12px;font-weight:500;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">${escapeHtml(a.name||'Sin nombre')}</div>
          <div style="display:flex;gap:6px;margin-top:3px;flex-wrap:wrap">
            <span style="font-size:11px;color:var(--m)">Gasto: ${a.spend_fmt||'—'}</span>
            <span style="font-size:11px;color:var(--m)">CTR: ${a.ctr||'0'}%</span>
            ${(a.conversions||0)>0 ? `<span style="font-size:11px;color:var(--g)">Conv: ${a.conversions}</span>` : ''}
            ${freqBadge(a.frequency)}
          </div>
        </div>
      </div>`;

    let html = '';
    if (top.length) {
      html += `<div style="font-size:10px;font-weight:600;color:var(--g);text-transform:uppercase;letter-spacing:.5px;margin-bottom:6px">🏆 Mejor rendimiento</div>`;
      html += top.map(a => adRow(a, '✦ TOP', 'var(--g)')).join('');
    }
    if (bot.length) {
      html += `<div style="font-size:10px;font-weight:600;color:var(--y);text-transform:uppercase;letter-spacing:.5px;margin:10px 0 6px">⚠ Menor rendimiento</div>`;
      html += bot.map(a => adRow(a, '↓ LOW', 'var(--y)')).join('');
    }
    el.innerHTML = html;
    // Guardar contexto para AVI
    if (window.metaAdsData) window.metaAdsData.topAds = top;
  } catch(e) { el.innerHTML = '<div style="font-size:12px;color:var(--red);text-align:center;padding:12px">Error cargando anuncios</div>'; }
}

function renderMetaAlerts(alerts, account) {
  const wrap = document.getElementById('meta-alerts-wrap');
  const list = document.getElementById('meta-alerts-list');
  if (!wrap || !list) return;

  let html = '';

  // Estado de cuenta
  if (account) {
    const statusColor = account.status === 'ok' ? 'var(--g)' : account.status === 'warning' ? 'var(--y)' : 'var(--red)';
    html += `<div style="display:flex;align-items:center;gap:8px;padding:8px 12px;background:var(--bg2);border:1px solid var(--br);border-radius:8px;margin-bottom:6px;font-size:12px">
      <div style="width:8px;height:8px;border-radius:50%;background:${statusColor}"></div>
      <span>${account.status_msg}</span>
      ${account.balance > 0 ? `<span style="margin-left:auto;color:var(--m)">Saldo: ${account.balance_fmt}</span>` : ''}
    </div>`;
  }

  alerts.forEach(a => {
    const color = a.type === 'error' ? 'var(--red)' : a.type === 'warning' ? 'var(--y)' : 'var(--g)';
    const bg    = a.type === 'error' ? 'rgba(232,39,42,.08)' : a.type === 'warning' ? 'rgba(245,197,67,.08)' : 'rgba(34,201,122,.08)';
    const icon  = a.type === 'error' ? '⚠' : a.type === 'warning' ? '◆' : '✓';
    html += `<div style="padding:8px 12px;background:${bg};border-left:3px solid ${color};border-radius:6px;margin-bottom:6px;font-size:12px;color:${color}">${icon} ${a.msg}</div>`;
  });

  if (html) {
    wrap.style.display = 'block';
    list.innerHTML = html;
  } else {
    wrap.style.display = 'none';
  }
}

function renderMetaCampaigns(camps) {
  const tbody = document.getElementById('meta-camps-body');
  const count = document.getElementById('meta-camps-count');
  if (!tbody) return;

  if (count) count.textContent = `(${camps.length})`;

  if (camps.length === 0) {
    tbody.innerHTML = '<tr><td colspan="11" style="text-align:center;color:var(--m);padding:24px">Sin campañas en este período</td></tr>';
    return;
  }

  tbody.innerHTML = camps.map(c => {
    const isActive = c.status === 'active';
    const statusPill = isActive
      ? `<span class="pill pill-ok">Activa</span>`
      : `<span class="pill pill-pause">Pausada</span>`;
    const toggleBtn = isActive
      ? `<button onclick="metaAction('pause_campaign','${c.id}','${c.name.replace(/'/g,"\'")}',this)" style="padding:3px 8px;border-radius:5px;font-size:11px;background:rgba(245,197,67,.15);border:1px solid rgba(245,197,67,.3);color:var(--y);cursor:pointer;font-family:var(--fb)">⏸ Pausar</button>`
      : `<button onclick="metaAction('activate_campaign','${c.id}','${c.name.replace(/'/g,"\'")}',this)" style="padding:3px 8px;border-radius:5px;font-size:11px;background:rgba(34,201,122,.15);border:1px solid rgba(34,201,122,.3);color:var(--g);cursor:pointer;font-family:var(--fb)">▶ Activar</button>`;
    const alertsBadge = c.alerts && c.alerts.length > 0
      ? `<span title="${c.alerts.join(', ')}" style="font-size:10px;padding:1px 5px;border-radius:4px;background:rgba(245,197,67,.15);color:var(--y);margin-left:4px">⚠ ${c.alerts.length}</span>`
      : '';

    const ctrVal   = parseFloat(c.ctr) || 0;
    const freqVal  = parseFloat(c.frequency) || 0;
    // Conversiones: primero compras/leads, si no hay usar conversaciones (WhatsApp/Messenger)
    const convVal  = (c.conversions || 0) + (c.leads || 0) || (c.conversations || 0);
    // Costo/conv: usar el que aplique según datos disponibles
    const costConv = (c.cpa && c.cpa > 0)
      ? c.cpa_fmt
      : (c.cpconv && c.cpconv > 0 ? c.cpconv_fmt : '—');
    const ctrColor = ctrVal > 0 && ctrVal < 1 ? 'color:var(--y)' : '';
    const freqColor= freqVal > 3.5 ? 'color:var(--y)' : freqVal > 0 ? '' : 'color:var(--m)';
    const freqTip  = freqVal > 3.5 ? ' title="⚠ Frecuencia alta — audiencia saturada"' : '';

    return `<tr>
      <td style="max-width:180px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap" title="${c.name}">${c.name}${alertsBadge}</td>
      <td>${statusPill}</td>
      <td>
        <span id="budget-val-${c.id}" data-raw="${c.daily_budget||0}">${c.daily_budget_fmt}</span>
        <button onclick="editBudget('${c.id}')" style="padding:2px 5px;border-radius:4px;font-size:10px;background:none;border:1px solid var(--br2);color:var(--m);cursor:pointer;margin-left:4px;font-family:var(--fb)">✎</button>
      </td>
      <td>${c.spend_fmt}</td>
      <td>${c.reach ? c.reach.toLocaleString() : '—'}</td>
      <td>${c.clicks ? c.clicks.toLocaleString() : '—'}</td>
      <td style="${ctrColor}">${ctrVal > 0 ? ctrVal.toFixed(2)+'%' : '—'}</td>
      <td>${convVal > 0 ? convVal.toLocaleString() : '—'}</td>
      <td>${costConv}</td>
      <td style="${freqColor}"${freqTip}>${freqVal > 0 ? freqVal.toFixed(2) : '—'}</td>
      <td style="white-space:nowrap">
        <div style="display:flex;gap:4px">
          ${toggleBtn}
          <button onclick="metaAction('duplicate_campaign','${c.id}','${c.name.replace(/'/g,"\'")}',this)" style="padding:3px 8px;border-radius:5px;font-size:11px;background:rgba(74,158,248,.1);border:1px solid rgba(74,158,248,.2);color:var(--b);cursor:pointer;font-family:var(--fb)">⧉ Dup.</button>
          <button onclick="if(confirm('¿Eliminar campaña ${c.name.replace(/'/g,"\'")}?'))metaAction('delete_campaign','${c.id}','',this)" style="padding:3px 8px;border-radius:5px;font-size:11px;background:rgba(232,39,42,.1);border:1px solid rgba(232,39,42,.2);color:var(--red);cursor:pointer;font-family:var(--fb)">✕</button>
        </div>
      </td>
    </tr>`;
  }).join('');
}

function editBudget(campaignId) {
  const el = document.getElementById('budget-val-' + campaignId);
  const current = el?.textContent || '';
  const rawVal  = el?.dataset.raw || '';
  const newBudget = prompt(`Nuevo presupuesto diario en COP (sin puntos ni símbolos):\nActual: ${current}`, rawVal || '');
  if (!newBudget || isNaN(newBudget) || parseFloat(newBudget) <= 0) return;
  fetch('api/meta-action.php', {
    method: 'POST',
    headers: {'Content-Type': 'application/json'},
    body: JSON.stringify({client_id: parseInt(currentClient), action: 'update_budget', campaign_id: campaignId, budget: parseFloat(newBudget), budget_type: 'daily'})
  })
  .then(r => r.json())
  .then(res => {
    if (res.ok) {
      const el = document.getElementById('budget-val-' + campaignId);
      const v = parseFloat(newBudget);
      if (el) el.textContent = v >= 1000000 ? '$' + (v/1000000).toFixed(1) + 'M' : v >= 1000 ? '$' + (v/1000).toFixed(0) + 'K' : '$' + v;
      showToast('✓ Presupuesto actualizado', 'var(--g)');
    } else {
      showToast('✗ ' + (res.error || 'Error'), 'var(--red)');
    }
  }).catch(() => showToast('✗ Error de conexión', 'var(--red)'));
}

function metaAction(action, campaignId, campaignName, btn) {
  const origText = btn ? btn.textContent : '';
  if (btn) { btn.disabled = true; btn.textContent = '...'; }
  fetch('api/meta-action.php', {
    method: 'POST',
    headers: {'Content-Type': 'application/json'},
    body: JSON.stringify({client_id: parseInt(currentClient), action, campaign_id: campaignId})
  })
  .then(r => r.json())
  .then(res => {
    if (res.ok) {
      showToast('✓ ' + res.msg, 'var(--g)');
      setTimeout(() => loadMetaData(currentClient, currentPeriod), 1000);
    } else {
      showToast('✗ ' + (res.error || 'Error'), 'var(--red)');
      if (btn) { btn.disabled = false; btn.textContent = origText; }
    }
  }).catch(() => {
    showToast('✗ Error de conexión', 'var(--red)');
    if (btn) { btn.disabled = false; btn.textContent = origText; }
  });
}

function showMetaModal(mode, campaignId) {
  const modal = document.getElementById('meta-modal-bg');
  const title = document.getElementById('meta-modal-title');
  const btn   = document.getElementById('meta-modal-btn');
  document.getElementById('meta-modal-camp-id').value = campaignId || '';
  document.getElementById('mc-name').value = '';
  document.getElementById('mc-budget').value = '';
  document.getElementById('mc-start').value = '';
  document.getElementById('mc-stop').value = '';
  document.getElementById('meta-modal-msg').textContent = '';

  if (mode === 'create') {
    title.textContent = 'Nueva campaña';
    btn.textContent = 'Crear campaña';
  } else if (mode === 'duplicate') {
    title.textContent = 'Duplicar campaña';
    btn.textContent = 'Duplicar';
  }
  modal.style.display = 'flex';
}

function executeMetaModal() {
  const campId  = document.getElementById('meta-modal-camp-id').value;
  const name    = document.getElementById('mc-name').value.trim();
  const budget  = parseFloat(document.getElementById('mc-budget').value);
  const budType = document.getElementById('mc-budget-type').value;
  const obj     = document.getElementById('mc-objective').value;
  const start   = document.getElementById('mc-start').value;
  const stop    = document.getElementById('mc-stop').value;
  const msg     = document.getElementById('meta-modal-msg');
  const btn     = document.getElementById('meta-modal-btn');

  if (!name) { msg.style.color='var(--red)'; msg.textContent='El nombre es obligatorio'; return; }
  if (!budget || budget <= 0) { msg.style.color='var(--red)'; msg.textContent='Ingresa un presupuesto válido'; return; }

  btn.disabled = true; btn.textContent = 'Procesando...';
  msg.style.color = 'var(--m)'; msg.textContent = 'Enviando a Meta...';

  const isNew = !campId;
  const payload = {
    client_id:    parseInt(currentClient),
    action:       isNew ? 'create_campaign' : 'duplicate_campaign',
    name, objective: obj,
    daily_budget: budget,
    budget_type:  budType,
  };
  if (start) payload.start_time = new Date(start).toISOString();
  if (stop)  payload.stop_time  = new Date(stop).toISOString();
  if (!isNew) { payload.campaign_id = campId; payload.new_name = name; }

  fetch('api/meta-action.php', {
    method: 'POST',
    headers: {'Content-Type': 'application/json'},
    body: JSON.stringify(payload)
  })
  .then(r => r.json())
  .then(res => {
    btn.disabled = false; btn.textContent = isNew ? 'Crear campaña' : 'Duplicar';
    if (res.ok) {
      msg.style.color = 'var(--g)';
      msg.textContent = '✓ ' + res.msg;
      setTimeout(() => {
        document.getElementById('meta-modal-bg').style.display = 'none';
        loadMetaData(currentClient, currentPeriod);
      }, 1500);
    } else {
      msg.style.color = 'var(--red)';
      msg.textContent = '✗ ' + (res.error || 'Error');
    }
  }).catch(() => {
    btn.disabled = false;
    msg.style.color = 'var(--red)';
    msg.textContent = '✗ Error de conexión';
  });
}

function generateMetaRecommendations(data) {
  const wrap = document.getElementById('meta-avi-rec');
  if (!wrap) return;
  wrap.style.display = 'flex';

  const m     = data.metrics;
  const camps = data.campaigns || [];
  const active = camps.filter(c => c.status === 'active');
  const topCamp = [...camps].sort((a,b) => b.spend - a.spend)[0];

  // Auto-recomendaciones basadas en datos reales
  const recs = [];
  if (m.frequency > 3.5) recs.push({type:'warning', msg:`Frecuencia de ${m.frequency} — considera nuevos creativos o ampliar audiencia`});
  if (m.ctr < 1 && m.clicks > 50) recs.push({type:'warning', msg:`CTR bajo (${m.ctr}%) — los creativos actuales no están generando suficiente interés`});
  if (active.length === 0) recs.push({type:'error', msg:'No hay campañas activas en este período'});
  if (topCamp && topCamp.conversions === 0 && topCamp.spend > 100000) recs.push({type:'warning', msg:`"${topCamp.name}" tiene ${topCamp.spend_fmt} invertidos sin conversiones`});
  if (m.leads > 0 && m.cpl) recs.push({type:'ok', msg:`CPL actual: ${m.cpl_fmt} — ${m.leads} leads generados en el período`});

  const recEl = document.getElementById('meta-rec-list');
  const subEl = document.getElementById('meta-rec-sub');
  if (subEl) subEl.textContent = `${active.length} campañas activas · ${m.spend_fmt} invertidos`;
  if (recEl) {
    recEl.innerHTML = recs.map(r => {
      const color = r.type === 'error' ? 'var(--red)' : r.type === 'warning' ? 'var(--y)' : 'var(--g)';
      const icon  = r.type === 'error' ? '⚠' : r.type === 'warning' ? '◆' : '✓';
      return `<div class="ai ai-${r.type === 'ok' ? 'ok' : r.type === 'warning' ? 'w' : 'r'}">${icon} ${r.msg}</div>`;
    }).join('') || '<div class="ai ai-ok">✓ Todo se ve bien con las campañas actuales</div>';
  }
}

function askMetaRec() {
  const q = document.getElementById('meta-rec-q')?.value.trim();
  if (!q || !metaCurrentData) return;
  const ctx = getContext('meta');
  const m = metaCurrentData.metrics;
  const camps = metaCurrentData.campaigns || [];
  const dataCtx = `Datos actuales Meta Ads: Inversión ${m.spend_fmt}, ROAS ${m.roas||'N/A'}, CTR ${m.ctr}%, Frecuencia ${m.frequency}, Conversiones ${m.conversions}, Leads ${m.leads}, CPL ${m.cpl_fmt||'N/A'}. Campañas activas: ${camps.filter(c=>c.status==='active').map(c=>c.name).join(', ')}. ${ctx}`;
  callAVI(q, dataCtx, 'meta-rec-resp', 'AVI analizando tus datos...', 'meta');
  document.getElementById('meta-rec-q').value = '';
}

function loadMetaRecommendations() {
  if (!currentClient || currentClient === 'all') return;
  const btn = document.getElementById('meta-rec-ia-btn');
  const wrap = document.getElementById('meta-ia-recs');
  if (btn) { btn.disabled = true; btn.textContent = '⟳ Analizando...'; }
  if (wrap) { wrap.style.display = 'block'; wrap.innerHTML = '<div style="font-size:12px;color:var(--m);padding:8px 0">Consultando Meta API y Claude IA...</div>'; }

  fetch(`api/meta-data.php?client_id=${currentClient}&action=recommendations`)
    .then(r => r.json())
    .then(data => {
      if (btn) { btn.disabled = false; btn.textContent = '✦ Análisis IA completo'; }
      if (!data.ok || (!data.ai?.length && !data.meta_native?.length)) {
        if (wrap) wrap.innerHTML = '<div style="font-size:12px;color:var(--m);padding:8px 0">Sin recomendaciones disponibles para esta cuenta.</div>';
        return;
      }
      const priorityColor = {alta:'var(--red)',media:'var(--y)',baja:'var(--g)'};
      const areaIcon = {presupuesto:'💰',creativos:'🎨',audiencia:'👥',pujas:'📈',estructura:'⚙'};
      let html = '<div style="font-size:11px;color:var(--m);text-transform:uppercase;letter-spacing:.7px;margin-bottom:8px">Análisis IA de la cuenta</div>';
      (data.ai || []).forEach(r => {
        const color = priorityColor[r.priority] || 'var(--b)';
        const icon  = areaIcon[r.area] || '✦';
        html += `<div style="padding:10px 12px;background:var(--bg3);border-left:3px solid ${color};border-radius:6px;margin-bottom:6px">
          <div style="display:flex;align-items:center;gap:6px;margin-bottom:4px">
            <span style="font-size:13px">${icon}</span>
            <span style="font-size:12px;font-weight:600;color:var(--t)">${escapeHtml(r.title)}</span>
            <span style="margin-left:auto;font-size:10px;padding:1px 6px;border-radius:4px;background:${color}22;color:${color}">${r.priority}</span>
          </div>
          <div style="font-size:12px;color:var(--m);margin-bottom:3px">${escapeHtml(r.action)}</div>
          <div style="font-size:11px;color:var(--g)">→ ${escapeHtml(r.impact)}</div>
        </div>`;
      });
      (data.meta_native || []).forEach(r => {
        html += `<div style="padding:8px 12px;background:var(--bg2);border:1px solid var(--br);border-radius:6px;margin-bottom:6px;font-size:12px">
          <div style="font-weight:600;color:var(--b);margin-bottom:3px">Meta: ${escapeHtml(r.title)}</div>
          <div style="color:var(--m)">${escapeHtml(r.body)}</div>
        </div>`;
      });
      if (wrap) wrap.innerHTML = html;
    })
    .catch(() => {
      if (btn) { btn.disabled = false; btn.textContent = '✦ Análisis IA completo'; }
      if (wrap) wrap.innerHTML = '<div style="font-size:12px;color:var(--red);padding:8px 0">Error al obtener recomendaciones.</div>';
    });
}

function competenciaRapida(tipo) {
  const ctx = getContext('meta');
  const client = currentClient !== 'all' ? window.CLIENTS?.find(x=>x.id==currentClient) : null;
  const clientInfo = client ? `Cliente: ${client.name}, Industria: ${client.industry}` : '';
  const metaData = metaCurrentData ? `Inversión actual: ${metaCurrentData.metrics.spend_fmt}, CTR: ${metaCurrentData.metrics.ctr}%` : '';

  const prompts = {
    competidores: `Identifica los principales competidores en Meta Ads para: ${clientInfo}. Busca en la Biblioteca de Anuncios de Meta (facebook.com/ads/library) empresas similares. Analiza: quiénes son, qué tipos de anuncios usan, qué ofertas tienen, cuánto tiempo llevan activos. Datos propios: ${metaData}. Contexto: ${ctx}`,
    creativos: `Analiza los creativos que están funcionando en Meta Ads para la industria de: ${clientInfo}. Basándote en tendencias de la Biblioteca de Anuncios: formatos más usados, mensajes que resuenan, CTAs efectivos, elementos visuales comunes. Sugiere mejoras para los anuncios actuales. Datos propios: ${metaData}. Contexto: ${ctx}`,
    estrategia: `Basándote en los datos del Super Cerebro (${metaData}) y en la competencia de ${clientInfo} en Meta Ads, crea una estrategia de diferenciación. Incluye: propuesta de valor única, ángulos creativos no explorados por la competencia, segmentación recomendada, estructura de campañas sugerida. Contexto: ${ctx}`,
    hooks: `Investiga y genera los 10 hooks más efectivos para ads de ${clientInfo} en Meta Ads. Incluye: hooks de problema, hooks de curiosidad, hooks de beneficio, hooks de urgencia y hooks de prueba social. Para cada hook incluye ejemplo concreto y por qué funciona psicológicamente. También incluye tendencias de video ads que están funcionando bien en esta industria actualmente. Datos: ${metaData}. Contexto: ${ctx}`,
  };

  callAVI(prompts[tipo], ctx, 'avi-resp-meta-comp', 'AVI analizando competencia...', 'meta');
}

// ——— META ADS: INDICADORES DE OPTIMIZACIÓN ———
function renderMetaOptimIndicators(m) {
  const grid  = document.getElementById('meta-optim-grid');
  const panel = document.getElementById('meta-optim-panel');
  if (!grid || !m) return;

  const ctr    = parseFloat(m.ctr)       || 0;
  const freq   = parseFloat(m.frequency) || 0;
  const roas   = parseFloat(m.roas)      || 0;
  const cpm    = parseFloat(m.cpm)       || 0;
  const spend  = parseFloat(m.spend)     || 0;
  const clicks = parseInt(m.clicks)      || 0;
  const cpc    = clicks > 0 ? spend / clicks : 0;

  const imp  = parseInt(m.impressions) || 0;
  const indicators = [];

  // CTR: mostrar siempre que haya impresiones (ctr=0 es también una señal de alerta)
  if (imp > 0) {
    const color = ctr >= 1.5 ? 'var(--g)' : ctr >= 0.5 ? 'var(--y)' : 'var(--red)';
    const label = ctr >= 1.5 ? 'Excelente' : ctr >= 0.5 ? 'Aceptable' : 'Revisar';
    const desc  = ctr >= 1.5 ? 'Tus anuncios generan buena atención' : ctr >= 0.5 ? 'Hay margen de mejora en el creativo' : 'Los anuncios no están generando clics — revisar imagen y copy';
    indicators.push({ name:'CTR', fmt:ctr.toFixed(2)+'%', color, label, desc });
  }
  // Frecuencia
  if (imp > 0 && freq > 0) {
    const color = freq <= 2 ? 'var(--g)' : freq <= 3 ? 'var(--y)' : 'var(--red)';
    const label = freq <= 2 ? 'Óptima' : freq <= 3 ? 'Revisar' : 'Fatiga';
    const desc  = freq <= 2 ? 'Cada persona ve el anuncio pocas veces — audiencia fresca' : freq <= 3 ? 'Las personas empiezan a ver el anuncio repetido — considera nuevos creativos' : 'Audiencia saturada — cambiar creativos urgente';
    indicators.push({ name:'Frecuencia', fmt:freq.toFixed(2)+'×', color, label, desc });
  }
  if (roas > 0) {
    const color = roas >= 3 ? 'var(--g)' : roas >= 1.5 ? 'var(--y)' : 'var(--red)';
    const label = roas >= 3 ? 'Excelente' : roas >= 1.5 ? 'Aceptable' : 'Revisar';
    const desc  = `Por cada $1 invertido recuperas $${roas}` + (roas >= 3 ? ' — escalar campaña' : roas >= 1.5 ? ' — optimizar segmentación' : ' — revisar oferta y landing');
    indicators.push({ name:'ROAS', fmt:roas+'×', color, label, desc });
  }
  if (cpm > 0) {
    const desc = 'Costo por cada 1,000 personas alcanzadas — referencial según tu sector';
    indicators.push({ name:'CPM', fmt:m.cpm_fmt||'—', color:'var(--b)', label:'Informativo', desc });
  }
  if (cpc > 0) {
    const f = cpc>=1000000?'$'+(cpc/1000000).toFixed(1)+'M':cpc>=1000?'$'+(cpc/1000).toFixed(0)+'K':'$'+cpc.toFixed(0);
    indicators.push({ name:'CPC', fmt:f, color:'var(--b)', label:'Informativo', desc:'Cuánto cuesta cada clic en tu anuncio' });
  }
  if ((m.conversations||0) > 0) {
    indicators.push({ name:'Conversaciones', fmt:Number(m.conversations).toLocaleString(), color:'var(--g)', label:'Con datos', desc:'Personas que iniciaron chat desde el anuncio (WhatsApp / Messenger)' });
  }

  grid.innerHTML = indicators.map(ind => `
    <div style="padding:10px 14px;background:var(--bg3);border:1px solid var(--br);border-radius:8px;border-left:4px solid ${ind.color}">
      <div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:6px">
        <span style="font-size:11px;color:var(--m);font-weight:600;text-transform:uppercase;letter-spacing:.5px">${ind.name}</span>
        <span style="font-size:10px;font-weight:600;padding:2px 7px;border-radius:4px;background:${ind.color}22;color:${ind.color}">${ind.label}</span>
      </div>
      <div style="font-size:26px;font-weight:700;color:${ind.color};line-height:1;margin-bottom:6px">${ind.fmt}</div>
      <div style="font-size:11px;color:var(--m);line-height:1.4">${ind.desc}</div>
    </div>`).join('');

  if (panel) panel.style.display = indicators.length ? 'block' : 'none';
}

// ——— GOOGLE ADS: INDICADORES DE OPTIMIZACIÓN ———
function renderGoogleOptimIndicators(m, camps) {
  const grid  = document.getElementById('g-optim-grid');
  const panel = document.getElementById('g-optim-panel');
  if (!grid || !m) return;

  const imp      = parseInt(m.impressions)    || 0;
  const clicks   = parseInt(m.clicks)         || 0;
  const ctr      = parseFloat(m.ctr)          || 0;
  const roas     = parseFloat(m.roas)         || 0;
  const cpa      = parseFloat(m.cost_per_conv)|| 0;
  const convs    = parseFloat(m.conversions)  || 0;
  const convRate = clicks > 0 ? (convs / clicks * 100) : 0;

  // Impression Share promedio de campañas (competencia)
  const campsWithIS = (camps||[]).filter(c => c.imp_share > 0);
  const avgIS = campsWithIS.length
    ? campsWithIS.reduce((s, c) => s + (c.imp_share||0), 0) / campsWithIS.length
    : 0;

  const indicators = [];

  // CTR (Search: ≥5% excelente, 2-5% aceptable, <2% revisar)
  if (imp > 0) {
    const color = ctr >= 5 ? 'var(--g)' : ctr >= 2 ? 'var(--y)' : 'var(--red)';
    const label = ctr >= 5 ? 'Excelente' : ctr >= 2 ? 'Aceptable' : 'Revisar';
    const desc  = ctr >= 5 ? 'Tus anuncios generan mucha atención en búsquedas'
      : ctr >= 2 ? 'Hay margen de mejora en títulos y extensiones'
      : 'Los anuncios no generan clics — revisar copy y keywords negativas';
    indicators.push({ name:'CTR', fmt:ctr.toFixed(2)+'%', color, label, desc });
  }

  // ROAS
  if (roas > 0) {
    const color = roas >= 4 ? 'var(--g)' : roas >= 2 ? 'var(--y)' : 'var(--red)';
    const label = roas >= 4 ? 'Excelente' : roas >= 2 ? 'Aceptable' : 'Revisar';
    const desc  = `Por cada $1 invertido recuperas $${roas.toFixed(1)}`
      + (roas >= 4 ? ' — escalar presupuesto' : roas >= 2 ? ' — optimizar pujas y landing' : ' — revisar oferta, keywords y landing page');
    indicators.push({ name:'ROAS', fmt:roas.toFixed(2)+'×', color, label, desc });
  }

  // Tasa de conversión
  if (clicks > 0) {
    const color = convRate >= 5 ? 'var(--g)' : convRate >= 2 ? 'var(--y)' : 'var(--red)';
    const label = convRate >= 5 ? 'Excelente' : convRate >= 2 ? 'Aceptable' : 'Revisar';
    const desc  = convRate >= 5 ? 'Tu landing convierte muy bien — mantener'
      : convRate >= 2 ? 'Mejorar landing page y llamados a la acción'
      : 'La landing no convierte — revisar velocidad, formulario y oferta';
    indicators.push({ name:'Conv. Rate', fmt:convRate.toFixed(2)+'%', color, label, desc });
  }

  // CPA
  if (cpa > 0) {
    const cpaFmt = cpa >= 1000000 ? '$'+(cpa/1000000).toFixed(1)+'M' : cpa >= 1000 ? '$'+(cpa/1000).toFixed(0)+'K' : '$'+cpa.toFixed(0);
    indicators.push({ name:'CPA', fmt:cpaFmt, color:'var(--b)', label:'Informativo', desc:'Cuánto cuesta cada conversión — compara con tu margen de ganancia' });
  }

  // Impression Share (competencia)
  if (avgIS > 0) {
    const color = avgIS >= 60 ? 'var(--g)' : avgIS >= 30 ? 'var(--y)' : 'var(--red)';
    const label = avgIS >= 60 ? 'Dominando' : avgIS >= 30 ? 'Oportunidad' : 'Perdiendo';
    const desc  = avgIS >= 60
      ? `Apareces en el ${avgIS.toFixed(0)}% de búsquedas disponibles — buena visibilidad`
      : avgIS >= 30
      ? `Solo apareces en el ${avgIS.toFixed(0)}% de búsquedas — aumentar presupuesto o mejorar Quality Score`
      : `Solo el ${avgIS.toFixed(0)}% de visibilidad — la competencia te está ganando en la subasta`;
    indicators.push({ name:'Imp. Share', fmt:avgIS.toFixed(0)+'%', color, label, desc });
  }

  // CPC promedio (informativo)
  if (m.avg_cpc > 0) {
    const cpcFmt = m.avg_cpc >= 1000000 ? '$'+(m.avg_cpc/1000000).toFixed(1)+'M' : m.avg_cpc >= 1000 ? '$'+(m.avg_cpc/1000).toFixed(0)+'K' : '$'+m.avg_cpc.toFixed(0);
    indicators.push({ name:'CPC Prom.', fmt:cpcFmt, color:'var(--b)', label:'Informativo', desc:'Costo por clic promedio — compara con el valor de cada cliente para tu negocio' });
  }

  grid.innerHTML = indicators.map(ind => `
    <div style="padding:10px 14px;background:var(--bg3);border:1px solid var(--br);border-radius:8px;border-left:4px solid ${ind.color}">
      <div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:6px">
        <span style="font-size:11px;color:var(--m);font-weight:600;text-transform:uppercase;letter-spacing:.5px">${ind.name}</span>
        <span style="font-size:10px;font-weight:600;padding:2px 7px;border-radius:4px;background:${ind.color}22;color:${ind.color}">${ind.label}</span>
      </div>
      <div style="font-size:26px;font-weight:700;color:${ind.color};line-height:1;margin-bottom:6px">${ind.fmt}</div>
      <div style="font-size:11px;color:var(--m);line-height:1.4">${ind.desc}</div>
    </div>`).join('');

  if (panel) panel.style.display = indicators.length ? 'block' : 'none';
}

// ——— META ADS: CONJUNTOS (AD SETS) ———
function loadMetaAdSets() {
  if (!currentClient || currentClient === 'all') return;
  const campId = document.getElementById('meta-as-camp-filter')?.value || '';
  const period = document.getElementById('meta-period-sel')?.value || '30d';

  const loading = document.getElementById('meta-as-loading');
  const wrap    = document.getElementById('meta-as-wrap');
  const empty   = document.getElementById('meta-as-empty');
  if (loading) { loading.style.display = 'block'; loading.textContent = 'Cargando conjuntos...'; }
  if (wrap)    wrap.style.display  = 'none';
  if (empty)   empty.style.display = 'none';

  let url = `api/meta-data.php?client_id=${currentClient}&action=all_adsets&period=${period}`;
  if (period === 'custom') {
    const since = document.getElementById('meta-date-from')?.value;
    const until = document.getElementById('meta-date-to')?.value;
    if (since && until) url += `&since=${since}&until=${until}`;
  }
  if (campId) url += `&campaign_id=${encodeURIComponent(campId)}`;

  fetch(url)
    .then(r => r.json())
    .then(data => {
      if (loading) loading.style.display = 'none';
      if (!data.ok || !data.adsets?.length) {
        if (empty) { empty.style.display = 'block'; empty.textContent = data.error || 'Sin conjuntos en este período.'; }
        return;
      }
      renderMetaAdSets(data.adsets);
      if (wrap) wrap.style.display = 'block';
    })
    .catch(() => {
      if (loading) { loading.style.display = 'block'; loading.textContent = 'Error de conexión.'; }
    });
}

function renderMetaAdSets(list) {
  const tbody = document.getElementById('meta-as-body');
  if (!tbody) return;
  tbody.innerHTML = list.map(as => {
    const isActive  = as.status === 'ACTIVE' || as.status === 'active';
    const ctr       = parseFloat(as.ctr)       || 0;
    const freq      = parseFloat(as.frequency) || 0;
    const ctrColor  = ctr  >= 1.5 ? 'var(--g)' : ctr  >= 0.5 ? 'var(--y)' : ctr  > 0 ? 'var(--red)' : '';
    const freqColor = freq >  3   ? 'var(--red)' : freq >= 2  ? 'var(--y)' : freq > 0 ? 'var(--g)'   : '';
    const freqTip   = freq > 3 ? ' title="⚠ Fatiga publicitaria"' : freq >= 2 ? ' title="Frecuencia alta — vigilar"' : '';
    const statusPill = isActive
      ? `<span class="pill pill-ok">Activo</span>`
      : `<span class="pill pill-pause">Pausado</span>`;
    const toggleBtn = isActive
      ? `<button onclick="metaAdSetAction('pause_adset','${as.id}',this)" style="padding:3px 8px;border-radius:5px;font-size:11px;background:rgba(245,197,67,.15);border:1px solid rgba(245,197,67,.3);color:var(--y);cursor:pointer;font-family:var(--fb)">⏸ Pausar</button>`
      : `<button onclick="metaAdSetAction('activate_adset','${as.id}',this)" style="padding:3px 8px;border-radius:5px;font-size:11px;background:rgba(34,201,122,.15);border:1px solid rgba(34,201,122,.3);color:var(--g);cursor:pointer;font-family:var(--fb)">▶ Activar</button>`;
    const optGoal = (as.optimization||'').replace(/_/g,' ');
    return `<tr>
      <td style="max-width:200px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap" title="${escapeHtml(as.name)}">${escapeHtml(as.name)}</td>
      <td>${statusPill}</td>
      <td style="font-size:11px;color:var(--m)">${optGoal}</td>
      <td>${as.budget_fmt||'—'}</td>
      <td>${as.spend_fmt||'—'}</td>
      <td>${as.reach?parseInt(as.reach).toLocaleString():'—'}</td>
      <td>${as.clicks?parseInt(as.clicks).toLocaleString():'—'}</td>
      <td style="color:${ctrColor}">${ctr>0?ctr.toFixed(2)+'%':'—'}</td>
      <td>${freq>0?`<span style="display:inline-block;padding:2px 7px;border-radius:20px;font-size:11px;font-weight:700;background:${freq>3?'rgba(232,39,42,.15)':freq>=2?'rgba(245,197,67,.15)':'rgba(34,201,122,.15)'};color:${freqColor}"${freqTip}>${freq.toFixed(2)}</span>`:'—'}</td>
      <td>${(as.conversions||0)>0?as.conversions:'—'}</td>
      <td>${as.cpa_fmt||'—'}</td>
      <td style="white-space:nowrap">${toggleBtn}</td>
    </tr>`;
  }).join('');
}

function metaAdSetAction(action, adsetId, btn) {
  const origText = btn ? btn.textContent : '';
  if (btn) { btn.disabled = true; btn.textContent = '...'; }
  fetch('api/meta-action.php', {
    method: 'POST',
    headers: {'Content-Type':'application/json'},
    body: JSON.stringify({client_id:parseInt(currentClient), action, adset_id:adsetId})
  })
  .then(r => r.json())
  .then(res => {
    if (res.ok) {
      showToast('✓ ' + res.msg, 'var(--g)');
      setTimeout(() => loadMetaAdSets(), 800);
    } else {
      showToast('✗ ' + (res.error||'Error'), 'var(--red)');
      if (btn) { btn.disabled = false; btn.textContent = origText; }
    }
  }).catch(() => {
    showToast('✗ Error de conexión', 'var(--red)');
    if (btn) { btn.disabled = false; btn.textContent = origText; }
  });
}

// ——— META ADS: ANUNCIOS INDIVIDUALES ———
let metaAdsCurrentList = [];

function loadMetaAdsTab() {
  if (!currentClient || currentClient === 'all') return;
  const campId = document.getElementById('meta-ads-camp-filter')?.value || '';
  const period = document.getElementById('meta-period-sel')?.value || '30d';

  const loading = document.getElementById('meta-ads-loading');
  const grid    = document.getElementById('meta-ads-grid');
  const tWrap   = document.getElementById('meta-ads-table-wrap');
  const empty   = document.getElementById('meta-ads-empty');
  if (loading) { loading.style.display = 'block'; loading.textContent = 'Cargando anuncios...'; }
  if (grid)    grid.style.display  = 'none';
  if (tWrap)   tWrap.style.display = 'none';
  if (empty)   empty.style.display = 'none';

  let url = `api/meta-data.php?client_id=${currentClient}&action=ads&period=${period}`;
  if (period === 'custom') {
    const since = document.getElementById('meta-date-from')?.value;
    const until = document.getElementById('meta-date-to')?.value;
    if (since && until) url += `&since=${since}&until=${until}`;
  }
  if (campId) url += `&campaign_id=${encodeURIComponent(campId)}`;

  fetch(url)
    .then(r => r.json())
    .then(data => {
      if (loading) loading.style.display = 'none';
      if (!data.ok || !data.ads?.length) {
        if (empty) { empty.style.display = 'block'; empty.textContent = data.error || 'Sin anuncios en este período.'; }
        return;
      }
      metaAdsCurrentList = data.ads;
      const view = document.getElementById('meta-ads-view')?.value || 'grid';
      if (view === 'table') {
        renderMetaAdsTable(data.ads);
        if (tWrap) tWrap.style.display = 'block';
      } else {
        renderMetaAds(data.ads);
        if (grid) grid.style.display = 'grid';
      }
    })
    .catch(() => {
      if (loading) { loading.style.display = 'block'; loading.textContent = 'Error de conexión.'; }
    });
}

function renderMetaAds(list) {
  const grid = document.getElementById('meta-ads-grid');
  if (!grid) return;
  grid.innerHTML = list.map(ad => {
    const isActive = ad.status === 'ACTIVE' || ad.status === 'active';
    const ctr = parseFloat(ad.ctr) || 0;
    const ctrColor = ctr >= 1.5 ? 'var(--g)' : ctr >= 0.5 ? 'var(--y)' : ctr > 0 ? 'var(--red)' : 'var(--m)';
    const statusDot = isActive ? 'var(--g)' : 'var(--m)';
    const thumb = ad.thumbnail
      ? `<img src="${escapeHtml(ad.thumbnail)}" style="width:100%;height:130px;object-fit:cover;border-radius:6px 6px 0 0;display:block" onerror="this.style.display='none'">`
      : `<div style="width:100%;height:80px;background:var(--bg2);border-radius:6px 6px 0 0;display:flex;align-items:center;justify-content:center;font-size:28px">📷</div>`;
    const toggleBtn = isActive
      ? `<button onclick="metaAdAction('pause_ad','${ad.id}',this)" style="flex:1;padding:4px;border-radius:5px;font-size:10px;background:rgba(245,197,67,.15);border:1px solid rgba(245,197,67,.3);color:var(--y);cursor:pointer;font-family:var(--fb)">⏸ Pausar</button>`
      : `<button onclick="metaAdAction('activate_ad','${ad.id}',this)" style="flex:1;padding:4px;border-radius:5px;font-size:10px;background:rgba(34,201,122,.15);border:1px solid rgba(34,201,122,.3);color:var(--g);cursor:pointer;font-family:var(--fb)">▶ Activar</button>`;
    return `<div style="background:var(--bg2);border:1px solid var(--br);border-radius:8px;overflow:hidden">
      ${thumb}
      <div style="padding:8px 10px">
        <div style="display:flex;align-items:center;gap:5px;margin-bottom:4px">
          <div style="width:7px;height:7px;border-radius:50%;background:${statusDot};flex-shrink:0"></div>
          <div style="font-size:11px;color:var(--t);font-weight:600;overflow:hidden;text-overflow:ellipsis;white-space:nowrap" title="${escapeHtml(ad.name)}">${escapeHtml(ad.name)}</div>
        </div>
        ${ad.title ? `<div style="font-size:10px;color:var(--m);font-weight:600;margin-bottom:2px">${escapeHtml(ad.title)}</div>` : ''}
        ${ad.body  ? `<div style="font-size:10px;color:var(--m);margin-bottom:6px;line-height:1.4;overflow:hidden;max-height:32px">${escapeHtml(ad.body)}</div>` : ''}
        <div style="display:grid;grid-template-columns:1fr 1fr 1fr;gap:3px;margin-bottom:6px">
          <div style="text-align:center;padding:3px;background:var(--bg3);border-radius:4px">
            <div style="font-size:9px;color:var(--m)">CTR</div>
            <div style="font-size:11px;font-weight:600;color:${ctrColor}">${ctr>0?ctr.toFixed(1)+'%':'—'}</div>
          </div>
          <div style="text-align:center;padding:3px;background:var(--bg3);border-radius:4px">
            <div style="font-size:9px;color:var(--m)">Gasto</div>
            <div style="font-size:11px;font-weight:600;color:var(--t)">${ad.spend_fmt||'—'}</div>
          </div>
          <div style="text-align:center;padding:3px;background:var(--bg3);border-radius:4px">
            <div style="font-size:9px;color:var(--m)">Imp.</div>
            <div style="font-size:11px;font-weight:600;color:var(--t)">${ad.impressions>1000?(ad.impressions/1000).toFixed(1)+'K':ad.impressions||'—'}</div>
          </div>
        </div>
        <div style="display:flex;gap:4px">${toggleBtn}</div>
      </div>
    </div>`;
  }).join('');
}

function renderMetaAdsTable(list) {
  const tbody = document.getElementById('meta-ads-table-body');
  if (!tbody) return;
  tbody.innerHTML = list.map(ad => {
    const isActive  = ad.status === 'ACTIVE' || ad.status === 'active';
    const ctr       = parseFloat(ad.ctr)       || 0;
    const freq      = parseFloat(ad.frequency) || 0;
    const ctrColor  = ctr  >= 1.5 ? 'var(--g)' : ctr  >= 0.5 ? 'var(--y)' : ctr  > 0 ? 'var(--red)' : '';
    const freqColor = freq > 3 ? 'var(--red)' : freq >= 2 ? 'var(--y)' : '';
    const statusPill = isActive
      ? `<span class="pill pill-ok">Activo</span>`
      : `<span class="pill pill-pause">Pausado</span>`;
    const toggleBtn = isActive
      ? `<button onclick="metaAdAction('pause_ad','${ad.id}',this)" style="padding:3px 8px;border-radius:5px;font-size:11px;background:rgba(245,197,67,.15);border:1px solid rgba(245,197,67,.3);color:var(--y);cursor:pointer;font-family:var(--fb)">⏸ Pausar</button>`
      : `<button onclick="metaAdAction('activate_ad','${ad.id}',this)" style="padding:3px 8px;border-radius:5px;font-size:11px;background:rgba(34,201,122,.15);border:1px solid rgba(34,201,122,.3);color:var(--g);cursor:pointer;font-family:var(--fb)">▶ Activar</button>`;
    const thumb = ad.thumbnail
      ? `<img src="${escapeHtml(ad.thumbnail)}" style="width:48px;height:48px;object-fit:cover;border-radius:5px" onerror="this.style.opacity='.3'">`
      : `<div style="width:48px;height:48px;background:var(--bg3);border-radius:5px;display:flex;align-items:center;justify-content:center;font-size:18px">📷</div>`;
    return `<tr>
      <td>${thumb}</td>
      <td style="max-width:180px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap" title="${escapeHtml(ad.name)}">${escapeHtml(ad.name)}</td>
      <td>${statusPill}</td>
      <td>${ad.spend_fmt||'—'}</td>
      <td>${ad.reach?parseInt(ad.reach).toLocaleString():'—'}</td>
      <td>${ad.clicks?parseInt(ad.clicks).toLocaleString():'—'}</td>
      <td style="color:${ctrColor}">${ctr>0?ctr.toFixed(2)+'%':'—'}</td>
      <td style="color:${freqColor}">${freq>0?freq.toFixed(2):'—'}</td>
      <td>${(ad.conversions||0)>0?ad.conversions:'—'}</td>
      <td>${ad.cpa_fmt||'—'}</td>
      <td>${toggleBtn}</td>
    </tr>`;
  }).join('');
}

function toggleMetaAdsView() {
  const view  = document.getElementById('meta-ads-view')?.value || 'grid';
  const grid  = document.getElementById('meta-ads-grid');
  const tWrap = document.getElementById('meta-ads-table-wrap');
  if (!metaAdsCurrentList.length) { loadMetaAdsTab(); return; }
  if (view === 'table') {
    if (grid)  grid.style.display  = 'none';
    if (tWrap) { tWrap.style.display = 'block'; renderMetaAdsTable(metaAdsCurrentList); }
  } else {
    if (tWrap) tWrap.style.display = 'none';
    if (grid)  { grid.style.display = 'grid'; renderMetaAds(metaAdsCurrentList); }
  }
}

function metaAdAction(action, adId, btn) {
  const origText = btn ? btn.textContent : '';
  if (btn) { btn.disabled = true; btn.textContent = '...'; }
  fetch('api/meta-action.php', {
    method: 'POST',
    headers: {'Content-Type':'application/json'},
    body: JSON.stringify({client_id:parseInt(currentClient), action, ad_id:adId})
  })
  .then(r => r.json())
  .then(res => {
    if (res.ok) {
      showToast('✓ ' + res.msg, 'var(--g)');
      setTimeout(() => loadMetaAdsTab(), 800);
    } else {
      showToast('✗ ' + (res.error||'Error'), 'var(--red)');
      if (btn) { btn.disabled = false; btn.textContent = origText; }
    }
  }).catch(() => {
    showToast('✗ Error de conexión', 'var(--red)');
    if (btn) { btn.disabled = false; btn.textContent = origText; }
  });
}

// ——— META ADS: BREAKDOWN POR DIMENSIÓN ———
function loadMetaBreakdown(type) {
  if (!currentClient || currentClient === 'all') {
    const cont = document.getElementById('meta-bd-content');
    if (cont) cont.innerHTML = '<div style="font-size:12px;color:var(--m);padding:8px 0">Selecciona un cliente para ver el desglose.</div>';
    return;
  }
  document.querySelectorAll('.meta-bd-btn').forEach(b => {
    const active = b.dataset.bd === type;
    b.style.background  = active ? 'rgba(232,39,42,.15)' : 'var(--bg3)';
    b.style.borderColor = active ? 'rgba(232,39,42,.3)'  : 'var(--br2)';
    b.style.color       = active ? 'var(--red)' : 'var(--m)';
  });

  const period = document.getElementById('meta-period-sel')?.value || '30d';
  const cont   = document.getElementById('meta-bd-content');
  if (cont) cont.innerHTML = '<div style="font-size:12px;color:var(--m);padding:8px 0">Cargando desglose...</div>';

  let url = `api/meta-data.php?client_id=${currentClient}&action=breakdown&breakdown=${type}&period=${period}`;
  if (period === 'custom') {
    const since = document.getElementById('meta-date-from')?.value;
    const until = document.getElementById('meta-date-to')?.value;
    if (since && until) url += `&since=${since}&until=${until}`;
  }

  fetch(url)
    .then(r => r.json())
    .then(data => {
      if (!data.ok || !data.breakdown?.length) {
        if (cont) cont.innerHTML = '<div style="font-size:12px;color:var(--m);padding:8px 0">' + escapeHtml(data.error||'Sin datos de desglose.') + '</div>';
        return;
      }
      const rows     = data.breakdown;
      const maxSpend = Math.max(...rows.map(r => parseFloat(r.spend)||0));
      const labels   = {publisher_platform:'Plataforma',device_platform:'Dispositivo',age:'Edad',gender:'Género'};
      let html = `<div style="font-size:11px;color:var(--m);margin-bottom:8px;text-transform:uppercase;letter-spacing:.6px">Desglose por ${labels[type]||type}</div>`;
      html += '<div style="overflow-x:auto"><table class="tbl" style="min-width:580px"><thead><tr>';
      html += `<th>${labels[type]||'Grupo'}</th><th>Gasto</th><th>Alcance</th><th>Clics</th><th>CTR%</th><th>Conv.</th><th>CPM</th></tr></thead><tbody>`;
      html += rows.map(r => {
        const pct      = maxSpend > 0 ? ((parseFloat(r.spend)||0)/maxSpend*100).toFixed(0) : 0;
        const ctr      = parseFloat(r.ctr) || 0;
        const ctrColor = ctr >= 1.5 ? 'var(--g)' : ctr >= 0.5 ? 'var(--y)' : ctr > 0 ? 'var(--red)' : '';
        const cpm      = parseFloat(r.cpm) || 0;
        const cpmFmt   = cpm>=1000000?'$'+(cpm/1000000).toFixed(1)+'M':cpm>=1000?'$'+(cpm/1000).toFixed(0)+'K':cpm>0?'$'+cpm.toFixed(0):'—';
        return `<tr>
          <td style="min-width:110px">
            <div style="font-weight:600;color:var(--t);font-size:12px">${escapeHtml(r.label||'—')}</div>
            <div style="height:3px;background:var(--br);border-radius:2px;margin-top:3px"><div style="height:100%;background:var(--red);border-radius:2px;width:${pct}%"></div></div>
          </td>
          <td>${r.spend_fmt||'—'}</td>
          <td>${r.reach?parseInt(r.reach).toLocaleString():'—'}</td>
          <td>${r.clicks?parseInt(r.clicks).toLocaleString():'—'}</td>
          <td style="color:${ctrColor}">${ctr>0?ctr.toFixed(2)+'%':'—'}</td>
          <td>${(r.conversions||0)>0?r.conversions:'—'}</td>
          <td>${cpmFmt}</td>
        </tr>`;
      }).join('');
      html += '</tbody></table></div>';
      if (cont) cont.innerHTML = html;
    })
    .catch(() => {
      if (cont) cont.innerHTML = '<div style="font-size:12px;color:var(--red);padding:8px 0">Error al cargar desglose.</div>';
    });
}

// ——— META ADS: AVI PÚBLICOS ———
function metaPubAVI(question) {
  const q = question || document.getElementById('meta-pub-q')?.value.trim();
  if (!q) return;
  const ctx   = getContext('meta');
  const m     = metaCurrentData?.metrics;
  const camps = (metaCampsAll||[]).filter(c=>c.status==='active').map(c=>c.name).join(', ');
  const dataCtx = m
    ? `Datos Meta: Inversión ${m.spend_fmt}, CTR ${m.ctr}%, Frecuencia ${m.frequency}, Alcance ${m.reach?m.reach.toLocaleString():'—'}. Campañas activas: ${camps||'ninguna'}.`
    : 'Sin datos de campaña cargados.';
  callAVI(q, `${dataCtx} ${ctx}`, 'avi-resp-meta-pub', 'AVI analizando públicos...', 'meta');
  const inp = document.getElementById('meta-pub-q');
  if (inp) inp.value = '';
}

// ——— META ADS: ANALIZAR TAB CON AVI ———
function metaAnalyzeTab(tab, respId) {
  const resp = document.getElementById(respId);
  if (!resp) return;
  resp.style.display = 'block';
  const ctx     = getContext('meta');
  const m       = metaCurrentData?.metrics;
  const baseCtx = m
    ? `Datos del período — Inversión: ${m.spend_fmt}, ROAS: ${m.roas||'N/A'}×, CTR: ${m.ctr}%, Frecuencia: ${m.frequency}, Alcance: ${m.reach?m.reach.toLocaleString():'—'}, Conversiones: ${m.conversations||0}. Contexto: ${ctx}`
    : `Sin datos cargados. Contexto: ${ctx}`;
  let q;
  if (tab === 'campanas') {
    const names = metaCampsAll.map(c=>`${c.name} (${c.status}, gasto:${c.spend_fmt}, CTR:${c.ctr}%)`).join('; ');
    q = `Analiza estas campañas de Meta Ads como experto: ${names}. Identifica las de mejor rendimiento, detecta problemas (frecuencia >3, CTR <1%, sin conversiones con gasto alto) y da acciones concretas. ${baseCtx}`;
  } else if (tab === 'conjuntos') {
    q = `Analiza los conjuntos de anuncios de este cliente en Meta Ads. Identifica patrones por objetivo de optimización, presupuesto y eficiencia. Recomienda qué conjuntos pausar, cuáles escalar y cómo redistribuir el presupuesto. ${baseCtx}`;
  } else if (tab === 'anuncios') {
    const adNames = metaAdsCurrentList.slice(0,10).map(a=>`${a.name} (CTR:${a.ctr}%, gasto:${a.spend_fmt})`).join('; ');
    q = `Analiza estos anuncios de Meta Ads: ${adNames||'sin anuncios cargados'}. Identifica cuáles creativos funcionan mejor, cuáles pausar y qué creativos nuevos probar. ${baseCtx}`;
  } else {
    q = `Analiza los datos de la sección ${tab} de Meta Ads para este cliente y da recomendaciones. ${baseCtx}`;
  }
  callAVI(q, ctx, respId, 'AVI analizando...', 'meta');
}

// ——— WIZARD: GENERAR COPY CON AVI ———
function wizardGenerateCopy() {
  const q    = document.getElementById('wizard-copy-q')?.value.trim();
  const ctx  = getContext('meta');
  const obj  = document.getElementById('wc-objective')?.value  || '';
  const pub  = document.getElementById('was-location')?.value  || document.getElementById('brief-publico')?.value || '';
  const goal = document.getElementById('was-optimization')?.value || '';
  const objLabels  = {OUTCOME_LEADS:'Generación de Leads',OUTCOME_SALES:'Ventas',OUTCOME_TRAFFIC:'Tráfico',OUTCOME_ENGAGEMENT:'Interacción'};
  const goalLabels = {CONVERSATIONS:'Conversaciones',LEAD_GENERATION:'Leads',LINK_CLICKS:'Clics',REACH:'Alcance'};
  const base = `Crea copy optimizado para un anuncio de Meta Ads. Objetivo: ${objLabels[obj]||obj}. Optimización: ${goalLabels[goal]||goal}. Público: ${pub||'no especificado'}. ${q?'Requisitos: '+q+'.':''} Contexto: ${ctx}.`;
  const prompt = `${base}

Genera:
1. **Titular** (máx 40 caracteres, directo e impactante)
2. **Texto principal** (máx 125 caracteres, enfocado en el beneficio)
3. **Variante B** para test A/B
4. **CTA recomendado** y por qué
5. **Hook** para versión en video/reels

Sé específico, usa verbos de acción y habla directamente al dolor o deseo del público objetivo.`;

  callAVI(prompt, ctx, 'wizard-copy-resp', 'AVI generando copy...', 'meta', text => {
    const headlineMatch = text.match(/\*\*Titular\*\*[:\s]+(.{5,40})/);
    const textMatch     = text.match(/\*\*Texto principal\*\*[:\s]+(.{5,125})/);
    if (headlineMatch) {
      const hl = document.getElementById('ad-headline');
      if (hl && !hl.value) { hl.value = headlineMatch[1].trim().replace(/\*\*/g,''); const ap = document.getElementById('ap-head'); if(ap) ap.textContent = hl.value; }
    }
    if (textMatch) {
      const pt = document.getElementById('ad-primary-text');
      if (pt && !pt.value) { pt.value = textMatch[1].trim().replace(/\*\*/g,''); const ap = document.getElementById('ap-text'); if(ap) ap.textContent = pt.value; }
    }
  });
}

// ——— WIZARD CREAR CAMPAÑA ———
function goWizardStep(step) {
  document.getElementById('wizard-p1').style.display = step===1 ? 'flex' : 'none';
  document.getElementById('wizard-p2').style.display = step===2 ? 'block' : 'none';
  document.getElementById('wizard-p3').style.display = step===3 ? 'block' : 'none';
  document.getElementById('wizard-p4').style.display = step===4 ? 'block' : 'none';
  document.querySelectorAll('.wizard-step').forEach((el, i) => {
    const isActive = i+1 <= step;
    el.style.background = isActive ? 'var(--rl)' : 'var(--bg3)';
    el.style.borderColor = isActive ? 'var(--rb)' : 'var(--br2)';
    el.style.color = isActive ? 'var(--red)' : 'var(--m)';
  });
  if (step === 4) buildWizardSummary();
  if (step === 2) _loadMetaAudienceIfSaved();
  if (step === 3 && (!metaPages || !metaPages.length)) loadMetaPages();
}

function saveMetaAudience() {
  if (!currentClient || currentClient === 'all') { showToast('Selecciona un cliente primero', 'var(--y)'); return; }
  const gAll = document.getElementById('was-gender-all');
  const gM   = document.getElementById('was-gender-m');
  const gF   = document.getElementById('was-gender-f');
  const gender = gM?.classList.contains('active') ? 'male' : gF?.classList.contains('active') ? 'female' : 'all';
  const aud = {
    location:     document.getElementById('was-location')?.value    || '',
    ageMin:       document.getElementById('was-age-min')?.value     || '',
    ageMax:       document.getElementById('was-age-max')?.value     || '',
    gender,
    interests:    document.getElementById('was-interests')?.value   || '',
    budget:       document.getElementById('was-budget')?.value      || '',
    budgetType:   document.getElementById('was-budget-type')?.value || 'daily',
    optimization: document.getElementById('was-optimization')?.value|| '',
    bid:          document.getElementById('was-bid')?.value         || '',
    linkUrl:      document.getElementById('ad-link-url')?.value     || '',
  };
  localStorage.setItem(`meta_audience_${currentClient}`, JSON.stringify(aud));
  showToast('✓ Audiencia guardada — se pre-cargará en la próxima campaña', 'var(--g)');
}

function _loadMetaAudienceIfSaved() {
  if (!currentClient || currentClient === 'all') return;
  const raw = localStorage.getItem(`meta_audience_${currentClient}`);
  if (!raw) return;
  try {
    const a = JSON.parse(raw);
    const set = (id, val) => { const el = document.getElementById(id); if (el && val) el.value = val; };
    set('was-location',    a.location);
    set('was-age-min',     a.ageMin);
    set('was-age-max',     a.ageMax);
    set('was-interests',   a.interests);
    set('was-budget',      a.budget);
    set('ad-link-url',     a.linkUrl);
    set('was-budget-type',    a.budgetType);
    set('was-optimization',   a.optimization);
    set('was-bid',            a.bid);
    const gAll = document.getElementById('was-gender-all');
    const gM   = document.getElementById('was-gender-m');
    const gF   = document.getElementById('was-gender-f');
    if (gAll && gM && gF) {
      [gAll,gM,gF].forEach(e => e.classList.remove('active'));
      if (a.gender === 'male')        gM.classList.add('active');
      else if (a.gender === 'female') gF.classList.add('active');
      else                            gAll.classList.add('active');
    }
  } catch(e) {}
}

function runBriefAVI() {
  const obj   = document.getElementById('brief-objetivo')?.value;
  const pres  = document.getElementById('brief-presupuesto')?.value;
  const pub   = document.getElementById('brief-publico')?.value;
  const dur   = document.getElementById('brief-duracion')?.value;
  const comp  = document.getElementById('brief-competidores')?.value;
  const ctx   = getContext('meta');
  const data  = metaCurrentData;
  const mdata = data ? `Inversión actual: ${data.metrics.spend_fmt}, CTR: ${data.metrics.ctr}%, Frecuencia: ${data.metrics.frequency}, Conversaciones: ${data.metrics.conversations}` : '';
  const c = currentClient!=='all' ? CLIENTS.find(x=>x.id==currentClient) : null;
  const q = `Actúa como estratega experto en Meta Ads. Con base en los siguientes datos del cliente, recomienda la configuración ideal para una nueva campaña:
Objetivo: ${obj||'no especificado'}
Presupuesto: ${pres||'no especificado'} COP/día
Público: ${pub||'no especificado'}
Duración: ${dur||'no especificada'}
Competidores mencionados: ${comp||'identifícalos tú basado en la industria'}
Rendimiento actual: ${mdata}
Contexto: ${ctx}
Incluye: objetivo de campaña recomendado, estructura de Ad Sets, segmentación detallada, estrategia de puja, copy sugerido para el anuncio, y por qué estas recomendaciones tienen sentido para este negocio.`;

  callAVI(q, ctx, 'brief-resp', 'AVI analizando y diseñando la estrategia...', 'meta', () => {
    // Tras el análisis mostrar botón para aplicar configuración al formulario
    const resp = document.getElementById('brief-resp');
    if (!resp) return;
    const existing = document.getElementById('brief-apply-btn');
    if (existing) existing.remove();
    const btn = document.createElement('button');
    btn.id = 'brief-apply-btn';
    btn.textContent = '✦ Aplicar configuración al formulario →';
    btn.style.cssText = 'margin-top:10px;padding:8px 18px;border-radius:7px;font-size:12px;background:var(--red);border:none;color:#fff;cursor:pointer;font-family:var(--fb);display:block';
    btn.onclick = () => applyBriefPlan(obj, pres, pub, dur, comp, ctx, c?.name||'');
    resp.parentElement.appendChild(btn);
  });
}

function applyBriefPlan(obj, pres, pub, dur, comp, ctx, clientName) {
  const btn = document.getElementById('brief-apply-btn');
  if (btn) { btn.disabled=true; btn.textContent='Generando plan...'; }
  fetch('api/meta-action.php', {
    method: 'POST',
    headers: {'Content-Type':'application/json'},
    body: JSON.stringify({
      client_id:    parseInt(currentClient),
      action:       'ai_plan_campaign',
      objetivo:     obj||'',
      presupuesto:  pres||'',
      publico:      pub||'',
      duracion:     dur||'',
      competidores: comp||'',
      context:      ctx,
      client_name:  clientName,
    })
  })
  .then(r=>r.json())
  .then(res => {
    if (btn) { btn.disabled=false; btn.textContent='✦ Aplicar configuración al formulario →'; }
    if (!res.ok) { showToast('✗ '+(res.error||'Error generando plan'), 'var(--red)'); return; }
    const p = res.plan;
    // Rellenar campos Paso 2
    const set = (id, val) => { const el=document.getElementById(id); if(el) el.value=val; };
    set('wc-name',         p.campaign_name || '');
    set('was-name',        p.adset_name    || '');
    set('was-budget',      p.daily_budget  || '');
    set('was-age-min',     p.age_min       || 18);
    set('was-age-max',     p.age_max       || 65);
    set('was-interests',   p.interests     || '');
    set('was-location',    p.location      || 'CO');
    // Selects
    const selSet = (id, val) => { const el=document.getElementById(id); if(el) el.value=val; };
    selSet('wc-objective',   p.objective        || 'OUTCOME_LEADS');
    selSet('was-optimization',p.optimization_goal|| 'CONVERSATIONS');
    selSet('was-bid',         p.bid_strategy     || 'LOWEST_COST_WITHOUT_CAP');
    selSet('was-budget-type', p.budget_type      || 'daily');
    // Género
    const gAll=document.getElementById('was-gender-all');
    const gM=document.getElementById('was-gender-m');
    const gF=document.getElementById('was-gender-f');
    if (gAll&&gM&&gF) {
      [gAll,gM,gF].forEach(e=>e.classList.remove('active'));
      if (p.gender==='male') gM.classList.add('active');
      else if (p.gender==='female') gF.classList.add('active');
      else gAll.classList.add('active');
    }
    // Mostrar rationale
    if (p.rationale || p.tips) {
      const resp = document.getElementById('brief-resp');
      if (resp) {
        const note = document.createElement('div');
        note.style.cssText = 'margin-top:8px;padding:10px 12px;background:var(--rl);border:1px solid var(--rb);border-radius:8px;font-size:12px;color:var(--red)';
        note.innerHTML = `<strong>Plan aplicado.</strong> ${p.rationale||''} ${p.tips ? '<br>💡 '+p.tips : ''}`;
        resp.parentElement.appendChild(note);
      }
    }
    showToast('✓ Configuración aplicada al formulario', 'var(--g)');
    setTimeout(() => goWizardStep(2), 800);
  })
  .catch(() => {
    if (btn) { btn.disabled=false; btn.textContent='✦ Aplicar configuración al formulario →'; }
    showToast('✗ Error de conexión', 'var(--red)');
  });
}

function buildWizardSummary() {
  const name    = document.getElementById('wc-name')?.value || '—';
  const obj     = document.getElementById('wc-objective')?.value || '—';
  const start   = document.getElementById('wc-start')?.value || 'Inmediato';
  const stop    = document.getElementById('wc-stop')?.value || 'Sin fin';
  const asName  = document.getElementById('was-name')?.value || '—';
  const budget  = document.getElementById('was-budget')?.value || '—';
  const btype   = document.getElementById('was-budget-type')?.value === 'daily' ? 'diario' : 'total';
  const optim   = document.getElementById('was-optimization')?.value || '—';
  const bid     = document.getElementById('was-bid')?.value || '—';
  const loc     = document.getElementById('was-location')?.value || 'Colombia';
  const ageMin  = document.getElementById('was-age-min')?.value || '18';
  const ageMax  = document.getElementById('was-age-max')?.value || '65';
  const inter   = document.getElementById('was-interests')?.value || 'No especificados';
  const adName  = document.getElementById('ad-name')?.value || '—';
  const headline= document.getElementById('ad-headline')?.value || '—';
  const primary = document.getElementById('ad-primary-text')?.value || '—';
  const url     = document.getElementById('ad-link-url')?.value || '—';
  const cta     = document.getElementById('ad-cta')?.value || '—';
  const adMode  = document.getElementById('ad-panel-image')?.style.display !== 'none' ? 'Imagen subida' : 'Post de página';
  const el = document.getElementById('wizard-summary');
  if (el) el.innerHTML = `
    <div style="display:grid;grid-template-columns:1fr 1fr 1fr;gap:12px">
      <div>
        <div style="color:var(--red);font-size:11px;text-transform:uppercase;letter-spacing:.7px;margin-bottom:6px">Campaña</div>
        <div><span style="color:var(--m)">Nombre:</span> ${name}</div>
        <div><span style="color:var(--m)">Objetivo:</span> ${obj}</div>
        <div><span style="color:var(--m)">Inicio:</span> ${start}</div>
        <div><span style="color:var(--m)">Fin:</span> ${stop}</div>
      </div>
      <div>
        <div style="color:var(--b);font-size:11px;text-transform:uppercase;letter-spacing:.7px;margin-bottom:6px">Ad Set</div>
        <div><span style="color:var(--m)">Nombre:</span> ${asName}</div>
        <div><span style="color:var(--m)">Presupuesto:</span> $${parseInt(budget||0).toLocaleString()} COP/${btype}</div>
        <div><span style="color:var(--m)">Optimización:</span> ${optim}</div>
        <div><span style="color:var(--m)">Puja:</span> ${bid}</div>
        <div><span style="color:var(--m)">Ubicación:</span> ${loc}</div>
        <div><span style="color:var(--m)">Edad:</span> ${ageMin}-${ageMax} años</div>
        <div><span style="color:var(--m)">Intereses:</span> ${inter}</div>
      </div>
      <div>
        <div style="color:var(--g);font-size:11px;text-transform:uppercase;letter-spacing:.7px;margin-bottom:6px">Anuncio</div>
        <div><span style="color:var(--m)">Nombre:</span> ${adName}</div>
        <div><span style="color:var(--m)">Creativo:</span> ${adMode}</div>
        <div><span style="color:var(--m)">Titular:</span> ${headline}</div>
        <div><span style="color:var(--m)">Texto:</span> ${primary.substring(0,40)}${primary.length>40?'...':''}</div>
        <div><span style="color:var(--m)">URL:</span> ${url.substring(0,30)}${url.length>30?'...':''}</div>
        <div><span style="color:var(--m)">CTA:</span> ${cta}</div>
      </div>
    </div>`;
}

// Estado del wizard para el creativo
let wizardAdImageHash = '';   // hash de imagen ya subida
let wizardAdSelectedPost = null; // {id, page_id} del post seleccionado

async function executeWizard() {
  const name   = document.getElementById('wc-name')?.value.trim();
  const budget = parseFloat(document.getElementById('was-budget')?.value);
  const msg    = document.getElementById('wizard-msg');
  const btn    = document.getElementById('wizard-create-btn');
  const pageId = document.getElementById('ad-page-id-select')?.value;

  if (!name) { msg.style.color='var(--red)'; msg.textContent='El nombre de la campaña es obligatorio'; return; }
  if (!budget||budget<=0) { msg.style.color='var(--red)'; msg.textContent='El presupuesto del Ad Set es obligatorio'; return; }
  if (!pageId) { msg.style.color='var(--red)'; msg.textContent='Selecciona la página de Facebook en el paso 3'; return; }

  const adMode = document.getElementById('ad-panel-image')?.style.display !== 'none' ? 'image'
               : document.getElementById('ad-panel-video')?.style.display !== 'none' ? 'video' : 'post';
  if (adMode === 'image' && !wizardAdImageHash) {
    msg.style.color='var(--red)'; msg.textContent='Sube una imagen en el paso 3 antes de crear'; return;
  }
  if (adMode === 'video' && !wizardAdVideoId) {
    msg.style.color='var(--red)'; msg.textContent='Sube un video en el paso 3 antes de crear'; return;
  }
  if (adMode === 'post' && !wizardAdSelectedPost) {
    msg.style.color='var(--red)'; msg.textContent='Selecciona un post de la página en el paso 3'; return;
  }

  btn.disabled=true; btn.textContent='Creando...';
  msg.style.color='var(--m)';

  const gender = document.getElementById('was-gender-all')?.classList.contains('active') ? [] :
    [...(document.getElementById('was-gender-m')?.classList.contains('active') ? [1] : []),
     ...(document.getElementById('was-gender-f')?.classList.contains('active') ? [2] : [])];

  try {
    // PASO 1 — Campaña + Ad Set
    msg.textContent = '1/2 · Creando campaña y Ad Set...';
    const campPayload = {
      client_id:         parseInt(currentClient),
      action:            'create_campaign_with_adset',
      name,
      objective:         document.getElementById('wc-objective')?.value,
      start_time:        document.getElementById('wc-start')?.value ? new Date(document.getElementById('wc-start').value).toISOString() : '',
      stop_time:         document.getElementById('wc-stop')?.value ? new Date(document.getElementById('wc-stop').value).toISOString() : '',
      adset_name:        document.getElementById('was-name')?.value || name + ' - Ad Set 1',
      daily_budget:      budget,
      budget_type:       document.getElementById('was-budget-type')?.value,
      optimization_goal: document.getElementById('was-optimization')?.value,
      bid_strategy:      document.getElementById('was-bid')?.value,
      age_min:           parseInt(document.getElementById('was-age-min')?.value||18),
      age_max:           parseInt(document.getElementById('was-age-max')?.value||65),
      genders:           gender,
      locations:         [{country: document.getElementById('was-location')?.value?.split(',')[0]?.trim()?.toUpperCase() || 'CO'}],
      interests:         document.getElementById('was-interests')?.value ? [{name: document.getElementById('was-interests').value}] : [],
    };
    const campRes = await fetch('api/meta-action.php', {method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify(campPayload)}).then(r=>r.json());
    if (!campRes.ok) throw new Error(campRes.error||'Error creando campaña/Ad Set');

    // PASO 2 — Anuncio (creative + ad)
    msg.textContent = '2/2 · Creando anuncio...';
    const adPayload = {
      client_id: parseInt(currentClient),
      action:    'create_ad',
      adset_id:  campRes.adset_id,
      page_id:   pageId,
      ad_name:   document.getElementById('ad-name')?.value || name + ' - Anuncio 1',
      mode:      adMode,
    };
    if (adMode === 'image') {
      adPayload.image_hash   = wizardAdImageHash;
      adPayload.headline     = document.getElementById('ad-headline')?.value || '';
      adPayload.primary_text = document.getElementById('ad-primary-text')?.value || '';
      adPayload.link_url     = document.getElementById('ad-link-url')?.value || '';
      adPayload.cta          = document.getElementById('ad-cta')?.value || 'LEARN_MORE';
    } else if (adMode === 'video') {
      adPayload.video_id     = wizardAdVideoId;
      adPayload.headline     = document.getElementById('ad-headline')?.value || '';
      adPayload.primary_text = document.getElementById('ad-primary-text')?.value || '';
      adPayload.link_url     = document.getElementById('ad-link-url')?.value || '';
      adPayload.cta          = document.getElementById('ad-cta')?.value || 'LEARN_MORE';
    } else {
      adPayload.post_id = wizardAdSelectedPost.id;
    }
    const adRes = await fetch('api/meta-action.php', {method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify(adPayload)}).then(r=>r.json());
    if (!adRes.ok) throw new Error(adRes.error||'Error creando anuncio');

    // Éxito total
    msg.style.color='var(--g)';
    msg.textContent='✓ Campaña, Ad Set y Anuncio creados en estado PAUSADO';
    showToast('✓ Todo creado en Meta. Revísalo antes de activar.', 'var(--g)');
    setTimeout(() => {
      goWizardStep(1);
      wizardAdImageHash = '';
      wizardAdVideoId = '';
      wizardAdSelectedPost = null;
      switchMetaTab('campanas', document.querySelector('.meta-tab:nth-child(2)'));
      loadMetaData(currentClient, currentPeriod);
    }, 2500);

  } catch(err) {
    btn.disabled=false; btn.textContent='Crear todo en Meta →';
    msg.style.color='var(--red)'; msg.textContent='✗ '+err.message;
  }
}

// ——— WIZARD: MANEJO DE CREATIVOS ———
let wizardAdVideoId = '';

function switchAdMode(mode) {
  ['image','video','post'].forEach(m => {
    const panel = document.getElementById('ad-panel-'+m);
    const btn   = document.getElementById('ad-mode-'+m);
    if (!panel||!btn) return;
    const active = m === mode;
    panel.style.display      = active ? 'block' : 'none';
    btn.style.background     = active ? 'var(--rl)' : 'var(--bg3)';
    btn.style.borderColor    = active ? 'var(--rb)' : 'var(--br2)';
    btn.style.color          = active ? 'var(--red)' : 'var(--m)';
  });
}

function handleAdFileSelect(input) {
  const file = input.files[0];
  if (!file) return;
  processAdFile(file);
}

function handleAdFileDrop(event) {
  event.preventDefault();
  document.getElementById('ad-drop-zone').style.borderColor = 'var(--br2)';
  const file = event.dataTransfer.files[0];
  if (!file) return;
  processAdFile(file);
}

function processAdFile(file) {
  if (!file.type.startsWith('image/')) { showToast('Solo se admiten imágenes (JPG, PNG)', 'var(--red)'); return; }
  if (file.size > 30 * 1024 * 1024) { showToast('La imagen no puede superar 30MB', 'var(--red)'); return; }

  const preview = document.getElementById('ad-upload-preview');
  const img     = document.getElementById('ad-preview-img');
  const status  = document.getElementById('ad-upload-status');

  const reader = new FileReader();
  reader.onload = e => {
    img.src = e.target.result;
    preview.style.display = 'block';
    status.textContent = 'Subiendo a Meta...';
    // Subir imagen al servidor → Meta
    const base64 = e.target.result;
    fetch('api/meta-action.php', {
      method: 'POST',
      headers: {'Content-Type':'application/json'},
      body: JSON.stringify({
        client_id:  parseInt(currentClient),
        action:     'upload_image',
        image_data: base64,
        filename:   file.name,
      })
    })
    .then(r=>r.json())
    .then(res => {
      if (res.ok) {
        wizardAdImageHash = res.hash;
        status.style.color = 'var(--g)';
        status.textContent = '✓ Imagen subida a Meta · Hash: '+res.hash.substring(0,12)+'...';
        document.getElementById('ad-drop-zone').style.display = 'none';
      } else {
        status.style.color = 'var(--red)';
        status.textContent = '✗ '+(res.error||'Error subiendo imagen');
        wizardAdImageHash = '';
      }
    })
    .catch(() => { status.style.color='var(--red)'; status.textContent='✗ Error de conexión'; });
  };
  reader.readAsDataURL(file);
}

function clearAdImage() {
  wizardAdImageHash = '';
  document.getElementById('ad-preview-img').src = '';
  document.getElementById('ad-upload-preview').style.display = 'none';
  document.getElementById('ad-drop-zone').style.display = 'block';
  document.getElementById('ad-upload-status').textContent = 'Imagen seleccionada';
  document.getElementById('ad-upload-status').style.color = 'var(--m)';
  document.getElementById('ad-file-input').value = '';
}

function handleAdVideoSelect(input) {
  const file = input.files[0];
  if (file) processAdVideo(file);
}

function handleAdVideoDrop(event) {
  event.preventDefault();
  document.getElementById('ad-video-drop').style.borderColor = 'var(--br2)';
  const file = event.dataTransfer.files[0];
  if (file) processAdVideo(file);
}

function processAdVideo(file) {
  const allowed = ['video/mp4','video/quicktime','video/x-msvideo','video/mpeg'];
  if (!allowed.includes(file.type)) { showToast('Formato no soportado. Usa MP4 o MOV.', 'var(--red)'); return; }
  if (file.size > 1024*1024*1024) { showToast('El video supera el límite de 1GB', 'var(--red)'); return; }

  const preview = document.getElementById('ad-video-preview');
  const vid     = document.getElementById('ad-preview-vid');
  const status  = document.getElementById('ad-video-status');
  const progress= document.getElementById('ad-video-progress');
  const bar     = document.getElementById('ad-video-bar');

  // Mostrar preview local inmediato
  vid.src = URL.createObjectURL(file);
  preview.style.display = 'block';
  document.getElementById('ad-video-drop').style.display = 'none';
  status.style.color = 'var(--m)';
  status.textContent = 'Subiendo a Meta... ('+Math.round(file.size/1024/1024)+'MB)';
  progress.style.display = 'block';
  bar.style.width = '0%';

  const formData = new FormData();
  formData.append('client_id', currentClient);
  formData.append('video', file, file.name);

  const xhr = new XMLHttpRequest();
  xhr.open('POST', 'api/meta-upload-video.php');
  // CSRF token
  const csrf = document.querySelector('meta[name="dm-csrf"]')?.content || '';
  if (csrf) xhr.setRequestHeader('X-CSRF-Token', csrf);

  xhr.upload.onprogress = e => {
    if (e.lengthComputable) bar.style.width = Math.round(e.loaded/e.total*100)+'%';
  };
  xhr.onload = () => {
    progress.style.display = 'none';
    try {
      const res = JSON.parse(xhr.responseText);
      if (res.ok) {
        wizardAdVideoId = res.video_id;
        status.style.color = 'var(--g)';
        status.textContent = '✓ Video subido a Meta · ID: '+res.video_id;
      } else {
        status.style.color = 'var(--red)';
        status.textContent = '✗ '+(res.error||'Error subiendo video');
        wizardAdVideoId = '';
      }
    } catch(e) { status.style.color='var(--red)'; status.textContent='✗ Error de respuesta'; }
  };
  xhr.onerror = () => { status.style.color='var(--red)'; status.textContent='✗ Error de conexión'; };
  xhr.send(formData);
}

function clearAdVideo() {
  wizardAdVideoId = '';
  const vid = document.getElementById('ad-preview-vid');
  if (vid.src) URL.revokeObjectURL(vid.src);
  vid.src = '';
  document.getElementById('ad-video-preview').style.display = 'none';
  document.getElementById('ad-video-drop').style.display = 'block';
  document.getElementById('ad-video-status').textContent = 'Video seleccionado';
  document.getElementById('ad-video-input').value = '';
}

// ——— WIZARD: PÁGINAS Y POSTS DE META ———
let metaPages = [];

function loadMetaPages() {
  fetch('api/meta-action.php', {
    method:'POST',
    headers:{'Content-Type':'application/json'},
    body:JSON.stringify({client_id:parseInt(currentClient), action:'get_pages'})
  })
  .then(r=>r.json())
  .then(res => {
    if (!res.ok) { showToast('✗ '+(res.error||'Error cargando páginas'), 'var(--red)'); return; }
    metaPages = res.pages || [];
    const sel1 = document.getElementById('ad-page-select');
    const sel2 = document.getElementById('ad-page-id-select');
    [sel1, sel2].forEach(sel => {
      if (!sel) return;
      sel.innerHTML = '<option value="">— Selecciona una página —</option>';
      // Separar FB e IG con optgroups
      const grpFB = document.createElement('optgroup');
      grpFB.label = 'Facebook';
      const grpIG = document.createElement('optgroup');
      grpIG.label = 'Instagram';
      metaPages.forEach(p => {
        const opt = document.createElement('option');
        opt.value = p.id;
        opt.dataset.type     = p.type;
        opt.dataset.fbPageId = p.fb_page_id || p.id;
        opt.textContent = (p.type==='instagram' ? '📸 ' : '📘 ') + p.name;
        if (p.type === 'instagram') grpIG.appendChild(opt);
        else grpFB.appendChild(opt);
      });
      if (grpFB.children.length) sel.appendChild(grpFB);
      if (grpIG.children.length) sel.appendChild(grpIG);
    });
    showToast('✓ '+metaPages.length+' cuentas cargadas', 'var(--g)');
    // Auto-seleccionar si solo hay una
    if (metaPages.length === 1) {
      sel1.value = sel2.value = metaPages[0].id;
      loadPagePosts(metaPages[0].id, metaPages[0].type);
    }
  })
  .catch(() => showToast('✗ Error cargando páginas', 'var(--red)'));
}

function loadPagePosts(pageId, pageType) {
  if (!pageId) return;
  // Detectar tipo desde el option si no se pasa
  if (!pageType) {
    const opt = document.querySelector(`#ad-page-select option[value="${pageId}"]`);
    pageType = opt?.dataset.type || 'facebook';
  }
  // Sincronizar el otro selector
  const sel2 = document.getElementById('ad-page-id-select');
  if (sel2 && !sel2.value) sel2.value = pageId;

  const grid = document.getElementById('ad-posts-grid');
  grid.innerHTML = '<div style="font-size:12px;color:var(--m);grid-column:1/-1;padding:8px">Cargando '+(pageType==='instagram'?'publicaciones de Instagram':'posts de Facebook')+'...</div>';

  fetch('api/meta-action.php', {
    method:'POST',
    headers:{'Content-Type':'application/json'},
    body:JSON.stringify({client_id:parseInt(currentClient), action:'get_page_posts', page_id:pageId, page_type:pageType})
  })
  .then(r=>r.json())
  .then(res => {
    grid.innerHTML = '';
    if (!res.ok || !res.posts?.length) {
      grid.innerHTML = '<div style="font-size:12px;color:var(--m);grid-column:1/-1;padding:8px">'+(res.error||'No se encontraron publicaciones')+'</div>';
      return;
    }
    res.posts.forEach(post => {
      const card = document.createElement('div');
      card.style.cssText = 'border:2px solid var(--br2);border-radius:8px;overflow:hidden;cursor:pointer;transition:.15s';
      card.onclick = () => selectAdPost(post, pageId, pageType, card);
      if (post.full_picture) {
        const isVideo = post.media_type === 'VIDEO';
        card.innerHTML = `<div style="position:relative"><img src="${post.full_picture}" style="width:100%;height:70px;object-fit:cover">${isVideo?'<div style="position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);font-size:20px">▶</div>':''}</div>`;
      } else {
        card.innerHTML = `<div style="height:70px;display:flex;align-items:center;justify-content:center;font-size:10px;color:var(--m);padding:4px;text-align:center">${(post.message||'Publicación sin imagen').substring(0,50)}</div>`;
      }
      const label = document.createElement('div');
      label.style.cssText = 'font-size:10px;color:var(--m);padding:4px 5px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis';
      label.textContent = (post.message||'Sin texto').substring(0,40);
      card.appendChild(label);
      grid.appendChild(card);
    });
  })
  .catch(() => { grid.innerHTML = '<div style="font-size:12px;color:var(--red)">Error cargando publicaciones</div>'; });
}

function selectAdPost(post, pageId, pageType, card) {
  document.querySelectorAll('#ad-posts-grid > div').forEach(c => {
    c.style.borderColor = 'var(--br2)';
    c.style.background = '';
  });
  card.style.borderColor = 'var(--red)';
  card.style.background = 'var(--rl)';
  // Para Instagram, el page_id del creative debe ser el FB page_id vinculado
  const opt = document.querySelector(`#ad-page-select option[value="${pageId}"]`);
  const fbPageId = opt?.dataset.fbPageId || pageId;
  wizardAdSelectedPost = {id: post.id, page_id: fbPageId, source: pageType};
  const sel = document.getElementById('ad-selected-post');
  sel.style.display = 'block';
  sel.textContent = '✓ '+(pageType==='instagram'?'📸':'📘')+' '+(post.message||post.id).substring(0,80);
}

// ——— ADS LIBRARY ———
function searchAdsLibrary() {
  const q = document.getElementById('lib-search')?.value.trim();
  const country = document.getElementById('lib-country')?.value || 'CO';
  const results = document.getElementById('lib-results');
  if (!q) return;
  if (results) results.innerHTML = '<div style="font-size:12px;color:var(--m)">Buscando en Meta Ads Library...</div>';
  fetch('api/meta-data.php?client_id=' + currentClient + '&action=library&q=' + encodeURIComponent(q) + '&country=' + country)
    .then(r => r.json())
    .then(data => {
      if (!data.ok || !data.ads || data.ads.length === 0) {
        results.innerHTML = '<div style="font-size:12px;color:var(--m)">Sin resultados. Usa el boton Abrir para buscar directamente.</div>';
        return;
      }
      let html = '';
      data.ads.forEach(function(ad) {
        const body = ((ad.ad_creative_bodies || [''])[0] || '').substring(0, 120);
        const date = (ad.ad_delivery_start_time || '').split('T')[0] || '?';
        const platforms = (ad.publisher_platforms || []).join(', ') || '?';
        const link = ad.ad_snapshot_url ? '<a href="' + ad.ad_snapshot_url + '" target="_blank" style="color:var(--b)">Ver anuncio</a>' : '';
        html += '<div style="background:var(--bg3);border:1px solid var(--br);border-radius:8px;padding:10px 12px;margin-bottom:8px;font-size:12px">';
        html += '<div style="font-weight:600;color:var(--t);margin-bottom:4px">' + (ad.page_name || 'Pagina desconocida') + '</div>';
        html += '<div style="color:var(--m);margin-bottom:4px">' + body + '...</div>';
        html += '<div style="display:flex;gap:8px;font-size:11px;color:var(--m)">';
        html += '<span>Desde: ' + date + '</span>';
        html += '<span>Plataformas: ' + platforms + '</span>';
        html += link;
        html += '</div></div>';
      });
      results.innerHTML = html;
      const adsText = data.ads.map(function(a) {
        return '- ' + a.page_name + ': ' + ((a.ad_creative_bodies || [''])[0] || '').substring(0, 80) + ' (activo desde ' + (a.ad_delivery_start_time || '?').split('T')[0] + ')';
      }).join('\n');
      const ctx = getContext('meta');
      callAVI('Analiza estos anuncios de competidores en Meta Ads Library para "' + q + '". Identifica patrones en el copy, formatos y mensajes. Sugiere diferenciacion:\n' + adsText + '\nContexto: ' + ctx, ctx, 'avi-resp-meta-comp', 'AVI analizando anuncios de competidores...', 'meta');
    })
    .catch(function() {
      if (results) results.innerHTML = '<div style="font-size:12px;color:var(--red)">Error consultando la biblioteca. Usa el boton Abrir.</div>';
    });
}
function showSinCreditosModal() {
  if (document.getElementById('modal-sin-creditos')) return;
  const modal = document.createElement('div');
  modal.id = 'modal-sin-creditos';
  modal.style.cssText = 'position:fixed;inset:0;background:rgba(0,0,0,.7);z-index:9999;display:flex;align-items:center;justify-content:center';
  modal.innerHTML = `
    <div style="background:var(--bg2);border:1px solid var(--red);border-radius:12px;padding:36px 40px;max-width:420px;text-align:center;box-shadow:0 20px 60px rgba(0,0,0,.5)">
      <div style="font-size:48px;margin-bottom:16px">⚠️</div>
      <h3 style="color:var(--red);margin:0 0 12px;font-size:18px">Te has quedado sin créditos</h3>
      <p style="color:var(--m);margin:0 0 24px;line-height:1.6">Contacta al equipo <strong style="color:var(--b)">SuperCerebro</strong> para recargar.</p>
      <button onclick="document.getElementById('modal-sin-creditos').remove()"
        style="padding:10px 28px;border-radius:8px;background:var(--red);border:none;color:#fff;font-size:14px;cursor:pointer;font-family:var(--fb)">
        Cerrar
      </button>
    </div>`;
  document.body.appendChild(modal);
}

// Interceptor global — CSRF + detección de sesión expirada
let _sessionExpired = false;
const _fetch = window.fetch.bind(window);
const _csrfToken = document.querySelector('meta[name="dm-csrf"]')?.content || '';
window.fetch = async function(url, opts = {}) {
  const method = (opts.method || 'GET').toUpperCase();
  // Inyectar CSRF en llamadas internas que no sean GET
  if (method !== 'GET' && typeof url === 'string' && !url.startsWith('http')) {
    opts = Object.assign({}, opts);
    opts.headers = Object.assign({}, opts.headers || {}, {'X-CSRF-Token': _csrfToken});
  }
  const response = await _fetch(url, opts);
  try {
    const ct = response.headers.get('content-type') || '';
    if (ct.includes('application/json')) {
      const clone = response.clone();
      const data  = await clone.json();
      if (data && data.error === 'No autenticado' && !_sessionExpired) {
        _sessionExpired = true;
        showToast('⚠ Sesión expirada — redirigiendo al login...', 'var(--y)');
        setTimeout(() => { window.location.href = 'login.php'; }, 2500);
      }
    }
  } catch(e) {}
  return response;
};

function checkAuth(data) {
  if (data && data.error === 'No autenticado') return true;
  return false;
}

function showToast(msg, color, duration) {
  let toast = document.getElementById('global-toast');
  if (!toast) {
    toast = document.createElement('div');
    toast.id = 'global-toast';
    toast.style.cssText = 'position:fixed;bottom:20px;right:20px;padding:10px 16px;border-radius:8px;font-size:13px;z-index:9999;transition:opacity .3s;font-family:var(--fb)';
    document.body.appendChild(toast);
  }
  const bg = color === 'var(--g)' ? 'rgba(34,201,122,.15)' : color === 'var(--b)' ? 'rgba(74,158,248,.15)' : 'rgba(232,39,42,.15)';
  toast.style.background = bg;
  toast.style.border = `1px solid ${color}`;
  toast.style.color = color;
  toast.textContent = msg;
  toast.style.opacity = '1';
  if (toast._timer) clearTimeout(toast._timer);
  toast._timer = setTimeout(() => { toast.style.opacity = '0'; }, duration || 3000);
}

// Carga los contextos del cliente desde la BD y rellena los textareas
function loadClientContexts(clientId){
  if(!clientId||clientId==='all') return;
  fetch('api/context.php',{
    method:'POST',
    headers:{'Content-Type':'application/json'},
    body:JSON.stringify({action:'load',client_id:clientId,section:'meta'})
  })
  .then(r=>r.json())
  .then(d=>{
    if(clientId != currentClient) return;
    if(!d.ok) return;
    ['meta','google','seo','web','videos','imagenes'].forEach(s=>{
      const ta=document.getElementById(s+'-ctx');
      if(ta && d[s]) ta.value=d[s];
      if(d[s]) contexts[s]=d[s];
    });
  })
  .catch(()=>{});
}

// ——— AVI CALLS ———
function getContext(section){
  // Primero memoria (ya cargada desde BD al seleccionar cliente)
  if(contexts[section]) return contexts[section];
  // Fallback al textarea por si acaso
  const ta=document.getElementById(section+'-ctx');
  return ta ? ta.value : '';
}

function renderMD(text){
  if(!text) return '';
  let h=text.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
  h=h.replace(/\*\*(.*?)\*\*/g,'<strong>$1</strong>');
  h=h.replace(/__(.*?)__/g,'<strong>$1</strong>');
  h=h.replace(/\*([^*\n]{1,80})\*/g,'<em>$1</em>');
  h=h.replace(/`([^`]+)`/g,'<code style="background:var(--bg2);padding:1px 5px;border-radius:3px;font-size:11px;font-family:monospace">$1</code>');
  h=h.replace(/^(━+[^\n]+━+)$/gm,'<div style="font-weight:700;color:var(--red);margin:12px 0 4px;font-size:12px;letter-spacing:.5px">$1</div>');
  h=h.replace(/^(#{1,3})\s+(.+)$/gm,'<div style="font-weight:700;margin:10px 0 3px;font-size:13px">$2</div>');
  h=h.replace(/^---+$/gm,'<hr style="border:none;border-top:1px solid var(--br2);margin:8px 0">');
  h=h.replace(/^(\d+)\.\s+(.+)$/gm,'<div style="margin:3px 0"><span style="color:var(--red);font-weight:700">$1.</span> $2</div>');
  h=h.replace(/^[-•]\s+(.+)$/gm,'<div style="margin:2px 0 2px 10px">· $1</div>');
  // Tablas markdown: bloques de líneas con |
  h=h.replace(/((?:\|[^\n]+\|\n?){2,})/g, block => {
    const lines = block.trim().split('\n').filter(l => l.trim());
    if (lines.length < 2) return block;
    const isSep = l => /^\|[\s\-|:]+\|$/.test(l.trim());
    let tbl = '<div style="overflow-x:auto;margin:10px 0"><table style="width:100%;border-collapse:collapse;font-size:11px">';
    let firstData = true;
    lines.forEach(line => {
      if (isSep(line)) return;
      const cells = line.split('|').slice(1,-1);
      const isHdr = firstData;
      if (firstData) firstData = false;
      const tag = isHdr ? 'th' : 'td';
      const rs = isHdr
        ? 'background:var(--bg3);font-weight:700;color:var(--t)'
        : 'background:var(--bg);color:var(--t)';
      tbl += '<tr>' + cells.map(c =>
        `<${tag} style="border:1px solid var(--br2);padding:5px 9px;text-align:left;white-space:nowrap;${rs}">${c.trim()}</${tag}>`
      ).join('') + '</tr>';
    });
    tbl += '</table></div>';
    return tbl;
  });
  h=h.replace(/\n/g,'<br>');
  return h;
}

function _aviFinish(respId) {
  // Re-enable shortcuts + send button after any AVI call
  document.querySelectorAll('#avi-drw-shortcuts .db').forEach(b => { b.disabled = false; b.style.opacity = ''; });
  const sb = document.querySelector('#avi-drw-foot .avi-send');
  if (sb) { sb.disabled = false; sb.textContent = 'Ejecutar →'; }
  // Scroll respuesta a la vista si es el drawer
  if (respId === 'avi-drw-resp') {
    const el = document.getElementById(respId);
    if (el) setTimeout(() => el.scrollIntoView({ behavior: 'smooth', block: 'nearest' }), 80);
  }
}

function callAVI(question, context, respId, loadingText='AVI analizando...', section='', onDone=null){
  const resp=document.getElementById(respId);
  if(!resp) return;
  resp.style.display='block';
  resp.textContent=loadingText;
  const c=currentClient!=='all'?CLIENTS.find(x=>x.id==currentClient):null;
  const savedCtx=c?(contexts[section]||getContext(section)||context):context;
  const payload={
    question, section,
    client_name:     c?c.name:'',
    client_industry: c?c.industry:'',
    client_url:      c?c.url:'',
    client_ctx:      savedCtx,
    brand_ctx:       getBrandContext(),
  };
  fetch('api/avi.php',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify(payload)})
    .then(r=>r.json()).then(d=>{
      const text = d.response || 'Sin respuesta';
      resp.innerHTML = renderMD(text);
      if (text.includes('Sin créditos') || text.includes('credit')) showToast('⚠️ Sin créditos Claude API', 'var(--red)', 5000);
      else if (text.includes('API Key de Claude inválida') || text.includes('🔑')) showToast('🔑 Claude API Key inválida — ve a ⚙ APIs', 'var(--red)', 6000);
      else if (text.includes('saturada') || text.includes('🔄')) showToast('🔄 Claude API saturada — reintenta en 30s', 'var(--y)', 5000);
      else if (text.includes('Límite de llamadas') || text.includes('⏳')) showToast('⏳ Rate limit Claude API — espera y reintenta', 'var(--y)', 5000);
      _aviFinish(respId);
      if(onDone) onDone(text);
    })
    .catch(()=>{
      resp.textContent='Configura la Claude API Key en ⚙ APIs para activar AVI con IA real.';
      _aviFinish(respId);
    });
}

function askAVI(panel){
  const map={
    'main'     :{q:'avi-q',       ctx:'',resp:'avi-resp-main',    section:''},
    'meta-comp':{q:'meta-comp-q', ctx:'', resp:'avi-resp-meta-comp',section:'meta'},
    'g-comp'   :{q:'g-comp-q',    ctx:'', resp:'avi-resp-g-comp',  section:'google'},
    'web'      :{q:'web-q',       ctx:'', resp:'avi-resp-web',     section:'web'},
    'vid-tool' :{q:'vid-tool-q',  ctx:'', resp:'avi-resp-vid-tool', section:'videos', post: text=>{
      const btns=document.getElementById('vid-herr-tool-btns');
      if(btns){btns.style.display='block'; highlightRecommendedTool(text,'vid-herr-tool-btns');}
    }},
    'vid-strat':{q:'vid-strat-q', ctx:'', resp:'avi-resp-vid-strat',section:'videos'},
    'img-tool' :{q:'img-tool-q',  ctx:'', resp:'avi-resp-img-htool',section:'imagenes'},
    'img-strat':{q:'img-strat-q', ctx:'', resp:'avi-resp-img-strat',section:'imagenes'},
  };
  const cfg=map[panel];
  if(!cfg) return;
  const q=document.getElementById(cfg.q)?.value.trim();
  if(!q) return;
  callAVI(q, cfg.ctx, cfg.resp, 'AVI analizando...', cfg.section, cfg.post||null);
  document.getElementById(cfg.q).value='';
}

// Analiza una sección de Google Ads con AVI (botones "✦ Analizar con AVI")
function analyzeSection(sectionKey, respId) {
  const c = CLIENTS.find(x => x.id == currentClient);
  if (!c) { showToast('Selecciona un cliente primero', 'var(--y)'); return; }
  const clientCtx = `Cliente: ${c.name} | Industria: ${c.industry || ''} | Web: ${c.url || ''}`;

  const configs = {
    'g-campaigns': {
      q: 'Analiza el rendimiento de estas campañas de Google Ads. Dime las 3 acciones más urgentes: cuáles escalar, cuáles pausar y por qué.',
      ctx: () => !gCampaignsList.length ? '' :
        'CAMPAÑAS:\n' + gCampaignsList.map(c =>
          `- ${c.name}: $${c.spend||0} gastado, ${c.clicks||0} clics, CTR ${c.ctr||0}%, conv ${c.conversions||0}, CPA $${c.cpa||0}, IS ${c.imp_share||0}%, estado: ${c.status}`
        ).join('\n'),
    },
    'g-keywords': {
      q: 'Analiza estas keywords de Google Ads. ¿Cuáles tienen mejor ROI, cuáles pausar por gasto sin conversión y qué nuevas keywords agregar?',
      ctx: () => !gKwData.length ? '' :
        'KEYWORDS:\n' + gKwData.slice(0, 25).map(k =>
          `- "${k.keyword}": ${k.clicks||0} clics, CTR ${k.ctr||0}%, CPC $${k.cpc||0}, conv ${k.conversions||0}, estado: ${k.status}`
        ).join('\n'),
    },
    'g-terms': {
      q: 'Analiza estos términos de búsqueda reales. ¿Cuáles son irrelevantes y deben agregarse como negativos? ¿Cuáles son oportunidades de nuevas keywords?',
      ctx: () => '',
    },
    'g-segments': {
      q: 'Basándote en el rendimiento de estas campañas, ¿en qué segmentos de audiencia, dispositivos u horarios deberíamos enfocar el presupuesto de Google Ads?',
      ctx: () => !gCampaignsList.length ? '' :
        'CAMPAÑAS:\n' + gCampaignsList.map(c =>
          `- ${c.name}: $${c.spend||0} invertidos, ${c.conversions||0} conv, CPA $${c.cpa||0}`
        ).join('\n'),
    },
  };

  const cfg = configs[sectionKey];
  if (!cfg) return;
  const dataCtx = cfg.ctx();
  const fullCtx = [clientCtx, dataCtx].filter(Boolean).join('\n\n');
  callAVI(cfg.q, fullCtx, respId, 'AVI analizando datos de Google Ads...', 'google');
}

// ——— VIDEOS MODULE ———
let lastVidScript = '';
let lastTomasScript = '';

let lastRefScript = '';
let tomasFileList = [];
let refFileList   = [];

function switchVidModo(modo){
  const modos = ['cero','tomas','ref'];
  const btns  = {cero:'vid-modo-cero', tomas:'vid-modo-tomas', ref:'vid-modo-ref'};
  const forms = {cero:'vid-form-cero', tomas:'vid-form-tomas', ref:'vid-form-ref'};
  modos.forEach(m => {
    const active = m === modo;
    const btn = document.getElementById(btns[m]);
    const frm = document.getElementById(forms[m]);
    if(btn){ btn.style.background=active?'var(--red)':'var(--bg3)'; btn.style.borderColor=active?'var(--red)':'var(--br2)'; btn.style.color=active?'#fff':'var(--m)'; }
    if(frm) frm.style.display = active ? 'block' : 'none';
  });
}

function onTomasFilesChange(input){
  tomasFileList = Array.from(input.files);
  const names = tomasFileList.map(f=>f.name).join(', ');
  document.getElementById('vid-tomas-filenames').textContent = tomasFileList.length + ' archivo(s): ' + names;
}

function onRefFilesChange(input){
  refFileList = Array.from(input.files);
  const names = refFileList.map(f=>f.name).join(', ');
  document.getElementById('vid-ref-filenames').textContent = refFileList.length + ' archivo(s): ' + names;
}

function onTomasVideoChange(input){
  const file = input.files[0];
  if(!file) return;
  document.getElementById('vid-tomas-videoname').textContent = file.name;
  const statusEl = document.getElementById('vid-tomas-upload-status');
  statusEl.textContent = '✓ Listo — click en "Analizar"';
  statusEl.style.color = 'var(--g)';
}

async function submitVideoForAnalysis(videoInputId, extraDesc, plataforma, tipo, respElId, toolBtnsId){
  const videoInput = document.getElementById(videoInputId);
  const file = videoInput?.files[0];
  const ctx  = getContext('videos');
  const c    = currentClient!=='all' ? CLIENTS.find(x=>x.id==currentClient) : null;
  const respEl = document.getElementById(respElId);
  respEl.style.display='block';

  if(file){
    // Video subido — ffmpeg extrae frames automaticamente
    respEl.textContent = '⏳ Subiendo video y extrayendo frames... (puede tomar 20-30 segundos)';
    const fd = new FormData();
    fd.append('mode','analyze_video');
    fd.append('video_file', file);
    fd.append('tipo', tipo);
    fd.append('extra', extraDesc);
    fd.append('plataforma', plataforma);
    fd.append('client_ctx', ctx);
    fd.append('client_name', c?c.name:'');
    fd.append('client_industry', c?c.industry:'');
    fd.append('client_url', c?c.url:'');
    try {
      const res  = await fetch('api/videos-analyze.php',{method:'POST',body:fd});
      const data = await res.json();
      if(data.ok){
        if(tipo==='tomas'){ lastTomasScript=data.response; const cw=document.getElementById('vid-tomas-script-copy-wrap'); if(cw) cw.style.display='flex'; }
        else { lastRefScript=data.response; const cw=document.getElementById('vid-ref-script-copy-wrap'); if(cw) cw.style.display='flex'; }
        respEl.innerHTML = renderMD(data.response);
        document.getElementById(toolBtnsId).style.display='block';
        highlightRecommendedTool(data.response, toolBtnsId);
      } else { respEl.textContent='✗ '+(data.error||'Error'); }
    } catch(e){ respEl.textContent='✗ Error de conexion'; }
    return;
  }
  return false; // no habia video
}

async function generateTomasScript(){
  const plat    = document.getElementById('vid-tomas-plataforma').value;
  const objetivo= document.getElementById('vid-tomas-objetivo').value;
  const desc    = document.getElementById('vid-tomas-desc').value.trim();
  const estilos = [...document.querySelectorAll('#vid-form-tomas .stag.active')].map(t=>t.textContent).join(', ');
  const ctx     = getContext('videos');
  const respEl  = document.getElementById('avi-resp-tomas');
  const c       = currentClient!=='all' ? CLIENTS.find(x=>x.id==currentClient) : null;
  respEl.style.display='block'; respEl.textContent='AVI analizando...';

  // Prioridad 1: video subido → ffmpeg
  const videoFile = document.getElementById('vid-tomas-video')?.files[0];
  if(videoFile){
    const handled = await submitVideoForAnalysis('vid-tomas-video', desc + (estilos?' Estilo: '+estilos:''), plat, 'tomas', 'avi-resp-tomas','vid-tomas-tool-btns');
    return;
  }

  // Prioridad 2: imagenes/frames subidos → vision
  if(tomasFileList.length > 0){
    const fd = new FormData();
    fd.append('mode','tomas_vision');
    fd.append('desc_tomas', desc);
    fd.append('objetivo', objetivo + (estilos?' Estilo: '+estilos:''));
    fd.append('plataforma', plat);
    fd.append('client_ctx', ctx);
    fd.append('client_name', c?c.name:'');
    fd.append('client_industry', c?c.industry:'');
    fd.append('client_url', c?c.url:'');
    tomasFileList.forEach(f => fd.append('tomas_files[]', f));
    const res = await fetch('api/videos-analyze.php',{method:'POST',body:fd});
    const data = await res.json();
    if(data.ok){ lastTomasScript=data.response; respEl.innerHTML=renderMD(data.response); const cw=document.getElementById('vid-tomas-script-copy-wrap'); if(cw) cw.style.display='flex'; document.getElementById('vid-tomas-tool-btns').style.display='block'; highlightRecommendedTool(data.response,'vid-tomas-tool-btns'); }
    else { respEl.textContent='✗ '+(data.error||'Error'); }
    return;
  }

  // Prioridad 3: solo descripcion → versión texto pura
  if(!desc){ respEl.textContent='Sube un video, imágenes, o describe tus tomas.'; return; }
  generateTomasScriptText();
}

async function analyzeReference(tipo){
  const link    = document.getElementById('vid-ref-link').value.trim();
  const desc    = document.getElementById('vid-ref-desc').value.trim();
  const plat    = document.getElementById('vid-ref-plataforma').value;
  const dur     = document.getElementById('vid-ref-duracion').value;
  const ctx     = getContext('videos');
  const respEl  = document.getElementById('avi-resp-ref');
  const c       = currentClient!=='all' ? CLIENTS.find(x=>x.id==currentClient) : null;
  respEl.style.display='block'; respEl.textContent='AVI analizando el video de referencia...';

  // Prioridad 1: video subido → ffmpeg extrae frames
  const videoFile = document.getElementById('vid-ref-video')?.files[0];
  if(videoFile){
    await submitVideoForAnalysis('vid-ref-video', (link?'Link original: '+link+'\n':'')+desc, plat, 'referencia','avi-resp-ref','vid-ref-tool-btns');
    return;
  }

  // Prioridad 2: capturas subidas → vision
  if(refFileList.length > 0){
    const fd = new FormData();
    fd.append('mode','tomas_vision');
    fd.append('desc_tomas', (link?'Video de referencia: '+link+'\n':'')+desc);
    fd.append('objetivo',`Deconstruir y adaptar la formula viral para ${plat} en ${dur}s`);
    fd.append('plataforma', plat);
    fd.append('client_ctx', ctx);
    fd.append('client_name', c?c.name:'');
    fd.append('client_industry', c?c.industry:'');
    fd.append('client_url', c?c.url:'');
    refFileList.forEach(f => fd.append('tomas_files[]', f));
    const res  = await fetch('api/videos-analyze.php',{method:'POST',body:fd});
    const data = await res.json();
    if(data.ok){ lastRefScript=data.response; respEl.innerHTML=renderMD(data.response); const cw=document.getElementById('vid-ref-script-copy-wrap'); if(cw) cw.style.display='flex'; document.getElementById('vid-ref-tool-btns').style.display='block'; highlightRecommendedTool(data.response,'vid-ref-tool-btns'); }
    else { respEl.textContent='✗ '+(data.error||'Error'); }
    return;
  }

  // Prioridad 3: solo descripcion + link
  if(!link && !desc){ respEl.textContent='Sube el video, capturas, o describe que pasa en el video.'; return; }
  const tipoExtra = tipo==='solo_hooks'?'\n\nFOCO: Solo genera 5 versiones del gancho adaptadas.':tipo==='formula'?'\n\nFOCO: Solo identifica la formula replicable.':'';
  const fd = new FormData();
  fd.append('mode','link');
  fd.append('link', link);
  fd.append('link_desc', desc+` Plataforma destino: ${plat}. Duracion: ${dur}s.`+tipoExtra);
  fd.append('client_ctx', ctx);
  fd.append('client_name', c?c.name:'');
  fd.append('client_industry', c?c.industry:'');
  fd.append('client_url', c?c.url:'');
  fd.append('plataforma', plat);
  const res  = await fetch('api/videos-analyze.php',{method:'POST',body:fd});
  const data = await res.json();
  if(data.ok){ lastRefScript=data.response; respEl.innerHTML=renderMD(data.response); const cw=document.getElementById('vid-ref-script-copy-wrap'); if(cw) cw.style.display='flex'; document.getElementById('vid-ref-tool-btns').style.display='block'; highlightRecommendedTool(data.response,'vid-ref-tool-btns'); }
  else { respEl.textContent='✗ '+(data.error||'Error'); }
}

function generateRefToolPrompt(herramienta){
  if(!lastRefScript){ document.getElementById('avi-resp-ref-tool').style.display='block'; document.getElementById('avi-resp-ref-tool').textContent='Genera el script adaptado primero.'; return; }
  const ctx=getContext('videos');
  const guias={runway:'Runway Gen-4: prompt en ingles con movimiento de camara, sujeto, accion, estilo y iluminacion. Max 200 palabras.',kling:'Kling AI v2: prompt en ingles, 100-150 palabras por escena, sujeto+accion+ambiente+estilo.',heygen:'HeyGen: descripcion del avatar, script exacto que dira, fondo y voz.',pika:'Pika 2.0: prompt corto 50-80 palabras en ingles, sujeto+accion+estilo cinematico.',hailuo:'Hailuo AI (MiniMax): prompt en ingles fotorrealista, 80-120 palabras. Especifica: sujeto real, iluminacion cinematica, camara (dolly, crane, handheld), ambiente y emocion. Optimizado para redes sociales.',veo3:'Veo 3 (Google): prompt en ingles con descripcion narrativa completa. Unico: incluye descripcion de SONIDO (musica de fondo, efectos ambientales, voz). Escena + audio + mood en un parrafo.',seedance:'Seedance (ByteDance): prompt en ingles enfocado en consistencia de personajes. Especifica: descripcion fisica exacta del personaje, accion, escena, estilo TikTok-nativo, camara movil y ritmo rapido.'};
  const q=`Basandote en este script adaptado:\n\n${lastRefScript}\n\nCrea el prompt perfecto en ingles para ${herramienta.toUpperCase()}. ${guias[herramienta]||''} Listo para pegar directamente.`;
  const respEl=document.getElementById('avi-resp-ref-tool');
  respEl.style.display='block'; respEl.textContent=`Optimizando para ${herramienta}...`;
  const c=currentClient!=='all'?CLIENTS.find(x=>x.id==currentClient):null;
  fetch('api/avi.php',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({question:q,section:'videos',client_name:c?c.name:'',client_industry:c?c.industry:'',client_url:c?c.url:'',client_ctx:ctx, brand_ctx:getBrandContext()})})
    .then(r=>r.json()).then(d=>{ respEl.innerHTML=renderMD(d.response||''); })
    .catch(()=>{ respEl.textContent='Error.'; });
}

function copyRefPrompt(){
  const txt=document.getElementById('avi-resp-ref-tool').innerText||document.getElementById('avi-resp-ref').innerText;
  navigator.clipboard.writeText(txt).then(()=>{
    const el=document.getElementById('vid-ref-copy-confirm'); el.style.display='inline';
    setTimeout(()=>el.style.display='none',2000);
  });
}

function switchVidTab(id, el){
  document.querySelectorAll('.vid-panel').forEach(p=>{p.style.display='none';p.classList.remove('active')});
  document.querySelectorAll('.vid-tab').forEach(t=>{t.style.color='var(--m)';t.style.borderBottomColor='transparent'});
  const panel=document.getElementById('vpanel-'+id);
  if(panel){panel.style.display='block';panel.classList.add('active');}
  if(el){el.style.color='var(--t)';el.style.borderBottomColor='var(--red)';}
}

function highlightRecommendedTool(responseText, toolBtnsId) {
  const match = responseText.match(/HERRAMIENTA:\s*(runway|kling|heygen|pika|sora|luma|capcut|hailuo|veo3|veo|seedance|wan|midjourney|dalle|flux|ideogram|leonardo|firefly)/i);
  if (!match) return;
  const tool = match[1].toLowerCase();
  const container = document.getElementById(toolBtnsId);
  if (!container) return;
  container.querySelectorAll('button[onclick]').forEach(btn => {
    const fn = (btn.getAttribute('onclick') || '').toLowerCase();
    const hit = fn.includes("'"+tool+"'") || fn.includes('"'+tool+'"');
    btn.style.background = hit ? 'var(--red)' : 'var(--bg3)';
    btn.style.color      = hit ? '#fff' : 'var(--t)';
    btn.style.border     = hit ? '1px solid var(--red)' : '1px solid var(--br2)';
  });
}

function generateVidScript(){
  const plat=document.getElementById('vid-plataforma').value;
  const dur=document.getElementById('vid-duracion').value;
  const idea=document.getElementById('vid-idea').value.trim();
  const tipos=[...document.querySelectorAll('#vid-form-cero .stag.active')].map(t=>t.textContent).join(', ');
  if(!idea){document.getElementById('avi-resp-videos').style.display='block';document.getElementById('avi-resp-videos').textContent='Escribe la idea del video primero.';return;}
  const ctx=getContext('videos');
  const d=parseInt(dur), mid=Math.round(d*0.35), devEnd=d-8;
  const q=`Crea un script COMPLETO y listo para producir para ${plat}, duración ${dur}s.
Tipo: ${tipos||'general'}. Idea: "${idea}".
${ctx?'CLIENTE: '+ctx:''}

━━━ SCRIPT COMPLETO ━━━

**[0-2s] PATTERN INTERRUPT**
Visual: [acción exacta — nunca "Hola soy"]
Texto en pantalla: [TEXTO MAYÚSCULAS, máx 6 palabras]

**[2-5s] GANCHO VERBAL**
Narración: [máx 10 palabras que generen curiosidad o planteen un problema]

**[5-${mid}s] IDENTIFICACIÓN**
Visual: [qué se ve en pantalla]
Narración: [dolor o deseo específico de la audiencia]
Texto en pantalla: [subtítulo]

**[${mid}-${devEnd}s] DESARROLLO**
[Una línea por toma: Xs→Xs | visual | narración | texto en pantalla]
[Micro-cliffhanger cada 5-7s para retención]

**[${devEnd}-${d-4}s] MOMENTO VIRAL**
[Dato más impactante o elemento que conecta con el inicio]

**[${d-4}-${d}s] CTA**
Narración: [acción directa en imperativo]
Texto en pantalla: [CTA visual]

━━━ PRODUCCIÓN ━━━
🎵 Audio: [tendencia de sonido o tipo de música específico para ${plat} 2026]
📐 Ratio: [ratio correcto para ${plat}]
📝 Caption: [texto completo para la publicación con 3-5 hashtags]

━━━ PROMPT RUNWAY GEN-4 ━━━
[Prompt en inglés 150 palabras: camera movement + subject + action + environment + lighting + style + mood. Listo para pegar directamente.]

━━━ PROMPT KLING AI v2 ━━━
[Prompt en inglés 120 palabras: subject + action + background + visual style + camera movement. Listo para pegar directamente.]`;
  const respEl = document.getElementById('avi-resp-videos');
  respEl.style.display='block';
  respEl.textContent='AVI generando script viral...';
  const c=currentClient!=='all'?CLIENTS.find(x=>x.id==currentClient):null;
  fetch('api/avi.php',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({
    question:q, section:'videos',
    client_name:c?c.name:'', client_industry:c?c.industry:'', client_url:c?c.url:'', client_ctx:ctx, brand_ctx:getBrandContext()
  })})
  .then(r=>r.json()).then(d=>{
    lastVidScript = d.response||'';
    respEl.innerHTML = renderMD(lastVidScript);
    const cw=document.getElementById('vid-script-copy-wrap'); if(cw){cw.style.display='flex';}
    document.getElementById('vid-tool-btns').style.display='block';
    highlightRecommendedTool(lastVidScript,'vid-tool-btns');
  })
  .catch(()=>{respEl.textContent='Configura la Claude API Key en ⚙ APIs para activar AVI.';});
}

function generateTomasScriptText(){
  const plat=document.getElementById('vid-tomas-plataforma').value;
  const objetivo=document.getElementById('vid-tomas-objetivo').value;
  const desc=document.getElementById('vid-tomas-desc').value.trim();
  const estilos=[...document.querySelectorAll('#vid-form-tomas .stag.active')].map(t=>t.textContent).join(', ');
  if(!desc){document.getElementById('avi-resp-tomas').style.display='block';document.getElementById('avi-resp-tomas').textContent='Describe tus tomas primero.';return;}
  const ctx=getContext('videos');
  const q=`Eres experto en edición de video viral 2026. El cliente tiene footage propio.
Objetivo: ${objetivo} para ${plat}. Estilo: ${estilos||'cinematico'}.
${ctx?'CLIENTE: '+ctx:''}

TOMAS DISPONIBLES:
${desc}

━━━ PLAN DE EDICIÓN ━━━

**[0-2s] APERTURA — GANCHO VISUAL**
[Toma exacta, encuadre específico, acción — sin presentación]

**ORDEN Y TIMING DE TOMAS**
[Toma X (Xs→Xs): descripción visual | transición hacia siguiente | texto en pantalla]
[Una línea por toma, orden óptimo para retención]

**TEXTO EN PANTALLA — TIMING EXACTO**
[Xs→Xs | "TEXTO EXACTO" | posición | estilo animación sugerida]

**🎵 AUDIO**
[Nombre de tendencia o tipo de música específico para ${plat} 2026]
[Puntos de corte en beat, fade in/out, volumen sugerido]

━━━ PROMPT CAPCUT IA ━━━
[Instrucciones específicas para Auto-Cut, efectos de texto animados, filtros y beat sync. En español, listo para usar directamente en CapCut.]

━━━ PROMPT RUNWAY GEN-4 ━━━
[Prompt en inglés 150 palabras para mejorar o transformar las tomas con img2video: camera movement + subject + action + style + lighting. Listo para pegar.]`;
  const respEl = document.getElementById('avi-resp-tomas');
  respEl.style.display='block';
  respEl.textContent='AVI analizando tomas y generando plan...';
  const c=currentClient!=='all'?CLIENTS.find(x=>x.id==currentClient):null;
  fetch('api/avi.php',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({
    question:q, section:'videos',
    client_name:c?c.name:'', client_industry:c?c.industry:'', client_url:c?c.url:'', client_ctx:ctx, brand_ctx:getBrandContext()
  })})
  .then(r=>r.json()).then(d=>{
    lastTomasScript = d.response||'';
    respEl.innerHTML = renderMD(lastTomasScript);
    const cw=document.getElementById('vid-tomas-script-copy-wrap'); if(cw){cw.style.display='flex';}
    document.getElementById('vid-tomas-tool-btns').style.display='block';
    highlightRecommendedTool(lastTomasScript,'vid-tomas-tool-btns');
  })
  .catch(()=>{respEl.textContent='Configura la Claude API Key en ⚙ APIs para activar AVI.';});
}

function generateToolPrompt(herramienta){
  if(!lastVidScript){document.getElementById('avi-resp-tool-prompt').style.display='block';document.getElementById('avi-resp-tool-prompt').textContent='Genera el script primero.';return;}
  const ctx=getContext('videos');
  const guias={
    runway:`Runway Gen-4 usa prompts en ingles con este formato: [camera movement], [subject description], [action], [style], [lighting], [mood]. Maximo 200 palabras. Especifica movimiento de camara (slow push in, aerial drone shot, etc).`,
    kling:`Kling AI v2 acepta prompts en ingles o español. Especifica: sujeto principal, accion, fondo/ambiente, estilo visual, duracion por clip. Funciona mejor con prompts de 100-150 palabras por escena.`,
    heygen:`HeyGen necesita: descripcion del avatar o persona, script de texto exacto que dira el presentador, fondo virtual o imagen de fondo, idioma y acento del voiceover.`,
    pika:`Pika 2.0 responde bien a prompts cortos de 50-80 palabras en ingles: [sujeto], [accion especifica], [ambiente], [estilo cinematico], [camara].`,
    sora:`Sora de OpenAI acepta prompts narrativos largos. Describe la escena como si fuera un guion cinematografico en ingles. Incluye iluminacion, clima, emocion y movimiento de camara.`,
    luma:`Luma Dream Machine es ideal para objetos y productos 3D. Prompt en ingles: [objeto/producto], [material y textura], [iluminacion de estudio], [movimiento de camara orbital], [fondo].`,
    hailuo:`Hailuo AI (MiniMax): prompt fotorrealista en ingles, 80-120 palabras. Sujeto real + iluminacion cinematica + tipo de camara (dolly in, crane shot, handheld) + ambiente + emocion. Top para redes sociales 2026.`,
    veo3:`Veo 3 (Google DeepMind): prompt narrativo en ingles con descripcion de imagen Y sonido. Ejemplo: "A chef cooking in a busy kitchen, sizzling sounds, upbeat background music, handheld camera". Incluir sonidos es su ventaja unica.`,
    wan:`Wan 2.1 (Alibaba, open source): ideal para imagen-a-video. Prompt en ingles corto y directo: describe la imagen de entrada + el movimiento que debe tener + duracion. Excelente para animar fotos de producto.`,
  };
  const q=`Basandote en este script:\n\n${lastVidScript}\n\nCrea el prompt perfecto y optimizado para ${herramienta.toUpperCase()} en ingles. ${guias[herramienta]} El prompt debe capturar la esencia visual del script y estar listo para pegar directamente en la herramienta.`;
  const respEl=document.getElementById('avi-resp-tool-prompt');
  respEl.style.display='block';
  respEl.textContent=`Optimizando prompt para ${herramienta}...`;
  const c=currentClient!=='all'?CLIENTS.find(x=>x.id==currentClient):null;
  fetch('api/avi.php',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({
    question:q, section:'videos', client_name:c?c.name:'', client_industry:c?c.industry:'', client_url:c?c.url:'', client_ctx:ctx, brand_ctx:getBrandContext()
  })})
  .then(r=>r.json()).then(d=>{
    respEl.innerHTML=renderMD(d.response||'');
    document.getElementById('vid-tool-copy-wrap').style.display='flex';
  })
  .catch(()=>{respEl.textContent='Error generando prompt.';});
}

function generateTomasToolPrompt(herramienta){
  if(!lastTomasScript){document.getElementById('avi-resp-tomas-tool').style.display='block';document.getElementById('avi-resp-tomas-tool').textContent='Genera el script de edicion primero.';return;}
  const ctx=getContext('videos');
  const guias={
    runway:`Runway Gen-4: prompt en ingles con movimiento de camara, descripcion visual de cada clip, transiciones y estilo. Maximo 200 palabras.`,
    kling:`Kling AI v2: describe cada toma con sujeto, accion, ambiente y estilo. Prompts de 100-150 palabras por escena en ingles.`,
    capcut:`CapCut IA: describe el ritmo de edicion, tipo de transiciones, efectos de texto y musica. Incluye instrucciones para el auto-cut y beat sync.`,
    descript:`Descript: describe como debe quedar el corte final editando por transcripcion. Indica que partes eliminar, donde agregar B-roll y efectos de sonido.`,
    hailuo:`Hailuo AI (MiniMax): un prompt por toma, en ingles, 80-120 palabras. Fotorrealismo extremo, especifica tipo de camara, iluminacion y emocion del sujeto. Ideal para tomas de personas y productos.`,
    veo3:`Veo 3 (Google): prompt en ingles que incluye descripcion visual + descripcion de audio (sonidos ambiente, musica, efectos). Su mayor ventaja es generar video con sonido nativo sincronizado.`,
    wan:`Wan 2.1: perfecto para animar fotos reales. Prompt en ingles corto: describe la foto de entrada + el movimiento deseado (camera zoom, subject walking, product rotating). Open source, gratuito.`,
  };
  const q=`Basandote en este plan de edicion:\n\n${lastTomasScript}\n\nCrea el prompt perfecto para ${herramienta.toUpperCase()} en ingles. ${guias[herramienta]} Listo para pegar directamente en la herramienta.`;
  const respEl=document.getElementById('avi-resp-tomas-tool');
  respEl.style.display='block';
  respEl.textContent=`Optimizando prompt para ${herramienta}...`;
  const c=currentClient!=='all'?CLIENTS.find(x=>x.id==currentClient):null;
  fetch('api/avi.php',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({
    question:q, section:'videos', client_name:c?c.name:'', client_industry:c?c.industry:'', client_url:c?c.url:'', client_ctx:ctx, brand_ctx:getBrandContext()
  })})
  .then(r=>r.json()).then(d=>{
    respEl.innerHTML=renderMD(d.response||'');
    document.getElementById('vid-tomas-copy-wrap').style.display='flex';
  })
  .catch(()=>{respEl.textContent='Error generando prompt.';});
}

function quickTomasPrompt(herramienta){
  switchVidModo('tomas');
  const desc=document.getElementById('vid-tomas-desc').value.trim();
  if(!desc){document.getElementById('avi-resp-tomas').style.display='block';document.getElementById('avi-resp-tomas').textContent='Describe tus tomas primero.';return;}
  generateTomasScriptText();
}

function copyVidScript(){
  const txt=document.getElementById('avi-resp-videos').innerText;
  navigator.clipboard.writeText(txt).then(()=>{ const el=document.getElementById('vid-script-copy-confirm'); el.style.display='inline'; setTimeout(()=>el.style.display='none',2000); });
}
function copyTomasScript(){
  const txt=document.getElementById('avi-resp-tomas').innerText;
  navigator.clipboard.writeText(txt).then(()=>{ const el=document.getElementById('vid-tomas-script-copy-confirm'); el.style.display='inline'; setTimeout(()=>el.style.display='none',2000); });
}
function copyRefScript(){
  const txt=document.getElementById('avi-resp-ref').innerText;
  navigator.clipboard.writeText(txt).then(()=>{ const el=document.getElementById('vid-ref-script-copy-confirm'); el.style.display='inline'; setTimeout(()=>el.style.display='none',2000); });
}
function copyHooksResult(){
  const txt=document.getElementById('avi-resp-hooks').innerText;
  navigator.clipboard.writeText(txt).then(()=>{ const el=document.getElementById('vid-hooks-copy-confirm'); el.style.display='inline'; setTimeout(()=>el.style.display='none',2000); });
}
function generateToolPromptFromHerr(herramienta){
  const ctx=getContext('videos');
  const rec=document.getElementById('avi-resp-vid-tool')?.innerText||'';
  const guias={
    runway:`Runway Gen-4: prompt en ingles con movimiento de camara, sujeto, accion, estilo y iluminacion. Max 200 palabras.`,
    kling:`Kling AI v2: prompt en ingles, 100-150 palabras por escena, sujeto+accion+ambiente+estilo.`,
    heygen:`HeyGen: descripcion del avatar, script exacto que dira el presentador, fondo y voz.`,
    pika:`Pika 2.0: prompt corto 50-80 palabras en ingles, sujeto+accion+estilo cinematico.`,
    sora:`Sora: prompt narrativo largo en ingles estilo guion cinematografico. Incluye iluminacion, clima, emocion y movimiento de camara.`,
    luma:`Luma Dream Machine: objeto/producto 3D, material, iluminacion estudio, movimiento orbital, fondo.`,
    hailuo:`Hailuo AI (MiniMax): fotorrealista, 80-120 palabras en ingles. Sujeto real, iluminacion cinematica, tipo de camara, emocion. Top redes sociales 2026.`,
    veo3:`Veo 3 (Google): prompt en ingles con descripcion visual + descripcion de audio (musica, efectos, ambiente). Incluir sonido es su ventaja unica.`,
    seedance:`Seedance (ByteDance): consistencia de personajes, TikTok-nativo. Descripcion fisica exacta del personaje, accion, estilo movil rapido.`,
    wan:`Wan 2.1: imagen-a-video. Prompt en ingles corto: imagen de entrada + movimiento deseado. Open source, gratuito.`,
  };
  const q=`Basandote en esta descripcion de necesidad de video:\n\n${rec}\n\nCrea el prompt perfecto y completo en ingles para ${herramienta.toUpperCase()}. ${guias[herramienta]||''} El prompt debe estar listo para pegar directamente en la herramienta.`;
  const respEl=document.getElementById('avi-resp-herr-tool');
  respEl.style.display='block'; respEl.textContent=`Generando prompt para ${herramienta}...`;
  const c=currentClient!=='all'?CLIENTS.find(x=>x.id==currentClient):null;
  fetch('api/avi.php',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({
    question:q, section:'videos', client_name:c?c.name:'', client_industry:c?c.industry:'', client_url:c?c.url:'', client_ctx:ctx, brand_ctx:getBrandContext()
  })})
  .then(r=>r.json()).then(d=>{
    respEl.innerHTML=renderMD(d.response||'');
    const cw=document.getElementById('vid-herr-copy-wrap'); if(cw) cw.style.display='flex';
  })
  .catch(()=>{respEl.textContent='Error.';});
}

function copyHerrPrompt(){
  const txt=document.getElementById('avi-resp-herr-tool').innerText;
  navigator.clipboard.writeText(txt).then(()=>{
    const el=document.getElementById('vid-herr-copy-confirm'); el.style.display='inline';
    setTimeout(()=>el.style.display='none',2000);
  });
}

function copyToolPrompt(){
  const txt=document.getElementById('avi-resp-tool-prompt').innerText;
  navigator.clipboard.writeText(txt).then(()=>{
    const el=document.getElementById('vid-copy-confirm');
    el.style.display='inline';
    setTimeout(()=>el.style.display='none',2000);
  });
}

function copyTomasPrompt(){
  const txt=document.getElementById('avi-resp-tomas-tool').innerText;
  navigator.clipboard.writeText(txt).then(()=>{
    const el=document.getElementById('vid-tomas-copy-confirm');
    el.style.display='inline';
    setTimeout(()=>el.style.display='none',2000);
  });
}

function quickVidScript(tipo){
  const ctx=getContext('videos');
  const plat=document.getElementById('vid-plataforma').value;
  const idea=document.getElementById('vid-idea').value.trim()||'el negocio del cliente';
  const prompts={
    gancho:`Genera 5 ganchos de alto impacto para los primeros 3 segundos de un video de ${plat} sobre: "${idea}". Cada gancho debe ser máx 10 palabras. ${ctx?'Marca: '+ctx:''}`,
    cta:`Genera 5 CTAs irresistibles para el cierre de un video de ${plat} sobre: "${idea}". Directos, con urgencia y acción clara. ${ctx?'Marca: '+ctx:''}`,
    completo_ads:`Crea un script de video publicitario de 30s para ${plat} sobre: "${idea}". Estructura: Gancho (0-3s) → Problema (3-8s) → Solución (8-20s) → Prueba social (20-25s) → Oferta+CTA (25-30s). Incluye texto en pantalla y narración separados. ${ctx?'Marca: '+ctx:''}`,
    variantes:`Crea 3 variantes de script de 30s para ${plat} sobre: "${idea}". Cada variante con un ángulo diferente: (1) Emocional/historia, (2) Educativo/datos, (3) Humor/entretenimiento. ${ctx?'Marca: '+ctx:''}`,
  };
  const rqv=document.getElementById('avi-resp-videos');
  rqv.style.display='none'; rqv.innerHTML='';
  const qscw=document.getElementById('vid-script-copy-wrap'); if(qscw) qscw.style.display='none';
  callAVI(prompts[tipo], ctx, 'avi-resp-videos', 'AVI generando...', 'videos');
  // mostrar copiar al terminar
  if(qscw && rqv){ const obs=new MutationObserver(()=>{ if(rqv.innerHTML && !rqv.textContent.includes('generando')){ qscw.style.display='flex'; obs.disconnect(); }}); obs.observe(rqv,{childList:true,characterData:true,subtree:true}); }
}

function generateHooks(subtipo){
  const nicho=document.getElementById('vid-hook-nicho').value.trim();
  const emocion=document.getElementById('vid-hook-emocion').value;
  const ctx=getContext('videos');
  if(!nicho&&!ctx){document.getElementById('avi-resp-hooks').style.display='block';document.getElementById('avi-resp-hooks').textContent='Escribe el nicho primero.';return;}
  const base=nicho||(ctx.substring(0,80));
  const subtipoTexts={
    tendencias:`ganchos estilo trending/viral que funcionan ahora mismo en TikTok e Instagram`,
    preguntas:`ganchos en formato pregunta que generen curiosidad inmediata`,
    datos:`ganchos que empiecen con datos, estadísticas o cifras impactantes`,
  };
  const extra=subtipo?subtipoTexts[subtipo]:'ganchos efectivos con emoción de '+emocion;
  const q=`Eres experto en hooks para videos de redes sociales. Genera 10 ${extra} para el nicho: "${base}".

Para cada hook incluye: el texto exacto del gancho (máx 12 palabras) y una breve explicación de por qué funciona. Numera del 1 al 10.`;
  const respHooks=document.getElementById('avi-resp-hooks');
  const cw=document.getElementById('vid-hooks-copy-wrap');
  if(cw) cw.style.display='none';
  const origFetch=callAVI;
  callAVI(q, ctx, 'avi-resp-hooks', 'AVI investigando hooks...', 'videos');
  // Mostrar botón copiar después de que callAVI termine (usa MutationObserver)
  if(cw && respHooks){
    const obs=new MutationObserver(()=>{ if(respHooks.innerHTML && !respHooks.textContent.includes('investigando')){ cw.style.display='flex'; obs.disconnect(); }});
    obs.observe(respHooks,{childList:true,characterData:true,subtree:true});
  }
}

function usarFormula(el){
  const formula=el.dataset.formula;
  document.getElementById('vid-hook-nicho').focus();
  switchVidTab('hooks', document.querySelector('.vid-tab[onclick*="hooks"]'));
  setTimeout(()=>{
    const nicho=document.getElementById('vid-hook-nicho').value.trim()||'tu negocio';
    document.getElementById('vid-hook-nicho').value=formula.replace('[tema]',nicho).replace('[X]','alto').replace('[resultado]','resultados increíbles').replace('[problema]','este problema').replace('[resultado negativo]','no obtienes resultados');
  },50);
}

// ——— IMÁGENES IA ———
let lastImgPrompt = '';
let lastImgPotPrompt = '';
let lastImgRefPrompt = '';

function switchImgTab(id, el) {
  document.querySelectorAll('.img-panel').forEach(p=>{p.style.display='none';p.classList.remove('active')});
  document.querySelectorAll('.img-tab').forEach(t=>{t.style.color='var(--m)';t.style.borderBottomColor='transparent'});
  const panel = document.getElementById('ipanel-'+id);
  if(panel){panel.style.display='block';panel.classList.add('active');}
  if(el){el.style.color='var(--t)';el.style.borderBottomColor='var(--red)';}
}

function switchImgModo(modo) {
  const modos = ['crear','potenciar','replicar'];
  modos.forEach(m => {
    document.getElementById('img-form-'+m).style.display = modo===m ? 'block' : 'none';
    const btn = document.getElementById('imgmodo-'+m);
    if(btn){
      btn.style.background = modo===m ? 'var(--red)' : 'var(--bg3)';
      btn.style.border     = modo===m ? 'none' : '1px solid var(--br2)';
      btn.style.color      = modo===m ? '#fff' : 'var(--t)';
    }
  });
}

function generateImgPrompt() {
  const uso     = document.getElementById('img-uso').value;
  const ratio   = document.getElementById('img-ratio').value;
  const desc    = document.getElementById('img-desc').value.trim();
  const estilos = [...document.querySelectorAll('#img-form-crear .stag.active')].map(t=>t.textContent).join(', ');
  const ctx     = getContext('imagenes');
  const c       = currentClient!=='all' ? CLIENTS.find(x=>x.id==currentClient) : null;
  const respEl  = document.getElementById('avi-resp-imagenes');
  if(!desc && !ctx){ respEl.style.display='block'; respEl.textContent='Describe la imagen primero.'; return; }
  respEl.style.display='block'; respEl.textContent='AVI generando prompt...';

  const brandCtx = getBrandContext();
  const q = `Eres experto en generación de imágenes con IA 2026. Crea los prompts para esta imagen:

Uso: ${uso}
Proporción: ${ratio}
Estilo: ${estilos || 'fotorrealista'}
Descripción: ${desc || '(usa el contexto del cliente)'}
${ctx ? 'Contexto de negocio: '+ctx : ''}
${brandCtx ? 'Identidad visual de marca: '+brandCtx : ''}

Genera:
1. 🤖 PROMPT PRINCIPAL — en inglés, listo para Midjourney o Flux. Estructura: sujeto, ambiente, iluminación, estilo, cámara, mood. Incluye --ar ${ratio.replace(':','/')} al final si es para Midjourney.
2. 🎨 VERSIÓN DALL-E 3 — prompt descriptivo en inglés adaptado para DALL-E 3
3. ⚡ NEGATIVE PROMPT — qué evitar (para Midjourney/Leonardo)
4. 💡 CONSEJO DE USO — ajuste específico para que funcione mejor en el uso destino (${uso})
5. 📐 COMPOSICIÓN — dónde posicionar el sujeto y dónde dejar espacio para texto/copy`;

  fetch('api/avi.php',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({
    question:q, section:'imagenes',
    client_name:c?c.name:'', client_industry:c?c.industry:'', client_url:c?c.url:'', client_ctx:ctx, brand_ctx:getBrandContext()
  })})
  .then(r=>r.json()).then(d=>{
    lastImgPrompt = d.response||'';
    respEl.textContent = lastImgPrompt;
    document.getElementById('img-tool-btns').style.display='block';
    highlightRecommendedTool(lastImgPrompt, 'img-tool-btns');
  })
  .catch(()=>{ respEl.textContent='Configura la Claude API Key en ⚙ APIs.'; });
}

function onImgRefChange(input) {
  const file = input.files[0];
  if(!file) return;
  document.getElementById('img-ref-filename').textContent = file.name;
  const reader = new FileReader();
  reader.onload = e => {
    document.getElementById('img-ref-thumb').src = e.target.result;
    document.getElementById('img-ref-preview').style.display = 'block';
  };
  reader.readAsDataURL(file);
}

async function analyzeImgReference() {
  const file    = document.getElementById('img-ref-file').files[0];
  const desc    = document.getElementById('img-ref-desc').value.trim();
  const uso     = document.getElementById('img-ref-uso').value;
  const ratio   = document.getElementById('img-ref-ratio').value;
  const ctx     = getContext('imagenes');
  const c       = currentClient!=='all' ? CLIENTS.find(x=>x.id==currentClient) : null;
  const respEl  = document.getElementById('avi-resp-img-ref');

  if(!file){ respEl.style.display='block'; respEl.textContent='Sube una imagen de referencia primero.'; return; }
  respEl.style.display='block'; respEl.textContent='AVI analizando imagen con Vision...';

  const fd = new FormData();
  fd.append('ref_image', file);
  fd.append('desc',    desc);
  fd.append('uso',     uso);
  fd.append('ratio',   ratio);
  fd.append('client_ctx',      ctx);
  fd.append('client_name',     c?c.name:'');
  fd.append('client_industry', c?c.industry:'');
  fd.append('client_url',      c?c.url:'');
  fd.append('client_id',       c?c.id:'');

  try {
    const res  = await fetch('api/images-analyze.php',{method:'POST',body:fd});
    const data = await res.json();
    if(data.ok){
      lastImgRefPrompt = data.response;
      respEl.textContent = lastImgRefPrompt;
      document.getElementById('img-ref-tool-btns').style.display='block';
      highlightRecommendedTool(lastImgRefPrompt, 'img-ref-tool-btns');
    } else { respEl.textContent='✗ '+(data.error||'Error'); }
  } catch(e){ respEl.textContent='✗ Error de conexión'; }
}

function generateImgToolPrompt(herramienta) {
  if(!lastImgPrompt){ document.getElementById('avi-resp-img-tool').style.display='block'; document.getElementById('avi-resp-img-tool').textContent='Genera el prompt primero.'; return; }
  const ctx = getContext('imagenes');
  const c   = currentClient!=='all' ? CLIENTS.find(x=>x.id==currentClient) : null;
  const guias = {
    midjourney: 'Midjourney v6.1: /imagine prompt [descripción detallada], [estilo], [iluminación], [cámara]. Incluye parámetros al final: --ar [ratio] --v 6.1 --q 2. Añade --style raw para fotorrealismo.',
    dalle: 'DALL-E 3: prompt en inglés muy descriptivo, sin parámetros técnicos. Describe exactamente lo que debe aparecer, colores, composición y estilo. Puede incluir instrucciones de texto en imagen.',
    flux: 'Flux.1 Dev: prompt en inglés detallado, sin parámetros. Funciona mejor con descripciones de 50-150 palabras. Muy fotorrealista. Describe lighting, texture, camera lens y mood.',
    ideogram: 'Ideogram 2.0: ideal si hay texto en la imagen. Especifica el texto entre comillas, fuente/estilo tipográfico, posición. Prompt en inglés con descripción visual clara.',
    leonardo: 'Leonardo AI Phoenix: prompt en inglés + negative prompt separado. Agrega style preset (Cinematic, Photography, etc.) en el panel. Especifica sujeto, acción, ambiente, iluminación.',
    firefly: 'Adobe Firefly 3: prompt en inglés o español. Usa Content type (Photo/Art/Graphic), selecciona Style en el panel. Excelente para imágenes sin restricciones de derechos para uso comercial.',
  };
  const q = `Basándote en este prompt base:\n\n${lastImgPrompt}\n\nCrea el prompt optimizado específicamente para ${herramienta.toUpperCase()}.\n${guias[herramienta]||''}\nListo para pegar directamente en la herramienta.`;
  const respEl = document.getElementById('avi-resp-img-tool');
  respEl.style.display='block'; respEl.textContent=`Optimizando para ${herramienta}...`;
  fetch('api/avi.php',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({question:q,section:'imagenes',client_name:c?c.name:'',client_industry:c?c.industry:'',client_url:c?c.url:'',client_ctx:ctx, brand_ctx:getBrandContext()})})
    .then(r=>r.json()).then(d=>{ respEl.textContent=d.response||''; })
    .catch(()=>{ respEl.textContent='Error.'; });
}

function generateImgRefToolPrompt(herramienta) {
  if(!lastImgRefPrompt){ document.getElementById('avi-resp-img-ref-tool').style.display='block'; document.getElementById('avi-resp-img-ref-tool').textContent='Analiza la imagen de referencia primero.'; return; }
  const ctx = getContext('imagenes');
  const c   = currentClient!=='all' ? CLIENTS.find(x=>x.id==currentClient) : null;
  const guias = {
    midjourney: 'Midjourney v6.1: incluye parámetros --ar, --v 6.1, --style raw para fotorrealismo. Captura el mood y estilo visual exacto de la referencia.',
    dalle: 'DALL-E 3: describe en detalle el estilo visual analizado, sin parámetros técnicos. Incluye iluminación, paleta y composición observados.',
    flux: 'Flux.1 Dev: 50-150 palabras en inglés. Captura la iluminación, textura y mood exactos observados en la referencia.',
    ideogram: 'Ideogram 2.0: si la referencia tiene texto, replícalo con instrucciones tipográficas exactas.',
    leonardo: 'Leonardo AI Phoenix: prompt + negative prompt. Incluye style preset más cercano al estilo de la referencia.',
    firefly: 'Adobe Firefly 3: describe el estilo y mood de la referencia. Selecciona el Content type adecuado en el panel.',
  };
  const q = `Basándote en este análisis de imagen de referencia:\n\n${lastImgRefPrompt}\n\nCrea el prompt optimizado para ${herramienta.toUpperCase()} para replicar ese estilo.\n${guias[herramienta]||''}\nListo para pegar directamente.`;
  const respEl = document.getElementById('avi-resp-img-ref-tool');
  respEl.style.display='block'; respEl.textContent=`Optimizando para ${herramienta}...`;
  fetch('api/avi.php',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({question:q,section:'imagenes',client_name:c?c.name:'',client_industry:c?c.industry:'',client_url:c?c.url:'',client_ctx:ctx, brand_ctx:getBrandContext()})})
    .then(r=>r.json()).then(d=>{ respEl.textContent=d.response||''; })
    .catch(()=>{ respEl.textContent='Error.'; });
}

function onImgPotChange(input) {
  const file = input.files[0];
  if(!file) return;
  document.getElementById('img-pot-filename').textContent = file.name;
  const reader = new FileReader();
  reader.onload = e => {
    document.getElementById('img-pot-thumb').src = e.target.result;
    document.getElementById('img-pot-preview').style.display = 'block';
  };
  reader.readAsDataURL(file);
}

async function analyzeOwnPhoto() {
  const file    = document.getElementById('img-pot-file').files[0];
  const objetivo= document.getElementById('img-pot-objetivo').value;
  const uso     = document.getElementById('img-pot-uso').value;
  const ratio   = document.getElementById('img-pot-ratio').value;
  const estilos = [...document.querySelectorAll('#img-estilos-pot .stag.active')].map(t=>t.textContent).join(', ');
  const ctx     = getContext('imagenes');
  const c       = currentClient!=='all' ? CLIENTS.find(x=>x.id==currentClient) : null;
  const respEl  = document.getElementById('avi-resp-pot');

  if(!file){ respEl.style.display='block'; respEl.textContent='Sube una foto primero.'; return; }
  respEl.style.display='block'; respEl.textContent='AVI analizando tu foto con Vision...';

  const fd = new FormData();
  fd.append('ref_image',   file);
  fd.append('modo',        'potenciar');
  fd.append('objetivo',    objetivo);
  fd.append('uso',         uso);
  fd.append('ratio',       ratio);
  fd.append('estilos',     estilos);
  fd.append('client_ctx',      ctx);
  fd.append('client_name',     c?c.name:'');
  fd.append('client_industry', c?c.industry:'');
  fd.append('client_url',      c?c.url:'');
  fd.append('client_id',       c?c.id:'');

  try {
    const res  = await fetch('api/images-analyze.php',{method:'POST',body:fd});
    const data = await res.json();
    if(data.ok){
      lastImgPotPrompt = data.response;
      respEl.textContent = lastImgPotPrompt;
      document.getElementById('img-pot-tool-btns').style.display='block';
      highlightRecommendedTool(lastImgPotPrompt, 'img-pot-tool-btns');
    } else { respEl.textContent='✗ '+(data.error||'Error'); }
  } catch(e){ respEl.textContent='✗ Error de conexión'; }
}

function generateImgPotToolPrompt(herramienta) {
  if(!lastImgPotPrompt){ document.getElementById('avi-resp-pot-tool').style.display='block'; document.getElementById('avi-resp-pot-tool').textContent='Analiza tu foto primero.'; return; }
  const ctx = getContext('imagenes');
  const c   = currentClient!=='all' ? CLIENTS.find(x=>x.id==currentClient) : null;
  const guias = {
    midjourney:'Midjourney v6.1: /imagine prompt con --ar ratio --v 6.1. Para img2img usar --iw 0.5-2 con la imagen como referencia.',
    dalle:'DALL-E 3: describe el resultado deseado con todos los detalles visuales. Indica claramente qué mantener y qué transformar.',
    flux:'Flux.1 Dev: prompt detallado en inglés 50-150 palabras. Describe la transformación deseada manteniendo el sujeto original.',
    ideogram:'Ideogram 2.0: si necesitas texto en la imagen resultante, especifícalo entre comillas con instrucciones tipográficas.',
    leonardo:'Leonardo AI Phoenix: prompt + negative prompt. Usa el modo Image2Image si está disponible para mantener la composición.',
    firefly:'Adobe Firefly 3: usa Generative Fill o Generate Similar para transformar manteniendo elementos. Libre de copyright.',
  };
  const q = `Basándote en este análisis de foto propia:\n\n${lastImgPotPrompt}\n\nCrea el prompt optimizado para ${herramienta.toUpperCase()} para lograr la transformación indicada.\n${guias[herramienta]||''}\nListo para pegar directamente.`;
  const respEl = document.getElementById('avi-resp-pot-tool');
  respEl.style.display='block'; respEl.textContent=`Optimizando para ${herramienta}...`;
  fetch('api/avi.php',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({question:q,section:'imagenes',client_name:c?c.name:'',client_industry:c?c.industry:'',client_url:c?c.url:'',client_ctx:ctx, brand_ctx:getBrandContext()})})
    .then(r=>r.json()).then(d=>{ respEl.textContent=d.response||''; })
    .catch(()=>{ respEl.textContent='Error.'; });
}

function generateImgIdeas(subtipo) {
  const nicho   = document.getElementById('img-idea-nicho').value.trim();
  const objetivo= document.getElementById('img-idea-objetivo').value;
  const ctx     = getContext('imagenes');
  const c       = currentClient!=='all' ? CLIENTS.find(x=>x.id==currentClient) : null;
  const respEl  = document.getElementById('avi-resp-img-ideas');
  if(!nicho && !ctx){ respEl.style.display='block'; respEl.textContent='Escribe el nicho primero.'; return; }
  const base = nicho || (ctx.substring(0,80));
  const subtipoTexts = {
    ads:   'conceptos de imagen específicamente diseñados para convertir en Meta Ads y Google Display, con copy space y CTA visual claro',
    viral: 'conceptos de imagen con alto potencial viral para Instagram y TikTok, que generen saves y shares',
    marca: 'conceptos para construir identidad visual de marca consistente y reconocible en todas las plataformas',
  };
  const focus = subtipo ? subtipoTexts[subtipo] : `imágenes para ${objetivo}`;
  const q = `Eres experto en creación de imágenes para marketing digital 2026. Genera 10 ideas de ${focus} para el nicho: "${base}".${ctx?'\nContexto: '+ctx:''}

Para cada idea incluye:
- Concepto: descripción visual en 1-2 líneas
- Por qué funciona: el principio psicológico o de marketing detrás
- Herramienta recomendada: cuál usar para generarla
Numera del 1 al 10.`;
  respEl.style.display='block'; respEl.textContent='AVI generando ideas...';
  fetch('api/avi.php',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({
    question:q, section:'imagenes',
    client_name:c?c.name:'', client_industry:c?c.industry:'', client_url:c?c.url:'', client_ctx:ctx, brand_ctx:getBrandContext()
  })})
  .then(r=>r.json()).then(d=>{ respEl.textContent=d.response||''; })
  .catch(()=>{ respEl.textContent='Configura la Claude API Key en ⚙ APIs.'; });
}

function usarConceptoImg(el) {
  const concepto = el.dataset.concepto;
  switchImgTab('prompts', document.querySelector('.img-tab[onclick*="prompts"]'));
  switchImgModo('crear');
  setTimeout(()=>{ document.getElementById('img-desc').value = concepto; }, 50);
}

function copyImgPrompt(modo) {
  const map = {
    crear:     { tool:'avi-resp-img-tool',     main:'avi-resp-imagenes',  confirm:'img-copy-confirm',     guard:'Genera el prompt primero.' },
    potenciar: { tool:'avi-resp-pot-tool',      main:'avi-resp-pot',       confirm:'img-pot-copy-confirm', guard:'Analiza tu foto primero.' },
    replicar:  { tool:'avi-resp-img-ref-tool',  main:'avi-resp-img-ref',   confirm:'img-ref-copy-confirm', guard:'Analiza la imagen de referencia primero.' },
  };
  const cfg = map[modo];
  if(!cfg) return;
  const toolTxt = document.getElementById(cfg.tool)?.textContent || '';
  const mainTxt = document.getElementById(cfg.main)?.textContent || '';
  const txt = (toolTxt && toolTxt !== cfg.guard) ? toolTxt : mainTxt;
  navigator.clipboard.writeText(txt).then(()=>{
    const el = document.getElementById(cfg.confirm);
    if(el){ el.style.display='inline'; setTimeout(()=>el.style.display='none',2000); }
  });
}

// ——— SEO TABS ———
let _gscData = null;
let _seoDevice = 'all';

function switchSEOTab(tab) {
  ['rendimiento','oportunidades','cobertura','sitemaps','cwv','schema','contenido'].forEach(t => {
    const panel = document.getElementById('seo-panel-' + t);
    const btn   = document.getElementById('seo-tab-' + t);
    if (panel) panel.style.display = t === tab ? '' : 'none';
    if (btn) {
      btn.style.color = t === tab ? 'var(--t)' : 'var(--m)';
      btn.style.borderBottom = t === tab ? '2px solid var(--red)' : '2px solid transparent';
    }
  });
  if (tab === 'oportunidades' && _gscData) renderGSCOpportunities(_gscData);
  if (tab === 'sitemaps') { loadSitemapList(); loadSitemapUrls(); }
  if (tab === 'cobertura') loadGSCCobertura();
  if (tab === 'rendimiento') loadRankHistory();
  if (tab === 'contenido') checkWPStatus();
}

function seoDeviceFilter(device) {
  _seoDevice = device;
  ['all','mobile','desktop'].forEach(d => {
    const btn = document.getElementById('gsc-dev-' + d);
    if (btn) btn.classList.toggle('active', d === device);
  });
  if (_gscData) renderGSCKeywords(_gscData);
}

// ——— GSC DATA ———
function loadGSCData(clientId, period, force) {
  if (!clientId || clientId === 'all') return;
  period = period || document.getElementById('gsc-period-sel')?.value || '30d';
  ['gsc-top3','gsc-top10','gsc-clicks','gsc-pos','gsc-imp-main','gsc-ctr-main'].forEach(id => { const el=document.getElementById(id); if(el) el.textContent='...'; });
  const kwEl = document.getElementById('gsc-keywords-list');
  if (kwEl) kwEl.innerHTML = '<div style="text-align:center;color:var(--m);padding:16px;font-size:12px">Cargando...</div>';
  const ncEl = document.getElementById('gsc-no-creds');
  if (ncEl) ncEl.style.display = 'none';
  const url = `api/gsc-data.php?client_id=${clientId}&period=${period}` + (force ? '&_t=' + Date.now() : '');
  fetch(url)
    .then(r => r.json())
    .then(data => {
      if (!data.ok) {
        if (ncEl) {
          const err = data.error || '';
          let msg = '⚠ ' + err;
          if (!err || err.toLowerCase().includes('domain') || err.toLowerCase().includes('dominio')) {
            msg = '⚠ Configura el dominio de Google Search Console en este cliente (Clientes → pestaña Search Console).';
          } else if (err.toLowerCase().includes('token') || err.toLowerCase().includes('oauth') || err.toLowerCase().includes('grant')) {
            msg = '⚠ Token de Google expirado — Search Console no puede conectarse.<br><button onclick="window.location.href=\'api/oauth-callback.php?return_to=seo\'" style="margin-top:8px;padding:7px 18px;border-radius:7px;font-size:12px;background:rgba(245,197,67,.2);border:1px solid rgba(245,197,67,.5);color:var(--y);cursor:pointer;font-family:var(--fb);font-weight:600">🔗 Reconectar Google OAuth →</button>';
          }
          ncEl.innerHTML = msg;
          ncEl.style.display = 'block';
        }
        ['gsc-top3','gsc-top10','gsc-clicks','gsc-pos','gsc-imp-main','gsc-ctr-main'].forEach(id => { const el=document.getElementById(id); if(el) el.textContent='—'; });
        if (kwEl) kwEl.innerHTML = '<div style="text-align:center;color:var(--m);padding:16px;font-size:12px">Sin datos</div>';
        setOvEl('ov-organic', 'Sin GSC'); setOvEl('ov-organic-d', 'Conecta Search Console');
        return;
      }
      _gscData = data.data;
      const m = data.data.metrics;
      const d = data.data.deltas || {};

      // Métricas principales
      const setM = (id, val) => { const el=document.getElementById(id); if(el) el.textContent=val; };
      setM('gsc-top3', m.top3);
      setM('gsc-top10', m.top10);
      setM('gsc-clicks', m.clicks.toLocaleString());
      setM('gsc-imp-main', m.impressions.toLocaleString());
      setM('gsc-ctr-main', m.ctr + '%');
      setM('gsc-pos', m.avg_position);

      // Deltas
      const fmtDelta = (v, invert) => {
        if (v === null || v === undefined) return 'vs anterior';
        const up = invert ? v < 0 : v > 0;
        const col = up ? 'var(--g)' : v === 0 ? 'var(--m)' : 'var(--red)';
        return `<span style="color:${col}">${v > 0 ? '▲' : v < 0 ? '▼' : ''}${Math.abs(v)}${typeof v === 'number' && v !== Math.round(v) ? '' : '%'} vs anterior</span>`;
      };
      const setD = (id, v, invert) => { const el=document.getElementById(id); if(el) el.innerHTML=fmtDelta(v,invert); };
      setD('gsc-clicks-delta', d.clicks, false);
      setD('gsc-imp-delta', d.impressions, false);
      setD('gsc-ctr-delta', d.ctr, false);
      setD('gsc-pos-delta', d.position, true); // positivo = mejoró (número bajó)

      // Overview
      setOvEl('ov-organic', m.clicks.toLocaleString());
      setOvEl('ov-organic-d', 'CTR ' + m.ctr + '%');

      // Keywords
      gscKeywordsList = data.data.keywords || [];
      renderGSCKeywords(data.data);

      // Páginas
      renderGSCPages(data.data.pages || []);

      // Dispositivos
      renderGSCDevices(data.data.devices || {});

      // Países
      renderGSCCountries(data.data.countries || []);

      // Chart
      const trend = data.data.trend || [];
      if (charts.seo && trend.length) {
        charts.seo.data.labels = trend.map(t => t.date.substring(5));
        charts.seo.data.datasets[0].data = trend.map(t => t.clicks);
        charts.seo.update();
      }

      // Oportunidades (si tab activo)
      const oppPanel = document.getElementById('seo-panel-oportunidades');
      if (oppPanel && oppPanel.style.display !== 'none') renderGSCOpportunities(data.data);

      // Sub-labels
      const kwSub = document.getElementById('gsc-kw-sub');
      if (kwSub) kwSub.textContent = gscKeywordsList.length + ' keywords';
      const pSub = document.getElementById('gsc-pages-sub');
      if (pSub) pSub.textContent = (data.data.pages||[]).length + ' páginas';

      aviAgentSEOPanelUpdate();
      seoAdvancedInit();
      // Cargar rank history si el panel está visible
      const rhCard = document.getElementById('rank-history-card');
      if (rhCard) loadRankHistory();
    })
    .catch(() => {
      ['gsc-top3','gsc-top10','gsc-clicks','gsc-pos','gsc-imp-main','gsc-ctr-main'].forEach(id => { const el=document.getElementById(id); if(el) el.textContent='—'; });
      setOvEl('ov-organic', 'Sin GSC'); setOvEl('ov-organic-d', '');
    });
}

function renderGSCKeywords(data) {
  const kwEl = document.getElementById('gsc-keywords-list');
  if (!kwEl) return;
  let kws = data.keywords || [];
  // Filtrar por dispositivo si aplica (solo si hay device data)
  kwEl.innerHTML = kws.length ? kws.map(k => {
    const posColor = k.position<=3 ? 'var(--g)' : k.position<=10 ? 'var(--b)' : 'var(--m)';
    const pos = Math.round(k.position * 10) / 10;
    return `<div class="srow">
      <div style="width:36px;color:${posColor};font-size:11px;font-weight:700">#${pos}</div>
      <div class="skw">${k.keyword}</div>
      <div class="sstat">${k.clicks.toLocaleString()}</div>
      <div class="sstat" style="min-width:56px">${k.impressions.toLocaleString()}</div>
      <div class="sstat" style="color:${k.ctr>=5?'var(--g)':k.ctr>=2?'var(--y)':'var(--red)'}">${k.ctr}%</div>
    </div>`;
  }).join('') : '<div style="text-align:center;color:var(--m);padding:16px;font-size:12px">Sin keywords</div>';
}

function renderGSCPages(pages) {
  const el = document.getElementById('gsc-pages-list');
  if (!el) return;
  el.innerHTML = pages.length ? pages.map(p => {
    const url = p.page.replace(/^https?:\/\/[^\/]+/, '') || '/';
    const posColor = p.position<=3 ? 'var(--g)' : p.position<=10 ? 'var(--b)' : 'var(--m)';
    return `<div class="srow" style="align-items:center">
      <div style="flex:1;font-size:11px;color:var(--t);overflow:hidden;text-overflow:ellipsis;white-space:nowrap" title="${p.page}">${url}</div>
      <div class="sstat">${p.clicks.toLocaleString()}</div>
      <div class="sstat" style="min-width:60px">${p.impressions.toLocaleString()}</div>
      <div class="sstat" style="color:${p.ctr>=5?'var(--g)':p.ctr>=2?'var(--y)':'var(--red)'}">${p.ctr}%</div>
      <div class="sstat" style="min-width:44px;color:${posColor}">#${Math.round(p.position*10)/10}</div>
    </div>`;
  }).join('') : '<div style="text-align:center;color:var(--m);padding:16px;font-size:12px">Sin páginas</div>';
}

function renderGSCDevices(devices) {
  const row = document.getElementById('gsc-devices-row');
  const grid = document.getElementById('gsc-devices-grid');
  if (!row || !grid) return;
  const devs = Object.entries(devices);
  if (!devs.length) { row.style.display = 'none'; return; }
  row.style.display = 'block';
  const icons = {mobile:'📱',desktop:'🖥',tablet:'📲'};
  grid.innerHTML = devs.map(([name, d]) => `
    <div style="background:var(--bg3);border-radius:8px;padding:8px 12px">
      <div style="font-size:11px;color:var(--m);margin-bottom:4px">${icons[name]||'💻'} ${name}</div>
      <div style="font-size:16px;font-weight:700">${d.clicks.toLocaleString()}</div>
      <div style="font-size:10px;color:var(--m)">${d.impressions.toLocaleString()} imp · CTR ${d.ctr}% · Pos #${d.position}</div>
    </div>
  `).join('');
}

function renderGSCCountries(countries) {
  const row = document.getElementById('gsc-countries-row');
  const list = document.getElementById('gsc-countries-list');
  if (!row || !list) return;
  if (!countries.length) { row.style.display = 'none'; return; }
  row.style.display = 'block';
  const max = countries[0]?.clicks || 1;
  list.innerHTML = countries.slice(0,7).map(c => `
    <div style="display:flex;align-items:center;gap:8px">
      <div style="width:110px;font-size:11px;color:var(--t);font-weight:600;flex-shrink:0">${c.name}</div>
      <div style="flex:1;height:6px;background:var(--bg3);border-radius:3px;overflow:hidden">
        <div style="width:${Math.round(c.clicks/max*100)}%;height:100%;background:var(--b);border-radius:3px"></div>
      </div>
      <div style="font-size:11px;color:var(--m);width:52px;text-align:right">${c.clicks.toLocaleString()} clics</div>
    </div>
  `).join('');
}

function renderGSCOpportunities(data) {
  const loadingEl = document.getElementById('gsc-opps-loading');
  if (loadingEl) loadingEl.style.display = 'none';

  // Quick wins
  const qw = data.quick_wins || [];
  const qwCard = document.getElementById('gsc-qw-card');
  const qwList = document.getElementById('gsc-quick-wins-list');
  if (qwCard && qwList) {
    qwCard.style.display = qw.length ? '' : 'none';
    qwList.innerHTML = qw.map(k => {
      const posColor = k.position<=10 ? 'var(--b)' : 'var(--y)';
      return `<div class="srow">
        <div style="width:44px;color:${posColor};font-size:11px;font-weight:700">#${Math.round(k.position*10)/10}</div>
        <div class="skw">${k.keyword}</div>
        <div class="sstat" style="min-width:60px">${k.impressions.toLocaleString()}</div>
        <div class="sstat">${k.clicks.toLocaleString()}</div>
        <div class="sstat" style="color:${k.ctr>=3?'var(--y)':'var(--red)'}">${k.ctr}%</div>
      </div>`;
    }).join('') || '<div style="text-align:center;color:var(--m);padding:12px;font-size:12px">No hay quick wins identificados</div>';
  }

  // Low CTR
  const lc = data.low_ctr || [];
  const lcCard = document.getElementById('gsc-lowctr-card');
  const lcList = document.getElementById('gsc-low-ctr-list');
  if (lcCard && lcList) {
    lcCard.style.display = lc.length ? '' : 'none';
    lcList.innerHTML = lc.map(k => {
      const potClicks = Math.round(k.impressions * 0.05);
      return `<div class="srow">
        <div style="width:44px;font-size:11px;font-weight:700;color:var(--m)">#${Math.round(k.position*10)/10}</div>
        <div class="skw">${k.keyword}</div>
        <div class="sstat" style="min-width:60px">${k.impressions.toLocaleString()}</div>
        <div class="sstat" style="color:var(--red)">${k.ctr}%</div>
        <div class="sstat" style="min-width:60px;color:var(--g)">~${potClicks.toLocaleString()}</div>
      </div>`;
    }).join('') || '<div style="text-align:center;color:var(--m);padding:12px;font-size:12px">Sin CTR bajas identificadas</div>';
  }

  // No clicks
  const nc = data.no_clicks || [];
  const ncCard = document.getElementById('gsc-noclicks-card');
  const ncList = document.getElementById('gsc-no-clicks-list');
  if (ncCard && ncList) {
    ncCard.style.display = nc.length ? '' : 'none';
    ncList.innerHTML = nc.map(k => `
      <div class="srow">
        <div style="width:44px;font-size:11px;font-weight:700;color:var(--m)">#${Math.round(k.position*10)/10}</div>
        <div class="skw">${k.keyword}</div>
        <div class="sstat" style="min-width:60px">${k.impressions.toLocaleString()}</div>
        <div class="sstat" style="color:var(--m)">0</div>
      </div>
    `).join('') || '<div style="text-align:center;color:var(--m);padding:12px;font-size:12px">Sin keywords sin clics</div>';
  }
}

// ——— SITEMAPS ———
function loadSitemapList() {
  if (!currentClient) { showToast('Selecciona un cliente', 'var(--y)'); return; }
  const el = document.getElementById('sitemap-gsc-list');
  if (el) el.innerHTML = '<div style="text-align:center;color:var(--m);padding:16px;font-size:12px">Cargando sitemaps...</div>';
  fetch(`api/sitemap.php?action=list&client_id=${currentClient}`)
    .then(r => r.json())
    .then(data => {
      if (!el) return;
      if (!data.ok) { el.innerHTML = `<div style="color:var(--red);padding:12px;font-size:12px">Error: ${data.error}</div>`; return; }
      const sms = data.sitemaps || [];
      if (!sms.length) { el.innerHTML = '<div style="text-align:center;color:var(--m);padding:16px;font-size:12px">No hay sitemaps registrados en GSC</div>'; return; }
      el.innerHTML = `
        <div class="srow" style="font-size:10px;color:var(--m);padding-bottom:5px;border-bottom:1px solid var(--br)">
          <div style="flex:1">Sitemap</div>
          <div style="min-width:70px;text-align:right" data-tip="URLs enviadas al sitemap">Enviadas</div>
          <div style="min-width:70px;text-align:right" data-tip="URLs que Google ha indexado de este sitemap">Indexadas</div>
          <div style="min-width:60px;text-align:right">Errores</div>
          <div style="min-width:80px;text-align:right" data-tip="Última vez que Google descargó el sitemap">Última vez</div>
        </div>
        ${sms.map(sm => {
          const statusColor = sm.errors > 0 ? 'var(--red)' : sm.warnings > 0 ? 'var(--y)' : 'var(--g)';
          const pct = sm.submitted > 0 ? Math.round(sm.indexed / sm.submitted * 100) : 0;
          return `<div class="srow">
            <div style="flex:1;font-size:11px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap" title="${sm.path}">${sm.path}</div>
            <div class="sstat" style="min-width:70px">${sm.submitted.toLocaleString()}</div>
            <div class="sstat" style="min-width:70px;color:${pct>=80?'var(--g)':pct>=50?'var(--y)':'var(--red)'}">${sm.indexed.toLocaleString()} <span style="font-size:10px;color:var(--m)">(${pct}%)</span></div>
            <div class="sstat" style="min-width:60px;color:${statusColor}">${sm.errors > 0 ? sm.errors + ' ✗' : sm.warnings > 0 ? sm.warnings + ' ⚠' : '✓'}</div>
            <div class="sstat" style="min-width:80px;color:var(--m)">${sm.last_dl || '—'}</div>
          </div>`;
        }).join('')}
      `;
    })
    .catch(() => { if (el) el.innerHTML = '<div style="color:var(--red);padding:12px;font-size:12px">Error de conexión</div>'; });
}

function loadSitemapUrls() {
  if (!currentClient) { showToast('Selecciona un cliente', 'var(--y)'); return; }
  const el = document.getElementById('sitemap-urls-list');
  if (el) el.innerHTML = '<div style="text-align:center;color:var(--m);padding:16px;font-size:12px">Descargando sitemap.xml...</div>';
  fetch(`api/sitemap.php?action=fetch&client_id=${currentClient}`)
    .then(r => r.json())
    .then(data => {
      if (!el) return;
      if (!data.ok) { el.innerHTML = `<div style="color:var(--red);padding:12px;font-size:12px">Error: ${data.error}</div>`; return; }
      const urls = data.urls || [];
      if (data.is_index) {
        el.innerHTML = `<div style="font-size:12px;color:var(--b);margin-bottom:8px">📋 Sitemap Index con ${data.count} sitemaps hijo</div>` +
          urls.map(u => `<div class="srow"><div style="flex:1;font-size:11px">${u.url}</div><div style="font-size:10px;color:var(--m)">${u.lastmod||''}</div></div>`).join('');
      } else {
        el.innerHTML = `<div style="font-size:12px;color:var(--m);margin-bottom:8px">${data.count} URLs en sitemap${data.count > 200 ? ' (mostrando 200)' : ''}</div>` +
          `<div class="srow" style="font-size:10px;color:var(--m);padding-bottom:4px;border-bottom:1px solid var(--br)">
            <div style="flex:1">URL</div>
            <div style="min-width:70px;text-align:right">Última mod.</div>
            <div style="min-width:60px;text-align:right" data-tip="Prioridad de rastreo: 1.0=máxima, 0.5=normal, 0.1=baja">Prioridad</div>
          </div>` +
          urls.map(u => {
            const path = u.url.replace(/^https?:\/\/[^\/]+/, '') || '/';
            return `<div class="srow">
              <div style="flex:1;font-size:11px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap" title="${u.url}">${path}</div>
              <div class="sstat" style="min-width:70px;color:var(--m)">${u.lastmod||'—'}</div>
              <div class="sstat" style="min-width:60px;color:${u.priority>=0.8?'var(--g)':u.priority>=0.5?'var(--m)':'var(--red)'}">${u.priority}</div>
            </div>`;
          }).join('');
      }
    })
    .catch(() => { if (el) el.innerHTML = '<div style="color:var(--red);padding:12px;font-size:12px">Error al leer sitemap.xml</div>'; });
}

function submitSitemap() {
  if (!currentClient) { showToast('Selecciona un cliente', 'var(--y)'); return; }
  fetch(`api/sitemap.php?action=submit&client_id=${currentClient}`)
    .then(r => r.json())
    .then(data => {
      if (data.ok) { showToast('✓ ' + data.message, 'var(--g)'); loadSitemapList(); }
      else showToast('Error: ' + data.error, 'var(--red)');
    })
    .catch(() => showToast('Error de conexión', 'var(--red)'));
}

// ——— CWV ———
function setCWVStrategy(strategy) {
  ['mobile','desktop'].forEach(s => {
    const btn = document.getElementById('cwv-btn-' + s);
    if (btn) btn.classList.toggle('active', s === strategy);
  });
}

function renderCWVResult(result) {
  const score = result.performance;
  const scoreEl = document.getElementById('cwv-score-circle');
  const labelEl = document.getElementById('cwv-score-label');
  const stratEl = document.getElementById('cwv-strategy-label');
  if (scoreEl) {
    const col = score >= 90 ? '#22c97a' : score >= 50 ? '#f5c543' : '#e8272a';
    scoreEl.style.borderColor = col;
    scoreEl.style.color = col;
    scoreEl.textContent = score;
  }
  if (labelEl) labelEl.textContent = score >= 90 ? '✓ Bueno' : score >= 50 ? '⚠ Necesita mejoras' : '✗ Deficiente';
  if (labelEl) labelEl.style.color = score >= 90 ? 'var(--g)' : score >= 50 ? 'var(--y)' : 'var(--red)';
  if (stratEl) stratEl.textContent = result.strategy === 'mobile' ? '📱 Móvil' : '🖥 Escritorio';

  // Field data (CrUX)
  const field = result.field || {};
  const fieldBanner = document.getElementById('cwv-field-banner');
  const fieldOverall = document.getElementById('cwv-field-overall');
  const fieldMetrics = document.getElementById('cwv-field-metrics');
  if (fieldBanner && field.overall) {
    fieldBanner.style.display = '';
    const catColors = {FAST:'var(--g)',AVERAGE:'var(--y)',SLOW:'var(--red)'};
    const catLabels = {FAST:'✓ Buena experiencia real',AVERAGE:'⚠ Necesita mejoras',SLOW:'✗ Experiencia deficiente'};
    if (fieldOverall) {
      fieldOverall.textContent = catLabels[field.overall] || field.overall;
      fieldOverall.style.color = catColors[field.overall] || 'var(--m)';
    }
    if (fieldMetrics) {
      const fmts = {lcp:'LCP',fcp:'FCP',cls:'CLS',inp:'INP',ttfb:'TTFB'};
      const catCol = {FAST:'var(--g)',AVERAGE:'var(--y)',SLOW:'var(--red)'};
      fieldMetrics.innerHTML = Object.entries(fmts).filter(([k]) => field[k]).map(([k,label]) => {
        const f = field[k];
        return `<div style="font-size:11px"><span style="color:var(--m)">${label}:</span> <span style="color:${catCol[f.category]||'var(--m)'};font-weight:600">${f.category||'—'}</span> <span style="color:var(--m)">(p75: ${f.p75||'—'})</span></div>`;
      }).join('');
    }
  } else if (fieldBanner) fieldBanner.style.display = 'none';

  // Lab metrics grid
  const grid = document.getElementById('cwv-metrics-grid');
  if (grid) {
    const metrics = [
      {key:'lcp',  label:'LCP',  tip:'Largest Contentful Paint — tiempo en que carga el elemento más grande visible. <2.5s bueno, <4s mejorable, >4s deficiente.', good:2500,  avg:4000,  unit:'ms'},
      {key:'fcp',  label:'FCP',  tip:'First Contentful Paint — tiempo hasta que aparece el primer contenido. <1.8s bueno.',                                        good:1800,  avg:3000,  unit:'ms'},
      {key:'cls',  label:'CLS',  tip:'Cumulative Layout Shift — inestabilidad visual (elementos que se mueven). <0.1 bueno, <0.25 mejorable.',                    good:0.1,   avg:0.25,  unit:''},
      {key:'tbt',  label:'TBT',  tip:'Total Blocking Time — tiempo que el hilo principal está bloqueado. <200ms bueno.',                                           good:200,   avg:600,   unit:'ms'},
      {key:'si',   label:'SI',   tip:'Speed Index — qué tan rápido se renderiza el contenido visualmente. <3.4s bueno.',                                          good:3400,  avg:5800,  unit:'ms'},
      {key:'tti',  label:'TTI',  tip:'Time to Interactive — cuándo la página es completamente interactiva. <3.8s bueno.',                                         good:3800,  avg:7300,  unit:'ms'},
      {key:'inp',  label:'INP',  tip:'Interaction to Next Paint — tiempo de respuesta a interacciones del usuario. <200ms bueno. Nuevo estándar 2024.',           good:200,   avg:500,   unit:'ms'},
      {key:'ttfb', label:'TTFB', tip:'Time to First Byte — tiempo de respuesta del servidor. <800ms bueno.',                                                       good:800,   avg:1800,  unit:'ms'},
    ];
    grid.innerHTML = metrics.map(({key,label,tip,good,avg,unit}) => {
      const m = result[key];
      if (!m || m.display === '—') return '';
      const val = m.value;
      const col = val === null ? 'var(--m)' : val <= good ? 'var(--g)' : val <= avg ? 'var(--y)' : 'var(--red)';
      const score = m.score === null ? '' : m.score >= 0.9 ? '🟢' : m.score >= 0.5 ? '🟡' : '🔴';
      return `<div style="background:var(--bg2);border-radius:8px;padding:10px 12px" data-tip="${tip}">
        <div style="font-size:10px;color:var(--m);margin-bottom:3px">${score} ${label}</div>
        <div style="font-size:16px;font-weight:700;color:${col}">${m.display}</div>
      </div>`;
    }).join('');
  }

  // Oportunidades
  const opps = result.opportunities || [];
  const oppsEl = document.getElementById('cwv-opps');
  const oppsList = document.getElementById('cwv-opps-list');
  if (oppsEl && oppsList) {
    oppsEl.style.display = opps.length ? '' : 'none';
    oppsList.innerHTML = opps.map(o => {
      const col = o.score === null ? 'var(--m)' : o.score < 0.5 ? 'var(--red)' : 'var(--y)';
      return `<div style="display:flex;align-items:flex-start;gap:10px;padding:8px 10px;background:var(--bg2);border-radius:8px">
        <div style="width:8px;height:8px;border-radius:50%;background:${col};flex-shrink:0;margin-top:4px"></div>
        <div>
          <div style="font-size:12px;font-weight:600">${o.title}</div>
          ${o.savings ? `<div style="font-size:11px;color:var(--g);margin-top:1px">Ahorro estimado: ${o.savings}</div>` : ''}
          ${o.desc ? `<div style="font-size:10px;color:var(--m);margin-top:2px">${o.desc.substring(0,120)}${o.desc.length>120?'...':''}</div>` : ''}
        </div>
      </div>`;
    }).join('');
  }

  // Diagnósticos
  const diags = result.diagnostics || [];
  const diagsEl = document.getElementById('cwv-diags');
  const diagsList = document.getElementById('cwv-diags-list');
  if (diagsEl && diagsList) {
    diagsEl.style.display = diags.length ? '' : 'none';
    diagsList.innerHTML = diags.map(d => `
      <div style="display:flex;align-items:center;gap:8px;font-size:11px;padding:5px 8px;background:var(--bg2);border-radius:6px">
        <span>⚠</span><span style="flex:1">${d.title}</span>${d.display ? `<span style="color:var(--y)">${d.display}</span>` : ''}
      </div>
    `).join('');
  }

  // Historial
  loadCWVHistory();
}

async function loadCWVHistory() {
  const el = document.getElementById('cwv-history-list');
  if (!el || !currentClient || currentClient === 'all') return;
  const url = document.getElementById('cwv-url')?.value.trim() || _seoClientUrl();
  if (!url) return;
  el.innerHTML = '<div style="text-align:center;color:var(--m);padding:12px;font-size:12px">Cargando historial...</div>';
  try {
    const r = await fetch(`api/cwv-history.php?client_id=${encodeURIComponent(currentClient)}&url=${encodeURIComponent(url)}`);
    const d = await r.json();
    if (!d.ok || !d.rows || !d.rows.length) {
      el.innerHTML = '<div style="text-align:center;color:var(--m);padding:12px;font-size:12px">Sin historial para esta URL. Ejecuta más análisis para ver la evolución.</div>';
      return;
    }
    const colP = v => v >= 90 ? 'var(--g)' : v >= 50 ? 'var(--y)' : 'var(--red)';
    el.innerHTML = '<table style="width:100%;border-collapse:collapse;font-size:12px">'
      + '<thead><tr style="background:var(--bg2)">'
      + '<th style="padding:7px 10px;text-align:left;color:var(--m)">Fecha</th>'
      + '<th style="padding:7px 10px;text-align:center;color:var(--m)">Estrategia</th>'
      + '<th style="padding:7px 10px;text-align:center;color:var(--m)">Score</th>'
      + '<th style="padding:7px 10px;text-align:center;color:var(--m)">LCP</th>'
      + '<th style="padding:7px 10px;text-align:center;color:var(--m)">CLS</th>'
      + '<th style="padding:7px 10px;text-align:center;color:var(--m)">TBT</th>'
      + '</tr></thead><tbody>'
      + d.rows.map(row => `<tr style="border-bottom:1px solid var(--br)">
          <td style="padding:7px 10px;color:var(--m)">${row.checked_at}</td>
          <td style="padding:7px 10px;text-align:center">${row.strategy === 'mobile' ? '📱' : '🖥'}</td>
          <td style="padding:7px 10px;text-align:center;font-weight:700;color:${colP(row.performance)}">${row.performance}</td>
          <td style="padding:7px 10px;text-align:center;color:var(--m)">${row.lcp ? (row.lcp/1000).toFixed(2)+'s' : '—'}</td>
          <td style="padding:7px 10px;text-align:center;color:var(--m)">${row.cls != null ? row.cls : '—'}</td>
          <td style="padding:7px 10px;text-align:center;color:var(--m)">${row.tbt ? Math.round(row.tbt)+'ms' : '—'}</td>
        </tr>`).join('')
      + '</tbody></table>';
  } catch(e) {
    el.innerHTML = '<div style="text-align:center;color:var(--m);padding:12px;font-size:12px">Error cargando historial.</div>';
  }
}

// ——— RANK HISTORY CHART ———
let _rankChart=null,_rankHistoryPeriod='30d';
async function loadRankHistory(period){
  if(!currentClient||currentClient==='all') return;
  if(period){_rankHistoryPeriod=period;['30d','60d','90d'].forEach(p=>{const b=document.getElementById('rh-btn-'+p.replace('d',''));if(b)b.classList.toggle('active',p===period);});}
  const lE=document.getElementById('rank-history-loading'),wE=document.getElementById('rank-history-wrap'),eE=document.getElementById('rank-history-empty');
  if(lE)lE.style.display='block';if(wE)wE.style.display='none';if(eE)eE.style.display='none';
  try{
    const d=await fetch(`api/gsc-data.php?action=position_history&client_id=${currentClient}&days=${_rankHistoryPeriod}`).then(r=>r.json());
    if(lE)lE.style.display='none';
    if(!d.ok||!d.data||!d.data.length){if(eE)eE.style.display='block';return;}
    if(wE)wE.style.display='block';
    if(_rankChart){_rankChart.destroy();_rankChart=null;}
    const ctx=document.getElementById('chartRankPos');if(!ctx)return;
    const isDark=!document.body.classList.contains('light'),gc=isDark?'rgba(255,255,255,.06)':'rgba(0,0,0,.06)',tc=isDark?'rgba(240,240,240,.4)':'rgba(0,0,0,.45)';
    _rankChart=new Chart(ctx,{type:'line',data:{labels:d.data.map(r=>r.date.substring(5)),datasets:[{label:'Posición',data:d.data.map(r=>r.position),borderColor:'#4a9ef8',backgroundColor:'rgba(74,158,248,.1)',tension:.35,fill:true,pointRadius:d.data.length<=30?3:0,pointHoverRadius:5}]},options:{responsive:true,maintainAspectRatio:false,plugins:{legend:{display:false},tooltip:{callbacks:{label:c=>'Pos. '+c.parsed.y}}},scales:{x:{ticks:{color:tc,font:{size:10},maxTicksLimit:10},grid:{color:gc}},y:{reverse:true,min:1,ticks:{color:tc,font:{size:10},callback:v=>'#'+v},grid:{color:gc},title:{display:true,text:'Posición (↓ mejor)',color:tc,font:{size:10}}}}}});
  }catch(e){if(lE)lE.style.display='none';if(eE){eE.textContent='Error al cargar historial.';eE.style.display='block';}}
}

// ——— COBERTURA GSC ———
async function loadGSCCobertura(force){
  if(!currentClient||currentClient==='all') return;
  const lE=document.getElementById('cob-loading'),dE=document.getElementById('cob-data'),eE=document.getElementById('cob-error');
  if(lE)lE.style.display='block';if(dE)dE.style.display='none';if(eE)eE.style.display='none';
  try{
    const d=await fetch(`api/gsc-data.php?action=cobertura&client_id=${currentClient}`+(force?'&_t='+Date.now():'')).then(r=>r.json());
    if(lE)lE.style.display='none';
    if(!d.ok){if(eE){eE.textContent='⚠ '+(d.error||'Error al cargar cobertura');eE.style.display='block';}return;}
    const v=d.data,se=(id,val)=>{const el=document.getElementById(id);if(el)el.textContent=val;};
    se('cob-submitted',v.submitted||0);se('cob-indexed',v.indexed||0);se('cob-not-indexed',v.not_indexed||0);se('cob-errors',v.errors||0);
    const pct=v.submitted>0?Math.round((v.indexed/v.submitted)*100):0;
    const bar=document.getElementById('cob-progress-bar'),lbl=document.getElementById('cob-progress-label');
    if(bar){bar.style.width=pct+'%';bar.style.background=pct>=80?'var(--g)':pct>=50?'var(--y)':'var(--red)';}
    if(lbl)lbl.textContent=`${pct}% de URLs enviadas están indexadas en Google`;
    const tbody=document.getElementById('cob-sitemaps-body');
    if(tbody&&v.sitemaps){const esc=s=>String(s||'').replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');tbody.innerHTML=v.sitemaps.map(sm=>`<tr><td style="font-size:11px;word-break:break-all">${esc(sm.path)}</td><td style="text-align:center">${sm.submitted}</td><td style="text-align:center;color:var(--g)">${sm.indexed}</td><td style="text-align:center;color:${(sm.submitted-sm.indexed)>0?'var(--y)':'var(--m)'}">${Math.max(0,sm.submitted-sm.indexed)}</td><td style="text-align:center;color:${sm.errors>0?'var(--red)':'var(--m)'}">${sm.errors}</td><td style="font-size:11px;color:var(--m)">${esc(sm.last_dl||'—')}</td></tr>`).join('')||'<tr><td colspan="6" style="text-align:center;color:var(--m);padding:14px">No hay sitemaps en GSC</td></tr>';}
    if(dE)dE.style.display='block';
  }catch(e){if(lE)lE.style.display='none';if(eE){eE.textContent='⚠ Error de red';eE.style.display='block';}}
}

// ——— KEYWORD GAP ———
async function runKeywordGap(){
  const competitor=document.getElementById('kw-gap-competitor')?.value.trim();
  if(!competitor){alert('Ingresa el dominio del competidor');return;}
  const respEl=document.getElementById('kw-gap-resp');if(!respEl)return;
  respEl.innerHTML='<span style="color:var(--m);font-size:12px">✦ AVI analizando keyword gap...</span>';respEl.style.display='block';
  const kws=(_gscData?.keywords||[]).slice(0,30).map(k=>`${k.query} (pos ${Math.round(k.position)}, ${k.clicks} clics)`).join('\n');
  const q=`Analiza el keyword gap entre mi sitio y el competidor "${competitor}".\n\nMis top keywords en GSC:\n${kws||'Sin datos GSC aún'}\n\nDime:\n1. Qué tipos de keywords debería tener y no tengo\n2. Qué temas cubre el competidor que yo no\n3. Las 10 keywords de mayor oportunidad para crear contenido\n4. Estrategia para cerrar el gap en 60 días`;
  const c=CLIENTS?.find(x=>x.id==currentClient)||{},ctx=document.getElementById('seo-ctx')?.value||'',brandCtx=typeof getBrandContext==='function'?getBrandContext():'';
  try{
    const token=document.querySelector('meta[name="dm-csrf"]')?.content||'';
    const d=await fetch('api/avi.php',{method:'POST',headers:{'Content-Type':'application/json','X-CSRF-Token':token},body:JSON.stringify({question:q,section:'seo',client_name:c.name||'',client_ctx:ctx,brand_ctx:brandCtx})}).then(r=>r.json());
    respEl.innerHTML=typeof renderMD==='function'?renderMD(d.response||'Sin respuesta'):(d.response||'Sin respuesta').replace(/\n/g,'<br>');
  }catch(e){respEl.innerHTML='<span style="color:#f87171">Error al contactar AVI.</span>';}
}

// ——— PUBLICAR EN WORDPRESS ———
async function publishToWordPress(){
  const _seoArticleText = typeof _lastSEOArticleRaw !== 'undefined' ? _lastSEOArticleRaw : '';
  if(!_seoArticleText){alert('Genera un artículo primero antes de publicar.');return;}
  if(!currentClient||currentClient==='all'){alert('Selecciona un cliente primero.');return;}
  const btn=document.getElementById('wp-publish-btn');
  if(btn){btn.disabled=true;btn.textContent='📤 Publicando...';}
  const keyword=document.getElementById('art-keyword')?.value?.trim()||'Artículo SEO';
  const token=document.querySelector('meta[name="dm-csrf"]')?.content||'';
  try{
    const d=await fetch('api/wp-publish.php',{method:'POST',headers:{'Content-Type':'application/json','X-CSRF-Token':token},body:JSON.stringify({action:'post',client_id:currentClient,title:keyword,content:_seoArticleText})}).then(r=>r.json());
    if(!d.ok){alert('Error: '+(d.error||'No se pudo publicar'));}
    else if(d.edit_url&&confirm('✅ Borrador creado en WordPress.\n¿Abrir el editor para publicar?')){window.open(d.edit_url,'_blank');}
    else if(!d.edit_url){alert('✅ Artículo publicado como borrador en WordPress.');}
  }catch(e){alert('Error de red al conectar con WordPress.');}
  finally{if(btn){btn.disabled=false;btn.textContent='📤 Publicar en WordPress';}}
}

function exportGSCKeywords() {
  const rows = document.querySelectorAll('#gsc-keywords-list .srow');
  if (!rows.length) return;
  let csv = 'Posicion,Keyword,Clics,CTR\n';
  rows.forEach(r => {
    const cols = r.querySelectorAll('.skw,.sstat,[style*="width:32px"]');
    if (cols.length >= 4) csv += `"${cols[0].textContent}","${cols[1].textContent}","${cols[2].textContent}","${cols[3].textContent}"\n`;
  });
  const a = document.createElement('a'); a.href = 'data:text/csv;charset=utf-8,' + encodeURIComponent(csv);
  a.download = 'gsc-keywords.csv'; a.click();
}

// ——— SHOPIFY DATA ———
function loadShopifyData(clientId, force) {
  if (!clientId || clientId === 'all') return;
  document.getElementById('shopify-no-creds').style.display = 'none';
  ['sh-revenue','sh-orders','sh-products','sh-pending'].forEach(id => { const el=document.getElementById(id); if(el) el.textContent='...'; });
  document.getElementById('sh-orders-list').innerHTML = '<div style="text-align:center;color:var(--m);padding:16px;font-size:12px">Cargando...</div>';
  fetch(`api/shopify-data.php?client_id=${clientId}${force?'&_t='+Date.now():''}`)
    .then(r => r.json())
    .then(data => {
      if (!data.ok) {
        document.getElementById('shopify-no-creds').style.display = 'block';
        ['sh-revenue','sh-orders','sh-products','sh-pending'].forEach(id => { const el=document.getElementById(id); if(el) el.textContent='—'; });
        document.getElementById('sh-orders-list').innerHTML = '<div style="text-align:center;color:var(--m);padding:16px;font-size:12px">Sin datos</div>';
        return;
      }
      const m = data.data.metrics;
      const s = data.data.shop;
      document.getElementById('sh-revenue').textContent   = m.revenue_fmt;
      document.getElementById('sh-aov').textContent       = 'ticket: ' + m.avg_order_fmt;
      document.getElementById('sh-orders').textContent    = m.orders_30d;
      document.getElementById('sh-fulfilled').textContent = m.fulfilled + ' completadas';
      document.getElementById('sh-products').textContent  = m.total_products;
      document.getElementById('sh-customers').textContent = m.total_customers + ' clientes';
      document.getElementById('sh-pending').textContent   = m.pending;
      document.getElementById('sh-cancelled').textContent = m.cancelled + ' canceladas';
      document.getElementById('sh-shop-name').textContent = s.name ? s.name + ' · ' + s.currency : '';
      // Órdenes recientes
      const orders = data.data.recent_orders || [];
      const stColors = {paid:'var(--g)',pending:'var(--y)',refunded:'#f87171',fulfilled:'var(--g)',partial:'var(--y)'};
      document.getElementById('sh-orders-list').innerHTML = orders.length ? orders.map(o =>
        `<div class="srow"><div style="width:55px;font-size:11px;color:var(--b)">${o.name}</div><div style="flex:1;font-size:11px;color:var(--m)">${o.date}</div><div class="sstat">$${o.total.toLocaleString()}</div><div style="min-width:70px;text-align:right;font-size:10px;color:${stColors[o.financial_status]||'var(--m)'}">${o.financial_status}</div></div>`
      ).join('') : '<div style="text-align:center;color:var(--m);padding:16px;font-size:12px">Sin órdenes</div>';
      // Stock bajo
      const ls = data.data.low_stock || [];
      document.getElementById('sh-lowstock-list').innerHTML = ls.length ? ls.map(p =>
        `<div class="srow"><div style="flex:1;font-size:12px">${p.product}</div><div style="font-size:11px;color:var(--m);min-width:50px">${p.variant}</div><div style="min-width:30px;text-align:right;color:${p.stock<=2?'#f87171':'var(--y)'}; font-weight:600">${p.stock}</div></div>`
      ).join('') : '<div style="text-align:center;color:var(--g);padding:12px;font-size:12px">✓ Sin alertas de stock</div>';
    })
    .catch(() => {
      ['sh-revenue','sh-orders','sh-products','sh-pending'].forEach(id => { const el=document.getElementById(id); if(el) el.textContent='—'; });
    });
}

// ——— ACTIVOS DE MARCA ———
let brandAssets = { logo:'', colors:'', fonts:'', manual:'', description:'', styles:[] };

function loadBrandAssets(clientId) {
  if(!clientId || clientId==='all') {
    document.getElementById('marca-no-client').style.display='block';
    document.getElementById('marca-content').style.display='none';
    document.getElementById('marca-colors').value='';
    document.getElementById('marca-fonts').value='';
    document.getElementById('marca-description').value='';
    document.getElementById('marca-color-swatches').innerHTML='';
    resetBrandLogo(); resetBrandManual();
    for(let s=1;s<=4;s++) resetBrandStyle(s);
    return;
  }
  document.getElementById('marca-no-client').style.display='none';
  document.getElementById('marca-content').style.display='block';
  fetch('api/brand-assets.php?action=load&client_id='+clientId)
    .then(r=>r.json()).then(d=>{
      if(!d.ok) return;
      brandAssets = { logo:d.logo||'', colors:d.colors||'', fonts:d.fonts||'', manual:d.manual||'', description:d.description||'', styles:d.styles||[] };
      if(d.logo){ showBrandLogo(d.logo+'?t='+Date.now()); } else { resetBrandLogo(); }
      document.getElementById('marca-colors').value = d.colors||'';
      renderColorSwatches();
      document.getElementById('marca-fonts').value = d.fonts||'';
      document.getElementById('marca-description').value = d.description||'';
      if(d.manual){ showBrandManual(d.manual.split('/').pop()); } else { resetBrandManual(); }
      for(let s=1;s<=4;s++){
        const path = d.styles[s-1]||'';
        if(path){ showBrandStyle(s, path+'?t='+Date.now()); } else { resetBrandStyle(s); }
      }
    }).catch(()=>{});
}

function showBrandLogo(url){
  document.getElementById('marca-logo-placeholder').style.display='none';
  const img=document.getElementById('marca-logo-img');
  img.src=url; img.style.display='block';
  document.getElementById('marca-logo-del').style.display='inline-block';
}
function resetBrandLogo(){
  document.getElementById('marca-logo-placeholder').style.display='block';
  const img=document.getElementById('marca-logo-img');
  img.src=''; img.style.display='none';
  document.getElementById('marca-logo-del').style.display='none';
  document.getElementById('marca-logo-status').textContent='';
}
function showBrandStyle(slot, url){
  document.getElementById('marca-style-'+slot+'-ph').style.display='none';
  const img=document.getElementById('marca-style-'+slot+'-img');
  img.src=url; img.style.display='block';
  document.getElementById('marca-style-'+slot+'-del').style.display='block';
}
function resetBrandStyle(slot){
  document.getElementById('marca-style-'+slot+'-ph').style.display='block';
  const img=document.getElementById('marca-style-'+slot+'-img');
  img.src=''; img.style.display='none';
  document.getElementById('marca-style-'+slot+'-del').style.display='none';
}

function showBrandManual(filename){
  document.getElementById('marca-manual-placeholder').style.display='none';
  const nm = document.getElementById('marca-manual-name');
  nm.textContent = filename || 'manual.pdf'; nm.style.display='block';
  document.getElementById('marca-manual-extract-btn').style.display='inline-block';
  document.getElementById('marca-manual-del').style.display='inline-block';
}
function resetBrandManual(){
  document.getElementById('marca-manual-placeholder').style.display='block';
  document.getElementById('marca-manual-name').style.display='none';
  document.getElementById('marca-manual-extract-btn').style.display='none';
  document.getElementById('marca-manual-del').style.display='none';
  document.getElementById('marca-manual-status').textContent='';
}

async function uploadBrandManual(input){
  const file = input.files[0]; if(!file) return;
  const clientId = currentClient;
  if(!clientId || clientId==='all'){ alert('Selecciona un cliente primero'); return; }
  const status = document.getElementById('marca-manual-status');
  status.style.color='var(--m)'; status.textContent='Subiendo...';
  const fd = new FormData();
  fd.append('action','upload_manual'); fd.append('client_id',clientId); fd.append('file',file);
  try {
    const res = await fetch('api/brand-assets.php',{method:'POST',body:fd});
    const data = await res.json();
    if(data.ok){
      showBrandManual(file.name);
      brandAssets.manual = data.path;
      status.style.color='var(--g)'; status.textContent='✓ Manual guardado';
    } else {
      status.style.color='var(--red)'; status.textContent = data.error||'Error al subir';
    }
  } catch(e){ status.style.color='var(--red)'; status.textContent='Error de conexión'; }
  input.value='';
}

async function deleteBrandManual(){
  const clientId = currentClient; if(!clientId || clientId==='all') return;
  const fd = new FormData();
  fd.append('action','delete'); fd.append('client_id',clientId); fd.append('tipo','manual');
  const res = await fetch('api/brand-assets.php',{method:'POST',body:fd});
  const data = await res.json();
  if(data.ok){ resetBrandManual(); brandAssets.manual=''; }
}

async function extractLogoColors(){
  const clientId = currentClient;
  if(!clientId || clientId==='all'){ alert('Selecciona un cliente primero'); return; }
  const btn = document.getElementById('marca-extract-logo-btn');
  const status = document.getElementById('marca-extract-status');
  btn.disabled=true; btn.textContent='Analizando...'; status.textContent='';
  try {
    const fd = new FormData();
    fd.append('action','colors_from_logo'); fd.append('client_id',clientId);
    const res = await fetch('api/brand-extract.php',{method:'POST',body:fd});
    const data = await res.json();
    if(data.ok){
      document.getElementById('marca-colors').value = data.colors;
      renderColorSwatches();
      await saveBrandMeta();
      status.style.color='var(--g)'; status.textContent='✓ '+data.colors;
    } else {
      status.style.color='var(--red)'; status.textContent = data.error||'Error al extraer';
    }
  } catch(e){ status.style.color='var(--red)'; status.textContent='Error de conexión'; }
  btn.disabled=false; btn.textContent='✦ Extraer del logo';
}

async function extractBrandManual(){
  const clientId = currentClient; if(!clientId || clientId==='all') return;
  const btn = document.getElementById('marca-manual-extract-btn');
  const status = document.getElementById('marca-manual-status');
  btn.disabled=true; btn.textContent='Analizando...'; status.textContent='';
  try {
    const fd = new FormData();
    fd.append('action','extract_from_manual'); fd.append('client_id',clientId);
    const res = await fetch('api/brand-extract.php',{method:'POST',body:fd});
    const data = await res.json();
    if(data.ok){
      if(data.colors){ document.getElementById('marca-colors').value=data.colors; renderColorSwatches(); }
      if(data.fonts){ document.getElementById('marca-fonts').value=data.fonts; }
      if(data.brand_description){ document.getElementById('marca-description').value=data.brand_description; }
      await saveBrandMeta();
      status.style.color='var(--g)';
      status.textContent='✓ Identidad de marca extraída — colores, fuentes, tono, elementos y personalidad';
    } else {
      status.style.color='var(--red)'; status.textContent = data.error||'Error al analizar';
    }
  } catch(e){ status.style.color='var(--red)'; status.textContent='Error de conexión'; }
  btn.disabled=false; btn.textContent='✦ Analizar manual';
}

function renderColorSwatches(){
  const val = document.getElementById('marca-colors').value;
  const cont = document.getElementById('marca-color-swatches');
  const hexes = val.split(',').map(s=>s.trim()).filter(s=>/^#[0-9A-Fa-f]{3,6}$/.test(s));
  cont.innerHTML = hexes.map(h=>`<div style="width:28px;height:28px;border-radius:50%;background:${h};border:2px solid rgba(255,255,255,.15);title='${h}'" title="${h}"></div>`).join('');
}

async function uploadBrandAsset(tipo, input, slot) {
  const file = input.files[0];
  if(!file) return;
  const clientId = currentClient;
  if(!clientId || clientId==='all'){ alert('Selecciona un cliente primero'); return; }

  const action = tipo === 'logo' ? 'upload_logo' : 'upload_style';
  const fd = new FormData();
  fd.append('action', action);
  fd.append('client_id', clientId);
  fd.append('file', file);
  if(slot) fd.append('slot', slot);

  if(tipo==='logo') document.getElementById('marca-logo-status').textContent='Subiendo...';

  try {
    const res  = await fetch('api/brand-assets.php', {method:'POST', body:fd});
    const data = await res.json();
    if(data.ok){
      if(tipo==='logo'){
        showBrandLogo(data.path);
        document.getElementById('marca-logo-status').textContent='✓ Logo guardado';
        brandAssets.logo = data.path;
      } else {
        showBrandStyle(slot, data.path);
        brandAssets.styles[slot-1] = data.path;
      }
    } else {
      alert(data.error||'Error al subir');
    }
  } catch(e){ alert('Error de conexión'); }
  input.value='';
}

async function deleteBrandAsset(tipo){
  const clientId = currentClient;
  if(!clientId || clientId==='all') return;
  const fd = new FormData();
  fd.append('action','delete'); fd.append('client_id',clientId); fd.append('tipo',tipo);
  const res = await fetch('api/brand-assets.php',{method:'POST',body:fd});
  const data = await res.json();
  if(data.ok){
    if(tipo==='logo'){ resetBrandLogo(); brandAssets.logo=''; }
    else {
      const slot = parseInt(tipo.split('_')[1]);
      resetBrandStyle(slot);
      brandAssets.styles[slot-1]='';
    }
  }
}

async function saveBrandMeta(){
  const clientId = currentClient;
  if(!clientId || clientId==='all') return;
  const colors      = document.getElementById('marca-colors').value.trim();
  const fonts       = document.getElementById('marca-fonts').value.trim();
  const description = document.getElementById('marca-description').value.trim();
  const res = await fetch('api/brand-assets.php',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({action:'save_meta',client_id:clientId,colors,fonts,description})});
  const data = await res.json();
  if(data.ok){
    brandAssets.colors=colors; brandAssets.fonts=fonts; brandAssets.description=description;
    ['marca-meta-saved','marca-desc-saved'].forEach(id=>{
      const el=document.getElementById(id);
      if(el){ el.style.display='inline'; setTimeout(()=>el.style.display='none',2000); }
    });
  }
}

function getBrandContext(){
  const parts=[];
  if(brandAssets.colors)      parts.push('Colores de marca: '+brandAssets.colors);
  if(brandAssets.fonts)       parts.push('Tipografía: '+brandAssets.fonts);
  if(brandAssets.description) parts.push('Identidad de marca:\n'+brandAssets.description);
  return parts.join('\n');
}

// ——— META COPY ———
function generateMetaCopy(){
  const q=document.getElementById('meta-copy-q').value.trim();
  if(!q) return;
  const ctx=getContext('meta');
  callAVI(`Actúa como experto en Meta Ads para una agencia de marketing. Genera copy completo para anuncio: "${q}". Incluye: 3 opciones de TITULAR (máx 40 caracteres cada uno), 2 opciones de DESCRIPCIÓN (máx 125 caracteres), 3 opciones de TEXTO PRINCIPAL (máx 125 caracteres), y 3 opciones de CTA.`, ctx, 'meta-copy-resp', 'AVI generando copy...', 'meta');
}
function quickCopy(fmt){
  const prompts = {
    feed: 'Genera copy optimizado para formato Feed de Instagram/Facebook. Incluye 3 titulares (máx 40 chars), 2 descripciones (máx 125 chars) y 3 CTAs.',
    stories: 'Genera copy para Stories/Reels. Texto corto, impactante, para los primeros 3 segundos. Incluye hook, propuesta de valor y CTA.',
    reels: 'Genera guión corto para Reels de 15-30 segundos. Hook (0-3s), desarrollo (3-12s), CTA (12-15s). Texto en pantalla y narración.',
    carrusel: 'Genera copy para carrusel de 5 tarjetas. Tarjeta 1: hook/problema. Tarjetas 2-4: solución/beneficios. Tarjeta 5: CTA.',
    video_script: 'Genera guión completo de video publicitario de 30-60 segundos. Incluye: hook (0-3s), problema (3-8s), solución (8-20s), prueba social (20-35s), oferta y CTA (35-60s). Indica texto en pantalla, narración y música sugerida.',
    lineamientos_disenador: `Genera un brief creativo completo para el diseñador. Incluye: formato y dimensiones, paleta de colores, tipografía recomendada, elementos visuales sugeridos, texto en imagen (máx 20%), referencias de estilo, dos y no hacer, y todos los copies listos para usar.`,
  };
  document.getElementById('meta-copy-q').value = prompts[fmt] || `Genera copy para formato ${fmt}`;
  generateMetaCopy();
}

// ——— SEGMENTATION ———
function toggleSeg(el){
  el.classList.toggle('active');
}
function analyzeSegmentation(){
  const loc=document.getElementById('seg-location').value;
  const ageMin=document.getElementById('seg-age-min').value;
  const ageMax=document.getElementById('seg-age-max').value;
  const interests=document.getElementById('seg-interests').value;
  const budget=document.getElementById('seg-budget').value;
  const ctx=getContext('meta');
  const q=`Analiza esta segmentación para Meta Ads y sugiere mejoras: Ubicación: ${loc||'no especificada'}. Edad: ${ageMin||'?'}-${ageMax||'?'} años. Intereses: ${interests||'no especificados'}. Presupuesto diario: ${budget||'no especificado'} COP. Contexto: ${ctx}. Dame recomendaciones específicas de audiencias, segmentación detallada y estimado de alcance.`;
  callAVI(q, ctx, 'seg-resp', 'AVI analizando segmentación...', 'meta');
}

// ——— FORMAT SPECS ———
const fmtSpecs={
  feed:{name:'Feed / Imagen estática',specs:[['Resolución','1080×1080 px (cuadrado) o 1080×1350 px (vertical)'],['Formato','JPG o PNG'],['Tamaño máx','30 MB'],['Relación aspecto','1:1 o 4:5'],['Texto en imagen','Menos del 20% del área'],['Titular','Máx 40 caracteres'],['Descripción','Máx 125 caracteres'],['CTA','Recomendado siempre']]},
  stories:{name:'Stories e imágenes verticales',specs:[['Resolución','1080×1920 px'],['Formato','JPG, PNG o MP4'],['Duración video','Máx 15 segundos'],['Tamaño máx','30 MB (imagen) / 250 MB (video)'],['Zona segura','Mantener 14% superior e inferior libre de texto'],['Audio','Diseñar sin audio (80% se ve en silencio)'],['CTA','Deslizar hacia arriba o botón superpuesto']]},
  carousel:{name:'Carrusel (múltiples tarjetas)',specs:[['Tarjetas','2 a 10 tarjetas'],['Resolución por tarjeta','1080×1080 px'],['Formato','JPG, PNG o MP4'],['Tamaño máx por tarjeta','30 MB'],['Titular por tarjeta','Máx 40 caracteres'],['Descripción por tarjeta','Máx 20 caracteres'],['URL destino','Puede variar por tarjeta']]},
  video:{name:'Video en Feed',specs:[['Resolución mínima','1080×1080 px (cuadrado) o 1080×1350 px'],['Formato','MP4, MOV o GIF'],['Duración','1 segundo a 240 minutos'],['Tamaño máx','4 GB'],['Subtítulos','Muy recomendados (85% sin audio)'],['Miniatura','JPG o PNG, misma relación de aspecto'],['Los primeros 3 seg','Son cruciales — captura la atención de inmediato']]},
};
function selectFormat(f){
  document.querySelectorAll('.fmt-card').forEach(c=>c.classList.remove('active'));
  event.currentTarget.classList.add('active');
  const s=fmtSpecs[f];
  const det=document.getElementById('fmt-detail');
  det.className='fmt-detail show';
  det.innerHTML=`<div style="font-family:var(--fh);font-size:12px;font-weight:600;margin-bottom:8px;color:var(--b)">${s.name}</div>`+
    s.specs.map(([l,v])=>`<div class="spec-row"><span class="spec-label">${l}</span><span class="spec-val">${v}</span></div>`).join('');
}

// ——— CHECKLIST ———
function toggleCheck(el){ el.classList.toggle('done'); }

// ——— GOOGLE KEYWORDS ———
function generateKeywords(type){
  const prod=document.getElementById('kw-product').value;
  const loc=document.getElementById('kw-location').value;
  const ctx=getContext('google');
  const prompts={
    research:`Como experto en Google Ads y SEO, investiga y crea keywords para: "${prod}" en "${loc||'Colombia'}". Organiza en tabla con columnas: Keyword | Intención (transaccional/informacional/navegacional) | Volumen estimado (alto/medio/bajo) | Competencia | Tipo de concordancia recomendado. Incluye mínimo 20 keywords relevantes. Contexto: ${ctx}`,
    longtail:`Genera 25 keywords de cola larga (long tail) para: "${prod}" en "${loc||'Colombia'}". Enfócate en búsquedas muy específicas con alta intención de conversión y menor competencia. Formato: lista con keyword y su intención. Contexto: ${ctx}`,
    negative:`Genera una lista completa de keywords negativas para una campaña de "${prod}". Incluye términos que no deben activar los anuncios (búsquedas informacionales, competidores, términos irrelevantes). Agrupa por categoría. Contexto: ${ctx}`,
    groups:`Agrupa las keywords de "${prod}" en Ad Groups lógicos por tema. Para cada grupo incluye: nombre del Ad Group, 5-8 keywords principales, intención de búsqueda y copy sugerido de titular RSA. Contexto: ${ctx}`,
  };
  callAVI(prompts[type], ctx, 'kw-resp', 'AVI investigando keywords...', 'google');
}

// ══════════════════════════════════════════════════════════
// SEO AVANZADO — Schema, robots.txt, Contenido automatizado
// ══════════════════════════════════════════════════════════

let _seoArticleLen = 'long';
function setArticleLen(len) {
  _seoArticleLen = len;
  document.querySelectorAll('#seo-article-form .db').forEach(b => b.classList.remove('active'));
  event.currentTarget.classList.add('active');
}
function seoToggleArticleForm() {
  const f = document.getElementById('seo-article-form');
  if (f) f.style.display = f.style.display === 'none' ? 'block' : 'none';
}
function _seoClientUrl() {
  const c = currentClient && currentClient !== 'all' ? CLIENTS.find(x => x.id == currentClient) : null;
  return c?.url ? (c.url.startsWith('http') ? c.url : 'https://' + c.url) : '';
}
function _seoCopyBtn(respId) {
  return `<button onclick="seoCopyResp('${respId}')" style="position:absolute;top:8px;right:8px;padding:4px 10px;border-radius:5px;font-size:11px;background:var(--bg3);border:1px solid var(--br2);color:var(--m);cursor:pointer;font-family:var(--fb)">Copiar</button>`;
}
function seoCopyResp(respId) {
  const el = document.getElementById(respId);
  if (!el) return;
  navigator.clipboard.writeText(el.innerText || el.textContent).then(() => showToast('✓ Copiado al portapapeles', 'var(--g)'));
}
const _schemaTypeLabels = {
  auto:'negocio (auto-detectar tipo)', local:'negocio local', ecommerce:'tienda online',
  services:'empresa de servicios', blog:'blog o revista', medical:'clínica/salud', restaurant:'restaurante',
};
function runSchemaAdvanced() {
  const url  = document.getElementById('schema-url')?.value.trim() || _seoClientUrl();
  const type = document.getElementById('schema-type')?.value || 'auto';
  const ctx  = getContext('seo');
  if (!url) { showToast('Ingresa la URL del sitio', 'var(--y)'); return; }
  const prompt = `Experto SEO técnico. Analiza "${url}" tipo ${_schemaTypeLabels[type]||type}. Genera JSON-LD Schema.org completo: 1) Organization/LocalBusiness (nombre,URL,logo,redes) 2) WebSite con SearchAction 3) BreadcrumbList 4) Schema adicional según tipo (FAQPage, Product, MedicalOrganization, Restaurant, Service). Para cada schema: código válido listo para <head>, dónde insertar, obligatorios vs recomendados. Contexto: ${ctx}`;
  const el = document.getElementById('schema-adv-resp');
  if (el) { el.style.display='block'; el.innerHTML='<div style="color:var(--m);font-size:12px;padding:12px 0">Generando Schema.org completo...</div>'; }
  callAVI(prompt, ctx, 'schema-adv-resp', 'AVI generando Schema...', 'seo', () => { document.getElementById('schema-adv-resp')?.insertAdjacentHTML('beforeend', _seoCopyBtn('schema-adv-resp')); });
}
function seoQuickSchema(type) {
  const url = _seoClientUrl() || 'el sitio del cliente';
  const ctx = getContext('seo');
  const prompts = {
    faq:       `Schema FAQPage JSON-LD para "${url}". 8 preguntas frecuentes relevantes con respuestas detalladas. Código válido para Google Rich Results. Contexto: ${ctx}`,
    local:     `Schema LocalBusiness JSON-LD completo para "${url}": nombre, dirección, teléfono, horario, coordenadas, servicios, área de servicio, redes sociales. Contexto: ${ctx}`,
    breadcrumb:`Schema BreadcrumbList JSON-LD para "${url}". Jerarquía completa con ejemplos para Home, categorías y páginas de detalle. Contexto: ${ctx}`,
    product:   `Schema Product JSON-LD completo para un producto de "${url}": nombre, descripción, imagen, precio, disponibilidad, AggregateRating, marca. Contexto: ${ctx}`,
    review:       `Schema AggregateRating + Review JSON-LD para "${url}". Valoración agregada + 3 reseñas ejemplo. Cómo obtener estrellas en Google. Contexto: ${ctx}`,
    article:      `Schema Article JSON-LD para artículos de blog en "${url}": headline, author, datePublished, dateModified, image, publisher con logo. Incluye NewsArticle y BlogPosting. Código completo listo para <head>. Contexto: ${ctx}`,
    organization: `Schema Organization JSON-LD completo para "${url}": nombre, logo, URL, sameAs (redes sociales), contactPoint, address, founder. Código para Knowledge Panel en Google. Contexto: ${ctx}`,
  };
  const el = document.getElementById('schema-adv-resp');
  if (el) { el.style.display='block'; el.innerHTML='<div style="color:var(--m);font-size:12px;padding:12px 0">Generando schema...</div>'; }
  callAVI(prompts[type]||prompts.local, ctx, 'schema-adv-resp', 'AVI generando schema...', 'seo', () => { document.getElementById('schema-adv-resp')?.insertAdjacentHTML('beforeend', _seoCopyBtn('schema-adv-resp')); });
}
function runRobotsOptimizer() {
  const url = document.getElementById('robots-url')?.value.trim() || _seoClientUrl();
  const cms = document.getElementById('robots-cms')?.value || 'wordpress';
  const ctx = getContext('seo');
  if (!url) { showToast('Ingresa la URL del sitio', 'var(--y)'); return; }
  const cmsRules = {
    wordpress:'Bloquea /wp-admin/, /wp-login.php, /wp-json/, /?s=, /feed/, /tag/, /author/',
    shopify:'Bloquea /admin, /cart, /checkout, /orders, /account, variantes duplicadas',
    woocommerce:'Bloquea /wp-admin/, /cart/, /checkout/, /my-account/, parámetros filtro WooCommerce',
    prestashop:'Bloquea /administration/, /modules/, /themes/, parámetros de ordenación',
    custom:'Bloquea paneles admin, checkout, URLs con parámetros de tracking',
  };
  const prompt = `Experto SEO técnico. Robots.txt optimizado para "${url}" (CMS: ${cms}). ${cmsRules[cms]||cmsRules.custom}. Permite CSS/JS/imágenes necesarios. Sitemap: ${url}/sitemap.xml. Crawl-delay para bots no deseados. Entrega archivo completo entre bloques de código, luego explicación por sección y 3 errores comunes a evitar. Contexto: ${ctx}`;
  const el = document.getElementById('robots-resp');
  if (el) { el.style.display='block'; el.innerHTML='<div style="color:var(--m);font-size:12px;padding:12px 0">Generando robots.txt optimizado...</div>'; }
  callAVI(prompt, ctx, 'robots-resp', 'AVI generando robots.txt...', 'seo', () => { document.getElementById('robots-resp')?.insertAdjacentHTML('beforeend', _seoCopyBtn('robots-resp')); });
}
function runContentCalendar() {
  const ctx = getContext('seo');
  const kws = gscKeywordsList.slice(0,40).map(k => `${k.keyword} (pos:${Math.round(k.position||99)}, imp:${k.impressions||0}, ctr:${k.ctr||0}%)`).join('\n');
  const clientUrl = _seoClientUrl();
  const prompt = `Eres estratega de contenido SEO experto.${kws?`\n\nKeywords reales de Google Search Console:\n${kws}\n`:''}\nCrea un calendario de contenido SEO para los próximos 30 días${clientUrl?` para ${clientUrl}`:''}.

Criterios de priorización:
- 🔴 URGENTE: keywords en posición 4-10 con >500 impresiones y CTR<3% (título/meta mal optimizado)
- 🟡 IMPORTANTE: keywords posición 11-20 con >200 impresiones (artículo nuevo puede subir rápido)
- 🟢 PLANIFICADO: keywords emergentes o long-tail con oportunidad de volumen

Genera la tabla completa:
| Prioridad | Semana | Fecha sugerida | Tipo de contenido | Keyword principal | Título SEO (≤60 chars) | Keywords secundarias | Formato | Objetivo |

Tipos: Artículo blog · Landing de servicio · FAQ · Guía completa · Comparativa · Caso de éxito · Contenido evergreen

Después de la tabla, incluye:
**TOP 3 piezas más urgentes** — explica por qué cada una tiene mayor impacto inmediato
**KPIs para medir éxito** — qué métricas revisar a los 30, 60 y 90 días
**Tip de implementación** — cómo publicar y notificar a Google (Search Console, sitemap)

Contexto del negocio: ${ctx}`;
  const el = document.getElementById('content-auto-resp');
  if (el) { el.style.display='block'; el.innerHTML='<div style="color:var(--m);font-size:12px;padding:12px 0">Analizando GSC y generando calendario 30 días...</div>'; }
  callAVI(prompt, ctx, 'content-auto-resp', 'AVI generando calendario...', 'seo', () => { document.getElementById('content-auto-resp')?.insertAdjacentHTML('beforeend', _seoCopyBtn('content-auto-resp')); });
}
function runAutoArticle() {
  const kw  = document.getElementById('article-kw')?.value.trim();
  const ext = document.getElementById('article-extra')?.value.trim();
  const ctx = getContext('seo');
  if (!kw) { showToast('Escribe la keyword principal', 'var(--y)'); return; }
  const lenMap = { short:'450-600 palabras, 3 H2', medium:'900-1100 palabras, 5 H2', long:'1400-1800 palabras, 6-8 H2 con H3' };
  const kws = gscKeywordsList.slice(0,12).map(k=>k.keyword).join(', ');
  const prompt = `Redactor SEO experto. Keyword: "${kw}". Extensión: ${lenMap[_seoArticleLen]||lenMap.long}.${ext?` Instrucciones: ${ext}.`:''}${kws?` Keywords del sitio para incluir naturalmente: ${kws}.`:''}
ESTRUCTURA: 1)Meta title(≤60,keyword al inicio) 2)Meta description(≤155,keyword+CTA) 3)H1 atractivo 4)Intro con hook y keyword en primer párrafo 5)Cuerpo con H2/H3 y long-tail semánticas 6)Conclusión con CTA 7)Alt text para 3 imágenes.
Tono profesional y cercano, mercado hispanohablante. Contexto: ${ctx}`;
  const el = document.getElementById('content-auto-resp');
  if (el) { el.style.display='block'; el.innerHTML='<div style="color:var(--m);font-size:12px;padding:12px 0">Escribiendo artículo SEO completo...</div>'; }
  callAVI(prompt, ctx, 'content-auto-resp', 'AVI escribiendo artículo...', 'seo', () => { document.getElementById('content-auto-resp')?.insertAdjacentHTML('beforeend', _seoCopyBtn('content-auto-resp')); });
}
function runContentIdeas() {
  const ctx = getContext('seo');
  const kws = gscKeywordsList.slice(0,20).map(k=>k.keyword).join(', ');
  const clientUrl = _seoClientUrl();
  const prompt = `15 ideas de contenido SEO alto impacto${clientUrl?` para ${clientUrl}`:''}.${kws?` Keywords del sitio: ${kws}.`:''}
Cada idea: Título(≤60) · Tipo · Keyword principal+2-3 sec. · Intención · Dificultad · Por qué convierte.
Agrupa: 🔥 Urgente · 📅 Planificado · 🌿 Evergreen. Contexto: ${ctx}`;
  const el = document.getElementById('content-auto-resp');
  if (el) { el.style.display='block'; el.innerHTML='<div style="color:var(--m);font-size:12px;padding:12px 0">Generando ideas de contenido...</div>'; }
  callAVI(prompt, ctx, 'content-auto-resp', 'AVI generando ideas...', 'seo', () => { document.getElementById('content-auto-resp')?.insertAdjacentHTML('beforeend', _seoCopyBtn('content-auto-resp')); });
}

// ── Core Web Vitals / PageSpeed Insights ─────────────────
async function runPageSpeed(strategy) {
  const url = (document.getElementById('cwv-url')?.value || '').trim() || _seoClientUrl();
  if (!url) { showToast('Ingresa la URL del sitio', 'var(--y)'); return; }

  // Toggle strategy buttons
  ['mobile','desktop'].forEach(s => {
    const b = document.getElementById('cwv-btn-' + s);
    if (b) b.classList.toggle('active', s === strategy);
  });

  const loading = document.getElementById('cwv-loading');
  const result  = document.getElementById('cwv-result');
  const errEl   = document.getElementById('cwv-error');
  if (loading) loading.style.display = 'block';
  if (result)  result.style.display  = 'none';
  if (errEl)   errEl.style.display   = 'none';

  try {
    const res = await fetch('api/pagespeed.php', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ url, strategy, client_id: currentClient && currentClient !== 'all' ? parseInt(currentClient) : 0 })
    });
    const d = await res.json();
    if (loading) loading.style.display = 'none';

    if (!d.ok) {
      if (errEl) { errEl.textContent = '✗ ' + (d.error || 'Error al analizar'); errEl.style.display = 'block'; }
      return;
    }

    const r = d.result;
    r.strategy = strategy;
    renderCWVResult(r);

    const scoreColor = r.performance >= 90 ? '#22c97a' : r.performance >= 50 ? '#f5c543' : '#E8272A';
    const scoreLabel = r.performance >= 90 ? 'Bueno' : r.performance >= 50 ? 'Mejorable' : 'Pobre';
    if (result) result.style.display = 'block';
    showToast(`⚡ Performance ${r.performance} — ${scoreLabel}`, scoreColor, 4000);

  } catch(e) {
    if (loading) loading.style.display = 'none';
    if (errEl) { errEl.textContent = '✗ Error de conexión'; errEl.style.display = 'block'; }
  }
}

// ── Auto-init: rellena contexto real del cliente ─────────
function seoAdvancedInit() {
  const url = _seoClientUrl();
  const ctx = getContext('seo') || document.getElementById('seo-ctx')?.value || '';

  // Pre-llenar URLs (Schema, robots.txt, Core Web Vitals)
  ['schema-url','robots-url','cwv-url'].forEach(id => {
    const el = document.getElementById(id);
    if (el && !el.value && url) el.value = url;
  });

  // Auto-detectar CMS desde el contexto del negocio
  const cmsEl = document.getElementById('robots-cms');
  if (cmsEl && cmsEl.value === 'wordpress') {
    const ctxLow = ctx.toLowerCase();
    if (ctxLow.includes('shopify'))           cmsEl.value = 'shopify';
    else if (ctxLow.includes('woocommerce'))  cmsEl.value = 'woocommerce';
    else if (ctxLow.includes('prestashop'))   cmsEl.value = 'prestashop';
    else if (ctxLow.includes('wordpress') || ctxLow.includes('wp')) cmsEl.value = 'wordpress';
  }

  // Auto-detectar tipo de negocio para Schema desde contexto
  const typeEl = document.getElementById('schema-type');
  if (typeEl && typeEl.value === 'auto') {
    const ctxLow = ctx.toLowerCase();
    if (ctxLow.match(/tienda|ecommerce|e-commerce|producto|shopify|woocommerce/)) typeEl.value = 'ecommerce';
    else if (ctxLow.match(/clínica|médico|salud|dental|doctor|hospital|consultorio/)) typeEl.value = 'medical';
    else if (ctxLow.match(/restaurante|comida|menú|platos|delivery|bar/)) typeEl.value = 'restaurant';
    else if (ctxLow.match(/blog|revista|noticias|artículos|contenido/)) typeEl.value = 'blog';
    else if (ctxLow.match(/agencia|servicio|empresa|consultoría|estudio/)) typeEl.value = 'services';
    else if (ctxLow.match(/local|ciudad|barrio|dirección|sucursal/)) typeEl.value = 'local';
  }

  // Sugerir keyword para artículo: mejor oportunidad de GSC (pos 4-20, impresiones altas, CTR bajo)
  const kwEl = document.getElementById('article-kw');
  if (kwEl && !kwEl.value && gscKeywordsList.length) {
    const opp = gscKeywordsList
      .filter(k => k.position >= 4 && k.position <= 20 && (k.impressions || 0) > 50)
      .sort((a,b) => (b.impressions||0) - (a.impressions||0))[0];
    if (opp) kwEl.placeholder = `Sugerida: "${opp.keyword}" (pos.${Math.round(opp.position)}, ${opp.impressions} imp.)`;
  }
}

// ══════════════════════════════════════════════════════════

function analyzeBudget(){
  const budget=document.getElementById('g-budget').value;
  const ctx=getContext('google');
  const q=`Analiza y recomienda distribución de presupuesto de Google Ads de ${budget||'no especificado'} COP mensuales. Incluye: distribución por campaña/tipo, CPC estimado por keyword, ubicaciones geográficas recomendadas con mayor ROI, estacionalidad y ajustes de puja recomendados. Contexto: ${ctx}`;
  callAVI(q, ctx, 'budget-resp', 'AVI analizando presupuesto...', 'google');
}

function generateRSA(){
  const q=document.getElementById('rsa-q').value.trim();
  if(!q) return;
  const ctx=getContext('google');
  const prompt=`Crea un anuncio RSA (Responsive Search Ad) completo para Google Ads sobre: "${q}". Genera exactamente: 15 TITULARES (máx 30 caracteres cada uno, numerados) y 4 DESCRIPCIONES (máx 90 caracteres cada una, numeradas). Asegúrate de incluir la keyword principal en al menos 3 titulares. Marca con ⭐ los más fuertes. Contexto: ${ctx}`;
  callAVI(prompt, ctx, 'rsa-resp', 'AVI creando anuncio RSA...', 'google');
  document.getElementById('rsa-preview').style.display='block';
}

// ——— WIZARD NUEVA CAMPAÑA GOOGLE ADS ———
let gcwPlanData = null;
let gcwSelectedType = 'SEARCH';
let gcwSelectedGoal = 'LEADS';

function openGCWizard() {
  gcwPlanData = null;
  gcwSelectedType = 'SEARCH';
  gcwSelectedGoal = 'LEADS';
  document.getElementById('gcw-step1').style.display = 'block';
  document.getElementById('gcw-step2').style.display = 'none';
  document.getElementById('gcw-plan-loading').style.display = 'none';
  document.getElementById('gcw-brief').value = '';
  document.getElementById('gcw-url').value = '';
  document.querySelector('.gcw-btns-row').style.display = 'flex';
  document.getElementById('gc-wizard-modal').style.display = 'flex';
}

function closeGCWizard() {
  document.getElementById('gc-wizard-modal').style.display = 'none';
}

function selectGCWType(btn) {
  gcwSelectedType = btn.dataset.val;
  document.querySelectorAll('.gcw-type-btn').forEach(b => {
    b.style.borderColor = 'var(--br2)'; b.style.background = 'var(--bg3)'; b.style.color = 'var(--m)';
  });
  btn.style.borderColor = 'var(--red)'; btn.style.background = 'rgba(220,38,38,.1)'; btn.style.color = 'var(--t)';
}

function selectGCWGoal(btn) {
  gcwSelectedGoal = btn.dataset.val;
  document.querySelectorAll('.gcw-goal-btn').forEach(b => {
    b.style.borderColor = 'var(--br2)'; b.style.background = 'var(--bg3)'; b.style.color = 'var(--m)';
  });
  btn.style.borderColor = 'var(--red)'; btn.style.background = 'rgba(220,38,38,.1)'; btn.style.color = 'var(--t)';
}

async function runGCWizardPlan() {
  const brief = document.getElementById('gcw-brief').value.trim();
  const url   = document.getElementById('gcw-url').value.trim();
  if (!brief) { showToast('Escribe un brief para continuar', 'warn'); return; }
  const type = gcwSelectedType;
  const goal = gcwSelectedGoal;
  const btnsRow = document.querySelector('#gcw-step1 .gcw-btns-row');
  if (btnsRow) btnsRow.style.display = 'none';
  document.getElementById('gcw-plan-loading').style.display = 'block';
  try {
    const fd = new FormData();
    fd.append('action', 'ai_plan');
    fd.append('brief', brief);
    fd.append('type', type);
    fd.append('goal', goal);
    fd.append('url', url);
    fd.append('context', getContext('google'));
    if (currentClient && currentClient !== 'all') fd.append('client_id', currentClient);
    const res = await fetch('api/google-ads-create.php', { method:'POST', body: fd });
    const d = await res.json();
    if (d.error) throw new Error(d.error);
    gcwPlanData = { plan: d.plan || d, type, url };
    const preview = typeof gcwPlanData.plan === 'object'
      ? JSON.stringify(gcwPlanData.plan, null, 2)
      : String(gcwPlanData.plan);
    document.getElementById('gcw-plan-preview').textContent = preview;
    document.getElementById('gcw-step1').style.display = 'none';
    document.getElementById('gcw-step2').style.display = 'block';
  } catch(e) {
    showToast('Error generando plan: ' + e.message, 'error');
    if (btnsRow) btnsRow.style.display = 'flex';
  } finally {
    document.getElementById('gcw-plan-loading').style.display = 'none';
  }
}

async function createGCWizardFull() {
  if (!gcwPlanData) return;
  const loading = document.getElementById('gcw-create-loading');
  const result  = document.getElementById('gcw-create-result');
  loading.style.display = 'block';
  result.style.display  = 'none';
  try {
    const fd = new FormData();
    fd.append('action', 'create_full');
    fd.append('plan', JSON.stringify(gcwPlanData.plan));
    fd.append('type', gcwPlanData.type);
    fd.append('url', gcwPlanData.url);
    if (currentClient && currentClient !== 'all') fd.append('client_id', currentClient);
    const res = await fetch('api/google-ads-create.php', { method:'POST', body: fd });
    const d = await res.json();
    if (d.error) throw new Error(d.error);
    result.style.cssText = 'display:block;background:rgba(34,197,94,.12);border:1px solid rgba(34,197,94,.3);color:#22c55e;padding:12px;border-radius:8px;font-size:13px;margin-top:12px';
    result.textContent = '✓ Campaña creada. ID: ' + (d.campaign_id || d.id || 'ver Google Ads');
    showToast('Campaña creada en Google Ads', 'success');
    setTimeout(() => { closeGCWizard(); loadGCampaigns(); }, 2500);
  } catch(e) {
    result.style.cssText = 'display:block;background:rgba(248,113,113,.1);border:1px solid rgba(248,113,113,.3);color:#f87171;padding:12px;border-radius:8px;font-size:13px;margin-top:12px';
    result.textContent = '✗ Error: ' + e.message;
  } finally {
    loading.style.display = 'none';
  }
}

// ——— SEO TOOLS ———
function seoTool(type, name){
  currentSEOTool=type;
  document.querySelectorAll('#page-seo .fmt-card').forEach(c=>c.classList.remove('active'));
  event.currentTarget.classList.add('active');

  // Artículo SEO → panel dedicado
  if (type === 'seo-article') {
    const panel = document.getElementById('seo-article-panel');
    if (panel) {
      panel.style.display = 'block';
      panel.scrollIntoView({ behavior:'smooth', block:'start' });
      const badge = document.getElementById('art-gsc-badge');
      if (badge) badge.style.display = (currentClient && currentClient !== 'all') ? 'inline' : 'none';
    }
    return;
  }

  const phMap={
    'seo-audit':'URL del sitio a auditar (ej: https://cliente.com)',
    'seo-schema':'URL de la página y tipo de schema (FAQ, Article, LocalBusiness...)',
    'seo-sitemap':'Dominio del sitio (ej: cliente.com)',
    'seo-speed':'URL del sitio para diagnóstico de velocidad',
    'seo-content':'Tema del bloque de contenido y keyword objetivo',
    'seo-robots':'URL del sitio (ej: https://cliente.com) y CMS: WordPress / Shopify / otro',
  };
  document.getElementById('seo-tool-q').placeholder=phMap[type]||'Detalles adicionales...';
  const btn=document.getElementById('seo-tool-btn');
  btn.style.display='block';
  btn.textContent=name;
}

let _lastSEOArticleRaw = '';

async function runSEOArticle() {
  const keyword = (document.getElementById('art-keyword')?.value || '').trim();
  if (!keyword) { showToast('Ingresa una keyword principal', 'var(--y)'); return; }
  if (!currentClient || currentClient === 'all') { showToast('Selecciona un cliente primero', 'var(--y)'); return; }

  const btn = document.getElementById('btn-gen-article');
  const respEl = document.getElementById('seo-article-resp');
  const rendered = document.getElementById('seo-article-rendered');
  if (btn) { btn.disabled = true; btn.textContent = '⟳ Generando...'; }
  if (respEl) respEl.style.display = 'none';

  try {
    const _csrfToken = document.querySelector('meta[name="dm-csrf"]')?.content || '';
    const res = await fetch('api/seo-article.php', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json', 'X-CSRF-Token': _csrfToken },
      body: JSON.stringify({
        client_id: currentClient,
        keyword,
        type:      document.getElementById('art-type')?.value  || 'article',
        tone:      document.getElementById('art-tone')?.value  || 'professional',
        min_words: document.getElementById('art-words')?.value || 800,
        extra:     document.getElementById('art-extra')?.value || '',
      }),
    });
    const d = await res.json();

    if (!d.ok) {
      showToast('✗ ' + (d.error || 'Error generando artículo'), 'var(--red)');
      return;
    }

    _lastSEOArticleRaw = d.article || '';
    if (rendered) rendered.innerHTML = renderMD(_lastSEOArticleRaw);
    if (respEl) respEl.style.display = 'block';

    // Mostrar keywords GSC usadas
    const kwsPanel = document.getElementById('seo-article-kws');
    const kwsList  = document.getElementById('seo-article-kws-list');
    if (d.gsc_used && d.related_kws && d.related_kws.length && kwsPanel && kwsList) {
      kwsList.innerHTML = d.related_kws.slice(0,15).map(k =>
        `<span style="background:var(--bg3);border:1px solid var(--br2);border-radius:12px;padding:2px 8px;font-size:10px;color:var(--m)">${escapeHtml(k.query)} <span style="color:var(--g)">pos.${k.pos}</span></span>`
      ).join('');
      kwsPanel.style.display = 'block';
    }

    showToast('✓ Artículo generado correctamente', 'var(--g)');
    respEl.scrollIntoView({ behavior:'smooth', block:'start' });

  } catch(e) {
    showToast('✗ Error de conexión', 'var(--red)');
  } finally {
    if (btn) { btn.disabled = false; btn.textContent = '✦ Generar artículo'; }
  }
}

function copySEOArticle() {
  if (!_lastSEOArticleRaw) return;
  navigator.clipboard.writeText(_lastSEOArticleRaw)
    .then(() => showToast('✓ Artículo copiado al portapapeles', 'var(--g)'))
    .catch(() => showToast('✗ Error al copiar', 'var(--red)'));
}

function downloadSEOArticle() {
  if (!_lastSEOArticleRaw) return;
  const kw = (document.getElementById('art-keyword')?.value || 'articulo').trim().replace(/\s+/g,'-').toLowerCase();
  const blob = new Blob([_lastSEOArticleRaw], { type:'text/markdown' });
  const a = document.createElement('a');
  a.href = URL.createObjectURL(blob);
  a.download = `seo-${kw}-${new Date().toISOString().slice(0,10)}.md`;
  a.click();
  URL.revokeObjectURL(a.href);
}

function runSEOTool(){
  const input=document.getElementById('seo-tool-q').value.trim();
  const ctx=getContext('seo');
  const prompts={
    'seo-audit':`Haz una auditoría SEO completa para: "${input}". Analiza y reporta en formato estructurado: 1) Meta tags (title, description, h1-h6) 2) Velocidad y Core Web Vitals 3) Estructura de URLs 4) Contenido y densidad de keywords 5) Backlinks y autoridad 6) Schema markup 7) Mobile-friendly 8) Accesibilidad. Para cada punto da estado (✅/⚠️/❌) y recomendación. Contexto: ${ctx}`,
    'seo-article':`Crea un artículo SEO optimizado completo sobre: "${input}". Incluye: H1 optimizado, introducción atractiva, 5-7 secciones con H2/H3, palabras clave secundarias distribuidas naturalmente, párrafo de conclusión, meta description (máx 160 caracteres) y sugerencias de imágenes con alt text. Mínimo 800 palabras. Contexto: ${ctx}`,
    'seo-schema':`Genera el código JSON-LD de Schema.org para: "${input}". Incluye el código completo listo para insertar en el <head> del HTML, con todos los campos relevantes correctamente completados. Contexto: ${ctx}`,
    'seo-sitemap':`Genera un sitemap.xml completo y una estrategia de arquitectura de sitio para: "${input}". Incluye: estructura de URLs recomendada, prioridades (priority) y frecuencia de cambio (changefreq) por tipo de página, y el XML completo listo para subir. Contexto: ${ctx}`,
    'seo-speed':`Analiza y proporciona un plan de optimización de velocidad para: "${input}". Cubre: Core Web Vitals (LCP, FID/INP, CLS), optimización de imágenes, caché, minificación, CDN, lazy loading, y pasos específicos en WordPress si aplica. Prioriza por impacto. Contexto: ${ctx}`,
    'seo-content':`Crea bloques de contenido SEO optimizados para: "${input}". Genera: 1) Sección FAQ con 8 preguntas y respuestas (listas para Schema FAQ) 2) Tabla comparativa de características/precios 3) Fragmento destacado (Featured Snippet) optimizado 4) 3 ideas de contenido adicional para el blog. Contexto: ${ctx}`,
    'seo-robots':`Genera un robots.txt optimizado para: "${input}". Detecta el CMS desde el input, bloquea paneles admin, URLs con parámetros innecesarios, feeds duplicados. Permite CSS/JS/imágenes. Añade sitemap.xml. Entrega el archivo completo en bloque de código, luego explicación sección por sección. Contexto: ${ctx}`,
  };
  callAVI(prompts[currentSEOTool]||(currentSEOTool+': '+input), ctx, 'seo-tool-resp', 'AVI ejecutando herramienta SEO...', 'seo');
}

// ——— CONFIG ———
function saveCfg(svc){
  const el=document.getElementById('s-'+svc);
  el.style.color='var(--y)'; el.textContent='Guardando...';
  const data={service:svc};
  if(svc==='claude') data.key=document.getElementById('claude-key').value;
  if(svc==='meta'){data.app_id=document.getElementById('meta-app').value;data.token=document.getElementById('meta-tok').value;}
  if(svc==='google'){
    data.token     = document.getElementById('g-tok').value;
    data.oauth_cid = document.getElementById('g-oauth-cid').value;
    data.oauth_cs  = document.getElementById('g-oauth-cs').value;
    data.mcc_id    = document.getElementById('g-mcc-id').value;
  }
  fetch('api/save-config.php',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify(data)})
    .then(r=>r.json()).then(res=>{el.style.color=res.ok?'var(--g)':'var(--red)';el.textContent=res.ok?'✓ Guardado':'✗ '+(res.error||'Error');})
    .catch(()=>{el.style.color='var(--y)';el.textContent='⚠ Sube api/save-config.php';});
}

function saveMailchimpCreds(){
  const el  = document.getElementById('s-mailchimp');
  const key = (document.getElementById('mailchimp-key').value||'').trim();
  if (!currentClient || currentClient==='all') { el.style.color='var(--y)'; el.textContent='Selecciona un cliente primero'; return; }
  if (!key) { el.style.color='var(--y)'; el.textContent='Ingresa la API key'; return; }
  el.style.color='var(--m)'; el.textContent='Guardando...';
  fetchJSON('api/save-credentials.php',{client_id:currentClient,service:'mailchimp',fields:{api_key:key}})
    .then(r=>{ el.style.color=r.ok?'var(--g)':'var(--red)'; el.textContent=r.ok?'✓ Guardado para cliente '+currentClient:'✗ '+(r.error||'Error'); })
    .catch(()=>{ el.style.color='var(--red)'; el.textContent='Error de red'; });
}

// WP creds desde panel SEO → Contenido
async function saveWPCredsSEO() {
  const st = document.getElementById('wp-save-status-seo');
  const badge = document.getElementById('wp-status-badge');
  const url  = (document.getElementById('wp-url-seo')?.value  || '').trim();
  const user = (document.getElementById('wp-user-seo')?.value || '').trim();
  const pass = (document.getElementById('wp-pass-seo')?.value || '').trim().replace(/\s+/g,'');
  if (!currentClient || currentClient==='all') { if(st){st.style.color='var(--y)';st.textContent='Selecciona un cliente primero';} return; }
  if (!url || !user || !pass) { if(st){st.style.color='var(--y)';st.textContent='Completa los 3 campos';} return; }
  if (st) { st.style.color='var(--m)'; st.textContent='Guardando...'; }
  try {
    const r = await fetchJSON('api/save-credentials.php', {client_id:currentClient, service:'wordpress', fields:{wp_url:url,wp_user:user,wp_app_password:pass}});
    if (r.ok) {
      if (st) { st.style.color='var(--g)'; st.textContent='✓ WordPress conectado'; }
      if (badge) { badge.style.background='rgba(34,201,122,.1)'; badge.style.border='1px solid rgba(34,201,122,.3)'; badge.style.color='var(--g)'; badge.textContent='✓ Conectado'; }
    } else {
      if (st) { st.style.color='var(--red)'; st.textContent='✗ '+(r.error||'Error'); }
    }
  } catch(e) { if(st){st.style.color='var(--red)';st.textContent='Error de red';} }
}

// Verificar estado WP al entrar al tab contenido
async function checkWPStatus() {
  const badge = document.getElementById('wp-status-badge');
  const form  = document.getElementById('wp-connect-form');
  if (!badge || !currentClient || currentClient==='all') return;
  try {
    const r = await fetch(`api/get-credentials.php?client_id=${currentClient}`).then(x=>x.json());
    const wp = r.credentials?.wordpress || {};
    if (wp.wp_url && wp.wp_user && wp.wp_app_password) {
      badge.style.background='rgba(34,201,122,.1)'; badge.style.border='1px solid rgba(34,201,122,.3)'; badge.style.color='var(--g)'; badge.textContent='✓ Conectado: '+wp.wp_url;
      // Pre-fill campos con URL guardada (sin mostrar password)
      const urlEl=document.getElementById('wp-url-seo'); if(urlEl) urlEl.value=wp.wp_url;
      const userEl=document.getElementById('wp-user-seo'); if(userEl) userEl.value=wp.wp_user;
    } else {
      badge.style.background='rgba(250,204,21,.1)'; badge.style.border='1px solid rgba(250,204,21,.25)'; badge.style.color='var(--y)'; badge.textContent='Sin configurar';
    }
  } catch(e) {}
}

function saveWPCreds(){
  const el=document.getElementById('s-wordpress');
  const url=(document.getElementById('wp-url')?.value||'').trim();
  const user=(document.getElementById('wp-user')?.value||'').trim();
  const pass=(document.getElementById('wp-app-pass')?.value||'').trim().replace(/\s+/g,'');
  if(!currentClient||currentClient==='all'){el.style.color='var(--y)';el.textContent='Selecciona un cliente primero';return;}
  if(!url||!user||!pass){el.style.color='var(--y)';el.textContent='Completa URL, usuario y Application Password';return;}
  el.style.color='var(--m)';el.textContent='Guardando...';
  fetchJSON('api/save-credentials.php',{client_id:currentClient,service:'wordpress',fields:{wp_url:url,wp_user:user,wp_app_password:pass}})
    .then(r=>{el.style.color=r.ok?'var(--g)':'var(--red)';el.textContent=r.ok?'✓ WordPress configurado':'✗ '+(r.error||'Error');})
    .catch(()=>{el.style.color='var(--red)';el.textContent='Error de red';});
}

// ——— CHARTS ———
function initChart(type){
  setTimeout(()=>{
    if(type==='main'&&!charts.main){charts.main=new Chart(document.getElementById('chartMain'),{type:'bar',data:{labels:MONTHS,datasets:[{data:[12.1,13.4,14.8,15.2,16.1,17.4,18.4],backgroundColor:'rgba(232,39,42,.38)',borderColor:'#E8272A',borderWidth:1.5,borderRadius:4},{data:[52.3,58.7,65.2,67.4,71.3,78.9,86.5],type:'line',borderColor:'#22c97a',backgroundColor:'rgba(34,201,122,.07)',fill:true,tension:.4,pointRadius:3}]},options:{responsive:true,maintainAspectRatio:false,plugins:{legend:{display:false}},scales:{x:{ticks:{color:'rgba(240,240,240,.4)',font:{size:10}},grid:{color:'rgba(255,255,255,.04)'}},y:{ticks:{color:'rgba(240,240,240,.4)',font:{size:10},callback:v=>'$'+v+'M'},grid:{color:'rgba(255,255,255,.04)'}}}}});}
    if(type==='donut'&&!charts.donut){charts.donut=new Chart(document.getElementById('chartDonut'),{type:'doughnut',data:{labels:['Meta','Google','SEO','Web'],datasets:[{data:[42,35,15,8],backgroundColor:['#4a9ef8','#f87171','#22c97a','#f5c543'],borderWidth:0}]},options:{responsive:false,cutout:'72%',plugins:{legend:{display:false}}}});}
    if(type==='meta'){charts.meta=new Chart(document.getElementById('chartMeta')??document.createElement('canvas'),{type:'line',data:{labels:MONTHS,datasets:[{data:[1.1,1.3,1.5,1.4,1.6,1.8,1.9],borderColor:'#4a9ef8',backgroundColor:'rgba(74,158,248,.07)',tension:.4,fill:true,pointRadius:3}]},options:{responsive:true,maintainAspectRatio:false,plugins:{legend:{display:false}},scales:{x:{ticks:{color:'rgba(240,240,240,.4)',font:{size:10}},grid:{color:'rgba(255,255,255,.04)'}},y:{ticks:{color:'rgba(240,240,240,.4)',font:{size:10},callback:v=>'$'+v+'M'},grid:{color:'rgba(255,255,255,.04)'}}}}});}
    if(type==='seo'){charts.seo=new Chart(document.getElementById('chartSEO'),{type:'line',data:{labels:MONTHS,datasets:[{data:[52,57,61,66,71,78,84],borderColor:'#22c97a',backgroundColor:'rgba(34,201,122,.09)',tension:.4,fill:true,pointRadius:3}]},options:{responsive:true,maintainAspectRatio:false,plugins:{legend:{display:false}},scales:{x:{ticks:{color:'rgba(240,240,240,.4)',font:{size:10}},grid:{color:'rgba(255,255,255,.04)'}},y:{ticks:{color:'rgba(240,240,240,.4)',font:{size:10},callback:v=>v+'K'},grid:{color:'rgba(255,255,255,.04)'}}}}});}
  },50);
}

// ——— PROPUESTAS DE DISEÑO ———
let currentProposalId = null;
let pendingLogoPath = null;
let selectedStyleKeys  = {1:'moderno_bold', 2:'corporativo_premium', 3:'startup_colorido'};
let selectedStyleNames = {1:'Moderno Bold', 2:'Corporativo Premium', 3:'Startup Colorido'};

function switchWebTab(id, el) {
  document.querySelectorAll('.web-panel').forEach(p => p.style.display = 'none');
  document.querySelectorAll('.web-tab').forEach(t => {
    t.style.color = 'var(--m)';
    t.style.borderBottomColor = 'transparent';
  });
  const panel = document.getElementById('wpanel-' + id);
  if (panel) panel.style.display = 'block';
  if (el) { el.style.color = 'var(--t)'; el.style.borderBottomColor = 'var(--red)'; }
  if (id === 'propuestas'  && currentClient && currentClient !== 'all') loadProposalsList();
  if (id === 'competencia' && currentClient && currentClient !== 'all') loadCompHistory();
  if (id === 'shopify'     && currentClient && currentClient !== 'all') loadShopifyData(currentClient);
  if (id === 'wordpress'   && currentClient && currentClient !== 'all') loadWPData(currentClient);
  if (id === 'general'     && currentClient && currentClient !== 'all') loadWebOverview(currentClient);
}

// ── WEB OVERVIEW (Tab General): Uptime + SSL + PageSpeed ─────────────────────
function loadWebOverview(clientId) {
  if (!clientId || clientId === 'all') {
    const nc = document.getElementById('web-no-client');
    if (nc) nc.style.display = 'block';
    return;
  }
  const nc = document.getElementById('web-no-client');
  if (nc) nc.style.display = 'none';
  loadWebUptime(false, clientId);
  loadWebSSL(false, clientId);
  loadWebPageSpeed(false, clientId);
}

async function loadWebUptime(force, clientId) {
  const cid = clientId || currentClient;
  if (!cid || cid === 'all') return;
  const el = document.getElementById('web-uptime-content');
  if (!el) return;
  el.innerHTML = '<div style="font-size:12px;color:var(--m)">Verificando...</div>';
  try {
    const r = await fetchJSON(`api/uptime-check.php?client_id=${cid}${force?'&force=1':''}`);
    if (!r.ok) {
      el.innerHTML = `<div style="font-size:12px;color:var(--m)">${r.error || 'No configurado'}</div>`;
      return;
    }
    const statusColor = r.is_up ? 'var(--g)' : 'var(--red)';
    const latencyColor = r.latency_ms < 800 ? 'var(--g)' : r.latency_ms < 2000 ? 'var(--y)' : 'var(--red)';
    const uptimeColor  = r.uptime_pct >= 99 ? 'var(--g)' : r.uptime_pct >= 95 ? 'var(--y)' : 'var(--red)';
    el.innerHTML = `
      <div style="display:flex;align-items:center;gap:8px;margin-bottom:8px">
        <span style="font-size:22px;font-weight:800;color:${statusColor}">${r.uptime_pct}%</span>
        <span style="font-size:11px;padding:2px 7px;border-radius:10px;font-weight:700;background:${r.is_up?'rgba(34,201,122,.15)':'rgba(232,39,42,.15)'};color:${statusColor}">${r.is_up?'EN LÍNEA':'CAÍDO'}</span>
      </div>
      <div style="font-size:11px;color:var(--m);margin-bottom:4px">Latencia: <span style="color:${latencyColor};font-weight:600">${r.latency_ms} ms</span></div>
      <div style="font-size:11px;color:var(--m)">HTTP ${r.status_code}</div>
      <div style="font-size:10px;color:var(--m);margin-top:6px;word-break:break-all">${r.url}</div>`;
  } catch(e) {
    el.innerHTML = '<div style="font-size:12px;color:var(--m)">Error al verificar</div>';
  }
}

async function loadWebSSL(force, clientId) {
  const cid = clientId || currentClient;
  if (!cid || cid === 'all') return;
  const el = document.getElementById('web-ssl-content');
  if (!el) return;
  el.innerHTML = '<div style="font-size:12px;color:var(--m)">Verificando...</div>';
  try {
    const r = await fetchJSON(`api/ssl-check.php?client_id=${cid}${force?'&force=1':''}`);
    if (!r.ok) {
      el.innerHTML = `<div style="font-size:12px;color:var(--m)">${r.error || 'No disponible'}</div>`;
      return;
    }
    const clr = r.status === 'ok' ? 'var(--g)' : r.status === 'warning' ? 'var(--y)' : 'var(--red)';
    const icon = r.status === 'ok' ? '✓' : r.status === 'warning' ? '⚠' : '✗';
    el.innerHTML = `
      <div style="display:flex;align-items:center;gap:8px;margin-bottom:8px">
        <span style="font-size:22px;font-weight:800;color:${clr}">${r.days_left}d</span>
        <span style="font-size:11px;padding:2px 7px;border-radius:10px;font-weight:700;background:${clr === 'var(--g)'?'rgba(34,201,122,.15)':clr==='var(--y)'?'rgba(245,197,67,.15)':'rgba(232,39,42,.15)'};color:${clr}">${icon} ${r.days_left > 30 ? 'VÁLIDO' : r.days_left > 7 ? 'VENCE PRONTO' : 'CRÍTICO'}</span>
      </div>
      <div style="font-size:11px;color:var(--m);margin-bottom:2px">Emisor: <span style="color:var(--t)">${r.issuer}</span></div>
      <div style="font-size:11px;color:var(--m);margin-bottom:2px">Válido hasta: <span style="color:var(--t)">${r.valid_to}</span></div>
      <div style="font-size:11px;color:var(--m)">Dominio: <span style="color:var(--t)">${r.host}</span></div>`;
  } catch(e) {
    el.innerHTML = '<div style="font-size:12px;color:var(--m)">Error al verificar SSL</div>';
  }
}

async function loadWebPageSpeed(force, clientId) {
  const cid = clientId || currentClient;
  if (!cid || cid === 'all') return;
  // Get URL from WP credentials to auto-fill
  const el = document.getElementById('web-ps-content');
  if (!el) return;
  el.innerHTML = '<div style="font-size:12px;color:var(--m)">Obteniendo URL del sitio...</div>';

  // First get the URL from WP credentials
  let siteUrl = '';
  try {
    const wr = await fetchJSON(`api/wp-data.php?client_id=${cid}&action=overview`);
    if (wr.ok && wr.url) siteUrl = wr.url;
  } catch(e) {}

  if (!siteUrl) {
    el.innerHTML = `<div style="font-size:12px;color:var(--m)">Configura WordPress para análisis automático.</div>
      <div style="margin-top:8px;display:flex;gap:6px;align-items:center">
        <input id="web-ps-url" type="text" placeholder="https://cliente.com" style="flex:1;padding:5px 8px;border-radius:6px;border:1px solid var(--br2);background:var(--bg2);color:var(--t);font-size:11px;font-family:var(--fb)">
        <button onclick="runWebPageSpeed()" style="padding:5px 10px;border-radius:6px;font-size:11px;background:var(--red);border:none;color:#fff;cursor:pointer;font-family:var(--fb)">Analizar</button>
      </div>`;
    return;
  }

  el.innerHTML = '<div style="font-size:12px;color:var(--m)">Analizando velocidad... (~15s)</div>';
  try {
    const [mobile, desktop] = await Promise.all([
      fetchJSON(`api/pagespeed.php?url=${encodeURIComponent(siteUrl)}&strategy=mobile&client_id=${cid}`),
      fetchJSON(`api/pagespeed.php?url=${encodeURIComponent(siteUrl)}&strategy=desktop&client_id=${cid}`),
    ]);

    const scoreHtml = (label, score) => {
      if (!score && score !== 0) return '';
      const s = parseInt(score);
      const clr = s >= 90 ? '#22c97a' : s >= 50 ? '#f5c543' : '#e8272a';
      return `<div style="text-align:center">
        <div style="width:44px;height:44px;border-radius:50%;border:3px solid ${clr};display:flex;align-items:center;justify-content:center;font-size:14px;font-weight:800;color:${clr};margin:0 auto 4px">${s}</div>
        <div style="font-size:10px;color:var(--m)">${label}</div>
      </div>`;
    };

    const mScore = mobile?.categories?.performance?.score != null ? Math.round(mobile.categories.performance.score * 100) : null;
    const dScore = desktop?.categories?.performance?.score != null ? Math.round(desktop.categories.performance.score * 100) : null;

    el.innerHTML = `
      <div style="display:flex;gap:16px;justify-content:center;margin-bottom:8px">
        ${scoreHtml('📱 Móvil', mScore)}
        ${scoreHtml('🖥 Desktop', dScore)}
      </div>
      <div style="font-size:10px;color:var(--m);text-align:center;word-break:break-all">${siteUrl}</div>
      <div style="text-align:center;margin-top:8px">
        <button onclick="switchWebTab('general',document.querySelector('.web-tab'));showPage('seo')" style="font-size:11px;background:none;border:none;color:var(--b);cursor:pointer;text-decoration:underline">Ver análisis completo →</button>
      </div>`;
  } catch(e) {
    el.innerHTML = '<div style="font-size:12px;color:var(--m)">Error al analizar velocidad</div>';
  }
}

async function runWebPageSpeed() {
  const urlEl = document.getElementById('web-ps-url');
  const url   = urlEl ? urlEl.value.trim() : '';
  if (!url) return;
  const el = document.getElementById('web-ps-content');
  if (el) el.innerHTML = '<div style="font-size:12px;color:var(--m)">Analizando... (~15s)</div>';
  try {
    const [mobile, desktop] = await Promise.all([
      fetchJSON(`api/pagespeed.php?url=${encodeURIComponent(url)}&strategy=mobile&client_id=${currentClient||0}`),
      fetchJSON(`api/pagespeed.php?url=${encodeURIComponent(url)}&strategy=desktop&client_id=${currentClient||0}`),
    ]);
    const scoreHtml = (label, score) => {
      if (!score && score !== 0) return '';
      const s = parseInt(score);
      const clr = s >= 90 ? '#22c97a' : s >= 50 ? '#f5c543' : '#e8272a';
      return `<div style="text-align:center"><div style="width:44px;height:44px;border-radius:50%;border:3px solid ${clr};display:flex;align-items:center;justify-content:center;font-size:14px;font-weight:800;color:${clr};margin:0 auto 4px">${s}</div><div style="font-size:10px;color:var(--m)">${label}</div></div>`;
    };
    const mScore = mobile?.categories?.performance?.score != null ? Math.round(mobile.categories.performance.score * 100) : null;
    const dScore = desktop?.categories?.performance?.score != null ? Math.round(desktop.categories.performance.score * 100) : null;
    if (el) el.innerHTML = `<div style="display:flex;gap:16px;justify-content:center;margin-bottom:8px">${scoreHtml('📱 Móvil', mScore)}${scoreHtml('🖥 Desktop', dScore)}</div><div style="font-size:10px;color:var(--m);text-align:center;word-break:break-all">${url}</div>`;
  } catch(e) {
    if (el) el.innerHTML = '<div style="font-size:12px;color:var(--m)">Error al analizar</div>';
  }
}

// ── WORDPRESS (Tab WordPress) ─────────────────────────────────────────────────
async function loadWPData(clientId, force) {
  const cid = clientId || currentClient;
  if (!cid || cid === 'all') return;

  const overviewEl = document.getElementById('wp-overview-content');
  const postsEl    = document.getElementById('wp-posts-content');
  const noCreds    = document.getElementById('wp-no-creds');

  if (overviewEl) overviewEl.innerHTML = '<div style="font-size:12px;color:var(--m);text-align:center;padding:12px">Cargando...</div>';
  if (postsEl)    postsEl.innerHTML    = '<div style="font-size:12px;color:var(--m);text-align:center;padding:12px">Cargando...</div>';

  const [overviewR, postsR] = await Promise.all([
    fetchJSON(`api/wp-data.php?client_id=${cid}&action=overview${force?'&force=1':''}`).catch(()=>({ok:false})),
    fetchJSON(`api/wp-data.php?client_id=${cid}&action=posts${force?'&force=1':''}`).catch(()=>({ok:false})),
  ]);

  // ── Overview
  if (!overviewR.ok) {
    if (noCreds) noCreds.style.display = 'block';
    if (overviewEl) overviewEl.innerHTML = `<div style="font-size:12px;color:var(--m)">${overviewR.error || 'No disponible'}</div>`;
  } else {
    if (noCreds) noCreds.style.display = 'none';
    const o = overviewR;
    overviewEl.innerHTML = `
      <div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(140px,1fr));gap:10px">
        <div><div style="font-size:10px;color:var(--m);text-transform:uppercase;letter-spacing:.5px;margin-bottom:3px">Sitio</div><div style="font-size:13px;font-weight:700;color:var(--t)">${o.name||'—'}</div><div style="font-size:11px;color:var(--m)">${o.description||''}</div></div>
        <div><div style="font-size:10px;color:var(--m);text-transform:uppercase;letter-spacing:.5px;margin-bottom:3px">Versión WP</div><div style="font-size:13px;font-weight:700;color:var(--t)">${o.wp_version||'—'}</div></div>
        <div><div style="font-size:10px;color:var(--m);text-transform:uppercase;letter-spacing:.5px;margin-bottom:3px">Tema activo</div><div style="font-size:13px;font-weight:700;color:var(--t)">${o.theme||'—'}</div></div>
        <div><div style="font-size:10px;color:var(--m);text-transform:uppercase;letter-spacing:.5px;margin-bottom:3px">Posts</div><div style="font-size:13px;font-weight:700;color:var(--t)">${o.total_posts??'—'}</div></div>
        <div><div style="font-size:10px;color:var(--m);text-transform:uppercase;letter-spacing:.5px;margin-bottom:3px">Páginas</div><div style="font-size:13px;font-weight:700;color:var(--t)">${o.total_pages??'—'}</div></div>
      </div>`;
  }

  // ── Posts
  if (!postsR.ok || !postsR.posts?.length) {
    if (postsEl) postsEl.innerHTML = `<div style="font-size:12px;color:var(--m);padding:8px 0">${postsR.error || 'Sin posts recientes'}</div>`;
  } else {
    const countEl = document.getElementById('wp-posts-count');
    if (countEl) countEl.textContent = `${postsR.posts.length} recientes`;
    const statusBadge = s => {
      const colors = {publish:'rgba(34,201,122,.15)',draft:'rgba(245,197,67,.15)',private:'rgba(155,127,244,.15)'};
      const textColors = {publish:'var(--g)',draft:'var(--y)',private:'var(--p)'};
      return `<span style="font-size:10px;padding:1px 6px;border-radius:8px;background:${colors[s]||'var(--bg3)'};color:${textColors[s]||'var(--m)'};font-weight:600">${s}</span>`;
    };
    postsEl.innerHTML = `<div style="display:flex;flex-direction:column;gap:0">` +
      postsR.posts.map(p => `
        <div style="display:flex;align-items:center;gap:10px;padding:9px 0;border-bottom:1px solid var(--br)">
          <div style="flex:1;min-width:0">
            <div style="font-size:12px;font-weight:600;color:var(--t);white-space:nowrap;overflow:hidden;text-overflow:ellipsis">${p.title}</div>
            <div style="font-size:11px;color:var(--m);margin-top:2px">${p.date}</div>
          </div>
          <div style="display:flex;align-items:center;gap:6px;flex-shrink:0">
            ${statusBadge(p.status)}
            ${p.link ? `<a href="${p.link}" target="_blank" style="font-size:11px;color:var(--b);text-decoration:none" title="Ver post">↗</a>` : ''}
          </div>
        </div>`).join('') + `</div>`;
  }
}

async function loadWPPlugins(clientId) {
  const cid = clientId || currentClient;
  if (!cid || cid === 'all') return;
  const el    = document.getElementById('wp-plugins-content');
  const badge = document.getElementById('wp-plugins-badge');
  if (el) el.innerHTML = '<div style="font-size:12px;color:var(--m);text-align:center;padding:12px">Cargando plugins...</div>';
  try {
    const r = await fetchJSON(`api/wp-data.php?client_id=${cid}&action=plugins`);
    if (!r.ok) {
      if (el) el.innerHTML = `<div style="font-size:12px;color:var(--m);padding:8px 0">${r.error}</div>`;
      return;
    }
    if (badge) {
      badge.style.display = 'inline';
      if (r.updates > 0) {
        badge.style.background = 'rgba(232,39,42,.15)';
        badge.style.color = 'var(--red)';
        badge.textContent = `⚠ ${r.updates} actualizaciones`;
      } else {
        badge.style.background = 'rgba(34,201,122,.15)';
        badge.style.color = 'var(--g)';
        badge.textContent = `✓ Al día (${r.total})`;
      }
    }
    if (!r.plugins?.length) {
      if (el) el.innerHTML = '<div style="font-size:12px;color:var(--m);padding:8px 0">Sin plugins encontrados.</div>';
      return;
    }
    // Sort: needs_update first, then by name
    const sorted = [...r.plugins].sort((a,b) => (b.needs_update?1:0)-(a.needs_update?1:0) || a.name.localeCompare(b.name));
    el.innerHTML = `<div style="overflow-x:auto"><table class="tbl" style="width:100%">
      <thead><tr><th>Plugin</th><th>Versión</th><th>Estado</th><th>Actualización</th></tr></thead>
      <tbody>${sorted.map(p => `
        <tr style="${p.needs_update?'background:rgba(232,39,42,.04)':''}">
          <td style="font-size:12px;font-weight:${p.needs_update?'700':'400'};color:${p.needs_update?'var(--t)':'var(--m)'}">${p.name}</td>
          <td style="font-size:11px;color:var(--m)">${p.version}</td>
          <td><span style="font-size:10px;padding:1px 6px;border-radius:8px;background:${p.status==='active'?'rgba(34,201,122,.15)':'var(--bg3)'};color:${p.status==='active'?'var(--g)':'var(--m)'}">${p.status}</span></td>
          <td>${p.needs_update ? `<span style="font-size:11px;font-weight:700;color:var(--red)">→ ${p.new_version}</span>` : '<span style="font-size:11px;color:var(--g)">✓</span>'}</td>
        </tr>`).join('')}
      </tbody></table></div>`;
  } catch(e) {
    if (el) el.innerHTML = '<div style="font-size:12px;color:var(--m)">Error al cargar plugins</div>';
  }
}

function webAVIquick(question, respId) {
  const resp = document.getElementById(respId || 'avi-resp-web');
  if (!resp) return;
  resp.style.display = 'block';
  resp.innerHTML = '<span style="color:var(--m);font-size:12px">AVI analizando...</span>';
  const q = document.getElementById('web-q');
  if (q && !respId) q.value = question;
  const ctx = `[Web General — Uptime: ${document.getElementById('web-uptime-content')?.innerText?.slice(0,80)||'N/A'} | SSL: ${document.getElementById('web-ssl-content')?.innerText?.slice(0,80)||'N/A'} | PageSpeed: ${document.getElementById('web-ps-content')?.innerText?.slice(0,80)||'N/A'} | WP: ${document.getElementById('wp-overview-content')?.innerText?.slice(0,120)||'N/A'}]`;
  callAVI(question + '\n\nContexto actual del sitio: ' + ctx, resp);
}

function loadProposalsList() {
  if (!currentClient || currentClient === 'all') return;
  const el = document.getElementById('proposals-list');
  if (!el) return;
  el.innerHTML = '<div style="text-align:center;color:var(--m);padding:16px;font-size:12px">Cargando...</div>';
  fetch('api/proposals.php?action=list&client_id=' + currentClient)
    .then(r => r.json())
    .then(data => {
      if (!data.ok || data.proposals.length === 0) {
        el.innerHTML = '<div style="text-align:center;color:var(--m);padding:20px;font-size:13px">Sin propuestas aún. Crea la primera.</div>';
        return;
      }
      const statusLabels = {draft:'Borrador',generating:'Generando...',ready:'Lista',sent:'Enviada al cliente',voted:'Cliente votó',final:'Versión final ✓'};
      const statusColors = {draft:'var(--m)',generating:'var(--y)',ready:'var(--g)',sent:'var(--b)',voted:'var(--red)',final:'var(--p)'};
      const timeAgo = iso => {
        if (!iso) return null;
        const s = Math.floor((Date.now() - new Date(iso)) / 1000);
        if (s < 60)  return 'hace ' + s + 's';
        if (s < 3600) return 'hace ' + Math.floor(s/60) + 'min';
        if (s < 86400) return 'hace ' + Math.floor(s/3600) + 'h';
        return 'hace ' + Math.floor(s/86400) + 'd';
      };
      el.innerHTML = data.proposals.map(p => {
        const viewed = timeAgo(p.last_viewed_at);
        const viewedBadge = viewed ? `<span title="Cliente abrió el link ${viewed}" style="font-size:10px;padding:2px 7px;border-radius:10px;background:rgba(74,158,248,.1);color:var(--b)">👁 ${viewed}</span>` : '';
        const finalBadge = p.last_final_at ? `<span title="Versión final generada ${timeAgo(p.last_final_at)}" style="font-size:10px;padding:2px 7px;border-radius:10px;background:rgba(155,127,244,.15);color:var(--p)">✦ final</span>` : '';
        return `
        <div style="display:flex;align-items:center;gap:10px;padding:10px 12px;background:var(--bg3);border-radius:8px;margin-bottom:6px;cursor:pointer" onclick="loadProposalPreview(${p.id})">
          <div style="flex:1;min-width:0">
            <div style="font-size:13px;font-weight:500;white-space:nowrap;overflow:hidden;text-overflow:ellipsis">${p.project_name}</div>
            <div style="font-size:11px;color:var(--m);margin-top:2px">${new Date(p.created_at).toLocaleDateString('es-CO')}</div>
          </div>
          <div style="display:flex;gap:5px;align-items:center;flex-shrink:0">
            ${viewedBadge}${finalBadge}
            ${p.client_vote > 0 ? '<span style="font-size:10px;padding:2px 7px;border-radius:10px;background:rgba(232,39,42,.1);color:var(--red)">Votó: ' + p.client_vote + '</span>' : ''}
            <span style="font-size:10px;padding:2px 7px;border-radius:10px;background:rgba(255,255,255,.05);color:${statusColors[p.status]||'var(--m)'}">${statusLabels[p.status]||p.status}</span>
            <button onclick="event.stopPropagation();loadProposalPreview(${p.id})" style="padding:4px 10px;border-radius:5px;font-size:11px;background:rgba(74,158,248,.1);border:1px solid rgba(74,158,248,.2);color:var(--b);cursor:pointer;font-family:var(--fb)">Ver →</button>
          </div>
        </div>`;
      }).join('');
    }).catch(() => { el.innerHTML = '<div style="color:var(--red);padding:12px;font-size:12px">Error cargando propuestas</div>'; });
}

function showProposalForm() {
  document.getElementById('proposal-form').style.display = 'block';
  document.getElementById('proposals-preview').style.display = 'none';
  document.getElementById('prop-id').value = '';
  document.getElementById('prop-name').value = '';
  const sv = id => { const e = document.getElementById(id); if(e) e.value = ''; };
  ['prop-brief','prop-brief-hidden','prop-structure','prop-refs','prop-colors',
   'prop-fonts','prop-notes','prop-goal','prop-competitors-urls','prop-form-msg'].forEach(sv);
  document.querySelectorAll('#goal-tags .stag.active, #integration-tags .stag.active').forEach(t => t.classList.remove('active'));
  const ca = document.getElementById('colors-auto'); if(ca) ca.style.display = 'none';
  const lp = document.getElementById('logo-preview'); if(lp) lp.innerHTML = '';
  if (typeof setBriefMode === 'function') setBriefMode('upload');
  currentProposalId = null;
  // Pre-llenar brand assets del cliente activo
  if (brandAssets.colors) { const c = document.getElementById('prop-colors'); if(c) c.value = brandAssets.colors; }
  if (brandAssets.fonts)  { const f = document.getElementById('prop-fonts');  if(f) f.value = brandAssets.fonts;  }
  if (brandAssets.logo) {
    const lp = document.getElementById('logo-preview');
    if (lp) lp.innerHTML = '<img src="' + brandAssets.logo + '" style="height:40px;border-radius:6px;border:1px solid var(--br2)"><span style="font-size:11px;color:var(--g);margin-left:6px">✓ Logo de Activos de Marca</span>';
  }
}

function hideProposalForm() {
  document.getElementById('proposal-form').style.display = 'none';
}

async function uploadLogoFile(input) {
  if (!input.files || !input.files[0]) return;
  const file = input.files[0];
  const preview = document.getElementById('logo-preview');

  if (!currentClient || currentClient === 'all' || isNaN(parseInt(currentClient))) {
    showToast('Selecciona un cliente primero', 'var(--red)'); return;
  }

  // Preview local inmediato
  const reader = new FileReader();
  reader.onload = e => {
    if (preview) preview.innerHTML = '<img src="' + e.target.result + '" style="height:50px;border-radius:6px;border:1px solid var(--br2)">';
  };
  reader.readAsDataURL(file);

  // Subir directo — sin requerir proposal_id
  const formData = new FormData();
  formData.append('logo', file);
  formData.append('client_id', currentClient);
  formData.append('proposal_id', currentProposalId || 0);
  formData.append('type', 'logo');

  try {
    const r = await fetch('api/proposals.php?action=upload', {method:'POST', body: formData});
    const data = await r.json();
    if (data.ok) {
      if (data.colors && data.colors.length > 0) {
        document.getElementById('colors-auto').style.display = 'block';
        document.getElementById('colors-manual').style.display = 'none';
        const swatches = document.getElementById('colors-swatches');
        if (swatches) swatches.innerHTML = data.colors.map(c =>
          '<div style="display:flex;align-items:center;gap:5px">' +
          '<div style="width:28px;height:28px;border-radius:5px;background:' + c + ';border:1px solid rgba(255,255,255,.1)"></div>' +
          '<span style="font-size:11px;color:var(--m)">' + c + '</span></div>'
        ).join('');
        document.getElementById('prop-colors').value = data.colors.join(', ');
        showToast('✓ Colores extraídos del logo', 'var(--g)');
      } else {
        showToast('✓ Logo subido', 'var(--g)');
      }
      // Guardar path — si la propuesta ya existe actualiza BD, si no guarda pendiente
      if (data.path) {
        pendingLogoPath = data.path;
        if (currentProposalId) {
          await fetch('api/proposals.php?action=save', {
            method:'POST', headers:{'Content-Type':'application/json'},
            body: JSON.stringify({id: currentProposalId, client_id: parseInt(currentClient),
              project_name: document.getElementById('prop-name')?.value || 'Sin nombre',
              logo_path: data.path})
          });
        }
      }
    } else {
      showToast('✗ Error subiendo logo: ' + (data.error||''), 'var(--red)');
    }
  } catch(e) {
    showToast('✗ Error de conexión al subir logo', 'var(--red)');
  }
}


async function uploadManualFile(input) {
  if (!input.files || !input.files[0]) return;
  if (!currentClient || currentClient === 'all' || isNaN(parseInt(currentClient))) {
    showToast('Selecciona un cliente primero', 'var(--red)'); return;
  }
  const file = input.files[0];
  const status = document.getElementById('manual-status');
  if (status) { status.style.color = 'var(--y)'; status.textContent = 'Subiendo manual...'; }

  try {
    if (!currentProposalId) {
      const id = await saveProposal();
      if (!id) {
        if (status) { status.style.color = 'var(--red)'; status.textContent = '✗ Guarda la propuesta primero'; }
        return;
      }
    }

    const formData = new FormData();
    formData.append('brand_manual', file);
    formData.append('client_id', currentClient);
    formData.append('proposal_id', currentProposalId || 0);
    formData.append('type', 'brand_manual');

    const r = await fetch('api/proposals.php?action=upload', {method:'POST', body: formData});
    if (!r.ok) throw new Error('HTTP ' + r.status);
    const data = await r.json();

    if (data.ok) {
      if (status) { status.style.color = 'var(--g)'; status.textContent = '✓ Manual de marca subido — AVI lo usará como referencia'; }
      showToast('✓ Manual de marca subido', 'var(--g)');
    } else {
      if (status) { status.style.color = 'var(--red)'; status.textContent = '✗ ' + (data.error || 'Error al subir'); }
    }
  } catch(e) {
    if (status) { status.style.color = 'var(--red)'; status.textContent = '✗ Error de conexión'; }
  }
}

function updateColors() {
  const c1 = document.getElementById('color1').value;
  const c2 = document.getElementById('color2').value;
  const c3 = document.getElementById('color3').value;
  const c4 = document.getElementById('color4').value;
  document.getElementById('prop-colors').value = c1 + ', ' + c2 + ', ' + c3 + ', ' + c4;
}

function setBriefMode(mode) {
  const uploadArea = document.getElementById('brief-upload-area');
  const writeArea  = document.getElementById('brief-write-area');
  const btnUpload  = document.getElementById('brief-mode-upload');
  const btnWrite   = document.getElementById('brief-mode-write');
  if (mode === 'upload') {
    uploadArea.style.display = 'block';
    writeArea.style.display  = 'none';
    btnUpload.style.background = 'var(--rl)'; btnUpload.style.borderColor = 'var(--rb)'; btnUpload.style.color = 'var(--red)';
    btnWrite.style.background  = 'var(--bg3)'; btnWrite.style.borderColor  = 'var(--br2)'; btnWrite.style.color  = 'var(--m)';
  } else {
    uploadArea.style.display = 'none';
    writeArea.style.display  = 'block';
    btnWrite.style.background  = 'var(--rl)'; btnWrite.style.borderColor  = 'var(--rb)'; btnWrite.style.color  = 'var(--red)';
    btnUpload.style.background = 'var(--bg3)'; btnUpload.style.borderColor = 'var(--br2)'; btnUpload.style.color = 'var(--m)';
  }
}

async function uploadBriefFile(input) {
  if (!input.files || !input.files[0]) return;
  const file = input.files[0];
  const status = document.getElementById('brief-file-status');
  const nameEl = document.getElementById('brief-filename');
  if (nameEl) nameEl.textContent = file.name;

  if (!currentClient || currentClient === 'all' || isNaN(parseInt(currentClient))) {
    if (status) { status.style.color = 'var(--red)'; status.textContent = '✗ Selecciona un cliente primero'; }
    return;
  }

  if (status) { status.style.color = 'var(--y)'; status.textContent = 'Leyendo archivo...'; }

  const formData = new FormData();
  formData.append('brief_file', file);
  formData.append('client_id', currentClient);

  try {
    const r = await fetch('api/proposals.php?action=upload_brief', {method:'POST', body: formData});
    if (!r.ok) throw new Error('HTTP ' + r.status);
    const data = await r.json();
    if (data.ok) {
      const hidden = document.getElementById('prop-brief-hidden');
      if (hidden) hidden.value = data.text || '';
      applyBriefUrls(data.urls || []);
      if (status) {
        if (data.text && data.text.length > 50) {
          status.style.color = 'var(--g)';
          status.textContent = '✓ Brief cargado: ' + file.name + ' (' + data.text.length + ' chars)';
          // Preview del texto extraído
          let prev = document.getElementById('brief-text-preview');
          if (!prev) {
            prev = document.createElement('div');
            prev.id = 'brief-text-preview';
            prev.style.cssText = 'margin-top:8px;background:var(--bg3);border:1px solid var(--br2);border-radius:8px;padding:10px;font-size:11px;color:var(--m);max-height:100px;overflow-y:auto;white-space:pre-wrap';
            document.getElementById('brief-upload-area').appendChild(prev);
          }
          prev.textContent = data.text.substring(0, 500) + (data.text.length > 500 ? '...' : '');
        } else {
          status.style.color = 'var(--y)';
          status.textContent = '⚠ Archivo subido pero sin texto extraído. Usa "Escribir" para ingresar el brief.';
          setBriefMode('write');
        }
      }
    } else {
      if (status) { status.style.color = 'var(--red)'; status.textContent = '✗ ' + (data.error || 'Error al leer'); }
    }
  } catch(e) {
    if (status) { status.style.color = 'var(--red)'; status.textContent = '✗ Error de conexión (' + e.message + ')'; }
  }
}


async function findCompetitors() {
  const project = document.getElementById('prop-name')?.value || '';
  const brief   = document.getElementById('prop-brief-hidden')?.value || document.getElementById('prop-brief')?.value || '';
  const status  = document.getElementById('comp-status');
  if (!project) { status.style.color='var(--y)'; status.textContent='Escribe el nombre del proyecto primero'; return; }

  status.style.color = 'var(--y)'; status.textContent = 'Buscando competidores...';

  try {
    const r = await fetch('api/competitive_analysis.php?action=find', {
      method: 'POST',
      headers: {'Content-Type':'application/json'},
      body: JSON.stringify({client_id: parseInt(currentClient), project, industry: '', description: brief.substring(0,500)})
    });
    const data = await r.json();
    if (checkAuth(data)) { status.style.color='var(--y)'; status.textContent='Sesión expirada'; return; }

    if (data.ok && data.competitors) {
      const urls = data.competitors.map(c => c.url).join('\n');
      const el = document.getElementById('prop-competitors-urls');
      if (el) el.value = urls;
      status.style.color = 'var(--g)';
      status.textContent = '✓ ' + data.competitors.length + ' competidores identificados';
      document.getElementById('comp-analysis-wrap').style.display = 'block';
      showToast('✓ Competidores encontrados', 'var(--g)');
    } else {
      status.style.color = 'var(--red)'; status.textContent = '✗ No se pudieron identificar competidores';
      if (data.error === 'SIN_CREDITOS') showSinCreditosModal();
    }
  } catch(e) {
    status.style.color = 'var(--red)'; status.textContent = '✗ Error de conexión';
  }
}

async function analyzeCompetitors() {
  const urlsText = document.getElementById('prop-competitors-urls')?.value || '';
  const urls = urlsText.split('\n').filter(u => u.trim().startsWith('http')).map(u => ({name: u.trim(), url: u.trim()}));
  if (!urls.length) { showToast('Agrega URLs de competidores primero', 'var(--y)'); return; }

  const status = document.getElementById('comp-status');
  status.style.color = 'var(--y)'; status.textContent = 'Analizando competidores con AVI...';

  try {
  const r = await fetch('api/competitive_analysis.php?action=analyze', {
    method: 'POST',
    headers: {'Content-Type':'application/json'},
    body: JSON.stringify({
      client_id: parseInt(currentClient),
      proposal_id: currentProposalId || 0,
      competitors: urls,
      project: document.getElementById('prop-name')?.value || '',
      industry: '',
    })
  });
  const data = await r.json();
  if (checkAuth(data)) { status.style.color='var(--y)'; status.textContent='Sesión expirada'; return; }

  if (data.ok) {
    const result = document.getElementById('comp-analysis-result');
    if (result) {
      result.style.display = 'block';
      result.style.color = 'var(--t)';
      result.innerHTML = data.analysis.replace(/\n/g, '<br>');
    }
    status.style.color = 'var(--g)'; status.textContent = '✓ Análisis completado';
    showToast('✓ Análisis de competencia listo', 'var(--g)');
  } else {
    status.style.color = 'var(--red)'; status.textContent = '✗ Error en análisis';
    if (data.error === 'SIN_CREDITOS') showSinCreditosModal();
  }
  } catch(e) {
    status.style.color = 'var(--red)'; status.textContent = '✗ Error de conexión';
  }
}

function buildExtraNotes() {
  const goals        = [...document.querySelectorAll('#goal-tags .stag.active')].map(t => t.textContent).join(', ');
  const integrations = [...document.querySelectorAll('#integration-tags .stag.active')].map(t => t.textContent).join(', ');
  const goal_extra   = document.getElementById('prop-goal')?.value || '';
  const notes        = document.getElementById('prop-notes')?.value || '';
  const diferencial  = document.getElementById('comp-diferencial')?.value || '';
  const valor        = document.getElementById('comp-valor')?.value || '';
  const publico      = document.getElementById('comp-publico')?.value || '';
  const ubicacion    = document.getElementById('comp-ubicacion')?.value || '';
  const comp_urls    = document.getElementById('prop-competitors-urls')?.value || '';
  const comp_analysis = document.getElementById('comp-analysis-result')?.textContent || '';

  let extra = '';
  if (goals)        extra += 'Objetivo: ' + goals + (goal_extra ? ' — ' + goal_extra : '') + '\n';
  if (integrations) extra += 'Integraciones: ' + integrations + '\n';
  if (diferencial)  extra += 'Diferencial: ' + diferencial + '\n';
  if (valor)        extra += 'Propuesta de valor: ' + valor + '\n';
  if (publico)      extra += 'Publico objetivo: ' + publico + '\n';
  if (ubicacion)    extra += 'Ubicacion clientes: ' + ubicacion + '\n';
  if (notes)        extra += 'Notas: ' + notes + '\n';
  if (comp_urls)    extra += 'Competidores: ' + comp_urls.replace(/\n/g, ', ') + '\n';
  if (comp_analysis) extra += 'Analisis competencia (asesoria): ' + comp_analysis.substring(0, 800) + '\n';
  return extra.trim();
}

// Detectar y autocompletar URLs del brief subido
function applyBriefUrls(urls) {
  if (!urls || urls.length === 0) return;
  const refsEl = document.getElementById('prop-refs');
  if (!refsEl) return;
  const existing = refsEl.value.trim();
  const newUrls = urls.filter(u => !existing.includes(u));
  if (newUrls.length > 0) {
    refsEl.value = (existing ? existing + '\n' : '') + newUrls.join('\n');
    showToast('✓ ' + newUrls.length + ' referentes extraídos del brief', 'var(--g)');
  }
}

async function saveProposal() {
  const name = document.getElementById('prop-name').value.trim();
  if (!name) { showProposalMsg('El nombre del proyecto es obligatorio', 'var(--red)'); return; }
  const payload = {
    action: 'save',
    client_id: parseInt(currentClient),
    id: parseInt(document.getElementById('prop-id').value) || 0,
    project_name: name,
    brief: document.getElementById('prop-brief-hidden')?.value || document.getElementById('prop-brief')?.value || '',
    structure: document.getElementById('prop-structure').value,
    references_urls: document.getElementById('prop-refs').value,
   replicate_url: document.getElementById('prop-replicate')?.value || '',
   brand_colors: document.getElementById('prop-colors')?.value || '',
   brand_fonts: document.getElementById('prop-fonts')?.value || '',
    extra_notes: buildExtraNotes(),
  };
  // Incluir logo pendiente si el logo fue subido antes de guardar por primera vez
  if (pendingLogoPath) { payload.logo_path = pendingLogoPath; pendingLogoPath = null; }
  try {
    const r = await fetch('api/proposals.php?action=save', {method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify(payload)});
    if (!r.ok) throw new Error('HTTP ' + r.status);
    const data = await r.json();
    if (checkAuth(data)) { showProposalMsg('Sesión expirada — redirigiendo...', 'var(--y)'); return null; }
    if (data.ok) {
      currentProposalId = data.id;
      document.getElementById('prop-id').value = data.id;
      showProposalMsg('✓ Guardado', 'var(--g)');
      loadProposalsList();
      return data.id;
    } else {
      showProposalMsg('✗ ' + (data.error || 'Error al guardar'), 'var(--red)');
      return null;
    }
  } catch(e) {
    showProposalMsg('✗ Error de conexión', 'var(--red)');
    return null;
  }
}

function showProposalMsg(msg, color) {
  const el = document.getElementById('prop-form-msg');
  if (el) { el.style.color = color; el.textContent = msg; }
}

async function saveAndGenerate() {
  const id = await saveProposal();
  if (!id) return;

  document.getElementById('proposal-form').style.display = 'none';
  document.getElementById('proposals-preview').style.display = 'block';

  const stmt = document.getElementById('proposals-preview-title');
  const grid = document.getElementById('previews-grid');
  grid.innerHTML = '';

  // Paso 1: Claude elige los 3 mejores estilos para este brief
  if (stmt) stmt.textContent = 'AVI está eligiendo los 3 estilos perfectos para este cliente...';
  try {
    const projectName = document.getElementById('prop-name')?.value || '';
    const briefVal    = document.getElementById('prop-brief-hidden')?.value || document.getElementById('prop-brief')?.value || '';
    const extraVal    = buildExtraNotes();
    const selR = await fetch('api/proposals.php?action=select_styles', {
      method: 'POST',
      headers: {'Content-Type':'application/json'},
      body: JSON.stringify({project_name: projectName, brief: briefVal, extra_notes: extraVal})
    });
    if (selR.ok) {
      const selData = await selR.json();
      if (selData.ok && selData.styles && selData.styles.length === 3) {
        selData.styles.forEach((st, i) => {
          selectedStyleKeys[i+1]  = st.key;
          selectedStyleNames[i+1] = st.name;
        });
        if (selData.overall) showToast('AVI: ' + selData.overall, 'var(--b)', 6000);
      }
    }
  } catch(e) { /* usar keys por defecto si falla */ }

  // Paso 2: crear las 3 cards con los nombres reales elegidos por Claude
  for (let s = 1; s <= 3; s++) {
    const catLabels = {1:'Oscuro', 2:'Claro', 3:'Colorido'};
    grid.innerHTML += `
      <div id="preview-card-${s}" style="background:var(--bg3);border:1px solid var(--br);border-radius:10px;overflow:hidden">
        <div style="padding:12px 14px;border-bottom:1px solid var(--br)">
          <div style="font-size:10px;color:var(--red);text-transform:uppercase;letter-spacing:.7px">Propuesta ${s} · ${catLabels[s]}</div>
          <div id="preview-style-name-${s}" style="font-size:13px;font-weight:600;margin-top:2px">${selectedStyleNames[s]}</div>
        </div>
        <div id="preview-${s}" style="height:320px;display:flex;align-items:center;justify-content:center;flex-direction:column;gap:8px">
          <div style="width:24px;height:24px;border:3px solid var(--br2);border-top-color:var(--red);border-radius:50%;animation:spin .8s linear infinite"></div>
          <div style="font-size:12px;color:var(--m);margin-top:4px">Diseñando con AVI...</div>
        </div>
      </div>`;
  }

  // Paso 3: generar las 3 propuestas en secuencia (evita rate limit 8k tokens/min)
  const projName = document.getElementById('prop-name')?.value || '';
  if (stmt) stmt.textContent = 'AVI generando propuesta 1 de 3...';
  await generateStyle(id, 1, selectedStyleKeys[1]);
  if (stmt) stmt.textContent = 'AVI generando propuesta 2 de 3...';
  await generateStyle(id, 2, selectedStyleKeys[2]);
  if (stmt) stmt.textContent = 'AVI generando propuesta 3 de 3...';
  await generateStyle(id, 3, selectedStyleKeys[3]);
  if (stmt) stmt.textContent = '✓ 3 propuestas listas — ' + projName;
}

async function generateStyle(id, style, style_key) {
  const previewEl = document.getElementById('preview-' + style);
  const sk = style_key || selectedStyleKeys[style] || null;
  try {
    const r = await fetch('api/proposals.php?action=generate', {
      method: 'POST',
      headers: {'Content-Type':'application/json'},
      body: JSON.stringify({id, style, style_key: sk})
    });
    if (!r.ok) throw new Error('HTTP ' + r.status);
    const data = await r.json();

    if (checkAuth(data)) {
      if (previewEl) previewEl.innerHTML = '<div style="color:var(--y);font-size:12px;padding:20px;text-align:center">⚠ Sesión expirada — recarga la página e inicia sesión de nuevo.</div>';
      return;
    }

    if (data.ok && data.html) {
      const blob = new Blob([data.html], {type:'text/html'});
      const url  = URL.createObjectURL(blob);
      proposalBlobUrls[style] = url;
      if (previewEl) {
        previewEl.innerHTML = `
          <div style="position:relative;width:100%;height:100%">
            <iframe src="${url}" style="width:1280px;height:${320/0.25}px;transform:scale(0.25);transform-origin:top left;border:none" scrolling="no"></iframe>
            <div style="position:absolute;inset:0;cursor:pointer" data-open="${style}"></div>
          </div>`;
        previewEl.querySelector('[data-open]')?.addEventListener('click', () => openProposalFull(style));
      }
      const card = document.getElementById('preview-card-' + style);
      if (card) {
        const actions = document.createElement('div');
        actions.style.cssText = 'padding:10px 14px;display:flex;gap:6px;border-top:1px solid var(--br)';
        const btnVer = document.createElement('button');
        btnVer.style.cssText = 'flex:1;padding:6px;border-radius:6px;font-size:11px;background:rgba(74,158,248,.1);border:1px solid rgba(74,158,248,.2);color:var(--b);cursor:pointer;font-family:var(--fb)';
        btnVer.textContent = 'Ver completa';
        btnVer.addEventListener('click', () => openProposalFull(style));
        const btnDl = document.createElement('button');
        btnDl.style.cssText = 'padding:6px 10px;border-radius:6px;font-size:11px;background:var(--bg3);border:1px solid var(--br2);color:var(--m);cursor:pointer;font-family:var(--fb)';
        btnDl.textContent = '↓';
        btnDl.addEventListener('click', () => downloadProposal(style));
        actions.appendChild(btnVer);
        actions.appendChild(btnDl);
        card.appendChild(actions);
      }
    } else {
      if (previewEl) previewEl.innerHTML = '<div style="color:var(--red);font-size:12px;padding:20px;text-align:center">✗ ' + (data.error || 'Error generando propuesta ' + style) + '</div>';
      if (data.error === 'SIN_CREDITOS') showSinCreditosModal();
    }
  } catch(e) {
    if (previewEl) previewEl.innerHTML = '<div style="color:var(--y);font-size:12px;padding:20px;text-align:center">⚠ Error de conexión o sesión expirada — recarga la página.</div>';
  }
}

const proposalBlobUrls = {};

function openProposalFull(style) {
  const url = proposalBlobUrls[style];
  if (url) window.open(url, '_blank');
}

function downloadProposal(style) {
  if (!currentProposalId) return;
  fetch('api/proposals.php?action=get&id=' + currentProposalId)
    .then(r => r.json())
    .then(data => {
      if (!data.ok) return;
      const html = data.proposal['proposal_' + style];
      if (!html) return;
      const blob = new Blob([html], {type:'text/html'});
      const a = document.createElement('a');
      a.href = URL.createObjectURL(blob);
      a.download = 'propuesta-' + style + '-' + (data.proposal.project_name || 'diseno').replace(/\s+/g,'-') + '.html';
      a.click();
    });
}

async function loadProposalPreview(id) {
  currentProposalId = id;
  finalBlobUrlAdmin = null;
  document.getElementById('proposals-preview').style.display = 'block';
  document.getElementById('proposal-form').style.display = 'none';

  const r = await fetch('api/proposals.php?action=get&id=' + id);
  const data = await r.json();
  if (!data.ok) return;

  const p = data.proposal;
  const stmt = document.getElementById('proposals-preview-title');
  if (stmt) stmt.textContent = p.project_name + (p.client_vote > 0 ? ' — Cliente eligió propuesta ' + p.client_vote : '');

  const grid = document.getElementById('previews-grid');
  grid.innerHTML = '';

  const styleNames = {1: p.proposal_1_name, 2: p.proposal_2_name, 3: p.proposal_3_name};

  for (let style = 1; style <= 3; style++) {
    const html = p['proposal_' + style];
    const isVoted = p.client_vote == style;
    const styleName = (styleNames[style] || '').replace(/</g,'&lt;').replace(/>/g,'&gt;');
    const btnVer  = html ? `<button data-open="${style}" style="flex:1;padding:6px;border-radius:6px;font-size:11px;background:rgba(74,158,248,.1);border:1px solid rgba(74,158,248,.2);color:var(--b);cursor:pointer;font-family:var(--fb)">Ver</button>` : '';
    const btnEdit = html ? `<button onclick="openEditor(${id},${style})" style="padding:6px 10px;border-radius:6px;font-size:11px;background:rgba(155,127,244,.1);border:1px solid rgba(155,127,244,.3);color:var(--p);cursor:pointer;font-family:var(--fb)">✎</button>` : '';
    const btnDl   = html ? `<button onclick="downloadProposal(${style})" style="padding:6px 10px;border-radius:6px;font-size:11px;background:var(--bg3);border:1px solid var(--br2);color:var(--m);cursor:pointer;font-family:var(--fb)">↓</button>` : '';
    const btnRegen = html
      ? `<button onclick="regenStyle(${id},${style},this)" title="Regenerar" style="padding:6px 10px;border-radius:6px;font-size:11px;background:rgba(245,197,67,.1);border:1px solid rgba(245,197,67,.3);color:var(--y);cursor:pointer;font-family:var(--fb)">↺</button>`
      : `<button onclick="regenStyle(${id},${style},this)" style="flex:1;padding:6px;border-radius:6px;font-size:11px;background:var(--rl);border:1px solid var(--rb);color:var(--red);cursor:pointer;font-family:var(--fb)">Generar esta propuesta</button>`;
    const div = document.createElement('div');
    div.style.cssText = 'background:var(--bg3);border:' + (isVoted ? '2px solid var(--red)' : '1px solid var(--br)') + ';border-radius:10px;overflow:hidden';
    div.innerHTML = `
      <div style="padding:12px 14px;border-bottom:1px solid var(--br);display:flex;align-items:center;justify-content:space-between">
        <div>
          <div style="font-size:10px;color:var(--red);text-transform:uppercase;letter-spacing:.7px">Propuesta ${style}</div>
          <div style="font-size:13px;font-weight:600;margin-top:2px">${styleName}</div>
        </div>
        ${isVoted ? '<span style="font-size:10px;padding:2px 8px;border-radius:10px;background:var(--rl);color:var(--red);font-weight:600">✓ Cliente eligió esta</span>' : ''}
      </div>
      <div id="pv-${style}" style="height:320px;background:#f5f5f5;display:flex;align-items:center;justify-content:center">
        ${html ? '' : '<span style="color:#999;font-size:12px">Sin generar</span>'}
      </div>
      <div style="padding:10px 14px;display:flex;gap:6px;border-top:1px solid var(--br)">
        ${btnVer}${btnEdit}${btnDl}${btnRegen}
      </div>`;
    grid.appendChild(div);
  }

  // Cargar iframes y registrar blob URLs — sin pasar HTML en atributos onclick
  for (let style = 1; style <= 3; style++) {
    const html = p['proposal_' + style];
    const el = document.getElementById('pv-' + style);
    if (html && el) {
      const blob = new Blob([html], {type:'text/html'});
      const url  = URL.createObjectURL(blob);
      proposalBlobUrls[style] = url;
      el.innerHTML = `<div style="position:relative;width:100%;height:100%">
        <iframe src="${url}" style="width:1280px;height:${320/0.25}px;transform:scale(0.25);transform-origin:top left;border:none" scrolling="no"></iframe>
        <div style="position:absolute;inset:0;cursor:pointer" data-open="${style}"></div>
      </div>`;
    }
  }

  // Event delegation para botones "Ver" y overlays — evita pasar HTML en atributos
  grid.querySelectorAll('[data-open]').forEach(el => {
    el.addEventListener('click', () => openProposalFull(parseInt(el.dataset.open)));
  });

  // Mostrar panel de votación y comentarios
  const votePanel = document.getElementById('vote-panel');
  const voteBadge = document.getElementById('vote-status-badge');
  const voteDetails = document.getElementById('vote-details');
  const commentsAdmin = document.getElementById('comments-panel-admin');

  if (votePanel) {
    if (p.client_vote > 0 || (p.client_comments && p.client_comments !== '[]')) {
      votePanel.style.display = 'block';
      const styleNames = {1: p.proposal_1_name, 2: p.proposal_2_name, 3: p.proposal_3_name};

      if (p.client_vote > 0) {
        voteBadge.style.background = 'rgba(34,201,122,.15)';
        voteBadge.style.color = 'var(--g)';
        voteBadge.textContent = '✓ Votó';
        voteDetails.innerHTML = '<strong style="color:var(--t)">Eligió: Propuesta ' + p.client_vote + ' — ' + (styleNames[p.client_vote]||'') + '</strong>';
      } else {
        voteBadge.style.background = 'rgba(245,197,67,.15)';
        voteBadge.style.color = 'var(--y)';
        voteBadge.textContent = 'Pendiente voto';
      }

      // Mostrar comentarios
      let comments = [];
      try { comments = JSON.parse(p.client_comments || '[]'); } catch(e) {
        if (p.client_comments) comments = [{section:'General', comment: p.client_comments, date:''}];
      }
      if (comments.length > 0) {
        const escH = s => String(s||'').replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
        commentsAdmin.innerHTML = '<div style="font-size:11px;color:var(--m);text-transform:uppercase;letter-spacing:.7px;margin-bottom:8px">Comentarios (' + comments.length + ')</div>' +
          comments.map(c => `<div style="background:var(--bg2);border-radius:7px;padding:8px 12px;margin-bottom:6px;font-size:12px">
            <div style="font-size:10px;color:var(--red);text-transform:uppercase;letter-spacing:.5px;margin-bottom:3px">${escH(c.section||'General')}</div>
            <div style="color:var(--t)">${escH(c.comment)}</div>
            ${c.date ? '<div style="font-size:10px;color:var(--m);margin-top:3px">' + new Date(c.date).toLocaleDateString('es-CO') + '</div>' : ''}
          </div>`).join('');
      }
    }
  }

  if (p.client_vote > 0) {
    const linkWrap = document.getElementById('client-link-wrap');
    if (linkWrap) linkWrap.style.display = 'none';
  }

  // Panel versión final
  const finalPanel = document.getElementById('final-version-panel');
  if (finalPanel && p.status === 'final') {
    finalPanel.style.display = 'block';
    const meta = document.getElementById('final-version-meta');
    if (meta) meta.textContent = (p.proposal_final_name || 'Versión Final') + (p.regen_count_final > 0 ? ' · ' + p.regen_count_final + ' refinamiento(s)' : '');
    // Cargar HTML de versión final via SELECT * (ya incluye proposal_final)
    const finalHtml = p.proposal_final || '';
    if (finalHtml) {
      renderFinalPreview(finalHtml);
    } else {
      // proposal_final puede no venir si la query lo excluyó — hacer fetch explícito
      fetch('api/proposals.php?action=get&id=' + id)
        .then(r => r.json())
        .then(d => { if (d.ok && d.proposal.proposal_final) renderFinalPreview(d.proposal.proposal_final); })
        .catch(() => {});
    }
  } else if (finalPanel) {
    finalPanel.style.display = 'none';
  }
}

let finalBlobUrlAdmin = null;

function renderFinalPreview(html) {
  const wrap = document.getElementById('final-preview-wrap');
  if (!wrap) return;
  const blob = new Blob([html], {type:'text/html'});
  finalBlobUrlAdmin = URL.createObjectURL(blob);
  wrap.innerHTML = `<div style="position:relative;width:100%;height:100%">
    <iframe src="${finalBlobUrlAdmin}" style="width:1280px;height:${340/0.265}px;transform:scale(0.265);transform-origin:top left;border:none" scrolling="no"></iframe>
    <div style="position:absolute;inset:0;cursor:pointer" onclick="openFinalFull()"></div>
  </div>`;
}

function openFinalFull() {
  if (finalBlobUrlAdmin) window.open(finalBlobUrlAdmin, '_blank');
}

function downloadFinalHTML() {
  if (!currentProposalId) return;
  fetch('api/proposals.php?action=get&id=' + currentProposalId)
    .then(r => r.json())
    .then(data => {
      if (!data.ok || !data.proposal.proposal_final) return;
      const blob = new Blob([data.proposal.proposal_final], {type:'text/html'});
      const a = document.createElement('a');
      a.href = URL.createObjectURL(blob);
      a.download = 'version-final-' + (data.proposal.project_name || 'diseno').replace(/\s+/g,'-') + '.html';
      a.click();
    });
}

function downloadProposalPDF() {
  if (!currentProposalId) { showToast('Guarda la propuesta primero', 'var(--y)'); return; }
  const btn = event.target;
  btn.textContent = '⏳ Generando PDF...';
  btn.disabled = true;
  window.location.href = 'api/generate_pdf.php?id=' + currentProposalId;
  setTimeout(() => { btn.textContent = '📄 Exportar PDF'; btn.disabled = false; }, 4000);
}

let editorProposalId = null;
let editorStyle = null;

function openEditor(id, style) {
  editorProposalId = id;
  editorStyle = style;
  const styleNames = {1:'Moderno Bold', 2:'Elegante Profesional', 3:'Fresco Dinámico'};
  document.getElementById('editor-style-badge').textContent = 'Propuesta ' + style + ' — ' + (styleNames[style] || '');

  fetch('api/proposals.php?action=get&id=' + id)
    .then(r => r.json())
    .then(data => {
      if (!data.ok) return;
      const html = data.proposal['proposal_' + style] || '';
      const editor = document.getElementById('html-editor');
      editor.value = html;
      // Preview inicial
      const blob = new Blob([html], {type:'text/html'});
      document.getElementById('editor-preview').src = URL.createObjectURL(blob);
      // Auto preview al escribir
      let debounce;
      editor.oninput = () => {
        clearTimeout(debounce);
        debounce = setTimeout(() => {
          const blob2 = new Blob([editor.value], {type:'text/html'});
          document.getElementById('editor-preview').src = URL.createObjectURL(blob2);
        }, 800);
      };
    });

  const modal = document.getElementById('editor-modal');
  modal.style.display = 'flex';
}

function closeEditor() {
  document.getElementById('editor-modal').style.display = 'none';
}

function previewEdited() {
  const html = document.getElementById('html-editor').value;
  const blob = new Blob([html], {type:'text/html'});
  window.open(URL.createObjectURL(blob), '_blank');
}

async function saveEditedProposal() {
  if (!editorProposalId || !editorStyle) return;
  const html = document.getElementById('html-editor').value;
  const msg = document.getElementById('editor-msg');
  msg.style.color = 'var(--y)'; msg.textContent = 'Guardando...';

  // Obtener propuesta completa y actualizar solo el estilo editado
  const r = await fetch('api/proposals.php?action=get&id=' + editorProposalId);
  const data = await r.json();
  if (!data.ok) { msg.style.color = 'var(--red)'; msg.textContent = 'Error al guardar'; return; }

  const p = data.proposal;
  const payload = {
    action: 'save',
    id: editorProposalId,
    client_id: parseInt(currentClient),
    project_name: p.project_name,
    brief: p.brief || '',
    structure: p.structure || '',
    references_urls: p.references_urls || '',
    brand_colors: p.brand_colors || '',
    brand_fonts: p.brand_fonts || '',
    extra_notes: p.extra_notes || '',
  };
  payload['proposal_' + editorStyle] = html;

  // Guardar via endpoint especial
  const r2 = await fetch('api/proposals.php?action=save_html', {
    method: 'POST',
    headers: {'Content-Type':'application/json'},
    body: JSON.stringify({id: editorProposalId, style: editorStyle, html})
  });
  const d2 = await r2.json();
  if (d2.ok) {
    msg.style.color = 'var(--g)'; msg.textContent = '✓ Guardado correctamente';
    showToast('✓ HTML guardado', 'var(--g)');
    // Actualizar preview en la lista
    loadProposalPreview(editorProposalId);
  } else {
    msg.style.color = 'var(--red)'; msg.textContent = '✗ Error al guardar';
  }
}

function showClientRequiredModal() {
  if (document.getElementById('modal-client-required')) {
    document.getElementById('modal-client-required').style.display = 'flex';
    return;
  }
  const modal = document.createElement('div');
  modal.id = 'modal-client-required';
  modal.style.cssText = 'position:fixed;inset:0;background:rgba(0,0,0,.7);z-index:500;display:flex;align-items:center;justify-content:center;backdrop-filter:blur(4px)';
  modal.innerHTML = `
    <div style="background:var(--bg2);border:1px solid var(--br2);border-radius:14px;padding:32px;max-width:400px;width:90%;text-align:center">
      <div style="font-size:40px;margin-bottom:12px">👤</div>
      <div style="font-family:var(--fh);font-size:18px;font-weight:700;margin-bottom:8px">Selecciona un cliente primero</div>
      <div style="font-size:13px;color:var(--m);margin-bottom:24px;line-height:1.6">Para crear una propuesta necesitas seleccionar el cliente en la barra superior.</div>
      <button onclick="document.getElementById('modal-client-required').style.display='none'" style="padding:9px 24px;border-radius:7px;font-size:13px;background:var(--red);border:none;color:#fff;cursor:pointer;font-family:var(--fb)">Entendido</button>
    </div>`;
  document.body.appendChild(modal);
}

async function deleteProposal(id, name) {
  if (!confirm('¿Eliminar la propuesta "' + name + '"? No se puede deshacer.')) return;
  const r = await fetch('api/proposals.php?action=delete', {
    method:'POST', headers:{'Content-Type':'application/json'},
    body:JSON.stringify({id})
  });
  const data = await r.json();
  if (data.ok) { showToast('✓ Propuesta eliminada','var(--g)'); loadProposalsList(); }
  else showToast('✗ Error al eliminar','var(--red)');
}

async function regenStyle(id, style, btn) {
  if (!confirm('¿Regenerar esta propuesta? Se reemplazará la versión actual.')) return;
  const card = document.getElementById('pv-' + style);
  const originalBtn = btn;
  if (originalBtn) { originalBtn.disabled = true; originalBtn.textContent = '...'; }
  if (card) card.innerHTML = '<div style="height:100%;display:flex;align-items:center;justify-content:center;flex-direction:column;gap:8px"><div style="width:24px;height:24px;border:3px solid var(--br2);border-top-color:var(--red);border-radius:50%;animation:spin .8s linear infinite"></div><div style="font-size:12px;color:var(--m)">Generando... (1-3 min)</div></div>';

  try {
    const r = await fetch('api/proposals.php?action=generate', {
      method: 'POST',
      headers: {'Content-Type':'application/json'},
      body: JSON.stringify({id, style, style_key: selectedStyleKeys[style] || null})
    });
    if (!r.ok) throw new Error('HTTP ' + r.status);
    const data = await r.json();

    if (checkAuth(data)) {
      if (card) card.innerHTML = '<div style="color:var(--y);font-size:12px;padding:20px;text-align:center">⚠ Sesión expirada — recarga la página e inicia sesión de nuevo.</div>';
      if (originalBtn) { originalBtn.disabled = false; originalBtn.textContent = '↺'; }
      return;
    }

    if (data.ok && data.html) {
      const blob = new Blob([data.html], {type:'text/html'});
      const url  = URL.createObjectURL(blob);
      proposalBlobUrls[style] = url;
      if (card) {
        card.innerHTML = `<div style="position:relative;width:100%;height:100%">
          <iframe src="${url}" style="width:1280px;height:${320/0.25}px;transform:scale(0.25);transform-origin:top left;border:none" scrolling="no"></iframe>
          <div style="position:absolute;inset:0;cursor:pointer" data-open="${style}"></div>
        </div>`;
        card.querySelector('[data-open]')?.addEventListener('click', () => openProposalFull(style));
      }
      showToast('✓ Propuesta ' + style + ' regenerada', 'var(--g)');
    } else {
      const msg = data.error || 'Error generando';
      if (card) card.innerHTML = '<div style="color:var(--red);font-size:12px;padding:20px;text-align:center">✗ ' + msg + '</div>';
      showToast('✗ ' + msg, 'var(--red)');
    }
  } catch(e) {
    if (card) card.innerHTML = '<div style="color:var(--red);font-size:12px;padding:20px;text-align:center">✗ Error de conexión. Intenta de nuevo.</div>';
    showToast('✗ Error de conexión', 'var(--red)');
  }
  if (originalBtn) { originalBtn.disabled = false; originalBtn.textContent = '↺'; }
}

async function sendToClient() {
  if (!currentProposalId) { showToast('Guarda la propuesta primero', 'var(--y)'); return; }
  const r = await fetch('api/proposals.php?action=send', {
    method: 'POST',
    headers: {'Content-Type':'application/json'},
    body: JSON.stringify({id: currentProposalId})
  });
  const data = await r.json();
  if (data.ok) {
    const wrap = document.getElementById('client-link-wrap');
    const inp  = document.getElementById('client-link');
    const open = document.getElementById('client-link-open');
    if (wrap) wrap.style.display = 'block';
    if (inp)  inp.value = data.link;
    if (open) open.href = data.link;
    showToast('✓ Link generado', 'var(--g)');
  } else {
    showToast('✗ Error generando link', 'var(--red)');
  }
}

function copyLink() {
  const inp = document.getElementById('client-link');
  if (inp) { navigator.clipboard.writeText(inp.value); showToast('✓ Link copiado', 'var(--g)'); }
}

// ——— CLOSE DROPDOWN ———
document.addEventListener('click',e=>{
  const d=document.querySelector('.umenu');
  if(d&&!d.parentElement.contains(e.target)) d.classList.remove('open');
});

// ——— PROFILE FORM ———
document.querySelector('form[action="api/update-profile.php"]')?.addEventListener('submit',function(e){
  e.preventDefault();
  const data=Object.fromEntries(new FormData(this).entries());
  fetch('api/update-profile.php',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify(data)})
    .then(r=>r.json()).then(res=>{
      const el=document.getElementById('profile-msg');
      el.style.color=res.ok?'var(--g)':'var(--red)';
      el.textContent=res.ok?'✓ Cambios guardados':'✗ '+(res.error||'Error');
    }).catch(()=>{});
});

// ——— SITIOS WEB TABS ———


function loadProposals(clientId) {
  if (!clientId || clientId === 'all') return;
  fetch('api/proposals.php?action=list&client_id=' + clientId)
    .then(r => r.json())
    .then(data => {
      const el = document.getElementById('proposals-list');
      if (!el) return;
      if (!data.ok || !data.proposals || data.proposals.length === 0) {
        el.innerHTML = '<div style="text-align:center;color:var(--m);padding:20px;font-size:13px">Sin propuestas. Crea la primera.</div>';
        return;
      }
      const statusLabels = {draft:'Borrador',generating:'Generando...',ready:'Lista',sent:'Enviada',voted:'Cliente votó',final:'Versión final ✓'};
      const statusColors = {draft:'var(--m)',generating:'var(--y)',ready:'var(--g)',sent:'var(--b)',voted:'var(--red)',final:'var(--p)'};
      el.innerHTML = data.proposals.map(function(p) {
        return '<div style="display:flex;align-items:center;gap:12px;padding:10px 12px;background:var(--bg3);border-radius:8px;margin-bottom:6px;cursor:pointer" onclick="loadProposalPreview(' + p.id + ')">' +
          '<div style="flex:1">' +
            '<div style="font-size:13px;font-weight:500">' + p.project_name + '</div>' +
            '<div style="font-size:11px;color:var(--m);margin-top:2px">' + new Date(p.created_at).toLocaleDateString('es-CO') + '</div>' +
          '</div>' +
          (p.client_vote > 0 ? '<span style="font-size:11px;padding:2px 8px;border-radius:10px;background:rgba(232,39,42,.1);color:var(--red)">Voto: ' + p.client_vote + '</span>' : '') +
          '<span style="font-size:11px;padding:2px 8px;border-radius:10px;background:rgba(255,255,255,.05);color:' + (statusColors[p.status]||'var(--m)') + '">' + (statusLabels[p.status]||p.status) + '</span>' +
          '<button onclick="event.stopPropagation();loadProposalPreview(' + p.id + ')" style="padding:4px 10px;border-radius:5px;font-size:11px;background:rgba(74,158,248,.1);border:1px solid rgba(74,158,248,.2);color:var(--b);cursor:pointer;font-family:var(--fb)">Ver →</button>' +
        '</div>';
      }).join('');
    });
}
function viewProposal(id) {
  fetch('api/proposals.php?action=get&id=' + id)
    .then(r => r.json())
    .then(data => {
      if (!data.ok) return;
      const p = data.proposal;
      currentProposalId = id;
      proposalHtmls = {1: p.proposal_1||'', 2: p.proposal_2||'', 3: p.proposal_3||''};
      showProposalForm();
      document.getElementById('p-name').value = p.project_name || '';
      document.getElementById('p-industry').value = p.industry || '';
      document.getElementById('p-description').value = p.description || '';
      document.getElementById('p-audience').value = p.target_audience || '';
      document.getElementById('p-tone').value = p.tone || '';
      document.getElementById('p-colors').value = p.brand_colors || '';
      document.getElementById('p-fonts').value = p.brand_fonts || '';
      document.getElementById('p-refs').value = p.references_urls || '';
      document.getElementById('p-structure').value = p.homepage_structure || '';
      document.getElementById('p-comments').value = p.client_comments || '';
      document.getElementById('proposals-preview').style.display = 'block';
      for (let i = 1; i <= 3; i++) {
        const frame = document.getElementById('preview-frame-' + i);
        if (frame && proposalHtmls[i]) frame.srcdoc = proposalHtmls[i];
      }
    });
}

function sendProposal(id) {
  fetch('api/proposals.php?action=send', {
    method: 'POST',
    headers: {'Content-Type': 'application/json'},
    body: JSON.stringify({id})
  })
  .then(r => r.json())
  .then(data => {
    if (data.ok) {
      showToast('Link generado', 'var(--g)');
      copyLink(data.link);
      loadProposals(currentClient);
    }
  });
}


async function generateProposals() {
  const name      = document.getElementById('p-name')?.value.trim();
  const industry  = document.getElementById('p-industry')?.value.trim();
  const desc      = document.getElementById('p-description')?.value.trim();
  const audience  = document.getElementById('p-audience')?.value.trim();
  const tone      = document.getElementById('p-tone')?.value.trim();
  const colors    = document.getElementById('p-colors')?.value.trim();
  const fonts     = document.getElementById('p-fonts')?.value.trim();
  const refs      = document.getElementById('p-refs')?.value.trim();
  const structure = document.getElementById('p-structure')?.value.trim();
  const comments  = document.getElementById('p-comments')?.value.trim();
  const statusEl  = document.getElementById('proposal-gen-status');

  if (!name) { showToast('Ingresa el nombre del proyecto', 'var(--red)'); return; }
  if (!structure) { showToast('Ingresa la estructura del homepage', 'var(--red)'); return; }

  statusEl.textContent = 'Generando 3 propuestas... (30-90 segundos)';
  statusEl.style.color = 'var(--y)';

  const brief = 'Proyecto: ' + name + '\nIndustria: ' + industry + '\nDescripcion: ' + desc +
    '\nPublico: ' + audience + '\nTono: ' + tone + '\nColores: ' + (colors||'No especificados') +
    '\nTipografia: ' + (fonts||'No especificada') + '\nReferentes: ' + (refs||'No especificados') +
    '\nEstructura:\n' + structure + '\nComentarios: ' + comments;

  const prompt = 'Eres un disenador web experto. Genera 3 propuestas de homepage COMPLETAS en HTML+CSS para este cliente.\n\nBRIEF:\n' + brief +
    '\n\nINSTRUCCIONES:\n- 3 estilos visuales DIFERENTES:\n  1. Aventurero y Dinamico: hero a pantalla completa, colores vibrantes, tipografia bold\n  2. Profesional y Elegante: diseno limpio, paleta sofisticada, serif\n  3. Moderno y Minimalista: mucho espacio blanco, sans-serif, iconos simples\n- HTML completo con CSS en <style>\n- Usa Google Fonts\n- Placeholders con https://picsum.photos/1920/1080 para imagenes hero\n- Responsive\n- Implementa TODAS las secciones del brief\n- Calidad profesional lista para mostrar al cliente\n\nRespuesta SOLO en JSON sin markdown:\n{"proposals":[{"name":"Aventurero y Dinamico","html":"<!DOCTYPE html>..."},{"name":"Profesional y Elegante","html":"<!DOCTYPE html>..."},{"name":"Moderno y Minimalista","html":"<!DOCTYPE html>..."}]}';

  try {
    const response = await fetch('https://api.anthropic.com/v1/messages', {
      method: 'POST',
      headers: {'Content-Type': 'application/json'},
      body: JSON.stringify({
        model: 'claude-sonnet-4-6',
        max_tokens: 16000,
        messages: [{role: 'user', content: prompt}]
      })
    });

    const data = await response.json();
    const text = (data.content?.[0]?.text || '').trim();

    let parsed;
    try { parsed = JSON.parse(text); }
    catch(e) {
      const match = text.match(/\{[\s\S]*\}/);
      if (match) parsed = JSON.parse(match[0]);
      else throw new Error('No se pudo parsear la respuesta de AVI');
    }

    const proposals = parsed.proposals;
    if (!proposals || proposals.length < 3) throw new Error('Respuesta incompleta de AVI');

    for (let i = 0; i < 3; i++) {
      proposalHtmls[i+1] = proposals[i].html;
      const frame = document.getElementById('preview-frame-' + (i+1));
      const nameEl2 = document.getElementById('preview-name-' + (i+1));
      if (frame) frame.srcdoc = proposals[i].html;
      if (nameEl2) nameEl2.textContent = proposals[i].name;
    }

    document.getElementById('proposals-preview').style.display = 'block';
    statusEl.textContent = '3 propuestas generadas';
    statusEl.style.color = 'var(--g)';

    if (currentClient !== 'all') {
      const saveRes = await fetch('api/proposals.php?action=create', {
        method: 'POST',
        headers: {'Content-Type': 'application/json'},
        body: JSON.stringify({
          client_id: parseInt(currentClient), project_name: name,
          industry, description: desc, target_audience: audience,
          tone, brand_colors: colors, brand_fonts: fonts,
          references_urls: refs, homepage_structure: structure,
          client_comments: comments
        })
      });
      const saveData = await saveRes.json();
      if (saveData.ok) {
        currentProposalId = saveData.id;
        await fetch('api/proposals.php?action=save_proposals', {
          method: 'POST',
          headers: {'Content-Type': 'application/json'},
          body: JSON.stringify({
            id: currentProposalId,
            proposal_1: proposalHtmls[1], proposal_1_name: proposals[0].name,
            proposal_2: proposalHtmls[2], proposal_2_name: proposals[1].name,
            proposal_3: proposalHtmls[3], proposal_3_name: proposals[2].name,
          })
        });
        showToast('Propuestas guardadas', 'var(--g)');
      }
    }

  } catch(err) {
    statusEl.textContent = 'Error: ' + err.message;
    statusEl.style.color = 'var(--red)';
  }
}

function saveAndSendProposals() {
  if (!currentProposalId) { showToast('Primero genera las propuestas', 'var(--y)'); return; }
  fetch('api/proposals.php?action=send', {
    method: 'POST',
    headers: {'Content-Type': 'application/json'},
    body: JSON.stringify({id: currentProposalId})
  })
  .then(r => r.json())
  .then(data => {
    if (data.ok) {
      const wrap = document.getElementById('proposal-link-wrap');
      const input = document.getElementById('proposal-link-input');
      if (wrap) wrap.style.display = 'block';
      if (input) input.value = data.link;
      showToast('Propuestas enviadas', 'var(--g)');
      loadProposals(currentClient);
    }
  });
}

function copyProposalLink() {
  const val = document.getElementById('proposal-link-input')?.value;
  if (val) navigator.clipboard.writeText(val).then(() => showToast('Link copiado', 'var(--g)'));
}

function toggleFullPreview(n) {
  const modal = document.getElementById('full-preview-modal');
  const frame = document.getElementById('full-preview-frame');
  const title = document.getElementById('full-preview-title');
  const nameEl = document.getElementById('preview-name-' + n);
  if (modal && frame && proposalHtmls[n]) {
    frame.srcdoc = proposalHtmls[n];
    if (title && nameEl) title.textContent = nameEl.textContent;
    modal.style.display = 'flex';
  }
}

// ——— MÓDULO COMPETENCIA ———
async function findCompetitorsCA() {
  const url    = document.getElementById('ca-url')?.value || '';
  const desc   = document.getElementById('ca-diferencial')?.value || '';
  const status = document.getElementById('ca-find-status');
  if (!url && !desc) { status.style.color='var(--y)'; status.textContent='Ingresa la URL o diferencial del negocio'; return; }
  status.style.color='var(--y)'; status.textContent='Buscando competidores...';
  const r = await fetch('api/competitive_analysis.php?action=find', {
    method:'POST', headers:{'Content-Type':'application/json'},
    body: JSON.stringify({client_id: parseInt(currentClient), project: url||desc, industry:'', description: desc})
  });
  const data = await r.json();
  if (data.ok && data.competitors) {
    document.getElementById('ca-competitors').value = data.competitors.map(c=>c.url).join('\n');
    status.style.color='var(--g)'; status.textContent='✓ ' + data.competitors.length + ' competidores encontrados';
  } else {
    status.style.color='var(--red)'; status.textContent='✗ No se encontraron. Agrégalos manualmente.';
  }
}

async function runCompAnalysis() {
  const status = document.getElementById('ca-status');
  const resultWrap = document.getElementById('ca-result-wrap');
  const diferencial = document.getElementById('ca-diferencial')?.value || '';
  const valor       = document.getElementById('ca-valor')?.value || '';
  const publico     = document.getElementById('ca-publico')?.value || '';
  const ubicacion   = document.getElementById('ca-ubicacion')?.value || '';
  const url         = document.getElementById('ca-url')?.value || '';
  const competitors = document.getElementById('ca-competitors')?.value || '';
  const types       = [...document.querySelectorAll('#ca-type-tags .stag.active')].map(t=>t.textContent).join(', ');
  const client      = CLIENTS.find(x=>x.id==currentClient);

  if (!diferencial && !url) { status.style.color='var(--y)'; status.textContent='Completa al menos el diferencial o la URL'; return; }
  if (!competitors) { status.style.color='var(--y)'; status.textContent='Agrega URLs de competidores'; return; }

  status.style.color='var(--y)'; status.textContent='AVI analizando la competencia... (30-60 seg)';
  if (resultWrap) resultWrap.style.display='none';

  const urls = competitors.split('\n').filter(u=>u.trim().startsWith('http')).map(u=>({name:u.trim(),url:u.trim()}));

  const r = await fetch('api/competitive_analysis.php?action=analyze', {
    method:'POST', headers:{'Content-Type':'application/json'},
    body: JSON.stringify({
      client_id: parseInt(currentClient),
      proposal_id: 0,
      competitors: urls,
      project: client?.name || url,
      industry: client?.industry || '',
      diferencial, valor, publico, ubicacion,
      analysis_types: types,
    })
  });
  const data = await r.json();

  if (data.ok) {
    status.style.color='var(--g)'; status.textContent='✓ Análisis completado';
    if (resultWrap) resultWrap.style.display='block';
    const adminEl  = document.getElementById('ca-result-admin');
    const clientEl = document.getElementById('ca-result-client');
    if (adminEl)  { adminEl.style.color='var(--t)'; adminEl.textContent=data.analysis; }
    if (clientEl) {
      // Vista cliente — solo insights y recomendaciones clave
      const lines = data.analysis.split('\n').filter(l=>l.trim());
      const summary = lines.slice(0,15).join('\n');
      clientEl.textContent = summary;
    }
    showToast('✓ Análisis de competencia listo', 'var(--g)');
  } else {
    status.style.color='var(--red)'; status.textContent='✗ Error: ' + (data.error||'Intenta de nuevo');
    if (data.error==='SIN_CREDITOS') showSinCreditosModal();
  }
}

function copyCAResult() {
  const el = document.getElementById('ca-result-admin') || document.getElementById('ca-result-client');
  if (el) { navigator.clipboard.writeText(el.textContent); showToast('✓ Análisis copiado', 'var(--g)'); }
}

function shareCAResult() {
  showToast('Función de compartir próximamente', 'var(--b)');
}

// ── Google Ads OAuth & account selector ──────────────────────────────────────
function connectGoogleOAuth() {
  const cid = document.getElementById('g-oauth-cid')?.value.trim();
  const cs  = document.getElementById('g-oauth-cs')?.value.trim();
  const dev = document.getElementById('g-tok')?.value.trim();
  const mcc = document.getElementById('g-mcc-id')?.value.trim();
  if (!cid || !cs || !dev) {
    showToast('Completa OAuth Client ID, Client Secret y Developer Token primero', 'var(--y)');
    return;
  }
  // Guardar primero, luego redirigir al OAuth
  const el = document.getElementById('s-google');
  if(el){ el.style.color='var(--y)'; el.textContent='Guardando y conectando...'; }
  fetch('api/save-config.php', {method:'POST', headers:{'Content-Type':'application/json'},
    body: JSON.stringify({service:'google', token:dev, oauth_cid:cid, oauth_cs:cs, mcc_id:mcc})
  }).then(r=>r.json()).then(d=>{
    if(d.ok) window.location.href = 'api/oauth-callback.php?return_to=config';
    else showToast('Error guardando config: ' + (d.error||''), 'var(--red)');
  }).catch(()=>{ window.location.href = 'api/oauth-callback.php?return_to=config'; });
}

async function loadGoogleCustomerId(cid) {
  const input = document.getElementById('g-customer-id-input');
  const st    = document.getElementById('g-customer-status');
  if (!input || !cid || cid==='all') return;
  input.value = ''; if(st) st.textContent = '';
  try {
    const r = await fetch('api/google-accounts.php?action=get_customer&client_id=' + cid);
    const d = await r.json();
    if (d.ok && d.customer_id) {
      const fmt = d.customer_id.replace(/(\d{3})(\d{3})(\d{4})/, '$1-$2-$3');
      input.value = fmt;
      if(st){ st.style.color='var(--g)'; st.textContent='✓ Activo'; }
    }
  } catch(e) {}
}

async function saveGoogleCustomerId() {
  const input = document.getElementById('g-customer-id-input');
  const st    = document.getElementById('g-customer-status');
  const custId = (input?.value || '').replace(/[^0-9]/g, '');
  if (!custId || !currentClient || currentClient==='all') { showToast('Selecciona un cliente e ingresa el Customer ID', 'var(--y)'); return; }
  st.textContent = 'Guardando...'; st.style.color = 'var(--m)';
  try {
    const r = await fetch('api/google-accounts.php?action=save', {
      method:'POST', headers:{'Content-Type':'application/json'},
      body: JSON.stringify({client_id: parseInt(currentClient), customer_id: custId})
    });
    const d = await r.json();
    if (d.ok) {
      st.style.color='var(--g)'; st.textContent='✓ Guardado';
      loadGoogleData(currentClient, currentGPeriod);
      showToast('✓ Customer ID guardado para el cliente', 'var(--g)');
    } else { st.style.color='var(--red)'; st.textContent='✗ '+d.error; }
  } catch(e) { st.style.color='var(--red)'; st.textContent='Error de conexión'; }
}

// Detectar redirect post-OAuth
(function(){
  const p = new URLSearchParams(location.search);
  if(p.get('google_connected')==='1'){
    const s=document.getElementById('g-connect-status');
    if(s){s.textContent='✓ Conectado';s.style.background='rgba(34,201,122,.1)';s.style.color='var(--g)';}
    showToast('✓ Cuenta Google conectada correctamente','var(--g)');
    history.replaceState({},'',location.pathname);
    const rt = p.get('return_to') || 'seo';
    showPage(rt);
  }
  if(p.get('google_error')){
    showToast('✗ Error OAuth: '+p.get('google_error'),'var(--red)');
    history.replaceState({},'',location.pathname);
  }
})();

// ── Google Ads completo ───────────────────────────────────────────────────────
let currentGPeriod = '30d';
let currentGTab    = 'overview';
let gCampaignsList = []; // cache local para filtros

const gFmt = n => n>=1000000?'$'+(n/1000000).toFixed(1)+'M':n>=1000?'$'+Math.round(n/1000)+'K':'$'+Math.round(n);
const gSt  = st => st==='enabled'?'<span style="color:var(--g)">● Activa</span>':'<span style="color:var(--m)">● Pausada</span>';
const gQS  = qs => { const c=qs>=8?'var(--g)':qs>=5?'var(--y)':'var(--red)'; return qs?`<span style="color:${c};font-weight:600">${qs}/10</span>`:'—'; };

function switchGTab(tab, el) {
  document.querySelectorAll('.g-panel').forEach(p => p.style.display='none');
  document.querySelectorAll('.g-tab').forEach(b => { b.style.color='var(--m)'; b.style.borderBottomColor='transparent'; });
  const panel = document.getElementById('gtab-'+tab);
  if (panel) panel.style.display='block';
  if (el) { el.style.color='var(--t)'; el.style.borderBottomColor='var(--red)'; }
  currentGTab = tab;
  if (!currentClient || currentClient==='all') return;
  if (tab==='overview')     loadGoogleData(currentClient, currentGPeriod);
  if (tab==='campaigns')    loadGCampaigns();
  if (tab==='adgroups')     loadGAdGroups();
  if (tab==='ads')          loadGAds();
  if (tab==='keywords')     loadGKeywords();
  if (tab==='search_terms') loadGSearchTerms();
  if (tab==='ai_negatives') { loadNegContext(); loadDailyReview(); }
  if (tab==='segments')     loadGSegments('device');
  if (tab==='auction')      { loadGCampaignFilters('g-auction-camp'); }
  if (tab==='billing')      loadGBilling();
}

let gCustomFrom = '';
let gCustomTo   = '';

function switchGPeriod(p) {
  currentGPeriod = p; gCustomFrom=''; gCustomTo='';
  ['7d','14d','30d','60d','90d'].forEach(x => { const b=document.getElementById('gp-'+x); if(b) b.classList.toggle('active',x===p); });
  const fr=document.getElementById('gp-from'); const to=document.getElementById('gp-to');
  if(fr) fr.value=''; if(to) to.value='';
  reloadGTab();
}

function switchGCustom() {
  const fr=document.getElementById('gp-from')?.value; const to=document.getElementById('gp-to')?.value;
  if(!fr||!to||fr>to) return;
  gCustomFrom=fr; gCustomTo=to; currentGPeriod='custom';
  ['7d','14d','30d','60d','90d'].forEach(x => { const b=document.getElementById('gp-'+x); if(b) b.classList.remove('active'); });
  reloadGTab();
}

function gDateParams() {
  if(gCustomFrom&&gCustomTo) return `&date_from=${gCustomFrom}&date_to=${gCustomTo}`;
  return `&period=${currentGPeriod}`;
}

function gStatusFilter() { return document.getElementById('g-status-filter')?.value || 'all'; }
function gOnlyActive() { return gStatusFilter()==='active' ? '1' : '0'; }

function reloadGTab() { switchGTab(currentGTab, document.querySelector(`.g-tab[data-tab="${currentGTab}"]`)); }

function gUrl(action, extra='') { return `api/google-ads.php?client_id=${currentClient}${gDateParams()}&action=${action}&only_active=${gOnlyActive()}${extra}`; }

async function loadGoogleData(cid, period) {
  if (!cid || cid==='all') return;
  if (period) { currentGPeriod = period; gCustomFrom=''; gCustomTo=''; }
  ['7d','14d','30d','60d','90d'].forEach(p => { const b=document.getElementById('gp-'+p); if(b) b.classList.toggle('active',p===currentGPeriod&&!gCustomFrom); });
  const pl=document.getElementById('g-period-label');
  const pLabels={'7d':'Últimos 7 días','14d':'Últimos 14 días','30d':'Últimos 30 días','60d':'Últimos 60 días','90d':'Últimos 90 días'};
  if(pl) pl.textContent = gCustomFrom&&gCustomTo ? `${gCustomFrom} → ${gCustomTo}` : (pLabels[currentGPeriod]||'');
  ['spend','conv','cpa','roas'].forEach(k=>{ const e=document.getElementById('g-m-'+k); if(e) e.textContent='…'; });
  try {
    const params = gCustomFrom&&gCustomTo ? `date_from=${gCustomFrom}&date_to=${gCustomTo}` : `period=${currentGPeriod}`;
    const data = await fetch(`api/google-data.php?client_id=${cid}&${params}`).then(r=>r.json());
    const err=document.getElementById('g-error-msg');
    if (!data.ok) {
      if(err){err.style.display='block';err.textContent='✗ '+(data.error||'Error Google Ads');}
      ['spend','conv','cpa','roas'].forEach(k=>{ const e=document.getElementById('g-m-'+k); if(e) e.textContent='—'; });
      return;
    }
    if(err) err.style.display='none';
    const m=data.data?.metrics||{};
    const s=document.getElementById('g-m-spend'); if(s) s.textContent=gFmt(m.spend||0);
    const c=document.getElementById('g-m-conv');  if(c) c.textContent=(m.conversions||0).toLocaleString('es-CO');
    const p2=document.getElementById('g-m-cpa');  if(p2) p2.textContent=gFmt(m.cost_per_conv||0);
    const ro=document.getElementById('g-m-roas'); if(ro) ro.textContent=(m.roas||0).toFixed(2)+'×';
    let camps=data.data?.campaigns||[];
    const sf=gStatusFilter();
    if(sf==='active') camps=camps.filter(c=>c.status==='enabled');
    else if(sf==='paused') camps=camps.filter(c=>c.status==='paused');
    const card=document.getElementById('g-campaigns-card'); const tbl=document.getElementById('g-campaigns-table');
    if(tbl&&camps.length){ if(card) card.style.display='block';
      tbl.innerHTML='<table class="tbl"><thead><tr>'+
        '<th data-tip="Nombre de la campaña en Google Ads">Campaña</th>'+
        '<th data-tip="Gasto real en el período seleccionado">Inversión</th>'+
        '<th data-tip="Clics recibidos en los anuncios">Clics</th>'+
        '<th data-tip="Conversiones completadas: compras, formularios, llamadas">Conv.</th>'+
        '<th data-tip="Costo por Adquisición: inversión / conversiones">CPA</th>'+
        '<th data-tip="Return on Ad Spend: valor de conversiones / inversión. &gt;3× es rentable">ROAS</th>'+
        '<th data-tip="Impression Share: % de subastas disponibles en que apareciste">Imp.Share</th>'+
        '<th data-tip="Estado actual de la campaña">Estado</th>'+
        '</tr></thead><tbody>'+
        camps.map(c=>`<tr><td style="max-width:180px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap" title="${escapeHtml(c.name||'')}">${escapeHtml(c.name||'—')}</td>
          <td>${gFmt(c.spend||0)}</td><td>${(c.clicks||0).toLocaleString('es-CO')}</td><td>${(c.conversions||0)}</td>
          <td>${gFmt(c.cpa||0)}</td>
          <td style="color:${(c.roas||0)>=3?'var(--g)':(c.roas||0)>=1.5?'var(--y)':'var(--red)'}">${(c.roas||0).toFixed(2)}×</td>
          <td style="color:${(c.imp_share||0)>=60?'var(--g)':(c.imp_share||0)>=30?'var(--y)':'var(--red)'}">${c.imp_share||0}%</td>
          <td>${gSt(c.status)}</td></tr>`).join('')+'</tbody></table>';
    } else if(card) card.style.display='none';
    // billing alert
    gCampaignsList = data.data?.campaigns || [];
    renderOvSnapshot();
    renderGoogleOptimIndicators(m, gCampaignsList);
    loadGBillingAlert();
    renderGPacingWidget(gCampaignsList);
    loadGConversionsCheck();
    loadGGeoTop();
  } catch(e) {
    const err=document.getElementById('g-error-msg'); if(err){err.style.display='block';err.textContent='✗ Error de conexión con Google Ads';}
    ['spend','conv','cpa','roas'].forEach(k=>{ const e=document.getElementById('g-m-'+k); if(e) e.textContent='—'; });
  }
}

// ── Budget Pacing ─────────────────────────────────────────────────────────────
function renderGPacingWidget(camps) {
  const card    = document.getElementById('g-pacing-card');
  const badge   = document.getElementById('g-pacing-badge');
  const content = document.getElementById('g-pacing-content');
  if (!card || !content) return;

  const active = (camps||[]).filter(c => c.status==='enabled' || c.status==='ENABLED');
  if (!active.length) { card.style.display='none'; return; }

  // Compute month pacing: daily_budget * days_in_month vs accumulated spend
  const now   = new Date();
  const day   = now.getDate();
  const daysM = new Date(now.getFullYear(), now.getMonth()+1, 0).getDate();
  const daysLeft = daysM - day;

  let totalBudgetMonth = 0, totalSpend = 0;
  active.forEach(c => {
    totalBudgetMonth += (c.budget || 0) * daysM;
    totalSpend       += (c.spend  || 0);
  });

  if (!totalBudgetMonth) { card.style.display='none'; return; }

  const pct        = totalSpend / totalBudgetMonth * 100;
  const expected   = (day / daysM) * 100;
  const proj       = totalSpend / (day||1) * daysM;
  const diff       = pct - expected;

  // Badge
  let badgeColor, badgeTxt;
  if (Math.abs(diff) <= 5)        { badgeColor='rgba(34,201,122,.18)'; badge.style.color='var(--g)'; badgeTxt='🟢 En ritmo'; }
  else if (diff > 5)              { badgeColor='rgba(248,113,113,.18)'; badge.style.color='var(--red)'; badgeTxt='🔴 Sobre ritmo'; }
  else                            { badgeColor='rgba(250,204,21,.18)'; badge.style.color='var(--y)'; badgeTxt='🟡 Bajo ritmo'; }
  badge.style.background = badgeColor;
  badge.textContent = badgeTxt;

  const barFill  = Math.min(pct, 100);
  const barColor = diff > 5 ? 'var(--red)' : diff < -5 ? 'var(--y)' : 'var(--g)';

  content.innerHTML = `
    <div style="margin-bottom:10px">
      <div style="display:flex;justify-content:space-between;font-size:11px;color:var(--m);margin-bottom:4px">
        <span>Gastado: ${gFmt(totalSpend)}</span>
        <span>Presupuesto mes: ${gFmt(totalBudgetMonth)}</span>
      </div>
      <div style="background:var(--bg3);border-radius:20px;height:8px;overflow:hidden;position:relative">
        <div style="width:${barFill}%;background:${barColor};height:100%;border-radius:20px;transition:.3s"></div>
        <div style="position:absolute;top:0;left:${expected}%;width:2px;height:100%;background:rgba(255,255,255,.4)" title="Ritmo esperado hoy: ${expected.toFixed(0)}%"></div>
      </div>
      <div style="display:flex;justify-content:space-between;font-size:10px;color:var(--m);margin-top:3px">
        <span>${pct.toFixed(1)}% ejecutado (día ${day}/${daysM})</span>
        <span>Esperado: ${expected.toFixed(1)}%</span>
      </div>
    </div>
    <div style="display:grid;grid-template-columns:1fr 1fr;gap:8px;font-size:11px">
      <div><div style="color:var(--m);margin-bottom:2px">Proyección fin de mes</div><div style="font-size:14px;font-weight:600;color:${proj>totalBudgetMonth?'var(--red)':'var(--t)'}">${gFmt(proj)}</div></div>
      <div><div style="color:var(--m);margin-bottom:2px">Presupuesto restante</div><div style="font-size:14px;font-weight:600">${gFmt(Math.max(0,totalBudgetMonth-totalSpend))}</div></div>
      <div><div style="color:var(--m);margin-bottom:2px">Días restantes</div><div style="font-size:14px;font-weight:600">${daysLeft}</div></div>
      <div><div style="color:var(--m);margin-bottom:2px">Campaña activas</div><div style="font-size:14px;font-weight:600">${active.length}</div></div>
    </div>`;
  card.style.display='block';
}

// ── Conversions check badge ────────────────────────────────────────────────────
async function loadGConversionsCheck() {
  const badge = document.getElementById('g-conv-badge');
  if (!badge || !currentClient || currentClient==='all') return;
  badge.style.display='none';
  try {
    const d = await fetch(gUrl('conversions_check')).then(r=>r.json());
    if (!d.ok || !d.data) return;
    const { active_count, total } = d.data;
    if (!total) return;
    const ok = active_count > 0;
    badge.style.display = 'inline-block';
    badge.style.background = ok ? 'rgba(34,201,122,.15)' : 'rgba(248,113,113,.15)';
    badge.style.color = ok ? 'var(--g)' : 'var(--red)';
    badge.style.border = ok ? '1px solid rgba(34,201,122,.3)' : '1px solid rgba(248,113,113,.3)';
    badge.textContent = ok ? `✓ ${active_count} conv. activas` : '⚠ Sin conversiones activas';
  } catch(e) { /* silencioso */ }
}

// ── Geographic Top 5 ──────────────────────────────────────────────────────────
async function loadGGeoTop() {
  const card    = document.getElementById('g-geo-card');
  const content = document.getElementById('g-geo-content');
  if (!card || !content || !currentClient || currentClient==='all') return;
  card.style.display = 'block';
  content.innerHTML = '<div style="font-size:12px;color:var(--m);text-align:center;padding:12px">Cargando…</div>';
  try {
    const d = await fetch(gUrl('geo_top')).then(r=>r.json());
    if (!d.ok || !d.geo || !d.geo.length) {
      card.style.display = 'none';
      return;
    }
    const rows = d.geo.slice(0,5);
    const maxConv = Math.max(...rows.map(r=>r.conversions||0), 1);
    content.innerHTML = '<table class="tbl" style="width:100%;font-size:12px"><thead><tr>'+
      '<th>Ubicación</th><th>Clics</th><th>Conv.</th><th>ROAS</th></tr></thead><tbody>'+
      rows.map((r,i)=>{
        const barW = Math.round((r.conversions||0)/maxConv*60);
        const roasC = (r.roas||0)>=3?'var(--g)':(r.roas||0)>=1?'var(--y)':'var(--red)';
        return `<tr>
          <td style="max-width:120px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">
            <span style="color:var(--m);font-size:10px">${i+1}.</span> ${escapeHtml(r.location||'—')}
            ${barW?`<div style="margin-top:2px;height:3px;width:${barW}px;background:var(--g);border-radius:2px;opacity:.6"></div>`:''}
          </td>
          <td>${(r.clicks||0).toLocaleString('es-CO')}</td>
          <td style="font-weight:600;color:${(r.conversions||0)>0?'var(--g)':'var(--m)'}">${(r.conversions||0).toLocaleString('es-CO',{maximumFractionDigits:1})}</td>
          <td style="color:${roasC}">${(r.roas||0).toFixed(1)}×</td>
        </tr>`;
      }).join('')+'</tbody></table>';
  } catch(e) { card.style.display='none'; }
}

// ── Campaign type filter (PMax / Search / Display / Video) ────────────────────
let gCampTypeFilter = 'all';
function filterGCampType(type, btn) {
  gCampTypeFilter = type;
  document.querySelectorAll('[id^="g-type-"]').forEach(b => b.classList.remove('active'));
  if (btn) btn.classList.add('active');
  const note = document.getElementById('g-camps-pmax-note');
  if (note) note.style.display = type==='PERFORMANCE_MAX' ? 'inline-block' : 'none';

  const cont = document.getElementById('g-camps-content');
  if (!cont) return;
  const rows = cont.querySelectorAll('tbody tr[id^="gcamp-row-"]');
  let visible = 0;
  rows.forEach(row => {
    const campId = row.id.replace('gcamp-row-','');
    const camp = gCampaignsList.find(c=>c.id===campId||c.id==campId);
    const show = type==='all' || !camp || (camp.type||'').toUpperCase()===type.toUpperCase();
    row.style.display = show ? '' : 'none';
    if (show) visible++;
  });
  // If table not yet rendered, fall through to loadGCampaigns with filter applied after render
  if (!rows.length) loadGCampaigns();
}

async function loadGCampaigns() {
  const load=document.getElementById('g-camps-loading'); const cont=document.getElementById('g-camps-content');
  if(load) { load.style.display='block'; load.textContent='Cargando campañas…'; } if(cont) cont.innerHTML='';
  try {
    const d=await fetch(gUrl('campaigns')).then(r=>r.json());
    if(load) load.style.display='none';
    if(!d.ok){if(cont) cont.innerHTML=`<p style="color:var(--red);font-size:13px">✗ ${escapeHtml(d.error||'Error')}</p>`;return;}
    gCampaignsList=d.campaigns||[];
    const sf2=gStatusFilter();
    if(sf2==='paused') gCampaignsList=gCampaignsList.filter(c=>c.status==='paused');
    populateGCampFilters();
    if(!gCampaignsList.length){if(cont) cont.innerHTML='<p style="color:var(--m);font-size:13px">No se encontraron campañas para el filtro seleccionado.</p>';return;}
    if(cont) cont.innerHTML='<table class="tbl"><thead><tr>'+
      '<th data-tip="Nombre de la campaña en Google Ads">Campaña</th>'+
      '<th data-tip="Tipo de campaña: Search (búsqueda texto), Display (banners), PMax (Performance Max automatizado), Video (YouTube)">Tipo</th>'+
      '<th data-tip="Presupuesto diario máximo asignado a esta campaña">Budget/día</th>'+
      '<th data-tip="Gasto real en el período seleccionado">Inversión</th>'+
      '<th data-tip="Impresiones: veces que tu anuncio fue mostrado en el período">Imp.</th>'+
      '<th data-tip="Clics recibidos en los anuncios de esta campaña">Clics</th>'+
      '<th data-tip="Click-Through Rate: % de impresiones que resultaron en clics. Search &gt;5% es bueno">CTR</th>'+
      '<th data-tip="Costo por Clic promedio pagado en el período">CPC</th>'+
      '<th data-tip="Conversiones completadas: compras, formularios, llamadas configuradas como metas">Conv.</th>'+
      '<th data-tip="Costo por Adquisición: inversión / conversiones. Menor es mejor">CPA</th>'+
      '<th data-tip="Return on Ad Spend: valor de conversiones / inversión. &gt;3× es rentable">ROAS</th>'+
      '<th data-tip="Impression Share: % de subastas en que apareciste del total disponible. &lt;50% indica oportunidad de crecimiento">IS%</th>'+
      '<th data-tip="% de subastas perdidas por presupuesto agotado. Solución: aumentar el budget diario">Perdido budget%</th>'+
      '<th data-tip="Estado actual de la campaña en Google Ads">Estado</th>'+
      '<th>Acciones</th></tr></thead><tbody>'+
      gCampaignsList.map(c=>`<tr id="gcamp-row-${c.id}">
        <td style="max-width:160px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap" title="${escapeHtml(c.name)}">${escapeHtml(c.name)}</td>
        <td style="font-size:11px;color:var(--m)">${c.type.replace('SEARCH','Search').replace('DISPLAY','Display').replace('PERFORMANCE_MAX','PMax').replace('VIDEO','Video')||'—'}</td>
        <td><span id="gcamp-budget-${c.id}">${gFmt(c.budget||0)}</span></td><td>${gFmt(c.spend||0)}</td>
        <td>${(c.impressions||0).toLocaleString('es-CO')}</td><td>${(c.clicks||0).toLocaleString('es-CO')}</td>
        <td>${c.ctr||0}%</td><td>${gFmt(c.cpc||0)}</td><td>${c.conversions||0}</td>
        <td>${gFmt(c.cpa||0)}</td>
        <td style="color:${(c.roas||0)>=3?'var(--g)':(c.roas||0)>=1.5?'var(--y)':'var(--red)'}">${(c.roas||0).toFixed(2)}×</td>
        <td style="color:${(c.imp_share||0)>=60?'var(--g)':(c.imp_share||0)>=30?'var(--y)':'var(--red)'}">${c.imp_share||0}%</td>
        <td style="color:${(c.lost_budget||0)>20?'var(--red)':'var(--m)'}">${c.lost_budget||0}%</td>
        <td id="gcamp-status-${c.id}">${gSt(c.status)}</td>
        <td style="white-space:nowrap;display:flex;gap:5px;padding:6px 8px">
          <button onclick="toggleCampaign('${c.id}','${c.status}')" id="gcamp-toggle-${c.id}"
            style="padding:3px 8px;font-size:11px;border-radius:5px;border:1px solid ${c.status==='enabled'?'rgba(248,113,113,.4)':'rgba(34,201,122,.4)'};background:${c.status==='enabled'?'rgba(248,113,113,.1)':'rgba(34,201,122,.1)'};color:${c.status==='enabled'?'var(--red)':'var(--g)'};cursor:pointer">
            ${c.status==='enabled'?'Pausar':'Activar'}
          </button>
          <button onclick="openBudgetModal('${c.id}','${escapeHtml(c.budget_resource_name||'')}',${c.budget||0},'${escapeHtml(c.name)}')"
            style="padding:3px 8px;font-size:11px;border-radius:5px;border:1px solid var(--br2);background:var(--bg3);color:var(--m);cursor:pointer" title="Cambiar presupuesto">
            💰
          </button>
        </td>
      </tr>`).join('')+'</tbody></table>';
    // Apply any active type filter without re-fetching
    if (gCampTypeFilter && gCampTypeFilter !== 'all') filterGCampType(gCampTypeFilter, document.getElementById('g-type-'+gCampTypeFilter.toLowerCase().replace('performance_max','pmax')));
  } catch(e){ if(load) load.style.display='none'; if(cont) cont.innerHTML='<p style="color:var(--red);font-size:13px">✗ Error de conexión</p>'; }
}

let gBudgetModal = { campId:'', budgetRn:'', currentAmount:0 };
function openBudgetModal(campId, budgetRn, currentAmount, campName) {
  gBudgetModal = { campId, budgetRn, currentAmount };
  document.getElementById('g-budget-modal-name').textContent = campName;
  document.getElementById('g-budget-modal-input').value = currentAmount;
  document.getElementById('g-budget-modal').style.display='flex';
  setTimeout(()=>document.getElementById('g-budget-modal-input').focus(),50);
}
function closeBudgetModal() { document.getElementById('g-budget-modal').style.display='none'; }
async function saveBudget() {
  const amount = parseFloat(document.getElementById('g-budget-modal-input').value);
  if (!amount || amount <= 0) { alert('Ingresa un monto válido'); return; }
  const btn = document.querySelector('#g-budget-modal button');
  if(btn){ btn.textContent='Guardando…'; btn.disabled=true; }
  try {
    const d = await fetch(`api/google-ads.php?client_id=${currentClient}&action=update_budget`, {
      method:'POST', headers:{'Content-Type':'application/json'},
      body:JSON.stringify({budget_resource_name:gBudgetModal.budgetRn,amount})
    }).then(r=>r.json());
    if(!d.ok){ alert('Error: '+(d.error||'desconocido')); }
    else {
      closeBudgetModal();
      const el=document.getElementById('gcamp-budget-'+gBudgetModal.campId);
      if(el) el.textContent=gFmt(amount);
    }
  } catch(e){ alert('Error de conexión'); }
  if(btn){ btn.textContent='Guardar'; btn.disabled=false; }
}
async function toggleCampaign(campId, currentStatus) {
  const isActive = currentStatus==='enabled';
  if(!confirm(isActive?'¿Pausar esta campaña?':'¿Activar esta campaña?')) return;
  const btn=document.getElementById('gcamp-toggle-'+campId);
  if(btn){ btn.textContent='…'; btn.disabled=true; }
  try {
    const d = await fetch(`api/google-ads.php?client_id=${currentClient}&action=${isActive?'pause_campaign':'enable_campaign'}`,{
      method:'POST', headers:{'Content-Type':'application/json'},
      body:JSON.stringify({campaign_id:campId})
    }).then(r=>r.json());
    if(!d.ok){ alert('Error: '+(d.error||'desconocido')); if(btn){btn.textContent=isActive?'Pausar':'Activar';btn.disabled=false;} return; }
    const newStatus=d.status;
    if(btn){
      btn.textContent=newStatus==='enabled'?'Pausar':'Activar';
      btn.style.borderColor=newStatus==='enabled'?'rgba(248,113,113,.4)':'rgba(34,201,122,.4)';
      btn.style.background=newStatus==='enabled'?'rgba(248,113,113,.1)':'rgba(34,201,122,.1)';
      btn.style.color=newStatus==='enabled'?'var(--red)':'var(--g)';
      btn.disabled=false;
      btn.onclick=function(){toggleCampaign(campId,newStatus);};
    }
    const st=document.getElementById('gcamp-status-'+campId);
    if(st) st.innerHTML=gSt(newStatus);
  } catch(e){ alert('Error de conexión'); if(btn){btn.textContent=isActive?'Pausar':'Activar';btn.disabled=false;} }
}

async function loadGAdGroups() {
  const campId=document.getElementById('g-ag-camp-filter')?.value||'';
  const load=document.getElementById('g-ag-loading'); const cont=document.getElementById('g-ag-content');
  if(load) load.style.display='block'; if(cont) cont.innerHTML='';
  try {
    const d=await fetch(gUrl('adgroups',campId?`&campaign_id=${campId}`:'')).then(r=>r.json());
    if(load) load.style.display='none';
    if(!d.ok){if(cont) cont.innerHTML=`<p style="color:var(--red);font-size:13px">✗ ${escapeHtml(d.error||'Error')}</p>`;return;}
    const list=d.adgroups||[];
    if(!list.length){if(cont) cont.innerHTML='<p style="color:var(--m);font-size:13px">No se encontraron grupos de anuncios.</p>';return;}
    if(cont) cont.innerHTML='<table class="tbl"><thead><tr>'+
      '<th data-tip="Nombre del grupo de anuncios que contiene las keywords y anuncios relacionados">Grupo</th>'+
      '<th data-tip="Campaña a la que pertenece este grupo">Campaña</th>'+
      '<th data-tip="Gasto del grupo de anuncios en el período seleccionado">Inversión</th>'+
      '<th data-tip="Impresiones: veces que los anuncios de este grupo fueron mostrados">Imp.</th>'+
      '<th data-tip="Clics recibidos en los anuncios de este grupo">Clics</th>'+
      '<th data-tip="Click-Through Rate: % de impresiones que resultaron en clics">CTR</th>'+
      '<th data-tip="Costo por Clic promedio en este grupo de anuncios">CPC</th>'+
      '<th data-tip="Conversiones atribuidas a este grupo de anuncios">Conv.</th>'+
      '<th data-tip="Return on Ad Spend del grupo: valor de conversiones / gasto">ROAS</th>'+
      '<th data-tip="Estado del grupo: activo o pausado">Estado</th>'+
      '</tr></thead><tbody>'+
      list.map(g=>`<tr>
        <td style="max-width:150px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">${escapeHtml(g.name)}</td>
        <td style="font-size:11px;color:var(--m);max-width:130px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">${escapeHtml(g.campaign)}</td>
        <td>${gFmt(g.spend||0)}</td><td>${(g.impressions||0).toLocaleString('es-CO')}</td>
        <td>${(g.clicks||0).toLocaleString('es-CO')}</td><td>${g.ctr||0}%</td><td>${gFmt(g.cpc||0)}</td>
        <td>${g.conversions||0}</td><td style="color:${(g.roas||0)>=3?'var(--g)':(g.roas||0)>=1.5?'var(--y)':'var(--red)'}">${(g.roas||0).toFixed(2)}×</td>
        <td>${gSt(g.status)}</td></tr>`).join('')+'</tbody></table>';
  } catch(e){ if(load) load.style.display='none'; if(cont) cont.innerHTML='<p style="color:var(--red);font-size:13px">✗ Error de conexión</p>'; }
}

async function loadGAds() {
  const campId=document.getElementById('g-ads-camp-filter')?.value||'';
  const load=document.getElementById('g-ads-loading'); const cont=document.getElementById('g-ads-content');
  if(load) load.style.display='block'; if(cont) cont.innerHTML='';
  try {
    const d=await fetch(gUrl('ads',campId?`&campaign_id=${campId}`:'')).then(r=>r.json());
    if(load) load.style.display='none';
    if(!d.ok){if(cont) cont.innerHTML=`<p style="color:var(--red);font-size:13px">✗ ${escapeHtml(d.error||'Error')}</p>`;return;}
    const list=d.ads||[];
    if(!list.length){if(cont) cont.innerHTML='<p style="color:var(--m);font-size:13px">No se encontraron anuncios.</p>';return;}
    if(cont) cont.innerHTML=list.map(a=>`
      <div style="background:var(--bg2);border:1px solid var(--br);border-radius:10px;padding:14px 16px;margin-bottom:10px">
        <div style="display:flex;gap:10px;flex-wrap:wrap;align-items:flex-start;margin-bottom:10px">
          <div style="flex:1;min-width:200px">
            <div style="font-size:10px;color:var(--m);margin-bottom:4px">TITULARES (${a.headlines?.length||0})</div>
            ${(a.headlines||[]).slice(0,5).map((h,i)=>`<div style="font-size:12px;color:var(--b);margin-bottom:2px">${i+1}. ${escapeHtml(h)}</div>`).join('')}
            <div style="font-size:10px;color:var(--m);margin:6px 0 4px">DESCRIPCIONES</div>
            ${(a.descriptions||[]).map(d2=>`<div style="font-size:12px;color:var(--t);margin-bottom:2px">${escapeHtml(d2)}</div>`).join('')}
            ${a.final_url?`<div style="font-size:11px;color:var(--g);margin-top:6px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">${escapeHtml(a.final_url)}</div>`:''}
          </div>
          <div style="min-width:260px;display:grid;grid-template-columns:1fr 1fr;gap:8px">
            <div><div style="font-size:10px;color:var(--m)">INVERSIÓN</div><div style="font-size:14px;font-weight:600">${gFmt(a.spend||0)}</div></div>
            <div><div style="font-size:10px;color:var(--m)">CLICS</div><div style="font-size:14px;font-weight:600">${(a.clicks||0).toLocaleString('es-CO')}</div></div>
            <div><div style="font-size:10px;color:var(--m)">CTR</div><div style="font-size:14px;font-weight:600">${a.ctr||0}%</div></div>
            <div><div style="font-size:10px;color:var(--m)">CONV.</div><div style="font-size:14px;font-weight:600">${a.conversions||0}</div></div>
          </div>
        </div>
        <div style="display:flex;gap:8px;flex-wrap:wrap;font-size:11px">
          <span style="color:var(--m)">${escapeHtml(a.campaign)}</span><span style="color:var(--br2)">›</span>
          <span style="color:var(--m)">${escapeHtml(a.adgroup)}</span>
          <span style="margin-left:auto">${gSt(a.status)}</span>
          ${a.approval&&a.approval!=='APPROVED'?`<span style="color:var(--y)">⚠ ${a.approval}</span>`:''}
          ${(()=>{ const s=a.ad_strength||''; if(!s||s==='UNSPECIFIED'||s==='UNKNOWN') return ''; const map={'EXCELLENT':'var(--g)','GOOD':'var(--g)','POOR':'var(--red)','PENDING':'var(--m)','NO_ADS':'var(--m)'}; const lbl={'EXCELLENT':'★ Excelente','GOOD':'▲ Bueno','POOR':'▼ Bajo','PENDING':'◌ Pendiente','NO_ADS':'— Sin anuncios'}; return `<span style="font-size:10px;padding:2px 7px;border-radius:8px;background:rgba(255,255,255,.06);color:${map[s]||'var(--m)'}" data-tip="Ad Strength: calidad del anuncio según Google">${lbl[s]||s}</span>`; })()}
        </div>
      </div>`).join('');
  } catch(e){ if(load) load.style.display='none'; if(cont) cont.innerHTML='<p style="color:var(--red);font-size:13px">✗ Error de conexión</p>'; }
}

let gKwData=[];
async function loadGKeywords() {
  const campId=document.getElementById('g-kw-camp-filter')?.value||'';
  const load=document.getElementById('g-kw-loading'); const cont=document.getElementById('g-kw-content');
  if(load) load.style.display='block'; if(cont) cont.innerHTML='';
  try {
    const d=await fetch(gUrl('keywords',campId?`&campaign_id=${campId}`:'')).then(r=>r.json());
    if(load) load.style.display='none';
    if(!d.ok){if(cont) cont.innerHTML=`<p style="color:var(--red);font-size:13px">✗ ${escapeHtml(d.error||'Error')}</p>`;return;}
    gKwData=d.keywords||[]; renderGKeywords();
  } catch(e){ if(load) load.style.display='none'; if(cont) cont.innerHTML='<p style="color:var(--red);font-size:13px">✗ Error de conexión</p>'; }
}
function filterGKeywords(){ renderGKeywords(); }
function renderGKeywords(){
  const search  = (document.getElementById('g-kw-search')?.value||'').toLowerCase();
  const qsf     = document.getElementById('g-kw-qs-filter')?.value||'all';
  const cont    = document.getElementById('g-kw-content'); if(!cont) return;
  let list = search ? gKwData.filter(k=>(k.keyword||'').toLowerCase().includes(search)) : [...gKwData];
  if(qsf==='low')     list=list.filter(k=>k.qs>0&&k.qs<=5);
  if(qsf==='ok')      list=list.filter(k=>k.qs>=6&&k.qs<=7);
  if(qsf==='top')     list=list.filter(k=>k.qs>=8);
  if(qsf==='no_conv') list=list.filter(k=>!k.conversions&&k.impressions>0);
  const cnt=document.getElementById('g-kw-count'); if(cnt) cnt.textContent=list.length+' keywords';
  if(!list.length){cont.innerHTML='<p style="color:var(--m);font-size:13px">No se encontraron keywords con ese filtro.</p>';return;}
  const mtColor=m=>m==='exact'?'var(--b)':m==='phrase'?'var(--g)':'var(--m)';
  const qsLabel=(v,label)=>{if(!v||v==='UNKNOWN') return '';const c=v==='ABOVE_AVERAGE'?'var(--g)':v==='AVERAGE'?'var(--y)':'var(--red)';const t=v==='ABOVE_AVERAGE'?'▲':v==='AVERAGE'?'●':'▼';return `<span style="color:${c};font-size:10px" data-tip="${label}: ${v.replace(/_/g,' ')}">${t}</span>`;};
  const gIS=v=>{if(v===null||v===undefined) return '<span style="color:var(--m)">—</span>';const c=v>=60?'var(--g)':v>=30?'var(--y)':'var(--red)';return `<span style="color:${c};font-weight:600">${v}%</span>`;};
  const gTop=v=>{if(v===null||v===undefined) return '—';const c=v>=60?'var(--g)':v>=30?'var(--y)':'var(--red)';return `<span style="color:${c}">${v}%</span>`;};
  const rowBg=k=>k.qs>0&&k.qs<=4?'background:rgba(248,113,113,.04)':k.spend>0&&!k.conversions&&k.qs>0&&k.qs<=6?'background:rgba(250,204,21,.03)':'';
  cont.innerHTML='<table class="tbl"><thead><tr>'+
    '<th data-tip="Keyword configurada en la cuenta. Amarillo/rojo = QS bajo o sin conversiones">Keyword</th>'+
    '<th data-tip="Tipo de concordancia: Exact (solo ese término), Phrase (contiene la frase), Broad (variaciones)">Concord.</th>'+
    '<th data-tip="Quality Score 1-10 · ▲ Sobre el promedio · ● Promedio · ▼ Por debajo. Componentes: Anuncio (AD) · Landing page (LP) · CTR esperado (CTR)">QS · AD · LP · CTR</th>'+
    '<th data-tip="Puja máxima CPC configurada para esta keyword">Bid</th>'+
    '<th data-tip="Impresiones totales del período">Imp.</th>'+
    '<th data-tip="Clics recibidos">Clics</th>'+
    '<th data-tip="CTR: % de impresiones que resultaron en clic">CTR</th>'+
    '<th data-tip="CPC promedio real pagado por clic">CPC</th>'+
    '<th data-tip="Gasto total de la keyword en el período">Gasto</th>'+
    '<th data-tip="Conversiones atribuidas">Conv.</th>'+
    '<th data-tip="IS%: % de subastas que ganaste. Rojo &lt;30% (competencia domina) · Amarillo 30-60% · Verde &gt;60%">IS%</th>'+
    '<th data-tip="Top%: % de veces que el anuncio apareció en la parte superior de resultados (encima de orgánicos)">Top%</th>'+
    '<th data-tip="Abs.Top%: % de veces que apareció en posición #1 absoluta de la página">Pos.1%</th>'+
    '<th data-tip="Grupo de anuncios al que pertenece">Grupo</th>'+
    '<th data-tip="Agregar esta keyword como negativa en todas las campañas activas">Acción</th>'+
    '</tr></thead><tbody>'+
    list.map(k=>`<tr style="${rowBg(k)}">
      <td style="max-width:160px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-weight:${k.qs>0&&k.qs<=5?'600':'400'};color:${k.qs>0&&k.qs<=4?'var(--red)':k.qs>0&&k.qs<=6?'var(--y)':'var(--t)'}" data-tip="Campaña: ${escapeHtml(k.campaign||'')}">${escapeHtml(k.keyword)}</td>
      <td><span style="color:${mtColor(k.match_type)};font-size:11px;font-weight:600">${k.match_type==='exact'?'[exact]':k.match_type==='phrase'?'"phrase"':'broad'}</span></td>
      <td style="white-space:nowrap">${gQS(k.qs)} ${qsLabel(k.qs_ad,'Anuncio')} ${qsLabel(k.qs_landing,'Landing')} ${qsLabel(k.qs_ctr,'CTR esp.')}</td>
      <td>${k.bid>0?gFmt(k.bid):'—'}</td>
      <td>${(k.impressions||0).toLocaleString('es-CO')}</td>
      <td>${(k.clicks||0).toLocaleString('es-CO')}</td>
      <td>${k.ctr||0}%</td>
      <td>${k.cpc>0?gFmt(k.cpc):'—'}</td>
      <td style="color:${k.spend>0&&!k.conversions?'var(--red)':'var(--t)'}">${k.spend>0?gFmt(k.spend):'—'}</td>
      <td style="color:${k.conversions>0?'var(--g)':'var(--m)'};font-weight:${k.conversions>0?'600':'400'}">${k.conversions||'—'}</td>
      <td>${gIS(k.imp_share)}</td>
      <td>${gTop(k.top_share)}</td>
      <td>${gTop(k.abs_top)}</td>
      <td style="font-size:11px;color:var(--m);max-width:110px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">${escapeHtml(k.adgroup||'')}</td>
      <td><button onclick="openKwNegModal(${JSON.stringify(escapeHtml(k.keyword))},${JSON.stringify(k.match_type||'exact')})" style="padding:3px 8px;font-size:11px;background:rgba(239,68,68,.12);color:var(--red);border:1px solid rgba(239,68,68,.3);border-radius:5px;cursor:pointer;white-space:nowrap" data-tip="Agregar como negativa en todas las campañas activas">⊘ Negativa</button></td>
    </tr>`).join('')+'</tbody></table>';
  renderQSRecsPanel();
}

function renderQSRecsPanel() {
  const lowQS = gKwData.filter(k => k.qs > 0 && k.qs <= 5);
  const recsPanel  = document.getElementById('g-qs-recs');
  const fbPanel    = document.getElementById('g-kw-avi-fallback');
  if (!recsPanel) return;
  if (!lowQS.length) {
    recsPanel.style.display = 'none';
    if (fbPanel) fbPanel.style.display = 'block';
    return;
  }
  recsPanel.style.display = 'block';
  if (fbPanel) fbPanel.style.display = 'none';
  const countEl = document.getElementById('g-qs-recs-count');
  if (countEl) countEl.textContent = lowQS.length + ' keywords con QS bajo';

  // Group by primary issue
  const badAd      = lowQS.filter(k => k.qs_ad      === 'BELOW_AVERAGE');
  const badLanding = lowQS.filter(k => k.qs_landing === 'BELOW_AVERAGE');
  const badCTR     = lowQS.filter(k => k.qs_ctr     === 'BELOW_AVERAGE');
  const groups = [
    { label:'✍️ Mejorar copy del anuncio', color:'rgba(155,127,244,.12)', border:'rgba(155,127,244,.3)', text:'var(--p)', action:'Reescribe el texto del anuncio incluyendo la keyword de forma más relevante y con CTA claro.', kws: badAd },
    { label:'🏠 Mejorar landing page', color:'rgba(248,113,113,.08)', border:'rgba(248,113,113,.25)', text:'#f87171', action:'Asegura que el contenido de la landing page coincida exactamente con la intención de búsqueda de la keyword.', kws: badLanding },
    { label:'📈 Mejorar CTR esperado', color:'rgba(250,204,21,.08)', border:'rgba(250,204,21,.3)', text:'var(--y)', action:'Optimiza el titular del anuncio con la keyword exacta y añade extensiones de anuncio relevantes.', kws: badCTR },
  ].filter(g => g.kws.length > 0);

  const listEl = document.getElementById('g-qs-recs-list');
  if (!listEl) return;
  listEl.innerHTML = groups.map(g => `
    <div style="background:${g.color};border:1px solid ${g.border};border-radius:9px;padding:12px 14px">
      <div style="font-size:12px;font-weight:700;color:${g.text};margin-bottom:6px">${g.label} <span style="font-weight:400;opacity:.7">(${g.kws.length} kw)</span></div>
      <div style="font-size:11px;color:var(--m);margin-bottom:8px">${g.action}</div>
      <div style="display:flex;flex-wrap:wrap;gap:5px">
        ${g.kws.slice(0,8).map(k=>`<span style="background:var(--bg2);border:1px solid var(--br2);border-radius:4px;padding:2px 8px;font-size:11px;color:var(--t)">${escapeHtml(k.keyword)} <span style="color:var(--red);font-weight:700">QS${k.qs}</span></span>`).join('')}
        ${g.kws.length>8?`<span style="font-size:11px;color:var(--m);padding:2px 6px">+${g.kws.length-8} más</span>`:''}
      </div>
    </div>`).join('');
}

function analyzeQSWithAVI() {
  const lowQS = gKwData.filter(k => k.qs > 0 && k.qs <= 5);
  if (!lowQS.length) { showToast('No hay keywords con QS bajo para analizar', 'warn'); return; }
  const ctx = getContext('google');
  const kwLines = lowQS.slice(0, 30).map(k =>
    `• ${k.keyword} [QS:${k.qs}] AD:${k.qs_ad||'?'} LP:${k.qs_landing||'?'} CTR:${k.qs_ctr||'?'} | ${k.impressions} imp / ${k.clicks} clics / ${k.conversions} conv`
  ).join('\n');
  const prompt = `Analiza estas ${lowQS.length} keywords con Quality Score bajo (≤5) en Google Ads. Para cada grupo de problema (anuncio, landing, CTR esperado) da recomendaciones específicas y accionables para mejorar el QS. Prioriza las que tienen más impresiones.\n\nKeywords con QS bajo:\n${kwLines}\n\nContexto del negocio: ${ctx}`;
  callAVI(prompt, ctx, 'avi-g-keywords', 'AVI analizando Quality Score...', 'google');
  const panel = document.getElementById('avi-g-keywords');
  if (panel) panel.style.display = 'block';
}

let _kwNegKw='', _kwNegMatch='exact';
function openKwNegModal(keyword, matchType){
  _kwNegKw=keyword; _kwNegMatch=matchType||'exact';
  document.getElementById('kw-neg-kw-display').textContent=keyword;
  const sel=document.getElementById('kw-neg-match');
  if(sel) sel.value=matchType==='phrase'?'phrase':matchType==='broad'?'broad':'exact';
  const res=document.getElementById('kw-neg-result'); if(res){res.style.display='none';res.textContent='';}
  const m=document.getElementById('kw-neg-modal'); if(m) m.style.display='flex';
}
function closeKwNegModal(){
  const m=document.getElementById('kw-neg-modal'); if(m) m.style.display='none';
}
async function confirmKwNegative(){
  if(!_kwNegKw||!currentClient||currentClient==='all') return;
  const match=document.getElementById('kw-neg-match')?.value||'exact';
  const addCtx=document.getElementById('kw-neg-ctx')?.checked||false;
  const res=document.getElementById('kw-neg-result');
  const btn=document.querySelector('#kw-neg-modal button');
  if(btn){btn.disabled=true;btn.textContent='Aplicando…';}
  try {
    const d=await fetch(`api/google-ads.php?action=apply_negatives&client_id=${currentClient}`,{
      method:'POST',
      headers:{'Content-Type':'application/json','X-CSRF-Token':document.querySelector('meta[name="dm-csrf"]')?.content||''},
      body:JSON.stringify({keywords:[{keyword:_kwNegKw,match_type:match.toUpperCase()}],add_to_context:addCtx})
    }).then(r=>r.json());
    if(res){
      res.style.display='block';
      if(d.ok){
        res.style.color='var(--g)';
        res.textContent=`✓ ${d.message||'Negativa aplicada correctamente'}`;
        setTimeout(()=>closeKwNegModal(),1800);
      } else {
        res.style.color='var(--red)';
        res.textContent='✗ '+(d.error||'Error al aplicar');
      }
    }
  } catch(e){
    if(res){res.style.display='block';res.style.color='var(--red)';res.textContent='✗ Error de conexión';}
  }
  if(btn){btn.disabled=false;btn.textContent='Aplicar negativa';}
}

let gStData=[];
async function loadGSearchTerms(){
  if(!currentClient||currentClient==='all') return;
  const load=document.getElementById('g-st-loading'); const cont=document.getElementById('g-st-content');
  if(load) load.style.display='block'; if(cont) cont.innerHTML='';
  try {
    const d=await fetch(gUrl('search_terms')).then(r=>r.json());
    if(load) load.style.display='none';
    if(!d.ok){if(cont) cont.innerHTML=`<p style="color:var(--red);font-size:13px">✗ ${escapeHtml(d.error||'Error')}</p>`;return;}
    gStData=d.search_terms||[]; filterGSearchTerms();
  } catch(e){ if(load) load.style.display='none'; if(cont) cont.innerHTML='<p style="color:var(--red);font-size:13px">✗ Error de conexión</p>'; }
}
function filterGSearchTerms(){
  const search=(document.getElementById('g-st-search')?.value||'').toLowerCase();
  const filter=document.getElementById('g-st-filter')?.value||'all';
  const cont=document.getElementById('g-st-content'); if(!cont) return;
  let list=gStData;
  if(search) list=list.filter(t=>(t.term||'').toLowerCase().includes(search));
  if(filter==='no_conv') list=list.filter(t=>!(t.conversions>0));
  if(filter==='conv')    list=list.filter(t=>t.conversions>0);
  if(!list.length){cont.innerHTML='<p style="color:var(--m);font-size:13px">No hay términos que mostrar.</p>';return;}
  cont.innerHTML='<table class="tbl"><thead><tr>'+
    '<th data-tip="Término exacto que escribió el usuario en Google y que activó tu anuncio">Término de búsqueda</th>'+
    '<th data-tip="Campaña que capturó esta búsqueda">Campaña</th>'+
    '<th data-tip="Grupo de anuncios que activó el anuncio para este término">Grupo</th>'+
    '<th data-tip="Impresiones: veces que tu anuncio apareció para esta búsqueda">Imp.</th>'+
    '<th data-tip="Clics que recibiste para este término de búsqueda">Clics</th>'+
    '<th data-tip="Click-Through Rate: % de impresiones que resultaron en clics">CTR</th>'+
    '<th data-tip="Gasto total generado por este término. Rojo = gasto sin conversiones = candidato a negativo">Gasto</th>'+
    '<th data-tip="Conversiones generadas desde este término. 0 + alto gasto = posible negativo">Conv.</th>'+
    '<th data-tip="Estado del término en tu cuenta: Ninguna (libre), En lista (keyword activa), Negativa (excluido)">Estado</th>'+
    '</tr></thead><tbody>'+
    list.map(t=>`<tr style="${!t.conversions&&t.spend>0?'background:rgba(248,113,113,.04)':''}">
      <td style="max-width:200px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">${escapeHtml(t.term)}</td>
      <td style="font-size:11px;color:var(--m)">${escapeHtml(t.campaign)}</td>
      <td style="font-size:11px;color:var(--m)">${escapeHtml(t.adgroup)}</td>
      <td>${(t.impressions||0).toLocaleString('es-CO')}</td><td>${(t.clicks||0).toLocaleString('es-CO')}</td>
      <td>${t.ctr||0}%</td>
      <td style="color:${t.spend>0&&!t.conversions?'var(--red)':'var(--t)'}">${gFmt(t.spend||0)}</td>
      <td style="color:${t.conversions>0?'var(--g)':'var(--m)'}">${t.conversions||0}</td>
      <td style="font-size:11px">${t.status==='ADDED'?'<span style="color:var(--g)">En lista</span>':t.status==='EXCLUDED'?'<span style="color:var(--red)">Negativa</span>':'<span style="color:var(--m)">Ninguna</span>'}</td>
    </tr>`).join('')+'</tbody></table>';
}

async function loadNegContext() {
  if (!currentClient || currentClient==='all') return;
  try {
    const d = await fetch(`api/google-ads.php?client_id=${currentClient}&action=get_neg_context`).then(r=>r.json());
    if (!d.ok) return;
    const p=document.getElementById('g-neg-permanent'); if(p) p.value=d.permanent||'';
    const dl=document.getElementById('g-neg-daily');    if(dl) dl.value=d.daily||'';
  } catch(e){}
  loadTrustData();
}

async function saveNegContext() {
  if (!currentClient || currentClient==='all') return;
  const permanent=document.getElementById('g-neg-permanent')?.value||'';
  const daily=document.getElementById('g-neg-daily')?.value||'';
  try {
    const d=await fetch(`api/google-ads.php?client_id=${currentClient}&action=save_neg_context`,{
      method:'POST',headers:{'Content-Type':'application/json'},
      body:JSON.stringify({permanent,daily})
    }).then(r=>r.json());
    const sv=document.getElementById('g-neg-ctx-saved');
    if(d.ok&&sv){sv.style.display='inline';setTimeout(()=>sv.style.display='none',2500);}
    else showToast('Error guardando contexto','var(--red)');
  } catch(e){ showToast('Error de conexión','var(--red)'); }
}

// Resultados del análisis guardados para poder aplicarlos
let gNegAutoData=[], gNegReviewData=[];
let gTrustAutoMode=false;

async function loadTrustData(){
  if(!currentClient||currentClient==='all') return;
  try {
    const d=await fetch(`api/google-ads.php?client_id=${currentClient}&action=get_trust`).then(r=>r.json());
    if(!d.ok) return;
    const badge=document.getElementById('g-trust-badge');
    const bar=document.getElementById('g-trust-bar');
    const count=document.getElementById('g-trust-count');
    const pct=document.getElementById('g-trust-pct');
    const last=document.getElementById('g-trust-last');
    const btn=document.getElementById('g-trust-toggle-btn');
    const info=document.getElementById('g-trust-auto-info');
    gTrustAutoMode=d.auto_mode||false;
    const approved=d.approved||0; const threshold=d.threshold||30; const progress=d.progress||0;
    if(bar) bar.style.width=progress+'%';
    if(count) count.textContent=`${approved} / ${threshold} aprobaciones`;
    if(pct) pct.textContent=progress+'%';
    if(d.auto_mode){
      if(badge){badge.textContent='Automático ✓';badge.style.background='rgba(34,197,94,.15)';badge.style.color='var(--g)';}
      if(btn){btn.textContent='Desactivar modo automático';btn.style.background='rgba(248,113,113,.15)';btn.style.borderColor='rgba(248,113,113,.3)';btn.style.color='var(--red)';}
      const since=d.trust?.auto_since||'';
      if(info){info.style.display='inline';info.textContent=since?`Activo desde ${since}`:'';}
    } else {
      if(badge){badge.textContent='Aprendiendo';badge.style.background='rgba(234,179,8,.15)';badge.style.color='var(--y)';}
      if(btn){btn.textContent=approved>=threshold?'Activar modo automático ✓':'Activar modo automático';btn.style.background='rgba(74,158,248,.15)';btn.style.borderColor='rgba(74,158,248,.3)';btn.style.color='var(--b)';}
      if(info) info.style.display='none';
    }
    const summary=d.trust?.last_summary;
    if(last){
      if(d.auto_mode && summary?.date){
        last.innerHTML=`Último ciclo automático: <strong>${escapeHtml(summary.date)}</strong> — ${summary.applied||0} negativas aplicadas de ${summary.terms_analyzed||0} términos analizados`;
      } else if(!d.auto_mode && approved<threshold){
        last.textContent=`Necesitas ${threshold-approved} aprobaciones más para activar el modo automático.`;
      } else if(!d.auto_mode && approved>=threshold){
        last.textContent='Ya tienes suficiente historial. Puedes activar el modo automático.';
        if(badge){badge.style.background='rgba(34,197,94,.08)';badge.style.color='var(--g)';badge.textContent='Listo para automático';}
      }
    }
  } catch(e){}
}

async function toggleAutoMode(){
  if(!currentClient||currentClient==='all') return;
  const newMode=!gTrustAutoMode;
  if(newMode && !confirm('¿Activar modo automático? El SuperCerebro aplicará negativos directamente en Google Ads cada noche sin revisión manual.')) return;
  try {
    const d=await fetch(`api/google-ads.php?client_id=${currentClient}&action=set_trust_mode`,{
      method:'POST',headers:{'Content-Type':'application/json'},
      body:JSON.stringify({auto_mode:newMode})
    }).then(r=>r.json());
    if(d.ok){ showToast(newMode?'✓ Modo automático activado':'Modo automático desactivado',newMode?'var(--g)':'var(--m)'); loadTrustData(); }
    else showToast('✗ '+(d.error||'Error'),'var(--red)');
  } catch(e){ showToast('✗ Error de conexión','var(--red)'); }
}

let gPeriodTermsData = [];
async function loadPeriodTerms() {
  if (!currentClient || currentClient==='all') { showToast('Selecciona un cliente primero', 'var(--y)'); return; }
  const load = document.getElementById('g-terms-loading');
  const cont = document.getElementById('g-terms-content');
  const acts = document.getElementById('g-terms-actions');
  if (load) { load.style.display='block'; } if (cont) cont.innerHTML=''; if (acts) acts.style.display='none';
  try {
    const filter = document.getElementById('g-terms-filter')?.value || 'no_conv';
    const d = await fetch(gUrl('search_terms')).then(r=>r.json());
    if (load) load.style.display='none';
    if (!d.ok) { if(cont) cont.innerHTML=`<p style="color:var(--red);font-size:12px">✗ ${escapeHtml(d.error||'Error')}</p>`; return; }
    let terms = d.search_terms || [];
    if (filter === 'no_conv') terms = terms.filter(t => !(t.conversions > 0));
    else if (filter === 'with_conv') terms = terms.filter(t => t.conversions > 0);
    gPeriodTermsData = terms;
    if (!terms.length) { if(cont) cont.innerHTML='<p style="color:var(--m);font-size:12px">No hay términos para este período y filtro.</p>'; return; }
    if (cont) cont.innerHTML = '<div style="max-height:320px;overflow-y:auto"><table class="tbl"><thead><tr>' +
      '<th style="width:28px"><input type="checkbox" id="g-terms-all" onchange="toggleAllTerms(this.checked)"></th>' +
      '<th data-tip="Término exacto que escribió el usuario en Google">Término</th>'+
      '<th data-tip="Campaña que capturó esta búsqueda">Campaña</th>'+
      '<th data-tip="Veces que tu anuncio apareció para esta búsqueda">Imp.</th>'+
      '<th data-tip="Clics que recibiste">Clics</th>'+
      '<th data-tip="Gasto generado. Rojo = gastó sin convertir — candidato a negativo">Gasto</th>'+
      '<th data-tip="Conversiones generadas. 0 con alto gasto = ideal para negativar">Conv.</th>'+
      '</tr></thead><tbody>' +
      terms.map((t,i) => `<tr>
        <td><input type="checkbox" class="g-term-cb" data-i="${i}" onchange="updateTermsCount()"></td>
        <td style="font-weight:500">${escapeHtml(t.term)}</td>
        <td style="font-size:11px;color:var(--m);max-width:120px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">${escapeHtml(t.campaign)}</td>
        <td>${(t.impressions||0).toLocaleString('es-CO')}</td>
        <td>${t.clicks||0}</td>
        <td style="color:${t.spend>0&&!t.conversions?'var(--red)':'var(--t)'}">${gFmt(t.spend||0)}</td>
        <td style="color:${t.conversions>0?'var(--g)':'var(--m)'}">${t.conversions||0}</td>
      </tr>`).join('') + '</tbody></table></div>';
    if (acts) { acts.style.display='block'; updateTermsCount(); }
  } catch(e) { if(load) load.style.display='none'; if(cont) cont.innerHTML='<p style="color:var(--red);font-size:12px">✗ Error de conexión</p>'; }
}
function toggleAllTerms(checked) {
  document.querySelectorAll('.g-term-cb').forEach(cb => cb.checked=checked);
  updateTermsCount();
}
function updateTermsCount() {
  const n = document.querySelectorAll('.g-term-cb:checked').length;
  const el = document.getElementById('g-terms-count');
  if (el) el.textContent = n ? `${n} término${n>1?'s':''} seleccionado${n>1?'s':''}` : '';
}
async function applyTermsAsNegatives() {
  const checked = [...document.querySelectorAll('.g-term-cb:checked')].map(cb => parseInt(cb.dataset.i));
  if (!checked.length) { alert('Selecciona al menos un término'); return; }
  const keywords = checked.map(i => ({ keyword: gPeriodTermsData[i].term, match_type: 'exact' }));
  const btn = document.querySelector('#g-terms-actions button');
  if (btn) { btn.textContent='Aplicando…'; btn.disabled=true; }
  try {
    const d = await fetch(`api/google-ads.php?client_id=${currentClient}&action=apply_negatives`, {
      method:'POST', headers:{'Content-Type':'application/json'},
      body: JSON.stringify({ keywords, add_to_context: true })
    }).then(r=>r.json());
    if (!d.ok) { alert('Error: '+(d.error||'desconocido')); }
    else {
      alert(`✓ ${d.message}`);
      document.querySelectorAll('.g-term-cb:checked').forEach(cb => { cb.closest('tr').style.opacity='0.4'; cb.checked=false; });
      updateTermsCount();
      loadNegContext();
    }
  } catch(e) { alert('Error de conexión'); }
  if (btn) { btn.textContent='Añadir seleccionados como negativos'; btn.disabled=false; }
}

async function loadAccountNegatives(){
  if(!currentClient||currentClient==='all') return;
  const load=document.getElementById('g-acct-neg-loading');
  const cont=document.getElementById('g-acct-neg-content');
  if(load) load.style.display='block';
  if(cont) cont.innerHTML='';
  try {
    const d=await fetch(`api/google-ads.php?client_id=${currentClient}&action=get_account_negatives`).then(r=>r.json());
    if(load) load.style.display='none';
    if(!d.ok){if(cont) cont.innerHTML=`<span style="color:var(--red);font-size:12px">✗ ${escapeHtml(d.error||'Error')}</span>`;return;}
    const list=d.negatives||[];
    if(!list.length){if(cont) cont.innerHTML='<span style="font-size:12px;color:var(--m)">No hay negativos a nivel de cuenta aún.</span>';return;}
    const mtColor=m=>m==='exact'?'var(--b)':m==='phrase'?'var(--g)':'var(--m)';
    if(cont) cont.innerHTML=`<div style="font-size:12px;color:var(--m);margin-bottom:8px">${list.length} negativas a nivel de cuenta</div>`+
      '<div style="display:flex;flex-wrap:wrap;gap:6px">'+
      list.map(n=>`<span style="padding:3px 9px;border-radius:5px;background:var(--bg3);border:1px solid var(--br2);font-size:11px;color:${mtColor(n.match_type)}">${escapeHtml(n.keyword)}<span style="color:var(--m);margin-left:4px">[${n.match_type}]</span></span>`).join('')+
      '</div>';
  } catch(e){if(load) load.style.display='none'; if(cont) cont.innerHTML='<span style="font-size:12px;color:var(--red)">Error de conexión</span>';}
}

function renderNegList(list, containerEl) {
  if(!list.length){containerEl.innerHTML='<p style="font-size:12px;color:var(--m);padding:8px 0">Ninguno</p>';return;}
  const mtColor=m=>m==='exact'?'var(--b)':m==='phrase'?'var(--g)':'var(--m)';
  containerEl.innerHTML='<div style="display:grid;gap:8px">'+list.map((s,i)=>`
    <div style="background:var(--bg2);border:1px solid var(--br);border-radius:8px;padding:11px 14px;display:flex;gap:10px;align-items:flex-start">
      <div style="flex:1">
        <div style="font-size:13px;font-weight:600;margin-bottom:3px">${escapeHtml(s.keyword)}</div>
        <div style="font-size:12px;color:var(--m)">${escapeHtml(s.reason)}</div>
      </div>
      <span style="font-size:11px;padding:3px 8px;border-radius:4px;background:rgba(74,158,248,.1);color:${mtColor(s.match_type)};flex-shrink:0">${s.match_type||'exact'}</span>
    </div>`).join('')+'</div>';
}

function renderNegReviewList(list, containerEl) {
  if(!list.length){containerEl.innerHTML='<p style="font-size:12px;color:var(--m);padding:8px 0">Ninguno</p>';return;}
  const mtColor=m=>m==='exact'?'var(--b)':m==='phrase'?'var(--g)':'var(--m)';
  containerEl.innerHTML='<div style="display:grid;gap:8px">'+list.map((s,i)=>`
    <label style="background:var(--bg2);border:1px solid var(--br);border-radius:8px;padding:11px 14px;display:flex;gap:12px;align-items:flex-start;cursor:pointer">
      <input type="checkbox" class="g-neg-review-chk" data-idx="${i}" style="margin-top:3px;accent-color:var(--y);flex-shrink:0">
      <div style="flex:1">
        <div style="font-size:13px;font-weight:600;margin-bottom:3px">${escapeHtml(s.keyword)}</div>
        <div style="font-size:12px;color:var(--m)">${escapeHtml(s.reason)}</div>
      </div>
      <span style="font-size:11px;padding:3px 8px;border-radius:4px;background:rgba(74,158,248,.1);color:${mtColor(s.match_type)};flex-shrink:0">${s.match_type||'exact'}</span>
    </label>`).join('')+'</div>';
}

async function applyNegatives(keywords, addToContext) {
  if(!currentClient||currentClient==='all'||!keywords.length) return false;
  try {
    const d=await fetch(`api/google-ads.php?client_id=${currentClient}&action=apply_negatives`,{
      method:'POST',headers:{'Content-Type':'application/json'},
      body:JSON.stringify({keywords,add_to_context:addToContext})
    }).then(r=>r.json());
    if(!d.ok){ showToast('✗ '+(d.error||'Error al aplicar'),'var(--red)'); return false; }
    showToast(`✓ ${d.message}`,'var(--g)');
    if(addToContext){ loadNegContext(); loadTrustData(); }
    return true;
  } catch(e){ showToast('✗ Error de conexión','var(--red)'); return false; }
}

async function applyAutoNegatives(){
  if(!gNegAutoData.length){showToast('Sin datos de análisis. Ejecuta el análisis primero.','var(--m)');return;}
  const btn=document.getElementById('g-neg-apply-auto-btn');
  if(btn){btn.disabled=true;btn.textContent='Aplicando…';}
  const ok=await applyNegatives(gNegAutoData, true);
  if(btn){btn.disabled=false;btn.textContent=ok?'✓ Aplicadas':'Aplicar todos a Google Ads';}
  if(ok) loadAccountNegatives();
}

async function applySelectedNegatives(){
  const chks=[...document.querySelectorAll('.g-neg-review-chk:checked')];
  if(!chks.length){showToast('Selecciona al menos una palabra para aplicar','var(--m)');return;}
  const selected=chks.map(ch=>gNegReviewData[parseInt(ch.dataset.idx)]).filter(Boolean);
  const ok=await applyNegatives(selected, true);
  if(ok){
    // Desmarcar y recargar
    chks.forEach(ch=>{ch.checked=false;ch.closest('label').style.opacity='0.4';});
    loadAccountNegatives();
  }
}

async function loadGAINegatives(){
  const load=document.getElementById('g-neg-loading');
  const autoSec=document.getElementById('g-neg-auto');
  const revSec=document.getElementById('g-neg-review');
  const autoList=document.getElementById('g-neg-auto-list');
  const revList=document.getElementById('g-neg-review-list');
  const lbl=document.getElementById('g-neg-period-lbl');
  if(lbl) lbl.textContent={'7d':'7 días','14d':'14 días','30d':'30 días','60d':'60 días','90d':'90 días'}[currentGPeriod]||'30 días';
  if(load) load.style.display='block';
  if(autoSec) autoSec.style.display='none';
  if(revSec)  revSec.style.display='none';
  gNegAutoData=[]; gNegReviewData=[];
  try {
    const d=await fetch(gUrl('ai_negatives')).then(r=>r.json());
    if(load) load.style.display='none';
    if(!d.ok){ showToast('✗ '+(d.error||'Error al analizar'),'var(--red)'); return; }
    if(d.message){ showToast('✓ '+d.message,'var(--g)'); return; }
    const auto=d.auto||[]; const review=d.review||[];
    // Filtrar términos ya en contexto permanente
    const permLines = (document.getElementById('g-neg-permanent')?.value||'').split('\n').map(s=>s.trim().toLowerCase()).filter(Boolean);
    const filtAuto   = permLines.length ? auto.filter(t   => !permLines.includes((t.keyword||'').toLowerCase())) : auto;
    const filtReview = permLines.length ? review.filter(t => !permLines.includes((t.keyword||'').toLowerCase())) : review;
    const skipped = (auto.length+review.length) - (filtAuto.length+filtReview.length);
    gNegAutoData=filtAuto; gNegReviewData=filtReview;
    const analyzed=d.terms_analyzed||0;
    const skipNote = skipped ? ` · ${skipped} ya en permanentes omitidos` : '';
    showToast(`Analizados ${analyzed} términos → ${filtAuto.length} seguros, ${filtReview.length} para revisar${skipNote}`,'var(--g)');
    if(filtAuto.length && autoSec && autoList){ autoSec.style.display='block'; renderNegList(filtAuto,autoList); }
    if(filtReview.length && revSec && revList){ revSec.style.display='block'; renderNegReviewList(filtReview,revList); }
    if(!filtAuto.length&&!filtReview.length) showToast('Sin sugerencias nuevas para este período','var(--m)');
  } catch(e){ if(load) load.style.display='none'; showToast('✗ Error de conexión','var(--red)'); }
}

let _gDailyLoaded = '';
function _gDailyShowError(msg) {
  const errEl = document.getElementById('g-daily-error');
  const msgEl = document.getElementById('g-daily-error-msg');
  const load   = document.getElementById('g-daily-loading');
  if (load)  load.style.display='none';
  if (msgEl) msgEl.textContent = msg;
  if (errEl) errEl.style.display='block';
}
async function loadDailyReview() {
  if (!currentClient || currentClient==='all') return;
  const today = new Date().toISOString().slice(0,10);
  const cacheKey = currentClient + today;
  if (_gDailyLoaded === cacheKey) return;
  const load  = document.getElementById('g-daily-loading');
  const cont  = document.getElementById('g-daily-content');
  const empty = document.getElementById('g-daily-empty');
  const errEl = document.getElementById('g-daily-error');
  if (load)  load.style.display='block';
  if (cont)  cont.style.display='none';
  if (empty) empty.style.display='none';
  if (errEl) errEl.style.display='none';
  try {
    const r = await fetch(`api/google-ads.php?client_id=${currentClient}&action=daily_review`);
    const d = await r.json();
    if (load) load.style.display='none';
    if (!d.ok) { _gDailyShowError(d.error || 'Error al cargar la revisión diaria'); return; }
    const dateEl = document.getElementById('g-daily-date');
    if (dateEl) dateEl.textContent = d.date || '';
    if (!d.total_terms) { if (empty) empty.style.display='block'; _gDailyLoaded=cacheKey; return; }
    if (cont) cont.style.display='block';
    const sumEl = document.getElementById('g-daily-summary');
    if (sumEl) sumEl.textContent = d.summary || '';
    const _permLines = (document.getElementById('g-neg-permanent')?.value||'').split('\n').map(s=>s.trim().toLowerCase()).filter(Boolean);
    const negs = (d.negativas || []).filter(n => !_permLines.includes((n.keyword||'').toLowerCase()));
    const negSec   = document.getElementById('g-daily-neg-section');
    const negChips = document.getElementById('g-daily-neg-chips');
    if (negs.length && negSec && negChips) {
      negSec.style.display='block';
      const validMt = {'exact':'exact','phrase':'phrase','broad':'broad'};
      negChips.innerHTML = negs.map(n => {
        const mt = validMt[(n.match_type||'').toLowerCase()] || 'broad';
        return `<label style="display:flex;align-items:center;gap:5px;background:rgba(232,39,42,.1);border:1px solid rgba(232,39,42,.25);border-radius:20px;padding:4px 10px;cursor:pointer;font-size:11px" title="${escapeHtml(n.reason||'')}">
          <input type="checkbox" data-kw="${escapeHtml(n.keyword||'')}" data-mt="${mt}" checked style="cursor:pointer">
          <span style="color:var(--t)">${escapeHtml(n.keyword||'')}</span>
          <span style="color:var(--m);font-size:10px">${mt}</span>
        </label>`;
      }).join('');
      const badge = document.getElementById('g-daily-badge');
      if (badge) { badge.textContent=negs.length; badge.style.display='inline-block'; }
    }
    const keeps    = d.conservar || [];
    const keepSec  = document.getElementById('g-daily-keep-section');
    const keepChips = document.getElementById('g-daily-keep-chips');
    if (keeps.length && keepSec && keepChips) {
      keepSec.style.display='block';
      keepChips.innerHTML = keeps.map(k =>
        `<span style="background:rgba(34,201,122,.1);border:1px solid rgba(34,201,122,.25);border-radius:20px;padding:4px 10px;font-size:11px;color:var(--g)" title="${escapeHtml(k.reason||'')}">${escapeHtml(k.keyword||'')}</span>`
      ).join('');
    }
    _gDailyLoaded = cacheKey;
  } catch(e) { _gDailyShowError('Error de conexión: ' + (e.message||'intenta de nuevo')); }
}

async function applyDailyNegatives() {
  const chips = document.querySelectorAll('#g-daily-neg-chips input[type=checkbox]:checked:not(:disabled)');
  if (!chips.length) { showToast('Selecciona al menos un término','var(--y)'); return; }
  const kws = Array.from(chips).map(c => ({keyword: c.dataset.kw, match_type: c.dataset.mt}));
  const infoEl = document.getElementById('g-daily-apply-info');
  if (infoEl) infoEl.textContent = 'Aplicando…';
  try {
    const d = await fetch(`api/google-ads.php?client_id=${currentClient}&action=apply_negatives`, {
      method:'POST', body: JSON.stringify({keywords: kws})
    }).then(r=>r.json());
    if (!d.ok) { if(infoEl) infoEl.textContent=''; showToast('✗ '+(d.error||'Error'),'var(--red)'); return; }
    showToast(`✓ ${kws.length} negativos aplicados a las campañas`,'var(--g)');
    if (infoEl) infoEl.textContent = `✓ ${kws.length} aplicados`;
    chips.forEach(c => { c.closest('label').style.opacity='0.5'; c.disabled=true; });
    const badge = document.getElementById('g-daily-badge');
    if (badge) {
      const rem = document.querySelectorAll('#g-daily-neg-chips input[type=checkbox]:not(:disabled)').length;
      if (rem) badge.textContent=rem; else badge.style.display='none';
    }
  } catch(e) { if(infoEl) infoEl.textContent=''; showToast('✗ Error de conexión','var(--red)'); }
}

async function loadGAuction(){
  const campId=document.getElementById('g-auction-camp')?.value||'';
  const load=document.getElementById('g-auction-loading'); const cont=document.getElementById('g-auction-content');
  if(load) load.textContent='Cargando impression share por campaña…';
  if(cont) cont.innerHTML='';
  try {
    const d=await fetch(gUrl('auction',campId?`&campaign_id=${campId}`:'')).then(r=>r.json());
    if(load) load.textContent='';
    if(!d.ok){if(load) load.textContent='✗ '+escapeHtml(d.error||'Error');return;}
    const list=d.campaigns||[];
    if(!list.length){if(cont) cont.innerHTML='<p style="color:var(--m);font-size:13px">No hay datos disponibles para este período.</p>';return;}
    const isColor=v=>v>=60?'var(--g)':v>=30?'var(--y)':'var(--red)';
    if(cont) cont.innerHTML=
      '<div style="font-size:11px;color:var(--m);margin-bottom:10px">Impression Share = % de subastas en las que apareciste. Perdido por ranking o presupuesto = oportunidad de mejora.</div>'+
      '<table class="tbl"><thead><tr>'+
      '<th data-tip="Nombre de la campaña">Campaña</th>'+
      '<th data-tip="Impression Share total: % de subastas en que apareciste vs. el total disponible. 70%+ es muy bueno">IS%</th>'+
      '<th data-tip="Top IS: % de veces que apareciste en las posiciones superiores (encima de los resultados orgánicos)">Top IS%</th>'+
      '<th data-tip="Absolute Top IS: % de veces en la posición #1 absoluta — máxima visibilidad posible">Abs Top IS%</th>'+
      '<th data-tip="Subastas perdidas por bajo Quality Score o puja insuficiente. Mejorar anuncios y landing pages lo reduce">Perdido ranking%</th>'+
      '<th data-tip="Subastas perdidas por presupuesto agotado. Aumentar el budget diario resuelve este problema">Perdido budget%</th>'+
      '<th data-tip="Inversión total de la campaña en el período">Inversión</th>'+
      '<th data-tip="Estado actual de la campaña">Estado</th>'+
      '</tr></thead><tbody>'+
      list.map(c=>`<tr>
        <td style="max-width:180px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap" title="${escapeHtml(c.name)}">${escapeHtml(c.name)}</td>
        <td style="font-weight:700;color:${isColor(c.is)}">${c.is}%</td>
        <td style="color:${isColor(c.top_is)}">${c.top_is}%</td>
        <td style="color:${isColor(c.abs_top)}">${c.abs_top}%</td>
        <td style="color:${c.lost_rank>30?'var(--red)':c.lost_rank>15?'var(--y)':'var(--t)'}">${c.lost_rank}%</td>
        <td style="color:${c.lost_budget>20?'var(--red)':c.lost_budget>10?'var(--y)':'var(--t)'}">${c.lost_budget}%</td>
        <td>${gFmt(c.spend)}</td>
        <td>${gSt(c.status)}</td>
      </tr>`).join('')+'</tbody></table>';
  } catch(e){ if(load) load.textContent='✗ Error de conexión'; }
}

async function loadGBilling(){
  const load=document.getElementById('g-billing-loading'); const cont=document.getElementById('g-billing-content');
  if(load) load.style.display='block'; if(cont) cont.innerHTML='';
  try {
    const d=await fetch(gUrl('billing')).then(r=>r.json());
    if(load) load.style.display='none';
    if(!d.ok){if(cont) cont.innerHTML=`<p style="color:var(--red);font-size:13px">✗ ${escapeHtml(d.error||'Error')}</p>`;return;}
    const alerts=d.alerts||[]; const budgets=d.budgets||[];
    let html='';
    if(alerts.length) html+=alerts.map(a=>`<div style="background:rgba(248,113,113,.1);border:1px solid rgba(248,113,113,.3);border-radius:8px;padding:12px 16px;margin-bottom:10px;color:#f87171;font-size:13px">⚠ ${escapeHtml(a)}</div>`).join('');
    else html+='<div style="background:rgba(34,201,122,.08);border:1px solid rgba(34,201,122,.2);border-radius:8px;padding:12px 16px;margin-bottom:16px;color:var(--g);font-size:13px">✓ Sin alertas de facturación detectadas</div>';
    if(budgets.length) html+='<div class="card"><div class="chd"><div class="ctit">Presupuestos de cuenta</div></div><table class="tbl"><thead><tr><th>Estado</th><th>Gastado</th><th>Límite</th><th>% Uso</th></tr></thead><tbody>'+
      budgets.map(b=>{const pct=b.limit>0?Math.round(b.served/b.limit*100):0; return `<tr><td style="color:${b.status==='APPROVED'?'var(--g)':'var(--y)'}">${b.status}</td><td>${gFmt(b.served)}</td><td>${b.limit>0?gFmt(b.limit):'Sin límite'}</td><td style="color:${pct>90?'var(--red)':pct>70?'var(--y)':'var(--g)'}">${pct?pct+'%':'—'}</td></tr>`}).join('')+'</tbody></table></div>';
    if(cont) cont.innerHTML=html;
  } catch(e){ if(load) load.style.display='none'; if(cont) cont.innerHTML='<p style="color:var(--red);font-size:13px">✗ Error de conexión</p>'; }
}

async function loadGBillingAlert(){
  try {
    const d=await fetch(gUrl('billing')).then(r=>r.json());
    const al=document.getElementById('g-billing-alert');
    if(al) al.style.display=d.ok&&d.has_issue?'block':'none';
  } catch(e){}
}

function populateGCampFilters(){
  ['g-ag-camp-filter','g-ads-camp-filter','g-kw-camp-filter','g-auction-camp'].forEach(id=>{
    const sel=document.getElementById(id); if(!sel) return;
    const cur=sel.value;
    sel.innerHTML='<option value="">Todas las campañas</option>'+gCampaignsList.map(c=>`<option value="${escapeHtml(c.id)}">${escapeHtml(c.name)}</option>`).join('');
    if(cur) sel.value=cur;
  });
}

let currentGSeg = 'device';
function switchGSeg(seg, el) {
  currentGSeg = seg;
  document.querySelectorAll('.g-seg-btn').forEach(b => {
    b.style.background='var(--bg3)'; b.style.borderColor='var(--br2)'; b.style.color='var(--m)';
  });
  if(el){ el.style.background='rgba(232,39,42,.15)'; el.style.borderColor='rgba(232,39,42,.3)'; el.style.color='var(--red)'; }
  loadGSegments(seg);
}

async function loadGSegments(seg) {
  currentGSeg = seg || currentGSeg;
  const load=document.getElementById('g-seg-loading'); const cont=document.getElementById('g-seg-content');
  if(load) load.style.display='block'; if(cont) cont.innerHTML='';
  try {
    const d=await fetch(gUrl('segments',`&seg_type=${currentGSeg}`)).then(r=>r.json());
    if(load) load.style.display='none';
    if(!d.ok){if(cont) cont.innerHTML=`<p style="color:var(--red);font-size:13px">✗ ${escapeHtml(d.error||'Error')}</p>`;return;}
    const list=d.data||[];
    if(!list.length){if(cont) cont.innerHTML='<p style="color:var(--m);font-size:13px">No hay datos de segmentos para este período.</p>';return;}
    const deviceIcon={mobile:'📱',tablet:'📟',desktop:'🖥',connected_tv:'📺',other:'❓'};
    const nameKey=currentGSeg==='device'?'device':currentGSeg==='location'?'location':'age';
    const nameLabel=currentGSeg==='device'?'Dispositivo':currentGSeg==='location'?'Ubicación':'Rango de edad';
    // Calcular totales para % de participación
    const totalImp=list.reduce((s,r)=>s+(r.impressions||0),0);
    const totalSpend=list.reduce((s,r)=>s+(r.spend||0),0);
    const segTip = currentGSeg==='device'?'Dispositivo desde el que el usuario realizó la búsqueda':currentGSeg==='location'?'País o región desde donde se originó la búsqueda':'Rango de edad estimado del usuario';
    if(cont) cont.innerHTML='<table class="tbl"><thead><tr>'+
      `<th data-tip="${segTip}">${nameLabel}</th>`+
      '<th data-tip="Impresiones: veces que aparecieron tus anuncios en este segmento">Imp.</th>'+
      '<th data-tip="Participación porcentual de este segmento en el total de impresiones">Part.%</th>'+
      '<th data-tip="Clics recibidos desde este segmento">Clics</th>'+
      '<th data-tip="Click-Through Rate: % de impresiones que resultaron en clics">CTR</th>'+
      '<th data-tip="Costo por Clic promedio en este segmento">CPC</th>'+
      '<th data-tip="Inversión total gastada en este segmento">Gasto</th>'+
      '<th data-tip="Porcentaje del gasto total que representa este segmento">Gasto%</th>'+
      '<th data-tip="Conversiones completadas originadas desde este segmento">Conv.</th>'+
      '<th data-tip="Costo por Adquisición en este segmento: gasto / conversiones">CPA</th>'+
      '<th data-tip="Return on Ad Spend en este segmento: valor generado / gasto">ROAS</th>'+
      '</tr></thead><tbody>'+list.map(r=>{
        const name=currentGSeg==='device'?(deviceIcon[r.device]||'')+(r.device||'—').replace(/_/g,' '):escapeHtml(r[nameKey]||'—');
        const impPct=totalImp>0?Math.round(r.impressions/totalImp*100):0;
        const spendPct=totalSpend>0?Math.round(r.spend/totalSpend*100):0;
        return `<tr>
          <td style="font-weight:600;text-transform:capitalize">${name}</td>
          <td>${(r.impressions||0).toLocaleString('es-CO')}</td>
          <td><div style="display:flex;align-items:center;gap:6px"><div style="width:${impPct*0.6}px;height:6px;background:var(--b);border-radius:3px;min-width:2px"></div>${impPct}%</div></td>
          <td>${(r.clicks||0).toLocaleString('es-CO')}</td>
          <td>${r.ctr||0}%</td>
          <td>${gFmt(r.cpc||0)}</td>
          <td>${gFmt(r.spend||0)}</td>
          <td><div style="display:flex;align-items:center;gap:6px"><div style="width:${spendPct*0.6}px;height:6px;background:var(--red);border-radius:3px;min-width:2px"></div>${spendPct}%</div></td>
          <td style="color:${r.conversions>0?'var(--g)':'var(--m)'}">${r.conversions||0}</td>
          <td>${r.cpa>0?gFmt(r.cpa):'—'}</td>
          <td style="color:${(r.roas||0)>=3?'var(--g)':(r.roas||0)>=1.5?'var(--y)':'var(--m)'}">${r.roas>0?(r.roas||0).toFixed(2)+'×':'—'}</td>
        </tr>`;}).join('')+'</tbody></table>';
  } catch(e){ if(load) load.style.display='none'; if(cont) cont.innerHTML='<p style="color:var(--red);font-size:13px">✗ Error de conexión</p>'; }
}

function loadGCampaignFilters(selId){
  if(gCampaignsList.length) { populateGCampFilters(); return; }
  if(!currentClient||currentClient==='all') return;
  fetch(gUrl('campaigns')).then(r=>r.json()).then(d=>{ if(d.ok){ gCampaignsList=d.campaigns||[]; populateGCampFilters(); }});
}

// ── Actividad de cliente ──────────────────────────────────────────────────────
const ACT_LABELS = {
  'login':                       { icon:'🔐', label:'Inicio de sesión',          color:'var(--b)' },
  'logout':                      { icon:'🔓', label:'Cierre de sesión',           color:'var(--m)' },
  'login_fail':                  { icon:'⚠',  label:'Intento fallido de acceso', color:'var(--y)' },
  'proposal_viewed_by_client':   { icon:'👁',  label:'Cliente abrió propuesta',   color:'var(--b)' },
  'proposal_final_generated':    { icon:'✦',  label:'Cliente generó versión final', color:'var(--p)' },
  'refresh':                     { icon:'↻',  label:'Datos actualizados',         color:'var(--g)' },
  'context_save':                { icon:'💾', label:'Contexto guardado',           color:'var(--g)' },
  'credential_save':             { icon:'🔑', label:'Credencial guardada',         color:'var(--y)' },
};

function actTimeAgo(iso) {
  if (!iso) return '';
  const s = Math.floor((Date.now() - new Date(iso)) / 1000);
  if (s < 60)   return 'hace ' + s + 's';
  if (s < 3600) return 'hace ' + Math.floor(s/60) + 'min';
  if (s < 86400)return 'hace ' + Math.floor(s/3600) + 'h';
  return 'hace ' + Math.floor(s/86400) + 'd';
}

async function loadClientActivity(cid) {
  const panel = document.getElementById('card-activity');
  const list  = document.getElementById('activity-list');
  if (!panel || !list) return;
  if (!cid || cid === 'all') { panel.style.display = 'none'; return; }
  panel.style.display = 'block';
  list.innerHTML = '<div style="color:var(--m);font-size:12px;padding:8px 0">Cargando...</div>';
  try {
    const r    = await fetch('api/activity.php?client_id=' + cid + '&limit=30');
    const data = await r.json();
    if (!data.ok || !data.events.length) {
      list.innerHTML = '<div style="color:var(--m);font-size:12px;padding:8px 0">Sin actividad registrada.</div>';
      return;
    }
    list.innerHTML = data.events.map(ev => {
      const meta  = ACT_LABELS[ev.action] || { icon:'•', label: ev.action, color:'var(--m)' };
      const who   = ev.user_name ? ev.user_name : (ev.ip || '');
      const when  = actTimeAgo(ev.created_at);
      const detail = ev.detail ? '<span style="color:var(--m);font-size:11px"> — ' + ev.detail + '</span>' : '';
      return `<div style="display:flex;align-items:flex-start;gap:10px;padding:7px 0;border-bottom:1px solid var(--br)">
        <span style="font-size:15px;min-width:22px;color:${meta.color}">${meta.icon}</span>
        <div style="flex:1;min-width:0">
          <div style="font-size:12px;font-weight:600;color:var(--t)">${meta.label}${detail}</div>
          <div style="font-size:11px;color:var(--m);margin-top:2px">${who ? who + ' · ' : ''}${when}</div>
        </div>
      </div>`;
    }).join('');
  } catch(e) {
    list.innerHTML = '<div style="color:var(--red);font-size:12px;padding:8px 0">Error al cargar actividad.</div>';
  }
}

// ── Historial análisis competencia ────────────────────────────────────────────
async function loadCompHistory() {
  if (!currentClient || currentClient === 'all') return;
  const card = document.getElementById('ca-history-card');
  const list = document.getElementById('ca-history-list');
  if (!card || !list) return;
  try {
    const r    = await fetch('api/competitive_analysis.php?action=history&client_id=' + currentClient);
    const data = await r.json();
    if (!data.ok || !data.analyses.length) { card.style.display = 'none'; return; }
    card.style.display = 'block';
    list.innerHTML = data.analyses.map((a, i) => {
      const date = new Date(a.created_at).toLocaleDateString('es-MX', {day:'2-digit',month:'short',year:'numeric'});
      let comps = [];
      try { comps = JSON.parse(a.competitors).map(c => c.name); } catch(e) {}
      return `<div class="ca-hist-item" style="border-bottom:1px solid var(--br)">
        <div onclick="toggleCompItem(this)" style="display:flex;align-items:center;justify-content:space-between;padding:10px 0;cursor:pointer">
          <div>
            <span style="font-size:12px;font-weight:600;color:var(--t)">${date}</span>
            <span style="font-size:11px;color:var(--m);margin-left:8px">${comps.slice(0,3).join(', ')}${comps.length>3?' +' + (comps.length-3) + ' más':''}</span>
          </div>
          <span style="color:var(--m);font-size:12px">▾</span>
        </div>
        <div class="ca-hist-body" style="display:none;font-size:12px;color:var(--t);white-space:pre-wrap;line-height:1.6;padding-bottom:12px;max-height:400px;overflow-y:auto">${escapeHtml(a.analysis)}</div>
      </div>`;
    }).join('');
  } catch(e) { card.style.display = 'none'; }
}

function toggleCompItem(header) {
  const body = header.parentElement.querySelector('.ca-hist-body');
  if (!body) return;
  const open = body.style.display !== 'none';
  body.style.display = open ? 'none' : 'block';
  const arrow = header.querySelector('span:last-child');
  if (arrow) arrow.textContent = open ? '▾' : '▴';
}

function escapeHtml(s) {
  return String(s).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
}

// ── Reporte mensual ────────────────────────────────────────────────────────────
async function generateMonthlyReport() {
  if (!currentClient || currentClient === 'all') { showToast('Selecciona un cliente', 'var(--y)'); return; }
  const c = CLIENTS.find(x => x.id == currentClient);
  if (!c) return;
  const btn = document.getElementById('btn-monthly-report');
  const out = document.getElementById('monthly-report-content');
  const box = document.getElementById('monthly-report-result');
  if (!btn || !out) return;
  btn.disabled = true;
  btn.textContent = 'Generando...';
  if (box) box.style.display = 'none';

  const metaCtx    = (document.getElementById('meta-ctx')    || {value:''}).value.trim();
  const googleCtx  = (document.getElementById('google-ctx')  || {value:''}).value.trim();
  const seoCtx     = (document.getElementById('seo-ctx')     || {value:''}).value.trim();

  const prompt = `Eres un experto en marketing digital con 20+ años de experiencia. Genera un reporte mensual ejecutivo profesional para el cliente "${c.name}".

El reporte debe incluir:
## 1. RESUMEN EJECUTIVO
Párrafo de 2-3 oraciones con los logros principales del mes.

## 2. RESULTADOS META ADS
${metaCtx ? 'Contexto disponible:\n' + metaCtx : 'Sin datos de Meta Ads este mes.'}
- Métricas clave (alcance, impresiones, CTR, conversiones, ROAS)
- Comparativa con mes anterior
- Observaciones y optimizaciones realizadas

## 3. RESULTADOS GOOGLE ADS
${googleCtx ? 'Contexto disponible:\n' + googleCtx : 'Sin datos de Google Ads este mes.'}
- Métricas clave (impresiones, clics, CTR, CPC, conversiones, ROAS)
- Comparativa con mes anterior
- Optimizaciones realizadas

## 4. SEO Y WEB
${seoCtx ? 'Contexto disponible:\n' + seoCtx : 'Sin datos SEO este mes.'}
- Posicionamiento orgánico
- Tráfico web
- Mejoras técnicas realizadas

## 5. PRÓXIMOS PASOS
Lista de 3-5 acciones concretas para el próximo mes.

Usa un tono profesional, orientado a resultados. Sé específico y accionable. Si no hay datos de algún canal, indícalo brevemente y da recomendaciones generales.`;

  try {
    const r    = await fetch('api/avi.php', { method:'POST', headers:{'Content-Type':'application/json'}, body: JSON.stringify({ question: prompt, client_name: c.name, client_industry: c.industry||'', section: 'general' }) });
    const data = await r.json();
    if (data.response) {
      out.innerHTML = renderMD(data.response);
      if (box) box.style.display = 'block';
    } else {
      showToast('Error generando reporte. Intenta de nuevo.', 'var(--red)');
    }
  } catch(e) {
    showToast('Error de conexión', 'var(--red)');
  } finally {
    btn.disabled = false;
    btn.textContent = 'Generar con AVI →';
  }
}

function copyMonthlyReport() {
  const out = document.getElementById('monthly-report-content');
  if (out && out.textContent) {
    navigator.clipboard.writeText(out.textContent);
    showToast('✓ Reporte copiado', 'var(--g)');
  }
}

// --- INIT ---
if(!isAdmin && CLIENTS.length===1) selectClient(CLIENTS[0].id);
initChart('main'); initChart('donut');

// ═══════════════════════════════════════════════════════════
// AVI AGENTE — Copiloto de ejecución
// ═══════════════════════════════════════════════════════════

let _aviAgentCallback = null;

function aviAgentModalOpen(msg, explain, onConfirm) {
  const modal = document.getElementById('avi-agent-modal');
  if (!modal) return;
  document.getElementById('avi-agent-modal-msg').textContent = msg;
  const exEl = document.getElementById('avi-agent-modal-explain');
  if (explain) { exEl.textContent = explain; exEl.style.display = 'block'; }
  else { exEl.style.display = 'none'; }
  _aviAgentCallback = onConfirm;
  document.getElementById('avi-agent-modal-confirm').onclick = () => { aviAgentModalClose(); if (_aviAgentCallback) _aviAgentCallback(); };
  modal.style.display = 'flex';
}

function aviAgentModalClose() {
  const modal = document.getElementById('avi-agent-modal');
  if (modal) modal.style.display = 'none';
  _aviAgentCallback = null;
}

// Llama a api/avi-agent.php y renderiza respuesta + botones de acción
async function callAVIAgent({ respId, question, scope = 'all', availableActions = [], sectionData = {}, loadingText = 'AVI analizando datos reales...' }) {
  const c = currentClient !== 'all' ? CLIENTS.find(x => x.id == currentClient) : null;
  if (!c) { showToast('Selecciona un cliente primero', 'var(--y)'); return; }

  const respEl = document.getElementById(respId);
  if (!respEl) return;
  respEl.style.display = 'block';
  respEl.innerHTML = `<div class="avi-loading"><div class="avi-orb" style="width:18px;height:18px;font-size:9px">✦</div><span>${loadingText}</span></div>`;

  try {
    const res = await fetch('api/avi-agent.php', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        client_id: c.id,
        question,
        scope,
        available_actions: availableActions,
        section_data: sectionData,
      })
    });
    const d = await res.json();

    if (!d.ok) {
      const errMsg = d.error || 'Error';
      respEl.innerHTML = `<div style="color:var(--red);font-size:12px">${escapeHtml(errMsg)}</div>`;
      if (errMsg.includes('créditos') || errMsg.includes('credit')) showToast('⚠️ Sin créditos Claude API', 'var(--red)', 5000);
      else if (errMsg.includes('API Key') || errMsg.includes('🔑')) showToast('🔑 Claude API Key inválida — ve a ⚙ APIs', 'var(--red)', 6000);
      else if (errMsg.includes('saturada') || errMsg.includes('🔄')) showToast('🔄 Claude API saturada — reintenta en 30s', 'var(--y)', 5000);
      else if (errMsg.includes('Límite') || errMsg.includes('⏳')) showToast('⏳ Rate limit Claude API — espera y reintenta', 'var(--y)', 5000);
      _aviFinish(respId);
      return;
    }

    // Renderizar análisis
    let html = `<div class="avi-agent-text">${renderMD(d.text || '')}</div>`;

    // Renderizar botones de acción
    if (d.actions && d.actions.length > 0) {
      html += `<div class="avi-agent-actions" style="margin-top:14px;display:flex;flex-direction:column;gap:8px">
        <div style="font-size:11px;color:var(--m);text-transform:uppercase;letter-spacing:.7px;margin-bottom:2px">Acciones sugeridas</div>`;
      d.actions.forEach((act, i) => {
        const label = aviAgentActionLabel(act);
        const confirm = act.confirm || label;
        const explain = act.explain || '';
        html += `<button onclick="aviAgentExecute(${JSON.stringify(act).replace(/"/g,'&quot;')}, '${escapeHtml(confirm)}', '${escapeHtml(explain)}')"
          style="padding:8px 14px;border-radius:8px;font-size:12px;background:var(--rl);border:1px solid var(--rb);color:var(--red);cursor:pointer;font-family:var(--fb);font-weight:600;text-align:left;display:flex;align-items:center;gap:8px">
          <span style="font-size:14px">▶</span>${escapeHtml(label)}
        </button>`;
      });
      html += '</div>';
    }

    respEl.innerHTML = html;

    // Mostrar separador cuando hay datos + respuesta en el drawer
    if (respId === 'avi-drw-resp') {
      const dataEl = document.getElementById('avi-drw-data');
      const sep    = document.getElementById('avi-drw-sep');
      if (sep && dataEl && dataEl.style.display !== 'none') sep.style.display = 'block';
    }

    // Guardar en historial si es el drawer principal
    if (respId === 'avi-drw-resp' && typeof aviHistorySave === 'function') {
      const plainText = (d.text || '').replace(/<[^>]*>/g,'').replace(/\s+/g,' ').trim();
      aviHistorySave(question, plainText, scope);
    }

    _aviFinish(respId);

  } catch(e) {
    respEl.innerHTML = `<div style="color:var(--m);font-size:12px">Configura la Claude API Key en ⚙ APIs para activar AVI Agente.</div>`;
    _aviFinish(respId);
  }
}

function aviAgentActionLabel(act) {
  const type = act['0'] || Object.keys(act)[0] || '';
  const name = act.name || act.campaign_name || '';
  const kwCount = n => ((act[n]||'').split(',').filter(k=>k.trim()).length);
  const labels = {
    'pause_campaign':          `Pausar campaña "${name}"`,
    'activate_campaign':       `Activar campaña "${name}"`,
    'change_budget':           `Cambiar presupuesto de "${name}" a $${act.amount || ''}`,
    'pause_adset':             `Pausar Ad Set "${act.name || act.id}"`,
    'activate_adset':          `Activar Ad Set "${act.name || act.id}"`,
    'pause_adgroup':           `Pausar grupo "${act.name || act.id}"`,
    'enable_adgroup':          `Activar grupo "${act.name || act.id}"`,
    'pause_ad':                `Pausar anuncio "${act.name || act.id}"`,
    'activate_ad':             `Activar anuncio "${act.name || act.id}"`,
    'add_negatives':           `Agregar ${kwCount('keywords')} keywords negativas a "${act.campaign_name || name}"`,
    'add_positive_keywords':   `Agregar ${kwCount('keywords')} keywords positivas al grupo "${act.adgroup_name || act.adgroup_id}"`,
    'change_bidding':          `Cambiar puja de "${name}" a ${act.strategy || ''}`,
    'generate_report':         `Generar reporte ${act.period || ''}`,
    'send_report_email':       `Enviar reporte por email${act.email ? ' a '+act.email : ''}`,
  };
  const actionKey = act.action_type || type;
  return labels[actionKey] || `Ejecutar: ${actionKey}`;
}

async function aviAgentExecute(act, confirmMsg, explain) {
  aviAgentModalOpen(confirmMsg, explain, async () => {
    const actionKey = act.action_type || act['0'] || '';
    const service   = act.service || '';

    try {
      let result;

      // ── Pausar / activar campaña ──────────────────────────────────────────────
      if (actionKey === 'pause_campaign' || actionKey === 'activate_campaign') {
        if (service === 'meta') {
          result = await fetch('api/meta-action.php', { method:'POST', headers:{'Content-Type':'application/json'},
            body: JSON.stringify({ action: actionKey, client_id: currentClient, campaign_id: act.id }) }).then(r=>r.json());
        } else if (service === 'google') {
          const gAction = actionKey === 'pause_campaign' ? 'pause_campaign' : 'enable_campaign';
          result = await fetch('api/google-ads.php', { method:'POST', headers:{'Content-Type':'application/json'},
            body: JSON.stringify({ action: gAction, client_id: currentClient, campaign_id: act.id }) }).then(r=>r.json());
        } else if (service === 'tiktok') {
          result = await fetch('api/tiktok-data.php', { method:'POST', headers:{'Content-Type':'application/json'},
            body: JSON.stringify({ action: actionKey, client_id: currentClient, campaign_id: act.id }) }).then(r=>r.json());
        }

      // ── Cambiar presupuesto ───────────────────────────────────────────────────
      } else if (actionKey === 'change_budget') {
        if (service === 'meta') {
          result = await fetch('api/meta-action.php', { method:'POST', headers:{'Content-Type':'application/json'},
            body: JSON.stringify({ action:'update_budget', client_id: currentClient, campaign_id: act.id, budget: act.amount, budget_type: 'daily' }) }).then(r=>r.json());
        } else if (service === 'google') {
          result = await fetch('api/google-ads.php', { method:'POST', headers:{'Content-Type':'application/json'},
            body: JSON.stringify({ action:'change_budget', client_id: currentClient, campaign_id: act.id, amount: act.amount }) }).then(r=>r.json());
        } else if (service === 'tiktok') {
          result = await fetch('api/tiktok-data.php', { method:'POST', headers:{'Content-Type':'application/json'},
            body: JSON.stringify({ action:'change_tiktok_budget', client_id: currentClient, campaign_id: act.id, budget: act.amount }) }).then(r=>r.json());
        }

      // ── Ad Sets (Meta) ────────────────────────────────────────────────────────
      } else if (actionKey === 'pause_adset' || actionKey === 'activate_adset') {
        result = await fetch('api/meta-action.php', { method:'POST', headers:{'Content-Type':'application/json'},
          body: JSON.stringify({ action: actionKey, client_id: currentClient, adset_id: act.id }) }).then(r=>r.json());

      // ── Ad Groups (Google / TikTok) ───────────────────────────────────────────
      } else if (actionKey === 'pause_adgroup' || actionKey === 'enable_adgroup') {
        if (service === 'google') {
          result = await fetch('api/google-ads.php', { method:'POST', headers:{'Content-Type':'application/json'},
            body: JSON.stringify({ action: actionKey, client_id: currentClient, adgroup_id: act.id }) }).then(r=>r.json());
        } else if (service === 'tiktok') {
          const ttAction = actionKey === 'pause_adgroup' ? 'pause_adgroup' : 'activate_adgroup';
          result = await fetch('api/tiktok-data.php', { method:'POST', headers:{'Content-Type':'application/json'},
            body: JSON.stringify({ action: ttAction, client_id: currentClient, adgroup_id: act.id }) }).then(r=>r.json());
        }

      // ── Anuncios individuales ─────────────────────────────────────────────────
      } else if (actionKey === 'pause_ad' || actionKey === 'activate_ad') {
        if (service === 'meta') {
          result = await fetch('api/meta-action.php', { method:'POST', headers:{'Content-Type':'application/json'},
            body: JSON.stringify({ action: actionKey, client_id: currentClient, ad_id: act.id }) }).then(r=>r.json());
        } else if (service === 'google') {
          const gAction = actionKey === 'pause_ad' ? 'pause_ad' : 'enable_ad';
          result = await fetch('api/google-ads.php', { method:'POST', headers:{'Content-Type':'application/json'},
            body: JSON.stringify({ action: gAction, client_id: currentClient, ad_id: act.id, adgroup_id: act.adgroup_id }) }).then(r=>r.json());
        }

      // ── Keywords negativas (Google) ───────────────────────────────────────────
      } else if (actionKey === 'add_negatives') {
        const kws = (act.keywords || '').split(',').map(k => ({ keyword: k.trim(), match_type: 'BROAD' })).filter(k => k.keyword);
        result = await fetch('api/google-ads.php', { method:'POST', headers:{'Content-Type':'application/json'},
          body: JSON.stringify({ action:'apply_negatives', client_id: currentClient, keywords: kws }) }).then(r=>r.json());

      // ── Keywords positivas (Google) ───────────────────────────────────────────
      } else if (actionKey === 'add_positive_keywords') {
        const mt = (act.match_type || 'BROAD').toUpperCase();
        const kws = (act.keywords || '').split(',').map(k => ({ keyword: k.trim(), match_type: mt })).filter(k => k.keyword);
        result = await fetch('api/google-ads.php', { method:'POST', headers:{'Content-Type':'application/json'},
          body: JSON.stringify({ action:'add_keywords', client_id: currentClient, adgroup_id: act.adgroup_id, keywords: kws }) }).then(r=>r.json());

      // ── Cambiar estrategia de puja (Google) ───────────────────────────────────
      } else if (actionKey === 'change_bidding') {
        result = await fetch('api/google-ads.php', { method:'POST', headers:{'Content-Type':'application/json'},
          body: JSON.stringify({ action:'change_bidding', client_id: currentClient, campaign_id: act.campaign_id || act.id,
            strategy: act.strategy, target_cpa: act.target_cpa || 0, target_roas: act.target_roas || 0 }) }).then(r=>r.json());

      // ── Generar reporte ───────────────────────────────────────────────────────
      } else if (actionKey === 'generate_report') {
        result = await fetch('api/reports.php', { method:'POST', headers:{'Content-Type':'application/json'},
          body: JSON.stringify({ action:'generate', client_id: currentClient, period: act.period || 'last_30' }) }).then(r=>r.json());

      // ── Enviar reporte por email ──────────────────────────────────────────────
      } else if (actionKey === 'send_report_email') {
        const reportId = act.report_id && act.report_id !== 'latest' ? act.report_id : '';
        result = await fetch('api/reports.php', { method:'POST', headers:{'Content-Type':'application/json'},
          body: JSON.stringify({ action:'send_email', client_id: currentClient, report_id: reportId, email: act.email || '' }) }).then(r=>r.json());
      }

      if (result && result.ok !== false) {
        showToast('✓ Acción ejecutada correctamente', 'var(--g)');
      } else {
        showToast('✗ ' + (result?.error || 'Error al ejecutar'), 'var(--red)');
      }
    } catch(e) {
      showToast('✗ Error de conexión', 'var(--red)');
    }
  });
}

// ── AVI Agente Panel (Resumen) ────────────────────────────────────────────────
function aviAgentPanelUpdate() {
  const panel = document.getElementById('avi-agent-panel');
  const sub   = document.getElementById('avi-agent-sub');
  if (!panel) return;
  if (currentClient && currentClient !== 'all') {
    const c = CLIENTS.find(x => x.id == currentClient);
    panel.style.display = 'flex';
    if (sub && c) sub.textContent = 'Cliente activo: ' + c.name + ' · Meta + Google + SEO';
  } else {
    panel.style.display = 'none';
  }
}

function aviAgentQuick(scope, question) {
  document.getElementById('avi-agent-q').value = question;
  aviAgentSend(scope);
}

function aviAgentSend(scope) {
  const q = document.getElementById('avi-agent-q')?.value.trim();
  if (!q) return;
  const detectedScope = scope || detectAVIScope(q);
  const actions = ['pause_campaign','activate_campaign','change_budget','pause_adset','activate_adset','pause_adgroup','enable_adgroup','pause_ad','activate_ad','add_negatives','add_positive_keywords','change_bidding','generate_report','send_report_email'];
  callAVIAgent({
    respId: 'avi-agent-resp',
    question: q,
    scope: detectedScope,
    availableActions: actions,
    loadingText: 'AVI revisando datos reales...',
  });
  document.getElementById('avi-agent-q').value = '';
}

function detectAVIScope(q) {
  const ql = q.toLowerCase();
  if (ql.includes('meta') || ql.includes('facebook') || ql.includes('instagram')) return 'meta';
  if (ql.includes('google') || ql.includes('ads') || ql.includes('campaña')) return 'google';
  if (ql.includes('seo') || ql.includes('keyword') || ql.includes('orgánico') || ql.includes('posición')) return 'gsc';
  return 'all';
}

// ── AVI Agente Panel — Meta Ads ────────────────────────────────────────────────
function aviAgentMetaPanelUpdate() {
  const panel = document.getElementById('avi-meta-panel');
  const sub   = document.getElementById('avi-meta-sub');
  if (!panel) return;
  if (!currentClient || currentClient === 'all') { panel.style.display = 'none'; return; }
  const count = metaCampsAll.length;
  panel.style.display = 'flex';
  if (sub) sub.textContent = count
    ? `${count} campaña${count !== 1 ? 's' : ''} cargada${count !== 1 ? 's' : ''} · puede pausar, activar o cambiar presupuesto`
    : 'Sin campañas cargadas — selecciona un cliente';
}

function aviAgentMetaQuick(question) {
  document.getElementById('avi-meta-q').value = question;
  aviAgentMetaSend();
}

function aviAgentMetaSend() {
  const q = document.getElementById('avi-meta-q')?.value.trim();
  if (!q) return;
  const camps = metaCampsAll.map(c => ({
    id: c.id, name: c.name, status: c.status,
    spend: c.spend, roas: c.roas, ctr: c.ctr,
    frequency: c.frequency, daily_budget: c.daily_budget,
  }));
  callAVIAgent({
    respId:           'avi-agent-resp-meta',
    question:         q,
    scope:            'meta',
    availableActions: ['pause_campaign', 'activate_campaign', 'change_budget'],
    sectionData:      { campaigns: camps, period: document.getElementById('meta-period-sel')?.value || '30d' },
    loadingText:      'AVI analizando campañas Meta...',
  });
  document.getElementById('avi-meta-q').value = '';
}

// ── AVI Agente Panel — Google Ads ─────────────────────────────────────────────
function aviAgentGooglePanelUpdate() {
  const panel = document.getElementById('avi-google-panel');
  const sub   = document.getElementById('avi-google-sub');
  if (!panel) return;
  if (!currentClient || currentClient === 'all') { panel.style.display = 'none'; return; }
  const count = gCampaignsList.length;
  panel.style.display = 'flex';
  if (sub) sub.textContent = count
    ? `${count} campaña${count !== 1 ? 's' : ''} cargada${count !== 1 ? 's' : ''} · puede pausar, activar, cambiar presupuesto o añadir negativos`
    : 'Sin campañas cargadas — selecciona un cliente';
}

function aviAgentGoogleQuick(question) {
  document.getElementById('avi-google-q').value = question;
  aviAgentGoogleSend();
}

function aviAgentGoogleSend() {
  const q = document.getElementById('avi-google-q')?.value.trim();
  if (!q) return;
  const camps = gCampaignsList.map(c => ({
    id: c.id, name: c.name, status: c.status,
    spend: c.spend, roas: c.roas, cpa: c.cpa,
    conversions: c.conversions, imp_share: c.imp_share,
    daily_budget: c.daily_budget,
  }));
  callAVIAgent({
    respId:           'avi-agent-resp-google',
    question:         q,
    scope:            'google',
    availableActions: ['pause_campaign', 'activate_campaign', 'change_budget', 'add_negatives'],
    sectionData:      { campaigns: camps, period: currentGPeriod || '30d' },
    loadingText:      'AVI analizando campañas Google Ads...',
  });
  document.getElementById('avi-google-q').value = '';
}

// ── AVI Agente Panel — Reportes ────────────────────────────────────────────────
function aviAgentRptPanelUpdate() {
  const panel = document.getElementById('avi-rpt-panel');
  if (!panel) return;
  if (!currentClient || currentClient === 'all') { panel.style.display = 'none'; return; }
  panel.style.display = 'flex';
}

function aviAgentRptQuick(question) {
  document.getElementById('avi-rpt-q').value = question;
  aviAgentRptSend();
}

function aviAgentRptSend() {
  const q = document.getElementById('avi-rpt-q')?.value.trim();
  if (!q) return;
  const period = document.getElementById('rpt-period')?.value || 'last_30';
  callAVIAgent({
    respId:           'avi-agent-resp-rpt',
    question:         q,
    scope:            'all',
    availableActions: ['generate_report'],
    sectionData:      { period },
    loadingText:      'AVI preparando análisis y reporte...',
  });
  document.getElementById('avi-rpt-q').value = '';
}

// ── AVI Agente Panel — SEO ─────────────────────────────────────────────────────
function aviAgentSEOPanelUpdate() {
  const panel = document.getElementById('avi-seo-panel');
  const sub   = document.getElementById('avi-seo-sub');
  if (!panel) return;
  if (!currentClient || currentClient === 'all') { panel.style.display = 'none'; return; }
  panel.style.display = 'flex';
  if (sub) sub.textContent = gscKeywordsList.length
    ? `${gscKeywordsList.length} keywords cargadas desde GSC · análisis y recomendaciones accionables`
    : 'Sin datos GSC — configura Search Console para este cliente';
}

function aviAgentSEOQuick(question, respId) {
  respId = respId || 'avi-agent-resp-seo';
  const inp = document.getElementById('avi-seo-q');
  if (inp) inp.value = question;
  aviAgentSEOSend(respId);
}

function aviAgentSEOSend(respId) {
  respId = respId || 'avi-agent-resp-seo';
  const q = respId === 'avi-agent-resp-seo'
    ? (document.getElementById('avi-seo-q')?.value.trim())
    : (document.getElementById('avi-seo-q')?.value.trim());
  if (!q) return;
  const kws = gscKeywordsList.slice(0, 30).map(k => ({
    keyword: k.keyword, clicks: k.clicks, position: k.position, impressions: k.impressions,
    ctr: k.ctr,
  }));
  callAVIAgent({
    respId,
    question:         q,
    scope:            'gsc',
    availableActions: [],
    sectionData:      { keywords: kws, period: document.getElementById('gsc-period-sel')?.value || '30d', gscData: _gscData },
    loadingText:      'AVI analizando datos SEO reales...',
  });
  if (respId === 'avi-agent-resp-seo') {
    const inp = document.getElementById('avi-seo-q');
    if (inp) inp.value = '';
  }
}

// ── AVI Drawer Flotante ───────────────────────────────────────────────────────

function aviToggle() {
  const d = document.getElementById('avi-drawer');
  if (d && d.classList.contains('open')) aviClose(); else aviOpen();
}
function aviOpen() {
  document.getElementById('avi-drawer')?.classList.add('open');
  const bd = document.getElementById('avi-bd');
  if (bd) bd.style.display = 'block';
  document.getElementById('avi-fab')?.classList.add('open');
  aviDrawerClientUpdate();
  _aviRenderShortcuts();
  _aviRefreshDataPanel();
  if (document.getElementById('avi-history-panel')?.style.display === 'block') aviHistoryRender();
  setTimeout(() => document.getElementById('avi-drw-q')?.focus(), 320);
}
function aviClose() {
  document.getElementById('avi-drawer')?.classList.remove('open');
  const bd = document.getElementById('avi-bd');
  if (bd) bd.style.display = 'none';
  document.getElementById('avi-fab')?.classList.remove('open');
}

function _aviDataSnapshot(scope) {
  let html = '';
  const mc = (Array.isArray(metaCampsAll) ? metaCampsAll : []).filter(c => c.status==='active'||c.status==='ACTIVE');
  const gc = (Array.isArray(gCampaignsList) ? gCampaignsList : []).filter(c => c.status==='enabled'||c.status==='ENABLED');
  const kl = Array.isArray(gscKeywordsList) ? gscKeywordsList : [];
  const fmt = n => typeof n === 'number' ? n.toLocaleString('es-CO', {maximumFractionDigits:2}) : (n || '—');
  const fmtCOP = n => typeof n === 'number' ? '$' + (n >= 1000 ? (n/1000).toFixed(0)+'K' : n.toFixed(0)) : '—';

  // ── Meta Ads ──────────────────────────────────────────────────────────────
  if ((scope==='meta'||scope==='all') && mc.length) {
    const totalSpend = mc.reduce((s,c) => s+(c.spend||0), 0);
    html += `<div style="margin-bottom:12px">
      <div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:6px">
        <div style="font-size:10px;font-weight:700;color:var(--b);text-transform:uppercase;letter-spacing:.5px">📘 Meta Ads · ${mc.length} activas</div>
        <div style="font-size:10px;color:var(--m)">Total: ${fmtCOP(totalSpend)}</div>
      </div>`;
    mc.slice(0,6).forEach(c => {
      const roas = c.roas || 0;
      const roasColor = roas >= 2 ? 'var(--g)' : roas >= 1 ? 'var(--y)' : 'var(--red)';
      const roasDot   = roas >= 2 ? '🟢' : roas >= 1 ? '🟡' : '🔴';
      const ctr = c.ctr || 0;
      const ctrColor = ctr >= 2 ? 'var(--g)' : ctr >= 1 ? 'var(--y)' : 'var(--m)';
      const freq = c.frequency || 0;
      const freqWarn = freq > 3 ? `<span style="color:var(--y);font-size:10px" title="Fatiga: frecuencia alta"> ⚠${fmt(freq)}x</span>` : '';
      html += `<div style="padding:5px 0;border-bottom:1px solid var(--br)">
        <div style="display:flex;align-items:center;gap:5px;margin-bottom:3px">
          <span style="font-size:9px">${roasDot}</span>
          <div style="flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:11px;color:var(--t)">${escapeHtml(c.name)}</div>
          <span style="font-size:11px;color:var(--m);flex-shrink:0">${fmtCOP(c.spend)}</span>
        </div>
        <div style="display:flex;gap:8px;padding-left:14px">
          <span style="font-size:10px;color:${roasColor}">ROAS ${fmt(roas)}</span>
          <span style="font-size:10px;color:${ctrColor}">CTR ${fmt(ctr)}%</span>
          ${freqWarn}
        </div>
      </div>`;
    });
    html += '</div>';
  }

  // ── Google Ads ────────────────────────────────────────────────────────────
  if ((scope==='google'||scope==='all') && gc.length) {
    const totalSpend = gc.reduce((s,c) => s+(c.spend||0), 0);
    html += `<div style="margin-bottom:12px">
      <div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:6px">
        <div style="font-size:10px;font-weight:700;color:var(--g);text-transform:uppercase;letter-spacing:.5px">🟢 Google Ads · ${gc.length} activas</div>
        <div style="font-size:10px;color:var(--m)">Total: ${fmtCOP(totalSpend)}</div>
      </div>`;
    gc.slice(0,6).forEach(c => {
      const is = c.imp_share || 0;
      const isDot  = is >= 60 ? '🟢' : is >= 30 ? '🟡' : '🔴';
      const isColor= is >= 60 ? 'var(--g)' : is >= 30 ? 'var(--y)' : 'var(--red)';
      const cpa = c.cpa || 0;
      const convs = c.conversions || 0;
      const lostB = c.lost_budget || 0;
      const budgetWarn = lostB > 20 ? `<span style="color:var(--red);font-size:10px" title="Pierde IS% por presupuesto"> ⬆presup</span>` : '';
      html += `<div style="padding:5px 0;border-bottom:1px solid var(--br)">
        <div style="display:flex;align-items:center;gap:5px;margin-bottom:3px">
          <span style="font-size:9px">${isDot}</span>
          <div style="flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:11px;color:var(--t)">${escapeHtml(c.name)}</div>
          <span style="font-size:11px;color:var(--m);flex-shrink:0">${fmtCOP(c.spend)}</span>
        </div>
        <div style="display:flex;gap:8px;padding-left:14px">
          <span style="font-size:10px;color:${isColor}">IS ${fmt(is)}%</span>
          ${convs > 0 ? `<span style="font-size:10px;color:var(--g)">${fmt(convs)} conv</span>` : ''}
          ${cpa > 0 ? `<span style="font-size:10px;color:var(--m)">CPA ${fmtCOP(cpa)}</span>` : ''}
          ${budgetWarn}
        </div>
      </div>`;
    });
    html += '</div>';
  }

  // ── SEO / GSC ────────────────────────────────────────────────────────────
  if ((scope==='gsc'||scope==='all') && kl.length) {
    const top3  = kl.filter(k => (k.position||99) <= 3).length;
    const top10 = kl.filter(k => (k.position||99) <= 10).length;
    html += `<div style="margin-bottom:12px">
      <div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:6px">
        <div style="font-size:10px;font-weight:700;color:var(--m);text-transform:uppercase;letter-spacing:.5px">🔍 SEO · Top keywords</div>
        <div style="font-size:10px;color:var(--m)"><span style="color:var(--g)">${top3} top3</span> · ${top10} top10</div>
      </div>`;
    kl.slice(0,8).forEach(k => {
      const pos = k.position || 99;
      const posColor = pos <= 3 ? 'var(--g)' : pos <= 10 ? 'var(--b)' : pos <= 20 ? 'var(--y)' : 'var(--m)';
      const posDot   = pos <= 3 ? '🟢' : pos <= 10 ? '🔵' : '🟡';
      const ctr = k.ctr || 0;
      const ctrHi = ctr >= 5;
      html += `<div style="padding:4px 0;border-bottom:1px solid var(--br)">
        <div style="display:flex;align-items:center;gap:5px">
          <span style="font-size:9px">${posDot}</span>
          <div style="flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:11px;color:var(--t)">${escapeHtml(k.keyword||k.query||'')}</div>
          <span style="font-size:10px;font-weight:700;color:${posColor};flex-shrink:0">#${Math.round(pos)}</span>
          <span style="font-size:10px;color:var(--m);flex-shrink:0">${fmt(k.clicks)}c</span>
          ${ctrHi ? `<span style="font-size:9px;color:var(--g)" title="CTR alto">✓</span>` : ''}
        </div>
      </div>`;
    });
    html += '</div>';
  }

  return html;
}

function _aviRefreshDataPanel() {
  const el  = document.getElementById('avi-drw-data');
  const sep = document.getElementById('avi-drw-sep');
  if (!el) return;
  const hasClient = currentClient && currentClient !== 'all' && CLIENTS.find(x => x.id == currentClient);
  if (!hasClient) {
    el.innerHTML = `<div style="display:flex;flex-direction:column;align-items:center;gap:6px;padding:12px 0;color:var(--m)">
      <div style="font-size:20px;opacity:.4">◈</div>
      <div style="font-size:11px;text-align:center;line-height:1.5">Selecciona un cliente del menú superior<br>para ver sus datos en tiempo real</div>
    </div>`;
    el.style.display = 'block';
    if (sep) sep.style.display = 'none';
    return;
  }
  const snap = _aviDataSnapshot(_aviDrawerScope);
  if (snap) {
    el.innerHTML = snap;
    el.style.display = 'block';
    // Mostrar separador solo si también hay respuesta visible
    const resp = document.getElementById('avi-drw-resp');
    if (sep) sep.style.display = (resp && resp.style.display !== 'none') ? 'block' : 'none';
  } else {
    el.innerHTML = '';
    el.style.display = 'none';
    if (sep) sep.style.display = 'none';
  }
}

function renderOvSnapshot(force) {
  const card = document.getElementById('ov-snapshot-card');
  const grid = document.getElementById('ov-snap-grid');
  if (!card || !grid) return;
  const metaHtml   = _aviDataSnapshot('meta');
  const googleHtml = _aviDataSnapshot('google');
  const gscHtml    = _aviDataSnapshot('gsc');
  if (!metaHtml && !googleHtml && !gscHtml) {
    if (force) card.style.display = 'none';
    return;
  }
  const ph = '<div style="color:var(--m);font-size:12px;padding:12px 0;text-align:center">Sin datos cargados</div>';
  grid.innerHTML =
    `<div>${metaHtml || ph}</div>` +
    `<div>${googleHtml || ph}</div>` +
    `<div>${gscHtml || ph}</div>`;
  card.style.display = 'block';
}

function aviDrawerSetContext(page) {
  _aviCurrentPage = page || 'overview';
  const labels = { overview:'Dashboard', meta:'Meta Ads', google:'Google Ads', seo:'SEO', reportes:'Reportes', tiktok:'TikTok Ads', web:'Web', videos:'Videos', imagenes:'Imágenes', marca:'Activos', config:'APIs', profile:'Perfil' };
  const badge = document.getElementById('avi-drw-badge');
  if (badge) badge.textContent = labels[_aviCurrentPage] || _aviCurrentPage;
  const pageToScope = { meta:'meta', google:'google', seo:'gsc', tiktok:'tiktok', overview:'all', reportes:'all' };
  _aviDrawerScope = pageToScope[_aviCurrentPage] || 'all';
  _aviRenderShortcuts();
  _aviRefreshDataPanel();
}

function _aviRenderShortcuts() {
  const wrap = document.getElementById('avi-drw-shortcuts');
  if (!wrap) return;
  const shortcuts = _AVI_SC[_aviCurrentPage] || _AVI_SC.overview;
  wrap.innerHTML = '';
  shortcuts.forEach((s, i) => {
    const btn = document.createElement('button');
    btn.className = 'db';
    btn.textContent = `${s.icon} ${s.label}`;
    btn.addEventListener('click', () => aviDrawerQuick(s.scope, s.q));
    wrap.appendChild(btn);
  });
}

function aviDrawerClientUpdate() {
  const el = document.getElementById('avi-drw-ctx');
  if (!el) return;
  if (!currentClient || currentClient === 'all') {
    el.textContent = 'Modo general · Selecciona cliente para análisis con datos reales';
  } else {
    const c = CLIENTS.find(x => x.id == currentClient);
    el.textContent = c ? c.name + ' · Meta + Google + SEO' : 'Cliente activo';
  }
}

function aviDrawerQuick(scope, question) {
  _aviDrawerScope = scope;
  _aviRefreshDataPanel();
  const q = document.getElementById('avi-drw-q');
  if (q) q.value = question;
  if (!document.getElementById('avi-drawer')?.classList.contains('open')) aviOpen();
  aviDrawerSend();
}

function aviDrawerSend() {
  const q = document.getElementById('avi-drw-q')?.value.trim();
  if (!q) return;
  document.getElementById('avi-drw-q').value = '';

  // Deshabilitar shortcuts y botón durante la llamada
  document.querySelectorAll('#avi-drw-shortcuts .db').forEach(b => { b.disabled = true; b.style.opacity = '.45'; });
  const sendBtn = document.querySelector('#avi-drw-foot .avi-send');
  if (sendBtn) { sendBtn.disabled = true; sendBtn.textContent = '…'; }

  // Sin cliente seleccionado → AVI general (sin datos reales ni acciones)
  const hasClient = currentClient && currentClient !== 'all' && CLIENTS.find(x => x.id == currentClient);
  if (!hasClient) {
    const respEl = document.getElementById('avi-drw-resp');
    if (respEl) respEl.style.display = 'block';
    callAVI(q, '', 'avi-drw-resp', 'AVI analizando...', _aviCurrentPage,
      (text) => { if (typeof aviHistorySave === 'function') aviHistorySave(q, text, 'general'); });
    return;
  }

  const actionsMap = {
    meta:     ['pause_campaign','activate_campaign','change_budget','pause_adset','activate_adset','pause_ad','activate_ad','generate_report','send_report_email'],
    google:   ['pause_campaign','activate_campaign','change_budget','pause_adgroup','enable_adgroup','pause_ad','activate_ad','add_negatives','add_positive_keywords','change_bidding','generate_report','send_report_email'],
    tiktok:   ['pause_campaign','activate_campaign','change_budget','pause_adgroup','enable_adgroup'],
    gsc:      ['generate_report','send_report_email'],
    reportes: ['generate_report','send_report_email'],
    all:      ['pause_campaign','activate_campaign','change_budget','pause_adset','activate_adset','pause_adgroup','enable_adgroup','pause_ad','activate_ad','add_negatives','add_positive_keywords','change_bidding','generate_report','send_report_email'],
  };
  const sdata = {};
  if (_aviDrawerScope === 'meta' || _aviDrawerScope === 'all') {
    const camps = (metaCampsAll || []).filter(c => c.status==='active'||c.status==='ACTIVE').map(c => ({ id:c.id, name:c.name, status:c.status, spend:c.spend, roas:c.roas, ctr:c.ctr, frequency:c.frequency, daily_budget:c.daily_budget }));
    if (camps.length) sdata.meta_campaigns = camps;
  }
  if (_aviDrawerScope === 'google' || _aviDrawerScope === 'all') {
    const camps = (gCampaignsList || []).filter(c => c.status==='enabled'||c.status==='ENABLED').map(c => ({ id:c.id, name:c.name, status:c.status, spend:c.spend, roas:c.roas, cpa:c.cpa, conversions:c.conversions, imp_share:c.imp_share, daily_budget:c.daily_budget }));
    if (camps.length) sdata.google_campaigns = camps;
  }
  if (_aviDrawerScope === 'gsc' || _aviDrawerScope === 'all') {
    const kws = (gscKeywordsList || []).slice(0,30).map(k => ({ keyword:k.keyword, clicks:k.clicks, position:k.position, impressions:k.impressions, ctr:k.ctr }));
    if (kws.length) sdata.seo_keywords = kws;
  }
  if (_aviCurrentPage === 'reportes') sdata.period = document.getElementById('rpt-period')?.value || 'last_30';
  callAVIAgent({
    respId:           'avi-drw-resp',
    question:         q,
    scope:            _aviDrawerScope,
    availableActions: actionsMap[_aviDrawerScope] || actionsMap.all,
    sectionData:      sdata,
    loadingText:      'AVI analizando datos reales...',
  });
}
