"use client";

import { memo, useContext, useEffect, useMemo, useRef, useState } from "react";
import {
  AlignEndVertical,
  ArrowLeft,
  Box,
  BriefcaseBusiness,
  CalendarHeart,
  ChartColumn,
  Clapperboard,
  Rotate3d,
  Scissors,
  Flower2,
  Frame,
  Layers,
  PartyPopper,
  Shapes,
  Smile,
  Sparkles,
  Store,
  Tag,
  AlignHorizontalJustifyCenter,
  AlignStartVertical,
  AlignVerticalJustifyCenter,
  AlignVerticalJustifyEnd,
  AlignVerticalJustifyStart,
  ArrowDownToLine,
  ArrowLeftRight,
  ArrowUpToLine,
  ChevronDown,
  ChevronUp,
  FlipHorizontal2,
  FlipVertical2,
  Loader2,
  ImagePlus,
  MousePointer2,
  RotateCcw,
  Search,
  Upload,
} from "lucide-react";
import {
  type ContagemDaPagina,
  FONTES,
  TIPOS_DE_FONTE,
  garantirFontes,
  ICONES,
  fontesDaMarca,
  caixaDaPeca,
  conteudoDaPeca,
  cssDaPintura,
  degrade,
  solida,
  sombraDura,
  sombraSuave,
  type DocumentoDoBanner,
  type EstiloDeTextura,
  type Forma,
  type FormaDoFundoDoTexto,
  type Peca,
  type PecaDeForma,
  type PecaDeIcone,
  type PecaDeImagem,
  type PecaDeTexto,
  type PecaDeTextura,
  type PecaDeTraco,
  type PecaDeQr,
  type PecaDeTabela,
  type PecaDeGrafico,
  type Pintura,
  type PontosDoDegrade,
  type Sombra,
} from "@/lib/banner-editor/documento";
import { MODELOS_DO_EDITOR, RAMOS_DOS_MODELOS, modeloAparece, type PedidoDoModelo } from "@/lib/banner-editor/modelos";
import {
  GRAFICOS_PRONTOS,
  MOLDURAS_PRONTAS,
  TRACOS_PRONTOS,
  qrCode,
  tabela,
  CHAMADAS_PRONTAS,
  COMBINACOES_PRONTAS,
  FORMAS_A_MAIS,
  FORMAS_PRONTAS,
  FUNDOS_PRONTOS,
  ICONES_PRONTOS,
  TEXTOS_PRONTOS,
  contadorDePaginas,
  TEXTURAS_PRONTAS,
  imagem,
  type Medida,
  type PecaPronta,
} from "@/lib/banner-editor/pecas-novas";
import type { ResumoDaArte } from "@/lib/banner-editor/artes";
import { TIPOS_DE_MODELO, enderecoDaMiniatura, miniaturaDoModelo, pedidoNeutro } from "@/lib/banner-editor/miniaturas";
import { VideoQueToca } from "@/components/artes/video-que-toca";
import { formatarTrecho, useTrechoEscolhido } from "./texto-rico";
import { arrastavel } from "./arrastar-do-painel";
import { PainelDeGlifos } from "./painel-de-glifos";
import { BibliotecaDeImagens } from "./biblioteca-de-imagens";
import { GRUPOS_DOS_ICONES_3D, GRUPOS_DOS_ORNAMENTOS, GRUPOS_DOS_RECORTES } from "@/lib/banner-editor/biblioteca";
import { FONTES_COM_GLIFOS, ehFonteComGlifos } from "@/lib/banner-editor/fontes-com-glifos";
import { exibicaoGuardada, htmlDoFundo } from "./desenho-guardado";
import { EfeitosDoTraco, EstiloDaLetra, MolduraDaFoto, PainelDaTabela, PainelDoGrafico, PainelDoQr } from "./paineis-novos";
import { AjustesCompletos, EfeitosDaCamada, SecaoDeDistorcer, SecaoDeMesclagem } from "./paineis-pro";
import { PainelDaReticula, PainelDasCoresDaFoto } from "./paineis-da-foto";
import type { MeioTom, TratamentoDaFoto, TrocaDeCor } from "@/lib/banner-editor/tratamento-da-foto";
import { ATALHOS_DO_BANCO, BancoDeImagens, BibliotecaDeFigurinhas, BuscaDeIcones, IconesEscolhidos, type AtalhoDoBanco } from "./bibliotecas-extras";
import { DATAS_ESPECIAIS, ELEMENTOS_3D } from "@/lib/banner-editor/pacotes";
import { DEGRADES_METALICOS } from "@/lib/banner-editor/metalicos";
import { CUPONS_PRONTOS, SELOS_DE_VENDA } from "@/lib/banner-editor/cupons-e-selos";
import { DESTAQUES_DE_VIDEO, LEGENDAS_DE_VIDEO, PARTICULAS_DE_VIDEO, TELAS_DE_VIDEO } from "@/lib/banner-editor/elementos-de-video";
import { MiniCena } from "./cena-no-palco";
import type { ProdutoDoEditor } from "../campos";
import { BotaoContaGotas, CampoDeBorda, CampoDePintura, CampoDeSombra, Chave, ContextoDasCores, Cor, Numero, Opcoes, PALETA_VIVA, Regua, lembrarCor, useCoresRecentes } from "./campos";

/*
  Os painéis da esquerda do editor de banners, um por vez (como no Canva):
  a biblioteca (modelos, elementos, texto, imagens, camadas) e os ajustes que a
  barra flutuante abre para a peça escolhida (fonte, cor, efeitos, posição,
  ajustes da foto, forma, textura). Toda prévia é desenhada com o mesmo HTML
  do banner.
*/

export type AbaDaBiblioteca = "modelos" | "elementos" | "texto" | "imagens" | "camadas" | "artes" | "marca";
export type AjusteDaPeca = "fonte" | "glifos" | "vetor" | "cor" | "cor-do-fundo" | "efeitos" | "posicao" | "ajustes" | "forma" | "textura" | "transparencia" | "camada" | "cores-da-foto" | "reticula";

/* ============================================================ peças */

/* Memo: a miniatura só refaz quando a página (ou o número dela) muda. */
export const Miniatura = memo(
  function Miniatura({ doc, largura, contagem = null }: { doc: DocumentoDoBanner; largura: number; contagem?: ContagemDaPagina | null }) {
  const escala = largura / doc.largura;
  /* A fatia do carrossel contínuo mostra o pedaço dela da arte larga (as fixas ficam no lugar delas). */
  const vista = doc.vista;
  const desenhar = (lista: typeof doc.pecas) =>
    lista.map((p) => {
      const ex = p.oculta ? null : exibicaoGuardada(p, doc.pecas, contagem);
      return ex ? <div key={p.id} style={{ position: "absolute", ...ex.estilo }} dangerouslySetInnerHTML={ex.html} /> : null;
    });
  return (
    <div className="relative overflow-hidden rounded-[10px]" style={{ width: largura, height: doc.altura * escala }}>
      <div className="absolute left-0 top-0 overflow-hidden" style={{ width: doc.largura, height: doc.altura, transform: `scale(${escala})`, transformOrigin: "0 0" }}>
        <div className="absolute top-0" style={{ left: -(vista?.x ?? 0), width: vista ? vista.larguraTotal : doc.largura, height: doc.altura }}>
          <div className="absolute inset-0" dangerouslySetInnerHTML={htmlDoFundo(doc.fundo, vista ? vista.larguraTotal : doc.largura, doc.altura)} />
          {desenhar(vista ? doc.pecas.filter((p) => !p.fixa) : doc.pecas)}
        </div>
        {vista ? desenhar(doc.pecas.filter((p) => p.fixa)) : null}
      </div>
    </div>
  );
  },
  (a, b) => a.doc === b.doc && a.largura === b.largura && (a.contagem?.pagina ?? 0) === (b.contagem?.pagina ?? 0) && (a.contagem?.total ?? 0) === (b.contagem?.total ?? 0),
);

/* O desenho da amostra guardado pela peça: a amostra só refaz (textura, filtro) quando a própria peça muda. */
const desenhoDaAmostra = new WeakMap<Peca, { caixa: React.CSSProperties; html: { __html: string } }>();
function desenhoGuardado(peca: Peca) {
  let d = desenhoDaAmostra.get(peca);
  if (!d) {
    const p = { ...peca, x: 0, y: 0 };
    d = { caixa: caixaDaPeca(p), html: { __html: conteudoDaPeca(p) } };
    desenhoDaAmostra.set(peca, d);
  }
  return d;
}

export const Amostra = memo(function Amostra({ peca, largura, altura, fundo }: { peca: Peca; largura: number; altura: number; fundo: string }) {
  const folga = Math.max(peca.w, peca.h) * 0.16;
  const escala = Math.min((largura - 10) / (peca.w + folga), (altura - 10) / (peca.h + folga));
  const desenho = desenhoGuardado(peca);
  return (
    <div className="relative overflow-hidden rounded-[10px]" style={{ width: largura, height: altura, background: fundo }}>
      <div className="absolute" style={{ left: (largura - peca.w * escala) / 2, top: (altura - peca.h * escala) / 2, width: peca.w, height: peca.h, transform: `scale(${escala})`, transformOrigin: "0 0" }}>
        <div style={{ position: "absolute", ...desenho.caixa }} dangerouslySetInnerHTML={desenho.html} />
      </div>
    </div>
  );
});

/* Várias peças juntas (combinação de texto), encolhidas para caber. */
function AmostraDeVarias({ pecas, largura, altura, fundo }: { pecas: Peca[]; largura: number; altura: number; fundo?: string }) {
  const x = Math.min(...pecas.map((p) => p.x));
  const y = Math.min(...pecas.map((p) => p.y));
  const w = Math.max(...pecas.map((p) => p.x + p.w)) - x;
  const h = Math.max(...pecas.map((p) => p.y + p.h)) - y;
  const escala = Math.min((largura - 12) / w, (altura - 12) / h);
  const claro = pecas.every((p) => p.tipo === "texto" && p.pintura.cor.toUpperCase() !== "#FFFFFF" && luminanciaSimples(p.pintura.cor) < 0.6);
  return (
    <div className="relative overflow-hidden rounded-[10px]" style={{ width: largura, height: altura, background: fundo ?? (claro ? "#F5EFE6" : "linear-gradient(135deg,#6D28D9,#1E1B4B)") }}>
      <div className="absolute" style={{ left: (largura - w * escala) / 2, top: (altura - h * escala) / 2, width: w, height: h, transform: `scale(${escala})`, transformOrigin: "0 0" }}>
        {pecas.map((p) => (
          <div key={p.id} style={{ position: "absolute", ...caixaDaPeca({ ...p, x: p.x - x, y: p.y - y }) }} dangerouslySetInnerHTML={{ __html: conteudoDaPeca(p) }} />
        ))}
      </div>
    </div>
  );
}

/* A foto da arte guardada (a mesma da galeria); sem foto ainda, a arte é montada aqui mesmo. */
function FotoDaArteGuardada({ arte, largura }: { arte: ResumoDaArte; largura: number }) {
  const [semFoto, setSemFoto] = useState(false);
  if (semFoto || !arte.updated_at) return arte.documento?.pecas ? <Miniatura doc={arte.documento} largura={largura} /> : <ImagePlus size={18} className="text-[#B4AEC6]" />;
  /* eslint-disable-next-line @next/next/no-img-element */
  return <img src={enderecoDaMiniatura(arte)} alt={arte.nome} loading="lazy" decoding="async" onError={() => setSemFoto(true)} className="block h-full w-auto object-contain" style={{ width: largura }} />;
}

function luminanciaSimples(hex: string) {
  const h = hex.replace("#", "");
  const [r, g, b] = [0, 2, 4].map((i) => parseInt(h.slice(i, i + 2), 16) / 255);
  return 0.2126 * r + 0.7152 * g + 0.0722 * b;
}

/* Branco não aparece em fundo claro: a amostra ganha fundo escuro. */
export function fundoDaAmostra(p: Peca) {
  const cor =
    p.tipo === "texto" ? (p.fundo ? null : p.pintura.cor) : p.tipo === "forma" ? (p.pintura.tipo === "nenhuma" ? p.borda?.cor : p.pintura.cor) : p.tipo === "textura" ? p.cor : p.tipo === "icone" ? p.pintura.cor : null;
  return cor && cor.toUpperCase() === "#FFFFFF" ? "linear-gradient(135deg,#6D28D9,#3B0764)" : "#F3F1F9";
}

/* Memo: a arte mexendo (arrastar, digitar) não refaz as amostras da biblioteca. */
const Grade = memo(function Grade<T extends Peca>({ pecas, medida, aoPor, colunas = 2, altura = 84, filtro = "" }: { pecas: PecaPronta<T>[]; medida: Medida; aoPor: (p: Peca) => void; colunas?: 2 | 3; altura?: number; filtro?: string }) {
  const amostras = useMemo(() => pecas.map((p) => ({ pronta: p, peca: p.criar(medida) })), [pecas, medida]);
  const visiveis = amostras.filter(({ pronta }) => pronta.nome.toLowerCase().includes(filtro.trim().toLowerCase()));
  if (!visiveis.length) return null;
  const largura = colunas === 3 ? 92 : 142;
  return (
    <div className={`grid gap-2 ${colunas === 3 ? "grid-cols-3" : "grid-cols-2"}`}>
      {visiveis.map(({ pronta, peca }) => (
        <button key={pronta.chave} type="button" onClick={() => aoPor(pronta.criar(medida))} {...arrastavel(() => pronta.criar(medida))} className="group rounded-[12px] p-1 text-left transition hover:bg-[#F3F0FF]" title={`${pronta.nome} · clique ou arraste para a arte`}>
          <div className="overflow-hidden rounded-[10px] ring-1 ring-black/5 transition group-hover:ring-2 group-hover:ring-[#8B5CF6]">
            <Amostra peca={peca} largura={largura} altura={altura} fundo={fundoDaAmostra(peca)} />
          </div>
          <div className="mt-1 truncate px-0.5 text-[11px] font-medium text-[#4B4760]">{pronta.nome}</div>
        </button>
      ))}
    </div>
  );
});

/*
 * A amostra de um elemento para vídeo: parada, como as outras; com o mouse em
 * cima, toca a animação dele (recortada em volta das peças, com folga para o
 * que se mexe).
 */
