/* =========================================================================
   VIRA SOLUÇÕES — design tokens & base styles
   Fonte da filosofia visual: MESTRE2X0.md (Partes V, XVIII, XIX)

   Estrutura atual: SÓ desktop. Toda adaptação para celular foi removida de
   propósito (pedido do cliente) para ser refeita do zero — não existe mais
   nenhum @media de largura/altura de viewport aqui, só os de preferência
   do usuário (prefers-reduced-motion, prefers-contrast, hover/pointer
   etc.), que não são "adaptação mobile" e continuam válidos em qualquer
   tamanho de tela.
   ========================================================================= */

/* ---------- Reset mínimo ---------- */
*, *::before, *::after { box-sizing: border-box; }
html, body, h1, h2, h3, h4, p, figure, blockquote, dl, dd { margin: 0; }
ul[class], ol[class] { list-style: none; margin: 0; padding: 0; }
img, picture, svg { display: block; max-width: 100%; }
input, button, textarea, select { font: inherit; color: inherit; }
a { color: inherit; text-decoration: none; }

/* ---------- Design tokens ---------- */
:root {
  /* Cor — uma cor de ação (gradiente), canvas nunca preto puro (V.1 / V.2) */
  --canvas: #0b0d10;
  --surface-1: #10141b;
  --surface-2: #171c26;
  --border-hair: rgba(255, 255, 255, 0.09);
  --border-strong: rgba(255, 255, 255, 0.18);

  --text-primary: #f5f6f8;
  --text-secondary: #a7adb8;
  --text-tertiary: #6e7580;

  --accent-from: #2d6bff;
  --accent-to: #6c4dff;
  --accent-solid: #4f66ff;
  --gradient-accent: linear-gradient(135deg, var(--accent-from) 0%, var(--accent-to) 100%);
  --shadow-cta: 0 10px 28px rgba(45, 107, 255, 0.32);

  /* Forma — uma filosofia de raio (V.4): pill em controles, 24/16 em cards */
  --radius-pill: 999px;
  --radius-lg: 24px;
  --radius-md: 16px;
  --radius-sm: 10px;

  /* Tipografia */
  --font-display: "Sora", ui-sans-serif, system-ui, -apple-system, sans-serif;
  --font-body: "Inter", ui-sans-serif, system-ui, -apple-system, sans-serif;

  /* Motion — curvas customizadas (XVIII.2), nunca os easings padrão do CSS */
  --ease-out: cubic-bezier(0.23, 1, 0.32, 1);
  --ease-in-out: cubic-bezier(0.77, 0, 0.175, 1);

  --section-py: clamp(4.5rem, 9vw, 8rem);
  --container-w: 1160px;
  --container-px: 40px;
  --header-h: 56px;
}

@media (prefers-color-scheme: light) {
  /* A marca é intencionalmente escura em qualquer esquema — reforça a decisão de V.2 */
  :root { color-scheme: dark; }
}

/* Superfícies mais sólidas quando o usuário pede mais contraste / menos transparência */
@media (prefers-contrast: more) {
  :root {
    --border-hair: rgba(255, 255, 255, 0.35);
    --border-strong: rgba(255, 255, 255, 0.6);
    --text-secondary: #c9cdd4;
  }
}

html {
  color-scheme: dark;
}

body {
  background: var(--canvas);
  color: var(--text-primary);
  font-family: var(--font-body);
  font-size: 1rem;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

/* Rolagem suave apenas para quem não pediu menos movimento */
@media all /* reduzir movimento IGNORADO de propósito (decisão do cliente) */ {
  html { scroll-behavior: smooth; }
}
html { scroll-padding-top: calc(var(--header-h) + 16px); }

/* ---------- Utilidades de layout ---------- */
.container {
  width: 100%;
  max-width: var(--container-w);
  margin-inline: auto;
  /* max(): nunca menos que o padding normal do projeto, mas cresce além
     dele quando há notch/borda arredondada de hardware na lateral
     (celular em paisagem) — protege o conteúdo sem afetar telas sem
     safe area (env() cai pra 0px e max() vira só --container-px). */
  padding-left: max(var(--container-px), env(safe-area-inset-left, 0px));
  padding-right: max(var(--container-px), env(safe-area-inset-right, 0px));
}

.section { padding-block: var(--section-py); position: relative; }

/* Todas as seções abaixo do hero compartilham o mesmo canvas escuro (nunca
   um bloco de cor sólida alternado): o ritmo de rolagem vem de brilhos
   radiais suaves que sangram para dentro das seções vizinhas, o mesmo
   princípio de "disfarçar a transição" já usado no vídeo do hero, em vez
   de um corte reto entre uma faixa clara e uma escura. */
.section-glow { overflow: hidden; }
.section-glow::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
}
.section-glow > .container { position: relative; z-index: 1; }
/* O raio da elipse é dimensionado para que o degradê já chegue a
   "transparent" bem antes de tocar qualquer borda da própria seção (nunca
   dependemos de mask-image para isso: em alguns motores de renderização o
   corte do mask fica visível como uma linha reta, o oposto do que se quer).
   Assim a emenda com a seção vizinha sempre encontra o mesmo canvas liso
   dos dois lados. */
.section-glow--a::before {
  background: radial-gradient(ellipse 35% 40% at 25% 32%, rgba(45, 107, 255, 0.16) 0%, transparent 60%);
}
.section-glow--b::before {
  background: radial-gradient(ellipse 35% 40% at 75% 68%, rgba(108, 77, 255, 0.16) 0%, transparent 60%);
}

.eyebrow {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-family: var(--font-body);
  font-size: 0.75rem;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--text-secondary);
  margin-bottom: 20px;
}
.eyebrow::before,
.eyebrow::after { content: "["; color: var(--accent-solid); }
.eyebrow::after { content: "]"; }

.section-head {
  max-width: 640px;
  margin-bottom: clamp(2.5rem, 5vw, 4rem);
}
.section-head.center { margin-inline: auto; text-align: center; }

h1, h2, h3, h4 {
  font-family: var(--font-display);
  font-weight: 700;
  color: var(--text-primary);
  letter-spacing: -0.01em;
}

h2 {
  font-size: clamp(2rem, 4vw, 2.75rem);
  line-height: 1.15;
  letter-spacing: -0.02em;
}

h3 {
  font-size: 1.25rem;
  font-weight: 600;
  letter-spacing: -0.01em;
}

.lead {
  font-size: clamp(1.0625rem, 1.4vw, 1.1875rem);
  line-height: 1.65;
  color: var(--text-secondary);
}

