/* ============================================================
   YOCTA TECNOLOGIA LTDA / site institucional
   Código autoral, sem dependências externas.
   Ordem: tokens > reset > tipografia > layout fixo > seções >
          utilitários > motion > acessibilidade > responsivo
   As fontes ficam em fonts.css (self-host, geradas do Google Fonts).
   ============================================================ */

:root{
  --ink:        #191919;
  --ink-deep:   #101010;
  --bone:       #E8E4D6;   /* creme oficial da marca */
  --mute:       #8B887C;
  --line:       rgba(232,228,214,.14);
  --line-soft:  rgba(232,228,214,.07);
  --signal:     #E8E4D6;   /* marca monocromática; um acento futuro entra trocando só esta linha */

  --display: "Archivo", "Helvetica Neue", Arial, sans-serif;
  --body:    "Inter Tight", "Helvetica Neue", Arial, sans-serif;
  --mono:    "IBM Plex Mono", ui-monospace, "SF Mono", monospace;

  --gut: clamp(20px, 4vw, 64px);
  --rail: clamp(0px, 8vw, 132px);

  --ease: cubic-bezier(.22,.61,.36,1);
  --ease-out: cubic-bezier(.16,1,.3,1);
}

/* ---------- reset ---------- */
*,*::before,*::after{ box-sizing:border-box; }
/* Contenção lateral em três camadas: html, body e main. O overflow do body é
   propagado ao viewport (e o body em si fica sem clipar nada), então hidden
   só no body nunca conteve de verdade: o extent lateral existia e o iOS
   deixava o gesto rolar até lá. clip é o valor correto (clipa sem criar
   scroll container); hidden fica como fallback para motores antigos. */
html{ scroll-behavior:smooth; -webkit-text-size-adjust:100%; overflow-x:hidden; overflow-x:clip; }
body{
  margin:0;
  background:var(--ink);
  color:var(--bone);
  font-family:var(--body);
  font-size:clamp(15px,1.05vw,17px);
  line-height:1.55;
  font-weight:400;
  overflow-x:hidden; overflow-x:clip;
  -webkit-font-smoothing:antialiased;
}
/* Trava de rolagem enquanto a cortina está na tela. touch-action cobre o
   iOS, que ignora overflow:hidden de body para gesto de toque; pinch-zoom,
   não none, para não roubar o zoom de acessibilidade nem por 2s. O :has
   espelha a trava no html, único lugar onde overscroll-behavior vale para
   o viewport (do body ele não propaga). */
body.is-locked{ overflow:hidden; height:100vh; height:100dvh; touch-action:pinch-zoom; overscroll-behavior:none; }
html:has(body.is-locked){ overflow:hidden; overscroll-behavior:none; }
img,svg{ display:block; max-width:100%; }
a{ color:inherit; text-decoration:none; }
::selection{ background:var(--signal); color:var(--ink); }
:focus-visible{ outline:2px solid var(--signal); outline-offset:4px; }

/* ---------- tipografia ---------- */
.eyebrow{
  font-family:var(--mono);
  font-size:11px;
  letter-spacing:.16em;
  text-transform:uppercase;
  color:var(--mute);
  font-weight:400;
}
.h-display{
  font-family:var(--display);
  font-weight:600;
  font-size:clamp(2.6rem,8.4vw,7.6rem);
  line-height:.94;
  letter-spacing:-.035em;
  margin:0;
}
.h-section{
  font-family:var(--display);
  font-weight:600;
  font-size:clamp(1.9rem,4.4vw,3.6rem);
  line-height:1.02;
  letter-spacing:-.028em;
  margin:0;
}
.h-item{
  font-family:var(--display);
  font-weight:600;
  font-size:clamp(1.35rem,2.3vw,2rem);
  line-height:1.1;
  letter-spacing:-.02em;
  margin:0;
}
.lede{
  font-size:clamp(1.05rem,1.5vw,1.35rem);
  line-height:1.5;
  color:var(--bone);
  max-width:36ch;
  margin:0;
}
p{ margin:0 0 1.1em; color:var(--mute); max-width:58ch; }
p:last-child{ margin-bottom:0; }
p strong{ color:var(--bone); font-weight:500; }
.mono{ font-family:var(--mono); font-size:12px; letter-spacing:.06em; }
sup{
  font-size:.6em;
  line-height:0;
  position:relative;
  top:-.5em;
  letter-spacing:0;
}

