Caderno interno · 29/10/2026 · noindex

/// 00 · autópsia visual

Não é página de builder. É um sistema.

A LP da Upward Academy em /promo parece “nível Apple” porque o HTML já nasce no estado final, as seções trocam de pele, e o JS só anima. O curso é outro assunto. Aqui está o que o site faz.

Fonte lp.upwardacademy.com.br/promo HTML 248 kB · CSS inline 137 kB · bundle 109 kB WDF 2.0.0 · Vercel · /v2/

Pular para a receita
13seções com kicker /// 00 até 10, cada uma com data-theme.
3peles: ink (#0A0A0A), paper (#EEEAE3), flame (#FF5A1F). O JS pinta o theme-color junto.
6scripts de motion: GSAP 3.15 + ScrollTrigger + SplitText + CustomEase + Lenis 1.3.26 + wdf.*.js.
0Webflow, Framer, Three.js, Lottie, Barba, Swiper. Design system próprio chamado WDF.

/// 01 · o que está no ar

HTML estático na Vercel, design system em /v2/.

Captura do HTML vivo em 29/10/2026. Comentários no próprio arquivo mapeiam um spec interno (core-top F0a, chrome-top F0b, §4.x). Quem desenhou isso pensa em peças, não em “seções de LP”.

Stack observada

  • Um <style> de 137 kB. Zero CSS externo.
  • Fontes self-hosted: Geist Variable (corpo), NeuePower Ultra/Medium (display), Geist Mono (kickers). Subset PT, preload do first-frame frame_0042.webp.
  • Bundle /v2/dist/wdf.4167b7bf16.js (109 kB) expõe WDF 2.0.0, registra plugins, cria as eases wdf.out e wdf.inout.
  • Lenis com lerp: .09, autoRaf: false, tick no ticker do GSAP.
  • Tracking (Meta Pixel, GA, Clarity) só no requestIdleCallback.

O boot antes da 1ª pintura

  • Tira no-js, coloca is-motion ou is-static.
  • Pula o preloader se: reduced-motion, saveData, hash na URL, UTM/gclid/fbclid, mobile < 900px, ou preloader já visto na sessão.
  • Dois scripts inline medem o hero (side vs stack no desktop, altura da figura no mobile) antes do GSAP — o topo não pula (CLS).
  • history.scrollRestoration = 'manual' porque o Lenis manda no scroll.

/// 02 · as libs

Seis scripts. Zero builder.

Tudo self-hosted em /v2/vendor/ e um bundle próprio. A ordem no HTML é a ordem do motor: GSAP primeiro, plugins, Lenis, WDF por último. Tracking fica de fora dessa fila — entra no idle.

1 GSAP → 2 ScrollTrigger → 3 SplitText → 4 CustomEase → 5 Lenis → 6 WDF 2.0.0
01 · core3.15.0

GSAP

/v2/vendor/gsap.min.js?v=3.15.0

Motor de tween. O WDF só liga o resto se gsap existir. Default de duração no registro: 1.15s, ease wdf.out.

GreenSockpadrão livre
02 · plugin3.15.0

ScrollTrigger

/v2/vendor/ScrollTrigger.min.js?v=3.15.0

Pin, scrub e troca de pele. Cada section[data-theme] dispara em start: "top 60px" e o bundle pinta o theme-color.

plugin livre
03 · club3.15.0

SplitText

/v2/vendor/SplitText.min.js?v=3.15.0

Quebra títulos em lines e words — não em letras. Plugin pago do GSAP Club. No caderno da Gestão Bem, um reveal CSS cobre o mesmo papel.

GSAP Clubpago
04 · plugin3.15.0

CustomEase

/v2/vendor/CustomEase.min.js?v=3.15.0

Cria as duas curvas do sistema: wdf.out .19,1,.22,1 e wdf.inout .77,0,.18,1. É a mesma bezier do CSS.

plugin livre
05 · scroll1.3.26

Lenis

/v2/vendor/lenis.min.js?v=1.3.26

lerp: .09, smoothWheel: true, syncTouch: false, autoRaf: false. O RAF é o ticker do GSAP. Touch fica nativo. respectReducedMotion: false — quem corta o smooth é o boot is-static.

MITdarkroom
06 · próprio2.0.0 · 109 kB

WDF

/v2/dist/wdf.4167b7bf16.js

O design system. Expõe window.WDF: registra plugins, cria as eases, liga Lenis, cursor, magnético, preloader, dock e o bus das camadas. Sem isso, os vendors são só arquivos.

Upwardproprietário
bootsó se os quatro existirem

O WDF recusa o motion pela metade.

ok = gsap && ScrollTrigger
     && SplitText && CustomEase
ok && gsap.registerPlugin(
  ScrollTrigger, SplitText, CustomEase)
ok && CustomEase.create(
  "wdf.out", ".19,1,.22,1")
ok && gsap.defaults({
  ease: "wdf.out", duration: 1.15 })

Fontes · também vendor

Self-hosted em /v2/fonts/, subset PT, preload no <head>. Não passam por fonts.googleapis.com.

  • Geist Variable — corpo, peso 100–900
  • NeuePower Ultra / Medium — display (Ultra com subset PT, Medium só ASCII)
  • Geist Mono Variable — kickers e meta

Tracking · idle, não no motor

Três tags num requestIdleCallback (fallback 3s). Fora da fila GSAP/Lenis, depois da página interativa.

  • Meta Pixel
  • Google Analytics 4
  • Microsoft Clarity

Não entra no HTML

Webflow Framer Three.js Lottie Barba Swiper ScrollSmoother GSAP Flip jQuery

/// 03 · anatomia

Seis camadas, não uma landing.

Os comentários do HTML separam chrome (sempre presente) de seções (que só pintam). É por isso que o site não “parece template”.

F0a · core-top

Sprite, skip, toast, dialog

Ícones e logo num sprite. Skip link. Toast e <dialog> prontos. Entra logo depois do body.

F0b · chrome-top

Preloader, nav, rail, cursor, grain

Grade 64px no preloader, duas tampas que se abrem. Nav em pílulas de vidro. Rail de progresso. Cursor dot+anel. Grain SVG.

Main · 13 seções

Pele por seção

Cada <section data-theme> é ink, paper ou flame. O ScrollTrigger sincroniza o theme-color do browser quando a seção cruza 60px do topo.

Tipo

Três famílias, escala clamp

Display (NeuePower), corpo (Geist), mono (kickers). Mega usa min(vw, svh). Corpo fixo em 16px. Kicker 12px com /// 00 ·.

Motion

Uma curva, um motor

cubic-bezier(.19, 1, .22, 1) em tudo. Durações xs 0.15 / s 0.3 / m 0.45 / l 0.9 / xl 1.15. SplitText em linhas, não em letras.

F0b · chrome-bottom

WhatsApp + dock

Botão flutuante no desktop, dock de conversão no mobile. Visibilidade por JS. CTA magnético com --mx/--my.

/// 04 · as três peles

Toca no botão do topo. É o mesmo truque.

Eles não pintam seção por seção com hex solto. Têm um dicionário (--ink, --paper, --flame) e um segundo dicionário semântico (--bg, --display, --cta-bg) que cada pele reescreve. O caderno que você está lendo usa os hex reais.

Ink#0A0A0A
Ink 2#141414
Paper#EEEAE3
Paper 2#F7F5F0
Flame#FF5A1F
Grey 2#9A9A9A

Paper inverte o contraste (fundo quente, texto ink, acento flame-deep). Flame vira o laranja em palco e o ink vira o acento. CTA sempre “o contrário do fundo”: branco no ink, ink no paper e no flame. Uma cor de marca, usada como interruptor — não como gradiente em tudo.

[data-theme=ink]   { --bg: var(--ink);   --display: #fff;    --cta-bg: #fff; --accent: var(--flame); }
[data-theme=paper] { --bg: var(--paper); --display: var(--ink); --cta-bg: var(--ink); --accent: var(--flame); }
[data-theme=flame] { --bg: var(--flame); --display: #fff;    --cta-bg: var(--ink); --accent: var(--ink); }

/// 05 · escala

O título é o herói. O corpo não compete.

Na LP o display chega a 172px e o mega usa o menor entre a viewport e 46svh. Corpo travado em 16 / 14 / 12. Kickers em 12px com letter-spacing aberto. Isso é o “nível Apple”: um cartaz, não um artigo.

/// 06 · chrome

O luxo está na borda da página.

Preloader, grain, cursor e botão magnético não vendem o curso. Eles dizem “isso foi feito à mão”. No desktop. No mobile eles desligam metade.

Grain

SVG de 160px com feTurbulence, opacity 0.06 (0.045 no paper). No desktop com motion, o bitmap anda em steps(1) a cada 1.2s — parece filme, não GIF. Este caderno usa o mesmo truque.

Cursor (só ponteiro fino)

Dot de 6px + anel de 38px. Lerp no anel (1 − 0.82^(dt×60)). Em links o anel cresce e pode mostrar um label mono. Some se um dialog abre. Prefer reduced-motion: nem nasce.

Magnético

Classe .magnetic + CSS translate: var(--mx) var(--my). O JS mede o retângulo, tira o offset atual, escreve a diferença. O botão “Pular para a receita” deste caderno faz a versão curta.

Preloader que se recusa

Grade 64px com máscara radial, duas tampas ink. Se o visitante veio de anúncio (UTM), está no celular, ou já viu nesta sessão — pre-skip e o preloader nem pinta. Performance como feature de marca.

/// 07 · o pulo do gato

O HTML já está no estado final.

Comentário literal da seção Solução: “HTML no ESTADO FINAL (.is-struck .is-selected .is-checked .is-paid); em is-motion o JS remove as classes ainda não alcançadas e as reaplica.” Autor: a foto já aparece inteira no CSS; o JS só esconde o rosto se houver motion.

Quem abre o arquivo sem JavaScript ainda vê a página vendida. O motion é um verniz, não a estrutura.Leitura dos comentários em /promo · 29/10/2026

Isso mata CLS, mata o “flash de conteúdo vazio” e deixa o no-js utilizável. Para as LPs da Gestão Bem: escreve o markup como se a animação já tivesse acabado. Se o GSAP falhar, a página continua certa.

/// 08 · motion

Um easing. Split por linha. Lenis no ticker.

O motor

  • GSAP registra ScrollTrigger + SplitText + CustomEase só se os quatro existirem.
  • CustomEase.create("wdf.out", ".19,1,.22,1") — a mesma curva do CSS.
  • Default de duração: 1.15s. Split em lines e words, não em caracteres (barato e legível).
  • Lenis lerp: .09, smoothWheel: true, syncTouch: false. O RAF é o ticker do GSAP, não um loop paralelo.

O que eles não usam

  • Three.js, canvas 3D, Lottie, Barba, Swiper, GSAP Flip, ScrollSmoother nativo.
  • Animação de letra por letra no hero (caro, feio no wrap PT-BR).
  • Smooth scroll no touch. Celular é nativo.
  • Motion se prefers-reduced-motion — vira is-static e o HTML final permanece.

/// 09 · o roteiro

Treze seções, uma ideia cada.

A beleza também é editorial: ink → paper → ink → paper → flame no preço → paper no autor → ink na garantia. O olho descansa. A oferta só pinta de laranja quando precisa.

—

Hero · inicio

Ink. Figura + first-frame webp. Layout side/stack medido no inline.

ink
00

Problema

Ainda ink. Continuação do herói, sem quebrar a pele.

ink
01

Solução · “A edição”

Paper. HTML já com struck/selected/checked/paid.

paper
02

Método

“Do zero absoluto à página vendida.” Volta ink.

ink
03

Superpoder

Bloco oculto a pedido do dono; anatomia continua no DOM.

ink · hidden
04

Depoimentos

Paper. Prova social no fundo quente.

paper
05

Showcase

Ink. “Galeria que liga ao chegar.” Posters já coloridos no HTML.

ink
06

Módulos

Paper. “Sumário vivo”, sem pin de scroll.

paper
07

Preço

Flame. A única seção que vira o laranja em palco.

flame
08

Autor · “Sem máscara”

Paper. Cortina da armadura → rosto, scrub reversível.

paper
09

Garantia

Ink de novo, depois do choque laranja.

ink
10

FAQ + CTA final

FAQ paper; fecho ink com data-ui-theme=flame no chrome.

paper / ink

/// 10 · o que copiar

Receita pra LP da Gestão Bem.

Oito hábitos. Nenhum pede GSAP Club, NeuePower nem 137 kB de CSS. O caderno que você está vendo já faz 1, 2, 3, 5 e 7.

01

Três tokens, três peles

--bg --fg --accent --muted --line. Uma pele dark, uma paper, uma de acento. Seção declara a pele, não o hex.

02

Kicker numerado

/// 00 · em mono. O visitante sabe onde está. Custa um :before.

03

Uma curva

cubic-bezier(.19, 1, .22, 1) em hover, translate e theme. Duração 150–250 ms no UI, 900–1150 ms no reveal.

04

HTML no estado final

Escreve a seção já “terminada”. Motion só descasca e recoloca. No-js continua vendendo.

05

Grain + respiro

SVG noise a 5%. Padding de seção clamp(96px, 14vh, 180px) na LP; aqui um pouco menos, a ideia é a mesma: ar.

06

Tipo em trio

Display com personalidade, corpo neutro, mono pra meta. Self-hosted. Preload só do first-frame e das woff2 do herói.

07

Desligar no mobile

Preloader, cursor, Lenis no touch, grain animado: off abaixo de 900px, off em UTM, off em reduced-motion.

08

Medir antes de pintar

Hero side/stack e altura da figura no inline, não no bundle. CLS zero é parte do “bonitão”.

/// 11 · o que não copiar

O verniz caro, o tracking, a oferta.

Deixa

Cursor custom em formulário. 137 kB de CSS inline. SplitText Club se um reveal CSS resolve. Copiar NeuePower, os posters, os depoimentos, o copy do curso. Pixel/Clarity “porque a LP tem”. Flame em todas as seções — eles usam uma vez, no preço.

Leva

Pele por seção. Kicker. HTML final. Uma easing. Grain quieto. Self-hosted. Tracking idle. Comentários no HTML apontando o spec (eles fazem isso; o arquivo vira documentação).