/* ==========================================================================
   FinanKey 4.0 - Reset, tipografia e app shell
   O shell e um grid de 3 faixas: header fixo, main rolavel, nav fixa.
   O main reserva espaco proprio para a nav, entao nada fica encoberto.
   ========================================================================== */

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

* {
  margin: 0;
}

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

body {
  min-height: 100dvh;
  background: var(--bg);
  color: var(--text);
  font-family: var(--font-sans);
  font-size: var(--fs-base);
  line-height: var(--lh-normal);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  overscroll-behavior-y: none;
}

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

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

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

a {
  color: var(--accent);
  text-decoration: none;
}

:where(button, a, input, select, textarea, [tabindex]):focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: var(--r-sm);
}

[hidden] {
  display: none !important;
}

/* Numeros de dinheiro sempre alinhados */
.num {
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1;
}

/* ==========================================================================
   App shell
   ========================================================================== */
.app {
  display: grid;
  grid-template-rows: auto 1fr auto;
  min-height: 100dvh;
  max-width: var(--shell-max);
  margin-inline: auto;
  /* Transparente de proposito: o cosmos vive no body e precisa aparecer
     por tras das laminas de vidro. */
  background: transparent;
  position: relative;
}

@media (min-width: 34rem) {
  .app {
    border-inline: 1px solid var(--border);
    min-height: 100dvh;
  }
}

/* ---- Header ---- */
.app-header {
  position: sticky;
  top: 0;
  z-index: var(--z-header);
  background: color-mix(in srgb, var(--bg) 88%, transparent);
  backdrop-filter: saturate(1.4) blur(12px);
  -webkit-backdrop-filter: saturate(1.4) blur(12px);
  border-bottom: 1px solid var(--border);
  padding-top: var(--safe-top);
}

.app-header__bar {
  height: var(--header-h);
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  padding-inline: var(--sp-4);
}

.app-header__title {
  font-size: var(--fs-lg);
  font-weight: var(--fw-semibold);
  letter-spacing: -0.01em;
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.app-header__actions {
  display: flex;
  align-items: center;
  gap: var(--sp-1);
}

/* ---- Main rolavel ---- */
.app-main {
  min-width: 0;
  padding: var(--sp-4);
  padding-inline: max(var(--sp-4), var(--safe-left));
  /* Reserva a altura da nav + area segura: conteudo nunca fica escondido */
  padding-bottom: calc(var(--nav-h) + var(--safe-bottom) + var(--sp-8));
  display: flex;
  flex-direction: column;
  gap: var(--sp-4);
}

/* ---- Navegacao inferior ---- */
.app-nav {
  position: fixed;
  bottom: 0;
  left: 50%;
  transform: translateX(-50%);
  width: 100%;
  max-width: var(--shell-max);
  z-index: var(--z-nav);
  background: color-mix(in srgb, var(--surface) 92%, transparent);
  backdrop-filter: saturate(1.4) blur(16px);
  -webkit-backdrop-filter: saturate(1.4) blur(16px);
  box-shadow: var(--shadow-nav);
  padding-bottom: var(--safe-bottom);
}

.app-nav__list {
  height: var(--nav-h);
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: 1fr;
  align-items: stretch;
}

.nav-item {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 3px;
  color: var(--text-3);
  font-size: var(--fs-2xs);
  font-weight: var(--fw-medium);
  letter-spacing: 0.01em;
  transition: color var(--dur-fast) var(--ease-out);
  position: relative;
  min-width: 0;
}

.nav-item__icon {
  width: 22px;
  height: 22px;
  flex: none;
}

.nav-item__label {
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.nav-item[aria-current="page"] {
  color: var(--accent);
}

.nav-item[aria-current="page"]::before {
  content: "";
  position: absolute;
  top: 0;
  left: 50%;
  transform: translateX(-50%);
  width: 28px;
  height: 3px;
  border-radius: 0 0 var(--r-full) var(--r-full);
  background: var(--accent);
}

@media (hover: hover) {
  .nav-item:hover {
    color: var(--text);
  }
  .nav-item[aria-current="page"]:hover {
    color: var(--accent);
  }
}

/* ==========================================================================
   Tipografia utilitaria
   ========================================================================== */
.t-display {
  font-size: var(--fs-3xl);
  font-weight: var(--fw-bold);
  line-height: var(--lh-tight);
  letter-spacing: -0.03em;
}

.t-title {
  font-size: var(--fs-xl);
  font-weight: var(--fw-semibold);
  line-height: var(--lh-snug);
  letter-spacing: -0.02em;
}

.t-heading {
  font-size: var(--fs-base);
  font-weight: var(--fw-semibold);
  line-height: var(--lh-snug);
  letter-spacing: -0.01em;
}

.t-body {
  font-size: var(--fs-md);
  color: var(--text-2);
}

.t-caption {
  font-size: var(--fs-xs);
  color: var(--text-3);
}

.t-label {
  font-size: var(--fs-2xs);
  font-weight: var(--fw-semibold);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-3);
}

.t-pos {
  color: var(--positive-text);
}
.t-neg {
  color: var(--negative-text);
}
.t-warn {
  color: var(--warning-text);
}
.t-muted {
  color: var(--text-3);
}

/* ==========================================================================
   Layout utilitario
   ========================================================================== */
/*
 * PILHAS: UMA COISA EMBAIXO DA OUTRA, COM AR ENTRE ELAS.
 *
 * As três variantes são autossuficientes. Antes só .stack tinha display, e
 * .stack-sm / .stack-lg eram só um "gap" — pensados como modificador
 * (div.stack.stack-sm). Mas foram usados SOZINHOS em 84 lugares, e gap em
 * bloco comum não faz nada: é ignorado sem erro, sem aviso, sem nada.
 *
 * O resultado estava no app inteiro. "Marcar como pago" colado em "Pagar
 * várias de uma vez", o "Excluir" encostado na grade de ações, os botões da
 * compra parcelada empilhados sem um pixel entre eles. E pelo menos três
 * remendos em materials.css que tentaram consertar o sintoma tela a tela —
 * cada um achando que o espaço era pequeno, quando ele simplesmente não
 * existia.
 *
 * Um nome de classe que parece funcionar e não funciona é pior que nenhum:
 * quem escreve "stack-sm" lê "pilha com espaço pequeno" e segue em frente.
 */
.stack,
.stack-sm,
.stack-lg {
  display: flex;
  flex-direction: column;
}
.stack {
  gap: var(--sp-3);
}
.stack-sm {
  gap: var(--sp-2);
}
.stack-lg {
  gap: var(--sp-5);
}

.row {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
}
/* Usado sozinho em varios lugares, entao precisa trazer o display junto:
   sem isso o nome e o valor colavam ("AlimentacaoR$ 374,37"). */
.row-between {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-3);
}
.row-wrap {
  flex-wrap: wrap;
}

