/* Espectro 24 — frontend v1
   Dark mode padrão, estética de cinema. Fontes de SISTEMA (serif/mono/sans)
   por decisão de offline-first: nenhuma requisição de rede, funciona por
   file://, sem CORS, sem Google Fonts. Ver README (desvios de design). */

:root {
  /* superfícies (dark cinema) */
  --bg:          #0b0c10;
  --bg-elev:     #14161d;
  --bg-elev-2:   #1b1e27;
  --border:      #262a35;
  --border-soft: #1e222c;

  /* texto */
  --text:      #f2f0ea;
  --text-dim:  #b7b4ab;
  --text-mute: #7d7a72;

  /* gradiente espectral — marca (vermelho → âmbar → verde → azul) */
  --spectrum: linear-gradient(90deg,
      #e5484d 0%, #f5820b 30%, #f5c518 48%,
      #46a758 68%, #3b82f6 100%);

  /* [rodada cor] as cores dos 3 grupos NÃO moram aqui: são os tokens
     `--grupo-neg/med/pos` (e `-soft`) da paleta ativa, definidos em
     `:root[data-cor=...]` no fim deste arquivo e escolhidos por `js/cor.js`
     (`?cor=`). Uma fonte só: faixa da home, cartões, RECEPÇÃO, bullets e
     condições leem os mesmos tokens. */

  --warn:     #f5c518;

  /* tipografia */
  --serif: Georgia, "Iowan Old Style", "Times New Roman", "Noto Serif", serif;
  --mono:  ui-monospace, "SF Mono", "JetBrains Mono", Menlo, Consolas, monospace;
  --sans:  system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", sans-serif;

  --maxw: 720px;
  --radius: 12px;

  /* [v1.9.43] A faixa do hero. O quadro mantém a largura que sempre teve
     (a coluna de leitura: `--maxw` de 760px acima de 600px, menos 20px de
     padding de cada lado = 720px) e a altura 16:9 dela. São as duas
     grandezas que a faixa full-bleed NÃO muda — ver `.hero-faixa-caixa`. */
  --hero-faixa-item-w: 720px;
  --hero-faixa-h: 405px;   /* 720 × 9/16 */
}



* { box-sizing: border-box; }

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

body {
  margin: 0;
  /* [v1.9.43] `clip` e não `hidden`: `hidden` faria do body um contêiner de
     rolagem e quebraria `position: sticky` na página inteira. Existe só
     para aparar os ~15px que `100vw` conta a mais por causa da barra de
     rolagem vertical, na faixa full-bleed do hero (`.hero-faixa-caixa`). */
  overflow-x: clip;
  background: var(--bg);
  color: var(--text);
  font-family: var(--sans);
  font-size: 16px;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

a { color: inherit; }

/* --- utilidades de tipografia --- */
.mono {
  font-family: var(--mono);
  font-size: 0.72rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--text-mute);
}
.serif { font-family: var(--serif); }

/* --- barra espectral (marca) --- */
.spectrum-bar {
  height: 5px;
  width: 100%;
  background: var(--spectrum);
}
.spectrum-bar--segmented {
  display: flex;
  gap: 3px;
  height: 6px;
  background: none;
}
.spectrum-bar--segmented span {
  flex: 1;
  border-radius: 2px;
}
.spectrum-line {
  height: 2px;
  width: 100%;
  background: var(--spectrum);
  opacity: 0.85;
}

/* --- layout --- */
.wrap {
  max-width: var(--maxw);
  margin: 0 auto;
  padding: 0 20px;
}

.site-footer {
  border-top: 1px solid var(--border-soft);
  margin-top: 56px;
  padding: 28px 0 48px;
}
.site-footer .wrap { display: flex; flex-direction: column; gap: 6px; }
.site-footer a { color: var(--text-dim); text-underline-offset: 3px; }
/* [v1.9.29, Entrega 4] O aviso exigido pelos termos do TMDB. Proeminente
   quer dizer LEGÍVEL e presente em toda página — não um asterisco. Fica no
   mesmo tamanho do resto do rodapé (0.9rem, não 0.7), e a marca do próprio
   Espectro continua acima e mais forte, como os termos pedem quando há
   marca do TMDB envolvida. */
.site-footer__aviso {
  color: var(--text-dim);
  font-size: 0.9rem;
  max-width: 62ch;
}
.site-footer__creditos { font-size: 0.9rem; }

/* =======================================================
   CRÉDITOS (v1.9.29) — a seção "Sobre" que o site não tinha
   ======================================================= */
.creditos { padding-bottom: 8px; }
.creditos__titulo {
  font-family: var(--serif);
  font-size: clamp(1.7rem, 5vw, 2.3rem);
  line-height: 1.15;
  margin: 26px 0 14px;
}
.creditos__intro { color: var(--text-dim); font-size: 1.02rem; line-height: 1.6; }
.creditos__bloco {
  border-top: 1px solid var(--border-soft);
  margin-top: 30px;
  padding-top: 20px;
}
.creditos__bloco p { line-height: 1.62; color: var(--text-dim); }
.creditos__h2 {
  font-family: var(--mono);
  font-size: 0.72rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--text-mute);
  margin: 0 0 12px;
}
/* O aviso literal exigido pelos termos: destacado, não diluído no corpo. */
.creditos__aviso {
  border-left: 2px solid var(--warn);
  background: var(--bg-elev);
  border-radius: 0 8px 8px 0;
  padding: 14px 16px;
  margin: 0 0 18px;
  font-size: 1rem;
}
.creditos__aviso .creditos__aviso-pt {
  display: block;
  margin-top: 7px;
  color: var(--text-mute);
  font-size: 0.92rem;
}
.creditos__link { font-family: var(--mono); font-size: 0.8rem; }

/* =======================================================
   HOME
   ======================================================= */
/* [v1.9.17] Compactado — a home é agora hero + mosaico, e o mosaico é o
   protagonista. Cada margem aqui foi encolhida deliberadamente para que o
   mosaico caiba o mais perto possível de uma tela sem exigir scroll; nada
   foi removido de conteúdo, só de respiro (a única exceção é `.home-sub`,
   cuja regra fica no arquivo mas o parágrafo saiu do HTML — a tagline +
   o rótulo da busca já cobrem a mesma mensagem em menos altura). */
.home-hero { padding: 20px 0 4px; text-align: center; }
.tagline {
  font-family: var(--mono);
  font-size: 0.68rem;
  letter-spacing: 0.26em;
  text-transform: uppercase;
  color: var(--text-mute);
  margin: 0 0 10px;
}
.home-title {
  font-family: var(--serif);
  font-weight: 700;
  font-size: clamp(2.2rem, 7vw, 3.4rem);
  line-height: 0.98;
  margin: 0 0 14px;
  letter-spacing: -0.01em;
}
.home-title .num {
  font-style: italic;
  background: var(--spectrum);
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
  color: transparent;
  padding-right: 0.05em;
}
.home-sub {
  font-size: 1.05rem;
  color: var(--text-dim);
  max-width: 34rem;
  margin: 0 auto;
  line-height: 1.55;
}

/* busca estética */
.search {
  margin: 0 auto 6px;
  max-width: 26rem;
  position: relative;
}
.search input {
  width: 100%;
  background: var(--bg-elev);
  border: 1px solid var(--border);
  color: var(--text);
  font-family: var(--sans);
  font-size: 1rem;
  padding: 14px 16px 14px 44px;
  border-radius: 999px;
  outline: none;
  transition: border-color 0.18s, box-shadow 0.18s;
}
.search input::placeholder { color: var(--text-mute); }
.search input:focus {
  border-color: #3a4152;
  box-shadow: 0 0 0 3px #3b82f61f;
}
.search__icon {
  position: absolute;
  left: 16px; top: 50%;
  transform: translateY(-50%);
  color: var(--text-mute);
  pointer-events: none;
  display: flex;
}
.search__hint {
  font-family: var(--mono);
  font-size: 0.68rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--text-mute);
  text-align: center;
  margin-top: 12px;
  min-height: 1em;
  opacity: 0;
  transition: opacity 0.2s;
}
.search__hint.is-visible { opacity: 1; }

.home-microcopy {
  text-align: center;
  font-family: var(--mono);
  font-size: 0.66rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--text-mute);
  margin: 4px 0 4px;
}

.section-label {
  font-family: var(--mono);
  font-size: 0.7rem;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--text-mute);
  margin: 0 0 16px;
}

/* =======================================================
   PÔSTER (v1.9.29) — mosaico da home e abertura da ficha
   =======================================================
   A proporção é RESERVADA antes de carregar, em `aspect-ratio` inline
   escrito pelo JS a partir de `ficha.poster_largura`/`poster_altura` (ver
   `js/poster.js`). Sem isso, 35 pôsteres chegando em ordem aleatória
   empurram a grade enquanto a pessoa lê. As regras abaixo NUNCA definem
   `aspect-ratio` — se definissem, sobrescreveriam ou brigariam com o valor
   real do filme (que não é sempre 2:3: `aftersun` é 1632×2449, o curta
   experimental é 505×750). */
.poster {
  display: block;
  position: relative;
  overflow: hidden;
  background: var(--bg-elev);
}
.poster__img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* a imagem entra sem piscar quando vem do cache e com um fade curto
     quando vem da rede; nada de animação coreografada — a animação do
     produto é a da BARRA, e ela é o momento de abrir um filme. */
  animation: poster-in 0.24s ease both;
}
@keyframes poster-in { from { opacity: 0; } to { opacity: 1; } }
@media (prefers-reduced-motion: reduce) {
  .poster__img { animation: none; }
}

/* AUSÊNCIA DE PÔSTER — estado DESENHADO, nunca imagem quebrada.
   Nenhum dos 35 filmes publicados está neste caso (medido: 35/35 com
   pôster), mas a expansão trará filmes obscuros com menos cobertura, e o
   estado precisa existir ANTES do primeiro. A caixa mantém a silhueta do
   pôster e mostra a marca do produto, não um ícone de erro. Também é onde
   cai uma falha do CDN (404, rede) — ver o handler de `error` no JS. */
.poster.is-vazio {
  background:
    repeating-linear-gradient(135deg,
      #ffffff08 0 2px, #00000000 2px 9px),
    var(--bg-elev-2);
}
.poster__vazio {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 6px;
}
.poster__vazio-marca {
  font-family: var(--serif);
  font-weight: 700;
  font-size: 1.5rem;
  line-height: 1;
  color: #ffffff1f;
}
.poster__vazio-nota {
  font-family: var(--mono);
  font-size: 0.56rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--text-mute);
}

/* --- no mosaico: o pôster É a célula --- */
.poster--mosaico {
  position: absolute;
  inset: 0;
  border-radius: 0;      /* o arredondamento é da célula, que tem overflow */
}
.poster--mosaico .poster__vazio-nota { font-size: 0.52rem; }

/* --- na ficha: CONTIDO, o produto não vira catálogo visual --- */
.poster--ficha {
  width: 200px;
  max-width: 46vw;
  margin: 0 0 18px;
  border-radius: 8px;
  border: 1px solid var(--border);
  box-shadow: 0 14px 34px -18px #000000e6;
}
@media (max-width: 640px) {
  .poster--ficha { width: 140px; margin-bottom: 14px; }
}

/* =======================================================================
   [v1.9.42] A GALERIA DE STILLS DO RODAPÉ FOI REMOVIDA (existiu da
   v1.9.39 à v1.9.41: grade estática, depois faixa rolante). O CSS dela
   saiu junto — `.stills-galeria`, `.stills-galeria__faixa`,
   `.stills-galeria__trilha` e `.still` não existem mais em lugar nenhum.

   A faixa passou a ser o HERO, no topo da página, e o estilo dela vive
   junto do `.backdrop` logo abaixo, porque ela OCUPA a caixa do backdrop
   — mesma largura, mesma proporção, mesmo fade na base, mesmo recuo do
   par ano → título. Manter as regras separadas convidaria as duas a
   divergirem, que é o defeito que o §3[E] já registrou uma vez.
   ======================================================================= */

/* [rodada página do filme] A GALERIA virou grade de 4 stills em TODAS as
   larguras (`.fp-galeria`, no fim do arquivo); a `.galeria-mobile` de uma
   coluna, só abaixo de 1024px, saiu. O lightbox abaixo é o mesmo. */

/* =======================================================================
   [v1.9.43] TELA CHEIA (lightbox) — abre ao tocar um item da galeria.

   SEM indicadores, SEM pontos, SEM setas e SEM contador, igual à faixa —
   a proibição vale aqui também. O único controle visível é o BOTÃO DE
   FECHAR, que a entrega pede por extenso; navegar é gesto (swipe lateral)
   ou teclado (setas), e fechar é gesto (swipe para baixo), botão ou Esc.
   ======================================================================= */
.lightbox {
  position: fixed;
  inset: 0;
  z-index: 100;
  background: #000;
  display: flex;
  align-items: center;
  /* o trilho horizontal com encaixe: swipe lateral navega, e o encaixe
     garante que o gesto termine sempre num quadro inteiro, nunca no meio
     de dois */
  overflow-x: auto;
  overflow-y: hidden;
  scroll-snap-type: x mandatory;
  overscroll-behavior: contain;
  scrollbar-width: none;
  -ms-overflow-style: none;
  touch-action: pan-x;
}
.lightbox::-webkit-scrollbar { display: none; }
.lightbox__quadro {
  flex: 0 0 100%;
  height: 100%;
  scroll-snap-align: center;
  display: flex;
  align-items: center;
  justify-content: center;
}
.lightbox__quadro img {
  max-width: 100%;
  max-height: 100%;
  width: auto;
  height: auto;
  display: block;
}
.lightbox__fechar {
  position: fixed;
  top: max(12px, env(safe-area-inset-top));
  right: 12px;
  z-index: 1;
  width: 44px;          /* alvo de toque de 44px, o mínimo confortável */
  height: 44px;
  border-radius: 50%;
  border: 0;
  background: rgba(0, 0, 0, 0.55);
  color: #fff;
  font-size: 22px;
  line-height: 1;
  cursor: pointer;
}
.lightbox__fechar:focus-visible {
  outline: 2px solid #fff;
  outline-offset: 2px;
}
/* enquanto o modo tela cheia está aberto, a página atrás não rola */
body.lightbox-aberto { overflow: hidden; }

@media (prefers-reduced-motion: reduce) {
  .lightbox { scroll-behavior: auto; }
}

/* --- [v1.9.30] O BACKDROP: a abertura da página do filme ---------------
   Substitui o pôster vertical no topo (decisão do dono do projeto). Ocupa a
   largura inteira da coluna de leitura e é 16:9 — a leitura vira horizontal
   e a barra de proporção sobe na página em vez de ser empurrada por uma
   capa de 300px de altura.

   A PROPORÇÃO É RESERVADA como a do pôster, e aqui o requisito é mais duro,
   não menos: sendo largo, o backdrop reserva MAIS altura que o pôster
   contido, então a falta de reserva daria um salto maior que o de antes.
   O `aspect-ratio` inline vem do JS a partir de
   `ficha.backdrop_largura`/`backdrop_altura`; esta regra NUNCA o define.

   Sem borda e sem sombra projetada, ao contrário do pôster: o pôster é um
   objeto pousado na página e a borda o recorta do fundo; o backdrop é a
   abertura, e uma moldura o transformaria num print. O degradê na base
   costura a imagem no fundo da página — o par ano → título vem logo abaixo,
   e uma aresta dura ali cortaria os dois. */
.backdrop {
  display: block;
  position: relative;
  overflow: hidden;
  width: 100%;
  /* [v1.9.32] SEM `border-radius` e SEM margem inferior: o backdrop deixou
     de ser um bloco fechado com cantos. Ele termina dissolvido no fundo, e
     quem ocupa o espaço logo abaixo é o texto do herói, que SOBE por cima
     dele (ver `.film-hero__text`). Cantos arredondados desenhariam a borda
     que o fade existe para apagar. */
  margin: 0;
  background: var(--bg-elev);
}

/* [v1.9.42] A FAIXA quando ela É o backdrop. Herda `.backdrop` inteiro
   (largura, proporção reservada, fade da base, full-bleed no mobile) e
   acrescenta só o que a rolagem exige.

   `overflow-x: auto` é o que dá arrasto, swipe e trackpad de graça — o
   movimento automático é só `scrollLeft` avançando em rAF (ver
   `faixa.js`, e o porquê de não ser `transform` animado).

   O SCROLL VERTICAL DA PÁGINA NÃO PODE SER SEQUESTRADO, e aqui isso pesa
   mais que na galeria de rodapé: a faixa é a PRIMEIRA coisa da página, e
   é onde o polegar começa a rolar. Por isso NÃO existe `touch-action`
   nenhum — o padrão deixa o navegador escolher o eixo do gesto —, e
   `overscroll-behavior-x: contain` só impede que o fim da faixa vire
   gesto de "voltar". */
/* [v1.9.43] A CAIXA DA FAIXA É FULL-BLEED — a única coisa da página que
   sai do contêiner de leitura. Decisão do dono: a faixa atravessa a
   janela de borda a borda, e todo o resto (ficha, barra, condições,
   bullets, veredito, narrativa, pesquisa) continua exatamente na mesma
   largura e com as mesmas margens de sempre.

   A TÉCNICA: `width: 100vw` com margem negativa de meia janela. O
   `left: 50%; transform: translateX(-50%)` faria o mesmo, mas criaria um
   contexto de empilhamento que atrapalharia o `::after` do fade.

   `100vw` INCLUI a barra de rolagem vertical na maioria dos motores, o
   que produziria alguns pixels de rolagem horizontal na página inteira.
   `overflow-x: clip` no `body` (abaixo) apara isso. `clip` e não
   `hidden`: `hidden` transformaria o body num contêiner de rolagem e
   quebraria `position: sticky` em qualquer lugar da página.

   A ALTURA NÃO ACOMPANHA a largura — e é o ponto. Se a proporção 16:9
   governasse a caixa full-bleed, o hero teria 810px de altura a 1440 e
   1440px a 2560, engolindo a página. A altura fica a MESMA de antes
   (a do quadro de 720px, ou seja 405px), e o que a largura extra compra
   são mais quadros SIMULTÂNEOS: 2,0 a 1440, 2,7 a 1920 e 3,6 a 2560. */
