// Canais → Leads & economics por oferta (spec 2026-07-29-leads-economics-canais-design):
// a tabela que vivia dentro do Funnel Sales Tracker virou página própria, com o bloco SMS
// preenchido (quebrado por tipo de disparo e por nº de potes) e COGS/Profit/PPL pela régua
// única do product_costs. Molde Leads.jsx: PeriodSelector canônico + MultiDropdown de
// produto, LoadingScreen no 1º load, FullLoadingOverlay nas trocas de filtro.
// SEM mock — dado operacional (ver offline honesto no fim do componente).
//
// Prefixos deste arquivo: estilos `lex`, constantes `LE_`, helpers `le*`.

// Rótulos dos 4 blocos de `type` (ordem vem de data.types — é a ordem do payload).
const LE_TYPE_LABELS = {
  welcome: 'Welcome · Salesbound',
  carrinho: 'Carrinho abandonado · Tauk + Logicall',
  high_ticket: 'High ticket',
  sms: 'SMS',
};
// Sub-faixas do bloco SMS (ordem vem de data.smsSubtipos).
const LE_SUB_LABELS = {
  broadcast: 'Broadcast', carrinho: 'Carrinho abandonado',
  upsell: 'Upsell por SMS', direto: 'Link direto',
};
// Por que cada bloco pode não ter linha nenhuma — texto honesto no lugar da tabela vazia.
const LE_TYPE_VAZIO = {
  welcome: 'Sem pedidos Salesbound nesta janela.',
  carrinho: 'Sem carrinhos nem pedidos de recuperação nesta janela.',
  high_ticket: 'Bloco sem fonte de dados: nenhuma plataforma expõe high ticket hoje. O bloco fica na tela de propósito, para a lacuna não sumir.',
  sms: 'Sem vendas de SMS nesta janela.',
};

const LE_COLS = [
  { key: 'oferta',  label: 'Oferta',  align: 'left'  },
  { key: 'pote',    label: 'Pote',    align: 'right' },
  { key: 'vendas',  label: 'Vendas',  align: 'right' },
  { key: 'leads',   label: 'Leads',   align: 'right' },
  { key: 'revenue', label: 'Revenue', align: 'right' },
  { key: 'cogs',    label: 'COGS',    align: 'right' },
  { key: 'profit',  label: 'Profit',  align: 'right' },
  { key: 'rpl',     label: 'RPL',     align: 'right' },
  { key: 'ppl',     label: 'PPL',     align: 'right' },
];

// Paleta REUSADA do Leads.jsx (LD_COLORS, validada em 27/jul pelo validador da dataviz —
// Leads.jsx carrega antes deste arquivo no index.html e o escopo Babel é compartilhado).
// Cor FIXA por tipo de disparo, na ordem da paleta — nunca ciclada, nunca recriada.
const LE_SUB_COLORS = { broadcast: LD_COLORS[0], carrinho: LD_COLORS[1], upsell: LD_COLORS[2], direto: LD_COLORS[3] };

const leFmtDia = (iso) => iso
  ? new Date(iso + 'T12:00:00').toLocaleDateString('pt-BR', { day: '2-digit', month: 'short', year: 'numeric' })
  : '—';

// RPL/PPL vivem na casa dos centavos (receita ÷ leads: $1,26 por lead). USD() arredonda
// para inteiro e apagaria a diferença entre $0,40 e $1,40 — aqui o dinheiro sai com 2
// casas, aplicando o MESMO câmbio (FX.rate) dos formatadores do projeto.
const leMoney2 = (n) => _sym() + (n * FX.rate).toLocaleString(_loc(), { minimumFractionDigits: 2, maximumFractionDigits: 2 });

// Painel de barras "receita por nº de potes" de UM tipo de disparo. São painéis separados
// de propósito (small multiples): as escalas variam muito entre os tipos e um eixo comum
// achataria os menores até virarem risco.
const LeBarPanel = ({ title, color, bars, total, vendas }) => {
  const max = bars.reduce((m, b) => Math.max(m, b.value), 0);
  return (
    <div style={lex.panel}>
      <div style={lex.panelHead}>
        <span style={{ ...lex.panelDot, background: color }} />
        <span style={lex.panelTitle}>{title}</span>
      </div>
      <div style={lex.panelSub}>{USDk(total)} · {NUM(vendas)} vendas</div>
      {bars.length === 0 || max <= 0 ? (
        <div style={lex.panelEmpty}>Sem receita nesta janela.</div>
      ) : (
        <div style={lex.bars}>
          {bars.map(b => (
            <div key={b.pote} style={lex.barCol} title={`${b.pote} pote(s) · ${USD(b.value)}`}>
              <div style={lex.barVal} className="fenix-num">{USDk(b.value)}</div>
              <div style={{ ...lex.bar, background: color, height: `${Math.max(3, (b.value / max) * 100)}%` }} />
              <div style={lex.barLbl} className="fenix-num">{b.pote}</div>
            </div>
          ))}
        </div>
      )}
      <div style={lex.panelAxis}>potes</div>
    </div>
  );
};