.grid-2 {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--sp-3);
}

.spacer {
  flex: 1;
}

.section {
  display: flex;
  flex-direction: column;
  gap: var(--sp-3);
}

.section__head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--sp-3);
  min-height: 1.5rem;
}

.section__link {
  font-size: var(--fs-sm);
  font-weight: var(--fw-medium);
  color: var(--accent);
  flex: none;
}

/* Rolagem horizontal contida - nunca deixa o body rolar lateralmente */
.scroll-x {
  display: flex;
  gap: var(--sp-2);
  overflow-x: auto;
  scrollbar-width: none;
  padding-bottom: var(--sp-1);
  margin-inline: calc(var(--sp-4) * -1);
  padding-inline: var(--sp-4);
  scroll-snap-type: x proximity;
}
.scroll-x::-webkit-scrollbar {
  display: none;
}
.scroll-x > * {
  scroll-snap-align: start;
  flex: none;
}

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

/* ==========================================================================
   Movimento

   A REGRA QUE DECIDE TUDO AQUI: animação existe para explicar, não para
   enfeitar. Cada uma abaixo responde a uma pergunta que a pessoa faria.

     "para onde eu fui?"    -> a tela entra do lado da aba que se escolheu
     "mudou alguma coisa?"  -> o mês novo sobe, o mês antigo desce
     "isto é uma lista?"    -> os cartões entram em cascata, de cima para baixo
     "meu toque pegou?"     -> o que se toca encolhe um pouco

   Tudo curto: 220ms a 360ms. Animação que se percebe como animação já está
   longa demais — num app que se abre vinte vezes por dia, ela vira espera.
   ========================================================================== */

/*
 * Só o conteúdo se move. O cabeçalho e a barra de baixo ficam parados de
 * propósito: são a moldura, e moldura que balança dá a sensação de que a
 * página inteira recarregou.
 */
