/*
 * Modelos de caneca (out/2026): a área de estampa da caneca de 325 ml
 * (20 × 9,5 cm em 300 ppp = 2362 × 1122). A arte dá a volta inteira: o
 * "lado A" (centro em 1/4, aparece com a alça à esquerda), o "lado B"
 * (centro em 3/4, com a alça à direita) e o meio (oposto à alça) ligando os
 * dois, com faixas na borda, confetes, trilhas, selos e ornamentos: nada de
 * espaço morto. As pontas encostam na alça e ficam livres de texto.
 *
 * Temas: bichinhos, animado, minimalista, tipográfico, recortes (foto sem
 * fundo, polaroid no varal, papel rasgado, foto dentro da letra), datas,
 * profissões e infantil. Os desenhos são as figurinhas do editor (Fluent
 * Emoji, da Microsoft, licença MIT) com contorno de adesivo e os traços do
 * Lucide (ISC); as fotos são do Unsplash (licença livre comercial) e moram no
 * depósito da Ktalogu. Os confetes saem de um sorteio com semente: o modelo
 * nasce sempre igual.
 */
import { htmlDoTextoLimpo, fonteDaChave, novoId, solida, type DocumentoDoBanner, type Peca, type PecaDeForma, type PecaDeIcone, type PecaDeImagem, type PecaDeTexto, type PecaDeTextura, type PecaDeTraco, type Pintura, type PontoDoTraco } from "./documento";
import { documento, fundoDoBanner, imagem, texto, type CamposDoTexto, type PedidoDoModelo } from "./modelos-base";
import { encaixar } from "./modelos-colecao";

const W = 2362;
const H = 1122;
/* O centro de cada face. */
const A = 590;
const B = 1771;
const MEIO = W / 2;

const DEPOSITO = "https://ptpstwltddugnnlewvbw.supabase.co/storage/v1/object/public/products/editor-modelos";
const FIGURINHA = (chave: string) => `${DEPOSITO}/figurinhas/${chave}.webp`;
const FOTO = (nome: string) => `${DEPOSITO}/canecas/${nome}.webp`;

/* ------------------------------------------------------------ peças curtas */

type Lugar = { x: number; y: number; w: number; h: number; rot?: number };
const sombraDe = (cor: string, y: number, desfoque: number, opacidade: number) => ({ cor, x: 0, y, desfoque, opacidade });

function t(nome: string, l: Lugar, campos: CamposDoTexto): PecaDeTexto {
  return texto({ nome, ...l, rot: l.rot ?? 0, ...campos });
}

/* Texto centrado em cx. */
function tc(nome: string, cx: number, y: number, w: number, h: number, campos: CamposDoTexto, rot = 0): PecaDeTexto {
  return t(nome, { x: cx - w / 2, y, w, h, rot }, { alinhamento: "centro", ...campos });
}

/* Letrinha de detalhe: caixa alta espaçada. */
const miuda = (nome: string, cx: number, y: number, w: number, conteudo: string, cor: string, tamanho = 34, fonte = "space-grotesk"): PecaDeTexto =>
  tc(nome, cx, y, w, tamanho * 1.7, { texto: conteudo, fonte, peso: 700, tamanho, espaco: 0.32, caixaAlta: true, cor });

function f(nome: string, forma: PecaDeForma["forma"], l: Lugar, pintura: Pintura | string, extra: Partial<PecaDeForma> = {}): PecaDeForma {
  return { id: novoId("f"), tipo: "forma", nome, ...l, rot: l.rot ?? 0, opacidade: 1, forma, pintura: typeof pintura === "string" ? solida(pintura) : pintura, raio: 0, borda: null, sombra: null, pontas: 7, profundidade: 0.2, ...extra };
}

/* Forma centrada em (cx, cy). */
const fc = (nome: string, forma: PecaDeForma["forma"], cx: number, cy: number, w: number, h: number, pintura: Pintura | string, extra: Partial<PecaDeForma> = {}) => f(nome, forma, { x: cx - w / 2, y: cy - h / 2, w, h }, pintura, extra);

function foto(nome: string, src: string, l: Lugar, extra: Partial<PecaDeImagem> = {}): PecaDeImagem {
  return { ...imagem({ nome, src, ...l, rot: l.rot ?? 0, sombra: false, ajuste: "cobrir" }), ...extra };
}

/* Uma figurinha centrada em (cx, cy), com o contorno de adesivo por padrão. */
function fig(nome: string, chave: string, cx: number, cy: number, lado: number, extra: Partial<PecaDeImagem> & { rot?: number; semAdesivo?: boolean } = {}): PecaDeImagem {
  const { rot = 0, semAdesivo = false, ...resto } = extra;
  return foto(nome, FIGURINHA(chave), { x: cx - lado / 2, y: cy - lado / 2, w: lado, h: lado, rot }, { ajuste: "conter", adesivo: semAdesivo ? null : { cor: "#FFFFFF", largura: Math.max(8, Math.round(lado * 0.028)) }, ...resto });
}

/* Ícone de traço (Lucide, ISC) centrado. */
function linha(nome: string, desenho: string, cx: number, cy: number, lado: number, cor: string, espessura = 1.4, extra: Partial<PecaDeIcone> = {}): PecaDeIcone {
  return { id: novoId("ic"), tipo: "icone", nome, x: cx - lado / 2, y: cy - lado / 2, w: lado, h: lado, rot: 0, opacidade: 1, icone: "lucide-desenho", desenho, pintura: solida(cor), espessura, preenchido: false, sombra: null, ...extra };
}

/* Ícone da casa centrado. */
function ic(nome: string, chave: string, cx: number, cy: number, lado: number, cor: string, extra: Partial<PecaDeIcone> = {}): PecaDeIcone {
  return { id: novoId("ic"), tipo: "icone", nome, x: cx - lado / 2, y: cy - lado / 2, w: lado, h: lado, rot: 0, opacidade: 1, icone: chave, pintura: solida(cor), espessura: 2, preenchido: false, sombra: null, ...extra };
}

function tex(nome: string, estilo: PecaDeTextura["estilo"], cor: string, extra: Partial<PecaDeTextura> = {}): PecaDeTextura {
  return { id: novoId("tex"), tipo: "textura", nome, x: 0, y: 0, w: W, h: H, rot: 0, opacidade: 0.12, bloqueada: true, estilo, cor, quantidade: 18, tamanho: 0, centroX: 50, centroY: 50, giro: 0, ...extra };
}

/* Traço livre: pontos em px da arte (vira fração da caixa justa). */
function traco(nome: string, pontos: Array<[number, number]>, cor: string, espessura: number, extra: Partial<PecaDeTraco> = {}): PecaDeTraco {
  const xs = pontos.map((p) => p[0]);
  const ys = pontos.map((p) => p[1]);
  const x = Math.min(...xs) - espessura;
  const y = Math.min(...ys) - espessura;
  const w = Math.max(...xs) - x + espessura;
  const h = Math.max(...ys) - y + espessura;
  const nos: PontoDoTraco[] = pontos.map(([px, py]) => ({ x: (px - x) / w, y: (py - y) / h }));
  return { id: novoId("tr"), tipo: "traco", nome, x, y, w, h, rot: 0, opacidade: 1, pontos: nos, fechado: false, suave: true, cor, espessura, tracejado: "solido", inicio: "nada", fim: "nada", preenchimento: nada, sombra: null, ...extra };
}

const nada: Pintura = { tipo: "nenhuma", cor: "#FFFFFF", cor2: "#FFFFFF", angulo: 0 };
const radial = (cor: string, cor2: string, alfa = 1, alfa2 = 0): Pintura => ({ tipo: "radial", cor, cor2, angulo: 0, alfa, alfa2 });

function trecho(conteudo: string, o: { cor?: string; fonte?: string; italico?: boolean; vazado?: { cor: string; largura: number } }) {
  const estilo = [
    o.cor ? `color:${o.cor}` : "",
    o.fonte ? `font-family:'${fonteDaChave(o.fonte).familia}'` : "",
    o.italico ? "font-style:italic" : "",
    o.vazado ? `color:transparent;-webkit-text-stroke:${o.vazado.largura}px ${o.vazado.cor}` : "",
  ]
    .filter(Boolean)
    .join(";");
  return `<span style="${estilo}">${conteudo}</span>`;
}
const html = (partes: string) => htmlDoTextoLimpo(partes);

const caneca = (fundo: { cor: string; cor2?: string; angulo?: number }, pecas: Peca[]) => documento(W, H, fundoDoBanner({ cor: fundo.cor, cor2: fundo.cor2 ?? null, angulo: fundo.angulo ?? 135, textura: "nenhuma" }), pecas);

/* Os traços do Lucide usados aqui (licença ISC). */
const LUCIDE = {
  cafe: `<path d="M10 2v2"/><path d="M14 2v2"/><path d="M16 8a1 1 0 0 1 1 1v8a4 4 0 0 1-4 4H7a4 4 0 0 1-4-4V9a1 1 0 0 1 1-1h14a4 4 0 1 1 0 8h-1"/><path d="M6 2v2"/>`,
  pata: `<circle cx="11" cy="4" r="2"/><circle cx="18" cy="8" r="2"/><circle cx="20" cy="16" r="2"/><path d="M9 10a5 5 0 0 1 5 5v3.5a3.5 3.5 0 0 1-6.84 1.045Q6.52 17.48 4.46 16.84A3.5 3.5 0 0 1 5.5 10Z"/>`,
  coracao: `<path d="M19 14c1.49-1.46 3-3.21 3-5.5A5.5 5.5 0 0 0 16.5 3c-1.76 0-3 .5-4.5 2-1.5-1.5-2.74-2-4.5-2A5.5 5.5 0 0 0 2 8.5c0 2.3 1.5 4.05 3 5.5l7 7Z"/>`,
  ondas: `<path d="M2 6c.6.5 1.2 1 2.5 1C7 7 7 5 9.5 5c2.6 0 2.4 2 5 2 2.5 0 2.5-2 5-2 1.3 0 1.9.5 2.5 1"/><path d="M2 12c.6.5 1.2 1 2.5 1 2.5 0 2.5-2 5-2 2.6 0 2.4 2 5 2 2.5 0 2.5-2 5-2 1.3 0 1.9.5 2.5 1"/>`,
  folha: `<path d="M11 20A7 7 0 0 1 9.8 6.1C15.5 5 17 4.48 19 2c1 2 2 4.18 2 8 0 5.5-4.78 10-10 10Z"/><path d="M2 21c0-3 1.85-5.36 5.08-6C9.5 14.52 12 13 13 12"/>`,
  brilho: `<path d="M9.937 15.5A2 2 0 0 0 8.5 14.063l-6.135-1.582a.5.5 0 0 1 0-.962L8.5 9.936A2 2 0 0 0 9.937 8.5l1.582-6.135a.5.5 0 0 1 .963 0L14.063 8.5A2 2 0 0 0 15.5 9.937l6.135 1.581a.5.5 0 0 1 0 .964L15.5 14.063a2 2 0 0 0-1.437 1.437l-1.582 6.135a.5.5 0 0 1-.963 0z"/>`,
  estrela: `<path d="M11.525 2.295a.53.53 0 0 1 .95 0l2.31 4.679a2.123 2.123 0 0 0 1.595 1.16l5.166.756a.53.53 0 0 1 .294.904l-3.736 3.638a2.123 2.123 0 0 0-.611 1.878l.882 5.14a.53.53 0 0 1-.771.56l-4.618-2.428a2.122 2.122 0 0 0-1.973 0L6.396 21.01a.53.53 0 0 1-.77-.56l.881-5.139a2.122 2.122 0 0 0-.611-1.879L2.16 9.795a.53.53 0 0 1 .294-.906l5.165-.755a2.122 2.122 0 0 0 1.597-1.16z"/>`,
  passaro: `<path d="M16 7h.01"/><path d="M3.4 18H12a8 8 0 0 0 8-8V7a4 4 0 0 0-7.28-2.3L2 20"/><path d="m20 7 2 .5-2 .5"/><path d="M10 18v3"/><path d="M14 17.75V21"/><path d="M7 18a6 6 0 0 0 3.84-10.61"/>`,
  pegadas: `<path d="M4 16v-2.38C4 11.5 2.97 10.5 3 8c.03-2.72 1.49-6 4.5-6C9.37 2 10 3.8 10 5.5c0 3.11-2 5.66-2 8.68V16a2 2 0 1 1-4 0Z"/><path d="M20 20v-2.38c0-2.12 1.03-3.12 1-5.62-.03-2.72-1.49-6-4.5-6C14.63 6 14 7.8 14 9.5c0 3.11 2 5.66 2 8.68V20a2 2 0 1 0 4 0Z"/><path d="M16 17h4"/><path d="M4 13h4"/>`,
  grao: `<path d="M10.165 6.598C9.954 7.478 9.64 8.36 9 9c-.64.64-1.521.954-2.402 1.165A6 6 0 0 0 8 22c7.732 0 14-6.268 14-14a6 6 0 0 0-11.835-1.402Z"/><path d="M5.341 10.62a4 4 0 1 0 5.279-5.28"/>`,
  sol: `<circle cx="12" cy="12" r="4"/><path d="M12 2v2"/><path d="M12 20v2"/><path d="m4.93 4.93 1.41 1.41"/><path d="m17.66 17.66 1.41 1.41"/><path d="M2 12h2"/><path d="M20 12h2"/><path d="m6.34 17.66-1.41 1.41"/><path d="m19.07 4.93-1.41 1.41"/>`,
  nuvem: `<path d="M17.5 19H9a7 7 0 1 1 6.71-9h1.79a4.5 4.5 0 1 1 0 9Z"/>`,
  raio: `<path d="M4 14a1 1 0 0 1-.78-1.63l9.9-10.2a.5.5 0 0 1 .86.46l-1.92 6.02A1 1 0 0 0 13 10h7a1 1 0 0 1 .78 1.63l-9.9 10.2a.5.5 0 0 1-.86-.46l1.92-6.02A1 1 0 0 0 11 14z"/>`,
};

