@charset "UTF-8";

/* FC-ASSET-VERSION: 2026-09-02.1 */

/* =====================================================================
   FC — Design system
   =====================================================================

   Registro: produto. A interface serve a tarefa e deve sumir dentro dela.

   DECISAO DE COR — por que violeta.
   A spec ja gastou quatro familias de cor com significado no DADO:
     doc 01 §2.3  vermelho=pendente · amarelo=agendado · verde=confirmado · azul=conciliado
     doc 01 §6.7  verde=positivo · vermelho=negativo
   Se a cor de acao fosse uma delas, um botao "Salvar" verde disputaria leitura
   com um saldo verde. Sobrou a faixa violeta (~295deg) — a unica sem carga
   semantica aqui. Ela e usada SO em acao primaria, selecao e foco.
   De quebra, descarta por principio os dois cliches do setor: o azul-marinho
   de banco e o verde-dinheiro.

   TEMA — claro por padrao.
   Cena: lancamento feito no celular, na fila do caixa, sob luz do dia. Isso
   decide. O escuro entra por prefers-color-scheme para a revisao noturna da
   fatura no desktop.
   ===================================================================== */


/* =====================================================================
   1. Tokens
   ===================================================================== */

:root {
  /* --- A PALETA INTEIRA SAI DE DOIS NUMEROS ---------------------------

     `--matiz` e o angulo de cor; `--croma` e um MULTIPLICADOR aplicado a
     saturacao de cada parada. Nao ha uma lista de cores por paleta: ha uma
     lista so, escrita em funcao destes dois.

     E isso e o que faz uma paleta parecer DESENHADA e nao recolorida: os
     cinzas carregam um traco do acento (croma 0.003 a 0.015), entao mudar o
     matiz move fundo, borda e texto junto com o botao. Trocar so o acento
     deixaria a tela roxa com um botao azul.

     AS CORES SEMANTICAS NAO ENTRAM AQUI. Verde e entrada, vermelho e saida,
     ambar e aviso — elas tem matiz fixo (150, 25, 75) e nao seguem a paleta.
     Um acento verde ao lado dos numeros de receita destruiria o significado
     da cor, que num sistema financeiro e a informacao mais rapida da tela.
     Por isso as paletas oferecidas ficam longe desses angulos.

     O padrao e o violeta original: quem nunca escolheu nada continua vendo
     exatamente o que via. */
  --matiz: 295;
  --croma: 1;
  /* --- Neutros -------------------------------------------------------
     Levemente puxados para o matiz do acento (chroma 0.003–0.012), nunca
     para o "warm" — o bege/creme e o default saturado de IA. */
  --bg:            oklch(0.985 calc(0.003 * var(--croma)) var(--matiz));
  --surface:       oklch(1     0     0);
  --surface-2:     oklch(0.967 calc(0.005 * var(--croma)) var(--matiz));   /* sidebar, toolbars, thead */
  --surface-3:     oklch(0.945 calc(0.007 * var(--croma)) var(--matiz));   /* hover de linha, trilhos */

  --border:        oklch(0.905 calc(0.006 * var(--croma)) var(--matiz));
  --border-strong: oklch(0.845 calc(0.008 * var(--croma)) var(--matiz));
  /* Borda de CAMPO. Escura o bastante para 3:1 contra o fundo do campo, que
     e o minimo da WCAG 1.4.11 para identificar um controle. Nao da para usar
     --border-strong aqui: ele fica em 1,6:1, e como o campo (--surface) e o
     fundo da pagina (--bg) sao quase a mesma cor, a borda e a UNICA coisa que
     diz onde o campo comeca. */
  --border-field:  oklch(0.66 calc(0.012 * var(--croma)) var(--matiz));

  /* Seta do <select>. Vive aqui, e nao dentro da regra, porque o data URI nao
     enxerga var() nem currentColor — a unica forma de ela seguir o tema e
     trocar a imagem inteira.

     NEUTROS, e nao tingidos do matiz: o hexadecimal fica preso aqui, entao um
     cinza arroxeado denunciaria a paleta antiga em toda seta de select,
     qualquer que fosse a paleta escolhida. */
  --seta-select: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%23767676' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
  /* Tique do checkbox. `%23fff` = --on-accent do tema claro. */
  --tique-check: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='11' height='11' viewBox='0 0 24 24' fill='none' stroke='%23ffffff' stroke-width='3.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20 6 9 17l-5-5'/%3E%3C/svg%3E");

  --ink:           oklch(0.22 calc(0.012 * var(--croma)) var(--matiz));   /* corpo   — 14:1 sobre --bg */
  --ink-2:         oklch(0.45 calc(0.010 * var(--croma)) var(--matiz));   /* apoio   — 7.2:1 */
  --ink-3:         oklch(0.525 calc(0.012 * var(--croma)) var(--matiz));   /* rotulos — AA contra TODO fundo, ver abaixo */
  /* O `--ink-3` ERA 0.545, com o comentario "4.8:1, piso AA" — medido so
     contra o `--bg`. O mesmo token aparece sobre o `--surface-3` (hover de
     linha, trilhos) e sobre o `--accent-soft` (a linha selecionada), e ali
     caia a 4,21:1; no escuro, a 3,98:1. Texto de 12-13px da meta e do saldo
     corrente, abaixo do piso.

     Agora 0.525 no claro e 0.66 no escuro, e o pior par fica em 4,5:1 ou mais
     contra os cinco fundos, nas cinco paletas. NAO HA NUMERO ESCRITO AQUI de
     proposito: o `puros.php` refaz a conta a partir deste arquivo a cada
     execucao ("Contraste do texto"), e um numero num comentario e exatamente
     o que envelheceu da ultima vez. */

  /* --- Acento (violeta) --------------------------------------------- */
  --accent:        oklch(0.52 calc(0.19 * var(--croma)) var(--matiz));
  --accent-hover:  oklch(0.46 calc(0.20 * var(--croma)) var(--matiz));
  --accent-active: oklch(0.41 calc(0.19 * var(--croma)) var(--matiz));
  --accent-ink:    oklch(0.42 calc(0.17 * var(--croma)) var(--matiz));    /* acento como TEXTO sobre claro */
  --accent-soft:   oklch(0.955 calc(0.028 * var(--croma)) var(--matiz));
  --accent-border: oklch(0.88 calc(0.055 * var(--croma)) var(--matiz));
  --on-accent:     oklch(0.99 calc(0.005 * var(--croma)) var(--matiz));

  /* --- Semantica do dado --------------------------------------------
     `-dot` e a versao viva do marcador; `-ink` e a versao legivel como
     texto. Sao diferentes de proposito: o que brilha num circulo de 8px
     nao passa em contraste como numero. */
  --pos:       oklch(0.50 0.145 150);   --pos-dot:  oklch(0.58 0.165 150);
  --pos-soft:  oklch(0.955 0.035 150);
  --neg:       oklch(0.52 0.185 25);    --neg-dot:  oklch(0.585 0.205 25);
  --neg-soft:  oklch(0.958 0.030 25);
  --warn:      oklch(0.50 0.120 75);    --warn-dot: oklch(0.735 0.155 82);
  --warn-soft: oklch(0.962 0.045 85);
  --info:      oklch(0.52 0.150 250);   --info-dot: oklch(0.60 0.165 250);
  --info-soft: oklch(0.955 0.032 250);

  /* --- Cores das categorias (identidade) ---------------------------
     A fonte e `Support\PaletaDeCategorias`, e um teste confere que os
     hex daqui sao os de la. Hex fixo, e nao `oklch` com `--matiz`: e
     IDENTIDADE, e a cor de uma categoria nao pode mudar com a paleta do
     sistema. O escuro tem passo proprio (ver os dois blocos do tema). */
  --serie-1: #2a78d6;
  --serie-2: #eb6834;
  --serie-3: #1baf7a;
  --serie-4: #eda100;
  --serie-5: #e87ba4;
  --serie-6: #008300;
  --serie-7: #4a3aa7;
  --serie-8: #e34948;
  --serie-neutra: #8b8b8b;

  /* --- Tipografia ---------------------------------------------------
     Uma familia so. Produto nao precisa de par display/corpo, e sem build
     nao ha webfont — a stack do sistema e a escolha certa, nao a preguicosa. */
  --font: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto,
          "Helvetica Neue", Arial, "Noto Sans", sans-serif;
  --font-mono: ui-monospace, "SF Mono", "Cascadia Mono", "Roboto Mono",
               Consolas, "Liberation Mono", monospace;

  /* Escala fixa em rem, razao ~1.14. Fluida com clamp() nao serve a UI de
     produto: encolher um titulo dentro de um painel estreito piora. */
  --fs-xs:   0.6875rem;   /* 11px — chips, contadores */
  --fs-sm:   0.75rem;     /* 12px — rotulos, metadado */
  --fs-base: 0.8125rem;   /* 13px — dado de tabela */
  --fs-md:   0.875rem;    /* 14px — corpo da UI */
  --fs-lg:   1rem;        /* 16px — titulo de painel */
  --fs-xl:   1.25rem;     /* 20px — titulo de pagina */
  --fs-2xl:  1.5rem;      /* 24px — saldo em destaque */
  --fs-3xl:  1.875rem;    /* 30px — saldo principal */

  /* --- Espaco (base 4px) --------------------------------------------- */
  /* Abaixo da base: existem porque 1px e 2px apareciam como literal em quinze
     lugares, e literal nao participa de nenhum ajuste global de densidade. */
  /* A DENSIDADE INTEIRA SAI DE UM NUMERO, como a paleta sai de dois.

     A escala abaixo e usada em 179 declaracoes de padding, margin e gap — e e
     por isso que um multiplicador aqui alcanca a tela toda, sem uma segunda
     folha de estilo por densidade.

     O QUE NAO ENTRA NA CONTA, de proposito:

     - `--s-px` e `--s-05` sao fio de cabelo (borda, deslocamento de 1px).
       Multiplicar 1px por 0.8 da 0.8px, que cada navegador arredonda do seu
       jeito — e a borda some em alguns.
     - `--fs-*` nao entra. Encolher a letra junto pareceria mais compacto e
       seria outra decisao, tomada de carona: 13px ja e o piso deste sistema.
     - `--h-control`, `--h-control-sm` e `--alvo-toque` nao entram. O piso da
       WCAG 2.5.5 nao e preferencia do usuario. O que a densidade aperta e o
       espaco ENTRE as coisas, que e onde esta a maior parte da altura de uma
       lista de quarenta linhas. */
  --densidade: 1;

  --s-px: 1px;     --s-05: 2px;
  --s-1: calc(0.25rem * var(--densidade));
  --s-2: calc(0.5rem  * var(--densidade));
  --s-3: calc(0.75rem * var(--densidade));
  --s-4: calc(1rem    * var(--densidade));
  --s-5: calc(1.25rem * var(--densidade));
  --s-6: calc(1.5rem  * var(--densidade));
  --s-8: calc(2rem    * var(--densidade));
  --s-10: calc(2.5rem * var(--densidade));
  --s-12: calc(3rem   * var(--densidade));
  --s-16: calc(4rem   * var(--densidade));

  /* Altura dos controles. Botao e campo lado a lado diferiam 2px porque cada
     um chegava na propria altura por padding — visivel em toda barra de filtro. */
  --h-control:    36px;
  --h-control-sm: 28px;

  /* Alvo minimo de toque. 44px e o piso da WCAG 2.5.5; 40 e o que cabe nesta
     densidade sem esticar a linha, com folga vinda do espaco entre os itens. */
  --alvo-toque: 40px;

  --r-sm: 4px;  --r: 7px;  --r-lg: 11px;  --r-full: 999px;

  /* --- Sombra: curta e discreta, produto nao flutua ------------------ */
  --shadow-sm: 0 1px 2px oklch(0.22 calc(0.012 * var(--croma)) var(--matiz) / 0.05);
  --shadow:    0 1px 3px oklch(0.22 calc(0.012 * var(--croma)) var(--matiz) / 0.07),
               0 4px 12px oklch(0.22 calc(0.012 * var(--croma)) var(--matiz) / 0.05);
  --shadow-lg: 0 8px 32px oklch(0.22 calc(0.012 * var(--croma)) var(--matiz) / 0.13),
               0 2px 8px  oklch(0.22 calc(0.012 * var(--croma)) var(--matiz) / 0.07);

  /* --- Movimento ----------------------------------------------------- */
  --dur-fast: 120ms;
  --dur:      180ms;
  --dur-slow: 260ms;
  --ease:     cubic-bezier(0.22, 1, 0.36, 1);   /* ease-out-quart */

  /* --- Camadas: escala nomeada, nunca 9999 --------------------------- */
  --z-base: 1;  --z-sticky: 100;  --z-dropdown: 200;
  --z-backdrop: 300;  --z-modal: 400;  --z-toast: 500;  --z-tooltip: 600;

  --sidebar-w:  216px;
  --sidebar-w-collapsed: 60px;
  --aside-w:    272px;
  --topbar-h:   52px;
  /* Altura da barra inferior + folga. Estava literal como `64px` em quatro
     pontos; o valor foi mantido para nao deslocar nada agora. O toast precisa
     dele para se empilhar acima da barra em vez de cobrir o botao de lancar. */
  --tabbar-h:   64px;

  color-scheme: light dark;
}

/* Escolha explicita do tema claro.
   Sem isto, `color-scheme: light dark` deixa o NAVEGADOR pintar scrollbar,
   popup de <select> e seletor de data em escuro quando o sistema esta escuro —
   dentro de uma pagina que o usuario acabou de mandar ficar clara. */
:root[data-theme="light"] { color-scheme: light; }

/* --- Tema escuro ----------------------------------------------------
   Mesmo matiz, nao um cinza neutro: o escuro precisa parecer o mesmo
   produto com a luz apagada, nao outro app. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --bg:            oklch(0.171 calc(0.008 * var(--croma)) var(--matiz));
    --surface:       oklch(0.212 calc(0.010 * var(--croma)) var(--matiz));
    --surface-2:     oklch(0.192 calc(0.009 * var(--croma)) var(--matiz));
    --surface-3:     oklch(0.252 calc(0.012 * var(--croma)) var(--matiz));

    --border:        oklch(0.298 calc(0.012 * var(--croma)) var(--matiz));
    --border-strong: oklch(0.382 calc(0.015 * var(--croma)) var(--matiz));
    --border-field:  oklch(0.510 calc(0.015 * var(--croma)) var(--matiz));   /* 3:1 sobre --surface escuro */
    /* `%23a3a3a3` = o cinza de rotulo do tema escuro.
       `%231d1d1d` = --on-accent do escuro: o tique fica ESCURO sobre o acento
       claro, que e o unico jeito de ele passar em contraste ali. */
    --seta-select: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%23a3a3a3' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
    --tique-check: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='11' height='11' viewBox='0 0 24 24' fill='none' stroke='%231d1d1d' stroke-width='3.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20 6 9 17l-5-5'/%3E%3C/svg%3E");

    --ink:           oklch(0.955 calc(0.004 * var(--croma)) var(--matiz));
    --ink-2:         oklch(0.762 calc(0.009 * var(--croma)) var(--matiz));
    --ink-3:         oklch(0.66  calc(0.011 * var(--croma)) var(--matiz));

    --accent:        oklch(0.685 calc(0.165 * var(--croma)) var(--matiz));
    --accent-hover:  oklch(0.745 calc(0.155 * var(--croma)) var(--matiz));
    --accent-active: oklch(0.635 calc(0.170 * var(--croma)) var(--matiz));
    --accent-ink:    oklch(0.760 calc(0.140 * var(--croma)) var(--matiz));
    --accent-soft:   oklch(0.285 calc(0.055 * var(--croma)) var(--matiz));
    --accent-border: oklch(0.375 calc(0.085 * var(--croma)) var(--matiz));
    --on-accent:     oklch(0.155 calc(0.020 * var(--croma)) var(--matiz));

    --pos:       oklch(0.745 0.150 150);  --pos-dot:  oklch(0.715 0.170 150);
    --pos-soft:  oklch(0.285 0.045 150);
    --neg:       oklch(0.715 0.165 25);   --neg-dot:  oklch(0.660 0.195 25);
    --neg-soft:  oklch(0.288 0.055 25);
    --warn:      oklch(0.795 0.135 82);   --warn-dot: oklch(0.780 0.150 82);
    --warn-soft: oklch(0.295 0.050 82);
    --info:      oklch(0.735 0.130 250);  --info-dot: oklch(0.690 0.150 250);
    --info-soft: oklch(0.285 0.055 250);
    --serie-1: #3987e5;
    --serie-2: #d95926;
    --serie-3: #199e70;
    --serie-4: #c98500;
    --serie-5: #d55181;
    --serie-6: #008300;
    --serie-7: #9085e9;
    --serie-8: #e66767;
    --serie-neutra: #6b6b6b;

    --shadow-sm: 0 1px 2px oklch(0 0 0 / 0.30);
    --shadow:    0 1px 3px oklch(0 0 0 / 0.36), 0 4px 12px oklch(0 0 0 / 0.28);
    --shadow-lg: 0 8px 32px oklch(0 0 0 / 0.50), 0 2px 8px oklch(0 0 0 / 0.34);
  }
}

