/* Mekuri — tema do app.
 *
 * Material 3 governa estrutura, alvo de toque, tipo e estado; a identidade do
 * Mekuri (tinta e índigo, capa como protagonista) entra pelos papéis de cor e
 * pela densidade, não desenhando componente próprio.
 *
 * Por que papel de cor e não hex solto: no esquema escuro do M3 o preenchimento
 * forte é claro e o texto em cima é escuro. Branco sobre o vermelho da marca
 * antiga media 4.01:1 — reprovado em AA — e era o mesmo par usado no botão
 * Continuar, no chip ligado e na barra de progresso.
 *
 * ---- Por que a paleta saiu do vermelho quente (2026-08-03) ----
 *
 * O app virou Mekuri, e o vermelho-escuro-quente tinha um problema que a troca
 * de nome deixou visível: **ele competia com as capas**. Arte de mangá e manhwa
 * é quase sempre saturada e quente, e um fundo da mesma família disputa a
 * atenção com aquilo que o app existe para mostrar. O princípio 1 do produto
 * diz que a página é o produto e o resto sai da frente — a paleta não estava
 * cumprindo isso.
 *
 * Tinta e índigo resolvem por oposição: superfícies frias e dessaturadas fazem
 * a capa avançar sem precisar de moldura, sombra ou brilho. O ciano do
 * `--primary` é o único acento frio brilhante, e por isso nunca é confundido
 * com arte; o âmbar do `--info` é a única cor quente, reservada ao que exige
 * atenção (NOVO).
 *
 * **Todo par foi medido, não estimado:** `tools/contraste.py` roda os 16 pares
 * que a interface desenha e `tests/test_core.py` os trava. A paleta nova passa
 * em AA com folga maior que a anterior — `--primary` sobre `--surface` foi de
 * 6.97:1 para 11.90:1. */

:root {
  /* ---- cor: papéis, não nomes de tinta ---------------------------------- */
  --primary: #8ad9ec;              /* ciano de tela: o brilho que um leitor noturno tem */
  --on-primary: #00323b;           /* texto/ícone sobre --primary: 8.7:1 */
  --primary-container: #17505f;
  --on-primary-container: #c4ecf8;

  /* Superfícies por elevação tonal: mesma família fria, claridade crescente.
     Substitui sombra empilhada, que no escuro só suja. O leve desvio para o
     azul (e não cinza puro) é o que faz a capa parecer impressa em cima. */
  --surface: #0e1116;
  --surface-1: #161a21;
  --surface-2: #1b2028;
  --surface-3: #20262f;
  --surface-4: #242b35;
  --surface-5: #29313c;
  --on-surface: #e6e9f0;
  --on-surface-variant: #b3bccb;   /* 9.9:1 sobre --surface */
  --outline: #8b95a5;              /* borda com significado (campo, contorno) */
  --outline-variant: #2c333e;      /* régua e separador */

  --inverse-surface: #e6e9f0;      /* snackbar M3: superfície invertida */
  --inverse-on-surface: #1b2028;
  --inverse-primary: #1c6376;

  --error: #ffb4ab;
  --on-error: #4a0906;
  --success: #8fdba4;              /* capítulo lido */
  --on-success: #05341a;
  /* **NOVO virou âmbar.** Era azul, que ao lado de um primário ciano viraria
     "mais um acento frio" — dois papéis distintos com a mesma voz. Âmbar é a
     única cor quente da paleta, e fica reservada ao que pede o olho. */
  --info: #f2c97d;                 /* badge NOVO */
  --on-info: #3d2a00;

  /* ---- forma ------------------------------------------------------------ */
  --shape-xs: 4px;
  --shape-s: 8px;
  --shape-m: 12px;
  --shape-l: 16px;
  --shape-xl: 28px;                /* diálogo e folha */
  --shape-full: 999px;

  /* ---- tipo: escala do M3, em rem para seguir o corpo de letra do sistema
   *
   * **Sem face própria, de propósito.** A restrição do produto é frontend sem
   * build *e* offline de verdade: uma face autohospedada teria de ir no APK e
   * no cache do service worker, e uma de CDN quebraria a primeira abertura em
   * modo avião. A voz da marca sai então de peso, largura e ritmo — que é o
   * que o leitor percebe de qualquer jeito num texto de interface curto.
   *
   * A escala ganhou `--t-display` para o nome do app e para os números que
   * merecem ser lidos de longe (a contagem da obra, o capítulo no leitor). */
  --t-display: 2rem;               /* 32sp — marca e número de destaque */
  --t-headline: 1.5rem;            /* 24sp */
  --t-title-l: 1.375rem;           /* 22sp — título da barra do topo */
  --t-title-m: 1rem;               /* 16sp */
  --t-title-s: .875rem;            /* 14sp */
  --t-body-l: 1rem;
  --t-body-m: .875rem;
  --t-body-s: .75rem;              /* 12sp — piso; nada abaixo disso */
  --t-label-l: .875rem;
  --t-label-m: .75rem;

  /* ---- estrutura -------------------------------------------------------- */
  --topo-h: 64px;                  /* M3 top app bar */
  --abas-h: 80px;                  /* M3 navigation bar */
  --alvo: 48px;                    /* alvo de toque mínimo */
  --lado: 16px;

  /* ---- movimento -------------------------------------------------------- */
  --ease: cubic-bezier(.2, 0, 0, 1);
  --dur: .2s;

  font-size: 16px;
  color-scheme: dark;
}

* { box-sizing: border-box; margin: 0; padding: 0; -webkit-tap-highlight-color: transparent; }
[hidden] { display: none !important; }

body {
  background: var(--surface);
  color: var(--on-surface);
  /* Roboto primeiro: é a face do sistema no Android, e a escala acima é a dela. */
  font-family: Roboto, "Noto Sans", system-ui, sans-serif;
  overscroll-behavior-y: none;
}

button { font: inherit; color: inherit; background: none; border: none; cursor: pointer; }
img { display: block; max-width: 100%; }
/* Sprite dos ícones: fica fora de vista sem `display:none`, que em WebView
   antigo às vezes derruba o `<use>` que aponta para os símbolos. */
#simbolos { position: absolute; width: 0; height: 0; overflow: hidden; }

/* Ícone padrão: traço que herda a cor do texto, então estado ativo/inativo é só
   trocar `color` — nada de dois arquivos por ícone. */
.ic svg, .aba svg, .lupa, .btn svg {
  width: 24px; height: 24px; fill: none; stroke: currentColor;
  stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round;
}

/* ------------------------------------------------------- camada de estado */
/* M3 responde ao toque com uma camada de estado sobre o próprio componente, não
   com troca de cor de fundo. O `.ondulacao` é injetado pelo JS no ponto tocado;
   a camada base cobre o resto (foco, pressionado sem coordenada). */
.ic, .btn, .aba, .chip, .folha-item, .cap, .fila-item, .fonte, .cartao,
.cartao-acao, .fab, .snack-acao, .ajustes-linha .btn {
  position: relative;
  overflow: hidden;
}
.ondulacao {
  position: absolute; border-radius: 50%; pointer-events: none;
  background: currentColor; opacity: .16; transform: scale(0);
  animation: ondular .45s var(--ease) forwards;
}
@keyframes ondular { to { transform: scale(2.4); opacity: 0; } }

@media (prefers-reduced-motion: reduce) {
  * { animation-duration: .01ms !important; transition-duration: .01ms !important; }
}

/* -------------------------------------------------------------- cabeçalho */
#cabecalho {
  position: sticky; top: 0; z-index: 20;
  background: var(--surface-2);
  border-bottom: 1px solid var(--outline-variant);
}
#topo {
  position: relative;
  display: flex; align-items: center; gap: 4px;
  height: calc(var(--topo-h) + env(safe-area-inset-top));
  padding: env(safe-area-inset-top) 4px 0 var(--lado);
}
#topo:has(#btn-voltar:not([hidden])) { padding-left: 4px; }
#titulo-topo {
  flex: 1; min-width: 0;
  font-size: var(--t-title-l); font-weight: 500; letter-spacing: 0;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  font-variant-numeric: tabular-nums;   /* "12 de 184" não dança ao trocar de tela */
}

/* **A marca só aparece onde ela é a resposta.** `telas/biblioteca.js` já troca
   o título para "Biblioteca" quando você está na biblioteca — o topo diz onde
   você está, não como o app se chama. Sobra a abertura, e é ali que "Mekuri"
   vale um tratamento: peso alto e a letra apertada que uma palavra curta
   aguenta, com o `i` final pontuado em ciano.

   Nada disso é imagem: é a mesma face do sistema, e some junto com o título
   quando a tela tem outro nome a dar. */
