/* ============================================================================
   NOVA ARQUITETURA — CAMADA DE CHECKOUT

   Este arquivo NÃO é um design system. Ele é a camada que acrescenta ao
   sistema existente as telas que ainda não existiam: a trilha de cinco etapas,
   os cartões de oferta adicional, os parceiros, o resumo lateral e a revisão.

   Tudo o que já existe no produto vem de css/styles.css, carregado ANTES deste
   arquivo em nova.html: paleta, tipografia, reset, `.container`, `.btn` e suas
   variantes, `.site-header`, `.site-footer`, `.pricing-grid`, `.plan-card` e
   toda a anatomia do cartão de plano.

   A versão anterior desta camada redefinia esses componentes com nomes
   próprios (`.plano`, `.cartao-acesso`, `.btn-primario`) e com tokens
   próprios. O resultado era um segundo produto: raio de 24/16/10 contra os
   32/20/14 do original, sombra mais rasa, botão primário navy onde o produto
   usa laranja. Nenhuma dessas diferenças é grande isolada, mas raio de canto,
   profundidade de sombra e cor de CTA são exatamente os sinais que o olho lê
   como "outra marca".

   A arquitetura comercial mudou. A pele, não.
   ========================================================================== */

/* ---------------------------------------------------- tokens desta camada --
   Apelidos para os tokens do sistema, para que as regras abaixo continuem
   legíveis, mais os poucos valores que o sistema ainda não tinha. Não há
   nenhuma cor, raio ou sombra nova: todos apontam para css/styles.css. */
:root {
  --raio-lg:   var(--radius-lg);    /* 32px */
  --raio:      var(--radius);       /* 20px */
  --raio-sm:   var(--radius-btn);   /* 14px */
  --sombra:    var(--shadow);

  /* Sombra em três camadas com spread negativo progressivo — medida na
     referência. Uma sombra só, mesmo bem calibrada, achata; três camadas
     curtas separam o elemento do fundo sem borrão visível. A cor é a do
     sistema (navy), não preto puro. */
  --sombra-flutuante:
    0 0.6px 0.6px -1.25px rgba(8, 30, 45, 0.10),
    0 2.3px 2.3px -2.5px  rgba(8, 30, 45, 0.09),
    0 10px 10px -3.75px   rgba(8, 30, 45, 0.05);
  --sombra-lg: var(--shadow-lg);
  --fonte:     var(--font);
  --largura:   var(--section-w);

  /* Escala de espaçamento em múltiplos de 4. O sistema legado usa valores
     literais; esta camada é nova e pode ser consistente desde o início. */
  --e1: 4px;  --e2: 8px;  --e3: 12px; --e4: 16px;
  --e5: 24px; --e6: 32px; --e7: 48px; --e8: 64px; --e9: 96px;

  /* Verde de confirmação: o sistema não tinha estado "adicionado". */
  --ok:      #0f7a4a;
  --ok-soft: #e7f5ee;

  /* Único override deliberado sobre o sistema. O --gray-light original
     (#8b97a3) rende 2,9:1 sobre branco e reprova em WCAG AA para texto
     pequeno. Nesta jornada ele carrega preço, unidade de cobrança e período
     — informação que decide compra e não pode depender de boa visão. */
  --gray-light: #6b7784;            /* 5,1:1 sobre branco */
}

/* -------------------------------------------------- cabeçalho flutuante --
   Logo e navegação em cartões próprios, como na referência: pílula branca com
   sombra em camadas e desfoque atrás. O desfoque importa quando o cabeçalho
   passa por cima de conteúdo na rolagem — sem ele, o branco chapado corta a
   página em duas. */
.site-header {
  padding-top: var(--e4);
  padding-bottom: var(--e4);
  align-items: center;
}
.topo-cartao {
  background: rgba(255, 255, 255, 0.86);
  backdrop-filter: blur(16px);
  -webkit-backdrop-filter: blur(16px);
  border-radius: 999px;
  box-shadow: var(--sombra-flutuante);
}
.topo-marca { padding: 10px 22px; display: inline-flex; align-items: center; }
.topo-marca .site-header-logo { height: 26px; width: auto; }
.site-nav.topo-cartao { padding: 5px 10px; gap: 2px; }
.site-nav.topo-cartao a { min-height: 34px; padding: 0 16px; border-radius: 999px; }
.site-nav.topo-cartao a:hover { background: var(--cyan-ghost); color: var(--petrol); }

/* Rolagem horizontal travada: o resumo lateral e a trilha são os dois
   componentes com risco real de estourar a largura no mobile. */
html { -webkit-text-size-adjust: 100%; }
body { overflow-x: hidden; }

/* O atributo `hidden` precisa vencer o `display` do sistema.

   O navegador implementa `hidden` como `[hidden] { display: none }` na folha
   do user-agent, que é a camada mais fraca: qualquer regra de autor que
   declare `display` no mesmo elemento a derrota. `.btn` do sistema declara
   `display: inline-flex`, e o JS esconde os botões "Adicionar" justamente
   marcando `hidden` — o resultado era o botão "Adicionar" continuar visível
   ao lado de "Remover" depois que o adicional já tinha sido incluído.

   Vale para todo elemento com `hidden`, não só para o botão em que o defeito
   apareceu: as etapas, as ofertas e os avisos usam o mesmo mecanismo. */
[hidden] { display: none !important; }

/* O reset do sistema não zera marcadores fora do cartão de plano, e as listas
   desta camada (itens da oferta, parceiros, trilha) trazem o próprio ícone —
   sem isto aparecem bolinha e check no mesmo item. */
/* `:where()` zera a especificidade, então este reset perde para qualquer
   regra de layout adiante — sem ele, `.tela-checkout ul` (0-1-1) derrotava
   `.oferta-itens` (0-1-0) e engolia a margem que separa a lista do botão. */
:where(.tela-checkout ul, .tela-checkout ol) { list-style: none; margin: 0; padding: 0; }

/* Foco visível e consistente. Nunca removido: é o único indicador de posição
   para quem navega por teclado. */
:focus-visible {
  outline: 3px solid var(--cyan);
  outline-offset: 3px;
  border-radius: 4px;
}

.skip-link {
  position: absolute;
  left: -9999px;
  top: var(--e3);
  z-index: 100;
  padding: var(--e3) var(--e4);
  background: var(--navy);
  color: #fff;
  border-radius: var(--raio-sm);
  text-decoration: none;
  font-weight: 600;
}
.skip-link:focus { left: var(--e5); }

/* ------------------------------------------------------ botões da camada --
   `.btn`, `.btn-primary`, `.btn-outline`, `.btn-plan` e `.btn-lg` vêm do
   sistema. Só duas variantes são novas, porque só elas não existiam: remover
   um item escolhido e voltar uma etapa. */

.btn-remover {
  background: none;
  color: #a4343a;
  box-shadow: inset 0 0 0 1.5px #e2c3c5;
  font-size: 0.86rem;
  padding: 9px 16px;
}
.btn-remover:hover:not(:disabled) {
  background: #fdf3f3;
  box-shadow: inset 0 0 0 1.5px #a4343a;
}

.btn-texto {
  background: none;
  color: var(--gray);
  padding-left: var(--e3);
  padding-right: var(--e3);
}
.btn-texto:hover:not(:disabled) { color: var(--navy); text-decoration: underline; }

/* O sistema não previa botão desabilitado fora do cartão de plano. */
.btn:disabled { opacity: .5; cursor: not-allowed; transform: none; }

/* CTA ao fim do comparativo. A faixa de fechamento com vídeo vem logo
   depois, mas ficava a uma rolagem inteira de distância — quem termina de ler
   a tabela precisa de um destino imediato. */
.pos-comparativo {
  display: flex;
  justify-content: center;
  margin-top: var(--e7);
}

/* --------------------------------------------------------------- telas -- */

/* A tela de planos usa `.pricing` do sistema. Esta regra vale para a tela de
   checkout, que não tem equivalente legado. */
.tela-checkout { padding: var(--e6) 0 var(--e9); }

/* A vitrine começa logo abaixo do cabeçalho: cada pixel aqui é pixel que o
   cartão não tem na primeira tela. */
.tela-planos.pricing { padding-top: var(--e2); }

/* Na LP a faixa de fechamento mora fora da seção de preços, então o
   `padding-bottom` de `.pricing` cai ENTRE a tabela e a faixa. Aqui a faixa
   está dentro da seção — para que suma junto no checkout — e esse mesmo
   padding caía depois dela, abrindo um vão morto de 84px antes do rodapé.
   O espaçamento é movido para antes da faixa, reproduzindo o ritmo original. */
.tela-planos { padding-bottom: 0; }
.tela-planos .band-wrap { margin-top: clamp(56px, 7vw, 84px); }

/* Fora do container de preços, a faixa precisa da margem lateral que o
   `.container` daria: sem ela, encosta nas bordas em telas estreitas. */
.tela-planos .band-wrap { padding: 0 24px; }
@media (min-width: 1248px) { .tela-planos .band-wrap { padding: 0; } }

/* `.pricing-meta` nasce com `margin-top: -16px` para encaixar o seletor
   Anual/Mensal logo abaixo do subtítulo. Esta tela não tem o seletor — o
   prazo de cobrança foi para dentro do checkout — e a margem negativa colava
   a prova social no texto acima. */
.tela-planos .pricing-meta { margin-top: 6px; }

/* Vitrine de duas colunas, a partir de 640px — que é onde o sistema já
   empilha os cartões. Sem esse limite a regra vencia a media query do sistema
   por especificidade e mantinha duas colunas estreitas no celular, estourando
   a largura da página.

   O Gratuito saiu da vitrine e dois cartões esticados sobre os 1200px do
   container ficariam com 590px cada, quase o dobro dos 387px para que o
   sistema foi desenhado. A largura é limitada para que cada cartão volte à
   proporção correta — e é ela também que mantém as features em uma linha. */
@media (min-width: 641px) {
  .tela-planos .pricing-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    max-width: 820px;
    margin-inline: auto;
  }
}

/* O Gratuito é um botão na faixa, como o "Cadastrar" do cadastro do Portal —
   não mais o link sublinhado que ficava no rodapé. Herda `.btn.btn-outline`
   do sistema; aqui só o que a faixa exige. */

/* Cartão de plano com sete itens de lista fica mais alto que o mínimo de 820px
   que o experimento reservava para a grade. A nova tela tem três cartões de
   alturas naturais diferentes e o alinhamento é feito pelo próprio flex do
   cartão. */
.pricing-grid { min-height: 0; }

/* ----------------------------------------------- altura igualada (15/09) ---
   Os dois cartões passam a ocupar a MESMA altura, e o que sobra vai para o
   miolo. Antes cada um media o próprio conteúdo: o Essencial, com menos
   linhas, terminava acima, e a diferença crescia toda vez que um rótulo do
   Profissional quebrava em duas linhas. O botão de cada cartão parava numa
   altura diferente, e o olho lia os dois como peças desencaixadas.

   `stretch` resolve a altura; a coluna flexível dentro do cartão resolve
   ONDE a sobra aparece — no miolo, empurrando o rodapé para a base, e não
   no fim de tudo, deixando o botão solto no meio do vazio. É o que torna a
   variação de linhas dos rótulos um problema do grid e não da copy. */
/* Subgrid: as três faixas do cartão — preço, lista, ação — passam a dividir
   as MESMAS linhas do grid externo, então cada faixa mede pelo maior dos dois
   cartões e as três alinham entre si.

   Foi a única forma de igualar os blocos de lista. Com flex, cada cartão
   distribuía a própria sobra: o bloco de preço do destaque é mais alto (o selo
   "RECOMENDADO" reserva espaço acima do kicker, e os subtítulos dos dois
   preços têm comprimentos diferentes), sobrava menos miolo para a lista dele e
   as duas <ul> diferiam de 31 a 39px conforme a janela. Nenhum ajuste de
   padding resolveria: a diferença vem de conteúdo, e a copy está fechada. */
.tela-planos .pricing-grid { grid-template-rows: auto 1fr auto; }
.tela-planos .plan-card {
  display: grid;
  grid-template-rows: subgrid;
  grid-row: span 3;
}
/* A lista continua esticando dentro da faixa do meio. */
.tela-planos .plan-corpo { min-height: 0; }

/* ======================================================================
   VITRINE DE PLANOS — layout de referência aprovado pelo owner em 11/09/2026

   Cinco decisões transpostas da referência, e só elas. O resto (paleta,
   tipografia, raios, sombras, botões) continua vindo de css/styles.css:

   1. duas colunas — contexto à esquerda, cartões à direita;
   2. cartão seccionado em cabeçalho, corpo e rodapé;
   3. lista com divisor entre itens, não apenas espaçamento;
   4. rótulo de seção antes da lista;
   5. cartão em destaque com fundo sólido, não com borda.

   O alinhamento item a item por subgrid saiu junto: com cada cartão
   seccionado e com fundo próprio, a pauta compartilhada perde o sentido — os
   cartões passam a ser lidos como blocos independentes, que é o que a
   referência faz.
   ====================================================================== */

/* A hero ocupa a dobra inteira.

   O pedido era duplo e em tensão: dar mais respiro acima dos planos e, ao
   mesmo tempo, impedir que a faixa do Básico apareça cortada no rodapé da
   primeira tela. Margem fixa não resolve os dois — o valor que acerta numa
   janela de 900px deixa a faixa aparecendo numa de 1100px.

   `min-height` em `svh` resolve: o bloco cresce com a janela, `align-content`
   centra os cartões no espaço que sobra (é o respiro pedido) e a faixa, que
   agora é irmã e não filha da grade, começa sempre depois da dobra.

   Os 122px descontados são o cabeçalho com seu padding mais o respiro da
   seção — medido, não estimado: com 132 a faixa parava 4px acima da dobra; `svh` em vez de `vh` porque no mobile a barra do navegador entra e
   sai da conta e `vh` mediria a janela sem ela. */
.precos-layout {
  min-height: calc(100svh - 122px);
  align-content: center;
  display: grid;
  /* A proporção não é estética, é aritmética: os cartões precisam terminar
     com ~382px cada para que as features caibam em uma linha, que é o
     requisito fechado antes. 1200 de container, menos 64 de gap, 1,38/2 da
     sobra para a direita, menos o gap interno, dividido por dois. */
  /* Cartões mais verticais, como os da referência.

     0,78fr é o piso da coluna de contexto, e o que o fixa é o h1: a linha
     mais larga dele mede 437px, e 0,78fr dá 443px. Em 0,76 o título passa a
     quebrar em três linhas.

     Os 40px que a coluna cede vão para os cartões, e existem por um motivo
     específico: o Essencial fica atrás do destaque e perde 24px de largura
     na sobreposição. Sem essa folga, o padding que separa o texto da borda do
     vizinho teria que sair da largura do próprio texto, e as features voltam
     a quebrar em duas linhas.

     As explicações de cada recurso vivem só no comparativo, com o "?" do
     `.tbl-help`. Foram testadas também no cartão e saíram: o ícone custava
     ~22px de cada linha, derrubava rótulos para duas linhas e pesava numa
     leitura que precisa ser de relance. */
  grid-template-columns: minmax(0, 0.78fr) minmax(0, 1.22fr);
  gap: var(--e8);
  align-items: start;
}

/* A coluna de contexto acompanha a rolagem enquanto os cartões passam: o
   fornecedor nunca perde de vista a pergunta que está respondendo. */
/* O kicker é `inline-block` no sistema, mas aqui virou item de um flex
   column — e item de flex estica por padrão (`align-items: stretch`), o que
   esticava a pílula de ponta a ponta da coluna. `align-self` a devolve ao
   tamanho do texto. */
.precos-contexto > .kicker { align-self: flex-start; }

.precos-contexto {
  position: sticky;
  top: var(--e6);
  /* `align-content: center` no layout deixou de esticar os itens, e sem
     altura o `margin-top: auto` da prova não tem espaço para empurrar. */
  align-self: stretch;
  /* Coluna inteira, para que a prova social possa descer até o pé — é onde a
     referência coloca o depoimento, longe do título e à altura do fim dos
     cartões. */
  display: flex;
  flex-direction: column;
  min-height: 560px;
}
.precos-contexto h1 {
  font-size: clamp(1.75rem, 2.6vw, 2.4rem);
  line-height: 1.1;
  letter-spacing: -0.03em;
  color: var(--navy);
  margin: 0 0 var(--e4);
}

/* Bloco de prova, no pé da coluna — sem cartão, no formato de depoimento da
   referência: o texto em corpo maior e, abaixo, o avatar com a linha de prova
   ao lado.

   Sem aspas, de propósito. O texto é descrição de produto, não fala de
   ninguém; entre aspas e logo acima de um avatar, passaria a ler como
   depoimento de uma pessoa que não existe. */
.prova {
  margin-top: auto;
  padding: 0;
  background: none;
  box-shadow: none;
}
.prova-texto {
  font-size: 1.3rem;
  line-height: 1.45;
  letter-spacing: -0.01em;
  color: var(--navy);
  margin: 0 0 var(--e6);
  max-width: 30ch;
}
.precos-contexto .pricing-proof {
  display: flex;
  align-items: center;
  gap: var(--e4);
  margin: 0;
  padding: 0;
  border: 0;
  font-size: 0.86rem;
  line-height: 1.35;
  color: var(--gray);
  text-align: left;
  max-width: 30ch;
}
/* O número em destaque e a frase abaixo, no lugar que a referência dá a nome
   e empresa. A quebra é semântica: o dado é o número. */
.pricing-proof strong {
  display: block;
  font-weight: 700;
  color: var(--navy);
}
.prova-avatar {
  flex-shrink: 0;
  width: 42px;
  height: 42px;
  border-radius: 999px;
  background: var(--cyan-ghost);
  color: var(--petrol);
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.prova-avatar svg { width: 21px; height: 21px; }
.precos-contexto .pricing-proof-dot { display: none; }

/* Dois cartões lado a lado dentro da coluna da direita. O escalonamento
   vertical do cartão em destaque é o que a referência usa para dizer "este é
   o outro patamar" sem precisar de selo — o selo fica, mas já não carrega o
   trabalho sozinho. */
.tela-planos .pricing-grid {
  /* Quanto o destaque avança sobre o vizinho. Um valor só, consumido em dois
     lugares que precisam concordar: a margem negativa do destaque e o padding
     direito do cartão de trás, que tem de somar essa faixa escondida ao
     respiro visível. Separados, um mudava e o outro não. */
  --sobreposicao: var(--e5);
  /* O destaque é mais largo. A hierarquia deixa de depender só da cor: o
     cartão recomendado ocupa mais espaço, começa mais alto e passa por cima
     do vizinho.

     1,10 em vez de 1,14: os 6px que o destaque cede devolvem ao Essencial a
     folga de linha que o respiro da sobreposição consumiu (de 9px de volta
     para 15px na feature mais longa). A hierarquia é a mesma — 306px visíveis
     contra 387px, e o olho não mede 6px num cartão de 390. */
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.10fr);
  gap: 0;
  /* `stretch`, não `start`: é o que iguala a altura dos dois cartões. Com
     `start` cada um media o próprio conteúdo, e a diferença crescia a cada
     rótulo que quebrava em duas linhas — o Essencial terminava acima e os
     botões paravam em alturas diferentes. O escalonamento do destaque
     continua vindo da margem do vizinho, não daqui. */
  align-items: stretch;
  max-width: none;
  margin-inline: 0;
}
/* O recuado agora é o Essencial; o destaque sobe e sobrepõe.

   16px, não os 24px de antes: com a altura igualada os dois rodapés já se
   alinham, então a margem serve só para o recomendado avançar sobre a linha
   do topo. Vinte e quatro empurrava o Essencial para baixo o bastante para
   ele parecer um cartão de outra fileira. */
