/* FinPulse — folha de componentes
 * -------------------------------
 * Consome tokens.css. REGRA: nenhuma cor literal em declaração aqui — cor nova
 * entra como token. Medida que se repete também é token; medida que é decisão
 * de um componente só mora aqui, comentada.
 *
 * ⚠️ ORDEM DOS <link> É CONTRATO, não estética (core/base.html):
 *      bootstrap.min.css → tokens.css → app.css
 * O tokens.css redefine variáveis --bs-* com a MESMA especificidade do
 * Bootstrap (`:root` e `[data-bs-theme]` valem 0,1,0). Ele vence apenas porque
 * carrega DEPOIS. Inverter a ordem quebra em silêncio todos os tints do tema
 * escuro: sem erro, sem aviso, só cor errada.
 */

/* Open Sans self-hosted — institucional do Manual §04. Sem CDN: a VM é NAT e
 * produção não deve depender de terceiro para renderizar texto. Variável
 * (300–700) num arquivo só, em vez de 4 estáticos. */
@font-face {
    font-family: "Open Sans";
    font-style: normal;
    font-weight: 300 700;
    font-display: swap;
    src: url("../fonts/OpenSans-Variable-latin.woff2") format("woff2-variations");
}

/* JetBrains Mono — a "utilitária monoespaçada" do Manual §04, reservada a
 * código, valores de cor, nomes de variável e specs técnicas. NUNCA texto
 * corrido: o split mono/sans é a gramática visual da marca. */
@font-face {
    font-family: "JetBrains Mono";
    font-style: normal;
    font-weight: 400 500;
    font-display: swap;
    src: url("../fonts/JetBrainsMono-Variable-latin.woff2") format("woff2-variations");
}

* { box-sizing: border-box; }

html, body { margin: 0; height: 100%; }

body {
    font-family: var(--font-stack);
    font-size: var(--font-size-base);
    line-height: 1.5;
    color: var(--text-primary);
    background: var(--bg-page);
}

a { color: var(--dpai-link); text-decoration: none; }
a:hover { color: var(--dpai-primary); }
.muted { color: var(--text-secondary); }
.mono { font-family: var(--font-mono); }

/* Foco visível e consistente. O Bootstrap cobre os próprios componentes, mas
 * link e elemento interativo próprio ficariam só com o outline do browser. */
:focus-visible { outline: var(--focus-outline); outline-offset: 2px; }

/* Quem pede menos movimento não vê transição nenhuma. O HarbourPulse não tem
 * isto — é dívida registrada no DESIGN-SYSTEM.md dele; aqui nasce coberto. */
@media (prefers-reduced-motion: reduce) {
    *, *::before, *::after {
        animation-duration: 0.01ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: 0.01ms !important;
    }
}

/* ============================ ícones ============================ */
/* O sprite fica no DOM só para servir os <symbol> aos <use>; nada dele é
 * pintado. `display:none` no <svg> raiz não impede o <use> de resolver. */
.icon-sprite { display: none; }

/* `fill: currentColor` é o ponto todo: a cor do ícone vem do texto do elemento,
 * então item ativo, hover e tema escuro já funcionam sem UMA regra de cor aqui.
 * Foi o que os emoji nunca puderam fazer — glifo de fonte não herda fill, e cada
 * SO desenhava o seu.
 *
 * O 16px casa com a altura de linha do --font-size-base (15px); o
 * vertical-align negativo assenta o ícone na linha de base do texto ao lado,
 * senão ele flutua alto e desalinha o rótulo do menu. */
.icon {
    width: 16px;
    height: 16px;
    flex-shrink: 0;
    fill: currentColor;
    vertical-align: -0.125em;
}

/* Ícone maior, para cabeçalho de card e estado vazio. */
.icon-lg { width: 22px; height: 22px; }

/* ==================== linha de pulso (Manual §05) ==================== */
/* Monolinha, nunca preenchida, monocromática. `non-scaling-stroke` no path
 * mantém a espessura em 2px mesmo com preserveAspectRatio="none" esticando o
 * viewBox — sem isso o traço engorda junto com a largura da tela. */
