/* =============================================================================
   screen_comercial_rpc_consumiveis.jsx — RPC Consumíveis (Decal & Biond)
   =============================================================================
   Tabs:
     1. Mapa RPC         — grelha A/B/C/D (carteira, família, SPIN, resultado)
     2. Show me the Money— facturação real mês a mês por comercial
     3. Forecast         — previsão de vendas (Fase 2)
     4. Preditividade    — fecho do mês (Fase 2)
     5. YTD Famílias     — mapa vendas YTD por família/artigo
     6. Media Sales AI   — sugestões AI (Fase 2)
     7. Sugestões AI     — acções de melhoria (Fase 2)
     8. Previsão AI      — previsão ML (Fase 3)

   Fase 1 (actual): Bloco D com dados reais Primavera.
   Blocos A, B, C, Encomendas disponíveis a partir da Fase 2 (após carteiras + SPIN).
   ============================================================================= */

// ─── Constants ─────────────────────────────────────────────────────────────────
const CONS_MESES    = ['Jan','Fev','Mar','Abr','Mai','Jun','Jul','Ago','Set','Out','Nov','Dez'];
const CONS_ANO_DEF  = 2026;
const CONS_MES_FECH = new Date().getMonth(); // 0-based, avança automaticamente
const CONS_SUPS     = ['¹','²','³','⁴','⁵','⁶','⁷'];

const CONS_MARCAS   = ['Decal PT','Decal EX','Decal AD','Decal ES','Decal PA','Biond'];
const CONS_MARCA_EMP = {
  'Decal PT': 'DECAL_PT',
  'Decal EX': 'DECAL_EX',
  'Decal AD': 'DECAL_AD',
  'Decal ES': 'DECAL_ES',
  'Decal PA': 'DECAL_PA',
  'Biond':    'BIOND',
};
// Mapeamento para o tenant FM (GestorConnect)
const CONS_MARCA_FM = {
  'Decal PT': 'PTDIG',
  'Decal EX': 'PTDIG',
  'Decal AD': 'DIGES',
  'Decal ES': 'DIGES',
  'Decal PA': 'DIGES',
  'Biond':    'PTDIG',
};

const CONS_TABS = [
  { id:'rpc',        label:'Mapa RPC' },
  { id:'money',      label:'Show me the Money' },
  { id:'forecast',   label:'Forecast' },
  { id:'predit',     label:'Preditividade' },
  { id:'ytd',        label:'YTD Famílias' },
  { id:'mediasales', label:'Media Sales AI' },
  { id:'sugestoes',  label:'Sugestões AI' },
  { id:'previsao',   label:'Previsão AI' },
];

const CONS_NOTES = [
  { n:1, text:'Cliente ativo = pelo menos 1 fatura nos últimos 90 dias. Carteira atribuída: lista fechada de clientes por comercial.' },
  { n:2, text:'Potencial identificado no diagnóstico SPIN: consumo anual estimado por família. Disponível na Fase 2 após formação da equipa no registo SPIN.' },
  { n:3, text:'Cobertura de Potencial = potencial ativo (≤ 12 meses) ÷ objetivo anual BP. Substitui a Cobertura de Pipeline. Em rampa no 1.º ano.' },
  { n:4, text:'Previsão = baseline recorrente da carteira (média 6 meses × coef. sazonal) + potencial SPIN ponderado. Disponível na Fase 2.' },
  { n:5, text:'RPC Rating: Resultado 40% · Carteira 25% · SPIN & Novos 20% · Desenvolvimento 15%. Fase 1: apenas Bloco D contribui (rating parcial).' },
  { n:6, text:'Obj. proposto = Vendas recorrentes × (1+3%) + 30% do potencial não capturado. Calculado na Fase 3.' },
  { n:7, text:'Encomendas registadas pelo comercial: Confirmada (aberta no ERP) ou Potencial (estimada, ainda não aberta). Taxa de Concretização = % do potencial faturado.' },
];

// ─── Formatters ────────────────────────────────────────────────────────────────
const cE = (n, compact=true) => {
  if (n == null || isNaN(n)) return '—';
  const abs = Math.abs(n), s = n < 0 ? '−' : '';
  if (compact && abs >= 1000000) return s+(abs/1000000).toFixed(1).replace('.',',')+'M';
  if (compact && abs >= 1000)    return s+Math.round(abs/1000)+'k';
  return s+new Intl.NumberFormat('pt-PT').format(Math.round(abs));
};
const cSign = n => n==null||isNaN(n) ? '—' : (n>=0?'+':'')+cE(n);
const cPct  = (v,t) => (t>0) ? (v/t*100).toFixed(1).replace('.',',')+'%' : '—';

// Map class string (p/n/a/m/b/f/fp) → inline style object
const cCls = (cls='') => {
  const s = {};
  if (cls.includes('n')) { s.color='#dc2626'; }
  else if (cls.includes('fp')) { s.color='#16a34a'; s.background='color-mix(in oklch,#3b82f6 8%,transparent)'; }
  else if (cls.includes('f')) { s.color='#3563c9'; s.background='color-mix(in oklch,#3b82f6 6%,transparent)'; }
  else if (cls.includes('p')) { s.color='#16a34a'; }
  else if (cls.includes('a')) { s.color='#d97706'; }
  else if (cls.includes('m')) { s.color='var(--dgd-fg-3)'; }
  if (cls.includes('b')) { s.fontWeight=700; }
  return s;
};

const parseCell = (v) => {
  if (Array.isArray(v)) return { val: v[0]||'—', cls: v[1]||'' };
  const s = (v == null || v === '') ? '—' : String(v);
  return { val: s, cls: s==='—' ? 'm' : '' };
};

// ─── Shared TD/TH base styles ─────────────────────────────────────────────────
const tdB = {
  padding:'4px 7px', textAlign:'right', fontSize:10.5,
  fontFamily:'var(--dgd-font-mono,monospace)', fontVariantNumeric:'tabular-nums',
  borderBottom:'1px solid var(--dgd-border-1)', whiteSpace:'nowrap',
};
const thB = { ...tdB, fontSize:9.5, fontWeight:600, letterSpacing:'0.06em', color:'var(--dgd-fg-3)', background:'var(--dgd-bg-surface)', borderBottom:'1px solid var(--dgd-border-1)' };

// ─── Hooks ─────────────────────────────────────────────────────────────────────

const useConsComerciais = (empresa) => {
  const [state, setState] = React.useState({ comerciais: null, loading: true });
  React.useEffect(() => {
    if (!empresa) { setState({ comerciais: [], loading: false }); return; }
    setState(s => ({ ...s, loading: true }));
    fetch(`/api/rpc/comerciais?empresa=${empresa}`)
      .then(r => r.ok ? r.json() : Promise.reject())
      .then(rows => setState({
        comerciais: rows.map(r => ({
          id: 'bd-'+r.colaborador_id+'-'+r.vendedor,
          nome: r.nome,
          vendedor: r.vendedor,
          empresa: r.empresa,
          foto_url: r.foto_url || null,
          seccao: r.seccao || 'Outros',
          manager_nome: r.manager_nome || null,
        })).sort((a,b) => a.nome.localeCompare(b.nome, 'pt')),
        loading: false,
      }))
      .catch(() => setState({ comerciais: null, loading: false }));
  }, [empresa]);
  return state;
};

const useConsYTD = (ano) => {
  const [state, setState] = React.useState({ byV: null, margemByV: null, ytdRows: null, loading: true });
  React.useEffect(() => {
    setState(s => ({ ...s, loading: true }));
    fetch(`/api/rpc/ytd?ano=${ano}`)
      .then(r => r.ok ? r.json() : Promise.reject())
      .then(data => {
        const byV = {}, margemByV = {};
        (data.mensal || []).forEach(r => {
          if (!byV[r.vendedor]) byV[r.vendedor] = new Array(12).fill(null);
          byV[r.vendedor][r.mes - 1] = parseFloat(r.valor) || 0;
        });
        (data.margemMensal || []).forEach(r => {
          if (!margemByV[r.vendedor]) margemByV[r.vendedor] = new Array(12).fill(null);
          margemByV[r.vendedor][r.mes - 1] = parseFloat(r.margem) || 0;
        });
        setState({ byV, margemByV, ytdRows: data.rows || [], loading: false });
      })
      .catch(() => setState({ byV: null, margemByV: null, ytdRows: null, loading: false }));
  }, [ano]);
  return state;
};

const useConsBP = (ano, refreshKey) => {
  const [bpMap, setBpMap] = React.useState({});
  React.useEffect(() => {
    fetch(`/api/rpc/bp-targets?ano=${ano}`)
      .then(r => r.ok ? r.json() : {})
      .then(d => setBpMap(d || {}))
      .catch(() => {});
  }, [ano, refreshKey]);
  return bpMap; // { vendedor: [12] }
};

const useConsFMPipeline = (fmEmpresa, ano) => {
  const [byVendedor, setByVendedor] = React.useState({});
  const [loading, setLoading] = React.useState(false);
  React.useEffect(() => {
    if (!fmEmpresa) return;
    setLoading(true);
    fetch(`/api/rpc/fm-pipeline?empresa=${fmEmpresa}&ano=${ano}`)
      .then(r => r.ok ? r.json() : { byVendedor: {} })
      .then(d => { setByVendedor(d.byVendedor || {}); setLoading(false); })
      .catch(() => { setByVendedor({}); setLoading(false); });
  }, [fmEmpresa, ano]);
  return { byVendedor, loading };
};

const useConsCarteira = (ano) => {
  const [byVendedor, setByVendedor] = React.useState({});
  React.useEffect(() => {
    fetch(`/api/rpc/carteira?ano=${ano}`)
      .then(r => r.ok ? r.json() : { byVendedor: {} })
      .then(d => { console.log('[carteira] keys:', Object.keys(d.byVendedor||{}).length, Object.keys(d.byVendedor||{}).slice(0,5)); setByVendedor(d.byVendedor || {}); })
      .catch(e => console.error('[carteira] error:', e));
  }, [ano]);
  return { byVendedor };
};

// ─── Info Tooltip (porta para document.body — escapa sticky/stacking) ──────────
const ConsInfoTip = ({ text }) => {
  const [pos, setPos] = React.useState(null);
  const ref = React.useRef(null);
  const handleEnter = () => {
    if (ref.current) {
      const r = ref.current.getBoundingClientRect();
      const flipDown = r.top < 160;
      setPos({ top: flipDown ? r.bottom + 8 : r.top - 8, left: r.left + r.width / 2, flipDown });
    }
  };
  const tooltip = pos ? ReactDOM.createPortal(
    <div style={{ position:'fixed', left:pos.left, top:pos.top,
      transform: pos.flipDown ? 'translate(-50%,0)' : 'translate(-50%,-100%)',
      zIndex:99999, background:'var(--dgd-bg-card,white)', border:'1px solid var(--dgd-border-1)',
      borderRadius:6, padding:'7px 10px', fontSize:11, color:'var(--dgd-fg-1)',
      whiteSpace:'normal', boxShadow:'0 4px 16px rgba(0,0,0,0.18)',
      lineHeight:1.5, width:240, pointerEvents:'none' }}>
      {text}
    </div>,
    document.body
  ) : null;
  return (
    <span style={{ position:'relative', display:'inline-block', marginLeft:3, marginRight:1, verticalAlign:'middle' }}>
      <span ref={ref}
        onMouseEnter={handleEnter}
        onMouseLeave={() => setPos(null)}
        style={{ cursor:'help', color:'var(--dgd-fg-3)', fontSize:9, fontWeight:700, border:'1px solid var(--dgd-border-1)', borderRadius:'50%', width:13, height:13, display:'inline-flex', alignItems:'center', justifyContent:'center', lineHeight:1, userSelect:'none' }}
      >?</span>
      {tooltip}
    </span>
  );
};

