/* ==========================================================================
   Hero do plano de entrada (100 Mega) — slide do Master Carousel.

   Restrição que manda no layout: o slide vive dentro de #master-hero-carousel,
   que trava em 680px no desktop e em 100svh menos o header no mobile.

   DIREÇÃO DE MOVIMENTO (a queixa era "essa animação está horrível"):
   existe UMA entrada, encadeada, que termina no preço/CTA, mais o anel que se
   fecha em volta do "100 Mega". Nada em loop, nada infinito, nada decorativo.
   Tudo preso a .animate-active, que é o contrato que os outros seis slides do
   carrossel já usavam e do qual este era o único de fora.
   ========================================================================== */

.hero-essencial-section {
    /* Tipografia do sistema (web_site/tema/tokens.css). Este era o ÚNICO slide
       do carrossel sem font-family declarada: caía no default do Bootstrap e
       renderizava em Segoe UI / SF enquanto os vizinhos usam Orbitron. Era a
       razão tipográfica de ele "parecer de outro site".
       ATENÇÃO: só os pesos 400/700/800 da Orbitron estão carregados
       (vendor/orbitron/orbitron.css) — nunca usar 600 nos elementos display. */
    --he-font-display: var(--vn-font-heading, "Orbitron", "Barlow", sans-serif);
    --he-font-texto: var(--vn-font-body, "Barlow", "Inter", system-ui, sans-serif);
    font-family: var(--he-font-texto);

    position: relative;
    width: 100%;
    height: 100%;
    display: flex;
    align-items: center;
    /* safe center: em container rolável, align-items:center torna o TOPO
       inalcançável quando o conteúdo estoura. Com safe, cai para flex-start.
       Declarado em duas linhas de propósito: navegador sem suporte a `safe`
       descarta só a segunda e mantém a centralização de hoje. */
    align-items: safe center;

    /* Recorta na horizontal para o ambiente decorativo (e os 50px da entrada
       lateral) continuarem contidos. A ROLAGEM DE EMERGÊNCIA NÃO MORA AQUI:
       ela foi para .hero-essencial-content — ver o comentário daquele bloco. */
    overflow-x: clip;

    /* #050b14 é a base LITERAL do carrossel (wrapper/css/style.css, fundo de
       #master-hero-carousel e dos slides; hero_streaming/css/style.css). Era #061422 -> #050b14 -> #04121c: no meio
       da transição do Swiper o fundo "trocava", e o slide chegava com outra base.
       O halo segue ciano como o de #tv-assinatura (hero_tv); a POSIÇÃO em 78% 40%
       fica, porque existe para dar volume atrás do palco — é layout, não paleta. */
    background:
        radial-gradient(120% 90% at 78% 40%, rgba(0, 255, 234, .10) 0%, rgba(5, 11, 20, 0) 62%),
        #050b14;
    /* CORPO DE TEXTO: os dois únicos valores que a casa usa, e só eles —
       rgba(255,255,255,.9x) (.text-light-80, hero_streaming/css/style.css)
       e #d1d9e0 (.tv-features-list li, hero_tv/css/style-restored.css).
       Eram SETE cinzas azulados próprios (#e9f4fb, #f2fbff, #b9e6f2 e quatro rgba
       entre 180 e 214) que não existem em nenhum vizinho: compostos sobre #050b14
       davam rgb(176,194,204) contra o rgb(230,230,230) da lede do streaming, ou
       seja, o corpo de texto mudava de TEMPERATURA e de brilho ao virar o slide,
       sem motivo declarado. Todos os valores novos SÓ SOBEM o contraste. */
    color: rgba(255, 255, 255, .92);

    /* Tempo da varredura do anel. O atraso do tick de origem é DERIVADO destes
       valores (a conta está no bloco do tick): mexeu aqui, refaça a conta lá.

       420ms e não 1100ms: com 1100 o anel só fechava em 1220ms e a agulha — que é o
       objeto mais brilhante do palco e usa a MESMA cor do botão primário — ainda
       brilhava ~450ms DEPOIS de o preço ter pousado, puxando o olho para o canto
       oposto ao dos CTAs. Agora o anel fecha em 300 + 420 = 720ms, logo antes de
       .he-acao terminar em 760ms: o último movimento da cena volta a ser a oferta,
       que é exatamente o que o comentário do bloco de entrada promete.

       POR QUE O ATRASO É 300ms E NÃO 120ms — número medido contra o relógio do
       Swiper, não gosto. O wrapper põe .animate-active 150ms depois do início da
       transição (100ms de triggerTimer + 50ms de triggerAnimation, em
       wrapper/js/script.js) e a transição dura 600ms (speed: 600). Com 120ms a
       varredura começava em t=270ms do trânsito, e 330 dos seus 420ms eram
       desenhados com o SVG inteiro ainda sendo ARRASTADO pela tela — 79% do gesto —
       sendo que a curva acima entrega 50% do anel nos primeiros 87ms. O que se via
       não era "algo mediu", era "algo piscou enquanto o slide passava". Com 300ms a
       varredura começa em t=450ms, quando o Swiper já cumpriu ~96% do percurso
       (bezier ease) e o palco está em ~88% de opacidade, com ~6px de deslocamento
       restante.

       O RELÓGIO INTEIRO DA CENA, para ninguém ter que recalcular na mão:
         palco entra .......... 40 -> 560ms
         anel varre ........... 300 -> 720ms
         agulha se dissolve ... 520 -> 740ms
         .he-acao (preço+CTAs) . assenta em 760ms
         .he-pagamento ......... 830ms
       O desenho fecha ANTES da oferta, e a oferta segue sendo o último movimento. */
    --he-sweep: 420ms;
    --he-sweep-ease: cubic-bezier(.32, .72, .28, 1);
    --he-sweep-delay: 300ms;   /* palco assentado E trânsito do Swiper terminado */

    /* CURVA DE ENTRADA DA CASA. cubic-bezier(0.25, 1, 0.5, 1) é a única usada nos
       cinco vizinhos para o gesto de entrada — 14 ocorrências entre hero_streaming,
       hero_tv, hero_gamer, hero_quem_somos e hero_trabalhe_conosco. Este slide usava
       cubic-bezier(.2, .7, .3, 1), mais linear no miolo: o conteúdo entrava com um
       "peso" diferente no exato gesto que se repete a cada 8s de autoplay.
       NÃO confundir com --he-sweep-ease: aquela é do DESENHO, e os atrasos dos ticks
       foram calculados em cima dela. São curvas de coisas diferentes. */
    --he-ease-entrada: cubic-bezier(0.25, 1, 0.5, 1);
}

/* ---------- ambiente decorativo ----------
   Removido. Malha, vinheta e camadas vêm do ::before compartilhado do wrapper
   (wrapper/css/style.css, bloco `::before` da lista de seções do carrossel).
   Não redesenhar aqui: era essa duplicata que fazia duas grades diferentes
   conviverem no mesmo carrossel. */

.hero-essencial-content {
    width: 100%;
    /* z-index vem da camada compartilhada do wrapper (wrapper/css/style.css, regra
       que dá `z-index: 3` aos *-content do carrossel): 1 = fundo, 2 = malha ::before,
       3 = conteúdo. Era 2 aqui — exatamente a camada da malha, o que empataria
       conteúdo e grade assim que o ::before entrasse. */
    padding-block: clamp(1.25rem, 3vh, 2.25rem);

    /* ROLAGEM DE EMERGÊNCIA — e por que ela mora AQUI e não na seção.
       Ela existe porque em zoom 200% num 1280x720 o viewport vira 640x360, a seção
       mede 288px e o conteúdo 557px: preço e os DOIS CTAs ficavam inalcançáveis
       (WCAG 1.4.4 / 1.4.10). Era `overflow-y: auto` na PRÓPRIA seção, e isso a
       transformava em scroll container — o ::before do wrapper é
       `position: absolute; inset: 0`, ou seja, ele passava a ter a altura da caixa
       de padding e ROLAVA JUNTO com o conteúdo. Na faixa em que a rolagem entra de
       verdade (telas baixas entre 769px e 991.98px, ou zoom acima de 768px) a parte
       rolada aparecia sem grade nenhuma, uma faixa lisa que os vizinhos não têm.
       Com o scroll container aqui dentro, a seção volta a ser caixa comum e o
       ::before cobre a seção inteira, rolada ou não.

       O `overflow-x: clip` é obrigatório: sem ele o eixo horizontal viraria `auto`
       (regra da cascata de overflow) e os 50px de entrada lateral do .he-palco —
       que caminham para a DIREITA — virariam overflow ROLÁVEL, ou seja, uma barra
       horizontal piscando por 520ms a cada troca de slide.
       MEDIDO NO AR, não suposto: com `overflow-y: auto` no outro eixo, o `clip`
       COMPUTA COMO `hidden` (é a regra da própria especificação: clip ao lado de um
       valor de rolagem vira hidden). Continua sem barra e continua recortando — mas
       `overflow-clip-margin` deixa de valer, então NÃO adianta declarar um aqui.
       Quem resolve a folga da entrada é a media query de 992-1199px lá embaixo, com
       o número medido. */
    max-height: 100%;
    overflow-x: clip;
    overflow-y: auto;
    overscroll-behavior-y: contain;
    scrollbar-width: thin;
    scrollbar-color: rgba(0, 255, 234, .35) transparent;
}

/* ---------- coluna de texto ----------
   Os clamp() abaixo usam a forma clamp(min, Xvw + Yrem, max), não
   clamp(min, Nvw, max). Na forma antiga o termo do meio era MENOR que o mínimo
   em toda a faixa de desktop comum (ex.: .he-lede tinha 1.05vw = 13,44px em
   1280px contra um mínimo de 14,4px), então 8 dos 11 clamps estavam mortos: o
   corpo de texto ficava travado no piso enquanto o display já estava no teto.
   Daí a leitura de "número gigante + textinho". */

/* Mesma peça que .trabalhe-eyebrow (hero_trabalhe_conosco/css/style.css):
   chanfro de 10px, ciano #00ffea, peso 800. Era uma pílula border-radius:999px em
   #7dffe4 — o único arredondado desse tipo no carrossel inteiro, num turquesa que
   não existe em tokens.css. letter-spacing cai de .16em para .1em porque o chanfro
   já consome canto e a casa usa ~1px. */