.hero-faixa-caixa {
  overflow: hidden;
  width: 100vw;
  margin-left: calc(50% - 50vw);
  margin-right: calc(50% - 50vw);
  height: var(--hero-faixa-h);
  /* a proporção inline que o JS escreve vale para o hero ESTÁTICO; aqui a
     altura é fixa e a razão não pode disputar com ela */
  aspect-ratio: auto !important;
}
.hero-faixa {
  width: 100%;
  height: 100%;
  overflow-x: auto;
  overflow-y: hidden;
  overscroll-behavior-x: contain;
  /* barra de scroll escondida com o scroll INTACTO, nos três motores */
  scrollbar-width: none;
  -ms-overflow-style: none;
}
.hero-faixa::-webkit-scrollbar { display: none; }
.hero-faixa:focus-visible {
  outline: 2px solid var(--fg);
  outline-offset: -3px;   /* para dentro: a faixa encosta na borda da tela */
}
/* ZERO gap entre os quadros: o alvo é uma sequência contínua, não um
   slideshow com transição. Um respiro entre eles desenharia a fronteira
   que o movimento existe para dissolver — mesmo raciocínio do "ZERO gap"
   da barra de proporção (§3[E], v1.9.26). */
/* `width: 100%` (da FAIXA), NÃO `max-content`. A diferença não é de
   estilo: os quadros são `flex: 0 0 100%`, e uma porcentagem resolve
   contra o pai — com `max-content` o pai depende dos filhos que dependem
   do pai, e o navegador resolve essa circularidade multiplicando tudo.
   MEDIDO ao vivo antes da correção: cada quadro saiu com **17280px** de
   largura em vez de 720. Com a trilha amarrada à largura da faixa, `100%`
   é sempre a caixa do hero, e é ela que os quadros ocupam; quem rola é a
   faixa, pelo transbordo dos filhos. */
.hero-faixa__trilha {
  display: flex;
  gap: 0;
  width: 100%;
  height: 100%;
}
/* Cada quadro ocupa exatamente a caixa do hero — um por tela, com os
   vizinhos entrando pela borda. `100%` da faixa, não uma largura fixa:
   assim desktop, mobile e a virada do aparelho saem todos certos, e é a
   mesma largura que `faixa.js` mede para derivar a velocidade. */
/* Largura FIXA do quadro, não mais `100%` da faixa: com a faixa
   full-bleed, `100%` faria um quadro só ocupar a janela inteira — o
   oposto do efeito pedido. 720px é a largura que o quadro já tinha
   (a coluna de leitura), mantida por decisão explícita: o que muda é
   quantos cabem, não o tamanho de cada um. */
.hero-faixa .hero-still {
  flex: 0 0 var(--hero-faixa-item-w);
  display: block;
  position: relative;
  overflow: hidden;
  background: var(--bg-elev);
}
.hero-faixa .hero-still img { display: block; width: 100%; height: 100%; }

/* [v1.9.33] O FADE — e o PISO DE CONTRASTE, FECHADO POR CONSTRUÇÃO.
   ------------------------------------------------------------------
   O par ano → título começa SOBRE a imagem. Backdrops variam muito no
   brilho da faixa inferior: `the-invite-2026` é escuro ali e funcionaria
   por sorte; `barbie` e `the-hateful-eight` são os mais claros do
   catálogo. Um degradê que só "escurece um pouco" entrega contraste
   diferente por filme, e o requisito é um PISO garantido em QUALQUER
   imagem.

   A CONSTRUÇÃO: o degradê chega a 100% OPACO antes do fim da imagem,
   formando uma FAIXA CHAPADA na base. O recuo negativo do texto tem de
   ser MENOR que essa faixa — nunca igual, nunca maior —, senão uma fração
   do texto pousa fora da região chapada, sobre pixel de imagem de verdade,
   e o piso deixa de ser garantido.

   [v1.9.32 → v1.9.33] ATÉ AQUI A FAIXA CHAPADA (68px) E O RECUO (58px)
   ERAM DOIS NÚMEROS EM COMENTÁRIOS DIFERENTES, que só concordavam porque
   alguém fez a conta uma vez e não errou. Nada no CSS impedia editar um
   sem o outro — mudar a faixa para 40px sem tocar no recuo de 58px teria
   revertido a garantia em SILÊNCIO, e o sintoma só apareceria como título
   ilegível no próximo backdrop claro que entrasse no catálogo.

   A CORREÇÃO, no mesmo espírito de `--k` na barra (uma propriedade
   registrada, duas fronteiras como funções puras dela): **UMA variável é
   a fonte, a outra é DERIVADA por `calc()`, e a desigualdade fica dentro
   da própria expressão — não numa nota ao lado.**

     --fade-solid   a faixa chapada. ÚNICO número que se ajusta ao gosto.
     --fade-folga   folga fixa e sempre positiva (nunca um dial de
                    ajuste fino — é o que garante a desigualdade).
     --hero-overlap: calc(var(--fade-solid) - var(--fade-folga))

   `--fade-folga` positiva é o que torna `--hero-overlap < --fade-solid`
   uma PROPRIEDADE ARITMÉTICA da expressão, não uma coincidência entre
   dois valores escolhidos à mão: subtrair um número positivo de
   `--fade-solid` produz, por definição de subtração, algo menor que
   `--fade-solid`. Mudar só `--fade-solid` (para dar mais ou menos
   invasão do título) move o recuo JUNTO, na mesma direção, sem que a
   desigualdade possa quebrar — o único jeito de quebrá-la seria zerar ou
   negativar `--fade-folga`, e ela não é o dial que esta entrega mexe.

   A CONSEQUÊNCIA CONTINUA A MESMA: o contraste do título e do ano vira
   INDEPENDENTE DA IMAGEM, idêntico ao contraste dos mesmos textos sobre o
   fundo da página em qualquer outro lugar do site — porque o texto nunca
   pousa fora da faixa 100% opaca. Não é "medimos os 34 e deu bom"; é que
   a imagem não entra na conta, e agora isso vale mesmo se `--fade-solid`
   for retocado depois sem ninguém lembrar do recuo.

   `--fade-h` (altura total do fade) e `--fade-solid` juntos definem ONDE
   a opacidade chega a 100% (`--fade-opaco-em`, também `calc()`), e os
   estágios intermediários do degradê são FRAÇÕES dessa mesma distância —
   não paradas fixas independentes. Editar `--fade-h` ou `--fade-solid`
   redesenha a curva inteira consistente consigo mesma.

   A diferença para o degradê do mosaico permanece: lá a base é PRETA (a
   célula é um card sobre fundo escuro); aqui é `--bg`, porque este
   degradê tem de casar EXATAMENTE com o fundo da página. */
