/* ==========================================================================
   CDEV — Rolagem de construção (Word Scroll)
   Fonte: docs/design-system.md § 5.8 + docs/copy.md § 1.1
   Mecânica (sticky + snap) e cor por item revertidas pra fidelidade 1:1 com
   o CodePen de referência (2026-08-17): varredura de matiz OKLCH por índice
   (--start/--end/--step), caixa 100vh com scroll-snap interno
   (`[data-snap="true"]`) e sem entrada 3D por item (só opacity + brightness,
   igual ao keyframe `brighten` original) — decisão explícita do dono depois
   de comparar lado a lado com o CodePen (as 3 adaptações anteriores tinham
   ficado documentadas como propositais, mas o pedido agora é fidelidade
   visual/mecânica, não mais a variante de marca). Único ponto mantido
   deliberadamente diferente do CodePen: `animation-range` com "cover"
   repetido nos dois lados (ver comentário no bloco @supports abaixo) — é
   fix de bug real do Chromium, não escolha de design.

   Nota: `.sr-only` (usada pelo <span> de acessibilidade do título) vive em
   assets/css/base.css — é utilitária cross-seção (também usada em
   #servicos/#diferenciais/#faq), não específica deste componente.
   ========================================================================== */

/* Tipografia fluida: mesma fórmula clamp() da referência. 2026-08-17: volta
   a ser UM jogo de constantes só (375px -> 1500px) — título e lista ficam
   lado a lado em qualquer largura agora (mesma disposição mobile/desktop,
   ver .cdev-word-scroll-inner abaixo), então não faz mais sentido ter um
   piso de fonte por faixa como antes (era só pro layout empilhado do
   mobile). Título + a frase mais longa (41 caracteres, "o sistema que faz
   seu negócio funcionar.") precisam caber numa linha só em qualquer
   largura. Piso do clamp em 320px (não 375 — bug real: com o piso em 375,
   telas mais estreitas de verdade, tipo Galaxy S8/S9 a 360px, ainda
   cortavam "Construímos" com o fallback de font-size fixo). --font-size-min:9
   medido no navegador em 320px (Playwright, overflow-x temporariamente
   visible pra achar o valor real — o `clip` da .cdev-word-scroll-inner
   escondia o estouro: 11px ainda vazava 16px em 320px). Nunca subir esse
   valor sem remedir. 9px é pequeno de propósito só no piso extremo — cresce
   rápido a partir daí (11px já em ~375px, ver clamp abaixo).

   Teto também tinha bug real: --font-size-max:48 parecia certo, mas
   `100vi` na fórmula do clamp acompanha a LARGURA DA TELA, não a largura do
   `.container` do Bootstrap — que trava em 1320px a partir de 1400px de
   viewport (breakpoint xxl) e NUNCA cresce mais, mesmo em monitor 4K. Com
   48px isso estourava ~56px dos dois lados numa tela 1920px (container
   ainda em 1320px, fonte grande demais pro espaço real). --font-size-max:42
   era o maior valor que cabia nos 1320px (medido; 44 já vazava 5px).

   2026-09-08: subido pra 56 (pedido do dono) + frases da lista (copy.md
   §1.1) encurtadas pra "Construímos + 2 palavras" — string mais longa foi
   de 53 pra ~40 caracteres. Pelo fator medido acima (~0.568px de largura
   renderizada por char por px de font-size, Poppins 500/700 misto,
   incluindo o gap entre título e item), 40 chars a 56px ≈ 1273px — cabe nos
   1320px do container padrão com folga (~47px), mas ainda assim larguei
   `.cdev-hero .container` pra 1400px no breakpoint xxl (base.css) por
   segurança/pedido explícito, já que a medição acima é estimada (fórmula,
   não Playwright) e a diferença de peso entre título (700) e item (500)
   deixa a métrica exata sensível ao conteúdo. Alinhei
   --font-width-max com o breakpoint real (1400, não 1500) — acima disso o
   clamp trava no teto de qualquer forma, então não muda nada crescer o
   monitor, só evita fingir que ainda sobra espaço que não existe. */