.visually-hidden {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

.skip-link {
  position: fixed;
  /* viewport-fit=cover deixa a página se estender por baixo do notch/
     barra de gestos — sem isto o link ficaria colado embaixo dessas
     áreas em vez de a 12px da borda visível de fato. Fallback 0px pra
     telas sem safe area nenhuma. */
  top: calc(12px + env(safe-area-inset-top, 0px));
  left: calc(12px + env(safe-area-inset-left, 0px));
  z-index: 100;
  background: var(--text-primary);
  color: var(--canvas);
  padding: 12px 20px;
  border-radius: var(--radius-sm);
  font-weight: 600;
  transform: translateY(-150%);
  transition: transform 180ms var(--ease-out);
}
.skip-link:focus-visible { transform: translateY(0); }

/* ---------- Foco visível consistente (Pilar Acessibilidade) ---------- */
a:focus-visible,
button:focus-visible,
summary:focus-visible,
[tabindex]:focus-visible {
  outline: 2px solid #86a1ff;
  outline-offset: 3px;
  border-radius: 6px;
}

/* ---------- Botões ---------- */
.btn {
  --btn-py: 16px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  font-family: var(--font-body);
  font-weight: 700;
  font-size: 0.9375rem;
  letter-spacing: 0.01em;
  padding: var(--btn-py) 30px;
  border-radius: var(--radius-pill);
  cursor: pointer;
  white-space: nowrap;
  transition: transform 160ms var(--ease-out), box-shadow 160ms var(--ease-out),
              background 160ms var(--ease-out), border-color 160ms var(--ease-out), color 160ms var(--ease-out);
}
.btn:active { transform: scale(0.97); }

/* ---------- Botão magnético + ripple (XX.3 do MESTRE2X0.md): dentro de um
   raio do cursor, o botão é "atraído"; o retorno usa um pequeno spring via
   JS (nunca transition linear, senão perde a sensação física de ímã — ver
   assets/js/main.js). Por isso a transition de `transform` é removida aqui:
   quem controla esse eixo é o JS quadro a quadro, não o CSS. O clique
   dispara um ripple nas coordenadas exatas do toque. */
.btn-magnetic {
  position: relative;
  overflow: hidden;
  transition: box-shadow 160ms var(--ease-out), background 160ms var(--ease-out),
              border-color 160ms var(--ease-out), color 160ms var(--ease-out);
}
.btn-ripple {
  position: absolute;
  border-radius: 50%;
  transform: scale(0);
  opacity: 0.35;
  background: currentColor;
  pointer-events: none;
  animation: btn-ripple-expand 500ms var(--ease-out) forwards;
}
@keyframes btn-ripple-expand {
  to { transform: scale(1); opacity: 0; }
}

.btn-primary {
  background: var(--gradient-accent);
  color: #ffffff;
  box-shadow: var(--shadow-cta);
}
@media (hover: hover) and (pointer: fine) {
  .btn-primary:hover { transform: translateY(-2px); box-shadow: 0 14px 32px rgba(76, 90, 255, 0.42); }
}

.btn-outline {
  background: transparent;
  color: var(--text-primary);
  border: 1px solid var(--border-strong);
}
@media (hover: hover) and (pointer: fine) {
  .btn-outline:hover { background: rgba(255, 255, 255, 0.06); border-color: rgba(255, 255, 255, 0.32); }
}

.btn-sm { --btn-py: 10px; font-size: 0.875rem; padding-inline: 22px; }

.btn-group { display: flex; flex-wrap: wrap; gap: 16px; align-items: center; }

/* ---------- Header / navegação ---------- */
.site-header {
  position: sticky;
  top: 0;
  z-index: 50;
  /* min-height (não height): garante os var(--header-h) inteiros de
     conteúdo mesmo quando env(safe-area-inset-top) soma espaço extra por
     cima num celular com notch/status bar — height fixo com box-sizing:
     border-box (regra global) faria esse espaço "comer" a altura da nav
     em vez de crescer além dela. */
  min-height: var(--header-h);
  padding-top: env(safe-area-inset-top, 0px);
  display: flex;
  align-items: center;
  background: rgba(11, 13, 16, 0.72);
  border-bottom: 1px solid transparent;
  transition: background 200ms var(--ease-out), border-color 200ms var(--ease-out);
}
@supports (backdrop-filter: blur(1px)) {
  .site-header { backdrop-filter: blur(14px) saturate(140%); }
}
.site-header.is-scrolled { border-bottom-color: var(--border-hair); }

@media (prefers-reduced-transparency: reduce) {
  .site-header { background: var(--canvas); backdrop-filter: none; }
}

.nav {
  width: 100%;
  max-width: var(--container-w);
  margin-inline: auto;
  padding-left: max(40px, env(safe-area-inset-left, 0px));
  padding-right: max(40px, env(safe-area-inset-right, 0px));
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
}

.brand {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.125rem;
  color: var(--text-primary);
}

.brand-mark {
  width: 24px;
  height: 17.5px;
  flex-shrink: 0;
  background: var(--gradient-accent);
  -webkit-mask-image: url("../img/v-mark-white.png");
  mask-image: url("../img/v-mark-white.png");
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
  -webkit-mask-size: contain;
  mask-size: contain;
  -webkit-mask-position: center;
  mask-position: center;
}

.nav-links {
  display: flex;
  align-items: center;
  gap: 36px;
}
.nav-links a {
  font-size: 0.9375rem;
  font-weight: 500;
  color: var(--text-secondary);
  transition: color 160ms var(--ease-out);
}
.nav-links a:hover { color: var(--text-primary); }
.nav-links a.is-active { color: var(--text-primary); }

.nav-actions { display: flex; align-items: center; gap: 12px; }
.nav-actions .btn-outline { display: inline-flex; }

/* .nav-toggle/.nav-menu (Parte XXI.11 — versão vertical de celular, ver
   @media max-width:767.98px mais abaixo): por padrão (tablet/desktop),
   o botão não existe visualmente e .nav-menu é "transparente" ao layout —
   display:contents faz nav-links e nav-actions continuarem filhos diretos
   do flex de .nav, exatamente como se a div não existisse. Só vira um
   menu recolhível de verdade dentro do @media de celular. */
.nav-toggle { display: none; }
.nav-menu { display: contents; }

@media not all /* reduzir movimento IGNORADO de propósito (decisão do cliente) */ {
  .site-header, .btn, .nav-links a { transition: none; }
}

/* ---------- Hero ---------- */
.hero {
  position: relative;
  overflow: hidden;
  min-height: calc(100dvh - var(--header-h));
  display: flex;
  align-items: center;
  padding-block: clamp(3rem, 8vw, 5rem);
}

.hero-decor {
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  overflow: hidden;
  /* Dissolve o retângulo do vídeo/vinheta no canvas liso da próxima seção
     em vez de cortar em uma linha reta: os últimos 14% da altura do hero
     esmaecem para transparente, revelando o mesmo canvas escuro por baixo
     de forma gradual. */
  -webkit-mask-image: linear-gradient(to bottom, #000 0%, #000 78%, transparent 100%);
  mask-image: linear-gradient(to bottom, #000 0%, #000 78%, transparent 100%);
}

/* Vídeo de fundo do hero, sob efeito de transparência (V.5: um único
   momento de "material" translúcido, com véu escuro por cima para o
   texto continuar legível em qualquer trecho do vídeo). */
.hero-video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 0.4;
}

/* Dissolve as bordas retangulares do vídeo no canvas escuro do hero: uma
   vinheta radial funde os cantos/laterais, e o degradê vertical escurece
   suavemente até opaco no topo (atrás da nav) e na base (antes da próxima
   seção), disfarçando onde o vídeo "começa e termina". */
.hero-video-scrim {
  position: absolute;
  inset: 0;
  background:
    radial-gradient(ellipse 75% 65% at 50% 50%, transparent 35%, rgba(11, 13, 16, 0.55) 78%, rgba(11, 13, 16, 0.96) 100%),
    linear-gradient(180deg, rgba(11, 13, 16, 0.6) 0%, rgba(11, 13, 16, 0.5) 22%, rgba(11, 13, 16, 0.58) 60%, rgba(11, 13, 16, 0.86) 88%, rgba(11, 13, 16, 0.99) 100%);
}

.hero-glow {
  position: absolute;
  top: 46%;
  left: 50%;
  transform: translate(-50%, -55%);
  width: min(70vw, 900px);
  height: min(70vw, 900px);
  border-radius: 50%;
  background: radial-gradient(circle, rgba(108, 77, 255, 0.22) 0%, rgba(45, 107, 255, 0.10) 42%, transparent 72%);
}

/* Ícone da marca (só o "V", sem o wordmark) como marca d'água atrás do
   conteúdo: pulsar branco suave + tilt 3D reativo ao cursor (XX.2). */
.hero-logo-wrap {
  position: absolute;
  top: 30%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: min(78vw, 900px);
  aspect-ratio: 1594 / 1166;
}

.hero-logo-bg {
  display: block;
  width: 100%;
  height: 100%;
  background-color: #f5f6f8;
  -webkit-mask-image: url("../img/v-mark.svg");
  mask-image: url("../img/v-mark.svg");
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
  -webkit-mask-size: contain;
  mask-size: contain;
  -webkit-mask-position: center;
  mask-position: center;
  opacity: 0.1;
  transform: perspective(900px) rotateX(0deg) rotateY(0deg);
  transition: transform 500ms var(--ease-out);
  animation: hero-logo-pulse 3.2s ease-in-out infinite;
  will-change: transform;
}
.hero-logo-bg.is-tracking { transition: none; }

@keyframes hero-logo-pulse {
  0%, 100% { opacity: 0.09; }
  50% { opacity: 0.26; }
}

@media not all /* reduzir movimento IGNORADO de propósito (decisão do cliente) */ {
  .hero-logo-bg { animation: none; opacity: 0.16; transition: none; }
}

.hero-inner {
  position: relative;
  z-index: 1;
  max-width: 760px;
  margin-inline: auto;
  text-align: center;
}

.hero h1 {
  font-size: clamp(2rem, 5.6vw, 4.5rem);
  line-height: 1.1;
  letter-spacing: -0.01em;
  text-transform: uppercase;
  text-wrap: balance;
}

.hero .lead {
  max-width: 34rem;
  margin-inline: auto;
  margin-top: 24px;
}

.hero .btn-group { margin-top: 40px; justify-content: center; }
.hero .btn-group .btn { text-transform: uppercase; letter-spacing: 0.02em; }

/* ---------- Reveal ao entrar/sair da tela ----------
   Um único sistema para o site inteiro (hero incluído): classe `.reveal`
   com fade + translateY, ligada por IntersectionObserver a `.is-visible`.
   Diferente de um reveal "de uma vez só", aqui a classe é removida quando
   o elemento sai da viewport (pra cima ou pra baixo) — o elemento
   desaparece gradativamente ao sair e reaparece ao voltar, em vez de
   permanecer visível para sempre após a primeira entrada. */
.reveal {
  opacity: 0;
  /* Deslizamento vertical grande e sem escala — o pedido foi literal:
     "deslizando debaixo da página pra cima até o lugar fixo", como se o
     site estivesse sendo montado peça por peça enquanto rola. Escala junto
     com translate lembra mais um "zoom" que um "slide"; só translateY
     vende melhor a sensação de peça subindo até encaixar. Conteúdo de
     rolagem/marketing tolera uma transição mais longa que UI de alta
     frequência (XVIII.2). */
  transform: translateY(72px);
  transition: opacity 800ms var(--ease-out), transform 800ms var(--ease-out);
  transition-delay: calc(var(--i, 0) * 90ms);
}
.reveal.is-visible { opacity: 1; transform: none; }

/* Pedido do cliente: o fade do título de "SEGURANÇA & DESEMPENHO" deve ser
   30% mais lento que o padrão do site (800ms → 1040ms), pra ficar notável.
   Escopado só a este título — mudar a duração de .reveal direto afetaria
   toda transição de entrada/saída do site inteiro. */
#diferenciais .section-head.reveal {
  transition-duration: 1040ms;
}

/* Pedido do cliente: caixa de perguntas do FAQ entra deslizando da esquerda
   pra direita. */
.reveal-left { transform: translateX(-72px); }
.reveal-left.is-visible { transform: none; }

/* ---------- Reveal: variação por canal (desktop / tablet / smartphone) ----------
   Pedido do cliente (30/09/2026): animações de entrada diferentes e
   alternativas por tipo de tela, não a mesma em qualquer lugar. O site já
   tinha 3 faixas de largura bem definidas pra outra finalidade (XXI.11 —
   tablet usa um canvas virtual em `zoom`, mobile usa reflow de verdade),
   então cada faixa ganha seu próprio estilo de entrada em vez de inventar
   novos breakpoints:

   - Desktop (≥1240px, regra base acima, sem media query): o efeito
     original de "montagem" (translateY(72px) puro, 800ms) — já era um
     pedido específico do cliente antes, mantido como está.
   - Tablet (768–1239.98px): fade + leve escala ("zoom-in" suave) em vez
     de deslizamento vertical — mais compacto e tátil, combina com telas
     vistas mais de perto / uso por toque, e não compete visualmente com
     o próprio zoom do canvas virtual que já escala esta faixa.
   - Smartphone (≤767.98px): deslocamento pequeno e transição mais curta
     (420ms vs. 800ms, delay em cascata reduzido) — não é o efeito "não
     caber" na tela, é reconhecer que em tela pequena + rolagem rápida de
     polegar uma transição de 800ms lê como lentidão, não elegância
     (mesmo raciocínio de XVIII/XIX do MESTRE2X0.md pra dispositivos mais
     restritos: menos trabalho de composição por scroll).

   `.reveal-left` (caixas do FAQ) preserva a direção horizontal em toda
   faixa — só a distância/duração acompanham a variação de cada canal,
   nunca o eixo do movimento.

   ESTE BLOCO PRECISA VIR ANTES de `prefers-reduced-motion` abaixo —
   CSS resolve empate de especificidade pela ORDEM no arquivo, não por
   quantos @media bateram; se viesse depois, um visitante em tela
   estreita COM a preferência ativada receberia de volta o
   deslocamento/atraso que `prefers-reduced-motion` existe pra remover. */
@media (min-width: 768px) and (max-width: 1239.98px) {
  .reveal { transition-duration: 650ms; }
  .reveal:not(.reveal-left) { transform: translateY(18px) scale(0.96); }
  .reveal:not(.reveal-left).is-visible { transform: none; }
  .reveal-left { transform: translateX(-48px); }
}

@media (max-width: 767.98px) {
  .reveal {
    transition: opacity 420ms var(--ease-out), transform 420ms var(--ease-out);
    transition-delay: calc(var(--i, 0) * 45ms);
  }
  .reveal:not(.reveal-left) { transform: translateY(22px); }
  .reveal:not(.reveal-left).is-visible { transform: none; }
  .reveal-left { transform: translateX(-28px); }
}

/* prefers-reduced-motion: reduce" não é "zero animação" (XVIII.10 do
   MESTRE2X0.md) — mantém o fade de opacidade, que ajuda a entender o que
   mudou na tela, e só remove o deslocamento/translateY e o atraso em
   cascata (isso sim é "movimento" no sentido que a preferência pede pra
   evitar). Continua sendo o JS quem alterna `.is-visible` normalmente.
   Vem DEPOIS dos blocos de largura acima de propósito — ver o comentário
   ali em cima — pra vencer em qualquer combinação de tela + preferência. */
@media not all /* reduzir movimento IGNORADO de propósito (decisão do cliente) */ {
  .reveal, .reveal-left {
    transform: none;
    transition: opacity 300ms ease;
    transition-delay: 0ms;
  }
}

/* ---------- Grids reutilizáveis ---------- */
.grid {
  display: grid;
  gap: 24px;
  grid-template-columns: 1fr;
}

/* ---------- Cards ---------- */
.card {
  background: var(--surface-1);
  border: 1px solid var(--border-hair);
  border-radius: var(--radius-lg);
  padding: 32px;
  height: 100%;
}

.card-icon {
  width: 44px;
  height: 44px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--radius-md);
  background: var(--surface-2);
  color: var(--icon-color, #b9c3ff);
  margin-bottom: 20px;
}
.card-icon svg { width: 22px; height: 22px; stroke: currentColor; }

.card h3 { margin-bottom: 10px; }
.card p { color: var(--text-secondary); font-size: 0.9375rem; line-height: 1.6; }

.card-link {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  margin-top: 20px;
  font-size: 0.875rem;
  font-weight: 600;
  color: var(--text-primary);
}
.card-link .arrow-wrap {
  width: 24px; height: 24px;
  border-radius: 50%;
  border: 1px solid var(--border-strong);
  display: inline-flex; align-items: center; justify-content: center;
  transition: transform 160ms var(--ease-out), background 160ms var(--ease-out);
}
.card-link .arrow-wrap svg { width: 12px; height: 12px; }
@media (hover: hover) and (pointer: fine) {
  .card-link:hover .arrow-wrap { transform: translate(2px, -2px); background: var(--gradient-accent); border-color: transparent; }
}

/* ---------- Diferenciais: bento assimétrico (família de layout própria,
   diferente da lista editorial de Serviços) ---------- */
.diff-grid {
  display: grid;
  gap: 20px;
  grid-template-columns: 1.15fr 1fr 1fr;
  grid-template-rows: auto auto;
}
.diff-card {
  display: flex;
  flex-direction: column;
  padding: 32px;
  /* Foto de fundo (uma por card, ver seletores abaixo) + véu escuro em
     degradê por cima — mesmo princípio do vídeo do hero (V.5): a imagem dá
     identidade visual, o véu garante contraste AA pro texto em cima,
     não importa quão clara a foto seja. */
  background-size: cover;
  background-position: center;
}
.diff-card .card-icon { width: 44px; height: 44px; margin-bottom: 20px; }
.diff-card .card-icon svg { width: 22px; height: 22px; }
.diff-card h3 { font-size: 1.25rem; margin-bottom: 10px; }
.diff-card p { font-size: 0.9375rem; line-height: 1.6; }
.diff-card--lead {
  grid-column: 1;
  grid-row: 1 / 3;
  background-image: linear-gradient(175deg, rgba(11, 13, 16, 0.35) 0%, rgba(11, 13, 16, 0.88) 100%), url("../img/diff-design.jpg");
  border-color: rgba(45, 107, 255, 0.18);
  justify-content: center;
}
.diff-card--lead h3 { font-size: 1.5rem; }
.diff-card--lead p { font-size: 1rem; }
.diff-card--wide {
  grid-column: 2 / 4;
  grid-row: 2;
  background-image: linear-gradient(175deg, rgba(11, 13, 16, 0.35) 0%, rgba(11, 13, 16, 0.88) 100%), url("../img/diff-evolucao.jpg");
  flex-direction: row;
  align-items: center;
  gap: 24px;
}
.diff-card--wide .card-icon { margin-bottom: 0; }
.diff-card[data-anim="fade"] {
  background-image: linear-gradient(175deg, rgba(11, 13, 16, 0.35) 0%, rgba(11, 13, 16, 0.88) 100%), url("../img/diff-performance.jpg");
}
.diff-card[data-anim="slide-left"] {
  background-image: linear-gradient(175deg, rgba(11, 13, 16, 0.35) 0%, rgba(11, 13, 16, 0.88) 100%), url("../img/diff-seguranca.jpg");
}

/* Entrada vinculada ao scroll (JS calcula opacity/transform a cada frame,
   ver assets/js/main.js) — sem transition aqui de propósito: o valor tem
   que seguir o scroll 1:1, uma transition concorrente deixaria o
   movimento "atrasado" em vez de acompanhar exatamente a rolagem. */
.diff-grid [data-anim] { will-change: transform, opacity; }

/* ---------- Processo: título "máquina de escrever" + carrossel ----------
   Pedido do cliente: o H2 digita letra por letra ao entrar na tela (JS
   quebra o texto em spans .type-char, cada um com --i = índice; a
   revelação em si é só CSS, então nunca "trava" ligada se o JS falhar —
   mesmo espírito do spotlight). O carrossel logo abaixo ganha uma entrada
   lateral (em vez do slide-up genérico do .reveal) pra sinalizar de cara
   que é um elemento horizontal/arrastável, reforçando a interatividade. */
.type-char {
  /* display:inline (não inline-block!): inline-block cria uma "caixa"
     independente por caractere, e o navegador passa a permitir quebra de
     linha ENTRE elas mesmo sem espaço no meio — foi assim que "precisa"
     virou "p" + "recisa" na primeira versão. Inline puro mantém as regras
     normais de quebra (só em espaço), porque não há espaço no DOM entre
     os spans de uma mesma palavra. */
  opacity: 0;
  transition: opacity 1ms steps(1) calc(var(--i, 0) * 45ms);
}
.type-char.is-visible { opacity: 1; }
@media not all /* reduzir movimento IGNORADO de propósito (decisão do cliente) */ {
  .type-char { transition: none; }
}

/* ---------- Processo: carrossel fixado por scroll (mesmo padrão XX.4 de
   Demos: pin + crossfade). Sem JS (ou se .is-pinned não for ligada), os 4
   passos ficam empilhados normalmente — fallback acessível por padrão. ---- */
.process-pin-inner { position: relative; z-index: 1; display: flex; flex-direction: column; gap: 28px; }
.process-stage { display: flex; flex-direction: column; gap: 24px; }

/* ---- Fundo em 4 lâminas fixas (pedido do cliente): 4 imagens que juntas
   formam uma única cena, sempre lado a lado na ordem 1→4 da esquerda pra
   direita — nunca trocam de posição nem saem da tela, ao contrário do
   crossfade de Demos. Cada lâmina reage ao card correspondente por ÍNDICE
   (não por tema, já que não há categorias aqui): P&B e escurecida em
   repouso, colorida e levemente maior (destacada) quando seu card é o
   ativo. Só existe no modo fixado — sem JS/pin, os 4 passos ficam
   empilhados sobre o fundo padrão do site, sem esta camada. ---- */
.process-bg { display: none; }
#processo.is-pinned .process-bg {
  display: flex;
  position: absolute;
  inset: 0;
  z-index: 0;
}
.process-bg-panel {
  flex: 1 1 25%;
  height: 100%;
  background-size: cover;
  background-position: center;
  filter: grayscale(1) brightness(0.55);
  transition: filter 600ms var(--ease-out), transform 600ms var(--ease-out);
}
.process-bg-panel.is-active {
  filter: grayscale(0) brightness(0.95) saturate(1.2);
  transform: scale(1.04);
  z-index: 1;
}
.process-bg::after {
  content: "";
  position: absolute;
  inset: 0;
  background: rgba(11, 13, 16, 0.4);
  pointer-events: none;
}
@media not all /* reduzir movimento IGNORADO de propósito (decisão do cliente) */ {
  .process-bg-panel { transition: filter 300ms ease; transform: none; }
}
/* Sombra de segurança no título: garante contraste de leitura mesmo quando
   a lâmina ativa embaixo fica clara/colorida. */
