/*
 * Numo — Sistema de Design
 * Rails 8 + Propshaft, CSS puro, sem build step, sem Tailwind.
 * Fonte da verdade dos tokens e componentes: DESIGN.md (raiz do repo).
 * Mobile-first, PWA, dark mode via toggle manual (data-theme, default claro).
 */

/* ===== Reset enxuto ===== */

*,
*::before,
*::after {
  box-sizing: border-box;
}

* {
  margin: 0;
}

html {
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
  color-scheme: light;
}

body {
  min-height: 100vh;
  min-height: 100dvh;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

img,
picture,
svg {
  display: block;
  max-width: 100%;
}

button,
input,
select,
textarea {
  font: inherit;
  color: inherit;
}

button {
  cursor: pointer;
  background: none;
  border: none;
}

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

ul,
ol {
  list-style: none;
  padding: 0;
}

table {
  border-collapse: collapse;
}

/* ===== Tokens ===== */

:root {
  /* Cor — base neutra */
  --color-ink: #141414; --color-ink-soft: #2b2b2b; --color-body: #52525b; --color-muted: #71717a;
  --color-hairline: #e5e5e5; --color-hairline-strong: #d4d4d8;
  --color-canvas: #ffffff; --color-surface: #f7f7f7; --color-surface-2: #efefef; --color-surface-pressed: #e4e4e7;
  --color-on-ink: #ffffff;

  /* Cor — marca "Marinho": hero, chips ativos, botões primários (--cor-marca do spec). */
  --color-brand: #1e3a6e; --color-brand-hover: #17305c; --color-brand-active: #11254a;
  --color-brand-subtle-bg: #e8eefb; --color-on-brand: #ffffff;

  /* Cor — acento: links, ações secundárias. Mesma matiz do marinho, mais claro (--cor-acento do spec). */
  --color-accent: #3563b8; --color-accent-hover: #2b53a0; --color-accent-active: #1f4489;
  --color-accent-subtle-bg: #e8eefb; --color-on-accent: #ffffff;

  /* Cor — status financeiro (fg = texto/ícone, bg = badge) */
  --color-status-pendente-fg: #b45309; --color-status-pendente-bg: #fef3e2;
  --color-status-baixado-fg: #047857; --color-status-baixado-bg: #e7f6ef;
  --color-status-estornado-fg: #57534e; --color-status-estornado-bg: #f1f0ee;
  --color-status-comprometido-fg: #1d4ed8; --color-status-comprometido-bg: #e9effd;
  /* Neutro: arquivado/inativo. Não reusa --color-status-estornado-* — semânticas diferentes
     (estornado é "anulado", arquivado é só "fora de uso"). */
  --color-status-neutro-fg: #6b7280; --color-status-neutro-bg: #eef0f3;

  /* Cor — valor monetário */
  --color-value-despesa: var(--color-ink); --color-value-receita: #047857; --color-value-negativo: #b91c1c;

  /* Divisor fino entre itens dentro do mesmo card (ex.: linhas do _card_dia) — mais discreto
     que --color-hairline, que separa blocos maiores. */
  --color-divisor: #eef0f3;

  /* Tipografia */
  --font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  --text-display: 2rem;      /* 32px — valor total no card de métrica */
  --text-title: 1.25rem;     /* 20px — saldo em destaque, título de seção */
  --text-value-lg: 1.5rem;   /* 24px — campo de valor em foco no form */
  --text-value-md: 1rem;     /* 16px — valor na linha de lançamento */
  --text-body: 0.9375rem;    /* 15px — texto corrido, labels de input */
  --text-label: 0.8125rem;   /* 13px — legendas, metadados */
  --text-caption: 0.75rem;   /* 12px — timestamp, texto auxiliar mínimo */
  --leading-tight: 1.15; --leading-base: 1.4; --leading-loose: 1.6;
  --weight-regular: 400; --weight-medium: 500; --weight-bold: 700;

  /* Espaçamento — base 4px */
  --space-1: 0.25rem; --space-2: 0.5rem; --space-3: 0.75rem; --space-4: 1rem;
  --space-5: 1.25rem; --space-6: 1.5rem; --space-8: 2rem; --space-10: 2.5rem; --space-12: 3rem;

  /* Raios */
  --radius-sm: 8px; --radius-md: 12px; --radius-lg: 16px; --radius-full: 999px;
  /* Raio dos componentes do redesign (card-dia, item-extrato, chips) — --raio do spec. */
  --radius-card: 14px;

  /* Sombra — elevação única, sem tiers múltiplos */
  --shadow-card: 0 1px 2px rgba(20,20,20,.04), 0 2px 8px rgba(20,20,20,.06);
  --shadow-nav: 0 -1px 0 rgba(20,20,20,.06), 0 -4px 12px rgba(20,20,20,.05);

  /* Altura visual da bottom-nav flutuante e a folga entre ela e a borda inferior.
     --bottom-nav-espaco é o footprint total (altura + gap): tudo que reserva espaço no
     rodapé (conteúdo, rodapé sticky do form) usa o espaço, não só a altura. */
  --bottom-nav-altura: 56px;
  --bottom-nav-gap: 16px;
  --bottom-nav-espaco: calc(var(--bottom-nav-altura) + var(--bottom-nav-gap));
  --shadow-modal: 0 8px 24px rgba(20,20,20,.16);

  /* Transição */
  --duration-fast: 120ms; --duration-base: 200ms; --duration-slow: 320ms;
  --ease-standard: cubic-bezier(.2, 0, 0, 1);

  /* Alvo de toque mínimo */
  --tap-min: 44px;
}

/* ===== Dark mode ===== */
/* Default é claro. Escuro entra por toggle manual (data-theme="dark" no <html>,
   persistido em localStorage por tema_controller). Não segue mais o SO. */

:root[data-theme="dark"] {
    color-scheme: dark;
    --color-ink: #f5f5f4; --color-ink-soft: #d4d4d4; --color-body: #b4b4b8; --color-muted: #8a8a93;
    --color-hairline: #2a2a2a; --color-hairline-strong: #383838;
    /* Superfície com leve tinta marinho, não cinza puro: o app carrega a matiz da marca
       também no escuro em vez de virar um app genérico "dark theme". */
    --color-canvas: #11151f; --color-surface: #171c29; --color-surface-2: #1e2434; --color-surface-pressed: #262d40;
    --color-on-ink: #121212;
    --color-divisor: #232a3a;

    /* Marca e acento viram a mesma matiz em versão neon: sobre fundo escuro, o marinho
       sólido (#1e3a6e) não tem contraste suficiente para texto/ícone — precisa clarear.
       Mantém uso de --color-brand para fundos sólidos (chip ativo, botão primário, hero)
       e --color-accent para texto/link, os dois já claros o bastante (4.5:1 texto / 3:1 UI). */
    --color-brand: #3f6bb8; --color-brand-hover: #4d78c2; --color-brand-active: #2f5296;
    --color-brand-subtle-bg: #1a2740; --color-on-brand: #f5f8ff;

    --color-accent: #6ea8ff; --color-accent-hover: #8ab8ff; --color-accent-active: #588fe0;
    --color-accent-subtle-bg: #1a2740; --color-on-accent: #0b1220;

    --color-status-pendente-fg: #fbbf24; --color-status-pendente-bg: #3a2a0f;
    --color-status-baixado-fg: #34d399; --color-status-baixado-bg: #0f2c22;
    --color-status-estornado-fg: #a8a29e; --color-status-estornado-bg: #262523;
    --color-status-comprometido-fg: #60a5fa; --color-status-comprometido-bg: #10203d;
    --color-status-neutro-fg: #9ca3af; --color-status-neutro-bg: #232a3a;

    --color-value-receita: #34d399; --color-value-negativo: #f87171;

    --shadow-card: 0 1px 2px rgba(0,0,0,.4), 0 2px 8px rgba(0,0,0,.3);
    --shadow-nav: 0 -1px 0 rgba(0,0,0,.4), 0 -4px 16px rgba(0,0,0,.35);
    --shadow-modal: 0 8px 32px rgba(0,0,0,.5);
}

/* ===== Base ===== */

html {
  font-family: var(--font-sans);
}

body {
  background: var(--color-surface);
  color: var(--color-ink);
  font-size: var(--text-body);
  line-height: var(--leading-base);
  font-weight: var(--weight-regular);
}

h1, h2, h3 {
  font-weight: var(--weight-bold);
  line-height: var(--leading-tight);
  color: var(--color-ink);
}

/* Título de seção: menor que o padrão do navegador e com ritmo vertical próprio —
   é ele que separa os blocos de uma tela, não caixas coloridas. */
h2 {
  font-size: var(--text-title);
  margin: var(--space-8) 0 var(--space-3);
}

/* Dentro de .form (flex column) margem não colapsa: ela SOMA ao gap e o título de seção
   do formulário ganha o dobro do ritmo. O gap já dá o espaçamento ali. */
.form > h2 {
  margin: 0;
}

a {
  color: var(--color-accent);
}

/* ===== Acessibilidade (não-negociável) ===== */

/* Foco visível: nunca outline: none sem substituto. */
:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 2px;
}

/* Alvo de toque mínimo em controles interativos. */
button,
.btn,
input:not([type="checkbox"]):not([type="radio"]),
select,
textarea {
  min-height: var(--tap-min);
}

/* Movimento respeita preferência do usuário. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    transition-duration: .01ms !important;
    animation-duration: .01ms !important;
    scroll-behavior: auto !important;
  }

  /* ::view-transition-* vive numa árvore de pseudo-elementos própria, fora do DOM normal
     -- o `*` acima não alcança. Sem isto, o cross-fade do onboarding (Turbo 8 View
     Transitions) ignoraria a preferência do usuário. */
  ::view-transition-group(*), ::view-transition-old(*), ::view-transition-new(*) {
    animation-duration: .01ms !important;
  }
}

/* ===== Layout ===== */

.page {
  max-width: 480px;
  margin-inline: auto;
  padding: var(--space-4);
  /* A bottom-nav é fixa: sem esta folga ela cobre o último elemento da página — o botão
     de salvar de qualquer formulário fica intocável no celular. Inclui a barra home do
     iPhone (safe-area) além da altura da própria nav. */
  padding-bottom: calc(var(--bottom-nav-espaco) + var(--space-6) + env(safe-area-inset-bottom, 0px));
  min-height: 100vh;
  min-height: 100dvh;
}

.page__header {
  margin-bottom: var(--space-6);
}

/* Título grande à la iOS: a tela se apresenta sem depender de barra de topo. */
.page__header h1 {
  font-size: 1.75rem;
  letter-spacing: -0.02em;
  /* O h1 de aceitar convite interpola o nome da empresa, que não tem limite de tamanho.
     Sem isto, um nome de palavra única com ~27 caracteres estoura os 390px e a página
     inteira rola na horizontal — mesmo motivo do valor no hero do dashboard. */
  overflow-wrap: anywhere;
}