.tela-planos .plan-card:not(.highlight) {
  margin-top: 16px;
  /* sem margem à direita: ela anulava a margem negativa do recomendado e a
     sobreposição não acontecia — os dois ficavam apenas encostados */
  /* O lado direito passa por baixo do cartão recomendado. Arredondado, a
     curva aparecia flutuando dentro da sobreposição e denunciava que são dois
     retângulos soltos; reto, a borda lê como se continuasse por trás. */
  border-top-right-radius: 0;
  border-bottom-right-radius: 0;
}
.tela-planos .plan-card.highlight {
  margin-top: 0;
  margin-left: calc(var(--sobreposicao) * -1);
  position: relative;
  z-index: 3;
  /* Duas sombras em vez de uma: a rasa desenha o contorno próximo e separa o
     cartão do vizinho por baixo; a profunda dá a distância até a página. Com
     a `--shadow-lg` sozinha o recomendado ficava no mesmo plano do Essencial
     assim que os dois passaram a ter a mesma altura — era a diferença de
     altura que vinha fazendo o trabalho de hierarquia. */
  box-shadow: 0 6px 16px rgba(8, 30, 45, 0.10), 0 32px 80px rgba(8, 30, 45, 0.22);
}

/* Responsivo do layout de duas colunas.

   O `minmax(0, 1fr)` que evita o estouro horizontal também comprime os
   cartões sem limite: em 390px eles chegavam a 98px de largura e a página
   passava no teste de overflow enquanto estava ilegível. O piso vem daqui,
   não do teste. */
@media (max-width: 960px) {
  .precos-layout {
    grid-template-columns: minmax(0, 1fr);
    gap: var(--e7);
  }
  /* Empilhado, a coluna de contexto deixa de acompanhar a rolagem — ela já
     está imediatamente acima dos cartões. */
  .precos-contexto { position: static; text-align: center; }
  .prova-texto { max-width: 60ch; margin-inline: auto; }
  .precos-contexto .pricing-proof { margin-top: var(--e5); }
}

@media (max-width: 640px) {
  .tela-planos .pricing-grid { grid-template-columns: minmax(0, 1fr); }
  /* Empilhado não há linhas para compartilhar: cada cartão volta a medir o
     próprio conteúdo, e o rodapé ancora pelo flex como antes. */
  .tela-planos .plan-card { display: flex; flex-direction: column; grid-row: auto; }
  .tela-planos .plan-corpo { flex: 1 1 auto; }
  /* Um abaixo do outro, escalonamento e sobreposição viram só buraco. */
  .tela-planos .plan-card.highlight { margin-top: 0; margin-left: 0; }
  .tela-planos .plan-card:not(.highlight) { margin-top: 0; margin-right: 0; }
  .tela-planos .pricing-grid { gap: var(--e4); }
  .precos-contexto { min-height: 0; }
  .precos-layout { min-height: 0; }
}

/* --------------------------------------------------------- faixa Básico --
   Os quatro elementos numa linha só, centrada e compacta: rótulo, descrição,
   link de entrada e a justificativa da cobrança. Espalhados pela largura do
   container liam como quatro blocos soltos; juntos, leem como nota de pé de
   página — que é o peso que este bloco deve ter ao lado dos planos pagos. */
/* A faixa acompanha a coluna dos cartões, não a largura da página: ela fala
   dos planos, e centrada no container inteiro flutuava longe deles. Repete a
   grade do layout e ocupa a segunda coluna.

   Fica fora de `.precos-layout` de propósito — é isso que permite ao layout
   ocupar a dobra e à faixa começar depois dela. */
.faixa-gratuito-wrap {
  /* Âncora do balão de "Por que cobramos". A faixa ocupa só a coluna direita
     e é estreita; centrado nela, o balão de 480px saía pela borda da tela.
     Ancorado no wrapper, que tem a largura do container, ele cabe. */
  position: relative;
  display: grid;
  grid-template-columns: minmax(0, 0.85fr) minmax(0, 1.15fr);
  gap: var(--e8);
  margin-top: var(--e7);
}
.faixa-gratuito {
  grid-column: 2;
  padding-top: var(--e5);
  border-top: 1px solid var(--line);
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: center;
  gap: var(--e2) var(--e3);
  text-align: center;
}
/* "Por que cobramos" desce para uma linha própria, abaixo da faixa. */
.faixa-gratuito .porque {
  flex-basis: 100%;
  justify-content: center;
  margin-top: var(--e2);
}
.faixa-gratuito-nome {
  font-size: 0.8rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--navy);
  white-space: nowrap;
}
.faixa-gratuito-texto {
  font-size: 0.86rem;
  line-height: 1.45;
  color: var(--gray);
}

/* Responsivo da faixa, declarado DEPOIS da base pelo mesmo motivo que o
   `.link-gratuito` logo abaixo: media query não soma especificidade, e as
   mesmas três regras viviam no bloco geral do layout, ACIMA — perdiam da base
   por ordem e nunca chegaram a valer. Até 15/09/2026 a faixa ficava com 176px
   dos 370 disponíveis em 390px, espremida contra a borda direita.

   A faixa repete a grade do layout e empilha junto com ele. O breakpoint é o
   mesmo do `.precos-layout`: presa aos 640px, ela manteria duas colunas entre
   641 e 960 depois que os cartões já tivessem empilhado. */
@media (max-width: 960px) {
  .faixa-gratuito-wrap { grid-template-columns: minmax(0, 1fr); gap: 0; }
  .faixa-gratuito { grid-column: 1; }
}
@media (max-width: 640px) {
  /* Empilhada e estreita, a descrição ganha linha própria em vez de dividir a
     primeira com o rótulo "BÁSICO". */
  .faixa-gratuito-texto { flex-basis: 100%; }
}

/* Link sublinhado, não botão. Este bloco existe para informar que a entrada
   gratuita existe, não para disputar a escolha com os planos pagos — um botão
   aqui daria a ele o mesmo peso dos CTAs logo acima. */
/* Alvo de toque de 44px sem inflar a linha: a altura visual fica em 32px e a
   área clicável é esticada por um pseudo-elemento invisível. Aumentar
   `min-height` para 44 empurraria a faixa 12px e ela voltaria a aparecer
   cortada no pé da primeira tela. */
.link-gratuito,
.porque-gatilho {
  position: relative;
  min-height: 32px;
  padding: 0;
  border: 0;
  background: none;
  font-family: inherit;
  font-size: 0.86rem;
  font-weight: 700;
  color: var(--petrol);
  text-decoration: underline;
  text-underline-offset: 3px;
  text-decoration-thickness: 1.5px;
  cursor: pointer;
  transition: color .15s ease;
}
/* Declarado DEPOIS da regra base, e não numa media query antes dela: o rótulo
   tem 284px e só cabe em uma linha acima de 640px. */
@media (min-width: 641px) {
  .link-gratuito { white-space: nowrap; }
}
.link-gratuito::after,
.porque-gatilho::after {
  content: "";
  position: absolute;
  inset: -6px -4px;
}
.link-gratuito:hover,
.porque-gatilho:hover { color: var(--navy); }

/* A justificativa da cobrança é informação de rodapé, não oferta: fica no
   mesmo tom cinza do texto ao lado, não no petrol dos links de ação. */
.porque-gatilho { color: var(--gray); cursor: help; }

/* "Por que cobramos" numa dica, não num acordeão: expandir empurraria o
   comparativo para baixo no meio da leitura.

   O conteúdo é longo para um tooltip — três parágrafos — e isso é deliberado:
   o texto é jurídico e não pode ser resumido sem perder o que protege. O
   balão é largo e ancorado acima do gatilho, e a mesma regra das outras dicas
   vale aqui: pausa antes de abrir no ponteiro, imediato no teclado e no
   toque, Esc fecha, e há ponte sobre o vão para o ponteiro alcançar o texto
   (WCAG 1.4.13). */
.porque {
  display: inline-flex;
  align-items: center;
}
/* Posição calculada em JS (flip + shift), não por `left: 50%`.

   Ancorado no wrapper, o balão nascia centrado na largura do container e o
   bico apontava para o vazio — o gatilho fica na ponta direita da linha.
   Ancorado no gatilho, voltava a estourar a tela em telas estreitas.

   `position: fixed` com `left`/`top` calculados resolve os dois: centra no
   gatilho, desliza até caber na viewport e reposiciona o bico com
   `--bico-x`. Fixed é seguro aqui porque o balão fecha ao rolar. */
.porque-balao {
  position: fixed;
  z-index: 60;
  width: min(480px, calc(100vw - 32px));
  padding: var(--e4) var(--e5);
  /* Claro, não navy: três parágrafos num bloco escuro viravam uma mancha que
     cobria os planos. Em branco com sombra, lê como nota de rodapé. */
  background: var(--card);
  color: var(--gray);
  border-radius: var(--raio);
  box-shadow: var(--sombra-lg);
  font-size: 0.76rem;
  line-height: 1.55;
  text-align: left;
  /* a altura máxima é definida em JS pelo espaço que existe; o texto rola
     dentro do balão quando a tela é curta demais para os três parágrafos */
  overflow-y: auto;
  overscroll-behavior: contain;
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transition: opacity .16s ease, visibility .16s;
}
.porque-balao > span { display: block; }
.porque-balao > span + span { margin-top: var(--e3); }

/* Ponte sobre o vão de 10px até o gatilho, para o ponteiro alcançar o texto
   sem perder o hover (requisito "hoverable" da WCAG 1.4.13). */
.porque-balao::before {
  content: "";
  position: absolute;
  left: 0;
  width: 100%;
  height: 12px;
  top: 100%;
}
.porque-balao[data-lado="baixo"]::before { top: auto; bottom: 100%; }

/* O bico segue o gatilho, não o centro do balão. */
.porque-balao::after {
  content: "";
  position: absolute;
  top: 100%;
  left: var(--bico-x, 50%);
  margin-left: -6px;
  border: 6px solid transparent;
  border-top-color: var(--card);
}
.porque-balao[data-lado="baixo"]::after {
  top: auto;
  bottom: 100%;
  border-top-color: transparent;
  border-bottom-color: var(--card);
}

.porque-gatilho:hover ~ .porque-balao,
.porque-balao:hover {
  opacity: 1;
  visibility: visible;
  pointer-events: auto;
  transition-delay: .45s;
}
.porque-gatilho:focus-visible ~ .porque-balao,
.porque-gatilho[aria-expanded="true"] ~ .porque-balao {
  opacity: 1;
  visibility: visible;
  pointer-events: auto;
  transition-delay: 0s;
}

@media (hover: none) {
  .porque-gatilho:hover ~ .porque-balao,
  .porque-balao:hover { opacity: 0; visibility: hidden; pointer-events: none; }
  .porque-gatilho[aria-expanded="true"] ~ .porque-balao {
    opacity: 1; visibility: visible; pointer-events: auto;
  }
}
@media (prefers-reduced-motion: reduce) {
  .porque-balao { transition: none; }
}

/* Rótulo da seção de recursos: o "RECURSOS" da referência. */
.plan-lista-rotulo {
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--gray-light);
  margin: 0 0 var(--e3);
}
.plan-card.highlight .plan-lista-rotulo { color: rgba(255, 255, 255, 0.62); }

/* Divisor entre itens em vez de espaço solto. É o que dá o ritmo tabular da
   referência e resolve de vez a ambiguidade de agrupamento quando um item
   ocupa duas linhas e o vizinho ocupa uma. */
.tela-planos .plan-card ul { gap: 0; margin-bottom: 0; }
.tela-planos .plan-card li {
  line-height: 1.45;
  /* 5px, não 6: janelas de ~900px não cruzam nenhum dos dois degraus de altura
     abaixo e ficavam 10px fora da dobra depois da copy de 15/09/2026. Baixar a
     base resolve sem inventar um terceiro breakpoint, e devolve 18px em toda
     janela. A entrelinha NÃO entra na conta: apertá-la é decisão de
     tipografia, e o owner autorizou espaçamento. */
  padding: 5px 0;
  border-bottom: 1px solid var(--line);
}
.tela-planos .plan-card li:last-child { border-bottom: 0; padding-bottom: 0; }

/* ------------------------------------------- volume percebido (15/09) ---
   Os dois cartões têm oito features, mas o Essencial ocupa mais linhas de
   texto — três rótulos dele quebram em duas linhas contra dois do
   Profissional. Resultado: o cartão que entrega TUDO do outro mais oito
   parecia o menor dos dois, e a sobra de altura ficava empoçada entre a
   última feature e o rodapé.

   A sobra agora se dilui pelas linhas: a lista ocupa o miolo inteiro e cada
   item cresce por igual. Nenhuma palavra muda e as duas listas terminam na
   mesma altura, que é o que o olho usa para comparar volume. */
.tela-planos .plan-corpo { display: flex; flex-direction: column; }
.tela-planos .plan-card ul { flex: 1 1 auto; display: flex; flex-direction: column; }
.tela-planos .plan-card li { flex: 1 1 auto; }

.tela-planos .plan-card li svg { margin-top: calc((1.45em - 16px) / 2); }
.plan-card.highlight li { border-bottom-color: rgba(255, 255, 255, 0.12); }

/* Destaque com fundo sólido: o cabeçalho mais escuro, o corpo um tom acima —
   a mesma separação que a referência faz com dois azuis. */
/* O fundo pertence ao CARTÃO, não à faixa.

   Estava aplicado ao `.plan-topo`: uma elipse de 420x260 dimensionada para o
   cartão inteiro, espremida numa faixa de ~230px, sobrava pelas bordas. E o
   corpo trocava `--navy` por `--navy-2`, o que abria uma linha de corte no
   meio do cartão.

   Agora o cartão carrega o gradiente do sistema, inteiro, e as faixas se
   distinguem por um véu translúcido — separação sem quebrar o fundo ao meio. */
/* O cartão em três faixas. O padding sai do cartão e passa para cada faixa,
   porque cada uma tem borda e ritmo próprios.

   Zerar o padding do cartão é parte da mesma regra, não um detalhe: o
   `.plan-card` do sistema traz `26px 30px 28px`, e enquanto ele ficou de pé as
   duas camadas somaram. O texto perdia 108px de largura num cartão de 305px e
   oito features quebravam em duas linhas — parecia problema de copy, era
   padding dobrado.

   Este bloco já se perdeu uma vez em edição por índice de string, e o sintoma
   foi silencioso: as faixas ficaram com padding 0 e o texto colou na borda.
   Há teste cobrindo os paddings agora. */
.tela-planos .plan-card { padding: 0; }
.tela-planos .plan-topo {
  position: relative;
  padding: var(--e5) var(--e5) var(--e4);
  border-top-left-radius: var(--raio-cartao, 24px);
  border-top-right-radius: var(--raio-cartao, 24px);
}
.tela-planos .plan-corpo {
  flex: 1;
  padding: var(--e4) var(--e5) var(--e2);
  border-top: 1px solid var(--line);
}
/* O resumo acima do CTA pertence ao botão, não à lista: colado nas features
   ele lê como mais um item. Respiro acima, encostado no CTA abaixo. */
.tela-planos .plan-rodape {
  padding: var(--e5) var(--e5) var(--e5);
  border-bottom-left-radius: var(--raio-cartao, 24px);
  border-bottom-right-radius: var(--raio-cartao, 24px);
}
.tela-planos .plan-card:not(.highlight) .plan-topo { border-top-right-radius: 0; }
.tela-planos .plan-card:not(.highlight) .plan-rodape { border-bottom-right-radius: 0; }

/* O cartão de trás precisa de padding para DOIS limites, não um.

   À esquerda a borda dele é a borda que se vê: 24px bastam. À direita a borda
   que se vê é a do destaque, 24px antes — a sobreposição. Com o mesmo padding
   dos dois lados, os 24px da direita ficavam inteiros escondidos e o texto
   terminava exatamente onde o destaque começa: respiro zero, medido em todas
   as larguras. Era o que dava a impressão de aperto, não a sobreposição em si.

   Na referência o cartão de trás também é cortado, mas o texto dele para bem
   antes do vizinho. Somar a sobreposição ao padding reproduz isso: 24px de
   sobreposição mais 24px de respiro visível, simétrico com a esquerda.

   O valor acompanha `margin-left` do destaque — os dois têm que mudar juntos,
   por isso o token `--sobreposicao`, declarado na grade. */
.tela-planos .plan-card:not(.highlight) .plan-topo,
.tela-planos .plan-card:not(.highlight) .plan-corpo,
.tela-planos .plan-card:not(.highlight) .plan-rodape {
  padding-right: calc(var(--e5) + var(--sobreposicao));
}

/* O selo "Recomendado" fica a 13px acima do cartão e é irmão das faixas. Os
   dois são posicionados com `z-index: auto`, então quem pinta por último é
   quem vem depois no DOM — e o selo vem antes. Enquanto o cartão tinha 26px de
   padding o selo caía na sobra e ninguém percebia; sem padding, a metade de
   baixo do selo passou a ficar sob `.plan-topo`. Uma camada resolve. */
.tela-planos .plan-tag { z-index: 1; top: -18px; }

/* O selo precisa do próprio respiro, e ele não pode vir do padding comum.

   O selo tem 30px de altura e sai 18px para fora, então invade 12px do topo do
   cartão. O padding do `.plan-topo` é o que separa essa invasão do kicker — e
   ele encolhe nas janelas baixas (24px → 16px → 12px). O resultado media 7px de
   vão em 900px, −1px em 768 e −5px em 720: nas duas janelas menores o selo
   passava por cima de "Para operação contínua".

   Um valor fixo só para o cartão em destaque resolve nos quatro tamanhos. Tem
   especificidade maior que as media queries acima, então vence sem depender de
   ordem — 30px de padding menos 12px de invasão dão 18px de vão constante. */
.tela-planos .plan-card.highlight .plan-topo { padding-top: 30px; }

/* Janela baixa: o mesmo cartão, com o ritmo comprimido.

   Num notebook de 768px de altura útil o cartão terminava 38px abaixo da
   dobra, e o que ficava cortado era justamente a microcópia sob o CTA — meia
   linha de texto aparecendo, que foi a reclamação. Escalar a tipografia mudaria
   a hierarquia; comprimir o espaço entre as linhas não muda nada do que se lê.

   Precisa vir DEPOIS das regras base: media query antes da regra perde por
   ordem de origem, com a mesma especificidade. */