const LeadsEconomicsView = () => {
  const [period, setPeriod] = React.useState('30d');
  const [customRange, setCustomRange] = React.useState(null);
  // Seleção MÚLTIPLA de produto: array de famílias canônicas. Vazio = todas (é o default
  // do backend, que não filtra quando `products` chega vazio).
  const [products, setProducts] = React.useState([]);
  const [data, setData] = React.useState(null);
  const [err, setErr] = React.useState(false);
  const [loading, setLoading] = React.useState(true);
  const fullLoading = useFullLoading();
  const withLoad = (fn) => (...a) => { fullLoading.mark(); fn(...a); };
  const retry = useAutoRetry(err);   // falhou → tenta de novo a cada 15s, sozinho

  React.useEffect(() => {
    const began = fullLoading.begin();
    let cancelled = false; setLoading(true);
    fetchLeadsEconomicsAPI({ period, start: customRange?.start, end: customRange?.end, products })
      .then(d => { if (!cancelled) { setData(d); setErr(false); } })
      .catch(() => { if (!cancelled) setErr(true); })
      .finally(() => { if (!cancelled) setLoading(false); fullLoading.end(began); });
    return () => { cancelled = true; };
  }, [period, customRange, products, retry]);

  const rows = (data && data.rows) || [];
  const types = (data && data.types) || ['welcome', 'carrinho', 'high_ticket', 'sms'];
  const subtipos = (data && data.smsSubtipos) || ['broadcast', 'carrinho', 'upsell', 'direto'];
  const filtrado = ((data && data.selectedProducts) || []).length > 0;

  // Agrupamento da tabela: 4 blocos de `type` na ordem do payload; dentro do SMS, uma
  // sub-faixa por tipo de disparo e, dentro dela, as linhas produto × potes. As linhas já
  // chegam ordenadas do backend (oferta por receita desc → type → sub-tipo → pote).
  const blocos = React.useMemo(() => types.map(t => {
    const rs = rows.filter(r => r.type === t);
    return {
      type: t, rows: rs,
      subs: t === 'sms'
        ? subtipos.map(s => ({ key: s, rows: rs.filter(r => r.subtipo === s) }))
        : null,
    };
  }), [data]);

  // Faixa de mini-gráficos: receita por nº de potes, um painel por tipo de disparo.
  const painesSms = React.useMemo(() => {
    const sms = rows.filter(r => r.type === 'sms');
    return subtipos.map(s => {
      const rs = sms.filter(r => r.subtipo === s);
      const by = {};
      rs.forEach(r => { by[r.pote] = (by[r.pote] || 0) + r.revenue; });
      // Potes em ordem crescente; "—" (pote desconhecido) sempre por último.
      const bars = Object.keys(by).sort((a, b) => {
        const an = /^\d+$/.test(a), bn = /^\d+$/.test(b);
        if (an && bn) return Number(a) - Number(b);
        return an ? -1 : (bn ? 1 : 0);
      }).map(p => ({ pote: p, value: by[p] })).filter(b => b.value > 0);
      return { key: s, bars, total: bars.reduce((t, b) => t + b.value, 0),
               vendas: rs.reduce((t, r) => t + r.vendas, 0) };
    });
  }, [data]);

  // 1º load falhou: LoadingScreen offline + retry automático — nunca card estático
  // sem retry (requisito ui-fenix, spec 2026-07-30-reconexao).
  if (err && !data) return (
    <>
      <LoadingScreen conn="offline" />
      <div style={lex.reconNota}>
        Esta tela cruza Salesbound/Logicall, SMS da buygoods e leads do bridge — sem a
        API não há número honesto para mostrar. Reconectando sozinha a cada 15s.
      </div>
    </>
  );
  // Primeiro carregamento: tela padrão do projeto (mesma do gate !hasData do App).
  if (!data) return <LoadingScreen conn="connecting"/>;

  const unmatched = data.unmatched || { ofertas: [], vendas: 0, revenue: 0, leads: 0 };
  const temUnmatched = (unmatched.ofertas || []).length > 0;

  // Célula "—" com o motivo certo no title: a lacuna é informação, não erro.
  const leDash = (motivo) => <span style={lex.dash} title={motivo}>—</span>;
  const semLeads = 'sem fonte de contagem de leads neste bloco (decisão da spec: só leads reais, nunca denominador inventado)';
  const semVenda = 'sem venda na janela — sem venda não há custo nem lucro';

  const leRow = (r, i) => (
    <tr key={r.type + '·' + (r.subtipo || '') + '·' + r.oferta + '·' + r.pote + '·' + i}
      onMouseEnter={e => { e.currentTarget.style.background = 'var(--gray-50)'; }}
      onMouseLeave={e => { e.currentTarget.style.background = 'transparent'; }}>
      <td style={lex.tdOferta}>{r.oferta}</td>
      <td style={lex.tdNum} className="fenix-num">{r.pote === '—' ? leDash('potes não identificados no nome da oferta') : r.pote}</td>
      <td style={lex.tdNum} className="fenix-num">{NUM(r.vendas)}</td>
      <td style={lex.tdNum} className="fenix-num">{r.leads == null ? leDash(semLeads) : NUM(r.leads)}</td>
      <td style={{ ...lex.tdNum, fontWeight: 700 }} className="fenix-num">{USD(r.revenue)}</td>
      <td style={lex.tdNum} className="fenix-num">{r.cogs == null ? leDash(semVenda) : USD(r.cogs)}</td>
      <td style={{ ...lex.tdNum, color: r.profit == null ? undefined : (r.profit >= 0 ? 'var(--positive)' : 'var(--negative)'), fontWeight: 700 }}
        className="fenix-num">{r.profit == null ? leDash(semVenda) : USD(r.profit)}</td>
      <td style={lex.tdNum} className="fenix-num">{r.rpl == null ? leDash(semLeads) : leMoney2(r.rpl)}</td>
      <td style={lex.tdNum} className="fenix-num">{r.ppl == null ? leDash(r.leads == null ? semLeads : semVenda) : leMoney2(r.ppl)}</td>
    </tr>
  );

  return (
    <div style={{ position: 'relative' }}>
      <div style={lex.header}>
        <div>
          <div style={lex.eye}>Canais</div>
          <div style={lex.title}>Leads &amp; economics por oferta</div>
        </div>
        <div style={lex.filterRow}>
          <PeriodSelector period={period} setPeriod={withLoad(setPeriod)}
            customRange={customRange} setCustomRange={withLoad(setCustomRange)} />
          <MultiDropdown icon="cart" label="Produto" values={products} onChange={withLoad(setProducts)}
            options={(data.families || []).map(f => ({ key: f, label: f }))} />
        </div>
      </div>

      {err && data && (
        <div style={lex.notice}><Icon name="alert" size={14} color="currentColor" />
          Falha ao atualizar — mostrando a última janela carregada.</div>
      )}
      {/* Bridge fora não derruba a página: só os leads de carrinho (e o RPL/PPL que
          dependem deles) somem. Receita e economics dos quatro blocos seguem de pé. */}
      {data.bridgeOk === false && (
        <div style={lex.notice}><Icon name="alert" size={14} color="currentColor" />
          BuyGoods Bridge indisponível — os leads de carrinho abandonado (e RPL/PPL) não
          entraram nesta janela. Receita, COGS e Profit continuam corretos.</div>
      )}

      {/* Small multiples: receita por nº de potes, um painel por tipo de disparo de SMS */}
      <div style={lex.panelRow}>
        {painesSms.map(p => (
          <LeBarPanel key={p.key} title={LE_SUB_LABELS[p.key] || p.key} color={LE_SUB_COLORS[p.key] || 'var(--fg-3)'}
            bars={p.bars} total={p.total} vendas={p.vendas} />
        ))}
      </div>

      <div style={lex.tableWrap}>
        <div style={lex.tableHead}>
          <div style={lex.cardEye}>Bridge × fenix · {leFmtDia(data.start)} — {leFmtDia(data.end)}</div>
          <div style={lex.tableTitle}>Oferta × pote, por canal de recuperação</div>
        </div>
        <table style={lex.table}>
          <thead><tr>
            {LE_COLS.map(c => (
              <th key={c.key} style={{ ...lex.th, textAlign: c.align }}>{c.label}</th>
            ))}
          </tr></thead>
          <tbody>
            {blocos.map(b => {
              const trs = [];
              trs.push(
                <tr key={'band-' + b.type}>
                  <td colSpan={LE_COLS.length} style={lex.band}>{LE_TYPE_LABELS[b.type] || b.type}</td>
                </tr>
              );
              if (b.type !== 'sms') {
                b.rows.forEach((r, i) => trs.push(leRow(r, i)));
                // Tauk não expõe produto no dado — entra como uma linha só, o total da
                // janela. Some quando há filtro de produto: não dá para atribuí-la.
                if (b.type === 'carrinho' && !filtrado && (data.tauk?.revenue > 0)) {
                  trs.push(
                    <tr key="tauk">
                      <td style={{ ...lex.tdOferta, color: 'var(--fg-3)' }}
                        title="A Tauk não grava produto no pedido — só o total da janela">Sem oferta identificada · Tauk</td>
                      <td style={lex.tdNum}>{leDash('a Tauk não expõe produto nem potes')}</td>
                      <td style={lex.tdNum}>{leDash('a Tauk não expõe contagem de pedidos por oferta aqui')}</td>
                      <td style={lex.tdNum}>{leDash(semLeads)}</td>
                      <td style={{ ...lex.tdNum, fontWeight: 700 }} className="fenix-num">{USD(data.tauk.revenue)}</td>
                      <td style={lex.tdNum}>{leDash('sem produto não há COGS')}</td>
                      <td style={lex.tdNum}>{leDash('sem produto não há profit')}</td>
                      <td style={lex.tdNum}>{leDash(semLeads)}</td>
                      <td style={lex.tdNum}>{leDash(semLeads)}</td>
                    </tr>
                  );
                }
                if (b.rows.length === 0 && !(b.type === 'carrinho' && !filtrado && data.tauk?.revenue > 0)) {
                  trs.push(
                    <tr key={'vazio-' + b.type}>
                      <td colSpan={LE_COLS.length} style={lex.blocoVazio}>{LE_TYPE_VAZIO[b.type]}</td>
                    </tr>
                  );
                }
              } else {
                // Bloco SMS: sub-faixa por tipo de disparo e, dentro dela, produto × potes.
                b.subs.forEach(s => {
                  const vendas = s.rows.reduce((t, r) => t + r.vendas, 0);
                  const receita = s.rows.reduce((t, r) => t + r.revenue, 0);
                  trs.push(
                    <tr key={'sub-' + s.key}>
                      <td colSpan={LE_COLS.length} style={lex.subBand}>
                        <span style={{ ...lex.subDot, background: LE_SUB_COLORS[s.key] || 'var(--fg-3)' }} />
                        {LE_SUB_LABELS[s.key] || s.key}
                        <span style={lex.subTotal}>{NUM(vendas)} vendas · {USD(receita)}</span>
                      </td>
                    </tr>
                  );
                  if (s.rows.length === 0) {
                    trs.push(
                      <tr key={'sub-vazio-' + s.key}>
                        <td colSpan={LE_COLS.length} style={lex.blocoVazio}>Sem disparo deste tipo com venda ou lead na janela.</td>
                      </tr>
                    );
                  }
                  s.rows.forEach((r, i) => trs.push(leRow(r, i)));
                });
                if (b.rows.length === 0) {
                  trs.push(
                    <tr key="vazio-sms-total">
                      <td colSpan={LE_COLS.length} style={lex.blocoVazio}>{LE_TYPE_VAZIO.sms}</td>
                    </tr>
                  );
                }
              }
              return trs;
            })}
            {!loading && rows.length === 0 && (
              <tr><td colSpan={LE_COLS.length} style={lex.empty}>Sem oferta nesta janela com os filtros atuais.</td></tr>
            )}
          </tbody>
        </table>
        <div style={lex.note}>
          Welcome = pedidos Salesbound (receita bruta) · Carrinho abandonado = carrinhos do
          bridge (leads) + Logicall e Tauk (receita bruta) · SMS = vendas buygoods com
          codename de SMS, por tipo de disparo · COGS e Profit pela régua única do
          product_costs (Profit já desconta frete e reserva de refund). Leads só existem
          onde há contagem real — nos demais blocos a coluna fica "—" de propósito.
        </div>
      </div>

      {/* Lacuna de classificação: ofertas sem família canônica. Sem filtro elas ESTÃO na
          tabela, sob o nome cru — o aviso avisa que somem ao marcar qualquer produto.
          Com filtro elas já sumiram, e o mesmo aviso vira nota discreta dizendo quanto
          ficou de fora — senão o total "encolhe" sem explicação. O backend calcula
          `unmatched` ANTES do filtro, de propósito: a lacuna não pode ser escondida. */}
      {temUnmatched && !filtrado && (
        <div style={lex.gap}>
          <div style={lex.gapTitle}>
            <Icon name="alert" size={14} color="var(--warning)" />
            {NUM(unmatched.vendas)} vendas e {USD(unmatched.revenue)} sem família de produto
          </div>
          <div style={lex.gapBody}>
            Estas ofertas não casam com nenhuma família do catálogo do FENIX (a Salesbound
            vende produtos fora dele). Elas aparecem na tabela sob o nome cru, mas
            <b> desaparecem assim que qualquer produto é marcado no filtro</b> — é lacuna
            real de classificação, não erro de soma. Ofertas: {(unmatched.ofertas || []).join(' · ')}.
            {unmatched.leads > 0 && <> Também há {NUM(unmatched.leads)} leads sem família.</>}
          </div>
        </div>
      )}
      {filtrado && (
        <div style={lex.gapQuiet}>
          Filtro de produto ativo ({(data.selectedProducts || []).join(', ')}).
          {temUnmatched
            ? <> Fora deste recorte ficaram {NUM(unmatched.vendas)} vendas e {USD(unmatched.revenue)} de
                ofertas sem família canônica ({(unmatched.ofertas || []).join(' · ')}) — limpe o filtro
                para vê-las sob o nome cru.</>
            : <> Ofertas sem família canônica ficam sempre de fora quando há filtro de produto.</>}
        </div>
      )}

      <FullLoadingOverlay show={fullLoading.show && !!data} conn="connecting"/>
    </div>
  );
};