function AmostraQueToca({ pecas, medida, largura, altura, fundo }: { pecas: Peca[]; medida: { largura: number; altura: number }; largura: number; altura: number; fundo: string }) {
  const [tocando, setTocando] = useState(false);
  const pagina = useMemo(() => {
    const x = Math.min(...pecas.map((p) => p.x));
    const y = Math.min(...pecas.map((p) => p.y));
    const w = Math.max(...pecas.map((p) => p.x + p.w)) - x;
    const h = Math.max(...pecas.map((p) => p.y + p.h)) - y;
    const fim = Math.max(1.6, ...pecas.map((p) => (p.animacao ? (p.animacao.fim ?? p.animacao.inicio + p.animacao.duracao) + 0.8 : 0)));
    const doc: DocumentoDoBanner = { versao: 3, largura: medida.largura, altura: medida.altura, fundo: { pintura: solida(fundo), textura: { tipo: "nenhuma", cor: "#FFFFFF", opacidade: 0, quantidade: 1, tamanho: 0, centroX: 50, centroY: 50, giro: 0 } }, pecas, animacao: { duracao: Math.min(6, fim), fundo: "parado" } };
    return { doc, recorte: { x: x - w * 0.12, y: y - h * 0.12, w: w * 1.24, h: h * 1.24 } };
  }, [pecas, medida, fundo]);
  return (
    <div className="relative overflow-hidden rounded-[10px]" style={{ width: largura, height: altura, background: fundo }} onMouseEnter={() => setTocando(true)} onMouseLeave={() => setTocando(false)}>
      {tocando ? <MiniCena paginas={[pagina.doc]} lado={largura} alto={altura} recorte={pagina.recorte} tocar /> : <AmostraDeVarias pecas={pecas} largura={largura} altura={altura} fundo={fundo} />}
    </div>
  );
}

/* A letra do atalho de teclado, ao lado do nome. */
function Tecla({ children }: { children: React.ReactNode }) {
  return <kbd className="inline-flex h-5 min-w-5 items-center justify-center rounded-md bg-white px-1 font-sans text-[10.5px] font-bold text-[#6B6584] ring-1 ring-black/10">{children}</kbd>;
}

function Secao({ titulo, children, acao }: { titulo: string; children: React.ReactNode; acao?: React.ReactNode }) {
  return (
    <section className="mb-6">
      <div className="mb-2.5 flex items-center justify-between">
        <h3 className="text-[13px] font-bold text-[#1F1B2E]">{titulo}</h3>
        {acao}
      </div>
      {children}
    </section>
  );
}

function Busca({ valor, aoMudar, placeholder }: { valor: string; aoMudar: (v: string) => void; placeholder: string }) {
  return (
    <label className="mb-5 flex h-10 items-center gap-2 rounded-[10px] bg-[#F1EFF6] px-3 text-[#77708F] focus-within:bg-white focus-within:ring-2 focus-within:ring-[#8B5CF6]">
      <Search size={16} />
      <input value={valor} onChange={(e) => aoMudar(e.target.value)} placeholder={placeholder} className="h-full min-w-0 flex-1 bg-transparent text-[13px] text-[#1F1B2E] outline-none placeholder:text-[#9A94AE]" />
    </label>
  );
}

/* ======================================================== biblioteca */

/* A lista fica a mesma entre um desenho e outro (a Grade guarda as amostras por ela). */
const TODAS_AS_FORMAS = [...FORMAS_PRONTAS, ...FORMAS_A_MAIS];
const semAcento = (s: string) => s.normalize("NFD").replace(/[̀-ͯ]/g, "").toLowerCase();

/* ======================================================= categorias de Elementos */

type PacoteDoPainel = { chave: string; nome: string; busca?: string; criar: (m: Medida) => Peca[] };

/* Os pacotes de "Datas especiais" que são da vida (aniversário, casamento...) e da empresa; o resto é data do comércio. */
const DATAS_DA_VIDA = ["aniversario", "parabens", "cha-de-bebe", "casamento", "formatura", "dia-das-criancas"];
const DATAS_DA_EMPRESA = ["inauguracao", "obrigado"];

/* Ícones do Lucide escolhidos por assunto (o nome em inglês é o do Lucide). */
const ICONES_DE_PROFISSAO = ["stethoscope", "syringe", "pill", "briefcase-medical", "heart-pulse", "scissors", "spray-can", "chef-hat", "cooking-pot", "utensils", "graduation-cap", "school", "book-open", "scale", "gavel", "wrench", "hammer", "drill", "hard-hat", "construction", "paintbrush", "palette", "camera", "car", "truck", "tractor", "dumbbell", "microscope", "flask-conical", "laptop", "code", "printer", "pen-tool", "ruler", "shirt", "paw-print", "plane", "shield", "music", "mic"];
const ICONES_DE_EMPRESA = ["store", "building", "factory", "landmark", "handshake", "briefcase-business", "chart-line", "trending-up", "chart-column", "badge-dollar-sign", "banknote", "wallet", "credit-card", "receipt", "calculator", "megaphone", "phone", "smartphone", "mail", "map-pin", "clock", "calendar", "package", "shopping-bag", "shopping-cart", "tag", "percent", "award", "trophy", "users", "id-card", "lightbulb", "wifi", "printer"];
const ICONES_DE_FESTA = ["cake", "cake-slice", "party-popper", "gift", "candy", "baby", "sparkles", "crown", "star", "heart", "music", "wine", "beer", "ice-cream-cone", "camera", "gem", "smile", "sticker"];

/* As fotos de cada assunto (o nome em português; a busca no acervo é em inglês). */
const FOTOS_DE_FESTA: AtalhoDoBanco[] = [
  { nome: "Aniversário", q: "birthday party" },
  { nome: "Bolo", q: "birthday cake" },
  { nome: "Balões", q: "balloons" },
  { nome: "Confete", q: "confetti" },
  { nome: "Festa infantil", q: "kids party" },
  { nome: "Casamento", q: "wedding" },
  { nome: "Bebê", q: "baby" },
  { nome: "Brinde", q: "toast champagne" },
];
const FOTOS_DE_PROFISSOES: AtalhoDoBanco[] = [
  { nome: "Médica", q: "doctor" },
  { nome: "Enfermagem", q: "nurse" },
  { nome: "Dentista", q: "dentist" },
  { nome: "Cabeleireira", q: "hairdresser" },
  { nome: "Barbearia", q: "barber" },
  { nome: "Manicure", q: "manicure nails" },
  { nome: "Cozinheira", q: "chef cooking" },
  { nome: "Confeitaria", q: "bakery pastry" },
  { nome: "Mecânico", q: "mechanic" },
  { nome: "Advocacia", q: "lawyer" },
  { nome: "Professora", q: "teacher classroom" },
  { nome: "Costureira", q: "sewing" },
  { nome: "Construção", q: "construction worker" },
  { nome: "Agricultura", q: "farmer" },
  { nome: "Fotógrafo", q: "photographer" },
  { nome: "Academia", q: "fitness gym" },
];
const FOTOS_DE_EMPRESA: AtalhoDoBanco[] = [
  { nome: "Escritório", q: "office" },
  { nome: "Reunião", q: "business meeting" },
  { nome: "Atendimento", q: "customer service" },
  { nome: "Loja", q: "shop store" },
  { nome: "Fábrica", q: "factory" },
  { nome: "Entrega", q: "delivery boxes" },
  { nome: "Computador", q: "laptop desk" },
  { nome: "Dinheiro", q: "money" },
];

/* As categorias do topo de Elementos (como as do Canva): cada uma abre só as seções dela. */
const CATEGORIAS_DOS_ELEMENTOS: Array<{ chave: string; nome: string; cor: string; Icone: typeof Shapes; secoes: string[] }> = [
  { chave: "formas", nome: "Formas", cor: "#06B6D4", Icone: Shapes, secoes: ["formas", "linhas"] },
  { chave: "figurinhas", nome: "Figurinhas", cor: "#F59E0B", Icone: Smile, secoes: ["figurinhas"] },
  { chave: "recortes", nome: "Recortes sem fundo", cor: "#0369A1", Icone: Scissors, secoes: ["recortes"] },
  { chave: "icones-3d", nome: "Ícones 3D", cor: "#7E22CE", Icone: Rotate3d, secoes: ["icones-3d"] },
  { chave: "ornamentos", nome: "Ornamentos e florais", cor: "#B45309", Icone: Flower2, secoes: ["ornamentos"] },
  { chave: "festas", nome: "Aniversário e festas", cor: "#EC4899", Icone: PartyPopper, secoes: ["festas-pacotes", "festas-figurinhas", "festas-recortes", "festas-icones", "festas-fotos"] },
  { chave: "profissoes", nome: "Profissões", cor: "#2563EB", Icone: BriefcaseBusiness, secoes: ["profissoes-figurinhas", "profissoes-icones", "profissoes-fotos"] },
  { chave: "empresas", nome: "Empresas", cor: "#0D9488", Icone: Store, secoes: ["empresas-pacotes", "empresas-figurinhas", "empresas-recortes", "empresas-3d", "empresas-icones", "empresas-fotos"] },
  { chave: "datas", nome: "Datas comemorativas", cor: "#EF4444", Icone: CalendarHeart, secoes: ["datas", "natal-3d", "halloween-3d"] },
  { chave: "icones", nome: "Ícones", cor: "#8B5CF6", Icone: Sparkles, secoes: ["icones"] },
  { chave: "vendas", nome: "Selos e ofertas", cor: "#F97316", Icone: Tag, secoes: ["selos", "cupons", "chamadas"] },
  { chave: "molduras", nome: "Molduras", cor: "#22C55E", Icone: Frame, secoes: ["molduras", "molduras-svg"] },
  { chave: "3d", nome: "Elementos 3D", cor: "#4F46E5", Icone: Box, secoes: ["3d"] },
  { chave: "texturas", nome: "Texturas e fundos", cor: "#CA8A04", Icone: Layers, secoes: ["texturas", "fundos"] },
  { chave: "graficos", nome: "Gráficos e QR", cor: "#65A30D", Icone: ChartColumn, secoes: ["graficos"] },
  { chave: "video", nome: "Para vídeo", cor: "#C026D3", Icone: Clapperboard, secoes: ["video-telas", "video-destaques", "video-legendas", "video-particulas"] },
];

/* Os grupos da biblioteca que a busca de cada categoria procura (as outras procuram em tudo). */
function gruposDaCategoria(chave: string): string[] | undefined {
  if (chave === "icones-3d") return GRUPOS_DOS_ICONES_3D;
  if (chave === "recortes") return GRUPOS_DOS_RECORTES;
  if (chave === "ornamentos") return GRUPOS_DOS_ORNAMENTOS;
  if (chave === "molduras") return ["molduras-svg", "cantos", "divisores"];
  if (chave === "datas") return ["3d-natal", "3d-halloween"];
  if (chave === "empresas") return ["negocios", "3d-geral"];
  return undefined;
}

/* A lista inteira (sem categoria aberta): o que serve a qualquer arte primeiro; o que é só de vídeo no fim. */
const ORDEM_DOS_ELEMENTOS = ["formas", "figurinhas", "recortes-amostra", "icones-3d-amostra", "ornamentos-amostra", "festas-pacotes", "datas", "empresas-pacotes", "icones", "selos", "cupons", "chamadas", "molduras", "linhas", "3d", "texturas", "fundos", "graficos", "video-telas", "video-destaques", "video-legendas", "video-particulas"];

