:root {
  /* Modo claro é o padrão destas variáveis; o modo escuro mora no bloco
     [data-theme="dark"] logo abaixo. Quem decide qual dos dois usar é
     js/theme-toggle.js — pelo sistema operacional na primeira visita, pela
     escolha manual salva depois disso — e aplica o atributo ANTES da
     primeira pintura, num trecho inline no <head> de cada tela, para o tema
     errado não piscar por uma fração de segundo. */

  /* Avisa o navegador de qual tema é o nosso. Sem isto, o que o navegador
     desenha SOZINHO continua claro no modo escuro: a barra de rolagem, e
     principalmente o calendário nativo que abre no <input type="month"> do
     Painel — um retângulo branco estourando no meio de uma tela escura. */
  color-scheme: light;

  /* ————— O código de cor —————
     Cada TIPO de número tem uma cor, e sempre a mesma, em toda tela:

       azul     dinheiro parado no banco      --banco
       verde    entrou, ou ainda vai entrar   --receita
       vermelho saiu, ou ainda vai sair       --despesa
       âmbar    vence logo, fatura fechando   --warning
       roxo     cartão                        --violet

     O número vem colorido SEMPRE, inclusive quando está tudo tranquilo: a
     cor diz o que o número É, não se a pessoa deve se preocupar. Quem diz
     que algo é urgente é a posição na tela (o bloco de cima) e a palavra
     ("vencida") — não uma cor que muda debaixo do mesmo rótulo.
     Antes, --despesa (#a5432a) e --warning (#b5562b) eram quase a mesma
     tinta: gasto normal e alarme tinham a mesma cara. */
  --bg: #f2f4f1;
  --surface: #ffffff;
  --border: #d9dfda;
  --text: #1f2a24;
  --text-muted: #5c6b62;
  --primary: #2e7d5b;
  --primary-dark: #23684b;
  --receita: #23684b;
  --despesa: #b6402e;
  /* Âmbar tem dois tons porque um só não serve para as duas funções: o que
     é claro o bastante para preencher um chip é claro demais para ler em
     cima de branco. --warning é o de LER, --warning-fill o de PINTAR. */
  --warning: #8a5a0e;
  --violet: #6b4a63;
  --btn-text: #ffffff;
  /* Fundo da linha sob o cursor. Preto/branco translúcido em vez de uma cor
     fixa: as tabelas ficam ora sobre --surface (dentro de painel), ora sobre
     --bg, e um tom transparente escurece/clareia o que estiver embaixo em vez
     de brigar com ele. */
  --row-hover: rgba(31, 42, 36, 0.045);
  --radius: 12px;
  /* Sombra quase nula. Sobre um fundo que não é branco, a borda de 1px já
     separa o painel do que está atrás; a sombra só existe para o painel não
     parecer colado. */
  --shadow: 0 1px 2px rgba(31, 42, 36, 0.05);

  /* Atkinson Hyperlegible, desenhada pelo Braille Institute justamente para
     quem tem dificuldade de leitura: cada letra é feita para NÃO poder ser
     confundida com outra — o l não vira I, o 0 não vira O, o b não vira d.
     Numa tela onde tudo é número e nome de conta, a fonte é o argumento de
     acessibilidade, não um enfeite.
     Antes, --font-body e --font-display eram a mesma coisa (Calibri), então
     não havia distinção nenhuma entre título e texto; e Calibri/Carlito/
     Candara são fontes de Office, ou seja, em Linux, Android e iPhone tudo
     isso já caía em Arial de qualquer jeito. */
  --font-body: "Atkinson Hyperlegible Next", "Segoe UI", system-ui, sans-serif;
  --font-display: "Atkinson Hyperlegible Next", "Segoe UI", system-ui, sans-serif;

  /* Escala de fonte e de espaçamento. Não existiam — cada tela tinha os
     próprios números soltos (10.5, 11, 12, 13, 13.5, 14, 15.5, 19, 22px), e a
     informação que mais precisa ser lida (texto de instrução) acabava sempre
     no menor tamanho e na cor mais apagada. Usar daqui pra frente; os
     literais existentes não foram reescritos de uma vez, para não abrir uma
     mudança visual gigante no meio de um ajuste de acessibilidade. */
  --fs-xs: 12px;
  --fs-sm: 13px;
  --fs-base: 15px;
  --fs-md: 16px;
  --fs-lg: 18px;
  --fs-xl: 22px;
  --space-1: 4px;
  --space-2: 8px;
  --space-3: 12px;
  --space-4: 16px;
  --space-5: 24px;
  --space-6: 32px;

  /* ————— Tokens novos, ainda com os valores de hoje —————
     Esta leva não muda um pixel de propósito: cada valor abaixo é
     exatamente o que já estava escrito à mão em algum lugar do arquivo.
     Trocar o VALOR (a identidade nova) é um passo separado, e só depois
     que os literais espalhados tiverem virado var(). Assim, se a cara do
     sistema mudar errado, dá para ver a mudança num commit só. */

  /* Dois raios, não um. `--radius` (10px) só vale para painel, cartão e
     diálogo; botão, campo, item de menu e chip sempre foram 8px na mão —
     28 literais contra 14 usos do token. Sem o segundo token, mudar o raio
     mexia em metade da interface e deixava a outra metade para trás. */
  --radius-sm: 8px;

  /* Azul de banco. Ainda não é usado por ninguém: entra agora para o
     código de cor ficar declarado inteiro num lugar só. Verde é o que
     entrou, vermelho o que saiu, âmbar o que está para vencer, roxo o
     cartão (--violet, que já era a cor de cartão nos badges de origem) e
     azul o dinheiro parado no banco. A cor diz O QUE É o número, não se a
     pessoa deve se preocupar — isso quem diz é a posição e a palavra. */
  --banco: #2b5f8f;
  /* Fundo de bloco que precisa se destacar do painel sem virar caixa nova. */
  --surface-2: #eaeeea;

  /* `--warning` é lido como TEXTO em quatro lugares e como FUNDO em outros
     tantos, e o mesmo tom não serve para os dois: âmbar claro o bastante
     para preencher é claro demais para ler em cima de branco. O âmbar de
     preenchimento vivia hardcoded como #f59e0b em cinco pontos do arquivo
     (um deles regra morta, que saiu junto); é ele que passa a morar aqui. */
  --warning-fill: #f2b84b;

  --fs-2xl: 28px;
  --fs-display: 34px;

  /* Dinheiro. A versão monoespaçada da mesma família: uma coluna de valores
     alinha vírgula com vírgula, e o zero cortado não se confunde com O. */
  --font-mono: "Atkinson Hyperlegible Mono", Consolas, "Cascadia Mono", monospace;

  /* A paleta do gráfico de rosca morava dentro do charts.js, fora do CSS e
     fora do tema: as mesmas doze cores no claro e no escuro. Aqui elas
     entram idênticas ao array antigo; o escuro ganha os seus no passo da
     identidade.
     Nenhuma delas repete --despesa nem --warning de propósito: num gráfico
     onde TODAS as fatias são despesa, pintar uma delas da cor de "isto é uma
     despesa" ou de "isto é um alerta" só faz o olho procurar um significado
     que não existe. Vizinhas nunca são do mesmo tom, para a fatia seguinte
     nunca encostar numa parecida. */
  --chart-1: #2e7d5b;
  --chart-2: #3d6e9c;
  --chart-3: #8b5e8f;
  --chart-4: #c08a2e;
  --chart-5: #4fa07e;
  --chart-6: #6e86a8;
  --chart-7: #a85c6b;
  --chart-8: #7a8f3f;
  --chart-9: #2a6b6b;
  --chart-10: #c4744a;
  --chart-11: #5b5fa3;
  --chart-12: #7e8b95;

  /* Usado no detalhe da fatura desde sempre, declarado nunca: o valor
     abaixo é o mesmo que estava no fallback do var(). */
  --bg-subtle: rgba(127, 127, 127, 0.06);
}

.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* Primeiro elemento focável da página. Some visualmente até receber foco —
   aí precisa aparecer por cima de tudo, porque é o atalho para quem navega
   por teclado pular a sidebar inteira (hoje ~13 links) e ir direto ao
   conteúdo. Sem ele, chegar na tabela de Lançamentos por Tab exigia passar
   pela sidebar inteira mais os chips de saldo — nenhum atalho existia. */
.skip-link {
  position: absolute;
  top: -100px;
  left: 12px;
  z-index: 200;
  background: var(--primary);
  color: var(--btn-text);
  padding: 10px 16px;
  border-radius: var(--radius-sm);
  font-size: var(--fs-sm);
  font-weight: 600;
  text-decoration: none;
  transition: top 0.15s ease;
}

.skip-link:focus {
  top: 12px;
}

@media (prefers-reduced-motion: reduce) {
  .skip-link {
    transition: none;
  }
}

/* O escuro repete o mesmo código de cor do claro — azul de banco continua
   azul, vermelho de saída continua vermelho — só que puxado para cima em
   claridade, porque cor escura sobre fundo escuro não se lê. */
:root[data-theme="dark"] {
  --bg: #161b18;
  --surface: #1f2621;
  --surface-2: #262e28;
  --border: #2e3832;
  --text: #e8ece9;
  --text-muted: #9aa79f;
  --primary: #6fbf95;
  --primary-dark: #8ed1b0;
  --receita: #6fbf95;
  --despesa: #e5776c;
  --warning: #f2c46b;
  --violet: #b98cb9;
  --banco: #7fb3e0;
  --warning-fill: #f2c46b;
  /* Texto do botão: no escuro o botão é verde CLARO, então a letra em cima
     dele precisa ser escura, não clara. */
  --btn-text: #10201a;
  --row-hover: rgba(255, 255, 255, 0.055);
  --shadow: 0 1px 2px rgba(0, 0, 0, 0.35);
  --chart-1: #5fb88c;
  --chart-2: #7ba9d6;
  --chart-3: #c295c6;
  --chart-4: #e0b45f;
  --chart-5: #7fd4b0;
  --chart-6: #a3b5ce;
  --chart-7: #d695a0;
  --chart-8: #b0c46b;
  --chart-9: #5fa8a8;
  --chart-10: #e5a17a;
  --chart-11: #9295d6;
  --chart-12: #b6c3ce;
  --bg-subtle: rgba(255, 255, 255, 0.04);
  color-scheme: dark;
}

* {
  box-sizing: border-box;
}

/* Toque no celular. `manipulation` desliga a espera de ~300ms que o
   navegador guardava para ver se vinha um segundo toque (zoom por toque
   duplo) — sem isso, todo botão parece meio segundo mais lento do que é. E
   o realce de toque padrão do Android é um retângulo azul que ignora o raio
   do canto; trocado por um véu na cor da marca. */
a,
button,
summary,
label,
[role="button"],
[role="option"] {
  touch-action: manipulation;
  -webkit-tap-highlight-color: color-mix(in srgb, var(--primary) 18%, transparent);
}

/* Um só lugar para desligar movimento, em vez de um bloco por componente.
   Havia quatro (skip-link, gaveta, colapsável, toast) e eles cobriam metade
   das animações: o cartão clicável, os dois painéis que abrem por max-height
   e o interruptor continuavam se mexendo para quem pediu ao sistema que a
   tela ficasse parada. Fica quase instantâneo em vez de zero para não
   quebrar quem escuta transitionend. */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

body {
  margin: 0;
  font-family: var(--font-body);
  background: var(--bg);
  color: var(--text);
}

a {
  color: inherit;
  text-decoration: none;
}

nav.topbar {
  display: flex;
  align-items: center;
  gap: 4px;
  padding: 0 20px;
  height: 56px;
  background: var(--surface);
  border-bottom: 1px solid var(--border);
  position: sticky;
  top: 0;
  z-index: 10;
  overflow-x: auto;
}

nav.topbar .brand {
  font-family: var(--font-display);
  font-weight: 700;
  margin-right: 20px;
  white-space: nowrap;
  padding: 0;
  color: var(--text);
  text-decoration: none;
}

nav.topbar .brand:hover {
  background: none;
  color: var(--text);
}

/* ---------------------------------------------------------------------------
   Layout de autenticação (login, cadastro, trocar senha, painel do
   consultor) — cartão único centralizado numa página sem sidebar. Essas 5
   telas não têm #app-nav, então nav.js/nav-search.js nunca rodam nelas.
   --------------------------------------------------------------------------- */
body.auth-page {
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 20px;
}

.auth-card {
  width: 100%;
  max-width: 380px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: 28px;
}

.auth-card h1 {
  font-family: var(--font-display);
  font-size: 1.15rem;
  margin: 0 0 4px;
}

.auth-card .hint {
  color: var(--text-muted);
  font-size: var(--fs-sm);
  margin: 0 0 20px;
}

.auth-card label {
  margin: 14px 0 4px;
}

.auth-card input {
  padding: 9px 10px;
  font-size: 0.95rem;
}

.auth-card input[type="password"],
.auth-card input.pw-input {
  padding-right: 36px;
}

.auth-card .pw-field {
  position: relative;
}

.auth-card .pw-toggle {
  position: absolute;
  right: 4px;
  top: 4px;
  width: 28px;
  height: 28px;
  margin: 0;
  padding: 0;
  background: transparent;
  border: none;
  color: var(--text-muted);
  font-size: 1rem;
  line-height: 1;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
}

.auth-card .pw-toggle:hover {
  background: var(--border);
  border-radius: 6px;
}

.auth-card button[type="submit"] {
  margin-top: 20px;
  width: 100%;
  padding: 10px;
  font-size: 0.95rem;
}

.auth-card button[type="submit"]:disabled {
  opacity: 0.6;
  cursor: default;
}

