/* NetFibra Tech — componentes compartilhados
   ---------------------------------------------------------------------
   Botão, campo, cartão, indicador, selo, tabela, estados (vazio,
   carregando, erro), alerta e paginação: cada um declarado UMA vez,
   aqui, por cima de tokens.css e da casca (shell.css). As folhas de
   página só posicionam; não redesenham o componente.

   Antes deste arquivo cada família era declarada em três ou quatro folhas
   ao mesmo tempo e o que chegava à tela dependia da ordem de carga: o
   mesmo botão primário media 38px na dashboard, 40px em relatórios, 42px
   no cockpit e 44px na auditoria. A medição das telas renderizadas achou
   11 alturas de botão, 8 desenhos de campo e 11 de cartão.

   Regras que valem para todas as peças:

   - geometria em dois degraus: 40px é o controle padrão (confortável no
     mouse, aceitável no toque) e 32px o compacto, para dentro de linha
     de tabela e cabeçalho de cartão;
   - caixa alta só em kicker (a palavrinha acima do título) e no selo de
     estado. Botão, rótulo de campo, cabeçalho de tabela e rótulo de
     indicador são em caixa de frase: caixa alta por toda parte vira
     ruído e esconde a hierarquia que ela pretendia marcar;
   - peso 600 marca; 700 é só número de KPI, título de página e <strong>;
   - toda cor vem de tokens.css. Esta folha não conhece hexadecimal. */

/* ---------- Tipografia base ----------
   <b> e <strong> nascem com font-weight:bolder no navegador, que é
   RELATIVO: dentro de um bloco já em 800 o navegador sobe para 900 e o
   texto fecha o contraforma das letras. Fixar o degrau de destaque do
   sistema resolve sem precisar caçar cada ocorrência. */
b,
strong {
  font-weight: var(--nf-weight-bold);
}

code,
pre,
kbd,
samp {
  font-family: var(--nf-font-mono);
}

/* =====================================================================
   BOTÃO (Button)
   ---------------------------------------------------------------------
   Quatro variantes, uma forma:

     .nf-primary-button    a ação principal da tela — uma por tela
     .nf-secondary-button  as demais ações com corpo
     .nf-ghost-button      ação discreta, sem moldura (cabeçalho de
                           cartão, linha de tabela, "Limpar")
     .nf-danger-button     ação destrutiva: apagar, revogar, descartar

   Modificadores: .nf-btn--sm (32px), .nf-btn--icon (quadrado, só
   ícone, exige aria-label), .nf-btn--block (ocupa a largura).

   O rótulo é em caixa de frase ("Salvar configuração", não "SALVAR
   CONFIGURAÇÃO") e sem espaçamento entre letras: caixa alta em 12px era
   o que fazia os botões parecerem gritar e, pior, igualava a ação
   principal às secundárias.
   ===================================================================== */
.nf-primary-button,
.nf-secondary-button,
.nf-ghost-button,
.nf-danger-button {
  min-height: var(--nf-control-h);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--nf-space-2);
  padding: var(--nf-pad-control);
  border: 1px solid transparent;
  border-radius: var(--nf-radius-sm);
  font-family: inherit;
  font-size: var(--nf-fs-body);
  font-weight: var(--nf-weight-strong);
  letter-spacing: 0;
  line-height: 1.2;
  text-transform: none;
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
  transition: background var(--nf-transition), border-color var(--nf-transition), color var(--nf-transition);
}

.nf-primary-button svg,
.nf-secondary-button svg,
.nf-ghost-button svg,
.nf-danger-button svg {
  width: 16px;
  height: 16px;
  flex: 0 0 16px;
  fill: currentColor;
}

/* Primário: a única ação cheia da cor da marca em cada tela. Usa a
   marca em força total, não o degrau rebaixado — o degrau existe para
   área grande (barra, anel, faixa), e um botão não é isso. */
.nf-primary-button {
  background: var(--nf-primary);
  color: var(--nf-on-primary);
}

.nf-primary-button:hover { background: var(--nf-primary-hover); }
.nf-primary-button:active { background: var(--nf-primary-active); }

/* Secundário: mesma forma, moldura em vez de preenchimento. */
.nf-secondary-button {
  border-color: var(--nf-border-strong);
  background: var(--nf-surface-2);
  color: var(--nf-text);
}

.nf-secondary-button:hover {
  border-color: var(--nf-primary-line);
  background: var(--nf-surface-3);
}

.nf-secondary-button:active { background: var(--nf-active); }

/* Fantasma: só o texto, ganha fundo ao passar o mouse. */
.nf-ghost-button {
  background: transparent;
  color: var(--nf-text-soft);
}

.nf-ghost-button:hover {
  background: var(--nf-hover);
  color: var(--nf-text);
}

.nf-ghost-button:active { background: var(--nf-active); }

/* Perigo: vermelho de significado, nunca a marca. Rebaixado por padrão
   (fundo suave) para não disputar com o primário; o diálogo de
   confirmação é quem dá o peso. */
.nf-danger-button {
  border-color: var(--nf-danger-line);
  background: var(--nf-danger-soft);
  color: var(--nf-danger);
}

.nf-danger-button:hover { background: rgba(var(--nf-red-rgb), .2); }
.nf-danger-button:active { background: rgba(var(--nf-red-rgb), .26); }

/* Botão que funciona como chip de filtro: o pressionado ganha a tinta da
   marca, a mesma linguagem do "Editar layout" ativo. */
.nf-secondary-button[aria-pressed="true"],
.nf-ghost-button[aria-pressed="true"] {
  border-color: var(--nf-primary-line);
  background: var(--nf-primary-soft);
  color: var(--nf-text);
}

/* Compacto: ações dentro de linhas de tabela e de cabeçalhos. */
.nf-btn--sm {
  min-height: var(--nf-control-h-sm);
  padding: 0 var(--nf-space-3);
  font-size: var(--nf-fs-caption);
}

.nf-btn--sm svg {
  width: 14px;
  height: 14px;
  flex-basis: 14px;
}

/* Só ícone: quadrado, do tamanho do controle. O nome da ação vai em
   aria-label (e em data-tip, se tiver dica) — ícone sozinho não é
   rótulo. */
.nf-btn--icon {
  width: var(--nf-control-h);
  padding: 0;
}

.nf-btn--icon.nf-btn--sm { width: var(--nf-control-h-sm); }

/* Largura toda: formulário de login, modal de privacidade, telefone. */
.nf-btn--block {
  width: 100%;
}

.nf-primary-button:disabled,
.nf-secondary-button:disabled,
.nf-ghost-button:disabled,
.nf-danger-button:disabled,
.nf-primary-button[aria-disabled="true"],
.nf-secondary-button[aria-disabled="true"],
.nf-ghost-button[aria-disabled="true"],
.nf-danger-button[aria-disabled="true"] {
  opacity: .55;
  cursor: not-allowed;
  pointer-events: none;
}

/* Ocupado (o nf-dialog troca o rótulo e marca aria-busy enquanto a
   ação roda): o botão continua no lugar, só não responde. */
.nf-primary-button[aria-busy="true"],
.nf-secondary-button[aria-busy="true"],
.nf-danger-button[aria-busy="true"] {
  opacity: .7;
  cursor: progress;
  pointer-events: none;
}