// ─── Cell Info Modal ───────────────────────────────────────────────────────────
const ConsInfoModal = ({ info, onClose }) => {
  const [drill, setDrill] = React.useState(null);
  const [drillLoading, setDrillLoading] = React.useState(false);
  const [carteiraRows, setCarteiraRows] = React.useState(null);
  const [carteiraLoading, setCarteiraLoading] = React.useState(false);

  React.useEffect(() => {
    const handler = e => { if (e.key === 'Escape') onClose(); };
    window.addEventListener('keydown', handler);
    return () => window.removeEventListener('keydown', handler);
  }, [onClose]);

  // Fetch drill-down when info has drillParams; use drillData directly if provided
  React.useEffect(() => {
    if (!info) { setDrill(null); return; }
    if (info.drillData) { setDrill(info.drillData); setDrillLoading(false); return; }
    if (!info.drillParams) { setDrill(null); return; }
    setDrillLoading(true);
    const { vendedor, ano, mes } = info.drillParams;
    const mesQ = mes != null ? `&mes=${mes+1}` : '';
    fetch(`/api/rpc/ytd-detail?vendedor=${vendedor}&ano=${ano}${mesQ}`)
      .then(r => r.ok ? r.json() : [])
      .then(rows => { setDrill(rows); setDrillLoading(false); })
      .catch(() => { setDrill([]); setDrillLoading(false); });
  }, [info]);

  // Fetch carteira client drill-down
  React.useEffect(() => {
    if (!info?.carteiraParams) { setCarteiraRows(null); return; }
    const { tipo, vendedor, ano, mes } = info.carteiraParams;
    setCarteiraLoading(true);
    const mesQ = mes != null ? `&mes=${mes+1}` : '';
    fetch(`/api/rpc/carteira-detail?tipo=${tipo}&vendedor=${encodeURIComponent(vendedor)}&ano=${ano}${mesQ}`)
      .then(r => r.ok ? r.json() : { rows: [] })
      .then(d => { setCarteiraRows(d.rows || []); setCarteiraLoading(false); })
      .catch(() => { setCarteiraRows([]); setCarteiraLoading(false); });
  }, [info]);

  if (!info) return null;
  const secColors = { 'Gestão de Carteira':'#3b82f6', 'Desenvolvimento por Família':'#8b5cf6', 'Prospeção SPIN & Novos Clientes':'#8b5cf6', 'Encomendas do Mês':'#f59e0b', 'Resultado':'#22c55e' };
  const accentColor = secColors[info.section] || '#64748b';
  const totalValor = drill ? drill.reduce((s,r) => s + parseFloat(r.valor||0), 0) : 0;

  return ReactDOM.createPortal(
    <>
      <div onClick={onClose} style={{ position:'fixed', inset:0, zIndex:2000, background:'rgba(0,0,0,0.45)', backdropFilter:'blur(2px)' }}></div>
      <div style={{ position:'fixed', top:'50%', left:'50%', transform:'translate(-50%,-50%)', zIndex:2001, width: info.drillParams ? 'min(720px,94vw)' : info.carteiraParams ? 'min(600px,94vw)' : 'min(440px,92vw)', background:'var(--dgd-bg-card,white)', borderRadius:12, boxShadow:'0 24px 64px rgba(0,0,0,0.22)', border:'1px solid var(--dgd-border-1)', overflow:'hidden', display:'flex', flexDirection:'column', maxHeight:'85vh' }}>
        {/* Header */}
        <div style={{ padding:'14px 20px 12px', borderBottom:'1px solid var(--dgd-border-1)', display:'flex', alignItems:'center', gap:10, flexShrink:0 }}>
          <div style={{ fontFamily:'var(--dgd-font-mono,monospace)', fontSize:9, fontWeight:700, letterSpacing:'0.1em', textTransform:'uppercase', color:'white', background:`color-mix(in oklch,${accentColor} 80%,#1e293b)`, padding:'3px 8px', borderRadius:4, whiteSpace:'nowrap' }}>{info.section}</div>
          <div style={{ fontSize:13, fontWeight:600, color:'var(--dgd-fg-1)', flex:1 }}>{info.label}</div>
          <button onClick={onClose} style={{ background:'none', border:'none', cursor:'pointer', color:'var(--dgd-fg-3)', fontSize:20, lineHeight:1, padding:'0 4px', marginLeft:4 }}>×</button>
        </div>
        <div style={{ padding:'20px 24px', overflowY:'auto', flex:1 }}>
          {/* Valor seleccionado */}
          {info.val && info.val !== '—' && (
            <div style={{ marginBottom:16, padding:'10px 16px', borderRadius:8, background:'var(--dgd-bg-surface)', border:'1px solid var(--dgd-border-1)', display:'flex', alignItems:'baseline', gap:10 }}>
              <div style={{ fontSize:10, color:'var(--dgd-fg-3)', fontFamily:'var(--dgd-font-mono,monospace)', letterSpacing:'0.08em', flexShrink:0 }}>{info.period}</div>
              <div style={{ fontSize:20, fontWeight:700, color:'var(--dgd-fg-1)', fontFamily:'var(--dgd-font-mono,monospace)', letterSpacing:'-0.01em' }}>{info.val}</div>
            </div>
          )}
          {/* Descrição */}
          {info.tip && (
            <div style={{ fontSize:12.5, color:'var(--dgd-fg-2)', lineHeight:1.65, marginBottom: info.formula ? 16 : 0 }}>
              {info.tip}
            </div>
          )}
          {/* Fórmula */}
          {info.formula && (
            <div style={{ marginTop:0, padding:'10px 14px', borderRadius:7, background:`color-mix(in oklch,${accentColor} 6%,transparent)`, border:`1px solid color-mix(in oklch,${accentColor} 22%,transparent)`, fontSize:11, fontFamily:'var(--dgd-font-mono,monospace)', color:'var(--dgd-fg-1)', whiteSpace:'pre-line', lineHeight:1.7 }}>
              {info.formula}
            </div>
          )}
          {/* Fase placeholder */}
          {info.fase && (
            <div style={{ marginTop:14, padding:'8px 12px', borderRadius:6, background:'color-mix(in oklch,#f59e0b 7%,transparent)', border:'1px solid color-mix(in oklch,#f59e0b 25%,transparent)', fontSize:11, color:'#92400e', fontWeight:500 }}>
              ⏳ Disponível na {info.fase} — após integração de carteiras e SPIN.
            </div>
          )}
          {/* Carteira client drill-down */}
          {info.carteiraParams && (() => {
            const { tipo, vendedorNome, ano } = info.carteiraParams;
            const anoAnt = ano - 1;
            const thS = { padding:'5px 10px', color:'var(--dgd-fg-3)', fontWeight:600, letterSpacing:'0.06em', borderBottom:'1px solid var(--dgd-border-1)', whiteSpace:'nowrap', fontSize:11, fontFamily:'var(--dgd-font-mono,monospace)' };
            const tdS = { padding:'4px 10px', borderBottom:'1px solid var(--dgd-border-1)', fontSize:11, fontFamily:'var(--dgd-font-mono,monospace)', whiteSpace:'nowrap' };
            const total = carteiraRows ? carteiraRows.reduce((s,r) => s+parseFloat(r.valor||r.valor_ano||r.valor_mes||0), 0) : 0;
            return (
              <div style={{ marginTop:20 }}>
                <div style={{ fontSize:10, fontWeight:700, letterSpacing:'0.1em', textTransform:'uppercase', color:'var(--dgd-fg-3)', fontFamily:'var(--dgd-font-mono,monospace)', marginBottom:8 }}>
                  {vendedorNome} · {info.period}
                </div>
                {carteiraLoading && <div style={{ padding:'20px 0', color:'var(--dgd-fg-3)', fontSize:12 }}>A carregar...</div>}
                {!carteiraLoading && carteiraRows && carteiraRows.length === 0 && (
                  <div style={{ padding:'16px 0', color:'var(--dgd-fg-3)', fontSize:12 }}>Sem dados para este período.</div>
                )}
                {!carteiraLoading && carteiraRows && carteiraRows.length > 0 && (
                  <div style={{ overflowX:'auto', borderRadius:7, border:'1px solid var(--dgd-border-1)' }}>
                    <table style={{ borderCollapse:'collapse', width:'100%' }}>
                      <thead>
                        <tr style={{ background:'var(--dgd-bg-surface)' }}>
                          <th style={{ ...thS, textAlign:'left' }}>Cliente</th>
                          {tipo === 'ativos' && <><th style={{ ...thS, textAlign:'right' }}>Valor</th><th style={{ ...thS, textAlign:'right' }}>Fam.</th></>}
                          {tipo === 'risco' && <><th style={{ ...thS, textAlign:'right' }}>Val. {anoAnt}</th><th style={{ ...thS, textAlign:'right' }}>Últ. mês</th></>}
                          {tipo === 'retencao' && <><th style={{ ...thS, textAlign:'right' }}>{anoAnt}</th><th style={{ ...thS, textAlign:'right' }}>{ano}</th><th style={{ ...thS, textAlign:'right' }}>Δ%</th></>}
                          {tipo === 'novos' && info.carteiraParams.mes != null && <><th style={{ ...thS, textAlign:'right' }}>Val. mês</th><th style={{ ...thS, textAlign:'right' }}>Val. {ano}</th></>}
                          {tipo === 'novos' && info.carteiraParams.mes == null && <><th style={{ ...thS, textAlign:'right' }}>1.ª compra</th><th style={{ ...thS, textAlign:'right' }}>Val. {ano}</th></>}
                          {(tipo === 'vcart' || tipo === 'vnovos') && <th style={{ ...thS, textAlign:'right' }}>Valor</th>}
                          {tipo === 'familias' && <><th style={{ ...thS, textAlign:'right' }}>Nº Fam.</th><th style={{ ...thS, textAlign:'right' }}>Valor</th></>}
                        </tr>
                      </thead>
                      <tbody>
                        {carteiraRows.map((r, i) => {
                          const bg = i%2===0 ? 'transparent' : 'var(--dgd-bg-surface)';
                          const vAnt = parseFloat(r.valor_ant||0), vAno = parseFloat(r.valor_ano||0);
                          const deltaPct = vAnt > 0 ? ((vAno-vAnt)/vAnt*100) : null;
                          return (
                            <tr key={i} style={{ background: bg }}>
                              <td style={{ ...tdS, color:'var(--dgd-fg-1)', maxWidth:200, overflow:'hidden', textOverflow:'ellipsis' }}>{r.cliente}</td>
                              {tipo === 'ativos' && <>
                                <td style={{ ...tdS, textAlign:'right', fontWeight:600 }}>{cE(parseFloat(r.valor||0))}</td>
                                <td style={{ ...tdS, textAlign:'right', color:'var(--dgd-fg-3)' }}>{r.n_fam}</td>
                              </>}
                              {tipo === 'risco' && <>
                                <td style={{ ...tdS, textAlign:'right', fontWeight:600 }}>{cE(parseFloat(r.valor_ant||0))}</td>
                                <td style={{ ...tdS, textAlign:'right', color:'var(--dgd-fg-3)' }}>{CONS_MESES[(parseInt(r.ultimo_mes)||1)-1]}</td>
                              </>}
                              {tipo === 'retencao' && <>
                                <td style={{ ...tdS, textAlign:'right', color:'var(--dgd-fg-3)' }}>{cE(vAnt)}</td>
                                <td style={{ ...tdS, textAlign:'right', fontWeight:600 }}>{cE(vAno)}</td>
                                <td style={{ ...tdS, textAlign:'right', color: deltaPct==null?'var(--dgd-fg-3)':deltaPct>=0?'#16a34a':'#dc2626' }}>
                                  {deltaPct != null ? (deltaPct>=0?'+':'')+deltaPct.toFixed(0)+'%' : '—'}
                                </td>
                              </>}
                              {tipo === 'novos' && info.carteiraParams.mes != null && <>
                                <td style={{ ...tdS, textAlign:'right', fontWeight:600 }}>{cE(parseFloat(r.valor_mes||0))}</td>
                                <td style={{ ...tdS, textAlign:'right', color:'var(--dgd-fg-3)' }}>{cE(parseFloat(r.valor_ano||0))}</td>
                              </>}
                              {tipo === 'novos' && info.carteiraParams.mes == null && <>
                                <td style={{ ...tdS, textAlign:'right', color:'var(--dgd-fg-3)' }}>{CONS_MESES[(parseInt(r.primeiro_mes)||1)-1]}</td>
                                <td style={{ ...tdS, textAlign:'right', fontWeight:600 }}>{cE(parseFloat(r.valor_ano||0))}</td>
                              </>}
                              {(tipo === 'vcart' || tipo === 'vnovos') && (
                                <td style={{ ...tdS, textAlign:'right', fontWeight:600 }}>{cE(parseFloat(r.valor||0))}</td>
                              )}
                              {tipo === 'familias' && <>
                                <td style={{ ...tdS, textAlign:'right', fontWeight:600, color: parseInt(r.n_fam)>=3?'#16a34a':parseInt(r.n_fam)>=2?'#d97706':'var(--dgd-fg-2)' }}>{r.n_fam}</td>
                                <td style={{ ...tdS, textAlign:'right' }}>{cE(parseFloat(r.valor||0))}</td>
                              </>}
                            </tr>
                          );
                        })}
                      </tbody>
                      <tfoot>
                        <tr style={{ background:'var(--dgd-bg-surface)' }}>
                          <td style={{ ...tdS, fontWeight:700, borderTop:'2px solid var(--dgd-border-1)' }}>Total ({carteiraRows.length} clientes)</td>
                          {tipo === 'ativos' && <><td style={{ ...tdS, textAlign:'right', fontWeight:700, borderTop:'2px solid var(--dgd-border-1)' }}>{cE(carteiraRows.reduce((s,r)=>s+parseFloat(r.valor||0),0))}</td><td style={{ ...tdS, borderTop:'2px solid var(--dgd-border-1)' }}></td></>}
                          {tipo === 'risco' && <><td style={{ ...tdS, textAlign:'right', fontWeight:700, borderTop:'2px solid var(--dgd-border-1)' }}>{cE(carteiraRows.reduce((s,r)=>s+parseFloat(r.valor_ant||0),0))}</td><td style={{ ...tdS, borderTop:'2px solid var(--dgd-border-1)' }}></td></>}
                          {tipo === 'retencao' && <><td style={{ ...tdS, borderTop:'2px solid var(--dgd-border-1)', textAlign:'right' }}>{cE(carteiraRows.reduce((s,r)=>s+parseFloat(r.valor_ant||0),0))}</td><td style={{ ...tdS, textAlign:'right', fontWeight:700, borderTop:'2px solid var(--dgd-border-1)' }}>{cE(carteiraRows.reduce((s,r)=>s+parseFloat(r.valor_ano||0),0))}</td><td style={{ ...tdS, borderTop:'2px solid var(--dgd-border-1)' }}></td></>}
                          {tipo === 'novos' && info.carteiraParams.mes != null && <><td style={{ ...tdS, textAlign:'right', fontWeight:700, borderTop:'2px solid var(--dgd-border-1)' }}>{cE(carteiraRows.reduce((s,r)=>s+parseFloat(r.valor_mes||0),0))}</td><td style={{ ...tdS, borderTop:'2px solid var(--dgd-border-1)' }}></td></>}
                          {tipo === 'novos' && info.carteiraParams.mes == null && <><td style={{ ...tdS, borderTop:'2px solid var(--dgd-border-1)' }}></td><td style={{ ...tdS, textAlign:'right', fontWeight:700, borderTop:'2px solid var(--dgd-border-1)' }}>{cE(carteiraRows.reduce((s,r)=>s+parseFloat(r.valor_ano||0),0))}</td></>}
                          {(tipo === 'vcart' || tipo === 'vnovos') && <td style={{ ...tdS, textAlign:'right', fontWeight:700, borderTop:'2px solid var(--dgd-border-1)' }}>{cE(carteiraRows.reduce((s,r)=>s+parseFloat(r.valor||0),0))}</td>}
                          {tipo === 'familias' && <><td style={{ ...tdS, borderTop:'2px solid var(--dgd-border-1)' }}></td><td style={{ ...tdS, textAlign:'right', fontWeight:700, borderTop:'2px solid var(--dgd-border-1)' }}>{cE(carteiraRows.reduce((s,r)=>s+parseFloat(r.valor||0),0))}</td></>}
                        </tr>
                      </tfoot>
                    </table>
                  </div>
                )}
              </div>
            );
          })()}
          {/* Drill-down table */}
          {info.drillParams && (
            <div style={{ marginTop:20 }}>
              <div style={{ fontSize:10, fontWeight:700, letterSpacing:'0.1em', textTransform:'uppercase', color:'var(--dgd-fg-3)', fontFamily:'var(--dgd-font-mono,monospace)', marginBottom:8 }}>
                Detalhe — {info.period} · {info.drillParams.vendedorNome}
              </div>
              {drillLoading && <div style={{ padding:'20px 0', color:'var(--dgd-fg-3)', fontSize:12 }}>A carregar...</div>}
              {!drillLoading && drill && drill.length === 0 && (
                <div style={{ padding:'16px 0', color:'var(--dgd-fg-3)', fontSize:12 }}>Sem linhas de detalhe para este período.</div>
              )}
              {!drillLoading && drill && drill.length > 0 && (
                <div style={{ overflowX:'auto', borderRadius:7, border:'1px solid var(--dgd-border-1)' }}>
                  <table style={{ borderCollapse:'collapse', width:'100%', fontSize:11, fontFamily:'var(--dgd-font-mono,monospace)' }}>
                    <thead>
                      <tr style={{ background:'var(--dgd-bg-surface)' }}>
                        <th style={{ padding:'5px 10px', textAlign:'left', color:'var(--dgd-fg-3)', fontWeight:600, letterSpacing:'0.06em', borderBottom:'1px solid var(--dgd-border-1)', whiteSpace:'nowrap' }}>Família</th>
                        <th style={{ padding:'5px 10px', textAlign:'left', color:'var(--dgd-fg-3)', fontWeight:600, letterSpacing:'0.06em', borderBottom:'1px solid var(--dgd-border-1)', whiteSpace:'nowrap' }}>Artigo</th>
                        <th style={{ padding:'5px 10px', textAlign:'right', color:'var(--dgd-fg-3)', fontWeight:600, letterSpacing:'0.06em', borderBottom:'1px solid var(--dgd-border-1)', whiteSpace:'nowrap' }}>Qtd</th>
                        <th style={{ padding:'5px 10px', textAlign:'right', color:'var(--dgd-fg-3)', fontWeight:600, letterSpacing:'0.06em', borderBottom:'1px solid var(--dgd-border-1)', whiteSpace:'nowrap' }}>Valor</th>
                        <th style={{ padding:'5px 10px', textAlign:'right', color:'var(--dgd-fg-3)', fontWeight:600, letterSpacing:'0.06em', borderBottom:'1px solid var(--dgd-border-1)', whiteSpace:'nowrap' }}>Margem%</th>
                      </tr>
                    </thead>
                    <tbody>
                      {drill.map((r, i) => {
                        const v = parseFloat(r.valor||0);
                        const m = parseFloat(r.margem||0);
                        const mPct = v > 0 ? (m/v*100) : null;
                        return (
                          <tr key={i} style={{ background: i%2===0 ? 'transparent' : 'var(--dgd-bg-surface)' }}>
                            <td style={{ padding:'4px 10px', color:'var(--dgd-fg-2)', borderBottom:'1px solid var(--dgd-border-1)', whiteSpace:'nowrap' }}>{r.familia || '—'}</td>
                            <td style={{ padding:'4px 10px', color:'var(--dgd-fg-1)', borderBottom:'1px solid var(--dgd-border-1)', whiteSpace:'nowrap', maxWidth:200, overflow:'hidden', textOverflow:'ellipsis' }}>{r.artigo || '—'}</td>
                            <td style={{ padding:'4px 10px', textAlign:'right', color:'var(--dgd-fg-3)', borderBottom:'1px solid var(--dgd-border-1)' }}>{parseFloat(r.qtd||0).toFixed(0)}</td>
                            <td style={{ padding:'4px 10px', textAlign:'right', fontWeight:600, color:'var(--dgd-fg-1)', borderBottom:'1px solid var(--dgd-border-1)' }}>{cE(v)}</td>
                            <td style={{ padding:'4px 10px', textAlign:'right', borderBottom:'1px solid var(--dgd-border-1)', color: mPct==null?'var(--dgd-fg-3)':mPct>=35?'#16a34a':mPct>=25?'#d97706':'#dc2626' }}>
                              {mPct != null ? mPct.toFixed(1)+'%' : '—'}
                            </td>
                          </tr>
                        );
                      })}
                    </tbody>
                    <tfoot>
                      <tr style={{ background:'var(--dgd-bg-surface)' }}>
                        <td colSpan={3} style={{ padding:'5px 10px', color:'var(--dgd-fg-2)', fontWeight:700, borderTop:'2px solid var(--dgd-border-1)' }}>Total ({drill.length} artigos)</td>
                        <td style={{ padding:'5px 10px', textAlign:'right', fontWeight:700, color:'var(--dgd-fg-1)', borderTop:'2px solid var(--dgd-border-1)' }}>{cE(totalValor)}</td>
                        <td style={{ padding:'5px 10px', textAlign:'right', borderTop:'2px solid var(--dgd-border-1)', color:'var(--dgd-fg-3)' }}>
                          {(() => { const tm = drill.reduce((s,r)=>s+parseFloat(r.margem||0),0); return totalValor>0?(tm/totalValor*100).toFixed(1)+'%':'—'; })()}
                        </td>
                      </tr>
                    </tfoot>
                  </table>
                </div>
              )}
            </div>
          )}
        </div>
      </div>
    </>,
    document.body
  );
};

