/**
 * Dashboard — o que está feito e o que falta fazer.
 *
 * Tinha quatro KPIs a zeros e um comentário a dizer "Fase 6". Dois deles nem
 * tabela tinham por baixo — publicações e sessões do ponto de venda — e
 * mostrá-los a zero não era um dado: era uma promessa disfarçada de número.
 *
 * ## Metade disto não são números, são coisas por fazer
 *
 * Um dashboard de contagens diz como está. Quem abre isto de manhã quer saber
 * **o que tem de fazer a seguir**, e por isso as pendências vêm primeiro, cada
 * uma com o sítio onde se resolve. Quando não há nenhuma, diz-se — e aí sim, os
 * números são o que interessa.
 *
 * ## O que ainda não se mede aparece pelo nome
 *
 * Um zero e um "ainda não medimos isto" são coisas diferentes, e um dashboard
 * que as confunde ensina a desconfiar de todos os outros números. O que falta
 * ligar está numa lista à parte, a dizer o que falta.
 *
 * ## A forma é a do Efferd (dashboard-11), o conteúdo não
 *
 * A grelha, os KPIs com ícone e o cartão de "AI insight" vêm do bloco. O que
 * **não** veio: as setas de variação (+5,8% face ao mês anterior). Não há
 * histórico para as calcular, e uma seta inventada é exactamente o número
 * disfarçado que este ecrã deixou de mostrar. O "insight" também não é um
 * texto de exemplo — é o Agente PM a dizer o que o trava, a partir das
 * pendências verdadeiras.
 */

const DASH_NUMEROS = [
  { chave: 'campanhas_activas', rotulo: 'Activas', Icone: IconCampanhas },
  { chave: 'lojas', rotulo: 'Lojas', Icone: IconLojas },
  { chave: 'paineis', rotulo: 'Painéis instalados', Icone: IconPaineis },
];


// O cartão do Agente PM, o número dos dados confirmados e o painel tracejado «Resultados» saíram
// (advogado da simplicidade, 3 Nov): o lugar é dos vídeos, e o tracejado mostrava o que ainda não se faz.

/**
 * De quem é a vez (lib/fila.js, 24 Set): o que é contigo, cada um com o botão
 * que leva onde se resolve; o que está com a NetScreen e com a app, com desde
 * quando, para a espera não parecer silêncio (parecer do Codex, secção 4).
 */
function DashFila({ slug, tua, responsavel = [], netscreen, sistema, novidades: todas = [], onIr }) {
  const [vistas, setVistas] = React.useState(() => novidadesVistas(slug));
  const novidades = todas.filter((n) => !vistas.includes(chaveDaNovidade(n)));
  const quando = (d) => (d ? `desde ${formatDataCurta(d)}` : '');
  return (
    <div className="relative z-[1] mt-3 space-y-5 pb-4">
      {tua.length > 0 && (
        <ul>
          {tua.map((item) => (
            <li key={item.chave} className="flex flex-wrap items-baseline justify-between gap-3 border-t border-border px-4 py-3 lg:px-5">
              <div className="min-w-0">
                <p className="text-sm text-foreground">{item.texto}</p>
                {(item.motivo || item.desde) && (
                  <p className="mt-0.5 text-xs text-muted-foreground">{[item.motivo, quando(item.desde)].filter(Boolean).join(' · ')}</p>
                )}
              </div>
              {item.rota && (
                <button
                  type="button"
                  onClick={() => onIr(item.rota)}
                  className="ns-btn ns-btn-sm ns-btn-suave shrink-0 text-xs"
                >
                  {item.accao || 'Resolver'}
                </button>
              )}
            </li>
          ))}
        </ul>
      )}
      {/* O perfil Leitura (becos de 9 Out, ponto 15): o que é do cliente, sem botão, e a quem cabe. */}
      {responsavel.length > 0 && (
        <div className="px-4 lg:px-5">
          <p className="text-xs text-muted-foreground">Com o responsável da conta</p>
          <ul className="mt-1 space-y-0.5">
            {responsavel.map((item) => (
              <li key={item.chave} className="text-sm text-foreground">
                {item.texto}
                {item.desde && <span className="text-xs text-muted-foreground">{` · ${quando(item.desde)}`}</span>}
              </li>
            ))}
          </ul>
          <SoQuemPode className="mt-1" accao="tratar disto" />
        </div>
      )}
      {novidades.length > 0 && (
        <ul className="space-y-1 px-4 lg:px-5">
          {novidades.map((item) => (
            <li key={item.chave} className="flex flex-wrap items-baseline justify-between gap-2 border-l-2 border-primary pl-3 text-sm text-foreground">
              <span>{item.texto}{item.desde && <span className="text-xs text-muted-foreground">{` · ${quando(item.desde)}`}</span>}</span>
              {item.rota && (
                <button type="button" onClick={() => { onIr(item.rota); if (window.location.pathname === item.rota) { marcarNovidadeVista(slug, item); setVistas(novidadesVistas(slug)); } }} className="text-xs text-muted-foreground underline underline-offset-2 hover:text-foreground">
                  {item.accao || 'Ver'}
                </button>
              )}
            </li>
          ))}
        </ul>
      )}
      {[['À espera da NetScreen', netscreen], ['A app está a tratar', sistema]].filter(([, l]) => l.length).map(([titulo, lista]) => (
        <div key={titulo} className="px-4 lg:px-5">
          <p className="text-xs text-muted-foreground">{titulo}</p>
          <ul className="mt-1 space-y-0.5">
            {lista.map((item) => (
              <li key={item.chave} className="flex flex-wrap items-center gap-x-2 text-sm text-foreground">
                {/* O que a app está a fazer gira (8 Out); o que espera pela NetScreen, não. */}
                {lista === sistema && item.chave !== 'parado' && !item.parado && <ATrabalhar className="text-primary" />}
                {item.texto.charAt(0).toUpperCase() + item.texto.slice(1)}
                {item.desde && <span className="text-xs text-muted-foreground">{` · ${quando(item.desde)}`}</span>}
              </li>
            ))}
          </ul>
          <p className="mt-1 text-xs text-muted-foreground">Não precisas de fazer nada.</p>
        </div>
      ))}
    </div>
  );
}

