/* ── P48-11 + P48-15 — a casca da página de vendas 48: topo, menu, rodapé e botão fixo ──────

   Carregada DEPOIS de `pagina-vendas-48-uau.css`, de quem usa os tokens. P48-11 pôs aqui o
   menu de âncoras da PRÓPRIA página e a parada da âncora; P48-15 veste a casca com a direção A
   do protótipo v6.2 e acrescenta as duas peças que o owner aprovou: o menu no celular (obs. 12)
   e o botão fixo de compra no celular (P8).

   O rodapé ⛔ não muda de marcação: é o `sp4-footer` que a página do 47º publicava, escrito uma vez
   em `_rodape48.html`. O que muda é só a roupa, e ela mora aqui. */

/* ── ⭐ P48-48 · A BARRA DE CONTAGEM ATÉ A PROVA ────────────────────────────────────────────

   Ela mora ACIMA do topo, no FLUXO — ⛔ fixa ao rolar. Uma linha no desktop, até duas no
   celular, e ⛔ transborda em 320 px (a caixa dos dígitos é `flex-wrap`).

   A direção de arte é a desta página, ⛔ a do 47º: papel (`--raised`, o mesmo tom do cartão),
   tinta (`--ink` na chamada) e o âmbar ÚNICO (`--accent`) nos números. ⛔ Dourado, ⛔ cor
   nova, ⛔ caixa alta — a folha da 48 ⛔ grita em seletor nenhum, e a régua da VOZ conta isso.

   Contraste medido sobre `--raised` (#F3F1EC): `--ink` 12,66:1, `--soft` 5,79:1 e `--accent`
   5,44:1. Os três passam de 4,5:1, que é o mínimo de texto em AA.

   ⚠ CLS ZERO, e por DUAS coisas juntas, ⛔ por uma: (1) o HTML servido já vem com os quatro
   valores pintados na forma final — o JavaScript reescreve o mesmo texto, ⛔ preenche caixa
   vazia; (2) `min-height` na linha e `min-width` em `ch` no número. Sem o piso em `ch`, o
   segundo indo de "9" a "10" mudaria a largura da célula e empurraria as vizinhas sessenta
   vezes por minuto. ⚠ `tabular-nums` sozinho ⛔ basta: Fraunces ⛔ traz o recurso, e um
   recurso opcional que a fonte ⛔ tem é uma promessa que ⛔ se cumpre. */
.pv-contagem { border-bottom: 1px solid var(--rule); background: var(--raised); }
.pv-contagem__linha {
  display: flex; flex-wrap: wrap; align-items: baseline; justify-content: center;
  gap: 4px 14px; min-height: 44px;
  max-width: calc(var(--wide) + 2 * var(--gutter)); margin: 0 auto;
  padding: 8px var(--gutter); text-align: center;
}
body.pv48 .pv-contagem__chamada { margin: 0; font: 500 14px/1.35 var(--sans); color: var(--ink); }
.pv-contagem__nums {
  display: flex; flex-wrap: wrap; min-width: 0; align-items: baseline; justify-content: center;
  gap: 2px 12px;
}
.pv-contagem__cel { display: inline-flex; align-items: baseline; gap: 5px; }
.pv-contagem__n {
  min-width: 2.2ch; text-align: center;
  font: 600 18px/1 var(--serif); color: var(--accent);
  font-variant-numeric: tabular-nums; font-feature-settings: "tnum" 1;
}
.pv-contagem__u { font: 400 13px/1.35 var(--sans); color: var(--soft); }
body.pv48 .pv-contagem__data,
body.pv48 .pv-contagem__fim,
body.pv48 .pv-contagem__semjs {
  width: 100%; margin: 0; font: 400 13px/1.35 var(--sans); color: var(--soft);
}
/* A forma LARGA a partir de 600px; abaixo disso, a compacta que o owner autorizou. É a mesma
   data e a mesma hora — o que encurta é a frase, ⛔ o fato. */
.pv-contagem__data-larga { display: none; }
@media (min-width: 600px) {
  .pv-contagem__data-larga { display: inline; }
  .pv-contagem__data-curta { display: none; }
  .pv-contagem__linha { gap: 4px 18px; }
}