:root {
  --fade-h:     232px;   /* altura total do ::after */
  --fade-solid: 68px;    /* faixa 100% opaca — o ÚNICO número pra ajustar */
  --fade-folga: 10px;    /* fixa e positiva — garante recuo < faixa */
  --fade-opaco-em:  calc(var(--fade-h) - var(--fade-solid));
  --hero-overlap:   calc(var(--fade-solid) - var(--fade-folga));
}
.backdrop::after {
  content: "";
  position: absolute;
  inset: auto 0 0 0;
  height: var(--fade-h);
  background: linear-gradient(to bottom,
      #0b0c1000 0px,
      #0b0c1040 calc(var(--fade-opaco-em) * 0.415),
      #0b0c10a6 calc(var(--fade-opaco-em) * 0.756),
      #0b0c10e0 calc(var(--fade-opaco-em) * 0.927),
      #0b0c10 var(--fade-opaco-em),
      #0b0c10 var(--fade-h));
  pointer-events: none;
}
.backdrop.is-vazio::after { content: none; }

/* O RECUO do texto sobre a imagem vem de `--hero-overlap`, acima — ele NÃO
   é redefinido aqui. 58px no desktop (68 − 10) é o que faz o TÍTULO
   invadir a imagem, e não só o ano: o ano ocupa ~30px (linha + margem),
   então os ~28px restantes são a fatia do título que fica sobre a
   imagem — o título CRUZA a borda, começa dentro do fade e termina no
   fundo da página, que é o que o esboço pede.

   MARGEM DE TOPO DO PRIMEIRO FILHO ZERADA: o projeto não tem reset de
   `p`, então `.film-header__meta` carregava o `margin-top: 1em` do
   navegador (11,2px no corpo dele) e comia parte do recuo — o recuo
   pedido e o recuo real divergiam sem sintoma visível. */
.film-hero { position: relative; }
.film-hero__text {
  position: relative;   /* pinta ACIMA do ::after do backdrop */
  z-index: 1;
  margin-top: calc(-1 * var(--hero-overlap));
}
.film-hero__text > :first-child { margin-top: 0; }
/* FALLBACK sem backdrop: o pôster contido volta e a composição antiga
   volta com ele — texto INTEIRAMENTE abaixo da imagem. Um pôster 2:3 de
   200px não tem faixa inferior larga o bastante para um título de 3,6rem,
   e deixar o título subir cobriria o cartaz. Ver `header()` em filme.js. */
.film-hero--poster .film-hero__text { margin-top: 0; }

/* [v1.9.32] ANIMAÇÃO DE ENTRADA DE ANO E TÍTULO — a segunda animação de
   entrada da página, ao lado da sequência da barra (v1.9.28).
   ------------------------------------------------------------------
   A COREOGRAFIA, decidida e registrada: as duas rodam EM PARALELO, a
   partir de 0ms, e a do título é MUITO mais curta. A do título termina em
   ~500ms; a da barra segue até 1020ms. O título NÃO atrasa a barra em um
   único milissegundo.

   POR QUE NÃO ENCADEADAS. Encadear (título, depois barra) somaria ~500ms
   de tempo morto antes de o conteúdo começar a se mover, e a barra É o
   conteúdo — o título é a moldura. A página abriria com meio segundo em
   que nada do que ela veio dizer está acontecendo.

   POR QUE O TÍTULO AINDA ASSIM "VEM PRIMEIRO". Ele vem primeiro na ordem
   em que TERMINA, não na ordem em que começa: a moldura se assenta e fica
   parada enquanto o conteúdo ainda está resolvendo. É a hierarquia certa
   — o olho pousa no que parou de se mexer.

   O ESCALONAMENTO ano → título (70ms) é a única sequência interna, e ela
   é de leitura: o ano é o metadado, o título é o assunto.

   DISCIPLINA IGUAL À DA BARRA: `transform` + `opacity` apenas (as duas
   propriedades que compõem sem relayout), 10px de deslocamento,
   desaceleração pura. Sem bounce, sem spring, sem overshoot. */
@media (prefers-reduced-motion: no-preference) {
  /* O ESTADO INICIAL VIVE AQUI DENTRO — é a construção da v1.9.27/v1.9.28,
     e é o que faz `reduce` entregar o estado final sem depender de nenhuma
     regra de desligamento: o estado BASE do CSS já é o final. Se estes
     `opacity: 0` morassem fora do bloco, `reduce` deixaria o título
     invisível para sempre. */
  .film-hero__text .film-header__meta,
  .film-hero__text .film-header__title {
    opacity: 0;
    animation: hero-in 430ms cubic-bezier(0.22, 0.68, 0.28, 1) both;
  }
  .film-hero__text .film-header__meta  { animation-delay: 0ms; }
  .film-hero__text .film-header__title { animation-delay: 70ms; }
}
@keyframes hero-in {
  from { opacity: 0; transform: translateY(10px); }
  to   { opacity: 1; transform: none; }
}

@media (max-width: 640px) {
  .backdrop {
    /* De borda a borda no mobile: com 20px de padding de cada lado a imagem
       ficaria com 335px de largura numa tela de 375px, e um quadro 16:9
       nesse tamanho lê como miniatura. O recuo negativo devolve os 20px. */
    width: calc(100% + 40px);
    margin-left: -20px;
    margin-right: -20px;
  }
  /* [v1.9.33] SÓ AS TRÊS VARIÁVEIS-FONTE MUDAM. `--fade-opaco-em` e
     `--hero-overlap` NÃO são redeclaradas aqui — são `calc()` que já
     dependem destas três, então recalculam sozinhas quando o navegador
     resolve `.backdrop::after`/`.film-hero__text` neste breakpoint. O
     `background`/`height` de `.backdrop::after` também não são
     redeclarados: já usam `var(--fade-h)`/`var(--fade-opaco-em)`, e por
     isso seguem o valor mobile sem repetir o gradiente inteiro.

     O backdrop mede ~211px de altura a 375px — um fade de 232px (o do
     desktop) cobriria a imagem quase inteira. Encolhe proporcionalmente,
     com a MESMA folga positiva do desktop reduzida de 10px para 6px
     (a caixa é menor, e 10px nela é uma fração maior da faixa chapada). */
  :root {
    --fade-h:     156px;
    --fade-solid: 58px;   /* → recuo 52px: título entra ~12px na imagem */
    --fade-folga: 6px;
  }
}

/* O estado de ausência do backdrop reusa o desenho do pôster (mesma hachura,
   mesma marca), na caixa 16:9 em vez da 2:3. É o mesmo caminho para "não
   veio" e "quebrou no CDN" — nunca ícone de imagem quebrada. */
.backdrop.is-vazio {
  background:
    repeating-linear-gradient(135deg,
      #ffffff08 0 2px, #00000000 2px 9px),
    var(--bg-elev-2);
}

/* =======================================================
   MOSAICO (Entrega 1-4 v1.9.17; refeito na v1.9.18)
   ======================================================= */
/* Mais largo que a coluna de leitura (`--maxw`, 720/760px) de propósito —
   o mosaico é a peça visual da home, e confiná-lo à largura de um parágrafo
   desperdiçaria a tela em telas largas. `filme.html` não usa esta classe. */
.mosaic-wrap {
  max-width: 1080px;
  margin: 0 auto;
  padding: 4px 14px 24px;
}
.mosaic {
  display: grid;
  /* auto-fill (não auto-fit): a contagem de colunas nasce da LARGURA
     disponível dividida pelo tamanho mínimo da célula — nunca de um número
     de filmes fixo. Um catálogo de 20 vira menos LINHAS, um de 60 vira
     mais linhas; a contagem de colunas por breakpoint não muda com N
     (Entrega 4). */
  grid-template-columns: repeat(auto-fill, minmax(132px, 1fr));
  gap: 8px;
  /* dá espaço para a célula em hover crescer (scale) sem cortar nas bordas
     do grid — bem menor que antes porque o scale agora é sutil (1.06). */
  padding: 4px;
}
/* [v1.9.18] A célula era um bloco de cor sólida (a distribuição inteira
   preenchendo o quadrado) — com ~30 dos 35 filmes majoritariamente
   positivos, a home virava dezenas de retângulos azuis quase idênticos, e
   o título só aparecia num hover que escurecia a TELA TODA (inclusive o
   alvo do próprio hover). Virou um card escuro comum, com o TEXTO como
   protagonista — a distribuição vira uma faixa fina de 5px colada na base,
   não a célula inteira. Mais alta que quadrada (4/5, não 1/1): títulos de
   até ~37 caracteres ("Homem-Aranha: Através do Aranhaverso") precisam de
   2-3 linhas, e um quadrado apertava demais. */
.mosaic-cell {
  position: relative;
  /* [v1.9.29] 4/5 → 2/3: a célula assume a proporção do PÔSTER. A razão de
     ser 4/5 na v1.9.18 era acomodar títulos de até ~37 caracteres em 2-3
     linhas num card sem imagem; com o pôster ocupando a célula, o texto
     mora num degradê sobre ele e a caixa passa a ser a da arte. A grade
     fica ~20% mais alta (5 linhas de 213px contra 5 de 165px no desktop) —
     é o custo de densidade que a decisão de mostrar pôster cobra, e está
     medido no relatório da versão. */
  aspect-ratio: 2 / 3;
  display: block;
  background: var(--bg-elev);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  overflow: hidden;
  text-decoration: none;
  color: inherit;
  cursor: pointer;
  transform: scale(1);
  transition: transform 0.18s ease, background-color 0.18s ease,
              border-color 0.18s ease, box-shadow 0.18s ease;
}
/* `.mosaic-cell { display: block }` acima tem a MESMA especificidade do
   `[hidden] { display: none }` do UA stylesheet — sem esta regra, a busca
   (`home.js`, `el.hidden = true`) esconde o elemento do fluxo de
   acessibilidade mas ele continua VISÍVEL na tela, porque a regra da
   classe (que vem depois no cascade) vence o empate. Achado ao testar a
   busca com a primeira versão do mosaico. */
.mosaic-cell[hidden] { display: none; }

/* HOVER, v1.9.18: nada escurece — nem os vizinhos, nem a própria célula.
   Scale sutil (a v1.9.17 usava 1.32, agressivo o bastante pra cobrir
   vizinhos inteiros; aqui é 1.06, só um destaque), fundo um tom mais claro,
   borda mais definida, sombra. `z-index` sobe só o suficiente pra sombra
   não ficar atrás do vizinho. */
.mosaic-cell:hover,
.mosaic-cell:focus-visible {
  transform: scale(1.06);
  background: var(--bg-elev-2);
  border-color: #3a4152;
  box-shadow: 0 10px 26px -10px #000000b3;
  z-index: 2;
  outline: none;
}
.mosaic-cell:focus-visible {
  box-shadow: 0 0 0 3px #3b82f680, 0 10px 26px -10px #000000b3;
}

.mosaic-cell__body {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  /* padding-bottom maior que o topo: dá respiro pra faixa (abaixo) sem o
     texto colar nela. */
  padding: 10px 10px 17px;
  /* [v1.9.29] O DEGRADÊ, e por que ele é opaco embaixo. O título tem de ser
     legível sobre QUALQUER pôster — inclusive os claros (`barbie`,
     `wonka`). Um véu uniforme apagaria a arte inteira; este só existe onde
     o texto está, e chega a 96% de preto na base, o que garante o contraste
     do título sem tocar os dois terços de cima da imagem. Sobre a célula
     VAZIA (sem pôster) ele é praticamente invisível — o fundo já é escuro —
     e o desenho continua o mesmo card da v1.9.18. */
  background: linear-gradient(to top,
      #000000fa 0%, #000000f0 24%, #000000ba 44%, #00000059 62%,
      #00000000 84%);
}
.mosaic-cell__title {
  font-family: var(--serif);
  font-weight: 700;
  font-size: 0.92rem;      /* nunca abaixo de 13px (Entrega 4) — ver @media */
  line-height: 1.22;
  color: var(--text-dim);
  margin: 0;
  /* título sempre visível (Entrega 1) — a instrução de revelar só no hover
     da v1.9.17 fica revogada; só a COR muda no hover (clareia). */
  transition: color 0.18s ease;
}
.mosaic-cell:hover .mosaic-cell__title,
.mosaic-cell:focus-visible .mosaic-cell__title { color: var(--text); }
.mosaic-cell__year {
  font-family: var(--mono);
  font-size: 0.66rem;
  letter-spacing: 0.1em;
  color: var(--text-mute);
  margin-top: 4px;
}

/* a faixa: 3 segmentos horizontais, largura = proporção real do bucket
   (negativas → mornos → positivas, mesma ordem de leitura do resto do
   site). Sangra até as bordas do card — fica com o arredondamento inferior
   porque `.mosaic-cell` já tem `overflow: hidden` + `border-radius`. Cores
   dos tokens `--grupo-neg/med/pos` (ver o fim do arquivo) —
   nunca as do resto do site. */
.mosaic-cell__strip {
  position: absolute;
  left: 0; right: 0; bottom: 0;
  /* [v1.9.29] 5px → 6px, e um fio escuro em cima. Contra fundo chapado
     (v1.9.18) 5px bastavam; sobre um pôster a faixa precisa se destacar da
     arte sem depender da cor dela, e o fio faz isso para pôster claro e
     escuro com a mesma regra. A faixa é o único sinal de RECEPÇÃO da
     célula — é o que impede a home de virar um catálogo de capas. */
  height: 6px;
  box-shadow: 0 -1px 0 #00000073;
  display: flex;
}
.mosaic-cell__seg { height: 100%; }

/* Touch não tem hover — mas aqui isso importa MUITO menos que na v1.9.17,
   porque o título já é sempre visível por padrão (Entrega 1). Só o
   feedback de toque muda. */
@media (hover: none) {
  .mosaic-cell:active { transform: scale(0.97); background: var(--bg-elev-2); }
}

.mosaic-hint {
  text-align: center;
  font-family: var(--mono);
  font-size: 0.64rem;
  letter-spacing: 0.04em;
  color: var(--text-mute);
  margin: 14px 10px 0;
  line-height: 1.6;
}
/* [v1.9.26] Os três viraram RÓTULO dentro de um glossário (ver o
   comentário em index.html). Ganham o peso e o tracking que rótulo tem no
   resto do site, para se lerem como NOME e não como palavra colorida no
   meio da frase — a glosa entre parênteses ao lado fica na cor da frase.
   As cores de grupo são as mesmas de antes (paleta paralela da home). */
.mosaic-hint__neg,
.mosaic-hint__med,
.mosaic-hint__pos {
  font-weight: 600;
  letter-spacing: 0.1em;
}
.mosaic-hint__neg { color: var(--grupo-neg); }
.mosaic-hint__med { color: var(--grupo-med); }
.mosaic-hint__pos { color: var(--grupo-pos); }

@media (max-width: 900px) {
  .mosaic { grid-template-columns: repeat(auto-fill, minmax(118px, 1fr)); }
  .mosaic-cell__title { font-size: 0.85rem; }
}
@media (max-width: 640px) {
  .mosaic { grid-template-columns: repeat(3, 1fr); gap: 6px; }
  .mosaic-cell__body { padding: 8px 8px 13px; }
  .mosaic-cell__title { font-size: 0.82rem; }  /* 13.1px — nunca abaixo de 13px */
  .mosaic-cell:hover { transform: scale(1); z-index: 0; }
}

/* =======================================================
   TELA DO FILME
   ======================================================= */
.topbar { padding: 20px 0 4px; }
.back-link {
  font-family: var(--mono);
  font-size: 0.7rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--text-mute);
  text-decoration: none;
  display: inline-flex;
  align-items: center;
  gap: 8px;
}
.back-link:hover { color: var(--text-dim); }

/* [v1.9.32] O topo perdeu o padding superior: o backdrop encosta na barra
   de navegação e a página abre pela imagem. */
.film-header { padding: 0 0 6px; }
.film-header__meta {
  font-family: var(--mono);
  font-size: 0.7rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--text-mute);
  margin-bottom: 12px;
}
.film-header__title {
  font-family: var(--serif);
  font-weight: 700;
  font-size: clamp(2.4rem, 9vw, 3.6rem);
  line-height: 1.02;
  letter-spacing: -0.01em;
  margin: 0 0 14px;
}

/* [v1.9.32] `.chip` NÃO É MAIS USADA na página do filme — o link do
   Letterboxd deixou de ser pill (ver `.reviews-link`). A classe fica
   porque `creditos.html` continua usando; se ela ficar sem nenhum
   consumidor, sai. */
.chip {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  background: var(--bg-elev);
  border: 1px solid var(--border);
  border-radius: 999px;
  padding: 7px 14px;
  font-family: var(--mono);
  font-size: 0.68rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--text-dim);
  text-decoration: none;
  transition: border-color 0.18s, color 0.18s;
}
.chip:hover { border-color: #3a4152; color: var(--text); }

/* [v1.9.32] O LINK DO LETTERBOXD, secundário. Sem caixa, sem borda, sem
   fundo — mono pequena, na mesma direção do disclosure APROFUNDAR da
   v1.9.26: parte do bloco editorial, não um componente pousado nele.

   A ÁREA DE TOQUE não é sacrificada junto com a caixa: o padding vertical
   continua existindo (invisível, mas clicável), e no mobile ele cresce
   para chegar aos 44px de alvo recomendado. Um link "discreto" que fica
   difícil de acertar com o polegar seria um downgrade de acessibilidade
   disfarçado de refinamento visual. */
.reviews-link {
  display: inline-block;
  margin-top: 12px;
  padding: 6px 0;
  font-family: var(--mono);
  font-size: 0.68rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--text-mute);
  text-decoration: none;
  border-bottom: 1px solid #ffffff1f;
  transition: color 0.18s, border-color 0.18s;
}
.reviews-link:hover { color: var(--text-dim); border-bottom-color: #ffffff3d; }
.reviews-link:focus-visible {
  outline: 2px solid var(--grupo-pos);
  outline-offset: 3px;
  border-radius: 2px;
  color: var(--text);
}
@media (max-width: 640px) {
  /* 44px de alvo: 0,68rem × 1,4 de linha ≈ 15px de texto + 2×14px. */
  .reviews-link { padding: 14px 0; }
}

/* [v1.9.32] A FICHA PERDEU O CARD e ficou solta — sem fundo, sem borda,
   sem padding de caixa. Com a SINOPSE removida (decisão final do dono, ver
   `fichaBlock` em filme.js e SPEC §3[E]) sobrou uma linha só, e um card
   em volta de uma linha desenha uma caixa sem conteúdo para conter. */
.ficha {
  background: none;
  border: 0;
  border-radius: 0;
  padding: 0;
  margin: 0 0 4px;
}
/* [v1.9.26] TIPOGRAFIA DA LINHA DE METADADOS — decisão FINAL do dono do
   projeto, depois de comparar com uma variante Inter auto-hospedada. Só a
   linha de metadados. (A sinopse, que este comentário dizia não ser
   tocada, foi REMOVIDA da página na v1.9.32 — decisão final do dono.)

   O PEDIDO ERA "a fonte que a Apple usa" — a San Francisco (SF Pro).

   POR QUE NÃO EXISTE ARQUIVO DE SF PRO AQUI, e não é negociável: a licença
   da Apple restringe o uso da SF Pro a mock-ups de interface para
   iOS/OS X/tvOS. Ela não autoriza redistribuição nem uso como webfont em
   site próprio. A pilha de fontes de SISTEMA abaixo é a saída legal: ela
   entrega a San Francisco REAL em Mac e iPhone porque usa a fonte JÁ
   INSTALADA no aparelho — o site não distribui nada —, Segoe UI no
   Windows e Roboto no Android. Custo zero de carregamento; em troca, a
   página muda de aparência conforme o aparelho. `-apple-system` vem
   primeiro (diferente de `--sans`, que começa em `system-ui`) porque é o
   que garante a SF em Safari/WebKit mais antigo.

   REGISTRO DE ESCOPO, para a spec não guardar uma frase falsa: sans NÃO é
   família nova no projeto — `--sans` (pilha de sistema) já existe desde a
   v1 e é o que `body` e os campos de busca usam. O que é novo é usar sans
   na linha EDITORIAL da ficha, que era monoespaçada. */
:root {
  --sans-ui: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
             Helvetica, Arial, sans-serif;
}

/* Peso, tamanho, tracking, caixa e cor: o que tira a linha do ar de
   terminal — a monoespaçada em caixa alta com tracking largo era o que
   fazia ela ler como log em vez de legenda. */
.ficha__line {
  font-family: var(--sans-ui);
  font-size: 0.86rem;
  font-weight: 400;
  letter-spacing: 0.005em;
  text-transform: none;
  color: var(--text-dim);
  line-height: 1.6;
  font-feature-settings: "kern" 1, "liga" 1;
}
.ficha__line .dot { opacity: 0.45; margin: 0 8px; }

/* [v1.9.32] O NOME DO DIRETOR abre a linha, em CAIXA ALTA e sem o prefixo
   "dir." — o prefixo era muleta de quando o nome vinha em caixa normal no
   meio de outros dados; a caixa alta já o identifica como nome próprio em
   destaque.

   A caixa alta é do CSS, nunca do dado (`text-transform`, não
   `toUpperCase()`): o texto no DOM continua "Olivia Wilde", que é o que o
   leitor de tela anuncia e o que uma busca por texto encontra.

   O TRACKING de 0,04em existe porque caixa alta sem ele fecha demais; e o
   `--sans-ui` continua sendo a família da linha inteira — a v1.9.26 tirou
   o mono-caixa-alta da linha, e isto NÃO o traz de volta: é uma palavra em
   caixa alta dentro de uma linha sans. */
.ficha__dir {
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--text-dim);
}

/* [v1.9.32] ETIQUETA DE SEÇÃO — "RECEPÇÃO" e "EM DETALHE · TEMA A TEMA".
   Tipografia de rótulo mono que o projeto já usa (mesma família, corpo e
   tracking do rótulo que existia antes da v1.9.26 e da legenda da barra);
   nenhuma família nova entra por causa disto. */
.section-label {
  font-family: var(--mono);
  font-size: 0.72rem;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--text-mute);
  margin: 0 0 14px;
}

/* =======================================================================
   [v1.9.26] BARRA DE PROPORÇÃO — decisão FINAL do dono do projeto, depois
   de duas rodadas de comparação (três variantes com respiro escuro entre
   faixas, depois duas contínuas). O veredito sobre a mais forte da
   primeira rodada ("sólida"): "tem umas coisas interessantes, mas não dá
   ideia de continuidade — parece que são três barras separadas, cortadas
   com vão no meio". O erro era conceitual: a recepção de um filme é UMA
   população particionada em três, não três medições independentes — um
   vão desenha três objetos onde o dado tem um. Esta barra tem ZERO gap,
   ZERO fio separador, ZERO respiro escuro.

   Paleta oficial dos grupos (nada de verde/vermelho — semáforo codifica
   certo/errado, e o produto se recusa a julgar os grupos); largura em
   percentual exato somando 100; nenhum número dentro da barra.
   ======================================================================= */
.proportion { margin: 18px 0 6px; }
.proportion__bar {
  position: relative;
  display: flex;
  width: 100%;
  height: 14px;
  overflow: hidden;             /* arredonda as pontas da faixa inteira */
  border-radius: 3px;
  background: #1a1d25;          /* trilha, caso não haja fatia nenhuma */
}

.proportion__legend {
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  gap: 6px 18px;
  margin: 9px 0 0;
  padding: 0;
}
.proportion__legend li { display: inline-flex; align-items: center; gap: 7px; }
.proportion__legend-label {
  font-family: var(--mono);
  font-size: 0.68rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  font-weight: 600;
}
.proportion__swatch { width: 10px; height: 10px; border-radius: 2px; flex: none; }
.proportion__swatch[data-group="negativas"] { background: var(--grupo-neg); }
.proportion__swatch[data-group="medianas"]  { background: var(--grupo-med); }
.proportion__swatch[data-group="positivas"] { background: var(--grupo-pos); }
.proportion__legend li[data-group="negativas"] .proportion__legend-label { color: var(--grupo-neg); }
.proportion__legend li[data-group="medianas"]  .proportion__legend-label { color: var(--grupo-med); }
.proportion__legend li[data-group="positivas"] .proportion__legend-label { color: var(--grupo-pos); }

/* [v1.9.26, Entrega 1] O DISCLAIMER DA COTA, preservado. A informação que
   ele carrega ("profundidade igual por grupo ≠ peso igual") fica ancorada
   no objeto que mostra o peso. Discreto, nunca escondido. */
.proportion__note {
  font-size: 0.82rem;
  line-height: 1.5;
  color: var(--text-mute);
  border-left: 2px solid var(--border);
  padding-left: 12px;
  margin: 14px 0 0;
  max-width: 44rem;
}

/* [v1.9.28] `--k` — O ÚNICO NÚMERO ANIMADO DA BARRA, e a razão de ele
   existir. A animação de entrada desliza as FRONTEIRAS de uma partição em
   terços até a distribuição real; se cada camada animasse a sua largura
   por conta própria, seriam duas animações que PARECEM a mesma função do
   tempo. Com `--k` é uma função só, lida por dois lugares:

     x(k)     = neutro + (fim − neutro) × k        (cada fronteira)
     menor(k) = 33,333 + (menor_final − 33,333) × k   (a diagonal)

   Precisa de `@property` porque propriedade personalizada NÃO REGISTRADA
   não interpola — ela salta no meio do keyframe. Registrada como
   `<number>` com `initial-value: 1`, ela interpola continuamente e o valor
   de repouso já é o estado final.

   DEGRADAÇÃO em navegador sem `@property` (anterior a Chrome 85 / Safari
   16.4 / Firefox 128): o `var(--k, 1)` de cada fórmula garante o ESTADO
   FINAL correto em repouso; o que se perde é a continuidade do deslize,
   que vira um salto no meio da duração. Nunca fica errado parado — fica
   sem interpolação. Mesma política do fallback de `container-type` logo
   abaixo. */
@property --k {
  syntax: "<number>";
  inherits: true;
  initial-value: 1;
}

/* Camadas empilhadas, cada uma começando na borda esquerda e terminando
   na sua fronteira com uma aresta DIAGONAL; a de baixo (positivas)
   preenche a barra toda. Ver `barraContinua` em filme.js para a geometria
   e para o cálculo adaptativo de `--diag`.

   `--diag` é a projeção horizontal da diagonal. O JS grava só
   `--menor-pct` (o percentual da menor fatia FINAL, um número sem unidade
   — que é DADO e nunca muda); a conversão para pixel acontece aqui, com
   `cqw` (1cqw = 1% da largura do contêiner). Assim a diagonal acompanha
   resize sozinha, sem `ResizeObserver` e sem um recálculo em JS.

   [v1.9.28] A DIAGONAL ACOMPANHA A INTERPOLAÇÃO (`--menor-agora`). Ela é
   função da fatia mais fina EXIBIDA, e durante o deslize a fatia mais
   fina exibida é a interpolada — no estado de terços, 33,333%. Fixá-la no
   valor final faria a barra animar inteira com uma diagonal dimensionada
   para um destino que ainda não chegou. A identidade
   `min(lerp(t, f_i, k)) = lerp(t, min(f_i), k)` (todas as fatias partem
   do MESMO 33,333%) é o que permite escrever isso como uma conta só, sem
   comparar as três em tempo de execução. */
.proportion { container-type: inline-size; }
.proportion .proportion__bar {
  --k: 1;                               /* 1 = distribuição real, 0 = terços */
  --menor-pct: 10;                      /* substituído pelo JS, por filme */
  --menor-agora: calc(33.333 + (var(--menor-pct) - 33.333) * var(--k, 1));
  --diag: clamp(3px, calc(var(--menor-agora) * 0.55 * 1cqw), 12px);
}
/* Navegador sem unidade de contêiner: diagonal fixa e conservadora —
   menor que o teto, para não comer a fatia estreita de um filme como
   `the-godfather` onde não dá para medir o contêiner. */
