feat(site): blocos 3D da marca, novas ofertas e hero mais abrangente
Hero e movimento - cena 3D em tempo real com os blocos da marca (kit feito no Blender, 3d/blocos.py): marca, tela, integrações e dados; segue a rolagem, tem vida em repouso, reage ao cursor e, parada no topo, passa sozinha pelos estados com legenda - três níveis de aparelho (full, lite, static); sem WebGL fica o pôster - título novo: "Sites e sistemas sob medida para o jeito que a sua empresa trabalha." - faixa de clientes com o ramo, linha do primeiro passo, hero alinhado às seções - rótulos digitados, entrada dos blocos, cortina de blocos entre páginas e capa compartilhada entre a lista de cases e o case Ofertas - atendimento no WhatsApp com IA e nota fiscal para a reforma tributária (páginas de serviço, cartões da home, formulário e aviso com data de validade) - página /ia atualizada, com fontes - peças 3D em loop nos cartões e topos de página (3d/pecas.py) Design - hover por papel (bloco nos botões, barra nos links), rótulos sem caixa alta - um destino por rótulo: "Falar sobre meu projeto" leva ao formulário, "Chamar no WhatsApp" abre o WhatsApp - logo montado em blocos, 404 redesenhado, grades sem célula vazia - ícones do PWA e manifesto novos Specs em specs/, contexto de design em DESIGN.md e PRODUCT.md.
This commit is contained in:
1 parent
4671837c44
commit
c7d09d63d4
165 files changed
+7940
-3790
No files matched your search
@@ -0,0 +1,260 @@
|
||||
# Blocos: hero 3D feito no Blender, movimento e ícone do app
|
||||
**Autor:** Evandro · **Data:** 2026-11-09 · **Porte:** grande · **Status:** em execução
|
||||
|
||||
## 1. Objetivo
|
||||
Trocar o fundo de partículas do hero por uma peça 3D modelada no Blender (os blocos da marca, que se remontam em tela, integrações e dados), dar ao site uma linguagem de movimento única (escrita, entrada e saída de componentes, troca de tela) e corrigir o ícone do aplicativo instalado. Hoje o 3D é um fundo discreto, só no computador, e o celular não recebe nada.
|
||||
|
||||
Leitura (frontend-orchestrator): refino com mundo visual novo no hero · Persuade (suposição S1) · web · `3d`, `premium` → impeccable, awwwards, threejs-fundamentals/geometry/materials/shaders/animation.
|
||||
|
||||
## 2. Escopo
|
||||
- Dentro: ícones do manifesto; pipeline Blender → web em `3d/`; hero em três níveis (tempo real, tempo real leve, pôster); escrita, entrada/saída e troca de tela em CSS; peças 3D pré-renderizadas nos cartões de Soluções e no 404.
|
||||
- Fora (não fazer): biblioteca de animação (GSAP, Motion) ou React Three Fiber, removidos em 2026-10-14 por desempenho; mudar texto, SEO ou estrutura das seções; service worker; modelos gerados por IA ou de bibliotecas de terceiros; vídeo de rolagem no celular; merge dos branches `wip/*`.
|
||||
|
||||
## 3. Critérios de aceite
|
||||
- CA1 (ícone): o manifesto serve 4 ícones em URLs novas (`any` e `maskable`, 192 e 512) e `id: "/"`; no maskable a marca cabe na zona segura (caixa ≤ 57% do lado, centrada ±2%) → `pnpm test -- app/manifest.test.ts` e `node scripts/build-icons.mjs --check`.
|
||||
- CA2 (ativos): `blender -b --python 3d/blocos.py` recria do zero `public/3d/` (`bloco.glb` ≤ 40 KB, `blocos.json` ≤ 40 KB em gzip, 2 matcaps ≤ 30 KB cada, `hero-poster.webp` ≤ 90 KB), com N ≤ 320 blocos e ≤ 60 mil triângulos → o comando sai com `OK` e código 0.
|
||||
- CA3 (pôster): sem JavaScript ou com movimento reduzido o hero mostra o pôster da marca em blocos, todo o conteúdo visível, sem pedido a `/3d/bloco.glb` nem ao pacote do Three.js → `pnpm test:e2e -- hero-3d`.
|
||||
- CA4 (computador): em aparelho `full` os blocos montam a marca em até 2,6 s e a rolagem leva marca → tela → integrações → dados dentro dos trechos de `hero-captions.ts` → `pnpm test -- lib/blocos.test.ts` e captura local com GPU nos progressos 0, 0,35, 0,62 e 0,85.
|
||||
- CA5 (celular): em aparelho `lite` a cena roda com DPR ≤ 1,5 e 30 quadros por segundo sob demanda, trocando de estado sozinha enquanto está à vista; se a média dos 60 primeiros quadros passar de 28 ms, volta ao pôster e lembra na sessão → `pnpm test -- lib/device.test.ts BlocosScene` e inspeção num celular real.
|
||||
- CA6 (desempenho): em 390×844 com rede e CPU limitadas, LCP < 2,5 s e continua sendo o H1, CLS < 0,05, os dois botões inteiros na primeira dobra → `shoot.mjs` do awwwards (`mobile.vitals`) sobre `pnpm build && pnpm start`.
|
||||
- CA7 (escrita): rótulos em fonte mono entram digitados (cursor de bloco) e o trecho verde do H1 entra como marca-texto; o texto está no HTML desde o primeiro quadro; só `transform`, `opacity` e `clip-path` animam → `pnpm test -- Hero` e inspeção com movimento reduzido.
|
||||
- CA8 (entrada e saída): menu do celular, vídeo ampliado, aviso de cookies, botão do WhatsApp e perguntas frequentes animam ao abrir e ao fechar, sem JavaScript novo; navegador sem suporte troca na hora → `pnpm test:e2e -- mobile-menu keyboard` verdes e inspeção.
|
||||
- CA9 (troca de tela): a troca de página usa a cortina de blocos (≤ 700 ms) e a capa do projeto vira o topo do case; com movimento reduzido não há animação → `pnpm test:e2e -- navigation routes` verdes e captura dos quadros da troca.
|
||||
- CA10 (peças): os 3 cartões de Soluções e o 404 mostram peças do mesmo kit renderizadas no Blender (loop ≤ 3 s, ≤ 250 KB por arquivo, pôster ≤ 40 KB), tocando só à vista, sem contexto WebGL extra → `ls -l public/3d` e `pnpm test:e2e`.
|
||||
- CA11 (fechamento): `pnpm lint && pnpm typecheck && pnpm test && pnpm build && pnpm test:e2e` verdes; impeccable critique sem heurística abaixo de 3/4; audit sem falha; jury com 8 ou mais em cada categoria.
|
||||
|
||||
## 4. Contexto verificado
|
||||
Código lido no `main` do Gitea (`4671837`, 2026-10-24), que é o que está no ar. O checkout local está em `b2f3899` (2026-10-05), 54 commits atrás, com `origin` num repositório do GitHub que não existe mais.
|
||||
- `package.json`: só `three` 0.182 e `lenis`; animação é CSS (`animation-timeline`, View Transitions).
|
||||
- `app/components/cinematic/HeroFieldScene.tsx:26,422,692,969,999`: nuvem de pontos procedural em três camadas, fundo ambiente. Padrão de referência do motor (renderizador, descarte, desenho por software).
|
||||
- `app/components/cinematic/HeroField.tsx:195,201` e `lib/device.ts:42-53`: toque, ≤ 4 núcleos, ≤ 4 GB, economia de dados ou movimento reduzido não recebem cena nem baixam o Three.js.
|
||||
- `app/components/Hero.tsx:64-66,92,143`: palco, H1 (LCP) e `HeroWork` (navegador e celular com trabalho real, 40% da largura).
|
||||
- `app/components/hero-captions.ts`: legendas "A tela", "As integrações", "Os dados" com trechos 0,18–0,49, 0,51–0,75, 0,79–1,07.
|
||||
- `app/manifest.ts:17-28`: dois ícones (`/brand/icon-192.png`, `/icon.png`), sem `purpose` nem `id`. Arquivos iguais aos do ar (md5) e com o símbolo de blocos atual. A marca ocupa 395×392 de 512 (77%), fora da zona segura.
|
||||
- `app/template.tsx:29` e `app/globals.css:1038-1054`: troca de página por `<ViewTransition name="page">`, fade com escala.
|
||||
- `app/globals.css:755-767,963-977,1075`: entrada do título, das seções (`ds-scene-in`, `view()`) e dos títulos por palavra. Não há saída animada.
|
||||
- `app/components/WorkModalities.tsx:265,348`, `app/not-found.tsx:34-46` (404 fora da marca), `Faq.tsx:43`, `Header.tsx:247`, `showcase/ProjectLightbox.tsx:253`, `showcase/ProjectList.tsx:84`, `app/cases/_components/CaseCover.tsx:57`.
|
||||
- Máquina: Blender 5.2.2 LTS (`io_scene_gltf2`, Cycles com OptiX na RTX 5060 Ti 16 GB, addon `blender_mcp`), `ffmpeg`, `magick`. Não há outro programa de 3D instalado.
|
||||
|
||||
## 5. Desenho
|
||||
**Conceito.** Ideia: os blocos da marca são as peças com que se monta o site ou o sistema de cada cliente. Assinatura: os blocos se soltam e se remontam com a rolagem. Verbo: encaixar (chega rápido, assenta curto, sem quique). Quadro para compartilhar: a marca em porcelana com os quatro cubos verdes ao lado do título.
|
||||
|
||||
Decisões:
|
||||
- Um kit, quatro estados (`marca`, `tela`, `integracoes`, `dados`) com o mesmo número de blocos. As três legendas atuais ficam como estão.
|
||||
- Fonte é o script `3d/blocos.py` (mapas de blocos, chanfro, materiais, luz, exportação). O `.blend` é gerado em `3d/out/` (ignorado no git). Descartado: `.blend` modelado à mão e versionado, binário que ninguém revisa nem reconstrói.
|
||||
- Tempo real com uma `InstancedMesh` e matcaps renderizados dos materiais do Blender; a troca de estado é interpolada no vertex shader. Descartado: luz e sombra em tempo real (peso no celular) e vídeo de rolagem (o projeto já o tirou do celular).
|
||||
- `GLTFLoader` do próprio `three`, sem Draco nem meshopt (a geometria é um bloco só).
|
||||
- Três níveis: `full` (regra atual), `lite` (toque, ≥ 6 núcleos, ≥ 4 GB ou não informado), `static` (o resto, e qualquer aparelho que estoure o orçamento de quadro).
|
||||
- Movimento 2D só com CSS: `steps()` nos detalhes (cursor, cortina), ease-out no resto, `@starting-style` com `allow-discrete` para saída.
|
||||
- Peças secundárias pré-renderizadas no Cycles (vídeo curto com pôster), nunca WebGL por cartão.
|
||||
|
||||
Interfaces:
|
||||
```ts
|
||||
// public/3d/blocos.json
|
||||
{ n: number; estados: ["marca","tela","integracoes","dados"]; dados: number[] } // [estado][bloco] = x, y, z, escala, tom (0 porcelana, 1 lima)
|
||||
// lib/blocos.ts
|
||||
export function stateAt(p: number): { from: number; to: number; t: number }
|
||||
// lib/device.ts
|
||||
export type SceneTier = "full" | "lite" | "static";
|
||||
export function sceneTier(): SceneTier
|
||||
// app/components/cinematic/BlocosScene.tsx: mesmas props de HeroFieldSceneProps
|
||||
export function prepareBlocos(): Promise<void>
|
||||
export default function BlocosScene(props: HeroFieldSceneProps)
|
||||
```
|
||||
Mapa de arquivos. Criar: `3d/blocos.py`, `3d/pecas.py`, `public/3d/*`, `public/brand/pwa-*.png`, `scripts/build-icons.mjs`, `lib/blocos.ts`, `app/components/cinematic/BlocosScene.tsx`, `e2e/hero-3d.spec.ts`, `DESIGN.md`. Modificar: `app/manifest.ts`, `lib/device.ts`, `HeroField.tsx`, `Hero.tsx`, `app/globals.css`, `WorkModalities.tsx`, `not-found.tsx`, `ProjectList.tsx`, `CaseCover.tsx`. Remover no fim: `HeroFieldScene.tsx` e seus testes, `public/brand/icon-192.png`.
|
||||
|
||||
## 6. Restrições, riscos, suposições
|
||||
- Restrições: Next 16 com APIs diferentes (ler `node_modules/next/dist/docs/` antes de codar, `AGENTS.md`); trabalho em branch, nunca na `main`; nenhum servidor de desenvolvimento em segundo plano (`pnpm build && pnpm start`, encerrado ao fim).
|
||||
- Riscos: o CI roda sem GPU, então a cena em tempo real só se confere em captura local; o celular pode perder LCP (o orçamento de quadro e o CA6 seguram); sincronizar o checkout exige trocar o `origin`. Reverter é desfazer o branch; nada toca dados.
|
||||
- O worktree `~/.cache/security-fixes/portifolio-evandrodevbr` (branch `chore/security-updates-2026-11-09`) nasceu do mesmo commit velho e vai conflitar com o `main` real.
|
||||
- S1: o site é de venda (Persuade), com vitrine de portfólio.
|
||||
- S2: há um celular Android e um iPhone para a inspeção dos CA5 e CA1.
|
||||
- P1 (bloqueia T2): qual é a "logo nova"? O repositório e o site no ar já usam o símbolo de blocos nos ícones; nada mais novo foi encontrado. Resposta recomendada: é esse símbolo, e o que se vê no celular é ícone sem versão maskable mais o ícone guardado da instalação antiga.
|
||||
- P2 (bloqueia T7): no computador, a peça 3D ocupa a coluna da direita e as molduras de `HeroWork` saem do hero (os projetos reais começam uma rolagem abaixo)? Resposta recomendada: sim.
|
||||
|
||||
## 7. Microtasks
|
||||
|
||||
### Fase 0 · base e ícone
|
||||
### T0 · Sincronizar o checkout com o Gitea · N0
|
||||
- Serve: todos · Depende de: "ok" do usuário
|
||||
- Fazer: apagar `AGENTS.md` e `CLAUDE.md` não rastreados (idênticos aos do Gitea); `git remote set-url origin https://git.evandro.dev.br/evandro/portifolio-evandrodevbr.git`; `git pull --ff-only`; `git switch -c feat/blocos-3d-motion`; `pnpm install --frozen-lockfile`.
|
||||
- Verificar: `git log -1 --format=%h` → `4671837` ou mais novo; `pnpm test` verde.
|
||||
- [x] feito
|
||||
|
||||
### T1 · Escrever o teste do manifesto · N2
|
||||
- Serve: CA1 · Depende de: T0 · Arquivos: criar `app/manifest.test.ts` · Padrão: `app/robots.test.ts`
|
||||
- Fazer: afirmar `id === "/"` e exatamente estes ícones: `/brand/pwa-192.png` e `/brand/pwa-512.png` (`purpose: "any"`), `/brand/pwa-maskable-192.png` e `/brand/pwa-maskable-512.png` (`purpose: "maskable"`), todos `image/png`, e que cada arquivo existe em `public/`.
|
||||
- Verificar: `pnpm test -- app/manifest.test.ts` → falha (ainda não implementado).
|
||||
- [x] feito
|
||||
|
||||
### T2 · Gerar os ícones a partir do símbolo · N2
|
||||
- Serve: CA1 · Depende de: T1, bloqueada por P1 · Arquivos: criar `scripts/build-icons.mjs` e os 4 PNG em `public/brand/`
|
||||
- Fazer: com `sharp`, compor `public/brand/mark-dark.svg` sobre fundo `rgb(8,10,9)`. `any`: marca com 77% do lado. `maskable`: marca com 56% do lado, centrada. `--check` relê os PNG e falha se o tamanho ou a caixa da marca saírem do CA1.
|
||||
- Verificar: `node scripts/build-icons.mjs && node scripts/build-icons.mjs --check` → `OK`.
|
||||
- [x] feito
|
||||
|
||||
### T3 · Apontar o manifesto para os ícones novos · N2
|
||||
- Serve: CA1 · Depende de: T2 · Arquivos: modificar `app/manifest.ts:17-28`; remover `public/brand/icon-192.png`
|
||||
- Fazer: trocar a lista de ícones pela do T1 e acrescentar `id: "/"`.
|
||||
- Verificar: `pnpm test -- app/manifest.test.ts` → passa; `grep -rn "icon-192" app lib scripts e2e` → vazio.
|
||||
- [x] feito
|
||||
|
||||
### CP1 · após T0–T3 · N0
|
||||
- Verificar: `pnpm lint && pnpm typecheck && pnpm test && pnpm build`. Depois do deploy, reinstalar o app no Android e no iPhone (o iPhone só troca o ícone reinstalando) e conferir o ícone.
|
||||
|
||||
### Fase 1 · Blender e hero
|
||||
### T4 · Registrar conceito e movimento em DESIGN.md · N4
|
||||
- Serve: CA4, CA7, CA9 · Depende de: T0 · Arquivos: criar `DESIGN.md`
|
||||
- Fazer: cartão do conceito da seção 5, tokens que já existem em `globals.css`, durações e curvas do verbo "encaixar", os três níveis de aparelho.
|
||||
- Pronto quando: `impeccable context` lê o arquivo sem aviso.
|
||||
- [x] feito
|
||||
|
||||
### T5 · Montar o kit e os estados no Blender · N3
|
||||
- Serve: CA2 · Depende de: T0 · Arquivos: criar `3d/blocos.py`; `.gitignore` com `3d/out/`
|
||||
- Fazer: bloco chanfrado (2 segmentos); os 4 estados como mapas de blocos no próprio script, com `marca` derivado dos retângulos de `public/brand/mark-dark.svg`; exportar `bloco.glb` e `blocos.json` no formato da seção 5; afirmar no script o mesmo N em todos os estados e os limites do CA2.
|
||||
- Verificar: `blender -b --python 3d/blocos.py` → `OK blocos=<N> tris=<T>` e código 0.
|
||||
- [x] feito
|
||||
|
||||
### T6 · Acertar material, luz e pôster · N4
|
||||
- Serve: CA2, CA3 · Depende de: T5 · Arquivos: modificar `3d/blocos.py`
|
||||
- Fazer: porcelana fosca `#f4f3ef` e esmalte `#d5fa00` com emissão leve, luz de estúdio, Cycles com OptiX; renderizar os 2 matcaps (256 px) e `hero-poster.webp` (1200 px, fundo transparente). Ajuste fino pelo Blender MCP (`execute_blender_code`, `look`), com os valores copiados para o script.
|
||||
- Pronto quando: pôster sem ruído, com as proporções de `mark-dark.svg` e 4 cubos verdes; arquivos dentro do CA2.
|
||||
- [x] feito
|
||||
|
||||
### CP2 · após T4–T6 · N0
|
||||
- Verificar: mostrar o pôster ao usuário e seguir só com o "ok" dele.
|
||||
|
||||
### T7 · Compor o hero com o pôster · N4
|
||||
- Serve: CA3, CA6 · Depende de: T6, bloqueada por P2 · Arquivos: `Hero.tsx:64-66,143`, `app/globals.css` (bloco `.hero-*`), criar `e2e/hero-3d.spec.ts`
|
||||
- Fazer: pôster via `next/image` (lazy, prioridade baixa) no lugar de `.hero-glow::after`; no computador, coluna da direita; em 390 px, sem cobrir texto e com ≥ 96 px visíveis na primeira dobra. Teste e2e do CA3 primeiro.
|
||||
- Verificar: `pnpm test:e2e -- hero-3d` → passa; capturas em 1440×900 e 390×844 aprovadas pelo usuário.
|
||||
- [x] feito
|
||||
|
||||
### T8 · Classificar o aparelho em três níveis · N2
|
||||
- Serve: CA5 · Depende de: T0 · Arquivos: `lib/device.ts:42-53`, `lib/device.test.ts`
|
||||
- Fazer: `sceneTier()`: `static` com movimento reduzido, economia de dados, ≤ 4 núcleos ou memória informada < 4; senão `lite` com ponteiro `coarse` (exige ≥ 6 núcleos quando informado); senão `full` pela regra atual. Remover `isLowPowerDevice` e trocar a chamada em `HeroField.tsx:195`. Testes primeiro.
|
||||
- Verificar: `pnpm test -- lib/device.test.ts` → passa; `grep -rn isLowPowerDevice app lib` → vazio.
|
||||
- [x] feito
|
||||
|
||||
### T9 · Escrever o motor da cena de blocos · N3
|
||||
- Serve: CA4, CA5 · Depende de: T5, T6, T8 · Arquivos: criar `lib/blocos.ts`, `lib/blocos.test.ts`, `app/components/cinematic/BlocosScene.tsx` · Padrão: `HeroFieldScene.tsx:692-999`, `FramePulse.ts`
|
||||
- Fazer: `stateAt(p)` com os trechos de `hero-captions.ts` (teste primeiro); `InstancedMesh` com os estados em atributos por instância e mistura no vertex shader; entrada em 2,6 s; `full` segue `progressRef`, `lite` troca de estado a cada 4 s à vista; orçamento de 28 ms nos 60 primeiros quadros chama `onError` e grava na sessão.
|
||||
- Verificar: `pnpm test -- lib/blocos.test.ts BlocosScene` → passa.
|
||||
- [x] feito
|
||||
|
||||
### T10 · Ligar a cena ao hero · N2
|
||||
- Serve: CA3, CA4, CA5 · Depende de: T7, T9 · Arquivos: `HeroField.tsx`, `app/globals.css` (`.hero-field`)
|
||||
- Fazer: importar `BlocosScene` e `prepareBlocos` no lugar de `HeroFieldScene` e `prepareField`; `static` não carrega nada; `lite` sem `trackDive`; o canvas cobre o pôster com fade quando `data-ready`.
|
||||
- Verificar: `pnpm test -- HeroField` e `pnpm test:e2e -- hero-3d scroll` → passam.
|
||||
- [x] feito
|
||||
|
||||
### CP3 · após T7–T10 · N0
|
||||
- Verificar: suíte e build; captura local com GPU (CA4); celular real (CA5); `shoot.mjs` (CA6).
|
||||
|
||||
### T11 · Remover a cena de partículas · N2
|
||||
- Serve: CA11 · Depende de: CP3 · Arquivos: remover `HeroFieldScene.tsx` e `HeroFieldScene*.test.tsx`; ajustar o comentário de `hero-captions.ts`
|
||||
- Verificar: `grep -rn HeroFieldScene app lib` → vazio; `pnpm typecheck && pnpm test` → passam.
|
||||
- [x] feito
|
||||
|
||||
### Fase 2 · movimento 2D
|
||||
### T12 · Animar a escrita · N2
|
||||
- Serve: CA7 · Depende de: T4 · Arquivos: `app/globals.css` (junto de `:755-767`), `Hero.tsx:92`, `Section.tsx`
|
||||
- Fazer: classe `.type-line` (largura em `ch` com `steps(var(--n))`, cursor de bloco verde que pisca 3 vezes) nos rótulos mono; marca-texto do trecho verde do H1 por `scaleX` num pseudo-elemento; tudo dentro de `prefers-reduced-motion: no-preference`.
|
||||
- Verificar: `pnpm test -- Hero Section` → passa; inspeção com movimento reduzido: texto inteiro, sem cursor.
|
||||
- [x] feito
|
||||
|
||||
### T13 · Animar entrada e saída dos componentes · N2
|
||||
- Serve: CA8 · Depende de: T4 · Arquivos: `app/globals.css`, `app/components/showcase/lightbox.css`, `app/components/conversion.css`
|
||||
- Fazer: `@starting-style` com `transition-behavior: allow-discrete` no menu do celular, no `<dialog>` e no `::backdrop`, no aviso de cookies e no botão do WhatsApp; `interpolate-size: allow-keywords` com `::details-content` nas perguntas frequentes; 180 ms para sair, 280 ms para entrar.
|
||||
- Verificar: `pnpm test:e2e -- mobile-menu keyboard contact` → passam.
|
||||
- [x] feito
|
||||
|
||||
### T14 · Trocar de página com a cortina de blocos · N3
|
||||
- Serve: CA9 · Depende de: T4 · Arquivos: `app/globals.css:1038-1054`
|
||||
- Fazer: substituir `page-out` e `page-in` por uma máscara em grade que fecha e abre na diagonal em `steps()`, 240 ms para sair e 460 ms para entrar; manter o cabeçalho parado e o bloco de movimento reduzido.
|
||||
- Verificar: `pnpm test:e2e -- navigation routes` → passam; captura de 3 quadros da troca.
|
||||
- [x] feito
|
||||
|
||||
### T15 · Levar a capa do projeto ao topo do case · N3
|
||||
- Serve: CA9 · Depende de: T14 · Arquivos: `showcase/ProjectList.tsx:84`, `app/cases/_components/CaseCover.tsx:57` · Padrão: `app/template.tsx:29`
|
||||
- Fazer: envolver as duas imagens em `<ViewTransition name={"case-" + slug}>`, conforme a documentação do Next 16 instalada.
|
||||
- Verificar: `pnpm test -- ProjectList CaseCover` e `pnpm test:e2e -- navigation` → passam.
|
||||
- [x] feito
|
||||
|
||||
### CP4 · após T12–T15 · N0
|
||||
- Verificar: suíte, build e revisão do diff de CSS (só `transform`, `opacity`, `clip-path`, `mask`).
|
||||
|
||||
### Fase 3 · peças 3D
|
||||
### T16 · Renderizar as quatro peças · N3
|
||||
- Serve: CA10 · Depende de: T6 · Arquivos: criar `3d/pecas.py`
|
||||
- Fazer: com o kit do T5, montar `site` (vitrine com balão de conversa), `sistema` (grade que vira barras), `revisao` (escudo) e `404` (marca com um bloco caindo); loop de 3 s a 24 quadros por segundo no Cycles, 640 px, fundo `--ink`; `ffmpeg` gera `.webm` e `.mp4` mais pôster `.webp`; o script afirma os limites do CA10.
|
||||
- Verificar: `blender -b --python 3d/pecas.py` → `OK`.
|
||||
- [x] feito
|
||||
|
||||
### T17 · Usar as peças nos cartões de Soluções · N2
|
||||
- Serve: CA10 · Depende de: T16 · Arquivos: `WorkModalities.tsx:265,348` · Padrão: `showcase/useShowcaseMedia.ts`
|
||||
- Fazer: trocar a ilustração de cada cartão por `<video muted loop playsInline preload="none" poster>` que toca só à vista; com movimento reduzido ou economia de dados fica o pôster.
|
||||
- Verificar: `pnpm test -- WorkModalities` → passa.
|
||||
- [x] feito
|
||||
|
||||
### T18 · Refazer o 404 com a marca · N2
|
||||
- Serve: CA10 · Depende de: T16 · Arquivos: `app/not-found.tsx:34-46`, `app/not-found.test.tsx`
|
||||
- Fazer: tokens do site (`--ink`, `--slash`, fonte display) no lugar das cores `slate`, e o loop `404` no lugar do ícone `Code2`; manter título e links.
|
||||
- Verificar: `pnpm test -- not-found` → passa.
|
||||
- [x] feito
|
||||
|
||||
### CP5 · fechamento · N0
|
||||
- Verificar: cada CA da seção 3, com o resultado anotado aqui; impeccable critique e audit; jury; `Status: concluída`.
|
||||
|
||||
## 8. Registro
|
||||
2026-11-09 — spec escrita sobre o `main` do Gitea (`4671837`); o checkout local está 54 commits atrás.
|
||||
2026-11-09 — aprovada pelo usuário: P1 = o símbolo que o site já usa; P2 = a peça 3D ocupa a coluna da direita e as molduras de `HeroWork` saem do hero.
|
||||
2026-11-09 — T0 feito: `origin` no Gitea, branch `feat/blocos-3d-motion`, base `4671837`, 150 suítes verdes antes de qualquer mudança.
|
||||
2026-11-09 — CP1: `pnpm lint`, `pnpm typecheck`, `pnpm test` (151 suítes, 1654 testes) e `pnpm build` verdes; `build-icons.mjs --check` → `OK`. Falta só conferir o ícone no celular depois do deploy.
|
||||
2026-11-09 — T2: o `sharp` é resolvido a partir do pacote `next`, porque não é dependência direta do site.
|
||||
2026-11-09 — T8: `lib/device.paths.test.ts` também citava `isLowPowerDevice` e foi convertido.
|
||||
2026-11-09 — interface de `blocos.json` mudou de 5 para 7 números por bloco (`x, y, z, sx, sy, sz, tom`): a marca não cabe numa grade única, então cada retângulo dela é ladrilhado com blocos de escala própria e o contorno fica exato.
|
||||
2026-11-09 — o `next dev` reescreve uma linha do `AGENTS.md` (nível do título); não é mudança deste trabalho.
|
||||
2026-11-09 — o projeto não tem `PRODUCT.md`; o impeccable pede que ele seja escrito com o usuário antes do T7 (composição do hero).
|
||||
2026-11-09 — T5: N = 238 blocos, 25.704 triângulos no total; `bloco.glb` 2,7 KB, `blocos.json` 3 KB em gzip; mesma saída em duas execuções.
|
||||
2026-11-09 — T6: materiais, luz de três pontos, pôster (38 KB) e matcaps (2 a 3 KB) saem do próprio `3d/blocos.py` em Cycles com OptiX, em 6 s. O ajuste foi feito por renders sem interface, não pelo Blender MCP: dá o mesmo resultado e fica reproduzível. Os matcaps se chamam `matcap-porcelana.webp` e `matcap-lima.webp`.
|
||||
2026-11-09 — CA8 não fecha como escrito: o menu do celular, o aviso de cookies e o botão do WhatsApp são desmontados pelo React ao fechar (`Header.tsx`, `GoogleAnalytics.tsx`, `FloatingWhatsApp.tsx`), e CSS não anima a saída de elemento que some do DOM. Só o vídeo ampliado (`<dialog>`) e as perguntas frequentes (`<details>`) aceitam saída sem JavaScript. T13 fica à espera da decisão do usuário.
|
||||
2026-11-09 — T12: rótulos das seções digitados (`.ds-type`) e bloco verde que recolhe sobre as palavras em verde do H1 (`.hero-mark`). O rótulo do hero ficou de fora: ele usa Space Grotesk de propósito (não baixar fonte antes do LCP) e não é mono. `Reveal.test.tsx` também afirmava `ds-reveal` no rótulo e foi atualizado. Conferido no navegador em 1440×900 com as animações pausadas em 700, 1150 e 4000 ms.
|
||||
2026-11-09 — `public/3d` saiu da checagem do Biome (`biome.json`): `blocos.json` é gerado e compacto de propósito.
|
||||
2026-11-09 — aprovado pelo usuário: o pôster (CP2); entrada em todos os componentes e saída só onde o elemento continua no DOM (CA8 passa a valer assim); os três ganchos recomendados, em spec própria (`specs/2026-11-09-ganchos-e-ofertas.md`).
|
||||
2026-11-09 — `PRODUCT.md` escrito com as respostas do usuário; o site também serve a recrutadores e empresas de tecnologia.
|
||||
2026-11-09 — T7: a peça fica na coluna da direita de 1024 px em diante (até 46% da largura) e, abaixo disso, no canto, na linha do rótulo (112 a 200 px). As molduras de navegador e celular saíram do hero e a prop `workUrl` foi removida da cadeia; o cartão do site da SEV continua abaixo de 1024 px. A malha de pontos estática (`.hero-glow::after`) saiu. Conferido em 1440×900 e 390×844; a aprovação visual do usuário fica para o CP3, com o site rodando em local.
|
||||
2026-11-09 — T13: entrada por `@starting-style` no menu do celular, no aviso de cookies e no botão do WhatsApp; abrir e fechar animados nas perguntas frequentes. A saída do vídeo ampliado não aparece: `ShowcaseProvider.tsx:74-79` desmonta o `<dialog>` no `close`. A altura das perguntas frequentes é a única propriedade de leiaute animada.
|
||||
2026-11-09 — entrada da cena (CA4): como o pôster já mostra a marca montada, a cena assume com os blocos a até 1,2 unidade do lugar e assenta em 1,4 s, em vez de montar de longe em 2,6 s.
|
||||
2026-11-09 — Fase 3 (T16 a T18) adiada para depois da spec dos ganchos: os cartões de Soluções vão mudar de 3 para 4, e as peças dependem de quais cartões existem.
|
||||
2026-11-09 — T9: `lib/blocos.ts` (`parseBlocos`, `stateAt`, `createFrameBudget`, `framePiece`) e `BlocosScene.tsx` (uma chamada de desenho, estados em atributos por instância, matcaps do Blender). Conferido com GPU real (ANGLE Vulkan, RTX 5060 Ti): os quatro estados batem com as prévias do Blender.
|
||||
2026-11-09 — T10: `HeroField` trata os três níveis. No `full` o canvas cobre o palco e a peça descansa sobre o pôster (`placementRef`); no mergulho ela vai para a área útil entre o cabeçalho e as legendas (`stageRef`: 6% a 94% da largura, do cabeçalho + 24 px a 70% da altura). No `lite` o canvas ocupa só a caixa do pôster. `HeroField` passou para depois de `.hero-scrim`, para a peça não ficar sob o véu. `--hero-open` e a máscara do fundo ambiente saíram.
|
||||
2026-11-09 — o primeiro trecho de `stateAt` é 0,16 a 0,30 (era 0,10 a 0,26): os blocos só se mexem com o título quase apagado. Em p = 0,20 os blocos mais à esquerda encostam no fim do subtítulo, que está com 16% de opacidade.
|
||||
2026-11-09 — a caixa da marca na cena é 15,25 unidades (o enquadramento real do pôster), não 13. Canvas contra pôster em 1440×900: centro a 1,4%, tamanho a 3,1%; a porcelana do canvas sai 11% mais clara que a do pôster (vem do matcap, que é de uma esfera vista de frente).
|
||||
2026-11-09 — T11: `HeroFieldScene.tsx` e seus testes removidos.
|
||||
2026-11-09 — T14: cortina de blocos em 600 ms (`@property --page-wipe`, 8 colunas, `round(down, …, 12.5vw)`); a página que sai fica parada. `app/template.tsx` passou a usar `default="page"` em vez de `name="page"`: com nome comum, a página antiga pulava para o topo ao sair rolada. `jest.setup.ts` repassa os filhos de `ViewTransition`.
|
||||
2026-11-09 — CP3 parcial: captura com GPU em 1440×900 nos progressos 0, 0,23, 0,35, 0,63 e 0,93 vista e aprovada pela sessão principal. Faltam celular real (CA5) e a medição de LCP com rede limitada (CA6).
|
||||
2026-11-09 — T15: a capa viaja com o nome `case-<id>` (`cover-transition.ts`) na lista da home no celular, em `/cases` e, no computador, a partir do projeto em exibição na vitrine (`PinnedReel`, pelo pôster e não pelo vídeo). No primeiro quadro há um pequeno salto, porque o pôster mostra o topo do site e o vídeo estava no meio da rolagem.
|
||||
2026-11-09 — CP3 e CP4: `biome check`, `pnpm typecheck`, `pnpm test` (150 suítes, 1612 testes), `pnpm build` e `pnpm test:e2e` (53 testes, com axe e os três de `hero-3d`) verdes sobre `pnpm start -p 3001`, encerrado ao fim. Para rodar a suíte e2e foi baixado o Chromium do Playwright (`playwright install chromium`).
|
||||
2026-11-09 — CA6 medido em 390×844 com CPU 4× mais lenta e rede de 1,6 Mbps: LCP em 812 ms, no H1; CLS 0. O `shoot.mjs` do awwwards não roda sem o pacote `playwright`; a medição foi feita com um script descartável sobre `@playwright/test`.
|
||||
2026-11-09 — pendente: celular real (CA5 e ícone do CA1), aprovação visual do usuário da composição do hero, Fase 3 e fechamento (CP5).
|
||||
2026-11-09 — Fase 3 revista (T16 e T17): as peças em loop vão para onde não há prova real para mostrar. São três: `sistema` (no cartão, no lugar do esquema em CSS), `whatsapp` (cartão novo) e `404`. O cartão do site continua com a captura do site real e o da revisão com o trecho de relatório: trocar trabalho real por escultura iria contra o `PRODUCT.md`. A T17 foi feita junto com a T4 da spec dos ganchos.
|
||||
2026-11-09 — T16: `3d/pecas.py` gera `peca-sistema`, `peca-whatsapp` e `peca-404` (webm de 18 a 51 KB, mp4 de 21 a 49 KB, pôster de 5 a 7 KB; 640×400, 3 s, sem áudio). Em `3d/blocos.py` a luz virou `luz_de_estudio()` e `main()` ficou sob `if __name__ == "__main__"`.
|
||||
2026-11-09 — T17: `OfferVideo.tsx` toca o loop só à vista e fica no pôster com movimento reduzido ou economia de dados; usado nos cartões `sistema` e `whatsapp`.
|
||||
2026-11-09 — T18: 404 com os tokens do site, os botões de `button-styles.ts` e o loop da marca; sem ícones.
|
||||
2026-11-09 — o matcap da porcelana sai com 0,3 ponto de exposição a menos: o centro foi de 222 para 203, contra 201 na frente dos blocos do pôster.
|
||||
2026-11-09 — Impeccable atualizado para 4.8.2 pelo gate com `--force`, depois da revisão dos achados (1 alto e 29 médios, todos lidos como falsos positivos). Cópia da 4.7.0 em `~/tmp/impeccable-4.7.0-backup`.
|
||||
2026-11-09 — CP5, fechamento. Resultado por critério:
|
||||
- CA1: passa nos testes e no `--check`; falta ver o ícone instalado num celular depois do deploy.
|
||||
- CA2: passa (`OK blocos=238 tris=25704`, todos os arquivos dentro dos limites).
|
||||
- CA3: passa (`e2e/hero-3d.spec.ts`, 3 testes).
|
||||
- CA4: passa, com a entrada revista (assenta em 1,4 s); visto com GPU em 1440×900.
|
||||
- CA5: passa nos testes e em celular emulado com GPU; falta celular real.
|
||||
- CA6: passa. LCP no título em 340 ms (computador com GPU), 116 ms (sem GPU) e 868 ms (celular, CPU 4× mais lenta e rede de 1,6 Mbps); CLS 0.
|
||||
- CA7: passa.
|
||||
- CA8: passa como revisto (saída só nas perguntas frequentes).
|
||||
- CA9: passa; medido à parte: a cortina roda de 150 a 850 ms em home → /cases, home → /servicos e /cases → case.
|
||||
- CA10: passa, com três peças em vez de quatro.
|
||||
- CA11: NÃO passa. Suíte toda verde (biome, tipos, 152 suítes e 1631 testes, build, 57 testes e2e), mas a crítica do Impeccable deu 29/40 com Consistência em 2 (o botão "Falar sobre meu projeto" leva a lugares diferentes, o item "IA" do menu é ambíguo, o verde do WhatsApp não tem token), a auditoria deu 15/20 e o crítico cego do jury deu 6,4 (Design 6, Usabilidade 7, Criatividade 6, Conteúdo 7). Relatórios em `~/tmp/avaliacao/impeccable.md` e `~/tmp/.jury/127.0.0.1-3001/scorecard.md`. Diagnóstico rápido das regras de design: 9 (falha só nos rótulos em caixa alta).
|
||||
2026-11-09 — achado da crítica corrigido: em computador com GPU o LCP era a imagem do pôster aos 2,3 s, porque a troca de opacidade ao esconder o pôster o repintava. O pôster agora carrega junto com a página e some por `visibility`; o LCP voltou ao título.
|
||||
2026-11-09 — 404 alinhado à grade do site (`ds-section-shell`) e com a peça menor no celular, para os botões não ficarem sob o botão do WhatsApp.
|
||||
Reference in new issue
Block a user