/* ----------------------------------------------------------- os detalhes */

/* Sorteio com semente (mulberry32): o mesmo modelo nasce sempre igual. */
function sorteio(semente: number) {
  let a = semente >>> 0;
  return () => {
    a = (a + 0x6d2b79f5) >>> 0;
    let x = a;
    x = Math.imul(x ^ (x >>> 15), x | 1);
    x ^= x + Math.imul(x ^ (x >>> 7), x | 61);
    return ((x ^ (x >>> 14)) >>> 0) / 4294967296;
  };
}

type Zona = { x: number; y: number; w: number; h: number };
type TipoDeConfete = "bola" | "anel" | "estrela" | "mais" | "risco" | "coracao" | "brilho";

/* Confetes espalhados nos vazios (fora das zonas de texto e desenho). */
function confetes(semente: number, quantos: number, cores: string[], evitar: Zona[], o: { tipos?: TipoDeConfete[]; min?: number; max?: number; area?: Zona; opacidade?: number } = {}): Peca[] {
  const r = sorteio(semente);
  const tipos = o.tipos ?? ["bola", "anel", "estrela", "risco"];
  const area = o.area ?? { x: 70, y: 70, w: W - 140, h: H - 140 };
  const min = o.min ?? 16;
  const max = o.max ?? 38;
  const postos: Array<[number, number, number]> = [];
  const pecas: Peca[] = [];
  for (let i = 0; i < quantos; i += 1) {
    for (let tentativa = 0; tentativa < 60; tentativa += 1) {
      const s = min + r() * (max - min);
      const cx = area.x + r() * area.w;
      const cy = area.y + r() * area.h;
      const bate = evitar.some((z) => cx > z.x - s && cx < z.x + z.w + s && cy > z.y - s && cy < z.y + z.h + s) || postos.some(([px, py, ps]) => Math.hypot(px - cx, py - cy) < (ps + s) * 1.6);
      if (bate) continue;
      postos.push([cx, cy, s]);
      const cor = cores[Math.floor(r() * cores.length)];
      const tipo = tipos[Math.floor(r() * tipos.length)];
      const giro = Math.round(r() * 360);
      const comum = { opacidade: o.opacidade ?? 1 };
      if (tipo === "bola") pecas.push(fc("Confete", "circulo", cx, cy, s, s, cor, comum));
      else if (tipo === "anel") pecas.push(fc("Confete", "circulo", cx, cy, s * 1.1, s * 1.1, nada, { ...comum, borda: { cor, largura: Math.max(3, s * 0.2) } }));
      else if (tipo === "estrela") pecas.push(ic("Confete", "estrela-4", cx, cy, s * 1.5, cor, { ...comum, preenchido: true, espessura: 0.5, rot: giro % 45 }));
      else if (tipo === "mais") pecas.push(ic("Confete", "mais", cx, cy, s * 1.4, cor, { ...comum, espessura: 3.2, rot: giro % 45 }));
      else if (tipo === "coracao") pecas.push(fc("Confete", "coracao", cx, cy, s * 1.3, s * 1.2, cor, { ...comum, rot: (giro % 50) - 25 }));
      else if (tipo === "brilho") pecas.push(linha("Confete", LUCIDE.brilho, cx, cy, s * 1.6, cor, 1.6, { ...comum, preenchido: true }));
      else pecas.push(f("Confete", "retangulo", { x: cx - s * 0.9, y: cy - s * 0.18, w: s * 1.8, h: s * 0.36, rot: giro }, cor, { ...comum, raio: s }));
      break;
    }
  }
  return pecas;
}

/* A borda ondulada (conchas) de cima ou de baixo: um traço fechado só. */
function conchas(emBaixo: boolean, cor: string): PecaDeTraco {
  const d = 70;
  const pontos: Array<[number, number]> = [];
  for (let x = W + d; x >= -d; x -= d / 2) pontos.push([x, Math.round(x / (d / 2)) % 2 ? 66 : 34]);
  pontos.push([-d, -60], [W + d, -60]);
  const virados = emBaixo ? pontos.map(([x, y]): [number, number] => [x, H - y]) : pontos;
  return traco(emBaixo ? "Conchas de baixo" : "Conchas de cima", virados, cor, 1, { fechado: true, preenchimento: solida(cor), bloqueada: true });
}

/* Borda de cima e de baixo da caneca (o "aro" da estampa); poucas peças (o limite é 120 por página). */
function aro(estilo: "conchas" | "faixas" | "pontos" | "xadrez" | "fios" | "atencao", cor: string, cor2: string): Peca[] {
  const faixa = (nome: string, y: number, h: number, pintura: string, extra: Partial<PecaDeForma> = {}) => f(nome, "retangulo", { x: 0, y, w: W, h }, pintura, { bloqueada: true, ...extra });
  if (estilo === "conchas") {
    return [conchas(false, cor), conchas(true, cor), faixa("Fio de cima", 80, 4, cor2), faixa("Fio de baixo", H - 84, 4, cor2)];
  }
  if (estilo === "faixas") {
    return [faixa("Faixa de cima", 0, 40, cor), faixa("Fio de cima", 52, 6, cor2), faixa("Faixa de baixo", H - 40, 40, cor), faixa("Fio de baixo", H - 58, 6, cor2)];
  }
  if (estilo === "pontos") {
    return [
      faixa("Faixa de cima", 0, 54, cor),
      tex("Bolinhas de cima", "pontos", cor2, { y: 0, h: 54, opacidade: 0.9, tamanho: 30 }),
      faixa("Faixa de baixo", H - 54, 54, cor),
      tex("Bolinhas de baixo", "pontos", cor2, { y: H - 54, h: 54, opacidade: 0.9, tamanho: 30 }),
    ];
  }
  if (estilo === "xadrez") {
    return [
      faixa("Faixa de cima", 0, 68, cor2),
      tex("Xadrez de cima", "xadrez", cor, { y: 0, h: 68, opacidade: 1, tamanho: 34 }),
      faixa("Faixa de baixo", H - 68, 68, cor2),
      tex("Xadrez de baixo", "xadrez", cor, { y: H - 68, h: 68, opacidade: 1, tamanho: 34 }),
    ];
  }
  if (estilo === "atencao") {
    return [
      faixa("Faixa de atenção", 0, 62, cor),
      tex("Listras de atenção", "listras", cor2, { y: 0, h: 62, opacidade: 1, tamanho: 34, giro: 45 }),
      faixa("Faixa de atenção", H - 62, 62, cor),
      tex("Listras de atenção", "listras", cor2, { y: H - 62, h: 62, opacidade: 1, tamanho: 34, giro: 45 }),
    ];
  }
  return [
    f("Fio de cima", "retangulo", { x: 70, y: 46, w: W - 140, h: 3 }, cor),
    f("Fio fino de cima", "retangulo", { x: 70, y: 60, w: W - 140, h: 1.5 }, cor2),
    f("Fio de baixo", "retangulo", { x: 70, y: H - 49, w: W - 140, h: 3 }, cor),
    f("Fio fino de baixo", "retangulo", { x: 70, y: H - 61.5, w: W - 140, h: 1.5 }, cor2),
  ];
}

/* Divisor: dois fios e um enfeite no meio. */
function divisor(cx: number, y: number, w: number, cor: string, enfeite: "estrela" | "coracao" | "ponto" | "pata" = "estrela"): Peca[] {
  const fio = (w - 90) / 2;
  const meio =
    enfeite === "coracao" ? fc("Enfeite", "coracao", cx, y, 34, 30, cor) : enfeite === "pata" ? linha("Enfeite", LUCIDE.pata, cx, y, 40, cor, 2) : enfeite === "ponto" ? fc("Enfeite", "circulo", cx, y, 14, 14, cor) : ic("Enfeite", "estrela-4", cx, y, 40, cor, { preenchido: true, espessura: 0.5 });
  return [f("Fio do divisor", "retangulo", { x: cx - w / 2, y: y - 2, w: fio, h: 4 }, cor, { raio: 2 }), meio, f("Fio do divisor", "retangulo", { x: cx + 45, y: y - 2, w: fio, h: 4 }, cor, { raio: 2 })];
}

/* Selo redondo: disco, frase em volta e o miolo. */
function selo(nome: string, cx: number, cy: number, d: number, cor: string, corDaLetra: string, frase: string, miolo: Peca[] = [], rot = 0): Peca[] {
  return [
    fc(nome, "circulo", cx, cy, d, d, cor, { sombra: sombraDe("#000000", 8, 18, 0.22), rot }),
    fc(`${nome} (aro)`, "circulo", cx, cy, d * 0.62, d * 0.62, nada, { borda: { cor: corDaLetra, largura: Math.max(2, d * 0.012) } }),
    tc(`${nome} (frase)`, cx, cy - d * 0.42, d * 0.84, d * 0.84, { texto: frase, fonte: "space-grotesk", peso: 700, tamanho: d * 0.085, espaco: 0.18, cor: corDaLetra, curva: 100 }, rot),
    ...miolo,
  ];
}

/* Moldura costurada (tracejada) em volta de uma zona. */
function costura(z: Zona, cor: string, espessura = 4): PecaDeTraco {
  return traco("Costura", [[z.x, z.y], [z.x + z.w, z.y], [z.x + z.w, z.y + z.h], [z.x, z.y + z.h]], cor, espessura, { fechado: true, suave: false, tracejado: "tracejado" });
}

/* Aro tracejado (círculo feito com o traço suave). */
function aroTracejado(cx: number, cy: number, r: number, cor: string, espessura = 4, tracejado: PecaDeTraco["tracejado"] = "tracejado"): PecaDeTraco {
  const pontos: Array<[number, number]> = Array.from({ length: 16 }, (_, i) => [cx + r * Math.cos((i / 16) * Math.PI * 2), cy + r * Math.sin((i / 16) * Math.PI * 2)]);
  return traco("Aro tracejado", pontos, cor, espessura, { fechado: true, tracejado });
}

/* Varal: o fio em curva e o que pendura nele (bandeirinha, lâmpada...). */
function varal(x0: number, x1: number, y: number, queda: number, quantos: number, cor: string, pendurar: (x: number, y: number, i: number, giro: number) => Peca[]): Peca[] {
  const ponto = (s: number): [number, number] => [x0 + s * (x1 - x0), y + queda * 4 * s * (1 - s)];
  const fio = traco("Fio do varal", Array.from({ length: 11 }, (_, i) => ponto(i / 10)), cor, 4);
  const coisas = Array.from({ length: quantos }, (_, i) => {
    const s = (i + 0.5) / quantos;
    const [x, yy] = ponto(s);
    const giro = (Math.atan((queda * 4 * (1 - 2 * s)) / (x1 - x0)) * 180) / Math.PI;
    return pendurar(x, yy, i, giro);
  }).flat();
  return [fio, ...coisas];
}

/* Bandeirinha triangular pendurada. */
const bandeirinha = (x: number, y: number, lado: number, cor: string, giro: number) => f("Bandeirinha", "triangulo", { x: x - lado / 2, y: y - 4, w: lado, h: lado * 1.1, rot: 180 + giro }, cor);

/* Trilha de pegadas em diagonal (patinhas, passos). */
function trilha(desenho: string, de: [number, number], ate: [number, number], quantos: number, lado: number, cor: string, opacidade = 0.55): Peca[] {
  const giro = (Math.atan2(ate[1] - de[1], ate[0] - de[0]) * 180) / Math.PI + 90;
  return Array.from({ length: quantos }, (_, i) => {
    const s = i / Math.max(1, quantos - 1);
    const lado2 = i % 2 ? 1 : -1;
    const nx = -(ate[1] - de[1]);
    const ny = ate[0] - de[0];
    const n = Math.hypot(nx, ny) || 1;
    return linha("Pegada", desenho, de[0] + s * (ate[0] - de[0]) + (nx / n) * lado * 0.35 * lado2, de[1] + s * (ate[1] - de[1]) + (ny / n) * lado * 0.35 * lado2, lado, cor, 1.8, { rot: giro, opacidade });
  });
}