.cdev-word-scroll {
  --font-size-min: 9;
  --font-size-max: 56;
  --font-ratio-min: 1.1;
  --font-ratio-max: 1.33;
  --font-width-min: 320;
  --font-width-max: 1400;
  --font-level: 0;
  /* Varredura de matiz OKLCH por item (idêntico ao CodePen): --start/--end
     em graus de hue, --step calculado em CSS puro no <ul> (precisa de
     --count, setado 1x pelo JS). Valores 25/100 replicam a chamada
     WordScroll.init({ start:25, end:100, ... }) do CodePen de referência. */
  --start: 25;
  --end: 100;
  --lightness: 65%;
  --base-chroma: 0.3;
  /* Offset compartilhado pelo título sticky (top) e pelo respiro da lista
     (padding-block do <ul> — ver nota abaixo). Valor "cheio" (50vh) é o da
     referência original. */
  --ws-offset: calc(50vh - 0.5lh);
  width: 100%;
  font-family: var(--font-display);
}

/* Caixa vira scroll container próprio quando o JS liga o snap
   (initWordScroll em main.js, dataset.snap) — mesmo mecanismo do CodePen
   (`.word-scroll[data-snap="true"]`). Título sticky e os `animation-timeline:
   view()` dos <li> passam a medir progresso contra ESTA caixa, não mais
   contra a página (comportamento esperado, é o efeito do CodePen). */
.cdev-word-scroll[data-snap="true"] {
  height: 100vh;
  overflow-y: auto;
  scroll-snap-type: y proximity;
  /* Scroll continua funcionando (mouse/touch/teclado) — só a barra visual
     some, pedido do dono. */
  scrollbar-width: none;
  -ms-overflow-style: none;
}
.cdev-word-scroll[data-snap="true"]::-webkit-scrollbar {
  display: none;
}
.cdev-word-scroll[data-snap="true"] li {
  scroll-snap-align: center;
}
/* Opcional/inerte por padrão (igual ao CodePen — nunca liga
   data-sync-scrollbar via HTML/JS aqui), mantido por fidelidade. */
[data-sync-scrollbar="true"] {
  scrollbar-color: oklch(var(--lightness) var(--chroma, 0) var(--hue)) transparent;
}
/* Spacer divs (antes/depois de .cdev-word-scroll-inner) replicam a
   estrutura do CodePen 1:1. O CodePen de referência não define regras
   próprias pra `.scroll-spacer` (fica com altura 0 de bloco comum) — mesma
   coisa aqui, quem cria o respiro real é padding-block no <ul> abaixo. */

/* Histórico, RC-01/dt-01 (docs/06-qualidade/bugs-conhecidos.md): duas
   tentativas de compactar --ws-offset (20vh, depois 6vh) tentaram encolher
   o bloco de rolagem pro CTA caber na dobra; revertidas por divergir do
   CodePen de referência. Depois disso o componente passou por uma
   contenção 100vh + overflow próprio (revertida em 2026-08-16 revisão 2,
   mecânica removida — era `[data-cdev-snap]` — e REINTRODUZIDA em
   2026-08-17 como `[data-snap="true"]` por pedido explícito de fidelidade
   ao CodePen; ver cabeçalho do arquivo). --ws-offset continua no valor
   "cheio" (50vh): é ele quem dá o percurso de scroll (agora contido na
   caixa 100vh) que revela cada palavra. */

.cdev-word-scroll {
  --fluid-min: calc(var(--font-size-min) * pow(var(--font-ratio-min), var(--font-level)));
  --fluid-max: calc(var(--font-size-max) * pow(var(--font-ratio-max), var(--font-level)));
  --fluid-preferred: calc((var(--fluid-max) - var(--fluid-min)) / (var(--font-width-max) - var(--font-width-min)));
  font-size: clamp(
    (var(--fluid-min) / 16) * 1rem,
    ((var(--fluid-min) / 16) * 1rem) - (((var(--fluid-preferred) * var(--font-width-min)) / 16) * 1rem) + (var(--fluid-preferred) * 100vi),
    (var(--fluid-max) / 16) * 1rem
  );
}