/**
 * "Começar" (28 Set, avaliação de UX, mudança 1): para quem ainda não tem
 * campanhas, ou tem a marca por dar ou por confirmar, o Início é um percurso de
 * três passos, e não quatro zeros, créditos e espaço. Os estados vêm do que já
 * existe: o `onboarding` de `GET /api/:slug/marca` e a lista das campanhas.
 *
 * O passo seguinte fica em destaque, com o único botão principal da página; os
 * feitos ficam com visto e uma ligação discreta, para se poder voltar.
 */
function passosComecar(slug, onboarding, totalCampanhas, criarActivo = false) {
  const feito = (chave) => (onboarding.find((o) => o.chave === chave) || {}).feito;
  // As cores e o logótipo confirmados também são a marca dada: quem carregou só o logótipo e confirmou a
  // identidade continuava com o passo 1 por fazer (agentes, 20 Out).
  const kit = Boolean(feito('kit'));
  const passos = [
    {
      chave: 'dar',
      titulo: 'Dar a tua marca',
      texto: 'O manual de normas, o logótipo, umas fotografias ou o site. Lemos tudo e propomos como a marca aparece e como fala.',
      feito: Boolean(feito('kb') || feito('manual') || kit),
      botao: 'Carregar os ficheiros',
      rota: `/${slug}/marca/fontes`,
    },
    {
      chave: 'confirmar',
      titulo: 'Confirmar a tua marca',
      // Com a identidade confirmada e a voz por confirmar, diz-se o que falta: o passo parecia não ter mexido.
      texto: kit
        ? 'As cores e o logótipo estão confirmados. Falta confirmar a maneira de falar.'
        : 'Vês a marca inteira num exemplo, com as cores, o logótipo e a maneira de falar, e confirmas de uma vez.',
      feito: Boolean(kit && feito('voz')),
      botao: 'Ver e confirmar a marca',
      rota: `/${slug}/marca/resumo`,
    },
    // Com o Criar activo, o primeiro passo é o vídeo: dois caminhos para começar confundiam (utilizadores
    // virgens, 3 Nov: «Criar vídeo» e «Nova campanha» não falavam um com o outro).
    criarActivo ? {
      chave: 'campanha',
      titulo: 'Criar o primeiro vídeo',
      texto: 'Dizes o que queres promover e juntas uma fotografia ou o site. A plataforma faz o vídeo para as redes e para os painéis; revês e aprovas.',
      feito: totalCampanhas > 0,
      botao: 'Criar o vídeo',
      rota: `/${slug}/criar`,
    } : {
      chave: 'campanha',
      titulo: 'Criar a primeira campanha',
      texto: 'Dizes o que queres comunicar, como o dirias a um colega. O Agente PM escreve os anúncios para as redes e para os painéis.',
      feito: totalCampanhas > 0,
      botao: 'Criar a campanha',
      // Abre logo o briefing de uma campanha nova, que é uma página (28 Set).
      rota: `/${slug}/campanhas/nova`,
    },
  ];
  const seguinte = passos.find((x) => !x.feito);
  // Uma campanha criada antes de a marca estar dada e confirmada não fecha o passo 3 à frente dos outros
  // («1 de 3 feitos» com só o 3 feito, agentes, 20 Out): diz que já existe, e conta quando lá se chegar.
  const campanha = passos[2];
  if (campanha.feito && seguinte && seguinte !== campanha) {
    campanha.feito = false;
    campanha.adiantado = `/${slug}/campanhas`;
    campanha.texto = 'Já há uma campanha criada. Os anúncios saem com a marca: dá-a e confirma-a primeiro.';
  }
  return { passos, seguinte: seguinte ? seguinte.chave : null };
}