// ─── Note Tooltip ─────────────────────────────────────────────────────────────
const ConsNoteBadge = ({ n, activeNote, setActiveNote }) => (
  <sup
    onClick={() => setActiveNote(activeNote === n ? null : n)}
    title={CONS_NOTES.find(x => x.n === n)?.text || ''}
    style={{ cursor:'pointer', color:'#3563c9', fontWeight:700, fontSize:9, marginLeft:3, userSelect:'none' }}
  >{CONS_SUPS[n-1]}</sup>
);

// ─── Tab: Mapa RPC ─────────────────────────────────────────────────────────────
const TabMapaRPCCons = ({ com, ytdMensal, bpMensal, margemMensal, onBpSave, fmData, carteiraByV, comerciaisFiltrados }) => {
  const mes = CONS_MES_FECH;
  const [activeNote, setActiveNote] = React.useState(null);
  const [infoModal, setInfoModal]   = React.useState(null);
  const [editCell, setEditCell]     = React.useState(null); // { mesIdx, val } — edição BP inline
  const [saving, setSaving]         = React.useState(false);
  const noteProps = { activeNote, setActiveNote };
  const DASH12 = Array(12).fill('—');

  const saveBP = async (mesIdx, rawVal) => {
    setEditCell(null);
    if (!com) return;
    const valor = parseFloat(String(rawVal).replace(',', '.').replace(/\s/g, '')) || 0;
    setSaving(true);
    try {
      await fetch('/api/rpc/bp-targets', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ vendedor: com.vendedor, ano: CONS_ANO_DEF, mes: mesIdx + 1, valor }),
      });
      if (onBpSave) onBpSave();
    } catch {}
    setSaving(false);
  };

  const DRILL_ROWS = new Set(['Vendas Fact.','Margem','BP Desvio','RPC Rating']);
  const CARTEIRA_DRILL = { 'Clientes Ativos':'ativos', 'Clientes em Risco':'risco', 'Retenção 12m':'retencao', 'Novos Clientes (1.ª compra)':'novos', 'Vendas Carteira':'vcart', 'Vendas Novos Clientes':'vnovos', 'Famílias / Cliente':'familias' };

  const openCell = (row, sec, val, period, mesIdx) => {
    if (!row.tip && !row.formula) return;
    const drillParams = DRILL_ROWS.has(row.l) && com
      ? { vendedor: com.vendedor, vendedorNome: com.nome, ano: CONS_ANO_DEF, mes: mesIdx != null ? mesIdx : null }
      : null;
    const carteiraTipo = CARTEIRA_DRILL[row.l];
    const carteiraParams = carteiraTipo && com
      ? { tipo: carteiraTipo, vendedor: com.vendedor, vendedorNome: com.nome, ano: CONS_ANO_DEF, mes: mesIdx != null ? mesIdx : null }
      : null;
    setInfoModal({ label:row.l, section:sec.label, tip:row.tip||null, formula:row.formula||null, fase:row.fase||null, val, period, drillParams, carteiraParams });
  };

  // ─── Block D computed values ────────────────────────────────────────────────

  const bpYTD   = bpMensal.slice(0, mes+1).reduce((s,v) => s+(v||0), 0);
  const bpAnual  = bpMensal.reduce((s,v) => s+(v||0), 0);
  const vendasYTD= ytdMensal.slice(0, mes+1).reduce((s,v) => s+(v||0), 0);

  // Margem %
  const margemPctByMonth = ytdMensal.map((v,i) => {
    const m = margemMensal[i];
    if (i > mes) return null;
    if (!v || !m) return null;
    return (m / v) * 100;
  });
  const margemYTDV   = margemMensal.slice(0, mes+1).reduce((s,v) => s+(v||0), 0);
  const margemYTDPct = vendasYTD > 0 ? (margemYTDV / vendasYTD) * 100 : null;

  // BP Desvio (cumulative per closed month)
  let cumDesvio = 0;
  const desvioByMonth = CONS_MESES.map((_, i) => {
    if (i > mes) return null;
    const v = ytdMensal[i], b = bpMensal[i];
    if (v == null && b == null) return null;
    cumDesvio += ((v||0) - (b||0));
    return cumDesvio;
  });
  const desvioYTD = vendasYTD - bpYTD;

  // Rating (Phase 1: Block D only, 40% weight)
  let ratingYTD = null;
  if (bpYTD > 0 && mes >= 0) {
    const vendasScore = Math.min(vendasYTD / bpYTD, 1.10) * 100;
    const margemScore = margemYTDPct != null ? Math.min(margemYTDPct / 35, 1.10) * 100 : 50;
    const blockD = 0.60 * vendasScore + 0.40 * margemScore;
    ratingYTD = 0.40 * blockD; // other blocks excluded in Phase 1
  }

  // ─── FM helpers ─────────────────────────────────────────────────────────────
  const fmArr  = (key) => fmData?.[key] || new Array(12).fill(0);
  const fmYTD  = (key) => fmArr(key).slice(0, mes+1).reduce((s,v)=>s+(v||0), 0);
  const fmCell = (key, i, fmt) => {
    if (!fmData) return ['—','m'];
    const v = fmArr(key)[i];
    if (i > mes) return ['—','m'];
    return v > 0 ? [fmt(v), ''] : ['—','m'];
  };
  const fmCellYTD = (key, fmt) => {
    if (!fmData) return ['—','m'];
    const t = fmYTD(key);
    return t > 0 ? [fmt(t), 'b'] : ['—','m'];
  };
  const fmHasData = !!fmData;

  // ─── Carteira data ───────────────────────────────────────────────────────────
  const carteiraData = React.useMemo(() => {
    if (!carteiraByV) return null;
    if (com) return carteiraByV[com.vendedor] || null;
    const coms = comerciaisFiltrados || [];
    if (!coms.length) return null;
    const agg = {
      ativosPerMes: new Array(12).fill(0), valorMedioPerMes: new Array(12).fill(null),
      novosPerMes: new Array(12).fill(0), vendasNovosPerMes: new Array(12).fill(0),
      vendasCarteiraPerMes: new Array(12).fill(0),
      familiasCliente: null, risco: 0, totalClientes2025: 0, totalClientes2026: 0,
      retencao: null, riscoPct: null,
    };
    let famTotal = 0, famCount = 0, prevTotal = 0, retainedTotal = 0;
    coms.forEach(c => {
      const d = carteiraByV[c.vendedor];
      if (!d) return;
      d.ativosPerMes.forEach((v,i) => agg.ativosPerMes[i] += v);
      d.novosPerMes.forEach((v,i) => agg.novosPerMes[i] += v);
      d.vendasNovosPerMes.forEach((v,i) => agg.vendasNovosPerMes[i] += v);
      d.vendasCarteiraPerMes.forEach((v,i) => agg.vendasCarteiraPerMes[i] += v);
      agg.risco += d.risco || 0;
      agg.totalClientes2025 += d.totalClientes2025 || 0;
      agg.totalClientes2026 += d.totalClientes2026 || 0;
      if (d.familiasCliente != null) { famTotal += d.familiasCliente; famCount++; }
      if (d.totalClientes2025 > 0) { prevTotal += d.totalClientes2025; retainedTotal += Math.round((d.retencao||0)*d.totalClientes2025); }
    });
    if (famCount > 0) agg.familiasCliente = famTotal / famCount;
    agg.riscoPct = agg.totalClientes2025 > 0 ? agg.risco / agg.totalClientes2025 : null;
    agg.retencao = prevTotal > 0 ? retainedTotal / prevTotal : null;
    agg.ativosPerMes.forEach((n,i) => {
      agg.valorMedioPerMes[i] = n > 0 && ytdMensal[i] != null ? ytdMensal[i] / n : null;
    });
    return agg;
  }, [carteiraByV, com, comerciaisFiltrados, ytdMensal]);

  const sumArr = (arr, upTo) => (arr||[]).slice(0,upTo+1).reduce((s,v)=>s+(v||0),0);
  const cd = carteiraData;
  const crtAtivos  = cd ? CONS_MESES.map((_,i) => i>mes?['—','m']:cd.ativosPerMes[i]>0?[String(cd.ativosPerMes[i]),'']:['—','m']) : DASH12;
  const crtValMed  = cd ? CONS_MESES.map((_,i) => i>mes?['—','m']:cd.valorMedioPerMes[i]>0?[cE(cd.valorMedioPerMes[i]),'']:['—','m']) : DASH12;
  const crtNovos   = cd ? CONS_MESES.map((_,i) => i>mes?['—','m']:cd.novosPerMes[i]>0?[String(cd.novosPerMes[i]),'']:['—','m']) : DASH12;
  const crtVNovos  = cd ? CONS_MESES.map((_,i) => i>mes?['—','m']:cd.vendasNovosPerMes[i]>0?[cE(cd.vendasNovosPerMes[i]),'']:['—','m']) : DASH12;
  const crtVCart   = cd ? CONS_MESES.map((_,i) => i>mes?['—','m']:cd.vendasCarteiraPerMes[i]!=0?[cE(cd.vendasCarteiraPerMes[i]),'']:['—','m']) : DASH12;
  const crtAtivosYTD = cd&&cd.totalClientes2026>0 ? [String(cd.totalClientes2026),'b'] : ['—','m'];
  const crtValMedYTD = (() => { const yv=ytdMensal.slice(0,mes+1).reduce((s,v)=>s+(v||0),0); return cd&&cd.totalClientes2026>0&&yv>0?[cE(yv/cd.totalClientes2026),'b']:['—','m']; })();
  const crtRiscoAno  = cd&&cd.risco>0 ? [String(cd.risco)+(cd.riscoPct!=null?' ('+Math.round(cd.riscoPct*100)+'%)':''),'n'] : ['—','m'];
  const crtRetAno    = cd&&cd.retencao!=null ? [(cd.retencao*100).toFixed(0)+'%','b '+(cd.retencao>=0.92?'p':cd.retencao>=0.8?'a':'n')] : ['—','m'];
  const crtFamYTD    = cd&&cd.familiasCliente!=null ? [cd.familiasCliente.toFixed(1).replace('.',','),'b '+(cd.familiasCliente>=2.5?'p':'a')] : ['—','m'];
  const crtNovosYTD  = cd ? (() => { const t=sumArr(cd.novosPerMes,mes); return t>0?[String(t),'b']:['—','m']; })() : ['—','m'];
  const crtVNovosYTD = cd ? (() => { const t=sumArr(cd.vendasNovosPerMes,mes); return t>0?[cE(t),'b']:['—','m']; })() : ['—','m'];
  const crtVCartYTD  = cd ? (() => { const t=sumArr(cd.vendasCarteiraPerMes,mes); return t>0?[cE(t),'b']:['—','m']; })() : ['—','m'];

  // ─── Grid section definitions ───────────────────────────────────────────────
  const sections = [
    {
      id: 'A', label: 'Gestão de Carteira',
      grpBg: 'color-mix(in oklch,#3b82f6 5%,transparent)',
      rows: [
        { l:'Clientes Ativos',       note:1, obj:'≥85%',    tip:'Clientes com pelo menos 1 fatura no mês. Contagem real Primavera (ytd_snap). Objetivo ≥85% requer carteira atribuída (CLIVEND — Fase 3).', formula:'Contagem de clientes distintos com fatura no mês\nFonte: comercial_ytd_snap (Primavera)\n\nNota: sem CLIVEND não é possível calcular % de carteira — mostra contagem absoluta', m:crtAtivos, ytd:crtAtivosYTD, res:['—','m'], ano:'—' },
        { l:'Clientes em Risco',            obj:'≤8%',     tip:'Clientes activos em 2025 que ainda não compraram em 2026. Proxy de risco de churn. Obj ≤8% requer carteira CLIVEND (Fase 3).', formula:'= Clientes com fatura em 2025 mas sem fatura em 2026 (até hoje)\nFonte: comercial_ytd_snap comparação ano anterior\n\nNota: % requer carteira total CLIVEND', m:DASH12, ytd:['—','m'], res:['—','m'], ano:crtRiscoAno },
        { l:'Retenção 12m',                 obj:'≥92%',    tip:'% de clientes que compraram em 2025 e também compraram em 2026. Mede fidelização da carteira activa. Fonte: Primavera ytd_snap.', formula:'= Clientes que compraram em 2025 E em 2026 ÷ Total clientes 2025 × 100\nFonte: comercial_ytd_snap', m:DASH12, ytd:['—','m'], res:['—','m'], ano:crtRetAno },
        { l:'Valor Médio / Cliente',        obj:'≥med+5%', tip:'Vendas YTD ÷ nº de clientes distintos com compra no mês. Indicador de profundidade de relação comercial. Fonte: Primavera ytd_snap.', formula:'= Σ Vendas mês ÷ Nº clientes activos no mês\nFonte: comercial_ytd_snap', m:crtValMed, ytd:crtValMedYTD, res:['—','m'], ano:'—' },
      ],
    },
    {
      id: 'B', label: 'Desenvolvimento por Família',
      grpBg: 'color-mix(in oklch,#8b5cf6 5%,transparent)',
      rows: [
        { l:'Famílias / Cliente', note:2, obj:'≥2,5', tip:'Nº médio de famílias de produto distintas compradas por cliente em 2026. Obj ≥2,5 — promove cross-sell de consumíveis. Fonte: Primavera ytd_snap.', formula:'= Σ famílias distintas por cliente (ano) ÷ Nº clientes activos\nFonte: comercial_ytd_snap', m:DASH12, ytd:crtFamYTD, res:['—','m'], ano:'—' },
        { l:'Share of Wallet',    note:2, obj:'≥40%', tip:'Estimativa da % do consumo total de consumíveis do cliente capturada pela Decal/Biond. Calculado sobre potencial SPIN. Obj ≥40%. Fase 2.', formula:'= Σ Vendas ao cliente ÷ Potencial SPIN identificado × 100', fase:'Fase 2', m:DASH12, ytd:['—','m'], res:['—','m'], ano:'—' },
      ],
    },
    {
      id: 'C', label: 'Prospeção SPIN & Novos Clientes',
      grpBg: 'color-mix(in oklch,#8b5cf6 5%,transparent)',
      rows: [
        { l:'Reuniões SPIN',              obj:'10/mês',  tip:'Nº de reuniões de diagnóstico SPIN realizadas no mês. Obj 10/mês. Inclui 1.ªs visitas e revisões de potencial. Registo manual pelo comercial. Fase 2.', formula:'Contagem de reuniões registadas no CRM\nInclui: 1.ª visita, revisão SPIN, follow-up qualificado', fase:'Fase 2', m:DASH12, ytd:['—','m'], res:['—','m'], ano:'—' },
        {
          l:'OPTMKT', obj:'—',
          tip:'Oportunidades abertas via Marketing (Source: ADS, NEWSLETTER, WEBPAGE). Contagem pelo mês de abertura. Fonte: GestorConnect FM (FMRS_API_OPORTUNIDADES).',
          formula:'Contagem de OPs com Source_GrupoFilter = MKT\npor mês de abertura (StartDate)',
          m: CONS_MESES.map((_,i) => fmCell('optmkt', i, v => String(v))),
          ytd: fmCellYTD('optmkt', v => String(v)),
          res: '—', ano: '—',
        },
        {
          l:'OPTCOM', obj:'—',
          tip:'Oportunidades abertas via acção comercial (excl. Marketing). Contagem pelo mês de abertura. Fonte: GestorConnect FM (FMRS_API_OPORTUNIDADES).',
          formula:'Contagem de OPs com Source_GrupoFilter ≠ MKT\npor mês de abertura (StartDate)',
          m: CONS_MESES.map((_,i) => fmCell('optcom', i, v => String(v))),
          ytd: fmCellYTD('optcom', v => String(v)),
          res: '—', ano: '—',
        },
        {
          l:'OPMANAGERF9', obj:'—',
          tip:'Soma do valor de pipeline (Produto_Valor) de todas as OPs abertas no mês (todos os stages, excl. WON e LOST). Fonte: GestorConnect FM.',
          formula:'= Σ Produto_Valor de OPs (todos os stages)\npor mês de abertura (StartDate)\nexcl. WON e LOST',
          m: CONS_MESES.map((_,i) => fmCell('opmanagerf9', i, cE)),
          ytd: fmCellYTD('opmanagerf9', cE),
          res: '—', ano: '—',
        },
        { l:'Potencial Identificado', note:2, obj:'—',  tip:'Consumo anual estimado do cliente em consumíveis, identificado no diagnóstico SPIN. Acumulado por família. Base para Share of Wallet e Cobertura. Fase 2.', formula:'= Σ consumo anual estimado por família (diagnóstico SPIN)\nRegistado por cliente, acumulado na carteira', fase:'Fase 2', m:DASH12, ytd:['—','m'], res:'—', ano:'—' },
        { l:'Cobertura de Potencial', note:3, obj:'≥1,5x', tip:'Potencial SPIN ativo (≤12 meses) ÷ objetivo anual BP. Garante que há pipeline suficiente para atingir o BP. Obj ≥1,5x. Fase 2.', formula:'= Potencial SPIN ativo (≤12 meses) ÷ Objectivo BP anual', fase:'Fase 2', m:DASH12, ytd:['—','m'], res:['—','m'], ano:'—' },
        { l:'Novos Clientes (1.ª compra)', obj:'2/mês', tip:'Clientes que realizaram a 1.ª compra em 2026 (não existiam em 2025). Conta apenas 1 vez por cliente, no mês da 1.ª compra. Fonte: Primavera ytd_snap.', formula:'Clientes em 2026 com fatura, que não têm registo em 2025\nContados no mês da 1.ª fatura do ano\nFonte: comercial_ytd_snap', m:crtNovos, ytd:crtNovosYTD, res:['—','m'], ano:'—' },
        { l:'Conversão SPIN → Cliente',   obj:'≥25%',  tip:'% de reuniões SPIN realizadas que resultaram em 1.ª compra num prazo de 90 dias. Obj ≥25%. Mede a eficácia da prospeção.', formula:'= Novos clientes (90 dias após SPIN) ÷ Reuniões SPIN × 100', fase:'Fase 2', m:DASH12, ytd:['—','m'], res:['—','m'], ano:'—' },
      ],
    },
    {
      id: 'ENC', label: 'Encomendas do Mês',
      grpBg: 'color-mix(in oklch,#f59e0b 5%,transparent)',
      rows: [
        {
          l:'F9FACTURAR', note:7, obj:'—',
          tip:'OPs em stage F9 (90/92) com ForecastDate no mês — pipeline confirmado a faturar. Requer campo ForecastDate em FMRS_API_OPORTUNIDADES (pendente Walter). Fase 2.',
          formula:'= Σ Produto_Valor de OPs com Stage ≥ 90\npor ForecastDate (data prevista de faturação)\nFonte: FMRS_API_OPORTUNIDADES + ForecastDate',
          fase:'Fase 2',
          m: DASH12, ytd: ['—','m'], res: '—', ano: '—',
        },
        { l:'Encomendas Potenciais',  note:7, obj:'—', tip:'Encomendas estimadas pelo comercial ainda não abertas no ERP. Baseadas em compromisso verbal ou proposta enviada. Fase 2.', formula:'Σ valor estimado pelo comercial\n(compromisso verbal ou proposta enviada, ainda não no ERP)', fase:'Fase 2', m:DASH12, ytd:['—','m'], res:'—', ano:'—' },
        { l:'Taxa de Concretização',         obj:'≥70%', tip:'% do valor de encomendas potenciais que foi efetivamente faturado. Obj ≥70%. Mede a fiabilidade das previsões do comercial.', formula:'= Σ Faturado do mês anterior ÷ (Confirmadas + Potenciais) × 100', fase:'Fase 2', m:DASH12, ytd:['—','m'], res:['—','m'], ano:'—' },
      ],
    },
    {
      id: 'D', label: 'Resultado',
      grpBg: 'color-mix(in oklch,#22c55e 5%,transparent)',
      rows: [
        {
          l:'Objectivo BP', obj:'—', tip:'Objetivo mensal de vendas do Business Plan. Definido no início do ano fiscal. Fonte: comercial_bp_targets (portal BD).', formula:'Fonte: tabela comercial_bp_targets\nInserido pelo gestor de equipa por vendedor/mês\nBP YTD = Σ target Jan → mês actual',
          m: bpMensal.map((v,i) => i>mes ? ['—','m'] : (v!=null ? cE(v) : '—')),
          ytd: bpYTD>0 ? [cE(bpYTD),'b'] : ['—','m'],
          res: '—',
          ano: bpAnual>0 ? cE(bpAnual) : '—',
        },
        { l:'Vendas Carteira',       obj:'—', tip:'Vendas a clientes que já compraram em 2025 (recorrentes). Separa crescimento orgânico de novos clientes. Fonte: Primavera ytd_snap.', formula:'= Σ vendas a clientes presentes em 2025 E em 2026\nFonte: comercial_ytd_snap', m:crtVCart, ytd:crtVCartYTD, res:'—', ano:'—' },
        { l:'Vendas Novos Clientes', obj:'—', tip:'Vendas a clientes sem histórico em 2025 (1.ª compra em 2026). Mede crescimento orgânico de nova carteira. Fonte: Primavera ytd_snap.', formula:'= Σ vendas a clientes sem fatura em 2025\nFonte: comercial_ytd_snap', m:crtVNovos, ytd:crtVNovosYTD, res:'—', ano:'—' },
        {
          l:'Vendas Fact.', obj:'—', tip:'Total de vendas faturadas no Primavera. Inclui todos os tipos de documento de venda activos. Fonte: comercial_ytd_snap (sync diário).', formula:'Fonte: comercial_ytd_snap (sync Primavera diário às 03h)\nTipos de doc activos: FA, VD, FR, FAC, FAT, FAD\nVendas YTD = Σ valor Jan → mês actual',
          m: ytdMensal.map((v,i) => i>mes ? ['—','m'] : (v!=null ? cE(v) : '—')),
          ytd: vendasYTD>0 ? [cE(vendasYTD),'b'] : ['—','m'],
          res: '—',
          ano: '—',
        },
        { l:'Previsão Vendas', note:4, obj:'—', tip:'Previsão = baseline recorrente da carteira (média 6 meses × coeficiente sazonal) + potencial SPIN ponderado por probabilidade de fecho. Fase 2.', formula:'= Média(vendas últimos 6 meses) × coef. sazonal histórico\n  + Σ (Potencial SPIN × probabilidade de fecho)', fase:'Fase 2', m:DASH12, ytd:'—', res:'—', ano:'—' },
        {
          l:'Margem', obj:'35%', tip:'Margem bruta % = (Vendas − Custo mercadoria) ÷ Vendas. Obj ≥35%. Verde ≥35%, Amarelo ≥25%, Vermelho <25%. Fonte: comercial_ytd_snap.', formula:'Margem % = Σ margem_bruta ÷ Σ vendas × 100\n\nVerde  ≥ 35%\nAmarelo ≥ 25%\nVermelho < 25%',
          m: margemPctByMonth.map((pct,i) => {
            if (i > mes) return ['—','m'];
            if (pct == null) return ['—','m'];
            const s = pct.toFixed(1).replace('.',',')+'%';
            const cls = pct>=35?'p':pct>=25?'a':'n';
            return [s, cls];
          }),
          ytd: margemYTDPct!=null
            ? [margemYTDPct.toFixed(1).replace('.',',')+'%', 'b '+(margemYTDPct>=35?'p':margemYTDPct>=25?'a':'n')]
            : ['—','m'],
          res: margemYTDPct!=null
            ? [(margemYTDPct-35>=0?'+':'')+(margemYTDPct-35).toFixed(1).replace('.',',')+'pp', margemYTDPct>=35?'p':margemYTDPct>=25?'a':'n']
            : ['—','m'],
          ano: '—',
        },
        {
          l:'BP Desvio', note:4, obj:'—', tip:'Desvio acumulado = Vendas Fact. acumuladas − Objetivo BP acumulado. Positivo (verde) = acima do objetivo. Negativo (vermelho) = abaixo.', formula:'Desvio = Σ Vendas Fact.(Jan→mês) − Σ BP(Jan→mês)\n\nPositivo (verde)  = acima do objectivo\nNegativo (vermelho) = abaixo do objectivo',
          m: desvioByMonth.map((d,i) => {
            if (i > mes) return ['—','f m'];
            if (d == null) return ['—','m'];
            return [cSign(d), 'b '+(d>=0?'p':'n')];
          }),
          ytd: [cSign(desvioYTD), 'b '+(desvioYTD>=0?'p':'n')],
          res: '—',
          ano: ['—','m'],
        },
        {
          l:'RPC Rating', note:5, obj:'100%', tip:'Rating composto: Resultado 40% · Carteira 25% · SPIN & Novos 20% · Desenvolvimento 15%. Fase 1: apenas Bloco D contribui (40% do peso). Verde ≥80, Amarelo ≥60, Vermelho <60.', formula:'Rating = Resultado×40% + Carteira×25% + SPIN×20% + Dev×15%\n\nFase 1 (actual): Rating parcial = Resultado×40%\n  Resultado = Vendas%BP×60% + Margem%Obj×40%\n\nVerde ≥ 80 · Amarelo ≥ 60 · Vermelho < 60',
          m: DASH12,
          ytd: ratingYTD!=null
            ? [ratingYTD.toFixed(0)+'%', 'b '+(ratingYTD>=80?'p':ratingYTD>=60?'a':'n')]
            : ['—','m'],
          res: ratingYTD!=null
            ? [(ratingYTD-100>=0?'+':'')+(ratingYTD-100).toFixed(0)+'pp', ratingYTD>=80?'p':ratingYTD>=60?'a':'n']
            : ['—','m'],
          ano: '—',
        },
      ],
    },
  ];

  // ─── Render ───────────────────────────────────────────────────────────────
  return (
    <div>
      <ConsInfoModal info={infoModal} onClose={() => setInfoModal(null)} />
      {/* Active note */}
      {activeNote && (() => {
        const note = CONS_NOTES.find(x => x.n === activeNote);
        return note ? (
          <div style={{ marginBottom:12, padding:'8px 14px', borderRadius:7, background:'color-mix(in oklch,#3b82f6 8%,transparent)', border:'1px solid color-mix(in oklch,#3b82f6 20%,transparent)', fontSize:12, color:'var(--dgd-fg-1)', lineHeight:1.55 }}>
            <strong>Nota {CONS_SUPS[note.n-1]}:</strong> {note.text}
          </div>
        ) : null;
      })()}

      {/* Table */}
      <div style={{ overflowX:'auto', borderRadius:8, border:'1px solid var(--dgd-border-1)' }}>
        <table style={{ borderCollapse:'collapse', width:'100%', minWidth:1200 }}>
          <thead>
            <tr>
              <th style={{ ...thB, textAlign:'left', minWidth:230, position:'sticky', left:0, zIndex:2 }}>—</th>
              <th style={{ ...thB, color:'#7c3aed', background:'color-mix(in oklch,#7c3aed 6%,transparent)', minWidth:70 }}>Obj.</th>
              {CONS_MESES.map((m, i) => (
                <th key={m} style={{
                  ...thB,
                  color:      i===mes ? '#3563c9' : i>mes ? 'var(--dgd-fg-3)' : undefined,
                  background: i===mes ? 'color-mix(in oklch,#3b82f6 7%,transparent)' : undefined,
                  minWidth: 52,
                }}>{m}</th>
              ))}
              <th style={{ ...thB, borderLeft:'2px solid var(--dgd-border-1)', minWidth:64 }}>YTD</th>
              <th style={{ ...thB, borderLeft:'1px solid var(--dgd-border-1)', minWidth:64 }}>Result.</th>
              <th style={{ ...thB, borderLeft:'1px solid var(--dgd-border-1)', minWidth:64 }}>Ano</th>
            </tr>
          </thead>
          <tbody>
            {sections.map((sec, si) => (
              <React.Fragment key={sec.id}>
                {/* Section label row */}
                <tr>
                  <td colSpan={17} style={{ padding:'3px 7px', fontSize:9, letterSpacing:'0.12em', textTransform:'uppercase', fontWeight:700, color:'var(--dgd-fg-3)', fontFamily:'var(--dgd-font-mono,monospace)', background:'var(--dgd-bg-surface)', borderTop: si>0 ? '2px solid var(--dgd-border-1)' : undefined, borderBottom:'1px solid var(--dgd-border-1)' }}>
                    {sec.label}
                  </td>
                </tr>
                {sec.rows.map((row, ri) => {
                  const ytdC = parseCell(row.ytd);
                  const resC = parseCell(row.res);
                  const anoC = parseCell(row.ano);
                  const btop = {};
                  const bg = sec.grpBg || 'transparent';
                  const isBPRow = row.l === 'Objectivo BP';
                  return (
                    <tr key={ri} style={{ background: bg }}>
                      <td style={{ ...tdB, textAlign:'left', position:'sticky', left:0, background: bg, zIndex:1, color:'var(--dgd-fg-1)', ...btop }}>
                        {row.l}
                        {isBPRow && com && (
                          <span style={{ marginLeft:6, fontSize:9, color:'var(--dgd-fg-3)', fontFamily:'var(--dgd-font-mono,monospace)', letterSpacing:'0.06em' }}>
                            {saving ? 'a guardar…' : '✎ clique para editar'}
                          </span>
                        )}
                        {row.tip && <ConsInfoTip text={row.tip} />}
                        {row.note && <ConsNoteBadge n={row.note} {...noteProps} />}
                      </td>
                      <td style={{ ...tdB, color:'#7c3aed', background:'color-mix(in oklch,#7c3aed 6%,transparent)', fontWeight:500, ...btop }}>{row.obj}</td>
                      {row.m.map((v, i) => {
                        const {val, cls} = parseCell(v);
                        const isCur = i === mes;
                        const isFut = i > mes;

                        // ─ BP editable cell
                        if (isBPRow && com && !isFut) {
                          const isEditing = editCell?.mesIdx === i;
                          if (isEditing) {
                            return (
                              <td key={i} style={{ ...tdB, background: isCur ? 'color-mix(in oklch,#3b82f6 7%,transparent)' : bg, padding:'2px 4px' }}>
                                <input
                                  autoFocus
                                  defaultValue={editCell.val}
                                  style={{ width:52, fontSize:10.5, fontFamily:'var(--dgd-font-mono,monospace)', textAlign:'right', border:'1.5px solid #3b82f6', borderRadius:3, padding:'1px 3px', background:'white', outline:'none' }}
                                  onKeyDown={e => {
                                    if (e.key === 'Enter') saveBP(i, e.target.value);
                                    if (e.key === 'Escape') setEditCell(null);
                                  }}
                                  onBlur={e => saveBP(i, e.target.value)}
                                />
                              </td>
                            );
                          }
                          return (
                            <td key={i}
                              onClick={() => setEditCell({ mesIdx: i, val: bpMensal[i] ? String(Math.round(bpMensal[i])) : '' })}
                              title="Clique para editar objectivo BP"
                              style={{ ...tdB, background: isCur ? 'color-mix(in oklch,#3b82f6 7%,transparent)' : bg, ...cCls(cls), cursor:'text', ...btop }}>{val}</td>
                          );
                        }

                        const clickable = !isFut && (row.tip || row.formula);
                        return (
                          <td key={i}
                            onClick={clickable ? () => openCell(row, sec, val, CONS_MESES[i], i) : undefined}
                            style={{
                              ...tdB,
                              background: isCur ? 'color-mix(in oklch,#3b82f6 7%,transparent)' : bg,
                              color: (isFut && !cls) ? 'var(--dgd-fg-3)' : undefined,
                              ...cCls(cls),
                              ...btop,
                              cursor: clickable ? 'pointer' : 'default',
                            }}>{val}</td>
                        );
                      })}
                      <td onClick={() => openCell(row, sec, ytdC.val, 'YTD', null)} style={{ ...tdB, borderLeft:'2px solid var(--dgd-border-1)', fontWeight:600, ...cCls(ytdC.cls), ...btop, cursor:(row.tip||row.formula)?'pointer':'default' }}>{ytdC.val}</td>
                      <td onClick={() => openCell(row, sec, resC.val, 'Resultado', null)} style={{ ...tdB, borderLeft:'1px solid var(--dgd-border-1)', ...cCls(resC.cls), ...btop, cursor:(row.tip||row.formula)?'pointer':'default' }}>{resC.val}</td>
                      <td onClick={() => openCell(row, sec, anoC.val, 'Ano', null)} style={{ ...tdB, borderLeft:'1px solid var(--dgd-border-1)', color:'var(--dgd-fg-2)', ...cCls(anoC.cls), ...btop, cursor:(row.tip||row.formula)?'pointer':'default' }}>{anoC.val}</td>
                    </tr>
                  );
                })}
              </React.Fragment>
            ))}
          </tbody>
        </table>
      </div>

      {/* Phase 1 notice */}
      <div style={{ marginTop:16, padding:'10px 14px', borderRadius:7, background:'color-mix(in oklch,#3b82f6 6%,transparent)', border:'1px solid color-mix(in oklch,#3b82f6 25%,transparent)', fontSize:11.5, color:'#1e40af', lineHeight:1.5 }}>
        <strong>Dados em tempo real:</strong> BP · Vendas Fact. · Margem · BP Desvio (Primavera) · OPTMKT · OPTCOM · OPMANAGERF9 · F9FACTURAR (GestorConnect FM).
        {' '}Reuniões SPIN · Carteira · SPIN Potencial disponíveis na <strong>Fase 2</strong>.
      </div>

      {/* Notes legend */}
      <div style={{ marginTop:10, display:'flex', flexWrap:'wrap', gap:'6px 16px' }}>
        {CONS_NOTES.map(note => (
          <span key={note.n}
            onClick={() => setActiveNote(activeNote===note.n ? null : note.n)}
            style={{ fontSize:11, color:'var(--dgd-fg-3)', cursor:'pointer', textDecoration: activeNote===note.n ? 'underline' : 'none' }}>
            {CONS_SUPS[note.n-1]} {note.text.split('.')[0]}.
          </span>
        ))}
      </div>
    </div>
  );
};

