"use client";

import Image from "next/image";
import { useCallback, useEffect, useMemo, useRef, useState, type ReactNode } from "react";
import {
  ArrowLeft,
  CalendarClock,
  Check,
  CheckCheck,
  CheckCircle2,
  Clock3,
  CreditCard,
  ExternalLink,
  FileText,
  Flame,
  Grid2x2,
  Info,
  Eye,
  LogIn,
  MessageCircle,
  MessageSquarePlus,
  Package,
  Paperclip,
  Pencil,
  Reply,
  RotateCcw,
  Search,
  Send,
  Settings,
  ShoppingCart,
  Smile,
  StickyNote,
  Store,
  Tag,
  Ticket,
  Trash2,
  User,
  UserPlus,
  Users,
  X,
  Zap,
} from "lucide-react";
import AtivarAvisos from "@/components/chats/ativar-avisos";
import ChaveDoAtendimento from "@/components/chats/chave-do-atendimento";
import ConfiguracoesDoChats from "@/components/chats/configuracoes";
import PainelDeEmojis from "@/components/chats/emojis";
import EditorDeRespostas, { type RespostaPronta } from "@/components/chats/respostas-prontas";
import { prepararSom, tocarSomDeMensagem } from "@/components/chats/som";
import { getSupabaseClient } from "@/lib/supabase";

/*
  Chats Ktalogu.

  Uma tela so para conversar: o trilho escolhe com quem (a loja online ou a
  equipe), a lista escolhe qual conversa, o meio e a conversa e a direita e o
  contexto — o pedido, os arquivos que ja passaram por ali, os links.

  Tres decisoes mudam a sensacao de uso:

  - o que ja foi visto fica guardado, entao trocar de aba ou voltar para uma
    conversa e instantaneo: a tela antiga aparece na hora e so depois se
    atualiza sozinha;
  - a mensagem enviada aparece no mesmo instante, com um relogio no lugar da
    confirmacao ate o servidor responder;
  - mensagem que chega em outra conversa vira aviso no canto, com som, como em
    qualquer aplicativo de mensagem.
*/

type Conversa = {
  id: string;
  kind: string;
  nome: string;
  detalhe: string;
  avatar: string | null;
  orderId: string | null;
  quoteId: string | null;
  clientId: string | null;
  artStatus: string | null;
  ultimaMensagemEm: string | null;
  ultimaMensagem: string | null;
  ultimoAutor: string | null;
  encerrada: boolean;
  visitas: number;
  estagio: string | null;
  responsavel: string | null;
  responsavelNome: string | null;
  etiquetas: string[];
  retomarEm?: string | null;
  retomarNota?: string | null;
  /* Só aparece na busca: o pedaço da mensagem onde o termo foi achado. */
  trecho?: string | null;
  naoLidas: number;
};

/* O produto ou o cupom que viaja dentro da mensagem. */
type Cartao = {
  tipo: "produto" | "cupom";
  titulo?: string | null;
  subtitulo?: string | null;
  imagem?: string | null;
  preco?: number | null;
  link?: string | null;
  codigo?: string | null;
};

type Mensagem = {
  id: string;
  author_type: string;
  author_id: string | null;
  sender_name: string | null;
  body: string | null;
  card?: Cartao | null;
  attachment_url: string | null;
  attachment_name: string | null;
  attachment_kind: string | null;
  reply_to?: string | null;
  respondendo?: { id: string; quem: string; resumo: string } | null;
  edited_at?: string | null;
  internal?: boolean;
  is_art: boolean;
  art_status: string | null;
  art_decision_note: string | null;
  created_at: string;
  deleted_at: string | null;
  lida_pelo_outro: string | null;
  /* So existe na tela: a mensagem que ainda esta a caminho do servidor. */
  pendente?: boolean;
};

type CabecalhoDaConversa = {
  id: string;
  kind: string;
  contato: string;
  detalhe: string;
  avatar: string | null;
  orderId: string | null;
  quoteId: string | null;
  clientId: string | null;
  pedido: {
    id: string;
    number: number;
    status: string | null;
    total_amount: number | null;
    art_status: string | null;
  } | null;
  orcamento: { id: string; number: number; status: string | null; total: number | null } | null;
  podeResponder: boolean;
  encerrada: boolean;
  encerradaPor: string | null;
  encerradaEm: string | null;
  visitas: number;
  primeiraVisita: string | null;
  ultimaVisita: string | null;
  chaveDoVisitante: string | null;
  estagio: string | null;
  responsavel: string | null;
  responsavelNome: string | null;
  etiquetas: string[];
  retomarEm: string | null;
  retomarNota: string | null;
};

type Colega = { id: string; nome: string; email: string; cargo: string };

/* Quem está sentado no balcão agora. */
type Eu = {
  id: string;
  nome: string;
  email: string;
  avatar: string | null;
  papel: string;
  ehDono: boolean;
  loja: string | null;
  logoDaLoja: string | null;
  podeResponder: boolean;
  podeConfigurar: boolean;
};

type AvisoNaTela = { id: string; threadId: string; quem: string; texto: string };

type Visitante = {
  chave: string;
  nome: string | null;
  pagina: string;
  caminho: string | null;
  produtoId: string | null;
  produto: string | null;
  preco: number | null;
  imagem: string | null;
  threadId: string | null;
  clienteId: string | null;
  chegouPor: string | null;
  carrinhoItens: number;
  carrinhoTotal: number;
  voltasNoProduto: number;
  minutosNaLoja: number;
  paginasVistas: number;
  temperatura: string;
  motivo: string;
  desde: string;
  visto: string;
};

/* Um passo do caminho que a pessoa fez na loja. */
type PassoDaRota = {
  id: string;
  tipo: string;
  titulo: string | null;
  caminho: string | null;
  produtoId: string | null;
  produto: string | null;
  preco: number | null;
  imagem: string | null;
  quantidade: number | null;
  valor: number | null;
  detalhe: string | null;
  em: string;
};

type ResumoDaRota = {
  chegouPor: string | null;
  minutos: number;
  paginas: number;
  carrinhoItens: number;
  carrinhoTotal: number;
  primeiraVez: string | null;
  ultimaVez: string | null;
  ehCliente: boolean;
};

type PlacarDoDia = { entraram: number; falaram: number; compraram: number };

type Decisao = {
  titulo: string;
  texto: string;
  confirmar: string;
  tom?: "perigo" | "normal";
  aoConfirmar: () => void | Promise<void>;
};

const DIA_EM_MS = 24 * 60 * 60 * 1000;
const INTERVALO_DE_AGRUPAMENTO_MS = 5 * 60 * 1000;
const TAMANHO_MAXIMO = 8 * 1024 * 1024;
const TEMPO_DO_AVISO_MS = 7000;

const STATUS_DO_PEDIDO: Record<string, string> = {
  pending_production: "Aguardando produção",
  in_production: "Em produção",
  finished: "Pronto",
  delivered: "Entregue",
  canceled: "Cancelado",
};

const STATUS_DO_ORCAMENTO: Record<string, string> = {
  draft: "Rascunho",
  sent: "Enviado ao cliente",
  approved: "Aprovado",
  rejected: "Recusado",
  production: "Em produção",
  delivered: "Entregue",
};

const ARTE: Record<string, string> = {
  pending: "Arte aguardando o cliente",
  approved: "Arte aprovada",
  rejected: "Arte reprovada",
};

/*
  Funil dentro da propria conversa.

  Nao e um CRM a parte: e a mesma conversa carregando em que pe esta. Cinco
  estados dao conta do que acontece numa grafica — o resto e enfeite que ninguem
  mantem atualizado.
*/
const ESTAGIOS: { chave: string; rotulo: string }[] = [
  { chave: "novo", rotulo: "Novo" },
  { chave: "atendendo", rotulo: "Atendendo" },
  { chave: "negociando", rotulo: "Negociando" },
  { chave: "fechado", rotulo: "Fechado" },
  { chave: "perdido", rotulo: "Perdido" },
];

const TEMPERATURAS: Record<string, string> = {
  quente: "Quente",
  morno: "Morno",
  frio: "Frio",
};

/*
  As ações rápidas da barra "/".

  Quem atende não pode largar a conversa para procurar o preço de um produto em
  outra aba, nem decorar o código do cupom da semana. Digitando "/" tudo o que
  a loja tem chega na conversa: o produto com foto, o cupom com o código, a
  frase que já foi escrita mil vezes.
*/
type Comando = {
  chave: "produto" | "cupom" | "resposta" | "orcamento" | "cliente" | "nota" | "loja" | "encerrar";
  rotulo: string;
  dica: string;
  icone: ReactNode;
  /* Alguns comandos abrem uma lista; outros agem na hora. */
  imediato?: boolean;
};

const COMANDOS: Comando[] = [
  { chave: "produto", rotulo: "Produto", dica: "manda a foto e o preço", icone: <Package size={15} /> },
  { chave: "cupom", rotulo: "Cupom", dica: "oferece um desconto que está valendo", icone: <Ticket size={15} /> },
  { chave: "resposta", rotulo: "Resposta pronta", dica: "as frases que você salvou", icone: <Zap size={15} /> },
  {
    chave: "orcamento",
    rotulo: "Virar orçamento",
    dica: "abre um rascunho com o cliente e o produto",
    icone: <FileText size={15} />,
    imediato: true,
  },
  {
    chave: "cliente",
    rotulo: "Virar cliente",
    dica: "dá ficha para quem ainda é visitante",
    icone: <UserPlus size={15} />,
    imediato: true,
  },
  {
    chave: "nota",
    rotulo: "Recado interno",
    dica: "só a equipe lê — o cliente não vê",
    icone: <StickyNote size={15} />,
    imediato: true,
  },
  { chave: "loja", rotulo: "Link da loja", dica: "manda o endereço da vitrine", icone: <Store size={15} />, imediato: true },
  {
    chave: "encerrar",
    rotulo: "Encerrar atendimento",
    dica: "fecha esta conversa",
    icone: <CheckCircle2 size={15} />,
    imediato: true,
  },
];

/* O texto que acompanha o cupom: quem recebe precisa saber quanto vale e até
   quando, sem abrir nada. */
function condicoesDoCupom(cupom: { tipo: string; valor: number; minimo: number | null; vence: string | null }) {
  const desconto =
    cupom.tipo === "free_shipping"
      ? "Frete grátis"
      : cupom.tipo === "percent"
      ? `${String(cupom.valor).replace(".", ",")}% de desconto`
      : `${dinheiro(cupom.valor)} de desconto`;
  const minimo = cupom.minimo ? ` em compras a partir de ${dinheiro(cupom.minimo)}` : "";
  const validade = cupom.vence ? ` · vale até ${new Date(cupom.vence).toLocaleDateString("pt-BR")}` : "";
  return `${desconto}${minimo}${validade}`;
}

/* Cada passo da rota tem um icone e um jeito de ser lido em voz alta. */
function iconeDoPasso(tipo: string) {
  if (tipo === "entrou") return <LogIn size={13} />;
  if (tipo === "produto") return <Package size={13} />;
  if (tipo === "busca") return <Search size={13} />;
  if (tipo === "carrinho") return <ShoppingCart size={13} />;
  if (tipo === "checkout") return <CreditCard size={13} />;
  if (tipo === "compra") return <CheckCircle2 size={13} />;
  if (tipo === "mensagem") return <MessageCircle size={13} />;
  return <Eye size={13} />;
}

function detalheDoPasso(passo: PassoDaRota) {
  if (passo.tipo === "carrinho") {
    const quantos = Number(passo.quantidade || 1);
    return `${quantos} ${quantos === 1 ? "item" : "itens"} · ${dinheiro(passo.valor)}`;
  }
  if (passo.tipo === "checkout" || passo.tipo === "compra") return passo.valor ? dinheiro(passo.valor) : null;
  if (passo.tipo === "mensagem") return passo.titulo ? `“${passo.titulo}”` : null;
  if (passo.tipo === "entrou") return passo.detalhe ? `veio do ${passo.detalhe}` : passo.caminho;
  if (passo.tipo === "produto") return null;
  return passo.caminho;
}

function tituloDoPasso(passo: PassoDaRota) {
  if (passo.tipo === "entrou") return "Entrou na loja";
  if (passo.tipo === "produto") return passo.produto || passo.titulo || "Viu um produto";
  if (passo.tipo === "busca") return `Buscou “${passo.detalhe || passo.titulo || ""}”`;
  if (passo.tipo === "carrinho") return "Colocou no carrinho";
  if (passo.tipo === "checkout") return "Abriu o checkout";
  if (passo.tipo === "compra") return "Comprou";
  if (passo.tipo === "mensagem") return passo.detalhe || "Falou no chat";
  return passo.titulo || passo.caminho || "Outra página";
}

/*
  O rosto de quem ainda não tem rosto.

  Visitante não fez cadastro, não subiu foto e muitas vezes nem disse o nome:
  as iniciais viravam um "VI" sem sentido, e usar a foto do produto no lugar da
  pessoa confundia as duas coisas — parecia que o cliente se chamava "caneca".
  O bonequinho diz o que é: alguém, ainda sem nome.
*/
function bonequinho(tamanho = 19) {
  return <User size={tamanho} strokeWidth={2} />;
}

function iniciais(nome?: string | null) {
  const partes = String(nome || "").trim().split(/\s+/).filter(Boolean);
  if (!partes.length) return "?";
  if (partes.length === 1) return partes[0].slice(0, 2).toUpperCase();
  return (partes[0][0] + partes[partes.length - 1][0]).toUpperCase();
}

function hora(iso: string) {
  return new Date(iso).toLocaleTimeString("pt-BR", { hour: "2-digit", minute: "2-digit" });
}

function rotuloDoDia(iso: string) {
  const data = new Date(iso);
  const zerar = (valor: Date) => new Date(valor.getFullYear(), valor.getMonth(), valor.getDate()).getTime();
  const diferenca = Math.round((zerar(new Date()) - zerar(data)) / DIA_EM_MS);
  if (diferenca === 0) return "Hoje";
  if (diferenca === 1) return "Ontem";
  return data.toLocaleDateString("pt-BR", { day: "2-digit", month: "long", year: "numeric" });
}

/*
  Há quanto tempo a pessoa está esperando.

  "18:42" não cobra ninguém; "há 40 min" cobra. É a diferença entre uma lista
  que informa e uma que faz alguém responder.
*/
function esperaEmPalavras(iso?: string | null) {
  if (!iso) return "";
  const minutos = Math.floor((Date.now() - new Date(iso).getTime()) / 60000);
  if (Number.isNaN(minutos) || minutos < 1) return "agora";
  if (minutos < 60) return `há ${minutos} min`;
  const horas = Math.floor(minutos / 60);
  if (horas < 24) return `há ${horas}h`;
  const dias = Math.floor(horas / 24);
  return dias === 1 ? "há 1 dia" : `há ${dias} dias`;
}

/* Na lista, a hora so ajuda no que e de hoje; o resto quer saber o dia. */
function quandoNaLista(iso?: string | null) {
  if (!iso) return "";
  const data = new Date(iso);
  if (Number.isNaN(data.getTime())) return "";
  if (new Date().toDateString() === data.toDateString()) return hora(iso);
  if (new Date(Date.now() - DIA_EM_MS).toDateString() === data.toDateString()) return "Ontem";
  return data.toLocaleDateString("pt-BR", { day: "2-digit", month: "2-digit" });
}

function dinheiro(valor?: number | null) {
  return new Intl.NumberFormat("pt-BR", { style: "currency", currency: "BRL" }).format(Number(valor || 0));
}

/* O que o cliente cola como endereco tem que virar link clicavel — copiar e
   colar na barra do navegador e trabalho que a tela pode poupar. */
