function renderDashboard(){
  const{resumen:r,ventasHora:vh,ventasPeriodo:vp,ventasMes:vm,formasPago:fp,metas,metasConfig}=cache;
  if(!r)return;

  $('s-desc').textContent=fmt(r.total_descuentos);
  $('s-cort').textContent=fmt(r.total_cortesias);
  $('s-alim').textContent=fmt(r.total_alimentos);
  $('s-beb').textContent=fmt(r.total_bebidas);

  const cd=(vp&&vp.length)
    ?vp
    :(periodoActual==='hoy'
      ?vh.map(h=>({...h,venta:h.venta||0,dia:String(h.hora).padStart(2,'0')}))
      :[{ dia: fechaDesde || fecha || '', venta: r.venta_total || r.venta_pagada || 0, tickets: r.cuentas_pagadas || 0 }]);
  $('chart-wrap').innerHTML=buildLineChart(cd);
  $('chart-sub').textContent=`Total: ${fmt(r.venta_total || r.venta_pagada || cd.reduce((a,d)=>a+(d.venta||0),0))}`;

  renderHoraBars();

  const tops=cache.productos.slice(0,10);
  const rankClass=i=>i===0?'gold':i===1?'silver':i===2?'bronze':'';
  if(tops.length){
    $('top-list').innerHTML=tops.map((p,i)=>`<div class="top-row"><div class="top-rank ${rankClass(i)}">${i+1}</div><div class="top-name">${p.producto||p.descripcion||'—'}</div><div class="top-val">${fmt(p.total_venta||p.total||0)}</div></div>`).join('');
    $('top-total-val').textContent=fmt(tops.reduce((a,p)=>a+(p.total_venta||p.total||0),0));
    $('top-total').style.display='flex';
  }else{$('top-list').innerHTML='<div class="empty">Sin datos</div>';$('top-total').style.display='none';}

  $('c-abiertas').textContent=r.mesas_abiertas||0;
  $('c-abiertas-imp').textContent=fmt(r.venta_en_curso||0);
  renderMesasInicio();
  $('c-atendidas').textContent=r.cuentas_pagadas||0;
  $('c-atendidas-imp').textContent=fmt(r.venta_pagada||0);

  const tc=(r.cuentas_pagadas||0)+(r.mesas_abiertas||0);
  
  const comensales = r.total_comensales || 0;
  const ticketPerComensal = comensales > 0 ? fmt((r.venta_total||0) / comensales) : '—';
  $('mt-comensales').textContent = comensales > 0 ? comensales.toLocaleString('es-MX') : '—';
  $('mt-comensales-sub').textContent = comensales > 0
    ? `${ticketPerComensal} por comensal · ${r.cuentas_pagadas||0} cuentas`
    : `${r.cuentas_pagadas||0} cuentas cerradas`;
  
  const minProm = r.tiempo_promedio_min || 0;
  const fmtTiempo = m => m >= 60 ? `${Math.floor(m/60)}h ${m%60}min` : `${m} min`;
  $('mt-tiempo').textContent = minProm > 0 ? fmtTiempo(minProm) : '—';
  $('mt-tiempo-sub').textContent = minProm > 0
    ? `promedio · ${r.mesas_abiertas||0} mesa(s) abierta(s) ahora`
    : `${r.mesas_abiertas||0} mesa(s) abierta(s) ahora`;
  $('mt-desc').textContent=fmt(r.total_descuentos);
  $('mt-desc-sub').textContent=`Cortesías: ${fmt(r.total_cortesias||0)}`;

  
  try{
    const cuentasPagadas=r.cuentas_pagadas||0;
    const ventaPagada=r.venta_pagada||0;
    const tpTile=$('tp-tile');
    const metricsGrid=$('metrics-row-grid');
    if(tpTile){
      if(cuentasPagadas>0&&ventaPagada>0){
        const ticketProm=ventaPagada/cuentasPagadas;
        $('tp-val').textContent=fmt(ticketProm);
        $('tp-sub').textContent=`${cuentasPagadas} cuenta${cuentasPagadas!==1?'s':''} cerrada${cuentasPagadas!==1?'s':''}`;
        tpTile.style.display='block';
        
        if(metricsGrid)metricsGrid.style.gridTemplateColumns='repeat(2,1fr)';
      }else{
        tpTile.style.display='none';
        if(metricsGrid)metricsGrid.style.gridTemplateColumns='repeat(3,1fr)';
      }
    }
  }catch(_e){}

  
  try{
    const pmWrap=$('pm-daily-wrap');
    if(pmWrap&&metas&&metasConfig.meta_diaria>0&&periodoActual==='hoy'){
      const ventaHoy=Number(metas.venta_hoy || r.venta_total || 0);
      const metaDiaria=metasConfig.meta_diaria;
      const rawPct=(ventaHoy/metaDiaria)*100;
      const pct=Math.min(Math.round(rawPct),100);
      
      const estado=pct>=80?'on-track':pct>=50?'at-risk':'behind';
      
      const ahora=new Date();
      const minutosDia=ahora.getHours()*60+ahora.getMinutes();
      const pctHora=Math.round((minutosDia/1440)*100);
      
      const pmPct=$('pm-pct');
      const pmFill=$('pm-fill');
      const pmAchieved=$('pm-achieved');
      const pmRemaining=$('pm-remaining');
      const pmExpected=$('pm-expected-line');
      if(pmPct){pmPct.textContent=`${pct}%`;pmPct.className=`pm-pct ${estado}`;}
      if(pmFill){
        
        pmFill.style.width='0%';
        pmFill.className=`pm-fill ${estado}`;
        requestAnimationFrame(()=>requestAnimationFrame(()=>{pmFill.style.width=`${pct}%`;}));
      }
      if(pmAchieved)pmAchieved.textContent=`${fmt(ventaHoy)} logrado`;
      if(pmRemaining){
        const resta=Math.max(metaDiaria-ventaHoy,0);
        pmRemaining.textContent=resta>0?`Resta ${fmt(resta)}`:'¡Meta alcanzada!';
      }
      
      if(pmExpected&&pctHora>0&&pctHora<100){
        pmExpected.style.display='block';
        pmExpected.style.left=`${pctHora}%`;
      }else if(pmExpected){pmExpected.style.display='none';}
      pmWrap.style.display='block';
    }else if(pmWrap){
      pmWrap.style.display='none';
    }
  }catch(_e){}

  if(metas){
    const hayMetas=metasConfig.meta_diaria>0||metasConfig.meta_mensual>0||metasConfig.meta_anual>0;
    if(!hayMetas){
      $('metas-grid').innerHTML=`<div style="padding:16px;color:var(--text-3);font-size:12px;font-weight:600;text-align:center;grid-column:1/-1">Configura tus metas — <a href="/configuracion-metas" style="color:var(--orange);font-weight:700">Ir a configuración</a></div>`;
      $('metas-card').style.display='block';
    }else{
      const ml=[
        {l:'Meta diaria',p:'hoy',meta:metasConfig.meta_diaria,log:Number(metas.venta_hoy || r.venta_total || 0),c:'#ef4444'},
        {l:'Meta mensual',p:'mes actual',meta:metasConfig.meta_mensual,log:metas.venta_mes_actual,c:'#f59e0b'},
        {l:'Meta anual',p:'año',meta:metasConfig.meta_anual,log:metas.venta_anio_actual,c:'#2563eb'},
      ];
      $('metas-grid').innerHTML=ml.map(m=>{
        const pct=m.meta>0?Math.min(Math.round(((m.log||0)/m.meta)*100),100):0;
        return`<div class="meta-col"><div class="meta-period">${m.l}<br/><small style="color:var(--text-3)">${m.p}</small></div>${buildDonut(pct,m.c,88)}<div class="meta-avance" style="color:${m.c}">${pct}% completado</div><div class="meta-restante">Resta: ${fmt(Math.max(m.meta-(m.log||0),0))}</div></div>`;
      }).join('');
      $('metas-card').style.display='block';
    }
  }

  const fc=['#10b981','#2563eb','#f59e0b','#ef4444'];
  $('formas-body').innerHTML=fp&&fp.length
    ?`<table class="btm-table"><thead><tr><th></th><th>Forma de pago</th><th class="th-r">Importe</th><th class="th-r">%</th></tr></thead><tbody>${fp.map((f,i)=>`<tr><td><div style="width:8px;height:8px;border-radius:50%;background:${fc[i%4]}"></div></td><td>${f.forma_pago}</td><td class="td-r">${fmt(f.total)}</td><td class="td-pct">${f.porcentaje}%</td></tr>`).join('')}</tbody></table>`
    :'<div class="empty">Sin datos</div>';

  const sv=cache.ventasServicio||[];
  const sc=['#2563eb','#10b981','#f59e0b','#ef4444'];
  $('servicio-body').innerHTML=sv&&sv.length
    ?`<table class="btm-table"><thead><tr><th>Servicio</th><th class="th-r">Tickets</th><th class="th-r">Importe</th><th class="th-r">%</th></tr></thead><tbody>${sv.map((s,i)=>`<tr><td style="font-weight:700;color:${sc[i%4]}">${s.servicio}</td><td class="td-r">${s.tickets||0}</td><td class="td-r">${fmt(s.total)}</td><td class="td-pct">${Number(s.porcentaje||0).toFixed(1)}%</td></tr>`).join('')}</tbody></table>`
    :'<div class="empty">Sin datos</div>';

  const tt=(r.total_alimentos||0)+(r.total_bebidas||0)+(r.total_otros||0);
  const tipos=[{l:'Alimentos',v:r.total_alimentos,c:'#10b981'},{l:'Bebidas',v:r.total_bebidas,c:'#2563eb'},{l:'Otros',v:r.total_otros,c:'#f59e0b'}];
  $('tipo-body').innerHTML=`<table class="btm-table"><thead><tr><th>Tipo</th><th class="th-r">Importe</th><th class="th-r">%</th></tr></thead><tbody>${tipos.map(t=>`<tr><td style="font-weight:700;color:${t.c}">${t.l}</td><td class="td-r">${fmt(t.v)}</td><td class="td-pct">${tt>0?((t.v/tt)*100).toFixed(1):0}%</td></tr>`).join('')}</tbody></table>`;

  
  renderTopbarExecutive();
  renderAlertas();
  renderComparativoAyer();
}