// ─── Tab: Show me the Money ────────────────────────────────────────────────────
const TabShowMoneyCons = ({ comerciais, byV, margemByV, ano }) => {
  const mes = CONS_MES_FECH;
  const [modal, setModal] = React.useState(null);

  if (!comerciais || comerciais.length === 0) {
    return <div style={{ padding:'40px', textAlign:'center', color:'var(--dgd-fg-3)', fontSize:13 }}>Sem comerciais para esta marca.</div>;
  }

  const totVendas = new Array(12).fill(0);
  const totMargem = new Array(12).fill(0);

  comerciais.forEach(c => {
    (byV[c.vendedor]||[]).forEach((v,i) => { if(v!=null) totVendas[i] += v; });
    (margemByV[c.vendedor]||[]).forEach((v,i) => { if(v!=null) totMargem[i] += v; });
  });

  const ytdTot = totVendas.slice(0, mes+1).reduce((s,v) => s+v, 0);

  return (
    <>
    <ConsInfoModal info={modal} onClose={() => setModal(null)} />
    <div style={{ overflowX:'auto', borderRadius:8, border:'1px solid var(--dgd-border-1)' }}>
      <table style={{ borderCollapse:'collapse', width:'100%', minWidth:1100 }}>
        <thead>
          <tr>
            <th style={{ ...thB, textAlign:'left', minWidth:180, position:'sticky', left:0, background:'var(--dgd-bg-card,white)' }}>Comercial</th>
            {CONS_MESES.map((m, i) => (
              <th key={m} style={{ ...thB, color: i===mes?'#3563c9':i>mes?'var(--dgd-fg-3)':undefined, background: i===mes?'color-mix(in oklch,#3b82f6 7%,transparent)':undefined, minWidth:52 }}>{m}</th>
            ))}
            <th style={{ ...thB, borderLeft:'2px solid var(--dgd-border-1)', minWidth:64 }}>YTD</th>
            <th style={{ ...thB, borderLeft:'1px solid var(--dgd-border-1)', minWidth:52 }}>Margem</th>
          </tr>
        </thead>
        <tbody>
          {comerciais.map((c, ci) => {
            const vals = byV[c.vendedor] || new Array(12).fill(null);
            const mvals = margemByV[c.vendedor] || new Array(12).fill(null);
            const ytd = vals.slice(0, mes+1).reduce((s,v) => s+(v||0), 0);
            const ytdM = mvals.slice(0, mes+1).reduce((s,v) => s+(v||0), 0);
            const margemPct = ytd > 0 ? (ytdM/ytd)*100 : null;
            const isLast = ci === comerciais.length - 1;
            return (
              <tr key={c.id} style={{ background: ci%2===0?'var(--dgd-bg-card,white)':'var(--dgd-bg-surface,#fafafa)' }}>
                <td style={{ ...tdB, textAlign:'left', position:'sticky', left:0, background: ci%2===0?'var(--dgd-bg-card,white)':'var(--dgd-bg-surface,#fafafa)', color:'var(--dgd-fg-1)', fontWeight:500, borderBottom:isLast?'2px solid var(--dgd-border-1)':undefined }}>{c.nome}</td>
                {vals.map((v, i) => {
                  const clickable = i <= mes && v != null;
                  const cellVal = i > mes ? '—' : v != null ? cE(v) : '—';
                  return (
                    <td key={i}
                      onClick={clickable ? () => setModal({ label:c.nome, section:'Show me the Money', tip:`Vendas faturadas por ${c.nome} em ${CONS_MESES[i]} ${ano}.`, formula:'Fonte: comercial_ytd_snap (sync diário às 03h)\nDetalhe por família e artigo.', val:cellVal, period:`${CONS_MESES[i]} ${ano}`, drillParams:{ vendedor:c.vendedor, vendedorNome:c.nome, ano, mes:i } }) : undefined}
                      style={{ ...tdB, color:i>mes?'var(--dgd-fg-3)':v==null?'var(--dgd-fg-3)':undefined, background:i===mes?'color-mix(in oklch,#3b82f6 4%,transparent)':undefined, borderBottom:isLast?'2px solid var(--dgd-border-1)':undefined, cursor:clickable?'pointer':'default' }}>
                      {cellVal}
                    </td>
                  );
                })}
                <td
                  onClick={ytd>0 ? () => setModal({ label:c.nome, section:'Show me the Money', tip:`Vendas YTD de ${c.nome} em ${ano}.`, formula:'Fonte: comercial_ytd_snap — acumulado anual.', val:ytd>0?cE(ytd):'—', period:`YTD ${ano}`, drillParams:{ vendedor:c.vendedor, vendedorNome:c.nome, ano, mes:null } }) : undefined}
                  style={{ ...tdB, fontWeight:700, borderLeft:'2px solid var(--dgd-border-1)', color:'var(--dgd-fg-1)', borderBottom:isLast?'2px solid var(--dgd-border-1)':undefined, cursor:ytd>0?'pointer':'default' }}>{ytd>0?cE(ytd):'—'}</td>
                <td style={{ ...tdB, borderLeft:'1px solid var(--dgd-border-1)', color: margemPct==null?'var(--dgd-fg-3)':margemPct>=35?'#16a34a':margemPct>=25?'#d97706':'#dc2626', borderBottom:isLast?'2px solid var(--dgd-border-1)':undefined }}>
                  {margemPct!=null ? margemPct.toFixed(1).replace('.',',')+'%' : '—'}
                </td>
              </tr>
            );
          })}
          {/* Totals row */}
          <tr style={{ background:'var(--dgd-bg-surface,#f8fafc)' }}>
            <td style={{ ...tdB, textAlign:'left', position:'sticky', left:0, background:'var(--dgd-bg-surface,#f8fafc)', color:'var(--dgd-fg-2)', fontWeight:700, fontSize:11, letterSpacing:'0.08em', textTransform:'uppercase' }}>Totais</td>
            {totVendas.map((v, i) => (
              <td key={i} style={{ ...tdB, color: i>mes?'var(--dgd-fg-3)':undefined, background: i===mes?'color-mix(in oklch,#3b82f6 4%,transparent)':undefined, fontWeight:600 }}>
                {i>mes ? '—' : v>0 ? cE(v) : '—'}
              </td>
            ))}
            <td style={{ ...tdB, fontWeight:700, borderLeft:'2px solid var(--dgd-border-1)', color:'var(--dgd-fg-1)' }}>{ytdTot>0?cE(ytdTot):'—'}</td>
            <td style={{ ...tdB, borderLeft:'1px solid var(--dgd-border-1)', color:'var(--dgd-fg-2)' }}>
              {(() => {
                const m = totMargem.slice(0,mes+1).reduce((s,v)=>s+v,0);
                return ytdTot>0 ? (m/ytdTot*100).toFixed(1).replace('.',',')+'%' : '—';
              })()}
            </td>
          </tr>
        </tbody>
      </table>
    </div>
    </>
  );
};