.app-main--da-direita > *,
.app-main--da-esquerda > *,
.app-main--mes-frente > *,
.app-main--mes-tras > * {
  animation: var(--fk-entrada) var(--dur-slow) var(--ease-out) both;
}

.app-main--da-direita { --fk-entrada: fk-entra-direita; }
.app-main--da-esquerda { --fk-entrada: fk-entra-esquerda; }
.app-main--mes-frente { --fk-entrada: fk-entra-baixo; }
.app-main--mes-tras { --fk-entrada: fk-entra-cima; }

/*
 * A cascata.
 *
 * O atraso cresce até o quinto cartão e para: com dez, os últimos entrariam
 * meio segundo depois dos primeiros e a tela pareceria travada carregando.
 * Cinco degraus bastam para o olho ler "isto veio em ordem".
 */
.app-main[class*="app-main--"] > *:nth-child(1) { animation-delay: 0ms; }
.app-main[class*="app-main--"] > *:nth-child(2) { animation-delay: 35ms; }
.app-main[class*="app-main--"] > *:nth-child(3) { animation-delay: 70ms; }
.app-main[class*="app-main--"] > *:nth-child(4) { animation-delay: 105ms; }
.app-main[class*="app-main--"] > *:nth-child(n+5) { animation-delay: 140ms; }

@keyframes fk-entra-direita {
  from { opacity: 0; transform: translate3d(18px, 0, 0); }
}
@keyframes fk-entra-esquerda {
  from { opacity: 0; transform: translate3d(-18px, 0, 0); }
}
@keyframes fk-entra-baixo {
  from { opacity: 0; transform: translate3d(0, 10px, 0); }
}
@keyframes fk-entra-cima {
  from { opacity: 0; transform: translate3d(0, -10px, 0); }
}

/*
 * O traço da aba ativa cresce do meio para os lados. É o único elemento da
 * moldura que se move, porque é o único que responde ao toque.
 */
.nav-item[aria-current="page"]::before {
  animation: fk-nav-traco var(--dur-base) var(--ease-out);
}
@keyframes fk-nav-traco {
  from { opacity: 0; transform: translateX(-50%) scaleX(0.25); }
}

/* O ícone da aba escolhida dá um passo à frente e volta. */
.nav-item[aria-current="page"] .nav-item__icon {
  animation: fk-nav-icone var(--dur-slow) var(--ease-out);
}
@keyframes fk-nav-icone {
  40% { transform: translateY(-3px) scale(1.08); }
}

/*
 * Resposta ao toque, onde ainda não havia.
 *
 * O app já encolhia botão, cartão de ferramenta e tecla da calculadora. Estes
 * são os que faltavam — e a regra é a mesma: 2% de encolhimento, rápido o
 * bastante para parecer o próprio dedo afundando a superfície.
 */
.nav-item:active .nav-item__icon,
.month-switch button:active,
.cat-icon:active {
  transform: scale(0.92);
}
.nav-item__icon,
.month-switch button {
  transition: transform var(--dur-fast) var(--ease-out);
}

/* A folha e o cartão do assistente aparecem com um leve avanço. */
.assist-fab {
  animation: fk-pop var(--dur-slow) var(--ease-out) both;
}
@keyframes fk-pop {
  from { opacity: 0; transform: scale(0.8); }
}

/*
 * Nada disto vale para quem pediu menos movimento. O bloco geral logo abaixo
 * já corta a duração de tudo, mas o atraso da cascata sobreviveria — e cinco
 * cartões esperando 140ms cada, sem animação para justificar, seria só uma
 * tela que demora a aparecer.
 */
/*
 * "REDUZIR MOVIMENTO" É O PADRÃO, NÃO UMA TRAVA.
 *
 * Os blocos abaixo seguem o sistema — mas a pessoa pode escolher em Mais →
 * Aparência → Animações. "Sempre" liga mesmo com o sistema pedindo menos
 * (data-movimento="sempre" desliga estes blocos); "Reduzidas" desliga mesmo
 * com o sistema permitindo (data-movimento="reduzido" liga as cópias).
 *
 * Existe por um caso real: o Windows vinha com "efeitos de animação"
 * desligado, o navegador repassou isso como prefers-reduced-motion, e o
 * bloco global cortava TODA animação e transição do app com !important. A
 * pessoa pediu animação, recebeu animação, e não viu nenhuma — nem as que já
 * existiam. Seguir o sistema continua sendo o certo por padrão; o que não
 * dava era não haver saída.
 */
