"use client";

import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { ChevronLeft, ChevronRight, Clapperboard, Coffee, Copy, ThumbsDown, ThumbsUp, UserCheck, UserPlus, Heart, Link2, Loader2, MoreHorizontal, Pencil, Plus, Printer, Ruler, Search, Send, Sparkles, Store, Trash2, Undo2, Users, X } from "lucide-react";
import { getSupabaseClient } from "@/lib/supabase";
import { getCompanyContext, resolveCompanyPublicProfileId } from "@/lib/company-context";
import { Aviso } from "@/components/ui/aviso";
import CaixaDeConfirmacao, { type Confirmacao } from "@/components/ui/caixa-de-confirmacao";
import { CampoDecimal } from "@/components/ui/campo-decimal";
import MarcaDoDesign from "@/components/design/marca-do-design";
import { GaleriaIndisponivel, abrirArte, apagarArte, criarArte, duplicarArte, listarArtesDaGaleria, salvarArte, type ArteNaGaleria, type ResumoDaArte, type VinculoComOrcamento } from "@/lib/banner-editor/artes";
import { situacaoDasProvas, type ResumoDaProva } from "@/lib/banner-editor/provas";
import { CONTAGEM_VAZIA, chaveDaCasa, contagemDo, favoritar, meuEstado, resumoDoAutor, seguir, votar, type Contagem, type ResumoDoAutor } from "@/lib/banner-editor/comunidade-social";
import { CORES_NOMEADAS, OCASIOES, ocasioesDoTexto, textoDoModelo } from "@/lib/banner-editor/etiquetas";
import { TIPOS_DE_MODELO, enderecoDaMiniatura, fotosDasPaginas, miniaturaDoModelo, pedidoNeutro, pedirMiniatura, type FotoPronta, type TipoDeModelo } from "@/lib/banner-editor/miniaturas";
import { VideoQueToca } from "./video-que-toca";
import { NOMES_DOS_TIPOS, contarUso, documentoDoModelo, enviarParaComunidade, enviosDaEmpresa, modelosAprovados, retirarEnvio, tipoDaArte, type ModeloDaComunidade } from "@/lib/banner-editor/comunidade";
import { CHAVES_DAS_FONTES_DE_BASE, ICONES, garantirFontes, type DocumentoDoBanner } from "@/lib/banner-editor/documento";
import { FORMATOS_DA_ARTE, LIMITE_DO_LADO, MINIMO_DO_LADO, formatoDaChave, formatoDoTamanho, medidaDeImpressao, pxDe, type FormatoDaArte, type GrupoDoFormato } from "@/lib/banner-editor/formatos";
import { MODELOS_DO_EDITOR, RAMOS_DOS_MODELOS, type ModeloDoEditor, type RamoDoModelo } from "@/lib/banner-editor/modelos";
import { EditorDeBanner } from "@/components/pagina-inicial/editor-de-banner/editor-de-banner";
import { Miniatura } from "@/components/pagina-inicial/editor-de-banner/paineis";

/*
  KtaloguDesign: a tela inicial do editor de artes, no jeito do Canva. De
  cima para baixo:
   - "O que vamos criar hoje?", a busca (tamanhos, modelos e designs) e os
     atalhos: "Criar um design" e os grupos de tamanho abrem a janela de
     tamanhos (como o "Criar um design" do Canva: grupos à esquerda, cada
     tamanho num cartão desenhado; clicar abre o editor em branco);
   - "Continue criando": os designs da pessoa, os mais recentes primeiro;
   - "Explorar categorias de modelos": um cartão por tipo de modelo (post,
     carrossel, story, banner da loja, caneca) com a cara de um modelo de
     verdade; embaixo, SÓ os modelos do tipo escolhido, no tamanho dele, com
     a cor, o logo e a foto de um produto da loja. Cada modelo é de um uso
     (modelos.ts): nada de modelo de post encaixado numa caneca. Junto, os
     modelos da COMUNIDADE (artes que outros assinantes publicaram e o dono
     da plataforma aprovou no Owner), com o nome e o avatar de quem fez;
   - clicar num modelo abre a prévia (como a do Canva): quem fez, o tipo, a
     categoria e o tamanho; "Usar este modelo" abre a cópia no editor;
   - o menu de cada design tem "Publicar na comunidade" (vai para análise).
*/

type Aberta = { largura: number; altura: number; formato: string | null; arte: ResumoDaArte | null; modelo: string | null };
type GrupoDaJanela = GrupoDoFormato | "populares" | "personalizado";

/* Os grupos de tamanho (as bolinhas do topo e a lista da janela "Criar um design"). */
const GRUPOS_DA_JANELA: Array<{ chave: GrupoDaJanela; nome: string; cor: string; Icone: typeof Store }> = [
  { chave: "populares", nome: "Populares", cor: "#7C3AED", Icone: Sparkles },
  { chave: "redes", nome: "Redes sociais", cor: "#EC4899", Icone: Heart },
  { chave: "loja", nome: "Loja", cor: "#F97316", Icone: Store },
  { chave: "anuncios", nome: "Anúncios e vídeo", cor: "#0EA5E9", Icone: Clapperboard },
  { chave: "impressao", nome: "Impressão", cor: "#10B981", Icone: Printer },
  { chave: "personalizado", nome: "Tamanho personalizado", cor: "#1F2937", Icone: Ruler },
];
const POPULARES = ["post-retrato", "story", "post", "banner-loja", "caneca", "capa-facebook", "youtube", "a4"];

/* A prévia de um modelo: da casa (montado com a cara da loja) ou da comunidade. */
type Previa = { tipo: TipoDeModelo; casa?: { modelo: ModeloDoEditor; doc: DocumentoDoBanner }; comunidade?: ModeloDaComunidade };
type Publicacao = { arte: ResumoDaArte; nome: string; descricao: string; ramo: RamoDoModelo | null; aceite: boolean; enviando: boolean; erro: string };

/* A aprovação do cliente (a prova mais nova do design), no cartão da galeria. */
const SITUACAO_DA_APROVACAO: Record<ResumoDaProva["situacao"], { rotulo: string; classe: string }> = {
  aguardando: { rotulo: "Esperando o cliente", classe: "bg-[#F1EFF6] text-[#4B4760]" },
  aprovada: { rotulo: "Aprovada pelo cliente", classe: "bg-[#DCFCE7] text-[#166534]" },
  alteracao: { rotulo: "Cliente pediu alteração", classe: "bg-[#FFF7E6] text-[#8A5300]" },
};

const SITUACAO_DO_ENVIO: Record<ModeloDaComunidade["status"], { rotulo: string; classe: string }> = {
  pendente: { rotulo: "Em análise", classe: "bg-[#FEF3C7] text-[#92400E]" },
  aprovado: { rotulo: "Na comunidade", classe: "bg-[#D1FAE5] text-[#065F46]" },
  recusado: { rotulo: "Recusado", classe: "bg-[#FFE4E6] text-[#9F1239]" },
};

/* O avatar de quem fez o modelo (a foto ou a inicial); o da casa é a marca da Ktalogu. */
function AvatarDoAutor({ nome, src, tamanho = 32, casa = false }: { nome: string; src?: string | null; tamanho?: number; casa?: boolean }) {
  if (casa) {
    return (
      <span className="flex shrink-0 items-center justify-center rounded-full bg-[#7C3AED] text-white ring-2 ring-white" style={{ width: tamanho, height: tamanho }}>
        <MarcaDoDesign size={Math.round(tamanho * 0.55)} />
      </span>
    );
  }
  if (src) {
    /* eslint-disable-next-line @next/next/no-img-element */
    return <img src={src} alt="" className="shrink-0 rounded-full object-cover ring-2 ring-white" style={{ width: tamanho, height: tamanho }} />;
  }
  return (
    <span className="flex shrink-0 items-center justify-center rounded-full bg-[linear-gradient(135deg,#EC4899,#7C3AED)] font-bold text-white ring-2 ring-white" style={{ width: tamanho, height: tamanho, fontSize: tamanho * 0.42 }}>
      {nome.trim().slice(0, 1).toUpperCase() || "?"}
    </span>
  );
}

/* A marca da rede no cartão do tamanho (os logos oficiais do editor). */
const REDE_DO_FORMATO: Record<string, string> = { post: "instagram-cor", "post-retrato": "instagram-cor", story: "instagram-cor", destaque: "instagram-cor", "capa-facebook": "facebook-cor", youtube: "youtube-cor" };

const quando = new Intl.DateTimeFormat("pt-BR", { day: "2-digit", month: "short" });
const semAcento = (t: string) => t.normalize("NFD").replace(/[̀-ͯ]/g, "").toLowerCase();

/** A miniatura que cabe na caixa (mede a caixa e encolhe a arte até caber, centrada). */
/* O selo no canto do cartão do modelo: carrossel (com as páginas) ou reels. */
function SeloDoModelo({ modelo }: { modelo: ModeloDoEditor }) {
  if (modelo.feitoPara === "caneca") {
    return (
      <span data-selo-do-modelo className="pointer-events-none absolute right-2 top-2 flex items-center gap-1 rounded-full bg-[#140B2B]/80 px-2 py-[3px] text-[10.5px] font-bold text-white backdrop-blur-sm">
        <Coffee size={11} strokeWidth={2.6} />
        Caneca
      </span>
    );
  }
  if (modelo.feitoPara !== "carrossel" && modelo.feitoPara !== "story") return null;
  return (
    <span data-selo-do-modelo className="pointer-events-none absolute right-2 top-2 flex items-center gap-1 rounded-full bg-[#140B2B]/80 px-2 py-[3px] text-[10.5px] font-bold text-white backdrop-blur-sm">
      {modelo.feitoPara === "carrossel" ? <Copy size={11} strokeWidth={2.6} /> : <Clapperboard size={11} strokeWidth={2.6} />}
      {modelo.feitoPara === "carrossel" ? `Carrossel · ${modelo.paginas ?? 2} páginas` : "Reels"}
    </span>
  );
}

function ArteQueCabe({ doc, folga = 0 }: { doc: DocumentoDoBanner; folga?: number }) {
  const caixa = useRef<HTMLDivElement | null>(null);
  const [medida, setMedida] = useState<{ w: number; h: number } | null>(null);
  useEffect(() => {
    const el = caixa.current;
    if (!el) return;
    const medir = () => setMedida({ w: el.clientWidth, h: el.clientHeight });
    medir();
    const observador = new ResizeObserver(medir);
    observador.observe(el);
    return () => observador.disconnect();
  }, []);
  const largura = medida ? Math.max(10, Math.min(medida.w - folga * 2, ((medida.h - folga * 2) * doc.largura) / doc.altura)) : 0;
  return (
    <div ref={caixa} className="flex h-full w-full items-center justify-center">
      {largura ? (
        <div className="overflow-hidden rounded-[6px] shadow-[0_1px_2px_rgba(16,12,32,0.08),0_8px_24px_-10px_rgba(16,12,32,0.28)]">
          <Miniatura doc={doc} largura={largura} />
        </div>
      ) : null}
    </div>
  );
}

/* A arte só é montada quando chega perto da tela (o que está longe fica de fora). */
function QuandoAparecer({ children }: { children: React.ReactNode }) {
  const caixa = useRef<HTMLDivElement | null>(null);
  const [visto, setVisto] = useState(false);
  useEffect(() => {
    const el = caixa.current;
    if (!el || visto) return;
    const observador = new IntersectionObserver((itens) => itens.some((i) => i.isIntersecting) && setVisto(true), { rootMargin: "600px" });
    observador.observe(el);
    return () => observador.disconnect();
  }, [visto]);
  return (
    <div ref={caixa} className="h-full w-full">
      {visto ? children : null}
    </div>
  );
}

/*
 * A foto pronta do modelo da casa; se o modelo mudou depois da foto, ele é
 * montado quando aparece. Com `folheia` (o cartão da galeria) e mais de uma
 * página, o mouse em cima vai passando as páginas, como no Canva; as outras
 * fotos só são pedidas na primeira vez que o mouse passa.
 */
function FotoDoModelo({ tipo, modelo, doc, className = "", folheia = false }: { tipo: TipoDeModelo; modelo: ModeloDoEditor; doc: DocumentoDoBanner; className?: string; folheia?: boolean }) {
  const foto = miniaturaDoModelo(tipo.chave, modelo.chave, doc);
  const fotos = foto ? fotosDasPaginas(foto) : [];
  const [passando, setPassando] = useState(false);
  const [atual, setAtual] = useState(0);
  /* Modelo animado: o vídeo toca sozinho (as páginas já passam nele). */
  const video = foto?.video && foto.video.marca === foto.marca ? foto.video : null;
  const varias = folheia && fotos.length > 1 && !video;
  useEffect(() => {
    if (!passando || !varias) return;
    const relogio = setInterval(() => setAtual((i) => (i + 1) % fotos.length), 900);
    return () => clearInterval(relogio);
  }, [passando, varias, fotos.length]);
  if (foto && video) return <VideoQueToca src={video.src} capa={fotos[0].src} largura={fotos[0].largura} altura={fotos[0].altura} nome={modelo.nome} duracao={video.duracao} className={className} />;
  if (foto) {
    return (
      <div
        className="relative"
        onMouseEnter={() => varias && setPassando(true)}
        onMouseLeave={() => {
          setPassando(false);
          setAtual(0);
        }}
        data-paginas-do-modelo={fotos.length}
      >
        {/* eslint-disable-next-line @next/next/no-img-element */}
        <img src={fotos[0].src} alt={modelo.nome} width={fotos[0].largura} height={fotos[0].altura} loading="lazy" decoding="async" className={`block h-auto w-full ${className}`} data-foto-pronta />
        {passando
          ? fotos.slice(1).map((q, i) => (
              /* eslint-disable-next-line @next/next/no-img-element */
              <img key={q.src} src={q.src} alt="" decoding="async" className={`absolute inset-0 h-full w-full object-cover transition-opacity duration-300 ${atual === i + 1 ? "opacity-100" : "opacity-0"}`} data-pagina-folheada={i + 2} />
            ))
          : null}
        {varias ? (
          <span className={`pointer-events-none absolute left-1/2 top-2.5 flex -translate-x-1/2 gap-1 rounded-full bg-[#140B2B]/55 px-2 py-1 backdrop-blur-sm transition-opacity ${passando ? "opacity-100" : "opacity-0"}`}>
            {fotos.map((q, i) => (
              <span key={q.src} className={`h-1.5 rounded-full transition-all ${i === atual ? "w-4 bg-white" : "w-1.5 bg-white/50"}`} />
            ))}
          </span>
        ) : null}
      </div>
    );
  }
  return (
    <div className={`w-full ${className}`} style={{ aspectRatio: `${doc.largura} / ${doc.altura}` }} data-montado-na-hora>
      <QuandoAparecer>
        <ArteQueCabe doc={doc} />
      </QuandoAparecer>
    </div>
  );
}