// ─── Tab: YTD Famílias ─────────────────────────────────────────────────────────
const TabYTDFamiliasCons = ({ ytdRows, selCom, vendedoresFiltrados }) => {
  const mes = CONS_MES_FECH;
  const mLabel = CONS_MESES[mes];
  const [modal, setModal] = React.useState(null);

  // Filter rows for selected comercial, or by current equipa/marca if in aggregate mode
  const vSet = React.useMemo(() => new Set(vendedoresFiltrados || []), [vendedoresFiltrados]);
  const rows = selCom
    ? ytdRows.filter(r => r.vendedor === selCom.vendedor)
    : ytdRows.filter(r => vSet.size === 0 || vSet.has(r.vendedor));

  // Aggregate by familia
  const famMap = {};
  rows.forEach(r => {
    if (!famMap[r.familia]) famMap[r.familia] = { familia:r.familia, valor:0, qtd:0 };
    famMap[r.familia].valor += parseFloat(r.valor)||0;
    famMap[r.familia].qtd   += parseFloat(r.qtd)||0;
  });
  const fams = Object.values(famMap).sort((a,b) => b.valor-a.valor);
  const totalValor = fams.reduce((s,f) => s+f.valor, 0);

  if (!fams.length) {
    return (
      <div style={{ padding:'40px', textAlign:'center', color:'var(--dgd-fg-3)', fontSize:13 }}>
        {selCom ? `Sem dados de famílias para ${selCom.nome} em ${CONS_ANO_DEF}.` : `Sem dados de famílias para ${CONS_ANO_DEF}.`}
      </div>
    );
  }

  return (
    <div>
      <ConsInfoModal info={modal} onClose={() => setModal(null)} />
      <div style={{ marginBottom:10, fontSize:12, color:'var(--dgd-fg-3)' }}>
        YTD Jan–{mLabel} · {selCom ? selCom.nome : 'Todas as equipas'} · {fams.length} família{fams.length!==1?'s':''}
      </div>
      <div style={{ overflowX:'auto', borderRadius:8, border:'1px solid var(--dgd-border-1)' }}>
        <table style={{ borderCollapse:'collapse', width:'100%', minWidth:500 }}>
          <thead>
            <tr>
              <th style={{ ...thB, textAlign:'left', minWidth:200 }}>Família</th>
              <th style={{ ...thB }}>Vendas YTD</th>
              <th style={{ ...thB }}>Mix %</th>
              <th style={{ ...thB }}>Potencial SPIN</th>
              <th style={{ ...thB }}>Share of Wallet</th>
              <th style={{ ...thB, color:'#7c3aed' }}>Obj. 2027 proposto</th>
            </tr>
          </thead>
          <tbody>
            {fams.map((f, i) => {
              const mix = totalValor > 0 ? (f.valor/totalValor*100) : 0;
              const barW = Math.round(mix * 1.2);
              const drillData = rows
                .filter(r => r.familia === f.familia)
                .map(r => ({ familia:r.familia, artigo:r.artigo, qtd:r.qtd, valor:r.valor, margem:null }))
                .sort((a,b) => parseFloat(b.valor||0) - parseFloat(a.valor||0));
              return (
                <tr key={f.familia}
                  onClick={() => setModal({ label:f.familia||'(sem família)', section:'YTD Famílias', tip:`Artigos da família ${f.familia||'(sem família)'} — YTD Jan–${mLabel}.`, val:cE(f.valor), period:`YTD ${CONS_ANO_DEF}`, drillData })}
                  style={{ background:i%2===0?'var(--dgd-bg-card,white)':'var(--dgd-bg-surface,#fafafa)', cursor:'pointer' }}>
                  <td style={{ ...tdB, textAlign:'left', color:'var(--dgd-fg-1)' }}>
                    <span style={{ display:'inline-block', width:Math.min(barW,80)+'px', height:5, borderRadius:3, background:'color-mix(in oklch,#3b82f6 55%,transparent)', verticalAlign:'middle', marginRight:8 }} />
                    {f.familia || '(sem família)'}
                  </td>
                  <td style={{ ...tdB, fontWeight:600 }}>{cE(f.valor)}</td>
                  <td style={{ ...tdB, color:'var(--dgd-fg-2)' }}>{mix.toFixed(0)}%</td>
                  <td style={{ ...tdB, color:'var(--dgd-fg-3)' }}>—</td>
                  <td style={{ ...tdB, color:'var(--dgd-fg-3)' }}>—</td>
                  <td style={{ ...tdB, color:'#7c3aed' }}>—</td>
                </tr>
              );
            })}
          </tbody>
          <tfoot>
            <tr>
              <td style={{ ...tdB, textAlign:'left', fontWeight:700, color:'var(--dgd-fg-1)', borderTop:'2px solid var(--dgd-border-1)' }}>Total</td>
              <td style={{ ...tdB, fontWeight:700, borderTop:'2px solid var(--dgd-border-1)' }}>{cE(totalValor)}</td>
              <td style={{ ...tdB, borderTop:'2px solid var(--dgd-border-1)', color:'var(--dgd-fg-2)' }}>100%</td>
              <td colSpan={3} style={{ ...tdB, borderTop:'2px solid var(--dgd-border-1)', color:'var(--dgd-fg-3)', textAlign:'left' }}>Potencial SPIN disponível na Fase 2</td>
            </tr>
          </tfoot>
        </table>
      </div>
    </div>
  );
};

