# 08 · Pipeline de produção · HTML+Chrome headless

Pipeline oficial do calendário Maio 2026. Todos os 97 PNGs finais foram gerados por esse fluxo. Use este arquivo como referência para regenerar qualquer post, adicionar novos, ou expandir o pipeline.

## 1. Stack

| Camada | Ferramenta | Versão/nota |
|---|---|---|
| Layout | HTML5 + CSS3 (Fraunces + Host Grotesk via Google Fonts + `@font-face` local) | — |
| Automação | Python 3.11+ | `helpers.py` + `generate*.py` |
| Rasterização | Chrome headless via `subprocess` | viewport fixo, `--screenshot`, `--window-size` |
| Pós-rasterização | `sips` / `imagemagick` opcional | redução para 1080 do feed |
| PDF consolidado | Chrome headless `--print-to-pdf` + `build_apresentacao.py` | Neo-Swiss A4 landscape |

## 2. Localização do pipeline

Pasta raiz:
```
/Users/matheusfilipe/SQUAD FILM/Clientes/LacquaMarina/B-INSTAGRAM/Calendario-Maio-2026/_HTML_source/
```

Arquivos:
```
_HTML_source/
├── helpers.py                   — funções compartilhadas (fonts, colors, snippet headers)
├── generate.py                  — renderização base (posts 01-15)
├── generate2.py                 — renderização em massa (posts 16-27)
├── generate3.py                 — renderização alternativa / retrabalho
├── build_apresentacao.py        — PDF Neo-Swiss consolidado 32p
├── *.html                       — fontes HTML de cada post (legacy antes de gerar)
└── tokens.css                   — cores/fontes/spacing (compartilhado)
```

Output:
```
Calendario-Maio-2026/NN_DD-maio_PILAR_formato_ratio/
├── cover.png | slide_01.png     — capa
├── slide_02.png ... slide_N.png — slides adicionais
└── *.html                       — HTML fonte do post (opcional)
```

## 3. Fluxo canônico por post

### 3.1 Single foto (4:5)

1. Definir briefing (calendário + pilar + tom).
2. Escrever **Prompt fotografia** (Bloco A) → Nano Banana/Flux → 2160×2700.
3. Upscale Topaz/Magnific se abaixo de 2160 em largura.
4. Grading final Lightroom — preset Portra 400 + roll-off Classic Chrome.
5. Escrever **Composição do post** (Bloco B) como HTML.
6. Renderizar via Chrome headless em 1080×1350 @ 2x (output 2160×2700).
7. Downsample para 1080×1350 final (PNG).
8. Rodar QA 7 pontos.
9. Salvar em `NN_DD-maio_PILAR_foto_4x5/cover.png`.

### 3.2 Carrossel (4:5)

1. Definir briefing da capa + N slides internos.
2. Gerar N+1 fotos (ou reaproveitar banco visual).
3. Montar HTML de cada slide (capa com header-strip Neo-Swiss, internos com folio).
4. Renderizar cada slide separadamente (mesmo viewport 1080×1350).
5. Output: `slide_01.png` (capa) + `slide_02.png` ... `slide_N.png`.
6. Validar consistência de header-strip + folio + tipografia em todos os slides.

### 3.3 Stories (9:16, múltiplos frames)

1. Viewport 1080×1920.
2. 4 a 12 frames por post.
3. HTML com layout vertical: foto full-bleed ou foto + overlay cream inferior.
4. Salvar como `frame_01.png` ... `frame_NN.png`.

### 3.4 Reel thumbnail (9:16)

1. Viewport 1080×1920.
2. Apenas 1 frame (capa do vídeo).
3. Vídeo separado (MP4) produzido em After Effects / Premiere.

### 3.5 Quote card (9:16)

1. Viewport 1080×1920.
2. Fundo dark `#0E1518` ou cream `#EFE9DD`.
3. Headline Fraunces 72pt + attribution Host Grotesk 14pt caps.

## 4. Comando Chrome headless padrão

```bash
/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome \
  --headless \
  --hide-scrollbars \
  --disable-gpu \
  --force-device-scale-factor=2 \
  --window-size=1080,1350 \
  --screenshot="/caminho/output.png" \
  "file:///caminho/post.html"
```

Para 9:16:
```
--window-size=1080,1920
```

Escala 2x gera 2160×2700 ou 2160×3840 — depois downsampling se necessário.

## 5. Fontes via HTML

```html
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Fraunces:ital,opsz,wght@0,9..144,300..700;1,9..144,300..700&family=Host+Grotesk:wght@400;500;600&display=swap" rel="stylesheet">
```

Para PDF (Britti permitida), usar `@font-face` apontando para o arquivo local na pasta `/fonts/`.

## 6. CSS base (trecho canônico)

```css
:root {
  --cream:  #EFE9DD;
  --ink:    #0A0A0A;
  --dark:   #0E1518;
  --teal:   #1F3A4D;
  --terra:  #B56B3F;
  --gold:   #D4A04C;
  --gold-weak: rgba(212, 160, 76, 0.35);
  --overlay-dark: rgba(10, 10, 10, 0.55);

  --f-display: 'Fraunces', 'Times New Roman', serif;
  --f-body:    'Host Grotesk', 'Inter', sans-serif;
}

html, body {
  margin: 0;
  padding: 0;
  font-family: var(--f-body);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

.fraunces-italic {
  font-family: var(--f-display);
  font-style: italic;
  font-weight: 400;
  letter-spacing: 0;
}
```