/* Grupo de ações: botões lado a lado, com a mesma distância em qualquer
   tela. Em rodapé de formulário, alinhados à direita. */
.nf-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--nf-space-2);
}

.nf-actions--end { justify-content: flex-end; }

/* =====================================================================
   CAMPO (Field)
   ---------------------------------------------------------------------
   `.nf-field` é o invólucro; dentro dele o rótulo, o controle, a ajuda
   e o erro. Duas marcações convivem — a de <label> envolvendo tudo e a
   de <div> com <label for> — e as duas são atendidas:

     <label class="nf-field">
       <span class="nf-field-label">Nome</span>
       <input …>
       <small class="nf-field-help">Como aparece nos relatórios.</small>
     </label>

     <div class="nf-field nf-field--error">
       <label class="nf-field-label" for="x">Nome</label>
       <input id="x" …>
       <small class="nf-field-error">Informe o nome.</small>
     </div>

   O rótulo é 12–13px, peso 500, caixa de frase, cor de texto suave: ele
   nomeia o campo, não compete com o valor. A ajuda é legenda apagada; o
   erro, legenda em vermelho de significado. Os apelidos (.cfg-field,
   .auth-field, .nf-site-controls) são as telas que ainda usam o nome
   antigo até a Fase 6; o desenho é um só.
   ===================================================================== */.nf-field,
.cfg-field,
.auth-field {
  display: grid;
  gap: var(--nf-space-1);
  min-width: 0;
}.nf-field-label,
.nf-field > span:first-child,
.nf-field > label:first-child,
.cfg-field > label,
.cfg-field > span:first-child,
.auth-field > span:first-child {
  display: block;
  margin: 0 0 var(--nf-space-1);
  color: var(--nf-text-soft);
  font-size: var(--nf-fs-label);
  font-weight: var(--nf-weight-medium);
  letter-spacing: 0;
  line-height: var(--nf-lh-heading);
  text-transform: none;
}

/* Rótulo em linha com o controle (label envolvendo "Nome{{ form.name }}"):
   o texto do próprio label é o rótulo. */
.nf-field,
.cfg-field,
.auth-field {
  color: var(--nf-text-soft);
  font-size: var(--nf-fs-label);
  font-weight: var(--nf-weight-medium);
}

.nf-field-help,
.nf-field > small,
.cfg-field > small,
.auth-field > small {
  display: block;
  margin: var(--nf-space-1) 0 0;
  color: var(--nf-muted);
  font-size: var(--nf-fs-caption);
  font-weight: var(--nf-weight-body);
  line-height: var(--nf-lh-body);
}

.nf-field-error,
.cfg-error,
.nf-form-error,
.errorlist {
  display: block;
  margin: var(--nf-space-1) 0 0;
  padding: 0;
  color: var(--nf-danger);
  font-size: var(--nf-fs-caption);
  font-weight: var(--nf-weight-medium);
  line-height: var(--nf-lh-body);
  list-style: none;
}

.nf-field--required .nf-field-label::after,
.nf-field--required > span:first-child::after {
  content: " *";
  color: var(--nf-danger);
}

/* O controle em si. */.nf-field input,
.nf-field select,
.nf-field textarea,
.cfg-field input,
.cfg-field select,
.cfg-field textarea,
.auth-field input,
.nf-site-controls select,
.nf-input,
.nf-select,
.nf-textarea {
  min-height: var(--nf-control-h);
  width: 100%;
  padding: 0 var(--nf-space-3);
  border: 1px solid var(--nf-border-strong);
  border-radius: var(--nf-radius-sm);
  color: var(--nf-text);
  background: var(--nf-surface-2);
  font-family: inherit;
  font-size: var(--nf-fs-control);
  font-weight: var(--nf-weight-body);
  line-height: 1.3;
  transition: border-color var(--nf-transition), box-shadow var(--nf-transition);
}

.nf-field textarea,
.cfg-field textarea,
.nf-textarea {
  min-height: 96px;
  padding: var(--nf-space-2) var(--nf-space-3);
  line-height: var(--nf-lh-body);
  resize: vertical;
}.nf-field input::placeholder,
.cfg-field input::placeholder,
.auth-field input::placeholder,
.nf-input::placeholder,
.nf-textarea::placeholder,
.nf-field textarea::placeholder,
.cfg-field textarea::placeholder {
  color: var(--nf-faint);
  opacity: 1;
}

/* Realce de passagem: o campo responde antes do clique. */.nf-field input:hover,
.nf-field select:hover,
.nf-field textarea:hover,
.cfg-field input:hover,
.cfg-field select:hover,
.cfg-field textarea:hover,
.auth-field input:hover,
.nf-site-controls select:hover,
.nf-input:hover,
.nf-select:hover,
.nf-textarea:hover {
  border-color: var(--nf-primary-line);
}

/* Foco: UM anel, o da marca, e só ele. Antes o campo ganhava o
   box-shadow do :focus e por cima o outline do :focus-visible genérico:
   dois anéis, um dentro do outro. */.nf-field input:focus-visible,
.nf-field select:focus-visible,
.nf-field textarea:focus-visible,
.cfg-field input:focus-visible,
.cfg-field select:focus-visible,
.cfg-field textarea:focus-visible,
.auth-field input:focus-visible,
.nf-site-controls select:focus-visible,
.nf-input:focus-visible,
.nf-select:focus-visible,
.nf-textarea:focus-visible {
  outline: none;
  border-color: var(--nf-primary-line);
  box-shadow: var(--nf-focus);
}

/* Erro: a moldura avisa, o texto explica. */
.nf-field--error input,
.nf-field--error select,
.nf-field--error textarea,
.cfg-field.has-error input,
.cfg-field.has-error select,
.cfg-field.has-error textarea,
.nf-input[aria-invalid="true"],
.nf-select[aria-invalid="true"],
.nf-textarea[aria-invalid="true"] {
  border-color: var(--nf-danger-line);
}

.nf-field--error input:focus-visible,
.nf-field--error select:focus-visible,
.nf-field--error textarea:focus-visible,
.cfg-field.has-error input:focus-visible,
.cfg-field.has-error select:focus-visible,
.cfg-field.has-error textarea:focus-visible {
  border-color: var(--nf-danger);
  box-shadow: 0 0 0 3px var(--nf-danger-soft);
}

/* Desabilitado e somente leitura: o valor continua legível (é
   informação), o controle não convida. */
.nf-field input:disabled,
.nf-field select:disabled,
.nf-field textarea:disabled,
.cfg-field input:disabled,
.cfg-field select:disabled,
.cfg-field textarea:disabled,
.auth-field input:disabled,
.nf-input:disabled,
.nf-select:disabled,
.nf-textarea:disabled {
  color: var(--nf-muted);
  background: var(--nf-surface-3);
  cursor: not-allowed;
  opacity: .8;
}

.nf-field input:read-only:not(select),
.cfg-field input:read-only:not(select),
.nf-input:read-only {
  background: var(--nf-surface);
}

/* Caixas de seleção: 13px era menor que o mínimo confortável para toque. */
input[type="checkbox"],
input[type="radio"] {
  width: 18px;
  height: 18px;
  flex: 0 0 18px;
  accent-color: var(--nf-primary);
  cursor: pointer;
}

