diff --git a/docs/changelog/2026-09-16-c176.md b/docs/changelog/2026-09-16-c176.md new file mode 100644 index 00000000..a3f4bb7d --- /dev/null +++ b/docs/changelog/2026-09-16-c176.md @@ -0,0 +1,5 @@ +feat(C176): página pública do corte — revisão de design (superfície neutra, vídeo herói, WhatsApp-first no mobile) + +Redesenha `/corte/` preservando o contrato C167 (rota, `noindex`, metadata). O "fundo colorido" era o gradiente do `body` herdado de `:root` — o segmento não tinha `data-theme` nem layout; um novo `src/app/(frontend)/corte/layout.tsx` espelha `[type]/layout.tsx` (`data-theme="editorial"` + `h-dvh overflow-y-auto bg-background`), consertando de uma vez a page e o `not-found` e provendo o scroll interno (sticky do `SiteHeader`). A page porta o gate aprovado (`docs/plans/corte-pagina-publica-design-ui-design.html`): vídeo herói (full-bleed no mobile, arredondado no desktop; `object-cover` faz o poster 4:3 preencher o quadro 16:9), título alinhado à esquerda, meta `tipo · data · duração` (relógio `02:14`, literal do gate), descrição com medida de prosa controlada e contraste AA, kit de compartilhamento responsivo — WhatsApp primário full-width acima da descrição no mobile (alvo 48 px) e kit inline de 3 botões no desktop (44 px) —, ações secundárias em grade de 2 colunas no mobile, link da sessão no YouTube inline na meta (desktop) e avulso (mobile), e rodapé com crédito CC BY + nota de não listagem. Estado indisponível redesenhado (ícone, copy e botão "Voltar ao início") e centralizado. `SpeechCutShareActions.tsx` extraiu `SpeechCutWhatsAppButton`/`SpeechCutDownloadButton` (o dono do URL/rótulo do WhatsApp segue `buildSpeechCutShare`) e ganhou `controlClassName` opcional — acervo/biblioteca continuam idênticos. Guard e2e novo: o HTML servido carrega `data-theme="editorial"` (regressão do fundo tintado). `campaignSpeechCut` (5) e `campaignSpeechAcervo` (11) verdes locais; `pnpm gate:fast` verde. Sem migration/schema/access/Consent. + +Closes #1086 diff --git a/docs/plans/corte-pagina-publica-design-impl.md b/docs/plans/corte-pagina-publica-design-impl.md new file mode 100644 index 00000000..0a796f91 --- /dev/null +++ b/docs/plans/corte-pagina-publica-design-impl.md @@ -0,0 +1,119 @@ +# Impl: Página pública do corte: revisão de design + +Status: em execução +Atualizado em: 2026-09-16 +Issue: #1086 +Intenção: docs/plans/corte-pagina-publica-design.md +Appetite restante: herdado (~1–1,5 dia eng, uma página). Sem inflar. + +## Leitura da intenção + +- **Outcome:** a página `/corte/` passa a ler como peça cuidada e compartilhável — vídeo herói, título alinhado à esquerda, meta/descrição em superfície neutra (zero fundo tintado), WhatsApp-first no celular, e o estado indisponível igualmente bonito — sem tocar rota, `noindex` ou metadata (C167). +- **O que NÃO negociar:** rota `/corte/` e `robots: { index: false, follow: false }` inalterados; MP4 armazenado como player (nunca YouTube/VOD); crédito `Fonte: Câmara dos Deputados · CC BY 4.0`; mesma tela para id desconhecido e despublicado (não revela existência); comportamento do acervo/biblioteca que reusa `SpeechCutShareActions` idêntico. +- **O que reavaliar (hipóteses da intenção):** + - _"fundo colorido por theme/`editorial-bg`/gradiente"_ — confirmado: não é `editorial-bg`; é o gradiente global do `body` (`src/app/(frontend)/styles.css:1025-1026`) + `:root --background:#ae1603` (`styles.css:124`) que `/corte` herda por **não ter `data-theme` nem layout**. O conserto é local, não na paleta global. + - _"SiteHeader pode precisar mexer"_ — não precisa: `src/components/SiteHeader.tsx` já é `sticky` e pronto para viver dentro de um scroll container (`[type]/layout.tsx` é o precedente). Reusar como está. + +## Abordagem recomendada + +```mermaid +flowchart LR + L["corte/layout.tsx (novo)
data-theme=editorial
h-dvh overflow-y-auto bg-background"] --> P["corte/[id]/page.tsx
composição responsiva"] + L --> N["corte/not-found.tsx
estado indisponível"] + P --> S["SpeechCutShareActions.tsx
exporta WhatsApp + Download button"] + S --> C["CopyLinkButton (reuso)"] + P --> SC["buildSpeechCutShare / speechCutPublicPath (reuso)"] +``` + +**Opções consideradas:** A) layout de segmento `corte/layout.tsx` + composição responsiva na page reusando botões extraídos do dono; B) editar o gradiente do `body` em `styles.css`; C) wrapper `data-theme` só dentro de `page.tsx`. +**Recomendação:** **A** — espelha `src/app/(frontend)/[type]/layout.tsx:12-20` (mesma classe `data-theme="editorial" h-dvh w-full overflow-y-auto bg-background text-foreground`), conserta **de uma vez** a page e `corte/not-found.tsx` (o `not-found` do segmento é envolvido pelo layout do segmento), usa os tokens neutros de `styles.css:143-162` (`--background:#f6f4f3`), e mantém o conserto escopado à rota. O `h-screen`/`overflow-hidden` do root (`(frontend)/layout.tsx:76,80`) já exige que a página dona do viewport forneça o scroll interno — hoje `/corte` não fornece. +**Rejeitadas:** **B** porque o gradiente do `body` sustenta as páginas públicas escuras (home/`petition`/`campaign-site`) — mexer nele vaza para fora do escopo; **C** porque um wrapper na page não cobre `corte/not-found.tsx` e duplica a responsabilidade de shell que o App Router já oferece no layout; ambas trocam um conserto de 1 arquivo por risco de regressão global. + +### Decisão: ordem responsiva do share kit (WhatsApp acima da descrição no mobile, kit inline abaixo no desktop) + +**Opções consideradas:** A) extrair/exportar `SpeechCutWhatsAppButton` (+`SpeechCutDownloadButton`) de `src/components/SpeechCutShareActions.tsx` e o próprio componente passar a consumi-los, compondo os marcadores responsivos na page; B) duplicar o kit inteiro em `hidden lg:flex` / `lg:hidden` sem extração; C) reordenar via utilitários `order-*` com um único kit. +**Recomendação:** **A** — mantém um único dono do URL/rótulo do WhatsApp (`buildSpeechCutShare`, já em `SpeechCutShareActions.tsx:37`) e do download do MP4, com a page só posicionando blocos: `sm:hidden` WhatsApp `h-12 w-full` logo após a meta → descrição (instância única) → `sm:hidden` grade 2 colunas (`CopyLinkButton` + `SpeechCutDownloadButton`) → `hidden sm:flex` `SpeechCutShareActions primary="whatsapp"` → YouTube inline na meta no desktop (`hidden sm:inline`) e link avulso no mobile (`sm:hidden`). O limite é `sm` (640 px) porque o gate tem exatamente duas cenas (390 e 1280): uma única fronteira realiza as duas sem inventar um estado intermédio não desenhado (o plano dizia `lg`; ajuste de execução). A API pública do componente e o markup do acervo/biblioteca permanecem idênticos (o acervo usa `primary="copy"`; nada muda em `SpeechCutResultCard.tsx:49` e `SpeechCutLibraryShareActions.tsx:25`). +**Rejeitadas:** **B** porque duplica fonte de verdade do CTA (label/URL) em dois lugares — DRY de conhecimento; **C** porque o agrupamento difere entre viewports (mobile = WhatsApp primário + grade de secundários; desktop = kit de 3 inline) e não há como "fatiar" um componente ao redor da descrição com `order-*`; **D** (prop nova `variant="mobile"` no componente) porque cria um gêmeo dentro do dono e arrisca o acervo — contra "edit the owner, don't twin". Não criar arquivo novo `src/components/*.tsx`: um componente novo fica fora do manifest e cai no fallback smoke (`scripts/lib/e2e-affected-manifest.mjs:320-325`). + +### Decisão: mapa de tokens do artefato → tokens do app (sob `data-theme="editorial"`) + +**Opções consideradas:** A) portar classes literais do artefato (`text-stone-*`, `bg-white`, `border-stone-300`); B) portar para tokens semânticos do app. +**Recomendação:** **B**, token-level (o artefato já usa `text-muted-foreground`/`border-border` na meta/footer, então o port é o que ele mesmo declara): +`text-stone-950` → `text-foreground`; `text-stone-700`/`text-stone-800` → `text-foreground/90`; `text-stone-600`/`text-stone-500` → `text-muted-foreground`; `bg-white` em controles → `bg-card`; superfície da página (`
`) → `bg-background`; `border-stone-300` → `border-border`; `bg-stone-100 text-stone-600` (chip indisponível) → `bg-muted text-muted-foreground`. O `