/* ── ⭐ P48-61 · A BARRA DA PROVA EM UMA LINHA, NO CELULAR ──────────────────────────────────

   `1ª fase em 10/01 · 101d 12h 30m 05s`, abaixo de 600 px e só com JavaScript. O molde é o da
   faixa da oferta compacta (P48-58), com as lições dela já pagas, e a razão é a mesma: a
   primeira tela do celular tem de entregar o PLAYER.

   ⭐ **A conta que motiva a fatia**, medida na P48-60 em 375×548: com a barra da OFERTA no ar
   (43 px) o player fica em 0,86 da primeira tela; com a barra da PROVA (122 px) ele cai para
   0,42, contra o mínimo de ⅔ da régua. Os 122 px são a chamada quebrando em duas linhas, a
   contagem numa terceira e a data numa quarta. Uma linha só tira ~79 px do topo.

   ⚠ **A troca é do OLHO, ⛔ da árvore acessível.** A chamada do owner e a linha da data saem de
   vista por `clip`, com a MESMA receita da `.pv-oferta__larga` — elas continuam sendo lidas, na
   mesma ordem e com as mesmas palavras, em qualquer largura. `display: none` as tiraria de lá, e
   o celular perderia a data completa da prova, com a hora, o fuso e a ressalva do edital. A
   linha compacta é DECORAÇÃO (`aria-hidden` no HTML), então ⛔ há leitura dobrada.

   ⚠ **SÓ COM JavaScript**, e ⛔ é detalhe: sem ele o `<noscript>` diz "a data e a hora prováveis
   acima continuam válidas", e a data tem de estar À VISTA para a frase ser verdadeira. Em
   `html.sem-js` a barra fica exatamente como sempre foi.

   ⚠ **⛔ Há regra nova para ≥ 600 px**: ali a barra continua a da P48-48, byte a byte — 58,95 px
   em 1280×800, o número da P48-60.

   ⚠ **No estado pós-zero** a barra fica com a mensagem do fim SOZINHA no olho, exatamente como
   a faixa da oferta faz: a linha compacta vai `hidden` com a chamada e os dígitos, e a data
   clipada continua na árvore acessível. Passada a hora provável da prova, a linha que importa é
   a que manda consultar o calendário oficial.

   ⚠ O `·` é `::after` do trecho da data, ⛔ um irmão — a lição da P48-56 · Etapa 2, repetida:
   como irmão ele seria mais um item do `flex-wrap` e poderia quebrar sozinho para a linha de
   baixo. E ele só existe a partir de 390 px, pela razão MEDIDA na oferta: em 375 px os ~8 px
   dele consomem a folga e derrubam a contagem para a segunda linha. Abaixo disso quem separa é
   o `gap`. */
.pv-contagem__curta, .pv-contagem__u--curta { display: none; }
@media (max-width: 599.98px) {
  /* ⚠ A receita é a do `sp5-sr-only` SEM o `!important` dele, e ⛔ por descuido: lá a classe é
     geral e pode cair sobre um componente com `position` próprio; aqui os dois alvos são nós
     desta barra, que ⛔ declaram `position` em regra nenhuma. E a folha da barra ⛔ pode ter
     `!important`: a régua da P48-56 o proíbe no bloco vizinho, e esta fatia ⛔ o estreia. */
  html.com-js .pv-contagem__chamada,
  html.com-js .pv-contagem__data {
    position: absolute; width: 1px; height: 1px; overflow: hidden;
    clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap;
  }
  html.com-js .pv-contagem__linha { gap: 2px 6px; padding: 7px var(--gutter); }
  html.com-js .pv-contagem__curta { display: block; }
  html.com-js body.pv48 .pv-contagem__curta { margin: 0; }
  /* A tipografia da `.pv-oferta__ate`, na tinta DESTA barra: `--soft` sobre `--raised` mede
     5,79:1, e é a mesma tinta que a linha da data já usava. */
  html.com-js .pv-contagem__qual { font: 400 13px/1.35 var(--sans); color: var(--soft); }
  /* As longas são `aria-hidden` dentro dos dígitos, então aqui `display: none` ⛔ tira nada de
     ninguém — o que o leitor de tela recebe é a frase acessível, inteira e à parte. */
  html.com-js .pv-contagem__u--larga { display: none; }
  /* ⚠ A CONTAGEM APERTA com os MESMOS números da oferta compacta, e ⛔ é gosto — é o que faz a
     linha única existir. O piso em `ch` continua: 2ch cabe os dois dígitos, e sem ele o segundo
     indo de "9" para "10" empurraria as vizinhas sessenta vezes por minuto. */
  html.com-js .pv-contagem__nums { gap: 2px 4px; }
  html.com-js .pv-contagem__cel { gap: 2px; }
  html.com-js .pv-contagem__n { min-width: 2ch; font-size: clamp(12.5px, 3.2vw, 17px); }
  /* ⚠ UMA regra por seletor, e ⛔ é estilo: a lição da P48-60 é que régua que indexa as regras
     por seletor guarda só a ÚLTIMA delas, e a penúltima passa a medir `None`. */
  html.com-js .pv-contagem__u--curta { display: inline; margin-left: -1px; font-size: 10px; }
}
@media (min-width: 390px) and (max-width: 599.98px) {
  html.com-js .pv-contagem__qual::after { content: " ·"; }
}