.cdev-word-scroll-inner {
  /* 2026-09-09/10: pedido original do dono era "Construímos" travado num
     pixel fixo. Tentei 3 abordagens antes desta, todas com defeito real
     (confirmado via screenshot, não só teoria):
     1) Grid 1fr/1fr (fronteira no centro matemático): trava o título, mas
        limita a lista a metade da largura — a frase mais longa cortaria.
     2) Grid max-content (título) + 1fr (lista): título travado, lista sem
        risco de corte, mas o bloco cola na borda ESQUERDA (sem espaço
        livre pro justify-content redistribuir) — nada centralizado.
     3) `<ul>` com largura FIXA (17em) + flex+justify-content:center: título
        trava de fato, MAS itens curtos ("o sistema.") deixam ~700px de
        vão vazio à direita dentro dessa caixa — o CONJUNTO VISÍVEL
        (título+palavra) fica visualmente puxado pra esquerda (print real
        confirmou: "Construímos" apareceu a ~280px de um viewport de
        1920px, não perto do centro). Travar a CAIXA não trava o que o
        olho vê como centralizado.

     Decisão final (pedido explícito do dono depois de ver o print): priorizar
     o conjunto SEMPRE parecer centralizado de verdade, aceitando que
     "Construímos" se desloque um pouco entre itens — impossível ter as
     duas coisas ao mesmo tempo quando a palavra ao lado muda de largura
     (geometria, não bug). Voltou a ser flex simples: `<ul>` se dimensiona
     pelo próprio conteúdo (shrink-to-fit, sem width fixo), e
     justify-content:center centraliza o bloco REAL a cada item. O
     deslocamento do título agora é bem mais contido que a versão original
     de 18 frases (1-7 palavras, até 41 caracteres): a lista atual (copy.md
     §1.1) é sempre "Construímos + 2 palavras", 10-28 caracteres — ainda
     varia, mas bem menos. */
  display: flex;
  flex-direction: row;
  justify-content: center;
  align-items: normal;
  gap: 0.4em;
  line-height: 1.25;
  /* Rede de segurança: com nowrap no título e nos itens (abaixo), o clamp
     de fonte já é calibrado pra nunca estourar a largura, mas isso evita
     que uma largura extrema empurre a página inteira pro lado.
     `overflow-x: clip` (nunca `hidden`) — bug real encontrado: `hidden` faz
     este elemento virar "scroll container" aos olhos do
     `animation-timeline: view()` dos <li> lá dentro (mesmo sem overflow de
     verdade, mesmo sem scroll de verdade), então o navegador media o
     progresso contra ESTA caixa em vez da viewport da página — a animação
     nunca batia com o centro real da tela (medido via
     Animation.timeline.currentTime no DevTools/Playwright). `clip` bloqueia
     o transbordo igual, mas não cria scroll container. `overflow-y: visible`
     explícito ao lado pra não deixar o navegador forçar os dois eixos pro
     mesmo valor. */
  overflow-x: clip;
  overflow-y: visible;
}

.cdev-hero .cdev-word-scroll-title {
  position: sticky;
  top: var(--ws-offset);
  margin: 0;
  /* flex:none = nunca encolhe (o título nunca pode perder espaço pro item
     da lista) — não impede o bloco de se mover entre itens, só garante que
     "Construímos" sempre renderiza no seu tamanho real. Ver decisão de
     centralização em .cdev-word-scroll-inner acima. */
  flex: none;
  height: fit-content;
  font-size: inherit;
  font-weight: 700;
  letter-spacing: -0.4px;
  white-space: nowrap;
}