.auth-links {
  margin-top: 16px;
  font-size: var(--fs-sm);
  color: var(--text-muted);
  display: flex;
  justify-content: space-between;
}

.auth-links.center {
  justify-content: center;
}

.auth-links a {
  color: var(--primary);
}

.auth-links a:hover {
  text-decoration: underline;
}

.auth-back {
  display: inline-block;
  margin-bottom: 16px;
  font-size: var(--fs-sm);
  color: var(--text-muted);
}

.auth-back:hover {
  color: var(--text);
}

/* ---------------------------------------------------------------------------
   Páginas legais (política de privacidade, termos de uso) — texto corrido,
   sem cartão nem sidebar: são as duas telas que existem para SEREM LIDAS, não
   para receber clique. Largura de leitura (65ch) em vez do --radius/--shadow
   das outras telas de conta, que fazem sentido para um formulário curto, não
   para um parágrafo.
   --------------------------------------------------------------------------- */
body.legal-page {
  min-height: 100vh;
  padding: 32px 20px 64px;
}

.legal-content {
  max-width: 65ch;
  margin: 0 auto;
}

.legal-content h1 {
  font-family: var(--font-display);
  font-size: 1.4rem;
  margin: 0 0 4px;
}

.legal-content .legal-updated {
  color: var(--text-muted);
  font-size: var(--fs-sm);
  margin: 0 0 28px;
}

.legal-content h2 {
  font-family: var(--font-display);
  font-size: 1.05rem;
  margin: 28px 0 8px;
}

.legal-content p, .legal-content li {
  line-height: 1.6;
  color: var(--text);
}

.legal-content ul {
  padding-left: 20px;
}

.legal-content a {
  color: var(--primary);
}

/* ---------------------------------------------------------------------------
   Barra lateral de navegação (telas do cliente — ver js/nav.js).
   O painel do consultor continua usando nav.topbar, acima.
   --------------------------------------------------------------------------- */

/* ============================================================================
   `hidden` ESCONDE. Uma regra, no começo do arquivo, e nunca mais.

   ⚠️ ISTO JÁ CUSTOU QUATRO DEFEITOS NESTE PROJETO, o último deles trancando o
   dono do sistema fora dele.

   `[hidden]` é só um estilo do navegador com especificidade de atributo
   (0,1,0,0) — a mesma de uma classe. QUALQUER regra deste arquivo que declare
   `display` no mesmo elemento vence por ordem de cascata, e o atributo `hidden`
   deixa de fazer efeito sem avisar ninguém: o JavaScript põe `el.hidden = true`,
   o elemento continua na tela, e o defeito só aparece como "o botão que não
   devia estar aí".

   Três remendos avulsos foram escritos antes desta linha, um por elemento
   (`.bulk-cancel-btn-th[hidden]`, `.tx-bulk-mobile[hidden]`,
   `.palette-overlay[hidden]`), cada um com um comentário explicando esta mesma
   armadilha. O quarto caso foi o "← Voltar" da tela de trocar senha
   (`.auth-back` é `display: inline-block`): a tela mandava esconder o link e
   ele ficava visível, oferecendo uma saída que devolvia a pessoa para a própria
   tela. Remendar caso a caso é o que garante um quinto.

   `!important` aqui é o uso legítimo dele: a intenção "este elemento está
   escondido" tem de ganhar de qualquer `display` de layout, sempre. Quem
   precisa esconder de outro jeito (animação de saída, por exemplo) usa uma
   classe, não o atributo.
   ============================================================================ */
[hidden] {
  display: none !important;
}

:root {
  --sidebar-w: 246px;

  /* Altura do que fica GRUDADO no topo e cobre o começo do conteúdo — só a
     faixa de saldo, no computador. Existe para o scroll-margin-top lá
     embaixo ter um número só para acertar quando a faixa mudar de tamanho.
     No celular entra também a barra fixa de 52px (ver a media query). */
  --stuck-h: 48px;
}

body.has-sidebar {
  padding-left: var(--sidebar-w);
}

/* A faixa de saldo gruda no topo, já que não há mais menu horizontal acima. */
body.has-sidebar #balance-banner {
  top: 0;
}

.sidebar {
  position: fixed;
  top: 0;
  left: 0;
  bottom: 0;
  width: var(--sidebar-w);
  background: var(--surface);
  border-right: 1px solid var(--border);
  overflow-y: auto;
  overscroll-behavior: contain;
  z-index: 20;
  /* A barra de gestos do iPhone come os últimos ~34px da tela; sem isto, o
     último item do menu fica debaixo dela e não dá para tocar. */
  padding-bottom: calc(20px + env(safe-area-inset-bottom));
  padding-top: env(safe-area-inset-top);
  padding-left: env(safe-area-inset-left);
}

/* A marca era o emoji 💰 seguido do nome. Emoji desenha diferente em cada
   sistema, muda de cara a cada atualização e some no leitor de tela como
   "saco de dinheiro". No lugar dele, um quadradinho na cor da marca: mesma
   função de âncora no alto da barra — o olho volta sempre ao mesmo ponto —
   sem depender do emoji de ninguém. */
.sidebar-brand {
  display: flex;
  align-items: center;
  gap: 10px;
  position: sticky;
  top: 0;
  background: var(--surface);
  border-bottom: 1px solid var(--border);
  padding: 14px 18px 12px;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: var(--fs-md);
  letter-spacing: -0.01em;
  color: var(--text);
  z-index: 1;
}

.sidebar-brand::before {
  content: "";
  flex-shrink: 0;
  width: 14px;
  height: 14px;
  border-radius: 4px;
  background: var(--primary);
}

.sidebar-group {
  padding: 10px 10px 0;
}

.sidebar-group-label {
  font-size: var(--fs-xs);
  letter-spacing: 0.09em;
  text-transform: uppercase;
  font-weight: 700;
  color: var(--text-muted);
  padding: 0 10px 4px;
}

.sidebar-item {
  display: block;
  padding: 4px 10px 5px;
  border-radius: var(--radius-sm);
}

.sidebar-item .t {
  display: block;
  font-size: var(--fs-sm);
  line-height: 1.3;
  color: var(--text);
}

.sidebar-item .d {
  display: block;
  font-size: var(--fs-xs);
  line-height: 1.25;
  color: var(--text-muted);
}

.sidebar-item:hover {
  background: var(--bg);
}

/* Onde eu estou: o item ativo ganha fundo E uma barra na lateral. Só o fundo
   não bastava — é o item que responde "onde eu estou" numa lista de catorze,
   e quem se perde ao trocar de tela precisa achar isso sem procurar. */
.sidebar-item.active {
  background: color-mix(in srgb, var(--primary) 12%, transparent);
  box-shadow: inset 3px 0 0 var(--primary);
}

.sidebar-item.active .t {
  color: var(--primary-dark);
  font-weight: 700;
}

.sidebar-item:focus-visible {
  outline: 2px solid var(--primary);
  outline-offset: -2px;
}

/* Contorno de foco geral. Especificidade ZERO por causa do :where() — é o
   que faz as regras acima e abaixo continuarem valendo naquilo que definem:
   .sidebar-item já tem a sua (traço para dentro, para não vazar da sidebar);
   button.danger define cor/borda/fundo mas não define outline, então PASSA A
   COMPOR com esta regra em vez de brigar — contorno normal por cima do
   vermelho de aviso; input/select usam :focus (não :focus-visible) e não
   entram aqui, intocados. Antes só existia contorno dedicado para
   input/select/.sidebar-item/button.danger/.toggle-switch — todo o resto
   (link comum, botão comum, card clicável, badge clicável) ficava com o
   outline padrão do navegador ou sem nenhum. */
:where(a, button, summary, [role="option"], [role="button"], [tabindex]):focus-visible {
  outline: 2px solid var(--primary);
  outline-offset: 2px;
}

/* Barra de topo com o botão do menu — só aparece em tela estreita. */
.nav-toggle {
  display: none;
  align-items: center;
  gap: 10px;
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  /* No iPhone a barra encosta no entalhe da câmera e nos cantos
     arredondados: sem a margem de segurança do sistema, o ☰ fica embaixo do
     vidro. `env()` vale 0 em tudo que não tem entalhe, então a conta não
     muda nada no computador. */
  height: calc(52px + env(safe-area-inset-top));
  padding: env(safe-area-inset-top) max(14px, env(safe-area-inset-right)) 0
    max(14px, env(safe-area-inset-left));
  background: var(--surface);
  border-bottom: 1px solid var(--border);
  z-index: 30;
}

.nav-toggle button {
  font-size: var(--fs-lg);
  line-height: 1;
  padding: 6px 11px;
  background: none;
  border: 1px solid var(--border);
  color: var(--text);
}

.nav-toggle-brand {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: var(--fs-base);
}

.sidebar-overlay {
  display: none;
}

@media (max-width: 900px) {
  body.has-sidebar {
    padding-left: 0;
    padding-top: calc(52px + env(safe-area-inset-top));
    /* Barra fixa (52px) mais a faixa de saldo, que gruda logo abaixo dela. */
    --stuck-h: calc(100px + env(safe-area-inset-top));
  }

  body.has-sidebar #balance-banner {
    top: calc(52px + env(safe-area-inset-top));
  }

  .nav-toggle {
    display: flex;
  }

  .sidebar {
    transform: translateX(-100%);
    transition: transform 0.2s ease;
    box-shadow: 4px 0 24px rgba(0, 0, 0, 0.18);
  }

  .sidebar.open {
    transform: none;
  }

  .sidebar-overlay.open {
    display: block;
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, 0.45);
    z-index: 19;
  }
}

@media (prefers-reduced-motion: reduce) {
  .sidebar {
    transition: none;
  }
}

nav.topbar a {
  padding: 8px 14px;
  border-radius: var(--radius-sm);
  color: var(--text-muted);
  font-size: var(--fs-base);
  white-space: nowrap;
}

nav.topbar a:hover {
  background: var(--bg);
  color: var(--text);
}

nav.topbar a.active {
  background: var(--primary);
  color: var(--btn-text);
}

main {
  max-width: 1100px;
  margin: 0 auto;
  padding: 24px 20px 60px;
}

/* Quem clica em "Pular para o conteúdo" cai no <main>, e o navegador rola
   até ele — mas a faixa de saldo gruda no topo e cobria justamente o que
   acabou de receber o foco. O atalho existia e mesmo assim a pessoa não via
   onde tinha parado. Mesma conta vale para o link direto a um título. */
main,
h1,
h2 {
  scroll-margin-top: calc(var(--stuck-h) + 12px);
}

/* Título é a frase mais curta da tela e a mais fácil de quebrar feio: sem
   isto, "O que precisa de você hoje" larga um "hoje" sozinho na segunda
   linha. `balance` reparte as linhas para ficarem do mesmo tamanho. */
h1,
h2 {
  text-wrap: balance;
}

/* Migalha derivada do menu (nav.js), sempre "Grupo › Tela". É a resposta a
   "perco o contexto ao trocar de tela": com ela, o contexto fica escrito na
   própria página, e não só marcado (de leve) na sidebar — que no celular
   está fechada na maior parte do tempo. */
.page-crumb {
  font-size: var(--fs-sm);
  color: var(--text-muted);
  margin-bottom: var(--space-2);
}

.page-crumb a {
  color: var(--text-muted);
  text-decoration: underline;
  text-decoration-color: var(--border);
  text-underline-offset: 2px;
}

.page-crumb a:hover {
  color: var(--text);
}

.page-crumb [aria-current="page"] {
  color: var(--text);
  font-weight: 600;
}

h1 {
  font-family: var(--font-display);
  font-size: var(--fs-2xl);
  font-weight: 700;
  margin: 0 0 20px;
}

h2 {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: var(--fs-lg);
  margin: 0 0 14px;
  color: var(--text-muted);
  font-weight: 600;
}

.cards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: 14px;
  margin-bottom: 28px;
}

.card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 16px 18px;
  box-shadow: var(--shadow);
}

.card .label {
  font-size: var(--fs-sm);
  color: var(--text-muted);
  margin-bottom: 6px;
}

.card .value {
  font-size: var(--fs-xl);
  font-weight: 700;
}

.value.receita { color: var(--receita); }
.value.despesa { color: var(--despesa); }

/* Card que leva pra lista já filtrada (ex.: gastos extras do mês). */
a.card {
  display: block;
  color: inherit;
  text-decoration: none;
  transition: border-color 0.15s, transform 0.15s;
}

a.card:hover {
  border-color: var(--primary);
  transform: translateY(-1px);
}

.card .hint {
  font-size: var(--fs-xs);
  color: var(--text-muted);
  margin-top: 4px;
}

.panel {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 18px 20px;
  box-shadow: var(--shadow);
  margin-bottom: 24px;
}

.charts-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(340px, 1fr));
  gap: 16px;
}

form.form-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
  gap: 12px;
  align-items: end;
}

/* O formulário "Novo empréstimo" tem rótulos de comprimento bem diferente —
   de uma a duas linhas — e o `align-items: end` acima soma esse excesso
   ACIMA do campo mais curto para alinhar o FUNDO de todos, o que empurra
   cada input para uma altura diferente dentro da mesma linha da grade.
   Alinhar pelo topo, com todo rótulo reservando a mesma altura, é o que
   faz o input começar sempre no mesmo lugar. Escopado a este formulário só
   — os outros oito que usam .form-grid têm rótulos homogêneos, e o
   align-items:end ali é o que cola um botão sem rótulo no rodapé da
   fileira, ao lado dos inputs. */
#debt-form {
  align-items: start;
}

