/* =============================================================================
   apoio.digital — folha de estilo do aplicativo
   =============================================================================
   Direção: neutralidade partidária é requisito jurídico (§1.4 do CONTEXTO),
   então é também requisito visual. Nenhuma cor de partido brasileiro.
   Índigo elétrico como ação, âmbar exclusivamente para marcos.

   Tema único e claro, do convite ao painel. Houve uma fase com o painel
   escuro — a ideia era destacá-lo como "território da pessoa" e fazer a
   constelação brilhar. Na prática, alternar o fundo no meio da navegação
   parecia defeito, e num app que a pessoa abre no sol, na rua, entre uma
   conversa e outra, contraste alto vale mais que efeito.
   ========================================================================== */

@import url('https://fonts.googleapis.com/css2?family=Bricolage+Grotesque:opsz,wght@12..96,600;12..96,800&family=Instrument+Sans:wght@400;500;600&family=JetBrains+Mono:wght@700&display=swap');

:root {
  --ink: #0e1116;
  --graphite: #1b2029;
  --mist: #e7eaf0;
  --paper: #ffffff;
  --slate: #656e82;
  --volt: #4534e8;
  --volt-soft: #6d5eff;
  --ember: #ff9a1f;
  --ember-ink: #8a5200; /* âmbar legível como texto sobre fundo claro */
  --danger: #d92d20;

  --display: 'Bricolage Grotesque', system-ui, sans-serif;
  --body: 'Instrument Sans', system-ui, -apple-system, sans-serif;
  --mono: 'JetBrains Mono', ui-monospace, monospace;

  --r: 14px;
  --shell: 460px;
}

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

/* ★ `hidden` PRECISA VENCER, e sem isto ele perde quase sempre.
 *
 * O navegador esconde `[hidden]` por uma regra da folha padrão dele, de
 * especificidade mínima. Qualquer `.classe { display: flex }` nossa ganha dela
 * — e aí o elemento continua na tela com o atributo posto. O pior é que o
 * código do app fica CERTO: `$('x').hidden = true` executa, o atributo aparece
 * no DOM, e o teste que confere o atributo passa. Só a tela discorda.
 *
 * Foi remendado duas vezes caso a caso (`.folha[hidden]`, `.channel[hidden]`),
 * e em 06/08/2026 uma varredura achou mais cinco classes na mesma situação:
 *
 *   .inviter  — o cartão "quem te convidou" aparecia VAZIO, com "—", para
 *               quem chega sem convite. Visível no primeiro build de iOS.
 *   .btn      — o "Continuar" da fila de cargos não sumia quando a UF não tem
 *               candidatura. Antes de 15/08 isso é toda UF em produção: o
 *               botão levava a um beco que o próprio código tentou fechar.
 *   .consent  — a caixa "Avisar neste aparelho" não sumia onde o push não
 *               existe. No iPhone ela aparece e não pode funcionar.
 *   .resolved, .btn-ghost — mesmo formato.
 *
 * Uma regra global resolve as cinco e as próximas. O `!important` é o que
 * garante que ela vença qualquer `display` de classe — é o motivo de ela
 * existir, não descuido. */
[hidden] { display: none !important; }

html, body {
  margin: 0;
  padding: 0;
  min-height: 100%;
  font-family: var(--body);
  background: var(--mist);
  color: var(--ink);
  -webkit-font-smoothing: antialiased;
}


/* O respiro do topo soma a ÁREA SEGURA do aparelho, e não é detalhe de acabamento.
 *
 * O `viewport-fit=cover` do app.html manda o conteúdo ocupar a tela inteira,
 * inclusive o que fica atrás do relógio, da bateria e da ilha dinâmica. Em
 * troca, é nosso o trabalho de afastar o conteúdo dessas áreas. Com 28px fixos
 * não afastava: no primeiro build de iOS a barra de progresso e o "APOIO.
 * DIGITAL" apareceram ENCOSTADOS no relógio, e o título logo abaixo.
 *
 * `env(safe-area-inset-*)` vale zero onde não há recorte — no computador e no
 * Android o resultado é idêntico ao de antes. Nos lados também, que é o que
 * segura o texto longe do canto arredondado quando o telefone deita.
 *
 * Vale para os dois caminhos do iPhone: o app da loja e o site adicionado à
 * tela de início, que hoje é como o iPhone usa o produto. */
.shell {
  max-width: var(--shell);
  margin: 0 auto;
  padding:
    calc(28px + env(safe-area-inset-top))
    calc(20px + env(safe-area-inset-right))
    calc(48px + env(safe-area-inset-bottom))
    calc(20px + env(safe-area-inset-left));
  min-height: 100vh;
  min-height: 100dvh;
  display: flex;
  flex-direction: column;
}

/* --- Tipografia ---------------------------------------------------------- */

h1, h2 {
  font-family: var(--display);
  font-weight: 800;
  letter-spacing: -0.028em;
  line-height: 1.04;
  margin: 0 0 12px;
}

h1 { font-size: clamp(30px, 8vw, 40px); }
h2 { font-size: 22px; }

p { line-height: 1.55; margin: 0 0 16px; color: var(--slate); }

.eyebrow {
  font-family: var(--body);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--volt);
  margin: 0 0 14px;
}


.fineprint { font-size: 13px; line-height: 1.5; color: var(--slate); }
.fineprint a { color: var(--volt); }

/* --- Os três passos da primeira tela --------------------------------------
   O contador é desenhado à mão porque o marcador nativo não aceita cor nem
   peso próprios de forma confiável entre navegadores — e o número é metade
   do recado aqui: são três passos, curtos, e dá para ver isso antes de ler.
   -------------------------------------------------------------------------- */

.howto {
  list-style: none;
  counter-reset: passo;
  margin: 0 0 18px;
  padding: 0;
}

.howto li {
  counter-increment: passo;
  position: relative;
  padding-left: 34px;
  margin-bottom: 12px;
  font-size: 14.5px;
  line-height: 1.5;
  color: var(--slate);
}

.howto li b { color: var(--ink); font-weight: 600; }

.howto li::before {
  content: counter(passo);
  position: absolute;
  left: 0;
  top: 1px;
  width: 22px;
  height: 22px;
  border-radius: 50%;
  background: var(--volt);
  color: #fff;
  font-family: var(--mono);
  font-size: 12px;
  display: grid;
  place-items: center;
}

/* --- Telas --------------------------------------------------------------- */

.screen { display: none; flex: 1; flex-direction: column; }
.screen.active { display: flex; animation: rise .34s cubic-bezier(.2,.7,.3,1) both; }

/* A primeira tela de quem vem da página de convite, decidida antes do JS.
   Quem marca o <html> é o <script> no <head> do app.html, e é lá que está o
   porquê. Vale só até o app.js assumir: ele tira a classe e volta a mandar
   pelo `active`, como todas as outras telas. */
html.veio-do-convite #s-invite { display: none; }
html.veio-do-convite #s-login  { display: flex; }

@keyframes rise {
  from { opacity: 0; transform: translateY(10px); }
  to   { opacity: 1; transform: none; }
}

.spacer { flex: 1; min-height: 20px; }

/* --- Progresso ----------------------------------------------------------- */

.steps { display: flex; gap: 5px; margin-bottom: 26px; }
.steps span {
  height: 3px;
  flex: 1;
  border-radius: 99px;
  background: rgba(14, 17, 22, .12);
  transition: background .3s;
}
.steps span.done { background: var(--volt); }