.pulse-trace { display: block; width: 100%; height: 10px; }
.pulse-trace path {
    fill: none;                     /* §05: nunca preenchido */
    stroke: var(--dpai-primary);    /* §05: indigo sobre claro */
    stroke-width: 2;                /* §05: traço de 2–3px */
    stroke-linecap: round;
    stroke-linejoin: round;
    /* Desenha uma vez e para: `forwards`, sem `infinite`. O dasharray precisa
     * cobrir o comprimento do path (≈500 + os picos). */
    stroke-dasharray: 560;
    stroke-dashoffset: 560;
    animation: pulso-desenha 3s cubic-bezier(0.2, 0.7, 0.2, 1) forwards;
}
/* §05: teal sobre fundo escuro. */
.pulse-trace-deep path { stroke: var(--dpai-accent); }

@keyframes pulso-desenha { to { stroke-dashoffset: 0; } }

/* ============================ layout ============================ */
.layout { display: flex; min-height: 100vh; }

/* Sidebar em Pulse Navy — Manual §03 ("navbar, sidebar, fundos escuros").
 * Cor chapada, sem gradiente: o manual proíbe gradiente decorativo (§01
 * "Direta"). A largura é decisão deste componente, não escala compartilhada. */
.sidebar {
    width: 248px;
    flex-shrink: 0;
    padding: var(--space-4) var(--space-3);
    background: var(--ink-deep);
    color: var(--text-on-deep);
}

/* Bloco de marca da sidebar: lockup da empresa em cima, nome do produto embaixo.
 * §02 exige área de reserva ao redor do lockup igual à altura do símbolo — daí o
 * padding generoso e o gap, em vez de encostar o wordmark no nome do produto. */
.brand {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    padding: var(--space-2) var(--space-2) var(--space-5);
}
.brand-lockup { display: block; line-height: 0; }
.brand-produto {
    font-weight: 700;
    font-size: var(--font-size-xl);
    color: var(--text-on-deep);
    line-height: 1.1;
}
/* §07: o wordmark nunca quebra em duas linhas. Vale para o nome do produto pelo
 * mesmo motivo — quebra muda a leitura da marca. */
.brand-logo, .brand-produto { white-space: nowrap; }

.nav-group {
    font-size: var(--font-size-xxs);
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: var(--text-on-deep-muted);
    margin: var(--space-4) var(--space-2) var(--space-1);
}

.nav-item {
    display: block;
    padding: var(--space-2);
    border-radius: var(--radius-md);
    color: var(--text-on-deep);
    margin: 2px 0;   /* respiro mínimo entre itens; abaixo da escala, de propósito */
}
/* Hover = deslocamento sutil de fundo, nunca escurecer a cor em 40%: o traço da
 * marca é de interação quieta. */
.nav-item:hover { background: var(--overlay-hover); color: var(--text-on-deep); }
.nav-item.active { background: var(--dpai-primary); color: var(--text-on-brand); }
.nav-item.muted { color: var(--text-on-deep-muted); }
.nav-empty, .nav-empty small {
    color: var(--text-on-deep-muted);
    padding: var(--space-2);
    font-size: var(--font-size-sm);
}

/* ============================ conteúdo ============================ */
.content { flex: 1; display: flex; flex-direction: column; min-width: 0; }

.topbar {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: var(--space-3);
    padding: var(--space-3) var(--space-5);
    background: var(--bg-card);
    border-bottom: 1px solid var(--border-card);
}
.user { display: flex; gap: var(--space-3); align-items: center; color: var(--text-secondary); }
.inline { display: inline; }
.link {
    background: none;
    border: 0;
    color: var(--dpai-link);
    cursor: pointer;
    font-size: var(--font-size-sm);
    padding: 0;
}
.page { padding: var(--space-6); max-width: 1100px; }

h1 { margin: 0 0 var(--space-1); font-size: var(--font-size-xxl); font-weight: 700; }
.lead { color: var(--text-secondary); margin: 0 0 var(--space-5); }

/* ============================ cards ============================ */
/* Hairline de 1px como separador, não sombra — o traço da marca prefere borda
 * fina a card flutuante. */
.cards {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
    gap: var(--space-4);
}
.card {
    background: var(--bg-card);
    border: 1px solid var(--border-card);
    border-radius: var(--radius-lg);
    padding: var(--space-4);
}
/* Desde o Sprint 5 o conteúdo é um <svg class="icon icon-lg">, então quem dimensiona
 * é a classe do ícone, não `font-size`. O `color` continua valendo, e é o que importa:
 * `.icon` usa `fill: currentColor`, então o ícone herda esta cor.
 *
 * É --dpai-accent-text (teal escurecido, 11.64:1) e não --dpai-accent puro: o teal
 * puro dá 2.49:1 sobre a superfície do card, abaixo até do piso de 3:1 do WCAG 1.4.11
 * para elemento gráfico. */