// ─── Tab: Placeholder ──────────────────────────────────────────────────────────
const TabConsPlaceholder = ({ tab }) => {
  const info = {
    forecast:   { title:'Forecast', desc:'Previsão de vendas por comercial: baseline recorrente da carteira (média 6 meses × coef. sazonal) + potencial SPIN ponderado por probabilidade. Disponível na Fase 2.', fase:'Fase 2' },
    predit:     { title:'Preditividade', desc:'Fecho do mês em curso: faturado + encomendas confirmadas + 60% do potencial registado pelo comercial. Drilldown de oportunidades/encomendas ativas. Disponível na Fase 2.', fase:'Fase 2' },
    mediasales: { title:'Media Sales AI', desc:'Análise AI de padrões de compra, alertas de clientes em risco e sugestões de próxima ação por comercial. Disponível na Fase 2.', fase:'Fase 2' },
    sugestoes:  { title:'Sugestões AI', desc:'Acções de melhoria por comercial: famílias com potencial não capturado, clientes sem visita SPIN, comparação entre pares. Disponível na Fase 2.', fase:'Fase 2' },
    previsao:   { title:'Previsão AI', desc:'Modelos preditivos ML (ARIMA/Prophet) adaptados ao modelo de consumíveis: baseline de carteira + sazonalidade histórica + potencial SPIN. Disponível na Fase 3.', fase:'Fase 3' },
  }[tab] || { title: tab, desc: 'Em construção.', fase: 'Fase 2' };

  return (
    <div style={{ display:'flex', alignItems:'center', justifyContent:'center', minHeight:320 }}>
      <div style={{ textAlign:'center', maxWidth:440, padding:'40px 32px', borderRadius:12, background:'var(--dgd-bg-card,white)', border:'1px solid var(--dgd-border-1)', boxShadow:'0 2px 8px rgba(0,0,0,0.06)' }}>
        <div style={{ fontSize:32, marginBottom:12 }}>🔧</div>
        <div style={{ fontSize:15, fontWeight:700, color:'var(--dgd-fg-1)', marginBottom:8 }}>{info.title}</div>
        <div style={{ fontSize:13, color:'var(--dgd-fg-2)', lineHeight:1.6, marginBottom:16 }}>{info.desc}</div>
        <div style={{ display:'inline-block', padding:'4px 14px', borderRadius:20, background:'color-mix(in oklch,#f59e0b 12%,transparent)', border:'1px solid color-mix(in oklch,#f59e0b 30%,transparent)', fontSize:11, fontWeight:600, color:'#92400e' }}>{info.fase}</div>
      </div>
    </div>
  );
};