/* ── ⭐ P48-56 · Etapa 2 · A BARRA DA OFERTA ────────────────────────────────────────────────

   O pedido do owner é literal: "a barra deve ter um forte destaque para chamar muito a atenção
   de quem chega na página". O destaque é de VALOR, ⛔ de alarme — a faixa INVERTE a página
   (tinta cheia de ponta a ponta, texto em papel), o preço novo é grande e DOURADO em Fraunces
   e os números da contagem crescem com a tela. ⛔ Vermelho, ⛔ neon, ⛔ piscar, ⛔ `animation`,
   ⛔ `transition`: nada aqui se move, e por isso `prefers-reduced-motion` ⛔ tem o que conter.

   ⭐ ETAPA 2b (27/09, decisão visual do owner): o preço novo subiu de `clamp(19px, 5.2vw, 27px)`
   para `clamp(24px, 6.8vw, 30px)` e trocou o papel pelo DOURADO DA MARCA — "um forte destaque
   para chamar MUITO a atenção". Só o preço: o anterior riscado segue `--of-fosco` e os números
   da contagem seguem `--paper`.

   ⚠ ⛔ HÁ ÂMBAR DE AÇÃO AQUI, e ⛔ é gosto: `--accent` (#7D5C1E) sobre `--ink` (#2A2A2E) mede
   **2,33:1** e reprovaria o mínimo de 4,5:1 da WCAG AA. A única cor de ação da página ⛔ pode
   ser LIDA sobre esta faixa, então ela ⛔ entra nela. O dourado da marca ⛔ é ela: é outro token,
   mais claro, e é o mesmo que os e-mails usam no logo sobre fundo escuro. Medido sobre `--ink`,
   com o medidor da P48-19 (`docs/evidence/p48-01/contraste_sessao08.py`):

   | tinta | valor | contraste |
   |---|---|---|
   | `--paper` (chamada, números, a mensagem pós-zero) | #FAF9F6 | **13,58:1** |
   | `--of-fosco` (preço anterior riscado, unidades, separador, linha do fim) | #B9B7B0 | **7,12:1** |
   | `--gold` (o preço novo, e só ele) | #B88A2E | **4,57:1** |

   ⚠ O dourado passa AA como TEXTO COMUM (4,57 ≥ 4,5), e é assim que o aparato o mede — o
   `<strong>` entra na tabela de tamanhos dele como 17 px, que é o PIOR caso. Na tela ele é
   texto grande de verdade (≥ 24 px, peso 600), e para texto grande o mínimo é 3:1. A margem
   medida é dos 4,5, ⛔ dos 3: quem baixar o `clamp` ⛔ derruba o contraste.

   `--of-fosco` é token NOVO e mora aqui, no escopo da faixa: ele só existe porque nenhuma
   tinta da página de hoje foi desenhada para papel sobre tinta, e `--soft` (#5D5D64) mediria
   1,71:1 — ilegível. Ele é "papel mais apagado", que é exatamente o papel do preço anterior.

   ⚠ CLS ZERO pelas MESMAS duas coisas da P48-48: o HTML já vem com os quatro valores na forma
   final, e o número tem piso em `ch`. Sem o piso, o segundo indo de "9" a "10" empurraria as
   células vizinhas sessenta vezes por minuto.

   ⚠ A ALTURA é requisito, ⛔ acabamento: a faixa ⛔ pode empurrar o vídeo para fora da
   primeira tela no celular (meta do owner: ~120 px em 390 px de largura, e o player inteiro
   visível em 390×664 e 430×740). É por isso que o padding é 7 px, o `gap` vertical é 2 px e o
   preço e os números crescem por `clamp` com o `vw` — medido em
   `docs/evidence/p48-56/medida-com-e-sem-oferta.json`, e REMEDIDO depois da Etapa 2b. */