@supports not (container-type: inline-size) {
  .proportion .proportion__bar { --diag: 5px; }
}
.proportion__layer {
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  display: block;
  /* [v1.9.28] A FRONTEIRA, interpolada. `--neutro` e `--fim` vêm do JS
     (percentuais sem unidade); `--k` vem da animação. `+ --diag / 2` é a
     geometria de sempre: a diagonal fica CENTRADA na fronteira, então a
     meia altura da barra cai exatamente em x(k). */
  width: calc(
    (var(--neutro) + (var(--fim) - var(--neutro)) * var(--k, 1)) * 1%
    + var(--diag) / 2
  );
  clip-path: polygon(0 0, 100% 0, calc(100% - var(--diag)) 100%, 0 100%);
  /* A aresta diagonal precisa de uma marca própria para não depender só
     do contraste entre as duas cores adjacentes — sem ela, quem não
     distingue laranja de dourado perde a fronteira. `drop-shadow`
     acompanha o recorte do `clip-path`, então a sombra traça EXATAMENTE a
     diagonal, e só ela: nas outras arestas o deslocamento de 1px cai
     dentro da própria camada e não aparece. É a menor marca possível que
     resolve — uma dobra, não um corte: as cores continuam encostadas. */
  filter: drop-shadow(1px 0 0 #0b0c1088);
}
/* A camada de baixo: 100% da barra, sem recorte e SEM FRONTEIRA PRÓPRIA.
   Não participa da interpolação porque não tem o que interpolar — a região
   dela é "o que sobra", e é isso que faz a soma fechar 100% em todo frame
   por CONSTRUÇÃO, e não por sincronia entre animações. */
.proportion__layer--fundo {
  width: 100%;
  clip-path: none;
}
.proportion__layer[data-group="negativas"] { background: var(--grupo-neg); }
.proportion__layer[data-group="medianas"]  { background: var(--grupo-med); }
.proportion__layer[data-group="positivas"] { background: var(--grupo-pos); }

/* =======================================================================
   [v1.9.27, Entrega 3] CALLOUT DE PERCENTUAL — os três números abaixo da
   barra, cada um ancorado na sua fatia por um indicador fino.

   A REGRA DE COLISÃO mora aqui, e não no JS, pela mesma razão que `--diag`
   (§3[E]): ela mistura três grandezas que vivem em lugares diferentes — o
   centro da fatia é DADO (percentual, vem do JSON), a largura da caixa do
   número é TIPOGRAFIA (`ch` da mono, que só o CSS conhece sem medir) e a
   largura da barra é LAYOUT (muda a cada resize). `min()`/`max()` misturam
   porcentagem e `ch` sem reclamar, então a conta inteira reage a resize e
   a zoom de fonte sozinha, sem `ResizeObserver` e sem um recálculo em JS.

   EMPACOTAMENTO DA ESQUERDA PARA A DIREITA:
     x1 = max(0,            min(c1 − L/2,  100% − 3L − 2g))
     x2 = max(x1 + L + g,   min(c2 − L/2,  100% − 2L − g))
     x3 = max(x2 + L + g,   min(c3 − L/2,  100% − L))
   Cada número vai para o centro da sua fatia; quando não cabe, escorrega o
   mínimo necessário e o indicador inclina. O ponto de ancoragem não se
   move — quem se move é o rótulo. Sempre tem solução enquanto 3L + 2g
   couber na barra: ~135px contra 335px a 375px de viewport.

   O termo `(var(--n) − i)` generaliza para 1, 2 ou 3 fatias (o JSON pode
   trazer uma fatia zerada, que `fatiasDeProporcao` filtra): o que cada
   número reserva à direita é o espaço dos que ainda vêm depois dele.
   ======================================================================= */
.proportion__callout {
  position: relative;
  font-family: var(--mono);
  font-size: 0.74rem;
  letter-spacing: 0.01em;
  --L: 5.6ch;            /* caixa do número — "~100%" cabe em 5ch de mono */
  /* [v1.9.28] A FOLGA MÍNIMA passou de 8px para 14px, e o motivo é o NEON
     PERMANENTE. Com o halo aceso o tempo todo, ele passa a ocupar espaço
     o tempo todo — e a regra de empacotamento tinha sido calculada sem
     ele. Em `the-godfather` a 375px o segundo número começa a 8px do fim
     do primeiro; um halo de raio 11px de cada lado atravessaria essa
     folga e misturaria o brilho de dois grupos de cores DIFERENTES, que é
     exatamente o que a paleta por grupo existe para não fazer.

     A conta, com R = raio do halo permanente (11px) e P = respiro que a
     caixa já dá em volta do texto (a caixa tem 5,6ch e o texto mais largo,
     "~100%", tem 5ch → P ≈ 5,75px de cada lado):

         folga_entre_textos = --gap + 2P  ≥  2R
         --gap ≥ 2(11) − 2(5,75) = 10,5px  →  14px, com margem

     ESCOLHIDO CONTAR O HALO NA REGRA, E TAMBÉM LIMITAR O RAIO — as duas
     coisas, porque cada uma sozinha é frágil: só limitar o raio deixaria
     a garantia dependendo de um número que a próxima calibração de brilho
     pode mexer sem perceber; só aumentar a folga deixaria o halo livre
     para crescer. Aumentar a folga não custa nada nos 30 filmes em que a
     restrição não morde (o rótulo já cabia no centro da sua fatia); custa
     ~6px de deslocamento a mais só nos que já estavam deslocados. */
  --gap: 14px;           /* folga mínima entre duas caixas vizinhas */
  --half: calc(var(--L) / 2);
  --step: calc(var(--L) + var(--gap));
  --track: 9px;          /* altura da pista do indicador, barra → número */
  --t: 1px;              /* espessura do indicador */
  height: calc(var(--track) + 1.15rem);
  margin-top: 3px;

  --x1: max(0px,
            min(calc(var(--c1) * 1% - var(--half)),
                calc(100% - var(--L) - (var(--n) - 1) * var(--step))));
  --x2: max(calc(var(--x1) + var(--step)),
            min(calc(var(--c2) * 1% - var(--half)),
                calc(100% - var(--L) - (var(--n) - 2) * var(--step))));
  --x3: max(calc(var(--x2) + var(--step)),
            min(calc(var(--c3) * 1% - var(--half)),
                calc(100% - var(--L))));
}

/* Um invólucro por fatia, do tamanho do callout inteiro: é ele que dá aos
   filhos um bloco contentor com a LARGURA DA BARRA, que é o sistema de
   coordenadas em que `--c` está escrito. */
.proportion__anchor { position: absolute; inset: 0; pointer-events: none; }
.proportion__anchor:nth-child(1) { --c: var(--c1); --x: var(--x1); }
.proportion__anchor:nth-child(2) { --c: var(--c2); --x: var(--x2); }
.proportion__anchor:nth-child(3) { --c: var(--c3); --x: var(--x3); }
.proportion__anchor {
  --cx:  calc(var(--c) * 1%);              /* centro VERDADEIRO da fatia */
  --lcx: calc(var(--x) + var(--half));     /* centro do rótulo, já movido */
}
.proportion__anchor[data-group="negativas"] { --g: var(--grupo-neg); --g-soft: var(--grupo-neg-soft); }
.proportion__anchor[data-group="medianas"]  { --g: var(--grupo-med); --g-soft: var(--grupo-med-soft); }
.proportion__anchor[data-group="positivas"] { --g: var(--grupo-pos); --g-soft: var(--grupo-pos-soft); }

/* [v1.9.28] O ESTADO ACESO PERMANENTE. Estes dois tokens são o REPOUSO do
   neon — e, por serem o estado base, são também o que `prefers-reduced-
   motion: reduce` entrega no primeiro frame: o neon aceso NÃO é
   movimento, e fica.

   Calibração: núcleo branco fechado (2px), halo na cor do grupo (6px) e
   um halo externo na cor a 20% de alfa (11px) — aceso o bastante para ler
   como neon, discreto o bastante para não competir com o título do filme,
   que é serifado, 2,6rem e ocupa a largura da coluna. O RAIO MÁXIMO DE
   11px é requisito de layout: ver a derivação de `--gap` acima. O pico da
   ignição vai a 28px, mas por ~35ms e escalonado — dois vizinhos nunca
   estão no pico ao mesmo tempo (55ms de defasagem). */
.proportion__pct {
  position: absolute;
  left: var(--x);
  top: var(--track);
  width: var(--L);
  text-align: center;
  font-variant-numeric: tabular-nums;
  --pct-aceso: #ffffff;
  --pct-neon: 0 0 2px rgba(255, 255, 255, 0.65), 0 0 6px var(--g),
              0 0 11px var(--g-soft), 0 0 0 transparent;
  color: var(--pct-aceso);
  text-shadow: var(--pct-neon);
}

/* O indicador, em duas metades porque o CSS não tem sinal. A que aponta
   para a direita mede `max(0, rótulo − centro)`; a que aponta para a
   esquerda, `max(0, centro − rótulo)`. Só uma tem largura de verdade — a
   outra colapsa para `--t` e, por estar ancorada NO CENTRO VERDADEIRO,
   vira a marquinha vertical em cima dele. Sem deslocamento as duas
   colapsam e sobra uma marca vertical de 2px, que é o caso mais comum do
   catálogo. `clip-path` desenha a faixa fina inclinada dentro da caixa;
   com a caixa em `--t`, o polígono degenera num retângulo vertical. */
.proportion__lead {
  position: absolute;
  top: 0;
  height: var(--track);
  background: var(--g);
  opacity: 0.5;
  pointer-events: none;
}
.proportion__lead--dir {
  left: var(--cx);
  width: max(var(--t), calc(var(--lcx) - var(--cx)));
  clip-path: polygon(0 0, var(--t) 0, 100% 100%, calc(100% - var(--t)) 100%);
}
.proportion__lead--esq {
  right: calc(100% - var(--cx));
  width: max(var(--t), calc(var(--cx) - var(--lcx)));
  clip-path: polygon(100% 0, calc(100% - var(--t)) 0, 0 100%, var(--t) 100%);
}

/* =======================================================================
   [v1.9.28] ANIMAÇÃO DE ENTRADA DA BARRA — as FRONTEIRAS DESLIZAM.

   O MODELO ANTERIOR SAIU INTEIRO. A v1.9.27 preenchia a barra de 0 a 100%
   com um bloco neutro e só então revelava as cores; a camada de prefill
   (`.proportion__prefill`, `#454b5a`) foi REMOVIDA, não escondida atrás de
   flag. O modelo novo: a barra nasce COMPLETA, particionada em três partes
   iguais, e as fronteiras deslizam até a distribuição real. Com isso as
   antigas Fase 1 (fill) e Fase 2 (partição) deixam de existir como etapas
   separadas — viram UMA.

   O ESTADO BASE DESTE ARQUIVO CONTINUA SENDO O ESTADO FINAL. Tudo que a
   animação faz está DENTRO de `@media (prefers-reduced-motion:
   no-preference)`, inclusive o estado inicial (`--k: 0`, número apagado).
   É a única construção em que `reduce` entrega o estado final no primeiro
   frame sem depender de nenhuma regra de desligamento. O
   `* { animation: none !important }` lá embaixo continua valendo como
   segunda linha, mas nada aqui depende dele.

   AS DUAS FASES, e a soma:
     A · fronteiras    0ms → 650ms     terços ──▶ distribuição real
     B · ignição     650ms → 1020ms    3 números × 260ms, escalonados 55ms
   Total 1020ms.

   FASE A — UMA FUNÇÃO TEMPORAL SÓ. `--k` é animado uma vez, na barra; as
   duas fronteiras e a diagonal são funções puras dele (ver `@property
   --k` acima). Não são duas animações com temporização igual: é uma
   animação, lida por dois lugares. E a camada de baixo ocupa 100% da
   barra em todos os frames, então a soma fecha por CONSTRUÇÃO — a região
   da terceira fatia é o que sobra, não um terceiro valor animado.

   A DURAÇÃO É FIXA (650ms) e independente da distribuição. A DISTÂNCIA
   que cada fronteira percorre é consequência do dado — `cats-2019` move a
   primeira fronteira 53 pontos percentuais e `napoleon-2023` move 11 —,
   mas as duas levam os mesmos 650ms. Amarrar a duração à distância faria
   a animação codificar uma segunda variável competindo com a barra.

   A CURVA é desaceleração pura: `cubic-bezier(0.22, 0.68, 0.28, 1)`, os
   dois pontos de controle dentro de [0,1]. Isso não é preferência, é o
   que garante `k ∈ [0,1]` em todo instante — e `k` fora desse intervalo
   (overshoot, bounce, spring, elasticidade) produziria x1 > x2, ou seja,
   uma fatia de largura NEGATIVA. A proibição de overshoot é geométrica
   antes de ser estética.

   NENHUM VÃO EM FRAME NENHUM. Não há gutter, fio separador, margem,
   borda-separador nem pixel transparente entre regiões, nem durante o
   deslize: as camadas não mudam de opacidade nem de posição, só de
   LIMITE, e continuam encostadas umas nas outras o tempo todo.

   FASE B — ver `proportion-ignite`.

   NENHUM TEMPORIZADOR EM JS. Fase A é uma animação sem atraso; o
   encadeamento da Fase B é `animation-delay: 650ms + var(--ordem) × 55ms`.
   Sair da página no meio não deixa nada pela metade porque não há nada
   guardado em lugar nenhum: a página é remontada do zero a cada visita, e
   o estado final é o estado base.
   ======================================================================= */
@media (prefers-reduced-motion: no-preference) {
  .proportion__bar {
    animation: proportion-fronteiras 650ms cubic-bezier(0.22, 0.68, 0.28, 1) both;
  }
  .proportion__lead {
    animation: proportion-lead 260ms ease-out both;
    animation-delay: calc(650ms + var(--ordem, 0) * 55ms);
  }
  .proportion__pct {
    animation: proportion-ignite 260ms linear both;
    animation-delay: calc(650ms + var(--ordem, 0) * 55ms);
  }
}

@keyframes proportion-fronteiras {
  from { --k: 0; }
  to   { --k: 1; }
}

@keyframes proportion-lead {
  from { opacity: 0; }
  to   { opacity: 0.5; }
}

/* A IGNIÇÃO — letreiro antigo entrando em funcionamento. Começa apagado,
   oscila, dá um pico e ESTABILIZA ACESO.

   [v1.9.28] O INÍCIO PASSOU DE OPACIDADE 0,16 PARA 0, revertendo a escolha
   da v1.9.27, e o motivo é o modelo novo de animação. Antes, a barra
   crescia vazia e um número fantasma embaixo dela não contradizia nada.
   Agora a barra nasce em TERÇOS e as fronteiras deslizam — um `~2%`
   visível, ancorado na posição FINAL, ficaria meio segundo apontando para
   uma região que naquele instante é 33%. O empacotamento do callout não
   depende de `--k` (as posições são as finais desde o primeiro frame), e é
   justamente por isso que os rótulos não podem estar visíveis antes de as
   fronteiras chegarem. Ausentes durante a interpolação, acesos depois, já
   no lugar certo e com o valor certo.

   O TEXTO CONTINUA NO DOM desde o primeiro frame, com o valor final: o que
   muda é opacidade, cor e sombra. A animação segue sendo de aparência, não
   de conteúdo.

   [v1.9.28] O NEON FICA LIGADO — correção da decisão da v1.9.27, pelo dono
   do projeto. O brilho NÃO decai depois do pico: o repouso é um estado
   ACESO permanente (núcleo branco + halo na cor do grupo), não um resíduo.
   A ignição continua sendo o EVENTO — apagado → flicker → pico →
   estabiliza —, e o pico continua mais intenso que o repouso; o que mudou
   foi o destino.

   AS QUATRO CAMADAS DE SOMBRA SÃO AS MESMAS EM TODOS OS QUADROS, com a
   quarta zerada no repouso. `text-shadow` com número DIFERENTE de camadas
   entre dois quadros não interpola — salta. Manter a contagem é o que faz
   o pico descer suavemente até o repouso em vez de piscar para ele.

   O RAIO DO HALO PERMANENTE É LIMITADO A 11px, e isso é requisito de
   layout, não de gosto: ver `--gap` no callout. */
@keyframes proportion-ignite {
  0%   { opacity: 0; color: var(--text-mute);
         text-shadow: 0 0 0 transparent, 0 0 0 transparent,
                      0 0 0 transparent, 0 0 0 transparent; }
  14%  { opacity: 0.52; color: var(--text-dim); }
  22%  { opacity: 0.12; color: var(--text-mute); }
  34%  { opacity: 0.88; color: var(--text-dim); }
  42%  { opacity: 0.30; color: var(--text-mute); }
  58%  { opacity: 1;    color: #ffffff;
         text-shadow: 0 0 2px #fff, 0 0 6px var(--g),
                      0 0 15px var(--g), 0 0 28px var(--g-soft); }
  100% { opacity: 1;    color: var(--pct-aceso);
         text-shadow: var(--pct-neon); }
}

/* [v1.9.19] VEREDITO — Entrega 2. Uma frase, montada por TEMPLATE sobre o
   lift já computado (ver `veredito()` em filme.js) — zero LLM.
   [v1.9.26, Entrega 1] MUDOU DE POSIÇÃO, não de conteúdo: era a primeira
   coisa depois da ficha, passa a fechar a análise, depois dos bullets e
   antes da narrativa colapsada. Conclusão lida ANTES da evidência é
   asserção; lida DEPOIS é fecho — e o topo agora tem a barra, que diz
   "quanta gente de cada lado" sem precisar de leitura. Só a margem
   superior muda (respiro de seção nova, no lugar do respiro de "colado na
   ficha"); família, corpo, cor e largura são os mesmos de sempre. */
.verdict {
  font-family: var(--serif);
  font-size: 1.22rem;
  line-height: 1.5;
  color: var(--text);
  margin: 40px 0 8px;
  max-width: 40rem;
}

/* [v1.9.34] A LINHA DE AUSÊNCIA DE VEREDITO — ocupa a POSIÇÃO do veredito e
   precisa NÃO ser lida como um (SPEC §2.5). Ela não afirma nada sobre o
   filme; diz por que o produto se recusa a afirmar.

   O que a distingue, e cada escolha faz um trabalho:
   · SANS, não serifa — a serifa é a voz editorial do produto (veredito,
     narrativa); esta linha é do sistema falando de si, o mesmo registro do
     `.disclaimer` e do `.proportion__note`;
   · corpo menor e cor secundária — não compete com os bullets acima, que são
     o achado de verdade nesta página;
   · a barra à esquerda marca "nota de método" sem caixa nem ícone, no mesmo
     vocabulário discreto do disclosure APROFUNDAR.

   NÃO é `.verdict` com modificador, de propósito: um modificador convida a
   herdar a serifa de volta num refactor futuro, e aí a linha vira veredito
   por acidente de CSS. */
.verdict-absent {
  font-family: var(--sans);
  font-size: 0.94rem;
  line-height: 1.55;
  color: var(--text-dim);
  margin: 40px 0 8px;
  max-width: 40rem;
  padding-left: 14px;
  border-left: 2px solid var(--border);
}

/* divisor — [v1.9.26] só a linha arco-íris. O rótulo "EM DETALHE · TEMA A
   TEMA" saiu da tela (Entrega 1: não há mais um resumo antes dele do qual
   separar "o detalhe"; os bullets vêm direto), e o `.disclaimer` que vinha
   embaixo virou `.proportion__note`, sob a barra. A classe
   `.detail-divider__label` fica no arquivo sem uso? Não — foi removida
   junto, como a v1.9.19 fez com a tabela: remoção é remoção. */
.detail-divider { margin: 30px 0 4px; }
.detail-divider .spectrum-line { margin-bottom: 16px; }
.disclaimer {
  font-size: 0.9rem;
  color: var(--text-mute);
  line-height: 1.5;
  border-left: 2px solid var(--border);
  padding-left: 14px;
  margin: 0 0 8px;
}

/* [v1.9.19] BULLETS AGRUPADOS POR SENTIMENTO — Entrega 3/4. Dois blocos
   (três quando o meio é dominante — `--sentiment-groups--3`), MESMO
   leiaute entre negativas e positivas (a neutralidade de TRATAMENTO do §0
   continua valendo entre esses dois). RESPIRO em vez de linha (Entrega 7):
   nenhuma borda entre colunas — o espaço em branco do `gap` já separa. */
.sentiment-wrap { margin-top: 12px; }
.sentiment-groups {
  display: grid;
  grid-template-columns: 1fr;
  gap: 8px 40px;
}
@media (min-width: 720px) {
  .sentiment-groups.sentiment-groups--2 { grid-template-columns: 1fr 1fr; }
  .sentiment-groups.sentiment-groups--3 { grid-template-columns: 1fr 1fr 1fr; }
}

/* [v1.9.45] O MODO DE DUAS COLUNAS DO MOBILE (≤640px) não mora aqui: ele
   sobrescreve regras de `.theme*`/`.group*` declaradas mais abaixo neste
   arquivo, e `@media` não soma especificidade — declarado neste ponto, ele
   perderia para elas. Fica DEPOIS da última delas, perto do fim do arquivo
   — procure por "AS DUAS COLUNAS CABEM NO CELULAR". (Até a v1.9.47 esta
   nota dizia "logo depois de `.group__janela`"; essa regra saiu na
   v1.9.48 e a âncora agora é a busca pelo título.) */
/* grupo — agora um item de grid (ou o conteúdo de um `.disclosure`
   colapsado), nunca mais uma faixa full-width com `border-top`; a
   separação vem do `gap` do grid, ou do próprio `<details>`. */
.group { padding: 0; }
.group__header { display: flex; align-items: baseline; flex-wrap: wrap; gap: 10px; margin-bottom: 4px; }
.group__dot { width: 11px; height: 11px; border-radius: 50%; align-self: center; }
.group__name {
  font-family: var(--mono);
  font-size: 0.82rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  font-weight: 600;
}
.group__notas {
  font-family: var(--mono);
  font-size: 0.68rem;
  letter-spacing: 0.06em;
  color: var(--text-mute);
}
/* v1.4.0 — share real da faixa. Mesmo estilo nos três grupos por decisão de
   produto (neutralidade de TRATAMENTO): a diferença entre 3% e 79% deve vir
   do número, nunca de destaque tipográfico dado a um dos lados. */
.group__share {
  font-family: var(--mono);
  font-size: 0.68rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-dim);
  /* [v1.9.20] herda o `margin-left: auto` que "N de M analisadas" tinha —
     é o último número do header agora que a contagem saiu, e continua
     empurrado pra direita, mesmo lugar de sempre. */
  margin-left: auto;
}
.group[data-group="negativas"] .group__name { color: var(--grupo-neg); }
.group[data-group="medianas"]  .group__name { color: var(--grupo-med); }
.group[data-group="positivas"] .group__name { color: var(--grupo-pos); }