.card-icon { color: var(--dpai-accent-text); line-height: 1; }
.card-title { font-weight: 600; margin: var(--space-2) 0 var(--space-1); }
.card-desc { color: var(--text-secondary); font-size: var(--font-size-sm); min-height: 38px; }
.card-meta { color: var(--text-secondary); font-size: var(--font-size-xs); margin-top: var(--space-2); }

/* ==================== KPI card (Manual §08) ==================== */
/* Componente de core/_kpi_card.html. Antes do Sprint 6 cada um dos 17 cards
 * carregava `style="border-top:3px solid var(--status-…)"` à mão, e
 * .kpi-label/.kpi-value existiam nos templates SEM regra de CSS — o número saía
 * no tamanho do corpo, sem hierarquia. */
.kpi-card {
    background: var(--bg-card);
    border: 1px solid var(--border-card);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-card);
    overflow: hidden;            /* trunca a barra de status no raio da borda */
    height: 100%;                /* iguala a altura dentro da mesma linha da grade */
    display: flex;
    flex-direction: column;
}
.kpi-body { padding: var(--space-4); }

/* Barra superior de STATUS. A cor comunica o veredito antes da leitura do
 * número — não é enfeite. Default é o acento da marca, para indicador neutro. */
.kpi-status-bar { height: var(--accent-bar); background: var(--dpai-accent); }
.kpi-status-positive .kpi-status-bar { background: var(--status-positive); }
.kpi-status-warning  .kpi-status-bar { background: var(--status-warning); }
.kpi-status-negative .kpi-status-bar { background: var(--status-negative); }
.kpi-status-info     .kpi-status-bar { background: var(--status-info); }
.kpi-status-brand    .kpi-status-bar { background: var(--dpai-primary); }

/* Hierarquia do §04: rótulo 13/400, valor 32/700, apoio 12/400. O valor é
 * MONOCROMÁTICO (--text-primary): o manual §08 manda o status ir na barra e no
 * rótulo, nunca colorindo o número inteiro. */
.kpi-label {
    color: var(--text-secondary);
    font-size: var(--font-size-sm);
    line-height: 1.3;
}
.kpi-value {
    color: var(--text-primary);
    font-size: var(--font-size-xxl);
    font-weight: 700;
    line-height: 1.15;
    margin-top: var(--space-1);
    /* Valor monetário longo (R$ 1.234.567,89) não pode empurrar a largura do
     * card nem quebrar no meio do número. */
    overflow-wrap: anywhere;
}
.kpi-nota {
    color: var(--text-secondary);
    font-size: var(--font-size-xs);
    margin-top: var(--space-1);
}

/* ==================== container de gráfico ==================== */
/* O canvas do Chart.js precisa de altura EXPLÍCITA no pai: com
 * `maintainAspectRatio:false` ele preenche o container, e container sem altura
 * colapsa para 0 — o gráfico simplesmente não aparece. Era o `style="height:300px"`
 * repetido em 4 telas. */
.chart-box { height: var(--height-chart); }
.chart-box > canvas { width: 100%; height: 100%; }

/* ==================== larguras de contêiner ==================== */
/* Os tokens --width-* existiam desde o Sprint 3 e NÃO eram usados: as 8 telas
 * repetiam `style="max-width:560px|640px|480px"`, com os mesmos valores. */
.w-form-sm { max-width: var(--width-form-sm); }
.w-form    { max-width: var(--width-form); }
.w-form-lg { max-width: var(--width-form-lg); }
.w-card-sm { max-width: var(--width-card-sm); }

/* Painel lateral do assistente de IA. O min() do token mantém o painel inteiro
 * em tela estreita, onde 420px passaria da viewport. */
.panel-ia { width: var(--width-panel); }

/* ==================== callout ==================== */
/* Card com faixa de acento na borda ESQUERDA, para observação contextual. Mesmo
 * token da barra do KPI: o manual §03 trata "barra do KPI" e "borda do callout"
 * como a mesma espessura de acento. Antes era `border-left:4px` inline em 2
 * telas — 1px a mais, sem motivo. */