export function PainelDaBiblioteca({
  aba,
  doc,
  pedidoDoModelo,
  logo,
  proporcaoDoLogo,
  produtos,
  fotoDosModelos,
  enviando,
  artes,
  arteAberta,
  galeriaIndisponivel,
  imagensDaPessoa,
  aoTrocarDocumento,
  aoPor,
  aoPorVarias,
  aoEnviarImagem,
  aoEscolherFotoDosModelos,
  aoAbrirArte,
}: {
  aba: AbaDaBiblioteca;
  doc: DocumentoDoBanner;
  pedidoDoModelo: PedidoDoModelo;
  logo: string | null;
  proporcaoDoLogo: number | null;
  produtos: ProdutoDoEditor[];
  fotoDosModelos: string | null;
  enviando: boolean;
  /* As artes guardadas (null = carregando). */
  artes: ResumoDaArte[] | null;
  arteAberta: string | null;
  galeriaIndisponivel: boolean;
  /* Imagens que a pessoa já enviou (nesta arte e nas guardadas). */
  imagensDaPessoa: string[];
  aoTrocarDocumento: (d: DocumentoDoBanner) => void;
  aoPor: (p: Peca) => void;
  aoPorVarias: (p: Peca[]) => void;
  aoEnviarImagem: (arquivos: FileList | File[] | null) => void;
  aoEscolherFotoDosModelos: (url: string | null) => void;
  aoAbrirArte: (arte: ResumoDaArte) => void;
}) {
  const medida = useMemo(() => ({ largura: doc.largura, altura: doc.altura }), [doc.largura, doc.altura]);
  /* Os feitos para este tamanho primeiro (reels no story, post e carrossel no post). */
  /*
   * Na lista vai a FOTO pronta de cada modelo (a mesma da galeria), sem montar
   * as peças; o clique monta o modelo com a cor, o logo e a foto do produto
   * escolhida aqui.
   */
  const amostras = useMemo(
    () =>
      MODELOS_DO_EDITOR.filter((m) => modeloAparece(m, pedidoDoModelo.largura, pedidoDoModelo.altura, Boolean(doc.impressao))).map((m) => {
        const tipo = TIPOS_DE_MODELO.find((t) => t.filtro(m)) ?? null;
        const neutro = tipo ? m.montar(pedidoNeutro(tipo)) : null;
        return { modelo: m, foto: tipo && neutro ? miniaturaDoModelo(tipo.chave, m.chave, neutro) : null, neutro };
      }),
    // eslint-disable-next-line react-hooks/exhaustive-deps -- a impressão só muda com o tamanho
    [pedidoDoModelo.largura, pedidoDoModelo.altura, Boolean(doc.impressao)],
  );
  const [filtro, setFiltro] = useState("");
  /* A categoria aberta em Elementos (como as do Canva); null = a lista com todas. */
  const [categoria, setCategoria] = useState<string | null>(null);
  const arquivo = useRef<HTMLInputElement | null>(null);
  const comFoto = produtos.filter((p) => p.image_url && p.name.toLowerCase().includes(filtro.trim().toLowerCase()));

  if (aba === "modelos") {
    return (
      <div>
        {produtos.some((p) => p.image_url) ? (
          <Secao titulo="Foto do produto nos modelos">
            <div className="-mx-1 flex gap-1.5 overflow-x-auto px-1 pb-1">
              <button type="button" onClick={() => aoEscolherFotoDosModelos(null)} className={`flex h-14 w-14 shrink-0 items-center justify-center rounded-[10px] text-[11px] font-semibold ring-1 ${!fotoDosModelos ? "text-[#6D28D9] ring-2 ring-[#8B5CF6]" : "text-[#77708F] ring-black/10"}`}>
                Sem foto
              </button>
              {produtos
                .filter((p) => p.image_url)
                .slice(0, 30)
                .map((p) => (
                  <button key={p.id} type="button" title={p.name} onClick={() => aoEscolherFotoDosModelos(p.image_url || null)} className={`h-14 w-14 shrink-0 overflow-hidden rounded-[10px] ring-1 ${fotoDosModelos === p.image_url ? "ring-2 ring-[#8B5CF6]" : "ring-black/10"}`}>
                    {/* eslint-disable-next-line @next/next/no-img-element */}
                    <img src={p.image_url || ""} alt="" className="h-full w-full object-cover" />
                  </button>
                ))}
            </div>
          </Secao>
        ) : null}
        <Secao titulo="Modelos prontos">
          <div className="space-y-3">
            {amostras.map(({ modelo, foto, neutro }) => (
              <button key={modelo.chave} type="button" onClick={() => aoTrocarDocumento(modelo.montar(pedidoDoModelo))} className="group block w-full text-left" data-modelo-do-painel={modelo.chave}>
                <div className="overflow-hidden rounded-[12px] ring-1 ring-black/5 transition group-hover:ring-2 group-hover:ring-[#8B5CF6]">
                  {modelo.chave === "branco" ? (
                    <div className="flex items-center justify-center bg-[#F3F1F9] text-[12px] font-bold text-[#7C3AED]" style={{ aspectRatio: `${Math.max(0.5, Math.min(3.6, pedidoDoModelo.largura / pedidoDoModelo.altura))}` }} data-foto-do-painel>
                      Só o fundo
                    </div>
                  ) : foto?.video && foto.video.marca === foto.marca ? (
                    <span className="pointer-events-none block" data-foto-do-painel>
                      <VideoQueToca src={foto.video.src} capa={foto.src} largura={foto.largura} altura={foto.altura} nome={modelo.nome} duracao={foto.video.duracao} />
                    </span>
                  ) : foto ? (
                    /* eslint-disable-next-line @next/next/no-img-element */
                    <img src={foto.src} alt={modelo.nome} width={foto.largura} height={foto.altura} loading="lazy" decoding="async" draggable={false} className="block h-auto w-full select-none" data-foto-do-painel />
                  ) : (
                    <Miniatura doc={neutro ?? modelo.montar(pedidoDoModelo)} largura={292} />
                  )}
                </div>
                <div className="mt-1.5 flex items-baseline justify-between gap-2 px-0.5">
                  <span className="text-[12.5px] font-semibold text-[#1F1B2E]">{modelo.nome}</span>
                  <span className="truncate text-[11px] text-[#8A849E]">
                    {modelo.feitoPara === "carrossel" ? `Carrossel · ${modelo.paginas ?? 2} páginas` : modelo.feitoPara === "story" ? "Reels" : modelo.feitoPara === "caneca" ? "Caneca" : modelo.ramo ? RAMOS_DOS_MODELOS[modelo.ramo] : modelo.descricao}
                  </span>
                </div>
              </button>
            ))}
          </div>
        </Secao>
      </div>
    );
  }

  if (aba === "elementos") {
    const termo = semAcento(filtro.trim());
    const filtrados = <T extends { nome: string; busca?: string }>(lista: T[], extra = "") => (termo ? lista.filter((c) => semAcento(`${c.nome} ${c.busca ?? ""} ${extra}`).includes(termo)) : lista);
    /* Cartões de elementos prontos (várias peças): os de vídeo tocam a animação com o mouse em cima. */
    const cartoes = (titulo: string, lista: PacoteDoPainel[], marca: string, jeito: "video" | "selo" | "data" = "selo", extra = "") => {
      const vistos = filtrados(lista, extra);
      if (!vistos.length) return null;
      return (
        <Secao key={marca} titulo={titulo}>
          <div className="grid grid-cols-2 gap-2" {...{ [marca]: "" }}>
            {vistos.map((c) => (
              <button
                key={c.chave}
                type="button"
                onClick={() => aoPorVarias(c.criar(medida))}
                {...arrastavel(() => c.criar(medida))}
                className="group rounded-[12px] p-1 text-left transition hover:bg-[#F3F0FF]"
                title={jeito === "video" ? `${c.nome} (já animado: passe o mouse para ver)` : c.nome}
                {...(jeito === "data" ? { "data-pacote": c.chave } : { "data-elemento-pronto": c.chave })}
              >
                <div className={`overflow-hidden rounded-[10px] ring-1 ring-black/5 transition group-hover:ring-2 group-hover:ring-[#8B5CF6] ${jeito === "video" ? "" : "bg-[#F3F1F9]"}`}>
                  {jeito === "video" ? (
                    <AmostraQueToca pecas={c.criar(medida)} medida={medida} largura={142} altura={96} fundo={/cursor|mao|contagem|botao|v-p-/.test(c.chave) ? "#2A2540" : "#ECE8F6"} />
                  ) : (
                    <AmostraDeVarias pecas={c.criar(medida)} largura={142} altura={96} fundo={jeito === "selo" ? "#F1EEF8" : undefined} />
                  )}
                </div>
                <div className="mt-1 truncate px-0.5 text-[11px] font-medium text-[#4B4760]">{c.nome}</div>
              </button>
            ))}
          </div>
        </Secao>
      );
    };
    /* Figurinhas, ícones e fotos só aparecem sem a busca de cima (eles têm a busca própria). */
    const semBusca = (no: React.ReactNode) => (termo ? null : no);
    const SECOES: Record<string, () => React.ReactNode> = {
      formas: () => (
        <Secao key="formas" titulo="Formas" acao={<span className="flex items-center gap-1 text-[10.5px] text-[#8A849E]" title="Atalhos de teclado">
            <Tecla>R</Tecla> retângulo <Tecla>C</Tecla> círculo
          </span>}>
          <Grade pecas={TODAS_AS_FORMAS} medida={medida} aoPor={aoPor} colunas={3} altura={72} filtro={filtro} />
        </Secao>
      ),
      figurinhas: () => semBusca(
        <Secao key="figurinhas" titulo="Figurinhas">
          <BibliotecaDeFigurinhas medida={medida} aoPor={aoPor} />
        </Secao>,
      ),
      "festas-pacotes": () => cartoes("Aniversário e festas", DATAS_ESPECIAIS.filter((c) => DATAS_DA_VIDA.includes(c.chave)), "data-festas", "data", "aniversario festa"),
      "festas-figurinhas": () => semBusca(
        <Secao key="festas-figurinhas" titulo="Figurinhas de festa">
          <BibliotecaDeFigurinhas medida={medida} aoPor={aoPor} grupos={["festas", "amor"]} />
        </Secao>,
      ),
      "festas-recortes": () => semBusca(<BibliotecaDeImagens key="festas-recortes" titulo="Corações, confetes e efeitos" medida={medida} aoPor={aoPor} grupos={["clipart", "efeitos"]} />),
      "empresas-recortes": () => semBusca(<BibliotecaDeImagens key="empresas-recortes" titulo="Pessoas trabalhando (recortes)" medida={medida} aoPor={aoPor} grupos={["negocios"]} limite={12} aoVerTodos={() => setCategoria("recortes")} />),
      "empresas-3d": () => semBusca(<BibliotecaDeImagens key="empresas-3d" titulo="Ícones 3D" medida={medida} aoPor={aoPor} grupos={["3d-geral"]} limite={12} aoVerTodos={() => setCategoria("icones-3d")} />),
      "natal-3d": () => semBusca(<BibliotecaDeImagens key="natal-3d" titulo="Natal em 3D" medida={medida} aoPor={aoPor} grupos={["3d-natal"]} />),
      "halloween-3d": () => semBusca(<BibliotecaDeImagens key="halloween-3d" titulo="Halloween em 3D" medida={medida} aoPor={aoPor} grupos={["3d-halloween"]} />),
      recortes: () => semBusca(<BibliotecaDeImagens key="recortes" titulo="Recortes sem fundo (PNG)" medida={medida} aoPor={aoPor} grupos={GRUPOS_DOS_RECORTES} />),
      "icones-3d": () => semBusca(<BibliotecaDeImagens key="icones-3d" titulo="Ícones 3D" medida={medida} aoPor={aoPor} grupos={GRUPOS_DOS_ICONES_3D} />),
      "recortes-amostra": () => semBusca(<BibliotecaDeImagens key="recortes-amostra" titulo="Recortes sem fundo (PNG)" medida={medida} aoPor={aoPor} grupos={GRUPOS_DOS_RECORTES} limite={9} aoVerTodos={() => setCategoria("recortes")} />),
      /* Os ornamentos em SVG: a cor de cada parte troca nas bolinhas da barra. */
      ornamentos: () => semBusca(<BibliotecaDeImagens key="ornamentos" titulo="Ornamentos e florais (cor trocável)" medida={medida} aoPor={aoPor} grupos={GRUPOS_DOS_ORNAMENTOS} />),
      "ornamentos-amostra": () => semBusca(<BibliotecaDeImagens key="ornamentos-amostra" titulo="Ornamentos e florais (cor trocável)" medida={medida} aoPor={aoPor} grupos={GRUPOS_DOS_ORNAMENTOS} limite={9} aoVerTodos={() => setCategoria("ornamentos")} />),
      "molduras-svg": () => semBusca(<BibliotecaDeImagens key="molduras-svg" titulo="Molduras, cantos e bordas decorativas" medida={medida} aoPor={aoPor} grupos={["molduras-svg", "cantos", "divisores"]} />),
      "icones-3d-amostra": () => semBusca(<BibliotecaDeImagens key="icones-3d-amostra" titulo="Ícones 3D" medida={medida} aoPor={aoPor} grupos={GRUPOS_DOS_ICONES_3D} limite={9} aoVerTodos={() => setCategoria("icones-3d")} />),
      "festas-icones": () => semBusca(
        <Secao key="festas-icones" titulo="Ícones de festa">
          <IconesEscolhidos medida={medida} aoPor={aoPor} nomes={ICONES_DE_FESTA} />
        </Secao>,
      ),
      "festas-fotos": () => semBusca(
        <Secao key="festas-fotos" titulo="Fotos de festa">
          <BancoDeImagens aoEnviarImagem={(lista) => aoEnviarImagem(lista)} atalhos={FOTOS_DE_FESTA} inicial={FOTOS_DE_FESTA[0]} />
        </Secao>,
      ),
      "profissoes-figurinhas": () => semBusca(
        <Secao key="profissoes-figurinhas" titulo="Figurinhas de profissões">
          <BibliotecaDeFigurinhas medida={medida} aoPor={aoPor} grupos={["profissoes", "objetos"]} />
        </Secao>,
      ),
      "profissoes-icones": () => semBusca(
        <Secao key="profissoes-icones" titulo="Ícones de profissões">
          <IconesEscolhidos medida={medida} aoPor={aoPor} nomes={ICONES_DE_PROFISSAO} />
        </Secao>,
      ),
      "profissoes-fotos": () => semBusca(
        <Secao key="profissoes-fotos" titulo="Fotos de profissões">
          <BancoDeImagens aoEnviarImagem={(lista) => aoEnviarImagem(lista)} atalhos={FOTOS_DE_PROFISSOES} inicial={FOTOS_DE_PROFISSOES[0]} />
        </Secao>,
      ),
      "empresas-pacotes": () => cartoes("Para a empresa", DATAS_ESPECIAIS.filter((c) => DATAS_DA_EMPRESA.includes(c.chave)), "data-empresa", "data", "empresa negocio loja"),
      "empresas-figurinhas": () => semBusca(
        <Secao key="empresas-figurinhas" titulo="Figurinhas de empresa">
          <BibliotecaDeFigurinhas medida={medida} aoPor={aoPor} grupos={["negocios"]} />
        </Secao>,
      ),
      "empresas-icones": () => semBusca(
        <Secao key="empresas-icones" titulo="Ícones de empresa">
          <IconesEscolhidos medida={medida} aoPor={aoPor} nomes={ICONES_DE_EMPRESA} />
        </Secao>,
      ),
      "empresas-fotos": () => semBusca(
        <Secao key="empresas-fotos" titulo="Fotos de empresa">
          <BancoDeImagens aoEnviarImagem={(lista) => aoEnviarImagem(lista)} atalhos={FOTOS_DE_EMPRESA} inicial={FOTOS_DE_EMPRESA[0]} />
        </Secao>,
      ),
      datas: () => cartoes("Datas comemorativas", DATAS_ESPECIAIS.filter((c) => !DATAS_DA_VIDA.includes(c.chave) && !DATAS_DA_EMPRESA.includes(c.chave)), "data-datas-especiais", "data"),
      icones: () => (
        <Secao key="icones" titulo="Ícones">
          <Grade pecas={ICONES_PRONTOS} medida={medida} aoPor={aoPor} colunas={3} altura={60} filtro={filtro} />
          <div className="mt-3">
            <BuscaDeIcones medida={medida} aoPor={aoPor} />
          </div>
        </Secao>
      ),
      selos: () => cartoes("Selos de venda", SELOS_DE_VENDA, "data-selos-de-venda"),
      cupons: () => cartoes("Cupons e vales", CUPONS_PRONTOS, "data-cupons"),
      chamadas: () => (
        <Secao key="chamadas" titulo="Selos, etiquetas e faixas">
          <Grade pecas={CHAMADAS_PRONTAS} medida={medida} aoPor={aoPor} filtro={filtro} />
        </Secao>
      ),
      molduras: () => (
        <Secao key="molduras" titulo="Molduras de foto">
          <p className="mb-2 text-[11.5px] leading-snug text-[#8A849E]">Ponha a moldura e solte uma foto em cima dela.</p>
          <Grade pecas={MOLDURAS_PRONTAS} medida={medida} aoPor={aoPor} colunas={3} altura={72} filtro={filtro} />
        </Secao>
      ),
      linhas: () => (
        <Secao key="linhas" titulo="Linhas e setas">
          <p className="mb-2 text-[11.5px] leading-snug text-[#8A849E]">Para desenhar à mão ou com a caneta, use as ferramentas no lado direito da arte.</p>
          <Grade pecas={TRACOS_PRONTOS} medida={medida} aoPor={aoPor} colunas={2} altura={56} filtro={filtro} />
        </Secao>
      ),
      "3d": () => {
        const vistos = filtrados(ELEMENTOS_3D, "3d");
        if (!vistos.length) return null;
        return (
          <Secao key="3d" titulo="Elementos 3D">
            <div className="grid grid-cols-4 gap-1.5" data-elementos-3d>
              {vistos.map((c) => (
                <button key={c.chave} type="button" onClick={() => aoPorVarias(c.criar(medida))} {...arrastavel(() => c.criar(medida))} className="group rounded-[10px] p-0.5 text-center transition hover:bg-[#F3F0FF]" title={c.nome} data-pacote={c.chave}>
                  <div className="overflow-hidden rounded-[9px] bg-[#F3F1F9] ring-1 ring-black/5 transition group-hover:ring-2 group-hover:ring-[#8B5CF6]">
                    <AmostraDeVarias pecas={c.criar(medida)} largura={66} altura={62} />
                  </div>
                  <div className="mt-0.5 truncate text-[10.5px] font-medium text-[#4B4760]">{c.nome}</div>
                </button>
              ))}
            </div>
          </Secao>
        );
      },
      texturas: () => (
        <Secao key="texturas" titulo="Texturas">
          <Grade pecas={TEXTURAS_PRONTAS} medida={medida} aoPor={aoPor} colunas={3} altura={60} filtro={filtro} />
        </Secao>
      ),
      fundos: () => (
        <Secao key="fundos" titulo="Fundos">
          <Grade pecas={FUNDOS_PRONTOS} medida={medida} aoPor={aoPor} colunas={3} altura={52} filtro={filtro} />
        </Secao>
      ),
      graficos: () => (
        <Secao key="graficos" titulo="QR code, tabela e gráficos">
          <div className="mb-2 grid grid-cols-2 gap-2">
            <button type="button" onClick={() => aoPor(qrCode(medida))} {...arrastavel(() => qrCode(medida))} className="flex h-10 items-center justify-center rounded-[10px] bg-[#F1EFF6] text-[12.5px] font-bold text-[#1F1B2E] transition hover:bg-[#E9E4F8]">
              QR code
            </button>
            <button type="button" onClick={() => aoPor(tabela(medida))} {...arrastavel(() => tabela(medida))} className="flex h-10 items-center justify-center rounded-[10px] bg-[#F1EFF6] text-[12.5px] font-bold text-[#1F1B2E] transition hover:bg-[#E9E4F8]">
              Tabela
            </button>
          </div>
          <Grade pecas={GRAFICOS_PRONTOS} medida={medida} aoPor={aoPor} colunas={3} altura={64} filtro={filtro} />
        </Secao>
      ),
      "video-telas": () => cartoes("Para vídeo: telas e interface", TELAS_DE_VIDEO, "data-telas-de-video", "video", "video animado"),
      "video-destaques": () => cartoes("Para vídeo: destaques animados", DESTAQUES_DE_VIDEO, "data-destaques-de-video", "video", "video animado"),
      "video-legendas": () => cartoes("Para vídeo: títulos e legendas", LEGENDAS_DE_VIDEO, "data-legendas-de-video", "video", "video animado"),
      "video-particulas": () => cartoes("Para vídeo: partículas", PARTICULAS_DE_VIDEO, "data-particulas-de-video", "video", "video animado"),
    };
    const aberta = CATEGORIAS_DOS_ELEMENTOS.find((c) => c.chave === categoria) ?? null;
    const lista = aberta ? aberta.secoes : ORDEM_DOS_ELEMENTOS;
    return (
      <div>
        <Busca valor={filtro} aoMudar={setFiltro} placeholder={aberta ? `Procurar em ${aberta.nome.toLowerCase()}…` : "Procurar formas, selos, figurinhas…"} />
        {aberta ? (
          <div className="mb-4 flex items-center gap-2" data-categoria-aberta={aberta.chave}>
            <button type="button" onClick={() => setCategoria(null)} className="flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-[#F3F1F9] text-[#4B4760] transition hover:bg-[#EDE7FF] hover:text-[#6D28D9]" aria-label="Voltar para todas as categorias" title="Todas as categorias" data-voltar-categorias>
              <ArrowLeft size={16} />
            </button>
            <h3 className="text-[15px] font-bold text-[#1F1B2E]">{aberta.nome}</h3>
          </div>
        ) : termo ? null : (
          <Secao titulo="Explorar categorias">
            <div className="grid grid-cols-3 gap-x-1.5 gap-y-3" data-categorias-dos-elementos>
              {CATEGORIAS_DOS_ELEMENTOS.map((c) => (
                <button key={c.chave} type="button" onClick={() => setCategoria(c.chave)} className="group flex min-w-0 flex-col items-center gap-1.5 rounded-[12px] px-0.5 pb-1 pt-2 text-center transition hover:bg-[#F6F3FF]" data-categoria={c.chave}>
                  <span className="relative block h-[54px] w-[54px]">
                    <span className="absolute inset-0 translate-x-[5px] -translate-y-[4px] rotate-[8deg] rounded-[15px] opacity-30" style={{ background: c.cor }} />
                    <span className="absolute inset-0 flex items-center justify-center rounded-[15px] text-white shadow-[inset_0_-3px_0_rgba(0,0,0,0.14)]" style={{ background: c.cor }}>
                      <c.Icone size={25} strokeWidth={2.1} />
                    </span>
                  </span>
                  <span className="line-clamp-2 text-[11.5px] font-semibold leading-tight text-[#1F1B2E]">{c.nome}</span>
                </button>
              ))}
            </div>
          </Secao>
        )}
        {/* Com a busca: o que a biblioteca (recortes e ícones 3D) tem com essa palavra vem primeiro. */}
        {termo ? <BibliotecaDeImagens titulo="Recortes, ícones 3D e ornamentos" medida={medida} aoPor={aoPor} termo={filtro} grupos={aberta ? gruposDaCategoria(aberta.chave) : undefined} /> : null}
        {lista.map((chave) => SECOES[chave]?.() ?? null)}
      </div>
    );
  }

  if (aba === "texto") {
    const botaoGrande = "block w-full rounded-[10px] bg-[#F1EFF6] px-4 text-left text-[#1F1B2E] transition hover:bg-[#E9E4F8]";
    return (
      <div>
        <div className="mb-6 space-y-2">
          <button type="button" className={`${botaoGrande} py-3 text-[22px] font-extrabold`} onClick={() => aoPor(TEXTOS_PRONTOS[0].criar(medida))} {...arrastavel(() => TEXTOS_PRONTOS[0].criar(medida))}>
            Adicionar um título
          </button>
          <button type="button" className={`${botaoGrande} py-2.5 text-[16px] font-bold`} onClick={() => aoPor(TEXTOS_PRONTOS[1].criar(medida))} {...arrastavel(() => TEXTOS_PRONTOS[1].criar(medida))}>
            Adicionar um subtítulo
          </button>
          <button type="button" className={`${botaoGrande} flex items-center justify-between py-2 text-[13px]`} onClick={() => aoPor(TEXTOS_PRONTOS[2].criar(medida))} {...arrastavel(() => TEXTOS_PRONTOS[2].criar(medida))} title="Atalho: tecla T">
            Adicionar um texto <Tecla>T</Tecla>
          </button>
          <button type="button" className={`${botaoGrande} flex items-center justify-between py-2 text-[13px]`} onClick={() => aoPor(contadorDePaginas(medida))} title="O número da página e o total (01/05), fixo em todas as páginas">
            Contador de páginas <span className="rounded-md bg-[#EDE7FF] px-1.5 py-0.5 text-[11px] font-bold tabular-nums text-[#5B21B6]">01/05</span>
          </button>
        </div>
        <Secao titulo="Combinações de texto">
          <div className="grid grid-cols-2 gap-2">
            {COMBINACOES_PRONTAS.map((c) => (
              <button key={c.chave} type="button" onClick={() => aoPorVarias(c.criar(medida))} {...arrastavel(() => c.criar(medida))} className="group rounded-[12px] p-1 text-left transition hover:bg-[#F3F0FF]" title={c.nome}>
                <div className="overflow-hidden rounded-[10px] ring-1 ring-black/5 transition group-hover:ring-2 group-hover:ring-[#8B5CF6]">
                  <AmostraDeVarias pecas={c.criar(medida)} largura={142} altura={96} />
                </div>
                <div className="mt-1 truncate px-0.5 text-[11px] font-medium text-[#4B4760]">{c.nome}</div>
              </button>
            ))}
          </div>
        </Secao>
        <Secao titulo="Estilos de texto">
          <Grade pecas={TEXTOS_PRONTOS.slice(3)} medida={medida} aoPor={aoPor} />
        </Secao>
        <Secao titulo="Chamadas de venda">
          <Grade pecas={CHAMADAS_PRONTAS} medida={medida} aoPor={aoPor} />
        </Secao>
      </div>
    );
  }

  if (aba === "imagens") {
    return (
      <div>
        <input ref={arquivo} type="file" accept="image/png,image/jpeg,image/webp" multiple className="hidden" onChange={(e) => {
          aoEnviarImagem(e.target.files);
          /* Limpa a escolha: mandar as mesmas fotos de novo também funciona. */
          e.target.value = "";
        }} />
        <button
          type="button"
          disabled={enviando}
          onClick={() => arquivo.current?.click()}
          className="mb-2 flex w-full flex-col items-center justify-center gap-1.5 rounded-[14px] border-2 border-dashed border-[#C4B5FD] bg-[#F7F4FF] px-4 py-5 text-[#5B21B6] transition hover:border-[#8B5CF6] hover:bg-[#EFE9FF] disabled:opacity-60"
        >
          {enviando ? <Loader2 size={22} className="animate-spin" /> : <Upload size={22} />}
          <span className="text-[13.5px] font-bold">{enviando ? "Enviando…" : "Enviar imagem do computador"}</span>
          <span className="text-[11.5px] font-medium text-[#7C6BA8]">PNG, JPG ou WebP · também dá para arrastar para a arte ou colar (Ctrl+V)</span>
        </button>
        <div className="mb-6" />
        {imagensDaPessoa.length ? (
          <Secao titulo="Suas imagens">
            <div className="grid grid-cols-3 gap-1.5">
              {imagensDaPessoa.slice(0, 60).map((src) => (
                <button key={src} type="button" onClick={() => aoPor(imagem(medida, src, "foto"))} {...arrastavel(() => imagem(medida, src, "foto"))} className="aspect-square overflow-hidden rounded-[10px] bg-[#F3F1F9] ring-1 ring-black/5 transition hover:ring-2 hover:ring-[#8B5CF6]">
                  {/* eslint-disable-next-line @next/next/no-img-element */}
                  <img src={src} alt="" className="h-full w-full object-cover" />
                </button>
              ))}
            </div>
          </Secao>
        ) : null}
        {logo ? (
          <Secao titulo="Logo da loja">
            <button type="button" onClick={() => aoPor(imagem(medida, logo, "logo", proporcaoDoLogo || 3))} {...arrastavel(() => imagem(medida, logo, "logo", proporcaoDoLogo || 3))} className="flex h-20 w-full items-center justify-center rounded-[12px] bg-[#F3F1F9] p-3 ring-1 ring-black/5 transition hover:ring-2 hover:ring-[#8B5CF6]">
              {/* eslint-disable-next-line @next/next/no-img-element */}
              <img src={logo} alt="Logo da loja" className="max-h-full max-w-full object-contain" />
            </button>
          </Secao>
        ) : null}
        <BibliotecaDeImagens titulo="Recortes sem fundo e ícones 3D" medida={medida} aoPor={aoPor} />
        <Secao titulo="Banco de imagens grátis">
          <BancoDeImagens aoEnviarImagem={(lista) => aoEnviarImagem(lista)} inicial={ATALHOS_DO_BANCO[0]} />
        </Secao>
        <Secao titulo="Fotos dos produtos">
          <Busca valor={filtro} aoMudar={setFiltro} placeholder="Procurar produto" />
          {comFoto.length ? (
            <div className="grid grid-cols-3 gap-1.5">
              {comFoto.slice(0, 90).map((p) => (
                <button key={p.id} type="button" title={p.name} onClick={() => p.image_url && aoPor(imagem(medida, p.image_url, "foto"))} {...arrastavel(() => imagem(medida, p.image_url || "", "foto"))} className="aspect-square overflow-hidden rounded-[10px] bg-[#F3F1F9] ring-1 ring-black/5 transition hover:ring-2 hover:ring-[#8B5CF6]">
                  {/* eslint-disable-next-line @next/next/no-img-element */}
                  <img src={p.image_url || ""} alt={p.name} className="h-full w-full object-cover" />
                </button>
              ))}
            </div>
          ) : (
            <p className="text-[12.5px] text-[#8A849E]">Nenhum produto com foto{filtro ? " com esse nome" : ""}.</p>
          )}
        </Secao>
      </div>
    );
  }

  /* As camadas moram em camadas.tsx (o editor abre o painel delas direto). */
  if (aba === "artes") {
    if (galeriaIndisponivel) {
      return <p className="rounded-[12px] bg-[#FFF7E6] p-3 text-[12.5px] leading-snug text-[#8A5300]">A galeria de artes ainda não foi ligada no banco. Você pode criar e baixar normalmente; as artes passam a ficar guardadas assim que ela for ligada.</p>;
    }
    if (!artes) {
      return (
        <div className="flex items-center gap-2 text-[12.5px] text-[#8A849E]">
          <Loader2 size={15} className="animate-spin" /> Abrindo as suas artes…
        </div>
      );
    }
    if (!artes.length) return <p className="text-[12.5px] leading-snug text-[#8A849E]">Nenhuma arte guardada ainda. Esta aqui já fica guardada sozinha a cada mudança.</p>;
    return (
      <div>
        <p className="mb-3 text-[12px] leading-snug text-[#8A849E]">As artes ficam guardadas sozinhas. Abrir uma de outro tamanho adapta ela a este.</p>
        <div className="grid grid-cols-2 gap-2.5">
          {artes.map((arte) => {
            const escala = 142 / arte.largura;
            const alto = Math.min(150, arte.altura * escala);
            return (
              <button key={arte.id} type="button" onClick={() => aoAbrirArte(arte)} className={`group rounded-[12px] p-1 text-left transition hover:bg-[#F3F0FF] ${arte.id === arteAberta ? "bg-[#F1ECFF] ring-2 ring-[#8B5CF6]" : ""}`}>
                <div className="flex items-center justify-center overflow-hidden rounded-[10px] bg-[#F3F1F9] ring-1 ring-black/5" style={{ height: alto }}>
                  <FotoDaArteGuardada arte={arte} largura={Math.min(142, (alto / arte.altura) * arte.largura)} />
                </div>
                <div className="mt-1 truncate px-0.5 text-[11.5px] font-semibold text-[#1F1B2E]">{arte.nome}</div>
                <div className="px-0.5 text-[10.5px] text-[#8A849E]">
                  {arte.largura} × {arte.altura}
                </div>
              </button>
            );
          })}
        </div>
      </div>
    );
  }

  return null;
}