/* Linha de contexto acima do título (empresa, marca): pequena, nunca compete com o h1. */
.page__header__contexto {
  font-size: var(--text-label);
  font-weight: var(--weight-medium);
  color: var(--color-muted);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  margin-bottom: var(--space-1);
}

/* ===== Flash ===== */

.flash {
  padding: var(--space-3) var(--space-4);
  border-radius: var(--radius-md);
  font-size: var(--text-body);
  font-weight: var(--weight-medium);
  margin-bottom: var(--space-4);
}

.flash--notice {
  background: var(--color-status-baixado-bg);
  color: var(--color-status-baixado-fg);
}

.flash--alert {
  background: color-mix(in srgb, var(--color-value-negativo) 12%, var(--color-canvas));
  color: var(--color-value-negativo);
}

/* ===== Dialog de aviso ===== */
.form {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}

.field {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

/* `.field` empata em especificidade com a regra `[hidden]` do UA stylesheet e vence por
   vir depois — sem isto, o atributo `hidden` que o preview_controller usa para sumir com
   "1ª parcela em" perderia a batalha e o campo continuaria visível. */
.field[hidden] {
  display: none;
}

.field label {
  font-size: var(--text-body);
  font-weight: var(--weight-medium);
  color: var(--color-ink-soft);
}

/* 16px mínimo: abaixo disso o Safari iOS dá zoom automático ao focar. */
.field input,
.field select,
.field textarea {
  font-size: 1rem;
  padding: var(--space-3);
  border: 1px solid var(--color-hairline-strong);
  border-radius: var(--radius-md);
  background: var(--color-canvas);
  color: var(--color-ink);
  width: 100%;
}

.field input::placeholder,
.field textarea::placeholder {
  color: var(--color-muted);
}

/* Radio e checkbox são glifos nativos, não caixas de texto: a regra acima os esticava para
   a largura do campo e a bolinha aparecia centralizada, longe do próprio rótulo — era o
   que acontecia com "No cartão" na tela de editar. Reset em regra própria, e não com
   :not() na regra de cima, porque o :not() subiria a especificidade dela a ponto de
   derrubar o estilo do campo de valor. */
.field input[type="radio"],
.field input[type="checkbox"] {
  width: auto;
  padding: 0;
  border: none;
  border-radius: 0;
  background: none;
}

/* ===== Botões ===== */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: var(--space-3) var(--space-5);
  border-radius: var(--radius-md);
  border: 1px solid transparent;
  font-size: var(--text-body);
  font-weight: var(--weight-medium);
  /* Rótulo de botão nunca quebra em duas linhas: quebrado, deixa de parecer botão. */
  white-space: nowrap;
  transition: background var(--duration-fast) var(--ease-standard),
              border-color var(--duration-fast) var(--ease-standard),
              color var(--duration-fast) var(--ease-standard);
}

.btn--primary {
  width: 100%;
  background: var(--color-brand);
  border-color: var(--color-brand);
  color: var(--color-on-brand);
}

.btn--primary:hover {
  background: var(--color-brand-hover);
  border-color: var(--color-brand-hover);
}

.btn--primary:active {
  background: var(--color-brand-active);
  border-color: var(--color-brand-active);
}

.btn--ghost {
  background: transparent;
  border-color: var(--color-hairline-strong);
  color: var(--color-body);
}

.btn--ghost:hover {
  background: var(--color-surface-2);
  color: var(--color-ink);
}

.btn--ghost:active {
  background: var(--color-surface-pressed);
}

/* Ação dentro de linha de lista: compacta, sem ocupar a largura do card. */
.btn--sm {
  width: auto;
  padding: var(--space-2) var(--space-4);
  font-size: var(--text-label);
}

/* Sair, excluir: vermelho de texto basta — fundo vermelho gritaria numa tela de rotina.
   Os estados vêm junto porque .btn--ghost:hover é mais específico e devolvia a cor normal
   bem na hora em que o cursor está sobre o botão, que é quando o aviso mais importa. */
.btn--destrutivo,
.btn--destrutivo:hover,
.btn--destrutivo:active {
  color: var(--color-value-negativo);
}

/* ===== Utilitários ===== */

.muted {
  color: var(--color-muted);
  font-size: var(--text-body);
}

/* ===== Tipografia de valores monetários ===== */
/* Números tabulares evitam o "pulo" de largura em update via Turbo Stream. */

.valor-monetario {
  font-variant-numeric: tabular-nums lining-nums;
  font-weight: var(--weight-medium);
  letter-spacing: -0.01em;
  white-space: nowrap;
}

.valor-monetario--pendente {
  color: var(--color-status-pendente-fg);
}

/* ===== Card de métrica ===== */
/* ===== Linha de lançamento em lista ===== */
/* Lista densa dentro de um único card; divisórias hairline, não card-por-linha. */

.lista-lancamentos {
  background: var(--color-canvas);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-card);
  overflow: hidden;
}

.linha-lancamento {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  min-height: var(--tap-min);
  padding: var(--space-3) var(--space-4);
  border-bottom: 1px solid var(--color-hairline);
}

.linha-lancamento:last-child {
  border-bottom: none;
}

.linha-lancamento__valor {
  margin-left: auto;
}

.form-acerto-parcial {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin-top: var(--space-1);
}

.form-acerto-parcial__valor {
  width: 6.5rem;
  font-size: var(--text-caption);
  padding: var(--space-1) var(--space-2);
}

/* ===== Form de lançamento ===== */
/* Caminho de "menos de 1 minuto": valor grande, categoria em chips, salvar de largura total. */

/* Prefixado com .field para vencer `.field input`, que devolvia a caixa com borda e
   anulava o estilo de sublinhado deste campo. */
.field .form-lancamento__valor-input {
  font-size: var(--text-value-lg);
  font-variant-numeric: tabular-nums;
  font-weight: var(--weight-bold);
  border: none;
  border-bottom: 2px solid var(--color-hairline-strong);
  border-radius: 0;
  padding: var(--space-2) 0;
  background: transparent;
  width: 100%;
}

/* O foco vive no próprio sublinhado — outline em volta viraria anel duplo. */
.field .form-lancamento__valor-input:focus-visible {
  outline: none;
  border-color: var(--color-accent);
}

.chips-categoria {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
}

/* Chip de seleção única (categoria e forma de pagamento): mesmo componente visual,
   um radio nativo escondido dentro do label. */
.chip-categoria,
.chip-pagamento {
  display: inline-flex;
  align-items: center;
  min-height: var(--tap-min);
  padding: var(--space-2) var(--space-4);
  border-radius: var(--radius-full);
  border: 1px solid var(--color-hairline-strong);
  background: var(--color-canvas);
  font-size: var(--text-body);
  color: var(--color-ink);
  cursor: pointer;
  transition: background var(--duration-fast) var(--ease-standard);
}

/* O estado vem do radio nativo escondido dentro do label: teclado, leitor de tela e
   validação `required` continuam sendo os do navegador, sem JavaScript de estado. */
.chip-categoria:has(input:checked),
.chip-pagamento:has(input:checked) {
  background: var(--color-brand);
  border-color: var(--color-brand);
  color: var(--color-on-brand);
}

.chip-categoria:has(input:focus-visible),
.chip-pagamento:has(input:focus-visible) {
  outline: 2px solid var(--color-accent);
  outline-offset: 2px;
}

/* ===== Bottom-nav ===== */
/* Pill flutuante: 2 destinos (Relatórios / Você) + FAB central. Total ímpar = simétrico.
   Descolada das bordas para respirar; conteúdo reserva --bottom-nav-espaco no rodapé. */

.bottom-nav {
  position: fixed;
  left: var(--bottom-nav-gap);
  right: var(--bottom-nav-gap);
  bottom: calc(var(--bottom-nav-gap) + env(safe-area-inset-bottom, 0px));
  display: flex;
  background: var(--color-canvas);
  box-shadow: var(--shadow-modal);
  border-radius: var(--radius-full);
  padding: var(--space-1);
}

.bottom-nav__item {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-1);
  min-height: var(--bottom-nav-altura);
  justify-content: center;
  color: var(--color-muted);
  font-size: var(--text-caption);
}

.bottom-nav__item[aria-current="page"] {
  color: var(--color-accent);
  font-weight: var(--weight-medium);
}

/* ===== Segmented (sub-seções de Relatórios: Resumo / Lançamentos) ===== */
/* Toggle entre dashboard e extrato. Substitui a antiga aba "Lançamentos" da bottom-nav. */

.segmented {
  display: flex;
  gap: var(--space-1);
  background: var(--color-surface);
  padding: var(--space-1);
  border-radius: var(--radius-full);
  margin-bottom: var(--space-4);
}

.segmented__item {
  flex: 1;
  text-align: center;
  padding: var(--space-2);
  border-radius: var(--radius-full);
  font-size: var(--text-caption);
  font-weight: var(--weight-medium);
  color: var(--color-muted);
}

.segmented__item[aria-current="page"] {
  background: var(--color-canvas);
  color: var(--color-accent);
  box-shadow: var(--shadow-card);
}

/* ===== Badge de status ===== */
/* Rótulo sempre por extenso — cor nunca é o único sinal. */

.badge-status {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  padding: var(--space-1) var(--space-2);
  border-radius: var(--radius-full);
  font-size: var(--text-caption);
  font-weight: var(--weight-medium);
}

.badge-status--pendente {
  background: var(--color-status-pendente-bg);
  color: var(--color-status-pendente-fg);
}

.badge-status--baixado {
  background: var(--color-status-baixado-bg);
  color: var(--color-status-baixado-fg);
}

.badge-status--parcial {
  background: var(--color-status-pendente-bg);
  color: var(--color-status-pendente-fg);
}

.badge-status--estornado {
  background: var(--color-status-estornado-bg);
  color: var(--color-status-estornado-fg);
}

.badge-status--comprometido {
  background: var(--color-status-comprometido-bg);
  color: var(--color-status-comprometido-fg);
}

/* ===== Tabela de relatório com scroll horizontal próprio ===== */
/* A tabela nunca estoura a página — scroll contido no próprio wrapper. */

.tabela-relatorio-wrap {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  border-radius: var(--radius-lg);
  border: 1px solid var(--color-hairline);
  background: var(--color-canvas);
}

.tabela-relatorio {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--text-body);
}

.tabela-relatorio caption {
  text-align: left;
  padding: var(--space-3) var(--space-4) 0;
  font-size: var(--text-label);
}

.tabela-relatorio thead th {
  position: sticky;
  top: 0;
  background: var(--color-surface);
  text-align: left;
  padding: var(--space-3) var(--space-4);
  font-size: var(--text-label);
  color: var(--color-muted);
  font-weight: var(--weight-medium);
  border-bottom: 1px solid var(--color-hairline);
}

.tabela-relatorio td {
  padding: var(--space-3) var(--space-4);
  border-bottom: 1px solid var(--color-hairline);
}