.pv-oferta {
  --of-fosco: #B9B7B0;
  background: var(--ink);
  /* ⚠ A tinta da FAIXA, ⛔ só a de cada filho: a frase que existe só para o leitor de tela
     (`sp5-sr-only`) ⛔ declara `color`, e sem esta linha ela herdaria a tinta da página —
     `--ink` sobre `--ink`, 1:1. Ela é oculta por `clip`, então ninguém a veria; mas uma régua
     de contraste com exceção é uma régua que aprende a calar. Herdando papel, ⛔ há exceção. */
  color: var(--paper);
}
.pv-oferta__linha {
  display: flex; flex-wrap: wrap; align-items: baseline; justify-content: center;
  gap: 2px 12px;
  max-width: calc(var(--wide) + 2 * var(--gutter)); margin: 0 auto;
  padding: 7px var(--gutter); text-align: center;
}
body.pv48 .pv-oferta__chamada { margin: 0; font: 500 13.5px/1.35 var(--sans); color: var(--paper); }
.pv-oferta__de { font: 400 13.5px/1.35 var(--sans); color: var(--of-fosco); }
.pv-oferta__por {
  font: 600 clamp(24px, 6.8vw, 30px)/1.05 var(--serif); letter-spacing: -.01em;
  /* ⭐ Etapa 2b: o DOURADO DA MARCA, e ele veste SÓ este nó. `--gold` mora no `:root` de
     `app.css`, que esta página carrega antes das duas folhas dela — ⛔ é cor nova, é o token
     que o portal já usa no logo sobre fundo escuro. ⛔ Confundir com `--accent`: aquele é a
     cor de AÇÃO (o botão), mede 2,33:1 aqui e continua fora da faixa. */
  color: var(--gold);
}
.pv-oferta__nums {
  display: flex; flex-wrap: wrap; min-width: 0; align-items: baseline; justify-content: center;
  gap: 2px 8px;
}
.pv-oferta__cel { display: inline-flex; align-items: baseline; gap: 5px; }
.pv-oferta__n {
  min-width: 2.2ch; text-align: center;
  font: 600 clamp(17px, 4.6vw, 22px)/1 var(--serif); color: var(--paper);
  font-variant-numeric: tabular-nums; font-feature-settings: "tnum" 1;
}
.pv-oferta__u { font: 400 12px/1.35 var(--sans); color: var(--of-fosco); }
/* ⭐ O separador só existe ONDE A CONTAGEM CABE NUMA LINHA, e isso é geometria, ⛔ gosto.
   Medido em 375×548: as quatro unidades quebram em 3 + 1, e o `·` da terceira fica PENDURADO no
   fim da linha — sujeira que o olho lê como erro. Abaixo de 1100px quem separa é o `gap`,
   exatamente como a barra da prova (P48-48) já fazia em toda largura.

   ⚠ E ele é `::after` da CÉLULA, ⛔ um irmão dela: como irmão, seria mais um item do
   `flex-wrap` e poderia quebrar sozinho para a linha de baixo. Como parte da célula, ⛔ pode.
   De quebra, saem três nós do DOM — ele é decoração pura, e os dígitos já são `aria-hidden`.

   ⭐ **P48-58: a fronteira subiu de 600 para 1100 px**, porque é em 1100 que a faixa troca de
   FORMA (ver o bloco da forma compacta, abaixo). Entre 600 e 1099 a contagem passou a usar os
   rótulos curtos (`7d 23h 11m 05s`), e ali o `·` de célula seria um segundo separador na mesma
   linha que o `·` da data já separa. */
@media (min-width: 1100px) {
  .pv-oferta__cel:not(:last-child)::after {
    content: "·"; margin-left: 3px; font: 400 12px/1.35 var(--sans); color: var(--of-fosco);
  }
}
body.pv48 .pv-oferta__fim {
  width: 100%; margin: 0; font: 400 12px/1.35 var(--sans); color: var(--of-fosco);
}
/* A mensagem pós-zero fica SOZINHA na faixa, então ela é a linha principal daquele estado. */
body.pv48 .pv-oferta__acabou {
  width: 100%; margin: 0; font: 500 14px/1.35 var(--sans); color: var(--paper);
}
@media (min-width: 600px) {
  .pv-oferta__linha { gap: 4px 18px; }
  body.pv48 .pv-oferta__chamada { font: 500 15px/1.35 var(--sans); }
  .pv-oferta__de { font: 400 15px/1.35 var(--sans); }
}