/* Alternancia manual: precisa vencer o media query nos dois sentidos. */
:root[data-theme="dark"] {
  --bg: oklch(0.171 calc(0.008 * var(--croma)) var(--matiz));      --surface: oklch(0.212 calc(0.010 * var(--croma)) var(--matiz));
  --surface-2: oklch(0.192 calc(0.009 * var(--croma)) var(--matiz)); --surface-3: oklch(0.252 calc(0.012 * var(--croma)) var(--matiz));
  --border: oklch(0.298 calc(0.012 * var(--croma)) var(--matiz));  --border-strong: oklch(0.382 calc(0.015 * var(--croma)) var(--matiz));
  --border-field: oklch(0.510 calc(0.015 * var(--croma)) var(--matiz));
  --seta-select: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%23a3a3a3' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
  --tique-check: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='11' height='11' viewBox='0 0 24 24' fill='none' stroke='%231d1d1d' stroke-width='3.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20 6 9 17l-5-5'/%3E%3C/svg%3E");
  --ink: oklch(0.955 calc(0.004 * var(--croma)) var(--matiz));     --ink-2: oklch(0.762 calc(0.009 * var(--croma)) var(--matiz));
  --ink-3: oklch(0.66  calc(0.011 * var(--croma)) var(--matiz));
  --accent: oklch(0.685 calc(0.165 * var(--croma)) var(--matiz));  --accent-hover: oklch(0.745 calc(0.155 * var(--croma)) var(--matiz));
  --accent-active: oklch(0.635 calc(0.170 * var(--croma)) var(--matiz)); --accent-ink: oklch(0.760 calc(0.140 * var(--croma)) var(--matiz));
  --accent-soft: oklch(0.285 calc(0.055 * var(--croma)) var(--matiz)); --accent-border: oklch(0.375 calc(0.085 * var(--croma)) var(--matiz));
  --on-accent: oklch(0.155 calc(0.020 * var(--croma)) var(--matiz));
  --pos: oklch(0.745 0.150 150); --pos-dot: oklch(0.715 0.170 150); --pos-soft: oklch(0.285 0.045 150);
  --neg: oklch(0.715 0.165 25);  --neg-dot: oklch(0.660 0.195 25);  --neg-soft: oklch(0.288 0.055 25);
  --warn: oklch(0.795 0.135 82); --warn-dot: oklch(0.780 0.150 82); --warn-soft: oklch(0.295 0.050 82);
  --info: oklch(0.735 0.130 250);--info-dot: oklch(0.690 0.150 250);--info-soft: oklch(0.285 0.055 250);
  --serie-1: #3987e5;
  --serie-2: #d95926;
  --serie-3: #199e70;
  --serie-4: #c98500;
  --serie-5: #d55181;
  --serie-6: #008300;
  --serie-7: #9085e9;
  --serie-8: #e66767;
  --serie-neutra: #6b6b6b;
  --shadow-sm: 0 1px 2px oklch(0 0 0 / 0.30);
  --shadow: 0 1px 3px oklch(0 0 0 / 0.36), 0 4px 12px oklch(0 0 0 / 0.28);
  --shadow-lg: 0 8px 32px oklch(0 0 0 / 0.50), 0 2px 8px oklch(0 0 0 / 0.34);
  color-scheme: dark;
}


/* =====================================================================
   2. Reset
   ===================================================================== */

*, *::before, *::after { box-sizing: border-box; }

* { margin: 0; }

html {
  -webkit-text-size-adjust: 100%;
  tab-size: 4;
}

body {
  min-height: 100dvh;
  background: var(--bg);
  color: var(--ink);
  font: 400 var(--fs-md)/1.5 var(--font);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-rendering: optimizeLegibility;
}

img, svg, video { display: block; max-width: 100%; }
input, button, textarea, select { font: inherit; color: inherit; }
button { background: none; border: 0; }
a { color: var(--accent-ink); text-decoration-thickness: 1px; text-underline-offset: 2px; }
ul, ol { list-style: none; padding: 0; }
table { border-collapse: collapse; width: 100%; }
h1, h2, h3, h4 { font-weight: 600; letter-spacing: -0.011em; text-wrap: balance; }
h1 { font-size: var(--fs-xl); }
h2 { font-size: var(--fs-lg); }
h3 { font-size: var(--fs-md); }
:where(p) { text-wrap: pretty; }

/* Anel de foco unico no sistema inteiro. */
:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: var(--r-sm);
}
:focus:not(:focus-visible) { outline: none; }

/* Acessivel a leitor de tela, invisivel na tela. */
.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip-path: inset(50%); white-space: nowrap;
}

/* =====================================================================
   `hidden` VOLTA A ESCONDER.
   =====================================================================
   O navegador traz `[hidden] { display: none }` na propria folha, mas folha
   de autor vence folha do navegador SEMPRE, independente de especificidade —
   e `.field { display: flex }` e regra de autor. Resultado: todo campo
   condicional do sistema estava visivel, o tempo inteiro.

   O estrago era maior do que parecia. Nao era so poluicao visual: os campos
   ficavam no accessibility tree, entao quem usa leitor de tela ouvia "Para a
   conta" em toda despesa, e "Cartao" em toda transferencia. E as tres
   escritas de `el.hidden = ...` no app.js eram inertes — o formulario nunca
   se adaptava ao tipo escolhido, em lugar nenhum.

   Declarado ANTES dos componentes de propósito, e sem `!important`: assim
   qualquer regra que precise mostrar algo escondido continua podendo, por
   especificidade, sem lutar contra uma bomba. Ver `.selecao[hidden]`, que ja
   fazia isso na mao para um caso so.
   ===================================================================== */
[hidden] { display: none; }

.skip-link {
  position: absolute; top: var(--s-2); left: var(--s-2); z-index: var(--z-tooltip);
  padding: var(--s-2) var(--s-4); border-radius: var(--r);
  background: var(--accent); color: var(--on-accent); font-weight: 550;
  transform: translateY(-200%);
}
.skip-link:focus { transform: none; }


/* =====================================================================
   3. Numeros
   =====================================================================
   Numerais tabulares em TODO valor. Sem isso as colunas de uma tabela
   financeira desalinham a cada digito e o olho perde a coluna. */

.num,
.money,
td.num, th.num,
[data-money] {
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1;
  letter-spacing: -0.006em;
}

.num--pos  { color: var(--pos); }
.num--neg  { color: var(--neg); }
.num--zero { color: var(--ink-3); }
/* Numero que NAO e bom nem ruim — a serie de Lucros, por exemplo. Verde diria
   "isso e bom" sobre um numero que so mede quanto foi. */
.num--info { color: var(--info); }

.num-strong { font-weight: 600; }

.money-lg { font-size: var(--fs-2xl); font-weight: 600; letter-spacing: -0.02em; }
.money-xl { font-size: var(--fs-3xl); font-weight: 600; letter-spacing: -0.024em; }

/* Prefixo R$ menor e discreto: o numero e a informacao, a moeda e contexto. */
.money-lg .cur, .money-xl .cur {
  font-size: 0.6em; font-weight: 500; color: var(--ink-3);
  margin-right: 0.15em; letter-spacing: 0;
}


/* =====================================================================
   4. Estrutura da aplicacao
   =====================================================================
   Mobile-first: uma coluna. As colunas laterais aparecem por breakpoint
   — responsividade estrutural, nao tipografia fluida. */

.app {
  display: grid;
  grid-template-columns: 1fr;
  grid-template-areas: "main";
  min-height: 100dvh;
}

.app__main {
  grid-area: main;
  display: flex;
  flex-direction: column;
  min-width: 0;                       /* deixa o conteudo encolher no grid */
  padding-bottom: calc(64px + env(safe-area-inset-bottom));  /* nav inferior */
}

@media (min-width: 900px) {
  .app {
    grid-template-columns: var(--sidebar-w) minmax(0, 1fr);
    grid-template-areas: "sidebar main";
  }
  .app__main { padding-bottom: 0; }
}


/* --- Navegacao lateral (desktop) ------------------------------------ */

.sidebar {
  grid-area: sidebar;
  display: none;
  flex-direction: column;
  gap: var(--s-1);
  padding: var(--s-4) var(--s-3);
  background: var(--surface-2);
  border-right: 1px solid var(--border);
  position: sticky;
  top: 0;
  height: 100dvh;
  overflow-y: auto;
  overscroll-behavior: contain;
}

@media (min-width: 900px) { .sidebar { display: flex; } }

.sidebar__brand {
  display: flex; align-items: center; gap: var(--s-2);
  padding: var(--s-2) var(--s-3) var(--s-5);
  font-size: var(--fs-lg); font-weight: 650; letter-spacing: -0.02em;
}
.sidebar__mark {
  display: grid; place-items: center;
  width: 26px; height: 26px; border-radius: var(--r-sm);
  background: var(--accent); color: var(--on-accent);
  font-size: var(--fs-sm); font-weight: 700; letter-spacing: 0;
}

.sidebar__group + .sidebar__group { margin-top: var(--s-5); }

.sidebar__label {
  padding: 0 var(--s-3) var(--s-2);
  font-size: var(--fs-xs); font-weight: 600; color: var(--ink-3);
}

.nav-item {
  display: flex; align-items: center; gap: var(--s-3);
  padding: var(--s-2) var(--s-3);
  border-radius: var(--r);
  color: var(--ink-2);
  font-size: var(--fs-md); font-weight: 500;
  text-decoration: none;
  transition: background var(--dur-fast) var(--ease),
              color var(--dur-fast) var(--ease);
}
.nav-item:hover { background: var(--surface-3); color: var(--ink); }
.nav-item:active { background: var(--border); }

.nav-item.is-active {
  background: var(--accent-soft);
  color: var(--accent-ink);
  font-weight: 550;
}
.nav-item.is-active .nav-item__icon { color: var(--accent); }

.nav-item__icon { flex: none; width: 17px; height: 17px; color: var(--ink-3); }
.nav-item__badge {
  margin-left: auto;
  font-size: var(--fs-xs); font-weight: 600;
  color: var(--ink-3); font-variant-numeric: tabular-nums;
}

.sidebar__footer { margin-top: auto; padding-top: var(--s-4); }


/* --- Barra superior -------------------------------------------------- */

.topbar {
  position: sticky; top: 0; z-index: var(--z-sticky);
  display: flex; align-items: center; gap: var(--s-3);
  min-height: var(--topbar-h);
  padding: var(--s-2) var(--s-4);
  background: color-mix(in oklab, var(--bg) 88%, transparent);
  backdrop-filter: blur(8px);
  border-bottom: 1px solid var(--border);
}

.topbar__title {
  font-size: var(--fs-md); font-weight: 600; letter-spacing: -0.012em;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.topbar__spacer { flex: 1 1 auto; }


/* --- Seletor de periodo ---------------------------------------------
   Presente em toda tela de dado e persistido em sessao (doc 01 §6.3). */

/**
 * ALTURA FIXA, e nao "o que der".
 *
 * O seletor media ~38px por soma de paddings, e como a topbar so declara
 * `min-height: var(--topbar-h)` (52px), ela crescia para ~55px. O token passava
 * a mentir, e quem dependia dele — o cabecalho de dia da lista, que gruda em
 * `top: var(--topbar-h)` — ficava 3px curto: uma fresta por onde as linhas
 * passavam por baixo da barra semitransparente.
 *
 * Com `--h-control` a topbar fecha exatamente nos 52px do token, e de quebra o
 * seletor passa a ter a mesma altura dos botoes e campos ao lado.
 */
.period {
  display: flex; align-items: stretch;
  height: var(--h-control);
  border: 1px solid var(--border);
  border-radius: var(--r);
  background: var(--surface);
  overflow: hidden;
}

.period__nav {
  display: grid; place-items: center;
  width: 30px;
  color: var(--ink-2); cursor: pointer;
  transition: background var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease);
}
.period__nav:hover { background: var(--surface-3); color: var(--ink); }
.period__nav:active { background: var(--border); }
.period__nav svg { width: 15px; height: 15px; }

.period__current {
  display: grid; place-items: center;
  min-width: 8.5rem; padding: var(--s-2) var(--s-3);
  border-inline: 1px solid var(--border);
  font-size: var(--fs-base); font-weight: 550;
  font-variant-numeric: tabular-nums;
  white-space: nowrap; cursor: pointer;
  transition: background var(--dur-fast) var(--ease);
}
.period__current:hover { background: var(--surface-3); }

@media (max-width: 560px) { .period__current { min-width: 6.5rem; font-size: var(--fs-sm); } }

/**
 * Grade de meses do seletor de período.
 *
 * Quatro colunas de três: é a forma que um calendário de meses tem, e ela cabe
 * em 375px sem apertar. Ir de agosto de 2026 a janeiro de 2025 passou de
 * dezenove cliques na seta — cada um recarregando a página — para um.
 */
.modal--periodo { max-width: 24rem; }

.grade-meses {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: var(--s-2);
}

.grade-meses__item {
  min-height: var(--alvo-toque);
  padding: var(--s-2);
  border: 1px solid var(--border);
  border-radius: var(--r);
  background: var(--surface);
  color: var(--ink-2);
  font-size: var(--fs-base); font-weight: 550;
  cursor: pointer;
  transition: background var(--dur-fast) var(--ease),
              border-color var(--dur-fast) var(--ease),
              color var(--dur-fast) var(--ease);
}
.grade-meses__item:hover { background: var(--surface-3); color: var(--ink); }

/* O mes ESCOLHIDO: preenchido, como um botao primario. */
.grade-meses__item.is-active {
  background: var(--accent); border-color: var(--accent);
  color: var(--on-accent);
}

/**
 * O mes de HOJE: so um anel, nunca preenchido.
 *
 * Ele e uma referencia ("onde estamos no calendario"), nao uma selecao. Se os
 * dois fossem preenchidos, a grade mostraria duas escolhas e nenhuma resposta.
 */
.grade-meses__item.is-hoje:not(.is-active) {
  border-color: var(--accent);
  color: var(--accent-ink);
}


/* --- Area de conteudo ------------------------------------------------ */

.content {
  flex: 1;
  padding: var(--s-5) var(--s-4) var(--s-10);
}

@media (min-width: 900px) { .content { padding: var(--s-6) var(--s-6) var(--s-12); } }

/* Conteudo + painel de contexto (Lancamentos, Fatura). */
.split {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--s-5);
  align-items: start;
}

/* Abaixo de 1180px a lista vem PRIMEIRO: no celular o lancamento e a tarefa,
   e o painel de saldos empurraria a lista inteira para baixo da dobra. Os
   numeros ficam na faixa .resumo-mobile, acima. */
@media (max-width: 1179px) {
  .split__aside { order: 2; }
  .split > .stack-4 { order: 1; }
}

@media (min-width: 1180px) {
  .split { grid-template-columns: var(--aside-w) minmax(0, 1fr); }
  .split__aside { position: sticky; top: calc(var(--topbar-h) + var(--s-5)); }
}

/* --- Faixa de resumo do mobile --------------------------------------- */

.resumo-mobile {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--s-2);
  margin-bottom: var(--s-4);
  padding: var(--s-3);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
}

.resumo-mobile__item {
  display: flex; flex-direction: column; gap: 2px;
  min-width: 0;
  text-align: center;
}
.resumo-mobile__item + .resumo-mobile__item {
  border-left: 1px solid var(--border);
}
.resumo-mobile__rotulo {
  font-size: var(--fs-xs); color: var(--ink-3);
}
.resumo-mobile .num { font-size: var(--fs-base); }

@media (min-width: 1180px) { .resumo-mobile { display: none; } }

/**
 * Variante de DUAS colunas — usada na fatura.
 *
 * La o resumo tem um valor so, e ele pode ser longo ("R$ 123.456,78"). Em tres
 * colunas de ~100px num aparelho de 375px, o numero seria cortado justamente
 * na tela que existe para mostra-lo.
 */