/* th de linha herda do navegador centro + negrito; aqui é rótulo, não destaque. */
.tabela-relatorio tbody th {
  padding: var(--space-3) var(--space-4);
  border-bottom: 1px solid var(--color-hairline);
  text-align: left;
  font-weight: var(--weight-regular);
}

.tabela-relatorio tfoot th {
  padding: var(--space-3) var(--space-4);
  text-align: left;
}

/* Alinhado à direita e sem quebra: sem o nowrap, "R$ 340,00" partia em duas linhas na
   tabela de parcelas da tela de editar, que tem quatro colunas em 390px. */
.tabela-relatorio .col-valor {
  text-align: right;
  white-space: nowrap;
}

/* ===== Empty state ===== */
/* Sem ilustração pesada — ícone simples, uma frase, uma ação. */

.empty-state {
  /* U16 — um eixo só por tela: o vazio partilha o eixo esquerdo dos campos e das listas,
     em vez de misturar centralizado com alinhado à esquerda na mesma tela. */
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-3);
  padding: var(--space-10) var(--space-4);
  color: var(--color-muted);
}

.empty-state__titulo {
  font-size: var(--text-title);
  color: var(--color-ink);
  font-weight: var(--weight-medium);
}

/* ============================================================
   Utilitários e componentes de relatório
   ============================================================ */

/* Rótulo que só o leitor de tela precisa (campos cujo contexto já é visual).
   !important porque a função inteira deste utilitário é vencer: `.field input` é mais
   específico e estava dando 375px de largura aos radios escondidos dos chips, empurrando
   a tela de lançar para 537px num viewport de 375 e criando rolagem horizontal. */
.visually-hidden {
  position: absolute;
  width: 1px !important;
  height: 1px !important;
  padding: 0 !important;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0 !important;
}

/* Formulário de uma linha só: renomear categoria, escolher mês. */
.form--inline {
  display: flex;
  /* Precisa vir depois de .form, que é coluna: sem isto o `flex: 1 1` do campo cresce no
     eixo vertical e o input vira uma caixa alta. */
  flex-direction: row;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2);
}

/* O rótulo ocupa a linha inteira; campo e botão dividem a linha de baixo. */
.form--inline label {
  flex: 1 0 100%;
  font-size: var(--text-label);
  color: var(--color-muted);
}

.form--inline input {
  flex: 1 1 10rem;
  min-width: 0;
}

.form--inline input[type="submit"],
.form--inline .btn {
  flex: 0 0 auto;
}

/* Stepper de mês: um controle só no lugar de form + input nativo + botões de atalho.
   O input type="month" nativo aparecia em inglês fora do celular e pedia três linhas de
   tela para uma escolha de um toque. */
.seletor-mes {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2);
  background: var(--color-canvas);
  border-radius: var(--radius-full);
  box-shadow: var(--shadow-card);
  padding: var(--space-1) var(--space-2);
  margin-bottom: var(--space-4);
}

/* Variante fixa no topo (extrato, dashboard): fundo marinho para se destacar do conteúdo
   que passa por baixo ao rolar. z-index acima do conteúdo normal mas abaixo de qualquer
   modal/toast. */
.seletor-mes--sticky {
  position: sticky;
  top: 0;
  z-index: 5;
  background: var(--color-brand);
  box-shadow: var(--shadow-nav);
}

.seletor-mes--sticky .seletor-mes__seta {
  color: var(--color-on-brand);
}

.seletor-mes--sticky .seletor-mes__seta:hover,
.seletor-mes--sticky .seletor-mes__seta:active {
  background: rgb(255 255 255 / .12);
}

.seletor-mes--sticky .seletor-mes__atual {
  color: var(--color-on-brand);
}

.seletor-mes__seta {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--tap-min);
  min-height: var(--tap-min);
  border-radius: var(--radius-full);
  color: var(--color-ink-soft);
  transition: background var(--duration-fast) var(--ease-standard);
}

.seletor-mes__seta:hover {
  background: var(--color-surface-2);
}

.seletor-mes__seta:active {
  background: var(--color-surface-pressed);
}

.seletor-mes__atual {
  font-size: var(--text-value-md);
  font-weight: var(--weight-bold);
  color: var(--color-ink);
}

/* Link discreto logo abaixo do stepper: voltar ao mês atual, limpar filtro.
   A folga de baixo é sempre a mesma do .seletor-mes sozinho — o link entra e sai da tela
   conforme o mês, e se ele somasse margem o conteúdo saltaria a cada toque na seta. */
.seletor-mes__acao {
  /* U16 — no extrato "Ver todos os meses" segue o eixo esquerdo do resto. No hero do
     dashboard (showcase centrado) o override abaixo mantém o centro. */
  text-align: left;
  font-size: var(--text-label);
  margin-top: calc(-1 * var(--space-2));
  margin-bottom: var(--space-2);
}

.hero .seletor-mes__acao {
  text-align: center;
}

/* O alvo tocável é do link, não do parágrafo: em caixa inline o min-height não vale. */
.seletor-mes__acao a {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: var(--tap-min);
  padding-inline: var(--space-3);
}

/* Chips de destino secundário no topo de uma lista (reembolsos, estornadas, filtro).
   Rolável na horizontal: numa tela de 375px o grupo de status (Todas/Pendentes/Baixadas/
   Estornadas) + drill-down removível não cabe sem quebrar linha, e quebrar linha empurra
   o conteúdo abaixo a cada filtro trocado. nowrap + overflow-x resolve sem JS. */
.chips-filtro {
  display: flex;
  flex-wrap: nowrap;
  gap: var(--space-2);
  margin-bottom: var(--space-5);
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
  /* <fieldset> tem min-width:min-content no UA stylesheet do Chrome/Firefox — sem zerar
     isso, o navegador ignora overflow-x:auto e estica o fieldset até caber todos os
     chips de uma vez, rolando a PÁGINA inteira na horizontal em vez do chip-bar. */
  min-width: 0;
}

.chips-filtro::-webkit-scrollbar {
  display: none;
}

/* Fade na margem direita só quando a faixa transborda (marcado pelo chips_scroll_controller):
   sem barra de rolagem, é o único sinal de que há mais chip fora da vista. U11/R10. */
.chips-filtro--rolavel {
  -webkit-mask-image: linear-gradient(to right, #000 88%, transparent);
  mask-image: linear-gradient(to right, #000 88%, transparent);
}

/* Borda em --color-muted e texto em acento, não hairline sobre canvas: a pílula precisa
   se ler como destino navegável. Hairline sobre canvas dá 1.5:1 de contorno e canvas
   sobre a superfície da página dá 1.04:1 — nos dois casos o chip some e vira texto solto,
   que é justamente o que os links laranja de antes NÃO faziam. */
.chip-filtro {
  /* position:relative é o que faz o <input class="visually-hidden"> (absolute) ficar
     preso dentro do label em vez de escapar pro viewport inteiro: sem isso, o input do
     ÚLTIMO chip de um fieldset com scroll interno se posiciona na coordenada estática
     que teria SEM o scroll — geralmente fora dos 375px da tela — e o navegador conta
     isso como overflow da PÁGINA, não do chip-bar (que já rola direitinho sozinho). */
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  min-height: var(--tap-min);
  padding: var(--space-1) var(--space-4);
  border: 1px solid var(--color-muted);
  border-radius: var(--radius-full);
  background: var(--color-canvas);
  color: var(--color-accent);
  font-size: var(--text-label);
  font-weight: var(--weight-medium);
  white-space: nowrap;
  flex: 0 0 auto;
  cursor: pointer;
  transition: background var(--duration-fast) var(--ease-standard),
              border-color var(--duration-fast) var(--ease-standard),
              color var(--duration-fast) var(--ease-standard);
}

.chip-filtro:active {
  background: var(--color-surface-pressed);
}

/* Chip como <input type="radio">/checkbox nativo dentro do <label>: estado ativo vem do
   próprio input (teclado, leitor de tela e submit de formulário grátis, sem JS de estado) —
   mesmo padrão de .chip-categoria. Fundo marinho comunica "ativo" sem depender só de cor
   (o texto do chip muda de cor junto, e o input mantém foco visível). */
.chip-filtro:has(input:checked) {
  background: var(--color-brand);
  border-color: var(--color-brand);
  color: var(--color-on-brand);
}

.chip-filtro:has(input:focus-visible) {
  outline: 2px solid var(--color-accent);
  outline-offset: 2px;
}

/* Stepper do onboarding: mesmo chip, mas navegação (link pra outra etapa, não seleção
   de filtro), então o estado ativo vem de aria-current em vez de input:checked. */
.chip-filtro[aria-current="step"] {
  background: var(--color-brand);
  border-color: var(--color-brand);
  color: var(--color-on-brand);
}

/* Barra proporcional da série mensal: CSS puro, sem biblioteca de gráfico —
   a leitura que importa é "qual mês pesa mais", e para isso largura basta. */
.barra {
  display: block;
  height: 0.6rem;
  width: var(--proporcao, 0%);
  min-width: 2px;
  border-radius: var(--radius-full);
  /* U12 — a barra usa a cor da categoria quando informada (mesma da bolinha e do ícone),
     e cai no marinho da marca onde não há categoria (ex.: gráfico de competências). */
  background: var(--cor-categoria, var(--color-brand));
}

.tabela-relatorio tfoot th,
.tabela-relatorio tfoot td {
  border-top: 2px solid var(--color-hairline-strong);
  font-weight: var(--weight-medium);
}

/* Desktop: a coluna de 480px é a medida certa para lançar no celular, mas espreme as
   tabelas de relatório. Alargar aqui atende os dois usos sem uma segunda folha. */
@media (min-width: 760px) {
  .page {
    max-width: 720px;
  }
}

/* ============================================================
   Formulário de lançamento: agrupamento e revelação progressiva
   ============================================================ */

/* Nove campos seguidos sem agrupamento viram uma parede. Três blocos curtos
   ("Quanto", "O que", "Como foi pago") deixam o formulário legível de relance. */
.grupo-campos {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
  border: none;
  padding: 0;
  margin-bottom: var(--space-8);
}

.grupo-campos__titulo {
  font-size: var(--text-label);
  font-weight: var(--weight-medium);
  color: var(--color-muted);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  padding: 0;
  margin-bottom: var(--space-1);
}

/* minmax(0, 1fr), não 1fr: a coluna de grid tem min-width auto por padrão, e o
   <input type="month"> nativo tem largura mínima intrínseca grande. Com 1fr puro, as duas
   colunas se recusavam a encolher e empurravam a página inteira para 537px num viewport
   de 375 — a tela mais usada do app rolando na horizontal. */
.dupla-campos {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: var(--space-3);
}

.dupla-campos input {
  min-width: 0;
  width: 100%;
}

.rotulo-grupo {
  font-size: var(--text-body);
  font-weight: var(--weight-medium);
  color: var(--color-ink-soft);
}

.opcao-pagamento {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  min-height: var(--tap-min);
  font-size: var(--text-body);
  color: var(--color-ink-soft);
  cursor: pointer;
}

/* O cartão só existe depois de escolher "No cartão". Antes disso ele era um campo que
   parecia opcional, ficava vazio, e o lançamento só falhava no fim. */
.campo-cartao {
  display: none;
  padding-left: var(--space-6);
}

.escolha-pagamento:has(#forma_cartao:checked) .campo-cartao {
  display: block;
}

/* Botão de largura total pode quebrar: "Acertar tudo com <apelido>" carrega a parte local
   do e-mail, que não tem limite de tamanho. Com nowrap ele vazaria a caixa e, em apelido
   longo, a página inteira rolaria na horizontal. */
.btn--bloco {
  width: 100%;
  white-space: normal;
}

/* Botão desabilitado precisa PARECER desabilitado: sem isto o usuário toca num botão de
   aparência normal e conclui que o app travou. */
.btn:disabled,
.btn[disabled],
input[type="submit"]:disabled {
  opacity: 0.45;
  cursor: not-allowed;
}

/* Linha de lançamento: o toque vale na linha inteira, não só no link "Editar", e a altura
   não muda por causa do badge. */
.linha-lancamento {
  position: relative;
  align-items: flex-start;
}

.linha-lancamento__valor {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: var(--space-1);
  margin-left: auto;
}

.linha-lancamento .link-linha::after {
  content: "";
  position: absolute;
  inset: 0;
}

/* Os botões reais da linha precisam ficar acima da área de toque estendida. */
.linha-lancamento form,
.linha-lancamento .btn:not(.link-linha) {
  position: relative;
  z-index: 1;
}

.linha-lancamento__info {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  align-items: flex-start;
  min-width: 0;
}

.linha-lancamento__acao {
  display: flex;
  align-items: center;
  color: var(--color-muted);
  min-height: var(--tap-min);
}

/* Lista de navegação das configurações: padrão de app (rótulo à esquerda, contagem e
   chevron à direita), no lugar de cards de estatística que ninguém consultava. */
.lista-config {
  background: var(--color-canvas);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-card);
  overflow: hidden;
  margin-bottom: var(--space-6);
}

.lista-config li + li {
  border-top: 1px solid var(--color-hairline);
}

.linha-config {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  min-height: 52px;
  padding: var(--space-3) var(--space-4);
  color: var(--color-ink);
  font-weight: var(--weight-medium);
  transition: background var(--duration-fast) var(--ease-standard);
}

.linha-config:active {
  background: var(--color-surface-pressed);
}

.linha-config__meta {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  color: var(--color-muted);
  font-weight: var(--weight-regular);
  font-variant-numeric: tabular-nums;
}

/* Ação em lote logo abaixo da lista do grupo (button_to embrulha num <form>). */
.lista-lancamentos + form {
  margin-top: var(--space-3);
}

/* Cabeçalho de grupo por pessoa nos reembolsos: nome e total na mesma linha. */
.grupo-reembolso__cabecalho {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-3);
  margin: var(--space-8) 0 var(--space-3);
}