/* Polaroid girada: moldura, foto em cima e legenda embaixo, girando juntas em volta do centro. */
function polaroid(nome: string, src: string, cx: number, cy: number, w: number, h: number, rot: number, legenda: string, enquadre?: PecaDeImagem["enquadre"]): Peca[] {
  const m = w * 0.065;
  const s = w - m * 2;
  const r = (rot * Math.PI) / 180;
  const girar = (dx: number, dy: number) => ({ x: cx + dx * Math.cos(r) - dy * Math.sin(r), y: cy + dx * Math.sin(r) + dy * Math.cos(r) });
  const centroDaFoto = girar(0, -h / 2 + m + s / 2);
  const centroDaLegenda = girar(0, -h / 2 + m + s + (h - m - s) / 2);
  const pregador = girar(0, -h / 2 + 6);
  const altLegenda = h - m - s;
  return [
    f(`${nome} (moldura)`, "retangulo", { x: cx - w / 2, y: cy - h / 2, w, h, rot }, "#FFFFFF", { raio: 6, sombra: sombraDe("#000000", 18, 30, 0.3) }),
    foto(nome, src, { x: centroDaFoto.x - s / 2, y: centroDaFoto.y - s / 2, w: s, h: s, rot }, enquadre ? { enquadre } : {}),
    t(`${nome} (legenda)`, { x: centroDaLegenda.x - s / 2, y: centroDaLegenda.y - altLegenda / 2, w: s, h: altLegenda, rot }, { texto: legenda, fonte: "caveat", peso: 700, tamanho: altLegenda * 0.6, cor: "#2B2B2B", alinhamento: "centro" }),
    f(`${nome} (pregador)`, "retangulo", { x: pregador.x - 16, y: pregador.y - 38, w: 32, h: 76, rot }, "#C98B4F", { raio: 6, sombra: sombraDe("#000000", 4, 6, 0.3) }),
  ];
}

/* Ramo de folhas em arco (coroa) em volta de um ponto. */
function ramo(cx: number, cy: number, r: number, de: number, ate: number, quantas: number, cor: string, lado = 70): Peca[] {
  return Array.from({ length: quantas }, (_, i) => {
    const a = ((de + ((ate - de) * i) / Math.max(1, quantas - 1)) * Math.PI) / 180;
    return linha("Folha", LUCIDE.folha, cx + r * Math.cos(a), cy + r * Math.sin(a), lado, cor, 1.4, { rot: (a * 180) / Math.PI + (ate > de ? 45 : -135) });
  });
}

/* ============================================================== BICHINHOS */

/* Gato e café no amarelo-sol: aro de conchas, selo, trilha de patinhas no meio e frase redonda. */
function gatinhoDoCafe(): DocumentoDoBanner {
  const MARROM = "#3B1F0E";
  const LARANJA = "#FF6B1A";
  const frase: Zona = { x: B - 560, y: 220, w: 1120, h: 470 };
  const desenho: Zona = { x: A - 470, y: 120, w: 980, h: 900 };
  return caneca({ cor: "#FFD43B", cor2: "#FFC21A", angulo: 160 }, [
    tex("Bolinhas", "pontos", "#FFFFFF", { opacidade: 0.3, tamanho: 26 }),
    ...aro("conchas", LARANJA, "#FFFFFF"),
    fc("Círculo", "circulo", A, 575, 800, 800, LARANJA),
    aroTracejado(A, 575, 445, MARROM, 5),
    fc("Brilho do círculo", "circulo", A - 120, 420, 360, 360, radial("#FFFFFF", LARANJA, 0.35, 0)),
    fig("Croissant", "croissant", A - 330, 840, 230, { rot: -14 }),
    fig("Gato", "gato", A - 30, 590, 640, { sombra: sombraDe("#8A3A00", 24, 40, 0.35) }),
    fig("Café", "cafe", A + 290, 820, 290, { rot: 8 }),
    traco("Fumaça", [[A + 250, 650], [A + 275, 610], [A + 250, 570], [A + 275, 530]], "#FFFFFF", 7, { opacidade: 0.85 }),
    traco("Fumaça", [[A + 320, 650], [A + 345, 610], [A + 320, 570], [A + 345, 530]], "#FFFFFF", 7, { opacidade: 0.85 }),
    ...selo("Selo", A + 390, 250, 230, "#FFFFFF", MARROM, "100% RONRONADO • 100% RONRONADO • ", [linha("Pata do selo", LUCIDE.pata, A + 390, 250, 76, LARANJA, 2)], -10),
    fig("Patinhas", "patinhas", A - 400, 250, 140, { rot: -20 }),
    ...trilha(LUCIDE.pata, [1060, 940], [1250, 230], 6, 70, MARROM, 0.35),
    miuda("Assunto", B, 160, 900, "café • gato • carinho", MARROM, 32),
    tc("Frase", B, 220, 1120, 470, {
      texto: "Primeiro\no café,\ndepois o miau.",
      html: html(`Primeiro<br>o café,<br>depois o ${trecho("miau.", { cor: LARANJA })}`),
      fonte: "lilita-one",
      peso: 400,
      tamanho: 138,
      linha: 1.02,
      cor: MARROM,
    }),
    ...divisor(B, 770, 520, MARROM, "pata"),
    tc("Assinatura", B, 800, 900, 110, { texto: "café & ronrons", fonte: "caveat", peso: 700, tamanho: 84, cor: "#8A4B0F" }),
    tc("Etiqueta", B, 925, 600, 80, { texto: "SERVIDO COM AMOR", fonte: "fredoka", peso: 700, tamanho: 32, espaco: 0.12, cor: "#FFFFFF", fundo: { forma: "etiqueta", cor: MARROM } }),
    ...confetes(11, 34, ["#FFFFFF", LARANJA, MARROM], [frase, desenho, { x: B - 470, y: 140, w: 940, h: 90 }, { x: B - 300, y: 760, w: 600, h: 260 }, { x: 1020, y: 120, w: 340, h: 880 }, { x: 0, y: 0, w: W, h: 100 }, { x: 0, y: H - 100, w: W, h: 100 }], { tipos: ["bola", "anel", "estrela", "risco"] }),
  ]);
}

/* O pet recortado com contorno de adesivo, selo "bom garoto", fichinha e o nome grande. */
function melhorAmigo(): DocumentoDoBanner {
  const AMARELO = "#FFD23F";
  const ESCURO = "#0A5C55";
  const CORAL = "#FF5A5F";
  const texto_: Zona = { x: B - 560, y: 120, w: 1120, h: 860 };
  const desenho: Zona = { x: A - 470, y: 110, w: 960, h: 920 };
  return caneca({ cor: "#16C7B4", cor2: "#0A8F86", angulo: 160 }, [
    tex("Ondas", "ondas", "#FFFFFF", { opacidade: 0.08, tamanho: 60 }),
    ...aro("pontos", ESCURO, "#FFFFFF"),
    fc("Círculo", "circulo", A, 565, 840, 840, AMARELO),
    aroTracejado(A, 565, 470, "#FFFFFF", 5),
    foto("Pet", FOTO("pet-corgi-recorte"), { x: A - 430, y: 160, w: 860, h: 870 }, { ajuste: "conter", adesivo: { cor: "#FFFFFF", largura: 22 }, sombra: sombraDe("#04403B", 26, 44, 0.4) }),
    fig("Osso", "osso", A + 380, 210, 160, { rot: 24 }),
    fig("Bola", "bola-de-futebol", A - 380, 860, 150, { rot: -12 }),
    ...selo("Selo", A + 380, 840, 240, CORAL, "#FFFFFF", "BOM GAROTO • BOM GAROTO • ", [fig("Coração do selo", "coracao-branco", A + 380, 840, 90, { semAdesivo: true })], 8),
    ...trilha(LUCIDE.pata, [1040, 960], [1320, 150], 6, 66, "#FFFFFF", 0.4),
    tc("Melhor amigo", B, 130, 1040, 150, { texto: "o melhor amigo do mundo", fonte: "caveat", peso: 700, tamanho: 92, cor: AMARELO, curva: 14 }),
    tc("Nome", B, 270, 1100, 400, { texto: "THOR", fonte: "anton", peso: 400, tamanho: 380, linha: 1, cor: "#FFF6E0", espaco: 0.04, efeito: { tipo: "levantar", cor: ESCURO, forca: 0.7 } }),
    ...divisor(B, 715, 640, AMARELO, "pata"),
    ...["Corgi", "3 anos", "Comilão"].map((rotulo, i) => tc(`Ficha ${rotulo}`, B - 330 + i * 330, 770, 290, 84, { texto: rotulo, fonte: "fredoka", peso: 700, tamanho: 42, cor: ESCURO, fundo: { forma: "pilula", cor: "#FFFFFF" } })),
    tc("Desde", B, 900, 420, 80, { texto: "desde 2021", fonte: "space-grotesk", peso: 700, tamanho: 34, espaco: 0.3, caixaAlta: true, cor: "#FFFFFF" }),
    ...confetes(12, 26, ["#FFFFFF", AMARELO], [texto_, desenho, { x: 1000, y: 120, w: 360, h: 880 }], { tipos: ["bola", "anel", "mais"], opacidade: 0.75 }),
  ]);
}

/* Gatinho recortado em corações, novelo e peixinho; "MÃE DE GATO" com a palavra vazada. */
function maeDeGato(): DocumentoDoBanner {
  const ROSA_ESCURO = "#C2185B";
  const texto_: Zona = { x: B - 580, y: 140, w: 1160, h: 860 };
  const desenho: Zona = { x: A - 500, y: 100, w: 1000, h: 940 };
  return caneca({ cor: "#FF5FA2", cor2: "#FF2E7E", angulo: 135 }, [
    tex("Bolinhas", "pontos", "#FFFFFF", { opacidade: 0.14, tamanho: 20 }),
    ...aro("conchas", "#FFFFFF", "#FFC2DC"),
    fc("Coração", "coracao", A, 540, 960, 900, "#FFFFFF", { opacidade: 0.22 }),
    fc("Coração do meio", "coracao", A, 560, 760, 700, nada, { borda: { cor: "#FFFFFF", largura: 6 }, opacidade: 0.9 }),
    fig("Novelo", "novelo-de-la", A + 370, 860, 210, { rot: 12 }),
    traco("Fio do novelo", [[A + 300, 900], [A + 200, 980], [A + 60, 950], [A - 60, 1000], [A - 200, 960]], "#7B61FF", 6, { opacidade: 0.9 }),
    foto("Gato", FOTO("gato-laranja-recorte"), { x: A - 470, y: 330, w: 940, h: 720 }, { ajuste: "conter", adesivo: { cor: "#FFFFFF", largura: 18 }, sombra: sombraDe("#7A0B3A", 18, 30, 0.35) }),
    fig("Peixe", "peixe", A - 390, 250, 170, { rot: -16 }),
    fig("Coração brilhante", "coracao-brilhante", A + 400, 220, 170, { rot: 14 }),
    ...trilha(LUCIDE.pata, [1060, 950], [1320, 160], 6, 64, "#FFFFFF", 0.45),
    miuda("Certificado", B, 150, 800, "certificado oficial", "#FFE3F0", 32),
    tc("Título", B, 210, 1160, 500, {
      texto: "MÃE DE\nGATO",
      html: html(`MÃE DE<br>${trecho("GATO", { vazado: { cor: "#FFFFFF", largura: 7 } })}`),
      fonte: "archivo-black",
      peso: 400,
      tamanho: 230,
      linha: 0.98,
      cor: "#FFFFFF",
    }),
    ...divisor(B, 735, 560, "#FFFFFF", "coracao"),
    tc("Orgulho", B, 760, 900, 120, { texto: "e com muito orgulho", fonte: "caveat", peso: 700, tamanho: 88, cor: "#FFE3F0" }),
    tc("Nível", B, 900, 520, 80, { texto: "NÍVEL: PROFISSIONAL", fonte: "fredoka", peso: 700, tamanho: 34, espaco: 0.1, cor: ROSA_ESCURO, fundo: { forma: "pilula", cor: "#FFFFFF" } }),
    fig("Gato apaixonado", "gato-apaixonado", B + 470, 880, 170, { rot: 10 }),
    ...confetes(13, 28, ["#FFFFFF", "#FFC2DC", "#FFE066"], [texto_, desenho, { x: 1000, y: 120, w: 380, h: 880 }], { tipos: ["coracao", "bola", "estrela"], opacidade: 0.85 }),
  ]);
}