.resumo-mobile:has(> .resumo-mobile__item:nth-child(2):last-child) {
  grid-template-columns: repeat(2, 1fr);
}

/**
 * Navegacao de mes fora do painel lateral, so no celular.
 *
 * Na fatura ela morava dentro do `.split__aside`, que desce para depois da
 * lista abaixo de 1180px: trocar de mes exigia rolar a fatura inteira ate o fim.
 */
.period--mobile {
  width: 100%;
  margin-bottom: var(--s-4);
  justify-content: space-between;
}
.period--mobile .period__current { flex: 1; }
@media (min-width: 1180px) { .period--mobile { display: none; } }


/* =====================================================================
   5. Painel
   =====================================================================
   Painel e a unidade de agrupamento — nao "card" decorativo. Nunca
   aninhar um dentro do outro. */

.panel {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  overflow: hidden;
}

.panel__head {
  display: flex; align-items: center; gap: var(--s-3);
  padding: var(--s-3) var(--s-4);
  border-bottom: 1px solid var(--border);
}
.panel__title { font-size: var(--fs-md); font-weight: 600; letter-spacing: -0.01em; }
.panel__sub   { font-size: var(--fs-sm); color: var(--ink-3); }
.panel__tools { margin-left: auto; display: flex; align-items: center; gap: var(--s-2); }

.panel__body { padding: var(--s-4); }
.panel__body--flush { padding: 0; }

.panel__foot {
  padding: var(--s-3) var(--s-4);
  border-top: 1px solid var(--border);
  background: var(--surface-2);
}

/* Grade que respira sem breakpoint. */
.panel-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
  gap: var(--s-5);
  align-items: start;
}


/* =====================================================================
   6. Saldos — Confirmado x Projetado
   =====================================================================
   Doc 01 §6.4: os dois SEMPRE lado a lado, nunca atras de um toggle.
   Este e o bloco mais lido do sistema; ganha peso proprio. */

/* Contagem do que sera apagado na remocao de um espaco. Pares
   rotulo/numero, com o numero alinhado a direita para os totais se compararem
   de relance — e o total separado por uma linha, porque ele e a soma e nao
   mais um item. */
.lista-purga { list-style: none; margin: 0; padding: 0; }
.lista-purga li {
  display: flex; justify-content: space-between; gap: var(--s-3);
  padding: var(--s-2) 0;
  border-bottom: 1px solid var(--border);
  font-size: var(--fs-base);
}
.lista-purga li:last-child { border-bottom: 0; }
.lista-purga strong { font-variant-numeric: tabular-nums; }
.lista-purga__total {
  border-top: 1px solid var(--border-strong);
  font-weight: 600;
}

/* --- Convites (tela de administrador) ------------------------------
   Mesma anatomia dos saldos: cabecalho + linhas numa grade unica, para as
   colunas se alinharem sem tabela. Aqui nao ha numero a alinhar a direita,
   entao tudo comeca na esquerda. */
.convites { display: flex; flex-direction: column; }

.convites__head,
.convites__row {
  display: grid;
  grid-template-columns: 1.6fr minmax(6rem, auto) minmax(9rem, auto) minmax(7rem, auto) auto;
  gap: var(--s-3);
  align-items: center;
  padding: var(--s-2) var(--s-4);
}

.convites__head {
  padding-block: var(--s-2);
  border-bottom: 1px solid var(--border);
  font-size: var(--fs-xs); font-weight: 600; color: var(--ink-3);
}

.convites__row { border-bottom: 1px solid var(--border); }
.convites__row:last-of-type { border-bottom: 0; }

.convites__email {
  display: flex; flex-direction: column; min-width: 0;
  font-size: var(--fs-base);
}
.convites__email span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* No celular a grade de quatro colunas nao cabe: vira uma linha por campo,
   com o e-mail em cima e o resto em sequencia. O cabecalho some — sem colunas
   alinhadas ele nao rotula nada. */
@media (max-width: 640px) {
  .convites__head { display: none; }
  .convites__row  { grid-template-columns: 1fr; gap: var(--s-1); }
}

/* O link do convite: 100 caracteres de token que precisam ser selecionaveis
   inteiros. `break-all` porque quebrar so em espaco deixaria a URL estourando
   a largura do painel. */
.codigo-copiavel {
  margin: 0;
  padding: var(--s-2) var(--s-3);
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--r);
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  line-height: 1.5;
  color: var(--ink-2);
  word-break: break-all;
  user-select: all;
}

.balances { display: flex; flex-direction: column; }

.balance-head,
.balance-row {
  display: grid;
  grid-template-columns: 1.4fr minmax(5.5rem, auto) minmax(5.5rem, auto);
  gap: var(--s-3);
  align-items: center;
  padding: var(--s-2) var(--s-4);
}

.balance-head {
  padding-block: var(--s-2);
  border-bottom: 1px solid var(--border);
  font-size: var(--fs-xs); font-weight: 600; color: var(--ink-3);
}
.balance-head span:not(:first-child) { text-align: right; }

.balance-row {
  border-bottom: 1px solid var(--border);
  transition: background var(--dur-fast) var(--ease);
}
.balance-row:hover { background: var(--surface-2); }
.balance-row:last-of-type { border-bottom: 0; }

.balance-row__name {
  display: flex; align-items: center; gap: var(--s-2);
  /* `wrap` para a barra de limite dos cartoes cair na linha de baixo, sob o
     nome, em vez de disputar espaco com ele. Ver `.meter--linha`. */
  flex-wrap: wrap;
  min-width: 0;
  font-size: var(--fs-base);
}
.balance-row__name span {
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* Marcador de conta: circulo cheio na cor da conta, 8px. */
.dot-account {
  flex: none; width: 8px; height: 8px; border-radius: var(--r-full);
  background: var(--c, var(--ink-3));
}

.balance-row__value {
  text-align: right;
  font-size: var(--fs-base);
  font-variant-numeric: tabular-nums;
}
/**
 * O projetado e subordinado ao confirmado — MENOS quando e negativo.
 *
 * `:not(.num--neg)` porque as duas classes tem a mesma especificidade e esta
 * vem depois no arquivo: sem a excecao, ela venceria o `moneyClass()` e o
 * unico numero do painel capaz de gritar "esta conta vai estourar" continuaria
 * saindo no cinza de apoio.
 *
 * Positivo segue quieto de proposito: pintar de verde toda conta saudavel
 * roubaria a atencao da que nao esta.
 */
.balance-row__value--projected:not(.num--neg) { color: var(--ink-2); }
.balance-row__value--projected.num--neg { font-weight: 600; }

.balance-row--total {
  border-top: 1px solid var(--border-strong);
  border-bottom: 0;
  background: var(--surface-2);
  font-weight: 650;
}
.balance-row--total .balance-row__value { font-size: var(--fs-md); }

/* Checkbox por conta que filtra TODOS os blocos (doc 01 §5.1). */
.balance-row__check { flex: none; }


/* --- "A realizar": a diferenca entre confirmado e projetado -----------
   O numero que a tela nao dizia. Fica entre o total e o grafico porque e
   a ponte entre os dois: o quanto ainda vai acontecer, e o desenho de
   quando. */

.a-realizar {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: var(--s-3);
  padding: var(--s-2) var(--s-4);
  border-top: 1px solid var(--border);
  background: var(--surface-2);
}
.a-realizar__rotulo { font-size: var(--fs-sm); color: var(--ink-3); }


/* --- A forma do mes --------------------------------------------------- */

.grafico-mes {
  padding: var(--s-3) var(--s-4) var(--s-3);
  border-top: 1px solid var(--border);
  background: var(--surface-2);
}
.grafico-mes .chart { height: 52px; }

.grafico-mes__rodape {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: var(--s-2);
  margin-top: var(--s-2);
  font-size: var(--fs-sm);
}
.grafico-mes__alerta { color: var(--neg); font-weight: 550; }

/* Marca o dia de hoje: separa o que ja aconteceu do que e projecao. */
.chart__hoje {
  stroke: var(--ink-3);
  stroke-width: 1;
  stroke-dasharray: 2 3;
  opacity: 0.7;
}


/* --- Lancamento vencido -----------------------------------------------
   Fundo levemente tingido em vez de barra lateral colorida: barra na
   borda e ruido visual que compete com a bolinha de status, que ja
   carrega o significado. */

.entry--vencido { background: color-mix(in oklab, var(--neg-soft) 55%, var(--surface)); }
.entry--vencido:hover { background: var(--neg-soft); }
.entry--vencido .entry__desc { font-weight: 550; }


/* --- Recorrencia pausada ---------------------------------------------
   Esmaecida, mas ainda legivel: ela continua explicando lancamentos que
   existem de verdade no extrato. Escondida ou apagada demais, o usuario
   nao acha o que precisa retomar. */

/**
 * ESMAECER POR COR, NAO POR `opacity`.
 *
 * `opacity: 0.55` mistura o texto com o fundo: --ink-2, que sozinho da 7,2:1,
 * caia para ~2,4:1 — abaixo do minimo de 4,5:1 da WCAG 1.4.3. E texto de
 * elemento ATIVO nao tem a isencao que `:disabled` tem.
 *
 * `--ink-3` e o piso AA declarado nos tokens (4,8:1) e continua claramente
 * mais apagado que o --ink das linhas normais, entao o efeito visual que se
 * queria — "esta ai, mas esta pausada" — se mantem. A opacidade fica so no
 * PONTINHO de status, que e decoracao e ja tem o rotulo ao lado.
 */
.entry--inativa .entry__desc,
.entry--inativa .entry__value,
.entry--inativa .entry__running { color: var(--ink-3); }
.entry--inativa .status-dot { opacity: 0.55; }
.entry--inativa:hover .entry__desc,
.entry--inativa:hover .entry__value { color: var(--ink-2); }
.entry--inativa .entry__desc { text-decoration: line-through solid var(--ink-3); }


/* --- Linha de conta na tela de gerenciamento -------------------------
   Quatro colunas em vez de tres: entra a coluna de acoes. As acoes so
   aparecem no hover (ou sempre, no toque, onde nao existe hover). */

.conta-row {
  grid-template-columns: 1.4fr minmax(5.5rem, auto) minmax(5.5rem, auto) auto;
}

/* Conta inativa: esmaece por cor, nao por `opacity` — ver a nota em
   `.entry--inativa`. O saldo continua sendo dinheiro que existe. */
.conta-row--inativa .balance-row__name,
.conta-row--inativa .balance-row__value { color: var(--ink-3); }
.conta-row--inativa .dot-account { opacity: 0.5; }

.conta-row__acoes {
  display: flex; gap: 2px;
  opacity: 0;
  transition: opacity var(--dur-fast) var(--ease);
}
.conta-row:hover .conta-row__acoes,
.conta-row:focus-within .conta-row__acoes { opacity: 1; }

@media (hover: none) {
  .conta-row__acoes { opacity: 1; }
}

@media (max-width: 640px) {
  .balance-head { display: none; }
  .conta-row {
    grid-template-columns: 1fr auto;
    grid-template-areas: "nome  acoes" "conf  proj";
    row-gap: var(--s-2);
  }
  .conta-row .balance-row__name  { grid-area: nome; }
  .conta-row__acoes              { grid-area: acoes; }
  .conta-row .balance-row__value:nth-of-type(1) { grid-area: conf; text-align: left; }
  .conta-row .balance-row__value:nth-of-type(2) { grid-area: proj; }
}


/* --- Formulario: duas colunas que viram uma no estreito -------------- */

.form-grid-2 {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
  gap: var(--s-4);
}


/* --- Seletor de cor: paleta fechada, nao color picker ---------------- */

.cores { display: flex; flex-wrap: wrap; gap: var(--s-2); }

.cor { cursor: pointer; line-height: 0; }
.cor input { position: absolute; opacity: 0; width: 0; height: 0; }

.cor > span:first-of-type {
  display: block;
  width: 24px; height: 24px;
  border-radius: var(--r-full);
  background: var(--c);
  box-shadow: 0 0 0 2px var(--surface), 0 0 0 2px transparent;
  transition: box-shadow var(--dur-fast) var(--ease), transform var(--dur-fast) var(--ease);
}
.cor:hover > span:first-of-type { transform: scale(1.08); }

.cor input:checked + span {
  box-shadow: 0 0 0 2px var(--surface), 0 0 0 4px var(--c);
}
.cor input:focus-visible + span {
  box-shadow: 0 0 0 2px var(--surface), 0 0 0 4px var(--accent);
}


/* --- Arvore de categorias -------------------------------------------
   Dois niveis. A filha e indicada por recuo + fio vertical, nao por
   caixa aninhada: card dentro de card e sempre errado. */

.cat-lista { display: flex; flex-direction: column; }

.cat {
  display: flex; align-items: center; gap: var(--s-2);
  padding: var(--s-2) var(--s-4);
  border-bottom: 1px solid var(--border);
  transition: background var(--dur-fast) var(--ease);
}
.cat:last-child { border-bottom: 0; }
.cat:hover { background: var(--surface-2); }

.cat--filha {
  padding-left: var(--s-10);
  position: relative;
  font-size: var(--fs-base);
}
/* Fio de ligacao com a categoria-pai. */
.cat--filha::before {
  content: "";
  position: absolute;
  left: var(--s-6); top: 0; bottom: 0;
  border-left: 1px solid var(--border);
}

/* Mesma troca de `opacity` por cor da linha pausada do extrato: com 0,55 o
   nome da categoria caia para ~2,2:1, e a contagem ao lado para ~2,4:1. */
.cat--inativa .cat__nome,
.cat--inativa .cat__contagem { color: var(--ink-3); }
.cat--inativa .cat__cor { opacity: 0.5; }

.cat__cor {
  flex: none; width: 9px; height: 9px;
  border-radius: var(--r-full);
  background: var(--c, var(--ink-3));
}

.cat__nome { flex: 1; min-width: 0; font-weight: 500; }
.cat--filha .cat__nome { font-weight: 400; }

.cat__contagem {
  flex: none;
  font-size: var(--fs-sm); color: var(--ink-3);
  font-variant-numeric: tabular-nums;
}

.cat__acoes {
  flex: none;
  display: flex; gap: 1px;
  opacity: 0;
  transition: opacity var(--dur-fast) var(--ease);
}
.cat:hover .cat__acoes,
.cat:focus-within .cat__acoes { opacity: 1; }

@media (hover: none) { .cat__acoes { opacity: 1; } }

/* Reaproveita o chevron horizontal como seta de subir/descer. */
.gira-90 { transform: rotate(90deg); }


/* =====================================================================
   7. Status do lancamento
   =====================================================================
   Quatro estados, quatro cores (doc 01 §2.3). A cor nunca e o unico
   portador do significado: o title/aria carrega o nome. */

.status-dot {
  flex: none;
  display: inline-block;
  width: 9px; height: 9px;
  border-radius: var(--r-full);
  box-shadow: 0 0 0 2.5px var(--halo, transparent);
}
.status-dot--pendente   { background: var(--neg-dot);  --halo: var(--neg-soft); }
.status-dot--agendado   { background: var(--warn-dot); --halo: var(--warn-soft); }
.status-dot--confirmado { background: var(--pos-dot);  --halo: var(--pos-soft); }
.status-dot--conciliado { background: var(--info-dot); --halo: var(--info-soft); }

/* Filtro de status: alternadores (doc 01 §5.2). */
/* =====================================================================
   Bloco "Precisa de voce" — acao antes de analise
   =====================================================================
   A Visao geral abria pelos saldos e seguia para donuts e graficos: sete
   blocos respondendo "como foi", nenhum respondendo "o que eu preciso fazer".

   Ele fica FORA da grade arrastavel, e largura cheia: aviso que se pode
   arrastar para o fim da pagina e aviso que se pode esconder. */
.panel--pendencias { margin-bottom: var(--s-4); }

/* A cor da BORDA ESQUERDA e a gravidade. Fundo colorido inteiro competiria
   com os numeros dos blocos abaixo todo dia — e o estado normal desta tela e
   "nada em aberto", entao o padrao tem de ser discreto. */
.panel--pendencias { border-left: 3px solid var(--border-strong); }
.panel--pendencias.is-critico  { border-left-color: var(--neg); }
.panel--pendencias.is-atencao  { border-left-color: var(--warn); }
.panel--pendencias.is-ok       { border-left-color: var(--pos); }

/* O estado vazio e o estado NORMAL: uma linha, nao um `empty` do tamanho dos
   outros. Na maior parte dos dias nao ha nada a fazer, e um bloco grande
   dizendo isso empurraria os numeros para baixo da dobra todos os dias. */
.pendencias__ok {
  display: flex; align-items: center; gap: var(--s-2);
  margin: 0; color: var(--ink-2); font-size: var(--fs-sm);
}
.pendencias__ok svg { color: var(--pos); flex: none; }

.pendencias { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--s-2); }