.grupo-reembolso__cabecalho h2 {
  margin: 0;
}

/* Valor de compra estornada: riscado, sem cor de alerta — não é erro, é registro anulado. */
.valor-monetario--estornado {
  color: var(--color-status-estornado-fg);
  text-decoration: line-through;
  text-decoration-thickness: 1.5px;
}

/* Aviso colado no botão de salvar: quando ele trava, é para cá que a pessoa olha. */
.status-lancamento:empty {
  display: none;
}

/* Glossário curto na tela de configurações. Os três termos que o produto não consegue
   evitar, em <details> nativo: fechado por padrão, a tela deixa de ser um manual. */
.ajuda {
  margin: var(--space-8) 0;
}

.ajuda__item {
  border-bottom: 1px solid var(--color-hairline);
}

.ajuda__item summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  min-height: var(--tap-min);
  padding: var(--space-3) 0;
  font-weight: var(--weight-medium);
  color: var(--color-ink);
  cursor: pointer;
  list-style: none;
}

.ajuda__item summary::-webkit-details-marker {
  display: none;
}

.ajuda__item summary::after {
  content: "+";
  color: var(--color-muted);
  font-size: var(--text-title);
  line-height: 1;
}

.ajuda__item[open] summary::after {
  content: "–";
}

.ajuda__item p {
  color: var(--color-body);
  padding-bottom: var(--space-4);
}

/* O preview nasce vazio e só chega ~300ms depois. Sem altura reservada, ele empurra os
   campos seguintes para baixo bem na hora em que o dedo já está descendo para tocá-los. */
#preview_parcelas {
  display: block;
  min-height: 7rem;
}

/* A bottom-nav é fixa, então "rolar até o elemento" pode parar com ele embaixo dela —
   tanto no scroll do navegador (âncora, foco por teclado) quanto no do Selenium, que
   passava a clicar na nav em vez do botão. scroll-margin faz a rolagem parar antes. */
a,
button,
input,
select,
textarea,
[tabindex] {
  scroll-margin-bottom: calc(var(--bottom-nav-espaco) + var(--space-8) + env(safe-area-inset-bottom, 0px));
  scroll-margin-top: var(--space-6);
}

/* Avatar: foto se houver, iniciais como fallback — tokens do DESIGN.md, sem lib. */
.avatar {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  width: 40px;
  height: 40px;
  border-radius: 50%;
  background: var(--color-accent-subtle-bg, var(--color-surface-2));
  color: var(--color-accent, var(--color-ink));
  font-weight: var(--weight-medium);
  font-size: var(--text-caption, 0.8rem);
  overflow: hidden;
}

.avatar img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.avatar--lg {
  width: 64px;
  height: 64px;
  font-size: 1.25rem;
}

/* Linha de perfil no menu Você: avatar + nome/e-mail empilhados, chevron à direita. */
.linha-config--perfil {
  justify-content: flex-start;
}

.linha-config__perfil-texto {
  display: flex;
  flex-direction: column;
  min-width: 0;
  flex: 1;
}

.linha-config__perfil-texto .muted {
  font-weight: var(--weight-regular);
  font-size: var(--text-caption, 0.8rem);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.field--avatar {
  display: flex;
  align-items: center;
  gap: var(--space-4);
}

/* U17 — botão nativo de arquivo estilizado no lugar (não escondido): mais acessível que
   esconder o input e falsear um <label>. Segue a linguagem do .btn--ghost. */
.field--avatar input[type="file"] {
  font-size: var(--text-label);
  color: var(--color-muted);
}

.field--avatar input[type="file"]::file-selector-button {
  margin-right: var(--space-3);
  padding: var(--space-2) var(--space-4);
  border: 1px solid var(--color-muted);
  border-radius: var(--radius-full);
  background: var(--color-canvas);
  color: var(--color-ink);
  font: inherit;
  font-weight: var(--weight-medium);
  cursor: pointer;
}

.field--avatar input[type="file"]::file-selector-button:hover {
  background: var(--color-surface);
}

.field-group {
  border: 1px solid var(--color-hairline);
  border-radius: var(--radius-lg);
  padding: var(--space-4);
  display: grid;
  gap: var(--space-3);
}

.field-group legend {
  font-weight: var(--weight-medium);
  padding: 0 var(--space-2);
}

/* ============================================================
   Redesign — extrato bancário (U1: fundação)
   ============================================================ */

/* Badge neutro: arquivado, inativo. Cor cinza — nunca vermelho, que é reservado a
   "estornado"/erro. */
.badge-status--neutro {
  background: var(--color-status-neutro-bg);
  color: var(--color-status-neutro-fg);
}

/* Card do dia no extrato: cabeçalho (data por extenso + subtotal) separado do corpo por
   --color-divisor, itens dentro do mesmo card separados pela mesma linha fina — não
   card-por-lançamento, que é pesado demais numa lista de 15+ itens por mês. */
.card-dia {
  background: var(--color-canvas);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-card);
  overflow: hidden;
  margin-bottom: var(--space-4);
}

/* <summary> nativo: remove o triângulo default e vira área de toque do acordeão. */
.card-dia__cabecalho {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  border-bottom: 1px solid var(--color-divisor);
  cursor: pointer;
  list-style: none;
}

.card-dia__cabecalho::-webkit-details-marker {
  display: none;
}

/* Fechado: o cabeçalho é a única borda visível — sem o divisor pendurado embaixo. */
.card-dia:not([open]) .card-dia__cabecalho {
  border-bottom: none;
}

