diff --git a/.gitignore b/.gitignore index 8435f118..19268987 100644 --- a/.gitignore +++ b/.gitignore @@ -111,6 +111,10 @@ next-env.d.ts # workstation, nunca versionado (o deploy npm não o toca). /scripts/reels/.venv/ +# C208 — artes da Rádio Jorge Solla 1313 (zeno.fm): o que é versionado mora em +# public/campaign-kit/radio/; aqui ficam só rascunhos/previews locais. +/data/radio-artes/ + # Local-only sensitive material (agents may read; never commit content) /private/** !/private/README.md diff --git a/docs/changelog/2026-09-21-c208.md b/docs/changelog/2026-09-21-c208.md new file mode 100644 index 00000000..38eb8056 --- /dev/null +++ b/docs/changelog/2026-09-21-c208.md @@ -0,0 +1 @@ +**C208 — Artes da Rádio Jorge Solla 1313: logo escolhida, capa e website card (2026-09-21):** a estação no zeno.fm (que estava com os três campos de imagem ocupados pela arte da **carreata** eventual) ganha identidade geral, decidida com o humano em conversa e agora registrada no repo. Os ativos versionados vivem em `public/campaign-kit/radio/` e são reproduzidos por `scripts/build-radio-artes.mjs`: **logo** = o "O" do SOLLA da marca positiva em fundo branco (recorte do artboard oficial `public/Prancheta 1@3x.png` — letra com a estrela vazada e o rastro verde/amarelo/azul — em PNG opaco para a estrela seguir branca em qualquer fundo; o zeno exibe o logo em círculo de 152²), **capa** = `RÁDIO` (Brexter Bold) + **marca vertical** oficial (o "1313" embaixo do JORGE SOLLA, com o slogan) + o candidato à direita em conjunto centralizado (ilustração como padrão, `capa-com-foto.png` como alternativa), e **station website card** em 1200×720 no mesmo sistema de fundo branco (nome + assinatura). O contrato de cada campo foi medido na própria página da estação e virou guarda do builder (falha fechado): a capa 2048×1024 tem de manter todo o conteúdo na **faixa segura central 2048×512** (no desktop o zeno corta 4:1) e deixar branca a área do logo circular que ele sobrepõe no canto inferior esquerdo (x 60..380, y ≥700) — a ilustração (500 px) e a marca entram inteiras em y 262..762; o card é exibido em quadrado (`aspect-square`), então tudo vive no quadrado central 720×720. A **ilustração em alta** (`radio/ilustracao-solla-1313.png`, 769×1122 RGBA) sai da ilustração do card de foto de perfil (`public/cards/photo-portrait-frame.png`, que só existia a ~255 px): recorte nativo, 4× por IA (Real-ESRGAN ncnn-vulkan, modelo `realesrgan-x4plus-anime`) e remoção de fundo por flood fill com abertura morfológica (a faixa do frame é o vermelho legado `#b72031`, não a cor do kit) — receita no cabeçalho do builder. As **logos finalistas** ficam em `radio/finalistas/` (arquivo da decisão): "1313" no vermelho, marca completa positiva no branco e o "O" branco no vermelho. O mapa de uso entra na seção nova do `public/campaign-kit/README.md` (a marca continua com dono único no README + manual; nada de recópia de paleta) e o builder depende de Brexter e Arimo no fontconfig local, com instruções no cabeçalho. Sem UI do app, sem migration, sem Consent/access, sem URL pública nova — são assets estáticos + um builder de script. diff --git a/docs/plans/artes-radio-jorge-solla-1313.md b/docs/plans/artes-radio-jorge-solla-1313.md new file mode 100644 index 00000000..2c5ec536 --- /dev/null +++ b/docs/plans/artes-radio-jorge-solla-1313.md @@ -0,0 +1,103 @@ +# Artes da Rádio Jorge Solla 1313 (zeno.fm) — logo, capa e website card + +Status: entregue (2026-09-21) +Atualizado em: 2026-09-21 — as-built: ativos versionados em `public/campaign-kit/radio/` (logo, capa, website card, ilustração em alta e as três logos finalistas), gerados por `scripts/build-radio-artes.mjs`; mapa de uso no `public/campaign-kit/README.md`. +Issue: #1241 (C208) · Design UI: N/A — peças estáticas aprovadas pelo humano no fluxo (não é superfície de app) +Impeccable: A — assets e builder; nenhuma UI do app tocada +Appetite: ~0,5 dia eng; 3 ativos + finalistas + builder; sem migration +Responsável: — + +## Intenção + +A estação [`Rádio Jorge Solla 1313`](https://zeno.fm/radio/jorge-solla-1313/) no +zeno.fm estava com os três campos de imagem ocupados pela arte da **carreata** +(eventual). O humano pediu artes **gerais** da rádio — logo, capa e station +website card — para o painel do zeno, com a marca oficial do kit 1313, e depois +decidiu o sistema visual em conversa: a **logo escolhida é o "O" do SOLLA** +(letra oficial com a estrela vazada e o rastro verde/amarelo/azul) em fundo +branco; a capa usa a marca vertical ("1313" embaixo do "JORGE SOLLA", com o +slogan) centrada com o candidato à direita; o card segue o mesmo fundo branco. +Esta entrega **registra** no repo os ativos aprovados, as logos finalistas, a +ilustração do candidato em alta e o builder que os reproduz. + +## Persona e fluxo + +- **Persona / contexto:** quem opera a estação no painel do zeno (comunicação) e + quem produz peças da campanha (agentes de design, reels, dossiês). +- **Job principal:** achar o ativo oficial certo e saber o contrato de cada + campo do zeno (corte circular do logo, faixa segura da capa, corte quadrado do + card) sem adivinhar. +- **Fluxo desejado:** abre o README do kit → acha a seção da rádio → pega o + arquivo do campo → sobe no painel. Se precisar mudar, roda o builder. + +## Contrato do zeno (medido na página da estação) + +| Campo | Arquivo | Como o zeno exibe | +| -------------------- | ------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Logo | `radio/radio-jorge-solla-1313-logo.png` (1024², fundo branco opaco) | círculo pequeno (CDN entrega 152², `rounded-full`); o desenho tem de ler dentro do círculo e em ~40 px | +| Cover Image | `radio/radio-jorge-solla-1313-capa.png` (2048×1024) | 2:1 no celular, 3:1 e 4:1 no desktop (`object-cover` central) → todo o conteúdo na **faixa segura central 2048×512**; o logo circular sobreposto no desktop (x 60..380, y ≥700) tem de achar a área branca | +| Station Website Card | `radio/radio-jorge-solla-1313-website-card.png` (1200×720) | quadrado (`aspect-square`, corte central 720×720) → o conteúdo vive nesse quadrado | + +- Limites de arquivo: logo ≥500² e ≤5 MB; capa ≥2048×1024 e ≤5 MB; card ≥500×300 + e ≤500 KB. Todos os três ficam muito abaixo (69 KB / 325 KB / 64 KB). +- O builder **revalida** os contratos (faixa segura, área do logo do zeno e + quadrado central do card) e falha fechado se algo sair do lugar. + +## Decisões (2026-09-21) + +- **Logo:** o "O" do SOLLA da marca positiva em fundo branco — recorte do + artboard oficial `public/Prancheta 1@3x.png` (mesma paleta do kit: + `#e4102f`/`#184e92`/`#009647`/`#ffeb00` conferidos), em PNG opaco para a + estrela continuar branca sobre qualquer fundo. Finalistas arquivadas em + `radio/finalistas/`: "1313" no vermelho (`numero-negativo`), marca completa + positiva no branco e o "O" branco no vermelho. +- **Capa:** `RÁDIO` (Brexter Bold, vermelho) + marca vertical + candidato à + direita, em conjunto centralizado; a ilustração (padrão) e a foto + (`capa-com-foto.png`) entram inteiras na faixa segura (y 262..762). +- **Sem repetir símbolo:** onde o "JORGE SOLLA" estilizado já está, não entra o + "O" como badge — a estrela já vive dentro do próprio lockup. +- **Ilustração em alta:** a do card de foto de perfil + (`public/cards/photo-portrait-frame.png`) só existia a ~255 px; virou + 769×1122 por recorte + 4× por IA (Real-ESRGAN ncnn-vulkan, modelo + `realesrgan-x4plus-anime`) + remoção de fundo por flood fill + abertura + morfológica (a faixa do frame é `#b72031`, não a cor do kit). Receita no + cabeçalho do builder; a ilustração final é entrada versionada. + +## Objetivo e aceite + +- `public/campaign-kit/radio/` com logo, capa, website card, `capa-com-foto.png` + (alternativa) e `ilustracao-solla-1313.png` (alta, RGBA), mais as três + finalistas em `radio/finalistas/`. +- `public/campaign-kit/README.md` com a seção da rádio (mapa de uso + contrato + dos campos + como regenerar) — a marca continua com dono único. +- `scripts/build-radio-artes.mjs` reproduz o conjunto a partir dos ativos do kit + e falha fechado fora do contrato; depende de Brexter e Arimo instalados no + fontconfig local (instruções no cabeçalho). +- ONE entrada em `docs/changelog/2026-09-21-c208.md`. + +## Fora de escopo + +- Trocar a arte no painel do zeno (é upload humano; a entrega é o arquivo). +- Player próprio da rádio na home (S25, Issue #1239) e qualquer UI do app. +- Redesenhar os ativos oficiais do kit ou o card de foto de perfil. +- Versionar os intermediários do upscale por IA (o `.4x` fica local; reprodutível + pelo comando documentado). + +## Rabbit holes + +- **Segundo mapa de uso da marca.** A seção da rádio descreve o contrato dos + campos; as regras de marca continuam só no README do kit + manual. **Corte:** + sem recópia de paleta/regra. +- **Segunda fonte da ilustração.** Guardar o `.4x` gigante no repo para "não + precisar da IA". **Corte:** a entrada é o PNG final; a receita é o comando. +- **Arte por campo com nomes próprios.** **Corte:** nomes espelham o campo do + zeno (`logo`, `capa`, `website-card`), sem sinônimos. + +## Referências + +- GitHub Issue: #1241 (C208) +- `public/campaign-kit/README.md` (mapa de uso; dona da marca) e + `docs/campaign-kit/manual-campanha-jorge-solla-1313.pdf` +- `scripts/build-radio-artes.mjs` (builder + receita da ilustração) +- S21/S22/S24 (`docs/plans/jingles-*.md`, `docs/plans/radio-embed-direto.md`) — + a rádio no site; aqui é a identidade da estação fora do site diff --git a/knip.json b/knip.json index bcc7f4ee..07027d41 100644 --- a/knip.json +++ b/knip.json @@ -21,7 +21,7 @@ "src/components/ui/marker.tsx", "src/components/ui/message.tsx" ], - "ignoreBinaries": ["opencode", "ffmpeg", "ffprobe"], + "ignoreBinaries": ["opencode", "ffmpeg", "ffprobe", "fc-match"], "ignoreDependencies": [ "tailwindcss-animate", "tw-animate-css", diff --git a/public/campaign-kit/README.md b/public/campaign-kit/README.md index a7a0ce17..51de5677 100644 --- a/public/campaign-kit/README.md +++ b/public/campaign-kit/README.md @@ -76,3 +76,35 @@ manual; as entradas abaixo descrevem a forma (uso a confirmar). não é uma quarta mensagem nem headline solta. - O nome e o número são os elementos de memorização: não recriar lockups tipográficos quando o ativo oficial existe. + +## Rádio Jorge Solla 1313 (zeno.fm) + +Artes da estação [`zeno.fm/radio/jorge-solla-1313`](https://zeno.fm/radio/jorge-solla-1313/), +geradas por [`scripts/build-radio-artes.mjs`](../../scripts/build-radio-artes.mjs) +a partir dos ativos deste kit e dos artboards de alta em `public/`. Os campos do +painel do zeno têm contratos diferentes — medidos na própria página da estação: + +| Arquivo | Campo | Como o zeno exibe | +| --- | --- | --- | +| `radio/radio-jorge-solla-1313-logo.png` (1024², fundo branco opaco) | Logo | círculo pequeno (CDN entrega 152², `rounded-full`) — o desenho tem de ler dentro do círculo e em ~40 px | +| `radio/radio-jorge-solla-1313-capa.png` (2048×1024) | Cover Image | 2:1 no celular, 3:1 e 4:1 no desktop (`object-cover` central) → todo o conteúdo na **faixa segura central 2048×512**; a área do logo circular sobreposto no desktop (x 60..380, y ≥700) fica branca | +| `radio/radio-jorge-solla-1313-website-card.png` (1200×720) | Station Website Card | quadrado (`aspect-square`, corte central 720×720) → o conteúdo vive nesse quadrado | + +- **Logo escolhida (2026-09-21):** o "O" do SOLLA da marca positiva em fundo + branco — recorte do artboard `public/Prancheta 1@3x.png`, com a estrela vazada + e o rastro verde/amarelo/azul. As finalistas (arquivo da decisão) ficam em + `radio/finalistas/`: "1313" no vermelho, marca completa positiva no branco e o + "O" branco no vermelho. +- **Capa:** `RÁDIO` (Brexter Bold) + marca vertical oficial (o "1313" embaixo do + JORGE SOLLA, com o slogan) + o candidato à direita, em conjunto centralizado — + ilustração (padrão) ou foto (`radio/capa-com-foto.png`, alternativa). +- **Ilustração em alta:** `radio/ilustracao-solla-1313.png` (769×1122, RGBA) + parte da ilustração do card de foto de perfil + (`cards/photo-portrait-frame.png`, recorte em x 100..355, y 840..1265), + sobe 4× por IA (Real-ESRGAN ncnn-vulkan, modelo `realesrgan-x4plus-anime`) e + perde o fundo por flood fill + abertura morfológica (a faixa do frame é + `#b72031`, não a cor do kit). A receita completa está no cabeçalho do builder. +- **Regenerar:** `node scripts/build-radio-artes.mjs` — exige Brexter e Arimo no + fontconfig local (instruções no cabeçalho do script). O builder revalida o + contrato da capa e do card (faixa segura, área do logo do zeno, quadrado + central) e falha fechado. diff --git a/public/campaign-kit/radio/capa-com-foto.png b/public/campaign-kit/radio/capa-com-foto.png new file mode 100644 index 00000000..b3987573 Binary files /dev/null and b/public/campaign-kit/radio/capa-com-foto.png differ diff --git a/public/campaign-kit/radio/finalistas/logo-finalista-1313-no-vermelho.png b/public/campaign-kit/radio/finalistas/logo-finalista-1313-no-vermelho.png new file mode 100644 index 00000000..92c9c528 Binary files /dev/null and b/public/campaign-kit/radio/finalistas/logo-finalista-1313-no-vermelho.png differ diff --git a/public/campaign-kit/radio/finalistas/logo-finalista-marca-completa-no-branco.png b/public/campaign-kit/radio/finalistas/logo-finalista-marca-completa-no-branco.png new file mode 100644 index 00000000..b5103c61 Binary files /dev/null and b/public/campaign-kit/radio/finalistas/logo-finalista-marca-completa-no-branco.png differ diff --git a/public/campaign-kit/radio/finalistas/logo-finalista-o-branco-no-vermelho.png b/public/campaign-kit/radio/finalistas/logo-finalista-o-branco-no-vermelho.png new file mode 100644 index 00000000..3fc45330 Binary files /dev/null and b/public/campaign-kit/radio/finalistas/logo-finalista-o-branco-no-vermelho.png differ diff --git a/public/campaign-kit/radio/ilustracao-solla-1313.png b/public/campaign-kit/radio/ilustracao-solla-1313.png new file mode 100644 index 00000000..a6259492 Binary files /dev/null and b/public/campaign-kit/radio/ilustracao-solla-1313.png differ diff --git a/public/campaign-kit/radio/radio-jorge-solla-1313-capa.png b/public/campaign-kit/radio/radio-jorge-solla-1313-capa.png new file mode 100644 index 00000000..baef62a4 Binary files /dev/null and b/public/campaign-kit/radio/radio-jorge-solla-1313-capa.png differ diff --git a/public/campaign-kit/radio/radio-jorge-solla-1313-logo.png b/public/campaign-kit/radio/radio-jorge-solla-1313-logo.png new file mode 100644 index 00000000..83bd22ee Binary files /dev/null and b/public/campaign-kit/radio/radio-jorge-solla-1313-logo.png differ diff --git a/public/campaign-kit/radio/radio-jorge-solla-1313-website-card.png b/public/campaign-kit/radio/radio-jorge-solla-1313-website-card.png new file mode 100644 index 00000000..5e4fc2f4 Binary files /dev/null and b/public/campaign-kit/radio/radio-jorge-solla-1313-website-card.png differ diff --git a/scripts/build-radio-artes.mjs b/scripts/build-radio-artes.mjs new file mode 100644 index 00000000..d6fcb0b1 --- /dev/null +++ b/scripts/build-radio-artes.mjs @@ -0,0 +1,306 @@ +/** + * Artes da Rádio Jorge Solla 1313 — `public/campaign-kit/radio/`. + * + * Gera (e revalida) os ativos da estação no zeno.fm a partir dos ativos + * oficiais do kit 1313 e dos artboards de alta em `public/`: + * + * radio-jorge-solla-1313-logo.png 1024x1024 logo escolhida (o "O" + * do SOLLA em fundo branco; + * o zeno exibe em círculo) + * radio-jorge-solla-1313-capa.png 2048x1024 capa da estação (crop + * 4:1 no desktop = a faixa + * segura 2048x512 central) + * radio-jorge-solla-1313-website-card.png 1200x720 station website card + * (exibido em quadrado) + * capa-com-foto.png 2048x1024 alternativa da capa com + * a foto oficial + * finalistas/logo-finalista-*.png 1024x1024 logos finalistas + * (arquivo da decisão) + * ilustracao-solla-1313.png 769x1122 ilustração em alta + * (entrada versionada) + * + * Regras de marca: `public/campaign-kit/README.md` (paleta #e4102f/#184e92, marca + * positiva/oficial para fundo claro, sem recriar lockup). "RÁDIO" usa Brexter + * Bold (display face oficial, `src/app/(frontend)/fonts`) e a assinatura usa + * Arimo (face dos cards, via next/font). Prerequisito local (fora do repo): + * + * cp src/app/'(frontend)'/fonts/Brexter-Bold.ttf ~/.local/share/fonts/ + * cp /.next/static/media/.woff2 ~/.local/share/fonts/Arimo-Regular.woff2 + * fc-cache -f ~/.local/share/fonts + * + * Uso: node scripts/build-radio-artes.mjs + */ +import { execFileSync } from 'node:child_process' +import { statSync } from 'node:fs' +import { mkdir } from 'node:fs/promises' +import { createRequire } from 'node:module' + +const require = createRequire(new URL('../package.json', import.meta.url)) +const sharp = require('sharp') + +const REPO = new URL('../', import.meta.url).pathname +const KIT = `${REPO}public/campaign-kit` +const OUT = `${KIT}/radio` +const FINALISTS = `${OUT}/finalistas` + +const RED = '#e4102f' +const BLUE = '#184e92' +const WHITE = '#ffffff' + +const SAFE_TOP = 256 +const SAFE_BOTTOM = 768 +const OVERLAY = { left: 60, right: 380, top: 700 } // logo circular do zeno (desktop) + +const assertFont = (family) => { + const matched = execFileSync('fc-match', ['--format', '%{family}', `${family}:bold`], { + encoding: 'utf8', + }) + if (!matched.toLowerCase().includes(family.toLowerCase())) { + console.error(`Fonte "${family}" não está instalada no fontconfig local — veja o cabeçalho.`) + process.exit(1) + } +} + +async function text({ str, font = 'Brexter', size, fill, tracking = 0, weight = 700 }) { + const w = Math.ceil(str.length * size * 1.7 + 4 * tracking + 240) + const h = Math.ceil(size * 3) + const svg = ` + ${str}` + const t = await sharp(Buffer.from(svg)) + .png() + .trim({ threshold: 1 }) + .png() + .toBuffer({ resolveWithObject: true }) + return { input: t.data, w: t.info.width, h: t.info.height } +} + +async function asset(file, width) { + const t = await sharp(file) + .trim({ threshold: 5 }) + .resize({ width }) + .png() + .toBuffer({ resolveWithObject: true }) + return { input: t.data, w: t.info.width, h: t.info.height } +} + +/** "O" do SOLLA recortado do artboard de alta (caixa medida no trim de 1654x1245). */ +async function oMonogram(file, width) { + const trim = await sharp(file).trim({ threshold: 5 }).png().toBuffer({ resolveWithObject: true }) + const glyph = await sharp(trim.data) + .extract({ left: 349, top: 258, width: 361, height: 330 }) + .png() + .toBuffer() + const t = await sharp(glyph).resize({ width }).png().toBuffer({ resolveWithObject: true }) + return { input: t.data, w: t.info.width, h: t.info.height } +} + +/** Marca vertical oficial (nome, 1313 embaixo e slogan). */ +async function verticalMark(height) { + const trim = await sharp(`${REPO}public/Prancheta 1@3x.png`) + .trim({ threshold: 5 }) + .png() + .toBuffer({ resolveWithObject: true }) + const t = await sharp(trim.data).resize({ height }).png().toBuffer({ resolveWithObject: true }) + return { input: t.data, w: t.info.width, h: t.info.height } +} + +/** Foto oficial do candidato (cutout com alfa). */ +async function candidatePhoto(height) { + const trim = await sharp(`${REPO}public/JOA00162.avif`) + .trim({ threshold: 5 }) + .png() + .toBuffer({ resolveWithObject: true }) + const t = await sharp(trim.data).resize({ height }).png().toBuffer({ resolveWithObject: true }) + return { input: t.data, w: t.info.width, h: t.info.height } +} + +/** Ilustração em alta (recorte + upscale por IA + fundo removido — ver README do kit). */ +async function illustration(height) { + const t = await sharp(`${OUT}/ilustracao-solla-1313.png`) + .trim({ threshold: 1 }) + .resize({ height }) + .png() + .toBuffer({ resolveWithObject: true }) + return { input: t.data, w: t.info.width, h: t.info.height } +} + +const place = (layers) => + layers.map(({ asset: a, x, y, cx, cy }) => ({ + input: a.input, + left: Math.round(x ?? cx - a.w / 2), + top: Math.round(y ?? cy - a.h / 2), + })) + +async function render({ name, width, height, layers, bg = WHITE }) { + const buffer = await sharp({ create: { width, height, channels: 4, background: bg } }) + .composite(place(layers)) + .png({ compressionLevel: 9 }) + .toBuffer() + await sharp(buffer).toFile(`${OUT}/${name}`) + console.log( + name, + `${width}x${height}`, + `${(statSync(`${OUT}/${name}`).size / 1024).toFixed(0)}KB`, + ) + return buffer +} + +/** Faixa segura da capa inteira branca fora de y 256..768, e livre do logo do zeno. */ +async function assertCoverSafe(buffer) { + const { data, info } = await sharp(buffer).raw().toBuffer({ resolveWithObject: true }) + const white = (i) => data[i] > 250 && data[i + 1] > 250 && data[i + 2] > 250 + let outside = 0 + let overlay = 0 + for (let y = 0; y < info.height; y++) { + for (let x = 0; x < info.width; x++) { + const i = (y * info.width + x) * 4 + if (white(i)) continue + if (y < SAFE_TOP || y >= SAFE_BOTTOM) outside++ + if (x >= OVERLAY.left && x <= OVERLAY.right && y >= OVERLAY.top) overlay++ + } + } + if (outside > 0 || overlay > 0) { + throw new Error( + `capa fora do contrato: ${outside}px fora da faixa segura, ${overlay}px no logo do zeno`, + ) + } +} + +/** Card: nada fora do quadrado central 720x720 (é o crop exibido no zeno). */ +async function assertCardSquare(buffer) { + const { data, info } = await sharp(buffer).raw().toBuffer({ resolveWithObject: true }) + const white = (i) => data[i] > 250 && data[i + 1] > 250 && data[i + 2] > 250 + let outside = 0 + for (let y = 0; y < info.height; y++) { + for (let x = 0; x < info.width; x++) { + if (x >= 240 && x < 960) continue + if (!white((y * info.width + x) * 4)) outside++ + } + } + if (outside > 0) throw new Error(`card com ${outside}px fora do quadrado central`) +} + +assertFont('Brexter') +assertFont('Arimo') + +const ILLUSTRATION = `${OUT}/ilustracao-solla-1313.png` +try { + await sharp(ILLUSTRATION).metadata() +} catch { + console.error( + `Ilustração ausente: ${ILLUSTRATION} — o pipeline está documentado em public/campaign-kit/README.md.`, + ) + process.exit(1) +} +await mkdir(FINALISTS, { recursive: true }) + +/* ------------------------------- Logo ------------------------------- + * O "O" do SOLLA da marca positiva em fundo branco opaco — legível no círculo + * pequeno do zeno e sem repetir o símbolo onde o nome estilizado já está. + */ +await render({ + name: 'radio-jorge-solla-1313-logo.png', + width: 1024, + height: 1024, + layers: [{ asset: await oMonogram(`${REPO}public/Prancheta 1@3x.png`, 580), cx: 512, cy: 512 }], +}) + +/* ------------------------------- Capa ------------------------------- + * "RÁDIO" + marca vertical (nome, 1313 e slogan) + candidato à direita em + * conjunto centralizado; tudo dentro da faixa segura 2048x512. + */ +{ + const KICKER_PX = 58 + const KICKER_GAP = 14 + const MARK_H = 420 + const PERSON_H = 500 + const PERSON_GAP = 60 + + const kicker = await text({ str: 'RÁDIO', size: KICKER_PX, fill: RED, tracking: 22 }) + const mark = await verticalMark(MARK_H) + const top = 512 - (kicker.h + KICKER_GAP + mark.h) / 2 + for (const [name, person] of [ + ['radio-jorge-solla-1313-capa.png', await illustration(PERSON_H)], + ['capa-com-foto.png', await candidatePhoto(PERSON_H)], + ]) { + const groupW = mark.w + PERSON_GAP + person.w + const markX = Math.round((2048 - groupW) / 2) + const buffer = await render({ + name, + width: 2048, + height: 1024, + layers: [ + { asset: kicker, cx: markX + mark.w / 2, cy: top + kicker.h / 2 }, + { asset: mark, x: markX, y: top + kicker.h + KICKER_GAP }, + { asset: person, cx: markX + mark.w + PERSON_GAP + person.w / 2, cy: 512 }, + ], + }) + await assertCoverSafe(buffer) + } +} + +/* --------------------------- Website card ---------------------------- + * 1200x720 (5:3) · o zeno exibe em quadrado (corte central 720x720). + */ +{ + const kicker = await text({ str: 'RÁDIO', size: 52, fill: RED, tracking: 16 }) + const mark = await asset(`${KIT}/jorge-solla-positivo.png`, 560) + const tag = await text({ + str: 'A rádio da nossa caminhada pela Bahia.', + font: 'Arimo', + weight: 400, + size: 30, + fill: BLUE, + tracking: 1, + }) + let y = 360 - (kicker.h + 14 + mark.h + 22 + tag.h) / 2 + const layers = [] + for (const [item, gap] of [ + [kicker, 14], + [mark, 22], + [tag, 0], + ]) { + layers.push({ asset: item, cx: 600, y }) + y += item.h + gap + } + const buffer = await render({ + name: 'radio-jorge-solla-1313-website-card.png', + width: 1200, + height: 720, + layers, + }) + await assertCardSquare(buffer) +} + +/* ----------------------------- Finalistas ---------------------------- + * O arquivo da decisão de logo (2026-09-21): a escolhida é o "O" (acima). + */ +{ + const numero = await asset(`${KIT}/numero-negativo.png`, 860) + await render({ + name: 'finalistas/logo-finalista-1313-no-vermelho.png', + width: 1024, + height: 1024, + bg: RED, + layers: [{ asset: numero, cx: 512, cy: 512 }], + }) + + const completa = await asset(`${REPO}public/Prancheta 1@3x.png`, 840) + await render({ + name: 'finalistas/logo-finalista-marca-completa-no-branco.png', + width: 1024, + height: 1024, + layers: [{ asset: completa, cx: 512, cy: 512 }], + }) + + const oBranco = await oMonogram(`${REPO}public/Prancheta 3@3x.png`, 580) + await render({ + name: 'finalistas/logo-finalista-o-branco-no-vermelho.png', + width: 1024, + height: 1024, + bg: RED, + layers: [{ asset: oBranco, cx: 512, cy: 512 }], + }) +}