/* ===================================================== ajustes da peça */

const NOMES_DAS_FORMAS: Record<string, string> = {
  retangulo: "Retângulo",
  pilula: "Pílula",
  circulo: "Círculo",
  triangulo: "Triângulo",
  losango: "Losango",
  estrela: "Estrela",
  explosao: "Explosão",
  etiqueta: "Etiqueta",
  faixa: "Faixa",
  seta: "Seta",
  balao: "Balão",
  coracao: "Coração",
  onda: "Onda",
  hexagono: "Hexágono",
  pentagono: "Pentágono",
  octogono: "Octógono",
  paralelogramo: "Paralelo",
  cruz: "Cruz",
  raio: "Raio",
  nuvem: "Nuvem",
  gota: "Gota",
  semicirculo: "Meia-lua",
  anel: "Anel",
  rasgado: "Rasgado",
  fita: "Fita",
};

function GradeDeFormas<T extends string>({ formas, valor, aoEscolher }: { formas: readonly T[]; valor: T | null; aoEscolher: (f: T) => void }) {
  return (
    <div className="grid grid-cols-4 gap-1.5">
      {formas.map((f) => {
        const amostra = {
          id: `amostra-${f}`,
          tipo: "forma",
          x: 0,
          y: 0,
          w: 44,
          h: 32,
          rot: 0,
          opacidade: 1,
          forma: (f === "pilula" ? "retangulo" : f) as Forma,
          pintura: solida(valor === f ? "#7C3AED" : "#C8C2DA"),
          raio: f === "pilula" ? 16 : 6,
          borda: null,
          sombra: null,
          pontas: f === "estrela" ? 5 : 14,
          profundidade: f === "estrela" ? 0.55 : 0.22,
        } as PecaDeForma;
        return (
          <button
            key={f}
            type="button"
            title={NOMES_DAS_FORMAS[f] || f}
            onClick={() => aoEscolher(f)}
            className={`flex flex-col items-center gap-1 rounded-[10px] px-1 py-2 text-[10px] font-medium transition ${valor === f ? "bg-[#F1ECFF] text-[#5B21B6] ring-2 ring-[#8B5CF6]" : "bg-[#F6F4FB] text-[#6B6584] hover:bg-[#EEEAF8]"}`}
          >
            <span className="relative block h-[32px] w-[44px]" dangerouslySetInnerHTML={{ __html: conteudoDaPeca(amostra) }} />
            <span className="max-w-full truncate">{NOMES_DAS_FORMAS[f] || f}</span>
          </button>
        );
      })}
    </div>
  );
}