// ─── Main Screen ───────────────────────────────────────────────────────────────
const ScreenComercialRPCConsumiveis = ({ userTipo, userName, user }) => {
  const [tab,      setTab]      = React.useState('rpc');
  const [ano,      setAno]      = React.useState(CONS_ANO_DEF);
  const [marca,    setMarca]    = React.useState('Decal PT');
  const [selEquipa, setSelEquipa] = React.useState(null); // null = todas
  const [selComId, setSelComId] = React.useState(null);

  const empresa = CONS_MARCA_EMP[marca];

  const [bpRefresh, setBpRefresh] = React.useState(0);
  const handleBpSave = React.useCallback(() => setBpRefresh(k => k + 1), []);

  const { comerciais, loading: comsLoading } = useConsComerciais(empresa);
  const { byV, margemByV, ytdRows, loading: ytdLoading } = useConsYTD(ano);
  const bpMap = useConsBP(ano, bpRefresh);

  const fmEmpresa = CONS_MARCA_FM[marca];
  const { byVendedor: fmByV, loading: fmLoading } = useConsFMPipeline(fmEmpresa, ano);
  const { byVendedor: carteiraByV } = useConsCarteira(ano);

  // Equipas disponíveis para a marca actual (ordenadas)
  const equipas = React.useMemo(() => {
    if (!comerciais) return [];
    const seen = new Set();
    return comerciais
      .map(c => c.seccao)
      .filter(s => { if (seen.has(s)) return false; seen.add(s); return true; })
      .sort((a,b) => a.localeCompare(b, 'pt'));
  }, [comerciais]);

  // Reset equipa quando muda de marca
  React.useEffect(() => { setSelEquipa(null); setSelComId(null); }, [marca]);

  // Comerciais filtrados pela equipa seleccionada
  const comerciaisFiltrados = React.useMemo(() => {
    if (!comerciais) return [];
    if (!selEquipa) return comerciais;
    return comerciais.filter(c => c.seccao === selEquipa);
  }, [comerciais, selEquipa]);

  // "Todos" = selComId null; comercial específico = selComId = c.id
  const isAll = selComId === null;

  const selCom = React.useMemo(() => {
    if (isAll || !comerciaisFiltrados.length) return null;
    return comerciaisFiltrados.find(c => c.id === selComId) || null;
  }, [isAll, comerciaisFiltrados, selComId]);

  // Agrega arrays mensais de todos os comerciais filtrados
  const aggMensal = React.useCallback((map) => {
    const result = new Array(12).fill(null);
    comerciaisFiltrados.forEach(c => {
      (map?.[c.vendedor] || []).forEach((v, i) => {
        if (v != null) result[i] = (result[i] || 0) + v;
      });
    });
    return result;
  }, [comerciaisFiltrados]);

  const ytdMensal    = React.useMemo(() => isAll ? aggMensal(byV)       : (selCom ? (byV?.[selCom.vendedor]       || new Array(12).fill(null)) : new Array(12).fill(null)), [isAll, selCom, aggMensal, byV]);
  const margemMensal = React.useMemo(() => isAll ? aggMensal(margemByV) : (selCom ? (margemByV?.[selCom.vendedor] || new Array(12).fill(null)) : new Array(12).fill(null)), [isAll, selCom, aggMensal, margemByV]);
  const bpMensal     = React.useMemo(() => isAll ? aggMensal(bpMap)     : (selCom ? (bpMap[selCom.vendedor]       || new Array(12).fill(null)) : new Array(12).fill(null)), [isAll, selCom, aggMensal, bpMap]);

  // Vendedores filtrados (para YTD Famílias em modo Todos)
  const vendedoresFiltrados = React.useMemo(() => comerciaisFiltrados.map(c => c.vendedor), [comerciaisFiltrados]);

  // FM pipeline data — agrega para todos os comerciais filtrados (modo Todos) ou por comercial individual
  const FM_KEYS = ['optmkt','optcom','opmanagerf9','wonValor'];
  const aggFMData = React.useMemo(() => {
    if (!fmByV || !comerciaisFiltrados.length) return null;
    const result = {};
    FM_KEYS.forEach(k => { result[k] = new Array(12).fill(0); });
    let hasAny = false;
    comerciaisFiltrados.forEach(c => {
      const vd = fmByV[c.vendedor];
      if (!vd) return;
      hasAny = true;
      FM_KEYS.forEach(k => {
        (vd[k] || []).forEach((v, i) => { result[k][i] += v || 0; });
      });
    });
    return hasAny ? result : null;
  }, [fmByV, comerciaisFiltrados]);

  const fmData = React.useMemo(() => {
    if (isAll) return aggFMData;
    if (!selCom || !fmByV) return null;
    return fmByV[selCom.vendedor] || null;
  }, [isAll, selCom, fmByV, aggFMData]);

  // KPIs strip
  const mLabel   = CONS_MESES[CONS_MES_FECH];
  const ytdTotal = ytdMensal.slice(0, CONS_MES_FECH+1).reduce((s,v) => s+(v||0), 0);
  const bpTotal  = bpMensal.slice(0, CONS_MES_FECH+1).reduce((s,v) => s+(v||0), 0);
  const desvio   = ytdTotal - bpTotal;
  const pctBP    = bpTotal > 0 ? (ytdTotal/bpTotal)*100 : null;
  const kpisLabel = isAll ? (selEquipa || 'Todos os comerciais') : (selCom?.nome || '');

  const kpis = (ytdTotal > 0 || bpTotal > 0) ? [
    { l:'VENDAS YTD', v: cE(ytdTotal), c: ytdTotal>=bpTotal ? '#16a34a' : '#dc2626', d:`Jan–${mLabel} · ${kpisLabel}` },
    { l:'BP YTD',     v: cE(bpTotal),  c: 'var(--dgd-fg-2)', d:`Objectivo acumulado Jan–${mLabel}` },
    { l:'DESVIO',     v: cSign(desvio), c: desvio>=0?'#16a34a':'#dc2626', d:'Vendas − BP acumulado' },
    { l:'% BP',       v: pctBP!=null ? pctBP.toFixed(1).replace('.',',')+'%' : '—', c: pctBP==null?'var(--dgd-fg-3)':pctBP>=100?'#16a34a':pctBP>=85?'#d97706':'#dc2626', d:'Realizado vs objectivo' },
  ] : [];

  const hasData = !!(byV && Object.keys(byV).length > 0);
  const showComSelector = ['rpc','money','ytd'].includes(tab);

  return (
    <div className="scrollbar" style={{height:'100%',overflowY:'auto',background:'var(--dgd-bg-app,#f8fafc)'}}>
      <div style={{padding:'18px 24px 80px'}}>

        {/* Header */}
        <div style={{display:'flex',alignItems:'flex-start',gap:12,marginBottom:16,flexWrap:'wrap'}}>
          <div style={{flex:1}}>
            <div style={{fontSize:10,color:'var(--dgd-fg-3)',fontFamily:'var(--dgd-font-mono,monospace)',letterSpacing:'0.1em',marginBottom:2}}>COMERCIAL · RPC CONSUMÍVEIS · {ano}</div>
            <h2 className="font-display" style={{margin:0,fontSize:21,fontWeight:600,letterSpacing:'-0.01em',color:'var(--dgd-fg-1)'}}>
              RPC Consumíveis
              {hasData
                ? <span style={{fontSize:11,fontWeight:400,color:'#166534',marginLeft:10,fontFamily:'var(--dgd-font-mono,monospace)',background:'color-mix(in oklch,#22c55e 10%,transparent)',padding:'2px 7px',borderRadius:4,border:'1px solid color-mix(in oklch,#22c55e 35%,transparent)'}}>REAL</span>
                : <span style={{fontSize:11,fontWeight:400,color:'#b45309',marginLeft:10,fontFamily:'var(--dgd-font-mono,monospace)',background:'color-mix(in oklch,#f59e0b 10%,transparent)',padding:'2px 7px',borderRadius:4,border:'1px solid color-mix(in oklch,#f59e0b 35%,transparent)'}}>A CARREGAR</span>
              }
            </h2>
          </div>
          {/* Ano segmented control */}
          <div style={{display:'flex',border:'1px solid var(--dgd-border-1)',borderRadius:7,overflow:'hidden',background:'var(--dgd-bg-card,white)'}}>
            {[2025,2026].map(y => (
              <button key={y} onClick={() => setAno(y)}
                style={{padding:'4px 11px',fontSize:12,fontWeight:y===ano?700:400,background:y===ano?'var(--dgd-fg-1)':'transparent',color:y===ano?'white':'var(--dgd-fg-2)',border:'none',cursor:'pointer'}}>
                {y}
              </button>
            ))}
          </div>
        </div>

        {/* Marca pills */}
        <div style={{display:'flex',gap:6,marginBottom:8,flexWrap:'wrap',alignItems:'center'}}>
          <span style={{fontSize:10,color:'var(--dgd-fg-3)',fontFamily:'var(--dgd-font-mono,monospace)',letterSpacing:'0.08em',minWidth:52}}>MARCA</span>
          {CONS_MARCAS.map(m => (
            <button key={m} onClick={() => setMarca(m)}
              style={{padding:'4px 10px',borderRadius:6,fontSize:11,fontWeight:marca===m?700:400,background:marca===m?'var(--dgd-fg-1)':'var(--dgd-bg-card,white)',color:marca===m?'white':'var(--dgd-fg-2)',border:'1px solid '+(marca===m?'var(--dgd-fg-1)':'var(--dgd-border-1)'),cursor:'pointer'}}>
              {m}
            </button>
          ))}
          {comsLoading && <span style={{fontSize:11,color:'var(--dgd-fg-3)'}}>A carregar...</span>}
        </div>

        {/* Equipa pills */}
        {showComSelector && equipas.length > 1 && (
          <div style={{display:'flex',gap:6,marginBottom:8,flexWrap:'wrap',alignItems:'center'}}>
            <span style={{fontSize:10,color:'var(--dgd-fg-3)',fontFamily:'var(--dgd-font-mono,monospace)',letterSpacing:'0.08em',minWidth:52}}>EQUIPA</span>
            <button onClick={() => { setSelEquipa(null); setSelComId(null); }}
              style={{padding:'4px 10px',borderRadius:6,fontSize:11,fontWeight:selEquipa===null?700:400,background:selEquipa===null?'#6366f1':'var(--dgd-bg-card,white)',color:selEquipa===null?'white':'var(--dgd-fg-2)',border:'1px solid '+(selEquipa===null?'#6366f1':'var(--dgd-border-1)'),cursor:'pointer'}}>
              Todas
            </button>
            {equipas.map(eq => (
              <button key={eq} onClick={() => { setSelEquipa(eq); setSelComId(null); }}
                style={{padding:'4px 10px',borderRadius:6,fontSize:11,fontWeight:selEquipa===eq?700:400,background:selEquipa===eq?'#6366f1':'var(--dgd-bg-card,white)',color:selEquipa===eq?'white':'var(--dgd-fg-2)',border:'1px solid '+(selEquipa===eq?'#6366f1':'var(--dgd-border-1)'),cursor:'pointer'}}>
                {eq}
              </button>
            ))}
          </div>
        )}

        {/* Comercial pills (só nos tabs que precisam) */}
        {showComSelector && comerciaisFiltrados.length > 0 && (
          <div style={{display:'flex',gap:6,marginBottom:14,flexWrap:'wrap',alignItems:'center'}}>
            <span style={{fontSize:10,color:'var(--dgd-fg-3)',fontFamily:'var(--dgd-font-mono,monospace)',letterSpacing:'0.08em',minWidth:52}}>COMERCIAL</span>
            <button onClick={() => setSelComId(null)}
              style={{padding:'4px 10px',borderRadius:6,fontSize:11,fontWeight:isAll?700:400,background:isAll?'var(--dgd-fg-1)':'var(--dgd-bg-card,white)',color:isAll?'white':'var(--dgd-fg-2)',border:'1px solid '+(isAll?'var(--dgd-fg-1)':'var(--dgd-border-1)'),cursor:'pointer'}}>
              Todos
            </button>
            {comerciaisFiltrados.map(c => (
              <button key={c.id} onClick={() => setSelComId(c.id)}
                style={{padding:'4px 10px',borderRadius:6,fontSize:11,fontWeight:selCom?.id===c.id?700:400,background:selCom?.id===c.id?'var(--dgd-fg-1)':'var(--dgd-bg-card,white)',color:selCom?.id===c.id?'white':'var(--dgd-fg-2)',border:'1px solid '+(selCom?.id===c.id?'var(--dgd-fg-1)':'var(--dgd-border-1)'),cursor:'pointer'}}>
                {c.nome}
              </button>
            ))}
          </div>
        )}

        {/* Sem comerciais */}
        {!comsLoading && comerciaisFiltrados.length === 0 && comerciais && comerciais.length > 0 && selEquipa && (
          <div style={{marginBottom:12,padding:'8px 12px',borderRadius:6,background:'color-mix(in oklch,#6366f1 8%,transparent)',border:'1px solid color-mix(in oklch,#6366f1 25%,transparent)',fontSize:11,color:'#3730a3'}}>
            Sem comerciais na equipa <strong>{selEquipa}</strong>.
          </div>
        )}
        {!comsLoading && comerciais && comerciais.length === 0 && (
          <div style={{marginBottom:12,padding:'8px 12px',borderRadius:6,background:'color-mix(in oklch,#f59e0b 8%,transparent)',border:'1px solid color-mix(in oklch,#f59e0b 25%,transparent)',fontSize:11,color:'#92400e'}}>
            Sem comerciais para <strong>{marca}</strong> (empresa: {empresa}). Verificar <code>colaborador_erp_ids</code>.
          </div>
        )}

        {/* KPIs rápidos */}
        {kpis.length > 0 && (
          <div style={{display:'flex',gap:8,marginBottom:14,flexWrap:'wrap'}}>
            {kpis.map(k => (
              <div key={k.l} style={{flex:1,minWidth:120,padding:'10px 18px',borderRadius:8,background:'var(--dgd-bg-card,white)',border:'1px solid var(--dgd-border-1)'}}>
                <div style={{fontSize:10,color:'var(--dgd-fg-3)',fontFamily:'var(--dgd-font-mono,monospace)',letterSpacing:'0.08em',marginBottom:3}}>{k.l}</div>
                <div className="font-display" style={{fontSize:22,fontWeight:700,color:k.c,letterSpacing:'-0.02em',lineHeight:1.1}}>{k.v}</div>
                <div style={{fontSize:10.5,color:'var(--dgd-fg-3)',marginTop:4}}>{k.d}</div>
              </div>
            ))}
          </div>
        )}

        {/* Sub-tabs */}
        <div style={{display:'flex',gap:2,marginBottom:16,borderBottom:'1px solid var(--dgd-border-1)',flexWrap:'wrap'}}>
          {CONS_TABS.map(t => (
            <button key={t.id} onClick={() => setTab(t.id)}
              style={{padding:'7px 14px',fontSize:12,fontWeight:tab===t.id?700:400,color:tab===t.id?'var(--dgd-fg-1)':'var(--dgd-fg-3)',background:'none',border:'none',borderBottom:tab===t.id?'2px solid var(--dgd-fg-1)':'2px solid transparent',cursor:'pointer',marginBottom:-1,whiteSpace:'nowrap'}}>
              {t.label}
            </button>
          ))}
        </div>

        {/* Loading */}
        {(comsLoading || ytdLoading || fmLoading) && (
          <div style={{padding:'20px 0',color:'var(--dgd-fg-3)',fontSize:12}}>A carregar dados {empresa}{fmLoading?' (FM pipeline)':''}...</div>
        )}

        {/* Tab content */}
        {!comsLoading && !ytdLoading && (
          <>
            {tab==='rpc' && comerciaisFiltrados.length > 0 && <TabMapaRPCCons com={selCom} ytdMensal={ytdMensal} bpMensal={bpMensal} margemMensal={margemMensal} onBpSave={handleBpSave} fmData={fmData} carteiraByV={carteiraByV} comerciaisFiltrados={comerciaisFiltrados} />}
            {tab==='rpc' && comerciaisFiltrados.length === 0 && <div style={{padding:'40px',textAlign:'center',color:'var(--dgd-fg-3)',fontSize:13}}>Seleciona uma marca para ver o Mapa RPC.</div>}
            {tab==='money' && <TabShowMoneyCons comerciais={comerciaisFiltrados} byV={byV||{}} margemByV={margemByV||{}} ano={ano} />}
            {tab==='ytd'   && <TabYTDFamiliasCons ytdRows={ytdRows||[]} selCom={selCom} vendedoresFiltrados={vendedoresFiltrados} />}
            {['forecast','predit','mediasales','sugestoes','previsao'].includes(tab) && <TabConsPlaceholder tab={tab} />}
          </>
        )}

        {/* Nota dados */}
        {hasData
          ? <div style={{marginTop:24,padding:'10px 14px',borderRadius:7,background:'color-mix(in oklch,#22c55e 7%,transparent)',border:'1px solid color-mix(in oklch,#22c55e 25%,transparent)',fontSize:11,color:'#166534'}}>
              <strong>Dados em tempo real</strong> — Vendas Fact. + Margem: Primavera (comercial_ytd_snap) · BP: portal (comercial_bp_targets) · Comerciais: PostgreSQL (colaborador_erp_ids). Blocos A/B/C: Fase 2.
            </div>
          : <div style={{marginTop:24,padding:'10px 14px',borderRadius:7,background:'color-mix(in oklch,#f59e0b 7%,transparent)',border:'1px solid color-mix(in oklch,#f59e0b 25%,transparent)',fontSize:11,color:'#92400e'}}>
              <strong>A carregar dados...</strong> — Primavera sync.
            </div>
        }
      </div>
    </div>
  );
};

window.ScreenComercialRPCConsumiveis = ScreenComercialRPCConsumiveis;
