/* Report Mimaki — pipeline das equipas Mimaki (PT, GC, ES, PA, AD) na linguagem de ranks da Mimaki EMEA.
   Dados do módulo Comercial (Gestor, snapshot Primavera, BP, F9, encomendas RPC). Corpo em inglês para a Mimaki.
   Expõe window.MktResultadosMimakiScreen. */

const MKR_API = '/api/marketing/resultados/v2/mimaki';
const MKR_FILTROS_KEY = 'mkt-mimaki-filtros';
const MKR_TAB_KEY = 'mkt-mimaki-tab';
const MKR_TABS = [
  { id: 'overview', label: 'Overview' },
  { id: 'teams', label: 'Teams' },
  { id: 'segments', label: 'Segments' },
  { id: 'contact', label: 'Mimaki Contact' },
  { id: 'ops', label: 'Opportunities' },
  { id: 'map', label: 'Ranking map' },
];
const MKR_RANK_COR = { D: '#94A3B8', C: '#F59E0B', B: '#3859D0', A: '#7B61FF', S: '#16A34A', Lost: '#DC2626' };
const MKR_MESES = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];

function mkrEur(v) {
  if (v == null || !Number.isFinite(Number(v))) return '—';
  const n = Number(v);
  if (Math.abs(n) >= 1e6) return `€${(n / 1e6).toFixed(2)}M`;
  if (Math.abs(n) >= 1e4) return `€${Math.round(n / 1000).toLocaleString('en-GB')}k`;
  return `€${Math.round(n).toLocaleString('en-GB')}`;
}
function mkrN(v) {
  if (v == null || !Number.isFinite(Number(v))) return '—';
  return Math.round(Number(v)).toLocaleString('en-GB');
}
function mkrPct(v) {
  return v == null || !Number.isFinite(Number(v)) ? '—' : `${Number(v).toFixed(1)}%`;
}
function mkrQs(extra = {}) {
  const p = new URLSearchParams();
  const email = window.currentUser?.email;
  if (email) p.set('email', email);
  Object.entries(extra).forEach(([k, v]) => { if (v != null && v !== '') p.set(k, v); });
  return p.toString();
}
function mkrEquipaCurta(t) {
  return String(t || '').replace('MIMAKI ', '').replace('DECAL ', 'Decal ');
}
function mkrMesLabel(chave) {
  const [y, m] = String(chave).split('-').map(Number);
  return `${MKR_MESES[m - 1]} ${String(y).slice(2)}`;
}

const MkrTh = ({ children, right, style, colSpan }) => (
  <th colSpan={colSpan} style={{ textAlign: right ? 'right' : 'left', padding: '8px 10px', fontSize: 10, fontWeight: 600, color: 'var(--text-dim)', fontFamily: 'var(--font-mono)', letterSpacing: '.04em', textTransform: 'uppercase', whiteSpace: 'nowrap', borderBottom: '1px solid var(--border)', background: 'var(--bg-elev)', position: 'sticky', top: 0, zIndex: 1, ...style }}>{children}</th>
);
const MkrTd = ({ children, right, mono, muted, style, title, rowSpan }) => (
  <td title={title} rowSpan={rowSpan} style={{ textAlign: right ? 'right' : 'left', padding: '7px 10px', fontSize: 12, borderTop: '1px solid var(--border)', whiteSpace: 'nowrap', color: muted ? 'var(--text-muted)' : 'var(--text)', fontFamily: mono ? 'var(--font-mono)' : undefined, fontVariantNumeric: 'tabular-nums', ...style }}>{children}</td>
);
const MkrTabela = ({ children, maxHeight }) => (
  <div className="scrollbar" style={{ overflow: 'auto', maxHeight, border: '1px solid var(--border)', borderRadius: 10, background: 'var(--bg-elev)' }}>
    <table style={{ width: '100%', borderCollapse: 'collapse' }}>{children}</table>
  </div>
);
const MkrRank = ({ rank, sub }) => (
  <span style={{ display: 'inline-flex', alignItems: 'center', justifyContent: 'center', minWidth: 26, height: 20, padding: '0 6px', borderRadius: 5, fontSize: 11, fontWeight: 700, color: '#fff', background: MKR_RANK_COR[rank] || 'var(--text-dim)', fontFamily: 'var(--font-mono)' }}>{sub || rank}</span>
);
const MkrBarra = ({ valor, max, cor, alvo, altura = 8 }) => {
  const m = Math.max(Number(max) || 0, Number(valor) || 0, Number(alvo) || 0, 0.0001);
  return (
    <div style={{ position: 'relative', height: altura, background: 'var(--bg-sunken, #e8edf5)', borderRadius: altura / 2 }}>
      <div style={{ position: 'absolute', left: 0, top: 0, bottom: 0, width: `${Math.min(100, (Number(valor) || 0) / m * 100)}%`, background: cor, borderRadius: altura / 2, transition: 'width .4s' }} />
      {alvo != null && <div title="Target" style={{ position: 'absolute', left: `${Math.min(100, alvo / m * 100)}%`, top: -3, bottom: -3, width: 2, background: 'var(--text)' }} />}
    </div>
  );
};

// ── Overview ────────────────────────────────────────────────────────────────
const MkrCelula = ({ n, valor, forte }) => (
  n ? (
    <span style={{ display: 'inline-flex', flexDirection: 'column', alignItems: 'flex-end', lineHeight: 1.25 }}>
      <span style={{ fontWeight: forte ? 700 : 600 }}>{mkrN(n)}</span>
      <span style={{ fontSize: 10.5, color: 'var(--text-muted)' }}>{mkrEur(valor)}</span>
    </span>
  ) : <span style={{ color: 'var(--text-dim)' }}>—</span>
);

const MkrPipeline = ({ data, isMobile }) => {
  const p = data.pipeline;
  const a = data.alvos;
  const maxValor = Math.max(...p.escada.map(e => e.valor), 1);
  const alvoC = a.bp_restante != null ? a.bp_restante * a.cobertura_c : null;
  const okC = alvoC != null && a.pipeline_c_eur >= alvoC;
  const cols = ['D', 'C', 'B', 'A'];
  const linhaTotal = { segmento: 'Total', ...p };
  return (
    <AnCartao titulo="Open pipeline · Mimaki ranking" sub={`All open Mimaki opportunities today in Mimaki CRM ranks · ${mkrN(p.n)} opportunities · ${mkrN(p.pt)} Portugal · ${mkrN(p.es)} Spain · ${mkrN(p.direct)} Direct · ${mkrN(p.dealer)} Dealer`}
      direita={
        <span style={{ textAlign: 'right', whiteSpace: 'nowrap' }}>
          <span className="font-display" style={{ fontSize: 18, fontWeight: 700 }}>{mkrEur(p.valor)}</span>
          <div style={{ fontSize: 11, color: 'var(--text-muted)' }}>weighted {mkrEur(p.ponderado)}</div>
        </span>
      }>
      <div style={{ display: 'grid', gridTemplateColumns: isMobile ? 'repeat(3, 1fr)' : 'repeat(6, 1fr)', gap: 8 }}>
        {p.escada.map(e => (
          <div key={e.key} style={{ border: '1px solid var(--border)', borderTop: `3px solid ${MKR_RANK_COR[e.rank]}`, borderRadius: 8, padding: '10px 10px 9px', opacity: e.n ? 1 : 0.5 }}>
            <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
              <MkrRank rank={e.rank} sub={e.key} />
              <span style={{ fontSize: 10.5, color: 'var(--text-muted)', fontFamily: 'var(--font-mono)' }}>{Math.round(e.prob * 100)}%</span>
            </div>
            <div className="font-display" style={{ fontSize: 20, fontWeight: 700, marginTop: 6 }}>{mkrN(e.n)}</div>
            <div style={{ fontSize: 11.5, fontWeight: 600, fontVariantNumeric: 'tabular-nums' }}>{mkrEur(e.valor)}</div>
            <div style={{ fontSize: 10.5, color: 'var(--text-muted)', marginBottom: 6 }}>weighted {mkrEur(e.ponderado)}</div>
            <MkrBarra valor={e.valor} max={maxValor} cor={MKR_RANK_COR[e.rank]} altura={5} />
          </div>
        ))}
      </div>
      <div style={{ display: 'flex', height: 10, borderRadius: 5, overflow: 'hidden', margin: '12px 0 4px', background: 'var(--bg-sunken, #e8edf5)' }}>
        {p.escada.filter(e => e.valor > 0).map(e => (
          <div key={e.key} title={`${e.key} · ${mkrEur(e.valor)}`} style={{ width: `${e.valor / Math.max(p.valor, 1) * 100}%`, background: MKR_RANK_COR[e.rank] }} />
        ))}
      </div>
      <div style={{ fontSize: 10.5, color: 'var(--text-dim)', marginBottom: 12 }}>Share of open value by rank. C1 = Gestor stage 50 (event) at a company with a demo · C2 = Gestor stage 65 (demo) · C3 (demo completed) has no Gestor stage yet.</div>

      <div style={{ display: 'flex', alignItems: 'center', gap: 12, flexWrap: 'wrap', padding: '10px 12px', borderRadius: 8, marginBottom: 12, background: `color-mix(in oklch, ${okC ? 'var(--success, #16a34a)' : 'var(--danger, #dc2626)'} 7%, transparent)` }}>
        <span style={{ fontSize: 10, fontWeight: 700, letterSpacing: '.06em', textTransform: 'uppercase', fontFamily: 'var(--font-mono)', color: okC ? 'var(--success, #16a34a)' : 'var(--danger, #dc2626)' }}>5× rule</span>
        <span style={{ fontSize: 12 }}>
          Rank C pipeline <b>{mkrEur(a.pipeline_c_eur)}</b>
          {a.bp_restante != null
            ? <> = <b>{a.cobertura_c_real != null ? `${a.cobertura_c_real}×` : '—'}</b> the remaining budget {mkrEur(a.bp_restante)} ({a.bp_restante_equipas.join(', ')}) · target {mkrEur(alvoC)} ({a.cobertura_c}×) · expected at 20% {mkrEur(a.pipeline_c_eur * a.conv_c)}</>
            : <> · no BP in comercial_bp_targets for this selection</>}
        </span>
        <span style={{ fontSize: 12, marginLeft: isMobile ? 0 : 'auto' }}>A–C expected at 30%: <b>{mkrEur(a.esperado_ac_eur)}</b></span>
      </div>

      <MkrTabela>
        <thead>
          <tr>
            <MkrTh>Segment</MkrTh>
            {cols.map(k => <MkrTh key={k} right><MkrRank rank={k} /></MkrTh>)}
            <MkrTh right>Open</MkrTh><MkrTh right>Weighted</MkrTh>
            <MkrTh right>Portugal</MkrTh><MkrTh right>Spain</MkrTh><MkrTh right>Direct</MkrTh><MkrTh right>Dealer</MkrTh>
          </tr>
        </thead>
        <tbody>
          {[...p.segmentos, linhaTotal].map(s => {
            const total = s.segmento === 'Total';
            const st = total ? { fontWeight: 700, background: 'var(--bg-sunken, #f1f5f9)' } : undefined;
            return (
              <tr key={s.segmento}>
                <MkrTd style={{ fontWeight: total ? 700 : 600, ...st }}>{s.segmento}</MkrTd>
                {cols.map(k => <MkrTd key={k} right style={st}><MkrCelula n={s.por_rank[k].n} valor={s.por_rank[k].valor} forte={total} /></MkrTd>)}
                <MkrTd right style={st}><MkrCelula n={s.n} valor={s.valor} forte /></MkrTd>
                <MkrTd right style={{ fontWeight: 600, ...st }}>{mkrEur(s.ponderado)}</MkrTd>
                <MkrTd right muted style={st}>{mkrN(s.pt)}</MkrTd><MkrTd right muted style={st}>{mkrN(s.es)}</MkrTd>
                <MkrTd right muted style={st}>{mkrN(s.direct)}</MkrTd><MkrTd right muted style={st}>{mkrN(s.dealer)}</MkrTd>
              </tr>
            );
          })}
        </tbody>
      </MkrTabela>
      <div style={{ fontSize: 10.5, color: 'var(--text-dim)', marginTop: 8, lineHeight: 1.5 }}>
        Weighted = value × Mimaki rank probability (D 30%, C1 50%, C2 60%, C3 70%, B 80%, A 90%). Dealer = Mimaki PA team.
        {p.antigas.n > 0 && ` ${mkrN(p.antigas.n)} open opportunities (${mkrEur(p.antigas.valor)}) started more than 12 months ago.`}
        {p.sem_valor > 0 && ` ${mkrN(p.sem_valor)} have no value in the Gestor.`}
      </div>
    </AnCartao>
  );
};