#processo .section-head h2 { text-shadow: 0 2px 16px rgba(0, 0, 0, 0.55); }

.process-step {
  position: relative;
  overflow: hidden;
  background: var(--surface-1);
  border: 1px solid var(--border-hair);
  border-radius: var(--radius-lg);
  padding: 28px;
  transform: perspective(800px) rotateX(0deg) rotateY(0deg);
  transition: transform 300ms var(--ease-out);
  will-change: transform;
}
/* Enquanto o JS está atualizando o tilt quadro a quadro, remove a
   transition (senão ela briga com o update contínuo e fica com atraso
   perceptível) — a mesma técnica já usada no tilt do ícone do hero. */
.process-step.is-tracking { transition: none; }
.step-index {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 54px; height: 54px;
  border-radius: 50%;
  background: var(--surface-2);
  border: 1px solid var(--border-strong);
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.0625rem;
  color: var(--text-primary);
  margin-bottom: 20px;
  transition: background 260ms var(--ease-out), border-color 260ms var(--ease-out), color 260ms var(--ease-out), transform 260ms var(--ease-out);
}
.process-step h3 { position: relative; z-index: 1; margin-bottom: 8px; }
.process-step p { position: relative; z-index: 1; color: var(--text-secondary); font-size: 0.9375rem; line-height: 1.6; }
.process-step .step-index { position: relative; z-index: 1; }

/* Fundo interno de cada card: um vetor (SVG puro, nunca foto) com ícones
   que representam o tema da etapa — pedido do cliente. Baixa opacidade e
   z-index:0 pra nunca competir com o texto (que já é z-index:1 acima). */
.process-step-bg {
  position: absolute;
  inset: 0;
  z-index: 0;
  color: var(--icon-color, #b9c3ff);
  opacity: 0.12;
  pointer-events: none;
}
.process-step-bg svg { width: 100%; height: 100%; }

/* ---- Modo fixado (JS liga .is-pinned em #processo — ver initPinCarousel em
   assets/js/main.js): mesmo mecanismo "pin + crossfade conforme o scroll"
   (XX.4) já usado em Demos. A altura artificial abaixo é o que impede
   pular a seção sem passar pelas 4 etapas. ---- */
#processo.is-pinned {
  height: calc(var(--process-count, 4) * var(--process-slide-vh, 70) * 1dvh);
  /* Sem padding-block aqui: o .process-pin já se autocentraliza dentro da
     altura fixada (align-items:center); o padding padrão de .section só
     criava uma "zona morta" preta entre esta seção e a anterior/seguinte,
     sem servir pra nada (pedido do cliente: reduzir a distância). Sem JS
     (sem .is-pinned), o padding normal de .section continua valendo. */
  padding-block: 0;
}
#processo.is-pinned .process-pin {
  position: sticky;
  top: var(--header-h);
  height: calc(100dvh - var(--header-h));
  display: flex;
  align-items: center;
  overflow: hidden;
}
#processo.is-pinned .process-stage {
  position: relative;
  display: block;
  min-height: 260px;
}
#processo.is-pinned .process-step {
  position: absolute;
  inset: 0;
  opacity: 0;
  transform: translateY(24px) scale(0.96);
  transition: opacity 500ms var(--ease-out), transform 500ms var(--ease-out);
  pointer-events: none;
}
#processo.is-pinned .process-step.is-active {
  opacity: 1;
  transform: none;
  pointer-events: auto;
}
@media not all /* reduzir movimento IGNORADO de propósito (decisão do cliente) */ {
  #processo.is-pinned .process-step { transition: opacity 300ms ease; transform: none; }
}

/* ---------- Spotlight interativo: brilho radial que segue o cursor,
   reutilizado nos cards/linhas de Processo, Serviços, Compromisso e FAQ
   (XX.2 do MESTRE2X0.md — "spotlight/glow que segue o cursor na borda").
   JS só atualiza --x/--y no pointermove; a revelação em si é CSS puro
   (:hover), então nunca fica "presa" ligada se o JS falhar por algum
   motivo. Só ativa com cursor real, nunca em touch (XX.6). ---------- */
.spotlight { position: relative; }
.spotlight::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 0;
  opacity: 0;
  pointer-events: none;
  background: radial-gradient(circle 220px at var(--x, 50%) var(--y, 50%), rgba(76, 90, 255, 0.16), transparent 72%);
  transition: opacity 240ms var(--ease-out);
}
@media (hover: hover) and (pointer: fine) {
  .spotlight:hover::before { opacity: 1; }
}

.process-progress-track {
  margin-top: 32px;
  height: 3px;
  border-radius: var(--radius-pill);
  background: var(--border-hair);
  overflow: hidden;
}
.process-progress-fill {
  height: 100%;
  width: 0%;
  border-radius: inherit;
  background: var(--gradient-accent);
}
@media all /* reduzir movimento IGNORADO de propósito (decisão do cliente) */ {
  .process-progress-fill { transition: width 120ms linear; }
}

.case-card {
  display: flex;
  flex-direction: column;
  border-radius: var(--radius-lg);
  border: 1px solid var(--border-hair);
  background: var(--surface-1);
  overflow: hidden;
  height: 100%;
}
.case-card .case-thumb {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 8 / 5;
  object-fit: cover;
  border-bottom: 1px solid var(--border-hair);
  transition: transform 500ms var(--ease-out);
}
@media (hover: hover) and (pointer: fine) {
  .case-card:hover .case-thumb { transform: scale(1.035); }
}
.case-card .case-body { padding: 28px; display: flex; flex-direction: column; gap: 10px; flex-grow: 1; }
.case-card .tag {
  align-self: flex-start;
  font-size: 0.75rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--icon-color, #b9c3ff);
  border: 1px solid var(--border-hair);
  border-radius: var(--radius-pill);
  padding: 6px 14px;
}
.case-card h3 { margin-top: 4px; }
.case-card p { color: var(--text-secondary); font-size: 0.9375rem; line-height: 1.6; flex-grow: 1; }

/* Entrada vinculada ao scroll (JS calcula opacity/transform a cada frame,
   ver assets/js/main.js) — mesmo motivo do diff-grid: sem transition
   competindo com o valor que já é recalculado no próprio scroll. */
#cases .grid > * { will-change: transform, opacity; }

/* Pedido do cliente: os cards rodam em looping como uma "vitrine
   interativa" depois da entrada — carrossel horizontal com scroll-snap
   (mesma técnica já usada no Processo), válido em QUALQUER largura agora
   (antes disso só existia no celular). O avanço automático em si é feito
   por JS (assets/js/main.js) chamando scrollTo() neste container; aqui só
   o mecanismo de rolagem/snap. */
#cases .grid {
  display: flex;
  gap: 24px;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
  padding-bottom: 4px;
  align-items: stretch;
}
#cases .grid::-webkit-scrollbar { display: none; }
/* height: auto (não o 100% do .case-card): num flex sem altura definida o
   100% não resolve e ainda bloqueia o stretch — cada card ficava da altura
   do próprio texto, desalinhando a fileira. Com auto, todos esticam até o
   card mais alto e o "Visitar site" fica alinhado na base. */
#cases .grid > * {
  flex: 0 0 min(380px, 84vw);
  height: auto;
  scroll-snap-align: start;
}

/* ---------- Controles do carrossel: setas + bolinhas ----------
   Afastadas do .card-link/.arrow-wrap (mesma linguagem visual: círculo
   com borda, ícone de seta, preenchimento de marca no hover) porque aqui
   o círculo é maior e é um <button> de navegação de verdade, não parte de
   um link de card. */
.cases-controls { display: flex; align-items: center; gap: 16px; margin-bottom: 28px; }
.cases-arrow {
  flex-shrink: 0;
  width: 44px;
  height: 44px;
  border-radius: 50%;
  border: 1px solid var(--border-strong);
  background: transparent;
  color: var(--text-primary);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  transition: background 160ms var(--ease-out), border-color 160ms var(--ease-out), transform 160ms var(--ease-out);
}
.cases-arrow svg { width: 18px; height: 18px; }
@media (hover: hover) and (pointer: fine) {
  .cases-arrow:hover { background: var(--gradient-accent); border-color: transparent; transform: translateY(-2px); }
}
.cases-dots { display: flex; align-items: center; gap: 8px; }
.cases-dot {
  width: 8px;
  height: 8px;
  padding: 0;
  border: none;
  border-radius: var(--radius-pill);
  background: var(--border-strong);
  cursor: pointer;
  transition: background 160ms var(--ease-out), width 160ms var(--ease-out);
}
.cases-dot.is-active { width: 22px; background: var(--gradient-accent); }
@media (hover: hover) and (pointer: fine) {
  .cases-dot:not(.is-active):hover { background: var(--text-tertiary); }
}

/* ---------- Demos: carrossel controlado por scroll (XX.4 do MESTRE2X0.md:
   "pin + content swap"). Estado base (sem JS, ou JS não liga .is-pinned):
   os 5 slides ficam empilhados e visíveis normalmente — fallback acessível
   por padrão, nunca dependente de JS pra existir. Nenhum destes é um
   cliente real: são demonstrações do que a VIRA pode construir, sempre
   sinalizadas como tal via `.demo-tag`, nunca um case inventado. */
.demo-section { position: relative; }
/* Layout por áreas nomeadas (não por ordem no DOM): "copy" é o texto de
   introdução (eyebrow/título/lead/dica), "progress" é a lista numerada e
   "stage" é o preview — texto e lista empilhados na mesma coluna, preview
   ao lado. */