/* [v1.9.19] tint de fundo por sentimento — atrás de FLAG (Entrega 7),
   NUNCA ligado por padrão. Ativa com `?tint=1` na URL (ver filme.js) —
   ainda não decidido se acentua demais a assimetria entre os dois blocos
   agora que o meio saiu do meio-a-meio; existe pra o dono do projeto
   comparar com e sem, olhando, antes de decidir. 2-3% de opacidade, os
   MESMOS tokens `--neg/--med/--pos` de sempre (nenhuma cor nova). */
.group.is-tinted[data-group="negativas"] { background: color-mix(in srgb, var(--grupo-neg) 3%, transparent); border-radius: var(--radius); padding: 14px 16px; }
.group.is-tinted[data-group="medianas"]  { background: color-mix(in srgb, var(--grupo-med) 3%, transparent); border-radius: var(--radius); padding: 14px 16px; }
.group.is-tinted[data-group="positivas"] { background: color-mix(in srgb, var(--grupo-pos) 3%, transparent); border-radius: var(--radius); padding: 14px 16px; }

/* [2026-09-17] NOTA EDITORIAL do grupo — "modo reduzido" saiu do alerta
   de sistema abaixo (`.mode-warning`) para esta gramática, a mesma do
   disclosure `.theme__toggle` ("APROFUNDAR"): rótulo mono maiúsculo
   pequeno, cor herdada do grupo, sem caixa, sem borda, sem ícone. Um
   filete fino em cima é o único separador — o mesmo dispositivo editorial
   que o resto da página usa para dividir seções (`.site-footer`,
   `.creditos__bloco`), nunca uma caixa com fundo. NUNCA escondido — mesma
   regra de produto do `.mode-warning`, só a linguagem visual muda: isto é
   contexto (amostra pequena É sinal, não defeito), não um erro do
   sistema. Ver ABERTO.md B5. */
.group-note {
  margin: 14px 0 6px;
  padding-top: 10px;
  border-top: 1px solid var(--border-soft);
  font-size: 0.85rem;
  line-height: 1.5;
  color: var(--text-dim);
}
.group-note__label {
  display: block;
  font-family: var(--mono);
  font-size: 0.64rem;
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  margin-bottom: 4px;
}
.group[data-group="negativas"] .group-note__label { color: var(--grupo-neg); }
.group[data-group="medianas"]  .group-note__label { color: var(--grupo-med); }
.group[data-group="positivas"] .group-note__label { color: var(--grupo-pos); }

/* aviso de modo degradado — NUNCA escondido (regra de produto) */
.mode-warning {
  display: flex;
  gap: 10px;
  align-items: flex-start;
  background: #f5c5180f;
  border: 1px solid #f5c51833;
  border-left: 3px solid var(--warn);
  border-radius: 8px;
  padding: 12px 14px;
  margin: 14px 0 6px;
  font-size: 0.9rem;
  line-height: 1.5;
  color: var(--text-dim);
}
.mode-warning strong { color: var(--warn); font-weight: 600; }
.mode-warning__icon { flex-shrink: 0; margin-top: 1px; }
.sem-analise-link {
  display: inline-block;
  margin-top: 6px;
  font-family: var(--mono);
  font-size: 0.7rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-dim);
}

/* temas — hierarquia (Entrega 7): o olho vai no QUE (nome, cor principal).
   [v1.9.20, Entrega 2] `.theme__freq` ("~X de N" ao lado do nome) saiu do
   JS — nenhuma contagem de review em texto — e a regra sai daqui junto;
   a BARRA abaixo é quem comunica o peso agora, sozinha. */
.themes { margin-top: 16px; display: flex; flex-direction: column; gap: 20px; }
.theme__top { display: flex; align-items: baseline; gap: 12px; }
.theme__name { font-size: 1rem; font-weight: 500; color: var(--text); flex: 1; }
/* =======================================================================
   [v1.9.47] A TRILHA SAIU. Sobra o TRAÇO.

   A trilha (`#242832` + borda) existia desde a Entrega 7 com o argumento
   de que "a parte vazia precisa se ler de relance". O efeito colateral é
   a FORMA: trilha cheia + preenchimento é a gramática de barra de
   progresso, e barra de progresso promete uma coisa que se COMPLETA. A
   intensidade passa a vir da comparação entre os traços vizinhos — todos
   ancorados na mesma margem esquerda, que é a única referência que sobra
   e por isso não pode ser deslocada por nada (ver o piso abaixo).

   O QUE A BARRA CODIFICA — e a medição corrigiu a premissa desta entrega.
   NÃO é `rotulo_forca`: esse campo só existe em `condicoes.*` e o
   `filme.js` não o lê em lugar nenhum. A largura é
   `mencoes_aproximadas / n_reviews_analisadas`, calculada em `themeRow` —
   CONTÍNUA, não uma escada discreta. No catálogo publicado: 633 itens,
   60 larguras distintas, de 10,0% a 87,5%, mediana 25,0%. A trilha
   representava o denominador `N` (40 em 569 dos 633 itens); ela sai, e o
   denominador continua onde sempre esteve em texto — no `aria-label` da
   barra, que não muda.

   `overflow: hidden` sai junto com a trilha: ele existia para RECORTAR o
   preenchimento contra o raio dela. Sem caixa, não há o que recortar.

   ATENÇÃO ao `box-sizing: border-box` global: com `height: 6px` e borda de
   1px, o preenchimento tinha 4px de altura REAL dentro de uma caixa de
   6px, e o 100% dele era a largura da barra MENOS 2px de borda. Sem borda,
   os 6px e a largura inteira passam a ser do traço. A caixa externa
   continua com a MESMA altura e as MESMAS margens de sempre, de propósito:
   o ritmo vertical do bloco de bullets não se mexe com esta entrega. */
.theme__bar {
  height: 6px;
  margin: 8px 0 8px;
}
/* O PISO DE COMPRIMENTO, e ele é deliberadamente INERTE hoje.

   Sem trilha, um traço muito curto vira cisco solto no vão — e o clamp de
   `_construir_temas` permite `mencoes_aproximadas` igual a 0, então o caso
   degenerado (traço de largura zero, invisível) é alcançável pelo código
   mesmo não tendo ocorrido ainda.

   [2026-09-17, remedido sobre 99 filmes/1780 itens — ABERTO.md B5] 14px
   virou distorção quando a expansão publicou `goodfellas`/negativas
   ("Tratamento raso das personagens femininas e visão masculina"), 7,5%
   = 11,96px no mobile — abaixo do piso antigo. Remedido do zero: a
   distribuição inteira, não só o item que quebrou.

   **A RESTRIÇÃO QUE DECIDE O VALOR NÃO É MAIS A FIDELIDADE — É
   `piso >= 2 × altura`.** A distribuição sozinha pediria 10px (sai do
   VÃO dela: outlier isolado em 11,96px mobile, resto só recomeça em
   15,95px, 10px fica abaixo dos dois com folga — não é o valor do
   goodfellas arredondado). Mas a altura do traço é 6px, e
   `test_o_piso_ainda_le_como_traco_e_nao_como_ponto` (v1.9.47) exige o
   piso em pelo menos o dobro dela para ler como TRAÇO, não como PONTO —
   12px é o menor valor que passa nas duas. Sem essa regra, 10px
   passaria; ela é o que sobra a revisitar se o piso voltar a ser
   questão, não a distribuição (ver ABERTO.md B5 para as duas outras
   opções cogitadas e não tomadas: baixar a altura do traço, ou
   revisitar a proporção 2× em si).

   12px contra o real de 11,96px do goodfellas é 0,0375px de diferença —
   SUBPIXEL, nenhum navegador renderiza essa diferença; é o limite da
   unidade de medida, não uma distorção visível. A tolerância que isso
   exige em `test_o_piso_nao_alonga_nenhum_item_publicado` está
   documentada NAQUELE teste, com a razão escrita ao lado — reconhece o
   limite da unidade, não afrouxa a invariante.

   O PISO SÓ MORDE NO MOBILE (159,5px) — nunca é a restrição no desktop.
   O mesmo item mais fraco do catálogo (7,5%) já dá 16,0px na coluna
   desktop de 3 grupos (213,3px, caso `--3`, meio dominante) e 25,5px na
   de 2 grupos (340px, caso `--2`, o comum) — as duas acima de qualquer
   piso candidato medido (14/12/10/8/6px).

   A FOLGA VALE PARA 99 FILMES, NÃO É GARANTIA PERMANENTE. Com o
   catálogo em ~300, itens abaixo de 10% deixam de ser caso isolado por
   simples volume — remedir a distribuição inteira quando o catálogo
   crescer, pelo mesmo método (não reajustar ao próximo outlier sem medir
   de novo). */
.theme__bar span {
  display: block;
  height: 100%;
  min-width: 12px;
  border-radius: 999px;
  transition: width 0.6s cubic-bezier(0.2, 0.7, 0.2, 1);
}
.group[data-group="negativas"] .theme__bar span { background: var(--grupo-neg); }
.group[data-group="medianas"]  .theme__bar span { background: var(--grupo-med); }
.group[data-group="positivas"] .theme__bar span { background: var(--grupo-pos); }

/* =======================================================================
   [v1.9.26, Entrega 4] O DISCLOSURE "APROFUNDAR".

   O QUE SAIU E POR QUÊ. A v1.9.19 (Entrega 6) trocou um "+" que ninguém
   percebia ser clicável por um PILL na cor do grupo — fundo preenchido,
   `border-radius: 999px`, padding de CTA. Resolveu a afordance e criou
   outro problema: dentro de um bullet cujo elemento principal é o NOME do
   tema, o pill virava a coisa mais chamativa da linha. A referência agora
   é disclosure editorial minimalista (GOV.UK Details e os disclosures
   "flush" de design systems modernos): fundo transparente, sem borda, sem
   cápsula, sem raio grande — o controle pertence ao bloco editorial, não é
   um componente colado nele.

   NENHUMA LINGUAGEM VISUAL NOVA: a família é a monoespaçada que já rotula
   tudo no site, o chevron é o mesmo ícone (mesmo stroke, mesmo round-cap)
   do back-link e das `.disclosure`, e a cor é a do próprio grupo — só que
   com intensidade MENOR que o nome do tema e que a barra de frequência,
   que continuam sendo o que o olho pega primeiro.

   O que NÃO mudou: a posição do controle dentro do bullet, as barras de
   frequência, a hierarquia tipográfica, as cores dos grupos, o conteúdo
   expandido e a lógica de expansão/colapso (botão + `aria-expanded` +
   classe no alvo). É redesenho de aparência e de animação, e só. */
.theme__toggle {
  background: none;
  border: 0;
  border-radius: 3px;              /* só para o anel de foco não ficar duro */
  padding: 9px 2px 9px 0;
  min-height: 40px;                /* área de toque; ver @media (hover:none) */
  margin-top: 2px;
  cursor: pointer;
  font-family: var(--mono);
  font-size: 0.64rem;
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--text-dim);
  opacity: 0.78;                   /* o degrau de peso contra nome e barra */
  display: inline-flex;
  align-items: center;
  gap: 8px;
  transition: color 0.16s, opacity 0.16s;
}
/* Cor do grupo, e nada além dela. Sem fundo, os tokens `--neg-soft`/
   `--med-soft`/`--pos-soft` (que eram o preenchimento do pill) ficam sem
   NENHUM uso na folha. Registrados aqui e mantidos em `:root` de
   propósito: apagar cor da paleta é decisão de paleta, não efeito colateral
   de um redesenho de controle. */
.group[data-group="negativas"] .theme__toggle { color: var(--grupo-neg); }
.group[data-group="medianas"]  .theme__toggle { color: var(--grupo-med); }
.group[data-group="positivas"] .theme__toggle { color: var(--grupo-pos); }

/* ESTADOS: repouso (acima), hover, foco, aberto. Todos pela mesma via —
   opacidade —, para que nenhum deles reintroduza caixa ou borda. */
.theme__toggle:hover { opacity: 1; }
.theme__toggle[aria-expanded="true"] { opacity: 1; }
.theme__toggle:focus-visible {
  outline: 2px solid #3b82f6aa;
  outline-offset: 2px;
  opacity: 1;
}
/* Chevron menor que o das `.disclosure` — mesmo desenho, peso menor, como
   o resto do controle. Gira acompanhando a abertura, na MESMA curva e na
   MESMA duração da área, para os dois lerem como um movimento só. */