@media (max-height: 820px) {
  .tela-planos .plan-topo { padding: var(--e4) var(--e5) var(--e2); }
  .tela-planos .plan-corpo { padding: var(--e2) var(--e5) var(--e2); }
  .tela-planos .plan-rodape { padding: 14px var(--e5); }
  /* A altura volta pelo ritmo da lista, não pelo rodapé: a entrelinha continua
     a mesma, muda só a distância até o filete, e o ganho se dilui por nove
     linhas em vez de concentrar numa borda. Medido em 15/09/2026, depois que a
     copy nova levou o cartão a 700px: 3px→2px devolve 18px. */
  .tela-planos .plan-card li { padding: 2px 0; }

  /* O respiro do bloco de preço, não a tipografia. Somadas, as quatro margens
     do topo valem 40px em janela alta — mais do que o cartão inteiro precisa
     para trazer a microcopy de volta para dentro da dobra. Nenhum corpo de
     texto muda de tamanho, nenhuma entrelinha se aperta e o rodapé fica
     intacto: o que encolhe é a distância entre o kicker, o nome, a promessa e
     o preço, que em janela baixa é justamente onde o olho menos sente. */
  .tela-planos .plan-kicker  { margin-bottom: 6px; }
  .tela-planos .plan-card h3 { margin-bottom: 4px; }
  .tela-planos .plan-tagline { margin-bottom: 8px; }
  .tela-planos .plan-price   { margin-bottom: 8px; }
  .tela-planos .plan-lista-rotulo { margin-bottom: 6px; }
  .tela-planos .plan-corpo   { padding-top: 0; padding-bottom: 0; }
}

/* Segundo degrau, para a janela de 720px de altura útil. É o piso: abaixo
   disso o cartão não cabe sem mexer na tipografia, e aí a página rola — o que
   é o comportamento correto, não um defeito a ser espremido. */
@media (max-height: 740px) {
  .tela-planos .plan-topo { padding: var(--e3) var(--e5) var(--e1); }
  .tela-planos .plan-corpo { padding: var(--e1) var(--e5) var(--e1); }
  /* O rodapé não desce de 12px: abaixo disso o vão acima do resumo fica menor
     que os 14px que o separam do CTA, e o resumo volta a ler como item da
     lista — o inverso do que o owner pediu. A altura que falta sai da lista,
     que tem nove linhas para diluir. */
  .tela-planos .plan-rodape { padding: 12px var(--e5); }
  /* Piso: 1.5px→0 devolve 27px, medidos. O filete de 1px continua separando os
     itens, então a lista fica densa mas não vira bloco. Abaixo daqui só sobra
     mexer na entrelinha ou encurtar rótulo — decisões que não são de
     espaçamento e que o owner ainda não tomou. */
  .tela-planos .plan-card li { padding: 0; }

  /* Segundo passo do mesmo mecanismo. O padding-top do destaque cede 4px e
     não mais: são eles que dão o vão entre o selo "RECOMENDADO" e o kicker, e
     o QA exige 14px ali — abaixo disso o selo lê como se estivesse pousado em
     cima do texto, que foi o defeito que criou aquela trava. */
  .tela-planos .plan-kicker  { margin-bottom: 4px; }
  .tela-planos .plan-tagline { margin-bottom: 6px; }
  .tela-planos .plan-price   { margin-bottom: 6px; }
  .tela-planos .plan-card.highlight .plan-topo { padding-top: 26px; }
  /* Os últimos pixels que faltavam para o cartão INTEIRO caber em 720px — a
     microcopy "Sem pagamento" estava 2px fora, e é ela que responde à dúvida
     que o botão levanta. O rótulo da lista perde só a margem; o texto fica. */
  .tela-planos .plan-lista-rotulo { margin-bottom: 0; }
}

.tela-planos .plan-card.highlight .plan-topo,
.tela-planos .plan-card.highlight .plan-corpo,
.tela-planos .plan-card.highlight .plan-rodape {
  /* Nenhum fundo nas faixas: o gradiente do cartão atravessa inteiro. Qualquer
     véu, por mais leve, desenha uma aresta horizontal no meio do cartão — foi
     o que fez o azul parecer remendado. A separação fica por conta da linha de
     1px, que é o suficiente. */
  background: transparent;
  border-top-color: rgba(255, 255, 255, 0.09);
}

/* Itens herdados de outro plano ("tudo do Essencial") e itens ainda em
   avaliação precisam se distinguir dos itens entregues. */