#debt-form label {
  min-height: 2.2em;
}

label {
  display: block;
  font-size: var(--fs-sm);
  color: var(--text-muted);
  margin-bottom: 4px;
}

input, select {
  width: 100%;
  padding: 8px 10px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--border);
  background: var(--bg);
  color: var(--text);
  font-size: var(--fs-base);
}

input:focus, select:focus {
  outline: 2px solid var(--primary);
  outline-offset: -1px;
}

button {
  cursor: pointer;
  border: none;
  border-radius: var(--radius-sm);
  padding: 9px 16px;
  font-size: var(--fs-base);
  font-weight: 600;
  font-family: inherit;
  background: var(--primary);
  color: var(--btn-text);
}

button:hover {
  background: var(--primary-dark);
}

button.secondary {
  background: transparent;
  color: var(--text-muted);
  border: 1px solid var(--border);
}

/* Discreto por padrão — só fica vermelho ao passar o mouse ou focar. Excluir
   tinha o mesmo peso visual de Editar lado a lado; a facilidade de acionar
   deveria ser proporcional ao custo do erro, não igual. */
button.danger {
  background: transparent;
  color: var(--text-muted);
  border: 1px solid transparent;
  padding: 5px 10px;
  font-size: var(--fs-xs);
}

button.danger:hover,
button.danger:focus-visible {
  color: var(--despesa);
  border-color: var(--despesa);
  background: color-mix(in srgb, var(--despesa) 8%, transparent);
}

button.small {
  padding: 5px 10px;
  font-size: var(--fs-xs);
  background: transparent;
  color: var(--primary);
  border: 1px solid var(--primary);
}

table {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--fs-base);
}

th, td {
  text-align: left;
  padding: 10px 8px;
  border-bottom: 1px solid var(--border);
}

th {
  color: var(--text-muted);
  font-weight: 600;
  font-size: var(--fs-xs);
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

/* Destaque da linha sob o cursor, em TODA tabela do sistema.

   Numa linha larga — data na ponta esquerda, valor lá na direita — o olho
   perde a fileira no meio do caminho e lê o valor da linha de baixo. O fundo
   levemente puxado diz, sem pedir atenção, qual linha está sendo lida; e dá o
   retorno de "é aqui que vou clicar" antes do clique.

   Fica atrás de `@media (hover: hover)` porque em tela de toque o navegador
   simula hover no último item tocado: o destaque grudava numa linha e não saía
   mais, apontando para a linha errada.

   O fundo vai no <td>, não no <tr>: a linha da tabela não pinta fundo de forma
   confiável quando as células têm background próprio, e é a célula que carrega
   a borda de baixo. */
@media (hover: hover) {
  tbody tr:hover > td {
    background: var(--row-hover);
  }

  /* As linhas-painel não são uma fileira de dados — são a gaveta que abre
     debaixo de uma ("pode ser pagamento de...", editor de divisão). Pintar o
     fundo delas apagaria a cor própria de cada painel. */
  tbody tr.provisioned-match-row:hover > td,
  tbody tr.split-row:hover > td {
    background: transparent;
  }
}

td.num {
  text-align: right;
  font-variant-numeric: tabular-nums;
}

td.num.receita { color: var(--receita); }
td.num.despesa { color: var(--despesa); }

.receita { color: var(--receita); }
.despesa { color: var(--despesa); }

/* As outras duas do código de cor (ver o comentário no :root). `banco` e
   `cartao` não existiam: saldo de conta e saldo de cartão saíam pretos, ou
   pior, verdes/vermelhos conforme o sinal — o que fazia "tenho R$ 3.000 no
   Nubank" ter exatamente a mesma cara de "recebi R$ 3.000". */
.banco { color: var(--banco); }
.cartao { color: var(--violet); }
.atencao { color: var(--warning); }
.value.banco { color: var(--banco); }
.value.cartao { color: var(--violet); }
td.num.banco { color: var(--banco); }
td.num.cartao { color: var(--violet); }

/* Dinheiro é sempre em monoespaçada, para as colunas alinharem vírgula com
   vírgula e o olho poder comparar dois valores pela largura. */
td.num,
.card .value,
.balance-strip-v,
.debt-figure .value,
.month-balance .v,
.resumo-item .v,
.transf-valor,
.legend .amount {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
}

.badge {
  display: inline-block;
  font-size: var(--fs-xs);
  padding: 2px 7px;
  border-radius: 999px;
  font-weight: 600;
  white-space: nowrap;
}

.badge.dup {
  background: color-mix(in srgb, var(--despesa) 15%, transparent);
  color: var(--despesa);
}

.badge.pendente {
  background: color-mix(in srgb, var(--warning) 18%, transparent);
  color: color-mix(in srgb, var(--warning) 78%, black);
}

.badge.autorizado {
  background: color-mix(in srgb, var(--receita) 18%, transparent);
  color: var(--receita);
}

.badge.extra {
  background: color-mix(in srgb, var(--warning) 18%, transparent);
  color: color-mix(in srgb, var(--warning) 78%, black);
}

:root[data-theme="dark"] .badge.pendente,
:root[data-theme="dark"] .badge.extra {
  color: var(--warning);
}

/* Selo "Extra" clicável: marca/desmarca uma despesa como não planejada
   direto na linha, sem precisar entrar no modo de edição. */
button.badge {
  border: 1px solid transparent;
  cursor: pointer;
  font-family: inherit;
  line-height: 1.5;
}

button.badge.extra:hover {
  border-color: var(--warning);
}

button.badge.extra-off {
  background: transparent;
  color: var(--text-muted);
  border-color: var(--border);
  opacity: 0.65;
}

button.badge.extra-off:hover {
  opacity: 1;
  border-color: var(--warning);
  color: var(--warning);
}

button.badge[disabled] {
  cursor: progress;
  opacity: 0.5;
}

.reconcile-row {
  display: flex;
  justify-content: space-between;
  gap: 12px;
  padding: 6px 0;
  border-bottom: 1px solid var(--border);
}

.reconcile-row:last-child {
  border-bottom: none;
}

.progress-bar {
  height: 8px;
  border-radius: 999px;
  background: var(--border);
  overflow: hidden;
  margin: 6px 0;
}

.progress-bar-fill {
  height: 100%;
  border-radius: 999px;
}

.budget-card {
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 14px 16px;
  margin-bottom: 12px;
}

.budget-card .budget-head {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 8px;
  font-size: var(--fs-base);
  margin-bottom: 4px;
}

.row-actions {
  display: flex;
  gap: 6px;
  justify-content: flex-end;
  /* Sem isto, a célula de Ações (14% de largura, table-layout:fixed) deixava
     o conteúdo mais largo — "Editar" + "travado · destravar" numa linha só —
     transbordar para a ESQUERDA (o flex-end ancora pela direita) por cima da
     coluna Valor vizinha, sobrepondo o número. Quebrando linha aqui, o
     conteúdo fica empilhado DENTRO da própria célula. */
  flex-wrap: wrap;
}

.msg {
  font-size: var(--fs-sm);
  margin-top: 10px;
  display: none;
}

.msg.error { color: var(--despesa); display: block; }
.msg.success { color: var(--receita); display: block; }

/* `.provisioned-hint` e o @keyframes que ele usava saíram daqui: nenhum JS
   e nenhum HTML jamais emitiu essa classe. Era a única animação infinita do
   sistema — um ponto âmbar pulsando para sempre, sem parada, sem controle e
   sem respeitar prefers-reduced-motion. Regra morta que só esperava alguém
   ligar. */

.provisioned-match-row td {
  padding: 0;
  border-bottom: none;
}

.provisioned-panel {
  max-height: 0;
  overflow: hidden;
  transition: max-height 0.25s ease;
  background: color-mix(in srgb, var(--warning-fill) 8%, var(--surface));
}

.provisioned-match-row.open .provisioned-panel {
  max-height: 640px;
  overflow-y: auto;
}

.provisioned-panel-inner {
  padding: 12px 16px;
  border-bottom: 1px solid var(--border);
}

.provisioned-panel-title {
  font-size: var(--fs-xs);
  color: var(--text-muted);
  margin-bottom: 8px;
}

.provisioned-match-item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 7px 0;
  border-bottom: 1px dashed var(--border);
}

.provisioned-match-item:last-child {
  border-bottom: none;
}

.provisioned-match-info {
  font-size: var(--fs-sm);
}

.provisioned-match-info .name {
  font-weight: 600;
}

.provisioned-match-info .meta {
  color: var(--text-muted);
  font-size: var(--fs-xs);
}

.provisioned-match-empty {
  font-size: var(--fs-sm);
  color: var(--text-muted);
  padding: 4px 0;
}

/* Painel de "Dividir": mesmo mecanismo de abrir/fechar do provisioned-panel,
   cor própria (azul) pra não parecer o mesmo aviso de "pode ser pagamento". */
.split-row td {
  padding: 0;
  border-bottom: none;
}

.split-panel {
  max-height: 0;
  overflow: hidden;
  transition: max-height 0.25s ease;
  background: color-mix(in srgb, var(--primary) 6%, var(--surface));
}

.split-row.open .split-panel {
  max-height: 640px;
  overflow-y: auto;
}

.split-panel-inner {
  padding: 12px 16px;
  border-bottom: 1px solid var(--border);
}

.split-line {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  align-items: center;
  margin-bottom: 6px;
}

.split-line select {
  min-width: 150px;
  flex: 1;
}

.split-line input[type="text"] {
  min-width: 140px;
  flex: 1;
}

.split-line input[type="number"] {
  width: 110px;
}

.split-total {
  font-size: var(--fs-sm);
  margin: 8px 0;
}

.split-total.balanced { color: var(--receita); }
.split-total.unbalanced { color: var(--despesa); }

/* Bloco recolhível "+ Novo lançamento". */
.form-toggle-btn {
  width: 100%;
  text-align: left;
  background: transparent;
  color: var(--primary);
  border: 1px dashed var(--border);
}

.form-toggle-btn:hover {
  background: var(--bg);
}

/* `max-height` com um teto fixo (era 3000px) é número mágico: um formulário
   alto (Empréstimos tem 16 campos; em tela estreita a grade vira 1 coluna e
   a altura sobe) chega perto do teto, e passar dele corta o conteúdo em
   silêncio. `grid-template-rows: 0fr -> 1fr` anima a altura sem precisar
   SABER a altura — o próprio grid resolve, então não há teto para estourar.
   `min-height: 0` no filho é obrigatório: sem ele, um item de grid não
   encolhe abaixo do próprio conteúdo, e a transição não tem o que animar. */
.collapsible {
  display: grid;
  grid-template-rows: 0fr;
  transition: grid-template-rows 0.3s ease;
}

.collapsible.open {
  grid-template-rows: 1fr;
}

.collapsible-inner {
  min-height: 0;
  overflow: hidden;
  padding-top: 16px;
}

@media (prefers-reduced-motion: reduce) {
  .collapsible {
    transition: none;
  }
}

/* Cabeçalho da tabela de Lançamentos com ordenação e filtro por coluna. */
th.sortable {
  cursor: pointer;
  user-select: none;
  white-space: nowrap;
}

th.sortable:hover {
  color: var(--text);
}

th.sortable .sort-arrow {
  margin-left: 4px;
  opacity: 0.5;
}

th.sortable.sorted .sort-arrow {
  opacity: 1;
}

#tx-table-controls tr.filter-row th {
  padding: 4px 8px 10px;
}

#tx-table-controls tr.filter-row input,
#tx-table-controls tr.filter-row select {
  font-size: var(--fs-xs);
  padding: 5px 6px;
}

#balance-banner {
  position: sticky;
  top: 56px;
  z-index: 9;
  background: var(--surface);
  border-bottom: 1px solid var(--border);
}

/* A FAIXA DE SALDO, FECHADA — uma linha.

   Eram de sete a quinze chips em fila que rolava para o lado, no topo de
   TODAS as telas: "Saldo hoje", "Previsto receber", "Previsto pagar", "Saldo
   Real" e mais um por conta, todos com o mesmo peso visual e três deles
   dizendo "saldo" com definições diferentes — foi o que a auditoria de 06/09
   apontou. Fechada, a faixa mostra só o número que responde "posso gastar?";
   o resto abre por baixo, com os números agrupados por tipo. */
.balance-strip {
  max-width: 1100px;
  margin: 0 auto;
  padding: 0 20px;
}

.balance-strip-btn {
  display: flex;
  align-items: baseline;
  gap: 10px;
  width: 100%;
  padding: 9px 0;
  background: none;
  border: none;
  border-radius: var(--radius-sm);
  color: inherit;
  font: inherit;
  text-align: left;
  cursor: pointer;
}

.balance-strip-k {
  font-size: var(--fs-sm);
  color: var(--text-muted);
}

.balance-strip-v {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-size: var(--fs-lg);
  font-weight: 700;
}

/* A seta sozinha, em texto solto e cinza, não lia como botão — era fácil
   nunca notar que a faixa abre. Uma bolha com borda é o mesmo desenho do
   `.mes-stepper button` no Painel: ali já significa "clique aqui", e o
   sinal vale em repouso, não só ao passar o mouse — quem usa o dedo no
   celular nunca vê hover nenhum.
   `align-self: center` porque o botão-pai alinha pela BASE do texto (o
   rótulo e o valor, tamanhos diferentes, precisam bater na mesma linha) —
   um círculo não tem base de texto, e nessa regra ele subia ou descia. */
.balance-strip-seta {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  align-self: center;
  width: 20px;
  height: 20px;
  flex-shrink: 0;
  border: 1px solid var(--border);
  border-radius: 50%;
  color: var(--text-muted);
  font-size: 11px;
  transition: transform 0.15s ease, border-color 0.15s ease, background 0.15s ease;
}