.demo-pin-inner {
  display: grid;
  grid-template-columns: 0.85fr 1.15fr;
  grid-template-areas: "copy stage" "progress stage";
  column-gap: 60px;
  row-gap: 0;
  align-items: center;
}
.demo-copy { grid-area: copy; }
.demo-progress { grid-area: progress; }
.demo-stage { grid-area: stage; }
.demo-copy .lead { margin-top: 16px; max-width: 40ch; }
.demo-hint {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  margin-top: 24px;
  font-size: 0.8125rem;
  color: var(--text-tertiary);
}
.demo-hint svg { width: 16px; height: 16px; flex-shrink: 0; }

.demo-progress {
  display: flex;
  flex-direction: column;
  gap: 2px;
  border-left: 1px solid var(--border-hair);
  margin-top: 28px;
}
.demo-progress-dot {
  display: flex;
  align-items: center;
  gap: 12px;
  background: none;
  border: none;
  border-left: 2px solid transparent;
  margin-left: -1px;
  padding: 10px 0 10px 16px;
  text-align: left;
  cursor: pointer;
  color: var(--text-tertiary);
  font-family: var(--font-body);
  font-size: 0.875rem;
  transition: color 220ms var(--ease-out), border-color 220ms var(--ease-out);
}
.demo-progress-num { font-family: var(--font-display); font-weight: 700; font-size: 0.75rem; }
.demo-progress-dot.is-active { color: var(--text-primary); border-left-color: var(--accent-solid); }
@media (hover: hover) and (pointer: fine) {
  .demo-progress-dot:hover { color: var(--text-primary); }
}

/* ---- Fundo em tela cheia por categoria: cobre a seção inteira (não só a
   área do preview), atrás de copy/progress/stage — pedido explícito do
   cliente. Um layer por categoria, com crossfade via JS (.is-active
   alternado por initPinCarousel conforme o slide ativo) igual ao crossfade
   já usado nos próprios slides. Sem JS/pin (fallback), fica oculto: nesse
   estado os slides ficam simplesmente empilhados sobre o fundo padrão do
   site, sem a foto temática. ---- */
.demo-bg { display: none; }
.demo-section.is-pinned .demo-bg {
  display: block;
  position: absolute;
  inset: 0;
  z-index: 0;
}
.demo-bg-layer {
  position: absolute;
  inset: 0;
  opacity: 0;
  background-size: cover;
  background-position: center;
  /* As fotos de origem são bem escuras (fundo quase preto); um leve realce
     de brilho/saturação garante que a imagem apareça de verdade como fundo,
     em vez de sumir atrás do véu abaixo. */
  filter: brightness(1.4) saturate(1.2);
  transition: opacity 500ms var(--ease-out);
}
.demo-bg-layer.is-active { opacity: 1; }
/* Véu em gradiente, não chapado: mais escuro só do lado do texto (esquerda)
   pra manter contraste de leitura, bem mais claro do lado do preview —
   pedido do cliente foi a imagem aparecer de verdade como fundo, não ficar
   quase invisível atrás de um véu escuro uniforme. */
.demo-bg::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(100deg, rgba(11, 13, 16, 0.88) 0%, rgba(11, 13, 16, 0.6) 35%, rgba(11, 13, 16, 0.25) 65%, rgba(11, 13, 16, 0.12) 100%);
}
@media not all /* reduzir movimento IGNORADO de propósito (decisão do cliente) */ {
  .demo-bg-layer { transition: opacity 300ms ease; }
}
.demo-pin-inner { position: relative; z-index: 1; }

.demo-stage { display: flex; flex-direction: column; gap: 40px; align-items: center; }
.demo-slide {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 20px;
  width: 100%;
}
.demo-tag {
  font-size: 0.75rem;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-tertiary);
}

/* ---- Tema por categoria: cada demo tem sua própria paleta/tipografia,
   escopada só ao mockup (variáveis --mock-*), pra transmitir "a VIRA se
   adapta a cada nicho" sem alterar a identidade visual do site em si. A
   foto de fundo de cada categoria vive em .demo-bg-layer (acima), não
   aqui. ---- */
.demo-bg-layer[data-theme="institucional"] { background-image: url("../img/demo-institucional.jpg"); }
.demo-slide[data-theme="institucional"] {
  --mock-primary: #0D47A1;
  --mock-secondary: #1976D2;
  --mock-accent: #FFC107;
  --mock-font-display: "Montserrat", var(--font-display);
  --mock-font-body: "Open Sans", var(--font-body);
}
.demo-bg-layer[data-theme="erp"] { background-image: url("../img/demo-erp.jpg"); }
.demo-slide[data-theme="erp"] {
  --mock-primary: #0B3D91;
  --mock-secondary: #1E88E5;
  --mock-accent: #00BFA5;
  --mock-font-display: "Inter", var(--font-display);
  --mock-font-body: "Inter", var(--font-body);
  --mock-font-mono: "IBM Plex Mono", monospace;
}
.demo-bg-layer[data-theme="automacao"] { background-image: url("../img/demo-automacao.jpg"); }
.demo-slide[data-theme="automacao"] {
  --mock-primary: #6C4DFF;
  --mock-secondary: #2D6BFF;
  --mock-accent: #00E5FF;
  --mock-font-display: "Space Grotesk", var(--font-display);
  --mock-font-body: "Inter", var(--font-body);
}
.demo-bg-layer[data-theme="privado"] { background-image: url("../img/demo-privado.jpg"); }
.demo-slide[data-theme="privado"] {
  --mock-primary: #1A1A2E;
  --mock-secondary: #16213E;
  --mock-accent: #E94560;
  --mock-font-display: "Sora", var(--font-display);
  --mock-font-body: "Inter", var(--font-body);
}
.demo-bg-layer[data-theme="delivery"] { background-image: url("../img/demo-delivery.jpg"); }
.demo-slide[data-theme="delivery"] {
  --mock-primary: #FF6B00;
  --mock-secondary: #FFB800;
  --mock-accent: #00C853;
  --mock-font-display: "Poppins", var(--font-display);
  --mock-font-body: "Inter", var(--font-body);
}

