/* ============================================================
   Vision — site público
   Depende de tokens.css (variáveis de marca, fontes, escala).
   Prefixo .v-* para todo componente, como no UI kit do design system.
   ============================================================ */

*,
*::before,
*::after { box-sizing: border-box; }

html { scroll-behavior: smooth; }

body {
  margin: 0;
  background: var(--vision-black);
  color: var(--vision-paper);
  font-family: var(--font-sans);
  font-size: var(--fs-body);
  line-height: var(--lh-body);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

a { color: inherit; text-decoration: none; }
img, svg { max-width: 100%; }
button { font: inherit; }

/* A home inteira é uma superfície escura: fixa os tons de texto do tema dark
   sem depender do atributo [data-theme], que ainda não temos no site. */
.v-page {
  min-height: 100vh;
  --fg-1: #F5F4F7;
  --fg-2: #C8C8D2;
  --fg-3: #8A8A95;
}

/* ============================= FUNDO ANIMADO ============================= */

/* Atmosfera da página: duas manchas de roxo, maiores que a tela, passeando
   devagar atrás de tudo. Fica `fixed` para o movimento não depender da rolagem
   — o pedido é um gradiente que anda sozinho, mesmo com a página parada — e em
   `z-index: -1` para pintar sobre o preto do `body` e debaixo do conteúdo.

   São dois elementos movidos por `transform`, e não um `background-position`
   animado: transformar é o que o compositor resolve sem repintar, e uma
   animação que nunca termina não pode custar pintura a cada quadro. As manchas
   são radiais, já nascem suaves e dispensam o `filter: blur()` dos brilhos do
   hero, que numa camada do tamanho da tela pesaria. */
.v-aura {
  position: fixed;
  inset: 0;
  z-index: -1;
  /* As manchas passeiam para fora da borda; sem o corte a página ganharia
     rolagem lateral. */
  overflow: hidden;
  pointer-events: none;
}

.v-aura::before,
.v-aura::after {
  content: "";
  position: absolute;
  border-radius: 50%;
  /* A camada anima o tempo todo: vale manter a promessa de pista própria. */
  will-change: transform;
}

/* De cima à esquerda, no roxo claro da marca. */
.v-aura::before {
  width: 90vmax; height: 90vmax;
  left: -30vmax; top: -34vmax;
  background: radial-gradient(circle,
    rgba(169, 147, 255, 0.17) 0%,
    rgba(91, 63, 224, 0.11) 42%,
    transparent 70%);
  animation: v-aura-a 44s linear infinite;
}

/* De baixo à direita, no roxo forte. As duas se cruzam no meio da tela em
   tempos diferentes, e é do encontro delas que vem a variação do fundo. */
.v-aura::after {
  width: 74vmax; height: 74vmax;
  right: -26vmax; bottom: -30vmax;
  background: radial-gradient(circle,
    rgba(91, 63, 224, 0.20) 0%,
    rgba(169, 147, 255, 0.08) 45%,
    transparent 70%);
  animation: v-aura-b 61s linear infinite;
}

/* Circuitos fechados — o último quadro repete o primeiro, então a volta não
   tem emenda. `linear` no lugar de uma curva de saída porque o movimento é
   para ser constante: qualquer `ease` daria a impressão de parar em cada ponto
   do caminho. Os períodos são longos e sem divisor comum, para o par não
   repetir o mesmo encontro a cada volta. */
@keyframes v-aura-a {
  0%   { transform: translate3d(0, 0, 0) scale(1); }
  25%  { transform: translate3d(9vmax, 7vmax, 0) scale(1.09); }
  50%  { transform: translate3d(15vmax, -3vmax, 0) scale(1.02); }
  75%  { transform: translate3d(6vmax, -9vmax, 0) scale(1.07); }
  100% { transform: translate3d(0, 0, 0) scale(1); }
}

@keyframes v-aura-b {
  0%   { transform: translate3d(0, 0, 0) scale(1.04); }
  25%  { transform: translate3d(-11vmax, -6vmax, 0) scale(1); }
  50%  { transform: translate3d(-16vmax, 5vmax, 0) scale(1.1); }
  75%  { transform: translate3d(-5vmax, 10vmax, 0) scale(1.03); }
  100% { transform: translate3d(0, 0, 0) scale(1.04); }
}

.v-container {
  width: 100%;
  max-width: 1280px;
  margin: 0 auto;
  padding: 0 32px;
}

/* Acessibilidade -------------------------------------------------------- */

.v-skip-link {
  position: absolute;
  left: 16px;
  top: -100px;
  z-index: 100;
  padding: 12px 20px;
  border-radius: var(--radius-pill);
  background: var(--vision-purple);
  color: var(--vision-black);
  font-weight: var(--fw-semibold);
  transition: top var(--dur-2) var(--ease-standard);
}
.v-skip-link:focus { top: 16px; }

:where(a, button, [tabindex]):focus-visible {
  outline: 2px solid var(--vision-purple);
  outline-offset: 3px;
  border-radius: var(--radius-xs);
}

.v-visually-hidden {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* ============================= NAV ============================= */

.v-nav {
  position: sticky;
  top: 0;
  z-index: 50;
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: 24px;
  padding: 18px 32px;
  background: rgba(10, 10, 10, 0.6);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  border-bottom: 1px solid rgba(255, 255, 255, 0.06);
}

.v-nav__brand {
  justify-self: start;
  display: inline-flex;
  align-items: center;
  /* Logotipo branco: o SVG é inline com fill="currentColor", então a cor vem
     daqui. Os arquivos "-white" do design system não servem por si só — são
     idênticos aos "-black" e não declaram fill (renderizam preto). */
  color: var(--vision-white);
}
.v-nav__brand svg { height: 26px; width: auto; display: block; }

.v-nav__links {
  justify-self: center;
  display: flex;
  align-items: center;
  gap: 28px;
  font-size: 14px;
  font-weight: var(--fw-medium);
}
.v-nav__link {
  position: relative;
  color: var(--fg-2);
  padding: 4px 0;
  transition: color var(--dur-2) var(--ease-standard);
}
.v-nav__link:hover { color: var(--vision-white); }
.v-nav__link[aria-current="true"] { color: var(--vision-white); }
.v-nav__link[aria-current="true"]::after {
  content: "";
  position: absolute;
  left: 0; right: 0; bottom: -6px;
  height: 1.5px;
  background: var(--vision-purple);
}

.v-nav__actions { justify-self: end; display: flex; align-items: center; gap: 14px; }

/* Alternador de idioma — usa a view set_language do Django, então é um form. */
.v-lang { display: flex; align-items: center; gap: 2px; }
.v-lang__btn {
  padding: 6px 8px;
  border-radius: var(--radius-sm);
  font-size: 13px;
  font-weight: var(--fw-semibold);
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--fg-3);
  transition: color var(--dur-2) var(--ease-standard);
}
.v-lang__btn:hover { color: var(--vision-white); }
.v-lang__btn[aria-current="true"] { color: var(--vision-purple); }
.v-lang__sep { color: var(--fg-4); font-size: 12px; }

.v-nav__toggle {
  display: none;
  background: none;
  border: 1px solid rgba(255, 255, 255, 0.2);
  border-radius: var(--radius-sm);
  color: var(--vision-paper);
  padding: 8px;
  cursor: pointer;
  line-height: 0;
}

/* ============================= BOTÕES ============================= */

.v-btn {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 14px 24px;
  border: none;
  border-radius: var(--radius-pill);
  font-family: var(--font-sans);
  font-size: var(--fs-body-sm);
  font-weight: var(--fw-semibold);
  letter-spacing: var(--ls-button);
  white-space: nowrap;   /* rótulo de 2+ palavras não pode quebrar: a pílula vira círculo */
  cursor: pointer;
  transition: background var(--dur-2) var(--ease-standard),
              color var(--dur-2) var(--ease-standard),
              border-color var(--dur-2) var(--ease-standard),
              transform var(--dur-2) var(--ease-standard);
}
.v-btn--sm { padding: 10px 18px; font-size: 14px; }

.v-btn--accent { background: var(--vision-purple); color: var(--vision-black); }
.v-btn--accent:hover { background: var(--vision-white); transform: translateY(-1px); }
.v-btn--accent:active { background: var(--vision-purple-strong); color: var(--vision-white); }

.v-btn--ghost {
  background: transparent;
  color: var(--vision-paper);
  border: 1px solid rgba(255, 255, 255, 0.2);
}
.v-btn--ghost:hover { border-color: var(--vision-purple); color: var(--vision-purple); }
.v-btn--ghost:active { border-color: var(--vision-purple-strong); color: var(--vision-purple-strong); }

.v-btn--dark { background: var(--vision-black); color: var(--vision-white); }

/* Contorno em gradiente sobre fundo transparente.
   `border` não aceita gradiente e `border-image` ignora border-radius (a pílula
   viraria retângulo). Por isso a borda é um pseudo-elemento preenchido com o
   gradiente e mascarado para mostrar só o anel — o miolo fica de fato
   transparente e o glow do hero aparece através dele. */
.v-btn--gradient {
  position: relative;
  background: transparent;
  color: var(--vision-paper);
}
.v-btn--gradient::before {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  padding: 1.5px;          /* espessura da borda */
  background: linear-gradient(
    115deg,
    var(--vision-purple-strong) 0%,
    var(--vision-purple) 45%,
    var(--vision-purple-light) 100%
  );
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
          mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
          mask-composite: exclude;
  pointer-events: none;
}
.v-btn--gradient:hover { background: rgba(169, 147, 255, 0.12); color: var(--vision-white); }
.v-btn--gradient:active { background: rgba(169, 147, 255, 0.22); }

/* Ponto de radar: o círculo sólido mais dois anéis que se expandem. Os anéis
   ficam defasados em meio ciclo, o que dá a leitura de varredura contínua em
   vez de piscada. */
.v-pulse {
  position: relative;
  display: inline-block;
  width: 7px;
  height: 7px;
  flex-shrink: 0;
  border-radius: 50%;
  background: var(--vision-purple);
}
.v-pulse::before,
.v-pulse::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: 50%;
  border: 1.5px solid var(--vision-purple);
  box-shadow: 0 0 6px rgba(169, 147, 255, 0.45);
  animation: v-radar 2.4s var(--ease-out) infinite;
}
.v-pulse::after { animation-delay: 1.2s; }

@keyframes v-radar {
  /* Sem parada intermediária de opacidade: o esmaecimento acompanha toda a
     expansão, senão o anel some antes de chegar na borda e a varredura
     praticamente não é percebida. */
  from { transform: scale(1);   opacity: 0.9; }
  to   { transform: scale(3.6); opacity: 0; }
}

.v-btn--dark:hover { background: var(--vision-white); color: var(--vision-black); transform: translateY(-1px); }

.v-icon { width: 16px; height: 16px; stroke-width: 1.5; flex-shrink: 0; }

/* ============================= HERO ============================= */

.v-hero {
  position: relative;
  padding: 88px 0 120px;
  overflow: hidden;
  /* Medida da coluna de abertura: cerca de 60% da tela. O limite em px segura
     a linha em telas largas, onde o container já parou de crescer. Vive aqui
     porque o título e a linha fina abaixo dele têm de andar juntos. */
  --v-hero-measure: min(60vw, 760px);
}
.v-hero__inner { position: relative; z-index: 2; }

/* Fundo da abertura: o gradiente do ShaderGradient (hero-gradient.js) num
   canvas, sobre uma imitação dele em CSS — o quase preto com as duas manchas
   de azul que o shader mostra no primeiro quadro. É o que fica sem script ou
   sem WebGL, e o que aparece no instante antes do canvas, que entra por cima
   sem mudar o tom. A máscara apaga a borda de baixo para a hero se dissolver
   no fundo da página em vez de terminar numa linha. */