/* --- Formulários --------------------------------------------------------- */

label.field { display: block; margin-bottom: 16px; }

label.field > span {
  display: block;
  font-size: 13px;
  font-weight: 600;
  margin-bottom: 6px;
}

/* Seletor por EXCLUSÃO, não por lista.
   A versão anterior listava text, tel e select. Quando entraram campos
   type="email" e type="password", eles ficaram fora da regra e apareceram
   no tamanho padrão do navegador — estreitos, com fonte pequena e sem
   borda, no meio de uma tela onde tudo o mais ocupa a largura inteira.
   Excluir caixa e rádio cobre qualquer tipo que venha depois. */
input:not([type="checkbox"]):not([type="radio"]), select {
  width: 100%;
  padding: 14px 15px;
  font-family: var(--body);
  font-size: 16px; /* 16px evita o zoom automático do iOS */
  color: var(--ink);
  background: var(--paper);
  border: 1.5px solid rgba(14, 17, 22, .14);
  border-radius: var(--r);
  outline: none;
  transition: border-color .18s;
}

input:focus-visible, select:focus-visible, button:focus-visible {
  border-color: var(--volt);
  outline: 2px solid var(--volt);
  outline-offset: 2px;
}

/* O campo `.code`, do código de seis dígitos, saiu junto com o OTP por SMS. */

/* --- Botões -------------------------------------------------------------- */

button {
  font-family: var(--body);
  font-size: 16px;
  font-weight: 600;
  border: none;
  border-radius: var(--r);
  cursor: pointer;
}

.btn {
  display: block;
  width: 100%;
  padding: 16px;
  background: var(--volt);
  color: #fff;
  transition: transform .12s, opacity .18s;
}

.btn:active { transform: scale(.985); }
.btn:disabled { opacity: .38; cursor: not-allowed; }

/* Mesma aparência do desabilitado, mas o botão CONTINUA clicável.
 *
 * Um testador relatou que "Concluir cadastro" parece pronto enquanto as
 * autorizações obrigatórias estão em branco. Estava certo: botão de aparência
 * ativa que não avança mente sobre o próprio estado.
 *
 * Desabilitar de verdade resolveria a aparência e criaria um beco: `disabled`
 * não dispara clique, então a mensagem que diz QUAL caixa falta nunca
 * apareceria — e nesta tela são duas obrigatórias no meio de duas opcionais.
 * A pessoa ficaria diante de um botão apagado sem saber o que fazer.
 *
 * Com `aria-disabled` o leitor de tela anuncia indisponível, o olho vê
 * apagado, e o toque continua explicando o que falta. */
.btn[aria-disabled="true"] { opacity: .38; cursor: not-allowed; }

.btn-ghost {
  background: transparent;
  color: var(--slate);
  padding: 13px;
  width: 100%;
  font-size: 14px;
}

/* Botão secundário — o meio que faltava entre o .btn cheio e o .btn-ghost,
   que é texto solto. Existe porque uma ação de navegação no meio do painel
   não pode competir com o "Continuar" nem sumir como link: sem borda, ela
   lê como legenda e ninguém toca. Mesmo nome que em site.css, de propósito:
   as duas folhas nomeiam a mesma ideia. */
.btn-line {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  width: 100%;
  /* Respiro próprio. No painel ele fica entre os dois contadores e o cartão do
     marco, os dois blocos densos — encostado nos dois, lê como parte de um
     deles em vez de ação separada. */
  margin: 6px 0 22px;
  padding: 14px;
  background: var(--paper);
  color: var(--ink);
  border: 1.5px solid rgba(14, 17, 22, .14);
  font-size: 15px;
  font-weight: 600;
  transition: border-color .16s, transform .12s;
}

.btn-line:hover { border-color: var(--volt); }
.btn-line:active { transform: scale(.985); }
.btn-line svg { width: 17px; height: 17px; flex: 0 0 17px; }


/* --- Correção de dados (art. 18, III) ------------------------------------
   Recolhida por padrão: a tela existe para desligar avisos e excluir conta,
   e um formulário aberto empurraria as duas coisas para fora da primeira
   dobra. Quem precisa corrigir vai atrás; quem não precisa não tropeça.
   ------------------------------------------------------------------------ */

.corrigir {
  background: var(--paper);
  border: 1.5px solid rgba(14, 17, 22, .1);
  border-radius: var(--r);
  padding: 4px 15px;
  margin-top: 14px;
}

.corrigir > summary {
  cursor: pointer;
  padding: 12px 0;
  font-size: 15px;
  font-weight: 600;
  list-style-position: inside;
}

.corrigir[open] > summary { margin-bottom: 8px; }
.corrigir .field { margin-bottom: 12px; }
.corrigir .btn { margin-top: 4px; }
.corrigir .fineprint { margin: 12px 0 14px; }

.btn-danger { color: var(--danger); }

/* O botão que confirma de verdade. Sólido, porque a essa altura a pessoa já
   digitou EXCLUIR — o passo perigoso é o anterior, e ele é fantasma. */
.btn-solid-danger { background: var(--danger); color: #fff; }
.btn-solid-danger:disabled { opacity: .38; cursor: not-allowed; }

#delete-confirm input {
  width: 100%;
  padding: 15px;
  margin-bottom: 12px;
  font-family: var(--mono);
  font-size: 16px; /* abaixo de 16px o iOS dá zoom ao focar */
  letter-spacing: .12em;
  text-align: center;
  background: var(--paper);
  border: 1.5px solid rgba(14, 17, 22, .12);
  border-radius: var(--r);
  color: var(--ink);
}

#delete-confirm input:focus {
  outline: none;
  border-color: var(--danger);
}

/* --- Entrada por provedor ------------------------------------------------
   Todos os provedores com o MESMO peso visual. Destacar um deles empurra a
   escolha, e aqui a escolha carrega consequência: é a conta de rede social
   que a pessoa associa à própria opinião política.
   ------------------------------------------------------------------------ */

.btn-social {
  display: flex;
  align-items: center;
  gap: 12px;
  width: 100%;
  padding: 14px 16px;
  margin-bottom: 10px;
  background: var(--paper);
  color: var(--ink);
  border: 1.5px solid rgba(14, 17, 22, .14);
  font-size: 15px;
  font-weight: 600;
  text-align: left;
  transition: border-color .16s, transform .12s;
}

.btn-social:active { transform: scale(.985); }
.btn-social:hover { border-color: rgba(14, 17, 22, .3); }
.btn-social:disabled { opacity: .45; cursor: not-allowed; }

.btn-social svg {
  width: 20px;
  height: 20px;
  flex: 0 0 20px;
}

/* O alternador entrar/criar e o separador "ou" saíram junto com a senha.
   A entrada por e-mail é um campo e um botão: o link serve tanto para quem
   já tem conta quanto para quem não tem, então não há o que escolher. */

/* --- Cartão de quem convidou --------------------------------------------- */

.inviter {
  display: flex;
  align-items: center;
  gap: 13px;
  padding: 14px;
  background: var(--paper);
  border-radius: var(--r);
  margin-bottom: 24px;
}

.avatar {
  width: 42px; height: 42px;
  flex: 0 0 42px;
  border-radius: 50%;
  background: var(--volt);
  color: #fff;
  display: grid;
  place-items: center;
  font-family: var(--display);
  font-weight: 800;
  font-size: 17px;
}

