/* ═══════════════════════════════════════════════════════════════════════════
   VANTRA — FUNDACAO VISUAL
   Direcao "do edital a divisa", aprovada no V0 (02/08/2026). Detalhe na
   skill vantra-design. A amostra clicavel e /estilo.html.

   ⚠️ ESTE E O UNICO LUGAR ONDE COR, TIPO, ESPACO E MOVIMENTO SAO DEFINIDOS.

   Antes desta data o bloco :root estava COPIADO em 16 HTMLs -- e ja havia
   se desencontrado em 4 versoes diferentes. Era a causa raiz da
   inconsistencia visual. Nunca mais redeclarar :root numa pagina: mexa aqui
   e as 16 mudam juntas.
   ═══════════════════════════════════════════════════════════════════════════ */

/* ═══════════════════════════════════════════════════════════════════════════
   FONTES HOSPEDADAS POR NOS  ·  16/09/2026

   Antes vinham do Google, e isso custava uma CADEIA SERIAL na primeira visita:
   o HTML pedia o CSS ao fonts.googleapis.com (medido contra a producao: comeca
   em 99ms, dura 205ms) e so DEPOIS de ele chegar o navegador ficava sabendo os
   enderecos dos .woff2, no fonts.gstatic.com -- outra origem, outro DNS, outro
   TLS. Quatro pedidos a terceiros antes da primeira letra sair na fonte certa.
   Quanto DURAM os .woff2 nao da para medir de fora: o gstatic nao manda
   Timing-Allow-Origin, entao o Resource Timing devolve zero para eles.

   Mesma decisao e mesmo motivo do supabase-js (rules/paginas.md 9): servidor de
   terceiro no caminho critico e latencia que nao controlamos, e se ele cair o
   Vantra cai junto. Sairam tambem duas origens da CSP.

   SAO FONTES VARIAVEIS: um arquivo por familia/subset cobre todos os pesos.
   18 declaracoes, 6 arquivos. As declaracoes repetem exatamente o que o Google
   declarava -- de proposito, para o comportamento ser identico.

   Subsets: latin e latin-ext. Cirilico, grego e vietnamita ficaram de fora -- o
   produto e para concurseiro brasileiro. Texto nesses alfabetos cai na fonte do
   sistema, em vez de nao aparecer.

   🔴 ARMADILHA QUE ME PEGOU AO GERAR ISTO: no CSS do Google o comentario
   /* subset */ vem ANTES do @font-face. Dividir o arquivo por "@font-face"
   associa cada bloco ao comentario do bloco SEGUINTE -- off-by-one. Os
   unicode-range foram parar nos arquivos errados, NENHUMA face cobriu latim e
   todo o texto do site caiu no fallback. O teste pegou (954 blocos de texto com
   largura diferente, 0 fontes carregadas). Se for regerar: cortar NOS
   comentarios, e conferir que toda face 'latin' contem U+0000-00FF.

   PARA ATUALIZAR: nao editar o .woff2. Baixar de novo do Google e trocar o
   arquivo -- mesma regra do supabase-js.
   Licenca das tres familias: SIL Open Font License 1.1.
   ═══════════════════════════════════════════════════════════════════════════ */