.card-dia__data {
  font-size: var(--text-label);
  font-weight: var(--weight-medium);
  color: var(--color-muted);
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

.card-dia__subtotal {
  font-size: var(--text-value-md);
  font-weight: var(--weight-medium);
}

.card-dia__itens > * + * {
  border-top: 1px solid var(--color-divisor);
}

/* Linha de lançamento no extrato: ícone circular da categoria, título forte, meta
   discreta, valor à direita. Deliberadamente SEM borda-esquerda colorida (decisão do
   usuário) — a cor já está no ícone, uma segunda faixa colorida ao lado é redundante e
   deixa a lista com cara de planilha de novo. */
.item-extrato {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  row-gap: var(--space-2);
  gap: var(--space-3);
  min-height: var(--tap-min);
  padding: var(--space-3) var(--space-4);
  position: relative;
}

/* Estornada tem badge + botão "Reverter" além do valor: em 375px isso não cabe numa
   linha só. wrap evita rolagem horizontal, e o botão fica sozinho na 2ª linha, ocupando
   a largura toda em vez de espremido ao lado do badge. */
.item-extrato > .btn {
  flex-basis: 100%;
}

.item-extrato__icone {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  width: 36px;
  height: 36px;
  border-radius: 50%;
  /* Cor da categoria chega via --cor-categoria inline no elemento; fallback neutro
     evita ícone invisível quando a categoria não define cor ainda. */
  background: color-mix(in srgb, var(--cor-categoria, var(--color-muted)) 16%, var(--color-canvas));
  color: var(--cor-categoria, var(--color-muted));
}

.item-extrato__info {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
  flex: 1;
}

.item-extrato__titulo {
  font-weight: var(--weight-medium);
  color: var(--color-ink);
  /* U16 — o nome do lançamento é o dado que a pessoa veio ver: ganha até duas linhas antes
     de truncar, em vez de uma só. A seta ao lado é enfeite e encolhe primeiro. */
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.item-extrato__meta {
  font-size: var(--text-label);
  color: var(--color-muted);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.item-extrato__valor {
  margin-left: auto;
  flex-shrink: 0;
}

/* Estornada: riscada + opacidade reduzida — é um registro anulado, não um erro do sistema,
   então nada de vermelho gritante aqui (esse fica pro badge de status quando precisar). */
.item-extrato--estornada .item-extrato__titulo,
.item-extrato--estornada .item-extrato__valor {
  text-decoration: line-through;
  text-decoration-thickness: 1.5px;
}

.item-extrato--estornada {
  opacity: 0.6;
}

/* Link "Acertar ›" do item pendente: acento âmbar (mesma cor do badge pendente), não
   marinho — reserva o marinho pra ações primárias e mantém a leitura de "isso ainda
   precisa de atenção" consistente com o resto do app. */
.item-extrato__acertar {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  color: var(--color-status-pendente-fg);
  font-size: var(--text-label);
  font-weight: var(--weight-medium);
  white-space: nowrap;
}

/* Alvo de toque estendido pra linha inteira, mesmo padrão de .linha-lancamento. */
.item-extrato .link-linha::after {
  content: "";
  position: absolute;
  inset: 0;
}

.item-extrato form,
.item-extrato .btn:not(.link-linha) {
  position: relative;
  z-index: 1;
}

/* ===== Bottom-nav: + central em destaque (FAB) ===== */
/* O botão de lançar é a ação mais frequente do app — precisa se destacar dos outros 3
   itens de navegação, não competir em igualdade de peso visual com eles. */

.bottom-nav__item--fab {
  position: relative;
}

.bottom-nav__item--fab svg {
  display: none;
}

.bottom-nav__item--fab::before {
  content: "";
  position: absolute;
  top: -18px;
  width: 52px;
  height: 52px;
  border-radius: 50%;
  background: var(--color-brand);
  box-shadow: var(--shadow-nav);
}

.bottom-nav__item--fab::after {
  content: "+";
  position: absolute;
  top: -18px;
  width: 52px;
  height: 52px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 1.75rem;
  line-height: 1;
  font-weight: var(--weight-regular);
  color: var(--color-on-brand);
}

/* Some com o rótulo de texto: o círculo já comunica a ação primária sozinho. */
.bottom-nav__item--fab {
  color: transparent;
}

.bottom-nav__item--fab:focus-visible {
  outline: none;
}

.bottom-nav__item--fab:focus-visible::before {
  outline: 2px solid var(--color-accent);
  outline-offset: 2px;
}

/* Você já está nesta aba: o círculo ganha um anel e sobe um pouco, sinalizando
   "selecionado" sem depender do rótulo (que fica escondido no FAB). */
.bottom-nav__item--fab[aria-current="page"]::before {
  box-shadow: var(--shadow-nav), 0 0 0 4px var(--color-accent-subtle-bg);
  outline: 2px solid var(--color-accent);
  outline-offset: 2px;
}

/* ===== Item editável (cartões/categorias) ===== */
/* Texto + lápis por padrão; toque abre o form nativo via <details> — sem JS, sem
   segunda tela. Arquivados ficam numa seção <details> própria, colapsada. */

/* Um container só com divisórias finas, não um card com sombra por item: é o padrão que
   o DESIGN.md define para lista densa (.lista-config, .card-dia) e que esta lista
   contrariava — quatro categorias viravam quatro caixas empilhadas. */
.lista-editavel {
  background: var(--color-canvas);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-card);
  overflow: hidden;
}

.item-editavel + .item-editavel {
  border-top: 1px solid var(--color-divisor);
}

.item-editavel__details {
  padding: 0;
}

.item-editavel__resumo {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  min-height: var(--tap-min);
  padding: var(--space-3) var(--space-4);
  cursor: pointer;
  list-style: none;
}

.item-editavel__resumo::-webkit-details-marker {
  display: none;
}

.item-editavel__cor {
  width: 14px;
  height: 14px;
  border-radius: var(--radius-full);
  flex-shrink: 0;
}

.item-editavel__nome {
  flex: 1;
  font-size: var(--text-body);
  color: var(--color-ink);
}

.item-editavel__lapis {
  color: var(--color-muted);
  flex-shrink: 0;
}

.item-editavel__form {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  padding: 0 var(--space-4) var(--space-4);
}

.item-editavel--arquivado + .item-editavel--arquivado {
  border-top: 1px solid var(--color-divisor);
}

.item-editavel--arquivado {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  min-height: var(--tap-min);
  padding: var(--space-3) var(--space-4);
}

.item-editavel--arquivado .item-editavel__nome {
  color: var(--color-muted);
}

/* Seção colapsada de arquivados: some da leitura principal sem sumir de vez. */
.secao-arquivados {
  margin-top: var(--space-6);
}

.secao-arquivados > summary {
  cursor: pointer;
  font-size: var(--text-label);
  color: var(--color-muted);
  font-weight: var(--weight-medium);
  padding: var(--space-2) 0;
  list-style: none;
}

.secao-arquivados > summary::-webkit-details-marker {
  display: none;
}

.secao-arquivados .lista-editavel {
  margin-top: var(--space-2);
}

/* ===== Item de form: chip de categoria (U5) ===== */

/* Ponto de cor dentro do chip de categoria — a mesma cor que aparece no ícone do item de
   extrato, para a pessoa aprender a associar cor↔categoria em um lugar só. */
.chip-categoria__ponto {
  display: inline-block;
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: var(--cor-categoria, var(--color-muted));
  margin-right: var(--space-2);
  flex-shrink: 0;
}

/* ===== Swatches de cor (categoria) + chips de pagamento (U5): mesmo corpo flex ===== */

.swatches,
.chips-pagamento {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
}

.swatch {
  width: 32px;
  height: 32px;
  border-radius: var(--radius-full);
  display: inline-flex;
  cursor: pointer;
  border: 2px solid transparent;
  box-shadow: inset 0 0 0 1px var(--color-hairline-strong);
}

.swatch:has(input:checked) {
  border-color: var(--color-ink);
}

.swatch:has(input:focus-visible) {
  outline: 2px solid var(--color-accent);
  outline-offset: 2px;
}

/* O select de cartão só existe depois de escolher a forma "cartão" — mesmo raciocínio do
   .campo-cartao original: um campo que parece opcional e some no fim é pior do que um
   campo que só aparece quando faz sentido preenchê-lo. */
.campo-revelado {
  display: none;
  padding-left: var(--space-1);
  margin-top: var(--space-2);
}

.grupo-pagamento:has(#forma_pagamento_cartao:checked) .campo-revelado--cartao {
  display: block;
}

.grupo-quem-pagou:has(#quem_pagou_novo:checked) .campo-revelado--nome-novo {
  display: block;
}

/* "1ª parcela em" liga/desliga via `hidden` no preview_controller (JS): o seletor CSS
   `[value="N"]` só enxerga o atributo HTML inicial, não o valor digitado depois — não
   dá para expressar "mais de uma parcela" em CSS puro aqui. */

/* Data: "Hoje"/"Ontem" escrevem no campo nativo via Stimulus; "Outra" só foca nele — o
   campo continua sempre visível e é sempre ele quem vale no submit. */
.chips-data {
  display: flex;
  gap: var(--space-2);
  margin-bottom: var(--space-2);
}

/* Card com borda de erro: o mesmo cartão que agrupa o campo, só que sinalizado — não move
   o layout dos outros cards, só acrescenta cor e a mensagem embaixo do campo errado. */
.grupo-campos--erro {
  border: 1px solid var(--color-value-negativo);
  border-radius: var(--radius-card);
  padding: var(--space-4);
  background: color-mix(in srgb, var(--color-value-negativo) 4%, var(--color-canvas));
}

.erro-campo {
  color: var(--color-value-negativo);
  font-size: var(--text-label);
  margin-top: var(--space-1);
}

.texto-fixo {
  padding: var(--space-2) 0;
  color: var(--color-ink-soft);
}

/* Rodapé do form: resumo vivo + Salvar. Fica no fim da página, no fluxo normal — a
   pessoa rola o formulário inteiro e o resumo/botão são a última coisa que ela vê. */
.rodape-lancamento {
  margin-top: var(--space-5);
  padding-top: var(--space-4);
  border-top: 1px solid var(--color-hairline);
}

.rodape-lancamento__resumo {
  margin-bottom: var(--space-3);
}

/* ============================================================
   Dashboard (U4): hero marinho, card de ação, quebras clicáveis
   ============================================================ */

/* Hero: mesmo fundo marinho do seletor sticky, para o número mais importante da tela
   (gasto do mês) morar dentro da mesma faixa de cor, não abaixo dela. */
.hero {
  background: var(--color-brand);
  color: var(--color-on-brand);
  border-radius: var(--radius-lg);
  padding: 0 var(--space-4) var(--space-5);
  margin-bottom: var(--space-4);
  overflow: hidden;
}

.hero .seletor-mes {
  border-radius: 0;
  box-shadow: none;
  margin-bottom: var(--space-4);
}

.hero .seletor-mes__acao {
  color: var(--color-on-brand);
}

.hero .seletor-mes__acao a {
  color: inherit;
}

.hero__rotulo {
  font-size: var(--text-label);
  font-weight: var(--weight-medium);
  opacity: 0.85;
}

.hero__valor {
  font-size: var(--text-display);
  font-weight: var(--weight-bold);
  overflow-wrap: anywhere;
}

.hero__comprometido {
  font-size: var(--text-label);
  opacity: 0.85;
  margin-top: var(--space-1);
}

/* Mini gráfico de 5 competências no hero: colunas verticais, altura proporcional.
   CSS puro (flex), sem lib. A coluna do mês de referência ganha opacidade cheia; as
   demais ficam esmaecidas — o destaque é o "onde estou", não um segundo dado. */
.grafico-competencias {
  display: flex;
  align-items: flex-end;
  gap: var(--space-2);
  height: 72px;
  margin-top: var(--space-5);
}

.grafico-competencias__coluna {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: flex-end;
  height: 100%;
  gap: var(--space-1);
}

.grafico-competencias__barra {
  width: 100%;
  height: var(--proporcao, 0%);
  min-height: 2px;
  border-radius: var(--radius-sm);
  background: var(--color-on-brand);
  opacity: 0.4;
}

.grafico-competencias__coluna--referencia .grafico-competencias__barra {
  opacity: 1;
}

.grafico-competencias__rotulo {
  font-size: var(--text-caption);
  opacity: 0.7;
  text-transform: uppercase;
}

.grafico-competencias__coluna--referencia .grafico-competencias__rotulo {
  opacity: 1;
  font-weight: var(--weight-medium);
}

/* Card de ação de reembolsos: só existe no DOM quando há pendentes — a ausência do
   elemento É o estado "nada pendente", sem precisar de mensagem de "tudo certo". */
.card-acao {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  background: var(--color-status-pendente-bg, var(--color-surface));
  border: 1px solid var(--color-status-pendente-fg);
  border-radius: var(--radius-lg);
  padding: var(--space-4);
  margin-bottom: var(--space-4);
  color: var(--color-ink);
}

.card-acao__texto {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
}

/* Lista "Por categoria" / "Por responsável": dot de cor + rótulo + barra proporcional +
   valor, tudo numa linha só — a barra é a leitura rápida, o valor é a confirmação. */
.dashboard-quebra {
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}

.dashboard-quebra__linha {
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: center;
  gap: var(--space-2) var(--space-3);
  color: inherit;
  text-decoration: none;
}

a.dashboard-quebra__linha:hover .dashboard-quebra__rotulo {
  text-decoration: underline;
}

.dashboard-quebra__dot {
  grid-row: 1;
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: var(--cor-categoria, var(--color-muted));
  flex-shrink: 0;
}

.dashboard-quebra__rotulo {
  grid-row: 1;
  font-weight: var(--weight-medium);
}

.dashboard-quebra__linha .barra {
  grid-column: 1 / -1;
  grid-row: 2;
}

.dashboard-quebra__valor {
  grid-row: 1;
  justify-self: end;
  font-variant-numeric: tabular-nums;
}

/* "Por cartão": mini-cards em grid, não lista — cartões são poucos e comparáveis lado a
   lado, diferente de categoria/responsável que podem ter muitas linhas. */
.grid-cartoes {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: var(--space-3);
}

.mini-card {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  background: var(--color-canvas);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-card);
  padding: var(--space-3);
  text-decoration: none;
  color: inherit;
}

a.mini-card:active {
  background: var(--color-surface-pressed);
}

.mini-card__rotulo {
  font-size: var(--text-label);
  color: var(--color-muted);
}

.mini-card__valor {
  font-weight: var(--weight-bold);
  font-variant-numeric: tabular-nums;
}

/* ============================================================
   Onboarding: stepper, folga de rodapé e tela de conclusão
   ============================================================ */

/* O wizard não monta a bottom-nav (ver comentário no layout): a folga que .page reserva
   para ela sobrava como ~120px de canvas vazio no fim de cada etapa — parecia página que
   não terminou de carregar. Aqui o rodapé volta a ser só respiro + safe-area. */
.page--onboarding {
  padding-bottom: calc(var(--space-8) + env(safe-area-inset-bottom, 0px));
}

/* Nota: etapa curta (Empresa tem 2 campos) sobra tela num aparelho de 812px, e isso é
   normal — tentativas de "preencher" o vão (ancorar o rodapé embaixo, esticar o card)
   só trocaram vazio cinza por vazio branco e por um card com metade do corpo vazia.
   O DESIGN.md proíbe ilustração/textura de enfeite, então não há o que pôr ali: o
   conteúdo termina onde termina. */

/* Quatro segmentos de mesma largura, um por etapa. O estado mora na barra (hairline = a
   fazer, marinho = feita/atual) e é reforçado no rótulo (✓ + peso), porque cor sozinha
   não pode ser o único sinal. Em 375px sobra ~80px por coluna: "Categorias", o rótulo
   mais longo, ocupa ~62px em --text-caption. */
.stepper {
  margin-bottom: var(--space-6);
}

/* 5 colunas iguais: em 375px dá ~63px por coluna, onde "Categorias" cabe em uma linha
   agora que o rótulo não carrega mais o ✓. Se um rótulo futuro estourar, os itens alinham
   pelo topo e a linha acompanha a mais alta — mas o rótulo é que deve encurtar. */
.stepper__trilha {
  display: grid;
  grid-template-columns: repeat(5, minmax(0, 1fr));
  gap: var(--space-1);
  list-style: none;
  padding: 0;
  margin: 0;
}

.stepper__etapa {
  display: flex;
}

/* O link ocupa a coluna inteira e chega a --tap-min de altura: o alvo de toque é a
   coluna, não as ~12px do rótulo. */
/* Rótulo centrado sob a barra, que é a largura da coluna: centrado, rótulo curto e barra
   larga se lêem como o mesmo item. */
.stepper__link {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: var(--space-2);
  width: 100%;
  min-height: var(--tap-min);
  color: var(--color-muted);
  font-size: var(--text-caption);
  font-weight: var(--weight-medium);
  line-height: var(--leading-base);
  text-decoration: none;
  transition: color var(--duration-fast) var(--ease-standard);
}

.stepper__link::before {
  content: "";
  display: block;
  /* width explícita: com align-items:center o ::before encolheria ao conteúdo (vazio). */
  width: 100%;
  height: 4px;
  border-radius: var(--radius-full);
  background: var(--color-hairline-strong);
  transition: background var(--duration-fast) var(--ease-standard);
}

/* Três estados de barra, nunca só dois. Cheia = feita ou atual; meia-tinta = etapa que
   ficou para trás sem preencher (pulada); hairline = ainda não alcançada. A trilha só
   anda para frente. Ver o comentário longo em _barra_progresso.html.erb. */
.stepper__link--feita::before,
.stepper__link--atual::before {
  background: var(--color-brand);
}

/* Tracejada, não só "marinho mais claro": a diferença entre pulada e não-alcançada
   precisa sobreviver a quem não distingue as duas tintas e à tela no sol ("cor nunca é o
   único sinal", DESIGN.md). Traço interrompido é diferença de forma, legível antes da
   cor — e continua dizendo "passei por aqui sem terminar". */
.stepper__link--pulada::before {
  background: repeating-linear-gradient(
    90deg,
    var(--color-brand) 0 5px,
    var(--color-canvas) 5px 9px
  );
}

/* Rótulo acompanha o estado: --color-muted (4.8:1) só na etapa ainda não alcançada; quem
   já passou fica em --color-body (7.7:1). Com tudo em muted, "Cartão" na etapa 3 ficava
   apagado a ponto de leitores não acharem o rótulo. */
.stepper__link--feita,
.stepper__link--pulada {
  color: var(--color-body);
}

.stepper__link--atual {
  color: var(--color-ink);
  font-weight: var(--weight-bold);
}

.stepper__link:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 2px;
  border-radius: var(--radius-sm);
}