/* O panda nas nuvens, varal de luzinhas, lua e bateria no fim: noite roxa e lima. */
function modoSoneca(): DocumentoDoBanner {
  const LIMA = "#C6FF4D";
  const LILAS = "#B7AEFF";
  const texto_: Zona = { x: B - 560, y: 170, w: 1120, h: 860 };
  const desenho: Zona = { x: A - 520, y: 170, w: 1040, h: 900 };
  return caneca({ cor: "#1D1B4F", cor2: "#3A2C8F", angulo: 160 }, [
    tex("Céu", "pontos", "#FFFFFF", { opacidade: 0.18, tamanho: 9 }),
    ...varal(60, W - 60, 40, 90, 22, "#5D53B8", (x, y, i) => [fc("Luzinha", "circulo", x, y + 18, 26, 30, i % 3 === 0 ? "#FFE58A" : i % 3 === 1 ? LIMA : "#FF9BD2", { efeitos: [{ tipo: "brilho", cor: i % 3 === 0 ? "#FFE58A" : i % 3 === 1 ? LIMA : "#FF9BD2", x: 0, y: 0, desfoque: 22, opacidade: 0.9 }] })]),
    fig("Lua", "lua", A + 360, 300, 230, { semAdesivo: true, efeitos: [{ tipo: "brilho", cor: "#FFE58A", x: 0, y: 0, desfoque: 40, opacidade: 0.7 }] }),
    fig("Nuvem", "nuvem", A - 230, 880, 520, { semAdesivo: true }),
    fig("Nuvem", "nuvem", A + 250, 900, 440, { semAdesivo: true }),
    fig("Panda", "panda", A - 20, 610, 560, { sombra: sombraDe("#0D0B2E", 20, 30, 0.35) }),
    fig("Zzz", "zzz", A + 320, 470, 210, { rot: 8 }),
    fig("Ursinho", "ursinho-de-pelucia", A - 400, 380, 190, { rot: -12 }),
    fig("Estrela", "estrela-brilhante", A - 120, 250, 110, { semAdesivo: true }),
    fig("Estrela cadente", "estrela", MEIO, 300, 120, { semAdesivo: true, rot: 12 }),
    tc("Título", B, 200, 1120, 540, {
      texto: "MODO\nSONECA\nATIVADO",
      html: html(`MODO<br>${trecho("SONECA", { cor: LIMA })}<br>ATIVADO`),
      fonte: "unbounded",
      peso: 800,
      tamanho: 142,
      linha: 1.08,
      cor: "#FFFFFF",
    }),
    /* A bateria quase no fim. */
    f("Bateria", "retangulo", { x: B - 200, y: 790, w: 330, h: 120 }, nada, { raio: 22, borda: { cor: "#FFFFFF", largura: 8 } }),
    f("Polo da bateria", "retangulo", { x: B + 136, y: 826, w: 22, h: 48 }, "#FFFFFF", { raio: 6 }),
    f("Carga", "retangulo", { x: B - 182, y: 808, w: 52, h: 84 }, "#FF4D6D", { raio: 10 }),
    tc("Carga em %", B - 30, 800, 220, 100, { texto: "5%", fonte: "unbounded", peso: 800, tamanho: 54, cor: "#FFFFFF" }),
    t("Não perturbe", { x: B + 200, y: 790, w: 330, h: 120 }, { texto: "não\nperturbe", fonte: "caveat", peso: 700, tamanho: 58, linha: 0.95, cor: LILAS }),
    miuda("Até o café", B, 950, 900, "recarregando até o café", LILAS, 30),
    ...confetes(14, 30, ["#FFFFFF", LIMA, LILAS], [texto_, desenho, { x: 0, y: 0, w: W, h: 150 }], { tipos: ["estrela", "bola"], min: 10, max: 26, opacidade: 0.8 }),
  ]);
}

/* ================================================================ ANIMADO */

/* Carinha derretendo na explosão, balão "socorro!", despertador e a medida de café: laranja vivo. */
function segundaFeira(): DocumentoDoBanner {
  const AMARELO = "#FFE14D";
  const TINTA = "#3A0B00";
  const texto_: Zona = { x: B - 580, y: 170, w: 1160, h: 840 };
  const desenho: Zona = { x: A - 500, y: 110, w: 1040, h: 920 };
  return caneca({ cor: "#FF4D2E", cor2: "#FF8A1A", angulo: 120 }, [
    tex("Raios", "raios", "#FFFFFF", { opacidade: 0.12, quantidade: 30, centroX: 25, centroY: 50 }),
    tex("Retícula", "meio-tom", TINTA, { opacidade: 0.12, tamanho: 18, giro: 135 }),
    ...aro("xadrez", TINTA, "#FFFFFF"),
    fc("Explosão", "explosao", A, 580, 900, 900, AMARELO, { pontas: 18, profundidade: 0.12, borda: { cor: TINTA, largura: 6 } }),
    fig("Derretendo", "derretendo", A - 30, 600, 520),
    fig("Revirando os olhos", "revirando-os-olhos", A + 330, 330, 220, { rot: 12 }),
    fig("Café", "cafe", A - 340, 840, 250, { rot: -10 }),
    fig("Despertador", "despertador", A + 340, 850, 220, { rot: 10 }),
    tc("Balão", A - 330, 210, 330, 190, { texto: "socorro!", fonte: "bangers", peso: 400, tamanho: 70, cor: TINTA, fundo: { forma: "balao", cor: "#FFFFFF" } }, -8),
    fc("Raio do meio", "raio", MEIO - 10, 330, 120, 170, AMARELO, { rot: 12, borda: { cor: TINTA, largura: 4 } }),
    fc("Raio do meio", "raio", MEIO + 30, 800, 90, 130, "#FFFFFF", { rot: -14 }),
    tc("Segunda", B, 200, 1160, 210, { texto: "SEGUNDA-FEIRA", fonte: "bangers", peso: 400, tamanho: 190, espaco: 0.03, cor: "#FFFFFF", efeito: { tipo: "eco", cor: "#B32000", forca: 0.6 } }),
    tc("De novo", B, 410, 1000, 240, { texto: "de novo?", fonte: "pacifico", peso: 400, tamanho: 160, cor: AMARELO }, -3),
    miuda("Doses", B, 700, 1000, "doses de café necessárias", "#FFFFFF", 30),
    ...[0, 1, 2, 3, 4].map((i) => linha(`Dose ${i + 1}`, LUCIDE.cafe, B - 280 + i * 140, 830, 110, i < 4 ? "#FFFFFF" : AMARELO, 1.8, { opacidade: i < 4 ? 1 : 1 })),
    tc("Mais doses", B + 430, 790, 150, 90, { texto: "+1", fonte: "bangers", peso: 400, tamanho: 80, cor: AMARELO }),
    ...confetes(15, 26, ["#FFFFFF", AMARELO], [texto_, desenho, { x: 0, y: 0, w: W, h: 90 }, { x: 0, y: H - 90, w: W, h: 90 }], { tipos: ["mais", "risco", "estrela"], opacidade: 0.85 }),
  ]);
}

/* Controle com brilho neon, painel de jogo (vidas, pontos), "PAUSA PRO CAFÉ" e "continuar?". */
function pausaProCafe(): DocumentoDoBanner {
  const ROSA = "#FF2BD6";
  const CIANO = "#22E4FF";
  const LILAS = "#B9A6FF";
  const texto_: Zona = { x: B - 560, y: 120, w: 1120, h: 900 };
  const desenho: Zona = { x: A - 520, y: 110, w: 1040, h: 920 };
  return caneca({ cor: "#12062B", cor2: "#2A0B5C", angulo: 160 }, [
    tex("Grade", "xadrez", CIANO, { opacidade: 0.05, tamanho: 70 }),
    f("Neon de cima", "retangulo", { x: 0, y: 44, w: W, h: 5 }, CIANO, { efeitos: [{ tipo: "brilho", cor: CIANO, x: 0, y: 0, desfoque: 18, opacidade: 1 }] }),
    f("Neon de baixo", "retangulo", { x: 0, y: H - 49, w: W, h: 5 }, ROSA, { efeitos: [{ tipo: "brilho", cor: ROSA, x: 0, y: 0, desfoque: 18, opacidade: 1 }] }),
    t("Pontos", { x: 90, y: 90, w: 600, h: 60 }, { texto: "SCORE 999.999", fonte: "space-grotesk", peso: 700, tamanho: 38, espaco: 0.2, cor: CIANO }),
    ...[0, 1, 2].map((i) => fig(`Vida ${i + 1}`, "coracao-vermelho", A + 220 + i * 80, 120, 64, { semAdesivo: true })),
    fc("Brilho", "circulo", A, 590, 900, 900, radial(ROSA, "#12062B", 0.45, 0)),
    fig("Controle", "controle-de-videogame", A, 590, 640, { semAdesivo: true, efeitos: [{ tipo: "brilho", cor: ROSA, x: 0, y: 0, desfoque: 46, opacidade: 0.8 }] }),
    fig("Café", "cafe", A + 350, 330, 220, { rot: 12 }),
    fig("Joystick", "joystick", A - 380, 850, 210, { rot: -12 }),
    fig("Fone", "fone-de-ouvido", A - 380, 340, 210, { rot: -14 }),
    fig("Moeda", "medalha-de-ouro", A + 380, 860, 150, { rot: 10 }),
    ...[0, 1, 2, 3].map((i) => f(`Pixel ${i + 1}`, "retangulo", { x: MEIO - 40 + (i % 2) * 44, y: 470 + Math.floor(i / 2) * 44 + (i === 3 ? 120 : 0), w: 30, h: 30 }, i % 2 ? CIANO : ROSA, { opacidade: 0.8 })),
    tc("Pausa", B, 140, 1100, 280, { texto: "PAUSA", fonte: "russo-one", peso: 400, tamanho: 240, espaco: 0.06, cor: "#FFFFFF", efeito: { tipo: "neon", cor: ROSA, forca: 0.8 } }),
    tc("Pro café", B, 420, 1100, 220, { texto: "PRO CAFÉ", fonte: "russo-one", peso: 400, tamanho: 170, espaco: 0.06, cor: CIANO, efeito: { tipo: "neon", cor: CIANO, forca: 0.7 } }),
    tc("Continuar", B, 700, 700, 80, { texto: "CONTINUAR?", fonte: "space-grotesk", peso: 700, tamanho: 44, espaco: 0.3, cor: "#FFFFFF" }),
    tc("Sim", B - 150, 790, 240, 84, { texto: "▶ SIM", fonte: "space-grotesk", peso: 700, tamanho: 40, espaco: 0.2, cor: "#12062B", fundo: { forma: "pilula", cor: CIANO } }),
    tc("Sim 2", B + 150, 790, 240, 84, { texto: "SIM", fonte: "space-grotesk", peso: 700, tamanho: 40, espaco: 0.2, cor: CIANO, fundo: { forma: "pilula", cor: "#2A0B5C" } }),
    miuda("Game over", B, 920, 1000, "game over para o sono", LILAS, 30),
    ...confetes(16, 30, [CIANO, ROSA, LILAS], [texto_, desenho, { x: 0, y: 0, w: W, h: 170 }, { x: 0, y: H - 80, w: W, h: 80 }], { tipos: ["mais", "bola"], min: 10, max: 22, opacidade: 0.7 }),
  ]);
}

/* ============================================================ MINIMALISTA */

/* Xícara de traço fino com vapor no círculo terracota, fios na borda e "bom dia" em serifa. */
function bomDia(): DocumentoDoBanner {
  const TINTA = "#1E1E1E";
  const TERRACOTA = "#E2582B";
  const AREIA = "#F6E7DA";
  return caneca({ cor: "#FFFFFF" }, [
    ...aro("fios", TINTA, TERRACOTA),
    fc("Halo", "circulo", A, 561, 760, 760, AREIA),
    fc("Círculo", "circulo", A, 561, 600, 600, TERRACOTA),
    aroTracejado(A, 561, 345, TINTA, 2.5, "pontilhado"),
    linha("Café", LUCIDE.cafe, A + 20, 600, 380, "#FFFFFF", 1.05),
    traco("Vapor", [[A - 40, 420], [A - 15, 380], [A - 40, 340], [A - 15, 300]], "#FFFFFF", 5),
    traco("Vapor", [[A + 40, 420], [A + 65, 380], [A + 40, 340], [A + 65, 300]], "#FFFFFF", 5),
    linha("Sol", LUCIDE.sol, A - 330, 240, 90, TERRACOTA, 1.3),
    linha("Grão", LUCIDE.grao, A + 360, 880, 70, TINTA, 1.3, { rot: 20 }),
    linha("Grão", LUCIDE.grao, A + 420, 820, 50, TERRACOTA, 1.3, { rot: -30 }),
    miuda("Número", MEIO, 520, 260, "nº 01", TERRACOTA, 26),
    f("Fio do meio", "retangulo", { x: MEIO - 1, y: 300, w: 2, h: 200 }, TINTA, { opacidade: 0.3 }),
    f("Fio do meio", "retangulo", { x: MEIO - 1, y: 580, w: 2, h: 200 }, TINTA, { opacidade: 0.3 }),
    miuda("Hora", B, 210, 800, "07:00 · café da manhã", "#8A8A8A", 28),
    tc("Bom dia", B, 290, 1100, 300, { texto: "bom dia", fonte: "dm-serif-display", peso: 400, tamanho: 270, cor: TINTA, italico: true }),
    ...divisor(B, 650, 360, TERRACOTA, "ponto"),
    miuda("Comece", B, 690, 900, "comece pelo café", TERRACOTA, 44),
    tc("Rodapé", B, 820, 900, 90, { texto: "um dia de cada vez, uma xícara por vez", fonte: "fraunces", peso: 400, tamanho: 40, italico: true, cor: "#6B6B6B" }),
  ]);
}