/* A bolinha sozinha ainda dependia de quem a reconhecesse como botão — em
   texto, ninguém tem dúvida. Fica plantada ao lado da seta, sempre visível
   (não só no hover), com a mesma cor de link que o resto do sistema usa
   para dizer "isto reage a clique" (o `<summary>` de "Mais números", por
   exemplo). Em telas muito estreitas ela sai — ver a media query — porque
   "Saldo real" mais um valor negativo de seis dígitos já enche a linha, e a
   bolinha com borda continua ali, sozinha, dizendo a mesma coisa. */
.balance-strip-dica {
  flex-shrink: 0;
  margin-left: auto;
  padding-right: 2px;
  font-size: var(--fs-xs);
  color: var(--primary-dark);
  text-decoration: underline;
  white-space: nowrap;
}

@media (max-width: 480px) {
  .balance-strip-dica {
    display: none;
  }
}

.balance-strip-btn:hover .balance-strip-seta,
.balance-strip-btn:focus-visible .balance-strip-seta {
  border-color: var(--primary);
  color: var(--primary-dark);
  background: color-mix(in srgb, var(--primary) 10%, transparent);
}

.balance-strip-btn[aria-expanded="true"] .balance-strip-seta {
  transform: rotate(180deg);
}

.balance-strip-btn:hover .balance-strip-k {
  text-decoration: underline;
}

.balance-strip-detail {
  background: var(--bg);
  border-top: 1px solid var(--border);
}

/* A REFERÊNCIA RÁPIDA. O mesmo bloco em toda tela: no Painel ele fica
   aberto, nas outras ele mora dentro da faixa. Mesmo desenho, mesmo lugar,
   mesmas cores — é isso que faz reconhecer o número sem ler o rótulo. */
.ref-grid {
  max-width: 1100px;
  margin: 0 auto;
  padding: 16px 20px;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
  gap: 12px 32px;
}

.ref-col {
  min-width: 0;
}

.ref-titulo {
  margin: 0 0 6px;
  font-size: var(--fs-xs);
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-muted);
}

/* O título da coluna também carrega a cor do tipo: é a legenda do código de
   cor, sem precisar de legenda. Só "bancos" tem cor própria agora — a coluna
   de cartão saiu daqui (ver o comentário em `renderReferenciaHtml`). */
.ref-titulo.banco { color: var(--banco); }

.ref-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  padding: 3px 0;
}

.ref-k {
  min-width: 0;
  overflow-wrap: break-word;
  font-size: var(--fs-sm);
  color: var(--text-muted);
}

.ref-v {
  flex-shrink: 0;
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-size: var(--fs-sm);
  font-weight: 700;
}

.ref-total {
  margin-top: 4px;
  padding-top: 6px;
  border-top: 1px solid var(--border);
}

.ref-total .ref-k {
  color: var(--text);
  font-weight: 700;
}

/* Era ".ref-nota, .ref-vazio": a nota ("não entra no total dos bancos")
   pertencia só à coluna de cartão, que saiu da referência rápida. */
.ref-vazio {
  margin: 6px 0 0;
  font-size: var(--fs-xs);
  color: var(--text-muted);
  line-height: 1.45;
}

.balance-erro {
  max-width: 1100px;
  margin: 0 auto;
  padding: 10px 20px;
  font-size: var(--fs-sm);
  color: var(--text-muted);
}

/* "Sair" morava na faixa de saldo, empurrado para a direita por um
   margin-left: auto no meio dos chips. Agora é o rodapé da barra lateral —
   junto do resto da navegação, e não no meio dos números. */
.sidebar-footer {
  margin-top: 10px;
  padding: 12px 14px 0;
  border-top: 1px solid var(--border);
}

.logout-btn {
  width: 100%;
  padding: 7px 12px;
  font-size: var(--fs-sm);
  background: transparent;
  color: var(--text-muted);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  cursor: pointer;
  white-space: nowrap;
}

.logout-btn:hover {
  color: var(--despesa);
  border-color: var(--despesa);
}

.filters {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  margin-bottom: 18px;
}

.filters > div {
  min-width: 140px;
}

.empty {
  color: var(--text-muted);
  font-size: var(--fs-base);
  padding: 20px 0;
  text-align: center;
}

.legend {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin-top: 12px;
  font-size: var(--fs-sm);
}

.legend .item {
  display: flex;
  align-items: center;
  gap: 8px;
}

/* O nome da categoria é o único pedaço elástico da linha. Sem `min-width: 0`
   um item de flex se recusa a encolher abaixo do próprio texto, então
   "Alimentação fora de casa" empurrava o valor para fora da legenda em vez
   de quebrar. */
.legend .item > span:not(.swatch):not(.amount) {
  min-width: 0;
  overflow-wrap: break-word;
}

.legend .swatch {
  width: 10px;
  height: 10px;
  border-radius: 3px;
  flex-shrink: 0;
}

.legend .amount {
  margin-left: auto;
  font-variant-numeric: tabular-nums;
  color: var(--text-muted);
}

.table-wrap {
  overflow-x: auto;
}

/* O invólucro da tabela de Lançamentos não pode ser um contêiner de rolagem —
   e é por isso que o cabeçalho fixo nunca funcionou.

   `.table-wrap` pede `overflow-x: auto`, e o navegador promove o `overflow-y`
   junto (não existe "rola só na horizontal" com `auto`). Com isso o invólucro
   vira o scrollport mais próximo, e `position: sticky` no <th> passa a valer em
   relação a ELE, não à janela. Como o invólucro não rola na vertical, o
   cabeçalho ficava com zero de curso: descia junto com a página em vez de
   grudar no topo — e a faixa que ele ocupava cobria a primeira linha, que
   aparecia cortada ao meio e nunca terminava de aparecer.

   `clip` recorta igual, mas NÃO cria contêiner de rolagem: com ele o
   `overflow-y: visible` sobrevive, o sticky volta a valer contra a janela, e a
   tabela continua sem poder vazar para fora do painel. */
#tx-groups .table-wrap {
  overflow-x: clip;
  overflow-y: visible;
}

/* Sem o invólucro para recortar, uma descrição longa e sem espaço (um código de
   transação do banco, por exemplo) poderia vazar para fora do painel. */
.tx-table td {
  overflow-wrap: anywhere;
}

/* Explicação do "A vencer". Fica DENTRO do painel dobrado, acima da tabela —
   quem já sabe o que fazer nunca esbarra nela (o painel abre fechado); quem
   abriu porque não sabia lê antes de mexer. Largura limitada de propósito:
   linha de texto longa demais faz o olho perder a volta, e o painel ocupa a
   tela inteira. */
.explica-avencer {
  max-width: 70ch;
  margin: 0 0 16px;
  padding: 12px 14px;
  border-left: 3px solid var(--primary);
  background: color-mix(in srgb, var(--primary) 7%, transparent);
  border-radius: 0 var(--radius) var(--radius) 0;
}

.explica-avencer p {
  margin: 0 0 8px;
  font-size: var(--fs-sm, 13px);
  line-height: 1.55;
  color: var(--text);
}

.explica-avencer p:last-child {
  margin-bottom: 0;
}

.explica-avencer .explica-nota {
  color: var(--text-muted);
  padding-top: 8px;
  border-top: 1px solid var(--border);
}

.toggle-switch {
  position: relative;
  display: inline-block;
  width: 42px;
  height: 24px;
}

.toggle-switch input {
  opacity: 0;
  width: 0;
  height: 0;
}

.toggle-switch .slider {
  position: absolute;
  cursor: pointer;
  inset: 0;
  background: var(--border);
  border-radius: 999px;
  transition: background 0.15s ease;
}

.toggle-switch .slider::before {
  content: "";
  position: absolute;
  width: 18px;
  height: 18px;
  left: 3px;
  top: 3px;
  background: #fff;
  border-radius: 50%;
  transition: transform 0.15s ease;
}

.toggle-switch input:checked + .slider {
  background: var(--receita);
}

.toggle-switch input:checked + .slider::before {
  transform: translateX(18px);
}

.toggle-switch input:focus-visible + .slider {
  outline: 2px solid var(--primary);
  outline-offset: 2px;
}

.read-only-banner {
  background: color-mix(in srgb, var(--warning-fill) 15%, var(--surface));
  border-bottom: 1px solid var(--border);
  padding: 8px 20px;
  font-size: var(--fs-sm);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
}

.read-only-banner button {
  padding: 5px 12px;
  font-size: var(--fs-xs);
  background: transparent;
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: 6px;
  cursor: pointer;
  white-space: nowrap;
}

.read-only-banner button:hover {
  background: var(--border);
}

/* Cartão da conciliação que agrupa cópias de um extrato importado duas vezes. */
.reconcile-batch {
  border: 1px solid color-mix(in srgb, var(--warning) 45%, var(--border));
  background: color-mix(in srgb, var(--warning) 8%, transparent);
  border-radius: var(--radius);
  padding: 12px 14px;
  margin-top: 8px;
}

.reconcile-batch-title {
  font-weight: 700;
  font-size: var(--fs-base);
  margin-bottom: 4px;
}

.reconcile-batch-text {
  font-size: var(--fs-sm);
  color: var(--text-muted);
  margin: 0 0 10px;
  line-height: 1.5;
}

.reconcile-batch .reconcile-row {
  font-size: var(--fs-sm);
  gap: 10px;
}

.reconcile-batch-actions {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  margin-top: 12px;
}

/* Cabeçalho de cada mês na lista de lançamentos: o saldo em destaque à direita
   e, abaixo, os números do mês em tamanho pequeno para não competir com a tabela. */
.month-head {
  margin-bottom: 14px;
}

.month-head-top {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 14px;
  flex-wrap: wrap;
}

.month-title {
  font-size: var(--fs-lg);
  font-weight: 700;
  color: var(--text);
  margin: 0;
}

.month-balance {
  display: flex;
  align-items: baseline;
  gap: 8px;
}

.month-balance .k {
  font-size: var(--fs-xs);
  color: var(--text-muted);
}