.plan-feature-heranca .plan-feature-label { font-weight: 600; color: var(--navy); }
.plan-card.highlight .plan-feature-heranca .plan-feature-label { color: #fff; }
.plan-feature-avaliacao { color: var(--gray-light); }
.plan-feature-avaliacao svg { color: #c3ced8; }
.plan-card.highlight .plan-feature-avaliacao { color: rgba(255, 255, 255, 0.62); }
.plan-card.highlight .plan-feature-avaliacao svg { color: rgba(255, 255, 255, 0.4); }

/* O resumo acima do CTA usa `.plan-limit` do sistema, inclusive a margem de
   14px que ele já define — não sobrescrever.

   O que precisa de correção é a ancoragem. No subgrid, esta linha tem a
   altura do conteúdo mais alto entre os três cartões: o Gratuito ocupa duas
   linhas com a delimitação de modalidades, os outros dois ocupam uma. Com o
   alinhamento padrão ao topo, o texto curto ficava a 37px do botão, contra
   14px no original — exatamente o distanciamento percebido. Ancorar ao fim da
   linha cola o texto no CTA nos três cartões, seja qual for a altura. */
.tela-planos .plan-card .plan-limit { align-self: end; }

/* Selo de contexto no cabeçalho, no lugar dos links de navegação da LP. */
.site-nav-selo {
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--gray-light);
}

/* ------------------------------------------------------------ etiquetas -
   `.plan-tag` do sistema cobre o selo do cartão em destaque. Estas três são
   etiquetas em linha, dentro de texto, e não existiam. */

.etiqueta {
  display: inline-block;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  padding: 3px 8px;
  border-radius: 999px;
  vertical-align: middle;
}
.etiqueta-avaliacao { background: #eef1f4; color: #5a6672; }
.etiqueta-economia  { background: #fdf0d6; color: #7a5205; }
.etiqueta-terceiro  { background: var(--cyan-soft); color: #075a64; }

/* ------------------------------------------------------------- checkout - */

.voltar-planos {
  display: inline-flex;
  align-items: center;
  gap: var(--e2);
  min-height: 44px;
  padding: var(--e2) var(--e3);
  margin-bottom: var(--e5);
  background: none;
  border: none;
  font-family: inherit;
  font-size: 15px;
  font-weight: 600;
  color: var(--gray);
  cursor: pointer;
}
.voltar-planos:hover { color: var(--navy); text-decoration: underline; }

/* sem compromisso ---------------------------------------------------------
   Três avisos, e só três. A promessa "acesso antecipado, sem compromisso"
   valia na vitrine e terminava nela: o checkout falava em "Cobrança",
   "Adicionar VIP" e "+ R$ 60 /mês" sem uma linha dizendo que ninguém cobra.

   Cada um mora no ponto onde a dúvida nasce — a entrada da tela, a linha logo
   abaixo do botão de adicionar, e o topo da etapa chamada Cobrança. Nenhum
   deles anuncia pesquisa: o vocabulário é o de um produto em acesso
   antecipado, que é o que a pessoa acha que está vendo. */

/* Reusa `.kicker` da vitrine — mesma pílula, mesmo texto. Só o espaçamento
   muda: aqui ele fica entre o "Trocar de plano" e a trilha, não acima de um
   título de página. */
.checkout-kicker { margin-bottom: 18px; }

/* OS DOIS AVISOS SÃO `.plan-microcopy`, A MESMA CLASSE DOS CARTÕES DA VITRINE.
   Lá ela já carrega "Saiba mais aqui. Sem pagamento." debaixo de cada CTA, com
   o tamanho, a cor e a centralização que fazem um texto ler como microcopy e
   não como parágrafo. Nasceu aqui uma classe paralela com exatamente os mesmos
   valores — duas verdades para a mesma decisão, e a primeira edição de uma
   delas já teria aberto a divergência. Reusar torna a paridade estrutural: o
   dia em que o microcopy da vitrine mudar, o do checkout muda junto.

   Só o posicionamento é local, porque só ele muda de contexto. */

/* Debaixo do "Adicionar VIP", atravessando as duas colunas da faixa. A margem
   vem daqui e não de `.plan-microcopy`: a faixa é grid e já tem `row-gap`, e a
   margem do cartão somaria a ele. */
.oferta-vip-acao > .vip-microcopy { grid-column: 1 / -1; grid-row: 3; margin: 0; }

/* Na etapa de Cobrança o aviso segue o alinhamento do `<h2>` que ele responde.
   Centralizado ali, leria como legenda de um bloco que ainda não começou. */
.cobranca-microcopy { margin: 6px 0 18px; text-align: left; }

/* trilha ------------------------------------------------------------------ */

.trilha { margin-bottom: var(--e7); }
.trilha-lista {
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  gap: var(--e2);
}
.trilha-item {
  display: flex;
  align-items: center;
  gap: var(--e2);
  padding-top: var(--e3);
  border-top: 3px solid var(--line);
  font-size: 13px;
  font-weight: 600;
  color: var(--gray-light);
}
.trilha-num {
  flex: none;
  display: grid;
  place-items: center;
  width: 22px; height: 22px;
  border-radius: 50%;
  background: var(--line);
  color: var(--gray);
  font-size: 12px;
}
.trilha-item[data-estado="ativa"] { border-top-color: var(--navy); color: var(--navy); }
.trilha-item[data-estado="ativa"] .trilha-num { background: var(--navy); color: #fff; }
.trilha-item[data-estado="concluida"] { border-top-color: var(--cyan); color: var(--petrol); }
.trilha-item[data-estado="concluida"] .trilha-num { background: var(--cyan); color: #fff; }
.trilha-item[data-estado="concluida"] .trilha-num::after { content: "\2713"; }
.trilha-item[data-estado="concluida"] .trilha-num { font-size: 0; }
.trilha-item[data-estado="concluida"] .trilha-num::after { font-size: 12px; }

.trilha-nome { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* colunas ----------------------------------------------------------------- */

.checkout-colunas {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 340px;
  gap: var(--e6);
  align-items: start;
}

.checkout-principal {
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--raio-lg);
  padding: var(--e7);
}

.etapa-conta {
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--petrol);
  margin-bottom: var(--e3);
}
.etapa h2 {
  font-size: clamp(22px, 2.6vw, 30px);
  font-weight: 700;
  color: var(--navy);
  margin-bottom: var(--e3);
}
.etapa-sub { font-size: 16px; color: var(--gray); margin-bottom: var(--e6); }

.etapa-acoes {
  display: flex;
  align-items: center;
  gap: var(--e3);
  margin-top: var(--e6);
  padding-top: var(--e5);
  border-top: 1px solid var(--line);
  flex-wrap: wrap;
}
/* "Continuar sem" fica à direita e com peso honesto: é secundário, não
   escondido — a reunião pediu que recusar o adicional seja tão claro quanto
   aceitá-lo. */
.etapa-acoes [data-avancar] { margin-left: auto; }

/* cartão de acesso -------------------------------------------------------- */

.cartao-acesso {
  padding: var(--e5);
  background: var(--tile);
  border: 1px solid var(--line);
  border-radius: var(--raio);
}
.cartao-acesso-topo {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--e4);
  flex-wrap: wrap;
  margin-bottom: var(--e2);
}
.cartao-acesso-topo h3 { font-size: 20px; color: var(--navy); }
.cartao-acesso-preco { font-size: 17px; font-weight: 700; color: var(--navy); }
.cartao-acesso-promessa { font-size: 15px; color: var(--gray); }

/* período ----------------------------------------------------------------- */

/* ============================ cobrança: a escolha do ciclo ==================
   Reescrito em 14/09/2026 no padrão da referência (LATAM), a pedido do owner.

   Antes eram dois cartões empilhados com o rádio NATIVO do sistema
   operacional, estilizado só por `accent-color`. Funcionava, mas o controle
   mudava de forma entre Windows, macOS e Android, e num cartão desenhado isso
   lê como campo de formulário administrativo — não como escolha comercial.

   Agora o input nativo continua no DOM, invisível, e a marca redonda é
   desenhada. O que se ganha: aparência idêntica em todo lugar, animação na
   seleção, e foco de teclado visível. O que NÃO se perde: navegação por setas,
   `name`/`value` no submit e o anúncio do grupo pelo leitor de tela, porque o
   `<fieldset>`/`<legend>` e o `<input type="radio">` continuam sendo os de
   verdade. */

/* Sem moldura própria: o cartão da etapa é o ÚNICO cartão, a mesma regra que
   vale no upgrade. Borda dentro de borda cortava o bloco em duas caixas
   concêntricas e inflava a altura sem acrescentar informação — eram 372px para
   um conteúdo que cabe em bem menos. */
.cobranca {
  display: grid;
  grid-template-columns: 208px minmax(0, 1fr);
  gap: var(--e5);
  align-items: center;
  margin-top: var(--e4);
}
/* `[hidden]` é `display: none` por folha do navegador, e perde para o
   `display: grid` declarado acima — a mesma armadilha que já custou uma
   regressão no botão do upgrade. Planos por participação escondem este bloco,
   então sem esta linha o cartão continuaria na tela. */
.cobranca[hidden] { display: none; }
.cobranca-arte img { display: block; width: 100%; height: auto; }

.cobranca-promessa {
  font-size: 14.5px;
  line-height: 1.5;
  color: var(--gray);
  margin-bottom: var(--e4);
  max-width: 62ch;
}
.cobranca-promessa strong { color: var(--ink); font-weight: 700; }

/* O fieldset perde a cara de fieldset: o cartão já é a moldura, e uma segunda
   borda aqui dentro cortaria o bloco em duas caixas concêntricas. */
.periodo { margin: 0; padding: 0; border: 0; }
.periodo-legenda {
  padding: 0;
  margin-bottom: var(--e3);
  font-size: 15px;
  font-weight: 600;
  color: var(--navy);
}
.periodo-opcoes { display: flex; flex-direction: column; gap: var(--e2); }

/* A opção INTEIRA é o alvo — não só a bolinha. Resolve de uma vez o alvo de
   toque, a área de clique e o destaque do estado. */
.periodo-opcao {
  border: 1.5px solid var(--line);
  border-radius: var(--raio-sm);
  transition: border-color .18s ease, background-color .18s ease, box-shadow .18s ease;
}
.periodo-opcao:hover { border-color: #c3ced8; background: var(--tile); }
/* `box-shadow` em vez de engrossar a borda: a caixa não muda de tamanho, então
   nada pula ao trocar de opção. */
.periodo-opcao:has(input:checked) {
  border-color: var(--petrol);
  background: var(--cyan-ghost);
  box-shadow: inset 0 0 0 1px var(--petrol);
}

/* Invisível, mas PRESENTE: `display: none` tiraria o controle da ordem de
   tabulação e mataria a navegação por setas. */
.periodo-opcao input {
  position: absolute;
  width: 0;
  height: 0;
  margin: 0;
  appearance: none;
}
.periodo-opcao label {
  display: flex;
  align-items: flex-start;
  gap: var(--e3);
  padding: var(--e3) var(--e4);
  cursor: pointer;
}

.periodo-marca {
  width: 24px;
  height: 24px;
  flex: none;
  margin-top: 1px;
  border: 2px solid var(--petrol);
  border-radius: 50%;
  display: inline-flex;
  transition: box-shadow .16s ease;
}
/* O ponto cresce de 0 a 1 com um leve estouro. A referência usa
   `display: none → block`, que é impossível de animar; aqui a mudança de
   escala entrega a confirmação da escolha. Overshoot é apropriado neste caso
   e só neste: é resposta a um clique, não movimento de layout. */
.periodo-marca::before {
  content: "";
  width: 14px;
  height: 14px;
  margin: auto;
  background: var(--petrol);
  border-radius: 50%;
  transform: scale(0);
  opacity: 0;
  transition: transform 160ms cubic-bezier(.34, 1.56, .64, 1), opacity 120ms linear;
}
.periodo-opcao input:checked + label .periodo-marca::before {
  transform: scale(1);
  opacity: 1;
}
/* Halo de 6px no hover: amplia o alvo VISUAL sem alterar o layout. */
.periodo-opcao label:hover .periodo-marca { box-shadow: 0 0 0 6px var(--cyan-ghost); }
/* A referência não tem NENHUMA regra de foco: com o input em 0×0, quem navega
   por teclado fica sem saber onde está. Falha WCAG 2.4.7. Três linhas. */
.periodo-opcao input:focus-visible + label .periodo-marca {
  outline: 3px solid var(--cyan);
  outline-offset: 3px;
}

.periodo-corpo { display: flex; flex-direction: column; gap: 1px; }
.periodo-titulo {
  font-size: 15px;
  font-weight: 700;
  color: var(--navy);
  display: flex;
  align-items: center;
  gap: var(--e2);
  flex-wrap: wrap;
  margin-bottom: 2px;
}
/* Duas linhas com pesos deliberadamente distantes. O equivalente mensal é o
   número em que as duas opções se comparam, então é ele que fica grande; o
   valor que sai da conta do fornecedor vem logo abaixo, menor — menor, jamais
   ausente, porque omitir o valor cheio é drip pricing. */
.periodo-preco {
  font-size: 18px;
  font-weight: 700;
  line-height: 1.25;
  letter-spacing: -0.01em;
  color: var(--navy);
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 0 8px;
}
/* O preço cheio ao lado do com desconto, em corpo menor, peso normal e cinza:
   a diferença tem de ser legível de relance, sem disputar o número que decide.
   Mesma linha de base — empilhado, lia como dois preços, não como um preço e
   sua referência. */
.periodo-riscado {
  font-size: 14px;
  font-weight: 500;
  letter-spacing: 0;
  color: var(--gray-light);
  text-decoration-thickness: 1.5px;
}
.periodo-riscado[hidden] { display: none; }
.etiqueta-economia[hidden] { display: none; }
.periodo-detalhe { font-size: 12.5px; line-height: 1.4; color: var(--gray-light); }
.periodo-nota { font-size: 12.5px; color: var(--gray-light); margin-top: var(--e3); }

/* O avançar alinhado ao fim da coluna de conteúdo, onde a leitura termina.
   Reaproveita `.link-continuar` do upgrade em vez de criar um segundo estilo
   de link: dois links de avançar com pesos diferentes seriam lidos como duas
   ações diferentes. */
.cobranca-avancar {
  display: flex;
  justify-content: flex-end;
  margin-top: var(--e4);
}

/* Verde, e não o âmbar da pílula de economia: são coisas diferentes e não
   podem parecer a mesma. "Recomendado" é orientação; o valor economizado, que
   é a razão dela, vive junto do preço. */
.etiqueta-recomendado { background: var(--ok-soft); color: var(--ok); }

@media (max-width: 560px) {
  .cobranca { grid-template-columns: minmax(0, 1fr); }
  .cobranca-arte { max-width: 168px; margin: 0 auto; }
}

/* ofertas ----------------------------------------------------------------- */

.oferta {
  padding: var(--e6);
  border: 1.5px solid var(--line);
  border-radius: var(--raio);
  transition: border-color .18s ease, background-color .18s ease;
}
.oferta[data-selecionado="sim"] {
  border-color: var(--ok);
  background: var(--ok-soft);
}
.oferta-topo {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--e4);
  flex-wrap: wrap;
  margin-bottom: var(--e4);
}
.oferta-topo h3 { font-size: 20px; color: var(--navy); }
.oferta-preco { font-size: 18px; font-weight: 700; color: var(--navy); white-space: nowrap; }

.oferta-itens { display: flex; flex-direction: column; gap: var(--e3); margin-bottom: var(--e5); }
.oferta-itens li {
  position: relative;
  padding-left: 26px;
  font-size: 15px;
  color: var(--gray);
}
.oferta-itens li::before {
  content: "";
  position: absolute;
  left: 0; top: 8px;
  width: 14px; height: 8px;
  border-left: 2px solid var(--cyan);
  border-bottom: 2px solid var(--cyan);
  transform: rotate(-45deg);
}
.oferta-itens strong { color: var(--ink); }
.oferta-itens-simples li { color: var(--gray); }

/* Dentro da coluna de texto do cartão, e não abaixo dele: `margin-top` no
   lugar do `margin-bottom`, porque agora ele fecha o bloco em vez de separar
   o cartão do que vem depois. */
.oferta-vip .oferta-limite { margin: var(--e4) 0 0; }
.oferta-limite {
  padding: var(--e3) var(--e4);
  margin-bottom: var(--e5);
  background: #f6f8fa;
  border-left: 3px solid #c3ced8;
  border-radius: 0 var(--raio-sm) var(--raio-sm) 0;
  font-size: 13px;
  color: var(--gray);
}
.oferta[data-selecionado="sim"] .oferta-limite { background: #ffffff9c; }

.oferta-acoes { display: flex; gap: var(--e3); flex-wrap: wrap; }
.oferta-estado {
  margin-top: var(--e3);
  font-size: 14px;
  font-weight: 600;
  color: var(--ok);
}
.oferta-estado:empty { margin-top: 0; }

.oferta-bloqueada { background: var(--tile); }
.oferta-bloqueada h3 { font-size: 20px; color: var(--navy); margin-bottom: var(--e3); }
.oferta-bloqueada > p { font-size: 15px; color: var(--gray); margin-bottom: var(--e4); }
.oferta-bloqueada .oferta-limite { margin: var(--e4) 0 0; }

/* parceiros --------------------------------------------------------------- */

/* DORMENTE desde 14/09/2026 — nenhum destes seletores casa com algo no ar.
   A seção de serviços de parceiros saiu do checkout a pedido do owner, com a
   palavra "por enquanto", e o HTML dela está inteiro em
   qa/nova/secao-parceiros.html. As regras ficam aqui de propósito: colar o
   HTML de volta é o bastante para a seção voltar a funcionar. Se a decisão
   virar definitiva, apagar daqui até `.parceiro-estado:empty`, mais
   `.resumo-parceiros` e `.dados-titulo-parceiros` mais abaixo. */
.parceiros { display: grid; gap: var(--e4); }
.parceiro {
  padding: var(--e5);
  border: 1.5px solid var(--line);
  border-radius: var(--raio);
  transition: border-color .18s ease, background-color .18s ease;
}
.parceiro[data-selecionado="sim"] { border-color: var(--ok); background: var(--ok-soft); }
.parceiro-cabeca {
  display: flex;
  align-items: center;
  gap: var(--e3);
  flex-wrap: wrap;
  margin-bottom: var(--e2);
}
.parceiro-cabeca h3 { font-size: 18px; color: var(--navy); }
.parceiro > p { font-size: 15px; color: var(--gray); margin-bottom: var(--e4); }
.parceiro-estado { margin-top: var(--e3); font-size: 14px; font-weight: 600; color: var(--ok); }
.parceiro-estado:empty { margin-top: 0; }

/* revisão ----------------------------------------------------------------- */

.revisao { display: flex; flex-direction: column; gap: var(--e3); }

/* Grade de três faixas em vez de flex: a terceira coluna reserva o espaço do
   botão Remover mesmo nas linhas que não têm um — sem isso, a linha da base
   (não removível) empurra seu valor para a direita e a coluna de preços deixa
   de ser uma coluna. */
.revisao-linha {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto 92px;
  align-items: center;
  gap: var(--e4);
  padding: var(--e4) var(--e5);
  background: var(--tile);
  border: 1px solid var(--line);
  border-radius: var(--raio-sm);
}
.revisao-corpo { min-width: 0; }
.revisao-nome  { font-size: 16px; font-weight: 700; color: var(--navy); }
.revisao-desc  { font-size: 14px; color: var(--gray); }
.revisao-valor { font-size: 16px; font-weight: 700; color: var(--navy); white-space: nowrap; text-align: right; }
.revisao-linha .btn-remover { padding: 8px var(--e3); min-height: 36px; font-size: 13px; }

.revisao-grupo-titulo {
  margin-top: var(--e4);
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--gray-light);
}

.revisao-total {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: var(--e4);
  margin-top: var(--e4);
  padding: var(--e5);
  background: var(--navy);
  border-radius: var(--raio-sm);
  color: #fff;
}
.revisao-total-rotulo { font-size: 15px; font-weight: 600; }
.revisao-total-valor  { font-size: 26px; font-weight: 700; }

/* resumo lateral ---------------------------------------------------------- */

.resumo {
  position: sticky;
  top: var(--e5);
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--raio-lg);
  overflow: hidden;
}
.resumo-alternar,
.resumo-alca,
.resumo-backdrop,
.resumo-barra { display: none; }

.resumo-corpo { padding: var(--e5); }
.resumo-titulo {
  font-size: 13px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--gray-light);
  margin-bottom: var(--e4);
}

/* ------------------------------------ linhas expansíveis (15/09/2026) ---
   Cada linha do resumo abre o que ela contém: o plano mostra as features do
   próprio cartão, o VIP mostra as quatorze capacidades. É a mesma escada que
   o cartão exibe na vitrine, agora presente durante o checkout inteiro — o
   momento em que a pergunta "vale o que custa?" volta, e em que a vitrine já
   ficou para trás.

   Tudo fechado por padrão. Quem não quer detalhe vê três números e um total;
   quem quer auditar, abre. Aberto de origem, a coluna passaria da altura da
   tela e o total sairia de vista — que é o oposto do que um resumo existe
   para fazer. */
.resumo-linhas {
  margin: 0;
  padding: 0;
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: var(--e3);
}

.resumo-linha {
  position: relative;
  border-radius: 12px;
  /* Contorno por `box-shadow: inset`, e não por `border`: com borda, o 1px que
     aparece no hover encolhe a caixa de conteúdo e o texto inteiro dá um pulo
     lateral. O inset pinta por dentro sem tocar na caixa. */
  box-shadow: inset 0 0 0 1px var(--line);
  transition: box-shadow 160ms ease;
}
.resumo-linha:hover { box-shadow: inset 0 0 0 1px var(--cyan); }
.resumo-linha-topo { position: relative; }
.resumo-linha-titulo { margin: 0; font: inherit; }

/* O `<h3>` acima existe para o leitor de tela: é por cabeçalho que se percorre
   um resumo. Sem ele, a única forma de atravessar a coluna é tabular por tudo. */
.resumo-cabecalho {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  padding: 11px 12px;
  background: none;
  border: 0;
  border-radius: 12px;
  font-family: inherit;
  text-align: left;
  cursor: pointer;
}
/* Linha sem detalhe não expande — e não finge que expande: sem chevron, sem
   cursor de clique. */
.resumo-cabecalho[disabled] { cursor: default; }
/* `:focus-visible`, não `:focus`: o anel aparece para quem navega por teclado
   e some para quem clicou com o mouse. Com `:focus`, todo clique deixa um anel
   aceso — ruído puro. */
.resumo-cabecalho:focus-visible { outline: 2px solid var(--petrol); outline-offset: 2px; }

/* 34px contra os 42 da referência: o orçamento horizontal desta coluna é de
   240px úteis, não os 322 de lá, e cada pixel do círculo sai do nome do plano. */
.resumo-circulo {
  flex: none;
  display: grid;
  place-items: center;
  width: 34px;
  height: 34px;
  border-radius: 50%;
  background: var(--cyan-soft);
  color: var(--petrol);
}
.resumo-circulo svg { width: 18px; height: 18px; }
/* Sólido só no total. Ver o comentário no HTML. */
.resumo-circulo--cheio { background: var(--petrol); color: #fff; }

.resumo-textos { flex: 1 1 auto; min-width: 0; }
.resumo-rotulo { margin: 0; display: block; font-size: 14px; font-weight: 700; line-height: 1.25; color: var(--navy); }
.resumo-valor  { margin: 0; display: block; font-size: 16px; font-weight: 700; line-height: 1.3; color: var(--navy); white-space: nowrap; }
.resumo-desc   { display: block; font-size: 12px; line-height: 1.35; color: var(--gray); }

.resumo-chevron {
  flex: none;
  width: 16px;
  height: 16px;
  color: var(--petrol);
  /* 200ms contra os 280ms do painel: a micro-animação do ícone termina ANTES
     da macro do layout. Ao contrário, o ícone parece perseguir a caixa. */
  transition: transform 200ms cubic-bezier(.4, 0, .2, 1);
}
.resumo-cabecalho[aria-expanded="true"] .resumo-chevron { transform: rotate(180deg); }

/* O painel anima a ALTURA REAL. `max-height` com um teto chutado gasta a maior
   parte da duração percorrendo altura que não existe: um painel de 200px com
   `max-height: 2000px` em 500ms atinge o tamanho real aos 50ms e passa os
   outros 450 crescendo no vazio — e, ao fechar, fica 450ms imóvel antes de
   começar a descer. Com `grid-template-rows: 0fr → 1fr` o navegador interpola
   a altura de conteúdo, e os 280ms escritos aqui são os 280ms que se veem. */
.resumo-painel {
  display: grid;
  grid-template-rows: 0fr;
  opacity: 0;
  transition: grid-template-rows 280ms cubic-bezier(.4, 0, .2, 1), opacity 140ms linear;
}
/* `min-height: 0` é obrigatório: sem ele o filho do grid recusa encolher
   abaixo do conteúdo e a animação não sai do lugar. */
.resumo-painel > div { overflow: hidden; min-height: 0; }
.resumo-linha[data-aberto="true"] .resumo-painel {
  grid-template-rows: 1fr;
  opacity: 1;
  /* O texto entra DEPOIS da caixa ter começado a abrir, e sai na frente dela.
     Entrar junto dá a impressão de o conteúdo empurrar a caixa. */
  transition: grid-template-rows 280ms cubic-bezier(.4, 0, .2, 1), opacity 180ms linear 100ms;
}
/* Chrome < 107 e Safari < 16 não interpolam `0fr`. O fallback é corte seco —
   nunca a volta ao `max-height` chutado, que troca "sem animação" por
   "animação que mente". */
@supports not (grid-template-rows: 0fr) {
  .resumo-painel { display: none; opacity: 1; }
  .resumo-linha[data-aberto="true"] .resumo-painel { display: block; }
}

/* Recuo de 30px, e não os 56 que alinhariam o detalhe exatamente sob o texto
   do cabeçalho: o alinhamento perfeito é mais bonito e custaria uma palavra
   por linha na lista inteira. 30px já comunica subordinação. */
.resumo-detalhe {
  margin: 0;
  padding: 4px 12px 14px 30px;
  list-style: none;
}
.resumo-detalhe li {
  position: relative;
  padding-left: 15px;
  font-size: 13px;
  line-height: 1.5;
  color: var(--gray);
}
.resumo-detalhe li + li { margin-top: 6px; }
.resumo-detalhe li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 7px;
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: var(--cyan);
}

/* Quem navega por teclado ou por leitor de tela precisa saber o estado sem ver
   o chevron; quem usa sintetizador precisa do valor por extenso, porque
   "R$ 189,90" é lido como "erre cifrão". */
.resumo-sr {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
}

/* ------------------------------------------- remover o VIP (15/09/2026) ---
   A remoção mora no resumo porque, com o avanço automático para a Cobrança, o
   visitante não volta mais ao cartão do VIP — e era lá que o botão vivia. O
   resumo é a única superfície presente o tempo todo.

   Discreta em repouso e vermelha no hover: a cor de destruição aparece quando
   a ação está prestes a acontecer, não antes. O alvo de toque tem 24px mesmo
   com o ícone em 14 — `padding` em vez de tamanho maior mantém o peso visual
   pequeno sem deixar a área de clique abaixo do mínimo utilizável. */
/* A lixeira é IRMÃ do cabeçalho, não filha: botão dentro de botão é marcação
   inválida, e o navegador resolve isso engolindo o clique de um dos dois. Ela
   fica sobre a linha, à esquerda do chevron, e o cabeçalho reserva o espaço
   dela no `padding-right` para o valor não passar por baixo. */
.resumo-linha--removivel .resumo-cabecalho { padding-right: 38px; }
.resumo-remover {
  position: absolute;
  /* Centrada no cabeçalho, que é o que o `.resumo-linha-topo` mede — e não no
     `<li>`, que cresce com o painel aberto. */
  top: 50%;
  right: 8px;
  transform: translateY(-50%);
  display: inline-grid;
  place-items: center;
  width: 24px;
  height: 24px;
  padding: 0;
  border: 0;
  border-radius: 6px;
  background: none;
  color: var(--gray-light);
  cursor: pointer;
  transition: color 120ms ease, background-color 120ms ease;
}
.resumo-remover svg { width: 14px; height: 14px; }
.resumo-remover:hover,
.resumo-remover:focus-visible { color: #c0392b; background: rgba(192, 57, 43, 0.08); }

.resumo-parceiros {
  margin-top: var(--e4);
  padding-top: var(--e4);
  border-top: 1px dashed var(--line);
  font-size: 13px;
  color: var(--gray);
}

/* Mesma razão do cabeçalho: em 220px o valor não cabe ao lado do rótulo.
   Círculo e rótulo abrem a linha, o número ocupa a linha inteira abaixo — e
   ganha com isso, porque é o elemento que a coluna existe para mostrar. */
.resumo-total {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px;
  margin-top: var(--e4);
  padding: var(--e4) 12px 0;
  border-top: 2px solid var(--navy);
}
.resumo-total-rotulo { flex: 1 1 auto; font-size: 15px; font-weight: 700; color: var(--navy); }
.resumo-total-bloco  {
  flex-basis: 100%;
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0 6px;
  min-width: 0;
  margin-top: -2px;
}
.resumo-total-valor  { font-size: 24px; font-weight: 700; color: var(--navy); white-space: nowrap; line-height: 1.15; }
.resumo-total-periodo { font-size: 13px; font-weight: 600; color: var(--gray); }
.resumo-total-periodo:empty { display: none; }

/* rodapé ------------------------------------------------------------------ */

.rodape {
  padding: var(--e6) 0;
  background: var(--navy);
  color: #b9c6d1;
}
.rodape p { font-size: 13px; max-width: 720px; }

/* ------------------------------------------------------------ responsivo - */

@media (max-width: 1000px) {
  .planos { grid-template-columns: 1fr; max-width: 560px; margin: 0 auto; }
  .plano-cabeca { min-height: 0; }

  .checkout-colunas { grid-template-columns: 1fr; }
  .checkout-principal { padding: var(--e5); }

  .trilha-lista { gap: var(--e1); }
  /* No mobile a trilha vira só a régua numerada: os nomes das cinco etapas
     não cabem sem truncar em algo ilegível. A etapa ativa continua nomeada
     no topo do painel ("Etapa 2 de 5" + título). */
  .trilha-nome { display: none; }
  .trilha-item { justify-content: center; }

  /* ------------------------------------------- bottom sheet (15/09/2026) ---
     O resumo desliza de baixo por `transform: translateY()`, não por altura:
     `transform` é composto na GPU e mantém 60fps até em aparelho fraco,
     enquanto animar `height` ou `bottom` obriga o navegador a refazer layout a
     cada quadro. Fechado, o sheet fica inteiro FORA da tela; quem o chama é a
     barra fixa, que vive fora dele. */
  .resumo {
    position: fixed;
    inset: auto 0 0 0;
    top: auto;
    /* Acima do banner de consentimento (z-index 100). O sheet é modal —
       tem backdrop e trava a rolagem —, o banner não; enquanto ele estiver
       aberto, nada pode ficar por cima. Fechado, o banner reaparece.
       Barra > sheet > backdrop > banner. */
    z-index: 111;
    display: flex;
    flex-direction: column;
    border-radius: var(--raio-lg) var(--raio-lg) 0 0;
    border: 1px solid var(--line);
    border-bottom: none;
    box-shadow: 0 -8px 30px rgba(8, 30, 45, 0.16);
    max-height: 85dvh;
    transform: translateY(101%);
    transition: transform 280ms cubic-bezier(.4, 0, .2, 1);
  }
  .resumo[data-aberto="sim"] { transform: translateY(0); }
  /* Fechado, o sheet não pode receber foco por Tab: ele está fora da tela, e
     um foco invisível é pior que um foco ausente. */
  .resumo:not([data-aberto="sim"]) { visibility: hidden; }
  .resumo[data-aberto="sim"] { visibility: visible; }

  .resumo-alca {
    display: block;
    width: 100%;
    padding: 10px 0 2px;
    background: none;
    border: 0;
    cursor: pointer;
  }
  .resumo-alca span {
    display: block;
    width: 44px;
    height: 4px;
    margin: 0 auto;
    border-radius: 99px;
    background: var(--line);
  }
  .resumo-alca:focus-visible { outline: 2px solid var(--petrol); outline-offset: -4px; }

  .resumo-backdrop {
    display: block;
    position: fixed;
    inset: 0;
    z-index: 110;
    background: rgba(8, 30, 45, 0.42);
    opacity: 0;
    pointer-events: none;
    transition: opacity 280ms linear;
  }
  .resumo-backdrop[data-visivel="sim"] { opacity: 1; pointer-events: auto; }

  /* A barra é o gatilho e fica sempre visível, por cima do backdrop: com o
     sheet aberto ela é o segundo caminho de fechamento, ao lado da alça, do
     backdrop e do Esc. */
  .resumo-barra {
    /* `display` explícito: a regra do desktop esconde a barra, e media
       query não soma especificidade — sem redeclarar aqui, o `none`
       de cima continua valendo e o mobile fica sem gatilho nenhum. */
    display: block;
    position: fixed;
    inset: auto 0 0 0;
    z-index: 112;
    background: var(--card);
    border-top: 1px solid var(--line);
    /* Sem isto, a barra do sistema do iPhone come o gatilho. */
    padding-bottom: env(safe-area-inset-bottom);
  }
  .resumo-alternar {
    display: flex;
    align-items: center;
    gap: var(--e3);
    width: 100%;
    min-height: 56px;
    padding: var(--e4) var(--e5);
    background: none;
    border: none;
    font-family: inherit;
    cursor: pointer;
    text-align: left;
  }
  .resumo-alternar:focus-visible { outline: 2px solid var(--petrol); outline-offset: -2px; }
  .resumo-alternar-rotulo { font-size: 15px; font-weight: 700; color: var(--navy); }
  .resumo-alternar-total  { margin-left: auto; font-size: 17px; font-weight: 700; color: var(--navy); }
  .resumo-alternar-seta {
    flex: none;
    width: 10px; height: 10px;
    border-right: 2px solid var(--gray);
    border-bottom: 2px solid var(--gray);
    transform: rotate(-135deg);
    transition: transform .2s ease;
  }
  .resumo-alternar[aria-expanded="true"] .resumo-alternar-seta { transform: rotate(45deg); }

  /* O corpo rola dentro do sheet; a altura do sheet é que é limitada. Rolagem
     no sheet inteiro faria a alça sair de vista junto com o conteúdo. */
  .resumo-corpo {
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
    padding: 0 var(--e5) var(--e5);
    /* O sheet termina atrás da barra fixa: o último elemento precisa da folga
       dela para não ficar coberto. */
    padding-bottom: calc(var(--e5) + 72px);
  }
  .resumo-titulo { display: none; }

  /* espaço para a barra fixa não cobrir o último botão */
  .tela-checkout { padding-bottom: 120px; }

  /* O banner de consentimento também mora no rodapé, com z-index 100, e cobria
     a barra do resumo inteira — no mobile ela é o ÚNICO caminho até o total e
     até o detalhe dos itens. Quem não respondesse o banner (ele não é modal,
     dá para ignorar e seguir) ficava sem resumo no checkout inteiro. O banner
     sobe a altura da barra; a ordem de leitura fica banner acima, resumo
     abaixo, e nenhum dos dois esconde o outro. */
  .consent-banner { bottom: calc(72px + env(safe-area-inset-bottom)); }
}

/* Quem pede menos movimento recebe o mesmo resultado, sem o percurso. Não é
   preferência estética: para parte das pessoas com desordem vestibular, painel
   deslizante provoca enjoo de verdade. */
@media (prefers-reduced-motion: reduce) {
  .resumo,
  .resumo-painel,
  .resumo-linha[data-aberto="true"] .resumo-painel,
  .resumo-chevron,
  .resumo-backdrop,
  .resumo-alternar-seta { transition-duration: 1ms; }
}

@media (max-width: 560px) {
  .wrap { padding: 0 var(--e4); }
  .tela { padding: var(--e6) 0 var(--e7); }
  .checkout-principal { padding: var(--e5) var(--e4); }
  .plano { padding: var(--e5); }
  .oferta { padding: var(--e5) var(--e4); }
  .etapa-acoes { flex-direction: column-reverse; align-items: stretch; }
  .etapa-acoes [data-avancar] { margin-left: 0; }
  .etapa-acoes .btn { width: 100%; }
  .oferta-acoes .btn { width: 100%; }
  /* Em telas estreitas os 92px do botão espremem o nome do item; o botão cai
     para a linha de baixo e o valor volta a encostar no nome. */
  .revisao-linha { grid-template-columns: minmax(0, 1fr) auto; }
  .revisao-linha .btn-remover { grid-column: 1 / -1; justify-self: start; }
}

/* ------------------------------------------------------- reduced motion -- */

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
    scroll-behavior: auto !important;
  }
}

/* ------------------------------------------- consentimento sobre a dobra --
   O banner do sistema tem 700px fixos e mora no canto inferior esquerdo. Na
   LP antiga isso não encostava em nada; aqui os cartões descem até o pé da
   primeira tela, e em qualquer janela de 768px ou menos o banner cobria o CTA
   do Essencial — só o do Essencial.

   Isso não é só estética: o experimento compara a escolha entre dois planos, e
   um banner que esconde o botão de um deles na primeira impressão vira viés de
   medição, não ruído.

   A coluna dos cartões começa em ~43% da largura da janela em toda a faixa de
   1024px a 1920px (medido: 0,427 / 0,432 / 0,435 / 0,442). Um teto de
   `42vw - 28px` mantém a borda direita do banner à esquerda do cartão, sem
   breakpoint nenhum e sem mexer na LP de produção.

   O número acompanha a proporção da grade: quando a coluna de contexto
   encolheu de 0,85fr para 0,78fr, o teto anterior (46vw) passou a cobrir o CTA
   de novo. Quem guardou a relação foi o teste de QA, não o comentário — se
   mexer na grade, rode `node tools/qa-nova.mjs` antes de concluir. */
@media (min-width: 1024px) {
  .consent-banner { width: min(700px, calc(42vw - 28px)); }
}

/* ------------------------------------------------ oferta de upgrade (VIP) --
   A referência (LATAM+) coloca ilustração à esquerda, benefícios com check no
   meio e preço com botão à direita. A mesma divisão, sem a ilustração: aqui o
   que ocupa o lugar dela é a promessa, porque o produto é abstrato e um
   desenho genérico não diria nada que o texto não diga melhor.

   O preço aparece em três linhas, e a ordem importa: a diferença primeiro
   (é a informação que o fornecedor quer), o antes→depois em seguida (é o que
   dá escala à diferença) e o novo total por último (é o que ele vai pagar).
   Só a primeira linha seria o enquadramento que mais infla resposta. */
.oferta-destaque {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(240px, 0.55fr);
  gap: var(--e6);
  align-items: start;
  position: relative;
}
.oferta-destaque .oferta-corpo h3 {
  font-size: 22px;
  color: var(--navy);
  margin-bottom: var(--e2);
}
.oferta-promessa {
  font-size: 15px;
  color: var(--gray);
  margin-bottom: var(--e4);
}

/* Selo do canto, no lugar do "Novo" da referência. */
.oferta-selo {
  position: absolute;
  top: calc(var(--e5) * -1);
  left: var(--e5);
  background: var(--cyan-ghost);
  color: var(--petrol);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  padding: 5px 12px;
  border-radius: 999px;
}

/* Lista com check — o mesmo tratamento dos cartões de plano, para o fornecedor
   reconhecer que está lendo a mesma gramática visual. */
.oferta-checks {
  display: flex;
  flex-direction: column;
  gap: var(--e3);
  margin: 0;
  padding: 0;
  list-style: none;
}
.oferta-checks li {
  display: flex;
  align-items: flex-start;
  gap: var(--e3);
  font-size: 15px;
  line-height: 1.45;
  color: var(--ink);
}
.oferta-checks svg {
  width: 16px;
  height: 16px;
  flex: none;
  color: var(--cyan);
  margin-top: calc((1.45em - 16px) / 2);
}

/* Coluna de preço e ação. */
.oferta-lado {
  background: var(--tile);
  border-radius: var(--raio);
  padding: var(--e5);
  display: flex;
  flex-direction: column;
  gap: var(--e1);
}
.oferta-delta {
  font-size: 30px;
  font-weight: 700;
  letter-spacing: -0.02em;
  color: var(--navy);
  line-height: 1.1;
}
.oferta-cheio { font-size: 14px; color: var(--gray); }
.oferta-novo-total {
  font-size: 14px;
  font-weight: 600;
  color: var(--navy);
  margin-bottom: var(--e4);
}
.oferta-lado .oferta-acoes .btn { width: 100%; justify-content: center; }
.oferta-destaque[data-selecionado="sim"] .oferta-lado { background: var(--ok-soft); }

@media (max-width: 760px) {
  .oferta-destaque { grid-template-columns: minmax(0, 1fr); gap: var(--e5); }
  .oferta-delta { font-size: 26px; }
}

/* Mensagem que substitui o bloco de cobrança em planos sem ciclo. */
.periodo-indisponivel {
  background: var(--tile);
  border-radius: var(--raio-sm);
  padding: var(--e4);
  font-size: 14px;
  color: var(--gray);
}

/* Rótulo de legenda só para leitor de tela: o título da etapa já diz o que a
   escolha é, e repetir "Forma de cobrança" na tela seria eco. */
.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;
}