/* Monograma com coroa de folhas nas duas faces (aparece com qualquer mão): verde-garrafa e dourado. */
function monograma(): DocumentoDoBanner {
  const VERDE = "#0F5132";
  const OURO = "#C9A227";
  const lado = (cx: number): Peca[] => [
    fc("Aro", "circulo", cx, 480, 680, 680, nada, { borda: { cor: VERDE, largura: 6 } }),
    fc("Aro fino", "circulo", cx, 480, 640, 640, nada, { borda: { cor: OURO, largura: 2 } }),
    ...ramo(cx, 480, 372, 205, 115, 8, OURO, 54),
    ...ramo(cx, 480, 372, -25, 65, 8, OURO, 54),
    ic("Estrelinha", "estrela-4", cx, 112, 46, OURO, { preenchido: true, espessura: 0.5 }),
    tc("Inicial", cx, 170, 600, 560, { texto: "A", fonte: "abril-fatface", peso: 400, tamanho: 500, linha: 1, cor: VERDE }),
    /* O nome corta o aro embaixo: o fundo branco abre a passagem dele. */
    tc("Nome", cx, 710, 780, 210, { texto: "Ana Clara", fonte: "great-vibes", peso: 400, tamanho: 136, cor: OURO, fundo: { forma: "retangulo", cor: "#FFFFFF", raio: 0 } }),
    miuda("Desde", cx, 930, 500, "est. 2026", VERDE, 28, "cinzel"),
  ];
  return caneca({ cor: "#FFFFFF" }, [
    ...aro("fios", VERDE, OURO),
    ...lado(A),
    ...lado(B),
    ic("Losango do meio", "estrela-4", MEIO, 561, 60, OURO, { preenchido: true, espessura: 0.5 }),
    ...[-1, 1].map((s) => fc("Ponto do meio", "circulo", MEIO, 561 + s * 70, 10, 10, OURO)),
  ]);
}

/* Paisagem geométrica completa (céu, sol com raios, montanhas com neve, lago, pássaros) e "Respira.". */
function respira(): DocumentoDoBanner {
  const NAVY = "#1B2A49";
  const VERDE = "#2A9D8F";
  const CORAL = "#E76F51";
  const AREIA = "#F4E3C9";
  const MOSTARDA = "#F4A261";
  return caneca({ cor: "#FFFFFF" }, [
    ...aro("fios", NAVY, CORAL),
    /* A janela em arco com a paisagem dentro. */
    f("Céu", "retangulo", { x: A - 400, y: 130, w: 800, h: 790 }, AREIA, { raio: 400 }),
    fc("Sol", "circulo", A + 120, 380, 240, 240, MOSTARDA),
    linha("Raios do sol", LUCIDE.sol, A + 120, 380, 360, MOSTARDA, 0.9, { opacidade: 0.6 }),
    linha("Pássaro", LUCIDE.passaro, A - 200, 300, 60, NAVY, 1.6),
    linha("Pássaro", LUCIDE.passaro, A - 120, 260, 44, NAVY, 1.6),
    f("Montanha grande", "triangulo", { x: A - 400, y: 420, w: 560, h: 420 }, NAVY),
    f("Neve", "triangulo", { x: A - 175, y: 420, w: 110, h: 82 }, "#FFFFFF"),
    f("Montanha do meio", "triangulo", { x: A - 120, y: 360, w: 520, h: 480 }, VERDE),
    f("Neve", "triangulo", { x: A + 88, y: 360, w: 104, h: 96 }, "#FFFFFF"),
    f("Morro", "triangulo", { x: A + 130, y: 600, w: 300, h: 240 }, CORAL),
    f("Lago", "retangulo", { x: A - 400, y: 840, w: 800, h: 80 }, "#9ED8D1"),
    ...[0, 1, 2].map((i) => f("Reflexo", "retangulo", { x: A - 250 + i * 160, y: 860 + (i % 2) * 22, w: 120, h: 6 }, "#FFFFFF", { raio: 3 })),
    f("Moldura da janela", "retangulo", { x: A - 400, y: 130, w: 800, h: 790 }, nada, { raio: 400, borda: { cor: NAVY, largura: 6 } }),
    miuda("Coordenadas", A, 960, 700, "22°54' S · 43°10' O", NAVY, 26),
    linha("Folha", LUCIDE.folha, MEIO, 300, 80, VERDE, 1.3, { rot: -20 }),
    f("Fio do meio", "retangulo", { x: MEIO - 1, y: 380, w: 2, h: 360 }, NAVY, { opacidade: 0.25 }),
    linha("Folha", LUCIDE.folha, MEIO, 820, 80, CORAL, 1.3, { rot: 160 }),
    miuda("Lembrete", B, 200, 700, "lembrete do dia", CORAL, 30),
    tc("Respira", B, 260, 1100, 290, { texto: "Respira.", fonte: "fraunces", peso: 700, tamanho: 240, cor: NAVY }),
    ...divisor(B, 600, 420, VERDE, "ponto"),
    tc("O resto", B, 640, 1100, 240, { texto: "O resto a gente\nresolve.", fonte: "fraunces", peso: 400, tamanho: 96, linha: 1.15, italico: true, cor: CORAL }),
    linha("Ondinhas", LUCIDE.ondas, B, 930, 90, VERDE, 1.3),
  ]);
}

/* ============================================================ TIPOGRÁFICO */

/* Faixas de "atenção" na borda, xícara com vapor e as letras gigantes "NÃO FALE COMIGO antes do CAFÉ". */
function naoFaleComigo(): DocumentoDoBanner {
  const OURO = "#FFC531";
  const texto_: Zona = { x: B - 580, y: 90, w: 1160, h: 940 };
  const desenho: Zona = { x: A - 480, y: 140, w: 960, h: 880 };
  return caneca({ cor: "#141414" }, [
    tex("Grão", "grao", "#FFFFFF", { opacidade: 0.06 }),
    ...aro("atencao", OURO, "#141414"),
    fc("Luz", "circulo", A, 580, 900, 900, radial(OURO, "#141414", 0.35, 0)),
    aroTracejado(A, 590, 400, OURO, 4),
    fig("Café", "cafe", A, 600, 580, { rot: -6 }),
    traco("Vapor", [[A - 60, 300], [A - 30, 250], [A - 60, 200], [A - 30, 150]], "#FFFFFF", 8, { opacidade: 0.8 }),
    traco("Vapor", [[A + 40, 300], [A + 70, 250], [A + 40, 200], [A + 70, 150]], "#FFFFFF", 8, { opacidade: 0.8 }),
    fig("Brilhos", "brilhos", A + 360, 300, 160, { semAdesivo: true }),
    tc("Placa", A - 300, 870, 380, 90, { texto: "ZONA SEM CAFÉ", fonte: "space-grotesk", peso: 700, tamanho: 34, espaco: 0.14, cor: "#141414", fundo: { forma: "etiqueta", cor: OURO } }, -6),
    ...["!", "!", "!"].map((c, i) => tc(`Exclamação ${i + 1}`, MEIO - 50 + i * 50, 470 + (i % 2) * 30, 60, 160, { texto: c, fonte: "anton", peso: 400, tamanho: 130, cor: i === 1 ? OURO : "#FFFFFF", opacidade: 0.9 }, -8 + i * 8)),
    tc("Não fale", B, 110, 1150, 180, { texto: "NÃO FALE COMIGO", fonte: "anton", peso: 400, tamanho: 146, espaco: 0.02, cor: "#FFFFFF" }),
    tc("Antes do", B, 270, 800, 200, { texto: "antes do", fonte: "pacifico", peso: 400, tamanho: 120, cor: OURO }, -4),
    tc("Café", B, 440, 1100, 470, { texto: "CAFÉ", fonte: "anton", peso: 400, tamanho: 430, linha: 1, espaco: 0.03, cor: OURO }),
    miuda("Aviso", B, 930, 900, "sério. é para o seu bem.", "#BDBDBD", 28),
    ...confetes(17, 14, ["#FFFFFF", OURO], [texto_, desenho, { x: 0, y: 0, w: W, h: 100 }, { x: 0, y: H - 100, w: W, h: 100 }, { x: MEIO - 120, y: 430, w: 240, h: 260 }], { tipos: ["mais", "bola"], min: 10, max: 20, opacidade: 0.6 }),
  ]);
}

/* Selo "MOVIDO A CAFÉ", estrelas, e o antes e depois do café embaixo da frase: roxo e mostarda. */
function maisCafeMenosDrama(): DocumentoDoBanner {
  const MOSTARDA = "#FFC93C";
  const ROXO = "#2B1A6B";
  const CREME = "#FFF3D6";
  const texto_: Zona = { x: B - 580, y: 170, w: 1160, h: 830 };
  const desenho: Zona = { x: A - 470, y: 120, w: 940, h: 900 };
  return caneca({ cor: ROXO, cor2: "#4B2BB5", angulo: 150 }, [
    tex("Céu", "pontos", "#FFFFFF", { opacidade: 0.08, tamanho: 12 }),
    ...aro("faixas", MOSTARDA, CREME),
    fc("Selo", "circulo", A, 575, 800, 800, MOSTARDA, { sombra: sombraDe("#140A3A", 18, 40, 0.4) }),
    aroTracejado(A, 575, 360, ROXO, 3, "pontilhado"),
    tc("Texto do selo", A, 225, 700, 700, { texto: "MOVIDO A CAFÉ • MOVIDO A CAFÉ • ", fonte: "space-grotesk", peso: 700, tamanho: 64, espaco: 0.2, cor: ROXO, curva: 100 }),
    fc("Miolo", "circulo", A, 575, 470, 470, ROXO),
    fig("Café", "cafe", A, 585, 320, { semAdesivo: true }),
    ...[0, 1, 2, 3, 4].map((i) => ic(`Estrela ${i + 1}`, "estrela-4", A - 120 + i * 60, 760, 34, MOSTARDA, { preenchido: true, espessura: 0.5 })),
    fig("Grão", "brilhos", A + 410, 230, 140, { semAdesivo: true }),
    tc("Frase", B, 190, 1160, 420, {
      texto: "Mais café,\nmenos drama.",
      html: html(`Mais café,<br>menos ${trecho("drama.", { cor: MOSTARDA })}`),
      fonte: "abril-fatface",
      peso: 400,
      tamanho: 150,
      linha: 1.1,
      cor: CREME,
    }),
    ...divisor(B, 650, 520, MOSTARDA),
    fig("Antes", "chorando", B - 330, 820, 180),
    tc("Antes (rótulo)", B - 330, 920, 260, 60, { texto: "antes", fonte: "caveat", peso: 700, tamanho: 50, cor: CREME }),
    traco("Seta", [[B - 200, 820], [B - 60, 780], [B + 140, 810]], MOSTARDA, 7, { fim: "seta" }),
    tc("Do café", B - 30, 700, 280, 70, { texto: "1 café depois", fonte: "caveat", peso: 700, tamanho: 50, cor: MOSTARDA }, -4),
    fig("Depois", "de-oculos-escuros", B + 300, 820, 190),
    tc("Depois (rótulo)", B + 300, 920, 260, 60, { texto: "depois", fonte: "caveat", peso: 700, tamanho: 50, cor: CREME }),
    ...confetes(18, 28, [MOSTARDA, CREME, "#8E7BFF"], [texto_, desenho, { x: 0, y: 0, w: W, h: 80 }, { x: 0, y: H - 80, w: W, h: 80 }], { tipos: ["estrela", "bola", "anel"], opacidade: 0.85 }),
  ]);
}

/* ================================================================ RECORTES */