/* Aviso de estado persistente (nome da empresa ainda no placeholder). Não é <dialog>:
   modal que abre sozinho no load tapa o formulário que a pessoa veio usar antes dela
   olhar a tela. Aqui o aviso fica na página, acima do form, e some quando o estado
   deixa de valer. Mesma faixa âmbar do status pendente — é pendência, não erro. */
.aviso-inline {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-3);
  padding: var(--space-4);
  margin-bottom: var(--space-6);
  border-radius: var(--radius-card);
  /* Superfície neutra, não o âmbar de --color-status-pendente: naquele token o app diz
     "dinheiro aguardando baixa". Emprestá-lo para um aviso sobre o NOME da empresa gasta
     um sinal financeiro num assunto que não é financeiro, e a cor deixa de ter um só
     ofício. O peso do aviso vem da posição (acima do form) e da ação embutida. */
  background: var(--color-surface-2);
  border: 1px solid var(--color-hairline-strong);
  color: var(--color-ink);
  font-size: var(--text-body);
}

.aviso-inline__corpo {
  line-height: var(--leading-loose);
}

.aviso-inline .btn {
  width: auto;
  background: transparent;
  border-color: var(--color-hairline-strong);
  color: var(--color-accent);
}

/* Tela de conclusão: era o único componente do wizard sem CSS nenhum — h1 colado no
   parágrafo, botão logo abaixo e meia tela de cinza. É o fim da jornada (peak-end):
   centraliza no viewport, sela com o ✓ e lista o que ficou pronto, para o marco parecer
   marco. Sem ilustração nem gradiente (anti-padrão do DESIGN.md): selo, texto e ação. */
.onboarding-concluido {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  gap: var(--space-4);
  /* dvh, não vh: no Safari iOS a barra de endereço come 100vh e empurra a ação para fora.
     62, não 78: a caixa de centralização não inclui o rodapé que vem depois dela, então
     um valor alto empurrava o bloco para cima e sobrava um vão maior embaixo do que em
     cima -- centralizado no próprio contêiner, torto na tela. */
  min-height: 62dvh;
}

.onboarding-concluido__selo {
  display: grid;
  place-items: center;
  width: 64px;
  height: 64px;
  border-radius: 50%;
  background: var(--color-brand-subtle-bg);
  color: var(--color-brand);
}

.onboarding-concluido h1 {
  font-size: 1.75rem;
  letter-spacing: -0.02em;
  /* Nome de empresa não tem limite de tamanho e é interpolado aqui. */
  overflow-wrap: anywhere;
}

.onboarding-concluido__resumo {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  width: 100%;
  padding: var(--space-4);
  border-radius: var(--radius-card);
  /* canvas + a elevação única do app, não --color-surface: o body já é --color-surface,
     então o card sumia no fundo e a lista parecia solta na página. */
  background: var(--color-canvas);
  box-shadow: var(--shadow-card);
  list-style: none;
  margin: 0;
  text-align: left;
}

.onboarding-concluido__item {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  font-size: var(--text-body);
  color: var(--color-body);
}

.onboarding-concluido__item svg {
  flex: 0 0 auto;
  color: var(--color-status-baixado-fg);
}

/* Pendência listada junto do que ficou pronto: quem pulou o cartão vê onde retomar em vez
   de descobrir depois que o app pede um cartão que ele nunca cadastrou. */
.onboarding-concluido__item--pendente {
  color: var(--color-muted);
}

.onboarding-concluido__item--pendente svg {
  color: var(--color-muted);
}

.onboarding-concluido__acoes {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  width: 100%;
}

/* Erro ao lado do campo. Vermelho semântico + o texto do próprio validador: cor sozinha
   não diz o que corrigir, e resumo só no topo obriga a rolar de volta caçando o campo. */
.erro-campo {
  font-size: var(--text-label);
  font-weight: var(--weight-medium);
  color: var(--color-value-negativo);
}

.field input[aria-invalid="true"],
.field select[aria-invalid="true"] {
  border-color: var(--color-value-negativo);
}

/* Texto auxiliar persistente sob um campo. Placeholder some quando a pessoa digita, que
   é exatamente quando a explicação ainda faz falta. */
.ajuda-campo {
  font-size: var(--text-label);
  color: var(--color-muted);
  line-height: var(--leading-base);
}

.ajuda-campo--centro {
  text-align: center;
}

/* Assinatura de confiança do wizard: por que o Numo pede um dado e quem passa a vê-lo.
   Num app que mexe com dinheiro, a pergunta que trava o preenchimento não é "como
   preencho" e sim "por que você quer isso" — e ela precisa ser respondida no campo, não
   numa política linkada que ninguém abre. Distinta de .ajuda-campo (que explica COMO
   preencher) pela régua à esquerda e pelo texto menos apagado: é afirmação sobre o dado,
   não nota de rodapé. Régua em --color-brand, o mesmo marinho da ação primária, para o
   sinal de confiança e o sinal de "prosseguir" virem da mesma voz.
   Regra do conteúdo: finalidade concreta + quem enxerga. Nunca garantia de segurança
   ("dados protegidos", "100% seguro") — promessa não verificável no código custa mais
   confiança do que silêncio, e justamente com quem tem menos como checar. */
.motivo {
  border-left: 2px solid var(--color-brand);
  padding-left: var(--space-3);
  font-size: var(--text-label);
  line-height: var(--leading-base);
  color: var(--color-body);
}

/* Saída secundária no fim de uma etapa ("ainda não tenho cartão", "concluir onboarding"):
   separada do bloco de ação primária, para não competir com ele. */