#titulo-topo.marca {
  font-size: var(--t-display); font-weight: 800; letter-spacing: -.03em;
}
#titulo-topo.marca::after {
  content: "."; color: var(--primary);
}
/* Botão de ícone M3: 48dp de alvo com 24dp de glifo dentro. */
.ic {
  width: var(--alvo); height: var(--alvo); border-radius: var(--shape-full);
  flex: none; color: var(--on-surface); display: grid; place-items: center;
}
.ic.girando svg { animation: girar .8s linear infinite; }
@keyframes girar { to { transform: rotate(360deg); } }

/* Barra de progresso indeterminada do topo. Um fio de 3px diz "estou
   trabalhando" sem ocupar espaço nem competir com o título. */
#carga {
  position: absolute; left: 0; right: 0; bottom: -1px; height: 3px;
  overflow: hidden; background: var(--primary-container);
}
#carga::after {
  content: ""; position: absolute; inset: 0; width: 40%;
  background: var(--primary); animation: correr 1.1s ease-in-out infinite;
}
@keyframes correr {
  0% { transform: translateX(-100%); } 100% { transform: translateX(320%); }
}

/* -------------------------------------------------------------- busca */
#barra-busca {
  display: flex; align-items: center; gap: 12px;
  margin: 4px var(--lado) 12px; padding: 0 16px; height: 56px;
  background: var(--surface-3); border-radius: var(--shape-full);
}
#barra-busca .lupa { flex: none; color: var(--on-surface-variant); }
#busca {
  flex: 1; min-width: 0; height: 100%; background: none; border: none; outline: none;
  color: var(--on-surface); font-size: var(--t-body-l);
}
#busca::placeholder { color: var(--on-surface-variant); }
#busca::-webkit-search-cancel-button { filter: invert(.7); }

/* --------------------------------------------------------------- chips */
/* Só a tela de Fontes usa chip agora: na biblioteca a faixa de chips virou o
   funil do topo, que abre folha. */
.chip {
  flex: none; display: inline-flex; align-items: center; gap: 6px;
  min-height: 40px; padding: 0 16px; border-radius: var(--shape-s);
  background: transparent; color: var(--on-surface-variant);
  font-size: var(--t-label-l); font-weight: 500; white-space: nowrap;
  border: 1px solid var(--outline);
}
/* O chip mede 40dp (M3), mas o dedo precisa de 48: a expansão invisível resolve
   sem engordar a faixa.

   **A expansão não funcionava, e a causa era o `overflow: hidden` da camada de
   estado** (a regra da `.ondulacao`, acima). Ela existe para recortar a onda no
   contorno do componente, e recortava junto o `::before` que estende o alvo:
   medido, o `::before` saía com 46px de altura em vez de 48, e um toque a 3px
   acima do chip acertava `.fontes-filtros` — o pai. A regra "48dp sem exceção"
   do DESIGN.md estava escrita e não valia na única tela que usa chip.

   `overflow: visible` só aqui: o chip não tem cantos que a onda precise
   recortar (ela nasce dentro e a borda é reta), então nada se perde. */
.chip { overflow: visible; }
.chip::before { content: ""; position: absolute; inset: -4px 0; }
.chip svg { width: 18px; height: 18px; fill: none; stroke: currentColor; stroke-width: 2; }
.chip.on {
  background: var(--primary-container); color: var(--on-primary-container);
  border-color: transparent;
}

/* -------------------------------------------------------------- abas */
#abas {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 25;
  display: grid; grid-auto-flow: column; grid-auto-columns: 1fr;
  height: calc(var(--abas-h) + env(safe-area-inset-bottom));
  padding: 12px 0 env(safe-area-inset-bottom);
  background: var(--surface-2);
  border-top: 1px solid var(--outline-variant);
}
/* **Existem no DOM, mas não na barra do celular.** `#abas` distribui as colunas
   com `grid-auto-columns: 1fr`: sem esta linha, o espaçador e o "Adicionar
   obra" do trilho virariam duas colunas na barra inferior e espremeriam as
   quatro abas de 25% para 16% cada. Eles só ganham lugar no trilho lateral,
   onde o `flex` de `html.alvo-web` os reconhece. */
#abas > .trilho-espaco, #abas > .trilho-add { display: none; }

.aba {
  display: flex; flex-direction: column; align-items: center; gap: 4px;
  color: var(--on-surface-variant); font-size: var(--t-label-m); font-weight: 500;
  overflow: visible;
}
/* Pílula do M3 atrás do ícone: diz qual aba está ativa por forma, não só por
   cor — o que também resolve daltonismo sem texto extra. */
.aba .ind {
  width: 64px; height: 32px; border-radius: var(--shape-full);
  display: grid; place-items: center;
  transition: background var(--dur) var(--ease);
}
.aba.on { color: var(--on-surface); }
.aba.on .ind { background: var(--primary-container); color: var(--on-primary-container); }
.aba.on .ind svg { stroke-width: 2.2; }

/* --------------------------------------------------------------- FAB */
/* Uma ação primária por tela, no alcance do polegar. O `+` morava no canto
   superior direito, o ponto mais difícil de alcançar numa tela de 915px. */
.fab {
  position: fixed; right: var(--lado); z-index: 26;
  bottom: calc(var(--abas-h) + 16px + env(safe-area-inset-bottom));
  width: 56px; height: 56px; border-radius: var(--shape-l);
  display: grid; place-items: center;
  background: var(--primary-container); color: var(--on-primary-container);
  box-shadow: 0 3px 6px rgba(0,0,0,.42), 0 1px 3px rgba(0,0,0,.3);
}
.fab svg { width: 24px; height: 24px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; }
body.lendo .fab { display: none; }

/* -------------------------------------------------------------- conteúdo */
#tela {
  padding: 12px var(--lado);
  padding-bottom: calc(var(--abas-h) + 32px + env(safe-area-inset-bottom));
}

.secao-cab { display: flex; align-items: baseline; gap: 10px; margin: 24px 2px 12px; }
.secao-cab:first-child { margin-top: 4px; }
.secao-titulo { font-size: var(--t-title-m); font-weight: 500; flex: 1; }
.ver-tudo {
  color: var(--primary); font-size: var(--t-label-l); font-weight: 500;
  flex: none; padding: 8px; margin: -8px;
}

/* Duas colunas no celular: a capa é o que identifica a obra, e a 100px de largura
   a arte vira miniatura. O ganho de densidade veio do texto ter subido para cima
   da arte — o cartão é só a capa, sem as duas linhas + barra que antes somavam
   ~90px por obra abaixo dela. */
.grade {
  display: grid; gap: 10px 8px;
  grid-template-columns: repeat(auto-fill, minmax(148px, 1fr));
}

/* `<button>` desde a correção de acessibilidade: como `<div onclick>` a grade
   inteira era invisível para o TalkBack. `display: block` e `text-align: left`
   desfazem o padrão de botão sem mudar um pixel do que já estava desenhado. */
.cartao {
  position: relative; min-width: 0; border-radius: var(--shape-m);
  display: block; width: 100%; padding: 0; text-align: left;
  font: inherit; color: inherit; background: none; border: none;
}
/* Foco visível: sem isto navegar por teclado/D-pad percorre a grade às cegas. */
.cartao:focus-visible, .cap:focus-visible {
  outline: 3px solid var(--primary); outline-offset: 2px;
}
.capa {
  width: 100%; aspect-ratio: 2/3; object-fit: cover; border-radius: var(--shape-m);
  background: var(--surface-3);
}
/* Capa que não carregou: sem isto o alt/ícone quebrado da imagem aparecia por
   baixo do degradê e o cartão virava um borrão sem título legível. */
.capa.sem-arte { background: var(--surface-4); }

/* Faixa inferior com o título. O degradê vive no próprio bloco (sem
   pseudo-elemento) e só escurece o terço de baixo, que é onde a arte de capa
   quase sempre é fundo. `pointer-events: none` deixa o toque e a ondulação
   passarem para o cartão inteiro. */
.cartao-info {
  position: absolute; inset: auto 0 0 0; padding: 32px 10px 10px;
  pointer-events: none;
  background: linear-gradient(to top,
    rgba(5,7,11,.92) 0%, rgba(5,7,11,.72) 45%, rgba(5,7,11,0) 100%);
}
/* Só o desktop mostra a linha de metadados — ver `cartao()` em `core/ui.js`.
   No celular, duas linhas de texto por item custavam a sexta obra da tela. */
.cartao-meta { display: none; }

.cartao-info h4 {
  font-size: var(--t-body-m); font-weight: 500; line-height: 1.3; color: #fff;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
  /* o degradê basta na maioria das capas; a sombra segura o caso da capa clara */
  text-shadow: 0 1px 3px rgba(0,0,0,.65);
}