.month-balance .v {
  font-size: var(--fs-lg);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

.month-stats {
  display: flex;
  flex-wrap: wrap;
  gap: 6px 20px;
  margin-top: 8px;
  padding-top: 8px;
  border-top: 1px solid var(--border);
}

.month-stat {
  display: flex;
  align-items: baseline;
  gap: 6px;
  font-size: var(--fs-sm);
}

.month-stat .k {
  color: var(--text-muted);
}

.month-stat .v {
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}

/* Aviso de duplicata no preview da importação. */
.dup-note {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
  font-size: var(--fs-xs);
  color: var(--text-muted);
  margin-top: 4px;
}

.dup-note .dup-warn {
  color: var(--warning);
  flex-basis: 100%;
}

/* Dicas em tópicos abaixo do cadastro de lançamentos. */
ul.dicas {
  margin: -8px 0 24px;
  padding-left: 20px;
  color: var(--text-muted);
  font-size: var(--fs-sm);
  line-height: 1.6;
}

/* O -8px do topo existe para a lista colar no parágrafo acima dela. Dentro de
   um colapsável ele vira defeito: a técnica de fechar é `grid-template-rows:
   0fr`, e margem NEGATIVA no filho faz a linha fechada resolver em 16px em vez
   de zero — a primeira linha do texto ficava aparecendo, cortada ao meio,
   embaixo do botão de um painel supostamente fechado. */
.collapsible ul.dicas {
  margin: 0;
}

ul.dicas li { margin-bottom: 3px; }
ul.dicas strong { color: var(--text); }

/* Resumo do que está filtrado, logo acima da lista. */
.filtro-resumo {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 14px;
  flex-wrap: wrap;
  background: var(--surface);
  border: 1px solid var(--border);
  border-left: 3px solid var(--primary);
  border-radius: var(--radius);
  padding: 10px 16px;
  margin-bottom: 16px;
  font-size: var(--fs-sm);
}

.filtro-resumo-etiquetas {
  display: flex;
  align-items: center;
  gap: 6px;
  flex-wrap: wrap;
}

.filtro-resumo-etiquetas .k { color: var(--text-muted); }

.filtro-resumo .etiqueta {
  background: color-mix(in srgb, var(--primary) 12%, transparent);
  color: var(--primary);
  border: none;
  border-radius: 999px;
  padding: 3px 10px;
  font-size: var(--fs-xs);
  font-weight: 600;
}

.filtro-resumo .etiqueta:hover {
  background: color-mix(in srgb, var(--primary) 22%, transparent);
}

.filtro-resumo-total {
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

/* Resumo do mês na tela de Recorrentes. */
.resumo-linha {
  display: flex;
  flex-wrap: wrap;
  gap: 12px 32px;
}

.resumo-item {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.resumo-item .k { font-size: var(--fs-xs); color: var(--text-muted); }

.resumo-item .v {
  font-size: var(--fs-lg);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

.resumo-item.destaque {
  padding-left: 32px;
  border-left: 1px solid var(--border);
}

.resumo-item.destaque .v { font-size: var(--fs-xl); }

.resumo-nota {
  font-size: var(--fs-sm);
  color: var(--text-muted);
  margin: 14px 0 0;
  line-height: 1.5;
}

.resumo-nota.alerta { color: var(--warning); font-weight: 600; }

/* Rodapé de cada meta: data alvo à esquerda, registrar aporte à direita. */
.meta-rodape {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
  font-size: var(--fs-xs);
  color: var(--text-muted);
}

.meta-acoes {
  display: flex;
  align-items: center;
  gap: 6px;
}

.meta-aporte {
  display: flex;
  align-items: center;
  gap: 5px;
  margin: 0;
  font-size: var(--fs-xs);
}

.meta-aporte input {
  width: 92px;
  padding: 4px 8px;
  font-size: var(--fs-sm);
}

/* Lançamentos já conferidos com o extrato: recuados visualmente, porque são
   histórico fechado — não há o que fazer com eles. */
tr.linha-conferida { opacity: 0.72; }

.conferido-check {
  color: var(--receita);
  font-weight: 700;
}

.conferido-nota {
  font-size: var(--fs-xs);
  color: var(--text-muted);
  font-style: italic;
}

/* Atalho de conferência no cabeçalho do mês. */
.month-conferir {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  margin-top: 10px;
  padding-top: 10px;
  border-top: 1px dashed var(--border);
  font-size: var(--fs-sm);
  color: var(--text-muted);
}

.month-conferir.conferido { color: var(--receita); font-weight: 600; }
.month-conferir a { color: var(--primary); text-decoration: underline; }
.month-conferir input[type="date"] { width: 145px; padding: 4px 8px; font-size: var(--fs-sm); }
.month-conferir input[type="number"] { width: 165px; padding: 4px 8px; font-size: var(--fs-sm); }
.month-conferir .conf-msg { flex-basis: 100%; font-size: var(--fs-xs); }

/* Selo de transferência entre contas próprias, ao lado do selo Extra. */
button.badge.transf {
  background: color-mix(in srgb, var(--primary) 18%, transparent);
  color: var(--primary);
  font-size: var(--fs-xs);
  padding: 2px 8px;
}

button.badge.transf-off {
  background: transparent;
  color: var(--text-muted);
  border-color: var(--border);
  opacity: 0.55;
}

button.badge.transf-off:hover {
  opacity: 1;
  color: var(--primary);
  border-color: var(--primary);
}

/* Painel que sugere pares de transferência. */
.transf-par {
  border-top: 1px solid var(--border);
  padding: 8px 0;
}

.transf-par label {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 0;
  font-size: var(--fs-base);
  color: var(--text);
  cursor: pointer;
}

.transf-par input { width: auto; }
.transf-rota { font-weight: 600; }
.transf-valor { margin-left: auto; font-weight: 700; font-variant-numeric: tabular-nums; }

.transf-detalhe {
  font-size: var(--fs-xs);
  color: var(--text-muted);
  padding-left: 26px;
  line-height: 1.5;
  margin-top: 2px;
}

/* Escolha da conta da outra ponta, aberta na própria linha do lançamento. */
.transf-escolha {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  margin-left: 4px;
  vertical-align: middle;
}

.transf-escolha .k { font-size: var(--fs-xs); color: var(--text-muted); }
.transf-escolha select { width: auto; min-width: 110px; padding: 3px 7px; font-size: var(--fs-xs); }

/* Aviso de transferência marcada sem a outra metade. */
.transf-incompleta {
  background: color-mix(in srgb, var(--warning-fill) 10%, transparent);
  border: 1px solid color-mix(in srgb, var(--warning-fill) 40%, var(--border));
  border-radius: var(--radius);
  padding: 10px 14px;
  margin-bottom: 14px;
  font-size: var(--fs-sm);
  line-height: 1.5;
  color: var(--text-muted);
}

.transf-incompleta strong { color: var(--text); }

/* "Editar todos" / "Salvar todos": um botao so, no cabecalho de cada mes.
   Mesmo desenho do "Editar" de cada linha (pilula com contorno), maior e em
   cor propria -- para ler como acao de lote, nao mais uma linha entre outras.
   Mora no cabecalho de CADA mes (clicar em qualquer um age sobre a lista
   inteira; e o mesmo botao reaparecendo porque o cabecalho tambem reaparece).

   Antes, o Salvar/Cancelar do modo em massa vivia numa barra flutuante presa
   ao pe da tela -- longe do botao que abriu a edicao. Agora a acao termina
   onde comecou, e o estado do modo fica no proprio rotulo. */
.bulk-edit-btn-th {
  background: transparent;
  color: var(--violet);
  border: 1.5px solid var(--violet);
  padding: 4px 10px;
  font-size: var(--fs-xs);
  font-weight: 700;
  text-transform: none;
  letter-spacing: normal;
  white-space: nowrap;
}

.bulk-edit-btn-th:hover {
  background: color-mix(in srgb, var(--violet) 12%, transparent);
}

/* Preenchido no modo "Salvar todos": e a acao que grava, e precisa pesar mais
   que o "Editar todos" vazado de onde ela veio -- senao a troca de rotulo
   passa despercebida. */
.bulk-edit-btn-th.modo-salvar {
  background: var(--violet);
  color: #fff;
}

.bulk-edit-btn-th.modo-salvar:hover {
  background: color-mix(in srgb, var(--violet) 85%, black);
}

.bulk-edit-btn-th:disabled {
  opacity: 0.6;
  cursor: default;
}

.bulk-cancel-btn-th {
  background: transparent;
  color: var(--text-muted);
  border: 1px solid var(--border);
  padding: 4px 10px;
  font-size: var(--fs-xs);
  font-weight: 600;
  text-transform: none;
  letter-spacing: normal;
  white-space: nowrap;
  margin-left: 4px;
}

.bulk-cancel-btn-th:hover {
  background: var(--bg);
  color: var(--text);
}

/* Os mesmos dois botões, para o celular. No computador eles vivem no cabeçalho
   da tabela; lá o cabeçalho é escondido por completo, então precisam de um
   lugar próprio — logo acima da lista, grudados no topo enquanto se rola.

   Sticky, não fixo no rodapé: rolando uma lista de cartões, "Salvar todos"
   precisa estar à mão sem cobrir o cartão que está sendo editado. Grudado no
   alto ele acompanha a rolagem e nunca fica por cima do campo em foco — que é
   justamente o que o teclado do celular já disputa por baixo. */
.tx-bulk-mobile {
  display: none;
  gap: 8px;
  align-items: center;
  position: sticky;
  top: var(--tx-sticky-top, 56px);
  z-index: 8;
  padding: 8px 0;
  margin-bottom: 4px;
  background: var(--bg);
}

.tx-bulk-mobile .bulk-edit-btn-th,
.tx-bulk-mobile .bulk-cancel-btn-th {
  font-size: var(--fs-sm, 13px);
  padding: 9px 14px;
  margin-left: 0;
}

/* ---------------------------------------------------------------------------
   Cabeçalho da tabela de cada mês: título + ordenação + filtro por coluna, na
   mesma linha, estilo planilha. Vive dentro da própria <table> do mês (uma
   <thead> por mês, não uma tabela à parte) — colunas alinham por construção,
   não por sincronizar duas tabelas diferentes. `position: sticky` no <th>
   faz esse cabeçalho grudar no topo enquanto se rola AQUELE mês, e sair de
   cena assim que o cabeçalho do mês seguinte o empurra por trás (mesmo
   mecanismo de "cabeçalho de seção fixo" de qualquer lista longa agrupada).
   --------------------------------------------------------------------------- */

.tx-table {
  table-layout: fixed;
  width: 100%;
}

.tx-table thead th {
  position: sticky;
  top: var(--tx-sticky-top, 56px);
  z-index: 8;
  background: var(--surface);
  padding: 10px 8px;
  /* Sem `nowrap`. Ele fazia o cabeçalho — em especial a última célula, com
     "Editar todos" mais dois ícones — exigir mais largura do que a tabela tinha,
     e a tabela inteira transbordava. Isso ficou escondido enquanto o invólucro
     rolava de lado; sem a rolagem, virava barra de rolagem horizontal na PÁGINA
     em telas menores. Deixando quebrar linha, cada rótulo se acomoda na coluna
     que tem — e os botões, que não podem partir no meio, mantêm o `nowrap`
     deles próprios (.bulk-edit-btn-th / .bulk-cancel-btn-th). */
  border-bottom: 2px solid var(--border);
}

.tx-table th.sortable {
  cursor: pointer;
  user-select: none;
}

.tx-table th.sortable:hover {
  color: var(--text);
}

.tx-table .th-label {
  margin-right: 2px;
}

.tx-table .sort-arrow {
  opacity: 0.45;
  font-size: var(--fs-xs);
}

.tx-table th.sortable.sorted .sort-arrow {
  opacity: 1;
  color: var(--primary);
}

/* Era o menor texto do sistema inteiro (10px) e ainda por cima a meia
   opacidade: o botão que abre o filtro de uma coluna. Justamente o que
   precisa ser encontrado sem saber que existe. A altura do cabeçalho grudado
   é medida em tempo de execução (lancamentos.js), então crescer 2px aqui não
   desalinha nada. */
.th-filter-btn {
  background: transparent;
  border: none;
  padding: 2px 4px;
  font-size: var(--fs-xs);
  cursor: pointer;
  opacity: 0.65;
  vertical-align: middle;
}

.th-filter-btn:hover,
.th-filter-btn.ativo {
  opacity: 1;
}

.th-filter-btn.ativo {
  background: color-mix(in srgb, var(--primary) 18%, transparent);
  border-radius: 4px;
}

.tx-th-actions-header {
  text-align: right;
  white-space: nowrap;
}

.th-icon-btn {
  background: transparent;
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 4px 7px;
  font-size: var(--fs-xs);
  cursor: pointer;
  margin-left: 4px;
}

.th-icon-btn:hover {
  background: var(--bg);
}

/* Painel do filtro: sobe do ícone clicado, não faz parte do fluxo da
   tabela (position:fixed calculado em JS), então nunca desalinha nada. */
.th-filter-popover {
  position: fixed;
  z-index: 30;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: 12px 14px;
  min-width: 210px;
  font-size: var(--fs-sm);
}

.th-filter-popover label {
  display: block;
  font-size: var(--fs-xs);
  color: var(--text-muted);
  margin-bottom: 4px;
}

.th-filter-popover input,
.th-filter-popover select {
  width: 100%;
  margin-bottom: 4px;
}

.th-filter-popover .pop-ou {
  font-size: var(--fs-xs);
  color: var(--text-muted);
  margin: 8px 0 6px;
  text-align: center;
}

.th-filter-popover .pop-limpar {
  width: 100%;
  margin-top: 6px;
}

/* No card do celular o cabeçalho por coluna não faz sentido (cada campo já
   vem rotulado no próprio cartão) — some, e quem filtra/ordena usa a barra
   simples de #tx-mobile-filters em vez de ícone por coluna. */
#tx-mobile-filters {
  display: none;
}

@media (max-width: 640px) {
  /* Previstos entrou no mesmo molde: onze colunas num telefone é rolagem
     lateral sem fim, e cada linha vira cartão pelos mesmos `data-label`. */
  .tx-table thead,
  .tabela-editavel thead {
    display: none;
  }

  #tx-mobile-filters {
    display: block;
  }

  .tx-bulk-mobile {
    display: flex;
  }

  #tx-filters-panel {
    display: none;
  }
}

/* AS TRÊS PORTAS DE LANÇAMENTOS, LADO A LADO.

   Empilhadas, "A vencer", "Novo lançamento" e as orientações ocupavam a
   altura inteira antes da primeira linha de dado — e as orientações eram
   cinco parágrafos SEMPRE abertos, que quem já sabe usar a tela relia todo
   dia. Agora são três botões numa fileira, e o texto só aparece se for pedido.

   Aberto, o painel atravessa a fileira inteira e desce para baixo dos outros
   dois (`order: 1`): a tabela de "Novo lançamento" tem sete colunas e a de
   "A vencer" tem cinco — nenhuma cabe num terço da largura. Assim os botões
   fechados continuam alinhados em cima, no mesmo lugar de sempre, e o
   conteúdo aberto ganha a tela toda. */
.acoes-topo {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  gap: 12px;
  margin-bottom: 18px;
}

/* Flex, e não grid de colunas fixas, porque a fileira tem DOIS ou TRÊS
   painéis: "A vencer" só aparece quando existe conta vencendo. Numa grade de
   auto-fit os dois sobrariam encolhidos à esquerda, com metade da linha vazia;
   aqui eles dividem a largura que houver, sejam dois ou três. */
.acoes-topo > .panel {
  flex: 1 1 210px;
  min-width: 0;
  margin: 0;
}

.acoes-topo > .panel.aberto {
  flex-basis: 100%;
  order: 1;
}

/* O link "Faturas" na linha da conta carregava oito declarações em `style=`
   — borda, raio, cor, tamanho — escritas à mão dentro do JS, fora do alcance
   de qualquer troca de identidade. */
.badge-link {
  display: inline-block;
  padding: 5px 10px;
  border: 1px solid var(--primary);
  border-radius: var(--radius-sm);
  color: var(--primary-dark);
  text-decoration: none;
  font-size: var(--fs-xs);
  font-weight: 600;
  white-space: nowrap;
}

.badge-link:hover {
  background: color-mix(in srgb, var(--primary) 10%, transparent);
}

/* Os três campos de cartão dividem uma célula só na edição de Contas. O
   seletor repete `.tabela-editavel td` porque o piso de 128px lá de cima vale
   para todo campo de texto da tabela, e aqui são três caixas de dois dígitos
   lado a lado — 128px cada estouraria a célula. */
.ed-cartao {
  display: flex;
  gap: 4px;
  flex-wrap: wrap;
}

.tabela-editavel td .ed-cartao input {
  width: 78px;
  min-width: 0;
}

/* Força a coluna a caber os três lado a lado. Sem o piso eles empilham em
   três linhas e a linha da conta fica três vezes mais alta que as vizinhas. */
.tabela-editavel td .ed-cartao {
  min-width: 250px;
}

/* Título do painel com ações do lado. Existe porque o botão de editar
   PRECISA ficar fora da tabela: com onze colunas de campo, a tabela rola de
   lado, e um botão dentro do <th> some da tela junto com a rolagem — some o
   Salvar de quem está no meio da edição. Fora dela, ele fica sempre no mesmo
   lugar, no computador e no celular. */
.panel-head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: 8px 16px;
  margin-bottom: 14px;
}