.pendencias__item {
  display: flex; align-items: center; gap: var(--s-2);
  padding: var(--s-2) 0;
  border-bottom: 1px solid var(--border);
}
.pendencias__item:last-child { border-bottom: 0; padding-bottom: 0; }

.pendencias__icone { display: grid; place-items: center; flex: none; }
.pendencias__item--critico .pendencias__icone { color: var(--neg); }
.pendencias__item--atencao .pendencias__icone { color: var(--warn); }

/* O texto empurra o botao para a direita, e quebra em telas estreitas em vez
   de esmagar o alvo de acao. */
.pendencias__texto {
  flex: 1 1 auto; min-width: 0;
  font-size: var(--fs-sm); line-height: 1.5;
}
.pendencias__texto .num { white-space: normal; }

/* O botao de cada linha e o que separa "pendencia" de "reclamacao": uma
   pendencia que so informa cobra e nao diz por onde. */
.pendencias__item .btn { flex: none; }

@media (max-width: 560px) {
  /* Abaixo disto o botao ao lado do texto deixa ~8ch para a frase. Ele desce
     para a linha seguinte, alinhado a esquerda com o texto. */
  .pendencias__item { flex-wrap: wrap; }
  .pendencias__texto { flex-basis: 100%; }
  .pendencias__item .btn { margin-left: calc(16px + var(--s-2)); }
}

/* =====================================================================
   Chips de filtro ativo
   =====================================================================
   O filtro de contas mora em SESSAO e e gravado por quatro telas. O unico
   aviso de que estava ligado era um flash no instante do clique: dias depois a
   lista chegava curta e nada na tela explicava. E abaixo de 900px a barra
   lateral — onde vive o seletor de contas — nao existe, entao o filtro ficava
   ligado e inalcancavel.

   Estes chips sao a resposta: o que esta filtrado, e o X para desligar. */
/* `filtro-chip`, e nao `chip`: `.chip` ja existe e esta em uso em cinco telas
   (etiqueta de conta, de tag, de status, de categoria de sistema). Reusar o nome
   fez o CSS novo repintar as etiquetas antigas E a etiqueta antiga valer para o
   chip de filtro. */
.filtros-ativos {
  display: flex; flex-wrap: wrap; align-items: center;
  gap: var(--s-2);
}

/* A contagem e o numero mais importante da barra: ela e que diz se a lista
   curta e um mes tranquilo ou um filtro esquecido. */
.filtros-ativos__contagem {
  font-size: var(--fs-sm); color: var(--ink-2);
  font-variant-numeric: tabular-nums;
  margin-right: var(--s-1);
}

/* Cada chip e um FORM (desligar filtro e escrita em sessao, e escrita nao vai
   por GET), entao o `display` precisa ser explicito — form e block. */
.filtro-chip {
  display: inline-flex; align-items: center; gap: var(--s-1);
  padding: 0 var(--s-1) 0 var(--s-2);
  border: 1px solid var(--border-strong);
  border-radius: 999px;
  background: var(--surface-2);
  font-size: var(--fs-sm);
  line-height: 1;
}

.filtro-chip__rotulo { color: var(--ink); padding: .3rem 0; }

/* 44x44 de alvo com 12px de icone: o quadrado de toque passa da borda visivel
   do chip, e e de proposito. Alvo pequeno em chip pequeno e o erro classico —
   quem erra remove o filtro errado, ou nao remove nenhum. */
.filtro-chip__x {
  display: grid; place-items: center;
  min-width: 2.75rem; min-height: 2.75rem;
  margin: -1.05rem -0.55rem -1.05rem 0;
  border: 0; background: none; cursor: pointer;
  color: var(--ink-3);
  border-radius: 999px;
}
.filtro-chip__x:hover  { color: var(--neg); }
.filtro-chip__x:focus-visible {
  outline: 2px solid var(--accent); outline-offset: 2px;
}

.status-filters { display: flex; flex-wrap: wrap; gap: var(--s-2); }

.status-toggle {
  display: inline-flex; align-items: center; gap: var(--s-2);
  padding: var(--s-1) var(--s-3);
  border: 1px solid var(--border);
  border-radius: var(--r-full);
  background: var(--surface);
  font-size: var(--fs-sm); font-weight: 500; color: var(--ink-2);
  cursor: pointer;
  transition: background var(--dur-fast) var(--ease),
              border-color var(--dur-fast) var(--ease),
              color var(--dur-fast) var(--ease);
}
.status-toggle:hover { background: var(--surface-2); color: var(--ink); }
/**
 * O ESTADO VEM DO CHECKBOX, nao de um `aria-pressed` no <label>.
 *
 * `aria-pressed` nao vale em `<label>` — era ignorado, e o filtro nao anunciava
 * estado nenhum a quem usa leitor de tela. Quem ja carrega o estado e o
 * `<input type="checkbox">` dentro dele.
 *
 * Ganho de brinde: o realce agora acompanha o clique NO MESMO QUADRO. Antes ele
 * dependia do atributo reimpresso pelo servidor, entao o filtro so "acendia"
 * quando a resposta chegava — num 4G lento, o toque parecia nao ter pego.
 */
.status-toggle:has(input:checked) {
  background: var(--surface-3);
  border-color: var(--border-strong);
  color: var(--ink);
}
/* Desmarcado nao e desabilitado: e um controle clicavel, com um contador
   dentro, e por isso precisa dos 4,5:1. Com `opacity: 0.55` ficava em 2,5:1. O
   estado continua obvio pelo par fundo+borda que o marcado acrescenta. */
.status-toggle:not(:has(input:checked)) { color: var(--ink-3); }
.status-toggle:not(:has(input:checked)) .status-toggle__count { color: var(--ink-3); }
.status-toggle__count {
  font-size: var(--fs-xs); color: var(--ink-3);
  font-variant-numeric: tabular-nums;
}


/* =====================================================================
   8. Extrato / listagem de lancamentos
   =====================================================================
   Lista densa, nao cards. Cada linha carrega: status, data, descricao,
   chips de contexto, valor e saldo acumulado. */

.ledger { display: flex; flex-direction: column; }

.ledger__day {
  /* `+ 1px` pela borda inferior da topbar, que nao entra no `min-height`.
     Sem isso sobra uma fresta e as linhas aparecem passando por tras. */
  position: sticky; top: calc(var(--topbar-h) + 1px); z-index: var(--z-base);
  display: flex; align-items: baseline; gap: var(--s-3);
  padding: var(--s-2) var(--s-4);
  background: var(--surface-2);
  border-block: 1px solid var(--border);
  font-size: var(--fs-sm); font-weight: 600; color: var(--ink-2);
}
.ledger__day-total {
  margin-left: auto;
  font-size: var(--fs-sm); color: var(--ink-3);
  font-variant-numeric: tabular-nums;
}

.entry {
  display: grid;
  /**
   * A COLUNA DE VALOR TEM LARGURA MINIMA, e nao `auto`.
   *
   * Com `auto` cada linha dimensiona a propria coluna pelo proprio conteudo:
   * "R$ 45,00" e "R$ 1.240,00" terminam no mesmo ponto a direita, mas as
   * VIRGULAS caem em colunas diferentes. O `tabular-nums` alinha os digitos
   * dentro de um numero, nao os numeros entre si — o que faltava era a coluna
   * ter a mesma largura em todas as linhas.
   *
   * 9ch cobre "999.999,99" na fonte tabular; acima disso a coluna cresce.
   */
  grid-template-columns: auto 1fr minmax(9ch, auto);
  grid-template-areas:
    "mark  desc   value"
    "mark  meta   running";
  column-gap: var(--s-3);
  row-gap: var(--s-05);
  padding: var(--s-3) var(--s-4);
  border-bottom: 1px solid var(--border);
  cursor: pointer;
  /* Sem isto, cada toque na linha pisca o retangulo cinza do navegador — que
     nao combina com o realce proprio de selecao e parece defeito. */
  -webkit-tap-highlight-color: transparent;
  transition: background var(--dur-fast) var(--ease);
}
.entry:hover { background: var(--surface-2); }
.entry:last-child { border-bottom: 0; }
/* Selecionada para conferencia. A borda a esquerda existe porque so o fundo
   nao sobrevive a uma lista longa: correndo o olho por 41 linhas, uma variacao
   de fundo passa batida e a barra la embaixo diria um numero que o usuario nao
   consegue rastrear ate as linhas que o formaram. */
.entry.is-selected {
  background: var(--accent-soft);
  box-shadow: inset 3px 0 0 var(--accent);
}

/* So linha com valor e clicavel. Saldo anterior e subtotal do dia nao tem
   `data-centavos`, entao nem o cursor promete o que nao acontece. */
.entry:not([data-centavos]) { cursor: default; }

/* Previsao de fatura: esta na lista para o saldo dizer a verdade, mas nao e um
   lancamento. O tracejado a esquerda diz isso sem precisar de legenda. */
.entry--previsao {
  box-shadow: inset 3px 0 0 var(--info);
  background: color-mix(in srgb, var(--info-soft) 45%, transparent);
}
.entry--previsao:hover { background: color-mix(in srgb, var(--info-soft) 65%, transparent); }
.entry--previsao .entry__desc { font-style: italic; color: var(--ink-2); }

/* Confirmacao: estreita de proposito. O texto e uma pergunta curta, e um
   dialogo largo faria os dois botoes ficarem longe um do outro. */
.modal--confirmar { max-width: 26rem; }
.confirmar__texto {
  margin: 0;
  font-size: var(--fs-md);
  color: var(--ink-2);
  line-height: 1.55;
}

.entry__mark { grid-area: mark; display: flex; align-items: center; padding-top: 3px; }

.entry__desc {
  grid-area: desc;
  min-width: 0;
  font-size: var(--fs-md); font-weight: 500;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

.entry__meta {
  grid-area: meta;
  display: flex; align-items: center; flex-wrap: wrap; gap: var(--s-1) var(--s-2);
  min-width: 0;
}

.entry__value {
  grid-area: value;
  text-align: right; white-space: nowrap;
  font-size: var(--fs-md); font-weight: 600;
  font-variant-numeric: tabular-nums;
}

/* Saldo acumulado: presente, mas subordinado ao valor da linha. */
.entry__running {
  grid-area: running;
  text-align: right; white-space: nowrap;
  font-size: var(--fs-sm); color: var(--ink-3);
  font-variant-numeric: tabular-nums;
}

/**
 * NEGATIVO SALTA; POSITIVO CONTINUA QUIETO.
 *
 * A coluna existe para responder "em que dia eu estouro", e respondia com a
 * mesma cor nos dois lados. Agora ela recebe `moneyClass()`, mas o verde e
 * devolvido a --ink-3: pintar de verde quarenta linhas saudaveis roubaria a
 * atencao da unica que importa, e a hierarquia com o valor da linha (que e o
 * numero principal) se perderia.
 */
.entry__running.num--pos { color: var(--ink-3); }
.entry__running.num--neg { color: var(--neg); font-weight: 600; }

/**
 * A TRAVESSIA PARA O NEGATIVO.
 *
 * Marcada so na PRIMEIRA linha em que o saldo cruza — depois dela o mes
 * costuma seguir negativo, e repetir a marca em trinta linhas esconderia
 * justamente o dia que se procura.
 */
.entry--rompimento { border-top: 2px solid var(--neg); }
.entry--rompimento .entry__running::after {
  content: "estoura aqui";
  display: block;
  font-size: var(--fs-xs); font-weight: 500; color: var(--neg);
  letter-spacing: .01em;
}

/* Primeira linha do periodo (doc 01 §5.2). */
.entry--opening {
  background: var(--surface-2);
  cursor: default;
  font-style: normal;
}
.entry--opening:hover { background: var(--surface-2); }
.entry--opening .entry__desc { color: var(--ink-2); font-weight: 500; }

/* Subtotal do dia na fatura: separador, nao lancamento. Fica discreto de
   proposito — se competir em peso com os itens, a lista vira uma escada. */
.entry--dia {
  cursor: default;
  padding-top: var(--s-4);
  border-top: 1px solid var(--border);
}
.entry--dia:hover { background: transparent; }
.entry--dia .entry__desc,
.entry--dia .entry__value {
  color: var(--ink-3);
  font-size: var(--fs-sm);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .04em;
}
.entry--dia .entry__running { font-size: var(--fs-xs); }


/* Bola de status clicavel: confirmar e a acao mais repetida do sistema,
   entao ela mora no proprio marcador, sem abrir menu. */
.btn--marca {
  display: grid; place-items: center;
  width: 20px; height: 20px; margin: -4px;
  border-radius: var(--r-full);
  cursor: pointer;
  transition: background var(--dur-fast) var(--ease);
}
.btn--marca:hover { background: var(--surface-3); }
.btn--marca:active { background: var(--border); }

/* Acoes da linha: aparecem no hover, sempre visiveis no toque. */
.entry__acoes {
  display: inline-flex; gap: 1px;
  margin-left: auto;
  opacity: 0;
  transition: opacity var(--dur-fast) var(--ease);
}
.entry:hover .entry__acoes,
.entry:focus-within .entry__acoes { opacity: 1; }

@media (hover: none) { .entry__acoes { opacity: 1; } }

/* =====================================================================
   Alvos de toque
   =====================================================================
   No dedo, `.btn--marca` tinha 20x20 e os botoes de acao 27x27 colados a 1px
   de distancia — bem abaixo dos 44px da WCAG 2.5.5, e na acao MAIS REPETIDA do
   sistema (confirmar um lancamento).

   Cresce por `padding`, NUNCA por margem negativa. O `.btn--marca` ja tem
   `margin: -4px` para nao esticar a linha; ampliar por ali faria a area do
   botao invadir a linha vizinha, e um toque destinado a SELECIONAR viraria
   CONFIRMAR — que mexe em dinheiro. A linha fica um pouco mais alta no
   celular, e esse e o preco certo a pagar.
   ===================================================================== */
@media (hover: none) {
  /* O `-4px` que ja existia continua o mesmo: ele so encaixa o botao na coluna,
     e nao aumenta area. O que cresce e a caixa — de 20 para 40 —, e ela cabe
     dentro do `padding: var(--s-3)` da propria linha, sem alcancar a vizinha. */
  .btn--marca {
    width: var(--alvo-toque); height: var(--alvo-toque);
    margin: -4px;
  }

  /* 1px entre dois alvos e um convite ao erro. */
  .entry__acoes { gap: var(--s-2); }
  .entry__acoes .btn--icon.btn--sm {
    width: var(--alvo-toque); min-height: var(--alvo-toque);
  }
}

/* Bloco "Mais opcoes" do lancamento rapido: o caminho curto fica curto. */
.avancado > summary {
  cursor: pointer;
  padding: var(--s-2) 0;
  font-size: var(--fs-base); font-weight: 550; color: var(--ink-2);
  list-style: none;
}
.avancado > summary::-webkit-details-marker { display: none; }
.avancado > summary::before { content: "+ "; color: var(--ink-3); }
.avancado[open] > summary::before { content: "\2212 "; }
.avancado > summary:hover { color: var(--ink); }


/* --- Chips de contexto ----------------------------------------------- */

.chip {
  display: inline-flex; align-items: center; gap: var(--s-1);
  max-width: 12rem;
  padding: 1px var(--s-2);
  border-radius: var(--r-sm);
  background: var(--surface-3);
  font-size: var(--fs-xs); font-weight: 500; color: var(--ink-2);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.chip__dot { flex: none; width: 6px; height: 6px; border-radius: var(--r-full); background: var(--c, var(--ink-3)); }

.chip--accent { background: var(--accent-soft); color: var(--accent-ink); }
.chip--pos { background: var(--pos-soft); color: var(--pos); }
.chip--neg { background: var(--neg-soft); color: var(--neg); }
.chip--warn { background: var(--warn-soft); color: var(--warn); }
.chip--info { background: var(--info-soft); color: var(--info); }

/* Sentido da transferencia: sai desta conta (→) ou entra nela (←). */
.seta-transfer {
  flex: none;
  color: var(--ink-3);
  font-size: var(--fs-sm);
  line-height: 1;
}

/* Marcadores de serie: recorrente, parcelado, anexo. */
.entry__flags { display: inline-flex; align-items: center; gap: var(--s-1); color: var(--ink-3); }
.entry__flags svg { width: 12px; height: 12px; }


/* --- Cartao ---------------------------------------------------------- */

.cartao .panel__head { align-items: flex-start; }
.cartao .panel__head .dot-account { margin-top: 6px; }
.cartao--inativo { opacity: 0.6; }


/* --- Anexos ---------------------------------------------------------- */

.anexos { display: flex; flex-direction: column; gap: 1px; }

.anexo {
  display: flex; align-items: center; gap: var(--s-2);
  padding: var(--s-2) var(--s-3);
  background: var(--surface-2);
  border-radius: var(--r-sm);
  font-size: var(--fs-base);
}
.anexo__icone   { flex: none; color: var(--ink-3); display: flex; }
.anexo__nome    { flex: 1; min-width: 0; color: var(--accent-ink); }
.anexo__tamanho {
  flex: none; font-size: var(--fs-sm); color: var(--ink-3);
  font-variant-numeric: tabular-nums;
}

.anexo-envio {
  display: flex; align-items: center; gap: var(--s-3); flex-wrap: wrap;
}
/* <label for> disfarçado de botao: o input de arquivo nativo nao aceita
   estilo, entao ele fica em .sr-only e o label vira o alvo do clique. */
.anexo-envio label { cursor: pointer; }


/* =====================================================================
   9. Botoes
   =====================================================================
   Um vocabulario so, em todas as telas. Todos os estados definidos:
   default, hover, active, focus, disabled, loading. */

.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--s-2);
  min-height: 34px;
  padding: 0 var(--s-4);
  border: 1px solid transparent;
  border-radius: var(--r);
  font-size: var(--fs-md); font-weight: 550; line-height: 1;
  white-space: nowrap; cursor: pointer;
  transition: background var(--dur-fast) var(--ease),
              border-color var(--dur-fast) var(--ease),
              color var(--dur-fast) var(--ease),
              opacity var(--dur-fast) var(--ease);
}
.btn svg { width: 15px; height: 15px; flex: none; }