/* `33/175` no canto de cima: é a pergunta que se faz olhando a estante ("parei
   em qual?") e ocupa o canto que a arte quase nunca usa. Pílula clara e opaca —
   sobre arte escura (a maioria das capas) o selo escuro sumia. Tabular para os
   números não dançarem de cartão para cartão. */
.contagem {
  position: absolute; top: 6px; left: 6px;
  background: var(--inverse-surface); color: var(--inverse-on-surface);
  font-size: var(--t-label-m); font-weight: 600; font-variant-numeric: tabular-nums;
  padding: 3px 8px; border-radius: var(--shape-s);
}
/* Prévia de capítulos no resultado de busca. Mesmo selo da estante, no mesmo
   canto — é o mesmo tipo de informação ("quanto tem aqui"), e inventar uma
   segunda forma para dizer a mesma coisa é o que faz interface parecer colcha.

   O caso `0` ganha cor de erro porque é o mais caro de descobrir tarde: a obra
   existe na fonte, mas **nenhum** capítulo nos idiomas que o app importa, e
   adicioná-la traz uma estante vazia. Sem o destaque, `0` se lê como mais um
   número pequeno. */
.contagem.aviso { background: var(--error); color: var(--on-error); }

/* NOVO foi para a direita: a esquerda agora é da contagem. */
.badge {
  position: absolute; top: 6px; right: 6px; background: var(--info); color: var(--on-info);
  font-size: var(--t-label-m); font-weight: 700; padding: 3px 8px; border-radius: var(--shape-s);
}

/* Resultado de busca em fonte (`cartaoResultado`): não tem progresso nem
   contagem, então o título fica embaixo da capa mesmo. */
.cartao > h4 {
  font-size: var(--t-body-s); font-weight: 500; margin-top: 8px; line-height: 1.3;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
/* Botão redondo sobre a capa (adicionar da busca): a ação fica no item, não numa
   faixa embaixo que dobrava a altura de cada cartão. */
.cartao-acao {
  position: absolute; top: 6px; right: 6px; width: var(--alvo); height: var(--alvo);
  border-radius: 50%; display: grid; place-items: center;
  background: rgba(14,17,22,.82); color: var(--on-surface);
}
.cartao-acao svg { width: 22px; height: 22px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; }
.cartao-acao.pronto { background: var(--primary); color: var(--on-primary); }
.cartao-acao:disabled { opacity: .55; }
.barra-prog {
  height: 4px; border-radius: var(--shape-full); background: var(--surface-4);
  margin-top: 10px; overflow: hidden;
}
.barra-prog > i { display: block; height: 100%; background: var(--primary); }

/* -------------------------------------------------------------- detalhes */
.det-topo { display: flex; gap: 16px; margin-bottom: 8px; }
.det-topo .capa { width: 132px; flex: none; }
.det-info { min-width: 0; }
.det-info h2 { font-size: var(--t-headline); font-weight: 500; line-height: 1.25; }
.det-info .sub { color: var(--on-surface-variant); font-size: var(--t-body-m); margin-top: 8px; }
.tags { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 12px; }
.tag {
  font-size: var(--t-body-s); background: var(--surface-3); color: var(--on-surface-variant);
  padding: 5px 10px; border-radius: var(--shape-s);
}
/* Sinopse em 4 linhas, com o resto a um toque. Sem o corte ela empurrava
   "Capítulos (N)" e a lista inteira para baixo da dobra no celular — e a lista
   é a razão de estar nesta tela. O `<details>` nativo dá o "mais" sem JS. */
.sinopse {
  color: var(--on-surface-variant); font-size: var(--t-body-m); line-height: 1.6; margin: 16px 0;
}
.sinopse-txt.cortada {
  display: -webkit-box; -webkit-line-clamp: 4; -webkit-box-orient: vertical; overflow: hidden;
}
.sinopse-mais {
  color: var(--primary); font-size: var(--t-label-l); font-weight: 500;
  min-height: var(--alvo); padding: 0; text-align: left;
}
.acoes { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin: 16px 0; }

/* Botões M3. `.btn` é o tonal (ação secundária), `.primario` é o preenchido
   (uma por tela), `.texto` é o de texto (diálogo). Altura 48 em todos: é o alvo
   mínimo, e igualar as alturas é o que faz a linha parecer desenhada. */
.btn {
  min-height: var(--alvo); padding: 0 20px; border-radius: var(--shape-full);
  background: var(--surface-3); color: var(--on-surface);
  font-size: var(--t-label-l); font-weight: 500;
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
}
.btn svg { width: 20px; height: 20px; }
.btn.primario { background: var(--primary); color: var(--on-primary); }
.btn.texto { background: none; color: var(--primary); padding: 0 12px; }
.btn.perigo { color: var(--error); }
.btn:disabled { opacity: .38; }

/* Continuar é o único botão com texto na tela de obra: leva o capítulo e o ponto
   de retomada, que é o que o leitor quer saber antes de tocar. */
.acoes .continuar { flex: 1 1 220px; justify-content: flex-start; text-align: left; padding: 8px 20px; }
.continuar-txt { display: flex; flex-direction: column; line-height: 1.25; min-width: 0; }
.continuar-txt b { font-size: var(--t-label-l); }
.continuar-txt small {
  font-weight: 400; font-size: var(--t-body-s); opacity: .9;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.acoes .ic.acao {
  width: var(--alvo); height: var(--alvo); border-radius: var(--shape-full);
  background: var(--surface-3);
}
.acoes .ic.acao.on { background: var(--primary-container); color: var(--on-primary-container); }
.acoes .ic.acao.on svg { fill: currentColor; }

/* Dica do gesto: o toque longo é a única porta para as ações em massa e não tem
   affordance própria. Uma linha na régua da seção custa menos que descobrir por
   acidente. */
.dica { font-size: var(--t-body-s); color: var(--on-surface-variant); margin: -6px 2px 10px; }

.lista-cap { display: flex; flex-direction: column; }
.cap {
  display: flex; align-items: center; gap: 12px;
  min-height: 56px; padding: 8px 12px; border-radius: var(--shape-m);
  /* segurar o capítulo abre o menu de ações; sem isto o WebView seleciona o
     texto e mostra a lupa do sistema por cima */
  -webkit-touch-callout: none; -webkit-user-select: none; user-select: none;
}
.cap + .cap { box-shadow: inset 0 1px var(--outline-variant); }
.cap.lido .nome { color: var(--on-surface-variant); }
.cap.andamento { background: var(--surface-2); }
.cap .nome { flex: 1; min-width: 0; font-size: var(--t-body-l); line-height: 1.35; }
.cap .nome small {
  display: block; color: var(--on-surface-variant); font-size: var(--t-body-s); margin-top: 3px;
}
.cap .pin {
  font-size: var(--t-label-m); font-weight: 700; flex: none;
  background: var(--info); color: var(--on-info); padding: 3px 8px; border-radius: var(--shape-s);
}
/* Check e download: alvo de 48, glifo pequeno. A caixa cresceu, o desenho não. */
.cap .check, .cap .down {
  flex: none; width: var(--alvo); height: var(--alvo); border-radius: 50%;
  display: grid; place-items: center; background: none;
}
.cap .check { color: var(--outline); }
.cap .check svg {
  width: 22px; height: 22px; fill: none; stroke: currentColor;
  stroke-width: 2.4; stroke-linecap: round; stroke-linejoin: round;
}
.cap.lido .check { color: var(--success); }
.cap .down { color: var(--on-surface-variant); }
.cap .down svg {
  width: 22px; height: 22px; fill: none; stroke: currentColor;
  stroke-width: 1.9; stroke-linecap: round; stroke-linejoin: round;
}
.cap .down.pronto { color: var(--success); }
.cap .down.erro { color: var(--error); }
.cap .down:disabled { opacity: .38; }
/* Progresso como anel em volta do próprio botão: cabe no lugar que já existia e
   é atualizado trocando uma custom property, sem redesenhar a linha. */
.cap .down.baixando {
  color: var(--on-surface);
  background: conic-gradient(var(--primary) calc(var(--p, 0) * 1%), var(--surface-4) 0);
  box-shadow: inset 0 0 0 4px var(--surface);
}
.cap.andamento .down.baixando { box-shadow: inset 0 0 0 4px var(--surface-2); }

/* ---------------------------------------------------- tela de downloads */
.lista-fila { display: flex; flex-direction: column; gap: 8px; }
.fila-item {
  display: flex; align-items: center; gap: 12px; padding: 12px;
  border-radius: var(--shape-m); background: var(--surface-2);
}
.fila-texto { flex: 1; min-width: 0; }
.fila-texto .nome { font-size: var(--t-body-l); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.fila-texto small { display: block; color: var(--on-surface-variant); font-size: var(--t-body-s); margin: 4px 0 8px; }
.fila-texto .barra-prog { margin-top: 0; }
.fila-texto .barra-prog > i { transition: width .3s linear; }
.ic.pequeno { width: var(--alvo); height: var(--alvo); }
.ic.pequeno svg { width: 20px; height: 20px; }
.ic.perigo { color: var(--error); }
.mini-capa {
  width: 40px; height: 60px; flex: none; object-fit: cover;
  border-radius: var(--shape-s); background: var(--surface-3);
}

/* -------------------------------------------------------------- leitor */
#tela.leitor { padding: 0; background: #000; }
body.lendo #abas, body.lendo #cabecalho { display: none; }
.leitor-fluxo, .leitor-bloco { display: flex; flex-direction: column; }
/* Emenda entre capítulos: precisa cortar a arte com clareza, senão o leitor não
   percebe que trocou de capítulo — mas sem virar uma tela de intervalo. */
.cap-banner {
  display: flex; align-items: center; gap: 12px;
  padding: 20px var(--lado); background: var(--surface-1);
}
.cap-banner .marca { width: 3px; align-self: stretch; background: var(--primary); border-radius: 2px; }
.cap-banner-txt { min-width: 0; }
.cap-banner small {
  display: block; color: var(--on-surface-variant); font-size: var(--t-body-s);
  text-transform: uppercase; letter-spacing: .08em;
}
.cap-banner b { font-size: var(--t-title-m); font-weight: 500; }
.leitor-emendando { padding: 26px var(--lado); text-align: center; color: var(--on-surface-variant); font-size: var(--t-body-m); }
.leitor-emendando.ruim { color: var(--error); }
.leitor-paginas { display: flex; flex-direction: column; align-items: center; }
.leitor-paginas img { width: 100%; max-width: 900px; }
.leitor-paginas img.falhou {
  min-height: 40vh; background: var(--surface-1); display: grid; place-items: center;
  color: var(--on-surface-variant); font-size: var(--t-body-m);
}

/* HUD do leitor: aparece ao toque e sai sozinho. Sem ele a tela é um vazio preto
   sem capítulo, sem posição e sem saída visível depois do primeiro segundo. */
.hud-prog {
  position: fixed; left: 0; top: 0; height: 3px; z-index: 32;
  background: var(--primary); width: 0;
  transition: width .12s linear;
}
.hud {
  position: fixed; left: 0; right: 0; top: 0; z-index: 31;
  display: flex; align-items: center; gap: 8px;
  padding: env(safe-area-inset-top) 8px 0 4px;
  height: calc(56px + env(safe-area-inset-top));
  background: linear-gradient(rgba(0,0,0,.86), rgba(0,0,0,.55) 70%, transparent);
  transition: opacity var(--dur) var(--ease), transform var(--dur) var(--ease);
}
.hud.oculto { opacity: 0; transform: translateY(-8px); pointer-events: none; }
.hud-txt { flex: 1; min-width: 0; }
.hud-txt b {
  display: block; font-size: var(--t-title-s); font-weight: 500;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.hud-txt small { display: block; font-size: var(--t-body-s); color: var(--on-surface-variant); }
/* Saída de emergência: só aparece quando as páginas falham (site com bloqueio
   anti-hotlink). Na leitura normal fica escondida e não disputa a tela. */
.leitor-escape {
  position: fixed; right: var(--lado); bottom: calc(20px + env(safe-area-inset-bottom));
  z-index: 30; display: inline-flex; align-items: center;
  min-height: 56px; padding: 0 20px; border-radius: var(--shape-l);
  background: var(--primary); color: var(--on-primary);
  font-size: var(--t-label-l); font-weight: 500; text-decoration: none;
  box-shadow: 0 3px 6px rgba(0,0,0,.42), 0 1px 3px rgba(0,0,0,.3);
}
.leitor-fim {
  padding: 40px 20px calc(60px + env(safe-area-inset-bottom)); text-align: center;
  color: var(--on-surface-variant);
}
.leitor-nav { display: flex; gap: 12px; justify-content: center; margin-top: 20px; flex-wrap: wrap; }

/* ------------------------------------------------- snackbar / diálogo / folha */
/* Snackbar do M3: superfície invertida e, quando a ação é desfazível, um botão
   dentro. Toast sem ação não deixava desfazer nada — e "marcar até aqui" vira
   até 180 capítulos de uma vez. */
#toast {
  position: fixed; left: var(--lado); right: var(--lado);
  bottom: calc(var(--abas-h) + 16px + env(safe-area-inset-bottom));
  z-index: 60; display: flex; align-items: center; gap: 8px;
  background: var(--inverse-surface); color: var(--inverse-on-surface);
  padding: 14px 8px 14px 16px; border-radius: var(--shape-xs);
  font-size: var(--t-body-m); line-height: 1.4;
  box-shadow: 0 3px 6px rgba(0,0,0,.42), 0 1px 3px rgba(0,0,0,.3);
  animation: subir-snack .2s var(--ease);
}
body.lendo #toast { bottom: calc(16px + env(safe-area-inset-bottom)); }
@keyframes subir-snack { from { transform: translateY(12px); opacity: 0; } }
#toast .txt { flex: 1; min-width: 0; }
.snack-acao {
  flex: none; min-height: 40px; padding: 0 12px; border-radius: var(--shape-xs);
  color: var(--inverse-primary); font-size: var(--t-label-l); font-weight: 600;
  text-transform: none;
}

#modal {
  position: fixed; inset: 0; z-index: 50; display: grid; place-items: center;
  background: rgba(0,0,0,.6); padding: 24px;
}
#modal .caixa {
  background: var(--surface-3); border-radius: var(--shape-xl);
  padding: 24px; width: 100%; max-width: 440px;
  animation: surgir .2s var(--ease);
}
@keyframes surgir { from { transform: scale(.94); opacity: 0; } }
#modal h3 { font-size: var(--t-headline); font-weight: 400; margin-bottom: 16px; }
#modal input, #modal select, #modal textarea {
  width: 100%; min-height: var(--alvo);
  background: var(--surface-1); border: 1px solid var(--outline); border-radius: var(--shape-xs);
  padding: 12px 16px; color: var(--on-surface); font-size: var(--t-body-l); margin-bottom: 12px;
}
#modal textarea { min-height: 96px; }
/* M3 põe as ações do diálogo à direita, como botões de texto: o destrutivo deixa
   de ter o mesmo peso visual do confirmar. */
