/* CineMatch — a folha de estilos do app.
   Saiu de dentro do index.html na 1.48.0 (era o <style> das linhas 49–6338), sem mudar uma
   regra: o conteúdo abaixo é o mesmo, byte a byte, com as quebras de linha em LF. Os testes
   leem esta folha colada de volta no lugar do <link> (tests/fonte-do-app.mjs). */
  /* OS DOIS RECORTES SÃO OS DO GOOGLE, e a divisão é a mesma que ele publica: `latin`
     cobre o português inteiro (todo acento nosso vive em U+00C0–U+00FF) e `latin-ext` só
     desce quando um título estrangeiro do TMDB pede um glifo que o primeiro não tem.
     Quem lê em pt-BR baixa 13,8 KB e nunca vê o segundo arquivo.
     woff2 e não o .ttf: 22,7 KB somando os dois recortes contra 61 KB do arquivo bruto,
     pela mesma tipografia. Os dois entram no SHELL do service worker — a promessa aqui é
     justamente a primeira abertura offline, e ela é o único momento em que a fonte não
     estaria em cache. */
  @font-face{
    font-family:'Bebas Neue';
    font-style:normal;
    font-weight:400;
    /* `swap`: o cartaz chegar um quadro depois é melhor que texto invisível esperando */
    font-display:swap;
    src:url('assets/fonts/bebas-neue-latin.woff2') format('woff2');
    unicode-range:U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC,
      U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215,
      U+FEFF, U+FFFD;
  }
  @font-face{
    font-family:'Bebas Neue';
    font-style:normal;
    font-weight:400;
    font-display:swap;
    src:url('assets/fonts/bebas-neue-latin-ext.woff2') format('woff2');
    unicode-range:U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304,
      U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB,
      U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
  }
  :root{
    /* o par da meta `color-scheme` do topo: a meta chega antes de qualquer folha e cobre a
       primeira pintura; esta linha mantém a declaração viva no CSS, que é onde ela também
       governa os controles nativos (campo de texto, seleção, barra de rolagem) */
    /* o padrão; o tema claro troca para `light` no bloco logo abaixo. Sem isso, os
       aparelhos com escurecimento automático de conteúdo web aplicariam o filtro POR CIMA
       do tema claro — que é o mesmo defeito que a 1.30 consertou, ao contrário. */
    color-scheme: dark;
    /* ---- base ---- */
    --bg: #0a0a10;
  --bg-halo: #14151d;   /* o clarão do topo do fundo; no tema claro ele inverte */
    --bg-panel: #12131a;
    --card-top: #1c1e29;
    --card-bot: #08090d;
    --gold: #e8b54d;
  /* O OURO TEM DOIS PAPÉIS, e no tema claro eles se separam.
     `--gold` é o ouro VIVO: preenchimento de CTA, aresta, brilho. `--gold-ink` é a TINTA
     de todo texto dourado. Aqui os dois são iguais — no escuro, `#e8b54d` sobre o preto dá
     10,5 de contraste e escreve muito bem. No claro ele dá 1,6 sobre creme e SOME, então lá
     a tinta escurece e o preenchimento continua vivo. Sem esta separação, o tema claro
     precisaria de uma exceção em cada um dos 75 lugares que escrevem em ouro. */
  --gold-ink: #e8b54d;   --gold-ink-rgb: 232,181,77;
    --gold-dim: #a3823a;
    --like-green: #4CAF7A;
    --rose: #c4485e;
    --text: #f5f1e8;
    --text-dim: #8a8f9c;
    /* --line é o nome legado da aresta canônica (--edge, definida na fundação
       mais abaixo): uma fonte só de verdade, dois nomes durante a transição */
    --line: var(--edge);

    /* ---- canais RGB: toda tinta e todo halo derivam da cor-base ----
       antes existiam 18 alfas diferentes de rgba(232,181,77,…) espalhados pela folha,
       cada um uma cópia literal do dourado. Mudar a marca deixava todos pra trás. */
    --gold-rgb: 232,181,77;
    --rose-rgb: 196,72,94;
    --bg-rgb: 10,10,16;
    --bg-panel-rgb: 18,19,26;
    /* o topo da folha (--surface-3-top) em canais: quem precisa dele é o vidro do masthead
       parado, que tem que ser a MESMA tinta da superfície por baixo — com a cor do painel
       (mais escura) a faixa lia como um degrau colado sobre a folha */
    --surface-3-rgb: 34,36,47;
    --like-green-rgb: 76,175,122;

    /* ---- pares texto/fundo dos carimbos de swipe ---- */
    --stamp-like-bg: rgba(46,125,80,.85);
    --stamp-nope-bg: rgba(158,42,64,.85);
    --on-green: #eafff2;
    --on-rose: #ffeef1;

    /* ---- o Ranking (1.58.0) ---- */
    /* 1.58.0: o movimento contra a semana anterior. Verde e rosa próprios, e não --like-green
       e --rose: esses foram escolhidos para PREENCHER carimbo, e o rosa escrito em 12px sobre
       o preto dá 3,9:1. Estes passam de 7:1 no escuro; o papel tem os seus. */
    --rank-sobe: #6fd39c;
    --rank-desce: #ee8ea0;

    --toast-bg: #1b1d27;

    /* ---- derivados de superfície ---- */
    --on-gold: #1a1206;          /* texto sobre preenchimento dourado (9 usos crus antes) */
    --gold-tint-bg: #1d1808;     /* painel escuro com tinta dourada: modo selecionado, sala */
    --gold-tint-deep: #0f0d05;   /* par mais escuro do gradiente acima */
    /* TINTAS DE ESCREVER SOBRE IMAGEM (1.51.2): o pôster é foto escurecida nos DOIS temas,
       então estas NÃO trocam no tema claro — e por isso não aparecem no bloco dele */
    --on-image: #f5f1e8;
    --on-image-dim: rgba(245,241,232,.72);
    --on-image-edge: rgba(245,241,232,.18);
    --gold-on-image: #e8b54d;
    --gold-on-image-edge: rgba(232,181,77,.45);
    --rank-sobe-on-image: #79dca4;
    --rank-desce-on-image: #f19aab;
    /* A MARQUISE DO RANKING (1.58.0) é objeto da sala, como a TV do Início: um letreiro
       aceso de cinema de rua é creme com letra preta, de dia e de noite. Estes tokens moram
       só aqui e NUNCA entram no bloco do tema claro — é isso que os faz não trocar. */
    --marquise-placa: #f4ecd8;
    --marquise-placa-luz: #fffaf0;
    --marquise-tinta: #16110b;
    --marquise-tinta-apagada: rgba(22,17,11,.52);
    --marquise-moldura: #2a1d0c;
    --marquise-lampada: #ffd98a;
    /* A CINEMATECA (1.60.0): o INGRESSO da entrada e a PELÍCULA da sala Noites são objetos,
       como a marquise do Ranking — papel creme e filme preto nos dois temas. Nunca entram
       no bloco do tema claro. */
    --ingresso-papel:#f4ecd8; --ingresso-luz:#fffaf0; --ingresso-tinta:#16110b; --ingresso-apoio:#5b4e3a;
    --ingresso-rubrica:#7d580c; --ingresso-picote:rgba(22,17,11,.28); --ingresso-fio:rgba(22,17,11,.18);
    --pelicula:#0c0b0a; --pelicula-furo:#efe4c8; --pelicula-data:#f3e9d2; --pelicula-aresta:rgba(232,181,77,.35);
    /* a sombra do ingresso e a luz das vitrines seguem o tema (são o ambiente, não o objeto) */
    --ingresso-sombra:rgba(0,0,0,.5);
    --vitrine-luz:rgba(255,214,150,.13); --vitrine-fundo-top:rgba(0,0,0,.25); --vitrine-fundo:rgba(0,0,0,0);
    --disco-letra:#15110a;
    --panel-deep: #0e0c14;       /* fundo dos painéis em gradiente (antes #0e0c14 e #0e0c15,
                                    dois valores a um dígito de distância — deriva de copiar/colar) */
    --scrim: rgba(5,5,10,.72);   /* fundo dos overlays (antes rgba(4,4,7,.7) e rgba(5,5,10,.72)) */

    /* ---- estados desabilitados (cor explícita, nunca opacity: o texto precisa
           continuar legível porque carrega a explicação do que está bloqueado) ---- */
    --text-disabled: #7d8390;
    --line-disabled: #2b2e3a;
    --gold-disabled-bg: #3a3529;
    --gold-disabled-text: #c4bcaa;
    --gold-chip-disabled-bg: #4a4433;
    /* texto claro, não quase-preto: sobre o dourado apagado o par escuro dava 2:1 —
       o chip selecionado-mas-bloqueado era o texto menos legível do app, justamente
       num estado que precisa explicar o que está bloqueado. Agora 6,2:1. */
    --gold-chip-disabled-text: #d8d2c4;

    /* ================================================================
       FUNDAÇÃO COMPARTILHADA — material, tipografia, movimento e foco.
       Tudo abaixo é sistema: as superfícies consomem estes tokens em vez
       de repetir valores. Uma sala de cinema tem luz vindo de cima; toda
       a gramática de material aqui deriva dessa única direção de luz.
       ================================================================ */

    /* ---- níveis de superfície ----
       base = a sala (o próprio fundo); painel = a mesa; elevado = o que
       está sobre a mesa; flutuante = o que foi levantado (sheet, diálogo).
       Cada nível é um par topo/base: a face de cima recebe mais luz. */
    --surface-1-top:#15161f;  --surface-1-bot:#101118;
    --surface-2-top:#1c1d28;  --surface-2-bot:#15161f;
    --surface-3-top:#22242f;  --surface-3-bot:#191a23;

    /* ---- arestas ----
       --edge é a aresta canônica; --line é o nome legado, agora derivado
       dela (um sistema só, dois nomes durante a transição). --edge-lit é a
       aresta VOLTADA PRA LUZ: só a borda de cima usa. */
    --edge:#23252f;
    --edge-lit:#343747;
    --edge-strong:#3d4053;
    --edge-gold: rgba(var(--gold-rgb), .42);

    /* ---- brilho interno: a fina linha de luz na quina superior ---- */
    --sheen-1: rgba(255,255,255,.04);
    --sheen-2: rgba(255,255,255,.055);
    --sheen-3: rgba(255,255,255,.075);

    /* ---- sombra por nível: contato curto + queda longa ---- */
    --shadow-1: 0 1px 2px rgba(0,0,0,.44), 0 8px 18px -10px rgba(0,0,0,.6);
    --shadow-2: 0 2px 6px rgba(0,0,0,.46), 0 16px 32px -14px rgba(0,0,0,.66);
    --shadow-3: 0 8px 18px rgba(0,0,0,.5), 0 34px 64px -22px rgba(0,0,0,.74);

    /* ---- raio por nível: quanto maior a peça, mais macia a quina ---- */
    --radius-xs:8px; --radius-sm:10px; --radius-md:14px;
    --radius-lg:18px; --radius-xl:24px; --radius-pill:999px;

    /* ---- divisórias ----
       translúcidas de propósito: a mesma régua lê certo sobre o fundo da
       sala e sobre um painel elevado, sem precisar de um hex por contexto. */
    --rule: rgba(255,255,255,.06);
    --rule-strong: rgba(255,255,255,.10);

    /* ---- tipografia: papéis, não tamanhos avulsos ----
       display = cartaz (Bebas); title = título de superfície; group = título
       de grupo; body = corpo; support = apoio; label = rótulo (caixa alta, e
       por isso EXCEÇÃO); num = metadado numérico, tabular. */
    --font-display:'Bebas Neue', ui-sans-serif, sans-serif;
    --t-display-size: clamp(34px, 9.5vw, 48px); --t-display-lh:.94;  --t-display-track:.5px;  --t-display-weight:400;
    --t-title-size:22px;   --t-title-lh:1.16;   --t-title-track:.1px;  --t-title-weight:700;
    --t-group-size:16px;   --t-group-lh:1.3;    --t-group-track:.1px;  --t-group-weight:700;
    --t-body-size:14px;    --t-body-lh:1.55;    --t-body-weight:400;
    --t-support-size:12.5px; --t-support-lh:1.5; --t-support-weight:400;
    --t-label-size:11px;   --t-label-lh:1.3;    --t-label-track:1.4px; --t-label-weight:800;
    --t-num-size:13px;     --t-num-lh:1.2;      --t-num-track:.2px;    --t-num-weight:700;

    /* ---- movimento: três durações e três intenções de curva ---- */
    --dur-1:120ms;  /* resposta tátil: pressão, troca de cor */
    --dur-2:200ms;  /* mudança de estado no lugar */
    --dur-3:320ms;  /* entrada/saída de superfície */
    --ease-entrance: cubic-bezier(.16,1,.3,1);
    --ease-exit: cubic-bezier(.4,0,.9,.4);
    --ease-emphasis: cubic-bezier(.2,.8,.2,1);
    --ease-standard: cubic-bezier(.2,0,0,1);

    /* ---- foco: um anel só, e a polaridade inverte sobre preenchimento claro ---- */
    --focus-ring:#ffd77a;
    --focus-ring-on-gold:#0d1017;
    --focus-ring-w:2px;
    --focus-ring-offset:2px;
    --focus-ring-radius:4px;

    /* ================================================================
       GRAMÁTICA DO OURO — três empregos, um metal.
       ================================================================
       O ouro do CineMatch é ONIPRESENTE por decisão do dono e não se reserva
       ao match. Isso não é o problema; a falta da regra era. A comparação cega
       mediu a consequência no Ranking: "o ouro faz quatro trabalhos simultâneos
       … e ainda em dois gradientes diferentes: o botão 'Filmes' tem uma faixa de
       brilho no topo que o chip 'Todos' não tem". Dois empregos indistintos e
       duas receitas de metal para o mesmo emprego.

       · OURO CHEIO — `--gold-metal` de fundo, `--on-gold` de texto, aresta
         `--gold-edge`, fio de luz `--gold-sheen`. Significa O QUE ESTÁ EM VIGOR:
         a ação primária (o que você vai tocar), a opção selecionada (o que você
         escolheu) e o carimbo do que aconteceu (o match, a contagem de filtros
         ativos). É a ÚNICA aplicação com massa, e a lista de quem tem direito a
         ela é fechada — está no contrato de `premium-system-r7.test.mjs`.
       · OURO TINTA — `--gold` como `color`. Significa O QUE É DA MARCA OU DO
         ESTADO: rótulo de seção, numeral de posição, ícone, aba ativa, marca de
         foco de título. Sem fundo, sem massa.
       · OURO APAGADO — `--gold-dim`. Acento que acompanha sem reivindicar:
         glifos de linha, marca d'água, ícone de estado vazio.

       POR QUE DUAS PEÇAS DOURADAS NA MESMA TELA NÃO COMPETEM: porque nunca são
       o mesmo emprego. Tinta e apagado não têm massa, então não disputam com
       metal — a hierarquia é de MATÉRIA, não de cor. E quando duas peças de
       metal aparecem juntas, como o alternador "Filmes" e o chip "Todos" do
       Ranking, elas são seleções de TRILHOS DIFERENTES: o que as distingue é o
       que rotulam, não como brilham. É justamente por isso que a receita do
       metal tem de ser uma só — a diferença de brilho entre elas não carregava
       informação nenhuma, só ruído.

       ---- o metal, uma receita ----
       a peça é a mesma cor da marca; o que muda é COMO a luz corre nela.
       O topo pega a luz, a base cai pro tom profundo, a borda é mais quente
       que o corpo. Sem halo: metal reflete, não brilha sozinho. */
    --gold-lit:#f6d288;
    --gold-deep:#c9922f;
    --gold-edge:#f0c76a;
    --gold-metal: linear-gradient(177deg, var(--gold-lit) 0%, var(--gold) 42%, var(--gold-deep) 100%);
    /* o fio de luz do metal. Eram três valores soltos a um dígito de distância
       (.32 no botão, .32 no chip, .3 no trilho do interruptor) — o bastante para
       a comparação cega ler "dois gradientes diferentes" na mesma tela. */
    --gold-sheen: rgba(255,255,255,.32);

    /* ---- gramática de botão ---- */
    --btn-secondary-fg: var(--text);
    --btn-ghost-fg: var(--text-dim);
    --btn-danger-fg:#f0b0bc;
    --btn-danger-edge: rgba(var(--rose-rgb), .58);
    /* desabilitado é cor explícita, nunca opacity: o rótulo bloqueado carrega
       a explicação do bloqueio e precisa continuar legível (5:1 ou mais) */
    --btn-disabled-bg:#20222c;
    --btn-disabled-fg:#8f95a3;
    --btn-disabled-edge:#2b2e3a;
    --btn-danger-disabled-fg:#a98a92;

    /* ---- texto de apoio dentro de superfícies elevadas ----
       antes existiam #aeb2bd, #afb3bf e #adb2bf, três cópias a um dígito de
       distância nascidas de copiar/colar. Agora é um valor só. */
    --text-soft:#aeb2bd;
    /* pares texto/fundo de estado */
    --on-ok:#b8d8c5;
    --on-error:#f3a5b3;
    --on-gold-tint:#c6b994;
    --on-danger-title:#f0c8d0;
    --on-danger-body:#c7aab0;

    /* ---- navegação e zona de ação ----
       a altura da tabbar e o respiro acima dela eram números repetidos em cinco
       lugares; agora a folga do CTA é declarada uma vez e não depende de sobrar
       espaço no fim do conteúdo. */
    --tabbar-h:72px;
    --dock-gap:16px;
    /* altura da rampa do véu da zona de ação: o conteúdo desaparece ao longo dela em vez
       de terminar cortado numa aresta. Também é o que o #setup reserva de
       `scroll-padding-bottom`, pra que a última região sempre role até ficar livre. */
    --dock-veil:68px;
    /* rampa da própria ARESTA da navegação. A do `--dock-veil` acompanha a zona de ação, e
       a zona de ação deixa de ser fixa em viewport baixo (200% de zoom) — ali o véu sumia
       junto e o cartão voltava a terminar a faca sobre a barra. Esta pertence à barra:
       existe em qualquer altura de tela.
       ALTURA E CURVA SÃO UMA SÓ NA CONCHA INTEIRA. Eram duas: 36px em duas paradas aqui,
       54px em quatro no Histórico — a mesma borda dissolvendo de dois jeitos conforme a
       aba. E a curta era justamente a que o Histórico já tinha documentado como
       insuficiente: nos últimos px o véu estava quase opaco e os primeiros mal
       escureciam, então o que passava por baixo continuava lendo como cortado a faca.
       Quem repousa nessa faixa no Início é um TÍTULO de seção, e título pela metade lê
       como quebrado — um cartaz meio dissolvido ainda lê como "tem mais abaixo".
       Paradas em % são estáveis aqui porque a altura da rampa é o próprio token, que é
       constante; a razão de o `--dock-veil` usar px (elemento de altura variável) não
       se aplica. */
    --tabbar-veil:54px;
    --tabbar-veil-fade: linear-gradient(to top,
      var(--bg) 0,
      rgba(var(--bg-rgb),.92) 26%,
      rgba(var(--bg-rgb),.55) 58%,
      rgba(var(--bg-rgb),0) 100%);
    --dock-space: calc(var(--tabbar-h) + env(safe-area-inset-bottom));
    /* ---- O VIDRO DA DISSOLUÇÃO: a segunda metade da mesma receita ----
       Um véu de opacidade pura tem zona de transição MAIS CURTA que uma linha de
       texto: o topo dos glifos chega quase limpo e a base quase apagada, e uma
       palavra assim lê como quebrada, não como "tem mais abaixo". Foi o que a
       comparação cega registrou como defeito de SISTEMA — "corta uma linha de
       texto pela metade em 5 das 8 telas" — e a prova de qual era a variável
       está no próprio produto: das três arestas que dissolvem, as duas que
       NUNCA receberam a queixa (a zona de ação do Início e o pé da folha de
       Ajustes) eram exatamente as duas que já tinham desfoque progressivo. A que
       recebeu a queixa era a única sem ele.
       O desfoque tira a LEGIBILIDADE antes do brilho: o que entra na rampa deixa
       de ser lido como palavra e passa a ser lido como camada — e uma camada não
       tem metade. A máscara faz o vidro nascer do nada, senão a própria borda do
       vidro viraria a segunda aresta dura no lugar da primeira.
       Os três consumidores leem daqui. Fonte igual garante curva igual POR
       CONSTRUÇÃO; valor igual só garante até alguém editar um dos lados — foi a
       lição que o `--tabbar-veil` já tinha ensinado nesta mesma aresta. */
    --veil-glass: blur(6px);
    --veil-glass-mask: linear-gradient(180deg, transparent 0, #000 62%);

    /* ---- campo de texto ---- */
    --field-bg: var(--surface-1-bot);
    --field-edge: var(--edge);
    --field-edge-hover: var(--edge-strong);
    --field-invalid: var(--rose);

    /* ---- a sala do Início (1.54.0): o sofá é tecido ameixa, a TV é grafite ---- */
    --sofa-tecido:#3a2e3f;  --sofa-tecido-fundo:#2b2231;
    --sofa-assento:#4b3c52; --sofa-assento-fundo:#382c40;
    --sofa-braco:#43354a;   --sofa-braco-fundo:#30263a;
    --sofa-base:#241c29;    --sofa-luz:rgba(255,255,255,.08);
    --sofa-vao:#1a141e; /* o fundo entre as almofadas (1.57.0) */
    --sofa-reflexo:rgba(245,241,232,.14);  --sofa-sombra:rgba(0,0,0,.8);
    --sofa-chamar-fundo:rgba(232,181,77,.08); --sofa-chamar-aro:rgba(232,181,77,.7);
    /* a TV NÃO entra no tema claro: é preta de verdade, e o texto na tela dela é --on-image */
    --tv-moldura:#2c2a36;   --tv-moldura-fundo:#1b1a23;
    --tv-tela:#0b0b12;      --tv-tela-luz:#1d1b2a;
  }
  /* ===================== O TEMA CLARO: "PAPEL" =====================
     O app não vira branco — vira PAPEL. A escolha é de produto e foi do dono: num fundo
     creme o dourado continua parecendo dourado, e o cartaz continua saltando da página,
     que é o que um fundo branco puro não faz (ele compete com a arte em vez de emoldurá-la).

     A REGRA QUE GOVERNA ESTE BLOCO INTEIRO: aqui só moram TOKENS. Nenhuma regra de
     componente, nenhum seletor de tela. Se um componente precisar de um valor diferente no
     claro, o certo é ele passar a ler um token — não este bloco crescer uma exceção. É essa
     disciplina que faz um segundo tema custar 60 linhas em vez de 6.000.

     O OURO SE PARTIU EM DOIS, e essa é a mudança de fundo. No escuro, um ouro só servia
     para tudo. No claro ele não pode: `#e8b54d` sobre creme dá 1,6 de contraste — some.
     Então `--gold` continua sendo o ouro VIVO (preenchimento do CTA, arestas, brilhos) e
     `--gold-ink` é a TINTA (todo texto dourado). No escuro os dois são o mesmo valor, que é
     o que mantém o tema atual intocado. */
  :root[data-tema="claro"]{
    color-scheme: light;

    --bg:#f1eadd;                       --bg-rgb:241,234,221;
    --bg-halo:#fdfaf4;   /* no claro a luz do topo CLAREIA, em vez de escurecer */
    --bg-panel:#fbf7ef;                 --bg-panel-rgb:251,247,239;
    --panel-deep:#f6f0e4;
    --card-top:#ffffff;                 --card-bot:#f4ede0;

    --surface-1-top:#fffdf9;  --surface-1-bot:#f8f2e7;
    --surface-2-top:#fffefb;  --surface-2-bot:#f6efe3;
    --surface-3-top:#ffffff;  --surface-3-bot:#f7f1e6;  --surface-3-rgb:255,253,249;

    /* medidos contra o fundo bege: 13,9 / 6,3 / 4,6 — os três passam AA de texto */
    /* o apoio também é medido contra a pastilha dourada: é lá que ele aparece mais claro */
    --text:#241d13;  --text-soft:#5c5344;  --text-dim:#6b6151;

    /* vivo para preencher (o texto escuro em cima dá 5,8), tinta para escrever (4,7) */
    --gold:#c8901f;        --gold-rgb:200,144,31;
    /* medido no PIOR ponto, que é a base do gradiente da pastilha dourada (#f3e4c1) e não
       o fundo da tela: 5,1 lá, 5,4 no fundo, 6,3 na superfície branca */
    --gold-ink:#7d580c;    --gold-ink-rgb:125,88,12;
    --on-gold:#2a1d05;
    --gold-tint-bg:#f9eed6;  --gold-tint-deep:#f3e4c1;
    --edge-gold: rgba(var(--gold-ink-rgb), .40);

    --edge:#e2d8c6;  --edge-lit:#f0e8d9;  --edge-strong:#cdc0a9;
    --rule:rgba(36,29,19,.10);  --rule-strong:rgba(36,29,19,.17);

    /* A LUZ CONTINUA VINDO DE CIMA, e é por isso que estes dois invertem de significado.
       No escuro o material recebe um brilho branco na aresta superior; no claro, a mesma
       luz produz SOMBRA na parte de baixo. Manter o brilho branco aqui não daria errado de
       forma visível — daria pior: superfícies chapadas, sem nenhum relevo. */
    --sheen-1:rgba(70,56,30,.030);  --sheen-2:rgba(70,56,30,.042);  --sheen-3:rgba(70,56,30,.055);
    --shadow-1:0 1px 2px rgba(80,64,36,.10), 0 6px 14px -8px rgba(80,64,36,.16);
    --shadow-2:0 2px 6px rgba(80,64,36,.12), 0 14px 28px -12px rgba(80,64,36,.20);
    --shadow-3:0 8px 18px rgba(80,64,36,.14), 0 30px 56px -20px rgba(80,64,36,.26);

    /* o toast continua ESCURO: ele é uma faixa que passa por cima de tudo, e um aviso
       claro sobre fundo claro precisaria de borda para existir */
    --toast-bg:#2b2318;
    /* e o véu dos overlays continua escurecendo, porque o que ele faz é apagar o fundo */
    --scrim: rgba(40,32,20,.42);

    --text-disabled:#9a9182;
    --line-disabled:#ddd3c1;
    --gold-disabled-bg:#eee3cb;  --gold-disabled-text:#8f846f;
    --gold-chip-disabled-bg:#e9dcc0;  --gold-chip-disabled-text:#7c705a;

    /* o movimento do Ranking escrito sobre papel: verde e rosa escurecem para passar de 4,5 */
    --rank-sobe:#226440;    --rank-desce:#a8324a;  /* sobe: #2c7a4e dava 4,2 nas linhas (1.67.0) */

    /* ---- OS ESTADOS TAMBÉM MUDAM DE LADO ----
       Rosa e verde foram escolhidos para brilhar sobre preto; sobre papel eles somem. O
       `--btn-danger-fg` de #f0b0bc dá 1,5 de contraste aqui — literalmente ilegível, e é o
       texto que avisa que uma ação apaga tudo. A varredura de contraste pegou os sete de
       uma vez, na tela de Ajustes. */
    --btn-danger-fg:#a02f45;          /* 5,9 — #b03a50 dava 4,3 sobre a folha dos Ajustes (1.67.0) */
    --btn-danger-edge: rgba(160,47,69,.45);
    --btn-danger-disabled-fg:#a98a92;
    --on-danger-title:#a02f45;        /* 5,9 */
    --on-danger-body:#7d4a54;         /* 5,9 */
    --rose:#b03a50;   --rose-rgb:176,58,80;
    --on-ok:#276b45;  --on-error:#a02f45;  --on-gold-tint:#6f4e0a;
    --btn-disabled-bg:#eae2d2;  --btn-disabled-fg:#7f7566;  --btn-disabled-edge:#ddd3c1;
    /* o ouro apagado é RÓTULO, não decoração: em 10px ele precisa dos mesmos 4,5 */
    --gold-dim:#7d580c;               /* 5,4 */

    --focus-ring:#8a6410;

    /* o sofá no papel: o MESMO ameixa, só mais aceso. A primeira versão clareava até o
       lilás pastel e lia como brinquedo ao lado da TV preta (achado da revisão), e o disco
       violeta sumia contra o assento */
    --sofa-tecido:#9c7a92;  --sofa-tecido-fundo:#876579;
    --sofa-assento:#ad8ca3; --sofa-assento-fundo:#94738a;
    --sofa-braco:#a3819a;   --sofa-braco-fundo:#8b6a81;
    --sofa-base:#6f5367;    --sofa-luz:rgba(255,255,255,.28);
    --sofa-vao:#553e4f;
    --sofa-reflexo:rgba(255,255,255,.22);  --sofa-sombra:rgba(80,64,36,.45);
    /* no papel o lugar de chamar é um disco CLARO sobre o tecido ameixa, com aro de tinta */
    --sofa-chamar-fundo:rgba(255,250,240,.82); --sofa-chamar-aro:#7d580c;
    --ingresso-sombra:rgba(80,56,16,.28);
    --vitrine-luz:rgba(255,240,200,.9); --vitrine-fundo-top:#efe7d7; --vitrine-fundo:#f8f2e6;
    --disco-letra:#fffaf0;
  }

  /* AS PEÇAS QUE SÃO ESCURAS POR NATUREZA NÃO SEGUEM O TEMA, e por isso reimportam a
     paleta escura inteira. São três, e as três têm o mesmo motivo: elas não são tela, são
     SALA. O splash é a luz apagando; o clímax do match e o do solo acontecem sobre um
     fundo quase preto com o pôster aceso, que é a imagem que o app inteiro existe para
     produzir. Clarear ali não seria coerência de tema — seria acender a luz no meio do
     filme. E há um detalhe técnico junto: com o `--gold-ink` do tema claro, o coração da
     marca sairia MARROM sobre o preto do clímax, porque a tinta escura foi calibrada
     contra papel.
     O splash tem um segundo motivo, este de engenharia: ele cobre a tela desde o primeiro
     quadro, antes de o script ler qual tema a pessoa escolheu. Mantendo-o escuro, a troca
     acontece por baixo dele e ninguém vê o lampejo — sem precisar de um segundo bloco de
     script no cabeçalho, que quebraria o hash único da CSP. */
  :root[data-tema="claro"] #splash,
  :root[data-tema="claro"] .match-takeover{
    --text:#f5f1e8;  --text-soft:#aeb2bd;  --text-dim:#8a8f9c;
    --gold:#e8b54d;  --gold-rgb:232,181,77;
    --gold-ink:#e8b54d;  --gold-ink-rgb:232,181,77;
    --gold-dim:#a3823a;  --on-gold:#1a1206;
    --edge:#23252f;  --edge-lit:#343747;  --edge-strong:#3d4053;
    --rule:rgba(255,255,255,.06);  --rule-strong:rgba(255,255,255,.10);
    --surface-1-top:#15161f;  --surface-1-bot:#101118;
    --surface-2-top:#1c1d28;  --surface-2-bot:#15161f;
    --surface-3-top:#22242f;  --surface-3-bot:#191a23;
    --sheen-1:rgba(255,255,255,.04);  --sheen-2:rgba(255,255,255,.055);  --sheen-3:rgba(255,255,255,.075);
    /* ESTA LISTA NÃO É UM GOSTO, É UMA CONTA: tem que cobrir TODO token que as regras do
       clímax leem e que o tema claro redefine. Faltavam quatro, e o preço apareceu no
       aparelho do dono: `.mt-secondary button` pinta o fundo com `--bg-panel-rgb` e o
       texto com `--text`. Com o `--text` reimportado (creme) e o `--bg-panel-rgb` do
       PAPEL (quase branco), "Continuar jogando" virou creme sobre branco — o botão
       existia, media certo, e não se lia. Nada disso dá erro: dá uma tela pior.
       Há teste que refaz a conta a partir do CSS do clímax. */
    --bg-panel:#12131a;  --bg-panel-rgb:18,19,26;
    /* `--line: var(--edge)` mora no :root, e um token que aponta para outro é resolvido
       ONDE ELE É DECLARADO — não onde é lido. Redeclarar `--edge` aqui não conserta o
       `--line` que o :root já calculou com a aresta do papel: quem lê `var(--line)` dentro
       do clímax recebe a linha creme. Por isso a corrente inteira volta, elo por elo. */
    --line:#23252f;
    --card-top:#1c1e29;  --card-bot:#08090d;
    --shadow-1:0 1px 2px rgba(0,0,0,.44), 0 8px 18px -10px rgba(0,0,0,.6);
    color:var(--text);
  }
  :root[data-tema="claro"] #splash{ background:#0a0a10; }

  *{box-sizing:border-box; margin:0; padding:0;}
  /* `hidden` não é regra desta folha: é declaração da folha do AGENTE DO USUÁRIO, e
     origem de autor vence origem de agente do usuário INDEPENDENTEMENTE de
     especificidade. Bastava um `display:` de autor pousar no elemento — e a gramática
     de botão logo abaixo declara `display:inline-flex` em `.btn`/`.result-btn` — para o
     atributo virar decoração. Foi assim que o dono viu "Ver trailer" pintado ao lado do
     player que ele já estava assistindo (o botão é fallback, nasce `hidden` e
     `hydrateDetailExtras` só o revela quando não há trailer embutível), e a mesma causa
     pintava "Limpar filtros" no Histórico sem filtro nenhum ligado.
     `!important` aqui não é força bruta: é devolver ao atributo do HTML a última palavra
     que ele sempre teve que ter. As regras `.algo[hidden]{display:none}` espalhadas pela
     folha são remendos anteriores da MESMA armadilha, um por peça; ficam de pé porque
     são inofensivas, mas nenhuma peça nova precisa mais descobrir isto sozinha. */
  [hidden]{ display:none !important; }
  /* controles não herdam a fonte do documento por padrão — sem isto, botões
     caem no Arial do navegador e a tipografia do app se parte em dois */
  button, input, select, textarea{ font-family: inherit; }
  /* ===== sistema de ícones (SVG inline, herdam a cor do contexto) ===== */
  .ico-svg{ display:inline-block; vertical-align:-.18em; flex:none; }
  .lbl-ico{ margin-right:7px; vertical-align:-.16em; color:var(--gold-ink); }
  .mode-btn.selected .lbl-ico, .chip.selected .lbl-ico{ color:inherit; }
  /* marca grande dos estados vazios: substitui o emoji genérico */
  .spot{
    width:64px; height:64px; margin:0 auto 14px; display:block;
    color:var(--gold-dim); opacity:.9;
  }
  .empty-state .spot{ margin-bottom:12px; }
  /* arte do tutorial e do handoff: ilustração de verdade, não ícone de UI esticado */
  .tut-art{ display:block; width:216px; height:auto; margin:0 auto; }
  .tut-art .ln{ stroke:var(--gold-dim); stroke-width:2; fill:none; stroke-linecap:round; stroke-linejoin:round; }
  .tut-art .ln-f{ fill:var(--gold-dim); stroke:none; }
  .tut-art .ln-soft{ stroke:var(--line); stroke-width:2; fill:none; stroke-linecap:round; stroke-linejoin:round; }
  .tut-art .fill-panel{ fill:var(--bg-panel); }
  .tut-art .yes{ stroke:var(--like-green); }
  .tut-art .yes-f{ fill:var(--like-green); }
  .tut-art .no{ stroke:var(--rose); }
  .tut-art .gold{ stroke:var(--gold); }
  .tut-art .gold-f{ fill:var(--gold); }

  /* ================================================================
     FUNDAÇÃO — primitivas compartilhadas.
     Toda superfície do app consome daqui. Nada abaixo é decoração:
     cada nível de luz, cada curva e cada anel comunica um estado.
     ================================================================ */

  /* ---- material: quatro níveis, uma receita ----
     A luz vem de cima. Por isso a borda superior é mais clara que as
     laterais, o preenchimento cai do topo pra base e existe um fio de
     luz interno na quina de cima. A sombra cresce com o nível. */
  .mat{
    border:1px solid var(--edge); border-top-color:var(--edge-lit);
    border-radius:var(--radius-lg);
    background: linear-gradient(180deg, var(--surface-1-top), var(--surface-1-bot));
    box-shadow: var(--shadow-1), inset 0 1px 0 var(--sheen-1);
  }
  .mat--raised{
    border:1px solid var(--edge); border-top-color:var(--edge-lit);
    border-radius:var(--radius-md);
    background: linear-gradient(180deg, var(--surface-2-top), var(--surface-2-bot));
    box-shadow: var(--shadow-2), inset 0 1px 0 var(--sheen-2);
  }
  .mat--floating{
    border:1px solid var(--edge-strong); border-top-color:var(--edge-lit);
    border-radius:var(--radius-xl);
    background: linear-gradient(180deg, var(--surface-3-top), var(--surface-3-bot));
    box-shadow: var(--shadow-3), inset 0 1px 0 var(--sheen-3);
  }
  /* nível 0: a própria sala. Serve pra "desligar" o material de um bloco
     que precisa se fundir ao fundo sem perder a aresta. */
  .mat--flat{
    border:1px solid var(--edge); border-radius:var(--radius-lg);
    background:var(--bg); box-shadow:none;
  }

  /* ---- divisórias ----
     A régua padrão nasce translúcida e some nas pontas: ela separa sem
     desenhar uma caixa. A régua dura é a que fecha uma REGIÃO inteira. */
  .rule{
    border:0; height:1px; flex:none;
    background: linear-gradient(90deg, transparent, var(--rule-strong) 10%, var(--rule-strong) 90%, transparent);
  }
  .rule--hard{ background: var(--edge); }
  .rule--vertical{ width:1px; height:auto; background: var(--rule); }

  /* ---- tipografia: sete papéis ----
     Display é cartaz e só aparece na entrada de uma superfície. Rótulo em
     caixa alta é EXCEÇÃO: quando ele vira tecido, tudo lê como formulário. */
  .t-display{
    font-family:var(--font-display); font-weight:var(--t-display-weight);
    font-size:var(--t-display-size); line-height:var(--t-display-lh); letter-spacing:var(--t-display-track);
  }
  .t-title{
    font-size:var(--t-title-size); line-height:var(--t-title-lh);
    letter-spacing:var(--t-title-track); font-weight:var(--t-title-weight); color:var(--text);
  }
  .t-group{
    font-size:var(--t-group-size); line-height:var(--t-group-lh);
    letter-spacing:var(--t-group-track); font-weight:var(--t-group-weight); color:var(--text);
  }
  .t-body{ font-size:var(--t-body-size); line-height:var(--t-body-lh); font-weight:var(--t-body-weight); }
  .t-support{ font-size:var(--t-support-size); line-height:var(--t-support-lh); font-weight:var(--t-support-weight); color:var(--text-dim); }
  .t-label{
    font-size:var(--t-label-size); line-height:var(--t-label-lh);
    letter-spacing:var(--t-label-track); font-weight:var(--t-label-weight);
    text-transform:uppercase; color:var(--gold-dim);
  }
  .t-num{
    font-size:var(--t-num-size); line-height:var(--t-num-lh);
    letter-spacing:var(--t-num-track); font-weight:var(--t-num-weight);
    font-variant-numeric: tabular-nums;
  }

  /* ---- gramática de botão ----
     Quatro papéis e um estado desabilitado por papel. Os nomes legados
     entram na mesma regra: a gramática existe uma vez só. */
  .btn,
  .cta, .copy-btn, .result-btn, .modal-close, .settings-action, .btn-ghost, .btn-danger{
    display:inline-flex; align-items:center; justify-content:center; gap:8px;
    min-height:44px; border-radius:var(--radius-md);
    font-family:inherit; font-weight:700; cursor:pointer;
    transition: background-color var(--dur-1) var(--ease-standard),
                border-color var(--dur-1) var(--ease-standard),
                color var(--dur-1) var(--ease-standard),
                transform var(--dur-1) var(--ease-standard);
  }
  .btn:active:not(:disabled),
  .cta:active:not(:disabled), .result-btn:active:not(:disabled), .btn-ghost:active:not(:disabled){
    transform: translateY(1px);
  }
  /* primário: a peça de metal. Uma por estado. */
  .btn--primary,
  .cta, .copy-btn, .result-btn.primary, .confirm-actions .primary, .runner-add-btn.primary{
    background: var(--gold-metal); color: var(--on-gold);
    border:1px solid var(--gold-edge);
    box-shadow: var(--shadow-1), inset 0 1px 0 var(--gold-sheen);
  }
  /* secundário: material elevado, texto claro, aresta visível */
  .btn--secondary,
  .result-btn, .modal-close, .settings-action, .btn-ghost, .back-link, .gear-btn{
    background: linear-gradient(180deg, var(--surface-2-top), var(--surface-2-bot));
    color: var(--btn-secondary-fg);
    border:1px solid var(--edge); border-top-color:var(--edge-lit);
    box-shadow: var(--shadow-1), inset 0 1px 0 var(--sheen-2);
  }
  /* fantasma: sem material nenhum. Só texto e alvo. */
  .btn--ghost,
  .link-btn, .round-exit, .ars-discard{
    background:none; border:1px solid transparent; box-shadow:none;
    color: var(--btn-ghost-fg);
  }
  /* destrutivo: contorno de aviso, nunca preenchimento — apagar não compete
     com a ação principal da tela */
  .btn--danger,
  .btn-danger, .settings-danger .settings-action{
    background: rgba(var(--rose-rgb), .08); color: var(--btn-danger-fg);
    border:1px solid var(--btn-danger-edge); box-shadow:none;
  }
  .btn--danger.is-solid, .confirm-actions .danger{
    background: var(--rose); color:#fff; border-color: var(--rose);
  }
  /* desabilitado: cor explícita por papel. Nunca opacity — o rótulo bloqueado
     é justamente quem explica o bloqueio e precisa continuar legível. */
  .btn:disabled,
  .btn--secondary:disabled, .btn--ghost:disabled,
  .result-btn:disabled, .modal-close:disabled, .settings-action:disabled, .btn-ghost:disabled{
    background: var(--btn-disabled-bg); color: var(--btn-disabled-fg);
    border-color: var(--btn-disabled-edge); box-shadow:none;
    cursor:not-allowed; transform:none;
  }
  .btn--primary:disabled, .cta:disabled, .copy-btn:disabled, .result-btn.primary:disabled{
    background: var(--gold-disabled-bg); color: var(--gold-disabled-text);
    border-color: var(--line-disabled); box-shadow:none; cursor:not-allowed; transform:none;
  }
  .btn--danger:disabled, .btn-danger:disabled{
    background: var(--btn-disabled-bg); color: var(--btn-danger-disabled-fg);
    border-color: var(--btn-disabled-edge); box-shadow:none; cursor:not-allowed;
  }

  /* ---- campo de texto: seis estados ---- */
  .field,
  .nickname-input, .history-search{
    width:100%; min-height:44px; padding:13px 15px;
    border-radius:var(--radius-md); border:1px solid var(--field-edge);
    background: var(--field-bg); color:var(--text);
    font-family:inherit; font-size:16px; line-height:1.3;
    box-shadow: inset 0 1px 2px rgba(0,0,0,.35);
    transition: border-color var(--dur-1) var(--ease-standard), background-color var(--dur-1) var(--ease-standard);
  }
  .field::placeholder, .nickname-input::placeholder, .history-search::placeholder{ color:var(--text-dim); opacity:1; }
  .field:hover:not(:disabled), .nickname-input:hover, .history-search:hover{ border-color: var(--field-edge-hover); }
  .field:focus, .nickname-input:focus, .history-search:focus{ border-color: var(--gold); }
  .field:disabled{
    background: var(--btn-disabled-bg); color: var(--btn-disabled-fg);
    border-color: var(--btn-disabled-edge); cursor:not-allowed;
  }
  .field[aria-invalid="true"]{ border-color: var(--field-invalid); background: rgba(var(--rose-rgb), .07); }

  /* ---- switch ---- */
  .switch:active::after{ width:24px; }
  .switch[aria-disabled="true"]{ cursor:not-allowed; }
  .switch[aria-disabled="true"]::before{ background: var(--btn-disabled-edge); }
  .switch[aria-disabled="true"]::after{ background: var(--btn-disabled-fg); }

  /* ---- chip ---- */
  .chip:active:not(.chip-disabled){ transform: scale(.97); }
  .chip[aria-invalid="true"]{ border-color: var(--field-invalid); color: var(--text); }

  /* ---- segmented control ----
     Um grupo onde exatamente uma opção vale. Os botões de modo e de mídia
     já eram isso; agora têm a mesma gramática de estado. */
  .seg{ display:flex; gap:8px; }
  .mode-btn, .seg-item{
    transition: background-color var(--dur-1) var(--ease-standard),
                border-color var(--dur-1) var(--ease-standard),
                color var(--dur-1) var(--ease-standard);
  }
  .seg-item:active,
  .mode-btn:active{ transform: scale(.985); }
  .seg-item[disabled],
  .mode-btn[disabled]{
    background: var(--btn-disabled-bg); color: var(--btn-disabled-fg);
    border-color: var(--btn-disabled-edge); cursor:not-allowed; transform:none;
  }
  .seg-item[aria-pressed="true"], .seg-item.selected{
    background: var(--gold-metal); color: var(--on-gold); border-color: var(--gold-edge);
    box-shadow: inset 0 1px 0 var(--gold-sheen);
  }
  @keyframes cselect-open{ from{ opacity:0; transform: translateY(-6px) scale(.99); } }

  /* ---- esqueleto de carregamento ----
     Mesmo material das superfícies, com uma varredura curta de luz. Nunca
     um spinner genérico: o esqueleto já mostra a FORMA do que vem. */
  .skel{
    position:relative; overflow:hidden; border-radius:var(--radius-sm);
    background: linear-gradient(180deg, var(--surface-2-top), var(--surface-2-bot));
    box-shadow: inset 0 0 0 1px var(--sheen-1);
  }
  .skel::after{
    content:''; position:absolute; inset:0;
    background: linear-gradient(100deg, transparent 18%, var(--sheen-3) 50%, transparent 82%);
    transform: translateX(-100%);
    animation: skel-sweep 1.5s var(--ease-standard) infinite;
  }
  @keyframes skel-sweep{ to{ transform: translateX(100%); } }
  .skel-row{ display:flex; align-items:center; gap:13px; padding:13px 0; border-bottom:1px solid var(--rule); }
  .skel-lines{ flex:1; min-width:0; display:flex; flex-direction:column; gap:8px; }

  /* ---- moldura de pôster ----
     Cartaz é 2:3. A moldura garante a proporção, o anel de contorno, a
     sombra de contato, o carregamento e o caso sem pôster — as três
     superfícies consomem a MESMA moldura em três escalas. */
  .pf,
  .rank-thumb, .result-emoji, .raffle-poster, .sess-art-poster, .mt-poster{
    position:relative; flex:none; aspect-ratio:2 / 3; overflow:hidden;
    border-radius: var(--pf-radius, var(--radius-sm));
    background: linear-gradient(160deg, var(--card-top), var(--card-bot));
    box-shadow: inset 0 0 0 1px var(--sheen-2), var(--shadow-1);
    display:flex; align-items:center; justify-content:center;
    color: var(--text-dim);
  }
  .pf-img{ position:absolute; inset:0; width:100%; height:100%; object-fit:cover; display:block; }
  .pf--thumb{ width:44px; --pf-radius: var(--radius-xs); }
  .pf--card{ width:160px; --pf-radius: var(--radius-md); }
  .pf--lead{
    width:190px; --pf-radius: var(--radius-md);
    box-shadow: inset 0 0 0 1px var(--sheen-3), var(--shadow-3);
  }
  .pf--gold{ box-shadow: inset 0 0 0 1px var(--edge-gold), var(--shadow-2); }
  /* enquanto a imagem não chegou, a própria moldura é o esqueleto */
  .pf:has(.pf-img:not(.is-loaded))::after,
  .rank-thumb:has(.pf-img:not(.is-loaded))::after,
  .result-emoji:has(.pf-img:not(.is-loaded))::after{
    content:''; position:absolute; inset:0; z-index:1;
    background: linear-gradient(100deg, transparent 18%, var(--sheen-3) 50%, transparent 82%);
    transform: translateX(-100%);
    animation: skel-sweep 1.5s var(--ease-standard) infinite;
  }
  .pf-img{ z-index:2; }
  /* sem pôster: marca de filme desenhada, não emoji solto */
  .pf-fallback{
    display:flex; align-items:center; justify-content:center;
    width:100%; height:100%; color: var(--gold-dim);
  }
  .pf-fallback svg{ width:44%; height:auto; max-width:38px; opacity:.7; }

  /* ---- foco: um anel só ----
     Claro sobre superfície escura; sobre preenchimento dourado a polaridade
     inverte (anel escuro), que é a única forma de continuar visível ali.
     O anel é dourado CLARO (11:1 contra o preto mineral, contra 8,6:1 do
     dourado da marca) porque precisa vencer sobre painel, sobre pôster e
     sobre o fundo da sala com a mesma leitura. */
  :focus-visible{
    outline: var(--focus-ring-w) solid var(--focus-ring);
    outline-offset: var(--focus-ring-offset);
    border-radius: var(--focus-ring-radius);
  }
  /* Peça de preenchimento dourado: o anel troca de POLARIDADE e de LADO.
     A cor (`--focus-ring-on-gold`, #0d1017) foi escolhida para ser lida sobre o ouro —
     mas o deslocamento positivo do sistema (+2px) a pousava no fundo quase preto da
     tela, onde ela some (medido em 360x800: anel contra fundo abaixo de 1:1). Um anel
     invisível é o mesmo que anel nenhum, e estas são as ações decisivas do app.
     O deslocamento negativo põe o anel dentro da própria peça, que é onde a cor tem
     contraste. Achado do proprietário do Tutorial, que consertou a peça dele e
     escalou o resto; a correção global é da fundação. */
  .cta:focus-visible, .copy-btn:focus-visible, .btn--primary:focus-visible,
  .chip.selected:focus-visible, .result-btn.primary:focus-visible,
  .confirm-actions .primary:focus-visible{
    outline-color: var(--focus-ring-on-gold);
    outline-offset: calc(-2px - var(--focus-ring-w));
  }
  /* dentro de contêiner com overflow:hidden o anel externo é CORTADO — aqui ele
     entra pra dentro da peça em vez de sumir */
  .ring-inset:focus-visible, .provider-tabs .chip:focus-visible, .card .card-details:focus-visible, .detail-media-play:focus-visible {
    outline-offset:-3px;
  }
  /* ---- e o TERCEIRO caso do anel: um TÍTULO que recebe foco por programa ----
     O produto move o foco para um cabeçalho em três caminhos — o gesto de voltar
     (`focusScreenMasthead`), a abertura de Ajustes e a barra de destinos do painel.
     Um `h1`/`h2`/`h3` com `tabindex="-1"` não é controle: pôr anel de botão em volta de
     uma frase inteira lê como campo de formulário, e foi por isso que Ajustes desenhou
     uma marca própria. A marca está certa; o defeito era ela existir num lugar só — a
     comparação cega leu exatamente isso ("o título AJUSTES tem uma barra dourada que
     nenhum outro título do produto tem"). Aqui ela vira REGRA: todo título que recebe
     foco por programa ganha a mesma marca, e nenhum outro elemento ganha.
     É OURO TINTA, não metal — acento de estado, sem massa (ver GRAMÁTICA DO OURO).
     A altura é em `em` para acompanhar qualquer papel de título, de Bebas 34 px a um
     `h3` de 16 px, e `bottom` em vez de `height` para acompanhar títulos de duas linhas. */
  .is-focus-marked{ position:relative; }
  .is-focus-marked:focus{ outline:none; }
  .is-focus-marked::before{
    content:''; position:absolute; left:-10px; width:3px;
    top: calc(.16em + var(--focus-mark-inset, 0px));
    bottom: .16em;
    border-radius:3px; background:var(--gold);
  }
  /* o papel `.modal-title` reserva 8px de respiro no topo; a marca desce com ele para
     continuar rente à primeira linha, e não ao topo da caixa */
  .modal-title.is-focus-marked{ --focus-mark-inset:8px; }

  /* ===== momento de match: o clímax do produto ganha a tela inteira ===== */
  .match-takeover{
    position:fixed; inset:0; z-index:200;
    display:flex; flex-direction:column; align-items:center; justify-content:center;
    padding:26px 22px calc(26px + env(safe-area-inset-bottom));
    background:
      radial-gradient(ellipse at 50% 22%, rgba(var(--gold-rgb), .16), transparent 62%),
      /* opaco de verdade: com 96% a carta de trás (título, sinopse, a barra de cima)
         aparecia fantasma atrás do "Deu match!" */
      #06060b;
    /* overflow-x explícito: o clarão é mais largo que a tela de propósito, e overflow de
       elemento transformado é tratado de formas diferentes entre navegadores */
    text-align:center; overflow-y:auto; overflow-x:hidden;
    animation: mt-in .32s cubic-bezier(.16,1,.3,1) both;
    isolation:isolate;
  }
  @keyframes mt-in{ from{ opacity:0; } }
  /* O ESTOURO: um clarão dourado que nasce onde o pôster vai aparecer e se abre pra tela.
     É a ideia material da cena — as escolhas separadas convergiram num ponto e aquilo
     acende. Roda uma vez, em transform/opacity (nada de animar background-size, que
     repintaria a tela inteira a cada frame). */
  .match-takeover::before{
    content:''; position:absolute; left:50%; top:42%; z-index:-1;
    width:min(150vw, 620px); aspect-ratio:1; margin:-1px;
    transform:translate(-50%,-50%) scale(.35);
    border-radius:50%; pointer-events:none;
    background: radial-gradient(closest-side, rgba(var(--gold-rgb), .30), rgba(var(--gold-rgb), .09) 45%, transparent 72%);
    animation: mt-bloom .95s .1s cubic-bezier(.16,1,.3,1) both;
  }
  @keyframes mt-bloom{
    0%   { opacity:0; transform:translate(-50%,-50%) scale(.35); }
    35%  { opacity:1; }
    100% { opacity:.55; transform:translate(-50%,-50%) scale(1); }
  }
  /* A CELEBRAÇÃO CURTA veste o mesmo palco do clímax e muda só o que precisa: ela não tem
     ações, então não há o que esperar dentro dela, e sai por conta própria. O cursor de
     mão é o único aviso de que o toque adianta a saída. */
  .match-takeover--cheer{ cursor:pointer; transition:opacity var(--dur-2) var(--ease-exit); }
  .match-takeover--cheer.is-out{ opacity:0; }
  .mt-inner{ width:100%; max-width:330px; display:flex; flex-direction:column; align-items:center; }
  /* CENTRADO SEM PERDER O TOPO (1.67.0). Com `justify-content:center` num palco que rola, o
     que passa da altura transborda para OS DOIS LADOS, e o lado de cima não se alcança
     rolando: no aparelho deitado o "Todo mundo curtiu" ficava 31px acima da tela. A margem
     automática centra quando cabe e encosta no topo quando não cabe. */
  .match-takeover{ justify-content:flex-start; }
  .match-takeover > .mt-inner{ margin-block:auto; }
  .mt-kicker{
    font-size:12px; letter-spacing:2.5px; text-transform:uppercase; font-weight:800;
    color:var(--gold-dim); animation: mt-rise .4s .04s cubic-bezier(.16,1,.3,1) both;
  }
  /* Dois votos chegam verdes, convergem em ouro e liberam uma onda curta. O palco isola
     a luz ao redor da marca sem alterar o fluxo ou a geometria usada nos resultados. */
  .mt-mark-stage{
    position:relative; isolation:isolate; width:78px; height:48px; margin:8px 0 5px;
    display:grid; place-items:center; flex:none;
  }
  .mt-match-aura,
  .mt-match-ring{
    position:absolute; left:50%; top:50%; border-radius:50%; pointer-events:none;
    transform:translate(-50%,-50%); transform-origin:center;
  }
  .mt-match-aura{
    z-index:-2; width:150px; aspect-ratio:1; opacity:.3;
    background:radial-gradient(circle, rgba(var(--gold-rgb),.4), rgba(var(--gold-rgb),.09) 43%, transparent 70%);
    animation:mt-aura-bloom .72s .28s cubic-bezier(.16,1,.3,1) both;
  }
  .mt-match-ring{
    z-index:-1; width:70px; aspect-ratio:1; opacity:0;
    border:1px solid rgba(255,213,106,.58);
    animation:mt-ring-release .56s .38s cubic-bezier(.16,1,.3,1) both;
  }
  .match-takeover .match-mark--takeover{
    position:relative; z-index:1; display:block; width:78px; height:48px; overflow:visible;
    color:var(--gold-ink); transform-box:fill-box; transform-origin:center;
    animation:mt-mark-pulse .58s .48s cubic-bezier(.16,1,.3,1) both;
  }
  .match-takeover .match-mark-heart{
    transform-box:view-box; transform-origin:center;
  }
  .match-takeover .match-mark-heart--left{
    animation:mt-heart-left .44s .04s cubic-bezier(.16,1,.3,1) both;
  }
  .match-takeover .match-mark-heart--right{
    animation:mt-heart-right .44s .04s cubic-bezier(.16,1,.3,1) both;
  }
  .match-takeover .match-mark-intersection{
    transform-box:fill-box; transform-origin:center;
    animation:mt-heart-light .26s .34s cubic-bezier(.16,1,.3,1) both;
  }
  .match-takeover .match-mark-charge path{
    fill:none; stroke:#fff1b7; stroke-width:1.5; stroke-linecap:round;
    stroke-dasharray:1; stroke-dashoffset:1; opacity:0;
    animation:mt-charge-run .38s .38s cubic-bezier(.16,1,.3,1) both;
  }
  .match-takeover .match-mark-core{
    opacity:0; transform-box:fill-box; transform-origin:center;
    animation:mt-core-flash .34s .34s cubic-bezier(.16,1,.3,1) both;
  }
  /* ---- O CLÍMAX DO SOLO: UM CORAÇÃO SÓ ----
     Mesmo mundo do match (o clarão, a estampa, o pôster vêm da classe `.match-takeover`),
     outra marca. Ele nasce em 0.55 do tamanho e cresce até 1 — que é a medida da marca de
     match ao lado —, sai do VERDE do carimbo "curti" e chega ao OURO da casa. O círculo
     abre quando ele termina de crescer, e não antes: é a chegada que se comemora.
     `transform-box:fill-box` porque o que escala é o CAMINHO dentro do SVG, e sem isso a
     origem do transform seria o canto do viewBox — o coração cresceria para o lado. */
  .solo-takeover .solo-mark{
    position:relative; z-index:1; display:block; width:48px; height:48px; overflow:visible;
    color:var(--like-green);
  }
  .solo-takeover .solo-mark-heart{
    transform-box:fill-box; transform-origin:center;
    animation:solo-heart-grow .62s .06s cubic-bezier(.16,1,.3,1) both;
  }
  /* a aura e o anel são os MESMOS do match, atrasados para abrir na chegada do coração */
  .solo-takeover .mt-match-aura{ animation-delay:.5s; }
  .solo-takeover .mt-match-ring{ animation-delay:.56s; }
  @keyframes solo-heart-grow{
    0%{ color:var(--like-green); opacity:.45; transform:scale(.55); }
    55%{ color:#7bd3a2; opacity:1; }
    78%{ transform:scale(1.12); }
    100%{ color:var(--gold-ink); opacity:1; transform:scale(1); }
  }
  @media (prefers-reduced-motion: reduce){
    .solo-takeover .solo-mark-heart{ animation:none !important; }
    .solo-takeover .solo-mark{ color:var(--gold-ink); }
  }
  @keyframes mt-heart-left{
    0%{ color:var(--like-green); opacity:.5; transform:translate(-5px,1.5px) rotate(-7deg) scale(.88); }
    70%{ color:#70c994; opacity:1; }
    100%{ color:var(--gold-ink); opacity:1; transform:none; }
  }
  @keyframes mt-heart-right{
    0%{ color:var(--like-green); opacity:.5; transform:translate(5px,1.5px) rotate(7deg) scale(.88); }
    70%{ color:#70c994; opacity:1; }
    100%{ color:var(--gold-ink); opacity:1; transform:none; }
  }
  @keyframes mt-heart-light{
    0%{ opacity:0; transform:scale(.2); }
    58%{ opacity:1; transform:scale(1.18); }
    100%{ opacity:1; transform:scale(1); }
  }
  @keyframes mt-charge-run{
    0%{ opacity:0; stroke-dashoffset:1; }
    28%{ opacity:1; }
    82%{ opacity:.9; stroke-dashoffset:0; }
    100%{ opacity:0; stroke-dashoffset:0; }
  }
  @keyframes mt-core-flash{
    0%{ opacity:0; transform:scale(.2); }
    36%{ opacity:1; transform:scale(1.15); }
    100%{ opacity:0; transform:scale(1.7); }
  }
  @keyframes mt-mark-pulse{
    0%, 48%, 100%{ transform:scale(1); }
    22%{ transform:scale(1.1); }
    62%{ transform:scale(1.05); }
  }
  @keyframes mt-ring-release{
    0%{ opacity:0; transform:translate(-50%,-50%) scale(.28); }
    24%{ opacity:.85; }
    100%{ opacity:0; transform:translate(-50%,-50%) scale(1.7); }
  }
  @keyframes mt-aura-bloom{
    0%{ opacity:.1; transform:translate(-50%,-50%) scale(.48); }
    42%{ opacity:.72; }
    100%{ opacity:.3; transform:translate(-50%,-50%) scale(1); }
  }
  /* o título é revelado por máscara, do centro pras bordas — lê como carimbo batendo, não
     como texto subindo. clip-path não toca layout nem repinta os vizinhos. */
  .mt-title{
    font-family:'Bebas Neue', sans-serif; font-size:52px; line-height:.95; color:var(--gold-ink);
    letter-spacing:1.5px; margin-top:2px;
    animation: mt-stamp .5s .32s cubic-bezier(.16,1,.3,1) both;
  }
  @keyframes mt-stamp{
    from{ opacity:0; clip-path:inset(0 42% 0 42%); transform:scale(1.04); }
    to  { opacity:1; clip-path:inset(0 -6% 0 -6%); transform:scale(1); }
  }
  .mt-poster{
    width:170px; flex:none; aspect-ratio:2/3; margin:18px 0 14px; border-radius:14px; overflow:hidden;
    background: linear-gradient(160deg, var(--card-top), var(--card-bot));
    border:1px solid rgba(var(--gold-rgb), .5);
    box-shadow: 0 18px 44px rgba(0,0,0,.6);
    display:flex; align-items:center; justify-content:center; font-size:54px;
    animation: mt-land .6s .18s cubic-bezier(.16,1,.3,1) both;
  }
  .mt-poster img{ width:100%; height:100%; object-fit:cover; display:block; }
  /* o pôster ATERRISSA no ponto do clarão, com um assentamento curto de escala. É o objeto
     em que as escolhas convergiram, então ele chega como resultado, não deslizando de baixo. */
  @keyframes mt-land{
    0%   { opacity:0; transform:scale(.82); }
    60%  { opacity:1; transform:scale(1.035); }
    100% { opacity:1; transform:scale(1); }
  }
  .mt-name{ font-family:'Bebas Neue', sans-serif; font-size:27px; line-height:1.05; letter-spacing:.5px; animation: mt-rise .42s .30s cubic-bezier(.16,1,.3,1) both; }
  .mt-meta{ font-size:12px; color:var(--text-dim); margin-top:5px; animation: mt-rise .42s .34s cubic-bezier(.16,1,.3,1) both; }
  .mt-who{ font-size:13px; color:var(--gold-ink); margin-top:9px; font-weight:600; animation: mt-rise .42s .40s cubic-bezier(.16,1,.3,1) both; }
  .mt-actions{ width:100%; display:flex; flex-direction:column; gap:9px; margin-top:22px; animation: mt-rise .42s .40s cubic-bezier(.16,1,.3,1) both; }
  .mt-actions .cta{ margin-top:0; }
  .mt-secondary{
    display:flex; gap:9px;
  }
  .mt-secondary button{
    flex:1; padding:12px; border-radius:12px; border:1px solid var(--line);
    background:rgba(var(--bg-panel-rgb), .8); color:var(--text); font-size:13px; font-weight:700;
    cursor:pointer; font-family:inherit;
  }
  /* "CONTINUAR" É A PORTA DE QUEM NÃO QUER DECIDIR AGORA (1.55.0, o dono achou discreto):
     as duas metades eram gêmeas, e a que mantém a rodada viva lia como a menos importante.
     Ela ganha a aresta e a tinta de ouro — sem PREENCHER, porque ouro cheio aqui é o
     "Assistir", e a tela não oferece duas ações douradas. */
  #mtKeep, #soloKeep{ border-color:rgba(var(--gold-rgb), .55); color:var(--gold-ink); }
  /* celular estreito: "Continuar escolhendo" quebrava em duas linhas numa das duas metades */
  @media (max-width: 380px){
    .mt-secondary button{ padding:12px 6px; font-size:12.5px; white-space:nowrap; }
  }
  @keyframes mt-rise{ from{ opacity:0; transform: translateY(14px); } }
  /* telas curtas (celular pequeno, aparelho deitado): o pico continua cabendo inteiro */
  @media (max-height: 640px){
    .match-takeover{ padding-top:16px; padding-bottom:16px; }
    .mt-mark-stage{ width:62px; height:38px; margin:5px 0 3px; }
    .match-takeover .match-mark--takeover{ width:62px; height:38px; }
    .mt-title{ font-size:38px; }
    .mt-poster{ width:118px; margin:11px 0 9px; }
    .mt-name{ font-size:22px; }
    .mt-actions{ margin-top:15px; }
  }
  @media (prefers-reduced-motion: reduce){
    .match-takeover .match-mark--takeover,
    .match-takeover .match-mark-heart,
    .match-takeover .match-mark-intersection,
    .match-takeover .match-mark-charge path,
    .match-takeover .match-mark-core,
    .match-takeover .mt-match-aura,
    .match-takeover .mt-match-ring{
      animation:none !important;
    }
    .match-takeover .match-mark-heart{
      color:var(--gold-ink); opacity:1; transform:none;
    }
    .match-takeover .match-mark-intersection{ opacity:1; transform:none; }
    .match-takeover .match-mark-charge path,
    .match-takeover .match-mark-core,
    .match-takeover .mt-match-ring{
      opacity:0;
    }
  }

  /* texto só para leitores de tela */
  .sr-only{
    position:absolute; width:1px; height:1px; padding:0; margin:-1px;
    overflow:hidden; clip:rect(0 0 0 0); white-space:nowrap; border:0;
  }
  /* o sistema de foco mora na fundação, lá em cima — um anel só pro app inteiro */
  /* estados de hover — só em aparelhos com ponteiro de verdade */
  @media (hover:hover){
    .chip:not(.selected):hover{ border-color: var(--gold-dim); color: var(--text); }
    .genre-disclosure:hover{ border-color:var(--gold-dim); }
    .mode-btn:not(.selected):hover, .social-btn:hover, .back-link:hover,
    .gear-btn:hover, .account-pill:hover, .result-btn:not(.primary):hover, .modal-close:hover{ border-color: var(--gold-dim); color: var(--text); }
    .cta:hover, .copy-btn:hover, .result-btn.primary:hover{ filter: brightness(1.07); }
    .action-btn:hover{ transform: scale(1.06); }
    .tab-btn:hover{ color: var(--text); }
    .tab-btn.active:hover{ color: var(--gold-ink); }
    .history-remove-btn:hover, .detail-overlay-close:hover{ color: var(--rose); border-color: var(--rose); }
    .runner-add-btn:hover{ background: rgba(var(--gold-rgb), .12); }
    /* linha inteira é alvo: com ponteiro de verdade ela precisa dizer isso antes do clique.
       Uma regra só — a saída para documento externo deixou de ser peça à parte. */
    .settings-nav-row:hover,
    .settings-details summary:hover{ background: var(--sheen-1); }
    .settings-nav-row:hover .settings-nav-go{ color: var(--gold-dim); }
  }
  /* ---- redução de movimento: alternativa intencional, não só zerar ----
     Zerar tudo é o piso: o estado final continua sendo o mesmo e nenhuma
     informação depende de ver o percurso. Mas o que o movimento COMUNICAVA
     precisa continuar dito por outro meio — abaixo, caso a caso. */
  @media (prefers-reduced-motion: reduce){
    *, *::before, *::after{
      animation-duration:.01ms !important;
      animation-iteration-count:1 !important;
      transition-duration:.01ms !important;
    }
    /* esqueleto: sem varredura, a MESMA faixa de luz fica parada em diagonal
       sobre o material. Continua dizendo "isto ainda não é conteúdo" — a
       informação era a faixa, não o percurso dela. */
    .skel::after,
    .pf:has(.pf-img:not(.is-loaded))::after,
    .rank-thumb:has(.pf-img:not(.is-loaded))::after,
    .result-emoji:has(.pf-img:not(.is-loaded))::after{
      animation:none !important; transform:none;
      background: linear-gradient(100deg, transparent 26%, var(--sheen-2) 52%, transparent 76%);
    }
  }
  body{
    /* O HALO DO TOPO É UM TOKEN, e não um hex. Ele era #14151d fixo: no tema claro o app
       ganhava uma sombra escura por baixo de tudo, invisível nas telas cheias e evidente
       atrás de qualquer folha translúcida. Foi a varredura de contraste que o encontrou. */
    background: radial-gradient(ellipse at top, var(--bg-halo) 0%, var(--bg) 55%);
    color: var(--text);
    font-family: ui-sans-serif, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
    min-height: 100vh;
    display:flex;
    justify-content:center;
    overflow-x:hidden;
  }
  .phone{
    width:100%;
    max-width:420px;
    height:100vh;
    height:100dvh; /* considera as barras dinâmicas do navegador mobile */
    overflow-y:auto;
    overflow-x:hidden;
    scrollbar-width:none;
    -ms-overflow-style:none;
    background: var(--bg);
    position:relative;
    display:flex;
    flex-direction:column;
  }
  .phone::-webkit-scrollbar{ width:0; height:0; display:none; }
  /* NENHUMA BARRA DESENHADA, EM LUGAR NENHUM. Cada região rolável já escondia a sua
     (`.phone`, `#setup`, `#history`, a folha dos Ajustes, os trilhos de chips), mas a regra
     estava espalhada por uma dúzia de blocos: bastava uma região nova esquecer a linha para
     a barra clássica do Windows/Android aparecer sobre o conteúdo — que é o que o dono viu
     no Ranking. Aqui ela deixa de ser lembrete e passa a ser padrão da folha. O GESTO não
     muda: rolar continua rolando, e o que some é só o desenho da barra. */
  *{ scrollbar-width:none; -ms-overflow-style:none; }
  *::-webkit-scrollbar{ width:0; height:0; display:none; }
  .topbar{
    display:flex; align-items:center; justify-content:space-between;
    padding: 18px 20px 14px;
    border-bottom: 1px solid var(--line);
    position:sticky; top:0; z-index:60;
    background: rgba(var(--bg-rgb), .92);
    backdrop-filter: blur(8px);
    -webkit-backdrop-filter: blur(8px);
  }
  /* durante a votação o chrome inteiro sai de cena — barra de cima E abas. Logo, conta e
     engrenagem não servem a ninguém no meio de um card, e custavam 77px de altura na única
     tela do app que não rola. As abas saem pelo motivo oposto: com o celular passando de
     mão em mão, um destino a um toque de distância é convite pra pessoa errada abandonar a
     vez de outra. A saída nomeada da régua ("Sair") é a única porta, e ela guarda a rodada.
     Vale só enquanto há card na mão: no resultado e nas telas de espera o chrome volta. */
  body.in-round .topbar,
  body.in-round .tabbar{ display:none !important; }

  /* ===== splash de abertura ===== */
  #splash{
    position:fixed; inset:0; z-index:999;
    background:var(--bg);
    display:flex; flex-direction:column; align-items:center; justify-content:center;
    gap:22px;
    transition: opacity .5s ease;
  }
  #splash.hide{ opacity:0; pointer-events:none; }
  .splash-glow{
    position:absolute; top:22%; left:50%; transform:translate(-50%,-50%);
    width:340px; height:340px; border-radius:50%;
    background: radial-gradient(closest-side, rgba(var(--gold-rgb), .14), transparent);
    animation: splash-glow 2.4s ease-in-out infinite;
  }
  @keyframes splash-glow{ 50%{ opacity:.55; transform:translate(-50%,-50%) scale(1.12); } }
  .splash-logo{
    width:150px; height:150px;
    filter: drop-shadow(0 14px 30px rgba(0,0,0,.55));
    animation:
      splash-pop .8s cubic-bezier(.16,1,.3,1) both,
      splash-beat 2s 1s ease-in-out infinite;
  }
  @keyframes splash-pop{
    0%{ transform: scale(.3) rotate(-22deg); opacity:0; }
    70%{ transform: scale(1.07) rotate(3deg); opacity:1; }
    100%{ transform: scale(1) rotate(0deg); opacity:1; }
  }
  @keyframes splash-beat{
    0%, 100%{ transform: scale(1); }
    12%{ transform: scale(1.05); }
    24%{ transform: scale(1); }
    36%{ transform: scale(1.04); }
    48%{ transform: scale(1); }
  }
  .splash-title{
    font-family:'Bebas Neue', sans-serif;
    font-size:32px; color:var(--gold-ink); line-height:1;
    animation: splash-title .6s .5s ease both;
  }
  @keyframes splash-title{
    from{ opacity:0; transform:translateY(12px); letter-spacing:16px; }
    to{ opacity:1; transform:none; letter-spacing:7px; }
  }
  .splash-dots{ display:flex; gap:7px; margin-top:2px; animation: splash-title .4s .9s ease both; }
  .splash-dots i{
    width:7px; height:7px; border-radius:50%; background:var(--gold-dim);
    animation: splash-dot 1.1s ease-in-out infinite;
  }
  .splash-dots i:nth-child(2){ animation-delay:.18s; }
  .splash-dots i:nth-child(3){ animation-delay:.36s; }
  @keyframes splash-dot{ 40%{ transform:translateY(-6px); background:var(--gold); } }
  @media (prefers-reduced-motion: reduce){
    #splash *{ animation:none !important; opacity:1; }
  }
  .logo{
    font-family:'Bebas Neue', sans-serif;
    font-size: 28px;
    letter-spacing: 2px;
    color: var(--gold-ink);
    /* Bebas Neue só tem um peso; sem isto o navegador sintetiza um negrito falso */
    font-weight: 400;
  }
  .logo span{ color: var(--text); }

  /* o <main> é só o landmark: repassa a altura do .phone pras telas sem criar caixa própria.
     min-height:0 mantém a cadeia de flex que o #swipe usa pra não rolar */
  .screens{ flex:1; display:flex; flex-direction:column; min-height:0; }
  .screen{ flex:1; display:none; flex-direction:column; padding: 20px; padding-bottom: 130px; }
  .screen.active{ display:flex; }
  .setup-people-column{ min-width:0; margin-bottom:12px; }
  /* a tela de swipe não rola: o card se adapta e as ações ficam sempre visíveis */
  #swipe.active{
    min-height:0;
    overflow:hidden;
    padding-bottom: calc(86px + env(safe-area-inset-bottom));
  }
  /* no resultado, a régua de rodada está vazia e não deve reservar uma faixa fixa.
     O respiro inicial entra na própria lista: aparece ao abrir e sai naturalmente ao rolar. */
  #swipe.showing-results{ padding-top:0; }
  #swipe.showing-results .round-bar{ display:none; }
  #swipe.showing-results .results-wrap{ padding-top:20px; }

  /* SETUP SCREEN */
  /*
    THESIS: CineMatch é uma mesa de decisão, não uma pilha de filtros equivalentes.
    OWN-WORLD: preto mineral, divisórias de sala e ouro apenas para decisão e estado.
    STORY: retomar vem primeiro; sem rodada, pessoas, onde assistir e refinamento conduzem à ação.
    FIRST VIEWPORT: promessa editorial curta, modo recomendado e CTA contextual na zona do polegar.
    FORM: Operate premium em fluxo linear, três intenções no DOM, preservando o fluxo legado.
  */
  #setup{
    /* os nomes locais continuam, mas agora derivam da fundação: um sistema só */
    --setup-surface: var(--surface-1-top);
    --setup-surface-raised: var(--surface-2-top);
    --setup-divider: var(--edge);
    --setup-radius: var(--radius-lg);
    overflow-y:auto;
    scrollbar-width:none;
    -ms-overflow-style:none;
    /* quem reserva o respiro é a própria zona de ação (margin-bottom), não um resto
       de padding da tela: assim a folga acima da navegação é a mesma com o conteúdo
       curto (a zona repousa no fim) e com o conteúdo longo (ela gruda) */
    padding-bottom: 0;
    /* a última região sempre pode ser rolada até ficar INTEIRAMENTE livre do véu: sem
       isto, rolar até o fim ainda parava com a região sob a zona de ação */
    scroll-padding-bottom: calc(var(--dock-space) + var(--dock-veil) + 64px);
  }
  #setup::-webkit-scrollbar{ display:none; }

  /* ===== faixa de entrada: o palco =====
     Não é mais um bloco de texto sobre o preto: é uma faixa que sangra até as
     bordas da tela, recebe a luz pela quina de cima e fecha embaixo com a régua
     de ouro. É o mesmo sistema de luz do material — não é glow decorativo. */
  /* O palco é uma GRADE desde o celular: promessa à esquerda, cartaz à direita. Empilhada,
     a arte era uma tira embaixo do texto e o palco crescia; lado a lado, o pôster triplica
     de tamanho e a faixa fica MAIS baixa do que era com as três miniaturas. */
  /* ---- O PALCO É O OBJETO QUE GOVERNA ----
     A comparação cega mediu o defeito: em 390x844 a mesa de decisões ocupava
     100.034px² e era o ÚNICO objeto com material levantado da tela, contra 31.541px²
     de um palco que era faixa chapada sobre o preto. Quem governava a composição era
     um formulário — e formulário não hierarquiza sozinho.
     A correção não tira controle nenhum da tela: ela troca QUEM veste o material.
     O palco passa a ser um painel pendurado — sangra no topo e nos lados, fecha em
     raio embaixo — com gradiente, aresta iluminada e queda própria; as três regiões
     de decisão descem para o mesmo tratamento de linha sob régua que os dois resumos
     já tinham. Um objeto levantado, o resto carvado. (Antes o levantado era a mesa.)
     O raio embaixo também responde ao "retângulo de sangria total com arestas duras
     de 1px num layout onde todo o resto é arredondado": a faixa deixa de ser listra
     de fundo e vira peça com forma. */
  .home-marquee{
    position:relative;
    display:grid; grid-template-columns:minmax(0,1fr) auto;
    align-items:end; column-gap:16px;
    margin:-20px -20px 16px; padding:16px 20px 15px;
    border-radius:0 0 var(--radius-xl) var(--radius-xl);
    background: linear-gradient(180deg, var(--surface-1-top), var(--surface-1-bot));
    box-shadow: var(--shadow-2);
  }
  /* sem arte no aparelho a promessa ocupa a faixa inteira */
  .home-marquee:not(:has(.marquee-shelf:not([hidden]))){ grid-template-columns:minmax(0,1fr); }
  .home-marquee::before{
    content:''; position:absolute; inset:0; pointer-events:none; z-index:1;
    border-radius:inherit;
    background:
      radial-gradient(132% 116% at 6% -34%, rgba(var(--gold-rgb), .11), transparent 58%),
      linear-gradient(180deg, rgba(255,255,255,.035), transparent 46%);
  }
  /* a régua de ouro acompanha o raio: encostada nos cantos ela voltaria a ser uma
     aresta reta atravessando uma peça arredondada. Recuada até onde a curva começa,
     ela fecha o palco e some antes de bater na quina. */
  .home-marquee::after{
    content:''; position:absolute; left:var(--radius-xl); right:var(--radius-xl); bottom:0; height:1px; z-index:2;
    background: linear-gradient(90deg, transparent, var(--edge-gold) 10%, var(--edge-gold) 76%, transparent);
  }
  /* MATERIAL VINDO DE IMAGEM: o mesmo pôster que está no cartaz pinta o fundo do palco,
     borrado e sob véu. É o que separa "cartaz de cinema" de "caixa com miniatura" — e ele
     só existe quando existe arte de verdade, porque não é enfeite. O véu é opaco na
     coluna do texto: o contraste da promessa nunca depende da imagem que caiu ali. */
  .marquee-art{
    position:absolute; inset:0; z-index:0; overflow:hidden; pointer-events:none;
    border-radius:inherit;
    opacity:0; transition: opacity var(--dur-3) var(--ease-entrance);
  }
  .marquee-art::before{
    content:''; position:absolute; inset:-20%;
    background: var(--marquee-art, none) center 30% / cover no-repeat;
    filter: blur(24px) saturate(.8);
  }
  .marquee-art::after{
    content:''; position:absolute; inset:0;
    background: linear-gradient(90deg,
      var(--bg) 9%, rgba(var(--bg-rgb), .9) 42%, rgba(var(--bg-rgb), .6) 100%);
  }
  .home-marquee.has-art .marquee-art{ opacity:1; }
  @media (prefers-reduced-motion: reduce){ .marquee-art{ transition:none; } }
  .marquee-copy{ position:relative; z-index:3; min-width:0; }
  .marquee-shelf{ position:relative; z-index:3; min-width:0; margin:0; }
  .marquee-shelf[hidden]{ display:none; }
  /* UMA peça, em tamanho de cartaz. Três molduras escalonadas não tinham eixo comum —
     nem topo nem base — e uma fileira de arte sem linha compartilhada lê como acidente. */
  .shelf-frames{ --shelf-h:152px; display:flex; align-items:flex-end; }
  .shelf-frame{
    position:relative;
    height:var(--shelf-h); width:auto; --pf-radius:var(--radius-sm);
    box-shadow: inset 0 0 0 1px var(--edge-gold), var(--shadow-2);
  }
  /* o alvo não tem material próprio: quem se apresenta como peça tocável é o cartaz, e
     um botão desenhado em volta dele viraria moldura dentro de moldura */
  .shelf-open{
    display:flex; align-items:flex-end;
    padding:0; border:0; background:none; font:inherit; color:inherit; cursor:pointer;
  }
  .shelf-open:focus-visible{ outline-offset:4px; }
  /* O CARTAZ DE TRÁS. Diz "foi mais de um" sem contador — a mesma frase que o leque do
     Histórico diz na lista, aqui inclinada porque a peça é grande e cabe o gesto. Ele
     recua em três eixos ao mesmo tempo (menor, mais escuro, girado), porque um só desses
     recuos deixa o par lendo como duas peças de igual peso em vez de uma atrás da outra.
     O giro tem origem no canto de baixo à direita: é o ponto que o cartaz da frente
     cobre, então a inclinação abre para a ESQUERDA, para dentro do palco, e não para a
     borda da tela. */
  .shelf-frame--tras{
    height:calc(var(--shelf-h) * .84);
    margin-right:calc(var(--shelf-h) * -.34);
    transform:rotate(-7deg); transform-origin:100% 100%;
    filter:brightness(.6) saturate(.8);
    box-shadow: inset 0 0 0 1px var(--edge), var(--shadow-1);
  }
  .shelf-frame--frente{ z-index:1; }
  /* ---- TRÊS CARTAZES, sem o leque crescer ----
     Com três ou mais títulos a peça empilha três. Os dois de trás se escondem MAIS do que
     o único de trás do par (sobra de 22px e 12px, contra 33px no par), e é isso que mantém
     o cartaz da frente no mesmo lugar: medida a medida, o trio ocupa 136px contra os 135px
     do par. Se o recuo fosse o mesmo do par, a terceira peça empurraria o leque 45px para
     dentro do palco e a frente sairia do eixo que ela tem hoje.
     O giro cresce com a profundidade (-5° e -11° contra -7° do par): quem está mais atrás
     está mais tombado, que é como uma pilha de fotos na mesa se comporta. */
  .shelf-frames--trio .shelf-frame--tras{
    height:calc(var(--shelf-h) * .88);
    margin-right:calc(var(--shelf-h) * -.44);
    transform:rotate(-5deg);
  }
  .shelf-frames--trio .shelf-frame--fundo{
    height:calc(var(--shelf-h) * .78);
    margin-right:calc(var(--shelf-h) * -.44);
    transform:rotate(-11deg); transform-origin:100% 100%;
    filter:brightness(.46) saturate(.7);
    box-shadow: inset 0 0 0 1px var(--edge), var(--shadow-1);
  }
  /* título sem pôster: tipografado dentro da moldura em vez de moldura muda */
  .shelf-frame .pf-fallback{ flex-direction:column; gap:6px; padding:10px; text-align:center; }
  .shelf-frame .pf-fallback svg{ width:auto; max-width:26px; }
  .shelf-frame-title{
    max-width:100%; color:var(--text-soft); font-size:11px; line-height:1.3;
    overflow:hidden; display:-webkit-box; -webkit-line-clamp:4; -webkit-box-orient:vertical;
  }
  .shelf-note{
    margin-top:8px; color:var(--text-dim); text-align:right;
    font-size:var(--t-support-size); line-height:var(--t-support-lh);
  }
  .shelf-note b{ color:var(--text-soft); font-weight:600; }
  /* EXCEÇÃO de caixa alta: é a única sobrescrita editorial do app, e ela existe
     pra ser lida ANTES do título — o papel só funciona em caixa alta */
  .setup-kicker{
    color:var(--gold-dim); margin-bottom:7px;
    font-size:var(--t-label-size); line-height:var(--t-label-lh);
    letter-spacing:var(--t-label-track); font-weight:var(--t-label-weight);
    text-transform:uppercase;
  }
  h1.title{
    font-family:var(--font-display);
    font-size: 34px;
    letter-spacing:var(--t-display-track);
    line-height:1.05;
    margin-bottom:6px;
  }
  p.sub{ color:var(--text-dim); font-size:var(--t-body-size); line-height:var(--t-body-lh); margin-bottom:22px; }
  #setup h1.title{ max-width:13ch; font-size:var(--t-display-size); line-height:var(--t-display-lh); letter-spacing:var(--t-display-track); text-wrap:balance; }
  #setup p.sub{ max-width:48ch; margin-bottom:0; }
  /* era rótulo de formulário em caixa alta repetido em toda seção — o que
     achatava a hierarquia inteira. Vira TÍTULO DE GRUPO: caixa normal, tamanho
     e peso próprios, cor de texto. A caixa alta some daqui e vira exceção. */
  .section-label{
    margin: 18px 0 10px; color: var(--text);
    font-size:var(--t-group-size); line-height:var(--t-group-lh);
    letter-spacing:var(--t-group-track); font-weight:var(--t-group-weight);
  }
  .chip-grid{ display:flex; flex-wrap:wrap; gap:8px; }
  .chip{
    padding:6px 16px; min-height:44px; border-radius:var(--radius-pill);
    border:1px solid var(--edge); border-top-color:var(--edge-lit);
    background: linear-gradient(180deg, var(--surface-2-top), var(--surface-2-bot));
    box-shadow: inset 0 1px 0 var(--sheen-1);
    color:var(--text-dim); font-size:13px;
    cursor:pointer;
    transition: background-color var(--dur-1) var(--ease-standard),
                border-color var(--dur-1) var(--ease-standard),
                color var(--dur-1) var(--ease-standard),
                transform var(--dur-1) var(--ease-standard);
    font-weight:500;
    font-family:inherit;
    display:inline-flex; align-items:center; justify-content:center;
  }
  .chip.selected{
    background: var(--gold-metal); color:var(--on-gold);
    border-color:var(--gold-edge); font-weight:700;
    box-shadow: inset 0 1px 0 var(--gold-sheen);
  }
  /* desabilitado com cor explícita, não opacity: estes estados carregam a
     explicação do que está bloqueado, então precisam continuar legíveis */
  .chip-disabled{
    color:var(--text-disabled); background:transparent; border-color:var(--line-disabled);
    border-style:dashed; pointer-events:none; cursor:not-allowed;
  }
  .chip-hint{ font-size:var(--t-support-size); color:var(--text-dim); margin:8px 0 0; line-height:var(--t-support-lh); }
  .genre-disclosure{
    width:100%; min-height:52px; margin-top:18px; padding:11px 2px;
    display:flex; align-items:center; justify-content:space-between; gap:12px;
    border:0; border-top:1px solid var(--rule); border-bottom:1px solid var(--rule);
    background:transparent; color:var(--text); text-align:left; cursor:pointer;
  }
  .genre-disclosure b{ display:block; font-size:13px; }
  .genre-disclosure small{ display:block; margin-top:3px; color:var(--text-dim); font-size:12px; }
  .genre-disclosure svg{ color:var(--gold-dim); transition:transform var(--dur-2) var(--ease-standard); }
  .genre-disclosure[aria-expanded="true"] svg{ transform:rotate(180deg); }
  /* logo depois de uma linha de ajuste, a régua de cima já é a da linha: duas réguas a 20px
     uma da outra liam como um defeito de desenho */
  .setting-row + .genre-disclosure{ border-top:0; margin-top:0; }
  .genre-disclosure-panel{ padding-top:12px; }
  /* Com a aba Partida fora do ar, esta faixa é a ÚNICA porta de volta pra uma rodada
     aberta — vale tanto pra quem tocou em "Sair" agora quanto pra quem fechou o app e
     voltou depois. Por isso ela mora acima das três decisões: quem tem rodada pendente
     precisa vê-la antes de começar outra por engano. */
  /* Ações na mesma linha do título, e a frase de onde a rodada parou logo abaixo ocupando
     a largura toda. As três coisas lado a lado espremiam "Estéfano parou no card 8/15"
     numa coluna de 115px; empilhadas, os botões ficavam longe do rótulo que explicam. */
  .active-round-strip{
    margin:0 0 18px; padding:16px;
    border:1px solid var(--gold-dim); border-top-color:var(--gold-edge);
    border-radius:var(--setup-radius);
    background:var(--gold-tint-deep);
    box-shadow: var(--shadow-2), inset 0 1px 0 rgba(var(--gold-rgb), .14);
  }
  .active-round-strip[hidden]{ display:none; }
  /* "Rodada em andamento" + os dois botões pedem 301px e a faixa tem 307 num iPhone de
     375: cabe, com os botões enxutos. Num aparelho mais estreito não cabe — daí o wrap,
     que desce as ações pra linha de baixo em vez de picotar o título em duas linhas. */
  .ars-head{ display:flex; flex-wrap:wrap; align-items:center; gap:6px; }
  .active-round-strip b{ flex:1 1 auto; white-space:nowrap; color:var(--gold-ink); font-size:14px; }
  .active-round-strip span{ display:block; margin-top:7px; color:#c6b994; font-size:12px; line-height:1.45; }
  .ars-actions{ display:flex; align-items:center; gap:2px; flex:none; }
  /* as salas em aberto vestem a MESMA faixa da rodada em andamento: é a mesma situação
     vista por quem usa ("tenho uma coisa começada aqui"). Os botões não levam o ouro cheio
     — a tela inicial nunca oferece duas ações douradas, e o CTA principal já é uma */
  .salas-abertas:empty{ display:none; }
  .salas-abertas .ars-actions{ gap:6px; }
  .ars-actions .result-btn{ flex:none; padding:11px; }
  /* descartar é o caminho destrutivo: nomeado (um ✕ ao lado de "Continuar" não diz se
     fecha a faixa ou joga a rodada fora) e sem peso visual nenhum */
  .ars-discard{
    flex:none; min-height:44px; padding:0 6px;
    border:0; background:transparent; color:#d08a98;
    font-size:12px; font-weight:700; cursor:pointer;
  }
  /* material nível 1: a mesa onde as decisões acontecem */
  .setup-region{
    min-width:0; padding:15px 16px;
    border:1px solid var(--edge); border-top-color:var(--edge-lit);
    border-radius:var(--setup-radius);
    background: linear-gradient(180deg, var(--surface-1-top), var(--surface-1-bot));
    box-shadow: var(--shadow-1), inset 0 1px 0 var(--sheen-1);
  }
  .setup-region + .setup-region{ margin-top:12px; }
  .setup-region-head{ display:flex; align-items:center; justify-content:space-between; gap:12px; margin-bottom:11px; }
  .setup-region .section-label{ margin:0; }
  .setup-region-note{ color:var(--text-dim); font-size:var(--t-support-size); line-height:1.35; text-align:right; }
  /* resumo do que está configurado no modal, visível já na tela inicial.
     SEGUNDO (e último) idioma de controle: a LINHA que abre um painel. Sem caixa própria —
     caixa dentro de caixa era o que dava aos três cards o mesmo peso e achatava a tela.
     A régua da região já separa; o "Mudar" já diz que abre. */
  .filter-summary{
    /* os três filhos no MESMO eixo: pelo topo, o texto subia para o alto dos 44px e o
       "Mudar" (centrado) ficava 5px abaixo dele — medido em 360x800 (1.54.0) */
    display:flex; align-items:center; gap:9px; width:100%; margin-top:0;
    padding:10px 8px; margin-inline:-8px; width:calc(100% + 16px);
    min-height:44px; border-radius:var(--radius-sm);
    border:1px solid transparent; background:none; box-shadow:none;
    color:var(--text-dim); font-family:inherit;
    font-size:var(--t-support-size); text-align:left; cursor:pointer; line-height:1.4;
  }
  /* ---- REGRA DO OURO NESTA SUPERFÍCIE ----
     Ouro é a ESCOLHA ativa e a AÇÃO decisiva. Só isso. Ele estava acumulando quatro
     funções — marca, ícone de categoria, seleção e o link "Mudar" —, e com quatro papéis
     na mesma tela nada se destaca por cor: o CTA dourado deixava de ser o objeto mais
     quente da composição. O ícone marca o assunto da linha (não é decisão) e "Mudar" é o
     rótulo de um controle que já é a linha inteira, com 44px de alvo, hover e anel de
     foco — a afordância não depende da cor. */
  .filter-summary > .ico-svg{ color:var(--text-dim); margin-top:1px; }
  .filter-summary b{ color:var(--text); font-weight:600; }
  /* a viúva "· relevância" sozinha numa segunda linha lia como item de lista, não como o
     fim de uma frase. O bloco de valor é filho de um flex (portanto bloco de verdade), e
     equilibrar as linhas resolve na raiz: nenhuma linha sobra com um fragmento. */
  .filter-summary > span{ text-wrap:balance; }
  /* o link vinha preso à primeira linha de um bloco de DUAS, deixando um buraco embaixo
     dele; ele pertence à linha inteira, então se alinha ao centro dela */
  .filter-summary .fs-edit{ margin-left:auto; align-self:center; color:var(--text-soft); font-weight:700; flex:none; }
  /* pisca quando a rodada é bloqueada por falta de streaming: liga o aviso ao lugar de
     resolver. Desde o sofá (1.54.0) o lugar é a TV: a tela dela ganha a borda rosa da nota
     que aparece logo embaixo, e pisca duas vezes */
  .tv.needs-attention .tv-tela{ box-shadow:inset 0 0 0 2px var(--rose); animation:needs-attention .5s ease 2; }
  @keyframes needs-attention{ 50%{ background:rgba(var(--rose-rgb), .22); } }
  @media (prefers-reduced-motion: reduce){ .tv.needs-attention .tv-tela{ animation:none; } }
  /* ---- P3-H12 · uma saída nomeada por destino ----
     Com a rodada bloqueada, "Mudar" (na linha antiga) e "Escolher" (na peça de aviso)
     chamavam o mesmo painel a 35px um do outro: dois rótulos, um destino. A TV do sofá
     (1.54.0) não escreve "Mudar" nenhum — ela é o objeto que se toca —, então a única
     saída NOMEADA continua sendo o "Escolher" da peça que nomeia a consequência. A TV
     segue sendo botão, com o nome acessível completo. */
  /* o aviso de rodada bloqueada: peça de FLUXO dentro da região que ele explica. A régua
     rosa à esquerda liga o aviso à borda rosa da linha de cima sem repetir a moldura
     inteira, e a saída fica no mesmo objeto. */
  .setup-block-note{
    display:flex; align-items:center; gap:10px;
    margin-top:8px; padding:9px 10px 9px 12px;
    /* contorno inteiro, não faixa lateral (1.54.0): a peça mora embaixo da TV, e é a borda
       rosa DELA que liga o aviso à causa */
    border:1px solid rgba(var(--rose-rgb), .38);
    border:1px solid color-mix(in srgb, var(--rose) 40%, transparent);
    border-radius:var(--radius-sm);
    background:rgba(var(--rose-rgb), .07);
    color:var(--text); font-size:var(--t-support-size); line-height:var(--t-support-lh);
  }
  .setup-block-note[hidden]{ display:none; }
  .setup-block-note span{ flex:1; min-width:0; text-wrap:pretty; }
  .setup-block-note button{
    flex:none; min-height:44px; padding:6px 10px;
    border:0; background:none; cursor:pointer; font-family:inherit;
    /* o rosa de traço (#c4485e) dá 3,6:1 sobre o preto e reprova em AA num rótulo de
       13px; quem tem papel de TEXTO na gramática rosa é o --btn-danger-fg (10,9:1) */
    color:var(--btn-danger-fg); font-size:13px; font-weight:800; letter-spacing:.2px;
  }
  /* toast com ação (desfazer) — acima do que estiver ancorado no rodapé, NUNCA cobrindo o
     CTA. O `bottom` daqui é só o piso (nada ancorado além da navegação): quem manda é o
     `toastBottomOffset()`, que MEDE a peça ancorada em vez de adivinhar a tela. O valor
     fixo antigo tapava o botão de começar inteiro quando a home ganhou zona fixa. */
  .app-toast{
    position:fixed; left:50%; transform:translateX(-50%);
    bottom: calc(84px + env(safe-area-inset-bottom));
    width:calc(100% - 32px); max-width:388px; z-index:150;
    display:flex; align-items:center; gap:12px;
    background: linear-gradient(180deg, var(--surface-3-top), var(--surface-3-bot));
    border:1px solid var(--edge-strong); border-top-color:var(--edge-lit);
    border-radius:var(--radius-md);
    padding:12px 14px; font-size:13px; color:var(--text); line-height:1.4;
    box-shadow: var(--shadow-3), inset 0 1px 0 var(--sheen-3);
    animation: toast-in var(--dur-3) var(--ease-entrance) both;
  }
  .app-toast span{ flex:1; }
  .app-toast button{
    flex:none; background:none; border:none; cursor:pointer;
    color:var(--gold-ink); font-weight:800; font-size:13px;
    letter-spacing:.2px;
    /* é a única forma de desfazer um afrouxamento de filtros: 27px de altura era
       alvo de precisão pra uma ação com prazo de 8 segundos */
    min-height:44px; padding:6px 10px; display:flex; align-items:center;
  }
  @keyframes toast-in{ from{ opacity:0; transform:translateX(-50%) translateY(10px); } }
  .mode-toggle{ display:flex; gap:10px; margin-top:6px; }
  .mode-btn{
    flex:1; min-height:44px; padding:12px; border-radius:var(--radius-md);
    border:1px solid var(--edge); border-top-color:var(--edge-lit);
    background: linear-gradient(180deg, var(--surface-2-top), var(--surface-2-bot));
    box-shadow: inset 0 1px 0 var(--sheen-2);
    color:var(--text-dim); cursor:pointer; text-align:left;
    font-family:inherit;
  }
  .mode-btn.selected{
    border-color: var(--gold-dim); border-top-color: var(--gold-edge);
    background: linear-gradient(180deg, var(--gold-tint-bg), var(--gold-tint-deep));
    color:var(--text);
    box-shadow: inset 0 1px 0 rgba(var(--gold-rgb),.20), var(--shadow-1);
  }
  /* o carimbo anda NA LINHA do rótulo: ancorado no canto absoluto ele encostava na
     última letra de "Em grupo", sem folga nenhuma */
  #setup .mode-btn[data-recommended="true"] b{ display:flex; align-items:center; flex-wrap:wrap; }
  .mode-btn b{ display:block; font-size:14px; margin-bottom:2px; color:var(--text); }
  .mode-btn small{ font-size:12px; }
  /* apoio de linha dentro de uma região: papel de suporte, caixa normal. É o degrau
     ABAIXO do título de grupo — sem ele, quatro decisões numa caixa liam igual. */
  .setup-row{ margin-top:13px; padding-top:13px; border-top:1px solid var(--rule); }
  .setup-row[hidden]{ display:none; }
  .setup-row-label{
    display:block; margin-bottom:7px; color:var(--text-soft); font-weight:600;
    font-size:var(--t-support-size); line-height:var(--t-support-lh);
  }
  /* ============================== O SOFÁ (1.54.0) ==============================
     A região de configuração do Início é a cena de uma noite de filme: a TV no alto, o sofá
     embaixo, e cada decisão é um objeto dela (ver renderSofa, js/03). Três regras de desenho:
     · os objetos são DESENHADOS em caixas de CSS, não em imagem: eles trocam de tema pelos
       tokens --sofa-* (o tecido clareia no papel) e a TV continua escura nos dois (TV é preta
       de verdade — o que se escreve na tela dela lê as tintas --on-image, que não trocam);
     · o ouro só aparece no que pede ação: a TV sem streaming, o lugar de chamar alguém, o
       botão do controle remoto e o celular na mão. O resto da cena é tecido e sombra;
     · os nomes ficam FORA do desenho, numa fileira embaixo do sofá: em cima do tecido o texto
       dependeria da cor do tecido, e no tema claro ele não se leria. */
  .sala-palco{ display:grid; justify-items:center; gap:14px; padding-top:4px; }
  .sala-palco > .setup-block-note{ justify-self:stretch; margin:-4px 0 0; }

  /* o botão ao lado do título: troca entre "só eu" e "com mais gente". É texto de apoio com
     alvo de 44px — a ação dourada da tela continua sendo começar */
  .sofa-modo{
    min-height:44px; margin:-12px -4px -12px 0; padding:0 4px 0 12px; border:0; background:none;
    color:var(--gold-ink); font:inherit; font-size:var(--t-support-size); font-weight:700; cursor:pointer;
    text-decoration:underline; text-decoration-color:rgba(var(--gold-ink-rgb), .38); text-underline-offset:3px;
  }
  .sofa-modo:hover{ text-decoration-color:currentColor; }

  /* ---- a TV ---- */
  .tv{
    position:relative; display:grid; justify-items:center; width:min(214px, 66%); padding:0; border:0;
    background:none; color:inherit; font:inherit; cursor:pointer; -webkit-tap-highlight-color:transparent;
  }
  .tv-moldura{
    display:block; width:100%; aspect-ratio:16/9; padding:6px; border-radius:12px;
    background:linear-gradient(180deg, var(--tv-moldura), var(--tv-moldura-fundo));
    box-shadow:0 12px 22px -14px rgba(0,0,0,.85), inset 0 1px 0 rgba(255,255,255,.06);
    transition:transform var(--dur-1) var(--ease-standard);
  }
  .tv:active .tv-moldura{ transform:scale(.985); }
  .tv-tela{
    display:grid; place-items:center; align-content:center; gap:6px; height:100%; padding:6px 10px;
    border-radius:8px; overflow:hidden;
    background:radial-gradient(100% 90% at 50% 15%, var(--tv-tela-luz), var(--tv-tela));
  }
  .tv-logos{ display:flex; flex-wrap:wrap; justify-content:center; gap:4px; }
  .tv-logos i{
    font-style:normal; font-size:11px; font-weight:750; line-height:1; letter-spacing:.1px; white-space:nowrap;
    padding:5px 7px; border-radius:6px; background:rgba(255,255,255,.08); color:var(--on-image);
  }
  /* A TELA INICIAL DE UMA SMART TV (1.61.0): um bloco por streaming com o logo oficial, e o
     vidro com um reflexo diagonal parado por cima de tudo. O bloco é OBJETO da TV, como a
     tela: escuro nos dois temas. */
  .tv-tela{ position:relative; }
  .tv-tela::after{ content:""; position:absolute; inset:0; pointer-events:none; border-radius:inherit;
    background:linear-gradient(118deg, rgba(255,255,255,.055), rgba(255,255,255,.03) 30%, rgba(255,255,255,0) 30.5%); }
  .tv-apps{ display:flex; justify-content:center; gap:6px; }
  .tv-app{ flex:none; width:32px; height:32px; border-radius:8px; overflow:hidden; display:grid; place-items:center;
    font-style:normal; background:rgba(255,255,255,.08);
    box-shadow:0 5px 9px -4px rgba(0,0,0,.8), inset 0 0 0 1px rgba(255,255,255,.12); }
  .tv-app img{ display:block; width:100%; height:100%; object-fit:cover; }
  .tv-app--mais{ font-size:11px; font-weight:800; color:var(--on-image); }
  .tv-legenda{
    max-width:100%; font-size:10.5px; line-height:1.2; color:var(--on-image-dim);
    white-space:nowrap; overflow:hidden; text-overflow:ellipsis;
  }
  .tv-vazia{ display:grid; justify-items:center; gap:2px; color:var(--gold-on-image); text-align:center; line-height:1.25; }
  .tv-vazia b{ font-size:12.5px; font-weight:750; }
  .tv-vazia > span{ font-size:11px; }
  .tv-pe{ display:block; width:54px; height:7px; border-radius:0 0 7px 7px; background:var(--tv-moldura-fundo); }
  /* metadado, não pendência: tinta de apoio. Em ouro ele era o texto dourado mais alto
     acima do botão de começar, e ouro nesta superfície é ação (achado da revisão) */
  .tv-palpite{ margin-top:5px; font-size:11.5px; font-weight:600; color:var(--text-dim); }
  .tv--vazia .tv-tela{ box-shadow:inset 0 0 0 1.5px rgba(var(--gold-rgb), .62); }
  /* SÓ o estado que impede a rodada se mexe: a tela pulsa devagar, de dentro para fora */
  @media (prefers-reduced-motion: no-preference){
    .tv--vazia:not(.needs-attention) .tv-tela{ animation:tv-chama 2.4s var(--ease-standard) infinite; }
    @keyframes tv-chama{
      50%{ box-shadow:inset 0 0 0 1.5px rgba(var(--gold-rgb), .22), inset 0 0 26px rgba(var(--gold-rgb), .18); }
    }
  }

  /* ---- o sofá ---- */
  .sofa{ position:relative; width:100%; max-width:344px; }
  .sofa-desenho{ position:absolute; left:0; right:0; top:0; height:92px; pointer-events:none; }
  .sofa-encosto{
    position:absolute; left:16px; right:16px; top:0; height:56px; border-radius:20px 20px 9px 9px;
    /* a luz da TV bate no encosto: um clarão largo vindo de cima, no meio */
    background:
      radial-gradient(60% 95% at 50% 0%, var(--sofa-reflexo), transparent 72%),
      linear-gradient(180deg, var(--sofa-tecido), var(--sofa-tecido-fundo));
    box-shadow:inset 0 2px 0 var(--sofa-luz);
  }
  .sofa-braco{
    position:absolute; top:28px; width:30px; height:64px; border-radius:14px;
    background:linear-gradient(180deg, var(--sofa-braco), var(--sofa-braco-fundo));
    box-shadow:inset 0 2px 0 var(--sofa-luz), 0 6px 12px -8px rgba(0,0,0,.6);
  }
  .sofa-braco--esq{ left:0; }
  .sofa-braco--dir{ right:0; }
  .sofa-base{ position:absolute; left:8px; right:8px; bottom:0; height:22px; border-radius:9px; background:var(--sofa-base); }
  /* O FUNDO DO ASSENTO (1.57.0, o dono viu no celular). As almofadas têm 4px de vão entre
     si, e entre o fim do encosto (56px) e o começo da base (70px) não havia tecido nenhum:
     pelo vão aparecia o fundo da TELA — no papel, uma fresta creme cortando o sofá. Este
     retângulo mais escuro que tudo ao redor mora atrás das almofadas (é o ::before, pintado
     antes do encosto, dos braços e da base, que o cobrem onde se sobrepõem) e só aparece nos
     vãos, onde lê como a sombra funda entre uma almofada e outra. */
  .sofa-desenho::before{
    content:""; position:absolute; left:22px; right:22px; top:40px; height:44px; border-radius:6px;
    background:var(--sofa-vao);
  }
  /* a sombra de contato: sem ela o sofá pairava sobre a tela */
  .sofa-desenho::after{
    content:""; position:absolute; left:3%; right:3%; bottom:-8px; height:15px; border-radius:50%;
    background:radial-gradient(closest-side, var(--sofa-sombra), transparent);
  }

  .sofa-lugares{
    position:relative; list-style:none; margin:0; padding:16px 30px 0;
    display:grid; grid-template-columns:repeat(4, minmax(0, 1fr)); gap:4px;
  }
  .sofa-lugares > li{ display:grid; min-width:0; }
  .sofa-lugar{
    display:grid; justify-items:center; width:100%; min-width:0; padding:0; border:0; border-radius:12px;
    background:none; color:inherit; font:inherit; cursor:pointer; -webkit-tap-highlight-color:transparent;
  }
  span.sofa-lugar{ cursor:default; }
  .sofa-assento{ position:relative; display:grid; place-items:end center; width:100%; height:64px; }
  .sofa-assento::before{
    content:""; position:absolute; left:1px; right:1px; bottom:0; height:36px; border-radius:11px 11px 7px 7px;
    background:linear-gradient(180deg, var(--sofa-assento), var(--sofa-assento-fundo));
    box-shadow:inset 0 2px 0 var(--sofa-luz);
  }
  .sofa-pessoa{
    position:relative; z-index:1; width:40px; height:40px; margin-bottom:14px; border-radius:50%;
    /* 24px e não 19: é o tamanho de TEXTO GRANDE da WCAG, onde o mínimo é 3:1. A tinta escura
       sobre o anil (#4a6fe3) dá 4,1:1 e a branca 4,5 — as duas abaixo do mínimo de texto comum.
       Em 24px as doze cores passam, sem mexer na cor da pessoa (achado do documentador) */
    display:grid; place-items:center; font-family:'Bebas Neue', ui-sans-serif, sans-serif; font-size:24px; line-height:1;
    padding-top:2px; color:#15121a; background:var(--cor, var(--gold));
    box-shadow:0 0 0 3px var(--sofa-assento-fundo), 0 7px 12px -6px rgba(0,0,0,.85);
    transition:transform var(--dur-2) var(--ease-standard);
  }
  .sofa-lugar:hover .sofa-pessoa{ transform:translateY(-2px); }
  .sofa-lugar:active .sofa-pessoa{ transform:translateY(1px) scale(.97); }
  .sofa-vazio{
    position:relative; z-index:1; width:38px; height:38px; margin-bottom:15px; border-radius:50%;
    display:grid; place-items:center; color:var(--gold-ink);
    /* é a peça da cena que PEDE o toque: fundo e aro por token, porque no papel o tecido é
       escuro e o aro dourado de 8% sumia contra ele (achado da revisão) */
    border:1.5px dashed var(--sofa-chamar-aro); background:var(--sofa-chamar-fundo);
  }
  .sofa-vazio--link{ color:var(--text-dim); border-color:var(--edge-strong); background:none; }
  /* o nome começa 8px abaixo do sofá, fora do tecido */
  .sofa-nome{
    max-width:100%; margin-top:16px; font-size:11.5px; font-weight:650; line-height:1.2; color:var(--text);
    white-space:nowrap; overflow:hidden; text-overflow:ellipsis;
  }
  .sofa-nome--apoio{ color:var(--text-dim); font-weight:600; }

  /* o celular na mão: o aparelho em que a pessoa vota. Um só, com você, é o "passa de mão
     em mão"; na sala, o seu (os outros chegam pelo link) */
  .sofa-celular{
    position:absolute; z-index:2; right:-6px; bottom:-6px; width:17px; height:26px; border-radius:5px;
    border:1.5px solid var(--gold); display:grid; justify-items:center; align-content:start; padding-top:3px;
    /* a tela ACESA: é o que faz a peça ler como celular, e não como etiqueta */
    background:radial-gradient(90% 70% at 50% 58%, rgba(var(--gold-rgb), .55), transparent 72%), var(--tv-tela);
    box-shadow:0 3px 6px -2px rgba(0,0,0,.7);
  }
  /* o entalhe em cima; o traço embaixo era lido como "_" */
  .sofa-celular::after{ content:""; width:5px; height:1.5px; border-radius:2px; background:rgba(var(--gold-rgb), .8); }

  /* SENTAR é a assinatura da cena: o disco cai no assento. Só o lugar que acabou de ser
     ocupado anima — redesenhar o sofá inteiro a cada letra de um nome não pode fazer todo
     mundo pular. Sem preenchimento de animação: o repouso é a pessoa sentada */
  @media (prefers-reduced-motion: no-preference){
    .sofa-lugar--novo .sofa-pessoa{ animation:sofa-senta 460ms var(--ease-entrance); }
    @keyframes sofa-senta{ from{ transform:translateY(-24px) scale(.7); opacity:0; } }
  }

  /* o controle remoto, pousado no braço direito: abre os ajustes da rodada (a mesma porta da
     linha "Rodada" logo abaixo). O alvo é de 44px; o desenho é menor.
     O DONO PEDIU QUE ELE SE MOSTRASSE COMO BOTÃO (1.54.0): parado, um remoto desenhado é
     enfeite. Três coisas dizem "isto se aperta":
     · o botão dele é um LED ACESO (halo dourado fixo — destaque, não movimento);
     · ele SACODE duas vezes a cada 7 segundos, como um controle vibrando no sofá (1.55.0,
       pedido do dono: a sacudida só na abertura passava despercebida). Em laço, sem JS: a
       tela escondida (outra aba) não anima, e o movimento reduzido desliga tudo;
     · nas primeiras visitas, junto da sacudida, uma plaquinha "Ajustes da rodada" aparece
       embaixo dele e some. A PLAQUINHA para de vez quando a pessoa usa o remoto uma vez, ou
       depois de cinco visitas (ver chamarAtencaoProRemoto); a sacudida continua. */
  .sofa-controle{
    position:absolute; z-index:3; right:-9px; top:10px; width:44px; height:56px; padding:0; border:0;
    background:none; cursor:pointer; display:grid; place-items:center; -webkit-tap-highlight-color:transparent;
  }
  .sofa-controle > span{
    display:grid; justify-items:center; align-content:start; gap:4px; width:24px; height:42px; padding-top:6px;
    border-radius:8px; background:var(--tv-moldura-fundo); transform:rotate(-14deg);
    box-shadow:0 4px 10px -4px rgba(0,0,0,.85), inset 0 0 0 1px rgba(255,255,255,.1);
    transition:transform var(--dur-2) var(--ease-standard);
  }
  .sofa-controle:hover > span, .sofa-controle:focus-visible > span{ transform:rotate(-6deg) translateY(-2px); }
  .sofa-controle:active > span{ transform:rotate(-10deg) translateY(1px) scale(.96); }
  .sofa-controle i{
    display:block; width:8px; height:8px; border-radius:50%; background:var(--gold);
    box-shadow:0 0 0 2px rgba(var(--gold-rgb), .22), 0 0 8px 1px rgba(var(--gold-rgb), .75);
  }
  .sofa-controle b{ display:block; width:13px; height:2.5px; border-radius:2px; background:rgba(255,255,255,.26); }
  @media (prefers-reduced-motion: no-preference){
    /* sem preenchimento: entre uma sacudida e outra o repouso é o remoto pousado. As duas
       sacudidas moram no primeiro 1,6 s do ciclo de 7 s; o resto é o remoto parado. O toque
       (hover/active) continua mandando: a animação só existe enquanto ninguém mexe nele. */
    /* quatro vezes e para (1.67.0): em laço infinito, o movimento que começa sozinho e não
       termina é o que a WCAG 2.2.2 pede para dar como parar — e quem não achou o remoto em
       quase 30 s já foi chamado o bastante */
    .sofa-controle:not(:hover):not(:active):not(:focus-visible) > span{ animation:remoto-chama 7s var(--ease-standard) .5s 4; }
    @keyframes remoto-chama{
      0%, 23%, 100%{ transform:rotate(-14deg); }
      2.1%{ transform:rotate(-2deg) translateY(-4px); }
      4.1%{ transform:rotate(-24deg) translateY(-2px); }
      6.2%{ transform:rotate(-6deg) translateY(-3px); }
      8.2%{ transform:rotate(-18deg); }
      11.4%{ transform:rotate(-14deg); }
      13.5%{ transform:rotate(-2deg) translateY(-4px); }
      15.5%{ transform:rotate(-24deg) translateY(-2px); }
      17.6%{ transform:rotate(-6deg) translateY(-3px); }
      19.6%{ transform:rotate(-18deg); }
    }
  }
  /* a plaquinha: escura nos dois temas, como o próprio remoto (é a voz dele), com a ponta
     apontando para ele. Some sozinha; o leitor de tela já ouve "Ajustes da rodada" no botão.
     MORA EMBAIXO DO REMOTO, com a ponta para cima (1.55.0, pedido do dono). Ao lado dele, à
     esquerda, ela ficava acima do encosto e a ponta apontava para o vazio sobre o remoto;
     centrada nele, cobriria os lugares 3 e 4; em cima, o "Palpite do app". Embaixo ela pousa
     no assento e no braço, acima da fileira dos nomes, sem cobrir nenhum disco nem texto.
     A ponta fica a 21px da borda direita da plaquinha = o centro do remoto (right:-9, 44 de
     largura → 13px para dentro do sofá, mais os 8 que a plaquinha sai dele). */
  .sofa-dica{
    position:absolute; z-index:4; right:-8px; top:68px; display:flex; align-items:center; gap:6px;
    padding:6px 10px; border-radius:999px; background:var(--tv-moldura); color:var(--on-image);
    font-size:12px; font-weight:700; line-height:1; white-space:nowrap; pointer-events:none;
    box-shadow:0 8px 16px -8px rgba(0,0,0,.8), inset 0 0 0 1px rgba(255,255,255,.08);
    animation:dica-vida 4.2s var(--ease-standard) .45s;
    opacity:0;
  }
  .sofa-dica[hidden]{ display:none; }
  .sofa-dica .ico-svg{ color:var(--gold-on-image); flex:none; }
  .sofa-dica::after{
    content:""; position:absolute; right:17px; top:-4px; width:8px; height:8px;
    background:var(--tv-moldura); transform:rotate(45deg); border-radius:1px;
  }
  @keyframes dica-vida{
    0%{ opacity:0; transform:translateY(-6px); }
    10%, 86%{ opacity:1; transform:none; }
    100%{ opacity:0; transform:none; }
  }
  @media (prefers-reduced-motion: reduce){
    @keyframes dica-vida{ 0%{ opacity:0; } 10%, 86%{ opacity:1; } 100%{ opacity:0; } }
  }

  /* ---- os trilhos embaixo do sofá e o aviso de lotação ---- */
  .sala-cena .setup-row{ margin-top:16px; }
  .sofa-lotado{ margin:8px 0 0; font-size:var(--t-support-size); line-height:var(--t-support-lh); color:var(--text-soft); }

  /* ---- a folha do nome ---- */
  #sofaPessoaOverlay{ z-index:126; }
  /* a folha de gênero do Ranking (1.58.0) nasce direto da aba, sem nada embaixo: degrau próprio */
  #rankGeneroOverlay{ z-index:127; }
  .sofa-folha-campo{ width:100%; margin:4px 0 12px; }
  /* TRILHO — o primeiro dos dois únicos idiomas de controle da tela: um grupo onde
     exatamente uma opção vale. Poço embutido; o escolhido ganha MATERIAL próprio (peça
     levantada dentro do poço), não só cor. O mesmo poço veste o contador e o campo de
     nomes: era borda-dentro-de-borda e borda tracejada, duas construções a mais. */
  .seg-track, .stepper{
    display:grid; grid-template-columns:minmax(0,1fr) minmax(0,1fr); gap:4px; padding:4px;
    border:1px solid var(--edge); border-top-color:var(--edge-lit);
    border-radius:var(--radius-md);
    background: linear-gradient(180deg, var(--surface-1-bot), var(--surface-1-top));
    box-shadow: inset 0 1px 3px rgba(0,0,0,.36);
  }
  /* três opções não cabem na coluna estreita de uma linha de ajuste: o trilho desce para
     a linha de baixo e ocupa a largura inteira, que é onde ele fica legível */
  .seg-track--tres{ grid-template-columns:repeat(3, minmax(0,1fr)); }
  .seg-track--tres .mode-btn{ text-align:center; padding-inline:6px; }
  /* NAO e o mesmo que `--stack`, que ja existe e vira a linha inteira em coluna (o
     bloco de duracao usa aquele). Aqui o rotulo FICA na coluna dele, ao lado do glifo, e
     so o controle desce: tres opcoes nao cabem na coluna estreita da direita, mas o
     rotulo perder a coluna quebraria o alinhamento de todas as outras linhas. */
  .setting-row--wrap{ flex-wrap:wrap; }
  .setting-row--wrap .seg-track{ flex-basis:100%; margin-top:10px; }
  .seg-track .mode-btn{
    background:none; border-color:transparent; box-shadow:none;
    padding:8px 10px; border-radius:var(--radius-sm);
  }
  /* O ESCOLHIDO SE VÊ PELA ARESTA, NÃO SÓ PELO RELEVO (1.62.0). A peça levantada sozinha
     diferia do poço por 1,16:1 no escuro e 1,05:1 no papel — a auditoria mediu, e o mínimo
     para indicar estado é 3:1. A aresta é o ouro APAGADO (acento que acompanha, não massa):
     `--gold-dim` dá ~5,5:1 contra o poço nos dois temas. O anel de dentro dobra a espessura
     sem mexer na caixa, e o rótulo de quem NÃO está escolhido recua para a tinta de apoio. */
  .seg-track .mode-btn.selected{
    background: linear-gradient(180deg, var(--surface-3-top), var(--surface-3-bot));
    border-color:var(--gold-dim);
    box-shadow: var(--shadow-1), inset 0 1px 0 var(--sheen-3), inset 0 0 0 1px var(--gold-dim);
    color:var(--text);
  }
  .seg-track .mode-btn:not(.selected) b{ color:var(--text-dim); }
  .seg-track .mode-btn.selected .lbl-ico{ color:var(--gold-ink); }
  .seg-track .mode-btn b{ font-size:13px; }
  /* DENTRO DOS AJUSTES O ROTULO ADOTA O PAPEL DA LINHA (14/600), e nao o 13/700 que ele
     tem no Inicio. O painel resolve com quatro papeis tipograficos e mais nenhum: uma
     quinta combinacao ali dentro nao le como "outra coisa", le como descuido. */
  .setting-row--wrap .mode-btn b{ font-size:14px; font-weight:600; line-height:1.3; }
  .seg-track .mode-btn small{ font-size:11.5px; }
  /* o stepper dividia a linha com uma frase de 60 caracteres: em 360px de largura sobravam
     ~150px pro texto e ele quebrava em três linhas desalinhadas do controle. A frase desceu
     e o lugar ao lado passou a ser usado por algo acionável — os nomes de quem vai jogar. */
  .group-size{ display:flex; align-items:stretch; gap:10px; margin-top:12px; }
  /* o contador e o campo de nomes herdam o POÇO acima; aqui fica só a geometria.
     UM controle, não três objetos: o valor flutuava num vão preto entre duas peças
     levantadas separadas, e a leitura era "dois botões e um número perdido no meio".
     A gramática do trilho já responde isso — dentro do poço só se levanta o que foi
     ESCOLHIDO, e num contador não há escolha, há valor. Os dois passos viram zonas
     quietas da mesma superfície e o número passa a ser a peça central. */
  .stepper{ display:flex; align-items:center; justify-content:center; gap:0; flex:none; }
  .stepper button{
    width:44px;height:44px; border-radius:var(--radius-sm);
    border:0; background:none; box-shadow:none;
    /* a comparação cega leu os sinais como "cinza fraco": --text-dim (5,9:1 sobre o
       poço) é a cor de METADADO, e um passo de contador é ação. --text-soft dá 8,8:1
       e o corpo sobe um degrau — continua sem material próprio, que é a regra do poço. */
    color:var(--text-soft); font-size:20px; cursor:pointer; flex:none;
    transition: background-color var(--dur-1) var(--ease-standard), color var(--dur-1) var(--ease-standard);
  }
  .stepper button:hover{ background:var(--sheen-2); color:var(--text); }
  .stepper button:active{ background:var(--sheen-3); color:var(--text); }
  /* ---- o poço DIVIDIDO ----
     O contador e o campo de nomes vestiam o mesmo poço com a mesma anatomia: mesmo
     raio, mesma borda, mesmo preenchimento, lado a lado — "um controle e um input
     indistinguíveis", e sem alvo de toque desenhado para os dois passos. O idioma
     único do poço fica (é decisão da rodada 3 e está certa); o que muda é a ANATOMIA
     de dentro: o contador é um poço de três células, o campo de nomes é um poço
     inteiriço. As réguas moram no VALOR, não nos botões — assim os dois passos
     continuam sendo zonas quietas da mesma superfície e o número continua sendo a
     peça central, só que agora com as fronteiras do alvo visíveis. */
  .stepper b{
    min-width:30px; padding:0 10px; align-self:stretch;
    display:flex; align-items:center; justify-content:center;
    border-left:1px solid var(--rule); border-right:1px solid var(--rule);
    text-align:center; font-variant-numeric:tabular-nums;
    color:var(--text); font-size:17px; font-weight:700;
  }
  /* material e cor vêm da gramática .btn--primary; aqui fica só a geometria.
     EXCEÇÃO de caixa alta: é a única ação decisiva de cada estado. */
  .cta{
    margin-top:auto; border-radius:var(--radius-md);
    padding:16px; font-size:15px; font-weight:800; letter-spacing:.3px;
    text-transform:uppercase;
  }
  /* ZONA DE AÇÃO. É região, não um botão solto no fim da coluna: carrega o resumo
     do que vai começar, gruda acima da navegação com scrim e reserva o próprio
     respiro (--dock-gap). Antes a folga sobrava do conteúdo — e sobrava 9px. */
  /* o Ranking usa a MESMA doca (ver .rank-cta-bar): fonte igual garante curva igual
     por construção; valor copiado só garante até alguém editar um lado. */
  .setup-cta-bar, .rank-cta-bar{
    position:sticky; z-index:5;
    bottom: var(--dock-space);
    margin-top:auto; margin-bottom: var(--dock-space);
    /* o respiro é PADDING da própria zona, não margem: assim o scrim pinta também a
       faixa entre o botão e a navegação, e nenhum pedaço de card fica espiando ali */
    /* VÉU, não guilhotina. A rampa antiga tinha 26px, dos quais só 9 ficavam acima do
       botão opaco: o conteúdo em repouso terminava fatiado na altura-x, com metade em
       brilho quase pleno e metade sumindo numa aresta dura. As paradas agora são em PX
       (não em %, que mudava com a altura da zona) e a rampa inteira — --dock-veil — fica
       acima do botão, então o que passa por baixo desaparece progressivamente. */
    padding: var(--dock-veil) 0 var(--dock-gap);
    background: linear-gradient(180deg,
      rgba(var(--bg-rgb), 0) 0,
      rgba(var(--bg-rgb), .14) 26px,
      rgba(var(--bg-rgb), .52) 48px,
      var(--bg) var(--dock-veil), var(--bg) 100%);
  }
  /* ---- o que atravessa a rampa fica ATRÁS DE VIDRO, não decapitado ----
     Uma rampa só de opacidade tem uma zona de transição mais curta que uma linha de
     texto: o topo dos glifos ficava quase limpo e a base quase apagada, e um TÍTULO
     assim lê como quebrado (um cartaz meio dissolvido lê como "tem mais abaixo"; uma
     palavra pela metade lê como defeito). Foi o que a comparação cega mediu a 5× na
     linha de Refinamento: "o topo dos glifos aparecendo acima da borda do botão".
     O desfoque progressivo tira a legibilidade ANTES de tirar o brilho: o que entra na
     rampa deixa de ser lido como palavra e passa a ser lido como camada. A máscara faz
     o desfoque nascer do nada, senão a própria borda do vidro viraria uma segunda
     aresta dura no lugar da primeira. */
  .setup-cta-bar::before, .rank-cta-bar::before{
    content:''; position:absolute; left:0; right:0; top:0; height:var(--dock-veil);
    pointer-events:none;
    backdrop-filter: var(--veil-glass); -webkit-backdrop-filter: var(--veil-glass);
    -webkit-mask-image: var(--veil-glass-mask);
    mask-image: var(--veil-glass-mask);
  }
  .dock-inner{
    display:flex; flex-direction:column; gap:11px;
    padding:13px 14px 14px; border-radius:var(--radius-lg);
    border:1px solid var(--edge); border-top-color:var(--edge-lit);
    background: linear-gradient(180deg, var(--surface-1-top), var(--surface-1-bot));
    box-shadow: var(--shadow-2), inset 0 1px 0 var(--sheen-2);
  }
  .dock-recap{
    margin:0; min-width:0; color:var(--text-soft);
    font-size:var(--t-support-size); line-height:var(--t-support-lh);
    font-variant-numeric: tabular-nums;
  }
  .dock-actions{ display:flex; flex-direction:column; gap:4px; min-width:0; }
  /* peça de metal: o bisel vem das ARESTAS (topo claro, base profunda), não de mais
     uma sombra — assim a elevação continua sendo a neutra do sistema, sem halo
     colorido, e o pulso do CTA não briga com o fio de luz interno. */
  #startBtn{
    margin-top:0; min-height:52px; width:100%;
    border-top-color:#ffe2ab; border-bottom-color:var(--gold-deep);
  }
  /* o pulso é ENTRADA e resposta, não respiração eterna: três ciclos e para. Movimento
     infinito dentro de um app aberto por minutos é zona cinza da WCAG 2.2.2 e cansa; o
     ganho de atenção acontece nos primeiros segundos de qualquer jeito. Ele volta a
     disparar quando a configuração muda — que é quando ele tem algo novo a dizer. */
  #startBtn.is-pulsing{ animation: cta-pulse 2.6s ease-in-out 3; }
  @keyframes cta-pulse{
    50%{ transform: scale(1.018); box-shadow: 0 6px 24px rgba(0, 0, 0, .32); }
  }
  /* UMA peça dourada por estado. Com rodada aberta quem manda é "Continuar", na faixa de
     retomada: dois botões com o MESMO gradiente empurravam para a ação errada, e a maior
     das duas era a que descartava a rodada de pé. Aqui a zona fixa recua para contorno. */
  #startBtn.is-alt{
    background: linear-gradient(180deg, var(--surface-2-top), var(--surface-2-bot));
    color: var(--gold-ink);
    border:1px solid var(--gold-dim); border-top-color:var(--gold-edge);
    box-shadow: var(--shadow-1), inset 0 1px 0 var(--sheen-2);
    animation:none;
  }
  /* sobre material escuro o anel escuro de "em cima de ouro" sumiria */
  #startBtn.is-alt:focus-visible{ outline-color: var(--focus-ring); }
  @media (prefers-reduced-motion: reduce){
    #startBtn.is-pulsing{ animation:none; }
  }
  #joinRoomLink{ align-self:center; }
  /* Viewport muito baixo (zoom de 200%, celular deitado): topo + navegação já comem
     metade da altura, e uma zona de ação grudada em cima disso cobriria o próprio
     cartaz. Ali ela volta a andar com o conteúdo — continua reservando o respiro. */
  @media (max-height:540px){
    .setup-cta-bar{ position:static; }
  }
  /* O Ranking desiste da doca fixa MAIS CEDO, e é medida, não gosto: em 740×400 a área
     entre as barras é de 250px, dos quais a doca tomava 118 — quase metade da tela para
     um botão, com o pódio inteiro empurrado para debaixo do véu. Acima deste limiar ela
     volta a valer a pena, porque sobra tela para o placar atrás dela. */
  @media (max-height:620px){
    .rank-cta-bar{ position:static; margin-top:18px; }
  }
  /* ---- a aresta da navegação também tem véu ----
     Consequência direta da regra acima: com a zona de ação no fluxo, o conteúdo passa a
     rolar por baixo de uma barra opaca de borda dura e volta a terminar cortado — o mesmo
     defeito que a rampa da zona de ação resolveu no celular, reaparecendo justamente a
     200% de zoom. A rampa fica onde o corte acontece: na barra. Assim ela não depende de
     a zona de ação estar fixa, e vale em qualquer altura de tela.
     Altura E curva vêm do par de tokens `--tabbar-veil` / `--tabbar-veil-fade`, que é a
     mesma rampa que o Histórico pinta nesta borda. Uma aresta, uma dissolução. */
    body:has(#setup.active) .tabbar::before{
      content:''; position:absolute; left:0; right:0; bottom:100%;
      height:var(--tabbar-veil);
      background:var(--tabbar-veil-fade);
      pointer-events:none;
    }
    /* e o VIDRO da mesma dissolução, que faltava só nesta aresta (ver `--veil-glass`) */
    body:has(#setup.active) .tabbar::after{
      content:''; position:absolute; left:0; right:0; bottom:100%;
      height:var(--tabbar-veil); pointer-events:none;
      backdrop-filter: var(--veil-glass); -webkit-backdrop-filter: var(--veil-glass);
      -webkit-mask-image: var(--veil-glass-mask);
              mask-image: var(--veil-glass-mask);
    }
  /* ---- P1-H15 · ONDE A ZONA DE AÇÃO É FIXA, ELA É A ARESTA — e o véu recolhe ----
     A rodada 6 unificou a rampa das três abas num par de tokens, e unificou certo. O que
     ninguém conferiu foi QUEM MORA na faixa: o Início é a única superfície com um
     controle fixo dentro do alcance do véu, e a promoção de 36 px para 54 px passou a
     rampa por cima de 81% do #startBtn. O juiz mediu no pixel pintado e provou a causa
     por experimento controlado: o rótulo caía a 2,70:1 em 8 das 14 linhas de glifo, e
     desligar o véu devolvia o mínimo a 7,58:1. É a ação primária do app, no estado
     padrão da tela de entrada, e o botão é `sticky` — nunca sai de baixo do véu.

     O conserto NÃO é desfazer o token (isso traria de volta as duas gramáticas e só
     atenuaria o contraste). É corrigir o ESCOPO, com a regra que faltava:

       o véu dissolve CONTEÚDO QUE ROLA numa aresta fixa.
       Ele nunca escurece um CONTROLE que mora naquela faixa.

     Com a zona de ação fixa, ela pinta `--bg` opaco da própria rampa até a barra: não há
     nada por baixo para dissolver, e o único efeito visível do véu é apagar o botão. As
     duas calhas de 20 px que a zona não cobre mostram fundo de página, também sem nada a
     dissolver. Ali o véu não tem trabalho — recolhe.

     Onde a zona de ação ANDA COM O CONTEÚDO (viewport baixo, 200% de zoom), o véu
     continua de pé: aquele é literalmente o caso para o qual ele foi escrito, e ali o
     conteúdo — o CTA junto — rola mesmo para debaixo de uma barra opaca de borda dura.
     `max-height:540px` e `min-height:541px` particionam a altura de tela: nenhuma fica
     sem regra, nenhuma recebe as duas. */
  @media (min-height:541px){
    body:has(#setup.active) .tabbar::before,
    body:has(#setup.active) .tabbar::after{ content:none; }
  }
  /* a tela se apresenta: as regiões sobem uma atrás da outra, curto e causal.
     Em movimento reduzido nada anima — o estado final já comunica o mesmo. */
  #setup.active .home-marquee,
  #setup.active .active-round-strip,
  #setup.active .setup-region{ animation: surface-rise var(--dur-3) var(--ease-entrance) both; }
  #setup.active .active-round-strip{ animation-delay:45ms; }
  #setup.active .setup-region{ animation-delay:80ms; }
  @keyframes surface-rise{ from{ opacity:0; transform:translateY(10px); } }
  @media (prefers-reduced-motion: reduce){
    #setup.active .home-marquee,
    #setup.active .active-round-strip,
    #setup.active .setup-region{ animation:none; }
  }

  /* O setup precisa mostrar as duas decisões finais antes do CTA em celulares estreitos.
     Nada some: reduzimos apenas respiros que se repetiam entre blocos e mantemos todos os
     controles com pelo menos 44px. */
  /* O celular estreito é o aparelho principal do produto: as três decisões, os dois
     resumos e a ação têm que caber num viewport de 800px COM tabbar. A régua aqui é
     densidade, nunca papel — nenhum rótulo troca de nível, só de tamanho. */
  /* O shell inteiro tem no máximo 420px, então esta é a composição-base do produto. */
    #setup{ padding-top:6px; }
    #setup .home-marquee{ margin:-6px -20px 8px; padding:8px 20px 8px; }
    /* CARTAZ, não manchete. Sem arte no aparelho — primeira visita — o que ocupa o
       palco é a pergunta, e ela precisa ter escala de cartaz para governar a tela.
       A 26px com `max-width:13ch` ela media 135px numa faixa de 390: 215px (55% da
       largura) sobravam vazios à direita, que foi o que a comparação cega mediu.
       A medida agora é a faixa inteira e o corpo cresce junto; com estante, a coluna
       do pôster reclama a metade direita e a pergunta se acomoda na que sobra. */
    #setup h1.title{ font-size:clamp(38px, 12.2vw, 52px); margin-bottom:0; max-width:none; }
    /* com estante o cartaz de verdade é o pôster: a pergunta cede escala e se acomoda
       na coluna que sobra — sem `max-width`, que ali produziria quatro linhas de duas
       palavras numa coluna de 235px */
    #setup .home-marquee:has(.marquee-shelf:not([hidden])) h1.title{ font-size:27px; }
    #setup p.sub{ display:none; }
    #setup .setup-kicker{ margin-bottom:2px; font-size:10px; }
    /* o cartaz é a peça dominante da faixa; a promessa se acomoda na coluna que sobra.
       Com as três miniaturas empilhadas embaixo do texto a faixa media 181px pra entregar
       arte de 66px — aqui ela mede menos e entrega 152px de pôster. */
    #setup .home-marquee{ align-items:center; column-gap:14px; }
    #setup .shelf-frames{ --shelf-h:152px; }
    #setup .shelf-note{ margin-top:6px; font-size:11px; }
    /* no celular estreito o título de grupo cede tamanho, não papel: continua
       sendo título (caixa normal, peso 700), só que num degrau abaixo */
    #setup .section-label{ margin:12px 0 7px; font-size:14px; }
    /* AS TRÊS REGIÕES SÃO PARES. Na rodada 5 a mesa de pessoas ficou sendo o único
       objeto com material levantado e os dois resumos viraram linhas sob régua — um
       objeto levantado, o resto recua. A escolha do objeto estava errada: quem ficou
       levantado foi o FORMULÁRIO, e a comparação cega leu exatamente isso ("formulário
       não hierarquiza sozinho"). O papel de peça levantada passou para o palco; aqui
       embaixo as três regiões recebem o MESMO tratamento de linha sob régua, com a
       mesma anatomia (título de grupo · nota à direita · valor). Nada é removido: os
       trilhos continuam sendo poços — carvados, não levantados —, então o material
       levantado da tela volta a ser um só. É a mesma correção que fez o Histórico
       saltar: uma anatomia por linha, um eixo declarado uma vez. */
    #setup .setup-region{
      padding:8px 2px 2px; border:0; border-top:1px solid var(--rule);
      border-radius:0; background:none; box-shadow:none;
    }
    #setup .setup-people-column{ display:block; margin-bottom:0; }
    /* a primeira régua da pilha é a do palco: a região de cima não repete a linha */
    #setup .setup-people-column > .setup-region:first-child{ border-top:0; padding-top:2px; }
    #setup .setup-region + .setup-region{ margin-top:0; }
    #setup .setup-region-head{ margin-bottom:3px; }
    #setup .setup-region .section-label{ margin:0; }
    #setup .setup-region-note{ font-size:11.5px; }
    #setup .setup-row{ margin-top:5px; padding-top:5px; }
    #setup .setup-row-label{ margin-bottom:4px; font-size:11.5px; }
    /* no trilho a descrição sai da tela e fica no aria-label: em 360px ela custava
       duas linhas por botão e o rótulo já diz a diferença entre os dois modos */
    #setup .seg-track{ gap:3px; padding:3px; }
    #setup .seg-track .mode-btn{ min-height:44px; padding:6px 9px; display:flex; align-items:center; }
    /* "Celulares diferentes" ocupa duas linhas onde a irmã ocupa uma — e o glifo, sendo
       inline, sentava na linha de base da PRIMEIRA delas, 8px acima do centro do bloco.
       O par lia como desalinhado. Virando o rótulo um flex, o glifo se centra contra o
       bloco inteiro e as duas opções voltam a ter o mesmo eixo, com uma ou duas linhas.
       (A quebra em si é conteúdo: a 360px o rótulo não cabe numa linha em nenhum corpo
       que ainda respeite o alvo de 44px.) */
    #setup .seg-track .mode-btn b{ font-size:12.5px; margin:0; display:flex; align-items:center; }
    #setup .seg-track .mode-btn b .lbl-ico{ align-self:center; }
    #setup .seg-track .mode-btn small{ display:none; }
    #setup .filter-summary{ margin-top:0; padding:8px; }
    /* o valor completo continua a um toque de distância, no painel que a linha abre:
       em 360px ele custava três linhas e empurrava a ação pra fora da dobra */
    #setup #filterSummary > span{
      display:-webkit-box; -webkit-line-clamp:2; -webkit-box-orient:vertical; overflow:hidden;
    }
    /* no celular a zona de ação não precisa de caixa própria: o CTA já é a peça mais
       larga da tela e envelopá-lo seria cartão dentro de cartão. O que ela precisa é
       do respiro garantido e do véu — e disso ela continua cuidando. */
    #setup .dock-inner{
      padding:0; gap:6px; border:0; border-radius:0; background:none; box-shadow:none;
    }
    /* ---- P3-H13 · MEDIDO NA RODADA 7, E O DIAGNÓSTICO DOS DOIS LAUDOS ESTÁ ERRADO ----
       O laudo 6 acoplou este achado ao P1-H15 ("os 53 px de banda morta são exatamente a
       folga que o botão precisa ganhar"). A medição no layout vivo desfaz o acoplamento:
       o CTA nunca precisou de folga. Ele está em y 708..756 ANTES e DEPOIS do conserto do
       P1-H15 — o que o apagava era o véu pintando por cima, não a falta de espaço. Os
       dois achados nunca foram o mesmo conserto.
       E a banda também não é "fundo liso". Medido em 390x844, estado padrão:
       `#setup` tem scrollHeight 782 contra clientHeight 767 — a tela ROLA, 15 px. A faixa
       entre a última região (y655) e o botão (y708) é a parte de baixo da rampa de
       dissolução da zona de ação, e a rampa está viva: é por ela que esses 15 px passam.
       O que ela tem de errado é escala, não existência — 68 px de rampa para 15 px de
       percurso.
       As três saídas foram testadas e as três são piores:
         · trazer a recapitulação (recomendação do laudo 6) repete o que os dois trilhos
           logo acima já dizem — o defeito que o mesmo laudo cobra em outros dois lugares;
         · deixar o palco absorver a folga não faz nada aqui:
           não HÁ folga (a tela está 15 px em excesso, não em falta), então `flex-grow`
           recebe zero;
         · encurtar a rampa não move o botão, que é preso por `bottom: var(--dock-space)`,
           nem a última região, que é presa pelo conteúdo acima — só muda a sobra de lugar,
           abrindo embaixo do botão o que fechar em cima dele.
       Fica registrado como medido, não como resolvido. */
    /* P3-H13 (rodada 6) · a banda morta fechou-se sozinha quando o palco tomou a altura dele: a
       última região passou a ENCOSTAR na rampa (era y=636 contra uma zona que começava
       em 652 e só ficava opaca em 708 — 72px de nada). Testei a outra saída que o laudo
       oferecia, trazer a recapitulação da retomada para o estado padrão, e ela é pior:
       "Em grupo · 2 pessoas · mesmo celular" é literalmente o que os dois trilhos logo
       acima mostram, então o preço da faixa preenchida seria dizer a mesma coisa duas
       vezes — o defeito que este mesmo laudo cobra em outros dois lugares. O resumo
       continua onde tem o que dizer: com rodada aberta, onde ele declara uma
       CONSEQUÊNCIA que nenhum trilho declara. */
    #setup .dock-recap{ display:none; }
    #setup .dock-inner:has(#startBtn.is-alt) .dock-recap{ display:block; }
    #setup #startBtn{ min-height:48px; padding:12px; }

  /* SWIPE SCREEN */
  /* cabeçalho e painel de grupo eram duas caixas empilhadas dizendo a mesma coisa por
     dois meios (quem está votando, quanto falta) e comiam ~150px. Viraram uma régua só:
     avatares · de quem é a vez · quanto falta. */
  /* A RÉGUA QUEBRA EM VEZ DE CORTAR (1.67.0). Com "Ver o resultado (N)" na régua, "Sair",
     a vez, o botão e o contador não cabiam em 360px: o contador saía cortado e "Vez de:
     Pessoa 2" virava "Vez …" (auditoria cega de 2026-10-05). A vez tem largura mínima, e
     quando não cabe é o botão e o contador que descem para uma segunda linha. */
  .round-bar{
    display:flex; align-items:center; gap:10px; flex-wrap:wrap; row-gap:6px;
    min-height:40px; margin-bottom:10px;
  }
  /* com as abas fora do ar durante a votação, esta é a única saída da rodada. Ela precisa
     parecer saída (seta + rótulo) sem competir com o card: cinza, sem moldura, 44px de alvo. */
  .round-exit{
    min-width:44px; min-height:44px; padding:0 10px; flex:none;
    border-radius:var(--radius-sm);
    font-size:12px; font-weight:700; cursor:pointer;
  }
  .round-exit::before{ content:'←'; margin-right:5px; color:var(--gold-dim); }
  /* A saída com o que já deu match. Ouro de ESTADO — aresta e tinta, sem preenchimento:
     ela informa que existe resultado guardado e oferece ir vê-lo. O ouro cheio continua
     sendo do clímax. Empurrada pro fim da régua junto com o contador, que é o outro dado
     de "quanto já andou". */
  .round-matches{
    margin-left:auto; flex:none;
    min-height:34px; padding:0 11px;
    display:inline-flex; align-items:center; gap:6px;
    border:1px solid var(--edge-gold); border-radius:var(--radius-pill);
    background:rgba(var(--gold-rgb), .09); color:var(--gold-ink);
    font-family:inherit; font-size:12px; font-weight:700; white-space:nowrap; cursor:pointer;
  }
  .round-matches[hidden]{ display:none; }
  .round-matches:hover{ background:rgba(var(--gold-rgb), .16); }
  .round-matches svg{ flex:none; }
  /* com o botão na régua o contador perde o `margin-left:auto` pra ele; sem esta regra os
     dois brigavam pela mesma cola e o contador colava no "vez de fulano" */
  .round-matches + .count{ margin-left:0; }
  .round-bar .count{ font-size:12px; color:var(--text-dim); flex:none; font-variant-numeric:tabular-nums; margin-left:auto; }
  .round-turn{ font-size:13px; color:var(--gold-ink); font-weight:700; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
  /* com o botão de resultado na régua, a vez não encolhe: quem desce de linha é o botão */
  .round-bar:has(.round-matches:not([hidden])) .round-turn{ flex:1 0 auto; }
  .round-turn:empty{ display:none; }
  .card-stack{
    position:relative; flex:1; display:flex; align-items:center; justify-content:center;
    min-height:260px;
    /* o clamp da sinopse precisa responder à altura do CARD, não da tela: no modo grupo o
       painel de participantes come ~100px e o mesmo celular tem card bem menor */
    container-type: size; container-name: deck;
  }
  .card{
    /* o teto de 430px deixava 88px ociosos num iPhone padrão e 208px num Pro Max — espaço
       que a sinopse podia usar. Crescer também aproxima o pôster do 2:3 de um cartaz de
       verdade (em 430px ele ficava mais largo que alto e cortava demais). */
    position:absolute; width:100%; max-width:340px; height:min(100%, 620px);
    /* rede de segurança: se algum motor mobile (certas versões de Safari iOS/WebView com
       `container-type: size`) resolver `height:100%` contra uma altura de container
       colapsada, o card viraria 0px e sumiria. Um piso relativo à viewport garante que ele
       sempre apareça. Fica abaixo da altura real em todos os tamanhos, então nunca briga
       com o layout normal; só entra em ação se a altura calculada colapsar. */
    min-height: 50vh; min-height: 50dvh;
    border-radius: var(--radius-xl); overflow:hidden; cursor:grab;
    background: linear-gradient(160deg, var(--card-top), var(--card-bot));
    border: 1px solid var(--edge); border-top-color: var(--edge-lit);
    box-shadow: var(--shadow-3), inset 0 1px 0 var(--sheen-2);
    user-select:none; touch-action:none;
    display:flex; flex-direction:column;
    transition: transform var(--dur-3) var(--ease-emphasis), opacity var(--dur-3) var(--ease-exit);
  }
  .card.dragging{ transition:none; cursor:grabbing; }
  .poster{
    /* min-height garante que o pôster nunca seja espremido pela sinopse: com o card livre
       pra crescer, sem isso a proporção mudaria a cada título e o baralho perderia ritmo */
    flex:1; min-height:52%; display:flex; align-items:flex-end; padding:18px;
    position:relative;
    /* A faixa que os botões do card ocupam à direita. As duas medidas vivem AQUI, e não
       como números soltos no JS, porque a segunda é a PRIMEIRA mais o respiro: separadas,
       uma mudava sem a outra e o nome do filme voltava a apanhar.
       A largura é fixa de propósito. Com ela livre, o rótulo mandava — e o rótulo do
       "+ Minha lista" carrega o NOME da lista padrão, que a pessoa pode renomear pro que
       quiser. Um nome comprido esticava a coluna por cima do título de novo, agora sem
       nenhum número no CSS pra segurar. Fixa, o nome é que cede: sobra reticência. */
    --card-tools-w:118px;
    --card-tools-lane:calc(var(--card-tools-w) + 14px);
  }
  .poster::before{
    content:''; position:absolute; inset:0;
    /* o scrim de baixo protege o título; o de cima é novo e protege os badges, que ficam
       em top:20px — onde antes só havia pôster cru e o contraste dependia da imagem */
    background:
      linear-gradient(180deg, rgba(0,0,0,.55) 0%, rgba(0,0,0,0) 22%),
      linear-gradient(180deg, rgba(0,0,0,0) 40%, rgba(0,0,0,.85) 100%);
  }
  /* O QUE ESTÁ ESCRITO SOBRE O PÔSTER NÃO SEGUE O TEMA (1.51.2). O pôster é sempre uma foto
     escurecida pelo véu de baixo, nos dois temas — e o texto por cima dela lia `--text` e
     `--gold-ink`, que no papel viram tinta escura: nome, ano e streaming sumiam no preto
     do véu. Quem escreve sobre imagem lê as tintas `--on-image`, que não trocam. */
  .poster{ color:var(--on-image); }
  .poster-emoji{ position:absolute; top:20px; right:20px; z-index:1; font-size:13px; background:rgba(0,0,0,.66); padding:4px 10px; border-radius:var(--radius-pill); color:var(--gold-on-image); border:1px solid var(--gold-on-image-edge); }
  .card-timer{ position:absolute; top:20px; left:20px; z-index:1; font-size:13px; font-weight:700; font-variant-numeric:tabular-nums; background:rgba(0,0,0,.72); padding:4px 10px; border-radius:var(--radius-pill); color:var(--on-image-dim); border:1px solid var(--on-image-edge); transition: color var(--dur-2) var(--ease-standard), border-color var(--dur-2) var(--ease-standard); }
  .card-timer.urgent{ color:var(--rose); border-color:rgba(var(--rose-rgb), .5); animation: timer-pulse 1s infinite; }
  @keyframes timer-pulse{ 50%{ transform: scale(1.08); } }
  /* desabilitado com cor explícita, não opacity: um botão a 30% de opacidade
     sobre pôster escuro fica invisível em vez de ficar "apagado" */
  .action-btn:disabled{
    background: var(--btn-disabled-bg); border-color: var(--btn-disabled-edge);
    color: var(--btn-disabled-fg); box-shadow:none; cursor:default;
  }
  .action-btn:disabled:active{ transform:none; }
  .poster-copy{ position:relative; z-index:1; }
  .poster-copy--inset{ padding-right:var(--card-tools-lane); }
  .poster-title{ position:relative; z-index:1; font-family:'Bebas Neue'; font-size:30px; letter-spacing:.5px; line-height:1.02; }
  .poster-meta{ position:relative; z-index:1; font-size:11px; color:var(--gold-on-image); margin-top:4px; letter-spacing:.5px; }
  /* A meta quebra ENTRE os metadados, nunca dentro de um. Com a coluna de botões reservando
     sua faixa, a linha passou a caber em duas — e quebrando onde calhasse ela partia
     "148 min" ao meio, deixando "min" sozinho numa linha própria. Medir não pegou (nada
     transbordava, nada se sobrepunha); o print pegou na primeira olhada. */
  .poster-meta span{ white-space:nowrap; }
  /* uma linha de sinopse é pouco pra decidir um filme. O botão traz trailer e elenco pro
     meio da votação, no canto do pôster: perto da decisão, longe do caminho do polegar que
     arrasta. Fundo opaco porque ele pousa sobre pôsteres de qualquer cor. */
  /* ---- os dois botões EMPILHAM, não dividem a régua ----
     Lado a lado eles somavam ~230px numa faixa de 347: o par não cabia à direita do
     título e o "+ Minha lista" acabava pousado EM CIMA do nome do filme, que é a única
     coisa que o card precisa dizer. `flex-wrap` não salvava — a quebra só acontece
     quando não cabe, e "não cabe" aqui já era tarde: o estrago é a sobreposição, não o
     transbordo. Em coluna a peça ocupa a largura de UM botão, e é essa largura que o
     título reserva (`--card-tools-lane`).
     `align-items:stretch` numa coluna de largura livre iguala os dois pelo mais largo:
     o par lê como um bloco só, não como dois botões de tamanhos diferentes. */
  .card-tools{
    position:absolute; right:14px; bottom:14px; z-index:2;
    display:flex; flex-direction:column; align-items:stretch; gap:8px;
    width:var(--card-tools-w); max-width:calc(100% - 28px);
  }
  .card-details, .card-save{
    min-height:44px; padding:9px 12px; display:inline-flex; align-items:center; justify-content:center; gap:7px;
    border:1px solid rgba(255,255,255,.42); border-radius:10px;
    background:rgba(var(--bg-rgb),.82); color:var(--text); font-size:12px; font-weight:700;
    cursor:pointer; backdrop-filter:blur(6px); -webkit-backdrop-filter:blur(6px);
    font-family:inherit; white-space:nowrap; overflow:hidden; text-overflow:ellipsis;
  }
  /* Salvo é ESTADO, e estado se lê pela borda e pela tinta do texto — não por um
     preenchimento dourado. O ouro cheio é do match, e emprestá-lo pra "está na lista"
     faria dois momentos muito diferentes usarem a mesma voz. */
  .card-save[aria-pressed='true']{
    border-color:var(--edge-gold); color:var(--gold-ink);
  }
  .card-body{ padding:16px 18px 20px; }
  /* o limite da sinopse passa a ser visual (4 linhas), não uma contagem de caracteres
     chutada no JS: o corte por caracteres cortava no meio da palavra e jogava fora até
     70% do texto mesmo quando havia espaço sobrando no card */
  .card-body p{
    font-size:13px; color:var(--text-dim); line-height:1.5;
    display:-webkit-box; -webkit-box-orient:vertical; -webkit-line-clamp:4; line-clamp:4;
    overflow:hidden;
  }
  /* o limite de linhas acompanha o espaço real do card. Sinopses do TMDB em pt-BR vão de
     ~140 a ~570 caracteres, então num celular grande a pessoa lê a sinopse INTEIRA em vez
     de um trecho. Os cortes foram calibrados medindo: em todos eles o pôster fica por volta
     de 70% do card, que é a proporção que a passada 7 estabilizou.
     Container query e não media query porque solo e grupo têm cards de altura diferente no
     MESMO celular. Sem suporte, cai no clamp base de 4 linhas — degradação segura. */
  @container deck (max-height: 379px){ .card-body p{ -webkit-line-clamp:3; line-clamp:3; } }
  @container deck (min-height: 460px){ .card-body p{ -webkit-line-clamp:6; line-clamp:6; } }
  @container deck (min-height: 580px){ .card-body p{ -webkit-line-clamp:8; line-clamp:8; } }
  .stamp{
    position:absolute; top:36px; padding:9px 18px; border-radius:10px;
    font-family:'Bebas Neue'; font-size:32px; letter-spacing:2.5px; line-height:1;
    border:4px solid; opacity:0; pointer-events:none; z-index:3;
    box-shadow: 0 4px 18px rgba(0,0,0,.55);
  }
  .stamp.like{ left:20px; border-color: var(--like-green); color:var(--on-green); background: var(--stamp-like-bg); transform:rotate(-14deg); }
  .stamp.nope{ right:20px; border-color: var(--rose); color:var(--on-rose); background: var(--stamp-nope-bg); transform:rotate(14deg); }

  /* Faixa do primeiro card de cada pessoa. A dica de GESTO foi removida a pedido do dono
     (será repensada); o que fica é a regra do modo em grupo — "vote com sinceridade" — que
     antes ocupava permanentemente o painel de participantes. Some sozinha em 7s ou no
     primeiro toque, porque é abertura de rodada, não letreiro. */
  .card-hint{
    position:absolute; left:12px; right:12px; bottom:12px; z-index:4;
    padding:9px 12px; border-radius:12px;
    background: rgba(var(--bg-rgb), .86);
    backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px);
    border:1px solid var(--line);
    pointer-events:none;
    animation: hint-in .5s cubic-bezier(.16,1,.3,1) both;
    transition: opacity .35s ease, transform .35s ease;
  }
  .card-hint.fade{ opacity:0; transform: translateY(6px); }
  @keyframes hint-in{ from{ opacity:0; transform: translateY(10px); } }
  .card-hint .ch-rule{
    display:block; color: var(--gold-dim); font-weight:600;
    text-align:center; font-size:11px; line-height:1.35;
  }
  /* a faixa é absoluta (sair de cena não pode reflowar o card no meio da leitura), então o
     espaço dela precisa ser RESERVADO na sinopse — senão o texto corre por baixo do vidro e
     lê como conteúdo cortado. Com uma linha só (podendo quebrar em duas), 72px cobrem a
     caixa e ainda deixam respiro; a sinopse cede 3 linhas em vez das 2 de antes. */
  .card.has-hint .card-body{ padding-bottom:72px; }
  .card.has-hint .card-body p{ -webkit-line-clamp:3; line-clamp:3; }

  /* fileira do polegar: "curti" e "passo" são ações opostas, sem confirmação, no gesto mais
     repetido do app. 26px de separação entre dois alvos de 58px era pouco pra decisão
     irreversível — agora 40px entre alvos de 64px. */
  .actions{ display:flex; justify-content:center; align-items:center; gap:40px; margin-top:18px; }
  .action-btn{
    width:64px; height:64px; border-radius:50%; border:1.5px solid var(--edge);
    background: linear-gradient(180deg, var(--surface-2-top), var(--surface-2-bot));
    box-shadow: var(--shadow-1), inset 0 1px 0 var(--sheen-2);
    font-size:22px; cursor:pointer;
    display:flex; align-items:center; justify-content:center;
    transition: transform var(--dur-1) var(--ease-standard);
  }
  .action-btn:active{ transform: scale(.9); }
  .action-btn.nope{ color: var(--rose); border-color: rgba(var(--rose-rgb), .4); }
  .action-btn.like{ color: var(--like-green); border-color: rgba(var(--like-green-rgb), .5); }
  .action-btn.undo{ width:44px; height:44px; align-self:center; font-size:17px; color:var(--text-dim); }
  /* o undo some e volta no meio do baralho (modo cronometrado). Com display:none ele saía
     do fluxo e EMPURRAVA "curti"/"passo" lateralmente debaixo do polegar de quem estava
     votando; visibility mantém o espaço reservado e a fileira imóvel. */
  .action-btn.undo.is-hidden{ visibility:hidden; pointer-events:none; }

  /* MATCH TOAST */
  .match-banner{
    position:fixed; left:0; right:0; top:0; z-index:50;
    display:flex; justify-content:center; pointer-events:none;
  }
  .match-card{
    margin-top:60px; background: linear-gradient(135deg,var(--gold-tint-bg),var(--gold-tint-deep));
    border:2px solid var(--gold); border-top-color:var(--gold-edge); border-radius:var(--radius-md);
    padding:16px 22px;
    text-align:center; max-width:320px;
    /* elevação com offset e blur contido; o halo largo hairline lia como decoração */
    box-shadow: var(--shadow-2);
    opacity:0; transform: translateY(-20px) scale(.95);
    transition: opacity var(--dur-3) var(--ease-entrance), transform var(--dur-3) var(--ease-entrance);
  }
  .match-card.show{ opacity:1; transform: translateY(0) scale(1); }
  .match-card .mtitle{ font-family:'Bebas Neue'; letter-spacing:1.5px; color:var(--gold-ink); font-size:20px; }
  .match-card .msub{ font-size:13px; color:var(--text-dim); margin-top:4px; }
  /* O anúncio do segundo match em diante. Mesma peça, registro mais alto: a marca do match
     entra na linha do título e o ouro fecha em preenchimento, porque unanimidade não se diz
     com a mesma voz de "já são 2 curtidas". Ainda assim é FAIXA, não clímax — ela não pega
     toque, não prende foco e sai sozinha. */
  .match-card--unanime{
    border-color:var(--gold-edge);
    background: linear-gradient(135deg, var(--gold-tint-deep), var(--gold-tint-bg));
    box-shadow: var(--shadow-3);
  }
  .match-card--unanime .mtitle{
    display:flex; align-items:center; justify-content:center; gap:8px;
    font-size:23px; color:var(--gold-ink);
  }
  .match-card--unanime .msub{ color:var(--text); font-weight:700; }
  .match-card .mfoot{ font-size:11.5px; color:var(--text-soft); margin-top:6px; letter-spacing:.2px; }
  /* a marca pulsa uma vez ao entrar: é a "leve animação" que marca o acerto sem pedir nada.
     Quem pediu menos movimento recebe a faixa pronta — o texto diz a mesma coisa. */
  @media (prefers-reduced-motion: no-preference){
    .match-card--unanime.show .mtitle svg{ animation: extra-match-pop .52s var(--ease-emphasis) both; }
    @keyframes extra-match-pop{ from{ transform:scale(.55); opacity:0; } 60%{ transform:scale(1.16); } to{ transform:scale(1); opacity:1; } }
  }

  /* telas curtas em paisagem (celular deitado, tablets pequenos, janela redimensionada) */
  @media (orientation: landscape) and (max-height: 560px){
    .screen{ padding-bottom:90px; }
    /* Em paisagem o swipe rolava e a fileira curti/passo ficava ATRÁS da tabbar, fora da
       tela: quem girasse o aparelho no meio da rodada perdia os botões de votar. Aqui a
       tela volta a não rolar e o card cede altura pras ações, que é a única parte
       obrigatória. Os alvos continuam acima de 44px. */
    #swipe.active{
      overflow:hidden;
      padding-bottom: calc(84px + env(safe-area-inset-bottom));
    }
    /* min-height:0 em vez de um piso fixo: em paisagem o card absorve o que sobrar depois
       do cabeçalho e das ações, em vez de reivindicar altura e empurrar os botões pra fora */
    .card-stack{ min-height:0; }
    /* em paisagem o card CEDE altura pras ações, então o piso de segurança sai de cena:
       aqui ele reivindicaria altura demais e empurraria os botões pra fora da tela */
    .card{ height:min(100%, 220px); min-height:0; }
    /* COM CARTA NA MÃO A BARRA DE ABAS NÃO ESTÁ NA TELA (1.62.0), e os 84px acima eram a
       reserva para ela: em 740×400 sobravam vazios embaixo das ações enquanto o card ficava
       com 186px — o "+ Minha lista" começava ACIMA do topo do card e a pastilha do streaming
       saía espremida entre os dois botões. Sem a barra, a reserva vira card, e o teto de
       altura sobe para o que as três peças do canto do pôster precisam. */
    body.in-round #swipe.active{ padding-bottom: calc(14px + env(safe-area-inset-bottom)); }
    body.in-round .card{ height:min(100%, 300px); }
    .actions{ margin-top:8px; gap:28px; }
    .action-btn{ width:52px; height:52px; }
    .action-btn.undo{ width:44px; height:44px; }
    .round-bar{ min-height:34px; margin-bottom:6px; }
    .round-bar .avatar{ width:28px; height:28px; font-size:11px; }
  }

  /* GROUP PANEL */
  .group-panel{ display:flex; flex:none; }
  .avatars{ display:flex; }
  .avatar{
    width:34px;height:34px;border-radius:50%; display:flex;align-items:center;justify-content:center;
    font-size:12px; font-weight:700; background:var(--line); color:var(--text-dim); border:2px solid transparent;
    letter-spacing:-0.3px; flex:none;
  }
  .avatar.voted{ background: var(--gold); color:var(--on-gold); border-color:var(--gold); }
  /* na régua os avatares se sobrepõem levemente: 4 pessoas cabem em 104px em vez de 160,
     e a fila continua legível porque quem já votou é dourado sólido */
  .round-bar .avatars .avatar{ margin-right:-9px; border-color: var(--bg); background:var(--bg-panel); }
  .round-bar .avatars .avatar:last-child{ margin-right:0; }
  .round-bar .avatars .avatar.voted{ background: var(--gold); }
  .round-bar .avatars .avatar.active{ border-color: var(--gold); color:var(--gold-ink); }

  /* ===== RANKING ===== */
  /* ---- placar se lê de cima para baixo ----
     A rodada anterior desta tela desenhava o top 10 como carrossel horizontal.
     Medido na baseline: em 390px cabiam 1,9 dos 10 cartazes, sem contagem e sem
     afordância de que houvesse mais — ou seja, a ORDEM, que é a única informação
     que um ranking carrega, era justamente a que não dava para ver. E o eixo do
     gesto brigava com o resto do app, que rola na vertical.
     Agora o placar é uma coluna: o líder é o ÚNICO objeto levantado da tela e os
     perseguidores recuam para uma estante de linhas com UM eixo de texto só — a
     mesma lição de faixa constante que o Histórico pagou com `--hist-lane`. */
  #rank{
    --rank-place-lane:40px;   /* a coluna da posição é constante na lista inteira (numeral Bebas) */
    --rank-mov-lane:30px;     /* a coluna do movimento, idem */
    --rank-row-thumb:44px;
    --rank-gutter:14px;
    --rank-bleed:20px;        /* = padding lateral da tela: a faixa que rola sangra até a borda */
    /* A faixa de streamings não termina numa faca. O sangramento acima faz a rampa
       coincidir com a calha da página: parada no scroll 0, o chip mais à esquerda
       começa depois da dissolução e não é tocado por ela; ao rolar, é o chip que
       atravessa a rampa. Mesma ideia do véu da barra de abas, no outro eixo. */
    --rank-rail-fade: linear-gradient(90deg,
      transparent 0,
      #000 var(--rank-bleed),
      #000 calc(100% - var(--rank-bleed)),
      transparent 100%);
  }
  .rank-shell{ width:100%; max-width:1120px; margin:0 auto; }
  .rank-hero{ padding:2px 0 0; }
  /* ---- A MARQUISE (1.58.0, "Letreiro + parada", escolha do dono) ----
     O título da tela é o letreiro de um cinema de rua: painel creme aceso, letras pretas,
     lâmpadas em volta. É OBJETO da sala, como a TV do Início — por isso lê só os tokens
     --marquise-*, que não trocam de tema (letreiro aceso é creme de dia e de noite).
     AS LÂMPADAS SEGUEM O CONTORNO (1.59.0, pedido do dono). Uma grade de pontos em CSS só
     sabe fazer linhas retas, e nos quatro cantos arredondados as lâmpadas saíam em quadrado.
     Agora elas são círculos de um SVG (`.rank-lampadas`), postos pelo script ao longo de um
     retângulo arredondado que corre no MEIO da calha, com o raio da moldura menos meia
     calha — é o mesmo arco da borda, deslocado para dentro (`acenderLampadas`). O script
     mede a calha e o raio no CSS computado, então o layout de tela baixa (calha de 8px)
     também sai certo, e redesenha quando a largura muda.
     O brilho delas é luz de lâmpada de verdade (é o assunto do objeto), e mora só na borda.
     A ANIMAÇÃO É O CORRE-CORRE (pedido do dono): uma sim, uma não. As lâmpadas são numeradas
     em volta do letreiro e se dividem em PARES e ÍMPARES, que se revezam — enquanto uma
     metade acende a outra apaga, e o olho lê a luz correndo. Toca ao trocar de recorte
     (`piscarMarquise`); o repouso é tudo aceso, e com movimento reduzido não toca. */
  .rank-marquise{
    position:relative; padding:11px; border-radius:var(--radius-lg);
    background:var(--marquise-moldura);
    box-shadow:0 18px 30px -18px rgba(0,0,0,.9), inset 0 0 0 1px rgba(255,217,138,.25);
  }
  .rank-lampadas{
    position:absolute; inset:0; width:100%; height:100%; overflow:visible; pointer-events:none;
    filter:drop-shadow(0 1px 2px rgba(255,200,110,.85));
  }
  .rank-lampadas circle{ fill:var(--marquise-lampada); }
  @media (prefers-reduced-motion: no-preference){
    /* dois tempos iguais, sem transição entre eles: é lâmpada, não dimmer */
    .rank-marquise.is-acendendo .lampada-par{ animation:lampada-par .5s steps(1) 4; }
    .rank-marquise.is-acendendo .lampada-impar{ animation:lampada-impar .5s steps(1) 4; }
    @keyframes lampada-par{ 50%{ opacity:.18; } }
    @keyframes lampada-impar{ 0%{ opacity:.18; } 50%{ opacity:1; } }
  }
  .rank-placa{
    position:relative; z-index:1; padding:12px 12px 8px; border-radius:6px; text-align:center;
    background:
      repeating-linear-gradient(to bottom, transparent 0 33px, rgba(22,17,11,.08) 33px 34px),
      radial-gradient(120% 90% at 50% 20%, var(--marquise-placa-luz), var(--marquise-placa));
    box-shadow:inset 0 0 18px rgba(255,210,130,.55);
  }
  /* o título é Bebas em caixa alta com tracking largo: são as letras de encaixar do letreiro */
  .rank-titulo{
    margin:0; font-family:var(--font-display); font-weight:var(--t-display-weight);
    font-size:clamp(32px, 10.4vw, 42px); line-height:1; letter-spacing:4px;
    color:var(--marquise-tinta);
  }
  .rank-midia{ display:flex; justify-content:center; gap:4px; margin-top:4px; }
  .rank-midia-btn{
    min-height:44px; padding:0 12px; border:0; border-radius:var(--radius-xs); background:none; cursor:pointer;
    font-family:var(--font-display); font-weight:var(--t-display-weight); font-size:26px; line-height:1;
    letter-spacing:3px; color:var(--marquise-tinta-apagada);
  }
  .rank-midia-btn[aria-pressed="true"]{
    color:var(--marquise-tinta);
    text-decoration:underline; text-decoration-thickness:2px; text-underline-offset:4px;
  }
  .rank-midia-btn:focus-visible{ outline-color:var(--marquise-tinta); }

  /* ---- a janela, o recorte e o gênero ----
     A data da janela em destaque e, embaixo, o recorte que a lista está mostrando — as duas
     são DERIVADAS do que está embaixo (tipo, gênero, streaming e origem do dado). Ao lado,
     o filtro de gênero: um botão, e não mais uma fileira de chips, porque os streamings é que
     ficam à vista (pedido do dono). Escolhido, ele veste a tinta de ouro e diz o nome. */
  .rank-recorte{ display:flex; align-items:center; justify-content:space-between; gap:12px; margin-top:14px; }
  .rank-scope{
    min-width:0; margin:0; text-wrap:pretty;
    font-size:var(--t-support-size); line-height:var(--t-support-lh); color:var(--text-dim);
  }
  .rank-scope b{ display:block; color:var(--text); font-weight:700; }
  .rank-genero{
    flex:none; max-width:52%; min-height:44px; display:flex; align-items:center; gap:6px; padding:0 12px;
    border-radius:var(--radius-sm); border:1px solid var(--edge); border-top-color:var(--edge-lit);
    background:linear-gradient(180deg, var(--surface-2-top), var(--surface-2-bot)); color:var(--text);
    font-family:inherit; font-size:var(--t-support-size); font-weight:700; cursor:pointer;
  }
  .rank-genero > span{ min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
  .rank-genero .ico-svg{ flex:none; color:var(--text-dim); }
  .rank-genero.is-ativo{ border-color:rgba(var(--gold-rgb), .7); color:var(--gold-ink); }
  .rank-genero.is-ativo .ico-svg{ color:var(--gold-ink); }
  /* ---- a régua dos streamings, à vista ---- */
  .rank-rail{ display:flex; flex-wrap:wrap; align-items:center; gap:10px; margin:12px 0 0; }
  .provider-tabs{
    flex:1 1 100%;
    display:flex; gap:8px; overflow-x:auto;
    padding:0 var(--rank-bleed) 6px; margin-inline: calc(var(--rank-bleed) * -1);
    scrollbar-width:none; -ms-overflow-style:none;
    -webkit-mask-image: var(--rank-rail-fade);
    mask-image: var(--rank-rail-fade);
  }
  .provider-tabs::-webkit-scrollbar{ display:none; }
  .provider-tabs .chip{ white-space:nowrap; scroll-margin-inline: var(--rank-bleed); }

  /* ---- o placar ---- */
  .rank-board{ margin-top:14px; }
  .rank-list{ list-style:none; margin:0; padding:0; }
  .rank-list li{ margin:0; }
  /* o item inteiro é o alvo, e é um botão de verdade: Enter, Espaço e nome
     acessível vêm do elemento, não de um remendo de teclado */
  .rank-card{
    display:block; width:100%; padding:0;
    /* o reset do agente do usuário mora AQUI e não em cada variante: enquanto ele estava
       só na linha, o cartaz do pódio nasceu com o cinza #f0f0f0 de botão nativo por baixo
       do título — uma faixa clara que ninguém desenhou, e que o `elementsFromPoint` foi
       quem revelou (a caixa media certo e o texto estava lá) */
    background:none; border:0;
    font-family:inherit; color:inherit; text-align:left; cursor:pointer;
    /* o item que recebe foco pelo teclado precisa parar ABAIXO da barra de cima e
       ACIMA da de baixo: sem isto o navegador rolava o item para debaixo do
       cabeçalho grudado e o anel ficava escondido */
    scroll-margin-top:92px; scroll-margin-bottom: calc(var(--tabbar-h) + 16px);
  }
  /* ---- O 1º LUGAR: um banner com o próprio pôster (1.58.0) ----
     O pódio de três cartazes saiu com a mistura "Letreiro + parada" (escolha do dono). O
     líder continua sendo o ÚNICO objeto levantado da tela, agora como um banner: o cartaz à
     esquerda, o numeral grande, o título em Bebas, e ao fundo o MESMO pôster ampliado e
     desfocado sob um véu escuro — cada semana tem a cor do seu campeão. Tudo escrito ali é
     sobre imagem, então lê as tintas --on-image (que não trocam de tema).
     O fundo é um <img> e não `background-image`: o render desta tela não escreve estilo
     inline (há teste), e o <img> passa pelo mesmo `safePosterUrl` do cartaz. */
  .rank-lider{
    position:relative; overflow:hidden; isolation:isolate;
    display:grid; grid-template-columns:104px minmax(0,1fr); align-items:center; gap:14px;
    padding:16px; border-radius:var(--radius-lg);
    background:var(--card-bot, #08090d); color:var(--on-image);
    box-shadow:inset 0 0 0 1px var(--on-image-edge), var(--shadow-3);
  }
  .rank-lider-fundo{
    position:absolute; inset:-30px; z-index:-2; width:calc(100% + 60px); height:calc(100% + 60px);
    object-fit:cover; filter:blur(22px) saturate(1.2); opacity:.9; pointer-events:none;
  }
  .rank-lider::before{
    content:""; position:absolute; inset:0; z-index:-1; pointer-events:none;
    background:
      radial-gradient(90% 80% at 72% 45%, rgba(8,8,12,.45), transparent 75%),
      linear-gradient(180deg, rgba(8,8,12,.6), rgba(8,8,12,.88) 70%);
  }
  .rank-lider .pf{ width:104px; --pf-radius:var(--radius-sm); box-shadow:0 12px 24px -8px rgba(0,0,0,.8); }
  .rank-lider-info{ min-width:0; display:flex; flex-direction:column; }
  .rank-lider-num{
    display:flex; align-items:flex-end; gap:8px;
    font-family:var(--font-display); font-weight:var(--t-display-weight); font-size:64px; line-height:.8;
    color:var(--gold-on-image);
  }
  .rank-lider-num .rank-mov{ margin-bottom:6px; }
  .rank-lider-titulo{
    margin:8px 0 4px; font-family:var(--font-display); font-weight:var(--t-display-weight);
    font-size:26px; line-height:.95; letter-spacing:.3px; color:var(--on-image); text-wrap:balance;
    display:-webkit-box; -webkit-line-clamp:3; -webkit-box-orient:vertical; overflow:hidden;
  }
  .rank-lider-meta{ font-size:var(--t-support-size); line-height:var(--t-support-lh); color:var(--on-image-dim); }
  .rank-lider-placar{
    margin-top:8px;
    font-size:var(--t-num-size); font-weight:700; color:var(--on-image);
    letter-spacing:var(--t-num-track); font-variant-numeric:tabular-nums;
  }
  /* o streaming tem linha própria: numa linha só com a contagem, a quebra deixava um "· Disney+"
     começando a linha seguinte com o ponto solto */
  .rank-lider-onde{ margin-top:2px; font-size:var(--t-num-size); font-weight:700; color:var(--gold-on-image); }
  /* celular estreito: o título do líder cede tamanho (nunca linhas: cortar o nome do 1º com
     reticências foi a regressão que a revisão pegou) e o cartaz encolhe, para a primeira
     tela mostrar o começo da parada acima da doca (achado da revisão em 360×780) */
  @media (max-width:380px){
    .rank-lider{ grid-template-columns:88px minmax(0,1fr); gap:12px; padding:14px; }
    .rank-lider .pf{ width:88px; }
    .rank-lider-num{ font-size:54px; }
    .rank-lider-titulo{ font-size:22px; }
  }

  /* ---- o movimento contra a semana anterior ----
     Uma peça só, nas linhas, no banner e no detalhe: seta DESENHADA e número (subiu/caiu),
     um traço para a mesma posição, e "NOVO" em tinta de ouro para quem estreou no top. Sem
     semana anterior no banco, a peça não aparece — o app não inventa movimento. */
  .rank-mov{
    display:inline-flex; align-items:center; gap:2px; line-height:1; white-space:nowrap;
    font-family:ui-sans-serif, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; letter-spacing:0;
    font-size:var(--t-num-size); font-weight:800; font-variant-numeric:tabular-nums;
  }
  .rank-mov .ico-svg{ flex:none; }
  .rank-mov--sobe{ color:var(--rank-sobe); }
  .rank-mov--desce{ color:var(--rank-desce); }
  .rank-mov--igual{ color:var(--text-dim); }
  .rank-mov--novo{ color:var(--gold-ink); font-size:10.5px; letter-spacing:.8px; }
  .rank-lider .rank-mov--sobe{ color:var(--rank-sobe-on-image); }
  .rank-lider .rank-mov--desce{ color:var(--rank-desce-on-image); }
  .rank-lider .rank-mov--igual{ color:var(--on-image-dim); }
  .rank-lider .rank-mov--novo{ color:var(--gold-on-image); }

  /* ---- do 2º ao 10º: as linhas da parada ----
     Uma anatomia só, repetida: a posição grande em Bebas (a coluna é constante na lista
     inteira), a coluna do movimento, o pôster, e o texto com a BARRA DE MATCHES — a força de
     cada título medida contra o 1º lugar, que é o que um número solto não dizia. A largura
     da barra chega por uma variável ligada pelo script (`--forca`), porque o render não
     escreve estilo inline. Empate: a posição se repete, em tinta de apoio, e o texto diz. */
  .rank-row{
    display:grid;
    grid-template-columns: var(--rank-place-lane) var(--rank-mov-lane) var(--rank-row-thumb) minmax(0,1fr);
    align-items:center; gap:0 10px;
    padding:10px 4px;
    border-bottom:1px solid var(--rule);
    transition: background-color var(--dur-1) var(--ease-standard);
  }
  .rank-place{
    justify-self:end;
    font-family:var(--font-display); font-weight:var(--t-display-weight);
    font-size:34px; line-height:1; color:var(--text); font-variant-numeric:tabular-nums;
  }
  .rank-place--empate{ color:var(--text-soft); }
  .rank-mov-col{ display:flex; justify-content:center; }
  .rank-row .pf{ width:var(--rank-row-thumb); --pf-radius: var(--radius-xs); }
  /* COM A LETRA DO SISTEMA EM 150–200% (1.62.0) a linha tinha 210px e o nome do título
     ficava com 58: "Toy Story 5" saía como "Toy St…". Quem cede é o pôster da linha — o nome
     é o que se lê, e o cartaz grande do 1º continua lá em cima. O botão de gênero desce para
     a linha de baixo em vez de cortar a própria palavra ("Gên…"). */
  @media (max-width:300px){
    .rank-row{ grid-template-columns: 30px var(--rank-mov-lane) minmax(0,1fr); gap:0 8px; }
    .rank-row .pf{ display:none; }
    .rank-place{ font-size:26px; }
    .rank-recorte{ flex-wrap:wrap; }
    .rank-genero{ max-width:100%; }
  }
  .rank-info{ min-width:0; }
  .rank-info b{
    display:block; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
    font-size:var(--t-body-size); line-height:1.3; font-weight:700; color:var(--text);
  }
  .rank-info > span{
    display:block; margin-top:2px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
    font-size:var(--t-support-size); line-height:var(--t-support-lh); color:var(--text-soft);
  }
  .rank-forca{ display:flex; align-items:center; gap:8px; margin-top:6px; }
  .rank-forca i{
    display:block; flex:none; height:4px; width:calc(var(--forca, 1) * 72%); min-width:6px; border-radius:2px;
    background:linear-gradient(90deg, var(--gold-deep), var(--gold));
  }
  .rank-forca small{
    font-size:var(--t-support-size); font-weight:700; color:var(--text-dim);
    font-variant-numeric:tabular-nums; white-space:nowrap;
  }
  @media (prefers-reduced-motion: no-preference){
    .rank-list.is-entrando .rank-forca i{ animation:rank-barra .7s var(--ease-entrance); transform-origin:left; }
    @keyframes rank-barra{ from{ transform:scaleX(0); } }
  }

  /* ---- a unidade do placar, dita onde o primeiro número aparece ----
     A legenda morava no PÉ, depois de 1650px de rolagem: quem lia "14" no topo não tinha
     como saber o que aquilo contava, e o dono disse exatamente isso — o número não dizia
     nada. Ela mora logo depois da lista, e define "match" antes de explicar o peso. */
  .rank-legend{
    max-width:52ch; margin:12px 0 16px; color:var(--text-dim);
    font-size:var(--t-support-size); line-height:var(--t-support-lh); text-wrap:pretty;
  }
  /* ---- a saída da tela ----
     "Jogar com esse top" é a ação que a tela existe pra oferecer, e ficava no fim de toda
     a rolagem — o oposto do princípio de ir do abrir ao play em minutos. Agora ela mora na
     mesma doca do Início, com a mesma receita de véu: uma gramática só para "a ação desta
     tela", e não duas curvas parecidas que divergem no primeiro ajuste. */
  /* a mesma anatomia da doca do Início: coluna, e o botão ocupa a largura inteira dela.
     Sem isto o `.cta` é `inline-flex` — largura do rótulo — e o botão do placar nascia
     encostado à esquerda enquanto o "Começar agora" atravessa a doca de ponta a ponta. */
  .rank-cta-bar{ display:flex; flex-direction:column; }
  .rank-cta-bar .cta{ margin-top:0; width:100%; }
  /* rampa mais curta que a do Início, e por um motivo medido: lá a doca carrega um resumo
     e precisa de uma transição longa; aqui é um botão só, e cada pixel de véu é pixel de
     CARTAZ dissolvido — no estado sem rede, em que o aviso empurra o pódio 113px pra
     baixo, a rampa de 60 comia o rodapé dos três cartazes. */
  .rank-cta-bar{ --dock-veil:40px; }

  /* ---- O RESPIRO DO PÉ É DA DOCA, NÃO DA TELA ----
     `.screen` reserva 130px por baixo para as telas que terminam em conteúdo solto. Aqui
     embaixo mora a doca do CTA, que já leva o próprio `margin-bottom: var(--dock-space)`:
     os dois somados deixavam 129px de NADA entre o botão e a barra de abas (medido no fim
     da rolagem — CTA terminando em 610 com a barra começando em 739). Era o "espaço vazio"
     que o dono viu. O Início já tinha pago esta conta (`#setup{ padding-bottom:0 }`); o
     Ranking ficou para trás quando ganhou a mesma doca.
     Os OUTROS TRÊS PÉS desta tela não têm doca — o detalhe de um título, o vazio e o
     esqueleto —, e por isso pagam o respiro por dentro: sem isto o último pixel de cada um
     encostaria na navegação. */
  #rank{ padding-bottom:0; }
  /* O RANKING ROLA DENTRO DA PRÓPRIA TELA, como o Início e a Cinemateca (1.64.1). Era a
     única aba que rolava o `.phone` — um rolador do tamanho exato da janela, que o Chrome do
     Android promove a rolador da PÁGINA ("implicit root scroller") e cuja barra ele desenha
     por conta própria, por cima do `scrollbar-width:none`. Foi por isso que o dono viu a
     barra só aqui, e que ela não aparecia no navegador do computador. A doca continua
     `sticky`: ela gruda no pé DESTE rolador, que é o mesmo pé da janela. */
  #rank.active{ min-height:0; overflow-y:auto; overflow-x:hidden; }
  #rankDetail, .rank-empty, .rank-skeleton{
    padding-bottom: calc(var(--dock-space) + var(--tabbar-veil) + 12px);
  }

  /* ---- sem conexão é COMPOSIÇÃO, não erro ----
     O produto decidiu que sem rede a pessoa vê o próprio top. Isso é um estado
     de produto: material, prosa legível e uma retomada — não um aviso de 11px
     espremido entre o cabeçalho e a lista. */
  .rank-offline{
    display:flex; align-items:center; flex-wrap:wrap; gap:10px;
    margin-top:16px; padding:12px 14px;
  }
  .rank-offline .ico-svg{ color:var(--gold-dim); flex:none; }
  .rank-offline p{
    flex:1 1 200px; min-width:0; color:var(--text-soft);
    font-size:var(--t-support-size); line-height:var(--t-support-lh);
  }
  .rank-offline .btn{ flex:none; padding:0 14px; }

  /* ---- vazio: três causas, três composições ---- */
  /* ---- P3-R3 · filtro não fica de pé sobre conjunto vazio ----
     Com o placar vazio, o alternador Filmes/Séries e os sete recortes de streaming
     continuavam de pé sobre coisa nenhuma: dois trilhos de controle oferecendo recortar
     o que não existe. A composição de vazio já carrega a explicação E a saída (começar
     uma rodada), então ela basta sozinha.
     A EXCEÇÃO é o vazio POR FILTRO: ali o placar tem conteúdo, só não naquele recorte —
     e o trilho é justamente o que desfaz o filtro, então ele fica. `rank-empty` distingue
     os dois casos pelo botão que cada um carrega. */
  #rank:has(.rank-empty [data-rank-start]) .rank-rail,
  #rank:has(.rank-empty [data-rank-start]) .rank-genero{ display:none; }
  .rank-empty{ text-align:center; margin:44px auto 0; max-width:42ch; color:var(--text-dim); }
  .rank-empty p{ font-size:var(--t-body-size); line-height:var(--t-body-lh); text-wrap:pretty; }
  .rank-empty .btn{ margin-top:18px; padding:0 18px; }

  /* ---- carregamento: a forma do placar, não uma frase ---- */
  /* o esqueleto tem a forma do que vem: o banner do líder e as linhas da parada. Com o
     pódio antigo aqui a lista pularia de forma ao chegar. */
  .rank-skel-lider{ height:188px; border-radius:var(--radius-lg); margin-bottom:10px; }
  .rank-skel-row{
    display:grid;
    grid-template-columns: var(--rank-place-lane) var(--rank-mov-lane) var(--rank-row-thumb) minmax(0,1fr);
    align-items:center; gap:0 10px;
    padding:12px 4px; border-bottom:1px solid var(--rule);
  }
  .rank-skel-thumb{ width:var(--rank-row-thumb); aspect-ratio:2/3; border-radius:var(--radius-xs); }
  .rank-skel-line{ height:12px; border-radius:var(--radius-xs); }
  .rank-skel-line--sm{ height:10px; width:52%; }
  .rank-skel-line--place{ height:24px; width:70%; justify-self:end; }

  @media (hover:hover){
    .rank-row:hover{ background: var(--sheen-1); }
  }

  /* ---- telas curtas: paisagem no celular e 200% de zoom ----
     A 200% a dobra inteira era cabeçalho e recortes, e o placar começava abaixo
     dela. O cabeçalho cede ESCALA, não conteúdo: a linha de recorte é o que
     explica de onde vem o dado e não pode sair da tela. */
  @media (max-height:620px){
    /* o letreiro cede ESCALA, não conteúdo: a linha de recorte é o que explica de onde vem
       o dado e não pode sair da tela. O banner do líder cede altura pelo cartaz. */
    .rank-marquise{ padding:8px; }
    .rank-placa{ padding:8px 10px 4px; }
    .rank-titulo{ font-size:28px; }
    .rank-midia-btn{ font-size:22px; }
    .rank-recorte{ margin-top:10px; }
    .rank-rail{ margin-top:10px; gap:8px; }
    .rank-board{ margin-top:12px; }
    .rank-lider{ min-height:0; grid-template-columns:80px minmax(0,1fr); }
    .rank-lider .pf{ width:80px; }
    .rank-lider-num{ font-size:48px; }
    .rank-legend{ margin:10px 0 12px; }
  }

  /* geometria só: a moldura (proporção, anel, sombra, carregamento, fallback)
     vem da primitiva .pf lá da fundação */
  .rank-thumb{ width:44px; --pf-radius: var(--radius-xs); font-size:18px; }

  /* ---- o encosto da barra de abas no ranking ----
     O placar rola POR BAIXO de uma barra fixa e opaca. Na baseline, no estado sem
     conexão, o CTA da tela terminava cortado a faca pela borda de 1px da barra.
     Aqui a aresta dissolve com o MESMO par de tokens que o Início e o Histórico
     consomem: `--tabbar-veil` / `--tabbar-veil-fade`. Fonte igual garante curva
     igual por construção; valor igual só garantiria até alguém editar um lado. */
    body:has(#rank.active) .tabbar::before{
      content:''; position:absolute; left:0; right:0; bottom:100%;
      height:var(--tabbar-veil);
      background:var(--tabbar-veil-fade);
      pointer-events:none;
    }
    /* e o VIDRO da mesma dissolução. O scrim sozinho tem zona de transição mais curta
       que uma linha de texto — era o que decepava a linha do placar na aresta. */
    body:has(#rank.active) .tabbar::after{
      content:''; position:absolute; left:0; right:0; bottom:100%;
      height:var(--tabbar-veil); pointer-events:none;
      backdrop-filter: var(--veil-glass); -webkit-backdrop-filter: var(--veil-glass);
      -webkit-mask-image: var(--veil-glass-mask);
              mask-image: var(--veil-glass-mask);
    }
  /* ---- e o véu RECOLHE onde a doca é fixa — a mesma regra do Início (P1-H15) ----
     A regra é de escopo, não de gosto: o véu dissolve CONTEÚDO QUE ROLA numa aresta fixa,
     e nunca escurece um CONTROLE que mora naquela faixa. Com a doca do placar fixa, ela
     pinta `--bg` opaco da própria rampa até a barra: não sobra nada por baixo para
     dissolver, e o único efeito visível do vidro é apagar o botão — foi o que o dono viu
     ("no ranking tem um blur que não fica legal"). Abaixo de 621px a doca volta ao fluxo,
     o placar rola de novo para debaixo da barra, e ali o véu tem trabalho outra vez.
     O par de media queries particiona a altura: nenhuma faixa fica sem regra nem com duas. */
  @media (min-height:621px){
    body:has(#rank.active) .tabbar::before,
    body:has(#rank.active) .tabbar::after{ content:none; }
  }

  /* cabeçalho de estado do resultado passado: rótulo discreto, sem competir com o título
     nem com o "Recomeçar" que fecha a coluna */
  .round-over{
    display:flex; align-items:center; justify-content:center; gap:7px;
    /* margem negativa contra o gap de 14px da coluna: encosta no título, porque rotula
       ele. Uma borda no meio diria que são coisas diferentes — são a mesma. */
    margin:0 0 -6px; padding:0;
    font-size:var(--t-support-size); font-weight:600; letter-spacing:.2px; color:var(--text-dim);
  }
  .round-over svg{ color:var(--gold-dim); flex:none; }
  .empty-state{ text-align:center; margin-top:60px; color:var(--text-dim); }
  /* ONDE ASSISTIR no detalhe (1.67.0): os streamings que a casa abre são botões; os outros,
     só o nome. Os da pessoa vêm primeiro e com a aresta de ouro apagado (estado, não ação). */
  .detail-onde{ margin-top:16px; display:grid; gap:8px; }
  .detail-onde-titulo{ margin:0; font-size:13px; font-weight:800; letter-spacing:.4px; color:var(--text); }
  .detail-onde-lista{ display:flex; flex-wrap:wrap; gap:8px; }
  .onde-chip{
    min-height:36px; padding:6px 12px; display:inline-flex; align-items:center;
    border-radius:var(--radius-pill); border:1px solid var(--edge);
    background:linear-gradient(180deg, var(--surface-2-top), var(--surface-2-bot));
    color:var(--text); font:inherit; font-size:13px; font-weight:700; cursor:pointer;
  }
  .onde-chip.is-meu{ border-color:var(--gold-dim); color:var(--gold-ink); }
  .onde-chip--texto{ cursor:default; background:transparent; color:var(--text-soft); font-weight:600; }
  .detail-onde-nada, .detail-onde-alugar{ margin:0; font-size:13px; color:var(--text-soft); }
  /* O BOTÃO DO ESTADO VAZIO MORA NA PRÓPRIA LINHA (1.66.4). O `.cta` é inline-flex, e logo
     depois do texto ele pousava no meio da frase ("…tente de novo. [Voltar]") — a auditoria
     cega de 2026-10-05 viu em sala não encontrada e em rodada sem internet. */
  .empty-state > .cta{ display:flex; width:fit-content; margin-inline:auto; }
  /* contador de progresso da montagem do baralho: presente, sem competir com o texto */
  .deck-progress{ display:block; margin-top:8px; font-size:12px; color:var(--gold-dim); font-variant-numeric:tabular-nums; }
  .deck-progress:empty{ display:none; }

  /* SETTINGS GEAR + MODAL */
  /* material e aresta vêm da gramática .btn--secondary da fundação */
  .gear-btn{
    width:44px; height:44px; border-radius:var(--radius-sm);
    color:var(--text-dim); font-size:16px; cursor:pointer;
    display:flex; align-items:center; justify-content:center;
  }
  /* ---- ENQUANTO A FOLHA ESTÁ ABERTA, A ENGRENAGEM RECUA ----
     A comparação cega notou que ela continuava "visível e ativa atrás da
     folha". O scrim já a impedia de ser clicada, mas ela continuava com material
     próprio — borda, gradiente e fio de luz — brilhando por trás de uma peça que
     ela mesma abriu, e disputando o canto superior direito com o X do painel.
     Aqui ela devolve o material e vira parte do fundo escurecido. Não é estado
     desabilitado (que tem cor própria e mora no botão): é a peça de ORIGEM
     saindo de cena enquanto o destino está aberto. */
  body:has(#settingsModal.show) .gear-btn{
    background:none; border-color:transparent; box-shadow:none;
    color:var(--text-disabled); pointer-events:none;
  }
  /* OS CONTROLES NUNCA SAEM DA TELA; quem cede é a marca (1.62.0). Com a letra do sistema
     em 200% o Chrome do Android desenha o app em ~187px, e a barra somava 20+114+8+44+8+44+20
     = 258px: a engrenagem dos Ajustes ficava 43px além da borda, sem rolagem que a trouxesse
     — a única porta para os Ajustes, inalcançável. A marca pode encolher e, no limite, ser
     cortada; o alvo de toque de 44px não encolhe. */
  .topbar-actions{ display:flex; align-items:center; gap:8px; flex:none; }
  .topbar .logo{ min-width:0; overflow:hidden; white-space:nowrap; }
  @media (max-width:300px){
    .topbar{ padding-inline:12px; gap:6px; }
    .topbar .logo{ font-size:20px; letter-spacing:1px; }
    .topbar-actions{ gap:4px; }
  }
  @media (max-width:220px){ .topbar .logo{ font-size:16px; } }
  /* o nome inteiro na barra ocupava até 120px pra dizer algo que a pessoa já sabe (quem
     ela é). Vira um disco de iniciais — ou a foto do Google, que identifica sem ler. */
  .account-pill{
    width:44px; height:44px; padding:0; border-radius:50%;
    border:1px solid rgba(var(--gold-rgb), .45); border-top-color:var(--gold-edge);
    background: linear-gradient(180deg, var(--gold-tint-bg), var(--gold-tint-deep));
    box-shadow: var(--shadow-1), inset 0 1px 0 rgba(var(--gold-rgb), .16);
    color:var(--gold-ink);
    font-size:13px; font-weight:800; letter-spacing:.2px;
    display:flex; align-items:center; justify-content:center;
    overflow:hidden; flex:none; cursor:pointer; font-family:inherit;
  }
  .account-pill img{ width:100%; height:100%; object-fit:cover; display:block; }
  .account-panel-card{
    background: var(--bg-panel); border:1px solid var(--line); border-radius:20px;
    padding:30px 22px 24px; text-align:center;
  }
  /* ---- painel de conta ---- */
  .account-avatar{
    width:64px; height:64px; margin:0 auto; border-radius:50%;
    display:flex; align-items:center; justify-content:center;
    background: var(--gold-tint-bg); border:1px solid rgba(var(--gold-rgb), .35);
    color:var(--gold-dim); overflow:hidden;
  }
  .account-avatar img{ width:100%; height:100%; object-fit:cover; }
  /* blocos internos alinham à esquerda: o cartão é centralizado, mas formulário e
     números precisam de uma margem de leitura estável */
  .account-block{ text-align:left; margin-top:22px; padding-top:18px; border-top:1px solid var(--line); }
  .account-block .section-label{ margin-top:0; }
  .account-nick-row{ display:flex; gap:8px; align-items:stretch; }
  .account-nick-row .nickname-input{ flex:1; margin-bottom:0; }
  .account-nick-row .result-btn{ flex:none; padding:0 16px; }
  /* campo informativo, não editável: diz de onde vem o baralho do top sem sugerir que dá
     pra mudar ali (quem manda é o filtro do ranking) */
  .readonly-field{
    display:flex; align-items:center; gap:9px; margin:0;
    padding:12px 14px; border-radius:12px;
    background:rgba(var(--bg-rgb), .5); border:1px solid var(--line);
    color:var(--text); font-size:13px; line-height:1.4;
  }
  .readonly-field svg{ color:var(--gold-dim); flex:none; }
  .account-block .chip-hint{ margin-top:8px; }
  .account-block .stats-numbers .stat b{ font-size:28px; }
  .modal-overlay{
    position:fixed; inset:0; background:var(--scrim);
    display:none; align-items:flex-end; justify-content:center; z-index:80;
  }
  .modal-overlay.show{ display:flex; }
  /* material nível 3: a folha é a peça mais alta da pilha */
  .modal-sheet{
    width:100%; max-width:420px;
    background: linear-gradient(180deg, var(--surface-3-top), var(--surface-3-bot));
    border:1px solid var(--edge-strong); border-top-color:var(--edge-lit); border-bottom:none;
    border-radius:var(--radius-xl) var(--radius-xl) 0 0;
    transform: translateY(100%); transition: transform var(--dur-3) var(--ease-emphasis);
    max-height:min(92vh, 880px);
    display:flex; flex-direction:column;
    box-shadow: var(--shadow-3), inset 0 1px 0 var(--sheen-3);
  }
  .modal-drag-zone{ flex:none; padding:14px 20px 8px; cursor:grab; touch-action:none; }
  .modal-scroll{
    /* `overflow-x:hidden` e não o `visible` implícito: o vidro do masthead parado se estende
       além do respiro lateral pra cobrir a coluna inteira, e sem o corte aqui esse excedente
       viraria rolagem horizontal na folha. */
    overflow-y:auto; overflow-x:hidden; padding:0 18px calc(24px + env(safe-area-inset-bottom));
    /* SEM ÂNCORA DE ROLAGEM. Ao reabrir os Ajustes, o painel fecha todas as revelações e
       zera a rolagem — e o Chrome desfazia o zero: com o conteúdo encolhendo, o ancoramento
       automático puxa a posição de volta para manter o que estava à vista, e a folha reabria
       no meio do inventário (medido: 393px em vez de 0). Aqui não há o que ancorar: quem
       decide a posição de abertura é o app. */
    overflow-anchor:none;
    scrollbar-width:none; -ms-overflow-style:none;
    overscroll-behavior: contain;
  }
  .modal-scroll::-webkit-scrollbar{ display:none; }
  .modal-overlay.show .modal-sheet{ transform: translateY(0); }
  .modal-handle{ width:40px; height:5px; background:rgba(255,255,255,0.3); border-radius:4px; margin:0 auto; }
  /* font-weight explícito: estes títulos viraram h2 e o Bebas Neue não tem negrito real */
  .modal-title{ position:relative; font-family:'Bebas Neue', ui-sans-serif, sans-serif; font-weight:400; font-size:34px; line-height:1; letter-spacing:1px; margin:0; padding-top:8px; }
  .modal-title:focus{ outline:none; }
  .modal-sub{ max-width:62ch; font-size:13px; line-height:1.5; color:var(--text-soft); margin:7px 0 0; }
  /* O MASTHEAD FICA. Rolando, "Ajustes" saía da tela e levava junto o X — que é o único
     controle de fechamento que o teclado alcança e o único no alcance do polegar. Quem
     estava no fim do painel tinha que rolar de volta pra achar a saída.
     Parado, porém, ele custaria caro: display de 34px + prosa + respiro somam ~90px dos
     ~747 que a folha tem num 375×812, e seriam 90px cobrados em toda rolagem. Por isso ele
     ENCOLHE — passada a primeira rolagem, o display vira rótulo e a prosa sai. O que fica é
     onde a pessoa está e como sair, que é a função de uma barra parada. */
  .settings-header{
    position:sticky; top:0; z-index:6; padding:8px 52px 14px 2px;
  }
  /* o vidro nasce só quando há conteúdo passando por baixo. `inset` lateral negativo cobre
     o respiro do .modal-scroll — o excedente é cortado pelo overflow-x da própria coluna,
     e por isso ele não pode ser `visible` (ver a regra do .modal-scroll acima). */
  .settings-header::before{
    content:''; position:absolute; inset:-2px -30px 0; z-index:-1; pointer-events:none;
    /* SÓLIDA, e a aresta é quem marca o limite. Com uma rampa no pé da faixa, a linha que
       passava por baixo ficava meio visível bem na borda — lê como texto cortado, não como
       camada. É o oposto da doca do Início: lá o véu dissolve conteúdo NO MEIO da tela e
       precisa de transição longa; aqui a faixa é uma barra de aplicativo, e barra de
       aplicativo tem borda. A tinta é a da própria folha, senão ela lê como peça colada. */
    background: rgb(var(--surface-3-rgb));
    box-shadow: 0 1px 0 var(--rule);
    opacity:0; transition: opacity var(--dur-2) var(--ease-standard);
  }
  .settings-header.is-stuck::before{ opacity:1; }
  /* A FAIXA PARADA PRECISA CABER O X INTEIRO. Encolhida só pelo padding, ela media 39px
     contra os 44px do botão — e o disco vazava por baixo do vidro, meio dentro e meio
     fora. Aumentar a faixa é o caminho certo: o alvo de toque não pode encolher para
     caber num desenho. Com `min-height` e alinhamento central, o título e o X dividem a
     mesma linha e o disco fica com folga igual em cima e embaixo. */
  .settings-header.is-stuck{
    display:flex; align-items:center; min-height:52px;
    padding-top:2px; padding-bottom:6px;
  }
  .settings-header.is-stuck .settings-close-icon{ top:50%; transform:translateY(-50%); }
  /* o título desce de DISPLAY para o papel de linha: some a voz de cartaz, fica a etiqueta
     de "você está em Ajustes". A troca é SECA, sem transição, e é uma decisão: animar
     `font-size` e `padding` é animar LAYOUT — reflow a cada quadro, no elemento que está
     grudado enquanto a coluna inteira rola debaixo dele. O único movimento aqui é a
     opacidade do vidro, que é composição pura. */
  .settings-header.is-stuck .modal-title{ font-size:21px; letter-spacing:.5px; padding-top:6px; }
  .settings-header.is-stuck .modal-sub{ display:none; }
  /* O X mora no masthead, ancorado à direita do título. Ele já viveu aqui antes da barra
     de destino — o que tinha empurrado ele para fora era a barra disputando a mesma faixa.
     Sem a barra, a faixa é dele de novo: peça de 44x44 alinhada ao topo do cabeçalho,
     sempre à mão porque o masthead não rola junto com o inventário na largura. */
  .settings-close-icon{
    position:absolute; top:2px; right:0; z-index:4;
    width:44px; height:44px; padding:0;
    border:1px solid var(--edge-strong); border-top-color:var(--edge-lit); border-radius:50%;
    background: linear-gradient(180deg, var(--surface-2-top), var(--surface-2-bot));
    box-shadow: inset 0 1px 0 var(--sheen-2);
    color:var(--text);
    display:flex; align-items:center; justify-content:center; cursor:pointer;
  }
  /* ================= AJUSTES — centro de controle =================
     Duas regiões: ESTADO (o ledger, o que é verdade agora) e INVENTÁRIO
     (o que dá pra mudar). No celular elas empilham; na largura, viram
     colunas de verdade, com o estado parado e os controles rolando.

     MATERIAL: a folha já é nível 3 (a peça mais alta da pilha). Um card
     de nível 1 ou 2 dentro dela ficaria mais ESCURO que o fundo e leria
     como buraco, não como peça apoiada. A receita da fundação é mantida
     — luz de cima, aresta clara em cima, sombra de contato — só que o
     preenchimento é véu translúcido: assim o card levanta sobre qualquer
     ponto do gradiente da folha, e não sobre um tom fixo. */
  /* ================= A REGRA ÚNICA DO PAINEL =================
     Duas colunas, uma divisória, um raio, três papéis de texto.

     Toda linha do painel — eixo de estado, switch, destino ou detalhe — abre
     com o MESMO slot de glifo começando em --set-pad, então todo rótulo cai em
     --set-label. O fiapo de 1 px nasce exatamente nessa coluna, sempre.
     Cabeçalho e prosa de grupo entram na mesma coluna de texto; peça aninhada
     (ação destrutiva, saída para documento) também. Não há linha de exceção:
     era a exceção, não a ambição, que cobrava o preço aqui. */
  #settingsModal{
    --settings-lift: linear-gradient(180deg, var(--sheen-3), var(--sheen-1));
    --set-pad:15px; --set-lead:26px; --set-gap:12px;
    --set-label: calc(var(--set-pad) + var(--set-lead) + var(--set-gap));
    /* O papel LINHA pesa 600, o papel GRUPO pesa 700 e mede 16: dois degraus de
       diferença, não um. Com os dois em 700 só o corpo separava nível 2 de nível
       3, e 16 contra 14 px é diferença que o olho não cobra — "Conta e
       sincronização" e "Sons do app" liam como o mesmo posto. */
    --set-row-size:14px; --set-row-lh:1.3; --set-row-weight:600; --set-support-lh:1.45;
    /* ---- RAMPA DO VÉU: a mesma do Início, não uma segunda ----
       O Início e o Histórico dissolvem a aresta de baixo com `--tabbar-veil` /
       `--tabbar-veil-fade`: 54 px em QUATRO paradas. Aqui eram 20 px em duas, e
       20 px em duas é a rampa curta que o Histórico já tinha documentado como
       insuficiente — nos últimos px ela está quase opaca e nos primeiros mal
       escurece, então o que passa por baixo continua lendo como cortado a faca.
       Foi o que a comparação cega viu no último cartão do painel.
       A altura e a curva agora são as do Início. A diferença é só de MECANISMO:
       lá a rampa é um scrim pintado por cima do fundo da tela; aqui o fundo é o
       gradiente da folha, que um scrim de cor chapada não acompanharia — então
       a mesma curva é escrita como MÁSCARA. Onde o véu do Início cobre com alfa
       1 / .92 / .55 / 0 de baixo para cima, a máscara daqui revela com
       0 / .08 / .45 / 1 nas mesmas distâncias. */
    --set-veil:54px;
    --set-veil-fade: linear-gradient(180deg,
      #000 calc(100% - var(--set-veil)),
      rgba(0,0,0,.45) calc(100% - var(--set-veil) * .58),
      rgba(0,0,0,.08) calc(100% - var(--set-veil) * .26),
      transparent 100%);
  }
  /* Toda região de Ajustes que rola termina numa rampa, nunca num corte seco
     nem numa barra do sistema. A rampa mora somente na aresta de baixo, como
     nas outras superfícies, e cai sobre padding para não cobrir conteúdo. */
  #settingsModal .modal-scroll{
    /* o respiro do pé precisa ser maior que a rampa inteira */
    padding-bottom: calc(var(--set-veil) + 10px + env(safe-area-inset-bottom));
    -webkit-mask-image: var(--set-veil-fade);
            mask-image: var(--set-veil-fade);
  }
  /* ---- e o que atravessa a rampa fica ATRÁS DE VIDRO, não decapitado ----
     Mesma receita do `.setup-cta-bar::before` do Início, pelos mesmos motivos:
     uma rampa só de opacidade tem zona de transição mais curta que uma linha de
     texto, então o topo dos glifos chega quase limpo e a base quase apagada — e
     um rótulo assim lê como quebrado, não como "tem mais abaixo". O desfoque
     tira a legibilidade ANTES de tirar o brilho, e a máscara faz o desfoque
     nascer do nada para a borda do vidro não virar a segunda aresta dura. */
  #settingsModal .modal-sheet{ position:relative; }
  #settingsModal .modal-sheet::after{
    content:''; position:absolute; left:0; right:0; bottom:0;
    height:var(--set-veil); pointer-events:none;
    border-bottom-left-radius:inherit; border-bottom-right-radius:inherit;
    backdrop-filter: var(--veil-glass); -webkit-backdrop-filter: var(--veil-glass);
    -webkit-mask-image: var(--veil-glass-mask);
            mask-image: var(--veil-glass-mask);
  }
  .settings-control-center{ display:flex; flex-direction:column; min-width:0; }
  .settings-groups{ min-width:0; }

  /* ---- os três papéis de texto (o quarto é o display, só na entrada) ---- */
  /* GRUPO: título do ledger, dos quatro grupos e da zona de dados. Um só. */
  .settings-group-head h3, .settings-danger h3, .settings-danger-block h4{
    margin:0; color:var(--text);
    font-size:var(--t-group-size); line-height:var(--t-group-lh);
    letter-spacing:var(--t-group-track); font-weight:var(--t-group-weight);
  }
  /* LINHA: o rótulo de qualquer linha e o rótulo da ação. Um degrau abaixo do
     grupo no corpo E no peso — o rótulo de ação entra aqui junto, senão o painel
     ganharia um quinto papel só para botão. */
  .settings-card .setting-copy b, .settings-nav-copy b,
  .settings-details summary, .settings-action, .settings-footer .modal-close{
    font-size:var(--set-row-size); line-height:var(--set-row-lh);
    font-weight:var(--set-row-weight); letter-spacing:0;
  }
  /* APOIO: toda a prosa e todo metadado, sem nenhum rótulo em caixa alta. */
  #settingsModal .modal-sub, .settings-group-summary, .settings-save-state,
  .settings-card .setting-copy span,
  .settings-nav-copy > span, .settings-details-body > p, .settings-danger p,
  .settings-version, #settingsModal .source-row p, .settings-link-quieto{
    font-size:var(--t-support-size); line-height:var(--set-support-lh);
    font-weight:var(--t-support-weight); letter-spacing:0;
  }

  /* ---- o slot de glifo: uma caixa só, em toda linha e em todo cabeçalho ----
     Um matiz só (ouro discreto). A cor de um glifo só muda quando ele REPORTA
     estado, e só há dois desvios possíveis: ouro pleno = conta conectada,
     rosa = erro. Nenhum dos dois é ornamento. */
  /* o glifo mora sempre na PRIMEIRA linha do rótulo, não no meio da linha:
     numa linha de duas ou três linhas de texto, centralizar jogava o glifo
     para o meio da descrição e desfazia a leitura em coluna */
  .settings-group-ico, .settings-save-ico, .settings-nav-ico, .settings-row-ico{
    flex:none; align-self:flex-start; width:var(--set-lead); height:20px;
    display:flex; align-items:center; justify-content:center;
    color:var(--gold-dim);
  }
  .settings-row-ico.is-signed{ color:var(--gold-ink); }
  .settings-save-ico[data-state="error"]{ color:var(--on-error); }


  /* ---- grupos: título fora, controles dentro do card, tudo na mesma coluna ---- */
  .settings-group{ padding:22px 0 0; }
  /* o ícone é irmão do título, não parte dele: marca o lugar sem entrar no nome
     acessível do cabeçalho */
  .settings-group-head{
    display:flex; align-items:center; gap:var(--set-gap); padding-inline:var(--set-pad);
  }
  .settings-group-summary{
    margin:5px 0 11px; padding:0 var(--set-pad) 0 var(--set-label); color:var(--text-soft);
  }
  /* três planos: o card é o grupo, a linha vive dentro dele e o detalhe raro
     abre dentro da linha. Antes eram três réguas finas e nenhuma hierarquia. */
  .settings-card{
    overflow:hidden;
    border:1px solid var(--edge); border-top-color:var(--edge-lit);
    border-radius:var(--radius-md);
    background: var(--settings-lift);
    box-shadow: var(--shadow-1), inset 0 1px 0 var(--sheen-2);
  }
  /* A ÚNICA divisória do painel: 1 px de --rule, nascendo na coluna do rótulo e
     indo até a borda do card. Vale entre linhas irmãs do card e entre eixos do
     ledger — não existe segunda gramática. */
  .settings-card > *{ position:relative; }
  .settings-card > * + *::before{
    content:''; position:absolute; top:0; left:var(--set-label); right:0;
    height:1px; background:var(--rule);
  }
  .settings-card .setting-row{
    position:relative; padding:13px var(--set-pad); border-bottom:0; gap:var(--set-gap);
  }
  .settings-card .setting-copy{ flex:1; min-width:0; }
  .settings-card .setting-copy b{ display:block; }
  .settings-card .setting-copy span{ display:block; margin-top:3px; color:var(--text-soft); }
  /* As duas zonas vermelhas têm a mesma classe, mas uma é <button> e a outra é <a> — e o
     navegador trata as duas de formas diferentes: o botão encolhe até o texto, a âncora
     estica até a margem e ainda vem sublinhada. O resultado eram dois controles de
     espécies diferentes dentro de cartões idênticos, o que lê como defeito, não decisão.
     `width:fit-content` e `text-decoration:none` igualam os dois sem tocar no HTML. */
  .settings-action{
    display:flex; align-items:center; justify-content:center; min-height:44px;
    width:fit-content; max-width:100%; text-decoration:none;
    margin:0 var(--set-pad) 0 var(--set-label); padding:10px 14px;
    border-radius:var(--radius-sm); cursor:pointer;
  }
  .settings-action svg{ margin-right:8px; flex:none; }
  /* o caminho por e-mail continua existindo ao lado do botão de excluir (1.63.0): é a saída
     de quem prefere, e a de quando a função do banco ainda não rodou. Link discreto, alvo
     de toque inteiro. */
  .settings-link-quieto{
    display:flex; align-items:center; min-height:44px; width:fit-content;
    margin:2px var(--set-pad) 0 var(--set-label);
    /* tamanho e entrelinha vêm do papel de APOIO do painel (regra acima): quatro papéis só */
    color:var(--text-dim);
    text-decoration:underline; text-underline-offset:3px;
  }

  /* ---- linha de destino: leva a outro painel, documento ou detalhe ---- */
  .settings-nav-row{
    position:relative; display:flex; align-items:center; gap:var(--set-gap); width:100%;
    min-height:56px; padding:12px var(--set-pad); text-align:left; text-decoration:none;
    background:none; border:0; border-radius:0; box-shadow:none;
    color:var(--text); font-family:inherit; cursor:pointer;
    transition: background-color var(--dur-1) var(--ease-standard);
  }
  .settings-nav-row:active{ background: var(--sheen-1); }
  .settings-nav-copy{ flex:1; min-width:0; }
  .settings-nav-copy b{ display:block; }
  .settings-nav-copy > span{ display:block; margin-top:3px; color:var(--text-soft); }
  .settings-nav-go{ flex:none; color:var(--text-dim); }
  /* ---- SAÍDA PARA FORA DO APP: a última exceção do eixo, removida ----
     Era peça aninhada e vivia DENTRO do recuo do corpo revelado, então o glifo
     e o rótulo nasciam --set-label à direita dos de todas as outras linhas.
     Medido: o rótulo caía em 126 px do .modal-scroll contra 72 px de todas as
     outras — 54 px de desvio, a única quebra da régua no painel.
     Agora ela é LINHA: sai do recuo por margem negativa, herda padding, altura
     e divisória de qualquer linha de destino e cai no mesmo eixo. O que diz
     "isto abre fora do app" continua sendo o GLIFO DE DESTINO — a seta que
     deixa a moldura, e não o chevron —, que é marca, não deslocamento. */
  .settings-nav-row--out{
    /* width:auto, não o 100% herdado da linha base: 100% resolve sobre a caixa de
       CONTEÚDO do corpo revelado, então as margens negativas deslocavam a linha
       sem alargá-la e a divisória parava 68 px antes da borda do card */
    width:auto;
    margin:0 calc(var(--set-pad) * -1) calc(var(--set-pad) * -1) calc(var(--set-label) * -1);
    border-radius:0;
  }
  .settings-nav-row--out::before{
    content:''; position:absolute; top:0; left:var(--set-label); right:0;
    height:1px; background:var(--rule);
  }

  /* ---- detalhe raro: densidade menor, uma linha só até abrir ---- */
  .settings-details{ position:relative; }
  .settings-details summary{
    display:flex; align-items:center; gap:var(--set-gap);
    padding:14px var(--set-pad); list-style:none;
    color:var(--text); cursor:pointer;
  }
  .settings-details summary::-webkit-details-marker{ display:none; }
  /* seta que GIRA = revelar no lugar; o chevron fixo das linhas de destino = ir embora.
     Dois gestos diferentes, duas marcas diferentes. */
  /* 1,5 px, não 2: é a mesma espessura que um traço de 2 no viewBox de 24 pinta
     quando o glifo mede 18 px. A seta era o único traço do painel fora da grade. */
  .settings-details summary::after{
    content:''; width:8px; height:8px; margin-left:auto; flex:none;
    border-right:1.5px solid var(--gold-dim); border-bottom:1.5px solid var(--gold-dim);
    transform:rotate(45deg) translate(-2px,-2px); transition:transform var(--dur-2) var(--ease-standard);
  }
  .settings-details[open] summary::after{ transform:rotate(-135deg); }
  /* O SINAL DE QUE HÁ PARAQUEDAS GUARDADO.
     Esta linha ABRIA sozinha quando existia um retrato de resgate, e o dono relatou o
     efeito colateral: os Ajustes prometem "reabrir é começar do começo", e uma revelação
     já aberta no meio do painel lê como estado velho, não como aviso. Fechada como todas,
     ela ainda precisa dizer que tem algo lá dentro — senão o paraquedas volta a ficar
     costurado e nunca instalado, que foi o achado da auditoria.
     Um pingo ao lado do rótulo resolve os dois: a linha nasce fechada como todas as outras
     e mesmo assim a porta anuncia o conteúdo. E não é SÓ cor: o texto para leitor de tela
     mora dentro dele. */
  .settings-row-dot{
    width:7px; height:7px; flex:none; border-radius:50%;
    background:var(--gold); box-shadow:0 0 0 3px rgba(var(--gold-rgb),.16);
  }
  .settings-row-dot[hidden]{ display:none; }
  /* o corpo revelado entra na coluna de texto: abrir não muda a régua. O recuo
     é do CORPO (prosa e créditos); a linha de destino que mora aqui dentro sai
     dele por margem negativa e volta para o eixo das linhas. */
  .settings-details-body{ padding:2px var(--set-pad) var(--set-pad) var(--set-label); }
  .settings-details-body > p{ margin:0 0 12px; color:var(--text-soft); }
  /* A ZONA DESTRUTIVA É UMA LINHA QUE ABRE. Fechada, ela é uma linha como as outras — o
     mesmo alvo, a mesma seta que gira — e o único desvio é o matiz: rosa no glifo e no
     rótulo, porque a cor aqui não é enfeite, é o aviso de que atrás desta porta as coisas
     não voltam. A caixa continua existindo, agora com uma linha só de altura. */
  .settings-danger{
    margin-top:26px; padding:0; border:1px solid rgba(var(--rose-rgb),.46);
    border-radius:var(--radius-md); background:rgba(var(--rose-rgb),.07);
  }
  .settings-danger h3, .settings-danger h4{ color:var(--on-danger-title); }
  .settings-danger-head .settings-group-ico,
  .settings-details--danger > summary .settings-nav-ico{ color:var(--on-danger-title); }
  .settings-details--danger > summary{ color:var(--on-danger-title); }
  .settings-details--danger > summary::after{ border-color:rgba(var(--rose-rgb),.72); }
  .settings-danger p{
    margin:5px 0 12px; padding:0; color:var(--on-danger-body);
  }
  /* o corpo revelado abre mão do recuo de glifo: aqui dentro não há coluna de ícones a
     respeitar, e o texto lê melhor na largura inteira do bloco */
  .settings-danger-body{ padding:2px var(--set-pad) var(--set-pad); }
  /* dois blocos, uma régua entre eles: apagar o APARELHO e excluir a CONTA são coisas
     diferentes, e a divisória é o que impede quem procura uma de tropeçar na outra */
  .settings-danger-block + .settings-danger-block{
    margin-top:16px; padding-top:16px; border-top:1px solid rgba(var(--rose-rgb),.32);
  }
  .settings-danger-block h4{ margin:0; }
  /* uma régua só dentro do bloco: a margem esquerda do botão existe pra alinhá-lo à coluna
     do RÓTULO, e aqui dentro não há coluna de glifo — o botão passava a flutuar 53px à
     direita do texto que o explica */
  .settings-danger-block .settings-action, .settings-danger-block .settings-link-quieto{ margin-left:0; margin-right:0; }
  .settings-footer{ padding:20px 0 0; }
  .settings-footer .modal-close{ margin:0; min-height:44px; width:100%; }
  .settings-version{ margin:12px 0 0; text-align:center; color:var(--text-soft); }
  /* A VERSÃO NOVA É UM CARTÃO NO ALTO DOS AJUSTES (1.66.2): ícone, o que é, o que acontece, e
     um botão que diz o verbo. Só existe quando uma versão nova já assumiu com o app aberto, e
     só age no toque — o app nunca recarrega sozinho. Aresta e fundo em ouro apagado: é
     novidade, não ação principal (o dourado cheio continua sendo o "Concluir" da tela). */
  .versao-nova{
    display:flex; align-items:center; gap:12px; margin:0 0 16px; padding:12px 12px 12px 14px;
    border-radius:var(--radius-md); border:1px solid var(--edge-gold); background:var(--gold-tint-bg);
  }
  .versao-nova-ico{ flex:none; width:36px; height:36px; border-radius:50%; display:grid; place-items:center;
    background:rgba(var(--gold-rgb), .16); color:var(--gold-ink); }
  .versao-nova-texto{ flex:1; min-width:0; display:flex; flex-direction:column; gap:2px; }
  .versao-nova-texto b{ font-size:14px; font-weight:700; color:var(--text); line-height:1.25; }
  .versao-nova-texto span{ font-size:12.5px; color:var(--text-soft); line-height:1.35; }
  .versao-nova-btn{
    flex:none; min-height:44px; padding:0 16px; border-radius:999px; cursor:pointer;
    border:1px solid var(--gold); background:transparent; color:var(--gold-ink);
    font:inherit; font-size:13.5px; font-weight:800;
  }
  .versao-nova-btn:focus-visible{ outline:2px solid var(--gold); outline-offset:2px; }
  /* Região viva de salvamento. Era o terceiro eixo do ledger, no TOPO do painel; o resto
     do ledger saiu por repetir o que a tela já dizia, e esta frase desceu para o pé, ao
     lado do botão que encerra o painel — que é onde a pergunta "isso ficou salvo?" de fato
     acontece. Salvo não acende cor nenhuma: não há notícia. O erro é o único que sobe de
     cor, no texto E na marca, porque é o único que precisa interromper a varredura. */
  .settings-save{
    display:flex; align-items:center; justify-content:center; gap:8px;
    margin:0 0 14px; color:var(--text-soft);
  }
  .settings-save-ico{ width:18px; height:18px; }
  .settings-save-state{ color:var(--text-soft); }
  .settings-save-state[data-state="error"]{ color:var(--on-error); }
  /* Créditos obrigatórios do TMDB e do JustWatch: última linha do card, na mesma coluna de
     texto das linhas de detalhe. Sem borda própria — a divisória do card já separa. */
  .settings-sources .source-row:first-child{ padding-top:0; }
  .settings-sources .source-row:last-of-type{ padding-bottom:0; }
  .setting-row{
    display:flex; align-items:center; justify-content:space-between;
    padding: 14px 0; border-bottom: 1px solid var(--rule);
    gap: 14px;
  }
  .setting-row:last-of-type{ border-bottom:none; }
  /* o rótulo do sub-bloco de duração deixa a caixa alta: ele é um título de
     grupo pequeno, não uma etiqueta de formulário */
  .duration-sub-label{ display:block; font-size:13px; color:var(--text); letter-spacing:0; font-weight:700; margin-bottom:7px; }
  .duration-sub{ transition: opacity var(--dur-2) var(--ease-standard); }
  .duration-sub.disabled{ pointer-events:none; }
  .duration-sub.disabled .duration-sub-label{ color:var(--text-disabled); }
  .duration-sub.disabled .chip{ color:var(--text-disabled); background:transparent; border-color:var(--line-disabled); border-style:dashed; }
  .duration-sub.disabled .chip.selected{ background:var(--gold-chip-disabled-bg); color:var(--gold-chip-disabled-text); border-color:var(--gold-chip-disabled-bg); border-style:solid; }
  .result-card.unanimous{
    border-color: var(--gold);
    box-shadow: 0 0 0 1px rgba(var(--gold-rgb), .35) inset, 0 0 22px rgba(var(--gold-rgb), .28);
    animation: unanimous-glow 2.4s ease-in-out infinite;
  }
  @keyframes unanimous-glow{
    50%{ box-shadow: 0 0 0 1px rgba(var(--gold-rgb), .5) inset, 0 0 30px rgba(var(--gold-rgb), .45); }
  }
  /* ---- O QUE A SORTE ESCOLHEU, LEMBRADO ----
     No detalhe de uma partida com vários matches, o título sorteado era só mais um cartão
     com um selo pequeno: a decisão da noite tinha o mesmo peso visual das outras opções que
     ficaram para trás. O dono pediu ênfase, e ela vem como HALO — aresta dourada e um
     brilho externo baixo em volta do cartão inteiro.
     Ele NÃO pulsa, e isso é a fronteira: o pulso dourado pertence ao match (`.unanimous`),
     que é o momento alto do app; o sorteio é o desempate, e ganha estado, não clímax — a
     mesma regra que já governa o selo `.drawn-tag` (aresta e tinta, sem preenchimento).
     E por isso a regra sai de cena quando o cartão TAMBÉM é unânime: duas sombras douradas
     na mesma caixa não somam ênfase, viram borrão. */
  .result-card--drawn:not(.unanimous){
    border-color: var(--edge-gold);
    box-shadow: 0 0 0 1px rgba(var(--gold-rgb), .22) inset, 0 0 20px rgba(var(--gold-rgb), .18);
  }
  /* inline-flex pra alinhar o ícone SVG com o texto (antes era um ✨ solto no meio da string) */
  .unanimous-tag{
    display:inline-flex; align-items:center; gap:5px;
    font-size:11px; font-weight:800; color:var(--on-gold);
    background: var(--gold-metal); border-radius:var(--radius-pill);
    box-shadow: inset 0 1px 0 var(--gold-sheen);
    padding:4px 10px; margin-top:6px; letter-spacing:.3px;
  }
  /* "A sorte escolheu" é ESTADO, não clímax: aresta e tinta de ouro, sem preenchimento.
     O ouro cheio pertence ao match — o selo ao lado usa ele —, e emprestá-lo aqui poria os
     dois no mesmo tom de voz. O match é a conquista do grupo; o sorteio é o desempate. */
  .drawn-tag{
    display:inline-flex; align-items:center; gap:5px;
    font-size:11px; font-weight:800; color:var(--gold-ink);
    border:1px solid var(--edge-gold); border-radius:var(--radius-pill);
    background:rgba(var(--gold-rgb), .09);
    padding:3px 9px; margin-top:6px; letter-spacing:.3px;
  }
  .unanimous-tag + .drawn-tag{ margin-left:6px; }
  .setting-copy b{ display:block; font-size:14px; }
  .setting-copy span{ display:block; margin-top:3px; font-size:var(--t-support-size); color:var(--text-soft); line-height:1.45; }
  .switch{
    position:relative; width:50px; height:44px; flex:none;
    background:transparent; border-radius:22px; cursor:pointer;
  }
  .switch::before{
    content:''; position:absolute; top:50%; left:2px; transform:translateY(-50%);
    width:46px; height:27px; border-radius:20px;
    background:var(--surface-3-top); box-shadow: inset 0 1px 2px rgba(0,0,0,.5);
    transition: background var(--dur-2) var(--ease-standard);
  }
  .switch.on::before{ background:var(--gold-metal); box-shadow: inset 0 1px 0 var(--gold-sheen); }
  /* ---- O DISCO É UM OBJETO SÓ, NOS DOIS ESTADOS ----
     Mesmo diâmetro, mesma posição no trilho, mesma aresta. O preenchimento
     inverte porque o CONTRASTE obriga: disco claro sobre ouro dá 1,13:1 e disco
     escuro sobre o trilho apagado dá 1,20:1 — nenhum passa nos 3:1 da WCAG
     1.4.11. A inversão é exigência, não desenho novo. O que dá para escolher é
     QUANTO o claro brilha, e é o que a rodada 6 mudou (ver abaixo).

     ---- RODADA 7: O ASSUNTO ENCERRA AQUI, POR ARITMÉTICA ----
     Terceira observação independente sobre esta peça. A das rodadas 5 e 6 era de
     geometria e foi respondida com medição (o juiz da 6 mediu e deu limpo: trilho
     46x28, disco 21x21 idêntico nos dois estados, erro de centragem de 0,25 px,
     `box-shadow:none`). A da rodada 7 é outra e merece resposta própria: não fala
     de legibilidade, fala de GRAMÁTICA DE COMPONENTE — "o disco deveria ser a
     constante e a trilha a variável; um objeto que troca de cor ao deslizar lê
     como dois objetos".

     O argumento é bom e a resposta não é preferência: é que o CONJUNTO SOLUÇÃO É
     VAZIO. Para um disco de cor CONSTANTE passar nos 3:1 da WCAG 1.4.11 contra os
     dois estados do trilho, a luminância dele teria de satisfazer ao mesmo tempo

        L >= 0,1542   (para vencer o trilho apagado, L 0,0181)
        L <= 0,0773   (para vencer o ouro no ponto mais escuro, L 0,3318)

     e não existe número nos dois intervalos. Os extremos confirmam sem álgebra:
     PRETO ABSOLUTO sobre o trilho apagado dá 1,36:1, e BRANCO ABSOLUTO sobre o
     ouro mais claro dá 1,45:1. Nenhuma cor fixa chega perto — a distância entre
     os dois estados do trilho é grande demais para qualquer preenchimento único.
     Fixar o disco não é uma troca de legibilidade por gramática: é abrir mão dos
     3:1 nos DOIS estados de uma vez.

     O que a inversão entrega hoje, medido nos mesmos termos: 7,27:1 desligado e
     6,74:1 ligado (no ponto pior do gradiente do ouro) — mais que o dobro do
     exigido em cada estado.

     E a identidade do objeto, que é a preocupação legítima do argumento, está
     resolvida por FORMA, não por cor: diâmetro, posição no trilho, ombros de 3 px
     e nitidez de aresta são idênticos nos dois estados. O que desliza é
     reconhecidamente a mesma peça; o que muda é de que lado da luz ela está — que
     é, aliás, o que o Material 3 faz no mesmo componente e pelo mesmo motivo.
     VEREDICTO: fica como está. Contrato em `premium-system-r7.test.mjs`.

     O DISCO CENTRALIZA COMO O TRILHO — top:50% + translateY(-50%) — e não por um
     `top` em px: sem isso as duas peças caem em caminhos de rasterização
     diferentes (o trilho carrega transform e é pintado em float; o disco era
     encaixado na grade de pixel do dispositivo) e, com a linha em y fracionário,
     o disco subia meio pixel. Isso foi a rodada 5, e continua valendo.

     ---- A SOMBRA SAIU: ELA ERA A SEGUNDA ARESTA ----
     A rodada 6 mediu o pixel pintado nos DOIS eixos e nos DOIS estados, a 12×.
     A caixa sempre declarou 3 px de folga em toda a volta, mas o disco carregava
     `--shadow-1`, que é a elevação de CARTÃO: 2 px de borrão mais 18 px de queda.
     Numa folga de 3 px isso não cabe — a tinta saía do disco e caía em cima do
     ouro. Daí os três defeitos que a comparação cega leu como um componente
     errado, todos com a mesma causa:
       · o disco pintado media 22,0 px no eixo horizontal em vez de 21;
       · a folga de ouro à direita caía para 2,56 px contra 2,82 acima, e a de
         baixo para 2,08 px, com a faixa ainda 30% mais escura;
       · a aresta do disco ESCURO borrava numa rampa de 0,54 a 1,48 px enquanto a
         do disco CLARO era nítida em 0,07 — sombra preta some no trilho apagado
         e aparece no ouro. Dois tratamentos de aresta no mesmo componente.
     Sem sombra, a aresta do disco é geometria pura: 0,06 px de rampa nos oito
     lados dos dois estados, 3,00 px de folga nos três ombros e 21,0 px de
     diâmetro. A elevação do interruptor mora no TRILHO, que é quem tem área para
     ela: sombra interna quando apagado, metal dourado quando aceso.

     ---- E O DESLIGADO DEIXOU DE SER O ESTADO MAIS CHAMATIVO ----
     Contraste é RELAÇÃO; peso visual é BRILHO ABSOLUTO. Com `--text` o disco
     desligado pintava a 241 de luminância — mais claro que o pico do ouro (221) e
     que qualquer outra massa sólida do painel. O estado que significa "nada
     acontecendo" era o que puxava o olho primeiro. Com `--text-soft` ele pinta a
     178, abaixo do ouro, e mantém 7,3:1 contra o trilho — mais que o dobro dos
     3:1 exigidos, e ainda longe de `--btn-disabled-fg`, que é outro estado. */
  .switch::after{
    content:''; position:absolute; top:50%; left:5px; width:21px; height:21px;
    transform: translateY(-50%);
    background: var(--text-soft); border-radius:50%;
    /* NENHUMA sombra: qualquer tinta fora do disco mora na folga de 3 px */
    box-shadow: none;
    /* width continua na lista: é o alargamento tátil de .switch:active::after */
    transition: transform var(--dur-2) var(--ease-emphasis),
                background-color var(--dur-2) var(--ease-standard),
                width var(--dur-1) var(--ease-standard);
  }
  .switch.on::after{ transform: translateY(-50%) translateX(19px); background:var(--on-gold); }
  .modal-close{
    margin-top:8px; width:100%; min-height:44px; padding:12px 14px; border-radius:var(--radius-md);
    font-weight:700; cursor:pointer;
  }
  /* painel dos diálogos de confirmação (retomar rodada, apagar partida, sair da conta):
     antes cada um repetia o mesmo bloco de estilo inline */
  .confirm-panel{
    background: linear-gradient(180deg, var(--surface-3-top), var(--surface-3-bot));
    border:1px solid var(--edge-strong); border-top-color:var(--edge-lit);
    border-radius:var(--radius-lg);
    box-shadow: var(--shadow-3), inset 0 1px 0 var(--sheen-3);
    padding:22px; max-width:320px; width:100%; text-align:center;
  }
  /* título e corpo dos diálogos de confirmação: o par se repetia inline nos três */
  .confirm-panel h3{ font-size:14px; font-weight:700; margin-bottom:6px; }
  /* h1 secundário: ranking, histórico e salas usam um degrau abaixo do título da home */
  h1.title--sm{ font-size:28px; }
  /* linhas de ajuste e runner-cards que empilham conteúdo em vez de alinhar em linha */
  .setting-row--stack{ flex-direction:column; align-items:stretch; }
  .runner-card--stack{ flex-direction:column; align-items:stretch; text-align:left; gap:0; }
  .confirm-text{ color:var(--text-dim); font-size:13px; margin-bottom:16px; }
  /* as duas variantes (confirmar / destruir) vêm da gramática de botão da
     fundação; aqui fica só a divisão de espaço */
  .confirm-actions{ display:flex; gap:10px; }
  .confirm-actions .modal-close{ flex:1; margin:0; }
  /* a sala "cada um no seu tempo": o seletor de quantas pessoas mora dentro do diálogo, e a
     espera de quem já votou oferece convite e saída em vez de um relógio */
  .confirm-panel .stepper{ margin:2px 0 18px; }
  .confirm-text--pergunta{ margin-bottom:8px; }
  .livre-espera-acoes{ display:flex; flex-direction:column; gap:10px; margin:22px auto 0; width:100%; max-width:320px; }
  .livre-espera-acoes .cta{ margin-top:0; position:static; }
  /* o pedido de aviso (1.50.0): quando já está ligado ou bloqueado, vira uma frase, não um botão */
  .livre-aviso-nota{ text-align:center; margin:2px 0 0; }

  /* BOTTOM TAB BAR */
  .tabbar{
    position:fixed; left:50%; transform:translateX(-50%); bottom:0;
    width:100%; max-width:420px;
    display:flex; border-top:1px solid var(--line); background: var(--bg);
    padding: 10px 8px calc(10px + env(safe-area-inset-bottom));
    z-index:70;
    box-shadow: 0 -10px 24px rgba(0,0,0,.4);
  }
  .tab-btn{
    flex:1; display:flex; flex-direction:column; align-items:center; justify-content:center; gap:3px;
    background:none; border:none; color:var(--text-dim); font-size:11px; cursor:pointer;
    padding:6px 2px; font-weight:600; letter-spacing:.2px;
    min-height:52px; white-space:nowrap;
  }
  .tab-btn .ico{ font-size:19px; height:19px; line-height:1; display:flex; align-items:center; justify-content:center; }
  .tab-btn .ico svg{ display:block; }
  .tab-btn.active{ color: var(--gold-ink); }

  /* ONBOARDING */
  /* geometria e estados vêm da primitiva .field da fundação; aqui só a margem
     que o formulário de apelido pede entre um campo e o próximo */
  .nickname-input{ margin-bottom:14px; }
  /* a régua de "ou" deixa a caixa alta: ela separa dois caminhos, não rotula um campo */
  .divider{ display:flex; align-items:center; gap:10px; margin:22px 0 16px; color:var(--text-dim); font-size:var(--t-support-size); letter-spacing:.2px; }
  .divider::before, .divider::after{ content:''; flex:1; height:1px; background: var(--rule-strong); }
  .social-row{ display:flex; gap:10px; }
  .social-btn{
    flex:1; display:flex; align-items:center; justify-content:center; gap:8px;
    padding:13px; border-radius:var(--radius-md);
    border:1px solid var(--edge); border-top-color:var(--edge-lit);
    background: linear-gradient(180deg, var(--surface-2-top), var(--surface-2-bot));
    box-shadow: var(--shadow-1), inset 0 1px 0 var(--sheen-2);
    color:var(--text); font-size:14px; font-weight:600; cursor:pointer;
  }
  .social-btn .sico{ font-weight:800; font-size:15px; color:var(--gold-ink); }
  .social-btn .soon-tag{
    font-size:11px; font-weight:700; letter-spacing:.2px;
    color:var(--gold-dim); border:1px solid var(--edge); padding:2px 6px; border-radius:var(--radius-pill);
  }
  .micro-note{ font-size:13px; color:var(--text-dim); text-align:center; margin-top:14px; line-height:1.55; }

  /* ===== TUTORIAL =====
     A primeira tela do app, e a última do ciclo a entrar no sistema. Três regras:
     (1) UM objeto levantado — a moldura com o print do app; título, texto, pontos e
         botão recuam para o papel deles;
     (2) o ouro fica no ponto ativo e no CTA (escolha e ação), nunca no título;
     (3) nenhum px de tipografia solto: os papéis da fundação fazem o ritmo. */

  /* sem barra de abas na primeira tela, os 130px que `.screen` reserva por baixo eram
     dobra jogada fora — justamente a altura que a moldura precisa para ser objeto */
  /* `min-height:0` é o que faz a corrente de encolhimento chegar até a moldura: sem ele
     `.screen` fica com `min-height:auto`, a tela cresce além da dobra e o palco nunca
     recebe a ordem de recuar. Foi assim que, reaberta pelos Ajustes (com barra de cima E
     de baixo na tela), a ação decisiva foi parar por baixo da navegação. */
  /* `--tut-h` é a ALTURA DE REFERÊNCIA do palco de quadros, e é o único número que muda
     de viewport para viewport: a largura de cada arranjo sai dela por multiplicação
     (abaixo). Antes esse teto estava escrito dentro de `.tut-frame` e cada dobra o
     reescrevia — com dois quadros por página, três cópias do mesmo número em três
     media queries seria a receita de um arranjo estourar em silêncio. */
  /* 64vh/520 e não 58vh/470: com "Pular" fora do topo o palco herdou os 58px daquela
     fileira, e um teto que não cresce junto os devolve como ar em volta do card. O
     `max-height:100%` de cada arranjo continua sendo o freio — este número é o TETO, não a
     altura, e é o freio que decide nas telas curtas. */
  #tutorial{ --tut-h: min(64vh, 520px); min-height:0; padding-bottom: calc(20px + env(safe-area-inset-bottom)); }
  /* nada de reserva para a barra de abas: reaberto por "Rever como funciona", o tutorial
     ESCONDE as duas barras (ver `mostrarCromoDoApp`), e a tela é a mesma dos dois lados */
  .tut-shell{
    flex:1; min-height:0; width:100%; max-width:420px; margin-inline:auto;
    display:flex; flex-direction:column;
  }
  /* AS DUAS SAÍDAS DIVIDEM UMA LINHA SÓ, e é isso que faz "Pular" não custar altura
     nenhuma: ele mora na fileira que o botão dourado já ocupava. No topo ele era uma
     FILEIRA inteira — 44px de pastilha mais 14 de respiro —, e os 58px saíam do palco, que
     é a única coisa desta tela que a pessoa precisa ver. Aqui os dois ficam juntos no
     alcance do polegar, com a hierarquia dita pelo material: ouro cheio a ação, fantasma a
     desistência. O ouro leva o que sobra (`flex:1`), então ele continua sendo a peça
     larga da tela. */
  .tut-foot-row{ width:100%; display:flex; align-items:stretch; gap:10px; }
  /* `.back-link` nasce para o canto de uma tela: ela traz `align-self:flex-start` e um
     `margin-bottom` de 14px, e os dois juntos faziam "Pular" pousar 4px acima de
     "Próximo" e terminar 10px antes dele — dois botões lado a lado sem base comum, que é
     o que o dono viu. Aqui os dois são um PAR: mesma altura por esticamento, mesma base. */
  .tut-foot-row .tut-skip{ flex:0 0 auto; align-self:stretch; margin-bottom:0; }
  .tut-stage{
    flex:1; min-height:0;
    display:flex; flex-direction:column; align-items:center; justify-content:center;
    /* 16 e não 22: com a fala partida em duas, o respiro é cobrado DUAS vezes, e cada
       pixel dele sai do card — que é a peça que o dono pediu maior. */
    gap:16px; text-align:center;
    transition: transform var(--dur-2) var(--ease-exit), opacity var(--dur-2) var(--ease-exit);
  }

  /* O PALCO DE QUADROS. Cada página mostra um PAR, e os dois arranjos têm contas
     diferentes de largura — mas a mesma origem (`--tut-h`), para que nenhuma dobra
     precise decidir duas vezes:

       cards   → dois recortes 450/852 lado a lado: largura = 2 × (altura × 450/852) + calha
       screens → duas telas 9/20 sobrepostas: a de trás manda, a da frente entra por cima

     As proporções não são escolha de desenho: são as dos ARQUIVOS, e o contrato da suíte
     compara as três pontas (o `aspect-ratio` daqui, o `width`/`height` do `<img>` e os
     bytes do WebP). Divergir em qualquer uma volta a produzir salto de layout.

     Largura é o que dirige o tamanho (a proporção devolve a altura), e o teto vem da
     altura: `min(100%, altura × fator)`. Com o oposto — altura fixa e largura derivada —
     o par estourava a lateral no celular estreito antes de qualquer `max-height` agir. */
  .tut-duo{
    flex:0 1 auto; min-height:0;
    display:flex; align-items:center; justify-content:center;
  }
  /* PÁGINA 1 — O GESTO, DEMONSTRADO NO CARD DE VERDADE.
     Eram dois prints congelados de votos JÁ CONCLUÍDOS: mostravam a consequência e nunca o
     movimento. A primeira tentativa de conserto trocou os prints por uma MINIATURA PRÓPRIA
     — pôster, título e nada mais — e o dono foi direto ao ponto: aquilo não parece o app.
     Agora o palco monta o CARD REAL, com as classes reais (`.card`, `.poster`,
     `.card-body`, `.stamp`): mesma moldura, mesmo título em Bebas, mesma linha dourada de
     meta, mesmo "Ver detalhes", mesma sinopse cortada pelo mesmo `@container`. Se o card da
     rodada mudar, este muda junto — é o mesmo CSS, não uma imitação que envelhece sozinha.
     O que muda é só o que precisa: ele é inerte, não escuta ponteiro, e a peça inteira fica
     fora da árvore acessível. */
  .tut-swipe-demo{
    position:relative; flex:0 1 auto; min-height:0;
    /* LARGURA QUASE REAL, de propósito. O card da rodada tem teto de 340px, e todo o
       miolo dele é dimensionado para isso: o carimbo tem 32px de tipo, o título 30px, o
       "Ver detalhes" 12px. Num palco estreito nada disso encolhe junto — medido a 247px,
       o carimbo cobria um terço do pôster. Mantendo a largura do baralho, as proporções
       internas são as da rodada sem precisar reescrever uma linha delas. */
    /* O PALCO é só a MOLDURA: altura da dobra, retrato preservado por `aspect-ratio`
       (sem ele, o `max-height` encolhia a altura e deixava a largura — em 320x568 saía um
       card mais largo que alto, que o baralho nunca produz). Quem desenha por dentro é
       `.tut-demo-escala`, em tamanho de baralho. */
    height:var(--tut-h); max-height:100%; aspect-ratio:62 / 100;
    width:auto; max-width:min(86vw, 340px);
  }
  /* O CARD EM TAMANHO DE BARALHO, REDUZIDO POR ESCALA.
     O card da rodada mede por dentro em px absolutos — título 30, carimbo 32, botão 12 —
     porque lá ele sempre tem os 340px de teto. Num palco de tutorial isso estoura: medido
     em 320x568, o card resolvia 139px de largura e o carimbo cobria a peça inteira.
     Encolher cada peça por dentro seria reescrever o card e inventar proporções que a
     rodada não tem. Aqui ele nasce em 340x548 — o baralho de verdade — e o conjunto
     inteiro é reduzido por `transform`, que preserva TODAS as proporções de uma vez.
     `--tut-escala` vem do JS: é a única medida que o CSS não sabe fazer sozinho, porque
     dividir comprimento por comprimento não existe em `calc()`. */
  .tut-demo-escala{
    position:absolute; top:0; left:0; width:340px; height:548px;
    transform:scale(var(--tut-escala, 1)); transform-origin:0 0;
    container-type:size; container-name:deck;
  }
  /* `z-index:1` não é enfeite: é o que põe o card ENTRE as duas metades da mão — a mão
     fixa em 0, atrás; o polegar em 4, na frente. Sem ele as três peças cairiam na ordem
     do documento e a mão que segura o celular apareceria por cima do pôster. */
  .tut-demo-move{
    position:absolute; inset:0; z-index:1;
    animation:tut-card-tease var(--tut-loop-ms, 3400ms) var(--ease-emphasis) infinite;
    transform-origin:50% 88%; will-change:transform;
  }
  /* o card do palco não é agarrável nem clicável: o gesto que ele ensina acontece na
     rodada, não aqui. `cursor` e `pointer-events` são a única divergência do card real. */
  /* `min-height:50vh` é rede de segurança do BARALHO: lá o container pode colapsar e o
     card sumiria. Aqui o palco tem altura definida, então a rede vira o problema — medido
     em 320x568, ela empurrava o card 60px para fora do palco, por cima da fala. */
  /* `height:100%` é obrigatório, não redundante: a dobra deitada do BARALHO limita o card
     a 220px, e essa conta não vale aqui — o palco vive numa caixa fixa de 340x548 que é
     reduzida por escala, então a altura da viewport não diz nada sobre ele. Sem isto,
     medido em 683x384: card de 125x81, mais largo que alto. */
  .tut-swipe-demo .card{ cursor:default; min-height:0; height:100%; }
  .tut-swipe-demo .card-details{ pointer-events:none; }
  /* O QUE O CARD MOSTRA SEM CARTAZ: a mesma marca-d'água que a rodada desenha quando o
     TMDB não tem arte. No card real ela vem por estilo inline; aqui é classe, porque a
     suíte proíbe estilo inline no render — e some assim que o cartaz chega. */
  .tut-demo-sem-cartaz{
    position:absolute; top:40%; left:50%; transform:translate(-50%,-50%);
    z-index:0; opacity:.13; color:var(--text);
  }
  .poster.tem-cartaz .tut-demo-sem-cartaz{ display:none; }
  /* os dois botões do palco são DEMONSTRAÇÃO: nem o de guardar nem o de detalhes atendem
     toque, e a peça inteira já está fora da árvore acessível */
  .tut-swipe-demo .card-save{ pointer-events:none; }
  /* A MÃO, EM DUAS PEÇAS SOBREPOSTAS. Mesma caixa e mesmo viewBox (151x212): é isso que
     faz o polegar nascer encaixado no punho, sem nenhuma coordenada combinada à mão.
     ELA SEGURA O CELULAR, então entra pelo canto de baixo à direita e SANGRA para fora do
     card — punho e aparelho ficam além da borda, como numa foto de mão com telefone.
     Os % são do card de 340x548: dentro de `.tut-demo-escala`, a redução do palco leva as
     duas peças junto sem que nenhuma precise saber o tamanho da tela.
     O SANGRAMENTO DA DIREITA é o único número aqui que encosta num limite: `.phone` recorta
     (`overflow:hidden`, sem barra), e a lasca do aparelho já nasce colada na borda direita
     do viewBox — não existe folga dentro do desenho para absorver o corte.
     -22% E NÃO -16%: em -16% o polegar pousava sobre a sinopse e sobre "Ver detalhes", e o
     dono pediu a mão mais para a direita. Cada ponto percentual aqui é 3,4px do card de
     340, e a folga que existe para gastar é a calha entre o palco e a borda da tela — o
     palco tem a proporção do card (62/100) e sobra largura nas duas laterais em qualquer
     dobra. Medido depois da mudança: a 320, 375 e 390px nada do desenho é serrado. */
  .tut-hand-fixed, .tut-hand-thumb{
    position:absolute; right:-14%; bottom:-10%; width:42%; height:auto;
    /* decoração: o gesto que ela ensina acontece na rodada, e a caixa do <img> é um
       retângulo cheio — sem isto ela ficaria por cima do card inteiro no teste de toque */
    pointer-events:none;
    /* o desenho é PRETO (#050505) e o app é quase preto: na parte que sangra para fora do
       card ele sumiria sem o halo branco de 1px. A segunda sombra é a que descola a mão
       do cartaz quando ela passa por cima. */
    filter:drop-shadow(0 0 1px rgba(255,255,255,.96)) drop-shadow(0 2px 4px rgba(0,0,0,.72));
  }
  /* A MÃO FIXA FICA ATRÁS DO CARD, e é o ponto inteiro da peça: quem segura o celular está
     ATRÁS dele, nunca por cima do pôster nem da escrita. Fora da borda ela aparece
     inteira; onde cruza o card, o card ganha. Sem `animation` — é a peça parada do par. */
  .tut-hand-fixed{ z-index:0; }
  .tut-hand-thumb{
    z-index:4;
    animation:tut-thumb-tease var(--tut-loop-ms, 3400ms) var(--ease-emphasis) infinite;
    transform-origin:50% 84%; will-change:transform;
  }
  /* a alternativa de movimento reduzido: um card imóvel não ensina gesto nenhum, então os
     dois sentidos passam a ser ditos por escrito, dentro da própria peça.
     `rgba(var(--bg-rgb), …)` e não `color-mix`: aqui `--bg-rgb` é o TRIPLETE `10,10,16`,
     e não uma cor — trocar a função deixaria o painel sem fundo, calado. */
  .tut-reduced-decisions{
    display:none; position:absolute; z-index:5; top:18px; left:12px; right:12px;
    margin:0; padding:8px 10px; border:1px solid var(--edge-lit);
    border-radius:var(--radius-sm); background:rgba(var(--bg-rgb), .88);
    color:var(--text); font-size:var(--t-support-size); line-height:var(--t-support-lh);
    text-align:center;
  }
  /* 3,4s: centro → esquerda → centro → direita → centro. A pausa no centro é o que impede
     o laço de virar vaivém nervoso no canto do olho. */
  @keyframes tut-card-tease{
    0%, 12%, 48%, 60%, 96%, 100%{ transform:translateX(0) rotate(0); }
    26%{ transform:translateX(-30px) rotate(-4deg); }
    74%{ transform:translateX(30px) rotate(4deg); }
  }
  /* O POLEGAR TEM LAÇO PRÓPRIO, e é DE PROPÓSITO: ele SAI ANTES do card (24% contra 26%),
     e é essa defasagem — não a distância — que faz o dedo empurrar em vez de acompanhar.
     O CURSO É CURTO. Uma versão anterior mandava o polegar 38px e 9°, mais que o próprio
     card, e naquela composição a mão inteira andava junto. Agora o punho fica PARADO atrás
     do card: um polegar que percorre 38px se arranca da própria junta a cada laço e a peça
     deixa de parecer uma mão. 10px e 2,5° é o quanto um polegar de verdade anda sem soltar
     do punho — quem faz a distância é o card, que responde ao empurrão. */
  @keyframes tut-thumb-tease{
    0%, 10%, 46%, 60%, 94%, 100%{ transform:translate(0,0) rotate(0); }
    24%{ transform:translate(-10px, 1px) rotate(-2.5deg); }
    72%{ transform:translate(10px, 1px) rotate(2.5deg); }
  }
  /* cada carimbo só existe durante o deslocamento que o produz: `PASSO` na ida à esquerda,
     `CURTI` na ida à direita. Os dois no mesmo quadro seriam mentira sobre o gesto. */
  @keyframes tut-stamp-nope{
    0%, 12%{ opacity:0; }
    26%{ opacity:.9; }
    40%{ opacity:.28; }
    48%, 60%, 74%, 96%, 100%{ opacity:0; }
  }
  @keyframes tut-stamp-like{
    0%, 12%, 26%, 48%, 60%{ opacity:0; }
    74%{ opacity:.9; }
    88%{ opacity:.28; }
    96%, 100%{ opacity:0; }
  }
  .tut-swipe-demo .stamp.like{
    animation:tut-stamp-like var(--tut-loop-ms, 3400ms) var(--ease-emphasis) infinite;
  }
  .tut-swipe-demo .stamp.nope{
    animation:tut-stamp-nope var(--tut-loop-ms, 3400ms) var(--ease-emphasis) infinite;
  }
  /* PÁGINA 2 — O CONTROLE EM CIMA, AS DUAS TELAS EMBAIXO.
     Eram só as duas fotos empilhadas na mesa: mostravam que existem dois modos e nunca
     DE ONDE sai a escolha. Agora o palco monta o TRILHO REAL da tela inicial — as mesmas
     classes (`.seg-track`, `.mode-btn`, `.selected`) e os mesmos glifos (`ICONS.phone` /
     `ICONS.phones`) —, e um relógio alterna os dois modos: o botão acende, a tela dele vem
     à frente um tico maior e nítida, e a outra recua desfocada.
     A sobreposição continua sendo do ARRANJO, não uma afirmação sobre o app: as duas telas
     existem, só nunca ao mesmo tempo — e é exatamente isso que a troca encena. */
  /* A CONTA DESTA PÁGINA É AO CONTRÁRIO DA ANTIGA, e precisou ser.
     Antes o par de telas tirava a LARGURA de `--tut-h` por multiplicação e a altura vinha
     sozinha, da proporção dos arquivos. Com uma linha de botões em cima isso quebra: a
     altura do par não sabe que perdeu espaço, e o multiplicador que serve numa dobra estoura
     na outra — medido em 320x568, o par passava 137px por baixo do palco, em cima da fala,
     com a mesma regra que cabia em 375x812. Pior: o rótulo "Celulares diferentes" quebra em
     duas linhas nas telas estreitas, então a própria linha de botões muda de altura.
     Agora o palco tem a altura de referência, o trilho leva o que precisa e O QUE SOBRA é a
     ALTURA do par — a largura dele é que sai daí, pela proporção do arranjo. Nenhum número
     por dobra: a conta se corrige sozinha em qualquer sobra. */
  .tut-screens-demo{
    flex:0 1 auto; min-height:0;
    height:var(--tut-h); max-height:100%; width:100%;
    display:flex; flex-direction:column; align-items:center; gap:12px;
  }
  /* o trilho é DEMONSTRAÇÃO: acende sozinho e não atende toque. A peça inteira está fora
     da árvore acessível, e é por isso que os botões são <span> e não <button> — elemento
     focável dentro de `aria-hidden` é armadilha de teclado: o Tab pousaria num controle
     que ninguém vê. */
  .tut-modes{ flex:0 0 auto; width:100%; pointer-events:none; }
  .tut-modes .mode-btn{ cursor:default; }
  /* O PAR NASCIA TORTO: "Celulares diferentes" quebra em duas linhas e "Mesmo celular" cabe
     em uma, então a descrição de cada modo caía numa altura diferente e o trilho lia como
     desalinhado. Reservar duas linhas no rótulo põe as duas descrições na mesma base — a
     mesma correção que o pódio do Ranking precisou, pelo mesmo motivo. */
  /* 2,8em e não 2×1,25em: o ícone de 17px ao lado do texto alarga a caixa de linha, e com
     a reserva menor o rótulo de duas linhas passava dela e a descrição descia 3px */
  .tut-modes .mode-btn b{ min-height:2.8em; }
  /* O CLIQUE. O botão que acabou de receber `selected` afunda e volta; a animação
     reinicia sozinha porque a classe é REPOSTA a cada volta do laço. */
  .tut-modes .mode-btn.selected{ animation:tut-mode-press 420ms var(--ease-emphasis); }
  @keyframes tut-mode-press{ from{ transform:scale(.965); } to{ transform:scale(1); } }

  /* OS QUADROS DEIXARAM DE SER TELAS INTEIRAS.
     Eram prints de 9/20 — o celular todo — e o dono usou o app publicado e disse o óbvio:
     dentro de uma moldura de ~150px de largura, uma tela inteira de 540px de conteúdo é um
     borrão. Um print de tela cheia só se lê em tela cheia. Agora cada arquivo é um RECORTE
     do assunto da página (a região "Com quem?", o código da sala), em 3/5, gerado por
     `docs/tools/tutorial-shots.mjs`. Mesmo pixel de app; o que muda é quanto dele cabe.
     A conta de legibilidade: antes o quadro media 150px na tela para 540px de conteúdo
     (escala 0,28); agora mede ~200 para 351 (escala 0,57) — o dobro, e é isso que o dono
     pediu ao circular em vermelho o pedaço que interessava.

     A PROPORÇÃO DO ARRANJO continua DERIVADA, não escolhida: cada tela ocupa 64% da
     largura em 3/5 (0,64 × 5/3 = 1,067 de altura), a da direita desce 8% da própria altura
     (~0,085 da largura do arranjo) e o giro pede mais ~3% de folga (girar um retângulo
     alarga E aumenta a caixa que ele ocupa): 1,067 + 0,085 + 0,03 = 1,182. Com 28% de
     sobreposição, 0,64 + 0,64 − 0,28 = 1,00 de largura exata. Mexer em qualquer um desses
     números exige refazer a divisão.
     3/5 E NÃO 3/4: o dono disse que os prints estavam "cortando algumas coisas", e
     estavam — o lobby da sala não cabia em 468px de recorte e "Iniciar rodada" saía
     fatiado ao meio. O quadro ficou 25% mais alto SEM ficar mais estreito, então a escala
     de leitura não mudou (continuam ~200px na tela para 351px de conteúdo); o que mudou é
     quanto cabe. E a altura estava sobrando: o arranjo é limitado pela LARGURA (teto de
     94% por causa do giro) e desperdiçava 143px de altura em 375×812.
     64% e não 70%: com 70% a sobreposição vai a 40% e a tela de trás perde quase toda a
     área visível — o par deixa de ler como DUAS telas e vira uma pilha embaralhada, que é
     o oposto do que a página diz.
     AS DUAS TÊM O MESMO TAMANHO por decisão do dono: a da direita já foi 46%, e o par
     nascia torto antes mesmo de o laço começar. Agora a única diferença de tamanho na tela
     é a que o laço produz — o que o par mostra parado é o empate. */
  /* `max-width:94%` e não 100%: os quadros são girados, e girar um retângulo alarga a
     caixa que ele ocupa. Com o teto em 100% o canto saía pela borda da tela, cortado a faca. */
  .tut-duo--screens{
    flex:1 1 auto; min-height:0;
    height:100%; width:auto; max-width:94%; aspect-ratio:1 / 1.182;
  }
  .tut-duo--screens .tut-frame{ aspect-ratio:3/5; }
  /* A DESCRIÇÃO DE CADA MODO É A PRIMEIRA COISA A SAIR quando a altura aperta, e sai cedo:
     ela custa 42px de linha de botões, e cada pixel ali vem direto do tamanho das telas
     embaixo. Medido em 320x568, com a descrição o par caía para 107px de largura; sem ela
     volta a 145. O rótulo sozinho já separa os dois modos, e o parágrafo logo abaixo diz o
     mesmo com mais palavras — é a mesma economia que a tela inicial faz na dobra dela. */
  @media (max-height:720px){
    .tut-modes .mode-btn small{ display:none; }
  }
  /* o giro do arranjo virou variável porque agora ele entra numa transformação COMPOSTA
     (giro + plano): escrito duas vezes, o ângulo sairia de sincronia calado */
  .tut-duo--screens .tut-frame:first-child{ --giro:-3deg; flex:0 0 64%; }
  .tut-duo--screens .tut-frame:last-child{
    --giro:4.5deg; --desce:8%; flex:0 0 64%; margin-left:-28%;
  }
  /* O PLANO DE FUNDO É O PADRÃO: quem não foi escolhido recua. Assim a regra de frente
     precisa dizer só quem está na frente, e não os dois lados de cada troca. */
  /* O PLANO DE FUNDO É O PADRÃO: quem não foi escolhido recua. Assim a regra de frente
     precisa dizer só quem está na frente, e não os dois lados de cada troca.
     A ESCOLHIDA FICA NO TAMANHO NATURAL (1) e a outra ENCOLHE — e não o contrário. A
     diferença que se lê é a mesma, mas crescer além de 1 faria a tela da frente sangrar
     para fora da caixa que o palco reservou, justo por cima da fala logo abaixo. */
  /* O DESLOCAMENTO DO LEQUE ENTROU NA TRANSFORMAÇÃO, e não é enfeite: enquanto ele era
     `margin-top`, o arranjo precisava alinhar os quadros pelo TOPO da caixa — e a caixa é
     dimensionada pela altura do palco, que sobra quando a largura é o limite. O resultado
     medido em 375×812: quadros terminando em y=415 dentro de uma caixa que ia até 516, ou
     seja 100px de vão colado embaixo do leque. Como `translateY` desce a peça sem mexer no
     alinhamento, os três voltam a ser centralizados e o ar fica dividido em cima e embaixo.
     Atenção à unidade: % de `translateY` é da ALTURA do próprio quadro, enquanto % de
     `margin-top` era da LARGURA do arranjo — os números não se copiam, se convertem. */
  .tut-screens-demo .tut-frame{
    z-index:0; opacity:.72; filter:blur(1.6px);
    transform: translateY(var(--desce, 0)) rotate(var(--giro, 0deg)) scale(var(--plano, .9));
    transition: transform var(--dur-3) var(--ease-emphasis),
                filter var(--dur-3) var(--ease-emphasis),
                opacity var(--dur-3) var(--ease-emphasis);
  }
  .tut-screens-demo.is-same .tut-frame[data-device="same"],
  .tut-screens-demo.is-remote .tut-frame[data-device="remote"],
  .tut-screens-demo.is-sessions .tut-frame[data-device="sessions"],
  .tut-screens-demo.is-lists .tut-frame[data-device="lists"],
  .tut-screens-demo.is-taste .tut-frame[data-device="taste"]{
    --plano:1; z-index:2; opacity:1; filter:none;
  }

  /* ---- PÁGINA 3 — A CINEMATECA, TRÊS TELAS EM LEQUE ----
     O trilho aqui é o das ABAS, com as classes reais da tela (`cine-tabs` / `cine-tab`),
     porque é esse o controle que a pessoa vai encontrar. Mesma regra do trilho de modos:
     peça decorativa, sem alvo de toque.
     A PROPORÇÃO DO ARRANJO é derivada, como a da página 2, e a conta é esta: três recortes
     3/4 ocupando 56% da largura (0,56 × 4/3 = 0,747 de altura) com 34% de sobreposição
     entre eles — 3 × 0,56 − 2 × 0,34 = 1,00 de largura exata — as laterais descendo 5% da
     própria altura (~0,047 da largura) e o giro pedindo ~3% de folga
     (0,56 × 5/3 = 0,933; 0,933 + 0,047 + 0,03 = 1,010). Mexer em qualquer um desses
     números exige refazer a divisão.
     56% e não mais: com 60% a sobreposição sobe para 40% e as duas telas de trás somem
     quase por inteiro atrás da da frente — o leque deixa de ler como três telas, que é o
     que a página existe para dizer.
     Os quadros são RECORTES desde a 1.31: o leque de telas inteiras cabia em 42% cada e
     virava borrão — ver a nota da página 2, que pagou a mesma conta.
     O teto de 94% é o mesmo da página 2, e pelo mesmo motivo: as laterais são giradas. */
  .tut-tabs{ flex:0 0 auto; width:100%; pointer-events:none; }
  .tut-tabs .cine-tab{ cursor:default; }
  .tut-tabs .cine-tab[aria-selected="true"]{ animation:tut-mode-press 420ms var(--ease-emphasis); }
  .tut-duo--cinemateca{
    flex:1 1 auto; min-height:0;
    height:100%; width:auto; max-width:94%; aspect-ratio:1 / 1.010;
  }
  .tut-duo--cinemateca .tut-frame{ aspect-ratio:3/5; flex:0 0 56%; }
  /* as duas laterais recuam e descem; a do meio fica no eixo. O giro é o mesmo recurso da
     página 2 (variável, porque entra numa transformação composta com o plano). */
  .tut-duo--cinemateca .tut-frame:first-child{ --giro:-4deg; --desce:7%; }
  .tut-duo--cinemateca .tut-frame:nth-child(2){ --giro:0deg; margin-left:-34%; z-index:1; }
  .tut-duo--cinemateca .tut-frame:last-child{ --giro:4deg; --desce:7%; margin-left:-34%; }

  /* A MOLDURA. `min-height:0` deixa a peça encolher em vez de empurrar o texto para fora
     em telas baixas; a proporção vem do arranjo, porque os dois formatos de quadro
     (recorte de card e tela inteira) convivem na mesma tela. */
  .tut-frame{
    position:relative; min-height:0;
    margin:0; border-radius:var(--radius-lg); overflow:hidden;
    background: linear-gradient(180deg, var(--surface-3-top), var(--surface-3-bot));
    border:1px solid var(--edge); border-top-color:var(--edge-lit);
    box-shadow: var(--shadow-3), inset 0 1px 0 var(--sheen-3);
  }
  /* o que sobra quando o print NÃO chega: as imagens ficam fora do SHELL do service
     worker de propósito, então a primeira abertura offline não tem os arquivos. O cartaz
     tipográfico mora SOB a imagem — some coberto quando ela existe, reaparece quando não */
  .tut-frame-fallback{
    position:absolute; inset:0; z-index:0;
    display:flex; flex-direction:column; align-items:center; justify-content:center;
    gap:12px; padding:18px; text-align:center; color:var(--gold-dim);
  }
  .tut-frame-fallback b{
    color:var(--text-dim); font-weight:600;
    font-size:var(--t-support-size); line-height:var(--t-support-lh);
  }
  .tut-shot{
    position:relative; z-index:1;
    display:block; width:100%; height:100%; object-fit:cover; object-position:top center;
  }
  /* um <img> que falha ainda desenha o ícone de imagem quebrada no canto, mesmo com
     `alt=""`. Falhar aqui é caso REAL (a imagem está fora do SHELL do service worker):
     a moldura apaga a imagem e devolve o quadro inteiro ao cartaz tipográfico */
  .tut-frame.is-missing .tut-shot{ display:none; }
  /* `position:relative; z-index:1` é o "atrás das escritas" da mão: o palco é uma camada
     POSICIONADA e a mão sangra para fora dele, então sem esta linha a lasca do aparelho
     passaria por cima do título. A fala fica acima de tudo que é palco. */
  .tut-copy{ position:relative; z-index:1; display:flex; flex-direction:column; gap:8px; }
  /* o bloco de cima é só o título, e ele não disputa altura com o palco: `flex:0 0 auto`
     mais o `gap` do palco já dão o respiro. O `.tut-stage` centraliza o conjunto, então
     sobra ar dividido em cima e embaixo — nunca uma faixa colada numa das pontas. */
  .tut-copy--topo{ flex:0 0 auto; }
  /* A FALA DE BAIXO POUSA NUM HALO, e o halo é o fundo da tela. A mão sangra para fora do
     palco e passa POR TRÁS da fala (a fala é camada 1, a mão vive dentro do palco); só que
     passar por trás não basta — sobre o traço preto, que ainda carrega um halo branco de
     1px para existir num app quase preto, uma linha de 12,5px fica difícil de ler. Foi o
     que o dono viu no celular dele, que é mais estreito que o meu.
     RAMPA E NÃO CAIXA: uma caixa com fundo seria uma peça nova no meio da tela, com aresta
     e tudo; a rampa só APAGA o que está atrás e some antes de encostar em qualquer coisa.
     E ELA SOME PELOS QUATRO LADOS. A primeira versão era só vertical: sumia em cima e
     embaixo, e nas LATERAIS terminava a faca, na borda da caixa de texto — a mão entrava
     na rampa e reaparecia inteira 20px depois, com um corte reto no meio do traço. O dono
     viu na hora.
     A DIVISÃO DE TRABALHO É O QUE FAZ ISSO CABER EM DUAS LINHAS: o degradê do FUNDO
     resolve cima e baixo, e uma MÁSCARA resolve as laterais. Uma máscara só — compor duas
     exigiria `mask-composite`, que não é universal, e sem ele elas se SOMAM (união), o que
     apagaria justamente as bordas que a rampa existe para suavizar. Assim, no navegador
     que não conhecer máscara nenhuma, o que sobra é exatamente a versão anterior.
     As margens negativas dão à rampa a área para desaparecer sem comer a largura do texto:
     a caixa cresce para FORA, e o que a fala tem para escrever continua igual. E os
     respiros são do TAMANHO das rampas — menores, a primeira linha pousaria dentro do
     degradê. A cor sai de `--bg`, então ela clareia no tema claro e escurece no escuro. */
  .tut-copy--pe{
    padding:22px 30px 14px;
    margin:-22px -30px -14px;
    background: linear-gradient(180deg,
      rgba(var(--bg-rgb), 0) 0, var(--bg) 22px,
      var(--bg) calc(100% - 14px), rgba(var(--bg-rgb), 0) 100%);
    -webkit-mask-image: linear-gradient(90deg,
      transparent 0, #000 30px, #000 calc(100% - 30px), transparent 100%);
    mask-image: linear-gradient(90deg,
      transparent 0, #000 30px, #000 calc(100% - 30px), transparent 100%);
  }
  .tut-title{ color:var(--text); margin:0; text-wrap:balance; }
  .tut-text{ color:var(--text-dim); margin:0 auto; max-width:34ch; }
  /* A CONSEQUÊNCIA do gesto. Nasceu como rodapé quase apagado — tipo de apoio, cor mais
     baixa da tela — porque uma linha que ANUNCIA o clímax não pode competir com o momento
     em que ele acontece. O dono pediu mais destaque, e a subida é medida, não solta: sobe
     ao corpo do parágrafo, ganha a cor de apoio no lugar da apagada e um pouco mais de ar
     acima da régua. O que NÃO muda é a fronteira: continua sem ouro, sem caixa alta e sem
     página própria — o realce é de leitura, não uma segunda celebração.
     `balance` e não o padrão: sem ele a frase quebrava com "match." sozinho na segunda
     linha — a palavra mais carregada da tela virando órfã tipográfica. */
  .tut-note{
    color:var(--text-soft); margin:8px auto 0; max-width:34ch; text-wrap:balance;
    padding-top:12px; border-top:1px solid var(--rule);
  }
  /* o termo em relevo é a palavra que a frase existe para dizer. Peso e a cor CHEIA de
     texto: é o degrau que sobra depois que o ouro está descartado. */
  .tut-note-term{ color:var(--text); font-weight:700; }

  .tut-foot{
    flex:0 0 auto; width:100%; max-width:420px; margin-inline:auto;
    display:flex; flex-direction:column; align-items:center; gap:12px; padding-top:12px;
  }
  .tut-next{ flex:1; min-width:0; }
  /* O anel do CTA vem de `--focus-ring-on-gold` (#0d1017) — cor escolhida para ser lida
     SOBRE o ouro. Com o deslocamento externo do sistema (+2px) ele pousa no fundo quase
     preto da tela e desaparece: medido em 360x800, anel e fundo ficam a menos de 1:1.
     Aqui ele entra na peça, onde a cor foi feita para viver.
     ACHADO COMPARTILHADO: vale para todo `.cta` dourado do app; o conserto global é da
     fundação, não desta superfície. */
  .tut-next:focus-visible{ outline-offset:-4px; }
  .tutorial-dots{ display:flex; justify-content:center; gap:6px; }
  /* só a cor transiciona. A largura muda junto (7px → 20px), mas animá-la significa
     recalcular layout a cada frame — e escalar por transform distorceria o raio da borda.
     Como o slide inteiro já está animando nesses 200ms, a troca instantânea de largura
     não é perceptível e o layout não é tocado. */
  /* raio de pílula (o `4px` deixava as pontas do ponto ativo quase retas em 20x7) e a
     aresta forte no ponto apagado: o indicador de posição precisa ser LIDO, não adivinhado */
  .t-dot{ width:7px; height:7px; border-radius:var(--radius-pill); background:var(--edge-strong); transition: background-color var(--dur-2) var(--ease-standard); }
  .t-dot.active{ width:20px; background:var(--gold); }

  /* a virada de página: sai para o lado de onde a pessoa veio, entra do lado oposto.
     Curto, causal e cancelável — o arrasto interrompe a transição em vez de brigar com ela */
  .tut-stage.is-out-next{ transform:translateX(-34px); opacity:0; }
  .tut-stage.is-out-prev{ transform:translateX(34px); opacity:0; }
  .tut-stage.is-in-next{ animation: tut-in-next var(--dur-2) var(--ease-entrance) both; }
  .tut-stage.is-in-prev{ animation: tut-in-prev var(--dur-2) var(--ease-entrance) both; }
  .tut-stage.is-dragging{ transition:none; }
  @keyframes tut-in-next{ from{ transform:translateX(30px); opacity:0; } to{ transform:none; opacity:1; } }
  @keyframes tut-in-prev{ from{ transform:translateX(-30px); opacity:0; } to{ transform:none; opacity:1; } }
  @keyframes tut-in-fade{ from{ opacity:0; } to{ opacity:1; } }
  @media (prefers-reduced-motion: reduce){
    /* tutorial: a alternativa é intencional, não a ausência do efeito. A página dissolve
       no lugar, sem deslocamento — e parado o que comunica a posição é o par ponto ativo
       + rótulo do botão, que mudam no mesmo quadro. */
    .tut-stage.is-out-next, .tut-stage.is-out-prev{ transform:none; }
    .tut-stage.is-in-next, .tut-stage.is-in-prev{ animation-name: tut-in-fade; }
    /* o palco do gesto para de vez: sem laço, sem carimbo piscando, e a frase que
       explica os dois sentidos assume o lugar da demonstração */
    .tut-demo-move, .tut-hand-thumb{ animation:none; }
    .tut-swipe-demo .stamp{ animation:none; opacity:0; }
    .tut-reduced-decisions{ display:flex; align-items:center; justify-content:center; }
    /* página 2: o relógio dos modos nem chega a ser criado (o JS confere a mesma consulta),
       então as duas telas ficam lado a lado, nítidas e do mesmo tamanho. O parágrafo já diz
       que existem dois modos; um par piscando não acrescenta nada a quem pediu quieto. */
    .tut-screens-demo .tut-frame{ transition:none; filter:none; opacity:1; --plano:1; }
    .tut-modes .mode-btn.selected{ animation:none; }
  }
  /* Dobra baixa e larga — celular deitado, e também o 200% de zoom num monitor, que
     resolve para 683x384 CSS. Empilhada, a coluna estourava a tela e empurrava a ação
     decisiva para fora da vista. Deitada, a mesma composição do canvas largo resolve:
     o print de um lado, a fala do outro. */
  @media (max-height:600px) and (orientation:landscape){
    /* deitada, a dobra é baixa: só a ALTURA DE REFERÊNCIA muda, e as duas contas de
       largura a seguem sozinhas. Os 182px são a soma do que a dobra deve a "Pular", ao
       rodapé e ao respiro da tela. */
    /* O TÍTULO PASSA A SER MEDIDO PELA ALTURA. `--t-display-size` é
       `clamp(34px, 9.5vw, 48px)` — uma conta de LARGURA de viewport. Numa dobra deitada
       de 384px de altura o navegador tem 683px de largura e a conta trava no teto de
       48px, enquanto a coluna de fala real tem ~180px (o `.phone` tem teto de 420px).
       Resultado medido: duas linhas de 62px de título dentro de 225px de palco, mais o
       parágrafo e a linha do match — o excesso saía por cima do "Pular" e do CTA. Aqui a
       mesma variável passa a ler a dimensão que de fato falta. Continua sendo o token do
       papel de display; muda a régua que o mede. */
    #tutorial{ --tut-h: min(calc(100vh - 182px), 340px); --t-display-size: 8vh; }
    .tut-shell{ max-width:760px; }
    /* `safe center` e não `center`: quando a fala não cabe, centralizar EMPURRA metade
       do excesso para cima do "Pular" e a outra metade para cima do CTA — e nada disso
       é alcançável por rolagem, porque conteúdo centralizado transborda pelos dois
       lados. Com `safe`, a coluna volta a começar no topo assim que estoura, e aí a
       rolagem do palco resolve. O rodapé fica FORA do palco: a ação decisiva não sai
       da dobra em nenhum dos dois casos. */
    .tut-stage{
      flex-direction:row; align-items:safe center; justify-content:center; gap:22px;
      overflow-y:auto; overscroll-behavior:contain;
      scrollbar-width:none; -ms-overflow-style:none;
    }
    .tut-stage::-webkit-scrollbar{ display:none; }
    /* a dobra deitada devolve ao conteúdo cada folga que era só respiro: são ~24px, e é
       essa margem que separa "cabe" de "rola" nesta tela */
    #tutorial{ padding-bottom: calc(12px + env(safe-area-inset-bottom)); }
    .tut-copy{ gap:6px; }
    .tut-note{ margin-top:0; padding-top:7px; }
    /* A REPARTIÇÃO DA LARGURA é o que decide esta dobra. `.phone` tem teto de 420px,
       então o palco deitado tem ~380px — não os 760 que `.tut-shell` pede. Sem teto, o
       par de quadros levava 223px (59%) e sobravam 129 para a fala: o título quebrava em
       três linhas e a soma estourava a altura. 44% reparte a régua a favor da leitura. */
    /* o teto de largura é dos PALCOS, e o palco da página 2 passou a ser o conjunto
       trilho+telas: aplicado ao `.tut-duo` de dentro, os 44% passariam a ser 44% do palco
       e o par de telas encolheria duas vezes */
    .tut-swipe-demo, .tut-screens-demo{ flex:0 1 auto; max-width:44%; }
    /* a descrição dos modos já saiu na dobra de altura acima (600 < 720), e o par de telas
       não precisa de número novo aqui: ele vive do que sobra da altura, em qualquer dobra */
    /* DEITADA, O PALCO RECORTA. `.tut-stage` ganha `overflow-y:auto` acima — e overflow
       em um eixo torna o outro `auto` junto —, então o que sangra para fora dele é
       cortado em silêncio (a barra de rolagem está escondida). Medido em 683x384: a mão
       passava 11px do palco e a ponta do punho saía serrada. Só o sangramento de BAIXO
       encolhe; o da direita continua, porque ali existe a calha até a fala. */
    .tut-hand-fixed, .tut-hand-thumb{ bottom:-4%; }
    .tut-copy{ max-width:44ch; text-align:left; }
    .tut-text, .tut-note{ margin-inline:0; }
    .tut-foot{ padding-top:8px; gap:8px; }
  }

  /* ROOM SCREEN */
  .back-link{
    color:var(--text-dim);
    font-size:13px; font-weight:700; cursor:pointer; padding:8px 16px; min-height:44px;
    display:inline-flex; align-items:center; gap:7px;
    margin-bottom:14px; align-self:flex-start; border-radius:var(--radius-pill);
  }
  .rank-detail-nav{ display:flex; align-items:center; margin:0 0 14px; }
  .rank-detail-nav .back-link{ margin:0; }
  /* de onde a pessoa veio, dito com o dado que estava na linha tocada */
  .rank-detail-place{
    margin:0 0 10px; color:var(--gold-dim); font-weight:700;
    font-size:var(--t-num-size); letter-spacing:var(--t-num-track);
    font-variant-numeric: tabular-nums;
  }
  .room-code-box{
    display:flex; align-items:center; justify-content:space-between;
    background: linear-gradient(135deg,var(--gold-tint-bg),var(--gold-tint-deep)); border:1.5px dashed var(--gold);
    border-radius:16px; padding:20px; margin-top:6px;
  }
  .room-code{ font-family:var(--font-display); font-size:36px; letter-spacing:6px; color: var(--gold-ink); }
  .copy-btn{ border-radius:var(--radius-sm); padding:9px 16px; min-height:44px; font-size:12px; font-weight:800; }
  .participant-list{ display:flex; flex-direction:column; gap:10px; }
  .participant-row{
    display:flex; align-items:center; gap:12px; padding:10px 14px;
    border:1px solid var(--edge); border-top-color:var(--edge-lit); border-radius:var(--radius-md);
    background: linear-gradient(180deg, var(--surface-1-top), var(--surface-1-bot));
    box-shadow: var(--shadow-1), inset 0 1px 0 var(--sheen-1);
  }
  .participant-row .avatar{ width:32px; height:32px; font-size:11px; }
  .room-settings-card{
    display:flex; flex-direction:column; gap:14px; margin-top:20px; padding:16px;
    border:1px solid var(--edge); border-top-color:var(--edge-lit); border-radius:var(--radius-lg);
    background: linear-gradient(180deg, var(--surface-1-top), var(--surface-1-bot));
    box-shadow: var(--shadow-1), inset 0 1px 0 var(--sheen-1);
  }
  .room-settings-copy{ display:flex; flex-direction:column; gap:5px; }
  .room-settings-copy b{ font-size:14px; }
  .room-settings-copy span{ color:var(--text-dim); font-size:12px; line-height:1.5; }
  .room-settings-actions{ display:grid; grid-template-columns:1fr 1fr; gap:8px; }
  .room-settings-actions .result-btn{ width:100%; }
  .room-lobby-grid{ display:flex; flex-direction:column; }
  .room-invite-column,.room-people-column{ min-width:0; }
  .room-lobby-actions{ margin-top:24px; display:flex; flex-direction:column; gap:10px; }
  .room-lobby-actions .cta{ margin-top:0; }
  @media (max-width:359px){
    .room-settings-actions{ grid-template-columns:1fr; }
  }
  .participant-row b{ font-size:14px; }
  .participant-row span{ margin-left:auto; font-size:11px; color:var(--gold-ink); }

  /* HISTORY */
  /* ================= CINEMATECA (1.60.0) — o museu de cinema da casa =================
     Escolha do dono entre três protótipos e cinco capas: a entrada é um INGRESSO, as três
     salas se trocam por PLACAS de esmalte, e cada sala tem a forma do que guarda — película
     para as noites, vitrines para as listas, cartões para a turma. */
  .cine-ingresso{ margin:14px 0 0; --canhoto:72px; filter:drop-shadow(0 14px 16px var(--ingresso-sombra)); }
  /* os dois dentes do picote, um de cada lado, na altura da linha tracejada — contados a
     partir do PÉ, porque o canhoto tem altura fixa e o alto do bilhete não */
  .cine-ingresso-papel{
    position:relative; padding:16px 18px 14px; border-radius:10px; color:var(--ingresso-tinta);
    background:radial-gradient(120% 90% at 50% 0%, var(--ingresso-luz), var(--ingresso-papel) 70%);
    -webkit-mask:radial-gradient(circle 9px at 0 calc(100% - var(--canhoto)), transparent 98%, #000) left / 51% 100% no-repeat,
                 radial-gradient(circle 9px at 100% calc(100% - var(--canhoto)), transparent 98%, #000) right / 51% 100% no-repeat;
            mask:radial-gradient(circle 9px at 0 calc(100% - var(--canhoto)), transparent 98%, #000) left / 51% 100% no-repeat,
                 radial-gradient(circle 9px at 100% calc(100% - var(--canhoto)), transparent 98%, #000) right / 51% 100% no-repeat;
  }
  /* as rubricas do bilhete em Bebas: caixa alta POR DESENHO, sem text-transform */
  .cine-ingresso-topo{ display:flex; justify-content:space-between; margin:0; font-family:var(--font-display); font-weight:var(--t-display-weight); font-size:14px; line-height:1; letter-spacing:1.8px; color:var(--ingresso-rubrica); font-variant-numeric:tabular-nums; }
  .cine-ingresso h1{ margin:6px 0 0; padding-left:5px; text-align:center; font-family:var(--font-display); font-weight:var(--t-display-weight); font-size:54px; line-height:.84; letter-spacing:5px; color:var(--ingresso-tinta); text-wrap:balance; }
  .cine-ingresso-desde{ margin:6px 0 0; text-align:center; font-size:12px; font-weight:700; color:var(--ingresso-apoio); }
  .cine-ingresso-picote{ position:absolute; left:14px; right:14px; bottom:var(--canhoto); border-top:2px dashed var(--ingresso-picote); }
  /* os três campos do canhoto ficam nas MESMAS colunas das três placas logo abaixo */
  .cine-ingresso-campos{ display:grid; grid-template-columns:repeat(3, 1fr); margin:24px -4px 0; }
  .cine-ingresso-campos div{ display:grid; justify-items:center; gap:4px; padding:0 6px; }
  .cine-ingresso-campos div + div{ border-left:1px solid var(--ingresso-fio); }
  .cine-ingresso-campos dt{ font-family:var(--font-display); font-weight:var(--t-display-weight); font-size:14px; line-height:1; letter-spacing:1.4px; color:var(--ingresso-rubrica); }
  .cine-ingresso-campos dd{ margin:0; font-family:var(--font-display); font-weight:var(--t-display-weight); font-size:30px; line-height:1; color:var(--ingresso-tinta); font-variant-numeric:tabular-nums; }
  .cine-sync{ display:inline-flex; align-items:center; gap:6px; margin:12px 0 0; min-height:44px; padding:4px 4px 4px 12px; border-radius:var(--radius-pill); border:1px dashed var(--edge-strong); color:var(--text-soft); font-size:var(--t-support-size); }
  .cine-sync .ico-svg{ flex:none; color:var(--text-dim); }
  /* o desenho tem 34px, mas o ALVO tem 44: a área de toque cresce para fora (::after) */
  .cine-sync-entrar{ position:relative; min-height:34px; padding:0 10px; border:0; background:none; color:var(--gold-ink); font:inherit; font-weight:750; cursor:pointer; border-radius:var(--radius-pill); }
  .cine-sync-entrar::after{ content:""; position:absolute; inset:-5px -4px; }
  .cine-sync-entrar:disabled{ color:var(--text-dim); cursor:default; }

  /* ---- as salas: placas de esmalte com parafusos. A demonstração do tutorial veste as
     MESMAS classes num <span>, então o centro do rótulo é explícito (span não herda o
     centro que o <button> ganha do agente do usuário) ---- */
  .cine-tabs{ display:grid; grid-template-columns:repeat(3, 1fr); gap:6px; margin:14px 0 0; }
  /* AS PLACAS FICAM SEMPRE À VISTA (1.61.0, pedido do dono): presas no alto da tela quando o
     ingresso sobe, numa faixa da cor do fundo que vai de borda a borda (sangra o respiro de
     20px da .screen) e dissolve embaixo, para a sala passar por baixo sem corte seco. A
     faixa é a mesma receita da barra do topo: fundo quase opaco com desfoque. */
  /* top NEGATIVO: o grude conta a partir do respiro de 20px da .screen, e com top:0 sobrava uma
     fresta acima da faixa por onde a sala aparecia */
  #history .cine-salas{ position:sticky; top:-20px; z-index:8; margin:6px -20px 0; padding:8px 20px 10px;
    background:rgba(var(--bg-rgb), .94); -webkit-backdrop-filter:blur(8px); backdrop-filter:blur(8px); }
  #history .cine-salas::after{ content:""; position:absolute; left:0; right:0; top:100%; height:14px; pointer-events:none;
    background:linear-gradient(180deg, rgba(var(--bg-rgb), .94), rgba(var(--bg-rgb), 0)); }
  .cine-tab{
    position:relative; min-height:48px; padding:0 6px;
    display:flex; align-items:center; justify-content:center;
    border:1px solid var(--edge-strong); border-radius:10px;
    background:linear-gradient(180deg, var(--surface-3-top), var(--surface-2-bot));
    box-shadow:inset 0 1px 0 var(--sheen-1), var(--shadow-1);
    color:var(--text); cursor:pointer;
    font-family:var(--font-display); font-weight:var(--t-display-weight); font-size:22px; letter-spacing:1.6px; line-height:1;
    transition:color var(--dur-1) var(--ease-standard);
  }
  .cine-tab::before, .cine-tab::after{ content:""; position:absolute; top:6px; width:4px; height:4px; border-radius:50%; background:var(--edge-strong); }
  .cine-tab::before{ left:6px; } .cine-tab::after{ right:6px; }
  /* A ESCOLHIDA VESTE O OURO (1.47.0, pedido do dono): a mesma receita dos outros seletores */
  .cine-tab[aria-selected='true']{
    color:var(--on-gold);
    background:var(--gold-metal);
    border-color:var(--gold-edge);
    box-shadow:inset 0 1px 0 var(--gold-sheen), var(--shadow-1);
  }
  .cine-tab[aria-selected='true']::before, .cine-tab[aria-selected='true']::after{ background:rgba(26,18,6,.35); }

  /* A CINEMATECA COM LETRA GRANDE (1.62.0). Com a fonte do sistema em 150–200%, o Chrome do
     Android desenha o app em 250–187px, e três peças daqui não cediam: o nome do ingresso
     (54px fixos: "CINEM" e o resto cortado pelo papel), a coluna PESSOAS do canhoto (saía
     pela borda) e a placa "A turma" (38px além da tela). O nome escala com a largura — a
     conta é a do papel: 10 letras de Bebas ≈ 4,9em com o espaçamento, dentro de 100vw
     menos os 40px da tela e os 36px do papel — e para em 54px, que é o de sempre. */
  .cine-ingresso h1{ font-size:clamp(20px, calc((100vw - 76px) / 5.2), 54px); letter-spacing:.09em; }
  .cine-tabs{ grid-template-columns:repeat(3, minmax(0, 1fr)); }
  .cine-ingresso-campos{ grid-template-columns:repeat(3, minmax(0, 1fr)); }
  @media (max-width:300px){
    .cine-tab{ font-size:15px; letter-spacing:.6px; text-align:center; padding:4px; }
    .cine-ingresso-papel{ padding-inline:12px; }
    .cine-ingresso-campos dt{ font-size:11px; letter-spacing:.4px; }
    .cine-ingresso-campos dd{ font-size:24px; }
    .cine-ingresso-campos div{ padding:0 2px; }
    #history .cta{ white-space:normal; }
  }

  /* ---- o texto de parede de cada sala ---- */
  .cine-parede{ margin:22px 0 12px; }
  .cine-parede h2{ margin:0; font-family:var(--font-display); font-weight:var(--t-display-weight); font-size:44px; line-height:.9; letter-spacing:1px; color:var(--text); }
  .cine-parede p{ margin:6px 0 0; max-width:40ch; color:var(--text-soft); font-size:var(--t-support-size); line-height:1.5; }
  .cine-parede p b{ color:var(--text); font-weight:700; }
  .cine-h3{ margin:28px 0 2px; font-family:var(--font-display); font-weight:var(--t-display-weight); font-size:28px; line-height:1; letter-spacing:.8px; color:var(--text); }
  .cine-h3-sub{ margin:0 0 12px; color:var(--text-soft); font-size:13px; }
  /* a fileira de chips rola até a BORDA da tela (sangra o respiro de 20px da .screen): em
     360px o último chip fica visivelmente cortado pela borda do aparelho, que é o convite
     a rolar, e não pela borda de uma caixa invisível */
  .cine-filtro{ display:flex; gap:6px; overflow-x:auto; margin:10px -20px 0; padding:0 20px 2px; scroll-padding-inline:20px; }
  .cine-filtro .chip{ flex:none; }

  /* ---- A SALA NOITES: a película ----
     A tira corre pela esquerda com perfurações creme; a data de cada noite fica carimbada
     nela, como a numeração de borda do filme. */
  .noites-fita{ position:relative; margin-top:16px; padding-left:52px; }
  .noites-fita::before{
    content:""; position:absolute; left:2px; top:0; bottom:0; width:34px; border-radius:3px;
    background-color:var(--pelicula);
    background-image:
      radial-gradient(ellipse 2.6px 3.4px at 3px 50%, var(--pelicula-furo) 0 1.8px, transparent 2.4px),
      radial-gradient(ellipse 2.6px 3.4px at 31px 50%, var(--pelicula-furo) 0 1.8px, transparent 2.4px);
    background-size:34px 11px; background-repeat:repeat-y;
    box-shadow:inset 0 0 0 1px rgba(255,255,255,.05);
  }
  .noites-mes-cab{ display:flex; align-items:flex-end; justify-content:space-between; gap:10px; margin:22px 0 4px; }
  .noites-mes:first-child .noites-mes-cab{ margin-top:4px; }
  .noites-mes-cab h3{ margin:0; font-family:var(--font-display); font-weight:var(--t-display-weight); font-size:34px; line-height:.9; letter-spacing:.8px; color:var(--text); }
  .noites-mes-cab p{ margin:3px 0 0; font-size:12.5px; color:var(--text-soft); }
  .noites-retro{ flex:none; min-height:44px; display:inline-flex; align-items:center; gap:6px; padding:0 11px; border-radius:11px; cursor:pointer;
    border:1px solid var(--edge-gold); background:var(--gold-tint-bg); color:var(--gold-ink); font:inherit; font-weight:750; font-size:12.5px; }
  .noite{ position:relative; margin-top:12px; }
  /* A DATA É CARIMBADA ENTRE AS PERFURAÇÕES, no eixo da tira (1.64.1, o dono viu torta no
     celular). A tira vai de 2 a 36px da fita (eixo em 19px) e a noite começa em 52px; com 34px
     de largura e left:-48px a data ia de 4 a 38px — 2px fora do eixo, tapando os furos de um
     lado e deixando os do outro à vista. Com 22px ela cabe entre as duas fileiras de furos
     (3 e 31px): left = 19 − 11 − 52 = −44px. */
  .noite-data{ position:absolute; left:-44px; top:10px; width:22px; padding:5px 0; display:grid; justify-items:center; line-height:.9; border-radius:4px;
    background:var(--pelicula); color:var(--pelicula-data); border:1px solid var(--pelicula-aresta); }
  .noite-data b{ font-family:var(--font-display); font-weight:var(--t-display-weight); font-size:20px; font-variant-numeric:tabular-nums; }
  /* o letter-spacing entra depois da ÚLTIMA letra também: o mesmo tanto à esquerda centra o dia */
  .noite-data small{ font-family:var(--font-display); font-weight:var(--t-display-weight); font-size:11px; letter-spacing:.8px; padding-left:.8px; color:var(--gold-on-image); }
  .noite-cartao{
    display:block; width:100%; padding:12px; text-align:left; cursor:pointer; color:inherit; font:inherit;
    border-radius:var(--radius-lg); border:1px solid var(--edge);
    background:linear-gradient(180deg, var(--surface-2-top), var(--surface-2-bot));
    box-shadow:var(--shadow-2);
    transition:border-color var(--dur-1) var(--ease-standard);
  }
  .noite-cartao:hover{ border-color:var(--edge-strong); }
  .noite-topo{ display:flex; align-items:center; justify-content:space-between; gap:8px; font-size:12px; font-weight:650; color:var(--text-soft); }
  .noite-discos{ display:inline-flex; flex:none; }
  .noite-disco{ width:20px; height:20px; border-radius:50%; display:grid; place-items:center; font-size:9.5px; font-weight:800;
    color:var(--disco-letra); background:var(--pessoa, var(--edge-strong)); box-shadow:0 0 0 2px var(--surface-2-top); }
  .noite-disco + .noite-disco{ margin-left:-6px; }
  .noite-disco.is-neutro{ color:var(--text); }
  .noite-posters{ display:flex; align-items:flex-end; gap:6px; margin:10px 0 8px; }
  .noite-poster{ position:relative; flex:none; width:48px; aspect-ratio:2/3; border-radius:6px; overflow:hidden; background:var(--surface-3-bot); opacity:.78; box-shadow:0 6px 10px -6px rgba(0,0,0,.7); display:grid; place-items:center; color:var(--text-dim); }
  .noite-poster img{ display:block; width:100%; height:100%; object-fit:cover; }
  .noite-poster.is-decidido{ width:74px; opacity:1; box-shadow:0 0 0 2px var(--gold), 0 10px 18px -8px rgba(0,0,0,.8); }
  .noite-mais{ align-self:center; font-size:12px; font-weight:800; color:var(--text-soft); }
  .noite-como{ display:flex; align-items:baseline; gap:6px; font-size:13.5px; color:var(--text-soft); line-height:1.4; }
  .noite-como .ico-svg{ flex:none; color:var(--gold-ink); position:relative; top:2px; }
  .noite-como b{ color:var(--text); }
  .noite-pe{ display:flex; align-items:center; justify-content:space-between; gap:8px; margin-top:8px; font-size:12px; color:var(--text-soft); }
  .noite-nota{ flex:none; display:inline-flex; align-items:center; min-height:26px; padding:0 9px; border-radius:var(--radius-pill); border:1px solid var(--edge-strong); font-weight:700; color:var(--text-soft); }
  .noite-nota.is-bom{ color:var(--rank-sobe); border-color:color-mix(in srgb, var(--rank-sobe) 45%, transparent); }
  .noite-nota.is-ruim{ color:var(--rank-desce); border-color:color-mix(in srgb, var(--rank-desce) 45%, transparent); }
  .noite-sem{ display:block; width:100%; min-height:44px; padding:10px 12px; text-align:left; cursor:pointer; font:inherit; font-size:13px;
    border-radius:var(--radius-md); border:1px dashed var(--edge-strong); background:none; color:var(--text-soft); }
  .noite-sem b{ color:var(--text); }
  /* a lixeira só existe no modo Gerenciar, no canto do cartão; a linha de cima abre espaço
     para ela, senão ela pousa em cima das bolinhas de quem estava */
  .noite .history-remove-btn{ position:absolute; top:4px; right:4px; z-index:2; background:var(--surface-2-top); }
  .history-content.is-managing .noite-topo{ padding-right:44px; }
  .history-content.is-managing .noite-sem{ padding-right:56px; }
  .noites-leitura{ margin:12px 0 0; padding:12px 14px; border-radius:var(--radius-md); border:1px solid var(--edge-gold); background:var(--gold-tint-bg); font-size:13px; line-height:1.5; color:var(--text-soft); }
  .noites-leitura b{ color:var(--text); }
  .noite-skel{ height:150px; border-radius:var(--radius-lg); }
  /* o vazio da sala Noites cabe acima da barra: o ingresso zerado e a frase do alto já dizem
     o que a sala guarda, e aqui sobra só a saída (com a película vazia junto, o botão ficava
     sob o véu da barra de abas em 375×812) */
  /* duas classes de propósito: .history-empty (mais abaixo na folha) reserva 230px e centra */
  .history-empty.noites-sem-nada{ min-height:0; padding:4px 0 0; align-items:flex-start; justify-content:flex-start; }
  .history-empty.noites-sem-nada .cta{ margin-top:0; }

  /* ---- A SALA LISTAS: vitrines ----
     Uma caixa iluminada de cima com os cartazes em pé, em leque, e a placa embaixo. O leque
     é por CLASSE (vitrine-capa--N-de-M), porque o render não escreve estilo inline. */
  .vitrines{ display:grid; gap:14px; }
  .vitrine{ position:relative; isolation:isolate; overflow:hidden; border-radius:var(--radius-lg); border:1px solid var(--edge-strong);
    background:linear-gradient(180deg, var(--surface-2-top), var(--surface-2-bot)); box-shadow:var(--shadow-2); }
  .vitrine-caixa{ position:relative; display:block; width:100%; height:132px; padding:0; border:0; border-bottom:1px solid var(--edge-strong); cursor:pointer;
    background:radial-gradient(70% 90% at 50% -10%, var(--vitrine-luz), transparent 70%), linear-gradient(180deg, var(--vitrine-fundo-top), var(--vitrine-fundo) 30%); }
  .vitrine-caixa::after{ content:""; position:absolute; left:0; right:0; bottom:0; height:12px; background:linear-gradient(180deg, rgba(0,0,0,.28), rgba(0,0,0,0)); }
  .vitrine-capa{ position:absolute; bottom:12px; left:50%; width:62px; aspect-ratio:2/3; border-radius:3px; overflow:hidden; background:var(--surface-3-bot);
    display:grid; place-items:center; color:var(--text-dim); box-shadow:0 10px 14px -6px rgba(0,0,0,.8);
    transform-origin:50% 100%; transform:translateX(calc(-50% + var(--x, 0px))) rotate(var(--r, 0deg));
    transition:transform var(--dur-2) var(--ease-entrance); }
  .vitrine-capa img{ display:block; width:100%; height:100%; object-fit:cover; }
  .vitrine-capa--1-1{ --x:0px; --r:0deg; z-index:10; }
  .vitrine-capa--1-2{ --x:-75px; --r:-2deg; z-index:10; }
  .vitrine-capa--2-2{ --x:75px; --r:2deg; z-index:9; }
  .vitrine-capa--1-3{ --x:-75px; --r:-4deg; z-index:9; }
  .vitrine-capa--2-3{ --x:0px; --r:0deg; z-index:10; }
  .vitrine-capa--3-3{ --x:75px; --r:4deg; z-index:9; }
  .vitrine-capa--1-4{ --x:-75px; --r:-6deg; z-index:9; }
  .vitrine-capa--2-4{ --x:-25px; --r:-2deg; z-index:10; }
  .vitrine-capa--3-4{ --x:25px; --r:2deg; z-index:9; }
  .vitrine-capa--4-4{ --x:75px; --r:6deg; z-index:8; }
  .vitrine-capa--1-5{ --x:-75px; --r:-8deg; z-index:8; }
  .vitrine-capa--2-5{ --x:-37px; --r:-4deg; z-index:9; }
  .vitrine-capa--3-5{ --x:0px; --r:0deg; z-index:10; }
  .vitrine-capa--4-5{ --x:38px; --r:4deg; z-index:9; }
  .vitrine-capa--5-5{ --x:75px; --r:8deg; z-index:8; }
  .vitrine-caixa:hover .vitrine-capa{ transform:translateX(calc(-50% + var(--x, 0px))) rotate(calc(var(--r, 0deg) * 1.4)) translateY(-2px); }
  .vitrine-vazia{ position:absolute; inset:0; display:grid; place-content:center; justify-items:center; gap:6px; color:var(--text-dim); font-size:12.5px; }
  .vitrine-placa{ display:grid; grid-template-columns:minmax(0, 1fr) auto auto; align-items:center; gap:8px; padding:12px 8px 12px 14px; }
  .vitrine-nome{ min-width:0; }
  .vitrine-nome h4{ margin:0; font-family:var(--font-display); font-weight:var(--t-display-weight); font-size:24px; line-height:1; letter-spacing:.6px; color:var(--text); overflow-wrap:anywhere; }
  .vitrine-nome p{ margin:3px 0 0; font-size:12.5px; color:var(--text-soft); }
  .vitrine-jogar{ min-height:44px; display:inline-flex; align-items:center; gap:6px; padding:0 14px; cursor:pointer; font:inherit; font-weight:800; font-size:13.5px;
    border-radius:var(--radius-md); border:1px solid var(--gold-edge); background:var(--gold-metal); color:var(--on-gold); box-shadow:inset 0 1px 0 var(--gold-sheen); }
  .vitrine-mais{ width:44px; height:44px; display:grid; place-items:center; cursor:pointer; border-radius:var(--radius-md); border:1px solid var(--edge); background:none; color:var(--text-soft); }
  .vitrine-mais:hover{ color:var(--text); border-color:var(--edge-strong); }
  .cine-montar{ display:grid; grid-template-columns:1fr 1fr; gap:8px; margin-top:14px; }
  .cine-montar button{ min-height:76px; display:grid; place-items:center; align-content:center; gap:3px; padding:8px; cursor:pointer; font:inherit; font-size:13px;
    border-radius:var(--radius-lg); border:1px dashed var(--edge-strong); background:none; color:var(--text); }
  .cine-montar button b{ font-weight:700; }
  .cine-montar button small{ color:var(--text-dim); font-weight:600; font-size:11.5px; }
  .cine-montar .ico-svg{ color:var(--gold-ink); }

  /* ---- A SALA "A TURMA": um cartão por pessoa, na cor dela ---- */
  .turma-cartoes{ display:grid; grid-template-columns:1fr 1fr; gap:10px; }
  .turma-cartao{ position:relative; overflow:hidden; display:grid; align-content:start; justify-items:start; gap:6px; min-height:44px; padding:12px; text-align:left; cursor:pointer; font:inherit; color:var(--text);
    border-radius:var(--radius-md); border:1px solid var(--edge-strong);
    background:linear-gradient(160deg, color-mix(in srgb, var(--pessoa, var(--gold)) 22%, transparent), transparent 60%), var(--surface-2-bot); }
  .turma-cartao::after{ content:""; position:absolute; right:-18px; top:-18px; width:70px; height:70px; border-radius:50%; background:var(--pessoa, var(--gold)); opacity:.16; pointer-events:none; }
  .turma-cartao b{ font-family:var(--font-display); font-weight:var(--t-display-weight); font-size:24px; line-height:1; letter-spacing:.6px; overflow-wrap:anywhere; }
  .turma-cartao small{ font-size:12px; line-height:1.4; color:var(--text-soft); }
  .turma-cartao small em{ font-style:normal; font-weight:800; color:var(--text); }
  .turma-cartao small{ display:grid; justify-self:stretch; min-width:0; }
  .turma-linha{ min-width:0; }
  .turma-gosto{ display:flex; gap:4px; min-width:0; white-space:nowrap; }
  .turma-gosto em{ min-width:0; overflow:hidden; text-overflow:ellipsis; }
  .turma-gosto > span{ flex:none; }
  .turma-cartao[aria-pressed="true"]{ border-color:var(--pessoa, var(--gold)); box-shadow:0 0 0 1px var(--pessoa, var(--gold)), var(--shadow-2); }
  .turma-cartao--mais, .turma-cartao--gerenciar{ border-style:dashed; background:none; place-content:center; justify-items:center; text-align:center; }
  .turma-cartao--mais::after, .turma-cartao--gerenciar::after{ content:none; }
  .turma-cartao--gerenciar b{ font-family:inherit; font-size:14px; font-weight:750; letter-spacing:0; }
  .turma-cartao--gerenciar .ico-svg{ color:var(--gold-ink); }
  .turma-disco{ width:40px; height:40px; border-radius:50%; display:grid; place-items:center; flex:none;
    font-family:var(--font-display); font-weight:var(--t-display-weight); font-size:22px; color:var(--disco-letra);
    background:var(--pessoa, var(--gold)); box-shadow:0 0 0 2px var(--bg); }
  .turma-perto{ margin-top:22px; padding-top:18px; border-top:1px dashed var(--edge-strong); }
  .turma-perto-nome{ margin:0; font-family:var(--font-display); font-weight:var(--t-display-weight); font-size:30px; line-height:1; letter-spacing:.6px; color:var(--pessoa, var(--gold-ink)); }
  .turma-frase{ margin:8px 0 0; font-size:14px; line-height:1.55; color:var(--text-soft); }
  .turma-frase b{ color:var(--text); font-variant-numeric:tabular-nums; }
  .turma-portas{ display:flex; flex-wrap:wrap; gap:6px; margin-top:12px; }
  .turma-portas .chip{ gap:5px; }
  .turma-bloco{ margin-top:22px; }
  .turma-generos{ list-style:none; margin:10px 0 0; padding:0; display:grid; gap:12px; }
  .turma-genero-cab{ display:flex; justify-content:space-between; align-items:baseline; gap:8px; font-size:13px; font-weight:650; color:var(--text); }
  .turma-genero-cab b{ color:var(--pessoa, var(--gold-ink)); font-weight:800; font-variant-numeric:tabular-nums; }
  .turma-genero-cab small{ margin-left:6px; color:var(--text-dim); font-weight:600; font-size:11px; }
  .turma-trilho{ position:relative; display:block; height:8px; margin-top:5px; border-radius:4px; background:var(--edge); }
  .turma-cheia{ position:absolute; left:0; top:0; bottom:0; width:calc(var(--v, 0) * 100%); border-radius:4px; background:var(--pessoa, var(--gold)); }
  .turma-marca{ position:absolute; top:-4px; width:4px; height:16px; margin-left:calc(-2px + var(--desvio, 0px)); left:calc(var(--v, 0) * 100%); border-radius:2px; background:var(--pessoa); box-shadow:0 0 0 2px var(--bg); }
  .turma-pares{ list-style:none; margin:0; padding:0; }
  .turma-pares li{ display:grid; grid-template-columns:auto minmax(0, 1fr) auto; align-items:center; gap:10px; padding:9px 0; border-bottom:1px solid var(--rule); font-size:13px; color:var(--text); }
  .turma-pares b{ font-family:var(--font-display); font-weight:var(--t-display-weight); font-size:26px; line-height:1; color:var(--gold-ink); font-variant-numeric:tabular-nums; }
  .turma-dupla{ display:inline-flex; }
  .turma-disco--p{ width:26px; height:26px; font-size:14px; }
  .turma-disco--p + .turma-disco--p{ margin-left:-7px; }
  /* troca de tela pelo gesto de arrastar: só OPACIDADE. Deslizar a tela com transform
     tornaria a tela o "bloco de contenção" das barras fixas (a doca do CTA do Início e do
     Ranking) durante a animação, e elas pulariam de lugar */
  @keyframes tela-chega{ from{ opacity:.35; } to{ opacity:1; } }
  .screen.tela-chega{ animation:tela-chega 180ms var(--ease-standard); }
  @media (prefers-reduced-motion: reduce){ .screen.tela-chega{ animation:none; } }

  /* ---- A TROCA DE ABA DESLIZA (1.53.0) — ver `irParaAba` ----
     O que anima é a FOTO das duas telas (::view-transition-old/new da raiz), nunca a
     `.screen`: é isso que deixa as docas fixas no lugar. O topo e a barra de abas ganham
     nome próprio e saem da foto da raiz — ficam parados, e a barra só troca a aba acesa
     num cruzamento curto, que é a animação padrão de um grupo com nome.
     É o padrão do EIXO COMPARTILHADO (o das abas do Android): a tela que sai desliza 56px e
     some no primeiro terço; a que chega só começa a aparecer DEPOIS, deslizando os mesmos 56px
     do outro lado. Na primeira versão as duas faziam o caminho juntas, cruzando a opacidade —
     e a foto do meio da animação (Chrome sem janela, velocidade a 10%) mostrou o Início e o
     Ranking sobrepostos, texto por cima de texto. Separadas no tempo, nunca há tela dupla.
     O deslocamento é curto de propósito: a vizinha chega JÁ perto do lugar, e isso lê como
     "a aba ao lado" sem virar um carrossel que atravessa a tela. */
  #topbar{ view-transition-name:topo-do-app; }
  #tabbar{ view-transition-name:abas-do-app; }
  @media (prefers-reduced-motion: no-preference){
    :root[data-aba-vai]::view-transition-old(root){ animation:aba-sai 300ms cubic-bezier(.4, 0, .2, 1) both; }
    :root[data-aba-vai]::view-transition-new(root){ animation:aba-entra 300ms cubic-bezier(.4, 0, .2, 1) both; }
    :root[data-aba-vai="frente"]{ --aba-sai-x:-56px; --aba-entra-x:56px; }
    :root[data-aba-vai="tras"]{ --aba-sai-x:56px; --aba-entra-x:-56px; }
    @keyframes aba-sai{ 35%, 100%{ opacity:0; } 100%{ transform:translateX(var(--aba-sai-x)); } }
    @keyframes aba-entra{ 0%, 35%{ opacity:0; transform:translateX(var(--aba-entra-x)); } 100%{ opacity:1; transform:none; } }
  }

  /* o respiro do pé é UM só, e mora no `#history` (104px, dos quais 73 ficam sob a barra
     de abas). Enquanto ele também estava aqui, as duas abas terminavam com 208px de nada —
     a aba Partidas, que nunca teve o segundo padding, era a única que acabava certo. */
  .cine-lists{ display:flex; flex-direction:column; gap:18px; padding:18px 0 0; }

  /* ---- MEU GOSTO ----
     Um gráfico de barras feito de CSS, não de biblioteca: o app é uma PWA que promete abrir
     offline, e uma dependência de CDN pra desenhar cinco proporções seria o mesmo erro que
     a Bebas Neue já pagou nesta folha.
     O número fica SEMPRE escrito ao lado da barra. A barra é o atalho visual; ela não pode
     ser a única forma de ler o dado, porque a leitura por comprimento é justamente a que
     falha em tela pequena, com pouca luz e pra quem enxerga mal. */
  .cine-taste{ display:flex; flex-direction:column; gap:22px; padding:18px 0 0; }
  /* ---- GERENCIAR QUEM APARECE ----
     O chip da ação não disputa com os chips de pessoa: os de pessoa são ESCOLHA (e o aceso
     usa o ouro), este é uma porta para outra tela — fica em tipo de apoio, com o glifo à
     frente do rótulo, do mesmo jeito que "Mudar" abre o refinamento na tela inicial. */
  .taste-manage{ display:inline-flex; align-items:center; gap:7px; font-weight:700; }
  .taste-manage .ico-svg{ color:var(--text-dim); }
  .taste-manage-list{ display:flex; flex-direction:column; }
  /* nome de um lado, ações do outro, e a linha NÃO é um alvo: aqui não existe um gesto
     "principal" sobre a pessoa — juntar e apagar são as duas coisas que há —, e um botão
     esticado por baixo dos dois seria controle dentro de controle. */
  /* A BOLINHA DE COR ENTROU NA LINHA, e com ela três blocos não cabem lado a lado num
     celular: a linha quebra, e é a quebra que precisa ser desenhada. A cor e o nome ficam
     na primeira faixa; as ações descem JUNTAS para a segunda, encostadas à direita.
     A primeira tentativa deixou os dois botões soltos na grade e mandou um para cada
     ponta — o vão entre "Juntar" e "Apagar" ficou do tamanho da linha. */
  /* A FICHA DE CADA PESSOA (1.61.0): a bolinha da cor, e na coluna do texto o nome, a conta e
     as duas ações logo embaixo, à esquerda. O desenho do botão tem 32px e o ALVO tem 44: a
     área de toque cresce para fora (::after), como na bolinha. */
  .taste-manage-row{
    display:grid; grid-template-columns:auto minmax(0,1fr); gap:12px;
    align-items:start; padding:14px 0; border-bottom:1px solid var(--rule);
  }
  .taste-manage-row .taste-color-dot{ position:relative; margin-top:1px; }
  .taste-manage-row .taste-color-dot::after{ content:""; position:absolute; inset:-11px; }
  .taste-manage-acts{ display:flex; flex-wrap:wrap; gap:8px; margin-top:9px; }
  .taste-manage-row:last-child{ border-bottom:none; }
  .taste-manage-copy{ min-width:0; }
  .taste-manage-copy b{
    display:block; color:var(--text); font-weight:700;
    overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
  }
  .taste-manage-copy span{
    display:block; margin-top:2px; color:var(--text-dim);
    font-size:var(--t-support-size); line-height:var(--t-support-lh);
  }
  .taste-manage-act{ position:relative; display:inline-flex; align-items:center; gap:5px; min-height:32px; padding:0 12px 0 10px; font-size:12.5px; font-weight:700; color:var(--text-soft); }
  .taste-manage-act::after{ content:""; position:absolute; inset:-6px -4px; }
  .taste-manage-act .ico-svg{ flex:none; }
  /* destrutivo é CONTORNO de aviso, nunca preenchimento — a mesma regra do `.btn--danger`,
     repetida aqui porque `.chip` é declarado depois dele e venceria a cascata */
  .taste-manage-drop{
    color:var(--btn-danger-fg); border-color:var(--btn-danger-edge);
    background:rgba(var(--rose-rgb), .08);
  }
  .taste-block{ display:flex; flex-direction:column; gap:8px; }
  .taste-note{ margin:0; font-size:var(--t-support-size); line-height:1.5; color:var(--text-soft); }
  .taste-note--foot{ color:var(--text-dim); }

  .taste-bars{ list-style:none; margin:0; padding:0; display:flex; flex-direction:column; gap:9px; }
  .taste-bar{ display:grid; grid-template-columns:minmax(0,1fr) auto; gap:4px 10px; align-items:center; }
  .taste-bar-name{
    grid-column:1; font-size:var(--t-body-size); color:var(--text);
    overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
  }
  /* o valor mora na MESMA linha do nome, à direita: em coluna própria ele empurraria o
     nome pra um corredor estreito e todo gênero de nome longo viraria reticências */
  .taste-bar-value{
    grid-column:2; text-align:right; font-size:var(--t-body-size); font-weight:700;
    color:var(--gold-ink); font-variant-numeric:tabular-nums; white-space:nowrap;
  }
  .taste-bar-value i{
    font-style:normal; margin-left:6px; font-size:var(--t-support-size); font-weight:400;
    color:var(--text-dim);
  }
  .taste-bar-track{
    grid-column:1 / -1; grid-row:2; height:8px; border-radius:var(--radius-pill);
    background:rgba(255,255,255,.06); overflow:hidden;
  }
  .taste-bar-fill{
    display:block; height:100%; border-radius:var(--radius-pill);
    background:linear-gradient(90deg, var(--gold-dim), var(--gold));
  }
  /* a barra cresce ao entrar, uma vez só. Quem pediu menos movimento recebe ela pronta —
     a informação é a mesma, e ela nunca dependeu da animação pra ser lida. */
  @media (prefers-reduced-motion: no-preference){
    .taste-bar-fill{ animation: taste-grow .5s var(--ease-standard) both; }
    @keyframes taste-grow{ from{ transform:scaleX(0); transform-origin:left; } }
  }
  /* ---- O TÍTULO É QUE ABRE A COMPARAÇÃO ----
     A primeira versão pendurava um alvo de toque em CADA gênero, e alvo não encolhe pra
     caber em desenho: oito linhas de ~34px obrigavam a afastar tudo pra caber 44px de
     toque em cada uma, e a lista — que é uma leitura de alturas — virava formulário.
     Um alvo só, no título, diz a mesma coisa. A pastilha ao lado é o que torna o toque
     visível: um título que abre algo e não parece um botão é um segredo. */
  .taste-open-title{
    display:flex; align-items:center; gap:10px; flex-wrap:wrap;
    width:100%; margin:7px 0 3px; padding:11px 0; min-height:44px;
    background:none; border:0; text-align:left; cursor:pointer;
    color:var(--text); font-size:var(--t-group-size); line-height:var(--t-group-lh);
    letter-spacing:var(--t-group-track); font-weight:var(--t-group-weight);
  }
  .taste-open-title:focus-visible{ outline:2px solid var(--gold); outline-offset:3px; border-radius:var(--radius-sm); }
  .taste-open-hint{
    display:inline-flex; align-items:center; gap:5px; padding:3px 10px;
    border-radius:var(--radius-pill); border:1px solid rgba(var(--gold-rgb), .38);
    background:rgba(var(--gold-rgb), .10); color:var(--gold-ink);
    font-size:var(--t-support-size); font-weight:600; letter-spacing:0;
  }
  .taste-open-title:active .taste-open-hint{ background:rgba(var(--gold-rgb), .18); }
  /* a linha de quem é dono do aparelho, achada de relance no meio dos outros nomes */
  .taste-bar--meu .taste-bar-name{ font-weight:700; color:var(--text); }

  /* ---- A BARRA COM TODO MUNDO DENTRO ----
     As demãos são absolutas no mesmo trilho e vão da maior para a menor: cada uma começa na
     esquerda, então a menor pinta por cima e o que se lê é ONDE CADA PESSOA PARA. Sem o
     `position:relative` no trilho elas escapariam para o canto da tela. */
  .taste-bars--multi{ gap:15px; }
  /* a régua dos tracinhos (1.61.0): a mesma do "de perto", ocupando a linha inteira embaixo
     do nome e dos números */
  .taste-bar--multi .turma-trilho{ grid-column:1 / -1; margin-top:3px; }
  .taste-bar-value--multi{ display:flex; flex-wrap:wrap; justify-content:flex-end; gap:9px; }
  .taste-bar-value--multi b{ font-weight:700; font-variant-numeric:tabular-nums; color:var(--pessoa, var(--text)); }

  /* a legenda amarra cor e pessoa — e é o atalho pra trocar, porque é ali que a pergunta
     "qual é a minha?" acontece */
  .taste-legend{ display:flex; flex-wrap:wrap; gap:8px; margin:0 0 4px; }
  .taste-legend-chip{
    display:inline-flex; align-items:center; gap:8px; min-height:38px; padding:6px 13px;
    border-radius:var(--radius-pill); border:1px solid var(--edge);
    background:rgba(255,255,255,.04); color:var(--text);
    font-size:var(--t-support-size); cursor:pointer;
  }
  .taste-legend-chip i{ width:12px; height:12px; border-radius:50%; display:block; flex:none; background:var(--pessoa); }
  .taste-legend-chip:focus-visible{ outline:2px solid var(--gold); outline-offset:2px; }

  .taste-color-grid{ display:grid; grid-template-columns:repeat(auto-fit, minmax(148px, 1fr)); gap:10px; }
  .taste-color-swatch{
    display:flex; align-items:center; gap:11px; min-height:52px; padding:10px 14px;
    border-radius:var(--radius-md); border:1px solid var(--edge); border-top-color:var(--edge-lit);
    background:linear-gradient(180deg, var(--surface-1-top), var(--surface-1-bot));
    color:var(--text); font-size:var(--t-body-size); text-align:left; cursor:pointer;
  }
  .taste-color-swatch i{ width:22px; height:22px; border-radius:50%; background:var(--cor); flex:none; }
  .taste-color-swatch.is-atual{ border-color:var(--cor); box-shadow:inset 0 0 0 1px var(--cor); }
  .taste-color-swatch:focus-visible{ outline:2px solid var(--gold); outline-offset:2px; }

  /* a bolinha na linha do Gerenciar: o mesmo gesto da legenda, no lugar onde a pessoa é
     administrada */
  .taste-color-dot{
    width:26px; height:26px; flex:none; padding:0; cursor:pointer;
    border-radius:50%; border:2px solid rgba(255,255,255,.16); background:var(--cor);
  }
  .taste-color-dot:focus-visible{ outline:2px solid var(--gold); outline-offset:2px; }

  /* Dentro da folha de comparação os blocos são irmãos DIRETOS do rolo, sem a régua de
     espaçamento que o painel da aba dá de graça — e a nota do pé nascia encostada na
     última barra, lendo como legenda dela. */
  #tasteCompareOverlay .taste-bars{ margin-top:14px; }
  #tasteCompareOverlay .taste-note--foot{ margin-top:20px; }
  /* o anel do tracinho é da cor da FOLHA, não da tela: é ele que separa o tracinho da barra */
  #tasteCompareOverlay .turma-marca{ box-shadow:0 0 0 2px var(--surface-3-top); }
  .comparar-leitura{ margin-top:10px; }

  /* AS COMPARAÇÕES SE FOLHEIAM (1.61.0): uma seta em cada borda do cabeçalho, pontos que dizem
     em qual se está, e o miolo entrando do lado de onde veio. Na ponta, a seta daquele lado
     apaga sem sair do lugar (o título não pula). O eixo vertical é do navegador (rolar e o
     arraste de fechar); o horizontal é do gesto de folhear. */
  #tasteCompareOverlay .modal-scroll{ touch-action:pan-y; }
  .comparar-cab{ display:grid; grid-template-columns:44px minmax(0, 1fr) 44px; align-items:start; gap:6px; margin:0 -8px; }
  .comparar-cab .list-menu-head{ text-align:center; padding-bottom:4px; }
  .comparar-seta{ width:44px; height:44px; margin-top:2px; display:grid; place-items:center; padding:0; cursor:pointer;
    border-radius:50%; border:1px solid var(--edge-strong); color:var(--text);
    background:linear-gradient(180deg, var(--surface-2-top), var(--surface-2-bot)); box-shadow:var(--shadow-1); }
  .comparar-seta[data-passo="-1"] .ico-svg{ transform:scaleX(-1); }
  .comparar-seta.is-ponta{ visibility:hidden; }
  .comparar-pontos{ display:flex; justify-content:center; gap:6px; margin:0 0 14px; }
  .comparar-pontos:empty{ display:none; }
  .comparar-pontos i{ width:6px; height:6px; border-radius:3px; background:var(--edge-strong); }
  .comparar-pontos i.is-aqui{ width:18px; background:var(--gold); }
  @media (prefers-reduced-motion: no-preference){
    .comparar-corpo--da-direita{ animation:comparar-da-direita 220ms var(--ease-entrance); }
    .comparar-corpo--da-esquerda{ animation:comparar-da-esquerda 220ms var(--ease-entrance); }
    @keyframes comparar-da-direita{ from{ opacity:0; transform:translateX(28px); } }
    @keyframes comparar-da-esquerda{ from{ opacity:0; transform:translateX(-28px); } }
  }

  /* ---- AS LISTAS PRONTAS NO PAINEL DE IMPORTAR ----
     Mesma anatomia da linha da estante — texto à esquerda, gesto à direita —, porque é a
     mesma pergunta ("o que é esta lista, e como eu a pego"). Sem ícone temático: seis
     ícones arbitrários dizem menos que seis nomes, e um ícone repetido seis vezes é ruído. */
  /* O ✕ DOS PAINÉIS É ABSOLUTO NO CANTO, E O TÍTULO É CENTRADO NA LARGURA INTEIRA: um título
     longo em Bebas passa por baixo dele. Foi consertado três vezes, painel por painel — o de
     importar lista (1.40.0), o "Como vai ser a rodada" e o "O que entra na busca" (1.52.x) — e
     a varredura achou mais: "Jogar com o top da semana", "Sua lista está no ar" e o NOME de uma
     lista, que é digitado e pode ter qualquer largura. Então a regra é do PADRÃO: todo título
     que abre o cartão de um painel com ✕ começa abaixo do ✕ (que vai até ~44px do topo).
     Respiro dos lados não servia: em 360px ele quebrava o título em duas linhas, que custam
     mais altura que descer 24px. O sorteio fica de fora — o título dele é curto e a folha tem
     medida própria. Painel novo com esse formato nasce certo sem ninguém lembrar. */
  .detail-overlay-close + .account-panel-card:not(.raffle-panel) > .modal-title:first-child{ padding-top:32px; }
  /* a fileira de escolha do card: miniaturas do tamanho do polegar, a escolhida em ouro */
  .share-pick{ margin-top:16px; }
  .share-pick-label{
    margin:0 0 8px; font-size:var(--t-support-size); color:var(--text-dim); text-align:center;
  }
  .share-pick-row{ display:flex; gap:8px; justify-content:center; flex-wrap:wrap; }
  /* ---- A FOLHA DE ESCOLHER O FILME ----
     Ela NASCE NO MEIO DA TELA, e é a única folha do app que faz isso. As outras sobem de
     baixo porque são menus: a mão vai até elas, decide e volta. Esta é uma comparação — a
     pessoa olha três ou cinco cartazes e pesa —, e colada na borda inferior ela obrigava o
     olho a descer para o canto do aparelho, longe do centro da atenção. */
  #choosePickOverlay{ align-items:center; }
  #choosePickOverlay .modal-sheet{
    max-height:min(80vh, 620px); border-radius:var(--radius-lg);
    margin:0 12px; width:auto; max-width:420px;
  }
  #choosePickOverlay .modal-drag-zone{ display:none; }

  .choose-grid{
    display:grid; grid-template-columns:repeat(auto-fit, minmax(124px, 1fr));
    gap:12px; padding:4px 0 6px;
  }
  .choose-card{
    display:flex; flex-direction:column; gap:7px; padding:0; cursor:pointer;
    background:none; border:0; text-align:left; color:var(--text); font:inherit;
  }
  .choose-art{
    position:relative; display:block; aspect-ratio:2/3; overflow:hidden;
    border-radius:var(--radius-md); border:2px solid var(--edge);
    background:var(--surface-2-top);
  }
  .choose-art img{ width:100%; height:100%; object-fit:cover; display:block; }
  .choose-sem-arte{
    position:absolute; inset:0; display:flex; align-items:center; justify-content:center;
    color:var(--text-dim);
  }
  /* a marca de escolhido mora no CARTAZ, não numa borda fina: numa grade de capas
     coloridas, uma borda dourada compete com a arte e some no cartaz que já é dourado */
  .choose-marca{
    position:absolute; top:6px; right:6px; width:26px; height:26px;
    display:none; align-items:center; justify-content:center;
    border-radius:50%; background:var(--gold); color:var(--bg);
  }
  .choose-card[aria-pressed="true"] .choose-art{
    border-color:var(--gold); box-shadow:0 0 0 3px rgba(var(--gold-rgb), .25);
  }
  .choose-card[aria-pressed="true"] .choose-marca{ display:flex; }
  .choose-card:not([aria-pressed="true"]) .choose-art{ opacity:.82; }
  .choose-card:active .choose-art{ transform:scale(.98); }
  .choose-card:focus-visible .choose-art{ outline:2px solid var(--gold); outline-offset:3px; }
  /* DUAS LINHAS RESERVADAS mesmo quando o nome cabe em uma. É a mesma conta do pódio do
     Ranking e do placar do perfil: sem ela, "Duna: Parte Dois" (uma linha) e "O Auto da
     Compadecida" (duas) põem o nome do streaming em alturas diferentes lado a lado, e a
     grade lê como desalinhada mesmo estando certa. */
  .choose-nome{
    font-size:var(--t-body-size); font-weight:700; line-height:1.25;
    min-height:2.5em;
    display:-webkit-box; -webkit-line-clamp:2; -webkit-box-orient:vertical; overflow:hidden;
  }
  .choose-onde{ font-size:var(--t-support-size); color:var(--gold-ink); line-height:1.2; }
  .share-pick-item{
    width:52px; height:78px; padding:0; overflow:hidden; cursor:pointer;
    border-radius:var(--radius-sm); border:2px solid var(--edge); background:var(--surface-2-top);
    display:flex; align-items:center; justify-content:center; font-size:22px;
  }
  .share-pick-item img{ width:100%; height:100%; object-fit:cover; display:block; }
  .share-pick-item[aria-pressed="true"]{ border-color:var(--gold); box-shadow:0 0 0 2px rgba(var(--gold-rgb),.3); }
  .share-pick-item:not([aria-pressed="true"]){ opacity:.6; }
  .share-preview{ transition:opacity .18s var(--ease-standard); }

  /* o pé da partida: a ação fica LONGE dos cartazes, com régua, porque ela não é sobre um
     título — é sobre a partida inteira */
  .sess-share{
    margin-top:18px; padding-top:16px; border-top:1px solid var(--rule);
    display:flex; justify-content:center;
  }

  .import-ready{ margin-top:26px; }
  .import-ready .section-label{ margin:0 0 4px; }
  .ready-list{ display:flex; flex-direction:column; gap:8px; margin-top:12px; }
  .ready-card{
    display:flex; align-items:center; gap:12px; width:100%; min-height:60px;
    padding:12px 14px; text-align:left; cursor:pointer;
    border:1px solid var(--edge); border-top-color:var(--edge-lit); border-radius:var(--radius-md);
    background:linear-gradient(180deg, var(--surface-1-top), var(--surface-1-bot));
    box-shadow:var(--shadow-1), inset 0 1px 0 var(--sheen-1);
    color:var(--text); font:inherit;
  }
  .ready-copy{ flex:1; min-width:0; }
  .ready-copy b{ display:block; font-weight:700; color:var(--text); }
  .ready-copy span{
    display:block; margin-top:2px; color:var(--text-dim);
    font-size:var(--t-support-size); line-height:var(--t-support-lh);
  }
  .ready-go{ color:var(--gold-ink); flex:none; display:flex; }
  .ready-card:active{ transform:translateY(1px); }
  .ready-card:focus-visible{ outline:2px solid var(--gold); outline-offset:2px; }
  /* enquanto o recorte é buscado o cartão não pode parecer inerte, e trocar o texto por
     "Buscando…" faria a lista inteira dançar de altura */
  .ready-card.is-busy{ opacity:.55; }
  .ready-card.is-busy .ready-go{ animation: ready-pulsa 1s ease-in-out infinite; }
  @keyframes ready-pulsa{ 50%{ opacity:.25; } }
  /* ---- AS LISTAS DA CASA: a vitrine de importar (1.68.0) ----
     O código virou uma linha só no alto (quem tem um, tem pressa); embaixo, dois trilhos de
     filtro — streaming, com o logo, e tipo — e uma grade de cartões. Cada cartão é uma
     vitrine pequena: três cartazes em leque sob a mesma luz das vitrines da Cinemateca, o
     logo do streaming no canto e o nome. O toque guarda; guardada, a aresta vira ouro
     apagado e o rótulo diz isso (estado, não ação: o ouro cheio continua sendo do match). */
  .casa-painel .account-panel-card{ padding-bottom:18px; }
  .casa-sub{ margin-top:4px; text-align:center; }
  .casa-codigo{ display:flex; gap:8px; margin-top:16px; }
  /* o campo já guarda o código em maiúsculas (limparCodigo): sem text-transform aqui */
  .casa-codigo .nickname-input{ flex:1; min-width:0; margin:0; height:46px; letter-spacing:1.5px; }
  /* o convite é frase, não código: sem a fonte e a caixa do código digitado */
  .casa-codigo .nickname-input::placeholder{ letter-spacing:0; text-transform:none; font-family:inherit; font-size:14px; font-weight:600; }
  .casa-codigo-ir{
    flex:none; min-height:46px; padding:0 16px; border-radius:var(--radius-md); cursor:pointer;
    border:1px solid var(--edge); border-top-color:var(--edge-lit);
    background:linear-gradient(180deg, var(--surface-2-top), var(--surface-2-bot)); color:var(--text);
    font:inherit; font-size:13px; font-weight:800;
  }
  .casa-nota{ min-height:18px; margin:6px 0 0; font-size:12px; color:var(--gold-ink); }
  .casa-filtros{ display:grid; gap:8px; margin:10px -20px 0; }
  .casa-trilho{ display:flex; gap:8px; overflow-x:auto; padding:2px 20px; scrollbar-width:none; }
  .casa-trilho::-webkit-scrollbar{ display:none; }
  .casa-filtro{
    flex:none; display:inline-flex; align-items:center; gap:7px; min-height:38px; padding:0 13px;
    border-radius:var(--radius-pill); border:1px solid var(--edge); cursor:pointer; white-space:nowrap;
    background:linear-gradient(180deg, var(--surface-2-top), var(--surface-2-bot)); color:var(--text-soft);
    font:inherit; font-size:13px; font-weight:700;
  }
  .casa-filtro img{ width:22px; height:22px; border-radius:6px; object-fit:cover; flex:none; }
  .casa-filtro--tipo{ min-height:34px; padding:0 12px; font-size:12.5px; }
  .casa-filtro.selected{ border-color:var(--gold); color:var(--gold-ink); background:rgba(var(--gold-rgb), .1); }
  .casa-filtro:focus-visible, .casa-cartao:focus-visible{ outline:2px solid var(--gold); outline-offset:2px; }
  .casa-quando{ margin:10px 0 0; font-size:12px; color:var(--text-dim); text-align:center; }
  .casa-galeria{ margin-top:12px; }
  .casa-grupo{ display:flex; align-items:center; gap:8px; margin:18px 0 10px; font-size:14px; font-weight:800; color:var(--text); }
  .casa-grupo:first-child{ margin-top:4px; }
  .casa-grupo img{ width:24px; height:24px; border-radius:7px; object-fit:cover; }
  .casa-grade{ display:grid; grid-template-columns:repeat(2, minmax(0, 1fr)); gap:10px; }
  @media (min-width:560px){ .casa-grade{ grid-template-columns:repeat(3, minmax(0, 1fr)); } }
  .casa-cartao{
    position:relative; display:flex; flex-direction:column; min-width:0; padding:0; overflow:hidden; cursor:pointer; text-align:left;
    border-radius:var(--radius-md); border:1px solid var(--edge-strong);
    background:linear-gradient(180deg, var(--surface-2-top), var(--surface-2-bot)); box-shadow:var(--shadow-1);
    color:var(--text); font:inherit;
  }
  .casa-cartao:active{ transform:translateY(1px); }
  .casa-cartao.is-guardada{ border-color:var(--gold-dim); }
  .casa-cartao.is-busy{ opacity:.6; }
  .casa-vitrine{
    position:relative; display:block; height:104px; border-bottom:1px solid var(--edge-strong);
    background:radial-gradient(70% 90% at 50% -10%, var(--vitrine-luz), transparent 70%), linear-gradient(180deg, var(--vitrine-fundo-top), var(--vitrine-fundo) 30%);
  }
  .casa-capa{
    position:absolute; bottom:9px; left:50%; width:46px; aspect-ratio:2/3; border-radius:3px; overflow:hidden;
    background:var(--surface-3-bot); box-shadow:0 8px 12px -6px rgba(0,0,0,.8);
    transform-origin:50% 100%; transform:translateX(calc(-50% + var(--x, 0px))) rotate(var(--r, 0deg));
  }
  .casa-capa img{ display:block; width:100%; height:100%; object-fit:cover; }
  .casa-capa--1-1, .casa-capa--2-3{ --x:0px; --r:0deg; z-index:3; }
  .casa-capa--1-2{ --x:-20px; --r:-4deg; z-index:3; }
  .casa-capa--2-2{ --x:20px; --r:4deg; z-index:2; }
  .casa-capa--1-3{ --x:-34px; --r:-7deg; z-index:2; }
  .casa-capa--3-3{ --x:34px; --r:7deg; z-index:2; }
  .casa-logo{ position:absolute; top:8px; left:8px; z-index:4; width:24px; height:24px; border-radius:7px; object-fit:cover; box-shadow:0 2px 6px rgba(0,0,0,.5); }
  .casa-nome{
    display:-webkit-box; -webkit-box-orient:vertical; -webkit-line-clamp:2; line-clamp:2; overflow:hidden;
    margin:9px 10px 0; min-height:2.6em; font-size:13px; font-weight:800; line-height:1.3; color:var(--text);
  }
  /* quebra em vez de encostar na borda: "20 títulos  ✓ Guardada" não cabe em todo cartão */
  .casa-rodape{ display:flex; flex-wrap:wrap; align-items:center; justify-content:space-between; gap:2px 6px; margin:6px 10px 10px; }
  .casa-meta{ font-size:11.5px; color:var(--text-dim); white-space:nowrap; }
  .casa-estado{ display:inline-flex; align-items:center; gap:3px; font-size:12px; font-weight:800; color:var(--gold-ink); white-space:nowrap; }
  .casa-cartao.is-guardada .casa-estado{ color:var(--text-soft); }
  .casa-cartao--esqueleto{ cursor:default; pointer-events:none; }
  .casa-cartao--esqueleto .casa-nome{ height:2.6em; margin:9px 10px 12px; border-radius:6px; background:var(--surface-3-bot); }
  .casa-vazio{ display:flex; flex-direction:column; align-items:center; gap:8px; padding:24px 8px; text-align:center; color:var(--text-dim); font-size:13px; }
  @media (prefers-reduced-motion: no-preference){
    .casa-cartao--esqueleto .casa-vitrine, .casa-cartao--esqueleto .casa-nome{ animation:ready-pulsa 1.2s ease-in-out infinite; }
  }

  .cine-lists-group{ display:flex; flex-direction:column; gap:10px; }
  /* ---- JOGAR COM VÁRIAS / JUNTAR NUMA LISTA MINHA (1.68.0) ----
     Linhas de escolha com o cartaz da lista; marcada, a linha ganha aresta e o disco de ouro
     (estado: o ouro cheio fica com o botão que joga). */
  .cine-varias{
    display:flex; align-items:center; gap:12px; width:100%; margin-top:14px; padding:12px 14px; cursor:pointer; text-align:left;
    border-radius:var(--radius-md); border:1px solid var(--edge-gold);
    background:linear-gradient(180deg, var(--surface-2-top), var(--surface-2-bot)); color:var(--gold-ink); font:inherit;
  }
  .cine-varias span{ display:grid; gap:2px; }
  .cine-varias b{ font-size:14px; color:var(--text); }
  .cine-varias small{ font-size:12px; color:var(--text-dim); }
  .juntar-sub{ margin-top:4px; text-align:center; }
  .juntar-lista{ display:grid; gap:8px; margin-top:16px; }
  .juntar-linha{
    display:flex; align-items:center; gap:12px; width:100%; min-height:62px; padding:8px 12px 8px 8px; cursor:pointer; text-align:left;
    border-radius:var(--radius-md); border:1px solid var(--edge);
    background:linear-gradient(180deg, var(--surface-1-top), var(--surface-1-bot)); color:var(--text); font:inherit;
  }
  .juntar-linha:focus-visible, .juntar-opcao:focus-visible, .cine-varias:focus-visible{ outline:2px solid var(--gold); outline-offset:2px; }
  .juntar-linha.is-on{ border-color:var(--gold-dim); background:rgba(var(--gold-rgb), .07); }
  .juntar-capa{ position:relative; flex:none; width:32px; aspect-ratio:2/3; border-radius:3px; overflow:hidden; display:grid; place-items:center;
    background:var(--surface-3-bot); color:var(--text-dim); }
  .juntar-capa img{ width:100%; height:100%; object-fit:cover; }
  .juntar-copia{ flex:1; min-width:0; display:grid; gap:2px; }
  .juntar-copia b{ font-size:14px; font-weight:700; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
  .juntar-copia span{ font-size:12px; color:var(--text-dim); }
  .juntar-marca{ flex:none; width:24px; height:24px; border-radius:50%; display:grid; place-items:center;
    border:1.5px solid var(--edge-strong); color:transparent; }
  .juntar-linha.is-on .juntar-marca{ border-color:var(--gold); background:var(--gold); color:var(--on-gold); }
  .juntar-seta{ flex:none; color:var(--gold-ink); display:flex; }
  .juntar-linha--nova .juntar-capa{ border:1px dashed var(--edge-strong); background:transparent; color:var(--gold-ink); }
  .juntar-opcao{ display:flex; align-items:center; gap:10px; width:100%; min-height:44px; margin-top:12px; padding:0 4px; cursor:pointer;
    background:none; border:0; color:var(--text-soft); font:inherit; font-size:13px; font-weight:600; text-align:left; }
  .juntar-caixa{ flex:none; width:22px; height:22px; border-radius:6px; display:grid; place-items:center; border:1.5px solid var(--edge-strong); color:transparent; }
  .juntar-opcao.is-on .juntar-caixa{ border-color:var(--gold); background:var(--gold); color:var(--on-gold); }
  .juntar-ir{ position:static; width:100%; margin-top:12px; }

  /* ---- a folha de ações do "⋮" ---- */
  .list-menu-head{ padding:0 2px 6px; }
  .list-menu-items{ display:flex; flex-direction:column; }
  .list-menu-item{
    display:flex; align-items:center; gap:13px; width:100%;
    padding:13px 2px; text-align:left;
    border:0; border-bottom:1px solid var(--rule); background:none;
    color:var(--text); font-family:inherit; cursor:pointer;
  }
  .list-menu-item:last-child{ border-bottom:0; }
  .rank-genero-item{ justify-content:space-between; min-height:48px; }
  .rank-genero-item b{ font-size:var(--t-body-size); font-weight:700; }
  .rank-genero-item small{ flex:none; font-size:var(--t-support-size); color:var(--text-dim); font-variant-numeric:tabular-nums; }
  .rank-genero-item[aria-pressed="true"] b{ color:var(--gold-ink); }
  .rank-genero-item[aria-pressed="true"] small{ color:var(--gold-ink); }
  .list-menu-ico{ flex:none; display:flex; color:var(--gold-dim); }
  .list-menu-copy{ min-width:0; }
  .list-menu-copy b{ display:block; font-size:14px; font-weight:700; line-height:1.3; }
  .list-menu-copy span{ display:block; margin-top:2px; font-size:12px; color:var(--text-soft); line-height:1.4; }
  /* a ação de maior sucesso ganha a tinta da marca no glifo — um item de folha não vira
     bloco dourado, senão a folha inteira passa a ter dois primários */
  .list-menu-item--destaque .list-menu-ico{ color:var(--gold-ink); }
  .list-menu-item--destaque .list-menu-copy b{ color:var(--gold-ink); }
  /* e a destrutiva fecha a folha, em rosa, separada do resto pelo próprio fiapo da lista */
  /* o rosa de PREENCHER (`--rose`) escrito em 14px dava 3,5:1 ("Tirar do sofá", medido pela
     auditoria de 2026-09-29); a tinta de texto do erro é `--on-error`, que troca com o tema */
  .list-menu-item--perigo .list-menu-ico,
  .list-menu-item--perigo .list-menu-copy b{ color:var(--on-error); }
  .join-erro{ min-height:1.35em; margin:10px 0 0; text-align:center; font-size:var(--t-support-size); font-weight:700; color:var(--on-error); }
  /* Estado "já está nesta lista" no seletor. Ouro na ARESTA do glifo e na tinta, nunca
     preenchido: o ouro cheio é do match, e emprestá-lo pra "guardado" faria dois momentos
     muito diferentes falarem com a mesma voz — a mesma regra que a pastilha do card segue. */
  .list-pick.is-in .list-menu-ico{ color:var(--gold-ink); }
  .list-pick.is-in .list-menu-copy b{ color:var(--gold-ink); }

  /* ---- A PILHA DE CAMADAS DAS LISTAS ----
     A regra é a mesma do `#detailOverlay`: quem NASCE de dentro de outro painel precisa
     ficar por cima de quem o abriu, senão abre atrás e a tela não muda nada.
       120  painéis de tela cheia, `#listOverlay` ("Ver os títulos") entre eles
       130  a folha do "⋮" — sai da estante, cobre a estante
       140  `#detailOverlay`, o detalhe de um título: ele nasce de dentro do `#listOverlay`
            (tocar num título da lista) e por isso precisa estar ACIMA dele
       160  o seletor "Salvar na lista": nasce de dentro do detalhe
       170  criar/renomear: nasce de dentro do seletor E de dentro da folha do "⋮"
     Acima disso só o clímax do match (200) e as confirmações (1000). */
  #listMenuOverlay{ z-index:130; }
  #saveListOverlay{ z-index:160; }
  #listNamePanelOverlay{ z-index:170; }
  /* as folhas do retrato entram na MESMA escada, sem empatar com ninguém: "Quem aparece
     aqui" sai da aba (132), a comparação de um recorte sai da aba também mas por outra
     porta (134 — as duas nunca coexistem, e o desempate escrito vale mais que a ordem do
     DOM) e "Juntar com quem" nasce de dentro da primeira (172) */
  #tasteManageOverlay{ z-index:132; }
  #tasteCompareOverlay{ z-index:134; }
  #tasteMergeOverlay{ z-index:172; }
  /* a cor nasce de DENTRO das duas portas (da legenda da comparação e da linha do
     Gerenciar), então fica acima das duas — e acima do "Juntar", que sai da mesma folha */
  #tasteColorOverlay{ z-index:176; }
  /* A PRÉVIA DO COMPARTILHAMENTO NASCE DE QUALQUER LUGAR — do fim de rodada, do painel do
     sorteio e, desde a 1.41, do detalhe de uma partida no histórico. Herdando o 120 de
     `.detail-overlay` ela ficava ATRÁS do `#detailOverlay` (140): o botão abria uma tela
     que ninguém via, com tamanho e display corretos. Medir a caixa não mostra isso; quem
     mostrou foi `elementFromPoint`, que devolvia o pôster da partida no meio da prévia.
     180 é o degrau acima de tudo o que pode estar aberto por baixo dela, e abaixo do
     clímax do match (200), que nunca coexiste com uma prévia. */
  #shareOverlay{ z-index:180; }
  /* a escolha nasce da tela de RESULTADO, que não é um overlay: 136 a põe acima das folhas
     do retrato (132/134) sem empatar com nenhuma, e abaixo de tudo que possa nascer dela */
  #choosePickOverlay{ z-index:136; }

  /* ---- por que a subida destas folhas é ANIMAÇÃO e não transição ----
     A folha dos Ajustes é um elemento fixo no HTML: ela já está pintada em
     `translateY(100%)` quando a classe `show` chega, então o navegador tem os dois lados da
     transição e ela anima. Estas nascem por `appendChild`, e um elemento recém-criado não
     tem estado anterior — a transição só existiria se a classe fosse aplicada num quadro
     DEPOIS da inserção, o que obriga a um `requestAnimationFrame`.
     E rAF não é garantido: numa aba em segundo plano, num navegador que economiza bateria
     ou numa janela que não está compondo quadros, o callback simplesmente não roda. Quando
     ele não roda, a folha fica sem `show` — ou seja, em `display:none` — e o toque abre uma
     tela que ninguém vê. Foi exatamente o que aconteceu aqui na verificação.
     Com `animation`, quem dispara é o próprio navegador ao inserir o elemento. Não há
     quadro intermediário pra perder, e a folha existe visível desde o primeiro. */
  #listMenuOverlay .modal-sheet,
  #saveListOverlay .modal-sheet,
  #tasteManageOverlay .modal-sheet,
  #tasteCompareOverlay .modal-sheet,
  #tasteColorOverlay .modal-sheet,
  #choosePickOverlay .modal-sheet,
  #lembreteOverlay .modal-sheet,
  #rankGeneroOverlay .modal-sheet,
  #tasteMergeOverlay .modal-sheet{ transform:none; transition:none; }
  /* A folha do meio não pode DESLIZAR DE BAIXO: ela não vem de lugar nenhum, está no
     centro. Subir da borda a faria atravessar a tela inteira para parar no meio, que lê
     como engano. Ela cresce no lugar — e, como as outras, por `animation`, porque um
     elemento recém-criado não tem estado anterior para uma transição. */
  @media (prefers-reduced-motion: no-preference){
    #choosePickOverlay .modal-sheet{ animation: folha-cresce .22s var(--ease-emphasis); }
    #choosePickOverlay .modal-sheet.is-out{ animation: folha-encolhe .16s var(--ease-standard) both; }
    @keyframes folha-cresce{ from{ transform:scale(.94); opacity:0; } }
    @keyframes folha-encolhe{ to{ transform:scale(.96); opacity:0; } }
  }
  @media (prefers-reduced-motion: no-preference){
    /* SEM `both` na entrada, e isto é a metade que faltava do mesmo raciocínio: `both`
       segura o estado inicial (`translateY(100%)`, fora da tela) até a animação começar.
       Onde as animações não avançam — aba em segundo plano, janela que não compõe quadros,
       economia de bateria — ela nunca começa, e a folha fica presa embaixo da tela pra
       sempre. Foi o que a verificação mediu: topo em y=720 numa tela de 720.
       Sem fill, o estado de repouso é o normal (no lugar); a animação só tira a folha dali
       quando de fato tem como rodar. O modo de falhar passa a ser "aparece sem deslizar". */
    #listMenuOverlay .modal-sheet,
    #saveListOverlay .modal-sheet,
    #tasteManageOverlay .modal-sheet,
    #tasteCompareOverlay .modal-sheet,
    #tasteColorOverlay .modal-sheet,
    #lembreteOverlay .modal-sheet,
    #sofaPessoaOverlay .modal-sheet,
    #rankGeneroOverlay .modal-sheet,
    #tasteMergeOverlay .modal-sheet{ animation: folha-sobe var(--dur-3) var(--ease-emphasis); }
    #listMenuOverlay .modal-sheet.is-out,
    #saveListOverlay .modal-sheet.is-out,
    #tasteManageOverlay .modal-sheet.is-out,
    #tasteCompareOverlay .modal-sheet.is-out,
    #tasteColorOverlay .modal-sheet.is-out,
    #lembreteOverlay .modal-sheet.is-out,
    #sofaPessoaOverlay .modal-sheet.is-out,
    #rankGeneroOverlay .modal-sheet.is-out,
    #tasteMergeOverlay .modal-sheet.is-out{ animation: folha-desce .18s var(--ease-standard) both; }
    @keyframes folha-sobe{ from{ transform:translateY(100%); } }
    @keyframes folha-desce{ to{ transform:translateY(100%); } }
  }

  /* ---- o corpo de uma lista: as MESMAS linhas da estante, um degrau adentro ----
     O comentário que ficava aqui dizia "a mesma linha do histórico". A intenção estava
     certa; a implementação nunca chegou lá. O que havia era um CARTÃO por título — moldura,
     raio e fundo próprios — e, dentro dele, um `<button>` SEM RESET: o reset global do app
     só herda `font-family` para botões, então aquele vinha com `border:2px outset black`
     sobre `rgb(240,240,240)`, o padrão cru do navegador. Num app quase preto isso é uma
     caixa cinza-claro em relevo por título — três molduras aninhadas por linha, a mais
     interna desenhada por outro sistema operacional.
     Agora a linha é a linha: contêiner, fiapo entre uma e outra, botão esticado por cima e
     a MESMA escada de z-index que a estante e o histórico já usam — conteúdo ≤2 < abrir 3
     < remover 4. O cartaz fica em 44px, igual ao da estante de listas, para que as duas
     telas compartilhem o mesmo trilho à esquerda: saindo de uma e entrando na outra, o
     texto continua começando no mesmo x. */
  .list-items{ list-style:none; margin:16px 0 0; padding:0; display:flex; flex-direction:column; }
  .list-item{
    position:relative;
    display:flex; align-items:center; gap:11px;
    padding:9px 0; border-bottom:1px solid var(--rule);
    transition: background-color var(--dur-1) var(--ease-standard);
  }
  .list-item:last-child{ border-bottom:0; }
  .list-item:hover{ background:var(--sheen-1); }
  .list-item .rank-thumb{ flex:none; position:relative; z-index:2; --pf-radius:var(--radius-sm); }
  .list-item-copy{ flex:1; min-width:0; }
  .list-item-copy b{
    display:block; font-size:14px; line-height:1.3; color:var(--text);
    overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
  }
  /* duas linhas, e não reticência: o streaming é o último pedaço da linha e o mais útil
     dela, e era justamente ele que o ✕ cortava */
  .list-item-copy span{
    margin-top:2px; font-size:var(--t-support-size); color:var(--text-soft);
    display:-webkit-box; -webkit-line-clamp:2; -webkit-box-orient:vertical; overflow:hidden;
  }
  .list-item-open{
    position:absolute; inset:0; z-index:3;
    background:none; border:0; padding:0; margin:0;
    cursor:pointer; border-radius:var(--radius-xs);
  }
  .list-item-open:focus-visible{ outline-offset:3px; }
  /* Sem moldura: emoldurado, o ✕ tinha o mesmo peso visual da linha inteira e a tela lia
     como uma fileira de botões de apagar com um filme ao lado de cada um. Ele é a ação
     rara; a linha é o conteúdo. O disco rosa só aparece sob o dedo. */
  .list-item-drop{
    position:relative; z-index:4; flex:none;
    width:44px; height:44px; display:flex; align-items:center; justify-content:center;
    border:0; border-radius:50%;
    background:none; color:var(--text-dim); cursor:pointer; font-family:inherit;
    transition: color var(--dur-1) var(--ease-standard), background-color var(--dur-1) var(--ease-standard);
  }
  .list-item-drop:hover{ color:var(--rose); background:rgba(var(--rose-rgb), .12); }
  /* a ação da LISTA fecha o painel, embaixo dos títulos: ela é sobre a prateleira, não
     sobre nenhum dos filmes, e no topo ela chegava antes do conteúdo que a pessoa veio ver */
  .list-panel-foot{ margin-top:18px; }
  .list-panel-foot .result-btn{ width:100%; }

  /* ================= A BUSCA DE TÍTULOS =================
     Até aqui uma lista só engordava por dentro de uma RODADA: a pessoa jogava, curtia, e o
     título caía na prateleira. Quem lembrou do filme no ônibus não tinha porta nenhuma —
     precisava montar uma partida inteira e torcer pro baralho sortear justamente aquele.
     Esta é a porta que faltava, e ela vive nos dois lugares onde a intenção nasce: no topo
     da estante (guardar sem saber ainda em qual prateleira) e dentro de uma lista aberta
     (guardar NAQUELA, que é o caso em que o alvo já está decidido).

     A ANATOMIA É A DA ESTANTE, de propósito: cartaz de 44px, nome, linha de metadados. Sair
     da lista e entrar na busca não pode parecer trocar de aplicativo — o trilho à esquerda
     continua no mesmo x, e o que muda é só o botão da direita (✕ tira, + guarda). */

  /* ---- a porta: um campo que não é campo ----
     Ele PARECE a caixa de busca porque é isso que promete, mas é um botão. Um <input> de
     verdade aqui abriria o teclado do sistema dentro de um painel que rola, empurrando a
     estante pra fora da tela pra digitar num campo que ainda não tem onde mostrar resposta.
     O toque leva pra superfície que foi desenhada pra isso, e lá o campo é real. */
  .find-open{
    display:flex; align-items:center; gap:10px; width:100%;
    min-height:46px; padding:0 14px; margin-bottom:16px;
    border:1px solid var(--edge); border-top-color:var(--edge-lit);
    border-radius:var(--radius-sm);
    background: linear-gradient(180deg, var(--surface-2-top), var(--surface-2-bot));
    box-shadow: var(--shadow-1), inset 0 1px 0 var(--sheen-1);
    color:var(--text-dim); font-family:inherit; font-size:13.5px; text-align:left;
    cursor:pointer;
    transition: color var(--dur-1) var(--ease-standard), border-color var(--dur-1) var(--ease-standard);
  }
  .find-open .ico-svg{ flex:none; color:var(--gold-dim); }
  .find-open:hover{ color:var(--text); border-color:var(--edge-gold); }
  /* dentro de uma lista aberta a porta é a mesma peça, só que anunciando o alvo */
  .find-open--dentro{ margin:16px 0 0; }

  /* ---- a superfície da busca ----
     Um degrau acima da estante (120) e do "⋮" (130), um degrau abaixo do detalhe (140):
     ela nasce de dentro da estante e o detalhe nasce de dentro dela. A folha "Salvar na
     lista" (160) continua por cima de tudo isso, que é onde ela precisa estar quando a
     pessoa tem mais de uma prateleira. */
  #findOverlay{ z-index:135; }
  /* mais alta que os outros diálogos porque é a única que existe pra ROLAR uma fileira:
     86dvh cortava a lista em quatro linhas e meia num aparelho comum */
  #findOverlay .detail-overlay-panel{
    max-height:92dvh;
    background: linear-gradient(180deg, var(--surface-1-top), var(--surface-1-bot));
    border:1px solid var(--edge); border-top-color:var(--edge-lit);
    box-shadow: var(--shadow-3), inset 0 1px 0 var(--sheen-1);
  }
  /* UM filho não-✕, porque é ele que a regra geral de `.detail-overlay-panel` transforma em
     scroller. Cabeçalho e corpo como dois irmãos fariam os DOIS rolarem, e o campo de busca
     sumiria da tela na primeira descida — justo a peça que precisa ficar. */
  .find-shell{ display:flex; flex-direction:column; }
  .find-head{
    /* 5 e não 3: o cabeçalho é a peça mais alta da superfície por definição — nada que
       role pode passar por cima do campo onde se digita. O número fica acima do topo da
       escada da linha (4) de propósito, para que o dia em que alguém tirar o `isolation`
       da linha volte a ser um defeito visível e não um apagão silencioso. */
    position:sticky; top:0; z-index:5;
    padding:16px 16px 12px;
    /* opaco e não translúcido: o que passa por baixo dele é uma fileira de cartazes, e
       vidro aqui deixaria a arte atravessar o campo de digitação */
    background:var(--surface-1-top);
    border-bottom:1px solid var(--rule);
    border-radius:20px 20px 0 0;
  }
  /* a faixa do ✕ é reservada pelo padding-right do título, não por 56px de topo vazio:
     numa tela que já é alta, um cabeçalho em branco custaria uma linha de resultado */
  .find-head .modal-title{ text-align:left; padding-right:46px; margin:0; }
  .find-head .find-sub{
    margin:3px 0 0; padding-right:46px;
    font-size:var(--t-support-size); line-height:var(--t-support-lh); color:var(--text-dim);
  }
  .find-head .find-sub b{ color:var(--gold-ink); font-weight:700; }

  .find-field{ position:relative; display:flex; align-items:center; margin-top:12px; }
  .find-field > .ico-svg{
    position:absolute; left:12px; color:var(--text-dim); pointer-events:none;
    transition: color var(--dur-1) var(--ease-standard);
  }
  .find-field:focus-within > .ico-svg{ color:var(--gold-ink); }
  .find-input{
    width:100%; min-height:46px; padding:0 46px 0 38px;
    border:1px solid var(--edge); border-top-color:var(--edge-lit);
    border-radius:var(--radius-sm);
    background:var(--bg); color:var(--text);
    font-family:inherit; font-size:15px; /* 15px+ é o que impede o iOS de dar zoom no foco */
    -webkit-appearance:none; appearance:none;
  }
  .find-input::placeholder{ color:var(--text-dim); }
  .find-input::-webkit-search-cancel-button{ -webkit-appearance:none; appearance:none; }
  .find-input:focus{ outline:none; border-color:var(--edge-gold); }
  .find-field:focus-within{ border-radius:var(--radius-sm); }
  /* o ✕ do campo ocupa a caixa de toque inteira mas só desenha o glifo: emoldurado, ele
     competiria com o próprio campo que limpa */
  .find-clear{
    position:absolute; right:1px;
    width:44px; height:44px; display:flex; align-items:center; justify-content:center;
    border:0; border-radius:50%; background:none; color:var(--text-dim);
    cursor:pointer; font-family:inherit;
  }
  .find-clear:hover{ color:var(--text); }

  .find-body{ padding:14px 16px 20px; }
  /* o rótulo de uma fileira. O mesmo desenho de `.section-label` (ouro tinta, caixa alta),
     porque é o mesmo papel — e a fileira DISCRETA o rebaixa para cinza, que é como ela
     anuncia "isto aqui é oferta, não resposta". */
  .find-group + .find-group{ margin-top:22px; padding-top:18px; border-top:1px solid var(--rule); }
  .find-group-label{
    margin:0 0 2px;
    font-size:var(--t-label-size); line-height:var(--t-label-lh);
    letter-spacing:var(--t-label-track); font-weight:var(--t-label-weight);
    text-transform:uppercase; color:var(--gold-ink);
  }
  .find-group-note{ margin:0 0 8px; font-size:var(--t-support-size); color:var(--text-dim); }
  /* ---- A FILEIRA DISCRETA ----
     "Parecidos" e "Em alta" NÃO são o que a pessoa pediu — são o que oferecemos. Se
     tivessem o mesmo peso dos resultados, a tela passaria a responder uma pergunta que
     ninguém fez, e o título procurado disputaria atenção com oito que não foram procurados.
     A distinção é de MATÉRIA, não de tamanho de fonte: cartaz menor, nome em tinta de apoio
     e rótulo em cinza. Ela continua inteiramente tocável e legível — só não reivindica. */
  .find-group--eco .find-group-label{ color:var(--text-dim); }

  .find-rows{ list-style:none; margin:0; padding:0; display:flex; flex-direction:column; }
  .find-row{
    position:relative;
    /* ---- A ESCADA DA LINHA É LOCAL, E ISTO É O QUE A PRENDE ----
       `position:relative` SEM `z-index` não cria contexto de empilhamento: os filhos com
       2/3/4 sobem para o contexto do painel e passam a competir com o cabeçalho grudado
       (que está em 5). O dono fotografou o resultado — os discos de "+" desenhados POR
       CIMA do título e do campo de busca ao rolar a fileira.
       A escada 2 < 3 < 4 é uma conversa DENTRO da linha (cartaz < abrir < guardar), e
       nunca teve nada a dizer sobre o cabeçalho. `isolation` declara exatamente isso: a
       linha vira uma unidade opaca ao empilhamento de fora, a escada continua valendo por
       dentro, e quem conversa com o cabeçalho é a linha inteira, no nível dela.
       A estante e o painel de uma lista usam a mesma escada e nunca mostraram o defeito
       porque nenhuma das duas tem peça grudada por cima — a fileira ali rola sob nada. */
    isolation:isolate;
    display:flex; align-items:center; gap:11px;
    padding:9px 0; border-bottom:1px solid var(--rule);
    transition: background-color var(--dur-1) var(--ease-standard);
  }
  .find-row:last-child{ border-bottom:0; }
  .find-row:hover{ background:var(--sheen-1); }
  /* a MESMA escada da estante e do painel de uma lista: conteúdo ≤2 < abrir 3 < guardar 4.
     Sem ela o botão esticado engole o "+", e guardar vira abrir o detalhe. */
  .find-row .rank-thumb{ flex:none; position:relative; z-index:2; width:44px; --pf-radius:var(--radius-sm); }
  .find-row-copy{ flex:1; min-width:0; }
  .find-row-copy b{
    display:block; font-size:14px; line-height:1.3; color:var(--text);
    overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
  }
  .find-row-copy span{
    display:block; margin-top:2px; font-size:var(--t-support-size); color:var(--text-soft);
    overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
  }
  .find-row-open{
    position:absolute; inset:0; z-index:3;
    background:none; border:0; padding:0; margin:0;
    cursor:pointer; border-radius:var(--radius-xs);
  }
  .find-row-open:focus-visible{ outline-offset:3px; }
  .find-add{
    position:relative; z-index:4; flex:none;
    width:44px; height:44px; display:flex; align-items:center; justify-content:center;
    border:1px solid var(--edge); border-radius:50%;
    background:none; color:var(--text-dim); cursor:pointer; font-family:inherit;
    transition: color var(--dur-1) var(--ease-standard), border-color var(--dur-1) var(--ease-standard),
                background-color var(--dur-1) var(--ease-standard);
  }
  .find-add:hover{ color:var(--gold-ink); border-color:var(--edge-gold); }
  /* guardado é OURO TINTA, não ouro cheio: o disco reporta um estado, não é a ação
     primária da tela. Metal aqui poria oito peças em vigor ao mesmo tempo numa fileira. */
  .find-add.is-in{
    color:var(--gold-ink); border-color:var(--edge-gold);
    background:rgba(var(--gold-rgb), .12);
  }
  /* o pulso do instante em que entra: 200ms, uma vez, e some. É a confirmação que o dedo
     sente antes de o toast chegar. */
  .find-add.is-in.just-in{ animation: find-pop var(--dur-2) var(--ease-emphasis); }
  @keyframes find-pop{ 40%{ transform:scale(1.18); } }
  @media (prefers-reduced-motion: reduce){
    .find-add.is-in.just-in{ animation:none; }
  }

  .find-row--eco{ padding:7px 0; }
  .find-row--eco .rank-thumb{ width:36px; }
  .find-row--eco .find-row-copy b{ font-size:13px; color:var(--text-soft); font-weight:600; }

  /* ---- os estados que não são fileira ---- */
  .find-msg{ padding:26px 4px; text-align:center; }
  .find-msg .spot{ color:var(--gold-dim); }
  .find-msg b{
    display:block; margin-top:12px;
    font-size:var(--t-group-size); line-height:var(--t-group-lh); color:var(--text);
  }
  .find-msg p{ margin:6px auto 0; max-width:30ch; font-size:var(--t-support-size); line-height:1.5; color:var(--text-dim); }
  .find-msg .result-btn{ margin-top:16px; }
  /* o esqueleto tem a MEDIDA da linha que vai substituir: um spinner centralizado faria a
     fileira saltar de altura na chegada da resposta, e a tela inteira dançaria a cada letra */
  .find-skel{ display:flex; align-items:center; gap:11px; padding:9px 0; border-bottom:1px solid var(--rule); }
  .find-skel i{ display:block; border-radius:var(--radius-sm); background:var(--surface-2-top); }
  .find-skel i:first-child{ flex:none; width:44px; height:66px; }
  .find-skel span{ flex:1; display:block; }
  .find-skel span i:nth-child(1){ height:11px; width:62%; }
  .find-skel span i:nth-child(2){ height:9px; width:38%; margin-top:8px; background:var(--surface-1-top); }
  .find-skel{ animation: find-breathe 1.4s var(--ease-standard) infinite; }
  @keyframes find-breathe{ 50%{ opacity:.45; } }
  @media (prefers-reduced-motion: reduce){
    .find-skel{ animation:none; opacity:.6; }
  }

  /* o código de importação usa o mesmo desenho do código de sala: cinco caixas de letra */
  .list-code{
    font-family:ui-monospace, SFMono-Regular, Menlo, monospace;
    font-size:20px; font-weight:700; letter-spacing:6px; text-align:center;
    color:var(--gold-ink); text-transform:uppercase;
  }

  /* O RESPIRO DO PÉ COBRE A BARRA **E O VÉU**, e é conta, não gosto: a navegação mede
     `--dock-space` (64 + área segura) e a dissolução por cima dela mede `--tabbar-veil`
     (54). Os 104px fixos de antes cobriam a barra e MERGULHAVAM 23px dentro da rampa — o
     dono viu a última frase de Listas e de Meu gosto por baixo do borrão. Enquanto cada
     painel pagava um segundo padding de 104, a soma escondia o defeito; corrigir a soma o
     revelou. Agora os dois tokens entram na conta e sobram 12px de folga acima da rampa. */
  #history{
    overflow-y:auto; scrollbar-width:none; -ms-overflow-style:none;
    padding-bottom: calc(var(--dock-space) + var(--tabbar-veil) + 12px);
  }
  #history::-webkit-scrollbar{ display:none; }
  .history-shell{ width:100%; max-width:1120px; margin:0 auto; }
  /* a linha de resumo é também a contagem do recorte: uma linha só, sempre. */
  .history-insight{
    max-width:48ch; margin-top:6px; color:var(--text-dim);
    font-size:var(--t-support-size); line-height:var(--t-support-lh);
    font-variant-numeric: tabular-nums;
  }
  /* dado em romano bold e branco contra prosa apagada — o mesmo par que os
     rótulos em versalete usam na estante. O ouro do app continua onde carrega
     significado; no meio de um parágrafo ele era ornamento. */
  .history-insight b{ color:var(--text); font-weight:700; }
  .history-layout{ display:flex; flex-direction:column; }
  .history-search-wrap{ position:relative; flex:1; min-width:0; }
  .history-search-wrap svg{
    position:absolute; left:14px; top:50%; transform:translateY(-50%);
    color:var(--text-dim); pointer-events:none;
  }
  /* a busca é um campo de texto: herda a primitiva .field. O filtro de modo e o
     de período deixaram de ser <select> do sistema e usam o .cselect do app. */
  .history-search{ padding-left:42px; font-size:var(--t-body-size); }
  .history-control{ width:100%; }
  .history-foot{ margin-top:26px; padding-top:14px; border-top:1px solid var(--rule); }
  .history-manage-btn{
    flex:none; min-height:44px; padding:0 12px; gap:7px;
    font-size:var(--t-support-size); font-weight:600; color:var(--text-dim);
  }
  .history-manage-btn:hover{ color:var(--text); }
  .history-manage-btn[aria-pressed="true"]{
    color:var(--gold-ink); border-color:rgba(var(--gold-ink-rgb),.34);
    background:linear-gradient(180deg, var(--gold-tint-bg), var(--gold-tint-deep));
  }
  .history-content{ min-width:0; }
  /* ---- remover é exceção ----
     Antes o botão aparecia em TODA linha, no caminho do polegar, com peso
     permanente igual ao da própria partida — dezoito convites para apagar numa
     tela cuja tarefa é lembrar. Agora ele só existe sob intenção declarada
     ("Gerenciar noites"), e a confirmação continua sendo o desfazer do toast.
     `display:none` também tira o alvo da conta de acessibilidade e do foco. */
  .history-content:not(.is-managing) .history-remove-btn{ display:none; }
  /* marca do match: dois corações que viram um */
  .match-mark{ vertical-align:-.16em; margin-right:.34em; color:var(--gold-ink); overflow:visible; }
  /* cabeçalho do detalhe da partida */
  .sess-head{ text-align:center; margin-bottom:18px; }
  .sess-title{
    font-family:var(--font-display); font-size:30px; letter-spacing:1.2px;
    color:var(--gold-ink); line-height:1;
  }
  /* metadados como fichas separadas em vez de uma frase cinza corrida: cada um é um fato
     diferente (onde, quando, com quem) e antes vinham colados numa linha só.
     Sem caixa alta: é metadado, o mesmo papel `num` das linhas da lista. */
  .sess-meta{
    display:flex; flex-wrap:wrap; justify-content:center; align-items:center;
    gap:6px 14px; margin-top:10px;
    font-size:var(--t-support-size); letter-spacing:var(--t-num-track);
    font-variant-numeric: tabular-nums; color:var(--text-dim);
  }
  .sess-meta span{ display:inline-flex; align-items:center; gap:5px; }
  .sess-meta span + span{ position:relative; }
  .sess-meta .sess-who{ flex-basis:100%; justify-content:center; }
  .sess-meta .sess-who::before{ display:none; }
  .sess-meta span + span::before{
    content:''; position:absolute; left:-8px; top:50%; transform:translateY(-50%);
    width:3px; height:3px; border-radius:50%; background:var(--line);
  }
  /* o painel agora tem material próprio, então o conteúdo da partida ganha a
     margem interna dele em vez de encostar nas bordas */
  #detailOverlayContent.is-session-detail{ padding:var(--detalhe-topo) 16px 20px; }
  .sess-detail-layout{ padding:0; }
  /* ---- o palco é uma ESTANTE, não um monte ----
     Os cartazes vinham em tamanhos diferentes e com bases desalinhadas (cada um
     com sua rotação e seu deslocamento vertical): três objetos jogados, e o olho
     lia descuido. Agora vale uma regra só e ela é visível — mesma largura, mesma
     base, sobreposição constante. É a mesma estante das molduras vazias do
     estado sem partida, com a mesma linha de chão. */
  /* ---- rodada 6: a junta acaba porque a sobreposição acaba ----
     A comparação cega ampliou a junta a 5x e achou "o pior encontro de arestas
     das doze telas". A causa era geométrica, não de acabamento: o cartaz de
     trás entrava 26px POR BAIXO do da frente, então os dois cantos
     arredondados da esquerda dele ficavam escondidos e o que sobrava à vista
     era uma aresta reta encostada num canto redondo. Nenhum ajuste de sombra
     ou de raio conserta isso — só a sobreposição some.
     A mesma sobreposição era a segunda metade do problema: para caber, o par
     media 158px num palco de 318 (48%), com dois vazios laterais grandes.
     A estante não precisa de leque para dizer "foi mais de um" — ela já tem a
     linha de chão, e os cartões ao lado já enumeram os títulos. Os cartazes
     passam a dividir a largura INTEIRA do palco com uma folga entre eles: cada
     peça mostra as quatro quinas, todas têm a mesma altura por construção
     (mesma largura + `aspect-ratio` da moldura) e não há mais junta nenhuma
     para acertar. A largura vem do palco, não de um px por viewport: uma
     regra só em todo o shell.
     (O leque de sobreposição CONTINUA na lista, onde a faixa é estreita e o
     empilhamento é a única forma de dizer "dois" sem contador.) */
  .sess-art-stage{
    --sess-gap:10px;
    --sess-cols:2;
    --sess-shelf-w: calc((100% - (var(--sess-cols) - 1) * var(--sess-gap)) / var(--sess-cols));
    position:relative; min-height:178px; margin:0 0 16px;
    display:flex; align-items:flex-end; justify-content:center; gap:var(--sess-gap);
    padding:20px 20px 20px;
    overflow:hidden; border-radius:var(--radius-lg);
    border:1px solid var(--edge); border-top-color:var(--edge-lit);
    background:var(--panel-deep); isolation:isolate;
    box-shadow: inset 0 1px 0 var(--sheen-1);
  }
  .sess-art-stage::before{
    content:''; position:absolute; inset:0; z-index:-1;
    background:linear-gradient(140deg,rgba(var(--gold-rgb),.09),transparent 55%);
  }
  /* a linha de chão: é ela que torna a base comum um FATO visível */
  .sess-art-stage::after{
    content:''; position:absolute; left:10px; right:10px; bottom:15px;
    height:1px; pointer-events:none;
    background:linear-gradient(90deg, transparent, var(--rule-strong) 18%, var(--rule-strong) 82%, transparent);
  }
  /* três cartazes na estante: a folga é a mesma, a largura se reparte por três */
  .sess-art-stage:has(.sess-art-poster--3){ --sess-cols:3; }
  .sess-art-poster{
    width:var(--sess-shelf-w); --pf-radius: var(--radius-md);
    align-items:flex-end; color:var(--text); flex:none;
    box-shadow: inset 0 0 0 1px var(--sheen-3), var(--shadow-2);
  }
  .sess-art-poster img{ position:absolute; inset:0; width:100%; height:100%; object-fit:cover; }
  /* ================= O CARROSSEL DA PARTIDA =================
     O palco parado virou peça que ACOMPANHA a leitura. `sticky` no topo da rolagem: ele
     fica à vista enquanto os cartões passam, então a arte da partida nunca sai da tela
     junto com o cabeçalho. Para grudar, ele teve que sair de dentro do cabeçalho no DOM —
     ali dentro só teria a altura do próprio cabeçalho para percorrer.
     A distribuição deixou de ser flex e passou a ser POSIÇÃO: cada cartaz pousa em
     `--off × --sess-passo` a partir do centro, e é uma variável só (o índice da vez) que
     move o conjunto. Com flex, "centralizar o terceiro de cinco" exigiria recalcular
     larguras a cada troca; com deslocamento, é uma conta por peça e o navegador interpola
     o resto. O `overflow:hidden` que o palco já tinha é o que faz os de fora sumirem sem
     precisar escondê-los um a um. */
  /* O PALCO PARA NO TOPO DA ROLAGEM, e não 56px abaixo dele.
     `top:0` num contêiner que rola gruda no topo do PADDING BOX, e este tem 56px de faixa
     reservada ao ✕ — sobrava uma fresta dessa altura entre a borda de cima e o palco, e
     era por ela que as descrições já lidas passavam aparecendo. O deslocamento negativo
     cancela exatamente a faixa: o palco encosta na borda e nada passa por cima dele.
     Ele NÃO cobre o ✕: o botão é a peça mais alta do painel por definição (ver o z-index
     dele logo abaixo), e passa a pousar sobre o palco — que é onde o dono o quis. */
  .sess-art-stage--carrossel{
    --sess-passo:74px;
    position:sticky; top:calc(-1 * var(--detalhe-topo)); z-index:3;
    display:block; min-height:0; height:196px; padding:18px 20px 20px;
    /* `pan-y` é o que divide o gesto sem disputa: o navegador continua rolando a lista na
       vertical, nativamente, e me entrega o horizontal sem que eu precise cancelar nada.
       Interceptar os dois no JS faria a rolagem vertical passar a depender do meu código —
       trocar uma rolagem nativa por uma emulada é sempre um mau negócio. */
    touch-action:pan-y;
  }
  /* cartaz fora do trio não recebe toque: ele está no box do palco, invisível, e pegaria
     o dedo de quem mira no vizinho de verdade */
  .sess-art-stage--carrossel .sess-art-poster:not(.is-atual):not(.is-vizinho){ pointer-events:none; }
  .sess-art-stage--carrossel .sess-art-poster{
    position:absolute; left:50%; bottom:20px; width:104px;
    cursor:pointer; /* o cartaz abre o detalhe do título — ver montarCarrosselDaPartida */
    /* a peça FORA do trio é o estado padrão: assim a regra do centro e a do vizinho
       precisam dizer só o que elas são, e não desfazer o que a outra fez */
    transform:translateX(calc(-50% + var(--off, 0) * var(--sess-passo))) scale(.78);
    opacity:0; z-index:0;
    transition:transform var(--dur-3) var(--ease-emphasis),
               opacity var(--dur-3) var(--ease-emphasis),
               box-shadow var(--dur-2) var(--ease-standard);
  }
  .sess-art-stage--carrossel .sess-art-poster.is-vizinho{
    transform:translateX(calc(-50% + var(--off, 0) * var(--sess-passo))) scale(.86);
    opacity:.46; z-index:1;
  }
  /* O CARTAZ DA VEZ. O aro é o mesmo ouro de decisão do sistema; o brilho sai pelas
     LATERAIS e não em volta — duas sombras deslocadas no eixo x, sem desfoque por cima ou
     por baixo. Um halo redondo empurraria a peça para o vocabulário do clímax do match,
     que é o único momento alto do app e não pode ser emprestado a um estado de leitura. */
  .sess-art-stage--carrossel .sess-art-poster.is-atual{
    transform:translateX(-50%) scale(1);
    opacity:1; z-index:2;
    box-shadow:
      inset 0 0 0 1px var(--sheen-3),
      0 0 0 2px var(--gold),
      -18px 0 24px -10px rgba(var(--gold-rgb), .42),
      18px 0 24px -10px rgba(var(--gold-rgb), .42),
      var(--shadow-3);
  }
  /* com movimento reduzido o carrossel não desliza: ele TROCA. O aro continua, porque ele
     é informação — diz qual descrição está sendo lida — e não enfeite. */
  @media (prefers-reduced-motion: reduce){
    .sess-art-stage--carrossel .sess-art-poster{ transition:none; }
  }
  /* ---- palco de um match só ----
     Achado P3 do juiz: aberto pelo Histórico, um único match usava MENOS canvas
     que o multiposter — o atalho direto entregava um card e nada de palco. Ele
     herda o palco da partida, em outra composição: um cartaz só, maior, sem
     leque e sem legenda sobreposta (o título já está em Bebas no card ao lado,
     e repetir sobre a arte era ruído). */
  .sess-art-stage--solo{ min-height:252px; }
  /* um cartaz só não reparte nada, então a largura dele é declarada — mas na
     mesma proporção de palco cheio que o par ganhou: 152px num palco de 278
     eram 55% de contêiner com dois vazios laterais, o mesmo defeito medido no
     multiposter. */
  .sess-art-stage--solo .sess-art-poster{
    width:190px; --pf-radius: var(--radius-md);
    box-shadow: inset 0 0 0 1px var(--sheen-3), var(--shadow-3);
  }
  /* ---- a ordem do documento ----
     rótulo ("Deram match") → dado (modo · quando · com quem) → obra (o palco) →
     ação (os cartões). Antes o palco vinha antes do rótulo e o diálogo abria sem
     dizer o que estava mostrando. A ordem mudou no DOM, não por `order`: a ordem
     visual e a ordem que o leitor de tela percorre continuam sendo a mesma. */
  .sess-detail-lead{ display:flex; flex-direction:column; min-width:0; }
  .sess-detail-lead .sess-art-stage{ flex:1 1 auto; }
  .sess-card-list{ min-width:0; }
  /* ================= O ROLO DE FILME =================
     A coluna de descrições vira uma PELÍCULA: duas calhas perfuradas nas laterais e cada
     obra ocupando um fotograma. Não é enfeite solto — é o material do produto dito na
     única superfície que existe para LEMBRAR de filmes, e é o mesmo vocabulário do palco
     logo acima (moldura de cartaz, linha de chão).
     A DIVISÃO ENTRE OS FOTOGRAMAS entrou junto e não por acaso: com dois cartões colados,
     cada um com a própria borda, a junta virava um fio duplo de 2px que não separava nada.
     Aqui o cartão devolve moldura, raio e sombra ao rolo e fica sendo o que ele é — um
     quadro da tira —, e o que separa um do outro é UMA linha, como na película.
     Os furos são geometria, não imagem: `repeating-linear-gradient` sobre uma faixa da
     largura do furo. Um data URI daria o canto arredondado do furo real, mas custaria uma
     cor escrita à mão dentro da URL, fora do sistema de tokens — o mesmo defeito que o
     card de compartilhamento acabou de perder. */
  .sess-detail-layout .sess-card-list{
    --rolo-calha:20px;   /* a borda perfurada de cada lado */
    --rolo-passo:19px;   /* de furo a furo */
    --rolo-furo:10px;    /* largura do furo */
    --rolo-altura:8px;   /* altura do furo */
    /* O RESPIRO É PARTE DA CALHA, não sobra dela. Sem ele o texto começava 6px depois do
       furo — geometricamente fora, e mesmo assim lendo como se estivesse por cima, porque
       a 6px o olho não separa duas coisas de peso parecido. A margem perfurada de um filme
       de verdade também não encosta na imagem: existe uma faixa lisa entre uma coisa e
       outra, e é ela. Custa 18px de largura útil de texto (259 -> 241). */
    --rolo-respiro:9px;
    position:relative; overflow:hidden;
    padding-inline:calc(var(--rolo-calha) + var(--rolo-respiro));
    /* A TIRA CARREGA O MATERIAL QUE OS CARTÕES DEVOLVERAM, e isso não é gosto: é o que faz
       o furo existir. Na primeira versão ela era `--panel-deep` (#0e0c14) e o furo, que é
       vazado na cor do fundo da tela (#0a0a10), ficava a 4/2/4 por canal da tira em volta
       — invisível. Película é um material OPACO sobre o fundo, e o furo só se lê porque a
       tira é mais clara que aquilo que aparece através dela. Medido agora: 18/19/24 no topo
       e 11/12/15 na base, contra os 4/2/4 de antes.
       O gradiente é raso de propósito (`surface-2` de ponta a ponta, e não descendo para
       `surface-1` como o cartão fazia): base de filme é quase uniforme, e uma tira que
       escurece muito para baixo apagaria os furos de baixo justamente onde a lista é mais
       longa. */
    background:linear-gradient(180deg, var(--surface-2-top), var(--surface-2-bot));
    border:1px solid var(--edge); border-top-color:var(--edge-lit);
    border-radius:var(--radius-lg);
    box-shadow:inset 0 1px 0 var(--sheen-1);
  }
  .sess-detail-layout .sess-card-list::before,
  .sess-detail-layout .sess-card-list::after{
    content:''; position:absolute; top:0; bottom:0; width:var(--rolo-calha);
    pointer-events:none; z-index:1;
    /* O FURO É VAZIO — pinta a cor do fundo da tela, não uma peça escura por cima —, E TEM
       ARESTA: um fio de luz em cima e outro embaixo, que é onde a espessura da película
       pega a luz. Num app quase preto a aresta faz mais pela leitura do que a diferença de
       preenchimento: entre a tira e o furo sobram 18/19/24 por canal no topo e 11/12/15 na
       base, e sozinho esse degrau some justamente na parte de baixo da lista, que é onde
       ela costuma ser mais longa. Com o fio, o furo se lê nas duas pontas. */
    background-image:repeating-linear-gradient(180deg,
      transparent 0 calc((var(--rolo-passo) - var(--rolo-altura)) / 2 - 1px),
      var(--edge-lit) calc((var(--rolo-passo) - var(--rolo-altura)) / 2 - 1px) calc((var(--rolo-passo) - var(--rolo-altura)) / 2),
      var(--bg) calc((var(--rolo-passo) - var(--rolo-altura)) / 2) calc((var(--rolo-passo) + var(--rolo-altura)) / 2),
      var(--edge-lit) calc((var(--rolo-passo) + var(--rolo-altura)) / 2) calc((var(--rolo-passo) + var(--rolo-altura)) / 2 + 1px),
      transparent calc((var(--rolo-passo) + var(--rolo-altura)) / 2 + 1px) var(--rolo-passo));
    background-size:var(--rolo-furo) var(--rolo-passo);
    background-position:center 10px;
    background-repeat:repeat-y;
  }
  /* a calha é uma FAIXA da tira, não a mesma superfície do quadro: o fio interno é a linha
     de quadro da película, e é ele que diz onde acaba a margem perfurada e começa a obra */
  .sess-detail-layout .sess-card-list::before{ left:0; box-shadow:inset -1px 0 0 var(--rule); }
  .sess-detail-layout .sess-card-list::after{ right:0; box-shadow:inset 1px 0 0 var(--rule); }
  /* o cartão devolve a moldura ao rolo: dentro da tira ele é um FOTOGRAMA, e moldura
     dentro de moldura era o defeito que o próprio diálogo já tinha corrigido em volta */
  .sess-detail-layout .sess-card-list .result-card{
    background:none; border:0; border-radius:0; box-shadow:none;
    padding-inline:0;
  }
  /* ---- O FOTOGRAMA QUE A SORTE ESCOLHEU ACENDE ----
     Numa partida com vários matches, o título sorteado era só mais um quadro da tira com um
     selo pequeno: a decisão da noite pesava o mesmo que as opções descartadas. O dono pediu
     ênfase, e ela precisa caber na metáfora da película — devolver moldura ao cartão
     desfaria justamente o que este bloco corrigiu.
     Então o quadro ACENDE em vez de ganhar caixa: luz dourada entrando por cima (como a do
     projetor parada nele) e um fio de ouro contornando o fotograma. O brilho precisa de
     área para dissolver e o quadro é colado nas laterais, então a margem negativa devolve a
     largura da calha SEM mover o texto — o eixo esquerdo continua um só.
     Ele não pulsa: pulso dourado é do match (`.unanimous`), que é o momento alto do app; o
     sorteio é o desempate e ganha estado, a mesma fronteira do selo `.drawn-tag`. */
  .sess-detail-layout .sess-card-list .result-card--drawn{
    background: radial-gradient(130% 110% at 50% 0%,
      rgba(var(--gold-rgb), .20), rgba(var(--gold-rgb), .05) 55%, transparent 78%);
    box-shadow: inset 0 0 0 1px rgba(var(--gold-rgb), .34), 0 0 26px rgba(var(--gold-rgb), .18);
    border-radius: var(--radius-md);
    margin-inline: calc(var(--rolo-respiro) * -1);
    padding-inline: var(--rolo-respiro);
  }
  /* A LINHA DE QUADRO. Um `border-top` não serve aqui por uma razão de desenho: borda vai
     de ponta a ponta e termina em canto reto, e a peça que ela divide é uma tira com
     margem lisa dos dois lados. Como barra própria ela PARA antes da calha e termina
     arredondada — não encosta na perfuração, e o fim dela não é um corte.
     3px e `--rule-strong`: mais grossa que a régua comum do app porque aqui ela é a única
     coisa separando duas obras, agora que o cartão devolveu a moldura ao rolo. Continua
     sem compartimentar: os títulos são a mesma partida. */
  .sess-detail-layout .sess-match + .sess-match{ position:relative; }
  .sess-detail-layout .sess-match + .sess-match::before{
    content:''; position:absolute; z-index:2; top:0; left:0; right:0; height:3px;
    border-radius:999px; background:var(--rule-strong); pointer-events:none;
  }
  /* ---- um eixo esquerdo só ----
     Dentro do cartão, título e metadados começavam recuados atrás da miniatura
     (x≈105) enquanto o selo de nota e os botões voltavam para o padding do
     cartão (x≈40): o mesmo cartão tinha DUAS margens esquerdas. Aqui a miniatura
     era, além disso, a terceira aparição da mesma arte — o palco logo acima já
     mostra o cartaz inteiro. Some a miniatura, e o cartão inteiro passa a
     começar no mesmo x. No resultado da rodada e no ranking, onde não há palco,
     ela continua. */
  .sess-detail-layout .result-card .result-emoji{ display:none; }
  /* ---- uma voz de cartaz por diálogo ----
     "Título de tela e item de lista com o mesmo papel tipográfico": "DERAM
     MATCH" e "CIDADE DE DEUS" saíam as duas em Bebas caixa alta, a 8px de
     corpo uma da outra. Duas vozes de display na mesma caixa de 380px, e o
     olho não sabe qual delas manda.
     A fundação já decide isto: `.t-display` só na ENTRADA da superfície — que
     aqui é o cabeçalho do diálogo. Os cartões são a LISTA de obras da partida,
     e a lista que venceu a comparação cega já nomeia obra em romano bold
     branco (`.hist-titles`). O diálogo passa a falar a língua da linha que o
     abriu, e o nome do filme volta a aparecer como foi escrito ("Cidade de
     Deus"), não em versal por imposição da fonte.
     Escopo: só a composição de partida. No resultado da rodada e no ranking o
     cartaz continua em Bebas. */
  .sess-detail-layout .result-title{
    font-family:inherit; font-size:var(--t-title-size); line-height:var(--t-title-lh);
    letter-spacing:var(--t-title-track); font-weight:var(--t-title-weight);
  }
  /* ---- um eixo esquerdo TAMBÉM na zona de ação ----
     O cartão tinha três alinhamentos: título, gênero e selo no eixo do cartão;
     "Ver detalhes" centralizado numa linha própria; e "Abrir no X" em largura
     total. A causa era `flex:1` nos dois botões — quando o par não cabia na
     régua, cada um ganhava uma linha inteira e centralizava o rótulo dentro
     dela. Aqui cada botão pede só a largura do próprio rótulo e começa no eixo
     do cartão: cabendo, dividem a régua; não cabendo, o segundo desce e começa
     no MESMO x. Um eixo do topo ao pé do cartão. */
  /* ---- ESTAS REGRAS SÃO DO CARTÃO COMPACTO, e só dele ----
     Elas desenham a régua dos FOTOGRAMAS da película: a lista de matches de uma partida,
     onde cada cartão é um resumo e as ações são secundárias.
     O `:not(.result-card--rich)` não é zelo: numa partida de UM match só, o detalhe
     completo é montado DENTRO deste mesmo layout (é ele que traz o palco junto), e sem o
     recorte o cartão rico herdava a régua do resumo — "Salvar na lista" encolhia para
     107px de fantasma sem padding, enquanto o mesmo botão, no detalhe aberto a partir de
     uma partida com vários matches, ocupava a régua inteira. O mesmo botão, na mesma tela,
     com dois tamanhos, decidido por quantos matches a partida teve. */
  /* 24px de gap, e o número tem história: ele era 10 aqui mais 14 de padding DENTRO do
     fantasma. Somava a mesma distância, só que pelo lado errado — ver a regra do fantasma
     logo abaixo. Separação entre duas peças é trabalho do vão, não do forro de uma delas. */
  .sess-detail-layout .result-card:not(.result-card--rich) .result-actions{
    justify-content:flex-start; align-items:center; gap:24px;
  }
  .sess-detail-layout .result-card:not(.result-card--rich) .result-actions .result-btn{ flex:0 0 auto; }
  /* O fantasma não tem material, então o padding dele é ar: sem zerar o da ESQUERDA o
     rótulo começaria 11px à direita do eixo de todo o resto do cartão.
     E é zerado dos DOIS lados, porque a caixa dele tem que abraçar o rótulo. A tentativa
     anterior pôs 14px só à direita para afastá-lo do botão vizinho, e isso desloca o
     rótulo dentro da própria caixa: 1px de folga à esquerda contra 15px à direita. Não se
     via enquanto o fantasma não tinha borda — até ele receber o anel de foco, e aí o
     rótulo aparece encostado num lado do anel. Padding é forro de uma peça; distância
     entre duas peças é o vão, e é lá (no `gap`) que ela mora agora. */
  .sess-detail-layout .result-card:not(.result-card--rich) .result-actions .btn--ghost{
    padding-left:0; padding-right:0;
  }
  /* ---- a ação primária deixa de ser a peça mais apagada do cartão ----
     Entre dois matches nenhum veste o ouro (são pares, e a regra de um primário
     por estado continua valendo). Só que o papel secundário da fundação é
     `--surface-2`, que é EXATAMENTE o topo do gradiente do próprio
     `.result-card`: o botão vinha pintado com o fundo em que estava. Aqui ele
     sobe um nível de material e ganha a aresta forte — continua sem ouro e
     passa a ler como controle.
     `:not(.primary)` porque o mesmo botão é o primário dourado quando a
     partida tem um match só, e ali ele não pode ser rebaixado. */
  .sess-detail-layout .result-actions .result-btn[data-stream]:not(.primary){
    background: linear-gradient(180deg, var(--surface-3-top), var(--surface-3-bot));
    border-color: var(--edge-strong); border-top-color: var(--edge-lit);
    box-shadow: var(--shadow-1), inset 0 1px 0 var(--sheen-3);
    padding-left:16px; padding-right:16px;
  }
  /* overlay de detalhe do título (histórico) */
  .detail-overlay{
    position:fixed; inset:0; z-index:120;
    display:flex; align-items:center; justify-content:center;
    padding:20px;
  }
  /* ---- por que o detalhe do título mora um degrau acima de todos os outros painéis ----
     `#detailOverlay` é ESTÁTICO e nasce no topo do documento; os painéis do sorteio, da
     lista e do compartilhar são criados com `appendChild` e entram DEPOIS dele. Com o
     mesmo z-index, quem vem por último pinta por cima — então "Ver detalhes" no sorteio
     abria o detalhe INTEIRO atrás da roleta e a tela não mudava nada. Medir a caixa não
     mostrava: ela abria com display:flex e tamanho certo. Quem revelou foi perguntar ao
     navegador quem recebe o toque no meio dela (`elementFromPoint`), que respondia
     "#raffleOverlay".
     O degrau é a regra, não o remendo: o detalhe de um título é sempre a ÚLTIMA porta
     aberta — ele nasce de dentro de outro painel (o sorteio, "Ver os títulos" de uma
     lista, uma partida do histórico) e precisa ficar por cima de quem o abriu. Fechá-lo
     devolve a pessoa ao painel de origem, que continua lá.
     Acima dele só o clímax do match (200) e os diálogos de confirmação (1000). */
  #detailOverlay{ z-index:140; }
  /* ---- o véu se compromete ----
     Com 72% de opacidade e 4px de desfoque o título e as linhas da tela de baixo
     continuavam PERFEITAMENTE legíveis atrás da sobreposição, competindo com
     ela: uma camada modal que não escurece o suficiente não lê como camada.
     O desfoque também dessatura, para que pôster nenhum apareça em cor cheia
     através do véu. */
  .detail-overlay-backdrop{
    position:absolute; inset:0; background:rgba(4,4,8,.90);
    backdrop-filter: blur(16px) saturate(.55); -webkit-backdrop-filter: blur(16px) saturate(.55);
  }
  /* o painel NÃO rola: quem rola é o conteúdo dentro dele. Antes o painel era o scroller e
     o ✕ (position:absolute) rolava junto — numa partida com vários matches você descia a
     lista e o botão de fechar saía da tela. Todos os seis overlays têm a mesma estrutura
     (botão + um wrapper de conteúdo), então isto vale pra todos de uma vez. */
  .detail-overlay-panel{
    position:relative; width:100%; max-width:380px; max-height:86vh; max-height:86dvh;
    border-radius:20px;
    display:flex; flex-direction:column;
    animation: overlay-in .22s ease;
  }
  /* ---- o scroller do diálogo não empresta a barra do sistema ----
     A comparação cega viu a barra de rolagem NATIVA do Windows aparecer à
     direita, por cima do desenho próprio do painel: cinza do sistema operacional
     dentro de um diálogo que o app inteiro pinta. `.phone` e `#history` já
     escondem a deles pelo mesmo motivo; este scroller era o único que não. */
  .detail-overlay-panel > :not(.detail-overlay-close){
    flex:1 1 auto; min-height:0; overflow-y:auto;
    border-radius:inherit;
    scrollbar-width:none; -ms-overflow-style:none;
  }
  .detail-overlay-panel > :not(.detail-overlay-close)::-webkit-scrollbar{ display:none; }
  /* ---- o painel da partida tem superfície própria ----
     Sem material, a região do painel que nenhuma peça pintava não era vazio
     neutro: era VIDRO. Através dela a biblioteca de fundo aparecia com texto
     legível e pôsteres em cor cheia DENTRO dos limites do diálogo — o olho lia
     vazamento de camada, não profundidade. Agora o diálogo é o mesmo material
     dos cartões, um nível abaixo deles, e nada do fundo se lê por dentro.
     Escopo: só a composição de partida (histórico). O detalhe de um título
     avulso continua sendo o cartão direto, sem moldura em volta de moldura. */
  #detailOverlay:has(.sess-detail-layout) .detail-overlay-panel{
    background: linear-gradient(180deg, var(--surface-1-top), var(--surface-1-bot));
    border:1px solid var(--edge); border-top-color:var(--edge-lit);
    box-shadow: var(--shadow-3), inset 0 1px 0 var(--sheen-1);
  }
  /* ---- A TAMPA DA FAIXA DO ✕ ----
     A faixa de `--detalhe-topo` no alto do painel é reservada ao ✕, e quem a mantém limpa é
     o palco: ele gruda no topo com deslocamento negativo justamente para cancelá-la.
     Só que isso depende de `position:sticky` com `top` negativo dentro de um flex — e o
     dono fotografou, no celular, a película escorrendo por essa faixa nos dois cantos de
     cima. Não reproduzi em Chrome; ou seja, é comportamento de motor, e não uma conta
     errada que eu possa corrigir na origem sem ver.
     Esta tampa torna a pergunta irrelevante: uma peça opaca, da largura inteira do painel,
     ocupando exatamente a faixa reservada. `z-index:2` é o degrau que importa — acima do
     conteúdo que rola (auto/0), abaixo do palco (3) e do ✕ (4). Onde o palco se comporta,
     ela fica invisível debaixo dele; onde não se comporta, é ela que segura a faixa.
     `pointer-events:none` porque ela não é peça de interação, e o raio acompanha o canto do
     painel para não desenhar um quadrado sobre a borda arredondada. */
  #detailOverlay:has(.sess-detail-layout) .detail-overlay-panel::before{
    content:''; position:absolute; left:0; right:0; top:0;
    height:var(--detalhe-topo);
    background:var(--surface-1-top);
    border-radius:20px 20px 0 0;
    z-index:2; pointer-events:none;
  }
  @keyframes overlay-in{ from{ transform:translateY(14px); opacity:0; } }
  /* o conteúdo do detalhe (histórico e ranking) começa com título centralizado e ia parar
     debaixo do ✕ — 44px de botão sobre a linha do título. Aqui a faixa dele é reservada.
     Os painéis .account-panel-card não precisam: já têm 30px de topo e título curto. */
  /* `--detalhe-topo` é a faixa reservada ao ✕, e ela precisa ser UM número só: o palco fixo
     da partida sobe exatamente essa distância para encostar no topo, e os dois saindo de
     sincronia deixariam uma fresta por onde as descrições passariam aparecendo. */
  /* A faixa reservada ao ✕ é UM número, e ele mora no PAINEL: a tampa que a protege é um
     `::before` do painel, e o conteúdo que a respeita é filho dele. Declarada só no
     conteúdo, a tampa não a enxergava e as duas podiam divergir sem ninguém notar. */
  .detail-overlay-panel{ --detalhe-topo:56px; }
  #detailOverlayContent{ padding-top:var(--detalhe-topo); }
  /* ---- o ✕ centrado por GEOMETRIA, não por métrica de fonte ----
     A comparação cega mediu o botão desalinhado em três eixos a 12x. A causa era
     uma só: o glifo era texto (`✕`, U+2715) dentro de um `<button>` com padding
     do agente de usuário e `line-height` normal, então a posição dele vinha da
     linha de base da fonte — sempre alguns décimos abaixo e à esquerda do centro
     do disco, e variando com a fonte que o sistema resolvesse.
     Agora o botão é uma caixa flex com padding zerado e o glifo é um SVG de duas
     diagonais na mesma linguagem de traço do resto dos ícones: o centro dele é o
     centro do quadrado por construção, e o anel de foco, o disco e o glifo
     passam a ser concêntricos por definição. */
  .detail-overlay-close{
    /* z-index:4 e não 2: a saída é a peça mais alta do painel por definição. Com o palco
       da partida fixo em 3 e encostado no topo, um ✕ em 2 ficaria POR BAIXO da arte —
       fechar viraria adivinhação justo na tela que mais rola. */
    position:absolute; top:10px; right:10px; z-index:4;
    display:flex; align-items:center; justify-content:center;
    padding:0; line-height:1;
    width:44px; height:44px; border-radius:50%;
    border:1px solid var(--edge); border-top-color:var(--edge-lit); color:var(--text);
    background: linear-gradient(180deg, var(--surface-2-top), var(--surface-2-bot));
    box-shadow: var(--shadow-1), inset 0 1px 0 var(--sheen-2);
    font-size:13px; cursor:pointer;
  }
  .detail-overlay-close svg{ display:block; }
  /* ---- a única exceção ao 4, e ela CONFIRMA a regra ----
     A busca de títulos é o único painel com uma peça GRUDADA por cima do conteúdo: o
     cabeçalho, que precisa ficar acima da fileira que rola por baixo dele (z-index:5, ver
     `.find-head`). Como ele é opaco, ficar acima do ✕ não tirou só o toque — apagou o botão
     da tela, e o painel ficou sem saída visível. O dono viu.
     O conserto respeita a frase acima em vez de contorná-la: quem sobe é a SAÍDA, para o
     degrau seguinte. Baixar o cabeçalho devolveria o defeito que ele foi posto ali para
     resolver. Esta superfície tem três níveis e precisa dos três — o que rola < o que fica
     grudado < a saída.
     Mora aqui, colada à regra que excepciona, e não lá na seção da busca: é aqui que alguém
     procura quando o ✕ se comportar de um jeito estranho. */
  #findOverlay .detail-overlay-close{ z-index:6; }
  /* painel de estatísticas do histórico */
  .stats-panel{
    background: linear-gradient(165deg, var(--surface-2-top), var(--surface-1-bot));
    border:1px solid var(--edge); border-top-color:var(--edge-lit); border-radius:var(--radius-lg);
    box-shadow: var(--shadow-2), inset 0 1px 0 var(--sheen-2);
    padding:18px 16px 16px; margin-bottom:22px;
  }
  .stats-panel .stats-label{
    font-size:var(--t-group-size); color:var(--text); letter-spacing:var(--t-group-track);
    font-weight:var(--t-group-weight); text-align:center; margin-bottom:12px;
  }
  /* OS TRÊS NÚMEROS PRECISAM COMEÇAR NA MESMA LINHA, e por isso o alinhamento é pelo TOPO.
     Com `center`, cada coluna era centrada na própria altura — e "Títulos escolhidos" quebra
     em duas linhas onde "Partidas" cabe em uma, então a caixa mais alta empurrava o número
     dela para cima e o placar saía em degraus. É o mesmo defeito que o pódio do Ranking já
     tinha pago, e a saída é a mesma: alinhar pelo topo e RESERVAR duas linhas no rótulo,
     para que as bases também fiquem no lugar quando um rótulo couber em uma só. */
  .stats-numbers{ display:flex; align-items:flex-start; justify-content:space-around; }
  .stats-numbers .stat{ text-align:center; flex:1; }
  .stats-numbers .stat b{ display:block; font-family:var(--font-display); font-size:34px; line-height:1; color:var(--gold-ink); letter-spacing:.5px; font-variant-numeric:tabular-nums; }
  .stats-numbers .stat span{
    display:block; margin-top:5px; font-size:var(--t-support-size); line-height:1.35;
    min-height:2.7em;   /* duas linhas de 1.35 */
    color:var(--text-dim); letter-spacing:0;
  }
  /* a régua tem a altura do número, e com o alinhamento pelo topo ela fica exatamente ao
     lado dele — antes, centrada, escorregava para o meio do rótulo */
  .stat-div{ width:1px; height:34px; background:var(--rule-strong); flex:none; }
  .stats-favs{ display:flex; gap:10px; margin-top:16px; }
  .stats-favs .fav{
    flex:1; padding:10px 12px; text-align:center;
    border:1px solid var(--edge); border-top-color:var(--edge-lit); border-radius:var(--radius-md);
    background: linear-gradient(180deg, var(--surface-2-top), var(--surface-2-bot));
    box-shadow: inset 0 1px 0 var(--sheen-2);
  }
  .stats-favs .fav span{ display:block; font-size:var(--t-support-size); color:var(--text-dim); letter-spacing:0; margin-bottom:3px; }
  .stats-favs .fav b{ font-size:14px; color:var(--text); }
  /* min-*: o alvo é garantido mesmo se o ícone crescer com o zoom de texto —
     width/height fixos encolhiam o alvo junto com a caixa em 200% */
  .history-remove-btn{
    min-width:44px; min-height:44px; width:44px; height:44px; border-radius:50%; flex:none;
    border:1px solid var(--edge); background:transparent;
    color:var(--text-dim); font-size:12px; cursor:pointer;
    transition: color var(--dur-1) var(--ease-standard), border-color var(--dur-1) var(--ease-standard);
  }

  .history-empty{
    min-height:230px; display:flex; flex-direction:column; align-items:center;
    justify-content:center; text-align:center; padding:24px 18px;
  }
  .history-empty p{ max-width:34ch; color:var(--text-dim); font-size:14px; line-height:1.55; }
  /* o piso de 190px nunca passa da tela: com a letra do sistema em 200% ela tem 187px */
  .history-empty .cta{ width:auto; min-width:min(190px, 100%); }
  /* o vazio POR FILTRO não tem estante de molduras nem frase própria — o cabeçalho já
     nomeou o recorte vazio lá em cima. Sobra a saída, e reservar 230px de altura para um
     botão só deixava a peça boiando num vão que não é dela. */
  .history-empty--filtered{ min-height:132px; }
  .history-empty-line{ text-wrap:pretty; }
  /* ---- o encosto da barra de abas ----
     A lista rola POR BAIXO de uma barra fixa e opaca: o item que cruza a barra
     era cortado a faca, com um fiapo de 1px sobrando embaixo. O degradê dissolve
     o conteúdo dentro da barra em vez de guilhotiná-lo — e é a mesma luz de cima
     do resto do material, só invertida para virar chão.
     A barra é sempre a navegação inferior do shell móvel. */
    body:has(#history.active) .tabbar::before{
      content:''; position:absolute; left:0; right:0; bottom:100%;
      /* ---- a dívida do véu, paga na integração ----
         Esta rampa nasceu neste bloco: 36px em duas paradas era curta e linear
         (nos últimos 14px o véu já estava quase opaco, e os 22px anteriores mal
         escureciam), então virou 54px em quatro paradas — a dissolução começa
         antes e acelera no fim.
         Na rodada 6 o Início promoveu esse desenho ao par `--tabbar-veil` /
         `--tabbar-veil-fade`, mas os dois lados ficaram travados: o Histórico não
         podia consumir os tokens sem derrubar um assert que vive no arquivo de
         teste do Início, e o Início não podia editar o CSS do Histórico. Cada dono
         fez a sua metade e deixou a outra registrada.
         A integração fechou: aqui os tokens, e lá o contrato passou a exigir que
         as duas superfícies leiam da MESMA FONTE em vez de repetirem o mesmo
         valor. Fonte igual garante curva igual por construção; valor igual só
         garantia até alguém editar um dos dois lados. */
      height:var(--tabbar-veil);
      background:var(--tabbar-veil-fade);
      pointer-events:none;
    }
    /* e o VIDRO da mesma dissolução (ver `--veil-glass`): a rampa de cor sozinha
       entregava o cartaz da última linha com o topo limpo e a base apagada. */
    body:has(#history.active) .tabbar::after{
      content:''; position:absolute; left:0; right:0; bottom:100%;
      height:var(--tabbar-veil); pointer-events:none;
      backdrop-filter: var(--veil-glass); -webkit-backdrop-filter: var(--veil-glass);
      -webkit-mask-image: var(--veil-glass-mask);
              mask-image: var(--veil-glass-mask);
    }
  .history-next{ margin-top:26px; padding-top:20px; border-top:1px solid var(--rule); }
  .history-next-line{
    max-width:40ch; margin-bottom:16px; color:var(--text-dim); text-wrap:pretty;
    font-size:var(--t-support-size); line-height:var(--t-support-lh);
  }
  .history-next .cta{ width:auto; min-width:200px; }

  /* RESULTS */
  .results-wrap{ width:100%; max-width:360px; display:flex; flex-direction:column; gap:14px; }
  /* font-weight explícito: virou h2 e o Bebas Neue não tem negrito real */
  .results-heading{ font-family:var(--font-display); font-weight:var(--t-display-weight); font-size:30px; letter-spacing:1px; color:var(--gold-ink); text-align:center; margin-bottom:2px; }
  /* recebe foco programático no fim da rodada; o anel do :focus-visible não deve aparecer
     por isso, só quando a pessoa realmente navegar por teclado até aqui */
  .results-heading:focus{ outline:none; }
  .result-card{
    background: linear-gradient(160deg, var(--surface-2-top), var(--surface-1-bot));
    border:1px solid var(--edge); border-top-color:var(--edge-lit);
    border-radius:var(--radius-lg); padding:18px;
    box-shadow: var(--shadow-2), inset 0 1px 0 var(--sheen-2);
  }
  .result-top{ display:flex; gap:14px; align-items:flex-start; margin-bottom:10px; }
  .result-emoji{ font-size:34px; width:52px; --pf-radius: var(--radius-sm); }
  .result-title{ font-family:var(--font-display); font-size:22px; letter-spacing:.5px; line-height:1.05; }
  .result-meta{ font-size:var(--t-support-size); color:var(--text-dim); margin-top:3px; font-variant-numeric:tabular-nums; }
  .result-provider{ font-size:11px; color:var(--gold-ink); margin-top:4px; font-weight:700; }
  .result-desc{ font-size:13px; color:var(--text-dim); line-height:1.55; margin-bottom:12px; }
  .result-scores{ display:flex; gap:8px; }
  .score-pill{
    padding:7px 12px; font-size:13px; font-weight:700; display:flex; align-items:center; gap:6px;
    border:1px solid var(--edge); border-top-color:var(--edge-lit); border-radius:var(--radius-sm);
    background: linear-gradient(180deg, var(--surface-2-top), var(--surface-2-bot));
    box-shadow: inset 0 1px 0 var(--sheen-2);
    font-variant-numeric: tabular-nums;
  }
  /* ---- o par de botões se equilibra ----
     Com `flex:1` os dois ganhavam larguras iguais, e o rótulo mais longo ("Abrir
     no Prime Video") quebrava em DUAS linhas ao lado de um botão de uma linha só.
     Agora cada um pede a largura do seu próprio rótulo: se os dois cabem, dividem
     a régua; se não cabem, cada um recebe uma régua inteira — dois botões de
     mesma altura em vez de um alto e um baixo. */
  .result-actions{ display:flex; flex-wrap:wrap; gap:8px; margin-top:12px; }
  .result-btn{
    flex:1; padding:11px; min-height:44px; border-radius:var(--radius-sm);
    font-size:13px; font-weight:700; cursor:pointer;
  }
  .result-actions .result-btn{ flex:1 1 auto; min-width:max-content; white-space:nowrap; }
  /* ---- no detalhe, "Abrir no X" tem LINHA PRÓPRIA ----
     `min-width:max-content` garante que rótulo nenhum seja cortado, então dois botões que
     somam quase a largura da régua não estouram: eles cabem, e o que sobra pra respiro é o
     que sobrou da conta. Medido no detalhe de um título sem trailer embutível, que é o caso
     comum: "Salvar na lista" (173px) + "Abrir no Netflix" fechavam a régua com 3px de
     folga entre os dois — o texto encostava na borda dos dois lados e o botão lia como
     apertado, que foi exatamente o que o dono viu.
     A saída não é encolher a fonte nem abreviar o rótulo: é parar de fazer a ação primária
     dividir linha. Ela é a única peça dourada do cartão e a razão da tela existir; linha
     inteira é o lugar dela. Sobra a régua toda para "Salvar na lista" — ou para ele mais o
     "Ver trailer", quando não há player embutido, que é o par que já cabia folgado. */
  .result-card--rich .result-actions .result-btn[data-stream]{ flex:1 0 100%; }
  /* ---- detalhe do título: trailer, classificação indicativa e elenco ----
     Só o overlay aberto pelo pôster usa este bloco (renderResultCard com rich:true). O
     resultado da partida e o histórico seguem com o card compacto de sempre. */
  .detail-media{
    position:relative; aspect-ratio:16/9; width:100%; margin-bottom:14px;
    border-radius:var(--radius-md); overflow:hidden; background:var(--card-top);
    border:1px solid var(--edge); border-top-color:var(--edge-lit);
  }
  .detail-media img, .detail-media iframe{ width:100%; height:100%; display:block; border:0; object-fit:cover; }
  .detail-media-play{
    display:block; position:relative; width:100%; height:100%; padding:0; margin:0;
    border:0; background:none; cursor:pointer; color:inherit; font:inherit;
  }
  .detail-play-badge{
    position:absolute; inset:0; margin:auto; width:54px; height:54px; border-radius:50%;
    background:rgba(var(--gold-rgb), .94); color:var(--on-gold);
    display:flex; align-items:center; justify-content:center;
  }
  /* enquanto o TMDB não responde: a caixa já ocupa o espaço final, então a imagem que
     chega depois não empurra o título pra baixo debaixo do dedo de quem está lendo.
     O spinner genérico virou o esqueleto do sistema: mesmo material, mesma varredura. */
  .detail-media--loading::after{
    content:''; position:absolute; inset:0;
    background: linear-gradient(100deg, transparent 18%, var(--sheen-3) 50%, transparent 82%);
    transform: translateX(-100%);
    animation: skel-sweep 1.5s var(--ease-standard) infinite;
  }
  @media (prefers-reduced-motion: reduce){
    .detail-media--loading::after{ animation:none !important; transform:none; background:var(--sheen-2); }
  }
  /* selo da ClassInd nas cores oficiais. O número é parte da marca, por isso o nome
     acessível vai no aria-label em vez de texto solto ao lado. */
  .classind{
    width:26px; height:26px; border-radius:5px; flex:none; align-self:center;
    display:inline-flex; align-items:center; justify-content:center;
    font-size:13px; font-weight:700; color:#fff;
  }
  .classind--l{ background:#00a650; }
  .classind--10{ background:#0071bc; }
  /* branco sobre o amarelo oficial dá 1,8:1 e é ilegível de fato, não só no papel:
     aqui o número vai escuro, sem sair da família da cor */
  .classind--12{ background:#f2c500; color:#3b2f00; }
  .classind--14{ background:#f58220; }
  /* 16 anos: o vermelho de antes dava 4,38 com o número branco; este dá 5,1 (1.67.0) */
  .classind--16{ background:#d81920; }
  .classind--18{ background:#000; }
  /* o slot do selo não ocupa lugar enquanto está vazio; e quando o TMDB não tem o dado do
     Brasil, a ficha diz isso em texto de apoio, sem selo inventado nem cor da ClassInd */
  .classind-slot{ display:contents; }
  .classind-nd{ align-self:center; font-size:var(--t-support-size); color:var(--text-dim); }
  .score-pill .tmdb-mark{ width:54px; height:auto; display:block; }
  /* na lista de uma partida o card inteiro leva ao detalhe. Sem role=button: os botoes
     dentro dele ja sao interativos, e aninhar papel de botao quebraria o leitor de tela.
     Quem navega por teclado chega pelo proprio botao "Ver detalhes". */
  .sess-match{ cursor:pointer; }
  .detail-people{ margin-top:18px; }
  .detail-director{ font-size:13px; color:var(--text); margin:0 0 12px; }
  .detail-director span{ color:var(--text-dim); }
  .people-strip{
    display:flex; gap:14px; list-style:none; padding:0 2px 4px; margin:0;
    overflow-x:auto; scrollbar-width:none; -webkit-overflow-scrolling:touch;
  }
  .people-strip::-webkit-scrollbar{ display:none; }
  .person{ width:66px; flex:none; text-align:center; }
  .person-face{
    width:58px; height:58px; border-radius:50%; margin:0 auto 6px; overflow:hidden;
    background:var(--card-top); border:1px solid var(--line);
    display:flex; align-items:center; justify-content:center;
    font-size:15px; font-weight:700; color:var(--text-dim);
  }
  .person-face img{ width:100%; height:100%; object-fit:cover; }
  .person b{ display:block; font-size:11px; line-height:1.3; color:var(--text); font-weight:600; }
  .person span{ display:block; font-size:11px; line-height:1.3; color:var(--text-dim); }
  .runner-card{
    border:1px dashed var(--edge-strong); border-radius:var(--radius-md); padding:14px 16px;
    display:flex; align-items:center; gap:12px; background: rgba(var(--gold-rgb), .05);
  }
  .runner-card .rank-thumb{ width:38px; font-size:16px; }
  .runner-card b{ display:block; font-size:14px; }
  .runner-card span{ font-size:12px; color:var(--text-dim); }
  /* min-height:44px — este botão só renderiza quando existem quase-matches, por isso
     escapou da varredura de alvos de toque da passada 6 (estava com 31px) */
  .runner-add-btn{ margin-left:auto; background:none; border:1px solid var(--gold); color:var(--gold-ink); border-radius:8px; padding:7px 12px; min-height:44px; font-size:12px; font-weight:700; cursor:pointer; white-space:nowrap; }
  /* fim de rodada sem match: a ação mais provável ("me mostra outros") ganha peso de CTA,
     a rodada final vira secundária e encerrar vira link discreto */
  /* ritmo: o gap da coluna é 14px pra tudo. Aqui a zona de AÇÃO ganha ar embaixo pra não
     encostar na de consulta, e a saída se descola do resto — contraste deliberado no lugar
     de um intervalo só repetido do topo ao rodapé. */
  .next-round{ display:flex; flex-direction:column; margin-bottom:8px; }
  .next-round .cta{ margin-top:0; }
  .next-round .chip-hint{ margin:8px 0 0; text-align:center; }
  .results-wrap > .link-btn{ margin-top:10px; }
  /* "o mais votado" é consulta, não destino: linha compacta em vez do card de resultado
     inteiro, que ocupava 311px e dominava a tela no teste de apertar os olhos apesar de
     ser o item de menor prioridade aqui */
  /* ---- APURAÇÃO ----
     A tabela que responde "e no que deu, então?". Cada linha é um título, e a barra atrás
     dela é a fração de gente que curtiu — a mesma informação que o número dá, na forma que
     se lê sem contar. O vencedor não ganha cor nova: ganha a borda de ouro que o resto do
     app já usa pra "esta é a peça escolhida". Ouro CHEIO continua sendo só do match. */
  .tally{ display:flex; flex-direction:column; gap:10px; }
  .tally-label{
    margin:0; font-size:var(--t-support-size); font-weight:700; letter-spacing:0;
    color:var(--text-dim);
  }
  .tally-list{ list-style:none; margin:0; padding:0; display:flex; flex-direction:column; gap:6px; }
  .tally-row{
    position:relative; overflow:hidden;
    display:flex; align-items:center; gap:10px;
    padding:9px 12px; border-radius:var(--radius-sm);
    border:1px solid var(--line); background:rgba(var(--bg-panel-rgb), .5);
  }
  /* a barra é FUNDO, não peça: fica atrás do texto e nunca disputa leitura com ele */
  .tally-row::before{
    content:''; position:absolute; inset:0 auto 0 0; width:var(--fatia, 0%);
    background:rgba(var(--gold-rgb), .10); pointer-events:none;
  }
  .tally-row > *{ position:relative; }
  .tally-row--winner{ border-color:var(--gold-dim); }
  .tally-row--winner::before{ background:rgba(var(--gold-rgb), .18); }
  .tally-pos{
    flex:none; width:18px; text-align:center;
    font-size:12px; font-weight:700; color:var(--text-dim); font-variant-numeric:tabular-nums;
  }
  .tally-name{ flex:1; min-width:0; }
  .tally-name b{ display:block; font-size:14px; line-height:1.25; }
  .tally-name span{ display:block; font-size:12px; color:var(--text-dim); }
  /* tabular-nums: sem ele, "5/6" e "11/12" desalinham a coluna e a tabela perde o eixo */
  .tally-count{
    flex:none; font-size:15px; font-weight:700; color:var(--text);
    font-variant-numeric:tabular-nums; letter-spacing:0;
  }
  .tally-count i{ font-style:normal; font-size:12px; font-weight:600; color:var(--text-dim); }
  .tally-row--winner .tally-count{ color:var(--gold-ink); }
  .top-voted{
    display:flex; flex-direction:column; gap:12px;
    padding:14px; border-radius:var(--radius-md);
    border:1px dashed var(--edge-strong); background:rgba(var(--bg-panel-rgb), .5);
  }
  /* deixou a caixa alta: é o rótulo de uma consulta secundária e não precisa
     do peso de etiqueta que ele tinha */
  .top-voted-label{
    margin:0; font-size:var(--t-support-size); font-weight:700; letter-spacing:0;
    color:var(--text-dim);
  }
  .top-voted-row{ display:flex; align-items:center; gap:12px; }
  .top-voted-row b{ display:block; font-size:14px; line-height:1.25; }
  .top-voted-row span{ font-size:12px; color:var(--text-dim); }
  .top-voted .result-btn{ width:100%; }
  .link-btn{
    font-family:inherit;
    font-size:13px; font-weight:600; cursor:pointer; text-decoration:underline;
    text-underline-offset:3px; min-height:44px; align-self:center; padding:0 12px;
  }

  /* ---- rodapé de saída dos painéis de configuração ----
     O ✕ no canto é o gesto de DESCARTAR na convenção de todo mundo, e aqui ele salvava.
     Quem mudava os streamings não tinha como saber se sair pelo ✕ manteria a escolha, e a
     dúvida custava uma reabertura pra conferir. O rodapé diz a regra e dá a saída afirmativa. */
  .panel-footer{
    margin-top:22px; padding-top:16px; border-top:1px solid var(--line);
    display:flex; flex-direction:column; gap:10px;
  }
  .panel-footer .cta{ margin-top:0; width:100%; }
  .panel-footer-note{
    margin:0; font-size:12px; color:var(--text-dim); line-height:1.45; text-align:center;
  }
  .panel-footer-note b{ color:var(--gold-dim); font-weight:700; }
  /* a caixa do erro cresce para FORA (margem negativa do tamanho do respiro): assim o
     título "Seus streamings" começa na mesma coluna dos outros títulos do painel */
  .provider-field{
    margin:4px -12px 0; padding:12px; border:1px solid transparent; border-radius:14px;
  }
  .provider-field .section-label{ margin-top:0; }
  .provider-field.has-error{
    border-color:var(--rose); background:rgba(var(--rose-rgb), .08);
    animation:provider-attention .48s ease 2;
  }
  .provider-error:empty{ min-height:0; margin:0; }
  .provider-error{
    min-height:18px; margin:8px 0 0; color:var(--rose);
    font-size:12px; font-weight:700; line-height:1.4; text-align:left;
  }
  @keyframes provider-attention{
    50%{ transform:translateX(3px); background:rgba(var(--rose-rgb), .14); }
  }
  @media (prefers-reduced-motion:reduce){
    .provider-field.has-error{ animation:none; }
  }

  /* ---- resultado: uma ação principal só ----
     "Abrir no Netflix" morava dentro do card e "Recomeçar" ganhava um botão dourado de
     largura inteira: a saída da decisão pesava mais que a decisão. Aqui a ação dourada é
     sempre a que leva ao play (ou ao sorteio, quando há mais de um match), e começar de
     novo desce pro peso de link. */
  .results-actions{ display:flex; flex-direction:column; gap:10px; }
  .results-actions .cta{ margin-top:0; }
  /* O VEREDITO OCUPA O LUGAR DO BOTÃO DE SORTEAR — e por isso tem a altura e a régua de um
     botão, não de um parágrafo solto: se encolhesse, a tela inteira subiria um degrau no
     instante em que a decisão acontece, e o olho leria "sumiu alguma coisa" em vez de
     "ficou decidido". Material de peça apoiada com aresta dourada, nunca preenchimento
     dourado: ouro cheio é a marca da AÇÃO, e aqui não há mais nada a fazer. */
  .results-verdict{
    display:flex; align-items:center; justify-content:center; gap:8px;
    margin:0; padding:14px; min-height:48px;
    border:1px solid var(--edge-gold); border-radius:var(--radius-md);
    background:rgba(var(--gold-rgb), .07);
    color:var(--text-soft); font-size:13.5px; font-weight:700; text-align:center;
  }
  .results-verdict svg{ flex:none; color:var(--gold-ink); }
  .results-verdict b{ color:var(--gold-ink); font-weight:800; }
  /* nome legado: é o botão SECUNDÁRIO da gramática (material elevado + aresta),
     não o fantasma. Aqui fica só a largura que o rodapé de resultado pede. */
  .btn-ghost{
    display:flex; width:100%; padding:14px; border-radius:var(--radius-md);
    font-family:inherit; font-size:13.5px; font-weight:700;
  }
  .btn-ghost svg{ flex:none; }

  /* ---- sorteio entre os matches ---- */
  .raffle-panel{ text-align:center; }
  /* ---- TIRAR NA SORTE: as cartas (1.66.0, escolha do dono entre três versões:
     https://claude.ai/artifact/XQvNZ3dMpBinzB4YqPqrpi) ----
     Os títulos aparecem de frente, viram de costas, embaralham e são dados na mesa; alguém
     toca numa carta. Cada carta é um botão em 3D com duas faces; a posição, o giro e o
     tamanho moram em variáveis que o script troca, e a transição anda sozinha entre elas. */
  .raffle-mesa{ position:relative; width:100%; margin:14px 0 4px; perspective:900px; transition:height .4s var(--ease-standard); }
  .raffle-carta{
    position:absolute; left:0; top:0; padding:0; border:0; background:none; color:var(--on-image); font:inherit; cursor:default;
    width:var(--w, 90px); aspect-ratio:2/3; transform-style:preserve-3d; z-index:var(--z, 1);
    transform:translate(var(--x, 0px), var(--y, 0px)) rotate(var(--r, 0deg)) rotateY(var(--vira, 0deg)) scale(var(--s, 1));
    transition:transform .5s cubic-bezier(.3,.7,.2,1), filter .4s, opacity .4s;
  }
  .raffle-carta.pode{ cursor:pointer; }
  .raffle-carta:focus-visible{ outline:2px solid var(--gold); outline-offset:4px; border-radius:var(--radius-sm); }
  .raffle-face, .raffle-verso{ position:absolute; inset:0; border-radius:var(--radius-sm); overflow:hidden;
    backface-visibility:hidden; -webkit-backface-visibility:hidden; box-shadow:0 10px 18px -8px rgba(0,0,0,.85); }
  .raffle-face{ background:var(--surface-3-bot); display:flex; align-items:flex-end; }
  .raffle-face img{ position:absolute; inset:0; width:100%; height:100%; object-fit:cover; }
  .raffle-face b{ position:relative; width:100%; padding:14px 6px 5px; font-size:11px; font-weight:800; line-height:1.15; text-align:left;
    background:linear-gradient(180deg, transparent, rgba(0,0,0,.85)); color:var(--on-image);
    display:-webkit-box; -webkit-line-clamp:2; -webkit-box-orient:vertical; overflow:hidden; }
  .raffle-face .raffle-sem-arte{ position:absolute; inset:0; display:grid; place-items:center; color:var(--text-dim); }
  /* o verso é objeto da sala (não segue o tema): o baralho da casa, escuro com o coração em ouro */
  .raffle-verso{ transform:rotateY(180deg); border:2px solid var(--gold-deep, #c8901f); display:grid; place-items:center;
    background:radial-gradient(circle at 50% 50%, #1d1a22 0 26%, transparent 27%), repeating-linear-gradient(45deg, #15131b 0 6px, #1f1b26 6px 12px); }
  .raffle-verso svg{ width:36%; height:auto; color:#e8b54d; }
  .raffle-carta.fora{ filter:grayscale(.7) brightness(.5); }
  .raffle-carta.tirada .raffle-face{ box-shadow:0 0 0 3px var(--gold), 0 18px 30px -10px rgba(0,0,0,.9); }
  @media (prefers-reduced-motion: reduce){ .raffle-carta, .raffle-mesa{ transition:none; } }
  .raffle-name{
    font-family:'Bebas Neue'; font-size:26px; letter-spacing:.8px; line-height:1.1;
    min-height:34px; display:flex; align-items:center; justify-content:center;
  }
  .raffle-status{ font-size:12.5px; color:var(--text-dim); margin:0 0 16px; }

  /* ---- prévia do card de compartilhamento ----
     mostrar o card antes de compartilhar resolve dois problemas de uma vez: o card fica
     visível (é o ponto do recurso) e o navigator.share passa a ser chamado do toque no
     botão daqui — gesto fresco que o iOS exige, com a imagem já pronta. */
  .share-preview{
    width:100%; max-width:300px; display:block; margin:0 auto;
    border-radius:16px; box-shadow: 0 16px 38px rgba(0,0,0,.55);
  }

  /* ---- QR da sala ---- */
  .qr-box{
    display:flex; flex-direction:column; align-items:center; gap:10px;
    margin-top:14px; padding:16px; border-radius:16px;
    border:1px solid var(--line); background: var(--bg-panel);
  }
  /* o QR precisa de zona de silêncio e contraste real pra ler: fundo branco sólido, não o
     dourado da marca. Legibilidade da câmera vence a paleta aqui. Sem width/height: o canvas
     aparece no tamanho natural, porque redimensionar por CSS borra os módulos. */
  .qr-canvas{ display:block; border-radius:8px; background:#fff; image-rendering: pixelated; }
  .qr-box p{ margin:0; font-size:12px; color:var(--text-dim); text-align:center; line-height:1.45; }

  /* ---- sobre e fontes de dados ---- */
  .source-row{
    display:flex; align-items:center; gap:12px;
    padding:12px 0; border-bottom:1px solid var(--line);
  }
  .source-row:last-of-type{ border-bottom:none; }
  .source-row p{ margin:0; font-size:12px; color:var(--text-dim); line-height:1.5; }
  .source-mark{ flex:none; display:flex; align-items:center; }
  .source-row--stack{ flex-direction:column; align-items:flex-start; gap:10px; }
  /* O wordmark oficial fica na mesma linha da atribuição. Embutido no HTML, ele não depende
     de um asset separado no cache da PWA; 96px mantém a marca proporcional sem dominar o bloco. */
  /* a linha do TMDB não precisa mais declarar o alinhamento: ele virou regra da anatomia */
  .source-row--tmdb{ flex-direction:row; gap:12px; }
  .source-tmdb-logo{ width:96px; height:auto; display:block; flex:none; }
  .source-row--tmdb p{ flex:1; min-width:0; }
  /* mesma largura do wordmark do TMDB acima: alinha as duas marcas e faz os dois
     parágrafos de atribuição começarem na mesma coluna */
  .source-jw{
    flex:none; display:flex; align-items:center; justify-content:center;
    height:26px; min-width:96px; padding:0 9px; border-radius:5px;
    background:#fbc51b; color:#000; font-size:11px; font-weight:800; letter-spacing:.4px;
  }
  .source-links{ display:flex; flex-wrap:wrap; gap:14px; margin-top:12px; }
  .source-links a{ min-width:44px; min-height:44px; display:inline-flex; align-items:center; font-size:12px; color:var(--gold-dim); font-weight:700; }
  /* na tela de match os quase-matches ficam recolhidos: o momento é de celebrar e apertar
     o play, não de comparar alternativas. Aberto, mostra TODOS — antes só o primeiro
     colocado aparecia, escolhido arbitrariamente entre empatados. */
  /* O BLOCO DOS INCLUÍDOS É UMA SEGUNDA LISTA, e a régua acima dele é o que diz isso.
     Ele não tem material próprio: os cartões são os mesmos da lista de cima, porque um
     título incluído participa da mesma decisão. O que muda é o NOME em cima deles —
     "Quase bateram" — e a linha que explica por que estão ali. Dar caixa ou cor ao bloco
     o transformaria numa peça à parte, e ele não é: é o resto da mesma lista. */
  .results-quase{ margin-top:18px; border-top:1px solid var(--rule); padding-top:4px; }
  .results-quase .section-label{ margin-bottom:2px; display:flex; align-items:center; gap:7px; }
  .results-quase .section-label svg{ flex:none; color:var(--gold-ink); }
  .results-quase-sub{ font-size:12.5px; color:var(--text-dim); line-height:1.45; margin-bottom:12px; }

  /* ---- O CARTAZ NA FRENTE (1.64.0, escolha do dono) ----
     O título sugerido vem grande, com o próprio pôster e o véu que escurece o pé: é FOTO nos
     dois temas, então tudo escrito aqui lê as tintas --on-image (que não trocam). O cartaz
     inteiro é um botão (abre o detalhe); a decisão mora logo embaixo, com UM dourado. */
  .results-wrap--cartaz{ gap:16px; }
  .res-heroi{
    position:relative; isolation:isolate; overflow:hidden; width:100%;
    min-height:clamp(260px, 72vw, 340px); padding:16px;
    display:flex; flex-direction:column; justify-content:flex-end; align-items:flex-start; gap:4px;
    border-radius:var(--radius-lg); border:1px solid var(--edge);
    background:linear-gradient(160deg, var(--card-top), var(--card-bot));
    color:var(--on-image); text-align:left; font:inherit; cursor:pointer;
  }
  .res-heroi-img{ position:absolute; inset:0; width:100%; height:100%; object-fit:cover; z-index:-2; }
  .res-heroi::after{
    content:""; position:absolute; inset:0; z-index:-1; pointer-events:none;
    background:linear-gradient(180deg, rgba(0,0,0,.08) 22%, rgba(0,0,0,.55) 55%, rgba(0,0,0,.92));
  }
  .res-heroi:focus-visible{ outline:2px solid var(--gold); outline-offset:3px; }
  .res-heroi-rotulo{ display:flex; align-items:center; gap:6px; font-size:12.5px; font-weight:800; color:var(--gold-on-image); }
  .res-heroi-rotulo .ico-svg{ flex:none; }
  .res-heroi-nome{
    font-family:var(--font-display); font-weight:var(--t-display-weight);
    font-size:clamp(34px, 11vw, 46px); line-height:.92; letter-spacing:.02em; text-wrap:balance;
  }
  .res-heroi-meta{ font-size:12.5px; line-height:1.4; color:var(--on-image-dim); }
  .res-decisao{ display:flex; flex-direction:column; gap:12px; }
  .res-decisao .cta{ width:100%; display:flex; justify-content:center; margin-top:0; }
  .res-formas{ display:flex; flex-direction:column; align-items:center; gap:8px; }
  .res-formas p{ margin:0; font-size:var(--t-support-size); color:var(--text-dim); }
  .res-chips{ display:flex; flex-wrap:wrap; justify-content:center; gap:8px; }
  .res-chips button{
    min-height:44px; padding:0 16px; border-radius:999px; cursor:pointer;
    display:inline-flex; align-items:center; gap:7px;
    border:1px solid var(--edge); border-top-color:var(--edge-lit);
    background:linear-gradient(180deg, var(--surface-2-top), var(--surface-2-bot));
    color:var(--text); font:inherit; font-size:13.5px; font-weight:700;
  }
  .res-chips .ico-svg{ flex:none; color:var(--gold-ink); }
  .res-faixa-bloco{ display:flex; flex-direction:column; gap:8px; }
  .res-faixa-bloco .results-quase-sub{ margin-bottom:0; }
  .res-faixa{ display:flex; gap:10px; overflow-x:auto; padding-bottom:4px; scroll-snap-type:x proximity; }
  .res-faixa-item{
    flex:none; width:96px; display:flex; flex-direction:column; gap:4px; padding:0;
    background:none; border:none; color:var(--text); font:inherit; text-align:left; cursor:pointer;
    scroll-snap-align:start;
  }
  .res-faixa-item .rank-thumb{ width:96px; --pf-radius:var(--radius-sm); }
  .res-faixa-item:focus-visible{ outline:2px solid var(--gold); outline-offset:2px; border-radius:var(--radius-sm); }
  .res-faixa-nome{
    font-size:12.5px; font-weight:700; line-height:1.25;
    display:-webkit-box; -webkit-line-clamp:2; -webkit-box-orient:vertical; overflow:hidden;
  }
  .res-faixa-leg{ font-size:11.5px; line-height:1.3; color:var(--text-dim); }
  /* O SELO EM CIMA DO CARTAZ (1.64.1): quantos curtiram e a estrela da super curtida. Pousa
     sobre o pôster, então lê as tintas que não trocam com o tema; o match (N/N) escreve em
     ouro, o quase em creme — é o número que diz que não foi unânime. */
  .res-faixa-item{ position:relative; }
  .res-faixa-selo{
    position:absolute; top:6px; left:6px; z-index:3;
    display:inline-flex; align-items:center; gap:3px; padding:4px 7px; border-radius:999px;
    background:rgba(0,0,0,.76); border:1px solid rgba(255,255,255,.16);
    color:var(--on-image); font-size:11.5px; font-weight:800; line-height:1; font-variant-numeric:tabular-nums;
  }
  .res-faixa-selo.is-match{ color:var(--gold-on-image); border-color:color-mix(in srgb, var(--gold-on-image) 55%, transparent); }
  .res-faixa-selo .ico-svg{ flex:none; color:var(--gold-on-image); }
  /* sem cartaz grande, a faixa É a tela: cartazes maiores, o terceiro espiando na borda */
  .res-faixa-bloco--principal .res-faixa-item,
  .res-faixa-bloco--principal .res-faixa-item .rank-thumb{ width:128px; }
  .res-faixa-bloco--principal .res-faixa-nome{ font-size:13.5px; }
  /* o resto vira três portas discretas: nenhuma delas é a decisão */
  .res-rodape{ display:flex; justify-content:space-around; gap:4px; padding-top:8px; border-top:1px solid var(--rule); }
  .res-rodape button{
    min-width:72px; min-height:52px; padding:4px 6px; cursor:pointer;
    display:flex; flex-direction:column; align-items:center; justify-content:center; gap:4px;
    background:none; border:none; border-radius:var(--radius-sm);
    color:var(--text-dim); font:inherit; font-size:12px; font-weight:600;
  }
  .res-rodape button:hover, .res-rodape button:focus-visible{ color:var(--text); }
  .res-rodape button:focus-visible{ outline:2px solid var(--gold); outline-offset:1px; }
  #notaAppOverlay .rate-card h3{ margin:0 0 10px; }

  /* ---- O PLACAR DO RESULTADO (1.65.0, escolha do dono) ----
     Sem cartaz grande, a tela é uma lista na ordem: pôster (abre o detalhe), texto e a marca
     (a linha inteira marca). Os pontinhos são as pessoas: ouro no match, creme no quase. */
  .placar-res{ display:flex; flex-direction:column; gap:10px; }
  .placar-dica{ margin:0; text-align:center; font-size:var(--t-support-size); color:var(--text-dim); line-height:1.4; }
  .placar-lista{ list-style:none; margin:0; padding:0; border:1px solid var(--edge); border-radius:var(--radius-lg); overflow:hidden;
    background:linear-gradient(180deg, var(--surface-2-top), var(--surface-2-bot)); }
  .placar-linha{ position:relative; display:flex; align-items:stretch; }
  .placar-linha + .placar-linha, .placar-separa + .placar-linha{ border-top:1px solid var(--edge); }
  .placar-separa{ padding:7px 12px; border-top:1px solid var(--edge); font-size:11.5px; font-weight:700; color:var(--text-dim);
    background:rgba(var(--bg-rgb), .35); }
  .placar-capa{ flex:none; padding:8px 0 8px 10px; background:none; border:0; cursor:pointer; }
  .placar-capa .rank-thumb{ width:46px; --pf-radius:var(--radius-sm); }
  .placar-capa:focus-visible{ outline:2px solid var(--gold); outline-offset:-2px; }
  .placar-marcar{ flex:1; min-width:0; min-height:64px; display:flex; align-items:center; gap:10px; padding:8px 12px 8px 10px;
    background:none; border:0; color:var(--text); font:inherit; text-align:left; cursor:pointer; }
  .placar-marcar:focus-visible{ outline:2px solid var(--gold); outline-offset:-2px; }
  .placar-info{ flex:1; min-width:0; display:flex; flex-direction:column; gap:3px; }
  .placar-nome{ font-size:14px; font-weight:700; line-height:1.25; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
  .placar-meta{ font-size:11.5px; color:var(--text-dim); line-height:1.3; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
  .placar-curtidas{ display:flex; align-items:center; gap:4px; font-size:11.5px; color:var(--text-soft); font-variant-numeric:tabular-nums; }
  .placar-curtidas i{ width:7px; height:7px; border-radius:50%; background:var(--edge-strong); }
  .placar-curtidas i.on{ background:var(--text-soft); }
  .placar-linha.is-match .placar-curtidas i.on{ background:var(--gold); }
  .placar-curtidas span{ margin-left:2px; }
  .placar-curtidas b{ display:inline-flex; align-items:center; gap:3px; margin-left:4px; color:var(--gold-ink); font-weight:700;
    min-width:0; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
  .placar-curtidas b .ico-svg{ flex:none; }
  .placar-check{ flex:none; width:24px; height:24px; border-radius:50%; border:2px solid var(--edge-strong); display:grid; place-items:center; color:transparent; }
  .placar-marcar[aria-pressed="true"]{ background:rgba(var(--gold-rgb), .09); }
  .placar-linha:has(.placar-marcar[aria-pressed="true"]){ background:rgba(var(--gold-rgb), .09); }
  .placar-marcar[aria-pressed="true"] .placar-check{ border-color:var(--gold); background:var(--gold); color:var(--on-gold); }
  .placar-doca{ display:flex; flex-direction:column; gap:8px; }
  .placar-doca .cta{ width:100%; display:flex; justify-content:center; margin-top:0; }
  .placar-doca-rotulo{ margin:0; text-align:center; font-size:var(--t-support-size); color:var(--text-dim); }
  .placar-doca-rotulo b{ color:var(--text); }
  .placar-doca-alt{ display:flex; justify-content:center; flex-wrap:wrap; gap:4px 16px; }
  .placar-doca-alt button{ min-height:44px; display:inline-flex; align-items:center; gap:6px; padding:0 8px; background:none; border:0;
    color:var(--text-soft); font:inherit; font-size:13.5px; font-weight:700; cursor:pointer; }
  .placar-doca-alt button .ico-svg{ color:var(--gold-ink); }
  .placar-doca-alt button:focus-visible{ outline:2px solid var(--gold); outline-offset:1px; border-radius:var(--radius-sm); }
  .placar-doca-alt .placar-limpar{ color:var(--text-dim); font-weight:600; text-decoration:underline; text-underline-offset:3px; }

  .near-reveal{ border:1px dashed var(--line); border-radius:16px; background:rgba(var(--gold-rgb), .04); }
  .near-reveal summary{
    display:flex; align-items:center; gap:8px; cursor:pointer;
    padding:14px 16px; min-height:44px; box-sizing:border-box;
    font-size:13px; font-weight:700; color:var(--text-dim); list-style:none;
  }
  .near-reveal summary::-webkit-details-marker{ display:none; }
  .near-reveal summary::after{
    content:''; margin-left:auto; width:8px; height:8px; flex:none;
    border-right:2px solid var(--gold-dim); border-bottom:2px solid var(--gold-dim);
    transform:rotate(45deg) translateY(-2px); transition: transform .2s;
  }
  .near-reveal[open] summary{ color:var(--text); }
  .near-reveal[open] summary::after{ transform:rotate(-135deg) translateY(-2px); }
  /* .near-list tem display:flex, e definir display num filho direto de <details> fechado
     vence o display:none do navegador — sem este par, o disclosure não escondia nada */
  .near-reveal:not([open]) .near-list{ display:none; }
  .near-reveal .near-list{ padding:0 16px 16px; }
  .near-reveal .near-list li .runner-add-btn{ margin-left:auto; }
  /* quase-matches numa sala remota: lista pra decidir em voz alta, já que revotar exigiria
     uma coluna de rodada nos votos que o banco ainda não tem */
  .near-list{ list-style:none; display:flex; flex-direction:column; gap:10px; }
  .near-list li{ display:flex; align-items:center; gap:12px; }
  .near-list b{ display:block; font-size:14px; line-height:1.25; }
  .near-list span{ font-size:11px; color:var(--text-dim); }

  /* AVALIAÇÃO */
  .rate-card{
    text-align:center; padding:14px;
    border:1px solid var(--edge); border-top-color:var(--edge-lit); border-radius:var(--radius-md);
    background: linear-gradient(180deg, var(--surface-1-top), var(--surface-1-bot));
    box-shadow: var(--shadow-1), inset 0 1px 0 var(--sheen-1);
  }
  .star-row{ display:flex; justify-content:center; gap:8px; }
  /* a estrela vazia é CONTROLE, não enfeite: na cor da aresta ela dava 1,8:1 e sumia (a
     auditoria mediu; o piso para um controle é 3:1). A tinta de apoio passa nos dois temas. */
  .star{ font-size:26px; color:var(--text-dim); cursor:pointer; transition: color var(--dur-1) var(--ease-standard), transform var(--dur-1) var(--ease-standard); background:none; border:none; width:44px; height:44px; padding:0; line-height:1; display:flex; align-items:center; justify-content:center; }
  .star:hover{ color: var(--gold-ink); transform: scale(1.15); }
  /* as duas portas do pós-avaliação dividem a linha em partes iguais: nenhuma das duas
     pode parecer a saída "de verdade" e a outra a consolação */
  .rate-followup{ display:flex; gap:8px; }
  .rate-followup > *{ flex:1 1 0; min-width:0; text-align:center; text-decoration:none; }
  @media (max-width:360px){ .rate-followup{ flex-direction:column; } }

  /* HANDOFF SCREEN */
  .handoff-wrap{ width:100%; max-width:340px; margin:0 auto; text-align:center; display:flex; flex-direction:column; align-items:center; padding-top:14px; }
  .handoff-card{
    width:100%; position:relative; overflow:hidden;
    background: linear-gradient(168deg, var(--surface-3-top), var(--panel-deep) 65%);
    border:1px solid var(--edge-strong); border-top-color:var(--edge-lit); border-radius:var(--radius-xl);
    padding:30px 22px 24px;
    box-shadow: var(--shadow-3), inset 0 1px 0 var(--sheen-3);
  }
  .handoff-card::before{
    content:''; position:absolute; top:-70px; left:50%; transform:translateX(-50%);
    width:240px; height:150px; border-radius:50%;
    background: radial-gradient(closest-side, rgba(var(--gold-rgb), .16), transparent);
    pointer-events:none;
  }
  .handoff-emoji{ margin-bottom:6px; display:block; }
  .handoff-emoji .tut-art{ width:150px; }
  /* só o aparelho balança, como se passasse de uma mão pra outra */
  .handoff-emoji .art-phone{ animation: handoff-slide 2s ease-in-out infinite; transform-origin:60px 43px; }
  @keyframes handoff-slide{
    0%,100%{ transform: translateX(-9px) rotate(-5deg); }
    50%{ transform: translateX(9px) rotate(5deg); }
  }
  @media (prefers-reduced-motion: reduce){ .handoff-emoji{ animation:none; } }
  /* EXCEÇÃO de caixa alta: é a legenda de um cartaz de troca de turno, lida a
     um metro de distância e antes do nome grande */
  .handoff-title{ font-size:12px; color:var(--text-dim); text-transform:uppercase; letter-spacing:2px; font-weight:700; }
  .handoff-name{ font-family:var(--font-display); font-size:40px; color:var(--gold-ink); letter-spacing:1px; margin-top:4px; line-height:1; }
  .handoff-dots{ display:flex; gap:7px; justify-content:center; margin:16px 0 2px; }
  .handoff-dots i{
    width:9px; height:9px; border-radius:50%;
    background:var(--line); border:1px solid transparent;
  }
  /* nomes das últimas partidas: um toque em vez de teclado no meio da troca */
  .handoff-recent{ display:flex; flex-wrap:wrap; gap:8px; justify-content:center; }
  .handoff-recent:empty{ display:none; }
  .handoff-recent .chip{ padding:6px 14px; min-height:44px; font-size:13px; }
  .handoff-dots i.done{ background:var(--gold); }
  .handoff-dots i.next{ background:transparent; border-color:var(--gold); box-shadow:0 0 8px rgba(var(--gold-rgb), .5); }
  .avatar.active{ background:transparent; border-color:var(--gold); color:var(--gold-ink); }


  /* ================= AJUSTES em largura: centro de controle real =================
     Não é a folha do celular esticada: são DUAS REGIÕES com trabalhos diferentes.
     À esquerda o ESTADO fica parado (é leitura, não rola). À direita o INVENTÁRIO
     rola sozinho dentro da folha — por isso o painel nunca fica cortado embaixo,
     como ficava quando a folha inteira era o contêiner de rolagem.

     A segunda condição é a que resolve o zoom de 200% e o celular deitado: quando
     sobra largura mas falta ALTURA, empilhar é o pior arranjo possível — o recorte
     inicial mostrava só o overview. Em duas regiões, estado e controle aparecem
     juntos na primeira tela. */
  /* Pilha em altura curta — 200% de zoom (1366 vira 683×384 px CSS) e celular
     deitado estreito. Sem largura para duas regiões, o que resolve é encolher o
     masthead: o mesmo raciocínio que o Início já aplica em max-height:540px.
     A regra de colunas não muda; só o ar diminui. */
  @media (max-width:779px) and (max-height:560px){
    #settingsModal .modal-sheet{ max-height:100%; }
    /* altura curta: a rampa encolhe junto com o resto do ar, mas continua sendo
       a MESMA curva — o que muda é a distância, não a gramática */
    #settingsModal{ --set-veil:34px; }
    #settingsModal .modal-scroll{ padding-bottom: calc(var(--set-veil) + 6px); }
    /* sem puxador para anunciar, a zona de arraste é só respiro — e respiro é a
       primeira coisa a sair quando a tela tem 384 px de altura */
    #settingsModal .modal-drag-zone{ display:none; }
    #settingsModal .modal-title{ font-size:24px; padding-top:0; }
    #settingsModal .modal-sub{ margin-top:4px; max-width:none; }
    #settingsModal .settings-header{ padding:6px 52px 6px 2px; }
    #settingsModal .settings-group{ padding-top:8px; }
    #settingsModal .settings-save{ margin-bottom:8px; }
    #settingsModal .settings-group-summary{ margin:3px 0 6px; }
  }

  /* A grade exige largura E folga: a 200% de zoom (1366 vira 683 px CSS) a
     segunda condição ainda casava e a coluna de controles mostrava um switch
     por vez. Com o piso em 780 px o zoom cai para a pilha do celular, e o
     celular deitado — 800 e 844 px de largura — continua em duas regiões. */
  @media (min-width:780px) and (max-height:620px) and (display-mode:browser){
    #settingsModal{ align-items:center; padding:24px; }
    #settingsModal .modal-sheet{
      width:min(820px, calc(100vw - 48px)); max-width:820px; max-height:min(92vh,760px);
      border-bottom:1px solid var(--edge-strong); border-radius:var(--radius-xl);
      transform:translateY(24px) scale(.985);
      box-shadow: var(--shadow-3), inset 0 1px 0 var(--sheen-3);
    }
    #settingsModal.show .modal-sheet{ transform:translateY(0) scale(1); }
    #settingsModal .modal-drag-zone{ display:none; }
    /* a rolagem sai do contêiner inteiro e vai para a região de controles */
    #settingsModal .modal-scroll{
      overflow:hidden; display:flex; flex-direction:column;
      flex:1; min-height:0; padding:20px 30px 22px;
    }
    #settingsModal .settings-header{ padding:0 48px 10px 2px; }
    /* uma coluna só, centrada: com três grupos e nenhuma região de estado, a grade de
       duas colunas deixaria um terço da folha vazio ao lado do que rola */
    #settingsModal .settings-control-center{
      display:flex; flex-direction:column; flex:1; min-height:0;
      width:100%; max-width:660px; margin-inline:auto;
    }
    #settingsModal .settings-groups{
      /* o respiro de baixo precisa ser maior que a rampa inteira, senão o fim da
         rolagem apagaria o último card de verdade */
      overflow-y:auto; min-height:0; flex:1; padding:0 4px calc(var(--set-veil) + 8px);
      scrollbar-width:none; -ms-overflow-style:none;
      overscroll-behavior: contain;
      -webkit-mask-image: var(--set-veil-fade);
              mask-image: var(--set-veil-fade);
    }
    #settingsModal .settings-groups::-webkit-scrollbar{ display:none; }
    #settingsModal .settings-group:first-child{ padding-top:8px; }
    /* a zona de dados sai da região que rola e vira o degrau antes do rodapé: rara
       demais para disputar a rolagem com os controles, perigosa demais para sumir */
    #settingsModal .settings-danger{ flex:none; margin-top:14px; }
    /* na largura o contêiner de rolagem é a COLUNA, não a folha: o véu da folha sai
       (ele fatiaria o masthead e o rodapé, que não rolam) e passa para os grupos. */
    #settingsModal .modal-scroll{ -webkit-mask-image:none; mask-image:none; padding-bottom:22px; }
    #settingsModal .modal-sheet::after{ display:none; }
    /* barra de baixo: Concluir sempre à mão sem obrigar a rolar o inventário até o fim */
    #settingsModal .settings-footer{
      flex:none; display:flex; flex-direction:row-reverse;
      align-items:center; justify-content:space-between; gap:16px;
      margin-top:2px; padding:12px 0 0; border-top:1px solid var(--rule-strong);
    }
    #settingsModal .settings-footer .modal-close{ width:auto; min-width:170px; padding-inline:24px; }
    #settingsModal .settings-version{ margin:0; text-align:left; }
    #settingsModal .settings-save{ order:-1; margin:0; justify-content:flex-start; }
  }

  /* largura de sobra mas altura curta: o masthead encolhe para o ledger e o
     primeiro grupo caberem juntos na primeira tela */
  @media (min-width:780px) and (max-height:620px) and (display-mode:browser){
    #settingsModal .modal-scroll{ padding:16px 24px 16px; }
    #settingsModal .modal-title{ font-size:27px; padding-top:0; }
    #settingsModal .settings-header{ padding-bottom:12px; }
    /* a linha de apoio ganha a largura toda do masthead: em altura curta ela cabe numa
       linha só, e a linha economizada é o que faz o primeiro grupo aparecer inteiro */
    #settingsModal .modal-sub{ margin-top:4px; max-width:none; }
    #settingsModal .settings-group{ padding-top:14px; }
    #settingsModal .settings-danger{ margin-top:14px; }
    #settingsModal .settings-footer{ padding-top:10px; }
  }



  /* ======================= 1.51.0 — A NOITE DE CINEMA INTEIRA =======================
     As peças de js/11-noite-de-cinema.js. Tudo lê token: o tema claro continua sendo só
     troca de token, e nada aqui pede exceção no bloco `:root[data-tema="claro"]`. */

  /* ---- a super curtida: a quarta peça da fileira, do tamanho do desfazer ----
     Ela é o espelho do desfazer do outro lado (os dois pequenos por fora, os dois grandes no
     meio) e por isso a fileira aperta o vão quando ela aparece: 44+64+64+48 com 40 de vão
     não cabem nos 343px de um iPhone de 375. Ouro de TINTA e de aresta, nunca preenchido: o
     ouro cheio pertence ao match. */
  .actions.tem-super{ gap:24px; }
  .action-btn.super{
    position:relative; width:48px; height:48px; align-self:center;
    color:var(--gold-ink); border-color:var(--edge-gold);
  }
  .action-btn.super[hidden]{ display:none; }

  /* LETRA DO SISTEMA GRANDE = CARTA ESTREITA (1.66.3). Com a letra do celular em 150–200% o
     app é desenhado em 250–187px, e a carta se desmontava: a faixa dos botões ("+ Minha
     lista", "Ver detalhes") tem largura FIXA de 118px e o título reservava 132 de uma carta
     de 174 — o nome sobrava com 40px e os botões pousavam em cima dele; na fileira de voto,
     44+64+64 com 40 de vão não cabiam em 148px, e os botões viravam ovais de 19px.
     (Auditoria cega de 2026-10-05.) Na carta estreita os botões saem do canto e vão para
     BAIXO do título, como dois quadrados de 44px só com o ícone — o nome continua no
     aria-label de cada um, e em texto eles não cabiam lado a lado nem em 250px. A fileira de
     voto encolhe os alvos e o vão em degraus, e nenhum botão de voto encolhe sozinho
     (flex-shrink:0): um círculo espremido em oval é o pior dos dois mundos. */
  .card-save .card-tool-ico{ display:none; }
  .card-tool-ico{ display:inline-flex; }
  .action-btn{ flex-shrink:0; }
  /* CARTA A CARTA (1.67.0): o nome de quem vota agora pousa no meio do cartaz e some. Nasce
     por appendChild, então anima com `animation` e sem preenchimento: o repouso é o estado
     final (sumido), e onde a animação não roda a faixa simplesmente não aparece. */
  .card-vez{
    position:absolute; left:50%; top:42%; z-index:3; transform:translate(-50%, -50%);
    padding:10px 18px; border-radius:var(--radius-pill); pointer-events:none;
    background:rgba(0,0,0,.72); border:1px solid var(--gold-on-image-edge);
    color:var(--gold-on-image); font-family:'Bebas Neue'; font-size:26px; letter-spacing:.6px; white-space:nowrap;
    opacity:0; animation:card-vez 1.4s var(--ease-standard);
  }
  @keyframes card-vez{ 0%{ opacity:0; transform:translate(-50%, -40%) scale(.92); } 15%, 70%{ opacity:1; transform:translate(-50%, -50%) scale(1); } 100%{ opacity:0; } }
  @media (prefers-reduced-motion: reduce){ .card-vez{ animation:card-vez-parado 1.4s steps(1); } @keyframes card-vez-parado{ 0%, 99%{ opacity:1; } } }
  @container deck (max-width: 300px){
    .poster{ flex-direction:column; align-items:stretch; justify-content:flex-end; padding:14px; }
    .poster-copy--inset{ padding-right:0; }
    .poster-title{ font-size:24px; }
    .card-tools{ position:static; flex-direction:row; justify-content:flex-start; width:auto; max-width:none; margin-top:8px; }
    .card-details, .card-save{ width:44px; min-width:44px; height:44px; padding:0; }
    .card-tool-rotulo{ display:none; }
    .card-save .card-tool-ico{ display:inline-flex; }
    .poster-emoji{ top:14px; right:14px; max-width:calc(100% - 28px); overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
  }
  @media (max-width:300px){
    .actions, .actions.tem-super{ gap:12px; }
    .action-btn{ width:52px; height:52px; }
    .action-btn.undo, .action-btn.super{ width:40px; height:40px; }
  }
  @media (max-width:220px){
    .actions, .actions.tem-super{ gap:6px; }
    .action-btn{ width:46px; height:46px; }
    .action-btn.undo, .action-btn.super{ width:36px; height:36px; }
  }
  .super-conta{
    position:absolute; top:-5px; right:-5px; min-width:20px; height:20px; padding:0 5px;
    display:flex; align-items:center; justify-content:center;
    border-radius:var(--radius-pill); font-size:11px; font-weight:800;
    color:var(--gold-ink); background:var(--bg); border:1px solid var(--edge-gold);
  }
  .card.is-super{ box-shadow:0 0 0 2px rgba(var(--gold-rgb), .7), 0 18px 48px rgba(var(--gold-rgb), .28); }
  .super-tag{
    display:inline-flex; align-items:center; gap:5px;
    font-size:11px; font-weight:800; color:var(--gold-ink);
    border:1px solid var(--edge-gold); border-radius:var(--radius-pill);
    background:rgba(var(--gold-rgb), .09);
    padding:3px 9px; margin-top:6px; letter-spacing:.3px;
  }
  /* o respiro vai DEPOIS da marca, e não antes da seguinte: quando a seguinte desce para a
     linha de baixo, uma margem à esquerda a deixaria 6px fora do alinhamento das outras */
  .unanimous-tag:has(+ .super-tag), .super-tag:has(+ .drawn-tag){ margin-right:6px; }

  /* ---- o porquê do card: no alto do pôster, um motivo por vez ----
     Mora onde o cronômetro mora quando não há cronômetro, e desce para baixo dele quando
     há. A largura para antes da pastilha do streaming, que é a outra peça do alto. */
  .card-motivo{
    position:absolute; top:20px; left:20px; z-index:1;
    max-width:calc(100% - 150px);
    display:flex; flex-direction:column; gap:4px;
    font-size:12.5px; font-weight:700; line-height:1.3;
    color:rgba(255,255,255,.95); background:rgba(0,0,0,.66);
    border:1px solid rgba(var(--gold-rgb), .45); border-radius:var(--radius-sm);
    padding:6px 10px; pointer-events:none;
    opacity:0; transform:translateY(-4px);
    transition:opacity .25s var(--ease-standard), transform .25s var(--ease-standard);
  }
  .card-motivo.is-on{ opacity:1; transform:none; }
  .card-motivo--abaixo{ top:58px; }
  .card-motivo--lista{ transition:none; }
  .poster-meta-onde{ display:none; }
  /* na carta estreita (letra grande, ver o bloco da 1.66.3 mais acima) a reserva de 150px
     para o streaming deixava o motivo com 32px, e "Nota 8,9 no TMDB" virava quatro linhas
     em cima do título: ele desce para a linha de baixo da pastilha, numa linha só. Na
     estreitíssima ele sai — o leitor de tela já recebe os motivos no anúncio do card — e a
     sinopse cede uma linha para o pôster caber o nome. */
  @container deck (max-width: 300px){
    .card-motivo, .card-motivo--abaixo{ top:50px; left:14px; max-width:calc(100% - 28px); }
    .card-motivo span{ white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
    .poster:has(.card-timer) .poster-emoji{ max-width:calc(100% - 100px); }
    .card-timer{ top:14px; left:14px; }
  }
  @container deck (max-width: 210px){
    .card-motivo{ display:none; }
    .card-body p{ -webkit-line-clamp:2; line-clamp:2; }
    /* o streaming desce do canto para a linha dos dados: no canto ele dividia a altura com
       um título de três linhas e pousava em cima dele */
    .poster-emoji{ display:none; }
    .poster-meta-onde{ display:inline; }
  }
  @media (prefers-reduced-motion: reduce){ .card-motivo{ transition:none; transform:none; } }

  /* ---- a busca: clima, classificação e aluguel ---- */
  .busca-nota{ text-align:left; margin:6px 0 0; }
  /* a última linha antes do rodapé: a régua do rodapé já separa, e a da linha fazia par com ela */
  .busca-aluguel{ margin-top:14px; text-align:left; border-bottom:0; }

  /* ---- o duelo final: dois cartazes, e quem ganha fica ---- */
  #dueloOverlay{ z-index:138; }
  .duelo{ text-align:center; }
  .duelo-conta{ font-size:var(--t-support-size); color:var(--gold-ink); font-weight:700; letter-spacing:.4px; margin:0 0 4px; }
  .duelo-sub{ margin:4px 0 16px; }
  /* O ✕ MIRA O MEIO DOS CARTAZES, NÃO O DA COLUNA (1.55.0, o dono viu torto). Com
     `align-self:center` ele centrava na altura inteira do par — cartaz + nome em 1 ou 2
     linhas + streaming + super curtida —, então caía abaixo do meio do cartaz, e cada par
     caía num lugar. Agora ele se alinha pelo topo e desce metade da altura do cartaz, que é
     conta da LARGURA (cartaz 2/3): coluna = (100cqw − 8·2 de vão − 4 da trilha do meio) / 2,
     meio do cartaz = coluna × 0,75, menos metade do selo (16).
     O selo encosta nos dois cartazes (margem negativa): é um "versus", não um separador. */
  .duelo-par{ display:grid; grid-template-columns:1fr auto 1fr; align-items:start; gap:8px; container-type:inline-size; }
  .duelo-x{
    position:relative; z-index:1; display:grid; place-items:center; width:32px; height:32px; margin:0 -14px;
    margin-top:calc((100cqw - 20px) * .375 - 16px);
    border-radius:50%; background:linear-gradient(180deg, var(--surface-3-top), var(--surface-3-bot)); border:1px solid var(--edge); color:var(--gold-ink);
    font-size:20px; font-weight:800; line-height:1; box-shadow:0 6px 14px -6px rgba(0,0,0,.7);
  }
  .duelo-lado{
    display:flex; flex-direction:column; gap:6px; padding:0; cursor:pointer; min-width:0;
    background:none; border:0; color:var(--text); font:inherit; text-align:center;
  }
  .duelo-arte{
    position:relative; display:block; aspect-ratio:2/3; overflow:hidden;
    border-radius:var(--radius-md); border:2px solid var(--edge); background:var(--surface-2-top);
    transition:transform .2s var(--ease-standard), border-color .2s var(--ease-standard);
  }
  .duelo-arte img{ width:100%; height:100%; object-fit:cover; display:block; }
  .duelo-sem-arte{ position:absolute; inset:0; display:flex; align-items:center; justify-content:center; color:var(--text-dim); }
  .duelo-lado:active .duelo-arte{ transform:scale(.97); }
  .duelo-lado:focus-visible .duelo-arte{ outline:2px solid var(--gold); outline-offset:3px; }
  .duelo-lado.is-venceu .duelo-arte{ border-color:var(--gold); box-shadow:0 0 0 3px rgba(var(--gold-rgb), .3); }
  .duelo-nome{
    font-size:var(--t-body-size); font-weight:700; line-height:1.25; min-height:2.5em;
    display:-webkit-box; -webkit-line-clamp:2; -webkit-box-orient:vertical; overflow:hidden;
  }
  .duelo-onde{ font-size:var(--t-support-size); color:var(--text-soft); line-height:1.2; }
  .duelo-lado .super-tag{ align-self:center; margin-top:0; }

  /* ---- O PLACAR DO DUELO E A ESPADA (1.65.0, pedido do dono) ----
     Cada linha compara os dois lados; quem leva escreve em ouro com a seta apontando para o
     cartaz dele. As linhas entram em cascata SEM atraso nem fill-mode (a regra das peças que
     nascem por appendChild): durações crescentes com uma curva que segura o apagado no
     começo — onde a animação não roda, a linha simplesmente já está lá. */
  .duelo-par .duelo-x{ font-family:var(--font-display); font-weight:var(--t-display-weight); font-size:15px; letter-spacing:.06em; }
  .duelo-lado.is-favorito .duelo-arte{ border-color:var(--gold); }
  .duelo-fav{
    position:absolute; left:6px; right:6px; top:6px; z-index:2; padding:4px 6px; border-radius:999px;
    background:rgba(0,0,0,.78); border:1px solid color-mix(in srgb, var(--gold-on-image) 60%, transparent);
    color:var(--gold-on-image); font-size:11px; font-weight:800; line-height:1.1; text-align:center; font-variant-numeric:tabular-nums;
  }
  .duelo-placar{ list-style:none; margin:14px 0 0; padding:4px 0; display:flex; flex-direction:column;
    border-top:1px solid var(--rule); border-bottom:1px solid var(--rule); }
  .dp-linha{ display:grid; grid-template-columns:1fr minmax(96px, auto) 1fr; align-items:center; gap:8px; padding:6px 2px; }
  .dp-linha + .dp-linha{ border-top:1px dashed var(--rule); }
  .dp-rotulo{ font-size:11.5px; font-weight:700; color:var(--text-soft); line-height:1.2; text-align:center; }
  .dp-rotulo small{ display:block; font-size:11px; font-weight:500; color:var(--text-dim); }
  .dp-valor{ display:flex; align-items:center; gap:5px; font-size:14px; font-weight:700; color:var(--text-soft); font-variant-numeric:tabular-nums; }
  .dp-a{ justify-content:flex-start; }
  .dp-b{ justify-content:flex-end; }
  .dp-valor i{ font-style:normal; font-size:10px; color:var(--gold-ink); }
  .dp-valor.is-leva{ color:var(--gold-ink); font-size:15px; }
  .duelo-veredito{ margin:12px 0 0; font-size:13.5px; color:var(--text-soft); line-height:1.4; }
  .duelo-veredito b{ color:var(--gold-ink); }
  .duelo .duelo-sub{ margin:6px 0 0; }
  .duelo-preparando{ padding-block:44px; }
  .duelo-preparando .duelo-conta{ display:flex; align-items:center; justify-content:center; gap:6px; margin:0; }
  @media (prefers-reduced-motion: no-preference){
    .dp-linha{ animation:dp-entra calc(380ms + var(--i) * 170ms) cubic-bezier(.9,0,.25,1); }
    .duelo-veredito{ animation:dp-entra calc(480ms + var(--i) * 170ms) cubic-bezier(.9,0,.25,1); }
    .duelo-fav{ animation:dp-fav 1.4s cubic-bezier(.9,0,.2,1); }
  }
  @keyframes dp-entra{ from{ opacity:0; transform:translateY(6px); } }
  @keyframes dp-fav{ from{ opacity:0; transform:scale(.6); } }

  /* o corte: as duas metades são o mesmo cartaz com recortes complementares na diagonal da
     lâmina; o repouso é o FINAL (caídas e apagadas) */
  .duelo-lado.is-cortado, .duelo-lado.is-perdeu{ pointer-events:none; }
  .duelo-lado.is-perdeu{ opacity:.25; transition:opacity .3s; }
  .duelo-lado.is-cortado .duelo-arte{ overflow:visible; border-color:transparent; background:none; }
  .duelo-lado.is-cortado .duelo-arte > img,
  .duelo-lado.is-cortado .duelo-arte > .duelo-sem-arte,
  .duelo-lado.is-cortado .duelo-arte > .duelo-fav{ visibility:hidden; }
  .duelo-lado.is-cortado .duelo-nome, .duelo-lado.is-cortado .duelo-onde{ opacity:.3; transition:opacity .4s .3s; }
  .corte-metade{ position:absolute; inset:0; overflow:hidden; border-radius:inherit; opacity:0; }
  .corte-metade img{ width:100%; height:100%; object-fit:cover; display:block; }
  .corte-1{ clip-path:polygon(0 0, 100% 0, 0 100%); transform:translate(-30px, -42px) rotate(-18deg); animation:corte-1 .9s cubic-bezier(.2,.6,.35,1); }
  .corte-2{ clip-path:polygon(100% 0, 100% 100%, 0 100%); transform:translate(26px, 84px) rotate(16deg); animation:corte-2 .9s cubic-bezier(.2,.6,.35,1); }
  @keyframes corte-1{ 0%, 28%{ opacity:1; transform:none; } 100%{ opacity:0; transform:translate(-30px, -42px) rotate(-18deg); } }
  @keyframes corte-2{ 0%, 28%{ opacity:1; transform:none; } 100%{ opacity:0; transform:translate(26px, 84px) rotate(16deg); } }
  .corte-rastro{ position:absolute; inset:0; width:100%; height:100%; overflow:visible; z-index:3; opacity:0; pointer-events:none;
    animation:corte-rastro .6s ease-out; }
  .corte-rastro line{ stroke:#fff8e6; stroke-width:2.5; stroke-linecap:round; vector-effect:non-scaling-stroke;
    filter:drop-shadow(0 0 4px var(--gold)) drop-shadow(0 0 10px var(--gold)); stroke-dasharray:200; }
  @keyframes corte-rastro{ 0%{ opacity:1; stroke-dashoffset:200; } 30%{ opacity:1; } 100%{ opacity:0; } }
  .corte-rastro{ stroke-dashoffset:0; }
  .corte-espada{ position:absolute; z-index:4; width:30%; height:auto; left:35%; top:-10%; opacity:0; pointer-events:none;
    transform-origin:50% 80%; animation:corte-espada .42s cubic-bezier(.3,0,.2,1);
    filter:drop-shadow(0 4px 8px rgba(0,0,0,.6)); }
  @keyframes corte-espada{
    0%{ opacity:1; transform:translate(140%, -70%) rotate(35deg); }
    70%{ opacity:1; transform:translate(-150%, 90%) rotate(35deg); }
    100%{ opacity:0; transform:translate(-190%, 120%) rotate(35deg); }
  }
  .espada-lamina{ fill:#dfe4ec; stroke:#8d96a6; stroke-width:.8; }
  .espada-fio{ stroke:#ffffff; stroke-width:.8; opacity:.8; }
  .espada-guarda, .espada-pomo{ fill:var(--gold); }
  .espada-cabo{ fill:#5b3a1e; }

  /* ---- as faixas do Início: a pergunta do dia seguinte e o que chegou ----
     O mesmo material da faixa de rodada em andamento, e sem ouro cheio nos botões. */
  .pergunta-slot[hidden] { display:none; }
  .pergunta-filme{
    display:flex; align-items:flex-start; gap:12px;
    margin:0 0 14px; padding:14px;
    border:1px solid var(--gold-dim); border-top-color:var(--gold-edge);
    border-radius:var(--setup-radius); background:var(--gold-tint-deep);
    box-shadow:var(--shadow-2), inset 0 1px 0 rgba(var(--gold-rgb), .14);
  }
  .pergunta-filme .rank-thumb{ flex:none; width:40px; height:60px; }
  .pergunta-corpo{ flex:1; min-width:0; }
  .pergunta-titulo{ margin:0; font-size:15px; color:var(--text); line-height:1.35; }
  .pergunta-titulo b{ color:var(--gold-ink); }
  .pergunta-sub{ margin:4px 0 10px; font-size:var(--t-support-size); color:var(--text-soft); line-height:1.45; }
  .pergunta-filme{ flex-wrap:wrap; }
  .pergunta-sub{ margin-bottom:0; }
  /* cada resposta com a largura do próprio texto, e as três dividem o que sobra: com colunas
     iguais, "Mais ou menos" quebrava em duas linhas numa pastilha de 98px */
  .pergunta-opcoes{ flex-basis:100%; display:flex; flex-wrap:wrap; gap:4px; }
  .pergunta-opcoes .chip{ flex:1 1 auto; white-space:nowrap; min-height:44px; padding:0 8px; font-size:13px; justify-content:center; }
  .pergunta-nao{ flex-basis:100%; margin:-4px 0 -8px; min-height:40px; font-size:12.5px; color:var(--text-dim); }
  .chegou-fechar{
    flex:none; width:44px; height:44px; display:flex; align-items:center; justify-content:center;
    background:none; border:0; color:var(--text-dim); cursor:pointer; margin-right:-8px;
  }

  /* ---- "Vem aí": a data na frente, como numa agenda ---- */
  #vemAiOverlay{ z-index:131; }
  .vemai-lista{ display:flex; flex-direction:column; gap:8px; }
  .vemai-item{
    display:flex; align-items:center; gap:12px; width:100%; min-height:64px; padding:8px 10px;
    background:linear-gradient(180deg, var(--surface-2-top), var(--surface-2-bot));
    border:1px solid var(--edge); border-radius:var(--radius-md);
    color:var(--text); font:inherit; text-align:left; cursor:pointer;
  }
  .vemai-item .rank-thumb{ flex:none; width:36px; height:54px; }
  .vemai-data{ flex:none; width:44px; display:flex; flex-direction:column; align-items:center; line-height:1.1; }
  .vemai-data b{ font-size:14px; color:var(--gold-ink); white-space:nowrap; }
  .vemai-data small{ font-size:11px; color:var(--text-dim); }
  .vemai-corpo{ flex:1; min-width:0; }
  .vemai-corpo b{ display:block; font-size:14px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
  .vemai-corpo span{ display:block; margin-top:2px; font-size:var(--t-support-size); color:var(--text-soft); text-wrap:pretty; }
  /* os meses leem como os títulos de seção do resto do app: à esquerda, na coluna da lista */
  #vemAiOverlay .section-label{ text-align:left; }
  .vemai-mais{ margin-top:4px; }

  /* ---- A PROGRAMAÇÃO (1.61.0): o que chegou e o que vem aí numa linha do tempo ----
     Uma coluna de datas carimbadas — a mesma leitura das datas na película da sala Noites —
     costurada por um fio vertical. O que já chegou vem em cima, com a data em ouro; a linha
     de HOJE separa; o que vem aí segue embaixo, em tinta de texto. Linhas sem caixa: é agenda
     para consultar, e a sala continua sendo das vitrines. */
  .programacao{ margin-top:30px; }
  .programacao[hidden]{ display:none; }
  .prog-linha{ position:relative; list-style:none; margin:4px 0 0; padding:0; }
  .prog-linha::before{ content:""; position:absolute; left:22px; top:14px; bottom:14px; width:1px; background:var(--edge-strong); }
  .prog-item{ position:relative; display:flex; align-items:center; gap:12px; padding:6px 0; }
  .prog-data{ position:relative; z-index:1; flex:none; width:45px; padding:6px 0 5px; display:grid; justify-items:center; gap:1px; line-height:.9;
    border-radius:6px; border:1px solid var(--edge-strong); background:var(--bg); }
  .prog-data b{ font-family:var(--font-display); font-weight:var(--t-display-weight); font-size:25px; color:var(--text); font-variant-numeric:tabular-nums; }
  .prog-data small{ font-family:var(--font-display); font-weight:var(--t-display-weight); font-size:12px; letter-spacing:1px; color:var(--text-soft); }
  .prog-item--chegou .prog-data{ border-color:var(--edge-gold); background:linear-gradient(var(--gold-tint-bg), var(--gold-tint-bg)), var(--bg); }
  .prog-item--chegou .prog-data b{ color:var(--gold-ink); }
  .prog-corpo{ flex:1; min-width:0; display:flex; align-items:center; gap:10px; min-height:60px; padding:0; border:0; background:none;
    color:var(--text); font:inherit; text-align:left; cursor:pointer; }
  .prog-corpo .rank-thumb{ flex:none; width:40px; height:60px; }
  .prog-texto{ flex:1; min-width:0; display:grid; gap:2px; line-height:1.3; }
  .prog-texto b{ font-size:14px; font-weight:700; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
  .prog-texto > span{ font-size:var(--t-support-size); color:var(--text-soft); }
  .prog-texto .prog-rotulo{ font-size:12px; font-weight:800; color:var(--gold-ink); }
  .prog-hoje{ position:relative; display:flex; align-items:center; gap:10px; margin:4px 0; }
  .prog-hoje span{ position:relative; z-index:1; flex:none; width:45px; padding:2px 0; text-align:center; background:var(--bg);
    font-family:var(--font-display); font-weight:var(--t-display-weight); font-size:14px; line-height:1; letter-spacing:1.6px; color:var(--gold-ink); }
  .prog-hoje::after{ content:""; flex:1; height:1px; background:linear-gradient(90deg, var(--gold-dim), rgba(var(--gold-rgb), 0)); }
  .programacao .vemai-mais{ margin:6px 0 0 57px; }

  /* ---- a retrospectiva ---- */
  #retroOverlay{ z-index:139; }
  .retro{ text-align:center; }
  .retro-trilho{ max-width:220px; margin:0 auto 14px; }
  .retro-sobre{ margin:0; font-size:13px; font-weight:800; color:var(--gold-ink); }
  .retro-titulo{ margin-top:2px; }
  .retro-nums{ display:grid; grid-template-columns:repeat(3, 1fr); gap:8px; margin:16px 0; align-items:start; }
  .retro-num b{ display:block; font-family:'Bebas Neue', sans-serif; font-weight:400; font-size:40px; line-height:1; color:var(--gold-ink); }
  .retro-num span{ display:block; min-height:2.6em; margin-top:4px; font-size:var(--t-support-size); color:var(--text-soft); line-height:1.3; }
  .retro-capas{ display:flex; justify-content:center; gap:8px; margin:6px 0 14px; }
  .retro-capas .rank-thumb{ width:52px; height:78px; }
  .retro-fatos{ list-style:none; margin:0; padding:0; text-align:left; }
  .retro-fatos li{ display:flex; justify-content:space-between; gap:12px; padding:11px 2px; border-top:1px solid var(--edge); font-size:14px; }
  .retro-fatos li span{ color:var(--text-soft); }
  .retro-fatos li b{ color:var(--text); text-align:right; }

  /* ======================= A ESTANTE DE LOUROS (1.51.1) =======================
     A coroa dos festivais, gerada em SVG (ver LOURO_RAMO em js/11). Ganha: as folhas da
     frente em ouro cheio, as de trás na tinta do ouro (em escuro as duas coincidem, e a
     diferença vira só a opacidade), um disco de metal claro e o emblema escrito em ouro.
     Bloqueada: a MESMA coroa só em traço, na tinta apagada — o lugar existe e está vazio.
     A sombra tem deslocamento e desfoque: é a coroa pousada na prateleira, não um brilho. */
  .louro-svg{ display:block; width:100%; height:auto; overflow:visible; }
  .louro-haste{ fill:none; stroke-width:1.6; stroke-linecap:round; }
  .louro-emblema{ fill:none; stroke-width:1.9; stroke-linecap:round; stroke-linejoin:round; }
  .louro-svg.is-ganha .louro-haste{ stroke:var(--gold-ink); }
  .louro-svg.is-ganha .louro-folha{ fill:var(--gold); }
  .louro-svg.is-ganha .louro-folha--tras{ fill:var(--gold-ink); opacity:.62; }
  .louro-svg.is-ganha .louro-disco{
    fill:rgba(var(--gold-rgb), .13); stroke:rgba(var(--gold-rgb), .55); stroke-width:1.2;
  }
  /* `color` junto do traço: a lente do encontro na marca do match é PREENCHIDA com
     currentColor, e sem isso ela sairia na cor do texto em volta */
  .louro-svg.is-ganha .louro-emblema{ stroke:var(--gold-ink); color:var(--gold-ink); }
  .louro-svg.is-falta .louro-haste{ stroke:var(--edge-strong); }
  .louro-svg.is-falta .louro-folha{ fill:none; stroke:var(--text-dim); stroke-width:1.1; opacity:.55; }
  .louro-svg.is-falta .louro-folha--tras{ opacity:.3; }
  .louro-svg.is-falta .louro-disco{ fill:none; stroke:var(--edge-strong); stroke-width:1.2; stroke-dasharray:2.4 3; }
  .louro-svg.is-falta .louro-emblema{ stroke:var(--text-dim); color:var(--text-dim); opacity:.8; }

  /* a linha do perfil: as três últimas coroas em leque, a conta e a porta */
  .conquistas-entrada{
    display:flex; align-items:center; gap:14px; width:100%; min-height:72px; padding:10px 12px;
    border:1px solid var(--edge-gold); border-radius:var(--radius-md);
    background:linear-gradient(180deg, rgba(var(--gold-rgb), .09), rgba(var(--gold-rgb), .03));
    color:var(--text); font:inherit; text-align:left; cursor:pointer;
    transition:border-color var(--dur-2) var(--ease-standard), transform var(--dur-1) var(--ease-standard);
  }
  .conquistas-entrada:hover{ border-color:rgba(var(--gold-rgb), .6); }
  .conquistas-entrada:active{ transform:scale(.99); }
  .conquistas-entrada > .ico-svg{ flex:none; color:var(--gold-ink); }
  .louros-pilha{ flex:none; display:flex; align-items:center; padding-left:2px; }
  /* a coroa da frente inteira, as outras duas recuadas atrás dela, menores e mais escuras:
     a mesma gramática do leque de cartazes da estante do Início */
  .louro-mini{
    position:relative; width:54px; margin-left:-24px;
    filter:drop-shadow(0 2px 3px rgba(0,0,0,.3));
  }
  .louro-mini:first-child{ margin-left:0; z-index:3; }
  .louro-mini:nth-child(2){ z-index:2; transform:scale(.8) translateY(-2px); opacity:.72; }
  .louro-mini:nth-child(3){ z-index:1; transform:scale(.64) translateY(-4px); opacity:.5; }
  .conquistas-entrada-texto{ flex:1; min-width:0; }
  .conquistas-entrada-texto b{ display:block; font-size:14px; font-weight:700; line-height:1.3; white-space:nowrap; }
  .conquistas-num{ color:var(--gold-ink); }
  /* a linha da última conquista quebra em vez de cortar o nome no meio ("Noite m…") */
  .conquistas-entrada-texto > span{
    display:block; margin-top:2px; font-size:var(--t-support-size); color:var(--text-soft);
  }

  /* a estante: nasce de dentro do perfil (120), então fica um degrau acima dele */
  #conquistasOverlay{ z-index:141; }
  .estante-louros{ text-align:center; }
  .estante-placar{ margin:6px 0 0; font-size:15px; color:var(--text-soft); }
  .estante-placar b{ font-family:'Bebas Neue', sans-serif; font-weight:400; font-size:30px; line-height:1; color:var(--gold-ink); letter-spacing:.02em; margin-right:2px; }
  .estante-proxima{
    margin:10px auto 6px; max-width:32ch; font-size:var(--t-support-size); line-height:1.5; color:var(--text-soft);
  }
  .estante-proxima b{ color:var(--text); }
  .estante-louros .section-label{ text-align:left; margin-top:22px; }
  .louros-grade{
    list-style:none; margin:10px 0 0; padding:0;
    display:grid; grid-template-columns:repeat(3, minmax(0, 1fr)); gap:18px 10px;
  }
  .louro{ display:flex; flex-direction:column; align-items:center; text-align:center; min-width:0; }
  /* 1.53.0: cada coroa é um botão que abre a ficha dela. O botão é o alvo inteiro (coroa,
     nome e data), sem cara de botão: a grade continua sendo uma estante, e é o toque que
     responde — o mesmo afundar de 1% das outras portas do perfil */
  .louro-botao{
    display:flex; flex-direction:column; align-items:center; width:100%; min-height:44px;
    padding:6px 2px 8px; border:0; border-radius:var(--radius-md); background:none;
    color:inherit; font:inherit; text-align:center; cursor:pointer;
    transition:background-color var(--dur-2) var(--ease-standard), transform var(--dur-1) var(--ease-standard);
  }
  .louro-botao:hover{ background:rgba(var(--gold-rgb), .06); }
  .louro-botao:active{ transform:scale(.97); }
  .louro-arte{ width:min(88px, 100%); }
  .louro.is-ganha .louro-arte{ filter:drop-shadow(0 3px 4px rgba(0,0,0,.28)); }
  .louro-nome{
    margin-top:6px; font-size:12.5px; font-weight:700; line-height:1.25; min-height:2.5em;
    display:-webkit-box; -webkit-line-clamp:2; -webkit-box-orient:vertical; overflow:hidden;
    color:var(--text);
  }
  .louro.is-falta .louro-nome{ color:var(--text-soft); }
  .louro-meta{ margin-top:2px; min-height:1.35em; font-size:11.5px; line-height:1.35; color:var(--text-dim); }
  .louro.is-ganha .louro-meta{ color:var(--gold-ink); }

  /* O MOMENTO DA ESTANTE: as coroas ganhas acendem em onda quando ela abre. Sem atraso e
     sem preenchimento (a regra das folhas criadas por appendChild vale aqui): o repouso é a
     coroa ACESA, e a onda sai de durações crescentes com uma curva que segura o apagado no
     começo — onde a animação não roda, a coroa simplesmente já está acesa. */
  @media (prefers-reduced-motion: no-preference){
    .louro.is-ganha .louro-svg{
      animation: louro-acende calc(.55s + var(--i, 0) * 120ms) cubic-bezier(.7, 0, .25, 1);
    }
    @keyframes louro-acende{
      from{ opacity:.25; filter:grayscale(1); transform:scale(.9); }
    }
  }

  /* ---- A FICHA DE UMA CONQUISTA (1.53.0) ----
     Nasce de dentro da estante (141), então fica um degrau acima dela — e abaixo da faixa de
     avisos (150). A coroa vem grande no alto, a data logo abaixo do nome, e o texto em
     blocos rotulados: "Como foi" (só se é sua) e "Como se conquista". */
  #conquistaOverlay{ z-index:145; }
  .conquista-ficha{ text-align:center; }
  .conquista-arte{ width:132px; margin:4px auto 2px; }
  .conquista-ficha.is-ganha .conquista-arte{ filter:drop-shadow(0 4px 6px rgba(0,0,0,.3)); }
  .conquista-estado{ margin:6px 0 0; font-size:var(--t-support-size); font-weight:600; color:var(--text-soft); }
  .conquista-ficha.is-ganha .conquista-estado{ color:var(--gold-ink); }
  .conquista-ficha .section-label{ text-align:left; margin-top:20px; }
  .conquista-texto{ margin:6px 0 0; text-align:left; font-size:14px; line-height:1.5; color:var(--text); }
  .conquista-barra{
    height:8px; margin-top:12px; border-radius:999px; background:var(--edge); overflow:hidden;
  }
  .conquista-barra i{ display:block; height:100%; border-radius:inherit; background:var(--gold); }
  .conquista-conta{ margin:6px 0 0; text-align:left; font-size:var(--t-support-size); color:var(--text-soft); }

  /* ---- A CONQUISTA CELEBRADA (1.55.0, pedido do dono) ----
     Uma placa de festival que desce do topo: a coroa de louros da conquista, "Conquista
     desbloqueada", o nome no tipo de cartaz do app e como foi ganha. Chove papel dourado —
     folhas de ouro, fitas e estrelas de quatro pontas, NUNCA confete colorido de aniversário:
     o app é uma sala de cinema, e a única cor da festa é a da casa.
     Camada 190: acima do detalhe (140), das fichas (145), do toast (150) e da prévia de
     compartilhar (180); abaixo do clímax (200), que ela espera sair (ver proximaFesta).
     Tudo que tem atraso começa INVISÍVEL NO ESTADO BASE (opacidade 0 ou fora da placa), e
     por isso nenhuma animação de entrada precisa de preenchimento — é a regra das folhas
     criadas por appendChild: com `both`, o estado inicial ficaria preso se ela não rodasse. */
  .conquista-festa{
    position:fixed; left:0; right:0; top:calc(env(safe-area-inset-top) + 10px); z-index:190;
    display:flex; justify-content:center; padding:0 16px; pointer-events:none;
    animation:cf-desce .6s var(--ease-entrance);
  }
  .conquista-festa.is-out{ animation:cf-sai .3s var(--ease-standard) forwards; }
  /* ABAIXO DA BARRA DE CIMA (1.67.0): pousada no topo ela cobria Conta e Ajustes por vários
     segundos seguidos. Com carta na mão a barra não está na tela, e a placa sobe. */
  body:not(.in-round) .conquista-festa{ top:calc(env(safe-area-inset-top) + 78px); }

  @keyframes cf-desce{
    0%{ transform:translateY(-140%); }
    70%{ transform:translateY(6px); }
  }
  @keyframes cf-sai{ to{ transform:translateY(-140%); opacity:0; } }
  .cf-placa{
    position:relative; overflow:hidden; pointer-events:auto; display:flex; align-items:center; gap:12px;
    width:100%; max-width:388px; min-height:84px; padding:10px 16px 10px 10px; border-radius:20px;
    background:linear-gradient(180deg, var(--surface-3-top), var(--surface-3-bot));
    border:1px solid rgba(var(--gold-rgb), .6);
    box-shadow:var(--shadow-3), 0 0 0 4px rgba(var(--gold-rgb), .1), 0 0 38px -8px rgba(var(--gold-rgb), .45), inset 0 1px 0 var(--sheen-3);
    color:var(--text); font:inherit; text-align:left; cursor:pointer; -webkit-tap-highlight-color:transparent;
  }
  .cf-placa:active{ transform:scale(.985); }
  .cf-placa:focus-visible{ outline:2px solid var(--gold); outline-offset:3px; }
  /* o brilho que atravessa a placa uma vez, como luz batendo numa placa de metal */
  .cf-brilho{
    position:absolute; inset:0; pointer-events:none; transform:translateX(-110%);
    background:linear-gradient(105deg, transparent 30%, rgba(var(--gold-rgb), .26) 50%, transparent 70%);
    animation:cf-varre 1.1s var(--ease-standard) .45s;
  }
  @keyframes cf-varre{ from{ transform:translateX(-110%); } to{ transform:translateX(110%); } }
  .cf-louro{
    position:relative; flex:none; width:66px; filter:drop-shadow(0 3px 5px rgba(0,0,0,.3));
    animation:cf-louro .85s var(--ease-entrance);
  }
  @keyframes cf-louro{
    0%{ transform:scale(.35) rotate(-28deg); opacity:0; }
    55%{ transform:scale(1.12) rotate(4deg); opacity:1; }
  }
  .cf-texto{ position:relative; min-width:0; flex:1; display:grid; gap:1px; }
  .cf-texto small{ font-size:11px; font-weight:800; letter-spacing:.9px; text-transform:uppercase; color:var(--gold-ink); }
  .cf-texto b{
    font-family:var(--font-display); font-weight:400; font-size:26px; line-height:1; letter-spacing:.4px;
    color:var(--text); overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
  }
  .cf-texto span{ font-size:var(--t-support-size); line-height:1.3; color:var(--text-soft); }
  /* deitado a placa encolhe (1.67.0): com 400px de altura ela tomava metade da tela. Vem
     DEPOIS das regras da placa, senão o min-height de 84 delas vence */
  @media (max-height: 560px){
    .cf-placa{ min-height:56px; max-width:340px; padding:6px 12px 6px 8px; gap:10px; }
    .cf-louro{ width:42px; }
    .cf-texto b{ font-size:20px; }
    .cf-texto span{ display:none; }
  }

  /* a chuva de papel dourado: as peças nascem no meio da placa, sobem um pouco e caem */
  .cf-chuva{ position:absolute; left:16px; right:16px; top:34px; height:0; pointer-events:none; }
  .cf-peca{
    position:absolute; left:var(--x); top:0; display:block; opacity:0;
    animation:cf-cai var(--dur) cubic-bezier(.25,.6,.45,1) var(--atraso);
  }
  @keyframes cf-cai{
    0%{ opacity:0; transform:translate(0, 0) rotate(0) scale(.5); }
    8%{ opacity:1; }
    22%{ transform:translate(calc(var(--deriva) * .35), -46px) rotate(calc(var(--giro) * .3)) scale(1); }
    80%{ opacity:1; }
    100%{ opacity:0; transform:translate(var(--deriva), 300px) rotate(var(--giro)) scale(.9); }
  }
  .cf-folha{ width:7px; height:10px; border-radius:1.5px; }
  .cf-fita{ width:3px; height:15px; border-radius:2px; }
  .cf-estrela{
    width:11px; height:11px;
    clip-path:polygon(50% 0, 62% 38%, 100% 50%, 62% 62%, 50% 100%, 38% 62%, 0 50%, 38% 38%);
  }
  .cf-cor-a{ background:var(--gold); }
  .cf-cor-b{ background:color-mix(in srgb, var(--gold) 45%, #fff6dc); }
  .cf-cor-c{ background:var(--gold-ink); }
  @media (prefers-reduced-motion: reduce){
    .conquista-festa{ animation:cf-aparece .3s linear; }
    .conquista-festa.is-out{ animation:cf-some .3s linear forwards; }
    .cf-chuva, .cf-brilho{ display:none; }
    .cf-louro{ animation:none; }
    @keyframes cf-aparece{ from{ opacity:0; } }
    @keyframes cf-some{ to{ opacity:0; } }
  }

  /* ---- "ABRIR NO APP": o convite que caiu no navegador do Android (1.55.0) ----
     Uma faixa no alto, por cima de tudo que a abertura mostra (tutorial inclusive: quem
     chega pela primeira vez por um convite veria o tutorial no navegador). Camada 195:
     acima da placa de conquista (190), abaixo do clímax (200). O botão é contorno de ouro,
     nunca ouro cheio — a ação dourada da tela é entrar na sala. */
  .abrir-no-app{
    position:fixed; left:12px; right:12px; top:calc(env(safe-area-inset-top) + 10px); z-index:195;
    display:flex; align-items:center; gap:10px; max-width:420px; margin:0 auto; padding:8px 6px 8px 14px;
    border-radius:var(--radius-md); border:1px solid var(--edge-strong);
    background:linear-gradient(180deg, var(--surface-3-top), var(--surface-3-bot));
    box-shadow:var(--shadow-3); color:var(--text); font-size:13px; line-height:1.3;
    animation:abrir-no-app-desce var(--dur-3) var(--ease-entrance);
  }
  @keyframes abrir-no-app-desce{ from{ opacity:0; transform:translateY(-12px); } }
  .abrir-no-app-texto{ flex:1; min-width:0; color:var(--text-soft); }
  .abrir-no-app-texto b{ display:block; color:var(--text); }
  .abrir-no-app-btn{
    flex:none; display:inline-flex; align-items:center; min-height:44px; padding:0 14px; border-radius:999px;
    border:1px solid rgba(var(--gold-rgb), .6); color:var(--gold-ink); font-weight:800; text-decoration:none;
  }
  .abrir-no-app-fechar{
    flex:none; display:grid; place-items:center; width:40px; height:44px; padding:0; border:0;
    background:none; color:var(--text-dim); cursor:pointer;
  }

  /* ---- A VITÓRIA DO DUELO (1.55.0, pedido do dono) ----
     O cartaz que venceu vai para o centro, a coroa de louros das conquistas se fecha em volta
     dele e cai o mesmo papel dourado da placa de conquista. A peça dura 1,9 s e dá lugar ao
     detalhe do vencedor (ver vitoriaDoDuelo). Movimento reduzido nem chega aqui: o JS pula. */
  .duelo.is-vitoria{ padding-bottom:22px; }
  .dv-palco{ position:relative; width:230px; height:230px; margin:6px auto 8px; display:grid; place-items:center; }
  .dv-brilho{
    position:absolute; inset:6%; border-radius:50%; pointer-events:none;
    background:radial-gradient(circle, rgba(var(--gold-rgb), .34), transparent 66%);
    animation:dv-brilho 1.9s var(--ease-standard);
  }
  @keyframes dv-brilho{ 0%{ transform:scale(.4); opacity:0; } 35%{ transform:scale(1.08); opacity:1; } }
  .dv-louro{ position:absolute; inset:0; width:100%; animation:dv-louro .9s var(--ease-entrance); }
  @keyframes dv-louro{
    0%{ transform:scale(.55) rotate(-24deg); opacity:0; }
    60%{ transform:scale(1.06) rotate(3deg); opacity:1; }
  }
  .dv-cartaz{
    position:relative; display:block; width:100px; aspect-ratio:2/3; overflow:hidden; border-radius:10px;
    border:2px solid var(--gold); background:var(--surface-2-top);
    box-shadow:0 0 0 4px rgba(var(--gold-rgb), .18), 0 18px 34px -12px rgba(0,0,0,.8);
    animation:dv-cartaz .7s var(--ease-entrance);
  }
  .dv-cartaz img{ width:100%; height:100%; object-fit:cover; display:block; }
  @keyframes dv-cartaz{ 0%{ transform:scale(.72) translateY(14px); } 60%{ transform:scale(1.07); } }
  .dv-chuva{ left:0; right:0; top:40%; }
  .duelo.is-vitoria .duelo-conta{ display:flex; align-items:center; justify-content:center; gap:6px; font-size:12px; letter-spacing:.9px; text-transform:uppercase; }

  /* ---- o lembrete: a folha nasce dos Ajustes, então fica acima deles ---- */
  #lembreteOverlay{ z-index:137; }
  .lembrete-dias, .lembrete-horas{ justify-content:flex-start; margin-bottom:6px; }
  .lembrete-nota:empty{ display:none; }

  /* ---- Feed ou Story na prévia de compartilhar ---- */
  .share-formato{ max-width:240px; margin:14px auto 0; }
  /* nos trilhos de duas opções curtas o rótulo é centrado em cada metade (o padrão do
     trilho é à esquerda, feito para rótulo + descrição longos) */
  .share-formato .mode-btn, .retro-trilho .mode-btn{ text-align:center; }
  .share-formato .mode-btn small{ display:block; font-size:11px; color:var(--text-dim); font-weight:600; }
  .share-preview--story{ max-width:230px; }