.theme__toggle .chevron {
  display: inline-flex;
  transition: transform 0.32s cubic-bezier(0, 0, 0.58, 1);
}
.theme__toggle .chevron svg { width: 12px; height: 12px; }
.theme__toggle[aria-expanded="true"] .chevron { transform: rotate(180deg); }

/* -----------------------------------------------------------------------
   A ANIMAÇÃO — o requisito principal da entrega, acima do visual.

   A sensação pedida: o conteúdo estava ESCONDIDO EMBAIXO do bullet e
   desliza para fora dele até a posição final. Espacial e vertical, não
   fade. Três camadas fazem isso, e cada uma tem um trabalho:

     .theme__example        a caixa que ABRE — `grid-template-rows` de
                            `0fr` para `1fr`. É o jeito de animar altura
                            "até o conteúdo" sem altura fixa e sem medir
                            nada em JS (o `max-height` chutado é o hack
                            que esta estrutura existe para evitar).
     .theme__example-clip   RECORTA. É o que faz o texto parecer estar
                            atrás da borda de cima, e não simplesmente
                            aparecendo.
     .theme__example-inner  DESLIZA. Começa 10px ACIMA da posição final —
                            portanto, no instante zero, atrás da borda
                            superior do recorte — e desce enquanto a caixa
                            cresce. A opacidade COMPLEMENTA (sobe mais
                            rápido que o deslize), nunca substitui.

   A CURVA foi escolhida por medição, não por gosto — a altura da caixa
   amostrada a 0/80/160/240/320ms, abrindo e fechando, em três candidatas:

     0.22,0.61,0.36,1   abrir 0 35 76 95 100   fechar 100 16  2  0 0
     0.4,0,0.2,1        abrir 0  6 60 92 100   fechar 100 58  5  0 0
     0,0,0.58,1  ←      abrir 0 14 47 82 100   fechar 100 39 10  1 0

   A primeira (uma ease-out agressiva) responde bem ao abrir mas colapsa
   84% da altura nos primeiros 80ms ao fechar — lê como corte, não como
   recolher. A segunda (ease-in-out) tem a metade do movimento espremida
   entre 80 e 160ms nos dois sentidos, e ainda dá 80ms de nada no começo
   da abertura, o que num controle de manipulação direta lê como atraso.
   `cubic-bezier(0, 0, 0.58, 1)` — a `ease-out` clássica — é a única em
   que nenhum quarto da duração carrega mais que ~40% do movimento nos
   DOIS sentidos: responde no instante do clique e chega devagar.

   SEM spring e SEM bounce: os dois pontos de controle ficam dentro de
   [0,1], então nem a altura nem o deslize ultrapassam o valor final em
   instante nenhum — as duas séries acima são monotônicas.

   320ms: curto o bastante para não ficar entre o clique e a leitura,
   longo o bastante para o olho ver a DIREÇÃO do movimento, que é o efeito
   todo. O fechamento é a MESMA declaração ao contrário: nada de duração
   nem de curva assimétrica.

   TODA a caixa fica no `-inner` (margem, filete, padding): se a margem
   ficasse no elemento externo, o estado fechado teria 10px de sobra.

   `visibility` no `-clip` é o que impede a regressão de acessibilidade que
   esta técnica traz de brinde: ao contrário de `display: none`, uma linha
   de grid de altura zero NÃO tira o conteúdo da árvore de acessibilidade,
   e um leitor de tela leria a paráfrase de todo bullet fechado da página.
   `visibility: hidden` tira; o `transition-delay` de 0.32s segura a
   visibilidade até o fim da animação de fechar, para o texto não sumir de
   uma vez no meio dela.
   ----------------------------------------------------------------------- */
.theme__example {
  display: grid;
  grid-template-rows: 0fr;
  transition: grid-template-rows 0.32s cubic-bezier(0, 0, 0.58, 1);
}
.theme__example.is-open { grid-template-rows: 1fr; }

.theme__example-clip {
  overflow: hidden;
  min-height: 0;                   /* sem isto o item de grid não encolhe */
  visibility: hidden;
  transition: visibility 0s linear 0.32s;
}
.theme__example.is-open .theme__example-clip {
  visibility: visible;
  transition-delay: 0s;
}

.theme__example-inner {
  margin-top: 10px;
  font-size: 0.94rem;
  line-height: 1.55;
  color: var(--text-dim);
  border-left: 2px solid var(--border);
  padding-left: 14px;
  transform: translateY(-10px);
  opacity: 0;
  transition: transform 0.32s cubic-bezier(0, 0, 0.58, 1),
              opacity 0.22s cubic-bezier(0, 0, 0.58, 1);
}
.theme__example.is-open .theme__example-inner {
  transform: translateY(0);
  opacity: 1;
}
.theme__flag {
  display: block;
  margin-top: 8px;
  font-family: var(--mono);
  font-size: 0.62rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-mute);
}


/* [v1.9.48] `.group__obs` (a observação geral, em serifa, abaixo dos
   bullets) e `.group__janela` ("escritas majoritariamente entre…") saíram
   da folha — o JS parou de construir os dois elementos, decisão do dono.
   Ver `groupBlock` em filme.js. Os DADOS que os alimentavam continuam no
   JSON; o que saiu é a tela. */

/* [v1.9.19] DISCLOSURE genérico — narrativa completa colapsada (Entrega 1)
   e o grupo-do-meio recolhido (Entrega 4). RESPIRO em vez de linha
   (Entrega 7): a separação da seção anterior é margem, não borda pesada. */