#modal-acoes { display: flex; gap: 8px; justify-content: flex-end; margin-top: 8px; flex-wrap: wrap; }
.modal-aviso { color: var(--on-surface-variant); font-size: var(--t-body-m); line-height: 1.5; margin: 4px 0 2px; }

/* Menu do ⋮ como folha de baixo: no celular o polegar alcança a base da tela,
   não o canto superior direito onde o botão fica. */
#folha { position: fixed; inset: 0; z-index: 55; background: rgba(0,0,0,.6); display: flex; align-items: flex-end; }
.folha-caixa {
  width: 100%; background: var(--surface-2);
  border-radius: var(--shape-xl) var(--shape-xl) 0 0;
  padding: 8px 8px calc(16px + env(safe-area-inset-bottom));
  animation: subir .22s var(--ease);
  touch-action: none;
}
@keyframes subir { from { transform: translateY(100%); } }
.folha-caixa::before {
  content: ""; display: block; width: 32px; height: 4px; border-radius: var(--shape-full);
  background: var(--outline-variant); margin: 6px auto 12px;
}
.folha-item {
  display: flex; align-items: center; gap: 16px; width: 100%;
  min-height: 56px; padding: 8px 16px; border-radius: var(--shape-l);
  font-size: var(--t-body-l); text-align: left;
}
.folha-item svg {
  width: 24px; height: 24px; flex: none; fill: none; stroke: currentColor;
  stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round;
  color: var(--on-surface-variant);
}
.folha-item.perigo, .folha-item.perigo svg { color: var(--error); }
#folha-titulo {
  padding: 4px 16px 8px; font-size: var(--t-title-s); font-weight: 500;
  color: var(--on-surface-variant);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* Cabeçalho de identidade da folha de conta: mesmo desenho de Ajustes, para a
   conta se apresentar igual nos dois lugares. Anula o `nowrap` do título comum,
   porque aqui há duas linhas de propósito. */
#folha-titulo.folha-conta {
  display: flex; align-items: center; gap: 14px;
  padding: 8px 16px 14px; white-space: normal; overflow: visible;
  box-shadow: inset 0 -1px var(--outline-variant); margin-bottom: 4px;
}