/* ---------- preloader ---------- */
#loader{
  position:fixed; inset:0; z-index:200;
  background:var(--ink-deep);
  display:flex; align-items:flex-end; justify-content:flex-end;
  padding:var(--gut);
  transition:transform 1s var(--ease-out);
  overflow:clip;   /* o átomo gira além do próprio viewBox; nada disso vira extent */
}
#loader.done{ transform:translateY(-101%); }
#loader .l-count{ font-family:var(--mono); font-size:clamp(1.6rem,5vw,3rem); color:var(--signal); font-variant-numeric:tabular-nums; }
#loader .l-bar{
  position:absolute; left:0; bottom:0; height:1px; width:100%;
  background:var(--signal); transform:scaleX(0); transform-origin:left;
}

/* coreografia de montagem da marca no preloader */
/* overflow visível porque o viewBox dos SVGs da marca é o recorte justo dela
   (alpha bbox), e um átomo que gira precisa de mais espaço que a própria
   bbox parada: o raio máximo a partir do centro é 167,4 unidades, contra as
   157 que a caixa oferece na horizontal. Sem isso a ponta do braço é cortada
   ao passar pela lateral. Vale para .hero-mark também. Não é para trocar por
   um viewBox maior sem recalcular width e right junto, senão a marca encolhe
   e sai de posição. */
.l-logo{
  position:absolute; left:50%; top:50%;
  transform:translate(-50%,-50%);
  width:min(76vw,640px); height:auto;
  overflow:visible;
}
/* NENHUMA peça desta coreografia usa transform-box:fill-box, e isso é
   deliberado. fill-box em <g> de SVG é instável no Safari; quando falha, a
   caixa de referência vira o viewBox inteiro e duas coisas quebram de uma vez:
   a origem do giro cai no canto e o átomo sai da tela, e o translateY(130%)
   das letras passa a valer 130% da altura do viewBox, jogando-as para longe
   e nunca trazendo de volta. Em celular o resultado é a marca à esquerda e o
   resto invisível.

   A cura é não depender da propriedade: view-box com coordenada explícita
   para o que precisa de origem, e unidade absoluta para o que é deslocamento.
   Atenção à convenção, que é contraintuitiva e eu verifiquei renderizando:
   com view-box, transform-origin é resolvido contra o ZERO do sistema de
   coordenadas, não contra o canto do viewBox. Ou seja, a coordenada aqui é
   absoluta (315.1 750), não relativa ao min-x/min-y. Errar isso não gera aviso
   nenhum: a peça simplesmente gira em torno de um ponto distante e some.

   Todas as coordenadas abaixo foram MEDIDAS do arquivo, não estimadas; se a
   marca mudar, medir de novo (centro do átomo, topo da haste, altura do clip).

   Tempo: o ciclo completo é 5000ms, definido em site.js. Esta coreografia
   fecha em 2000ms, deixando a marca montada e parada por ~1,8s antes de a
   cortina começar a subir aos 3980ms. Mexer no ritmo aqui exige conferir o
   HOLD lá, que precisa continuar maior que estes 2000ms. */
.l-logo .lg-atom,.l-logo .lg-rule{ transform-box:view-box; }

.l-logo .lg-atom{
  transform-origin:315.1px 750px;          /* centro do átomo, coord. absoluta */
  animation:lg-spin 2s var(--ease-out) forwards,
            lg-fade .6s linear forwards;
}
@keyframes lg-spin{ from{ transform:rotate(-900deg); } to{ transform:rotate(0deg); } }
@keyframes lg-fade{ from{ opacity:0; } to{ opacity:1; } }

.l-logo .lg-rule{
  transform-origin:505.4px 589px;          /* topo da haste, coord. absoluta */
  transform:scaleY(0);
  animation:lg-rule .8s var(--ease-out) .65s forwards;
}
@keyframes lg-rule{ to{ transform:scaleY(1); } }

/* 130px são 130 unidades do viewBox, e o clip lgw tem 126 de altura: a letra
   fica escondida abaixo dele. Absoluto de propósito, no lugar de 130%. */
.l-logo .lg-letter{ transform:translateY(130px); animation:lg-up .9s var(--ease-out) forwards; }
.l-logo .lg-word .lg-letter:nth-child(1){ animation-delay:.78s; }
.l-logo .lg-word .lg-letter:nth-child(2){ animation-delay:.85s; }
.l-logo .lg-word .lg-letter:nth-child(3){ animation-delay:.92s; }
.l-logo .lg-word .lg-letter:nth-child(4){ animation-delay:.99s; }
.l-logo .lg-word .lg-letter:nth-child(5){ animation-delay:1.06s; }
@keyframes lg-up{ to{ transform:translateY(0); } }

.l-logo .lg-sub{ opacity:0; transform:translateY(12px); animation:lg-sub .7s var(--ease-out) 1.3s forwards; }
@keyframes lg-sub{ to{ opacity:1; transform:translateY(0); } }