@font-face {
  font-family: 'Archivo';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('/assets/fontes/archivo-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Archivo';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('/assets/fontes/archivo-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Archivo';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url('/assets/fontes/archivo-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Archivo';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url('/assets/fontes/archivo-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Archivo';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url('/assets/fontes/archivo-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Archivo';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url('/assets/fontes/archivo-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Archivo';
  font-style: normal;
  font-weight: 800;
  font-display: swap;
  src: url('/assets/fontes/archivo-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Archivo';
  font-style: normal;
  font-weight: 800;
  font-display: swap;
  src: url('/assets/fontes/archivo-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'JetBrains Mono';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('/assets/fontes/jetbrains-mono-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'JetBrains Mono';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('/assets/fontes/jetbrains-mono-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'JetBrains Mono';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('/assets/fontes/jetbrains-mono-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'JetBrains Mono';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('/assets/fontes/jetbrains-mono-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'JetBrains Mono';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url('/assets/fontes/jetbrains-mono-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'JetBrains Mono';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url('/assets/fontes/jetbrains-mono-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Source Serif 4';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('/assets/fontes/source-serif-4-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Source Serif 4';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('/assets/fontes/source-serif-4-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Source Serif 4';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url('/assets/fontes/source-serif-4-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Source Serif 4';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url('/assets/fontes/source-serif-4-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

:root {
  /* 08/10/2026 (3.10b, regra da Vercel): o site e escuro -- o navegador precisa saber, senao
     a barra de rolagem, o calendario do campo de data e os controles nativos saem CLAROS */
  color-scheme: dark;
  /* ── COR ── seis tons, e nenhum e roxo ────────────────────────────────── */
  --breu:      #0E1620;   /* campo institucional -- azul-grafite frio */
  --casco:     #17222E;   /* superficie elevada: cartao, topbar, sidebar */
  --casco-2:   #1E2B39;   /* superficie sobre superficie */
  --linha:     #2A3947;   /* divisoria */
  --papel:     #E7E4DB;   /* o edital -- branco-osso FRIO, nunca creme */
  --latao:     #C08A2E;   /* o metal da insignia */
  --latao-c:   #E0AE55;   /* latao claro -- texto e borda sobre escuro */
  --latao-e:   #3A2B12;   /* latao escuro -- fundo de destaque discreto */
  --oliva:     #5C6B47;   /* a farda -- apoio, sucesso, estado neutro */
  --oliva-c:   #8CA06B;
  --brasa:     #B4432E;   /* alerta e prazo */
  --brasa-c:   #E0705A;

  --texto:     #DDE4EA;
  --texto-2:   #8FA0AE;
  /* 29/09/2026: era #5F7183 -- contraste de 2,9 a 3,6 sobre os fundos, abaixo
     dos 4,5 recomendados para letra pequena. O dono: "tem o tom quase igual ao
     do fundo, algumas pessoas nao conseguem ver". #7A8C9F da 4,7 no casco e
     5,3 no breu, e continua mais escuro que o --texto-2 (a hierarquia fica).
     04/10/2026 (auditoria UX-02, roadmap 3.10): #7A8C9F dava 4,16 no --casco-2
     (rotulos pequenos em maiusculas: "SESSOES", "MATERIA"). #8494A6 da 4,64 no
     casco-2, 5,19 no casco e 5,86 no breu -- e segue mais escuro que o texto-2. */
  --texto-3:   #8494A6;

  /* ── TIPO ── tres vozes, tres funcoes ─────────────────────────────────── */
  --display:   "Archivo", "Arial Narrow", Arial, sans-serif;   /* titulo, patente, botao */
  --corpo:     "Source Serif 4", Georgia, serif;               /* texto corrido */
  --dado:      "JetBrains Mono", Consolas, monospace;          /* numero, prazo, % */

  --t-xs: .75rem;  --t-sm: .875rem; --t-md: 1rem;
  --t-lg: 1.25rem; --t-xl: 1.75rem; --t-2xl: 2.5rem;

  /* ── ESPACO ── multiplos de 4 ─────────────────────────────────────────── */
  --e1: .25rem; --e2: .5rem; --e3: 1rem; --e4: 1.5rem; --e5: 2.5rem; --e6: 4rem;

  /* ── RAIO ── DOIS. Antes eram 8 (8/10/12/14/16/20/50%/999px) ──────────── */
  --r-p: 3px;   /* peca pequena: botao, campo, chip */
  --r-g: 6px;   /* peca grande: cartao, painel, modal */

  /* ── MOVIMENTO ── curvas e duracoes (fonte: skill emil-design-eng) ──────
     NUNCA ease-in em interface: comeca devagar e faz a tela parecer travada
     exatamente no instante em que a pessoa esta olhando.                   */
  --saida:     cubic-bezier(.23, 1, .32, 1);      /* entra e sai */
  --percurso:  cubic-bezier(.77, 0, .175, 1);     /* move na tela */
  --gaveta:    cubic-bezier(.32, .72, 0, 1);      /* gaveta estilo iOS */

  --d-toque:   140ms;   /* botao apertado */
  --d-dica:    160ms;   /* tooltip, chip, foco */
  --d-menu:    200ms;   /* dropdown, popover */
  --d-painel:  320ms;   /* modal, gaveta */
  --d-fecha:   180ms;   /* SAIR e sempre mais rapido que ENTRAR */

  /* ── ESTRUTURA ── */
  --sidebar-w: 240px;

  /* ═══════════════════════════════════════════════════════════════════════
     PONTE PARA OS NOMES ANTIGOS
     As 16 paginas usam var(--purple), var(--bg), var(--gold)... espalhados
     por centenas de linhas de CSS inline. Reapontar os nomes antigos para a
     paleta nova troca o visual do site inteiro SEM editar essas linhas.
     Trocar 70 valores na mao seria erro garantido em algum canto.
     Esta ponte sai quando as paginas forem reescritas uma a uma (V4 a V6).
     ═══════════════════════════════════════════════════════════════════════ */
  --bg:         var(--breu);
  --surface:    var(--casco);
  --surface2:   var(--casco-2);
  --border:     var(--linha);
  --purple:     var(--latao);      /* o antigo roxo de marca vira o latao */
  --purple-lt:  var(--latao-c);
  --purple-dim: var(--latao-e);
  --gold:       var(--latao-c);    /* dourado e roxo faziam o mesmo papel; agora e um so */
  --green:      var(--oliva-c);
  --red:        var(--brasa-c);
  --text:       var(--texto);
  --muted:      var(--texto-2);
  --white:      var(--papel);      /* branco puro vira branco-osso */
}

/* ═══ TIPOGRAFIA BASE ═══
   Aplicada com seletor de baixa especificidade para nao brigar com o CSS
   inline que ja existe nas paginas. */
body {
  font-family: var(--corpo);
  background: var(--bg);
  color: var(--text);
  -webkit-font-smoothing: antialiased;
}

/* ── A BARRA DE ROLAGEM QUE PISCAVA ────────────────────────────────────────
   O Lucas em 04/08/2026: "quando clico em outro menu, rapidamente aparece
   aquela barra dos cantos, na horizontal e na vertical".

   MEDIDO, e a causa e a propria animacao de troca de pagina: o miolo entra
   deslizando 34px para o lado (vantra-pg-entra) e sai deslizando 26px
   (vantra-pg-sai). Enquanto ele esta deslocado, sobra conteudo fora da janela
   -- e o navegador mostra a barra, mesmo por 220ms. Aferido em 13 de cada 100
   quadros, nas quatro paginas testadas, sempre 34px.

   A ANIMACAO NAO FOI TOCADA. Ela e decisao dele ("quero que o site seja
   fluido"), e o defeito nunca esteve nela: esta em o navegador tratar como
   "conteudo para rolar" algo que e so um efeito passando.

   `clip` e nao `hidden`, de proposito: `hidden` transforma o elemento em area
   de rolagem, o que quebra `position: sticky` de qualquer filho e muda o
   comportamento do scroll da pagina. `clip` so corta o que passa da borda, sem
   criar area de rolagem nenhuma. A linha com `hidden` vem antes como reserva
   para navegador antigo que nao conhece `clip` -- quem conhece usa a segunda.

   ⚠️ Isto corta APENAS o eixo horizontal. A rolagem vertical da pagina
   continua intacta -- e por isso que nao se usa `overflow: clip` sozinho. */
html, body {
  overflow-x: hidden;
  overflow-x: clip;
}
h1, h2, h3, h4, h5, h6, button, .btn, .page-title, .nav-label, .sidebar-logo {
  font-family: var(--display);
}
h1, h2, h3 { text-wrap: balance; letter-spacing: -.01em; }

/* numero, prazo e percentual alinham em coluna como planilha oficial */
.num, .stat-value, .xp, [data-num] {
  font-family: var(--dado);
  font-variant-numeric: tabular-nums;
}

/* ═══ FOCO VISIVEL ═══ obrigatorio, nunca remover: e como quem navega por
   teclado sabe onde esta. */
:focus-visible {
  outline: 2px solid var(--latao-c);
  outline-offset: 2px;
  border-radius: var(--r-p);
}

/* ═══ TOQUE ═══ todo elemento apertavel responde ao dedo. */
button, .btn, [role="button"] {
  transition: transform var(--d-toque) var(--saida),
              background-color var(--d-toque) ease,
              border-color var(--d-toque) ease,
              color var(--d-toque) ease;
}
button:active, .btn:active, [role="button"]:active { transform: scale(.97); }

/* ═══ A DIVISA ═══ nivel a esquerda (vem do XP), tag a direita (se conquista).
   O bico e o que faz ler como insignia e nao como etiqueta de plano. */
.divisa {
  display: inline-flex; align-items: stretch;
  font-family: var(--display); font-size: var(--t-xs); font-weight: 800;
  letter-spacing: .12em; text-transform: uppercase;
  transition: transform var(--d-dica) var(--saida);
}
.divisa .nivel {
  background: linear-gradient(180deg, var(--latao-c), var(--latao));
  color: #121A24; padding: .34rem .75rem .34rem 1.15rem;
  clip-path: polygon(0 0, 100% 0, calc(100% - .5rem) 50%, 100% 100%, 0 100%, .5rem 50%);
  /* 10/10/2026 (V6): no celular, "3º SARGENTO BM" ao lado de "em formacao: ORADOR DE GUERRA · 64%" se partia
     em TRES linhas. A patente e insignia -- nao parte; quem cede espaco e o texto da tag. */
  white-space: nowrap; flex: 0 0 auto; display: flex; align-items: center;
}
.divisa { max-width: 100%; }
.divisa .tag {
  color: var(--latao-c); border: 1px solid var(--latao); border-left: none;
  padding: .34rem .8rem; font-weight: 700;
  min-width: 0;
}
.divisa .tag.vazia {
  color: var(--texto-3); border-color: var(--linha); font-weight: 400;
  font-family: var(--corpo); text-transform: none; letter-spacing: 0;
}
/* 04/10/2026 (auditoria UX-02, roadmap 3.10): era opacidade .55 na divisa
   inteira -- o nome da especialidade caia para 3,3:1. Enferrujada apaga o METAL
   (a patente perde a cor, a borda some) e o nome segue legivel. */
.divisa.enferrujada .nivel { filter: grayscale(.8) brightness(.85); }
.divisa.enferrujada .tag { color: var(--texto-2); border-color: var(--linha); }

/* ═══ ENTRADA EM CASCATA ═══ CSS puro, no lugar da biblioteca de CDN.
   Roda fora da thread principal: nao perde quadro enquanto a pagina carrega.
   Nada nasce de scale(0) -- nada no mundo real some e reaparece do nada. */
.cascata > * {
  opacity: 0; transform: translateY(10px);
  animation: vantra-surge 420ms var(--saida) forwards;
}
.cascata > *:nth-child(1) { animation-delay:   0ms; }
.cascata > *:nth-child(2) { animation-delay:  60ms; }
.cascata > *:nth-child(3) { animation-delay: 120ms; }
.cascata > *:nth-child(4) { animation-delay: 180ms; }
.cascata > *:nth-child(5) { animation-delay: 240ms; }
.cascata > *:nth-child(6) { animation-delay: 300ms; }
@keyframes vantra-surge { to { opacity: 1; transform: translateY(0); } }

/* ═══ CASCA — REDE DE SEGURANCA ═══
   Escrito com :where(), que tem especificidade ZERO: qualquer regra da pagina
   vence sem esforco. Nao existe para padronizar nada -- existe para que nenhuma
   pagina fique SEM a casca.

   🐛 Motivo real, achado em 02/08/2026 quando o Lucas disse que Minha Conta
   estava "escangalhada": a conta.html nao tinha `.nav-link` nenhum (o menu
   renderizava como link azul sublinhado do navegador) e nao tinha o `.main` de
   desktop -- so a versao dentro do @media de celular. No computador o conteudo
   ficava ESCONDIDO ATRAS da barra lateral fixa.

   Nao era feiura de design: era CSS faltando. E ninguem percebeu porque cada
   pagina carrega a propria copia da casca -- o mesmo defeito de raiz do :root
   e do menu. */
:where(.nav-link) {
  display: flex; align-items: center; gap: .65rem;
  padding: .65rem .75rem; border-radius: var(--r-p);
  color: var(--muted); text-decoration: none;
  font-size: .88rem; font-weight: 500; cursor: pointer;
  border: none; background: none; width: 100%; text-align: left;
}
/* Aqui NAO da para usar :where(): especificidade zero perde para a regra
   `.main` que as paginas declaram dentro do @media de celular, e o conteudo
   voltava a ficar atras da barra. `.sidebar ~ .main` (0,2,0) e deterministico
   e so atinge quem TEM barra lateral -- landing e login nao sao afetados.
   O par de celular abaixo precisa da mesma especificidade, senao o desktop
   venceria no telefone. */
.sidebar ~ .main {
  margin-left: var(--sidebar-w);
  flex: 1;
  padding: 2rem 2.5rem;
  max-width: calc(100vw - var(--sidebar-w));
}
@media (max-width: 768px) {
  .sidebar ~ .main { margin-left: 0; padding: 1.25rem; max-width: 100vw; }
}

/* ═══ COMPONENTES DA CASCA — o que faltava ═══
   Especificidade normal (0,1,0): a pagina que declarar a propria versao vence
   pela ordem, porque o <style> inline vem depois do <link>. Isto e piso, nao teto.

   🐛 Motivo (02/08/2026): `.card` NAO EXISTIA em lugar compartilhado nenhum --
   nem no app.css. Cada pagina declarava a sua, e a conta.html simplesmente nao
   declarava. O HTML dela ja dizia class="card" em 5 secoes e nenhuma virava
   cartao: a pagina inteira era texto solto. Nao era falta de design, era falta
   de CSS -- de novo. */
.card {
  background: var(--casco);
  border: 1px solid var(--linha);
  border-radius: var(--r-g);
  padding: var(--e4);
  margin-bottom: var(--e3);
}
.page-sub, .section-sub {
  color: var(--texto-2);
  font-size: var(--t-sm);
  margin: 0 0 var(--e3);
  max-width: 68ch;
}
.section-title {
  font-family: var(--display);
  font-weight: 700;
  font-size: var(--t-lg);
  letter-spacing: -.01em;
  margin: 0 0 var(--e1);
  color: var(--texto);
}
/* uma marca de latao a esquerda do titulo -- a mesma linguagem da divisa */
.card > .section-title::before {
  content: '';
  display: inline-block;
  width: 3px; height: .85em;
  background: var(--latao);
  border-radius: 0 var(--r-p) var(--r-p) 0;
  margin-right: .6rem;
  vertical-align: -.06em;
}
/* 10/10/2026 (Lote D, V6): os fios eram o FUNDO aparecendo pela fenda de 1px -- e onde a ultima linha
   tinha vaga (6 dados em 4 colunas), a vaga inteira virava um bloco cinza. Agora cada peca desenha o
   proprio fio (direita e embaixo) e a vaga fica da cor do painel. O que passa da borda o overflow corta. */
.dados-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr));
  background: var(--casco-2);
  border: 1px solid var(--linha);
  border-radius: var(--r-p);
  overflow: hidden;
  margin-top: var(--e3);
}
.dados-grid > * { padding: var(--e3); box-shadow: 1px 0 0 var(--linha), 0 1px 0 var(--linha); min-width: 0; }
.dados-grid dt, .dados-grid .rotulo, .dados-grid strong, .dados-grid .dado-label {
  display: block;
  font-family: var(--dado);
  font-size: var(--t-xs);
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--texto-3);
  margin-bottom: var(--e1);
  font-weight: 700;
}
.dados-grid dd, .dados-grid .dado-valor { margin: 0; font-size: var(--t-md); color: var(--texto); overflow-wrap: anywhere; }

/* ZONA DE PERIGO: fundo levemente quente e faixa de brasa. Ela tem que
   PARECER diferente do resto, nao so estar escrita no fim da pagina. */
.card.zona-perigo {
  border-color: color-mix(in srgb, var(--brasa) 45%, transparent);
  border-left: 3px solid var(--brasa);
  background: linear-gradient(180deg, color-mix(in srgb, var(--brasa) 7%, transparent), transparent 60%), var(--casco);
}
.zona-perigo .section-title { color: var(--brasa-c); }
.zona-perigo .section-title::before { background: var(--brasa); }
.card.zona-perigo .aviso {
  background: color-mix(in srgb, var(--brasa) 9%, transparent);
  border: 1px solid color-mix(in srgb, var(--brasa) 25%, transparent);
  border-radius: var(--r-p);
  padding: var(--e3);
  margin: var(--e3) 0;
  font-size: var(--t-sm);
}

/* BOTOES da casca */
.btn {
  font-family: var(--display);
  font-size: var(--t-sm); font-weight: 700;
  letter-spacing: .05em;
  padding: .7rem 1.15rem;
  border-radius: var(--r-p);
  border: 1px solid transparent;
  cursor: pointer;
  display: inline-flex; align-items: center; gap: var(--e2);
  text-decoration: none;
}
.btn-roxo    { background: var(--latao); color: #121A24; border-color: var(--latao); }
.btn-fantasma{ background: transparent; color: var(--texto); border-color: var(--linha); }
.zona-perigo .btn-fantasma { color: var(--brasa-c); border-color: color-mix(in srgb, var(--brasa) 50%, transparent); }
/* 10/10/2026 (Lote D, V2): a casca nao tinha estado DESLIGADO -- o "Comecar" do Banco, travado enquanto
   carrega, parecia identico ao que se pode apertar. Apagado e sem subir/brilhar. Opacidade so no botao,
   que e peca unica (a regra do 3.10 fala de cartao inteiro, onde ela derruba o texto de dentro). */
.btn:disabled, .btn:disabled:hover, .btn:disabled:active,
.btn[aria-disabled="true"], .btn[aria-disabled="true"]:hover { opacity: .5; cursor: not-allowed; transform: none; box-shadow: none; }
.btn:disabled::before, .btn:disabled::after { display: none; }
@media (hover: hover) and (pointer: fine) {
  .btn-roxo:hover     { background: var(--latao-c); border-color: var(--latao-c); }
  .btn-fantasma:hover { border-color: var(--latao); color: var(--latao-c); }
  .zona-perigo .btn-fantasma:hover { background: var(--brasa); color: #fff; border-color: var(--brasa); }
}

/* ═══ MENU LATERAL ═══
   A regra base de .nav-link mora INLINE em cada pagina (e um dos seletores que
   divergem, ver rules/paginas.md 8.5). CSS inline vem depois do <link> e vence
   com a mesma especificidade -- entao aqui usamos `.sidebar .nav-link` (0,2,0)
   para ganhar de `.nav-link` (0,1,0) INDEPENDENTE DA ORDEM. Mesmo truque do
   menu de celular (blocos executados, 8.11). */
/* 🐛 TERCEIRA VEZ o mesmo defeito, corrigido de vez em 02/08/2026.
   O Lucas mandou dois prints: o menu do Progresso e o do Cronograma tinham
   espacamentos diferentes. O HTML era identico (conferido por hash) -- o que
   divergia era o CSS: 8 paginas declaravam as PROPRIAS 6 regras de .nav-link
   (e o dashboard declarava duas vezes), enquanto Conta e Cronograma caiam no
   compartilhado. Duas aparencias.

   Agora esta e a UNICA definicao. As copias das paginas foram removidas.
   A especificidade `.sidebar .nav-link` (0,2,0) ganha de `.nav-link` (0,1,0)
   independente da ordem -- entao mesmo que alguem cole uma copia de novo no
   futuro, o menu continua igual. */
.sidebar .nav-link {
  position: relative;
  display: flex; align-items: center; gap: .65rem;
  padding: .65rem .75rem;
  border-radius: var(--r-p);
  color: var(--muted);
  text-decoration: none;
  font-family: var(--corpo);
  font-size: .88rem; font-weight: 500;
  cursor: pointer; border: none; background: none;
  width: 100%; text-align: left;
  /* transform, nao padding: padding refaz o layout e empurra os vizinhos --
     era o "sobe e desce" ao clicar de um menu para outro. */
  transition: color var(--d-dica) ease,
              background-color var(--d-dica) ease,
              transform var(--d-dica) var(--saida);
}
.sidebar .nav-link svg {
  width: 16px; height: 16px; flex-shrink: 0;
  transition: transform var(--d-dica) var(--saida);
}
.sidebar .nav-link.active {
  color: var(--latao-c);
  background: color-mix(in srgb, var(--latao) 10%, transparent);
}

/* ═══ HOVER SO PARA QUEM TEM MOUSE ═══
   No celular o toque dispara hover e o elemento fica aceso e grudado. */
@media (hover: hover) and (pointer: fine) {
  .divisa:hover { transform: translateY(-1px); }
  .sidebar .nav-link:not(.active):hover {
    color: var(--texto);
    background: rgba(255,255,255,.04);
    transform: translateX(4px);         /* desliza sem refazer o layout */
  }
  .sidebar .nav-link:not(.active):hover svg { transform: translateX(1px); }
}

/* ═══ ENTRADA DO CONTEUDO ═══
   Só o miolo anima na troca de pagina. A sidebar fica parada de proposito:
   identica nas 9 paginas e sem animacao, ela parece ter PERMANECIDO enquanto
   so o conteudo trocou -- que e o efeito que o Lucas pediu. */
/* 🐛 CORRIGIDO em 02/08/2026 -- o Lucas relatou "a fluidez foi embora, esta
   seco e bugando quando clico no outro". Ele estava certo, e a causa era eu.

   O .main tem view-transition-name: o navegador TIRA UMA FOTO dele e anima a
   foto. Se o elemento REAL tambem tem animation de entrada, os dois animam ao
   mesmo tempo -- e como esta comeca em opacity 0, a tela pisca no meio da
   passagem. Duas animacoes disputando o mesmo elemento nao somam: brigam.

   Agora a animacao de entrada so existe onde NAO ha transicao de pagina, como
   ultimo recurso. Onde ha, quem anima e o navegador, sozinho. */

@keyframes vantra-entra {
  from { opacity: 0; transform: translateY(8px); }
  to   { opacity: 1; transform: translateY(0); }
}

/* ══════════════════════════════════════════════════════════════════════════
   TRANSICAO ENTRE PAGINAS — a correcao do "seco"
   ══════════════════════════════════════════════════════════════════════════
   O navegador fotografa a pagina velha, carrega a nova por baixo e faz a
   passagem. Nao ha o descarregar-e-remontar que causava a piscada.

   A barra lateral leva um NOME: o navegador entende que a sidebar da pagina
   A e a MESMA da pagina B, e simplesmente nao a anima. Ela fica parada
   enquanto o conteudo desliza -- que e o efeito de aplicativo, nao de site.

   Navegador sem suporte ignora tudo isto e cai no fade do transicao.js.
   Nada quebra. */
/* ⚠️ A View Transitions API esta DESLIGADA de proposito.
   Ela dispara nos meus testes (pageswap e pagereveal confirmam), mas o Lucas
   continuou vendo corte seco em tres rodadas seguidas. Duas coisas animando a
   mesma troca de pagina brigam -- foi o defeito das rodadas anteriores.
   O desvanecer agora e feito por JS (assets/js/transicao.js), que eu consigo
   fotografar quadro a quadro. Se um dia a API for reativada, o JS tem de sair
   junto. */

/* ── A TROCA DE PAGINA ─────────────────────────────────────────────────────
   Sai: o miolo desliza e apaga. A barra lateral NAO -- ela fica.
   Entra: o miolo chega deslizando do outro lado.
   O tempo e generoso de proposito: ele pediu para dar tempo de ver. */
body.vantra-saindo .main {
  animation: vantra-pg-sai 260ms var(--saida) forwards;
}
body.vantra-saindo { pointer-events: none; }
@keyframes vantra-pg-sai {
  to { opacity: 0; transform: translateX(-26px); }
}

.main { animation: vantra-pg-entra 420ms var(--saida) both; }
@keyframes vantra-pg-entra {
  from { opacity: 0; transform: translateX(34px); }
  to   { opacity: 1; transform: translateX(0); }
}

@media (prefers-reduced-motion: reduce) {
  body.vantra-saindo .main,
  .main { animation-duration: 1ms; }
}

/* ══════════════════════════════════════════════════════════════════════════
   VIDA NAS PECAS — o que o Lucas chamou de "efeitos"
   ══════════════════════════════════════════════════════════════════════════ */

/* CARTOES: entram em cascata visivel e sobem ao passar o mouse.
   O brilho e latao com pouca opacidade -- o metal aparecendo sob a luz. */
.card, .stat-card, .materia-card, .habilidade-card, .recurso-card {
  transition: transform 240ms var(--saida),
              border-color 240ms ease,
              box-shadow 240ms ease;
}
@media (hover: hover) and (pointer: fine) {
  .card:hover, .stat-card:hover, .materia-card:hover,
  .habilidade-card:hover, .recurso-card:hover {
    transform: translateY(-3px);
    border-color: color-mix(in srgb, var(--latao) 35%, transparent);
    box-shadow: 0 10px 30px -12px rgba(0,0,0,.6),
                0 0 0 1px color-mix(in srgb, var(--latao) 12%, transparent);
  }
}

/* BOTOES: o brilho atravessa a peca quando o mouse passa.
   Puro CSS, roda na placa de video, e o unico enfeite que eu deixei chamativo
   -- os outros ficam quietos para este aparecer (a regra do V0: gastar ousadia
   em um lugar so). */
.btn-primary, .btn-1, button.primary {
  position: relative;
  overflow: hidden;
}
.btn-primary::after, .btn-1::after, button.primary::after {
  content: '';
  position: absolute; inset: 0;
  background: linear-gradient(105deg, transparent 35%,
              rgba(255,255,255,.28) 50%, transparent 65%);
  transform: translateX(-120%);
  transition: transform 600ms var(--percurso);
  pointer-events: none;
}
@media (hover: hover) and (pointer: fine) {
  .btn-primary:hover::after, .btn-1:hover::after, button.primary:hover::after {
    transform: translateX(120%);
  }
}

/* NUMEROS grandes pulsam de leve quando a pagina abre -- o XP, a patente,
   a porcentagem. Uma vez so, sem repetir: e comemoracao, nao piscar. */
.stat-value, .xp-valor, .nivel-numero {
  animation: vantra-pulso 700ms var(--saida) both;
}
@keyframes vantra-pulso {
  0%   { opacity: 0; transform: scale(.94); }
  60%  { opacity: 1; transform: scale(1.02); }
  100% { opacity: 1; transform: scale(1); }
}

/* BARRAS DE PROGRESSO crescem em vez de aparecer prontas */
.progress-fill, .barra-progresso > *, .progresso-preenchido {
  transition: width 900ms var(--percurso);
}

/* ══════════════════════════════════════════════════════════════════════════
   PECAS — o acabamento
   ══════════════════════════════════════════════════════════════════════════
   Ordem do Lucas em 02/08/2026: "o site nao e baseado so em cores, ele
   precisa de botoes personalizados bonitos (...) quando passa o mouse por
   cima, eles levantam". Referencias: 21st.dev e o repertorio de
   micro-interacao de SaaS premium.

   As tecnicas daqui sao as mesmas que as bibliotecas caras usam. O que muda
   e que ali vem em React+Tailwind e aqui e CSS puro -- traduzido, nao copiado.

   TUDO roda em transform, opacity e filter: as tres coisas que a placa de
   video faz sozinha, entao nao engasga em celular fraco.
   ══════════════════════════════════════════════════════════════════════════ */

/* @property faz o navegador saber INTERPOLAR o angulo. Sem isto, gradiente
   girando so pula de um valor para outro. E o truque por tras de toda borda
   que "corre" nos sites caros. */
@property --giro {
  syntax: '<angle>'; inherits: false; initial-value: 0deg;
}

/* ── BOTOES ───────────────────────────────────────────────────────────────
   Tres coisas acontecem juntas no hover: a peca SOBE, a sombra CRESCE por
   baixo (e o que faz parecer que levantou de verdade, nao que so mudou de
   cor), e um brilho ATRAVESSA. */
.btn, .btn-roxo, .btn-fantasma, .btn-primary, .btn-1 {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  will-change: transform;
  transition: transform 220ms var(--saida),
              box-shadow 220ms var(--saida),
              background-color 200ms ease,
              border-color 200ms ease,
              color 200ms ease;
}

/* o brilho: uma faixa clara inclinada que cruza a peca */
.btn::before, .btn-roxo::before, .btn-primary::before, .btn-1::before {
  content: '';
  position: absolute; inset: -1px;
  background: linear-gradient(100deg, transparent 30%,
              rgba(255,255,255,.30) 48%, rgba(255,255,255,.10) 56%, transparent 72%);
  transform: translateX(-130%);
  transition: transform 700ms var(--percurso);
  pointer-events: none; z-index: 1;
}

@media (hover: hover) and (pointer: fine) {
  .btn:hover, .btn-roxo:hover, .btn-fantasma:hover,
  .btn-primary:hover, .btn-1:hover {
    transform: translateY(-2px);
  }
  .btn-roxo:hover, .btn-primary:hover, .btn-1:hover {
    box-shadow: 0 8px 20px -6px color-mix(in srgb, var(--latao) 55%, transparent),
                0 2px 6px -2px rgba(0,0,0,.5);
  }
  .btn-fantasma:hover {
    box-shadow: 0 8px 18px -8px rgba(0,0,0,.7);
    border-color: var(--latao);
  }
  .btn:hover::before, .btn-roxo:hover::before,
  .btn-primary:hover::before, .btn-1:hover::before { transform: translateX(130%); }
}
/* ao APERTAR, ela afunda -- e a resposta ao dedo */
.btn:active, .btn-roxo:active, .btn-fantasma:active,
.btn-primary:active, .btn-1:active {
  transform: translateY(0) scale(.975);
  transition-duration: var(--d-toque);
}

/* a setinha anda quando o mouse chega: "vai por aqui" */
.btn .seta, .btn [data-seta] { transition: transform 260ms var(--saida); display: inline-block; }
@media (hover: hover) and (pointer: fine) {
  .btn:hover .seta, .btn:hover [data-seta] { transform: translateX(4px); }
}

/* ── CARTOES ──────────────────────────────────────────────────────────────
   Sobem, ganham fio de latao e uma sombra funda. O ::after e um contorno
   que so acende no hover -- borda de verdade mudaria o tamanho e empurraria
   o layout. */
.card, .stat-card, .materia-card, .habilidade-card, .recurso-card {
  position: relative;
  will-change: transform;
  transition: transform 280ms var(--saida), box-shadow 280ms var(--saida);
}
.card::after, .stat-card::after, .materia-card::after,
.habilidade-card::after, .recurso-card::after {
  content: '';
  position: absolute; inset: 0;
  border-radius: inherit;
  padding: 1px;
  background: linear-gradient(140deg, color-mix(in srgb, var(--latao-c) 55%, transparent), color-mix(in srgb, var(--latao-c) 0%, transparent) 45%,
                                      color-mix(in srgb, var(--latao-c) 0%, transparent) 60%, color-mix(in srgb, var(--latao-c) 30%, transparent));
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor; mask-composite: exclude;
  opacity: 0;
  transition: opacity 280ms ease;
  pointer-events: none;
}
@media (hover: hover) and (pointer: fine) {
  .card:hover, .stat-card:hover, .materia-card:hover,
  .habilidade-card:hover, .recurso-card:hover {
    transform: translateY(-4px);
    box-shadow: 0 18px 40px -20px rgba(0,0,0,.75),
                0 4px 12px -6px rgba(0,0,0,.4);
  }
  .card:hover::after, .stat-card:hover::after, .materia-card:hover::after,
  .habilidade-card:hover::after, .recurso-card:hover::after { opacity: 1; }
}

/* LUZ QUE SEGUE O MOUSE. As coordenadas vem do JS (pecas.js); sem JS o cartao
   fica normal, sem quebrar nada. */
.card[data-luz]::before {
  content: '';
  position: absolute; inset: 0;
  border-radius: inherit;
  background: radial-gradient(340px circle at var(--mx, 50%) var(--my, 0%),
              color-mix(in srgb, var(--latao-c) 10%, transparent), transparent 62%);
  opacity: 0; transition: opacity 320ms ease;
  pointer-events: none;
}
@media (hover: hover) and (pointer: fine) {
  .card[data-luz]:hover::before { opacity: 1; }
}

/* ── DESTAQUE ─────────────────────────────────────────────────────────────
   Fio de latao girando na borda. Reservado ao que MERECE: a patente, a
   conquista recem-desbloqueada, a chamada principal. Regra do V0: gastar
   ousadia num lugar so. Se isto aparecer em tudo, deixa de valer. */
.destaque {
  position: relative;
  border-radius: var(--r-g);
  isolation: isolate;
}
.destaque::before {
  content: '';
  position: absolute; inset: 0;
  border-radius: inherit; padding: 1.5px;
  background: conic-gradient(from var(--giro),
              color-mix(in srgb, var(--latao-c) 0%, transparent) 0deg, var(--latao-c) 70deg,
              color-mix(in srgb, var(--latao-c) 0%, transparent) 150deg, color-mix(in srgb, var(--latao-c) 0%, transparent) 360deg);
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor; mask-composite: exclude;
  animation: gira 4s linear infinite;
  pointer-events: none; z-index: -1;
}
@keyframes gira { to { --giro: 360deg; } }

/* ── CAMPOS ───────────────────────────────────────────────────────────────
   O halo de latao ao focar diz "e aqui que voce esta digitando". */
input, textarea, select {
  transition: border-color 180ms ease, box-shadow 180ms ease, background-color 180ms ease;
}
/* 10/10/2026 (V6): caixa de marcar e botao de opcao saiam no AZUL do navegador (TAF, calendario...) --
   a unica cor fora da paleta no site. Tres paginas ja corrigiam a sua a mao; agora e uma regra so. */
input[type="checkbox"], input[type="radio"], progress, input[type="range"] { accent-color: var(--latao); }
input:focus, textarea:focus, select:focus {
  outline: none;
  border-color: var(--latao) !important;
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--latao) 16%, transparent);
}

/* ── LINHA QUE ACENDE ─────────────────────────────────────────────────────
   Para link de texto: o sublinhado CRESCE da esquerda em vez de aparecer. */
.link-vivo { position: relative; text-decoration: none; color: var(--latao-c); }
.link-vivo::after {
  content: ''; position: absolute; left: 0; bottom: -2px;
  width: 100%; height: 1px; background: currentColor;
  transform: scaleX(0); transform-origin: left;
  transition: transform 260ms var(--saida);
}
@media (hover: hover) and (pointer: fine) {
  .link-vivo:hover::after { transform: scaleX(1); }
}

/* ── PILULA DE ESTADO ─────────────────────────────────────────────────────
   Ponto que respira nos estados vivos ("estudando agora", "em dia"). */
.pilula {
  display: inline-flex; align-items: center; gap: .45rem;
  font-family: var(--dado); font-size: var(--t-xs); font-weight: 700;
  letter-spacing: .1em; text-transform: uppercase;
  padding: .3rem .7rem; border-radius: 999px;
  border: 1px solid currentColor;
}
.pilula::before {
  content: ''; width: 6px; height: 6px; border-radius: 50%;
  background: currentColor; box-shadow: 0 0 0 0 currentColor;
  animation: respira 2.4s var(--saida) infinite;
}
@keyframes respira {
  0%   { box-shadow: 0 0 0 0 currentColor; opacity: 1; }
  70%  { box-shadow: 0 0 0 6px transparent; opacity: .75; }
  100% { box-shadow: 0 0 0 0 transparent; opacity: 1; }
}
.pilula-ok    { color: var(--oliva-c); }
.pilula-aviso { color: var(--latao-c); }
.pilula-risco { color: var(--brasa-c); }

/* ═══ QUEM MARCOU "REDUZIR MOVIMENTO" NO APARELHO ═══
   Reduzido nao e zero: cor e opacidade ficam, deslocamento sai. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
    scroll-behavior: auto !important;
  }
  .cascata > * { opacity: 1; transform: none; }
  .stat-value, .xp-valor, .nivel-numero { animation: none; opacity: 1; }
  ::view-transition-old(*), ::view-transition-new(*) { animation: none !important; }
  .destaque::before, .pilula::before { animation: none; }
  .btn::before, .btn-roxo::before, .btn-primary::before, .btn-1::before { display: none; }
  .card:hover, .btn:hover, .btn-roxo:hover, .btn-fantasma:hover { transform: none; }
}

/* ═══ A DIVISA NA BARRA LATERAL ═══ V7, 02/08/2026
   Substitui o badge FREE/PRO que ficava no cartao do usuario.
   O badge de plano era a empresa falando do usuario ("voce e o cliente
   barato"); a divisa e o usuario falando de si ("voce e o Estrategista").
   O plano continua existindo -- so que em Minha Conta, que e onde ele
   interessa: na hora de decidir assinar. */
.user-divisa { margin-top: .3rem; }
.user-divisa .divisa { font-size: .58rem; letter-spacing: .1em; }
.user-divisa .divisa .nivel { padding: .22rem .5rem .22rem .8rem; }
.user-divisa .divisa .tag   { padding: .22rem .5rem; }

/* EM FORMACAO -- a peca mais importante do conjunto.
   Sem ela o usuario novo troca um badge que dizia algo por um espaco vazio,
   que e PIOR. "Calculista · 45%" mostra o alvo e a distancia: e um motivo
   para estudar hoje. Um cadeado nao e. */
.divisa .tag.emformacao {
  color: var(--texto-2);
  border-color: var(--linha);
  border-style: dashed;
  font-weight: 500;
}
/* 09/10/2026 (NUM-13): a palavra que diz que ainda nao foi conquistada */
/* 09/10/2026 (roadmap 3.21, RED-02): abas de uma secao (Conquistas: Condecoracoes | Quadro) */
.abas { display: flex; gap: var(--e1); flex-wrap: wrap; margin-top: var(--e3); border-bottom: 1px solid var(--linha); }
.abas a {
  padding: .55rem .9rem; min-height: 40px; display: inline-flex; align-items: center;
  font-family: var(--display); font-weight: 600; font-size: .85rem; color: var(--texto-2);
  text-decoration: none; border-bottom: 2px solid transparent; margin-bottom: -1px;
}
.abas a[aria-current="page"] { color: var(--papel); border-bottom-color: var(--latao); }
@media (hover: hover) { .abas a:hover { color: var(--papel); } }
.divisa .tag.emformacao .tag-rotulo { text-transform: none; letter-spacing: 0; font-weight: 400; color: var(--texto-3); }

/* ═══ CABECALHO DE PAGINA ═══ V6, 02/08/2026
   Medido antes: a barra do topo tinha SEIS aparencias diferentes entre as 10
   paginas -- alturas de 38 a 81px, e uma sem espaco embaixo. A causa nao era
   descuido de CSS: 3 paginas simplesmente NAO TINHAM subtitulo, entao a peca
   mudava de tamanho conforme a pagina.

   Titulo e subtitulo agora sao obrigatorios nas 10. O subtitulo nao e enfeite:
   e a frase que diz para que serve a tela -- quem abre "Progresso" pela
   primeira vez precisa saber o que vai encontrar ali. */
.main .topbar {
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: var(--e3); flex-wrap: wrap;
  margin-bottom: var(--e5);
  padding-bottom: var(--e3);
  border-bottom: 1px solid var(--linha);
}
.main .topbar .page-title { margin: 0; line-height: 1.15; }
.main .topbar .page-sub {
  margin: .35rem 0 0;
  color: var(--texto-2);
  font-size: var(--t-sm);
  max-width: 62ch;
}
.main .topbar-right { display: flex; align-items: center; gap: var(--e2); flex-wrap: wrap; }

/* ══════════════════════════════════════════════════════════════════════════
   TELA VAZIA — o que o usuario novo mais ve
   ══════════════════════════════════════════════════════════════════════════
   Medido em 02/08/2026: `progresso` e `edital` nao tinham NENHUM tratamento
   de vazio. Quem clicava no primeiro dia via um espaco em branco e nao sabia
   se o site tinha quebrado.

   Uma tela vazia boa faz TRES coisas, e as tres importam:
     1. mostra a FORMA do que vai aparecer (o fantasma), para a pessoa
        entender o que aquela tela e
     2. diz o que falta, em uma frase
     3. tem o botao que resolve, ali mesmo

   O fantasma e a parte que quase todo mundo pula. Texto sozinho explica; o
   fantasma faz a pessoa QUERER preencher -- ela ve o formato do resultado.
   ══════════════════════════════════════════════════════════════════════════ */
.vazio {
  position: relative;
  display: grid; place-items: center;
  text-align: center;
  padding: var(--e5) var(--e3);
  min-height: 16rem;
}
.vazio-fantasma {
  position: absolute; inset: 0;
  padding: var(--e3);
  display: grid; gap: .55rem; align-content: start;
  filter: blur(3px);
  opacity: .22;
  pointer-events: none;
  user-select: none;
  -webkit-mask-image: linear-gradient(180deg, #000 0%, transparent 88%);
          mask-image: linear-gradient(180deg, #000 0%, transparent 88%);
}
.vazio-fantasma > * {
  height: 2.6rem;
  background: var(--casco-2);
  border: 1px solid var(--linha);
  border-left: 3px solid var(--linha);
  border-radius: var(--r-p);
}
.vazio-conteudo { position: relative; z-index: 1; max-width: 34ch; }
.vazio-icone {
  display: inline-grid; place-items: center;
  width: 3rem; height: 3rem; margin-bottom: var(--e3);
  border: 1px solid var(--latao); border-radius: 50%;
  color: var(--latao-c);
  background: color-mix(in srgb, var(--latao) 8%, transparent);
}
.vazio-titulo {
  font-family: var(--display); font-weight: 800; font-size: var(--t-lg);
  letter-spacing: -.01em; margin: 0 0 var(--e2); color: var(--texto);
}
.vazio-texto { color: var(--texto-2); font-size: var(--t-sm); margin: 0 0 var(--e3); }
.vazio .btn { margin-top: var(--e1); }

/* ══════════════════════════════════════════════════════════════════════════
   ESQUELETO — o que aparece enquanto carrega
   ══════════════════════════════════════════════════════════════════════════
   Antes: a palavra "Carregando...". O problema nao e feiura -- e que a
   palavra nao diz NADA sobre o que vem, entao a tela pula de texto para
   conteudo e a pessoa perde a referencia.

   O esqueleto tem a FORMA do conteudo que vai chegar. A troca deixa de ser um
   salto e vira um preenchimento. Efeito medido em produto: a espera PARECE
   mais curta, mesmo com o tempo identico.
   ══════════════════════════════════════════════════════════════════════════ */
.esqueleto, .esqueleto-linha, .esqueleto-bloco {
  position: relative; overflow: hidden;
  background: var(--casco-2);
  border-radius: var(--r-p);
}
.esqueleto-linha { height: .85rem; margin-bottom: .5rem; }
.esqueleto-linha.curta { width: 45%; }
.esqueleto-linha.media { width: 70%; }
.esqueleto-bloco { height: 4.5rem; margin-bottom: .6rem; }

/* o brilho que atravessa e o que diferencia "carregando" de "quebrado":
   bloco cinza parado parece erro; bloco cinza que se move parece trabalho */
.esqueleto::after, .esqueleto-linha::after, .esqueleto-bloco::after {
  content: ''; position: absolute; inset: 0;
  background: linear-gradient(90deg, transparent, rgba(255,255,255,.055), transparent);
  transform: translateX(-100%);
  animation: esqueleto-brilho 1.4s ease-in-out infinite;
}
@keyframes esqueleto-brilho { to { transform: translateX(100%); } }

/* respeita quem pediu menos movimento -- sem o brilho, mas ainda cinza */
@media (prefers-reduced-motion: reduce) {
  .esqueleto::after, .esqueleto-linha::after, .esqueleto-bloco::after { animation: none; }
}

/* ══════════════════════════════════════════════════════════════════════════
   BOTAO: REVELA A SETA + ESTADO DE CARREGANDO
   ══════════════════════════════════════════════════════════════════════════
   Referencia escolhida pelo Lucas no 21st.dev: "get-started-button" do
   ozantekin -- "an animated call-to-action button that reveals a chevron icon
   on hover".

   O codigo de la e React + Tailwind + Framer Motion e nao roda aqui, entao o
   que foi trazido e a TECNICA, reconstruida em CSS puro.

   A REGRA QUE MAIS IMPORTA: a seta nao pode EMPURRAR nada.
   Se ela entrar no fluxo, o botao cresce ao passar o mouse e o layout inteiro
   danca. Por isso ela e posicionada por cima, e o que se move e so o rotulo --
   uns poucos pixels para a esquerda, abrindo espaco visual sem abrir espaco
   real. Botao que muda de tamanho no hover e o erro classico deste efeito.
   ══════════════════════════════════════════════════════════════════════════ */

.btn, .btn-roxo, .btn-fantasma, .btn-primary, .btn-1, .btn-upload, .btn-acessar {
  position: relative;
  isolation: isolate;
}

/* o rotulo precisa ser um elemento proprio para poder deslizar sozinho */
.btn > .rotulo, .btn-primary > .rotulo, .btn-roxo > .rotulo,
.btn-upload > .rotulo, .btn-acessar > .rotulo {
  display: inline-flex; align-items: center; gap: .5rem;
  transition: transform 260ms var(--saida);
}

/* a seta mora por cima, encostada na direita, e comeca invisivel e recuada.
   Nunca de opacity 0 + translate 0: entrar sem vir de lugar nenhum e o que
   faz a peca parecer que "pisca" em vez de "chegar". */
.btn > .seta-revela, .btn-primary > .seta-revela, .btn-roxo > .seta-revela,
.btn-upload > .seta-revela, .btn-acessar > .seta-revela {
  position: absolute;
  right: .8rem; top: 50%;
  display: inline-flex;
  transform: translateY(-50%) translateX(-7px);
  opacity: 0;
  transition: transform 280ms var(--saida), opacity 200ms ease;
  pointer-events: none;
}

@media (hover: hover) and (pointer: fine) {
  .btn:hover > .rotulo, .btn-primary:hover > .rotulo, .btn-roxo:hover > .rotulo,
  .btn-upload:hover > .rotulo, .btn-acessar:hover > .rotulo {
    transform: translateX(-9px);
  }
  .btn:hover > .seta-revela, .btn-primary:hover > .seta-revela,
  .btn-roxo:hover > .seta-revela, .btn-upload:hover > .seta-revela,
  .btn-acessar:hover > .seta-revela {
    transform: translateY(-50%) translateX(0);
    opacity: 1;
  }
}

/* quem navega por teclado tambem ve a seta -- o efeito nao pode ser
   exclusivo de quem tem mouse */
.btn:focus-visible > .rotulo, .btn-primary:focus-visible > .rotulo { transform: translateX(-9px); }
.btn:focus-visible > .seta-revela, .btn-primary:focus-visible > .seta-revela {
  transform: translateY(-50%) translateX(0); opacity: 1;
}

/* ── CARREGANDO ───────────────────────────────────────────────────────────
   Hoje NENHUM botao do site tem este estado. Quem clica em "Analisar edital"
   -- que chama a IA e demora segundos -- ve o botao parado, do jeitinho que
   estava. A pessoa clica de novo. E clicar de novo em chamada de IA e clicar
   de novo em dinheiro gasto.

   O botao guarda a LARGURA que tinha: trocar o texto por "Carregando..." faz
   a peca encolher ou crescer, e o layout pula. */
[data-carregando="true"] {
  pointer-events: none;
  cursor: wait;
}
[data-carregando="true"] > .rotulo,
[data-carregando="true"] > .seta-revela {
  opacity: 0;
  transform: translateX(0) scale(.96);
  transition: opacity 140ms ease, transform 140ms var(--saida);
}
[data-carregando="true"]::after {
  content: '';
  position: absolute; top: 50%; left: 50%;
  width: 1.05em; height: 1.05em;
  margin: -.525em 0 0 -.525em;
  border: 2px solid currentColor;
  border-right-color: transparent;
  border-radius: 50%;
  animation: girando 620ms linear infinite;
  opacity: .9;
}
@keyframes girando { to { transform: rotate(360deg); } }

/* Roda mais rapido do que parece necessario, e e de proposito: pesquisa de
   produto mostra que roda-de-espera rapida faz a espera PARECER menor, com o
   tempo real identico. */

@media (prefers-reduced-motion: reduce) {
  .btn > .seta-revela, .btn-primary > .seta-revela { transition: opacity 120ms ease; }
  [data-carregando="true"]::after { animation-duration: 1.6s; }
}

/* ══════════════════════════════════════════════════════════════════════════
   CARTAO COM GRAFICO — a semana de estudo
   ══════════════════════════════════════════════════════════════════════════
   Referencia escolhida pelo Lucas: "activity-chart-card" do ravikatiyar162
   -- titulo, total, e barras por dia da semana que sobem em cascata.

   Cai como uma luva aqui: o Vantra JA TEM esse dado (horas do cronometro por
   dia) e nunca o mostrou em forma nenhuma. Nao e enfeite novo -- e dado que
   ja existia e estava invisivel.

   Sem biblioteca de grafico: sao sete divs com altura em porcentagem. Uma
   biblioteca de grafico para desenhar sete barras custaria mais bytes que a
   pagina inteira.
   ══════════════════════════════════════════════════════════════════════════ */
.gsemana { display: grid; gap: var(--e3); }
.gsemana-topo {
  display: flex; align-items: baseline; justify-content: space-between; gap: var(--e2);
}
.gsemana-rotulo {
  font-family: var(--dado); font-size: var(--t-xs); letter-spacing: .14em;
  text-transform: uppercase; color: var(--texto-3);
}
.gsemana-total {
  font-family: var(--dado); font-size: var(--t-xl); font-weight: 700;
  color: var(--texto); font-variant-numeric: tabular-nums; line-height: 1;
}
.gsemana-barras {
  display: grid; grid-template-columns: repeat(7, 1fr);
  gap: .4rem; align-items: end; height: 6.5rem;
}
/* 30/09/2026: coluna em flex com um TRILHO que ocupa a altura que sobra; a
   barra fica presa ao fundo dele, e o % da altura passa a valer de verdade. */
.gsemana-col { display: flex; flex-direction: column; gap: .4rem; height: 100%; min-height: 0; }
.gsemana-trilho { flex: 1; position: relative; min-height: 0; }
.gsemana-barra {
  position: absolute; left: 0; right: 0; bottom: 0;
  background: linear-gradient(180deg, var(--latao-c), var(--latao));
  border-radius: var(--r-p) var(--r-p) 0 0;
  min-height: 3px;
  /* as barras crescem DE BAIXO, uma depois da outra. Crescer e o que faz
     parecer que o dado esta sendo medido; aparecer pronto nao diz nada. */
  transform-origin: bottom;
  transform: scaleY(0);
  animation: gsemana-sobe 520ms var(--saida) forwards;
}
/* 30/09/2026: era ".vazio" -- o mesmo nome do componente de estado vazio
   (min-height 16rem, logo acima): o dia sem estudo saia com 80px, o MAIOR. */
.gsemana-barra.sem-estudo { background: var(--casco-2); }
.gsemana-barra.hoje { box-shadow: 0 0 0 1px var(--latao-c); }
.gsemana-col:nth-child(1) .gsemana-barra { animation-delay:  40ms; }
.gsemana-col:nth-child(2) .gsemana-barra { animation-delay:  90ms; }
.gsemana-col:nth-child(3) .gsemana-barra { animation-delay: 140ms; }
.gsemana-col:nth-child(4) .gsemana-barra { animation-delay: 190ms; }
.gsemana-col:nth-child(5) .gsemana-barra { animation-delay: 240ms; }
.gsemana-col:nth-child(6) .gsemana-barra { animation-delay: 290ms; }
.gsemana-col:nth-child(7) .gsemana-barra { animation-delay: 340ms; }
@keyframes gsemana-sobe { to { transform: scaleY(1); } }

.gsemana-dia {
  font-family: var(--dado); font-size: .6rem; text-align: center;
  color: var(--texto-3); letter-spacing: .04em;
}
.gsemana-col.hoje .gsemana-dia { color: var(--latao-c); font-weight: 700; }

@media (prefers-reduced-motion: reduce) {
  .gsemana-barra { animation: none; transform: scaleY(1); }
}

/* ══════════════════════════════════════════════════════════════════════════
   EFEITO BENTO — o conteudo desliza quando o mouse chega
   ══════════════════════════════════════════════════════════════════════════
   Referencia escolhida pelo Lucas: o "bento-grid" do manuarora700 (Aceternity).
   ⚠️ Nao consegui o codigo-fonte -- a documentacao mostra so a estrutura, e o
   efeito mora no arquivo do componente. O que esta aqui e a tecnica visivel
   reconstruida, nao copia.

   O gesto: ao passar o mouse, o BLOCO DE TEXTO desliza alguns pixels para o
   lado, enquanto o cartao fica parado. E sutil de proposito -- o cartao ja
   sobe e ja acende o fio de latao; se o texto tambem saltasse, virava festa.

   So em `.bento`, nao em todo cartao: se tudo se mexe, nada chama atencao. */
.bento { position: relative; overflow: hidden; }
.bento-corpo { transition: transform 280ms var(--saida); }
.bento-visual {
  border-radius: var(--r-p);
  background:
    radial-gradient(120% 90% at 20% 0%, color-mix(in srgb, var(--latao) 16%, transparent), transparent 60%),
    var(--casco-2);
  border: 1px solid var(--linha);
  min-height: 5.5rem;
  margin-bottom: var(--e3);
  overflow: hidden;
}
/* Duas formas de usar, e a segunda existe porque eu quase quebrei o dashboard:
   1. com <div class="bento-corpo"> explicito, quando eu escrevo a peca do zero
   2. SEM tocar no HTML, quando o cartao ja existe -- ai deslizam os filhos
      diretos. Tentei envolver o conteudo dos cartoes prontos num corpo novo e
      abri duas <div> sem fechar; o balanco de tags acusou (101 x 103) antes de
      ir para o ar, mas a licao fica: mexer na ESTRUTURA de HTML pronto por
      script e caro. Efeito visual deve ser resolvido no CSS. */
@media (hover: hover) and (pointer: fine) {
  .bento:hover > .bento-corpo { transform: translateX(7px); }
  .bento:not(:has(> .bento-corpo)):hover > *:not(.bento-visual) { transform: translateX(7px); }
}
.bento:not(:has(> .bento-corpo)) > * { transition: transform 280ms var(--saida); }
@media (prefers-reduced-motion: reduce) {
  .bento:hover .bento-corpo { transform: none; }
}

/* ══════════════════════════════════════════════════════════════════════════
   PECAS C4 a C11 — as referencias que o Lucas escolheu no 21st.dev
   ══════════════════════════════════════════════════════════════════════════
   ⚠️ NENHUMA DELAS VEIO COM CODIGO. O 21st.dev mostra o exemplo de uso e
   guarda o fonte; alem disso tudo la e React + Tailwind + Framer Motion, que
   nao roda neste projeto. Entao o que esta aqui e a TECNICA que cada nome
   descreve, reconstruida em CSS puro e adaptada a nossa direcao visual --
   como ele pediu: "logico que voce vai adaptar, nao e pra escrever o mesmo".
   ══════════════════════════════════════════════════════════════════════════ */

/* ── C7 · ANEL DE PORCENTAGEM (radial progress) ───────────────────────────
   Serve ao dominio por materia, que hoje so aparece como barra. O anel diz
   "quanto de um todo"; a barra diz "quanto ja andou". Sao leituras
   diferentes, e materia pede a primeira.
   conic-gradient com --pct: um elemento, sem SVG, sem biblioteca. */
.anel {
  --pct: 0;
  position: relative;
  width: 4.5rem; height: 4.5rem;
  border-radius: 50%;
  display: grid; place-items: center;
  background: conic-gradient(var(--latao) calc(var(--pct) * 1%), var(--linha) 0);
  transition: background 700ms var(--percurso);
}
.anel::before {
  content: ""; position: absolute; inset: 5px;
  border-radius: 50%; background: var(--casco);
}
.anel > span {
  position: relative;
  font-family: var(--dado); font-size: var(--t-sm); font-weight: 700;
  color: var(--texto); font-variant-numeric: tabular-nums;
}
.anel.baixo { background: conic-gradient(var(--brasa)   calc(var(--pct)*1%), var(--linha) 0); }
.anel.medio { background: conic-gradient(var(--latao)   calc(var(--pct)*1%), var(--linha) 0); }
.anel.alto  { background: conic-gradient(var(--oliva-c) calc(var(--pct)*1%), var(--linha) 0); }
.anel.p { width: 2.6rem; height: 2.6rem; }
.anel.p::before { inset: 3px; }
.anel.p > span { font-size: .62rem; }

/* ── C7 · BARRA COM MARCOS ────────────────────────────────────────────────
   Os marcos sao os degraus que valem alguma coisa: 25, 50, 70 (onde nasce a
   TAG) e 90 (a versao elite). Sem eles a barra so informa; com eles ela da
   alvo -- a pessoa ve o proximo degrau e sabe quanto falta. */
.barra-marcos {
  position: relative; height: 8px;
  background: var(--breu); border: 1px solid var(--linha);
  border-radius: 999px;
}
.barra-marcos > i {
  display: block; height: 100%; border-radius: 999px;
  background: linear-gradient(90deg, var(--latao), var(--latao-c));
  transition: width 900ms var(--percurso);
}
.barra-marcos > b {
  position: absolute; top: 50%; width: 2px; height: 10px;
  background: var(--linha); transform: translate(-50%, -50%);
}
.barra-marcos > b.batido { background: var(--latao-c); }

/* ── C10 · ESQUELETO, variacoes ───────────────────────────────────────────
   Formas diferentes para conteudos diferentes: circulo para avatar, linha
   para texto, bloco para cartao. Esqueleto que nao tem a FORMA do que vem
   nao ajuda ninguem -- vira so um retangulo cinza. */
.esqueleto-circulo {
  width: 2.5rem; height: 2.5rem; border-radius: 50%;
  background: var(--casco-2); position: relative; overflow: hidden;
}
.esqueleto-circulo::after {
  content: ""; position: absolute; inset: 0;
  background: linear-gradient(90deg, transparent, rgba(255,255,255,.055), transparent);
  transform: translateX(-100%);
  animation: esqueleto-brilho 1.4s ease-in-out infinite;
}
.esqueleto-grupo { display: flex; gap: var(--e2); align-items: center; }
.esqueleto-grupo > div:last-child { flex: 1; }

/* ── C10 · TEXTO CINTILANTE (shimmer) ─────────────────────────────────────
   Para o texto que espera resposta da IA. "Lendo seu edital..." com a luz
   passando por cima diz "estou trabalhando"; o mesmo texto parado diz
   "travei". A diferenca e so uma animacao, e muda tudo na percepcao. */
.cintila {
  background: linear-gradient(90deg,
    var(--texto-3) 0%, var(--texto) 42%, var(--latao-c) 50%,
    var(--texto) 58%, var(--texto-3) 100%);
  background-size: 220% 100%;
  -webkit-background-clip: text; background-clip: text;
  color: transparent;
  animation: cintilar 2.2s linear infinite;
}
@keyframes cintilar { to { background-position: -220% 0; } }

/* ── C4 · CAMINHO DE VOLTA (breadcrumb) ───────────────────────────────────
   So faz sentido onde ha profundidade de verdade. No Vantra isso e raro:
   vale para "Progresso > Materia" e "Conquistas > Habilidade".
   NAO vai em toda tela -- caminho de uma etapa so e ruido. */
.caminho {
  display: flex; align-items: center; gap: .4rem; flex-wrap: wrap;
  font-family: var(--dado); font-size: var(--t-xs);
  color: var(--texto-3); margin-bottom: var(--e2);
}
.caminho a { color: var(--texto-2); text-decoration: none; transition: color 160ms ease; }
.caminho a:hover { color: var(--latao-c); }
.caminho .sep { opacity: .5; }
.caminho [aria-current] { color: var(--texto); }

/* ── C9 · FOLHA (sheet) ───────────────────────────────────────────────────
   No celular sobe do rodape: fica ao alcance do polegar e nao cobre a tela
   inteira. No computador entra pela lateral, que e o gesto certo la.
   E o mesmo componente -- o que muda e o lado de onde ele vem. */
.folha {
  position: fixed; z-index: 60;
  background: var(--casco); border: 1px solid var(--linha);
  box-shadow: 0 -20px 60px -30px rgba(0,0,0,.95);
  transition: transform var(--d-fecha) var(--gaveta);
}
.folha:not([data-aberta]) { pointer-events: none; }
/* 768px e a largura do iPad em retrato. As 11 paginas do app e o CSS do menu
   em vantra.js tratam 768 como CELULAR (max-width: 768px). Ate 15/09/2026 este
   par usava 767/768 e discordava deles: em 768px exatos a .folha abria no modo
   desktop enquanto a barra lateral estava escondida no modo celular, e o
   .btn-recolher ganhava display:flex dentro de uma barra fora da tela.
   Nao dava defeito visivel -- o botao estava escondido junto -- mas era uma
   armadilha esperando a primeira regra que saisse de dentro da barra.
   Alinhado a convencao das paginas: 768 e celular, 769 em diante e desktop. */
@media (max-width: 768px) {
  .folha {
    left: 0; right: 0; bottom: 0;
    border-radius: var(--r-g) var(--r-g) 0 0;
    padding: var(--e4) var(--e3) calc(var(--e4) + env(safe-area-inset-bottom));
    transform: translateY(101%);
  }
  .folha[data-aberta] { transform: translateY(0); transition-duration: var(--d-painel); }
  /* a alcinha diz "isto se puxa" sem precisar escrever */
  .folha::before {
    content: ""; position: absolute; top: .6rem; left: 50%;
    width: 2.4rem; height: 4px; border-radius: 999px;
    background: var(--linha); transform: translateX(-50%);
  }
}
@media (min-width: 769px) {
  .folha {
    top: 0; bottom: 0; right: 0; width: min(28rem, 92vw);
    border-radius: var(--r-g) 0 0 var(--r-g); padding: var(--e4);
    transform: translateX(101%);
  }
  .folha[data-aberta] { transform: translateX(0); transition-duration: var(--d-painel); }
}

/* ── C11 · CONQUISTA DESBLOQUEADA ────────────────────────────────────────
   O momento mais importante da gamificacao inteira -- e hoje ele NAO EXISTE:
   a pessoa sobe de patente e so o texto muda de lugar.

   Tres coisas acontecem juntas, e a ordem importa:
     1. o fundo escurece   -> isola o momento do resto da tela
     2. a insignia CHEGA   -> de 88% e girada 8 graus, assentando no lugar
     3. os raios abrem     -> por tras, girando devagar

   A curva da insignia passa de 1 e volta (cubic-bezier com 1.5): e o
   "assentar" de uma medalha sendo colocada, nao um retangulo aparecendo. */
.veu-conquista {
  position: fixed; inset: 0; z-index: 80;
  display: grid; place-items: center; padding: var(--e3);
  background: rgba(6,11,16,.82); backdrop-filter: blur(4px);
  opacity: 0; pointer-events: none;
  transition: opacity var(--d-fecha) var(--saida);
}
.veu-conquista[data-aberto] {
  opacity: 1; pointer-events: auto; transition-duration: var(--d-painel);
}
.conquista {
  position: relative; text-align: center; max-width: 22rem;
  padding: var(--e5) var(--e4);
  background: var(--casco); border: 1px solid var(--latao);
  border-radius: var(--r-g);
  transform: scale(.88) rotate(-8deg); opacity: 0;
  transition: transform 520ms cubic-bezier(.2, 1.5, .4, 1),
              opacity 260ms var(--saida);
}
.veu-conquista[data-aberto] .conquista { transform: scale(1) rotate(0deg); opacity: 1; }

/* os raios, atras da peca */
.conquista::before {
  content: ""; position: absolute; inset: -40%;
  background: conic-gradient(from 0deg,
    color-mix(in srgb, var(--latao-c) 16%, transparent) 0deg 6deg,  transparent 6deg 26deg,
    color-mix(in srgb, var(--latao-c) 16%, transparent) 26deg 32deg, transparent 32deg 52deg,
    color-mix(in srgb, var(--latao-c) 16%, transparent) 52deg 58deg, transparent 58deg 78deg);
  border-radius: 50%; opacity: 0; z-index: -1;
  transition: opacity 500ms ease;
}
.veu-conquista[data-aberto] .conquista::before {
  opacity: 1; animation: raios 22s linear infinite;
}
@keyframes raios { to { transform: rotate(360deg); } }

.conquista-medalha {
  width: 5rem; height: 5rem; margin: 0 auto var(--e3);
  display: grid; place-items: center; border-radius: 50%;
  background: linear-gradient(160deg, var(--latao-c), var(--latao));
  color: #121A24;
  box-shadow: 0 0 0 6px color-mix(in srgb, var(--latao) 14%, transparent), 0 14px 34px -14px color-mix(in srgb, var(--latao) 60%, transparent);
}
.conquista-rot {
  display: block; margin-bottom: var(--e2);
  font-family: var(--dado); font-size: .64rem; letter-spacing: .22em;
  text-transform: uppercase; color: var(--latao-c);
}
.conquista-nome {
  font-family: var(--display); font-weight: 800; font-size: var(--t-xl);
  line-height: 1.1; margin: 0 0 var(--e2); color: var(--texto);
}
.conquista-frase { color: var(--texto-2); font-size: var(--t-sm); margin: 0 0 var(--e4); }

@media (prefers-reduced-motion: reduce) {
  .conquista { transform: none; transition: opacity 200ms ease; }
  .veu-conquista[data-aberto] .conquista::before { animation: none; }
  .cintila { animation: none; background: none; color: var(--texto-2); }
  .esqueleto-circulo::after { animation: none; }
}

/* ══════════════════════════════════════════════════════════════════════════
   C3 · BARRA LATERAL RECOLHIVEL
   ══════════════════════════════════════════════════════════════════════════
   Referencias que o Lucas escolheu: "dashboard-with-collapsible-sidebar"
   (uniquesonu) e "sidebar" (manuarora700). Nenhuma das duas entregou codigo,
   entao aqui esta a tecnica padrao adaptada.

   POR QUE ISTO VALE A PENA NO VANTRA:
   a barra ocupa 240px fixos. Numa tela de notebook de 1366px isso e 18% da
   largura, permanentemente, para um menu que a pessoa usa poucas vezes por
   sessao. Recolher devolve esse espaco ao CONTEUDO, que e o que ela veio ver.

   A LARGURA E UMA VARIAVEL, e isso importa: `--sidebar-w` ja era usada pelo
   `.main` para calcular a margem. Trocar so o valor da variavel move a barra
   E o conteudo juntos, sem uma linha a mais. Se fosse pixel fixo em dois
   lugares, um deles ficaria para tras.
   ══════════════════════════════════════════════════════════════════════════ */

:root { --sidebar-recolhida: 68px; }

body[data-menu="recolhido"] { --sidebar-w: var(--sidebar-recolhida); }

.sidebar { transition: width var(--d-painel) var(--gaveta); }
.main    { transition: margin-left var(--d-painel) var(--gaveta),
                       max-width  var(--d-painel) var(--gaveta); }

/* o que some ao recolher: rotulo, titulo de secao, nome do usuario.
   Some por opacidade E largura -- so opacidade deixaria o texto ocupando
   espaco invisivel e a barra nao encolheria de verdade. */
body[data-menu="recolhido"] .sidebar .nav-link > span,
body[data-menu="recolhido"] .sidebar .nav-link { }

/* 🐛 O .sidebar-logo span NAO entra aqui: eu o inclui na primeira versao
   achando que era rotulo, e o logo virou "Ast al" -- aquele span e a letra R
   colorida de Vantra. Erro pego na foto, nao no codigo. Recolhido, o logo
   inteiro vira so a inicial (regra logo abaixo). */
body[data-menu="recolhido"] .nav-label,
body[data-menu="recolhido"] .user-details,
body[data-menu="recolhido"] .btn-logout span {
  opacity: 0;
  width: 0;
  overflow: hidden;
  white-space: nowrap;
  transition: opacity 120ms ease, width var(--d-painel) var(--gaveta);
}

/* o texto solto dentro dos links (que nao esta em <span>) precisa sumir tambem */
body[data-menu="recolhido"] .sidebar .nav-link {
  justify-content: center;
  padding-left: 0; padding-right: 0;
  font-size: 0;                      /* zera o texto solto */
  gap: 0;
}
body[data-menu="recolhido"] .sidebar .nav-link svg { font-size: var(--t-md); }
body[data-menu="recolhido"] .sidebar { padding-left: .5rem; padding-right: .5rem; }

/* o logo recolhido vira a inicial, centralizada. Esconder o texto e manter a
   caixa deixaria um buraco no topo da barra. */
body[data-menu="recolhido"] .sidebar-logo {
  font-size: 0; text-align: center;
}
body[data-menu="recolhido"] .sidebar-logo::before {
  content: "A";
  font-family: var(--display); font-weight: 800; font-size: 1.5rem;
  color: var(--papel);
}
body[data-menu="recolhido"] .sidebar-logo span { display: none; }
body[data-menu="recolhido"] .user-info { justify-content: center; padding: .5rem; }
body[data-menu="recolhido"] .btn-logout { justify-content: center; }

/* DICA AO PASSAR O MOUSE: recolhido, o icone sozinho nao diz o que e.
   Sem isto, recolher troca espaco por confusao -- que e um mau negocio. */
body[data-menu="recolhido"] .sidebar .nav-link { position: relative; }
body[data-menu="recolhido"] .sidebar .nav-link::after {
  content: attr(data-dica);
  position: absolute; left: calc(100% + .5rem); top: 50%;
  transform: translateY(-50%) translateX(-4px);
  background: var(--casco-2); color: var(--texto);
  border: 1px solid var(--linha); border-radius: var(--r-p);
  padding: .35rem .6rem;
  font-family: var(--corpo); font-size: .8rem; white-space: nowrap;
  opacity: 0; pointer-events: none; z-index: 70;
  transition: opacity var(--d-dica) ease, transform var(--d-dica) var(--saida);
  box-shadow: 0 8px 22px -12px rgba(0,0,0,.9);
}
@media (hover: hover) and (pointer: fine) {
  body[data-menu="recolhido"] .sidebar .nav-link:hover::after {
    opacity: 1; transform: translateY(-50%) translateX(0);
  }
}

/* O BOTAO DE RECOLHER */
.btn-recolher {
  display: none;                      /* so no computador -- ver abaixo */
  align-items: center; justify-content: center; gap: .5rem;
  width: 100%; margin-bottom: var(--e2);
  padding: .5rem;
  background: transparent; border: 1px solid var(--linha);
  border-radius: var(--r-p); color: var(--texto-3);
  cursor: pointer;
  transition: color var(--d-dica) ease, border-color var(--d-dica) ease;
}
.btn-recolher svg { transition: transform var(--d-painel) var(--gaveta); }
body[data-menu="recolhido"] .btn-recolher svg { transform: rotate(180deg); }
@media (hover: hover) and (pointer: fine) {
  .btn-recolher:hover { color: var(--latao-c); border-color: var(--latao); }
}

/* No celular a barra ja e uma gaveta que abre e fecha inteira (bloco 8.11).
   Recolher para icones ali nao resolve nada e ainda brigaria com a gaveta.
   Por isso o botao so existe a partir de 768px. */
@media (min-width: 769px) { .btn-recolher { display: flex; } }
/* 10/10/2026: o botao e criado pelo menu.js DEPOIS da primeira pintura -- "Minha conta" e "Encerrar sessao"
   desciam 42px logo ao abrir cada pagina (medido: banco, conquistas, progresso, tags...). O lugar dele fica
   reservado desde o inicio, com a MESMA altura; quando ele chega, a reserva sai e nada se mexe. */
.btn-recolher { height: 34px; box-sizing: border-box; }
@media (min-width: 769px) {
  .sidebar-bottom:not(:has(> .btn-recolher))::before { content: ''; display: block; height: 34px; margin-bottom: var(--e2); }
}
@media (max-width: 768px) { body[data-menu="recolhido"] { --sidebar-w: 240px; } }

@media (prefers-reduced-motion: reduce) {
  .sidebar, .main, .btn-recolher svg { transition: none; }
}

/* ══════════════════════════════════════════════════════════════════════════
   A DIVISA NO CARTAO DO USUARIO — ajuste de 02/08/2026
   ══════════════════════════════════════════════════════════════════════════
   O Lucas mandou print: a peca estava alta demais e VAZANDO para fora do
   cartao. Medido antes de mexer:

       cartao 207px · divisa 154px · VAZA 7px para fora · ALTURA 39px

   Os 39px vinham do line-height do corpo (1.65) sendo herdado por um texto
   de 9px: sobrava mais que o dobro de espaco em cima e embaixo da letra.
   Nao era padding excessivo -- era entrelinha de paragrafo aplicada a um
   selo. Por isso "diminuir o padding" nao teria resolvido.

   Alvo: caber inteira, texto centrado na vertical, e ficar RETA como ele
   pediu -- selo, nao balao.
   ══════════════════════════════════════════════════════════════════════════ */

.user-divisa {
  margin-top: .35rem;
  min-width: 0;

  max-width: 100%;
}

.user-divisa .divisa {
  font-size: .56rem;
  letter-spacing: .08em;
  line-height: 1;             /* ← a correcao principal: 39px viram 22px */
  max-width: 100%;
  min-width: 0;
}

.user-divisa .divisa .nivel,
.user-divisa .divisa .tag {
  line-height: 1;
  display: flex;
  align-items: center;        /* texto no meio da altura, na vertical */
  min-width: 0;
  white-space: nowrap;        /* <- a segunda causa dos 38px: "3o Sargento BM"
                                 quebrava em duas linhas e dobrava a altura */
}

/* Quem manda na largura: o avatar come 72px dos 207 do cartao, sobram 135
   para a divisa. Entao a PATENTE nunca encolhe (ela e a identidade) e a TAG
   absorve o aperto, cortando com reticencias quando o nome e longo.
   Nome cortado ainda comunica; cartao estourado nao. */
.user-divisa .divisa .nivel { flex: 0 0 auto; }
.user-divisa .divisa .tag   { flex: 0 1 auto; }

/* o bico encolhe junto: 0.5rem num selo de 22px ficava desproporcional */
.user-divisa .divisa .nivel {
  padding: .34rem .45rem .34rem .72rem;
  clip-path: polygon(0 0, 100% 0, calc(100% - .34rem) 50%, 100% 100%, 0 100%, .34rem 50%);
}

.user-divisa .divisa .tag {
  /* Medido: com a patente ja abreviada, "3o SGT BM" + "CALCULISTA" pediam
     146px num espaco de 135. Faltavam 11. Vieram de reduzir o espacamento
     entre letras e o respiro lateral -- ajuste no ESPACO, nao no tamanho da
     letra, que continua legivel. */
  letter-spacing: .04em;
  padding: .34rem .38rem;
  /* nome comprido nao empurra mais o cartao: corta com reticencias.
     Um nome cortado ainda comunica; um cartao estourado nao. */
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* quem ainda nao tem tag: texto curto, para o selo ficar do tamanho do
   cartao. "sem especialidade" e uma frase; aqui cabe uma palavra. */
.user-divisa .divisa .tag.vazia {
  font-size: .58rem;
  letter-spacing: 0;
  opacity: .75;
}

/* o cartao inteiro tambem precisa segurar o conteudo -- sem isto o flex
   deixa o filho transbordar mesmo com o filho comportado */
.user-info { min-width: 0; }
/* SEM overflow:hidden aqui. Foi esta linha, escrita por mim, que cortou o
   "s" de Lucas -- somada ao text-overflow do app.css. Nome de pessoa nao se
   corta: se faltar espaco, quem cede e o cartao. */

/* 🐛 "Luca" em vez de "Lucas": o corte com reticencias estava apertado demais
   para o nome, que e curto e importante. Nome de pessoa nao se corta -- se
   houver aperto, quem cede e o tamanho da letra, nao a ultima sílaba. */


/* ══════════════════════════════════════════════════════════════════════════
   O CARTAO DO USUARIO NAO PODE MUDAR DE TAMANHO
   ══════════════════════════════════════════════════════════════════════════
   🐛 MEDIDO em 02/08/2026, a partir do relato do Lucas: "a sidebar meio que
   sobe e desce, da um bug quando eu clico em outra".

       cartao sem a divisa .... 62px  -> menu comeca em y=208
       cartao com a divisa .... 79px  -> menu comeca em y=225
       >>> O MENU DESCIA 17px EM TODA TROCA DE PAGINA <<<

   A sequencia era: a pagina pinta a barra lateral com a divisa VAZIA, os
   dados chegam do banco meio segundo depois, a divisa nasce, e empurra o
   menu inteiro para baixo.

   A transicao estava certa; quem pulava era o conteudo por baixo dela.

   A CORRECAO E RESERVAR O ESPACO ANTES DE PRECISAR DELE.
   O lugar da divisa ja existe desde o primeiro quadro, ocupado por um
   esqueleto. Quando o dado chega, ele PREENCHE o espaco em vez de criar
   espaco. Nada se move.

   Vale para qualquer coisa que carrega depois: reservar a altura final e a
   diferenca entre "a tela montou" e "a tela pulou na cara da pessoa".
   ══════════════════════════════════════════════════════════════════════════ */

.user-divisa {
  min-height: 22px;          /* a altura exata da divisa montada */
  display: flex;
  align-items: center;
}

/* o esqueleto so aparece enquanto o espaco esta vazio: :empty some sozinho
   no instante em que o JS escreve dentro */
.user-divisa:empty::before {
  content: "";
  display: block;
  width: 7rem; height: 14px;
  border-radius: var(--r-p);
  background: var(--casco-2);
  opacity: .55;
}

/* O NOME tambem reserva a linha dele.
   "Carregando..." e mais largo que "Lucas": o texto encolhia e a linha
   piscava. Com altura fixa e esqueleto, a troca e so o conteudo. */
.user-name { min-height: 1.15em; }
.user-name:empty::before {
  content: "";
  display: block;
  width: 4.5rem; height: 12px;
  border-radius: var(--r-p);
  background: var(--casco-2);
  opacity: .55;
}

/* ── NOME LONGO, para nao virar problema quando alguem assinar ──────────────
   O Lucas levantou: "se uma pessoa comprar e entrar, o nome dele vai ficar
   cortado?". Pergunta certa -- e a resposta tinha de ser nao.

   Mostramos so o PRIMEIRO nome, entao o caso ruim e um primeiro nome muito
   longo (Alessandra, Wellington). A solucao nao e cortar nem deixar vazar:
   e a letra encolher ate caber, ate um piso legivel. Cortar so acontece em
   nome absurdo, e ai com reticencias e com o nome inteiro no title. */




/* ══════════════════════════════════════════════════════════════════════════
   REGRAS UNIFICADAS — 03/08/2026
   ══════════════════════════════════════════════════════════════════════════
   Estavam declaradas IDENTICAS em varias paginas. Cada copia era um lugar a
   mais para divergir sozinha -- foi assim que o menu, as cores e o cartao
   acabaram diferentes entre telas nesta semana.

   ⚠️ SO SUBIU O QUE E IDENTICO. Foi conferido corpo a corpo antes de remover:
   se uma copia divergisse, a regra inteira ficava de fora. Unificar
   divergencia e decisao de design, nao refatoracao -- e nao e o que o Lucas
   pediu aqui.

   As que divergem de verdade continuam nas paginas, e estao listadas no
   relatorio da varredura.
   ══════════════════════════════════════════════════════════════════════════ */

/* estava em 5 paginas, identica em todas */
.plan-beta { color: var(--gold); }

/* estava em 5 paginas, identica em todas */
.plan-pro { color: var(--green); }

/* estava em 4 paginas, identica em todas */
.topbar-right {
      display: flex;
      align-items: center;
      gap: 0.75rem;
    }

/* estava em 3 paginas, identica em todas */
.card-label {
      font-size: 0.72rem;
      font-weight: 600;
      letter-spacing: 0.1em;
      text-transform: uppercase;
      color: var(--muted);
      margin-bottom: 0.5rem;
    }

/* estava em 3 paginas, identica em todas */
.card-value {
      font-family: var(--display);
      font-weight: 800;
      font-size: 1.8rem;
      color: var(--white);
      line-height: 1;
    }

/* estava em 3 paginas, identica em todas */
.card-sub {
      font-size: 0.8rem;
      color: var(--muted);
      margin-top: 0.25rem;
    }

/* estava em 3 paginas, identica em todas */
.btn-upload {
      display: inline-flex;
      align-items: center;
      gap: 0.5rem;
      background: var(--purple);
      color: var(--breu);
      border: none;
      padding: 0.7rem 1.5rem;
      border-radius: var(--r-g);
      font-family: var(--corpo);
      font-weight: 600;
      font-size: 0.9rem;
      cursor: pointer;
      transition: opacity 0.2s, transform 0.15s;
    }

/* estava em 3 paginas, identica em todas */
.empty-icon { font-size: var(--t-2xl); margin-bottom: 0.75rem; }


/* ══════════════════════════════════════════════════════════════════════════
   O CARTAO DO USUARIO — fonte unica
   ══════════════════════════════════════════════════════════════════════════
   🐛 Corrigido em 03/08/2026 a partir de quatro prints do Lucas que mostravam
   o MESMO cartao quebrado de TRES maneiras diferentes: em uma pagina so o
   avatar aparecia, em outra o nome saia "Luca", em outra a divisa vazava.

   Tres sintomas, UMA causa: eu tinha escrito min-width:0 sem flex:1.
   Isso autoriza o elemento a encolher e NAO manda ele crescer -- entao ele
   colapsa para ZERO. Medido: .user-details com 0px de largura em 9 paginas.
   O que aparecia em cada tela dependia so do resto do CSS dela, e por isso
   cada uma quebrava de um jeito.

   O min-width:0 continua necessario: sem ele o texto longo empurraria o
   cartao. O que faltava era o par -- flex:1 para ocupar o espaco que sobra
   depois do avatar.

   ⚠️ NAO redeclarar .user-details, .user-name ou .user-divisa nas paginas.
   Eram 6 copias divergentes; agora e uma. */
.user-info { display: flex; align-items: center; gap: .65rem; }
.user-avatar { flex: 0 0 auto; }

.user-details {
  flex: 1 1 auto;    /* ocupa o que sobra ... */
  min-width: 0;      /* ... e pode encolher sem empurrar o cartao */
}
/* Com a divisa fora do cartao, o nome tem os 161px inteiros e nao ha mais
   nada disputando espaco com ele. Sem corte, sem reticencias, sem fonte
   encolhendo: ele simplesmente cabe. */



/* ══════════════════════════════════════════════════════════════════════════
   A DIVISA NA BARRA DO TOPO — 03/08/2026
   ══════════════════════════════════════════════════════════════════════════
   Mudanca pedida pelo Lucas, e ela conserta o corte do nome pela raiz em vez
   de por ajuste: no cartao, nome e divisa disputavam 161px e a divisa sozinha
   pede ate 180. Era uma disputa que o nome ia perder em alguma pagina, com
   algum nome -- e vinha perdendo.

   Aqui em cima ela tem espaco de sobra, fica visivel em toda pagina e nao
   competa com nada. No dashboard ela vem ANTES da sequencia de dias: a
   identidade primeiro, o numero depois.
   ══════════════════════════════════════════════════════════════════════════ */
.divisa-topo { display: inline-flex; }
.divisa-topo .divisa { font-size: var(--t-xs); letter-spacing: .1em; }
.divisa-topo .divisa .nivel { padding: .36rem .8rem .36rem 1.2rem; }
.divisa-topo .divisa .tag   { padding: .36rem .85rem; }

/* enquanto carrega, reserva o espaco -- senao a barra do topo pula quando o
   dado chega, que foi o defeito que consertamos na barra lateral */
.divisa-topo:empty { min-width: 11rem; min-height: 26px; }

@media (max-width: 640px) {
  /* no celular a barra do topo ja e apertada: a divisa desce para a linha de
     baixo em vez de espremer o titulo */
  .main .topbar-right { width: 100%; }
}

/* ═══ CAMPOS NO TOQUE: NUNCA MENOS DE 16 PX ═══ (04/10/2026, auditoria UX-03, roadmap 3.10)
   Campo com letra menor que 16 px faz o Safari do iPhone AMPLIAR a tela ao
   tocar -- e a pessoa fica com a pagina torta no meio do cadastro. A auditoria
   achou 19 (Entrar, Criar conta, Lista, Banco, TAF, Cronometro). Um piso so,
   aqui, em vez de 19 consertos espalhados que a proxima pagina esqueceria.
   - So em tela de toque (pointer: coarse): no computador nada muda.
   - Especificidade alta de proposito ((0,3,3)): vence ".share-link input" e
     "select.materia-select" das paginas sem precisar de !important.
   - Medido em 04/10: nenhum campo do site passa de 16 px, entao o piso nao
     encolhe ninguem. Regressao: node tools/testa-acessivel.js */
@media (pointer: coarse) {
  html body :is(input:not([type=checkbox]):not([type=radio]):not([type=range]), select, textarea) {
    font-size: 16px;
  }
}