.disclosure { margin: 36px 0 8px; }
.disclosure > summary {
  cursor: pointer;
  list-style: none;
  display: inline-flex;
  align-items: center;
  gap: 10px;
  font-family: var(--mono);
  font-size: 0.74rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--text-dim);
  padding: 10px 16px;
  background: var(--bg-elev);
  border: 1px solid var(--border);
  border-radius: 999px;
  transition: border-color 0.15s, color 0.15s, background 0.15s;
}
.disclosure > summary::-webkit-details-marker { display: none; }
.disclosure > summary:hover { border-color: #3a4152; color: var(--text); }
.disclosure > summary:focus-visible { outline: 2px solid #3b82f6aa; outline-offset: 3px; }
.disclosure .chevron { transition: transform 0.2s; display: inline-flex; }
.disclosure[open] > summary .chevron { transform: rotate(180deg); }
.disclosure[open] > summary { background: var(--bg-elev-2); }
.disclosure__body { margin-top: 22px; }
.disclosure__body.narrativa p {
  font-size: 1.06rem;
  line-height: 1.68;
  color: var(--text-dim);
  margin: 0 0 16px;
}
.disclosure__body.narrativa p:first-of-type { color: var(--text); }
/* meio-termo recolhido: o pill some (o `<details>` inteiro já é o
   controle), o rótulo fica na cor do meio. */
.disclosure--meio > summary { color: var(--grupo-med); }
.disclosure--meio > summary:hover { color: var(--text); }
.disclosure--meio .disclosure__body { margin-top: 26px; }

/* =======================================================
   MICRO-PESQUISA (A/B)
   ======================================================= */
.survey {
  background: var(--bg-elev);
  border: 1px solid var(--border-soft);
  border-radius: var(--radius);
  padding: 22px 22px 24px;
  margin: 44px 0 8px;
}
.survey__q {
  font-family: var(--serif);
  font-size: 1.2rem;
  margin: 0 0 16px;
}
.survey__options { display: flex; flex-direction: column; gap: 10px; margin-bottom: 14px; }
.survey__opt {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 13px 16px;
  border: 1px solid var(--border);
  border-radius: 10px;
  cursor: pointer;
  transition: border-color 0.15s, background 0.15s;
}
.survey__opt:hover { border-color: #3a4152; background: var(--bg-elev-2); }
.survey__opt input { accent-color: var(--grupo-pos); width: 17px; height: 17px; flex-shrink: 0; }
.survey__opt span { font-size: 0.98rem; }
.survey__opt:focus-within { border-color: var(--grupo-pos); box-shadow: 0 0 0 3px #3b82f61f; }
.survey textarea {
  width: 100%;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 10px;
  color: var(--text);
  font-family: var(--sans);
  font-size: 0.95rem;
  padding: 12px 14px;
  resize: vertical;
  min-height: 64px;
  outline: none;
  margin-bottom: 14px;
}
.survey textarea:focus { border-color: #3a4152; box-shadow: 0 0 0 3px #3b82f61f; }
.btn {
  font-family: var(--mono);
  font-size: 0.72rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  background: var(--text);
  color: var(--bg);
  border: none;
  border-radius: 999px;
  padding: 12px 22px;
  cursor: pointer;
  transition: opacity 0.15s;
}
.btn:hover { opacity: 0.88; }
.btn:disabled { opacity: 0.4; cursor: not-allowed; }
.btn--ghost {
  background: transparent;
  color: var(--text-dim);
  border: 1px solid var(--border);
}
.btn--ghost:hover { border-color: #3a4152; color: var(--text); opacity: 1; }
.btn:focus-visible { outline: 2px solid var(--grupo-pos); outline-offset: 2px; }
.survey__thanks { display: none; }
.survey__thanks.is-visible { display: block; }
.survey__thanks p { margin: 0 0 16px; color: var(--text-dim); }
.survey__thanks .choice-echo { color: var(--text); font-weight: 500; }
.survey__actions { display: flex; gap: 10px; flex-wrap: wrap; align-items: center; }
.copy-status {
  font-family: var(--mono);
  font-size: 0.66rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--grupo-pos);
  opacity: 0;
  transition: opacity 0.2s;
}
.copy-status.is-visible { opacity: 1; }

/* visualmente escondido, acessível a leitores de tela */
.visually-hidden {
  position: absolute; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden;
  clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

@media (min-width: 600px) {
  :root { --maxw: 760px; }
  .home-hero { padding: 28px 0 4px; }
}

@media (prefers-reduced-motion: reduce) {
  * { transition: none !important; animation: none !important; }

  /* [v1.9.26, Entrega 4] O disclosure "APROFUNDAR" cai para troca de
     estado instantânea, e é o comportamento CERTO, não uma degradação: a
     regra genérica acima já mata as três transições (linha de grid,
     transform, opacidade), e o estado final de cada uma é o correto em
     aberto e em fechado. As duas declarações abaixo são explícitas de
     propósito — a de `transform` porque um `translateY(-10px)` congelado
     seria um texto deslocado para sempre se a regra genérica algum dia
     mudar, e a de `visibility` porque o atraso de 0.32s que segura o
     fechamento não tem mais nada a segurar sem animação. */
  .theme__example.is-open .theme__example-inner { transform: none; }
  .theme__example-clip { transition-delay: 0s; }
}

/* Área de toque: onde não há hover (toque), o controle sobe para 44px, o
   mínimo confortável. No desktop 40px já basta e mantém o ritmo vertical
   entre os bullets (`.themes` tem `gap: 20px`). */
@media (hover: none) {
  .theme__toggle { min-height: 44px; padding: 11px 2px 11px 0; }
}

/* ===================================================================
   v1.9.14 — EIXO A EIXO: REMOVIDA na v1.9.19 (Entrega 5, decisão de
   produto — feedback de uso: a tabela de 3 colunas "não funciona na
   prática"). Removida mesmo, não escondida atrás de flag: as classes
   `.eixos*` que existiam aqui (grade de cabeçalho, linha por eixo, célula,
   selo, nota de denominador) saíram do CSS junto com o JS que as usava.
   O DADO que a tabela lia (bloco `eixos` do JSON) não muda em nada — ele
   agora alimenta `.verdict` (abaixo) e a ordem dos `.theme` dentro de
   `.group`. Se a tabela precisar voltar, ver o histórico do git.
   =================================================================== */


/* =======================================================================
   [v1.9.45] AS DUAS COLUNAS CABEM NO CELULAR — Tentativa A.

   O PROBLEMA, em uma frase: empilhado, o mobile mostra "uma coisa por
   vez". Quem chega na página sem ter visto o filme precisa comparar o que
   os dois lados dizem SOBRE O MESMO TEMA, e a comparação é justamente o
   que o empilhamento destrói — o segundo grupo só existe depois de duas
   telas e meia de rolagem, e a essa altura o primeiro já saiu da memória.

   O QUE MUDA: abaixo de 640px o `.sentiment-groups` deixa de ser uma
   coluna e passa a ser DUAS, as mesmas do desktop. A regra de ≥720px
   acima fica exatamente como estava — o desktop não é tocado por nada
   deste bloco —, e a faixa 641-719px (tablet em retrato estreito)
   continua empilhando como sempre: nada entre 641 e 719 muda.

   POR QUE ISSO ENCOLHE, e por quanto. Metade da largura não é metade da
   altura: o texto que quebrava em uma linha passa a quebrar em duas, e a
   altura de TEXTO é quase conservada quando a coluna é dividida ao meio.
   O que a segunda coluna economiza de verdade é a altura FIXA de cada
   bullet — o alvo de toque de 44px do "APROFUNDAR", os 6px da barra com
   as margens dela, o `gap` entre bullets, o cabeçalho do grupo —, que
   antes era paga DUAS vezes (uma por grupo empilhado) e agora é paga uma.
   São ~530px por filme só disso. O resto da economia vem da densidade
   ajustada aqui embaixo, e está medida no relatório da sessão.

   NEUTRALIDADE ESTRUTURAL (§0) — o que este bloco NÃO faz. Nenhuma regra
   daqui olha para `[data-group]`: as duas colunas são `1fr 1fr` (largura
   idêntica, não "a maior fica maior"), a tipografia é a mesma nos dois, as
   margens são as mesmas, e a contagem de bullets não é tocada por CSS
   nenhum. A única coisa que distingue um grupo do outro continua sendo a
   POSIÇÃO, decidida por `ordenarPorPeso` a partir do `share_real` — no
   desktop a coluna da esquerda, aqui também. Ver `filme.js`.

   NENHUM NÚMERO É REFORMATADO. `~5% das notas`, `★ 0,5–2` e a janela da
   amostra são strings prontas, montadas no JS a partir do dado; este
   bloco só decide onde elas quebram de linha e em que corpo aparecem.
   Nada aqui abrevia, arredonda ou recalcula algarismo.

   O TERCEIRO GRUPO (`--3`, quando o meio é dominante: `napoleon-2023` e
   `friday-the-13th-2009`). Três colunas em 375px dariam ~100px cada —
   um nome de tema de 31 caracteres quebraria em cinco linhas. Aqui o
   `--3` usa a MESMA grade de duas colunas, e o terceiro grupo desce para
   a segunda linha da grade, na mesma largura e com o mesmo leiaute dos
   outros dois. Quem desce é o TERCEIRO POR PESO, porque a ordem do DOM já
   vem ordenada por `share_real` — de novo, só a posição varia.

   O MEIO RECOLHIDO herda a densidade, de propósito. As regras de
   tipografia aqui embaixo casam `.group`/`.theme*` sem exigir
   `.sentiment-groups` como ancestral, então o `groupBlock` que vive dentro
   do `<details>` do meio (`.disclosure--meio`) recebe o mesmo corpo, o
   mesmo `gap` e as mesmas margens dos dois em destaque — ele só continua
   em largura cheia, como sempre esteve. Alternativa era escopar tudo na
   grade e deixar o meio com a tipografia do desktop: seria o MESMO
   componente com dois tamanhos na mesma tela. Só a GRADE é escopada. */
@media (max-width: 640px) {
  .sentiment-groups.sentiment-groups--2,
  .sentiment-groups.sentiment-groups--3 {
    grid-template-columns: 1fr 1fr;
  }
  /* Coluna estreita é uma caixa de conteúdo mínimo `auto` por padrão, e
     uma palavra longa ("Descaracterização") empurraria a grade para fora
     da tela. `min-width: 0` no item é o que impede o transbordo — mesma
     defesa que `.decide__col` já usa. */
  .sentiment-groups > .group { min-width: 0; }
  /* Coluna: 16px. Linha: 30px, e ela só existe no caso `--3`, para separar
     o grupo que desceu sem que ele pareça um rodapé dos de cima. */
  .sentiment-groups { gap: 30px 16px; }

  /* CABEÇALHO. `HATERS`/`FANS` + a faixa de estrelas cabem na primeira
     linha; o `~X% das notas` desce para a segunda. O `margin-left: auto`
     que empurrava o percentual para a direita sai: numa coluna de ~160px
     ele só criaria um vão, e alinhado à esquerda o número fica sob o
     rótulo do próprio grupo, que é onde se lê. Sai nos DOIS grupos. */
  .group__header { gap: 5px 8px; margin-bottom: 3px; }
  .group__share { margin-left: 0; }

  /* BULLETS. O `gap` cai de 20 para 10px porque o "APROFUNDAR" já carrega
     11px de respiro próprio em cima e embaixo (o padding do alvo de
     toque): 20px em cima disso era espaço dobrado, não ritmo. */
  .themes { margin-top: 12px; gap: 10px; }
  .theme__name {
    font-size: 0.875rem;   /* 14px — acima do piso de 13px do projeto */
    line-height: 1.3;
    overflow-wrap: break-word;
  }
  .theme__bar { margin: 6px 0 0; }
  .theme__toggle { margin-top: 0; }
  /* A paráfrase expandida NÃO encolhe. Ela vive numa linha de grade `0fr`
     enquanto está fechada, então o corpo dela não entra na altura do bloco
     — encolher aqui não devolveria um pixel e só pioraria a leitura de um
     texto que já cai para ~22 caracteres por linha na coluna estreita. Só
     o filete recua, para acompanhar a coluna. */
  .theme__example-inner { padding-left: 10px; }

  .mode-warning {
    font-size: 0.8rem;
    line-height: 1.45;
    padding: 9px 10px;
    gap: 7px;
    margin: 10px 0 4px;
  }
  .mode-warning__icon svg { width: 15px; height: 15px; }
}

/* =======================================================================
   [v1.9.37] CONDIÇÕES DE DECISÃO — o bloco que RECOMENDA (§0, terceira
   exceção deliberada). [v1.9.44] Entrava entre a BARRA e os BULLETS; agora
   entra entre os BULLETS e o VEREDITO — troca de posição decidida pelo
   dono, ver o comentário de `render()` em filme.js. O `margin-top` abaixo
   passou a usar o mesmo respiro que o `.verdict` já usava para "logo após
   os bullets" (era 34px, herdado da vizinhança com a barra).

   Cada escolha faz um trabalho, e nenhuma família/cor nova entra:

   · SERIFA no texto da condição — é a voz EDITORIAL do produto, a mesma do
     veredito e da narrativa. A condição é prosa do produto, não metadado;
   · MONO no cabeçalho, no peso e na proveniência — é o registro do SISTEMA
     falando (`.section-label`, `.proportion__note`, o disclosure APROFUNDAR
     usam o mesmo), e é o que separa visualmente "o que o código decidiu" de
     "o que o produto afirma";
   · a cor de cada coluna é a do GRUPO de origem (--pos azul / --neg
     âmbar-avermelhado), a paleta oficial dos três grupos. NUNCA verde e
     vermelho: a página inteira evita esse par, e "vale a pena / talvez
     evite" seria exatamente onde a leitura semafórica tentaria entrar;
   · a barra de 2px à esquerda marca a coluna sem caixa nem fundo, no mesmo
     vocabulário discreto de `.verdict-absent`.

   AS DUAS COLUNAS TÊM O MESMO PESO VISUAL, por decisão registrada: largura
   igual (`1fr 1fr`), mesma tipografia, mesmo espaçamento. `the-godfather`
   publica três condições de quem não recomenda com `~2% das notas` ao lado —
   o peso é dito pelo NÚMERO, nunca por encolher a coluna. Encolher
   reintroduziria a infidelidade por omissão que a v1.4.0 corrigiu. */
.decide {
  margin: 40px 0 8px;
}

.decide__meio {
  font-family: var(--mono);
  font-size: 0.76rem;
  line-height: 1.5;
  color: var(--text-mute);
  margin: 0 0 18px;
}

.decide__grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 26px;
}

.decide__col {
  padding-left: 14px;
  border-left: 2px solid var(--border);
  min-width: 0;            /* impede transbordo horizontal no grid */
}
.decide__col--pos { border-left-color: var(--grupo-pos); }
.decide__col--neg { border-left-color: var(--grupo-neg); }

.decide__head {
  font-family: var(--mono);
  font-size: 0.74rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  margin: 0 0 4px;
}
.decide__col--pos .decide__head { color: var(--grupo-pos); }
.decide__col--neg .decide__head { color: var(--grupo-neg); }

.decide__peso {
  font-family: var(--mono);
  font-size: 0.72rem;
  color: var(--text-mute);
  margin: 0 0 14px;
}

.decide__list {
  list-style: none;
  margin: 0;
  padding: 0;
}

.decide__item { margin: 0 0 15px; }
.decide__item:last-child { margin-bottom: 0; }

.decide__text {
  font-family: var(--serif);
  font-size: 1.02rem;
  line-height: 1.45;
  color: var(--text);
  margin: 0;
  overflow-wrap: break-word;
}

/* [v1.9.46] `.decide__meta` (o rótulo de força + tema de origem sob cada
   condição) foi removida daqui — o JS parou de gerar o elemento, decisão do
   dono. Ver o comentário de `condicoesBlock` em filme.js. */

@media (max-width: 640px) {
  /* Empilha. A coluna de MAIOR peso vem primeiro no DOM (ordenada em
     Python), então empilhar preserva a ordem por peso da v1.9.30 sem
     nenhuma regra de ordem no CSS. */
  .decide__grid { grid-template-columns: 1fr; gap: 22px; }
  .decide__text { font-size: 0.99rem; }
}

/* =======================================================
   HOME EDITORIAL — estrutura (rodada editorial)
   =======================================================
   Rodada de ESTRUTURA: fontes e cores são as atuais (tipografia e paleta
   são a próxima rodada). Regras de forma, todas aqui: só stills/backdrops
   16:9 (nunca pôster), cantos RETOS nas imagens, calha fina entre imagens
   (`--ed-calha`) e nenhuma sombra. A home vive em `ed-*`; `.busca*` é a
   lista de resultados da busca, que veio da home nova. */
/* [rodada editorial 2] PROVA DE FONTES: `--ed-fonte` é a variável ÚNICA
   da família dos títulos de coleção, títulos em texto, metadados e rótulos
   de seção da home. Sem ela (`?fonte=atual`, o padrão), cada elemento cai
   na família de sempre (`var(--ed-fonte, var(--serif))` etc.). `js/fonte.js`
   marca `<html data-fonte>` e carrega só a família pedida. */
:root[data-fonte="inter-tight"] { --ed-fonte: "Inter Tight", var(--sans); }
:root[data-fonte="instrument"] { --ed-fonte: "Instrument Sans", var(--sans); }

:root {
  /* [rodada editorial 2] o VÉU atrás do bloco de texto da manchete (canto
     inferior esquerdo do still): opacidade do preto, MEDIDA para o
     contraste WCAG >= 4,5 da justificativa sobre o pior still dos 98
     elegíveis (scripts/medir_legibilidade.py, dados/legibilidade_manchete.json).
     Constante ÚNICA. */
  --ed-veu: 0.53;
  --ed-calha: 4px;
  --ed-largura: 1180px;
}

/* a lista de resultados abre SOBRE o conteúdo, colada no campo */
.busca { margin-bottom: 0; }
.busca__resultados {
  position: absolute;
  left: 0; right: 0; top: calc(100% + 6px);
  z-index: 20;
  list-style: none;
  margin: 0;
  padding: 6px;
  max-height: min(60vh, 26rem);
  overflow-y: auto;
  background: var(--bg-elev);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: 0 14px 30px -12px #000000cc;
  text-align: left;
}
.busca__opcao { border-radius: 8px; }
.busca__opcao.is-ativo { background: var(--bg-elev-2); }
.busca__link {
  display: flex;
  flex-direction: column;
  padding: 8px 12px;
  text-decoration: none;
  color: var(--text);
}
.busca__titulo { font-family: var(--serif); font-weight: 700; line-height: 1.3; }
.busca__meta {
  font-family: var(--mono);
  font-size: 0.68rem;
  letter-spacing: 0.08em;
  color: var(--text-mute);
}
.busca__vazia {
  margin: 12px auto 0;
  max-width: 26rem;
  color: var(--text-dim);
  font-size: 0.92rem;
}
.busca__vazia a { color: var(--text); text-underline-offset: 3px; }

.ed-largura {
  max-width: var(--ed-largura);
  margin-left: auto;
  margin-right: auto;
  padding: 0 20px;
}

/* --- cabeçalho: nome, busca, Filmes e Método --- */
.ed-cabecalho { border-bottom: 1px solid var(--border-soft); }
.ed-cabecalho__linha {
  max-width: var(--ed-largura);
  margin: 0 auto;
  padding: 12px 20px;
  display: flex;
  align-items: center;
  gap: 20px;
}
.ed-cabecalho__nome {
  font-family: var(--serif);
  font-weight: 700;
  font-size: 1.25rem;
  text-decoration: none;
  white-space: nowrap;
}
.ed-cabecalho__busca { flex: 1; margin: 0; max-width: 26rem; }
.ed-cabecalho__busca input { padding-top: 9px; padding-bottom: 9px; font-size: 0.95rem; }
.ed-cabecalho__nav { display: flex; gap: 18px; margin-left: auto; }
.ed-cabecalho__nav a {
  font-family: var(--mono);
  font-size: 0.72rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--text-dim);
  text-decoration: none;
}
.ed-cabecalho__nav a:hover { color: var(--text); }

/* --- quadros: 16:9, cantos retos, sem sombra --- */
.ed-quadro {
  display: block;
  position: relative;
  aspect-ratio: 16 / 9;
  overflow: hidden;
  background: var(--bg-elev);
}
.ed-quadro__img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  border-radius: 0;
}

/* --- 1. manchete --- */
.ed-manchete { position: relative; }
.ed-manchete__visual { display: block; position: relative; }
.ed-faixa { display: flex; height: 5px; width: 100%; background: transparent; }
.ed-faixa.is-vazia { background: var(--border); }
.ed-faixa__seg { height: 100%; }
.ed-manchete__faixa { position: absolute; left: 0; right: 0; bottom: 0; }
/* alinhado à esquerda da coluna do resto da página (`--ed-largura`) */
.ed-manchete__texto {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 5px;            /* acima da faixa dos grupos */
  max-width: var(--ed-largura);
  margin: 0 auto;
  padding: 0 20px 28px;
}
/* a caixa do véu: só atrás do texto, cantos retos, sem sombra; o recuo
   negativo mantém o texto alinhado à coluna */
.ed-manchete__caixa {
  display: inline-block;
  max-width: calc(36rem + 40px);
  margin-left: -20px;
  padding: 18px 20px 16px;
  background: rgba(0, 0, 0, var(--ed-veu));
}
.ed-manchete__titulo { margin: 0 0 10px; line-height: 1; }
.ed-manchete__titulo a { text-decoration: none; display: inline-block; }
.ed-manchete__logo {
  display: block;
  max-width: min(420px, 100%);
  max-height: 150px;
  width: auto;
  height: auto;
}
.ed-manchete__logo.is-branco { filter: brightness(0) invert(1); }
.ed-manchete__titulo-texto {
  font-family: var(--ed-fonte, var(--serif));
  font-size: clamp(2rem, 4.4vw, 3.6rem);
  font-weight: 700;
  line-height: 1.05;
}
.ed-manchete__nativo { margin: 0 0 4px; color: var(--text-dim); }
.ed-manchete__linha {
  margin: 0 0 8px;
  font-family: var(--ed-fonte, var(--mono));
  font-size: 0.74rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text);
}
.ed-manchete__justificativa { margin: 0; max-width: 36rem; color: var(--text); line-height: 1.5; }

/* --- cartões (coleção e recém-chegados): título e meta ABAIXO do still --- */
.ed-cartao { display: block; text-decoration: none; color: var(--text); }
.ed-cartao__corpo { display: flex; flex-direction: column; padding: 8px 0 0; }
.ed-cartao__titulo { font-family: var(--ed-fonte, var(--serif)); font-weight: 700; line-height: 1.25; }
.ed-cartao:hover .ed-cartao__titulo { text-decoration: underline; text-underline-offset: 3px; }
.ed-cartao__nativo { font-size: 0.8rem; color: var(--text-mute); }
.ed-cartao__meta {
  font-family: var(--ed-fonte, var(--mono));
  font-size: 0.68rem;
  letter-spacing: 0.08em;
  color: var(--text-mute);
  margin-top: 2px;
}
.ed-link { color: var(--text-dim); text-decoration: none; }
.ed-link:hover { color: var(--text); }

/* --- 2. coleção do dia: texto 1/3 ao lado do still grande 2/3 --- */
.ed-colecao { margin-top: 56px; }
.ed-colecao__topo {
  display: grid;
  grid-template-columns: 1fr 2fr;
  gap: 32px;
  align-items: start;
}
.ed-colecao__rotulo { margin-bottom: 10px; }
.ed-colecao__titulo {
  font-family: var(--ed-fonte, var(--serif));
  font-size: clamp(1.5rem, 2.6vw, 2.1rem);
  line-height: 1.15;
  margin: 0 0 14px;
}
.ed-colecao__descricao,
.ed-colecao__frase { color: var(--text-dim); margin: 0 0 12px; line-height: 1.55; }
.ed-colecao__fileira {
  list-style: none;
  margin: 28px 0 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: var(--ed-calha);
}
.ed-colecao__fileira .ed-cartao__corpo { padding-right: 8px; }

/* --- 3. recém-chegados: grade de 3 --- */
.ed-recem { margin-top: 56px; }
.ed-recem__cabeca {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 12px;
}
.ed-recem__titulo { margin: 0; }
.ed-recem__grade {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--ed-calha);
}
.ed-recem__grade .ed-cartao__corpo { padding-right: 8px; }

/* --- 4. Explorar: só texto, quatro grupos lado a lado (desktop) --- */
.ed-explorar { margin-top: 56px; margin-bottom: 8px; }
.ed-explorar__grupos {
  display: grid;
  grid-template-columns: 1.6fr 1fr 1fr 1fr;
  gap: 32px;
}
.ed-explorar__nome {
  list-style: none;
  font-family: var(--ed-fonte, var(--mono));
  font-size: 0.72rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--text-mute);
  margin-bottom: 10px;
}
.ed-explorar__nome::-webkit-details-marker { display: none; }
.ed-explorar.is-colunas .ed-explorar__nome { cursor: default; }
.ed-explorar.is-colunas .ed-explorar__quantos { display: none; }
.ed-explorar__lista { list-style: none; margin: 0; padding: 0; }
.ed-explorar__lista li { margin: 0 0 6px; line-height: 1.35; }
.ed-explorar__item {
  font-family: var(--ed-fonte, var(--serif));
  color: var(--text);
  text-decoration: none;
}
.ed-explorar__item:hover { text-decoration: underline; text-underline-offset: 3px; }
.ed-explorar__n { color: var(--text-mute); font-family: var(--sans); font-size: 0.85em; }
.ed-explorar__indice { margin: 24px 0 0; }

/* =======================================================
   /filmes — EXPLORAR (rodada Explorar)
   =======================================================
   Grade de stills 16:9 (nunca pôster), 3 colunas, título e "Diretor · Ano"
   abaixo — o cartão dos recém-chegados da home (`.ed-cartao`). */
.ex { margin-top: 28px; }
.ex-topo { display: flex; align-items: baseline; justify-content: space-between; gap: 16px; }
.ex-titulo { font-family: var(--ed-fonte, var(--serif)); font-size: clamp(1.6rem, 3vw, 2.2rem); margin: 0 0 16px; }
.ex-vistas { display: flex; gap: 14px; }
.ex-vista {
  font-family: var(--ed-fonte, var(--mono));
  font-size: 0.72rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--text-mute);
  text-decoration: none;
  padding-bottom: 2px;
  border-bottom: 1px solid transparent;
}
.ex-vista.is-ativa { color: var(--text); border-bottom-color: var(--text); }
.ex-filtros { display: grid; grid-template-columns: repeat(4, 1fr); gap: 12px; }
.ex-filtro { display: flex; flex-direction: column; gap: 4px; }
.ex-filtro__nome {
  font-family: var(--ed-fonte, var(--mono));
  font-size: 0.66rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--text-mute);
}
.ex-filtro__select {
  width: 100%;
  background: var(--bg-elev);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: 0;
  font: inherit;
  font-size: 0.92rem;
  padding: 8px 10px;
}
.ex-chips { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin: 14px 0 0; }
.ex-chips[hidden] { display: none; }
.ex-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  border: 1px solid var(--border);
  padding: 3px 4px 3px 10px;
  font-size: 0.85rem;
}
.ex-chip__remover {
  background: none;
  border: 0;
  color: var(--text-dim);
  font-size: 1.05rem;
  line-height: 1;
  padding: 2px 6px;
  cursor: pointer;
}
.ex-chip__remover:hover { color: var(--text); }
.ex-limpar { color: var(--text-dim); font-size: 0.85rem; text-underline-offset: 3px; }
.ex-contador {
  margin: 18px 0 10px;
  font-family: var(--mono);
  font-size: 0.7rem;
  letter-spacing: 0.1em;
  color: var(--text-mute);
}
.ex-grade {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 24px var(--ed-calha);
}
.ex-grade[hidden], .ex-indice[hidden], .ex-vazio[hidden], .ex-mais[hidden] { display: none; }
.ex-grade .ed-cartao__corpo { padding-right: 8px; }
.ex-vazio { margin: 24px 0; color: var(--text-dim); }
.ex-vazio a { color: var(--text); }
.ex-mais {
  display: block;
  margin: 28px auto 0;
  background: none;
  border: 1px solid var(--border);
  color: var(--text);
  font: inherit;
  padding: 10px 22px;
  cursor: pointer;
}
.ex-mais:hover { border-color: var(--text-dim); }
.ex-indice__grupo { margin: 0 0 22px; }
.ex-indice__letra {
  font-family: var(--ed-fonte, var(--serif));
  font-size: 1.4rem;
  margin: 0 0 6px;
  border-bottom: 1px solid var(--border-soft);
}
.ex-indice__lista { list-style: none; margin: 0; padding: 0; columns: 2; column-gap: 40px; }
.ex-indice__linha { break-inside: avoid; margin: 0 0 6px; line-height: 1.35; }
.ex-indice__titulo { color: var(--text); text-decoration: none; font-family: var(--ed-fonte, var(--serif)); }
.ex-indice__titulo:hover { text-decoration: underline; text-underline-offset: 3px; }
.ex-indice__meta { color: var(--text-mute); font-size: 0.82rem; margin-left: 8px; }

/* rótulos de seção da home ("Coleção do dia", "Recém-chegados") */
.ed-main .section-label { font-family: var(--ed-fonte, var(--mono)); }

