# Palco alternado de agência — herói fixo, grão vivo, header em difference

Lido 2026-09-02 em <https://humandone.com/> (Gatsby + Chakra + ScrollSmoother),
medido por CDP em Chrome headless a 1920×1080 @2x. Nenhuma réplica foi
construída: as receitas abaixo vêm da folha e do computed da página viva, e as
curvas de scrub estão ajustadas contra amostras reais (rms indicado).

- **Header que nunca escolhe cor.** `<header position:fixed; z-index:20;
  mix-blend-mode: difference; color:#fff`. Sobre o palco #121214 ele sai branco;
  sobre a seção #ffffff sai preto. Um só elemento atravessa quatro inversões de
  fundo sem uma linha de JS de tema e sem duplicar o logotipo. O preço: o header
  não pode ter fundo próprio nem sombra, e qualquer cor de marca dentro dele
  vira seu complemento.
- **Grão vivo em três quadros.** Camada `position:fixed; inset:0; z-index:50;
  overflow:hidden; pointer-events:none`, com um filho `position:absolute;
  left:-50%; top:-50%; width:200%; height:200%` pintado por um PNG 128×128 de
  8 bits em paleta (≈3,6 KB, data-URI) com `background-repeat:repeat`; animação
  `1.2s steps(3) infinite both` deslocando o ladrilho em `translate3d` de ±5% a
  ±10% a cada 10% do ciclo. O `steps(3)` é a receita: três quadros por ciclo,
  não um deslize — é assim que lê como granulação de filme e não como textura
  arrastando. Efeito medido sobre o que está atrás: `#FFFFFF` declarado renderiza
  (252,252,252). O dobro de tamanho com deslocamento de meia tela existe só para
  o ladrilho nunca descobrir a borda enquanto treme.
- **A cortina branca é a headline com zoom.** Não existe painel branco subindo.
  O `<h1>` recebe um scrub de `scale` 1 → 355 com `transform-origin` no próprio
  centro (`856.26px 237.6px`), ease `x^4.02` — power4.in sobre scrollY 0 → 540,
  rms 0.0010 em 28 amostras; por volta de 400px de scroll uma letra branca já
  ocupa a tela inteira. Só então o herói (`position:fixed`, 1080px, que não rola)
  apaga por `opacity` 1 → 0 entre 755 e 1090, ease `x^2.03` (power2.in, rms
  0.0010): quando o fade começa, a troca de palco já aconteceu, e é por isso que
  a passagem não tem costura. O conteúdo seguinte sobe dentro de
  `#smooth-content` com `translateY(-scrollY)` do ScrollSmoother.
  **Como esta receita foi corrigida:** a primeira leitura descreveu uma cortina
  branca deslizando, porque as capturas de tela eram compatíveis com isso. O que
  provou o contrário foi um diff de pixel da réplica: 10,4% dos pixels divergiam
  no scroll 1080 com a opacidade do herói batendo em quatro casas decimais. Um
  `elementFromPoint` naquele ponto devolveu o `<h1>` medindo 604920×153360.
- **Título pinado como marca-d'água.** Entre os scrolls 2534 e 5306 o bloco do
  título recebe `translateY(scrollY − 2534)` — um pin de ScrollTrigger, com um
  `pin-spacer` de 513px segurando o lugar no fluxo — o que o mantém parado a
  233,5px do topo do viewport enquanto os cases sobem por cima. Sem o pin, a
  marca-d'água sai da tela nos primeiros 500px e a seção perde o fundo. O diff
  mecânico não pega isto (as posições em repouso batem); pega-se amostrando
  `getBoundingClientRect().top` do mesmo nó em vários scrolls e vendo o valor
  não mudar.