/* ---------- cursor ---------- */
#cursor{
  position:fixed; top:0; left:0; z-index:150;
  width:9px; height:9px; border-radius:50%;
  background:var(--signal);
  pointer-events:none;
  mix-blend-mode:difference;
  transform:translate3d(-50px,-50px,0) translate(-50%,-50%);
  transition:width .28s var(--ease), height .28s var(--ease), opacity .3s linear;
  will-change:transform;
}
#cursor.grow{ width:52px; height:52px; opacity:.85; }
@media (hover:none),(pointer:coarse){ #cursor{ display:none; } }

/* ---------- header ---------- */
header{
  position:fixed; top:0; left:0; right:0; z-index:100;
  display:flex; align-items:center; justify-content:space-between;
  padding:calc(var(--gut) * .62) var(--gut);
  mix-blend-mode:difference;
}
.brand{ display:flex; align-items:center; gap:12px; }
.brand svg{ width:26px; height:26px; }
.brand .wm{
  font-family:var(--display); font-weight:700;
  font-size:15px; letter-spacing:.22em; text-transform:uppercase;
}
.head-meta{ display:flex; gap:28px; align-items:center; }
.head-meta a:hover{ color:var(--signal); }

/* ---------- rail lateral com índice ---------- */
#rail{
  position:fixed; left:0; top:0; bottom:0; width:var(--rail);
  z-index:90;
  display:flex; flex-direction:column; justify-content:center; gap:18px;
  padding-left:var(--gut);
  pointer-events:none;
}
#rail a{
  pointer-events:auto;
  font-family:var(--mono); font-size:11px; letter-spacing:.14em;
  color:var(--mute);
  display:flex; align-items:center; gap:10px;
  transition:color .35s var(--ease);
}
#rail a::before{
  content:""; width:12px; height:1px; background:currentColor;
  transition:width .4s var(--ease);
}
#rail a:hover{ color:var(--bone); }
#rail a.on{ color:var(--signal); }
#rail a.on::before{ width:30px; }
#rail .r-label{ opacity:0; transform:translateX(-4px); transition:opacity .35s var(--ease), transform .35s var(--ease); }
#rail a.on .r-label,#rail a:hover .r-label{ opacity:1; transform:none; }

/* ---------- assinatura: a camada ----------
   hairline fixa no rodapé com um tick que percorre conforme o scroll */
#layer{
  position:fixed; left:0; right:0; bottom:0; height:1px; z-index:95;
  background:var(--line);
}
#layer .tick{
  position:absolute; bottom:0; left:0;
  width:44px; height:3px; background:var(--signal);
  transform:translateX(0);
  will-change:transform;
}
#layer .pct{
  position:absolute; right:var(--gut); bottom:14px;
  font-family:var(--mono); font-size:10px; letter-spacing:.14em; color:var(--mute);
  font-variant-numeric:tabular-nums;
}

/* ---------- estrutura de seção ---------- */
/* O clip lateral REAL mora aqui: é o main que contém a sangria deliberada
   do hero-mark (right negativo) na borda da tela. Sem ele o extent lateral
   do documento cresce e vira rolagem em WebKit. Só no eixo x: o y segue
   livre para o fluxo normal. */
main{ position:relative; z-index:10; overflow-x:clip; }
section{
  padding:clamp(88px,13vh,168px) var(--gut) clamp(88px,13vh,168px) calc(var(--rail) + var(--gut));
  border-top:1px solid var(--line-soft);
  position:relative;
}
section:first-child{ border-top:0; }
.sec-head{
  display:flex; align-items:baseline; gap:18px;
  margin-bottom:clamp(36px,6vh,72px);
}
.sec-num{ font-family:var(--mono); font-size:11px; color:var(--signal); letter-spacing:.14em; }
/* minmax(0,1fr), nunca 1fr puro: 1fr é minmax(auto,1fr), e o piso auto herda
   o min-content do conteúdo. Foi assim que a marquee de clientes (spans
   nowrap, duplicados pelo JS) alargou a coluna única do mobile para ~1846px
   e a seção 02 inteira estourou a tela. No desktop o bug não aparecia
   porque item que atravessa 12 trilhas flexíveis não impõe min-content; em
   coluna única, impõe. O min-width:0 nos itens fecha o mesmo buraco no nível
   deles (flex e grid aninhados). */
.grid{ display:grid; grid-template-columns:repeat(12,minmax(0,1fr)); gap:clamp(20px,2.6vw,44px); }
.grid > *{ min-width:0; }