.panel-head h2 {
  margin: 0;
}

.panel-head-acoes {
  display: flex;
  align-items: center;
  gap: 8px;
}

/* Campo de edição dentro de tabela larga: sem um piso, "Moradia" vira
   "Mora…" e "A definir ao pagar" vira uma caixa de três letras. */
.tabela-editavel td input[type="text"],
.tabela-editavel td input[type="date"] {
  min-width: 128px;
}

/* Um pouco mais que os campos de texto porque o rótulo mais longo em jogo é
   "Cartão de Crédito": em 128px ele sai como "Cartão c…" e as duas opções de
   cartão ficam indistinguíveis fechadas. */
.tabela-editavel td select {
  min-width: 152px;
}

.tabela-editavel td input[type="number"] {
  min-width: 92px;
}

/* Badges de origem na linha do tempo de "A pagar". */
.badge.origem-recorrente { background: color-mix(in srgb, var(--primary) 16%, transparent); color: var(--primary); }
.badge.origem-cartao { background: color-mix(in srgb, var(--violet) 18%, transparent); color: var(--violet); }
.badge.origem-emprestimo { background: color-mix(in srgb, var(--warning) 18%, transparent); color: var(--warning); }
:root[data-theme="dark"] .badge.origem-cartao {
  background: color-mix(in srgb, var(--violet) 12%, transparent);
}
a.badge { text-decoration: none; }
a.badge:hover { filter: brightness(0.92); }

/* ---------------------------------------------------------------------------
   Avisos (toast) e confirmações (dialog) no visual do sistema — js/ui.js.
   --------------------------------------------------------------------------- */

.toast-container {
  position: fixed;
  left: 50%;
  bottom: 22px;
  transform: translateX(-50%);
  z-index: 100;
  display: flex;
  flex-direction: column-reverse;
  gap: 8px;
  width: min(420px, calc(100vw - 32px));
  pointer-events: none;
}

.toast {
  display: flex;
  align-items: center;
  gap: 10px;
  background: var(--surface);
  color: var(--text);
  border: 1px solid var(--border);
  border-left: 4px solid var(--primary);
  border-radius: var(--radius);
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.18);
  padding: 12px 14px;
  font-size: var(--fs-sm);
  line-height: 1.45;
  opacity: 0;
  transform: translateY(8px);
  transition: opacity 0.18s ease, transform 0.18s ease;
  pointer-events: auto;
}

.toast.show {
  opacity: 1;
  transform: translateY(0);
}

.toast-success { border-left-color: var(--receita); }
.toast-error { border-left-color: var(--despesa); }

.toast-close {
  margin-left: auto;
  background: none;
  border: none;
  padding: 0 0 0 6px;
  font-size: var(--fs-xs);
  font-weight: 400;
  color: var(--text-muted);
  flex-shrink: 0;
}

.toast-close:hover { background: none; color: var(--text); }

.dialog-overlay {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.45);
  z-index: 101;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 20px;
}

.dialog-box {
  background: var(--surface);
  border-radius: var(--radius);
  box-shadow: 0 20px 50px rgba(0, 0, 0, 0.3);
  padding: 22px 24px;
  max-width: 420px;
  width: 100%;
}

.dialog-title {
  margin: 0 0 8px;
  font-size: var(--fs-lg);
  color: var(--text);
}

.dialog-message {
  margin: 0;
  font-size: var(--fs-sm);
  line-height: 1.55;
  color: var(--text-muted);
  white-space: pre-line;
}

.dialog-actions {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
  margin-top: 20px;
}

.dialog-confirm-danger {
  background: var(--despesa);
}

.dialog-confirm-danger:hover {
  background: color-mix(in srgb, var(--despesa) 85%, black);
}

@media (prefers-reduced-motion: reduce) {
  .toast { transition: none; }
}

/* ---------------------------------------------------------------------------
   Lista de Lançamentos em tela estreita: cada linha vira um cartão de duas
   colunas (rótulo · valor) em vez de obrigar a rolar a tabela de lado — é a
   tela que mais faz sentido conferir no celular, parado na fila do mercado.
   --------------------------------------------------------------------------- */
@media (max-width: 640px) {
  /* As tabelas de mês não têm mais <thead> próprio (o cabeçalho — títulos,
     ordenação, filtros e "Editar todos" — é o único, fixo, fora delas), então
     não há mais nada aqui pra esconder. */

  table.mobile-cards,
  table.mobile-cards tbody {
    display: block;
    width: 100%;
  }

  table.mobile-cards tr {
    display: block;
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 4px 12px;
    margin-bottom: 10px;
  }

  /* A linha oculta do "pode ser pagamento de..." não deve virar um cartão
     vazio quando fechada — só quando aberta (.open), como no layout normal. */
  table.mobile-cards tr.provisioned-match-row {
    display: none;
    border: none;
    padding: 0;
    margin: -6px 0 10px;
  }

  table.mobile-cards tr.provisioned-match-row.open {
    display: block;
  }

  table.mobile-cards tr.split-row {
    display: none;
    border: none;
    padding: 0;
    margin: -6px 0 10px;
  }

  table.mobile-cards tr.split-row.open {
    display: block;
  }

  table.mobile-cards td {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 14px;
    padding: 7px 0;
    border-bottom: 1px solid var(--border);
    text-align: right;
  }

  table.mobile-cards td:last-child {
    border-bottom: none;
  }

  table.mobile-cards td.tx-actions-cell {
    justify-content: flex-end;
  }

  /* Na edição em massa a célula de ações fica vazia (o Salvar é um só, lá em
     cima). Vazia e visível, ela vira uma faixa em branco no pé de cada cartão. */
  table.mobile-cards td.tx-actions-cell:empty {
    display: none;
  }

  table.mobile-cards td.tx-actions-cell .row-actions {
    justify-content: flex-end;
    flex-wrap: wrap;
  }

  table.mobile-cards td::before {
    content: attr(data-label);
    font-size: var(--fs-xs);
    font-weight: 600;
    color: var(--text-muted);
    text-align: left;
    flex-shrink: 0;
  }

  table.mobile-cards td[data-label="Descrição"] {
    text-align: right;
    flex-wrap: wrap;
  }

  /* Cartão em modo de edição. O `min-width` que os campos carregam no inline
     style existe para a TABELA do computador (impedir que a coluna encolha até
     o campo virar um risco); dentro do cartão ele faz o contrário — 180px de
     largura mínima empurram o campo para fora de uma tela de 360px, e a linha
     "rótulo · campo" quebra. Aqui o campo ocupa o espaço que sobra depois do
     rótulo, e nada mais. */
  table.mobile-cards td input,
  table.mobile-cards td select {
    min-width: 0;
    width: auto;
    flex: 1 1 auto;
    max-width: 62%;
  }

  /* Checkbox e afins não devem esticar junto: um quadrado de seleção com 62%
     da largura do cartão. */
  table.mobile-cards td input[type="checkbox"],
  table.mobile-cards td input[type="radio"] {
    flex: 0 0 auto;
    width: auto;
    max-width: none;
  }
}

/* Primeiros passos: estado vazio do Dashboard para quem ainda não cadastrou
   nenhuma conta. Some sozinho assim que a primeira conta é criada. */
ol.passos-iniciais {
  list-style: none;
  counter-reset: passo;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 4px;
}

ol.passos-iniciais li {
  counter-increment: passo;
  position: relative;
  padding: 14px 0 14px 40px;
  border-top: 1px solid var(--border);
}

ol.passos-iniciais li:first-child { border-top: none; }

ol.passos-iniciais li::before {
  content: counter(passo);
  position: absolute;
  left: 0;
  top: 14px;
  width: 26px;
  height: 26px;
  border-radius: 50%;
  background: color-mix(in srgb, var(--primary) 15%, transparent);
  color: var(--primary);
  font-weight: 700;
  font-size: var(--fs-sm);
  display: flex;
  align-items: center;
  justify-content: center;
}

.passo-titulo {
  font-weight: 600;
  font-size: var(--fs-base);
  margin-bottom: 2px;
}

.passo-texto {
  font-size: var(--fs-sm);
  color: var(--text-muted);
  margin-bottom: 6px;
}

/* Edição de empréstimo na própria linha: como são onze campos, a linha vira
   uma célula única com formulário em grade, no lugar onde a linha estava. */
tr.debt-edit-row > td {
  background: var(--bg);
  padding: 16px;
}

.debt-edit-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(170px, 1fr));
  gap: 12px;
}

.debt-edit-grid label {
  font-size: var(--fs-xs);
  margin-bottom: 3px;
}

.debt-edit-grid input,
.debt-edit-grid select {
  font-size: var(--fs-sm);
  padding: 6px 9px;
}

.debt-edit-actions {
  display: flex;
  gap: 8px;
  margin-top: 14px;
}



/* ---------------------------------------------------------------------------
   Empréstimos: um cartão por contrato, em três faixas.

   Substitui a tabela de 14 colunas que a tela tinha. A tabela não dava conta de
   dois motivos ao mesmo tempo: (1) já rolava na horizontal, e número de dinheiro
   que sai da vista é número que não é lido; (2) os dados do contrato e os
   números CALCULADOS HOJE são coisas de natureza diferente — um é o que o
   usuário digitou e não muda, o outro anda sozinho todo dia — e uma linha de
   tabela mistura os dois sem hierarquia nenhuma.

   As faixas, e o que cada cor quer dizer:
     neutra  → o contrato, o que foi digitado;
     verde   → o cálculo de hoje, com a data carimbada ao lado do título;
     violeta → a simulação de antecipação, sempre aberta e já calculada.

   A de antecipação não tem botão "Simular": ela chega calculada e recalcula
   sozinha quando o usuário mexe em qualquer controle. Era um clique obrigatório
   para ver o número mais útil da tela.
   --------------------------------------------------------------------------- */

.debt-list {
  display: grid;
  gap: 18px;
}

.debt-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  overflow: hidden;
}

.debt-card-head {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  padding: 14px 18px;
  border-bottom: 1px solid var(--border);
}

.debt-card-title {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: var(--fs-lg);
  margin-right: 2px;
}

.debt-card-head .row-actions {
  margin-left: auto;
}

.badge.sistema {
  background: color-mix(in srgb, var(--violet) 16%, transparent);
  color: var(--violet);
}

.badge.andamento {
  background: color-mix(in srgb, var(--primary) 15%, transparent);
  color: var(--primary);
}

.badge.quitado {
  background: color-mix(in srgb, var(--receita) 18%, transparent);
  color: var(--receita);
}

.debt-band {
  padding: 14px 18px;
  border-bottom: 1px solid var(--border);
}

.debt-band:last-child {
  border-bottom: none;
}

/* O cálculo de hoje. A borda à esquerda é o que separa de relance o que o
   sistema calculou do que o usuário digitou. */
.debt-band.calculo {
  background: color-mix(in srgb, var(--primary) 8%, transparent);
  border-left: 3px solid var(--primary);
}

.debt-band.antecipar {
  background: color-mix(in srgb, var(--violet) 9%, transparent);
  border-left: 3px solid var(--violet);
}

.debt-band-title {
  display: flex;
  align-items: baseline;
  gap: 8px;
  flex-wrap: wrap;
  font-size: var(--fs-xs);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-muted);
  margin-bottom: 12px;
}

/* O carimbo da data em que a conta foi feita. Sem ele, não há como o usuário
   saber que o número é de hoje e não de quando ele cadastrou o empréstimo. */
.debt-band-title .stamp {
  text-transform: none;
  letter-spacing: 0;
  font-weight: 500;
}

.debt-figures {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(148px, 1fr));
  gap: 14px 18px;
}

.debt-figure .label {
  font-size: var(--fs-sm);
  color: var(--text-muted);
  margin-bottom: 3px;
}

.debt-figure .value {
  font-weight: 700;
  font-size: var(--fs-lg);
  font-variant-numeric: tabular-nums;
}

/* O número que a pessoa abriu a tela para ver. */
.debt-figure .value.destaque {
  font-size: var(--fs-xl);
  line-height: 1.2;
}

.debt-figure .hint {
  font-size: var(--fs-xs);
  color: var(--text-muted);
  margin-top: 3px;
}

.debt-sim-controls {
  display: flex;
  gap: 12px;
  flex-wrap: wrap;
  align-items: end;
  margin-bottom: 14px;
}

.debt-sim-controls > div {
  min-width: 150px;
}

.debt-sim-controls label {
  font-size: var(--fs-xs);
  margin-bottom: 3px;
}

.debt-sim-controls input,
.debt-sim-controls select {
  font-size: var(--fs-sm);
  padding: 6px 9px;
}

.debt-sim-resumo {
  font-size: var(--fs-sm);
  line-height: 1.55;
  margin-bottom: 12px;
}

.debt-sim-options {
  display: flex;
  gap: 12px;
  flex-wrap: wrap;
}

.debt-sim-option {
  flex: 1;
  min-width: 258px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 12px 14px;
}

.debt-sim-option h4 {
  margin: 0;
  font-family: var(--font-display);
  font-size: var(--fs-base);
  font-weight: 700;
}

.debt-sim-option .explica {
  color: var(--text-muted);
  font-size: var(--fs-xs);
  margin: 4px 0 10px;
}