.he-eyebrow {
    display: inline-flex;
    align-items: center;
    gap: .5rem;
    margin: 0 0 .7rem;
    padding: .5rem .9rem;
    border: 1px solid rgba(0, 255, 234, .42);
    border-radius: 0;
    clip-path: polygon(10px 0, 100% 0, 100% calc(100% - 10px), calc(100% - 10px) 100%, 0 100%, 0 10px);
    background: rgba(5, 11, 20, .72);
    box-shadow: 0 0 18px rgba(0, 255, 234, .14);
    color: var(--vn-neon-blue, #00ffea);
    font-size: clamp(.72rem, .14vw + .68rem, .81rem);
    font-weight: 800;
    letter-spacing: .1em;
    text-transform: uppercase;
}

.he-eyebrow .bi {
    font-size: .95em;
    line-height: 1;
    color: var(--vn-neon-blue, #00ffea);
}

.he-titulo {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    margin: 0 0 .6rem;
    line-height: 1.05;
}

/* O nome do plano é a identidade deste slide e agora lidera a coluna.
   O "100" gigante saiu daqui: ele mora no palco, dentro do anel. Manter os
   dois era ter dois números grandes disputando o mesmo enquadramento. */
.he-nome {
    font-family: var(--he-font-display);
    font-size: clamp(1.5rem, 1.9vw + 1.05rem, 2.7rem);
    /* 800 é o peso de título de TODOS os vizinhos (.text-gradient-hero,
       .gamer-title, .section-title, .tv-value). Orbitron 800 está carregada. */
    font-weight: 800;
    color: #ffffff;
    letter-spacing: -.02em;
    text-shadow: 0 2px 10px rgba(0, 0, 0, .5);

    /* O nome vem do banco e é editável no admin. Sem teto de linhas, um nome
       longo estoura a caixa de 680px sem aviso. */
    display: -webkit-box;
    -webkit-line-clamp: 2;
    line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

/* Só entra em cena quando o palco sai (ver @media de tela baixa no fim).
   Sem isto, a velocidade desapareceria do slide inteiro nessa faixa. */
/* O ECO E O NÚMERO DO NÚCLEO SÃO O MESMO ELEMENTO em telas diferentes: o eco entra
   exatamente quando o palco sai (tela baixa e deitada). Por isso tudo o que os dois
   compartilham mora AQUI, num seletor só.
   Antes o comentário dizia "idêntico" mas só o gradiente era: o eco vinha com peso
   700 e letter-spacing -.02em contra 800 e -.03em do núcleo, então girar o celular
   para paisagem numa tela baixa trocava o PESO do "100 Mega". Agora a única coisa
   que diverge é o font-size, e a promessa virou verdade estrutural.
   Peso 800 porque é o peso de display de toda a casa (.gamer-title, .tv-value,
   .btn-hero-cta, .he-nome) — 700 era o único fora do padrão.

   O gradiente é --vn-gradient-cyan-text (web_site/tema/tokens.css). Ele nasceu aqui
   como paleta privada deste slide e foi promovido a token do tema para deixar de
   ser divergente: agora é o SEGUNDO gradiente oficial da casa, ao lado do
   .text-gradient-hero (hero_streaming/css/style.css). NÃO uso o laranja->ciano do
   .text-gradient-hero aqui porque ele levaria o pior ponto de contraste do MAIOR
   número da página de ~11,5:1 para ~5,8:1, e a restrição manda não regredir. */
.he-titulo__eco,
.he-nucleo__num {
    font-family: var(--he-font-display);
    font-weight: 800;
    letter-spacing: -.03em;
    background: var(--vn-gradient-cyan-text,
                linear-gradient(100deg, #ffffff 0%, #8bfff5 46%, #00ffea 100%));
    -webkit-background-clip: text;
    background-clip: text;
    /* par color + -webkit-text-fill-color é o padrão da casa (.text-gradient-hero):
       se background-clip falhar sobra branco legível, não texto invisível. */
    -webkit-text-fill-color: transparent;
    color: #ffffff;
}

.he-titulo__eco {
    display: none;
    order: -1;
}

/* ISTO NÃO É REDUNDÂNCIA COM O `color: #ffffff` DO .he-nome, É CORREÇÃO DE BUG.
   site-dark.css declara, para o site inteiro,
     body.dark-theme h2 span, body.dark-theme .highlight { color: var(--neon-blue) !important }
   e `!important` de folha de autor ganha de QUALQUER especificidade. Resultado
   medido no site no ar: o nome do plano renderizava CIANO CHAPADO — o único
   título de cor sólida do carrossel, e ainda no mesmo #00ffea do selo "Plano de
   entrada" logo acima. O arquivo dizia #ffffff e o navegador pintava #00ffea.

   A SAÍDA É A DA PRÓPRIA CASA, não um !important de resposta: os títulos dos
   vizinhos escapam dessa regra por -webkit-text-fill-color, não por color
   (.text-gradient-hero no streaming e no gamer, .highlight-gradient no quem
   somos — todos com color herdado/ciano e o fill mandando no que se vê).
   -webkit-text-fill-color vence color por ser mais específico na cascata de
   preenchimento de texto, sem precisar de !important e sem tocar na regra
   global, que é de todo o site.
   O `color: #ffffff` do .he-nome continua valendo como fallback para navegador
   sem -webkit-text-fill-color — é o mesmo par que o resto do arquivo usa.

   O .he-titulo__eco fica FORA daqui de propósito: ele já declara
   -webkit-text-fill-color: transparent para o gradiente, e sobrescrever isso
   apagaria o gradiente do "100 Mega". */
.hero-essencial-section .he-nome {
    -webkit-text-fill-color: #ffffff;
}

.he-lede {
    max-width: 46ch;
    margin: 0 0 clamp(1.1rem, 2.4vh, 1.6rem);
    font-size: clamp(.9rem, .19vw + .86rem, 1.03rem);
    line-height: 1.5;
    color: rgba(255, 255, 255, .82);
}

/* ---------- lista de benefícios ---------- */
.he-lista {
    list-style: none;
    /* margem de QUEBRA DE GRUPO: tem que ser visivelmente maior que o gap
       interno da própria lista, senão o olho lê blocos soltos em vez de grupos */
    margin: 0 0 clamp(1.2rem, 2.8vh, 1.75rem);
    padding: 0;
    display: grid;
    gap: clamp(.5rem, 1.2vh, .8rem);
}

.he-item {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr);
    align-items: center;
    gap: .8rem;
}

.he-item__icone {
    width: 2.35rem;
    height: 2.35rem;
    display: grid;
    place-items: center;
    /* chanfro de 8px = chip pequeno da casa (.tech-btn, .btn-speed-ultra,
       .trabalhe-feature). Era border-radius: 11px com gradiente #12d8c4/#0d8ef0. */
    border-radius: 0;
    clip-path: polygon(8px 0, 100% 0, 100% calc(100% - 8px), calc(100% - 8px) 100%, 0 100%, 0 8px);
    border: 1px solid rgba(0, 255, 234, .30);
    background: rgba(0, 255, 234, .08);
    color: var(--vn-neon-blue, #00ffea);
    flex: none;
}

/* Bootstrap Icons, como nos CINCO vizinhos (hero_tv, hero_gamer, hero_quem_somos,
   hero_trabalhe_conosco e hero_streaming). Aqui eram cinco <svg> de CONTORNO
   desenhados à mão, com stroke-width 1.7/1.8: o carrossel chegava a ter DOIS CTAs
   de WhatsApp com marcas diferentes — o glifo cheio do gamer e um contorno próprio
   neste slide. bootstrap-icons.css já carrega no base.html para o site inteiro. */
.he-item__icone i {
    font-size: 1.2rem;
    line-height: 1;
}

.he-item__txt {
    display: flex;
    flex-direction: column;
    min-width: 0;
}

.he-item__txt b {
    font-size: clamp(.9rem, .14vw + .87rem, 1rem);
    font-weight: 700;
    color: #ffffff;
    line-height: 1.25;
}

.he-item__txt small {
    font-size: clamp(.78rem, .1vw + .76rem, .85rem);
    color: rgba(255, 255, 255, .78);
    line-height: 1.35;
}

/* ---------- preço e ações ---------- */
.he-acao {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: clamp(.9rem, 2vw, 1.6rem);
    padding-top: clamp(.8rem, 1.8vh, 1.2rem);
    border-top: 1px solid rgba(255, 255, 255, .10);
}

.he-preco__de {
    display: block;
    font-size: clamp(.72rem, .1vw + .7rem, .8rem);
    letter-spacing: .12em;
    text-transform: uppercase;
    color: rgba(255, 255, 255, .78);
    margin-bottom: .12rem;
}

.he-preco__valor {
    display: flex;
    align-items: baseline;
    gap: .2rem;
    margin: 0;
    color: #ffffff;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

/* A CIFRA É LARANJA — e este comentário registra a decisão QUE VIGORA. Ele dizia o
   contrário do que a regra abaixo faz: argumentava por manter o branco enquanto o
   código já pintava de laranja. Num arquivo cuja autoridade inteira vem das decisões
   documentadas, essa era a linha com maior chance de fazer o próximo leitor desfazer
   o que está no ar.

   POR QUE LARANJA (ordem 1, seguir o padrão dos outros): .tv-currency (hero_tv) e
   .currency.text-orange (template do streaming) são os dois vizinhos que exibem
   preço, e os dois pintam o "R$" de laranja. Era #9fd6e8 — mais um azul avulso, que
   não existe em tokens.css — com peso 600, que a casa nem carrega.

   O NÚMERO, medido em produção sobre a base #050b14 (luminância relativa 0,00329):
     #ff4d1d (var(--vn-neon-orange), idêntico ao .tv-currency) ...... 5,95:1
     #ffffff (todo o resto do texto do slide) ...................... 19,73:1
   5,95 passa com folga o AA da restrição 6 ("contraste de texto >= 4,5"), MAS passa
   a ser o menor contraste de texto do slide: o piso cai de 11,48 para 5,95. Essa
   regressão foi ACEITA CONSCIENTEMENTE em troca da paridade de marca da ordem 1, e
   fica aqui registrado que o critério vigente desta peça é PASSAR O AA, e não "não
   regredir" — os dois critérios conviviam em conflito neste arquivo, e é isso que se
   resolve. Vale só para esta cifra; nenhum outro texto do slide desce de 19,73.

   O text-shadow laranja do valor logo abaixo (mesmo tratamento do .tv-value) segue
   sendo o sinal de marca que NÃO custa contraste nenhum. */
.he-preco__cifra {
    /* Mesma familia do valor ao lado: o par cifra+valor do hero_tv
       (.tv-currency / .tv-value) e a referencia deste bloco. Sem isto a cifra
       herdava a fonte de corpo e as duas metades do preco divergiam. */
    font-family: var(--he-font-display);
    font-size: clamp(.88rem, .38vw + .78rem, 1.12rem);
    font-weight: 700;
    color: var(--vn-neon-orange, #ff4d1d);
}

/* O preço é o pico de hierarquia da coluna: fica ACIMA do nome do plano
   (~48px contra ~41px em 1280px). O olho desce e para no valor, antes do CTA. */
.he-preco__valor strong {
    font-family: var(--he-font-display);
    font-size: clamp(2.25rem, 1.33vw + 1.93rem, 3.2rem);
    font-weight: 800;
    letter-spacing: -.022em;
    line-height: 1;
    color: #ffffff;
    /* mesmo tratamento de .tv-value (hero_tv/css/style-restored.css). É text-shadow: não altera o
       contraste calculado do texto, então não mexe na restrição 6. */
    text-shadow: 0 0 15px rgba(255, 77, 29, .55);
}

.he-preco__mes {
    font-size: clamp(.81rem, .19vw + .77rem, .94rem);
    color: rgba(255, 255, 255, .8);
}

/* Fallback do preço nulo (o campo `preco` do model Plano é null=True). Texto, não número. */
.he-preco__valor--consulta {
    font-family: var(--he-font-texto);
    font-size: clamp(1.1rem, .4vw + .98rem, 1.35rem);
    font-weight: 700;
    white-space: normal;
}

.he-botoes {
    display: flex;
    gap: .6rem;
    flex-wrap: wrap;
}

/* ---------- botões: o vocabulário da casa ----------
   Mesmo desenho de .btn-hero-cta (hero_streaming), .btn-cyber-cta e
   .btn-cyber-secondary (hero_tv), .trabalhe-btn (trabalhe_conosco) e
   .btn-shine-cta / .btn-tech-secondary (quem_somos): canto chanfrado por
   clip-path, border-radius 0, caixa alta, peso 800, hover translateY(-3px).

   A COR TEM FUNÇÃO NESTA CASA, não é decoração:
     laranja = a ação que converte   (repetido nos 5 vizinhos e até nos filtros ativos)
     ciano   = apoio / informação
   Este slide invertia o sistema: o botão que ASSINA era turquesa claro com texto
   ESCURO #032028 — o único claro-sobre-escuro do carrossel inteiro — e o secundário
   era branco neutro. Quem já tinha passado por outro slide aprendeu que "o laranja é
   o que assina", e aqui não havia nenhum. */
.he-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: .5rem;
    /* restrição 7: alvo de toque >= 44px. Antes os 49px saíam por acidente do
       line-height default; com line-height explícito o piso vira contrato. */
    min-height: 48px;
    padding: .88rem 1.35rem;
    border-radius: 0;
    clip-path: polygon(10px 0, 100% 0, 100% calc(100% - 10px), calc(100% - 10px) 100%, 0 100%, 0 10px);
    font-size: clamp(.88rem, .19vw + .83rem, 1rem);
    font-weight: 800;
    line-height: 1.2;
    text-transform: uppercase;
    letter-spacing: .03em;
    text-decoration: none;
    white-space: nowrap;
    transition: all .3s;
}

/* Mesmo motivo do ícone dos benefícios: glifo do Bootstrap Icons, não SVG próprio.
   O do WhatsApp é literalmente o MESMO de hero_gamer/index.html. */
.he-btn i {
    font-size: 1.05rem;
    line-height: 1;
    flex: 0 0 auto;
}

/* PRIMÁRIO: vidro laranja — idêntico ao dos outros cinco slides.
   Branco sobre este vidro composto em #050b14 fica em torno de 15:1.

   SOBRE OS box-shadow DAQUI PARA BAIXO (e o do .he-eyebrow, linha do clip-path de
   10px): o clip-path do .he-btn RECORTA box-shadow externo — a mesma mecânica
   documentada no bloco do :focus-visible logo abaixo. Ou seja, estes glows NÃO
   pintam um pixel. Ficam declarados DE PROPÓSITO, por paridade literal com
   .btn-hero-cta (hero_streaming), .btn-cyber-cta e .btn-cyber-secondary
   (hero_tv), que carregam exatamente os mesmos valores e são recortados
   pelo mesmo motivo: a ordem 1 é "seguir o padrão dos outros". NÃO REMOVER
   achando que é sobra esquecida, e não trocar por filter: drop-shadow() aqui
   sozinho — drop-shadow segue o contorno recortado e funcionaria, mas só vale se
   for aplicado em TODOS os botões da casa na mesma passada, senão este slide volta
   a ser o botão diferente. O hover real hoje é o translateY(-3px) mais o fundo. */
.he-btn--primario {
    background: var(--vn-glass-orange-bg);
    border: 1px solid var(--vn-glass-orange-border, #ff4d1d);
    color: #ffffff;
    text-shadow: var(--vn-glass-text-shadow);
    box-shadow: var(--vn-glass-orange-glow);
}

.he-btn--primario:hover,
.he-btn--primario:focus-visible {
    transform: translateY(-3px);
    background: var(--vn-glass-orange-bg-hover);
    box-shadow: var(--vn-glass-orange-glow-hover);
    color: #ffffff;
}

/* SECUNDÁRIO: contorno ciano — mesmo desenho de .btn-cyber-secondary.
   #00ffea sobre rgba(0,255,234,.05) em #050b14 fica em torno de 13:1. */
.he-btn--secundario {
    background: rgba(0, 255, 234, .05);
    border: 1px solid var(--vn-neon-blue, #00ffea);
    color: var(--vn-neon-blue, #00ffea);
}

.he-btn--secundario:hover,
.he-btn--secundario:focus-visible {
    transform: translateY(-3px);
    background: rgba(0, 255, 234, .12);
    border-color: var(--vn-neon-blue, #00ffea);
    box-shadow: 0 0 20px rgba(0, 255, 234, .35);
    color: var(--vn-neon-blue, #00ffea);
}

/* clip-path RECORTA outline e box-shadow externo — os botões chanfrados da casa
   perdem o anel de foco por causa disso. O anel INSET abaixo fica dentro do recorte
   e sempre aparece; o outline externo continua declarado para quem renderiza sem
   clip-path e para o modo de alto contraste. Herdar o bug da casa não valia. */
.he-btn:focus-visible {
    outline: 3px solid rgba(0, 255, 234, .85);
    outline-offset: 4px;
    box-shadow: inset 0 0 0 2px rgba(0, 255, 234, .95),
                inset 0 0 0 4px rgba(5, 11, 20, .85);
}

.he-pagamento {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: .4rem .55rem;
    margin: clamp(.8rem, 1.8vh, 1.15rem) 0 0;
    font-size: clamp(.78rem, .1vw + .76rem, .85rem);
    color: rgba(255, 255, 255, .78);
}

/* :not(.visually-hidden) porque o rótulo oculto do grupo ("Formas de pagamento:")
   também é um <span> desta linha e não pode virar selo. O utilitário do Bootstrap
   já neutraliza padding e border com !important, mas deixar o chanfro e o fundo
   fora dele é mais honesto do que depender daquela cascata. */
.he-pagamento span:not(.visually-hidden) {
    padding: .28rem .65rem;
    border: 1px solid rgba(255, 255, 255, .16);
    /* chanfro de 6px = selo minúsculo da casa (.trabalhe-highlight-label).
       Era border-radius: 999px, o último arredondado sobrando no slide. */
    border-radius: 0;
    clip-path: polygon(6px 0, 100% 0, 100% calc(100% - 6px), calc(100% - 6px) 100%, 0 100%, 0 6px);
    background: rgba(255, 255, 255, .05);
}

.he-pagamento em {
    font-style: normal;
    /* era rgba(150,178,192,.8), o texto mais apagado do slide, justamente na
       frase que quebra a objeção principal da oferta de entrada */
    color: rgba(255, 255, 255, .82);
}

/* ==========================================================================
   Coluna visual: o palco
   ==========================================================================
   Um objeto só: anel + número. Antes eram SEIS tokens no mesmo quadrado de
   400px — dois arcos (o anel SVG e o arco desenhado dentro do PNG), dois "100"
   (h1 e selo flutuante) e três "55" (pílula do PNG, chip do PNG e o preço). */

.he-col-visual {
    display: flex;
    justify-content: center;
}

.he-palco {
    position: relative;
    width: min(400px, 100%);
    aspect-ratio: 1 / 1;
    display: grid;
    place-items: center;
    /* container de consulta: deixa o número do núcleo ser uma FRAÇÃO do palco
       (unidade cqw abaixo), em vez de depender do viewport. Assim ele nunca
       estoura o anel em nenhuma largura, e some a necessidade de reescrever o
       tamanho em cada breakpoint. */
    container-type: inline-size;
}

.he-medidor {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    /* leva o início do traço para as 12h: o anel se fecha a partir do topo */
    transform: rotate(-90deg);
}

/* ==========================================================================
   O DESENHO ANIMADO — a varredura
   ==========================================================================
   Uma leitura só, com sentido: o arco VARRE o anel a partir das 12h, uma agulha
   laranja corre na cabeça do traço, o tick de origem acende no instante em que ela
   parte, o anel fecha e a agulha se dissolve. Nada disso repete.

   POR QUE transition E NÃO @keyframes, sempre, para tudo que é de UMA PASSADA:
   o carrossel roda com loop:true (opção `loop` do masterSwiper, em
   wrapper/js/script.js) e o loopFix do Swiper 11
   MOVE nós reais do DOM com append/prepend — não clona (o bundle não tem uma
   ocorrência sequer de swiper-slide-duplicate). Mover um nó reinicia @keyframes;
   não reinicia transition. Uma @keyframes aqui viraria o tique nervoso reiniciando
   toda vez que o slide voltasse.

   GEOMETRIA, para não virar número mágico:
   2*PI*132 = 829,3805 -> 829.4. TODO stroke-dasharray deste bloco tem que somar
   exatamente isso, senão a agulha desgruda da cabeça do arco.
   ========================================================================== */

.he-medidor__trilho,
.he-medidor__brilho,
.he-medidor__arco,
.he-cursor,
.he-cursor__aura {
    fill: none;
    stroke-linecap: round;
}

.he-medidor__trilho {
    stroke: rgba(255, 255, 255, .07);
    stroke-width: 10;
}

/* ANEL COMPLETO, não medição.
   Antes o arco parava em 78% ((829,4 - 182) / 829,4). Isso afirmava "quase no
   máximo" sobre o plano de PISO de uma linha 100 / 600 / 800 / 1000 Mega — e o
   próprio carrossel desmente isso dois slides adiante, no hero de 1 Giga. A
   métrica honesta seria 10%, que vende contra a casa; então a resposta certa
   não é recalibrar a porcentagem, é não afirmar porcentagem nenhuma. De quebra
   a abertura de 22% caía no quadrante superior ESQUERDO, ou seja, a moldura
   ficava rasgada exatamente do lado que encara a coluna de texto.

   2*PI*132 = 829,4. REPOUSO = 0, isto é, anel DESENHADO.
   O `animation: ... both` antigo fazia o contrário: forçava 829,4 (anel vazio)
   como primeiro quadro pintado, então quem abrisse o site em aba de segundo
   plano encontrava o velocímetro sem arco nenhum. */
/* BRILHO DO ARCO. Era `filter: drop-shadow(0 0 12px ...)` no próprio arco. Filtro
   em elemento que anima stroke-dashoffset é reprocessado sobre a região inteira do
   filtro (264x264 unidades) a cada quadro, ~60x/s por 1100ms: era o único ponto caro
   da entrada. Dois traços pintados custam menos, e o brilho passa a CRESCER junto
   com o arco em vez de existir inteiro desde o primeiro quadro.
   Espessura 20 (faixa 122..142) mantém folga para os ticks, que começam em 144. */
.he-medidor__brilho {
    stroke: rgba(0, 255, 234, .16);
    stroke-width: 20;
}

.he-medidor__arco {
    stroke: url(#heArco);
    stroke-width: 10;
    opacity: .95;
}

/* A AGULHA — o gesto que faz o anel MEDIR em vez de só aparecer.
   6 + 823.4 = 829.4 = a circunferência inteira, então existe exatamente UM traço de
   6 unidades por volta. Com o MESMO stroke-dashoffset do arco ele cai sempre na
   cabeça do traço. Isso é GEOMETRIA, não sincronia ajustada no olho:
     arco   (dasharray C,        offset D) ocupa [-D, C-D]  -> cabeça visível em C-D
     agulha (dasharray 6 / C-6,  offset D) tem o traço em    [C-D, C-D+6]
   Mesmo ponto. Mudar a duração depois não dessincroniza nada.
   No repouso (offset 0) ela volta às 12h: a medição dá a volta e retorna ao zero. */
.he-cursor,
.he-cursor__aura {
    stroke-dasharray: 6 823.4;
}

/* Aura larga no laranja da casa. É o único laranja do palco, e aparece exatamente
   no ponto onde a medição está acontecendo. Espessura 20 pelo mesmo motivo do
   brilho: não encostar nos ticks. */
.he-cursor__aura {
    stroke: var(--vn-neon-orange, #ff4d1d);
    stroke-width: 20;
}

/* Núcleo quente, quase branco: dá o ponto de luz sem estourar para amarelo. */
.he-cursor {
    stroke: #ffe3d8;
    stroke-width: 11;
}

/* REPOUSO = anel DESENHADO, agulha APAGADA. Mantém a decisão documentada logo acima:
   quem abre o site em aba de segundo plano ou sem Swiper encontra o desenho PRONTO,
   nunca o palco vazio. */
.he-medidor__arco,
.he-medidor__brilho {
    stroke-dasharray: 829.4;
    stroke-dashoffset: 0;
    transition: stroke-dashoffset var(--he-sweep, 420ms)
                var(--he-sweep-ease, cubic-bezier(.32, .72, .28, 1))
                var(--he-sweep-delay, 300ms);
}

.he-cursor,
.he-cursor__aura {
    stroke-dashoffset: 0;
    opacity: 0;
    /* a opacidade SEGURA em 1 até 520ms e só então cai em 220ms: a agulha fica acesa
       durante toda a varredura e termina de se dissolver em 740ms, praticamente no
       instante em que o anel fecha (300 + 420 = 720ms). Não é um piscar, é um pouso.
       Era `260ms linear 1000ms`: o pouso começava em 1000ms e terminava em 1260ms,
       meio segundo DEPOIS de o preço chegar, com a agulha laranja sendo a única
       coisa em movimento na tela — e no canto oposto ao dos botões.
       O ATRASO ACOMPANHA --he-sweep-delay, não é número solto:
       520 = 300 (início da varredura) + 220 (mais da metade dos 420ms do anel).
       Mexeu em --he-sweep ou em --he-sweep-delay? Refaça esta soma também. */
    transition: stroke-dashoffset var(--he-sweep, 420ms)
                var(--he-sweep-ease, cubic-bezier(.32, .72, .28, 1))
                var(--he-sweep-delay, 300ms),
                opacity 220ms linear 520ms;
}

/* ARMAR O DESENHO PARA A PRÓXIMA LEITURA — e por que são DOIS blocos.
   Fora de cena o anel volta a 829.4 (vazio) SEM animar, para se fechar limpo na
   próxima entrada. A transição de DURAÇÃO ZERO é o que impede o efeito de
   "desdesenhar" na saída, que seria ruído gratuito.

   É `transition` e não `@keyframes` de propósito: este carrossel roda com
   loop:true, e o loopFix do Swiper 11 MOVE nós reais do DOM (append/prepend),
   o que reinicia animações CSS do elemento movido. Transições não reiniciam.

   O prefixo .swiper-initialized é a classe que o próprio Swiper põe no container ao
   inicializar: se o Swiper não carregar (o wrapper cai em setupFallbackCarousel) ou
   se o JS falhar por completo, NENHUM dos dois blocos casa, o anel fica em 0 e o
   palco aparece COMPLETO em vez de vazio. Degradação na direção certa — e é por
   isso que o prefixo continua aqui em vez de ser trocado por `.swiper-slide`
   sozinho, que casaria mesmo sem JS nenhum e deixaria o anel vazio para sempre.

   O QUE MUDOU E POR QUÊ: o atraso de 560ms só faz sentido para o slide que SAI, que
   continua visível durante os 600ms da transição. Aplicado indistintamente, ele
   MATAVA a varredura em dois casos reais:

   (a) PRIMEIRA CARGA com o Essencial como slide 1. O Swiper adiciona
       `swiper-initialized` ANTES de emitir `init`, e o handler de init só põe
       `.animate-active` 50ms depois. Nesses 50ms a regra `:not(.animate-active)`
       mudava o valor ESPECIFICADO de 0 para 829.4 — mas com 560ms de atraso, então
       o valor RENDERIZADO continuava 0. Aos 50ms o `.animate-active` chegava, o
       especificado voltava a 0, a transição atrasada era cancelada e o renderizado
       nunca saía de 0: anel já fechado, ticks acesos, agulha nunca visível, ZERO
       movimento no SVG e nenhum erro no console. Hoje isso está invisível só porque
       `show_trabalhe_carousel_slide` está ligado e o Essencial é o slide 2;
       desmarcar essa caixa no admin apagava a animação do slide.
   (b) IDA E VOLTA em menos de 560ms (dois cliques no bullet, ou arrastar e voltar).
       Transição atrasada é CANCELÁVEL: bastava o seletor deixar de casar antes do
       prazo para o agendamento morrer com o anel ainda em 0 — e o slide reentrava
       com o desenho pronto, sem varredura.

   A separação abaixo resolve os dois pela CAUSA, sem JS: o Swiper atualiza as
   classes de slide ANTES de emitir os eventos, então `.swiper-slide-active` já
   distingue "estou saindo" de "estou entrando" no instante certo.
     - slide que SAI  -> arma com 560ms de atraso (o gesto de saída, preservado).
     - slide ATIVO    -> arma NA HORA, e como o `.animate-active` só chega depois
                         (50ms no init, ~150ms na troca), a varredura sempre roda.
   Armar na hora é invisível: o slide ativo só fica visível com `.animate-active`,
   e sem ele o .he-palco está em opacity 0. */
.swiper-initialized .swiper-slide:not(.swiper-slide-active) .hero-essencial-section:not(.animate-active) .he-medidor__arco,
.swiper-initialized .swiper-slide:not(.swiper-slide-active) .hero-essencial-section:not(.animate-active) .he-medidor__brilho,
.swiper-initialized .swiper-slide:not(.swiper-slide-active) .hero-essencial-section:not(.animate-active) .he-cursor,
.swiper-initialized .swiper-slide:not(.swiper-slide-active) .hero-essencial-section:not(.animate-active) .he-cursor__aura {
    stroke-dashoffset: 829.4;
    /* NÃO é `transition: none`. É duração ZERO com ATRASO de 560ms, e o atraso é a
       correção.
       O wrapper chama resetAnimations() no hook slideChangeTransitionStart do
       masterSwiper: nesse instante o activeIndex JÁ mudou, mas o slide antigo
       continua VISÍVEL pelos 600ms da transição (opção `speed` do masterSwiper, em
       wrapper/js/script.js). Com `transition: none` o reset caía em UM quadro à
       plena vista: o arco e o brilho saltavam de dashoffset 0 para 829.4 (o anel
       sumia de vez), os ticks apagavam de vez e a agulha nascia do nada às 12h, um
       traço LARANJA brilhante em cima de um palco que ainda estava em ~0,85 de
       opacidade. Era o único ponto do slide onde o movimento era um corte e não um
       gesto — exatamente o tipo de coisa que fez o dono chamar a versão anterior de
       horrível.
       560ms é MAIOR que os 520ms do fade do .he-palco (o palco já sumiu quando o
       reset vale) e MENOR que os 600ms da transição do Swiper (o desenho está
       armado antes de a próxima entrada começar). Mexeu num dos três, refaça.
       A `opacity` entra na mesma conta porque é ela que arma a agulha nas linhas
       logo abaixo — sem isso o salto de opacidade continuaria imediato. */
    transition: stroke-dashoffset 0s linear 560ms,
                opacity 0s linear 560ms;
}

/* O slide ATIVO ainda sem .animate-active: mesmos valores de estado, SEM atraso.
   É o que garante que a varredura exista na primeira carga e na volta rápida. */
.swiper-initialized .swiper-slide-active .hero-essencial-section:not(.animate-active) .he-medidor__arco,
.swiper-initialized .swiper-slide-active .hero-essencial-section:not(.animate-active) .he-medidor__brilho,
.swiper-initialized .swiper-slide-active .hero-essencial-section:not(.animate-active) .he-cursor,
.swiper-initialized .swiper-slide-active .hero-essencial-section:not(.animate-active) .he-cursor__aura {
    stroke-dashoffset: 829.4;
    transition: stroke-dashoffset 0s linear,
                opacity 0s linear;
}

/* Armada e estacionada às 12h, no zero, pronta para a próxima leitura. Estas linhas
   só declaram O QUE vale; QUANDO vale são as duas transições acima. */
.swiper-initialized .hero-essencial-section:not(.animate-active) .he-cursor       { opacity: 1; }
.swiper-initialized .hero-essencial-section:not(.animate-active) .he-cursor__aura { opacity: .45; }

/* ---------- o tick: o marco de origem acendendo na partida da agulha ----------
   Comprimento 12 (r=144 -> r=156), então stroke-dasharray:12 com dashoffset:12 é o
   estado apagado e dashoffset:0 é o aceso. A <line> foi escrita do ponto INTERNO
   para o EXTERNO no template justamente para o traço crescer PARA FORA e não invadir
   a faixa do anel.

   POR QUE SOBROU UM SÓ. Eram quatro marcas cardeais. Juntas elas desenhavam uma
   ESCALA, e uma escala com a agulha chegando ao fim afirmava "quase no máximo" sobre
   o plano de PISO de uma linha que vai até 1 Giga — exatamente a afirmação que o
   bloco do arco, logo acima, já tinha derrubado ao trocar o arco de 78% pelo anel
   inteiro. O que sobra é o MARCO DE ORIGEM: o ponto de onde a agulha parte e para
   onde ela volta, no laranja da casa. Some junto o empilhamento de delays que
   precisava ser recalculado a cada mexida em --he-sweep.

   O ATRASO É CALCULADO, NÃO CHUTADO. O marco está no progresso ZERO da varredura,
   então ele acende no instante em que a agulha nasce:
     delay do tick = --he-sweep-delay = 300ms
   A fórmula geral fica registrada para quem um dia acrescentar outro:
     delay = --he-sweep-delay + x(progresso do tick) * --he-sweep
   onde x é o tempo NORMALIZADO que --he-sweep-ease devolve para aquele progresso —
   não é o progresso em si, a curva não é linear. Se --he-sweep, --he-sweep-delay ou
   --he-sweep-ease mudar, refaça a conta.

   ORDEM VISUAL, NÃO ORDEM DESENHADA: .he-medidor tem transform: rotate(-90deg),
   então o tick desenhado às 3h aparece às 12h NA TELA. É por isso que a <line> do
   template carrega coordenadas de 3h — não "arrumar para 12h", o marco sairia de
   baixo da agulha. */
/* A cor sai do token do arco, não de um cinza-azulado próprio: era
   rgba(148,214,228,.34), um valor que não existe em tokens.css nem em nenhum
   vizinho — justamente o tipo de cor avulsa que o comentário do topo deste arquivo
   registra como o erro já corrigido. Mesmo alfa, mesmo efeito, família do arco. */
.he-tick {
    stroke: rgba(0, 255, 234, .34);
    stroke-width: 2;
    stroke-linecap: round;
    stroke-dasharray: 12;
    stroke-dashoffset: 0;      /* REPOUSO = aceso, igual ao anel */
    transition: stroke-dashoffset 110ms cubic-bezier(.2, .7, .3, 1),
                stroke 110ms linear;
}

/* O zero da escala: a agulha sai daqui e volta para cá. Marcado no laranja da casa
   para amarrar as duas pontas do gesto. É stroke decorativo, não texto. */
.he-tick--1 {
    stroke: var(--vn-neon-orange, #ff4d1d);
}

/* Mesma separação do anel, pelo mesmo motivo — ver o comentário longo lá em cima.
   Slide que SAI: duração zero com 560ms de atraso, para os ticks não apagarem de
   uma vez com o slide antigo ainda visível. Slide ATIVO ainda sem .animate-active:
   arma na hora, senão a escala nasce acesa e a passagem da agulha não acende nada. */
.swiper-initialized .swiper-slide:not(.swiper-slide-active) .hero-essencial-section:not(.animate-active) .he-tick {
    stroke-dashoffset: 12;
    stroke: rgba(0, 255, 234, .09);
    transition: stroke-dashoffset 0s linear 560ms,
                stroke 0s linear 560ms;
}

.swiper-initialized .swiper-slide-active .hero-essencial-section:not(.animate-active) .he-tick {
    stroke-dashoffset: 12;
    stroke: rgba(0, 255, 234, .09);
    transition: stroke-dashoffset 0s linear,
                stroke 0s linear;
}

/* O atraso REFERENCIA o token em vez de repetir o numero. Este tick esta no
   progresso ZERO da varredura, entao a formula do comentario acima colapsa para
   exatamente --he-sweep-delay, sem termo de curva nenhum: a linguagem resolve o
   caso sozinha. Escrito como literal, mudar --he-sweep-delay dessincronizava o
   acendimento do marco de partida da agulha em silencio. Um tick futuro em
   progresso != 0 continua exigindo a conta a mao. */
.swiper-initialized .hero-essencial-section.animate-active .he-tick--1 { transition-delay: var(--he-sweep-delay, 300ms); }

/* ---------- fluxo de dados na fibra ----------
   O ÚNICO movimento contínuo do slide, e o único @keyframes. Sem ele o palco morre
   aos 740ms — quando a agulha termina de se dissolver; a cena inteira fecha em
   830ms, no .he-pagamento — e fica imóvel pelos ~7,2s restantes de autoplay
   (delay 8000ms). No celular ele precisa de compensação de escala para cumprir isso
   de verdade, e são TRÊS degraus encadeados lá embaixo, um por tamanho de palco:
   @media (max-width: 991.98px) = tablet, palco 208px;
   @media (max-width: 991.98px) and (max-height: 890px) = celular em retrato, 128px;
   @media (max-width: 575px) and (max-height: 720px) = celular apertado, 96px.
   Cada um vence o anterior na cascata: mexer só no primeiro não chega ao celular.

   POR QUE ELE PODE SER @keyframes SE O RESTO NÃO PODE: o loopFix do Swiper reinicia
   @keyframes, e para uma animação de uma passada isso é o tique nervoso. Aqui o
   reinício é INVISÍVEL — e a proteção que garante isso é o `animation-play-state:
   paused` da regra base logo abaixo: fora do slide ativo o grupo está congelado, e
   qualquer loopFix acontece com o desenho fora de cena. NÃO REMOVER ESSE PAUSE.

   A simetria de 45 graus é a SEGUNDA rede, não a primeira, e o comentário anterior
   estava errado sobre ela: um reinício de @keyframes NÃO pula um múltiplo de 45
   graus. Ele devolve o transform para rotate(0deg) a partir do ângulo em que
   estava, e a simetria só reduz esse salto MÓDULO 45 graus — o deslocamento
   visível pode chegar a 22,5 graus, que em raio 112 são ~44 unidades de arco.
   Com o pause, ninguém vê. Sem o pause, vê.

   A CONTA QUE SUSTENTA A SIMETRIA — não mexer no raio sem refazer:
     2*PI*112 = 703,7168 ; 703,7168 / 8 = 87,9646 ; traço 18 + vão 69.965
   Se traço + vão deixar de dividir a circunferência em 8 partes exatas, aparece um
   vão desigual na emenda e o truque acima desmorona. */
.he-fluxo__linha {
    fill: none;
    stroke: var(--vn-neon-blue, #00ffea);
    stroke-width: 2;
    stroke-linecap: round;
    stroke-dasharray: 18 69.965;
    opacity: .30;
}

.he-fluxo {
    /* view-box: o centro de rotação é o centro do viewBox (160,160), que é o centro
       do anel, e não depende da bbox do traço. */
    transform-box: view-box;
    transform-origin: 160px 160px;
    /* 24s para a volta inteira = um pulso cruzando um ponto fixo a cada 3s. Calmo de
       propósito: isto é respiração de fundo, não chamariz. */
    animation: heFluxo 24s linear infinite;
    /* Fora do slide ativo não pinta nada. O carrossel tem 7 slides; sem isto o desenho
       giraria nos 6 em que ninguém está olhando. */
    animation-play-state: paused;
}

/* SEM o prefixo .swiper-initialized, de propósito: o carrossel de fallback
   (setupFallbackCarousel, em wrapper/js/script.js) também põe .animate-active, então
   o fluxo funciona nos dois caminhos. Se o JS falhar por completo o anel pontilhado
   fica parado — decoração inofensiva, não um buraco.

   NÃO HÁ `will-change: transform` AQUI, e a ausência é deliberada. Havia, com um
   comentário afirmando que aquilo era "a promoção real" de camada. Não era: elemento
   interno de SVG é rasterizado no layer do próprio <svg> e não ganha camada de
   composição própria por will-change no Chrome — a declaração comprava um hint de
   memória e nada mais. É a MESMA crença que o template já tinha corrigido para o
   <g> (o <g> existe pelo centro de rotação, não por composição); ela só havia
   migrado do template para cá.
   O custo real de não promover: a rotação do <g> repinta a região do desenho a cada
   quadro. A 15 graus por segundo, com stroke-width 2 e opacity .30 num palco de
   400x400, isso é desprezível e não justifica manter uma afirmação de performance
   sem medição dentro de um arquivo cuja autoridade vem de tudo estar conferido. */
.hero-essencial-section.animate-active .he-fluxo {
    animation-play-state: running;
}

@keyframes heFluxo {
    from { transform: rotate(0deg); }
    to   { transform: rotate(360deg); }
}

/* ---------- núcleo: onde o número mora ---------- */
.he-nucleo {
    position: relative;
    width: 74%;
    display: grid;
    justify-items: center;
    align-content: center;
    text-align: center;
    line-height: 1;
}

/* Família, peso, letter-spacing e gradiente vêm do seletor que este elemento
   compartilha com .he-titulo__eco (bloco do título, lá em cima). Aqui fica SÓ o que
   é do núcleo — se voltar a duplicar as declarações, os dois divergem de novo. */
.he-nucleo__num {
    font-variant-numeric: tabular-nums;
    /* fallback para navegador sem container queries */
    font-size: clamp(2.6rem, 7vw, 6.5rem);
    /* 26% da largura do palco: proporcional, nunca estoura o anel */
    font-size: 26cqw;
}

.he-nucleo__un {
    font-family: var(--he-font-display);
    font-size: clamp(.72rem, 5.2cqw, 1.3rem);
    font-weight: 700;
    letter-spacing: .22em;
    text-transform: uppercase;
    /* o mesmo cinza do .tv-features-list li (hero_tv/css/style-restored.css) */
    color: #d1d9e0;
    /* compensa o letter-spacing sobrando à direita da última letra */
    margin-left: .22em;
    margin-top: .35em;
}

/* ==========================================================================
   Entrada — mesma mecânica dos outros slides (.animate-active)
   ==========================================================================
   Uma passada só, curta, que termina em .he-acao (preço + botões): o olho desce
   pela oferta e para no valor. Nada aqui repete nem entra em loop.

   Por que o prefixo .swiper-initialized: `opacity: 0` de base é perigoso — se
   .animate-active nunca chegasse, a oferta inteira sumiria. Prefixando, o
   estado oculto só existe depois que o Swiper provou que bootou; sem Swiper o
   conteúdo simplesmente aparece estático. */

/* Eixo LATERAL, como .hero-streaming-section (478-493), #tv-assinatura (446-466) e
   .hero-gamer-section (439-456): texto vem da esquerda, coluna visual vem da direita.
   Este slide entrava na vertical (translate: 0 10px) e o palco com scale .97 — num
   carrossel que avança sozinho, o eixo de entrada é lido como assinatura do produto,
   e um slide subindo no meio de cinco que deslizam de lado lê como componente de
   outro site. O `overflow-x: clip` da seção contém os 50px. */
.swiper-initialized .hero-essencial-section .he-eyebrow,
.swiper-initialized .hero-essencial-section .he-titulo,
.swiper-initialized .hero-essencial-section .he-lede,
.swiper-initialized .hero-essencial-section .he-item,
.swiper-initialized .hero-essencial-section .he-acao,
.swiper-initialized .hero-essencial-section .he-pagamento {
    opacity: 0;
    transform: translateX(-50px);
    transition: opacity 420ms var(--he-ease-entrada, cubic-bezier(0.25, 1, 0.5, 1)),
                transform 420ms var(--he-ease-entrada, cubic-bezier(0.25, 1, 0.5, 1));
}

.swiper-initialized .hero-essencial-section .he-palco {
    opacity: 0;
    transform: translateX(50px);
    transition: opacity 520ms var(--he-ease-entrada, cubic-bezier(0.25, 1, 0.5, 1)),
                transform 520ms var(--he-ease-entrada, cubic-bezier(0.25, 1, 0.5, 1));
}

.swiper-initialized .hero-essencial-section.animate-active .he-eyebrow,
.swiper-initialized .hero-essencial-section.animate-active .he-titulo,
.swiper-initialized .hero-essencial-section.animate-active .he-lede,
.swiper-initialized .hero-essencial-section.animate-active .he-item,
.swiper-initialized .hero-essencial-section.animate-active .he-acao,
.swiper-initialized .hero-essencial-section.animate-active .he-pagamento {
    opacity: 1;
    transform: none;
}

.swiper-initialized .hero-essencial-section.animate-active .he-palco {
    opacity: 1;
    transform: none;
}

/* O palco assenta primeiro (é o fato), o texto desce, e a última coisa a
   chegar é a faixa de preço e botões. ~410ms de atraso máximo + 420ms de
   duração: acaba em ~830ms.
   O DESENHO FECHA ANTES, de propósito: o anel em 720ms (--he-sweep-delay 300 +
   --he-sweep 420) e a agulha se dissolvendo até 740ms, dentro da cadeia e não
   depois dela. Era 1220ms/1260ms, ou seja, o último movimento da cena acontecia
   longe do CTA e na cor do CTA. Se mexer em --he-sweep ou em --he-sweep-delay,
   confira estes números de novo. */
.swiper-initialized .hero-essencial-section.animate-active .he-palco            { transition-delay:  40ms; }
.swiper-initialized .hero-essencial-section.animate-active .he-titulo           { transition-delay:  60ms; }
.swiper-initialized .hero-essencial-section.animate-active .he-lede             { transition-delay: 120ms; }
.swiper-initialized .hero-essencial-section.animate-active .he-item:nth-child(1){ transition-delay: 180ms; }
.swiper-initialized .hero-essencial-section.animate-active .he-item:nth-child(2){ transition-delay: 225ms; }
.swiper-initialized .hero-essencial-section.animate-active .he-item:nth-child(3){ transition-delay: 270ms; }
.swiper-initialized .hero-essencial-section.animate-active .he-acao             { transition-delay: 340ms; }
.swiper-initialized .hero-essencial-section.animate-active .he-pagamento        { transition-delay: 410ms; }

/* ==========================================================================
   Responsivo
   ========================================================================== */

/* A ÚNICA faixa em que os 50px da entrada do palco não cabem, e o número é MEDIDO,
   não estimado. Com .hero-essencial-content recortando o eixo horizontal (ver o
   comentário daquele bloco), o que sobra à direita do palco depende da largura do
   .container do Bootstrap:
     992-1199px  -> container 960px  -> folga em repouso 34px   (50px NÃO cabem)
     1200-1399px -> container 1140px -> folga em repouso ~79px  (cabem)
     >=1400px    -> container 1320px -> folga maior ainda       (cabem)
   Sem esta regra, nesta faixa o primeiro quadro da entrada recortava ~16px do
   palco — o suficiente para comer a ponta do tick das 3h. 32px deixa 2px de folga
   medidos e a diferença de percurso (32 contra 50) não se lê a olho nu.
   Se o palco, o gutter (g-lg-5) ou o container mudarem, meça de novo: é
   `content.getBoundingClientRect().right - palco.getBoundingClientRect().right`
   com o translate da entrada aplicado. */
@media (min-width: 992px) and (max-width: 1199.98px) {
    .swiper-initialized .hero-essencial-section .he-palco {
        transform: translateX(32px);
    }
}

/* Abaixo de 992px o Bootstrap empilha as colunas. A altura do carrossel no
   mobile é 100svh menos o header, então o palco encolhe primeiro. */
@media (max-width: 991.98px) {
    /* EIXO CENTRAL, como nos cinco vizinhos (wrapper l.683 #hero-streaming,
       l.797 #tv-assinatura, l.880 .hero-gamer-section, mais quem_somos e
       trabalhe_conosco). Empilhado, o texto a esquerda com o palco centrado
       acima criava dois eixos concorrentes na tela onde esta a maior parte
       do trafego -- e era a unica peca do carrossel fora do padrao.
       A LISTA e a excecao deliberada: o bloco centraliza, mas o texto dentro
       dela continua a esquerda, senao os tres pares icone+texto perdem o
       alinhamento entre si e viram um zigue-zague. */
    .hero-essencial-section {
        text-align: center;
    }

    .he-eyebrow,
    .he-acao,
    .he-botoes,
    .he-pagamento {
        justify-content: center;
    }

    .he-titulo {
        align-items: center;
        justify-content: center;
    }

    .he-lede {
        margin-inline: auto;
    }

    .he-lista {
        width: fit-content;
        margin-inline: auto;
        text-align: left;
    }

    .hero-essencial-section {
        align-items: flex-start;

        /* O RESERVO DA PAGINAÇÃO MORA AQUI, NA SEÇÃO — e não no
           .hero-essencial-content, que é onde ele estava.
           Lá ele ficava DENTRO do container de rolagem: assim que o conteúdo
           estourava, o próprio reservo rolava para fora junto e a última linha
           encostava no fundo, POR BAIXO dos bullets — que são alvos clicáveis de
           32px de altura. MEDIDO em 390x844, em carga limpa: .hero-essencial-content
           com clientHeight 780 e scrollHeight 811, .he-pagamento ocupando 787-842 e
           a .swiper-pagination ocupando 811-843, ou seja, 31px de sobreposição entre
           texto e controle interativo — exatamente o bug que este bloco declarava
           resolvido.
           Na SEÇÃO ele funciona porque a seção é o container flex e
           .hero-essencial-content tem `max-height: 100%`: esse 100% passa a resolver
           contra a caixa de CONTEÚDO da seção, então o scroller TERMINA acima da
           faixa dos bullets em vez de terminar embaixo dela.
           É literalmente o que o wrapper já faz para os cinco vizinhos
           (wrapper/css/style.css, o `padding` do bloco mobile do carrossel) — por
           isso os fundos de conteúdo deles medem 541, 592 e 732 contra os 811 da
           paginação. Replicado aqui em vez de acrescentar este slide àquele seletor
           porque aquela regra traz junto `overflow: visible !important`, que
           anularia a rolagem de emergência declarada lá em cima.
           O ::before do wrapper não se importa: `inset: 0` num filho absoluto
           resolve contra a caixa de PADDING, então ele continua cobrindo a seção
           inteira. */
        padding-bottom: calc(42px + env(safe-area-inset-bottom, 0px));
    }

    .hero-essencial-content {
        /* Só o respiro de topo. O reservo de baixo subiu para a seção — ver o
           comentário logo acima; ele NÃO pode voltar para dentro do scroller. */
        padding-block: clamp(10px, 1.8svh, 18px) 0;
    }

    .he-col-visual {
        order: -1;
    }

    .he-palco {
        width: min(208px, 52vw);
    }

    .he-acao {
        gap: .9rem;
    }

    .he-botoes {
        width: 100%;
    }

    .he-btn {
        flex: 1 1 auto;
        justify-content: center;
    }

    /* O FLUXO PRECISA COMPENSAR A ESCALA DO PALCO, senão o propósito declarado lá em
       cima ("o palco não morre nos ~7s restantes do autoplay") não se cumpre
       justamente na tela onde está a maior parte do tráfego.
       ATENÇÃO: ESTE DEGRAU VALE PARA O PALCO DE 208px — ou seja, TABLET. Celular em
       retrato NÃO recebe 208px: o bloco @media (max-width: 991.98px) and
       (max-height: 890px) lá embaixo vem depois na cascata e derruba o palco para
       128px, e é lá que a compensação do celular mora. Não calibre o celular aqui.
       A conta: num palco de
       208px o raio 112 do viewBox 320 vira ~73px, circunferência ~457px; a 24s por
       volta os pulsos andam a ~19px/s, e o stroke-width 2 vira ~1,3px CSS a 30% de
       alfa. Movimento de 19px/s num traço de 1,3px a .30 não é lido como movimento:
       é um anel pontilhado parado. Com 15s e os valores abaixo são ~30px/s e ~1,7px,
       que é a leitura que o desktop já entrega — mesmo desenho, mesma respiração.
       DUAS AMARRAS DO PRÓPRIO ARQUIVO: (a) não tocar no raio 112 nem no
       stroke-dasharray 18 69.965 — a divisão exata da circunferência em 8
       (703,7168 / 8 = 87,9646) é o que esconde o salto do loopFix, e mexer só em
       duração, alfa e espessura preserva a conta; (b) não precisa de desligamento
       novo em prefers-reduced-motion: o bloco do fim já zera com `animation: none`,
       que reseta a duração junto. */
    .he-fluxo__linha {
        opacity: .42;
        stroke-width: 2.6;
    }

    .he-fluxo {
        animation-duration: 15s;
    }

    /* MESMA compensacao de escala do fluxo, pelo mesmo motivo: o palco cai de
       400px para min(208px, 52vw), entao o stroke-width 2 renderiza ~1,3px CSS
       e o marco de origem some justo no quadro em que a aura da agulha passa
       por cima dele. Engrossar devolve a leitura sem mexer no desktop. */
    .he-tick {
        stroke-width: 3.6;
    }
}

/* ---------- o fundo deste slide no celular ----------
   Abaixo de 768px o ::before compartilhado do wrapper é desligado para TODOS os
   slides (wrapper/css/style.css, @media max-width:768px que dá `display: none` aos
   ::before do carrossel). Os cinco vizinhos sobrevivem a isso porque têm camada
   fotográfica de mobile registrada no wrapper (tv_mobile, gamer_mobile,
   quem_somos_ai_mobile, trabalhe_conosco_mobile_v2) ou carregada no próprio slide
   (#bg-default do streaming). Este ficava azul-marinho chapado — a última diferença
   visível entre o slide novo e os antigos, e justamente onde está a maior parte do
   tráfego.

   NÃO existe essencial_mobile.* em static/img/hero/master_carousel/, e a decisão
   aqui é ASSUMIDA em vez de deixada em aberto: este é o slide GRÁFICO do
   carrossel. O assunto dele é o anel com o número; uma fotografia por baixo
   competiria com o desenho e ainda custaria contraste. Em vez de foto entra a
   própria malha de 40px do wrapper (mesmo bloco ::before citado acima), mesma
   geometria e mesma cor, só que mais discreta: as linhas do ::before ficam em
   .03 x .45 = .0135 efetivo, e aqui são .014 direto, sem camada nova.

   A VINHETA É VERTICAL, NÃO RADIAL, e isso é alinhamento com os vizinhos: os quatro
   slides fotográficos do wrapper escurecem de CIMA PARA BAIXO
   (linear-gradient(180deg, rgba(5,11,20,.75) 0%, rgba(5,11,20,.95) 100%)); aqui era
   uma vinheta radial centrada, ou seja, a curva de luminosidade do celular era a
   única diferente das outras quatro.
   Os ALFAS não podem ser os .75/.95 dos vizinhos, e a razão é geométrica, não de
   gosto: lá aquele par é um véu SOBRE UMA FOTO — é a foto que cria o degradê. Aqui
   não há foto, e o véu é da MESMA cor da base (#050b14): por cima da malha ele a
   apagaria (alfa efetivo .014 x .15 ≈ .002 no rodapé), e por baixo dela seria
   literalmente um no-op (#050b14 sobre #050b14). O que se importa dos vizinhos é a
   DIREÇÃO e a cor; o alfa é o que faz a malha sobreviver. O halo ciano continua na
   camada de cima e não é tocado pela vinheta.

   O recorte é `max-width: 768px` e não 767.98 DE PROPÓSITO: é exatamente o mesmo
   valor da media query do wrapper que desliga o ::before. Se os dois divergirem,
   sobra uma faixa com as DUAS malhas — o bug que este slide já teve. */
@media (max-width: 768px) {
    .hero-essencial-section {
        background:
            radial-gradient(120% 90% at 78% 40%, rgba(0, 255, 234, .10) 0%, rgba(5, 11, 20, 0) 62%),
            linear-gradient(180deg, rgba(5, 11, 20, 0) 0%, rgba(5, 11, 20, .45) 100%),
            linear-gradient(90deg, rgba(0, 255, 234, .014) 1px, transparent 1px),
            linear-gradient(rgba(0, 255, 234, .014) 1px, transparent 1px),
            #050b14;
        background-size: 100% 100%, 100% 100%, 40px 40px, 40px 40px, 100% 100%;
    }
}

@media (max-width: 575.98px) {
    .he-palco {
        width: min(172px, 46vw);
    }

    /* A lede NÃO some mais. Era `display: none`, que tira o texto também da
       árvore de acessibilidade — e ela é a única frase que explica a oferta.
       Sem ela, o mobile ficava com três rótulos secos, preço e botões: zero
       explicação para a maior parte do tráfego. Teto de linhas resolve o
       espaço sem apagar o conteúdo. */
    .he-lede {
        display: -webkit-box;
        -webkit-line-clamp: 3;
        line-clamp: 3;
        -webkit-box-orient: vertical;
        overflow: hidden;
        margin-bottom: .9rem;
    }

    /* única economia que não custa argumento */
    .he-item__txt small {
        display: none;
    }
}

/* ==========================================================================
   TELAS CURTAS — o recorte que a casa já usa, e que faltava só neste slide
   ==========================================================================
   O wrapper declara `@media (max-width: 575px) and (max-height: 720px)`
   (wrapper/css/style.css) e ali os CINCO vizinhos encolhem o visual e soltam o
   conteúdo secundário: .streaming-card cai para 58x78, .streaming-logo para 44px,
   .tv-solar-system para scale(.38), e somem #hero-streaming .lead,
   .streaming-desc-container, .trabalhe-feature-grid, .tv-features-list,
   .gamer-description e .quem-somos-section .feature-list.
   Este slide não estava naquela lista NEM tinha bloco equivalente aqui: ia de
   `max-width: 575.98px` direto para `max-height: 620px`. Entre 621 e 780px de
   altura ele era o ÚNICO slide do carrossel com o palco inteiro em pé — e essa é a
   faixa dos celulares comuns, porque com a barra do navegador na tela o 100svh de
   um 390x844 fica perto de 745px, não os 844 do devtools sem cromo.

   E NÃO ERA SÓ DIVERGÊNCIA DE ESTILO: era conteúdo fora da dobra. Medido em carga
   limpa, ANTES desta correção:
     375x667 -> seção 603px, conteúdo 794px, 191px de estouro. O CTA primário
                nascia em y=652, com 15 dos seus 48px visíveis; o CTA secundário e a
                linha de pagamento ficavam 0% visíveis.
     360x640 -> 211px de estouro, CTA primário inteiramente fora.
     320x658 -> 175px de estouro.
     390x844 -> 31px de estouro (o aparelho de referência), com a linha de formas de
                pagamento caindo por baixo dos bullets da paginação.
   A rolagem de emergência mantinha tudo alcançável (1.4.4 e 1.4.10 seguiam
   passando), mas ela é indescobrível no toque — `scrollbar-width: thin` não desenha
   nada no celular — e o autoplay troca de slide em 8s. O visitante vinha do
   Streaming, onde tudo coube, e no Essencial tinha que rolar para ver o preço.

   O PALCO ENCOLHE, NÃO SAI DE CENA. A ordem 2 é que a animação more NO DESENHO, e o
   desenho é o palco: apagá-lo aqui apagaria a animação inteira numa faixa em que um
   selo cabe com folga — e encolher o visual é exatamente o que os cinco vizinhos
   fazem nesta mesma faixa. Quem sai é a linha de formas de pagamento, o análogo das
   listas secundárias que eles derrubam.

   ESTES DOIS BLOCOS VÊM ANTES do `max-height: 620px` de propósito: lá embaixo o
   palco não cabe de jeito nenhum, e a regra que o tira de cena precisa continuar
   vencendo — o que só acontece se ela vier depois.
   Tudo aqui é propriedade estática: nada acrescenta movimento, então o bloco de
   prefers-reduced-motion não precisa de linha nova (é a regra do cabeçalho dele).

   O ORÇAMENTO, para quem for mexer: em 375x667 estes dois blocos devolvem ~230px
   (palco 172->104 = 68; ícones da lista 28,8; gaps e margens da lista 13,6; eyebrow
   10,4; título 3,2; margem da lede 5,6; lede de 3 para 2 linhas ~19; .he-acao 8;
   valor do preço 5,6; linha de pagamento 67,7) contra os 191px de estouro medidos.
   Revalidar com `c.scrollHeight - c.clientHeight === 0` em 375x667 e 360x640. */

/* Faixa larga: vale em toda a região em que o Bootstrap já empilhou as colunas.
   Só encolhe caixa e respiro — nenhuma decisão de conteúdo é tocada aqui.

   POR QUE O TETO É 890px E NÃO OS 780 QUE A PRIMEIRA MEDIÇÃO SUGERIA: a altura da
   media query é a do VIEWPORT, mas quem tem que caber é a SEÇÃO, que vale 100svh
   menos o header menos os 42px reservados para os bullets. O header mobile mede
   73px em produção — o token do wrapper foi corrigido de 64 para esse valor. Medido
   no ar em 390px de largura, o empilhamento em tamanho cheio mede 769px de
   conteúdo, logo o layout inteiro só cabe a partir de 769 + 42 + 73 = 884px de
   viewport, arredondado para 890 com folga para o nome e a lede, que vêm do banco e
   podem crescer.
   O TETO ERA 880 E DEIXAVA UMA BANDA DESCOBERTA: entre 881 e 887px de viewport o
   portão já tinha soltado e o conteúdo ainda não cabia — medido em 390x882,
   clientHeight 767 contra scrollHeight 773, 6px de estouro. Em 390x890 fecha em
   zero. Os 890 vêm dessa medição, e batem com a conta de 884 acima.
   Na prática: todo celular em retrato entra aqui, e é isso mesmo — este slide tem
   uma lista de três itens, preço e DOIS CTAs, que é mais conteúdo do que qualquer
   vizinho empilha. Um tablet em retrato (768x1024) NÃO entra: 1024 > 890. */
@media (max-width: 991.98px) and (max-height: 890px) {
    .he-palco {
        width: min(128px, 34vw);
    }

    /* SEGUNDA COMPENSAÇÃO DE ESCALA, e é ESTA que o celular real recebe.
       A calibração do bloco @media (max-width: 991.98px) lá em cima foi feita para
       um palco de 208px, que é o que um TABLET recebe. Esta media query vem depois
       na cascata e derruba o palco para min(128px, 34vw) — escala 128/320 = 0,4.
       Sem recompensar aqui, o que a TELA PINTA é stroke 2.6 x 0,4 = 1,04px CSS e
       2π·(112·0,4)/15s = 18,8px/s: abaixo do piso que o próprio comentário lá de
       cima declarou insuficiente ("não é lido como movimento: é um anel pontilhado
       parado"), justamente na tela onde está a maior parte do tráfego. O propósito
       escrito do fluxo — o palco não morrer nos ~7,2s restantes do autoplay — não
       se cumpria no celular.
       A CONTA, na escala 0,4:
         fluxo  4.2 x 0,4 = 1,68px CSS                      (meta ~1,7)
         fluxo  2π·(112·0,4) = 281,5px / 9,5s = 29,6px/s     (meta ~30)
         tick   5.8 x 0,4 = 2,32px CSS — o mesmo pixel que os 3.6 entregavam no
                palco de 208px, e o que tira o marco de origem de baixo da aura da
                agulha (20 unidades = 8px CSS aqui).
       AS DUAS AMARRAS DO ARQUIVO CONTINUAM VALENDO: (a) raio 112 e stroke-dasharray
       18 69.965 INTOCADOS — a divisão exata da circunferência em 8
       (703,7168 / 8 = 87,9646) é o que esconde o salto do loopFix, e mexer só em
       duração, alfa e espessura preserva a conta; (b) nada a acrescentar em
       prefers-reduced-motion: o `animation: none` do fim do arquivo reseta a
       duração junto e o tick já é zerado lá.
       COMO MEDIR SEM SE ENGANAR: getComputedStyle devolve a UNIDADE DE USUÁRIO do
       SVG (4.2 / 5.8), não o pixel pintado. Multiplique por
       palco.getBoundingClientRect().width / 320. */
    .he-fluxo__linha {
        stroke-width: 4.2;
        opacity: .5;
    }

    .he-fluxo {
        animation-duration: 9.5s;
    }

    .he-tick {
        stroke-width: 5.8;
    }

    /* PORTÃO DA LINHA DE PAGAMENTO — ela é REFORÇO da oferta, não a oferta, o
       análogo exato das listas secundárias que os cinco vizinhos derrubam nas
       faixas apertadas. Estava travada em `max-width: 575.98px`, e a faixa
       576-991px de largura com altura ~700-880px também não a comporta: medido em
       600x760 com o slide ativo, a linha ocupava y 692-748 enquanto a caixa visível
       de .hero-essencial-content terminava em 719 — 27 dos 56px pintados, o resto
       só na rolagem invisível. Aqui ela sai na MESMA condição do resto do aperto,
       cobrindo a faixa inteira em que o Bootstrap já empilhou as colunas. Acima de
       991.98px o layout volta a duas colunas e o problema não existe. */
    .he-pagamento {
        display: none;
    }

    .he-eyebrow {
        padding: .3rem .75rem;
        margin-bottom: .45rem;
    }

    .he-titulo {
        margin-bottom: .4rem;
    }

    .he-lede {
        margin-bottom: .55rem;
    }

    .he-lista {
        gap: .3rem;
        margin-bottom: .75rem;
    }

    .he-item__icone {
        width: 1.75rem;
        height: 1.75rem;
    }

    .he-item__icone i {
        font-size: 1rem;
    }

    .he-acao {
        gap: .7rem;
        padding-top: .5rem;
    }
}

/* Recorte LITERAL do wrapper — `575px` e `720px`, sem o .98. Se os dois valores
   divergirem sobra uma faixa fracionária em que os vizinhos já encolheram e este
   slide não; é o mesmo argumento que o bloco de `max-width: 768px` lá em cima já
   faz para a malha de fundo. */
@media (max-width: 575px) and (max-height: 720px) {
    /* 96px e não 104: com 104 sobravam 5px de estouro medidos em 360x640, o aparelho
       mais apertado desta faixa. Continua MAIOR que o visual dos vizinhos no mesmo
       recorte (.streaming-card 58x78, .streaming-logo 44px), e o número do núcleo,
       que é 26cqw, fica em ~25px — legível. */
    .he-palco {
        width: min(96px, 26vw);
    }

    /* TERCEIRA COMPENSAÇÃO DE ESCALA, pelo mesmo motivo do bloco de 890px: aqui o
       palco cai de 128 para 96, escala 96/320 = 0,3, e os valores de lá voltariam a
       1,26px e 22px/s. A conta nesta escala:
         fluxo  5.6 x 0,3 = 1,68px CSS
         fluxo  2π·(112·0,3) = 211,1px / 7s = 30,2px/s
         tick   7.8 x 0,3 = 2,34px CSS
       Mesmas duas amarras: raio 112 e dasharray 18 69.965 intocados; nada novo em
       prefers-reduced-motion. A opacidade .5 vem do bloco de 890px e não muda. */
    .he-fluxo__linha {
        stroke-width: 5.6;
    }

    .he-fluxo {
        animation-duration: 7s;
    }

    .he-tick {
        stroke-width: 7.8;
    }

    /* O último aperto, e ele tem destino declarado: sem estas quatro linhas o
       conteúdo fecha em 375x667 com folga ZERO — cabe, mas qualquer crescimento do
       nome ou da lede, que vêm do banco e são editáveis no admin, devolve o estouro.
       Com elas sobram ~9px medidos. Nenhuma toca em alvo de toque: os .he-btn
       continuam com min-height 48px (restrição 7). */
    .he-eyebrow {
        margin-bottom: .35rem;
    }

    .he-lista {
        margin-bottom: .5rem;
    }

    .he-acao {
        gap: .55rem;
    }

    .he-botoes {
        gap: .4rem;
    }

    /* Continua SEM `display: none`. O teto de linhas cai de 3 para 2 e a única frase
       que explica a oferta permanece na árvore de acessibilidade — a razão está por
       extenso no bloco de 575.98px logo acima, e ela não muda aqui. */
    .he-lede {
        -webkit-line-clamp: 2;
        line-clamp: 2;
    }

    .he-preco__valor strong {
        font-size: clamp(1.9rem, 8vw, 2.4rem);
    }

    /* A linha de pagamento SAÍA daqui. Subiu inteira para o portão de 890px acima,
       que cobre esta faixa toda (575 < 991,98 e 720 < 890) e ainda alcança a faixa
       576-991px de largura que este recorte não pegava. Uma fonte só de verdade. */
}

/* Tela baixa e deitada: o palco sai de cena para o conteúdo não cortar.
   Como o número mora NO palco, ele precisa voltar para o título aqui —
   senão a velocidade desaparece do slide inteiro. */
@media (max-height: 620px) and (max-width: 991.98px) {
    .he-col-visual {
        display: none;
    }

    .he-titulo__eco {
        display: block;
        font-size: clamp(2.2rem, 9vw, 3.2rem);
        line-height: 1;
    }

    .he-nome {
        font-size: clamp(1.05rem, 4vw, 1.35rem);
        margin-top: .2rem;
    }
}

/* ==========================================================================
   CELULAR DEITADO — o corte fechado por ALTURA, sem exigir largura de celular
   ==========================================================================
   TODOS os cortes de conteúdo acima estão travados por LARGURA (`575.98px` e
   `575px`), e nenhum deles casa com um celular em paisagem: 844x390 tem 844 de
   LARGURA. O único bloco que disparava ali era o de `max-height: 620px`, que apenas
   tira o palco de cena. Resultado medido em produção, carga limpa, em 844x390 com o
   slide ativo: .hero-essencial-content com clientHeight 276 contra scrollHeight 491
   — 215px de estouro, os DOIS .he-btn com 0 de 48px visíveis dentro da seção e o
   preço fora da dobra. No mesmo viewport, Streaming entrega 44/44 e Gamer 42/42 de
   CTA visível. A rolagem de emergência mantinha 1.4.10 passando, mas ela é
   indescobrível no toque e o autoplay troca o slide em 8s.

   ORÇAMENTO DOS 215px: a linha de pagamento (~69) já sai no portão de 890px, que
   agora vale para toda a faixa empilhada e alcança esta altura. Aqui entram os
   smalls da lista (~57), a lede de 3 para 2 linhas (~21), os gaps da lista (~10), o
   respiro do .he-acao (~10), o eco de 51px para 32px (~19) e o eyebrow (~30).

   POR QUE DEPOIS DO BLOCO DE 620px E NÃO ANTES: a única regra em conflito é o
   font-size do .he-titulo__eco, e é justamente ela que precisa VENCER — colocado
   antes, o eco continuaria em 51px e o orçamento não fecharia. O resto não colide:
   o `display: none` do .he-col-visual e o `display: block` do eco vêm de lá e
   continuam valendo.
   Tudo aqui é propriedade estática: nada acrescenta movimento, então o bloco de
   prefers-reduced-motion não precisa de linha nova (é a regra do cabeçalho dele).
   Revalidar com `c.scrollHeight - c.clientHeight === 0` em 844x390 e 667x375, e com
   os dois .he-btn de volta a 48/48 contra o retângulo de .hero-essencial-content. */
@media (max-width: 991.98px) and (max-height: 500px) {
    /* O EYEBROW FICA. A prescrição original mandava derrubá-lo, e o orçamento que a
       justificava vinha de uma medição com o layout do Swiper desatualizado. Medido
       de novo com carga limpa: as economias abaixo já fecham a conta com folga
       (94px sobrando em 667x375), e o selo é vocabulário compartilhado com os cinco
       vizinhos — apagá-lo aqui custaria consistência sem comprar altura nenhuma.
       Só o respiro encolhe. */
    .he-eyebrow {
        padding: .25rem .6rem;
        margin-bottom: .35rem;
    }

    /* mesma economia que o recorte de 575.98px já faz: reforço, não a oferta */
    .he-item__txt small {
        display: none;
    }

    /* Continua SEM sumir: só o teto de linhas cai de 3 para 2. A única frase que
       explica a oferta permanece na árvore de acessibilidade — a razão está por
       extenso no bloco de 575.98px, e ela não muda por causa da orientação. */
    .he-lede {
        display: -webkit-box;
        -webkit-line-clamp: 2;
        line-clamp: 2;
        -webkit-box-orient: vertical;
        overflow: hidden;
        margin-bottom: .3rem;
    }

    /* AQUI SOBRA LARGURA E FALTA ALTURA — o oposto do retrato, e é por isso que
       este bloco não é só "encolher fontes". Medido em 844x390 com carga limpa: a
       coluna de texto tem 771px de largura e a caixa visível apenas 276px de altura.
       A lista empilhada gastava 92px de altura para usar 200px de largura; deitada
       ela gasta 28px. `flex-wrap: wrap` em vez de `grid-auto-flow: column` é
       deliberado: numa paisagem estreita (667x375) os três pares quebram para duas
       fileiras em vez de espremer o texto — degrada, não estoura. */
    .he-lista {
        display: flex;
        flex-wrap: wrap;
        justify-content: center;
        column-gap: 1.25rem;
        row-gap: .25rem;
        margin-bottom: .35rem;
    }

    .he-item {
        gap: .5rem;
    }

    .he-item__icone {
        width: 1.5rem;
        height: 1.5rem;
    }

    .he-item__icone i {
        font-size: .85rem;
    }

    /* O bloco de 991.98px dá `width: 100%` ao .he-botoes, o que joga os CTAs para
       uma linha própria abaixo do preço — correto no retrato, caro na paisagem: são
       48px + o gap do .he-acao. Devolvendo a largura automática, preço e CTAs
       partilham a linha do .he-acao, que já é `display: flex; flex-wrap: wrap` na
       base e já está centralizado pelo bloco de 991.98px. Os .he-btn continuam com
       min-height 48px — nenhum alvo de toque encolhe (restrição 7). */
    .he-botoes {
        width: auto;
    }

    .he-acao {
        gap: .5rem .75rem;
        padding-top: .25rem;
    }

    /* O que decide se preco e CTAs cabem na MESMA linha e a largura somada, nao a
       altura: medido em 667x375, preco 198 + gap + botoes 447 dava 661 contra 620 de
       coluna, e o .he-botoes quebrava para baixo, custando os 48px que faltavam.
       Apertar o respiro lateral e o corpo da fonte devolve ~90px de largura e os
       dois cabem. A ALTURA NAO E TOCADA: min-height 48px continua valendo, e o alvo
       de toque segue acima dos 44px da restricao 7. */
    .he-btn {
        padding-inline: .7rem;
        font-size: .8rem;
    }

    .he-titulo {
        margin-bottom: .25rem;
    }

    .he-nome {
        margin-top: 0;
    }

    /* O eco é o número da velocidade quando o palco sai de cena. Encolhe, não some:
       sem ele a velocidade desaparece do slide inteiro nesta orientação. */
    .he-titulo__eco {
        font-size: clamp(1.5rem, 4.5vw, 1.75rem);
    }
}

/* ==========================================================================
   Movimento reduzido — zera TUDO.
   ==========================================================================
   Sob reduce o desenho aparece PRONTO: anel fechado, ticks acesos, agulha fora de
   cena, fluxo parado. Nada pisca, nada gira, e nada some.

   MANTER ESTE BLOCO NO FIM DO ARQUIVO. Os seletores repetem os de cima com a mesma
   especificidade e só vencem por virem depois. Movido para o meio, o
   prefers-reduced-motion deste slide para de funcionar SEM ERRO NENHUM — foi
   exatamente o que aconteceu quando heDesenha e heFlutua sumiram e o bloco que os
   zerava virou letra morta em silêncio.

   REGRA PARA QUEM MEXER: toda animação nova adicionada acima PRECISA ser desligada
   aqui. Acrescentou seletor lá em cima e não acrescentou aqui? Você introduziu uma
   regressão de acessibilidade invisível.
   ========================================================================== */

@media (prefers-reduced-motion: reduce) {

    /* anel e ticks: desenhados, parados, nos TRÊS estados.
       ATENÇÃO À ESPECIFICIDADE: os blocos de armar lá em cima agora são DOIS (slide
       que sai / slide ativo) e valem (0,6,0) e (0,5,0). Repetir aqui só a forma
       antiga (0,4,0) deixaria a regra nova ESCAPAR do desligamento — a regressão
       silenciosa exata que o cabeçalho deste bloco manda evitar. Os seletores abaixo
       são cópia literal daqueles, e vencem por virem depois. */
    .he-medidor__arco,
    .he-medidor__brilho,
    .he-tick,
    .swiper-initialized .swiper-slide:not(.swiper-slide-active) .hero-essencial-section:not(.animate-active) .he-medidor__arco,
    .swiper-initialized .swiper-slide:not(.swiper-slide-active) .hero-essencial-section:not(.animate-active) .he-medidor__brilho,
    .swiper-initialized .swiper-slide:not(.swiper-slide-active) .hero-essencial-section:not(.animate-active) .he-tick,
    .swiper-initialized .swiper-slide-active .hero-essencial-section:not(.animate-active) .he-medidor__arco,
    .swiper-initialized .swiper-slide-active .hero-essencial-section:not(.animate-active) .he-medidor__brilho,
    .swiper-initialized .swiper-slide-active .hero-essencial-section:not(.animate-active) .he-tick {
        stroke-dashoffset: 0;
        transition: none;
    }

    .swiper-initialized .swiper-slide:not(.swiper-slide-active) .hero-essencial-section:not(.animate-active) .he-tick,
    .swiper-initialized .swiper-slide-active .hero-essencial-section:not(.animate-active) .he-tick {
        stroke: rgba(0, 255, 234, .34);
    }

    /* precisa de regra própria: as linhas acima têm especificidade maior que
       .he-tick--1 e apagariam o laranja do zero da escala */
    .swiper-initialized .swiper-slide:not(.swiper-slide-active) .hero-essencial-section:not(.animate-active) .he-tick--1,
    .swiper-initialized .swiper-slide-active .hero-essencial-section:not(.animate-active) .he-tick--1,
    .he-tick--1 {
        stroke: var(--vn-neon-orange, #ff4d1d);
    }

    /* a agulha não existe sem varredura: fora de cena nos três estados */
    .he-cursor,
    .he-cursor__aura,
    .swiper-initialized .hero-essencial-section:not(.animate-active) .he-cursor,
    .swiper-initialized .hero-essencial-section:not(.animate-active) .he-cursor__aura,
    .swiper-initialized .swiper-slide:not(.swiper-slide-active) .hero-essencial-section:not(.animate-active) .he-cursor,
    .swiper-initialized .swiper-slide:not(.swiper-slide-active) .hero-essencial-section:not(.animate-active) .he-cursor__aura,
    .swiper-initialized .swiper-slide-active .hero-essencial-section:not(.animate-active) .he-cursor,
    .swiper-initialized .swiper-slide-active .hero-essencial-section:not(.animate-active) .he-cursor__aura {
        opacity: 0;
        transition: none;
    }

    /* as DUAS regras: sem a segunda, .animate-active reativaria via
       animation-play-state: running o que a primeira acabou de desligar */
    .he-fluxo,
    .hero-essencial-section.animate-active .he-fluxo {
        animation: none;
    }

    /* entrada do conteúdo */
    .swiper-initialized .hero-essencial-section .he-eyebrow,
    .swiper-initialized .hero-essencial-section .he-titulo,
    .swiper-initialized .hero-essencial-section .he-lede,
    .swiper-initialized .hero-essencial-section .he-item,
    .swiper-initialized .hero-essencial-section .he-acao,
    .swiper-initialized .hero-essencial-section .he-pagamento,
    .swiper-initialized .hero-essencial-section .he-palco {
        opacity: 1;
        transform: none;
        transition: none;
    }

    /* NÃO é `transition: none`. Aquilo desligava só a interpolação e deixava as
       regras de hover/focus de .he-btn--primario e --secundario valendo: sob reduce
       o botão passava a dar um PULO INSTANTÂNEO de 3px em vez de subir suavemente,
       e movimento instantâneo continua sendo movimento — mais brusco que o
       original, o oposto do que a restrição 4 pede.
       Aqui o deslocamento morre de verdade, e o fade de cor/fundo sobrevive: ele
       não é movimento e é o que diz ao usuário que o botão respondeu. */
    .he-btn {
        transition: background .3s, border-color .3s, color .3s, box-shadow .3s;
    }

    .he-btn--primario:hover,
    .he-btn--primario:focus-visible,
    .he-btn--secundario:hover,
    .he-btn--secundario:focus-visible {
        transform: none;
    }
}
