Skill: Regra dos Terços (animação preenchendo 2/3, um terço livre em cinza)
Reenquadra um deck do Mira na regra dos terços: divide o quadro num grid 3x3 e joga o conteúdo do slide (título + animação) para as colunas 1 e 2 (os dois terços da esquerda), deixando a coluna 3 (direita) inteira em cinza #333, limpa, para você sobrepor texto, lower-third ou câmera na edição. A animação é reformulada por metáfora para preencher os 2/3, no padrão de qualidade do /mira-vertical.
COL 1 COL 2 COL 3
┌──────────┬──────────┬──────────┐
│ TÍTULO │ │ │
├──────────┼──────────┤ CINZA │ ← #333, limpa
│ ANIMAÇÃO preenche 2/3│ (você │ você sobrepõe
├──────────┼──────────┤ sobrepõe│ na edição
│ │ │ depois) │
└──────────┴──────────┴──────────┘
conteúdo nas colunas 1+2 direita cinza livre
Tem dois modos: conversão (padrão quando existe deck de origem, passos abaixo) e criação nativa (quando não existe deck, ou o usuário pede um slide novo já em terços).
Fonte da verdade: o padrão desta skill está congelado na spec
_reversa_sdd/responsividade/sdd/mira-thirds.md(score 92) e no mockup_reversa_sdd/responsividade/sdd/referencias/3.jpg. Quando em dúvida sobre um valor exato, o resultado deve bater com o mockup.
O resultado, em uma frase
Num slide de conteúdo, nos 2/3 da esquerda: o título no topo (1 ou 2 linhas) e a animação grande preenchendo o box dos 2/3 abaixo. A coluna 3 (direita) fica cinza #333, vazia (sem título, animação ou pílula). Somem o subtítulo, o header do card e a base de pílulas. Capa e encerramento (sem .glass-card) mantêm o layout próprio.
CRITÉRIO Nº 1
A animação tem que preencher a maior parte do box dos 2/3; isso vale mais que a perfeição do reflow por metáfora. Nunca aceite a animação como faixa fina com área vazia (ou preta) em volta. Antes de entregar, cheque cada slide: se sobra muita área vazia, refaça. Vale o padrão do mira-animator: espaço vazio é defeito de composição; na criação nativa, o que a escala não resolver vira cenário ambiente da própria metáfora (parado ou em deriva lenta, nunca focal, e sempre dentro dos 2/3).
ÁREA SEGURA: 50px do BOX dos 2/3, com título E animação dentro
Todo deck desta skill leva área segura de 50px no box dos 2/3, e a animação tem que sempre CHEGAR na margem (fit-to-área-segura, item C), nunca parar antes dela. Validado nas imagens de referência (_reversa_sdd/responsividade/margem_ref/). A área segura pertence ao box dos 2/3 de altura cheia (o bloco do slide), NÃO ao palco da animação:
(A) Margem de 50px das bordas do box dos 2/3, tudo dentro dela. O bloco do slide (body > section:has(.glass-card) > div) cobre os 2/3 de altura cheia e leva padding: 50px; box-sizing: border-box; (já no bloco canônico). Título e animação vivem DENTRO da área segura: o título é a primeira coisa dentro dela (nunca colado na borda do quadro) e a animação preenche o resto, sem vazar para o 1/3 cinza (os 50px valem também na divisa com a coluna cinza). ERRADO: pôr os 50px como padding do .anim-stage, o que deixa o título fora da margem e a área segura só na animação. CERTO: a margem abraça o box dos 2/3 inteiro.
(B) O palco ocupa o resto da área segura + viewBox casado em runtime. No canônico, o bloco do slide, o .glass-card e o .anim-stage formam uma cadeia flex (flex: 1 1 auto; min-height: 0): o palco estica da base do título até a margem de baixo, na largura toda da área segura, sem aspect-ratio fixo. Como a altura do título varia (1 ou 2 linhas), a razão do palco não é fixa: case o viewBox à razão REAL do palco em runtime, no início de cada build/replay (o H autorado, H = W * 1080/1280, é só valor inicial):
// F = FMT.<slug> (objeto {W, H} usado pela animação); svg = seleção d3 do <svg>
function casarPalco() {
const r = svg.node().closest('.anim-stage').getBoundingClientRect();
if (r.width > 0 && r.height > 0) F.H = Math.round(F.W * r.height / r.width);
svg.attr('viewBox', `0 0 ${F.W} ${F.H}`);
}
// chame casarPalco() na 1ª linha do build/replay; re-case em resize e document.fonts.ready
// (o fitToArea a cada tick absorve a mudança sem precisar rebuildar)
(C) A animação PREENCHE até a margem (fit-to-área-segura). Não basta caber: a animação tem que ocupar todo o palco, encostando na margem sem cruzar. Envolva TODO o desenho num grupo <g> e, a cada quadro, escale/translade esse grupo para a bounding box dele preencher [EDGE, W-EDGE] × [EDGE, H-EDGE] (EDGE ≈ 14, só a folga de stroke/glow; a margem de 50px vem do padding do BLOCO dos 2/3):
const EDGE = 14;
function fitToArea(g) { // g = <g> que envolve TODO o desenho
g.attr('transform', null);
const b = g.node().getBBox();
if (!b.width || !b.height) return;
const tw = F.W - 2 * EDGE, th = F.H - 2 * EDGE;
const s = Math.min(tw / b.width, th / b.height);
const tx = EDGE + (tw - b.width * s) / 2 - b.x * s;
const ty = EDGE + (th - b.height * s) / 2 - b.y * s;
g.attr('transform', `translate(${tx},${ty}) scale(${s})`);
}
// no 'tick' da animação (ou depois de compor o quadro): fitToArea(root)
Vale para QUALQUER metáfora (grafo, fluxo, orbital, partículas): componha livre e deixe o fit preencher. O fit substitui o antigo clamp de margem e garante preenchimento determinístico. O 1/3 cinza continua intocado (o fit escala só o conteúdo do palco, que já vive nos 2/3).
(D) A margem é TRANSPARENTE. A área segura é espaço vazio (o padding de 50px do bloco dos 2/3), sem nenhuma pintura: nada de faixa vermelha, moldura, borda ou CSS/JS de debug no arquivo entregue. Se precisar conferir, meça no DevTools ou pinte temporariamente e REMOVA antes de entregar. Validação (a olho): título e animação inteiros dentro dos 50px do box dos 2/3 (altura cheia, inclusive na divisa com o 1/3 cinza), e a animação encostando na margem. Título colado na borda do quadro ou margem só na animação = ERRADO.
O 1/3 cinza é sagrado
Nada de título, pílula ou elemento da animação invade o terço reservado. Ele fica cinza #333, limpo, para sobreposição na edição (a versão antiga deixava o terço com a cor do slide).
Criação do zero na geometria nativa
Quando não houver deck de origem, ou o usuário pedir "cria um slide em terços sobre X", NÃO crie um slide centralizado para reenquadrar depois. O slide nasce composto em terços:
- Herde as regras criativas do
agents/mira-animator/SKILL.md: metáfora primeiro, animação depois (método A/B antes de codar), Regra Zero (metáfora + história + loop interno obrigatório), refinamento sob demanda por slide, regra de idioma, regra de título (sem ícone, máximo 6 palavras), estrutura do card com glass-card. - Composição nativa: o arquivo nasce com o bloco
<style id="mira-formato-thirds">desta skill no head, o conteúdo autorado para viver nas colunas 1+2 e a coluna 3 em cinza #333. A animação já nasce preenchendo o box dos 2/3. - Vantagem da autoria nativa: aqui você PODE compor a animação com o assunto principal sobre a linha de força entre a COL 2 e a COL 3, que é o ponto forte da regra dos terços.
REGRA DE IDIOMA
Siga agents/_shared/idioma.md. Texto visível em português correto. Proibido travessão (—): use vírgula ou dois-pontos.
Regra de Ouro: nunca destrua o original (modo conversão)
- O deck de origem (
index.html,index-1x1.htmlouindex-9x16.html) permanece intacto. - Você cria um arquivo novo ao lado, com sufixo
-thirds. - O que continua intocado mesmo na cópia: textos, rótulos, cores, easing, durações, loop interno e o
generation counter(window.__slugGen). Você reposiciona, oculta e reformula a geometria, não reescreve a animação do zero.
Composição é ortogonal ao formato
Esta skill não muda a proporção do quadro: desloca o conteúdo para os 2/3 de um lado e pinta o outro terço de cinza. Por isso combina sobre qualquer formato:
index.html(16:9) →index-thirds.htmlindex-1x1.html(1:1) →index-1x1-thirds.htmlindex-9x16.html(9:16) →index-9x16-thirds.html
Se o usuário não disser sobre qual arquivo aplicar, use o index.html (16:9). Se houver mais de um deck na pasta e ficar ambíguo, pergunte qual.
As três viradas de chave
1. Conteúdo nos 2/3 + 1/3 em cinza #333
Via CSS no bloco injetado:
- Encosta o conteúdo no lado da animação:
body > section { align-items: var(--thirds-align) }(flex-start= animação à esquerda, cinza à direita, padrão). - Bloco do slide = box dos 2/3 de ALTURA CHEIA com a área segura:
body > section > div { width: 66.667%; max-width: 66.667% }, e nos slides de conteúdo o bloco vira coluna flex de altura cheia (flex: 1 1 auto; min-height: 0) compadding: 50px(a área segura; a seção zera o padding próprio). - Pinta o 1/3 de cinza: um
linear-gradientnasectiondeixa os 2/3 no fundo do tema e o 1/3 em #333, de altura cheia, independente do conteúdo. Isso garante o terço cinza sólido do mockup.
2. Composição enxuta nos 2/3 (só título + animação)
Escopado com :has(.glass-card), como no vertical: oculta subtítulo, header e base do card e zera o chrome do .glass-card, para a animação preencher o box dos 2/3. Título colado no topo, auto-ajustado para no máximo 2 linhas.
3. Palco = todo o resto da área segura + viewBox casado em runtime + reflow
- Palco esticado (cadeia flex): bloco do slide,
.glass-carde.anim-stagecomflex: 1 1 auto; min-height: 0(já no canônico); o.anim-stagefica SEMaspect-ratiofixo e SEM padding, ocupando da base do título até a margem de baixo, na largura toda da área segura. - viewBox casando: para cada
<svg id="sv-...">, autore a altura inicial comoH = W * 1080 / 1280(mantendominX,minY,W) e case à razão real do palco em runtime comcasarPalco(seção da ÁREA SEGURA).preserveAspectRatio="xMidYMid meet"; sem letterbox nem distorção. Exemplo:viewBox="0 0 960 540"nasceviewBox="0 0 960 810"e ocasarPalcoajusta oHao palco medido. - Reflow por metáfora para preencher o box (CRITÉRIO Nº 1), conforme o playbook abaixo. Loop e generation counter intactos.
Auto-ajuste de título (script injetado)
O mesmo IIFE do vertical: mede a altura real de cada h2 e reduz a fonte 1px por vez (piso 18px) até caber em 2 linhas. Roda no load, document.fonts.ready e resize (debounced). Injete antes de lucide.createIcons():
(function () {
const MAX_LINES = 2, MIN_FONT = 18;
function fitTitles() {
document.querySelectorAll('body > section h2').forEach(h2 => {
h2.style.removeProperty('font-size');
let font = parseFloat(getComputedStyle(h2).fontSize), guard = 0;
while (h2.scrollHeight > parseFloat(getComputedStyle(h2).lineHeight) * MAX_LINES + 2 && font > MIN_FONT && guard < 90) {
font -= 1; h2.style.setProperty('font-size', font + 'px', 'important'); guard++;
}
});
}
window.addEventListener('load', fitTitles);
if (document.fonts && document.fonts.ready) document.fonts.ready.then(fitTitles);
window.addEventListener('resize', () => { clearTimeout(window.__fitT); window.__fitT = setTimeout(fitTitles, 150); });
})();
Playbook de reflow por metáfora (alvo: box dos 2/3, quase-quadrado ~1280×1080)
Objetivo: CRITÉRIO Nº 1, preencher a maior parte do box.
Radial / orbital / hub. Centrado no box; aumente o raio para preencher. Elipse quase circular (o box é quase-quadrado).
Fluxo (partícula entre nós). Eixo pode ficar horizontal (o box é levemente mais largo que alto), sempre reescalado para ocupar; ou na diagonal quando preencher melhor. Sem faixa fina.
Comparação A vs B / transformação. Lado a lado se couber grande, ou empilhe; realce alternado preservado.
Rede / grafo (force layout). forceCenter no centro do box; aumente espalhamento e forceCollide até usar o box; clampe as posições nas margens do box.
Flip cards (stage HTML). Sem viewBox; o box dos 2/3 vale como caixa; disponha os cards para preencher.
Princípio geral: identifique o eixo dominante e recomponha (escala e disposição) até o assunto ocupar a maior parte do box, com o loop preservado.
Passos
- Localizar o deck. Ache o arquivo de origem (
decks/<deck>/oudecks/<tema>/). Padrão:index.html. Se o usuário pedir terços sobre a versão quadrada/vertical, useindex-1x1.htmlouindex-9x16.html. Se faltar, ou não tiver.glass-card/.anim-stage/<svg id="sv-...">, aborte com mensagem clara sem criar arquivo parcial. - Copiar para o novo arquivo. Copie a origem para a versão
-thirdsna mesma pasta. A origem fica byte a byte igual. - Confirmar o seletor. Padrão do Mira:
body > section, bloco do slide é o filho diretobody > section > div. Ajuste se este deck embrulhar diferente. - Injetar a moldura. Logo antes de
</head>, como último bloco de estilo (depois do Tailwind e de qualquer blocomira-squared/mira-vertical), insira o<style id="mira-formato-thirds">canônico (abaixo): conteúdo nos 2/3, 1/3 cinza #333, composição só título + animação, canvas casado ao box. - Injetar o auto-ajuste de título. Adicione o IIFE
fitTitlesno bloco de scripts, antes delucide.createIcons(). - Reformular cada animação no JS. Para cada slide de conteúdo: case o
HdoviewBoxao box (H = W * 1080/1280, mantendominX,minY,W) e aplique o reflow para preencher o box (playbook). Preserve textos, cores, easing, durações, loop e generation counter. - Verificar o encaixe (CRITÉRIO Nº 1). Confira: (a) conteúdo nos 2/3 da esquerda (título + animação); (b) a animação preenche a maior parte do box dos 2/3, sem faixa fina/preta; (c) coluna 3 cinza #333 e limpa (nem título nem pílula invadem); (d) o loop roda; (e) a proporção do quadro não mudou; (f) capa/encerramento com layout próprio.
- Reportar. Informe o caminho do arquivo
-thirds, qual coluna ficou cinza (direita por padrão) e que a proporção não mudou (grave na mesma viewport do formato de origem).
Bloco <style id="mira-formato-thirds"> canônico (gerar exatamente isto)
<style id="mira-formato-thirds">
/* Regra dos terços: conteúdo nos 2/3, 1/3 em cinza #333. É composição, não muda
a proporção do quadro: aplica sobre 16:9, 1:1 ou 9:16. */
:root {
/* Lado da animação:
flex-start = animação à ESQUERDA, coluna da DIREITA cinza (padrão)
flex-end = animação à DIREITA, coluna da ESQUERDA cinza (troque também o gradiente) */
--thirds-align: flex-start;
--thirds-width: 66.667%; /* 2 de 3 colunas */
}
/* O 1/3 livre pintado de cinza #333 de altura cheia, via gradiente na seção.
Padrão (cinza à direita): tema nos 2/3, #333 no 1/3 direito.
Para cinza à esquerda: inverta os stops (#333 até 33.333%, depois o tema). */
body > section {
/* ALTURA DEFINIDA, não min-height. O template usa `min-h-screen` do Tailwind, que dá
min-height; sem `height`, a cadeia flex abaixo (flex: 1 1 auto) não tem o que
distribuir e o palco vaza para fora da tela. */
height: 100vh !important;
align-items: var(--thirds-align) !important;
background:
linear-gradient(to right,
var(--mira-bg, #000) 0,
var(--mira-bg, #000) 66.667%,
#333333 66.667%,
#333333 100%) !important;
}
/* O bloco do slide (título + animação) ocupa só as colunas 1+2. */
body > section > div {
width: var(--thirds-width) !important;
max-width: var(--thirds-width) !important;
}
/* AREA SEGURA: o box dos 2/3 cobre a ALTURA CHEIA e leva 50px de margem;
titulo e animacao vivem DENTRO (inclusive na divisa com o 1/3 cinza). */
body > section:has(.glass-card) { padding: 0 !important; }
body > section:has(.glass-card) > div { flex: 1 1 auto !important; min-height: 0 !important; display: flex !important; flex-direction: column !important; padding: 50px !important; box-sizing: border-box !important; }
body > section:has(.glass-card) .glass-card { flex: 1 1 auto !important; min-height: 0 !important; display: flex !important; flex-direction: column !important; }
/* Palco = TODO o resto da area segura (sem aspect-ratio fixo; o viewBox e casado
em runtime pelo casarPalco). Sem padding: a margem de 50px e do BLOCO dos 2/3. */
.anim-stage { flex: 1 1 auto !important; min-height: 0 !important; height: auto !important; width: 100% !important; aspect-ratio: auto !important; max-width: none !important; margin-inline: 0 !important; padding: 0 !important; box-sizing: border-box !important; }
/* Composição enxuta nos 2/3: só título + animação (escopo: slides com .glass-card). */
body > section h2 { font-size: clamp(28px, 4vh, 44px) !important; line-height: 1.1 !important; }
body > section:has(.glass-card) .text-center > p { display: none !important; } /* subtitulo */
body > section:has(.glass-card) .text-center { margin: 0 0 14px 0 !important; padding: 0 !important; flex: 0 0 auto !important; }
body > section .glass-card { background: none !important; border: none !important; box-shadow: none !important; backdrop-filter: none !important; padding: 0 !important; }
body > section .glass-card > div:first-child, /* header do card */
body > section .glass-card > div:last-child { display: none !important; } /* base (pilulas) */
</style>
Lado livre (padrão: direita). A coluna cinza é a da direita por padrão (--thirds-align: flex-start, animação à esquerda). Se o usuário pedir cinza à esquerda e animação à direita, troque --thirds-align: flex-end e inverta os stops do gradiente (#333 até 33.333%, depois o tema).
Observações honestas
- A skill mexe só no enquadramento e na geometria da animação (para preencher o box). Ela não escreve nada na coluna cinza: fica limpa de propósito para você compor por cima na edição.
- Os elementos fixos de navegação (barra de progresso, botão de próximo) seguem presos à viewport, como nos outros formatos. Gravando na resolução do formato de origem, ficam no lugar certo.
Edge cases
- Elemento do slide invadindo o 1/3 cinza: estreite o bloco daquele slide (
body > section:nth-of-type(N) > div { max-width: 60% !important }); a coluna cinza permanece limpa. - Animação radial: centrada no box dos 2/3; preenche bem (o box é quase-quadrado).
- Animação de fluxo: reescale para o box; pode manter horizontal (box levemente largo); sem faixa fina.
- Terços sobre 1:1 ou 9:16: aplique sobre essa origem; case o
aspect-ratiodo palco e oviewBoxa "2/3 da largura do quadro por altura cheia" daquele formato. - Slide sem
.glass-card(capa, encerramento): as regras:has(.glass-card)não se aplicam; layout próprio preservado. - Navegador sem
:has(): avise que a composição precisa de um Chromium moderno.
Checklist
Os que mais falham (cheque primeiro):
CRITÉRIO Nº 1: a animação preenche a maior parte do box dos 2/3 (nada de faixa fina/preta com sobra).
Coluna 3 (direita) em cinza #333, 100% limpa: nenhum título/animação/pílula invade.
Conteúdo nos 2/3 da esquerda: só título + animação (subtítulo, header e base ocultos).
ÁREA SEGURA no BOX dos 2/3: título E animação dentro da margem de 50px, e a animação CHEGANDO na margem. A margem é transparente: nenhum CSS/JS de debug (faixa vermelha) no arquivo entregue.
Palco esticado (cadeia flex, sem
aspect-ratiofixo) eviewBoxcasado em runtime (casarPalco), sem distorção nem letterbox.Nenhum título passa de 2 linhas (IIFE de auto-ajuste injetado).
Arquivo de origem intacto (byte a byte).
Novo arquivo
-thirdscriado na mesma pasta.Bloco
<style id="mira-formato-thirds">injetado como último estilo antes de</head>.Proporção do quadro preservada (a skill não muda 16:9, 1:1 nem 9:16).
Textos, cores, easing, durações, loop interno e generation counter intactos.
Capa e encerramento com layout próprio preservado.
Nenhum travessão (—); acentuação correta.