.v-hero__bg {
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  background:
    radial-gradient(40% 50% at 22% 38%, rgba(18, 18, 150, 0.5), transparent 70%),
    radial-gradient(45% 55% at 90% 85%, rgba(18, 18, 160, 0.55), transparent 70%),
    #05050d;
  -webkit-mask-image: linear-gradient(to bottom, #000 70%, transparent);
          mask-image: linear-gradient(to bottom, #000 70%, transparent);
}

.v-hero__canvas {
  position: absolute;
  inset: 0;
  display: block;
  width: 100%;
  height: 100%;
  opacity: 0;
  transition: opacity 900ms var(--ease-standard);
}
.v-hero__canvas.is-ready { opacity: 1; }

.v-eyebrow {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-size: var(--fs-eyebrow);
  font-weight: var(--fw-semibold);
  letter-spacing: var(--ls-caps);
  text-transform: uppercase;
  color: var(--vision-purple);
  margin: 0 0 24px;
}
.v-eyebrow::before { content: "|"; }

.v-hero__title {
  font-family: var(--font-display);
  font-size: clamp(44px, 6.4vw, 92px);
  font-weight: var(--fw-bold);
  line-height: 1.02;
  letter-spacing: -0.035em;
  margin: 0 0 28px;
  max-width: var(--v-hero-measure);
}
.v-hero__title em { font-style: italic; font-weight: var(--fw-regular); color: var(--vision-purple); }

/* Fecha o título e abre o texto de apoio, na mesma medida da coluna. */
.v-hero__rule {
  width: var(--v-hero-measure);
  max-width: 100%;
  height: 1px;
  background: rgba(255, 255, 255, 0.28);
  margin: 0 0 28px;
}

.v-hero__sub {
  font-size: clamp(17px, 2vw, 19px);
  line-height: var(--lh-body);
  color: var(--fg-2);
  max-width: 52ch;
  margin: 0 0 36px;
}
/* Mesma fonte e mesmo tamanho do restante do subtexto; o destaque vem da cor,
   do negrito e do itálico (sintetizado, já que a Darker Grotesque não tem face
   itálica própria). */
.v-hero__sub em {
  font-style: italic;
  font-weight: 700;
  color: var(--vision-purple);
}

.v-hero__ctas { display: flex; flex-wrap: wrap; gap: 12px; }

/* Entrada da abertura. O título já sobe palavra por palavra (`.v-words`); o
   resto do bloco entra em volta dele, numa sequência só: o olho junto com a
   primeira palavra, a linha se desenhando quando o título vai pela metade, e
   o subtexto e os botões, um de cada vez, logo atrás dela.

   Como as palavras, é tudo em CSS e roda na primeira pintura — a abertura já
   está na tela quando a página carrega, então não há rolagem a esperar nem
   motivo para depender do script. O curso de 12px e os 600ms são os do
   `.v-reveal` (spec §10), para a hero não destoar das seções abaixo dela.

   `backwards`, e não `both`: o estado inicial vale durante a espera, mas
   terminada a entrada a animação larga o `transform` — senão ela ficaria
   presa por cima do `translateY(-1px)` do hover dos botões. Com movimento
   reduzido o bloco de `prefers-reduced-motion` zera duração e espera, e tudo
   já nasce no lugar. */
.v-hero__lead {
  --v-hero-in-linha: 560ms;
  --v-hero-in-passo: 130ms;
}

.v-hero__lead > .v-eyebrow,
.v-hero__sub,
.v-hero__ctas > * {
  animation: v-hero-in var(--dur-4) var(--ease-standard) backwards;
}

.v-hero__sub { animation-delay: calc(var(--v-hero-in-linha) + var(--v-hero-in-passo)); }
.v-hero__ctas > :nth-child(1) { animation-delay: calc(var(--v-hero-in-linha) + var(--v-hero-in-passo) * 2); }
.v-hero__ctas > :nth-child(2) { animation-delay: calc(var(--v-hero-in-linha) + var(--v-hero-in-passo) * 3); }

@keyframes v-hero-in {
  from { opacity: 0; transform: translateY(12px); }
}

/* A linha não sobe: ela se desenha da esquerda para a direita, como um
   sublinhado fechando o título. */
.v-hero__rule {
  transform-origin: left center;
  animation: v-hero-rule 900ms var(--ease-out) var(--v-hero-in-linha) backwards;
}

@keyframes v-hero-rule {
  from { transform: scaleX(0); }
}

.v-hero__stats {
  display: grid;
  /* Colunas do tamanho do conteúdo, e não em fração do container: assim os três
     ficam agrupados no meio da tela, em vez de esticados até as bordas. */
  grid-template-columns: repeat(3, auto);
  justify-content: center;
  gap: 24px clamp(56px, 11vw, 168px);
  margin-top: 80px;
  padding-top: 32px;
  border-top: 1px solid rgba(255, 255, 255, 0.08);
}
.v-stat { display: flex; flex-direction: column; align-items: center; gap: 6px; text-align: center; }
.v-stat__value {
  /* Origem do confete do contador de cafés. */
  position: relative;
  font-family: var(--font-display);
  font-size: 36px;
  font-weight: var(--fw-bold);
  letter-spacing: var(--ls-heading);
  line-height: 1;
}
.v-stat__value em { font-style: italic; font-weight: var(--fw-regular); color: var(--vision-purple); }
/* Último dígito do número que segue subindo: entra de baixo, com a mesma
   máscara e o mesmo quadro-chave das palavras do título, para a troca não ser
   um corte seco. Mais curto que o título, porque se repete a cada passo. */
.v-digit {
  display: inline-block;
  clip-path: inset(-0.3em -0.12em 0 -0.12em);
  margin-bottom: -0.16em;
}
.v-digit__inner {
  display: inline-block;
  padding-bottom: 0.16em;
  transform: translateY(120%);
  animation: v-word-up 340ms var(--ease-out) forwards;
}

/* Confete: pedaços criados pelo JavaScript ao contador de cafés bater em 1000,
   espalhados em leque a partir do centro do número e removidos no fim. Cada um
   traz a própria direção, giro e cor em variáveis inline. */
.v-confetti {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 5px;
  height: 9px;
  border-radius: 1px;
  background: var(--cor);
  pointer-events: none;
  opacity: 0;
  /* 900ms é o mesmo valor de CONFETE_DURACAO no site.js, que agenda a
     remoção dos pedaços — mexer aqui pede mexer lá. */
  animation: v-confetti 900ms var(--ease-out) var(--atraso) forwards;
}

@keyframes v-confetti {
  0%   { opacity: 1; transform: translate3d(-50%, -50%, 0) rotate(0deg); }
  60%  { opacity: 1; }
  100% {
    opacity: 0;
    transform: translate3d(calc(-50% + var(--dx)), calc(-50% + var(--dy)), 0) rotate(var(--rot));
  }
}

.v-stat__label {
  font-size: var(--fs-eyebrow);
  font-weight: var(--fw-medium);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--fg-3);
}

/* ============================= SEÇÕES ============================= */

.v-section { padding: 100px 0; }
.v-section__head { max-width: 900px; margin-bottom: 64px; }
.v-section__eyebrow {
  font-size: var(--fs-eyebrow);
  font-weight: var(--fw-semibold);
  letter-spacing: var(--ls-caps);
  text-transform: uppercase;
  color: var(--section-accent, var(--vision-purple));
  margin: 0 0 18px;
}
.v-section__title {
  font-family: var(--font-display);
  font-size: clamp(36px, 5vw, 72px);
  font-weight: var(--fw-bold);
  line-height: 1;
  letter-spacing: var(--ls-display);
  margin: 0 0 18px;
}
.v-section__title em { font-style: italic; font-weight: var(--fw-regular); color: var(--section-accent, var(--vision-purple)); }
.v-section__sub {
  font-size: var(--fs-h5);
  line-height: var(--lh-body);
  color: var(--fg-2);
  max-width: 60ch;
  margin: 0;
}

/* Superfície escura própria da seção — daí os tons de texto, as bordas e o
   realce virem redeclarados aqui, e não herdados da página.

   `--section-bg` e `--section-accent` existem para os componentes que vivem nas
   duas superfícies: o realce é o roxo claro no escuro e o forte no claro (o
   claro não tem contraste sobre papel), e quem precisa da cor do fundo — a aba
   selecionada, que inverte — lê daqui em vez de fixar preto ou branco. Assim
   virar uma seção de lado é trocar a classe, e não caçar cor por cor. */
.v-section--dark {
  --section-bg: var(--vision-black);
  --section-accent: var(--vision-purple);
  /* Transparente, e não o preto do token: o preto da seção é o mesmo do
     `body`, então pintá-lo só serviria para tapar o gradiente do `.v-aura`. O
     token continua valendo para quem precisa da cor da superfície — a aba
     selecionada, que inverte, lê daqui. */
  background: transparent;
  color: var(--vision-paper);
  --fg-1: #F5F4F7;
  --fg-2: #C8C8D2;
  --fg-3: #8A8A95;
  --fg-4: #5C5C66;
  /* Bordas e realces do tema escuro: os do `:root` são pretos translúcidos e
     sumiriam sobre o preto da seção. */
  --border-1: rgba(255, 255, 255, 0.08);
  --border-2: rgba(255, 255, 255, 0.16);
  --surface-overlay: rgba(255, 255, 255, 0.05);
}

/* ============================= NOSSO DNA ============================= */

/* Duas colunas de peso igual: texto à esquerda, card à direita. `start` no
   alinhamento para o card não esticar até a altura do texto. */
.v-dna {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 64px;
  align-items: start;
}

/* Dentro da coluna a abertura não precisa do respiro de uma seção inteira, e a
   largura máxima de 900px é maior que a própria coluna. */
.v-dna__text .v-section__head { max-width: none; margin-bottom: 40px; }

.v-dna__eyebrow {
  font-size: var(--fs-eyebrow);
  font-weight: var(--fw-semibold);
  letter-spacing: var(--ls-caps);
  text-transform: uppercase;
  /* Mais apagado que o olho da seção — são dois olhos na mesma tela e só o de
     cima é o roxo da marca. */
  color: var(--fg-3);
  margin: 0 0 16px;
}

.v-dna__lead {
  font-family: var(--font-display);
  font-size: clamp(26px, 2.8vw, 40px);
  font-weight: var(--fw-bold);
  line-height: 1.1;
  letter-spacing: var(--ls-heading);
  margin: 0 0 28px;
  max-width: 22ch;
}
.v-dna__lead em { font-style: italic; font-weight: var(--fw-regular); color: var(--vision-purple); }

.v-dna__body {
  font-size: var(--fs-body-lg);
  line-height: var(--lh-body);
  color: var(--fg-2);
  max-width: 52ch;
}
.v-dna__body p { margin: 0 0 20px; }
.v-dna__body p:last-child { margin-bottom: 0; }

/* A linha solta do meio é a virada do texto: ganha respiro e a barra roxa. A
   barra é pseudo-elemento, e não `border-left`, porque ela é desenhada de cima
   para baixo na entrada da seção — e borda não cresce de uma ponta à outra. */
.v-dna__accent {
  position: relative;
  padding-left: 20px;
  margin: 28px 0;
}
.v-dna__accent::before {
  content: "";
  position: absolute;
  left: 0; top: 0; bottom: 0;
  width: 2px;
  background: var(--vision-purple);
}
.v-dna__accent em {
  font-family: var(--font-display);
  font-style: italic;
  font-weight: var(--fw-regular);
  font-size: var(--fs-h4);
  line-height: 1.25;
  color: var(--vision-paper);
}

/* --- Entrada da seção ---------------------------------------------------- */

/* A coluna de texto é um `.v-reveal--stagger`: seus quatro blocos (abertura,
   olho, frase de missão e corpo) sobem em sequência. O que segue são as
   demoras que não cabem na cascata genérica — detalhes que só esta seção tem.

   Os tempos aqui são absolutos de propósito: eles entram depois do último
   bloco da cascata, e amarrá-los ao passo dela faria qualquer ajuste de ritmo
   bagunçar o fim da sequência. */

/* O "-se" chega depois do resto do título: é ele que vira "diferencie" em
   "diferencie-se", e o intervalo entre os dois faz a palavra se completar na
   tela em vez de já nascer pronta. `inline-block` para poder deslizar; o
   espaço dele é guardado o tempo todo, então nada reflui quando aparece. */
.v-dna__text .v-section__title em {
  display: inline-block;
  opacity: 0;
  transform: translateX(-0.3em);
  transition: opacity var(--dur-4) var(--ease-out) 520ms,
              transform var(--dur-4) var(--ease-out) 520ms;
}
.v-dna__text.is-visible .v-section__title em { opacity: 1; transform: none; }

/* A barra roxa é riscada de cima para baixo, já com o parágrafo na tela. */
.v-dna__accent::before {
  transform: scaleY(0);
  transform-origin: top;
  transition: transform var(--dur-4) var(--ease-out) 840ms;
}
.v-dna__text.is-visible .v-dna__accent::before { transform: scaleY(1); }

/* O traço do contorno do card, de 0% a 100% da volta. Vai registrado porque
   variável comum não interpola: sem o `@property` o contorno apareceria
   inteiro de uma vez. Onde `@property` não existe é exatamente isso que
   acontece — o valor inicial declarado na regra abaixo mantém o gradiente
   válido, a troca para 100% é instantânea e o card ganha a borda sem o risco.
   Nada fica invisível, só sem o desenho. */
@property --v-card-traco {
  syntax: "<percentage>";
  initial-value: 0%;
  inherits: false;
}

/* O card não chega pronto: primeiro o contorno é riscado em volta dele, a
   partir do topo e no sentido do relógio, e só quando o traço fecha a volta é
   que o fundo e o conteúdo preenchem o que ele cercou. É por isso que este
   card não sobe nem esmaece como os outros `.v-reveal` — ele se desenha onde
   está, e sair do lugar enquanto se desenha seria um movimento a mais. */
.v-dna .v-profile-card {
  /* Os quatro tempos da entrada, contados do momento em que a seção entra na
     tela. Ficam em variáveis porque são seis declarações que têm de concordar
     entre si — mexer no ritmo é mexer aqui, e não caçar milissegundo por
     milissegundo. */
  --v-card-atraso: 180ms;     /* deixa a coluna de texto começar primeiro */
  --v-card-volta: 1200ms;     /* o traço dando a volta no card */
  --v-card-preenche: 1150ms;  /* fundo e conteúdo, já no fim do traço */
  --v-card-borda: 1380ms;     /* a borda definitiva, com o traço fechado */

  position: relative;
  /* O card em si está lá desde o início: é dentro dele que o desenho acontece.
     Daí ele dispensar a opacidade e o deslocamento do `.v-reveal`. */
  opacity: 1;
  transform: none;
  /* Enquanto o traço corre, o card é só contorno: nem fundo, nem borda. */
  background: transparent;
  border-color: transparent;
  transition: background 620ms var(--ease-out) var(--v-card-preenche),
              border-color 520ms var(--ease-out) var(--v-card-borda);
}
.v-dna .v-profile-card.is-visible {
  background: var(--vision-ink);
  border-color: rgba(255, 255, 255, 0.08);
}

/* O risco: um anel de 1px pintado por um gradiente cônico que abre de 0 a 360°.
   O `mask` recorta o miolo e deixa só a moldura — é o que faz o traço
   acompanhar o arredondamento do card, coisa que quatro linhas retas não
   fariam. `from -90deg` para começar no topo; `linear` porque um traço que se
   desenha não acelera no caminho. */
.v-dna .v-profile-card::before {
  content: "";
  position: absolute;
  inset: 0;
  /* Declarado aqui, e não só no `@property`: é este valor que mantém o
     gradiente cônico válido onde `@property` não existe. */
  --v-card-traco: 0%;
  /* Um pixel menos que o raio do card: o anel vive dentro da borda, e é por
     esse arredondamento de dentro que o `overflow: hidden` do card corta. */
  border-radius: calc(var(--radius-lg) - 1px);
  padding: 1px;
  background: conic-gradient(from -90deg,
    var(--vision-purple) 0%,
    var(--vision-purple) var(--v-card-traco),
    transparent var(--v-card-traco));
  -webkit-mask: linear-gradient(#000, #000) content-box,
                linear-gradient(#000, #000);
  -webkit-mask-composite: xor;
          mask: linear-gradient(#000, #000) content-box,
                linear-gradient(#000, #000);
          mask-composite: exclude;
  pointer-events: none;
  transition: --v-card-traco var(--v-card-volta) linear var(--v-card-atraso),
              opacity 520ms var(--ease-out) var(--v-card-borda);
}
/* A volta se fecha e, fechada, o traço roxo se apaga — quem fica no lugar dele
   é a borda discreta do card. A espera da opacidade (`--v-card-borda`) é o que
   põe o apagar depois do desenho, e não junto dele. */
.v-dna .v-profile-card.is-visible::before {
  --v-card-traco: 100%;
  opacity: 0;
}

/* O conteúdo é o preenchimento: cabeçalho, corpo e rodapé chegam junto com o
   fundo, não antes — enquanto o card é só contorno, não há nada dentro. */
.v-dna .v-profile-card > * {
  opacity: 0;
  transition: opacity 620ms var(--ease-out) var(--v-card-preenche);
}
.v-dna .v-profile-card.is-visible > * { opacity: 1; }

/* Sem JS nada ganha `.is-visible`, e a seção não pode ficar pela metade. */
.no-js .v-dna__text .v-section__title em { opacity: 1; transform: none; transition: none; }
.no-js .v-dna__accent::before { transform: scaleY(1); transition: none; }
.no-js .v-dna .v-profile-card {
  opacity: 1;
  transform: none;
  background: var(--vision-ink);
  border-color: rgba(255, 255, 255, 0.08);
  transition: none;
}
.no-js .v-dna .v-profile-card > * { opacity: 1; transition: none; }
/* Sem o desenho o anel não tem o que fazer, e a borda do card basta. */
.no-js .v-dna .v-profile-card::before { display: none; }

/* Card em retrato: cabeçalho, linha divisória e corpo empilhados. Sem padding
   no card — quem espaça são o cabeçalho e o corpo, para a linha entre os dois
   correr de ponta a ponta. */
.v-profile-card {
  display: flex;
  flex-direction: column;
  background: var(--vision-ink);
  border: 1px solid rgba(255, 255, 255, 0.08);
  border-radius: var(--radius-lg);
  overflow: hidden;
}

.v-profile-card__head {
  padding: 24px 28px;
  border-bottom: 1px solid rgba(255, 255, 255, 0.12);
}

/* Perfil resumido, abrindo o corpo do card: avatar à esquerda, nome e @ à
   direita dele. */
.v-profile-card__profile { display: flex; align-items: center; gap: 12px; }

.v-profile-card__avatar {
  flex: none;
  display: grid;
  place-items: center;
  width: 44px; height: 44px;
  border-radius: 50%;
  background: var(--vision-white);
  color: var(--vision-black);   /* o logotipo é inline e herda a cor daqui */
}
.v-profile-card__avatar svg { width: 24px; height: 24px; display: block; }

.v-profile-card__id { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.v-profile-card__name {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  font-size: var(--fs-body-sm);
  font-weight: var(--fw-bold);
  line-height: 1.2;
  color: var(--vision-paper);
}
/* Azul do selo do Instagram, não da paleta Vision: é a citação de uma marca. */
.v-profile-card__badge { width: 15px; height: 15px; color: #3897F0; flex: none; }
.v-profile-card__handle {
  font-size: var(--fs-body-sm);
  line-height: 1.2;
  color: var(--fg-3);
}

.v-profile-card__phrase {
  margin: 0 0 0 auto;   /* auto à esquerda: a frase encosta na direita */
  text-align: right;
  /* Uma linha só, no mesmo corpo do texto da coluna da esquerda. */
  white-space: nowrap;
  font-family: var(--font-sans);
  font-weight: var(--fw-regular);
  font-size: var(--fs-body-lg);
  line-height: 1.3;
  color: var(--fg-2);
}

.v-profile-card__body {
  flex: 1;
  display: flex;
  flex-direction: column;
  padding: 28px;
}

.v-profile-card__spacer { flex: 1; min-height: 32px; }

/* Rodapé: a linha de cima é a mesma do cabeçalho — como o card não tem padding
   próprio, ela corre de ponta a ponta. */
.v-profile-card__foot {
  display: flex;
  justify-content: flex-end;
  padding: 20px 28px;
  border-top: 1px solid rgba(255, 255, 255, 0.12);
  color: var(--vision-white);
}
.v-profile-card__foot svg { display: block; width: 96px; height: auto; }

.v-profile-card__text {
  margin-top: 28px;
  font-size: var(--fs-body);
  line-height: var(--lh-body);
  color: var(--fg-2);
}
.v-profile-card__text p { margin: 0 0 16px; }
.v-profile-card__text p:last-child { margin-bottom: 0; }
/* Exceção ao destaque serifado em itálico usado no resto do site: dentro do
   card o realce é a própria fonte do texto, em negrito. */
.v-profile-card__text em {
  font-family: inherit;
  font-style: normal;
  font-weight: var(--fw-bold);
  color: var(--vision-purple);
}

/* Superfície clara. `.v-page` fixa os tons do tema escuro para a página toda,
   então a seção precisa devolver os do tema claro — inclusive o roxo, que na
   versão clara da marca é o forte: o roxo claro não tem contraste sobre papel. */
.v-section--light {
  --section-bg: var(--vision-paper);
  --section-accent: var(--vision-purple-strong);
  background: var(--section-bg);
  color: var(--vision-ink);
  --fg-1: var(--vision-ink);
  --fg-2: var(--vision-graphite);
  --fg-3: var(--vision-slate);
  --fg-4: var(--vision-mist);
}

/* ========================= GRADIENTE DE SEÇÃO ========================= */

/* Gradiente de uma seção só, por cima da atmosfera da página: duas manchas de
   roxo bem diluído, uma de cada lado, balançando de leve. O movimento é curto
   e em `ease-in-out` de propósito — o `.v-aura` é o que corre atrás de tudo,
   sem parar; este aqui respira, para dar corpo à seção sem disputar atenção
   com o que está escrito nela.

   Mora fora do `.v-section--dark` porque é independente da superfície: um dia
   uma seção clara pode querer o mesmo, trocando só as cores das manchas. */
.v-section--glow {
  position: relative;
  /* As manchas passam das bordas da seção; sem o corte, elas vazariam para as
     seções vizinhas e o gradiente deixaria de ser desta. */
  overflow: hidden;
}

/* O conteúdo sobe acima das manchas. É preciso dizer: as manchas são
   posicionadas, e sem isto pintariam sobre o texto, não atrás dele. */
.v-section--glow > * { position: relative; z-index: 1; }

.v-section--glow::before,
.v-section--glow::after {
  content: "";
  position: absolute;
  border-radius: 50%;
  pointer-events: none;
  will-change: transform;
}

/* À esquerda, atrás da coluna de texto, no roxo claro da marca. */
.v-section--glow::before {
  left: -14%; top: -18%;
  width: 62%; height: 95%;
  background: radial-gradient(circle,
    rgba(169, 147, 255, 0.13) 0%,
    rgba(169, 147, 255, 0.04) 45%,
    transparent 72%);
  animation: v-glow-a 24s ease-in-out infinite alternate;
}

/* À direita, atrás do card, no roxo forte — é o lado que pede mais peso. */
.v-section--glow::after {
  right: -16%; bottom: -22%;
  width: 56%; height: 85%;
  background: radial-gradient(circle,
    rgba(91, 63, 224, 0.20) 0%,
    rgba(91, 63, 224, 0.06) 48%,
    transparent 72%);
  animation: v-glow-b 31s ease-in-out infinite alternate;
}

/* `alternate`: vai e volta pelo mesmo caminho, em curso curto. É balanço, não
   travessia — a travessia é a do `.v-aura`. Os dois períodos não têm divisor
   comum, então as duas manchas nunca repetem o mesmo encontro.

   `ease-in-out` solto, e não um token: o design system tem curvas de entrada e
   de saída, feitas para movimento que começa e termina, e um laço que volta
   pelo mesmo caminho precisa de uma curva simétrica — com `alternate` ela é
   espelhada na volta, e o balanço desacelera nas duas pontas. */
@keyframes v-glow-a {
  from { transform: translate3d(0, 0, 0) scale(1); }
  to   { transform: translate3d(5%, 4%, 0) scale(1.1); }
}
@keyframes v-glow-b {
  from { transform: translate3d(0, 0, 0) scale(1.06); }
  to   { transform: translate3d(-6%, -5%, 0) scale(1); }
}

/* ============================== BILLS ============================== */

/* Duas colunas como o Nosso DNA, mas centradas na vertical: aqui a coluna da
   direita é uma imagem de altura fixa, e não um card que começa junto ao
   título. A da esquerda é mais estreita porque o celular em pé é alto — dar
   metade da largura a ele deixaria o texto curto e a imagem gigante. */
.v-bills {
  display: grid;
  grid-template-columns: 1fr 0.8fr;
  gap: 64px;
  align-items: center;
}

.v-bills__sub {
  font-size: var(--fs-body-lg);
  line-height: var(--lh-body);
  color: var(--fg-2);
  max-width: 44ch;
  margin: 0;
}

.v-bills__ctas { margin-top: 36px; }

/* O celular fica sobre o brilho, daí o contexto de empilhamento. */
.v-bills__media {
  position: relative;
  display: flex;
  justify-content: center;
}

/* O celular chega depois da coluna de texto ao lado: as duas entram na tela no
   mesmo instante, e sem a espera a imagem — que é a peça maior — apareceria
   antes do que ela ilustra. A classe do reveal entra no seletor porque o
   `.v-reveal` vem depois neste arquivo, e o atalho `transition` de lá zeraria
   uma espera declarada só por `.v-bills__media`. */
.v-bills__media.v-reveal { transition-delay: 240ms; }

/* Mesma receita dos glows do hero: um radial desfocado, fora do fluxo e sem
   captar clique. Fica atrás da imagem por vir antes dela no HTML. */
.v-bills__glow {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 120%;
  aspect-ratio: 1;
  transform: translate(-50%, -50%);
  background: radial-gradient(circle, rgba(169, 147, 255, 0.28) 0%, transparent 65%);
  filter: blur(40px);
  pointer-events: none;
}

/* ------------------------------ iPhone ------------------------------ */

/* Mockup do iPhone 17 Pro Max, portado do componente "Apple iPhone 17 Pro"
   do Cult UI (cult-ui.com), de React + Tailwind para CSS puro. Ficou o
   acabamento grafite; a Dynamic Island interativa, que lá abria um player de
   música, não veio.

   Licença do original:

   MIT License

   Copyright (c) 2023 Jordan-Gilliam

   Permission is hereby granted, free of charge, to any person obtaining a copy
   of this software and associated documentation files (the "Software"), to
   deal in the Software without restriction, including without limitation the
   rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
   sell copies of the Software, and to permit persons to whom the Software is
   furnished to do so, subject to the following conditions:

   The above copyright notice and this permission notice shall be included in
   all copies or substantial portions of the Software.

   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
   AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
   FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
   IN THE SOFTWARE. */

/* O original tem 700px de altura e mede tudo em px. Aqui a altura manda — o
   aparelho é vertical e o que não pode é ele passar da altura da tela — e
   `--u` é "1px do original" nessa altura: toda medida vira `n * var(--u)`, e
   o celular inteiro escala junto, das bordas à fonte do relógio. */
.v-iphone {
  --v-iphone-h: clamp(420px, 60vh, 640px);
  --u: calc(var(--v-iphone-h) / 700);
  --v-iphone-frame: linear-gradient(145deg, #454549 0%, #3d3d41 15%, #363839 30%, #3a3a3e 50%, #424246 70%, #3b3b3f 85%, #343438 100%);
  --v-iphone-button: linear-gradient(180deg, #424246 0%, #3a3a3e 30%, #363839 50%, #3a3a3e 70%, #424246 100%);
  /* A tela-marcador é escura: barra de status e barra inferior em branco. */
  --v-iphone-ink: var(--vision-white);

  position: relative;
  height: var(--v-iphone-h);
  aspect-ratio: 71.5 / 149.6;
  max-width: 100%;
}

/* Moldura de titânio > borda preta > tela, cada camada um pouco mais
   arredondada por dentro que a anterior, como no aparelho. */
.v-iphone__frame {
  position: absolute;
  inset: 0;
  padding: calc(3 * var(--u));
  border-radius: calc(55 * var(--u));
  background: var(--v-iphone-frame);
  box-shadow:
    0 25px 50px -12px rgba(0, 0, 0, 0.25),
    0 12px 24px -8px rgba(0, 0, 0, 0.15);
}

.v-iphone__bezel {
  height: 100%;
  padding: calc(3 * var(--u));
  border-radius: calc(52 * var(--u));
  background: #0a0a0a;
}

.v-iphone__screen {
  position: relative;
  height: 100%;
  overflow: hidden;
  border-radius: calc(49 * var(--u));
  background: var(--vision-black);
  color: var(--v-iphone-ink);
}

/* ---------------------------- Tela do Bills ---------------------------- */

/* O dashboard do app, recriado a partir de um print de 428 pt de largura.
   `--p` é "1 pt do print" na escala do aparelho: a tela tem 322,6 `--u` de
   largura, e 322,6 / 428 ≈ 0,754. Toda medida tirada do print vira
   `n * var(--p)`, e a tela cresce e encolhe junto com a moldura.

   O conteúdo começa logo abaixo da Dynamic Island — o print não tem barra de
   status, quem faz esse papel é a do `.v-iphone__status`. O que passa do fim
   da tela é cortado, como num app rolável; a barra de abas fica presa
   embaixo, por cima. */
.v-bills-app {
  --p: calc(var(--u) * 0.754);
  --v-app-card: #161618;
  --v-app-line: #222225;
  --v-app-muted: #a3a3a8;
  --v-app-green: #5fcd6a;
  --v-app-orange: #f39a3b;

  position: absolute;
  inset: calc(44 * var(--u)) 0 0;
  overflow: hidden;
  padding: 0 calc(12 * var(--p));
  color: var(--vision-white);
  font-family: -apple-system, BlinkMacSystemFont, "SF Pro Text", system-ui, sans-serif;
  font-size: calc(13 * var(--p));
  line-height: 1.2;
  /* A grade de pontos do fundo do app. */
  background:
    radial-gradient(circle, rgba(255, 255, 255, 0.07) calc(0.9 * var(--p)), transparent calc(1.2 * var(--p)))
    0 0 / calc(14 * var(--p)) calc(14 * var(--p)),
    #000;
}

/* O `p` do design system traz a fonte do site; aqui vale a do sistema. */
:where(.v-bills-app) :is(p, dl, dd) { margin: 0; font-family: inherit; }

.v-bills-app svg {
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
}
/* Ícones cheios, à moda dos SF Symbols, com o recorte das linhas internas
   na cor do fundo em que eles ficam. */
.v-bills-app .v-bills-app__fill { fill: currentColor; stroke: none; }
.v-bills-app .v-bills-app__cut { stroke: #1c1c1e; }

/* O gradiente de marca do app, do roxo ao laranja, no logo e no saldo. */
.v-bills-app__logo,
.v-bills-app__total {
  background: linear-gradient(90deg, #b44cff 0%, #e765a8 55%, #f5a24a 100%);
  -webkit-background-clip: text;
          background-clip: text;
  color: transparent;
}

.v-bills-app__top {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-top: calc(14 * var(--p));
}

.v-bills-app__logo {
  padding-left: calc(2 * var(--p));
  font-family: ui-rounded, "SF Pro Rounded", -apple-system, system-ui, sans-serif;
  font-size: calc(30 * var(--p));
  font-weight: 800;
  letter-spacing: -0.02em;
}

.v-bills-app__month {
  display: flex;
  align-items: center;
  gap: calc(10 * var(--p));
  height: calc(38 * var(--p));
  padding: 0 calc(12 * var(--p));
  border-radius: 999px;
  background: #1c1c1e;
  font-size: calc(16 * var(--p));
  font-weight: 600;
}
.v-bills-app__month svg { width: calc(17 * var(--p)); height: calc(17 * var(--p)); stroke-width: 2.4; }
.v-bills-app__month span { margin-left: calc(4 * var(--p)); }
.v-bills-app__month .v-bills-app__caret { width: calc(12 * var(--p)); margin: 0 calc(10 * var(--p)) 0 calc(-6 * var(--p)); }
.v-bills-app__sep {
  width: 1px;
  height: calc(22 * var(--p));
  margin-left: calc(4 * var(--p));
  background: rgba(255, 255, 255, 0.25);
}
.v-bills-app__month .v-bills-app__eye { width: calc(22 * var(--p)); height: calc(22 * var(--p)); fill: currentColor; stroke: none; }
.v-bills-app__eye circle { fill: #1c1c1e; }

.v-bills-app__label {
  margin-top: calc(22 * var(--p));
  color: var(--v-app-muted);
  font-size: calc(12.5 * var(--p));
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.v-bills-app__total {
  width: fit-content;
  margin-top: calc(6 * var(--p));
  font-size: calc(39 * var(--p));
  font-weight: 800;
  letter-spacing: -0.01em;
  line-height: 1.15;
}

.v-bills-app__cards {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: calc(11 * var(--p));
  margin-top: calc(24 * var(--p));
  padding-bottom: calc(34 * var(--p));
}

/* As linhas que separam os blocos do dashboard vão de borda a borda da tela,
   passando por cima do respiro lateral do app. */
.v-bills-app__cards,
.v-bills-app__heading { position: relative; }
.v-bills-app__cards::after,
.v-bills-app__heading::before {
  content: "";
  position: absolute;
  left: calc(-12 * var(--p));
  right: calc(-12 * var(--p));
  height: 1px;
  background: var(--v-app-line);
}
.v-bills-app__cards::after { bottom: 0; }
.v-bills-app__heading::before { top: 0; }

.v-bills-app__card {
  display: flex;
  flex-direction: column;
  gap: calc(7 * var(--p));
  height: calc(78 * var(--p));
  padding: calc(18 * var(--p)) calc(20 * var(--p));
  border-radius: calc(16 * var(--p));
  background: var(--v-app-card);
}
.v-bills-app__card-label {
  display: flex;
  align-items: center;
  gap: calc(7 * var(--p));
  color: var(--v-app-muted);
  font-size: calc(12.5 * var(--p));
}
.v-bills-app__card-label svg { width: calc(15 * var(--p)); height: calc(15 * var(--p)); }
.v-bills-app__card-value {
  font-size: calc(19 * var(--p));
  font-weight: 700;
}
.v-bills-app__card--fatura .v-bills-app__card-label,
.v-bills-app__card--fatura .v-bills-app__card-value { color: var(--v-app-orange); }

.v-bills-app__chart {
  margin-top: calc(34 * var(--p));
  padding: calc(20 * var(--p)) calc(20 * var(--p)) calc(22 * var(--p));
  border-radius: calc(18 * var(--p));
  background: #0f0f11;
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.03);
}

/* Controle segmentado, com "Entradas" escolhida. */
.v-bills-app__tabs {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  padding: calc(2 * var(--p));
  border-radius: 999px;
  background: #2a2a2d;
  font-size: calc(14.5 * var(--p));
  font-weight: 500;
  text-align: center;
}
.v-bills-app__tabs span {
  padding: calc(6 * var(--p)) 0;
  border-radius: 999px;
}
.v-bills-app__tabs .is-active { background: #5c5c61; font-weight: 600; }

.v-bills-app__donut {
  display: block;
  /* 4 pt de folga em volta do anel, que encosta na borda do desenho: sem ela
     o contorno sai aparado em telas pequenas. */
  width: calc(192 * var(--p));
  height: calc(192 * var(--p));
  margin: calc(14 * var(--p)) auto calc(-4 * var(--p));
}
.v-bills-app__donut circle { fill: none; stroke-width: 46; stroke-linecap: butt; }

.v-bills-app__legend {
  display: flex;
  justify-content: center;
  gap: calc(10 * var(--p));
  margin-top: calc(2 * var(--p));
  color: var(--v-app-muted);
  font-size: calc(12 * var(--p));
}
.v-bills-app__legend span { display: flex; align-items: center; gap: calc(5 * var(--p)); }
.v-bills-app__legend i {
  width: calc(8 * var(--p));
  height: calc(8 * var(--p));
  border-radius: 50%;
}

.v-bills-app__rows {
  margin-top: calc(12 * var(--p));
  border-top: 1px solid var(--v-app-line);
  font-size: calc(12 * var(--p));
}
.v-bills-app__rows div {
  display: flex;
  justify-content: space-between;
  padding-top: calc(7 * var(--p));
}
.v-bills-app__rows dt { color: #d0d0d5; }
.v-bills-app__rows dd { color: var(--v-app-green); font-weight: 600; }

.v-bills-app__heading {
  margin-top: calc(30 * var(--p));
  padding-top: calc(30 * var(--p));
  font-size: calc(22 * var(--p));
  font-weight: 700;
}

/* Barra de abas flutuante, presa ao pé da tela, logo acima da barra do
   sistema (`.v-iphone__home`). */
.v-bills-app__tabbar {
  position: absolute;
  left: calc(12 * var(--p));
  right: calc(12 * var(--p));
  bottom: calc(16 * var(--u));
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  padding: calc(8 * var(--p));
  border-radius: calc(40 * var(--p));
  background: rgba(28, 28, 30, 0.96);
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.05), 0 calc(-8 * var(--p)) calc(24 * var(--p)) rgba(0, 0, 0, 0.6);
  color: #8e8e93;
  font-size: calc(12 * var(--p));
}
.v-bills-app__tabbar span {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: calc(5 * var(--p));
  padding: calc(9 * var(--p)) 0 calc(8 * var(--p));
  border-radius: calc(30 * var(--p));
  white-space: nowrap;
}
.v-bills-app__tabbar svg { width: calc(23 * var(--p)); height: calc(23 * var(--p)); }
.v-bills-app__tabbar .is-active { background: #2c2c2f; color: var(--vision-white); }
.v-bills-app__tabbar .is-active .v-bills-app__fill { fill: currentColor; }

/* Barra de status na fonte do sistema, que num Apple é a do próprio iOS. */
.v-iphone__status {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  z-index: 1;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: calc(16 * var(--u)) calc(24 * var(--u)) 0;
  font-family: -apple-system, BlinkMacSystemFont, "SF Pro Text", system-ui, sans-serif;
}

.v-iphone__time {
  width: calc(56 * var(--u));
  margin: calc(4 * var(--u)) 0 0 calc(4 * var(--u));
  font-size: calc(14 * var(--u));
  font-weight: 600;
  line-height: 1;
  letter-spacing: -0.02em;
}

.v-iphone__icons {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: calc(5 * var(--u));
  width: calc(68 * var(--u));
}

/* Sinal cheio: quatro barras crescendo de 2 em 2. */
.v-iphone__signal {
  display: flex;
  align-items: flex-end;
  gap: calc(1 * var(--u));
}
.v-iphone__signal i {
  width: calc(3 * var(--u));
  border-radius: calc(1 * var(--u));
  background: currentColor;
}
.v-iphone__signal i:nth-child(1) { height: calc(5 * var(--u)); }
.v-iphone__signal i:nth-child(2) { height: calc(7 * var(--u)); }
.v-iphone__signal i:nth-child(3) { height: calc(9 * var(--u)); }
.v-iphone__signal i:nth-child(4) { height: calc(11 * var(--u)); }

.v-iphone__wifi {
  width: calc(14 * var(--u));
  height: calc(14 * var(--u));
  fill: currentColor;
  stroke: currentColor;
  stroke-width: 2.4;
  stroke-linecap: round;
}
.v-iphone__wifi path { fill: none; }

/* Bateria em 94%: o contorno é a própria caixa, a carga é o `::before` e o
   polo é o `::after`, do lado de fora. */
.v-iphone__battery {
  position: relative;
  width: calc(24 * var(--u));
  height: calc(12 * var(--u));
  margin-right: calc(2 * var(--u));
  border: 1px solid color-mix(in srgb, currentColor 35%, transparent);
  border-radius: calc(4 * var(--u));
}
.v-iphone__battery::before {
  content: "";
  position: absolute;
  top: calc(1.2 * var(--u));
  bottom: calc(1 * var(--u));
  left: calc(1.2 * var(--u));
  width: calc(19 * 0.94 * var(--u));
  border-radius: calc(2.5 * var(--u));
  background: currentColor;
}
.v-iphone__battery::after {
  content: "";
  position: absolute;
  top: 50%;
  left: calc(100% + 1.5 * var(--u));
  width: calc(1.5 * var(--u));
  height: calc(5 * var(--u));
  transform: translateY(-50%);
  border-radius: 0 1px 1px 0;
  background: color-mix(in srgb, currentColor 40%, transparent);
}

.v-iphone__island {
  position: absolute;
  top: calc(12 * var(--u));
  left: 50%;
  z-index: 2;
  width: calc(110 * var(--u));
  height: calc(32 * var(--u));
  transform: translateX(-50%);
  border-radius: calc(16 * var(--u));
  background: #000;
}

/* A lente da câmera frontal, dentro da Island, à direita. */
.v-iphone__camera {
  position: absolute;
  top: 50%;
  right: calc(12 * var(--u));
  width: calc(10 * var(--u));
  height: calc(10 * var(--u));
  transform: translateY(-50%);
  border-radius: 50%;
  background: radial-gradient(circle at 30% 30%, #2a3a5a 0%, #0f1520 60%, #000 100%);
  box-shadow: inset 0 0 2px rgba(100, 150, 255, 0.3);
}

.v-iphone__home {
  position: absolute;
  bottom: calc(8 * var(--u));
  left: 50%;
  z-index: 1;
  width: calc(128 * var(--u));
  height: calc(4 * var(--u));
  transform: translateX(-50%);
  border-radius: 999px;
  background: color-mix(in srgb, currentColor 80%, transparent);
}

/* Botões laterais: liga/desliga à direita; Action e volume à esquerda, cada
   um com a luz batendo de cima e uma sombra curta logo abaixo (`::after`). */
.v-iphone__btn {
  position: absolute;
  width: calc(3 * var(--u));
  background: var(--v-iphone-button);
}

.v-iphone__btn--power {
  top: 28%;
  right: calc(-3 * var(--u));
  height: calc(65 * var(--u));
  border-radius: 0 2px 2px 0;
  box-shadow:
    1px 0 0 0 rgba(255, 255, 255, 0.15),
    2px 1px 3px -1px rgba(0, 0, 0, 0.25),
    3px 2px 6px 0 rgba(0, 0, 0, 0.15),
    inset -1px 0 1px rgba(255, 255, 255, 0.1);
}

.v-iphone__btn--action,
.v-iphone__btn--vol-up,
.v-iphone__btn--vol-down {
  left: calc(-3 * var(--u));
  isolation: isolate;
  border-radius: 2px 0 0 2px;
  box-shadow:
    -1px 0 0 0 rgba(255, 255, 255, 0.16),
    -2px 3px 5px -1px rgba(0, 0, 0, 0.34),
    -4px 5px 12px -2px rgba(0, 0, 0, 0.2),
    inset 1px 0 1px rgba(255, 255, 255, 0.12),
    inset 0 1px 0 rgba(255, 255, 255, 0.22),
    inset 0 -1px 0 rgba(0, 0, 0, 0.28);
}
.v-iphone__btn--action::after,
.v-iphone__btn--vol-up::after,
.v-iphone__btn--vol-down::after {
  content: "";
  position: absolute;
  top: calc(100% - 2px);
  left: -1px;
  right: -1px;
  z-index: -1;
  height: 10px;
  background: linear-gradient(to bottom, rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0.2), transparent);
  opacity: 0.72;
  filter: blur(4px);
  pointer-events: none;
}

.v-iphone__btn--action   { top: 20%; height: calc(22 * var(--u)); }
.v-iphone__btn--vol-up   { top: 27%; height: calc(40 * var(--u)); }
.v-iphone__btn--vol-down { top: 36%; height: calc(40 * var(--u)); }

/* Reflexo na diagonal sobre o aparelho inteiro, tela incluída. */
.v-iphone__shine {
  position: absolute;
  inset: 0;
  border-radius: calc(55 * var(--u));
  background: linear-gradient(135deg, rgba(255, 255, 255, 0.15) 0%, transparent 25%, transparent 75%, rgba(0, 0, 0, 0.1) 100%);
  pointer-events: none;
}

/* ============================= SERVIÇOS ============================= */

/* Abertura em duas colunas: intro à esquerda, abas à direita, alinhadas pela
   base do título. O subtexto vem abaixo das duas, na largura da esquerda. */
.v-services__head {
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: end;
  gap: 32px;
  margin-bottom: 18px;
}
.v-services__intro .v-section__title { margin-bottom: 0; }
.v-services__sub { margin-bottom: 64px; }

/* ------------------------------- ABAS ------------------------------- */

.v-tabs {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  justify-content: flex-end;
}
.v-tabs__tab {
  padding: 10px 18px;
  border: 1px solid var(--border-2);
  border-radius: var(--radius-pill);
  background: transparent;
  color: var(--fg-2);
  font-size: 14px;
  font-weight: var(--fw-semibold);
  letter-spacing: var(--ls-button);
  white-space: nowrap;
  cursor: pointer;
  transition: background var(--dur-2) var(--ease-standard),
              color var(--dur-2) var(--ease-standard),
              border-color var(--dur-2) var(--ease-standard);
}
.v-tabs__tab:hover { border-color: var(--section-accent); color: var(--section-accent); }
/* A aba escolhida inverte a seção: pinta-se com a cor do texto e escreve com a
   cor do fundo. Fixar o preto aqui só funcionava enquanto a seção era clara —
   sobre o escuro a pastilha sumiria dentro do próprio fundo. */
.v-tabs__tab[aria-selected="true"] {
  background: var(--fg-1);
  border-color: var(--fg-1);
  color: var(--section-bg);
}

/* ---------------------- CONTEÚDO DE CADA ABA ---------------------- */

/* Título do serviço, abrindo o painel da aba. Antes vinha sobreposto a uma
   foto de largura cheia; a foto saiu e o título passou a valer sozinho, então
   nada aqui precisa de posicionamento — é um heading comum, com a margem que
   antes pertencia ao cartão da foto. */
.v-service-title {
  display: flex;
  align-items: center;
  gap: clamp(16px, 2vw, 28px);
  margin: 0 0 48px;
  font-family: var(--font-display);
  /* Fluido em vez de um degrau no celular: o piso é o tamanho que a regra de
     768px fixava antes, então nada encolheu na tela estreita. */
  font-size: clamp(28px, 3.5vw, 48px);
  font-weight: var(--fw-bold);
  line-height: 1.1;
  color: var(--vision-white);
}
/* Um item de flex só, para o nome não se partir no `gap`. O `min-width` deixa
   ele encolher e quebrar linha na tela estreita, em vez de empurrar a faixa. */
.v-service-title__text { min-width: 0; }

/* Mesmo destaque das headlines da seção, em `.v-section__title em`: itálico,
   peso normal e o roxo da seção. Quem escolhe a palavra é o filtro
   `accent_last_word`, depois da tradução. */
.v-service-title em {
  font-style: italic;
  font-weight: var(--fw-regular);
  color: var(--section-accent, var(--vision-purple));
}

/* A linha é pseudo-elemento porque é ornamento, não conteúdo: não entra na
   marcação e leitor de tela não anuncia. `flex: 1` faz ela ocupar tudo que
   sobra, então termina na borda do container seja qual for o comprimento do
   nome do serviço. A cor vem de `--section-accent` — a seção é escura e o
   token já resolve para o roxo claro; se um dia virar clara, acompanha. */
.v-service-title::after {
  content: "";
  flex: 1;
  min-width: 40px;
  height: 1px;
  background: var(--section-accent, var(--vision-purple));
}

/* De 4 a 6 informações: duas colunas acomodam bem os dois extremos.
   A margem negativa compensa o padding dos itens: a superfície do hover avança
   para fora, mas número e texto seguem alinhados com o resto da seção. */
.v-service-info {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 24px;
  list-style: none;
  margin: 0 -24px;
  padding: 0;
}
/* Número à esquerda, o resto à direita. */
.v-service-info__item {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 28px;
  /* O número acompanha o topo do card, e não a meia-altura do texto: como as
     etapas têm descrições de comprimentos diferentes, centralizar deixava cada
     numeral numa altura, e a coluna da esquerda perdia a linha de base comum. */
  align-items: start;
  padding: 20px 24px;
  border-radius: var(--radius-md);
  transition: background var(--dur-2) var(--ease-standard);
}
/* Fundo levemente mais escuro no hover. `--surface-overlay` é o preto a 4% do
   tema claro — o mesmo realce discreto que o design system usa. */
.v-service-info__item:hover { background: var(--surface-overlay); }

.v-service-info__num {
  font-family: var(--font-sans);
  font-weight: var(--fw-black);
  font-size: var(--fs-display-2);
  line-height: 1;
  /* O roxo que a seção define: o forte sobre papel, o claro sobre preto. */
  color: var(--section-accent);
  margin: 0;
}

.v-service-info__eyebrow {
  font-size: var(--fs-eyebrow);
  font-weight: var(--fw-semibold);
  letter-spacing: var(--ls-caps);
  text-transform: uppercase;
  color: var(--fg-3);
  margin: 0 0 6px;
}
.v-service-info__title {
  font-size: var(--fs-h2);
  font-weight: var(--fw-semibold);
  line-height: var(--lh-heading);
  margin: 0 0 10px;
}
.v-service-info__desc {
  font-size: var(--fs-body-lg);
  line-height: var(--lh-body);
  color: var(--fg-2);
  margin: 0;
  max-width: 46ch;
}
/* Chamada da aba Soluções Personalizadas, no lugar da lista de etapas.

   Centrada, e não alinhada à esquerda como a lista que ela substitui: a lista
   ocupa duas colunas e ancora o olho na margem esquerda, enquanto aqui há um
   texto só — encostado à esquerda ele deixaria metade da faixa vazia. O fundo
   é o mesmo realce do hover das etapas, o preto a 4%, para o bloco ler como
   parte do painel e não como um card de outra seção. */
.v-service-callout {
  display: grid;
  justify-items: center;
  text-align: center;
  gap: 14px;
  padding: 56px 40px;
  border-radius: var(--radius-lg);
  background: var(--surface-overlay);
}
/* A ordem visual inverte a do documento: o subtítulo é o rótulo da divisão e
   vem acima do título, mas no HTML o título vem primeiro para o <h4> abrir o
   bloco — o leitor de tela anuncia o cabeçalho antes do rótulo que o qualifica. */
.v-service-callout__sub {
  order: -1;
  font-size: var(--fs-eyebrow);
  font-weight: var(--fw-semibold);
  letter-spacing: var(--ls-caps);
  text-transform: uppercase;
  color: var(--section-accent);
  margin: 0;
}
.v-service-callout__title {
  font-size: var(--fs-h2);
  font-weight: var(--fw-semibold);
  line-height: var(--lh-heading);
  margin: 0;
  max-width: 20ch;
}
.v-service-callout__text {
  font-size: var(--fs-body-lg);
  line-height: var(--lh-body);
  color: var(--fg-2);
  margin: 0;
  max-width: 52ch;
}
.v-service-callout__cta { margin-top: 10px; }

/* O painel recebe foco pelo teclado, mas não é um controle: o anel do :focus
   padrão poluiria a área inteira. O contorno só aparece na navegação por teclado
   fora de um clique — que é o que :focus-visible já resolve. */
.v-tabs__panel:focus { outline: none; }

/* Sem script os painéis viram uma lista corrida; quem identifica cada um é o
   título em `.v-service-title`. */
.no-js .v-tabs__panels { display: grid; gap: 40px; }


.v-services {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 1px;
  background: rgba(255, 255, 255, 0.08);
  border: 1px solid rgba(255, 255, 255, 0.08);
  border-radius: var(--radius-lg);
  overflow: hidden;
}
.v-service {
  display: flex;
  flex-direction: column;
  gap: 14px;
  min-height: 280px;
  padding: 36px 32px;
  background: var(--vision-black);
  transition: background var(--dur-2) var(--ease-standard);
}
.v-service:hover { background: var(--vision-ink); }
.v-service__icon {
  display: grid;
  place-items: center;
  width: 48px; height: 48px;
  border-radius: var(--radius-md);
  background: var(--vision-purple);
  color: var(--vision-black);
}
.v-service__icon svg { width: 24px; height: 24px; stroke-width: 1.5; }
.v-service__num {
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.1em;
  color: var(--fg-3);
}
.v-service__title {
  font-family: var(--font-display);
  font-size: var(--fs-h3);
  font-weight: var(--fw-bold);
  line-height: 1.1;
  letter-spacing: var(--ls-heading);
  margin: 4px 0;
}
.v-service__title em { font-style: italic; font-weight: var(--fw-regular); }
.v-service__desc {
  font-size: 14px;
  line-height: var(--lh-body);
  color: var(--fg-2);
  margin: 0;
}

/* ------------------------- CARROSSEL DA ABA ------------------------- */

/* A faixa anda para a esquerda a largura de uma lista inteira e recomeça. Para
   a emenda ser invisível, a largura de uma lista tem de ser exatamente metade
   da faixa: por isso o espaço entre os cartões é `margin-right` no item, e não
   `gap` — assim o último cartão carrega o próprio intervalo e as duas listas
   ficam idênticas. Com `gap`, o intervalo entre as listas contaria só uma vez e
   o ciclo saltaria meio espaço a cada volta. */
.v-marquee {
  margin-top: 60px;
  /* O recorte da faixa acontece na borda do padding, então esta folga é o que
     permite o card crescer no hover sem ter o topo e a base cortados. */
  padding: 12px 0;
  overflow: hidden;
}
.v-marquee__track {
  display: flex;
  width: max-content;
  /* Sem pausa: nada aqui interrompe o ciclo — nem hover, nem foco. Quem não
     quer movimento é atendido pelo bloco de `prefers-reduced-motion`. */
  animation: v-marquee-scroll 60s linear infinite;
  animation-play-state: running;
}
.v-marquee__group { display: flex; list-style: none; margin: 0; padding: 0; }
.v-marquee__item { margin-right: 20px; }

.v-marquee__media {
  position: relative;   /* os fundos `.bg-*` posicionam pseudo-elementos */
  overflow: hidden;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  width: clamp(170px, 20vw, 240px);
  aspect-ratio: 9 / 16;
  /* Cobre o intervalo entre o card aparecer e o pôster pintar. Os `.bg-*` vêm
     depois no arquivo, então quem tem fundo abstrato continua com o dele. */
  background: var(--vision-black);
  padding: 14px;
  border-radius: var(--radius-md);
  transition: transform var(--dur-2) var(--ease-standard),
              box-shadow var(--dur-2) var(--ease-standard);
}
/* Preenche o card inteiro, atrás da cortina do `::after` e das legendas — a
   ordem de pintura vem do DOM (pseudo-elemento depois do conteúdo), então não
   precisa de `z-index` para ninguém. `object-fit` recorta o 9:16 do arquivo no
   9:16 do card sem esticar. */
.v-marquee__video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* O contorno é `box-shadow`, e não `border`: anel não entra no cálculo da
   caixa, então o card não muda de tamanho nem desalinha a faixa — e ele
   acompanha o arredondamento sozinho. */
.v-marquee__media:hover {
  transform: scale(1.05);
  box-shadow: 0 0 0 2px var(--section-accent);
}
/* Escurece só as pontas, onde o texto fica: os fundos do design system vão do
   quase preto ao lilás claro, e sem isto a legenda some sobre os claros. Os
   `.bg-*` já usam `::before`, então a cortina vai no `::after`. */
.v-marquee__media::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg,
              rgba(10, 10, 10, 0.55) 0%, transparent 35%,
              transparent 65%, rgba(10, 10, 10, 0.55) 100%);
}
.v-marquee__name,
.v-marquee__place {
  position: relative;   /* acima da cortina */
  z-index: 1;
  margin: 0;
  color: var(--vision-white);
  line-height: 1.25;
}
.v-marquee__name {
  font-size: var(--fs-body-sm);
  font-weight: var(--fw-semibold);
}
.v-marquee__place {
  align-self: flex-end;
  text-align: right;
  font-size: var(--fs-caption);
  color: rgba(255, 255, 255, 0.75);
}

/* Dentro da aba o carrossel abre o painel, logo abaixo do título: o respiro de
   cima já é a margem do título, e o de baixo é que separa das etapas. Escopado
   no painel para não mexer na faixa de logotipos, que usa a mesma base. */
.v-tabs__panel .v-marquee {
  margin-top: 0;
  margin-bottom: 56px;
}

@keyframes v-marquee-scroll {
  from { transform: translateX(0); }
  to { transform: translateX(-50%); }
}

/* ---------------------- RESUMO NO PÉ DA ABA ---------------------- */

/* Três tópicos de largura igual, ocupando a página toda. Caixa alta e nada em
   negrito: o realce dos rótulos é só a cor. */
.v-service-meta {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  margin: 40px 0 0;
  font-size: var(--fs-body-sm);
  font-weight: var(--fw-regular);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--fg-3);
}

/* A divisão entre tópicos é a borda da própria coluna, não um caractere: ela
   acompanha a altura da faixa, inclusive quando um tópico quebra em duas
   linhas e os outros não. */
.v-service-meta__pair {
  font-weight: var(--fw-regular);
  padding-right: 24px;
  text-align: center;
}
.v-service-meta__pair + .v-service-meta__pair {
  padding-left: 24px;
  border-left: 1px solid var(--border-2);
}
.v-service-meta__pair:last-child { padding-right: 0; }

/* O roxo da seção: em corpo pequeno é o contraste que decide, e nenhum dos dois
   roxos serve nas duas superfícies. */
.v-service-meta__label { color: var(--section-accent); }

/* ============================== CASES ============================== */

/* Duas linhas de quatro. */
.v-cases {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 18px;
}

/* O card é claro como a seção: branco sobre o papel, que é o que separa um do
   outro sem precisar de sombra. Os tons de texto são os da seção — quem escurece
   é só a peça do hover, que cobre o card inteiro e leva os próprios tons. */
.v-case {
  position: relative;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  aspect-ratio: 4 / 3;
  padding: 20px;
  background: var(--vision-white);
  border: 1px solid var(--vision-cloud);
  border-radius: var(--radius-lg);
}

.v-case__head { display: flex; flex-direction: column; gap: 2px; }
.v-case__name {
  font-size: var(--fs-body-sm);
  font-weight: var(--fw-semibold);
  line-height: 1.2;
  color: var(--fg-1);
  margin: 0;
}
.v-case__place {
  font-size: var(--fs-caption);
  line-height: 1.2;
  color: var(--fg-3);
  margin: 0;
}

/* O logotipo ocupa o que sobra e fica no centro dos dois eixos. */
.v-case__logo { flex: 1; display: grid; place-items: center; min-height: 0; }

/* A imagem não ocupa a área toda: o recuo deixa o logotipo respirar entre o
   cabeçalho e a borda de baixo do card. O `contain` decide o resto — os
   arquivos vêm todos em 5:3, mas com margens internas diferentes e um lockup
   vertical no meio, então é a proporção de cada arte que manda no tamanho
   aparente; esticar qualquer uma para igualar seria deformar marca de cliente. */
.v-case__logo img {
  width: 78%;
  height: 78%;
  object-fit: contain;
}

/* A peça cobre o card inteiro no hover. `position: relative` também porque os
   fundos `.bg-*` posicionam pseudo-elementos. */
.v-case__cover {
  position: absolute;
  inset: 0;
  padding: 20px;
  opacity: 0;
  visibility: hidden;
  transition: opacity var(--dur-3) var(--ease-standard),
              visibility var(--dur-3) var(--ease-standard);
}
.v-case:hover .v-case__cover,
.v-case:focus-within .v-case__cover { opacity: 1; visibility: visible; }

/* O nome fica acima da peça e do gradiente que a escurece no topo. */
.v-case__cover-name {
  position: relative;
  z-index: 1;
  margin: 0;
  font-size: var(--fs-body-sm);
  font-weight: var(--fw-bold);
  line-height: 1.2;
  color: var(--vision-white);
}
/* Os `.bg-*` já usam `::before`; a cortina do topo vai no `::after`. */
.v-case__cover::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg, rgba(10, 10, 10, 0.6), transparent 40%);
}

/* Faixa de logotipos: mesma mecânica do carrossel das abas, outro conteúdo.
   A duração é menor porque a lista é mais estreita — o que se quer igual entre
   as duas faixas é a velocidade, não o tempo da volta. */

/* Vive fora do `.v-container`, então ocupa a largura inteira da tela. A linha
   de cima é o que separa os cards dos logotipos. */
.v-logo-strip {
  margin-top: 72px;
  border-top: 1px solid var(--border-2);
}

/* A faixa já fecha a seção com o respiro dela, então o rodapé de 100px da
   seção cairia por cima e abriria um vão grande demais até a seção seguinte.
   Quem tem faixa fecha mais curto — e é a presença dela que decide, não o id
   de Cases: a próxima seção que ganhar uma faixa herda o mesmo fecho. */
.v-section:has(.v-logo-strip) { padding-bottom: 24px; }
/* Mais apagado que o olho da seção, e sem as barras: são dois olhos na mesma
   tela e só o de cima é o roxo da marca — mesma regra do Nosso DNA. */
.v-logo-strip__eyebrow {
  margin: 0;
  padding-top: 32px;
  font-size: var(--fs-eyebrow);
  font-weight: var(--fw-semibold);
  letter-spacing: var(--ls-caps);
  text-transform: uppercase;
  color: var(--fg-3);
}
.v-marquee--logos { margin-top: 0; padding: 24px 0 32px; }
.v-marquee--logos .v-marquee__track { animation-duration: 45s; }
.v-marquee--logos .v-marquee__item { margin-right: 18px; }

/* Sem moldura: a caixa com borda era o desenho do marcador, e logotipo de
   cliente numa faixa não se emoldura — o que separa um do outro é o espaço.
   A caixa continua de tamanho fixo para todos, e o `contain` acomoda cada arte
   dentro dela, do lockup deitado ao quadrado. */
.v-logo-mark {
  display: block;
  width: 132px;
  height: 74px;
  object-fit: contain;
}

/* ======================== FUNDOS ABSTRATOS ======================== */

/* Visual de marca (spec §9), não imagem provisória. Usados hoje pelos cards
   de Cases e pelos carrosséis dos serviços. */
.bg-purple-haze { background: radial-gradient(circle at 30% 30%, var(--vision-purple) 0%, var(--vision-purple-strong) 50%, var(--vision-ink) 100%); }
.bg-bw-glow { background: radial-gradient(ellipse at 70% 30%, rgba(169, 147, 255, 0.5), transparent 60%), linear-gradient(140deg, var(--vision-black), var(--vision-graphite)); }
.bg-stripe { background: var(--vision-purple-haze); }
.bg-stripe::before {
  content: "";
  position: absolute;
  inset: 0;
  background: repeating-linear-gradient(60deg, rgba(169, 147, 255, 0.4) 0 3px, transparent 3px 18px);
}
.bg-conic { background: var(--vision-black); }
.bg-conic::before {
  content: "";
  position: absolute;
  inset: 20%;
  border-radius: 50%;
  background: conic-gradient(from 90deg at 50% 50%, var(--vision-purple), var(--vision-purple-strong), var(--vision-purple-haze), var(--vision-purple));
  filter: blur(20px);
}
.bg-grid {
  background-color: var(--vision-ink);
  background-image: linear-gradient(rgba(169, 147, 255, 0.18) 1px, transparent 1px),
                    linear-gradient(90deg, rgba(169, 147, 255, 0.18) 1px, transparent 1px);
  background-size: 32px 32px;
}
.bg-grid::before {
  content: "";
  position: absolute;
  inset: 0;
  background: radial-gradient(ellipse at 30% 50%, rgba(169, 147, 255, 0.4), transparent 60%);
}

/* Cards claros precisam inverter o texto e o véu de escurecimento. */
/* ============================= FEEDBACKS ============================= */

/* Três colunas em duas linhas. Sem proporção fixa, ao contrário dos Cases: o
   card acompanha o comprimento do depoimento. A linha inteira fica na altura do
   card mais alto (o `stretch` padrão da grade) e o rodapé desce para a base. */
.v-feedbacks {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 18px;
}

/* Card padrão da spec (§7): papel branco sobre o fundo claro da seção, borda
   fina, sombra leve em repouso e a subida de 4px no hover. */
.v-feedback {
  display: flex;
  flex-direction: column;
  margin: 0;
  padding: 28px;
  background: var(--vision-white);
  border: 1px solid var(--border-1);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-2);
  transition: box-shadow var(--dur-2) var(--ease-standard),
              transform var(--dur-2) var(--ease-standard);
}
.v-feedback:hover { box-shadow: var(--shadow-3); transform: translateY(-4px); }

/* A aspa ocupa só uma faixa estreita no alto da caixa do glifo: em The Seasons
   itálico negrito o desenho tem 0,211em de altura e começa 0,481em acima do
   meio da entrelinha. Daí os dois números — `line-height: 0.962` faz o topo da
   caixa cair exatamente no topo do desenho, e a margem negativa devolve o vão
   que sobra embaixo, para o card medir a aspa pelo desenho (~44px) e não pela
   caixa (~200px). Com a margem zerando o vão, a distância até o depoimento é o
   `margin-top` do próprio texto. O recuo à esquerda é da mesma família: o
   glifo tem 0,194em de espaço em branco antes do desenho, e sem descontá-lo a
   aspa nasceria adiantada em relação ao texto. Mudou a fonte, remedir com
   `measureText()` e os campos `actualBoundingBox*`. */
.v-feedback__mark {
  margin: 0 0 -0.751em -0.194em;
  font-family: var(--font-display);
  font-style: italic;
  font-weight: var(--fw-bold);
  font-size: 210px;
  line-height: 0.962;
  color: var(--vision-purple-strong);
}

.v-feedback__text {
  margin: 20px 0 24px;
  font-size: var(--fs-body);
  line-height: var(--lh-body);
  color: var(--fg-2);
}

/* `margin-top: auto` cola o rodapé na base: com depoimentos de comprimentos
   diferentes, os rodapés de uma mesma linha ficam alinhados entre si. Dentro
   dele, quem falou à esquerda e a nota à direita, centradas uma na outra. */
.v-feedback__foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  margin-top: auto;
  padding-top: 20px;
  border-top: 1px solid var(--border-1);
}
/* `min-width: 0` para o nome e o ramo poderem quebrar em vez de empurrar as
   estrelas para fora do card nas colunas mais estreitas. */
.v-feedback__who { min-width: 0; }
.v-feedback__stars {
  display: flex;
  gap: 3px;
  flex-shrink: 0;
  margin: 0;
  color: var(--vision-purple-strong);
}
.v-feedback__star { width: 16px; height: 16px; }
.v-feedback__name {
  margin: 0;
  font-size: var(--fs-body-sm);
  font-weight: var(--fw-bold);
  line-height: 1.3;
  color: var(--fg-1);
}
.v-feedback__meta {
  margin: 2px 0 0;
  font-size: var(--fs-caption);
  line-height: 1.4;
  color: var(--fg-3);
}

/* ============================= DEPOIMENTO ============================= */

.v-quote {
  position: relative;
  overflow: hidden;
  margin: 0;
  padding: 80px 64px;
  border-radius: var(--radius-xl);
  background: var(--vision-paper);
  color: var(--vision-ink);
}
.v-quote::before {
  content: "\201D";
  position: absolute;
  top: 30px; right: 60px;
  font-family: var(--font-display);
  font-style: italic;
  font-size: 220px;
  line-height: 0.8;
  color: var(--vision-purple);
  opacity: 0.8;
}
/* A aspa é ornamento do card, e o card é a moldura da cascata: ele aparece de
   uma vez, as peças dentro é que entram em sequência. Mas um 220px de roxo
   sozinho, antes do texto, não é moldura — é a seção inteira. Então a aspa
   esmaece no tempo da primeira peça. O pseudo-elemento não é filho para o
   `> *` da cascata, daí precisar da regra própria. */
.v-quote.v-reveal--stagger::before {
  opacity: 0;
  transition: opacity var(--v-stagger-dur) var(--ease-standard);
}
.v-quote.v-reveal--stagger.is-visible::before { opacity: 0.8; }
.no-js .v-quote::before { opacity: 0.8; transition: none; }
.v-quote__text {
  position: relative;
  z-index: 1;
  font-family: var(--font-sans);
  font-weight: var(--fw-medium);
  font-size: clamp(24px, 3.4vw, 36px);
  line-height: 1.35;
  letter-spacing: -0.01em;
  max-width: 720px;
  margin: 0 0 32px;
}
.v-quote__author { position: relative; z-index: 1; display: flex; align-items: center; gap: 14px; }
.v-quote__avatar {
  display: grid;
  place-items: center;
  width: 48px; height: 48px;
  border-radius: var(--radius-pill);
  background: var(--vision-purple);
  color: var(--vision-black);
  font-family: var(--font-display);
  font-size: var(--fs-h5);
  font-weight: var(--fw-bold);
}
.v-quote__name { font-size: var(--fs-body-sm); font-weight: var(--fw-semibold); }
.v-quote__role { font-size: var(--fs-caption); color: var(--vision-slate); }

/* ============================= FAQ ============================= */

/* Lista corrida separada por linhas, sem card: são onze itens, e onze caixas
   empilhadas pesariam mais que o conteúdo. A borda de baixo do último fecha o
   bloco. */
.v-faq {
  list-style: none;
  /* Mesma medida do `.v-section__head`: a lista corrida na largura inteira
     jogaria a seta a meia tela de distância da pergunta. */
  max-width: 900px;
  margin: 0;
  padding: 0;
  border-top: 1px solid rgba(255, 255, 255, 0.12);
}
.v-faq__item { border-bottom: 1px solid rgba(255, 255, 255, 0.12); }

/* O `h3` existe pela semântica; o peso e o tamanho vêm do botão. */
.v-faq__heading { margin: 0; font: inherit; }

/* Linha inteira clicável: número, pergunta e seta em uma grade de três
   colunas, com a pergunta ocupando o que sobra. */
.v-faq__question {
  display: grid;
  /* A coluna do índice é fixa em vez de `auto` porque cada item é uma grade
     própria: com `auto`, o 10 e o 11 seriam mais largos que os de um dígito e
     empurrariam só as suas perguntas para a direita. */
  grid-template-columns: 2ch 1fr auto;
  align-items: baseline;
  gap: 20px;
  width: 100%;
  padding: 26px 0;
  border: 0;
  background: none;
  color: inherit;
  text-align: left;
  cursor: pointer;
}

/* Índice em peso normal e no roxo da marca — o contraponto da pergunta, que é
   o negrito ao lado. */
.v-faq__num {
  font-size: var(--fs-body);
  font-weight: var(--fw-regular);
  font-variant-numeric: tabular-nums;
  color: var(--vision-purple);
}

.v-faq__label {
  font-size: var(--fs-h4);
  font-weight: var(--fw-bold);
  line-height: 1.25;
  letter-spacing: var(--ls-heading);
  color: var(--fg-1);
  transition: color var(--dur-2) var(--ease-standard);
}
.v-faq__question:hover .v-faq__label { color: var(--vision-purple); }

/* A seta gira meia volta quando a resposta abre. `align-self: center` porque a
   grade alinha pela linha de base do texto, e um ícone não tem uma. */
.v-faq__chevron {
  width: 20px;
  height: 20px;
  align-self: center;
  color: var(--fg-3);
  transition: transform var(--dur-3) var(--ease-standard),
              color var(--dur-2) var(--ease-standard);
}
.v-faq__question[aria-expanded="true"] .v-faq__chevron { transform: rotate(180deg); }
.v-faq__question:hover .v-faq__chevron { color: var(--vision-purple); }

/* A altura da resposta anima de `0fr` a `1fr` porque `height: auto` não é
   animável: a grade de uma linha só resolve a altura do conteúdo sozinha, e a
   fração interpola. O recorte fica no filho — é ele que tem altura fixa
   durante a transição. */
.v-faq__answer {
  display: grid;
  grid-template-rows: 1fr;
  transition: grid-template-rows var(--dur-3) var(--ease-standard);
}
.v-faq__answer-inner {
  overflow: hidden;
  /* `visibility` troca de valor de uma vez, no fim da transição ao fechar e no
     começo ao abrir: é o que tira a resposta fechada do leitor de tela e da
     busca da página sem cortar a animação pela metade. */
  transition: visibility var(--dur-3);
}

/* Estado fechado. O `aria-expanded` do botão é a única fonte da verdade — o
   JavaScript não guarda estado em classe nenhuma. */
.v-faq__item:has(.v-faq__question[aria-expanded="false"]) .v-faq__answer {
  grid-template-rows: 0fr;
}
.v-faq__item:has(.v-faq__question[aria-expanded="false"]) .v-faq__answer-inner {
  visibility: hidden;
}

/* O recuo alinha a resposta com a pergunta, e não com o número: 20px do gap da
   grade mais a largura de dois dígitos. */
.v-faq__text {
  margin: 0;
  padding: 0 40px 28px calc(20px + 2ch);
  font-size: var(--fs-body);
  line-height: var(--lh-body);
  color: var(--fg-2);
  max-width: 68ch;
}

/* ============================= CTA ============================= */

/* Coluna única e centrada: título, texto e botões empilhados nessa ordem.

   O degradê abre no lilás quase branco e fecha no roxo mais fundo. O limite
   do lado escuro é o `--vision-purple-bright`: o texto do card é preto, e ali
   ele ainda dá 5,3:1 de contraste — acima dos 4,5:1 que a WCAG AA pede para
   texto normal. Descer até o `--vision-purple-strong` cairia para 3,0:1 e
   obrigaria a virar o texto para branco, então a amplitude foi ganha
   clareando o topo, não escurecendo a base. A claridade do canto é uma
   segunda camada, radial, sobre a diagonal — mesma ideia do `.bg-bw-glow`. */
.v-cta {
  display: grid;
  justify-items: center;
  text-align: center;
  gap: 26px;
  padding: 80px 64px;
  border-radius: var(--radius-xl);
  background:
    radial-gradient(110% 130% at 12% 0%, var(--vision-purple-soft) 0%, transparent 58%),
    linear-gradient(135deg, var(--vision-purple-light) 0%, var(--vision-purple) 42%, var(--vision-purple-bright) 100%);
  color: var(--vision-black);
}
.v-cta__title {
  font-family: var(--font-display);
  font-size: clamp(36px, 5vw, 72px);
  font-weight: var(--fw-bold);
  line-height: 0.98;
  letter-spacing: var(--ls-display);
  margin: 0;
  /* Sem limite a manchete centrada abre até a borda do card e vira uma linha
     larga demais para ler de uma vez. */
  max-width: 16ch;
}
.v-cta__title em { font-style: italic; font-weight: var(--fw-regular); }
.v-cta__sub {
  font-size: var(--fs-body);
  line-height: 1.5;
  margin: 0;
  max-width: 52ch;
}
/* Os três canais na mesma linha, centrados, quebrando quando o card aperta. */
.v-cta__actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 10px;
  margin-top: 6px;
}

/* Faixa que fecha o card. Itens do tamanho do conteúdo, e não colunas iguais
   como no `.v-service-meta`: aqui a linha é centrada, e colunas iguais
   deixariam vãos desiguais entre os textos e as barras. */
.v-cta__meta {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  margin: 10px 0 0;
  font-size: var(--fs-caption);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  /* Preto aliviado: a faixa fecha o card, não disputa com os botões. */
  color: rgba(10, 10, 10, 0.66);
}
/* A barra entre os tópicos é a borda da coluna, não um caractere — assim ela
   acompanha a altura da faixa se um tópico quebrar em duas linhas. */
.v-cta__meta-pair { padding: 0 18px; }
.v-cta__meta-pair + .v-cta__meta-pair { border-left: 1px solid rgba(10, 10, 10, 0.28); }
.v-cta__meta-pair:first-child { padding-left: 0; }
.v-cta__meta-pair:last-child { padding-right: 0; }
/* O rótulo é a parte com destaque; o valor é que fica aliviado. */
.v-cta__meta-label { font-weight: var(--fw-bold); color: var(--vision-black); }

/* ============================= RODAPÉ ============================= */

.v-footer {
  margin-top: 100px;
  padding: 60px 0 40px;
  border-top: 1px solid rgba(255, 255, 255, 0.08);
}
/* Marca à esquerda, os três canais em uma linha à direita. */
.v-footer__top {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 24px 40px;
  padding-bottom: 40px;
}
.v-footer__brand { color: var(--vision-white); }
.v-footer__brand svg { height: 40px; width: auto; display: block; }

.v-footer__links {
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 12px 28px;
  margin: 0;
  padding: 0;
}
/* `inline-block` para o `::before` de baixo poder ser um bloco dentro do link:
   é ele que fixa a largura. */
.v-footer__link {
  display: inline-block;
  color: var(--fg-2);
  font-size: 14px;
  transition: color var(--dur-2) var(--ease-standard);
}
/* Cópia invisível do rótulo já em negrito, de altura zero: ela dita a largura
   do link, então engrossar no hover não alarga nada e a linha não treme. Fora
   da árvore de acessibilidade por causa do `visibility: hidden`. */
.v-footer__link::before {
  content: attr(data-label);
  display: block;
  height: 0;
  overflow: hidden;
  visibility: hidden;
  font-weight: var(--fw-bold);
  pointer-events: none;
}
.v-footer__link:hover,
.v-footer__link:focus-visible {
  color: var(--vision-purple);
  font-weight: var(--fw-bold);
}
.v-footer__bottom {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  justify-content: space-between;
  padding-top: 20px;
  border-top: 1px solid rgba(255, 255, 255, 0.06);
  font-size: var(--fs-eyebrow);
  color: var(--fg-3);
  align-items: center;
}
/* Fica junto ao aviso de direitos, à esquerda; a assinatura segue à direita.
   Herda tamanho e cor da faixa: é texto do rodapé, não um botão. Sem hover, a
   pedido — o sublinhado é o que sobra para dizer que dá para clicar. */
.v-footer__legal {
  margin-right: auto;
  margin-left: 20px;
  color: inherit;
  text-decoration: underline;
  text-underline-offset: 2px;
}

/* ===================== ENTRADA PALAVRA A PALAVRA ===================== */

/* Marcação vinda do filtro `word_reveal` (apps/website/templatetags/text.py).
   Cada palavra é uma janela mascarada embaixo e sobe de dentro dela, uma de
   cada vez — o atraso vem do índice `--i` que o filtro escreve em cada
   elemento. Tudo em CSS: roda na primeira pintura e não depende do script. */

.v-words {
  --v-word-dur: 720ms;
  --v-word-step: 90ms;
}

.v-word {
  display: inline-block;
  /* A máscara é `clip-path`, não `overflow`: um inline-block com overflow
     diferente de `visible` passa a ter como linha de base a própria borda de
     baixo, o que subiria as palavras e desalinharia o cursor ao lado delas.
     Aberta em cima e nos lados — o corte que importa é o de baixo, por onde a
     palavra entra —, para não aparar acentos nem a inclinação do itálico. */
  clip-path: inset(-0.3em -0.12em 0 -0.12em);
  /* Cancela o respiro de baixo do miolo, que senão afastaria as linhas. */
  margin-bottom: -0.16em;
}

.v-word__inner {
  display: inline-block;
  /* Respiro para que descendentes (o "q" de "que") fiquem acima do corte. */
  padding-bottom: 0.16em;
  transform: translateY(120%);
  animation: v-word-up var(--v-word-dur) var(--ease-out) forwards;
  animation-delay: calc(var(--i) * var(--v-word-step));
}

@keyframes v-word-up {
  to { transform: translateY(0); }
}

/* Cursor de inserção: entra depois da última palavra e fica piscando, como um
   campo de texto esperando a próxima letra. */
.v-caret {
  display: inline-block;
  width: 0.055em;
  min-width: 2px;
  height: 0.78em;
  margin-left: 0.1em;
  vertical-align: -0.04em;
  background: var(--vision-white);
  opacity: 0;
  animation: v-caret-blink 1.06s steps(1, end) infinite;
  /* O índice do cursor é o seguinte ao da última palavra; somar a duração faz
     ele esperar ela terminar de subir. Durante o atraso vale a opacidade 0
     acima — a animação não tem fill-mode, então não pisca antes da hora. */
  animation-delay: calc(var(--i) * var(--v-word-step) + var(--v-word-dur));
}

@keyframes v-caret-blink {
  0%, 49%   { opacity: 1; }
  50%, 100% { opacity: 0; }
}

/* ============================= REVEAL ============================= */

/* 600ms, o "large reveal" da spec (§10), e não os 360ms de "entrance": o que
   entra aqui é um bloco inteiro de seção, e em 360ms os 12px de curso passavam
   rápido demais para serem vistos. É também o tempo em que o resto do
   movimento de entrada do site já corria — a subida das palavras do hero leva
   720ms —, então o reveal deixa de ser a peça mais apressada da página. */
.v-reveal {
  opacity: 0;
  transform: translateY(12px);
  transition: opacity var(--dur-4) var(--ease-standard),
              transform var(--dur-4) var(--ease-standard);
}
.v-reveal.is-visible { opacity: 1; transform: none; }

/* Sem JS o conteúdo não pode ficar invisível. */
.no-js .v-reveal { opacity: 1; transform: none; transition: none; }

/* Entrada em cascata: o alvo do observer continua sendo o container, e quem
   sobe são os filhos diretos, um depois do outro. É no container porque é ele
   que o observer vê entrar na tela — se cada filho fosse um alvo, a ordem
   viria de onde cada um está na rolagem, e não da sequência que a gente quer.

   É a entrada padrão das aberturas de seção (olho, título, subtítulo) e dos
   blocos de texto que abrem uma coluna. Em relação ao `.v-reveal` simples não
   muda nem o curso nem a duração — a spec (§10) fixa os 12px e os 600ms, e
   cada peça faz exatamente o mesmo movimento. O que a cascata acrescenta é só
   a ordem: uma peça começa antes da outra terminar. */
.v-reveal--stagger {
  /* O passo é bem menor que a duração, então as peças correm sobrepostas — é
     dessa sobreposição que vem a leitura de movimento único, em vez de 600ms
     repetidos lado a lado. Esticar o passo é o que torna a sequência mais
     perceptível; estreitá-lo aproxima a cascata de uma entrada só. */
  --v-stagger-step: 150ms;
  --v-stagger-dur: var(--dur-4);
  /* O container não aparece nem some: ele é só a moldura da cascata. */
  opacity: 1;
  transform: none;
  transition: none;
}
.v-reveal--stagger > * {
  opacity: 0;
  transform: translateY(12px);
  transition: opacity var(--v-stagger-dur) var(--ease-standard),
              transform var(--v-stagger-dur) var(--ease-standard);
}
.v-reveal--stagger.is-visible > * { opacity: 1; transform: none; }

/* Oito passos cobrem com folga o maior bloco do site (o card de contato, com
   quatro). Além do oitavo a peça entra sem espera — uma cascata que não acaba
   deixaria o fim da seção chegando depois de quem já rolou para longe. */
.v-reveal--stagger > :nth-child(2) { transition-delay: calc(var(--v-stagger-step) * 1); }
.v-reveal--stagger > :nth-child(3) { transition-delay: calc(var(--v-stagger-step) * 2); }
.v-reveal--stagger > :nth-child(4) { transition-delay: calc(var(--v-stagger-step) * 3); }
.v-reveal--stagger > :nth-child(5) { transition-delay: calc(var(--v-stagger-step) * 4); }
.v-reveal--stagger > :nth-child(6) { transition-delay: calc(var(--v-stagger-step) * 5); }
.v-reveal--stagger > :nth-child(7) { transition-delay: calc(var(--v-stagger-step) * 6); }
.v-reveal--stagger > :nth-child(8) { transition-delay: calc(var(--v-stagger-step) * 7); }

.no-js .v-reveal--stagger > * { opacity: 1; transform: none; transition: none; }

/* Fila: os `.v-reveal` filhos deste container entram um depois do outro, na
   ordem em que cruzam o limiar. Serve às grades e às listas, onde cada item é
   alvo do observer por conta própria — a cascata acima não serviria, porque
   ali o container é o alvo, e numa lista longa os itens de baixo teriam
   animado muito antes de alguém chegar neles.

   O índice de cada um vem do `setupReveal`, em site.js, e não de um
   `nth-child`: a fila conta por leva — quem cruza o limiar no mesmo quadro —,
   então numa grade entra uma linha por vez, qualquer que seja o número de
   colunas naquele ponto de quebra. Em CSS isso seria um `nth-child(4n+k)` que
   erra a conta assim que a grade reflui. */
.v-reveal--queue { --v-queue-step: 130ms; }
.v-reveal--queue > .v-reveal {
  transition-delay: calc(var(--v-queue-i, 0) * var(--v-queue-step));
}

/* ==================== ÁRVORE DE LINKS (/minella/) ==================== */

/* Coluna estreita e centrada, sem cabeçalho nem rodapé: a página é aberta a
   partir da bio de uma rede social, quase sempre no celular, e tudo o que ela
   oferece cabe numa rolagem curta. O preto vem do `body`, que já é preto — aqui
   só entram os realces do tema escuro, porque os do `:root` são pretos
   translúcidos e sumiriam sobre ele. */
.v-tree {
  --border-2: rgba(255, 255, 255, 0.16);
  --surface-overlay: rgba(255, 255, 255, 0.05);
  --surface-overlay-strong: rgba(255, 255, 255, 0.10);
  display: grid;
  justify-items: center;
  gap: 26px;
  width: 100%;
  max-width: 560px;
  margin: 0 auto;
  padding: 64px 20px 88px;
  text-align: center;
}

.v-tree__head {
  display: grid;
  justify-items: center;
  gap: 18px;
}

/* O retrato é redondo e o arquivo pode não ser: `object-fit` recorta ao centro
   em vez de espremer o rosto na moldura. */
.v-tree__photo {
  width: 132px;
  height: 132px;
  border-radius: 50%;
  object-fit: cover;
  display: block;
  margin: 0;
}
.v-tree__name {
  font-family: var(--font-display);
  font-size: clamp(30px, 8vw, 42px);
  font-weight: var(--fw-bold);
  line-height: 1.05;
  letter-spacing: var(--ls-display);
  margin: 0;
}

.v-tree__social {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 14px;
  list-style: none;
  margin: 0;
  padding: 0;
}
/* Círculos de toque: 52px é o alvo confortável no celular, acima do mínimo de
   44px das diretrizes de acessibilidade. */
.v-tree__social-link {
  display: grid;
  place-items: center;
  width: 52px;
  height: 52px;
  border-radius: 50%;
  border: 1px solid var(--border-2);
  color: var(--fg-1);
  transition: border-color var(--dur-2) var(--ease-standard),
              color var(--dur-2) var(--ease-standard),
              background var(--dur-2) var(--ease-standard),
              transform var(--dur-2) var(--ease-standard);
}
.v-tree__social-link:hover {
  border-color: var(--vision-purple);
  color: var(--vision-purple);
  background: var(--surface-overlay);
  transform: translateY(-2px);
}
.v-tree__social-link .v-icon { width: 22px; height: 22px; }

/* Três partes numa linha: marca, texto e seta. As pontas têm a largura do
   conteúdo e o meio fica com o que sobra, então o texto pode quebrar em duas
   linhas sem empurrar o lagarto ou a seta para fora. */
.v-tree__pitch {
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: center;
  gap: 16px;
  width: 100%;
  padding: 18px 20px;
  border-radius: var(--radius-lg);
  border: 1px solid var(--border-2);
  background: var(--surface-overlay);
  text-align: left;
  transition: border-color var(--dur-2) var(--ease-standard),
              background var(--dur-2) var(--ease-standard);
}
.v-tree__pitch:hover {
  border-color: var(--vision-purple);
  background: var(--surface-overlay-strong);
}
.v-tree__pitch-mark {
  display: grid;
  place-items: center;
  width: 46px;
  height: 46px;
  border-radius: var(--radius-md);
  /* Roxo rebaixado, e não o cheio: o botão de cima é que carrega o roxo sólido,
     e dois blocos na mesma cor disputariam a atenção. */
  background: rgba(169, 147, 255, 0.14);
  color: var(--vision-purple);
}
.v-tree__pitch-mark .v-icon { width: 26px; height: 26px; }
.v-tree__pitch-text { display: grid; gap: 3px; min-width: 0; }
.v-tree__pitch-title {
  font-size: var(--fs-h5);
  font-weight: var(--fw-semibold);
  line-height: 1.25;
}
.v-tree__pitch-sub {
  font-size: var(--fs-body-sm);
  line-height: 1.4;
  color: var(--fg-2);
}
.v-tree__pitch-arrow {
  width: 20px;
  height: 20px;
  color: var(--fg-3);
  transition: transform var(--dur-2) var(--ease-standard),
              color var(--dur-2) var(--ease-standard);
}
.v-tree__pitch:hover .v-tree__pitch-arrow {
  transform: translateX(3px);
  color: var(--vision-purple);
}

.v-tree__services { width: 100%; margin-top: 14px; }
.v-tree__services-title {
  font-family: var(--font-display);
  font-size: clamp(26px, 7vw, 34px);
  font-weight: var(--fw-bold);
  line-height: 1.1;
  letter-spacing: var(--ls-display);
  margin: 0 0 22px;
}

/* Carrossel: os cinco serviços numa faixa só, que corre para o lado. É rolagem
   nativa, sem JavaScript — o dedo arrasta no celular, o trackpad desliza no
   computador e o Tab entre os botões traz cada card para dentro da vista
   sozinho. O `scroll-snap` de proximidade acomoda o card no lugar quando a
   rolagem para perto de uma borda, mas não briga com quem está navegando pelo
   teclado, como o `mandatory` faria.

   As margens negativas cancelam o respiro lateral da coluna: a faixa encosta
   nas duas bordas e os cards sobram para fora, e é essa sobra que diz que há
   mais coisa para o lado. O `padding` devolve o alinhamento, então o primeiro
   card nasce na mesma linha do resto da página.

   A barra de rolagem fica escondida de propósito: ela atravessaria a faixa logo
   abaixo dos botões, e o card cortado na borda já cumpre o papel de dizer que
   dá para arrastar. */
.v-tree__cards {
  display: flex;
  gap: 16px;
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scroll-snap-type: x proximity;
  scroll-padding-left: 20px;
  list-style: none;
  margin: 0 -20px;
  padding: 4px 20px 12px;
  scrollbar-width: none;
}
.v-tree__cards::-webkit-scrollbar { display: none; }

/* Título e botão vão por cima da imagem, no pé do card. Com o texto dentro, o
   card fica em retrato: deitado, a faixa que o texto ocupa comeria metade da
   foto e não sobraria imagem para ver. */
.v-tree-card {
  position: relative;
  overflow: hidden;
  flex: 0 0 200px;
  aspect-ratio: 3 / 4;
  scroll-snap-align: start;
  border-radius: var(--radius-md);
  /* Cor de espera enquanto o arquivo não chegou, como no cabeçalho das abas. */
  background-color: var(--vision-ink);
}
/* Serve à foto e ao vídeo: o card aceita os dois, e o que muda entre eles é só
   o elemento. `display: block` porque `<video>` nasce inline e herdaria a
   folga da linha de texto. */
.v-tree-card__media {
  position: absolute;
  inset: 0;
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform var(--dur-3) var(--ease-standard);
}
.v-tree-card:hover .v-tree-card__media { transform: scale(1.04); }

/* Véu que escurece só o pé, onde o texto fica: as fotos vão do quase preto ao
   claro, e sem ele o título sumiria sobre as claras. Começa transparente na
   metade de cima para a imagem continuar sendo o que se vê primeiro. */
.v-tree-card::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg,
              rgba(10, 10, 10, 0) 38%,
              rgba(10, 10, 10, 0.62) 68%,
              rgba(10, 10, 10, 0.9) 100%);
}
/* Acima do véu — o `::after` vem depois na pintura. */
.v-tree-card__body {
  position: absolute;
  z-index: 1;
  inset: auto 0 0 0;
  display: grid;
  justify-items: center;
  gap: 10px;
  padding: 14px 12px 14px;
}
.v-tree-card__title {
  font-size: var(--fs-body-lg);
  font-weight: var(--fw-semibold);
  line-height: 1.15;
  color: var(--vision-white);
  text-align: center;
  margin: 0;
}
/* Sobre foto, o contorno sozinho não segura o botão: o fundo escurecido e o
   desfoque dão a ele uma superfície própria, e a borda fica mais firme que a do
   fantasma padrão, que foi calibrada para fundo chapado. */
.v-tree-card__cta {
  background: rgba(10, 10, 10, 0.5);
  border-color: rgba(255, 255, 255, 0.38);
  -webkit-backdrop-filter: blur(5px);
  backdrop-filter: blur(5px);
}
.v-tree-card__cta:hover {
  background: rgba(10, 10, 10, 0.72);
  border-color: var(--vision-purple);
}

/* A página das cinco perguntas usa a mesma coluna, sem os blocos da árvore. */
.v-tree--simples { gap: 18px; padding-top: 96px; }

/* ============================= RESPONSIVO ============================= */

@media (max-width: 1024px) {
  /* Seis itens no menu já espremem os lados da grade da nav nessa faixa. */
  .v-nav__links { gap: 16px; }
  .v-dna { grid-template-columns: 1fr; gap: 48px; }
  /* Empilhado, o celular vem depois do texto — e encolhe, porque agora
     divide a altura da tela com o bloco inteiro em vez de ficar ao lado. */
  .v-bills { grid-template-columns: 1fr; gap: 48px; }
  .v-iphone { --v-iphone-h: clamp(380px, 52vh, 520px); }
  .v-services__head { grid-template-columns: 1fr; align-items: start; gap: 24px; }
  .v-tabs { justify-content: flex-start; }
  .v-service-info { grid-template-columns: 1fr; }
  .v-cases { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .v-feedbacks { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .v-services { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .v-quote, .v-cta { padding: 56px 40px; }
}

@media (max-width: 768px) {
  .v-page { --v-nav-h: 69px; }

  .v-tree { padding: 48px 20px 72px; gap: 22px; }
  .v-tree__photo { width: 112px; height: 112px; }
  .v-tree__pitch { gap: 13px; padding: 16px; }
  .v-tree__pitch-mark { width: 42px; height: 42px; }

  .v-container { padding: 0 20px; }
  .v-profile-card__head { padding: 20px; }
  .v-profile-card__body { padding: 20px; }
  .v-profile-card__foot { padding: 16px 20px; }
  .v-bills__ctas { margin-top: 28px; }
  .v-service-title { margin-bottom: 32px; }
  .v-service-info__num { font-size: 64px; }
  .v-service-info { margin: 0 -20px; }
  .v-service-info__item { padding: 16px 20px; gap: 20px; }
  .v-service-callout { padding: 40px 24px; }
  .v-service-callout__title { font-size: var(--fs-h3); }
  .v-cases { grid-template-columns: 1fr; }
  .v-feedbacks { grid-template-columns: 1fr; }
  .v-faq__question { gap: 14px; padding: 22px 0; }
  .v-faq__label { font-size: var(--fs-h5); }
  .v-faq__text { padding: 0 0 24px calc(14px + 2ch); }
  .v-service-meta { grid-template-columns: 1fr; gap: 14px; }
  .v-service-meta__pair { padding-right: 0; }
  .v-service-meta__pair + .v-service-meta__pair {
    padding: 14px 0 0;
    border-left: none;
    border-top: 1px solid var(--border-2);
  }
  /* Única faixa em que a frase não cabe em uma linha nos 20px: aqui o corpo
     cede para a linha não estourar o card. */
  .v-profile-card__phrase { font-size: clamp(12px, 3.9vw, var(--fs-body-lg)); }
  .v-nav {
    display: flex;
    justify-content: space-between;
    padding: 14px 20px;
    /* A barra é sticky, então ocupa espaço no topo: a abertura do hero desconta
       esta altura para caber em uma tela. Fixá-la aqui mantém a conta válida —
       14px de padding em cima e embaixo, 40px do botão do menu, 1px da borda. */
    min-height: var(--v-nav-h);
  }

  /* O menu vira uma gaveta controlada pelo botão; sem JS, o botão some e os
     links continuam visíveis empilhados. */
  .v-nav__toggle { display: block; }
  .v-nav__links {
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    /* Desfaz o `justify-self: center` que centraliza o menu na grade do
       desktop: em um elemento absoluto, um alinhamento diferente de
       `normal`/`stretch` faz a caixa ignorar `left`/`right` e encolher até o
       conteúdo — a gaveta abria como uma coluna estreita no meio da tela. */
    justify-self: stretch;
    flex-direction: column;
    align-items: flex-start;
    gap: 4px;
    padding: 20px;
    background: rgba(10, 10, 10, 0.96);
    backdrop-filter: blur(12px);
    -webkit-backdrop-filter: blur(12px);
    border-bottom: 1px solid rgba(255, 255, 255, 0.08);
    font-size: var(--fs-h5);
  }
  .v-nav__links[hidden] { display: none; }
  .v-nav__link { padding: 10px 0; width: 100%; }
  .v-nav__link[aria-current="true"]::after { display: none; }
  .v-nav__cta { display: none; }

  .v-section { padding: 72px 0; }
  .v-hero { padding: 0 0 88px; --v-hero-measure: 100%; }
  /* No celular a abertura ocupa a primeira tela e o texto fica no meio dela —
     centralizado só na vertical: as linhas continuam alinhadas à esquerda.
     `svh` mede a tela com a barra do navegador visível, então o bloco não
     salta quando ela se recolhe. Passando disso, o conteúdo simplesmente
     empurra o bloco para baixo. */
  .v-hero__lead {
    display: flex;
    flex-direction: column;
    justify-content: center;
    min-height: calc(100svh - var(--v-nav-h));
    padding: 32px 0;
  }
  .v-hero__title { font-size: clamp(40px, 10.5vw, 68px); }
  /* No celular os três continuam lado a lado, agora dividindo a largura em
     partes iguais — com colunas do tamanho do conteúdo, a mais estreita
     encolheria demais. */
  .v-hero__stats {
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 20px 12px;
    margin-top: 56px;
  }
  .v-stat__value { font-size: 30px; }
  .v-services { grid-template-columns: 1fr; }
  /* Sem espaço para marca e canais lado a lado: empilha, tudo à esquerda. */
  .v-footer__top { flex-direction: column; align-items: flex-start; gap: 28px; }
  .v-quote, .v-cta { padding: 40px 24px; }
  /* Três tópicos lado a lado não cabem em 390px: a faixa empilha e a divisão
     passa a ser a borda de cima. */
  .v-cta__meta { flex-direction: column; gap: 12px; }
  .v-cta__meta-pair { padding: 0; }
  .v-cta__meta-pair + .v-cta__meta-pair {
    padding-top: 12px;
    border-left: none;
    border-top: 1px solid rgba(10, 10, 10, 0.28);
  }
  .v-quote::before { font-size: 140px; top: 12px; right: 24px; }
  .v-section__head { margin-bottom: 40px; }
}

/* Terceiro ponto de quebra, só para a árvore de links: os outros dois nasceram
   das grades largas da home, e nada nela precisa deste tamanho. */
@media (max-width: 480px) {
  /* Card mais estreito para o seguinte aparecer cortado na borda: é esse pedaço
     à mostra que conta que a faixa anda para o lado. Com 200px na tela mais
     estreita o segundo card mal apareceria, e o carrossel passaria por uma
     imagem solta. */
  .v-tree-card { flex-basis: 168px; }
  .v-tree__social { gap: 12px; }
}

/* ============================= TEXTO LEGAL ============================= */

/* Coluna de leitura para a política de privacidade. A medida em `ch` acompanha
   a fonte: é a largura em que o olho acha a linha seguinte sem se perder, e
   vale mais que um valor em pixels que muda de sentido a cada tipo. */
.v-legal {
  max-width: 68ch;
  color: var(--fg-2);
}
.v-legal .v-section__title { margin-bottom: 8px; }
.v-legal__date {
  margin: 0 0 32px;
  font-size: var(--fs-caption);
  color: var(--fg-3);
}
.v-legal__lead {
  font-size: var(--fs-body-lg, 18px);
  color: var(--fg-1);
}
/* O espaço acima do título é maior que o abaixo: é isso que amarra cada
   cabeçalho ao texto que ele abre, em vez de deixá-lo solto entre dois blocos. */
.v-legal h2 {
  margin: 40px 0 12px;
  font-family: var(--font-display);
  font-size: var(--fs-h3);
  font-weight: var(--fw-bold);
  line-height: 1.2;
  color: var(--fg-1);
}
.v-legal p {
  margin: 0 0 16px;
  line-height: 1.65;
}
.v-legal .v-btn { margin-top: 8px; }

/* ============================= PÁGINA 404 ============================= */

/* Coluna única e centrada: não há conteúdo para comparar nem para percorrer,
   só um caminho de volta. Tudo o que a página faz é dizer onde a pessoa está e
   oferecer a saída. */
.v-404 { padding: 120px 0; }
.v-404__inner {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
}

/* O número é ornamento, não informação — quem chega aqui já sabe que algo deu
   errado, e "404" só diz isso a quem conhece o código. Por isso ele fica em
   tom apagado, um degrau abaixo do recado, e `aria-hidden` no HTML o tira da
   leitura em voz alta, onde soletrar "quatro zero quatro" não ajudaria
   ninguém.

   A folga abaixo é dele mesmo: a `line-height` apertada que o número precisa
   encosta o título nele, e sem esse respiro os dois leem como um bloco só. */
.v-404__code {
  margin: 0 0 var(--space-5);
  font-family: var(--font-display);
  font-size: clamp(88px, 18vw, 200px);
  line-height: var(--lh-display);
  letter-spacing: var(--ls-display);
  color: var(--vision-purple-soft);
  user-select: none;
}

.v-404__title {
  margin: 0 0 var(--space-4);
  font-family: var(--font-display);
  font-size: clamp(32px, 5vw, 56px);
  line-height: var(--lh-heading);
  letter-spacing: var(--ls-heading);
  color: var(--fg-1);
}

.v-404__text {
  max-width: 46ch;
  margin: 0 0 var(--space-7);
  font-size: var(--fs-body-lg);
  color: var(--fg-3);
}

@media (max-width: 640px) {
  .v-404 { padding: 72px 0; }
}

/* ============================= CONSENTIMENTO ============================= */

/* Faixa no pé da tela. Fica acima de tudo, mas não cobre a página inteira: a
   escolha não impede a leitura, e prender quem só quer ler seria pior que não
   perguntar. O `max-width` acompanha o container do site para a faixa não
   virar uma linha de texto larga demais nas telas grandes. */
.v-consent {
  position: fixed;
  z-index: 60;
  left: 16px;
  right: 16px;
  /* Acima da barra de gestos do iOS, que come os últimos pixels. */
  bottom: max(16px, env(safe-area-inset-bottom));
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 20px;
  max-width: 1216px;
  margin: 0 auto;
  padding: 16px 20px;
  border-radius: var(--radius-md);
  /* O banner vive fora de qualquer `.v-section`, então precisa trocar os
     tokens de borda por conta própria — os do `:root` são pretos translúcidos
     e sumiriam sobre este fundo escuro. Mesma troca que `.v-section--dark`. */
  --border-2: rgba(255, 255, 255, 0.16);
  border: 1px solid var(--border-2);
  background: var(--vision-ink);
  color: var(--vision-paper);
  box-shadow: 0 12px 32px rgba(0, 0, 0, 0.45);
}
/* `display: flex` acima é estilo de autor e venceria o `[hidden] {display:none}`
   da folha do navegador, que tem a mesma especificidade. Sem esta linha o
   banner apareceria mesmo escondido — inclusive depois de respondido. */
.v-consent[hidden] { display: none; }

/* Sublinhado permanente: dentro de um bloco de texto curto, cor sozinha não
   basta para dizer que é link — e não se deve exigir que o visitante passe o
   mouse para descobrir onde está a política. */
.v-consent__link {
  color: var(--vision-purple);
  text-decoration: underline;
  text-underline-offset: 2px;
  white-space: nowrap;
}
.v-consent__link:hover { color: var(--vision-white); }

.v-consent__text {
  margin: 0;
  font-size: var(--fs-body-sm);
  line-height: 1.45;
  color: var(--vision-paper);
}
/* Os botões não encolhem: o texto é que quebra linha antes deles. */
.v-consent__actions {
  display: flex;
  flex-shrink: 0;
  gap: 10px;
}

@media (max-width: 640px) {
  /* Empilha, e os botões passam a dividir a largura em partes iguais — recusar
     com o mesmo alcance de polegar que aceitar. */
  .v-consent {
    flex-direction: column;
    align-items: stretch;
    gap: 14px;
  }
  .v-consent__actions > * { flex: 1; justify-content: center; }
}

/* ============================= MOVIMENTO REDUZIDO ============================= */

@media (prefers-reduced-motion: reduce) {
  .v-marquee { overflow-x: auto; }
  .v-marquee__track { animation: none; }
  .v-marquee__group[aria-hidden] { display: none; }

  html { scroll-behavior: auto; }
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    /* Zerar também a espera, não só a duração: com movimento reduzido o JS
       marca tudo como visível de uma vez, e um `transition-delay` sobrando
       faria o conteúdo chegar aos pedaços — devagar — justamente para quem
       pediu o contrário. */
    animation-delay: 0ms !important;
    transition-delay: 0ms !important;
  }
  .v-reveal { opacity: 1; transform: none; }
  .v-reveal--stagger > * { opacity: 1; transform: none; }
  /* O fundo continua lá, parado: ele é cor de ambiente, não informação. */
  .v-aura::before,
  .v-aura::after,
  .v-section--glow::before,
  .v-section--glow::after { animation: none; }
  .v-dna__text .v-section__title em { opacity: 1; transform: none; }
  .v-dna__accent::before { transform: scaleY(1); }
  /* Sem o risco o card já nasce fechado: fundo, borda e conteúdo valem desde a
     primeira pintura, sem esperar o `.is-visible` que o script põe — como o
     `.v-reveal` acima. O anel sai de cena de uma vez; o `--v-card-traco` a 100%
     evita o contorno pela metade no quadro único que a duração encurtada
     deixaria passar. */
  .v-dna .v-profile-card {
    transform: none;
    background: var(--vision-ink);
    border-color: rgba(255, 255, 255, 0.08);
  }
  .v-dna .v-profile-card > * { opacity: 1; }
  .v-dna .v-profile-card::before { --v-card-traco: 100%; opacity: 0; }
  /* Sem a subida, a palavra tem de estar no lugar; sem a piscada, o cursor
     ainda marca o fim da frase. */
  .v-word__inner { transform: none; }
  .v-caret { opacity: 1; animation: none; }
  /* Sem a varredura o ponto continua legível como indicador de "estamos aqui". */
  .v-pulse::before,
  .v-pulse::after { display: none; }
}