/* Formulário: grade de campos e rodapé de ações, iguais em qualquer
   tela. A grade é fluida — duas ou três colunas conforme a largura — e
   `.nf-field--wide` ocupa a linha toda (endereço, observação). */
.nf-form {
  display: grid;
  gap: var(--nf-space-4);
  text-align: left;
}

.nf-form-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: var(--nf-space-4) var(--nf-space-5);
}

.nf-form-grid .nf-field--wide { grid-column: 1 / -1; }

.nf-form-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: flex-end;
  gap: var(--nf-space-2);
  margin-top: var(--nf-space-2);
  padding-top: var(--nf-space-4);
  border-top: 1px solid var(--nf-border);
}

/* Conjunto de campos: a legenda é título de seção, não caixa alta. */
.nf-fieldset {
  min-width: 0;
  margin: 0;
  padding: 0;
  border: 0;
}

.nf-fieldset > legend {
  display: block;
  width: 100%;
  margin: 0 0 var(--nf-space-3);
  padding: 0;
  color: var(--nf-text);
  font-size: var(--nf-fs-card);
  font-weight: var(--nf-weight-strong);
  letter-spacing: 0;
  text-transform: none;
}

.nf-fieldset > legend + p,
.nf-fieldset-help {
  margin: calc(-1 * var(--nf-space-2)) 0 var(--nf-space-4);
  color: var(--nf-muted);
  font-size: var(--nf-fs-caption);
  line-height: var(--nf-lh-body);
}

/* ---------- Foco visível ----------
   Um anel só, em qualquer controle que não seja campo (o campo tem o
   dele, acima): link, botão, resumo, área focável. */
a:focus-visible,
button:focus-visible,
summary:focus-visible,
[tabindex]:focus-visible {
  outline: 2px solid var(--nf-primary);
  outline-offset: 2px;
}

/* =====================================================================
   CARTÃO (Card)
   ---------------------------------------------------------------------
   `.nf-card` é a superfície; a separação do fundo vem da superfície, não
   do contorno. Dentro dele, uma anatomia só:

     .nf-card-head     linha de cima: título à esquerda, ações à direita
     .nf-card-title    h2/h3 do cartão (fs-card, 600)
     .nf-card-sub      a linha de apoio sob o título (legenda, apagada)
     .nf-card-actions  os botões do cabeçalho (compactos)
     .nf-card-body     o conteúdo
     .nf-card-foot     rodapé: nota, link "ver tudo", paginação

   Variantes:
     .nf-card--metric       indicador (ver KPI abaixo)
     .nf-card--chart        gráfico: o corpo ocupa a altura que sobrar
     .nf-card--status       faixa de estado na borda esquerda
     .nf-card--section      seção de página: cabeçalho separado por fio
     .nf-card--interactive  clicável inteiro (link ou botão)
     .nf-card--tight        recheio compacto (dentro de outro cartão)
     .nf-card--flush        sem recheio: tabela ou lista colada na borda

   Os nomes antigos (.card, .finance-card, .ck-card) são as
   telas que ainda não migraram; recebem só a superfície.
   ===================================================================== */.nf-card,
.card,
.finance-card,
.ck-card {
  position: relative;
  min-width: 0;
  padding: var(--nf-pad-card);
  border: 0;
  border-radius: var(--nf-radius);
  background: var(--nf-surface);
  color: var(--nf-text);
}

.nf-card--tight { padding: var(--nf-pad-card-tight); }

/* Cartão que embrulha uma tabela ou lista: o conteúdo cola nas bordas e
   o que passar da moldura é cortado (a tabela rola dentro do próprio
   .nf-table-wrap). Sete telas escreviam esta receita, cada uma na sua
   folha. */
.nf-card--flush {
  padding: 0;
  overflow: hidden;
}

.nf-card-head {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--nf-space-2) var(--nf-space-4);
  margin: 0 0 var(--nf-space-4);
}

.nf-card-head > div { min-width: 0; }

.nf-card-title {
  margin: 0;
  color: var(--nf-text);
  font-size: var(--nf-fs-card);
  font-weight: var(--nf-weight-strong);
  letter-spacing: 0;
  line-height: var(--nf-lh-heading);
  text-transform: none;
  text-wrap: balance;
}

.nf-card-sub {
  margin: var(--nf-space-1) 0 0;
  color: var(--nf-muted);
  font-size: var(--nf-fs-caption);
  line-height: var(--nf-lh-body);
}

.nf-card-actions {
  display: flex;
  flex: 0 0 auto;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--nf-space-2);
  margin-left: auto;
}

.nf-card-body { min-width: 0; }

.nf-card-foot {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--nf-space-3);
  margin: var(--nf-space-4) 0 0;
  padding-top: var(--nf-space-3);
  border-top: 1px solid var(--nf-border);
  color: var(--nf-muted);
  font-size: var(--nf-fs-caption);
}

/* Seção de página: o cabeçalho é separado do conteúdo por um fio, e o
   conteúdo (geralmente tabela ou lista) cola nas bordas. */
.nf-card--section { padding: 0; }

.nf-card--section > .nf-card-head {
  margin: 0;
  padding: var(--nf-space-4) var(--nf-pad-card-tight);
  border-bottom: 1px solid var(--nf-border);
}

.nf-card--section > .nf-card-body { padding: var(--nf-pad-card-tight); }
.nf-card--section > .nf-card-body--flush { padding: 0; }

.nf-card--section > .nf-card-foot {
  margin: 0;
  padding: var(--nf-space-3) var(--nf-pad-card-tight);
}

/* Gráfico: o cartão é uma coluna e o corpo estica. */
.nf-card--chart {
  display: flex;
  flex-direction: column;
}

.nf-card--chart > .nf-card-body {
  flex: 1 1 auto;
  min-height: 200px;
}

/* Estado: a faixa à esquerda diz como está antes de ler. */
.nf-card--status { border-left: 3px solid var(--nf-border-strong); }
.nf-card--status.is-success { border-left-color: var(--nf-success); }
.nf-card--status.is-warning { border-left-color: var(--nf-warning); }
.nf-card--status.is-danger { border-left-color: var(--nf-danger); }
.nf-card--status.is-info { border-left-color: var(--nf-info); }

/* Interativo: o cartão inteiro é o alvo. Como link, herda a cor do texto
   e não sublinha; como botão, perde a aparência de botão. */
.nf-card--interactive {
  display: block;
  width: 100%;
  text-align: inherit;
  font: inherit;
  color: inherit;
  text-decoration: none;
  cursor: pointer;
  transition: background var(--nf-transition), box-shadow var(--nf-transition), transform var(--nf-transition);
}

.nf-card--interactive:hover {
  background: var(--nf-surface-elevated);
  box-shadow: var(--nf-shadow-sm);
}

.nf-card--interactive:active { transform: translateY(1px); }

.nf-card--interactive:focus-visible {
  outline: 2px solid var(--nf-primary);
  outline-offset: 2px;
}

.nf-card--interactive[aria-pressed="true"],
.nf-card--interactive.is-selected {
  box-shadow: inset 0 0 0 1px var(--nf-primary-line);
  background: var(--nf-primary-soft);
}