.acao-secundaria {
  margin-top: var(--space-4);
}

/* Revelação progressiva com <details> nativo — sem JS, com teclado e leitor de tela de
   graça. Usado no lote de convites: uma linha à vista, as outras sob demanda. */
.revelar {
  border-top: 1px solid var(--color-hairline);
  padding-top: var(--space-4);
}

.revelar__gatilho {
  display: flex;
  align-items: center;
  min-height: var(--tap-min);
  color: var(--color-accent);
  font-size: var(--text-body);
  font-weight: var(--weight-medium);
  cursor: pointer;
  list-style: none;
}

/* O triângulo padrão do Safari/Chrome tem peso óptico próprio e destoa do resto; +/–
   acompanha a tipografia, mesmo padrão do glossário em <details> das Configurações. */
.revelar__gatilho::-webkit-details-marker {
  display: none;
}

.revelar__gatilho::after {
  content: "+";
  margin-left: var(--space-2);
  font-size: var(--text-title);
  line-height: 1;
}

.revelar[open] .revelar__gatilho::after {
  content: "–";
}

.revelar__gatilho:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 2px;
  border-radius: var(--radius-sm);
}

.revelar__corpo {
  padding-top: var(--space-4);
}

/* Chips só de leitura (categorias que a empresa já tem). Mesma forma dos chips
   selecionáveis para se lerem como a mesma coisa, mas sem cursor nem estado de toque —
   não há o que tocar. Fundo de superfície, não marinho: já está pronto, não é seleção. */
.chips-categoria--estatico {
  list-style: none;
  padding: 0;
  margin: 0;
}

.chip-categoria--pronto {
  gap: var(--space-2);
  background: var(--color-surface);
  border-color: var(--color-hairline);
  color: var(--color-body);
  cursor: default;
}

.chip-categoria--pronto svg {
  width: 16px;
  height: 16px;
  color: var(--color-status-baixado-fg);
}

.acao-secundaria--centro {
  text-align: center;
}

/* Saída de etapa como link de texto: mais leve que .btn--ghost de propósito, para não
   virar uma terceira escolha com o mesmo peso da ação primária. Alvo de toque ainda
   cumpre --tap-min via padding vertical. */
.link-saida {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: var(--tap-min);
  padding: var(--space-2) var(--space-3);
  color: var(--color-accent);
  font-size: var(--text-body);
  font-weight: var(--weight-medium);
}

/* Conteúdo de uma etapa do wizard. O resto do app é card branco sobre body cinza
   (.lista-config, .card-dia); as etapas eram o único lugar em que o formulário ficava
   solto direto no cinza, e o espaço que sobrava embaixo lia como página que não carregou
   em vez de fim de conteúdo. O card dá borda ao conteúdo e devolve o rodapé ao fundo. */
.etapa {
  background: var(--color-canvas);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-card);
  padding: var(--space-5) var(--space-4);
}

/* Só o último grupo DO FORMULÁRIO perde a margem (o gap do .form já separa do botão).
   Sem o `.form >`, o seletor casava também com a <section> "Já criadas" — que é o último
   elemento do seu próprio tipo — e as duas listas de chips colavam uma na outra. */
.etapa .form > .grupo-campos:last-of-type {
  margin-bottom: 0;
}

/* Saída do wizard: presente, mas subordinada — centralizada, discreta e depois de toda
   ação da etapa, para ser encontrável sem competir com o "Continuar". button_to gera um
   <form>, daí o reset de margem. */
.onboarding-rodape {
  display: flex;
  /* Mesmo eixo dos campos e do "Continuar" -- ver .acao-inline. */
  justify-content: flex-start;
  align-items: center;
  gap: var(--space-2);
  margin-top: var(--space-8);
}

.onboarding-rodape form {
  margin: 0;
}

.onboarding-rodape .link-saida {
  background: none;
  border: none;
  color: var(--color-muted);
  font-size: var(--text-label);
  cursor: pointer;
}

/* Chip de categoria já existente: sem borda de pílula e em superfície, para não se ler
   como um controle tocável ao lado dos chips do catálogo, que são idênticos em forma. */
.chip-categoria--pronto {
  border-color: transparent;
  background: var(--color-surface-2);
}

/* Campo travado (o "Nome" que vem do cadastro). Sem regra própria ele era pixel a pixel
   igual a um campo editável, e o primeiro campo da primeira tela do produto convidava a
   tocar em algo que não responde. Superfície em vez de canvas + cursor bloqueado dizem
   "só leitura" antes da linha de ajuda precisar explicar. */
.field input:disabled,
.field select:disabled,
.field textarea:disabled {
  background: var(--color-surface-2);
  border-color: var(--color-hairline);
  color: var(--color-body);
  cursor: not-allowed;
}


/* Itens já existentes exibidos como leitura (categorias da empresa). Sem pílula, sem
   borda: forma de texto, não de controle. */
.lista-pronta {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2) var(--space-4);
  list-style: none;
  padding: 0;
  margin: 0;
}

.lista-pronta__item {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  font-size: var(--text-body);
  color: var(--color-body);
}

.lista-pronta__item svg {
  width: 16px;
  height: 16px;
  color: var(--color-status-baixado-fg);
}

/* No escuro, --color-brand (#3f6bb8) sobre --color-brand-subtle-bg (#1a2740) dá ~3:1 —
   o ✓ some dentro do próprio selo. O acento claro (#6ea8ff) sobre o mesmo fundo dá ~6:1.
   A tabela de contraste do DESIGN.md audita marca contra canvas, não contra a tinta
   sutil da marca; este par ficou de fora e só aparece aqui. */
:root[data-theme="dark"] .onboarding-concluido__selo {
  color: var(--color-accent);
}

/* <legend> de um grupo de rádios que faz papel de rótulo de campo (Segmento). Casa com
   .field label; o reset de fieldset vem junto porque o UA desenha borda e padding
   próprios, e min-width:0 impede que o conteúdo force a largura da coluna. */
fieldset.field {
  border: none;
  padding: 0;
  margin: 0;
  min-width: 0;
}

.field__legenda {
  padding: 0;
  font-size: var(--text-body);
  font-weight: var(--weight-medium);
  color: var(--color-ink-soft);
}

/* Separa as saídas globais (pausar / sair da conta) das ações da etapa logo acima: sem a
   linha, "pular esta etapa" e "sair da conta" viravam a mesma pilha de links parecidos e
   um toque errado custava a sessão. */
.onboarding-rodape {
  border-top: 1px solid var(--color-hairline);
  padding-top: var(--space-4);
}

/* Grupo de chips que falhou na validação. Paridade com `.field input[aria-invalid]`: o
   banner diz "confira os campos destacados", então o grupo precisa estar destacado — sem
   isto o Segmento errado era só uma linha vermelha embaixo de cinco pílulas iguais. */
.chips-categoria--invalido {
  border: 1px solid var(--color-value-negativo);
  border-radius: var(--radius-card);
  padding: var(--space-3);
}

/* No escuro, --color-hairline-strong (#383838) sobre o canvas quase preto some: o
   .btn--ghost ficava como texto solto, sem parecer botão. A borda sobe para um cinza que
   se lê contra o fundo escuro sem virar uma segunda ação de peso igual à primária. */
:root[data-theme="dark"] .btn--ghost {
  border-color: var(--color-muted);
}

/* Ação secundária que não deve imitar a silhueta do botão primário: largura automática,
   centrada, fora do fluxo de largura cheia do .form. */
.acao-inline {
  display: flex;
  /* Esquerda, não centro: com o "Continuar" em pílula alinhada aos campos, qualquer
     ação centralizada abaixo dele quebrava o eixo único da tela. Tudo no wizard
     alinha à esquerda. */
  justify-content: flex-start;
}

/* .nota-obrigatorio saiu junto com as três linhas "todos os campos são obrigatórios" que
   abriam Perfil, Empresa e Cartão. A convenção que ela anunciava continua valendo (campo
   sem marca é obrigatório, exceção leva "(opcional)" no rótulo) — só não precisa ser dita
   em prosa: os campos são `required`, o browser barra o submit e aponta qual faltou. Era
   uma linha de texto por tela para informar o caso que nunca acontece. */

/* === Identidade do onboarding (piloto: etapa Cartão) ============================== */

/* Fraunces só onde a marca fala: wordmark e título de etapa. Corpo continua na pilha
   de sistema — serifada em texto corrido de formulário atrapalha, e o objetivo é UMA
   voz de display, não redecorar o app. woff2 subset latin, 35KB, self-hosted (CSP e
   offline do PWA não permitem fonte de CDN). */
@font-face {
  font-family: "Fraunces";
  src: url("/assets/fraunces-600-173bdde1.woff2") format("woff2");
  font-weight: 600;
  font-style: normal;
  font-display: swap;
}

/* Wordmark: o produto assina a tela. Minúsculo de propósito — assinatura, não banner. */
.wordmark {
  font-family: "Fraunces", Georgia, serif;
  font-size: var(--text-body);
  color: var(--color-brand);
  letter-spacing: 0.01em;
  margin-bottom: var(--space-1);
}

/* Título de etapa com voz. Fraunces 600 num corpo maior que --text-title: é o único
   elemento de display da tela, então pode ocupar esse espaço sozinho. */
/* h1.titulo-display: sem o h1, .page__header h1 (mais específico) vencia o font-size. */
h1.titulo-display {
  font-family: "Fraunces", Georgia, serif;
  /* Uma linha sempre: título quebrado ao meio perde a voz. Fluido com o viewport e
     teto no tamanho de desktop; o piso cobre 320px. Título novo tem que caber numa
     linha a 360px -- se não couber, encurta o texto, não a fonte. */
  white-space: nowrap;
  font-size: clamp(1.125rem, 6.4vw, 1.625rem);
  font-weight: 600;
  line-height: 1.2;
  color: var(--color-ink);
}

/* O dado vira objeto: cartão cadastrado renderiza como mini-cartão físico, não como
   linha de texto. Proporção real de cartão (1.586:1), fundo marinho da marca, apelido
   em cima, os dois dias no rodapé — a tela que pede um cartão mostra um cartão. */
.cartoes-mini {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
  list-style: none;
  padding: 0;
  margin: 0;
}

.cartao-fisico {
  /* Canto superior direito reto: eco do gesto assimétrico da faixa-herói. */
  border-radius: var(--radius-md) 0 var(--radius-md) var(--radius-md);
  aspect-ratio: 1.586;
  width: min(230px, 100%);
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  padding: var(--space-4);
  background: linear-gradient(135deg, var(--color-brand), var(--color-brand-active));
  color: var(--color-on-brand);
  animation: cartao-entra var(--duration-base, 200ms) var(--ease-standard) both;
}

.cartao-fisico__nome {
  font-family: "Fraunces", Georgia, serif;
  font-size: var(--text-value-md);
  font-weight: 600;
  overflow-wrap: anywhere;
}

.cartao-fisico__dias {
  font-size: var(--text-caption);
  opacity: 0.85;
  letter-spacing: 0.02em;
}

