/* =============================================================================
   TAKA DESIGN — 5ª DOBRA: PROCESSO / COMO FUNCIONA
   -----------------------------------------------------------------------------
   Carregue DEPOIS de passage.css e services.css: este arquivo consome a
   variante .spectrum-rule--v declarada em passage.css e assume a superfície
   opaca que services.css documentou para cartões que se sobrepõem.

   Tudo aqui vem do Design System PRISMA. Nenhum token novo de cor, tipografia
   ou motion é criado:
     · .spectrum-rule / .spectrum-rule--v  → o fio de entrada, o trilho vertical
                                             do modo linear e o arco de progresso
     · --ds-spectrum                       → o arco da órbita e a borda do node
                                             ativo (mesma receita de .btn-outline:
                                             duas camadas de background, uma
                                             recortada em padding-box e outra em
                                             border-box)
     · .badge badge-pill badge-pink        → o cabeçalho numerado do DS
                                             ("0X · Nome da seção"); rosa é o
                                             próximo da sequência do --ds-spectrum
                                             depois do índigo (Sobre) e do violeta
                                             (Serviços)
     · .badge badge-pill badge-neutral     → a qualificação "OPCIONAL" da etapa 06
     · .t-h2 / .t-h3 / .t-body / .t-label / .t-caption
     · .aurora                             → a atmosfera que toda dobra carrega
     · --ds-void / --ds-surface / --ds-r-* / --ds-ease

   O RITMO DA DOBRA
   ----------------
   Hero = impacto · Confiança = precisão · Sobre = humano · Serviços = produto ·
   Processo = método. Depois da intensidade do coverflow, esta dobra entra de
   forma PRECISA: nada de uma segunda animação cinematográfica: uma linha desce,
   vira círculo, e o círculo é uma timeline.

     fio de entrada → cabeçalho editorial → órbita presa (6 estados) → solta

   A COSTURA COM A DOBRA ANTERIOR
   ------------------------------
   Serviços fecha com a régua espectral abrindo A PARTIR do centro. Aqui a
   mesma linha CONTINUA: um fio vertical desce pelo eixo central atravessando o
   cabeçalho, chega ao palco e encosta em 12 horas da órbita — que é
   exatamente onde o arco espectral nasce. O gesto novo é a conversão: a linha
   reta vira circunferência. É a mesma gramática de Spectrum das quatro dobras
   anteriores (ambiente → conexão → progresso → caminho), não um vocabulário
   visual novo.

   DOIS ESTADOS DE LAYOUT
   ----------------------
   linear (padrão)    Timeline vertical espectral: os seis passos empilhados,
                      cada um com índice, título e descrição completos. É o que
                      o navegador desenha sem JS, sem GSAP, com o CDN fora do ar,
                      com prefers-reduced-motion — e é também a apresentação
                      deliberada de tablet retrato e mobile (item 27 do briefing:
                      responsividade de EXPERIÊNCIA, não perda de conceito).
   orbital (.is-orbital)
                      process.js confirma GSAP + movimento permitido + espaço
                      suficiente: o palco gruda no topo, os seis nodes assumem a
                      circunferência e o painel central passa a apresentar a
                      etapa ativa.

   O conteúdo é IDÊNTICO nos dois: os mesmos seis <li> de uma <ol>, na mesma
   ordem, com os mesmos títulos e as mesmas descrições. Só a posição espacial
   muda. Nada é gerado por JavaScript.

   Sticky em vez de `pin: true` do ScrollTrigger — a mesma escolha de
   passage.css e services.css, pelos mesmos motivos: nenhum pin-spacer injetado
   no fluxo, nenhum risco de salto no refresh, reversão com uma única classe e
   zero interferência na detecção de direção de scroll do header.
   ============================================================================= */

/* -----------------------------------------------------------------------------
   Trilho da seção
   -------------------------------------------------------------------------- */
.process {
  /* Vão que o fio de entrada atravessa antes do cabeçalho. */
  --pr-lead: clamp(3rem, 7vh, 5.5rem);
  /* Respiro entre o cabeçalho e o palco. O fio continua descendo por ele. */
  --pr-gap: clamp(1.75rem, 4.5vh, 3.5rem);
  --pr-tail: clamp(4rem, 9vh, 6.5rem);

  /* Curso de rolagem consumido pela timeline, além dos 100vh do palco.
     São CINCO travessias (01→02→…→06) e seis platôs. 260vh dá ~42vh de curso
     por travessia — na mesma ordem de grandeza dos 63vh que cada travessia do
     coverflow de Serviços consome, mas menor de propósito: girar 60° é um
     movimento mais curto que deslocar um baralho inteiro em perspectiva.
     O item 22 do briefing pede exatamente isso: distância confortável, não
     "6 etapas = 6000px". Em 1440 × 900 o pin inteiro custa 2340px. */
  --pr-travel: 260vh;

  /* Diâmetro da órbita. Limitado pelos TRÊS eixos ao mesmo tempo: altura do
     palco (a órbita precisa caber em 100vh com respiro para os chips dos nodes
     e para a dica de scroll), um teto absoluto para não virar um círculo
     desproporcional em telas muito altas, e a largura.

     O teto de 44rem só entra a partir de ~1100px de ALTURA de janela; abaixo
     disso quem manda é o 64vh, então notebook (1440 × 900 → 576px) e tablet
     acompanham sozinhos. Medido em 2560 × 1261, com o teto anterior de 40rem a
     órbita ocupava 51% da altura do palco e se perdia no vazio; 44rem leva a
     56% sem chegar perto do painel — a folga entre o canto do painel e o node
     de 2h sobe de 63px para 93px. */
  --pr-size: min(64vh, 44rem, 74vw);

  /* Diâmetro do node. O clamp acompanha a altura porque é ela que limita a
     órbita — um node que não escala junto com o raio deixa de parecer parte
     do mesmo sistema. */
  --pr-node: clamp(3.25rem, 6.4vh, 4.5rem);

  /* Largura do painel central. 62% do diâmetro cabe com folga: as posições
     3h e 9h da órbita estão VAZIAS (com seis nodes a 60° a partir de 12h, os
     ângulos ocupados são 12h, 2h, 4h, 6h, 8h e 10h), então o painel pode ser
     mais largo que o quadrado inscrito sem chegar perto de node nenhum.
     Medido nos quatro breakpoints de teste, a folga entre o canto do painel e
     a borda do node mais próximo (2h) fica entre 37px e 63px. */
  --pr-panel-w: min(calc(var(--pr-size) * .62), 24rem);

  position: relative;
  isolation: isolate;
  overflow: clip;                    /* contém o sangramento da .aurora */
  background: var(--ds-void);
  padding-bottom: var(--pr-tail);
  scroll-margin-top: var(--header-h);
}