function comLinks(texto: string) {
  const partes = texto.split(/(https?:\/\/[^\s]+)/g);
  return partes.map((parte, indice) =>
    /^https?:\/\//.test(parte) ? (
      <a key={indice} href={parte} target="_blank" rel="noreferrer" className="chat-link">
        {parte}
      </a>
    ) : (
      <span key={indice}>{parte}</span>
    )
  );
}

export default function ChatsApp() {
  const [aba, setAba] = useState<"loja" | "equipe">("loja");
  /* Dentro da Loja Online sao duas perguntas: quem esta esperando resposta e
     quem esta na loja agora. A segunda fica sempre a vista — e o movimento da
     vitrine, nao adianta escondido atras de um clique. */
  const [subAba, setSubAba] = useState<"conversas" | "visitantes">("conversas");
  const [conversas, setConversas] = useState<Conversa[]>([]);
  const [naoLidasPorAba, setNaoLidasPorAba] = useState<{ loja: number; equipe: number }>({ loja: 0, equipe: 0 });
  const [busca, setBusca] = useState("");
  const [situacao, setSituacao] = useState<"abertas" | "encerradas" | "esperando">("abertas");
  /* Quantas conversas estao devendo resposta: e o numero da aba Esperando. */
  const [esperando, setEsperando] = useState(0);
  const [abertaId, setAbertaId] = useState<string | null>(null);
  const [cabecalho, setCabecalho] = useState<CabecalhoDaConversa | null>(null);
  const [mensagens, setMensagens] = useState<Mensagem[]>([]);
  const [meuId, setMeuId] = useState<string | null>(null);
  const [lojaIds, setLojaIds] = useState<string[]>([]);
  const [carregandoFio, setCarregandoFio] = useState(false);

  const [texto, setTexto] = useState("");
  const [arquivo, setArquivo] = useState<File | null>(null);
  const [comoArte, setComoArte] = useState(false);
  const [enviando, setEnviando] = useState(false);
  const [erro, setErro] = useState<string | null>(null);

  const [menuAberto, setMenuAberto] = useState<string | null>(null);
  const [fotoAberta, setFotoAberta] = useState<{ url: string; nome?: string | null } | null>(null);
  const [painelAberto, setPainelAberto] = useState(false);
  const [equipe, setEquipe] = useState<Colega[]>([]);
  const [listaDeContatos, setListaDeContatos] = useState(false);
  const [avisos, setAvisos] = useState<AvisoNaTela[]>([]);
  const [visitantes, setVisitantes] = useState<Visitante[]>([]);
  const [visitanteAberto, setVisitanteAberto] = useState<Visitante | null>(null);
  const [abordagem, setAbordagem] = useState("");
  const [abordando, setAbordando] = useState(false);
  const [dia, setDia] = useState<PlacarDoDia>({ entraram: 0, falaram: 0, compraram: 0 });
  const [rota, setRota] = useState<PassoDaRota[]>([]);
  const [resumoDaRota, setResumoDaRota] = useState<ResumoDaRota | null>(null);
  const [mudandoEstagio, setMudandoEstagio] = useState(false);

  /* Ações rápidas: a barra "/" abre a lista, e o comando escolhido vira uma
     segunda lista (produtos, cupons, frases) filtrada pelo que se digita. */
  const [atalhoAberto, setAtalhoAberto] = useState(false);
  const [comando, setComando] = useState<Comando["chave"] | null>(null);
  const [itensDoAtalho, setItensDoAtalho] = useState<Record<string, unknown>[]>([]);
  const [carregandoAtalho, setCarregandoAtalho] = useState(false);
  const [indiceDoAtalho, setIndiceDoAtalho] = useState(0);
  const [respostasSugeridas, setRespostasSugeridas] = useState(false);
  const [editandoRespostas, setEditandoRespostas] = useState(false);
  const [emojisAbertos, setEmojisAbertos] = useState(false);

  /* Quem está no balcão, as configurações e o que a conversa aberta permite. */
  const [eu, setEu] = useState<Eu | null>(null);
  const [configAberta, setConfigAberta] = useState(false);
  const [citando, setCitando] = useState<Mensagem | null>(null);
  const [editando, setEditando] = useState<Mensagem | null>(null);
  const [notaInterna, setNotaInterna] = useState(false);
  const [temMais, setTemMais] = useState(true);
  const [carregandoAntigas, setCarregandoAntigas] = useState(false);
  const [equipeDaLoja, setEquipeDaLoja] = useState<Colega[]>([]);
  const [digitando, setDigitando] = useState<string | null>(null);
  /* Pergunta de sim ou nao com a cara da casa: o alerta do navegador e feio,
     trava a pagina e nao da para escrever direito. */
  const [decisao, setDecisao] = useState<Decisao | null>(null);

  const fimDoFio = useRef<HTMLDivElement | null>(null);
  const campoDeArquivo = useRef<HTMLInputElement | null>(null);
  const abertaRef = useRef<string | null>(null);
  abertaRef.current = abertaId;
  const visitanteAbertoRef = useRef<string | null>(null);
  visitanteAbertoRef.current = visitanteAberto?.chave || null;
  /* O canal da conversa aberta, guardado para avisar que estou digitando. */
  const canalDoFio = useRef<ReturnType<ReturnType<typeof getSupabaseClient>["channel"]> | null>(null);
  const ultimoAvisoDeDigitacao = useRef(0);

  /* O que ja foi carregado nao se joga fora: e o que faz a volta ser instantanea. */
  const cacheDeConversas = useRef<Record<string, Conversa[]>>({});
  const cacheDeMensagens = useRef<Record<string, Mensagem[]>>({});
  const cacheDeCabecalhos = useRef<Record<string, CabecalhoDaConversa>>({});

  useEffect(() => {
    prepararSom();
  }, []);

  /* ---------------- lista ---------------- */

  const carregarConversas = useCallback(async () => {
    try {
      const resposta = await fetch(
        `/api/chats/threads?aba=${aba}&situacao=${situacao}&busca=${encodeURIComponent(busca)}`,
        { cache: "no-store" }
      );
      const dados = (await resposta.json().catch(() => null)) as
        | {
            conversas?: Conversa[];
            naoLidas?: { loja: number; equipe: number };
            esperando?: number;
            lojaIds?: string[];
            meuId?: string;
          }
        | null;
      const lista = Array.isArray(dados?.conversas) ? dados.conversas : [];
      if (!busca) cacheDeConversas.current[`${aba}:${situacao}`] = lista;
      setConversas(lista);
      if (dados?.naoLidas) setNaoLidasPorAba(dados.naoLidas);
      if (typeof dados?.esperando === "number") setEsperando(dados.esperando);
      if (Array.isArray(dados?.lojaIds)) {
        setLojaIds((antes) =>
          antes.join(",") === dados.lojaIds!.join(",") ? antes : (dados.lojaIds as string[])
        );
      }
      if (dados?.meuId) setMeuId((antes) => antes || dados.meuId || null);
    } catch {
      /* Fica o que ja estava na tela: sumir com a lista por causa de uma
         consulta que falhou seria pior do que mostrar algo de um minuto atras. */
    }
  }, [aba, busca, situacao]);

  useEffect(() => {
    const atraso = window.setTimeout(() => void carregarConversas(), busca ? 260 : 0);
    return () => window.clearTimeout(atraso);
  }, [carregarConversas, busca]);

  useEffect(() => {
    const relogio = window.setInterval(() => void carregarConversas(), 20000);
    return () => window.clearInterval(relogio);
  }, [carregarConversas]);

  useEffect(() => {
    if (aba !== "equipe" || equipe.length) return;
    void (async () => {
      try {
        const resposta = await fetch("/api/chats/contatos", { cache: "no-store" });
        const dados = (await resposta.json().catch(() => null)) as { equipe?: Colega[] } | null;
        setEquipe(Array.isArray(dados?.equipe) ? dados.equipe : []);
      } catch {
        setEquipe([]);
      }
    })();
  }, [aba, equipe.length]);

  const carregarVisitantes = useCallback(async () => {
    try {
      const resposta = await fetch("/api/chats/visitantes", { cache: "no-store" });
      const dados = (await resposta.json().catch(() => null)) as
        | { visitantes?: Visitante[]; dia?: PlacarDoDia }
        | null;
      const lista = Array.isArray(dados?.visitantes) ? dados.visitantes : [];
      setVisitantes(lista);
      if (dados?.dia) setDia(dados.dia);
      /* A ficha aberta acompanha a lista: se a pessoa mudou de pagina, o
         cartao do meio muda junto, sem precisar clicar de novo. */
      setVisitanteAberto((antes) => (antes ? lista.find((item) => item.chave === antes.chave) || antes : antes));
    } catch {
      /* Fica a lista anterior: presenca some sozinha na proxima volta. */
    }
  }, []);

  useEffect(() => {
    if (aba !== "loja") return;
    void carregarVisitantes();
    /* Presenca envelhece rapido: a cada 15 segundos a lista se corrige — e o
       contador da sub-aba continua certo mesmo quem estando nas conversas. */
    const relogio = window.setInterval(() => void carregarVisitantes(), 15000);
    return () => window.clearInterval(relogio);
  }, [aba, carregarVisitantes]);

  /*
    A rota da pessoa na loja.

    Vale para quem esta navegando agora e para quem ja falou: dentro da conversa
    o painel da direita mostra o mesmo caminho, porque a pergunta "por que ele
    esta perguntando isso?" se responde olhando por onde ele passou.
  */
  const carregarRota = useCallback(async (alvo: { chave?: string | null; conversa?: string | null }) => {
    const parametros = alvo.chave
      ? `chave=${encodeURIComponent(alvo.chave)}`
      : alvo.conversa
        ? `conversa=${encodeURIComponent(alvo.conversa)}`
        : "";
    if (!parametros) {
      setRota([]);
      setResumoDaRota(null);
      return;
    }
    try {
      const resposta = await fetch(`/api/chats/rota?${parametros}`, { cache: "no-store" });
      const dados = (await resposta.json().catch(() => null)) as
        | { rota?: PassoDaRota[]; resumo?: ResumoDaRota | null }
        | null;
      setRota(Array.isArray(dados?.rota) ? dados.rota : []);
      setResumoDaRota(dados?.resumo || null);
    } catch {
      setRota([]);
      setResumoDaRota(null);
    }
  }, []);

  const abrirConversa = useCallback((threadId: string) => {
    if (threadId === abertaRef.current) return;
    setErro(null);
    setTexto("");
    setArquivo(null);
    abertaRef.current = threadId;

    /* Mostra o que ja se sabe antes de perguntar de novo. */
    const guardadas = cacheDeMensagens.current[threadId];
    const guardado = cacheDeCabecalhos.current[threadId];
    setMensagens(guardadas || []);
    setCabecalho(guardado || null);
    setCarregandoFio(!guardadas);
    setAbertaId(threadId);
    setConversas((antes) => antes.map((item) => (item.id === threadId ? { ...item, naoLidas: 0 } : item)));
    if (guardadas?.length) window.setTimeout(() => fimDoFio.current?.scrollIntoView({ block: "end" }), 30);
  }, []);

  function trocarDeAba(nova: "loja" | "equipe") {
    if (nova === aba) return;
    setAba(nova);
    setAbertaId(null);
    abertaRef.current = null;
    setBusca("");
    setVisitanteAberto(null);
    if (nova === "equipe") setSubAba("conversas");
    /* A lista antiga volta na hora; a consulta corrige depois, se mudou algo. */
    setConversas(cacheDeConversas.current[`${nova}:${situacao}`] || []);
  }

  /* Trocar de sub-aba nao e trocar de assunto: a conversa aberta continua onde
     estava, so a lista da esquerda muda de pergunta. */
  function trocarDeSubAba(nova: "conversas" | "visitantes") {
    if (nova === subAba) return;
    setSubAba(nova);
    setBusca("");
    if (nova === "conversas") setVisitanteAberto(null);
    else {
      setAbertaId(null);
      abertaRef.current = null;
    }
  }

  /* ---------------- fio ---------------- */

  const carregarFio = useCallback(
    async (threadId: string, opcoes: { rolar?: boolean; soMensagens?: boolean } = {}) => {
      try {
        const endereco = `/api/chats/threads/${threadId}/mensagens${opcoes.soMensagens ? "?so=mensagens" : ""}`;
        const resposta = await fetch(endereco, { cache: "no-store" });
        const dados = (await resposta.json().catch(() => null)) as
          | { conversa?: CabecalhoDaConversa; mensagens?: Mensagem[]; meuId?: string }
          | null;
        if (abertaRef.current !== threadId) return;

        const lista = Array.isArray(dados?.mensagens) ? dados.mensagens : [];
        cacheDeMensagens.current[threadId] = lista;
        setMensagens(lista);
        if (dados?.conversa) {
          cacheDeCabecalhos.current[threadId] = dados.conversa;
          setCabecalho(dados.conversa);
        }
        if (dados?.meuId) setMeuId(dados.meuId);
        if (opcoes.rolar) window.setTimeout(() => fimDoFio.current?.scrollIntoView({ block: "end" }), 40);
      } finally {
        setCarregandoFio(false);
      }
    },
    []
  );

  useEffect(() => {
    if (!abertaId) {
      setCabecalho(null);
      setMensagens([]);
      return;
    }
    void carregarFio(abertaId, { rolar: true, soMensagens: Boolean(cacheDeCabecalhos.current[abertaId]) });
    /* Rede de seguranca: se o aviso ao vivo nao chegar, a conversa se atualiza
       sozinha. */
    const relogio = window.setInterval(() => void carregarFio(abertaId, { soMensagens: true }), 12000);
    return () => window.clearInterval(relogio);
  }, [abertaId, carregarFio]);

  /* Conversa apontada no endereco: veio de um aviso do sino, de um push ou de
     um link colado. */
  useEffect(() => {
    const alvo = new URLSearchParams(window.location.search).get("conversa");
    if (alvo) abrirConversa(alvo);
  }, [abrirConversa]);

  /* ---------------- avisos ao vivo ---------------- */

  const mostrarAviso = useCallback((aviso: AvisoNaTela) => {
    setAvisos((antes) => [...antes.filter((item) => item.threadId !== aviso.threadId), aviso].slice(-3));
    window.setTimeout(() => {
      setAvisos((antes) => antes.filter((item) => item.id !== aviso.id));
    }, TEMPO_DO_AVISO_MS);
  }, []);

  /* A rota acompanha o que esta aberto: a ficha do visitante ou a conversa. */
  const chaveEmFoco = visitanteAberto?.chave || null;
  useEffect(() => {
    if (chaveEmFoco) {
      void carregarRota({ chave: chaveEmFoco });
      return;
    }
    if (abertaId) {
      void carregarRota({ conversa: abertaId });
      return;
    }
    setRota([]);
    setResumoDaRota(null);
  }, [chaveEmFoco, abertaId, carregarRota]);

  /*
    Movimento da vitrine, ao vivo.

    A pessoa trocou de pagina e o cartao dela ja muda aqui — sem esperar a volta
    do relogio. E o que separa "ver o movimento" de "ler um relatorio de quinze
    segundos atras".
  */
  useEffect(() => {
    if (!lojaIds.length) return;
    const supabase = getSupabaseClient();

    const inscritos = lojaIds.map((id) => {
      const canal = supabase.channel(`loja-movimento-${id}`);
      canal.on("broadcast", { event: "movimento" }, ({ payload }) => {
        const dados = (payload || {}) as { chave?: string };
        void carregarVisitantes();
        if (dados.chave && dados.chave === visitanteAbertoRef.current) {
          void carregarRota({ chave: dados.chave });
        }
      });
      canal.subscribe();
      return canal;
    });

    return () => {
      inscritos.forEach((canal) => void supabase.removeChannel(canal));
    };
  }, [lojaIds, carregarVisitantes, carregarRota]);

  /* Canal da conversa aberta: o que chega aqui entra direto no fio. */
  useEffect(() => {
    if (!abertaId) return;
    const supabase = getSupabaseClient();
    const canal = supabase.channel(`chat-${abertaId}`);
    let apagarAviso: number | undefined;

    canal.on("broadcast", { event: "novidade" }, () => {
      void carregarFio(abertaId, { rolar: true, soMensagens: true });
      void carregarConversas();
      setDigitando(null);
    });

    /*
      "Digitando…"

      Custa uma mensagem solta no mesmo canal e muda a espera: quem vê o outro
      escrevendo espera; quem vê a tela parada acha que foi ignorado e vai
      perguntar no WhatsApp.
    */
    canal.on("broadcast", { event: "digitando" }, ({ payload }) => {
      const quem = String((payload as { quem?: string })?.quem || "").trim();
      if (!quem) return;
      setDigitando(quem);
      window.clearTimeout(apagarAviso);
      apagarAviso = window.setTimeout(() => setDigitando(null), 4000);
    });

    canal.subscribe();
    canalDoFio.current = canal;

    return () => {
      window.clearTimeout(apagarAviso);
      canalDoFio.current = null;
      setDigitando(null);
      void supabase.removeChannel(canal);
    };
  }, [abertaId, carregarFio, carregarConversas]);

  /*
    Canais da loja e da pessoa: e por aqui que se sabe de mensagem chegando em
    OUTRA conversa. Sem isso, so quem estivesse com o fio certo aberto ficaria
    sabendo — e a pessoa passaria a tarde sem ver que alguem chamou.
  */
  useEffect(() => {
    if (!lojaIds.length && !meuId) return;
    const supabase = getSupabaseClient();
    /* A loja pode ter mais de um dono de dados (dono e empresa): ouve todos,
       senao o funcionario ficaria surdo para o que chega no canal da empresa. */
    const nomes = [
      ...lojaIds.map((id) => `chat-loja-${id}`),
      meuId ? `chat-pessoa-${meuId}` : null,
    ].filter(Boolean) as string[];

    const inscritos = nomes.map((nome) => {
      const canal = supabase.channel(nome);
      canal.on("broadcast", { event: "novidade" }, ({ payload }) => {
        const dados = (payload || {}) as { threadId?: string; autorNome?: string; resumo?: string };
        if (!dados.threadId || dados.threadId === abertaRef.current) return;
        void carregarConversas();
        void tocarSomDeMensagem();
        mostrarAviso({
          id: `${dados.threadId}-${Date.now()}`,
          threadId: dados.threadId,
          quem: dados.autorNome || "Nova mensagem",
          texto: dados.resumo || "Mandou uma mensagem",
        });
      });
      canal.subscribe();
      return canal;
    });

    return () => {
      inscritos.forEach((canal) => void supabase.removeChannel(canal));
    };
  }, [lojaIds, meuId, carregarConversas, mostrarAviso]);

  /*
    Marca de presenca.

    O sino do sistema fica em outra aba e nao tem como saber se a pessoa esta
    aqui. Esta marca no armazenamento do navegador e o recado: "estou com o
    Chats aberto, nao toque o sino de novo".
  */
  useEffect(() => {
    const marcar = () => {
      if (document.visibilityState !== "visible") return;
      try {
        window.localStorage.setItem("ktl:chats-ativo", String(Date.now()));
      } catch {
        /* Navegador sem armazenamento: o sino toca duas vezes, e so. */
      }
    };
    marcar();
    const relogio = window.setInterval(marcar, 5000);
    document.addEventListener("visibilitychange", marcar);
    return () => {
      window.clearInterval(relogio);
      document.removeEventListener("visibilitychange", marcar);
    };
  }, []);

  useEffect(() => {
    if (!menuAberto) return;
    const fechar = () => setMenuAberto(null);
    window.addEventListener("click", fechar);
    return () => window.removeEventListener("click", fechar);
  }, [menuAberto]);

  /* Clicar fora fecha os emojis e o menu da barra: painel que só fecha no botão
     acaba esquecido aberto por cima da conversa. */
  useEffect(() => {
    if (!emojisAbertos && !atalhoAberto && !comando) return;
    const fechar = () => {
      setEmojisAbertos(false);
      if (atalhoAberto && !comando) setAtalhoAberto(false);
    };
    window.addEventListener("click", fechar);
    return () => window.removeEventListener("click", fechar);
  }, [emojisAbertos, atalhoAberto, comando]);

  useEffect(() => {
    if (!fotoAberta) return;
    const aoTeclar = (evento: KeyboardEvent) => {
      if (evento.key === "Escape") setFotoAberta(null);
    };
    window.addEventListener("keydown", aoTeclar);
    return () => window.removeEventListener("keydown", aoTeclar);
  }, [fotoAberta]);

  /* ---------------- escrever ---------------- */

  async function enviar() {
    /* Editando, o mesmo botão salva a correção em vez de mandar outra. */
    if (editando) {
      await salvarEdicao();
      return;
    }
    if (!abertaId || enviando || (!texto.trim() && !arquivo)) return;
    if (arquivo && arquivo.size > TAMANHO_MAXIMO) {
      setErro("Arquivo maior que 8 MB.");
      return;
    }

    const conversaId = abertaId;
    const corpo = texto.trim();
    const anexo = arquivo;
    const arte = comoArte;
    const citada = citando;
    const interna = notaInterna;

    /*
      A mensagem entra na tela antes de existir no banco: quem escreveu ja sabe
      o que escreveu, esperar o servidor para ver o proprio texto e que era
      estranho. Se der errado, o balao sai e o texto volta para o campo.
    */
    const provisoria: Mensagem = {
      id: `enviando-${Date.now()}`,
      author_type: cabecalho?.kind === "team" ? "team" : "store",
      author_id: meuId,
      sender_name: "Você",
      body: corpo || null,
      attachment_url: anexo && anexo.type.startsWith("image/") ? URL.createObjectURL(anexo) : null,
      attachment_name: anexo?.name || null,
      attachment_kind: anexo ? (anexo.type.startsWith("image/") ? "image" : "file") : null,
      is_art: Boolean(anexo && arte && anexo.type.startsWith("image/")),
      art_status: null,
      art_decision_note: null,
      internal: interna,
      reply_to: citada?.id || null,
      respondendo: citada
        ? {
            id: citada.id,
            quem: citada.sender_name || "Mensagem",
            resumo: String(citada.body || "").slice(0, 120) || "Mensagem",
          }
        : null,
      created_at: new Date().toISOString(),
      deleted_at: null,
      lida_pelo_outro: null,
      pendente: true,
    };

    setMensagens((antes) => [...antes, provisoria]);
    setTexto("");
    setArquivo(null);
    setComoArte(false);
    setCitando(null);
    setNotaInterna(false);
    fecharAtalhos();
    setEmojisAbertos(false);
    if (campoDeArquivo.current) campoDeArquivo.current.value = "";
    window.setTimeout(() => fimDoFio.current?.scrollIntoView({ block: "end" }), 20);

    setEnviando(true);
    setErro(null);
    try {
      const dados = new FormData();
      dados.set("body", corpo);
      if (anexo) dados.set("file", anexo);
      if (anexo && arte) dados.set("is_art", "true");
      if (citada) dados.set("reply_to", citada.id);
      if (interna) dados.set("internal", "true");

      const resposta = await fetch(`/api/chats/threads/${conversaId}/mensagens`, { method: "POST", body: dados });
      const retorno = (await resposta.json().catch(() => null)) as { error?: string } | null;
      if (!resposta.ok) throw new Error(retorno?.error || "Não foi possível enviar.");

      await carregarFio(conversaId, { rolar: true, soMensagens: true });
      void carregarConversas();
    } catch (falha) {
      setMensagens((antes) => antes.filter((mensagem) => mensagem.id !== provisoria.id));
      setTexto(corpo);
      setErro(falha instanceof Error ? falha.message : "Não foi possível enviar.");
    } finally {
      setEnviando(false);
    }
  }

  /* Quem está no balcão: sem isto ninguém sabe se quem responde é o dono ou o
     funcionário que ficou logado na máquina. */
  useEffect(() => {
    void (async () => {
      try {
        const resposta = await fetch("/api/chats/eu", { cache: "no-store" });
        const dados = (await resposta.json().catch(() => null)) as { eu?: Eu } | null;
        if (dados?.eu) setEu(dados.eu);
      } catch {
        /* Sem a ficha, a tela continua funcionando — só sem o rosto. */
      }
    })();
  }, []);

  /* A equipe da loja, para transferir o atendimento. */
  useEffect(() => {
    if (equipeDaLoja.length) return;
    void (async () => {
      try {
        const resposta = await fetch("/api/chats/contatos", { cache: "no-store" });
        const dados = (await resposta.json().catch(() => null)) as { equipe?: Colega[] } | null;
        setEquipeDaLoja(Array.isArray(dados?.equipe) ? dados.equipe : []);
      } catch {
        setEquipeDaLoja([]);
      }
    })();
  }, [equipeDaLoja.length]);

  /*
    O começo da conversa.

    A tela abre pelo fim, que é o que interessa; quem precisa do combinado de
    três meses atrás pede mais e ele vem, sem recarregar nada.
  */
  async function carregarAntigas() {
    if (!abertaId || carregandoAntigas || !mensagens.length) return;
    setCarregandoAntigas(true);
    try {
      const primeira = mensagens[0];
      const resposta = await fetch(
        `/api/chats/threads/${abertaId}/mensagens?antes=${encodeURIComponent(primeira.created_at)}`,
        { cache: "no-store" }
      );
      const dados = (await resposta.json().catch(() => null)) as
        | { mensagens?: Mensagem[]; temMais?: boolean }
        | null;
      const antigas = Array.isArray(dados?.mensagens) ? dados.mensagens : [];
      if (!antigas.length) {
        setTemMais(false);
        return;
      }
      setMensagens((antes) => {
        const juntas = [...antigas, ...antes];
        cacheDeMensagens.current[abertaId] = juntas;
        return juntas;
      });
      setTemMais(Boolean(dados?.temMais));
    } finally {
      setCarregandoAntigas(false);
    }
  }

  /* Corrigir o que se escreveu, em vez de apagar e reescrever: some a dúvida do
     outro lado sobre o que estava ali antes. */
  async function salvarEdicao() {
    if (!abertaId || !editando) return;
    const novoTexto = texto.trim();
    if (!novoTexto) return;

    setEnviando(true);
    setErro(null);
    try {
      const resposta = await fetch(`/api/chats/threads/${abertaId}/mensagens`, {
        method: "PATCH",
        headers: { "content-type": "application/json" },
        body: JSON.stringify({ messageId: editando.id, body: novoTexto }),
      });
      if (!resposta.ok) {
        throw new Error((await resposta.json().catch(() => null))?.error || "Não foi possível editar.");
      }
      setEditando(null);
      setTexto("");
      await carregarFio(abertaId, { soMensagens: true });
      void carregarConversas();
    } catch (falha) {
      setErro(falha instanceof Error ? falha.message : "Não foi possível editar.");
    } finally {
      setEnviando(false);
    }
  }

  /*
    Da conversa para o negócio.

    Dois cliques que existiam só na cabeça de quem atende: o visitante vira
    ficha de cliente, e o papo vira orçamento com o produto que ele estava
    olhando. Sem isso, o atendente sai da conversa para digitar tudo de novo —
    e é aí que a venda esfria.
  */
  async function virarNegocio(acao: "cliente" | "orcamento") {
    if (!abertaId) return;
    setErro(null);
    try {
      const resposta = await fetch("/api/chats/negocio", {
        method: "POST",
        headers: { "content-type": "application/json" },
        body: JSON.stringify({ threadId: abertaId, acao }),
      });
      const retorno = (await resposta.json().catch(() => null)) as
        | { clienteId?: string; quoteId?: string; numero?: number; error?: string }
        | null;
      if (!resposta.ok) throw new Error(retorno?.error || "Não foi possível.");

      delete cacheDeCabecalhos.current[abertaId];
      await carregarFio(abertaId, {});
      void carregarConversas();

      if (acao === "orcamento" && retorno?.quoteId) {
        window.open(`/dashboard/quotes/${retorno.quoteId}`, "_blank", "noreferrer");
      }
    } catch (falha) {
      setErro(falha instanceof Error ? falha.message : "Não foi possível.");
    }
  }

  /*
    Marcar o dia de voltar.

    Às 9h da manhã seguinte, e não à meia-noite: lembrete que chega enquanto a
    gráfica está fechada é lembrete perdido.
  */
  async function marcarRetomar(dias: number | null) {
    if (!abertaId) return;
    let quando = "";
    if (dias !== null) {
      const dia = new Date();
      dia.setDate(dia.getDate() + dias);
      dia.setHours(9, 0, 0, 0);
      quando = dia.toISOString();
    }
    await mudarConversa({
      retomarEm: quando,
      retomarNota: quando ? `Retomar ${cabecalho?.contato || "esta conversa"}` : "",
    });
  }

  /* Responsável e etiquetas: o combinado fica à vista, e não no boca a boca. */
  async function mudarConversa(mudanca: Record<string, unknown>) {
    if (!abertaId) return;
    try {
      const resposta = await fetch(`/api/chats/threads/${abertaId}`, {
        method: "PATCH",
        headers: { "content-type": "application/json" },
        body: JSON.stringify(mudanca),
      });
      if (!resposta.ok) throw new Error("Não foi possível salvar.");
      delete cacheDeCabecalhos.current[abertaId];
      await carregarFio(abertaId, {});
      void carregarConversas();
    } catch (falha) {
      setErro(falha instanceof Error ? falha.message : "Não foi possível salvar.");
    }
  }

  /* ---------------- ações rápidas ---------------- */

  const carregarAtalho = useCallback(async (tipo: string, busca: string) => {
    setCarregandoAtalho(true);
    try {
      const resposta = await fetch(`/api/chats/atalhos?tipo=${tipo}&busca=${encodeURIComponent(busca)}`, {
        cache: "no-store",
      });
      const dados = (await resposta.json().catch(() => null)) as
        | { itens?: Record<string, unknown>[]; sugeridas?: boolean }
        | null;
      setItensDoAtalho(Array.isArray(dados?.itens) ? dados.itens : []);
      setRespostasSugeridas(Boolean(dados?.sugeridas));
      setIndiceDoAtalho(0);
    } catch {
      setItensDoAtalho([]);
    } finally {
      setCarregandoAtalho(false);
    }
  }, []);

  /* Enquanto um comando está aberto, o que se digita é a busca dele. */
  useEffect(() => {
    if (!comando || comando === "loja" || comando === "encerrar") return;
    const atraso = window.setTimeout(() => void carregarAtalho(comando, texto), texto ? 200 : 0);
    return () => window.clearTimeout(atraso);
  }, [comando, texto, carregarAtalho]);

  const comandosFiltrados = useMemo(() => {
    const termo = texto.startsWith("/") ? texto.slice(1).toLowerCase().trim() : "";
    if (!termo) return COMANDOS;
    return COMANDOS.filter(
      (item) => item.chave.includes(termo) || item.rotulo.toLowerCase().includes(termo)
    );
  }, [texto]);

  const listaDoAtalho: Record<string, unknown>[] = comando
    ? itensDoAtalho
    : (comandosFiltrados as unknown as Record<string, unknown>[]);

  function fecharAtalhos() {
    setAtalhoAberto(false);
    setComando(null);
    setItensDoAtalho([]);
    setIndiceDoAtalho(0);
  }

  /* Avisa o outro lado, no máximo uma vez a cada dois segundos: o recado é
     "estou aqui escrevendo", e não um telegrama por tecla. */
  function avisarQueEstouDigitando() {
    if (!canalDoFio.current || notaInterna) return;
    const agora = Date.now();
    if (agora - ultimoAvisoDeDigitacao.current < 2000) return;
    ultimoAvisoDeDigitacao.current = agora;
    void canalDoFio.current.send({
      type: "broadcast",
      event: "digitando",
      payload: { quem: eu?.nome || "A loja" },
    });
  }

  function digitou(valor: string) {
    setTexto(valor);
    if (valor.trim()) avisarQueEstouDigitando();
    if (comando) return;
    /* A barra só abre o menu quando é a primeira coisa da mensagem: "e/ou" no
       meio de uma frase não pode virar comando. */
    if (valor.startsWith("/") && !valor.includes(" ")) {
      setAtalhoAberto(true);
      setIndiceDoAtalho(0);
    } else {
      setAtalhoAberto(false);
    }
  }

  async function escolherComando(escolhido: Comando) {
    if (escolhido.chave === "encerrar") {
      fecharAtalhos();
      setTexto("");
      pedirParaEncerrar();
      return;
    }

    if (escolhido.chave === "nota") {
      /* O recado interno usa o mesmo campo, com outra cara: quem está
         escrevendo precisa ver, o tempo todo, que isso não vai para o cliente. */
      fecharAtalhos();
      setTexto("");
      setNotaInterna(true);
      return;
    }

    if (escolhido.chave === "orcamento" || escolhido.chave === "cliente") {
      fecharAtalhos();
      setTexto("");
      await virarNegocio(escolhido.chave);
      return;
    }

    if (escolhido.chave === "loja") {
      fecharAtalhos();
      setTexto("");
      try {
        const resposta = await fetch("/api/chats/atalhos?tipo=loja", { cache: "no-store" });
        const dados = (await resposta.json().catch(() => null)) as { link?: string | null } | null;
        if (dados?.link) setTexto(`Nossa loja: ${dados.link}`);
        else setErro("A loja ainda não tem endereço público.");
      } catch {
        setErro("Não consegui pegar o endereço da loja.");
      }
      return;
    }

    setComando(escolhido.chave);
    setTexto("");
    setItensDoAtalho([]);
    setIndiceDoAtalho(0);
  }

  /*
    A frase pronta com os dados desta conversa.

    Sem isto, a pessoa colava "Oi! Seu pedido está pronto" e voltava para
    escrever o nome na mão toda vez — o que faz parar de usar o atalho e
    escrever tudo de novo. O que não se sabe preencher some da frase, em vez de
    ir "{pedido}" para o cliente.
  */
  function comOsDados(frase: string) {
    const primeiroNome = String(cabecalho?.contato || "").trim().split(/\s+/)[0] || "";
    const pedido = cabecalho?.pedido ? `#${cabecalho.pedido.number}` : "";
    const orcamento = cabecalho?.orcamento ? `#${cabecalho.orcamento.number}` : "";

    return frase
      .replace(/\{nome\}/gi, primeiroNome)
      .replace(/\{pedido\}/gi, pedido || orcamento)
      .replace(/\{orcamento\}/gi, orcamento)
      .replace(/\{loja\}/gi, eu?.loja || "")
      .replace(/\{atendente\}/gi, String(eu?.nome || "").split(/\s+/)[0] || "")
      /* Sobrou marcador que esta conversa não sabe preencher: sai da frase, com
         os espaços em volta, para não virar "Seu pedido  está pronto". */
      .replace(/\s*\{[a-zà-ú]+\}\s*/gi, " ")
      .replace(/\s{2,}/g, " ")
      .trim();
  }

  async function escolherItem(item: Record<string, unknown>) {
    if (comando === "produto") {
      const cartao: Cartao = {
        tipo: "produto",
        titulo: String(item.nome || ""),
        imagem: (item.imagem as string) || null,
        preco: item.preco === null || item.preco === undefined ? null : Number(item.preco),
        link: (item.link as string) || null,
      };
      fecharAtalhos();
      setTexto("");
      await enviarCartao(cartao, "");
      return;
    }

    if (comando === "cupom") {
      const condicoes = condicoesDoCupom({
        tipo: String(item.tipo || ""),
        valor: Number(item.valor || 0),
        minimo: item.minimo === null || item.minimo === undefined ? null : Number(item.minimo),
        vence: (item.vence as string) || null,
      });
      const cartao: Cartao = {
        tipo: "cupom",
        titulo: String(item.codigo || ""),
        codigo: String(item.codigo || ""),
        subtitulo: condicoes,
      };
      fecharAtalhos();
      setTexto("");
      await enviarCartao(cartao, `Use o cupom ${item.codigo} e ganhe ${condicoes.split(" · ")[0]}.`);
      return;
    }

    if (comando === "resposta") {
      /* Resposta pronta não vai sozinha: cai no campo já com os dados desta
         conversa no lugar, para a pessoa ler antes de mandar. */
      fecharAtalhos();
      setTexto(comOsDados(String(item.texto || "")));
      return;
    }

    await escolherComando(item as unknown as Comando);
  }

  async function enviarCartao(cartao: Cartao, corpo: string) {
    if (!abertaId || enviando) return;
    const conversaId = abertaId;

    const provisoria: Mensagem = {
      id: `enviando-${Date.now()}`,
      author_type: cabecalho?.kind === "team" ? "team" : "store",
      author_id: meuId,
      sender_name: "Você",
      body: corpo || null,
      card: cartao,
      attachment_url: null,
      attachment_name: null,
      attachment_kind: null,
      is_art: false,
      art_status: null,
      art_decision_note: null,
      created_at: new Date().toISOString(),
      deleted_at: null,
      lida_pelo_outro: null,
      pendente: true,
    };

    setMensagens((antes) => [...antes, provisoria]);
    window.setTimeout(() => fimDoFio.current?.scrollIntoView({ block: "end" }), 20);

    setEnviando(true);
    setErro(null);
    try {
      const dados = new FormData();
      dados.set("body", corpo);
      dados.set("card", JSON.stringify(cartao));

      const resposta = await fetch(`/api/chats/threads/${conversaId}/mensagens`, { method: "POST", body: dados });
      const retorno = (await resposta.json().catch(() => null)) as { error?: string } | null;
      if (!resposta.ok) throw new Error(retorno?.error || "Não foi possível enviar.");

      await carregarFio(conversaId, { rolar: true, soMensagens: true });
      void carregarConversas();
    } catch (falha) {
      setMensagens((antes) => antes.filter((mensagem) => mensagem.id !== provisoria.id));
      setErro(falha instanceof Error ? falha.message : "Não foi possível enviar.");
    } finally {
      setEnviando(false);
    }
  }

  async function salvarRespostas(lista: RespostaPronta[]) {
    const resposta = await fetch("/api/chats/atalhos", {
      method: "PUT",
      headers: { "content-type": "application/json" },
      body: JSON.stringify({ respostas: lista }),
    });
    if (!resposta.ok) {
      const retorno = (await resposta.json().catch(() => null)) as { error?: string } | null;
      throw new Error(retorno?.error || "Não consegui salvar.");
    }
    if (comando === "resposta") await carregarAtalho("resposta", "");
  }

  function pedirParaApagar(messageId: string) {
    setMenuAberto(null);
    setDecisao({
      titulo: "Apagar esta mensagem?",
      texto: "Ela some para os dois lados e fica só o aviso de que foi apagada. Não dá para desfazer.",
      confirmar: "Apagar para todos",
      tom: "perigo",
      aoConfirmar: () => apagar(messageId),
    });
  }

  async function apagar(messageId: string) {
    if (!abertaId) return;
    try {
      const resposta = await fetch(
        `/api/chats/threads/${abertaId}/mensagens?messageId=${encodeURIComponent(messageId)}`,
        { method: "DELETE" }
      );
      const retorno = (await resposta.json().catch(() => null)) as { error?: string } | null;
      if (!resposta.ok) throw new Error(retorno?.error || "Não foi possível apagar.");
      await carregarFio(abertaId, { soMensagens: true });
      void carregarConversas();
    } catch (falha) {
      setErro(falha instanceof Error ? falha.message : "Não foi possível apagar.");
    }
  }

  function pedirParaEncerrar() {
    setDecisao({
      titulo: "Encerrar este atendimento?",
      texto:
        "A conversa sai da lista de abertas, sem apagar nada. Se a pessoa escrever de novo, ela volta sozinha e conta como uma nova visita.",
      confirmar: "Encerrar",
      aoConfirmar: () => encerrarAtendimento(true),
    });
  }

  async function encerrarAtendimento(encerrar: boolean) {
    if (!abertaId) return;
    try {
      const resposta = await fetch(`/api/chats/threads/${abertaId}`, {
        method: "PATCH",
        headers: { "content-type": "application/json" },
        body: JSON.stringify({ encerrar }),
      });
      const retorno = (await resposta.json().catch(() => null)) as { error?: string } | null;
      if (!resposta.ok) throw new Error(retorno?.error || "Não foi possível encerrar.");

      delete cacheDeCabecalhos.current[abertaId];
      if (encerrar) {
        setAbertaId(null);
        abertaRef.current = null;
      } else {
        await carregarFio(abertaId, {});
      }
      await carregarConversas();
    } catch (falha) {
      setErro(falha instanceof Error ? falha.message : "Não foi possível encerrar.");
    }
  }

  async function puxarConversa() {
    const alvo = visitanteAberto;
    const mensagem = abordagem.trim();
    if (!alvo || !mensagem || abordando) return;

    setAbordando(true);
    setErro(null);
    try {
      const resposta = await fetch("/api/chats/visitantes", {
        method: "POST",
        headers: { "content-type": "application/json" },
        body: JSON.stringify({ chave: alvo.chave, mensagem }),
      });
      const retorno = (await resposta.json().catch(() => null)) as { threadId?: string; error?: string } | null;
      if (!resposta.ok || !retorno?.threadId) throw new Error(retorno?.error || "Não foi possível falar agora.");

      setAbordagem("");
      setVisitanteAberto(null);
      setAba("loja");
      /* A abordagem virou conversa: e nas conversas que ela vive daqui para a
         frente. */
      setSubAba("conversas");
      await carregarConversas();
      abrirConversa(retorno.threadId);
    } catch (falha) {
      setErro(falha instanceof Error ? falha.message : "Não foi possível falar agora.");
    } finally {
      setAbordando(false);
    }
  }

  /* Em que pe esta esta conversa. E o funil sem tela de funil: a etiqueta mora
     na propria conversa e todo mundo ve a mesma coisa. */
  async function mudarEstagio(novo: string) {
    if (!abertaId || mudandoEstagio) return;
    const anterior = cabecalho?.estagio || null;
    const valor = anterior === novo ? "" : novo;

    setMudandoEstagio(true);
    setCabecalho((antes) => (antes ? { ...antes, estagio: valor || null } : antes));
    try {
      const resposta = await fetch(`/api/chats/threads/${abertaId}`, {
        method: "PATCH",
        headers: { "content-type": "application/json" },
        body: JSON.stringify({ estagio: valor }),
      });
      if (!resposta.ok) throw new Error("Não foi possível salvar.");
      const guardado = cacheDeCabecalhos.current[abertaId];
      if (guardado) cacheDeCabecalhos.current[abertaId] = { ...guardado, estagio: valor || null };
      setConversas((antes) =>
        antes.map((item) => (item.id === abertaId ? { ...item, estagio: valor || null } : item))
      );
    } catch {
      setCabecalho((antes) => (antes ? { ...antes, estagio: anterior } : antes));
    } finally {
      setMudandoEstagio(false);
    }
  }

  async function abrirConversaComColega(membroId: string) {
    setListaDeContatos(false);
    try {
      const resposta = await fetch("/api/chats/threads", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ membroId }),
      });
      const retorno = (await resposta.json().catch(() => null)) as { threadId?: string; error?: string } | null;
      if (!resposta.ok || !retorno?.threadId) throw new Error(retorno?.error || "Não foi possível abrir.");
      await carregarConversas();
      abrirConversa(retorno.threadId);
    } catch (falha) {
      setErro(falha instanceof Error ? falha.message : "Não foi possível abrir.");
    }
  }

  /* ---------------- contexto ---------------- */

  const arquivosDaConversa = useMemo(
    () => mensagens.filter((mensagem) => mensagem.attachment_url && !mensagem.deleted_at && !mensagem.pendente),
    [mensagens]
  );

  const linksDaConversa = useMemo(() => {
    const encontrados: string[] = [];
    mensagens.forEach((mensagem) => {
      const achados = String(mensagem.body || "").match(/https?:\/\/[^\s]+/g) || [];
      achados.forEach((link) => {
        if (!encontrados.includes(link)) encontrados.push(link);
      });
    });
    return encontrados.slice(0, 12);
  }, [mensagens]);

  const ehMinha = useCallback(
    (mensagem: Mensagem) =>
      cabecalho?.kind === "team" ? mensagem.author_id === meuId : mensagem.author_type !== "client",
    [cabecalho?.kind, meuId]
  );

  let ultimoDia = "";

  /*
    A linha do tempo da visita.

    Aparece nos dois lugares — na ficha de quem esta na loja agora e no painel
    de qualquer conversa — porque a pergunta e a mesma: por onde essa pessoa
    passou antes de falar comigo? Quando nao ha rota, o painel diz isso com
    todas as letras, em vez de sumir e deixar a duvida no ar.
  */
  const linhaDoTempo = (
    <div className="chat-secao">
      <div className="chat-secao-titulo">Rota na loja</div>

      {resumoDaRota ? (
        <dl>
          {resumoDaRota.chegouPor ? (
            <div className="chat-dado">
              <dt>Chegou por</dt>
              <dd>{resumoDaRota.chegouPor}</dd>
            </div>
          ) : null}
          <div className="chat-dado">
            <dt>Na loja</dt>
            <dd>
              {resumoDaRota.minutos} min · {resumoDaRota.paginas}{" "}
              {resumoDaRota.paginas === 1 ? "página" : "páginas"}
            </dd>
          </div>
          {resumoDaRota.carrinhoItens > 0 ? (
            <div className="chat-dado">
              <dt>Carrinho</dt>
              <dd>
                {resumoDaRota.carrinhoItens}{" "}
                {resumoDaRota.carrinhoItens === 1 ? "item" : "itens"} · {dinheiro(resumoDaRota.carrinhoTotal)}
              </dd>
            </div>
          ) : null}
        </dl>
      ) : null}

      {rota.length === 0 ? (
        <p className="chat-rota-vazio">
          Sem navegação registrada. A rota aparece quando a pessoa entra pela loja online com o chat ligado.
        </p>
      ) : (
        <ol className="chat-rota">
          {rota.map((passo) => {
            const detalhe = detalheDoPasso(passo);
            return (
              <li key={passo.id} className="chat-rota-passo" data-tipo={passo.tipo}>
                <span className="chat-rota-ponto">{iconeDoPasso(passo.tipo)}</span>
                <div className="chat-rota-corpo">
                  <div className="chat-rota-topo">
                    <span className="chat-rota-titulo">{tituloDoPasso(passo)}</span>
                    <span className="chat-rota-hora">{hora(passo.em)}</span>
                  </div>
                  {passo.tipo === "produto" && passo.imagem ? (
                    <div className="chat-rota-produto">
                      <Image
                        src={passo.imagem}
                        alt={passo.produto || "Produto"}
                        width={36}
                        height={36}
                        unoptimized
                      />
                      <span>{passo.preco ? dinheiro(passo.preco) : "abriu a página do produto"}</span>
                    </div>
                  ) : null}
                  {detalhe ? <div className="chat-rota-detalhe">{detalhe}</div> : null}
                </div>
              </li>
            );
          })}
        </ol>
      )}
    </div>
  );

  return (
    <div
      className="chat-app"
      data-fio={abertaId || visitanteAberto ? "aberto" : "fechado"}
      data-painel={painelAberto ? "aberto" : "fechado"}
    >
      {/* ---------------- trilho ---------------- */}
      <nav className="chat-rail">
        {/* A mesma carinha do menu recolhido do sistema: quem troca de aba tem
            que reconhecer a casa na hora. */}
        <div className="chat-rail-marca">
          <Image src="/brand-symbol.svg" alt="Ktalogu" width={26} height={26} />
        </div>

        <button
          type="button"
          className="chat-rail-item"
          data-ativo={aba === "loja" ? "sim" : "nao"}
          onClick={() => trocarDeAba("loja")}
        >
          <Store size={19} strokeWidth={2.1} />
          Loja
          {aba !== "loja" && naoLidasPorAba.loja ? <span className="chat-rail-bolha">{naoLidasPorAba.loja}</span> : null}
          {aba !== "loja" && !naoLidasPorAba.loja && visitantes.length ? (
            <span className="chat-rail-bolha" data-tom="vivo">
              {visitantes.length}
            </span>
          ) : null}
        </button>

        <button
          type="button"
          className="chat-rail-item"
          data-ativo={aba === "equipe" ? "sim" : "nao"}
          onClick={() => trocarDeAba("equipe")}
        >
          <Users size={19} strokeWidth={2.1} />
          Equipe
          {aba !== "equipe" && naoLidasPorAba.equipe ? (
            <span className="chat-rail-bolha">{naoLidasPorAba.equipe}</span>
          ) : null}
        </button>

        <div className="chat-rail-pe">
          <button
            type="button"
            className="chat-rail-icone"
            title="Configurações do Chats"
            aria-label="Configurações do Chats"
            onClick={() => setConfigAberta(true)}
          >
            <Settings size={18} />
          </button>

          <a href="/dashboard" className="chat-rail-icone" title="Voltar para o sistema">
            <Grid2x2 size={18} />
          </a>

          {/*
            Quem está logado.

            O Chats abre em aba separada, sem o menu do sistema em volta. Sem
            este rosto, ninguém percebe que ficou logado o funcionário na
            máquina do balcão — e a conversa sai assinada por quem não devia.
          */}
          {eu ? (
            <div className="chat-eu" title={`${eu.nome} · ${eu.papel}${eu.loja ? ` · ${eu.loja}` : ""}`}>
              {eu.avatar ? (
                <Image src={eu.avatar} alt={eu.nome} width={34} height={34} unoptimized />
              ) : (
                iniciais(eu.nome)
              )}
              <span className="chat-eu-ficha">
                <strong>{eu.nome}</strong>
                <em>{eu.papel}</em>
              </span>
            </div>
          ) : null}
        </div>
      </nav>

      {/* ---------------- lista ---------------- */}
      <section className="chat-lista">
        <div className="chat-lista-topo">
          <div style={{ display: "flex", alignItems: "center", gap: 8 }}>
            <div style={{ minWidth: 0, flex: 1 }}>
              <div className="chat-lista-titulo">{aba === "loja" ? "Loja Online" : "Equipe"}</div>
              <div className="chat-lista-sub">
                {aba === "equipe"
                  ? "Conversas internas da sua empresa"
                  : subAba === "conversas"
                    ? "Quem já falou com a loja"
                    : visitantes.length
                      ? `${visitantes.length} ${visitantes.length === 1 ? "pessoa navegando agora" : "pessoas navegando agora"}`
                      : "Ninguém navegando neste instante"}
              </div>
            </div>
            {aba === "equipe" ? (
              <button
                type="button"
                className="chat-icone"
                title="Conversar com alguém da equipe"
                onClick={(evento) => {
                  evento.stopPropagation();
                  setListaDeContatos((antes) => !antes);
                }}
              >
                <MessageSquarePlus size={18} />
              </button>
            ) : null}
          </div>

          {/*
            Conversas e Visitantes.

            Sao os dois estados da mesma pessoa: a que ja falou e a que ainda
            esta olhando. Ficam lado a lado porque a graca e justamente passar
            de uma para a outra — e a de visitantes fica sempre a vista, com o
            ponto vivo, porque movimento de vitrine escondido nao serve de nada.
          */}
          {aba === "loja" ? (
            <div className="chat-subabas">
              <button
                type="button"
                data-ativo={subAba === "conversas" ? "sim" : "nao"}
                onClick={() => trocarDeSubAba("conversas")}
              >
                Conversas
                {naoLidasPorAba.loja ? <span className="chat-subaba-bolha">{naoLidasPorAba.loja}</span> : null}
              </button>
              <button
                type="button"
                data-ativo={subAba === "visitantes" ? "sim" : "nao"}
                onClick={() => trocarDeSubAba("visitantes")}
              >
                Visitantes
                {visitantes.length ? (
                  <span className="chat-subaba-bolha" data-tom="vivo">
                    {visitantes.length}
                  </span>
                ) : null}
              </button>
            </div>
          ) : null}

          {/* O placar do dia: se o atendimento da loja online esta valendo a
              pena se le em tres numeros, nao em um relatorio. */}
          {aba === "loja" && subAba === "visitantes" ? (
            <div className="chat-placar">
              <div>
                <strong>{dia.entraram}</strong>
                <span>entraram</span>
              </div>
              <div>
                <strong>{dia.falaram}</strong>
                <span>falaram</span>
              </div>
              <div>
                <strong>{dia.compraram}</strong>
                <span>compraram</span>
              </div>
            </div>
          ) : null}

          {/* Encerradas somem do trabalho do dia, mas ficam a um clique.
              Presenca nao encerra: quem saiu da loja some sozinho. */}
          <div className="chat-filtro" hidden={subAba === "visitantes"}>
            {/*
              "Esperando você" é a pergunta que a pessoa faz de verdade ao
              sentar. Vem primeiro, e ali a ordem se inverte: quem espera há
              mais tempo aparece no topo, em vez de afundar toda vez que alguém
              escreve em outra conversa.
            */}
            <button
              type="button"
              data-ativo={situacao === "esperando" ? "sim" : "nao"}
              onClick={() => {
                setSituacao("esperando");
                setAbertaId(null);
                abertaRef.current = null;
                setConversas(cacheDeConversas.current[`${aba}:esperando`] || []);
              }}
            >
              Esperando
              {esperando ? <span className="chat-filtro-bolha">{esperando}</span> : null}
            </button>
            <button
              type="button"
              data-ativo={situacao === "abertas" ? "sim" : "nao"}
              onClick={() => {
                setSituacao("abertas");
                setAbertaId(null);
                abertaRef.current = null;
                setConversas(cacheDeConversas.current[`${aba}:abertas`] || []);
              }}
            >
              Abertas
            </button>
            <button
              type="button"
              data-ativo={situacao === "encerradas" ? "sim" : "nao"}
              onClick={() => {
                setSituacao("encerradas");
                setAbertaId(null);
                abertaRef.current = null;
                setConversas(cacheDeConversas.current[`${aba}:encerradas`] || []);
              }}
            >
              Encerradas
            </button>
          </div>

          <div className="chat-busca" hidden={subAba === "visitantes"}>
            <Search size={15} />
            <input
              value={busca}
              onChange={(evento) => setBusca(evento.target.value)}
              placeholder={aba === "loja" ? "Procurar cliente ou pedido" : "Procurar alguém"}
            />
          </div>

          <AtivarAvisos />
          {aba === "loja" ? <ChaveDoAtendimento /> : null}

          {listaDeContatos && aba === "equipe" ? (
            <div style={{ marginTop: 10, borderRadius: 12, border: "1px solid var(--linha)", overflow: "hidden" }}>
              {equipe.length === 0 ? (
                <div className="chat-vazio" style={{ padding: "18px 14px" }}>
                  Ninguém mais na equipe ainda.
                </div>
              ) : (
                equipe.map((colega) => (
                  <button
                    key={colega.id}
                    type="button"
                    className="chat-item"
                    style={{ padding: "9px 12px" }}
                    onClick={() => void abrirConversaComColega(colega.id)}
                  >
                    <div className="chat-avatar" style={{ height: 34, width: 34, borderRadius: 11 }}>
                      {iniciais(colega.nome)}
                    </div>
                    <div className="chat-item-corpo">
                      <div className="chat-item-nome">{colega.nome}</div>
                      <div className="chat-item-previa">
                        <span>{colega.cargo}</span>
                      </div>
                    </div>
                  </button>
                ))
              )}
            </div>
          ) : null}
        </div>

        <div className="chat-lista-rolagem">
          {subAba === "visitantes" ? (
            visitantes.length === 0 ? (
              <div className="chat-vazio">
                Ninguém na loja neste instante. Quem abrir a loja aparece aqui em poucos segundos, com a foto do
                produto que está olhando.
              </div>
            ) : (
              visitantes.map((visitante) => (
                <button
                  key={visitante.chave}
                  type="button"
                  className="chat-item"
                  data-ativo={visitanteAberto?.chave === visitante.chave ? "sim" : "nao"}
                  onClick={() => {
                    setVisitanteAberto(visitante);
                    setAbordagem("");
                    setErro(null);
                  }}
                >
                  {/* O lugar da pessoa é da pessoa: o produto aparece ao lado do
                      que ele está olhando, na linha de baixo. */}
                  <div className="chat-avatar" data-boneco="sim">
                    <span className="chat-ponto-vivo" />
                    {bonequinho()}
                  </div>
                  <div className="chat-item-corpo">
                    <div className="chat-item-linha">
                      <div className="chat-item-nome">
                        {visitante.nome || "Visitante"}
                        {visitante.temperatura === "quente" ? (
                          <span className="chat-fogo" title={visitante.motivo}>
                            <Flame size={11} />
                          </span>
                        ) : null}
                      </div>
                      <div className="chat-item-hora">{visitante.minutosNaLoja} min</div>
                    </div>
                    <div className="chat-item-previa">
                      {visitante.imagem ? (
                        <span className="chat-mini-produto">
                          <Image
                            src={visitante.imagem}
                            alt={visitante.produto || "Produto"}
                            width={20}
                            height={20}
                            unoptimized
                          />
                        </span>
                      ) : null}
                      <span>
                        {visitante.produto
                          ? `${visitante.produto}${visitante.preco ? ` · ${dinheiro(visitante.preco)}` : ""}`
                          : visitante.pagina}
                      </span>
                      {visitante.carrinhoItens > 0 ? (
                        <span className="chat-marca-carrinho">
                          <ShoppingCart size={10} />
                          {visitante.carrinhoItens}
                        </span>
                      ) : null}
                      {visitante.threadId ? <span className="chat-marca-visitas">já falou</span> : null}
                    </div>
                  </div>
                </button>
              ))
            )
          ) : conversas.length === 0 ? (
            <div className="chat-vazio">
              {busca
                ? "Nada encontrado com esse termo."
                : aba === "loja"
                  ? "Nenhuma conversa ainda. Elas começam quando você ou o cliente escrevem dentro de um pedido."
                  : "Nenhuma conversa interna. Toque no ícone acima para falar com alguém da equipe."}
            </div>
          ) : (
            conversas.map((conversa) => (
              <button
                key={conversa.id}
                type="button"
                className="chat-item"
                data-ativo={conversa.id === abertaId ? "sim" : "nao"}
                data-naolidas={conversa.naoLidas > 0 ? "sim" : "nao"}
                onClick={() => abrirConversa(conversa.id)}
              >
                <div
                  className="chat-avatar"
                  data-boneco={!conversa.avatar && conversa.kind === "visitor" ? "sim" : "nao"}
                >
                  {conversa.avatar ? (
                    <Image src={conversa.avatar} alt={conversa.nome} width={42} height={42} unoptimized />
                  ) : conversa.kind === "visitor" ? (
                    bonequinho()
                  ) : (
                    iniciais(conversa.nome)
                  )}
                </div>
                <div className="chat-item-corpo">
                  <div className="chat-item-linha">
                    <div className="chat-item-nome">{conversa.nome}</div>
                    {/* Esperando resposta, a hora dá lugar ao tempo de espera —
                        é o que faz a pessoa abrir aquela conversa primeiro. */}
                    <div className="chat-item-hora" data-espera={conversa.naoLidas > 0 ? "sim" : "nao"}>
                      {conversa.naoLidas > 0
                        ? esperaEmPalavras(conversa.ultimaMensagemEm)
                        : quandoNaLista(conversa.ultimaMensagemEm)}
                    </div>
                  </div>
                  <div className="chat-item-previa">
                    <span>
                      {/* Achado pela busca dentro das mensagens: mostra o trecho,
                          e não a última linha — senão a pessoa não entende por que
                          aquela conversa apareceu. */}
                      {conversa.trecho ? (
                        <em className="chat-trecho">“{conversa.trecho}”</em>
                      ) : (
                        <>
                          {conversa.kind !== "team" && conversa.ultimoAutor && conversa.ultimoAutor !== "client"
                            ? "Você: "
                            : ""}
                          {conversa.ultimaMensagem || conversa.detalhe || "Conversa aberta"}
                        </>
                      )}
                    </span>
                    {conversa.retomarEm ? (
                      <span className="chat-marca-retomar" title={conversa.retomarNota || "Retomar"}>
                        <CalendarClock size={10} />
                        {new Date(conversa.retomarEm).toLocaleDateString("pt-BR", { day: "2-digit", month: "2-digit" })}
                      </span>
                    ) : null}
                    {conversa.kind === "visitor" && conversa.visitas > 1 ? (
                      <span className="chat-marca-visitas">{conversa.visitas}ª</span>
                    ) : null}
                    {conversa.naoLidas > 0 ? <span className="chat-bolha">{conversa.naoLidas}</span> : null}
                  </div>
                </div>
              </button>
            ))
          )}
        </div>
      </section>

      {/* ---------------- fio ---------------- */}
      <section className="chat-fio">
        {subAba === "visitantes" ? (
          visitanteAberto ? (
            /*
              A ficha do visitante e uma conversa que ainda nao comecou.

              Por isso ela usa a mesma casca: o mesmo cabecalho, o mesmo fundo de
              papel, o mesmo campo de escrever embaixo. O que muda e o conteudo —
              no lugar dos baloes, o que a pessoa esta vendo agora.
            */
            <>
              <header className="chat-fio-topo">
                <button
                  type="button"
                  className="chat-icone chat-voltar"
                  onClick={() => setVisitanteAberto(null)}
                  aria-label="Voltar"
                >
                  <ArrowLeft size={18} />
                </button>

                <div className="chat-avatar" data-boneco="sim" style={{ height: 38, width: 38, borderRadius: 12 }}>
                  <span className="chat-ponto-vivo" />
                  {bonequinho(18)}
                </div>

                <div style={{ minWidth: 0 }}>
                  <div className="chat-fio-nome">
                    {visitanteAberto.nome || "Visitante"}
                    <span className="chat-selo-temperatura" data-tom={visitanteAberto.temperatura}>
                      {TEMPERATURAS[visitanteAberto.temperatura] || "Frio"}
                    </span>
                  </div>
                  <div className="chat-fio-detalhe">
                    Na loja há {visitanteAberto.minutosNaLoja} min · {visitanteAberto.paginasVistas}{" "}
                    {visitanteAberto.paginasVistas === 1 ? "página" : "páginas"}
                    {visitanteAberto.chegouPor ? ` · veio do ${visitanteAberto.chegouPor}` : ""}
                  </div>
                </div>

                <div className="chat-fio-acoes">
                  {visitanteAberto.threadId ? (
                    <button
                      type="button"
                      className="chat-icone"
                      title="Abrir a conversa que já existe"
                      onClick={() => {
                        const alvoDaConversa = visitanteAberto.threadId as string;
                        setSubAba("conversas");
                        setVisitanteAberto(null);
                        void carregarConversas();
                        abrirConversa(alvoDaConversa);
                      }}
                    >
                      <MessageCircle size={17} />
                    </button>
                  ) : null}
                  <button
                    type="button"
                    className="chat-icone"
                    title="Rota da visita"
                    onClick={() => setPainelAberto((antes) => !antes)}
                  >
                    <Info size={17} />
                  </button>
                </div>
              </header>

              <div className="chat-mensagens">
                <div className="chat-vitrine">
                  <div className="chat-vitrine-foto">
                    {visitanteAberto.imagem ? (
                      <Image
                        src={visitanteAberto.imagem}
                        alt={visitanteAberto.produto || "Produto"}
                        width={220}
                        height={220}
                        unoptimized
                      />
                    ) : (
                      <Eye size={26} />
                    )}
                    <span className="chat-vitrine-vivo">vendo agora</span>
                  </div>
                  <div className="chat-vitrine-corpo">
                    <div className="chat-vitrine-nome">{visitanteAberto.produto || visitanteAberto.pagina}</div>
                    {visitanteAberto.preco ? (
                      <div className="chat-vitrine-preco">{dinheiro(visitanteAberto.preco)}</div>
                    ) : (
                      <div className="chat-vitrine-caminho">{visitanteAberto.caminho || "Navegando pela loja"}</div>
                    )}
                    {visitanteAberto.voltasNoProduto > 1 ? (
                      <div className="chat-vitrine-marca">
                        {visitanteAberto.voltasNoProduto}ª vez neste produto hoje
                      </div>
                    ) : null}
                    {visitanteAberto.carrinhoItens > 0 ? (
                      <div className="chat-vitrine-carrinho">
                        <ShoppingCart size={13} />
                        {visitanteAberto.carrinhoItens}{" "}
                        {visitanteAberto.carrinhoItens === 1 ? "item no carrinho" : "itens no carrinho"} ·{" "}
                        {dinheiro(visitanteAberto.carrinhoTotal)}
                      </div>
                    ) : null}
                  </div>
                </div>

                <div className="chat-leitura" data-tom={visitanteAberto.temperatura}>
                  <Flame size={14} />
                  <span>
                    <strong>{TEMPERATURAS[visitanteAberto.temperatura] || "Frio"}</strong> — {visitanteAberto.motivo}.
                  </span>
                </div>

                {/* Atalhos de abordagem: o que trava a pessoa do balcao e a
                    primeira frase, e frase generica soa como robo. */}
                <div className="chat-sugestoes">
                  {[
                    visitanteAberto.produto
                      ? `Oi! Vi que você está olhando ${visitanteAberto.produto}. Posso ajudar em alguma dúvida?`
                      : "Oi! Precisa de ajuda para escolher? Estou por aqui.",
                    visitanteAberto.carrinhoItens > 0
                      ? "Vi que você montou o carrinho. Quer que eu confira o prazo de entrega antes de fechar?"
                      : "Quer que eu faça um preço para quantidade maior?",
                    "Tem alguma dúvida sobre prazo de produção?",
                  ].map((frase) => (
                    <button key={frase} type="button" onClick={() => setAbordagem(frase)}>
                      {frase}
                    </button>
                  ))}
                </div>
              </div>

              <div className="chat-escrever">
                {erro ? <div className="chat-erro">{erro}</div> : null}
                <div className="chat-caixa">
                  <textarea
                    value={abordagem}
                    onChange={(evento) => setAbordagem(evento.target.value.slice(0, 400))}
                    onKeyDown={(evento) => {
                      if (evento.key === "Enter" && !evento.shiftKey) {
                        evento.preventDefault();
                        void puxarConversa();
                      }
                    }}
                    rows={1}
                    placeholder="Escreva para puxar conversa"
                  />
                  <button
                    type="button"
                    className="chat-enviar"
                    onClick={() => void puxarConversa()}
                    disabled={!abordagem.trim() || abordando}
                    aria-label="Puxar conversa"
                  >
                    <Send size={16} />
                  </button>
                </div>
              </div>
            </>
          ) : (
            <div
              style={{
                display: "flex",
                flex: 1,
                flexDirection: "column",
                alignItems: "center",
                justifyContent: "center",
                gap: 10,
                color: "var(--tinta-fraca)",
                textAlign: "center",
                padding: 24,
              }}
            >
              <div className="chat-rail-marca" style={{ margin: 0, height: 52, width: 52 }}>
                <Image src="/brand-symbol.svg" alt="Ktalogu" width={32} height={32} />
              </div>
              <div style={{ fontSize: 15, fontWeight: 700, color: "var(--tinta)" }}>Movimento da loja</div>
              <div style={{ fontSize: 13, maxWidth: 340 }}>
                Escolha alguém da lista para ver o que está olhando e puxar conversa antes de a pessoa fechar a aba.
              </div>
            </div>
          )
        ) : !abertaId ? (
          <div
            style={{
              display: "flex",
              flex: 1,
              flexDirection: "column",
              alignItems: "center",
              justifyContent: "center",
              gap: 10,
              color: "var(--tinta-fraca)",
              textAlign: "center",
              padding: 24,
            }}
          >
            <div className="chat-rail-marca" style={{ margin: 0, height: 52, width: 52 }}>
                <Image src="/brand-symbol.svg" alt="Ktalogu" width={32} height={32} />
            </div>
            <div style={{ fontSize: 15, fontWeight: 700, color: "var(--tinta)" }}>Escolha uma conversa</div>
            <div style={{ fontSize: 13, maxWidth: 320 }}>
              Tudo o que a loja e a equipe falam fica aqui, junto do pedido de onde a conversa nasceu.
            </div>
          </div>
        ) : (
          <>
            <header className="chat-fio-topo">
              <button
                type="button"
                className="chat-icone chat-voltar"
                onClick={() => {
                  setAbertaId(null);
                  abertaRef.current = null;
                }}
              >
                <ArrowLeft size={18} />
              </button>

              <div
                className="chat-avatar"
                data-boneco={!cabecalho?.avatar && cabecalho?.kind === "visitor" ? "sim" : "nao"}
                style={{ height: 38, width: 38, borderRadius: 12 }}
              >
                {cabecalho?.avatar ? (
                  <Image src={cabecalho.avatar} alt={cabecalho.contato} width={38} height={38} unoptimized />
                ) : cabecalho?.kind === "visitor" ? (
                  bonequinho(18)
                ) : (
                  iniciais(cabecalho?.contato)
                )}
              </div>

              <div style={{ minWidth: 0 }}>
                <div className="chat-fio-nome">
                  {cabecalho?.contato || "Conversa"}
                  {cabecalho?.encerrada ? <span className="chat-selo-encerrada">Encerrada</span> : null}
                </div>
                <div className="chat-fio-detalhe">
                  {digitando ? (
                    <span className="chat-digitando">
                      {digitando} está digitando
                      <i />
                      <i />
                      <i />
                    </span>
                  ) : cabecalho?.pedido ? (
                    `Pedido #${cabecalho.pedido.number} · ${STATUS_DO_PEDIDO[String(cabecalho.pedido.status)] || "Pedido"}`
                  ) : cabecalho?.orcamento ? (
                    `Orçamento #${cabecalho.orcamento.number} · ${
                      STATUS_DO_ORCAMENTO[String(cabecalho.orcamento.status)] || "Orçamento"
                    }`
                  ) : (
                    cabecalho?.detalhe || ""
                  )}
                </div>
              </div>

              <div className="chat-fio-acoes">
                {cabecalho?.quoteId ? (
                  <a
                    className="chat-icone"
                    href={`/dashboard/quotes/${cabecalho.quoteId}`}
                    target="_blank"
                    rel="noreferrer"
                    title="Abrir o orçamento"
                  >
                    <ExternalLink size={17} />
                  </a>
                ) : null}
                {cabecalho?.orderId ? (
                  <a
                    className="chat-icone"
                    href={`/dashboard/orders/${cabecalho.orderId}`}
                    target="_blank"
                    rel="noreferrer"
                    title="Abrir o pedido"
                  >
                    <ExternalLink size={17} />
                  </a>
                ) : null}
                <button
                  type="button"
                  className="chat-icone"
                  title="Detalhes"
                  onClick={() => setPainelAberto((antes) => !antes)}
                >
                  <Info size={17} />
                </button>
              </div>
            </header>

            <div className="chat-mensagens">
              {/* O começo da conversa fica a um clique: uma conversa antiga não
                  pode perder o combinado que está lá atrás. */}
              {mensagens.length >= 100 && temMais ? (
                <div className="chat-carregar">
                  <button type="button" disabled={carregandoAntigas} onClick={() => void carregarAntigas()}>
                    {carregandoAntigas ? "Buscando..." : "Ver mensagens anteriores"}
                  </button>
                </div>
              ) : null}

              {carregandoFio && mensagens.length === 0 ? (
                <div className="chat-vazio">Abrindo a conversa...</div>
              ) : mensagens.length === 0 ? (
                <div className="chat-vazio">Nenhuma mensagem ainda. Escreva abaixo para começar.</div>
              ) : (
                mensagens.map((mensagem, indice) => {
                  const minha = ehMinha(mensagem);
                  const dia = rotuloDoDia(mensagem.created_at);
                  const mostrarDia = dia !== ultimoDia;
                  ultimoDia = dia;

                  const anterior = indice > 0 ? mensagens[indice - 1] : null;
                  const seguinte = indice < mensagens.length - 1 ? mensagens[indice + 1] : null;
                  const perto = (a?: Mensagem | null, b?: Mensagem | null) =>
                    Boolean(
                      a &&
                        b &&
                        ehMinha(a) === ehMinha(b) &&
                        Math.abs(new Date(b.created_at).getTime() - new Date(a.created_at).getTime()) <
                          INTERVALO_DE_AGRUPAMENTO_MS
                    );
                  const junto = !mostrarDia && perto(anterior, mensagem);
                  const fecha = !perto(mensagem, seguinte);
                  const apagada = Boolean(mensagem.deleted_at);

                  return (
                    <div key={mensagem.id}>
                      {mostrarDia ? (
                        <div className="chat-dia">
                          <span>{dia}</span>
                        </div>
                      ) : null}

                      <div
                        className="chat-linha"
                        data-minha={minha ? "sim" : "nao"}
                        data-junto={junto ? "sim" : "nao"}
                        data-fecha={fecha ? "sim" : "nao"}
                      >
                        {!minha ? (
                          <div className="chat-linha-avatar">
                            {fecha ? <span>{iniciais(mensagem.sender_name)}</span> : null}
                          </div>
                        ) : null}

                        <div
                          className="chat-balao"
                          data-pendente={mensagem.pendente ? "sim" : "nao"}
                          data-interna={mensagem.internal ? "sim" : "nao"}
                          onContextMenu={(evento) => {
                            if (!minha || apagada || mensagem.pendente || !cabecalho?.podeResponder) return;
                            evento.preventDefault();
                            setMenuAberto(mensagem.id);
                          }}
                        >
                          {apagada ? (
                            <span className="chat-balao-apagada">Esta mensagem foi apagada</span>
                          ) : (
                            <>
                              {mensagem.internal ? (
                                <div className="chat-selo-interno">
                                  <StickyNote size={11} />
                                  Só a equipe vê
                                </div>
                              ) : null}

                              {/* O trecho citado, como em qualquer aplicativo de
                                  mensagem: sem ele, "isso mesmo" não quer dizer nada. */}
                              {mensagem.respondendo ? (
                                <div className="chat-citacao">
                                  <span>{mensagem.respondendo.quem}</span>
                                  <em>{mensagem.respondendo.resumo}</em>
                                </div>
                              ) : null}

                              {mensagem.attachment_url && mensagem.attachment_kind === "image" ? (
                                <button
                                  type="button"
                                  className="chat-anexo-imagem"
                                  onClick={() =>
                                    setFotoAberta({
                                      url: mensagem.attachment_url || "",
                                      nome: mensagem.attachment_name,
                                    })
                                  }
                                >
                                  <Image
                                    src={mensagem.attachment_url}
                                    alt={mensagem.attachment_name || "Anexo"}
                                    width={420}
                                    height={420}
                                    unoptimized
                                  />
                                </button>
                              ) : null}

                              {mensagem.attachment_url && mensagem.attachment_kind !== "image" ? (
                                <a
                                  className="chat-anexo-arquivo"
                                  href={mensagem.attachment_url}
                                  target="_blank"
                                  rel="noreferrer"
                                >
                                  <FileText size={15} />
                                  <span>{mensagem.attachment_name || "Arquivo"}</span>
                                </a>
                              ) : null}

                              {mensagem.card ? (
                                mensagem.card.tipo === "cupom" ? (
                                  <div className="chat-cartao" data-tipo="cupom">
                                    <div className="chat-cartao-rotulo">Cupom de desconto</div>
                                    <div className="chat-cartao-codigo">
                                      {mensagem.card.codigo || mensagem.card.titulo}
                                    </div>
                                    {mensagem.card.subtitulo ? (
                                      <div className="chat-cartao-condicao">{mensagem.card.subtitulo}</div>
                                    ) : null}
                                  </div>
                                ) : (
                                  <a
                                    className="chat-cartao"
                                    data-tipo="produto"
                                    href={mensagem.card.link || "#"}
                                    target="_blank"
                                    rel="noreferrer"
                                  >
                                    {mensagem.card.imagem ? (
                                      <Image
                                        src={mensagem.card.imagem}
                                        alt={mensagem.card.titulo || "Produto"}
                                        width={56}
                                        height={56}
                                        unoptimized
                                      />
                                    ) : null}
                                    <span>
                                      <span className="chat-cartao-nome">{mensagem.card.titulo}</span>
                                      <span className="chat-cartao-preco">
                                        {mensagem.card.preco ? dinheiro(mensagem.card.preco) : "sob consulta"}
                                      </span>
                                    </span>
                                  </a>
                                )
                              ) : null}

                              {mensagem.body ? <p>{comLinks(mensagem.body)}</p> : null}

                              {mensagem.is_art ? (
                                <div className="chat-selo" data-arte={mensagem.art_status || "pending"}>
                                  {ARTE[String(mensagem.art_status || "pending")]}
                                </div>
                              ) : null}

                              {mensagem.art_decision_note ? (
                                <p style={{ marginTop: 4, fontSize: 11.5, opacity: 0.8 }}>
                                  “{mensagem.art_decision_note}”
                                </p>
                              ) : null}
                            </>
                          )}

                          {fecha ? (
                            <div className="chat-rodape-balao">
                              {mensagem.edited_at ? <span className="chat-editada">editada</span> : null}
                              {hora(mensagem.created_at)}
                              {mensagem.pendente ? (
                                <Clock3 size={12} />
                              ) : minha ? (
                                mensagem.lida_pelo_outro ? (
                                  <CheckCheck size={13} className="chat-lida" />
                                ) : (
                                  <CheckCheck size={13} />
                                )
                              ) : (
                                <Check size={13} />
                              )}
                            </div>
                          ) : null}

                          {!apagada && !mensagem.pendente && cabecalho?.podeResponder ? (
                            <button
                              type="button"
                              className="chat-menu-abrir"
                              aria-label="Opções da mensagem"
                              onClick={(evento) => {
                                evento.stopPropagation();
                                setMenuAberto(menuAberto === mensagem.id ? null : mensagem.id);
                              }}
                            >
                              <Reply size={13} />
                            </button>
                          ) : null}

                          {menuAberto === mensagem.id ? (
                            <div className="chat-menu" onClick={(evento) => evento.stopPropagation()}>
                              <button
                                type="button"
                                onClick={() => {
                                  setCitando(mensagem);
                                  setEditando(null);
                                  setMenuAberto(null);
                                }}
                              >
                                <Reply size={14} />
                                Responder citando
                              </button>

                              {minha && mensagem.body ? (
                                <button
                                  type="button"
                                  onClick={() => {
                                    setEditando(mensagem);
                                    setCitando(null);
                                    setTexto(mensagem.body || "");
                                    setMenuAberto(null);
                                  }}
                                >
                                  <Pencil size={14} />
                                  Editar
                                </button>
                              ) : null}

                              {minha ? (
                                <button
                                  type="button"
                                  className="chat-menu-perigo"
                                  onClick={() => pedirParaApagar(mensagem.id)}
                                >
                                  <Trash2 size={14} />
                                  Apagar para todos
                                </button>
                              ) : null}
                            </div>
                          ) : null}
                        </div>
                      </div>
                    </div>
                  );
                })
              )}
              <div ref={fimDoFio} />
            </div>

            {cabecalho && !cabecalho.podeResponder ? (
              <div className="chat-escrever">
                <div className="chat-vazio" style={{ padding: "10px 0" }}>
                  Você pode ler esta conversa, mas não tem permissão para responder.
                </div>
              </div>
            ) : (
              <div className="chat-escrever">
                {arquivo ? (
                  <div className="chat-anexo-preview">
                    <Paperclip size={14} />
                    <span style={{ maxWidth: 240, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>
                      {arquivo.name}
                    </span>
                    {arquivo.type.startsWith("image/") && cabecalho?.kind !== "team" ? (
                      <label>
                        <input
                          type="checkbox"
                          checked={comoArte}
                          onChange={(evento) => setComoArte(evento.target.checked)}
                        />
                        Enviar como arte para aprovação
                      </label>
                    ) : null}
                    <button
                      type="button"
                      className="chat-icone"
                      style={{ marginLeft: "auto", height: 26, width: 26 }}
                      onClick={() => {
                        setArquivo(null);
                        setComoArte(false);
                        if (campoDeArquivo.current) campoDeArquivo.current.value = "";
                      }}
                      aria-label="Remover anexo"
                    >
                      <X size={14} />
                    </button>
                  </div>
                ) : null}

                {erro ? <div className="chat-erro">{erro}</div> : null}

                {/* O que este envio é: resposta a alguém, correção, ou recado
                    que o cliente não vai ler. Fica à vista até sair. */}
                {citando ? (
                  <div className="chat-faixa" data-tom="citacao">
                    <Reply size={13} />
                    <span>
                      <strong>Respondendo {citando.sender_name || "a mensagem"}</strong>
                      <em>{String(citando.body || "").slice(0, 90) || "Mensagem"}</em>
                    </span>
                    <button type="button" onClick={() => setCitando(null)} aria-label="Cancelar">
                      <X size={13} />
                    </button>
                  </div>
                ) : null}

                {editando ? (
                  <div className="chat-faixa" data-tom="edicao">
                    <Pencil size={13} />
                    <span>
                      <strong>Editando uma mensagem já enviada</strong>
                      <em>Ela vai ficar marcada como editada.</em>
                    </span>
                    <button
                      type="button"
                      onClick={() => {
                        setEditando(null);
                        setTexto("");
                      }}
                      aria-label="Cancelar edição"
                    >
                      <X size={13} />
                    </button>
                  </div>
                ) : null}

                {notaInterna ? (
                  <div className="chat-faixa" data-tom="interna">
                    <StickyNote size={13} />
                    <span>
                      <strong>Recado interno</strong>
                      <em>Fica na conversa para a equipe. O cliente não vê.</em>
                    </span>
                    <button type="button" onClick={() => setNotaInterna(false)} aria-label="Cancelar">
                      <X size={13} />
                    </button>
                  </div>
                ) : null}

                <div className="chat-caixa-area" data-interna={notaInterna ? "sim" : "nao"}>
                  {/*
                    A barra "/".

                    Abre para cima, como em qualquer aplicativo de mensagem, e se
                    navega no teclado: seta, Enter, Esc. Quem prefere o mouse
                    clica; quem tem pressa nunca tira a mão do teclado.
                  */}
                  {atalhoAberto || comando ? (
                    <div className="chat-atalhos" onClick={(evento) => evento.stopPropagation()}>
                      <div className="chat-atalhos-topo">
                        <span>
                          {comando === "produto"
                            ? "Escolha o produto"
                            : comando === "cupom"
                              ? "Escolha o cupom"
                              : comando === "resposta"
                                ? "Suas respostas prontas"
                                : "Ações rápidas"}
                        </span>
                        {comando === "resposta" ? (
                          <button type="button" onClick={() => setEditandoRespostas(true)}>
                            <Pencil size={12} />
                            Editar
                          </button>
                        ) : (
                          <span className="chat-atalhos-dica">
                            {comando ? "Esc para voltar" : "digite para filtrar"}
                          </span>
                        )}
                      </div>

                      <div className="chat-atalhos-lista">
                        {carregandoAtalho && listaDoAtalho.length === 0 ? (
                          <div className="chat-atalhos-vazio">Procurando...</div>
                        ) : listaDoAtalho.length === 0 ? (
                          <div className="chat-atalhos-vazio">
                            {comando === "produto"
                              ? "Nenhum produto com esse nome."
                              : comando === "cupom"
                                ? "Nenhum cupom valendo agora. Cadastre em Catálogo › Marketing."
                                : comando === "resposta"
                                  ? "Nenhuma frase salva ainda."
                                  : "Nenhuma ação com esse nome."}
                          </div>
                        ) : (
                          listaDoAtalho.map((item, indice) => {
                            const escolhido = indice === indiceDoAtalho;

                            if (!comando) {
                              const acao = item as unknown as Comando;
                              return (
                                <button
                                  key={acao.chave}
                                  type="button"
                                  data-escolhido={escolhido ? "sim" : "nao"}
                                  onMouseEnter={() => setIndiceDoAtalho(indice)}
                                  onClick={() => void escolherComando(acao)}
                                >
                                  <span className="chat-atalho-icone">{acao.icone}</span>
                                  <span className="chat-atalho-corpo">
                                    <span className="chat-atalho-nome">
                                      {acao.rotulo}
                                      <em>/{acao.chave}</em>
                                    </span>
                                    <span className="chat-atalho-dica">{acao.dica}</span>
                                  </span>
                                </button>
                              );
                            }

                            return (
                              <button
                                key={String(item.id || item.atalho || indice)}
                                type="button"
                                data-escolhido={escolhido ? "sim" : "nao"}
                                onMouseEnter={() => setIndiceDoAtalho(indice)}
                                onClick={() => void escolherItem(item)}
                              >
                                {comando === "produto" ? (
                                  <span className="chat-atalho-foto">
                                    {item.imagem ? (
                                      <Image
                                        src={String(item.imagem)}
                                        alt={String(item.nome || "")}
                                        width={34}
                                        height={34}
                                        unoptimized
                                      />
                                    ) : (
                                      <Package size={15} />
                                    )}
                                  </span>
                                ) : (
                                  <span className="chat-atalho-icone">
                                    {comando === "cupom" ? <Ticket size={15} /> : <Zap size={15} />}
                                  </span>
                                )}

                                <span className="chat-atalho-corpo">
                                  <span className="chat-atalho-nome">
                                    {comando === "produto"
                                      ? String(item.nome || "")
                                      : comando === "cupom"
                                        ? String(item.codigo || "")
                                        : String(item.atalho || "")}
                                    {comando === "produto" && item.preco ? (
                                      <em>{dinheiro(Number(item.preco))}</em>
                                    ) : null}
                                  </span>
                                  <span className="chat-atalho-dica">
                                    {comando === "produto"
                                      ? item.preco
                                        ? "manda com foto e preço"
                                        : "sob consulta"
                                      : comando === "cupom"
                                        ? condicoesDoCupom({
                                            tipo: String(item.tipo || ""),
                                            valor: Number(item.valor || 0),
                                            minimo:
                                              item.minimo === null || item.minimo === undefined
                                                ? null
                                                : Number(item.minimo),
                                            vence: (item.vence as string) || null,
                                          })
                                        : String(item.texto || "")}
                                  </span>
                                </span>
                              </button>
                            );
                          })
                        )}
                      </div>

                      {comando === "resposta" && respostasSugeridas ? (
                        <div className="chat-atalhos-rodape">
                          Estas são sugestões nossas. Toque em <strong>Editar</strong> e escreva as suas.
                        </div>
                      ) : null}
                    </div>
                  ) : null}

                  {emojisAbertos ? <PainelDeEmojis aoEscolher={(emoji) => setTexto((antes) => antes + emoji)} /> : null}

                  <div className="chat-caixa">
                    <input
                      ref={campoDeArquivo}
                      type="file"
                      hidden
                      onChange={(evento) => {
                        const escolhido = evento.target.files?.[0] || null;
                        setArquivo(escolhido);
                        setComoArte(Boolean(escolhido?.type.startsWith("image/")) && cabecalho?.kind !== "team");
                      }}
                    />
                    {cabecalho?.kind === "quote" ? null : (
                      <button
                        type="button"
                        className="chat-icone"
                        onClick={() => campoDeArquivo.current?.click()}
                        aria-label="Anexar arquivo"
                      >
                        <Paperclip size={17} />
                      </button>
                    )}

                    <button
                      type="button"
                      className="chat-icone"
                      data-ativo={emojisAbertos ? "sim" : "nao"}
                      onClick={(evento) => {
                        evento.stopPropagation();
                        setEmojisAbertos((antes) => !antes);
                      }}
                      aria-label="Emojis"
                    >
                      <Smile size={17} />
                    </button>

                    {comando ? (
                      <span className="chat-comando-chip">
                        {COMANDOS.find((item) => item.chave === comando)?.rotulo}
                        <button type="button" onClick={fecharAtalhos} aria-label="Cancelar ação">
                          <X size={11} />
                        </button>
                      </span>
                    ) : null}

                    <textarea
                      value={texto}
                      onChange={(evento) => digitou(evento.target.value)}
                      onKeyDown={(evento) => {
                        const menuVisivel = atalhoAberto || Boolean(comando);

                        if (menuVisivel) {
                          if (evento.key === "ArrowDown") {
                            evento.preventDefault();
                            setIndiceDoAtalho((antes) => Math.min(antes + 1, Math.max(listaDoAtalho.length - 1, 0)));
                            return;
                          }
                          if (evento.key === "ArrowUp") {
                            evento.preventDefault();
                            setIndiceDoAtalho((antes) => Math.max(antes - 1, 0));
                            return;
                          }
                          if (evento.key === "Escape") {
                            evento.preventDefault();
                            fecharAtalhos();
                            if (!comando) setTexto("");
                            return;
                          }
                          if (evento.key === "Backspace" && comando && !texto) {
                            evento.preventDefault();
                            fecharAtalhos();
                            return;
                          }
                          if (evento.key === "Enter" && !evento.shiftKey && listaDoAtalho.length) {
                            evento.preventDefault();
                            const alvo = listaDoAtalho[Math.min(indiceDoAtalho, listaDoAtalho.length - 1)];
                            if (comando) void escolherItem(alvo);
                            else void escolherComando(alvo as unknown as Comando);
                            return;
                          }
                        }

                        if (evento.key === "Enter" && !evento.shiftKey) {
                          evento.preventDefault();
                          void enviar();
                        }
                      }}
                      rows={1}
                      placeholder={
                        comando === "produto"
                          ? "Procurar produto"
                          : comando === "cupom"
                            ? "Procurar cupom"
                            : comando === "resposta"
                              ? "Procurar frase"
                              : "Escreva uma mensagem — ou / para as ações"
                      }
                    />
                    <button
                      type="button"
                      className="chat-enviar"
                      onClick={() => void enviar()}
                      disabled={!texto.trim() && !arquivo}
                      aria-label="Enviar"
                    >
                      <Send size={16} />
                    </button>
                  </div>
                </div>
              </div>
            )}
          </>
        )}
      </section>

      {/* ---------------- contexto ---------------- */}
      <aside className="chat-contexto">
        {visitanteAberto ? (
          <>
            <div className="chat-contexto-capa">
              <div className="chat-avatar" data-boneco="sim">
                {bonequinho(28)}
              </div>
              <div>
                <div style={{ fontSize: 15, fontWeight: 800 }}>{visitanteAberto.nome || "Visitante"}</div>
                <div style={{ fontSize: 12, color: "var(--tinta-fraca)" }}>
                  {visitanteAberto.clienteId ? "Já é cliente da casa" : "Ainda sem cadastro"}
                </div>
              </div>
              <button
                type="button"
                className="chat-icone"
                style={{ position: "absolute", top: 12, right: 12 }}
                onClick={() => setPainelAberto(false)}
                aria-label="Fechar detalhes"
              >
                <X size={16} />
              </button>
            </div>

            {linhaDoTempo}

            {visitanteAberto.threadId ? (
              <div className="chat-secao">
                <button
                  type="button"
                  className="chat-botao-linha"
                  onClick={() => {
                    const alvoDaConversa = visitanteAberto.threadId as string;
                    setSubAba("conversas");
                    setVisitanteAberto(null);
                    void carregarConversas();
                    abrirConversa(alvoDaConversa);
                  }}
                >
                  <MessageCircle size={15} />
                  Abrir a conversa que já existe
                </button>
              </div>
            ) : null}

            {visitanteAberto.clienteId ? (
              <div className="chat-secao">
                <a
                  className="chat-botao-linha"
                  href={`/dashboard/clients/${visitanteAberto.clienteId}`}
                  target="_blank"
                  rel="noreferrer"
                >
                  <ExternalLink size={15} />
                  Ver a ficha do cliente
                </a>
              </div>
            ) : null}
          </>
        ) : !cabecalho ? (
          <div className="chat-vazio" style={{ paddingTop: 60 }}>
            Os detalhes da conversa aparecem aqui.
          </div>
        ) : (
          <>
            <div className="chat-contexto-capa">
              <div
                className="chat-avatar"
                data-boneco={!cabecalho.avatar && cabecalho.kind === "visitor" ? "sim" : "nao"}
              >
                {cabecalho.avatar ? (
                  <Image src={cabecalho.avatar} alt={cabecalho.contato} width={66} height={66} unoptimized />
                ) : cabecalho.kind === "visitor" ? (
                  bonequinho(28)
                ) : (
                  iniciais(cabecalho.contato)
                )}
              </div>
              <div>
                <div style={{ fontSize: 15, fontWeight: 800 }}>{cabecalho.contato}</div>
                <div style={{ fontSize: 12, color: "var(--tinta-fraca)" }}>{cabecalho.detalhe}</div>
              </div>
              <button
                type="button"
                className="chat-icone"
                style={{ position: "absolute", top: 12, right: 12 }}
                onClick={() => setPainelAberto(false)}
                aria-label="Fechar detalhes"
              >
                <X size={16} />
              </button>
            </div>

            {/*
              Em que pé está.

              É o funil sem tela de funil: a etiqueta mora na conversa, então
              ninguém precisa manter dois lugares atualizados. Clicar de novo na
              mesma etiqueta tira a marca.
            */}
            {cabecalho.podeResponder ? (
              <div className="chat-secao">
                <div className="chat-secao-titulo">Em que pé está</div>
                <div className="chat-estagios">
                  {ESTAGIOS.map((estagio) => (
                    <button
                      key={estagio.chave}
                      type="button"
                      data-ativo={cabecalho.estagio === estagio.chave ? "sim" : "nao"}
                      data-estagio={estagio.chave}
                      disabled={mudandoEstagio}
                      onClick={() => void mudarEstagio(estagio.chave)}
                    >
                      {estagio.rotulo}
                    </button>
                  ))}
                </div>
              </div>
            ) : null}

            {/*
              Quem está atendendo.

              Duas pessoas respondendo a mesma dúvida, cada uma com um prazo
              diferente, é o jeito mais rápido de perder a confiança do cliente.
            */}
            {cabecalho.podeResponder && cabecalho.kind !== "team" ? (
              <div className="chat-secao">
                <div className="chat-secao-titulo">Quem está atendendo</div>
                <div className="chat-responsavel">
                  <select
                    value={cabecalho.responsavel || ""}
                    onChange={(evento) => {
                      const escolhido = evento.target.value;
                      const pessoa =
                        escolhido === eu?.id
                          ? { id: eu?.id, nome: eu?.nome }
                          : equipeDaLoja.find((colega) => colega.id === escolhido);
                      void mudarConversa({
                        responsavel: escolhido ? { id: escolhido, nome: pessoa?.nome || "" } : {},
                      });
                    }}
                  >
                    <option value="">Ninguém ainda</option>
                    {eu ? <option value={eu.id}>{eu.nome} (eu)</option> : null}
                    {equipeDaLoja
                      .filter((colega) => colega.id !== eu?.id)
                      .map((colega) => (
                        <option key={colega.id} value={colega.id}>
                          {colega.nome}
                        </option>
                      ))}
                  </select>
                  {cabecalho.responsavel && cabecalho.responsavel !== eu?.id ? (
                    <button
                      type="button"
                      onClick={() => void mudarConversa({ responsavel: { id: eu?.id, nome: eu?.nome } })}
                    >
                      Assumir
                    </button>
                  ) : null}
                </div>
              </div>
            ) : null}

            {/*
              Retomar depois.

              A venda de gráfica não morre no "não": morre no silêncio depois do
              orçamento. Marcar o dia é o que traz a conversa de volta ao topo
              sozinha — e some assim que você responde, porque aí já retomou.
            */}
            {cabecalho.podeResponder && cabecalho.kind !== "team" ? (
              <div className="chat-secao">
                <div className="chat-secao-titulo">Retomar depois</div>

                {cabecalho.retomarEm ? (
                  <div className="chat-retomar-marcado">
                    <CalendarClock size={14} />
                    <span>
                      <strong>
                        {new Date(cabecalho.retomarEm).toLocaleString("pt-BR", {
                          day: "2-digit",
                          month: "2-digit",
                          hour: "2-digit",
                          minute: "2-digit",
                        })}
                      </strong>
                      {cabecalho.retomarNota ? <em>{cabecalho.retomarNota}</em> : null}
                    </span>
                    <button type="button" onClick={() => void marcarRetomar(null)} aria-label="Cancelar lembrete">
                      <X size={12} />
                    </button>
                  </div>
                ) : (
                  <>
                    <div className="chat-retomar">
                      <button type="button" onClick={() => void marcarRetomar(1)}>
                        Amanhã
                      </button>
                      <button type="button" onClick={() => void marcarRetomar(3)}>
                        Em 3 dias
                      </button>
                      <button type="button" onClick={() => void marcarRetomar(7)}>
                        Em 1 semana
                      </button>
                    </div>
                    <p style={{ marginTop: 6, fontSize: 11.5, color: "var(--tinta-fraca)", lineHeight: 1.45 }}>
                      No dia, a conversa volta ao topo e o sino toca. Some sozinho se você responder antes.
                    </p>
                  </>
                )}
              </div>
            ) : null}

            {/* Etiquetas: o funil diz em que pé está; a etiqueta diz o que é. */}
            {cabecalho.podeResponder && cabecalho.kind !== "team" ? (
              <div className="chat-secao">
                <div className="chat-secao-titulo">
                  <Tag size={11} style={{ marginRight: 4, verticalAlign: "-1px" }} />
                  Etiquetas
                </div>
                <div className="chat-etiquetas">
                  {cabecalho.etiquetas.map((etiqueta) => (
                    <span key={etiqueta} className="chat-etiqueta">
                      {etiqueta}
                      <button
                        type="button"
                        aria-label={`Tirar ${etiqueta}`}
                        onClick={() =>
                          void mudarConversa({
                            etiquetas: cabecalho.etiquetas.filter((item) => item !== etiqueta),
                          })
                        }
                      >
                        <X size={10} />
                      </button>
                    </span>
                  ))}
                  <input
                    placeholder="+ etiqueta"
                    onKeyDown={(evento) => {
                      if (evento.key !== "Enter") return;
                      evento.preventDefault();
                      const nova = evento.currentTarget.value.trim();
                      if (!nova) return;
                      evento.currentTarget.value = "";
                      void mudarConversa({ etiquetas: [...cabecalho.etiquetas, nova] });
                    }}
                  />
                </div>
              </div>
            ) : null}

            {/*
              O caminho do dinheiro.

              O papo já aconteceu; o que falta é virar negócio sem redigitar
              nada. O orçamento nasce rascunho — quem confere o preço é gente.
            */}
            {cabecalho.podeResponder && cabecalho.kind !== "team" && !cabecalho.orcamento ? (
              <div className="chat-secao">
                <div className="chat-secao-titulo">Virar negócio</div>
                {!cabecalho.clientId ? (
                  <button type="button" className="chat-botao-linha" onClick={() => void virarNegocio("cliente")}>
                    <UserPlus size={15} />
                    Cadastrar como cliente
                  </button>
                ) : null}
                <button type="button" className="chat-botao-linha" onClick={() => void virarNegocio("orcamento")}>
                  <FileText size={15} />
                  Criar orçamento desta conversa
                </button>
                <p style={{ marginTop: 4, fontSize: 11.5, color: "var(--tinta-fraca)", lineHeight: 1.45 }}>
                  Nasce como rascunho, já com o produto que a pessoa estava olhando. Nada é enviado sem você conferir.
                </p>
              </div>
            ) : null}

            {cabecalho.pedido ? (
              <div className="chat-secao">
                <div className="chat-secao-titulo">Pedido</div>
                <dl>
                  <div className="chat-dado">
                    <dt>Número</dt>
                    <dd>#{cabecalho.pedido.number}</dd>
                  </div>
                  <div className="chat-dado">
                    <dt>Situação</dt>
                    <dd>{STATUS_DO_PEDIDO[String(cabecalho.pedido.status)] || "—"}</dd>
                  </div>
                  <div className="chat-dado">
                    <dt>Total</dt>
                    <dd>{dinheiro(cabecalho.pedido.total_amount)}</dd>
                  </div>
                  {cabecalho.pedido.art_status ? (
                    <div className="chat-dado">
                      <dt>Arte</dt>
                      <dd>{ARTE[String(cabecalho.pedido.art_status)] || "—"}</dd>
                    </div>
                  ) : null}
                </dl>
                <a
                  className="chat-botao-linha"
                  href={`/dashboard/orders/${cabecalho.orderId}`}
                  target="_blank"
                  rel="noreferrer"
                >
                  <ExternalLink size={15} />
                  Abrir o pedido no sistema
                </a>
              </div>
            ) : null}

            {cabecalho.orcamento ? (
              <div className="chat-secao">
                <div className="chat-secao-titulo">Orçamento</div>
                <dl>
                  <div className="chat-dado">
                    <dt>Número</dt>
                    <dd>#{cabecalho.orcamento.number}</dd>
                  </div>
                  <div className="chat-dado">
                    <dt>Situação</dt>
                    <dd>{STATUS_DO_ORCAMENTO[String(cabecalho.orcamento.status)] || "—"}</dd>
                  </div>
                  <div className="chat-dado">
                    <dt>Total</dt>
                    <dd>{dinheiro(cabecalho.orcamento.total)}</dd>
                  </div>
                </dl>
                <a
                  className="chat-botao-linha"
                  href={`/dashboard/quotes/${cabecalho.quoteId}`}
                  target="_blank"
                  rel="noreferrer"
                >
                  <ExternalLink size={15} />
                  Abrir o orçamento no sistema
                </a>
                <p style={{ marginTop: 4, fontSize: 11.5, color: "var(--tinta-fraca)", lineHeight: 1.45 }}>
                  O que você escrever aqui aparece na página do orçamento que o cliente já acompanha. Para mandar
                  arte ou arquivo, use a página do orçamento.
                </p>
              </div>
            ) : null}

            {cabecalho.kind === "visitor" ? (
              <div className="chat-secao">
                <div className="chat-secao-titulo">Visitante da loja</div>
                <dl>
                  <div className="chat-dado">
                    <dt>Conversas</dt>
                    <dd>
                      {cabecalho.visitas > 1 ? `${cabecalho.visitas}ª vez que volta` : "Primeiro contato"}
                    </dd>
                  </div>
                  {cabecalho.primeiraVisita ? (
                    <div className="chat-dado">
                      <dt>Primeira vez</dt>
                      <dd>{new Date(cabecalho.primeiraVisita).toLocaleDateString("pt-BR")}</dd>
                    </div>
                  ) : null}
                  {cabecalho.ultimaVisita ? (
                    <div className="chat-dado">
                      <dt>Esteve aqui</dt>
                      <dd>{new Date(cabecalho.ultimaVisita).toLocaleString("pt-BR", { day: "2-digit", month: "2-digit", hour: "2-digit", minute: "2-digit" })}</dd>
                    </div>
                  ) : null}
                  <div className="chat-dado">
                    <dt>Contato</dt>
                    <dd>{cabecalho.detalhe || "não informou"}</dd>
                  </div>
                </dl>
                <p style={{ marginTop: 4, fontSize: 11.5, color: "var(--tinta-fraca)", lineHeight: 1.45 }}>
                  Sem cadastro: é reconhecido pelo navegador dele. Quem volta várias vezes está namorando a compra.
                </p>
              </div>
            ) : null}

            {linhaDoTempo}

            {cabecalho.podeResponder ? (
              <div className="chat-secao">
                {cabecalho.encerrada ? (
                  <>
                    <div className="chat-secao-titulo">Atendimento encerrado</div>
                    <p style={{ marginBottom: 8, fontSize: 12, color: "var(--tinta-fraca)", lineHeight: 1.45 }}>
                      {cabecalho.encerradaPor ? `Encerrado por ${cabecalho.encerradaPor}. ` : ""}
                      Se a pessoa escrever de novo, a conversa volta sozinha para as abertas.
                    </p>
                    <button type="button" className="chat-botao-linha" onClick={() => void encerrarAtendimento(false)}>
                      <RotateCcw size={15} />
                      Reabrir atendimento
                    </button>
                  </>
                ) : (
                  <button type="button" className="chat-botao-linha" onClick={pedirParaEncerrar}>
                    <CheckCircle2 size={15} />
                    Encerrar atendimento
                  </button>
                )}
              </div>
            ) : null}

            {arquivosDaConversa.length ? (
              <div className="chat-secao">
                <div className="chat-secao-titulo">Arquivos ({arquivosDaConversa.length})</div>
                <div className="chat-arquivos">
                  {arquivosDaConversa.slice(0, 9).map((mensagem) =>
                    mensagem.attachment_kind === "image" ? (
                      <a
                        key={mensagem.id}
                        href={mensagem.attachment_url || "#"}
                        onClick={(evento) => {
                          evento.preventDefault();
                          setFotoAberta({ url: mensagem.attachment_url || "", nome: mensagem.attachment_name });
                        }}
                      >
                        <Image
                          src={mensagem.attachment_url || ""}
                          alt={mensagem.attachment_name || "Arquivo"}
                          width={90}
                          height={90}
                          unoptimized
                        />
                      </a>
                    ) : (
                      <a
                        key={mensagem.id}
                        href={mensagem.attachment_url || "#"}
                        target="_blank"
                        rel="noreferrer"
                        style={{ display: "flex", alignItems: "center", justifyContent: "center" }}
                        title={mensagem.attachment_name || "Arquivo"}
                      >
                        <FileText size={18} color="#6f6f8e" />
                      </a>
                    )
                  )}
                </div>
              </div>
            ) : null}

            {linksDaConversa.length ? (
              <div className="chat-secao">
                <div className="chat-secao-titulo">Links</div>
                {linksDaConversa.map((link) => (
                  <a key={link} className="chat-link-item" href={link} target="_blank" rel="noreferrer">
                    {link.replace(/^https?:\/\//, "")}
                  </a>
                ))}
              </div>
            ) : null}

            {cabecalho.clientId ? (
              <div className="chat-secao">
                <a
                  className="chat-botao-linha"
                  href={`/dashboard/clients/${cabecalho.clientId}`}
                  target="_blank"
                  rel="noreferrer"
                >
                  <ExternalLink size={15} />
                  Ver a ficha do cliente
                </a>
              </div>
            ) : null}
          </>
        )}
      </aside>

      {/* ---------------- avisos de outra conversa ---------------- */}
      {avisos.length ? (
        <div className="chat-avisos">
          {avisos.map((aviso) => (
            <button
              key={aviso.id}
              type="button"
              className="chat-aviso"
              onClick={() => {
                setAvisos((antes) => antes.filter((item) => item.id !== aviso.id));
                abrirConversa(aviso.threadId);
              }}
            >
              <span className="chat-avatar" style={{ height: 34, width: 34, borderRadius: 11 }}>
                {iniciais(aviso.quem)}
              </span>
              <span style={{ minWidth: 0 }}>
                <span className="chat-aviso-quem">{aviso.quem}</span>
                <span className="chat-aviso-texto">{aviso.texto}</span>
              </span>
            </button>
          ))}
        </div>
      ) : null}

      {configAberta ? (
        <ConfiguracoesDoChats
          podeConfigurar={Boolean(eu?.podeConfigurar)}
          aoFechar={() => {
            setConfigAberta(false);
            /* As frases podem ter mudado ali dentro. */
            if (comando === "resposta") void carregarAtalho("resposta", "");
          }}
        />
      ) : null}

      {editandoRespostas ? (
        <EditorDeRespostas
          respostas={itensDoAtalho as unknown as RespostaPronta[]}
          aoFechar={() => setEditandoRespostas(false)}
          aoSalvar={salvarRespostas}
        />
      ) : null}

      {decisao ? (
        <div
          className="chat-decisao"
          role="dialog"
          aria-modal="true"
          onClick={() => setDecisao(null)}
        >
          <div className="chat-decisao-caixa" onClick={(evento) => evento.stopPropagation()}>
            <div className="chat-decisao-titulo">{decisao.titulo}</div>
            <p className="chat-decisao-texto">{decisao.texto}</p>
            <div className="chat-decisao-botoes">
              <button type="button" className="chat-decisao-cancelar" onClick={() => setDecisao(null)}>
                Cancelar
              </button>
              <button
                type="button"
                className="chat-decisao-confirmar"
                data-tom={decisao.tom || "normal"}
                onClick={async () => {
                  const acao = decisao.aoConfirmar;
                  setDecisao(null);
                  await acao();
                }}
              >
                {decisao.confirmar}
              </button>
            </div>
          </div>
        </div>
      ) : null}

      {fotoAberta ? (
        <div className="chat-foto" role="dialog" aria-modal="true" onClick={() => setFotoAberta(null)}>
          <button
            type="button"
            className="chat-foto-fechar"
            onClick={() => setFotoAberta(null)}
            aria-label="Fechar imagem"
          >
            <X size={20} />
          </button>
          <Image
            src={fotoAberta.url}
            alt={fotoAberta.nome || "Imagem da conversa"}
            width={1600}
            height={1600}
            unoptimized
            onClick={(evento) => evento.stopPropagation()}
          />
        </div>
      ) : null}
    </div>
  );
}