/* ---------- utilitários de grid e espaçamento ---------- */
.col-4{ grid-column:span 4; }
.col-7{ grid-column:span 7; }
.col-8{ grid-column:span 8; }
.col-start-9{ grid-column-start:9; }
.col-full{ grid-column:1 / -1; }
.mt-lg{ margin-top:clamp(30px,4vh,54px); }
.mb-lg{ margin-bottom:clamp(26px,4vh,46px); }
.mb-sm{ margin-bottom:14px; }
.txt-bone{ color:var(--bone); font-size:1.05rem; }

/* ---------- hero ---------- */
#hero{
  min-height:100svh;
  display:flex; flex-direction:column; justify-content:flex-end;
  padding-bottom:clamp(56px,9vh,104px);
  border-top:0;
}
.hero-mark{
  position:absolute; right:clamp(-40px,-2vw,10px); top:50%;
  width:min(44vw,540px); height:auto;
  opacity:0; pointer-events:none;
  overflow:visible;   /* o átomo gira para fora da bbox; ver nota em .l-logo */
  transform:translateY(-50%);
  animation:mark-in 1.2s var(--ease-out) .9s forwards,
            breathe 9s ease-in-out 2.4s infinite alternate;
}
@keyframes mark-in{ to{ opacity:.5; } }
@keyframes breathe{
  from{ transform:translateY(-50%) scale(1); }
  to{   transform:translateY(-50%) scale(1.015); }
}
/* O átomo gira devagar, como peça única. A rotação fica no grupo interno e
   não no <svg>: o elemento externo já carrega o translateY(-50%) e o scale
   do breathe, e um transform novo ali sobrescreveria os dois. É CSS e não
   JS de propósito, para girar também no toque, onde não existe ponteiro
   fino; o bloco de prefers-reduced-motion no fim do arquivo congela isso.
   Uma volta a cada 60s; para acelerar ou frear, mexer só nesse número. */
.hero-mark .hero-atom{
  transform-box:view-box;   /* não fill-box: quebra no Safari, ver nota em .l-logo */
  transform-origin:751.6px 750px;  /* centro do átomo, coord. absoluta do viewBox */
  will-change:transform;
  animation:hero-spin 60s linear infinite;
}
@keyframes hero-spin{ to{ transform:rotate(360deg); } }

.hero-copy{ position:relative; z-index:2; }
.hero-sub{
  display:flex; flex-wrap:wrap; gap:clamp(20px,4vw,72px);
  margin-top:clamp(26px,4vh,44px);
  align-items:flex-start;
}
.hero-scale{
  font-family:var(--mono); font-size:12px; color:var(--mute);
  letter-spacing:.1em;
}
.hero-scale b{ color:var(--bone); font-weight:500; }
.scroll-cue{
  margin-top:clamp(34px,6vh,64px);
  display:inline-flex; align-items:center; gap:12px;
}
.scroll-cue .bar{
  width:56px; height:1px; background:var(--line); position:relative; overflow:hidden;
}
.scroll-cue .bar::after{
  content:""; position:absolute; inset:0; background:var(--signal);
  transform:translateX(-100%); animation:sweep 2.4s var(--ease) infinite;
}
@keyframes sweep{ 0%{transform:translateX(-100%)} 55%{transform:translateX(0)} 100%{transform:translateX(100%)} }

/* ---------- 02 operação / métricas ---------- */
.metrics{ grid-column:1 / -1; display:grid; grid-template-columns:repeat(4,minmax(0,1fr)); gap:1px; background:var(--line-soft); border:1px solid var(--line-soft); }
.metric{ background:var(--ink); padding:clamp(20px,2.4vw,34px); }
.metric .v{
  font-family:var(--display); font-weight:600;
  font-size:clamp(1.8rem,3.6vw,3rem); line-height:1;
  letter-spacing:-.03em; display:block; margin-bottom:10px;
  font-variant-numeric:tabular-nums;
}
.metric .k{ font-family:var(--mono); font-size:10.5px; letter-spacing:.13em; text-transform:uppercase; color:var(--mute); }

/* ---------- 02 operação / separação de atribuição ----------
   A seção tem dois grupos que não podem ser lidos como um só. "Trajetória" é
   experiência pessoal do fundador, começada em março de 2016, anterior à
   empresa. "Yocta hoje" é a pessoa jurídica, constituída em 2019. Atribuir os
   dez anos ou os 17.000 usuários à empresa é afirmação falsa sobre o CNPJ que
   está no rodapé, e é por isso que a seção tem esta forma. Não fundir os dois
   grupos num container só, não reordenar de modo que uma métrica de um grupo
   encoste na do outro.

   A separação é feita com PESO IGUAL, não com hierarquia. Rebaixar a
   trajetória com opacidade ou corpo menor resolveria o problema jurídico
   escondendo informação verdadeira, que não é o objetivo: o objetivo é que
   cada número tenha dono explícito. Quem separa é o rótulo mais a régua, que
   cortam a seção de ponta a ponta. */