/* --- Profundidade da dobra inteira ------------------------------------------
   Mesma política de .services::before: uma única camada, sem clip nenhum,
   cobrindo a seção inteira. É ela que garante que não exista corte perceptível
   de background entre o fim de Serviços e o começo do Processo (item 24) — as
   duas seções descansam em --ds-void e o campo desta nasce difuso na região
   do cabeçalho, sem aresta.

   Cada parada final é `rgba(cor, 0)` e não `transparent`, pelo motivo
   documentado em passage.css: nenhum navegador passa por um cinza no meio da
   interpolação. Índigo, violeta e ciano são os mesmos do --ds-spectrum. */
.process::before {
  content: '';
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  background:
    /* --- A PONTE ---------------------------------------------------------
       BUG MEDIDO — corte reto de background na entrada da dobra.
       Capturado em 2560 × 1261 na altura exata da junta (y = 280 da janela):
       acima, tom violeta/rosa; abaixo, azul-preto. Uma aresta horizontal
       nítida atravessando a largura toda — o "corte perceptível de background"
       que o item 24 proíbe.

       A CAUSA está do lado de cima e não podia ser tocada: os dois campos de
       acento do CTA de Serviços são centrados em 97% e 99% da altura DAQUELA
       seção, ou seja, praticamente na borda. Eles chegam ao limite ainda perto
       do alfa cheio (calculado a partir de services.css: ~.045 de violeta e
       ~.040 de rosa em x = 88–92%) e são cortados ali. Serviços está aprovado
       e fechado; a emenda tinha que ser feita deste lado.

       Estes dois gradientes RECONSTROEM aquele valor em y = 0 — mesma paleta,
       mesmos parâmetros horizontais (85% em 88%, 58% em 92%) e mesmo
       enviesamento para a direita — e o dissipam ao longo de algumas centenas
       de pixels. A borda deixa de existir porque os dois lados passam a
       entregar a mesma cor nela.

       O raio VERTICAL vai em pixels e não em porcentagem: a altura desta seção
       muda com o viewport (o trilho tem 100vh + 260vh), e uma rampa
       percentual mudaria de comprimento junto — a emenda precisa de uma
       distância de dissipação estável. */
    radial-gradient(85% 620px at 88% 0%,
      rgba(168, 85, 247, .048), rgba(168, 85, 247, 0) 74%),
    radial-gradient(58% 400px at 92% 0%,
      rgba(236, 72, 153, .042), rgba(236, 72, 153, 0) 72%),

    /* --- O campo da dobra ------------------------------------------------
       Separa a órbita do vazio — ver .process__ambient, que modula o miolo
       dele durante o pin. Aqui fica só a base difusa.

       O centro está em 40% da altura com raio vertical de 40%: assim ele vale
       exatamente ZERO na borda superior. Não é estética — é a condição para a
       ponte acima funcionar. Na primeira versão este campo era `52% at 50%
       12%` e somava ~.036 de índigo em y = 0; a ponte apagava o degrau
       violeta/rosa e este campo criava outro, azul, no mesmo lugar. */
    radial-gradient(110% 40% at 50% 40%,
      rgba(99, 102, 241, .06), rgba(99, 102, 241, 0) 68%),
    radial-gradient(70% 30% at 14% 84%,
      rgba(34, 211, 238, .03), rgba(34, 211, 238, 0) 70%);
}

/* -----------------------------------------------------------------------------
   Introdução — o fio de entrada e o cabeçalho editorial
   -----------------------------------------------------------------------------
   O fio ocupa a altura INTEIRA da introdução (padding + cabeçalho), então ele
   não precisa saber quantas linhas o H2 tem: a geometria é do próprio bloco.
   Passa ATRÁS do cabeçalho (z-index 0 contra 2) e no eixo central, onde a
   headline — alinhada à esquerda e limitada a 22ch — nunca chega.
   -------------------------------------------------------------------------- */
.process__intro {
  position: relative;
  z-index: 1;
  padding-top: var(--pr-lead);
  padding-bottom: var(--pr-gap);
}