function renderMesasLegacy(){
  const normalizeMesas = window.ArkhonUtils && typeof window.ArkhonUtils.normalizeMesas === 'function'
    ? window.ArkhonUtils.normalizeMesas
    : (mesas => Array.isArray(mesas) ? mesas : []);
  const m = normalizeMesas(cache.mesas);
  cache.mesas = m;
  if(!m||!m.length){$('mesas-body').innerHTML='<div class="empty">Sin mesas activas en este momento</div>';return;}

  
  const sorted=[...m].sort((a,b)=>(b.minutos_abierta||0)-(a.minutos_abierta||0));

  const totalAbierto=sorted.reduce((a,t)=>a+(t.total||0),0);
  const urgentes=sorted.filter(t=>(t.minutos_abierta||0)>90).length;
  const enRiesgo=sorted.filter(t=>(t.minutos_abierta||0)>45&&(t.minutos_abierta||0)<=90).length;

  
  const capacidadEstimada=Math.max(sorted.length,1);
  const header=`<div class="mu-header">
    <div class="mu-stat">
      <div class="mu-stat-label">Mesas abiertas</div>
      <div class="mu-stat-val" style="color:var(--orange)">${sorted.length}</div>
      <div class="mu-occ-bar"><div class="mu-occ-fill" style="width:100%"></div></div>
    </div>
    <div class="mu-stat">
      <div class="mu-stat-label">Total en curso</div>
      <div class="mu-stat-val" style="color:var(--green)">${fmt(totalAbierto)}</div>
    </div>
    <div class="mu-stat">
      <div class="mu-stat-label">En riesgo / Urgente</div>
      <div class="mu-stat-val" style="color:${urgentes>0?'var(--red)':enRiesgo>0?'var(--yellow)':'var(--text-3)'}">
        ${urgentes>0?`${urgentes} urgente${urgentes!==1?'s':''}`:enRiesgo>0?`${enRiesgo} en riesgo`:'Sin alertas'}
      </div>
    </div>
  </div>`;

  const grid=sorted.map((t,i)=>{
    const nombre=t.mesa&&t.mesa.trim()?t.mesa.trim():`Cuenta ${t.foliocuenta||i+1}`;
    const mesero=t.nombre_mesero&&t.nombre_mesero!=='Sin asignar'?t.nombre_mesero:'Sin mesero';
    const mins=t.minutos_abierta||0;
    const tiempoTxt=mins>=60?`${Math.floor(mins/60)}h ${mins%60}m`:`${mins}m`;
    
    const badgeClass=mins>90?'critical':mins>45?'warn':'ok';
    const badgeLabel=mins>90?'Urgente':mins>45?'En riesgo':'Normal';
    const esUrgente=mins>90;
    return`<div class="mesa-card ocupada clickable ${esUrgente?'mesa-urgente':''}" onclick="openMesaModal(${Number(t.foliocuenta||0)})">
      <div class="mesa-num">${nombre}</div>
      <div class="mesa-chip ocupada"><div class="mesa-chip-dot"></div>Abierta</div>
      <div class="mesa-total">${fmt(t.total)}</div>
      <div class="mesa-mesero">${mesero}</div>
      <div class="mesa-tiempo-badge ${badgeClass}">${tiempoTxt} · ${badgeLabel}</div>
    </div>`;
  }).join('');
  $('mesas-body').innerHTML=header+`<div class="mesas-grid">${grid}</div>`;
}

let _mesasEstadoLoading = false;
let _mesasEstadoFetchedAt = 0;

function actualizarEstadoMesas(force = false){
  if (_mesasEstadoLoading) return;
  if (!force && cache.mesasEstado && Date.now() - _mesasEstadoFetchedAt < 4000) return;
  _mesasEstadoLoading = true;
  apiFetch('/mesas/estado-hoy').then(data => {
    if (data && typeof data === 'object') {
      cache.mesasEstado = data;
      _mesasEstadoFetchedAt = Date.now();
      if (currentTab === 'mesas') renderMesas();
    }
  }).catch(() => {}).finally(() => { _mesasEstadoLoading = false; });
}