/* --- Campos: apoio e marcação de opcional --------------------------------
   O que é opcional precisa PARECER opcional. Um campo sem marca é lido como
   obrigatório, e a pessoa trava procurando um dado que ela não precisa dar.
   ------------------------------------------------------------------------ */

label.field > span .opt {
  font-style: normal;
  font-weight: 500;
  font-size: 11.5px;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--slate);
  background: rgba(101, 110, 130, .12);
  padding: 2px 6px;
  border-radius: 5px;
  margin-left: 6px;
  vertical-align: 1px;
}

.fineprint.hint { display: block; margin-top: 7px; }

/* --- Endereço resolvido pelo CEP -----------------------------------------
   O resultado da consulta fica sempre visível e sempre corrigível. CEP de
   empresa e de caixa postal devolvem bairro errado com frequência, e um
   endereço que a pessoa não consegue corrigir é pior que campo em branco.
   ------------------------------------------------------------------------ */

.resolved {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 13px 15px;
  background: rgba(69, 52, 232, .07);
  border: 1.5px solid rgba(69, 52, 232, .22);
  border-radius: var(--r);
  margin: -4px 0 16px;
}

.resolved > div { flex: 1; min-width: 0; }
.resolved b { display: block; font-size: 15px; font-weight: 600; }

.resolved small {
  display: block;
  font-size: 12.5px;
  color: var(--slate);
  margin-top: 1px;
}

.resolved button {
  background: transparent;
  color: var(--volt);
  font-size: 13px;
  padding: 6px 2px;
  flex: 0 0 auto;
}

/* --- Seleção de candidatos -----------------------------------------------
   O âmbar aqui é o mesmo do marco, e é proposital: nas duas telas ele marca
   "isto é uma informação sobre o seu estado agora", não erro nem sucesso. A
   lista de demonstração não é um defeito (vermelho) nem uma confirmação
   (índigo) — é uma condição temporária, que sai sozinha em 15/08.
   ------------------------------------------------------------------------ */

.demo-note {
  padding: 14px 15px;
  background: rgba(255, 154, 31, .13);
  border: 1.5px solid rgba(255, 154, 31, .35);
  border-radius: var(--r);
  font-size: 13.5px;
  line-height: 1.5;
  color: var(--ember-ink);
  margin-bottom: 16px;
}

.demo-note b {
  display: block;
  margin-bottom: 4px;
  font-size: 14.5px;
  font-weight: 600;
}

.pick {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 14px;
  background: var(--paper);
  border: 1.5px solid transparent;
  border-radius: var(--r);
  margin-bottom: 9px;
  cursor: pointer;
  transition: border-color .16s;
}

/* Cartão de leitura, na consulta de candidaturas: mesma forma, sem caixa de
   seleção e sem cursor de clique. Parecer clicável e não fazer nada é pior
   que parecer estático — quem tocasse esperaria declarar apoio, que é
   exatamente o que esta tela não faz. */
.pick.leitura { cursor: default; }
.pick .who small.cargo {
  display: block;
  font-size: 11.5px;
  text-transform: lowercase;
  opacity: .75;
}

/* --- Mapa e apuração na tela de candidaturas -------------------------------
   As classes são as MESMAS de site.css (.rede-mapa, .n1…, .tocavel, .on),
   porque quem desenha é o módulo compartilhado js/mapa-br.js. A duplicação de
   regra entre as duas folhas é o preço de não haver etapa de build: se mudar
   a paleta aqui, mude lá também, senão o mesmo estado sai em dois tons.

   Escala de UMA cor, como na home. Uma escala que vá do vermelho ao azul
   empresta ao dado uma leitura partidária que ele não tem (§1.4).
   -------------------------------------------------------------------------- */

.rede-mapa { opacity: 0; transition: opacity .35s ease; margin-bottom: 8px; }
.rede-mapa.pronto { opacity: 1; }
.rede-mapa svg { display: block; width: 100%; height: auto; }