const DEGRADES_PRONTOS: Pintura[] = [
  degrade("#FFD400", "#FF5A00", 90),
  degrade("#FF3D00", "#E11D48", 120),
  degrade("#F472B6", "#7C3AED", 120),
  degrade("#7C3AED", "#2563EB", 120),
  degrade("#06B6D4", "#10B981", 120),
  degrade("#84CC16", "#047857", 120),
  degrade("#1E1B4B", "#0B0618", 160),
  degrade("#FFFFFF", "#E4E4E7", 180),
];

/*
 * As cores de um elemento em SVG (como o Canva): as bolinhas de todas as
 * cores dele em cima (a escolhida com o anel), a paleta para trocar a
 * escolhida e o "voltar às cores originais".
 */
function CoresDoVetor({ p, mudar, escolhida, aoEscolher }: { p: PecaDeImagem; mudar: (c: Partial<PecaDeImagem>) => void; escolhida: string; aoEscolher?: (original: string) => void }) {
  const vetor = p.vetor!;
  const atual = (c: string) => vetor.trocas?.[c] ?? c;
  const trocar = (original: string, cor: string) => mudar({ vetor: { ...vetor, trocas: { ...(vetor.trocas || {}), [original]: cor.toUpperCase() } } });
  const trocadas = Object.entries(vetor.trocas || {}).filter(([de, para]) => de !== para).length;
  return (
    <div data-cores-do-vetor>
      <Secao titulo="Cores deste elemento">
        <div className="flex flex-wrap gap-2">
          {vetor.cores.map((c) => (
            <button key={c} type="button" title={c === atual(c) ? c : `${c} → ${atual(c)}`} onClick={() => aoEscolher?.(c)} className={`h-9 w-9 rounded-full ring-1 ring-black/15 transition hover:scale-110 ${c === escolhida ? "ring-2 ring-[#8B5CF6] ring-offset-2" : ""}`} style={{ background: atual(c) }} data-cor-do-vetor={c} />
          ))}
        </div>
        {trocadas ? (
          <button type="button" onClick={() => mudar({ vetor: { cores: vetor.cores } })} className="mt-3 text-[12px] font-bold text-[#6D28D9] hover:underline">
            Voltar às cores originais
          </button>
        ) : null}
      </Secao>
      <PainelDeCor valor={solida(atual(escolhida))} aoMudar={(pintura) => trocar(escolhida, pintura.cor)} rotulo="Cor escolhida" soCor />
    </div>
  );
}

/** O painel grande de cor (como o do Canva): paleta da loja, usadas no banner, paleta viva, degradês prontos e o ajuste fino. */
function PainelDeCor({
  valor,
  aoMudar,
  rotulo,
  soCor = false,
  permiteSem = false,
  comAlcas = false,
}: {
  valor: Pintura;
  aoMudar: (p: Pintura) => void;
  rotulo: string;
  soCor?: boolean;
  permiteSem?: boolean;
  /* A peça mostra as alças do degradê no palco (forma, texto, ícone). */
  comAlcas?: boolean;
}) {
  const { daLoja, usadas } = useContext(ContextoDasCores);
  const recentes = useCoresRecentes();
  /* Trocar a cor mantém a transparência que já havia. */
  const porCor = (cor: string) => {
    lembrarCor(cor);
    aoMudar({ ...solida(cor), ...(valor.tipo === "solida" && valor.alfa !== undefined ? { alfa: valor.alfa } : {}) });
  };
  const amostra = (cor: string, chave: string) => (
    <button
      key={chave}
      type="button"
      title={cor}
      onClick={() => porCor(cor)}
      className={`h-9 w-9 rounded-[10px] ring-1 ring-black/10 transition hover:scale-110 ${valor.tipo === "solida" && valor.cor === cor ? "ring-2 ring-[#8B5CF6] ring-offset-2" : ""}`}
      style={{ background: cor }}
    />
  );
  return (
    <div>
      <div className="mb-5 flex items-center gap-2">
        <BotaoContaGotas aoPegar={porCor} grande />
        <span className="text-[12px] leading-snug text-[#8A849E]">Conta-gotas: pegue uma cor de qualquer ponto da arte.</span>
      </div>
      {recentes.length ? (
        <Secao titulo="Recentes">
          <div className="flex flex-wrap gap-2">{recentes.map((c) => amostra(c, `r${c}`))}</div>
        </Secao>
      ) : null}
      {daLoja.length ? (
        <Secao titulo="Cores da loja">
          <div className="flex flex-wrap gap-2">{daLoja.map((c) => amostra(c, `l${c}`))}</div>
        </Secao>
      ) : null}
      {usadas.length ? (
        <Secao titulo="Neste banner">
          <div className="flex flex-wrap gap-2">{usadas.slice(0, 16).map((c) => amostra(c, `u${c}`))}</div>
        </Secao>
      ) : null}
      <Secao titulo="Cores">
        <div className="grid grid-cols-7 gap-2">{PALETA_VIVA.map((c) => amostra(c, `p${c}`))}</div>
      </Secao>
      {soCor ? null : (
        <Secao titulo="Degradês">
          <div className="grid grid-cols-7 gap-2">
            {DEGRADES_PRONTOS.map((d, i) => (
              <button key={i} type="button" onClick={() => aoMudar(d)} className="h-9 w-9 rounded-[10px] ring-1 ring-black/10 transition hover:scale-110" style={{ background: `linear-gradient(${d.angulo}deg, ${d.cor}, ${d.cor2})` }} />
            ))}
          </div>
        </Secao>
      )}
      {soCor ? null : (
        <Secao titulo="Metálicos">
          <div className="grid grid-cols-5 gap-x-2 gap-y-2.5" data-metalicos>
            {DEGRADES_METALICOS.map((m) => {
              const escolhido = valor.tipo === "linear" && valor.cor === m.pintura.cor && valor.cor2 === m.pintura.cor2;
              return (
                <button key={m.chave} type="button" title={m.nome} data-metalico={m.chave} onClick={() => aoMudar({ ...m.pintura, paradas: m.pintura.paradas?.map((q) => ({ ...q })), ...(valor.tipo === "linear" && valor.pontos ? { pontos: valor.pontos } : {}) })} className="group flex min-w-0 flex-col items-center gap-1">
                  <span className={`block h-9 w-full rounded-[10px] ring-1 ring-black/10 transition group-hover:scale-105 ${escolhido ? "ring-2 ring-[#8B5CF6] ring-offset-2" : ""}`} style={{ background: cssDaPintura(m.pintura) }} />
                  <span className="max-w-full truncate text-[10.5px] font-medium text-[#4B4760]">{m.nome}</span>
                </button>
              );
            })}
          </div>
        </Secao>
      )}
      {comAlcas && valor.tipo === "linear" ? <DicaDasAlcas texto="Arraste as alças na peça para mudar a direção e onde cada cor começa. Clique numa alça para ajustar quanto ela cobre." /> : null}
      <Secao titulo={`Ajuste fino · ${rotulo}`}>
        {soCor ? (
          <CampoDePintura valor={{ ...valor, tipo: "solida" }} aoMudar={(p) => aoMudar({ ...p, tipo: "solida" })} rotulo={rotulo} />
        ) : (
          <CampoDePintura valor={valor} aoMudar={aoMudar} rotulo={rotulo} permiteSem={permiteSem} />
        )}
      </Secao>
    </div>
  );
}

function PainelDaFonte({ p, mudar }: { p: PecaDeTexto; mudar: (c: Partial<PecaDeTexto>) => void }) {
  const [filtro, setFiltro] = useState("");
  const comTrecho = useTrechoEscolhido();
  const grupos = TIPOS_DE_FONTE;
  /* O painel mostra cada fonte no desenho dela: a biblioteca inteira carrega quando ele abre. */
  useEffect(() => garantirFontes(FONTES.map((f) => f.chave)), []);
  return (
    <div>
      {comTrecho ? <p className="mb-3 rounded-[10px] bg-[#EDE7FF] px-3 py-2 text-[12px] font-semibold text-[#5B21B6]">A fonte vale só para o trecho escolhido.</p> : null}
      <Busca valor={filtro} aoMudar={setFiltro} placeholder="Procurar fonte" />
      {[
        { titulo: "Com glifos (letras com floreio)", lista: FONTES_COM_GLIFOS, selo: "glifos", marca: "data-fontes-com-glifos" },
        { titulo: "Da marca", lista: fontesDaMarca().filter((f) => !ehFonteComGlifos(f.chave)), selo: "sua", marca: "data-fontes-da-marca" },
      ].map(({ titulo, lista, selo, marca }) =>
        lista.filter((f) => f.nome.toLowerCase().includes(filtro.trim().toLowerCase())).length ? (
        <Secao key={titulo} titulo={titulo}>
          {selo === "glifos" ? <p className="-mt-1 mb-2 text-[11.5px] leading-snug text-[#8A849E]">Escreva o nome e abra os Glifos (Ω) para trocar as letras pelas de perninha.</p> : null}
          <div className="space-y-1" {...{ [marca]: "" }}>
            {lista
              .filter((f) => f.nome.toLowerCase().includes(filtro.trim().toLowerCase()))
              .map((f) => (
                <button
                  key={f.chave}
                  type="button"
                  onMouseDown={(e) => e.preventDefault()}
                  onClick={() => {
                    if (!(comTrecho && formatarTrecho("fonte", f.chave))) mudar({ fonte: f.chave });
                  }}
                  className={`flex w-full items-center justify-between rounded-[10px] px-3 py-2.5 text-left transition ${p.fonte === f.chave ? "bg-[#F1ECFF] ring-2 ring-[#8B5CF6]" : "hover:bg-[#F6F4FB]"}`}
                >
                  <span className="truncate text-[19px] leading-none text-[#1F1B2E]" style={{ fontFamily: `'${f.chave}', sans-serif` }} data-fonte={f.chave}>
                    {f.nome.replace(" (com glifos)", "")}
                  </span>
                  <span className="text-[10.5px] text-[#9A94AE]">{selo}</span>
                </button>
              ))}
          </div>
        </Secao>
        ) : null,
      )}
      {grupos.map(([tipo, titulo]) => {
        const lista = FONTES.filter((f) => f.tipo === tipo && f.familia.toLowerCase().includes(filtro.trim().toLowerCase()));
        if (!lista.length) return null;
        return (
          <Secao key={tipo} titulo={titulo}>
            <div className="space-y-1">
              {lista.map((f) => (
                <button
                  key={f.chave}
                  type="button"
                  onMouseDown={(e) => e.preventDefault()}
                  onClick={() => {
                    if (!(comTrecho && formatarTrecho("fonte", f.familia))) mudar({ fonte: f.chave });
                  }}
                  className={`flex w-full items-center justify-between rounded-[10px] px-3 py-2.5 text-left transition ${p.fonte === f.chave ? "bg-[#F1ECFF] ring-2 ring-[#8B5CF6]" : "hover:bg-[#F6F4FB]"}`}
                >
                  <span className="text-[19px] leading-none text-[#1F1B2E]" style={{ fontFamily: `'${f.familia}'`, fontWeight: f.pesos[f.pesos.length - 1] }}>
                    {f.familia}
                  </span>
                  <span className="text-[10.5px] text-[#9A94AE]">{f.pesos.length > 1 ? `${f.pesos.length} pesos` : ""}</span>
                </button>
              ))}
            </div>
          </Secao>
        );
      })}
    </div>
  );
}