.op-bloco{ grid-column:1 / -1; margin-top:clamp(34px,5vh,64px); }
.op-head{
  display:flex; align-items:center; gap:16px;
  font-family:var(--mono); font-weight:400; font-size:11px;
  letter-spacing:.16em; text-transform:uppercase; color:var(--signal);
  margin:0 0 clamp(16px,2.2vh,26px);
}
/* A régua é ::after e não elemento: um <span> vazio só para desenhar linha
   é ruído para leitor de tela. O h3 carrega o rótulo e nada mais. */
.op-head::after{ content:""; flex:1; height:1px; background:var(--line); }
.op-bloco .metrics{ margin-top:clamp(18px,2.4vh,28px); }
.op-bloco .clients{ margin-top:clamp(22px,3vh,38px); }
/* Legenda da faixa de instituições. Fica FORA do .marquee de propósito: lá
   dentro ela viraria item flex ao lado de um track de milhares de px, seria
   empurrada para fora e cortada pelo overflow:hidden, e o seletor
   .marquee span a repintaria com a tipografia display dos nomes. */
.op-nota{
  font-family:var(--mono); font-size:10.5px; letter-spacing:.11em;
  color:var(--mute); margin:12px 0 0; max-width:none;
}
.op-fecho{ margin-top:clamp(38px,5.5vh,72px); }
/* Três colunas, não as quatro do padrão. Com a grade de 4 os seis cartões
   caberiam num container só e a fronteira entre os grupos cairia no meio da
   primeira linha, encostando "17.000+ usuários" (pessoa física) em "5.700+
   usuários" (empresa): exatamente a leitura que a seção existe para impedir. */
.metrics--3{ grid-template-columns:repeat(3,minmax(0,1fr)); }
/* A faixa NÃO é uma vitrine de clientes, apesar do nome da classe. Depois da
   refatoração jurídica da seção 02 ela vive dentro do bloco "Trajetória" e
   lista instituições onde o FUNDADOR atuou, das quais só duas têm contrato
   com a empresa hoje. Duas consequências que não devem ser desfeitas sem
   decisão do dono:

   1. A faixa rola em loop (o track leva data-marquee), por decisão do dono,
      com o risco de leitura levantado e aceito: movimento contínuo é
      dispositivo promocional, e quatro nomes institucionais desfilando leem
      como carteira de clientes atual quando só duas têm contrato. Quem
      carrega o esclarecimento é o texto: a legenda em primeira pessoa e no
      passado logo abaixo, e o cartão "2 instituições com contrato" no bloco
      seguinte. Por isso esses dois não são decorativos e não podem ser
      encurtados nem afastados da faixa.
      Se um dia o data-marquee sair do track, a faixa vira lista estática
      sozinha (a regra .clients-nomes .track:not(.is-dup) mais abaixo assume)
      e a barra da última instituição precisa sair junto, senão fica
      pendurada no fim da frase.
   2. NÃO trocar para clients--logos. Exibir quatro brasões restaura a leitura
      de vitrine na forma mais forte possível, e em imagem, que é mais difícil
      de qualificar com texto do que um nome. A autorização das IES cobre
      exibir a marca; não cobre sugerir contrato vigente.

   O interruptor de duas apresentações continua no CSS abaixo, mas hoje só o
   bloco de nomes existe no HTML: o div.clients-logos e os PNGs foram
   removidos do repositório. Reintroduzi-los é decisão do dono, não de quem
   estiver editando. */
.clients{
  grid-column:1 / -1; margin-top:clamp(30px,4vh,56px);
  --logo-h:clamp(28px,3.2vw,42px);
}
.clients-logos{ display:flex; flex-wrap:wrap; align-items:center; gap:12px 22px; }
.clients--logos .clients-nomes{ display:none; }
.clients--nomes .clients-logos{ display:none; }
.chip{
  border:1px solid var(--line); border-radius:100px;
  padding:8px 18px; font-family:var(--mono); font-size:11px; letter-spacing:.1em;
  color:var(--bone);
}