.rede-mapa path {
  fill: rgba(14, 17, 22, .07);
  stroke: var(--mist, #eceef3);
  stroke-width: 1.5;
  stroke-linejoin: round;
  transition: fill .2s, opacity .2s;
  /* O traço é medido em PIXELS, não em unidades do viewBox.
     Sem isto, o terceiro nível vira mancha: o viewBox do Brasil tem 1000
     unidades de largura e o de uma cidade tem ~45, então o mesmo
     `stroke-width: 2` sai com 0,9px num e 25px no outro — cobrindo o bairro
     inteiro que deveria apenas contornar. */
  vector-effect: non-scaling-stroke;
}

.rede-mapa path.n1 { fill: rgba(69, 52, 232, .16); }
.rede-mapa path.n2 { fill: rgba(69, 52, 232, .32); }
.rede-mapa path.n3 { fill: rgba(69, 52, 232, .50); }
.rede-mapa path.n4 { fill: rgba(69, 52, 232, .72); }
.rede-mapa path.n5 { fill: var(--volt); }

.rede-mapa path.tocavel { cursor: pointer; }
.rede-mapa.filtrado path:not(.on) { opacity: .3; }
.rede-mapa path.on { stroke: var(--ink); stroke-width: 2; }

.mapa-nota { font-size: 12px; color: var(--slate); margin: 0 0 20px; line-height: 1.45; }

.rede-cabeca {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 10px;
}

.rede-cabeca h3 { margin: 0; font-size: 15px; }
.rede-cabeca span { font-size: 11px; color: var(--slate); text-align: right; }

.apurados { list-style: none; margin: 0 0 10px; padding: 0; }

.apurados li {
  display: grid;
  grid-template-columns: 20px minmax(0, 1fr) auto;
  align-items: center;
  gap: 12px;
  padding: 11px 14px;
  margin-bottom: 7px;
  background: var(--paper);
  border-radius: var(--r);
}

.apurados .pos { font-family: var(--mono); font-size: 12px; color: var(--slate); text-align: right; }
.apurados .nome { font-size: 14.5px; font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.apurados .nome small { display: block; font-weight: 400; font-size: 11.5px; color: var(--slate); }
.apurados .qtd { font-family: var(--mono); font-size: 15px; color: var(--volt); }

/* Só o estado sobrou de <select> aqui — cargo e partido viraram botões. A
   caixa continua estreita porque o conteúdo são duas letras: esticada na
   largura da tela, ela prometeria um texto que nunca vem. */
.filtros { display: grid; grid-template-columns: 96px; }
.filtros .field { margin-bottom: 12px; }

/* --- Cargo e partido: filtro por botão ------------------------------------
   Um grupo de botões, não uma linha de abas: as abas da classificação
   (.rank-aba) trocam a VISTA de uma lista, e aqui o toque recorta o conteúdo.
   O visual é o mesmo de propósito — a pessoa já aprendeu o que a pílula
   contornada de roxo significa no painel —, mas o estado ativo vive em
   aria-pressed, que é o que um leitor de tela anuncia para botão de filtro.

   Quebra de linha, nunca rolagem horizontal: com 30 partidos, uma faixa que
   rola esconde a maioria deles atrás de um gesto que ninguém descobre — e
   esconder partido nesta tela não é detalhe de layout.
   ------------------------------------------------------------------------ */

.filtro { margin-bottom: 14px; }

.filtro-rotulo {
  display: block;
  font-size: 13px;
  font-weight: 600;
  margin-bottom: 7px;
}

.chips { display: flex; flex-wrap: wrap; gap: 6px; }

.chip {
  padding: 7px 12px;
  font: inherit;
  font-size: 12.5px;
  font-weight: 600;
  color: var(--slate);
  background: transparent;
  border: 1.5px solid rgba(14, 17, 22, .12);
  border-radius: 99px;
  cursor: pointer;
  transition: border-color .16s, color .16s, background .16s;
}

.chip:hover { border-color: var(--volt); }

.chip.on {
  color: var(--volt);
  border-color: var(--volt);
  background: rgba(69, 52, 232, .07);
}

/* A quantidade em fonte de números: é ela que diz se o filtro leva a dez nomes
   ou a duzentos, e alinhada em mono ela se lê como número e não como parte do
   nome do cargo. */
.chip small {
  font-family: var(--mono);
  font-size: 11px;
  font-weight: 400;
  opacity: .75;
  margin-left: 2px;
}

.pick.on { border-color: var(--volt); }
.pick input { accent-color: var(--volt); width: 19px; height: 19px; flex: 0 0 19px; }
.pick .who { flex: 1; min-width: 0; }
.pick .who b { display: block; font-size: 15px; font-weight: 600; }
.pick .who small { color: var(--slate); font-size: 12.5px; }

/* A posição na lista de mais apoiadas. Ocupa a coluna estreita da esquerda,
   antes da foto, e é cinza: quem lidera já está em primeiro na tela — o
   número é referência, não medalha. Largura fixa para o "10" não empurrar a
   foto e desalinhar a coluna inteira. */
.pick .pos {
  flex: 0 0 18px;
  font-family: var(--mono);
  font-size: 12.5px;
  color: var(--slate);
  text-align: right;
}

/* A coluna da direita: número de urna em cima, apoios embaixo.
   Empilhados porque lado a lado eles disputam a mesma leitura — dois números
   na mesma linha, um deles sem rótulo, e ninguém sabe qual é qual. */
.pick .lado {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 1px;
}

/* Com a palavra "apoios" junto, sempre. É o que impede o número de ser lido
   como segunda parte do número de urna — e é também o que um fiscal precisa
   ver para saber o que a tela está contando. */
.pick .apoios {
  font-size: 11.5px;
  color: var(--slate);
  white-space: nowrap;
}

/* A linha do partido, quando o que está ali é um marcador e não um partido.
   Fica destacada em vez de cinza porque é o único aviso que sobrevive a uma
   captura de tela recortada logo abaixo do cartão. */
.pick .who small.demo,
.invite-head .who small.demo { color: var(--ember-ink); font-weight: 600; }

.pick .num {
  font-family: var(--mono);
  font-size: 15px;
  color: var(--volt);
}

/* "Já marcada: é a candidatura que Fulano apoia."
   Fica ACIMA da lista e não dentro de um cartão: é aviso sobre a lista
   inteira, e precisa ser lido antes de a pessoa passar os olhos pelas caixas
   já marcadas. Discreto, mas não apagado — é o que separa marcação herdada
   com transparência de opinião de terceiro entrando em silêncio. */
.doconvite {
  font-size: 13px;
  line-height: 1.45;
  color: var(--slate);
  background: rgba(69, 52, 232, .06);
  border-left: 2px solid var(--volt);
  padding: 9px 12px;
  border-radius: 0 8px 8px 0;
  margin: 0 0 12px;
}

/* "Ainda não sei" — opção da lista, mas não uma candidatura.
   Marcar precisa continuar sendo um toque igual ao das outras, então o
   tamanho e a caixa de seleção não mudam. O que muda é o peso: fundo
   apagado, sem foto e sem número, para que a lista continue lendo como
   "estas são as candidaturas, e esta é a saída". */
.pick.nenhuma {
  background: transparent;
  border-style: dashed;
  /* Entre o --mist do fundo e o --slate do texto: visível como contorno,
     sem competir com a borda cheia de uma candidatura marcada. */
  border-color: #c2c9d6;
}

.pick.nenhuma.on {
  background: var(--paper);
  border-style: solid;
  border-color: var(--volt);
}

.pick.nenhuma .who b { font-weight: 500; }

/* --- Busca e paginação da lista de candidaturas --------------------------
   A lista deixou de caber na tela quando a base do TSE entrou: 603 nomes na
   Bahia, 584 em São Paulo. Os dois controles aqui são o que a torna
   percorrível — a busca para quem já sabe o nome, o "Ver mais" para quem veio
   olhar. Ver renderCandidateList() em app.js.
   ------------------------------------------------------------------------ */

.busca {
  width: 100%;
  box-sizing: border-box;
  padding: 12px 14px;
  margin-bottom: 12px;
  font: inherit;
  font-size: 15px;
  color: var(--ink);
  background: var(--paper);
  border: 1.5px solid transparent;
  border-radius: var(--r);
}

.busca:focus {
  outline: none;
  border-color: var(--volt);
}

/* O botão fecha a lista, então não pode competir com o "Continuar" que fecha
   a TELA: contorno onde o outro é cheio. Quem rola até aqui procurando mais
   nomes acha; quem já escolheu não confunde com o caminho adiante. */
.ver-mais {
  display: block;
  width: 100%;
  padding: 13px;
  margin-bottom: 9px;
  font: inherit;
  font-size: 14.5px;
  font-weight: 600;
  color: var(--volt);
  background: transparent;
  border: 1.5px dashed #c2c9d6;
  border-radius: var(--r);
  cursor: pointer;
}

.ver-mais:hover { border-color: var(--volt); }

/* "de 583 restantes" — sem isto o botão não diz se falta uma página ou trinta,
   que é justamente o que decide entre rolar e buscar. */
.ver-mais small {
  font-weight: 400;
  color: var(--slate);
}

.lista-vazia {
  padding: 18px 14px;
  margin-bottom: 9px;
  font-size: 14px;
  color: var(--slate);
  text-align: center;
  background: var(--paper);
  border-radius: var(--r);
}

.linkish {
  font: inherit;
  color: var(--volt);
  font-weight: 600;
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
  text-decoration: underline;
}

/* --- Consentimento -------------------------------------------------------
   Os obrigatórios e o opcional são visualmente distintos de propósito.
   O opcional NUNCA nasce marcado (§R2). Se algum dia alguém "otimizar" a
   conversão marcando por padrão, o consentimento perde validade.
   ------------------------------------------------------------------------ */

.consent {
  display: flex;
  gap: 12px;
  padding: 15px;
  background: var(--paper);
  border-radius: var(--r);
  margin-bottom: 10px;
  cursor: pointer;
}

.consent.optional {
  background: transparent;
  border: 1.5px dashed rgba(14, 17, 22, .2);
}

.consent input { accent-color: var(--volt); width: 20px; height: 20px; flex: 0 0 20px; margin-top: 1px; }
.consent .txt { font-size: 13.5px; line-height: 1.5; }
.consent .txt b { display: block; margin-bottom: 3px; font-size: 14.5px; font-weight: 600; }
.consent a { color: var(--volt); }

.tag {
  display: inline-block;
  font-size: 10.5px;
  font-weight: 600;
  letter-spacing: .1em;
  text-transform: uppercase;
  padding: 3px 7px;
  border-radius: 5px;
  margin-bottom: 6px;
}

.tag.req { background: rgba(69, 52, 232, .1); color: var(--volt); }
.tag.opt { background: rgba(101, 110, 130, .13); color: var(--slate); }

/* --- Painel (escuro) ------------------------------------------------------ */

.panel-top {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  margin-bottom: 6px;
}

.tally { display: flex; gap: 26px; margin: 4px 0 22px; }

.tally .n {
  font-family: var(--mono);
  font-size: 40px;
  line-height: 1;
  font-variant-numeric: tabular-nums;
}

/* Novidades desde a última visita. Âmbar porque é o mesmo vocabulário visual
   dos marcos — reconhecimento, não alerta. Vermelho aqui leria como problema,
   e a notícia é boa. */
.novidades {
  margin: -12px 0 20px;
  padding: 11px 14px;
  background: rgba(255, 154, 31, .14);
  border-radius: var(--r);
  color: var(--ember-ink);
  font-size: 13.5px;
  font-weight: 600;
  line-height: 1.45;
}

/* Quem ainda não declarou apoio. Índigo e não âmbar: é convite a agir, não
   marco conquistado nem alerta de problema. */
.sem-apoio {
  background: rgba(69, 52, 232, .08);
  border-radius: var(--r);
  padding: 16px;
  margin: -12px 0 20px;
}

.sem-apoio b { display: block; font-size: 15px; margin-bottom: 4px; }
.sem-apoio > div { font-size: 13.5px; color: var(--slate); line-height: 1.45; }
.sem-apoio .btn { margin-top: 13px; }

/* A porta para rever o apoio declarado. Fica logo abaixo dos cartões de
   convite, e é DELIBERADAMENTE discreta: quem abre o painel vem convidar, não
   desapoiar. Ela precisa estar sempre lá e nunca puxar o olho — dar a ela o
   peso do bloco acima seria a plataforma sugerindo a troca, que é o oposto de
   §R2 (a escolha é da pessoa). */
.rever-apoio { margin: 4px 0 6px; }
.rever-apoio .fineprint { margin-bottom: 8px; }

.tally .direct .n { color: var(--ink); }
.tally .network .n { color: var(--volt-soft); }

.tally small {
  display: block;
  margin-top: 7px;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--slate);
}

/* --- Constelação: o elemento de assinatura -------------------------------
   Seu nó no centro e UM ANEL POR NÍVEL da rede, do 1 ao 10. As linhas se
   desenham no carregamento. É a única coisa que se move na tela inteira — e
   se move uma vez, ao abrir: rotação perpétua num painel que a pessoa abre
   dez vezes por dia deixa de ser encanto e vira ruído.

   Duas coisas aqui já foram diferentes e voltaram:

   1. O desenho era ELÍPTICO (Y multiplicado por 0.78) para sugerir órbita em
      perspectiva. Com os pontos distribuídos por igual, isso não lê como
      perspectiva — lê como círculo mal desenhado. É circunferência.

   2. O raio de cada ponto do nível 1 já foi proporcional à rede daquela
      pessoa. Sem escala visível e sem legenda, diferença contínua de área lê
      como desalinhamento, não como dado. Quem multiplicou virou ESTADO
      DISCRETO — âmbar, nomeado na legenda. Dois estados nomeados comunicam
      mais que dezoito áreas ligeiramente diferentes.
   ------------------------------------------------------------------------ */

.constellation {
  background: var(--paper);
  border: 1.5px solid rgba(14, 17, 22, .1);
  border-radius: 20px;
  padding: 8px;
  margin-bottom: 12px;
  overflow: hidden;
}

.constellation svg { display: block; width: 100%; height: auto; }

/* Circunferência-guia de cada nível: é ela que faz "anel" ser lido como
   camada, e não como pontos espalhados. */
.guide { fill: none; stroke: rgba(14, 17, 22, .07); stroke-width: 1; }
.guide.lit { stroke: rgba(69, 52, 232, .4); }

/* O movimento da constelação NÃO está aqui.
   Foi uma animação de CSS no grupo inteiro, e o `transform-origin` de um
   elemento SVG depende do `transform-box`: o pivô caía fora do centro e a
   figura balançava em vez de girar, com os pontos saindo dos próprios anéis.
   Hoje as posições são recalculadas a cada quadro (animarConstelacao, em
   public/js/app.js), onde o centro é constante e não há pivô para errar.
   O que fica aqui é o que o dedo precisa saber. */
.constellation svg { touch-action: pan-y; cursor: grab; }
.constellation svg:active { cursor: grabbing; }

/* O pulso do centro: um anel que nasce em você e se dissolve para fora, a
   cada quatro segundos. É o único movimento que a pessoa percebe de fato, e
   por isso está no ÚNICO ponto do desenho que é ela. */
.pulso {
  fill: none;
  stroke: var(--volt);
  transform-box: fill-box;
  transform-origin: center;
  animation: pulsar 4s cubic-bezier(.2, .6, .3, 1) infinite;
}

@keyframes pulsar {
  0%   { transform: scale(1);   opacity: .5; stroke-width: 2; }
  70%  { transform: scale(2.6); opacity: 0;  stroke-width: 1; }
  100% { transform: scale(2.6); opacity: 0;  stroke-width: 1; }
}

.edge {
  stroke: var(--volt-soft);
  stroke-width: 1;
  fill: none;
  opacity: .38;
  stroke-linecap: round;
  stroke-dasharray: 200;
  stroke-dashoffset: 200;
  animation: draw .7s ease forwards;
}

/* A ligação dos anéis externos com o anel de dentro.
   Mais apagada que a do nível 1 de propósito: aquela liga VOCÊ a uma pessoa
   que você conhece e pode clicar; esta diz só que quem está mais longe chegou
   por alguém mais perto. É estrutura de fundo, não relação nomeada. */
.edge.funda { opacity: .14; stroke-width: .9; }

@keyframes draw { to { stroke-dashoffset: 0; } }

.node { opacity: 0; animation: pop .32s ease forwards; }
@keyframes pop { to { opacity: 1; } }

/* Você. Era branco para brilhar no fundo escuro — no fundo claro, branco
   sobre branco é nó invisível bem no centro do desenho. */
.node.me { fill: var(--ink); cursor: pointer; transition: fill .16s; }
.node.me:hover { fill: var(--graphite); }
.node.me:focus-visible { outline: 2px solid var(--volt); outline-offset: 3px; }
.node.me.on { fill: var(--volt); }

/* "Você", embaixo do ponto do meio.
   O `font-size` NÃO está aqui: ele é calculado em renderConstellation a partir
   do raio que o desenho usou, porque o viewBox é recortado nele e um corpo
   fixo mudaria de tamanho na tela conforme a profundidade da rede. Peso 600 e
   não 800: é uma etiqueta que orienta, não um título que compete com os
   números do painel logo abaixo. */
.rotulo-me {
  fill: var(--paper);
  font-family: var(--body);
  font-weight: 600;
  letter-spacing: .02em;
  /* Sem isto a palavra fica na frente do círculo e come o clique dele — o
     ponto do meio pararia de abrir o próprio cartão. */
  pointer-events: none;
}

/* Nível 1 — as únicas pessoas que você conhece, e as únicas clicáveis. */
.node.lvl1 { fill: var(--volt); cursor: pointer; transition: fill .16s; }
.node.lvl1.mult { fill: var(--ember); }
.node.lvl1:hover { fill: var(--volt-soft); }
.node.lvl1:focus-visible { outline: 2px solid var(--volt); outline-offset: 2px; }
.node.lvl1.on { fill: var(--ink); }

/* Encerrou a conta. Fica apagado e sem nome, mas fica: a rede que essa
   pessoa trouxe continua sendo sua, e é isso que o nó representa. */
.node.lvl1.saiu { fill: #b9c0cf; }
.node.lvl1.saiu:hover { fill: var(--slate); }

.halo { fill: none; stroke: var(--volt); stroke-width: 1.5; opacity: 0; transition: opacity .2s; }
.halo.on { opacity: 1; }

/* Níveis 2+: em segundo plano, mas gente.
   O §6.3 fala da ÁRVORE da rede — "só primeiro nome dos outros" —, não do
   primeiro nível. Ponto com pessoa por trás clica e abre o cartão igual ao do
   nível 1; ponto que é só densidade (nível com mais gente do que cabe na
   tela) continua sem clique, porque ponto que abre nada é pior que ponto que
   nunca prometeu abrir. */
.deep { transition: opacity .25s; }
.deep.dim { opacity: .18; }

.node.pessoa { cursor: pointer; transition: fill .16s, r .16s; }
.node.pessoa:hover { fill: var(--volt) !important; }
.node.pessoa:focus-visible { outline: 2px solid var(--volt); outline-offset: 2px; }
.node.pessoa.on { fill: var(--ink) !important; }
.node.pessoa.saiu { fill: #b9c0cf !important; }

/* --- Escala de níveis -----------------------------------------------------
   Cada chip nomeia um anel do desenho e traz a contagem real daquele nível.
   Sem ela os anéis externos são enfeite: a pessoa vê pontos e não sabe que
   está olhando a quarta camada da própria rede.
   ------------------------------------------------------------------------ */

.levels-scale {
  display: flex;
  gap: 5px;
  margin-bottom: 12px;
  overflow-x: auto;
  padding-bottom: 2px;
}

.lvl-chip {
  flex: 1 0 auto;
  min-width: 48px;
  background: var(--paper);
  border: 1.5px solid transparent;
  border-radius: 10px;
  padding: 8px 4px 7px;
  text-align: center;
  cursor: pointer;
  transition: border-color .16s;
}

.lvl-chip.on { border-color: var(--volt); }
.lvl-chip .swatch { width: 9px; height: 9px; border-radius: 50%; margin: 0 auto 5px; }

.lvl-chip .qt {
  display: block;
  font-family: var(--mono);
  font-size: 13px;
  font-variant-numeric: tabular-nums;
  color: var(--ink);
}

.lvl-chip .lb {
  display: block;
  font-size: 9px;
  font-weight: 600;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--slate);
  margin-top: 2px;
}

/* --- Linha de detalhe da rede --------------------------------------------
   Reaproveita o .pick das telas de candidatura. Nasce preenchida com o estado
   vazio em vez de escondida: é ela que avisa que os pontos são tocáveis.
   ------------------------------------------------------------------------ */

.net-detail {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 14px;
  background: var(--paper);
  border: 1.5px solid transparent;
  border-radius: var(--r);
  margin-bottom: 22px;
  min-height: 68px;
  /* Só para o botão de conversa cair numa linha própria (ele pede 100% de
     base). Os cartões sem botão têm três filhos que sempre couberam numa
     linha, então nada mais muda de posição por causa disto. */
  flex-wrap: wrap;
}

/* "Falar com Fulana no WhatsApp".
   Verde do WhatsApp e não o --volt da marca: é o único botão do produto que
   entrega a pessoa a outro aplicativo, e vestir de marca própria um caminho
   que sai daqui confunde o que vai acontecer no toque. Ver botaoConversa() em
   app.js para o motivo de ele abrir o seletor de contatos, e não a conversa.

   Vale nos dois lugares em que a conversa nasce: o cartão de um ponto da
   constelação e a caixa de munição. É o MESMO botão, com o mesmo texto e o
   mesmo caminho — dois estilos parecidos fariam o mesmo gesto parecer duas
   funções diferentes. */
.net-detail .falar,
.municao .falar {
  flex: 0 0 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  margin-top: 2px;
  padding: 11px;
  font: inherit;
  font-size: 14px;
  font-weight: 600;
  color: #0d7a54;
  background: rgba(37, 211, 102, .12);
  border: 0;
  border-radius: 10px;
  cursor: pointer;
}

.net-detail .falar:hover,
.municao .falar:hover { background: rgba(37, 211, 102, .2); }
.net-detail .falar:focus-visible,
.municao .falar:focus-visible { outline: 2px solid var(--volt); outline-offset: 2px; }
.net-detail .falar svg,
.municao .falar svg { flex: 0 0 17px; }

/* "Fulana não autorizou a plataforma a mostrar o telefone dela."
   Aparece só quando o botão cai no seletor de contatos, e existe para o
   seletor não ser lido como defeito. Ver abrirConversa() em app.js. */
.net-detail .falar-nota,
.municao .falar-nota {
  flex: 0 0 100%;
  margin-top: 6px;
  font-size: 12.5px;
  line-height: 1.4;
  color: var(--slate);
  text-align: center;
}

/* ---------------------------------------------------------------------------
   A caixa de munição — com quem falar agora.

   Mesmo fundo do marco (rgba do --volt), e de propósito: as duas são as
   únicas caixas do painel que PEDEM alguma coisa. O resto informa. Quem
   aprendeu que aquele tom significa "há um próximo passo aqui" acerta nas
   duas sem reaprender.
   --------------------------------------------------------------------------- */
.municao {
  background: rgba(69, 52, 232, .08);
  border-radius: var(--r);
  padding: 16px;
  margin: 0 0 22px;
}

.municao h2 { font-size: 17px; line-height: 1.3; margin: 2px 0 6px; }
.municao .fineprint { margin-bottom: 14px; }

.municao-item {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
  padding: 12px;
  background: var(--paper);
  border-radius: var(--r);
}

.municao-item + .municao-item { margin-top: 8px; }

.municao-item .avatar {
  width: 38px;
  height: 38px;
  flex: 0 0 38px;
  display: grid;
  place-items: center;
  border-radius: 50%;
  background: rgba(69, 52, 232, .12);
  color: var(--volt);
  font-weight: 700;
  text-transform: uppercase;
}

.municao-item .who { flex: 1 1 auto; min-width: 0; }
.municao-item .who b { display: block; font-size: 15px; }
.municao-item .who small { font-size: 12.5px; color: var(--slate); }

.municao #municao-mais { margin-top: 12px; }

.net-detail.on { border-color: var(--volt); }

.net-detail .avatar {
  width: 38px;
  height: 38px;
  flex: 0 0 38px;
  border-radius: 50%;
  background: rgba(69, 52, 232, .1);
  color: var(--volt);
  display: grid;
  place-items: center;
  font-family: var(--display);
  font-weight: 600;
  font-size: 15px;
}

.net-detail.mult .avatar { background: rgba(255, 154, 31, .18); color: var(--ember-ink); }

.net-detail .who { flex: 1; min-width: 0; }
.net-detail .who b { display: block; font-size: 15px; font-weight: 600; }
.net-detail .who small { color: var(--slate); font-size: 12.5px; }

.net-detail .num {
  font-family: var(--mono);
  font-size: 15px;
  color: var(--volt);
  text-align: right;
  line-height: 1.25;
}

.net-detail .num small {
  display: block;
  font-family: var(--body);
  font-weight: 600;
  font-size: 9.5px;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--slate);
}

.empty-net {
  text-align: center;
  padding: 34px 20px;
  color: var(--slate);
  font-size: 14px;
  line-height: 1.55;
}

/* --- Link de compartilhamento -------------------------------------------- */

.mylink {
  display: flex;
  align-items: center;
  gap: 10px;
  background: var(--paper);
  border: 1.5px solid rgba(14, 17, 22, .12);
  border-radius: var(--r);
  padding: 13px 15px;
  margin-bottom: 12px;
}

.mylink code {
  flex: 1;
  font-family: var(--mono);
  font-size: 15px;
  letter-spacing: .1em;
  color: var(--ink);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.mylink button {
  background: transparent;
  color: var(--volt);
  font-size: 13px;
  padding: 4px 2px;
}

/* --- Canais de convite ---------------------------------------------------
   Todos com o mesmo peso. O WhatsApp domina no Brasil e vem primeiro pela
   ordem, não pelo destaque: quem usa Telegram ou e-mail não deve sentir que
   está no caminho secundário.
   ------------------------------------------------------------------------ */

/* DUAS LINHAS, com contagens diferentes de propósito.

   A de cima é conversa direta — WhatsApp, Telegram, e-mail —, que é como o
   convite de fato circula. A de baixo é alcance aberto, nas redes sociais.
   Separar em linhas é o que faz "mandar para alguém" e "publicar para todos"
   se distinguirem sem precisar de rótulo dizendo isso.

   Colunas FIXAS em três, não `auto-fit`. Com auto-fit as duas linhas se
   repartiam sozinhas conforme a largura e podiam acabar com a mesma
   contagem — e aí as duas viram um bloco só de sete botões, que é
   exatamente a leitura que a separação existe para evitar. */
.channels {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 8px;
  margin-bottom: 8px;
}

/* Três no desktop; quatro no celular, em 2×2. O quarto é o "Outro app", que é
   a folha do sistema e só existe onde ela serve — quem põe a classe é o
   channelsNode(), pela mesma condição que decide mostrar o botão. Em quatro
   colunas de 360px, "WhatsApp" não caberia numa linha só. */
.channels.com-nativo {
  grid-template-columns: repeat(2, 1fr);
}

/* Dentro da folha das redes a contagem varia de verdade por aparelho: no
   desktop, Instagram e TikTok não aparecem — não existe endereço de
   compartilhamento na web para nenhum dos dois (ver app.js). */
.channels.social {
  grid-template-columns: repeat(auto-fit, minmax(76px, 1fr));
  margin-bottom: 0;
}

/* Menores que os do envio direto. A hierarquia é real e vale mostrar: o
   convite acontece na conversa; publicar é complemento. */
.channels.social .channel {
  padding: 12px 6px;
  font-size: 11px;
  gap: 6px;
}

.channels.social .channel svg { width: 19px; height: 19px; flex: 0 0 19px; }

/* --- "Compartilhar nas redes sociais" -------------------------------------
   Não é um quarto botão da grade, de propósito: publicar é ação de outra
   natureza que enviar para uma pessoa, e o que separava as duas coisas antes
   era só uma quebra de linha entre grades iguais. Aqui ela vira uma linha de
   texto com seta, que é a forma que a interface inteira usa para "isto abre
   outro lugar".
   ------------------------------------------------------------------------ */

.mais-redes {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  width: 100%;
  margin-bottom: 14px;
  padding: 11px;
  font: inherit;
  font-size: 13.5px;
  font-weight: 600;
  color: var(--volt);
  background: none;
  border: 0;
  cursor: pointer;
}

.mais-redes svg { width: 15px; height: 15px; flex: 0 0 15px; }
.mais-redes:hover { text-decoration: underline; }
.mais-redes:focus-visible { outline: 2px solid var(--volt); outline-offset: 2px; border-radius: 8px; }

/* --- A folha das redes -----------------------------------------------------
   Sobe de baixo no celular, onde o polegar alcança, e fica centrada no
   desktop, onde não existe "de baixo". É o mesmo elemento nos dois — só o
   posicionamento muda.
   ------------------------------------------------------------------------ */

.folha {
  position: fixed;
  inset: 0;
  z-index: 60;
  display: flex;
  align-items: flex-end;
  justify-content: center;
}

/* Coberto pela regra global de [hidden] no topo deste arquivo. */

.folha-fundo {
  position: absolute;
  inset: 0;
  background: rgba(14, 17, 22, .45);
  animation: folha-fundo .18s ease;
}

.folha-caixa {
  position: relative;
  width: 100%;
  max-width: var(--shell);
  max-height: 82vh;
  overflow-y: auto;
  padding: 18px 16px calc(18px + env(safe-area-inset-bottom));
  background: var(--mist);
  border-radius: 18px 18px 0 0;
  animation: folha-sobe .22s cubic-bezier(.2, .8, .3, 1);
}

@media (min-width: 560px) {
  .folha { align-items: center; }
  .folha-caixa {
    border-radius: 18px;
    animation: folha-fundo .18s ease;
    margin: 0 16px;
  }
}

/* A folha dos textos legais é mais alta que as outras porque o conteúdo dela
   é para LER, não para escolher. O quadro rola por dentro; a caixa, não —
   duas rolagens aninhadas na mesma folha é o jeito de a pessoa não chegar ao
   fim de nenhuma das duas. */
.folha-legal .folha-caixa {
  max-height: 90vh;
  display: flex;
  flex-direction: column;
  padding-bottom: 0;
  overflow: hidden;
}

/* O quadro é o espaço; o documento entra e sai dele — ver o ★ em app.html. */
.quadro-legal {
  display: flex;
  flex: 1;
  min-height: 60vh;
}

.quadro-legal iframe {
  flex: 1;
  width: 100%;
  border: 0;
  background: var(--mist);
}

.folha-topo {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 4px;
}

.folha-topo h3 {
  margin: 0;
  font-family: var(--display);
  font-size: 17px;
  font-weight: 700;
}

.folha-x {
  display: grid;
  place-items: center;
  width: 34px;
  height: 34px;
  flex: 0 0 34px;
  color: var(--slate);
  background: none;
  border: 0;
  border-radius: 50%;
  cursor: pointer;
}

.folha-x:hover { background: rgba(14, 17, 22, .07); }
.folha-x svg { width: 18px; height: 18px; }

.folha .fineprint { margin-bottom: 14px; }

@keyframes folha-sobe {
  from { transform: translateY(14px); opacity: 0; }
  to   { transform: translateY(0);    opacity: 1; }
}

@keyframes folha-fundo {
  from { opacity: 0; }
  to   { opacity: 1; }
}

.channel {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  padding: 15px 6px;
  background: var(--paper);
  color: var(--ink);
  border: 1.5px solid rgba(14, 17, 22, .12);
  border-radius: var(--r);
  font-size: 12px;
  font-weight: 600;
  transition: border-color .16s, transform .12s;
}

.channel:active { transform: scale(.97); }
.channel:hover { border-color: rgba(14, 17, 22, .3); }

/* SEM ISTO, `hidden` NÃO ESCONDE NADA.
   O atributo aplica `display: none` pela folha do navegador, que perde para
   qualquer `display` declarado numa classe — e `.channel` declara `flex`. Os
   botões que dependem da folha do sistema (Instagram, TikTok, "Outro app")
   apareciam no desktop mesmo com o JavaScript marcando `hidden`, e clicar
   neles chamava um `navigator.share` que não existe ali. O código estava
   certo desde sempre; faltava esta linha para ele valer. */
/* Coberto pela regra global de [hidden] no topo deste arquivo. */

.channel svg { width: 22px; height: 22px; flex: 0 0 22px; }

/* --- Convite por candidatura ---------------------------------------------
   Um cartão por candidatura apoiada, cada um com seus canais. A mensagem
   fica visível de propósito: a pessoa está prestes a mandar aquilo em nome
   dela para alguém que confia nela, e merece ler antes.
   ------------------------------------------------------------------------ */

.invite-card {
  background: var(--paper);
  border: 1.5px solid rgba(14, 17, 22, .12);
  border-radius: var(--r);
  padding: 15px;
  margin-bottom: 12px;
}

.invite-head {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-bottom: 10px;
}

/* --- Foto da candidatura -------------------------------------------------
   Uma cor só para todas as iniciais. Sortear cor por candidatura encheria a
   tela de fundos vermelhos, azuis e verdes atribuídos pelo próprio sistema —
   leitura de filiação feita pela plataforma, que é o que §1.4 proíbe.
   ------------------------------------------------------------------------ */

.cand-photo {
  width: 44px;
  height: 44px;
  flex: 0 0 44px;
  border-radius: 50%;
  object-fit: cover;
  background: var(--mist);
}

.cand-initials {
  display: grid;
  place-items: center;
  background: var(--volt);
  color: #fff;
  font-family: var(--display);
  font-weight: 800;
  font-size: 15px;
  letter-spacing: .02em;
}

.pick .cand-photo { width: 38px; height: 38px; flex: 0 0 38px; font-size: 13px; }

.invite-head .who { flex: 1; min-width: 0; }
.invite-head .who b { display: block; font-size: 15px; font-weight: 600; }
.invite-head .who small { color: var(--slate); font-size: 12.5px; }

.invite-head .num {
  font-family: var(--mono);
  font-size: 16px;
  color: var(--volt);
}

.invite-msg {
  font-size: 13px;
  line-height: 1.5;
  color: var(--slate);
  background: rgba(14, 17, 22, .04);
  border-radius: 10px;
  padding: 10px 12px;
  margin: 0 0 12px;
}

.invite-card .channels.social { margin-bottom: 0; }

/* --- Marcos (único lugar onde o âmbar aparece) ---------------------------- */

.milestone {
  padding: 18px;
  background: rgba(255, 154, 31, .13);
  border: 1.5px solid rgba(255, 154, 31, .35);
  border-radius: var(--r);
  margin-bottom: 22px;
  /* O âmbar da marca (#ff9a1f) sobre fundo claro fica ilegível como texto.
     O tom escuro mantém a identidade e passa em contraste. */
  color: var(--ember-ink);
}

.milestone .eyebrow { color: var(--ember-ink); opacity: .75; margin-bottom: 6px; }

.milestone h2 {
  font-size: 26px;
  margin: 0 0 12px;
  color: var(--ember-ink);
}

.milestone-bar {
  height: 8px;
  border-radius: 99px;
  background: rgba(138, 82, 0, .16);
  overflow: hidden;
  margin-bottom: 10px;
}

.milestone-bar span {
  display: block;
  height: 100%;
  border-radius: 99px;
  background: var(--ember-ink);
  width: 0;
  transition: width .8s cubic-bezier(.2,.7,.3,1);
}

.milestone .fineprint { color: var(--ember-ink); margin: 0; }

/* A escada inteira à vista: os quatro degraus, o alcançado marcado e o fim
   visível. Mostrar só o próximo degrau faz parecer esteira sem chegada. */
.milestone-ladder {
  list-style: none;
  display: flex;
  gap: 6px;
  margin: 14px 0 0;
  padding: 0;
}

.milestone-ladder li {
  flex: 1;
  min-width: 0;
  text-align: center;
  font-size: 10.5px;
  line-height: 1.3;
  color: var(--ember-ink);
  opacity: .42;
}

.milestone-ladder li.reached { opacity: .8; }
.milestone-ladder li.current { opacity: 1; font-weight: 600; }

.milestone-ladder .dot {
  display: block;
  width: 9px;
  height: 9px;
  margin: 0 auto 5px;
  border-radius: 50%;
  border: 1.5px solid var(--ember-ink);
}

.milestone-ladder li.reached .dot { background: var(--ember-ink); }

.milestone-ladder .lbl {
  display: block;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.milestone-ladder .at { display: block; opacity: .7; font-family: var(--mono); }

/* --- Ranking -------------------------------------------------------------- */

/* As duas abas da classificação: o estado e o Brasil.
   Discretas de propósito — trocar o recorte de uma lista é ajuste, não uma
   escolha do tamanho de "Continuar". */
.rank-abas {
  display: flex;
  gap: 6px;
  margin-bottom: 10px;
}

.rank-aba {
  padding: 7px 13px;
  font: inherit;
  font-size: 12.5px;
  font-weight: 600;
  color: var(--slate);
  background: transparent;
  border: 1.5px solid rgba(14, 17, 22, .12);
  border-radius: 99px;
  cursor: pointer;
}

.rank-aba.on {
  color: var(--volt);
  border-color: var(--volt);
  background: rgba(69, 52, 232, .07);
}

.rank-row {
  display: flex;
  align-items: center;
  gap: 13px;
  padding: 11px 2px;
  border-bottom: 1px solid rgba(14, 17, 22, .09);
  font-size: 14.5px;
}

.rank-row .pos {
  font-family: var(--mono);
  font-size: 13px;
  color: var(--slate);
  width: 26px;
}

.rank-row .nm { flex: 1; }
.rank-row .ct { font-family: var(--mono); color: var(--volt); }
.rank-row.me { color: var(--ember-ink); font-weight: 600; }
.rank-row.me .ct, .rank-row.me .pos { color: var(--ember-ink); }

/* --- Mensagens ------------------------------------------------------------ */

.alert {
  padding: 13px 15px;
  border-radius: var(--r);
  font-size: 14px;
  line-height: 1.5;
  margin-bottom: 16px;
  display: none;
}

.alert.show { display: block; }
.alert.err { background: rgba(217, 45, 32, .1); color: var(--danger); }
.alert.ok { background: rgba(69, 52, 232, .1); color: var(--volt); }


/* --- Acessibilidade ------------------------------------------------------- */

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
  }
  .edge { stroke-dashoffset: 0; }
  .node { opacity: 1; }

  /* O pulso para de vez. A regra geral acima só encurta a duração, e animação
     infinita encurtada vira tremor — que é exatamente o que quem liga esta
     preferência está pedindo para não ver. O giro da constelação nem começa:
     animarConstelacao consulta a mesma preferência e sai antes do primeiro
     quadro, deixando o desenho parado onde foi montado. */
  .pulso { animation: none !important; opacity: 0; }
}

.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;
}

/* Bolha da camada de resgate — bairro do OpenStreetMap, quando a malha do
   IBGE nao nomeia a area do jeito que o CEP devolve. O raio e calculado em
   js/mapa-br.js, depois do enquadramento: r vive em unidades do viewBox, e
   viewBox de cidade grande e dez vezes o de cidade pequena. */
.rede-mapa circle.bolha {
  stroke: var(--paper, #fff);
  stroke-width: 1.2;
  vector-effect: non-scaling-stroke;
  cursor: pointer;
}

.rede-mapa circle.bolha.n1 { fill: rgba(69, 52, 232, .35); }
.rede-mapa circle.bolha.n2 { fill: rgba(69, 52, 232, .5); }
.rede-mapa circle.bolha.n3 { fill: rgba(69, 52, 232, .65); }
.rede-mapa circle.bolha.n4 { fill: rgba(69, 52, 232, .8); }
.rede-mapa circle.bolha.n5 { fill: var(--volt); }
.rede-mapa circle.bolha.on { stroke: var(--ink); stroke-width: 2; }