.cdev-word-scroll-inner ul {
  /* Volta a se dimensionar pelo próprio conteúdo (sem width/flex fixos):
     com white-space:nowrap no item + fonte recalibrada, quem impede o
     estouro de largura é o tamanho da fonte, não uma caixa reservada — ver
     decisão de centralização em .cdev-word-scroll-inner acima (item curto
     não deve deixar vão vazio, deixaria o conjunto puxado pra esquerda). */
  font-family: var(--font-display);
  font-weight: 500;
  list-style: none;
  margin: 0;
  /* padding-block (topo E base, mesmo valor) — igual ao CodePen. Volta a
     usar o mesmo --ws-offset dos dois lados agora que o scroll é contido
     dentro da caixa 100vh (`[data-snap="true"]` acima): o bug antigo (título
     sticky ficando preso sozinho ~700px depois da última frase) era efeito
     de o scroll vazar pra página inteira sem limite — dentro de uma caixa
     100vh com altura fixa isso não deveria se repetir, mas testar depois de
     confirmar em navegador. */
  padding-block: var(--ws-offset);
  padding-left: 0;
  /* --step (grau de hue entre cada item) calculado aqui, precisa de
     --count (setado 1x pelo JS em list.style, ver initWordScroll). */
  --step: calc((var(--end) - var(--start)) / (var(--count) - 1));
}

.cdev-word-scroll-inner li {
  margin: 0;
  white-space: nowrap;
  /* position:relative só pra z-index valer — precisa ficar ABAIXO de
     #credibilidade (ver main.css), pedido do dono. */
  position: relative;
  z-index: 1;
}

/* Cor por item: varredura de matiz OKLCH, --i setado 1x pelo JS (índice cru,
   não percentual). Item 0 fica em --start graus, último item em --end graus
   (mas o último item é sobrescrito pela regra de gradient-text abaixo). */
.cdev-word-scroll-inner li:not(:last-of-type) {
  color: oklch(var(--lightness) var(--base-chroma) calc(var(--start) + (var(--step) * var(--i))));
}

/* Título e último item: mesmo efeito "gradient-text" do CodePen —
   `canvasText`/`canvas` são cores de sistema (se adaptam ao color-scheme:
   dark do <html>, aqui sempre próximas de branco/preto). Não é decisão de
   marca, é o CodePen original. */
.cdev-hero .cdev-word-scroll-title,
.cdev-word-scroll-inner li:last-of-type {
  background: linear-gradient(canvasText 50%, color-mix(in oklch, canvas, canvasText 25%));
  background-clip: text;
  color: #0000;
}

/* Registro tipado (@property) de --start-opacity/--end-opacity/--brightness:
   sem isso, var() dentro de @keyframes não lê o valor setado por
   li:first-of-type/li:last-of-type de forma confiável no Chromium — todo
   item ficava preso no fallback (0.2/1.2), nunca no valor real (1) definido
   pra primeiro/último item (bug real, confirmado via Playwright: opacity
   computada ficava 0.2 mesmo com --start-opacity:1 no elemento). Registrar
   como <number> resolve. */
@property --start-opacity {
  syntax: '<number>';
  inherits: false;
  initial-value: 0.2;
}
@property --end-opacity {
  syntax: '<number>';
  inherits: false;
  initial-value: 0.2;
}
@property --brightness {
  syntax: '<number>';
  inherits: false;
  initial-value: 1.2;
}

/* Brilho ligado ao scroll: animation-timeline: view() puro (sem JS por
   frame). Só opacity + brightness — sem 3D (perspective/rotateX/blur) por
   pedido explícito de fidelidade ao CodePen, que só anima isso. */
@supports (animation-timeline: view()) {
  [data-animate="true"] li {
    opacity: 0.2;
    animation-name: cdev-ws-brighten;
    animation-fill-mode: both;
    animation-timing-function: linear;
    /* "cover" repetido nos dois lados (não só uma vez como no CodePen) —
       fix de bug real do Chromium: escrever só uma vez faz o navegador
       resolver o fim como range "normal" em vez de "cover", os dois lados
       descasam e a animação nunca sai do progress 0 (confirmado via
       Playwright/DevTools Animations). Único ponto que diverge do CodePen
       de propósito, ver cabeçalho do arquivo. */
    animation-range: cover calc(50% - 1lh) cover calc(50% + 1lh);
    animation-timeline: view();
  }

  [data-animate="true"] li:first-of-type {
    --start-opacity: 1;
  }

  [data-animate="true"] li:last-of-type {
    --brightness: 1;
    --end-opacity: 1;
  }
}

@keyframes cdev-ws-brighten {
  0% {
    opacity: var(--start-opacity, 0.2);
  }
  50% {
    opacity: 1;
    filter: brightness(var(--brightness, 1.2));
  }
  100% {
    opacity: var(--end-opacity, 0.2);
  }
}