/* ── ⭐ P48-58 · A FAIXA DA OFERTA EM UMA LINHA, NO CELULAR ─────────────────────────────────

   `R$ 297 até segunda, 05/10 · 7d 23h 11m 05s`, abaixo de 1100 px. A razão é a da fatia inteira:
   a primeira tela do celular tem de entregar o PLAYER, e três linhas de faixa custam dela. Medido
   em `docs/evidence/p48-58/`.

   ⚠ **A troca é do OLHO, ⛔ da árvore acessível.** `.pv-oferta__larga` esconde por `clip`, com a
   MESMA receita do `sp5-sr-only` da página: a frase do owner inteira e a linha do fim continuam
   sendo lidas, na mesma ordem e com as mesmas palavras, em qualquer largura. `display: none` as
   tiraria da árvore, e o celular perderia "Termina segunda, 05/10, às 23h59. Depois, volta a
   R$ 497." — que é justamente o que o riscado da linha dos botões ⛔ diz por inteiro.

   ⚠ **A forma compacta é DECORAÇÃO** (`aria-hidden` no HTML), então esconder o irmão largo do
   olho ⛔ cria leitura dobrada. Ela usa a MESMA classe do preço (`.pv-oferta__por`), porque o
   destaque dourado da Etapa 2b é decisão do owner e ⛔ muda com a largura.

   ⚠ **O `·` é `::after` do trecho da DATA, ⛔ um irmão** — a lição da Etapa 2, repetida: como
   irmão ele seria mais um item do `flex-wrap` e poderia quebrar sozinho para a linha de baixo. E
   ele só existe a partir de 390 px: medido, em 375 px a faixa ⛔ cabe numa linha (a contagem
   desce), e ali o `·` ficaria PENDURADO no fim da primeira — é o mesmo defeito que a Etapa 2
   corrigiu no separador de célula.

   ⚠ **`gap: 2px 8px` e ⛔ o `18px` de ≥600px**: entre 600 e 1099 a faixa é a compacta, e o vão
   largo de lá empurraria a contagem para a segunda linha em telas que aguentam uma só. */
@media (max-width: 1099.98px) {
  /* ⚠ A receita é a do `sp5-sr-only` SEM o `!important` dele, e ⛔ por descuido: lá o
     `!important` existe porque a classe é geral e pode cair sobre um componente com `position`
     próprio; aqui os dois alvos são nós desta faixa, que ⛔ declaram `position` em regra nenhuma.
     E a folha da barra ⛔ pode ter `!important`: `FolhaDaBarraDaOfertaTests` o proíbe. */
  .pv-oferta__larga {
    position: absolute; width: 1px; height: 1px; overflow: hidden;
    clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap;
  }
  .pv-oferta__linha { gap: 2px 6px; }
  body.pv48 .pv-oferta__curta { margin: 0; }
  .pv-oferta__ate { font: 400 13px/1.35 var(--sans); color: var(--of-fosco); }
  .pv-oferta__u--larga { display: none; }
  /* ⚠ A CONTAGEM APERTA, e ⛔ é gosto — é o que faz a linha única existir. Medido em 390 px: a
     largura útil da faixa é 335 px (390 − 15 da barra de rolagem − 2×20 do vão), a frase do preço
     com a data ocupa 214 px, e sobram 113 px. Com o tamanho de ≥1100 px a contagem mede 168 px e
     DESCE para a segunda linha; com estes números ela mede ~105 px e cabe. ⛔ Toca o preço: o
     destaque dourado de `clamp(24px, 6.8vw, 30px)` é decisão do owner (Etapa 2b) e ⛔ é desta
     fatia opinar sobre ele. */
  .pv-oferta__nums { gap: 2px 4px; }
  .pv-oferta__cel { gap: 2px; }
  /* ⚠ O piso em `ch` continua existindo — ele é o que impede o segundo de empurrar as vizinhas ao
     ir de "9" para "10". 2ch cabe os dois dígitos; abaixo disso o salto volta. */
  .pv-oferta__n { min-width: 2ch; font-size: clamp(12.5px, 3.2vw, 17px); }
  .pv-oferta__u--curta { margin-left: -1px; font-size: 10px; }
}
@media (min-width: 1100px) {
  .pv-oferta__curta, .pv-oferta__u--curta { display: none; }
}
/* ⛔ Ele existe abaixo de 390px, e a fronteira é MEDIDA, ⛔ escolhida. Sem ele a faixa cabe numa
   linha de 375 px para cima (folga de 7,4 px em 375, 11,4 em 390 e 35,9 em 430) e ⛔ cabe em 320
   (precisa de 307,8 px e tem 265). O próprio `·` ocupa ~8 px: em 375 ele consome a folga inteira e
   a contagem DESCE — medido, a faixa foi de 43 px numa linha para 59 px em duas. Então ele começa
   onde a folga o aguenta: 390 px. Abaixo disso quem separa é o `gap`, como a barra da prova
   (P48-48) sempre fez — e é a mesma lição que a Etapa 2 pagou no separador de célula. */