/* ------------------------------------------------------ descobrir / fontes */
.descobrir-busca { display: flex; gap: 8px; margin-bottom: 12px; }
.descobrir-busca input {
  flex: 1; min-width: 0; min-height: var(--alvo);
  background: var(--surface-3); border: none; border-radius: var(--shape-full);
  padding: 0 20px; color: var(--on-surface); font-size: var(--t-body-l);
}
.descobrir-busca .btn { flex: none; }

/* `pre-line` para as mensagens de duas frases quebrarem onde o texto manda. */
/* **O botão precisa sair da linha do texto.** A regra era `margin-top: 20px`
   num botão que continuava sendo conteúdo em linha do próprio `.vazio` — e
   margem vertical não separa caixa em linha. Numa tela estreita a quebra
   natural escondia o defeito; a 1440px, visto no navegador em 2026-08-03, o
   "Adicionar obra" encostava no fim da frase, como se fosse a última palavra
   dela.

   `flex` em coluna resolve na origem: o botão vira item próprio e o `gap`
   separa de verdade. A mensagem é um **nó de texto** solto (`vazio(msg)` passa
   a string direto), então quem a envolve é a caixa anônima do flex — e por
   isso a medida legível vem do `max-width` do próprio `.vazio`, não de um
   seletor de filho, que não a alcançaria. Sem ela a frase atravessava 1100px
   numa linha só a 1440px. */
.vazio {
  display: flex; flex-direction: column; align-items: center; gap: 20px;
  max-width: 52ch; margin-inline: auto;
  text-align: center; color: var(--on-surface-variant); padding: 48px 24px;
  line-height: 1.6; white-space: pre-line; font-size: var(--t-body-m);
}
/* ------------------------------------------------------ estado de partida
   O bloco de `partida()` em `core/ui.js` — a tela que ainda **não foi usada**,
   distinta do `.vazio` acima, que é o fim de um caminho ("nada encontrado").

   Por que não é só um `.vazio` com mais texto: o defeito que ele corrige é de
   **ancoragem**, não de conteúdo. Uma frase centralizada num vazio de 900px
   não dá ao olho onde pousar, e a tela lê como inacabada mesmo dizendo a coisa
   certa. Ícone, título e passos numerados dão três alturas diferentes de
   contraste, e é isso que faz o bloco parecer desenhado em vez de sobrado.

   `border` e `background` fecham o bloco: sem eles os passos flutuavam soltos
   e a lista parecia conteúdo da tela, não explicação dela. */
.partida {
  display: flex; flex-direction: column; align-items: center;
  max-width: 46ch; margin: 40px auto; padding: 40px 32px;
  background: var(--surface-2); border: 1px solid var(--surface-4);
  border-radius: var(--shape-l);
  text-align: center; color: var(--on-surface-variant);
}
.partida-icone {
  display: grid; place-items: center;
  width: 56px; height: 56px; margin-bottom: 20px;
  border-radius: 50%; background: var(--surface-4); color: var(--primary);
}
/* Traço, **não** preenchimento: o sprite de `index.html` é desenhado a linha
   (ver `.ic svg` acima). Com `fill: currentColor` a bússola do Descobrir virava
   um disco sólido — o círculo externo pintado por dentro. */
.partida-icone svg {
  width: 28px; height: 28px; fill: none; stroke: currentColor;
  stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round;
}
.partida-titulo {
  margin: 0; color: var(--on-surface);
  font-size: var(--t-title-l); font-weight: 600; line-height: 1.3;
}
.partida-msg {
  margin: 10px 0 0; line-height: 1.6; font-size: var(--t-body-m);
}
/* Os passos são a razão de o bloco existir: "ligue as fontes, depois busque" é
   uma **sequência**, e parágrafo não mostra sequência. `text-align: left`
   porque lista centralizada não tem margem comum para o olho descer. */
.partida-passos {
  margin: 24px 0 0; padding: 0; list-style: none; counter-reset: passo;
  display: flex; flex-direction: column; gap: 12px;
  text-align: left; width: 100%; font-size: var(--t-body-m); line-height: 1.5;
}
.partida-passos li {
  display: grid; grid-template-columns: 24px 1fr; gap: 12px; align-items: start;
}
.partida-passos li::before {
  counter-increment: passo; content: counter(passo);
  display: grid; place-items: center;
  width: 24px; height: 24px; border-radius: 50%;
  background: var(--surface-4); color: var(--on-surface);
  font-size: var(--t-body-s); font-weight: 600; font-variant-numeric: tabular-nums;
}
.partida .btn { margin-top: 28px; }

.spinner {
  width: 36px; height: 36px; border: 3px solid var(--surface-4); border-top-color: var(--primary);
  border-radius: 50%; margin: 56px auto; animation: girar .8s linear infinite;
}

.fontes-barra { display: flex; flex-direction: column; gap: 10px; margin-bottom: 12px; }
.fontes-barra input[type=search] {
  width: 100%; min-height: var(--alvo); padding: 0 20px; border-radius: var(--shape-full);
  border: none; background: var(--surface-3); color: var(--on-surface); font-size: var(--t-body-l);
}
.fontes-filtros { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; }
.sel {
  flex: 1 1 45%; min-height: var(--alvo); padding: 0 12px; border-radius: var(--shape-xs);
  font-size: var(--t-body-m);
  border: 1px solid var(--outline); background: var(--surface-1); color: var(--on-surface);
}
.fontes-conta { font-size: var(--t-body-s); color: var(--on-surface-variant); margin: 2px 2px 10px; }
.lista-fontes { display: flex; flex-direction: column; }
.fonte {
  display: flex; align-items: center; gap: 12px; min-height: 64px; padding: 8px 12px;
  border-radius: var(--shape-m);
}
.fonte + .fonte { box-shadow: inset 0 1px var(--outline-variant); }
.fonte-info { flex: 1; min-width: 0; }
.fonte-info .nome { font-size: var(--t-body-l); }
.fonte-info small { display: block; color: var(--on-surface-variant); font-size: var(--t-body-s); margin-top: 3px; }
/* Fonte que pode ser ligada mas que a busca não sabe consultar. Cor de erro e
   não um ícone: é uma condição do texto ("sem motor no app"), e o texto já a
   diz — a cor só faz o olho parar nela ao varrer a lista. */
.fonte-info small.aviso { color: var(--error); }
/* Dois botões no fim da linha (Lixeira: restaurar e apagar de vez). Classe
   própria em vez de `.acoes`, que tem `margin: 16px 0` e serve a uma barra de
   ações do corpo da tela, não a uma linha de lista de 64 px. */
.acoes-linha { display: flex; align-items: center; gap: 4px; flex: 0 0 auto; }
/* Interruptor M3: alvo de 48, desenho de 32×52, estado visível por posição e por
   preenchimento — não só por cor. */
.toggle {
  flex: 0 0 auto; width: 52px; height: var(--alvo); border-radius: var(--shape-full);
  position: relative; background: none; border: none;
}
.toggle::before {
  content: ""; position: absolute; left: 0; top: 8px; width: 52px; height: 32px;
  border-radius: var(--shape-full); background: var(--surface-4);
  border: 2px solid var(--outline); transition: background var(--dur) var(--ease);
}
.toggle::after {
  content: ""; position: absolute; top: 18px; left: 10px; width: 12px; height: 12px;
  border-radius: 50%; background: var(--outline);
  transition: left var(--dur) var(--ease), width var(--dur) var(--ease),
              height var(--dur) var(--ease), top var(--dur) var(--ease), background var(--dur) var(--ease);
}
.toggle.on::before { background: var(--primary); border-color: var(--primary); }
.toggle.on::after { top: 12px; left: 26px; width: 24px; height: 24px; background: var(--on-primary); }
.toggle:disabled { opacity: .38; }

/* ----------------------------------------------------- tela de ajustes */
/* Cada grupo é um cartão: o Android mostrava linhas soltas separadas por régua e
   nada dizia onde um assunto terminava. */