.btn--primary {
  background: var(--accent); color: var(--on-accent);
}
.btn--primary:hover  { background: var(--accent-hover); }
.btn--primary:active { background: var(--accent-active); }

.btn--secondary {
  background: var(--surface); border-color: var(--border-strong); color: var(--ink);
}
.btn--secondary:hover  { background: var(--surface-2); }
.btn--secondary:active { background: var(--surface-3); }

.btn--ghost { color: var(--ink-2); }
.btn--ghost:hover  { background: var(--surface-3); color: var(--ink); }
.btn--ghost:active { background: var(--border); }

.btn--danger { background: var(--neg); color: var(--on-accent); }
.btn--danger:hover { filter: brightness(0.92); }

.btn--sm { min-height: 27px; padding: 0 var(--s-3); font-size: var(--fs-base); }
.btn--icon { padding: 0; width: 34px; }
.btn--icon.btn--sm { width: 27px; }
.btn--block { width: 100%; }

.btn:disabled, .btn[aria-disabled="true"] {
  opacity: 0.45; cursor: not-allowed; pointer-events: none;
}

/* Estado de carregamento: o htmx marca o elemento durante o request. */
.btn.htmx-request { pointer-events: none; opacity: 0.7; }
.btn.htmx-request::after {
  content: ""; width: 13px; height: 13px;
  border: 2px solid currentColor; border-right-color: transparent;
  border-radius: var(--r-full);
  animation: spin 620ms linear infinite;
}

@keyframes spin { to { transform: rotate(360deg); } }


/* --- Botao flutuante: lancar sempre a um toque (doc 01 §5.2) --------- */

.fab {
  position: fixed;
  right: var(--s-4);
  bottom: calc(var(--tabbar-h) + var(--s-4) + env(safe-area-inset-bottom));
  z-index: var(--z-sticky);
  display: grid; place-items: center;
  width: 50px; height: 50px;
  border-radius: var(--r-full);
  background: var(--accent); color: var(--on-accent);
  box-shadow: var(--shadow-lg);
  cursor: pointer;
  transition: transform var(--dur) var(--ease), background var(--dur-fast) var(--ease);
}
/* Barra da selecao ------------------------------------------------------
   Canto inferior ESQUERDO de proposito: o toast fica no centro e o botao de
   lancar no direito. Uma barra que so aparece durante a conferencia nao pode
   disputar espaco com os dois controles que existem o tempo todo. */
.selecao {
  position: fixed;
  left: var(--s-4);
  bottom: calc(var(--tabbar-h) + var(--s-4) + env(safe-area-inset-bottom));
  z-index: var(--z-sticky);
  display: flex; align-items: center; gap: var(--s-3);
  padding: var(--s-2) var(--s-2) var(--s-2) var(--s-3);
  border-radius: var(--r-full);
  background: var(--surface-3);
  border: 1px solid var(--border-strong);
  box-shadow: var(--shadow-lg);
  font-size: var(--fs-sm);
}
.selecao[hidden] { display: none; }
.selecao__contagem { color: var(--ink-3); }
.selecao__total {
  font-weight: 700; font-variant-numeric: tabular-nums;
}

@media (min-width: 900px) {
  .selecao { left: calc(var(--sidebar-w) + var(--s-6)); bottom: var(--s-6); }
}

.fab:hover  { background: var(--accent-hover); transform: translateY(-1px); }
.fab:active { transform: translateY(0) scale(0.96); }
.fab svg { width: 22px; height: 22px; }

@media (min-width: 900px) { .fab { bottom: var(--s-6); right: var(--s-6); } }


/* =====================================================================
   10. Formularios
   ===================================================================== */

.field { display: flex; flex-direction: column; gap: var(--s-2); }

.field__label {
  font-size: var(--fs-sm); font-weight: 550; color: var(--ink-2);
}
.field__hint { font-size: var(--fs-sm); color: var(--ink-3); }

.field__error {
  display: flex; align-items: center; gap: var(--s-1);
  font-size: var(--fs-sm); color: var(--neg);
}

.input, .select, .textarea {
  width: 100%;
  min-height: var(--h-control);
  padding: var(--s-2) var(--s-3);
  background: var(--surface);
  /**
   * `--border-field`, e nao `--border-strong`.
   *
   * O campo (--surface) e o fundo da pagina (--bg) sao quase a mesma cor de
   * proposito, entao a BORDA e a unica coisa que diz onde o campo comeca. Com
   * `--border-strong` ela ficava em 1,6:1, muito abaixo dos 3:1 que a WCAG
   * 1.4.11 pede para identificar um controle — na pratica, formulario que so
   * se enxerga com luz boa e tela boa.
   */
  border: 1px solid var(--border-field);
  border-radius: var(--r);
  color: var(--ink);
  font-size: var(--fs-md);
  transition: border-color var(--dur-fast) var(--ease),
              box-shadow var(--dur-fast) var(--ease);
}
.textarea { min-height: 76px; resize: vertical; line-height: 1.5; }

/**
 * `<textarea class="input">` tambem so redimensiona na VERTICAL.
 *
 * O campo de observacoes do formulario da fatura usa `class="input"` em vez de
 * `class="textarea"`, e com isso herdava o `resize: both` do navegador: puxar o
 * canto arrastava o conteudo do modal para os lados, quebrando o layout dele.
 * Corrigir pelo seletor de elemento pega este caso e qualquer outro futuro.
 */
textarea.input { resize: vertical; min-height: 56px; line-height: 1.5; }

.input::placeholder, .textarea::placeholder {
  /* Placeholder tambem precisa dos 4.5:1 — cinza claro "elegante" e o
     erro de contraste mais comum. */
  color: var(--ink-3); opacity: 1;
}

.input:hover, .select:hover, .textarea:hover { border-color: var(--ink-3); }

.input:focus, .select:focus, .textarea:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px color-mix(in oklab, var(--accent) 18%, transparent);
}

.input:disabled, .select:disabled, .textarea:disabled {
  background: var(--surface-3); color: var(--ink-3); cursor: not-allowed;
}

.input[aria-invalid="true"] { border-color: var(--neg); }
.input[aria-invalid="true"]:focus {
  box-shadow: 0 0 0 3px color-mix(in oklab, var(--neg) 18%, transparent);
}

/**
 * Valor fixo, exibido na FORMA de campo.
 *
 * Usado onde o dado nao e escolha — o cartao no lancamento novo pela tela da
 * fatura, que e sempre o cartao daquela fatura. Um <input disabled> daria a
 * mesma aparencia, mas insinuaria um campo que poderia ser habilitado; isto
 * aqui e texto, e so herda a caixa para nao desalinhar a grade do formulario.
 */
.input--estatico {
  margin: 0;
  display: flex; align-items: center;
  background: var(--surface-3);
  color: var(--ink-2);
}

/* Entrada de valor: monoespacada e grande, e o campo mais usado do app. */
.input--money {
  font-family: var(--font-mono);
  font-size: var(--fs-xl); font-weight: 600;
  font-variant-numeric: tabular-nums;
  text-align: right;
  letter-spacing: -0.02em;
}

/**
 * A SETA VEM DE UM TOKEN, uma por tema.
 *
 * O cinza `%23767676` estava cravado dentro do data URI, calibrado para o tema
 * claro — no escuro ele quase sumia contra o fundo do campo.
 *
 * Um data URI nao enxerga `currentColor` nem `var()`, e `<select>` nao aceita
 * pseudo-elemento de forma confiavel, entao mascara nao serve aqui. A saida e
 * declarar a imagem inteira como token e trocar no bloco de cada tema — que e
 * exatamente onde toda a outra variacao de cor deste sistema ja mora.
 */
.select {
  appearance: none;
  padding-right: var(--s-8);
  background-image: var(--seta-select);
  background-repeat: no-repeat;
  background-position: right var(--s-3) center;
}

.check {
  display: inline-flex; align-items: center; gap: var(--s-2);
  cursor: pointer; user-select: none;
}
.check input {
  appearance: none;
  flex: none; width: 16px; height: 16px;
  border: 1px solid var(--border-strong);
  border-radius: var(--r-sm);
  background: var(--surface);
  cursor: pointer;
  transition: background var(--dur-fast) var(--ease),
              border-color var(--dur-fast) var(--ease);
}
/**
 * O TIQUE TAMBEM VEM DE UM TOKEN, como a seta do <select>.
 *
 * Com `stroke='white'` cravado, o tique ficava branco tambem sobre o --accent
 * do tema ESCURO, que e claro: 2,99:1, abaixo dos 3:1 que a WCAG 1.4.11 pede
 * para o simbolo que identifica o estado do controle. O `:indeterminate` logo
 * abaixo ja usava `--on-accent` corretamente — dois estados do mesmo
 * componente discordando entre si.
 */
.check input:checked {
  background: var(--accent) var(--tique-check) center / 11px no-repeat;
  border-color: var(--accent);
}
.check input:indeterminate {
  background: var(--accent); border-color: var(--accent);
  background-image: linear-gradient(var(--on-accent), var(--on-accent));
  background-size: 9px 2px; background-position: center; background-repeat: no-repeat;
}

/* Abas Despesa / Receita / Transferencia do lancamento rapido. */
.segmented {
  display: grid; grid-auto-flow: column; grid-auto-columns: 1fr;
  gap: 2px; padding: 2px;
  background: var(--surface-3);
  border-radius: var(--r);
}
/* Aceita <a> alem de <button>: as abas de /config sao navegacao de verdade
   (URL propria, botao voltar funcionando), nao um alternador de estado. */
.segmented button,
.segmented a {
  padding: var(--s-2);
  border-radius: calc(var(--r) - 2px);
  font-size: var(--fs-base); font-weight: 550; color: var(--ink-2);
  cursor: pointer;
  text-align: center; text-decoration: none;
  transition: background var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease);
}
.segmented button:hover,
.segmented a:hover { color: var(--ink); }
/**
 * Tres formas de dizer "este esta escolhido", uma para cada natureza:
 *   `aria-checked` — alternador de tipo (radiogroup), em formularios
 *   `aria-current` — aba que e um LINK para outra pagina (/config)
 *   `aria-selected` — o que restou de antes; mantido para nao quebrar tela
 *                     que ainda nao migrou
 *
 * Antes era so `aria-selected`, aplicado tambem a botoes e links onde ele nao
 * vale nada — o estilo funcionava e o leitor de tela nao anunciava estado
 * nenhum. A aparencia estava certa e o significado ausente.
 */
.segmented button[aria-selected="true"],
.segmented button[aria-checked="true"],
.segmented a[aria-selected="true"],
.segmented a[aria-current] {
  background: var(--surface); color: var(--ink); box-shadow: var(--shadow-sm);
}


/* =====================================================================
   11. Toast — inclui o "Desfazer" de 10s (doc 01 §6.5)
   ===================================================================== */

/**
 * O TOAST SOBE ACIMA DO FAB, em vez de cobri-lo.
 *
 * Ele ficava na mesma altura do botao de lancar e da barra de selecao, e como
 * `--z-toast` (500) vence `--z-sticky` (100), quem ganhava a sobreposicao era o
 * toast. Em 375px sao ~34px de largura em cima do FAB, por 10 segundos —
 * justamente o tempo em que o toast oferece "Desfazer" e voce quer tanto
 * desfazer quanto lancar o proximo. "Salvar e novo" virava esperar.
 *
 * A conta empilha: barra + folga + altura do FAB (50px) + respiro.
 */
.toasts {
  position: fixed;
  left: 50%; transform: translateX(-50%);
  bottom: calc(var(--tabbar-h) + var(--s-4) + 50px + var(--s-2)
               + env(safe-area-inset-bottom));
  z-index: var(--z-toast);
  display: flex; flex-direction: column-reverse; gap: var(--s-2);
  width: min(26rem, calc(100vw - var(--s-8)));
  pointer-events: none;
}
@media (min-width: 900px) { .toasts { bottom: var(--s-6); left: var(--s-6); transform: none; } }

.toast {
  display: flex; align-items: center; gap: var(--s-3);
  padding: var(--s-3) var(--s-4);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  background: var(--surface);
  box-shadow: var(--shadow-lg);
  font-size: var(--fs-md);
  pointer-events: auto;
  animation: toast-in var(--dur-slow) var(--ease);
}
.toast__icon { flex: none; width: 17px; height: 17px; }
.toast__text { flex: 1; min-width: 0; }
.toast--sucesso .toast__icon { color: var(--pos); }
.toast--erro    .toast__icon { color: var(--neg); }
.toast--aviso   .toast__icon { color: var(--warn); }
.toast--info    .toast__icon { color: var(--info); }

.toast__action {
  flex: none;
  padding: var(--s-1) var(--s-2);
  border-radius: var(--r-sm);
  color: var(--accent-ink); font-weight: 600; font-size: var(--fs-base);
  cursor: pointer;
}
.toast__action:hover { background: var(--accent-soft); }

/* Barra que esvazia nos 10 segundos do desfazer. */
.toast__timer {
  position: absolute; left: 0; bottom: 0; height: 2px;
  background: var(--accent);
  border-radius: 0 0 0 var(--r-lg);
  animation: drain 10s linear forwards;
}
.toast { position: relative; overflow: hidden; }

@keyframes toast-in {
  from { opacity: 0; transform: translateY(8px) scale(0.98); }
  to   { opacity: 1; transform: none; }
}
@keyframes drain { from { width: 100%; } to { width: 0%; } }


/* =====================================================================
   12. Modal — <dialog> nativo
   =====================================================================
   Elemento nativo para nao reinventar foco, ESC e camada (regra de
   produto: nao reinventar affordance padrao). */

dialog.modal {
  width: min(30rem, calc(100vw - var(--s-8)));
  max-height: min(85dvh, 44rem);
  padding: 0;
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  background: var(--surface);
  color: var(--ink);
  box-shadow: var(--shadow-lg);
  overflow: hidden;
}

/* CADEIA DE ALTURA DO MODAL — não mexer sem entender.
   `overflow-y: auto` no corpo só rola se a altura dele estiver CONTIDA. Para
   isso, cada nível entre o <dialog> e o .modal__body precisa ser flex column
   com `min-height: 0`.

   `min-height: 0` é o essencial: item flex nasce com `min-height: auto`, que o
   impede de encolher abaixo do conteúdo. Sem ele o corpo cresce, o max-height
   corta, e o rodapé com os botões fica fora da tela — foi o que aconteceu com
   o bloco de comprovantes. */

dialog.modal[open] { display: flex; flex-direction: column; }

dialog.modal > *,
dialog.modal form {
  display: flex;
  flex-direction: column;
  flex: 1 1 auto;
  min-height: 0;
}