/* Logo de cliente. Padroniza pela ALTURA, nunca pela largura: os logos chegam
   em proporções incompatíveis (lockup 6:1, brasão 1:1) e igualar a largura
   deixaria um gigante e o outro ilegível. A largura sai do arquivo, com teto
   para conter lockups compridos; object-fit garante que nada distorça.

   Recoloração para a paleta. Os logos chegam em PNG colorido e raster não tem
   fill para editar, então a conversão é feita no navegador. A cadeia abaixo
   foi resolvida numericamente, não estimada: brightness(0) achata qualquer cor
   em preto preservando o canal alfa, invert(1) leva a branco, e o resto tinge
   até #E8E5D6, um nível de verde longe do #E8E4D6 da marca. Serve igual para
   SVG, colorido ou já em creme: o resultado é sempre a silhueta em creme.

   O preço é achatar detalhe interno. Vazado feito com transparência real
   sobrevive; vazado pintado de branco por cima vira sólido. Se um logo tiver
   contraforma branca opaca, ele precisa de PNG com alfa de verdade.

   Ajuste óptico, e aqui está o motivo de existir uma escala: altura igual não
   é peso igual. Um lockup empilhado de três faixas (símbolo, nome, descritor)
   reparte a mesma altura entre três linhas e fica ilegível ao lado de um
   wordmark horizontal, que usa a altura inteira numa palavra só. Os fatores
   abaixo igualam o tamanho do TEXTO PRINCIPAL, não da caixa. Aplicar no
   elemento; nunca style inline, que a CSP bloqueia e o build reprova. */
.client-logo{
  height:calc(var(--logo-h) * var(--k,1)); width:auto; max-width:230px;
  object-fit:contain;
  filter:brightness(0) invert(1) sepia(.8) saturate(1.9) hue-rotate(-30deg) brightness(.91);
  opacity:.72;
  transition:opacity .35s var(--ease);
}
.client-logo.k-85 { --k:.85;  }   /* wordmark horizontal, 1 faixa */
.client-logo.k-115{ --k:1.15; }   /* lockup de 2 faixas */
.client-logo.k-130{ --k:1.3;  }   /* lockup de 3 faixas */
.client-logo:hover{ opacity:1; }

/* ---------- 03 soluções ---------- */
.sol{
  grid-column:1 / -1;
  display:grid; grid-template-columns:minmax(0,1fr) minmax(0,2.2fr); gap:clamp(20px,3vw,56px);
  padding:clamp(28px,4vh,52px) 0;
  border-top:1px solid var(--line-soft);
  position:relative;
}
.sol:first-of-type{ border-top:0; }
.sol .sol-tag{ font-family:var(--mono); font-size:11px; letter-spacing:.16em; color:var(--signal); text-transform:uppercase; }
.sol .sol-body{ display:grid; gap:16px; }
.sol ul{ list-style:none; padding:0; margin:6px 0 0; display:flex; flex-wrap:wrap; gap:8px 10px; }
.sol li{
  font-family:var(--mono); font-size:11px; letter-spacing:.06em;
  color:var(--mute); border:1px solid var(--line-soft); padding:6px 12px;
}

/* ---------- marquee ---------- */
.marquee{
  border-top:1px solid var(--line-soft); border-bottom:1px solid var(--line-soft);
  overflow:hidden; padding:22px 0; display:flex; user-select:none;
}
.marquee .track{ display:flex; gap:0; flex:0 0 auto; }
/* A animação só liga com o conteúdo duplicado (o loop desliza exatamente
   50%). O JS marca o track com is-dup ao duplicar; sem JS não há cópia, e
   um track único animado saltaria no meio do ciclo mostrando vazio. */
.marquee .track.is-dup{ animation:slide 42s linear infinite; }
/* Sem duplicação (sem JS), a faixa de clientes vira lista em linhas:
   conteúdo real não fica escondido atrás de um recorte. A marquee de
   serviços, decorativa e aria-hidden, segue cortada na borda. */
.clients-nomes .track:not(.is-dup){ flex-wrap:wrap; max-width:100%; row-gap:10px; }
.clients-nomes .track:not(.is-dup) span{ white-space:normal; }
/* A cópia que o JS acrescenta para fechar o loop. display:contents faz os
   spans de dentro virarem itens diretos do track, preservando o layout; o
   aria-hidden no elemento evita o leitor de tela anunciar a lista duas vezes. */
.marquee .track-copia{ display:contents; }
.marquee span{
  font-family:var(--display); font-weight:500;
  font-size:clamp(1rem,1.8vw,1.5rem); letter-spacing:-.01em;
  padding:0 26px; white-space:nowrap; color:var(--mute);
}
.marquee span i{ color:var(--signal); font-style:normal; }
@keyframes slide{ to{ transform:translateX(-50%); } }