/* =====================================================================
   INDICADOR (KPI / MetricCard)
   ---------------------------------------------------------------------
   A fileira de indicadores é o componente mais olhado do produto: quem
   abre a tela lê os quatro números antes de qualquer outra coisa. Um
   recipiente, quatro papéis de leitura:

     <article class="nf-card nf-card--metric">
       <span class="nf-kpi-label">Eventos em 24h</span>
       <strong class="nf-kpi-value">1.284</strong>
       <span class="nf-kpi-delta is-up">+12% na semana</span>
       <small class="nf-kpi-help">Registrados pelo monitoramento</small>
     </article>

   O rótulo é caixa de frase, 12–13px, peso 500: "EVENTOS 24H" em caixa
   alta e 700 disputava com o número, que é quem deve ganhar. Os nomes
   antigos (.nf-metric-card, .finance-kpis > .card com .label/strong/small)
   recebem o mesmo desenho até a Fase 7.
   ===================================================================== */.nf-card--metric,
.nf-metric-card,
.finance-kpis > .card {
  display: flex;
  flex-direction: column;
  gap: var(--nf-space-2);
  justify-content: center;
  min-height: 116px;
}.nf-kpi-label,
.nf-card--metric .label,
.nf-metric-card .label,
.nf-metric-card > span:first-child,
.finance-kpis .label,
.finance-kpi-head span {
  display: block;
  margin: 0;
  color: var(--nf-text-soft);
  font-size: var(--nf-fs-label);
  font-weight: var(--nf-weight-medium);
  letter-spacing: 0;
  line-height: var(--nf-lh-heading);
  text-transform: none;
}.nf-kpi-value,
.nf-card--metric strong,
.nf-metric-card strong,
.finance-kpis strong {
  display: block;
  margin: 0;
  color: var(--nf-text);
  font-size: var(--nf-fs-kpi);
  font-weight: var(--nf-weight-bold);
  line-height: var(--nf-lh-tight);
  letter-spacing: -.02em;
  font-variant-numeric: var(--nf-numeric);
}

.nf-kpi-value--sm { font-size: var(--nf-fs-metric); }

/* A unidade ao lado do número: menor e mais clara, sem virar outro
   número. */
.nf-kpi-unit {
  margin-left: var(--nf-space-1);
  color: var(--nf-muted);
  font-size: var(--nf-fs-value);
  font-weight: var(--nf-weight-medium);
  letter-spacing: 0;
}

/* Apoio: 12px, não o piso de 11px. É a linha que explica o número —
   "Eventos do cliente", "Autenticações concluídas" — e ler por horas no
   menor tamanho do sistema é o tipo de economia que se paga em vista
   cansada. */.nf-kpi-help,
.nf-card--metric small,
.nf-metric-card small,
.finance-kpis small {
  display: block;
  margin: 0;
  color: var(--nf-muted);
  font-size: var(--nf-fs-caption);
  font-weight: var(--nf-weight-body);
  line-height: var(--nf-lh-body);
}

/* Variação: cor de significado, seta desenhada pela folha. */
.nf-kpi-delta {
  display: inline-flex;
  align-items: center;
  gap: var(--nf-space-1);
  color: var(--nf-muted);
  font-size: var(--nf-fs-caption);
  font-weight: var(--nf-weight-medium);
  font-variant-numeric: var(--nf-numeric);
}

.nf-kpi-delta.is-up { color: var(--nf-success); }
.nf-kpi-delta.is-down { color: var(--nf-danger); }
.nf-kpi-delta.is-up::before { content: "↑"; }
.nf-kpi-delta.is-down::before { content: "↓"; }

/* As fileiras de KPI usam a mesma respiração. */.nf-kpis,
.finance-kpis {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: var(--nf-gap);
}

/* =====================================================================
   SELO DE ESTADO (StatusBadge)
   ---------------------------------------------------------------------
   Uma forma, e a variante muda só a cor. Altura fixa porque selo dentro
   de linha de tabela desalinha a linha quando cada um tem a sua. É a
   única peça além do kicker em caixa alta: três ou quatro letras
   ("ATIVO", "VENCIDO") em 12px com espaçamento — é o que faz o selo ler
   como estado e não como texto corrido.
   ===================================================================== */.nf-badge,
.status-pill {
  display: inline-flex;
  align-items: center;
  gap: var(--nf-space-2);
  min-height: 26px;
  padding: 0 var(--nf-space-3);
  border: 1px solid transparent;
  border-radius: var(--nf-radius-pill);
  color: var(--nf-text-soft);
  background: var(--nf-wash-strong);
  font-size: var(--nf-fs-label);
  font-weight: var(--nf-weight-strong);
  letter-spacing: var(--nf-tracking-label);
  line-height: 1;
  text-transform: uppercase;
  white-space: nowrap;
}

/* O ponto antes do texto: o estado também pela forma, não só pela cor. */
.nf-badge--dot::before {
  content: "";
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: currentColor;
}

/* Compacto: dentro de uma linha de lista de 20px, onde os 26px esticariam
   o item. O texto não encolhe abaixo do piso. */
.nf-badge--sm {
  min-height: 22px;
  padding: 0 var(--nf-space-2);
}

/* As quatro cores de significado, escritas uma vez. `success`, `warning`
   e as demais são os nomes que os templates já usam. */.nf-badge--success,
.status-pill.success,
.nf-r6-status--ok,
.nf-r6-pill--ok {
  color: var(--nf-success);
  border-color: var(--nf-success-line);
  background: var(--nf-success-soft);
}.nf-badge--warning,
.status-pill.pending,
.nf-r6-status--warn,
.nf-r6-pill--warn {
  color: var(--nf-warning);
  border-color: var(--nf-warning-line);
  background: var(--nf-warning-soft);
}.nf-badge--danger,
.status-pill.revoked,
.nf-r6-status--bad,
.nf-r6-pill--bad {
  color: var(--nf-danger);
  border-color: var(--nf-danger-line);
  background: var(--nf-danger-soft);
}.nf-badge--info,
.nf-r6-status--info,
.nf-r6-pill--info {
  color: var(--nf-info);
  border-color: var(--nf-info-line);
  background: var(--nf-info-soft);
}

.nf-badge--neutral {
  color: var(--nf-muted);
  border-color: var(--nf-border);
  background: var(--nf-wash);
}

/* O selo da tela técnica não entra na lista acima porque a folha dele é
   mais específica; o que se alinha aqui é a GEOMETRIA, que é o que o
   olho compara entre duas telas lado a lado. */
.nf-dash-v3 .dv3-status {
  min-height: 26px;
  border-radius: var(--nf-radius-pill);
  font-size: var(--nf-fs-label);
}

/* =====================================================================
   TABELA (Table)
   ---------------------------------------------------------------------
   Cabeçalho fixo em caixa de frase, zebra no lugar do traço entre
   linhas. "DATA/HORA" em caixa alta com espaçamento, na dashboard, no
   financeiro e na auditoria, era a maior fonte de ruído visual do
   produto; o cabeçalho distingue-se do dado pela cor e pelo peso.
   ===================================================================== */.nf-table,
.nf-dash-v3 .dv3-table {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--nf-fs-body);
}.nf-table th,
.nf-dash-v3 .dv3-table th {
  position: sticky;
  top: 0;
  z-index: 1;
  height: 36px;
  padding: 0 var(--nf-space-3);
  border-bottom: 1px solid var(--nf-border);
  color: var(--nf-text-soft);
  background: var(--nf-surface-2);
  font-size: var(--nf-fs-caption);
  font-weight: var(--nf-weight-strong);
  letter-spacing: 0;
  text-align: left;
  text-transform: none;
  white-space: nowrap;
}.nf-table td,
.nf-dash-v3 .dv3-table td {
  padding: var(--nf-space-3) var(--nf-space-3);
  border: 0;
  color: var(--nf-text-soft);
  vertical-align: middle;
}