/* Polaroids do casal num varal com pregadores, corações e "Nossa história": vermelho-paixão. */
function nossaHistoria(): DocumentoDoBanner {
  const ROSADO = "#FFD1DA";
  const texto_: Zona = { x: B - 560, y: 170, w: 1120, h: 820 };
  const desenho: Zona = { x: A - 560, y: 90, w: 1120, h: 940 };
  return caneca({ cor: "#E0243F", cor2: "#B5122E", angulo: 160 }, [
    tex("Bolinhas", "pontos", "#FFFFFF", { opacidade: 0.1, tamanho: 18 }),
    ...aro("conchas", "#FFFFFF", ROSADO),
    traco("Varal", [[60, 220], [A - 200, 300], [A + 200, 300], [1150, 220]], "#FFFFFF", 4, { opacidade: 0.9 }),
    ...polaroid("Foto 2", FOTO("casal-abraco"), A, 560, 380, 460, 2, "♥", { zoom: 1, x: 50, y: 32 }),
    ...polaroid("Foto 1", FOTO("casal-selfie"), A - 340, 545, 360, 440, -9, "nós dois"),
    ...polaroid("Foto 3", FOTO("casal-rosto"), A + 340, 545, 360, 440, 9, "pra sempre", { zoom: 1, x: 50, y: 30 }),
    fig("Carta", "carta-de-amor", A - 360, 900, 170, { rot: -12 }),
    fig("Anel", "anel", A + 380, 910, 140, { rot: 12 }),
    ...trilha(LUCIDE.coracao, [1080, 920], [1320, 180], 5, 60, "#FFFFFF", 0.5),
    miuda("Capítulo", B, 170, 800, "capítulo 1", ROSADO, 32),
    tc("Título", B, 230, 1120, 500, { texto: "Nossa\nhistória", fonte: "pacifico", peso: 400, tamanho: 200, linha: 1.15, cor: "#FFFFFF" }),
    ...divisor(B, 770, 520, "#FFFFFF", "coracao"),
    miuda("Desde", B, 810, 800, "desde 14 · 06 · 2019", ROSADO, 38),
    tc("Frase", B, 900, 900, 80, { texto: "e o melhor ainda está por vir", fonte: "caveat", peso: 700, tamanho: 60, cor: "#FFFFFF" }),
    fig("Dois corações", "dois-coracoes", B + 480, 240, 160, { rot: 10 }),
    fig("Coração com flecha", "coracao-com-flecha", B - 490, 270, 150, { rot: -12 }),
    ...confetes(19, 24, ["#FFFFFF", ROSADO], [texto_, desenho, { x: 0, y: 0, w: W, h: 80 }, { x: 0, y: H - 80, w: W, h: 80 }, { x: 1040, y: 120, w: 320, h: 880 }], { tipos: ["coracao", "bola"], opacidade: 0.8 }),
  ]);
}

/* "AMOR" com a foto do casal dentro das letras, trilha pontilhada até a frase e corações de traço. */
function amorNaFoto(): DocumentoDoBanner {
  const VERMELHO = "#E63946";
  const ROSA = "#FFE1E4";
  const amor = tc("Amor", A, 250, 1120, 540, { texto: "AMOR", fonte: "anton", peso: 400, tamanho: 470, linha: 1, espaco: 0.02, cor: "#1A1A1A" });
  const texto_: Zona = { x: B - 580, y: 170, w: 1160, h: 840 };
  return caneca({ cor: "#FFFFFF" }, [
    ...aro("fios", VERMELHO, "#1A1A1A"),
    amor,
    foto("Foto do casal", FOTO("casal-abraco"), { x: amor.x, y: amor.y, w: amor.w, h: amor.h }, { dentroDe: amor.id, enquadre: { zoom: 1.1, x: 50, y: 32 } }),
    miuda("Embaixo", A, 820, 900, "você + eu = tudo", VERMELHO, 46),
    miuda("Em cima", A, 160, 700, "um amor de verdade", "#1A1A1A", 30),
    linha("Coração", LUCIDE.coracao, A + 470, 190, 80, VERMELHO, 1.6, { rot: 14 }),
    linha("Coração", LUCIDE.coracao, A - 430, 930, 70, VERMELHO, 1.6, { rot: -12 }),
    traco("Trilha", [[A + 420, 860], [MEIO - 60, 960], [MEIO + 80, 760], [MEIO - 40, 560], [B - 520, 440]], VERMELHO, 4, { tracejado: "pontilhado", fim: "seta" }),
    fc("Coração da trilha", "coracao", MEIO + 30, 660, 60, 54, VERMELHO, { rot: 10 }),
    tc("Frase", B, 250, 1160, 380, {
      texto: "Você é o meu\nlugar favorito",
      html: html(`Você é o meu<br>${trecho("lugar favorito", { cor: VERMELHO, italico: true })}`),
      fonte: "dm-serif-display",
      peso: 400,
      tamanho: 140,
      linha: 1.15,
      cor: "#1A1A1A",
    }),
    ...divisor(B, 680, 520, VERMELHO, "coracao"),
    fig("Coração", "coracao-batendo", B, 830, 170, { semAdesivo: true }),
    miuda("Data", B, 930, 800, "14 · 06 · 2019", "#1A1A1A", 30),
    ...confetes(20, 20, [VERMELHO, "#FFB3BA"], [texto_, { x: A - 580, y: 120, w: 1160, h: 880 }, { x: MEIO - 200, y: 500, w: 400, h: 480 }], { tipos: ["coracao", "bola"], min: 12, max: 24, opacidade: 0.8 }),
  ]);
}

/* Foto da família colada no papel rasgado com fitas e rabiscos; "FAMÍLIA é tudo" e as regras da casa. */
function familiaETudo(): DocumentoDoBanner {
  const MARROM = "#3D1A00";
  const texto_: Zona = { x: B - 580, y: 130, w: 1160, h: 880 };
  const desenho: Zona = { x: A - 560, y: 90, w: 1120, h: 940 };
  return caneca({ cor: "#FFB703", cor2: "#FB8500", angulo: 150 }, [
    tex("Grão", "grao", "#000000", { opacidade: 0.06 }),
    ...aro("conchas", "#FFFFFF", MARROM),
    f("Papel de trás", "rasgado", { x: A - 470, y: 150, w: 940, h: 820, rot: 4 }, "#FFE8B0", { pontas: 9, profundidade: 0.05 }),
    f("Papel rasgado", "rasgado", { x: A - 510, y: 130, w: 1020, h: 860, rot: -3 }, "#FFFFFF", { pontas: 11, profundidade: 0.05, sombra: sombraDe("#7A3A00", 16, 30, 0.3) }),
    foto("Foto da família", FOTO("familia-estudio"), { x: A - 440, y: 200, w: 880, h: 720, rot: -3 }, { enquadre: { zoom: 1.15, x: 50, y: 40 } }),
    f("Fita", "retangulo", { x: A - 140, y: 130, w: 280, h: 80, rot: -9 }, "#FFF1B8", { opacidade: 0.85 }),
    f("Fita", "retangulo", { x: A + 330, y: 880, w: 220, h: 70, rot: 38 }, "#FFF1B8", { opacidade: 0.85 }),
    f("Fita", "retangulo", { x: A - 560, y: 860, w: 200, h: 66, rot: -34 }, "#FFF1B8", { opacidade: 0.85 }),
    linha("Rabisco de coração", LUCIDE.coracao, MEIO - 20, 260, 110, "#FFFFFF", 1.8, { rot: -12 }),
    linha("Rabisco de sol", LUCIDE.sol, MEIO + 10, 860, 110, "#FFFFFF", 1.6),
    tc("Família", B, 150, 1160, 250, { texto: "FAMÍLIA", fonte: "archivo-black", peso: 400, tamanho: 200, cor: "#FFFFFF", efeito: { tipo: "levantar", cor: "#9A4A00", forca: 0.6 } }),
    tc("É tudo", B, 380, 900, 240, { texto: "é tudo", fonte: "pacifico", peso: 400, tamanho: 170, cor: MARROM }, -3),
    f("Cartão das regras", "retangulo", { x: B - 420, y: 650, w: 840, h: 340 }, "#FFFFFF", { raio: 24, opacidade: 0.94, sombra: sombraDe("#7A3A00", 10, 24, 0.25) }),
    costura({ x: B - 400, y: 670, w: 800, h: 300 }, "#FB8500", 3),
    t("Regras", { x: B - 380, y: 680, w: 760, h: 60 }, { texto: "REGRAS DA CASA", fonte: "space-grotesk", peso: 700, tamanho: 34, espaco: 0.3, cor: "#FB8500" }),
    t("Lista", { x: B - 380, y: 740, w: 760, h: 220 }, { texto: "abraço de bom dia\ncafé sempre juntos\namor sem medida", fonte: "fredoka", peso: 600, tamanho: 44, linha: 1.5, cor: MARROM, lista: "checks", vertical: "topo" }),
    fig("Casa", "casa", B + 470, 650, 190, { rot: 8 }),
    fig("Coração", "coracao-laranja", B - 500, 560, 130, { rot: -12 }),
    ...confetes(21, 18, ["#FFFFFF", MARROM], [texto_, desenho, { x: 0, y: 0, w: W, h: 80 }, { x: 0, y: H - 80, w: W, h: 80 }, { x: MEIO - 140, y: 170, w: 280, h: 800 }], { tipos: ["bola", "anel", "risco"], opacidade: 0.6 }),
  ]);
}

/* ================================================================== DATAS */

/* Foto de mãe e bebê no coração com flores em volta e "MELHOR mãe DO MUNDO". */
function melhorMae(): DocumentoDoBanner {
  const VERMELHO = "#E11D48";
  const ROSA = "#FF8FAB";
  const CLARO = "#FFE4EC";
  const texto_: Zona = { x: B - 560, y: 120, w: 1120, h: 900 };
  const desenho: Zona = { x: A - 520, y: 100, w: 1040, h: 940 };
  return caneca({ cor: "#FFFFFF" }, [
    ...aro("pontos", ROSA, "#FFFFFF"),
    fc("Mancha", "circulo", A, 560, 980, 900, radial(CLARO, "#FFFFFF", 1, 0)),
    fc("Coração de trás", "coracao", A + 20, 560, 840, 790, ROSA, { rot: -6 }),
    foto("Foto", FOTO("mae-bebe"), { x: A - 380, y: 180, w: 760, h: 720 }, { mascara: "coracao", enquadre: { zoom: 1.15, x: 55, y: 45 } }),
    fig("Buquê", "buque", A - 400, 820, 240, { rot: -10 }),
    fig("Tulipa", "tulipa", A - 440, 300, 170, { rot: -18 }),
    fig("Rosa", "rosa", A + 420, 820, 200, { rot: 12 }),
    fig("Coração brilhante", "coracao-brilhante", A + 400, 220, 170, { rot: 14 }),
    linha("Coração", LUCIDE.coracao, MEIO, 300, 70, ROSA, 1.6, { rot: -10 }),
    linha("Coração", LUCIDE.coracao, MEIO + 30, 820, 56, VERMELHO, 1.6, { rot: 12 }),
    miuda("Edição", B, 120, 800, "edição dia das mães", ROSA, 30),
    tc("Melhor", B, 170, 1100, 170, { texto: "MELHOR", fonte: "archivo-black", peso: 400, tamanho: 136, espaco: 0.04, cor: VERMELHO }),
    tc("Mãe", B, 300, 1000, 380, { texto: "mãe", fonte: "great-vibes", peso: 400, tamanho: 340, linha: 1, cor: "#FF4D7E" }),
    tc("Do mundo", B, 660, 1100, 170, { texto: "DO MUNDO", fonte: "archivo-black", peso: 400, tamanho: 136, espaco: 0.04, cor: VERMELHO }),
    ...divisor(B, 860, 480, ROSA, "coracao"),
    tc("Assinatura", B, 890, 900, 110, { texto: "com amor, sua filha", fonte: "caveat", peso: 700, tamanho: 76, cor: "#8A1C3A" }),
    fig("Coração", "coracao-vermelho", B + 500, 470, 140, { rot: 15, semAdesivo: true }),
    fig("Coração", "coracao-rosa", B - 510, 520, 120, { rot: -12, semAdesivo: true }),
    ...confetes(22, 24, [ROSA, VERMELHO, "#FFC2D1"], [texto_, desenho, { x: 0, y: 0, w: W, h: 80 }, { x: 0, y: H - 80, w: W, h: 80 }, { x: MEIO - 100, y: 230, w: 200, h: 660 }], { tipos: ["coracao", "bola"], min: 12, max: 26, opacidade: 0.75 }),
  ]);
}