.ajustes-grupo {
  background: var(--surface-2); border-radius: var(--shape-l);
  padding: 8px 16px 16px; margin-bottom: 12px;
}
.ajustes-grupo h3 {
  font-size: var(--t-title-s); font-weight: 500; letter-spacing: .01em;
  color: var(--primary); margin: 12px 0 4px;
}
.ajustes-linha { display: flex; align-items: center; gap: 12px; min-height: 56px; padding: 6px 0; }
.ajustes-linha + .ajustes-linha { box-shadow: inset 0 1px var(--outline-variant); }
.ajustes-texto { flex: 1; min-width: 0; }
.ajustes-texto .nome { font-size: var(--t-body-l); }
.ajustes-texto small {
  display: block; color: var(--on-surface-variant); font-size: var(--t-body-s);
  margin-top: 4px; line-height: 1.45;
}
.ajustes-grupo .ajuda { font-size: var(--t-body-m); color: var(--on-surface-variant); margin: 4px 0 12px; line-height: 1.5; }
.ajustes-grupo .campo {
  width: 100%; min-height: var(--alvo); padding: 12px 16px; border-radius: var(--shape-xs);
  font-size: var(--t-body-m);
  border: 1px solid var(--outline); background: var(--surface-1); color: var(--on-surface);
}
.ajustes-acoes { display: flex; gap: 8px; margin-top: 12px; flex-wrap: wrap; }
.ajustes-acoes .btn { flex: 1 1 auto; }

/* ------------------------------------------------------------ identidade
   A conta é **quem você é**, não mais uma linha de configuração. Antes, o
   estado ficava numa frase de 12sp ("Ligado como …") que dizia o nome do
   *aparelho* — a pergunta "logado com qual e-mail?" não tinha resposta na
   tela. E as três ações (testar, sair, sincronizar) tinham peso idêntico,
   com a destrutiva do lado da diagnóstica.

   O bloco abaixo dá à conta a hierarquia que ela tem no produto: monograma,
   e-mail em corpo de leitura, estado em linha própria. */
.conta-cartao { display: flex; align-items: center; gap: 14px; padding: 4px 0 2px; }
.conta-marca {
  flex: none; width: 48px; height: 48px; border-radius: 50%;
  background: var(--primary-container); color: var(--on-primary-container);
  display: grid; place-items: center;
  font-size: var(--t-title-m); font-weight: 500; line-height: 1;
  /* A inicial é decorativa: quem lê por leitor de tela ouve o e-mail logo
     ao lado, e repetir a letra só atrapalharia. */
}
.conta-quem { flex: 1; min-width: 0; }
.conta-email {
  font-size: var(--t-body-l); color: var(--on-surface);
  /* E-mail é uma palavra só e longa: sem isto ele estoura a caixa no
     celular em vez de quebrar. */
  overflow-wrap: anywhere; line-height: 1.35;
}
.conta-estado {
  display: flex; align-items: center; gap: 6px;
  font-size: var(--t-body-s); color: var(--on-surface-variant); margin-top: 3px;
}
.conta-estado .ponto { width: 8px; height: 8px; border-radius: 50%; flex: none; }
.conta-estado.ok .ponto { background: var(--success); }
.conta-estado.off .ponto { background: var(--on-surface-variant); }
.conta-estado.ruim { color: var(--error); }
.conta-estado.ruim .ponto { background: var(--error); }

/* Ações da conta: uma primária de largura cheia, as secundárias abaixo.
   `flex: 1 1 auto` fazia três botões virarem uma grade 2+1 irregular no
   celular — o que mais denunciava improviso na tela. */
.conta-acoes { display: grid; gap: 8px; margin-top: 14px; }
.conta-acoes .btn { width: 100%; }
.conta-acoes .par { display: flex; gap: 8px; }
.conta-acoes .par .btn { flex: 1 1 0; min-width: 0; }
/* Sair é destrutivo e precisa **parecer** destrutivo. Texto, não
   preenchimento: o peso vem da cor, não da área. */
.btn.perigo-texto { color: var(--error); }

/* Formulário de conta dentro da folha do `⋮` (entrar / criar conta).
   `.campo` acima está escopado em `.ajustes-grupo`, então sem esta regra os
   inputs da folha sairiam sem borda e sem altura de alvo — o mesmo desenho
   precisa valer nos dois lugares onde se digita e-mail e senha. */
.folha-form { display: flex; flex-direction: column; gap: 10px; padding: 4px 4px 8px; }
.folha-form .campo {
  width: 100%; min-height: var(--alvo); padding: 12px 16px; border-radius: var(--shape-xs);
  font-size: var(--t-body-m);
  border: 1px solid var(--outline); background: var(--surface-1); color: var(--on-surface);
}
.folha-form .ajuda { margin: 0; }
/* Estado da conexão: cor + palavra, para não depender só de cor. */
.estado { display: flex; gap: 8px; align-items: flex-start; font-size: var(--t-body-m); line-height: 1.45; margin-top: 12px; }
.estado .ponto { width: 8px; height: 8px; border-radius: 50%; flex: none; margin-top: 7px; background: var(--on-surface-variant); }
.estado.ok .ponto { background: var(--success); }
.estado.ruim .ponto { background: var(--error); }
.estado.ruim { color: var(--error); }

/* Faixa de "isto é uma cópia" (§8.6). Discreta de propósito: o caso comum é
   durar os segundos do cold start e sumir sozinha quando o dado fresco chega.
   Um banner forte para algo que se resolve em dois segundos treinaria o
   usuário a ignorar avisos — e este precisa ser lido quando durar. */
.aviso-copia {
  display: flex; align-items: center; gap: 8px;
  padding: 8px 14px; margin: 0 0 6px;
  font-size: var(--t-body-s); color: var(--on-surface-variant);
  background: var(--surface-2); border-radius: 10px;
}
.aviso-copia .ponto {
  width: 8px; height: 8px; border-radius: 50%; flex: none;
  background: var(--on-surface-variant);
  animation: pulso-copia 1.4s ease-in-out infinite;
}
/* Sem animação para quem pediu para não ter: o ponto fica parado e a faixa
   continua dizendo a mesma coisa. */
@media (prefers-reduced-motion: reduce) {
  .aviso-copia .ponto { animation: none; }
}
@keyframes pulso-copia {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.35; }
}

/* ============================================================ regra dura
   A página nunca rola na horizontal. Faixas que rolam para o lado (filtros) são
   contidas por `overflow-x: clip` aqui. */
html, body, #app, #tela { max-width: 100%; overflow-x: clip; }
img, video, canvas { max-width: 100%; }
/* URL de obra/fonte é o texto longo típico: quebra em vez de esticar a página */
.fonte-info small, .ajustes-texto small, .sub, .sinopse, .estado { overflow-wrap: anywhere; }

/* Foco visível para teclado físico (Chromebook, tablet com teclado, TalkBack com
   navegação por foco). O anel azul do Chrome não pertence a esta paleta. */
:focus-visible { outline: 2px solid var(--primary); outline-offset: 2px; }

/* Celular dobrável aberto e tablet Android: a grade cresce, o resto não muda.
   Nada de barra flutuante centralizada — isso era regra de iPad. */
@media (min-width: 700px) {
  :root { --lado: 24px; }
  .grade { grid-template-columns: repeat(auto-fill, minmax(132px, 1fr)); }
  .det-topo .capa { width: 180px; }
}

/* ------------------------------------------------ entrada (só no alvo web)
   Tela cheia, centrada, com medida de leitura curta: é a primeira coisa que o
   visitante vê no navegador, e não há nada atrás dela para competir. No
   Android esta tela não existe — lá entrar é opcional e mora na folha do ⋮. */
.entrar {
  max-width: 380px; margin: 0 auto; padding: 40px var(--lado) 24px;
  display: flex; flex-direction: column; gap: 12px;
}
.entrar h2 { font-size: var(--t-headline); font-weight: 500; }
.entrar .ajuda {
  color: var(--on-surface-variant); font-size: var(--t-body-m);
  line-height: 1.5; margin-bottom: 4px;
}
.entrar .campo {
  width: 100%; min-height: var(--alvo); padding: 12px 16px;
  border-radius: var(--shape-xs); font-size: var(--t-body-m);
  border: 1px solid var(--outline); background: var(--surface-1);
  color: var(--on-surface);
}
.entrar-erro {
  color: var(--error); font-size: var(--t-body-m); line-height: 1.45;
  margin-top: 4px;
}
.entrar-acoes { display: flex; flex-direction: column; gap: 8px; margin-top: 8px; }
.entrar-acoes .btn { width: 100%; }

/* ══════════════════════════════════════════════ desktop (só no alvo web, §8.7)
   Tudo aqui é `html.alvo-web` **e** largura: o carimbo do alvo vem de
   `servicos/offline.js` e existe porque a largura sozinha decide errado. Um
   dobrável aberto e um tablet Android passam de 1000px com folga, e lá a barra
   inferior é a resposta certa — é a convenção do sistema e o polegar alcança a
   base da tela. No navegador de um PC, ela é um padrão de celular exibido a um
   mouse: cada destino fica a uma travessia de tela inteira do conteúdo.

   O `DESIGN.md` continua sendo o do Android e não muda. Isto é o irmão web que
   a §8.7 pede: mesma paleta, mesma tipografia, mesmos alvos — outra
   composição. */