.callout { border-left: var(--accent-bar) solid var(--dpai-accent); }
.callout-info     { border-left-color: var(--status-info); }
.callout-positive { border-left-color: var(--status-positive); }
.callout-warning  { border-left-color: var(--status-warning); }
.callout-negative { border-left-color: var(--status-negative); }

/* ==================== coluna de barra em tabela ==================== */
/* Célula que hospeda uma barra de proporção (participação, margem). A largura é
 * reservada para a barra ter escala legível; era `width:28%` e `width:25%`
 * inline, dois valores para o mesmo papel — unificados. */
.col-bar { width: 25%; }

/* ==================== progresso em linha ==================== */
/* Barra de progresso ao lado de um botão (o "Sugerir com IA"). Nasce oculta; o
 * JS troca para `inline-flex` ao iniciar, e estilo inline vence classe, então a
 * troca continua funcionando sem tocar no script.
 *
 * ⚠️ A LARGURA DO PREENCHIMENTO fica inline de propósito, no template e no JS:
 * é valor calculado em runtime (`fill.style.width = pct + '%'`) e classe de CSS
 * não expressa percentual dinâmico. É a única medida inline que sobra no projeto. */
.progress-inline {
    display: none;
    width: 200px;
    height: 20px;
    vertical-align: middle;
}

.empty-state {
    background: var(--bg-card);
    border: 1px dashed var(--border-card);
    border-radius: var(--radius-lg);
    padding: var(--space-8);
    text-align: center;
    color: var(--text-secondary);
}

/* ============================ mensagens ============================ */
.messages { list-style: none; padding: 0; margin: var(--space-3) var(--space-6) 0; }
.messages li {
    padding: var(--space-2) var(--space-3);
    border-radius: var(--radius-md);
    margin: var(--space-2) 0;
    border-left: var(--accent-bar) solid var(--tint-border-positive);
    background: var(--tint-positive);
    color: var(--on-tint-positive);
}
.messages li.error {
    border-left-color: var(--tint-border-negative);
    background: var(--tint-negative);
    color: var(--on-tint-negative);
}
.messages li.warning {
    border-left-color: var(--tint-border-warning);
    background: var(--tint-warning);
    color: var(--on-tint-warning);
}

/* ============================ login ============================ */
.login-wrap {
    min-height: 100vh;
    display: flex;
    align-items: center;
    justify-content: center;
    background: var(--ink-deep);
}
.login-card {
    background: var(--bg-card);
    border: 1px solid var(--border-card);
    border-radius: var(--radius-lg);
    padding: var(--space-6);
    width: 340px;   /* cartão de credencial, não contêiner de formulário */
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
    box-shadow: var(--shadow-elev);
}
/* §02: área de reserva ao redor do lockup = altura do símbolo. */
.login-lockup { display: flex; justify-content: center; padding: var(--space-2) 0 var(--space-4); }
.login-brand {
    font-size: var(--font-size-xxl);
    font-weight: 700;
    color: var(--dpai-primary);
    text-align: center;
    white-space: nowrap;
}
.login-sub { text-align: center; color: var(--text-secondary); margin: 0 0 var(--space-2); }
.login-card label {
    display: flex;
    flex-direction: column;
    font-size: var(--font-size-sm);
    color: var(--text-secondary);
    gap: var(--space-1);
}
.login-card input {
    padding: var(--space-2);
    border: 1px solid var(--border-card);
    border-radius: var(--radius-md);
    font-size: var(--font-size-base);
    font-family: inherit;
    background: var(--bg-card);
    color: var(--text-primary);
}
.login-error {
    background: var(--tint-negative);
    color: var(--on-tint-negative);
    border-left: var(--accent-bar) solid var(--tint-border-negative);
    padding: var(--space-2);
    border-radius: var(--radius-md);
    font-size: var(--font-size-sm);
    text-align: center;
}

/* ==================== números em tabela ==================== */
/* Coluna numérica alinhada à direita já existia nos templates (.text-end). O que
 * faltava era fonte tabular: na Open Sans proporcional o "1" é mais estreito que
 * o "8", então R$ 1.111,11 e R$ 8.888,88 têm largura diferente e a coluna
 * "dança" de linha em linha — com 50 linhas de valores monetários isso é ruído
 * constante. `tabular-nums` fixa a largura do dígito.
 *
 * Aplicado às células à direita da tabela, que neste app são todas numéricas, e
 * ao resumo da paginação. */