/* ---- Frame de telefone (app de delivery / app privado) ---- */
.device-frame {
  /* min(...,100%): a moldura encolhe pra caber a largura real do "stage"
     em vez de vazar da coluna, mantendo a proporção (aspect-ratio cuida
     da altura). */
  width: min(230px, 100%);
  aspect-ratio: 9 / 18.5;
  border-radius: 34px;
  border: 6px solid var(--surface-2);
  background: var(--canvas);
  padding: 6px;
  box-shadow: 0 30px 60px rgba(0, 0, 0, 0.4);
  flex-shrink: 0;
  /* Sem isto, aspect-ratio é só uma "preferência": um box com overflow
     visible tem min-height automático igual ao min-content do conteúdo
     (spec de sizing), e esse mínimo GANHA da altura vinda do aspect-ratio
     sempre que o conteúdo não cabe — o frame "incha" pra virar um
     retângulo alto em vez de manter a proporção de tela de celular.
     overflow:hidden remove esse mínimo automático — a proporção passa a
     valer de verdade, e .device-screen (que já tem seu próprio
     overflow:hidden) cuida de conter a lista internamente. */
  overflow: hidden;
}
.device-screen {
  height: 100%;
  border-radius: 26px;
  background: var(--surface-1);
  padding: 18px 14px 14px;
  display: flex;
  flex-direction: column;
  gap: 12px;
  overflow: hidden;
}
.app-topbar { display: flex; align-items: center; justify-content: space-between; font-size: 0.8125rem; font-weight: 700; color: var(--text-primary); font-family: var(--mock-font-display, var(--font-display)); }
.app-topbar-loc { display: flex; align-items: center; gap: 6px; font-weight: 500; color: var(--text-secondary); font-size: 0.75rem; }
.app-topbar-loc svg { width: 14px; height: 14px; flex-shrink: 0; }
.app-search { background: var(--surface-2); border-radius: var(--radius-pill); padding: 8px 14px; font-size: 0.75rem; color: var(--text-tertiary); }
.app-chips { display: flex; gap: 6px; }
.app-chips span {
  padding: 4px 10px;
  border-radius: var(--radius-pill);
  background: var(--surface-2);
  color: var(--text-tertiary);
  font-size: 0.625rem;
  font-weight: 600;
  white-space: nowrap;
}
.app-chips span.is-active { background: var(--mock-primary, var(--gradient-accent)); color: #fff; transition: background-color 400ms var(--ease-out); }
.app-card { display: flex; align-items: center; gap: 10px; background: var(--surface-2); border-radius: var(--radius-md); padding: 8px; }
.app-card-thumb {
  width: 40px; height: 40px;
  border-radius: var(--radius-sm);
  object-fit: cover;
  flex-shrink: 0;
  background: var(--surface-1);
}
.app-card-body { display: flex; flex-direction: column; gap: 2px; min-width: 0; flex-grow: 1; }
.app-card-body strong { font-size: 0.8125rem; color: var(--text-primary); }
.app-card-body span { font-size: 0.6875rem; color: var(--text-tertiary); }
.app-card-add {
  width: 22px; height: 22px;
  border-radius: 50%;
  background: var(--mock-secondary, var(--gradient-accent));
  color: #fff;
  display: inline-flex; align-items: center; justify-content: center;
  font-size: 0.9375rem;
  flex-shrink: 0;
  transition: background-color 400ms var(--ease-out);
}
.app-cart-bar {
  margin-top: auto;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  background: var(--mock-primary, var(--gradient-accent));
  border-radius: var(--radius-pill);
  padding: 8px 8px 8px 14px;
  color: #fff;
  font-size: 0.6875rem;
  font-weight: 700;
  transition: background-color 400ms var(--ease-out);
}
.app-cart-btn { background: rgba(255, 255, 255, 0.22); border-radius: var(--radius-pill); padding: 6px 12px; font-size: 0.625rem; }

.app-lock { display: inline-flex; align-items: center; gap: 4px; font-size: 0.625rem; color: var(--text-tertiary); }
.app-lock svg { width: 12px; height: 12px; }
.app-stories { display: flex; gap: 10px; }
.app-story {
  width: 30px; height: 30px;
  border-radius: 50%;
  background: var(--surface-2);
  border: 1px solid var(--border-strong);
  color: var(--text-secondary);
  display: inline-flex; align-items: center; justify-content: center;
  font-size: 0.6875rem;
  font-weight: 700;
  flex-shrink: 0;
}
.app-story.is-accent { background: var(--mock-accent, var(--gradient-accent)); border-color: transparent; color: #fff; transition: background-color 400ms var(--ease-out); }
.app-post { display: flex; flex-direction: column; gap: 10px; background: var(--surface-2); border-radius: var(--radius-md); padding: 10px; }
.app-post-head { display: flex; align-items: center; gap: 8px; font-size: 0.75rem; font-weight: 600; color: var(--text-primary); font-family: var(--mock-font-display, var(--font-display)); }
.app-post-avatar { width: 22px; height: 22px; border-radius: 50%; background: var(--mock-accent, var(--gradient-accent)); display: inline-block; flex-shrink: 0; transition: background-color 400ms var(--ease-out); }
.app-post-photo {
  aspect-ratio: 4 / 3;
  border-radius: var(--radius-sm);
  background: linear-gradient(160deg, rgba(45, 107, 255, 0.18), rgba(108, 77, 255, 0.12)), var(--surface-1);
  display: flex; align-items: center; justify-content: center;
  color: var(--text-tertiary);
}
.app-post-photo svg { width: 28px; height: 28px; }
.app-post-caption { font-size: 0.75rem; color: var(--text-secondary); }
.app-post-likes { font-size: 0.6875rem; color: var(--text-tertiary); }
.app-post-likes .app-post-comments { color: var(--text-secondary); text-decoration: underline; text-underline-offset: 2px; }

/* ---- Frame de navegador (ERP / site institucional) ---- */
.browser-frame {
  /* Mesmo raciocínio do .device-frame: encolhe pra caber a largura real do
     "stage". */
  width: min(340px, 100%);
  border-radius: 16px;
  border: 1px solid var(--border-hair);
  background: var(--surface-1);
  overflow: hidden;
  box-shadow: 0 30px 60px rgba(0, 0, 0, 0.4);
}
.browser-frame--wide { width: min(440px, 100%); }
.browser-frame-bar { display: flex; gap: 6px; padding: 10px 14px; border-bottom: 1px solid var(--border-hair); }
.browser-frame-bar span { width: 8px; height: 8px; border-radius: 50%; background: var(--border-strong); }

.erp-layout { display: grid; grid-template-columns: 46px 1fr; min-height: 210px; }
.erp-sidebar { display: flex; flex-direction: column; gap: 14px; padding: 16px 0; align-items: center; border-right: 1px solid var(--border-hair); }
.erp-sidebar span { width: 18px; height: 18px; border-radius: var(--radius-sm); background: var(--surface-2); }
.erp-sidebar span.is-accent { background: var(--mock-primary, var(--gradient-accent)); transition: background-color 400ms var(--ease-out); }
.erp-main { padding: 16px; display: flex; flex-direction: column; gap: 16px; }
.erp-kpis { display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; }
.erp-kpi { background: var(--surface-2); border-radius: var(--radius-sm); padding: 8px 10px; display: flex; flex-direction: column; gap: 4px; }
.erp-kpi-value { display: flex; align-items: baseline; flex-wrap: wrap; gap: 4px 6px; }
.erp-kpi span { font-size: 0.625rem; color: var(--text-tertiary); }
.erp-kpi strong { font-size: 0.8125rem; color: var(--text-primary); white-space: nowrap; font-family: var(--mock-font-mono, var(--font-body)); }
.erp-trend {
  font-size: 0.5625rem;
  font-weight: 700;
  color: var(--mock-accent, var(--accent-solid));
  white-space: nowrap;
  transition: color 400ms var(--ease-out);
}
.erp-chart-wrap { display: flex; flex-direction: column; gap: 6px; }
.erp-chart { display: flex; align-items: flex-end; gap: 8px; height: 84px; }
.erp-chart span { flex: 1; height: var(--h); border-radius: 4px 4px 0 0; background: var(--mock-secondary, var(--gradient-accent)); opacity: 0.75; transition: background-color 400ms var(--ease-out); }
.erp-chart-labels { display: flex; gap: 8px; }
.erp-chart-labels span { flex: 1; text-align: center; font-size: 0.5625rem; color: var(--text-tertiary); }
.erp-transactions { display: flex; flex-direction: column; gap: 8px; border-top: 1px solid var(--border-hair); padding-top: 12px; }
.erp-tx { display: flex; align-items: center; gap: 8px; font-size: 0.6875rem; }
.erp-tx-date { color: var(--text-tertiary); width: 40px; flex-shrink: 0; }
.erp-tx-desc { color: var(--text-secondary); flex-grow: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.erp-tx strong { color: var(--text-primary); flex-shrink: 0; font-family: var(--mock-font-mono, var(--font-body)); }

.site-mock { padding: 18px; display: flex; flex-direction: column; gap: 16px; }
.site-mock-nav { display: flex; gap: 14px; }
.site-mock-nav span { width: 30px; height: 6px; border-radius: var(--radius-pill); background: var(--surface-2); }
.site-mock-nav span.is-accent { background: var(--mock-primary, var(--gradient-accent)); width: 40px; transition: background-color 400ms var(--ease-out); }
.site-mock-hero {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  justify-content: flex-end;
  gap: 6px;
  min-height: 150px;
  border-radius: var(--radius-md);
  padding: 16px;
  background-size: cover;
  background-position: center;
  overflow: hidden;
}
.site-mock-hero::before {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(0deg, rgba(11, 13, 16, 0.88) 10%, rgba(11, 13, 16, 0.15) 80%);
}
.site-mock-eyebrow, .site-mock-headline, .site-mock-hero .site-mock-btn { position: relative; z-index: 1; }
.site-mock-eyebrow {
  font-size: 0.625rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.75);
}
.site-mock-headline { font-family: var(--mock-font-display, var(--font-display)); font-weight: 700; font-size: 1rem; color: #fff; line-height: 1.25; max-width: 22ch; }
.site-mock-btn {
  margin-top: 4px;
  display: inline-block;
  padding: 8px 18px;
  border-radius: var(--radius-pill);
  background: var(--mock-accent, var(--gradient-accent));
  color: #1a1a1a;
  font-size: 0.6875rem;
  font-weight: 700;
  transition: background-color 400ms var(--ease-out);
}
.site-mock-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; }
.site-mock-grid img { aspect-ratio: 1; border-radius: var(--radius-sm); object-fit: cover; width: 100%; }

/* ---- Automação ---- */
.automation-card {
  display: flex;
  /* Sempre em coluna: com 4 passos, um fluxo horizontal só cabe numa
     largura de container que a seção Demos nunca tem de sobra (a coluna do
     "stage" fixado é mais estreita que o card precisaria) — testado e
     confirmado que quebra em "L" mesmo em telas largas. Uma coluna com
     setas pra baixo é 100% robusta a qualquer largura. */
  flex-direction: column;
  align-items: center;
  gap: 8px;
  background: var(--surface-1);
  border: 1px solid var(--border-hair);
  border-radius: var(--radius-lg);
  padding: 20px 32px;
  box-shadow: 0 30px 60px rgba(0, 0, 0, 0.4);
}
.automation-step { display: flex; flex-direction: column; align-items: center; gap: 4px; max-width: 220px; text-align: center; }
.automation-step strong { font-size: 0.75rem; font-weight: 700; color: var(--text-primary); font-family: var(--mock-font-display, var(--font-display)); }
.automation-step small { font-size: 0.6875rem; line-height: 1.35; color: var(--text-tertiary); }
.automation-icon {
  width: 38px; height: 38px;
  border-radius: 50%;
  background: var(--surface-2);
  color: var(--mock-accent, var(--icon-color, #b9c3ff));
  display: inline-flex; align-items: center; justify-content: center;
  margin-bottom: 2px;
  transition: color 400ms var(--ease-out);
}
.automation-icon svg { width: 18px; height: 18px; }
.automation-arrow { color: var(--text-tertiary); font-size: 1rem; flex-shrink: 0; transform: rotate(90deg); line-height: 1; }

/* ---- Modo fixado (JS liga .is-pinned em #demos — ver assets/js/main.js):
   "pin + crossfade conforme o scroll" (XX.4). Sem JS (ou se o <script>
   falhar), o fallback acima (empilhado, sempre visível) permanece ativo —
   nunca depende de JS pra existir. ---- */
.demo-section.is-pinned {
  height: calc(var(--demo-count, 5) * var(--demo-slide-vh, 70) * 1dvh);
  /* Mesma lógica do #processo.is-pinned abaixo: sem padding-block, o
     .demo-pin já se autocentraliza — o padding padrão só virava uma "zona
     morta" preta entre seções vizinhas. */
  padding-block: 0;
}
.demo-section.is-pinned .demo-pin {
  position: sticky;
  top: var(--header-h);
  height: calc(100dvh - var(--header-h));
  display: flex;
  align-items: center;
  overflow: hidden;
}
.demo-section.is-pinned .demo-stage {
  position: relative;
  display: block;
  width: 100%;
  min-height: 380px;
}
.demo-section.is-pinned .demo-slide {
  position: absolute;
  inset: 0;
  opacity: 0;
  transform: translateY(24px) scale(0.96);
  transition: opacity 500ms var(--ease-out), transform 500ms var(--ease-out);
  pointer-events: none;
}
.demo-section.is-pinned .demo-slide.is-active {
  opacity: 1;
  transform: none;
  pointer-events: auto;
}
@media not all /* reduzir movimento IGNORADO de propósito (decisão do cliente) */ {
  .demo-section.is-pinned .demo-slide { transition: opacity 300ms ease; transform: none; }
}

/* ---------- Compromisso: grid 2x2 uniforme com numeração fantasma gigante
   no canto (família própria: nem bento assimétrico, nem lista de linha
   cheia, um grid parelho com contraste de escala por trás do conteúdo). */
.commitment-list {
  display: grid;
  gap: 20px;
  grid-template-columns: repeat(2, 1fr);
  /* grid-auto-rows:1fr garante os 4 cards com a mesma altura mesmo se um
     título quebrar em mais linhas que outro. */
  grid-auto-rows: 1fr;
}
.commitment-item {
  position: relative;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  gap: 16px;
  align-items: flex-start;
  padding: 32px;
  border-radius: var(--radius-lg);
  background: var(--surface-1);
  border: 1px solid var(--border-hair);
  transition: border-color 220ms var(--ease-out);
}
@media (hover: hover) and (pointer: fine) {
  .commitment-item:hover { border-color: var(--border-strong); }
}
.commitment-item .ghost-num {
  position: absolute;
  /* Deslocamento positivo (não negativo): o card tem overflow:hidden, e um
     valor negativo cortava o topo do número de forma reta e quebrada, não
     um "sangramento" intencional. Positivo mantém o numeral inteiro
     visível dentro do card. */
  top: 6px;
  right: 20px;
  font-family: var(--font-display);
  font-weight: 800;
  font-size: 6.5rem;
  line-height: 1;
  color: var(--surface-2);
  z-index: 0;
  pointer-events: none;
  user-select: none;
}
.commitment-item .check {
  position: relative;
  z-index: 1;
  flex-shrink: 0;
  width: 32px; height: 32px;
  border-radius: 50%;
  background: var(--gradient-accent);
  display: inline-flex; align-items: center; justify-content: center;
}
.commitment-item .check svg { width: 15px; height: 15px; stroke: #fff; }
.commitment-item h3 { position: relative; z-index: 1; font-size: 1.125rem; line-height: 1.3; margin-bottom: 4px; }
.commitment-item p { position: relative; z-index: 1; color: var(--text-secondary); font-size: 0.9375rem; line-height: 1.45; max-width: 34ch; }

/* ---------- FAQ ---------- */
/* Moldura da seção inteira (pedido do cliente): borda fina branca, cantos
   arredondados (reaproveita --radius-lg, mesma filosofia de raio do resto
   do site — V.4 do MESTRE2X0.md) e uma sombra pronunciada "de impacto" ao
   redor, dando a impressão de a seção estar suspensa/prestes a saltar da
   página. Exceção deliberada e isolada à regra geral de "zero sombra" do
   site (só o CTA primário tinha uma sombra antes, --shadow-cta) — nunca
   generalizar essa sombra pra outras seções sem pedido explícito.
   max-width/padding-inline calculados pra a área de conteúdo (depois do
   padding) bater exatamente com --container-w: a borda fica visivelmente
   "fora" do container, não colada nele. */
#faq {
  max-width: calc(var(--container-w) + 96px);
  margin-inline: auto;
  padding-inline: 48px;
  border: 1px solid rgba(245, 246, 248, 0.5);
  border-radius: var(--radius-lg);
  box-shadow: 0 50px 120px -20px rgba(0, 0, 0, 0.6), 0 0 80px rgba(76, 90, 255, 0.16);
}

.faq-panel { margin-top: 32px; }
.faq-panel-inner {
  position: relative;
  min-height: 480px;
  display: flex;
  align-items: center;
}
.faq-box { position: relative; z-index: 1; min-width: 0; max-width: 680px; width: 100%; }
/* Imagem livre "atrás" do conteúdo, não confinada a uma coluna/moldura —
   pedido do cliente: deve parecer parte do fundo, em ângulo, como se
   estivesse vindo do fundo da tela pra frente. Sangra pra fora da coluna
   (cortada pelo overflow:hidden de .section-glow, que #faq já tem) e some
   nas bordas via mask, em vez de terminar numa borda reta de "foto
   emoldurada". A entrada usa profundidade real (perspective + translateZ):
   parte de longe/pequena/mais rodada e "chega" até a pose final. */
.faq-visual {
  position: absolute;
  top: 50%;
  right: -10%;
  z-index: 0;
  width: 60%;
  max-width: 640px;
  pointer-events: none;
  -webkit-mask-image: linear-gradient(115deg, transparent 0%, rgba(0, 0, 0, 0.85) 22%, black 45%, black 80%, rgba(0, 0, 0, 0.35) 100%);
  mask-image: linear-gradient(115deg, transparent 0%, rgba(0, 0, 0, 0.85) 22%, black 45%, black 80%, rgba(0, 0, 0, 0.35) 100%);
  filter: drop-shadow(0 40px 60px rgba(0, 0, 0, 0.5));
  transform: translateY(-50%) perspective(1400px) translateZ(-260px) translateX(220px) rotateY(-26deg) rotateX(9deg) scale(0.8);
}
.faq-visual.is-visible {
  transform: translateY(-50%) perspective(1400px) translateZ(0) rotateY(-16deg) rotateX(5deg) scale(1);
}
@media (max-width: 960px) {
  .faq-panel-inner { min-height: 0; }
  /* display:none sobreposto por #vcs-canvas .faq-visual (Parte XXI, fim
     deste arquivo) — nada some mais por breakpoint, só encolhe junto com
     o resto pela escala. Regra mantida (não apagada) só como registro de
     que esta já foi a única adaptação mobile residual do site antes do
     plano de escala proporcional. */
  .faq-visual { display: none; }
}
@media not all /* reduzir movimento IGNORADO de propósito (decisão do cliente) */ {
  /* Mantém o ângulo fixo (decorativo, não é "movimento"), remove só o
     deslocamento/profundidade de entrada — mesma lógica do bloco XVIII.10
     acima. */
  .faq-visual { transform: translateY(-50%) perspective(1400px) rotateY(-16deg) rotateX(5deg); }
}

.faq-list { display: flex; flex-direction: column; gap: 12px; max-width: 760px; }
.faq-item {
  background: var(--surface-1);
  border: 1px solid var(--border-hair);
  border-radius: var(--radius-md);
  overflow: hidden;
}
.faq-item summary {
  cursor: pointer;
  list-style: none;
  padding: 22px 24px;
  display: flex;
  align-items: center;
  gap: 16px;
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 1rem;
  color: var(--text-primary);
}
.faq-item summary .faq-num {
  flex-shrink: 0;
  font-size: 0.8125rem;
  font-weight: 700;
  color: var(--text-tertiary);
  letter-spacing: 0.02em;
}
.faq-item summary .faq-question { flex-grow: 1; }
.faq-item summary::-webkit-details-marker { display: none; }
.faq-item summary .plus {
  flex-shrink: 0;
  width: 24px; height: 24px;
  position: relative;
}
.faq-item summary .plus::before,
.faq-item summary .plus::after {
  content: "";
  position: absolute;
  background: var(--text-secondary);
  transition: transform 200ms var(--ease-out), opacity 200ms var(--ease-out);
}
.faq-item summary .plus::before { top: 50%; left: 0; width: 100%; height: 1.5px; transform: translateY(-50%); }
.faq-item summary .plus::after { left: 50%; top: 0; height: 100%; width: 1.5px; transform: translateX(-50%); }
.faq-item.is-open-visual summary .plus::after { transform: translateX(-50%) rotate(90deg); opacity: 0; }
.faq-item .faq-answer { padding: 0 24px 22px; color: var(--text-secondary); font-size: 0.9375rem; line-height: 1.65; max-width: 60ch; }

@media not all /* reduzir movimento IGNORADO de propósito (decisão do cliente) */ {
  .faq-item summary .plus::before, .faq-item summary .plus::after { transition: none; }
}

/* ---------- CTA final: "Convite" ↔ "Grade de soluções" ----------
   Duas telas dentro da mesma seção (#contato), uma visível por vez,
   trocadas por classe/atributo hidden via JS — nunca navegação de página
   (ver assets/js/main.js). Pedido explícito do cliente: a segunda tela
   quebra o canvas escuro do resto do site (branco + linhas azuis) como um
   momento deliberado e único de "modo solução", não uma seção normal. */
.cta-final { padding-block: 0; }

/* As duas telas ocupam a mesma célula de grid (empilhadas), não posições
   sequenciais — é o que permite a que entra "subir por cima" da que sai
   sem cortar a seção no meio da troca. Importante: NENHUM dos dois painéis
   usa display:none (nem o atributo [hidden]) em momento algum — um painel
   com display:none sai por completo do cálculo de altura do grid, então a
   célula encolhia/crescia exatamente no instante da troca (o "reajuste de
   posição" reportado: o rodapé pulava pra cima/baixo junto). Os dois
   ficam sempre presentes e ocupando espaço (só opacity + pointer-events +
   [inert] mudam), então nenhuma das duas telas nunca sai do cálculo de
   layout — só que isso sozinho não garante a MESMA moldura: a altura
   "natural" de cada uma ainda podia variar (a grade de 5 cards é
   naturalmente mais alta que a foto+pergunta), fazendo o conteúdo de uma
   das telas passar do que cabia numa tela de altura só. Por isso .cta-
   stage recebe uma altura explícita e igual pras duas (não "a maior das
   duas", um valor fixo mesmo) — as duas telas são centralizadas dentro
   dela via align-items:center, então a moldura e o posicionamento na
   página são idênticos nos dois estados, e a troca vira só o conteúdo de
   dentro subindo, nunca a seção mudando de proporção. overflow:hidden
   recorta o começo desse deslocamento, como se o card entrando estivesse
   subindo de baixo do recorte da seção. */
.cta-stage {
  display: grid;
  overflow: hidden;
  height: clamp(640px, 82dvh, 760px);
}
.cta-panel {
  grid-area: 1 / 1;
  display: flex;
  align-items: center;
  padding-block: clamp(2.5rem, 5vw, 4rem);
  opacity: 0;
  pointer-events: none;
  z-index: 1;
  transition: transform 480ms var(--ease-out), opacity 480ms var(--ease-out);
}
.cta-panel > .container { width: 100%; }

.cta-panel.is-active { opacity: 1; pointer-events: auto; }
.cta-panel.is-incoming { z-index: 2; }
.cta-panel.is-enter-start {
  opacity: 0;
  transform: translateY(64px) scale(0.98);
  transition: none;
}
.cta-panel.is-leaving { transform: scale(0.96); }

@media not all /* reduzir movimento IGNORADO de propósito (decisão do cliente) */ {
  .cta-panel,
  .cta-panel.is-leaving,
  .cta-panel.is-enter-start {
    transform: none;
    transition: opacity 300ms ease;
  }
}

/* -- Estado 1: convite -- */
.cta-intro-grid {
  display: grid;
  grid-template-columns: 0.85fr 1.15fr;
  align-items: center;
  gap: clamp(2.5rem, 6vw, 5rem);
}

.cta-intro-visual { display: flex; justify-content: center; }
.cta-float-card {
  width: min(100%, 340px);
  height: auto;
  filter: drop-shadow(0 24px 48px rgba(0, 0, 0, 0.5));
  animation: cta-bounce 2.4s infinite;
}
/* Bounce leve, não um sine-wave simétrico: cada trecho do keyframe leva
   sua própria timing-function (só é possível declarando-a DENTRO do
   keyframe, não no `animation` do elemento) — sobe com ease-out (arranca
   rápido, desacelera no topo, como um pulo) e desce com ease-in
   (acelera caindo, como gravidade), em vez do vaivém uniforme de antes. */
@keyframes cta-bounce {
  0% { transform: translateY(0); animation-timing-function: cubic-bezier(0.33, 0, 0.2, 1); }
  45% { transform: translateY(-14px); animation-timing-function: cubic-bezier(0.55, 0, 1, 0.45); }
  100% { transform: translateY(0); }
}
@media not all /* reduzir movimento IGNORADO de propósito (decisão do cliente) */ {
  .cta-float-card { animation: none; }
}

.cta-question {
  font-weight: 800;
  font-size: clamp(2.5rem, 5vw, 4rem);
  line-height: 1.05;
  letter-spacing: -0.02em;
}
/* Os dois spans .type-heading (ver assets/js/main.js) substituem o <br>
   de antes: cada um vira uma linha própria. Filho DIRETO (>), não
   descendente qualquer — o próprio efeito de digitação injeta um
   <span class="type-char"> por LETRA dentro de cada linha, e um seletor
   sem o `>` também forçava esses spans de caractere pra display:block,
   empilhando a palavra na vertical, letra por letra (era exatamente o
   bug visto em produção). */
.cta-question > span { display: block; }

/* Botão e headline compartilham o mesmo eixo central — texto e CTA
   alinhados ao centro da coluna, não mais à esquerda. */
.cta-intro-copy { text-align: center; }

.cta-solution-btn {
  margin-top: 40px;
  padding: 22px 44px;
  font-size: 1.125rem;
  letter-spacing: 0.02em;
}
.cta-solution-btn-icon {
  width: 30px; height: 30px;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.2);
  display: inline-flex; align-items: center; justify-content: center;
  transition: transform 160ms var(--ease-out);
}
.cta-solution-btn-icon svg { width: 15px; height: 15px; }
@media (hover: hover) and (pointer: fine) {
  .cta-solution-btn:hover .cta-solution-btn-icon { transform: translate(2px, -2px); }
}

/* -- Estado 2: grade de soluções (exceção deliberada de canvas — ver
   comentário acima da .cta-final) -- */
.cta-solutions {
  /* Pedido do cliente (29/09/2026): o "modo claro" (fundo quase-branco +
     grade quadriculada azul) que existia aqui era um contraste
     deliberado com o resto do site — trocado pelo fundo padrão
     (--canvas), o mesmo tom de qualquer outra seção, em qualquer tamanho
     de tela (antes disto, só tinha sido trocado pro celular — ver
     XXI.11.7.1; agora é global). --cta-accent-on-white continua existindo
     porque os CARDS de solução (.solution-card) continuam BRANCOS de
     propósito — não eram "o fundo quadriculado" do pedido, e já têm
     contraste interno calibrado pro próprio fundo branco deles. */
  --cta-accent-on-white: #2e46d8;
  position: relative;
  width: 100vw;
  margin-left: calc(50% - 50vw);
  background-color: var(--canvas);
}

.cta-solutions-inner { position: relative; z-index: 1; }

.cta-back {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  background: none;
  border: none;
  box-shadow: none;
  appearance: none;
  padding: 0;
  cursor: pointer;
  font-family: var(--font-body);
  font-weight: 600;
  font-size: 0.875rem;
  color: var(--text-secondary);
  margin-bottom: clamp(1rem, 2.4vw, 1.5rem);
  transition: color 160ms var(--ease-out);
}
.cta-back svg { width: 16px; height: 16px; }
.cta-back:hover { color: var(--text-primary); }

.cta-solutions-title {
  text-align: center;
  color: var(--text-primary);
  font-size: clamp(2.25rem, 4.2vw, 3.25rem);
  max-width: 24ch;
  margin-inline: auto;
  margin-bottom: clamp(1.5rem, 3.6vw, 2.25rem);
}

.solution-grid {
  display: grid;
  grid-template-columns: repeat(6, 1fr);
  gap: 20px;
  align-items: stretch;
}

.solution-card {
  display: flex;
  flex-direction: column;
  background-color: #ffffff;
  background-size: cover;
  background-position: center;
  border: 1px solid rgba(45, 107, 255, 0.16);
  border-radius: var(--radius-lg);
  padding: 18px 16px;
  box-shadow: 0 18px 40px rgba(31, 42, 84, 0.08);
}
/* Uma imagem de fundo por card (pedido do cliente), uma por tema. As
   fontes variam muito de luminosidade entre si (institucional e app
   privado são quase pretas; landpage, e-commerce e pack de artes são
   claras) — o véu branco a 86% garante o mesmo contraste pro texto escuro
   do card (--cta-accent-on-white, #1f232b) em qualquer uma das cinco,
   sem precisar calibrar opacidade individualmente por imagem. */
.solution-card[data-theme="landpage"] {
  background-image: linear-gradient(rgba(255, 255, 255, 0.86), rgba(255, 255, 255, 0.86)), url("../img/solution-landpage.jpg");
}
.solution-card[data-theme="institucional"] {
  background-image: linear-gradient(rgba(255, 255, 255, 0.86), rgba(255, 255, 255, 0.86)), url("../img/solution-institucional.jpg");
}
.solution-card[data-theme="ecommerce"] {
  background-image: linear-gradient(rgba(255, 255, 255, 0.86), rgba(255, 255, 255, 0.86)), url("../img/solution-ecommerce.jpg");
}
.solution-card[data-theme="app-privado"] {
  background-image: linear-gradient(rgba(255, 255, 255, 0.86), rgba(255, 255, 255, 0.86)), url("../img/solution-app-privado.jpg");
}
.solution-card[data-theme="pack-artes"] {
  background-image: linear-gradient(rgba(255, 255, 255, 0.86), rgba(255, 255, 255, 0.86)), url("../img/solution-pack-artes.jpg");
}
/* Cardápio digital é categoria nova (29/09/2026), sem cliente real ainda —
   por isso, ao contrário dos outros cinco cartões, nenhuma foto própria
   pra usar de fundo aqui. Em vez de reaproveitar uma foto de banco de
   imagens fingindo ser um produto VIRA (proibido em DESIGN.md), o fundo é
   só um gradiente na cor de marca, sem pretender ser nada além disso. */
.solution-card[data-theme="cardapio"] {
  background-image: linear-gradient(rgba(255, 255, 255, 0.9), rgba(255, 255, 255, 0.9)), radial-gradient(circle at 82% 18%, rgba(255, 159, 67, 0.4), transparent 55%), linear-gradient(135deg, #fff4e6 0%, #ffe8d6 45%, #ffffff 100%);
}

.solution-index {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 0.8125rem;
  letter-spacing: 0.06em;
  /* var(--accent-solid) contra o branco do painel dá só 4.27:1 — abaixo do
     mínimo WCAG 2.2 de 4.5:1 pra texto pequeno (Parte XV, Acessibilidade).
     --cta-accent-on-white é a mesma família de cor, escurecida só o
     suficiente pra passar (6.69:1), escopada a este painel. */
  color: var(--cta-accent-on-white);
}

.solution-card h3 {
  color: #1f232b;
  font-size: 1.0625rem;
  margin-top: 8px;
  margin-bottom: 2px;
}

.solution-body {
  flex: 1;
  display: flex;
  flex-direction: column;
  margin-top: 8px;
}

.solution-benefits {
  display: flex;
  flex-direction: column;
  gap: 5px;
}
.solution-benefits li {
  position: relative;
  padding-left: 16px;
  font-size: 0.75rem;
  line-height: 1.4;
  /* 9.03:1 contra o fundo do card — WCAG 2.2 com folga (cor não muda com
     o tamanho da fonte, só o espaço ocupado). */
  color: #3f4652;
}
.solution-benefits li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.5em;
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: var(--cta-accent-on-white);
}

.solution-price {
  margin-top: auto;
  padding-top: 10px;
  border-top: 1px dashed rgba(45, 107, 255, 0.25);
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 0.875rem;
  color: #1f232b;
}

.solution-cta {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  margin-top: 10px;
  padding: 10px 18px;
  border-radius: var(--radius-pill);
  border: 1px solid var(--accent-solid);
  /* Mesmo motivo do .solution-index acima: var(--accent-solid) em texto
     pequeno contra o branco do painel não fecha 4.5:1 (WCAG 2.2). */
  color: var(--cta-accent-on-white);
  font-family: var(--font-body);
  font-weight: 700;
  font-size: 0.8125rem;
  transition: background 160ms var(--ease-out), color 160ms var(--ease-out);
}
@media (hover: hover) and (pointer: fine) {
  /* branco sobre var(--accent-solid) fecha só 4.49:1 (arredonda pra baixo
     de 4.5) — troca pra --cta-accent-on-white no preenchimento também. */
  .solution-cta:hover { background: var(--cta-accent-on-white); color: #ffffff; }
}

/* ---------- Rodapé ---------- */
.site-footer {
  padding-block: clamp(3rem, 7vw, 4.5rem) 2rem;
  /* Último elemento da página com viewport-fit=cover: sem isto o texto de
     copyright podia terminar embaixo da barra de gestos/home indicator. */
  padding-bottom: calc(2rem + env(safe-area-inset-bottom, 0px));
  border-top: 1px solid var(--border-hair);
  margin-top: clamp(2.5rem, 9vw, 8rem);
}
.footer-top {
  display: grid;
  gap: 40px;
  grid-template-columns: 1.4fr 1fr 1fr;
  padding-bottom: 40px;
}
.footer-brand img { width: 132px; height: auto; margin-bottom: 16px; }
.footer-brand p { color: var(--text-secondary); font-size: 0.9375rem; max-width: 32ch; }
.footer-col h3 {
  font-family: var(--font-body);
  font-size: 0.8125rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-tertiary);
  margin-bottom: 16px;
}
.footer-col ul { display: flex; flex-direction: column; gap: 12px; list-style: none; margin: 0; padding: 0; }
.footer-col a { color: var(--text-secondary); font-size: 0.9375rem; transition: color 160ms var(--ease-out); }
.footer-col a:hover { color: var(--text-primary); }

.footer-social { display: flex; gap: 12px; margin-top: 16px; }
.footer-social a {
  width: 38px; height: 38px;
  border-radius: 50%;
  border: 1px solid var(--border-hair);
  display: inline-flex; align-items: center; justify-content: center;
  transition: border-color 160ms var(--ease-out), background 160ms var(--ease-out);
}
.footer-social a svg { width: 16px; height: 16px; stroke: var(--text-secondary); }
@media (hover: hover) and (pointer: fine) {
  .footer-social a:hover { border-color: var(--border-strong); background: var(--surface-1); }
}

.footer-bottom {
  display: flex;
  flex-wrap: wrap;
  gap: 12px 24px;
  align-items: center;
  justify-content: space-between;
  padding-top: 28px;
  border-top: 1px solid var(--border-hair);
  color: var(--text-tertiary);
  font-size: 0.8125rem;
}

/* ---------- Página 404 ---------- */
.not-found {
  min-height: calc(100dvh - var(--header-h));
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  padding-block: 6rem;
}
.not-found-code {
  font-family: var(--font-display);
  font-weight: 800;
  font-size: clamp(4rem, 14vw, 8rem);
  line-height: 1;
  background: var(--gradient-accent);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}

/* =========================================================================
   PARTE XXI do MESTRE2X0.md — ADAPTAÇÃO MULTI-TELA POR ESCALA PROPORCIONAL
   ("zoom out"). Bloco 100% aditivo (Regra de Ouro 3 de V.ambiente.txt):
   nada acima desta linha foi removido ou reescrito por causa disto.

   Mecanismo: abaixo de --vcs-reference-width (a largura exata em que
   .container já hoje para de crescer: --container-w 1160px + 2x
   --container-px 40px = 1240px), todo o conteúdo de <body> (dentro de
   #vcs-canvas) é tratado como uma fotografia de largura fixa e re-escalado
   com transform: scale() (definido por assets/js/main.js, função no topo
   do arquivo) pra caber exatamente na largura real da tela — mesma posição
   relativa de cada elemento, mesma proporção entre eles, nada reempilhado
   ou redesenhado por breakpoint. Acima de --vcs-reference-width (desktop,
   notebooks, TVs com viewport típico de 1920px, monitores grandes), nada
   neste bloco se aplica: o layout continua 100% fluido, exatamente como
   hoje — zero risco de regressão pra quem já usa o site em desktop.

   transform (e não zoom) foi escolhido de propósito: é puramente uma
   operação de PINTURA, não reinterpreta nenhum vw/vh/dvh/% já usado no
   resto deste arquivo (esses continuam resolvendo exatamente como sempre
   resolveram) — ver PARTE XXI do MESTRE2X0.md para a análise completa e os
   riscos descartados (CSS zoom foi cogitado e rejeitado por reinterpretar
   essas unidades de forma inconsistente entre navegadores). */

:root {
  --vcs-reference-width: 1240px;
  /* Referência de altura só pras seções que usam dvh real mas NÃO são um
     carrossel de pin (hero, 404, .cta-stage) — não é a altura de nenhum
     dispositivo, é o valor que essas seções "acham" que a tela tem quando
     desenhadas pro desktop, pra manterem a MESMA proporção do desktop em
     vez de herdar a proporção de tela do aparelho (compatível com a faixa
     implícita no clamp() de .cta-stage: 640–760px em 82%). Os carrosséis
     de pin (Processo/Demos) NÃO usam esta variável — eles precisam que a
     janela sticky preencha a tela REAL em qualquer escala, o que exige a
     variável seguinte (--vcs-local-viewport-height) em vez desta — ver o
     comentário junto às regras de #processo.is-pinned/.demo-section.is-
     pinned, mais abaixo, pro porquê de misturar as duas ter causado um
     vão preto gigantesco (bug reportado pelo cliente e corrigido). */
  --vcs-reference-height: 1150px;
  --vcs-scale: 1;
  /* Altura de UMA tela real, convertida pro sistema de coordenadas LOCAL
     do canvas (pré-transform) — usada só pela caixa "janela" do pin
     (.process-pin/.demo-pin), nunca pela altura ARTIFICIAL do carrossel
     inteiro (essa continua em --vcs-reference-height, ver XXI.5.1 no
     MESTRE2X0.md). JS publica window.innerHeight / escala aqui; sem JS,
     100dvh (a altura real de verdade) é um fallback correto porque nesse
     cenário --vcs-scale também fica em 1 (transform vira no-op). */
  --vcs-local-viewport-height: 100dvh;
}

@media (min-width: 768px) and (max-width: 1239.98px) {
  /* Só TABLET a partir daqui (768–1239.98px) — desde 29/09/2026. Antes
     cobria também celular (max-width:1239.98px sozinho); o cliente pediu
     pra celular parar de usar escala proporcional e ganhar uma versão
     genuinamente vertical/reempilhada (Parte XXI.11, mais abaixo neste
     arquivo) — reflow de verdade, não fotografia reescalada do desktop.
     Tablet continua exatamente como documentado abaixo: mesma composição
     do desktop, só em escala menor.

     zoom, não transform: scale() — decisão tomada depois de testar as
     duas de verdade (isolado, fora do site) em vez de só argumentar no
     papel. transform QUEBRA position:sticky em qualquer descendente
     (confirmado em teste isolado: o header e os dois carrosséis de pin
     — .site-header, .process-pin, .demo-pin — pararam de grudar e
     passaram a rolar em lockstep com a página, causando o vão preto
     gigantesco entre seções reportado pelo cliente). zoom preserva
     sticky corretamente (mesmo teste isolado, com resultado oposto) e
     trata vw/vh/dvh exatamente igual a transform (ambos resolvem contra
     o viewport REAL, ignorando o fator de escala do ancestral) — ou
     seja, as correções de dvh/100vw feitas mais abaixo (pensadas
     originalmente para transform) continuam necessárias e válidas sem
     nenhuma mudança. zoom também encolhe a caixa de LAYOUT de verdade
     (diferente de transform), então #vcs-host não precisa mais de altura
     nem overflow calculados via JS — ver Parte XXI.1 do MESTRE2X0.md. */
  #vcs-canvas {
    width: var(--vcs-reference-width);
    zoom: var(--vcs-scale);
  }

  #vcs-canvas .hero {
    min-height: calc(var(--vcs-reference-height) - var(--header-h));
  }
  #vcs-canvas .not-found {
    min-height: calc(var(--vcs-reference-height) - var(--header-h));
  }

  /* Carrossel de pin (Processo/Demos): a altura ARTIFICIAL do container
     (que cria a distância de rolagem) e a altura da caixa "janela" que
     fica sticky por cima dela precisam vir da MESMA base
     (--vcs-local-viewport-height, não --vcs-reference-height) — é a
     proporção ENTRE as duas que faz a caixa ficar "grudada" enquanto os
     cards trocam por baixo, exatamente como no desktop (lá, ambas já usam
     100dvh, então a proporção sai de graça: count x slideVh continua
     significando "count x slideVh telas reais de rolagem parada", em
     qualquer tela). A primeira versão desta correção usava
     --vcs-reference-height (fixa) no container e --vcs-local-viewport-
     height (real ÷ escala) na janela — bases DIFERENTES, que quase
     igualavam as duas alturas e deixavam a maior parte da rolagem com a
     janela já "solta" (fora do modo sticky) antes da hora, mostrando só
     fundo preto por cima da seção seguinte ainda não revelada. Era o vão
     gigantesco reportado pelo cliente entre "VIRA o que você precisar" e
     "A solução que você precisa", e entre esta e "Pilares da empresa". */
  #vcs-canvas #processo.is-pinned {
    height: calc(var(--process-count, 4) * var(--process-slide-vh, 70) * (var(--vcs-local-viewport-height) / 100));
  }
  #vcs-canvas #processo.is-pinned .process-pin {
    height: calc(var(--vcs-local-viewport-height) - var(--header-h));
  }
  #vcs-canvas .demo-section.is-pinned {
    height: calc(var(--demo-count, 5) * var(--demo-slide-vh, 70) * (var(--vcs-local-viewport-height) / 100));
  }
  #vcs-canvas .demo-section.is-pinned .demo-pin {
    height: calc(var(--vcs-local-viewport-height) - var(--header-h));
  }

  #vcs-canvas .cta-stage {
    height: clamp(640px, calc(var(--vcs-reference-height) * 0.82), 760px);
  }

  /* .cta-solutions usa a técnica clássica de "sangria" (100vw real +
     margin-left negativo) pra escapar do container e cobrir a tela toda —
     funciona fora do canvas, mas 100vw/50vw são unidades de VIEWPORT REAL
     (transform não as reinterpreta, ver Parte XXI.1 do MESTRE2X0.md):
     resolviam contra a largura real do celular (ex. 375px) e DEPOIS ainda
     eram encolhidas pela escala do ancestral, encolhendo o painel inteiro
     (e a grade de 5 cards dentro dele) pra uma fração minúscula da tela —
     era o "card totalmente fora do layout" reportado pelo cliente. Trocado
     pelo equivalente em unidades LOCAIS (largura de referência do canvas
     em vez de 100vw), a mesma técnica de sangria volta a cobrir o canvas
     inteiro, que por sua vez preenche a tela real via a escala do
     ancestral — exatamente como o resto do site já faz. */
  #vcs-canvas .cta-solutions {
    width: var(--vcs-reference-width);
    margin-left: calc(50% - (var(--vcs-reference-width) / 2));
  }

  /* .faq-visual não deve mais desaparecer por breakpoint — contraria "mesma
     posição/mesmo campo de visão" do desktop (pedido do cliente). Some
     junto com tudo o resto pela escala, não por display:none. Sobrepõe a
     regra antiga (@media max-width:960px, mais acima neste arquivo) por
     especificidade (#vcs-canvas .faq-visual > .faq-visual sozinho), sem
     apagar o registro histórico de por que ela existia. */
  #vcs-canvas .faq-visual {
    display: block;
  }
}