/* ---------- 04 método ---------- */
.steps{ grid-column:1 / -1; display:grid; grid-template-columns:repeat(4,minmax(0,1fr)); gap:clamp(18px,2.4vw,40px); }
.step{ border-top:1px solid var(--line); padding-top:18px; }
.step .n{ font-family:var(--mono); font-size:11px; color:var(--signal); letter-spacing:.14em; display:block; margin-bottom:14px; }
.step h4{ font-family:var(--display); font-weight:600; font-size:1.05rem; letter-spacing:-.01em; margin:0 0 8px; }
.step p{ font-size:.92rem; }

/* ---------- 05 contato ---------- */
#contato{ padding-bottom:clamp(120px,16vh,200px); }
.cta{
  display:inline-block;
  font-family:var(--display); font-weight:600;
  /* piso 1.35rem, não 1.7: o e-mail tem 20 caracteres inquebráveis e no
     piso antigo media ~285px contra 280px úteis a 320px, estourando ainda
     mais no fallback de fonte antes do Archivo carregar */
  font-size:clamp(1.35rem,5.6vw,4.4rem);
  letter-spacing:-.035em; line-height:1;
  padding:6px 0;
  background-image:linear-gradient(var(--signal),var(--signal));
  background-size:0% 2px; background-repeat:no-repeat; background-position:0 100%;
  transition:background-size .5s var(--ease-out), color .35s var(--ease);
}
.cta:hover{ background-size:100% 2px; color:var(--signal); }
.contact-cols{ grid-column:1 / -1; display:grid; grid-template-columns:repeat(3,minmax(0,1fr)); gap:clamp(20px,3vw,48px); margin-top:clamp(46px,7vh,84px); }
.contact-cols div span{ display:block; }
.contact-cols .k{ font-family:var(--mono); font-size:10.5px; letter-spacing:.14em; text-transform:uppercase; color:var(--mute); margin-bottom:10px; }
footer{
  padding:26px var(--gut) 34px calc(var(--rail) + var(--gut));
  display:flex; justify-content:space-between; flex-wrap:wrap; gap:14px;
  border-top:1px solid var(--line-soft);
}

/* ---------- página de erro (404.html) ---------- */
/* A 404 reaproveita este CSS inteiro; só o que é exclusivo dela mora aqui.
   Ela NÃO carrega o site.js de propósito: uma página de erro tem que abrir
   mesmo com o JS quebrado. A consequência é um contrato: nada nela pode
   depender da classe .in, ou seja, nada de reveal-type, rise, line-grow ou
   data-split. Todo o conteúdo já nasce visível. */

/* justify-content:center em vez do flex-end do #hero. O #hero cola o texto
   embaixo porque abaixo dele existe o resto do site; na 404 não existe, e o
   flex-end deixava 85% da tela vazia com a frase rente ao rodapé. O
   padding-bottom volta ao valor de section para o centro ser real. */
.err-hero{ justify-content:center; padding-bottom:clamp(88px,13vh,168px); }

/* Mesma peça do hero e mesmo arquivo (logo-mark.svg), aqui como <img>.
   A rotação fica no <img> interno e não no wrapper pela mesma razão do
   .hero-mark: o wrapper carrega o translateY(-50%) da centralização, e um
   transform novo ali o sobrescreveria. Sob prefers-reduced-motion a
   animação é zerada e o transform do <img> volta a none, mas o wrapper
   mantém a posição: a marca não salta.
   Não há transform-box aqui: o alvo é um <img>, não um nó dentro do SVG.
   O centro do átomo coincide com o centro do viewBox de logo-mark.svg
   (751.6 750 em 282.3 241.3 938.6 1017.4), então o 50% 50% padrão já é a
   origem certa e a peça gira inteira, sem se despedaçar. */
.err-mark{
  position:absolute; right:clamp(-40px,-2vw,10px); top:50%;
  width:min(44vw,540px);
  transform:translateY(-50%);
  opacity:.5; pointer-events:none;
}
.err-mark img{ width:100%; height:auto; animation:err-spin 60s linear infinite; }
@keyframes err-spin{ to{ transform:rotate(360deg); } }

.err-sub{ max-width:46ch; color:var(--mute); margin-top:clamp(18px,2.6vh,28px); }
/* O link de volta é o único caminho de saída da página, então não pode ficar
   idêntico ao rótulo "ERRO 404" logo acima. Cor de texto cheia e uma régua
   embaixo dão a ele a afordância de link que o .eyebrow não tem. */
.err-back{
  display:inline-block; margin-top:clamp(26px,4vh,44px);
  font-family:var(--mono); font-size:12px; letter-spacing:.14em;
  text-transform:uppercase; color:var(--bone);
  padding-bottom:5px; border-bottom:1px solid var(--line);
  transition:border-color .35s var(--ease);
}
.err-back:hover{ border-bottom-color:var(--bone); }