.modal__head, .modal__foot { flex: none; }
/**
 * O VEU PRECISA ESCURECER OS DOIS TEMAS.
 *
 * A cor era `oklch(0.22 …)` cravada — o --ink do tema CLARO. No tema escuro a
 * pagina ja esta em 0.171, entao o veu ficava praticamente da mesma cor do
 * fundo (~1,04:1): o modal abria e nada atras dele mudava. Sem a separacao
 * visual, o dialogo parecia parte da tela, e nao algo por cima dela.
 *
 * `::backdrop` NAO herda do documento, entao nao adianta usar um token que
 * dependa de cascata — mas var() no proprio ::backdrop funciona, porque as
 * variaveis vem de :root. Preto puro com alfa serve aos dois temas: escurece
 * o claro e aprofunda o escuro.
 */
dialog.modal::backdrop {
  background: oklch(0 0 0 / 0.45);
  backdrop-filter: blur(2px);
}
dialog.modal[open] { animation: modal-in var(--dur) var(--ease); }

.modal__head {
  display: flex; align-items: center; gap: var(--s-3);
  padding: var(--s-4) var(--s-5);
  border-bottom: 1px solid var(--border);
}
.modal__title { font-size: var(--fs-lg); font-weight: 600; }
.modal__body {
  padding: var(--s-5);
  overflow-y: auto;
  overscroll-behavior: contain;   /* rolar o fim do modal não rola a página */
  flex: 1 1 auto;
  min-height: 0;
}
.modal__foot  {
  display: flex; justify-content: flex-end; gap: var(--s-2);
  /* Sem `wrap`, os tres botoes do formulario da fatura — Cancelar, Salvar e
     novo, Salvar — estouravam a largura em 360px e o ultimo saia da tela. */
  flex-wrap: wrap;
  padding: var(--s-4) var(--s-5);
  border-top: 1px solid var(--border);
  background: var(--surface-2);
}

@keyframes modal-in {
  from { opacity: 0; transform: translateY(10px) scale(0.985); }
  to   { opacity: 1; transform: none; }
}

/* Em telas estreitas o modal vira folha inferior — alcancavel com o polegar. */
@media (max-width: 640px) {
  dialog.modal {
    width: 100vw; max-width: none;
    margin: auto auto 0;
    border-radius: var(--r-lg) var(--r-lg) 0 0;
    padding-bottom: env(safe-area-inset-bottom);
  }
  dialog.modal[open] { animation: sheet-in var(--dur-slow) var(--ease); }
  @keyframes sheet-in { from { transform: translateY(100%); } to { transform: none; } }

  /**
   * 16px E O PISO QUE IMPEDE O SAFARI DE DAR ZOOM.
   *
   * O Safari do iPhone amplia a pagina ao focar um campo com fonte menor que
   * 16px — e NAO desfaz o zoom ao sair. O corpo da UI e 14px, entao o fluxo
   * inteiro de lancar (abrir o modal, tocar em Descricao) deixava a tela presa
   * a ~114%, com a interface cortada nas laterais ate o proximo recarregamento.
   * So o campo de valor escapava, por usar --fs-xl.
   *
   * `max()` em vez de `16px` fixo para que o campo de valor, que ja e maior,
   * continue maior.
   *
   * A saida obvia — `maximum-scale=1` no viewport — resolveria isto matando o
   * pinch-zoom da pagina inteira. Trocar um incomodo por uma barreira de
   * acessibilidade nao e conserto.
   */
  .input, .select, .textarea { font-size: max(16px, var(--fs-md)); }
}


/* =====================================================================
   13. Estado vazio e carregamento
   =====================================================================
   Doc 01 §6.8: nenhuma tela vazia sem orientacao do proximo passo. */

.empty {
  display: flex; flex-direction: column; align-items: center;
  gap: var(--s-3);
  padding: var(--s-12) var(--s-5);
  text-align: center;
}
.empty__icon {
  display: grid; place-items: center;
  width: 42px; height: 42px; border-radius: var(--r-lg);
  background: var(--surface-3); color: var(--ink-3);
}
.empty__icon svg { width: 20px; height: 20px; }
.empty__title { font-size: var(--fs-md); font-weight: 600; }
.empty__text  { max-width: 34ch; font-size: var(--fs-base); color: var(--ink-2); }

/* Skeleton, nao spinner no meio do conteudo (regra de produto). */
.skeleton {
  background: linear-gradient(90deg,
    var(--surface-3) 25%,
    color-mix(in oklab, var(--surface-3) 55%, var(--surface)) 37%,
    var(--surface-3) 63%);
  background-size: 400% 100%;
  border-radius: var(--r-sm);
  animation: shimmer 1.3s ease-in-out infinite;
}
.skeleton--text { height: 0.75rem; }
.skeleton--row  { height: 2.75rem; margin-bottom: 1px; border-radius: 0; }

@keyframes shimmer { from { background-position: 100% 0; } to { background-position: 0 0; } }

/* Indicador de progresso do htmx no topo da area de conteudo. */
.htmx-indicator { opacity: 0; transition: opacity var(--dur) var(--ease); }
.htmx-request .htmx-indicator, .htmx-request.htmx-indicator { opacity: 1; }


/* =====================================================================
   14. Graficos SVG
   =====================================================================
   Renderizados no servidor: sem dependencia, sem KB de JS, CSP limpa e
   funcionam com JS desligado. */

.chart { width: 100%; height: auto; overflow: visible; }
.chart__grid  { stroke: var(--border); stroke-width: 1; }
.chart__axis  { fill: var(--ink-3); font-size: 10px; font-variant-numeric: tabular-nums; }
.chart__bar   { transition: opacity var(--dur-fast) var(--ease); }
.chart__bar:hover { opacity: 0.82; }
.chart__bar--pos { fill: var(--pos-dot); }
.chart__bar--neg { fill: var(--neg-dot); }
.chart__line  { fill: none; stroke: var(--accent); stroke-width: 2;
                stroke-linecap: round; stroke-linejoin: round; }
.chart__area  { fill: var(--accent); opacity: 0.10; }

/* Dias com saldo negativo destacados na projecao (RN-13). */
.chart__danger { fill: var(--neg-soft); }
.chart__zero   { stroke: var(--border-strong); stroke-width: 1; stroke-dasharray: 3 3; }

.donut__slice {
  transition: opacity var(--dur-fast) var(--ease);
  cursor: pointer;
}
.donut__slice:hover { opacity: 0.8; }
.donut__center {
  fill: var(--ink); font-size: 15px; font-weight: 650;
  font-variant-numeric: tabular-nums; text-anchor: middle;
}
.donut__label { fill: var(--ink-3); font-size: 10px; text-anchor: middle; }

.legend { display: flex; flex-direction: column; gap: var(--s-1); }
.legend__item {
  display: grid; grid-template-columns: auto 1fr auto auto;
  align-items: center; gap: var(--s-2);
  padding: var(--s-1) var(--s-2);
  border-radius: var(--r-sm);
  font-size: var(--fs-base);
  cursor: pointer;
  transition: background var(--dur-fast) var(--ease);
}
.legend__item:hover { background: var(--surface-2); }
.legend__swatch { width: 9px; height: 9px; border-radius: 2px; background: var(--c); }
.legend__name  { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.legend__value { font-variant-numeric: tabular-nums; font-weight: 550; }
.legend__pct   { min-width: 3rem; text-align: right; color: var(--ink-3);
                 font-variant-numeric: tabular-nums; font-size: var(--fs-sm); }


/* =====================================================================
   15. Orcamento — semaforo (RN-14)
   ===================================================================== */

.meter {
  height: 5px; border-radius: var(--r-full);
  background: var(--surface-3); overflow: hidden;
}
.meter__fill {
  height: 100%; border-radius: var(--r-full);
  transition: width var(--dur-slow) var(--ease);
  /**
   * COR PADRAO, e nao so pelos modificadores.
   *
   * A cor vinha exclusivamente de `.meter--ok/--warn/--over`. Quem usasse
   * `.meter` sem modificador desenhava a CALHA e nada dentro dela — foi o que
   * aconteceu com o bloco "Resultados do mes" da Visao geral e com a barra de
   * progresso da importacao: duas barras que existiam no HTML, ocupavam
   * espaco e nunca mostraram nada.
   *
   * O semaforo (`--var-cor`) permite que quem tem uma cor propria a passe
   * como variavel local, sem precisar de um modificador para cada caso.
   */
  background: var(--meter-cor, var(--accent));
}
/**
 * Variante fina, para caber DENTRO de uma linha de lista.
 *
 * Usada sob o nome do cartao na Visao geral: a barra vira um rodape de 3px do
 * proprio nome, sem roubar uma linha da grade nem empurrar as colunas de valor.
 */
.meter--linha {
  display: block;
  /* `flex-basis: 100%` empurra a barra para a proxima linha do flex do nome —
     e o `max-width` a impede de atravessar a lista inteira. */
  flex-basis: 100%;
  max-width: 7rem;
  height: 3px;
  margin-top: calc(var(--s-05) * -1);
}

.meter--ok    .meter__fill { background: var(--pos-dot); }   /* < 70%  */
.meter--warn  .meter__fill { background: var(--warn-dot); }  /* 70–99% */
.meter--over  .meter__fill { background: var(--neg-dot); }   /* >= 100% */

/**
 * O campo de meta, na tela de orcamento.
 *
 * NAO usa `.input--money`: aquele e o campo de valor do lancamento, grande e em
 * `--fs-xl` porque e o campo mais usado do app e costuma estar sozinho num
 * modal. Aqui sao trinta campos numa tabela, um por categoria — no mesmo
 * tamanho, cada linha teria a altura de um titulo e a tela viraria um rolo.
 *
 * `width: 100%` com `min-width: 0` para o campo caber na celula da grade em vez
 * de esticar a coluna: sem o `min-width`, a largura intrinseca do input ganha
 * do `minmax()` e as colunas de valor saem do lugar no celular.
 */
.input--meta {
  margin: 0;
  width: 100%; min-width: 0;
  padding: var(--s-1) var(--s-2);
  font-variant-numeric: tabular-nums;
  text-align: right;
}
/* Campo vazio e meta ausente: o placeholder nao pode competir com os valores
   preenchidos ao lado. */
.input--meta::placeholder { color: var(--ink-3); }


/* =====================================================================
   15b. Trilha de auditoria (doc 06 par.11)
   =====================================================================
   Lista de eventos com a hora a esquerda. Nao usa `.entry`: aquela grade e
   feita para "descricao + valor + saldo acumulado", e aqui nao ha valor
   nenhum — havia coluna vazia em toda linha. */

.trilha { display: flex; flex-direction: column; }

.trilha__item {
  display: grid;
  grid-template-columns: 3.5rem 1fr;
  gap: var(--s-3);
  padding: var(--s-3) 0;
  border-bottom: 1px solid var(--border);
}
.trilha__item:last-child { border-bottom: 0; }

/* A hora fica numerica e alinhada a direita: a coluna vira uma regua, e o
   olho desce por ela sem reler cada linha. */
.trilha__quando {
  text-align: right;
  font-variant-numeric: tabular-nums;
  line-height: 1.3;
  color: var(--ink-2);
}
.trilha__quando strong { display: block; font-size: var(--fs-sm); }

.trilha__corpo { min-width: 0; }
.trilha__evento { margin: 0 0 var(--s-1); font-weight: 550; }
.trilha__evento .tiny { font-weight: 400; margin-left: var(--s-1); }

.trilha__campos {
  margin: 0 0 var(--s-1);
  padding: 0;
  list-style: none;
  font-size: var(--fs-sm);
}
/* `overflow-wrap` porque descricao longa e texto do usuario: sem isto uma
   linha sem espacos alarga a tela inteira no celular. */
.trilha__campos li { overflow-wrap: anywhere; }
.trilha__campos s { color: var(--ink-3); }


/* =====================================================================
   15c. Log do sistema (doc 02 par.8)
   =====================================================================
   Monoespacado e denso: e saida de maquina, e o que se procura aqui e um
   padrao repetido, nao uma frase. */

.log {
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  line-height: 1.55;
  /* A rolagem horizontal fica NESTE bloco, e nao na pagina: linha de log e
     longa por natureza, e sem isto ela alarga a tela inteira no celular. */
  overflow-x: auto;
}

.log__linha {
  display: grid;
  grid-template-columns: 3rem 4.5rem 1fr;
  gap: var(--s-2);
  padding: var(--s-1) 0;
  border-bottom: 1px solid var(--border);
}
.log__linha:last-child { border-bottom: 0; }

.log__hora  { color: var(--ink-3); font-variant-numeric: tabular-nums; }
.log__nivel { font-weight: 600; color: var(--ink-2); }

/* `overflow-wrap` porque a mensagem carrega caminho de arquivo e SQL, que nao
   tem espaco onde quebrar. */
.log__msg { min-width: 0; overflow-wrap: anywhere; }
.log__ctx { display: block; color: var(--ink-3); }

.log__linha--error   .log__nivel { color: var(--neg); }
.log__linha--warning .log__nivel { color: var(--warn); }
.log__linha--error   { background: color-mix(in oklab, var(--neg-soft) 40%, transparent); }

/* Linha fora do formato do `Core\Log`: ela e mostrada como veio, e a marca
   lateral diz que o sistema nao a escreveu. E normalmente a mais importante. */
.log__linha--crua {
  border-left: 2px solid var(--border-strong);
  padding-left: var(--s-2);
  color: var(--ink-2);
}


/* =====================================================================
   15d. Paletas
   =====================================================================
   Cada paleta e um par de numeros, e vale nos DOIS temas: aqui so se define
   matiz e croma, e o tema so define luminosidade. As duas coisas se somam em
   vez de brigar — e e por isso que quatro paletas x dois temas nao viraram
   oito listas de cor.

   Precisam vir DEPOIS do bloco `:root[data-theme="dark"]`: a especificidade
   e igual, entao quem vier por ultimo vence.

   No tema CLARO a diferenca entre elas e menor, de proposito: os neutros
   claros quase nao carregam matiz, e o acento aparece nos botoes e nos
   graficos. No escuro, onde o fundo e uma superficie grande, ela se ve
   inteira. */

/* Acento quase sem croma: o verde e o vermelho do dinheiro passam a ser a
   unica cor forte da tela. Num sistema financeiro isso e vantagem funcional,
   e nao so estetica — a cor volta a significar uma coisa so. */
:root[data-paleta="grafite"] { --matiz: 285; --croma: 0.15; }

/* Azul-esverdeado, distante de tudo que ja tem significado na tela. Croma em
   0.75 porque o teal em saturacao cheia fica berrante numa area grande. */
:root[data-paleta="petroleo"] { --matiz: 205; --croma: 0.75; }

/* O mesmo violeta com metade do croma. Serve para testar uma hipotese: boa
   parte do que se le como "cor de template" nao e o matiz, e o EXCESSO. */
:root[data-paleta="violeta-suave"] { --matiz: 295; --croma: 0.5; }

/* Azul escuro.

   E a unica paleta que mexe numa cor SEMANTICA, e o motivo e colisao: o
   acento em 265 fica a 15 graus do `--info` (250), e dois azuis quase iguais
   num toast sao piores que nenhum. O info anda para 230 — longe do acento e
   ainda longe do verde. Como `--info` tem valor proprio por tema, a correcao
   precisa aparecer nos tres lugares em que ele e definido. */
:root[data-paleta="indigo"] {
  --matiz: 265;
  --croma: 1;
  --info:      oklch(0.52 0.150 230);   --info-dot: oklch(0.60 0.165 230);
  --info-soft: oklch(0.955 0.032 230);
}

@media (prefers-color-scheme: dark) {
  :root[data-paleta="indigo"]:not([data-theme="light"]) {
    --info:      oklch(0.735 0.130 230); --info-dot: oklch(0.690 0.150 230);
    --info-soft: oklch(0.285 0.055 230);
  }
}

:root[data-paleta="indigo"][data-theme="dark"] {
  --info:      oklch(0.735 0.130 230); --info-dot: oklch(0.690 0.150 230);
  --info-soft: oklch(0.285 0.055 230);
}


/* --- O seletor de paleta, em /config/preferencias ---------------------
   A amostra de cada opcao carrega o proprio `data-paleta`, entao ela pinta com
   o acento de verdade — nao com uma cor copiada, que divergiria do CSS na
   primeira mudanca de valor. */

.paletas { display: grid; gap: var(--s-2); margin: var(--s-2) 0; }

.paleta,
.densidade {
  display: flex; align-items: center; gap: var(--s-3);
  padding: var(--s-2) var(--s-3);
  border: 1px solid var(--border);
  border-radius: var(--r);
  cursor: pointer;
  transition: background var(--dur-fast) var(--ease), border-color var(--dur-fast) var(--ease);
}
.paleta:hover,
.densidade:hover { background: var(--surface-2); }
.paleta.is-escolhida,
.densidade.is-escolhida { border-color: var(--accent); background: var(--accent-soft); }

/* O radio continua no DOM, e nao `display:none`: some do teclado e do leitor
   de tela junto. Fica sob a amostra, do tamanho dela. */
.paleta input,
.densidade input { position: absolute; opacity: 0; width: 1px; height: 1px; }
.paleta:focus-within,
.densidade:focus-within { outline: 2px solid var(--accent); outline-offset: 2px; }

/* Duas faixas: o acento e a superficie. Uma cor so nao mostraria que a paleta
   move tambem o fundo — que e metade do efeito. */
.paleta__amostra {
  flex: none; width: 2.5rem; height: 1.6rem;
  border-radius: var(--r-sm);
  border: 1px solid var(--border-strong);
  background: linear-gradient(to right, var(--accent) 0 60%, var(--surface-3) 60% 100%);
}

.paleta__texto { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.paleta__texto strong { font-weight: 550; font-size: var(--fs-md); }


/* =====================================================================
   15e. Densidade
   =====================================================================
   Um numero, e ele vale nos dois temas e em todas as paletas — as tres
   preferencias sao ortogonais de proposito, entao 5 paletas x 3 densidades
   continuam sendo 5 + 3 declaracoes, e nao 15.

   Nao precisa vir depois de nada: ninguem mais redefine `--densidade`. */

/* Cabem cerca de tres linhas a mais numa lista de lancamentos em tela cheia.
   0.8 e o limite antes de o texto de duas linhas da `.entry` comecar a
   encostar um no outro — abaixo disso a lista fica apertada, nao densa. */
:root[data-densidade="compacta"] { --densidade: 0.8; }

/* Para tela grande, ou para quem le melhor com ar. Acima de 1.2 os paineis
   comecam a caber menos coisa acima da dobra sem ficarem mais legiveis. */
:root[data-densidade="ampla"] { --densidade: 1.2; }

/* A ESCOLHA REUSA O DESENHO DA PALETA. `.densidade` herda de `.paleta` por
   lista de seletores em vez de copiar oito declaracoes — sao o mesmo controle
   com amostra diferente, e duas copias divergiriam no primeiro ajuste. */
.densidades { display: grid; gap: var(--s-2); margin: var(--s-2) 0; }

/* A amostra e tres barras, e o espaco entre elas SAI DO PROPRIO TOKEN — entao
   ela nao mente: e literalmente o gap que a lista vai ter. Altura fixa nas
   barras de proposito, porque a densidade nao encolhe conteudo, so espaco. */
.densidade__amostra {
  flex: none;
  display: flex; flex-direction: column; justify-content: center;
  gap: var(--s-1);
  width: 2.5rem; height: 1.6rem;
  padding: var(--s-1) var(--s-05);
  border-radius: var(--r-sm);
  border: 1px solid var(--border-strong);
  background: var(--surface-3);
}
.densidade__amostra i { display: block; height: 2px; border-radius: 1px; background: var(--ink-3); }



/* =====================================================================
   16. Navegacao inferior (mobile)
   =====================================================================
   A maioria dos lancamentos acontece no celular, na hora da compra
   (doc 01 §6.1). No mobile a navegacao vai para o polegar. */

.tabbar {
  position: fixed; inset: auto 0 0 0;
  z-index: var(--z-sticky);
  display: grid; grid-auto-flow: column; grid-auto-columns: 1fr;
  padding-bottom: env(safe-area-inset-bottom);
  background: color-mix(in oklab, var(--surface) 92%, transparent);
  backdrop-filter: blur(10px);
  border-top: 1px solid var(--border);
}
@media (min-width: 900px) { .tabbar { display: none; } }

.tabbar__item {
  display: flex; flex-direction: column; align-items: center; gap: 3px;
  padding: var(--s-2) var(--s-1) var(--s-3);
  color: var(--ink-3); text-decoration: none;
  font-size: var(--fs-xs); font-weight: 500;
  transition: color var(--dur-fast) var(--ease);
  -webkit-tap-highlight-color: transparent;
}
/**
 * O ROTULO NAO PODE QUEBRAR EM DUAS LINHAS.
 *
 * Se quebrar, a barra inteira cresce — e como o FAB e a barra de selecao se
 * posicionam a partir de `--tabbar-h`, que e um valor fixo, o botao de lancar
 * pousa EM CIMA da barra. Em 360px isso ja acontecia com "Lancamentos".
 *
 * O corte com reticencias e a rede de seguranca; a solucao de verdade e o
 * rotulo curto, que e o que a tabbar usa agora.
 */
.tabbar__item span {
  max-width: 100%;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.tabbar__item svg { width: 20px; height: 20px; flex: none; }

/* O "Mais" e um <button>, nao um <a>: precisa perder o visual de botao. */
button.tabbar__item {
  background: none; border: 0; font-family: inherit; cursor: pointer;
}

/**
 * A folha "Mais".
 *
 * Reusa `dialog.modal`, que no celular ja sobe de baixo com o canto arredondado.
 * O `max-height` existe para a folha nao virar tela cheia com nove itens — ela
 * deve continuar parecendo algo POR CIMA da tela, nao uma navegacao nova.
 */
.modal--folha { max-height: 85dvh; }
.modal--folha .modal__body { padding-top: var(--s-2); }
.menu-mais__grupo { display: flex; flex-direction: column; gap: var(--s-05); }
.menu-mais__grupo + .sidebar__label { margin-top: var(--s-4); }

/* Alvo de toque: no menu, cada linha e um destino inteiro. */
.modal--folha .nav-item { min-height: var(--alvo-toque); }

/* So faz sentido onde a sidebar nao existe. */
@media (min-width: 900px) { .modal--folha { display: none; } }
.tabbar__item.is-active { color: var(--accent); }
.tabbar__item:active { background: var(--surface-3); }


/* =====================================================================
   16b. Telas de autenticacao
   ===================================================================== */

body.auth {
  display: grid;
  place-items: center;
  padding: var(--s-5);
  min-height: 100dvh;
}

.auth__box { width: min(26rem, 100%); }
.auth__box .panel { box-shadow: var(--shadow); }

.auth__brand {
  display: flex; align-items: center; justify-content: center; gap: var(--s-2);
  margin-bottom: var(--s-5);
  font-size: var(--fs-lg); font-weight: 650; letter-spacing: -0.02em;
}

.alerta {
  display: flex; align-items: flex-start; gap: var(--s-2);
  padding: var(--s-3);
  border: 1px solid var(--border);
  border-radius: var(--r);
  font-size: var(--fs-base);
}
.alerta svg { flex: none; margin-top: 1px; }
.alerta--erro   { background: var(--neg-soft);  border-color: var(--neg);  color: var(--neg); }
.alerta--aviso  { background: var(--warn-soft); border-color: var(--warn); color: var(--warn); }
.alerta--info   { background: var(--info-soft); border-color: var(--info); color: var(--info); }

/* Codigo TOTP: espacado e monoespacado, para conferir digito a digito. */
.input--codigo {
  font-family: var(--font-mono);
  font-size: var(--fs-2xl); font-weight: 600;
  letter-spacing: 0.28em; text-align: center;
  padding-left: calc(var(--s-3) + 0.28em);
  font-variant-numeric: tabular-nums;
}

.passos { display: flex; flex-direction: column; gap: var(--s-6); counter-reset: passo; }
.passos > li {
  display: flex; flex-direction: column; gap: var(--s-2);
  padding-left: var(--s-8);
  position: relative;
}
.passos > li::before {
  counter-increment: passo;
  content: counter(passo);
  position: absolute; left: 0; top: 0;
  display: grid; place-items: center;
  width: 22px; height: 22px; border-radius: var(--r-full);
  background: var(--accent-soft); color: var(--accent-ink);
  font-size: var(--fs-xs); font-weight: 700;
  font-variant-numeric: tabular-nums;
}
.passos strong { font-size: var(--fs-md); font-weight: 600; }
.passos p { margin: 0; }

/* =====================================================================
   QR do segundo fator
   =====================================================================
   O SVG ja vem com branco e preto FIXOS, e nao com tokens do tema: leitor de
   celular assume escuro sobre claro, e QR invertido costuma nao engatar. O
   quadro branco destoa no tema escuro, e e o preco certo — QR bonito que nao
   le nao serve para nada.

   A moldura clara em volta continua a zona de silencio que o SVG ja tem: no
   tema escuro, o preto do painel encostando na borda do simbolo confunde o
   leitor tanto quanto a falta dela. */
.qr-2fa {
  display: flex; justify-content: center;
  padding: var(--s-3);
  background: #ffffff;
  border-radius: var(--r);
  border: 1px solid var(--border);
  margin-bottom: var(--s-3);
}
.qr-2fa svg { display: block; max-width: 100%; height: auto; }

.chave-box {
  display: flex; align-items: center; gap: var(--s-2);
  padding: var(--s-3);
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--r);
}
.chave {
  flex: 1; min-width: 0;
  font: 600 var(--fs-md)/1.4 var(--font-mono);
  letter-spacing: 0.06em;
  word-break: break-all;
}

.codigos {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr));
  gap: var(--s-2);
  padding: var(--s-3);
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--r);
}
.codigos code {
  font: 550 var(--fs-base)/1.5 var(--font-mono);
  letter-spacing: 0.04em;
}