/* Coluna numérica: alinhada à direita e com dígitos tabulares, para a
   vírgula cair na mesma vertical em todas as linhas. */
/* A célula quebra a palavra que não cabe, nunca qualquer palavra: com
   "anywhere" a largura mínima da coluna cai a uma letra e "Matriz" vira
   "Matri z". O que não couber rola na moldura. */
.nf-table td { overflow-wrap: break-word; }

.nf-table th.is-num,
.nf-table td.is-num,
.nf-table .nf-num {
  text-align: right;
  font-variant-numeric: var(--nf-numeric);
}.nf-table tbody tr:nth-child(even),
.nf-dash-v3 .dv3-table tbody tr:nth-child(even) {
  background: var(--nf-wash);
}.nf-table tbody tr:hover,
.nf-dash-v3 .dv3-table tbody tr:hover {
  background: var(--nf-wash-strong);
}

/* A moldura que rola: a tabela larga rola dentro dela, nunca a página. */
.nf-table-wrap {
  overflow-x: auto;
  border: 1px solid var(--nf-border);
  border-radius: var(--nf-radius-sm);
  scrollbar-color: var(--nf-brand-glow) var(--nf-wash-strong);
}

/* Sob o identificador, o detalhe (hostname, descrição) é texto do produto. */
.nf-table td.is-mono > small { font-family: var(--nf-font); }

/* ---------- Tabela dentro de cartão ----------
   O cartão é a moldura: a moldura própria do nf-table-wrap some e os
   cantos seguem os do cartão; a paginação, logo abaixo, vira o rodapé.

     <section class="nf-card nf-card--section">
       <div class="nf-table-wrap"><table class="nf-table nf-table--cards">…</table></div>
       {% include "components/paginacao.html" … %}
     </section>
*/
.nf-card > .nf-table-wrap { border: 0; border-radius: 0; }
.nf-card--section > .nf-table-wrap:first-child {
  border-top-left-radius: inherit;
  border-top-right-radius: inherit;
}
.nf-card--section > .nf-table-wrap:last-child {
  border-bottom-left-radius: inherit;
  border-bottom-right-radius: inherit;
}
.nf-card--section > .nf-pagination {
  margin: 0;
  padding: var(--nf-space-3) var(--nf-pad-card-tight);
}

/* ---------- A célula ----------
   Dois níveis: o valor forte em cima, o detalhe apagado embaixo
   ("visual" / "Usuário do cliente"; "10.0.0.1" / "fw-matriz"). É a
   receita da Auditoria, a tabela mais densa e mais legível do produto. */
.nf-table td > strong {
  display: block;
  color: var(--nf-text);
  font-weight: var(--nf-weight-strong);
}

.nf-table td > small {
  display: block;
  margin-top: 2px;
  color: var(--nf-muted);
  font-size: var(--nf-fs-caption);
  line-height: var(--nf-lh-body);
}

/* Identificador que se confere dígito a dígito: IP, serial, VLAN, chave. */
.nf-table .nf-mono,
.nf-table td.is-mono {
  font-family: var(--nf-font-mono);
  font-size: var(--nf-fs-caption);
}

/* Selo dentro da célula não estica a linha. */
.nf-table td .nf-badge { vertical-align: middle; }

/* Coluna de ações: encosta à direita, nunca quebra, botões compactos. */
.nf-table th.is-acoes,
.nf-table td.is-acoes {
  width: 1%;
  text-align: right;
  white-space: nowrap;
}

.nf-row-actions {
  display: inline-flex;
  align-items: center;
  justify-content: flex-end;
  gap: var(--nf-space-1);
}

/* Um formulário por ação (POST de uma linha) não ganha caixa própria. */
.nf-row-actions > form {
  display: inline-flex;
  margin: 0;
}

/* Linha que abre algo (histórico, detalhe): o cursor e o foco dizem. */
.nf-table tbody tr.is-clickable,
.nf-table tbody tr[role="button"] { cursor: pointer; }

.nf-table tbody tr[role="button"]:focus-visible,
.nf-table tbody tr.is-clickable:focus-visible {
  outline: 2px solid var(--nf-primary);
  outline-offset: -2px;
}

/* Linha desligada/arquivada: continua legível, não convida. */
.nf-table tbody tr.is-inactive > td { opacity: .6; }

/* Densidade compacta: listas longas da equipe. */
.nf-table--compact th { height: 32px; }
/* Célula de três ou quatro linhas (cliente, medida): alinhar pelo topo é
   o que faz a linha se ler como linha, e não como blocos boiando. */
.nf-table--top td { vertical-align: top; }

.nf-table--compact td { padding: var(--nf-space-2) var(--nf-space-3); }

/* A linha de contagem que acompanha a tabela ("4 hosts · 3 com SSH"). */
.nf-table-meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--nf-space-2) var(--nf-space-4);
  margin: 0 0 var(--nf-space-3);
  color: var(--nf-muted);
  font-size: var(--nf-fs-caption);
  font-variant-numeric: var(--nf-numeric);
}

/* Abaixo de 1024px a tabela com `data-label` nas células vira uma lista
   de cartões: cada linha um bloco, o rótulo da coluna desenhado pela
   folha. É o que evita a rolagem horizontal no tablet e no telefone. */
@media (max-width: 1024px) {
  .nf-table--cards,
  .nf-table--cards tbody,
  .nf-table--cards tr,
  .nf-table--cards td { display: block; }
  .nf-table--cards { min-width: 0; }

  .nf-table--cards thead {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip-path: inset(50%);
  }

  .nf-table--cards tbody tr {
    padding: var(--nf-space-3) var(--nf-space-4);
    border-bottom: 1px solid var(--nf-border);
  }

  .nf-table--cards tbody tr:nth-child(even) { background: transparent; }
  .nf-table--cards tbody tr:last-child { border-bottom: 0; }

  .nf-table--cards td {
    display: grid;
    grid-template-columns: minmax(96px, 35%) minmax(0, 1fr);
    gap: var(--nf-space-3);
    padding: var(--nf-space-1) 0;
    text-align: left;
  }

  .nf-table--cards td::before {
    content: attr(data-label);
    color: var(--nf-muted);
    font-size: var(--nf-fs-caption);
    font-weight: var(--nf-weight-medium);
  }

  /* O rótulo ocupa a primeira coluna; TUDO o que a célula carrega (o
     valor forte, o detalhe, o selo) fica na segunda, um embaixo do outro. */
  .nf-table--cards td::before { grid-column: 1; grid-row: 1; }
  .nf-table--cards td.is-mono::before { font-family: var(--nf-font); }
  .nf-table--cards td.is-num { text-align: left; }
  .nf-table--cards td > * { grid-column: 2; justify-self: start; }

  .nf-table--cards td.is-acoes,
  .nf-table--cards td[colspan] { grid-template-columns: 1fr; width: auto; }
  .nf-table--cards td.is-acoes::before,
  .nf-table--cards td[colspan]::before { display: none; }
  .nf-table--cards td.is-acoes > *,
  .nf-table--cards td[colspan] > * { grid-column: 1; }
}