@media (min-width: 390px) and (max-width: 1099.98px) {
  .pv-oferta__ate::after { content: " ·"; }
}

/* ── O topo ─────────────────────────────────────────────────────────────────────────────── */
/* z-index acima do botão fixo (40): o painel do menu é filho do topo e herda o empilhamento dele. */
.pv-topo { position: relative; z-index: 60; border-bottom: 1px solid var(--rule); background: var(--paper); }
.pv-topo__linha {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  max-width: calc(var(--wide) + 2 * var(--gutter)); margin: 0 auto; padding: 0 var(--gutter); min-height: 60px;
}
@media (min-width: 600px) { .pv-topo__linha { min-height: 72px; } }
.pv-marca {
  display: inline-flex; align-items: center; gap: 10px; min-height: 44px;
  font: 600 21px/1 var(--serif); letter-spacing: -.01em; color: var(--ink);
}
.pv-marca img { width: 34px; height: 34px; border-radius: 8px; }
.pv-marca__ponto { color: var(--accent); }

/* O menu. A partir de 960px ele é a linha de âncoras do protótipo; abaixo disso, com
   JavaScript, é um painel por cima da página que o botão "Menu" abre (obs. 12 do owner).

   Sem JavaScript o botão ⛔ não aparece e o topo é o de P48-11: a marca e o Entrar, inline, e as
   âncoras escondidas abaixo de 960px. Um menu que só abre com JavaScript ⛔ não pode ser o único
   caminho até o Entrar. */
.pv-menu { display: flex; align-items: center; gap: 28px; }
body.pv48 .pv-menu .pv48-nav__ancora { display: none; }
@media (min-width: 960px) {
  body.pv48 .pv-menu .pv48-nav__ancora { display: inline-flex; }
}
body.pv48 .pv-menu__link {
  align-items: center; font: 500 15px/1.2 var(--sans); color: var(--ink); text-decoration: none;
  padding: 12px 0; border-bottom: 2px solid transparent;
}
body.pv48 .pv-menu__link:hover, body.pv48 .pv-menu__link:focus-visible { border-bottom-color: var(--accent); }
body.pv48 .pv-entrar {
  display: inline-flex; align-items: center; min-height: 44px; padding: 0 14px;
  border: 1px solid var(--ctl); border-radius: 4px; color: var(--ink);
  font: 600 15px/1 var(--sans); text-decoration: none; white-space: nowrap;
}

.pv-menu__abrir, .pv-menu__fechar { display: none; }

@media (max-width: 959.98px) {
  html.com-js .pv-menu__abrir {
    display: inline-flex; align-items: center; min-height: 44px; padding: 0 14px;
    border: 1px solid var(--ctl); border-radius: 4px; background: var(--paper); cursor: pointer;
    font: 600 15px/1 var(--sans); color: var(--ink);
  }
  html.com-js .pv-menu { display: none; }
  /* Aberto: o painel cobre a página inteira, por cima de tudo, com as âncoras em coluna. */
  html.com-js .pv-menu[data-aberto] {
    position: fixed; inset: 0; z-index: 70; display: flex; flex-direction: column; align-items: stretch; gap: 0;
    padding: 72px var(--gutter) calc(24px + env(safe-area-inset-bottom)); overflow-y: auto;
    background: var(--paper);
  }
  html.com-js .pv-menu[data-aberto] .pv48-nav__ancora {
    display: flex; min-height: 56px; padding: 0; border-bottom: 1px solid var(--rule);
    font: 500 21px/1.25 var(--serif);
  }
  html.com-js .pv-menu[data-aberto] .pv-entrar { align-self: flex-start; margin-top: 24px; min-height: 48px; }
  html.com-js .pv-menu[data-aberto] .pv-menu__fechar {
    position: absolute; top: 8px; right: calc(var(--gutter) - 10px);
    display: grid; place-items: center; width: 48px; height: 48px; padding: 0;
    border: 0; border-radius: 4px; background: none; cursor: pointer; color: var(--ink);
  }
  .pv-menu__fechar svg { width: 22px; height: 22px; }
  /* Com o painel aberto a página de trás ⛔ não rola por baixo dele. */
  html.pv48-menu-aberto, html.pv48-menu-aberto body { overflow: hidden; }
}