/* =====================================================================
   17. Utilitarios
   ===================================================================== */

.stack   { display: flex; flex-direction: column; }
.stack-2 { display: flex; flex-direction: column; gap: var(--s-2); }
.stack-3 { display: flex; flex-direction: column; gap: var(--s-3); }
.stack-4 { display: flex; flex-direction: column; gap: var(--s-4); }
.stack-5 { display: flex; flex-direction: column; gap: var(--s-5); }

.row    { display: flex; align-items: center; gap: var(--s-3); }
.row-2  { display: flex; align-items: center; gap: var(--s-2); }
.row--between { display: flex; align-items: center; justify-content: space-between; gap: var(--s-3); }
.row--wrap { flex-wrap: wrap; }

.grow  { flex: 1 1 auto; min-width: 0; }
.muted { color: var(--ink-3); }
.dim   { color: var(--ink-2); }
.label { font-size: var(--fs-sm); font-weight: 550; color: var(--ink-2); }
.tiny  { font-size: var(--fs-sm); }
.right { text-align: right; }
.nowrap { white-space: nowrap; }
.truncate { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }

.hr { height: 1px; background: var(--border); border: 0; }

@media (max-width: 899px) { .desktop-only { display: none !important; } }
@media (min-width: 900px) { .mobile-only  { display: none !important; } }

/* Tabela larga rola sozinha; a pagina nunca rola na horizontal. */
.scroll-x { overflow-x: auto; overscroll-behavior-x: contain; }


/* =====================================================================
   18. Movimento reduzido
   =====================================================================
   Nao opcional. Quem pediu menos movimento recebe o resultado final
   imediato — inclusive a barra de 10s do desfazer, que vira estatica. */

@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;
  }
  .toast__timer { animation: none; width: 100%; }
  .skeleton { animation: none; }
}


/* =====================================================================
   19. Impressao
   ===================================================================== */