.table > :not(caption) > * > .text-end {
    font-variant-numeric: tabular-nums;
}

/* ==================== botão de ação em linha de tabela ==================== */
/* Ação por linha vira ÍCONE, não texto. Em Produtos eram 3 botões de texto por
 * linha × 50 linhas = 150 rótulos competindo com o dado — a coluna de ações
 * dominava a leitura da tabela.
 *
 * ⚠️ Ícone sozinho não é rótulo: cada botão leva `aria-label` E `title`. Sem o
 * aria-label o leitor de tela anuncia um link sem nome. */
.btn-acao {
    /* Quadrado, para os 2–3 botões da linha formarem uma régua regular. O
     * padding do .btn-sm do Bootstrap assume texto e deixaria o ícone
     * descentralizado. */
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 30px;
    height: 30px;
    padding: 0;
}

/* ==================== assistente de IA (widget flutuante) ==================== */
/* Vinha num <style> INLINE dentro de plugins/assistente_ia/templates/.../widget.html,
 * com 20 medidas e 4 cores literais — `color:#fff` e `rgba(0,0,0,.28)` que não
 * acompanhavam o tema. Trazido para cá no Sprint 7.
 *
 * ⚠️ A regra de reserva de espaço apontava para `.fin-main`, classe da era finsis
 * que o layout daqui NÃO tem (o shell usa .layout/.content/.page). Estava MORTA:
 * o botão flutuante tapava a última linha de toda tela. Agora é `.page`.
 *
 * As medidas ficam aqui, não em tokens: são decisão deste componente e não se
 * repetem em nenhum outro — é a regra do cabeçalho deste arquivo. */

/* Reserva no fim do conteúdo para o FAB nunca cobrir a última linha ou ação.
 * Deriva do tamanho do botão + respiro: 54 + 18 + 24. */
.page { padding-bottom: 96px; }

#iaFab {
    position: fixed;
    right: 18px;
    bottom: 18px;
    /* Abaixo do offcanvas do Bootstrap (1045) e acima do resto: o painel do
     * assistente tem de cobrir o próprio botão ao abrir. */
    z-index: 1040;
    width: 54px;
    height: 54px;
    border-radius: 50%;
    border: none;
    line-height: 1;
    color: var(--text-on-brand);
    background: var(--dpai-primary);
    box-shadow: var(--shadow-float);
    cursor: grab;
    /* O botão é arrastável: sem isto o toque rola a página em vez de mover. */
    touch-action: none;
    user-select: none;
}
#iaFab:hover { background: var(--dpai-link); }
#iaFab.iadrag { cursor: grabbing; box-shadow: var(--shadow-float-ativo); }
/* O ícone dentro do FAB é maior que o de linha, para o alvo de 54px ler bem. */
#iaFab .icon { width: 24px; height: 24px; vertical-align: middle; }

/* Bolhas da conversa. Assimetria no canto (raio menor de um lado) indica quem
 * fala, sem depender de cor — a mesma lógica de redundância dos badges. */
#iaMsgs .ia-bubble {
    max-width: 88%;
    padding: var(--space-2) var(--space-3);
    border-radius: var(--radius-lg);
    margin-bottom: var(--space-2);
    font-size: var(--font-size-sm);
    line-height: 1.35;
    white-space: pre-wrap;
    overflow-wrap: anywhere;
}
#iaMsgs .ia-user {
    margin-left: auto;
    background: var(--dpai-primary);
    color: var(--text-on-brand);
    border-bottom-right-radius: var(--radius-sm);
}
#iaMsgs .ia-bot {
    background: var(--bg-alt);
    color: var(--text-primary);
    border: 1px solid var(--border-card);
    border-bottom-left-radius: var(--radius-sm);
}

/* ==================== paginação ==================== */
/* Componente core/_paginacao.html. A estrutura (.pagination/.page-link) é do
 * Bootstrap; aqui só o arranjo e o resumo. */
.paginacao {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-2);
    margin-top: var(--space-3);
}
/* O resumo "1–50 de 26.501" é contexto, não ação: pesa menos que os links. */
.paginacao-resumo {
    color: var(--text-secondary);
    font-size: var(--font-size-xs);
    /* Números tabulares: sem isso a contagem muda de largura a cada página e o
     * bloco inteiro dança na horizontal ao navegar. */
    font-variant-numeric: tabular-nums;
}
/* Os pares de seta (primeira/última) ficam colados, para ler como um símbolo só. */
.paginacao .page-link .icon + .icon { margin-left: -5px; }