/* Foto do pai num escudo de herói, raios, nota de 5 estrelas e "PAI, MEU HERÓI": azul-royal. */
function paiHeroi(): DocumentoDoBanner {
  const AMARELO = "#FFC300";
  const AZUL_ESCURO = "#061A45";
  const texto_: Zona = { x: B - 580, y: 110, w: 1160, h: 900 };
  const desenho: Zona = { x: A - 500, y: 110, w: 1000, h: 920 };
  return caneca({ cor: "#0B2A6F", cor2: "#1450D9", angulo: 150 }, [
    tex("Retícula", "meio-tom", "#FFFFFF", { opacidade: 0.1, tamanho: 20, giro: 45 }),
    tex("Raios", "raios", "#FFFFFF", { opacidade: 0.06, quantidade: 26, centroX: 25, centroY: 50 }),
    ...aro("faixas", AMARELO, "#FFFFFF"),
    fc("Explosão", "explosao", A, 561, 960, 960, "#2563EB", { pontas: 20, profundidade: 0.1 }),
    fc("Aro", "circulo", A, 561, 830, 830, AMARELO, { sombra: sombraDe(AZUL_ESCURO, 18, 40, 0.4) }),
    foto("Foto", FOTO("pai-beijo"), { x: A - 375, y: 186, w: 750, h: 750 }, { circulo: true, enquadre: { zoom: 1.1, x: 55, y: 40 } }),
    fig("Troféu", "trofeu", A + 360, 870, 220, { rot: 10 }),
    fig("Coroa", "coroa", A - 330, 230, 190, { rot: -16 }),
    linha("Raio", LUCIDE.raio, MEIO - 20, 330, 130, AMARELO, 1.6, { preenchido: true, rot: 10 }),
    linha("Raio", LUCIDE.raio, MEIO + 40, 790, 100, "#FFFFFF", 1.6, { preenchido: true, rot: -12 }),
    tc("Pai", B, 120, 1000, 230, { texto: "PAI,", fonte: "russo-one", peso: 400, tamanho: 210, cor: "#FFFFFF" }),
    tc("Meu herói", B, 340, 1160, 230, { texto: "MEU HERÓI", fonte: "russo-one", peso: 400, tamanho: 180, cor: AMARELO, efeito: { tipo: "levantar", cor: AZUL_ESCURO, forca: 0.6 } }),
    tc("Desde", B, 590, 1000, 120, { texto: "desde o primeiro dia", fonte: "caveat", peso: 700, tamanho: 90, cor: "#FFFFFF" }),
    f("Cartão da nota", "retangulo", { x: B - 380, y: 760, w: 760, h: 190 }, AZUL_ESCURO, { raio: 95, opacidade: 0.85 }),
    miuda("Avaliação", B, 778, 600, "avaliação de pai", AMARELO, 28),
    ...[0, 1, 2, 3, 4].map((i) => fig(`Estrela ${i + 1}`, "estrela", B - 240 + i * 120, 880, 96, { semAdesivo: true })),
    ...confetes(23, 22, ["#FFFFFF", AMARELO], [texto_, desenho, { x: 0, y: 0, w: W, h: 80 }, { x: 0, y: H - 80, w: W, h: 80 }, { x: MEIO - 120, y: 230, w: 240, h: 660 }], { tipos: ["estrela", "mais", "bola"], opacidade: 0.75 }),
  ]);
}

/* Varal de luzinhas, árvore, presentes, boneco de neve e "Feliz Natal" em letra cursiva. */
function felizNatal(): DocumentoDoBanner {
  const DOURADO = "#FFD166";
  const texto_: Zona = { x: B - 560, y: 160, w: 1120, h: 860 };
  const desenho: Zona = { x: A - 540, y: 170, w: 1080, h: 950 };
  const cores = ["#FFD166", "#06D6A0", "#4CC9F0", "#FF8FAB"];
  return caneca({ cor: "#C1121F", cor2: "#8E0B16", angulo: 160 }, [
    tex("Neve", "pontos", "#FFFFFF", { opacidade: 0.25, tamanho: 14 }),
    ...varal(40, W - 40, 30, 110, 18, "#2D6A4F", (x, y, i) => [
      f("Soquete", "retangulo", { x: x - 9, y: y - 2, w: 18, h: 18 }, "#2D6A4F", { raio: 4 }),
      fc("Lâmpada", "gota", x, y + 40, 34, 48, cores[i % 4], { rot: 180, efeitos: [{ tipo: "brilho", cor: cores[i % 4], x: 0, y: 0, desfoque: 22, opacidade: 0.9 }] }),
    ]),
    fc("Chão de neve", "semicirculo", A, 960, 1080, 400, "#FFFFFF"),
    fig("Árvore de Natal", "arvore-de-natal", A - 40, 560, 680),
    fig("Presente", "presente", A + 320, 880, 240, { rot: 6 }),
    fig("Presente", "laco", A + 180, 960, 150, { rot: -8 }),
    fig("Boneco de neve", "boneco-de-neve", A - 370, 820, 280, { rot: -4 }),
    fig("Sino", "sino", A + 370, 380, 170, { rot: 14 }),
    fig("Bengala", "bala", MEIO, 520, 130, { rot: -20 }),
    tc("Feliz", B, 140, 900, 280, { texto: "Feliz", fonte: "great-vibes", peso: 400, tamanho: 230, cor: "#FFFFFF" }),
    tc("Natal", B, 330, 1100, 380, { texto: "Natal", fonte: "great-vibes", peso: 400, tamanho: 340, cor: "#FFFFFF" }),
    ...divisor(B, 740, 520, DOURADO),
    miuda("Boas festas", B, 770, 800, "boas festas · 2026", DOURADO, 34),
    tc("Ano novo", B, 850, 1000, 90, { texto: "e um ano novo cheio de café", fonte: "fredoka", peso: 600, tamanho: 50, cor: "#FFD6D9" }),
    ...confetes(24, 30, ["#FFFFFF"], [texto_, desenho, { x: 0, y: 0, w: W, h: 200 }, { x: MEIO - 90, y: 430, w: 180, h: 180 }], { tipos: ["estrela", "bola"], min: 10, max: 24, opacidade: 0.85 }),
  ]);
}

/* Coroa de flores fechada em volta do nome e a frase em serifa (aniversário, madrinha, presente). */
function floresComNome(): DocumentoDoBanner {
  const AMEIXA = "#7A2E8E";
  const ROSA = "#E4508A";
  const VERDE = "#2E7D5B";
  const flores: Array<[string, number, number, number, number]> = [
    ["girassol", -330, -230, 230, -14],
    ["erva", -120, -330, 170, -40],
    ["flor-amarela", 90, -330, 150, 10],
    ["tulipa", 300, -250, 210, 18],
    ["flor-de-cerejeira", 380, -40, 160, 6],
    ["hibisco", 330, 220, 230, 12],
    ["folhas-ao-vento", 110, 330, 180, 6],
    ["rosa", -110, 330, 170, -6],
    ["flor-de-cerejeira", -330, 240, 210, -10],
    ["lotus", -390, 10, 150, -8],
  ];
  return caneca({ cor: "#FFFFFF" }, [
    ...aro("fios", AMEIXA, ROSA),
    fc("Aro", "circulo", A, 561, 760, 760, nada, { borda: { cor: "#F6D5E5", largura: 4 } }),
    ...ramo(A, 561, 380, 0, 360, 18, VERDE, 56),
    ...flores.map(([chave, dx, dy, lado, rot], i) => fig(`Flor ${i + 1}`, chave, A + dx, 561 + dy, lado, { rot, semAdesivo: true })),
    miuda("Feliz aniversário", A, 470, 600, "feliz aniversário", ROSA, 28),
    tc("Nome", A, 500, 900, 260, { texto: "Juliana", fonte: "great-vibes", peso: 400, tamanho: 210, cor: AMEIXA }),
    linha("Folha", LUCIDE.folha, MEIO, 330, 80, VERDE, 1.3, { rot: -20 }),
    fig("Flor do meio", "flor-amarela", MEIO, 790, 110, { semAdesivo: true, rot: 12 }),
    miuda("Para você", B, 180, 800, "flores para quem floresce", ROSA, 30),
    tc("Frase", B, 250, 1160, 520, {
      texto: "Que nunca falte\namor, café\ne flores.",
      html: html(`Que nunca falte<br>amor, café<br>e ${trecho("flores.", { cor: ROSA, italico: true })}`),
      fonte: "dm-serif-display",
      peso: 400,
      tamanho: 128,
      linha: 1.15,
      cor: "#2D1B3D",
    }),
    ...divisor(B, 820, 460, ROSA, "coracao"),
    miuda("Data", B, 860, 600, "27 · 10 · 2026", AMEIXA, 30),
    fig("Cerejeira", "flor-de-cerejeira", B + 500, 900, 140, { rot: -10, semAdesivo: true }),
    fig("Tulipa", "tulipa", B - 520, 240, 140, { rot: -16, semAdesivo: true }),
    ...confetes(25, 22, [ROSA, "#FFC2D9", "#B8E0C8"], [{ x: B - 590, y: 160, w: 1180, h: 800 }, { x: A - 520, y: 110, w: 1040, h: 920 }, { x: MEIO - 80, y: 260, w: 160, h: 620 }], { tipos: ["bola", "coracao"], min: 10, max: 22, opacidade: 0.7 }),
  ]);
}

/* ============================================================== PROFISSÕES */

/* Quadro-negro com moldura de madeira, rabiscos de giz e "Professora que ama ensinar". */
function professora(): DocumentoDoBanner {
  const AMARELO = "#FFD23F";
  const VERDE = "#1F5C3A";
  const MADEIRA = "#9C6B3F";
  const GIZ = "#F2F2F2";
  return caneca({ cor: "#226B43", cor2: "#174A2E", angulo: 160 }, [
    tex("Giz", "grao", "#FFFFFF", { opacidade: 0.09 }),
    f("Moldura de madeira", "retangulo", { x: 0, y: 0, w: W, h: H }, nada, { borda: { cor: MADEIRA, largura: 34 } }),
    f("Fio da moldura", "retangulo", { x: 34, y: 34, w: W - 68, h: H - 68 }, nada, { borda: { cor: "#6E4526", largura: 6 } }),
    f("Apoio do giz", "retangulo", { x: MEIO - 300, y: H - 66, w: 600, h: 30 }, "#6E4526", { raio: 6 }),
    f("Giz", "retangulo", { x: MEIO - 180, y: H - 84, w: 90, h: 20 }, GIZ, { raio: 8 }),
    f("Giz amarelo", "retangulo", { x: MEIO + 60, y: H - 84, w: 70, h: 20 }, AMARELO, { raio: 8 }),
    tc("ABC", A - 90, 150, 300, 110, { texto: "ABC", fonte: "permanent-marker", peso: 400, tamanho: 84, cor: GIZ, opacidade: 0.85 }, -8),
    tc("Conta", A + 300, 150, 360, 110, { texto: "1 + 1 = 2", fonte: "permanent-marker", peso: 400, tamanho: 64, cor: GIZ, opacidade: 0.85 }, 6),
    fig("Livros", "livros", A - 100, 640, 520),
    fig("Maçã", "maca", A + 290, 720, 290, { rot: 10 }),
    fig("Lápis", "lapis", A - 380, 350, 220, { rot: -30 }),
    fig("Giz de cera", "giz-de-cera", A + 360, 360, 200, { rot: 25 }),
    fig("Capelo", "capelo", A - 380, 880, 180, { rot: -10 }),
    linha("Estrela", LUCIDE.estrela, A + 30, 270, 90, GIZ, 1.4, { opacidade: 0.7, rot: 12 }),
    traco("Rabisco", [[MEIO - 110, 380], [MEIO - 40, 320], [MEIO + 20, 420], [MEIO + 100, 350]], GIZ, 6, { opacidade: 0.7 }),
    linha("Coração de giz", LUCIDE.coracao, MEIO, 600, 110, AMARELO, 1.6, { opacidade: 0.9, rot: -8 }),
    linha("Estrela de giz", LUCIDE.estrela, MEIO + 10, 820, 80, GIZ, 1.4, { opacidade: 0.7 }),
    tc("Professora", B, 140, 1160, 210, { texto: "Professora", fonte: "permanent-marker", peso: 400, tamanho: 176, cor: "#FFFFFF" }),
    tc("Que ama", B, 360, 900, 150, { texto: "que ama", fonte: "permanent-marker", peso: 400, tamanho: 120, cor: "#FFFFFF" }),
    tc("Ensinar", B, 480, 1000, 260, { texto: "ensinar", fonte: "pacifico", peso: 400, tamanho: 190, cor: AMARELO }, -3),
    traco("Sublinhado", [[B - 300, 770], [B - 100, 755], [B + 100, 772], [B + 300, 758]], AMARELO, 8),
    tc("Nome", B, 830, 520, 110, { texto: "Prof. Ana", fonte: "fredoka", peso: 700, tamanho: 60, cor: VERDE, fundo: { forma: "pilula", cor: AMARELO } }),
    linha("Brilhos", LUCIDE.brilho, B + 500, 620, 100, GIZ, 1.3, { opacidade: 0.7 }),
    linha("Brilhos", LUCIDE.brilho, B - 520, 640, 80, AMARELO, 1.3, { opacidade: 0.7 }),
  ]);
}