function renderMesas(){
  actualizarEstadoMesas();
  const escMesa = window.ArkhonUtils && typeof window.ArkhonUtils.escapeHtml === 'function'
    ? window.ArkhonUtils.escapeHtml
    : (value => String(value ?? '').replace(/[&<>"']/g, char => ({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[char])));
  const normalizeMesas = window.ArkhonUtils && typeof window.ArkhonUtils.normalizeMesas === 'function'
    ? window.ArkhonUtils.normalizeMesas
    : (mesas => Array.isArray(mesas) ? mesas : []);
  const estado = cache.mesasEstado || {};
  const abiertas = normalizeMesas(Array.isArray(estado.abiertas) ? estado.abiertas : cache.mesas);
  const pagadas = Array.isArray(estado.pagadas) ? estado.pagadas : [];
  const resumen = estado.resumen || {};
  cache.mesas = abiertas;

  const sorted = [...abiertas].sort((a,b)=>(b.minutos_abierta||0)-(a.minutos_abierta||0));
  const totalAbierto = sorted.reduce((a,t)=>a+Number(t.total||0),0);
  const sinImprimir = sorted.filter(t=>Number(t.impreso)!==1).length;
  const impresasPendientes = sorted.filter(t=>Number(t.impreso)===1).length;
  const cuentasPagadas = Number(resumen.pagadas ?? cache.resumen?.cuentas_pagadas ?? 0);
  const totalPagado = Number(resumen.total_pagado ?? cache.resumen?.venta_pagada ?? 0);
  const urgentes = sorted.filter(t=>(t.minutos_abierta||0)>90).length;
  const enRiesgo = sorted.filter(t=>(t.minutos_abierta||0)>45&&(t.minutos_abierta||0)<=90).length;

  const header = `<div class="mu-header">
    <div class="mu-stat"><div class="mu-stat-label">Mesas abiertas</div><div class="mu-stat-val" style="color:var(--orange)">${sorted.length}</div><div class="mu-stat-sub">${fmt(totalAbierto)} en curso</div></div>
    <div class="mu-stat"><div class="mu-stat-label">Sin imprimir</div><div class="mu-stat-val" style="color:var(--orange)">${sinImprimir}</div><div class="mu-stat-sub">Cuentas todavía abiertas</div></div>
    <div class="mu-stat"><div class="mu-stat-label">Impresas, pendientes</div><div class="mu-stat-val" style="color:var(--purple)">${impresasPendientes}</div><div class="mu-stat-sub">Impresas sin pagar</div></div>
    <div class="mu-stat"><div class="mu-stat-label">Pagadas hoy</div><div class="mu-stat-val" style="color:var(--green)">${cuentasPagadas}</div><div class="mu-stat-sub">${fmt(totalPagado)} cobrado</div></div>
    <div class="mu-stat"><div class="mu-stat-label">En riesgo / urgente</div><div class="mu-stat-val mu-risk" style="color:${urgentes>0?'var(--red)':enRiesgo>0?'var(--yellow)':'var(--text-3)'}">${urgentes>0?`${urgentes} urgente${urgentes!==1?'s':''}`:enRiesgo>0?`${enRiesgo} en riesgo`:'Sin alertas'}</div></div>
  </div>`;

  const abiertasCards = sorted.map((t,i)=>{
    const nombre=t.mesa&&String(t.mesa).trim()?String(t.mesa).trim():`Cuenta ${t.foliocuenta||t.folio||i+1}`;
    const mesero=t.nombre_mesero&&t.nombre_mesero!=='Sin asignar'?t.nombre_mesero:'Sin mesero';
    const mins=Number(t.minutos_abierta||0);
    const tiempoTxt=mins>=60?`${Math.floor(mins/60)}h ${mins%60}m`:`${mins}m`;
    const impresa=Number(t.impreso)===1;
    const badgeClass=mins>90?'critical':mins>45?'warn':'ok';
    const badgeLabel=mins>90?'Urgente':mins>45?'En riesgo':'Normal';
    return `<div class="mesa-card ocupada clickable ${mins>90?'mesa-urgente':''}" onclick="openMesaModal(${Number(t.foliocuenta||t.folio||0)})">
      <div class="mesa-num">${escMesa(nombre)}</div>
      <div class="mesa-chip ${impresa?'impresa':'ocupada'}"><div class="mesa-chip-dot"></div>${impresa?'Impresa · pendiente':'Abierta · sin imprimir'}</div>
      <div class="mesa-total">${fmt(t.total)}</div><div class="mesa-mesero">${escMesa(mesero)}</div>
      <div class="mesa-tiempo-badge ${badgeClass}">${tiempoTxt} · ${badgeLabel}</div>
    </div>`;
  }).join('');

  const abiertasHtml = sorted.length
    ? `<div class="mesas-section-title">Cuentas abiertas <span>${sorted.length}</span></div><div class="mesas-grid">${abiertasCards}</div>`
    : '<div class="empty">Sin mesas abiertas en este momento</div>';

  const pagadasCards = pagadas.map(t=>{
    const nombre=t.mesa&&String(t.mesa).trim()?String(t.mesa).trim():`Cuenta ${t.numcheque||t.folio}`;
    const hora=t.fecha?new Date(t.fecha).toLocaleTimeString('es-MX',{hour:'2-digit',minute:'2-digit'}):'—';
    const referencia=t.numcheque?`Cheque #${t.numcheque}`:`Folio #${t.folio}`;
    return `<div class="mesa-card pagada">
      <div class="mesa-num">${escMesa(nombre)}</div><div class="mesa-chip pagada"><div class="mesa-chip-dot"></div>Pagada</div>
      <div class="mesa-total">${fmt(t.total)}</div><div class="mesa-mesero">${escMesa(referencia)} · ${escMesa(t.nombre_mesero||'Sin asignar')}</div>
      <div class="mesa-tiempo-badge paid">${hora}${Number(t.impreso)===1?' · Impresa':''}</div>
    </div>`;
  }).join('');
  const pagadasHtml = `<div class="mesas-section-title paid-title">Cuentas pagadas hoy <span>${cuentasPagadas}</span></div>${pagadasCards?`<div class="mesas-grid mesas-pagadas-grid">${pagadasCards}</div>`:'<div class="empty">Todavía no hay cuentas pagadas hoy</div>'}`;

  $('mesas-body').innerHTML = header + abiertasHtml + pagadasHtml;
}

function openMesaModal(folioCuenta){
  const m=(Array.isArray(cache.mesas)?cache.mesas:[]).find(item=>Number(item&&item.foliocuenta)===Number(folioCuenta));
  if(!m)return;
  const nombre=m.mesa&&m.mesa.trim()?m.mesa.trim():`Cuenta ${m.foliocuenta||folioCuenta}`;
  $('modal-mesa-name').textContent=nombre;
  $('modal-mesa-sub').textContent=m.nombre_mesero||'—';
  $('modal-total').textContent=fmt(m.total_actual||m.total||0);
  $('modal-body').innerHTML='<div class="spin-wrap"><div class="spinner"></div></div>';
  $('modal').classList.add('open');
  apiFetch(`/mesas/${m.foliocuenta}/detalle`).then(data=>{
    if(!data||!data.length){$('modal-body').innerHTML='<div class="empty">Sin detalle disponible</div>';return;}
    $('modal-body').innerHTML=data.map(p=>`<div class="modal-item"><div><div class="modal-prod">${p.producto||p.descripcion||'Producto'}</div><div class="modal-prod-sub">${p.grupo||''} · x${p.cantidad||1}</div></div><span class="modal-subtotal">${fmt(p.subtotal||p.total||0)}</span></div>`).join('');
  }).catch(()=>{$('modal-body').innerHTML='<div class="empty">Sin detalle disponible</div>';});
}
function closeModal(){$('modal').classList.remove('open');}

function renderProductos(){
  // New module handles this via prodReload() — just trigger it
  const kpis = document.getElementById('prod-kpis');
  if (kpis && !kpis.innerHTML.trim()) { try { prodReload(); } catch(_){} }
}

let _meseroChart=null;
function renderMeseros(){
  const me=cache.meseros;
  const kpiWrap=$('meseros-kpi');
  if(!me||!me.length){
    $('meseros-body').innerHTML='<div class="empty">Sin datos de meseros</div>';
    if(kpiWrap)kpiWrap.style.display='none';
    if(_meseroChart){_meseroChart.destroy();_meseroChart=null;}
    return;
  }
  const prev=cache.meserosPrev||{};
  const initials=n=>(n||'?').split(' ').map(p=>p[0]).join('').substring(0,2).toUpperCase();
  const colors=['#f97316','#10b981','#2563eb','#7c3aed','#f59e0b','#ef4444'];
  const shortName = value => {
    const text = String(value || '—').trim();
    return text.length > 16 ? `${text.slice(0, 14)}...` : text;
  };

  // ── Gráfica KPI
  if(kpiWrap){
    kpiWrap.style.display='block';
    const chartRows=me.slice(0,14);
    const labels=chartRows.map(m=>shortName(m.mesero||m.nombre||'—'));
    const ventas=chartRows.map(m=>m.venta_total||0);
    const tprom=chartRows.map(m=>m.ticket_promedio||0);
    const bgColors=chartRows.map((_,i)=>colors[i%colors.length]);
    const ctx=$('meseros-chart');
    if(ctx){
      if(_meseroChart){_meseroChart.destroy();_meseroChart=null;}
      _meseroChart=new Chart(ctx,{
        type:'bar',
        data:{
          labels,
          datasets:[
            {label:'Venta total',data:ventas,backgroundColor:ctx=>{const chart=ctx.chart;const area=chart.chartArea;if(!area)return bgColors[ctx.dataIndex%bgColors.length]+'cc';const g=chart.ctx.createLinearGradient(0,area.bottom,0,area.top);g.addColorStop(0,'rgba(249,115,22,.52)');g.addColorStop(1,'#f97316');return g;},borderColor:'rgba(249,115,22,.88)',borderWidth:1.5,borderRadius:10,borderSkipped:false,maxBarThickness:34,yAxisID:'y'},
            {label:'Ticket promedio',data:tprom,type:'line',borderColor:'#334155',backgroundColor:'rgba(51,65,85,.08)',borderWidth:2.5,pointRadius:4,pointHoverRadius:6,pointBackgroundColor:'#fff',pointBorderColor:'#334155',pointBorderWidth:2,tension:.35,fill:false,yAxisID:'y2'},
          ]
        },
        options:{
          responsive:true,maintainAspectRatio:false,
          animation:{duration:700,easing:'easeOutQuart'},
          layout:{padding:{top:8,right:10,left:4,bottom:0}},
          plugins:{
            legend:{display:true,labels:{usePointStyle:true,pointStyle:'circle',font:{family:"'Plus Jakarta Sans',sans-serif",size:11,weight:'700'},boxWidth:8,boxHeight:8,color:'#475569',padding:14}},
            tooltip:{
              backgroundColor:'rgba(15,23,42,.94)',
              padding:10,
              cornerRadius:8,
              displayColors:true,
              bodyFont:{family:"'Plus Jakarta Sans',sans-serif"},
              titleFont:{family:"'Plus Jakarta Sans',sans-serif",weight:'700'},
              callbacks:{label:ctx=>{const v=ctx.parsed.y;return` ${ctx.dataset.label}: $${Number(v).toLocaleString('es-MX',{minimumFractionDigits:0,maximumFractionDigits:0})}`;}}
            }
          },
          scales:{
            x:{grid:{display:false},ticks:{font:{family:"'Plus Jakarta Sans',sans-serif",size:10,weight:'700'},color:'#64748b',maxRotation:35,minRotation:28}},
            y:{position:'left',beginAtZero:true,border:{display:false},grid:{color:'rgba(148,163,184,.14)'},ticks:{font:{family:"'Plus Jakarta Sans',sans-serif",size:10,weight:'600'},color:'#94a3b8',callback:v=>v>=1000000?`$${(v/1000000).toFixed(1)}M`:v>=1000?`$${(v/1000).toFixed(0)}K`:`$${v}`}},
            y2:{position:'right',beginAtZero:true,border:{display:false},grid:{display:false},ticks:{font:{family:"'Plus Jakarta Sans',sans-serif",size:10,weight:'600'},color:'#64748b',callback:v=>v>=1000?`$${(v/1000).toFixed(0)}K`:`$${v}`}},
          }
        }
      });
    }
  }

  // ── Tarjetas individuales
  const ventaTotal=me.reduce((s,m)=>s+(m.venta_total||0),0);
  $('meseros-body').innerHTML=`<div class="meseros-grid">${me.map((m,i)=>{
    const nombre=m.mesero||m.nombre||'—';
    const venta=m.venta_total||0;
    const ventaAyer=prev[nombre]||0;
    const pct=ventaTotal>0?Math.round(venta/ventaTotal*100):0;
    const diff=ventaAyer>0?((venta-ventaAyer)/ventaAyer*100):null;
    const diffHtml=diff!==null?`<span style="font-size:9px;font-weight:700;color:${diff>=0?'#10b981':'#ef4444'};margin-left:4px">${diff>=0?'▲':'▼'}${Math.abs(diff).toFixed(1)}% vs ayer</span>`:'';
    const propinas=m.propinas||0;
    return`<div class="mesero-card">
      <div class="mesero-avatar" style="background:${colors[i%colors.length]}">${initials(nombre)}</div>
      <div style="flex:1;min-width:0">
        <div class="mesero-name">${nombre}${diffHtml}</div>
        <div class="mesero-meta">${m.tickets||0} cuentas · $${Number(m.ticket_promedio||0).toFixed(0)} prom.${propinas>0?` · prop. ${fmt(propinas)}`:''}</div>
        <div style="margin-top:6px;background:var(--bg);border-radius:4px;height:4px;overflow:hidden"><div style="height:4px;border-radius:4px;background:${colors[i%colors.length]};width:${pct}%"></div></div>
        <div style="font-size:9.5px;color:var(--text-3);font-weight:600;margin-top:2px">${pct}% del total del periodo</div>
      </div>
      <div class="mesero-right"><div class="mesero-total">${fmt(venta)}</div><div class="mesero-tickets">${m.tickets||0} tickets</div></div>
    </div>`;
  }).join('')}</div>`;
}

function renderTurnos(){
  const tu=cache.turnos;
  const esHoy=periodoActual==='hoy';
  if(!tu||!tu.length){$('turnos-body').innerHTML=`<div class="empty">Sin datos de turnos${esHoy?' hoy':' en el periodo seleccionado'}</div>`;return;}
  const fmtHora=d=>{if(!d)return'—';const dt=new Date(d);return dt.toLocaleTimeString('es-MX',{hour:'2-digit',minute:'2-digit'});};
  const fmtFecha=d=>{if(!d)return'';const dt=new Date(d);return dt.toLocaleDateString('es-MX',{weekday:'short',day:'numeric',month:'short'});};
  $('turnos-body').innerHTML=`<div class="turnos-grid">${tu.map(t=>{
    const activo=!t.cierre&&esHoy;
    const tprom=t.ticket_promedio||0;
    const fechaLabel=!esHoy?`<div style="font-size:9px;font-weight:700;color:var(--orange);text-transform:uppercase;letter-spacing:.05em;margin-bottom:6px">${fmtFecha(t.apertura)}</div>`:'';
    return`<div class="turno-card" style="position:relative">
      ${fechaLabel}
      <div class="turno-header">
        <div class="turno-name">${t.nombre_turno||'Turno'} <span style="color:var(--text-3);font-size:11px;font-weight:500">#${t.idturno}</span></div>
        <div class="turno-badge ${activo?'activo':'cerrado'}">${activo?'Activo':'Cerrado'}</div>
      </div>
      <div class="turno-stats">
        <div><div class="turno-stat-lbl">Total vendido</div><div class="turno-stat-val">${fmt(t.venta_total||0)}</div></div>
        <div><div class="turno-stat-lbl">Tickets</div><div class="turno-stat-val">${t.tickets||0}</div></div>
        <div><div class="turno-stat-lbl">Ticket promedio</div><div class="turno-stat-val" style="font-size:13px">${tprom>0?fmt(tprom):'—'}</div></div>
        <div><div class="turno-stat-lbl">Cajero</div><div class="turno-stat-val" style="font-size:12px;font-weight:700">${t.cajero||'—'}</div></div>
        <div><div class="turno-stat-lbl">Inicio</div><div class="turno-stat-val" style="font-size:12px">${fmtHora(t.apertura)}</div></div>
        <div><div class="turno-stat-lbl">Cierre</div><div class="turno-stat-val" style="font-size:12px">${fmtHora(t.cierre)}</div></div>
      </div>
      <div style="margin-top:12px;display:flex;gap:6px;flex-wrap:wrap">
        <button onclick="abrirCorteZTurno(${t.idturno}, ${activo})" style="display:flex;align-items:center;gap:5px;padding:6px 13px;background:var(--orange);color:#fff;border:none;border-radius:7px;font-size:11px;font-weight:700;cursor:pointer;box-shadow:0 2px 8px rgba(249,115,22,.28);font-family:inherit;transition:.15s;" onmouseover="this.style.transform='translateY(-1px)'" onmouseout="this.style.transform=''">
          <svg viewBox="0 0 24 24" style="width:12px;height:12px;stroke:#fff;stroke-width:2.2;fill:none;"><path d="M14 2H6a2 2 0 00-2 2v16a2 2 0 002 2h12a2 2 0 002-2V8z"/><polyline points="14 2 14 8 20 8"/><line x1="16" y1="13" x2="8" y2="13"/><line x1="16" y1="17" x2="8" y2="17"/><polyline points="10 9 9 9 8 9"/></svg>
          Ver Corte ${activo ? 'X' : 'Z'}
        </button>
      </div>
    </div>`;
  }).join('')}</div>`;
}


// ── DOMICILIOS ────────────────────────────────────────────────────────────────
const STATUS_DOM = {
  0: { label:'En cocina',  cls:'dom-cocina'  },
  1: { label:'Listo',      cls:'dom-listo'   },
  2: { label:'Asignado',   cls:'dom-asignado'},
  3: { label:'En ruta',    cls:'dom-ruta'    },
  4: { label:'Entregado',  cls:'dom-entregado'},
};

function fmtHoraMin(d){
  if(!d) return '—';
  try{ return new Date(d).toLocaleTimeString('es-MX',{hour:'2-digit',minute:'2-digit'}); }
  catch{ return '—'; }
}

function tiempoTxt(mins){
  if(!mins&&mins!==0) return '—';
  if(mins>=60) return `${Math.floor(mins/60)}h ${mins%60}m`;
  return `${mins}m`;
}

function renderDomicilios(){
  const dom = cache.domicilios;
  const body = $('domicilios-body');
  if(!body) return;

  if(!dom || !dom.length){
    body.innerHTML='<div class="empty">Sin pedidos de domicilio activos en este momento</div>';
    return;
  }

  const totalEnRuta   = dom.filter(d=>d.status_domicilio===3).length;
  const totalListo    = dom.filter(d=>d.status_domicilio===1||d.status_domicilio===2).length;
  const totalCocina   = dom.filter(d=>!d.status_domicilio||d.status_domicilio===0).length;
  const totalPendiente = dom.filter(d=>Number(d.pagado)!==1).reduce((a,d)=>a+Number(d.total_con_cargo||d.total||0),0);
  const totalImpresos  = dom.filter(d=>Number(d.impreso)===1).length;
  const totalPagados   = dom.filter(d=>Number(d.pagado)===1).length;
  const maxEspera     = Math.max(...dom.map(d=>d.minutos_espera||0));
  const urgentes      = dom.filter(d=>(d.minutos_espera||0)>45).length;

  const header = `<div class="mu-header">
    <div class="mu-stat">
      <div class="mu-stat-label">Pedidos activos</div>
      <div class="mu-stat-val" style="color:var(--orange)">${dom.length}</div>
    </div>
    <div class="mu-stat">
      <div class="mu-stat-label">Pendiente de pago</div>
      <div class="mu-stat-val" style="color:${totalPendiente>0?'var(--orange)':'var(--green)'}">${fmt(totalPendiente)}</div>
    </div>
    <div class="mu-stat">
      <div class="mu-stat-label">Impresos / Pagados</div>
      <div class="mu-stat-val" style="color:var(--text-2);font-size:14px">${totalImpresos} &nbsp;·&nbsp; ${totalPagados}</div>
    </div>
    <div class="mu-stat">
      <div class="mu-stat-label">En cocina / Listos / En ruta</div>
      <div class="mu-stat-val" style="color:var(--text-2);font-size:14px">
        ${totalCocina} &nbsp;·&nbsp; ${totalListo} &nbsp;·&nbsp; ${totalEnRuta}
      </div>
    </div>
    <div class="mu-stat">
      <div class="mu-stat-label">${urgentes>0?'Tardados (>45min)':'Tiempos'}</div>
      <div class="mu-stat-val" style="color:${urgentes>0?'var(--red)':'var(--text-3)'}">
        ${urgentes>0?`${urgentes} urgente${urgentes!==1?'s':''}`:'Sin alertas'}
      </div>
    </div>
  </div>`;

  const cards = dom.map(d=>{
    const st    = STATUS_DOM[d.status_domicilio||0] || STATUS_DOM[0];
    const impreso = Number(d.impreso) === 1;
    const pagado  = Number(d.pagado) === 1;
    const mins  = d.minutos_espera||0;
    const urgente = mins > 45;
    const tel   = d.telefono && d.telefono.trim() ? d.telefono.trim() : '—';
    const obs   = d.observaciones && d.observaciones.trim() ? d.observaciones.trim() : '';
    const mesaLabel = d.mesa && d.mesa.trim() ? d.mesa.trim() : `Folio ${d.folio}`;
    const areaLabel = d.area==='02'?'Domicilio':d.area==='03'?'Rápido':'Servicio';
    const cliente = d.cliente_nombre?.trim() || 'Cliente no registrado';
    const direccionPartes = [
      d.direccion_calle,
      d.direccion_numero ? `#${d.direccion_numero}` : '',
      d.direccion_interior ? `Int. ${d.direccion_interior}` : '',
      d.direccion_colonia,
      d.direccion_ciudad,
    ].filter(Boolean);
    const direccion = direccionPartes.join(', ') || (d.iddireccion ? `Dirección: ${d.iddireccion}` : 'Dirección no capturada');
    const cruces = [d.direccion_cruce_1,d.direccion_cruce_2].filter(Boolean).join(' y ');
    const productos = Array.isArray(d.productos) ? d.productos : [];
    const pedidoRows = productos.map(item => {
      const modificador = Number(item.modificador || 0) === 1;
      const qty = Number(item.cantidad || 0);
      const qtyLabel = Number.isInteger(qty) ? qty : qty.toFixed(2);
      return `<div class="dom-product-row${modificador?' is-modifier':''}">
        <span class="dom-product-qty">${modificador?'↳':qtyLabel+'×'}</span>
        <span class="dom-product-name">${prodEscape(item.producto || item.idproducto || 'Producto')}${item.comentario?`<small>${prodEscape(item.comentario)}</small>`:''}</span>
        <strong>${Number(item.importe||0)>0?fmt(item.importe):''}</strong>
      </div>`;
    }).join('');

    // Timeline pills
    const t1 = d.empaquetado ? `<span class="dom-tl-pill done">Prep ${fmtHoraMin(d.empaquetado)}</span>` : `<span class="dom-tl-pill pend">Prep -</span>`;
    const t2 = d.asignacion ? `<span class="dom-tl-pill done">Asignado ${fmtHoraMin(d.asignacion)}</span>` : `<span class="dom-tl-pill pend">Asignado -</span>`;
    const t3 = d.salida_repartidor ? `<span class="dom-tl-pill done">Salida ${fmtHoraMin(d.salida_repartidor)}</span>` : `<span class="dom-tl-pill pend">Salida -</span>`;

    return `<article class="dom-order-card${urgente?' mesa-urgente':''}">
      <div class="dom-order-head">
        <div><div class="dom-order-folio">${prodEscape(mesaLabel)}</div><div class="dom-order-meta">${prodEscape(areaLabel)} · ${Number(d.idturno||0)>0?'Turno #'+d.idturno:'Sin turno asignado'} · ${prodEscape(d.estacion || 'Sin estación')}</div></div>
        <div class="dom-order-statuses">
          <span class="dom-status-badge ${st.cls}">${st.label}</span>
          <span class="dom-account-badge ${impreso?'done':'pending'}">${impreso?`Impreso${Number(d.impresiones)>1?` (${d.impresiones})`:''}`:'Sin imprimir'}</span>
          <span class="dom-account-badge ${pagado?'paid':'pending'}">${pagado?'Pagado':'Pendiente de pago'}</span>
        </div>
      </div>
      <div class="dom-order-content">
        <section class="dom-customer-block">
          <div class="dom-block-label">Cliente y entrega</div>
          <div class="dom-customer-name">${prodEscape(cliente)}</div>
          <a class="dom-customer-phone" href="tel:${prodEscape(tel)}">☎ ${prodEscape(tel)}</a>
          <div class="dom-address">${prodEscape(direccion)}</div>
          ${cruces?`<div class="dom-address-detail">Entre ${prodEscape(cruces)}</div>`:''}
          ${d.direccion_referencia?`<div class="dom-address-detail">Referencia: ${prodEscape(d.direccion_referencia)}</div>`:''}
          ${obs?`<div class="dom-order-note">${prodEscape(obs)}</div>`:''}
          <div class="dom-driver">Repartidor: ${prodEscape(d.repartidor || 'Sin asignar')}</div>
        </section>
        <section class="dom-products-block">
          <div class="dom-block-label">Pedido · ${productos.filter(p=>Number(p.modificador||0)===0).length} productos</div>
          <div class="dom-products-list">${pedidoRows || '<div class="dom-products-empty">Sin detalle de productos</div>'}</div>
        </section>
      </div>
      <footer class="dom-order-footer">
        <div class="dom-timeline">${t1}${t2}${t3}<span class="mesa-tiempo-badge ${urgente?'critical':mins>20?'warn':'ok'}">${tiempoTxt(mins)} · ${urgente?'Tardado':mins>20?'En tiempo':'Reciente'}</span></div>
        <div class="dom-order-total"><span>${d.cargo_envio>0?`Pedido ${fmt(d.total||0)} + envío ${fmt(d.cargo_envio)}`:'Total del pedido'}</span><strong>${fmt(d.total_con_cargo||d.total||0)}</strong></div>
      </footer>
    </article>`;
  }).join('');

  body.innerHTML = header + `<div class="dom-orders-grid">${cards}</div>`;
}

// ── TURNO → CORTE Z / X ──────────────────────────────────────────────────────────
function abrirCorteZTurno(idturno, isX = false){
  // Pasamos idturno en la URL directamente — evita la race condition del postMessage
  // (el init de corte-z.html leia params y auto-cargaba hoy antes de que llegara el mensaje)
  switchTab('reportes', null);
  setTimeout(()=>{
    const iframe = document.getElementById('iframe-reportes');
    const panel  = document.getElementById('reportes-panel');
    const wrap   = document.getElementById('reportes-iframe-wrap');
    const title  = document.getElementById('rpt-iframe-title');
    if(panel) panel.style.display='none';
    if(wrap)  wrap.style.display='block';
    const reportName = isX ? 'X' : 'Z';
    if(title) title.textContent=`Corte de Caja ${reportName}`;
    if(iframe) iframe.src = `/corte-z?idturno=${idturno}&tipo=${reportName}`;
  }, 60);
}

// ══════════════════════════════════════════════════════════════════════════════
// ANALYTICS DE PRODUCTOS — Gráficas de pastel + KPIs + Baja rotación
// ══════════════════════════════════════════════════════════════════════════════
// MÓDULO DE PRODUCTOS — Estilo ERP / SAP
// KPIs + Tabla detallada + Filtros por período/turno/grupo + Sin movimiento
// ══════════════════════════════════════════════════════════════════════════════
let _prodPeriodo = 'dia';
let _prodData = null;
let _prodChartTop = null;
let _prodChartCat = null;

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

function prodEstado(index, total) {
  if (index < total * 0.3) return { color: '#22c55e', label: 'Alto' };
  if (index < total * 0.7) return { color: '#f59e0b', label: 'Medio' };
  return { color: '#ef4444', label: 'Bajo' };
}

function prodLegendPosition() {
  return 'bottom';
}

const PROD_CHART_COLORS = ['#f97316', '#2563eb', '#16a34a', '#f59e0b', '#7c3aed', '#0891b2', '#db2777', '#dc2626', '#65a30d', '#4f46e5'];

const prodArcShadowPlugin = {
  id: 'prodArcShadow',
  beforeDatasetDraw() {},
  afterDatasetDraw() {}
};

const prodPercentLabelsPlugin = {
  id: 'prodPercentLabels',
  afterDatasetsDraw(chart, args, opts = {}) {
    const dataset = chart.data.datasets?.[0];
    const values = dataset?.data || [];
    const total = values.reduce((sum, value) => sum + Number(value || 0), 0);
    if (!total) return;
    const explicitPercentages = Array.isArray(chart.options.plugins?.prodPercentLabels?.percentages)
      ? chart.options.plugins.prodPercentLabels.percentages
      : null;
    const meta = chart.getDatasetMeta(0);
    const minPercent = opts.minPercent ?? 3;
    const { ctx } = chart;
    ctx.save();
    ctx.textAlign = 'center';
    ctx.textBaseline = 'middle';
    ctx.font = '800 10px "Plus Jakarta Sans", Inter, system-ui, sans-serif';
    meta.data.forEach((arc, index) => {
      const pct = explicitPercentages ? Number(explicitPercentages[index] || 0) : Number(values[index] || 0) / total * 100;
      if (pct < minPercent) return;
      const pos = arc.tooltipPosition();
      const label = `${pct >= 10 ? pct.toFixed(0) : pct.toFixed(1)}%`;
      ctx.fillStyle = '#111827';
      ctx.fillText(label, pos.x, pos.y);
    });
    ctx.restore();
  }
};

function prodRenderSideList(anchorId, rows, valueKey = 'venta') {
  const canvas = document.getElementById(anchorId);
  const box = canvas?.parentElement;
  if (!box) return;
  box.classList.add('prod-chart-layout');
  let list = box.querySelector('.prod-chart-side-list');
  if (!list) {
    list = document.createElement('div');
    list.className = 'prod-chart-side-list';
    box.appendChild(list);
  }
  const data = Array.isArray(rows) ? rows : [];
  list.innerHTML = data.map((row, index) => {
    const name = prodEscape(row.producto || row.descripcion || row.categoria || 'Sin nombre');
    const pct = Number(row.porcentaje || 0);
    const value = valueKey === 'cantidad'
      ? Number(row.cantidad || row.total_cantidad || 0).toLocaleString('es-MX')
      : prodMoney(row[valueKey] || row.total_importe || 0);
    return `<div class="prod-chart-side-item">
      <span class="prod-chart-side-dot" style="background:${PROD_CHART_COLORS[index % PROD_CHART_COLORS.length]}"></span>
      <span class="prod-chart-side-name" title="${name}">${name}</span>
      <span class="prod-chart-side-value">${value}${pct ? ` · ${pct.toFixed(1)}%` : ''}</span>
    </div>`;
  }).join('');
}

function prodMoney(value) {
  return `$${Number(value || 0).toLocaleString('es-MX', { minimumFractionDigits: 2 })}`;
}

function prodRenderComparePicker() {
  const select = document.getElementById('prod-compare-filter');
  const optionsBox = document.getElementById('prod-compare-options');
  if (!select || !optionsBox) return;
  optionsBox.innerHTML = [...select.options].map(option => {
    const label = prodEscape(option.textContent || option.value);
    return `<label class="prod-compare-option" data-search="${prodEscape((option.textContent || '').toLocaleLowerCase('es-MX'))}">
      <input type="checkbox" value="${prodEscape(option.value)}" ${option.selected ? 'checked' : ''} onchange="prodSetCompareSelection(this.value,this.checked)">
      <span title="${label}">${label}</span>
    </label>`;
  }).join('') || '<div class="prod-compare-empty-option">No hay productos disponibles.</div>';
  prodUpdateCompareSummary();
}

function prodUpdateCompareSummary() {
  const select = document.getElementById('prod-compare-filter');
  const label = document.getElementById('prod-compare-trigger-label');
  const count = document.getElementById('prod-compare-count');
  if (!select || !label || !count) return;
  const selected = [...select.selectedOptions];
  count.textContent = selected.length;
  label.textContent = !selected.length
    ? 'Seleccionar productos'
    : selected.length === 1
      ? selected[0].textContent
      : `${selected.length} productos seleccionados`;
}

function prodToggleComparePicker(event) {
  event?.stopPropagation();
  const popover = document.getElementById('prod-compare-popover');
  const trigger = document.getElementById('prod-compare-trigger');
  if (!popover || !trigger) return;
  const willOpen = popover.hidden;
  popover.hidden = !willOpen;
  trigger.setAttribute('aria-expanded', String(willOpen));
  if (willOpen) {
    const search = document.getElementById('prod-compare-search');
    if (search) { search.value = ''; prodFilterCompareOptions(''); setTimeout(() => search.focus(), 0); }
  }
}

function prodCloseComparePicker() {
  const popover = document.getElementById('prod-compare-popover');
  const trigger = document.getElementById('prod-compare-trigger');
  if (popover) popover.hidden = true;
  if (trigger) trigger.setAttribute('aria-expanded', 'false');
}

function prodSetCompareSelection(value, checked) {
  const option = [...(document.getElementById('prod-compare-filter')?.options || [])].find(item => item.value === value);
  if (option) option.selected = checked;
  prodUpdateCompareSummary();
}

function prodFilterCompareOptions(value) {
  const query = String(value || '').trim().toLocaleLowerCase('es-MX');
  document.querySelectorAll('#prod-compare-options .prod-compare-option').forEach(option => {
    option.hidden = query ? !String(option.dataset.search || '').includes(query) : false;
  });
}

function prodApplyCompare(event) {
  event?.stopPropagation();
  prodCloseComparePicker();
  prodReload();
}

function prodClearCompare(event) {
  event?.stopPropagation();
  const select = document.getElementById('prod-compare-filter');
  if (select) [...select.options].forEach(option => { option.selected = false; });
  prodRenderComparePicker();
  prodCloseComparePicker();
  prodReload();
}

function prodCenterPlugin(label, value) {
  return {
    id: `prodCenter${String(label).replace(/\W/g, '')}`,
    afterDraw(chart) {
      const point = chart.getDatasetMeta(0)?.data?.[0];
      if (!point) return;
      const { ctx } = chart;
      ctx.save();
      ctx.textAlign = 'center';
      ctx.textBaseline = 'middle';
      ctx.fillStyle = '#0f172a';
      ctx.font = '800 18px Inter, system-ui, sans-serif';
      ctx.fillText(value, point.x, point.y - 7);
      ctx.fillStyle = '#64748b';
      ctx.font = '700 10px Inter, system-ui, sans-serif';
      ctx.fillText(label, point.x, point.y + 13);
      ctx.restore();
    }
  };
}

function prodDoughnutOptions(labelFormatter) {
  const compactLabel = value => {
    const text = String(value || '');
    return text.length > 22 ? `${text.slice(0, 19)}...` : text;
  };
  return {
    responsive: true,
    maintainAspectRatio: false,
    cutout: '68%',
    animation: { duration: 650, easing: 'easeOutQuart' },
    layout: { padding: { top: 6, right: 8, bottom: 4, left: 8 } },
    plugins: {
      legend: {
        display: false,
        position: prodLegendPosition(),
        labels: {
          usePointStyle: true,
          pointStyle: 'circle',
          font: { size: 10, weight: '700' },
          color: '#475569',
          padding: 10,
          boxWidth: 8,
          generateLabels(chart) {
            const labels = Chart.defaults.plugins.legend.labels.generateLabels(chart);
            return labels.map(label => ({ ...label, text: compactLabel(label.text) }));
          }
        }
      },
      tooltip: {
        backgroundColor: 'rgba(17,24,39,.97)',
        titleColor: '#fff',
        bodyColor: '#e5e7eb',
        displayColors: true,
        padding: 8,
        cornerRadius: 8,
        caretSize: 5,
        boxPadding: 4,
        titleFont: { size: 11, weight: '800' },
        bodyFont: { size: 11, weight: '650' },
        titleMarginBottom: 4,
        multiKeyBackground: '#fff',
        xAlign: 'center',
        yAlign: 'bottom',
        callbacks: {
          title(items) {
            const label = items && items[0] ? items[0].label : '';
            return compactLabel(label);
          },
          label: labelFormatter
        }
      }
    }
  };
}

function prodHorizontalBarOptions(rows) {
  return {
    responsive: true,
    maintainAspectRatio: false,
    indexAxis: 'y',
    animation: { duration: 500, easing: 'easeOutQuart' },
    layout: { padding: { top: 4, right: 92, bottom: 2, left: 2 } },
    interaction: { mode: 'nearest', axis: 'y', intersect: false },
    plugins: {
      legend: { display: false },
      tooltip: {
        backgroundColor: 'rgba(17,24,39,.97)',
        padding: 9,
        cornerRadius: 8,
        callbacks: {
          label(ctx) {
            const row = rows[ctx.dataIndex] || {};
            return ` ${prodMoney(ctx.raw)} · ${Number(row.porcentaje || 0).toFixed(1)}%`;
          }
        }
      }
    },
    scales: {
      x: {
        beginAtZero: true,
        border: { display: false },
        grid: { color: 'rgba(148,163,184,.16)', drawTicks: false },
        ticks: { color: '#94a3b8', font: { size: 9, weight: '650' }, padding: 6, callback: value => `$${Number(value).toLocaleString('es-MX',{notation:'compact',maximumFractionDigits:1})}` }
      },
      y: {
        border: { display: false },
        grid: { display: false },
        ticks: { color: '#334155', font: { size: 9.5, weight: '750' }, autoSkip: false, padding: 6 }
      }
    }
  };
}

function prodBarLabelsPlugin(rows) {
  return {
    id: `prodBarLabels${rows.length}`,
    afterDatasetsDraw(chart) {
      const meta = chart.getDatasetMeta(0);
      const { ctx, chartArea } = chart;
      ctx.save();
      ctx.textAlign = 'left';
      ctx.textBaseline = 'middle';
      ctx.fillStyle = '#64748b';
      ctx.font = '750 9.5px "Plus Jakarta Sans", Inter, sans-serif';
      meta.data.forEach((bar, index) => {
        const row = rows[index] || {};
        const label = `${prodMoney(row.venta || 0)} · ${Number(row.porcentaje || 0).toFixed(1)}%`;
        ctx.fillText(label, Math.min(bar.x + 7, chartArea.right + 7), bar.y);
      });
      ctx.restore();
    }
  };
}

function prodToday() {
  const d = new Date();
  return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}`;
}

function prodEnsureYearOptions() {
  const sel = document.getElementById('prod-anio-filter');
  if (!sel || sel.options.length) return;
  const y = new Date().getFullYear();
  for (let year = y; year >= y - 6; year--) {
    const o = document.createElement('option');
    o.value = String(year);
    o.textContent = String(year);
    sel.appendChild(o);
  }
  sel.value = String(y);
}

function prodEnsureRangeDefaults() {
  const desde = document.getElementById('prod-fecha-desde');
  const hasta = document.getElementById('prod-fecha-hasta');
  if (!desde || !hasta) return;
  const today = prodToday();
  if (!hasta.value) hasta.value = today;
  if (!desde.value) {
    const d = new Date(`${today}T12:00:00`);
    d.setDate(d.getDate() - 6);
    desde.value = `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}`;
  }
}

function prodSyncPeriodControls() {
  prodEnsureYearOptions();
  prodEnsureRangeDefaults();
  const yearSel = document.getElementById('prod-anio-filter');
  const rangeEl = document.getElementById('prod-custom-range');
  if (yearSel) yearSel.style.display = _prodPeriodo === 'anio' ? '' : 'none';
  if (rangeEl) rangeEl.style.display = _prodPeriodo === 'rango' ? 'flex' : 'none';
}

function prodFilter(periodo, btn) {
  _prodPeriodo = periodo;
  document.querySelectorAll('#prod-period-btns .period-btn').forEach(b => b.classList.remove('active'));
  if (btn) btn.classList.add('active');
  prodSyncPeriodControls();
  ['prod-turno-filter','prod-estacion-filter','prod-area-filter'].forEach(id => {
    const el = document.getElementById(id);
    if (!el) return;
    const first = el.options[0]?.outerHTML || '<option value="">Todos</option>';
    el.innerHTML = first;
    el.value = '';
  });
  prodReload();
}

function prodApplyRange() {
  if (_prodPeriodo === 'rango') prodReload();
}

function prodSwitchTab(tab) {
  const vend = document.getElementById('prod-vendidos-body');
  const sinm = document.getElementById('prod-sinmov-body');
  const btnV = document.getElementById('prod-tab-vendidos');
  const btnS = document.getElementById('prod-tab-sinmov');
  if (tab === 'sinmov') {
    vend.style.display = 'none'; sinm.style.display = '';
    btnV.classList.remove('active'); btnS.classList.add('active');
  } else {
    vend.style.display = ''; sinm.style.display = 'none';
    btnV.classList.add('active'); btnS.classList.remove('active');
  }
}

async function prodReload() {
  prodSyncPeriodControls();
  const turno = document.getElementById('prod-turno-filter')?.value || '';
  const grupo = document.getElementById('prod-grupo-filter')?.value || '';
  const estacion = document.getElementById('prod-estacion-filter')?.value || '';
  const area = document.getElementById('prod-area-filter')?.value || '';
  const compareSel = document.getElementById('prod-compare-filter');
  const compararIds = compareSel ? [...compareSel.selectedOptions].map(o => o.value).filter(Boolean) : [];
  const anio = document.getElementById('prod-anio-filter')?.value || '';
  const fechaDesde = document.getElementById('prod-fecha-desde')?.value || '';
  const fechaHasta = document.getElementById('prod-fecha-hasta')?.value || '';
  let url = `/api/productos/analytics?periodo=${_prodPeriodo}`;
  if (_prodPeriodo === 'anio' && anio) url += `&anio=${encodeURIComponent(anio)}`;
  if (_prodPeriodo === 'rango') {
    if (fechaDesde) url += `&fechaDesde=${encodeURIComponent(fechaDesde)}`;
    if (fechaHasta) url += `&fechaHasta=${encodeURIComponent(fechaHasta)}`;
  }
  if (turno) url += `&turno=${turno}`;
  if (grupo) url += `&grupo=${encodeURIComponent(grupo)}`;
  if (estacion) url += `&estacion=${encodeURIComponent(estacion)}`;
  if (area) url += `&area=${encodeURIComponent(area)}`;
  if (compararIds.length) url += `&compararIds=${encodeURIComponent(compararIds.join(','))}`;

  try {
    const kpisEl = document.getElementById('prod-kpis');
    if (kpisEl) kpisEl.innerHTML = '<div style="grid-column:1/-1;color:var(--text-3);font-size:12px;">Cargando productos...</div>';
    const token = window.LOCAL_API_KEY || (typeof LOCAL_API_KEY !== 'undefined' ? LOCAL_API_KEY : '');
    const res = await fetch(url, {
      credentials: 'include',
      headers: { 'Content-Type':'application/json', 'X-Api-Token': token }
    });
    if (res.status === 401) {
      // Try refresh token
      try {
        const rr = await fetch('/api/auth/refresh', { method:'POST', credentials:'include' });
        const rd = await rr.json();
        if (rd.ok && rd.token) {
          window.LOCAL_API_KEY = rd.token;
          return prodReload(); // retry
        }
      } catch(_){}
      document.getElementById('prod-kpis').innerHTML = '<div style="grid-column:1/-1;color:var(--text-3);font-size:12px;">Sesión expirada — recarga la página</div>';
      return;
    }
    if (!res.ok) {
      throw new Error(`Endpoint productos respondio ${res.status}`);
    }
    const data = await res.json();
    if (!data || !data.ok || !data.resumen) {
      document.getElementById('prod-kpis').innerHTML = `<div style="grid-column:1/-1;color:var(--text-3);font-size:12px;">Sin datos: ${data?.error || 'respuesta vacía'}</div>`;
      return;
    }
    _prodData = data;
    prodRender(data);
  } catch(e) {
    document.getElementById('prod-kpis').innerHTML = `<div style="grid-column:1/-1;color:var(--text-3);font-size:12px;">Error: ${e.message}</div>`;
  }
}

function prodRenderComparePanel(rows, resumen = {}) {
  const panel = document.getElementById('prod-compare-panel');
  if (!panel) return;
  const data = Array.isArray(rows) ? rows : [];
  if (!data.length) {
    panel.hidden = true;
    panel.innerHTML = '';
    return;
  }
  panel.hidden = false;
  const totalVenta = data.reduce((sum, row) => sum + Number(row.venta || 0), 0);
  const totalUnidades = data.reduce((sum, row) => sum + Number(row.cantidad || 0), 0);
  const winner = [...data].sort((a, b) => Number(b.venta || 0) - Number(a.venta || 0))[0];
  const ventaBase = Number(resumen.venta_total || 0);
  const shareGlobal = ventaBase > 0 ? (totalVenta / ventaBase) * 100 : 0;
  const bars = data.map((row, index) => {
    const venta = Number(row.venta || 0);
    const cantidad = Number(row.cantidad || 0);
    const pct = totalVenta > 0 ? (venta / totalVenta) * 100 : 0;
    const color = PROD_CHART_COLORS[index % PROD_CHART_COLORS.length];
    return `<div class="prod-compare-row">
      <div class="prod-compare-name"><span style="background:${color}"></span>${prodEscape(row.termino)}</div>
      <div class="prod-compare-bar"><i style="width:${Math.max(pct, 2)}%;background:${color}"></i></div>
      <div class="prod-compare-metric">${cantidad.toLocaleString('es-MX')} u · ${prodMoney(venta)} · ${pct.toFixed(1)}%</div>
    </div>`;
  }).join('');
  panel.innerHTML = `
    <div class="prod-compare-head">
      <div>
        <div class="prod-compare-title">Comparativo de productos</div>
        <div class="prod-compare-sub">Aplicado al periodo, turno, estación, área y categoría seleccionados.</div>
      </div>
      <div class="prod-compare-winner">Lidera: <b>${prodEscape(winner.termino)}</b></div>
    </div>
    <div class="prod-compare-kpis">
      <div><span>Venta comparada</span><b>${prodMoney(totalVenta)}</b></div>
      <div><span>Unidades</span><b>${totalUnidades.toLocaleString('es-MX')}</b></div>
      <div><span>Peso en venta total</span><b>${shareGlobal.toFixed(1)}%</b></div>
    </div>
    <div class="prod-compare-bars">${bars}</div>`;
}

function prodRender(data) {
  const { resumen, productos, sin_movimiento, categorias, turnos, grupos, estaciones, areas, productos_catalogo, comparativo_productos, desde, hasta } = data;
  const turnoSel = document.getElementById('prod-turno-filter');
  if (turnoSel && turnos && turnos.length) {
    const current = turnoSel.value;
    turnoSel.innerHTML = '<option value="">Todos los turnos</option>';
    turnos.forEach(t => { const o = document.createElement('option'); o.value = t.id; o.textContent = t.label; turnoSel.appendChild(o); });
    if ([...turnoSel.options].some(o => o.value === current)) turnoSel.value = current;
  }
  const grupoSel = document.getElementById('prod-grupo-filter');
  if (grupoSel && grupoSel.options.length <= 1 && grupos && grupos.length) {
    grupos.forEach(g => { const o = document.createElement('option'); o.value = g; o.textContent = g; grupoSel.appendChild(o); });
  }
  const estacionSel = document.getElementById('prod-estacion-filter');
  if (estacionSel && estaciones && estaciones.length) {
    const current = estacionSel.value;
    estacionSel.innerHTML = '<option value="">Todas las estaciones</option>';
    estaciones.forEach(e => { const o = document.createElement('option'); o.value = e.id; o.textContent = e.label; estacionSel.appendChild(o); });
    if ([...estacionSel.options].some(o => o.value === current)) estacionSel.value = current;
  }
  const areaSel = document.getElementById('prod-area-filter');
  if (areaSel && areas && areas.length) {
    const current = areaSel.value;
    areaSel.innerHTML = '<option value="">Todas las areas</option>';
    areas.forEach(a => { const o = document.createElement('option'); o.value = a.id; o.textContent = a.label; areaSel.appendChild(o); });
    if ([...areaSel.options].some(o => o.value === current)) areaSel.value = current;
  }
  const compareSel = document.getElementById('prod-compare-filter');
  if (compareSel && Array.isArray(productos_catalogo)) {
    const selected = new Set([...compareSel.selectedOptions].map(o => o.value));
    compareSel.innerHTML = productos_catalogo.map(p => {
      const id = String(p.idproducto || '');
      const label = `${p.producto || id}${p.grupo ? ` - ${p.grupo}` : ''}`;
      return `<option value="${prodEscape(id)}" ${selected.has(id) ? 'selected' : ''}>${prodEscape(label)}</option>`;
    }).join('');
    prodRenderComparePicker();
  }
  const rangoEl = document.getElementById('prod-rango');
  if (rangoEl) rangoEl.textContent = desde === hasta ? desde : `${desde} → ${hasta}`;

  const r = resumen;
  document.getElementById('prod-kpis').innerHTML = `
    <div class="prod-kpi-card is-money">
      <div class="prod-kpi-label">Venta total</div>
      <div class="prod-kpi-value">${prodMoney(r.venta_total)}</div>
    </div>
    <div class="prod-kpi-card">
      <div class="prod-kpi-label">Productos</div>
      <div class="prod-kpi-value">${r.productos_vendidos||0}</div>
    </div>
    <div class="prod-kpi-card">
      <div class="prod-kpi-label">Unidades</div>
      <div class="prod-kpi-value">${(r.unidades_totales||0).toLocaleString()}</div>
    </div>
    <div class="prod-kpi-card">
      <div class="prod-kpi-label">Tickets</div>
      <div class="prod-kpi-value">${r.tickets||0}</div>
    </div>
    <div class="prod-kpi-card">
      <div class="prod-kpi-label">Ticket prom.</div>
      <div class="prod-kpi-value">${prodMoney(r.ticket_promedio)}</div>
    </div>`;

  prodRenderComparePanel(comparativo_productos || [], resumen);

  // Barras horizontales: lectura directa de importe y participación.
  const ctxTop = document.getElementById('prod-chart-top');
  if (_prodChartTop) { try { _prodChartTop.destroy(); } catch(_){} _prodChartTop = null; }
  if (ctxTop && productos && productos.length) {
    const top10 = productos.slice(0, 10);
    _prodChartTop = new Chart(ctxTop, {
      type: 'bar',
      data: {
        labels: top10.map(p => {
          const nombre = String(p.producto || p.descripcion || 'Sin nombre');
          return nombre.length > 24 ? nombre.substring(0,22) + '…' : nombre;
        }),
        datasets: [{
          data: top10.map(p => Number(p.venta || 0)),
          backgroundColor: top10.map((_,index) => index === 0 ? '#f97316' : 'rgba(37,99,235,.78)'),
          hoverBackgroundColor: top10.map((_,index) => index === 0 ? '#ea580c' : '#2563eb'),
          borderWidth: 0,
          borderRadius: 6,
          borderSkipped: false,
          barPercentage: .72,
          categoryPercentage: .82,
        }]
      },
      options: prodHorizontalBarOptions(top10),
      plugins: [prodBarLabelsPlugin(top10)]
    });
  }

  // Barras horizontales: categorías comparables sin leyenda separada.
  const ctxCat = document.getElementById('prod-chart-cat');
  if (_prodChartCat) { try { _prodChartCat.destroy(); } catch(_){} _prodChartCat = null; }
  if (ctxCat && categorias && categorias.length) {
    const totalCategorias = categorias.reduce((sum, row) => sum + Number(row.venta || 0), 0);
    const catRows = categorias.map(row => ({
      ...row,
      porcentaje: totalCategorias > 0 ? (Number(row.venta || 0) / totalCategorias) * 100 : 0,
    }));
    _prodChartCat = new Chart(ctxCat, {
      type: 'bar',
      data: {
        labels: catRows.map(c => c.categoria),
        datasets: [{
          data: catRows.map(c => Number(c.venta || 0)),
          backgroundColor: catRows.map((_, index) => PROD_CHART_COLORS[index % PROD_CHART_COLORS.length] + 'd9'),
          borderWidth: 0,
          borderRadius: 6,
          borderSkipped: false,
          barPercentage: .72,
          categoryPercentage: .82,
        }]
      },
      options: prodHorizontalBarOptions(catRows),
      plugins: [prodBarLabelsPlugin(catRows)]
    });
  }

  const vendEl = document.getElementById('prod-vendidos-body');
  if (productos && productos.length) {
    const totalProds = productos.length;
    let h = `<table style="width:100%;border-collapse:collapse;font-size:11px;">
      <thead><tr style="background:var(--bg-2);border-bottom:2px solid var(--border);">
        <th style="padding:8px 6px;text-align:left;font-size:10px;color:var(--text-3);width:36px;">#</th>
        <th style="padding:8px 6px;text-align:left;font-size:10px;color:var(--text-3);">PRODUCTO</th>
        <th style="padding:8px 6px;text-align:left;font-size:10px;color:var(--text-3);">CATEGORÍA</th>
        <th style="padding:8px 6px;text-align:right;font-size:10px;color:var(--text-3);">CANT.</th>
        <th style="padding:8px 6px;text-align:right;font-size:10px;color:var(--text-3);">VENTA</th>
        <th style="padding:8px 6px;text-align:right;font-size:10px;color:var(--text-3);">%</th>
        <th style="padding:8px 6px;text-align:right;font-size:10px;color:var(--text-3);">P. PROM.</th>
        <th style="padding:8px 6px;text-align:right;font-size:10px;color:var(--text-3);">TICKETS</th>
        <th style="padding:8px 6px;text-align:center;font-size:10px;color:var(--text-3);">ESTADO</th>
      </tr></thead><tbody>`;
    productos.forEach((p, i) => {
      const bg = i % 2 ? 'var(--bg-2)' : 'transparent';
      let dot, estado;
      if (i < totalProds * 0.3)      { dot = '#22c55e'; estado = 'Alto'; }
      else if (i < totalProds * 0.7) { dot = '#f59e0b'; estado = 'Medio'; }
      else                           { dot = '#ef4444'; estado = 'Bajo'; }
      h += `<tr style="background:${bg};border-bottom:1px solid var(--border);">
        <td style="padding:6px;color:var(--text-3);font-weight:600;">${p.ranking}</td>
        <td style="padding:6px;font-weight:600;">${p.producto}</td>
        <td style="padding:6px;color:var(--text-3);font-size:10px;">${p.grupo}</td>
        <td style="padding:6px;text-align:right;font-weight:600;">${p.cantidad.toLocaleString()}</td>
        <td style="padding:6px;text-align:right;font-weight:600;">$${p.venta.toLocaleString('es-MX',{minimumFractionDigits:2})}</td>
        <td style="padding:6px;text-align:right;color:#111827;font-weight:700;">${p.porcentaje}%</td>
        <td style="padding:6px;text-align:right;">$${p.precio_promedio.toLocaleString('es-MX',{minimumFractionDigits:2})}</td>
        <td style="padding:6px;text-align:right;">${p.tickets}</td>
        <td style="padding:6px;text-align:center;"><span style="display:inline-block;width:8px;height:8px;border-radius:50%;background:${dot};margin-right:4px;"></span><span style="font-size:10px;color:var(--text-3);">${estado}</span></td>
      </tr>`;
    });
    const mobileCards = productos.map((p, i) => {
      const estado = prodEstado(i, totalProds);
      return `<article class="prod-mobile-card">
        <div class="prod-mobile-card-head">
          <span class="prod-rank">#${p.ranking}</span>
          <span class="prod-status"><span style="background:${estado.color}"></span>${estado.label}</span>
        </div>
        <div class="prod-mobile-name">${prodEscape(p.producto)}</div>
        <div class="prod-mobile-group">${prodEscape(p.grupo)}</div>
        <div class="prod-mobile-metrics">
          <div><small>Cant.</small><strong>${Number(p.cantidad || 0).toLocaleString('es-MX')}</strong></div>
          <div><small>Venta</small><strong>$${Number(p.venta || 0).toLocaleString('es-MX',{minimumFractionDigits:2})}</strong></div>
          <div><small>Ticket</small><strong>${Number(p.tickets || 0).toLocaleString('es-MX')}</strong></div>
        </div>
      </article>`;
    }).join('');
    vendEl.innerHTML = `<div class="prod-table-wrap">${h}</tbody></table></div><div class="prod-mobile-list">${mobileCards}</div>`;
  } else {
    vendEl.innerHTML = '<div style="padding:20px;text-align:center;color:var(--text-3);">Sin datos de ventas para este período</div>';
  }

  const sinEl = document.getElementById('prod-sinmov-body');
  if (sin_movimiento && sin_movimiento.length) {
    let h = `<table style="width:100%;border-collapse:collapse;font-size:11px;">
      <thead><tr style="background:var(--bg-2);border-bottom:2px solid var(--border);">
        <th style="padding:8px 6px;text-align:left;font-size:10px;color:var(--text-3);width:36px;">#</th>
        <th style="padding:8px 6px;text-align:left;font-size:10px;color:var(--text-3);">PRODUCTO</th>
        <th style="padding:8px 6px;text-align:left;font-size:10px;color:var(--text-3);">CATEGORÍA</th>
        <th style="padding:8px 6px;text-align:left;font-size:10px;color:var(--text-3);">CLAVE</th>
      </tr></thead><tbody>`;
    sin_movimiento.forEach((p, i) => {
      h += `<tr style="background:${i%2?'var(--bg-2)':'transparent'};border-bottom:1px solid var(--border);">
        <td style="padding:6px;color:var(--text-3);font-weight:600;">${i+1}</td>
        <td style="padding:6px;font-weight:600;">${p.producto}</td>
        <td style="padding:6px;color:var(--text-3);font-size:10px;">${p.grupo}</td>
        <td style="padding:6px;color:var(--text-3);font-size:10px;">${p.idproducto}</td>
      </tr>`;
    });
    const mobileCards = sin_movimiento.map((p, i) => `<article class="prod-mobile-card">
      <div class="prod-mobile-card-head"><span class="prod-rank">#${i + 1}</span><span class="prod-status is-muted">Sin movimiento</span></div>
      <div class="prod-mobile-name">${prodEscape(p.producto)}</div>
      <div class="prod-mobile-group">${prodEscape(p.grupo)}</div>
      <div class="prod-mobile-metrics">
        <div><small>Clave</small><strong>${prodEscape(p.idproducto)}</strong></div>
      </div>
    </article>`).join('');
    sinEl.innerHTML = `<div class="prod-table-wrap">${h}</tbody></table></div><div class="prod-mobile-list">${mobileCards}</div>`;
  } else {
    sinEl.innerHTML = '<div style="padding:20px;text-align:center;color:var(--text-3);">Todos los productos activos tuvieron movimiento</div>';
  }
}

// Auto-load when productos tab opens
const _origRenderProductos = typeof renderProductos === 'function' ? renderProductos : null;
function renderProductosWithAnalytics() {
  if (_origRenderProductos) _origRenderProductos();
  const kpis = document.getElementById('prod-kpis');
  prodSyncPeriodControls();
  if (kpis && !kpis.innerHTML.trim()) prodReload();
}

// Export to window for HTML onclick handlers
window.prodFilter = prodFilter;
window.prodApplyRange = prodApplyRange;
window.prodSwitchTab = prodSwitchTab;
window.prodReload = prodReload;
window.prodToggleComparePicker = prodToggleComparePicker;
window.prodFilterCompareOptions = prodFilterCompareOptions;
window.prodSetCompareSelection = prodSetCompareSelection;
window.prodApplyCompare = prodApplyCompare;
window.prodClearCompare = prodClearCompare;

document.addEventListener('click', event => {
  const picker = document.getElementById('prod-compare-picker');
  if (picker && !picker.contains(event.target)) prodCloseComparePicker();
});