function DashComecar({ passos, seguinte, onIr, podeMexer = true }) {
  const feitos = passos.filter((x) => x.feito).length;
  return (
    <section className="border border-border bg-card p-5 lg:p-6">
      <div className="flex flex-wrap items-baseline justify-between gap-2">
        <h2 className="text-lg font-semibold tracking-tight text-foreground">Começar</h2>
        <span className="text-xs tabular-nums text-muted-foreground">{`${feitos} de ${passos.length} feitos`}</span>
      </div>
      <p className="mt-1 text-sm text-muted-foreground">Três passos até ao primeiro anúncio.</p>
      <ol className="mt-5 space-y-3">
        {passos.map((x, i) => {
          const eSeguinte = x.chave === seguinte;
          return (
            <li
              key={x.chave}
              className={`flex gap-4 rounded-md border p-4 ${
                eSeguinte
                  ? 'border-[hsl(var(--primary)/0.6)] bg-[hsl(var(--primary)/0.07)]'
                  : 'border-border'
              }`}
              aria-current={eSeguinte ? 'step' : undefined}
            >
              <span
                className={`flex h-7 w-7 shrink-0 items-center justify-center rounded-full text-xs font-semibold ${
                  x.feito
                    ? 'bg-[hsl(var(--online)/0.18)] text-[hsl(var(--online))]'
                    : eSeguinte
                      ? 'bg-primary text-primary-foreground'
                      : 'bg-secondary text-muted-foreground'
                }`}
                aria-hidden="true"
              >
                {x.feito ? <IconCheck size={14} /> : i + 1}
              </span>
              <div className="min-w-0 flex-1">
                <p className={`text-sm font-medium ${x.feito ? 'text-muted-foreground' : 'text-foreground'}`}>
                  {x.titulo}
                  {x.feito && <span className="sr-only"> (feito)</span>}
                </p>
                {!x.feito && <p className="mt-1 max-w-prose text-sm leading-relaxed text-muted-foreground">{x.texto}</p>}
                {x.adiantado && (
                  <button type="button" onClick={() => onIr(x.adiantado)} className="mt-2 min-h-10 text-xs text-muted-foreground underline underline-offset-2 hover:text-foreground">
                    Ver as campanhas
                  </button>
                )}
                {/* O perfil Leitura não dá os passos: diz-se a quem cabem (becos de 9 Out, ponto 15). */}
                {eSeguinte && !podeMexer && <SoQuemPode className="mt-3" accao="dar este passo" />}
                {eSeguinte && podeMexer && (
                  <button
                    type="button"
                    onClick={() => onIr(x.rota)}
                    className="mt-3 bg-primary px-4 py-2 text-sm text-primary-foreground"
                  >
                    {x.botao}
                  </button>
                )}
              </div>
              {x.feito && (
                // Alvo de 40 px no telemóvel: o «Ver» tinha 19×16 (agentes, 20 Out).
                <button type="button" onClick={() => onIr(x.rota)} className="flex min-h-10 min-w-10 shrink-0 items-center justify-center self-center text-xs text-muted-foreground underline-offset-2 hover:text-foreground">
                  Ver
                </button>
              )}
            </li>
          );
        })}
      </ol>
    </section>
  );
}