@media (min-width: 1000px) {
  html.alvo-web {
    --lado: 32px;
    /* O trilho cede largura ao conteúdo quando a tela aperta: em 1024px, 232px
       fixos deixavam a grade com três colunas espremidas contra a régua. Cresce
       até 232 e para — navegação lateral mais larga que isso vira desperdício,
       porque os rótulos têm uma palavra. */
    --trilho: clamp(180px, 17vw, 232px);
    --leitura: 1360px;           /* teto do conteúdo, ver `#tela` abaixo */
  }

  /* O app vira duas colunas: trilho fixo e conteúdo. `grid` e não `flex`
     porque a coluna do trilho tem largura fixa e a do conteúdo é o resto —
     que é literalmente o que `auto 1fr` diz. */
  html.alvo-web body { display: block; }
  /* **As linhas são declaradas, não inferidas.** Com `grid-template-rows`
     implícito, o `grid-row: 1 / -1` do trilho criava duas faixas de altura
     igual: o cabeçalho esticava para 100dvh e o conteúdo era empurrado para
     fora da tela — desenhado, presente no DOM, invisível. `auto 1fr` diz o que
     se quer: o cabeçalho mede o que precisa, o conteúdo fica com o resto. */
  html.alvo-web #app {
    display: grid;
    grid-template-columns: var(--trilho) 1fr;
    grid-template-rows: auto 1fr;
    min-height: 100dvh;
  }

  /* ---------------------------------------------------------- trilho lateral
     A MESMA `<nav id="abas">`, reposicionada. Não é componente novo: o
     `aria-current`, o `montarAbas()` e a pílula do ativo continuam valendo,
     e por isso o teclado e o leitor de tela não perdem nada na troca. */
  html.alvo-web #abas {
    position: sticky; top: 0; left: auto; right: auto; bottom: auto;
    grid-row: 1 / -1; grid-column: 1;
    align-self: start; height: 100dvh;
    display: flex; flex-direction: column; gap: 4px;
    padding: 20px 12px;
    border-top: none;
    border-right: 1px solid var(--outline-variant);
  }
  /* A marca ocupa o topo do trilho — no celular ela vive na barra do topo, que
     aqui passa a ser só o título da tela. */
  /* **É aqui que a marca vive na web** — e é o único lugar dela na interface.
     No celular o topo diz onde você está ("Biblioteca", "Ajustes"), nunca como
     o app se chama; no desktop o trilho tem uma cabeceira própria, que é
     exatamente o espaço que um nome ocupa sem tomar o do conteúdo.

     Peso alto e letra apertada: é o tratamento que uma palavra curta aguenta.
     O sublinhado curto em ciano é a única marca gráfica do app — o mesmo
     acento da aba ativa e do botão Continuar, dizendo que são do mesmo
     sistema, e desenhado com a borda em vez de um caractere para não virar
     ruído em leitor de tela.

     O nome está no CSS porque este pseudo-elemento não existe no DOM. Trocar a
     marca mexe aqui **e** em `app/marca.js`; o teste cobra os dois juntos. */
  html.alvo-web #abas::before {
    content: "Mekuri";
    margin: 4px 12px 20px;
    padding-bottom: 10px;
    font-size: var(--t-display); font-weight: 800; letter-spacing: -.03em;
    color: var(--on-surface);
    border-bottom: 3px solid var(--primary);
    align-self: start;
  }
  /* Destino em linha: ícone e rótulo lado a lado, como em toda navegação
     lateral. A pílula deixa de ser um oval atrás do ícone e vira a linha
     inteira — que é a forma que o alvo tem aqui. */
  html.alvo-web .aba {
    flex-direction: row; justify-content: flex-start; gap: 12px;
    min-height: var(--alvo); padding: 0 12px;
    border-radius: var(--shape-full);
    font-size: var(--t-body-m);
    transition: background var(--dur) var(--ease);
  }
  html.alvo-web .aba .ind { width: 24px; height: 24px; background: none; }
  html.alvo-web .aba.on { background: var(--primary-container); color: var(--on-primary-container); }
  html.alvo-web .aba.on .ind { background: none; color: inherit; }
  /* Mouse existe aqui: o realce de passagem é informação, não enfeite. No
     celular não há hover e a regra nem é escrita. */
  html.alvo-web .aba:not(.on):hover { background: var(--surface-2); }

  /* ------------------------------------------------------------- cabeçalho
     Fica na coluna do conteúdo, não atravessa o trilho. */
  html.alvo-web #cabecalho { grid-column: 2; grid-row: 1; }
  html.alvo-web #topo { padding-left: var(--lado); padding-right: 8px; }

  /* --------------------------------------------------------------- conteúdo
     Teto de largura e centrado: sem ele a grade estica até 2560px e a capa
     vira miniatura numa fileira de doze. O `--leitura` é o que mantém o cartão
     no tamanho em que a arte da capa ainda se lê. */
  html.alvo-web #tela {
    grid-column: 2; grid-row: 2;
    width: 100%; max-width: var(--leitura); margin: 0 auto;
    padding: 20px var(--lado) 64px;
  }
  /* ------------------------------------------------- cartão da grade no PC
     **O texto sai de cima da arte.** No celular ele fica lá porque duas linhas
     por item custam a sexta obra da tela — com o polegar, densidade é o que
     decide. Com mouse não há esse aperto, e sobrepor cobre justamente o terço
     de baixo da capa, onde a arte costuma fechar a composição.

     Estudo do concorrente (2026-08-03, `yomu.com.br` com sessão): o cartão de
     lá é capa limpa + título + uma linha "112 capítulos · Cap. 111". A leitura
     ganha duas coisas de uma vez — a arte inteira e um dado que aqui exigia
     abrir a obra. É a única coisa do desenho deles que valia copiar: os três
     selos por cartão (`Manhwa`/`Yomu`/`HOT`) são ruído de agregador, e uma
     biblioteca pessoal não tem o que anunciar.

     O respiro também subiu (24px, como o deles): com a capa inteira visível,
     14px de goteira faziam as artes se tocarem e a grade virar uma colcha. */
  /* ------------------------------------------- por que a coluna é fixa aqui
     **Cada seção da tela inicial é uma `.grade` própria**, e com
     `minmax(186px, 1fr)` cada uma resolvia a própria largura de coluna: uma
     seção de 3 obras esticava as trilhas para encher os 1144px, a de baixo com
     5 obras usava trilhas menores, e as capas **não alinhavam entre seções**.
     Visto a 1440px em 2026-08-03: era o que fazia a tela parecer desmontada, e
     é a diferença estrutural real para o concorrente — lá a grade é uma só,
     contínua, e por isso o ritmo não quebra.

     Coluna de largura fixa dá a todas as seções o mesmo passo. As trilhas que
     sobram à direita ficam vazias em vez de deformar o cartão, que é
     exatamente o que se quer: o buraco à direita de uma seção curta lê como
     "acabou a seção", enquanto capa esticada lê como defeito.

     `auto-fill` continua (nunca `auto-fit`): com `auto-fit` as trilhas vazias
     colapsam e um único cartão — o caso real de "Continuar lendo" — se estica
     pela largura inteira, virando um pôster onde os vizinhos são miniaturas. */
  html.alvo-web .grade {
    grid-template-columns: repeat(auto-fill, 186px);
    gap: 28px 24px;
    justify-content: start;
  }
  /* **O cabeçalho da seção precisa de mais ar aqui do que no celular.** Lá o
     cartão termina na borda da capa, e 24px separam bem. Aqui ele termina em
     até duas linhas de título mais a linha de meta — texto que encosta no
     texto do cabeçalho seguinte, e "Biblioteca" nascia colada na obra de
     cima, como se fosse legenda dela em vez de título da seção.

     **O espaço vai no embrulho da seção, não no cabeçalho.** `secaoObras()`
     monta cada seção dentro de um `<div>` próprio, então *todo* `.secao-cab`
     é `:first-child` do seu embrulho — a regra `:first-child { margin-top }`
     que zera o espaço da primeira seção casava com **todas**, e o respiro
     medido entre seções era de 4px onde a folha pedia 40. Medido no navegador
     a 1440px; a captura não denunciava porque 4px de texto colado ainda parece
     "apertado", não "quebrado".

     `:has()` no lugar de um seletor de filho: quem precisa de margem é a caixa
     que contém a seção, e ela não tem classe própria. */
  html.alvo-web #tela > div:has(> .secao-cab) { margin-top: 40px; }
  html.alvo-web #tela > div:has(> .secao-cab):first-of-type { margin-top: 0; }

  html.alvo-web .cartao-info {
    position: static; padding: 10px 2px 0; background: none;
  }
  /* **Duas linhas reservadas, sempre.** O `line-clamp: 2` do celular corta o
     título comprido mas não reserva altura para o curto: numa fileira com
     "One Punch Man" ao lado de "To Be a Power in the Shadows!", a linha de
     meta de um subia uma linha inteira em relação à do vizinho, e a grade
     ganhava um serrilhado que nenhum espaçamento consertava.

     A altura fica no `h4` e vale para o texto, **não** para o cartão: o
     `line-height` é 1.3 e o `--t-body-m` é .875rem, então duas linhas são
     `2 * 1.3 * .875rem`. Escrito em `rem` e não em `em` porque `em` aqui
     herdaria o tamanho do próprio `h4` — que é o que se está declarando na
     linha de cima, e a ordem de resolução torna isso frágil de ler. */
  html.alvo-web .cartao-info h4 {
    color: var(--on-surface); text-shadow: none;
    font-size: var(--t-body-m); font-weight: 600;
    height: calc(2 * 1.3 * .875rem);
  }
  html.alvo-web .cartao-meta {
    display: block; margin-top: 3px;
    color: var(--on-surface-variant); font-size: var(--t-body-s);
    font-variant-numeric: tabular-nums;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  }
  /* O cartão **mantém** `overflow: hidden`: é ele que recorta a ondulação de
     toque (`.ondulacao` nasce no ponto do clique e cresce). Trocar para
     `visible` deixaria a onda vazar por cima dos vizinhos — e a regra da
     camada de estado vale nos dois alvos. O texto abaixo da capa não precisa
     de recorte nenhum para caber, porque o cartão cresce com ele. */
  html.alvo-web .cartao .capa { border-radius: var(--shape-m); }
  html.alvo-web .det-topo .capa { width: 232px; }
  /* Medida de leitura: sinopse em linha de 2000px não se lê. */
  html.alvo-web .sinopse-txt { max-width: 72ch; }
  /* Mesma razão, para os parágrafos de apoio que a tela solta fora de um
     `.ajustes-grupo` (a varredura de repetidos é a primeira). Sem isto a frase
     atravessava 1130px numa linha só a 1440px — o olho perde a volta. */
  html.alvo-web #tela > .ajuda { max-width: 72ch; }

  /* **A ação primária para de atravessar a tela.** No celular "Continuar"
     ocupa a largura porque o polegar mira uma faixa; com mouse, um botão de
     1150px para um clique é a maior coisa da tela sendo a menos precisa —
     e o alvo real (o cursor) mede um pixel. Encolhe para o tamanho do texto,
     e as ações ficam agrupadas à esquerda, alinhadas com a capa acima. */
  html.alvo-web .acoes { justify-content: flex-start; }
  html.alvo-web .acoes .continuar { flex: 0 1 auto; min-width: 260px; max-width: 380px; }

  /* **O FAB some no desktop, e isto é conserto, não gosto.** O `DESIGN-WEB.md`
     §5 já listava "FAB como ação primária de tela larga" entre o que não fazer,
     e o comentário que estava aqui dizia a mesma coisa — mas a regra só o
     **reposicionava**. Visto no navegador em 2026-08-03, o resultado era um `+`
     flutuando no canto inferior, sobre a coluna do trilho: lido como parte da
     navegação, e concorrendo com o "Adicionar obra" do próprio estado vazio.

     Com mouse a ação primária não precisa de botão flutuante — ela mora no
     conteúdo, e a tela vazia já a oferece com todas as letras. */
  html.alvo-web .fab { display: none; }

  /* O lugar dela é o rodapé do trilho, empurrado para baixo pelo espaçador: o
     que está no alto são **destinos**, e adicionar não é um lugar para onde ir.
     Forma de botão preenchido (não de pílula de aba) para dizer isso pela
     silhueta, antes da leitura. */
  /* Vencem o `display: none` da barra do celular por especificidade — e o
     `[hidden]` global continua acima dos dois, então a tela que não oferece
     "adicionar" segue sem o botão. */
  html.alvo-web #abas > .trilho-espaco { display: block; flex: 1; }
  html.alvo-web #abas > .trilho-add {
    display: flex; align-items: center; gap: 12px;
    min-height: var(--alvo); padding: 0 16px;
    border-radius: var(--shape-full);
    background: var(--primary); color: var(--on-primary);
    font-size: var(--t-label-l); font-weight: 500;
    transition: filter var(--dur) var(--ease);
  }
  html.alvo-web .trilho-add:hover { filter: brightness(1.08); }
  html.alvo-web .trilho-add svg {
    width: 22px; height: 22px; flex: none;
    fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round;
  }

  /* Modal e folha nascem colados à base (o polegar está lá). Com mouse, a
     folha de baixo obriga a percorrer a tela inteira para escolher um item. */
  html.alvo-web #folha { align-items: center; }
  html.alvo-web #folha .folha-caixa {
    max-width: 460px; margin: 0 auto;
    border-radius: var(--shape-xl);
    padding-bottom: 12px;
  }
  /* A alça é o `::before` da caixa: ela promete "arraste para baixo", gesto
     que não existe com mouse. */
  html.alvo-web #folha .folha-caixa::before { display: none; }
}