function DicaDasAlcas({ texto }: { texto: string }) {
  return (
    <p className="mb-4 flex items-start gap-2 rounded-[10px] bg-[#F1ECFF] px-3 py-2.5 text-[12px] font-medium leading-snug text-[#5B21B6]">
      <MousePointer2 size={14} className="mt-[1px] shrink-0" />
      {texto}
    </p>
  );
}

/* As direções prontas do sumiço (de onde aparece para onde some). */
const DIRECOES_DO_SUMICO: Array<{ nome: string; pontos: PontosDoDegrade }> = [
  { nome: "Some embaixo", pontos: { x1: 0.5, y1: 0.45, x2: 0.5, y2: 1 } },
  { nome: "Some em cima", pontos: { x1: 0.5, y1: 0.55, x2: 0.5, y2: 0 } },
  { nome: "Some à direita", pontos: { x1: 0.45, y1: 0.5, x2: 1, y2: 0.5 } },
  { nome: "Some à esquerda", pontos: { x1: 0.55, y1: 0.5, x2: 0, y2: 0.5 } },
  { nome: "Na diagonal", pontos: { x1: 0.3, y1: 0.3, x2: 1, y2: 1 } },
];

/** As direções prontas, o inverter e o quanto aparece em cada ponta (as alças fazem o resto, na peça). */
function SumicoComAlcas({ t, aoMudar }: { t: NonNullable<Peca["transparencia"]>; aoMudar: (t: NonNullable<Peca["transparencia"]>) => void }) {
  return (
    <div className="space-y-3.5">
      <DicaDasAlcas texto="Arraste as alças na peça: a primeira é onde ela aparece, a da seta é onde some. Vale qualquer ângulo; Shift trava de 15 em 15 graus." />
      <div className="grid grid-cols-2 gap-1.5">
        {DIRECOES_DO_SUMICO.map((d) => {
          const ativa = Math.abs(t.x1 - d.pontos.x1) < 0.01 && Math.abs(t.y1 - d.pontos.y1) < 0.01 && Math.abs(t.x2 - d.pontos.x2) < 0.01 && Math.abs(t.y2 - d.pontos.y2) < 0.01;
          return (
            <button
              key={d.nome}
              type="button"
              onClick={() => aoMudar({ ...t, ...d.pontos })}
              className={`flex h-10 items-center gap-2 rounded-[10px] px-2.5 text-[12px] font-medium transition ${ativa ? "bg-[#F1ECFF] text-[#5B21B6] ring-2 ring-[#8B5CF6]" : "bg-[#F6F4FB] text-[#4B4760] hover:bg-[#EEEAF8]"}`}
            >
              <span className="h-5 w-5 shrink-0 rounded-[5px] ring-1 ring-black/10" style={{ background: `linear-gradient(${Math.round((Math.atan2(d.pontos.x2 - d.pontos.x1, -(d.pontos.y2 - d.pontos.y1)) * 180) / Math.PI)}deg, #1F1B2E, rgba(31,27,46,0.06))` }} />
              {d.nome}
            </button>
          );
        })}
        <button type="button" onClick={() => aoMudar({ ...t, x1: t.x2, y1: t.y2, x2: t.x1, y2: t.y1 })} className="flex h-10 items-center gap-2 rounded-[10px] bg-[#F6F4FB] px-2.5 text-[12px] font-medium text-[#4B4760] transition hover:bg-[#EEEAF8]">
          <ArrowLeftRight size={15} className="shrink-0" /> Inverter
        </button>
      </div>
      <Regua rotulo="Aparece no começo" valor={Math.round(t.alfa * 100)} min={0} max={100} aoMudar={(v) => aoMudar({ ...t, alfa: v / 100 })} formato={(v) => `${Math.round(v)}%`} />
      <Regua rotulo="Aparece no fim" valor={Math.round(t.alfa2 * 100)} min={0} max={100} aoMudar={(v) => aoMudar({ ...t, alfa2: v / 100 })} formato={(v) => `${Math.round(v)}%`} />
    </div>
  );
}

/** Transparência da peça: por igual ou em degradê, com as alças no palco (como a do Corel). */
function PainelDaTransparencia({ peca, mudar }: { peca: Peca; mudar: (c: Partial<Peca>) => void }) {
  const t = peca.transparencia;
  return (
    <div>
      <Secao titulo="A peça inteira">
        <Regua rotulo="Aparece" valor={Math.round(peca.opacidade * 100)} min={5} max={100} aoMudar={(v) => mudar({ opacidade: v / 100 })} formato={(v) => `${Math.round(v)}%`} />
      </Secao>
      <Secao titulo="Em degradê" acao={<Chave rotulo="" ligado={Boolean(t)} aoMudar={(v) => mudar({ transparencia: v ? { ...DIRECOES_DO_SUMICO[0].pontos, alfa: 1, alfa2: 0 } : null, ...(peca.tipo === "imagem" ? { desvanecer: null } : {}) } as Partial<Peca>)} />}>
        {t ? (
          <>
            <div className="mb-3">
              <Opcoes
                valor={t.radial ? "radial" : "linear"}
                opcoes={[
                  { valor: "linear", rotulo: "De um lado ao outro" },
                  { valor: "radial", rotulo: "Do centro para fora" },
                ]}
                aoMudar={(v) => mudar({ transparencia: v === "radial" ? { ...t, radial: true, x1: 0.5, y1: 0.5, x2: 1, y2: 0.5 } : { ...t, radial: undefined, ...DIRECOES_DO_SUMICO[0].pontos } })}
              />
            </div>
            {t.radial ? (
              <div className="space-y-3">
                <p className="text-[12px] leading-snug text-[#8A849E]">Arraste as alças na peça: a do começo é o centro, a do fim é a borda do círculo.</p>
                <div className="grid grid-cols-2 gap-2">
                  <Regua rotulo="No centro" valor={Math.round(t.alfa * 100)} min={0} max={100} aoMudar={(v) => mudar({ transparencia: { ...t, alfa: v / 100 } })} formato={(v) => `${Math.round(v)}%`} />
                  <Regua rotulo="Na borda" valor={Math.round(t.alfa2 * 100)} min={0} max={100} aoMudar={(v) => mudar({ transparencia: { ...t, alfa2: v / 100 } })} formato={(v) => `${Math.round(v)}%`} />
                </div>
              </div>
            ) : (
              <SumicoComAlcas t={t} aoMudar={(transparencia) => mudar({ transparencia })} />
            )}
          </>
        ) : (
          <p className="text-[12px] leading-snug text-[#8A849E]">A peça some aos poucos de um ponto a outro: forma, foto, texto ou ícone. Ligue e arraste as alças na própria peça.</p>
        )}
      </Secao>
      <SecaoDeMesclagem peca={peca} mudar={mudar} />
    </div>
  );
}

const SOMBRAS: Array<{ chave: string; nome: string; valor: Sombra }> = [
  { chave: "nenhuma", nome: "Nenhuma", valor: null },
  { chave: "suave", nome: "Suave", valor: sombraSuave() },
  { chave: "marcada", nome: "Marcada", valor: sombraDura() },
  { chave: "longa", nome: "Longa", valor: { cor: "#000000", x: 10, y: 12, desfoque: 0, opacidade: 0.45 } },
  { chave: "brilho", nome: "Brilho", valor: { cor: "#FFFFFF", x: 0, y: 0, desfoque: 30, opacidade: 0.85 } },
];

function CartoesDeSombra({ valor, aoMudar, amostra }: { valor: Sombra; aoMudar: (s: Sombra) => void; amostra: (s: Sombra) => string }) {
  const igual = (a: Sombra, b: Sombra) => (!a && !b) || (a && b && a.x === b.x && a.y === b.y && a.desfoque === b.desfoque && a.cor === b.cor);
  return (
    <div className="grid grid-cols-3 gap-2">
      {SOMBRAS.map((s) => (
        <button key={s.chave} type="button" onClick={() => aoMudar(s.valor)} className="group text-center">
          <span className={`flex h-[58px] items-center justify-center rounded-[10px] bg-[#F3F1F9] transition ${igual(valor, s.valor) ? "ring-2 ring-[#8B5CF6]" : "ring-1 ring-black/5 group-hover:ring-[#C4B5FD]"}`} dangerouslySetInnerHTML={{ __html: amostra(s.valor) }} />
          <span className="mt-1 block text-[11px] font-medium text-[#4B4760]">{s.nome}</span>
        </button>
      ))}
    </div>
  );
}

function amostraDeLetra(s: Sombra) {
  const filtro = s ? `drop-shadow(${s.x / 3}px ${s.y / 3}px ${s.desfoque / 6}px rgba(${s.cor === "#FFFFFF" ? "255,255,255" : s.cor === "#FF2D87" ? "255,45,135" : "0,0,0"},${s.opacidade}))` : "none";
  const cor = s?.cor === "#FFFFFF" || s?.cor === "#FF2D87" ? "#7C3AED" : "#1F1B2E";
  return `<span style="font:900 26px Montserrat,sans-serif;color:${cor};filter:${filtro}">Ag</span>`;
}

function EfeitosDoTexto({ p, mudar }: { p: PecaDeTexto; mudar: (c: Partial<PecaDeTexto>) => void }) {
  const fundo = p.fundo;
  return (
    <div>
      <EstiloDaLetra p={p} mudar={mudar} />
      <Secao titulo="Sombra">
        <CartoesDeSombra valor={p.sombra} aoMudar={(sombra) => mudar({ sombra })} amostra={amostraDeLetra} />
        {p.sombra ? (
          <div className="mt-3">
            <CampoDeSombra valor={p.sombra} aoMudar={(sombra) => mudar({ sombra })} />
          </div>
        ) : null}
      </Secao>
      <Secao titulo="Contorno nas letras">
        <CampoDeBorda valor={p.contorno} aoMudar={(contorno) => mudar({ contorno })} rotulo="Contorno" padrao={Math.max(2, Math.round(p.tamanho * 0.04))} />
      </Secao>
      <Secao titulo="Fundo do texto" acao={<Chave rotulo="" ligado={Boolean(fundo)} aoMudar={(v) => mudar({ fundo: v ? { forma: "pilula", pintura: solida("#FFD400"), borda: null, raio: 16, pontas: 16, profundidade: 0.2 } : null })} />}>
        {fundo ? (
          <div className="space-y-3">
            <GradeDeFormas<FormaDoFundoDoTexto>
              formas={["pilula", "retangulo", "circulo", "explosao", "estrela", "etiqueta", "faixa", "balao", "seta", "rasgado"]}
              valor={fundo.forma}
              aoEscolher={(forma) => mudar({ fundo: { ...fundo, forma, pontas: forma === "estrela" ? 5 : forma === "explosao" ? 16 : fundo.pontas, profundidade: forma === "estrela" ? 0.55 : forma === "explosao" ? 0.2 : fundo.profundidade } })}
            />
            <CampoDePintura valor={fundo.pintura} aoMudar={(pintura) => mudar({ fundo: { ...fundo, pintura } })} rotulo="Cor do fundo" permiteSem />
            {fundo.forma === "retangulo" ? <Regua rotulo="Cantos" valor={fundo.raio} min={0} max={200} aoMudar={(raio) => mudar({ fundo: { ...fundo, raio } })} /> : null}
            {fundo.forma === "explosao" || fundo.forma === "estrela" ? (
              <>
                <Regua rotulo="Pontas" valor={fundo.pontas} min={3} max={40} aoMudar={(pontas) => mudar({ fundo: { ...fundo, pontas } })} />
                <Regua rotulo="Profundidade" valor={Math.round(fundo.profundidade * 100)} min={3} max={80} aoMudar={(v) => mudar({ fundo: { ...fundo, profundidade: v / 100 } })} formato={(v) => `${Math.round(v)}%`} />
              </>
            ) : null}
            {fundo.forma === "rasgado" ? (
              <>
                <Regua rotulo="Desenho do rasgo" valor={fundo.pontas} min={3} max={48} aoMudar={(pontas) => mudar({ fundo: { ...fundo, pontas } })} />
                <Regua rotulo="Tamanho do rasgo" valor={Math.round(fundo.profundidade * 100)} min={3} max={80} aoMudar={(v) => mudar({ fundo: { ...fundo, profundidade: v / 100 } })} formato={(v) => `${Math.round(v)}%`} />
              </>
            ) : null}
            <CampoDeBorda valor={fundo.borda} aoMudar={(borda) => mudar({ fundo: { ...fundo, borda } })} rotulo="Borda do fundo" padrao={4} />
          </div>
        ) : (
          <p className="text-[12px] leading-snug text-[#8A849E]">Ligue para o texto virar botão, selo, etiqueta de preço ou faixa.</p>
        )}
      </Secao>
      <Secao titulo="Marca-texto nas linhas" acao={<Chave rotulo="" ligado={Boolean(p.destaque)} aoMudar={(v) => mudar({ destaque: v ? { pintura: solida("#FFFFFF"), respiro: 0.25, raio: 0 } : null })} />}>
        {p.destaque ? (
          <div className="space-y-3">
            <CampoDePintura valor={p.destaque.pintura} aoMudar={(pintura) => mudar({ destaque: { ...p.destaque!, pintura } })} rotulo="Cor do marca-texto" />
            <Regua rotulo="Folga dos lados" valor={Math.round(p.destaque.respiro * 100)} min={0} max={120} aoMudar={(v) => mudar({ destaque: { ...p.destaque!, respiro: v / 100 } })} formato={(v) => `${(v / 100).toFixed(2)}`} />
            <Regua rotulo="Cantos" valor={p.destaque.raio} min={0} max={Math.round(p.tamanho)} aoMudar={(raio) => mudar({ destaque: { ...p.destaque!, raio } })} />
          </div>
        ) : (
          <p className="text-[12px] leading-snug text-[#8A849E]">Uma faixa de cor atrás de cada linha, que acompanha a quebra do texto.</p>
        )}
      </Secao>
      <Secao
        titulo="Curva"
        acao={
          p.curva ? (
            <button type="button" onClick={() => mudar({ curva: 0 })} className="text-[11.5px] font-semibold text-[#7C3AED] hover:underline">
              Reto
            </button>
          ) : null
        }
      >
        <Regua rotulo="Curvar o texto" valor={p.curva ?? 0} min={-100} max={100} aoMudar={(curva) => mudar({ curva: Math.abs(curva) < 2 ? 0 : curva })} formato={(v) => (Math.round(v) === 100 ? "Círculo" : `${Math.round(v)}`)} />
        <p className="mt-2 text-[11.5px] leading-snug text-[#8A849E]">Para cima arqueia, para baixo sorri. No 100 o texto fecha o círculo (selo).</p>
      </Secao>
      <Secao titulo="Espaçamento">
        <div className="space-y-3">
          <Regua rotulo="Entre letras" valor={p.espaco} min={-0.1} max={0.6} passo={0.01} aoMudar={(espaco) => mudar({ espaco })} formato={(v) => v.toFixed(2)} />
          <Regua rotulo="Entre linhas" valor={p.linha} min={0.7} max={2} passo={0.05} aoMudar={(linha) => mudar({ linha })} formato={(v) => v.toFixed(2)} />
          <Opcoes
            valor={p.vertical}
            opcoes={[
              { valor: "topo", rotulo: "No alto" },
              { valor: "meio", rotulo: "No meio" },
              { valor: "base", rotulo: "Embaixo" },
            ]}
            aoMudar={(vertical) => mudar({ vertical })}
          />
        </div>
      </Secao>
    </div>
  );
}