/* ---------- Alvos de toque ----------
   Links de ação precisam de área clicável, não só de texto. */
.privacy-details-link {
  display: inline-flex;
  align-items: center;
  min-height: var(--nf-control-h);
  padding: 0 var(--nf-space-3);
  border-radius: var(--nf-radius-sm);
  font-size: var(--nf-fs-caption);
  font-weight: var(--nf-weight-strong);
  text-decoration: none;
}

.privacy-details-link:hover {
  background: var(--nf-primary-soft);
}

/* =====================================================================
   INTERRUPTOR E CARTÃO DE ESCOLHA
   ---------------------------------------------------------------------
   `.nf-switch` é um checkbox nativo vestido: o POST funciona sem
   JavaScript e o teclado continua o de sempre. `.nf-choice-card` é a
   linha que o carrega, com título, descrição e badge — a tela de
   configuração da dashboard é a primeira a usá-los.
   ===================================================================== */
.nf-switch {
  position: relative;
  display: inline-flex;
  flex: 0 0 auto;
  width: 38px;
  height: 22px;
}

.nf-switch input {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  min-height: 0;
  margin: 0;
  padding: 0;
  opacity: 0;
  cursor: pointer;
}

.nf-switch-track {
  position: absolute;
  inset: 0;
  border: 1px solid var(--nf-border-strong);
  border-radius: var(--nf-radius-pill);
  background: var(--nf-surface-3);
  transition: background var(--nf-transition), border-color var(--nf-transition);
}

.nf-switch-track::after {
  content: "";
  position: absolute;
  top: 2px;
  left: 2px;
  width: 16px;
  height: 16px;
  border-radius: 50%;
  background: var(--nf-text-soft);
  transition: transform var(--nf-transition), background var(--nf-transition);
}

.nf-switch input:checked + .nf-switch-track {
  border-color: var(--nf-primary-active);
  background: var(--nf-primary-active);
}

.nf-switch input:checked + .nf-switch-track::after {
  transform: translateX(16px);
  background: var(--nf-on-primary);
}

.nf-switch input:focus-visible + .nf-switch-track {
  box-shadow: var(--nf-focus);
}

.nf-switch input:disabled + .nf-switch-track { opacity: .55; }

.nf-choice-card {
  display: flex;
  align-items: flex-start;
  gap: var(--nf-space-3);
  padding: var(--nf-space-3) var(--nf-space-4);
  border: 1px solid var(--nf-border);
  border-radius: var(--nf-radius-sm);
  background: var(--nf-surface-2);
  cursor: pointer;
}

.nf-choice-card:hover { border-color: var(--nf-border-strong); }

.nf-choice-card .nf-switch { margin-top: 2px; }

.nf-choice-copy { display: grid; gap: var(--nf-space-1); min-width: 0; flex: 1 1 auto; }

.nf-choice-copy strong { color: var(--nf-text); font-size: var(--nf-fs-body); font-weight: var(--nf-weight-strong); }

.nf-choice-copy small {
  color: var(--nf-muted);
  font-size: var(--nf-fs-caption);
  line-height: var(--nf-lh-body);
}

.nf-choice-card .nf-badge { flex: 0 0 auto; align-self: center; }

/* =====================================================================
   ESTADOS: VAZIO, CARREGANDO, ERRO
   ---------------------------------------------------------------------
   Os três includes em templates/components/ (vazio.html,
   carregando.html, erro.html) desenham estes blocos. O que vai neles é
   texto para quem opera: nunca stack trace, nunca payload de API, nunca
   a mensagem bruta do Zabbix ou do IXC.
   ===================================================================== */

/* ---------- Vazio (EmptyState) ----------
   Mesma altura de respiro em qualquer tela: o vazio é informação, não
   sobra de layout. Centralizado, porque em tabela ele ocupa a largura
   toda e alinhado à esquerda parece linha de dado. */.nf-empty,
.cfg-empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--nf-space-2);
  padding: var(--nf-space-7) var(--nf-space-5);
  color: var(--nf-muted);
  text-align: center;
}

.nf-empty-icone {
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  margin-bottom: var(--nf-space-2);
  border-radius: 50%;
  background: var(--nf-wash-strong);
  color: var(--nf-muted);
}

.nf-empty-icone svg {
  width: 20px;
  height: 20px;
  fill: currentColor;
}.nf-empty strong,
.cfg-empty h2 {
  margin: 0;
  color: var(--nf-text);
  font-size: var(--nf-fs-metric-sm);
  font-weight: var(--nf-weight-strong);
  line-height: var(--nf-lh-heading);
}.nf-empty p,
.nf-empty span,
.cfg-empty p {
  margin: 0;
  max-width: 52ch;
  color: var(--nf-text-soft);
  font-size: var(--nf-fs-body);
  line-height: var(--nf-lh-body);
}

.nf-empty .nf-secondary-button,
.nf-empty .nf-primary-button { margin-top: var(--nf-space-2); }

/* ---------- Carregando (LoadingState) ----------
   Não havia nenhum: as telas que buscam dado ficavam com o valor
   anterior na tela enquanto o novo não chegava, o que é pior que vazio
   — o número velho não se anuncia como velho. Duas formas: o texto com
   o giro (`.nf-loading`) e o esqueleto (`.nf-skeleton`), que ocupa o
   lugar do conteúdo com a forma dele. */
.nf-loading {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--nf-space-3);
  padding: var(--nf-space-7) var(--nf-space-5);
  color: var(--nf-muted);
  font-size: var(--nf-fs-body);
}

.nf-loading::before {
  content: "";
  width: 16px;
  height: 16px;
  border: 2px solid var(--nf-border-strong);
  border-top-color: var(--nf-primary);
  border-radius: var(--nf-radius-pill);
  animation: nf-spin 700ms linear infinite;
}

.nf-skeleton {
  display: grid;
  gap: var(--nf-space-3);
  padding: var(--nf-space-4) 0;
}

.nf-skeleton-linha {
  height: 12px;
  border-radius: var(--nf-radius-xs);
  background: var(--nf-wash-strong);
  animation: nf-pulse 1.4s ease-in-out infinite;
}

/* Larguras desiguais: linhas todas do mesmo tamanho parecem barra de
   progresso, não texto a caminho. */
.nf-skeleton-linha:nth-child(3n + 1) { width: 92%; }
.nf-skeleton-linha:nth-child(3n + 2) { width: 76%; }
.nf-skeleton-linha:nth-child(3n) { width: 58%; }

.nf-skeleton--bloco .nf-skeleton-linha { height: 44px; width: 100%; }

/* Área que está sendo relida: o conteúdo continua legível e se anuncia
   como desatualizado, em vez de sumir. */
[data-loading="true"],
[aria-busy="true"]:not(button) {
  opacity: .55;
  transition: opacity var(--nf-transition);
}

@keyframes nf-spin {
  to { transform: rotate(360deg); }
}

@keyframes nf-pulse {
  50% { opacity: .45; }
}

/* Quem pediu menos movimento não recebe giro nenhum. */
@media (prefers-reduced-motion: reduce) {
  .nf-loading::before,
  .nf-skeleton-linha { animation: none; }
  [data-loading="true"],
  .nf-card--interactive { transition: none; }
}