/* ==================== cor semântica em texto ==================== */
/* Mesma razão das badges: vocabulário de DOMÍNIO, não de framework. O Bootstrap
 * dá .text-success/.text-danger, mas usa a cor pura do status, que não passa
 * contraste em texto pequeno sobre superfície clara — o --on-tint-* é a versão
 * calculada para leitura (AA nos dois temas), e é a mesma cor das badges. */
.txt-positive { color: var(--on-tint-positive); }
.txt-negative { color: var(--on-tint-negative); }
.txt-warning  { color: var(--on-tint-warning); }
.txt-info     { color: var(--on-tint-info); }

/* ==================== controles de formulário ==================== */
/* ⚠️ MESMA ARMADILHA DOS BOTÕES, documentada na §4.1 do CLAUDE.md da plataforma:
 * os controles de formulário do Bootstrap 5.3 trazem cor COMPILADA DO SASS, não
 * variável. Redefinir --bs-primary não muda campo nenhum. O que está no CSS
 * vendorizado é literal:
 *
 *     .form-control:focus  { border-color:#86b7fe; box-shadow:0 0 0 .25rem rgba(13,110,253,.25) }
 *     .form-check-input:checked { background-color:#0d6efd; border-color:#0d6efd }
 *
 * Resultado até o Sprint 7: TODO campo do app tinha anel de foco azul do
 * Bootstrap, e todo checkbox marcado era #0d6efd — a marca desaparecia exatamente
 * onde o usuário está agindo. Botões e badges já tinham override explícito; os
 * campos ficaram de fora.
 *
 * O anel reusa --focus-ring, o mesmo token do :focus-visible global, então campo
 * e link focam igual. */
.form-control:focus,
.form-select:focus,
.form-check-input:focus {
    border-color: var(--dpai-primary);
    box-shadow: var(--focus-ring);
}
.form-check-input:checked {
    background-color: var(--dpai-primary);
    border-color: var(--dpai-primary);
}
/* Campo inválido em foco: o anel acompanha o erro, senão o indigo do foco
 * contradiz a borda vermelha e o estado fica ambíguo. */
.form-control.is-invalid:focus,
.was-validated .form-control:invalid:focus,
.form-select.is-invalid:focus,
.was-validated .form-select:invalid:focus {
    border-color: var(--status-negative);
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--status-negative) 25%, transparent);
}
/* Texto de erro e de ajuda do django-bootstrap5. O invalid-feedback já herda de
 * --bs-form-invalid-color (variável de verdade); a ajuda precisava do token. */
.form-text { color: var(--text-secondary); }

/* ==================== badges de status ==================== */
/* Portado do HarbourPulse. A base é a `.badge` do Bootstrap; aqui só formato
 * pílula + tema.
 *
 * ⚠️ As variantes seguem o vocabulário do DOMÍNIO — positive/negative/warning/
 * info/neutral — e não o do Bootstrap (success/danger/…). A razão não é estética:
 * a chave costuma vir do Python, e o backend não deve depender do nome de um
 * framework de CSS. Trocar de framework não pode virar migração de dados.
 *
 * Antes do Sprint 6 as 14 badges usavam `text-bg-success|danger|warning|info|
 * secondary` direto — o nome do Bootstrap vazando para o template.
 *
 * Os pares tint/on-tint já existiam no tokens.css, calculados para AA nos dois
 * temas, e não eram usados. */
.badge {
    --bs-badge-padding-x: 10px;
    --bs-badge-padding-y: 3px;
    --bs-badge-font-size: var(--font-size-xs);
    --bs-badge-font-weight: 600;
    --bs-badge-border-radius: 999px;
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
    line-height: 1.4;
    border: 1px solid transparent;
}
.badge-positive { background: var(--tint-positive); color: var(--on-tint-positive); border-color: var(--tint-border-positive); }
.badge-warning  { background: var(--tint-warning);  color: var(--on-tint-warning);  border-color: var(--tint-border-warning); }
.badge-negative { background: var(--tint-negative); color: var(--on-tint-negative); border-color: var(--tint-border-negative); }
.badge-info     { background: var(--tint-info);     color: var(--on-tint-info);     border-color: var(--tint-border-info); }
/* Neutro: sem semântica de estado. Usa a superfície alternativa, não um tint.
 *
 * ⚠️ A cor é --text-primary, NÃO --text-secondary. O cálculo de contraste do
 * Sprint 6 reprovou o secundário: #6B7280 sobre --bg-alt #F3F2EE dá **4.32:1**,
 * abaixo do piso de 4.5:1 para texto pequeno — e badge é texto pequeno (12px).
 * Com --text-primary vai a 13.85:1 no claro e 12.56:1 no escuro. Foi um defeito
 * que só o cálculo achou: na tela os dois parecem iguais. */