.process__seam {
  position: absolute;
  top: 0;
  bottom: 0;
  left: 50%;
  z-index: 0;
  width: 1px;
  translate: -50% 0;
  transform-origin: 50% 0%;
  pointer-events: none;
}
/* A máscara padrão de .spectrum-rule--v dissipa as DUAS pontas. A ponta de
   baixo precisa chegar sólida: ela não termina, ela é entregue ao trecho
   seguinte do mesmo fio dentro do palco (.process__approach).

   BUG MEDIDO — a ponta de CIMA também precisava ser sólida, e não era.
   Este fio recebe o traço de .services__cta-drop (services.css), que termina
   exatamente onde este começa (0px de vão, geometria exata — ver a nota lá).
   Com a ponta de cima ainda fadeando dos primeiros 14% (herdado do padrão do
   componente, pensado para um fio nascendo do nada), as duas pontas — a cauda
   esmaecida de um lado, a cabeça esmaecida do outro — se encontravam já
   apagadas, e a vizinhança inteira da costura ficava visivelmente escura.
   Capturado em screenshot: um trecho real de linha "apagada" entre as duas
   dobras. Sólida nas duas pontas, o traço todo lê como um fio só. */
.process__seam > .spectrum-rule--v {
  -webkit-mask-image: none;
          mask-image: none;
}

/* BUG MEDIDO — o fio e o trilho em eixos diferentes.
   Capturado em 390 × 844: o fio de entrada descia no eixo central da página
   (x = 195) e o trilho da timeline vertical corria pela coluna dos nodes
   (x = 44). Eram duas linhas paralelas separadas por 150px, e a continuidade
   que a dobra inteira constrói — Serviços fecha, o fio desce, o fio VIRA o
   caminho — simplesmente não existia no modo linear.

   No orbital o eixo certo é o centro, porque a órbita é simétrica. No linear o
   eixo certo é o dos nodes, porque é ali que o caminho de fato acontece. A
   conta reproduz a geometria do .container (max-width centralizado + gutter)
   mais meio node — o `max()` zera a parte da centralização em qualquer
   viewport menor que --app-container-max, que é onde o modo linear vive.

   E aí o fio deixa de atravessar a introdução inteira: nesse eixo ele passaria
   POR CIMA do badge e da headline, que no linear começam no mesmo gutter. Ele
   passa a ocupar só o vão entre o cabeçalho e a lista — curto, mas terminando
   exatamente onde o trilho começa (medido: fim do fio e topo do trilho ambos
   em y = 237), que é o que a costura precisa entregar. */
.process:not(.is-orbital) .process__seam {
  top: auto;
  bottom: 0;
  height: var(--pr-gap);
  left: calc(max(0px, (100% - var(--app-container-max)) / 2)
             + var(--app-gutter) + var(--pr-node) / 2);
  translate: 0 0;
}

.process__head {
  position: relative;
  z-index: 2;
}

.process__eyebrow { margin-bottom: clamp(1rem, 2.1vh, 1.5rem); }

.process__headline {
  margin: 0;
  color: #fff;
  max-width: 22ch;
}

/* Revelação por linha, mesmo princípio de .split-line do DS (invólucro com
   overflow, filho em bloco). Classe própria porque .split-line > span carrega
   `animation … both`, que venceria o estilo inline do GSAP. */
.process__line {
  display: block;
  overflow: hidden;
  padding-bottom: .18em;
  margin-bottom: -.18em;
}
.process__line > span {
  display: block;
  text-wrap: balance;
}

/* -----------------------------------------------------------------------------
   A experiência — trilho e palco
   -----------------------------------------------------------------------------
   No estado linear os dois são invólucros inertes: o palco não gruda, não tem
   altura fixa e não recorta nada.
   -------------------------------------------------------------------------- */
.process__experience { position: relative; }

.process.is-orbital .process__experience {
  min-height: calc(100vh + var(--pr-travel));
  min-height: calc(100svh + var(--pr-travel));
}

.process__stage { position: relative; }

.process.is-orbital .process__stage {
  position: sticky;
  top: 0;
  height: 100vh;
  height: 100svh;
  display: grid;
  place-items: center;
  /* O palco é a moldura da experiência: o chip de um node não deve empurrar a
     página nem aparecer sobre a seção seguinte. */
  overflow: hidden;
}

/* --- Atmosfera do palco -----------------------------------------------------
   "Toda dobra segue o mesmo esqueleto: régua espectral, atmosfera animada ao
   fundo, cabeçalho e conteúdo" — design_system.html.

   Um único campo radial, muito maior que a órbita e de alfa baixo: no teto de
   .05 que .trust__ambient, .about__ambient e .services__ambient já usam. Grande
   e difuso a esse ponto ele não tem borda perceptível — não se lê como um
   círculo de luz atrás do círculo da órbita (que seria exatamente o "holograma"
   que o item 18 proíbe), se lê como o fundo tendo profundidade ali.

   A máscara existe pela mesma causa medida em services.css: o palco tem
   overflow:hidden e esta camada o cobre inteiro; sem ela o brilho terminaria
   na borda do palco com o alfa que tivesse naquele ponto, e não em zero — uma
   aresta reta na entrada e outra na saída do pin.
   -------------------------------------------------------------------------- */