@media (prefers-reduced-motion: reduce) {
  :root:not([data-movimento="sempre"]) .app-main[class*="app-main--"] > * {
    animation: none !important;
    animation-delay: 0ms !important;
  }
}
:root[data-movimento="reduzido"] .app-main[class*="app-main--"] > * {
  animation: none !important;
  animation-delay: 0ms !important;
}

@media (prefers-reduced-motion: reduce) {
  :root:not([data-movimento="sempre"]) *,
  :root:not([data-movimento="sempre"]) *::before,
  :root:not([data-movimento="sempre"]) *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}
:root[data-movimento="reduzido"] *,
:root[data-movimento="reduzido"] *::before,
:root[data-movimento="reduzido"] *::after {
  animation-duration: 0.01ms !important;
  animation-iteration-count: 1 !important;
  transition-duration: 0.01ms !important;
  scroll-behavior: auto !important;
}

/* --------------------------------------------------------------------------
   Zoom desativado
   O meta viewport bloqueia a pinça; touch-action bloqueia o duplo toque.
   Escolha do dono do app, para o PWA se comportar como aplicativo nativo.
   -------------------------------------------------------------------------- */
/* --------------------------------------------------------------------------
   Travado no eixo vertical

   "manipulation" liberava arrasto nos dois eixos: bastava um deslize torto
   para o card inteiro sair andando de lado e voltar. Num app de verdade a
   tela não faz isso. "pan-y" permite só rolar para cima e para baixo — e de
   quebra continua matando a pinça e o duplo toque.

   As três camadas são de propósito: touch-action barra o gesto,
   overflow-x barra a rolagem, overscroll-behavior-x barra o efeito
   elástico da borda. Faltando qualquer uma, algum navegador ainda mexe.
   -------------------------------------------------------------------------- */
/*
 * "clip" e não "hidden", e a diferença não é cosmética.
 *
 * overflow-x: hidden faz do elemento um contêiner de rolagem, e a
 * especificação então promove o eixo Y de "visible" para "auto". No html isso
 * cria um segundo contêiner rolável dentro da viewport — e em vários
 * navegadores móveis o resultado é a rolagem por toque simplesmente parar.
 *
 * "clip" corta o que passa da borda sem criar contêiner nenhum: trava o lado
 * exatamente igual e não encosta na rolagem vertical.
 */
html {
  touch-action: pan-y;
  -webkit-text-size-adjust: 100%;
  overflow-x: clip;
  overscroll-behavior-x: none;
}

body {
  touch-action: pan-y;
  overflow-x: clip;
  overscroll-behavior-x: none;
  max-width: 100%;
}

/* Áreas que precisam rolar continuam rolando — só na vertical. */
.app-main,
.sheet__body,
.sheet-backdrop {
  touch-action: pan-y;
  overscroll-behavior-x: none;
}

/* As faixas de chips são a única exceção: ali o deslize lateral é o ponto.
   "contain" impede que o gesto vaze para a página quando a faixa acaba. */
.scroll-x,
.cat-picker,
.select-bar__actions {
  touch-action: pan-x;
  overscroll-behavior-x: contain;
}

/* ==========================================================================
   Desktop

   Até aqui o app era uma coluna de 30rem centralizada numa tela de 1920 —
   funcionava, mas desperdiçava a tela inteira e mantinha a barra de abas
   colada no rodapé, longe do olho e do mouse.

   A partir de 64rem a mesma marcação vira outro layout: a navegação sai do
   rodapé e vira coluna à esquerda, o conteúdo ganha largura de leitura, e as
   folhas param de subir do chão para aparecer no centro, como diálogo.
   Nada disso exige JavaScript diferente — é a mesma árvore, reorganizada.
   ========================================================================== */