@media print {
  .sidebar, .tabbar, .topbar, .fab, .toasts, .panel__tools { display: none !important; }
  .app { grid-template-columns: 1fr; }
  .panel { border-color: #ccc; break-inside: avoid; }

  /**
   * IMPRIMIR SEMPRE NO TEMA CLARO.
   *
   * `body { color: #000 }` sozinho nao bastava: `.panel__title`, `.num`, `.chip`
   * e dezenas de outras regras redeclaram `var(--ink*)`, entao quem estivesse
   * no tema escuro imprimia texto quase BRANCO sobre papel branco — a folha
   * saia em branco e o motivo nao era obvio.
   *
   * Redeclarar os TOKENS aqui conserta a folha inteira de uma vez, sem
   * precisar caçar cada regra: tudo que le var(--ink) passa a ler o valor
   * claro. Vale para `:root` e para o `[data-theme="dark"]` manual.
   */
  :root, :root[data-theme="dark"] {
    --bg:        #fff;
    --surface:   #fff;
    --surface-2: #fff;
    --surface-3: #f2f2f2;
    --border:        #ddd;
    --border-strong: #bbb;
    --border-field:  #999;
    --ink:   #000;
    --ink-2: #333;
    --ink-3: #555;
    --shadow-sm: none; --shadow: none; --shadow-lg: none;
  }

  body { background: #fff; color: #000; }
}

/* =====================================================================
   Donut de categorias (S9) — SVG renderizado no servidor
   =====================================================================
   Sem biblioteca: o percentual vira comprimento do traco porque o raio
   15.9155 da circunferencia ~100. Nao ha canvas nem script. */

.donut-bloco {
  display: grid; gap: var(--s-4);
  grid-template-columns: minmax(7rem, 9rem) 1fr;
  align-items: center;
}
@media (max-width: 520px) { .donut-bloco { grid-template-columns: 1fr; justify-items: center; } }

.donut { width: 100%; height: auto; max-width: 9rem; }

.donut-legenda { display: flex; flex-direction: column; gap: var(--s-1); min-width: 0; }
/* O item inerte ("Outros") tem a MESMA grade do link, para a coluna de valores
   continuar alinhada — muda so o que indica que ele nao leva a lugar nenhum. */
.donut-legenda a,
.donut-legenda__inerte {
  display: grid; align-items: center; gap: var(--s-2);
  grid-template-columns: auto 1fr auto auto;
  padding: var(--s-1) var(--s-2);
  border-radius: var(--r-sm);
  color: var(--ink-2); text-decoration: none;
  font-size: var(--fs-sm);
}
.donut-legenda a:hover { background: var(--surface-2); color: var(--ink); }
.donut-legenda__inerte { color: var(--ink-3); cursor: default; }
.donut-legenda .num { font-variant-numeric: tabular-nums; }

/* =====================================================================
   Relatorios (S10)
   ===================================================================== */

/* Abas: rotas de verdade, entao <a>. O estado ativo e sublinhado em vez de
   fundo cheio — a aba nao deve competir com os numeros da tela. */
.abas {
  display: flex; gap: var(--s-1);
  margin-bottom: var(--s-4);
  overflow-x: auto;
  border-bottom: 1px solid var(--border);
}
.abas__item {
  padding: var(--s-3) var(--s-4);
  white-space: nowrap;
  color: var(--ink-3);
  font-size: var(--fs-sm); font-weight: 500;
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
}
.abas__item:hover   { color: var(--ink-2); }
.abas__item.is-ativa {
  color: var(--accent);
  border-bottom-color: var(--accent);
}

/* Filtro de contas: uma faixa de chips, nao um painel com cabecalho. Ele
   acompanha as cinco abas e nao deve pesar mais que o relatorio. */
.filtro-contas {
  display: flex; align-items: center; flex-wrap: wrap;
  gap: var(--s-2);
  padding: var(--s-3) var(--s-4);
  margin-bottom: var(--s-4);
}
.filtro-contas__rotulo {
  display: inline-flex; align-items: center; gap: var(--s-2);
  margin-right: var(--s-2);
  font-size: var(--fs-sm); color: var(--ink-3);
}
.chip--check { cursor: pointer; }
.chip--check input { margin-right: var(--s-1); }

.segmented--sm button { padding: var(--s-1) var(--s-3); font-size: var(--fs-xs); }

.relatorio-total {
  display: flex; align-items: baseline; justify-content: space-between;
  padding-bottom: var(--s-3);
  margin-bottom: var(--s-2);
  border-bottom: 1px solid var(--border);
}
.relatorio-total .num-strong { font-size: var(--fs-xl); }

/* Linha de grupo x linha de filha: a filha e subordinada, e a indentacao
   sozinha nao basta numa lista longa — o tamanho do numero tambem cede. */
.entry--grupo { font-weight: 500; }
.entry--filha { padding-top: 2px; padding-bottom: 2px; }
.entry--filha .entry__desc { color: var(--ink-3); font-size: var(--fs-sm); }

/* Barra de participacao. Comparar de relance vale mais que ler a porcentagem. */
.barra {
  display: inline-block; width: 84px; height: 5px;
  border-radius: var(--r-full);
  background: var(--surface-3);
  overflow: hidden;
}
.barra__preenchida {
  display: block; height: 100%;
  background: var(--accent);
  border-radius: var(--r-full);
}

/* Tabela de relatorio em grid: alinhamento por coluna sem <table>, que e
   dificil de deixar responsivo. Rola na horizontal quando nao couber. */
.tabela-relatorio { display: grid; gap: 0; min-width: 0; overflow-x: auto; }
.tabela-relatorio__head,
.tabela-relatorio__row {
  display: grid;
  grid-template-columns: minmax(9rem, 1fr) repeat(3, minmax(6rem, auto));
  gap: var(--s-3);
  align-items: center;
  padding: var(--s-3) 0;
  border-bottom: 1px solid var(--border);
}
.tabela-relatorio__head {
  font-size: var(--fs-xs); text-transform: uppercase; letter-spacing: .04em;
  color: var(--ink-3);
}
.tabela-relatorio__row:last-child { border-bottom: 0; }
.tabela-relatorio__row--total {
  border-top: 1px solid var(--border-strong);
  border-bottom: 0;
  font-weight: 600;
}
.tabela-relatorio--comparativo .tabela-relatorio__row .tiny { margin-left: 2px; }

/* Evolucao: barras lado a lado por mes. Rola na horizontal no celular, onde
   doze meses nunca caberiam sem espremer os rotulos ate ficarem ilegiveis. */
/* O grafico de Evolucao nao tem mais CSS proprio de desenho: ele usa
   `.grafico__area`, `.grafico__eixo`, `.grafico__marca`, `.grafico__barra` e
   `.grafico__rotulos` — as mesmas classes dos graficos da Visao geral.

   Antes eram `<span>` com `height: N%` dentro de um flex, sem eixo e sem grade:
   um gráfico do qual não se lia valor nenhum. Trocar por SVG na grade comum
   apagou vinte linhas daqui e ganhou o eixo de graça. O que sobra abaixo e
   somente o que e especifico desta tela. */

/* A linha de resultados, abaixo do eixo dos meses.
   Herda a grade de `.grafico__rotulos`, entao cai alinhada com as barras. */
.evolucao__resultados {
  font-size: var(--fs-xs);
  font-variant-numeric: tabular-nums;
  margin-top: var(--s-1);
  padding-top: var(--s-1);
  border-top: 1px solid var(--border);
}

/* =====================================================================
   Projecao de 90 dias
   =====================================================================
   Ela vive dentro de `.grafico__area`, a mesma grade dos graficos da Visao
   geral: coluna 1 e a calha dos rotulos do eixo, coluna 2 e o desenho, linha 2
   sao os rotulos de data. Reaproveitar a grade e o que faz o `top: N%` do
   rotulo cair exatamente sobre a linha que o SVG desenhou na coordenada N. */

/* `grid-area` igual ao `.grafico__desenho`: sem isso o SVG cairia na coluna do
   eixo e os rotulos ficariam por cima dele. */
.projecao { display: block; width: 100%; height: 11rem; grid-area: 1 / 2; }

/* O zero nao e uma referencia entre outras — e a fronteira entre sobrar e
   faltar. Tracejado e mais forte que a grade. */
.projecao__zero {
  stroke: var(--border-strong); stroke-width: 1.5;
  stroke-dasharray: 3 3; vector-effect: non-scaling-stroke;
}
.projecao__linha {
  fill: none; stroke: var(--accent); stroke-width: 2;
  stroke-linejoin: round; stroke-linecap: round;
  vector-effect: non-scaling-stroke;
}

/**
 * A FAIXA ABAIXO DO ZERO, no lugar da linha inteira colorida.
 *
 * `.projecao__linha--risco` pintava a curva TODA de laranja quando havia
 * qualquer dia negativo — inclusive os trechos em que o saldo estava saudavel.
 * Laranja em tudo e o mesmo que laranja em nada: o alarme perdia o lugar.
 *
 * Agora quem alarma e a REGIAO: o territorio abaixo do zero fica tingido, e a
 * linha entrando nele diz sozinha o que aconteceu.
 */
.projecao__abaixo { fill: var(--neg); opacity: .07; }

/* O dia da virada, marcado no DESENHO. Antes ele era anunciado so no texto
   acima do grafico — o leitor tinha de achar no desenho por conta. */
.projecao__virada {
  stroke: var(--neg); stroke-width: 1.5;
  stroke-dasharray: 2 2; vector-effect: non-scaling-stroke;
}

/* Rotulos de data na linha 2 da grade, posicionados por `left` em % sobre a
   MESMA largura do desenho. `translateX(-50%)` centra cada um no seu dia; o
   primeiro e o ultimo sao empurrados para dentro para nao vazarem. */
.projecao__datas {
  grid-area: 2 / 2;
  position: relative; height: 1.2rem; margin-top: var(--s-1);
  font-size: var(--fs-xs); color: var(--ink-3);
}
.projecao__datas span {
  position: absolute; transform: translateX(-50%);
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}
.projecao__datas span:first-child { transform: none; }
.projecao__datas span:last-child  { transform: translateX(-100%); }
.projecao__extremos {
  display: flex; justify-content: space-between;
  margin-top: var(--s-3);
}
.projecao__extremos span span { display: block; }


/* =====================================================================
   26. Mes a mes — um painel por serie, na Visao geral
   =====================================================================
   Series com a MESMA escala: altura igual significa valor igual em qualquer um
   dos graficos. Quem calcula os extremos e SerieMensalDeCaixa; aqui so ha
   desenho.

   Eles ja moraram todos dentro de um painel so, quatro lado a lado, e cada um
   ficava com ~307px numa tela de 1400: doze barras viravam tracos de 15px.
   Cada painel agora ocupa DUAS colunas da grade — o dobro da largura. */

/* Grade PROPRIA dos graficos, ocupando a largura inteira da .panel-grid.
   Sem borda, sem fundo, sem titulo: os quatro paineis continuam quatro cartoes
   separados. Ela existe so para o alinhamento.

   Deixa-los soltos na .panel-grid ocupando duas colunas cada so funciona com
   um numero PAR de colunas. Numa tela de 1280 a grade tem tres, e um painel de
   duas deixa sempre uma sobrando — uma escada de buracos entre os cartoes.

   E duas colunas so a partir de 1150px, nao antes: abaixo disso um painel de
   largura inteira da MAIS espaco a cada rotulo de mes do que meia largura
   daria. Doze rotulos dividindo 500px ficam com 30px cada, que e exatamente o
   que "set/25" mede. */
/* O BLOCO e o conjunto; a grade e o arranjo interno dele.
   Sao dois elementos porque o bloco precisa de uma faixa com a pega de
   arrastar acima dos graficos — e a faixa nao pode virar item da grade
   interna, senao ocuparia uma celula e desalinharia o 2x2. */
.graficos-bloco { grid-column: 1 / -1; }

.graficos-faixa {
  display: flex; align-items: center; gap: var(--s-1);
  margin-bottom: var(--s-2);
}
.graficos-faixa__titulo {
  font-size: var(--fs-sm); font-weight: 600; color: var(--ink-3);
  letter-spacing: 0.02em; text-transform: uppercase;
}

.graficos-grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--s-5);
  align-items: start;
}
@media (min-width: 1150px) {
  .graficos-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

.panel--grafico {
  --barra: var(--accent);
  --linha: var(--ink-2);
  min-width: 0;   /* sem isto o SVG impede a coluna de encolher no celular */
}

.g--pos   { --barra: var(--pos-dot); }
.g--neg   { --barra: var(--neg-dot); }
.g--info  { --barra: var(--info-dot); }
.g--sinal { --barra: var(--pos-dot); }

.panel--grafico .panel__sub { margin-left: var(--s-1); }
.grafico__seletor { min-width: 0; }

/* O total ganha corpo de saldo: e o numero que a pessoa veio ler. */
.grafico__numeros {
  display: flex; align-items: baseline; gap: var(--s-2);
  flex-wrap: wrap; margin-bottom: var(--s-3);
}
.grafico__total {
  font-size: var(--fs-2xl); font-weight: 600;
  font-variant-numeric: tabular-nums; letter-spacing: -0.015em;
}
.grafico__periodo { margin-right: auto; }
.grafico__tendencia {
  white-space: nowrap; font-size: var(--fs-md); font-weight: 600;
  font-variant-numeric: tabular-nums;
}
.grafico__tendencia .tiny { font-weight: 400; }

/* preserveAspectRatio="none" estica o desenho na largura — o que se quer de
   barras. As LINHAS levam vector-effect no markup, senao a espessura esticaria
   junto e sairia grossa de um lado e fina do outro. */
.grafico__desenho { display: block; width: 100%; height: 9rem; grid-area: 1 / 2; }

.grafico__nota { margin: 0; line-height: 1.5; }

/* A calha do eixo e o desenho na MESMA linha da grade: as duas colunas ficam
   com a mesma altura, e e isso que faz `top: N%` no rotulo cair exatamente
   sobre a linha que o SVG desenhou na coordenada N. Os rotulos dos meses vao
   para a linha de baixo, alinhados so com o desenho. */
.grafico__area {
  display: grid;
  grid-template-columns: var(--eixo, 3ch) minmax(0, 1fr);
  column-gap: var(--s-2);
}
.grafico__eixo { position: relative; grid-area: 1 / 1; }

/* Numero absoluto nao empurra largura nenhuma, entao uma coluna `auto`
   colapsaria e os rotulos cairiam por cima do desenho. Quem dimensiona a
   coluna e o `--eixo` em `ch`, contado do maior rotulo no PHP. */
.grafico__eixo span {
  position: absolute; right: 0;
  transform: translateY(-50%);
  white-space: nowrap;
  font-size: var(--fs-xs); color: var(--ink-3);
  font-variant-numeric: tabular-nums;
}

/* Referencia, nao conteudo: bem apagada, atras das barras. */
.grafico__marca { stroke: var(--border); stroke-width: 1; }

/* O zero nao e uma referencia entre outras — e a fronteira entre sobrar e
   faltar. Com a mesma espessura das demais, um mes negativo cruza uma linha
   qualquer. */
.grafico__marca--zero { stroke: var(--border-strong); stroke-width: 1.5; }

.grafico__barra { fill: var(--barra); }

/* No grafico de Resultado o sinal manda: mes no vermelho e vermelho, mesmo que
   a serie inteira seja "boa". Na Evolucao as duas classes convivem no mesmo
   desenho — verde e entrada, vermelho e saida — e e o mesmo par de cores que o
   resto do sistema usa para dinheiro que entra e dinheiro que sai. */
.grafico__barra--neg { fill: var(--neg-dot); }
.grafico__barra--pos { fill: var(--pos-dot); }

/* =====================================================================
   Legenda de series
   =====================================================================
   Um grafico de UMA serie nao precisa dela: o titulo do painel diz o que a
   barra e. Dois valores no mesmo desenho, sim — verde e vermelho parecem
   obvios para quem escreveu o grafico, e na tela sao duas barras sem nome. O
   `title` do SVG nao serve de legenda: so aparece para quem tem mouse e
   adivinha que ha algo a descobrir ali. */
.grafico__legenda {
  display: flex; flex-wrap: wrap; gap: var(--s-3);
  font-size: var(--fs-sm); color: var(--ink-2);
  margin-bottom: var(--s-2);
}
.grafico__legenda > span { display: inline-flex; align-items: center; gap: var(--s-1); }

.grafico__chave {
  width: .7rem; height: .7rem; border-radius: 2px; flex: none;
}
.grafico__chave--pos { background: var(--pos-dot); }
.grafico__chave--neg { background: var(--neg-dot); }

/* Mes que ainda nao chegou: so tem o que a recorrencia ja criou, e barra baixa
   ali nao significa mes fraco. Fica esmaecido e fora da tendencia. */
.grafico__barra--futuro { opacity: .32; }

/* Tracejada de proposito: e reta derivada, nao dado medido. */
.grafico__tendencia-linha {
  fill: none; stroke: var(--linha); stroke-width: 1.5;
  stroke-dasharray: 5 4; stroke-linecap: round; opacity: .85;
}

.grafico__rotulos {
  grid-area: 2 / 2;
  display: grid;
  grid-template-columns: repeat(var(--colunas), minmax(0, 1fr));
  font-size: var(--fs-sm); color: var(--ink-3);
  text-align: center; margin-top: var(--s-1);
}
.grafico__rotulos > span { overflow: hidden; text-overflow: ellipsis; }

/* O ano so aparece onde a coluna comporta.
   Doze rotulos dividem a largura do painel; com o grafico estreito, "/26" e o
   que empurra um rotulo sobre o outro. Esconder por CSS deixa a decisao onde a
   largura e conhecida — o PHP nao tem como adivinhar tamanho de tela. */
.grafico__ano { opacity: .7; }
@media (max-width: 480px) { .grafico__ano { display: none; } }

/* Select compacto, para caber no cabecalho de um grafico.
   `min-height` tambem, e nao so `height`: a regra base de .input/.select fixa
   min-height 36px, que venceria a altura sozinha e deixaria o campo do mesmo
   tamanho de sempre. */
.select--sm {
  min-height: 1.9rem; height: 1.9rem;
  padding-block: 0; padding-inline: var(--s-2) var(--s-6);
  font-size: var(--fs-sm); background-position: right var(--s-2) center;
}


/* =====================================================================
   27. Reposicionar os blocos da Visao geral
   =====================================================================
   Arrastar pela PEGA, e nao pelo cartao inteiro: os blocos tem links,
   caixas de marcar e linhas clicaveis dentro, e um arrasto disparado em
   cima deles engoliria o clique. */

.pega {
  flex: none;
  display: inline-flex; align-items: center; justify-content: center;
  width: 1.5rem; height: 1.5rem; margin-left: calc(var(--s-2) * -1);
  padding: 0; border: 0; border-radius: var(--r-sm);
  background: none; color: var(--ink-3);
  cursor: grab;

  /* Apagada, mas NAO invisivel.
     Com `opacity: 0` a pega so existia depois de passar o mouse por cima, e
     uma funcao que a pessoa precisa descobrir por acidente e uma funcao que
     nao existe. Um tracinho de fundo em cada cartao ensina que os cartoes se
     movem, sem competir com o titulo. */
  opacity: .35;
  transition: opacity var(--dur-fast) var(--ease), background var(--dur-fast) var(--ease);

  /* Sem isto, o gesto no celular rola a pagina em vez de mover o bloco. */
  touch-action: none;
}

/* `:focus`, e nao `:focus-visible`.
   O `:focus-visible` so casa quando o navegador julga que aquele foco merece
   anel — uma heuristica que muda entre navegadores e que precisa acertar o
   caso do foco devolvido por `element.focus()`, que e o que acontece depois de
   mover o bloco com as setas. Aqui o que esta em jogo e apenas revelar um
   icone: mostra-lo tambem num foco de mouse nao custa nada, e `:focus`
   dispensa a heuristica inteira. */
.panel:hover > .panel__head > .pega,
.graficos-bloco:hover > .graficos-faixa > .pega,
.pega:focus { opacity: 1; }

.pega:hover { background: var(--surface-3); color: var(--ink); }
.pega:active { cursor: grabbing; }

/* Toque nao tem hover: ali a pega precisa ficar mais presente. */
@media (hover: none) { .pega { opacity: .6; } }

/* O bloco em transito segue o ponteiro. `position: fixed` o tira do fluxo,
   entao a largura vai travada em pixels pelo JS — sem isso ele encolheria
   para o conteudo no instante em que sai da grade. */
.arrastando {
  position: fixed !important;
  /* Era `60` cru, abaixo de --z-sticky (100): o bloco que voce esta segurando
     passava POR BAIXO da topbar ao subir. Diálogos continuam por cima sem
     disputa — `dialog[open]` vive na top layer, imune a z-index. */
  z-index: var(--z-modal);
  margin: 0 !important;
  pointer-events: none;
  /* `--sombra-lg` nao existe: o token e `--shadow-lg`, em ingles como todos os
     outros. O que sempre renderizou foi o fallback. */
  box-shadow: var(--shadow-lg);
  opacity: .95;
  transform: rotate(.4deg);
  cursor: grabbing;
}

/* O buraco que o bloco deixou, mostrando onde ele vai cair. */
.bloco-fantasma {
  border: 1.5px dashed var(--accent-border);
  border-radius: var(--r-lg);
  background: var(--accent-soft);
}

/* Enquanto arrasta, nada de selecionar texto por acidente no caminho. */
.arrastando-ativo { user-select: none; }

.ordem-reset { margin-top: var(--s-5); text-align: center; }

/* Impressao e leitura: a pega nao e conteudo. */
@media print { .pega, .ordem-reset { display: none !important; } }