/* Linha de batimento atravessando a caneca, estetoscópio no círculo, cruzes e "Enfermeira por amor". */
function enfermagem(): DocumentoDoBanner {
  const AMARELO = "#FFE066";
  const texto_: Zona = { x: B - 580, y: 120, w: 1160, h: 880 };
  const desenho: Zona = { x: A - 470, y: 120, w: 940, h: 900 };
  const batimento: Array<[number, number]> = [];
  for (let x = 60; x <= W - 60; x += 30) {
    const fase = (x - 60) % 600;
    const y = fase === 210 ? 560 : fase === 240 ? 1020 : fase === 270 ? 980 : fase === 300 ? 720 : 990;
    batimento.push([x, y]);
  }
  return caneca({ cor: "#00B4D8", cor2: "#0077B6", angulo: 150 }, [
    tex("Ondas", "ondas", "#FFFFFF", { opacidade: 0.08, tamanho: 70 }),
    ...aro("faixas", "#FFFFFF", "#90E0EF"),
    traco("Batimento", batimento, "#FFFFFF", 5, { suave: false, opacidade: 0.35 }),
    fc("Círculo", "circulo", A, 561, 800, 800, "#FFFFFF", { sombra: sombraDe("#004E7A", 18, 40, 0.3) }),
    aroTracejado(A, 561, 440, "#FFFFFF", 4),
    fig("Estetoscópio", "estetoscopio", A - 20, 560, 500, { semAdesivo: true }),
    fig("Seringa", "seringa", A + 300, 300, 200, { rot: 20 }),
    fig("Curativo", "curativo", A - 330, 840, 200, { rot: -15 }),
    fig("Coração", "coracao-vermelho", A + 310, 830, 170, { rot: 12 }),
    fig("Remédio", "remedio", A - 330, 300, 160, { rot: -20 }),
    ...[[MEIO - 30, 240, 90], [MEIO + 40, 420, 60]].map(([x, y, l]) => ic("Cruz", "mais", x, y, l, "#FFFFFF", { espessura: 3.4, opacidade: 0.8 })),
    miuda("Plantão", B, 140, 800, "plantão do amor · 24h", "#CAF0F8", 30),
    tc("Enfermeira", B, 190, 1160, 230, { texto: "Enfermeira", fonte: "lilita-one", peso: 400, tamanho: 190, cor: "#FFFFFF" }),
    tc("Por amor", B, 400, 1000, 260, { texto: "por amor", fonte: "pacifico", peso: 400, tamanho: 170, cor: AMARELO }, -3),
    ...divisor(B, 700, 520, "#FFFFFF", "coracao"),
    tc("Missão", B, 740, 1100, 90, { texto: "cuidar é a minha missão", fonte: "fredoka", peso: 600, tamanho: 56, cor: "#FFFFFF" }),
    tc("Nome", B, 860, 480, 90, { texto: "Enf. Carla", fonte: "fredoka", peso: 700, tamanho: 44, cor: "#0077B6", fundo: { forma: "pilula", cor: "#FFFFFF" } }),
    ...confetes(26, 20, ["#FFFFFF", AMARELO], [texto_, desenho, { x: 0, y: 0, w: W, h: 80 }, { x: 0, y: H - 80, w: W, h: 80 }, { x: MEIO - 90, y: 180, w: 180, h: 300 }], { tipos: ["mais", "bola"], min: 12, max: 24, opacidade: 0.7 }),
  ]);
}

/* ================================================================ INFANTIL */

/* Bandeirinhas, unicórnio no arco-íris entre nuvens e o nome da criança com brilho. */
function unicornio(): DocumentoDoBanner {
  const AMARELO = "#FFF3A3";
  const ROXO = "#6A2BB5";
  const cores = ["#FFFFFF", "#FFE066", "#7BDFF2", "#B2F7EF", "#FFC6FF"];
  const texto_: Zona = { x: B - 560, y: 170, w: 1120, h: 820 };
  const desenho: Zona = { x: A - 520, y: 170, w: 1040, h: 900 };
  return caneca({ cor: "#FF7EB6", cor2: "#9B5DE5", angulo: 135 }, [
    tex("Bolinhas", "pontos", "#FFFFFF", { opacidade: 0.16, tamanho: 16 }),
    ...varal(40, W - 40, 20, 100, 24, "#FFFFFF", (x, y, i, giro) => [bandeirinha(x, y, 70, cores[i % cores.length], giro)]),
    fig("Arco-íris", "arco-iris", A, 430, 640, { semAdesivo: true, opacidade: 0.95 }),
    fig("Nuvem", "nuvem", A - 280, 880, 360, { semAdesivo: true }),
    fig("Nuvem", "nuvem", A + 290, 900, 320, { semAdesivo: true }),
    fig("Unicórnio", "unicornio", A, 650, 580, { sombra: sombraDe("#5B1F9E", 18, 30, 0.3) }),
    fig("Brilhos", "brilhos", A + 390, 290, 160, { semAdesivo: true }),
    fig("Coroa", "coroa", A - 380, 330, 150, { rot: -14 }),
    fig("Estrela do meio", "estrela-brilhante", MEIO, 520, 120, { semAdesivo: true }),
    miuda("Princesa", B, 200, 800, "a princesa da casa", "#FFFFFF", 32),
    tc("Nome", B, 250, 1120, 380, { texto: "Sofia", fonte: "lilita-one", peso: 400, tamanho: 330, cor: "#FFFFFF", efeito: { tipo: "levantar", cor: ROXO, forca: 0.7 } }),
    tc("Mágica", B, 640, 1000, 180, { texto: "a mais mágica", fonte: "pacifico", peso: 400, tamanho: 120, cor: AMARELO }),
    ...divisor(B, 860, 460, "#FFFFFF"),
    miuda("Idade", B, 890, 600, "5 aninhos", "#FFFFFF", 32),
    fig("Estrela", "estrela-brilhante", B + 500, 300, 130, { semAdesivo: true }),
    fig("Estrela", "estrela-brilhante", B - 510, 860, 110, { semAdesivo: true }),
    ...confetes(27, 30, ["#FFFFFF", AMARELO, "#B2F7EF"], [texto_, desenho, { x: 0, y: 0, w: W, h: 190 }, { x: MEIO - 90, y: 430, w: 180, h: 180 }], { tipos: ["estrela", "bola", "coracao"], min: 12, max: 26, opacidade: 0.85 }),
  ]);
}

/* Dinossauros na selva com pegadas, balão "RAWR!" e "significa eu te amo em dinossauro". */
function dinossauro(): DocumentoDoBanner {
  const ESCURO = "#1B4332";
  const FOLHA = "#2D6A4F";
  const texto_: Zona = { x: B - 560, y: 110, w: 1120, h: 900 };
  const desenho: Zona = { x: A - 540, y: 100, w: 1080, h: 960 };
  return caneca({ cor: "#9BE15D", cor2: "#3FB950", angulo: 150 }, [
    tex("Pintinhas", "pontos", ESCURO, { opacidade: 0.08, tamanho: 20 }),
    ...aro("conchas", FOLHA, "#FFFFFF"),
    fc("Sol", "circulo", A + 300, 330, 300, 300, "#FFE066", { opacidade: 0.9 }),
    fc("Morro", "semicirculo", A, 1000, 1100, 360, FOLHA, { opacidade: 0.35 }),
    fig("Dinossauro", "dinossauro", A - 90, 600, 620),
    fig("Dinossauro pescoçudo", "dinossauro-pescocudo", A + 340, 770, 300, { rot: 4 }),
    fig("Erva", "erva", A - 420, 840, 220, { rot: -10 }),
    fig("Folha", "folhas-ao-vento", A - 400, 280, 170, { rot: -20 }),
    fig("Ovo", "ovo", A + 120, 900, 140, { rot: 14 }),
    tc("Balão", A + 330, 160, 300, 170, { texto: "rawr!", fonte: "bangers", peso: 400, tamanho: 70, cor: ESCURO, fundo: { forma: "balao", cor: "#FFFFFF" } }, 6),
    ...trilha(LUCIDE.pegadas, [1050, 960], [1330, 170], 5, 80, ESCURO, 0.4),
    tc("Rawr", B, 120, 1100, 340, { texto: "RAWR!", fonte: "bangers", peso: 400, tamanho: 320, espaco: 0.04, cor: ESCURO, efeito: { tipo: "levantar", cor: "#FFFFFF", forca: 0.8 } }),
    tc("Significa", B, 480, 1100, 260, { texto: "significa eu te amo\nem dinossauro", fonte: "fredoka", peso: 700, tamanho: 84, linha: 1.15, cor: ESCURO }),
    ...divisor(B, 790, 460, ESCURO, "coracao"),
    tc("Nome", B, 840, 460, 96, { texto: "Pedro", fonte: "fredoka", peso: 700, tamanho: 50, cor: "#FFFFFF", fundo: { forma: "pilula", cor: ESCURO } }),
    fig("Coração", "coracao-verde", B + 480, 870, 150, { rot: 10 }),
    fig("Folha", "folha-caida", B - 500, 880, 140, { rot: -14 }),
    ...confetes(28, 20, ["#FFFFFF", ESCURO], [texto_, desenho, { x: 0, y: 0, w: W, h: 80 }, { x: 0, y: H - 80, w: W, h: 80 }, { x: 1020, y: 120, w: 340, h: 880 }], { tipos: ["bola", "anel"], min: 12, max: 24, opacidade: 0.5 }),
  ]);
}

/* ============================================================ outros tamanhos */

/* Feito para a caneca; noutro tamanho deitado, encaixa inteiro. */
function emQualquerTamanho(montar: () => DocumentoDoBanner) {
  return (p: PedidoDoModelo): DocumentoDoBanner => {
    const doc = montar();
    return p.largura === doc.largura && p.altura === doc.altura ? doc : encaixar(doc, p.largura, p.altura);
  };
}

export const MODELOS_DE_CANECA = (
  [
    { chave: "caneca-gato-cafe", nome: "Gatinho do café", descricao: "Bichinhos · gato, selo e trilha de patinhas", montar: gatinhoDoCafe },
    { chave: "caneca-pet", nome: "Melhor amigo", descricao: "Recorte · foto do pet com o nome", montar: melhorAmigo },
    { chave: "caneca-mae-de-gato", nome: "Mãe de gato", descricao: "Recorte · gato, novelo e corações", montar: maeDeGato },
    { chave: "caneca-soneca", nome: "Modo soneca", descricao: "Bichinhos · panda, luzinhas e bateria", montar: modoSoneca },
    { chave: "caneca-segunda", nome: "Segunda-feira", descricao: "Animado · carinhas e quadrinho", montar: segundaFeira },
    { chave: "caneca-gamer", nome: "Pausa pro café", descricao: "Animado · gamer em neon", montar: pausaProCafe },
    { chave: "caneca-bom-dia", nome: "Bom dia", descricao: "Minimalista · traço fino e serifa", montar: bomDia },
    { chave: "caneca-monograma", nome: "Monograma", descricao: "Minimalista · inicial e nome nos dois lados", montar: monograma },
    { chave: "caneca-respira", nome: "Respira", descricao: "Minimalista · paisagem geométrica", montar: respira },
    { chave: "caneca-nao-fale", nome: "Antes do café", descricao: "Tipográfico · letras gigantes no preto", montar: naoFaleComigo },
    { chave: "caneca-drama", nome: "Menos drama", descricao: "Tipográfico · selo circular e antes/depois", montar: maisCafeMenosDrama },
    { chave: "caneca-polaroids", nome: "Nossa história", descricao: "Recortes · polaroids no varal", montar: nossaHistoria },
    { chave: "caneca-amor", nome: "Amor na foto", descricao: "Recortes · foto dentro das letras", montar: amorNaFoto },
    { chave: "caneca-familia", nome: "Família é tudo", descricao: "Recortes · papel rasgado e regras da casa", montar: familiaETudo },
    { chave: "caneca-mae", nome: "Melhor mãe", descricao: "Datas · Dia das Mães", montar: melhorMae },
    { chave: "caneca-pai", nome: "Pai herói", descricao: "Datas · Dia dos Pais", montar: paiHeroi },
    { chave: "caneca-natal", nome: "Feliz Natal", descricao: "Datas · luzinhas, árvore e presentes", montar: felizNatal },
    { chave: "caneca-flores", nome: "Flores com nome", descricao: "Datas · coroa de flores e nome", montar: floresComNome },
    { chave: "caneca-professora", nome: "Professora", descricao: "Profissões · quadro-negro com moldura", montar: professora },
    { chave: "caneca-enfermagem", nome: "Enfermagem", descricao: "Profissões · batimento e cuidado", montar: enfermagem },
    { chave: "caneca-unicornio", nome: "Unicórnio", descricao: "Infantil · bandeirinhas e nome", montar: unicornio },
    { chave: "caneca-dino", nome: "Dinossauro", descricao: "Infantil · RAWR!", montar: dinossauro },
  ] as const
).map((m) => ({ ...m, montar: emQualquerTamanho(m.montar), ramo: "sublimacao" as const, feitoPara: "caneca" as const }));