/*
 * A prévia de um modelo com várias páginas, como a do Canva: passa sozinha a
 * cada 3 s (para quando a pessoa mexe), com as setas, as bolinhas, o "2 / 5"
 * e as miniaturas das páginas embaixo. Setas do teclado também passam.
 */
function GaleriaDaPrevia({ fotos, nome }: { fotos: FotoPronta[]; nome: string }) {
  const [atual, setAtual] = useState(0);
  const [parado, setParado] = useState(false);
  const total = fotos.length;
  useEffect(() => {
    if (parado || total < 2) return;
    const relogio = setInterval(() => setAtual((i) => (i + 1) % total), 3000);
    return () => clearInterval(relogio);
  }, [parado, total]);
  useEffect(() => {
    if (total < 2) return;
    const tecla = (e: KeyboardEvent) => {
      if (e.key !== "ArrowRight" && e.key !== "ArrowLeft") return;
      setParado(true);
      setAtual((i) => (i + (e.key === "ArrowRight" ? 1 : total - 1)) % total);
    };
    window.addEventListener("keydown", tecla);
    return () => window.removeEventListener("keydown", tecla);
  }, [total]);
  const ir = (i: number) => {
    setParado(true);
    setAtual(((i % total) + total) % total);
  };
  const deitada = fotos[0].largura / fotos[0].altura > 1.6;
  return (
    <div className="flex w-full flex-col items-center gap-4" data-galeria-da-previa={total}>
      <div className="group relative grid w-full place-items-center" onMouseEnter={() => setParado(true)}>
        {fotos.map((q, i) => (
          /* eslint-disable-next-line @next/next/no-img-element */
          <img
            key={q.src}
            src={q.src}
            alt={i === 0 ? nome : `${nome}, página ${i + 1}`}
            draggable={false}
            decoding="async"
            data-previa-em-foto={i === atual ? "" : undefined}
            className={`col-start-1 row-start-1 max-w-full select-none rounded-[8px] object-contain shadow-[0_18px_40px_-16px_rgba(20,11,43,0.45)] transition-opacity duration-500 ${deitada ? "max-h-[46vh]" : total > 1 ? "max-h-[50vh]" : "max-h-[62vh]"} ${i === atual ? "opacity-100" : "pointer-events-none opacity-0"}`}
          />
        ))}
        {total > 1 ? (
          <>
            <button type="button" aria-label="Página anterior" onClick={() => ir(atual - 1)} className="absolute left-1 top-1/2 flex h-11 w-11 -translate-y-1/2 items-center justify-center rounded-full bg-white text-[#1F1B2E] shadow-[0_6px_18px_-6px_rgba(20,11,43,0.45)] transition hover:scale-105" data-pagina-anterior>
              <ChevronLeft size={22} />
            </button>
            <button type="button" aria-label="Próxima página" onClick={() => ir(atual + 1)} className="absolute right-1 top-1/2 flex h-11 w-11 -translate-y-1/2 items-center justify-center rounded-full bg-white text-[#1F1B2E] shadow-[0_6px_18px_-6px_rgba(20,11,43,0.45)] transition hover:scale-105" data-proxima-pagina>
              <ChevronRight size={22} />
            </button>
            <span className="absolute right-3 top-3 rounded-full bg-[#140B2B]/75 px-2.5 py-1 text-[12px] font-bold text-white backdrop-blur-sm" data-pagina-da-previa>
              {atual + 1} / {total}
            </span>
          </>
        ) : null}
      </div>
      {total > 1 ? (
        <div className="flex max-w-full gap-2 overflow-x-auto px-1 pb-1">
          {fotos.map((q, i) => (
            <button key={q.src} type="button" aria-label={`Página ${i + 1}`} onClick={() => ir(i)} className={`shrink-0 overflow-hidden rounded-[6px] transition ${i === atual ? "ring-[3px] ring-[#7C3AED] ring-offset-2" : "opacity-60 hover:opacity-100"}`}>
              {/* eslint-disable-next-line @next/next/no-img-element */}
              <img src={q.src} alt="" draggable={false} className={`block select-none object-cover ${deitada ? "h-12 w-auto" : "h-16 w-auto"}`} />
            </button>
          ))}
        </div>
      ) : null}
    </div>
  );
}

/*
 * A foto do design (tirada no servidor a cada versão). Sem foto ainda, pede
 * ao servidor e mostra o esboço do tamanho enquanto isso; se o servidor não
 * conseguir, monta a arte (só essa) quando ela aparece. Nova versão = outra
 * chave no cartão (o estado começa de novo).
 */
function FotoDoDesign({ arte }: { arte: ArteNaGaleria }) {
  const [src, setSrc] = useState(() => enderecoDaMiniatura(arte));
  const [estado, setEstado] = useState<"carregando" | "pronta" | "tirando" | "montar">("carregando");
  const [doc, setDoc] = useState<DocumentoDoBanner | null>(null);
  const semFoto = () => {
    if (estado === "tirando" || estado === "montar") return;
    setEstado("tirando");
    void pedirMiniatura(arte).then((nova) => {
      if (nova) {
        setSrc(`${nova}?v=${Date.now()}`);
        setEstado("carregando");
        return;
      }
      setEstado("montar");
      void abrirArte(arte.id)
        .then((a) => a?.documento?.pecas && setDoc(a.documento))
        .catch(() => undefined);
    });
  };
  if (estado === "montar") return doc ? <ArteQueCabe doc={doc} /> : <EsbocoDoTamanho largura={arte.largura} altura={arte.altura} />;
  return (
    <div className="relative flex h-full w-full items-center justify-center">
      {estado !== "pronta" ? <EsbocoDoTamanho largura={arte.largura} altura={arte.altura} pulsando /> : null}
      {estado !== "tirando" ? (
        /* eslint-disable-next-line @next/next/no-img-element */
        <img
          src={src}
          alt={arte.nome}
          loading="lazy"
          decoding="async"
          onLoad={() => setEstado("pronta")}
          onError={semFoto}
          data-foto-do-design
          className={`max-h-full max-w-full rounded-[5px] object-contain shadow-[0_1px_2px_rgba(16,12,32,0.08),0_8px_24px_-10px_rgba(16,12,32,0.28)] ${estado === "pronta" ? "" : "absolute opacity-0"}`}
        />
      ) : null}
    </div>
  );
}

/* O retângulo no tamanho da arte, enquanto a foto não chega. */
function EsbocoDoTamanho({ largura, altura, pulsando = false }: { largura: number; altura: number; pulsando?: boolean }) {
  const r = largura / altura;
  return <span className={`block max-h-full max-w-full rounded-[5px] bg-[#E4DFF0] ${pulsando ? "animate-pulse" : ""}`} style={r >= 4 / 3 ? { width: "100%", aspectRatio: `${r}` } : { height: "100%", aspectRatio: `${r}` }} />;
}

function DesenhoDoTamanho({ largura, altura, cor = "currentColor" }: { largura: number; altura: number; cor?: string }) {
  const lado = 18;
  const f = lado / Math.max(largura, altura);
  return (
    <span className="flex h-[18px] w-[18px] shrink-0 items-center justify-center">
      <span className="block rounded-[3px] border-[1.6px]" style={{ width: Math.max(6, largura * f), height: Math.max(6, altura * f), borderColor: cor }} />
    </span>
  );
}

/* O que a pessoa já fez na comunidade (votos e favoritos); `ligado` = a migration 228 está no banco. */
type EstadoSocial = { ligado: boolean; votos: Map<string, 1 | -1>; favoritos: Set<string> };
const SOCIAL_VAZIO: EstadoSocial = { ligado: false, votos: new Map(), favoritos: new Set() };
const numeroCurto = new Intl.NumberFormat("pt-BR", { notation: "compact", maximumFractionDigits: 1 });

/* Gostei, não gostei e o coração (favoritos) de um modelo, com os números de todo mundo. */
function AcoesDoModelo({ chave, social, aoMudar }: { chave: string; social: EstadoSocial; aoMudar: (s: EstadoSocial) => void }) {
  const [numeros, setNumeros] = useState<Contagem>(CONTAGEM_VAZIA);
  const [aviso, setAviso] = useState("");
  useEffect(() => {
    let vivo = true;
    void contagemDo(chave).then((c) => vivo && setNumeros(c));
    return () => {
      vivo = false;
    };
  }, [chave]);
  const voto = social.votos.get(chave) ?? 0;
  const salvo = social.favoritos.has(chave);
  const mudarVoto = async (valor: 1 | -1) => {
    const novo = voto === valor ? 0 : valor;
    const antes = { social, numeros };
    const votos = new Map(social.votos);
    if (novo) votos.set(chave, novo);
    else votos.delete(chave);
    aoMudar({ ...social, votos });
    setNumeros((n) => ({ ...n, gostei: n.gostei - (voto === 1 ? 1 : 0) + (novo === 1 ? 1 : 0), nao_gostei: n.nao_gostei - (voto === -1 ? 1 : 0) + (novo === -1 ? 1 : 0) }));
    try {
      await votar(chave, novo);
    } catch (erro) {
      aoMudar(antes.social);
      setNumeros(antes.numeros);
      setAviso(erro instanceof Error ? erro.message : "Não deu agora.");
    }
  };
  const mudarFavorito = async () => {
    const antes = { social, numeros };
    const favoritos = new Set(social.favoritos);
    if (salvo) favoritos.delete(chave);
    else favoritos.add(chave);
    aoMudar({ ...social, favoritos });
    setNumeros((n) => ({ ...n, salvos: Math.max(0, n.salvos + (salvo ? -1 : 1)) }));
    try {
      await favoritar(chave, !salvo);
    } catch (erro) {
      aoMudar(antes.social);
      setNumeros(antes.numeros);
      setAviso(erro instanceof Error ? erro.message : "Não deu agora.");
    }
  };
  const BOTAO = "flex h-10 items-center gap-1.5 rounded-full px-3.5 text-[13px] font-bold transition";
  return (
    <div className="mt-5" data-acoes-do-modelo>
      <div className="flex flex-wrap items-center gap-2">
        <div className="flex items-center rounded-full bg-[#F1EFF6] p-1">
          <button type="button" aria-pressed={voto === 1} title="Gostei" onClick={() => void mudarVoto(1)} className={`${BOTAO} h-8 ${voto === 1 ? "bg-white text-[#7C3AED] shadow-sm" : "text-[#4B4760] hover:text-[#1F1B2E]"}`} data-gostei>
            <ThumbsUp size={16} strokeWidth={2.4} className={voto === 1 ? "fill-[#7C3AED]/15" : ""} />
            {numeroCurto.format(numeros.gostei)}
          </button>
          <span className="mx-0.5 h-5 w-px bg-[#DCD7E8]" />
          <button type="button" aria-pressed={voto === -1} title="Não gostei" onClick={() => void mudarVoto(-1)} className={`${BOTAO} h-8 ${voto === -1 ? "bg-white text-[#1F1B2E] shadow-sm" : "text-[#4B4760] hover:text-[#1F1B2E]"}`} data-nao-gostei>
            <ThumbsDown size={16} strokeWidth={2.4} />
          </button>
        </div>
        <button
          type="button"
          aria-pressed={salvo}
          onClick={() => void mudarFavorito()}
          className={`${BOTAO} ${salvo ? "bg-[#FFE4EC] text-[#E11D48]" : "bg-[#F1EFF6] text-[#4B4760] hover:text-[#E11D48]"}`}
          data-favoritar
        >
          <Heart size={16} strokeWidth={2.4} className={salvo ? "fill-[#E11D48]" : ""} />
          {salvo ? "Nos favoritos" : "Favoritar"}
          {numeros.salvos ? <span className="font-semibold opacity-70">· {numeroCurto.format(numeros.salvos)}</span> : null}
        </button>
      </div>
      {aviso ? <p className="mt-2 text-[12px] font-semibold text-[#B91C1C]">{aviso}</p> : null}
    </div>
  );
}