// Os vídeos do Criar que ainda estão a ser feitos (15 a 40 min no worker).
const emCriacaoDash = (videos) => (Array.isArray(videos) ? videos.filter((v) => v.estado === 'fila' || v.estado === 'a_criar') : []);

/**
 * Um vídeo do Criar a ser feito. Na segunda visita o Início dizia «Não há nada à tua espera» e mais
 * nada, com um vídeo a meio (cliente simulado, 3 Nov): agora diz que está a ser criado e abre-o.
 */
function DashACriar({ slug, videos, onIr, parado = false }) {
  if (!videos.length || parado) return null; // parado: a linha da fila já diz que está à espera e porquê
  const v = videos[0];
  return (
    <div className="flex flex-wrap items-center justify-between gap-3 border-t border-border px-4 py-3 lg:px-5">
      <p role="status" className="flex items-center gap-2 text-sm text-foreground">
        <ATrabalhar className="text-primary" />
        {/* Na fila ainda não está a ser feito (utilizadora virgem, 4 Nov). */}
        {videos.length === 1
          ? (v.estado === 'fila' ? 'Um vídeo está na fila, à espera de vez.' : 'Um vídeo está a ser criado.')
          : `${videos.length} vídeos estão na fila ou a ser criados.`}
        {!v.campanhaId && <span className="text-xs text-muted-foreground">Entretanto podes escrever o post da campanha.</span>}
      </p>
      <button type="button" onClick={() => onIr(`/${slug}/criar?video=${encodeURIComponent(v.id)}`)}
        className="text-xs text-muted-foreground underline underline-offset-2 hover:text-foreground">
        {videos.length === 1 ? 'Abrir' : 'Abrir o mais recente'}
      </button>
    </div>
  );
}

/**
 * Os clientes de uma agência, no Início dela, com o que espera em cada um (ronda 13 dos virgens: a agência
 * entrava, via «Dar a tua marca» e «Nada à tua espera», e os clientes tinham vídeos e campanhas por aprovar).
 */
function DashClientesAgencia({ clientes, onNavegar }) {
  const [vez, setVez] = React.useState({});
  React.useEffect(() => {
    let vivo = true;
    Promise.all(clientes.map((c) => api.get(`/api/${c.slug}/fila`).then((f) => [c.slug, (f.cliente || []).length + (f.novidades || []).length]).catch(() => [c.slug, null])))
      .then((pares) => { if (vivo) setVez(Object.fromEntries(pares)); });
    return () => { vivo = false; };
  }, [clientes.map((c) => c.slug).join(',')]);
  if (!clientes.length) return null;
  return (
    <section aria-labelledby="dash-clientes" className="space-y-3">
      <h2 id="dash-clientes" className="text-lg text-foreground">Os teus clientes</h2>
      <p className="text-sm text-muted-foreground">Cada cliente tem a sua marca, campanhas e vídeos. Entra num para trabalhar nele; a marca da agência fica aqui.</p>
      <ul className="divide-y divide-border border border-border bg-card">
        {clientes.map((c) => (
          <li key={c.slug} className="flex flex-wrap items-center justify-between gap-3 px-4 py-3">
            <span className="text-sm text-foreground">{c.nome}
              <span className="ml-2 text-xs text-muted-foreground">{vez[c.slug] == null ? '' : vez[c.slug] === 0 ? 'nada à tua espera' : `${vez[c.slug]} ${vez[c.slug] === 1 ? 'coisa' : 'coisas'} à tua espera`}</span>
            </span>
            <button type="button" onClick={() => onNavegar(`/${c.slug}/dashboard`)} className="ns-btn ns-btn-sm ns-btn-secundario">Entrar</button>
          </li>
        ))}
      </ul>
    </section>
  );
}