/* ------------------------------------------------------- leitor no desktop
   A arte manda: o trilho sai de cena e a página ocupa a tela, como no celular.
   O que muda é a medida — uma tira de manhwa esticada a 2560px de largura fica
   ilegível — e o teclado, que aqui existe. */
@media (min-width: 1000px) {
  html.alvo-web body.lendo #app { grid-template-columns: 1fr; }
  html.alvo-web body.lendo #abas { display: none; }
  html.alvo-web body.lendo #cabecalho { grid-column: 1; grid-row: 1; }
  html.alvo-web body.lendo #tela { grid-column: 1; grid-row: 2; }
  html.alvo-web body.lendo #tela { max-width: none; padding: 0; }

  /* Coluna de leitura centrada, com o fundo escuro em volta servindo de
     moldura. 860px é onde a página de manhwa ainda tem detalhe sem obrigar o
     olho a varrer a tela na horizontal. */
  html.alvo-web .leitor-paginas img { max-width: 860px; margin: 0 auto; }
  html.alvo-web .leitor-fluxo { padding-bottom: 96px; }

  /* Legenda dos atalhos: aparece com o HUD e some com ele. Existe porque
     atalho que ninguém descobre não é recurso — e no celular esta linha nem é
     desenhada, porque lá não há teclado. */
  .leitor-teclas {
    position: fixed; left: 50%; bottom: 24px; transform: translateX(-50%);
    z-index: 30; display: none; gap: 18px;
    padding: 10px 18px; border-radius: var(--shape-full);
    background: var(--inverse-surface); color: var(--inverse-on-surface);
    font-size: var(--t-body-s);
    box-shadow: 0 4px 16px rgb(0 0 0 / .38);
    /* 600ms, não os 200 do sistema: aqui a saída **é** a mensagem ("já
       ensinei, sigo fora do caminho"). No tempo de um controle ela pisca. */
    transition: opacity 600ms var(--ease);
  }
  /* **Não segue o HUD.** Presa a ele, sumia em três segundos — cedo demais
     para ser lida. Ela tem o próprio relógio (`leitor.js`): fica seis
     segundos, some, e nas aberturas seguintes nem é desenhada. */
  html.alvo-web body.lendo .leitor-teclas { display: flex; }
  html.alvo-web body.lendo .leitor-teclas.indo { opacity: 0; pointer-events: none; }
  .leitor-teclas kbd {
    font: inherit; padding: 1px 6px; border-radius: 4px;
    background: rgb(0 0 0 / .12);
  }
}

/* Diálogo de capítulos repetidos (§ achado de 2026-08-03). A tela existe para
   o usuário **ver** que os dois endereços são o mesmo capítulo antes de
   juntar — então a URL, que normalmente é ruído, aqui é a informação. */
.dup .ajuda { margin-bottom: 10px; line-height: 1.5; }
.dup-amostra { display: grid; gap: 8px; margin-top: 4px; }
.dup-item {
  padding: 10px 12px; border-radius: var(--shape-s);
  background: var(--surface-2);
}
.dup-item b { font-size: var(--t-body-m); }
/* `break-all` e não `anywhere`: URLs de capítulo não têm espaço onde quebrar,
   e sem isto elas esticam o diálogo além da tela. */
.dup-item small {
  display: block; margin-top: 2px;
  font-size: var(--t-body-s); color: var(--on-surface-variant);
  word-break: break-all; line-height: 1.4;
}
.dup-resto { color: var(--on-surface-variant); font-size: var(--t-body-s); }