/* ---------- Erro (ErrorState) ----------
   Havia um só, na tela Financeiro, e era o componente com o pior texto
   do sistema: a explicação do erro saía em 11px e a ação de recuperação
   era um link sem corpo de botão. É exatamente o texto que alguém
   precisa ler com atenção, no momento em que está com pressa. O kicker
   ("Integração indisponível") é a única caixa alta; o título diz o que
   aconteceu e o parágrafo o que fazer. */
.nf-error-state,
.finance-error {
  display: flex;
  align-items: flex-start;
  gap: var(--nf-space-4);
  width: 100%;
  max-width: 720px;
  margin: var(--nf-space-8) auto;
  padding: var(--nf-pad-card);
  border-radius: var(--nf-radius);
  background: var(--nf-surface);
}

.nf-error-state > div { min-width: 0; }

.nf-error-state-kicker,
.nf-error-state > div > span:first-child,
.finance-error span {
  display: block;
  color: var(--nf-danger);
  font-size: var(--nf-fs-label);
  font-weight: var(--nf-weight-strong);
  letter-spacing: var(--nf-tracking-label);
  text-transform: uppercase;
}

.nf-error-state h2,
.finance-error h2 {
  margin: var(--nf-space-1) 0 var(--nf-space-2);
  color: var(--nf-text);
  font-size: var(--nf-fs-metric-sm);
  font-weight: var(--nf-weight-strong);
  line-height: var(--nf-lh-tight);
}

.nf-error-state p,
.finance-error p {
  margin: 0 0 var(--nf-space-4);
  max-width: 60ch;
  color: var(--nf-text-soft);
  font-size: var(--nf-fs-body);
  line-height: var(--nf-lh-body);
}

.nf-error-state-icon,
.finance-error-icon {
  width: 44px;
  height: 44px;
  flex: 0 0 44px;
  display: grid;
  place-items: center;
  border: 1px solid var(--nf-danger-line);
  border-radius: var(--nf-radius-pill);
  background: var(--nf-danger-soft);
  color: var(--nf-danger);
  font-size: var(--nf-fs-metric-sm);
  font-weight: var(--nf-weight-bold);
}

/* ---------- Alerta / mensagem (Alert) ----------
   Django manda as classes `success`, `warning`, `error` e `info` no
   `message.tags`; as três telas que mostram mensagem traduziam isso cada
   uma do seu jeito. */
.nf-alert,
.cfg-message {
  display: flex;
  align-items: center;
  gap: var(--nf-space-3);
  padding: var(--nf-space-3) var(--nf-space-4);
  border: 1px solid var(--nf-border);
  border-left-width: 3px;
  border-radius: var(--nf-radius-sm);
  color: var(--nf-text);
  background: var(--nf-surface-2);
  font-size: var(--nf-fs-body);
  line-height: var(--nf-lh-body);
}

.nf-alert + .nf-alert,
.cfg-message + .cfg-message {
  margin-top: var(--nf-space-2);
}

.nf-alert--success,
.cfg-message.success {
  border-left-color: var(--nf-success);
  background: var(--nf-success-soft);
}

.nf-alert--warning,
.cfg-message.warning {
  border-left-color: var(--nf-warning);
  background: var(--nf-warning-soft);
}

.nf-alert--danger,
.cfg-message.error {
  border-left-color: var(--nf-danger);
  background: var(--nf-danger-soft);
}

.nf-alert--info,
.cfg-message.info {
  border-left-color: var(--nf-info);
  background: var(--nf-info-soft);
}

/* A caixa que envolve as mensagens: mesma distância do topo do conteúdo
   em qualquer página. */
.account-messages {
  margin-bottom: var(--nf-stack);
}

/* ---------- Página (PageShell) ----------
   Cada tela abria o conteúdo do seu jeito — `.ck-page`, `.nf-r6-page` (já migrado),
   `.customer-select-page`, `.nf-dash-v3`, e três telas sem invólucro
   nenhum. O efeito é o que se vê ao navegar: a largura do conteúdo muda
   de página para página, e o topo começa em alturas diferentes. O
   cabeçalho de página (.nf-page-header) mora em shell.css. */
.nf-page,
.ck-page,
.nf-dash-v3 {
  display: flex;
  flex-direction: column;
  gap: var(--nf-stack);
  width: 100%;
  max-width: var(--nf-content-max);
  margin-inline: auto;
}

/* Seleção de cliente e módulo em construção NÃO entram na regra acima.
   As duas são telas de um cartão só, centralizado na altura da janela
   (`place-items: center`), e o `display: flex` daqui — que carrega
   depois de `app.css` — desmontaria esse centro. O que elas compartilham
   é o teto de largura, e é só isso que se aplica. */
.customer-select-page,
.module-placeholder {
  max-width: var(--nf-content-max);
  margin-inline: auto;
}

/* ---------- Texto que vem de fora ----------
   Nome de equipamento, mensagem de erro do fabricante, hostname, e-mail:
   tudo isso chega do servidor com tamanho imprevisível e cai dentro de
   caixas de largura fixa. Sem permissão de quebra, a palavra longa
   transborda o cartão e passa por cima do vizinho — o defeito clássico
   de painel de rede, e um dos que a conferência em 1600px encontrou. */.nf-dash-v3 .dv3-table td,
.nf-empty p,
.nf-error-state p,
.finance-error p,
.nf-alert,
.cfg-message,
.nf-card-title,
.nf-card-sub {
  overflow-wrap: anywhere;
}

/* ---------- Contador ao lado do título ----------
   "1 contrato", "2 ativos de 3": é medida, não estado. Estava numa caixa
   quadrada de 27px com borda da marca — desenhada para um número sozinho
   e usada com número MAIS palavra, então a caixa esticava e ficava com
   cara de selo torto ao lado do selo de verdade.

   Aqui é texto de apoio, sem moldura: o que precisa de moldura na linha
   é o estado (ATIVO), e dois elementos emoldurados lado a lado competem
   entre si em vez de se hierarquizarem. */.nf-count,
.finance-contracts-link {
  display: inline-flex;
  align-items: center;
  min-height: auto;
  padding: 0;
  border: 0;
  border-radius: 0;
  color: var(--nf-text-soft);
  background: none;
  font-size: var(--nf-fs-caption);
  font-weight: var(--nf-weight-strong);
  letter-spacing: 0;
  text-transform: none;
  white-space: nowrap;
}

.finance-contracts-link {
  color: var(--nf-primary);
  text-decoration: none;
}

.finance-contracts-link:hover { color: var(--nf-primary-hover); }

/* =====================================================================
   PAGINAÇÃO (Pagination)
   ---------------------------------------------------------------------
   O rodapé de toda lista paginada (templates/components/paginacao.html):
   "Mostrando 21–40 de 266 · Mostrar [20] por página · ‹ 1 2 3 … 14 ›".
   Nasceu em Clientes dedicados e agora é de Relatórios e Auditoria
   também; o cálculo mora em apps/core/paginacao.py.
   ===================================================================== */
.nf-pagination {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--nf-space-3);
  margin-top: var(--nf-space-3);
  padding: var(--nf-space-3) 0 0;
  border-top: 1px solid var(--nf-border);
}