.debt-sim-total {
  border-top: 1px solid var(--border);
  padding-top: 9px;
  margin-top: 4px;
  grid-column: 1 / -1;
}

.debt-nota {
  color: var(--text-muted);
  font-size: var(--fs-xs);
  margin: 10px 0 0;
}

.debt-aviso {
  background: color-mix(in srgb, var(--warning) 12%, transparent);
  border-left: 3px solid color-mix(in srgb, var(--warning) 55%, var(--border));
  font-size: var(--fs-sm);
  padding: 10px 14px;
}

.debt-erro {
  background: color-mix(in srgb, var(--despesa) 12%, transparent);
  border-left: 3px solid var(--despesa);
  font-size: var(--fs-sm);
  padding: 10px 14px;
}

.debt-edit-box {
  padding: 16px 18px;
}

/* ---------------------------------------------------------------------------
   Busca global (js/nav-index.js + js/nav-search.js).

   z-index 95: acima da sidebar/nav-toggle (30) e de todo conteúdo, mas
   abaixo de toast (100) e do confirmDialog (101) — um diálogo de exclusão
   nunca pode ficar atrás da busca.
   --------------------------------------------------------------------------- */

.palette-trigger {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  margin: 10px 0 4px;
  padding: 8px 10px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--border);
  background: var(--bg);
  color: var(--text-muted);
  font-size: var(--fs-sm);
  font-family: inherit;
  cursor: pointer;
  text-align: left;
  /* Com a base em 15px o rótulo passou a quebrar em duas linhas e o atalho
     junto com ele. O rótulo encolhe, o atalho nunca. */
  min-width: 0;
  white-space: nowrap;
  overflow: hidden;
}

.palette-trigger kbd {
  flex-shrink: 0;
}

.palette-trigger:hover {
  border-color: var(--primary);
  color: var(--text);
}

.palette-trigger kbd {
  margin-left: auto;
  font-family: inherit;
  font-size: var(--fs-xs);
  padding: 1px 6px;
  border: 1px solid var(--border);
  border-radius: 4px;
  background: var(--surface);
  color: var(--text-muted);
}

/* Botão da barra fixa do celular: só o ícone, ao lado do ☰. */
.palette-trigger--mobile {
  width: auto;
  margin: 0 0 0 auto;
  padding: 6px 11px;
  border-color: var(--border);
  font-size: var(--fs-lg);
}

.palette-trigger--sidebar {
  margin-left: 18px;
  margin-right: 18px;
  width: calc(100% - 36px);
}

/* Alternar modo claro/escuro (js/theme-toggle.js). Base sem posição nem
   variante nenhuma — cada lugar onde o botão aparece (sidebar, barra do
   celular, topo do consultor, telas de login) define a própria, sempre com
   o nome do componente na frente (.theme-toggle--X), nunca uma classe solta:
   foi exatamente uma classe solta reaproveitada de outro componente que fez
   o botão de busca cobrir a tela inteira, ver commit "Conserta a tela
   vazia". */
.theme-toggle {
  cursor: pointer;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--bg);
  color: var(--text-muted);
  font-family: inherit;
}

.theme-toggle:hover {
  border-color: var(--primary);
  color: var(--text);
}

/* Linha completa com rótulo visível, logo abaixo do gatilho de busca — a
   mesma razão de sempre: ícone sozinho não diz o que faz. */
.theme-toggle--sidebar {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 0 18px 10px;
  width: calc(100% - 36px);
  padding: 8px 10px;
  font-size: var(--fs-sm);
  text-align: left;
}

@media (max-width: 900px) {
  .theme-toggle--sidebar {
    display: none;   /* a sidebar some atrás da gaveta no celular */
  }
}

.theme-toggle--mobile {
  width: auto;
  margin: 0 0 0 8px;
  padding: 6px 11px;
  font-size: var(--fs-lg);
  display: flex;
  align-items: center;
  justify-content: center;
}

.theme-toggle--topbar {
  margin-right: 10px;
  padding: 6px 11px;
  font-size: var(--fs-lg);
}

/* Telas de login/cadastro/trocar senha não têm sidebar nem barra fixa —
   canto da tela é o único lugar disponível. */
.theme-toggle--auth {
  position: fixed;
  top: 16px;
  right: 16px;
  width: 36px;
  height: 36px;
  padding: 0;
  font-size: var(--fs-lg);
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--surface);
  z-index: 10;
}

.palette-overlay {
  position: fixed;
  inset: 0;
  z-index: 95;
  display: flex;
  align-items: flex-start;
  justify-content: center;
  padding-top: 8vh;
  background: rgba(0, 0, 0, 0.35);
}

.palette {
  width: min(560px, calc(100vw - 32px));
  max-height: min(70dvh, 560px);
  margin-top: 0;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

#palette-input {
  border: none;
  border-bottom: 1px solid var(--border);
  border-radius: 0;
  padding: 16px 18px;
  /* 16px É OBRIGATÓRIO: abaixo disso o Safari do iPhone dá zoom automático
     ao focar o campo, e a página inteira se desloca. */
  font-size: 16px;
  background: var(--surface);
}

/* O contorno do navegador some porque o campo ocupa a largura toda da caixa
   de busca e um retângulo colado nas bordas fica feio — mas some SÓ depois
   de existir substituto. Antes, o único sinal de que o campo estava focado
   era a linha de baixo trocando de cinza para verde: 1px, no rodapé de um
   campo que já é o único da caixa. */
#palette-input:focus {
  outline: none;
  border-bottom-color: var(--primary);
  box-shadow: inset 0 -3px 0 var(--primary);
}

#palette-list {
  list-style: none;
  margin: 0;
  padding: 6px;
  overflow-y: auto;
  overscroll-behavior: contain;
}

.palette-opt {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  padding: 9px 10px;
  border-radius: var(--radius-sm);
  color: var(--text);
}

.palette-opt.active,
.palette-opt:hover {
  background: color-mix(in srgb, var(--primary) 12%, transparent);
}

.palette-opt-icon {
  color: var(--text-muted);
  font-size: var(--fs-sm);
  line-height: 1.6;
}

.palette-opt-text {
  display: flex;
  flex-direction: column;
  min-width: 0;
}

.palette-opt-label {
  font-size: var(--fs-base);
  font-weight: 600;
}

.palette-opt-sub {
  font-size: var(--fs-xs);
  color: var(--text-muted);
}

.palette-opt-hint {
  font-size: var(--fs-xs);
  color: var(--text-muted);
  margin-top: 2px;
  font-style: italic;
}

.palette-hint {
  margin: 0;
  padding: 8px 14px;
  border-top: 1px solid var(--border);
  font-size: var(--fs-xs);
  color: var(--text-muted);
  text-align: center;
}

@media (max-width: 900px) {
  .palette-trigger--sidebar {
    display: none;   /* a sidebar some atrás da gaveta; o gatilho é o da barra fixa */
  }
}

/* ---------------------------------------------------------------------------
   "O que precisa de você hoje" (js/dashboard.js renderHoje). Fica no topo
   do painel do mês, antes até do filtro de conta/mês — é a resposta a
   "abro o sistema e não sei por onde começar". Reaproveita o padrão de
   faixa com borda colorida que já existe em Empréstimos (.debt-band), para
   o app inteiro falar a mesma língua visual de "isto é o que importa agora".
   --------------------------------------------------------------------------- */

/* ————— O PAINEL DO MÊS —————
   Uma coluna, blocos na ordem da urgência. A grade de dez cartões saiu:
   dez números do mesmo tamanho é o mesmo que nenhum. */
body.page-painel main {
  max-width: 820px;
}

/* No Painel, a faixa de saldo fica fechada e MUDA: a referência rápida
   aparece inteira dentro da própria tela, logo abaixo. O aviso de "modo
   consultor, somente leitura" continua visível — ele não é parte da faixa
   que abre. */
body.page-painel .balance-strip,
body.page-painel .balance-strip-detail {
  display: none;
}

/* Com a faixa escondida sobra o <div> vazio dela, e o fundo mais a borda de
   baixo desenhavam um risco de 1px atravessado no alto do Painel. O aviso de
   modo consultor, quando aparece ali dentro, tem fundo próprio. */
body.page-painel #balance-banner {
  background: none;
  border-bottom: none;
}

.painel-topo {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  justify-content: space-between;
  gap: 12px 20px;
  margin-bottom: 28px;
}

.painel-topo h1 {
  margin: 0;
  font-size: var(--fs-md);
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-muted);
}

.painel-contexto {
  margin: 2px 0 0;
  font-size: var(--fs-sm);
  color: var(--text-muted);
}

.painel-controles {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
}

/* Trocar de mês era escolher no calendário do sistema. Uma seta de cada lado
   resolve o caso de longe mais comum — "e no mês passado?" — sem abrir nada. */
.mes-stepper {
  display: flex;
  align-items: center;
  gap: 2px;
}

.mes-stepper button {
  padding: 6px 11px;
  font-size: var(--fs-lg);
  line-height: 1;
  background: transparent;
  color: var(--text-muted);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
}

.mes-stepper button:hover {
  background: var(--surface);
  color: var(--text);
}

.mes-stepper input {
  width: auto;
}

/* A FRASE. O único lugar da tela onde o tipo é grande. */
.frase-do-mes {
  margin: 0;
  font-family: var(--font-display);
  font-size: var(--fs-display);
  font-weight: 700;
  line-height: 1.2;
  letter-spacing: -0.015em;
  text-wrap: balance;
}

/* Monoespacada aqui, NAO. Ela existe para uma coluna de valores alinhar
   virgula com virgula — numa frase nao ha coluna nenhuma, e a 34px o "R$"
   ganhava a largura de tres digitos: o numero saia esticado, com um buraco no
   meio. `tabular-nums` sozinho ja garante que os digitos tenham a mesma
   largura, que e a parte que importa quando o valor muda. */
.frase-do-mes strong {
  font-variant-numeric: tabular-nums;
  font-weight: 700;
  white-space: nowrap;
}

/* De onde sai o número. Cada parcela na cor do seu tipo — é a mesma conta que
   a faixa das outras telas mostra por extenso. */
.frase-conta {
  margin: 10px 0 0;
  font-size: var(--fs-sm);
  color: var(--text-muted);
  line-height: 1.7;
}

/* Sem `color` aqui: cada span já carrega a classe do seu tipo (.banco,
   .despesa, .receita) e este seletor tem especificidade maior — declarar a
   cor aqui apagava as três de uma vez, que é exatamente o contrário do que a
   linha existe para fazer. */
.frase-conta span {
  font-variant-numeric: tabular-nums;
  font-weight: 700;
  white-space: nowrap;
}

/* Blocos sem caixa: no Painel a separação é o espaço e o título, não mais uma
   borda dentro de outra. */
.bloco {
  margin-top: 36px;
}

.bloco > h2 {
  margin: 0 0 12px;
  font-size: var(--fs-xs);
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-muted);
}

.bloco-mais {
  margin: 10px 0 0;
  font-size: var(--fs-sm);
}

.bloco-mais a {
  color: var(--primary-dark);
  text-decoration: underline;
}

/* Os três números do mês. */
.mes-numeros {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: 16px 24px;
}

.mes-num {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}

.mes-num-k {
  font-size: var(--fs-sm);
  color: var(--text-muted);
}

.mes-num-v {
  font-family: var(--font-display);
  font-variant-numeric: tabular-nums;
  font-size: var(--fs-2xl);
  font-weight: 700;
  line-height: 1.15;
}

.mes-num-nota {
  margin-top: 2px;
  font-size: var(--fs-xs);
  color: var(--text-muted);
  text-decoration: underline;
}

.mais-numeros {
  margin-top: 18px;
}

.mes-extras {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: 8px 24px;
  padding-top: 10px;
}

.mes-extra {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  padding: 4px 0;
  border-bottom: 1px solid var(--border);
}

.mes-extra-k {
  min-width: 0;
  font-size: var(--fs-sm);
  color: var(--text-muted);
}

.mes-extra-v {
  flex-shrink: 0;
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-size: var(--fs-sm);
  font-weight: 700;
}

#hoje-lista {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

/* Cada item carrega a cor do que ele é: vermelho o que já venceu ou já está
   negativo, âmbar o que ainda vai vencer. A barra na lateral existe para o
   tipo ser reconhecível antes de ler a frase. */
.hoje-item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 11px 14px;
  border-radius: var(--radius-sm);
  color: var(--text);
  font-size: var(--fs-base);
  font-weight: 600;
}

.hoje-item-warning {
  background: color-mix(in srgb, var(--warning-fill) 16%, transparent);
  box-shadow: inset 3px 0 0 var(--warning);
}

.hoje-item-warning:hover {
  background: color-mix(in srgb, var(--warning-fill) 28%, transparent);
}

.hoje-item-despesa {
  background: color-mix(in srgb, var(--despesa) 10%, transparent);
  box-shadow: inset 3px 0 0 var(--despesa);
}

.hoje-item-despesa:hover {
  background: color-mix(in srgb, var(--despesa) 18%, transparent);
}

/* No Painel a referência não fica dentro da faixa: é um bloco da tela. */
#referencia .ref-grid {
  max-width: none;
  padding: 0;
}

.panel h3 {
  margin: 0 0 12px;
  font-family: var(--font-display);
  font-size: var(--fs-base);
  font-weight: 700;
  color: var(--text);
  text-wrap: balance;
}

/* O gráfico de linha conta uma história ao longo do tempo: espremido em meia
   largura ele vira um risco. */
@media (min-width: 700px) {
  .charts-grid .chart-wide {
    grid-column: 1 / -1;
  }
}