/* =========================================================================
   PARTE XXI.11 do MESTRE2X0.md — VERSÃO VERTICAL DE CELULAR (substitui a
   XXI.10 inteira; pedido do cliente em 29/09/2026: "vamos tentar de outra
   forma" depois de duas rodadas de bugs na abordagem de escala proporcional
   aplicada também a celular). Diferença fundamental do resto desta Parte:
   daqui pra baixo NÃO é mais "a mesma composição do desktop, só menor" — é
   reflow de verdade, layout próprio por seção, o responsivo tradicional que
   V.ambiente.txt sempre descreveu. #vcs-canvas não recebe width nem zoom
   nesta faixa (a regra de tablet, acima, para em 768px) — o HTML narrativo
   dentro dele volta a fluir 100% normal, e cada seção abaixo ganha só os
   ajustes que realmente precisa. Escopo: max-width:767.98px (só celular de
   verdade — o limite "phone" de V.ambiente.txt §7; tablet a partir de
   768px continua no mecanismo de zoom, intocado). */
@media (max-width: 767.98px) {
  /* ---- Menu: expandido na própria barra, igual ao desktop ----
     Pedido do cliente (03/10/2026): sem hambúrguer no celular — marca,
     links e "Contato" ficam todos visíveis dentro da barra fixa, com fonte
     e espaçamentos que encolhem com a largura (clamp em vw) pra caber até
     em 320px. .nav-toggle continua display:none (regra base) e .nav-menu
     continua display:contents, então o JS do hambúrguer em main.js vira
     no-op aqui também. O padding lateral da barra cai de 40px pra 16px
     só no header — o conteúdo das seções mantém o --container-px. */
  .nav {
    gap: clamp(8px, 2.6vw, 16px);
    padding-left: max(16px, env(safe-area-inset-left, 0px));
    padding-right: max(16px, env(safe-area-inset-right, 0px));
  }
  .brand {
    gap: 6px;
    font-size: clamp(0.875rem, 4vw, 1.0625rem);
    flex-shrink: 0;
  }
  .brand-mark { width: 18px; height: 13px; }

  .nav-links {
    gap: clamp(10px, 3.4vw, 20px);
    margin-left: auto;
  }
  .nav-links a {
    position: relative;
    display: block;
    padding-block: 8px;
    font-size: clamp(0.6875rem, 3.2vw, 0.8125rem);
    white-space: nowrap;
  }
  .nav-actions { flex-shrink: 0; }
  .nav-actions .btn-sm {
    --btn-py: 7px;
    padding-inline: clamp(10px, 3vw, 14px);
    font-size: clamp(0.6875rem, 3.2vw, 0.8125rem);
    white-space: nowrap;
  }

  /* Página vigente: texto em peso maior + barrinha em gradiente da marca
     embaixo, com brilho suave, que "cresce" do centro ao carregar a
     página. aria-current="page" já vem no HTML de cada página. */
  .nav-links a.is-active { font-weight: 600; }
  .nav-links a.is-active::after {
    content: "";
    position: absolute;
    left: 0;
    right: 0;
    bottom: 2px;
    height: 2px;
    border-radius: var(--radius-pill);
    background: var(--gradient-accent);
    box-shadow: 0 0 10px rgba(79, 102, 255, 0.65);
    transform-origin: center;
    animation: nav-active-in 520ms var(--ease-out) 120ms both;
  }
  @keyframes nav-active-in {
    from { transform: scaleX(0); opacity: 0; }
    to   { transform: scaleX(1); opacity: 1; }
  }

  /* ---- Hero, .cta-stage, 404 — SEM override ----
     100dvh real (regra original, sempre existiu) já é exatamente correto
     aqui: sem canvas escalado, não há nenhuma distorção pra compensar. */

  /* ---- Diferenciais: 4 cards em coluna única ----
     grid-column/grid-row dos modificadores (--lead ocupava a coluna 1
     inteira, --wide ocupava 2 colunas na linha 2) só fazem sentido no
     grid 3-larguras do desktop — resetados pra fluir na ordem natural do
     DOM. --wide também volta pra coluna (ícone em cima do texto, não do
     lado) pra ficar visualmente igual aos outros 3 cards empilhados. */
  .diff-grid {
    grid-template-columns: 1fr;
    grid-template-rows: none;
  }
  .diff-card--lead, .diff-card--wide {
    grid-column: 1;
    grid-row: auto;
  }
  .diff-card--wide { flex-direction: column; }
  .diff-card--wide .card-icon { margin-bottom: 20px; }

  /* ---- Pilares da empresa: 2 colunas → 1 ---- */
  .commitment-list { grid-template-columns: 1fr; }

  /* ---- Demos: pilha vertical normal, sem prender rolagem ----
     assets/js/main.js (initPinCarousel) não ativa .is-pinned abaixo de
     768px — o fallback de acessibilidade que já existia por padrão neste
     arquivo (.demo-stage em coluna, todos os 5 slides sempre visíveis)
     assume sozinho. Só falta reempilhar .demo-pin-inner (hoje um grid de
     2 colunas com áreas nomeadas "copy stage"/"progress stage") numa
     coluna só, e esconder a lista numerada .demo-progress — sem pin não
     existe "avançar pro próximo card" pra ela indicar; a informação que
     ela repetia (número + nome de cada demo) já aparece em cada
     .demo-tag dentro do próprio .demo-slide empilhado. */
  .demo-pin-inner {
    grid-template-columns: minmax(0, 1fr);
    grid-template-areas: "copy" "stage";
    row-gap: 32px;
  }
  /* minmax(0,1fr), não só 1fr, na regra acima — e min-width:0 aqui: 1fr
     sozinho é minmax(auto,1fr), que nunca encolhe a trilha abaixo do
     min-content do conteúdo. Um dos mockups de demo tem texto com
     white-space:nowrap (ex. valores do ERP) — min-content disso é a
     largura do texto inteiro sem quebrar, então a trilha (e a página
     inteira) ficava mais larga que a tela pra caber, criando rolagem
     horizontal (confirmado: 36px de overflow em 375px, bisectado até
     .demo-copy). min-width:0 nos dois itens do grid é o que permite a
     trilha encolher de verdade; o overflow real do texto continua
     tratado onde já existia (.erp-tx-desc, por ex., já tinha isto). */
  .demo-copy, .demo-stage { min-width: 0; }
  .demo-progress { display: none; }
  .demo-hint { display: none; } /* "role pra trocar" não se aplica mais */

  /* ---- Processo: mesma lógica do Demos ----
     .process-stage já empilha em coluna por padrão (fallback sem pin);
     só falta esconder a barra de progresso (parada, sem pin pra
     avançá-la) e devolver o H2 ao título estático em vez do que
     initPinCarousel reescrevia por card ativo (a função nem roda mais
     aqui, então o H2 já fica parado no texto original do HTML — nada a
     fazer nele). */
  .process-progress-track { display: none; }

  /* ---- Processo: destaque de card guiado por scroll ----
     Pedido do cliente: sem o modo fixado (pin, XXI.11.5), os 4 cards ficam
     todos visíveis ao mesmo tempo — o destaque "anda" de card em card
     conforme assets/js/main.js (IntersectionObserver) alterna .is-in-view
     no que estiver mais perto do centro da tela, subindo ou descendo.
     border-color (não border, que mudaria a largura e empurraria o
     conteúdo — border-color troca só a cor, layout nunca se move) +
     transform (elevação) + box-shadow (--shadow-cta, o mesmo tom de azul
     já usado no botão primário — reaproveita o token de design em vez de
     inventar uma cor nova). */
  .process-step {
    transition: transform 400ms var(--ease-out), border-color 400ms var(--ease-out), box-shadow 400ms var(--ease-out);
  }
  .process-step.is-in-view {
    transform: translateY(-6px);
    border-color: var(--accent-solid);
    box-shadow: 0 24px 48px -16px rgba(0, 0, 0, 0.55), var(--shadow-cta);
  }
  @media not all /* reduzir movimento IGNORADO de propósito (decisão do cliente) */ {
    .process-step { transition: border-color 300ms ease, box-shadow 300ms ease; }
    .process-step.is-in-view { transform: none; }
  }

  /* ---- FAQ: visual decorativo escondido, não redimensionado ----
     .faq-visual sangra pra fora da coluna via position:absolute com
     rotação 3D (pedido original pro desktop) — não existe versão
     "encolhida" sensata disso ao lado de um acordeão de largura total;
     era decoração de fundo, então some, e .faq-panel-inner para de
     reservar uma altura mínima pensada pra caber essa imagem atrás. */
  .faq-visual { display: none; }
  .faq-panel-inner { min-height: 0; }
  /* align-items:center (base) fica estranho quando a pergunta quebra em
     3-4 linhas de largura total — "01"/"+" ficam flutuando no meio do
     bloco de texto em vez de alinhados com a primeira linha. */
  .faq-item summary { align-items: flex-start; }
  .faq-item summary .plus { margin-top: 2px; }

  /* ---- CTA: convite em 1 coluna ----
     .cta-float-card mais contido (era min(100%,340px) — quase a tela
     inteira em retrato) e gap reduzido: pedido do cliente pra "organizar"
     esta seção — a imagem sozinha empurrava o botão de ação pra baixo da
     dobra sem necessidade. */
  .cta-intro-grid { grid-template-columns: 1fr; text-align: center; gap: 28px; }
  .cta-intro-visual { order: -1; }
  .cta-float-card { width: min(60%, 220px); }

  /* ---- .cta-stage: altura automática, não mais compartilhada fixa ----
     Bug real reportado pelo cliente ("tela sobe sozinha desenquadrando os
     cards"): a altura fixa de .cta-stage (clamp 640–760px, pensada pro
     conteúdo do desktop) é MENOR que a altura natural de qualquer um dos
     dois painéis já reempilhados em coluna única pro celular (medido:
     ~666px de altura fixa contra ~753px de conteúdo real, dos dois
     lados) — com overflow:hidden (regra original, ainda válida pra
     desktop/tablet onde o conteúdo cabe), a sobra ficava cortada
     silenciosamente: o fundo do botão "VIRA SOLUÇÕES" no convite, e o
     fundo dos próprios cards no painel de soluções — exatamente o
     "desenquadramento" relatado. height:auto deixa a trilha do grid
     (.cta-intro e .cta-solutions continuam as duas com grid-area:1/1,
     sobrepostas) se ajustar ao MAIOR dos dois conteúdos — como as duas
     ocupam a mesma célula e ambas contam pro cálculo de altura
     independente de qual está visível (opacity:0 não tira do fluxo),
     a altura fica igual nos dois estados de qualquer forma, preservando
     o espírito original (nunca a seção mudar de tamanho ao trocar de
     painel) sem precisar de um número fixo adivinhado. */
  .cta-stage { height: auto; overflow: visible; }

  /* ---- "Qual solução você precisa?": título no topo do painel ----
     .cta-panel (base) centraliza verticalmente de propósito pro estado
     "convite" (.cta-intro) — só pro estado de soluções, alinha ao topo.
     O fundo (branco quadriculado → var(--canvas)) e as cores de título/
     botão "Voltar" já foram trocados na regra BASE de `.cta-solutions`/
     `.cta-solutions-title`/`.cta-back` (mais acima neste arquivo) — pedido
     do cliente em 29/09/2026 pra virar global, não só celular (era só
     celular até então, ver XXI.11.7.1). Nada a repetir aqui. */
  .cta-panel.cta-solutions {
    align-items: flex-start;
  }

  /* ---- Cards de solução: retângulo horizontal + carrossel ----
     De grade de 5 colunas (tablet/desktop, inalterada) pra um trilho
     horizontal com scroll-snap nativo, sem JS (Parte VI.7 — economia de
     complexidade: o navegador resolve swipe + parada suave sozinho). Cada
     cartão ocupa ~88% da largura real disponível (aqui já É a largura
     real — sem canvas escalado, --container-px/100vw resolvem contra o
     viewport de verdade, sem precisar de nenhuma correção). */
  .solution-grid {
    display: flex;
    grid-template-columns: none;
    overflow-x: auto;
    overscroll-behavior-x: contain;
    scroll-snap-type: x mandatory;
    -webkit-overflow-scrolling: touch;
    gap: 14px;
    margin-inline: calc(var(--container-px) * -1);
    padding-inline: var(--container-px);
    padding-bottom: 8px;
    scrollbar-width: none;
  }
  .solution-grid::-webkit-scrollbar { display: none; }

  /* Pedido do cliente (29/09/2026): sequência padronizada em qualquer
     cartão — título no topo; lista de composição do pacote embaixo dele,
     à esquerda; "Quero essa solução" sempre no canto inferior direito,
     não mais lado a lado com o título (era assim na versão anterior desta
     correção). grid-template-rows com 1fr na linha da lista: índice e
     título ficam com a altura natural deles, a lista de benefícios ganha
     o espaço sobrando (cartões com menos itens, como "App privado", não
     empurram o preço/botão pra mais perto do meio — o espaço vazio sobra
     ABAIXO da lista, não entre ela e o rodapé do cartão), e preço+botão
     ficam sempre na última linha, colados na base do cartão. */
  .solution-card {
    display: grid;
    grid-template-columns: 1fr auto;
    grid-template-rows: auto auto 1fr auto;
    column-gap: 16px;
    row-gap: 10px;
    min-height: 300px;
    padding: 28px 24px;
  }
  .solution-index { grid-column: 1 / -1; font-size: 1.375rem; }
  .solution-card h3 { grid-column: 1 / -1; margin-top: 0; }
  /* display:contents dissolve o wrapper .solution-body — sem isto,
     .solution-benefits e .solution-price ficariam presos dentro de UM
     item de grid só (o próprio .solution-body), sem poder ocupar linhas
     diferentes do card. Nenhum elemento é removido do DOM, só deixa de
     participar do layout como caixa própria (a mesma técnica já usada em
     .nav-menu, XXI.11.2, pra tablet/desktop). */
  .solution-body { display: contents; }
  .solution-benefits { grid-column: 1 / -1; }
  .solution-price {
    grid-column: 1;
    align-self: end;
    margin-top: 0;
    padding-top: 0;
    border-top: none;
    white-space: normal;
  }
  .solution-cta {
    grid-column: 2;
    align-self: end;
    justify-self: end;
    margin-top: 0;
  }

  /* ---- Rodapé: 3 colunas → 1 ---- */
  .footer-top { grid-template-columns: 1fr; }
}