/* Único movimento da tela: o mini-cartão entra deslizando quando a página volta com o
   cartão salvo. Nada mais anima. */
@keyframes cartao-entra {
  from { opacity: 0; transform: translateY(6px); }
}

@media (prefers-reduced-motion: reduce) {
  .cartao-fisico { animation: none; }
}

/* Etapa sem moldura: mata o card cinza-admin que embrulhava o formulário. O conteúdo
   termina onde termina — hairline no lugar de caixa. */
.etapa--quieta {
  background: transparent;
  border-radius: 0;
  box-shadow: none;
  padding: 0;
}

/* Botão-pílula: quebra a silhueta "retângulo azul full-width" do form genérico. Largura
   do conteúdo com respiro mínimo, cantos plenos, alinhado à esquerda com os campos. */
.btn--pilula {
  width: auto;
  min-width: 60%;
  align-self: flex-start;
  border-radius: var(--radius-full);
}

/* Faixa-herói: a tela ganha zonas em vez de ser um documento
   branco corrido — marca e navegação vivem numa faixa marinho, o conteúdo vem depois.
   Nasceu no wizard e passou a se chamar pelo que é, não por onde nasceu: as portas de
   entrada (login, cadastro, recuperar senha, aceite de convite) usam a mesma faixa.
   Continua exclusiva de wizard e portas de entrada — nenhuma tela do app logado
   ganha marinho novo.
   Margens negativas cancelam o padding do .page para a faixa sangrar até as bordas da
   coluna; o safe-area cobre o notch no PWA. */
.faixa-heroi {
  background: linear-gradient(160deg, var(--color-brand), var(--color-brand-active));
  color: var(--color-on-brand);
  margin: calc(-1 * var(--space-4)) calc(-1 * var(--space-4)) var(--space-6);
  padding: calc(var(--space-6) + env(safe-area-inset-top, 0px)) var(--space-4) var(--space-8);
  /* Canto direito reto, esquerdo redondo: o gesto assimétrico da identidade. O
     mini-cartão ecoa o mesmo canto -- repetido, lê como intenção; sozinho, leria
     como defeito. */
  border-radius: 0 0 0 var(--radius-lg);
}

.faixa-heroi .page__header {
  margin-bottom: var(--space-5);
}

/* Sem stepper embaixo (portas de entrada), o respiro do cabeçalho não separa nada —
   só alonga a faixa. O :has cobre isso sem precisar de classe modificadora na view. */
.faixa-heroi:not(:has(.stepper)) .page__header {
  margin-bottom: 0;
}

/* Linha de contexto dentro da faixa: o nome da empresa no aceite de convite é
   interpolação sem limite de tamanho e não cabe num h1 de uma linha só. */
.faixa-heroi__contexto {
  font-size: var(--text-label);
  color: rgba(255, 255, 255, 0.85);
  margin-top: var(--space-2);
  overflow-wrap: anywhere;
}

.faixa-heroi .wordmark,
.faixa-heroi .titulo-display {
  color: var(--color-on-brand);
}

.faixa-heroi .wordmark {
  opacity: 0.85;
}

/* Stepper sobre marinho: mesmos três estados por forma, tintas trocadas para a faixa.
   Alfa em vez de tons novos — herda qualquer ajuste futuro do marinho de graça. */
.faixa-heroi .stepper {
  margin-bottom: 0;
}

.faixa-heroi .stepper__link {
  color: rgba(255, 255, 255, 0.62);
}

.faixa-heroi .stepper__link--feita,
.faixa-heroi .stepper__link--pulada {
  color: rgba(255, 255, 255, 0.8);
}

.faixa-heroi .stepper__link--atual {
  color: #fff;
}

.faixa-heroi .stepper__link::before {
  background: rgba(255, 255, 255, 0.28);
}

.faixa-heroi .stepper__link--feita::before,
.faixa-heroi .stepper__link--atual::before {
  background: #fff;
}

.faixa-heroi .stepper__link--pulada::before {
  background: repeating-linear-gradient(90deg, #fff 0 5px, transparent 5px 9px);
}

.faixa-heroi .stepper__link:focus-visible {
  outline-color: #fff;
}

/* O cartão-preview vem logo depois da faixa, inteiro sobre o fundo. Já cavalgou a borda
   (metade no marinho, metade no fundo, estilo app de banco) — marinho sobre marinho lia
   como elemento desalinhado, não como sobreposição intencional. Sombra própria mantém o
   "objeto sobre a mesa". */
.cartao-fisico--destaque {
  box-shadow: 0 8px 22px rgba(17, 37, 74, 0.28);
}

/* Cartões já salvos: miniatura, dois por linha (metade da coluna menos o gap),
   proporção de cartão intacta. O preview é o objeto da vez; os salvos são histórico. */
.cartoes-mini .cartao-fisico {
  width: calc(50% - var(--space-3) / 2);
  padding: var(--space-3);
}

.cartoes-mini .cartao-fisico__nome {
  font-size: var(--text-label);
}

.cartoes-mini .cartao-fisico__dias {
  font-size: 0.6875rem;
}

/* Diálogo do produto. <dialog> nativo: foco preso, Escape e devolução de foco vêm de
   graça, sem uma linha de JS de acessibilidade. Canto direito reto no topo, o mesmo gesto
   assimétrico da faixa-herói — elemento novo de marca repete a assinatura. */
.dialogo {
  /* O reset global zera as margens e leva junto o `margin: auto` que o <dialog> nativo
     usa para se centrar — sem isto ele encosta no canto superior esquerdo e sangra pela
     borda, que lê como defeito de renderização, não como diálogo. */
  margin: auto;
  width: min(420px, calc(100vw - var(--space-8)));
  border: none;
  border-radius: var(--radius-lg) 0 var(--radius-lg) var(--radius-lg);
  padding: var(--space-6) var(--space-5);
  background: var(--color-canvas);
  color: var(--color-ink);
  box-shadow: var(--shadow-modal);
}

.dialogo::backdrop {
  background: rgba(17, 37, 74, 0.55);
}

/* Sem Fraunces: a serifada mora em exatamente três lugares (wordmark, título de etapa,
   nome do mini-cartão) e um título de diálogo não é nenhum deles. O canto reto já carrega
   a assinatura aqui — o DESIGN.md autoriza o gesto, não a fonte. */
.dialogo__titulo {
  font-size: var(--text-title);
  font-weight: var(--weight-medium);
  margin-bottom: var(--space-2);
}

.dialogo__acoes {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-2);
  margin-top: var(--space-5);
}

/* Cartão editável (tela de Cartões, U5): o mini-cartão É o alvo de toque que abre a
   edição. Na lista do wizard os cartões são histórico; aqui são objetos que se corrigem.
   <details> nativo, sem JS — o mesmo padrão de disclosure já usado no app. */
.cartoes-mini--editaveis {
  flex-direction: column;
  flex-wrap: nowrap;
}

.cartao-editavel__resumo {
  list-style: none;
  cursor: pointer;
}

.cartao-editavel__resumo::-webkit-details-marker { display: none; }

.cartao-editavel__resumo:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 2px;
}

/* Dentro do <summary> o mini-cartão é um <span>: precisa voltar a ser bloco para manter
   a proporção física. A largura vem de `.cartoes-mini .cartao-fisico` (metade da coluna):
   dar largura cheia aqui inflava o cartão a 320x200 com um vão morto no meio, e dois
   objetos grandes — o salvo e o preview do formulário — brigavam pela mesma tela. */
.cartao-editavel__resumo .cartao-fisico {
  display: flex;
}

/* Incompleto não é erro: sem os dias, o cartão perde a cor cheia da marca e fica com a
   silhueta pontilhada de "falta preencher". A cor sozinha não sinaliza — o aviso de texto
   abaixo diz o que falta e o que fazer. */
.cartao-fisico--incompleto {
  background: var(--color-surface-2);
  color: var(--color-body);
  border: 1px dashed var(--color-hairline-strong);
}

.cartao-editavel__aviso {
  display: block;
  margin-top: var(--space-2);
  font-size: var(--text-label);
  color: var(--color-status-pendente-fg);
}

.cartao-editavel__form {
  padding: var(--space-4) 0 var(--space-6);
}

/* Campos quietos (wizard e portas de entrada): superfície preenchida em vez de caixa com
   borda — a moldura de todos os lados é o que mais grita "formulário". A borda volta no
   foco, onde ela informa em vez de decorar. `.form--quieta` é a mesma coisa aplicada
   direto num <form>, onde não há o contêiner de etapa do wizard. */
.etapa--quieta .field input,
.form--quieta .field input {
  background: var(--color-surface-2);
  border-color: transparent;
}

.etapa--quieta .field input:focus,
.form--quieta .field input:focus {
  background: var(--color-canvas);
  border-color: var(--color-accent);
  outline: none;
}

/* "Mais opções" (U9): parcelas + reembolso recolhidos, fechado por padrão. O summary é uma
   linha tocável; o resumo à direita mostra o estado atual ("à vista · reembolso
   automático") para que recolher não esconda um parcelamento, que muda o total. */
.mais-opcoes {
  border-top: 1px solid var(--color-divisor);
  padding-block: var(--space-3);
}

.mais-opcoes__summary {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: var(--space-2);
  cursor: pointer;
  font-weight: var(--weight-medium);
  list-style: none;
}

.mais-opcoes__summary::-webkit-details-marker {
  display: none;
}

.mais-opcoes__resumo {
  font-weight: var(--weight-regular);
  font-size: var(--text-label);
}

.mais-opcoes[open] .mais-opcoes__summary {
  margin-bottom: var(--space-3);
}

/* === Toggle de tema (sol/lua) ==================================================== */
/* Discreto, fixo no canto superior direito. Fora do <main> pra não empurrar layout.
   Ícone reflete o tema atual: sol no claro, lua no escuro. currentColor herda o ink,
   então lê bem sobre canvas claro e escuro; sobre a faixa-herói marinho o botão fica
   com fundo próprio pra não sumir. */
.tema-toggle {
  position: fixed;
  top: max(var(--space-3), env(safe-area-inset-top));
  right: max(var(--space-3), env(safe-area-inset-right));
  z-index: 50;
  display: grid;
  place-items: center;
  width: 40px;
  height: 40px;
  border-radius: var(--radius-full);
  color: var(--color-ink);
  background: color-mix(in srgb, var(--color-canvas) 70%, transparent);
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
  transition: background var(--duration-fast) var(--ease-standard);
}

.tema-toggle:hover {
  background: var(--color-surface-pressed);
}

.tema-toggle svg {
  width: 20px;
  height: 20px;
}

/* Mostra só o ícone do tema vigente. */
.tema-toggle .tema-toggle__lua { display: none; }
:root[data-theme="dark"] .tema-toggle .tema-toggle__sol { display: none; }
:root[data-theme="dark"] .tema-toggle .tema-toggle__lua { display: block; }