/* A âncora para no TÍTULO da sessão (o `id` que `aria-labelledby` já declara), com folga. O
   topo ⛔ não flutua sobre o conteúdo: é respiro, ⛔ não compensação de barra fixa.

   ⭐ P48-48 · o `h3` entra na MESMA regra, e por isso ele entra na mesma declaração, ⛔ numa
   segunda: a âncora do anúncio A5 (`#pv48-volta-do-erro`, o título do item 3 da S8) tem de
   parar onde as quatro do menu param. Duas declarações com o mesmo valor é como elas passariam
   a divergir sem ninguém notar. ⚠ Só `h2` e `h3`: `[id]` solto pegaria cada `article`, `span` e
   `figure` com identificador — e a página tem dezenas. */
body.pv48 main h2[id], body.pv48 main h3[id] { scroll-margin-top: clamp(1.5rem, 4vw, 2.5rem); }

/* ── O aviso de mensuração (P48-13), vestido como nota de rodapé da página ────────────── */
body.pv48 aside[role="note"] { color: var(--soft); }
body.pv48 aside[role="note"] a, body.pv48 aside[role="note"] .linklike { color: var(--accent); }

/* ── O rodapé (`sp4-footer`, marcação do 47º) ─────────────────────────────────────────── */
body.pv48 .sp4-footer {
  padding: 48px 0 56px; border-top: 1px solid var(--rule); background: var(--raised);
  font: 400 14px/1.55 var(--sans); color: var(--soft);
}
body.pv48 .sp4-footer .container {
  width: 100%; max-width: calc(var(--wide) + 2 * var(--gutter)); margin: 0 auto; padding: 0 var(--gutter);
}
body.pv48 .sp4-footer__grid { display: grid; gap: 32px; }
@media (min-width: 760px) { body.pv48 .sp4-footer__grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (min-width: 1100px) { body.pv48 .sp4-footer__grid { grid-template-columns: 1.3fr .8fr .9fr 1.4fr; column-gap: 40px; } }
/* `app.css` grita os títulos do rodapé (`.site-footer h4 { text-transform: uppercase }`) e
   esconde a marca abaixo de 520px (a regra do cabeçalho do portal). Aqui os títulos ficam na
   caixa em que estão escritos, e a marca aparece em qualquer largura. */
body.pv48 .sp4-footer h4 {
  margin: 0 0 10px; font: 700 14px/1.3 var(--sans); color: var(--ink);
  text-transform: none; letter-spacing: 0;
}
body.pv48 .sp4-footer .sp5-footer__brand .brand__mark { display: inline; }
body.pv48 .sp4-footer ul { margin: 12px 0 0; padding: 0; list-style: none; }
body.pv48 .sp4-footer li a, body.pv48 .sp4-footer li .linklike {
  display: inline-flex; align-items: center; min-height: 44px; color: var(--ink);
  text-decoration: underline; text-decoration-color: var(--rule2); text-underline-offset: .18em;
  font: 400 14px/1.4 var(--sans); background: none; border: 0; padding: 0; cursor: pointer;
}
body.pv48 .sp4-footer p, body.pv48 .sp4-footer address { max-width: 46rem; font-style: normal; }
body.pv48 .sp4-footer p + p, body.pv48 .sp4-footer p + address { margin-top: 12px; }
body.pv48 .sp4-footer strong { color: var(--ink); }
body.pv48 .sp4-footer address a { color: var(--accent); overflow-wrap: anywhere; }
body.pv48 .sp5-footer__brand { display: flex; align-items: center; gap: 10px; }
body.pv48 .sp5-footer__brand img { width: 32px; height: 32px; border-radius: 8px; }
body.pv48 .sp5-footer__brand .brand__mark { font: 600 19px/1 var(--serif); color: var(--ink); }
body.pv48 .sp5-footer__brand .dot { color: var(--accent); }
body.pv48 .sp4-footer__legal { margin-top: 40px; padding-top: 20px; border-top: 1px solid var(--rule); }

/* ── P8 · o botão fixo de compra no celular ─────────────────────────────────────────────
   Entra depois da primeira dobra, sai quando o cartão de preço está na tela (para ⛔ não cobrir o
   botão de verdade), e ⛔ não cobre o rodapé nem o aviso de mensuração. Fora de vista, sai também
   da ordem de tabulação (o JS cuida de `tabindex` e `aria-hidden`). */
.pv-fixo {
  position: fixed; z-index: 40; left: 0; right: 0; bottom: 0;
  padding: 10px 16px calc(10px + env(safe-area-inset-bottom));
  background: var(--paper); border-top: 1px solid var(--rule2);
  transform: translateY(110%); visibility: hidden;
}
.pv-fixo[data-visivel] { transform: none; visibility: visible; }
body.pv48 .pv-cta--fixo { display: flex; width: 100%; }
@media (prefers-reduced-motion: no-preference) {
  .pv-fixo { transition: transform 220ms ease-out, visibility 0s linear 220ms; }
  .pv-fixo[data-visivel] { transition: transform 220ms ease-out, visibility 0s; }
}
@media (max-width: 899.98px) { html.com-js body.pv48 { padding-bottom: 76px; } }
@media (min-width: 900px) { .pv-fixo { display: none; } }
/* Zoom de 200 % / telas baixas: nada fixo cobrindo o texto. */
@media (max-height: 500px) { .pv-fixo { display: none; } html.com-js body.pv48 { padding-bottom: 0; } }

/* ══ P48-25 · item 1 — OS ESTADOS DA CASCA ═════════════════════════════════════════════════

   A mesma regra do corpo da página (`pagina-vendas-48-uau.css`, §P48-25 item 1): o `:hover`
   muda borda, fundo ou sublinhado; o `:active` desce 1px; o `:focus-visible` é o anel de 3px
   declarado uma vez na folha de cima. Aqui moram os controles que a casca escreve — o menu, o
   Entrar, os links do rodapé do 47º e o botão fixo do celular.

   ⚠ `body.pv48 .sp4-footer li a` pesa (0,1,3) e já impõe tinta e sublinhado aos links do
   rodapé: o estado deles tem de pesar mais que isso, senão o `main a:hover` da folha de cima
   ⛔ os alcança (eles ⛔ moram em `<main>`) e eles ficariam mortos. */
@media (prefers-reduced-motion: no-preference) {
  body.pv48 .pv-menu__link,
  body.pv48 .pv-entrar,
  html.com-js .pv-menu__abrir,
  body.pv48 .pv-menu__fechar,
  body.pv48 .sp4-footer li a,
  body.pv48 .sp4-footer li .linklike,
  body.pv48 .sp4-footer address a,
  body.pv48 aside[role="note"] a,
  body.pv48 aside[role="note"] .linklike {
    transition:
      background-color 140ms cubic-bezier(.2, .7, .2, 1),
      border-color 140ms cubic-bezier(.2, .7, .2, 1),
      color 140ms cubic-bezier(.2, .7, .2, 1),
      text-underline-offset 140ms cubic-bezier(.2, .7, .2, 1),
      text-decoration-color 140ms cubic-bezier(.2, .7, .2, 1),
      transform 140ms cubic-bezier(.2, .7, .2, 1);
  }
}

/* O menu: a barra âmbar já acendia no hover e no foco (P48-15). Faltava a pressão. */
body.pv48 .pv-menu__link:active { border-bottom-color: var(--accent); transform: translateY(1px); }
body.pv48 .pv-entrar:hover { border-color: var(--ink); }
body.pv48 .pv-entrar:active { border-color: var(--ink); transform: translateY(1px); }
html.com-js .pv-menu__abrir:hover { border-color: var(--ink); }
html.com-js .pv-menu__abrir:active { border-color: var(--ink); transform: translateY(1px); }
body.pv48 .pv-menu__fechar:hover { background: var(--quiet); }
body.pv48 .pv-menu__fechar:active { background: var(--quiet); transform: translateY(1px); }

/* O rodapé do 47º: o sublinhado dele é de fio (`--rule2`); sob a mão ele vira tinta. */
body.pv48 .sp4-footer li a:hover,
body.pv48 .sp4-footer li .linklike:hover { text-decoration-color: var(--ink); text-underline-offset: .28em; }
body.pv48 .sp4-footer li a:active,
body.pv48 .sp4-footer li .linklike:active { color: var(--accent-escuro); text-decoration-color: var(--accent-escuro); }
body.pv48 .sp4-footer address a:hover { text-underline-offset: .28em; text-decoration-thickness: 2px; }
body.pv48 .sp4-footer address a:active { color: var(--accent-escuro); }

/* O aviso de mensuração é parcial COMPARTILHADA, e a casca da 48 já escolheu vesti-la (é o que
   `e_reset_de_elemento` distingue de um reset). O estado segue a mesma escolha. */
body.pv48 aside[role="note"] a:hover,
body.pv48 aside[role="note"] .linklike:hover { text-underline-offset: .28em; text-decoration-thickness: 2px; }
body.pv48 aside[role="note"] a:active,
body.pv48 aside[role="note"] .linklike:active { color: var(--accent-escuro); }