- **Título que se retira para virar marca-d'água.** Um `<h2>` de 480px/480 na cor
  do próprio fundo (#121214) contém um `<div>` com
  `-webkit-text-fill-color: transparent; background-clip: text` e o gradiente da
  marca. No scroll, `scale` 1 → 0.75 e `opacity` 1 → 0.1 sobre 2555 → 3135
  (580px), ease `x^0.65` (out), rms 0.0173 — e as duas propriedades dividem um
  único progresso (divergência máxima 0.0002 em 21 amostras), o que identifica um
  tween só, não dois. O `color` escuro no elemento externo é o fallback: onde o
  `background-clip: text` não pinta, o título desaparece no fundo em vez de
  aparecer preto.
- **Bloco escuro que se descola da página.** No fim da seção, `border-radius`
  0 → 57.6px (3vw) e recuo lateral 0 → 32.16px, ambos no mesmo progresso, sobre
  270px de scroll, ease `x^3.23` — power3.in, rms 0.0069. Full-bleed durante toda
  a leitura, vira cartão só ao sair: a página inteira se comporta como uma folha
  que se solta.
- **Fio de grade sem borda.** `display:grid; gap:1px; background-color:#E1E1E4;
  border:1px solid #E1E1E4` com células brancas — o fundo aparece pelo gap e
  produz uma malha de 1px sem colapso de bordas, respondendo com
  `grid-template-columns` 2 → 3 → 5. Hover na grade leva todos os logos a 0.6 e
  o logo sob o cursor volta a 1 (`.grid:hover a > div {opacity:.6}` seguido de
  `.grid:hover a:hover > div {opacity:1}`): um holofote em duas regras.
- **Recuo em vez de avanço.** Card de case com `transform: scale(0.96)` em 0.35s
  ease-in-out no hover, e um `::after` de
  `radial-gradient(90.57% 67.98% at 94.74% 96.39%, #478CC4, rgba(71,140,196,.4)
  48.96%, transparent)` que entra em 0.3s ease-in. O cartão encolhe e acende num
  canto — o oposto do lift padrão.
- **Headline como SVG por breakpoint.** O `<h1>` não tem texto: carrega um SVG de
  dois `<path>` (um branco, um com `fill="url(#gradiente)"`) e a responsividade é
  a troca da prancheta — 552×504, 854×252, 1028×300, 1704×432. A palavra
  "Empowering" não existe em `document.body.innerText`. Custo: o `<h1>` é vazio
  para leitor de tela e para busca; ganho: o gradiente atravessa as letras em
  espaço de usuário, com quebra de linha e kerning fixados no desenho.
- **Rabisco desenhado no hover, e um sublinhado morto ao lado.** Cada link do nav
  carrega um `<svg>` absoluto sob a palavra cujo `path` vai de
  `stroke-dasharray: 0px, 220px` a `200px, 200px` numa transição de `0.6s` — o
  traço se desenha da esquerda para a direita, com um caminho diferente por link
  (comprimentos medidos: 62,2 para "Cases", 197,2 para "Cases in Figma"). Ao lado
  dele sobrevive um `::after` de 1px que é `width:0` em repouso **e** `width:0` no
  hover: o sublinhado clássico que o rabisco substituiu, deixado no bundle.
  **Como esta receita foi corrigida:** a primeira passagem de ponteiro relatou
  "nenhuma mudança computada" nos links do nav, porque a lista de propriedades
  amostradas não incluía `stroke-dasharray`. A lista de propriedades é o teto do
  que qualquer diff enxerga — quando um achado escapa por fora dela, alargue a
  lista; olhar com mais atenção não alcança.
- **Anel de botão em `::before`, não em `border`.** O pill do botão grande é um
  `::before` com `inset:0`, `border:1px solid #fff` e `border-radius:40px`. O
  `border` do próprio botão é `0px none`. É o que permite o hover inverter o
  preenchimento sem que o anel mude de espessura ou de posição — e é a razão de
  uma amostra de pixel a 2px de distância da borda não achar linha nenhuma.
- **Conota:** o gradiente único da marca
  (`linear-gradient(102.77deg, #B608FF, #FF5C00 33.33%, #FFA927 66.67%, #FFFFFF)`)
  é reservado a três lugares — a linha do benefício ("for Business Success"), a
  palavra "Work" e o "or" entre os dois botões de contato. As linhas que descrevem
  o serviço ficam brancas. A cor marca o que é do cliente, nunca o que é da
  agência; e por terminar em branco, o gradiente desmaia em vez de fechar.
- **Verificado por construção (2026-09-02).** Uma réplica local em HTML/CSS/JS
  puros, montada contra estas medidas, fechou o portão de paridade em **0
  divergências** em 32 papéis declarados, com 31 deles idênticos em x, y, largura
  e altura (o 32º é o ladrilho do grão, em outro quadro da própria animação) e
  0,44% de pixels acima de 6% de luminância contra um piso de 0,088% que é só o
  grão fora de fase. As três descobertas acima — zoom da headline, pin do título,
  `background-size: 135%` — vieram do diff, não da leitura.
- **Correction (2026-09-02, follow-up pass): the transform-origin above is not
  a fixed pixel pair, and the ease above is not a clean power curve.** Both
  the gate and the pixel diff had been run once, at 1920px; opened at 1512px,
  the zoom landed on the wrong word. `transform-origin` is breakpoint-scoped —
  `856.26px 237.6px` is right only at ≥1920, and at the 1028×300, 854×252 and
  552×504 SVG variants the same origin point sits inside "Success," not the
  headline, so the reveal painted a blurry orange smear instead of crisp white
  letters. Separately, refitting the scale-vs-scroll curve on 16 dense samples
  (y=0→500, scale 1→355) found the family itself wrong, not the exponent: the
  best power-curve fit (`x^3.95`) undershot by up to 8 percentage points of
  progress through the middle of the range (at y=360: real progress 0.289, fit
  0.273), one-sided rather than scattered. The recipe that actually matches
  drives the scale from the 16 measured (y, scale) pairs directly, via linear
  interpolation — never the closed form. General versions of both lessons
  (verify breakpoint-dependent properties at every breakpoint; check an ease
  fit's residual for one-sidedness before trusting the family) are in
  `live-extraction.md`'s *Sweeping the responsive dimension* / *The parity
  gate*, and in `SKILL.md`'s *Measuring an animation that a trigger fires*.