/* ---------- primitivas de motion ---------- */
.word{ display:inline-block; overflow:hidden; vertical-align:top; }
.word > i{
  display:inline-block; font-style:inherit;
  transform:translateY(110%);
  transition:transform .9s var(--ease-out);
}
.reveal-type.in .word > i{ transform:none; }

.rise{ opacity:0; transform:translateY(26px); transition:opacity .9s var(--ease-out), transform .9s var(--ease-out); }
.rise.in{ opacity:1; transform:none; }

.line-grow{ transform:scaleX(0); transform-origin:left; transition:transform 1.1s var(--ease-out); }
.line-grow.in{ transform:none; }

/* ---------- acessibilidade: menos movimento ---------- */
@media (prefers-reduced-motion:reduce){
  html{ scroll-behavior:auto; }
  *,*::before,*::after{ animation-duration:.001ms !important; animation-iteration-count:1 !important; transition-duration:.001ms !important; }
  .word > i,.rise,.line-grow{ transform:none !important; opacity:1 !important; }
  #loader{ display:none; }
  /* A faixa de clientes é conteúdo real (a marquee de serviços é decorativa
     e aria-hidden). Sem movimento, os nomes precisam estar todos visíveis:
     o track quebra em linhas em vez de ficar truncado na borda. O max-width
     é obrigatório: sem ele o track (flex-basis:auto) segue largo como o
     conteúdo e o wrap nunca acontece. */
  .clients-nomes .track{ flex-wrap:wrap; max-width:100%; row-gap:10px; }
  .clients-nomes .track span{ white-space:normal; }
}

/* ---------- responsivo ---------- */
@media (max-width:1024px){
  .metrics{ grid-template-columns:repeat(2,minmax(0,1fr)); }
  /* Reafirmado DEPOIS da regra genérica, de propósito. Sem esta linha os três
     cartões de cada grupo cairiam em duas colunas, e três itens em duas
     colunas deixam uma célula vazia pintada com --line-soft, que lê como
     cartão quebrado, não como espaço. Em 3 colunas a 1024px cada célula ainda
     tem cerca de 230px, dentro do que os rótulos comportam. Abaixo de 760px a
     regra de coluna única vence esta, por vir depois no arquivo. */
  .metrics--3{ grid-template-columns:repeat(3,minmax(0,1fr)); }
  .steps{ grid-template-columns:repeat(2,minmax(0,1fr)); }
  .contact-cols{ grid-template-columns:repeat(2,minmax(0,1fr)); }
}
@media (max-width:760px){
  #rail{ display:none; }
  section{ padding-left:var(--gut); }
  footer{ padding-left:var(--gut); }
  /* Sem rail, a marca da 404 encolhe e sobe. Em coluna única ela e a
     manchete disputam a mesma faixa vertical: centrada, o átomo cruzava a
     linha "Esta página". O topo em 32% o encaixa acima do bloco de texto. */
  .err-mark{ width:min(58vw,320px); opacity:.34; top:32%; }
  .head-meta .hide-sm{ display:none; }
  /* Em ~320px o e-mail mono com tracking .16em soma ~307px com a marca;
     qualquer fallback de fonte estoura o header. Tracking menor mantém a
     linha única com folga. */
  @media (max-width:360px){ .head-meta{ font-size:10px; letter-spacing:.1em; } }
  .sol{ grid-template-columns:minmax(0,1fr); gap:14px; }
  .hero-mark{ width:88vw; right:-30vw; }
  .contact-cols{ grid-template-columns:minmax(0,1fr); }
  #layer .pct{ display:none; }

  /* Métricas em coluna única. Em duas colunas, os quatro cartões viram um
     bloco 2x2 e os rótulos longos ("Usuários em ambientes sob operação hoje")
     quebram em três linhas dentro de ~170px. Empilhado, cada número fica com
     a largura inteira e o rótulo cabe em uma ou duas linhas. */
  .metrics{ grid-template-columns:minmax(0,1fr); }

  /* Método também colapsa: em duas colunas a célula cai para ~131px em
     320px, abaixo do confortável para os parágrafos dos passos. */
  .steps{ grid-template-columns:minmax(0,1fr); }

  /* colunas do grid colapsam em uma no mobile
     (correção sobre o mockup, que espremia spans de 12 colunas) */
  .grid{ grid-template-columns:minmax(0,1fr); }
  .col-4,.col-7,.col-8,.col-full{ grid-column:auto; }
  .col-start-9{ grid-column-start:auto; }
}