function ScreenDashboard({ slug, podeMexer = true, onNavegar, criarActivo = false, clientesDaAgencia = [] }) {
  const [dados, setDados] = React.useState(null);
  const [erro, setErro] = React.useState(null);
  const [totalCampanhas, setTotalCampanhas] = React.useState(null);
  const [onboarding, setOnboarding] = React.useState(null);
  const [aCriar, setACriar] = React.useState([]);
  const [videos, setVideos] = React.useState([]);
  const lerVideos = (v) => { setACriar(emCriacaoDash(v)); setVideos(Array.isArray(v) ? v : []); };

  // A vez muda do outro lado (a NetScreen revê, uma entrega chega): lê-se outra
  // vez de 30 em 30 segundos com o ecrã à vista, e ao voltar à janela.
  // Os vídeos em criação lêem-se junto: quando um fica pronto, a linha sai sem recarregar.
  const lerOutraVez = React.useCallback(() => {
    api.get(`/api/${slug}/criar/videos`).then(lerVideos).catch(() => {});
    return api.get(`/api/${slug}/dashboard`).then(setDados);
  }, [slug]);
  const actualizacaoFalhou = useActualizar(lerOutraVez, { activo: Boolean(dados) });

  React.useEffect(() => {
    setDados(null);
    setErro(null);
    setTotalCampanhas(null);
    setOnboarding(null);
    setACriar([]);
    setVideos([]);
    // O estado da marca decide se o Início é o "Começar". Se falhar, fica o
    // dashboard de sempre.
    api
      .get(`/api/${slug}/marca`)
      .then((r) => setOnboarding(r.onboarding || []))
      .catch(() => setOnboarding(false));
    api
      .get(`/api/${slug}/dashboard`)
      .then(setDados)
      .catch((e) => setErro(e.message || 'Não foi possível ler o dashboard.'));
    // Quantas campanhas há: o "Começar" muda quando nasce a primeira. As barras
    // por estado saíram (28 Set); a contagem não precisa delas.
    // As criações do Criar contam também: com três vídeos feitos e nenhum aprovado ainda, o Início dizia
    // «Criar a primeira campanha» (ensaio NESCAFÉ 3, 3 Nov). Sem o Criar activo, conta zero.
    Promise.all([
      api.get(`/api/${slug}/campanhas`),
      api.get(`/api/${slug}/criar/videos`).catch(() => []),
    ])
      .then(([r, videos]) => {
        setTotalCampanhas(r.campanhas.length + (Array.isArray(videos) ? videos.length : 0));
        lerVideos(videos);
      })
      .catch(() => setTotalCampanhas(false));
  }, [slug]);

  const cabecalho = (
    <header>
      <h1 className="mt-1 font-display text-4xl uppercase leading-none tracking-wide">
        {dados ? dados.marca : 'Início'}
      </h1>
      {clientesDaAgencia.length > 0 && <div className="mt-6"><DashClientesAgencia clientes={clientesDaAgencia} onNavegar={onNavegar} /></div>}
    </header>
  );

  if (erro) {
    return (
      <div className="mx-auto max-w-6xl space-y-6">
        {cabecalho}
        <div className="border border-border bg-card px-6 py-12 text-center">
          <p className="font-display text-xl tracking-wide">Não deu para ler</p>
          <p className="mt-2 text-sm text-muted-foreground">{erro}</p>
        </div>
      </div>
    );
  }

  // Espera-se pelas três leituras antes de escolher entre o "Começar" e o
  // dashboard, para não mostrar um e trocar pelo outro.
  if (!dados || onboarding === null || totalCampanhas === null) {
    return (
      <div className="mx-auto max-w-6xl space-y-6">
        {cabecalho}
        <SkeletonKpis />
      </div>
    );
  }

  const { numeros, espaco, fila } = dados;
  const limite = numeros.max_campanhas_activas;
  // Os créditos e as lojas já vêm na fila, à frente (api/dashboard.js).
  const tua = fila.cliente;
  const irPara = (rota) => onNavegar(rota.startsWith('/') ? rota : `/${slug}/${rota}`);

  const comecar = onboarding ? passosComecar(slug, onboarding, totalCampanhas || 0, criarActivo) : null;
  const emComecar = Boolean(comecar && totalCampanhas !== false
    && (totalCampanhas === 0 || comecar.passos.slice(0, 2).some((x) => !x.feito)));
  // O vídeo em criação já tem a sua linha (DashACriar): a da campanha a dizer o mesmo saía repetida (ronda 14).
  // Com o processamento parado a linha fica (diz porquê), e a do DashACriar perde a roda (revisor, 4 Nov).
  const processamentoParado = fila.sistema.some((i) => i.parado);
  const sistemaSemVideo = aCriar.length && !processamentoParado ? fila.sistema.filter((i) => !/: o vídeo está (na fila|a ser criado|à espera)/.test(i.texto || '')) : fila.sistema;
  const haFila = aCriar.length > 0 || tua.length > 0 || (fila.responsavel || []).length > 0 || fila.netscreen.length > 0 || fila.sistema.length > 0 || fila.novidades.length > 0;

  if (emComecar) {
    return (
      <div className="space-y-6">
        {cabecalho}
        <DashComecar passos={comecar.passos} seguinte={comecar.seguinte} onIr={irPara} podeMexer={podeMexer} />
        {/* O que já estiver à espera não se esconde por estar no "Começar". */}
        {haFila && (
          <Painel>
            <PainelMeta className="pb-0">
              <PainelTitulo Icone={IconAlerta}>À tua espera</PainelTitulo>
              <span className="font-mono text-xs tabular-nums text-muted-foreground">{tua.length}</span>
            </PainelMeta>
            <DashACriar slug={slug} videos={aCriar} onIr={irPara} parado={processamentoParado} />
            <DashFila slug={slug} tua={tua} responsavel={fila.responsavel || []} netscreen={fila.netscreen} sistema={sistemaSemVideo} novidades={fila.novidades} onIr={irPara} />
          </Painel>
        )}
      </div>
    );
  }

  return (
    <div className="space-y-9">
      {cabecalho}

      {/* De quem é a vez, primeiro: é o que se faz a seguir. No telemóvel ficava
          abaixo de quatro cartões de números (cliente simulado, 24 Set). */}
      <Painel>
        <PainelMeta className="pb-0">
          <PainelTitulo Icone={IconAlerta}>À tua espera</PainelTitulo>
          <span className="font-mono text-xs tabular-nums text-muted-foreground">{tua.length}</span>
        </PainelMeta>
        {actualizacaoFalhou && (
          <p className="px-4 pt-2 text-xs text-accent lg:px-5" role="status">Não deu para actualizar agora; voltamos a tentar sozinhos.</p>
        )}
        <DashACriar slug={slug} videos={aCriar} onIr={irPara} parado={processamentoParado} />
        {tua.length === 0 && !aCriar.length && !(fila.responsavel || []).length && !fila.netscreen.length && !fila.sistema.length && !fila.novidades.length ? (
          <PainelConteudo>
            <p className="text-sm text-foreground">Não há nada à tua espera.</p>
          </PainelConteudo>
        ) : (
          <>
            {tua.length === 0 && (
              <PainelConteudo>
                <p className="text-sm text-foreground">Não há nada à tua espera.</p>
              </PainelConteudo>
            )}
            <DashFila slug={slug} tua={tua} responsavel={fila.responsavel || []} netscreen={fila.netscreen} sistema={sistemaSemVideo} novidades={fila.novidades} onIr={irPara} />
          </>
        )}
      </Painel>

      {/* O que a plataforma fez, à vista: as capas dos vídeos (crítica visual, 3 Nov). */}
      {videosComCapa(videos).length > 0 && (
        <section className="space-y-4">
          <div className="flex items-baseline justify-between gap-3">
            <h2 className="text-lg text-foreground">Os teus vídeos</h2>
            {/* A lista fica no fundo do Criar: o Criar desce até ela quando a tiver (parceira, Codex X-46). */}
            <button type="button" onClick={() => irPara('criar?ver=criacoes')}
              className="flex items-center gap-1.5 text-xs text-muted-foreground outline-none transition-colors hover:text-foreground focus-visible:ring-1 focus-visible:ring-accent">
              Ver todas as criações
              <IconSeta size={14} />
            </button>
          </div>
          <MontraVideos slug={slug} videos={videos} onAbrir={(v) => irPara(`/${slug}/criar?video=${encodeURIComponent(v.id)}`)} />
        </section>
      )}

      {/* Os números — o `stats` do dashboard-11: ícone, rótulo, número grande. */}
      <Painel className="grid grid-cols-3 gap-px bg-[var(--border)]">
        {DASH_NUMEROS.map((k) => (
          <div key={k.chave} className="flex flex-col bg-background p-5">
            <k.Icone size={18} className="text-primary" />
            <span className="mt-3 text-xs text-muted-foreground">{k.rotulo}</span>
            <span className="mt-1 font-display text-5xl leading-none tracking-wide tabular-nums">
              {formatNumero(numeros[k.chave])}
              {k.chave === 'campanhas_activas' && limite !== null && (
                <span className="ml-1.5 font-sans text-sm tracking-normal text-muted-foreground">de {limite}</span>
              )}
            </span>
          </div>
        ))}
      </Painel>

    </div>
  );
}