/* ---------------------------------------- blocos da oferta de upgrade (VIP) --
   Oito benefícios soltos leem como coleção aleatória; em três blocos —
   oportunidades, gestão, atendimento — o fornecedor consegue dizer o que o
   upgrade muda na operação dele sem ler item por item. */
.oferta-publico {
  background: var(--tile);
  border-radius: var(--raio-sm);
  padding: var(--e4);
  margin-bottom: var(--e5);
}
.oferta-publico-rotulo {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--gray-light);
  margin-bottom: var(--e1);
}
.oferta-publico p:last-child { font-size: 14px; line-height: 1.5; color: var(--gray); }

.oferta-lista-rotulo {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--gray-light);
  margin-bottom: var(--e3);
}
.oferta-grupo {
  font-size: 14px;
  font-weight: 700;
  color: var(--navy);
  margin-top: var(--e4);
  margin-bottom: var(--e2);
}
.oferta-grupo:first-of-type { margin-top: 0; }
.oferta-checks + .oferta-grupo { margin-top: var(--e5); }

/* O "?" vem do comparativo (`.tbl-help`) de propósito: é o mesmo gesto, na
   mesma página, e inventar um segundo affordance para a mesma função faria o
   fornecedor aprender duas vezes. */
.oferta-checks .tbl-help { margin-left: 6px; vertical-align: middle; }

/* Asterisco do prazo de retorno. */
.oferta-nota {
  font-size: 12px;
  line-height: 1.5;
  color: var(--gray-light);
  margin-top: var(--e3);
}

/* ------------------------------------------------- acordeão das etapas -----
   As cinco decisões empilhadas, com o cabeçalho sempre visível e só o corpo da
   atual aberto — o padrão da referência. A versão anterior mostrava uma tela
   por vez: o fornecedor não sabia quantas decisões faltavam nem conseguia
   rever uma já respondida sem refazer o caminho. */
.tela-checkout .etapa {
  margin-bottom: var(--e3);
  padding: 0;
  overflow: hidden;
}
.etapa-cabecalho { margin: 0; }
.etapa-cabecalho-botao {
  width: 100%;
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto auto;
  align-items: center;
  gap: var(--e4);
  padding: var(--e5) var(--e6);
  background: none;
  border: 0;
  font: inherit;
  text-align: left;
  cursor: pointer;
}
.etapa-cabecalho-botao:disabled { cursor: default; }
.etapa-cabecalho-botao:not(:disabled):hover { background: var(--tile); }

.etapa-cabecalho-titulo {
  font-size: 18px;
  font-weight: 700;
  color: var(--navy);
}
/* Etapa ainda não alcançada: cabeçalho presente, mas sem peso — o fornecedor
   vê o que vem sem que pareça disponível. */
[data-aberta="nao"][data-visitada="nao"] .etapa-cabecalho-titulo { color: var(--gray-light); }

.etapa-cabecalho-conta {
  font-size: 13px;
  font-weight: 600;
  color: var(--petrol);
  background: var(--cyan-ghost);
  border-radius: 999px;
  padding: 3px 12px;
  white-space: nowrap;
}
[data-aberta="nao"][data-visitada="nao"] .etapa-cabecalho-conta {
  color: var(--gray-light);
  background: var(--tile);
}

/* Marca de etapa já respondida: o mesmo check das listas, em vez de um segundo
   vocabulário visual só para isto. */
[data-visitada="sim"] .etapa-cabecalho-conta::after {
  content: " ✓";
  font-weight: 700;
}

.etapa-cabecalho-seta {
  width: 10px; height: 10px;
  border-right: 2px solid var(--gray-light);
  border-bottom: 2px solid var(--gray-light);
  transform: rotate(45deg);
  transition: transform 0.2s ease;
}
[data-aberta="sim"] .etapa-cabecalho-seta { transform: rotate(-135deg); }
.etapa-cabecalho-botao:disabled .etapa-cabecalho-seta { opacity: 0.35; }

.etapa-corpo { padding: 0 var(--e6) var(--e6); }
/* O contador interno vira redundante: o cabeçalho já diz "1 de 5". */
.etapa-corpo .etapa-conta { display: none; }
[data-aberta="sim"] { box-shadow: var(--sombra-lg); }

@media (max-width: 640px) {
  .etapa-cabecalho-botao { padding: var(--e4) var(--e5); gap: var(--e3); }
  .etapa-cabecalho-titulo { font-size: 16px; }
  .etapa-corpo { padding: 0 var(--e5) var(--e5); }
}

/* O contêiner das etapas deixa de ser um cartão.

   Com o acordeão, cada etapa virou seu próprio cartão branco — e o contêiner
   continuava sendo outro, o que punha cartão dentro de cartão e apagava a
   separação entre as cinco decisões. Na referência o fundo da página aparece
   entre os cartões; é o que dá a leitura de blocos independentes. */
.checkout-principal {
  background: none;
  border: 0;
  border-radius: 0;
  padding: 0;
}
.tela-checkout .etapa {
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--raio-lg);
}

/* A trilha horizontal repetia, em outro formato, exatamente o que os
   cabeçalhos do acordeão já dizem — nome da etapa, posição e o que já foi
   respondido. Dois mapas da mesma jornada competem em vez de somar. */
.trilha { display: none; }

/* ------------------------------------------------- formulário de dados -----
   O único ponto do fluxo que pede dado pessoal. Nada aqui é enviado: o submit
   é interceptado e os campos morrem no navegador. */
.dados { margin-bottom: var(--e7); }
.dados-grade {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--e4);
  margin-bottom: var(--e5);
}
.campo { display: flex; flex-direction: column; gap: 6px; }
.campo-rotulo {
  font-size: 13px;
  font-weight: 600;
  color: var(--navy);
}
.campo input {
  font: inherit;
  font-size: 15px;
  color: var(--ink);
  padding: 11px var(--e4);
  border: 1px solid var(--line);
  border-radius: var(--raio-sm);
  background: var(--card);
}
.campo input::placeholder { color: var(--gray-light); }
.campo input:focus-visible { border-color: var(--cyan); }

/* Erro por campo, com o mesmo vermelho e o mesmo corpo da LP de produção
   (`.field.error` / `.field-error` em css/styles.css).

   `min-height: 1em` mesmo vazio: sem isso a linha de erro nasce ao aparecer a
   mensagem e empurra tudo abaixo dela: a pessoa lê um salto de layout no exato
   momento em que precisa ler o que errou. */