const MKR_FUNIL_CORES = ['#64748B', '#F59E0B', '#3859D0', '#7B61FF', '#16A34A'];
function mkrPctFunil(part, whole) {
  const p = whole ? (Number(part) || 0) / whole * 100 : 0;
  return p > 0 && p < 10 ? `${p.toFixed(1)}%` : `${Math.round(p)}%`;
}

const MkrFunilMimaki = ({ data, isMobile }) => {
  const Corpo = window.ResFunilCorpo;
  const f = data.funil_mimaki || [];
  if (!Corpo || f.length < 2 || typeof resFunilPath !== 'function') return null;
  const r = data.resumo;
  const a = data.alvos;
  const d = f[0];
  const c = f[1];
  const s = f[f.length - 1];
  const taxa = d.real ? s.real / d.real * 100 : 0;
  const convC = c.real ? s.real / c.real * 100 : null;
  const okC = convC != null && convC >= a.conv_c * 100;
  const etapas = f.map(e => ({ key: e.key, label: `${e.key} · ${e.label.replace(/^\w+ · /, '')}`, real: e.real, valor: e.valor, _e: e }));
  const chip = (cor, titulo, valor, nota) => (
    <div style={{ border: '1px solid var(--border)', borderLeft: `3px solid ${cor}`, borderRadius: 8, padding: '7px 10px', minWidth: 170 }}>
      <div style={{ fontSize: 9.5, fontWeight: 600, letterSpacing: '.05em', textTransform: 'uppercase', color: 'var(--text-dim)', fontFamily: 'var(--font-mono)' }}>{titulo}</div>
      <div style={{ fontSize: 15, fontWeight: 700, color: cor, fontVariantNumeric: 'tabular-nums', marginTop: 1 }}>{valor}</div>
      <div style={{ fontSize: 10.5, color: 'var(--text-muted)' }}>{nota}</div>
    </div>
  );
  return (
    <div className="card an-card" style={{ padding: '18px 18px 10px', borderRadius: 10, border: '1px solid var(--border)', background: 'var(--bg-elev)' }}>
      <div style={{ display: 'flex', justifyContent: 'space-between', gap: 16, alignItems: 'flex-end', flexWrap: 'wrap' }}>
        <div>
          <div style={{ fontSize: 10, fontWeight: 600, letterSpacing: '.06em', textTransform: 'uppercase', color: 'var(--text-dim)', fontFamily: 'var(--font-mono)', marginBottom: 6 }}>Mimaki leads ranking funnel</div>
          <div className="font-display" style={{ fontSize: 40, fontWeight: 650, letterSpacing: '-0.03em', lineHeight: 1, fontVariantNumeric: 'tabular-nums' }}>{mkrPctFunil(s.real, d.real)}</div>
          <div style={{ fontSize: 13, color: 'var(--text-muted)', marginTop: 6 }}>
            {mkrN(s.real)} sold of {mkrN(d.real)} opportunities started · {mkrEur(s.valor)} · {data.periodo.label} · {mkrN(r.open)} open · {mkrN(r.lost)} lost
          </div>
        </div>
        <div style={{ display: 'flex', gap: 8, flexWrap: 'wrap' }}>
          {chip(okC ? 'var(--success, #16a34a)' : 'var(--danger, #dc2626)', 'Rank C → Sold', convC == null ? '—' : mkrPct(convC), `Mimaki target ${Math.round(a.conv_c * 100)}%`)}
          {chip('var(--ai-500, #3859D0)', 'Open A–C × 30%', mkrEur(a.esperado_ac_eur), `${mkrN(a.pipeline_ac_n)} open · ${mkrEur(a.pipeline_ac_eur)}`)}
          {chip(a.cobertura_c_real != null && a.cobertura_c_real >= a.cobertura_c ? 'var(--success, #16a34a)' : 'var(--danger, #dc2626)', 'C pipeline vs budget', a.cobertura_c_real != null ? `${a.cobertura_c_real}×` : '—', `Mimaki rule ${a.cobertura_c}×`)}
        </div>
      </div>
      <Corpo etapas={etapas} H={isMobile ? 150 : 190} gid="mkr-funil" cores={MKR_FUNIL_CORES} pathFn={resFunilPath} pctFn={mkrPctFunil}
        ariaLabel="Mimaki leads ranking funnel" topSvg={62}
        rotulos={{ total: 'Reached', anterior: 'From previous rank', primeira: 'From D', valor: 'Value', fmtValor: mkrEur }}
        extraCelula={(e) => (
          <div style={{ fontSize: 10.5, color: 'var(--text-muted)', lineHeight: 1.35, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>
            <div style={{ overflow: 'hidden', textOverflow: 'ellipsis' }} title={`Value reached ${mkrEur(e.valor)} · Mimaki probability ${e._e.prob}`}>{mkrEur(e.valor)} · {e._e.prob}</div>
            <div style={{ overflow: 'hidden', textOverflow: 'ellipsis' }}>{e.key === 'S' ? `${mkrN(e._e.aqui_n)} sold` : `${mkrN(e._e.aqui_n)} open · ${mkrEur(e._e.aqui_valor)}`}</div>
          </div>
        )} />
      <div style={{ fontSize: 10.5, color: 'var(--text-dim)', margin: '4px 0 6px', lineHeight: 1.5 }}>
        Each rank counts the opportunities started in the period that are at that rank or higher today. Lost opportunities count in D only, because the Gestor does not keep the highest stage they reached, so conversion from C onwards reads high.
      </div>
    </div>
  );
};

const MkrAlvos = ({ data }) => {
  const a = data.alvos;
  const alvoC = a.bp_restante != null ? a.bp_restante * a.cobertura_c : null;
  const linhas = [
    {
      titulo: 'Rank C pipeline coverage', valor: a.pipeline_c_eur, alvo: alvoC, fmt: mkrEur,
      texto: a.bp_restante != null
        ? `${mkrN(a.pipeline_c_n)} open C opportunities = ${a.cobertura_c_real != null ? `${a.cobertura_c_real}×` : '—'} the remaining budget of ${mkrEur(a.bp_restante)} (${a.bp_restante_equipas.join(', ')}). Target ${a.cobertura_c}× with 20% conversion.`
        : `${mkrN(a.pipeline_c_n)} open C opportunities. The selected teams have no BP in comercial_bp_targets.`,
    },
    {
      titulo: 'Expected from ranks A–C (30%)', valor: a.esperado_ac_eur, alvo: a.bp_restante, fmt: mkrEur,
      texto: `${mkrN(a.pipeline_ac_n)} open A–C opportunities worth ${mkrEur(a.pipeline_ac_eur)} × 30% conversion${a.bp_restante != null ? `, against ${mkrEur(a.bp_restante)} still to sell this year` : ''}.`,
    },
    {
      titulo: 'Observed win rate', valor: a.conv_observada, alvo: a.conv_c * 100, fmt: mkrPct,
      texto: `Won ÷ (won + lost) for opportunities created in the period. ${mkrPct(a.conv_d_para_c)} of them reached rank C or above.`,
    },
  ];
  return (
    <AnCartao titulo="Mimaki Q Review targets" sub="20% conversion from rank C · 30% from ranks A–C">
      <div style={{ display: 'grid', gap: 14 }}>
        {linhas.map(l => {
          const ok = l.alvo != null && l.valor >= l.alvo;
          const cor = l.alvo == null ? 'var(--ai-500, #3859D0)' : ok ? 'var(--success, #16a34a)' : l.valor >= l.alvo * 0.7 ? 'var(--warning, #f59e0b)' : 'var(--danger, #dc2626)';
          return (
            <div key={l.titulo}>
              <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline', gap: 8, marginBottom: 5 }}>
                <span style={{ fontSize: 12, fontWeight: 600 }}>{l.titulo}</span>
                <span style={{ fontSize: 12.5, fontWeight: 700, color: cor, fontVariantNumeric: 'tabular-nums' }}>{l.fmt(l.valor)}{l.alvo != null && <span style={{ color: 'var(--text-muted)', fontWeight: 500 }}> / {l.fmt(l.alvo)}</span>}</span>
              </div>
              <MkrBarra valor={l.valor} max={l.alvo != null ? l.alvo * 1.2 : l.valor} alvo={l.alvo} cor={cor} />
              <div style={{ fontSize: 11, color: 'var(--text-muted)', marginTop: 5, lineHeight: 1.45 }}>{l.texto}</div>
            </div>
          );
        })}
      </div>
    </AnCartao>
  );
};

const MkrMeses = ({ meses }) => {
  const [over, setOver] = React.useState(null);
  const max = Math.max(...meses.map(m => Math.max(m.vendas, m.bp)), 1);
  const maxN = Math.max(...meses.map(m => m.novas), 1);
  const h = 140;
  return (
    <AnCartao titulo="Monthly trend" sub="Sales vs BP (bars) · new opportunities and wins per month"
      direita={<span style={{ display: 'flex', gap: 12, fontSize: 10.5, color: 'var(--text-muted)' }}>
        <span><span style={{ display: 'inline-block', width: 9, height: 9, borderRadius: 2, background: 'var(--ai-500, #3859D0)', marginRight: 4 }} />Sales</span>
        <span><span style={{ display: 'inline-block', width: 9, height: 9, borderRadius: 2, border: '1.5px solid var(--text)', marginRight: 4 }} />BP</span>
      </span>}>
      <div style={{ display: 'grid', gridTemplateColumns: `repeat(${meses.length}, 1fr)`, gap: 6, alignItems: 'end' }}>
        {meses.map(m => (
          <div key={m.mes} onMouseEnter={() => setOver(m.mes)} onMouseLeave={() => setOver(null)} style={{ display: 'flex', flexDirection: 'column', alignItems: 'stretch', gap: 4, cursor: 'default' }}>
            <div style={{ position: 'relative', height: h, display: 'flex', alignItems: 'flex-end', justifyContent: 'center' }}>
              {over === m.mes && (
                <div style={{ position: 'absolute', bottom: h + 4, left: '50%', transform: 'translateX(-50%)', background: 'var(--bg-elev)', border: '1px solid var(--border)', borderRadius: 6, padding: '6px 8px', fontSize: 11, whiteSpace: 'nowrap', boxShadow: '0 4px 14px rgba(0,0,0,.12)', zIndex: 2 }}>
                  <div style={{ fontWeight: 650 }}>{mkrMesLabel(m.mes)}</div>
                  <div>Sales {mkrEur(m.vendas)} · BP {mkrEur(m.bp)}</div>
                  <div>{m.novas} new · {m.won} won ({mkrEur(m.won_eur)})</div>
                </div>
              )}
              <div style={{ width: '62%', height: `${m.vendas / max * 100}%`, background: 'var(--ai-500, #3859D0)', borderRadius: '3px 3px 0 0', opacity: over && over !== m.mes ? 0.5 : 1 }} />
              {m.bp > 0 && <div style={{ position: 'absolute', left: '12%', right: '12%', bottom: `${m.bp / max * 100}%`, height: 0, borderTop: '2px solid var(--text)' }} />}
            </div>
            <div style={{ height: 4, background: 'var(--bg-sunken, #e8edf5)', borderRadius: 2 }}>
              <div style={{ width: `${m.novas / maxN * 100}%`, height: '100%', background: '#94A3B8', borderRadius: 2 }} />
            </div>
            <div style={{ textAlign: 'center', fontSize: 10, color: 'var(--text-muted)', fontFamily: 'var(--font-mono)' }}>{mkrMesLabel(m.mes)}</div>
            <div style={{ textAlign: 'center', fontSize: 10, color: 'var(--text-dim)' }}>{m.novas}/{m.won}</div>
          </div>
        ))}
      </div>
      <div style={{ fontSize: 10.5, color: 'var(--text-dim)', marginTop: 8 }}>Grey bar and numbers under each month: new opportunities / wins. BP exists for Mimaki PT and GC only. The current month is still open.</div>
    </AnCartao>
  );
};

const MkrQReview = ({ itens }) => (
  <AnCartao titulo="Q Review action tracker" sub="Gestor Closed Won value, same period this year vs last year">
    <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(210px, 1fr))', gap: 10 }}>
      {itens.map(q => {
        const sobe = q.delta_eur >= 0;
        return (
          <div key={q.id} style={{ border: '1px solid var(--border)', borderRadius: 8, padding: 12 }}>
            <div style={{ fontSize: 12, fontWeight: 650, lineHeight: 1.35 }}>{q.titulo}</div>
            <div style={{ fontSize: 10.5, color: 'var(--text-muted)', marginTop: 2 }}>Target: {q.alvo}</div>
            <div style={{ display: 'flex', alignItems: 'baseline', gap: 8, marginTop: 8 }}>
              <span className="font-display" style={{ fontSize: 19, fontWeight: 700 }}>{mkrEur(q.won_eur)}</span>
              <span style={{ fontSize: 11.5, fontWeight: 650, color: sobe ? 'var(--success, #16a34a)' : 'var(--danger, #dc2626)' }}>{sobe ? '+' : '−'}{mkrEur(Math.abs(q.delta_eur))}</span>
            </div>
            <div style={{ fontSize: 11, color: 'var(--text-muted)', marginTop: 3 }}>{q.won_n} won vs {q.won_ant_n} last year ({mkrEur(q.won_ant_eur)})</div>
            <div style={{ fontSize: 11, color: 'var(--text-muted)' }}>{q.open_n} open · {mkrEur(q.open_eur)}{q.direct_share != null ? ` · ${mkrPct(q.direct_share)} direct` : ''}</div>
          </div>
        );
      })}
    </div>
  </AnCartao>
);

const MkrOrigens = ({ data, onPick, isMobile }) => {
  const os = (data.origens || []).filter(o => o.n || o.aberto.n || o.won_periodo_n);
  const total = os.reduce((s, o) => s + o.n, 0);
  const novas = os.flatMap(o => o.detalhe.filter(d => d.nova).map(d => ({ det: d.det, origem: o.label })));
  if (!os.length) return null;
  return (
    <AnCartao titulo="Generated by · who brings the Mimaki opportunities"
      sub={`Opportunities started in ${data.periodo.label} by origin · marketing sources, exhibitions and open houses, Mimaki Contact, Decal sales PT & ES, technical assistance, partners and Mimaki sales. Click a row to filter the report.`}>
      <div style={{ display: 'flex', gap: 2, height: 12, borderRadius: 5, overflow: 'hidden', marginBottom: 6 }}>
        {os.filter(o => o.n > 0).map(o => (
          <div key={o.key} title={`${o.label} · ${mkrN(o.n)} (${mkrPct(o.share)})`} onClick={() => onPick(o.key)}
            style={{ width: `${o.n / Math.max(total, 1) * 100}%`, background: o.cor, cursor: 'pointer', opacity: data.origem && data.origem !== o.key ? 0.3 : 1 }} />
        ))}
      </div>
      <div style={{ display: 'flex', gap: '2px 12px', flexWrap: 'wrap', fontSize: 10.5, color: 'var(--text-muted)', marginBottom: 12 }}>
        {os.map(o => <span key={o.key}><span style={{ display: 'inline-block', width: 8, height: 8, borderRadius: 2, background: o.cor, marginRight: 4 }} />{o.label} {mkrPct(o.share)}</span>)}
      </div>
      <MkrTabela>
        <thead>
          <tr>
            <MkrTh style={{ borderBottom: 'none' }} />
            <MkrTh colSpan={6} style={{ textAlign: 'center', borderLeft: '1px solid var(--border)' }}>{`Started in ${data.periodo.label}`}</MkrTh>
            <MkrTh style={{ textAlign: 'center', borderLeft: '1px solid var(--border)' }}>Closed in period</MkrTh>
            <MkrTh colSpan={isMobile ? 2 : 3} style={{ textAlign: 'center', borderLeft: '1px solid var(--border)' }}>Open today</MkrTh>
            <MkrTh style={{ borderBottom: 'none', borderLeft: '1px solid var(--border)' }} />
          </tr>
          <tr>
            <MkrTh>Generated by</MkrTh>
            <MkrTh right style={{ borderLeft: '1px solid var(--border)' }}>Opportunities</MkrTh><MkrTh right>Same period LY</MkrTh>
            <MkrTh right>Won</MkrTh><MkrTh right>Lost</MkrTh><MkrTh right>Win rate</MkrTh><MkrTh right>At C+ or won</MkrTh>
            <MkrTh right style={{ borderLeft: '1px solid var(--border)' }}>Won (any start)</MkrTh>
            <MkrTh right style={{ borderLeft: '1px solid var(--border)' }}>Open</MkrTh><MkrTh right>Weighted</MkrTh>
            {!isMobile && <MkrTh style={{ minWidth: 130 }}>By rank</MkrTh>}
            <MkrTh style={{ borderLeft: '1px solid var(--border)' }}>Detail · started in period</MkrTh>
          </tr>
        </thead>
        <tbody>
          {os.map(o => {
            const activo = data.origem === o.key;
            const delta = o.n_ant ? (o.n - o.n_ant) / o.n_ant * 100 : null;
            const det = o.detalhe.filter(d => d.n > 0);
            const corWr = o.win_rate == null ? undefined : o.win_rate >= ALVO_WR_MKR ? 'var(--success, #16a34a)' : o.win_rate >= 14 ? 'var(--warning, #f59e0b)' : 'var(--danger, #dc2626)';
            const sep = { borderLeft: '1px solid var(--border)' };
            return (
              <tr key={o.key} className="mkr-linha" onClick={() => onPick(o.key)} title={activo ? 'Click again to show all origins' : `Filter the report by ${o.label}`}
                style={{ cursor: 'pointer', opacity: data.origem && !activo ? 0.45 : 1, background: activo ? `color-mix(in oklch, ${o.cor} 7%, transparent)` : undefined }}>
                <MkrTd style={{ fontWeight: 650 }}><span style={{ display: 'inline-block', width: 9, height: 9, borderRadius: 2, background: o.cor, marginRight: 7 }} />{o.label}</MkrTd>
                <MkrTd right style={sep}><b>{mkrN(o.n)}</b> <span style={{ fontSize: 10.5, color: 'var(--text-muted)' }}>{mkrPct(o.share)}</span></MkrTd>
                <MkrTd right muted>{mkrN(o.n_ant)}{delta != null && <span style={{ fontSize: 10.5, marginLeft: 4, color: delta >= 0 ? 'var(--success, #16a34a)' : 'var(--danger, #dc2626)' }}>{delta >= 0 ? '+' : '−'}{Math.abs(Math.round(delta))}%</span>}</MkrTd>
                <MkrTd right style={{ fontWeight: 600 }}>{mkrN(o.won)}</MkrTd>
                <MkrTd right muted>{mkrN(o.lost)}</MkrTd>
                <MkrTd right style={{ color: corWr, fontWeight: 600 }} title={`${mkrN(o.won)} won ÷ ${mkrN(o.won + o.lost)} closed · ${mkrN(o.open)} still open`}>{mkrPct(o.win_rate)}</MkrTd>
                <MkrTd right muted title={`${mkrN(o.chegou_c)} of ${mkrN(o.n)} are today at rank C or above, or won`}>{mkrPct(o.pct_c_mais)}</MkrTd>
                <MkrTd right style={sep}><MkrCelula n={o.won_periodo_n} valor={o.won_periodo_eur} forte /></MkrTd>
                <MkrTd right style={sep}><MkrCelula n={o.aberto.n} valor={o.aberto.valor} /></MkrTd>
                <MkrTd right muted>{mkrEur(o.aberto.ponderado)}</MkrTd>
                {!isMobile && (
                  <MkrTd style={{ minWidth: 130 }}>
                    <div style={{ display: 'flex', gap: 1, height: 8, borderRadius: 3, overflow: 'hidden', background: 'var(--bg-sunken, #e8edf5)' }}>
                      {['D', 'C', 'B', 'A'].filter(k => o.aberto.por_rank[k].valor > 0).map(k => (
                        <div key={k} title={`${k} · ${mkrN(o.aberto.por_rank[k].n)} · ${mkrEur(o.aberto.por_rank[k].valor)}`} style={{ width: `${o.aberto.por_rank[k].valor / Math.max(o.aberto.valor, 1) * 100}%`, background: MKR_RANK_COR[k] }} />
                      ))}
                    </div>
                  </MkrTd>
                )}
                <MkrTd muted style={{ fontSize: 11, whiteSpace: 'normal', minWidth: 210, ...sep }}>
                  {det.length > 1 || (det[0] && det[0].det !== o.label)
                    ? det.map((d, i) => (
                      <span key={d.det}>{i ? ' · ' : ''}{d.det} {mkrN(d.n)}{d.nova && <span title="No opportunities with this source in the same period last year" style={{ marginLeft: 3, fontSize: 9, fontWeight: 700, color: 'var(--ai-500, #3859D0)' }}>NEW</span>}</span>
                    ))
                    : o.por_equipa.map(e => `${e.label.replace('Mimaki ', '')} ${mkrN(e.n)}`).join(' · ')}
                </MkrTd>
              </tr>
            );
          })}
        </tbody>
      </MkrTabela>
      <div style={{ fontSize: 10.5, color: 'var(--text-dim)', marginTop: 8, lineHeight: 1.55 }}>
        <b>Started in the period</b>: opportunities by start date; win rate = won ÷ (won + lost) of those, the open ones are still pending. At C+ or won = share that is today at rank C or above or won; lost opportunities count as D because the Gestor does not keep the highest stage they reached.
        {' '}<b>Closed in the period</b>: Closed Won by close date, whatever the start date, so it can include opportunities started last year.
        {' '}The Gestor source decides the origin: Marketing = Website, Ads, Newsletter and SDR; Exhibitions &amp; Open House = trade shows and open house events; Technical assistance = DEP TECNICO and DEPART TÉCNICO; Mimaki Contact = leads passed by Mimaki; Decal sales PT &amp; ES = Mimaki machines opened by the Decal teams from their own sales contacts.
        {novas.length > 0 && <>{' '}NEW = source with no opportunities in the same period last year ({novas.map(n => n.det).join(', ')}), so the comparison with last year is partial for {[...new Set(novas.map(n => n.origem))].join(' and ')}.</>}
      </div>
    </AnCartao>
  );
};

const MkrOverview = ({ data, isMobile, mudarF }) => {
  const Kpi = window.ResKpi;
  const r = data.resumo;
  const eqs = data.equipa ? data.equipas.filter(e => e.key === data.equipa) : data.equipas;
  const vendas = eqs.reduce((s, e) => s + (Number(e.vendas) || 0), 0);
  const comBp = eqs.filter(e => e.bp_periodo != null);
  const vendasBp = comBp.reduce((s, e) => s + (Number(e.vendas) || 0), 0);
  const bp = comBp.reduce((s, e) => s + (Number(e.bp_periodo) || 0), 0);
  const pipelineAberto = data.pipeline.valor;
  return (
    <>
      <div style={{ display: 'grid', gridTemplateColumns: isMobile ? '1fr 1fr' : 'repeat(5, 1fr)', gap: 10, marginBottom: 14 }}>
        <Kpi label="Opportunities" value={mkrN(r.n)} sub={`${mkrN(r.pt)} Portugal · ${mkrN(r.es)} Spain`} />
        <Kpi label="Won" value={mkrN(r.won)} sub={`${mkrEur(r.won_eur)} · ${mkrN(r.lost)} lost`} />
        <Kpi label="Win rate" value={mkrPct(r.win_rate)} sub="Won ÷ (won + lost)" light={r.win_rate == null ? null : r.win_rate >= 20 ? 'verde' : r.win_rate >= 14 ? 'amarelo' : 'vermelho'} />
        <Kpi label="Open pipeline" value={mkrEur(pipelineAberto)} sub={`${mkrN(data.pipeline.n)} open today · weighted ${mkrEur(data.pipeline.ponderado)}`} />
        <Kpi label="Sales in period" value={mkrEur(vendas)} sub={bp > 0 ? `${mkrPct(vendasBp / bp * 100)} of BP ${mkrEur(bp)} (${comBp.map(e => e.label.replace('Mimaki ', '')).join('+')})` : 'No BP for this selection'} light={bp > 0 ? (vendasBp / bp >= 1 ? 'verde' : vendasBp / bp >= 0.85 ? 'amarelo' : 'vermelho') : null} />
      </div>
      <div style={{ marginBottom: 14 }}><MkrFunilMimaki data={data} isMobile={isMobile} /></div>
      <div style={{ marginBottom: 14 }}><MkrOrigens data={data} isMobile={isMobile} onPick={(k) => mudarF({ origem: data.origem === k ? '' : k })} /></div>
      <div style={{ marginBottom: 14 }}><MkrPipeline data={data} isMobile={isMobile} /></div>
      <div style={{ display: 'grid', gridTemplateColumns: isMobile ? '1fr' : '1fr 1.4fr', gap: 14, marginBottom: 14 }}>
        <MkrAlvos data={data} />
        <MkrQReview itens={data.qreview} />
      </div>
      <MkrMeses meses={data.meses} />
    </>
  );
};

// ── Teams ───────────────────────────────────────────────────────────────────
const MKR_MESBARS_ROTULOS = { corrente: 'current month', mensal: 'monthly', acumulado: 'cumulative', ritmo: 'At current pace', ritmoLegenda: 'current pace' };
const MKR_KPI_ROTULOS = { actual: 'Actual', obj: 'Target' };
const MKR_PAISES = ['Iberia', 'Portugal', 'Spain'];
const ALVO_WR_MKR = 20;
function mkrUn(v) {
  if (v == null || !Number.isFinite(Number(v))) return '—';
  const n = Number(v);
  return n.toLocaleString('en-GB', { maximumFractionDigits: Math.abs(n) < 100 && n % 1 ? 1 : 0 });
}
const mkrX = (v) => (v == null ? '—' : `${Number(v).toFixed(1)}×`);

const MkrToggle = ({ value, onChange, opcoes }) => (
  <span style={{ display: 'inline-flex', border: '1px solid var(--border)', borderRadius: 7, overflow: 'hidden', flexShrink: 0 }}>
    {opcoes.map(o => (
      <button key={o} type="button" onClick={() => onChange(o)}
        style={{ border: 'none', padding: '4px 10px', fontSize: 11.5, fontWeight: value === o ? 650 : 500, cursor: 'pointer', fontFamily: 'inherit', background: value === o ? 'var(--ai-500, #3859D0)' : 'transparent', color: value === o ? '#fff' : 'var(--text-muted)' }}>{o}</button>
    ))}
  </span>
);

const MkrMiniStat = ({ label, valor, sub }) => (
  <div style={{ minWidth: 0 }}>
    <div style={{ fontSize: 9.5, color: 'var(--text-dim)', textTransform: 'uppercase', letterSpacing: '.04em', fontFamily: 'var(--font-mono)' }}>{label}</div>
    <div style={{ fontSize: 13, fontWeight: 650, fontVariantNumeric: 'tabular-nums', whiteSpace: 'nowrap' }}>{valor}</div>
    {sub && <div style={{ fontSize: 10.5, color: 'var(--text-muted)', whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>{sub}</div>}
  </div>
);

const MkrCartaoEquipa = ({ e, activo, apagado, onClick }) => {
  const temBp = Number(e.bp_periodo) > 0;
  return (
    <button type="button" onClick={onClick} aria-pressed={activo} title={activo ? 'Click again to show all teams' : `Filter the report by ${e.label}`}
      style={{
        textAlign: 'left', cursor: 'pointer', font: 'inherit', color: 'inherit', background: 'var(--bg-elev)', minWidth: 0,
        border: `1px solid ${activo ? e.cor : 'var(--border)'}`, borderTop: `3px solid ${e.cor}`, borderRadius: 10, padding: '12px 12px 10px',
        opacity: apagado ? 0.5 : 1, boxShadow: activo ? `0 0 0 3px color-mix(in oklch, ${e.cor} 22%, transparent)` : 'none', transition: 'opacity .15s, box-shadow .15s',
      }}>
      <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline', gap: 6 }}>
        <span className="font-display" style={{ fontSize: 14, fontWeight: 700 }}>{e.label}</span>
        <span style={{ fontSize: 9.5, fontWeight: 600, fontFamily: 'var(--font-mono)', color: e.cor, textTransform: 'uppercase', letterSpacing: '.04em' }}>{e.canal}</span>
      </div>
      <div style={{ fontSize: 10.5, color: 'var(--text-muted)', marginBottom: 4 }}>{e.sub}</div>
      <AnGauge size={128}
        pct={temBp ? e.atingido_pct : (e.win_rate == null ? null : e.win_rate / (ALVO_WR_MKR) * 100)}
        valor={temBp ? mkrPct(e.atingido_pct) : mkrPct(e.win_rate)}
        label={temBp ? 'of BP in period' : 'Win rate'}
        sub={temBp ? `${mkrEur(e.vendas)} of ${mkrEur(e.bp_periodo)}` : `target ${ALVO_WR_MKR}% · no € BP`} />
      <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '8px 10px', marginTop: 10, paddingTop: 8, borderTop: '1px solid var(--border)' }}>
        <MkrMiniStat label="Sales" valor={mkrEur(e.vendas)} sub={`LY ${mkrEur(e.vendas_ant)}`} />
        <MkrMiniStat label="Open" valor={mkrEur(e.pipeline_aberto_eur)} sub={`weighted ${mkrEur(e.ponderado_eur)}`} />
        <MkrMiniStat label="Won / opps" valor={`${mkrN(e.won)} / ${mkrN(e.n)}`} sub={temBp ? `win rate ${mkrPct(e.win_rate)}` : `${mkrN(e.lost)} lost`} />
        <MkrMiniStat label="Rank C → S" valor={mkrPct(e.conv_c_s)} sub={`C coverage ${mkrX(e.cobertura_c_real)}`} />
      </div>
    </button>
  );
};

const MkrPilhaRank = ({ ranks }) => {
  const ks = ['D', 'C', 'B', 'A'];
  const total = ks.reduce((s, k) => s + (Number(ranks?.[k]?.valor) || 0), 0);
  if (!total) return <span style={{ fontSize: 11, color: 'var(--text-dim)' }}>No open value</span>;
  return (
    <div style={{ minWidth: 0 }}>
      <div style={{ display: 'flex', gap: 2, height: 10, borderRadius: 4, overflow: 'hidden' }}>
        {ks.filter(k => ranks[k].valor > 0).map(k => (
          <div key={k} title={`Rank ${k} · ${mkrN(ranks[k].n)} open · ${mkrEur(ranks[k].valor)}`} style={{ width: `${ranks[k].valor / total * 100}%`, background: MKR_RANK_COR[k] }} />
        ))}
      </div>
      <div style={{ display: 'flex', gap: '2px 9px', flexWrap: 'wrap', fontSize: 10.5, color: 'var(--text-muted)', marginTop: 4 }}>
        {ks.map(k => (
          <span key={k} style={{ whiteSpace: 'nowrap', opacity: ranks[k].n ? 1 : 0.5 }}>
            <span style={{ display: 'inline-block', width: 8, height: 8, borderRadius: 2, background: MKR_RANK_COR[k], marginRight: 4 }} />{k} {mkrN(ranks[k].n)} · {mkrEur(ranks[k].valor)}
          </span>
        ))}
      </div>
    </div>
  );
};

const MkrVendasBp = ({ data }) => {
  const eqs = data.equipa ? data.equipas.filter(e => e.key === data.equipa) : data.equipas;
  const comBp = eqs.filter(e => Array.isArray(e.bp_mensal));
  const lista = comBp.length ? comBp : eqs;
  const soma = (k) => Array.from({ length: 12 }, (_, i) => lista.reduce((s, e) => s + (Number(e[k]?.[i]) || 0), 0));
  const snapMes = data.fontes?.snapshot_primavera?.mes_max;
  const fimMes = Number(String(data.periodo.fim).slice(5, 7));
  const mes = lista.every(e => e.fonte_vendas === 'primavera') && snapMes ? Math.min(Number(snapMes), fimMes) : fimMes;
  const nomes = lista.map(e => e.label.replace('Mimaki ', '')).join(' + ');
  return (
    <AnCartao titulo={comBp.length ? 'Sales vs BP by month' : 'Sales by month'}
      sub={comBp.length
        ? `${nomes} · Primavera snapshot (comercial_ytd_snap) against comercial_bp_targets`
        : `${nomes} · Gestor Closed Won value by close date · no € BP for this team`}>
      <AnMesBars bp={comBp.length ? soma('bp_mensal') : new Array(12).fill(0)} real={soma('vendas_mensal')} mes={mes} altura={200} fmt={mkrEur}
        nomes={{ a: 'BP', b: 'Sales' }} rotulos={MKR_MESBARS_ROTULOS} />
      {comBp.length > 0 && comBp.length < eqs.length && (
        <div style={{ fontSize: 10.5, color: 'var(--text-dim)', marginTop: 6 }}>€ BP exists for {nomes} only. Spain, Partners and AllDecor follow the units BP in the machines chart.</div>
      )}
    </AnCartao>
  );
};

const MkrMaquinasMes = ({ eq, pais, setPais, mes }) => {
  const t = eq.paises[pais].TOTAL;
  return (
    <AnCartao titulo="Machines sold vs equipment BP by month" sub={`${pais} · units · BP Studio FY${String(eq.ano).slice(2)} against Gestor Closed Won`}
      direita={<MkrToggle value={pais} onChange={setPais} opcoes={MKR_PAISES} />}>
      <AnMesBars bp={t.bp_mensal} real={t.won_mensal} mes={mes} altura={200} fmt={mkrUn} nomes={{ a: 'BP units', b: 'Sold' }} rotulos={MKR_MESBARS_ROTULOS} />
    </AnCartao>
  );
};

const MkrEquipamentosSeg = ({ eq, pais, setPais, isMobile }) => {
  const p = eq.paises[pais];
  const t = p.TOTAL;
  const q = eq.trimestre_seguinte;
  const modelos = (() => {
    const l = eq.modelos.filter(m => pais === 'Iberia' || m.pais === pais);
    if (pais !== 'Iberia') return l;
    const by = new Map();
    for (const m of l) {
      const k = m.modelo.toUpperCase();
      const a = by.get(k) || { ...m, bp_qty_ano: 0, bp_qty: 0, won_qty: 0, won_eur: 0 };
      a.bp_qty_ano += m.bp_qty_ano; a.bp_qty += m.bp_qty; a.won_qty += m.won_qty; a.won_eur += m.won_eur;
      by.set(k, a);
    }
    return [...by.values()].map(m => ({ ...m, gap_qty: m.won_qty - m.bp_qty })).sort((a, b) => a.gap_qty - b.gap_qty);
  })();
  const pctCor = (v) => (v == null ? 'var(--text)' : v >= 100 ? 'var(--success, #16a34a)' : v >= 85 ? 'var(--warning, #f59e0b)' : 'var(--danger, #dc2626)');
  return (
    <AnCartao titulo="Mimaki machines vs equipment BP by segment"
      sub={`${pais} · BP Studio units (bp_objectivos, active version) · € BP = units × Iberia ASP · company level, all Mimaki teams`}
      direita={<MkrToggle value={pais} onChange={setPais} opcoes={MKR_PAISES} />}>
      <div style={{ display: 'grid', gridTemplateColumns: isMobile ? '1fr 1fr' : 'repeat(4, 1fr)', gap: 10, marginBottom: 14 }}>
        <MkrMiniStat label="Sold vs BP to date" valor={<span style={{ color: pctCor(t.pct_qty) }}>{mkrUn(t.won_qty)} / {mkrUn(t.bp_qty)} · {mkrPct(t.pct_qty)}</span>} sub={`year BP ${mkrUn(t.bp_qty_ano)} units`} />
        <MkrMiniStat label="Value vs € BP to date" valor={<span style={{ color: pctCor(t.pct_eur) }}>{mkrEur(t.won_eur)} / {mkrEur(t.bp_eur)}</span>} sub={`year BP ${mkrEur(t.bp_eur_ano)}`} />
        <MkrMiniStat label="Same period last year" valor={`${mkrUn(t.won_ant_qty)} units`} sub={`${t.won_qty >= t.won_ant_qty ? '+' : '−'}${mkrUn(Math.abs(t.won_qty - t.won_ant_qty))} units · ${mkrEur(t.won_ant_eur)}`} />
        <MkrMiniStat label={`Q${q} rank B+ needed`} valor={<span style={{ color: t.open_b_n >= t.b_necessarios ? 'var(--success, #16a34a)' : 'var(--danger, #dc2626)' }}>{mkrN(t.open_b_n)} / {mkrN(t.b_necessarios)}</span>} sub="open B+A vs Q budget ÷ 20%" />
      </div>
      <div style={{ display: 'grid', gridTemplateColumns: isMobile ? '1fr' : '1.5fr 1fr', gap: 18 }}>
        <div style={{ display: 'grid', gap: 12, alignContent: 'start' }}>
          {eq.segmentos.map(s => {
            const x = p[s.key];
            if (!x.bp_qty_ano && !x.won_qty) return null;
            return (
              <div key={s.key} style={{ display: 'grid', gridTemplateColumns: '1fr 112px', gap: 12, alignItems: 'center' }}>
                <AnBullet real={x.won_qty} bpData={x.bp_qty} bpAno={x.bp_qty_ano} fmt={mkrUn} label={`${s.key} · ${s.label}`}
                  nomes={{ real: 'Sold', marca: 'BP to date', fim: 'BP year', nota: '% of BP to date' }}
                  sub={`${mkrUn(x.won_qty)} sold · BP ${mkrUn(x.bp_qty)} to date, ${mkrUn(x.bp_qty_ano)} year · ${mkrEur(x.won_eur)} vs ${mkrEur(x.bp_eur)} · LY ${mkrUn(x.won_ant_qty)}`} />
                <div title={`BP Studio rule: Q${q} budget units ÷ 20% conversion = rank B+ (stage 80+) opportunities needed`}
                  style={{ border: '1px solid var(--border)', borderRadius: 7, padding: '5px 8px', textAlign: 'right' }}>
                  <div style={{ fontSize: 9, color: 'var(--text-dim)', fontFamily: 'var(--font-mono)', textTransform: 'uppercase' }}>Q{q} B+ open / need</div>
                  <div style={{ fontSize: 13, fontWeight: 700, fontVariantNumeric: 'tabular-nums', color: !x.b_necessarios ? 'var(--text-muted)' : x.open_b_n >= x.b_necessarios ? 'var(--success, #16a34a)' : 'var(--danger, #dc2626)' }}>{mkrN(x.open_b_n)} / {mkrN(x.b_necessarios)}</div>
                </div>
              </div>
            );
          })}
        </div>
        <div style={{ minWidth: 0 }}>
          <div style={{ fontSize: 11.5, fontWeight: 650, marginBottom: 6 }}>Largest gaps by model</div>
          <MkrTabela maxHeight={300}>
            <thead><tr><MkrTh>Model</MkrTh><MkrTh right>Sold</MkrTh><MkrTh right>BP to date</MkrTh><MkrTh right>Year</MkrTh><MkrTh right>Gap</MkrTh></tr></thead>
            <tbody>
              {modelos.slice(0, 10).map(m => (
                <tr key={`${m.pais}${m.modelo}`}>
                  <MkrTd><b>{m.modelo}</b> <span style={{ fontSize: 10.5, color: 'var(--text-muted)' }}>{m.segmento}</span></MkrTd>
                  <MkrTd right>{mkrUn(m.won_qty)}</MkrTd><MkrTd right muted>{mkrUn(m.bp_qty)}</MkrTd><MkrTd right muted>{mkrUn(m.bp_qty_ano)}</MkrTd>
                  <MkrTd right style={{ fontWeight: 650, color: m.gap_qty >= 0 ? 'var(--success, #16a34a)' : 'var(--danger, #dc2626)' }}>{m.gap_qty >= 0 ? '+' : '−'}{mkrUn(Math.abs(m.gap_qty))}</MkrTd>
                </tr>
              ))}
            </tbody>
          </MkrTabela>
        </div>
      </div>
      <div style={{ fontSize: 10.5, color: 'var(--text-dim)', marginTop: 10, lineHeight: 1.5 }}>
        Segments follow the BP Studio of the Comercial module (SG, IP, TA, CP, 3D). Sold = Mimaki machines Closed Won in the Gestor by close date, Portugal from the PTDIG company and Spain from DIGES. BP to date prorates the current month by days.
        Q{q} need = BP units of Q{q} ÷ 20% conversion, the same rule the BP Studio uses for stage 80+.
      </div>
    </AnCartao>
  );
};

const MkrScorecards = ({ data, onPick, isMobile }) => (
  <AnCartao titulo="Team scorecards" sub="Sales against BP (or last year when the team has no € BP) · Mimaki targets · open pipeline by rank today. Click a team to filter the report.">
    <div style={{ display: 'grid', gap: 4 }}>
      {data.equipas.map(e => {
        const temBp = Number(e.bp_periodo) > 0;
        const activo = data.equipa === e.key;
        return (
          <div key={e.key} className="mkr-linha-eq" onClick={() => onPick(e.key)}
            style={{ display: 'grid', gridTemplateColumns: isMobile ? '1fr' : '150px 1.2fr 1.3fr 1.3fr', gap: 16, alignItems: 'center', padding: '10px 8px', borderRadius: 8, cursor: 'pointer', borderTop: '1px solid var(--border)', opacity: data.equipa && !activo ? 0.45 : 1, background: activo ? `color-mix(in oklch, ${e.cor} 6%, transparent)` : undefined }}>
            <div style={{ display: 'flex', alignItems: 'center', gap: 8, minWidth: 0 }}>
              <span style={{ width: 9, height: 9, borderRadius: '50%', background: e.cor, flexShrink: 0 }} />
              <span style={{ minWidth: 0 }}>
                <b style={{ fontSize: 12.5 }}>{e.label}</b>
                <div style={{ fontSize: 10.5, color: 'var(--text-muted)' }}>{e.fonte_vendas === 'primavera' ? 'Primavera snapshot' : 'Gestor Closed Won'}</div>
              </span>
            </div>
            <AnBullet fmt={mkrEur}
              real={e.vendas} bpData={temBp ? e.bp_periodo : e.vendas_ant} bpAno={temBp ? e.bp_ano : 0}
              label={temBp ? 'Sales vs BP' : 'Sales vs last year'}
              nomes={temBp ? { real: 'Sales', marca: 'BP to date', fim: 'BP year', nota: '% of BP to date' } : { real: 'Sales', marca: 'Same period last year', fim: '—', nota: '% of last year' }}
              sub={temBp ? `${mkrEur(e.vendas)} of ${mkrEur(e.bp_periodo)} · year ${mkrEur(e.bp_ano)} · LY ${mkrEur(e.vendas_ant)}` : `${mkrEur(e.vendas)} vs ${mkrEur(e.vendas_ant)} last year`} />
            <AnMiniKpis rotulos={MKR_KPI_ROTULOS} itens={[
              { label: 'Win rate', valor: e.win_rate, obj: ALVO_WR_MKR, amarelo: 14, fmt: mkrPct },
              { label: 'C → Sold', valor: e.conv_c_s, obj: 20, amarelo: 14, fmt: mkrPct },
              { label: 'C coverage', valor: e.cobertura_c_real, obj: 5, amarelo: 3, fmt: mkrX },
            ]} />
            <div style={{ minWidth: 0 }}>
              <div style={{ display: 'flex', justifyContent: 'space-between', fontSize: 11, marginBottom: 3 }}>
                <span>Open {mkrN(e.pipeline_aberto_n)} · <b>{mkrEur(e.pipeline_aberto_eur)}</b></span>
                <span style={{ color: 'var(--text-muted)' }}>weighted {mkrEur(e.ponderado_eur)}</span>
              </div>
              <MkrPilhaRank ranks={e.abertas_rank} />
            </div>
          </div>
        );
      })}
    </div>
    <div style={{ fontSize: 10.5, color: 'var(--text-dim)', marginTop: 8, lineHeight: 1.5 }}>
      C coverage = open rank C value ÷ BP still to sell this year (Mimaki rule 5×), only for teams with € BP. C → Sold = opportunities started in the period that reached rank C and were sold.
    </div>
  </AnCartao>
);

const MkrEquipas = ({ data, mudarF, isMobile }) => {
  const paisDaEquipa = { 'MIMAKI PT': 'Portugal', 'MIMAKI ES': 'Spain' }[data.equipa] || 'Iberia';
  const [pais, setPais] = React.useState(paisDaEquipa);
  React.useEffect(() => { setPais(paisDaEquipa); }, [data.equipa]);
  const eq = data.equipamentos;
  const pick = (k) => mudarF({ equipa: data.equipa === k ? '' : k });
  const fim = String(data.periodo.fim);
  const mesEq = eq && Number(fim.slice(0, 4)) === eq.ano ? Number(fim.slice(5, 7)) : 12;
  return (
    <>
      <div style={{ display: 'grid', gridTemplateColumns: isMobile ? '1fr 1fr' : 'repeat(5, 1fr)', gap: 10, marginBottom: 14 }}>
        {data.equipas.map(e => <MkrCartaoEquipa key={e.key} e={e} activo={data.equipa === e.key} apagado={!!data.equipa && data.equipa !== e.key} onClick={() => pick(e.key)} />)}
      </div>
      <div style={{ marginBottom: 14 }}><MkrFunilMimaki data={data} isMobile={isMobile} /></div>
      <div style={{ marginBottom: 14 }}><MkrScorecards data={data} onPick={pick} isMobile={isMobile} /></div>
      <div style={{ display: 'grid', gridTemplateColumns: isMobile ? '1fr' : '1fr 1fr', gap: 14, marginBottom: 14 }}>
        <MkrVendasBp data={data} />
        {eq ? <MkrMaquinasMes eq={eq} pais={pais} setPais={setPais} mes={mesEq} /> : <AnCartao titulo="Machines sold vs equipment BP"><AnVazio texto="No equipment BP in bp_objectivos for this year" /></AnCartao>}
      </div>
      {eq && <div style={{ marginBottom: 14 }}><MkrEquipamentosSeg eq={eq} pais={pais} setPais={setPais} isMobile={isMobile} /></div>}
      <MkrTabelaEquipas data={data} />
    </>
  );
};

const MkrTabelaEquipas = ({ data }) => (
  <>
    <MkrTabela>
      <thead>
        <tr>
          <MkrTh>Team</MkrTh><MkrTh right>Opps</MkrTh><MkrTh right>Won</MkrTh><MkrTh right>Lost</MkrTh><MkrTh right>Win rate</MkrTh>
          <MkrTh right>Open pipeline</MkrTh><MkrTh right>Weighted</MkrTh><MkrTh right>C coverage</MkrTh><MkrTh right>Mimaki Contact</MkrTh><MkrTh right>Sales</MkrTh><MkrTh right>Last year</MkrTh><MkrTh right>BP period</MkrTh><MkrTh right>Achieved</MkrTh>
          <MkrTh right>To invoice</MkrTh><MkrTh right>Forecast</MkrTh><MkrTh>Sales source</MkrTh>
        </tr>
      </thead>
      <tbody>
        {data.equipas.map(e => (
          <tr key={e.key} style={{ opacity: data.equipa && data.equipa !== e.key ? 0.45 : 1 }}>
            <MkrTd><span style={{ display: 'inline-flex', alignItems: 'center', gap: 8 }}><span style={{ width: 8, height: 8, borderRadius: '50%', background: e.cor }} /><span><b>{e.label}</b><div style={{ fontSize: 10.5, color: 'var(--text-muted)' }}>{e.sub}</div></span></span></MkrTd>
            <MkrTd right>{mkrN(e.n)}</MkrTd><MkrTd right>{mkrN(e.won)}</MkrTd><MkrTd right muted>{mkrN(e.lost)}</MkrTd>
            <MkrTd right>{mkrPct(e.win_rate)}</MkrTd><MkrTd right>{mkrEur(e.pipeline_aberto_eur)}</MkrTd>
            <MkrTd right muted>{mkrEur(e.ponderado_eur)}</MkrTd><MkrTd right muted>{mkrX(e.cobertura_c_real)}</MkrTd><MkrTd right muted>{mkrN(e.mimaki_contact_n)}</MkrTd>
            <MkrTd right style={{ fontWeight: 650 }}>{mkrEur(e.vendas)}</MkrTd><MkrTd right muted>{mkrEur(e.vendas_ant)}</MkrTd>
            <MkrTd right>{mkrEur(e.bp_periodo)}</MkrTd>
            <MkrTd right style={{ color: e.atingido_pct == null ? undefined : e.atingido_pct >= 100 ? 'var(--success)' : e.atingido_pct >= 85 ? 'var(--warning)' : 'var(--danger)', fontWeight: 650 }}>{mkrPct(e.atingido_pct)}</MkrTd>
            <MkrTd right muted>{mkrEur(e.por_faturar)}</MkrTd><MkrTd right muted>{mkrEur(e.forecast)}</MkrTd>
            <MkrTd muted title={e.fonte_label} style={{ fontSize: 11 }}>{e.fonte_vendas === 'primavera' ? 'Primavera snapshot' : 'Gestor Closed Won'}</MkrTd>
          </tr>
        ))}
      </tbody>
    </MkrTabela>
    <div style={{ fontSize: 11, color: 'var(--text-muted)', marginTop: 10, lineHeight: 1.55 }}>
      Sales for Mimaki PT and GC come from the Primavera snapshot (comercial_ytd_snap) by team code. Spain, Partners and AllDecor have no Primavera ES feed, so sales use the value of Closed Won opportunities by close date, as the Comercial RPC does.
      BP comes from comercial_bp_targets, to invoice from the ECW backlog (primavera.f9_snap) and forecast from the RPC orders (rpc_encomendas). Opportunity counts use the start date inside the period.
    </div>
  </>
);

// ── Segments ────────────────────────────────────────────────────────────────
const MkrSegmentos = ({ data }) => {
  const max = Math.max(...data.segmentos.map(s => s.n), 1);
  return (
    <>
      <MkrTabela>
        <thead>
          <tr>
            <MkrTh>Segment</MkrTh><MkrTh style={{ width: '22%' }}>Opportunities</MkrTh><MkrTh right>Open</MkrTh><MkrTh right>Won</MkrTh><MkrTh right>Win rate</MkrTh>
            <MkrTh right>PT / ES</MkrTh><MkrTh right>Direct / Dealer</MkrTh><MkrTh right>Open value</MkrTh><MkrTh right>Won in period</MkrTh><MkrTh right>Last year</MkrTh>
          </tr>
        </thead>
        <tbody>
          {data.segmentos.map(s => (
            <tr key={s.segmento}>
              <MkrTd><b>{s.segmento}</b></MkrTd>
              <MkrTd><span style={{ display: 'grid', gridTemplateColumns: '1fr 40px', gap: 8, alignItems: 'center' }}><MkrBarra valor={s.n} max={max} cor="var(--ai-500, #3859D0)" /><span style={{ textAlign: 'right' }}>{mkrN(s.n)}</span></span></MkrTd>
              <MkrTd right>{mkrN(s.open)}</MkrTd><MkrTd right>{mkrN(s.won)}</MkrTd><MkrTd right>{mkrPct(s.win_rate)}</MkrTd>
              <MkrTd right muted>{mkrN(s.pt)} / {mkrN(s.es)}</MkrTd><MkrTd right muted>{mkrN(s.direct)} / {mkrN(s.dealer)}</MkrTd>
              <MkrTd right>{mkrEur(s.open_eur)}</MkrTd>
              <MkrTd right style={{ fontWeight: 650, color: s.won_periodo_eur >= s.won_ant_eur ? 'var(--success)' : 'var(--danger)' }}>{mkrEur(s.won_periodo_eur)}</MkrTd>
              <MkrTd right muted>{mkrEur(s.won_ant_eur)}</MkrTd>
            </tr>
          ))}
        </tbody>
      </MkrTabela>
      <div style={{ fontSize: 11, color: 'var(--text-muted)', marginTop: 10, lineHeight: 1.55 }}>
        Segments follow the classification of the pipeline dashboard: TA-DTF (DTF), TA (TxF, TS200, TS330-1600, TX300P, Tiger), SG 3.2m+ (TS330-3200, SWJ), IP Small (UJF), IP Large (JFX), Signage 1.8m (CJV, UCJV, UJV55, UJV100, UJ330H, JV330, JV200).
        Dealer is the Mimaki PA team; all other teams sell direct.
      </div>
    </>
  );
};

// ── Mimaki Contact (G1008) ──────────────────────────────────────────────────
const MkrContacto = ({ data, isMobile }) => {
  const Kpi = window.ResKpi;
  const c = data.mimaki_contact;
  const max = Math.max(...c.por_rank.map(x => x.n), 1);
  const hoje = Date.now();
  const parados = data.linhas
    .filter(l => l.source_group === 'Mimaki Contact' && l.rank === 'D' && /^10\b/.test(l.gestor_stage) && l.start_date)
    .map(l => ({ ...l, dias: Math.floor((hoje - Date.parse(`${l.start_date}T00:00:00`)) / 86400000) }))
    .filter(l => l.dias > 14)
    .sort((a, b) => b.dias - a.dias);
  return (
    <>
      <div style={{ display: 'grid', gridTemplateColumns: isMobile ? '1fr 1fr' : 'repeat(5, 1fr)', gap: 10, marginBottom: 14 }}>
        <Kpi label="Leads from Mimaki" value={mkrN(c.n)} sub={`${mkrN(c.pt)} Portugal · ${mkrN(c.es)} Spain`} />
        <Kpi label="In progress" value={mkrN(c.open)} sub={mkrEur(c.open_eur)} />
        <Kpi label="Won" value={mkrN(c.won)} sub={mkrEur(c.won_eur)} />
        <Kpi label="Lost" value={mkrN(c.lost)} sub={`Win rate ${mkrPct(c.win_rate)}`} />
        <Kpi label="Untouched > 14 days" value={mkrN(c.sem_avanco_14d)} sub="Still at Gestor stage 10" light={c.sem_avanco_14d > 0 ? 'vermelho' : 'verde'} />
      </div>
      <div style={{ display: 'grid', gridTemplateColumns: isMobile ? '1fr' : '1fr 1fr', gap: 14 }}>
        <AnCartao titulo="Follow-up by rank" sub="Current rank of the leads Mimaki sent (source G1008 Mimaki Contact)">
          <div style={{ display: 'grid', gap: 8 }}>
            {c.por_rank.map(x => (
              <div key={x.key} style={{ display: 'grid', gridTemplateColumns: '40px 1fr 40px', gap: 10, alignItems: 'center', fontSize: 12 }}>
                <MkrRank rank={x.key} /><MkrBarra valor={x.n} max={max} cor={MKR_RANK_COR[x.key]} altura={10} /><span style={{ textAlign: 'right', fontWeight: 650 }}>{mkrN(x.n)}</span>
              </div>
            ))}
          </div>
        </AnCartao>
        <AnCartao titulo="By team" sub="Who follows the Mimaki leads">
          {c.por_equipa.length ? (
            <div style={{ display: 'grid', gap: 8 }}>
              {c.por_equipa.map(x => (
                <div key={x.key} style={{ display: 'grid', gridTemplateColumns: '110px 1fr 40px', gap: 10, alignItems: 'center', fontSize: 12 }}>
                  <span>{x.label}</span><MkrBarra valor={x.n} max={c.n} cor={(data.equipas_def.find(e => e.key === x.key) || {}).cor} altura={10} /><span style={{ textAlign: 'right', fontWeight: 650 }}>{mkrN(x.n)}</span>
                </div>
              ))}
            </div>
          ) : <AnVazio texto="no Mimaki Contact leads in this period" />}
        </AnCartao>
      </div>
      {parados.length > 0 && (
        <AnCartao titulo="Leads to follow up now" sub="Mimaki Contact leads still at Gestor stage 10 after 14 days" acento="var(--danger, #dc2626)" style={{ marginTop: 14 }}>
          <MkrTabela>
            <thead><tr><MkrTh>Company</MkrTh><MkrTh>Team</MkrTh><MkrTh>Salesperson</MkrTh><MkrTh>Equipment</MkrTh><MkrTh right>Days</MkrTh><MkrTh>Start</MkrTh></tr></thead>
            <tbody>
              {parados.map(l => (
                <tr key={`${l.tenant}:${l.id}`} className="mkr-linha" onClick={() => mkrAbrirCrm(l, 'contact')} title="Open the opportunity in the CRM" style={{ cursor: 'pointer' }}>
                  <MkrTd style={{ fontWeight: 600 }}>{l.company || '—'}</MkrTd><MkrTd muted>{mkrEquipaCurta(l.team)}</MkrTd>
                  <MkrTd>{l.salesperson || '—'}</MkrTd><MkrTd>{l.equipment}</MkrTd>
                  <MkrTd right style={{ color: 'var(--danger)', fontWeight: 650 }}>{l.dias}</MkrTd><MkrTd mono muted>{l.start_date}</MkrTd>
                </tr>
              ))}
            </tbody>
          </MkrTabela>
        </AnCartao>
      )}
    </>
  );
};

// ── Opportunities ───────────────────────────────────────────────────────────
function mkrAbrirCrm(l, tab) {
  try { history.replaceState(null, '', `#screen=marketing&sub=resultados-mimaki&tab=${tab}`); } catch {}
  window.location.hash = `screen=marketing&sub=audiencias&op=${encodeURIComponent(l.id)}`;
}

const MkrOps = ({ data }) => {
  const Filtro = window.ResFiltro;
  const [q, setQ] = React.useState('');
  const [rank, setRank] = React.useState('');
  const [fonte, setFonte] = React.useState('');
  const [gerado, setGerado] = React.useState('');
  const [limite, setLimite] = React.useState(150);
  const norm = (s) => String(s || '').normalize('NFD').replace(/[\u0300-\u036f]/g, '').toLowerCase();
  const termo = norm(q.trim());
  const fontes = Object.keys(data.por_fonte).sort();
  const gerados = (data.origens || []).filter(o => o.n > 0);
  const lista = data.linhas.filter(l => (!rank || l.rank === rank) && (!fonte || l.source_group === fonte) && (!gerado || l.generated_by === gerado)
    && (!termo || norm(`${l.company} ${l.equipment} ${l.salesperson} ${l.source} ${l.id}`).includes(termo)));
  React.useEffect(() => { setLimite(150); }, [termo, rank, fonte, gerado]);
  return (
    <>
      <div style={{ display: 'flex', gap: 8, alignItems: 'center', flexWrap: 'wrap', marginBottom: 10 }}>
        <input value={q} onChange={e => setQ(e.target.value)} placeholder="Search company, equipment, salesperson" aria-label="Search opportunities"
          style={{ height: 30, minWidth: 240, flex: '0 1 320px', padding: '0 10px', border: '1px solid var(--border)', borderRadius: 7, background: 'var(--bg-elev)', color: 'var(--text)', fontSize: 12, fontFamily: 'inherit' }} />
        {Filtro && <Filtro label="Rank" value={rank} onChange={setRank} options={[{ value: '', label: 'All' }, ...['D', 'C', 'B', 'A', 'S', 'Lost'].map(r => ({ value: r, label: r }))]} menuMinWidth={120} />}
        {Filtro && gerados.length > 0 && <Filtro label="Generated by" value={gerado} onChange={setGerado} options={[{ value: '', label: 'All' }, ...gerados.map(o => ({ value: o.label, label: `${o.label} (${o.n})` }))]} menuMinWidth={200} />}
        {Filtro && <Filtro label="Source" value={fonte} onChange={setFonte} options={[{ value: '', label: 'All' }, ...fontes.map(f => ({ value: f, label: `${f} (${data.por_fonte[f]})` }))]} />}
        <span style={{ fontSize: 11.5, color: 'var(--text-muted)', marginLeft: 'auto' }}>{mkrN(lista.length)} of {mkrN(data.linhas.length)} opportunities</span>
      </div>
      <MkrTabela maxHeight="62vh">
        <thead>
          <tr>
            <MkrTh>Company</MkrTh><MkrTh>Country</MkrTh><MkrTh>Team</MkrTh><MkrTh>Salesperson</MkrTh><MkrTh>Generated by</MkrTh><MkrTh>Source</MkrTh>
            <MkrTh>Equipment</MkrTh><MkrTh>Segment</MkrTh><MkrTh>Gestor stage</MkrTh><MkrTh>Rank</MkrTh><MkrTh>Mimaki CRM step</MkrTh>
            <MkrTh right>Value</MkrTh><MkrTh>Start</MkrTh><MkrTh>Close</MkrTh>
          </tr>
        </thead>
        <tbody>
          {lista.slice(0, limite).map(l => (
            <tr key={`${l.tenant}:${l.id}`} className="mkr-linha" onClick={() => mkrAbrirCrm(l, 'ops')} title="Open the opportunity in the CRM" style={{ cursor: 'pointer' }}>
              <MkrTd style={{ maxWidth: 260, overflow: 'hidden', textOverflow: 'ellipsis', fontWeight: 600 }} title={l.company}>{l.company || '—'}</MkrTd>
              <MkrTd muted>{l.country}</MkrTd><MkrTd muted title={l.team_inferred ? 'No team in the Gestor · team of the salesperson' : undefined}>{mkrEquipaCurta(l.team)}{l.team_inferred ? '*' : ''}</MkrTd>
              <MkrTd>{l.salesperson || <span style={{ color: 'var(--text-dim)' }}>—</span>}</MkrTd>
              <MkrTd title={l.generated_by_detail}>{l.generated_by}</MkrTd>
              <MkrTd muted style={{ maxWidth: 160, overflow: 'hidden', textOverflow: 'ellipsis' }} title={l.source_group}>{l.source}</MkrTd>
              <MkrTd style={{ maxWidth: 220, overflow: 'hidden', textOverflow: 'ellipsis' }} title={l.equipment}>{l.equipment}</MkrTd>
              <MkrTd muted>{l.segment}</MkrTd><MkrTd muted>{l.gestor_stage}</MkrTd>
              <MkrTd><MkrRank rank={l.rank} sub={l.rank_sub} /></MkrTd>
              <MkrTd muted>{l.mimaki_status ? `${l.mimaki_status} · ${l.mimaki_status_label}` : '—'}</MkrTd>
              <MkrTd right>{mkrEur(l.value_eur)}</MkrTd>
              <MkrTd mono muted>{l.start_date || '—'}</MkrTd><MkrTd mono muted>{l.close_date || '—'}</MkrTd>
            </tr>
          ))}
        </tbody>
      </MkrTabela>
      {lista.length > limite && (
        <div style={{ textAlign: 'center', marginTop: 10 }}>
          <button type="button" className="btn btn-sm" onClick={() => setLimite(n => n + 300)}>Show {mkrN(Math.min(300, lista.length - limite))} more</button>
        </div>
      )}
      {!lista.length && <div style={{ marginTop: 10 }}><AnVazio texto="no opportunities for these filters" /></div>}
    </>
  );
};

// ── Ranking map ─────────────────────────────────────────────────────────────
const MKR_GRUPOS_MIMAKI = [['D', '30%', [1, 2, 3]], ['C', '50–70%', [4, 5, 6]], ['B', '80%', [7]], ['A', '90%', [8, 9, 10]], ['S', '100%', [11]]];
const MKR_MAPA_LINHA = 50;

// As duas tabelas usam as mesmas linhas (um passo Mimaki por linha) para ficarem alinhadas lado a lado.
const MkrMapa = ({ data, isMobile }) => {
  const linhas = [];
  MKR_GRUPOS_MIMAKI.forEach(([rank, prob, estados]) => estados.forEach((estado, i) => {
    linhas.push({ id: `e${estado}`, rank, prob, estado, primeiro: i === 0, n: estados.length, gestor: data.mapeamento.filter(m => m.estado === estado) });
  }));
  linhas.push({ id: 'lost', rank: 'Lost', prob: '', estado: null, primeiro: true, n: 1, gestor: data.mapeamento.filter(m => m.estado == null) });
  const td = (l, extra) => ({
    height: MKR_MAPA_LINHA, verticalAlign: 'middle', whiteSpace: 'normal',
    borderTop: l.primeiro ? '2px solid var(--border)' : '1px solid var(--border)', ...extra,
  });
  return (
    <div style={{ display: 'grid', gridTemplateColumns: isMobile ? '1fr' : '1fr 1fr', gap: 14, alignItems: 'start' }}>
      <AnCartao titulo="Gestor → Mimaki rank" sub="How each Gestor stage is reported to Mimaki">
        <MkrTabela>
          <thead><tr><MkrTh>Gestor stage</MkrTh><MkrTh style={{ width: 110 }}>Reported as</MkrTh></tr></thead>
          <tbody>
            {linhas.map(l => (
              <tr key={l.id}>
                <MkrTd style={td(l)}>
                  {l.gestor.length
                    ? l.gestor.map(m => <div key={m.gestor} style={{ lineHeight: 1.35 }}>{m.gestor}{m.nota && <sup style={{ color: 'var(--text-muted)', marginLeft: 2 }}>1</sup>}</div>)
                    : <span style={{ color: 'var(--text-dim)', fontStyle: 'italic' }}>No Gestor stage</span>}
                </MkrTd>
                <MkrTd style={td(l)}>
                  {l.gestor.length ? (
                    <span style={{ display: 'inline-flex', alignItems: 'center', gap: 6 }}>
                      <MkrRank rank={l.gestor[0].rank.replace(/\d$/, '')} sub={l.gestor[0].rank} />
                      {l.gestor[0].nota && <span style={{ fontSize: 10.5, color: 'var(--text-muted)' }}>or C1<sup>1</sup></span>}
                    </span>
                  ) : <span style={{ color: 'var(--text-dim)' }}>—</span>}
                </MkrTd>
              </tr>
            ))}
          </tbody>
        </MkrTabela>
        {data.mapeamento.filter(m => m.nota).map(m => (
          <div key={m.gestor} style={{ fontSize: 10.5, color: 'var(--text-muted)', marginTop: 8 }}><sup>1</sup> {m.gestor}: {m.nota}.</div>
        ))}
      </AnCartao>
      <AnCartao titulo="Mimaki leads ranking chart" sub="Ranks and CRM steps used by Mimaki EMEA">
        <MkrTabela>
          <thead><tr><MkrTh style={{ width: 110 }}>Mimaki rank</MkrTh><MkrTh>Mimaki CRM step</MkrTh></tr></thead>
          <tbody>
            {linhas.map(l => (
              <tr key={l.id}>
                {l.primeiro && (
                  <MkrTd style={td(l, { borderRight: '1px solid var(--border)' })} rowSpan={l.n}>
                    <span style={{ display: 'inline-flex', alignItems: 'center', gap: 8 }}>
                      <MkrRank rank={l.rank} />
                      {l.prob && <span style={{ color: 'var(--text-muted)', fontVariantNumeric: 'tabular-nums' }}>{l.prob}</span>}
                    </span>
                  </MkrTd>
                )}
                <MkrTd style={td(l, { color: l.gestor.length ? 'var(--text)' : 'var(--text-muted)' })}>
                  {l.estado ? `${l.estado} · ${data.estados_mimaki[l.estado]}` : <span style={{ color: 'var(--text-dim)', fontStyle: 'italic' }}>Closed lost · not a Mimaki step</span>}
                </MkrTd>
              </tr>
            ))}
          </tbody>
        </MkrTabela>
      </AnCartao>
    </div>
  );
};

// ── Ecrã ────────────────────────────────────────────────────────────────────
function mkrFiltrosIniciais() {
  try {
    const f = JSON.parse(sessionStorage.getItem(MKR_FILTROS_KEY) || 'null');
    if (f && f.periodo) return f;
  } catch {}
  return { periodo: 'ytd', year: new Date().getFullYear(), inicio: '', fim: '', equipa: '', origem: '' };
}

const MktResultadosMimakiScreen = ({ setSub }) => {
  const bp = window.useBreakpoint ? window.useBreakpoint() : { isMobile: false };
  const isMobile = bp.isMobile;
  const [f, setF] = React.useState(mkrFiltrosIniciais);
  const [tab, setTabState] = React.useState(() => {
    const h = new URLSearchParams(String(window.location.hash || '').replace(/^#/, '')).get('tab');
    if (h && MKR_TABS.some(t => t.id === h)) return h;
    try { return sessionStorage.getItem(MKR_TAB_KEY) || 'overview'; } catch { return 'overview'; }
  });
  const [data, setData] = React.useState(null);
  const [loading, setLoading] = React.useState(true);
  const [erro, setErro] = React.useState(null);
  const [overTab, setOverTab] = React.useState(null);
  const scrollRef = React.useRef(null);
  window.useMktZoneLoading && window.useMktZoneLoading(loading && !data);

  const setTab = (t) => {
    setTabState(t);
    try { sessionStorage.setItem(MKR_TAB_KEY, t); } catch {}
    if (scrollRef.current) scrollRef.current.scrollTop = 0;
  };
  const mudarF = (patch) => setF(prev => {
    const n = { ...prev, ...patch };
    try { sessionStorage.setItem(MKR_FILTROS_KEY, JSON.stringify(n)); } catch {}
    return n;
  });
  const params = { periodo: f.periodo, year: f.year, inicio: f.periodo === 'custom' ? f.inicio : '', fim: f.periodo === 'custom' ? f.fim : '', equipa: f.equipa, origem: f.origem || '' };

  React.useEffect(() => {
    let vivo = true;
    setLoading(true); setErro(null);
    fetch(`${MKR_API}?${mkrQs(params)}`, { headers: { 'Content-Type': 'application/json' } })
      .then(async r => { const b = await r.json().catch(() => ({})); if (!r.ok) throw new Error(b.error || `HTTP ${r.status}`); return b; })
      .then(d => { if (vivo) setData(d); })
      .catch(e => { if (vivo) setErro(e.message); })
      .finally(() => { if (vivo) setLoading(false); });
    return () => { vivo = false; };
  }, [f.periodo, f.year, f.inicio, f.fim, f.equipa, f.origem]);

  const Periodo = window.BriefingDateRangePicker;
  const Filtro = window.ResFiltro;
  const MenuAccoes = window.ResMenuAccoes;
  const Trilho = window.ResTrilho;
  const anoCorrente = new Date().getFullYear();
  const periodoValor = (() => {
    if (f.periodo === 'custom' && f.inicio && f.fim) {
      const h = resHomologo();
      const homologo = f.inicio === resDataIso(h.start) && f.fim === resDataIso(h.end);
      return { preset: homologo ? 'homologo' : 'custom', start: new Date(`${f.inicio}T00:00:00`), end: new Date(`${f.fim}T00:00:00`) };
    }
    const r = resPeriodoDatas(f.periodo, f.year) || resPeriodoDatas('ytd', f.year);
    return { preset: `${f.periodo}:${f.year}`, start: r.start, end: r.end };
  })();
  const mudarPeriodo = (v) => {
    if (v.preset === 'custom' || v.preset === 'homologo') {
      mudarF({ periodo: 'custom', inicio: resDataIso(v.start), fim: resDataIso(v.end), year: v.end.getFullYear() });
      return;
    }
    const [p, a] = String(v.preset).split(':');
    mudarF({ periodo: p || 'ytd', year: Number(a) || anoCorrente, inicio: '', fim: '' });
  };

  const csvUrl = `${MKR_API}.csv?${mkrQs(params)}`;
  const menu = [
    { id: 'resultados', label: 'Resultados', sub: 'Funil, objectivos e campanhas', onClick: () => setSub && setSub('resultados') },
    { id: 'analise', label: 'Análise da Digi AI', sub: 'Leitura, BP e campanhas a fazer', onClick: () => setSub && setSub('resultados-analise') },
    { id: 'mimaki', label: 'Report Mimaki', sub: 'Pipeline das equipas Mimaki em ranks Mimaki', checked: true, onClick: () => setTab('overview') },
    { separador: true },
    { id: 'csv', label: 'Exportar CSV para a Mimaki', sub: 'Uma linha por oportunidade, colunas em inglês', disabled: !data?.linhas?.length, onClick: () => window.open(csvUrl, '_blank', 'noopener') },
  ];
  const snap = data?.fontes?.snapshot_primavera;
  const equipasOpts = [{ value: '', label: 'Todas as equipas' }, ...((data?.equipas_def) || []).map(e => ({ value: e.key, label: e.label }))];
  const origensOpts = [{ value: '', label: 'Todas as origens' }, ...((data?.origens_def) || []).map(o => ({ value: o.key, label: o.label }))];
  const origemActiva = data?.origem ? (data.origens_def || []).find(o => o.key === data.origem) : null;

  return (
    <div style={{ display: 'flex', flexDirection: 'column', height: '100%', minHeight: 0, background: 'var(--bg)' }}>
      <style>{'.mkr-linha:hover td{background:var(--bg-hover,#f1f5f9)}.mkr-linha-eq:hover{background:var(--bg-hover,#f1f5f9)!important}'}</style>
      <div style={{ background: 'var(--bg-elev)', borderBottom: '1px solid var(--border)', padding: isMobile ? '16px 16px 0' : '20px 40px 0', flexShrink: 0 }}>
        <div style={{ fontSize: 11, fontWeight: 600, letterSpacing: '0.07em', color: 'var(--text-muted)', textTransform: 'uppercase', marginBottom: 6, fontFamily: 'var(--font-mono)' }}>
          MARKETING · RESULTADOS
        </div>
        <h1 style={{ margin: 0, fontSize: 21, fontWeight: 700, color: 'var(--text)', fontFamily: 'var(--font-display)', letterSpacing: '-0.01em', lineHeight: 1.2 }}>
          Report Mimaki
        </h1>
        <div style={{ fontSize: 12, color: 'var(--text-muted)', marginTop: 4 }}>Digidelta Iberia · Mimaki pipeline report for Mimaki EMEA</div>
        <div style={{ height: 1, background: 'var(--border)', margin: isMobile ? '14px -16px 0' : '16px -40px 0' }} />
        <div style={{ display: 'flex', gap: 0, overflowX: 'auto' }}>
          {MKR_TABS.map(t => {
            const active = tab === t.id;
            const over = overTab === t.id;
            return (
              <button key={t.id} type="button" onClick={() => setTab(t.id)} onMouseEnter={() => setOverTab(t.id)} onMouseLeave={() => setOverTab(null)}
                style={{
                  background: 'none', border: 'none', outline: 'none',
                  borderBottom: `2px solid ${active ? 'var(--ai-500)' : 'transparent'}`,
                  color: active || over ? 'var(--text)' : 'var(--text-muted)',
                  fontSize: 13, fontWeight: active ? 600 : 500,
                  fontFamily: 'var(--font-display)', letterSpacing: '.01em',
                  padding: '10px 18px 12px', cursor: 'pointer', whiteSpace: 'nowrap',
                  transition: 'color .15s, border-color .15s',
                }}>
                {t.label}
                {t.id === 'contact' && data?.mimaki_contact?.sem_avanco_14d > 0 && <span style={{ marginLeft: 6, fontSize: 10, fontWeight: 700, color: '#fff', background: 'var(--danger, #dc2626)', borderRadius: 99, padding: '1px 6px' }}>{data.mimaki_contact.sem_avanco_14d}</span>}
              </button>
            );
          })}
        </div>
      </div>

      <div ref={scrollRef} className="scrollbar" style={{ overflowY: 'auto', flex: 1, minHeight: 0, padding: isMobile ? '18px 16px 64px' : '28px 40px 64px', background: 'var(--bg)' }}>
        {Trilho && (
          <Trilho
            segmentos={[{ label: 'Resultados', onClick: () => setSub && setSub('resultados') }, { label: 'Report Mimaki' }]}
            nota={
              <span style={{ display: 'inline-flex', alignItems: 'center', gap: 10, fontSize: 12, color: 'var(--text-muted)', flexWrap: 'wrap' }}>
                {data && <span>{data.periodo.label}</span>}
                {snap?.mes_max && <span>snapshot Primavera (comercial_ytd_snap) até {String(snap.mes_max).padStart(2, '0')}/{String(data.periodo.fim).slice(0, 4)}</span>}
                {data && <span>Gestor sync · {mkrN(data.resumo.n)} ops</span>}
              </span>
            }
            extra={
              <>
                {Periodo ? (
                  <Periodo value={periodoValor} onChange={mudarPeriodo} presets={resPeriodoPresets()} resolver={resPeriodoResolver} />
                ) : Filtro && (
                  <Filtro label="Período" value={periodoValor.preset} onChange={id => mudarPeriodo({ preset: id, ...(resPeriodoResolver(id) || {}) })}
                    options={resPeriodoPresets().filter(p => p.id !== 'custom').map(p => ({ value: p.id, label: p.label }))} />
                )}
                {Filtro && <Filtro label="Equipa" value={f.equipa} onChange={(v) => mudarF({ equipa: v })} options={equipasOpts} menuMinWidth={170} />}
                {Filtro && <Filtro label="Origem" value={f.origem || ''} onChange={(v) => mudarF({ origem: v })} options={origensOpts} menuMinWidth={200} />}
                {MenuAccoes && <MenuAccoes icone items={menu} />}
              </>
            } />
        )}

        {loading && !data && <AnVazio altura={200} texto="A carregar o report Mimaki…" />}
        {erro && <div style={{ padding: 16, color: 'var(--danger)', fontSize: 12.5 }}>{erro}</div>}
        {data && (
          <div style={{ opacity: loading ? 0.6 : 1, transition: 'opacity .15s' }}>
            {origemActiva && (
              <div style={{ display: 'flex', alignItems: 'center', gap: 10, flexWrap: 'wrap', padding: '9px 12px', marginBottom: 14, borderRadius: 8, border: `1px solid color-mix(in oklch, ${origemActiva.cor} 35%, transparent)`, background: `color-mix(in oklch, ${origemActiva.cor} 7%, transparent)`, fontSize: 12 }}>
                <span style={{ width: 9, height: 9, borderRadius: 2, background: origemActiva.cor }} />
                <span>Showing opportunities generated by <b>{origemActiva.label}</b>. Primavera sales and BP stay at team level, because invoices carry no lead source.</span>
                <button type="button" className="btn btn-sm" style={{ marginLeft: 'auto' }} onClick={() => mudarF({ origem: '' })}>Show all origins</button>
              </div>
            )}
            {tab === 'overview' && <MkrOverview data={data} isMobile={isMobile} mudarF={mudarF} />}
            {tab === 'teams' && <MkrEquipas data={data} mudarF={mudarF} isMobile={isMobile} />}
            {tab === 'segments' && <MkrSegmentos data={data} />}
            {tab === 'contact' && <MkrContacto data={data} isMobile={isMobile} />}
            {tab === 'ops' && <MkrOps data={data} />}
            {tab === 'map' && <MkrMapa data={data} isMobile={isMobile} />}
            <div style={{ fontSize: 10.5, color: 'var(--text-dim)', marginTop: 18, lineHeight: 1.5 }}>{data.fontes.regra}</div>
          </div>
        )}
      </div>
    </div>
  );
};

window.MktResultadosMimakiScreen = MktResultadosMimakiScreen;