function EfeitosDaForma({ p, mudar }: { p: PecaDeForma; mudar: (c: Partial<PecaDeForma>) => void }) {
  return (
    <div>
      <Secao titulo="Forma">
        <GradeDeFormas<Forma>
          formas={["retangulo", "circulo", "triangulo", "losango", "estrela", "explosao", "coracao", "balao", "seta", "etiqueta", "faixa", "onda", "hexagono", "pentagono", "octogono", "paralelogramo", "cruz", "raio", "nuvem", "gota", "semicirculo", "anel", "rasgado", "fita"]}
          valor={p.forma}
          aoEscolher={(forma) => mudar({ forma, pontas: forma === "estrela" ? 5 : forma === "explosao" ? 16 : p.pontas, profundidade: forma === "estrela" ? 0.55 : forma === "explosao" ? 0.2 : p.profundidade })}
        />
        <div className="mt-3 space-y-3">
          {p.forma === "retangulo" ? <Regua rotulo="Cantos" valor={p.raio} min={0} max={Math.round(Math.min(p.w, p.h) / 2)} aoMudar={(raio) => mudar({ raio })} /> : null}
          {p.forma === "estrela" || p.forma === "explosao" ? (
            <>
              <Regua rotulo="Pontas" valor={p.pontas} min={3} max={40} aoMudar={(pontas) => mudar({ pontas })} />
              <Regua rotulo="Profundidade" valor={Math.round(p.profundidade * 100)} min={3} max={80} aoMudar={(v) => mudar({ profundidade: v / 100 })} formato={(v) => `${Math.round(v)}%`} />
            </>
          ) : null}
          {p.forma === "rasgado" ? (
            <>
              <Regua rotulo="Desenho do rasgo" valor={p.pontas} min={3} max={48} aoMudar={(pontas) => mudar({ pontas })} />
              <Regua rotulo="Tamanho do rasgo" valor={Math.round(p.profundidade * 100)} min={3} max={80} aoMudar={(v) => mudar({ profundidade: v / 100 })} formato={(v) => `${Math.round(v)}%`} />
            </>
          ) : null}
          {p.forma === "fita" ? <Regua rotulo="Grossura da fita" valor={Math.round(p.profundidade * 100)} min={3} max={80} aoMudar={(v) => mudar({ profundidade: v / 100 })} formato={(v) => `${Math.round(v)}%`} /> : null}
        </div>
      </Secao>
      <Secao titulo="Borda">
        <CampoDeBorda valor={p.borda} aoMudar={(borda) => mudar({ borda })} />
      </Secao>
      <Secao titulo="Sombra">
        <CampoDeSombra valor={p.sombra} aoMudar={(sombra) => mudar({ sombra })} />
      </Secao>
    </div>
  );
}

function EfeitosDaImagem({ p, mudar, aoEnquadrar }: { p: PecaDeImagem; mudar: (c: Partial<PecaDeImagem>) => void; aoEnquadrar?: () => void }) {
  return (
    <div>
      <Secao titulo="Recorte">
        <div className="space-y-3">
          <Opcoes
            valor={p.ajuste}
            opcoes={[
              { valor: "conter", rotulo: "Inteira" },
              { valor: "cobrir", rotulo: "Preencher" },
            ]}
            aoMudar={(ajuste) => mudar({ ajuste })}
          />
          {!p.circulo && !p.mascara ? <Regua rotulo="Cantos" valor={p.raio} min={0} max={Math.round(Math.min(p.w, p.h) / 2)} aoMudar={(raio) => mudar({ raio })} /> : null}
        </div>
      </Secao>
      <MolduraDaFoto p={p} mudar={mudar} GradeDeFormas={GradeDeFormas} aoEnquadrar={aoEnquadrar} />
      <Secao titulo="Moldura">
        <CampoDeBorda valor={p.borda} aoMudar={(borda) => mudar({ borda })} rotulo="Moldura" padrao={Math.max(4, Math.round(Math.min(p.w, p.h) * 0.03))} />
      </Secao>
      <Secao titulo="Contorno de adesivo" acao={<Chave rotulo="" ligado={Boolean(p.adesivo)} aoMudar={(v) => mudar({ adesivo: v ? { cor: "#FFFFFF", largura: Math.max(4, Math.round(Math.min(p.w, p.h) * 0.025)) } : null })} />}>
        {p.adesivo ? (
          <div className="space-y-3">
            <Cor rotulo="Cor do contorno" valor={p.adesivo.cor} aoMudar={(cor) => mudar({ adesivo: { ...p.adesivo!, cor } })} />
            <Regua rotulo="Grossura" valor={p.adesivo.largura} min={1} max={60} aoMudar={(largura) => mudar({ adesivo: { ...p.adesivo!, largura } })} />
          </div>
        ) : (
          <p className="text-[12px] leading-snug text-[#8A849E]">Contorna o recorte (foto sem fundo) como um adesivo de papel.</p>
        )}
      </Secao>
      <Secao titulo="Duas cores (duotone)" acao={<Chave rotulo="" ligado={Boolean(p.duotone)} aoMudar={(v) => mudar({ duotone: v ? DUOTONES[0] : null })} />}>
        {p.duotone ? (
          <div className="space-y-3">
            <div className="grid grid-cols-6 gap-2">
              {DUOTONES.map((d) => (
                <button
                  key={`${d.escuro}${d.claro}`}
                  type="button"
                  title="Usar estas duas cores"
                  onClick={() => mudar({ duotone: d })}
                  className={`h-9 rounded-[10px] ring-1 ring-black/10 transition hover:scale-110 ${p.duotone?.escuro === d.escuro && p.duotone?.claro === d.claro ? "ring-2 ring-[#8B5CF6] ring-offset-2" : ""}`}
                  style={{ background: `linear-gradient(135deg, ${d.escuro} 50%, ${d.claro} 50%)` }}
                />
              ))}
            </div>
            <div className="grid grid-cols-2 gap-2">
              <Cor rotulo="Sombras" valor={p.duotone.escuro} aoMudar={(escuro) => mudar({ duotone: { ...p.duotone!, escuro } })} />
              <Cor rotulo="Luzes" valor={p.duotone.claro} aoMudar={(claro) => mudar({ duotone: { ...p.duotone!, claro } })} />
            </div>
          </div>
        ) : (
          <p className="text-[12px] leading-snug text-[#8A849E]">Pinta a foto com duas cores: o escuro vira uma, o claro vira a outra.</p>
        )}
      </Secao>
      <Secao titulo="Sumir aos poucos" acao={<Chave rotulo="" ligado={Boolean(p.transparencia)} aoMudar={(v) => mudar({ transparencia: v ? { x1: 0.5, y1: 0.45, x2: 0.5, y2: 1, alfa: 1, alfa2: 0 } : null, desvanecer: null })} />}>
        {p.transparencia ? (
          <SumicoComAlcas t={p.transparencia} aoMudar={(transparencia) => mudar({ transparencia })} />
        ) : (
          <p className="text-[12px] leading-snug text-[#8A849E]">A foto se dissolve no fundo. Ligue e arraste as alças na própria foto, em qualquer direção.</p>
        )}
      </Secao>
      <Secao titulo="Sombra">
        <CampoDeSombra valor={p.sombra} aoMudar={(sombra) => mudar({ sombra })} />
      </Secao>
    </div>
  );
}

const DUOTONES: Array<{ escuro: string; claro: string }> = [
  { escuro: "#3B0A2A", claro: "#FF9FCB" },
  { escuro: "#14213D", claro: "#7FDBFF" },
  { escuro: "#1E1B4B", claro: "#C4B5FD" },
  { escuro: "#052E16", claro: "#A3E635" },
  { escuro: "#431407", claro: "#FDBA74" },
  { escuro: "#111111", claro: "#F5F5F5" },
];

function EfeitosDoIcone({ p, mudar }: { p: PecaDeIcone; mudar: (c: Partial<PecaDeIcone>) => void }) {
  return (
    <div>
      {/* Logo de rede (desenho oficial, já preenchido) e laço de presente não têm traço para engrossar. */}
      {p.desenho || !ICONES[p.icone]?.cheio ? (
        <Secao titulo="Traço">
          <div className="space-y-3">
            <Regua rotulo="Grossura" valor={p.espessura} min={0.5} max={4} passo={0.1} aoMudar={(espessura) => mudar({ espessura })} formato={(v) => v.toFixed(1)} />
            <Chave rotulo="Preencher por dentro" ligado={p.preenchido} aoMudar={(preenchido) => mudar({ preenchido })} />
          </div>
        </Secao>
      ) : null}
      <Secao titulo="Sombra">
        <CampoDeSombra valor={p.sombra} aoMudar={(sombra) => mudar({ sombra })} />
      </Secao>
    </div>
  );
}

const ESTILOS: Array<{ valor: EstiloDeTextura; nome: string }> = [
  { valor: "raios", nome: "Raios" },
  { valor: "pontos", nome: "Bolinhas" },
  { valor: "listras", nome: "Listras" },
  { valor: "xadrez", nome: "Xadrez" },
  { valor: "grade", nome: "Quadriculado" },
  { valor: "ondas", nome: "Ondas" },
  { valor: "grao", nome: "Grão" },
  { valor: "meio-tom", nome: "Retícula" },
  { valor: "topografia", nome: "Curvas de nível" },
];
/* As que se mexem no vídeo (particulas.ts). */
const PARTICULAS: Array<{ valor: EstiloDeTextura; nome: string }> = [
  { valor: "confete", nome: "Confete" },
  { valor: "neve", nome: "Neve" },
  { valor: "brilhos", nome: "Brilhos" },
  { valor: "bolhas", nome: "Bolhas" },
  { valor: "coracoes", nome: "Corações" },
  { valor: "faiscas", nome: "Faíscas" },
  { valor: "fogos", nome: "Fogos" },
];