.campo-invalido input { border-color: #d64545; }
.campo-invalido input:focus-visible { border-color: #d64545; }
.campo-erro {
  display: block;
  min-height: 1em;
  font-size: 12.5px;
  line-height: 1.3;
  color: #d64545;
}

.dados-perfil {
  border: 0;
  padding: 0;
  margin: 0 0 var(--e5);
  display: flex;
  align-items: center;
  gap: var(--e4);
  flex-wrap: wrap;
}
.dados-perfil legend { float: left; width: 100%; margin-bottom: var(--e2); }
.dados-perfil-opcao {
  display: inline-flex;
  align-items: center;
  gap: var(--e2);
  font-size: 15px;
  color: var(--ink);
  cursor: pointer;
}
.dados-perfil-opcao input { width: 18px; height: 18px; accent-color: var(--navy); }

/* O consentimento fica ANTES do botão. Depois dele, vira letra miúda que
   ninguém lê antes de decidir — e consentimento que se lê depois de consentir
   não é informado. */
/* Sem caixa e sem realce (owner, 14/09/2026): o consentimento é texto de apoio
   e fica logo antes do botão, onde já será lido. A faixa cinza o transformava
   no bloco mais pesado da etapa — mais destaque que os próprios campos —, o
   que é o oposto do que a hierarquia pede aqui.

   O link continua sublinhado e na cor de ação: quem procura o Aviso de
   Privacidade tem de achá-lo, e é o único elemento clicável da frase. */
.dados-consentimento {
  font-size: 13px;
  line-height: 1.55;
  color: var(--gray-light);
  margin-bottom: var(--e4);
  max-width: 78ch;
}
.dados-consentimento a { color: var(--petrol); text-decoration: underline; text-underline-offset: 3px; }

.dados-estado { font-size: 14px; color: var(--ok); margin-top: var(--e3); font-weight: 600; }
/* A mesma linha serve para confirmar o envio e para pedir correção, e a cor
   tem de acompanhar: "Confira os campos destacados." em verde de sucesso é
   contradição entre o que o texto diz e o que a cor diz. */
.dados-estado[data-tom="erro"] { color: #d64545; }

.dados-titulo-parceiros {
  font-size: 17px;
  color: var(--navy);
  padding-top: var(--e5);
  border-top: 1px solid var(--line);
  margin-bottom: var(--e2);
}

@media (max-width: 640px) {
  .dados-grade { grid-template-columns: minmax(0, 1fr); }
}

/* ================================================= cartão do VIP (upgrade) ==
   Duas zonas, não três: ilustração à esquerda, todo o resto à direita — com o
   preço e o botão na base, como a referência faz.

   A primeira tentativa pôs preço e botão numa terceira coluna. Em 716px de
   largura útil sobravam 288px para o texto, divididos em duas colunas de 145px:
   cada rótulo quebrava em três linhas e o cartão ficou mais alto do que a
   versão empilhada que ele veio substituir. Tirar a terceira coluna devolve
   576px ao texto e faz as duas colunas caberem de verdade. */
.oferta-vip {
  display: grid;
  grid-template-columns: 148px minmax(0, 1fr);
  gap: var(--e5);
  /* `start` em vez de `stretch`: a ilustração é quadrada, e esticar a caixa
     dela até a altura do texto deixava a figura pequena no meio de um bloco
     azul vazio de 400px. */
  align-items: start;
  padding: var(--e5);
}
.oferta-vip-arte {
  background: var(--cyan-ghost);
  border-radius: var(--raio);
  display: flex;
  align-items: center;
  justify-content: center;
  aspect-ratio: 1;
  padding: var(--e2);
}
.oferta-vip-arte img { width: 100%; height: auto; }

.oferta-vip-topo {
  display: flex;
  align-items: center;
  gap: var(--e3);
  margin-bottom: var(--e2);
  flex-wrap: wrap;
}
.oferta-vip-topo h2 { font-size: 21px; color: var(--navy); line-height: 1.2; }

/* Pílula "Novo", como a referência usa: sinaliza novidade sem explicar. */
.etiqueta-novo { background: #efe3ff; color: #6b3fa0; }

.oferta-vip .oferta-promessa {
  font-size: 14.5px;
  line-height: 1.5;
  color: var(--gray);
  margin-bottom: var(--e4);
  max-width: 62ch;
}

/* Duas colunas de benefícios: oito itens empilhados viram um paredão que o
   olho pula; lado a lado, cabem numa fixação só. O rótulo aqui serve para
   reconhecer — o detalhe mora no "?". */
.oferta-vip-checks {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 10px var(--e5);
  margin: 0 0 var(--e5);
  padding: 0;
  list-style: none;
}
.oferta-vip-checks li {
  display: flex;
  align-items: flex-start;
  gap: var(--e2);
  font-size: 14px;
  line-height: 1.4;
  color: var(--ink);
}
.oferta-vip-checks svg {
  width: 14px;
  height: 14px;
  flex: none;
  color: var(--cyan);
  margin-top: calc((1.4em - 14px) / 2);
}
.oferta-vip-checks .tbl-help { margin-left: 5px; vertical-align: middle; transform: scale(0.85); }

/* Preço e ação na base, lado a lado — o preço à esquerda porque é o que o
   fornecedor busca antes de decidir, e o botão à direita, onde a mão vai. */
.oferta-vip-acao {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--e5);
  flex-wrap: wrap;
  padding-top: var(--e4);
  border-top: 1px solid var(--line);
}

.oferta-vip-acao .oferta-delta {
  font-size: 26px;
  font-weight: 700;
  letter-spacing: -0.02em;
  color: var(--navy);
  line-height: 1.1;
  /* "+ R$ 60,00 /mês" é um preço, não uma frase: quebrado em duas linhas ele
     passa a ler como dois números. É esta trava que permite ao resumo ao lado
     ficar mais largo sem que o cartão perca o preço. */
  white-space: nowrap;
}
.oferta-delta-unidade { font-size: 13px; color: var(--gray-light); }
.oferta-vip-acao .oferta-estado { flex-basis: 100%; }
.oferta-vip[data-selecionado="sim"] .oferta-vip-arte { background: var(--ok-soft); }

@media (max-width: 560px) {
  .oferta-vip { grid-template-columns: minmax(0, 1fr); }
  .oferta-vip-arte { max-height: 140px; }
  .oferta-vip-checks { grid-template-columns: minmax(0, 1fr); }
  .oferta-vip-acao { flex-direction: column; align-items: stretch; }
  .oferta-vip-acao .btn { width: 100%; justify-content: center; }
}

/* ======================================================= alinhamento geral ==
   O cabeçalho encostava em 120px e todo o resto da página em 144px.

   `.container` do sistema tem `padding: 0 24px`; o `.site-header` tem
   `max-width` igual mas nenhum padding lateral. A logo e a navegação ficavam
   24px fora do alinhamento de tudo que vem abaixo — visível assim que os
   cartões do checkout ganharam borda própria e passaram a desenhar a coluna.

   A correção vive aqui, não em styles.css: a LP de produção tem o mesmo
   desvio, e consertá-la não é escopo desta candidata. */
.site-header { padding-left: 24px; padding-right: 24px; }

/* ================================== cartão do VIP sem cartão dentro de cartão
   A oferta tinha fundo, borda e raio próprios DENTRO do cartão da etapa, que
   por sua vez já é um cartão. Três fundos empilhados para uma coisa só, e
   64px de largura gastos em padding duplicado.

   Agora a oferta é conteúdo do cartão da etapa: sem fundo, sem borda, sem
   padding lateral. O que separa as zonas é o espaço, não mais uma moldura. */
.oferta-vip {
  background: none;
  border: 0;
  border-radius: 0;
  box-shadow: none;
  padding: 0;
  grid-template-columns: 176px minmax(0, 1fr);
  gap: var(--e6);
}

/* A ilustração perde a caixa azul e cresce: ela é o elemento que dá o tom da
   oferta, e estava reduzida dentro de um quadro que competia com ela. */
.oferta-vip-arte {
  background: none;
  border-radius: 0;
  padding: 0;
  aspect-ratio: auto;
  align-self: center;
}
.oferta-vip-arte img { width: 100%; max-width: 176px; }
.oferta-vip[data-selecionado="sim"] .oferta-vip-arte { background: none; }

/* Tipografia menor. O corpo estava em 14–15px e os títulos em 21–26px, medidas
   de página; dentro de um cartão de oferta isso empurra tudo para baixo e faz
   o bloco parecer maior do que o assunto dele. */
.oferta-vip-topo h2 { font-size: 19px; }
.oferta-vip .oferta-promessa { font-size: 14px; margin-bottom: var(--e5); }
.oferta-vip-checks li { font-size: 13.5px; }
.oferta-vip-acao .oferta-delta { font-size: 23px; }
.oferta-delta-unidade { font-size: 12.5px; }

/* Cantos menos arredondados. 32px num cartão de 780px de largura arredonda
   demais para o peso do conteúdo — o cartão passa a ler como pílula. */
.tela-checkout .etapa,
.resumo { border-radius: 18px; }

/* A ilustração ocupa a coluna inteira em vez de flutuar no meio dela.

   Centralizada e com 176px, ela deixava vazio acima e abaixo enquanto o texto
   ao lado ocupava 370px de altura — o olho lia dois blocos desconectados. Sem
   a caixa de fundo, o que dá unidade à composição é a figura acompanhar a
   altura do conteúdo. */
.oferta-vip { grid-template-columns: 200px minmax(0, 1fr); align-items: center; }
.oferta-vip-arte img { max-width: 200px; }

/* =========================== o cartão da etapa é o ÚNICO cartão =============
   Sobravam duas linhas dividindo o conteúdo por dentro: uma acima do preço e
   outra acima do botão de recusa. Cada uma cortava o cartão em mais uma faixa,
   e o resultado lia como três blocos empilhados em vez de uma oferta só.

   O que separa as zonas agora é espaço. A sombra do cartão é a única moldura
   da tela — é ela que diz onde a oferta começa e termina. */
.tela-checkout .etapa .oferta-vip-acao,
.tela-checkout [data-etapa="1"] .etapa-acoes {
  border-top: 0;
  padding-top: 0;
}
.tela-checkout .etapa .oferta-vip-acao { margin-top: var(--e6); }
.tela-checkout [data-etapa="1"] .etapa-acoes { margin-top: var(--e7); }

/* A ilustração cresce para o tamanho do esboço: ela é o que dá presença à
   oferta, e em 200px competia de igual para igual com um parágrafo. As margens
   negativas deixam a figura ultrapassar a caixa de texto sem esticar o cartão
   — é assim que ela ganha escala sem empurrar o conteúdo para baixo. */
.oferta-vip { grid-template-columns: 296px minmax(0, 1fr); gap: var(--e5); }
.oferta-vip-arte {
  margin: calc(var(--e6) * -1) 0;
  align-self: center;
}
.oferta-vip-arte img { max-width: 100%; }

@media (max-width: 900px) {
  .oferta-vip { grid-template-columns: 200px minmax(0, 1fr); }
  .oferta-vip-arte { margin: 0; }
}

/* Reequilíbrio das três larguras.

   A ilustração em 296px levou a coluna de texto a 396px, e duas colunas de
   186px fazem quase toda feature quebrar em duas linhas — o cartão voltou a
   crescer em altura pelo mesmo motivo que ele tinha acabado de encolher.

   O espaço sai do resumo, que tem folga: 340px para quatro linhas de texto e
   um total. Com 300px ele continua confortável e o cartão ganha 40px. */
.checkout-colunas { grid-template-columns: minmax(0, 1fr) 300px; }
.oferta-vip { grid-template-columns: 244px minmax(0, 1fr); }

/* ==================================== ilustração sangrando pelo cartão ======
   No esboço a figura é maior que o espaço que ocupa: ela fica ATRÁS do
   cartão e é o cartão que a corta, não o contrário. É o que dá escala à
   oferta sem transformar a ilustração num ícone.

   Três coisas fazem isso funcionar juntas:
   1. a coluna da arte tem largura fixa e a imagem é maior que ela;
   2. margens negativas empurram a figura para fora da caixa, inclusive por
      baixo do padding do cartão;
   3. `overflow: hidden` no cartão da etapa faz o corte.

   Sem a terceira, a figura vazaria por cima dos cartões de baixo. */
.checkout-colunas { grid-template-columns: minmax(0, 1fr) 260px; }

.oferta-vip { grid-template-columns: 252px minmax(0, 1fr); align-items: end; }
.oferta-vip-arte {
  margin: 0;
  align-self: end;
  overflow: visible;
  min-height: 0;
}
.oferta-vip-arte img {
  width: 372px;
  max-width: none;
  display: block;
  /* A figura desce além do padding inferior do cartão e é cortada por ele;
     à esquerda ela avança sobre o padding lateral pelo mesmo motivo. */
  margin: calc(var(--e7) * -1) 0 calc(var(--e6) * -1) calc(var(--e6) * -1);
}

@media (max-width: 900px) {
  .oferta-vip { grid-template-columns: 180px minmax(0, 1fr); align-items: center; }
  .oferta-vip-arte img { width: 100%; max-width: 200px; margin: 0; }
}

/* A figura ancorada ao fundo, não ao fluxo.

   Com margem negativa ela sangrava pela esquerda mas parava 98px antes da
   borda inferior: a altura dela dependia da altura do texto ao lado, que muda
   conforme o plano de origem e a quebra das features. Ancorada ao fundo do
   bloco, o sangramento é o mesmo em qualquer caso.

   Ela passa por baixo da faixa do botão de recusa, que fica à direita — é
   justamente o efeito de "imagem atrás, cartão cortando". */
.oferta-vip { position: relative; }
.oferta-vip-arte { min-height: 300px; }
.oferta-vip-arte img {
  position: absolute;
  left: calc(var(--e6) * -1);
  bottom: -116px;
  width: 400px;
  margin: 0;
}
@media (max-width: 900px) {
  .oferta-vip-arte { min-height: 0; }
  .oferta-vip-arte img { position: static; width: 100%; max-width: 200px; }
}

/* Valores finais do sangramento, medidos e não estimados: a figura precisa
   ultrapassar a borda inferior do cartão (700px) e a lateral esquerda (144px)
   para que o corte apareça. Com -116px ela parava 14px antes do fundo, e com
   -32px à esquerda encostava na borda sem passar. */
.oferta-vip-arte img { left: -76px; bottom: -196px; width: 440px; }

/* A figura grande e o texto confortável disputam a mesma largura.

   Com 440px e a coluna da arte em 252px, a ilustração avançava 88px sobre a
   coluna de texto e passava por cima do preço. A conta que governa isso é
   simples: a imagem só não invade se a largura dela couber na coluna da arte
   mais o quanto ela sangra à esquerda.

   O espaço vinha do resumo: ele guardava quatro linhas e um total, e 220px
   bastavam. Deixou de bastar em 15/09/2026, quando o resumo passou a abrir o
   conteúdo de cada item — com 170px úteis, o nome do plano descia uma palavra
   por linha e a lista de capacidades virava uma coluna de fragmentos.

   288px é o maior valor que NÃO custa nada ao cartão do VIP, medido: a arte
   continua sem alcançar o preço, as capacidades continuam em duas colunas e o
   "+ R$ 60,00 /mês" continua numa linha só — este último graças ao
   `white-space: nowrap` que passou a travá-lo, e que é o que tornou a troca
   possível. Acima de 300px o cartão começa a espremer de volta. */
.checkout-colunas { grid-template-columns: minmax(0, 1fr) 288px; }
.oferta-vip { grid-template-columns: 316px minmax(0, 1fr); }
.oferta-vip-arte img { left: -44px; width: 384px; bottom: -180px; }

/* ================================ o botão de avançar saiu de dentro do cartão
   No esboço do owner o CONTINUAR fica solto abaixo do cartão, não dentro dele.
   Para isso o `<article class="etapa">` deixou de ser o cartão e virou o
   agrupador: dentro dele o `.etapa-cartao` (fundo, borda, sombra) e, fora,
   o `.etapa-acoes`.

   O efeito colateral é a altura: o cartão encolheu a faixa inteira do botão, e
   com ela o vazio que sobrava acima da ilustração. */
.tela-checkout .etapa {
  background: none;
  border: 0;
  border-radius: 0;
  box-shadow: none;
  overflow: visible;
}
.etapa-cartao {
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: 18px;
  overflow: hidden;   /* é ele que corta a ilustração que sangra */
}
[data-aberta="sim"] .etapa-cartao { box-shadow: var(--sombra-lg); }
[data-aberta="sim"] { box-shadow: none; }

/* Fora do cartão, a faixa de ações não tem fundo nem moldura: ela pertence à
   página. E só existe na etapa aberta — sem isto, os cinco botões de avançar
   apareceriam empilhados, um por etapa. */
.tela-checkout .etapa-acoes {
  margin-top: var(--e4);
  margin-bottom: var(--e5);
  padding: 0;
  border: 0;
  background: none;
}
[data-aberta="nao"] .etapa-acoes { display: none; }

/* Com a faixa do botão fora, a ilustração ganha altura para crescer. */
.oferta-vip-arte img { width: 424px; left: -60px; bottom: -150px; }

/* Ajuste final: a imagem em 424px avançava 24px sobre a coluna de texto. Com
   o deslocamento à esquerda maior ela mantém o tamanho sem invadir, e sangra
   mais pelos dois lados — que é onde o corte do cartão aparece. */
.oferta-vip-arte img { left: -96px; bottom: -184px; }

/* A pílula "Novo" ao lado do título, não abaixo dele.

   Com título e promessa fundidos numa frase de três linhas, o flex-wrap jogou
   a pílula para a linha seguinte — ela virou um rótulo solto pairando entre o
   texto e os benefícios, sem pertencer a nenhum dos dois. Alinhada ao topo,
   à direita, ela volta a marcar a novidade do que está escrito ao lado. */
.oferta-vip-topo {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--e4);
  flex-wrap: nowrap;
  margin-bottom: var(--e5);
}
.oferta-vip-topo h2 {
  font-size: 18px;
  line-height: 1.4;
  font-weight: 500;
  color: var(--ink);
  max-width: 52ch;
}
.oferta-vip-topo h2 strong { font-weight: 700; color: var(--navy); }
.oferta-vip-topo .etiqueta-novo { flex: none; margin-top: 2px; }

/* ================== proporções do layout desenhado pelo owner (14/09/2026) ==
   Medidas tiradas do Figma dele: a ilustração ocupa ~46% da largura do cartão
   (contra 35% aqui) e vai de ponta a ponta na vertical, cortada pelo fundo. O
   texto encolhe e aceita quebrar em duas linhas — a troca que ele fez é dar a
   presença para a figura e tratar os rótulos como etiqueta, não como leitura.

   É a mesma disputa de largura de antes, resolvida para o outro lado: antes eu
   protegia a linha única das features, agora a figura ganha. */
.oferta-vip { grid-template-columns: 414px minmax(0, 1fr); gap: var(--e4); }

.oferta-vip-arte { min-height: 380px; }
.oferta-vip-arte img { width: 560px; left: -150px; bottom: -128px; }

/* Texto menor: com a coluna em ~440px, duas colunas de benefícios pedem corpo
   menor para o rótulo não virar três linhas. */
.oferta-vip-topo h2 { font-size: 16px; line-height: 1.45; }
.oferta-vip-checks { gap: 8px var(--e4); }
.oferta-vip-checks li { font-size: 12.5px; line-height: 1.35; }
.oferta-vip-checks svg { width: 12px; height: 12px; margin-top: calc((1.35em - 12px) / 2); }
.oferta-vip-acao .oferta-delta { font-size: 21px; }
.oferta-delta-unidade { font-size: 12px; }

@media (max-width: 900px) {
  .oferta-vip { grid-template-columns: 180px minmax(0, 1fr); }
  .oferta-vip-arte { min-height: 0; }
  .oferta-vip-arte img { position: static; width: 100%; max-width: 200px; }
}
/* Sangramento inferior: com a figura em 560px ela parava rente à borda. */
.oferta-vip-arte img { bottom: -196px; }

/* ====================== ilustração recortada e recusa como link ============
   O SVG original é 2000x2000 com a figura no meio: 55% da área era vazio, e
   era esse vazio que aparecia como espaço branco acima e à direita do homem.
   Aumentar a imagem só aumentava o vazio junto — por isso as tentativas
   anteriores não resolviam. Com o viewBox recortado no contorno real
   (1404x1289), cada pixel de largura vira figura. */
.oferta-vip-arte img { width: 520px; left: -104px; bottom: -96px; }

/* "Continuar" como texto sublinhado, não botão.

   Dois botões lado a lado com pesos parecidos fazem o fornecedor escolher
   entre duas ações; um botão e um link deixam claro qual é a oferta e qual é
   seguir em frente. É o que a referência faz — e continua sendo recusa neutra,
   sem esconder nem envergonhar. */
.link-continuar {
  background: none;
  border: 0;
  padding: 6px 2px;
  font: inherit;
  font-size: 15px;
  font-weight: 600;
  color: var(--petrol);
  text-decoration: underline;
  text-underline-offset: 4px;
  cursor: pointer;
}
.link-continuar:hover { color: var(--navy); }
.tela-checkout [data-etapa="1"] .etapa-acoes { justify-content: flex-end; }
/* A figura maior que o cartão: sangra em cima e embaixo, sem vazio no topo. */
.oferta-vip-arte img { width: 620px; left: -190px; bottom: -110px; }

/* ======================== "Continuar" dentro do cartão, sob o botão =========
   Decisão do owner em 14/09/2026: na etapa do upgrade, o link de seguir volta
   para dentro do cartão, centralizado abaixo do "Adicionar VIP".

   As demais etapas mantêm a faixa de ações fora — ali ela separa a decisão do
   avanço, e trazê-la para dentro faria cada cartão terminar com dois botões. */
.tela-checkout [data-etapa="1"] .etapa-corpo > .etapa-acoes {
  justify-content: center;
  margin: var(--e4) 0 0;
  padding: 0;
  border: 0;
}
.tela-checkout [data-etapa="1"] .etapa-corpo > .etapa-acoes [data-avancar] {
  margin-left: 0;
}

/* O bloco de ações da etapa 1 é irmão do corpo, dentro do cartão — não filho
   dele. O seletor anterior (`.etapa-corpo > .etapa-acoes`) não casava, e a
   regra que alinhava à direita continuava valendo. Daqui sai o padding lateral
   também, que vinha do corpo e ele não herda. */
.tela-checkout [data-etapa="1"] .etapa-cartao > .etapa-acoes {
  justify-content: center;
  margin: 0;
  padding: 0 var(--e6) var(--e6);
  border: 0;
}
.tela-checkout [data-etapa="1"] .etapa-cartao > .etapa-acoes [data-avancar] {
  margin-left: 0;
}

/* A ilustração não captura clique.

   Ela sangra por cima da faixa inferior do cartão, e o link "Continuar" ficou
   embaixo dela: o clique caía na imagem e o fornecedor não conseguia avançar.
   A figura é decorativa (`aria-hidden`), então ignorar o ponteiro é o
   comportamento correto — não um remendo para o teste passar. */
.oferta-vip-arte,
.oferta-vip-arte img { pointer-events: none; }

/* ============== o link de seguir na mesma coluna do botão de adicionar =====
   Ele vivia numa faixa própria no pé do cartão: centralizado em relação ao
   CARTÃO, ficava 334px à esquerda do botão — e a faixa ainda somava ~100px de
   altura morta entre a oferta e a borda inferior.

   Na mesma coluna do botão, ele fica exatamente sob ele, e o espaço extra
   desaparece porque não há mais uma faixa para ocupar. */
.oferta-vip-acao {
  align-items: flex-start;
}
.oferta-vip-acao > .btn-primary,
.oferta-vip-acao > .btn-remover {
  order: 2;
}
/* A posição definitiva do link vem mais abaixo, junto das outras atribuições
   de linha e coluna desta faixa — aqui fica só a ordem. */
.oferta-vip-acao .link-continuar {
  order: 3;
  text-align: center;
  margin-top: var(--e3);
}

/* Os dois itens que atravessam as duas colunas param de OPINAR sobre a largura
   delas. Num grid, um item cruzado contribui para o tamanho de toda coluna
   dimensionada por conteúdo: a base de 355px inflava a coluna do preço a 192px
   e espremia o botão, e o link de 206px inflava a do botão e espremia o preço.
   Cada correção empurrava o defeito para o outro lado.

   `width: 0` zera a contribuição intrínseca — para o algoritmo de colunas,
   estes itens passam a não existir. `min-width: 100%` os devolve à largura
   cheia na hora de pintar. As colunas passam a medir só o que de fato disputa
   espaço nelas: o preço e o botão. */
.oferta-vip-acao .vip-colapso-base {
  width: 0;
  min-width: 100%;
}
/* O rótulo do botão é curto por natureza; `nowrap` existe para que continue
   assim se alguém alongá-lo sem medir a faixa. */
.oferta-vip-acao > .btn-primary,
.oferta-vip-acao > .btn-remover { white-space: nowrap; }
/* Com o botão e o link empilhados à direita, a coluna precisa de largura
   própria para o link centralizar sob o botão e não sob o cartão. */
/* As duas colunas desta faixa, e a conta que as governa. Medido em 1440px:
   a faixa tem 336px e o gap come 24, sobram 312 para dividir.

     preço  136px  (nunca quebra: é um preço, não uma frase)
     botão  167px  "Adicionar VIP" em uma linha
     link   206px  "Continuar no plano Gratuito" em uma linha

   Preço + botão = 327. Cabe. Preço + link = 366. NÃO cabe — e era isso que
   partia os dois em duas e três linhas quando o link dividia a coluna com o
   botão. O link ganhou faixa inteira; sozinho na largura, sobra folga.

   A coluna 2 é `max-content`: ela mede o botão e não cede um pixel, porque
   encolher um botão de ação para caber é trocar clareza por geometria. A
   coluna 1 é `minmax(0, 1fr)` — o preço cabe nos 145 que sobram, e é a única
   que pode ceder quando a linha de base (355px) atravessa as duas e precisa
   quebrar. Com `minmax(max-content, 1fr)` aqui, o `max-content` dessa base
   vazava para o piso da coluna 1 e a inflava a 192px, espremendo o botão. */
.oferta-vip-acao {
  display: grid;
  grid-template-columns: minmax(0, 1fr) max-content;
  align-items: center;
  row-gap: var(--e3);
}
/* O preço não quebra e não é cortado: `nowrap` impede a quebra, e a coluna 1
   tem largura para ele. Sem os dois, o "/mês" ia parar por baixo do botão —
   sólido e pintado depois, ele engolia o sufixo em silêncio. */
.oferta-vip-acao .oferta-delta { white-space: nowrap; }
.oferta-vip-acao > div:first-child { grid-column: 1; grid-row: 1; }
/* A base atravessa as duas colunas, na faixa abaixo do preço e do botão: é
   a única forma de ela ter a largura do cartão em vez dos 188px da coluna do
   preço. O link de recusa desce uma faixa. */
.oferta-vip-acao > .vip-colapso-base { grid-column: 1 / -1; grid-row: 2; }
.oferta-vip-acao > .btn { grid-column: 2; grid-row: 1; width: 100%; justify-content: center; }
/* "Só registra seu interesse. Sem pagamento." fica ENTRE o botão e a saída,
   atravessando as duas colunas. Acima do link porque a dúvida que ele desfaz
   — "clicar aqui me cobra?" — é sobre o botão, e uma resposta impressa depois
   da alternativa chega quando a pessoa já escolheu sair. */

/* O link atravessa as DUAS colunas desde 16/09/2026. Na coluna do botão ele
   herdava 138px, e "Continuar no plano Gratuito" (206px numa linha) descia
   para três — um link de saída partido em três lê como texto quebrado, não
   como alternativa. Centralizado sob o botão era mais bonito; custava a
   legibilidade da única saída da tela.

   `width: 0` + `min-width: 100%`: zera a contribuição dele ao cálculo das
   colunas e devolve a largura cheia na pintura. Sem isso, o link de 206px
   inflava a coluna do botão e espremia o preço a 106px — cada correção
   empurrava o defeito para o outro lado da faixa. */
.oferta-vip-acao .link-continuar {
  grid-column: 1 / -1;
  grid-row: 4;
  margin: 0;
  width: 0;
  min-width: 100%;
  text-align: center;
  white-space: nowrap;
}

/* ===================== o cartão do VIP muda de ESTADO, não de assunto =======
   Decisão do owner em 14/09/2026, com a referência medida no DOM da LATAM.

   O que havia antes: aceitar o upgrade pintava o cartão inteiro de verde
   (`.oferta[data-selecionado="sim"]`) e acrescentava uma frase de confirmação
   embaixo. O cartão ficava do mesmo tamanho, com os oito benefícios e o preço
   ainda na tela, agora sobre um fundo colorido. Dois problemas: o verde é um
   sinal forte gasto numa informação fraca, e continuar exibindo o argumento de
   venda DEPOIS da venda convida a reconsiderar.

   O que passa a haver: o cartão encolhe. Título vira confirmação, benefícios e
   preço colapsam, o botão sólido vira link de saída. Nada de fundo colorido —
   o sinal é a mudança de forma, que é mais legível que cor e não precisa ser
   aprendida.

   Princípio de varejo anterior a qualquer framework: depois que o cliente diz
   sim, pare de vender e leve ele ao caixa. */

.oferta-vip {
  --vip-dur: 320ms;
  --vip-ease: cubic-bezier(.4, 0, .2, 1);
}

/* O verde sai. A borda continua neutra nos dois estados: quem confirma é o
   título com o check, não o cartão inteiro. */
.oferta-vip[data-selecionado="sim"] {
  border-color: var(--line);
  background: transparent;
}

/* --------------------------------------------------- troca do título ----
   As duas frases em LINHAS distintas de um grid cuja altura anima — não
   sobrepostas na mesma célula.

   A primeira versão empilhava as duas em `grid-area: 1 / 1` e tirava a inativa
   do fluxo com `position: absolute`. Funcionava para a largura, mas `position`
   não é animável: ela trocava no frame zero e a altura do título saltava seco
   no meio do cruzamento — 46px para 23px na ida, 70px para 93px na volta.

   Com `1fr / 0fr` a altura do título passa a acompanhar a frase ativa de forma
   contínua, na mesma curva do resto do cartão, e nenhuma das duas precisa sair
   do fluxo. `min-height: 0` no filho continua obrigatório: sem ele o item de
   grid recusa encolher abaixo do conteúdo. */
.vip-troca {
  display: grid;
  grid-template-rows: 1fr 0fr;
  transition: grid-template-rows var(--vip-dur) var(--vip-ease);
}
.vip-troca > * { overflow: hidden; min-height: 0; }
.oferta-vip[data-selecionado="sim"] .vip-troca { grid-template-rows: 0fr 1fr; }

/* O cabeçalho também vira grid para que a pílula "Novo" e o check ocupem o
   mesmo ponto — a confirmação chega onde o olho já estava pousado, em vez de
   abrir uma posição nova. A coluna fica com a largura do maior dos dois e não
   reflui na troca. */
.oferta-vip-topo {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: start;
  column-gap: var(--e3);
}
.oferta-vip-topo h2 { grid-area: 1 / 1; }
.oferta-vip-topo .etiqueta-novo,
.oferta-vip-topo .vip-check { grid-area: 1 / 2; justify-self: end; }

.vip-check { width: 24px; height: 24px; color: var(--ok); }

/* Crossfade assimétrico: o que sai some em 120ms, sem espera; o que entra só
   começa a aparecer aos 140ms. Fundir os dois ao mesmo tempo deixa uma janela
   de texto legível por cima de texto legível.

   A ASSIMETRIA PRECISA SER DECLARADA NOS DOIS ESTADOS, e não uma vez só no
   elemento. Essa foi a origem do "piscar" relatado pelo owner em 14/09/2026:
   a versão anterior declarava `120ms` em `.vip-sai` e `200ms 140ms` em
   `.vip-entra`, e uma transição declarada no elemento vale nos DOIS sentidos.
   Na ida os papéis batiam com os nomes e ficava correto; na volta eles se
   invertem — quem estava saindo passa a entrar — e o atraso de 140ms, que
   existe para segurar quem entra, passava a segurar quem devia sair.

   Medido com sonda de rAF antes da correção: 0 frames de sobreposição na ida
   contra 17 na volta, 11 deles com AMBOS os títulos acima de 50% de opacidade.
   Em 172ms os dois estavam simultaneamente legíveis (1.00 e 0.95).

   A regra, agora: quem SAI usa 120ms sem espera; quem ENTRA usa 200ms com
   140ms de espera. Cada estado declara o par que vale ao chegar nele. */
.vip-sai {
  /* No estado de oferta esta frase é a que entra. */
  opacity: 1;
  transition: opacity 200ms linear 140ms;
}
.vip-entra {
  /* ...e esta é a que sai. */
  opacity: 0;
  transform: translateY(4px);
  transition: opacity 120ms linear, transform 120ms ease-out;
}
.oferta-vip[data-selecionado="sim"] .vip-sai {
  /* No estado aceito os papéis se invertem, e os tempos junto com eles. */
  opacity: 0;
  pointer-events: none;
  transition: opacity 120ms linear;
}
.oferta-vip[data-selecionado="sim"] .vip-entra {
  opacity: 1;
  transform: none;
  transition: opacity 200ms linear 140ms, transform 200ms ease-out 140ms;
}

/* ------------------------------------------------- colapso de verdade ----
   `grid-template-rows: 1fr` para `0fr` anima altura automática sem altura fixa
   e sem JavaScript. Foi a técnica escolhida em vez das duas alturas fixas da
   referência porque aqui o conteúdo não tem altura previsível: o número de
   linhas dos benefícios muda com a largura da coluna, e o delta de preço muda
   de dígitos conforme o plano de origem.

   `min-height: 0` no filho não é detalhe: sem ele o item de grid recusa
   encolher abaixo do conteúdo e a animação simplesmente não acontece. */
.vip-colapso {
  display: grid;
  grid-template-rows: 1fr;
  transition: grid-template-rows var(--vip-dur) var(--vip-ease),
              opacity 120ms linear;
}
.vip-colapso > * { overflow: hidden; min-height: 0; }
.oferta-vip[data-selecionado="sim"] .vip-colapso {
  grid-template-rows: 0fr;
  opacity: 0;
}
/* A lista tem margem inferior própria; ela precisa ir embora junto, ou sobram
   24px de nada entre o título e o rodapé. */
.oferta-vip[data-selecionado="sim"] .oferta-vip-checks { margin-bottom: 0; }

/* ------------------------------------------------- a ilustração encolhe --
   As duas colunas descem juntas. Se a arte ficar do tamanho original ao lado
   de um texto de uma linha, o cartão vira um retângulo desequilibrado com uma
   figura grande guardando espaço vazio. */
.oferta-vip {
  transition: grid-template-columns var(--vip-dur) var(--vip-ease);
}
.oferta-vip-arte {
  transition: min-height var(--vip-dur) var(--vip-ease);
}
.oferta-vip-arte img {
  transition: width var(--vip-dur) var(--vip-ease),
              left var(--vip-dur) var(--vip-ease),
              bottom var(--vip-dur) var(--vip-ease);
}
.oferta-vip[data-selecionado="sim"] { grid-template-columns: 236px minmax(0, 1fr); }
.oferta-vip[data-selecionado="sim"] .oferta-vip-arte { min-height: 172px; }
/* Os três valores são geometria, não gosto. No estado aceito a caixa da arte
   passa a ser a altura INTEIRA do cartão (172px), enquanto no estado de oferta
   ela era menor que ele — então reduzir a figura na mesma proporção da coluna
   a faria vazar 93px por cima e decapitar a estrela.

   A conta: a figura tem razão 1404/1289, logo 280px de largura dão 257px de
   altura; com a base 70px abaixo da caixa, o topo cai em 172 − 257 + 70 = −15.
   Quinze pixels de sangramento superior — o mesmo do estado de oferta. */
.oferta-vip[data-selecionado="sim"] .oferta-vip-arte img {
  width: 280px;
  left: -40px;
  bottom: -70px;
}

/* ------------------------------------------- o botão desce de peso -------
   De botão sólido a link. O elemento é o mesmo — só o `data-` e o rótulo
   mudam (ver o JS) —, então a passagem é uma transição de propriedades e não
   uma troca de nós.

   A saída existe para reduzir a ansiedade do compromisso, mas não é
   convidativa: reversível, e não sugerida. */
.vip-botao {
  transition: background-color var(--vip-dur) var(--vip-ease),
              box-shadow var(--vip-dur) var(--vip-ease),
              color var(--vip-dur) var(--vip-ease),
              padding var(--vip-dur) var(--vip-ease),
              border-radius var(--vip-dur) var(--vip-ease),
              transform .15s ease;
}
.oferta-vip[data-selecionado="sim"] .vip-botao {
  background: none;
  box-shadow: none;
  color: #a4343a;
  font-weight: 400;
  font-size: 0.95rem;
  text-decoration: underline;
  text-underline-offset: 4px;
  border-radius: var(--raio-sm);
  /* Visual de link, alvo de botão: o "Eliminar" da referência mede 127x24px e
     reprova na WCAG 2.5.5. Aqui o padding leva a caixa a 44px sem que a
     aparência mude. */
  padding: 10px 8px;
  min-height: 44px;
  /* Migra para a coluna do preço que acabou de sumir — o movimento para a
     esquerda é o que a referência faz. O salto horizontal acontece enquanto o
     rótulo está em opacidade baixa, então o olho não o registra como corte. */
  grid-column: 1;
  grid-row: 2;
  width: auto;
  justify-self: start;
}
.oferta-vip[data-selecionado="sim"] .vip-botao:hover {
  background: none;
  color: #7d262b;
  transform: none;
}

/* --------------------------------------- o "Continuar" não se move -------
   Ele fica na coluna 2, linha 2, nos dois estados: a linha 1 é que colapsa.
   Isso elimina o único deslocamento perigoso do componente — um CTA que salta
   para debaixo do cursor logo após o clique produz avanço acidental.

   A guarda abaixo cobre os 400ms em que a linha 1 ainda está encolhendo e o
   "Continuar" está subindo: durante a transição ele não aceita clique. Dispara
   só na entrada do estado aceito, que é o único sentido em que o botão sobe. */
.oferta-vip[data-selecionado="sim"] .link-continuar {
  animation: vip-guarda-clique 400ms step-end;
}
@keyframes vip-guarda-clique {
  from { pointer-events: none; }
  to   { pointer-events: auto; }
}

/* ---------------------------------------------------------- movimento ----
   Nada a declarar aqui: a regra global de `prefers-reduced-motion` já zera
   `transition-duration` e `animation-duration` em `*` — e como as duas pontas
   deste efeito são estados determinísticos, e não uma animação com meio de
   caminho obrigatório, desligar a transição entrega exatamente o comportamento
   da referência. O fallback é o original. */

@media (max-width: 560px) {
  /* A primeira regra restaura o empilhamento do cartão em telas estreitas.

     Ele estava declarado lá em cima, junto do bloco original, mas as regras de
     reequilíbrio de largura que vieram DEPOIS — a última fixa a coluna da arte
     em 414px — não estão dentro de media query e, por cascata, anulavam
     aquele empilhamento. Como é a cascata que decide, a correção mora aqui.

     ATENÇÃO: isto NÃO conserta o mobile sozinho. Existe um defeito herdado e
     maior, acima na árvore e fora do escopo deste cartão: `.checkout-colunas`
     é um grid de duas colunas cuja faixa do resumo está fixa em 220px e não
     colapsa em nenhum breakpoint. Em 375px de viewport sobram 55px para a
     coluna principal INTEIRA, e as cinco etapas saem em uma letra por linha.
     Medido com todas as regras deste bloco removidas em runtime, para separar
     o herdado do novo: o mesmo resultado nos dois casos.

     Empilhado, a arte não tem coluna para encolher e o botão não tem para onde
     migrar: o colapso do conteúdo já entrega a redução.

     As três regras de geometria da figura precisam ser DESFEITAS aqui. No
     empilhado a imagem volta a ser estática e fluida (`position: static`,
     `width: 100%`), e as medidas absolutas do desktop — 280px de largura,
     deslocamentos negativos — venciam por especificidade e arrastavam a figura
     para fora do cartão. O encolhimento no mobile é de `max-width`, só. */
  .oferta-vip { grid-template-columns: minmax(0, 1fr); }
  .oferta-vip[data-selecionado="sim"] { grid-template-columns: minmax(0, 1fr); }
  .oferta-vip[data-selecionado="sim"] .oferta-vip-arte { min-height: 0; }
  .oferta-vip[data-selecionado="sim"] .oferta-vip-arte img {
    position: static;
    width: 100%;
    max-width: 132px;
    left: auto;
    bottom: auto;
  }
  .oferta-vip[data-selecionado="sim"] .vip-botao {
    grid-column: 1;
    grid-row: 2;
    width: 100%;
    justify-self: stretch;
  }
}

/* ======================= pergunta de atribuição ============================
   Aparece logo depois de escolher um plano ou aceitar o upgrade, e some assim
   que a pessoa responde ou dispensa. Curta de propósito: cada segundo aqui é
   cobrado da conversão, e o que se ganha em troca é a única leitura que diz
   POR QUE a escolha aconteceu.

   `.chip` vem do design system (css/styles.css) — aqui só o que o formato de
   lista exige. */
.atribuicao {
  width: min(560px, calc(100vw - 2 * var(--e5)));
  padding: var(--e6);
  border: 0;
  border-radius: var(--raio);
  box-shadow: var(--sombra-lg);
  color: var(--ink);
  /* Centralização determinística, o mesmo padrão do diálogo de migração. Era
     `inset: 0` + `margin: auto`, que só centraliza enquanto sobra espaço para
     a margem se distribuir: com a lista cheia, fonte grande ou zoom, a conta
     deixa de fechar e o diálogo volta para o canto superior esquerdo. `top` e
     `left` em 50% mais translação de metade da caixa não dependem de sobra. */
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  margin: 0;
  max-height: min(86vh, 720px);
  flex-direction: column;
}
/* O `display` só vale ABERTO. A folha do navegador dá `display: none` a um
   `<dialog>` fechado, e declarar `display: flex` na regra base o sobrescreve:
   o diálogo vira uma camada invisível por cima da página inteira, comendo
   todo clique. É a mesma armadilha do `[hidden]` que já custou duas
   regressões aqui — `display` declarado sempre vence o estado. */
.atribuicao[open] {
  /* Coluna com a LISTA rolando e o rodapé fixo. Rolar o diálogo inteiro
     empurrava "Finalizar seleção" para fora da vista em janelas baixas: a
     pessoa via nove opções e nenhum jeito de concluir. */
  display: flex;
}
.atribuicao::backdrop { background: rgba(8, 30, 45, 0.45); }

/* --------------------------------------------- migração de plano (15/09) ---
   Mesma caixa da pergunta de atribuição, com duas ações empilhadas em vez de
   chips. `display` só no estado aberto, nunca na base: um `<dialog>` com
   `display` fixo ignora o próprio `hidden` e fica visível fechado — armadilha
   que este arquivo já pagou três vezes. */
.migracao {
  display: none;
  width: min(560px, calc(100vw - 2 * var(--e5)));
  padding: var(--e6);
  border: 0;
  border-radius: var(--raio);
  box-shadow: var(--sombra-lg);
  color: var(--ink);
  /* Centralização DETERMINÍSTICA, não por `margin: auto`.

     O diálogo de atribuição usa `inset: 0` + `margin: auto`, que funciona
     enquanto a altura couber na viewport: a margem automática só se resolve
     se sobrar espaço para distribuir. Este aqui cresceu — ganhou a lista do
     que se passa a ter — e com fonte grande, zoom ou janela baixa a conta
     deixaria de fechar, devolvendo o diálogo ao canto superior esquerdo.

     `top/left` em 50% mais translação de metade da própria caixa não depende
     de espaço sobrando, e o `max-height` com rolagem interna resolve o resto. */
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  margin: 0;
  max-height: min(86vh, 720px);
}
/* Coluna com a LISTA rolando e as ações fixas no pé — o mesmo desenho do
   diálogo de atribuição, e pela mesma razão: rolar o diálogo inteiro empurra
   os botões para fora da vista, e a pessoa fica olhando para o que ganha sem
   enxergar como aceitar. */
.migracao[open] { display: flex; flex-direction: column; }
.migracao::backdrop { background: rgba(8, 30, 45, 0.45); }
.migracao-titulo {
  flex: 0 0 auto;
  font-size: 1.15rem;
  font-weight: 700;
  line-height: 1.3;
  margin: 0 0 var(--e3);
}
.migracao-texto {
  flex: 0 0 auto;
  font-size: 0.95rem;
  line-height: 1.5;
  color: var(--gray);
  margin: 0 0 var(--e5);
}
/* Cada recurso numa CAIXA com o próprio preço no cabeçalho: é o que permite a
   pessoa conferir a soma sem confiar no total que damos a ela. Antes as listas
   vinham soltas sob um título, e o "R$ 189,90 + R$ 60,00" do parágrafo não se
   ligava a nada visível. */
.migracao-corpo {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
  margin-bottom: var(--e4);
  /* Desvanece o pé enquanto houver conteúdo abaixo. Em janela baixa a segunda
     caixa é cortada logo depois do cabeçalho, e a borda arredondada dela
     encostando no limite da área rolável lê como caixa VAZIA — o VIP parecia
     não ter capacidade nenhuma. A máscara desfaz a ilusão dizendo, sem texto,
     que a lista continua. */
  mask-image: linear-gradient(to bottom, #000 calc(100% - 28px), transparent);
  -webkit-mask-image: linear-gradient(to bottom, #000 calc(100% - 28px), transparent);
}
/* Rolagem no fim: nada a desvanecer, e manter a máscara apagaria o último item. */
.migracao-corpo:not(:has(+ * )) { mask-image: none; }
.migracao-caixa {
  border: 1px solid var(--line);
  border-radius: 12px;
  padding: var(--e3) var(--e4) var(--e4);
}
.migracao-caixa + .migracao-caixa { margin-top: var(--e3); }
.migracao-caixa-topo {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--e3);
  margin-bottom: var(--e3);
  padding-bottom: var(--e2);
  border-bottom: 1px solid var(--line);
}
.migracao-caixa-nome { font-size: 0.95rem; font-weight: 700; color: var(--ink); }
.migracao-caixa-preco { font-size: 0.95rem; font-weight: 700; color: var(--ink); white-space: nowrap; }
.migracao-ganho-lista {
  list-style: none;
  margin: 0;
  padding: 0;
  columns: 2;
  column-gap: var(--e4);
  font-size: 0.86rem;
  line-height: 1.35;
}
.migracao-ganho-lista li {
  break-inside: avoid;
  padding-left: 18px;
  margin-bottom: 5px;
  position: relative;
  color: var(--ink);
}
/* O check desenhado em CSS, e não um SVG por item: são vinte e dois itens, e
   vinte e duas cópias do mesmo caminho pesariam mais que a lista inteira. */
.migracao-ganho-lista li::before {
  content: "";
  position: absolute;
  left: 2px;
  top: 5px;
  width: 9px;
  height: 5px;
  border-left: 2px solid var(--cyan);
  border-bottom: 2px solid var(--cyan);
  transform: rotate(-45deg);
}

/* A conta fica FORA da rolagem, colada às ações: é ela que fecha a decisão, e
   rolar para achar o total é o mesmo que não mostrá-lo. */
/* O ciclo acompanha o número grande, em corpo menor e peso normal: "+ R$
   60,00 /mês" lê como um preço só, e a linha de baixo fica livre para dizer
   sobre o que ele incide. */
.oferta-delta-ciclo {
  font-size: 0.5em;
  font-weight: 500;
  letter-spacing: 0;
}

/* Canto superior direito, onde a mão procura por ele. 40px de alvo com o
   ícone em 16: `padding` em vez de ícone grande mantém o peso visual baixo sem
   deixar a área de toque abaixo do mínimo utilizável. */
.migracao-fechar,
.atribuicao-fechar {
  position: absolute;
  top: 12px;
  right: 12px;
  display: grid;
  place-items: center;
  width: 40px;
  height: 40px;
  padding: 0;
  border: 0;
  border-radius: 10px;
  background: none;
  color: var(--gray-light);
  cursor: pointer;
  transition: color 120ms ease, background-color 120ms ease;
}
.migracao-fechar svg,
.atribuicao-fechar svg { width: 16px; height: 16px; }
.migracao-fechar:hover,
.atribuicao-fechar:hover { color: var(--navy); background: var(--tile); }
.migracao-fechar:focus-visible,
.atribuicao-fechar:focus-visible { outline: 2px solid var(--petrol); outline-offset: 2px; }
/* O título para antes do X: 12px de afastamento da borda mais os 40 do
   alvo de toque, e uma folga. */
.migracao-titulo { padding-right: 60px; }
.migracao-acoes [hidden] { display: none; }

.migracao-conta {
  flex: 0 0 auto;
  padding-top: var(--e3);
  border-top: 1px solid var(--line);
  margin-bottom: var(--e4);
}
.migracao-conta-linha {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--e3);
  margin: 0 0 4px;
  font-size: 0.88rem;
  color: var(--gray);
}
.migracao-conta-rotulo { flex: 0 1 auto; }
.migracao-conta-valor { text-align: right; }
/* A última linha é o total, e é a única em peso cheio: as de cima existem para
   dar contexto a ela, não para competir. */
.migracao-conta-linha.e-total {
  margin-top: var(--e2);
  padding-top: var(--e2);
  border-top: 1px solid var(--line);
  font-size: 1rem;
  font-weight: 700;
  color: var(--ink);
}

@media (max-width: 560px) {
  .migracao-ganho-lista { columns: 1; }
}

/* Empilhados e na ordem da decisão: a ação que muda o plano em cima, a que
   mantém tudo como está embaixo. Lado a lado, com textos de comprimentos tão
   diferentes, o botão de aceitar ficava estreito e o de recusar largo — a
   hierarquia saía invertida pelo tamanho. */
.migracao-acoes { display: flex; flex-direction: column; gap: var(--e2); flex: 0 0 auto; }
.migracao-acoes .btn { width: 100%; justify-content: center; }

.atribuicao-titulo {
  font-size: 21px;
  line-height: 1.25;
  color: var(--navy);
  margin-bottom: var(--e2);
  /* Para antes do X, pela mesma conta do título da migração: 12px de borda
     mais os 40 do alvo de toque, e uma folga. */
  padding-right: 60px;
}
.atribuicao-dica { font-size: 14px; color: var(--gray); margin-bottom: var(--e5); }

/* Em coluna, e não em nuvem de pílulas: os rótulos vêm dos cartões e são
   frases, não palavras. Lado a lado eles quebram em alturas diferentes e a
   lista deixa de ser varrível. */
.atribuicao-opcoes {
  display: flex;
  flex-direction: column;
  gap: var(--e2);
  /* É esta lista que rola, e não o diálogo: o rodapé com as ações precisa
     continuar à vista. `min-height: 0` é obrigatório para um filho de flex
     poder encolher abaixo do próprio conteúdo. */
  overflow-y: auto;
  min-height: 0;
  /* Respiro para a sombra do foco não ser cortada pela borda da rolagem. */
  padding: 2px;
  margin: -2px;
}
.atribuicao-opcoes .chip {
  width: 100%;
  display: flex;
  align-items: center;
  gap: var(--e3);
  text-align: left;
  border-radius: var(--raio-sm);
  padding: 11px var(--e4);
  cursor: pointer;
}
/* A caixa de marcar é desenhada no ::before: o chip é um <button> com
   aria-pressed, não um checkbox, porque o alvo inteiro precisa ser clicável e
   o estado precisa ser anunciado como "pressionado". */
.atribuicao-opcoes .chip::before {
  content: "";
  width: 18px;
  height: 18px;
  flex: none;
  border: 1.5px solid currentColor;
  border-radius: 5px;
}
.atribuicao-opcoes .chip[aria-pressed="true"] {
  border-color: var(--petrol);
  background: var(--cyan-ghost);
  color: var(--petrol);
}
.atribuicao-opcoes .chip[aria-pressed="true"]::before {
  content: "✓";
  display: grid;
  place-items: center;
  background: var(--petrol);
  border-color: var(--petrol);
  color: #fff;
  font-size: 12px;
  line-height: 1;
}
.atribuicao-opcoes .chip:focus-visible { outline: 3px solid var(--cyan); outline-offset: 2px; }

.atribuicao-status { min-height: 1.2em; margin-top: var(--e4); font-size: 13px; color: var(--gray-light); }

.atribuicao-acoes {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--e4);
  margin-top: var(--e4);
  flex-wrap: wrap;
}
.atribuicao-acoes .btn-primary:disabled { opacity: .5; cursor: not-allowed; }

@media (max-width: 560px) {
  .atribuicao { padding: var(--e5); }
  .atribuicao-acoes { flex-direction: column-reverse; align-items: stretch; }
  .atribuicao-acoes .btn { width: 100%; justify-content: center; }
}

/* O valor da cobrança sob a manchete: presente, nomeado, e num peso que não
   compete com o mensal equivalente. */
.resumo-cobranca {
  margin-top: var(--e2);
  font-size: 12.5px;
  line-height: 1.4;
  color: var(--gray-light);
}
.resumo-cobranca[hidden] { display: none; }

/* O "?" do subtítulo fica no meio de uma frase, e não no fim de um rótulo
   como os outros 37 da página: a vírgula vem logo depois dele. Sem o
   `vertical-align`, o círculo assenta na linha de base e empurra a vírgula
   para longe; o `margin` assimétrico devolve a pontuação para junto do
   texto, mantendo o respiro à esquerda, onde o termo termina.

   Só aqui, e não em `.tbl-help`: mexer na regra base moveria os 37 ícones
   do comparativo e do cartão do VIP, que estão certos onde estão. */
#table-subtitle .tbl-help { vertical-align: middle; margin: 0 2px; }

/* ======================================================================
   RESPONSIVO CONSOLIDADO — precisa ser a última coisa do arquivo
   ======================================================================
   Media query NÃO soma especificidade. Uma regra responsiva escrita no meio
   do arquivo perde para qualquer redeclaração posterior do mesmo seletor fora
   de media query, e este arquivo tem três rodadas sucessivas de reequilíbrio
   de `.checkout-colunas` e `.oferta-vip` escritas DEPOIS das media queries
   delas — cada uma feita para o desktop, cada uma desligando em silêncio o
   empilhamento do celular.

   O que isso produzia em 390px, medido em 15/09/2026: `.checkout-colunas`
   ficava em `70px 220px` em vez de uma coluna só, o cartão do VIP herdava
   20px de largura e o checkout inteiro era ilegível no telefone. Nenhum teste
   pegava, porque a suíte roda quase toda em 1440px e o CSS, lido isolado,
   parece correto.

   Regra para quem mexer aqui: responsivo novo entra NESTE bloco, não ao lado
   da regra que ele modifica. */

@media (max-width: 960px) {
  .faixa-gratuito-wrap { grid-template-columns: minmax(0, 1fr); gap: 0; }
  .faixa-gratuito { grid-column: 1; }
}

@media (max-width: 900px) {
  /* Uma coluna: o resumo deixa de ser coluna e vira o bottom sheet. */
  .checkout-colunas { grid-template-columns: minmax(0, 1fr); }
  /* A arte do VIP encolhe e sai do posicionamento absoluto — ancorada ao
     fundo do cartão, ela vazava para fora dele em tela estreita. */
  .oferta-vip { grid-template-columns: 180px minmax(0, 1fr); align-items: center; }
  .oferta-vip-arte { margin: 0; min-height: 0; }
  .oferta-vip-arte img { position: static; width: 100%; max-width: 200px; margin: 0; }
}

@media (max-width: 640px) {
  /* Empilhado não há linhas a compartilhar: o corpo do cartão volta a medir o
     próprio conteúdo. */
  .tela-planos .plan-corpo { flex: 1 1 auto; }
  .faixa-gratuito-texto { flex-basis: 100%; }
}

@media (max-width: 560px) {
  /* Abaixo disto a arte não cabe ao lado do texto. */
  .oferta-vip { grid-template-columns: minmax(0, 1fr); }
  .oferta-vip-checks { grid-template-columns: minmax(0, 1fr); }
  /* Uma coluna, e cada peça na sua linha. A faixa mede 272px no celular: o
     botão fica com os 167 dele e sobram 81 para o preço, que precisa de 136 —
     o "/mês" era cortado e o valor ficava pela metade.

     Reduzir `grid-template-columns` a uma coluna NÃO basta: o preço, o botão e
     o link têm coluna e linha atribuídas explicitamente mais acima, e o
     `grid-column: 2` do botão faz o grid criar uma segunda coluna implícita —
     o layout continuava em duas, só que sem ninguém ter pedido. A atribuição
     precisa ser desfeita aqui, item por item. A regra que existia neste ponto
     dizia `flex-direction: column`, inerte desde que esta faixa virou grid. */
  .oferta-vip-acao { grid-template-columns: minmax(0, 1fr); }
  .oferta-vip-acao > div:first-child { grid-column: 1; grid-row: 1; }
  .oferta-vip-acao > .btn { grid-column: 1; grid-row: 2; width: 100%; }
  .oferta-vip-acao > .vip-colapso-base { grid-column: 1; grid-row: 3; }
  .oferta-vip-acao > .vip-microcopy { grid-column: 1; grid-row: 4; }
  .oferta-vip-acao .link-continuar { grid-column: 1; grid-row: 5; }
}

/* ============ "Continuar" é botão só com borda, junto do VIP (23/09/2026) ===
   Decisão do owner depois do diagnóstico da onda 3: 13 das 20 pessoas que
   chegaram ao checkout não passaram desta etapa, e a única saída sem o VIP era
   um link de 15px na última linha do cartão. Agora ela é um botão SEM fundo e
   sem sombra: seguir fica à vista, e a oferta continua sendo o destaque.

   Os dois botões com a MESMA largura, um sobre o outro (owner, 23/09/2026,
   noite). A faixa mede 336px em toda largura de desktop; "Continuar como
   Profissional" pede 245px, e ao lado do preço o "Adicionar VIP" só tinha
   167. Com o preço na linha de cima, os dois ocupam a faixa inteira. A
   microcopy "Sem pagamento" fica embaixo dos dois, e a linha da base por
   último. */
.oferta-vip-acao > div:first-child { grid-column: 1 / -1; grid-row: 1; }
.oferta-vip-acao > .btn { grid-column: 1 / -1; grid-row: 2; width: 100%; }
.oferta-vip-acao .link-continuar.vip-seguir {
  grid-column: 1 / -1;
  grid-row: 3;
  justify-self: stretch;
  width: 100%;
  min-width: 0;
  min-height: 49px;
  padding: 0 20px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 1.5px solid currentColor;
  border-radius: var(--radius-btn);
  background: transparent;
  color: var(--petrol);
  text-decoration: none;
}
.oferta-vip-acao .link-continuar.vip-seguir:hover { color: var(--navy); background: var(--cyan-ghost); }
.oferta-vip-acao > .vip-microcopy { grid-row: 4; }
.oferta-vip-acao > .vip-colapso-base { grid-row: 5; }

/* Saída nomeada dentro do pop-up de migração: a microcopy do cartão com um
   link no fim, não um segundo botão. */
.migracao-saida { margin: 0; }
.migracao-saida-link {
  background: none;
  border: 0;
  padding: 0;
  font: inherit;
  font-weight: 600;
  color: var(--petrol);
  text-decoration: underline;
  text-underline-offset: 3px;
  cursor: pointer;
}
.migracao-saida-link:hover { color: var(--navy); }

@media (max-width: 560px) {
  /* Uma coluna, na mesma ordem do desktop. */
  .oferta-vip-acao > .btn { grid-column: 1; grid-row: 2; }
  .oferta-vip-acao .link-continuar.vip-seguir { grid-column: 1; grid-row: 3; }
  /* Em telas de 320-360px o rótulo de 245px não cabe numa linha: quebra
     dentro do botão em vez de vazar pela borda. */
  .oferta-vip-acao .link-continuar.vip-seguir { white-space: normal; line-height: 1.25; padding: 10px 16px; }
  .oferta-vip-acao > .vip-microcopy { grid-row: 4; }
  .oferta-vip-acao > .vip-colapso-base { grid-row: 5; }
}