/* Seguir quem publica: o sino avisa dos modelos novos dela. Mostra os seguidores. */
function BotaoSeguir({ autor, grande = false, aoMudar }: { autor: string; grande?: boolean; aoMudar?: (r: ResumoDoAutor) => void }) {
  const [resumo, setResumo] = useState<ResumoDoAutor | null>(null);
  const [mexendo, setMexendo] = useState(false);
  useEffect(() => {
    let vivo = true;
    void resumoDoAutor(autor).then((r) => {
      if (!vivo || !r) return;
      setResumo(r);
      aoMudar?.(r);
    });
    return () => {
      vivo = false;
    };
    /* aoMudar muda a cada desenho do pai; o que importa é o autor. */
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [autor]);
  if (!resumo) return null;
  const alternar = async () => {
    setMexendo(true);
    const novo = { ...resumo, sigo: !resumo.sigo, seguidores: Math.max(0, resumo.seguidores + (resumo.sigo ? -1 : 1)) };
    setResumo(novo);
    aoMudar?.(novo);
    try {
      await seguir(autor, novo.sigo);
    } catch {
      setResumo(resumo);
      aoMudar?.(resumo);
    } finally {
      setMexendo(false);
    }
  };
  return (
    <button
      type="button"
      disabled={mexendo}
      onClick={(e) => {
        e.stopPropagation();
        void alternar();
      }}
      title={resumo.sigo ? "Deixar de seguir" : "Seguir: o sino avisa quando sair modelo novo"}
      className={`flex shrink-0 items-center gap-1.5 rounded-full font-bold transition disabled:opacity-70 ${grande ? "h-11 px-5 text-[14px]" : "h-9 px-3.5 text-[12.5px]"} ${resumo.sigo ? "bg-[#F1EFF6] text-[#4B4760] hover:bg-[#E9E4F3]" : "bg-[#1F1B2E] text-white hover:bg-black"}`}
      data-seguir={resumo.sigo ? "seguindo" : "seguir"}
    >
      {resumo.sigo ? <UserCheck size={grande ? 17 : 15} /> : <UserPlus size={grande ? 17 : 15} />}
      {resumo.sigo ? "Seguindo" : "Seguir"}
    </button>
  );
}

/*
 * O perfil de quem publica na comunidade: foto, nome, loja, os números
 * (modelos, seguidores, curtidas, usos), o botão de seguir e todos os modelos
 * aprovados dela, em galeria. Clicar num modelo abre a prévia dele.
 */
function PerfilDoAutor({ autor, modelos, podeSeguir, aoFechar, aoAbrir }: { autor: { id: string; nome: string; avatar: string | null; loja: string | null }; modelos: ModeloDaComunidade[]; podeSeguir: boolean; aoFechar: () => void; aoAbrir: (c: ModeloDaComunidade) => void }) {
  const [resumo, setResumo] = useState<ResumoDoAutor | null>(null);
  useEffect(() => {
    let vivo = true;
    void resumoDoAutor(autor.id).then((r) => vivo && r && setResumo(r));
    return () => {
      vivo = false;
    };
  }, [autor.id]);
  const numeros: Array<[string, number]> = [
    ["modelos", resumo?.modelos ?? modelos.length],
    ["seguidores", resumo?.seguidores ?? 0],
    ["curtidas", resumo?.gostei ?? 0],
    ["usos", resumo?.usos ?? modelos.reduce((t, m) => t + (m.usos || 0), 0)],
  ];
  return (
    <div className="fixed inset-0 z-[75] flex items-center justify-center bg-[#140B2B]/55 p-3 backdrop-blur-[2px] md:p-8" onMouseDown={(e) => e.target === e.currentTarget && aoFechar()} role="dialog" aria-modal="true" aria-label={`Perfil de ${autor.nome}`} data-perfil-do-autor>
      <div className="flex h-full max-h-[880px] w-full max-w-[1180px] flex-col overflow-hidden rounded-[24px] bg-white shadow-[0_30px_80px_-20px_rgba(20,11,43,0.5)]">
        <div className="relative shrink-0 bg-[linear-gradient(135deg,#EDE9FE_0%,#FCE7F3_100%)] px-6 pb-6 pt-7 md:px-10">
          <button type="button" aria-label="Fechar" onClick={aoFechar} className="absolute right-5 top-5 flex h-9 w-9 items-center justify-center rounded-full bg-white/80 text-[#4B4760] hover:bg-white">
            <X size={18} />
          </button>
          <div className="flex flex-wrap items-center gap-5">
            <AvatarDoAutor nome={autor.nome} src={autor.avatar} tamanho={88} />
            <div className="min-w-0 flex-1">
              <p className="text-[12px] font-bold uppercase tracking-[0.14em] text-[#7C3AED]">Criador da comunidade</p>
              <h2 className="mt-1 text-[28px] font-extrabold leading-tight tracking-[-0.02em] text-[#1F1B2E]" data-nome-do-autor>
                {autor.nome}
              </h2>
              {autor.loja && autor.loja !== autor.nome ? <p className="text-[13.5px] text-[#6B6584]">{autor.loja}</p> : null}
            </div>
            {podeSeguir ? <BotaoSeguir autor={autor.id} grande aoMudar={setResumo} /> : null}
          </div>
          <div className="mt-6 grid grid-cols-2 gap-3 sm:grid-cols-4">
            {numeros.map(([rotulo, valor]) => (
              <div key={rotulo} className="rounded-[14px] bg-white/80 px-4 py-3" data-numero-do-autor={rotulo}>
                <p className="text-[24px] font-extrabold leading-none tracking-[-0.02em] text-[#1F1B2E]">{numeroCurto.format(valor)}</p>
                <p className="mt-1 text-[12px] font-semibold text-[#6B6584]">{rotulo}</p>
              </div>
            ))}
          </div>
        </div>
        <div className="min-h-0 flex-1 overflow-y-auto px-6 py-6 md:px-10">
          <h3 className="mb-4 text-[16px] font-extrabold text-[#1F1B2E]">Modelos publicados</h3>
          {modelos.length ? (
            <div className="gap-3" style={{ columnWidth: "220px" }}>
              {modelos.map((c) => (
                <button key={c.id} type="button" onClick={() => aoAbrir(c)} title={c.nome} className="group relative mb-3 block w-full break-inside-avoid overflow-hidden rounded-[10px] bg-[var(--ks-color-bg-inset)] text-left" data-modelo-do-autor={c.id}>
                  {c.miniatura ? (
                    /* eslint-disable-next-line @next/next/no-img-element */
                    <img src={c.miniatura} alt={c.nome} width={c.largura} height={c.altura} loading="lazy" decoding="async" className="block h-auto w-full transition duration-300 group-hover:scale-[1.02]" />
                  ) : (
                    <span className="flex w-full items-center justify-center text-[12px] text-[var(--ks-color-text-soft)]" style={{ aspectRatio: `${c.largura} / ${c.altura}` }}>
                      Sem prévia
                    </span>
                  )}
                  <span className="pointer-events-none absolute inset-x-0 bottom-0 bg-[linear-gradient(0deg,rgba(15,10,30,0.8),rgba(15,10,30,0))] px-3 pb-2.5 pt-9 opacity-0 transition group-hover:opacity-100">
                    <span className="block text-[13px] font-bold leading-tight text-white">{c.nome}</span>
                    <span className="mt-0.5 block text-[11.5px] leading-tight text-white/75">{NOMES_DOS_TIPOS[c.tipo]}</span>
                  </span>
                </button>
              ))}
            </div>
          ) : (
            <p className="text-[13.5px] text-[#8A849E]">Ainda não tem modelos na galeria.</p>
          )}
        </div>
      </div>
    </div>
  );
}

export function GaleriaDeArtes() {
  const [dono, setDono] = useState<string | null>(null);
  const [artes, setArtes] = useState<ArteNaGaleria[] | null>(null);
  const [indisponivel, setIndisponivel] = useState(false);
  const [falha, setFalha] = useState("");
  const [aberta, setAberta] = useState<Aberta | null>(null);
  /* A janela "Criar um design" (aberta no grupo escolhido). */
  const [janela, setJanela] = useState<GrupoDaJanela | null>(null);
  const [tipo, setTipo] = useState<string>("post");
  const [livre, setLivre] = useState({ largura: 1080, altura: 1080 });
  const [unidade, setUnidade] = useState<"px" | "cm">("px");
  const [busca, setBusca] = useState("");
  const [buscaDaJanela, setBuscaDaJanela] = useState("");
  /* O ramo dos modelos (sublimação, gráfica, personalizados): "todos" mostra tudo. */
  const [ramo, setRamo] = useState<RamoDoModelo | "todos">("todos");
  const [todosOsDesigns, setTodosOsDesigns] = useState(false);
  const [renomeando, setRenomeando] = useState<{ id: string; nome: string } | null>(null);
  const [menu, setMenu] = useState<string | null>(null);
  const [abrindo, setAbrindo] = useState<string | null>(null);
  const [confirmacao, setConfirmacao] = useState<Confirmacao | null>(null);
  /* A comunidade: os modelos aprovados e os envios desta empresa. */
  const [comunidade, setComunidade] = useState<ModeloDaComunidade[]>([]);
  const [envios, setEnvios] = useState<ModeloDaComunidade[]>([]);
  const [previa, setPrevia] = useState<Previa | null>(null);
  /* O lado social: votos e favoritos da pessoa, o filtro "Seus favoritos" e o perfil de um autor aberto. */
  const [social, setSocial] = useState<EstadoSocial>(SOCIAL_VAZIO);
  const [soFavoritos, setSoFavoritos] = useState(false);
  /* Filtros por etiqueta (ocasião e cor): valem em todos os tipos, como a busca. */
  const [ocasiao, setOcasiao] = useState<string | null>(null);
  const [cor, setCor] = useState<string | null>(null);
  const [perfil, setPerfil] = useState<{ id: string; nome: string; avatar: string | null; loja: string | null } | null>(null);
  const [usando, setUsando] = useState(false);
  const [publicacao, setPublicacao] = useState<Publicacao | null>(null);
  const [recado, setRecado] = useState("");
  /* O link público do design (o "Compartilhar"): copiado e mostrado num aviso. */
  const [linkDaArte, setLinkDaArte] = useState<{ nome: string; link: string } | null>(null);

  /* As fontes dos modelos (as miniaturas usam as mesmas do editor). */
  useEffect(() => garantirFontes(CHAVES_DAS_FONTES_DE_BASE), []);

  /* A situação da aprovação de cada design (a prova mais nova): aprovado, alteração pedida ou esperando o cliente. */
  const [situacoes, setSituacoes] = useState<Record<string, ResumoDaProva["situacao"]>>({});
  const carregar = useCallback(async (quem: string) => {
    try {
      const lista = await listarArtesDaGaleria(quem, 200);
      setArtes(lista);
      situacaoDasProvas(lista.map((a) => a.id))
        .then(setSituacoes)
        .catch(() => undefined);
      setIndisponivel(false);
    } catch (erro) {
      if (erro instanceof GaleriaIndisponivel) setIndisponivel(true);
      else setFalha(erro instanceof Error ? erro.message : "Não deu para abrir os seus designs.");
      setArtes([]);
    }
  }, []);

  const carregarComunidade = useCallback(async (quem: string) => {
    const [aprovados, daEmpresa] = await Promise.all([modelosAprovados(), enviosDaEmpresa(quem)]);
    setComunidade(aprovados);
    setEnvios(daEmpresa);
  }, []);

  useEffect(() => {
    (async () => {
      const supabase = getSupabaseClient();
      const {
        data: { user },
      } = await supabase.auth.getUser();
      if (!user) return;
      const ctx = await getCompanyContext(supabase, user);
      const quem = resolveCompanyPublicProfileId(ctx, user.id);
      setDono(quem);
      void carregarComunidade(quem);
      void meuEstado().then(setSocial);
      await carregar(quem);
    })();
  }, [carregar, carregarComunidade]);

  /* O link do sino (?modelo=<id>) abre a prévia do modelo; ?autor=<id> abre o perfil de quem publica. Uma vez só. */
  const linkUsado = useRef(false);
  useEffect(() => {
    if (linkUsado.current || !comunidade.length || typeof window === "undefined") return;
    const busca_ = new URLSearchParams(window.location.search);
    const id = busca_.get("modelo");
    const autor = busca_.get("autor");
    if (!id && !autor) return;
    linkUsado.current = true;
    const c = id ? comunidade.find((m) => m.id === id) : null;
    if (c) setPrevia({ tipo: TIPOS_DE_MODELO.find((t) => t.chave === c.tipo) || TIPOS_DE_MODELO[0], comunidade: c });
    const deQuem = autor ? comunidade.find((m) => m.user_id === autor) : null;
    if (deQuem) setPerfil({ id: deQuem.user_id, nome: deQuem.autor_nome, avatar: deQuem.autor_avatar, loja: deQuem.autor_loja });
    window.history.replaceState(null, "", window.location.pathname);
  }, [comunidade]);

  /* Esc fecha a janela de tamanhos, a prévia e a publicação. */
  useEffect(() => {
    if (!janela && !previa && !publicacao && !perfil) return;
    const tecla = (e: KeyboardEvent) => {
      if (e.key !== "Escape") return;
      if (perfil) {
        setPerfil(null);
        return;
      }
      setJanela(null);
      setPrevia(null);
      setPublicacao((p) => (p?.enviando ? p : null));
    };
    window.addEventListener("keydown", tecla);
    return () => window.removeEventListener("keydown", tecla);
  }, [janela, previa, publicacao, perfil]);

  const termo = semAcento(busca.trim());
  const tipoAtual = TIPOS_DE_MODELO.find((t) => t.chave === tipo) || TIPOS_DE_MODELO[0];

  /* A capa de cada categoria: o primeiro modelo dela, de verdade. */
  const capas = useMemo(() => {
    return Object.fromEntries(
      TIPOS_DE_MODELO.map((t) => {
        const lista = MODELOS_DO_EDITOR.filter(t.filtro);
        return [t.chave, { quantos: lista.length + comunidade.filter((c) => c.tipo === t.chave).length, modelo: lista[0] ?? null, doc: lista[0] ? lista[0].montar(pedidoNeutro(t)) : null }];
      }),
    ) as Record<string, { quantos: number; modelo: ModeloDoEditor | null; doc: DocumentoDoBanner | null }>;
  }, [comunidade]);

  /* Os da comunidade: os do tipo escolhido (com busca, de todos), também pelo nome de quem fez. */
  const daComunidade = useMemo(
    () =>
      comunidade
        .filter((c) => {
          if (soFavoritos) return social.favoritos.has(c.id);
          /* Da comunidade não se mede a cor (só a ocasião, pelo nome e a descrição). */
          if (cor) return false;
          const texto = textoDoModelo(`${c.nome} ${c.autor_nome} ${c.autor_loja || ""} ${NOMES_DOS_TIPOS[c.tipo]}`, c.descricao || "", null);
          if (ocasiao && !ocasioesDoTexto(texto).includes(ocasiao)) return false;
          if (!termo && !ocasiao && c.tipo !== tipoAtual.chave) return false;
          if (ramo !== "todos" && c.ramo !== ramo) return false;
          return !termo || texto.includes(termo);
        })
        .map((c) => ({ c, tipo: TIPOS_DE_MODELO.find((t) => t.chave === c.tipo) || TIPOS_DE_MODELO[0] })),
    [comunidade, termo, tipoAtual, ramo, soFavoritos, social.favoritos, ocasiao, cor],
  );
  /* A situação do envio de cada design desta empresa (o mais recente). */
  const envioDaArte = useMemo(() => {
    const mapa = new Map<string, ModeloDaComunidade>();
    for (const e of envios) if (e.arte_id && !mapa.has(e.arte_id)) mapa.set(e.arte_id, e);
    return mapa;
  }, [envios]);

  /* Os modelos: os do tipo escolhido; com busca, os de todos os tipos que batem (cada um no tamanho do tipo dele). Na galeria vão neutros (a foto pronta); a prévia monta com a cara da loja. */
  /*
   * Todos os modelos da casa, uma vez, com as etiquetas: o texto inteiro (para
   * a busca achar "natal" dentro do modelo), as ocasiões e as cores da foto.
   */
  const catalogo = useMemo(
    () =>
      TIPOS_DE_MODELO.flatMap((t) =>
        MODELOS_DO_EDITOR.filter(t.filtro).map((m) => {
          const doc = m.montar(pedidoNeutro(t));
          const texto = textoDoModelo(m.nome, m.descricao, doc);
          const ocasioes = ocasioesDoTexto(texto);
          const cores = miniaturaDoModelo(t.chave, m.chave, doc)?.cores ?? [];
          const rotulos = semAcento(`${t.nome} ${m.ramo ? RAMOS_DOS_MODELOS[m.ramo] : ""} ${ocasioes.map((o) => OCASIOES.find((x) => x.chave === o)?.nome).join(" ")} ${cores.map((c) => CORES_NOMEADAS.find((x) => x.chave === c)?.nome).join(" ")}`);
          return { modelo: m, tipo: t, doc, busca: `${texto} ${rotulos}`, ocasioes, cores };
        }),
      ),
    [],
  );
  /* Quantos modelos cada etiqueta tem (os chips só mostram as que existem). */
  const porEtiqueta = useMemo(() => {
    const ocasioes = new Map<string, number>();
    const cores = new Map<string, number>();
    for (const item of catalogo) {
      item.ocasioes.forEach((o) => ocasioes.set(o, (ocasioes.get(o) || 0) + 1));
      item.cores.forEach((c) => cores.set(c, (cores.get(c) || 0) + 1));
    }
    return { ocasioes, cores };
  }, [catalogo]);
  const filtrando = Boolean(termo || ocasiao || cor);
  const modelos = useMemo(
    () =>
      catalogo.filter((item) => {
        if (soFavoritos) return social.favoritos.has(chaveDaCasa(item.modelo.chave));
        if (!filtrando && item.tipo.chave !== tipoAtual.chave) return false;
        if (ramo !== "todos" && item.modelo.ramo !== ramo) return false;
        if (ocasiao && !item.ocasioes.includes(ocasiao)) return false;
        if (cor && !item.cores.includes(cor)) return false;
        return !termo || item.busca.includes(termo);
      }),
    [catalogo, termo, tipoAtual, ramo, soFavoritos, social.favoritos, ocasiao, cor, filtrando],
  );
  const misturado = filtrando || soFavoritos;
  const nomeDoFiltro = [ocasiao ? OCASIOES.find((o) => o.chave === ocasiao)?.nome : "", cor ? `em ${CORES_NOMEADAS.find((c) => c.chave === cor)?.nome.toLowerCase()}` : ""].filter(Boolean).join(" ");

  const designs = useMemo(() => (artes || []).filter((a) => !termo || semAcento(a.nome).includes(termo)), [artes, termo]);
  const designsVisiveis = todosOsDesigns || termo ? designs : designs.slice(0, 14);

  /* Os tamanhos da janela "Criar um design". */
  const termoDaJanela = semAcento(buscaDaJanela.trim());
  const tamanhosDaJanela = FORMATOS_DA_ARTE.filter((f) =>
    termoDaJanela ? semAcento(`${f.nome} ${f.detalhe}`).includes(termoDaJanela) : janela === "populares" ? POPULARES.includes(f.chave) : f.grupo === janela,
  ).sort((a, b) => (janela === "populares" && !termoDaJanela ? POPULARES.indexOf(a.chave) - POPULARES.indexOf(b.chave) : 0));
  const tamanhosDaBusca = termo ? FORMATOS_DA_ARTE.filter((f) => semAcento(`${f.nome} ${f.detalhe}`).includes(termo)) : [];

  const livreEmPx = unidade === "px" ? livre : { largura: pxDe(livre.largura * 10, 300), altura: pxDe(livre.altura * 10, 300) };
  const livreValido = livreEmPx.largura >= MINIMO_DO_LADO && livreEmPx.altura >= MINIMO_DO_LADO && livreEmPx.largura <= LIMITE_DO_LADO && livreEmPx.altura <= LIMITE_DO_LADO;

  const criarEmBranco = (f: FormatoDaArte) => {
    setJanela(null);
    setAberta({ largura: f.largura, altura: f.altura, formato: f.chave, arte: null, modelo: "branco" });
  };
  const criarPersonalizado = () => {
    if (!livreValido) return;
    setJanela(null);
    setAberta({ largura: livreEmPx.largura, altura: livreEmPx.altura, formato: null, arte: null, modelo: "branco" });
  };
  const abrirModelo = (t: TipoDeModelo, chave: string) => {
    const f = formatoDaChave(t.formato)!;
    setAberta({ largura: f.largura, altura: f.altura, formato: f.chave, arte: null, modelo: chave });
  };

  /* "Usar este modelo": o da casa abre montado; o da comunidade vira uma cópia nos designs da empresa. */
  const usarPrevia = async () => {
    if (!previa) return;
    if (previa.casa) {
      abrirModelo(previa.tipo, previa.casa.modelo.chave);
      setPrevia(null);
      return;
    }
    const modelo = previa.comunidade;
    if (!modelo || !dono) return;
    setUsando(true);
    try {
      const documento = await documentoDoModelo(modelo.id);
      if (!documento) throw new Error("Esse modelo não está mais disponível.");
      const id = await criarArte(dono, { nome: modelo.nome, largura: modelo.largura, altura: modelo.altura, formato: modelo.formato, documento });
      void contarUso(modelo.id);
      const arte = await abrirArte(id);
      if (!arte) throw new Error("Não deu para abrir a cópia do modelo.");
      setPrevia(null);
      setAberta({ largura: arte.largura, altura: arte.altura, formato: arte.formato, arte, modelo: null });
    } catch (erro) {
      setFalha(erro instanceof Error ? erro.message : "Não deu para usar esse modelo agora.");
      setPrevia(null);
    } finally {
      setUsando(false);
    }
  };

  const publicar = async () => {
    if (!publicacao || publicacao.enviando || !dono) return;
    setPublicacao({ ...publicacao, enviando: true, erro: "" });
    try {
      await enviarParaComunidade({ arteId: publicacao.arte.id, nome: publicacao.nome, descricao: publicacao.descricao, ramo: publicacao.ramo });
      setPublicacao(null);
      setRecado("Enviado para análise! Assim que a equipe Ktalogu aprovar, o modelo entra na galeria com o seu nome.");
      void carregarComunidade(dono);
    } catch (erro) {
      setPublicacao((p) => (p ? { ...p, enviando: false, erro: erro instanceof Error ? erro.message : "Não deu para enviar agora." } : p));
    }
  };

  const retirar = (envio: ModeloDaComunidade) => {
    setMenu(null);
    setConfirmacao({
      titulo: `Retirar "${envio.nome}" da comunidade?`,
      texto: envio.status === "aprovado" ? "O modelo sai da galeria das outras empresas. As cópias que elas já fizeram continuam com elas. O seu design continua aqui." : "O envio sai da fila de análise. O seu design continua aqui.",
      confirmar: "Retirar",
      tom: "perigo",
      aoConfirmar: async () => {
        setConfirmacao(null);
        try {
          await retirarEnvio(envio.id);
          if (dono) await carregarComunidade(dono);
        } catch (erro) {
          setFalha(erro instanceof Error ? erro.message : "Não deu para retirar.");
        }
      },
    });
  };

  const compartilhar = async (arte: ArteNaGaleria) => {
    setMenu(null);
    try {
      const r = await fetch("/api/artes/link-publico", { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ id: arte.id }) });
      const d = (await r.json().catch(() => ({}))) as { link?: string; error?: string };
      if (!r.ok || !d.link) throw new Error(d.error || "Não deu para fazer o link agora.");
      await navigator.clipboard?.writeText(d.link).catch(() => undefined);
      setLinkDaArte({ nome: arte.nome, link: d.link });
    } catch (erro) {
      setFalha(erro instanceof Error ? erro.message : "Não deu para fazer o link agora.");
    }
  };

  /*
   * O "Criar no KtaloguDesign" do item do orçamento (manda
   * ?criar=1&nome=Orçamento #12 · Caneca&orcamento=<id>&item=<id>): abre a
   * janela de tamanhos, e a arte nova já nasce com esse nome e ligada ao item
   * (o editor mostra "Enviar para o orçamento"). Uma vez só.
   */
  const [nomeDaNova, setNomeDaNova] = useState<string | null>(null);
  const [vinculoDaNova, setVinculoDaNova] = useState<VinculoComOrcamento | null>(null);
  useEffect(() => {
    const busca_ = new URLSearchParams(window.location.search);
    if (busca_.get("criar") !== "1") return;
    const nome = (busca_.get("nome") || "").trim().slice(0, 100);
    const uuid = /^[0-9a-f-]{36}$/i;
    const orcamento = busca_.get("orcamento") || "";
    const item = busca_.get("item") || "";
    setNomeDaNova(nome || null);
    setVinculoDaNova(uuid.test(orcamento) ? { orcamento, item: uuid.test(item) ? item : null } : null);
    setJanela("populares");
    window.history.replaceState(null, "", window.location.pathname);
  }, []);

  /* O link do editor que o assistente devolve (?arte=<id>) abre o design direto. Uma vez só. */
  const arteDoLink = useRef(false);
  useEffect(() => {
    if (arteDoLink.current || !dono || typeof window === "undefined") return;
    const id = new URLSearchParams(window.location.search).get("arte");
    if (!id || !/^[0-9a-f-]{36}$/i.test(id)) return;
    arteDoLink.current = true;
    window.history.replaceState(null, "", window.location.pathname);
    setAbrindo(id);
    abrirArte(id)
      .then((fresca) => {
        if (!fresca) throw new Error("Esse design não foi encontrado nesta empresa.");
        setAberta({ largura: fresca.largura, altura: fresca.altura, formato: fresca.formato, arte: fresca, modelo: null });
      })
      .catch((erro) => setFalha(erro instanceof Error ? erro.message : "Não deu para abrir esse design."))
      .finally(() => setAbrindo(null));
  }, [dono]);

  /* Abre sempre a versão do banco agora (a lista pode estar atrás da última gravação). */
  const abrirDoBanco = async (arte: ArteNaGaleria) => {
    setAbrindo(arte.id);
    try {
      const fresca = await abrirArte(arte.id);
      if (!fresca) throw new Error("Esse design não foi encontrado.");
      setAberta({ largura: fresca.largura, altura: fresca.altura, formato: fresca.formato, arte: fresca, modelo: null });
    } catch (erro) {
      setFalha(erro instanceof Error ? erro.message : "Não deu para abrir esse design.");
    } finally {
      setAbrindo(null);
    }
  };

  /* "Publicar na comunidade": a categoria sai do documento, que vem agora (a lista não traz). */
  const abrirPublicacao = async (arte: ArteNaGaleria) => {
    setMenu(null);
    try {
      const completa = await abrirArte(arte.id);
      if (!completa) throw new Error("Esse design não foi encontrado.");
      setPublicacao({ arte: completa, nome: arte.nome.slice(0, 80), descricao: "", ramo: null, aceite: false, enviando: false, erro: "" });
    } catch (erro) {
      setFalha(erro instanceof Error ? erro.message : "Não deu para abrir esse design.");
    }
  };

  const renomear = async () => {
    if (!renomeando) return;
    const novo = renomeando.nome.trim() || "Design sem nome";
    setRenomeando(null);
    try {
      await salvarArte(renomeando.id, { nome: novo });
      setArtes((lista) => (lista || []).map((a) => (a.id === renomeando.id ? { ...a, nome: novo } : a)));
    } catch (erro) {
      setFalha(erro instanceof Error ? erro.message : "Não deu para renomear.");
    }
  };

  const pedirParaApagar = (arte: ArteNaGaleria) => {
    setMenu(null);
    setConfirmacao({
      titulo: `Apagar "${arte.nome}"?`,
      texto: "O design sai daqui. Banners que já foram para a loja continuam lá.",
      confirmar: "Apagar design",
      tom: "perigo",
      aoConfirmar: async () => {
        setConfirmacao(null);
        try {
          await apagarArte(arte.id);
          setArtes((lista) => (lista || []).filter((a) => a.id !== arte.id));
        } catch (erro) {
          setFalha(erro instanceof Error ? erro.message : "Não deu para apagar.");
        }
      },
    });
  };

  const duplicar = async (arte: ArteNaGaleria) => {
    setMenu(null);
    if (!dono) return;
    try {
      const completa = await abrirArte(arte.id);
      if (!completa) throw new Error("Esse design não foi encontrado.");
      await duplicarArte(dono, completa);
      await carregar(dono);
    } catch (erro) {
      setFalha(erro instanceof Error ? erro.message : "Não deu para duplicar.");
    }
  };

  const abrirJanela = (grupo: GrupoDaJanela) => {
    setBuscaDaJanela("");
    setJanela(grupo);
  };

  const proporcaoDoTipo = (t: TipoDeModelo) => {
    const f = formatoDaChave(t.formato)!;
    return f.largura / f.altura;
  };
  const colunas = (proporcao: number) => (proporcao >= 2.4 ? 420 : proporcao >= 1.9 ? 360 : proporcao >= 0.95 ? 230 : 200);
  const formatoDoTipo = formatoDaChave(tipoAtual.formato)!;

  return (
    <div className="w-full pb-16">
      {/* ------------------------------------------------- a pergunta */}
      <section className="relative overflow-hidden rounded-[28px] bg-[linear-gradient(135deg,#DDF6F2_0%,#EDE9FE_55%,#F6E7FF_100%)] px-5 pb-8 pt-8 text-center md:pt-10">
        <p className="inline-flex items-center gap-2 text-[13px] font-extrabold tracking-[-0.01em] text-[#1F1B2E]">
          <span className="flex h-7 w-7 items-center justify-center rounded-[9px] bg-[#7C3AED] text-white">
            <MarcaDoDesign size={17} />
          </span>
          Ktalogu<span className="-ml-2 text-[#7C3AED]">Design</span>
        </p>
        <h1 className="mt-3 bg-[linear-gradient(90deg,#4F46E5,#7C3AED)] bg-clip-text text-[30px] font-extrabold leading-[1.1] tracking-[-0.025em] text-transparent md:text-[40px]">O que vamos criar hoje?</h1>
        <label className="mx-auto mt-6 flex h-[52px] max-w-[680px] items-center gap-3 rounded-full border border-white bg-white px-5 text-[#8A849E] shadow-[0_1px_2px_rgba(16,12,32,0.04),0_10px_30px_-18px_rgba(16,12,32,0.35)] transition focus-within:border-[#7C3AED] focus-within:shadow-[0_0_0_4px_rgba(124,58,237,0.12)]">
          <Search size={19} />
          <input
            value={busca}
            onChange={(e) => setBusca(e.target.value)}
            placeholder="Procure um tamanho, um modelo ou um design seu"
            className="h-full min-w-0 flex-1 bg-transparent text-[14.5px] text-[#1F1B2E] outline-none placeholder:text-[#8A849E]"
          />
          {busca ? (
            <button type="button" aria-label="Limpar a busca" onClick={() => setBusca("")} className="flex h-7 w-7 items-center justify-center rounded-full hover:bg-[#F1EFF6]">
              <X size={15} />
            </button>
          ) : null}
        </label>

        {/* Os atalhos (como os do Canva): criar e os grupos de tamanho. */}
        <div className="mt-7 flex flex-wrap justify-center gap-x-6 gap-y-4">
          <button type="button" data-criar-design onClick={() => abrirJanela("populares")} className="group flex w-[88px] flex-col items-center gap-2">
            <span className="flex h-[56px] w-[56px] items-center justify-center rounded-full bg-[#7C3AED] text-white shadow-[0_8px_20px_-8px_rgba(124,58,237,0.7)] transition group-hover:-translate-y-0.5">
              <Plus size={26} strokeWidth={2.6} />
            </span>
            <span className="text-[12.5px] font-bold leading-tight text-[#1F1B2E]">Criar um design</span>
          </button>
          <span className="hidden h-[56px] w-px bg-[#D9D3EA] sm:block" />
          {GRUPOS_DA_JANELA.filter((g) => g.chave !== "populares").map(({ chave, nome, cor, Icone }) => (
            <button key={chave} type="button" data-grupo-de-tamanho={chave} onClick={() => abrirJanela(chave)} className="group flex w-[88px] flex-col items-center gap-2">
              <span className="flex h-[56px] w-[56px] items-center justify-center rounded-full text-white transition group-hover:-translate-y-0.5" style={{ background: cor }}>
                <Icone size={24} strokeWidth={2} />
              </span>
              <span className="text-[12.5px] font-semibold leading-tight text-[#4B4760]">{nome}</span>
            </button>
          ))}
        </div>
      </section>

      {indisponivel ? (
        <div className="mt-8">
          <Aviso tom="neutro" titulo="Os designs ainda não ficam guardados">
            Dá para criar e baixar normalmente. Eles passam a aparecer aqui assim que a galeria for ligada.
          </Aviso>
        </div>
      ) : null}
      {falha ? (
        <div className="mt-8">
          <Aviso tom="ruim" titulo="Algo não deu certo" aoFechar={() => setFalha("")}>
            {falha}
          </Aviso>
        </div>
      ) : null}
      {linkDaArte ? (
        <div className="mt-8" data-link-da-arte>
          <Aviso tom="bom" titulo="Link copiado" aoFechar={() => setLinkDaArte(null)}>
            Quem abrir{" "}
            <a href={linkDaArte.link} target="_blank" rel="noopener noreferrer" className="break-all font-semibold text-[#6D28D9] underline">
              {linkDaArte.link}
            </a>{" "}
            vê &ldquo;{linkDaArte.nome}&rdquo;, toca o vídeo e baixa em PNG, JPG, PDF ou MP4, sem precisar de conta. Editar, só aqui no KtaloguDesign.
          </Aviso>
        </div>
      ) : null}
      {recado ? (
        <div className="mt-8" data-recado-da-comunidade>
          <Aviso tom="bom" titulo="Modelo enviado" aoFechar={() => setRecado("")}>
            {recado}
          </Aviso>
        </div>
      ) : null}

      {/* ------------------------------------------------- tamanhos que a busca achou */}
      {tamanhosDaBusca.length ? (
        <section className="mt-10">
          <h2 className="mb-4 text-[20px] font-extrabold tracking-[-0.015em] text-[var(--ks-color-text-primary)]">Tamanhos</h2>
          <div className="grid grid-cols-2 gap-4 sm:grid-cols-3 lg:grid-cols-5">
            {tamanhosDaBusca.map((f) => (
              <CartaoDoTamanho key={f.chave} formato={f} aoEscolher={() => criarEmBranco(f)} />
            ))}
          </div>
        </section>
      ) : null}

      {/* ------------------------------------------------- continue criando */}
      <section className="mt-10">
        <h2 className="mb-4 text-[20px] font-extrabold tracking-[-0.015em] text-[var(--ks-color-text-primary)]">
          {termo ? "Seus designs" : "Continue criando"}
          {artes?.length ? <span className="ml-2 text-[13px] font-bold text-[var(--ks-color-text-soft)]">{artes.length}</span> : null}
        </h2>
        {artes === null ? (
          <div className="flex items-center gap-2 py-8 text-[13px] text-[var(--ks-color-text-soft)]">
            <Loader2 size={16} className="animate-spin" /> Abrindo os seus designs…
          </div>
        ) : !designs.length ? (
          <p className="py-4 text-[13.5px] text-[var(--ks-color-text-soft)]">{busca ? "Nenhum design seu com esse nome." : "Os designs que você criar ficam aqui, guardados sozinhos a cada mudança."}</p>
        ) : (
          <>
            <div className="grid gap-x-3 gap-y-5" style={{ gridTemplateColumns: "repeat(auto-fill, minmax(168px, 1fr))" }} data-designs>
              {designsVisiveis.map((arte) => (
                <div key={arte.id} className="group relative">
                  <button
                    type="button"
                    data-abrir-arte={arte.id}
                    onClick={() => void abrirDoBanco(arte)}
                    title="Abrir no editor"
                    className="relative block aspect-[4/3] w-full rounded-[12px] bg-[var(--ks-color-bg-inset)] p-3 transition group-hover:bg-[#EFEAFD]"
                  >
                    <FotoDoDesign key={`${arte.id}@${arte.updated_at}`} arte={arte} />
                    {envioDaArte.get(arte.id) ? (
                      <span
                        data-situacao-do-envio={envioDaArte.get(arte.id)!.status}
                        title={envioDaArte.get(arte.id)!.status === "recusado" && envioDaArte.get(arte.id)!.motivo ? `Motivo: ${envioDaArte.get(arte.id)!.motivo}` : undefined}
                        className={`absolute left-2 top-2 flex items-center gap-1 rounded-full px-2 py-[3px] text-[10.5px] font-bold ${SITUACAO_DO_ENVIO[envioDaArte.get(arte.id)!.status].classe}`}
                      >
                        <Users size={11} strokeWidth={2.6} />
                        {SITUACAO_DO_ENVIO[envioDaArte.get(arte.id)!.status].rotulo}
                      </span>
                    ) : null}
                    {abrindo === arte.id ? (
                      <span className="absolute inset-0 flex items-center justify-center rounded-[14px] bg-white/60">
                        <Loader2 size={20} className="animate-spin text-[#7C3AED]" />
                      </span>
                    ) : null}
                  </button>
                  <div className="mt-1.5 flex items-start gap-1">
                    <div className="min-w-0 flex-1">
                      {renomeando?.id === arte.id ? (
                        <input
                          autoFocus
                          value={renomeando.nome}
                          onChange={(e) => setRenomeando({ id: arte.id, nome: e.target.value.slice(0, 120) })}
                          onBlur={() => void renomear()}
                          onKeyDown={(e) => {
                            if (e.key === "Enter") void renomear();
                            if (e.key === "Escape") setRenomeando(null);
                          }}
                          className="h-7 w-full rounded-md border border-[#7C3AED] px-1.5 text-[13px] font-bold outline-none"
                        />
                      ) : (
                        <p className="break-words text-[13px] font-bold leading-snug text-[var(--ks-color-text-primary)]" data-nome-do-design>{arte.nome}</p>
                      )}
                      {situacoes[arte.id] ? (
                        <span className={`mt-1 inline-flex rounded-full px-2 py-0.5 text-[10.5px] font-bold ${SITUACAO_DA_APROVACAO[situacoes[arte.id]].classe}`} data-situacao-da-aprovacao={situacoes[arte.id]}>
                          {SITUACAO_DA_APROVACAO[situacoes[arte.id]].rotulo}
                        </span>
                      ) : null}
                      <p className="mt-0.5 flex items-start gap-1.5 text-[12px] leading-snug text-[var(--ks-color-text-soft)]">
                        <DesenhoDoTamanho largura={arte.largura} altura={arte.altura} cor="#A1A1AA" />
                        {formatoDaChave(arte.formato)?.nome || formatoDoTamanho(arte.largura, arte.altura)?.nome || `${arte.largura} × ${arte.altura}`} · {quando.format(new Date(arte.updated_at))}
                      </p>
                    </div>
                    <div className="relative">
                      <button
                        type="button"
                        aria-label="Mais opções"
                        onClick={() => setMenu((m) => (m === arte.id ? null : arte.id))}
                        className="flex h-7 w-7 items-center justify-center rounded-lg text-[var(--ks-color-text-soft)] opacity-0 transition hover:bg-[var(--ks-color-bg-hover)] group-hover:opacity-100 focus:opacity-100"
                      >
                        <MoreHorizontal size={16} />
                      </button>
                      {menu === arte.id ? (
                        <>
                          <button type="button" aria-label="Fechar" className="fixed inset-0 z-[40] cursor-default" onClick={() => setMenu(null)} />
                          <div className="absolute right-0 top-8 z-[41] w-56 overflow-hidden rounded-xl border border-[var(--ks-color-border-subtle)] bg-[var(--ks-color-bg-elevated)] py-1 shadow-[var(--ks-shadow-overlay)]">
                            <button type="button" onClick={() => (setMenu(null), setRenomeando({ id: arte.id, nome: arte.nome }))} className="flex w-full items-center gap-2 px-3 py-2 text-[13px] font-semibold hover:bg-[var(--ks-color-bg-hover)]">
                              <Pencil size={14} /> Renomear
                            </button>
                            <button type="button" onClick={() => void duplicar(arte)} className="flex w-full items-center gap-2 px-3 py-2 text-[13px] font-semibold hover:bg-[var(--ks-color-bg-hover)]">
                              <Copy size={14} /> Duplicar
                            </button>
                            <button type="button" data-compartilhar-arte={arte.id} onClick={() => void compartilhar(arte)} className="flex w-full items-center gap-2 px-3 py-2 text-[13px] font-semibold hover:bg-[var(--ks-color-bg-hover)]">
                              <Link2 size={14} /> Copiar link para compartilhar
                            </button>
                            {envioDaArte.get(arte.id) && envioDaArte.get(arte.id)!.status !== "recusado" ? (
                              <button type="button" onClick={() => retirar(envioDaArte.get(arte.id)!)} className="flex w-full items-center gap-2 px-3 py-2 text-[13px] font-semibold hover:bg-[var(--ks-color-bg-hover)]">
                                <Undo2 size={14} /> Retirar da comunidade
                              </button>
                            ) : (
                              <button
                                type="button"
                                data-publicar-na-comunidade={arte.id}
                                onClick={() => void abrirPublicacao(arte)}
                                className="flex w-full items-center gap-2 px-3 py-2 text-[13px] font-semibold text-[#6D28D9] hover:bg-[var(--ks-color-bg-hover)]"
                              >
                                <Send size={14} /> Publicar na comunidade
                              </button>
                            )}
                            {envioDaArte.get(arte.id)?.status === "recusado" && envioDaArte.get(arte.id)!.motivo ? (
                              <p className="mx-3 my-1 rounded-lg bg-[#FFF1F2] px-2 py-1.5 text-[11.5px] leading-snug text-[#9F1239]">Recusado: {envioDaArte.get(arte.id)!.motivo}</p>
                            ) : null}
                            <button type="button" onClick={() => pedirParaApagar(arte)} className="flex w-full items-center gap-2 px-3 py-2 text-[13px] font-semibold text-[var(--ks-color-status-danger)] hover:bg-[var(--ks-color-bg-hover)]">
                              <Trash2 size={14} /> Apagar
                            </button>
                          </div>
                        </>
                      ) : null}
                    </div>
                  </div>
                </div>
              ))}
            </div>
            {!termo && designs.length > 14 ? (
              <div className="mt-6 flex justify-center">
                <button type="button" onClick={() => setTodosOsDesigns((v) => !v)} className="h-10 rounded-[10px] border border-[var(--ks-color-border-subtle)] bg-[var(--ks-color-bg-elevated)] px-5 text-[13px] font-bold text-[var(--ks-color-text-primary)] hover:border-[#C4B5FD]">
                  {todosOsDesigns ? "Mostrar menos" : `Mostrar mais (${designs.length - 14})`}
                </button>
              </div>
            ) : null}
          </>
        )}
      </section>

      {/* ------------------------------------------------- categorias de modelos */}
      {!termo ? (
        <section className="mt-12">
          <h2 className="mb-4 text-[20px] font-extrabold tracking-[-0.015em] text-[var(--ks-color-text-primary)]">Explorar categorias de modelos</h2>
          <div className="grid grid-cols-2 gap-3 sm:grid-cols-3 lg:grid-cols-5">
            {TIPOS_DE_MODELO.map((t) => {
              const ativo = t.chave === tipoAtual.chave;
              const capa = capas[t.chave];
              return (
                <button
                  key={t.chave}
                  type="button"
                  data-tipo-de-modelo={t.chave}
                  aria-pressed={ativo}
                  onClick={() => {
                    setTipo(t.chave);
                    setRamo("todos");
                  }}
                  className={`group relative flex h-[92px] items-center overflow-hidden rounded-[16px] px-4 text-left transition ${ativo ? "ring-[3px] ring-offset-2" : "hover:-translate-y-0.5"}`}
                  style={{ background: t.fundo, ...(ativo ? ({ "--tw-ring-color": t.cor } as React.CSSProperties) : {}) }}
                >
                  <span className="relative z-[1] max-w-[58%]">
                    <span className="block text-[14px] font-extrabold leading-tight" style={{ color: t.cor }}>
                      {t.nome}
                    </span>
                    <span className="mt-0.5 block text-[11.5px] font-semibold text-[#6B6584]">{capa ? `${capa.quantos} ${capa.quantos === 1 ? "modelo" : "modelos"}` : ""}</span>
                  </span>
                  {capa?.doc && capa.modelo ? (
                    <span className="pointer-events-none absolute -right-3 top-1/2 flex h-[78px] w-[46%] -translate-y-1/2 rotate-[-6deg] items-center justify-center transition group-hover:rotate-[-3deg]">
                      <span className="block overflow-hidden rounded-[6px] shadow-[0_1px_2px_rgba(16,12,32,0.08),0_8px_24px_-10px_rgba(16,12,32,0.28)]" style={capa.doc.largura / capa.doc.altura > 1.6 ? { width: "100%" } : { height: 78, aspectRatio: `${capa.doc.largura} / ${capa.doc.altura}` }}>
                        <FotoDoModelo tipo={t} modelo={capa.modelo} doc={capa.doc} />
                      </span>
                    </span>
                  ) : null}
                </button>
              );
            })}
          </div>
        </section>
      ) : null}

      {/* ------------------------------------------------- os modelos */}
      <section className="mt-10">
        <div className="mb-4 flex flex-wrap items-baseline justify-between gap-2">
          <h2 className="text-[20px] font-extrabold tracking-[-0.015em] text-[var(--ks-color-text-primary)]">
            {soFavoritos ? "Seus favoritos" : nomeDoFiltro && !termo ? `Modelos ${ocasiao ? "de " : ""}${nomeDoFiltro}` : termo ? "Modelos" : `Modelos de ${tipoAtual.nome}`}
            <span className="ml-2 text-[13px] font-semibold text-[var(--ks-color-text-soft)]">
              {soFavoritos ? `${modelos.length + daComunidade.length} salvos` : filtrando ? `${modelos.length + daComunidade.length} ${termo ? "que batem com a busca" : "em todos os formatos"}` : `${formatoDoTipo.nome} · ${capas[tipoAtual.chave]?.quantos ?? 0} ${capas[tipoAtual.chave]?.quantos === 1 ? "modelo" : "modelos"}`}
            </span>
          </h2>
          <div className="flex flex-wrap gap-1.5" role="group" aria-label="Ramo dos modelos">
            {social.ligado ? (
              <button
                type="button"
                aria-pressed={soFavoritos}
                onClick={() => setSoFavoritos((v) => !v)}
                className={`flex h-8 items-center gap-1.5 rounded-full px-3.5 text-[12.5px] font-bold transition ${soFavoritos ? "bg-[#E11D48] text-white" : "bg-[#FFE4EC] text-[#BE123C] hover:bg-[#FFD3DF]"}`}
                data-so-favoritos
              >
                <Heart size={14} strokeWidth={2.6} className={soFavoritos ? "fill-white" : ""} /> Favoritos{social.favoritos.size ? ` · ${social.favoritos.size}` : ""}
              </button>
            ) : null}
            {(["todos", ...(Object.keys(RAMOS_DOS_MODELOS) as RamoDoModelo[])] as const).map((chave) => (
              <button
                key={chave}
                type="button"
                data-ramo={chave}
                aria-pressed={!soFavoritos && ramo === chave}
                onClick={() => {
                  setSoFavoritos(false);
                  setRamo(chave);
                }}
                className={`h-8 rounded-full px-3.5 text-[12.5px] font-bold transition ${!soFavoritos && ramo === chave ? "bg-[#7C3AED] text-white" : "bg-[var(--ks-color-bg-inset)] text-[var(--ks-color-text-secondary)] hover:bg-[#EFEAFD] hover:text-[#7C3AED]"}`}
              >
                {chave === "todos" ? "Todos os ramos" : RAMOS_DOS_MODELOS[chave]}
              </button>
            ))}
          </div>
        </div>
        {/* A galeria fluida (como a do Canva): só as fotos, coladas; o nome aparece ao passar o mouse. */}
        {/* Ocasião e cor: um toque filtra os modelos de todos os formatos. */}
        {!soFavoritos ? (
          <div className="-mt-1 mb-5 flex flex-wrap items-center gap-1.5" data-filtros-de-etiqueta>
            {OCASIOES.filter((o) => porEtiqueta.ocasioes.get(o.chave)).map((o) => (
              <button
                key={o.chave}
                type="button"
                aria-pressed={ocasiao === o.chave}
                onClick={() => setOcasiao((atual) => (atual === o.chave ? null : o.chave))}
                className={`h-8 rounded-full border px-3 text-[12.5px] font-semibold transition ${ocasiao === o.chave ? "border-[#1F1B2E] bg-[#1F1B2E] text-white" : "border-[var(--ks-color-border-subtle)] bg-white text-[var(--ks-color-text-secondary)] hover:border-[#C4B5FD] hover:text-[#5B21B6]"}`}
                data-ocasiao={o.chave}
              >
                {o.nome}
              </button>
            ))}
            <span className="mx-1.5 h-6 w-px bg-[var(--ks-color-border-subtle)]" />
            {CORES_NOMEADAS.filter((c) => porEtiqueta.cores.get(c.chave)).map((c) => (
              <button
                key={c.chave}
                type="button"
                title={c.nome}
                aria-label={`Modelos em ${c.nome.toLowerCase()}`}
                aria-pressed={cor === c.chave}
                onClick={() => setCor((atual) => (atual === c.chave ? null : c.chave))}
                className={`h-7 w-7 rounded-full ring-1 ring-black/10 transition hover:scale-110 ${cor === c.chave ? "ring-[3px] ring-[#1F1B2E] ring-offset-2" : ""}`}
                style={{ background: c.amostra }}
                data-cor={c.chave}
              />
            ))}
            {ocasiao || cor ? (
              <button
                type="button"
                onClick={() => {
                  setOcasiao(null);
                  setCor(null);
                }}
                className="ml-1 flex h-8 items-center gap-1 rounded-full px-2.5 text-[12.5px] font-semibold text-[var(--ks-color-text-soft)] hover:text-[#1F1B2E]"
              >
                <X size={14} /> Limpar
              </button>
            ) : null}
          </div>
        ) : null}
        {/* Numa categoria todos têm o mesmo formato (grade justa); na busca os formatos se misturam (colunas fluidas). */}
        <div className={misturado ? "gap-3" : "grid gap-3"} style={misturado ? { columnWidth: "230px" } : { gridTemplateColumns: `repeat(auto-fill, minmax(${colunas(proporcaoDoTipo(tipoAtual))}px, 1fr))` }} data-modelos>
          {!misturado ? (
            <button type="button" onClick={() => criarEmBranco(formatoDoTipo)} className={`group block w-full break-inside-avoid ${misturado ? "mb-3" : ""}`} title={`Começar do zero · ${formatoDoTipo.nome}`}>
              <div className="flex items-center justify-center rounded-[10px] bg-[var(--ks-color-bg-inset)] transition group-hover:bg-[#EFEAFD]" style={{ aspectRatio: `${formatoDoTipo.largura} / ${formatoDoTipo.altura}` }}>
                <span className="flex flex-col items-center gap-2 text-[#7C3AED]">
                  <span className="flex h-11 w-11 items-center justify-center rounded-full bg-[#7C3AED] text-white transition group-hover:scale-105">
                    <Plus size={22} strokeWidth={2.6} />
                  </span>
                  <span className="text-[13px] font-bold">Em branco</span>
                </span>
              </div>
            </button>
          ) : null}
          {modelos.map(({ modelo, tipo: t, doc }) => (
            <button
              key={`${t.chave}-${modelo.chave}`}
              type="button"
              data-modelo={modelo.chave}
              title={modelo.nome}
              onClick={() => setPrevia({ tipo: t, casa: { modelo, doc } })}
              className={`group relative block w-full break-inside-avoid overflow-hidden rounded-[10px] bg-[var(--ks-color-bg-inset)] text-left ${misturado ? "mb-3" : ""}`}
            >
              <FotoDoModelo tipo={t} modelo={modelo} doc={doc} folheia className="transition duration-300 group-hover:scale-[1.02]" />
              <SeloDoModelo modelo={modelo} />
              <span className="pointer-events-none absolute inset-x-0 bottom-0 translate-y-1 bg-[linear-gradient(0deg,rgba(15,10,30,0.8),rgba(15,10,30,0))] px-3 pb-2.5 pt-9 opacity-0 transition group-hover:translate-y-0 group-hover:opacity-100">
                <span className="block text-[13px] font-bold leading-tight text-white" data-nome-do-modelo>
                  {modelo.nome}
                </span>
                <span className="mt-0.5 block text-[11.5px] leading-tight text-white/75">{termo ? `${t.nome} · ${modelo.descricao}` : modelo.descricao}</span>
              </span>
            </button>
          ))}
          {daComunidade.map(({ c, tipo: t }) => (
            <button
              key={c.id}
              type="button"
              data-modelo={`comunidade-${c.id}`}
              data-modelo-da-comunidade={c.id}
              title={`${c.nome} · por ${c.autor_nome}`}
              onClick={() => setPrevia({ tipo: t, comunidade: c })}
              className={`group relative block w-full break-inside-avoid overflow-hidden rounded-[10px] bg-[var(--ks-color-bg-inset)] text-left ${misturado ? "mb-3" : ""}`}
            >
              {c.miniatura ? (
                /* eslint-disable-next-line @next/next/no-img-element */
                <img src={c.miniatura} alt={c.nome} width={c.largura} height={c.altura} loading="lazy" decoding="async" className="block h-auto w-full transition duration-300 group-hover:scale-[1.02]" />
              ) : (
                <span className="flex w-full items-center justify-center text-[12px] text-[var(--ks-color-text-soft)]" style={{ aspectRatio: `${c.largura} / ${c.altura}` }}>
                  Sem prévia
                </span>
              )}
              <span className="pointer-events-none absolute right-2 top-2 flex items-center gap-1 rounded-full bg-[#140B2B]/80 px-2 py-[3px] text-[10.5px] font-bold text-white backdrop-blur-sm">
                <Users size={11} strokeWidth={2.6} /> Comunidade
              </span>
              <span className="pointer-events-none absolute bottom-2 left-2 transition group-hover:opacity-0">
                <AvatarDoAutor nome={c.autor_nome} src={c.autor_avatar} tamanho={28} />
              </span>
              <span className="pointer-events-none absolute inset-x-0 bottom-0 translate-y-1 bg-[linear-gradient(0deg,rgba(15,10,30,0.8),rgba(15,10,30,0))] px-3 pb-2.5 pt-9 opacity-0 transition group-hover:translate-y-0 group-hover:opacity-100">
                <span className="block text-[13px] font-bold leading-tight text-white">{c.nome}</span>
                <span className="mt-0.5 block text-[11.5px] leading-tight text-white/75">
                  por {c.autor_nome}
                  {termo ? ` · ${t.nome}` : ""}
                </span>
              </span>
            </button>
          ))}
        </div>
        {misturado && !modelos.length && !daComunidade.length ? <p className="py-4 text-[13.5px] text-[var(--ks-color-text-soft)]">{soFavoritos ? "Nenhum favorito ainda. Abra um modelo e toque no coração para guardar aqui." : termo ? "Nenhum modelo com esse nome." : "Nenhum modelo com essa combinação. Tire um dos filtros."}</p> : null}
      </section>

      {/* ------------------------------------------------- criar um design */}
      {janela ? (
        <div className="fixed inset-0 z-[70] flex items-center justify-center bg-[#140B2B]/45 p-3 backdrop-blur-[2px] md:p-8" onMouseDown={(e) => e.target === e.currentTarget && setJanela(null)} role="dialog" aria-modal="true" aria-label="Criar um design" data-janela-criar>
          <div className="relative flex h-full max-h-[860px] w-full max-w-[1380px] overflow-hidden rounded-[24px] bg-white shadow-[0_30px_80px_-20px_rgba(20,11,43,0.5)]">
            <aside className="hidden w-[270px] shrink-0 flex-col border-r border-[#EEEBF5] p-6 md:flex">
              <h2 className="mb-5 text-[26px] font-extrabold tracking-[-0.02em] text-[#1F1B2E]">Criar um design</h2>
              {GRUPOS_DA_JANELA.map(({ chave, nome, cor, Icone }) => (
                <button
                  key={chave}
                  type="button"
                  data-grupo-da-janela={chave}
                  onClick={() => (setBuscaDaJanela(""), setJanela(chave))}
                  className={`mb-1 flex h-11 items-center gap-3 rounded-[12px] px-3 text-left text-[14px] font-semibold transition ${janela === chave && !termoDaJanela ? "bg-[#EFEAFD] text-[#1F1B2E]" : "text-[#4B4760] hover:bg-[#F6F4FB]"}`}
                >
                  <span className="flex h-7 w-7 items-center justify-center rounded-[8px] text-white" style={{ background: cor }}>
                    <Icone size={16} />
                  </span>
                  {nome}
                </button>
              ))}
            </aside>
            <div className="flex min-w-0 flex-1 flex-col">
              <div className="flex items-center gap-3 border-b border-[#EEEBF5] p-4 md:p-5">
                <label className="flex h-12 flex-1 items-center gap-3 rounded-[12px] border-2 border-[#C4B5FD] px-4 text-[#8A849E] focus-within:border-[#7C3AED]">
                  <Search size={18} />
                  <input autoFocus value={buscaDaJanela} onChange={(e) => setBuscaDaJanela(e.target.value)} placeholder="O que você gostaria de criar?" className="h-full min-w-0 flex-1 bg-transparent text-[14.5px] text-[#1F1B2E] outline-none" />
                </label>
                <button type="button" aria-label="Fechar" onClick={() => setJanela(null)} className="flex h-11 w-11 shrink-0 items-center justify-center rounded-full bg-[#F1EFF6] text-[#4B4760] hover:bg-[#E6E1F2]">
                  <X size={20} />
                </button>
              </div>
              {/* No celular, os grupos viram fichas em cima. */}
              <div className="flex gap-1.5 overflow-x-auto px-4 pt-3 md:hidden">
                {GRUPOS_DA_JANELA.map(({ chave, nome }) => (
                  <button key={chave} type="button" onClick={() => (setBuscaDaJanela(""), setJanela(chave))} className={`h-8 shrink-0 rounded-full px-3 text-[12px] font-bold ${janela === chave ? "bg-[#7C3AED] text-white" : "bg-[#F1EFF6] text-[#4B4760]"}`}>
                    {nome}
                  </button>
                ))}
              </div>
              <div className="min-h-0 flex-1 overflow-y-auto p-4 md:p-6">
                {janela === "personalizado" && !termoDaJanela ? (
                  <div className="max-w-[460px]">
                    <h3 className="mb-1 text-[18px] font-extrabold text-[#1F1B2E]">Tamanho personalizado</h3>
                    <p className="mb-4 text-[13px] text-[#6B6584]">Em pixels para a tela; em centímetros, sai em 300 ppp para imprimir.</p>
                    <div className="mb-3 flex w-fit rounded-[10px] bg-[#F1EFF6] p-1">
                      {(["px", "cm"] as const).map((u) => (
                        <button key={u} type="button" onClick={() => (setUnidade(u), setLivre(u === "px" ? { largura: 1080, altura: 1080 } : { largura: 20, altura: 9.5 }))} className={`h-8 rounded-[8px] px-4 text-[12.5px] font-bold ${unidade === u ? "bg-white text-[#5B21B6] shadow" : "text-[#6B6584]"}`}>
                          {u}
                        </button>
                      ))}
                    </div>
                    <div className="flex items-center gap-2">
                      <CampoDecimal valor={livre.largura} aoMudar={(v) => setLivre((l) => ({ ...l, largura: unidade === "px" ? Math.round(v) : v }))} casas={unidade === "px" ? 0 : 1} aria-label="Largura" className="h-11 w-32 rounded-xl border border-[#E6E3EE] bg-white px-3 text-[14px] font-semibold outline-none focus:border-[#7C3AED]" />
                      <span className="text-[#8A849E]">×</span>
                      <CampoDecimal valor={livre.altura} aoMudar={(v) => setLivre((l) => ({ ...l, altura: unidade === "px" ? Math.round(v) : v }))} casas={unidade === "px" ? 0 : 1} aria-label="Altura" className="h-11 w-32 rounded-xl border border-[#E6E3EE] bg-white px-3 text-[14px] font-semibold outline-none focus:border-[#7C3AED]" />
                      <span className="text-[13px] font-semibold text-[#6B6584]">{unidade}</span>
                    </div>
                    {unidade === "cm" ? <p className="mt-2 text-[12px] text-[#8A849E]">Fica com {livreEmPx.largura} × {livreEmPx.altura} px.</p> : null}
                    {!livreValido ? <p className="mt-2 text-[12px] font-semibold text-[var(--ks-color-status-danger)]">Cada lado entre {MINIMO_DO_LADO} e {LIMITE_DO_LADO} px.</p> : null}
                    <button type="button" disabled={!livreValido} onClick={criarPersonalizado} className="mt-4 flex h-11 items-center gap-2 rounded-[10px] bg-[#7C3AED] px-5 text-[13.5px] font-bold text-white hover:bg-[#6D28D9] disabled:opacity-50">
                      <Plus size={16} strokeWidth={2.6} /> Criar design
                    </button>
                  </div>
                ) : (
                  <>
                    <h3 className="mb-4 text-[18px] font-extrabold text-[#1F1B2E]">{termoDaJanela ? "Tamanhos" : GRUPOS_DA_JANELA.find((g) => g.chave === janela)?.nome}</h3>
                    {tamanhosDaJanela.length ? (
                      <div className="grid grid-cols-2 gap-x-4 gap-y-5 sm:grid-cols-3 xl:grid-cols-4">
                        {tamanhosDaJanela.map((f) => (
                          <CartaoDoTamanho key={f.chave} formato={f} aoEscolher={() => criarEmBranco(f)} />
                        ))}
                      </div>
                    ) : (
                      <p className="text-[13.5px] text-[#8A849E]">Nenhum tamanho com esse nome. Use o tamanho personalizado.</p>
                    )}
                  </>
                )}
              </div>
            </div>
          </div>
        </div>
      ) : null}

      {/* ------------------------------------------------- a prévia do modelo (como a do Canva) */}
      {previa ? (
        <div className="fixed inset-0 z-[70] flex items-center justify-center bg-[#140B2B]/55 p-3 backdrop-blur-[2px] md:p-8" onMouseDown={(e) => e.target === e.currentTarget && !usando && setPrevia(null)} role="dialog" aria-modal="true" aria-label="Prévia do modelo" data-previa-do-modelo>
          <div className="relative flex max-h-full w-full max-w-[1180px] flex-col overflow-hidden rounded-[24px] bg-white shadow-[0_30px_80px_-20px_rgba(20,11,43,0.5)] md:flex-row">
            <div className="flex min-h-[320px] flex-1 items-center justify-center bg-[#F4F2F9] p-6 md:p-10">
              {previa.casa && miniaturaDoModelo(previa.tipo.chave, previa.casa.modelo.chave, previa.casa.doc)?.video ? (
                /* Modelo animado: o vídeo dele tocando, como vai sair. */
                (() => {
                  const g = miniaturaDoModelo(previa.tipo.chave, previa.casa!.modelo.chave, previa.casa!.doc)!;
                  const alto = Math.min(62, (g.altura / g.largura) * 100);
                  return (
                    <span className="block overflow-hidden rounded-[8px] shadow-[0_18px_40px_-16px_rgba(20,11,43,0.45)]" style={{ width: `min(100%, ${(62 * g.largura) / g.altura}vh)`, maxHeight: `${alto}vh` }} data-video-da-previa>
                      <VideoQueToca grande src={g.video!.src} capa={g.src} largura={g.largura} altura={g.altura} nome={previa.casa!.modelo.nome} duracao={g.video!.duracao} />
                    </span>
                  );
                })()
              ) : previa.casa && miniaturaDoModelo(previa.tipo.chave, previa.casa.modelo.chave, previa.casa.doc) ? (
                /* As fotos prontas do modelo (as mesmas da galeria): nada de montar peça por peça antes de usar. */
                <GaleriaDaPrevia fotos={fotosDasPaginas(miniaturaDoModelo(previa.tipo.chave, previa.casa.modelo.chave, previa.casa.doc)!)} nome={previa.casa.modelo.nome} />
              ) : previa.casa ? (
                <div className="pointer-events-none h-[58vh] w-full select-none">
                  <ArteQueCabe doc={previa.casa.doc} />
                </div>
              ) : previa.comunidade?.miniatura ? (
                /* eslint-disable-next-line @next/next/no-img-element */
                <img src={previa.comunidade.miniatura} alt={previa.comunidade.nome} draggable={false} className="max-h-[62vh] max-w-full select-none rounded-[8px] object-contain shadow-[0_18px_40px_-16px_rgba(20,11,43,0.45)]" />
              ) : (
                <span className="text-[13px] text-[#8A849E]">Sem prévia</span>
              )}
            </div>
            <div className="flex w-full shrink-0 flex-col p-6 md:w-[360px] md:p-7">
              <div className="flex items-start justify-between gap-3">
                <h2 className="text-[22px] font-extrabold leading-tight tracking-[-0.02em] text-[#1F1B2E]">{previa.casa ? previa.casa.modelo.nome : previa.comunidade?.nome}</h2>
                <button type="button" aria-label="Fechar" disabled={usando} onClick={() => setPrevia(null)} className="flex h-9 w-9 shrink-0 items-center justify-center rounded-full bg-[#F1EFF6] text-[#4B4760] hover:bg-[#E6E1F2]">
                  <X size={18} />
                </button>
              </div>
              <div className="mt-4 flex items-center gap-3" data-autor-do-modelo>
                {previa.casa ? (
                  <>
                    <AvatarDoAutor nome="Ktalogu" casa tamanho={40} />
                    <div className="min-w-0">
                      <p className="truncate text-[14px] font-bold text-[#1F1B2E]">por Ktalogu</p>
                      <p className="truncate text-[12px] text-[#8A849E]">Modelo oficial do KtaloguDesign</p>
                    </div>
                  </>
                ) : (
                  <>
                    <button
                      type="button"
                      onClick={() => setPerfil({ id: previa.comunidade!.user_id, nome: previa.comunidade!.autor_nome, avatar: previa.comunidade!.autor_avatar, loja: previa.comunidade!.autor_loja })}
                      className="group flex min-w-0 flex-1 items-center gap-3 rounded-[12px] text-left"
                      title="Ver o perfil e todos os modelos"
                      data-abrir-perfil
                    >
                      <AvatarDoAutor nome={previa.comunidade!.autor_nome} src={previa.comunidade!.autor_avatar} tamanho={40} />
                      <span className="min-w-0">
                        <span className="block truncate text-[14px] font-bold text-[#1F1B2E] group-hover:text-[#7C3AED] group-hover:underline">por {previa.comunidade!.autor_nome}</span>
                        <span className="block truncate text-[12px] text-[#8A849E]">{previa.comunidade!.autor_loja ? `${previa.comunidade!.autor_loja} · comunidade Ktalogu` : "Comunidade Ktalogu"}</span>
                      </span>
                    </button>
                    {social.ligado && previa.comunidade!.user_id !== dono ? <BotaoSeguir autor={previa.comunidade!.user_id} /> : null}
                  </>
                )}
              </div>
              <div className="mt-5 flex flex-wrap gap-1.5">
                <span className="rounded-full bg-[#EFEAFD] px-3 py-1 text-[12px] font-bold text-[#5B21B6]">{previa.tipo.nome}</span>
                {(() => {
                  const ramoDoModelo = previa.casa ? previa.casa.modelo.ramo : previa.comunidade?.ramo;
                  return ramoDoModelo ? <span className="rounded-full bg-[#F1EFF6] px-3 py-1 text-[12px] font-bold text-[#4B4760]">{RAMOS_DOS_MODELOS[ramoDoModelo]}</span> : null;
                })()}
                {formatoDaChave(previa.tipo.formato)?.nome !== previa.tipo.nome ? <span className="rounded-full bg-[#F1EFF6] px-3 py-1 text-[12px] font-bold text-[#4B4760]">{formatoDaChave(previa.tipo.formato)?.nome}</span> : null}
                {(() => {
                  const quantas = previa.casa ? previa.casa.modelo.paginas : previa.comunidade?.paginas;
                  return quantas && quantas > 1 ? <span className="rounded-full bg-[#F1EFF6] px-3 py-1 text-[12px] font-bold text-[#4B4760]">{quantas} páginas</span> : null;
                })()}
              </div>
              <p className="mt-4 text-[13.5px] leading-relaxed text-[#4B4760]">{previa.casa ? previa.casa.modelo.descricao : previa.comunidade?.descricao || "Modelo feito por um assinante da Ktalogu."}</p>
              {previa.comunidade?.usos ? <p className="mt-2 text-[12px] text-[#8A849E]">Usado {previa.comunidade.usos} {previa.comunidade.usos === 1 ? "vez" : "vezes"}</p> : null}
              {social.ligado ? <AcoesDoModelo key={previa.casa ? chaveDaCasa(previa.casa.modelo.chave) : previa.comunidade!.id} chave={previa.casa ? chaveDaCasa(previa.casa.modelo.chave) : previa.comunidade!.id} social={social} aoMudar={setSocial} /> : null}
              <div className="mt-auto pt-6">
                <button type="button" data-usar-modelo disabled={usando} onClick={() => void usarPrevia()} className="flex h-12 w-full items-center justify-center gap-2 rounded-[12px] bg-[#7C3AED] text-[14.5px] font-bold text-white transition hover:bg-[#6D28D9] disabled:opacity-70">
                  {usando ? <Loader2 size={17} className="animate-spin" /> : null}
                  {usando ? "Abrindo…" : "Usar este modelo"}
                </button>
                <p className="mt-2 text-center text-[11.5px] text-[#8A849E]">{previa.casa ? "Abre com a cor, o logo e um produto da sua loja." : "Abre uma cópia sua: o original não muda."}</p>
              </div>
            </div>
          </div>
        </div>
      ) : null}

      {perfil ? (
        <PerfilDoAutor
          autor={perfil}
          modelos={comunidade.filter((c) => c.user_id === perfil.id)}
          podeSeguir={social.ligado && perfil.id !== dono}
          aoFechar={() => setPerfil(null)}
          aoAbrir={(c) => {
            setPerfil(null);
            setPrevia({ tipo: TIPOS_DE_MODELO.find((t) => t.chave === c.tipo) || TIPOS_DE_MODELO[0], comunidade: c });
          }}
        />
      ) : null}

      {/* ------------------------------------------------- publicar na comunidade */}
      {publicacao ? (
        <div className="fixed inset-0 z-[70] flex items-center justify-center bg-[#140B2B]/55 p-3 backdrop-blur-[2px] md:p-8" role="dialog" aria-modal="true" aria-label="Publicar na comunidade" data-publicacao>
          <div className="flex max-h-full w-full max-w-[920px] flex-col overflow-hidden rounded-[24px] bg-white shadow-[0_30px_80px_-20px_rgba(20,11,43,0.5)] md:flex-row">
            <div className="flex min-h-[240px] flex-1 items-center justify-center bg-[#F4F2F9] p-6">
              <div className="h-[44vh] w-full">{publicacao.arte.documento?.pecas ? <ArteQueCabe doc={publicacao.arte.documento} /> : null}</div>
            </div>
            <div className="flex w-full shrink-0 flex-col overflow-y-auto p-6 md:w-[400px]">
              <h2 className="text-[20px] font-extrabold tracking-[-0.02em] text-[#1F1B2E]">Publicar na comunidade</h2>
              <p className="mt-1 text-[13px] leading-snug text-[#6B6584]">A sua arte vira um modelo para as outras empresas da Ktalogu, com o seu nome e a sua foto. Antes de entrar, a equipe Ktalogu confere.</p>
              {(() => {
                const tipoDaPublicacao = publicacao.arte.documento ? tipoDaArte({ ...publicacao.arte.documento, largura: publicacao.arte.largura, altura: publicacao.arte.altura }) : null;
                if (!tipoDaPublicacao) {
                  return <p className="mt-4 rounded-[12px] bg-[#FFF7E6] p-3 text-[12.5px] text-[#8A5300]">Esse tamanho ainda não tem categoria de modelos. Dá para publicar post, carrossel, story, banner da loja e caneca.</p>;
                }
                return (
                  <>
                    <p className="mt-4 text-[12px] font-bold uppercase tracking-[0.12em] text-[#8A849E]">Categoria</p>
                    <p className="mt-1 w-fit rounded-full bg-[#EFEAFD] px-3 py-1 text-[12.5px] font-bold text-[#5B21B6]" data-tipo-da-publicacao={tipoDaPublicacao}>{NOMES_DOS_TIPOS[tipoDaPublicacao]}</p>
                    <label className="mt-4 block text-[12px] font-bold uppercase tracking-[0.12em] text-[#8A849E]">
                      Nome do modelo
                      <input value={publicacao.nome} onChange={(e) => setPublicacao({ ...publicacao, nome: e.target.value.slice(0, 80) })} className="mt-1 h-11 w-full rounded-[10px] border border-[#E6E3EE] px-3 text-[14px] font-semibold normal-case tracking-normal text-[#1F1B2E] outline-none focus:border-[#7C3AED]" data-nome-da-publicacao />
                    </label>
                    <label className="mt-3 block text-[12px] font-bold uppercase tracking-[0.12em] text-[#8A849E]">
                      Descrição (opcional)
                      <textarea value={publicacao.descricao} onChange={(e) => setPublicacao({ ...publicacao, descricao: e.target.value.slice(0, 200) })} rows={2} placeholder="Ex.: caneca de Dia dos Pais com foto" className="mt-1 w-full rounded-[10px] border border-[#E6E3EE] p-3 text-[13.5px] normal-case tracking-normal text-[#1F1B2E] outline-none focus:border-[#7C3AED]" />
                    </label>
                    <p className="mt-3 text-[12px] font-bold uppercase tracking-[0.12em] text-[#8A849E]">Ramo</p>
                    <div className="mt-1 flex flex-wrap gap-1.5">
                      {([null, ...(Object.keys(RAMOS_DOS_MODELOS) as RamoDoModelo[])] as const).map((r) => (
                        <button key={r || "nenhum"} type="button" onClick={() => setPublicacao({ ...publicacao, ramo: r })} className={`h-8 rounded-full px-3 text-[12px] font-bold ${publicacao.ramo === r ? "bg-[#7C3AED] text-white" : "bg-[#F1EFF6] text-[#4B4760]"}`}>
                          {r ? RAMOS_DOS_MODELOS[r] : "Qualquer ramo"}
                        </button>
                      ))}
                    </div>
                    <label className="mt-4 flex cursor-pointer items-start gap-2.5 rounded-[12px] bg-[#F6F4FB] p-3 text-[12.5px] leading-snug text-[#4B4760]">
                      <input type="checkbox" checked={publicacao.aceite} onChange={(e) => setPublicacao({ ...publicacao, aceite: e.target.checked })} className="mt-0.5 h-4 w-4 shrink-0 accent-[#7C3AED]" data-aceite-da-publicacao />
                      As fotos e os textos são meus ou de uso livre, e autorizo a Ktalogu a mostrar esta arte como modelo para outras empresas, com o meu nome.
                    </label>
                  </>
                );
              })()}
              {publicacao.erro ? <p className="mt-3 text-[12.5px] font-semibold text-[var(--ks-color-status-danger)]">{publicacao.erro}</p> : null}
              <div className="mt-auto flex justify-end gap-2 pt-5">
                <button type="button" disabled={publicacao.enviando} onClick={() => setPublicacao(null)} className="h-11 rounded-[10px] px-4 text-[13.5px] font-semibold text-[#4B4760] hover:bg-[#F1EFF6]">
                  Cancelar
                </button>
                <button
                  type="button"
                  data-enviar-comunidade
                  disabled={publicacao.enviando || !publicacao.aceite || !publicacao.nome.trim() || !(publicacao.arte.documento && tipoDaArte({ ...publicacao.arte.documento, largura: publicacao.arte.largura, altura: publicacao.arte.altura }))}
                  onClick={() => void publicar()}
                  className="flex h-11 items-center gap-2 rounded-[10px] bg-[#7C3AED] px-5 text-[13.5px] font-bold text-white hover:bg-[#6D28D9] disabled:opacity-50"
                >
                  {publicacao.enviando ? <Loader2 size={16} className="animate-spin" /> : <Send size={16} />}
                  {publicacao.enviando ? "Enviando…" : "Enviar para análise"}
                </button>
              </div>
            </div>
          </div>
        </div>
      ) : null}

      {aberta ? (
        <EditorDeBanner
          key={aberta.arte?.id || `${aberta.largura}x${aberta.altura}-${aberta.modelo}`}
          largura={aberta.largura}
          altura={aberta.altura}
          formatoInicial={aberta.formato}
          arteInicial={aberta.arte}
          modeloInicial={aberta.modelo}
          nomeInicial={aberta.arte ? null : nomeDaNova}
          vinculoInicial={aberta.arte ? null : vinculoDaNova}
          aoFechar={() => {
            setAberta(null);
            setNomeDaNova(null);
            setVinculoDaNova(null);
            if (dono) void carregar(dono);
          }}
        />
      ) : null}
      <CaixaDeConfirmacao pedido={confirmacao} aoFechar={() => setConfirmacao(null)} />
    </div>
  );
}