function AjustesDaTextura({ p, mudar }: { p: PecaDeTextura; mudar: (c: Partial<PecaDeTextura>) => void }) {
  const particula = PARTICULAS.some((e) => e.valor === p.estilo);
  return (
    <div>
      <Secao titulo="Partículas (se mexem no vídeo)">
        <div className="grid grid-cols-3 gap-2" data-particulas>
          {PARTICULAS.map((e) => {
            const amostra: PecaDeTextura = { ...p, id: `amostra-${e.valor}`, x: 0, y: 0, w: 96, h: 56, rot: 0, opacidade: 1, estilo: e.valor, tamanho: 5, quantidade: 26, cor: "#FFFFFF", centroX: 50, centroY: 90 };
            return (
              <button key={e.valor} type="button" onClick={() => mudar({ estilo: e.valor, quantidade: particula ? p.quantidade : 60, tamanho: particula ? p.tamanho : 0, opacidade: particula ? p.opacidade : 1, centroX: e.valor === "faiscas" ? 50 : p.centroX, centroY: e.valor === "faiscas" ? 80 : p.centroY })} className="group text-center" data-particula={e.valor}>
                <span className={`relative block h-[56px] overflow-hidden rounded-[10px] bg-[linear-gradient(135deg,#1E1B4B,#4C1D95)] transition ${p.estilo === e.valor ? "ring-2 ring-[#8B5CF6] ring-offset-1" : "ring-1 ring-black/5"}`}>
                  <span className="absolute inset-0" dangerouslySetInnerHTML={{ __html: conteudoDaPeca(amostra) }} />
                </span>
                <span className="mt-1 block text-[11px] font-medium text-[#4B4760]">{e.nome}</span>
              </button>
            );
          })}
        </div>
      </Secao>
      <Secao titulo="Desenho">
        <div className="grid grid-cols-3 gap-2">
          {ESTILOS.map((e) => {
            const amostra: PecaDeTextura = { ...p, id: `amostra-${e.valor}`, x: 0, y: 0, w: 96, h: 56, rot: 0, opacidade: 1, estilo: e.valor, tamanho: 12, cor: "#FFFFFF" };
            return (
              <button key={e.valor} type="button" onClick={() => mudar({ estilo: e.valor })} className="group text-center">
                <span className={`relative block h-[56px] overflow-hidden rounded-[10px] bg-[linear-gradient(135deg,#7C3AED,#4C1D95)] transition ${p.estilo === e.valor ? "ring-2 ring-[#8B5CF6] ring-offset-1" : "ring-1 ring-black/5"}`}>
                  <span className="absolute inset-0 opacity-50" dangerouslySetInnerHTML={{ __html: conteudoDaPeca(amostra) }} />
                </span>
                <span className="mt-1 block text-[11px] font-medium text-[#4B4760]">{e.nome}</span>
              </button>
            );
          })}
        </div>
      </Secao>
      <Secao titulo="Ajustes">
        <div className="space-y-3.5">
          <Regua rotulo="Força" valor={Math.round(p.opacidade * 100)} min={2} max={100} aoMudar={(v) => mudar({ opacidade: v / 100 })} formato={(v) => `${Math.round(v)}%`} />
          {particula ? (
            <>
              <Regua rotulo="Quantas" valor={p.quantidade} min={4} max={300} aoMudar={(quantidade) => mudar({ quantidade })} />
              <Regua rotulo="Velocidade" valor={Math.round((p.velocidade ?? 1) * 100)} min={20} max={300} aoMudar={(v) => mudar({ velocidade: v / 100 })} formato={(v) => `${(v / 100).toFixed(1).replace(".", ",")}×`} />
              {p.estilo === "faiscas" ? <Regua rotulo="De onde saem (altura)" valor={p.centroY} min={0} max={100} aoMudar={(centroY) => mudar({ centroY })} formato={(v) => `${Math.round(v)}%`} /> : null}
            </>
          ) : null}
          {p.estilo === "raios" || p.estilo === "topografia" ? (
            <>
              <Regua rotulo={p.estilo === "raios" ? "Quantidade de raios" : "Quantidade de curvas"} valor={p.quantidade} min={4} max={p.estilo === "raios" ? 60 : 40} aoMudar={(quantidade) => mudar({ quantidade })} />
              <Regua rotulo="Centro na largura" valor={p.centroX} min={-20} max={120} aoMudar={(centroX) => mudar({ centroX })} formato={(v) => `${Math.round(v)}%`} />
              <Regua rotulo="Centro na altura" valor={p.centroY} min={-20} max={120} aoMudar={(centroY) => mudar({ centroY })} formato={(v) => `${Math.round(v)}%`} />
            </>
          ) : null}
          {p.estilo !== "raios" && p.estilo !== "grao" ? <Regua rotulo="Tamanho do desenho" valor={p.tamanho || Math.round(Math.min(p.w, p.h) * 0.06)} min={6} max={240} aoMudar={(tamanho) => mudar({ tamanho })} /> : null}
          {p.estilo !== "grao" && !particula ? <Regua rotulo="Giro do desenho" valor={p.giro} min={0} max={360} aoMudar={(giro) => mudar({ giro })} formato={(v) => `${Math.round(v)}°`} /> : null}
        </div>
      </Secao>
    </div>
  );
}

function PainelDePosicao({ peca, medida, mudar, aoOrdem, aoDistorcer, distorcendo }: { peca: Peca; medida: Medida; mudar: (c: Partial<Peca>) => void; aoOrdem: (para: "frente" | "tras" | "subir" | "descer") => void; aoDistorcer?: () => void; distorcendo?: boolean }) {
  const botao = "flex h-10 items-center justify-center gap-1.5 rounded-[10px] bg-[#F3F1F9] text-[12px] font-medium text-[#4B4760] transition hover:bg-[#EAE5F8] hover:text-[#1F1B2E]";
  return (
    <div>
      <SecaoDeDistorcer peca={peca} mudar={mudar} aoDistorcer={aoDistorcer} distorcendo={distorcendo} />
      <Secao titulo="Camada">
        <div className="grid grid-cols-2 gap-2">
          <button type="button" className={botao} onClick={() => aoOrdem("frente")}>
            <ArrowUpToLine size={15} /> Para a frente
          </button>
          <button type="button" className={botao} onClick={() => aoOrdem("tras")}>
            <ArrowDownToLine size={15} /> Para trás
          </button>
          <button type="button" className={botao} onClick={() => aoOrdem("subir")}>
            <ChevronUp size={15} /> Subir uma
          </button>
          <button type="button" className={botao} onClick={() => aoOrdem("descer")}>
            <ChevronDown size={15} /> Descer uma
          </button>
        </div>
      </Secao>
      <Secao titulo="Alinhar no banner">
        <div className="grid grid-cols-3 gap-2">
          <button type="button" className={botao} onClick={() => mudar({ x: 0 })}>
            <AlignStartVertical size={15} /> Esquerda
          </button>
          <button type="button" className={botao} onClick={() => mudar({ x: (medida.largura - peca.w) / 2 })}>
            <AlignHorizontalJustifyCenter size={15} /> Centro
          </button>
          <button type="button" className={botao} onClick={() => mudar({ x: medida.largura - peca.w })}>
            <AlignEndVertical size={15} /> Direita
          </button>
          <button type="button" className={botao} onClick={() => mudar({ y: 0 })}>
            <AlignVerticalJustifyStart size={15} /> Topo
          </button>
          <button type="button" className={botao} onClick={() => mudar({ y: (medida.altura - peca.h) / 2 })}>
            <AlignVerticalJustifyCenter size={15} /> Meio
          </button>
          <button type="button" className={botao} onClick={() => mudar({ y: medida.altura - peca.h })}>
            <AlignVerticalJustifyEnd size={15} /> Base
          </button>
        </div>
        <button type="button" className={`${botao} mt-2 w-full`} onClick={() => mudar({ x: 0, y: 0, w: medida.largura, h: medida.altura, rot: 0 })}>
          Ocupar o banner inteiro
        </button>
      </Secao>
      <Secao titulo="Tamanho e posição">
        <div className="grid grid-cols-2 gap-2">
          <Numero rotulo="Largura" valor={peca.w} aoMudar={(w) => mudar({ w: Math.max(4, w) })} />
          <Numero rotulo="Altura" valor={peca.h} aoMudar={(h) => mudar({ h: Math.max(4, h) })} />
          <Numero rotulo="X" valor={peca.x} aoMudar={(x) => mudar({ x })} negativo />
          <Numero rotulo="Y" valor={peca.y} aoMudar={(y) => mudar({ y })} negativo />
        </div>
        <div className="mt-3 flex items-end gap-2">
          <div className="min-w-0 flex-1">
            <Regua rotulo="Giro" valor={peca.rot} min={-180} max={180} aoMudar={(rot) => mudar({ rot })} formato={(v) => `${Math.round(v)}°`} />
          </div>
          <button type="button" className={`${botao} w-10`} title="Sem giro" onClick={() => mudar({ rot: 0 })}>
            <RotateCcw size={15} />
          </button>
        </div>
        <div className="mt-3 grid grid-cols-2 gap-2">
          <button type="button" className={`${botao} ${peca.espelharX ? "!bg-[#7C3AED] !text-white" : ""}`} onClick={() => mudar({ espelharX: !peca.espelharX })}>
            <FlipHorizontal2 size={15} /> Espelhar
          </button>
          <button type="button" className={`${botao} ${peca.espelharY ? "!bg-[#7C3AED] !text-white" : ""}`} onClick={() => mudar({ espelharY: !peca.espelharY })}>
            <FlipVertical2 size={15} /> Inverter
          </button>
        </div>
      </Secao>
    </div>
  );
}

/** O ajuste que a barra flutuante abriu, para a peça escolhida. */
export function PainelDoAjuste({
  ajuste,
  peca,
  medida,
  mudar,
  aoOrdem,
  aoEnquadrar,
  aoDistorcer,
  distorcendo,
  corDoVetor = null,
  aoCorDoVetor,
  foto = null,
}: {
  ajuste: AjusteDaPeca;
  peca: Peca;
  /* Elemento em SVG: qual cor original está sendo trocada. */
  corDoVetor?: string | null;
  aoCorDoVetor?: (original: string) => void;
  medida: Medida;
  mudar: (campos: Partial<Peca>) => void;
  aoOrdem: (para: "frente" | "tras" | "subir" | "descer") => void;
  /* Foto: entra no enquadre arrastando na arte. */
  aoEnquadrar?: () => void;
  /* Distorcer pelos 4 cantos na arte (liga e desliga as alças dos cantos). */
  aoDistorcer?: () => void;
  distorcendo?: boolean;
  /* Foto: o tratamento de agora (o que está sendo mexido vence o guardado), a foto de antes e quem aplica (o editor). */
  foto?: {
    tratamento: TratamentoDaFoto | null;
    origem: string;
    aoTratar: (campos: { trocas?: TrocaDeCor[]; meioTom?: MeioTom | null }) => void;
    aplicando: boolean;
    corInicial: string | null;
    dpi: number | null;
  } | null;
}) {
  const comTrecho = useTrechoEscolhido();
  if (peca.tipo === "imagem" && foto && ajuste === "cores-da-foto") return <PainelDasCoresDaFoto key={foto.corInicial ?? ""} origem={foto.origem} tratamento={foto.tratamento} aoTratar={foto.aoTratar} aplicando={foto.aplicando} corInicial={foto.corInicial} />;
  if (peca.tipo === "imagem" && foto && ajuste === "reticula") return <PainelDaReticula foto={peca} tratamento={foto.tratamento} aoTratar={foto.aoTratar} aplicando={foto.aplicando} dpi={foto.dpi} />;
  if (ajuste === "transparencia") return <PainelDaTransparencia peca={peca} mudar={mudar} />;
  if (ajuste === "vetor" && peca.tipo === "imagem" && peca.vetor) return <CoresDoVetor p={peca} mudar={mudar as (c: Partial<PecaDeImagem>) => void} escolhida={corDoVetor ?? peca.vetor.cores[0]} aoEscolher={aoCorDoVetor} />;
  if (ajuste === "camada") return <EfeitosDaCamada peca={peca} mudar={mudar} />;
  if (ajuste !== "posicao") {
    if (peca.tipo === "traco") {
      const m = mudar as (c: Partial<PecaDeTraco>) => void;
      /* Desenho fechado: a cor é a de dentro (o contorno fica nos ajustes dele). */
      if (ajuste === "cor" && peca.fechado) return <PainelDeCor valor={peca.preenchimento} aoMudar={(preenchimento) => m({ preenchimento })} rotulo="Cor de dentro" permiteSem comAlcas />;
      if (ajuste === "cor") return <PainelDeCor valor={solida(peca.cor)} aoMudar={(pintura) => m({ cor: pintura.cor })} rotulo="Cor da linha" soCor />;
      return <EfeitosDoTraco p={peca} mudar={m} />;
    }
    if (peca.tipo === "qr") return <PainelDoQr p={peca} mudar={mudar as (c: Partial<PecaDeQr>) => void} />;
    if (peca.tipo === "tabela") return <PainelDaTabela p={peca} mudar={mudar as (c: Partial<PecaDeTabela>) => void} />;
    if (peca.tipo === "grafico") return <PainelDoGrafico p={peca} mudar={mudar as (c: Partial<PecaDeGrafico>) => void} />;
  }
  if (peca.tipo === "icone" && ajuste !== "posicao") {
    const m = mudar as (c: Partial<PecaDeIcone>) => void;
    if (ajuste === "cor") return <PainelDeCor valor={peca.pintura} aoMudar={(pintura) => m({ pintura })} rotulo="Cor do ícone" comAlcas />;
    return <EfeitosDoIcone p={peca} mudar={m} />;
  }
  if (ajuste === "posicao") return <PainelDePosicao peca={peca} medida={medida} mudar={mudar} aoOrdem={aoOrdem} aoDistorcer={aoDistorcer} distorcendo={distorcendo} />;
  if (peca.tipo === "texto") {
    const m = mudar as (c: Partial<PecaDeTexto>) => void;
    if (ajuste === "fonte") return <PainelDaFonte p={peca} mudar={m} />;
    if (ajuste === "glifos") return <PainelDeGlifos p={peca} mudar={m} />;
    if (ajuste === "cor") {
      return (
        <PainelDeCor
          valor={peca.pintura}
          aoMudar={(pintura) => {
            /* Com um trecho escolhido, a cor (sólida) vai só para ele. */
            if (!(comTrecho && pintura.tipo === "solida" && formatarTrecho("cor", pintura.cor))) m({ pintura });
          }}
          rotulo={comTrecho ? "Cor do trecho" : "Cor da letra"}
          permiteSem={!comTrecho}
          soCor={comTrecho}
          comAlcas={!comTrecho}
        />
      );
    }
    if (ajuste === "cor-do-fundo" && peca.fundo) return <PainelDeCor valor={peca.fundo.pintura} aoMudar={(pintura) => m({ fundo: { ...peca.fundo!, pintura } })} rotulo="Fundo do texto" permiteSem />;
    return <EfeitosDoTexto p={peca} mudar={m} />;
  }
  if (peca.tipo === "forma") {
    const m = mudar as (c: Partial<PecaDeForma>) => void;
    if (ajuste === "cor") return <PainelDeCor valor={peca.pintura} aoMudar={(pintura) => m({ pintura })} rotulo={peca.nome === "Fundo" ? "Cor do fundo" : "Preenchimento"} permiteSem comAlcas />;
    return <EfeitosDaForma p={peca} mudar={m} />;
  }
  if (peca.tipo === "textura") {
    const m = mudar as (c: Partial<PecaDeTextura>) => void;
    if (ajuste === "cor") return <PainelDeCor valor={solida(peca.cor)} aoMudar={(p) => m({ cor: p.cor })} rotulo="Cor da textura" soCor />;
    return <AjustesDaTextura p={peca} mudar={m} />;
  }
  if (peca.tipo !== "imagem") return null;
  const m = mudar as (c: Partial<PecaDeImagem>) => void;
  if (ajuste === "ajustes") return <AjustesCompletos p={peca} mudar={m} />;
  return <EfeitosDaImagem p={peca} mudar={m} aoEnquadrar={aoEnquadrar} />;
}

export const TITULOS_DOS_AJUSTES: Record<AjusteDaPeca, string> = {
  fonte: "Fonte",
  glifos: "Glifos",
  vetor: "Cores do elemento",
  cor: "Cor",
  "cor-do-fundo": "Cor do fundo do texto",
  efeitos: "Efeitos",
  posicao: "Posição",
  ajustes: "Ajustes da foto",
  forma: "Forma",
  textura: "Textura",
  transparencia: "Transparência e mesclagem",
  camada: "Efeitos da camada",
  "cores-da-foto": "Trocar cores",
  reticula: "Retícula (halftone)",
};