/* Obrigatório (design-system § 5.8): reduced-motion desliga o brilho
   animado e trava no estado plenamente visível — cor por item continua
   (não é movimento). */
@media (prefers-reduced-motion: reduce) {
  [data-animate="true"] li {
    opacity: 1;
    filter: none;
    animation: none;
  }
}

/* ==========================================================================
   Entrada 3D scroll-driven (CSS puro) — pedido do dono, 2026-08-16.
   Replica a técnica do CodePen "azmBEPL" (Daniel Haim): perspective +
   rotateX interpolado via @property, movido pelo scroll nativo
   (animation-timeline), sem JS. Ver docs/design-system.md § 5.8.

   Por que no TÍTULO, e não na lista ou na caixa 100vh inteira:
   - a caixa inteira (`.cdev-word-scroll`) tem 100vh — rotacionar algo desse
     tamanho em X distorce/corta visualmente perto das bordas do viewport;
   - a lista já tem seu próprio efeito por item (brilho via
     animation-timeline: view(), ver bloco `[data-animate="true"] li` acima)
     — animar isso junto via CSS entraria em conflito de composição com a
     própria animação do item;
   - o título é pequeno, sempre visível (sticky) e é o elemento que
     "recebe" o lead — um pop-in 3D nele lê como um único momento
     orquestrado (design-system.md § 8), sem competir com o glow da lista.

   `view-timeline-name` fica em `.cdev-hero` (não em `.cdev-word-scroll`,
   que às vezes é seu próprio scroll container via `overflow-y: auto` do
   snap acima) — assim a entrada é sempre medida contra a página, nunca
   contra o scroll interno da caixa 100vh. `.cdev-word-scroll-title` sofre
   a `animation-timeline: --hero-enter` referenciando esse nome.
   ========================================================================== */
@property --ws-tilt {
  syntax: '<angle>';
  inherits: false;
  initial-value: 20deg;
}

.cdev-hero {
  view-timeline-name: --hero-enter;
  view-timeline-axis: block;
  /* 2026-09-08: fundo próprio (gradiente estático + canvas WebGL, ver
     .cdev-hero/.cdev-hero-canvas em base.css) — NÃO sobrescrever
     `background` aqui. `.section-dark` (também na <section id="hero">) seta
     `background: var(--cdev-black)` sólido; sem essa regra em base.css
     vencer por especificidade igual + ordem de carga (word-scroll.css
     depois de base.css), o preto opaco tamparia o gradiente do hero. */
}

.cdev-hero .cdev-word-scroll-title {
  transform: perspective(1000px) rotateX(var(--ws-tilt));
  opacity: 0;
  will-change: transform, opacity;
  animation: cdev-ws-tilt-in linear both;
  animation-timeline: --hero-enter;
  /* Assenta bem no início da entrada da seção — o título é sticky e
     precisa estar legível bem antes do lead começar a rolar a lista por
     dentro da caixa 100vh. */
  animation-range: entry 0% entry 50%;
}

@keyframes cdev-ws-tilt-in {
  from {
    --ws-tilt: 20deg;
    opacity: 0;
    transform: perspective(1000px) rotateX(var(--ws-tilt));
  }
  to {
    --ws-tilt: 0deg;
    opacity: 1;
    transform: perspective(1000px) rotateX(var(--ws-tilt));
  }
}

/* Fallback obrigatório — mesmo motivo documentado no resto deste arquivo:
   sem @supports aqui, um navegador sem suporte a animation-timeline nunca
   dispara a animação e o título fica preso apagado/rotacionado, uma falha
   silenciosa. Sem suporte, o título nasce direto no estado final. */
@supports not (animation-timeline: view()) {
  .cdev-hero .cdev-word-scroll-title {
    animation: none;
    will-change: auto;
    transform: none;
    opacity: 1;
  }
}

@media (prefers-reduced-motion: reduce) {
  .cdev-hero .cdev-word-scroll-title {
    animation: none;
    will-change: auto;
    transform: none;
    opacity: 1;
  }
}