/* O cartão de um tamanho na janela "Criar um design": o formato desenhado (com a marca da rede), o nome e a medida. */
function CartaoDoTamanho({ formato: f, aoEscolher }: { formato: FormatoDaArte; aoEscolher: () => void }) {
  const caixa = 120;
  const escala = caixa / Math.max(f.largura, f.altura);
  const w = Math.max(28, f.largura * escala);
  const h = Math.max(28, f.altura * escala);
  const rede = REDE_DO_FORMATO[f.chave];
  const cor = GRUPOS_DA_JANELA.find((g) => g.chave === f.grupo)?.cor || "#7C3AED";
  return (
    <button type="button" onClick={aoEscolher} data-tamanho={f.chave} className="group text-left">
      <span className="flex h-[168px] items-center justify-center rounded-[14px] bg-[#F4F2F9] transition group-hover:bg-[#EDE7FF]">
        <span className="relative rounded-[8px] bg-white p-1.5 shadow-[0_10px_24px_-12px_rgba(20,11,43,0.45)] transition group-hover:-translate-y-1" style={{ width: w + 12, height: h + 12 }}>
          <span className="relative block h-full w-full overflow-hidden rounded-[5px]" style={{ background: `linear-gradient(135deg, ${cor}, ${cor}B3)` }}>
            <span className="absolute left-[12%] top-[16%] h-[9%] min-h-[4px] w-[46%] rounded-full bg-white/90" />
            <span className="absolute left-[12%] top-[32%] h-[7%] min-h-[3px] w-[30%] rounded-full bg-white/60" />
            <span className="absolute bottom-[12%] right-[10%] aspect-square w-[26%] rounded-full bg-white/30" />
          </span>
          {rede ? <svg viewBox="0 0 24 24" className="absolute -left-2.5 -top-2.5 h-6 w-6 drop-shadow" aria-hidden="true" dangerouslySetInnerHTML={{ __html: ICONES[rede]?.desenho || "" }} /> : null}
        </span>
      </span>
      <span className="mt-2 block text-[13.5px] font-semibold text-[#1F1B2E] group-hover:text-[#7C3AED]">
        {f.nome}
        {f.detalhe ? <span className="font-normal text-[#6B6584]"> ({f.detalhe})</span> : null}
      </span>
      <span className="block text-[12px] tabular-nums text-[#8A849E]">{f.dpi ? medidaDeImpressao(f.largura, f.altura, f.dpi) : `${f.largura} × ${f.altura} px`}</span>
    </button>
  );
}