.process__ambient {
  /* Só no orbital: no modo linear a atmosfera da dobra é a camada de base
     (.process::before), que já cobre a seção inteira sem clip nenhum. Um
     campo radial dimensionado para um palco de 100vh, esticado sobre uma
     lista de seis etapas, não descreveria nada. */
  display: none;
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  -webkit-mask-image: linear-gradient(180deg,
      transparent 0%, #000 15%, #000 85%, transparent 100%);
          mask-image: linear-gradient(180deg,
      transparent 0%, #000 15%, #000 85%, transparent 100%);
}
.process.is-orbital .process__ambient { display: block; }
.process__ambient::before {
  content: '';
  position: absolute;
  inset: -10% -8%;
  background: radial-gradient(64% 74% at 50% 50%,
      rgba(99, 102, 241, .07), rgba(99, 102, 241, 0) 72%);
}
/* O acento que acompanha a etapa ativa. --pr-warm sai de process.js e vai de 0
   (etapa 01) a 1 (etapa 06): o campo migra de índigo para ciano ao longo da
   jornada, no mesmo teto de alfa. É o fundo registrando que o processo
   avançou — nunca uma troca de cor perceptível quadro a quadro. */
.process__ambient::after {
  content: '';
  position: absolute;
  inset: 0;
  background:
    radial-gradient(48% 54% at 50% 48%,
      rgba(168, 85, 247, calc(.05 * (1 - var(--pr-warm, 0)))), rgba(168, 85, 247, 0) 72%),
    radial-gradient(48% 54% at 50% 48%,
      rgba(34, 211, 238, calc(.042 * var(--pr-warm, 0))), rgba(34, 211, 238, 0) 72%);
}

/* BUG MEDIDO — recorte reto de fundo na fronteira Processo → Portfólio.
   Ver a nota espelhada em .portfolio__aurora (portfolio.css): dois blobs
   .aurora independentes e animados se encontram exatamente na linha em que
   um é clipado (aqui, por overflow:clip de .process) e o outro nasce. A
   correção é fazer os dois sumirem ANTES do corte, não tentar acertar a
   cor de um no valor do outro. Medido: o clip acontece por volta de 73%
   da altura PRÓPRIA deste blob (base cortada); a máscara zera um pouco
   antes disso. */
.process__aurora {
  inset: auto -12% -16% -12%;
  height: clamp(16rem, 38vh, 26rem);
  opacity: .12;
  filter: blur(100px);
  -webkit-mask-image: linear-gradient(180deg, #000 0%, #000 55%, transparent 85%);
          mask-image: linear-gradient(180deg, #000 0%, #000 55%, transparent 85%);
}

/* --- O trecho do fio que atravessa o palco ---------------------------------
   Continuação exata de .process__seam: o fio da introdução termina na borda
   superior de .process__experience, que é a borda superior do palco, e este
   trecho nasce em y = 0 e desce até encostar no topo do node de 12 horas.
   As duas metades leem como uma linha só porque, do ponto de vista do layout,
   são — a mesma costura geométrica de .trust__descent → .about__spine.

   A altura é calculada, não medida: metade do palco menos o raio do anel
   (99/200 do diâmetro, ver o viewBox do SVG) menos metade do node.
   -------------------------------------------------------------------------- */
.process__approach {
  position: absolute;
  top: 0;
  left: 50%;
  z-index: 1;
  width: 1px;
  height: calc(50% - var(--pr-size) * .495 - var(--pr-node) * .5 - .5rem);
  translate: -50% 0;
  transform-origin: 50% 0%;
  pointer-events: none;
  display: none;
}
.process.is-orbital .process__approach { display: block; }
/* BUG MEDIDO — o fio "apagava" bem antes de tocar o node 01.
   O comentário acima já dizia a intenção certa desde a primeira versão desta
   dobra ("desce até ENCOSTAR no topo do node de 12 horas") mas a máscara
   herdada do componente contradizia isso: dissipava nos últimos 38% da
   própria altura, então o traço nunca chegava sólido ao node — só um resíduo
   quase invisível. Não era perceptível como bug sozinho, mas passou a ser
   assim que .process__seam e .services__cta-drop (mais acima na mesma linha)
   deixaram de dissipar — com as duas primeiras pontas sólidas, esta era a
   ÚNICA que ainda apagava, e por isso passou a se destacar. Mesma correção:
   sólido do começo ao fim, já que a costura com o anel é geométrica (a altura
   deste elemento já é calculada para terminar exatamente no topo do node),
   não precisa de dissipação para funcionar. */
.process__approach > .spectrum-rule--v {
  -webkit-mask-image: none;
          mask-image: none;
}

/* -----------------------------------------------------------------------------
   A órbita
   -----------------------------------------------------------------------------
   No estado linear este elemento é um invólucro comum e a lista corre dentro
   dele. No orbital ele vira um quadrado de --pr-size que define o sistema de
   coordenadas de tudo: o anel, o painel e os seis nodes.
   -------------------------------------------------------------------------- */
.process__orbit {
  position: relative;
  z-index: 1;
}

.process.is-orbital .process__orbit {
  width: var(--pr-size);
  height: var(--pr-size);
  margin-inline: auto;
}

/* --- O anel ------------------------------------------------------------------
   SVG e não border-radius: só o SVG permite desenhar um ARCO de comprimento
   variável sobre a circunferência (stroke-dasharray) sem depender de
   mask-composite, que ainda tem comportamento desigual entre navegadores.

   `vector-effect: non-scaling-stroke` mantém o traço do ANEL DE BASE em 1px
   real em qualquer diâmetro — a régua do DS tem 1px, e ela não engorda porque
   o círculo cresceu. O ARCO não usa o efeito (ver a nota sobre `pathLength` no
   markup, em index.html): a espessura dele é escrita por process.js, já
   dividida pela escala do viewBox, com o mesmo resultado na tela.
   -------------------------------------------------------------------------- */
.process__ring {
  position: absolute;
  inset: 0;
  z-index: 0;
  display: none;
  width: 100%;
  height: 100%;
  overflow: visible;
  pointer-events: none;
}
.process.is-orbital .process__ring { display: block; }

/* O estado base da circunferência: discreto, presente o tempo todo. É ele que
   comunica que existe um caminho inteiro, mesmo antes de ser percorrido. */
.process__ring-base {
  stroke: rgba(255, 255, 255, .085);
  stroke-width: 1;
}

/* O caminho percorrido. Espectro de verdade — as sete paradas do
   --ds-spectrum —, aplicado como gradiente ESPACIAL: pontos vizinhos da
   circunferência recebem cores vizinhas, então quando o arco fecha os 360°
   na etapa 06 as duas pontas se encontram na mesma cor. Não existe emenda. */
.process__ring-arc {
  /* Valor de arranque apenas: process.js sobrescreve com a espessura corrigida
     pela escala do viewBox assim que mede a órbita. */
  stroke-width: 2;
  stroke-linecap: round;
}

/* -----------------------------------------------------------------------------
   O núcleo — painel + lista
   -----------------------------------------------------------------------------
   .process__core é a caixa que, no modo orbital, ocupa o centro da órbita. O
   painel é uma camada de superfície em `inset: 0` atrás dela, e a altura dos
   dois é ditada pela etapa mais alta da lista — que é o motivo de as seis
   etapas ficarem empilhadas na MESMA célula de grade em vez de posicionadas
   com `position: absolute`: assim a caixa tem altura real e nada precisa ser
   medido em JS.
   -------------------------------------------------------------------------- */
.process__core { position: relative; }

.process.is-orbital .process__core {
  position: absolute;
  top: 50%;
  left: 50%;
  z-index: 1;
  width: var(--pr-panel-w);
  translate: -50% -50%;
}

/* --- A superfície do painel -------------------------------------------------
   Mesma receita opaca que services.css documentou e pelo mesmo motivo: o
   .card do DS descansa em rgba(255,255,255,.025), pensado para grades em que
   nada se sobrepõe. Aqui as seis etapas ocupam a mesma célula e se cruzam em
   opacidade — com superfície translúcida, a etapa que sai atravessaria a que
   entra. A composição é a do .glass-panel do DS (gradiente branco a 165°)
   sobre --ds-surface, o token que o próprio DS documenta como "neutro ·
   cartões". Nenhuma cor nova.

   Elemento próprio em vez da classe .card: .card carrega `overflow: hidden`
   (que recortaria o fio pontilhado que sobe do painel para o node) e um
   `transform` de :hover que arrastaria a órbita inteira junto.
   -------------------------------------------------------------------------- */
.process__panel {
  display: none;
  position: absolute;
  inset: 0;
  z-index: 0;
  border-radius: var(--ds-r-lg);
  border: 1px solid rgba(255, 255, 255, .08);
  border-top-color: rgba(255, 255, 255, .16);
  border-left-color: rgba(255, 255, 255, .12);
  background-color: var(--ds-surface);
  background-image: linear-gradient(165deg,
      rgba(255, 255, 255, .075) 0%,
      rgba(255, 255, 255, .03) 45%,
      rgba(255, 255, 255, .012) 100%);
  /* A informação é a protagonista (item 18): o halo existe para separar o
     painel do fundo, não para brilhar. Índigo do DS, bem abaixo do nível de
     glow que o .btn-neon usa. */
  box-shadow:
    0 34px 80px -46px rgba(0, 0, 0, .95),
    0 0 60px -34px rgba(99, 102, 241, .75);
  pointer-events: none;
}
.process.is-orbital .process__panel { display: block; }

/* O fio pontilhado que liga o painel ao node ativo. Existe só no orbital, é
   sempre vertical (o node ativo mora sempre em 12 horas) e é curto de
   propósito: ele indica a relação, não desenha um raio. */
.process__link {
  display: none;
  position: absolute;
  left: 50%;
  bottom: calc(100% + .625rem);
  width: 1px;
  height: clamp(1.25rem, 3vh, 2.25rem);
  translate: -50% 0;
  /* --pr-focus vem de process.js: 1 com uma etapa pousada em 12 horas, 0 no
     meio de uma travessia. A opacidade do elemento pertence à timeline de
     entrada, então a modulação vive no alfa da própria cor. */
  background: linear-gradient(180deg,
      rgba(255, 255, 255, 0),
      rgba(255, 255, 255, calc(.34 * var(--pr-focus, 1))));
  -webkit-mask-image: repeating-linear-gradient(180deg, #000 0 3px, transparent 3px 7px);
          mask-image: repeating-linear-gradient(180deg, #000 0 3px, transparent 3px 7px);
  pointer-events: none;
}
.process.is-orbital .process__link { display: block; }

/* -----------------------------------------------------------------------------
   A lista das seis etapas
   -----------------------------------------------------------------------------
   <ol> de verdade, com <h3> e <p> reais, na ordem de leitura correta. A órbita
   é APRESENTAÇÃO: em nenhum estado uma etapa sai da árvore de acessibilidade.
   É por isso que as etapas inativas do modo orbital recuam por `opacity` e
   nunca por `visibility` — opacidade zero não remove nada do leitor de tela.
   -------------------------------------------------------------------------- */
.process__steps {
  position: relative;
  margin: 0;
  padding: 0;
  list-style: none;
}

/* --- LINEAR — a timeline vertical espectral ------------------------------- */
.process__steps {
  display: grid;
  gap: clamp(1.75rem, 4vh, 2.75rem);
  padding-left: 0;
}

.process__step {
  position: relative;
  display: grid;
  grid-template-columns: var(--pr-node) minmax(0, 1fr);
  align-items: start;
  column-gap: clamp(1rem, 3vw, 1.75rem);
}

/* O trilho: a mesma .spectrum-rule--v que desce da Barra de Confiança para o
   Sobre, aqui atravessando os seis nodes. */
.process__rail {
  position: absolute;
  top: 0;
  bottom: 0;
  left: calc(var(--pr-node) / 2);
  width: 1px;
  translate: -50% 0;
  transform-origin: 50% 0%;
  pointer-events: none;
}
.process.is-orbital .process__rail { display: none; }

/* BUG MEDIDO — a costura se partia no modo linear.
   A máscara padrão de .spectrum-rule--v dissipa as DUAS pontas em 18% da
   altura do elemento. Num trilho de 915px (medido em 390 × 844) isso são 165px
   de rampa no topo: o fio da introdução terminava sólido na borda superior do
   palco e o trilho só ficava visível 165px abaixo, com o vão caindo bem em
   cima do primeiro node. A linha lia como duas, e a continuidade que a dobra
   inteira constrói se perdia justo no primeiro passo.

   O trilho é a CONTINUAÇÃO do fio, não um elemento novo: começa sólido, exato
   no ponto em que o outro entrega. A dissipação fica só na ponta de baixo, que
   é onde a jornada de fato termina. */
.process__rail > .spectrum-rule--v {
  -webkit-mask-image: linear-gradient(180deg, #000 0%, #000 88%, transparent 100%);
          mask-image: linear-gradient(180deg, #000 0%, #000 88%, transparent 100%);
}

/* --- ORBITAL — as seis etapas na mesma célula ----------------------------- */
.process.is-orbital .process__steps {
  display: grid;
  gap: 0;
  grid-template-columns: minmax(0, 1fr);
  /* Estático de propósito: o bloco de posicionamento dos nodes precisa ser
     .process__core (cujo centro é o centro da órbita), não a lista. */
  position: static;
}
.process.is-orbital .process__step {
  grid-area: 1 / 1;
  display: block;
  /* Sem transição: quem escreve opacidade e deslocamento é o GSAP, quadro a
     quadro, a partir do progresso do scroll. Uma transição aqui somaria um
     segundo atraso ao scrub. */
  transition: none;
}

/* -----------------------------------------------------------------------------
   O node
   -----------------------------------------------------------------------------
   Círculo com o ícone dentro e o índice num chip sobre a borda superior.
   Deliberadamente SEM rótulo de texto ao redor da órbita: seis títulos
   girando em volta de um painel de texto seriam a única fonte real de colisão
   de labels (item 26) e deixariam a composição carregada. O título da etapa
   ativa já está no painel central, em corpo de leitura; os inativos se
   identificam por índice e ícone, que é o que o item 13 chama de suficiente.

   `--pr-on` (0 → 1) é a única variável de estado do node, escrita por
   process.js. Ela nasce em 1 e não em 0: enquanto este arquivo roda sozinho —
   sem JS, sem GSAP, sem rede — os seis nodes existem acesos, como um caminho
   já descrito por inteiro. Só quem confirma que vai animar tem o direito de
   apagar alguma coisa.
   -------------------------------------------------------------------------- */
.process__node {
  --pr-on: 1;
  position: relative;
  z-index: 3;
  display: grid;
  place-items: center;
  width: var(--pr-node);
  height: var(--pr-node);
  border-radius: 50%;
  border: 1px solid rgba(255, 255, 255, calc(.10 + .06 * var(--pr-on)));
  background: var(--ds-surface);
  color: rgba(255, 255, 255, calc(.42 + .58 * var(--pr-on)));
  box-shadow: 0 0 34px -8px rgba(99, 102, 241, calc(.9 * var(--pr-on)));
  transition: border-color .45s var(--ds-ease), box-shadow .45s var(--ds-ease);
}

.process.is-orbital .process__node {
  position: absolute;
  top: 50%;
  left: 50%;
  /* A centragem é feita pela propriedade `translate`, INDEPENDENTE de
     `transform` — que fica inteira para o GSAP escrever a posição orbital
     (x/y) e a escala. Dois donos na mesma matriz é justamente o bug que o
     resto do site evita por construção. */
  translate: -50% -50%;
  transition: none;
}

/* A borda espectral do node ativo, na receita que o próprio DS documenta em
   .btn-outline: duas camadas de background, a de baixo recortada em
   border-box (o espectro) e a de cima em padding-box (a superfície opaca).
   Sem mask-composite, sem truque novo — e o interior continua opaco, que é o
   que impede o espectro de vazar para dentro do círculo. */
.process__node::after {
  content: '';
  position: absolute;
  inset: 0;
  z-index: 0;
  border-radius: 50%;
  border: 1px solid transparent;
  background-image:
    linear-gradient(var(--ds-surface), var(--ds-surface)),
    var(--ds-spectrum);
  background-origin: border-box;
  background-clip: padding-box, border-box;
  background-size: 100% 100%, 300% 100%;
  animation: spectrum-slide 11s linear infinite;
  opacity: var(--pr-on);
  pointer-events: none;
}

.process__node > * { position: relative; z-index: 1; }

.process__node-icon {
  width: 46%;
  height: auto;
  opacity: calc(.55 + .45 * var(--pr-on));
}

/* O chip do índice, sentado sobre a borda superior do círculo. Fundo opaco em
   --ds-void: é ele que "corta" o anel por trás, do mesmo jeito que o nó da
   Barra de Confiança corta a régua. */
.process__node-chip {
  position: absolute;
  top: 0;
  left: 50%;
  translate: -50% -50%;
  padding: .1875rem .4375rem .1875rem .5rem;
  border-radius: var(--ds-r-pill);
  border: 1px solid rgba(255, 255, 255, calc(.12 + .2 * var(--pr-on)));
  background: var(--ds-void);
  font-family: 'Space Mono', ui-monospace, monospace;
  font-weight: 700;
  font-size: .625rem;
  line-height: 1;
  letter-spacing: .1em;
  color: rgba(255, 255, 255, calc(.45 + .55 * var(--pr-on)));
}

/* Estado interativo — só existe quando process.js promove o node a controle
   (role="button" + tabindex). No fallback estático ele é um marcador, e um
   marcador não finge ser clicável. */
.process__node[role='button'] { cursor: pointer; }
.process__node[role='button']:hover {
  border-color: rgba(255, 255, 255, .32);
  box-shadow: 0 0 40px -8px rgba(99, 102, 241, .95);
}
.process__node[role='button']:focus-visible {
  outline: 2px solid var(--ds-cyan);
  outline-offset: 4px;
}

/* -----------------------------------------------------------------------------
   O corpo da etapa — a ficha que ocupa o painel central
   -------------------------------------------------------------------------- */
.process__body {
  display: grid;
  align-content: start;
  gap: .625rem;
}
.process.is-orbital .process__body {
  position: relative;
  z-index: 1;
  padding: clamp(1.25rem, 2.4vw, 1.75rem);
  text-wrap: pretty;
}

.process__meta {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: .5rem .625rem;
  margin: 0;
  color: rgba(255, 255, 255, .28);
}
.process__meta-step { color: rgba(255, 255, 255, .62); }

/* Régua espectral curta como detalhe do cabeçalho da ficha — a linha da dobra,
   em escala mínima, exatamente como .services__card-term faz no cartão de
   serviço. */
.process__body-rule {
  width: 2.25rem;
  margin: .0625rem 0 .1875rem;
  border: 0;
  -webkit-mask-image: none;
          mask-image: none;
  opacity: .9;
}

.process__title {
  margin: 0;
  color: #fff;
  text-wrap: balance;
}

.process__text {
  margin: 0;
  max-width: 46ch;
  color: rgba(255, 255, 255, .58);
  text-wrap: pretty;
}

/* No modo linear os seis títulos aparecem TODOS ao mesmo tempo, empilhados —
   a escala .t-h3 do DS (1.75rem), que é a certa para um painel único em
   destaque, viraria seis manchetes competindo entre si. Aqui vale a escala
   .t-h4 do mesmo DS. Nenhum valor inventado: são os do .t-h4. */
.process:not(.is-orbital) .process__title {
  font-weight: 500;
  font-size: 1.25rem;
  line-height: 1.3;
  letter-spacing: -0.015em;
}

/* -----------------------------------------------------------------------------
   Dica de leitura
   -----------------------------------------------------------------------------
   Um palco preso precisa dizer, uma vez, que o scroll é o controle. Some assim
   que a primeira travessia começa (process.js cruza a opacidade com `pos`), e
   não volta. aria-hidden porque é uma instrução sobre a APRESENTAÇÃO visual:
   para quem usa leitor de tela as seis etapas já estão todas no documento.
   -------------------------------------------------------------------------- */
.process__hint {
  display: none;
  position: absolute;
  left: 50%;
  bottom: clamp(1.25rem, 4vh, 2.5rem);
  z-index: 2;
  align-items: center;
  gap: .625rem;
  margin: 0;
  translate: -50% 0;
  color: rgba(255, 255, 255, .26);
  white-space: nowrap;
  pointer-events: none;
}
.process.is-orbital .process__hint { display: flex; }
.process__hint svg { width: .875rem; height: auto; }

/* -----------------------------------------------------------------------------
   Responsivo
   -----------------------------------------------------------------------------
   O corte entre orbital e linear NÃO é feito aqui: quem decide é process.js
   (largura E altura mínimas + movimento permitido), e ele só adiciona
   .is-orbital quando as duas condições valem. Este arquivo trata do que muda
   DENTRO de cada estado.
   -------------------------------------------------------------------------- */

/* Notebook — mesmo conceito, curso um pouco menor. */
@media (max-width: 1179px) {
  .process { --pr-travel: 235vh; }
}

/* Tablet — o raio encolhe, o painel encolhe junto (é percentual do raio) e o
   node cede um degrau. Os seis nodes continuam existindo: o que se simplifica
   é a escala, não a informação. */
@media (max-width: 899px) {
  .process {
    --pr-travel: 215vh;
    /* O teto absoluto sobe de 34rem para 38rem: num tablet retrato alto
       (medido em 834 × 1112) o teto de 34rem prendia a órbita em 544px dentro
       de um palco de 1112, e a composição perdia presença sem ganhar nada —
       58vh já é o limite que importa nas alturas menores deste breakpoint.
       Verificado em 768px de largura, o pior caso: órbita 594 + node 58 = 652,
       ainda com 116px de folga lateral. */
    --pr-size: min(58vh, 38rem, 82vw);
    --pr-node: clamp(2.875rem, 5.6vh, 3.75rem);
  }
  .process__headline { max-width: none; }
  .process.is-orbital .process__body { padding: 1.125rem 1.25rem; }
}

/* Mobile — sempre linear (process.js não monta o orbital abaixo de 768px).
   Aqui só o node encolhe e a lista fecha o passo. */
@media (max-width: 599px) {
  .process {
    --pr-node: 2.5rem;
  }
  .process__steps { gap: 1.5rem; }
  .process__step { column-gap: 1rem; }
}

/* Viewports baixos — a órbita já é limitada por 64vh, mas o chip do node e a
   dica de scroll precisam de folga própria. */
@media (max-height: 720px) and (min-width: 768px) {
  .process { --pr-size: min(60vh, 34rem, 74vw); }
  .process__hint { bottom: .875rem; }
}

/* -----------------------------------------------------------------------------
   Movimento reduzido
   -----------------------------------------------------------------------------
   process.js não monta timeline nenhuma neste modo (gsap.matchMedia trata a
   condição), então .is-orbital não chega a existir e a dobra fica na timeline
   vertical: seis etapas completas, legíveis, sem palco preso e sem rotação.
   As regras abaixo são a rede de segurança, caso a classe sobre por qualquer
   caminho.
   -------------------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  .process.is-orbital .process__experience { min-height: 0; }
  .process.is-orbital .process__stage {
    position: static;
    height: auto;
    display: block;
    overflow: visible;
  }
  .process.is-orbital .process__orbit { width: auto; height: auto; }
  .process.is-orbital .process__ambient,
  .process.is-orbital .process__ring,
  .process.is-orbital .process__approach,
  .process.is-orbital .process__panel,
  .process.is-orbital .process__link,
  .process.is-orbital .process__hint { display: none; }
  .process.is-orbital .process__rail { display: block; }
  .process.is-orbital .process__core {
    position: static;
    width: auto;
    translate: none;
  }
  .process.is-orbital .process__steps {
    position: relative;
    gap: clamp(1.75rem, 4vh, 2.75rem);
  }
  .process.is-orbital .process__step {
    grid-area: auto;
    display: grid;
    grid-template-columns: var(--pr-node) minmax(0, 1fr);
  }
  .process.is-orbital .process__node {
    position: relative;
    top: auto;
    left: auto;
    translate: none;
  }
  .process.is-orbital .process__body { padding: 0; }
  .process.is-orbital .process__seam {
    top: auto;
    bottom: 0;
    height: var(--pr-gap);
    left: calc(max(0px, (100% - var(--app-container-max)) / 2)
               + var(--app-gutter) + var(--pr-node) / 2);
    translate: 0 0;
  }

  .process__node::after,
  .process__body-rule,
  .process__seam > .spectrum-rule,
  .process__approach > .spectrum-rule,
  .process__rail > .spectrum-rule { animation: none; }
}

/* -----------------------------------------------------------------------------
   Sinalização de camada
   -----------------------------------------------------------------------------
   Mesma política de passage.css, about.css e services.css: `will-change` só
   enquanto a timeline está de fato em curso (process.js liga e desliga
   .process-active no <html> pelo onToggle do ScrollTrigger).

   BUG MEDIDO — flicker na costura seam → approach → anel durante a entrada.
   Reportado pelo usuário e parcialmente reproduzido: uma captura mostrou um
   retângulo preto cobrindo boa parte do topo da viewport, desaparecendo no
   quadro seguinte, na mesma posição de scroll — sintoma clássico de troca de
   camada de composição em pleno voo.

   A causa: .process__seam, .process__approach e .process__ring são animados
   via scale()/opacity DURANTE TODO O SCRUB de tlHead (a entrada da dobra) —
   e, diferente de QUALQUER outra sequência de entrada com scrub do site
   (.passage-active .trust__rule, .about-active .about__stage/.about__img),
   nunca recebiam will-change. Sem a promoção antecipada a uma camada GPU
   própria, o navegador pode promover/despromover o elemento NO MEIO da
   animação — exatamente quando um scrub de 60fps está escrevendo transform a
   cada quadro — e essa troca é o que se vê como um flash.

   .process-active já cobre as duas janelas em que isto importa: o próprio
   pin (nodes/body, como já estava) e agora também a entrada (process.js
   liga a mesma classe a partir do onToggle de tlHead, não só do pin
   principal — ver a nota lá). */
.process-active .process__node { will-change: transform; }
.process-active .process__body { will-change: opacity, transform; }
.process-active .process__seam { will-change: transform; }
.process-active .process__approach { will-change: transform; }
.process-active .process__ring { will-change: opacity, transform; }

/* A ponte em Serviços passa pelo mesmo scrub (a mesma timeline, tlHead, é
   quem a anima — ver process.js) e pelo mesmo risco. */
.process-active .services__cta-node { will-change: opacity, transform; }
.process-active .services__cta-drop { will-change: transform; }