.nf-pagination-resumo {
  margin: 0;
  color: var(--nf-text-soft);
  font-size: var(--nf-fs-caption);
  font-variant-numeric: var(--nf-numeric);
}

/* O seletor é discreto por decisão: ele muda a densidade da tela, não o
   conteúdo dela, e não pode disputar atenção com os filtros de cima. */
.nf-pagination-tamanho {
  display: inline-flex;
  align-items: center;
  gap: var(--nf-space-2);
  color: var(--nf-muted);
  font-size: var(--nf-fs-caption);
}

.nf-pagination-tamanho select {
  min-height: var(--nf-control-h-sm);
  width: auto;
  padding: 0 var(--nf-space-2);
  border: 1px solid var(--nf-border);
  border-radius: var(--nf-radius-sm);
  background: var(--nf-surface-2);
  color: var(--nf-text);
  font: inherit;
  font-variant-numeric: var(--nf-numeric);
}

.nf-pagination-tamanho select:focus-visible {
  outline: none;
  border-color: var(--nf-primary-line);
  box-shadow: var(--nf-focus);
}

.nf-pagination-passos {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--nf-space-1);
  font-variant-numeric: var(--nf-numeric);
}

.nf-pagination-passos > a,
.nf-pagination-passos > span {
  min-width: var(--nf-control-h-sm);
  min-height: var(--nf-control-h-sm);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0 var(--nf-space-2);
  border: 1px solid transparent;
  border-radius: var(--nf-radius-sm);
  color: var(--nf-text-soft);
  font-size: var(--nf-fs-caption);
  font-weight: var(--nf-weight-medium);
  text-decoration: none;
}

.nf-pagination-passos > a:hover { color: var(--nf-text); background: var(--nf-hover); }
.nf-pagination-passos > a:focus-visible { outline: none; box-shadow: var(--nf-focus); }
.nf-pagination-passos > span[aria-disabled="true"] { color: var(--nf-faint); }
.nf-pagination-salto { color: var(--nf-faint); }

.nf-pagination-atual {
  border-color: var(--nf-primary-line);
  background: var(--nf-primary-soft);
  color: var(--nf-text);
}

/* =====================================================================
   FILTROS (FilterBar)
   ---------------------------------------------------------------------
   A barra acima de toda lista: a busca à esquerda, os seletores ao lado,
   as ações no fim; abaixo, os chips com contador quando a lista tem
   recortes. Um <form method="get"> com id — o seletor "por página" da
   paginação pertence a ele por `form=`.

     <form class="nf-filtros" method="get" id="dedFiltros">
       {% include "components/busca.html" with name="q" value=busca placeholder="…" %}
       <label class="nf-field"><span class="nf-field-label">Período</span><select name="days">…</select></label>
       <div class="nf-filtros-acoes">
         <button type="submit" class="nf-secondary-button">Aplicar</button>
         <a class="nf-ghost-button" href="?">Limpar</a>
       </div>
     </form>
     <div class="nf-chips" role="group" aria-label="…">
       <a class="nf-chip" aria-pressed="true" href="?filtro=todos">Todos <span class="nf-chip-contagem">35</span></a>
     </div>

   Em cima se escolhe O QUE ver; a paginação embaixo anda pelo resultado.
   ===================================================================== */
.nf-filtros {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: var(--nf-space-3);
  margin: 0 0 var(--nf-space-4);
}

/* Depois de um aviso, a barra respira. */
.nf-alert + .nf-filtros { margin-top: var(--nf-space-4); }

.nf-filtros > .nf-field {
  flex: 0 1 220px;
  min-width: 160px;
}

.nf-filtros > .nf-field--busca { flex: 1 1 320px; }

.nf-filtros-acoes {
  display: flex;
  align-items: center;
  gap: var(--nf-space-2);
}

.nf-filtros-acoes > * { white-space: nowrap; }

/* O campo de busca: lupa à esquerda, dentro do controle. */
.nf-busca {
  position: relative;
  display: flex;
  align-items: center;
}

.nf-busca > svg {
  position: absolute;
  left: var(--nf-space-3);
  width: 16px;
  height: 16px;
  fill: var(--nf-muted);
  pointer-events: none;
}

.nf-busca > input {
  flex: 1 1 auto;
  padding-left: calc(var(--nf-space-3) * 2 + 16px);
}

.nf-busca > .nf-busca-limpar {
  position: absolute;
  right: var(--nf-space-1);
}

/* Chips: o recorte da lista, com contador. Um só pressionado por vez;
   é link (GET) ou botão, e o estado vai em aria-pressed. */
.nf-chips {
  display: flex;
  flex-wrap: wrap;
  gap: var(--nf-space-2);
  margin: 0 0 var(--nf-space-4);
}

.nf-chip {
  display: inline-flex;
  align-items: center;
  gap: var(--nf-space-2);
  min-height: var(--nf-control-h-sm);
  padding: 0 var(--nf-space-3);
  border: 1px solid var(--nf-border-strong);
  border-radius: var(--nf-radius-pill);
  background: var(--nf-surface-2);
  color: var(--nf-text-soft);
  font: inherit;
  font-size: var(--nf-fs-caption);
  font-weight: var(--nf-weight-medium);
  line-height: 1.2;
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
  transition: background var(--nf-transition), border-color var(--nf-transition), color var(--nf-transition);
}

.nf-chip:hover {
  border-color: var(--nf-primary-line);
  color: var(--nf-text);
}

.nf-chip[aria-pressed="true"],
.nf-chip.is-active {
  border-color: var(--nf-primary-line);
  background: var(--nf-primary-soft);
  color: var(--nf-text);
}

.nf-chip-contagem {
  min-width: 20px;
  padding: 0 var(--nf-space-1);
  border-radius: var(--nf-radius-pill);
  background: var(--nf-wash-strong);
  color: var(--nf-muted);
  font-size: var(--nf-fs-label);
  font-variant-numeric: var(--nf-numeric);
  text-align: center;
}

.nf-chip[aria-pressed="true"] .nf-chip-contagem,
.nf-chip.is-active .nf-chip-contagem {
  background: var(--nf-primary-line);
  color: var(--nf-text);
}

/* A nota de filtro ativo: diz o que a lista está mostrando e oferece
   limpar. Fica entre a barra e a tabela. */
.nf-filtros-nota {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--nf-space-2);
  margin: calc(-1 * var(--nf-space-2)) 0 var(--nf-space-4);
  color: var(--nf-muted);
  font-size: var(--nf-fs-caption);
}

.nf-filtros-nota strong { color: var(--nf-text); }

@media (max-width: 768px) {
  .nf-filtros > .nf-field,
  .nf-filtros > .nf-field--busca { flex-basis: 100%; }
  .nf-filtros-acoes { width: 100%; }
}

/* No papel a lista impressa é a que está na tela: andar de página não
   faz sentido ali. */
@media print {
  .nf-pagination { display: none !important; }

  /* A tabela no papel: cabeçalho claro, sem zebra, sem cabeçalho fixo. */
  .nf-table th {
    position: static;
    color: var(--nf-paper-ink);
    background: var(--nf-paper-line-soft);
    border-color: var(--nf-paper-line);
  }
  .nf-table td { color: var(--nf-paper-ink); }
  .nf-table tbody tr:nth-child(even) { background: transparent; }
}