@media (max-width: 640px) {
  .frase-do-mes {
    font-size: var(--fs-2xl);
  }

  .painel-topo {
    margin-bottom: 22px;
  }

  /* No celular o seletor de mês já ocupa a linha quase inteira; deixar a
     conta ao lado dele espremia "Todas as contas" em "Toc". Cada um na sua
     linha. */
  .painel-controles {
    width: 100%;
  }

  .mes-stepper {
    flex: 1 1 100%;
  }

  .mes-stepper input {
    flex: 1;
    min-width: 0;
  }

  .painel-controles select {
    flex: 1 1 100%;
    width: 100%;
  }

  /* ⚠️ 16px NO CELULAR NÃO É ESCOLHA DE DESIGN.
     Abaixo disso o Safari do iPhone dá zoom automático ao focar o campo, e a
     página inteira se desloca — o mesmo motivo pelo qual o campo da busca é
     16px fixo. Como a escala base caiu para 15px, todo campo do sistema
     passaria a disparar isso. */
  input,
  select,
  textarea {
    font-size: 16px;
  }
}

.hoje-item-seta {
  color: var(--text-muted);
  flex-shrink: 0;
}

.hoje-vazio {
  margin: 0;
  color: var(--text-muted);
  font-size: var(--fs-base);
}

/* Os dois cards de resumo que são links (Guardado, Gastos fora do normal)
   ficavam idênticos aos oito que não são clicáveis — a única pista era a
   palavra "ver metas"/"ver lista" dentro do .hint, em letra miúda. Uma seta
   sempre visível (não só no :hover, que não existe em toque) avisa de
   longe que o cartão leva a algum lugar. */
a.card {
  position: relative;
}

a.card::after {
  content: "→";
  position: absolute;
  top: 14px;
  right: 16px;
  color: var(--text-muted);
  font-size: var(--fs-base);
}

/* De onde sai o Saldo real, por extenso. Era um title= — duas frases que
   nem teclado nem toque alcançavam — e virou texto de verdade, dentro do
   que a faixa abre. */
.balance-explicacao {
  max-width: 1100px;
  margin: 0 auto;
  padding: 0 20px 14px;
  font-size: var(--fs-xs);
  color: var(--text-muted);
  line-height: 1.5;
}

/* Texto de instrução longo (Empréstimos tinha ~250 palavras em 4
   parágrafos, sempre visíveis, antes de qualquer dado da tela). Mostra só
   a frase-resumo de cada parágrafo; o resto mora atrás de "Entender
   melhor" — nativo (<details>), então funciona com teclado e leitor de
   tela sem nenhum JS. */
.resumo-rapido {
  margin: 0 0 10px;
  padding-left: 20px;
  font-size: var(--fs-base);
  line-height: 1.6;
}

.resumo-rapido li {
  margin-bottom: 2px;
}

details summary {
  cursor: pointer;
  color: var(--primary);
  font-size: var(--fs-sm);
  font-weight: 600;
  padding: 4px 0;
}

details[open] summary {
  margin-bottom: 4px;
}

/* Detalhe de uma fatura, aberto embaixo da linha dela. Ele é uma tabela dentro
   de outra: sem um fundo próprio, as duas se confundem e não dá para dizer onde
   a fatura termina e o item começa. */
.invoice-detail > td {
  background: var(--bg-subtle, rgba(127, 127, 127, .06));
  padding: 14px 12px;
}
.invoice-detail table {
  font-size: var(--fs-sm);
}

/* ---------------------------------------------------------------------------
   CONEXÃO CONSULTOR / CLIENTE — a carteira do consultor
   (consultor-dashboard.html), o painel que ele abre de um cliente
   (consultor-cliente.html) e a tela onde o cliente autoriza
   (meus-consultores.html).

   Tudo aqui é reaproveitamento: as três telas usam .panel, .table-wrap,
   .badge, .toggle-switch, .bloco, .mes-num, .budget-card e os gráficos das
   telas do cliente. O que sobra são estas peças que não existiam.
   --------------------------------------------------------------------------- */

/* Parágrafo de abertura de tela: o que esta tela é, antes da primeira tabela.
   Existia como style="..." repetido no <p> de cada tela. */
.tela-intro {
  margin: -8px 0 22px;
  max-width: 68ch;
  font-size: var(--fs-sm);
  color: var(--text-muted);
  line-height: 1.55;
}

/* Nome e usuário empilhados numa célula só. O usuário é o que identifica a
   pessoa sem ambiguidade (dois clientes podem se chamar "João"), então ele
   aparece sempre, em segundo plano. */
.consultor-nome {
  font-weight: 600;
}

.consultor-usuario {
  font-size: var(--fs-xs);
  color: var(--text-muted);
}

/* O que a autorização significa, escrito, ao lado do selo. Um selo
   ("Painel + sistema") diz o estado; a frase diz a consequência. */
.consultor-estado {
  margin-top: 4px;
  font-size: var(--fs-xs);
  color: var(--text-muted);
  max-width: 40ch;
}

/* Link que age como botão pequeno numa fileira de ações — mesma medida do
   `button.small`, para "Ver painel" (um link, porque navega) não ficar do
   tamanho errado ao lado de "Abrir o sistema" (um botão, porque troca a
   sessão antes de navegar). */
a.small {
  display: inline-block;
  padding: 5px 10px;
  font-size: var(--fs-xs);
  color: var(--primary);
  border: 1px solid var(--primary);
  border-radius: 6px;
  font-weight: 600;
}

a.small:hover {
  background: color-mix(in srgb, var(--primary) 10%, transparent);
}

/* Os dois níveis explicados antes da tabela de interruptores. Duas colunas de
   toggle com cabeçalho de uma palavra é o desenho de consentimento em que a
   pessoa liga para descobrir o que ligou. */
.niveis-lista {
  margin: 0;
  display: grid;
  gap: 4px 16px;
}

.niveis-lista dt {
  font-weight: 700;
  color: var(--text);
}

.niveis-lista dd {
  margin: 0 0 12px;
  font-size: var(--fs-sm);
  color: var(--text-muted);
  max-width: 72ch;
  line-height: 1.55;
}

.niveis-nota {
  margin: 4px 0 0;
  padding-top: 12px;
  border-top: 1px solid var(--border);
  font-size: var(--fs-sm);
  color: var(--text-muted);
  max-width: 72ch;
  line-height: 1.55;
}

/* O aviso de somente leitura, quando ele é um bloco DENTRO da tela em vez da
   faixa grudada no topo (que é como ele aparece no modo de visualização das
   telas do cliente). */
.read-only-banner.solto {
  margin: 0 0 22px;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 10px 14px;
}

/* Cabeçalho do painel de um cliente: quem é, o que autorizou, e a única ação
   da tela. */
.cliente-topo {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 16px;
  flex-wrap: wrap;
  margin-bottom: 16px;
}

.cliente-topo h1 {
  margin-bottom: 4px;
}

.cliente-sub {
  margin: 0;
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
}

.cliente-desde,
.cliente-nota {
  font-size: var(--fs-xs);
  color: var(--text-muted);
}

.cliente-acoes {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 6px;
}

.cliente-nota {
  margin: 0;
  max-width: 24ch;
  text-align: right;
}

/* Rodapé de uma barra de progresso (orçamento, meta) no painel do consultor.
   A tela de Metas do cliente tem o `.meta-rodape` dela, com o mesmo desenho,
   porque ali o rodapé também abriga os controles de aporte — aqui não há
   controle nenhum, e é essa a diferença entre as duas. */
.budget-foot {
  display: flex;
  justify-content: space-between;
  gap: 8px;
  font-size: var(--fs-xs);
  color: var(--text-muted);
}

/* A nota embaixo de um número do mês é um LINK nas telas do cliente (leva à
   lista filtrada) e texto puro no painel do consultor, que não navega para as
   telas dele. Sublinhado sem link é a promessa de um clique que não existe. */
span.mes-num-nota {
  text-decoration: none;
}

/* ---------------------------------------------------------------------------
   ADMINISTRADOR CENTRAL (admin-usuarios.html). O login dele e a porta unica
   do sistema, /login.html, como o de todo mundo.

   A tela reaproveita quase tudo do resto do sistema — nav.topbar, .panel,
   .table-wrap, .badge, .row-actions, .form-grid, os diálogos de ui.js. O que
   está aqui são as três peças que não existiam: a caixa da senha que aparece
   uma vez, o campo de confirmação de apagar, e a frase que substituiu o botão
   "Criar conta" na tela de login.
   --------------------------------------------------------------------------- */

.rotulo-opcional {
  font-weight: 400;
  color: var(--text-muted);
}

/* A SENHA PROVISÓRIA, dentro do diálogo modal de `mostrarCredencial`
   (js/admin-usuarios.js).

   Ela existe em texto uma vez só: o banco guarda o hash. A moldura própria
   (`.senha-nova-dados`, `.senha-nova-aviso`) já não é a de uma caixa fixa na
   página — isso agora é `.dialog-box`, a mesma do diálogo de apagar conta —
   mas o conteúdo continua precisando se destacar dentro dela: um bloco com
   fundo, borda e a senha em fonte monoespaçada é o que faz a pessoa PARAR e
   copiar, em vez de a informação passar num relance. */
.senha-nova-dados {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 4px 14px;
  margin: 0 0 10px;
  align-items: baseline;
}

.senha-nova-dados dt {
  font-size: var(--fs-sm);
  color: var(--text-muted);
}

.senha-nova-dados dd {
  margin: 0;
  font-weight: 600;
}

/* Monoespaçada e grande: esta senha vai ser LIDA em voz alta ou copiada à mão.
   O alfabeto dela já evita 0/O e 1/l (ver `_senha_provisoria`, no backend); a
   fonte é a outra metade do mesmo cuidado. */
.senha-nova-dados code {
  font-family: var(--font-mono);
  font-size: var(--fs-lg);
  letter-spacing: 0.04em;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 3px 8px;
  user-select: all;
}

.senha-nova-aviso {
  margin: 0 0 12px;
  font-size: var(--fs-sm);
  color: var(--text-muted);
  max-width: 68ch;
  line-height: 1.55;
}

/* O campo que destrava "Apagar para sempre". Fica acima do botão e com folga
   dos dois lados: é o único lugar da interface em que se pede para a pessoa
   DIGITAR antes de uma ação irreversível, e apertado contra o botão ele passa
   a parecer parte dele. */
.apagar-confirma {
  display: block;
  margin: 14px 0 6px;
  font-size: var(--fs-sm);
  color: var(--text);
}

.apagar-confirma code {
  font-family: var(--font-mono);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 5px;
  padding: 1px 6px;
}

.dialog-box #apagar-usuario {
  width: 100%;
  margin-bottom: 4px;
}

/* A frase que ficou no lugar do "Criar conta" nas telas de login. */
.auth-sem-cadastro {
  margin: 14px 0 0;
  padding-top: 14px;
  border-top: 1px solid var(--border);
  line-height: 1.55;
}

/* O "Sair" da BARRA DE TOPO (consultor e administrador).

   `.logout-btn` nasceu no rodapé da barra lateral, onde ele é uma linha
   inteira e o `width: 100%` faz sentido. Na barra de topo esse mesmo 100%
   fazia o botão engolir todo o espaço que sobrava: medido em produção, 897px
   de "Sair" numa barra de 1266px, um retângulo gigante ao lado de dois links
   pequenos. Aqui ele volta ao tamanho do próprio texto e encosta à direita —
   longe dos links de navegação, porque "Sair" colado no que se clica toda hora
   é clique errado esperando acontecer. */
nav.topbar .logout-btn {
  width: auto;
  margin-left: auto;
}

/* Comando dentro de uma frase de ajuda (a recuperação de senha do
   administrador). Monoespaçado porque é para ser copiado literalmente, e não
   parafraseado. */
.auth-sem-cadastro code {
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 5px;
  padding: 1px 5px;
  white-space: nowrap;
}

/* A LINHA DE SAÍDA da tela de trocar senha.

   Ela é a única tela do sistema por onde passam os três tipos de logado, e a
   única cuja falha conhecida é PRENDER a pessoa: quando a troca é obrigatória,
   o middleware desvia todo GET de volta para ela. O "Sair" fica no topo, à
   direita, sempre visível sem rolar — não no rodapé, onde num celular ele
   aparece depois de três campos de senha e de um botão. */
.auth-topo {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 16px;
  min-height: 28px;
}

/* Sem o `margin-bottom` do `.auth-back` solto: quem espaça agora é a linha. */
.auth-topo .auth-back {
  margin-bottom: 0;
}

/* Discreto de propósito. É uma saída de emergência, não a ação principal da
   tela — mas tem de ser reconhecível como botão à primeira vista, porque quem
   procura por ela está travado. */
.auth-sair {
  margin-left: auto;
  padding: 5px 12px;
  font-size: var(--fs-xs);
  background: transparent;
  color: var(--text-muted);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  cursor: pointer;
  white-space: nowrap;
}

.auth-sair:hover {
  color: var(--despesa);
  border-color: var(--despesa);
}

/* A ESCOLHA, na tela de login: o caso raro em que a mesma senha serve para
   mais de uma conta da mesma pessoa (o mesmo e-mail como cliente e como
   consultor). O servidor devolve a lista em vez de escolher por ordem de
   tabela, e a pessoa diz onde quer entrar.

   Botões em coluna e de largura cheia, como o "Entrar" logo acima: aqui eles
   são a pergunta pendente da tela, não uma fileira de opções secundárias. */
.escolha-botoes {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin-top: 10px;
}

.escolha-botoes button {
  width: 100%;
}