## 7. Header-strip Neo-Swiss (snippet)

```html
<header class="header-strip">
  <div class="h-left"><img src="./brand/squad-mark-CREAM.svg" alt="Squad Film" height="20"></div>
  <div class="h-center">L'ACQUA · MAIO 2026</div>
  <div class="h-right"><img src="./brand/lacqua-wordmark-CREAM.svg" alt="L'Acqua Marina" height="36"></div>
</header>
```

```css
.header-strip {
  display: grid;
  grid-template-columns: 1fr 2fr 1fr;
  align-items: center;
  padding: 18px 48px;
  border-bottom: 0.5px solid var(--gold-weak);
  color: var(--cream);
  font-family: var(--f-body);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.12em;
  text-transform: uppercase;
}

.header-strip .h-left  { justify-self: start; }
.header-strip .h-right { justify-self: end;   }
.header-strip .h-center{ justify-self: center; color: var(--gold); }
```

## 8. Folio Neo-Swiss

```html
<span class="folio"><span class="folio-num">04</span>/27</span>
```

```css
.folio {
  font-family: var(--f-body);
  font-weight: 400;
  font-size: 9px;
  letter-spacing: 0.12em;
  color: var(--cream);
  opacity: 0.7;
}
.folio-num { color: var(--gold); opacity: 1; }
```

## 9. Renderização em batch

Exemplo Python (`generate.py`, simplificado):

```python
import subprocess
from pathlib import Path

POSTS = [
  {"slug": "01_01-maio_LOCALIZACAO_foto_4x5", "html": "post_01.html", "w": 1080, "h": 1350, "out": "cover.png"},
  {"slug": "03_03-maio_PROVA_quote_9x16",     "html": "post_03.html", "w": 1080, "h": 1920, "out": "cover.png"},
  # ... 27 entradas
]

CHROME = "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"
BASE = Path("/Users/matheusfilipe/SQUAD FILM/Clientes/LacquaMarina/B-INSTAGRAM/Calendario-Maio-2026")

for p in POSTS:
    out = BASE / p["slug"] / p["out"]
    out.parent.mkdir(parents=True, exist_ok=True)
    subprocess.run([
      CHROME, "--headless", "--hide-scrollbars", "--disable-gpu",
      "--force-device-scale-factor=2",
      f"--window-size={p['w']},{p['h']}",
      f"--screenshot={out}",
      f"file://{BASE.parent / '_HTML_source' / p['html']}"
    ], check=True)
```

## 10. PDF consolidado (Neo-Swiss 32p)

`build_apresentacao.py` gera o PDF oficial `LacquaMarina-Calendario-Maio-2026-Apresentacao.pdf` (5.0 MB, 32 páginas, A4 landscape). Fluxo:

1. Montar HTML mestre com todas as páginas (capa + índice + 27 posts + contracapa + assinatura).
2. Chrome headless `--print-to-pdf` + `--print-to-pdf-no-header`.
3. Validar 32 páginas, margens ≥ 96px, header-strip em todas, folio em todas.

## 11. Regras críticas do pipeline

- **Sempre renderizar em 2x.** Força `--force-device-scale-factor=2`. Depois downsampling se precisar.
- **Fontes locais em PDF.** Google Fonts offline = fallback Times New Roman. Validar antes.
- **Zero overflow.** Overflow horizontal = scroll bar = artefato na captura. Forçar `overflow: hidden` no body.
- **Cores em HEX.** Zero RGB em código. Zero HSL. Zero nome de cor (`white`/`black`).
- **Imagens nunca embutidas base64.** Sempre paths relativos `./img/foto.jpg`.
- **Validar antes de enviar:** abrir PDF no Preview, conferir overlap, espaçamento, fontes. `sips` pra medir pixels se necessário.

## 12. Regeneração de 1 post

Fluxo mínimo:
```bash
cd /Users/matheusfilipe/SQUAD\ FILM/Clientes/LacquaMarina/B-INSTAGRAM/Calendario-Maio-2026/_HTML_source
python3 generate.py --only 04  # regenera apenas post 04
```

Fluxo total:
```bash
python3 generate.py && python3 generate2.py && python3 build_apresentacao.py
```

## 13. Debug comum

| Sintoma | Causa provável | Fix |
|---|---|---|
| Fonte Times em vez de Fraunces | `<link>` não carregou | Checar conexão, ou usar `@font-face` local |
| Header-strip quebra em 2 linhas | label longo ou viewport estreito | Reduzir label ou `font-size` |
| Foto esticada/cortada | `object-fit: contain` em vez de `cover` | Trocar para `cover` + `object-position: center` |
| Emblema gold some em cream | `fill="currentColor"` + text-color `cream` | Forçar `fill="#D4A04C"` inline |
| PDF com header duplicado | `--print-to-pdf` sem `--no-pdf-header-footer` | Adicionar flag |
| Texto com antialiasing ruim | Falta `-webkit-font-smoothing: antialiased` | Adicionar no body |

Ver também `reference/09-arquivos-e-paths.md` para mapa completo de arquivos.