.badge-neutral  { background: var(--bg-alt); color: var(--text-primary); border-color: var(--border-card); }

/* ============================ tabelas e botões ============================ */
.btn-primary {
    background: var(--dpai-primary);
    color: var(--text-on-brand);
    border: 0;
    padding: var(--space-2) var(--space-4);
    border-radius: var(--radius-md);
    font-size: var(--font-size-base);
    font-family: inherit;
    cursor: pointer;
    display: inline-block;
}
.btn-primary:hover {
    background: color-mix(in srgb, var(--dpai-primary) 85%, black);
    color: var(--text-on-brand);
}

/* Tabela larga precisa de contêiner com overflow-x — sem isso a página inteira
 * rola na horizontal. */
.tabela-scroll { overflow-x: auto; }

table.grid {
    width: 100%;
    border-collapse: collapse;
    background: var(--bg-card);
    border: 1px solid var(--border-card);
    border-radius: var(--radius-md);
    overflow: hidden;
}
table.grid th, table.grid td {
    padding: var(--space-2) var(--space-3);
    border-bottom: 1px solid var(--border-card);
    text-align: left;
    font-size: var(--font-size-sm);
}
table.grid th {
    background: var(--bg-header);
    color: var(--on-header);
    font-weight: 600;
    font-size: var(--font-size-xs);
    text-transform: uppercase;
    letter-spacing: 0.04em;
}

/* Vocabulário de DOMÍNIO, não de framework: `badge-positive`, não
 * `badge-success`. A chave é gerada no Python, e trocar de framework de CSS
 * não toca em Python nenhum. */
.badge {
    padding: 2px var(--space-2);   /* 2px: ajuste óptico da caixa de linha */
    border-radius: 999px;          /* pill: raio full, conforme o traço da marca */
    font-size: var(--font-size-xs);
    background: var(--tint-brand-soft);
    color: var(--dpai-link);
}
.badge-positive { background: var(--tint-positive); color: var(--on-tint-positive); }
.badge-warning  { background: var(--tint-warning);  color: var(--on-tint-warning); }
.badge-negative { background: var(--tint-negative); color: var(--on-tint-negative); }
.badge-info     { background: var(--tint-info);     color: var(--on-tint-info); }

/* ============================ toggle de tema ============================ */
.theme-toggle {
    background: none;
    border: 1px solid var(--border-card);
    border-radius: var(--radius-md);
    color: var(--text-secondary);
    cursor: pointer;
    padding: var(--space-1) var(--space-2);
    line-height: 1;
}
.theme-toggle:hover { background: var(--bg-hover); color: var(--text-primary); }
/* Os dois símbolos ficam sempre no DOM; o CSS mostra o relevante. Alternar por
 * JS daria flash a cada navegação.
 *
 * Cada símbolo nomeia o DESTINO do clique: no tema claro aparece a lua (para
 * onde vai), então o símbolo escondido é o do estado atual. Desde o Sprint 5
 * são ícones do sprite, não texto — e herdam a cor do botão via currentColor. */
[data-bs-theme="light"] .theme-toggle .rotulo-claro,
[data-bs-theme="dark"]  .theme-toggle .rotulo-escuro { display: none; }

/* ============================ responsivo ============================ */
/* Desktop-first (max-width), coerente com o uso: quem opera um ERP trabalha em
 * tela grande. Tablet é caso secundário. */
@media (max-width: 768px) {
    .layout { flex-direction: column; }
    .sidebar { width: 100%; }
    .page { padding: var(--space-4); }
}
@media (max-width: 640px) {
    .cards { grid-template-columns: 1fr; }
    .topbar { flex-wrap: wrap; }
}