const lex = {
  header: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 16, marginBottom: 16, flexWrap: 'wrap' },
  eye: { font: 'var(--text-label)', letterSpacing: '.05em', textTransform: 'uppercase', color: 'var(--fg-3)' },
  title: { font: '800 22px/1.1 var(--font-sans)', color: 'var(--fg-1)', letterSpacing: '-.01em', marginTop: 4 },
  filterRow: { display: 'flex', alignItems: 'center', gap: 10, flexWrap: 'wrap' },
  notice: { display: 'flex', alignItems: 'center', gap: 6, font: '600 12px/1.4 var(--font-sans)', color: 'var(--warning)', background: 'var(--warning-bg)', padding: '10px 16px', borderRadius: 'var(--r-md)', marginBottom: 12 },
  // Faixa de small multiples (um painel por tipo de disparo de SMS)
  panelRow: { display: 'grid', gridTemplateColumns: 'repeat(4,minmax(0,1fr))', gap: 14, marginBottom: 16 },
  panel: { background: '#fff', border: '1px solid var(--border-1)', borderRadius: 'var(--r-lg)', boxShadow: 'var(--shadow-sm)', padding: '14px 16px 10px', minWidth: 0 },
  panelHead: { display: 'flex', alignItems: 'center', gap: 8 },
  panelDot: { width: 9, height: 9, borderRadius: 2, flex: 'none' },
  panelTitle: { font: '700 10.5px/1 var(--font-sans)', letterSpacing: '.05em', textTransform: 'uppercase', color: 'var(--fg-3)', whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' },
  panelSub: { font: '800 17px/1.2 var(--font-num)', color: 'var(--fg-1)', fontVariantNumeric: 'tabular-nums', marginTop: 8 },
  panelEmpty: { height: 96, display: 'flex', alignItems: 'center', justifyContent: 'center', font: 'var(--text-body-sm)', color: 'var(--fg-4)' },
  panelAxis: { font: '500 10px/1 var(--font-sans)', color: 'var(--fg-4)', textAlign: 'right', marginTop: 6 },
  bars: { display: 'flex', alignItems: 'flex-end', gap: 6, height: 96, marginTop: 10 },
  barCol: { flex: 1, minWidth: 0, display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'flex-end', height: '100%' },
  bar: { width: '100%', borderRadius: '4px 4px 0 0', minHeight: 3 },
  barVal: { font: '600 9.5px/1 var(--font-num)', color: 'var(--fg-3)', fontVariantNumeric: 'tabular-nums', marginBottom: 4, whiteSpace: 'nowrap' },
  barLbl: { font: '700 10px/1 var(--font-num)', color: 'var(--fg-2)', fontVariantNumeric: 'tabular-nums', marginTop: 5 },
  // Tabela padrão (molde ldx) — SEM minWidth e sem overflowX: em largura cheia as 9
  // colunas cabem, e rolagem lateral esconderia justamente Profit/RPL/PPL.
  tableWrap: { background: '#fff', border: '1px solid var(--border-1)', borderRadius: 'var(--r-lg)', boxShadow: 'var(--shadow-sm)', overflow: 'hidden' },
  tableHead: { padding: '16px 18px 12px' },
  cardEye: { font: 'var(--text-label)', letterSpacing: '.05em', textTransform: 'uppercase', color: 'var(--fg-3)' },
  tableTitle: { font: 'var(--text-h3)', color: 'var(--fg-1)', marginTop: 4 },
  table: { width: '100%', borderCollapse: 'collapse', tableLayout: 'auto' },
  th: { font: '700 10.5px/1 var(--font-sans)', letterSpacing: '.04em', textTransform: 'uppercase', color: 'var(--fg-3)', padding: '11px 12px', background: 'var(--bg-sunken)', borderTop: '1px solid var(--border-2)', borderBottom: '1px solid var(--border-2)', whiteSpace: 'nowrap', userSelect: 'none' },
  band: { padding: '10px 14px', background: 'var(--fenix-navy)', color: 'var(--fenix-cream)', font: '700 10.5px/1 var(--font-sans)', letterSpacing: '.06em', textTransform: 'uppercase', textAlign: 'left' },
  subBand: { padding: '9px 14px 9px 26px', background: 'var(--bg-sunken)', borderBottom: '1px solid var(--border-1)', font: '700 11px/1 var(--font-sans)', letterSpacing: '.03em', color: 'var(--fg-2)', textAlign: 'left' },
  subDot: { display: 'inline-block', width: 8, height: 8, borderRadius: 2, marginRight: 8, verticalAlign: 'middle' },
  subTotal: { float: 'right', font: '600 11px/1 var(--font-num)', color: 'var(--fg-3)', fontVariantNumeric: 'tabular-nums' },
  tdOferta: { padding: '10px 12px', borderBottom: '1px solid var(--border-1)', font: '600 12.5px/1.35 var(--font-sans)', color: 'var(--fg-1)', textAlign: 'left' },
  tdNum: { padding: '10px 12px', borderBottom: '1px solid var(--border-1)', font: 'var(--text-body-sm)', color: 'var(--fg-1)', whiteSpace: 'nowrap', textAlign: 'right', fontFamily: 'var(--font-num)', fontVariantNumeric: 'tabular-nums' },
  dash: { color: 'var(--fg-4)', cursor: 'help' },
  blocoVazio: { padding: '14px 14px 14px 26px', borderBottom: '1px solid var(--border-1)', font: 'var(--text-body-sm)', color: 'var(--fg-4)', textAlign: 'left' },
  empty: { padding: '34px 22px', textAlign: 'center', font: 'var(--text-body-sm)', color: 'var(--fg-4)' },
  note: { padding: '12px 18px 16px', font: '500 11px/1.6 var(--font-sans)', color: 'var(--fg-4)' },
  // Aviso da lacuna de classificação (unmatched)
  gap: { marginTop: 14, background: 'var(--warning-bg)', border: '1px solid var(--warning)', borderRadius: 'var(--r-md)', padding: '12px 16px' },
  gapTitle: { display: 'flex', alignItems: 'center', gap: 7, font: '700 12.5px/1.3 var(--font-sans)', color: 'var(--warning)' },
  gapBody: { font: '500 11.5px/1.6 var(--font-sans)', color: 'var(--fg-2)', marginTop: 6 },
  gapQuiet: { marginTop: 14, font: '500 11.5px/1.5 var(--font-sans)', color: 'var(--fg-4)' },
  reconNota: { textAlign: 'center', font: 'var(--text-body-sm)', color: 'var(--fg-4)', maxWidth: 520, margin: '-28px auto 0' },
};

window.LeadsEconomicsView = LeadsEconomicsView;