@media (min-width: 64rem) {
  .app {
    max-width: 76rem;
    grid-template-columns: 15rem minmax(0, 1fr);
    grid-template-rows: auto minmax(0, 1fr);
    grid-template-areas:
      "nav header"
      "nav main";
  }

  .app-header { grid-area: header; }
  .app-main { grid-area: main; }
  .app-nav { grid-area: nav; }

  /* Coluna de leitura: uma lista de lançamentos com 1200px de largura
     obriga o olho a varrer a tela toda para ligar nome e valor. */
  :root {
    --coluna-max: 52rem;
  }

  .app-main {
    /* Sem barra no rodapé, o vão reservado para ela deixa de fazer sentido. */
    padding-bottom: var(--sp-8);
    padding-inline: var(--sp-6);
    max-width: var(--coluna-max);
    width: 100%;
  }

  .app-nav {
    position: sticky;
    top: 0;
    left: auto;
    bottom: auto;
    transform: none;
    width: auto;
    max-width: none;
    height: 100dvh;
    align-self: start;
    padding: var(--sp-4) var(--sp-3);
    border-right: 1px solid var(--border);
    box-shadow: none;
  }

  .app-nav__list {
    height: auto;
    grid-auto-flow: row;
    grid-auto-columns: auto;
    gap: 2px;
  }

  .nav-item {
    flex-direction: row;
    justify-content: flex-start;
    gap: var(--sp-3);
    min-height: 2.75rem;
    padding-inline: var(--sp-3);
    border-radius: var(--r-md);
    font-size: var(--fs-sm);
  }

  .nav-item[aria-current="page"] {
    background: var(--surface-3);
  }

  /* O traço do item ativo era uma barra no topo, boa para aba inferior.
     Na coluna ele vira um traço vertical na borda esquerda. */
  .nav-item[aria-current="page"]::before {
    top: 50%;
    left: 0;
    transform: translateY(-50%);
    width: 3px;
    height: 1.25rem;
    border-radius: 0 var(--r-full) var(--r-full) 0;
  }

  /* O cabeçalho não precisa mais repetir a marca: ela vive na coluna. */
  .app-header {
    padding-inline: var(--sp-6);
  }

  /* Folhas viram diálogo centrado: subir do rodapé é gesto de celular. */
  .sheet-backdrop {
    align-items: center;
  }
  .sheet {
    max-width: 34rem;
    max-height: 84dvh;
    border-radius: var(--r-xl);
  }
  /* Centrada, ela não é mais arrastável para baixo — a alça sairia mentindo. */
  .sheet__handle {
    display: none;
  }
}

/*
 * TELAS MUITO LARGAS.
 *
 * Os números saíram de medir num monitor de 1920: com o teto anterior a casca
 * parava em 1344 e a coluna em 928, deixando 39% do monitor preto. Aqui a
 * casca cresce junto e a coluna vai atrás, sem virar aquela linha de texto
 * atravessada de ponta a ponta que ninguém consegue ler.
 *
 * As larguras moram SÓ AQUI. Espalhadas por dois blocos de media query, uma
 * regra acaba brigando com a outra e quem ganha vira questão de ordem no
 * arquivo — que é o tipo de detalhe que ninguém lembra ao mexer depois.
 */
@media (min-width: 90rem) {
  :root {
    --coluna-max: 68rem;
  }
  .app {
    max-width: 90rem;
  }
  .app-main {
    /* Centrada na coluna. Sem isto ela encosta na navegação e todo o vão
       sobra de um lado só — o desequilíbrio é o que mais salta aos olhos. */
    margin-inline: auto;
  }
}

@media (min-width: 96rem) {
  :root {
    --coluna-max: 74rem;
  }
  .app {
    max-width: 96rem;
  }
}

/* ==========================================================================
   PC DE VERDADE: A TELA LARGA DEIXA DE SER MOLDURA PRETA

   Medido num monitor de 1920: a casca parava em 1344, a coluna de conteúdo em
   928, e sobravam 471px de nada à direita mais 281 à esquerda — 39% do monitor
   vazio, com tudo empilhado numa tira de largura de celular.

   Uma coluna estreita é a escolha certa para LISTA: nome à esquerda e valor à
   direita, sem o olho varrer a tela toda para ligar os dois. Mas a tela de
   Início não é lista, é cartão — e cartão lado a lado é o que faz o monitor
   valer alguma coisa.
   ========================================================================== */
@media (min-width: 90rem) {
  /*
   * :has, e não uma classe por rota.
   *
   * Quem manda aqui é o CONTEÚDO, não o endereço: a tela que tem cartão de
   * saldo é a que ganha duas colunas. Uma classe por rota obrigaria a mexer no
   * roteador para uma decisão que é de layout, e ficaria desencontrada no dia
   * em que a tela mudasse de nome.
   */
  .app-main:has(> .balance) {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    /* Cartões de alturas diferentes: cada um começa no topo da sua linha em
       vez de esticar para acompanhar o vizinho mais alto. */
    align-items: start;
  }

  /* O seletor de mês é barra, não cartão: atravessa as duas colunas. */
  .app-main:has(> .balance) > .month-switch {
    grid-column: 1 / -1;
  }
}