/* --- prova de fontes: o tratamento das famílias novas --- */
:root[data-fonte]:not([data-fonte="atual"]) .ed-colecao__titulo,
:root[data-fonte]:not([data-fonte="atual"]) .ed-manchete__titulo-texto,
:root[data-fonte]:not([data-fonte="atual"]) .ed-cartao__titulo {
  text-transform: uppercase;
  font-weight: 600;
  letter-spacing: normal;
}
:root[data-fonte]:not([data-fonte="atual"]) .ed-main .section-label {
  text-transform: uppercase;
  font-weight: 600;
  font-size: 0.78rem;
  letter-spacing: normal;
  color: var(--text-dim);
}
/* /filmes: a mesma família e o mesmo tratamento */
:root[data-fonte]:not([data-fonte="atual"]) .ex-titulo,
:root[data-fonte]:not([data-fonte="atual"]) .ex-indice__letra,
:root[data-fonte]:not([data-fonte="atual"]) .ex-filtro__nome,
:root[data-fonte]:not([data-fonte="atual"]) .ex-vista {
  text-transform: uppercase;
  font-weight: 600;
  letter-spacing: normal;
}
/* metadado no formato MUBI: diretor em negrito, país e ano no peso normal,
   em caixa alta pequena */
:root[data-fonte]:not([data-fonte="atual"]) .ed-meta {
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: normal;
  font-weight: 400;
}
:root[data-fonte]:not([data-fonte="atual"]) .ed-meta__diretor { font-weight: 700; }
:root[data-fonte]:not([data-fonte="atual"]) .ed-cartao__meta { color: var(--text-dim); }

/* --- mobile (< 700px): still em cima, texto embaixo sobre fundo sólido --- */
@media (max-width: 699px) {
  .ed-cabecalho__linha { flex-wrap: wrap; gap: 10px 16px; padding: 10px 16px; }
  .ed-cabecalho__busca { order: 3; flex-basis: 100%; max-width: none; }
  .ed-largura { padding: 0 16px; }

  .ed-manchete__caixa {
    display: block;
    max-width: none;
    margin: 0;
    padding: 0;
    background: none;
  }
  .ed-manchete__texto {
    position: static;
    max-width: none;
    padding: 16px 16px 4px;
    background: var(--bg);
  }
  .ed-manchete__logo { max-width: min(280px, 100%); max-height: 110px; }
  .ed-manchete__titulo-texto { font-size: 2rem; }

  .ed-colecao { margin-top: 40px; }
  .ed-colecao__topo { grid-template-columns: 1fr; gap: 16px; }
  .ed-colecao__fileira { grid-template-columns: 1fr; gap: 12px; margin-top: 20px; }
  .ed-colecao__fileira .ed-cartao {
    display: grid;
    grid-template-columns: 40% 1fr;
    gap: 12px;
    align-items: center;
  }
  .ed-colecao__fileira .ed-cartao__corpo { padding: 0; }

  .ed-explorar__grupos { grid-template-columns: 1fr; gap: 0; }
  .ed-explorar__grupo { border-top: 1px solid var(--border-soft); }
  .ed-explorar__grupo:last-child { border-bottom: 1px solid var(--border-soft); }
  .ed-explorar__nome { cursor: pointer; margin: 0; padding: 14px 0; }
  .ed-explorar__nome::after { content: "+"; float: right; }
  .ed-explorar__grupo[open] .ed-explorar__nome::after { content: "–"; }
  .ed-explorar__lista { padding: 0 0 14px; }

  .ex-topo { flex-direction: column; gap: 0; }
  .ex-vistas { margin-bottom: 14px; }
  .ex-filtros { grid-template-columns: 1fr 1fr; }
  .ex-grade { grid-template-columns: 1fr; gap: 20px; }
  .ex-indice__lista { columns: 1; }

  .ed-recem { margin-top: 40px; }
  .ed-recem__cabeca { flex-direction: column; align-items: flex-start; gap: 2px; }
  .ed-recem__grade { grid-template-columns: 1fr; gap: 16px; }
}


/* =======================================================================
   PÁGINA DO FILME — linguagem editorial (rodada página do filme)
   =======================================================================
   Escopo: `body.pagina-filme`. Instrument Sans (`--ed-fonte`, de fonte.js),
   rótulos de seção como os da home, cantos retos, nenhuma sombra. As cores
   fortes dos grupos (--neg/--med/--pos) continuam nesta rodada: cor é a
   próxima etapa. A barra RECEPÇÃO (geometria, callout, animação) não é
   tocada; só os rótulos em volta dela mudam de tipografia. */
/* TRÊS LARGURAS: imagens, dados e prosa. Um eixo só (tudo centralizado). */
:root {
  --larg-imagem: 1180px;   /* topo, galeria, na mesma coleção */
  --larg-dados: 960px;     /* RECEPÇÃO, o que cada grupo diz, para decidir */
  --larg-prosa: 68ch;      /* veredito e narrativa, na fonte de cada bloco */
}
.pagina-filme { font-family: var(--ed-fonte, var(--sans)); }
.pagina-filme .fp { max-width: var(--larg-imagem); }
/* UMA COLUNA de leitura (`--larg-dados`), centralizada na página; todo bloco
   que não é imagem começa na MESMA margem esquerda dela. `--col-esq` é a
   margem que centraliza a coluna (% resolve contra o contêiner: o `.fp` no
   corpo, a janela no topo) e vale 0 quando o contêiner é mais estreito. A
   prosa só estreita o bloco (`--larg-prosa`) e continua alinhada à esquerda. */
.pagina-filme {
  --col-esq: max(0px, calc((100% - var(--larg-dados)) / 2));
}
.pagina-filme .verdict,
.pagina-filme .verdict-absent,
.pagina-filme .proportion,
.pagina-filme .grupos,
.pagina-filme .decide,
.pagina-filme .disclosure--narrativa {
  max-width: var(--larg-dados);
  margin-left: var(--col-esq);
  margin-right: 0;
}
.pagina-filme .verdict,
.pagina-filme .verdict-absent,
.pagina-filme .disclosure--narrativa { max-width: var(--larg-prosa); }
.pagina-filme .fp { padding-bottom: 8px; }

/* rótulos de seção: os da home */
.pagina-filme .section-label {
  font-family: var(--ed-fonte, var(--mono));
  text-transform: uppercase;
  font-weight: 600;
  font-size: 0.78rem;
  letter-spacing: normal;
  color: var(--text-dim);
}
.pagina-filme .fp-secao__cabeca {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 12px;
}
.pagina-filme .fp-secao__cabeca .section-label { margin: 0; }
.pagina-filme .fp-secao__cabeca .ed-link { white-space: nowrap; }
.pagina-filme .ed-link { font-family: var(--ed-fonte, var(--mono)); font-size: 0.78rem; background: none; border: 0; padding: 0; cursor: pointer; }
.pagina-filme .ed-link.mono { font-family: var(--ed-fonte, var(--mono)); }

/* --- 1. topo ---------------------------------------------------------- */
.fp-topo { position: relative; }
/* a faixa: cantos retos, sem sombra, e SEM o fade da base — o texto não sobe
   mais sobre a imagem (fica abaixo, em fundo sólido), então o degradê só
   escureceria a metade de baixo dos stills */
.pagina-filme .backdrop::after { content: none; }
.pagina-filme .hero-faixa-caixa,
.pagina-filme .hero-faixa .hero-still,
.pagina-filme .ed-quadro,
.pagina-filme .fp-galeria__item,
.pagina-filme .fp-galeria .hero-still { border-radius: 0; }
/* faixa e still estático: o texto vai ABAIXO, em fundo sólido */
.fp-topo--faixa .ed-manchete__texto,
.fp-topo--solido .ed-manchete__texto {
  position: static;
  /* o bloco do logo e da linha começa na MESMA margem da coluna de 960px
     (`--col-esq`, com piso de 20px de gutter: o topo não está no `.fp`) */
  max-width: var(--larg-dados);
  margin: 0 0 0 max(20px, var(--col-esq));
  padding: 22px 20px 0 0;
  background: var(--bg);
}
/* `?topo=estatico`: o bloco sobre o still alinha pela mesma margem; a caixa
   do véu invade 20px à esquerda (`margin-left: -20px`), como na home */
.fp-topo--manchete .ed-manchete__texto {
  max-width: var(--larg-dados);
  margin: 0 0 0 max(20px, var(--col-esq));
  padding: 0 20px 28px 0;
}
.fp-topo--faixa .ed-manchete__caixa,
.fp-topo--solido .ed-manchete__caixa {
  display: block;
  max-width: none;
  margin: 0;
  padding: 0;
  background: none;
}
.fp-topo .ed-manchete__titulo { margin: 0 0 8px; }
/* `?topo=estatico` no desktop: a caixa do véu é a da manchete da home */
.pagina-filme .ed-manchete__titulo-texto { margin: 0; }
.pagina-filme .ed-manchete__linha { margin: 0 0 4px; }
@media (max-width: 699px) {
  .fp-topo--faixa .ed-manchete__texto,
  .fp-topo--solido .ed-manchete__texto { margin-left: 16px; padding: 16px 16px 0 0; }
}

/* --- 2. veredito: linha fina, em corpo maior que o texto comum ---------- */
.pagina-filme .verdict,
.pagina-filme .verdict-absent {
  font-family: var(--ed-fonte, var(--sans));
  font-size: 1.28rem;
  font-weight: 400;
  line-height: 1.45;
  color: var(--text);
  max-width: var(--larg-prosa);
  margin-top: 26px;
  padding: 16px 0;
  border: 0;
  border-top: 1px solid var(--border);
  border-bottom: 1px solid var(--border);
}
.pagina-filme .verdict-absent { color: var(--text-dim); }

/* --- 3. RECEPÇÃO ---------------------------------------------------------- */
.pagina-filme .proportion { margin-top: 36px; }
.pagina-filme .proportion__legend { font-family: var(--ed-fonte, var(--sans)); }
.pagina-filme .proportion__legend-label {
  font-family: var(--ed-fonte, var(--sans));
  font-weight: 600;
  letter-spacing: normal;
}
.pagina-filme .proportion__legend-notas {
  font-family: var(--ed-fonte, var(--sans));
  font-size: 0.78rem;
  color: var(--text-mute);
}
.pagina-filme .proportion__pct { font-family: var(--ed-fonte, var(--sans)); }
.pagina-filme .proportion__swatch { border-radius: 0; }

/* --- 4. o que cada grupo diz ---------------------------------------------- */
.pagina-filme .grupos { margin-top: 52px; }
.pagina-filme .sentiment-wrap { margin-top: 0; }
.pagina-filme .group__name {
  font-family: var(--ed-fonte, var(--sans));
  font-weight: 700;
  letter-spacing: 0.04em;
  font-size: 0.9rem;
}
.pagina-filme .group__dot { border-radius: 0; }
.pagina-filme .group__notas,
.pagina-filme .group__share {
  font-family: var(--ed-fonte, var(--sans));
  font-size: 0.78rem;
  letter-spacing: normal;
  text-transform: none;
}
.pagina-filme .group__share { color: var(--text-dim); }
.pagina-filme .theme__name { font-family: var(--ed-fonte, var(--sans)); }
.pagina-filme .theme__toggle-label,
.pagina-filme .group-note__label,
.pagina-filme .group-note__texto { font-family: var(--ed-fonte, var(--sans)); }
.pagina-filme .theme__toggle-label { letter-spacing: normal; text-transform: none; font-size: 0.82rem; }
.pagina-filme .theme__example-inner { font-family: var(--ed-fonte, var(--sans)); }
.pagina-filme .theme__bar,
.pagina-filme .theme__bar span { border-radius: 0; }
.pagina-filme .mode-warning { border-radius: 0; }

/* --- 5. para decidir -------------------------------------------------------- */
.pagina-filme .decide { margin-top: 56px; }
.pagina-filme .decide__head,
.pagina-filme .decide__peso,
.pagina-filme .decide__meio { font-family: var(--ed-fonte, var(--sans)); }
.pagina-filme .decide__head {
  font-size: 0.82rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: none;
}
.pagina-filme .decide__peso { font-size: 0.8rem; color: var(--text-dim); letter-spacing: normal; }
.pagina-filme .decide__text { font-family: var(--ed-fonte, var(--sans)); font-size: 1.04rem; }
.pagina-filme .decide__meio { font-size: 0.82rem; }

/* --- 6. narrativa (fechada) -------------------------------------------------- */
.pagina-filme .disclosure { margin-top: 52px; }
.pagina-filme .disclosure--narrativa {
  font-size: 1.06rem;
}
.pagina-filme .disclosure > summary {
  font-family: var(--ed-fonte, var(--sans));
  font-weight: 600;
  letter-spacing: normal;
  text-transform: uppercase;
  font-size: 0.78rem;
  border-radius: 0;
}
.pagina-filme .disclosure__body.narrativa p { font-size: 1em; }
.pagina-filme .disclosure__body.narrativa p { font-family: var(--ed-fonte, var(--sans)); }

/* --- 7. galeria: grade de 4 stills, em todas as larguras ----------------------- */
.fp-galeria { margin-top: 56px; }
.fp-galeria__grade {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: var(--ed-calha);
}
.fp-galeria__item {
  min-width: 0;
  display: block;
  width: 100%;
  padding: 0;
  border: 0;
  background: none;
  cursor: pointer;
}
.fp-galeria__item:focus-visible { outline: 2px solid var(--text); outline-offset: 2px; }
.fp-galeria .hero-still { display: block; width: 100%; position: relative; overflow: hidden; background: var(--bg-elev); }
.fp-galeria .hero-still img { display: block; width: 100%; height: 100%; object-fit: cover; }

/* --- 8. na mesma coleção --------------------------------------------------------- */
.fp-colecao { margin-top: 56px; }
.fp-colecao__grade {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--ed-calha);
}
.fp-colecao__grade .ed-cartao__corpo { padding-right: 8px; }

@media (max-width: 699px) {
  .fp-galeria__grade { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .fp-colecao__grade { grid-template-columns: 1fr; gap: 16px; }
  .pagina-filme .verdict,
  .pagina-filme .verdict-absent { font-size: 1.12rem; }
  .pagina-filme .grupos,
  .pagina-filme .decide,
  .pagina-filme .disclosure,
  .fp-galeria,
  .fp-colecao { margin-top: 40px; }
}


/* =======================================================================
   [rodada cor] PALETAS DOS GRUPOS — `?cor=atual|apagada|editorial`
   =======================================================================
   `js/cor.js` marca `<html data-cor>`; o padrão é a constante `COR_PADRAO`
   LÁ (uma linha). Os hex são os de `scripts/medir_cores.py` (PALETAS), que
   mede contraste, ΔE2000 (normal, deuteranopia, protanopia) e luminância;
   `tests/test_cores_grupos.py` confere que os dois concordam. Sem JS, vale
   a `atual`. O `-soft` é o mesmo tom a 20% (preenchimento do callout). */
:root,
:root[data-cor="atual"] {
  --grupo-neg: #e0673f;
  --grupo-med: #e3b23c;
  --grupo-pos: #4d90e5;
}
:root[data-cor="apagada"] {
  --grupo-neg: #9c5f4e;
  --grupo-med: #a8874f;
  --grupo-pos: #5b7a99;
}
:root[data-cor="editorial"] {
  --grupo-neg: #c1a85c;
  --grupo-med: #95c6ba;
  --grupo-pos: #7e97c8;
}
:root {
  --grupo-neg-soft: color-mix(in srgb, var(--grupo-neg) 20%, transparent);
  --grupo-med-soft: color-mix(in srgb, var(--grupo-med) 20%, transparent);
  --grupo-pos-soft: color-mix(in srgb, var(--grupo-pos) 20%, transparent);
}


/* =======================================================================
   [rodada destaque] `?pct=` — peso do grupo: frase e barra
   =======================================================================
   `frase`: "cerca de 1 em cada N" depois do "~X% das notas", no mesmo corpo
   e em cor secundária. `barra`: 48px à esquerda do número, trilho sutil
   (`--trilho-pct`, contraste >= 1,5 contra o fundo) e preenchimento do
   tamanho do share, na cor do grupo, cantos retos. */
:root { --trilho-pct: #3a3e48; }
.pagina-filme .peso-extra { color: var(--text-mute); }
.pagina-filme .group__share--barra,
.pagina-filme .decide__peso--barra {
  display: inline-flex;
  align-items: center;
  gap: 8px;
}
.peso-barra {
  display: inline-block;
  flex: none;
  width: 48px;
  height: 6px;
  background: var(--trilho-pct);
  border-radius: 0;
}
.peso-barra__fill {
  display: block;
  height: 100%;
  min-width: 1px;
  background: var(--text-dim);
  border-radius: 0;
}
.peso-barra[data-g="negativas"] .peso-barra__fill { background: var(--grupo-neg); }
.peso-barra[data-g="medianas"] .peso-barra__fill { background: var(--grupo-med); }
.peso-barra[data-g="positivas"] .peso-barra__fill { background: var(--grupo-pos); }

/* =======================================================================
   [rodada cabeçalho em 3 colunas] A linha do peso ("barra + ~X% das notas")
   é UMA peça: nunca quebra por dentro. Se não cabe ao lado de "NOME notas a
   a b", desce inteira para a segunda linha, alinhada à ESQUERDA; se cabe
   (duas colunas, ou o Mixed recolhido em largura cheia), fica na primeira,
   empurrada para a direita como sempre. Quem decide é a largura do próprio
   grupo (container query), a mesma regra em 2 e 3 colunas e no mobile. */
.pagina-filme .group { container-type: inline-size; }
.pagina-filme .group__share {
  margin-left: 0;
  white-space: nowrap;
}
.pagina-filme .group__share--barra { flex-wrap: nowrap; }
@container (min-width: 380px) {
  .pagina-filme .group__share { margin-left: auto; }
}
