// Emporium da Arte — Other screens: Dashboard, Produtos, Estoque, Fornecedores, Vendas, Caixa, Relatórios

const { useState: useS, useMemo: useM, useEffect: useE } = React;

/* ─── useVirtualRows ─────────────────────────────────────────────────
   Virtualiza linhas de uma <table> cujo scroll vertical é da página
   (`.app-content`). Mede onde a tbody está em relação ao viewport do
   scroller, calcula qual janela de linhas precisa renderizar, e devolve
   spacers (px) pra preservar a altura total da tabela.

   Uso:
     const tbodyRef = React.useRef(null);
     const { range, topSpacer, bottomSpacer } = useVirtualRows({
       tbodyRef, totalRows: sorted.length, rowHeight: 52, overscan: 8,
       enabled: sorted.length > 80,
     });
     const visible = sorted.slice(range.start, range.end);
     <tbody ref={tbodyRef}>
       {topSpacer > 0 && <tr style={{height: topSpacer}}><td colSpan={N}/></tr>}
       {visible.map(row => <tr key={row.id}>...</tr>)}
       {bottomSpacer > 0 && <tr style={{height: bottomSpacer}}><td colSpan={N}/></tr>}
     </tbody>

   Requisitos: todas as linhas têm a mesma altura (rowHeight). Não há
   medição automática — se a altura real divergir muito, ajustar o valor
   passado. */
function useVirtualRows({ tbodyRef, totalRows, rowHeight, overscan = 8, enabled = true }) {
  // Inicializa: quando enabled=false, renderiza tudo de cara (sem virtualizar).
  // Quando enabled=true, renderiza ~50 linhas iniciais; o effect refina pelo scroll.
  const [range, setRange] = useS(() => ({
    start: 0,
    end: enabled ? Math.min(50, totalRows) : totalRows,
  }));

  useE(() => {
    if (!enabled) { setRange({ start: 0, end: totalRows }); return; }
    const scroller = document.querySelector(".app-content");
    if (!scroller) return;

    let rafId = null;
    const update = () => {
      rafId = null;
      const el = tbodyRef.current;
      if (!el) return;
      const tRect  = el.getBoundingClientRect();
      const sRect  = scroller.getBoundingClientRect();
      // offsetTop: quantos px da tbody já saíram pelo topo do scroller.
      // Negativo quando a tbody ainda não chegou no topo (estamos vendo o thead acima).
      const offsetTop = sRect.top - tRect.top;
      const visibleH  = scroller.clientHeight;
      const startIdx  = Math.max(0, Math.floor(offsetTop / rowHeight) - overscan);
      const visCount  = Math.ceil(visibleH / rowHeight) + overscan * 2;
      const endIdx    = Math.min(totalRows, startIdx + visCount);
      setRange(prev =>
        prev.start === startIdx && prev.end === endIdx ? prev : { start: startIdx, end: endIdx }
      );
    };
    const schedule = () => {
      if (rafId != null) return;
      rafId = requestAnimationFrame(update);
    };

    // Mede agora, depois roda de novo após o scroll restore (que acontece
    // em 2 rAFs aninhados em usePageScrollRestore).
    update();
    requestAnimationFrame(() => requestAnimationFrame(update));

    scroller.addEventListener("scroll", schedule, { passive: true });
    window.addEventListener("resize", schedule);
    return () => {
      if (rafId != null) cancelAnimationFrame(rafId);
      scroller.removeEventListener("scroll", schedule);
      window.removeEventListener("resize", schedule);
    };
  }, [totalRows, rowHeight, overscan, enabled, tbodyRef]);

  const topSpacer    = enabled ? range.start * rowHeight : 0;
  const bottomSpacer = enabled ? Math.max(0, (totalRows - range.end) * rowHeight) : 0;
  return { range, topSpacer, bottomSpacer };
}

// Cor única usada em toda exibição de categoria (rosa claro, igual à Tintas).
// Substitui o antigo `category.color` por categoria — não tem mais cor individual.
const DEFAULT_CATEGORY_COLOR = "#F94C99";

/* ─── Code 128B barcode encoder ───────────────────────────────
   Gera o pattern Code 128 subset B (ASCII 32–127) pra um texto.
   Retorna uma string de 0s e 1s onde "1" = barra preta, "0" = espaço.
   Cada caractere ocupa 11 módulos; Stop = 13 módulos.

   Uso: encodeCode128B("19001") → "1101001000011001..."
   Pra renderizar como SVG, ver o componente Barcode128 abaixo. */
const C128_PATTERNS = [
  "11011001100","11001101100","11001100110","10010011000","10010001100",
  "10001001100","10011001000","10011000100","10001100100","11001001000",
  "11001000100","11000100100","10110011100","10011011100","10011001110",
  "10111001100","10011101100","10011100110","11001110010","11001011100",
  "11001001110","10111001110","11001110100","11101101110","11101001100",
  "11100101100","11100100110","11101100100","11100110100","11100110010",
  "11011011000","11011000110","11000110110","10100011000","10001011000",
  "10001000110","10110001000","10001101000","10001100010","11010001000",
  "11000101000","11000100010","10110111000","10110001110","10001101110",
  "10111011000","10111000110","10001110110","11101110110","11010001110",
  "11000101110","11011101000","11011100010","11011101110","11101011000",
  "11101000110","11100010110","11101101000","11101100010","11100011010",
  "11101111010","11001000010","11110001010","10100110000","10100001100",
  "10010110000","10010000110","10000101100","10000100110","10110010000",
  "10110000100","10011010000","10011000010","10000110100","10000110010",
  "11000010010","11001010000","11110111010","11000010100","10001111010",
  "10100111100","10010111100","10010011110","10111100100","10011110100",
  "10011110010","11110100100","11110010100","11110010010","11011011110",
  "11011110110","11110110110","10101111000","10100011110","10001011110",
  "10111101000","10111100010","11110101000","11110100010","10111011110",
  "10111101110","11101011110","11110101110","11010000100","11010010000",
  "11010011100","11000111010", // 0..106
];
const C128_STOP = "1100011101011";  // stop pattern (13 módulos)

const encodeCode128B = (text) => {
  const t = String(text || "");
  const startVal = 104;                       // Start B
  let sum = startVal;
  let pattern = C128_PATTERNS[startVal];
  for (let i = 0; i < t.length; i++) {
    const v = t.charCodeAt(i) - 32;
    if (v < 0 || v > 95) continue;            // pula caracteres fora do subset B
    pattern += C128_PATTERNS[v];
    sum += v * (i + 1);
  }
  const check = sum % 103;
  pattern += C128_PATTERNS[check] + C128_STOP;
  return pattern;
};

/* Renderiza um Code 128B como SVG. text é o conteúdo a codificar.
   Por padrão mostra o texto humano abaixo do barcode (showText). */
const Barcode128 = ({ text, width = 200, height = 60, showText = true,
                      barColor = "#000", textSize = 11, style }) => {
  if (!text) return null;
  const bits     = encodeCode128B(text);
  const moduleW  = width / bits.length;
  const barH     = showText ? height - 14 : height;
  const rects    = [];
  for (let i = 0; i < bits.length; i++) {
    if (bits[i] === "1") rects.push(
      <rect key={i} x={i * moduleW} y={0} width={moduleW + 0.05}
            height={barH} fill={barColor}/>
    );
  }
  return (
    <svg width={width} height={height} viewBox={`0 0 ${width} ${height}`}
         style={{ display: "block", ...style }}>
      {rects}
      {showText && (
        <text x={width / 2} y={height - 2} textAnchor="middle"
              fontFamily="JetBrains Mono, monospace" fontSize={textSize}
              fontWeight="600" fill={barColor} letterSpacing="2">
          {text}
        </text>
      )}
    </svg>
  );
};

/* ─── Reusable: card + section heading ──────────────────────── */
const PageHead = ({ title, sub, actions }) => (
  <div className="page-head">
    <div>
      <h2 className="page-title">{title}</h2>
      {sub && <div className="page-sub">{sub}</div>}
    </div>
    <div style={{ display: "flex", gap: 8 }}>{actions}</div>
  </div>
);

/* Card colapsável — header clicável que abre/fecha o conteúdo. Usado nas
 * seções de Configurações pra reduzir o scroll. defaultOpen=true mantém
 * a primeira seção aberta por padrão; demais começam fechadas. */
const Collapsible = ({ title, sub, defaultOpen = false, children }) => {
  const [open, setOpen] = useS(!!defaultOpen);
  return (
    <div className="card card-pad" style={{ marginBottom: 16 }}>
      <button type="button"
              onClick={() => setOpen(o => !o)}
              style={{
                background: "transparent", border: "none", padding: 0, width: "100%",
                display: "flex", alignItems: "center", justifyContent: "space-between",
                gap: 12, cursor: "pointer", textAlign: "left",
              }}>
        <div>
          <h3 className="section-h" style={{ marginTop: 0, marginBottom: sub ? 4 : 0 }}>{title}</h3>
          {sub && (
            <div style={{ fontSize: 12, color: "var(--text-mute)" }}>{sub}</div>
          )}
        </div>
        <span style={{
          display: "inline-flex", alignItems: "center", justifyContent: "center",
          width: 28, height: 28, borderRadius: 999, background: "var(--bg-soft, #FAFAFA)",
          flexShrink: 0, fontSize: 12, fontWeight: 700, color: "var(--text-soft)",
          transform: open ? "rotate(180deg)" : "rotate(0)", transition: "transform .15s ease",
        }}>▾</span>
      </button>
      {open && (
        <div style={{ marginTop: 16 }}>{children}</div>
      )}
    </div>
  );
};

/* Header de coluna ordenável. Click dispara onSort(col); mostra ▲/▼
 * quando a coluna está ativa. */
const SortableTh = ({ col, sortBy, sortDir, onSort, align = "left", width, children, className = "" }) => {
  const active = sortBy === col;
  const arrow = active ? (sortDir === "asc" ? " ▲" : " ▼") : "";
  return (
    <th className={className}
        style={{ textAlign: align, width, cursor: "pointer", userSelect: "none",
                 color: active ? "var(--pink-600)" : undefined,
                 whiteSpace: "nowrap" }}
        onClick={() => onSort(col)}
        title="Clique para ordenar">
      {children}<span style={{ fontSize: 9, opacity: active ? 1 : 0.3, marginLeft: 4 }}>{arrow || "▲"}</span>
    </th>
  );
};

/* ────────────────────────────────────────────────────────────
   AUDITORIA — rodapé "incluído em/por · alterado em/por" + modal
   de histórico completo. Usados em modais de edição (Produto,
   Fornecedor, Cliente, Venda).
   ──────────────────────────────────────────────────────────── */
const fmtDateTime = (iso) => {
  if (!iso) return "—";
  const d = new Date(iso);
  if (isNaN(d.getTime())) return "—";
  const dd = String(d.getDate()).padStart(2, "0");
  const mm = String(d.getMonth() + 1).padStart(2, "0");
  const yyyy = d.getFullYear();
  const hh = String(d.getHours()).padStart(2, "0");
  const mi = String(d.getMinutes()).padStart(2, "0");
  return `${dd}/${mm}/${yyyy} ${hh}:${mi}`;
};

function AuditFooter({ entity, entityId, createdAt, createdByName, updatedAt, updatedByName, onOpenHistory }) {
  const hasCreated = createdAt;
  const hasUpdated = updatedAt && updatedAt !== createdAt;
  return (
    <div style={{
      marginTop: 18, padding: "10px 14px",
      background: "var(--bg-soft)", border: "1px solid var(--border)",
      borderRadius: "var(--r-md)",
      display: "flex", justifyContent: "space-between", alignItems: "center",
      gap: 12, flexWrap: "wrap",
      fontSize: 12, color: "var(--text-soft)",
    }}>
      <div style={{ display: "flex", flexDirection: "column", gap: 4 }}>
        {hasCreated && (
          <div>
            <span style={{ color: "var(--text-mute)" }}>Incluído em </span>
            <strong className="tabular" style={{ color: "var(--text)" }}>{fmtDateTime(createdAt)}</strong>
            {createdByName && <>
              <span style={{ color: "var(--text-mute)" }}> por </span>
              <strong style={{ color: "var(--text)" }}>{createdByName}</strong>
            </>}
          </div>
        )}
        {hasUpdated && (
          <div>
            <span style={{ color: "var(--text-mute)" }}>Alterado em </span>
            <strong className="tabular" style={{ color: "var(--text)" }}>{fmtDateTime(updatedAt)}</strong>
            {updatedByName && <>
              <span style={{ color: "var(--text-mute)" }}> por </span>
              <strong style={{ color: "var(--text)" }}>{updatedByName}</strong>
            </>}
          </div>
        )}
      </div>
      <button type="button" className="btn-link" onClick={onOpenHistory}
              title="Ver todas as alterações">
        <Icon name="clock" size={14}/> Histórico
      </button>
    </div>
  );
}

// Modal com timeline completa de alterações de uma entidade.
function AuditHistoryModal({ entity, entityId, title, onClose }) {
  const [items, setItems] = useS(null);
  const [error, setError] = useS(null);

  useE(() => {
    let alive = true;
    (async () => {
      try {
        const data = await dbGetEntityAudit(entity, entityId);
        if (alive) setItems(data);
      } catch (e) {
        if (alive) setError(e?.message || String(e));
      }
    })();
    return () => { alive = false; };
  }, [entity, entityId]);

  // Mapeamento humano-legível pros nomes de coluna
  const FIELD_LABELS = {
    code: "Código", name: "Nome", price: "Preço", cost: "Custo",
    current_stock: "Estoque", min_stock: "Estoque mínimo",
    category_id: "Categoria", supplier_id: "Fornecedor",
    is_active: "Ativo", is_online: "Online", description: "Descrição",
    weight_g: "Peso (g)", length_cm: "Comp. (cm)", width_cm: "Larg. (cm)", height_cm: "Alt. (cm)",
    tax_id: "Documento", phone: "Telefone", email: "E-mail",
    zip: "CEP", street: "Rua", street_number: "Número", complement: "Complemento",
    district: "Bairro", city: "Cidade", state: "UF",
    contact_name: "Contato", status: "Status",
    discount: "Desconto", delivery_fee: "Frete", total: "Total",
    online_status: "Status (online)", shipping_method: "Método de envio",
    tracking_code: "Rastreio",
  };
  const renderVal = (v) => {
    if (v === null || v === undefined) return <em style={{ color: "var(--text-mute)" }}>vazio</em>;
    if (typeof v === "boolean") return v ? "sim" : "não";
    if (typeof v === "object") return JSON.stringify(v);
    return String(v);
  };

  return (
    <div className="modal-backdrop">
      <div className="modal modal-wide" onClick={e => e.stopPropagation()}
           style={{ maxHeight: "85vh", display: "flex", flexDirection: "column" }}>
        <div className="modal-head" style={{ flexShrink: 0 }}>
          <div>
            <h2 style={{ margin: 0, fontSize: 20, fontWeight: 700 }}>{title || "Histórico de alterações"}</h2>
            <div style={{ fontSize: 12, color: "var(--text-mute)", marginTop: 4 }}>
              Todas as mudanças registradas neste cadastro
            </div>
          </div>
          <button className="btn-icon-mini" onClick={onClose}><Icon name="close" size={18}/></button>
        </div>

        <div style={{ overflowY: "auto", flex: 1 }}>
          {error && <div style={{ padding: 20, color: "var(--bad-600)" }}>{error}</div>}
          {!items && !error && (
            <div style={{ padding: 40, textAlign: "center", color: "var(--text-mute)" }}>Carregando histórico…</div>
          )}
          {items && items.length === 0 && (
            <div style={{ padding: 40, textAlign: "center", color: "var(--text-mute)" }}>
              Sem histórico de alterações ainda.
            </div>
          )}
          {items && items.length > 0 && (
            <div style={{ display: "flex", flexDirection: "column", gap: 12, padding: "8px 4px" }}>
              {items.map(ev => {
                const changeKeys = Object.keys(ev.changes || {});
                const actionColor = ev.action === "criou"   ? "var(--ok-600)"
                                  : ev.action === "alterou" ? "var(--pink-600)"
                                  : ev.action === "excluiu" ? "var(--bad-600)"
                                  :                            "var(--text-soft)";
                return (
                  <div key={ev.id} style={{
                    background: "white", border: "1px solid var(--border)",
                    borderRadius: "var(--r-md)", padding: 14,
                  }}>
                    <div style={{ display: "flex", justifyContent: "space-between", alignItems: "baseline", marginBottom: 8, gap: 8, flexWrap: "wrap" }}>
                      <div>
                        <strong style={{ color: actionColor, textTransform: "capitalize" }}>{ev.action}</strong>
                        <span style={{ color: "var(--text-mute)" }}> por </span>
                        <strong>{ev.actor_name || "Sistema"}</strong>
                      </div>
                      <div className="tabular" style={{ fontSize: 12, color: "var(--text-mute)" }}>
                        {fmtDateTime(ev.created_at)}
                      </div>
                    </div>
                    {ev.action === "criou" && (
                      <div style={{ fontSize: 13, color: "var(--text-soft)" }}>Cadastro inicial.</div>
                    )}
                    {ev.action === "alterou" && changeKeys.length > 0 && (
                      <table style={{ width: "100%", fontSize: 12 }}>
                        <thead>
                          <tr style={{ color: "var(--text-mute)", textAlign: "left" }}>
                            <th style={{ fontWeight: 600, padding: "4px 6px" }}>Campo</th>
                            <th style={{ fontWeight: 600, padding: "4px 6px" }}>De</th>
                            <th style={{ fontWeight: 600, padding: "4px 6px" }}>Para</th>
                          </tr>
                        </thead>
                        <tbody>
                          {changeKeys.map(k => (
                            <tr key={k}>
                              <td style={{ padding: "4px 6px", color: "var(--text-soft)" }}>{FIELD_LABELS[k] || k}</td>
                              <td style={{ padding: "4px 6px" }}>{renderVal(ev.changes[k]?.old)}</td>
                              <td style={{ padding: "4px 6px", fontWeight: 600 }}>{renderVal(ev.changes[k]?.new)}</td>
                            </tr>
                          ))}
                        </tbody>
                      </table>
                    )}
                    {ev.justification && (
                      <div style={{ fontSize: 12, color: "var(--text-mute)", marginTop: 8, fontStyle: "italic" }}>
                        “{ev.justification}”
                      </div>
                    )}
                  </div>
                );
              })}
            </div>
          )}
        </div>
      </div>
    </div>
  );
}


const Stat = ({ label, value, trend, trendDir, hint, accent, onHintClick }) => (
  <div className={"stat " + (accent ? "stat-accent" : "")}>
    <div className="stat-label">{label}</div>
    <div className="stat-value tabular">{value}</div>
    {trend && <div className={"stat-trend " + (trendDir || "up")}>{trendDir === "dn" ? "↓" : "↑"} {trend}</div>}
    {hint && (
      onHintClick
        ? (
          <button
            type="button"
            onClick={onHintClick}
            className="stat-hint"
            style={{
              background: "none", border: 0, padding: 0,
              color: "var(--pink-600, #C8125F)", fontWeight: 600,
              cursor: "pointer", textDecoration: "underline",
              textDecorationStyle: "dotted", textUnderlineOffset: 3,
              textAlign: "left",
            }}
          >
            {hint}
          </button>
        )
        : <div className="stat-hint">{hint}</div>
    )}
  </div>
);

/* ════════════════════════════════════════════════════════════
   DASHBOARD
   ════════════════════════════════════════════════════════════ */
function Dashboard() {
  const { setScreen } = useApp();

  // ─── Períodos ────────────────────────────────────────────────
  // Cada bloco do dashboard tem seu próprio recorte de tempo:
  //
  //  • statsRange  → KPIs do topo. Aceita presets (hoje/7d/30d/90d) ou
  //                  range customizado (de → até). Apenas os valores
  //                  finais (`from`/`to` em ISO) são enviados ao banco.
  //  • revenueDays → tamanho da série temporal do gráfico (qualquer N≥1).
  //  • paymixDays  → janela do mix de pagamentos (1=hoje, 7d, 30d, ...).
  //
  // O dashboard recarrega quando qualquer um deles muda — uma chamada
  // só ao banco busca os 3 ao mesmo tempo.
  const todayISO = () => new Date().toISOString().slice(0, 10);
  const daysAgoISO = (n) => {
    const d = new Date();
    d.setDate(d.getDate() - (n - 1));   // inclui hoje no range
    return d.toISOString().slice(0, 10);
  };

  const [statsRange,  setStatsRange]  = useS({ mode: "today", from: todayISO(), to: todayISO() });
  const [revenueDays, setRevenueDays] = useS(14);
  const [paymixDays,  setPaymixDays]  = useS(30);
  const [data,        setData]        = useS(null);
  const [loadErr,     setLoadErr]     = useS(null);

  useE(() => {
    let cancelled = false;
    setLoadErr(null);
    (async () => {
      try {
        const d = await dbDashboardSummary({
          revenueDays,
          statsFrom: statsRange.from,
          statsTo:   statsRange.to,
          paymixDays,
        });
        if (!cancelled) setData(d);
      } catch (e) {
        if (!cancelled) setLoadErr(e?.message || String(e));
      }
    })();
    return () => { cancelled = true; };
  }, [revenueDays, statsRange.from, statsRange.to, paymixDays]);

  const periodTotal     = Number(data?.period_total)      || 0;
  const periodCount     = Number(data?.period_count)      || 0;
  const periodAvgTicket = Number(data?.period_avg_ticket) || 0;
  const totalStockUnits = Number(data?.total_stock_units) || 0;
  const series          = data?.revenue_series || [];
  const topProducts     = data?.top_products   || [];
  const lowStock        = data?.low_stock      || [];
  const paymentMix      = data?.payment_mix    || [];

  // Texto descritivo do período dos KPIs (mostrado no hint dos cards)
  const fmtBR = (iso) => {
    if (!iso) return "—";
    const [y, m, d] = iso.split("-");
    return `${d}/${m}/${y}`;
  };
  const periodLabel = (() => {
    if (statsRange.mode === "today")  return "hoje";
    if (statsRange.mode === "7d")     return "últimos 7 dias";
    if (statsRange.mode === "30d")    return "últimos 30 dias";
    if (statsRange.mode === "90d")    return "últimos 90 dias";
    return `${fmtBR(statsRange.from)} → ${fmtBR(statsRange.to)}`;
  })();

  const todayLabel = new Date().toLocaleDateString("pt-BR", {
    day: "2-digit", month: "long", year: "numeric",
  });

  return (
    <div className="screen">
      <PageHead title="Dashboard" sub={`Visão geral · ${todayLabel}`}/>

      {loadErr && (
        <div style={{ padding: 16, color: "var(--pink-600)", fontWeight: 600 }}>
          Erro ao carregar dashboard: {loadErr}
        </div>
      )}

      {/* Seletor de período dos KPIs do topo. Presets + opção personalizada. */}
      <div style={{
        display: "flex", alignItems: "center", justifyContent: "space-between",
        gap: 12, flexWrap: "wrap", marginBottom: 14,
      }}>
        <div style={{ fontSize: 12, color: "var(--text-mute)", fontWeight: 600, textTransform: "uppercase", letterSpacing: ".05em" }}>
          Período · <span style={{ color: "var(--text-soft)" }}>{periodLabel}</span>
        </div>
        <PeriodSelector value={statsRange} onChange={setStatsRange}/>
      </div>

      <div className="stats-grid">
        <Stat label={statsRange.mode === "today" ? "Vendas hoje" : "Vendas no período"}
              value={BRL(periodTotal)}
              hint={periodCount > 0
                    ? `${periodCount} ${periodCount === 1 ? "ticket" : "tickets"} · ${periodLabel}`
                    : `nenhum ticket · ${periodLabel}`}
              accent />
        <Stat label="Ticket médio"
              value={BRL(periodAvgTicket)}
              hint={periodCount === 0 ? "—" : `${periodCount} ${periodCount === 1 ? "venda" : "vendas"} · ${periodLabel}`}/>
        <Stat label="Quantidade de tickets"
              value={periodCount.toLocaleString("pt-BR")}
              hint={periodLabel}/>
        <Stat label="Itens em estoque"
              value={totalStockUnits.toLocaleString("pt-BR")}
              hint={lowStock.length > 0
                    ? `${lowStock.length} ${lowStock.length === 1 ? "produto" : "produtos"} para repor`
                    : "tudo em ordem"}
              onHintClick={lowStock.length > 0 ? () => {
                // Pré-seleciona o filtro "Baixo" na tela Estoque (useStickyState
                // já lê de sessionStorage na montagem) e navega.
                try { sessionStorage.setItem("pdv:estoque.filter", JSON.stringify("baixo")); } catch {}
                setScreen("estoque");
              } : undefined}/>
      </div>

      <div className="dash-row">
        <div className="card card-pad">
          <div className="card-head">
            <h3 className="card-title">Faturamento — {revenueDays === 1 ? "hoje" : `últimos ${revenueDays} dias`}</h3>
            <div className="seg-toggle">
              {[
                { v: 1,  label: "Dia" },
                { v: 7,  label: "7d"  },
                { v: 14, label: "14d" },
                { v: 30, label: "30d" },
                { v: 90, label: "90d" },
              ].map(o => (
                <button key={o.v} className={revenueDays === o.v ? "active" : ""}
                        onClick={() => setRevenueDays(o.v)}>
                  {o.label}
                </button>
              ))}
            </div>
          </div>
          <RevenueChart series={series} loading={data == null && !loadErr}/>
        </div>

        <div className="card card-pad">
          <div className="card-head">
            <h3 className="card-title">Meio de Pagamentos</h3>
            <div className="seg-toggle">
              {[
                { v: 1,  label: "Dia" },
                { v: 7,  label: "7d"  },
                { v: 30, label: "30d" },
              ].map(o => (
                <button key={o.v} className={paymixDays === o.v ? "active" : ""}
                        onClick={() => setPaymixDays(o.v)}>
                  {o.label}
                </button>
              ))}
            </div>
          </div>
          {paymentMix.length === 0 ? (
            <div className="empty-mini" style={{ padding: 24, textAlign: "center", color: "var(--text-mute)", fontSize: 13 }}>
              {paymixDays === 1
                ? "Nenhum pagamento hoje."
                : `Nenhum pagamento nos últimos ${paymixDays} dias.`}
            </div>
          ) : paymentMix.map(p => {
            const pct = Number(p.pct) || 0;
            const color = PAYMENT_COLORS[p.method] || "#888";
            return (
              <div key={p.method} style={{ marginBottom: 14 }}>
                <div style={{ display: "flex", justifyContent: "space-between", fontSize: 13, marginBottom: 4 }}>
                  <span style={{ display: "inline-flex", alignItems: "center", gap: 8, fontWeight: 600 }}>
                    <span style={{ width: 8, height: 8, borderRadius: 4, background: color }}/>
                    {PAYMENT_LABELS[p.method] || p.method}
                  </span>
                  <span className="tabular">
                    <span style={{ color: "var(--text-mute)", fontWeight: 500, marginRight: 8 }}>
                      {BRL(Number(p.total_amount) || 0)}
                    </span>
                    {pct.toFixed(1)}%
                  </span>
                </div>
                <div className="meter"><div style={{ width: pct + "%", background: color }}/></div>
              </div>
            );
          })}
        </div>
      </div>

      <div className="dash-row">
        <div className="card card-pad">
          <div className="card-head">
            <h3 className="card-title">Mais vendidos · 30 dias</h3>
            <button className="btn-link" onClick={() => setScreen("vendas")}>Ver vendas →</button>
          </div>
          <div>
            {topProducts.length === 0 ? (
              <div className="empty-mini" style={{ padding: 16, color: "var(--text-mute)", fontSize: 13 }}>
                Nenhuma venda nos últimos 30 dias.
              </div>
            ) : topProducts.map((p, i) => (
              <div key={p.id} className="rank-row">
                <div className="rank-num">{i + 1}</div>
                <div className="dash-code-badge">{p.code}</div>
                <div style={{ flex: 1, minWidth: 0 }}>
                  <div style={{ fontWeight: 600, fontSize: 13, whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>
                    {p.name}
                  </div>
                  <div style={{ fontSize: 11, color: "var(--text-mute)" }}>
                    {Number(p.units_sold) || 0} {Number(p.units_sold) === 1 ? "unidade vendida" : "unidades vendidas"}
                  </div>
                </div>
                <div className="tabular" style={{ fontWeight: 700, fontSize: 14 }}>{BRL(Number(p.revenue) || 0)}</div>
              </div>
            ))}
          </div>
        </div>

        <div className="card card-pad">
          <div className="card-head">
            <h3 className="card-title">Estoque para repor</h3>
            <button className="btn-link" onClick={() => setScreen("estoque")}>Gerenciar →</button>
          </div>
          {lowStock.length === 0 ? (
            <div className="empty-mini" style={{ padding: 16, textAlign: "center", color: "var(--text-mute)", fontSize: 13 }}>
              Tudo em ordem ✿
            </div>
          ) : lowStock.slice(0, 5).map(p => {
            const stock = Number(p.stock) || 0;
            return (
              <div key={p.id} className="rank-row">
                <div className="dash-code-badge">{p.code}</div>
                <div style={{ flex: 1, minWidth: 0 }}>
                  <div style={{ fontWeight: 600, fontSize: 13, whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>
                    {p.name}
                  </div>
                  <div style={{ fontSize: 11, color: "var(--text-mute)" }}>
                    {p.supplier_name || "Sem fornecedor"}
                  </div>
                </div>
                <span className={"badge " + (stock <= 0 ? "badge-bad" : "badge-warn")}>
                  <span className="dot"/>{stock} un
                </span>
              </div>
            );
          })}
        </div>
      </div>
    </div>
  );
}

/* ────────────────────────────────────────────────────────────
   PeriodSelector — botões de preset (Hoje/7d/30d/90d) + popover
   "Personalizado" com inputs De / Até. Aceita range > 90 dias.

   value: { mode: 'today'|'7d'|'30d'|'90d'|'custom', from: 'YYYY-MM-DD', to: 'YYYY-MM-DD' }
   ──────────────────────────────────────────────────────────── */
function PeriodSelector({ value, onChange }) {
  const [open, setOpen] = useS(false);
  const [from, setFrom] = useS(value.from);
  const [to,   setTo]   = useS(value.to);
  const wrapRef = React.useRef(null);

  // Sincroniza inputs internos com o valor externo (caso preset mude o range)
  useE(() => { setFrom(value.from); setTo(value.to); }, [value.from, value.to]);

  // Fecha o popover ao clicar fora
  useE(() => {
    if (!open) return;
    const onDoc = (e) => { if (!wrapRef.current?.contains(e.target)) setOpen(false); };
    document.addEventListener("mousedown", onDoc);
    return () => document.removeEventListener("mousedown", onDoc);
  }, [open]);

  const today = () => new Date().toISOString().slice(0, 10);
  const daysAgo = (n) => {
    const d = new Date(); d.setDate(d.getDate() - (n - 1));
    return d.toISOString().slice(0, 10);
  };

  const setPreset = (mode) => {
    if (mode === "today") onChange({ mode, from: today(),     to: today() });
    if (mode === "7d")    onChange({ mode, from: daysAgo(7),  to: today() });
    if (mode === "30d")   onChange({ mode, from: daysAgo(30), to: today() });
    if (mode === "90d")   onChange({ mode, from: daysAgo(90), to: today() });
    if (mode === "all")   onChange({ mode, from: "2020-01-01", to: today() });
    setOpen(false);
  };

  const applyCustom = () => {
    if (!from || !to) return;
    // Garante from <= to (se o usuário inverter os dois inputs)
    const a = from <= to ? from : to;
    const b = from <= to ? to   : from;
    onChange({ mode: "custom", from: a, to: b });
    setOpen(false);
  };

  return (
    <div ref={wrapRef} style={{ position: "relative", display: "inline-flex", alignItems: "center", gap: 8 }}>
      <div className="seg-toggle">
        {[
          { v: "today", label: "Hoje" },
          { v: "7d",    label: "7d"   },
          { v: "30d",   label: "30d"  },
          { v: "90d",   label: "90d"  },
          { v: "all",   label: "Tudo" },
        ].map(o => (
          <button key={o.v} className={value.mode === o.v ? "active" : ""}
                  onClick={() => setPreset(o.v)}>
            {o.label}
          </button>
        ))}
        <button className={value.mode === "custom" ? "active" : ""}
                onClick={() => setOpen(o => !o)}
                title="Período personalizado">
          Personalizado
        </button>
      </div>

      {value.mode === "custom" && (
        <span style={{ fontSize: 12, color: "var(--text-soft)", fontWeight: 600,
                       background: "var(--pink-50, #FFE1EC)", padding: "4px 10px",
                       borderRadius: 999 }}>
          {fmtBR(value.from)} → {fmtBR(value.to)}
        </span>
      )}

      {open && (
        <div style={{
          position: "absolute", top: "calc(100% + 6px)", right: 0,
          background: "white",
          border: "1px solid var(--border)",
          borderRadius: "var(--r-md)",
          boxShadow: "var(--sh-md)",
          padding: 14, zIndex: 30,
          display: "flex", flexDirection: "column", gap: 10,
          minWidth: 260,
        }}>
          <div style={{ fontSize: 12, color: "var(--text-mute)", fontWeight: 600, textTransform: "uppercase", letterSpacing: ".05em" }}>
            Período personalizado
          </div>
          <div style={{ display: "flex", gap: 8 }}>
            <div className="field" style={{ flex: 1 }}>
              <label className="label">De</label>
              <input type="date" className="input" value={from}
                     onChange={e => setFrom(e.target.value)}/>
            </div>
            <div className="field" style={{ flex: 1 }}>
              <label className="label">Até</label>
              <input type="date" className="input" value={to}
                     onChange={e => setTo(e.target.value)}/>
            </div>
          </div>
          <div style={{ display: "flex", gap: 8, justifyContent: "flex-end" }}>
            <button className="btn btn-secondary" onClick={() => setOpen(false)}>Cancelar</button>
            <button className="btn btn-primary"   onClick={applyCustom}>Aplicar</button>
          </div>
          <div style={{ fontSize: 11, color: "var(--text-mute)" }}>
            Aceita qualquer intervalo — inclusive maior que 90 dias.
          </div>
        </div>
      )}
    </div>
  );
}

/* ────────────────────────────────────────────────────────────
   RevenueChart — gráfico de área com hover tooltip.
   Recebe `series: [{label, day, total}]` e renderiza linha + área.
   No hover, mostra um tooltip com o valor do dia mais próximo do
   cursor. Usa apenas SVG + estado React (sem libs externas).
   ──────────────────────────────────────────────────────────── */
function RevenueChart({ series, loading }) {
  const [hover, setHover] = useS(null);  // índice do ponto sob o cursor (ou null)

  if (loading) {
    return (
      <div style={{ height: 220, display: "flex", alignItems: "center", justifyContent: "center", color: "var(--text-mute)", fontSize: 13 }}>
        Carregando...
      </div>
    );
  }
  if (!series || series.length === 0) {
    return (
      <div style={{ height: 220, display: "flex", alignItems: "center", justifyContent: "center", color: "var(--text-mute)", fontSize: 13 }}>
        Sem vendas no período.
      </div>
    );
  }

  const W = 720, H = 220, PAD = 28;
  const values = series.map(d => Number(d.total) || 0);
  // max nunca é 0 — senão divisão por zero esconde o eixo. Garante 1.
  const max = Math.max(1, ...values);

  const xs = series.map((_, i) =>
    series.length === 1
      ? W / 2
      : PAD + (i * (W - PAD * 2) / (series.length - 1)));
  const ys = series.map(v => H - PAD - ((Number(v.total) || 0) / max) * (H - PAD * 2));

  const path = xs.map((x, i) => (i === 0 ? "M" : "L") + x + "," + ys[i]).join(" ");
  const area = path + " L" + xs[xs.length - 1] + "," + (H - PAD) + " L" + xs[0] + "," + (H - PAD) + " Z";

  // Quantos labels do eixo X mostrar — pra não virar borrão no 90d
  const labelStep = Math.max(1, Math.ceil(series.length / 12));

  // Calcula o índice mais próximo do mouse no espaço do SVG
  const onMove = (evt) => {
    const svg = evt.currentTarget;
    const pt  = svg.createSVGPoint();
    pt.x = evt.clientX; pt.y = evt.clientY;
    const ctm = svg.getScreenCTM();
    if (!ctm) return;
    const cur = pt.matrixTransform(ctm.inverse());
    let bestI = 0, bestDx = Infinity;
    for (let i = 0; i < xs.length; i++) {
      const dx = Math.abs(xs[i] - cur.x);
      if (dx < bestDx) { bestDx = dx; bestI = i; }
    }
    setHover(bestI);
  };

  const hoveredItem = hover != null ? series[hover] : null;

  return (
    <div style={{ position: "relative" }}>
      <svg viewBox={`0 0 ${W} ${H}`}
           style={{ width: "100%", height: 220, display: "block", touchAction: "none" }}
           onMouseMove={onMove}
           onMouseLeave={() => setHover(null)}>
        <defs>
          <linearGradient id="dashGrad" x1="0" y1="0" x2="0" y2="1">
            <stop offset="0%"   stopColor="var(--pink-500)" stopOpacity="0.18"/>
            <stop offset="100%" stopColor="var(--pink-500)" stopOpacity="0"/>
          </linearGradient>
        </defs>
        {[0, 0.25, 0.5, 0.75, 1].map((f, i) => (
          <line key={i}
                x1={PAD} y1={H - PAD - f * (H - PAD * 2)}
                x2={W - PAD} y2={H - PAD - f * (H - PAD * 2)}
                stroke="#E7E6E2" strokeDasharray="2 4"/>
        ))}
        <path d={area} fill="url(#dashGrad)"/>
        <path d={path} fill="none" stroke="var(--pink-500)" strokeWidth="2.5"
              strokeLinejoin="round" strokeLinecap="round"/>
        {xs.map((x, i) => (
          <g key={i}>
            <circle cx={x} cy={ys[i]} r={hover === i ? 5 : 3}
                    fill="var(--pink-500)"
                    stroke={hover === i ? "white" : "transparent"}
                    strokeWidth={2}/>
            {(i === 0 || i === xs.length - 1 || i % labelStep === 0) && (
              <text x={x} y={H - 8} fontSize="10" fill="#A1A09A" textAnchor="middle"
                    fontFamily="JetBrains Mono">
                {series[i].label}
              </text>
            )}
          </g>
        ))}
        {hover != null && (
          <line x1={xs[hover]} y1={PAD} x2={xs[hover]} y2={H - PAD}
                stroke="var(--pink-500)" strokeOpacity={0.35} strokeDasharray="2 3"/>
        )}
      </svg>

      {hoveredItem && (() => {
        // Posiciona o tooltip em coordenadas de tela, com base na proporção
        // do ponto dentro do SVG (W). Convertemos pra %.
        const left = (xs[hover] / W) * 100;
        const top  = (ys[hover] / H) * 100;
        // Espelha pro outro lado se estiver perto da borda direita
        const isRight = left > 80;
        return (
          <div style={{
            position: "absolute",
            left: `calc(${left}% + ${isRight ? -12 : 12}px)`,
            top:  `calc(${top}% - 8px)`,
            transform: isRight ? "translate(-100%, -100%)" : "translate(0, -100%)",
            background: "white",
            border: "1px solid var(--border)",
            borderRadius: "var(--r-md)",
            boxShadow: "var(--sh-md)",
            padding: "8px 12px",
            pointerEvents: "none",
            whiteSpace: "nowrap",
            zIndex: 5,
          }}>
            <div style={{ fontSize: 11, color: "var(--text-mute)", fontFamily: "var(--font-mono)" }}>
              {hoveredItem.label}
            </div>
            <div style={{ fontWeight: 700, fontSize: 14, color: "var(--pink-600)" }}>
              {BRL(Number(hoveredItem.total) || 0)}
            </div>
          </div>
        );
      })()}
    </div>
  );
}

/* ════════════════════════════════════════════════════════════
   PRODUTOS (lista + cadastro)
   ════════════════════════════════════════════════════════════ */
function Produtos() {
  // Filtros + ordenação persistem em sessionStorage — voltar pra tela
  // mantém busca, categoria, fornecedor, coluna e direção como estavam.
  const [q, setQ] = useStickyState("pdv:produtos.q", "");
  const [editing, setEditing] = useS(null); // product or "new"
  const [editingTab, setEditingTab] = useS("geral"); // 'geral' | 'ecommerce' — aba inicial do form
  const [showCategoriesMgr, setShowCategoriesMgr] = useS(false);
  const [catFilter, setCatFilter] = useStickyState("pdv:produtos.catFilter", "all");
  const [supFilter, setSupFilter] = useStickyState("pdv:produtos.supFilter", "all");
  const [sortBy,  setSortBy]  = useStickyState("pdv:produtos.sortBy",  "code");
  const [sortDir, setSortDir] = useStickyState("pdv:produtos.sortDir", "asc");
  const [statusView, setStatusView] = useStickyState("pdv:produtos.statusView", "active");
  const [openMenu, setOpenMenu] = useS(null); // { row, anchor }
  const [confirming, setConfirming] = useS(null); // { product, action }

  // Clicar em uma coluna ordena por ela. Clicar de novo inverte a direção.
  const toggleSort = (key) => {
    if (sortBy === key) {
      setSortDir(d => d === "asc" ? "desc" : "asc");
    } else {
      setSortBy(key);
      setSortDir("asc");
    }
  };

  // Catálogo carregado do Supabase
  const [products,   setProducts]   = useS(null);          // null = carregando
  const [categories, setCategories] = useS([]);
  const [suppliers,  setSuppliers]  = useS([]);
  const [loadError,  setLoadError]  = useS(null);

  const reloadCategories = async () => {
    const c = await dbListCategories();
    setCategories(c);
    return c;
  };
  const reloadSuppliers = async () => {
    const s = await dbListSuppliers();
    setSuppliers(s);
    return s;
  };
  const reload = async () => {
    try {
      const [p, c, s] = await Promise.all([dbListProducts(statusView), dbListCategories(), dbListSuppliers()]);
      setProducts(p);
      setCategories(c);
      setSuppliers(s);
      setLoadError(null);
    } catch (e) {
      setLoadError(e?.message || String(e));
      setProducts([]);
    }
  };

  // Recarrega ao trocar entre Ativos / Inativos
  useE(() => { reload(); }, [statusView]);

  const list = products || [];
  const filtered = list.filter(p => {
    if (q && !p.name.toLowerCase().includes(q.toLowerCase()) && !p.code.includes(q)) return false;
    if (catFilter !== "all" && p.cat !== catFilter) return false;
    if (supFilter !== "all" && p.supplier !== supFilter) return false;
    return true;
  });

  // Ordena uma cópia (sem mutar o filtered) — campo numérico ou texto.
  const marginPct = p => p.price > 0 ? (p.price - p.cost) / p.price : 0;
  const sortKey = {
    code:          p => p.code || "",
    name:          p => p.name || "",
    cat_name:      p => p.cat_name || "",
    supplier_name: p => p.supplier_name || "",
    cost:          p => Number(p.cost) || 0,
    price:         p => Number(p.price) || 0,
    margin:        p => marginPct(p),
    stock:         p => Number(p.stock) || 0,
    is_online:     p => (p.is_online ? 1 : 0),
  };
  const cmp = (a, b) => {
    const fn = sortKey[sortBy] || sortKey.code;
    const va = fn(a), vb = fn(b);
    if (typeof va === "string") return va.localeCompare(vb, "pt-BR");
    return va - vb;
  };
  const sorted = [...filtered].sort((a, b) => sortDir === "asc" ? cmp(a, b) : -cmp(a, b));

  // Mapa de cor por categoria — usado pra colorir o badge no grid.
  // Sem cor cadastrada → cai no rosa padrão.
  const catColorById = new Map(categories.map(c => [c.id, c.color || DEFAULT_CATEGORY_COLOR]));

  // Quem rola verticalmente é o .app-content (a tabela é renderizada
  // inteira). Restaura quando os produtos terminam de carregar.
  usePageScrollRestore("pdv:produtos.scroll", products !== null);

  // Virtualização: só ativa quando a lista é grande o suficiente pra
  // compensar o custo. Linha típica ~52px (badges em vez de texto multilinha).
  const PRODUCT_ROW_H = 52;
  const tbodyRef = React.useRef(null);
  const virt = useVirtualRows({
    tbodyRef,
    totalRows: sorted.length,
    rowHeight: PRODUCT_ROW_H,
    overscan: 8,
    enabled: sorted.length > 80,
  });
  const visibleRows = sorted.slice(virt.range.start, virt.range.end);

  return (
    <div className="screen">
      <PageHead title="Produtos"
        sub={products === null
          ? "Carregando..."
          : `${list.length} produtos cadastrados · ${categories.length} categorias`}
        actions={<>
          <button className="btn btn-secondary" onClick={() => setShowCategoriesMgr(true)}
                  title="Gerenciar categorias: editar, inativar, excluir, ver produtos">
            <Icon name="tag" size={14}/> Categorias
          </button>
          <button className="btn btn-secondary"><Icon name="download" size={14}/> Exportar</button>
          <button className="btn btn-primary" onClick={() => setEditing("new")}>
            <Icon name="plus" size={14}/> Novo produto
          </button>
        </>}
      />

      {loadError && (
        <div className="card card-pad" style={{ marginBottom: 16, borderColor: "var(--pink-200)" }}>
          <div style={{ color: "var(--pink-600)", fontWeight: 600 }}>Erro ao carregar produtos</div>
          <div style={{ fontSize: 12, color: "var(--text-mute)", marginTop: 4 }}>{loadError}</div>
        </div>
      )}

      <div className="filter-bar">
        <div className="input-icon" style={{ flex: 1 }}>
          <Icon name="search" size={16}/>
          <input className="input" placeholder="Buscar por nome ou código..." value={q} onChange={e => setQ(e.target.value)} />
        </div>
        <select className="input" value={catFilter} onChange={e => setCatFilter(e.target.value)}>
          <option value="all">Todas categorias</option>
          {categories.map(c => <option key={c.id} value={c.id}>{c.name}</option>)}
        </select>
        <select className="input" value={supFilter} onChange={e => setSupFilter(e.target.value)}>
          <option value="all">Todos fornecedores</option>
          {suppliers.map(s => <option key={s.id} value={s.id}>{s.name}</option>)}
        </select>
        <div className="seg-toggle" title="Alterna entre produtos ativos e inativos">
          <button className={statusView === "active"   ? "active" : ""} onClick={() => setStatusView("active")}>Ativos</button>
          <button className={statusView === "inactive" ? "active" : ""} onClick={() => setStatusView("inactive")}>Inativos</button>
        </div>
      </div>

      <div className="card card-flush table-scroll">
        <table className="table">
          <thead>
            <tr>
              <SortableTh col="code"          align="center" width={110} className="sticky-col-l" sortBy={sortBy} sortDir={sortDir} onSort={toggleSort}>Código</SortableTh>
              <SortableTh col="name"          align="left"                                       sortBy={sortBy} sortDir={sortDir} onSort={toggleSort}>Produto</SortableTh>
              <SortableTh col="cat_name"      align="center"                                     sortBy={sortBy} sortDir={sortDir} onSort={toggleSort}>Categoria</SortableTh>
              <SortableTh col="supplier_name" align="center"                                     sortBy={sortBy} sortDir={sortDir} onSort={toggleSort}>Fornecedor</SortableTh>
              <SortableTh col="price"         align="center"                                     sortBy={sortBy} sortDir={sortDir} onSort={toggleSort}>Preço</SortableTh>
              <SortableTh col="cost"          align="center"                                     sortBy={sortBy} sortDir={sortDir} onSort={toggleSort}>Custo</SortableTh>
              <SortableTh col="margin"        align="center"                                     sortBy={sortBy} sortDir={sortDir} onSort={toggleSort}>Margem</SortableTh>
              <SortableTh col="stock"         align="center"                                     sortBy={sortBy} sortDir={sortDir} onSort={toggleSort}>Estoque</SortableTh>
              <SortableTh col="is_online"     align="center" width={90}                          sortBy={sortBy} sortDir={sortDir} onSort={toggleSort}>Online</SortableTh>
              <th className="sticky-col-r" style={{ textAlign: "center", width: 80 }}>Ações</th>
            </tr>
          </thead>
          <tbody ref={tbodyRef}>
            {products === null ? (
              <tr>
                <td colSpan={10} style={{ textAlign: "center", padding: "48px 24px", color: "var(--text-mute)", fontSize: 14 }}>
                  Carregando produtos...
                </td>
              </tr>
            ) : sorted.length === 0 ? (
              <tr>
                <td colSpan={10} style={{ textAlign: "center", padding: "48px 24px", color: "var(--text-mute)", fontSize: 14 }}>
                  {list.length === 0
                    ? (statusView === "inactive" ? "Nenhum produto inativado." : "Nenhum produto cadastrado ainda.")
                    : "Não foram localizados produtos"}
                </td>
              </tr>
            ) : (<>
              {virt.topSpacer > 0 && (
                <tr aria-hidden="true"><td colSpan={10} style={{ height: virt.topSpacer, padding: 0, border: 0 }}/></tr>
              )}
              {visibleRows.map(p => {
              const margin = p.price > 0 ? ((p.price - p.cost) / p.price * 100).toFixed(0) : "0";
              const catColor = catColorById.get(p.cat) || DEFAULT_CATEGORY_COLOR;
              return (
                <tr key={p.id}>
                  <td className="sticky-col-l tabular" style={{ textAlign: "center", fontFamily: "var(--font-mono)", color: "var(--text-soft)", fontWeight: 600 }}>
                    {p.code}
                  </td>
                  <td>
                    <div style={{ fontWeight: 600 }}>{p.name}</div>
                  </td>
                  <td style={{ textAlign: "center" }}>{p.cat_name && <span className="badge" style={{ background: catColor + "1A", color: catColor }}>{p.cat_name}</span>}</td>
                  <td style={{ textAlign: "center" }}>{p.supplier_name || <span style={{ color: "var(--text-mute)" }}>—</span>}</td>
                  <td className="tabular" style={{ textAlign: "center", fontWeight: 700 }}>{BRL(p.price)}</td>
                  <td className="tabular" style={{ textAlign: "center", color: "var(--text-soft)" }}>{BRL(p.cost)}</td>
                  <td className="tabular" style={{ textAlign: "center", color: "var(--ok-600)" }}>{margin}%</td>
                  <td className="tabular" style={{ textAlign: "center" }}>
                    {p.stock === 0
                      ? <span className="badge badge-bad"><span className="dot"/>esgotado</span>
                      : (p.min > 0 && p.stock <= p.min)
                        ? <span className="badge badge-warn"><span className="dot"/>{p.stock}</span>
                        : <span style={{ color: "var(--text-soft)" }}>{p.stock}</span>}
                  </td>
                  <td style={{ textAlign: "center" }}>
                    <button
                      type="button"
                      title={p.is_online ? "Disponível no e-commerce — clique para editar" : "Não disponível online — clique para configurar"}
                      onClick={(e) => { e.stopPropagation(); setEditingTab("ecommerce"); setEditing(p); }}
                      style={{
                        background: "transparent", border: 0, padding: 6, cursor: "pointer",
                        display: "inline-flex", alignItems: "center", justifyContent: "center",
                        borderRadius: 999,
                      }}>
                      {p.is_online
                        ? <span style={{
                            display: "inline-block", width: 10, height: 10, borderRadius: "50%",
                            background: "var(--ok-500, #22c55e)",
                            boxShadow: "0 0 0 3px rgba(34,197,94,0.18)",
                          }}/>
                        : <span style={{ color: "var(--text-mute)" }}>—</span>}
                    </button>
                  </td>
                  <td className="sticky-col-r" style={{ textAlign: "center" }}>
                    <button className="btn-icon-mini" title="Ações"
                            onClick={(e) => {
                              e.stopPropagation();
                              setOpenMenu(openMenu?.row?.id === p.id
                                ? null
                                : { row: p, anchor: e.currentTarget });
                            }}>
                      <Icon name="more" size={16}/>
                    </button>
                  </td>
                </tr>
              );
            })}
              {virt.bottomSpacer > 0 && (
                <tr aria-hidden="true"><td colSpan={10} style={{ height: virt.bottomSpacer, padding: 0, border: 0 }}/></tr>
              )}
            </>)}
          </tbody>
        </table>
      </div>

      {openMenu && (() => {
        const p = openMenu.row;
        const isInactive = p.is_active === false;
        return (
          <RowActionMenu
            anchor={openMenu.anchor}
            onClose={() => setOpenMenu(null)}
            items={[
              { icon: "edit",  label: "Editar",  onClick: () => { setEditingTab("geral"); setEditing(p); } },
              { icon: isInactive ? "refresh" : "archive",
                label: isInactive ? "Reativar" : "Inativar",
                onClick: () => setConfirming({ product: p, action: isInactive ? "reactivate" : "deactivate" }) },
              { icon: "trash", label: "Excluir", danger: true,
                onClick: () => setConfirming({ product: p, action: "delete" }) },
            ]}
          />
        );
      })()}

      {confirming && (
        <ConfirmModal
          title={
            confirming.action === "delete"     ? "Excluir produto?" :
            confirming.action === "deactivate" ? "Inativar produto?" :
                                                  "Reativar produto?"
          }
          message={
            confirming.action === "delete"
              ? `Tem certeza que deseja excluir "${confirming.product.name}"? Não funciona se houver vendas registradas — use Inativar nesse caso.`
            : confirming.action === "deactivate"
              ? `"${confirming.product.name}" deixará de aparecer no PDV mas continuará no histórico. Pode reativar depois.`
              : `"${confirming.product.name}" voltará a aparecer no PDV. Vai falhar se outro produto ativo já estiver usando o mesmo código.`
          }
          confirmLabel={
            confirming.action === "delete"     ? "Excluir" :
            confirming.action === "deactivate" ? "Inativar" :
                                                  "Reativar"
          }
          danger={confirming.action === "delete"}
          error={confirming.error}
          onCancel={() => setConfirming(null)}
          onConfirm={async () => {
            try {
              if (confirming.action === "delete")     await dbDeleteProduct(confirming.product.id);
              if (confirming.action === "deactivate") await dbSetProductActive(confirming.product.id, false);
              if (confirming.action === "reactivate") await dbSetProductActive(confirming.product.id, true);
              setConfirming(null);
              await reload();
            } catch (e) {
              setConfirming({ ...confirming, error: e?.message || "Falha ao executar a ação." });
            }
          }}
        />
      )}

      {editing && <ProductForm product={editing === "new" ? null : editing}
                                categories={categories} suppliers={suppliers}
                                initialTab={editingTab}
                                onCategoriesChanged={reloadCategories}
                                onSuppliersChanged={reloadSuppliers}
                                onClose={() => { setEditing(null); setEditingTab("geral"); }}
                                onSaved={() => { setEditing(null); setEditingTab("geral"); reload(); }}/>}

      {showCategoriesMgr && (
        <CategoriesManager onClose={() => setShowCategoriesMgr(false)}
                           onChanged={async () => { await reloadCategories(); await reload(); }}/>
      )}
    </div>
  );
}

function ProductForm({ product, onClose, onSaved, categories = [], suppliers = [], initialTab = "geral", onCategoriesChanged, onSuppliersChanged }) {
  const isNew = !product;
  const { user } = useApp();
  const [showHistory, setShowHistory] = useS(false);
  // Listas vindas do parent (já carregadas do Supabase). NÃO usar fallback
  // pros mocks — os IDs do mock são strings tipo "c3"/"s2" e quebram o
  // INSERT no banco com "invalid input syntax for type uuid".
  const cats  = categories;
  const sups  = suppliers;
  const [tab, setTab] = useS(initialTab); // 'geral' | 'ecommerce'
  // Sem default automático na 1ª categoria/fornecedor: começa vazio (= "Sem ...").
  // Categoria/fornecedor são opcionais no schema (FK nullable).
  const [catId, setCatId]   = useS(product?.cat      || "");
  const [supId, setSupId]   = useS(product?.supplier || "");
  // Custo e preço: formato monetário com vírgula fixa e milhar automático.
  // Recebem o número do produto (em reais) → string mascarada; saem via parseBRL.
  const [cost,  setCost]    = useS(maskBRL(product?.cost  != null ? String(Math.round(product.cost  * 100)) : "0"));
  const [price, setPrice]   = useS(maskBRL(product?.price != null ? String(Math.round(product.price * 100)) : "0"));
  // Campos controlados (antes eram defaultValue / não salvavam)
  const [code,  setCode]  = useS(product?.code  || "");
  const [name,  setName]  = useS(product?.name  || "");
  const [stock, setStock] = useS(product?.stock != null ? String(product.stock) : "0");
  const [minStock, setMinStock] = useS(product?.min != null ? String(product.min) : "0");
  const [showNewCat, setShowNewCat] = useS(false);
  const [showNewSup, setShowNewSup] = useS(false);
  const [saving, setSaving]   = useS(false);
  const [saveError, setSaveError] = useS(null);  // erro geral (vindo do banco)
  const [errors,    setErrors]    = useS({});    // erros por campo: { code, name, ... }

  // ── E-commerce ──
  const [isOnline,    setIsOnline]    = useS(!!product?.is_online);
  const [description, setDescription] = useS(product?.description || "");
  const [weightG,     setWeightG]     = useS(product?.weight_g  != null ? String(product.weight_g)  : "");
  const [lengthCm,    setLengthCm]    = useS(product?.length_cm != null ? String(product.length_cm) : "");
  const [widthCm,     setWidthCm]     = useS(product?.width_cm  != null ? String(product.width_cm)  : "");
  const [heightCm,    setHeightCm]    = useS(product?.height_cm != null ? String(product.height_cm) : "");
  // Preço promocional: válido entre saleStartsAt..saleEndsAt. Datas vazias
  // = sem limite (começa imediatamente / não expira). Front grava sale_price=null
  // quando o campo fica vazio (desativa a promoção).
  const [salePrice,    setSalePrice]    = useS(maskBRL(
    product?.sale_price != null ? String(Math.round(product.sale_price * 100)) : ""
  ));
  // datetime-local espera "YYYY-MM-DDTHH:mm"; corta o "Z" e os segundos.
  const toLocalInput = (iso) => {
    if (!iso) return "";
    const d = new Date(iso);
    if (isNaN(d.getTime())) return "";
    const pad = (n) => String(n).padStart(2, "0");
    return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}T${pad(d.getHours())}:${pad(d.getMinutes())}`;
  };
  const [saleStartsAt, setSaleStartsAt] = useS(toLocalInput(product?.sale_starts_at));
  const [saleEndsAt,   setSaleEndsAt]   = useS(toLocalInput(product?.sale_ends_at));
  const [images,      setImages]      = useS([]); // [{id, url, position}]
  const [imgLoading,  setImgLoading]  = useS(false);
  const [imgError,    setImgError]    = useS(null);

  // Carrega imagens quando o produto já existe e o usuário entra na aba
  useE(() => {
    if (!product?.id || tab !== "ecommerce") return;
    let alive = true;
    (async () => {
      try {
        const list = await dbListProductImages(product.id);
        if (alive) setImages(list);
      } catch (e) {
        if (alive) setImgError(e?.message || String(e));
      }
    })();
    return () => { alive = false; };
  }, [product?.id, tab]);

  const onAddImages = async (fileList) => {
    if (!product?.id) {
      setImgError("Salve o produto antes de adicionar fotos.");
      return;
    }
    if (!fileList || fileList.length === 0) return;
    setImgLoading(true); setImgError(null);
    try {
      for (const file of Array.from(fileList)) {
        const rec = await dbUploadProductImage(product.id, file);
        setImages(prev => [...prev, rec]);
      }
    } catch (e) {
      setImgError(e?.message || String(e));
    } finally {
      setImgLoading(false);
    }
  };

  const onDeleteImage = async (id) => {
    if (!confirm("Remover essa foto?")) return;
    try {
      await dbDeleteProductImage(id);
      setImages(prev => prev.filter(i => i.id !== id));
    } catch (e) {
      setImgError(e?.message || String(e));
    }
  };

  // Limpa o erro de um campo quando o usuário começa a digitar
  const clearFieldError = (field) => {
    if (errors[field]) setErrors(e => { const r = { ...e }; delete r[field]; return r; });
  };

  // Aplica borda rosa + sombra suave quando o campo tem erro
  const errStyle = (field) => errors[field]
    ? { borderColor: "var(--pink-500)", boxShadow: "0 0 0 3px var(--pink-100)" }
    : undefined;

  const handleSave = async () => {
    if (saving) return;
    const newErrors = {};
    if (!code.trim())  newErrors.code = "Informe o código do produto.";
    if (!name.trim())  newErrors.name = "Informe o nome do produto.";
    if (Object.keys(newErrors).length) {
      setErrors(newErrors);
      setSaveError(null);
      // Volta pra aba geral se erros estão nos campos da aba geral
      if (newErrors.code || newErrors.name) setTab("geral");
      return;
    }
    setErrors({});
    setSaving(true);
    setSaveError(null);
    try {
      const savedId = await dbUpsertProduct({
        id:            product?.id || null,
        code:          code.trim(),
        name:          name.trim(),
        category_id:   catId || null,
        supplier_id:   supId || null,
        cost:          parseBRL(cost),
        price:         parseBRL(price),
        current_stock: Number(String(stock).replace(/\D/g, "")) || 0,
        min_stock:     Number(String(minStock).replace(/\D/g, "")) || 0,
        user_id:       user?.id || null,    // auditoria
      });
      // Salva campos de e-commerce numa chamada separada
      const salePriceNum = parseBRL(salePrice);
      await dbUpdateProductEcommerce({
        id:          savedId || product?.id,
        isOnline,
        description: description.trim(),
        weightG, lengthCm, widthCm, heightCm,
        // preço vazio (ou zero) = desativa promo. Datas vazias = sem limite.
        salePrice:    salePriceNum > 0 ? salePriceNum : null,
        saleStartsAt: salePriceNum > 0 && saleStartsAt ? new Date(saleStartsAt).toISOString() : null,
        saleEndsAt:   salePriceNum > 0 && saleEndsAt   ? new Date(saleEndsAt).toISOString()   : null,
      });
      onSaved?.();
    } catch (e) {
      setSaveError(e?.message || "Falha ao salvar o produto");
      setSaving(false);
    }
  };

  return (
    <div className="modal-backdrop">
      <div className="modal modal-form">
        <div className="modal-head">
          <div>
            <h2 style={{ margin: 0, fontSize: 22, fontWeight: 700 }}>{isNew ? "Cadastrar produto" : "Editar produto"}</h2>
            {!isNew && <div style={{ fontSize: 13, color: "var(--text-mute)", marginTop: 4 }}>Código {product.code} · cadastrado em 12/03/2025</div>}
          </div>
          <button className="btn-icon-mini" onClick={onClose}><Icon name="close" size={18}/></button>
        </div>

        {/* Barra de abas: Geral | E-commerce */}
        <div className="report-tabs" style={{ marginTop: 8, marginBottom: 18 }}>
          <button className={"report-tab " + (tab === "geral" ? "active" : "")}
                  type="button" onClick={() => setTab("geral")}>
            Geral
          </button>
          <button className={"report-tab " + (tab === "ecommerce" ? "active" : "")}
                  type="button" onClick={() => setTab("ecommerce")}>
            E-commerce {isOnline && <span style={{ marginLeft: 6, fontSize: 10, color: "var(--ok-600)" }}>● online</span>}
          </button>
        </div>

        {tab === "geral" && (<>
        <div className="code-callout">
          <div style={{flex:1}}>
            <div className="code-callout-l">Código do produto <span className="req-pill">obrigatório</span></div>
            <div className="code-callout-h">Você define manualmente. O sistema gera o código de barras automaticamente a partir dele.</div>
            <input className="input code-input" value={code}
                   onChange={e => { setCode(e.target.value.replace(/\D/g, "").slice(0, 6)); clearFieldError("code"); }}
                   placeholder="Ex.: 19025" maxLength={6} inputMode="numeric"
                   style={errStyle("code")}/>
            {errors.code && (
              <div style={{ fontSize: 12, color: "var(--pink-600)", marginTop: 6, fontWeight: 600 }}>
                {errors.code}
              </div>
            )}
          </div>
          <div className="code-barcode-preview">
            <div className="cbp-label">Código de barras gerado</div>
            <div className="cbp-bars"/>
            <div className="cbp-num">{code || "—"}</div>
          </div>
        </div>

        <div className="form-grid">
          <div className="field" style={{ gridColumn: "1 / -1" }}>
            <label className="label">Nome do produto</label>
            <input className="input" value={name}
                   onChange={e => { setName(e.target.value); clearFieldError("name"); }}
                   placeholder="Ex.: Linha DMC Mouliné — Rosa #3326"
                   style={errStyle("name")}/>
            {errors.name && (
              <div style={{ fontSize: 12, color: "var(--pink-600)", marginTop: 2, fontWeight: 600 }}>
                {errors.name}
              </div>
            )}
          </div>
          <div className="field">
            <label className="label">Categoria</label>
            <div style={{ display: "flex", gap: 6 }}>
              <select className="input" style={{ flex: 1 }} value={catId} onChange={e => setCatId(e.target.value)}>
                <option value="">Sem categoria</option>
                {cats.map(c => <option key={c.id} value={c.id}>{c.name}</option>)}
              </select>
              <button type="button" className="btn btn-secondary" onClick={() => setShowNewCat(true)} title="Nova categoria" style={{ padding: "0 12px" }}>
                <Icon name="plus" size={14}/>
              </button>
            </div>
          </div>
          <div className="field">
            <label className="label">Fornecedor</label>
            <div style={{ display: "flex", gap: 6 }}>
              <select className="input" style={{ flex: 1 }} value={supId} onChange={e => setSupId(e.target.value)}>
                <option value="">Sem fornecedor</option>
                {sups.map(s => <option key={s.id} value={s.id}>{s.name}</option>)}
              </select>
              <button type="button" className="btn btn-secondary" onClick={() => setShowNewSup(true)} title="Novo fornecedor" style={{ padding: "0 12px" }}>
                <Icon name="plus" size={14}/>
              </button>
            </div>
          </div>
          <div className="field">
            <label className="label">Custo</label>
            <div className="input-currency-wrap">
              <span className="input-currency-prefix">R$</span>
              <input className="input input-currency tabular" inputMode="numeric"
                     value={cost} onChange={e => setCost(maskBRL(e.target.value))}/>
            </div>
          </div>
          <div className="field">
            <label className="label">Preço de venda</label>
            <div className="input-currency-wrap">
              <span className="input-currency-prefix">R$</span>
              <input className="input input-currency tabular" inputMode="numeric"
                     value={price} onChange={e => setPrice(maskBRL(e.target.value))}/>
            </div>
          </div>
          <div className="field">
            <label className="label">Estoque atual</label>
            <input className="input tabular" value={stock} inputMode="numeric"
                   onChange={e => setStock(e.target.value.replace(/\D/g, ""))} placeholder="0"/>
          </div>
          <div className="field">
            <label className="label">Estoque mínimo</label>
            <input className="input tabular" value={minStock} inputMode="numeric"
                   onChange={e => setMinStock(e.target.value.replace(/\D/g, ""))}
                   placeholder="0"/>
          </div>
        </div>
        </>)}

        {tab === "ecommerce" && (
          <div>
            {/* Toggle de disponibilidade online */}
            <div style={{
              display: "flex", alignItems: "center", gap: 14,
              padding: "14px 16px", marginBottom: 18,
              background: isOnline ? "var(--pink-50, #FFE1EC)" : "var(--bg-soft, #FAFAFA)",
              border: "1px solid " + (isOnline ? "var(--pink-200, #FFC1D6)" : "var(--border)"),
              borderRadius: "var(--r-md)",
            }}>
              <label style={{ display: "flex", alignItems: "center", gap: 10, cursor: "pointer", flex: 1 }}>
                <input type="checkbox" checked={isOnline}
                       onChange={e => setIsOnline(e.target.checked)}
                       style={{ width: 18, height: 18 }}/>
                <div>
                  <div style={{ fontWeight: 700, fontSize: 14 }}>Disponível no e-commerce</div>
                  <div style={{ fontSize: 12, color: "var(--text-soft)", marginTop: 2 }}>
                    Quando marcado, o produto aparece no site público com as fotos cadastradas.
                  </div>
                </div>
              </label>
            </div>

            {/* Descrição longa */}
            <div className="field">
              <label className="label">Descrição (aparece na página do produto)</label>
              <textarea className="input"
                        rows={4}
                        value={description}
                        onChange={e => setDescription(e.target.value)}
                        placeholder="Conte sobre o material, dimensões, cuidados de uso, história da peça..."
                        style={{ resize: "vertical", fontFamily: "inherit" }}/>
            </div>

            {/* Dimensões e peso — peso/altura em cima, comprimento/largura abaixo. */}
            <div style={{ marginTop: 14 }}>
              <div className="label" style={{ marginBottom: 8 }}>Dimensões e peso (pra cotação de frete)</div>
              <div className="form-grid" style={{ gridTemplateColumns: "repeat(2, 1fr)" }}>
                <div className="field">
                  <label className="label">Peso (g)</label>
                  <input className="input tabular" inputMode="decimal"
                         value={weightG} onChange={e => setWeightG(e.target.value.replace(/[^\d.,]/g, "").replace(",", "."))}
                         placeholder="ex. 250"/>
                </div>
                <div className="field">
                  <label className="label">Altura (cm)</label>
                  <input className="input tabular" inputMode="decimal"
                         value={heightCm} onChange={e => setHeightCm(e.target.value.replace(/[^\d.,]/g, "").replace(",", "."))}
                         placeholder="ex. 5"/>
                </div>
                <div className="field">
                  <label className="label">Comprimento (cm)</label>
                  <input className="input tabular" inputMode="decimal"
                         value={lengthCm} onChange={e => setLengthCm(e.target.value.replace(/[^\d.,]/g, "").replace(",", "."))}
                         placeholder="ex. 20"/>
                </div>
                <div className="field">
                  <label className="label">Largura (cm)</label>
                  <input className="input tabular" inputMode="decimal"
                         value={widthCm} onChange={e => setWidthCm(e.target.value.replace(/[^\d.,]/g, "").replace(",", "."))}
                         placeholder="ex. 15"/>
                </div>
              </div>
            </div>

            {/* Preço promocional — preço + janela de validade. Vazio = sem promo. */}
            <div style={{ marginTop: 22 }}>
              <div className="label" style={{ marginBottom: 8, display: "flex", alignItems: "center", gap: 8 }}>
                <span>Preço promocional</span>
                {parseBRL(salePrice) > 0 && (
                  <span style={{
                    fontSize: 10, fontWeight: 700, padding: "2px 8px",
                    background: "var(--pink-100, #FFE1EC)", color: "var(--pink-700, #C0226A)",
                    borderRadius: 999, textTransform: "uppercase", letterSpacing: 0.4,
                  }}>Promoção ativa</span>
                )}
              </div>
              <div style={{ fontSize: 12, color: "var(--text-soft)", marginBottom: 10 }}>
                Quando preenchido, o site exibe o preço promocional em destaque dentro da janela definida.
                Deixe o preço em branco (ou zero) pra desativar a promoção.
              </div>
              <div className="form-grid" style={{ gridTemplateColumns: "repeat(2, 1fr)" }}>
                <div className="field" style={{ gridColumn: "1 / -1" }}>
                  <label className="label">Preço promocional (R$)</label>
                  <input className="input tabular" inputMode="decimal"
                         value={salePrice}
                         onChange={e => setSalePrice(maskBRL(e.target.value))}
                         placeholder="ex. R$ 49,90"/>
                </div>
                <div className="field">
                  <label className="label">Início da promoção</label>
                  <input type="datetime-local" className="input"
                         value={saleStartsAt} onChange={e => setSaleStartsAt(e.target.value)}/>
                  <div style={{ fontSize: 11, color: "var(--text-mute)", marginTop: 4 }}>
                    Em branco = começa agora.
                  </div>
                </div>
                <div className="field">
                  <label className="label">Fim da promoção</label>
                  <input type="datetime-local" className="input"
                         value={saleEndsAt} onChange={e => setSaleEndsAt(e.target.value)}/>
                  <div style={{ fontSize: 11, color: "var(--text-mute)", marginTop: 4 }}>
                    Em branco = sem data limite.
                  </div>
                </div>
              </div>
            </div>

            {/* Galeria de fotos */}
            <div style={{ marginTop: 22 }}>
              <div className="label" style={{ marginBottom: 8, display: "flex", justifyContent: "space-between", alignItems: "center" }}>
                <span>Fotos do produto {images.length > 0 && <span style={{ fontWeight: 400, color: "var(--text-mute)" }}>· {images.length}</span>}</span>
                {imgLoading && <span style={{ fontWeight: 400, color: "var(--text-mute)", fontSize: 12 }}>Enviando...</span>}
              </div>
              {!product?.id && (
                <div style={{ padding: 14, background: "var(--bg-soft, #FAFAFA)", border: "1px dashed var(--border)",
                              borderRadius: "var(--r-md)", color: "var(--text-soft)", fontSize: 13, textAlign: "center" }}>
                  Salve o produto primeiro pra poder adicionar fotos.
                </div>
              )}
              {product?.id && (
                <>
                  <div style={{
                    display: "grid",
                    gridTemplateColumns: "repeat(auto-fill, minmax(120px, 1fr))",
                    gap: 10, marginBottom: 12,
                  }}>
                    {images.map(img => (
                      <div key={img.id} style={{
                        position: "relative", aspectRatio: "1 / 1",
                        background: "var(--bg-soft, #FAFAFA)", border: "1px solid var(--border)",
                        borderRadius: "var(--r-sm, 6px)", overflow: "hidden",
                      }}>
                        <img src={img.url} alt=""
                             style={{ width: "100%", height: "100%", objectFit: "cover" }}/>
                        <button type="button"
                                className="btn-icon-mini"
                                onClick={() => onDeleteImage(img.id)}
                                title="Remover foto"
                                style={{
                                  position: "absolute", top: 4, right: 4,
                                  background: "rgba(255,255,255,.92)", padding: 4,
                                }}>
                          <Icon name="trash" size={14}/>
                        </button>
                      </div>
                    ))}
                    <label style={{
                      aspectRatio: "1 / 1",
                      background: "var(--bg-soft, #FAFAFA)",
                      border: "2px dashed var(--pink-300, #FFC1D6)",
                      borderRadius: "var(--r-sm, 6px)",
                      display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center",
                      cursor: imgLoading ? "wait" : "pointer", color: "var(--pink-600)",
                      fontSize: 12, fontWeight: 600, gap: 4, textAlign: "center", padding: 8,
                    }}>
                      <Icon name="plus" size={20}/>
                      <span>{imgLoading ? "Enviando..." : "Adicionar fotos"}</span>
                      <input type="file" accept="image/*" multiple disabled={imgLoading}
                             onChange={e => { onAddImages(e.target.files); e.target.value = ""; }}
                             style={{ display: "none" }}/>
                    </label>
                  </div>
                  {imgError && (
                    <div style={{ fontSize: 12, color: "var(--pink-600)", fontWeight: 600 }}>
                      {imgError}
                    </div>
                  )}
                </>
              )}
            </div>
          </div>
        )}

        {saveError && (
          <div style={{ fontSize: 13, color: "var(--pink-600)", marginTop: 16, fontWeight: 600, textAlign: "center" }}>
            {saveError}
          </div>
        )}

        {/* Rodapé de auditoria — só aparece em produto existente */}
        {!isNew && product && (
          <AuditFooter entity="products" entityId={product.id}
                       createdAt={product.created_at} createdByName={product.created_by_name}
                       updatedAt={product.updated_at} updatedByName={product.updated_by_name}
                       onOpenHistory={() => setShowHistory(true)}/>
        )}

        {/* Footer simples: Cancelar (esq) · Salvar (dir).
            Excluir/Inativar/Reativar agora ficam no menu de 3 pontos
            da própria tabela de Produtos — fora desse modal. */}
        <div style={{ display: "flex", gap: 10, marginTop: 16, justifyContent: "space-between", alignItems: "center", flexWrap: "wrap" }}>
          <button className="btn btn-secondary" onClick={onClose} disabled={saving}>Cancelar</button>
          <button className="btn btn-primary" onClick={handleSave} disabled={saving}>
            <Icon name="check" size={14}/> {saving ? "Salvando..." : (isNew ? "Cadastrar" : "Salvar")}
          </button>
        </div>

        {showHistory && !isNew && product && (
          <AuditHistoryModal entity="products" entityId={product.id}
                             title={`Histórico — ${product.name}`}
                             onClose={() => setShowHistory(false)}/>
        )}

        {showNewCat && <QuickAddCategory
          onClose={() => setShowNewCat(false)}
          onAdded={async (newId) => {
            // Atualiza a lista no parent ANTES de selecionar, pra option existir no <select>
            if (onCategoriesChanged) await onCategoriesChanged();
            setCatId(newId);
            setShowNewCat(false);
          }}/>}
        {showNewSup && <QuickAddSupplier
          onClose={() => setShowNewSup(false)}
          onAdded={async (newId) => {
            if (onSuppliersChanged) await onSuppliersChanged();
            setSupId(newId);
            setShowNewSup(false);
          }}/>}
      </div>
    </div>
  );
}

/* ════════════════════════════════════════════════════════════
   CategoriesManager — modal pra gerenciar categorias.
   Lista todas (ativas + inativas), permite editar nome/cor,
   inativar/ativar, excluir (se não tem produtos), e ver produtos
   vinculados a cada categoria.
   ════════════════════════════════════════════════════════════ */
function CategoriesManager({ onClose, onChanged }) {
  const [items, setItems] = useS(null);
  const [error, setError] = useS(null);
  const [editing, setEditing] = useS(null); // categoria sendo editada (ou "new")
  const [confirming, setConfirming] = useS(null);   // { cat, action }
  const [viewingProducts, setViewingProducts] = useS(null); // categoria que abriu a lista de produtos
  const [busyId, setBusyId] = useS(null);
  const [q, setQ] = useS(""); // busca por nome

  const reload = async () => {
    try {
      const list = await dbListCategoriesAdmin();
      setItems(list);
    } catch (e) {
      setError(e?.message || String(e));
    }
  };
  useE(() => { reload(); }, []);

  const doAction = async (cat, action) => {
    setBusyId(cat.id);
    try {
      if (action === "deactivate") await dbSetCategoryActive(cat.id, false);
      if (action === "reactivate") await dbSetCategoryActive(cat.id, true);
      if (action === "delete")     await dbDeleteCategory(cat.id);
      await reload();
      await onChanged?.();
      setConfirming(null);
    } catch (e) {
      setConfirming({ ...confirming, error: e?.message || "Falha na ação." });
    } finally {
      setBusyId(null);
    }
  };

  // Filtra por nome (case/acento-insensitive simples via toLowerCase).
  const filtered = (items || []).filter(c =>
    !q.trim() || c.name.toLowerCase().includes(q.trim().toLowerCase())
  );

  return (
    <div className="modal-backdrop">
      <div className="modal modal-wide modal-split">
        <div className="modal-head-pinned">
          <div>
            <h2 style={{ margin: 0, fontSize: 20, fontWeight: 700 }}>Gerenciar categorias</h2>
            <div style={{ fontSize: 12, color: "var(--text-mute)", marginTop: 4 }}>
              Edite, inative, exclua, ou veja os produtos de cada categoria.
            </div>
          </div>
          <div style={{ display: "flex", gap: 8, alignItems: "center" }}>
            <div className="input-icon" style={{ width: 240 }}>
              <Icon name="search" size={14}/>
              <input className="input" placeholder="Buscar categoria..."
                     value={q} onChange={e => setQ(e.target.value)}/>
            </div>
            <button className="btn btn-primary" onClick={() => setEditing("new")}>
              <Icon name="plus" size={14}/> Nova categoria
            </button>
            <button className="btn-icon-mini" onClick={onClose}><Icon name="close" size={18}/></button>
          </div>
        </div>

        <div className="modal-body">
          {error && (
            <div style={{ padding: 12, marginBottom: 12, background: "var(--bad-50, #FEF2F2)",
                          border: "1px solid var(--bad-200, #FECACA)", borderRadius: "var(--r-md)",
                          color: "var(--bad-600, #B91C1C)", fontSize: 13 }}>
              {error}
            </div>
          )}

          {!items && !error && (
            <div style={{ padding: 40, textAlign: "center", color: "var(--text-mute)" }}>Carregando…</div>
          )}

          {items && items.length === 0 && (
            <div style={{ padding: 40, textAlign: "center", color: "var(--text-mute)" }}>
              Nenhuma categoria cadastrada. Clica em "Nova categoria" pra criar a primeira.
            </div>
          )}

          {items && items.length > 0 && filtered.length === 0 && (
            <div style={{ padding: 40, textAlign: "center", color: "var(--text-mute)" }}>
              Nenhuma categoria encontrada para "{q}".
            </div>
          )}

          {items && filtered.length > 0 && (
            <table className="table">
              <thead>
                <tr>
                  <th>Categoria</th>
                  <th style={{ textAlign: "center" }}>Produtos</th>
                  <th style={{ textAlign: "center" }}>Status</th>
                  <th style={{ textAlign: "center", width: 240 }}>Ações</th>
                </tr>
              </thead>
              <tbody>
                {filtered.map(c => (
                  <tr key={c.id} style={{ opacity: c.is_active ? 1 : 0.55 }}>
                    <td>
                      <div style={{ display: "flex", alignItems: "center", gap: 10 }}>
                        <span style={{
                          display: "inline-block", width: 14, height: 14, borderRadius: 4,
                          background: c.color || "#F94C99", flexShrink: 0,
                        }}/>
                        <strong>{c.name}</strong>
                      </div>
                    </td>
                    <td style={{ textAlign: "center" }}>
                      <button className="btn-link" onClick={() => setViewingProducts(c)}
                              disabled={c.product_count === 0}
                              title={c.product_count === 0 ? "Sem produtos nesta categoria" : "Ver produtos"}>
                        {c.product_count} {c.product_count === 1 ? "produto" : "produtos"}
                        {c.product_count > c.active_product_count && (
                          <span style={{ color: "var(--text-mute)", marginLeft: 4 }}>
                            ({c.product_count - c.active_product_count} inativ.)
                          </span>
                        )}
                      </button>
                    </td>
                    <td style={{ textAlign: "center" }}>
                      {c.is_active
                        ? <span className="badge badge-ok"><span className="dot"/>Ativa</span>
                        : <span className="badge"><span className="dot"/>Inativa</span>}
                    </td>
                    <td style={{ textAlign: "center" }}>
                      <div style={{ display: "inline-flex", gap: 6 }}>
                        <button className="btn btn-secondary btn-sm" onClick={() => setEditing(c)} disabled={busyId === c.id}>
                          <Icon name="edit" size={12}/> Editar
                        </button>
                        {c.is_active
                          ? <button className="btn btn-secondary btn-sm"
                                    onClick={() => setConfirming({ cat: c, action: "deactivate" })}
                                    disabled={busyId === c.id}>
                              <Icon name="archive" size={12}/> Inativar
                            </button>
                          : <button className="btn btn-primary btn-sm"
                                    onClick={() => setConfirming({ cat: c, action: "reactivate" })}
                                    disabled={busyId === c.id}>
                              <Icon name="refresh" size={12}/> Reativar
                            </button>}
                        <button className="btn btn-danger btn-sm"
                                onClick={() => setConfirming({ cat: c, action: "delete" })}
                                disabled={busyId === c.id || c.product_count > 0}
                                title={c.product_count > 0 ? "Não pode excluir: tem produtos vinculados" : "Excluir"}>
                          <Icon name="trash" size={12}/>
                        </button>
                      </div>
                    </td>
                  </tr>
                ))}
              </tbody>
            </table>
          )}
        </div>

        <div className="modal-foot-pinned" style={{ justifyContent: "flex-end" }}>
          <button className="btn btn-secondary" onClick={onClose}>Fechar</button>
        </div>

        {editing && (
          <QuickAddCategory category={editing === "new" ? null : editing}
                            onClose={() => setEditing(null)}
                            onAdded={async () => {
                              setEditing(null);
                              await reload();
                              await onChanged?.();
                            }}/>
        )}

        {confirming && (
          <ConfirmModal
            title={
              confirming.action === "delete"     ? "Excluir categoria?" :
              confirming.action === "deactivate" ? "Inativar categoria?" :
                                                    "Reativar categoria?"
            }
            message={
              confirming.action === "delete"
                ? `Tem certeza que deseja excluir "${confirming.cat.name}"? Falha se houver produtos vinculados.`
              : confirming.action === "deactivate"
                ? `"${confirming.cat.name}" não aparecerá mais nos selects de cadastro de produto, mas os produtos vinculados continuam funcionando.`
                : `"${confirming.cat.name}" voltará a aparecer nos selects de cadastro de produto.`
            }
            confirmLabel={confirming.action === "delete" ? "Excluir"
                       : confirming.action === "deactivate" ? "Inativar"
                       : "Reativar"}
            danger={confirming.action === "delete"}
            error={confirming.error}
            onCancel={() => setConfirming(null)}
            onConfirm={() => doAction(confirming.cat, confirming.action)}
          />
        )}

        {viewingProducts && (
          <CategoryProductsModal category={viewingProducts}
                                 onClose={() => setViewingProducts(null)}/>
        )}
      </div>
    </div>
  );
}

// Sub-modal: lista produtos vinculados a uma categoria
function CategoryProductsModal({ category, onClose }) {
  const [items, setItems] = useS(null);
  const [error, setError] = useS(null);

  useE(() => {
    let alive = true;
    (async () => {
      try {
        const list = await dbListProductsByCategory(category.id);
        if (alive) setItems(list);
      } catch (e) {
        if (alive) setError(e?.message || String(e));
      }
    })();
    return () => { alive = false; };
  }, [category.id]);

  return (
    <div className="modal-backdrop" style={{ zIndex: 110 }}>
      <div className="modal" onClick={e => e.stopPropagation()}
           style={{ width: 560, maxHeight: "80vh", display: "flex", flexDirection: "column" }}>
        <div className="modal-head" style={{ flexShrink: 0 }}>
          <div>
            <h2 style={{ margin: 0, fontSize: 18, fontWeight: 700 }}>
              <span style={{
                display: "inline-block", width: 12, height: 12, borderRadius: 3,
                background: category.color, marginRight: 8, verticalAlign: "middle",
              }}/>
              Produtos em "{category.name}"
            </h2>
            <div style={{ fontSize: 12, color: "var(--text-mute)", marginTop: 4 }}>
              {items ? `${items.length} produto${items.length !== 1 ? "s" : ""}` : "Carregando…"}
            </div>
          </div>
          <button className="btn-icon-mini" onClick={onClose}><Icon name="close" size={18}/></button>
        </div>

        <div style={{ overflowY: "auto", flex: 1, marginTop: 8 }}>
          {error && <div style={{ color: "var(--bad-600)", padding: 12 }}>{error}</div>}
          {items && items.length === 0 && (
            <div style={{ padding: 40, textAlign: "center", color: "var(--text-mute)" }}>
              Nenhum produto nesta categoria.
            </div>
          )}
          {items && items.length > 0 && (
            <table className="table">
              <thead>
                <tr>
                  <th style={{ width: 80 }}>Código</th>
                  <th>Nome</th>
                  <th style={{ textAlign: "right", width: 100 }}>Preço</th>
                  <th style={{ textAlign: "center", width: 70 }}>Estoque</th>
                  <th style={{ textAlign: "center", width: 70 }}>Status</th>
                </tr>
              </thead>
              <tbody>
                {items.map(p => (
                  <tr key={p.id} style={{ opacity: p.is_active ? 1 : 0.55 }}>
                    <td className="tabular" style={{ fontFamily: "var(--font-mono)", fontSize: 12 }}>{p.code}</td>
                    <td>
                      {p.name}
                      {p.is_online && <span className="badge badge-pink" style={{ marginLeft: 8, fontSize: 10 }}>online</span>}
                    </td>
                    <td className="tabular" style={{ textAlign: "right", fontWeight: 700 }}>{BRL(p.price)}</td>
                    <td className="tabular" style={{ textAlign: "center" }}>{p.stock}</td>
                    <td style={{ textAlign: "center" }}>
                      {p.is_active
                        ? <span className="badge badge-ok"><span className="dot"/></span>
                        : <span className="badge"><span className="dot"/></span>}
                    </td>
                  </tr>
                ))}
              </tbody>
            </table>
          )}
        </div>
      </div>
    </div>
  );
}

/* ─── Modais leves para cadastro inline ───────────────────────
   QuickAddCategory aceita `category` opcional pra edição. Se null/undefined,
   cria nova. Editando, mostra também seletor de cor (não havia antes). */
function QuickAddCategory({ onClose, onAdded, category }) {
  const isEdit = !!category;
  const [name,     setName]     = useS(category?.name || "");
  const [color,    setColor]    = useS(category?.color || DEFAULT_CATEGORY_COLOR);
  const [imageUrl, setImageUrl] = useS(category?.image_url || null);
  const [uploading, setUploading] = useS(false);
  const [saving,   setSaving]   = useS(false);
  const [error,    setError]    = useS(null);

  const onPickFile = async (e) => {
    const file = e.target.files?.[0];
    e.target.value = "";  // permite re-upload do mesmo arquivo
    if (!file) return;
    setUploading(true); setError(null);
    try {
      // Se imagem antiga existir, remove do Storage antes (pra não orphar)
      if (imageUrl) {
        try { await dbDeleteCategoryImage(imageUrl); } catch {}
      }
      const url = await dbUploadCategoryImage(category?.id, file);
      setImageUrl(url);
    } catch (e) {
      setError(e?.message || "Falha ao subir a foto");
    } finally {
      setUploading(false);
    }
  };

  const removeImage = async () => {
    if (!imageUrl || uploading) return;
    setUploading(true); setError(null);
    try {
      await dbDeleteCategoryImage(imageUrl);
      setImageUrl(null);
    } catch (e) {
      setError(e?.message || "Falha ao remover a foto");
    } finally {
      setUploading(false);
    }
  };

  const submit = async () => {
    if (!name.trim() || saving || uploading) return;
    setSaving(true);
    setError(null);
    try {
      const id = await dbUpsertCategory({
        id:        category?.id || null,
        name:      name.trim(),
        icon:      category?.icon || "tag",
        color:     color || DEFAULT_CATEGORY_COLOR,
        image_url: imageUrl,
      });
      await onAdded?.(id, name.trim());
    } catch (e) {
      setError(e?.message || "Falha ao salvar a categoria");
      setSaving(false);
    }
  };

  const colorOptions = ["#F94C99", "#E91E78", "#9333EA", "#3B82F6", "#10B981", "#F59E0B", "#EF4444", "#6B7280"];

  return (
    <div className="modal-backdrop" style={{ zIndex: 200 }}>
      <div className="modal" style={{ width: 420 }} onClick={e => e.stopPropagation()}>
        <div className="modal-head">
          <h2 style={{ margin: 0, fontSize: 18, fontWeight: 700 }}>
            {isEdit ? "Editar categoria" : "Nova categoria"}
          </h2>
          <button className="btn-icon-mini" onClick={onClose}><Icon name="close" size={16}/></button>
        </div>
        <div className="field">
          <label className="label">Nome</label>
          <input className="input" autoFocus value={name} onChange={e => setName(e.target.value)}
                 onKeyDown={e => e.key === "Enter" && submit()} placeholder="Ex.: Tesouras" disabled={saving}/>
        </div>

        {/* Foto da categoria (exibida no site público) */}
        <div className="field" style={{ marginTop: 12 }}>
          <label className="label">Foto (exibida no site)</label>
          <div style={{ display: "flex", gap: 12, alignItems: "center" }}>
            {imageUrl ? (
              <div style={{ position: "relative" }}>
                <img src={imageUrl} alt=""
                     style={{ width: 96, height: 96, objectFit: "cover",
                              borderRadius: "var(--r-md)", border: "1px solid var(--border)" }}/>
              </div>
            ) : (
              <div style={{ width: 96, height: 96, borderRadius: "var(--r-md)",
                            border: "1px dashed var(--border)", background: color + "22",
                            display: "flex", alignItems: "center", justifyContent: "center",
                            color: "var(--text-mute)", fontSize: 11 }}>
                Sem foto
              </div>
            )}
            <div style={{ flex: 1, display: "flex", flexDirection: "column", gap: 6 }}>
              <label className="btn btn-secondary btn-sm" style={{ cursor: "pointer", justifyContent: "center" }}>
                <Icon name={imageUrl ? "edit" : "plus"} size={12}/>
                {uploading ? "Enviando..." : (imageUrl ? "Trocar foto" : "Enviar foto")}
                <input type="file" accept="image/*" onChange={onPickFile}
                       disabled={uploading || saving}
                       style={{ display: "none" }}/>
              </label>
              {imageUrl && (
                <button type="button" className="btn-link" onClick={removeImage}
                        disabled={uploading || saving}
                        style={{ color: "var(--pink-600)", justifyContent: "center" }}>
                  Remover foto
                </button>
              )}
            </div>
          </div>
        </div>

        <div className="field" style={{ marginTop: 12 }}>
          <label className="label">Cor</label>
          <div style={{ display: "flex", gap: 8, flexWrap: "wrap" }}>
            {colorOptions.map(c => (
              <button key={c} type="button"
                      onClick={() => setColor(c)}
                      style={{
                        width: 32, height: 32, borderRadius: 6,
                        background: c,
                        border: color === c ? "3px solid var(--text)" : "1px solid var(--border)",
                        cursor: "pointer",
                      }}
                      title={c}/>
            ))}
          </div>
        </div>
        {error && (
          <div style={{ fontSize: 13, color: "var(--pink-600)", marginTop: 12, fontWeight: 600, textAlign: "center" }}>
            {error}
          </div>
        )}
        <div style={{ display: "flex", gap: 10, marginTop: 20, justifyContent: "flex-end" }}>
          <button className="btn btn-secondary" onClick={onClose} disabled={saving || uploading}>Cancelar</button>
          <button className="btn btn-primary" disabled={!name.trim() || saving || uploading} onClick={submit}>
            <Icon name="check" size={14}/> {saving ? "Salvando..." : (isEdit ? "Salvar" : "Cadastrar")}
          </button>
        </div>
      </div>
    </div>
  );
}

function QuickAddSupplier({ onClose, onAdded }) {
  const { user } = useApp();
  const [kind, setKind]       = useS("pj");          // 'pj' | 'pf'
  const [name, setName]       = useS("");
  const [taxId, setTaxId]     = useS("");
  const [contact, setContact] = useS("");
  const [phone, setPhone]     = useS("");
  const [saving, setSaving]   = useS(false);
  const [error,  setError]    = useS(null);
  const isPF = kind === "pf";

  // Reaplica máscara correta no taxId quando troca o tipo
  const onChangeKind = (k) => {
    setKind(k);
    const digits = String(taxId || "").replace(/\D/g, "");
    setTaxId((k === "pf" ? maskCPF : maskCNPJ)(digits));
  };

  const submit = async () => {
    if (!name.trim() || saving) return;
    setSaving(true);
    setError(null);
    try {
      const id = await dbUpsertSupplier({
        name:      name.trim(),
        taxId:     taxId.trim(),
        taxIdType: kind,
        contact:   isPF ? "" : contact.trim(),
        phone:     phone.trim(),
        email:     "",
        city:      "",
        user_id:   user?.id || null,
      });
      await onAdded?.(id, name.trim());
    } catch (e) {
      setError(e?.message || "Falha ao cadastrar o fornecedor");
      setSaving(false);
    }
  };

  return (
    <div className="modal-backdrop" style={{ zIndex: 200 }}>
      <div className="modal" style={{ width: 720 }} onClick={e => e.stopPropagation()}>
        <div className="modal-head">
          <h2 style={{ margin: 0, fontSize: 18, fontWeight: 700 }}>Novo fornecedor</h2>
          <button className="btn-icon-mini" onClick={onClose}><Icon name="close" size={16}/></button>
        </div>

        <div className="field" style={{ marginBottom: 12 }}>
          <label className="label">Tipo</label>
          <div className="seg-toggle" style={{ alignSelf: "flex-start" }}>
            <button type="button" className={kind === "pj" ? "active" : ""} onClick={() => onChangeKind("pj")} disabled={saving}>Pessoa jurídica</button>
            <button type="button" className={kind === "pf" ? "active" : ""} onClick={() => onChangeKind("pf")} disabled={saving}>Pessoa física</button>
          </div>
        </div>

        <div className="field">
          <label className="label">{isPF ? "Nome completo" : "Razão social"}</label>
          <input className="input" autoFocus value={name} onChange={e => setName(e.target.value)}
                 onKeyDown={e => e.key === "Enter" && submit()}
                 placeholder={isPF ? "Ex.: Maria Aparecida" : "Ex.: Tecidos Belém Ltda."} disabled={saving}/>
        </div>

        <div className="form-grid" style={{ marginTop: 12 }}>
          <div className="field">
            <label className="label">{isPF ? "CPF" : "CNPJ"} <span className="label-opt">(opcional)</span></label>
            <input className="input" value={taxId} inputMode="numeric"
                   onChange={e => setTaxId((isPF ? maskCPF : maskCNPJ)(e.target.value))}
                   placeholder={isPF ? "000.000.000-00" : "00.000.000/0000-00"}
                   disabled={saving}
                   style={{ fontFamily: "var(--font-mono)" }}/>
          </div>
          <div className="field">
            <label className="label">Telefone {isPF && <span className="label-opt">/ WhatsApp</span>}</label>
            <input className="input" value={phone} inputMode="numeric"
                   onChange={e => setPhone(maskPhoneBR(e.target.value))}
                   placeholder="(11) 90000-0000" disabled={saving}
                   style={{ fontFamily: "var(--font-mono)" }}/>
          </div>
          {!isPF && (
            <div className="field" style={{ gridColumn: "1 / -1" }}>
              <label className="label">Contato <span className="label-opt">(opcional)</span></label>
              <input className="input" value={contact} onChange={e => setContact(e.target.value)}
                     placeholder="Nome da pessoa que atende" disabled={saving}/>
            </div>
          )}
        </div>

        {error && (
          <div style={{ fontSize: 13, color: "var(--pink-600)", marginTop: 12, fontWeight: 600, textAlign: "center" }}>
            {error}
          </div>
        )}

        <div style={{ fontSize: 12, color: "var(--text-mute)", marginTop: 12 }}>
          E-mail e endereço você pode completar depois em <b>Fornecedores</b>.
        </div>
        <div style={{ display: "flex", gap: 10, marginTop: 20, justifyContent: "flex-end" }}>
          <button className="btn btn-secondary" onClick={onClose} disabled={saving}>Cancelar</button>
          <button className="btn btn-primary" disabled={!name.trim() || saving} onClick={submit}>
            <Icon name="check" size={14}/> {saving ? "Salvando..." : "Cadastrar"}
          </button>
        </div>
      </div>
    </div>
  );
}

/* ════════════════════════════════════════════════════════════
   ESTOQUE
   ════════════════════════════════════════════════════════════ */
function Estoque() {
  // Filtros + ordenação persistem em sessionStorage — voltar pra tela
  // mantém busca, segmento (todos/baixo/esgotado) e coluna ordenada.
  const [filter, setFilter] = useStickyState("pdv:estoque.filter", "todos");
  const [q, setQ]           = useStickyState("pdv:estoque.q", "");
  const [products,   setProducts]   = useS(null);   // null = carregando
  const [loadError,  setLoadError]  = useS(null);
  const [entryFor,   setEntryFor]   = useS(null);   // product, "any" (botão topo) ou null
  const [exitFor,    setExitFor]    = useS(null);   // product ou null
  const [showHistory, setShowHistory] = useS(false);
  const [openRowMenu, setOpenRowMenu] = useS(null); // id do produto cujo menu de ações está aberto
  const [sortBy,  setSortBy]  = useStickyState("pdv:estoque.sortBy",  "code");
  const [sortDir, setSortDir] = useStickyState("pdv:estoque.sortDir", "asc");

  const toggleSort = (key) => {
    if (sortBy === key) setSortDir(d => d === "asc" ? "desc" : "asc");
    else { setSortBy(key); setSortDir("asc"); }
  };

  const reload = async () => {
    try {
      const p = await dbListProducts("active");
      setProducts(p);
      setLoadError(null);
    } catch (e) {
      setLoadError(e?.message || String(e));
      setProducts([]);
    }
  };
  useE(() => { reload(); }, []);

  const list = products || [];

  // Cards somam por produto cadastrado (independente do filtro de visualização).
  // "Valor em estoque" = preço × quantidade atual; "Custo em estoque" = custo × qtd.
  const totalPrice = list.reduce((s, p) => s + (Number(p.price) || 0) * (Number(p.stock) || 0), 0);
  const totalCost  = list.reduce((s, p) => s + (Number(p.cost)  || 0) * (Number(p.stock) || 0), 0);
  const totalItems = list.reduce((s, p) => s + (Number(p.stock) || 0), 0);
  const outCount   = list.filter(p => p.stock <= 0).length;
  const lowCount   = list.filter(p => p.stock > 0 && p.stock <= p.min).length;

  // Restaura scroll da página quando os produtos terminam de carregar.
  usePageScrollRestore("pdv:estoque.scroll", products !== null);

  // Virtualização da tabela — só ativa em catálogos grandes.
  const ESTOQUE_ROW_H = 52;
  const tbodyRef = React.useRef(null);

  const filtered = list.filter(p => {
    if (filter === "baixo"    && !(p.stock > 0 && p.stock <= p.min)) return false;
    if (filter === "esgotado" && !(p.stock <= 0)) return false;
    if (q) {
      const t = q.toLowerCase();
      if (!p.name.toLowerCase().includes(t) && !String(p.code || "").includes(t)) return false;
    }
    return true;
  });

  // Ordenação por coluna do cabeçalho.
  // status_rank: esgotado(0) → baixo(1) → ok(2). Útil pra colocar críticos no topo.
  const stateRank = p => p.stock <= 0 ? 0 : (p.stock <= p.min ? 1 : 2);
  const sortKey = {
    code:          p => p.code || "",
    name:          p => p.name || "",
    cat_name:      p => p.cat_name || "",
    supplier_name: p => p.supplier_name || "",
    min:           p => Number(p.min)   || 0,
    stock:         p => Number(p.stock) || 0,
    status:        p => stateRank(p),
  };
  const cmp = (a, b) => {
    const fn = sortKey[sortBy] || sortKey.code;
    const va = fn(a), vb = fn(b);
    if (typeof va === "string") return va.localeCompare(vb, "pt-BR");
    return va - vb;
  };
  const sorted = [...filtered].sort((a, b) => sortDir === "asc" ? cmp(a, b) : -cmp(a, b));

  const virt = useVirtualRows({
    tbodyRef,
    totalRows: sorted.length,
    rowHeight: ESTOQUE_ROW_H,
    overscan: 8,
    enabled: sorted.length > 80,
  });
  const visibleRows = sorted.slice(virt.range.start, virt.range.end);

  return (
    <div className="screen">
      <PageHead
        title="Controle de estoque"
        sub={products == null
              ? "Carregando..."
              : `${list.length} produtos · ${list.reduce((s, p) => s + (p.stock || 0), 0)} unidades em estoque`}
        actions={<>
          <button className="btn btn-secondary" onClick={() => setShowHistory(true)}>
            <Icon name="archive" size={14}/> Histórico
          </button>
          <button className="btn btn-primary" onClick={() => setEntryFor("any")} disabled={list.length === 0}>
            <Icon name="plus" size={14}/> Entrada de estoque
          </button>
        </>}
      />

      <div className="stats-grid">
        <Stat label="Valor em estoque (preço)" value={BRL(totalPrice)} hint="soma de preço × quantidade" accent/>
        <Stat label="Valor em estoque (custo)" value={BRL(totalCost)}  hint="soma de custo × quantidade"/>
        <Stat label="Total de itens"          value={totalItems} hint="soma das quantidades"/>
        <Stat label="Esgotados"               value={outCount} hint="prioridade alta"  trendDir={outCount > 0 ? "dn" : null}/>
      </div>

      <div className="filter-bar">
        <div className="input-icon" style={{ flex: 1 }}>
          <Icon name="search" size={16}/>
          <input className="input" placeholder="Buscar por nome ou código..."
                 value={q} onChange={e => setQ(e.target.value)}/>
        </div>
        <div className="seg-toggle">
          <button className={filter === "todos"    ? "active" : ""} onClick={() => setFilter("todos")}>Todos · {list.length}</button>
          <button className={filter === "baixo"    ? "active" : ""} onClick={() => setFilter("baixo")}>Baixo · {lowCount}</button>
          <button className={filter === "esgotado" ? "active" : ""} onClick={() => setFilter("esgotado")}>Esgotados · {outCount}</button>
        </div>
      </div>

      {loadError && (
        <div style={{ padding: 16, color: "var(--pink-600)", fontWeight: 600 }}>
          Erro ao carregar: {loadError}
        </div>
      )}

      {/* overflow:visible aqui é importante: o dropdown do menu de ações
          precisa transbordar o card, e o .card-flush tem overflow:hidden.
          O table-x-scroll interno permite rolar horizontal em telas
          pequenas sem clipar o dropdown verticalmente. */}
      <div className="card card-flush" style={{ overflow: "visible" }}>
       <div className="table-x-scroll">
        <table className="table">
          <thead>
            <tr>
              <SortableTh col="code"          align="center" width={110} sortBy={sortBy} sortDir={sortDir} onSort={toggleSort}>Código</SortableTh>
              <SortableTh col="name"          align="left"               sortBy={sortBy} sortDir={sortDir} onSort={toggleSort}>Produto</SortableTh>
              <SortableTh col="cat_name"      align="center"             sortBy={sortBy} sortDir={sortDir} onSort={toggleSort}>Categoria</SortableTh>
              <SortableTh col="supplier_name" align="center"             sortBy={sortBy} sortDir={sortDir} onSort={toggleSort}>Fornecedor</SortableTh>
              <SortableTh col="stock"         align="center" width={90}  sortBy={sortBy} sortDir={sortDir} onSort={toggleSort}>Atual</SortableTh>
              <SortableTh col="min"           align="center" width={80}  sortBy={sortBy} sortDir={sortDir} onSort={toggleSort}>Mín.</SortableTh>
              <SortableTh col="status"        align="center" width={110} sortBy={sortBy} sortDir={sortDir} onSort={toggleSort}>Status</SortableTh>
              <th style={{ textAlign: "center", width: 90 }}>Ações</th>
            </tr>
          </thead>
          <tbody ref={tbodyRef}>
            {products == null ? (
              <tr><td colSpan="8" style={{ textAlign: "center", padding: 40, color: "var(--text-mute)" }}>Carregando...</td></tr>
            ) : sorted.length === 0 ? (
              <tr><td colSpan="8" style={{ textAlign: "center", padding: 40, color: "var(--text-mute)" }}>
                {list.length === 0       ? "Nenhum produto cadastrado ainda."
                 : filter === "baixo"    ? "Nenhum produto com estoque baixo."
                 : filter === "esgotado" ? "Nenhum produto esgotado."
                 :                          "Nada encontrado para esse filtro."}
              </td></tr>
            ) : (<>
              {virt.topSpacer > 0 && (
                <tr aria-hidden="true"><td colSpan="8" style={{ height: virt.topSpacer, padding: 0, border: 0 }}/></tr>
              )}
              {visibleRows.map(p => {
              const state = p.stock <= 0 ? "esgotado" : (p.stock <= p.min ? "baixo" : "ok");
              return (
                <tr key={p.id}>
                  <td className="tabular" style={{ textAlign: "center", fontFamily: "var(--font-mono)", color: "var(--text-soft)", fontWeight: 700 }}>{p.code}</td>
                  <td style={{ fontWeight: 600 }}>{p.name}</td>
                  <td style={{ textAlign: "center", color: "var(--text-soft)" }}>{p.cat_name      || "—"}</td>
                  <td style={{ textAlign: "center", color: "var(--text-soft)" }}>{p.supplier_name || "—"}</td>
                  <td className="tabular" style={{ textAlign: "center", fontWeight: 700 }}>{p.stock}</td>
                  <td className="tabular" style={{ textAlign: "center", color: "var(--text-soft)" }}>{p.min}</td>
                  <td style={{ textAlign: "center" }}>
                    {state === "esgotado" ? <span className="badge badge-bad"><span className="dot"/>Esgotado</span>
                     : state === "baixo"  ? <span className="badge badge-warn"><span className="dot"/>Baixo</span>
                     : <span className="badge badge-ok"><span className="dot"/>Ok</span>}
                  </td>
                  <td style={{ textAlign: "center" }}>
                    <button className="btn-icon-mini" title="Ações"
                            onClick={(e) => {
                              e.stopPropagation();
                              setOpenRowMenu(openRowMenu?.row?.id === p.id
                                ? null
                                : { row: p, anchor: e.currentTarget });
                            }}>
                      <Icon name="more" size={16}/>
                    </button>
                  </td>
                </tr>
              );
            })}
              {virt.bottomSpacer > 0 && (
                <tr aria-hidden="true"><td colSpan="8" style={{ height: virt.bottomSpacer, padding: 0, border: 0 }}/></tr>
              )}
            </>)}
          </tbody>
        </table>
       </div>
      </div>

      {openRowMenu && (
        <RowActionMenu
          anchor={openRowMenu.anchor}
          onClose={() => setOpenRowMenu(null)}
          items={[
            { icon: "plus",  label: "Entrada", onClick: () => setEntryFor(openRowMenu.row) },
            { icon: "minus", label: "Saída",   onClick: () => setExitFor(openRowMenu.row) },
          ]}
        />
      )}

      {entryFor && (
        <StockEntryModal
          product={entryFor === "any" ? null : entryFor}
          products={list}
          onClose={() => setEntryFor(null)}
          onSaved={async () => { setEntryFor(null); await reload(); }}
        />
      )}

      {exitFor && (
        <StockExitModal
          product={exitFor}
          onClose={() => setExitFor(null)}
          onSaved={async () => { setExitFor(null); await reload(); }}
        />
      )}

      {showHistory && (
        <StockHistoryModal onClose={() => setShowHistory(false)}/>
      )}
    </div>
  );
}

// Mapeia método de pagamento -> nome do ícone do design system.
const PAYMENT_ICON = {
  pix:      "pix",
  credito:  "card",
  debito:   "card",
  dinheiro: "money",
  voucher:  "card",
  outro:    "money",
};

/* ────────────────────────────────────────────────────────────
   RowActionMenu — dropdown reutilizável de ações por linha/card.
   Usa position: fixed ancorada ao retângulo do botão (anchor) pra
   escapar de overflow:hidden/auto dos containers (cards, scrollers).
   `anchor` é o elemento DOM do botão que disparou o menu (capturado
   via e.currentTarget no onClick). `items` é uma lista de
   { icon, label, onClick, disabled, danger, title }.
   ──────────────────────────────────────────────────────────── */
function RowActionMenu({ anchor, items, onClose }) {
  const [pos, setPos] = useS(null);
  useE(() => {
    if (!anchor) return;
    const compute = () => {
      const r = anchor.getBoundingClientRect();
      setPos({
        top:   r.bottom + 4,
        right: Math.max(8, window.innerWidth - r.right),
      });
    };
    compute();
    window.addEventListener("resize", compute);
    window.addEventListener("scroll", compute, true);
    return () => {
      window.removeEventListener("resize", compute);
      window.removeEventListener("scroll", compute, true);
    };
  }, [anchor]);
  if (!pos) return null;
  return (
    <>
      <div onClick={onClose} style={{ position: "fixed", inset: 0, zIndex: 100 }}/>
      <div onClick={e => e.stopPropagation()} style={{
        position: "fixed", top: pos.top, right: pos.right,
        background: "#fff", border: "1px solid #F5C6DA", borderRadius: 10,
        boxShadow: "0 8px 24px rgba(233, 30, 120, .15)",
        zIndex: 101, minWidth: 180, padding: 4,
      }}>
        {items.map((item, i) => (
          <button key={i}
                  onClick={() => { if (!item.disabled) { item.onClick?.(); onClose?.(); } }}
                  disabled={item.disabled}
                  title={item.title || ""}
                  style={{
                    ...rowMenuItemStyle,
                    cursor: item.disabled ? "not-allowed" : "pointer",
                    color:  item.disabled ? "var(--text-mute)"
                          : item.danger   ? "var(--bad-500, #DC2626)"
                          :                  "var(--text)",
                    opacity: item.disabled ? 0.5 : 1,
                  }}>
            {item.icon && <Icon name={item.icon} size={13}/>} {item.label}
          </button>
        ))}
      </div>
    </>
  );
}

const rowMenuItemStyle = {
  display: "flex", alignItems: "center", gap: 8,
  width: "100%", textAlign: "left",
  padding: "8px 10px", border: "none", background: "transparent",
  borderRadius: 6, cursor: "pointer", fontSize: 13, fontWeight: 500,
  color: "var(--text)",
};

/* ────────────────────────────────────────────────────────────
   Modal "Entrada de estoque" — registra uma compra/ajuste,
   somando ao current_stock via trigger fn_apply_stock_movement.
   ──────────────────────────────────────────────────────────── */
function StockEntryModal({ product, products, onClose, onSaved }) {
  const { user } = useApp();
  const [productId, setProductId] = useS(product?.id || "");
  const [qty,    setQty]    = useS("");
  const [cost,   setCost]   = useS(product ? maskBRL(String(Math.round((product.cost || 0) * 100))) : "");
  const [reason, setReason] = useS("");
  const [saving, setSaving] = useS(false);
  const [error,  setError]  = useS(null);

  const selected = product || products.find(p => p.id === productId) || null;
  const qtyNum   = Number(String(qty).replace(/\D/g, "")) || 0;

  const submit = async () => {
    if (saving) return;
    if (!productId) { setError("Selecione um produto."); return; }
    if (qtyNum <= 0) { setError("Informe a quantidade."); return; }
    setSaving(true); setError(null);
    try {
      await dbAddStockEntry({
        productId,
        quantity: qtyNum,
        unitCost: parseBRL(cost) || null,
        reason:   reason.trim() || null,
        type:     "compra",
        userId:   user?.id || null,
      });
      await onSaved?.();
    } catch (e) {
      setError(e?.message || "Falha ao registrar a entrada.");
      setSaving(false);
    }
  };

  return (
    <div className="modal-backdrop">
      <div className="modal" style={{ width: 460 }} onClick={e => e.stopPropagation()}>
        <div className="modal-head">
          <h2 style={{ margin: 0, fontSize: 18, fontWeight: 700 }}>Entrada de estoque</h2>
          <button className="btn-icon-mini" onClick={onClose}><Icon name="close" size={16}/></button>
        </div>

        {!product && (
          <div className="field">
            <label className="label">Produto</label>
            <select className="input" value={productId}
                    onChange={e => setProductId(e.target.value)} disabled={saving}>
              <option value="">Selecione um produto...</option>
              {products.map(p => (
                <option key={p.id} value={p.id}>{p.code} · {p.name}</option>
              ))}
            </select>
          </div>
        )}

        {selected && (
          <div style={{
            background: "var(--surface-2, #FFF6FA)",
            border: "1px solid var(--border, #F5C6DA)",
            padding: 12, borderRadius: 10, marginBottom: 14,
          }}>
            <div style={{ fontSize: 11, color: "var(--text-mute)", fontFamily: "var(--font-mono)" }}>{selected.code}</div>
            <div style={{ fontWeight: 600, marginTop: 2 }}>{selected.name}</div>
            <div style={{ fontSize: 12, color: "var(--text-soft)", marginTop: 6 }}>
              Estoque atual: <strong>{selected.stock}</strong>
              {qtyNum > 0 && <> → <strong style={{ color: "var(--pink-600)" }}>{(selected.stock || 0) + qtyNum}</strong></>}
            </div>
          </div>
        )}

        <div className="field">
          <label className="label">Quantidade a adicionar</label>
          <input className="input tabular" autoFocus value={qty} inputMode="numeric"
                 onChange={e => setQty(e.target.value.replace(/\D/g, ""))}
                 placeholder="0" disabled={saving}/>
        </div>

        <div className="field">
          <label className="label">
            Custo unitário <span style={{ color: "var(--text-mute)", fontWeight: 400 }}>· opcional</span>
          </label>
          <div className="input-currency-wrap">
            <span className="input-currency-prefix">R$</span>
            <input className="input input-currency tabular" value={cost} inputMode="numeric"
                   onChange={e => setCost(maskBRL(e.target.value))} disabled={saving}/>
          </div>
        </div>

        <div className="field">
          <label className="label">
            Motivo <span style={{ color: "var(--text-mute)", fontWeight: 400 }}>· opcional</span>
          </label>
          <input className="input" value={reason} onChange={e => setReason(e.target.value)}
                 placeholder="Ex.: Compra Bordados Brasil · NF 12345" disabled={saving}/>
        </div>

        {error && (
          <div style={{ fontSize: 13, color: "var(--pink-600)", marginTop: 4, fontWeight: 600, textAlign: "center" }}>
            {error}
          </div>
        )}

        <div style={{ display: "flex", gap: 10, marginTop: 20, justifyContent: "flex-end" }}>
          <button className="btn btn-secondary" onClick={onClose} disabled={saving}>Cancelar</button>
          <button className="btn btn-primary" onClick={submit} disabled={saving || !productId || qtyNum <= 0}>
            <Icon name="check" size={14}/> {saving ? "Registrando..." : "Registrar entrada"}
          </button>
        </div>
      </div>
    </div>
  );
}

/* ────────────────────────────────────────────────────────────
   Modal "Saída de estoque" — registra perda/ajuste/devolução,
   gravando movimento NEGATIVO (o trigger subtrai do current_stock).
   ──────────────────────────────────────────────────────────── */
function StockExitModal({ product, onClose, onSaved }) {
  const { user } = useApp();
  const [qty,    setQty]    = useS("");
  const [type,   setType]   = useS("perda"); // perda | ajuste | devolucao
  const [reason, setReason] = useS("");
  const [saving, setSaving] = useS(false);
  const [error,  setError]  = useS(null);

  const qtyNum     = Number(String(qty).replace(/\D/g, "")) || 0;
  const stockAfter = (product?.stock || 0) - qtyNum;
  const willGoNegative = stockAfter < 0;

  const submit = async () => {
    if (saving) return;
    if (qtyNum <= 0) { setError("Informe a quantidade."); return; }
    setSaving(true); setError(null);
    try {
      await dbAddStockExit({
        productId: product.id,
        quantity:  qtyNum,
        reason:    reason.trim() || null,
        type,
        userId:    user?.id || null,
      });
      await onSaved?.();
    } catch (e) {
      setError(e?.message || "Falha ao registrar a saída.");
      setSaving(false);
    }
  };

  return (
    <div className="modal-backdrop">
      <div className="modal" style={{ width: 460 }} onClick={e => e.stopPropagation()}>
        <div className="modal-head">
          <h2 style={{ margin: 0, fontSize: 18, fontWeight: 700 }}>Saída de estoque</h2>
          <button className="btn-icon-mini" onClick={onClose}><Icon name="close" size={16}/></button>
        </div>

        <div style={{
          background: "var(--surface-2, #FFF6FA)",
          border: "1px solid var(--border, #F5C6DA)",
          padding: 12, borderRadius: 10, marginBottom: 14,
        }}>
          <div style={{ fontSize: 11, color: "var(--text-mute)", fontFamily: "var(--font-mono)" }}>{product.code}</div>
          <div style={{ fontWeight: 600, marginTop: 2 }}>{product.name}</div>
          <div style={{ fontSize: 12, color: "var(--text-soft)", marginTop: 6 }}>
            Estoque atual: <strong>{product.stock}</strong>
            {qtyNum > 0 && (
              <> → <strong style={{ color: willGoNegative ? "var(--bad-500, #DC2626)" : "var(--pink-600)" }}>
                {stockAfter}
              </strong></>
            )}
          </div>
        </div>

        <div className="field">
          <label className="label">Quantidade a retirar</label>
          <input className="input tabular" autoFocus value={qty} inputMode="numeric"
                 onChange={e => setQty(e.target.value.replace(/\D/g, ""))}
                 placeholder="0" disabled={saving}/>
        </div>

        <div className="field">
          <label className="label">Tipo</label>
          <div className="seg-toggle" style={{ alignSelf: "flex-start" }}>
            <button type="button" className={type === "perda"     ? "active" : ""} onClick={() => setType("perda")}     disabled={saving}>Perda / quebra</button>
            <button type="button" className={type === "ajuste"    ? "active" : ""} onClick={() => setType("ajuste")}    disabled={saving}>Ajuste</button>
            <button type="button" className={type === "devolucao" ? "active" : ""} onClick={() => setType("devolucao")} disabled={saving}>Devolução</button>
          </div>
        </div>

        <div className="field">
          <label className="label">
            Motivo <span style={{ color: "var(--text-mute)", fontWeight: 400 }}>· opcional</span>
          </label>
          <input className="input" value={reason} onChange={e => setReason(e.target.value)}
                 placeholder="Ex.: Quebra no transporte · Devolvido ao fornecedor" disabled={saving}/>
        </div>

        {willGoNegative && qtyNum > 0 && (
          <div style={{ fontSize: 12, color: "var(--text-mute)", textAlign: "center", marginTop: 4 }}>
            Atenção: o estoque ficará negativo após esta saída.
          </div>
        )}

        {error && (
          <div style={{ fontSize: 13, color: "var(--pink-600)", marginTop: 8, fontWeight: 600, textAlign: "center" }}>
            {error}
          </div>
        )}

        <div style={{ display: "flex", gap: 10, marginTop: 20, justifyContent: "flex-end" }}>
          <button className="btn btn-secondary" onClick={onClose} disabled={saving}>Cancelar</button>
          <button className="btn btn-primary" onClick={submit} disabled={saving || qtyNum <= 0}>
            <Icon name="check" size={14}/> {saving ? "Registrando..." : "Registrar saída"}
          </button>
        </div>
      </div>
    </div>
  );
}

/* ────────────────────────────────────────────────────────────
   Modal "Histórico de movimentações" — lista o livro-razão de
   stock_movements (compras, vendas, perdas, ajustes, estornos).
   ──────────────────────────────────────────────────────────── */
function StockHistoryModal({ onClose }) {
  const [rows, setRows]       = useS(null);    // null = carregando
  const [typeFilter, setTypeFilter] = useS("all");
  const [dateFrom, setDateFrom] = useS("");    // 'yyyy-mm-dd' ou ''
  const [dateTo,   setDateTo]   = useS("");
  const [error, setError]     = useS(null);
  // Ordenação por cabeçalho — default: mais recentes primeiro
  const [sortBy,  setSortBy]  = useS("created_at");
  const [sortDir, setSortDir] = useS("desc");
  const toggleSort = (key) => {
    if (sortBy === key) setSortDir(d => d === "asc" ? "desc" : "asc");
    else { setSortBy(key); setSortDir("asc"); }
  };

  useE(() => {
    let alive = true;
    (async () => {
      try {
        const data = await dbListStockMovements({ limit: 300 });
        if (alive) setRows(data);
      } catch (e) {
        if (alive) { setError(e?.message || String(e)); setRows([]); }
      }
    })();
    return () => { alive = false; };
  }, []);

  const TYPE_LABELS = {
    compra:        "Entrada (compra)",
    venda:         "Saída (venda)",
    estorno_venda: "Estorno de venda",
    ajuste:        "Ajuste",
    perda:         "Perda / quebra",
    inventario:    "Inventário",
    devolucao:     "Devolução",
  };
  const TYPE_TONE = {
    compra: "ok", estorno_venda: "ok",
    venda: "warn", perda: "bad", devolucao: "warn",
    ajuste: "soft", inventario: "soft",
  };

  const list = rows || [];
  // Aplica filtro de tipo + intervalo de datas. dateFrom/dateTo são 'yyyy-mm-dd'.
  // dateTo é inclusivo: somo 1 dia ao limite superior pra pegar o dia inteiro.
  const fromTs = dateFrom ? new Date(dateFrom + "T00:00:00").getTime() : null;
  const toTs   = dateTo   ? new Date(dateTo   + "T00:00:00").getTime() + 86400000 : null;
  const filteredOnly = list.filter(m => {
    if (typeFilter !== "all" && m.type !== typeFilter) return false;
    if (fromTs || toTs) {
      const ts = new Date(m.created_at || 0).getTime();
      if (fromTs && ts < fromTs) return false;
      if (toTs   && ts > toTs)   return false;
    }
    return true;
  });

  // Chaves de ordenação por coluna. Para "Qtd.", uso o valor assinado
  // (entradas positivas, saídas negativas) — assim asc deixa as maiores
  // saídas no topo.
  const sortKey = {
    created_at: m => new Date(m.created_at || 0).getTime(),
    product:    m => (m.product_name || "") + "·" + (m.product_code || ""),
    type:       m => TYPE_LABELS[m.type] || m.type || "",
    quantity:   m => Number(m.quantity) || 0,
    reason:     m => m.reason || "",
    user:       m => m.user_name || "",
  };
  const cmp = (a, b) => {
    const fn = sortKey[sortBy] || sortKey.created_at;
    const va = fn(a), vb = fn(b);
    if (typeof va === "string") return va.localeCompare(vb, "pt-BR");
    return va - vb;
  };
  const filtered = [...filteredOnly].sort((a, b) => sortDir === "asc" ? cmp(a, b) : -cmp(a, b));

  const fmtDate = (s) => {
    if (!s) return "—";
    try {
      const d = new Date(s);
      return d.toLocaleString("pt-BR", { day: "2-digit", month: "2-digit", year: "2-digit", hour: "2-digit", minute: "2-digit" });
    } catch { return String(s); }
  };

  return (
    <div className="modal-backdrop">
      <div className="modal modal-split" style={{ width: 880 }} onClick={e => e.stopPropagation()}>
        {/* Cabeçalho fixo — usa padding do .modal-head-pinned do design system */}
        <div className="modal-head-pinned">
          <h2 style={{ margin: 0, fontSize: 18, fontWeight: 700, display: "flex", alignItems: "center", gap: 8 }}>
            <Icon name="archive" size={16}/> Histórico de movimentações
          </h2>
          <button className="btn-icon-mini" onClick={onClose}><Icon name="close" size={16}/></button>
        </div>

        {/* Corpo rolável com padding interno (modal-body já cuida disso) */}
        <div className="modal-body">
          <div style={{ display: "flex", gap: 10, alignItems: "center", flexWrap: "wrap", marginBottom: 14 }}>
            <div className="seg-toggle">
              <button className={typeFilter === "all"     ? "active" : ""} onClick={() => setTypeFilter("all")}>Todos</button>
              <button className={typeFilter === "compra"  ? "active" : ""} onClick={() => setTypeFilter("compra")}>Entradas</button>
              <button className={typeFilter === "venda"   ? "active" : ""} onClick={() => setTypeFilter("venda")}>Vendas</button>
              <button className={typeFilter === "perda"   ? "active" : ""} onClick={() => setTypeFilter("perda")}>Perdas</button>
              <button className={typeFilter === "ajuste"  ? "active" : ""} onClick={() => setTypeFilter("ajuste")}>Ajustes</button>
            </div>
            <span style={{ marginLeft: "auto", fontSize: 12, color: "var(--text-mute)" }}>
              {rows == null ? "Carregando..." : `${filtered.length} movimentações`}
            </span>
          </div>

          {/* Filtro de período: De / Até. Ambos opcionais; podem ficar
              em branco pra ver tudo. Botão "Limpar" zera os dois. */}
          <div style={{ display: "flex", gap: 10, alignItems: "center", flexWrap: "wrap", marginBottom: 14 }}>
            <span style={{ fontSize: 12, color: "var(--text-soft)", fontWeight: 600 }}>Período:</span>
            <div style={{ display: "flex", alignItems: "center", gap: 6 }}>
              <label style={{ fontSize: 12, color: "var(--text-mute)" }}>De</label>
              <input type="date" className="input"
                     value={dateFrom} onChange={e => setDateFrom(e.target.value)}
                     style={{ padding: "8px 10px", fontSize: 13 }}
                     max={dateTo || undefined}/>
            </div>
            <div style={{ display: "flex", alignItems: "center", gap: 6 }}>
              <label style={{ fontSize: 12, color: "var(--text-mute)" }}>Até</label>
              <input type="date" className="input"
                     value={dateTo} onChange={e => setDateTo(e.target.value)}
                     style={{ padding: "8px 10px", fontSize: 13 }}
                     min={dateFrom || undefined}/>
            </div>
            {(dateFrom || dateTo) && (
              <button className="btn btn-secondary" style={{ padding: "8px 12px", fontSize: 12 }}
                      onClick={() => { setDateFrom(""); setDateTo(""); }}>
                Limpar
              </button>
            )}
          </div>

          {error && (
            <div style={{ padding: 12, color: "var(--pink-600)", fontWeight: 600, marginBottom: 12 }}>
              Erro: {error}
            </div>
          )}

          <div className="table-x-scroll"
               style={{ border: "1px solid var(--border, #F5C6DA)", borderRadius: 10 }}>
            <table className="table" style={{ margin: 0 }}>
              <thead>
                <tr>
                  <SortableTh col="created_at" align="left"  width={130} sortBy={sortBy} sortDir={sortDir} onSort={toggleSort}>Data</SortableTh>
                  <SortableTh col="product"    align="left"              sortBy={sortBy} sortDir={sortDir} onSort={toggleSort}>Produto</SortableTh>
                  <SortableTh col="type"       align="left"  width={160} sortBy={sortBy} sortDir={sortDir} onSort={toggleSort}>Tipo</SortableTh>
                  <SortableTh col="quantity"   align="right" width={90}  sortBy={sortBy} sortDir={sortDir} onSort={toggleSort}>Qtd.</SortableTh>
                  <SortableTh col="reason"     align="left"              sortBy={sortBy} sortDir={sortDir} onSort={toggleSort}>Motivo</SortableTh>
                  <SortableTh col="user"       align="left"  width={130} sortBy={sortBy} sortDir={sortDir} onSort={toggleSort}>Usuário</SortableTh>
                </tr>
              </thead>
              <tbody>
                {rows == null ? (
                  <tr><td colSpan="6" style={{ textAlign: "center", padding: 30, color: "var(--text-mute)" }}>Carregando...</td></tr>
                ) : filtered.length === 0 ? (
                  <tr><td colSpan="6" style={{ textAlign: "center", padding: 30, color: "var(--text-mute)" }}>
                    {list.length === 0 ? "Nenhuma movimentação registrada ainda." : "Nada nesse filtro."}
                  </td></tr>
                ) : filtered.map(m => {
                  const tone   = TYPE_TONE[m.type] || "soft";
                  const isOut  = Number(m.quantity) < 0;
                  const qtyAbs = Math.abs(Number(m.quantity) || 0);
                  return (
                    <tr key={m.id}>
                      <td className="tabular" style={{ fontSize: 12, color: "var(--text-soft)" }}>{fmtDate(m.created_at)}</td>
                      <td>
                        <div style={{ fontWeight: 600 }}>{m.product_name || "—"}</div>
                        <div style={{ fontSize: 11, fontFamily: "var(--font-mono)", color: "var(--text-mute)" }}>{m.product_code || ""}</div>
                      </td>
                      <td>
                        <span className={"badge " + (
                          tone === "ok"   ? "badge-ok" :
                          tone === "warn" ? "badge-warn" :
                          tone === "bad"  ? "badge-bad" : ""
                        )}>{TYPE_LABELS[m.type] || m.type}</span>
                      </td>
                      <td className="tabular" style={{ textAlign: "right", fontWeight: 700,
                                                       color: isOut ? "var(--bad-500, #DC2626)" : "var(--ok-500, #16A34A)" }}>
                        {isOut ? "−" : "+"}{qtyAbs}
                      </td>
                      <td style={{ fontSize: 13, color: "var(--text-soft)" }}>{m.reason || "—"}</td>
                      <td style={{ fontSize: 13, color: "var(--text-soft)" }}>{m.user_name || "—"}</td>
                    </tr>
                  );
                })}
              </tbody>
            </table>
          </div>
        </div>

        {/* Rodapé fixo — padding via .modal-foot-pinned */}
        <div className="modal-foot-pinned">
          <button className="btn btn-secondary" onClick={onClose}>Fechar</button>
        </div>
      </div>
    </div>
  );
}

/* ════════════════════════════════════════════════════════════
   FORNECEDORES
   ════════════════════════════════════════════════════════════ */
function Fornecedores() {
  const [editing, setEditing] = useS(null);          // supplier | "new" | null
  // Quando true, o SupplierForm esconde os botões Excluir/Inativar/Reativar.
  // Ligado quando o usuário abre o form via clique no card OU na linha da
  // lista — as ações destrutivas continuam disponíveis pelo menu de 3 pontos.
  const [editingFromClick, setEditingFromClick] = useS(false);
  const [openMenu, setOpenMenu] = useS(null);        // supplier id (qual menu de ações está aberto)
  const [confirming, setConfirming] = useS(null);    // { supplier, action: 'delete'|'deactivate'|'reactivate' } | null
  const [suppliers, setSuppliers] = useS(null);      // null = carregando
  const [productsList, setProductsList] = useS([]);  // pra contar produtos por fornecedor
  const [loadError, setLoadError] = useS(null);
  const openEditClick = (s) => { setEditingFromClick(true);  setEditing(s); };
  const openNewBtn    = ()  => { setEditingFromClick(false); setEditing("new"); };
  const closeEdit     = ()  => { setEditing(null); setEditingFromClick(false); };

  // Filtros e visualização
  const [q,            setQ]            = useS("");
  const [statusFilter, setStatusFilter] = useS("ativo"); // ativo | inativo
  const [viewMode,     setViewMode]     = useS("grid");  // grid | list
  const [sortBy,       setSortBy]       = useS("name");
  const [sortDir,      setSortDir]      = useS("asc");
  const toggleSort = (key) => {
    if (sortBy === key) setSortDir(d => d === "asc" ? "desc" : "asc");
    else { setSortBy(key); setSortDir("asc"); }
  };

  const reload = async () => {
    try {
      const [s, p] = await Promise.all([dbListSuppliers(), dbListProducts("all")]);
      setSuppliers(s);
      setProductsList(p);
      setLoadError(null);
    } catch (e) {
      setLoadError(e?.message || String(e));
      setSuppliers([]);
    }
  };
  useE(() => { reload(); }, []);

  const list = suppliers || [];
  const productCountFor = (id) => productsList.filter(p => p.supplier === id && p.is_active !== false).length;

  // Contagens (independentes do filtro atual — pra mostrar nos botões do toggle)
  const countActive   = list.filter(s => s.status === "ativo").length;
  const countInactive = list.filter(s => s.status === "inativo").length;

  // Aplica filtro de status + busca
  const filtered = list.filter(s => {
    if (s.status !== statusFilter) return false;
    if (q) {
      const t = q.toLowerCase();
      const blob = [s.name, s.taxId, s.email, s.contact, s.city, s.phone].filter(Boolean).join(" ").toLowerCase();
      if (!blob.includes(t)) return false;
    }
    return true;
  });

  // Ordenação (vale pros dois modos de visualização)
  const sortKey = {
    name:      s => s.name      || "",
    taxId:     s => s.taxId     || "",
    city:      s => s.city      || "",
    contact:   s => s.contact   || "",
    phone:     s => s.phone     || "",
    email:     s => s.email     || "",
    products:  s => productCountFor(s.id),
    status:    s => s.status    || "",
  };
  const cmp = (a, b) => {
    const fn = sortKey[sortBy] || sortKey.name;
    const va = fn(a), vb = fn(b);
    if (typeof va === "string") return va.localeCompare(vb, "pt-BR");
    return va - vb;
  };
  const sorted = [...filtered].sort((a, b) => sortDir === "asc" ? cmp(a, b) : -cmp(a, b));

  const runConfirmAction = async () => {
    if (!confirming) return;
    const { supplier, action } = confirming;
    try {
      if (action === "delete") {
        await dbDeleteSupplier(supplier.id);
      } else if (action === "deactivate") {
        await dbSetSupplierStatus(supplier.id, "inativo");
      } else if (action === "reactivate") {
        await dbSetSupplierStatus(supplier.id, "ativo");
      }
      setConfirming(null);
      await reload();
    } catch (e) {
      // Fica com o modal aberto e exibe a mensagem; o usuário pode fechar.
      setConfirming({ ...confirming, error: e?.message || "Falha ao executar a ação." });
    }
  };

  return (
    <div className="screen">
      <PageHead
        title="Fornecedores"
        sub={suppliers == null
              ? "Carregando..."
              : `${list.filter(s => s.status === "ativo").length} ativos · ${list.length} cadastrados`}
        actions={<button className="btn btn-primary" onClick={openNewBtn}><Icon name="plus" size={14}/> Novo fornecedor</button>}
      />

      {loadError && (
        <div style={{ padding: 16, color: "var(--pink-600)", fontWeight: 600 }}>
          Erro ao carregar: {loadError}
        </div>
      )}

      {/* Barra de filtros: busca + status (ativos/inativos) + visualização (grade/lista) */}
      <div className="filter-bar">
        <div className="input-icon" style={{ flex: 1 }}>
          <Icon name="search" size={16}/>
          <input className="input" placeholder="Buscar por nome, documento, contato, e-mail..."
                 value={q} onChange={e => setQ(e.target.value)}/>
        </div>
        <div className="seg-toggle" title="Filtrar por status">
          <button className={statusFilter === "ativo"   ? "active" : ""} onClick={() => setStatusFilter("ativo")}>Ativos · {countActive}</button>
          <button className={statusFilter === "inativo" ? "active" : ""} onClick={() => setStatusFilter("inativo")}>Inativos · {countInactive}</button>
        </div>
        <div className="seg-toggle" title="Modo de visualização">
          <button className={viewMode === "grid" ? "active" : ""} onClick={() => setViewMode("grid")} title="Cards"><Icon name="grid" size={14}/></button>
          <button className={viewMode === "list" ? "active" : ""} onClick={() => setViewMode("list")} title="Lista"><Icon name="list" size={14}/></button>
        </div>
      </div>

      {suppliers == null ? (
        <div className="card card-pad" style={{ textAlign: "center", padding: 40, color: "var(--text-mute)" }}>Carregando...</div>
      ) : list.length === 0 ? (
        <div className="card card-pad" style={{ textAlign: "center", padding: 40, color: "var(--text-mute)" }}>
          Nenhum fornecedor cadastrado ainda.
        </div>
      ) : sorted.length === 0 ? (
        <div className="card card-pad" style={{ textAlign: "center", padding: 40, color: "var(--text-mute)" }}>
          {q
            ? "Nada encontrado para essa busca."
            : statusFilter === "ativo"   ? "Nenhum fornecedor ativo."
            : "Nenhum fornecedor inativo."}
        </div>
      ) : viewMode === "grid" ? (
        /* ─── Visualização em CARDS ─────────────────────────────── */
        <div className="supplier-grid">
          {sorted.map(s => {
            const initials = (s.name || "").split(" ").map(w => w[0]).filter(Boolean).slice(0, 2).join("").toUpperCase();
            const isInactive = s.status === "inativo";
            return (
              <div key={s.id}
                   className={"supplier-card " + (isInactive ? "muted" : "")}
                   onClick={() => openEditClick(s)}
                   style={{ cursor: "pointer", position: "relative" }}>
                {/* botão de 3 pontos verticais (canto superior direito) */}
                <button className="btn-icon-mini" title="Ações"
                        style={{ position: "absolute", top: 12, right: 12 }}
                        onClick={(e) => {
                          e.stopPropagation();
                          setOpenMenu(openMenu?.row?.id === s.id
                            ? null
                            : { row: s, anchor: e.currentTarget });
                        }}>
                  <Icon name="moreV" size={16}/>
                </button>

                <div className="supplier-head">
                  <div className="supplier-avatar">{initials}</div>
                  <div style={{ flex: 1, minWidth: 0, paddingRight: 36 /* espaço pro botão 3 pontos */ }}>
                    <div className="supplier-name">{s.name}</div>
                    <div className="supplier-cnpj">{s.taxId || "—"}</div>
                  </div>
                </div>
                <div className="supplier-contact">
                  {s.contact && (
                    <div style={{ display: "flex", alignItems: "center", gap: 6, fontSize: 12 }}>
                      <Icon name="user" size={12}/> {s.contact}
                    </div>
                  )}
                  <div style={{ fontSize: 12, color: "var(--text-mute)", display: "flex", gap: 6 }}>
                    <span style={{ fontWeight: 600, minWidth: 50 }}>Whatsapp:</span>
                    <span style={{ color: s.phone ? "var(--text-soft)" : "var(--text-mute)" }}>{s.phone || "—"}</span>
                  </div>
                  <div style={{ fontSize: 12, color: "var(--text-mute)", display: "flex", gap: 6 }}>
                    <span style={{ fontWeight: 600, minWidth: 50 }}>Email:</span>
                    <span style={{ color: s.email ? "var(--text-soft)" : "var(--text-mute)", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{s.email || "—"}</span>
                  </div>
                  <div style={{ fontSize: 12, color: "var(--text-mute)", display: "flex", gap: 6 }}>
                    <span style={{ fontWeight: 600, minWidth: 50 }}>Cidade:</span>
                    <span style={{ color: s.city ? "var(--text-soft)" : "var(--text-mute)" }}>{s.city || "—"}</span>
                  </div>
                </div>
                <div style={{ display: "flex", gap: 8, marginTop: 12, alignItems: "center" }}>
                  <span className="badge" style={{ background: "var(--pink-50, #FFE1EC)", color: "var(--pink-700, #C9186F)" }}>
                    {productCountFor(s.id)} produtos
                  </span>
                  {isInactive && <span className="badge">inativo</span>}
                </div>
              </div>
            );
          })}
        </div>
      ) : (
        /* ─── Visualização em LISTA (tabela) ─────────────────────── */
        <div className="card card-flush" style={{ overflow: "visible" }}>
         <div className="table-x-scroll">
          <table className="table">
            <thead>
              <tr>
                <SortableTh col="name"     align="left"               sortBy={sortBy} sortDir={sortDir} onSort={toggleSort}>Nome</SortableTh>
                <SortableTh col="taxId"    align="left"  width={170}  sortBy={sortBy} sortDir={sortDir} onSort={toggleSort}>CNPJ / CPF</SortableTh>
                <SortableTh col="city"     align="left"               sortBy={sortBy} sortDir={sortDir} onSort={toggleSort}>Cidade</SortableTh>
                <SortableTh col="phone"    align="left"  width={150}  sortBy={sortBy} sortDir={sortDir} onSort={toggleSort}>Telefone</SortableTh>
                <SortableTh col="email"    align="left"               sortBy={sortBy} sortDir={sortDir} onSort={toggleSort}>E-mail</SortableTh>
                <SortableTh col="products" align="center" width={100} sortBy={sortBy} sortDir={sortDir} onSort={toggleSort}>Produtos</SortableTh>
                <SortableTh col="status"   align="center" width={100} sortBy={sortBy} sortDir={sortDir} onSort={toggleSort}>Status</SortableTh>
                <th style={{ textAlign: "right", width: 90 }}>Ações</th>
              </tr>
            </thead>
            <tbody>
              {sorted.map(s => {
                const isInactive = s.status === "inativo";
                return (
                  <tr key={s.id} onClick={() => openEditClick(s)}
                      style={{ cursor: "pointer", opacity: isInactive ? 0.65 : 1 }}>
                    <td style={{ fontWeight: 600 }}>{s.name}</td>
                    <td className="tabular" style={{ fontFamily: "var(--font-mono)", fontSize: 13, color: "var(--text-soft)" }}>{s.taxId || "—"}</td>
                    <td style={{ color: "var(--text-soft)" }}>{s.city || "—"}</td>
                    <td className="tabular" style={{ fontFamily: "var(--font-mono)", fontSize: 13, color: "var(--text-soft)" }}>{s.phone || "—"}</td>
                    <td style={{ color: "var(--text-soft)", fontSize: 13 }}>{s.email || "—"}</td>
                    <td className="tabular" style={{ textAlign: "center", fontWeight: 700 }}>{productCountFor(s.id)}</td>
                    <td style={{ textAlign: "center" }}>
                      {isInactive
                        ? <span className="badge">inativo</span>
                        : <span className="badge badge-ok"><span className="dot"/>ativo</span>}
                    </td>
                    <td style={{ textAlign: "right" }} onClick={e => e.stopPropagation()}>
                      <button className="btn-icon-mini" title="Ações"
                              onClick={(e) => {
                                e.stopPropagation();
                                setOpenMenu(openMenu?.row?.id === s.id
                                  ? null
                                  : { row: s, anchor: e.currentTarget });
                              }}>
                        <Icon name="more" size={16}/>
                      </button>
                    </td>
                  </tr>
                );
              })}
            </tbody>
          </table>
         </div>
        </div>
      )}

      {openMenu && (() => {
        const s = openMenu.row;
        const isInactive = s.status === "inativo";
        return (
          <RowActionMenu
            anchor={openMenu.anchor}
            onClose={() => setOpenMenu(null)}
            items={[
              { icon: "trash",
                label: "Excluir", danger: true,
                onClick: () => setConfirming({ supplier: s, action: "delete" }) },
              { icon: isInactive ? "refresh" : "archive",
                label: isInactive ? "Reativar" : "Inativar",
                onClick: () => setConfirming({ supplier: s, action: isInactive ? "reactivate" : "deactivate" }) },
            ]}
          />
        );
      })()}

      {editing && (
        <SupplierForm
          supplier={editing === "new" ? null : editing}
          hideDangerActions={editingFromClick}
          onClose={closeEdit}
          onSaved={async () => { closeEdit(); await reload(); }}
          onRequestDelete={(s)     => { closeEdit(); setConfirming({ supplier: s, action: "delete" }); }}
          onRequestDeactivate={(s) => { closeEdit(); setConfirming({ supplier: s, action: "deactivate" }); }}
          onRequestReactivate={(s) => { closeEdit(); setConfirming({ supplier: s, action: "reactivate" }); }}
        />
      )}

      {confirming && (
        <ConfirmModal
          title={
            confirming.action === "delete"     ? "Excluir fornecedor?" :
            confirming.action === "deactivate" ? "Inativar fornecedor?" :
                                                  "Reativar fornecedor?"
          }
          message={
            confirming.action === "delete"
              ? `Tem certeza que deseja excluir "${confirming.supplier.name}"? Essa ação não pode ser desfeita.`
            : confirming.action === "deactivate"
              ? `"${confirming.supplier.name}" deixará de aparecer como ativo, mas continuará no sistema. Pode reativar depois.`
              : `"${confirming.supplier.name}" voltará a aparecer como ativo.`
          }
          confirmLabel={
            confirming.action === "delete"     ? "Excluir" :
            confirming.action === "deactivate" ? "Inativar" :
                                                  "Reativar"
          }
          danger={confirming.action === "delete"}
          error={confirming.error}
          onConfirm={runConfirmAction}
          onCancel={() => setConfirming(null)}
        />
      )}
    </div>
  );
}

/* ────────────────────────────────────────────────────────────
   Modal de confirmação genérico (usado pelos cards e pelo form
   ao excluir/inativar/reativar). Mantém o look-and-feel do design
   system: backdrop + card pequeno + botão danger ou primary.
   ──────────────────────────────────────────────────────────── */
function ConfirmModal({ title, message, confirmLabel, danger, error, onConfirm, onCancel }) {
  const [busy, setBusy] = useS(false);
  const go = async () => {
    if (busy) return;
    setBusy(true);
    try { await onConfirm?.(); } finally { setBusy(false); }
  };
  return (
    <div className="modal-backdrop" style={{ zIndex: 250 }}>
      <div className="modal" style={{ width: 420 }} onClick={e => e.stopPropagation()}>
        <div className="modal-head">
          <h2 style={{ margin: 0, fontSize: 18, fontWeight: 700 }}>{title}</h2>
          <button className="btn-icon-mini" onClick={onCancel} disabled={busy}><Icon name="close" size={16}/></button>
        </div>
        <div style={{ fontSize: 14, color: "var(--text-soft)", lineHeight: 1.5 }}>
          {message}
        </div>
        {error && (
          <div style={{ fontSize: 13, color: "var(--pink-600)", fontWeight: 600, marginTop: 12, textAlign: "center" }}>
            {error}
          </div>
        )}
        <div style={{ display: "flex", gap: 10, marginTop: 20, justifyContent: "flex-end" }}>
          <button className="btn btn-secondary" onClick={onCancel} disabled={busy}>Cancelar</button>
          <button className={"btn " + (danger ? "btn-danger" : "btn-primary")} onClick={go} disabled={busy}>
            {busy ? "Processando..." : confirmLabel}
          </button>
        </div>
      </div>
    </div>
  );
}

/* ────────────────────────────────────────────────────────────
   SupplierForm — formulário completo de cadastro/edição.
   - Aplica máscara CPF/CNPJ conforme o tipo (PJ/PF) e telefone BR.
   - Valida e-mail (precisa ter @ e algo após o ponto).
   - Footer condicional:
        novo:    Cancelar (esq) | Cadastrar (dir)
        editar:  Cancelar (esq) | Excluir + Inativar/Reativar (centro) | Salvar (dir)
   ──────────────────────────────────────────────────────────── */
function SupplierForm({ supplier, onClose, onSaved, onRequestDelete, onRequestDeactivate, onRequestReactivate, hideDangerActions = false }) {
  const isNew     = !supplier;
  const isInactive = supplier?.status === "inativo";
  const { user } = useApp();
  const [showHistory, setShowHistory] = useS(false);

  const [kind,    setKind]    = useS(supplier?.taxIdType || "pj"); // 'pj' | 'pf'
  const [name,    setName]    = useS(supplier?.name    || "");
  const [taxId,   setTaxId]   = useS("");
  const [city,    setCity]    = useS(supplier?.city    || "");
  const [contact, setContact] = useS(supplier?.contact || "");
  const [phone,   setPhone]   = useS("");
  const [email,   setEmail]   = useS(supplier?.email   || "");
  const [errors,  setErrors]  = useS({});       // { name?, taxId?, email? }
  const [saving,  setSaving]  = useS(false);
  const [saveError, setSaveError] = useS(null);

  // Mesmo padrão do ProductForm: borda + halo rosa quando o campo tem erro
  const errStyle = (field) => errors[field]
    ? { borderColor: "var(--pink-500)", boxShadow: "0 0 0 3px var(--pink-100)" }
    : undefined;
  const clearFieldError = (field) =>
    setErrors(prev => prev[field] ? { ...prev, [field]: undefined } : prev);

  // Aplica máscara nos valores que vieram do BD ao montar
  useE(() => {
    setTaxId((supplier?.taxIdType === "pf" ? maskCPF : maskCNPJ)(supplier?.taxId || ""));
    setPhone(maskPhoneBR(supplier?.phone || ""));
  }, [supplier]);

  const isPF = kind === "pf";

  // Quando troca o tipo, reaplica a máscara correta no taxId
  const onChangeKind = (k) => {
    setKind(k);
    const digits = String(taxId || "").replace(/\D/g, "");
    setTaxId((k === "pf" ? maskCPF : maskCNPJ)(digits));
  };

  const validate = () => {
    const e = {};
    if (!name.trim()) e.name = "Informe o " + (isPF ? "nome completo." : "nome / razão social.");
    if (email.trim() && !isValidEmail(email)) e.email = "E-mail inválido. Precisa conter @ e domínio (ex.: nome@dominio.com).";
    setErrors(e);
    return Object.keys(e).length === 0;
  };

  const submit = async () => {
    if (saving) return;
    if (!validate()) return;
    setSaving(true);
    setSaveError(null);
    try {
      await dbUpsertSupplier({
        id:        supplier?.id || null,
        name:      name.trim(),
        taxId:     taxId.trim(),
        taxIdType: kind,
        contact:   isPF ? "" : contact.trim(),
        phone:     phone.trim(),
        email:     email.trim(),
        city:      city.trim(),
        user_id:   user?.id || null,
      });
      await onSaved?.();
    } catch (e) {
      setSaveError(e?.message || "Falha ao salvar o fornecedor.");
      setSaving(false);
    }
  };

  return (
    <div className="modal-backdrop">
      <div className="modal modal-form modal-split">
        <div className="modal-head-pinned">
          <h2 style={{ margin: 0, fontSize: 22, fontWeight: 700 }}>
            {isNew ? "Cadastrar fornecedor" : supplier.name}
          </h2>
          <button className="btn-icon-mini" onClick={onClose} disabled={saving}><Icon name="close" size={18}/></button>
        </div>

        <div className="modal-body">
          <div className="field" style={{ marginBottom: 16 }}>
            <label className="label">Tipo de cadastro</label>
            <div className="seg-toggle" style={{ alignSelf: "flex-start" }}>
              <button type="button" className={kind === "pj" ? "active" : ""} onClick={() => onChangeKind("pj")} disabled={saving}>Pessoa jurídica</button>
              <button type="button" className={kind === "pf" ? "active" : ""} onClick={() => onChangeKind("pf")} disabled={saving}>Pessoa física</button>
            </div>
          </div>

          <div className="form-grid">
            <div className="field" style={{ gridColumn: "1 / -1" }}>
              <label className="label">{isPF ? "Nome completo" : "Razão social"}</label>
              <input className="input" value={name}
                     onChange={e => { setName(e.target.value); clearFieldError("name"); }}
                     placeholder={isPF ? "Ex.: Maria Aparecida da Silva" : "Ex.: Bordados Brasil Ltda."}
                     style={errStyle("name")} disabled={saving}/>
              {errors.name && (
                <div style={{ fontSize: 12, color: "var(--pink-600)", marginTop: 6, fontWeight: 600 }}>{errors.name}</div>
              )}
            </div>

            <div className="field" style={{ gridColumn: "1 / -1" }}>
              <label className="label">{isPF ? "CPF" : "CNPJ"}</label>
              <input className="input" value={taxId} inputMode="numeric"
                     onChange={e => setTaxId((isPF ? maskCPF : maskCNPJ)(e.target.value))}
                     placeholder={isPF ? "000.000.000-00" : "00.000.000/0000-00"}
                     style={{ fontFamily: "var(--font-mono)", fontSize: 16, letterSpacing: "0.5px" }}
                     disabled={saving}/>
            </div>

            <div className="field" style={isPF ? { gridColumn: "1 / -1" } : undefined}>
              <label className="label">Cidade / UF</label>
              <input className="input" value={city} onChange={e => setCity(e.target.value)}
                     placeholder="Ex.: São Paulo / SP" disabled={saving}/>
            </div>

            {!isPF && (
              <div className="field">
                <label className="label">Contato</label>
                <input className="input" value={contact} onChange={e => setContact(e.target.value)}
                       placeholder="Nome da pessoa que atende" disabled={saving}/>
              </div>
            )}

            <div className="field" style={{ gridColumn: "1 / -1" }}>
              <label className="label">Telefone {isPF && <span className="label-opt">/ WhatsApp</span>}</label>
              <input className="input" value={phone} inputMode="numeric"
                     onChange={e => setPhone(maskPhoneBR(e.target.value))}
                     placeholder="(11) 90000-0000"
                     style={{ fontFamily: "var(--font-mono)" }}
                     disabled={saving}/>
            </div>

            <div className="field" style={{ gridColumn: "1 / -1" }}>
              <label className="label">E-mail <span className="label-opt">(opcional)</span></label>
              <input className="input" value={email} type="email"
                     onChange={e => { setEmail(e.target.value); clearFieldError("email"); }}
                     placeholder="contato@fornecedor.com"
                     style={errStyle("email")} disabled={saving}/>
              {errors.email && (
                <div style={{ fontSize: 12, color: "var(--pink-600)", marginTop: 6, fontWeight: 600 }}>{errors.email}</div>
              )}
            </div>
          </div>

          {saveError && (
            <div style={{ fontSize: 13, color: "var(--pink-600)", fontWeight: 600, marginTop: 12, textAlign: "center" }}>
              {saveError}
            </div>
          )}

          {!isNew && supplier && (
            <AuditFooter entity="suppliers" entityId={supplier.id}
                         createdAt={supplier.created_at} createdByName={supplier.created_by_name}
                         updatedAt={supplier.updated_at} updatedByName={supplier.updated_by_name}
                         onOpenHistory={() => setShowHistory(true)}/>
          )}
        </div>

        {/* Footer:
            - Novo:    space-between → Cancelar (esq) · Cadastrar (dir)
            - Editar:  space-between com 3 grupos → Cancelar (esq) · Excluir+Inativar (centro) · Salvar (dir) */}
        <div className="modal-foot-pinned" style={{ justifyContent: "space-between" }}>
          <button className="btn btn-secondary" onClick={onClose} disabled={saving}>Cancelar</button>

          {!isNew && !hideDangerActions && (
            <div style={{ display: "flex", gap: 10 }}>
              <button className="btn btn-danger" onClick={() => onRequestDelete?.(supplier)} disabled={saving}
                      title="Remove o fornecedor do banco. Falha se houver produtos vinculados.">
                <Icon name="trash" size={14}/> Excluir
              </button>
              {isInactive ? (
                <button className="btn btn-primary" onClick={() => onRequestReactivate?.(supplier)} disabled={saving}>
                  <Icon name="refresh" size={14}/> Reativar
                </button>
              ) : (
                <button className="btn btn-secondary" onClick={() => onRequestDeactivate?.(supplier)} disabled={saving}>
                  <Icon name="archive" size={14}/> Inativar
                </button>
              )}
            </div>
          )}

          <button className="btn btn-primary" onClick={submit} disabled={saving}>
            <Icon name="check" size={14}/> {saving ? "Salvando..." : (isNew ? "Cadastrar" : "Salvar")}
          </button>
        </div>

        {showHistory && !isNew && supplier && (
          <AuditHistoryModal entity="suppliers" entityId={supplier.id}
                             title={`Histórico — ${supplier.name}`}
                             onClose={() => setShowHistory(false)}/>
        )}
      </div>
    </div>
  );
}

/* ════════════════════════════════════════════════════════════
   VENDAS (histórico, com edição)
   ════════════════════════════════════════════════════════════ */
function Vendas() {
  const { user } = useApp();
  // scope:
  //   "caixa" → vendas do caixa atual em aberto (numeração #1, #2, … por sessão)
  //   "todas" → histórico completo (#sale_number global)
  // Default = "caixa" quando há sessão aberta, "todas" caso contrário.
  const [scope, setScope] = useS(null);            // null = ainda não decidiu (loading inicial)
  const [activeSession, setActiveSession] = useS(null); // { id, session_number, opened_at, ... }
  const [filter, setFilter]   = useS("todas");        // todas | concluida | cancelada
  const [q, setQ]             = useS("");
  // Esconde pagamentos de curso por default. Toggle "Mostrar cursos" persiste.
  const [showCourses, setShowCourses] = useStickyState("pdv:vendas.showCourses", false);
  const [sales, setSales]     = useS(null);           // null = carregando
  const [loadError, setLoadError] = useS(null);
  const [openMenu, setOpenMenu]   = useS(null);
  const [viewing, setViewing] = useS(null);           // sale.id (modal "Ver detalhes")
  const [editing, setEditing] = useS(null);           // sale.id (modal "Editar")
  const [confirming, setConfirming] = useS(null);     // sale (cancelar venda)
  // Ordenação por cabeçalho — default: mais recentes primeiro
  const [sortBy,  setSortBy]  = useS("sold_at");
  const [sortDir, setSortDir] = useS("desc");
  const toggleSort = (key) => {
    if (sortBy === key) setSortDir(d => d === "asc" ? "desc" : "asc");
    else { setSortBy(key); setSortDir("asc"); }
  };

  // Carrega vendas do escopo atual. Em "caixa" precisa de activeSession;
  // em "todas" busca o histórico global. Sales = null durante o fetch
  // mostra "Carregando..." e evita exibir dados antigos do escopo anterior.
  const reload = async (scopeOverride) => {
    const sc = scopeOverride || scope;
    if (!sc) return;
    setSales(null);
    setLoadError(null);
    try {
      let data;
      if (sc === "caixa") {
        if (!activeSession) { setSales([]); return; }
        data = await dbListSessionSales({ sessionId: activeSession.id });
      } else {
        // Server-side filter: 'venda' (default) ou 'all' (inclui cursos).
        data = await dbListSales({ limit: 300, origin: showCourses ? "all" : "venda" });
      }
      setSales(data);
    } catch (e) {
      setLoadError(e?.message || String(e));
      setSales([]);
    }
  };

  // Boot inicial: descobre se há caixa aberto e decide o escopo default.
  useE(() => {
    let alive = true;
    (async () => {
      try {
        const sess = await dbGetActiveCashSession();
        if (!alive) return;
        setActiveSession(sess);
        setScope(sess ? "caixa" : "todas");
      } catch {
        if (!alive) return;
        setScope("todas");
      }
    })();
    return () => { alive = false; };
  }, []);

  // Recarrega ao mudar de escopo, sessão ativa ou toggle de cursos.
  useE(() => { if (scope) reload(scope); }, [scope, activeSession?.id, showCourses]);

  // No escopo "caixa" o servidor retorna tudo da sessão; filtramos cursos
  // aqui no front quando o toggle está off.
  const list = (sales || []).filter(s => {
    if (scope === "caixa" && !showCourses && s.origin === "curso") return false;
    return true;
  });
  const countTotal  = list.length;
  const countDone   = list.filter(s => s.status === "concluida").length;
  const countCancel = list.filter(s => s.status === "cancelada").length;

  const filtered = list.filter(s => {
    if (filter === "concluida" && s.status !== "concluida") return false;
    if (filter === "cancelada" && s.status !== "cancelada") return false;
    if (q) {
      const t = q.toLowerCase();
      const blob = [
        "#" + s.sale_number,
        s.cashier_name,
        BRL(Number(s.total) || 0),
        ...((s.payment_methods || []).map(m => PAYMENT_LABELS[m] || m)),
        ...((s.products || []).map(p => p.name)),
      ].filter(Boolean).join(" ").toLowerCase();
      if (!blob.includes(t)) return false;
    }
    return true;
  });

  // Ordenação por coluna do cabeçalho. No modo "caixa", ordenar por "Venda"
  // usa a posição na sessão (#1, #2…); no modo "todas", usa o sale_number global.
  const sortKey = {
    sale_number:  s => scope === "caixa"
                        ? (Number(s.position_in_session) || 0)
                        : (Number(s.sale_number) || 0),
    sold_at:      s => new Date(s.sold_at || 0).getTime(),
    products:     s => (s.products || []).map(p => p.name).join(" ").toLowerCase(),
    units_total:  s => Number(s.units_total) || 0,
    total:        s => Number(s.total) || 0,
    payment:      s => (s.payment_methods || []).join(" "),
    status:       s => s.status || "",
    cashier_name: s => s.cashier_name || "",
  };
  const cmp = (a, b) => {
    const fn = sortKey[sortBy] || sortKey.sold_at;
    const va = fn(a), vb = fn(b);
    if (typeof va === "string") return va.localeCompare(vb, "pt-BR");
    return va - vb;
  };
  const sorted = [...filtered].sort((a, b) => sortDir === "asc" ? cmp(a, b) : -cmp(a, b));

  const fmtDate = (s) => {
    if (!s) return ["—", ""];
    try {
      const d = new Date(s);
      return [
        d.toLocaleDateString("pt-BR", { day: "2-digit", month: "2-digit", year: "2-digit" }),
        d.toLocaleTimeString("pt-BR", { hour: "2-digit", minute: "2-digit" }),
      ];
    } catch { return [String(s), ""]; }
  };

  return (
    <div className="screen">
      <PageHead
        title="Histórico de vendas"
        sub={sales == null
              ? "Carregando..."
              : scope === "caixa" && activeSession
                ? `Caixa #${activeSession.session_number} · ${countTotal} vendas · ${countDone} concluídas · ${countCancel} canceladas`
                : `${countTotal} vendas · ${countDone} concluídas · ${countCancel} canceladas`}
      />

      <div className="filter-bar">
        <div className="input-icon" style={{ flex: 1 }}>
          <Icon name="search" size={16}/>
          <input id="vendas-search" name="vendas-search"
                 className="input" placeholder="Buscar por número, operadora, total, pagamento..."
                 value={q} onChange={e => setQ(e.target.value)}/>
        </div>
        <div className="seg-toggle">
          <button className={filter === "todas"     ? "active" : ""} onClick={() => setFilter("todas")}>Todas · {countTotal}</button>
          <button className={filter === "concluida" ? "active" : ""} onClick={() => setFilter("concluida")}>Concluídas · {countDone}</button>
          <button className={filter === "cancelada" ? "active" : ""} onClick={() => setFilter("cancelada")}>Canceladas · {countCancel}</button>
        </div>
        <div className="seg-toggle">
          <button className={scope === "caixa" ? "active" : ""}
                  disabled={!activeSession}
                  title={!activeSession ? "Nenhum caixa em aberto" : undefined}
                  onClick={() => activeSession && setScope("caixa")}>
            {activeSession ? `Caixa atual #${activeSession.session_number}` : "Caixa atual"}
          </button>
          <button className={scope === "todas" ? "active" : ""}
                  onClick={() => setScope("todas")}>
            Todas as vendas
          </button>
        </div>
        <label style={{ display: "inline-flex", alignItems: "center", gap: 6, fontSize: 13,
                        color: "var(--text-soft)", cursor: "pointer", userSelect: "none" }}
               title="Inclui pagamentos de mensalidade de curso na listagem">
          <input type="checkbox" checked={showCourses} onChange={e => setShowCourses(e.target.checked)}/>
          Mostrar cursos
        </label>
      </div>

      {loadError && (
        <div style={{ padding: 16, color: "var(--pink-600)", fontWeight: 600 }}>
          Erro ao carregar: {loadError}
        </div>
      )}

      <div className="card card-flush" style={{ overflow: "visible" }}>
       <div className="table-x-scroll">
        <table className="table">
          <thead>
            <tr>
              <SortableTh col="sale_number"  align="center" width={80}                  sortBy={sortBy} sortDir={sortDir} onSort={toggleSort}>Venda</SortableTh>
              <SortableTh col="sold_at"      align="center" width={130}                 sortBy={sortBy} sortDir={sortDir} onSort={toggleSort}>Data / Hora</SortableTh>
              <SortableTh col="products"     align="left"                               sortBy={sortBy} sortDir={sortDir} onSort={toggleSort}>Produtos</SortableTh>
              <SortableTh col="units_total"  align="center" width={70}                  sortBy={sortBy} sortDir={sortDir} onSort={toggleSort}>Itens</SortableTh>
              <SortableTh col="total"        align="center" width={120}                 sortBy={sortBy} sortDir={sortDir} onSort={toggleSort}>Total</SortableTh>
              <SortableTh col="payment"      align="center"                             sortBy={sortBy} sortDir={sortDir} onSort={toggleSort}>Pagamento</SortableTh>
              <SortableTh col="status"       align="center" width={130}                 sortBy={sortBy} sortDir={sortDir} onSort={toggleSort}>Status</SortableTh>
              <SortableTh col="cashier_name" align="center"                             sortBy={sortBy} sortDir={sortDir} onSort={toggleSort}>Operadora</SortableTh>
              <th style={{ textAlign: "center", width: 80 }}>Ações</th>
            </tr>
          </thead>
          <tbody>
            {sales == null ? (
              <tr><td colSpan="9" style={{ textAlign: "center", padding: 40, color: "var(--text-mute)" }}>Carregando...</td></tr>
            ) : sorted.length === 0 ? (
              <tr><td colSpan="9" style={{ textAlign: "center", padding: 40, color: "var(--text-mute)" }}>
                {list.length === 0 ? "Nenhuma venda registrada ainda." : "Nada encontrado para essa busca."}
              </td></tr>
            ) : sorted.map(s => {
              const [date, time] = fmtDate(s.sold_at);
              const isCancelled = s.status === "cancelada";
              const productList = s.products || []; // [{ name, quantity }]
              // Formata cada produto: "Nome (N)" se quantity > 1, senão só o nome.
              const fmtProduct = (p) => {
                const qty = Number(p.quantity) || 0;
                return qty > 1 ? `${p.name} (${qty})` : p.name;
              };
              return (
                <tr key={s.id} onClick={() => setViewing(s.id)}
                    style={{ cursor: "pointer", opacity: isCancelled ? 0.7 : 1 }}>
                  <td style={{ textAlign: "center" }}>
                    <b style={{ fontFamily: "var(--font-mono)" }}>
                      #{scope === "caixa" ? s.position_in_session : s.sale_number}
                    </b>
                    {s.channel === "online" && (
                      <div style={{
                        display: "inline-block", marginLeft: 6,
                        padding: "1px 6px", borderRadius: 4,
                        background: "var(--pink-500)", color: "white",
                        fontSize: 9, fontWeight: 700, letterSpacing: ".05em",
                        verticalAlign: "middle",
                      }}>ONLINE</div>
                    )}
                    {s.origin === "curso" && (
                      <div style={{
                        display: "inline-block", marginLeft: 6,
                        padding: "1px 6px", borderRadius: 4,
                        background: "var(--info-500, #3B82F6)", color: "white",
                        fontSize: 9, fontWeight: 700, letterSpacing: ".05em",
                        verticalAlign: "middle",
                      }} title={s.course_name || "Pagamento de curso"}>CURSO</div>
                    )}
                  </td>
                  <td style={{ textAlign: "center" }}>
                    <div style={{ fontWeight: 500 }}>{date}</div>
                    <div style={{ fontSize: 11, color: "var(--text-mute)" }}>{time}</div>
                  </td>
                  <td style={{ fontSize: 13, minWidth: 180, maxWidth: 320 }}
                      title={productList.map(fmtProduct).join("\n")}>
                    {productList.length === 0 ? (
                      <span style={{ color: "var(--text-mute)" }}>—</span>
                    ) : productList.length === 1 ? (
                      <span>{fmtProduct(productList[0])}</span>
                    ) : (
                      /* Mais de um produto: lista vertical com bullet, com a
                         quantidade em parêntese se for maior que 1. */
                      <ul style={{ margin: 0, padding: 0, listStyle: "none", lineHeight: 1.4 }}>
                        {productList.map((p, i) => (
                          <li key={i} style={{ display: "flex", gap: 6 }}>
                            <span style={{ color: "var(--text-mute)" }}>•</span>
                            <span style={{ flex: 1, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>
                              {fmtProduct(p)}
                            </span>
                          </li>
                        ))}
                      </ul>
                    )}
                  </td>
                  <td className="tabular" style={{ textAlign: "center" }}>{Number(s.units_total) || 0}</td>
                  <td className="tabular" style={{ textAlign: "center", fontWeight: 700 }}>{BRL(Number(s.total) || 0)}</td>
                  <td style={{ textAlign: "center" }}>
                    {/* Pagamento: stacked vertical quando tem mais de um método;
                       inline quando é só um. */}
                    {(s.payment_methods || []).length === 0 ? (
                      <span style={{ color: "var(--text-mute)", fontSize: 12 }}>—</span>
                    ) : (
                      <div style={{
                        display: "flex",
                        flexDirection: s.payment_methods.length > 1 ? "column" : "row",
                        gap: s.payment_methods.length > 1 ? 4 : 6,
                        alignItems: "center",
                        justifyContent: "center",
                      }}>
                        {s.payment_methods.map(m => (
                          <span key={m} title={PAYMENT_LABELS[m] || m}
                                style={{
                                  background: (PAYMENT_COLORS[m] || "#888") + "22",
                                  color: PAYMENT_COLORS[m] || "#888",
                                  padding: "4px 8px", borderRadius: 6,
                                  display: "inline-flex", alignItems: "center", gap: 4,
                                  fontSize: 12, fontWeight: 600,
                                }}>
                            <Icon name={PAYMENT_ICON[m] || "money"} size={12}/>
                            {PAYMENT_LABELS[m] || m}
                          </span>
                        ))}
                      </div>
                    )}
                  </td>
                  <td style={{ textAlign: "center" }}>
                    {isCancelled
                      ? <span className="badge badge-bad"><span className="dot"/>Cancelada</span>
                      : <span className="badge badge-ok"><span className="dot"/>Concluída</span>}
                  </td>
                  <td style={{ textAlign: "center" }}>{s.cashier_name || "—"}</td>
                  <td style={{ textAlign: "center" }} onClick={e => e.stopPropagation()}>
                    <button className="btn-icon-mini" title="Ações"
                            onClick={(e) => {
                              e.stopPropagation();
                              setOpenMenu(openMenu?.row?.id === s.id
                                ? null
                                : { row: s, anchor: e.currentTarget });
                            }}>
                      <Icon name="more" size={16}/>
                    </button>
                  </td>
                </tr>
              );
            })}
          </tbody>
        </table>
       </div>
      </div>

      {openMenu && (() => {
        const s = openMenu.row;
        const isCancelled = s.status === "cancelada";
        return (
          <RowActionMenu
            anchor={openMenu.anchor}
            onClose={() => setOpenMenu(null)}
            items={[
              { icon: "eye",  label: "Ver detalhes", onClick: () => setViewing(s.id) },
              { icon: "edit", label: "Editar",
                disabled: isCancelled,
                title: isCancelled ? "Venda cancelada — só dá pra ver detalhes" : "",
                onClick: () => setEditing(s.id) },
            ]}
          />
        );
      })()}

      {viewing && (
        <SaleDetailModal saleId={viewing} onClose={() => setViewing(null)}/>
      )}
      {editing && (
        <SaleEditModal
          saleId={editing}
          onClose={() => setEditing(null)}
          onSaved={async () => { setEditing(null); await reload(); }}
          onRequestCancel={(s) => setConfirming(s)}
        />
      )}
      {confirming && (
        <ConfirmModal
          title="Cancelar venda?"
          message={`A venda #${confirming.sale_number} será marcada como cancelada e o estoque dos itens será estornado. Essa ação fica registrada no log de auditoria e não pode ser desfeita.`}
          confirmLabel="Cancelar venda"
          danger
          error={confirming.error}
          onCancel={() => setConfirming(null)}
          onConfirm={async () => {
            try {
              await dbCancelSale({ id: confirming.id, reason: "Cancelada via Histórico de vendas", userId: user?.id || null });
              setConfirming(null);
              setEditing(null);
              await reload();
            } catch (e) {
              setConfirming({ ...confirming, error: e?.message || "Falha ao cancelar." });
            }
          }}
        />
      )}
    </div>
  );
}

/* ────────────────────────────────────────────────────────────
   SaleDetailModal — visualização read-only de uma venda.
   Único botão de ação: "Imprimir recibo" (window.print()).
   ──────────────────────────────────────────────────────────── */
function SaleDetailModal({ saleId, onClose }) {
  const [data, setData]   = useS(null);
  const [error, setError] = useS(null);

  useE(() => {
    let alive = true;
    (async () => {
      try {
        const d = await dbGetSaleDetails(saleId);
        if (alive) setData(d);
      } catch (e) {
        if (alive) setError(e?.message || String(e));
      }
    })();
    return () => { alive = false; };
  }, [saleId]);

  const sale     = data?.sale;
  const items    = data?.items    || [];
  const payments = data?.payments || [];

  const fmtDateTime = (s) => {
    if (!s) return "—";
    try {
      const d = new Date(s);
      return d.toLocaleString("pt-BR", { day: "2-digit", month: "2-digit", year: "numeric", hour: "2-digit", minute: "2-digit" });
    } catch { return String(s); }
  };

  return (
    <div className="modal-backdrop">
      <div className="modal modal-form modal-split" onClick={e => e.stopPropagation()}>
        <div className="modal-head-pinned">
          <div>
            <h2 style={{ margin: 0, fontSize: 22, fontWeight: 700 }}>
              Venda {sale ? "#" + sale.sale_number : ""}
            </h2>
            {sale && (
              <div style={{ fontSize: 13, color: "var(--text-mute)", marginTop: 4 }}>
                {fmtDateTime(sale.sold_at)} · {sale.cashier_name || "—"}
                {sale.status === "cancelada" && <> · <strong style={{ color: "var(--bad-500, #DC2626)" }}>Cancelada</strong></>}
              </div>
            )}
          </div>
          <button className="btn-icon-mini" onClick={onClose}><Icon name="close" size={18}/></button>
        </div>

        <div className="modal-body">
          {error && <div style={{ color: "var(--pink-600)", fontWeight: 600 }}>Erro: {error}</div>}
          {!data && !error && <div style={{ color: "var(--text-mute)", textAlign: "center", padding: 20 }}>Carregando...</div>}

          {data && sale && (
            <>
              <div className="card card-flush" style={{ marginBottom: 16, border: "1px solid var(--border, #F5C6DA)", borderRadius: 10 }}>
                <table className="table" style={{ margin: 0 }}>
                  <thead>
                    <tr>
                      <th>Produto</th>
                      <th style={{ textAlign: "center", width: 70 }}>Qtd</th>
                      <th style={{ textAlign: "right",  width: 110 }}>Unit.</th>
                      <th style={{ textAlign: "right",  width: 120 }}>Total</th>
                    </tr>
                  </thead>
                  <tbody>
                    {items.map(i => (
                      <tr key={i.id}>
                        <td>
                          <b>{i.product_name}</b>
                          <div style={{ fontSize: 11, color: "var(--text-mute)", fontFamily: "var(--font-mono)" }}>{i.product_code}</div>
                        </td>
                        <td className="tabular" style={{ textAlign: "center" }}>{i.quantity}</td>
                        <td className="tabular" style={{ textAlign: "right" }}>{BRL(Number(i.unit_price))}</td>
                        <td className="tabular" style={{ textAlign: "right", fontWeight: 700 }}>{BRL(Number(i.line_total))}</td>
                      </tr>
                    ))}
                  </tbody>
                </table>
              </div>

              <div style={{ display: "flex", gap: 8, flexWrap: "wrap", marginBottom: 16 }}>
                {payments.map(p => (
                  <div key={p.id} style={{
                    background: (PAYMENT_COLORS[p.method] || "#888") + "22",
                    color:      PAYMENT_COLORS[p.method] || "#888",
                    padding: "8px 12px", borderRadius: 8,
                    display: "flex", alignItems: "center", gap: 8, fontWeight: 600,
                  }}>
                    <Icon name={PAYMENT_ICON[p.method] || "money"} size={14}/>
                    {PAYMENT_LABELS[p.method] || p.method} · {BRL(Number(p.amount))}
                    {Number(p.installments) > 1 && <span style={{ fontSize: 11, opacity: .8 }}>({p.installments}x)</span>}
                  </div>
                ))}
              </div>

              <div className="totals-box">
                <div className="pdv-total-row"><span>Subtotal</span><span>{BRL(Number(sale.subtotal))}</span></div>
                {Number(sale.discount) > 0 &&
                  <div className="pdv-total-row"><span>Desconto</span><span>− {BRL(Number(sale.discount))}</span></div>}
                {Number(sale.delivery_fee) > 0 &&
                  <div className="pdv-total-row"><span>Frete</span><span style={{ color: "var(--text-soft)" }}>+ {BRL(Number(sale.delivery_fee))}</span></div>}
                <div className="pdv-total-row pdv-total-grand"><span>Total</span><span>{BRL(Number(sale.total))}</span></div>
              </div>

              {sale.status === "cancelada" && (
                <div style={{ marginTop: 16, padding: 12, background: "#FEF2F2", borderRadius: 8, border: "1px solid #FECACA" }}>
                  <div style={{ fontWeight: 600, color: "var(--bad-500, #DC2626)" }}>Venda cancelada</div>
                  {sale.cancel_reason && <div style={{ fontSize: 13, marginTop: 4 }}>{sale.cancel_reason}</div>}
                  {sale.cancelled_at && <div style={{ fontSize: 12, color: "var(--text-mute)", marginTop: 4 }}>em {fmtDateTime(sale.cancelled_at)}</div>}
                </div>
              )}

              {sale.notes && (
                <div style={{ marginTop: 16, padding: 12, background: "var(--surface-2, #FFF6FA)", borderRadius: 8 }}>
                  <div style={{ fontSize: 11, color: "var(--text-mute)", textTransform: "uppercase", letterSpacing: ".05em", marginBottom: 4 }}>Observações</div>
                  <div style={{ fontSize: 13 }}>{sale.notes}</div>
                </div>
              )}
            </>
          )}
        </div>

        <div className="modal-foot-pinned" style={{ justifyContent: "space-between" }}>
          <button className="btn btn-secondary" onClick={onClose}>Fechar</button>
          <button className="btn btn-primary" onClick={() => window.print()} disabled={!sale}>
            <Icon name="print" size={14}/> Imprimir recibo
          </button>
        </div>
      </div>
    </div>
  );
}

/* ────────────────────────────────────────────────────────────
   SaleEditModal — edição completa de uma venda concluída.
   Operações:
     - X em cada item: chama dbDeleteSaleItem (imediato, recarrega).
     - "Adicionar item": form inline com produto/qtd/preço (imediato).
     - Pagamentos: editáveis localmente; aplicados em Salvar via
       dbReplaceSalePayments (substituição atômica).
     - Desconto e observações: aplicados em Salvar.
     - Cancelar venda: rota separada (botão danger).
   Os totais da venda são recalculados pelos triggers do BD; a UI
   re-fetcha após cada operação imediata pra refletir.
   ──────────────────────────────────────────────────────────── */
function SaleEditModal({ saleId, onClose, onSaved, onRequestCancel }) {
  const { user } = useApp();
  const [data, setData]     = useS(null);
  const [products, setProducts] = useS([]);
  const [error, setError]   = useS(null);
  const [discount, setDiscount] = useS("");
  const [notes, setNotes]       = useS("");
  const [paymentDraft, setPaymentDraft] = useS([]);     // [{ method, amount: maskBRL }]
  const [busy, setBusy]     = useS(false);              // operação imediata em curso (delete/add item)
  const [saving, setSaving] = useS(false);              // submit final
  const [saveError, setSaveError] = useS(null);

  // Form de "Adicionar item"
  const [showAddItem, setShowAddItem] = useS(false);
  const [newItemProductId, setNewItemProductId] = useS("");
  const [newItemQty,       setNewItemQty]       = useS("1");
  const [newItemPrice,     setNewItemPrice]     = useS("");
  const [newItemSearch,    setNewItemSearch]    = useS(""); // filtro por código ou nome

  // Carrega detalhes da venda + lista de produtos ativos
  const reload = async () => {
    try {
      const [d, plist] = await Promise.all([
        dbGetSaleDetails(saleId),
        dbListProducts("active"),
      ]);
      setData(d);
      setProducts(plist);
      const s = d?.sale;
      if (s) {
        setDiscount(maskBRL(String(Math.round((Number(s.discount) || 0) * 100))));
        setNotes(s.notes || "");
      }
      // Sincroniza paymentDraft com o que veio do BD
      setPaymentDraft((d?.payments || []).map(p => ({
        method: p.method,
        amount: maskBRL(String(Math.round((Number(p.amount) || 0) * 100))),
      })));
      setError(null);
    } catch (e) {
      setError(e?.message || String(e));
    }
  };
  useE(() => { reload(); }, [saleId]);

  const sale        = data?.sale;
  const items       = data?.items || [];
  const subtotal    = Number(sale?.subtotal) || 0;
  const discountVal = parseBRL(discount);
  const totalLive   = Math.max(0, subtotal - discountVal);
  const paymentsSum = paymentDraft.reduce((sum, p) => sum + parseBRL(p.amount), 0);
  const paymentDiff = totalLive - paymentsSum;

  // Quando seleciona um produto no form de novo item, preenche o preço atual
  const onPickNewProduct = (id) => {
    setNewItemProductId(id);
    const p = products.find(x => x.id === id);
    if (p) {
      setNewItemPrice(maskBRL(String(Math.round((Number(p.price) || 0) * 100))));
    }
  };

  // Filtra a lista de produtos do dropdown pelo termo de busca (código ou nome).
  // Exclui produtos com estoque zerado — venda só permitida com estoque > 0
  // (e o trigger fn_apply_stock_movement bloqueia no banco como segunda barreira).
  const filteredProducts = (() => {
    const t = newItemSearch.trim().toLowerCase();
    let base = products.filter(p => (Number(p.stock) || 0) > 0);
    if (t) {
      base = base.filter(p =>
        String(p.code || "").toLowerCase().includes(t) ||
        String(p.name || "").toLowerCase().includes(t)
      );
    }
    return base;
  })();

  // Auto-seleciona quando o termo de busca bate EXATO com um código —
  // pra fluxo "digita o código e dá Enter / Tab pra próxima campo".
  // Só auto-seleciona se o produto tiver estoque > 0.
  useE(() => {
    const t = newItemSearch.trim().toLowerCase();
    if (!t) return;
    const exact = products.find(p =>
      String(p.code || "").toLowerCase() === t && (Number(p.stock) || 0) > 0
    );
    if (exact && exact.id !== newItemProductId) {
      onPickNewProduct(exact.id);
    }
  }, [newItemSearch, products]);

  const removeItem = async (item) => {
    if (busy || saving) return;
    setBusy(true); setSaveError(null);
    try {
      await dbDeleteSaleItem({ itemId: item.id, userId: user?.id || null });
      await reload();
    } catch (e) {
      setSaveError(e?.message || "Falha ao remover o item.");
    } finally {
      setBusy(false);
    }
  };

  const addItem = async () => {
    if (busy || saving || !sale) return;
    if (!newItemProductId) { setSaveError("Selecione um produto."); return; }
    const qty   = Number(String(newItemQty).replace(",", ".")) || 0;
    const price = parseBRL(newItemPrice);
    if (qty <= 0)   { setSaveError("Quantidade deve ser maior que zero."); return; }
    if (price <= 0) { setSaveError("Preço unitário deve ser maior que zero."); return; }
    setBusy(true); setSaveError(null);
    try {
      await dbAddSaleItem({
        saleId:    sale.id,
        productId: newItemProductId,
        quantity:  qty,
        unitPrice: price,
        userId:    user?.id || null,
      });
      // Limpa o form e fecha
      setNewItemProductId(""); setNewItemQty("1"); setNewItemPrice("");
      setShowAddItem(false);
      await reload();
    } catch (e) {
      setSaveError(e?.message || "Falha ao adicionar item.");
    } finally {
      setBusy(false);
    }
  };

  const updatePayment = (idx, field, value) => {
    setPaymentDraft(arr => arr.map((p, i) => i === idx ? { ...p, [field]: value } : p));
  };
  const removePayment = (idx) => {
    setPaymentDraft(arr => arr.filter((_, i) => i !== idx));
  };
  const addPayment = () => {
    // Sugere preencher com o que está faltando pra fechar o total
    const missing = Math.max(0, totalLive - paymentsSum);
    setPaymentDraft(arr => [...arr, {
      method: "pix",
      amount: maskBRL(String(Math.round(missing * 100))),
    }]);
  };

  const submit = async () => {
    if (saving || busy || !sale) return;
    setSaving(true); setSaveError(null);
    try {
      // 1) atualiza desconto/observações (recalcula total no BD)
      await dbUpdateSaleMetadata({ id: sale.id, discount: discountVal, notes: notes.trim() });
      // 2) substitui pagamentos (filtra entradas vazias)
      const payments = paymentDraft
        .filter(p => p.method && parseBRL(p.amount) > 0)
        .map(p => ({ method: p.method, amount: parseBRL(p.amount) }));
      await dbReplaceSalePayments({ saleId: sale.id, payments });
      await onSaved?.();
    } catch (e) {
      setSaveError(e?.message || "Falha ao salvar.");
      setSaving(false);
    }
  };

  return (
    <div className="modal-backdrop">
      <div className="modal modal-form modal-split" onClick={e => e.stopPropagation()}>
        <div className="modal-head-pinned">
          <h2 style={{ margin: 0, fontSize: 22, fontWeight: 700 }}>
            Editar venda {sale ? "#" + sale.sale_number : ""}
          </h2>
          <button className="btn-icon-mini" onClick={onClose} disabled={saving || busy}><Icon name="close" size={18}/></button>
        </div>

        <div className="modal-body">
          {error && <div style={{ color: "var(--pink-600)", fontWeight: 600 }}>Erro: {error}</div>}
          {!data && !error && <div style={{ color: "var(--text-mute)", textAlign: "center", padding: 20 }}>Carregando...</div>}

          {data && sale && (
            <>
              {/* ─── Itens ─────────────────────────────────────────── */}
              <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: 8, flexWrap: "wrap", gap: 8 }}>
                <h3 style={{ margin: 0, fontSize: 14, fontWeight: 700, color: "var(--text-soft)", textTransform: "uppercase", letterSpacing: ".05em" }}>
                  Itens da venda
                </h3>
                {!showAddItem && (
                  <button className="btn btn-secondary btn-sm"
                          onClick={() => setShowAddItem(true)} disabled={busy || saving}>
                    <Icon name="plus" size={12}/> Adicionar item
                  </button>
                )}
              </div>

              <div className="card card-flush table-x-scroll" style={{ marginBottom: 16, border: "1px solid var(--border, #F5C6DA)", borderRadius: 10 }}>
                <table className="table" style={{ margin: 0 }}>
                  <thead>
                    <tr>
                      <th>Produto</th>
                      <th style={{ textAlign: "center", width: 60 }}>Qtd</th>
                      <th style={{ textAlign: "right",  width: 100 }}>Unit.</th>
                      <th style={{ textAlign: "right",  width: 110 }}>Total</th>
                      <th style={{ width: 36 }}/>
                    </tr>
                  </thead>
                  <tbody>
                    {items.length === 0 ? (
                      <tr><td colSpan="5" style={{ textAlign: "center", padding: 20, color: "var(--text-mute)" }}>
                        Nenhum item — adicione um produto abaixo.
                      </td></tr>
                    ) : items.map(i => (
                      <tr key={i.id}>
                        <td>
                          <b>{i.product_name}</b>
                          <div style={{ fontSize: 11, color: "var(--text-mute)", fontFamily: "var(--font-mono)" }}>{i.product_code}</div>
                        </td>
                        <td className="tabular" style={{ textAlign: "center" }}>{i.quantity}</td>
                        <td className="tabular" style={{ textAlign: "right" }}>{BRL(Number(i.unit_price))}</td>
                        <td className="tabular" style={{ textAlign: "right", fontWeight: 700 }}>{BRL(Number(i.line_total))}</td>
                        <td style={{ textAlign: "center" }}>
                          <button className="btn-icon-mini" title="Remover item"
                                  onClick={() => removeItem(i)} disabled={busy || saving}
                                  style={{ color: "var(--bad-500, #DC2626)" }}>
                            <Icon name="close" size={14}/>
                          </button>
                        </td>
                      </tr>
                    ))}
                  </tbody>
                </table>
              </div>

              {/* Form inline pra adicionar item */}
              {showAddItem && (
                <div style={{
                  border: "1px solid var(--pink-200, #F5C6DA)", borderRadius: 10,
                  padding: 12, marginBottom: 16, background: "var(--surface-2, #FFF6FA)",
                }}>
                  <div style={{ fontSize: 12, fontWeight: 700, color: "var(--text-soft)", marginBottom: 8 }}>Novo item</div>
                  <div className="form-grid" style={{ marginBottom: 10 }}>
                    {/* Busca + sugestões em tempo real (autocomplete).
                        Conforme o lojista digita o código ou o nome, a lista
                        abaixo mostra resultados clicáveis. Selecionar limpa
                        a busca e mostra o produto como chip. */}
                    {!newItemProductId && (
                      <div className="field" style={{ gridColumn: "1 / -1", position: "relative" }}>
                        <label className="label">
                          Buscar produto
                          {newItemSearch && (
                            <span style={{ color: "var(--text-mute)", fontWeight: 400, marginLeft: 6 }}>
                              · {filteredProducts.length} {filteredProducts.length === 1 ? "resultado" : "resultados"}
                            </span>
                          )}
                        </label>
                        <div className="input-icon">
                          <Icon name="search" size={16}/>
                          <input className="input" autoFocus value={newItemSearch}
                                 onChange={e => setNewItemSearch(e.target.value)}
                                 placeholder="Digite o código (ex.: 00001) ou parte do nome..."
                                 disabled={busy}/>
                        </div>

                        {/* Painel de sugestões — só aparece quando o lojista
                            está digitando algo e há resultados. */}
                        {newItemSearch && filteredProducts.length > 0 && (
                          <div style={{
                            position: "absolute", top: "100%", left: 0, right: 0,
                            marginTop: 4, background: "#fff",
                            border: "1px solid var(--border, #F5C6DA)", borderRadius: 8,
                            boxShadow: "0 8px 24px rgba(233, 30, 120, .12)",
                            zIndex: 30, maxHeight: 220, overflowY: "auto",
                          }}>
                            {filteredProducts.slice(0, 30).map(p => (
                              <button key={p.id} type="button"
                                      onClick={() => { onPickNewProduct(p.id); setNewItemSearch(""); }}
                                      style={{
                                        display: "flex", alignItems: "center", gap: 10,
                                        width: "100%", textAlign: "left",
                                        padding: "10px 12px", border: "none", background: "transparent",
                                        cursor: "pointer", fontSize: 13,
                                        borderBottom: "1px solid #f5f5f5",
                                      }}
                                      onMouseEnter={e => e.currentTarget.style.background = "var(--surface-2, #FFF6FA)"}
                                      onMouseLeave={e => e.currentTarget.style.background = "transparent"}>
                                <b style={{ fontFamily: "var(--font-mono)", color: "var(--pink-600)", minWidth: 60 }}>{p.code}</b>
                                <span style={{ flex: 1, fontWeight: 500 }}>{p.name}</span>
                                <span style={{ color: "var(--text-mute)", fontSize: 12 }}>{BRL(Number(p.price))}</span>
                              </button>
                            ))}
                          </div>
                        )}

                        {newItemSearch && filteredProducts.length === 0 && (
                          <div style={{
                            position: "absolute", top: "100%", left: 0, right: 0,
                            marginTop: 4, padding: 12, background: "#fff",
                            border: "1px solid var(--border, #F5C6DA)", borderRadius: 8,
                            color: "var(--text-mute)", fontSize: 13, textAlign: "center", zIndex: 30,
                          }}>
                            Nenhum produto encontrado para "{newItemSearch}".
                          </div>
                        )}
                      </div>
                    )}

                    {/* Chip do produto selecionado */}
                    {newItemProductId && (() => {
                      const sel = products.find(x => x.id === newItemProductId);
                      if (!sel) return null;
                      return (
                        <div className="field" style={{ gridColumn: "1 / -1" }}>
                          <label className="label">Produto selecionado</label>
                          <div style={{
                            display: "flex", alignItems: "center", gap: 10,
                            padding: "10px 12px", background: "#fff",
                            border: "1px solid var(--pink-200, #F5C6DA)", borderRadius: 8,
                          }}>
                            <b style={{ fontFamily: "var(--font-mono)", color: "var(--pink-600)" }}>{sel.code}</b>
                            <span style={{ flex: 1, fontWeight: 500, minWidth: 0, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>
                              {sel.name}
                            </span>
                            <button type="button" className="btn-icon-mini" title="Trocar produto"
                                    onClick={() => { setNewItemProductId(""); setNewItemPrice(""); setNewItemSearch(""); }}
                                    disabled={busy} style={{ color: "var(--bad-500, #DC2626)" }}>
                              <Icon name="close" size={14}/>
                            </button>
                          </div>
                        </div>
                      );
                    })()}

                    <div className="field">
                      <label className="label">Quantidade</label>
                      <input className="input tabular" value={newItemQty} inputMode="decimal"
                             onChange={e => setNewItemQty(e.target.value.replace(/[^\d.,]/g, ""))}
                             disabled={busy}/>
                    </div>
                    <div className="field">
                      <label className="label">Preço unitário</label>
                      <div className="input-currency-wrap">
                        <span className="input-currency-prefix">R$</span>
                        <input className="input input-currency tabular" value={newItemPrice} inputMode="numeric"
                               onChange={e => setNewItemPrice(maskBRL(e.target.value))} disabled={busy}/>
                      </div>
                    </div>
                  </div>
                  <div style={{ display: "flex", gap: 8, justifyContent: "flex-end", flexWrap: "wrap" }}>
                    <button className="btn btn-secondary btn-sm"
                            onClick={() => { setShowAddItem(false); setNewItemProductId(""); setNewItemQty("1"); setNewItemPrice(""); setNewItemSearch(""); }}
                            disabled={busy}>Cancelar</button>
                    <button className="btn btn-primary btn-sm" onClick={addItem} disabled={busy}>
                      <Icon name="check" size={12}/> {busy ? "Adicionando..." : "Adicionar"}
                    </button>
                  </div>
                </div>
              )}

              {/* ─── Pagamentos ────────────────────────────────────── */}
              <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: 8, flexWrap: "wrap", gap: 8 }}>
                <h3 style={{ margin: 0, fontSize: 14, fontWeight: 700, color: "var(--text-soft)", textTransform: "uppercase", letterSpacing: ".05em" }}>
                  Pagamentos
                </h3>
                <button className="btn btn-secondary btn-sm" onClick={addPayment} disabled={busy || saving}>
                  <Icon name="plus" size={12}/> Adicionar pagamento
                </button>
              </div>
              <div style={{
                border: "1px solid var(--border, #F5C6DA)", borderRadius: 10,
                padding: 10, marginBottom: 16,
              }}>
                {paymentDraft.length === 0 ? (
                  <div style={{ padding: 12, color: "var(--text-mute)", fontSize: 13, textAlign: "center" }}>
                    Nenhum pagamento — adicione um abaixo.
                  </div>
                ) : paymentDraft.map((p, idx) => (
                  /* flex-wrap permite que o select e o input quebrem em telas
                     estreitas. min-width nos itens evita squish ilegível. */
                  <div key={idx} style={{ display: "flex", gap: 8, alignItems: "center", marginBottom: 8, flexWrap: "wrap" }}>
                    <select className="input" value={p.method}
                            onChange={e => updatePayment(idx, "method", e.target.value)}
                            disabled={busy || saving}
                            style={{ flex: "1 1 130px", minWidth: 130 }}>
                      <option value="pix">Pix</option>
                      <option value="credito">Crédito</option>
                      <option value="debito">Débito</option>
                      <option value="dinheiro">Dinheiro</option>
                    </select>
                    <div className="input-currency-wrap" style={{ flex: "2 1 140px", minWidth: 140 }}>
                      <span className="input-currency-prefix">R$</span>
                      <input className="input input-currency tabular" value={p.amount} inputMode="numeric"
                             onChange={e => updatePayment(idx, "amount", maskBRL(e.target.value))}
                             disabled={busy || saving}/>
                    </div>
                    <button className="btn-icon-mini" title="Remover pagamento"
                            onClick={() => removePayment(idx)} disabled={busy || saving}
                            style={{ color: "var(--bad-500, #DC2626)", flexShrink: 0 }}>
                      <Icon name="close" size={14}/>
                    </button>
                  </div>
                ))}

                {/* Indicador de match entre pagamentos e total da venda */}
                {paymentDraft.length > 0 && (
                  <div style={{
                    fontSize: 12, marginTop: 4, padding: "6px 8px", borderRadius: 6,
                    background: Math.abs(paymentDiff) < 0.005 ? "var(--ok-50, #ECFDF5)" : "#FEF3F2",
                    color:      Math.abs(paymentDiff) < 0.005 ? "var(--ok-600, #047857)" : "var(--bad-500, #DC2626)",
                    fontWeight: 600,
                  }}>
                    {Math.abs(paymentDiff) < 0.005
                      ? `OK — pagamentos batem com o total de ${BRL(totalLive)}`
                      : paymentDiff > 0
                        ? `Falta ${BRL(paymentDiff)} pra cobrir o total de ${BRL(totalLive)}`
                        : `Pagamentos excedem o total em ${BRL(-paymentDiff)}`}
                  </div>
                )}
              </div>

              {/* ─── Desconto, total, observações ──────────────────── */}
              <div className="form-grid">
                <div className="field">
                  <label className="label">Desconto</label>
                  <div className="input-currency-wrap">
                    <span className="input-currency-prefix">R$</span>
                    <input className="input input-currency tabular" value={discount} inputMode="numeric"
                           onChange={e => setDiscount(maskBRL(e.target.value))} disabled={saving}/>
                  </div>
                </div>
                <div className="field">
                  <label className="label">Total final</label>
                  <input className="input tabular" value={BRL(totalLive)} readOnly
                         style={{ background: "var(--surface-2, #FFF6FA)", fontWeight: 700 }}/>
                </div>
                <div className="field" style={{ gridColumn: "1 / -1" }}>
                  <label className="label">
                    Observações <span style={{ color: "var(--text-mute)", fontWeight: 400 }}>· opcional</span>
                  </label>
                  <textarea className="input" rows={3} value={notes}
                            onChange={e => setNotes(e.target.value)} disabled={saving}
                            style={{ resize: "vertical", minHeight: 70 }}/>
                </div>
              </div>

              {saveError && (
                <div style={{ fontSize: 13, color: "var(--pink-600)", marginTop: 12, fontWeight: 600, textAlign: "center" }}>
                  {saveError}
                </div>
              )}
            </>
          )}
        </div>

        {/* Footer com 3 grupos: Cancelar (esq) · Cancelar venda (centro, danger) · Salvar (dir) */}
        <div className="modal-foot-pinned" style={{ justifyContent: "space-between" }}>
          <button className="btn btn-secondary" onClick={onClose} disabled={saving || busy}>Cancelar</button>
          {sale && (
            <button className="btn btn-danger" onClick={() => onRequestCancel?.(sale)} disabled={saving || busy}
                    title="Cancela a venda inteira; estorna estoque automaticamente.">
              <Icon name="close" size={14}/> Cancelar venda
            </button>
          )}
          <button className="btn btn-primary" onClick={submit} disabled={saving || busy || !data}>
            <Icon name="check" size={14}/> {saving ? "Salvando..." : "Salvar"}
          </button>
        </div>
      </div>
    </div>
  );
}

/* ════════════════════════════════════════════════════════════
   CAIXA (sessões, abertura/fechamento, edição)
   ════════════════════════════════════════════════════════════ */
function Caixa() {
  const { user, cashSession, setCashSession } = useApp();
  const [sessions, setSessions]     = useS(null);
  const [loadError, setLoadError]   = useS(null);
  const [movementFor, setMovementFor] = useS(null);   // { sessionId, defaultType }
  const [closeFor,    setCloseFor]    = useS(null);   // sessão a fechar
  const [detailsFor,  setDetailsFor]  = useS(null);   // sessão pra ver detalhes/reabrir
  const [openingCash, setOpeningCash] = useS(false);  // mostra modal de abertura
  const [sortBy,  setSortBy]  = useS("opened_at");
  const [sortDir, setSortDir] = useS("desc");
  const toggleSort = (key) => {
    if (sortBy === key) setSortDir(d => d === "asc" ? "desc" : "asc");
    else { setSortBy(key); setSortDir("asc"); }
  };

  const reload = async () => {
    try {
      const data = await dbListCashSessions({ limit: 200 });
      setSessions(data);
      setLoadError(null);
    } catch (e) {
      setLoadError(e?.message || String(e));
      setSessions([]);
    }
  };
  useE(() => { reload(); }, []);

  const list   = sessions || [];
  const aberto = list.find(s => s.status === "aberto");

  // Detecta caixa aberto de dia anterior — compara só a data (não hora)
  // pra não disparar alerta minutos depois da meia-noite vagamente; é
  // calendário, não 24h.
  const isStale = aberto && (() => {
    const opened = new Date(aberto.opened_at);
    const today  = new Date();
    return opened.toDateString() !== today.toDateString();
  })();

  const sortKey = {
    session_number: s => Number(s.session_number) || 0,
    opened_at:      s => new Date(s.opened_at || 0).getTime(),
    closed_at:      s => new Date(s.closed_at || 0).getTime(),
    opened_by_name: s => s.opened_by_name || "",
    sales_total:    s => Number(s.sales_total)    || 0,
    counted_amount: s => Number(s.counted_amount) || 0,
    cash_diff:      s => Number(s.cash_diff)      || 0,
    status:         s => s.status || "",
  };
  const cmp = (a, b) => {
    const fn = sortKey[sortBy] || sortKey.opened_at;
    const va = fn(a), vb = fn(b);
    if (typeof va === "string") return va.localeCompare(vb, "pt-BR");
    return va - vb;
  };
  const sorted = [...list].sort((a, b) => sortDir === "asc" ? cmp(a, b) : -cmp(a, b));

  const fmtDate = (s) => {
    if (!s) return "—";
    try { return new Date(s).toLocaleDateString("pt-BR", { day: "2-digit", month: "2-digit", year: "2-digit" }); }
    catch { return String(s); }
  };
  const fmtTime = (s) => {
    if (!s) return "—";
    try { return new Date(s).toLocaleTimeString("pt-BR", { hour: "2-digit", minute: "2-digit" }); }
    catch { return String(s); }
  };

  return (
    <div className="screen">
      <PageHead title="Controle de Caixa" sub={sessions == null ? "Carregando..." : "Aberturas, fechamentos e movimentações"}/>

      {loadError && (
        <div style={{ padding: 16, color: "var(--pink-600)", fontWeight: 600 }}>
          Erro ao carregar: {loadError}
        </div>
      )}

      {/* Card "Nenhum caixa aberto" — só quando não há sessão aberta */}
      {!aberto && sessions != null && (
        <div className="card card-pad" style={{
          marginBottom: 16,
          background: "var(--bg-soft, #FAFAFA)",
          borderColor: "var(--border)",
        }}>
          <div style={{ display: "flex", alignItems: "center", gap: 16, flexWrap: "wrap" }}>
            <div style={{
              width: 56, height: 56, borderRadius: "50%",
              background: "var(--pink-100)", color: "var(--pink-600)",
              display: "inline-flex", alignItems: "center", justifyContent: "center",
              flexShrink: 0,
            }}>
              <Icon name="cash" size={28}/>
            </div>
            <div style={{ flex: 1, minWidth: 220 }}>
              <div style={{ fontWeight: 700, fontSize: 16 }}>Nenhum caixa aberto</div>
              <div style={{ fontSize: 13, color: "var(--text-soft)", marginTop: 4 }}>
                {cashSession === null
                  ? "Você entrou em modo conferência (sem caixa). Abra um caixa pra começar a vender."
                  : "Abra um caixa pra registrar vendas, sangrias e suprimentos do dia."}
              </div>
            </div>
            <button className="btn btn-primary" onClick={() => setOpeningCash(true)}>
              <Icon name="plus" size={14}/> Abrir caixa
            </button>
          </div>
        </div>
      )}

      {/* Alerta caixa esquecido aberto de dia anterior */}
      {isStale && (
        <div className="card card-pad" style={{
          borderColor: "var(--warn-500, #F59E0B)",
          background:  "var(--warn-50, #FFFBEB)",
          marginBottom: 16,
        }}>
          <div style={{ display: "flex", alignItems: "center", gap: 14, flexWrap: "wrap" }}>
            <div style={{
              width: 44, height: 44, borderRadius: "50%",
              background: "var(--warn-500, #F59E0B)", color: "#fff",
              display: "inline-flex", alignItems: "center", justifyContent: "center", flexShrink: 0,
            }}>
              <Icon name="alert" size={22}/>
            </div>
            <div style={{ flex: 1, minWidth: 220 }}>
              <div style={{ fontWeight: 700, fontSize: 15 }}>Caixa de outro dia ainda aberto!</div>
              <div style={{ fontSize: 13, color: "var(--text-soft)", marginTop: 4 }}>
                O caixa <b>#{aberto.session_number}</b> foi aberto em <b>{fmtDate(aberto.opened_at)}</b> às {fmtTime(aberto.opened_at)} por {aberto.opened_by_name} e ainda não foi fechado. Feche-o antes de continuar operando hoje.
              </div>
            </div>
            <button className="btn btn-primary" onClick={() => setCloseFor(aberto)}>
              Fechar este caixa <Icon name="arrowR" size={14}/>
            </button>
          </div>
        </div>
      )}

      {/* Card do caixa em aberto */}
      {aberto && (
        <div className="card card-pad caixa-current">
          <div style={{ display: "flex", alignItems: "center", gap: 12, flexWrap: "wrap" }}>
            <div className="dot dot-ok dot-lg"/>
            <div style={{ flex: 1, minWidth: 220 }}>
              <div style={{ fontSize: 13, color: "var(--text-mute)", textTransform: "uppercase", letterSpacing: ".05em" }}>
                Caixa em aberto
              </div>
              <div style={{ fontSize: 22, fontWeight: 700, marginTop: 4 }}>
                #{aberto.session_number} · {aberto.opened_by_name || "—"} · desde {fmtTime(aberto.opened_at)}
              </div>
            </div>
            <button className="btn btn-secondary"
                    onClick={() => setMovementFor({ sessionId: aberto.id, defaultType: "sangria" })}>
              <Icon name="cash" size={14}/> Sangria
            </button>
            <button className="btn btn-secondary"
                    onClick={() => setMovementFor({ sessionId: aberto.id, defaultType: "suprimento" })}>
              <Icon name="plus" size={14}/> Suprimento
            </button>
            <button className="btn btn-primary" onClick={() => setCloseFor(aberto)}>
              Fechar caixa <Icon name="arrowR" size={14}/>
            </button>
          </div>

          {/* Stats — sem "Esperado em caixa" (removido a pedido).
              Saldo total soma o fluxo da sessão: fundo + vendas − sangrias + suprimentos. */}
          {(() => {
            const opening = Number(aberto.opening_balance) || 0;
            const sales   = Number(aberto.sales_total)     || 0;
            const sang    = Number(aberto.withdrawals)     || 0;
            const supr    = Number(aberto.supplies)        || 0;
            const saldo   = opening + sales - sang + supr;
            return (
              <div className="caixa-stats">
                <div>
                  <div className="caixa-stat-l">Fundo de troco</div>
                  <div className="caixa-stat-v">{BRL(opening)}</div>
                </div>
                <div>
                  <div className="caixa-stat-l">Vendas{Number(aberto.sales_count) > 0 ? ` (${aberto.sales_count})` : ""}</div>
                  <div className="caixa-stat-v" style={{ color: "var(--pink-600)" }}>{BRL(sales)}</div>
                </div>
                <div>
                  <div className="caixa-stat-l">Sangrias</div>
                  <div className="caixa-stat-v">{BRL(sang)}</div>
                </div>
                <div>
                  <div className="caixa-stat-l">Suprimentos</div>
                  <div className="caixa-stat-v">{BRL(supr)}</div>
                </div>
                <div className="caixa-stat-total">
                  <div className="caixa-stat-l">Saldo total</div>
                  <div className="caixa-stat-v">{BRL(saldo)}</div>
                </div>
              </div>
            );
          })()}
        </div>
      )}

      <h3 className="section-h">Histórico de caixas</h3>

      <div className="card card-flush" style={{ overflow: "visible" }}>
       <div className="table-x-scroll">
        <table className="table">
          <thead>
            <tr>
              <SortableTh col="session_number" align="center" width={80}  sortBy={sortBy} sortDir={sortDir} onSort={toggleSort}>Caixa</SortableTh>
              <SortableTh col="opened_at"      align="center" width={120} sortBy={sortBy} sortDir={sortDir} onSort={toggleSort}>Data</SortableTh>
              <SortableTh col="opened_by_name" align="center"             sortBy={sortBy} sortDir={sortDir} onSort={toggleSort}>Operadora</SortableTh>
              <SortableTh col="opened_at"      align="center" width={80}  sortBy={sortBy} sortDir={sortDir} onSort={toggleSort}>Aberto</SortableTh>
              <SortableTh col="closed_at"      align="center" width={80}  sortBy={sortBy} sortDir={sortDir} onSort={toggleSort}>Fechado</SortableTh>
              <SortableTh col="sales_total"    align="center" width={120} sortBy={sortBy} sortDir={sortDir} onSort={toggleSort}>Vendas</SortableTh>
              <SortableTh col="counted_amount" align="center" width={120} sortBy={sortBy} sortDir={sortDir} onSort={toggleSort}>Contado</SortableTh>
              <SortableTh col="cash_diff"      align="center" width={110} sortBy={sortBy} sortDir={sortDir} onSort={toggleSort}>Diferença</SortableTh>
              <SortableTh col="status"         align="center" width={110} sortBy={sortBy} sortDir={sortDir} onSort={toggleSort}>Status</SortableTh>
            </tr>
          </thead>
          <tbody>
            {sessions == null ? (
              <tr><td colSpan="9" style={{ textAlign: "center", padding: 40, color: "var(--text-mute)" }}>Carregando...</td></tr>
            ) : sorted.length === 0 ? (
              <tr><td colSpan="9" style={{ textAlign: "center", padding: 40, color: "var(--text-mute)" }}>Nenhum caixa registrado.</td></tr>
            ) : sorted.map(cx => {
              const diff = Number(cx.cash_diff) || 0;
              const hasCount = cx.counted_amount != null;
              return (
                <tr key={cx.id}
                    onClick={() => setDetailsFor(cx)}
                    style={{ cursor: "pointer" }}
                    title="Ver detalhes do caixa">
                  <td style={{ textAlign: "center" }}><b style={{ fontFamily: "var(--font-mono)" }}>#{cx.session_number}</b></td>
                  <td style={{ textAlign: "center" }}>{fmtDate(cx.opened_at)}</td>
                  <td style={{ textAlign: "center" }}>{cx.opened_by_name || "—"}</td>
                  <td className="tabular" style={{ textAlign: "center" }}>{fmtTime(cx.opened_at)}</td>
                  <td className="tabular" style={{ textAlign: "center" }}>{fmtTime(cx.closed_at)}</td>
                  <td className="tabular" style={{ textAlign: "center", fontWeight: 600 }}>{BRL(Number(cx.sales_total) || 0)}</td>
                  <td className="tabular" style={{ textAlign: "center" }}>
                    {hasCount ? BRL(Number(cx.counted_amount) || 0) : "—"}
                  </td>
                  <td className="tabular" style={{
                    textAlign: "center", fontWeight: 700,
                    color: !hasCount             ? "var(--text-mute)"
                         : Math.abs(diff) < 0.005 ? "var(--ok-600)"
                         : diff > 0               ? "var(--info-500, #2563EB)"
                         :                          "var(--bad-500, #DC2626)",
                  }}>
                    {!hasCount ? "—" : (diff >= 0 ? "+ " : "− ") + BRL(Math.abs(diff))}
                  </td>
                  <td style={{ textAlign: "center" }}>
                    {cx.status === "aberto"
                      ? <span className="badge badge-ok"><span className="dot"/>Aberto</span>
                      : <span className="badge"><span className="dot"/>Fechado</span>}
                  </td>
                </tr>
              );
            })}
          </tbody>
        </table>
       </div>
      </div>

      {movementFor && (
        <CashMovementModal
          sessionId={movementFor.sessionId}
          defaultType={movementFor.defaultType}
          onClose={() => setMovementFor(null)}
          onSaved={async () => { setMovementFor(null); await reload(); }}
        />
      )}
      {closeFor && (
        <CloseCashModal
          session={closeFor}
          onClose={() => setCloseFor(null)}
          onClosed={async () => { setCloseFor(null); await reload(); }}
        />
      )}
      {detailsFor && (
        <CashSessionDetailsModal
          session={detailsFor}
          hasOpenSession={!!aberto}
          onClose={() => setDetailsFor(null)}
          onReopened={async () => { setDetailsFor(null); await reload(); }}
          onCloseSession={(s) => { setDetailsFor(null); setCloseFor(s); }}
        />
      )}
      {openingCash && (
        <OpenCashModal
          user={user}
          onClose={() => setOpeningCash(false)}
          onOpened={async (session) => {
            // Promove de "modo conferência" pra caixa aberto: atualiza o
            // estado global, fecha o modal e recarrega a lista.
            setCashSession(session);
            setOpeningCash(false);
            await reload();
          }}
        />
      )}
    </div>
  );
}

/* ────────────────────────────────────────────────────────────
   OpenCashModal — abre uma nova sessão de caixa informando o
   fundo de troco inicial. Atalho da tela Caixa, espelha o
   passo de abertura que existe no login (pdv-shell.jsx).
   ──────────────────────────────────────────────────────────── */
function OpenCashModal({ user, onClose, onOpened }) {
  const [opening, setOpening] = useS("200,00");
  const [saving,  setSaving]  = useS(false);
  const [error,   setError]   = useS(null);

  const onSubmit = async (e) => {
    e?.preventDefault?.();
    if (!user?.id) { setError("Usuário não identificado."); return; }
    setSaving(true); setError(null);
    try {
      const session = await dbOpenCashSession(user.id, parseBRL(opening));
      onOpened(session);
    } catch (err) {
      setError(err?.message || String(err));
      setSaving(false);
    }
  };

  return (
    <div className="modal-backdrop">
      <div className="modal" style={{ width: 440 }} onClick={e => e.stopPropagation()} role="dialog" aria-modal="true">
        <div className="modal-head">
          <div>
            <h2 style={{ margin: 0, fontSize: 20, fontWeight: 700 }}>Abrir caixa</h2>
            <div style={{ fontSize: 12, color: "var(--text-mute)", marginTop: 4 }}>
              Informe o valor em dinheiro disponível na gaveta (fundo de troco).
            </div>
          </div>
          <button className="btn-icon-mini" onClick={onClose} aria-label="Fechar"><Icon name="close" size={18}/></button>
        </div>

        <div style={{
          padding: 12, marginBottom: 16, borderRadius: 8,
          background: "var(--pink-50, #FFE1EC)", border: "1px solid var(--pink-200)",
          display: "flex", alignItems: "center", gap: 10,
        }}>
          <div style={{
            width: 32, height: 32, borderRadius: "50%",
            background: "var(--pink-500)", color: "white",
            display: "flex", alignItems: "center", justifyContent: "center",
            flexShrink: 0, fontWeight: 700, fontSize: 13,
          }}>
            {(user?.name || "?").trim().slice(0, 2).toUpperCase()}
          </div>
          <div style={{ flex: 1 }}>
            <div style={{ fontSize: 12, color: "var(--text-mute)" }}>Será aberto por</div>
            <div style={{ fontWeight: 700 }}>{user?.name || "—"}</div>
          </div>
        </div>

        <form onSubmit={onSubmit}>
          <div className="field">
            <label className="label">Fundo de troco</label>
            <input className="input input-lg tabular"
                   value={opening}
                   onChange={e => setOpening(maskBRL(e.target.value))}
                   autoFocus
                   onFocus={e => e.target.select()}/>
            <div style={{ fontSize: 11, color: "var(--text-mute)", marginTop: 4 }}>
              Dinheiro físico na gaveta no início do turno. Pode ser R$ 0,00 se não tiver troco.
            </div>
          </div>

          {error && (
            <div style={{
              padding: 10, borderRadius: 8, marginTop: 8,
              background: "var(--pink-50, #FFE1EC)", color: "var(--pink-600)",
              fontSize: 13,
            }}>{error}</div>
          )}

          <div style={{ display: "flex", justifyContent: "flex-end", gap: 8, marginTop: 16 }}>
            <button type="button" className="btn btn-secondary" onClick={onClose} disabled={saving}>Cancelar</button>
            <button type="submit" className="btn btn-primary" disabled={saving}>
              {saving ? "Abrindo..." : "Abrir caixa"}
            </button>
          </div>
        </form>
      </div>
    </div>
  );
}

/* ────────────────────────────────────────────────────────────
   CashSessionDetailsModal — exibe detalhes de uma sessão (data/hora
   abertura, fechamento, totais, movimentações, audit log) e permite
   reabrir se a sessão estiver fechada E não houver outro caixa aberto.
   ──────────────────────────────────────────────────────────── */
function CashSessionDetailsModal({ session, hasOpenSession, onClose, onReopened, onCloseSession }) {
  const { user } = useApp();
  const [movements, setMovements] = useS(null);
  const [audit,     setAudit]     = useS(null);
  const [reason,    setReason]    = useS("");
  const [reopening, setReopening] = useS(false);
  const [error,     setError]     = useS(null);
  const [confirm,   setConfirm]   = useS(false); // exige confirm explícito antes de reabrir

  useE(() => {
    let alive = true;
    (async () => {
      try {
        const [mv, au] = await Promise.all([
          dbListCashMovements({ sessionId: session.id }),
          dbListCashSessionAudit(session.id),
        ]);
        if (!alive) return;
        setMovements(mv); setAudit(au);
      } catch (e) {
        if (!alive) return;
        setError(e?.message || String(e));
        setMovements([]); setAudit([]);
      }
    })();
    return () => { alive = false; };
  }, [session.id]);

  const doReopen = async () => {
    if (reopening) return;
    setReopening(true); setError(null);
    try {
      await dbReopenCashSession({
        id:     session.id,
        userId: user?.id || null,
        reason: reason.trim(),
      });
      await onReopened?.();
    } catch (e) {
      setError(e?.message || "Falha ao reabrir.");
      setReopening(false);
    }
  };

  const fmtDateTime = (s) => {
    if (!s) return "—";
    try { return new Date(s).toLocaleString("pt-BR",
      { day: "2-digit", month: "2-digit", year: "numeric",
        hour: "2-digit", minute: "2-digit" }); }
    catch { return String(s); }
  };

  const opening = Number(session.opening_balance) || 0;
  const sales   = Number(session.sales_total)     || 0;
  const sang    = Number(session.withdrawals)     || 0;
  const supr    = Number(session.supplies)        || 0;
  const counted = Number(session.counted_amount)  || 0;
  const diff    = Number(session.cash_diff)       || 0;
  const expected = opening + sales - sang + supr;
  const hasCount = session.counted_amount != null;
  const isOpen   = session.status === "aberto";
  // Só permite reabrir se: fechado AND não existe outro caixa em aberto
  const canReopen = !isOpen && !hasOpenSession;

  return (
    <div className="modal-backdrop">
      <div className="modal" style={{ width: 680, maxHeight: "90vh", overflowY: "auto" }} onClick={e => e.stopPropagation()}>
        <div className="modal-head">
          <div>
            <h2 style={{ margin: 0, fontSize: 18, fontWeight: 700 }}>
              Caixa #{session.session_number}
            </h2>
            <div style={{ marginTop: 4 }}>
              {isOpen
                ? <span className="badge badge-ok"><span className="dot"/>Aberto</span>
                : <span className="badge"><span className="dot"/>Fechado</span>}
            </div>
          </div>
          <button className="btn-icon-mini" onClick={onClose} disabled={reopening}>
            <Icon name="close" size={16}/>
          </button>
        </div>

        {error && (
          <div style={{ padding: 12, marginBottom: 12, background: "var(--pink-50, #FFE1EC)",
                       color: "var(--pink-600)", fontWeight: 600, borderRadius: "var(--r-sm, 6px)" }}>
            {error}
          </div>
        )}

        {/* Datas/operadores */}
        <div style={{
          display: "grid", gridTemplateColumns: "1fr 1fr", gap: 12,
          padding: "12px 14px", background: "var(--bg-soft, #FAFAFA)",
          borderRadius: "var(--r-md)", marginBottom: 14,
        }}>
          <div>
            <div style={{ fontSize: 11, color: "var(--text-mute)", textTransform: "uppercase", letterSpacing: ".05em" }}>
              Abertura
            </div>
            <div style={{ fontWeight: 600, marginTop: 2 }}>{fmtDateTime(session.opened_at)}</div>
            <div style={{ fontSize: 12, color: "var(--text-soft)" }}>por {session.opened_by_name || "—"}</div>
          </div>
          <div>
            <div style={{ fontSize: 11, color: "var(--text-mute)", textTransform: "uppercase", letterSpacing: ".05em" }}>
              Fechamento
            </div>
            <div style={{ fontWeight: 600, marginTop: 2 }}>{fmtDateTime(session.closed_at)}</div>
            <div style={{ fontSize: 12, color: "var(--text-soft)" }}>
              {session.closed_by_name ? `por ${session.closed_by_name}` : "—"}
            </div>
          </div>
        </div>

        {/* KPIs */}
        <div style={{ display: "grid", gridTemplateColumns: "repeat(3, 1fr)", gap: 10, marginBottom: 14 }}>
          {[
            { l: "Fundo de troco",    v: BRL(opening),   c: undefined },
            { l: `Vendas${Number(session.sales_count) > 0 ? ` (${session.sales_count})` : ""}`,
              v: BRL(sales),     c: "var(--pink-600)" },
            { l: "Esperado",          v: BRL(expected),  c: undefined, bold: true },
            { l: "Sangrias",          v: BRL(sang),      c: undefined },
            { l: "Suprimentos",       v: BRL(supr),      c: undefined },
            { l: "Contado",           v: hasCount ? BRL(counted) : "—",
              c: hasCount ? undefined : "var(--text-mute)" },
          ].map((s, i) => (
            <div key={i} style={{
              padding: "10px 12px", border: "1px solid var(--border)",
              borderRadius: "var(--r-sm, 6px)", background: "white",
            }}>
              <div style={{ fontSize: 11, color: "var(--text-mute)" }}>{s.l}</div>
              <div className="tabular"
                   style={{ fontWeight: s.bold ? 700 : 600, fontSize: 15, marginTop: 2, color: s.c }}>
                {s.v}
              </div>
            </div>
          ))}
        </div>

        {hasCount && (
          <div style={{
            padding: "10px 14px", marginBottom: 14,
            background: Math.abs(diff) < 0.005 ? "var(--ok-50, #ECFDF5)"
                       : diff > 0               ? "var(--info-50, #EFF6FF)"
                       :                          "var(--bad-50, #FEF2F2)",
            color:      Math.abs(diff) < 0.005 ? "var(--ok-600, #059669)"
                       : diff > 0               ? "var(--info-500, #2563EB)"
                       :                          "var(--bad-500, #DC2626)",
            borderRadius: "var(--r-md)", fontWeight: 700,
          }}>
            Diferença: {Math.abs(diff) < 0.005 ? "exato" : (diff >= 0 ? "+ " : "− ") + BRL(Math.abs(diff))}
          </div>
        )}

        {/* Movimentações */}
        <div style={{ marginBottom: 14 }}>
          <h4 style={{ margin: "0 0 8px 0", fontSize: 13, fontWeight: 700, textTransform: "uppercase", color: "var(--text-soft)", letterSpacing: ".04em" }}>
            Movimentações
          </h4>
          {movements == null ? (
            <div style={{ padding: 10, color: "var(--text-mute)", fontSize: 12 }}>Carregando...</div>
          ) : movements.length === 0 ? (
            <div style={{ padding: 10, color: "var(--text-mute)", fontSize: 12 }}>Sem sangrias ou suprimentos.</div>
          ) : (
            <table className="table" style={{ fontSize: 12 }}>
              <thead><tr>
                <th style={{ width: 110 }}>Data/Hora</th>
                <th style={{ width: 100 }}>Tipo</th>
                <th style={{ textAlign: "right", width: 100 }}>Valor</th>
                <th>Motivo</th>
                <th style={{ width: 120 }}>Usuário</th>
              </tr></thead>
              <tbody>
                {movements.map(m => (
                  <tr key={m.id}>
                    <td className="tabular">{fmtDateTime(m.created_at)}</td>
                    <td style={{ textTransform: "capitalize" }}>{m.type}</td>
                    <td className="tabular" style={{ textAlign: "right",
                                                     color: m.type === "sangria" ? "var(--pink-600)" : "var(--ok-600)" }}>
                      {m.type === "sangria" ? "− " : "+ "}{BRL(Number(m.amount))}
                    </td>
                    <td style={{ color: "var(--text-soft)" }}>{m.reason || "—"}</td>
                    <td style={{ color: "var(--text-soft)" }}>{m.user_name || "—"}</td>
                  </tr>
                ))}
              </tbody>
            </table>
          )}
        </div>

        {/* Audit log */}
        {audit && audit.length > 0 && (
          <div style={{ marginBottom: 14 }}>
            <h4 style={{ margin: "0 0 8px 0", fontSize: 13, fontWeight: 700, textTransform: "uppercase", color: "var(--text-soft)", letterSpacing: ".04em" }}>
              Histórico de reaberturas
            </h4>
            <div style={{ border: "1px solid var(--border)", borderRadius: "var(--r-sm, 6px)" }}>
              {audit.map(a => (
                <div key={a.id} style={{ padding: "8px 12px", borderBottom: "1px solid var(--border)", fontSize: 12 }}>
                  <div style={{ display: "flex", justifyContent: "space-between", gap: 8 }}>
                    <span style={{ fontWeight: 600, textTransform: "capitalize" }}>{a.action}</span>
                    <span className="tabular" style={{ color: "var(--text-mute)" }}>{fmtDateTime(a.created_at)}</span>
                  </div>
                  <div style={{ color: "var(--text-soft)", marginTop: 2 }}>
                    por {a.actor_name || "—"}
                    {a.justification && <> · <i>"{a.justification}"</i></>}
                  </div>
                </div>
              ))}
            </div>
          </div>
        )}

        {/* Ações */}
        <div style={{ display: "flex", gap: 8, justifyContent: "flex-end", flexWrap: "wrap" }}>
          <button className="btn btn-secondary" onClick={onClose} disabled={reopening}>
            Fechar
          </button>

          {isOpen && (
            <button className="btn btn-primary" onClick={() => onCloseSession(session)}>
              Fechar caixa <Icon name="arrowR" size={14}/>
            </button>
          )}

          {!isOpen && !confirm && (
            <button className="btn btn-primary"
                    onClick={() => setConfirm(true)}
                    disabled={!canReopen}
                    title={!canReopen ? "Há um caixa em aberto — feche-o primeiro" : ""}
                    style={!canReopen ? { opacity: 0.5, cursor: "not-allowed" } : undefined}>
              <Icon name="plus" size={14}/> Reabrir caixa
            </button>
          )}
        </div>

        {/* Bloco de confirmação inline (aparece após clicar em Reabrir) */}
        {!isOpen && confirm && (
          <div style={{
            marginTop: 16, padding: 14, background: "var(--warn-50, #FFFBEB)",
            border: "1px solid var(--warn-500, #F59E0B)", borderRadius: "var(--r-md)",
          }}>
            <div style={{ fontWeight: 700, marginBottom: 8 }}>
              Confirmar reabertura do caixa #{session.session_number}?
            </div>
            <div style={{ fontSize: 12, color: "var(--text-soft)", marginBottom: 10 }}>
              Os campos de fechamento (data, contado, diferença) serão apagados desta sessão.
              O snapshot anterior fica no histórico de reaberturas. Movimentações e vendas são
              preservadas.
            </div>
            <div className="field" style={{ marginBottom: 10 }}>
              <label className="label" htmlFor="reopen-reason">Motivo (opcional)</label>
              <input id="reopen-reason" name="reopen-reason"
                     className="input"
                     value={reason}
                     onChange={e => setReason(e.target.value)}
                     placeholder="Ex.: lançamento de venda esquecida"
                     disabled={reopening}/>
            </div>
            <div style={{ display: "flex", gap: 8, justifyContent: "flex-end" }}>
              <button className="btn btn-secondary" onClick={() => setConfirm(false)} disabled={reopening}>
                Cancelar
              </button>
              <button className="btn btn-primary" onClick={doReopen} disabled={reopening}>
                {reopening ? "Reabrindo..." : "Confirmar reabertura"}
              </button>
            </div>
          </div>
        )}
      </div>
    </div>
  );
}

/* ────────────────────────────────────────────────────────────
   CashMovementModal — registra sangria ou suprimento.
   Sangria: dinheiro saindo do caixa (recolhimento ao banco).
   Suprimento: dinheiro entrando (fundo extra).
   ──────────────────────────────────────────────────────────── */
function CashMovementModal({ sessionId, defaultType, onClose, onSaved }) {
  const { user } = useApp();
  const [type, setType]     = useS(defaultType || "sangria");
  const [amount, setAmount] = useS("0,00");
  const [reason, setReason] = useS("");
  const [saving, setSaving] = useS(false);
  const [error,  setError]  = useS(null);

  const submit = async () => {
    if (saving) return;
    const amt = parseBRL(amount);
    if (amt <= 0) { setError("Valor deve ser maior que zero."); return; }
    setSaving(true); setError(null);
    try {
      await dbAddCashMovement({
        sessionId, type, amount: amt,
        reason: reason.trim(),
        userId: user?.id || null,
      });
      await onSaved?.();
    } catch (e) {
      setError(e?.message || "Falha ao registrar.");
      setSaving(false);
    }
  };

  return (
    <div className="modal-backdrop">
      <div className="modal" style={{ width: 460 }} onClick={e => e.stopPropagation()}>
        <div className="modal-head">
          <h2 style={{ margin: 0, fontSize: 18, fontWeight: 700 }}>
            {type === "sangria" ? "Registrar sangria" : "Registrar suprimento"}
          </h2>
          <button className="btn-icon-mini" onClick={onClose} disabled={saving}><Icon name="close" size={16}/></button>
        </div>

        <div className="field">
          <label className="label">Tipo</label>
          <div className="seg-toggle" style={{ alignSelf: "flex-start" }}>
            <button type="button" className={type === "sangria"    ? "active" : ""} onClick={() => setType("sangria")}    disabled={saving}>Sangria (saída)</button>
            <button type="button" className={type === "suprimento" ? "active" : ""} onClick={() => setType("suprimento")} disabled={saving}>Suprimento (entrada)</button>
          </div>
          <div style={{ fontSize: 11, color: "var(--text-mute)", marginTop: 4 }}>
            {type === "sangria"
              ? "Dinheiro saindo do caixa (recolhimento ao banco, troco devolvido, etc.)."
              : "Dinheiro entrando no caixa (reforço de troco, devolução, etc.)."}
          </div>
        </div>

        <div className="field">
          <label className="label">Valor</label>
          <div className="input-currency-wrap">
            <span className="input-currency-prefix">R$</span>
            <input className="input input-currency tabular" autoFocus value={amount} inputMode="numeric"
                   onChange={e => setAmount(maskBRL(e.target.value))} disabled={saving}/>
          </div>
        </div>

        <div className="field">
          <label className="label">
            Motivo <span style={{ color: "var(--text-mute)", fontWeight: 400 }}>· opcional</span>
          </label>
          <input className="input" value={reason} onChange={e => setReason(e.target.value)}
                 placeholder={type === "sangria" ? "Ex.: Recolhimento bancário" : "Ex.: Reforço de troco"}
                 disabled={saving}/>
        </div>

        {error && (
          <div style={{ fontSize: 13, color: "var(--pink-600)", fontWeight: 600, textAlign: "center", marginTop: 8 }}>
            {error}
          </div>
        )}

        <div style={{ display: "flex", gap: 10, marginTop: 20, justifyContent: "flex-end" }}>
          <button className="btn btn-secondary" onClick={onClose} disabled={saving}>Cancelar</button>
          <button className="btn btn-primary" onClick={submit} disabled={saving || parseBRL(amount) <= 0}>
            <Icon name="check" size={14}/> {saving ? "Registrando..." : "Registrar"}
          </button>
        </div>
      </div>
    </div>
  );
}

/* ────────────────────────────────────────────────────────────
   CloseCashModal — confirma o fechamento de caixa.
   Mostra resumo (vendas, sangrias, suprimentos, movimentações),
   campo pra valor contado, calcula diferença e fecha via RPC.
   ──────────────────────────────────────────────────────────── */
function CloseCashModal({ session, onClose, onClosed }) {
  const { user } = useApp();
  const [movements, setMovements] = useS(null);
  const [sales,     setSales]     = useS(null);
  // Saldo esperado = fundo + TODAS as vendas - sangrias + suprimentos
  // (mesma fórmula do card de "Saldo total" na tela de Caixa, pra
  //  fechamento e card baterem). Pix/cartão também entra aqui — não
  //  só dinheiro — porque o card mostra o saldo total da sessão.
  const [counted, setCounted]     = useS(maskBRL(String(Math.round(((Number(session.opening_balance) || 0)
                                                                    + (Number(session.sales_total)  || 0)
                                                                    - (Number(session.withdrawals) || 0)
                                                                    + (Number(session.supplies)    || 0)) * 100))));
  const [notes,   setNotes]       = useS("");
  const [saving,  setSaving]      = useS(false);
  const [error,   setError]       = useS(null);

  useE(() => {
    let alive = true;
    (async () => {
      try {
        const [m, s] = await Promise.all([
          dbListCashMovements({ sessionId: session.id }),
          dbListSessionSales({  sessionId: session.id }),
        ]);
        if (!alive) return;
        setMovements(m);
        setSales(s);
      } catch (e) {
        if (alive) setError(e?.message || String(e));
      }
    })();
    return () => { alive = false; };
  }, [session.id]);

  const opening    = Number(session.opening_balance) || 0;
  const salesTotal = Number(session.sales_total)     || 0;
  const cashSales  = Number(session.cash_sales)      || 0;
  const sangrias   = Number(session.withdrawals)     || 0;
  const supr       = Number(session.supplies)        || 0;
  const expected   = opening + salesTotal - sangrias + supr;
  const countedNum = parseBRL(counted);
  const diff       = countedNum - expected;

  const fmtDateTime = (s) => {
    if (!s) return "—";
    try { return new Date(s).toLocaleString("pt-BR", { day: "2-digit", month: "2-digit", year: "2-digit", hour: "2-digit", minute: "2-digit" }); }
    catch { return String(s); }
  };

  const submit = async () => {
    if (saving) return;
    setSaving(true); setError(null);
    try {
      await dbCloseCashSession({
        id:            session.id,
        countedAmount: countedNum,
        userId:        user?.id || null,
        notes:         notes.trim(),
      });
      await onClosed?.();
    } catch (e) {
      setError(e?.message || "Falha ao fechar o caixa.");
      setSaving(false);
    }
  };

  const MOVEMENT_LABELS = {
    sangria: "Sangria", suprimento: "Suprimento", ajuste: "Ajuste", troco_inicial: "Troco inicial",
  };

  return (
    <div className="modal-backdrop">
      <div className="modal modal-form modal-split" onClick={e => e.stopPropagation()}>
        <div className="modal-head-pinned">
          <div>
            <h2 style={{ margin: 0, fontSize: 22, fontWeight: 700 }}>Fechar caixa #{session.session_number}</h2>
            <div style={{ fontSize: 13, color: "var(--text-mute)", marginTop: 4 }}>
              Aberto em {fmtDateTime(session.opened_at)} por {session.opened_by_name || "—"}
            </div>
          </div>
          <button className="btn-icon-mini" onClick={onClose} disabled={saving}><Icon name="close" size={18}/></button>
        </div>

        <div className="modal-body">
          {/* Resumo da sessão */}
          <h3 style={{ margin: "0 0 10px", fontSize: 14, fontWeight: 700, color: "var(--text-soft)", textTransform: "uppercase", letterSpacing: ".05em" }}>
            Resumo da sessão
          </h3>
          <div style={{
            display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(160px, 1fr))",
            gap: 10, marginBottom: 16,
          }}>
            <SummaryStat label="Fundo de troco" value={BRL(opening)}/>
            <SummaryStat label={`Vendas (${session.sales_count || 0})`} value={BRL(Number(session.sales_total) || 0)} accent/>
            <SummaryStat label="Vendas em dinheiro" value={BRL(cashSales)}/>
            <SummaryStat label="Sangrias" value={BRL(sangrias)} negative/>
            <SummaryStat label="Suprimentos" value={BRL(supr)}/>
          </div>

          {/* Movimentações da sessão — sangria/suprimento + vendas, em
              ordem cronológica decrescente. Vendas canceladas ficam riscadas
              e contam como "0,00" pra refletir o estorno. */}
          {(() => {
            const events = [];
            (movements || []).forEach(m => {
              events.push({
                kind: "movement",
                key:  "m_" + m.id,
                at:   m.created_at,
                type: m.type,
                amount: Number(m.amount) || 0,
                reason: m.reason || "",
              });
            });
            (sales || []).forEach(s => {
              events.push({
                kind: "sale",
                key:  "s_" + s.id,
                at:   s.sold_at,
                sale_number: s.sale_number,
                total: Number(s.total) || 0,
                cancelled: s.status === "cancelada",
                payment_methods: s.payment_methods || [],
                customer_name:   s.customer_name || "",
              });
            });
            events.sort((a, b) => new Date(b.at) - new Date(a.at));
            const loading = movements == null || sales == null;

            return (
              <>
                <h3 style={{ margin: "0 0 10px", fontSize: 14, fontWeight: 700, color: "var(--text-soft)", textTransform: "uppercase", letterSpacing: ".05em" }}>
                  Movimentações ({events.length})
                </h3>
                <div style={{ border: "1px solid var(--border, #F5C6DA)", borderRadius: 10, marginBottom: 16, maxHeight: 240, overflowY: "auto" }}>
                  <table className="table" style={{ margin: 0 }}>
                    <thead>
                      <tr>
                        <th style={{ width: 80 }}>Quando</th>
                        <th style={{ width: 120 }}>Tipo</th>
                        <th style={{ textAlign: "right", width: 110 }}>Valor</th>
                        <th>Detalhes</th>
                      </tr>
                    </thead>
                    <tbody>
                      {loading ? (
                        <tr><td colSpan="4" style={{ textAlign: "center", padding: 16, color: "var(--text-mute)" }}>Carregando...</td></tr>
                      ) : events.length === 0 ? (
                        <tr><td colSpan="4" style={{ textAlign: "center", padding: 16, color: "var(--text-mute)" }}>Nenhuma movimentação.</td></tr>
                      ) : events.map(ev => {
                        if (ev.kind === "movement") {
                          const sign = ev.type === "sangria" ? -1 : 1;
                          return (
                            <tr key={ev.key}>
                              <td className="tabular" style={{ fontSize: 12, color: "var(--text-soft)" }}>
                                {fmtDateTime(ev.at).split(" ")[1] || "—"}
                              </td>
                              <td>
                                <span className={"badge " + (ev.type === "sangria" ? "badge-warn" : "badge-ok")}>
                                  {MOVEMENT_LABELS[ev.type] || ev.type}
                                </span>
                              </td>
                              <td className="tabular" style={{
                                textAlign: "right", fontWeight: 700,
                                color: sign < 0 ? "var(--bad-500, #DC2626)" : "var(--ok-600, #047857)",
                              }}>
                                {sign < 0 ? "− " : "+ "}{BRL(ev.amount)}
                              </td>
                              <td style={{ fontSize: 13, color: "var(--text-soft)" }}>{ev.reason || "—"}</td>
                            </tr>
                          );
                        }
                        // venda
                        const cancelled = ev.cancelled;
                        return (
                          <tr key={ev.key} style={cancelled ? { opacity: 0.6, textDecoration: "line-through" } : undefined}>
                            <td className="tabular" style={{ fontSize: 12, color: "var(--text-soft)" }}>
                              {fmtDateTime(ev.at).split(" ")[1] || "—"}
                            </td>
                            <td>
                              <span className="badge" style={{
                                background: "var(--pink-50, #FFE1EC)",
                                color: "var(--pink-700, #C9186F)",
                              }}>
                                Venda #{ev.sale_number}
                              </span>
                            </td>
                            <td className="tabular" style={{
                              textAlign: "right", fontWeight: 700,
                              color: cancelled ? "var(--text-mute)" : "var(--pink-700, #C9186F)",
                            }}>
                              + {BRL(ev.total)}
                            </td>
                            <td style={{ fontSize: 13, color: "var(--text-soft)" }}>
                              {(ev.payment_methods || []).map(m => PAYMENT_LABELS[m] || m).join(" + ") || "—"}
                              {ev.customer_name && (
                                <span style={{ color: "var(--text-mute)" }}> · {ev.customer_name}</span>
                              )}
                              {cancelled && (
                                <span style={{ color: "var(--bad-500, #DC2626)", marginLeft: 6, fontWeight: 600 }}>
                                  (cancelada)
                                </span>
                              )}
                            </td>
                          </tr>
                        );
                      })}
                    </tbody>
                  </table>
                </div>
              </>
            );
          })()}

          {/* Conferência: valor contado + diferença */}
          <div className="form-grid">
            <div className="field">
              <label className="label">Valor contado em caixa</label>
              <div className="input-currency-wrap">
                <span className="input-currency-prefix">R$</span>
                <input className="input input-currency tabular" value={counted} inputMode="numeric"
                       onChange={e => setCounted(maskBRL(e.target.value))} disabled={saving}/>
              </div>
            </div>
            <div className="field">
              <label className="label">Diferença</label>
              <input className="input tabular" readOnly
                     value={(diff >= 0 ? "+ " : "− ") + BRL(Math.abs(diff))}
                     style={{
                       background: "var(--surface-2, #FFF6FA)",
                       fontWeight: 700,
                       color: Math.abs(diff) < 0.005 ? "var(--ok-600, #047857)"
                            : diff > 0               ? "var(--info-500, #2563EB)"
                            :                          "var(--bad-500, #DC2626)",
                     }}/>
            </div>
            <div className="field" style={{ gridColumn: "1 / -1" }}>
              <label className="label">
                Observações <span style={{ color: "var(--text-mute)", fontWeight: 400 }}>· opcional</span>
              </label>
              <textarea className="input" rows={2} value={notes}
                        onChange={e => setNotes(e.target.value)} disabled={saving}
                        placeholder="Ex.: cliente pagou venda do dia anterior, ajustei o troco..."
                        style={{ resize: "vertical", minHeight: 60 }}/>
            </div>
          </div>

          {error && (
            <div style={{ fontSize: 13, color: "var(--pink-600)", fontWeight: 600, marginTop: 12, textAlign: "center" }}>
              {error}
            </div>
          )}
        </div>

        <div className="modal-foot-pinned" style={{ justifyContent: "space-between" }}>
          <button className="btn btn-secondary" onClick={onClose} disabled={saving}>Cancelar</button>
          <button className="btn btn-primary" onClick={submit} disabled={saving}>
            <Icon name="check" size={14}/> {saving ? "Fechando..." : "Confirmar fechamento"}
          </button>
        </div>
      </div>
    </div>
  );
}

const SummaryStat = ({ label, value, accent, negative }) => (
  <div style={{
    background: accent ? "var(--pink-50, #FFE1EC)" : "var(--surface-2, #FFF6FA)",
    border: "1px solid " + (accent ? "var(--pink-200, #F5C6DA)" : "var(--border, #F5C6DA)"),
    borderRadius: 10, padding: "10px 12px",
  }}>
    <div style={{ fontSize: 11, color: "var(--text-mute)", textTransform: "uppercase", letterSpacing: ".05em" }}>{label}</div>
    <div className="tabular" style={{
      fontSize: 18, fontWeight: 700, marginTop: 2,
      color: accent ? "var(--pink-700, #C9186F)" : negative ? "var(--bad-500, #DC2626)" : "var(--text)",
    }}>{value}</div>
  </div>
);

/* ════════════════════════════════════════════════════════════
   RELATÓRIOS
   ════════════════════════════════════════════════════════════ */
/* ─── Helpers de período pros relatórios ─────────────────────────
   Converte presets ("hoje", "7d", "30d", "mes_atual", "mes_passado",
   "tudo") em { from, to } no formato ISO YYYY-MM-DD. */
const REPORT_PRESETS = [
  { id: "hoje",        label: "Hoje"        },
  { id: "7d",          label: "Últimos 7 dias"  },
  { id: "15d",         label: "Últimos 15 dias" },
  { id: "30d",         label: "Últimos 30 dias" },
  { id: "mes_atual",   label: "Mês atual"   },
  { id: "mes_passado", label: "Mês passado" },
  { id: "tudo",        label: "Todo período" },
  { id: "custom",      label: "Personalizado" },
];

const toISO = (d) => {
  const y = d.getFullYear();
  const m = String(d.getMonth() + 1).padStart(2, "0");
  const dd = String(d.getDate()).padStart(2, "0");
  return `${y}-${m}-${dd}`;
};

const presetToRange = (preset) => {
  const today = new Date();
  const todayISO = toISO(today);
  switch (preset) {
    case "hoje":   return { from: todayISO, to: todayISO };
    case "7d":     {
      const d = new Date(today); d.setDate(d.getDate() - 6);
      return { from: toISO(d), to: todayISO };
    }
    case "15d":    {
      const d = new Date(today); d.setDate(d.getDate() - 14);
      return { from: toISO(d), to: todayISO };
    }
    case "30d":    {
      const d = new Date(today); d.setDate(d.getDate() - 29);
      return { from: toISO(d), to: todayISO };
    }
    case "mes_atual": {
      const f = new Date(today.getFullYear(), today.getMonth(), 1);
      return { from: toISO(f), to: todayISO };
    }
    case "mes_passado": {
      const f = new Date(today.getFullYear(), today.getMonth() - 1, 1);
      const t = new Date(today.getFullYear(), today.getMonth(), 0);
      return { from: toISO(f), to: toISO(t) };
    }
    case "tudo":   return { from: "2000-01-01", to: todayISO };
    default:       return { from: todayISO, to: todayISO };
  }
};

const fmtBR = (iso) => {
  if (!iso) return "—";
  const [y, m, d] = iso.split("-");
  return `${d}/${m}/${y}`;
};

const DOW_LABELS = ["Dom", "Seg", "Ter", "Qua", "Qui", "Sex", "Sáb"];

/* Toolbar com presets de período + datas custom. Centraliza o controle
   do recorte temporal pra todas as abas do relatório. */
const PeriodToolbar = ({ preset, setPreset, from, to, setFrom, setTo }) => (
  <div style={{ display: "flex", gap: 8, alignItems: "center", flexWrap: "wrap" }}>
    <select className="input" value={preset}
            onChange={e => setPreset(e.target.value)}
            style={{ width: 180 }}>
      {REPORT_PRESETS.map(p => (
        <option key={p.id} value={p.id}>{p.label}</option>
      ))}
    </select>
    {preset === "custom" && (
      <>
        <input type="date" className="input tabular"
               value={from} onChange={e => setFrom(e.target.value)}
               style={{ width: 160 }}/>
        <span style={{ color: "var(--text-mute)" }}>→</span>
        <input type="date" className="input tabular"
               value={to} onChange={e => setTo(e.target.value)}
               style={{ width: 160 }}/>
      </>
    )}
    {preset !== "custom" && (
      <span style={{ fontSize: 12, color: "var(--text-mute)", marginLeft: 4 }}>
        {fmtBR(from)} → {fmtBR(to)}
      </span>
    )}
  </div>
);

/* Exporta um array de objetos para XLSX usando SheetJS (carregado via CDN
   no Emporium PDV.html). Cada linha é um objeto; keys viram colunas. */
const exportToXLSX = (rows, sheetName, fileName) => {
  if (!window.XLSX) {
    alert("A biblioteca XLSX não carregou. Verifique a conexão.");
    return;
  }
  if (!rows || rows.length === 0) {
    alert("Sem linhas para exportar.");
    return;
  }
  const ws = window.XLSX.utils.json_to_sheet(rows);
  const wb = window.XLSX.utils.book_new();
  window.XLSX.utils.book_append_sheet(wb, ws, sheetName);
  window.XLSX.writeFile(wb, fileName);
};

function Relatorios() {
  const [tab,    setTab]    = useS("vendas");
  // Mesmo PeriodSelector usado no Dashboard (botões Hoje/7d/30d/90d/Personalizado).
  // O estado fica num único objeto `range` pra simplificar a passagem.
  const initial = presetToRange("30d");
  const [range, setRange] = useS({ mode: "30d", from: initial.from, to: initial.to });
  const { from, to } = range;

  return (
    <div className="screen">
      <PageHead title="Relatórios" sub="Análises do negócio — vendas, lucro, fornecedores, estoque e horários"
        actions={<PeriodSelector value={range} onChange={setRange}/>}/>

      <div className="report-tabs">
        {[
          { id: "vendas",       label: "Vendas & Faturamento" },
          { id: "produtos",     label: "Produtos mais vendidos" },
          { id: "pagamentos",   label: "Pagamentos" },
          { id: "fornecedores", label: "Fornecedores · comissão" },
          { id: "cursos",       label: "Cursos" },
          { id: "estoque",      label: "Estoque" },
          { id: "horarios",     label: "Horário de pico" },
          { id: "lucro",        label: "Lucro / Margem" },
        ].map(t => (
          <button key={t.id} className={"report-tab " + (tab === t.id ? "active" : "")} onClick={() => setTab(t.id)}>{t.label}</button>
        ))}
      </div>

      {tab === "vendas"       && <ReportVendas       from={from} to={to}/>}
      {tab === "produtos"     && <ReportProdutos     from={from} to={to}/>}
      {tab === "pagamentos"   && <ReportPagamentos   from={from} to={to}/>}
      {tab === "fornecedores" && <ReportFornecedores from={from} to={to}/>}
      {tab === "cursos"       && <ReportCursos       from={from} to={to}/>}
      {tab === "estoque"      && <ReportEstoque      from={from} to={to}/>}
      {tab === "horarios"     && <ReportHorarios     from={from} to={to}/>}
      {tab === "lucro"        && <ReportLucro        from={from} to={to}/>}
    </div>
  );
}

/* ─── Aba: Vendas & Faturamento ─────────────────────────────────── */
function ReportVendas({ from, to }) {
  const [data,    setData]    = useS(null);
  const [revenue, setRevenue] = useS([]);
  const [loading, setLoading] = useS(true);
  const [err,     setErr]     = useS(null);

  // Mostra exatamente o intervalo selecionado — quando é "Hoje" (from===to),
  // o gráfico tem uma única barra (a do dia atual), centralizada via CSS.
  const isSingleDay = from === to;

  useE(() => {
    let alive = true;
    (async () => {
      setLoading(true); setErr(null);
      try {
        const [sum, rev] = await Promise.all([
          dbReportSummary(from, to),
          dbReportRevenueByDay(from, to),
        ]);
        if (!alive) return;
        setData(sum); setRevenue(rev);
      } catch (e) {
        if (!alive) return;
        setErr(e?.message || String(e));
      } finally {
        if (alive) setLoading(false);
      }
    })();
    return () => { alive = false; };
  }, [from, to]);

  if (loading) return <div style={{ padding: 40, textAlign: "center", color: "var(--text-mute)" }}>Carregando...</div>;
  if (err) return <div style={{ padding: 20, color: "var(--pink-600)" }}>{err}</div>;
  if (!data) return null;

  return (
    <>
      <div className="stats-grid">
        <Stat label="Faturamento"      value={BRL(Number(data.total_revenue) || 0)} accent/>
        <Stat label="Tickets"          value={data.total_tickets}
              hint={`ticket médio ${BRL(Number(data.avg_ticket) || 0)}`}/>
        <Stat label="Itens vendidos"   value={Number(data.total_items || 0).toLocaleString("pt-BR")}/>
        <Stat label="Dia mais forte"
              value={data.best_day_date ? fmtBR(data.best_day_date) : "—"}
              hint={data.best_day_total ? BRL(Number(data.best_day_total)) : "—"}/>
      </div>
      <div className="card card-pad">
        <h3 className="card-title">
          Faturamento diário
          {isSingleDay && (
            <span style={{ fontSize: 12, color: "var(--text-mute)", fontWeight: 400, marginLeft: 8 }}>
              · {fmtBR(to)}
            </span>
          )}
        </h3>
        <RevenueBarChart data={revenue} highlightDate={isSingleDay ? to : null} singleDay={isSingleDay}/>
      </div>
    </>
  );
}

function RevenueBarChart({ data, highlightDate, singleDay }) {
  const [hover, setHover] = useS(null); // índice da barra com hover
  if (!data || data.length === 0) {
    return <div style={{ padding: 40, textAlign: "center", color: "var(--text-mute)" }}>Sem vendas no período.</div>;
  }
  const max = Math.max(...data.map(d => Number(d.total) || 0), 1);
  const hovered = hover !== null ? data[hover] : null;
  const hv  = hovered ? (Number(hovered.total)   || 0) : 0;
  const ht  = hovered ? (Number(hovered.tickets) || 0) : 0;
  const havg = ht > 0 ? hv / ht : 0;

  return (
    <div style={{ position: "relative", marginTop: 12 }}>
      {/* Wrapper com scroll horizontal — quando o período tem muitos dias
          (30 dias, custom amplo), as barras manteriam `min-width` e o
          eixo ficaria comprimido em telas estreitas. Aqui o usuário pode
          arrastar lateralmente pra ver o gráfico inteiro. */}
      <div className="bar-chart-scroll">
      <div className="bar-chart"
           style={singleDay ? { justifyContent: "center", gap: 0 } : undefined}>
        {data.map((d, i) => {
          const v = Number(d.total) || 0;
          const [, m, dd] = d.d.split("-");
          const isHover = hover === i;
          const isToday = highlightDate && d.d === highlightDate;
          return (
            <div key={i} className="bar-col"
                 onMouseEnter={() => setHover(i)}
                 onMouseLeave={() => setHover(null)}
                 style={{
                   cursor: "pointer",
                   // Quando é o único dia, a barra ocuparia toda a largura
                   // por causa do `flex: 1`. Limitamos pra ficar centralizada.
                   ...(singleDay ? { flex: "0 0 120px", maxWidth: 120 } : {}),
                 }}>
              <div className="bar-fill"
                   style={{
                     height: (v / max * 100) + "%",
                     background: isHover  ? "var(--pink-700, #BE185D)"
                               : isToday  ? "var(--pink-500, #E91E78)"
                               :            undefined,
                     transition: "background 0.15s, filter 0.15s",
                     filter: isHover ? "brightness(1.1)" : undefined,
                   }}/>
              <div className="bar-label"
                   style={{ fontWeight: isHover || isToday ? 700 : undefined,
                            color: isToday ? "var(--pink-600)" : undefined }}>
                {dd}/{m}
              </div>
            </div>
          );
        })}
      </div>
      </div>

      {/* Tooltip flutuante no topo do card, sempre centralizado horizontalmente.
          Posição fixa (não segue cursor) pra evitar piscar em barras estreitas. */}
      {hovered && (
        <div style={{
          position: "absolute", top: -8, left: "50%",
          transform: "translate(-50%, -100%)",
          pointerEvents: "none", zIndex: 5,
        }}>
          <div style={{
            background: "var(--text, #15141A)", color: "white",
            padding: "10px 14px", borderRadius: 8, fontSize: 12,
            boxShadow: "0 8px 24px rgba(0,0,0,.18)",
            whiteSpace: "nowrap",
          }}>
            <div style={{ fontWeight: 700, marginBottom: 6, fontSize: 13 }}>
              {fmtBR(hovered.d)}
            </div>
            <div style={{ display: "grid", gridTemplateColumns: "auto auto", gap: "2px 12px" }}>
              <span style={{ color: "rgba(255,255,255,.7)" }}>Faturamento</span>
              <span className="tabular" style={{ fontWeight: 700, textAlign: "right" }}>{BRL(hv)}</span>
              <span style={{ color: "rgba(255,255,255,.7)" }}>Vendas</span>
              <span className="tabular" style={{ fontWeight: 700, textAlign: "right" }}>{ht}</span>
              <span style={{ color: "rgba(255,255,255,.7)" }}>Ticket médio</span>
              <span className="tabular" style={{ fontWeight: 700, textAlign: "right" }}>{BRL(havg)}</span>
            </div>
          </div>
        </div>
      )}
    </div>
  );
}

/* ─── Aba: Produtos mais vendidos ──────────────────────────────── */
function ReportProdutos({ from, to }) {
  const [rows,    setRows]    = useS([]);
  const [loading, setLoading] = useS(true);

  useE(() => {
    let alive = true;
    (async () => {
      setLoading(true);
      try {
        const r = await dbReportTopProducts(from, to, 20);
        if (!alive) return;
        setRows(r);
      } finally {
        if (alive) setLoading(false);
      }
    })();
    return () => { alive = false; };
  }, [from, to]);

  if (loading) return <div style={{ padding: 40, textAlign: "center", color: "var(--text-mute)" }}>Carregando...</div>;
  if (rows.length === 0) return <div className="card card-pad" style={{ textAlign: "center", color: "var(--text-mute)" }}>Sem vendas no período.</div>;

  const maxRev = Math.max(...rows.map(r => Number(r.revenue) || 0), 1);

  const exportProdutos = () => {
    exportToXLSX(rows.map(r => ({
      Código:        r.code,
      Produto:       r.name,
      Unidades:      Number(r.units),
      Faturamento:   Number(r.revenue).toFixed(2),
      "Custo total": Number(r.cost_total).toFixed(2),
      Lucro:         Number(r.profit).toFixed(2),
    })), "Top produtos", `top_produtos_${from}_${to}.xlsx`);
  };

  return (
    <div className="card card-pad">
      <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: 12 }}>
        <h3 className="card-title" style={{ margin: 0 }}>Top 20 produtos · {fmtBR(from)} → {fmtBR(to)}</h3>
        <button className="btn btn-secondary" onClick={exportProdutos}>
          <Icon name="download" size={14}/> XLSX
        </button>
      </div>
      <div style={{ marginTop: 8 }}>
        {rows.map((p, i) => {
          const rev = Number(p.revenue) || 0;
          const pct = rev / maxRev * 100;
          return (
            <div key={p.product_id} className="topbar-row">
              <div className="rank-num">{i + 1}</div>
              <div className="dash-code-badge">{p.code}</div>
              <div style={{ flex: 1, minWidth: 0 }}>
                <div style={{ fontWeight: 600, fontSize: 13 }}>{p.name}</div>
                <div className="meter meter-sm" style={{ marginTop: 4 }}>
                  <div style={{ width: pct + "%", background: "var(--pink-500)" }}/>
                </div>
              </div>
              <div style={{ width: 100, textAlign: "right" }}>
                <div style={{ fontWeight: 700 }} className="tabular">{Number(p.units)}</div>
                <div style={{ fontSize: 11, color: "var(--text-mute)" }} className="tabular">{BRL(rev)}</div>
              </div>
            </div>
          );
        })}
      </div>
    </div>
  );
}

/* ─── Aba: Pagamentos ──────────────────────────────────────────── */
function ReportPagamentos({ from, to }) {
  const [rows,    setRows]    = useS([]);
  const [loading, setLoading] = useS(true);

  useE(() => {
    let alive = true;
    (async () => {
      setLoading(true);
      try {
        const r = await dbReportPaymentsByMethod(from, to);
        if (!alive) return;
        setRows(r);
      } finally {
        if (alive) setLoading(false);
      }
    })();
    return () => { alive = false; };
  }, [from, to]);

  if (loading) return <div style={{ padding: 40, textAlign: "center", color: "var(--text-mute)" }}>Carregando...</div>;
  if (rows.length === 0) return <div className="card card-pad" style={{ textAlign: "center", color: "var(--text-mute)" }}>Sem vendas no período.</div>;

  const METHOD_STYLES = {
    pix:      { bg: "#FFE1EC", fg: "#C8125F" },
    credito:  { bg: "#DBEAFE", fg: "#1D4ED8" },
    debito:   { bg: "#DCFCE7", fg: "#15803D" },
    dinheiro: { bg: "#FEF3C7", fg: "#B45309" },
  };
  const METHOD_LABELS = { pix: "Pix", credito: "Crédito", debito: "Débito", dinheiro: "Dinheiro" };

  const grandTotal = rows.reduce((s, r) => s + Number(r.total || 0), 0);
  const leader = rows[0]; // já vem ordenado por total desc

  return (
    <>
      <div className="card card-pad" style={{ marginBottom: 16 }}>
        <h3 className="card-title">Pagamentos · {fmtBR(from)} → {fmtBR(to)}</h3>
        <div style={{
          display: "flex", gap: 32, alignItems: "center",
          flexWrap: "wrap", justifyContent: "center", marginTop: 12,
        }}>
          {/* Donut SVG */}
          <PaymentDonutChart rows={rows} leader={leader} grandTotal={grandTotal}/>

          {/* Legenda */}
          <div style={{ display: "flex", flexDirection: "column", gap: 10, minWidth: 220 }}>
            {rows.map(r => {
              const color = PAYMENT_COLORS[r.method] || "#888";
              return (
                <div key={r.method} style={{ display: "flex", alignItems: "center", gap: 10 }}>
                  <span style={{
                    width: 14, height: 14, borderRadius: 4, background: color,
                    flexShrink: 0, border: "1px solid rgba(0,0,0,.06)",
                  }}/>
                  <div style={{ flex: 1, minWidth: 0 }}>
                    <div style={{ fontSize: 13, fontWeight: 600 }}>
                      {METHOD_LABELS[r.method] || r.method}
                    </div>
                    <div style={{ fontSize: 11, color: "var(--text-mute)" }} className="tabular">
                      {BRL(Number(r.total))} · {r.tickets} ticket{r.tickets !== 1 ? "s" : ""}
                    </div>
                  </div>
                  <div className="tabular" style={{ fontWeight: 700, fontSize: 14 }}>
                    {Number(r.pct).toFixed(1)}%
                  </div>
                </div>
              );
            })}
          </div>
        </div>
      </div>

      <div className="card card-pad">
        <h3 className="card-title">Detalhamento</h3>
        <table className="table" style={{ marginTop: 12 }}>
          <thead><tr>
            <th>Método</th>
            <th style={{textAlign:"right"}}>Tickets</th>
            <th style={{textAlign:"right"}}>Faturamento</th>
            <th style={{textAlign:"right"}}>%</th>
          </tr></thead>
          <tbody>
            {rows.map(r => {
              const st = METHOD_STYLES[r.method] || { bg: "var(--bg-soft)", fg: "var(--text)" };
              return (
                <tr key={r.method}>
                  <td>
                    <span className="badge" style={{ background: st.bg, color: st.fg }}>
                      {METHOD_LABELS[r.method] || r.method}
                    </span>
                  </td>
                  <td className="tabular" style={{ textAlign: "right" }}>{r.tickets}</td>
                  <td className="tabular" style={{ textAlign: "right", fontWeight: 700 }}>{BRL(Number(r.total))}</td>
                  <td className="tabular" style={{ textAlign: "right" }}>{Number(r.pct).toFixed(1)}%</td>
                </tr>
              );
            })}
          </tbody>
        </table>
      </div>
    </>
  );
}

/* Donut SVG para o relatório de pagamentos. Renderiza um arco por método
   usando PAYMENT_COLORS, com um buraco central que mostra o método líder
   e seu percentual. Lida com o caso "100% num só método" (1 ticket) usando
   um circle ao invés de path — senão o SVG arc com ângulo 2π não fecha. */
function PaymentDonutChart({ rows, leader, grandTotal }) {
  const S = 220, C = S / 2, R = 90, INNER = 56;
  let acc = 0;
  const labels = { pix: "Pix", credito: "Crédito", debito: "Débito", dinheiro: "Dinheiro" };
  return (
    <svg viewBox={`0 0 ${S} ${S}`} style={{ width: 220, height: 220, flexShrink: 0 }}>
      {/* Anel de fundo (caso a soma seja zero) */}
      {grandTotal === 0 && (
        <circle cx={C} cy={C} r={R} fill="var(--bg-soft, #EEE)"/>
      )}

      {/* Fatias */}
      {rows.map(r => {
        const pct = Number(r.pct) || 0;
        if (pct <= 0) return null;
        const color = PAYMENT_COLORS[r.method] || "#888";
        // Slice única que cobre 100% → desenha como circle (o arc com ângulo
        // 2π colapsa pra zero porque start = end).
        if (pct >= 99.999) {
          return <circle key={r.method} cx={C} cy={C} r={R} fill={color}/>;
        }
        const start = (acc / 100) * Math.PI * 2;
        acc += pct;
        const end = (acc / 100) * Math.PI * 2;
        const x1 = C + Math.sin(start) * R, y1 = C - Math.cos(start) * R;
        const x2 = C + Math.sin(end)   * R, y2 = C - Math.cos(end)   * R;
        const big = pct > 50 ? 1 : 0;
        return (
          <path key={r.method}
                d={`M${C},${C} L${x1},${y1} A${R},${R} 0 ${big} 1 ${x2},${y2} Z`}
                fill={color} stroke="white" strokeWidth="2">
            <title>{(labels[r.method] || r.method)} · {pct.toFixed(1)}%</title>
          </path>
        );
      })}

      {/* Buraco central + label do líder */}
      <circle cx={C} cy={C} r={INNER} fill="white"/>
      {leader && (
        <>
          <text x={C} y={C - 6} textAnchor="middle"
                fontSize="11" fill="var(--text-mute, #71706B)">
            {labels[leader.method] || leader.method}
          </text>
          <text x={C} y={C + 16} textAnchor="middle"
                fontSize="22" fontWeight="700" fill="var(--text, #15141A)">
            {Number(leader.pct).toFixed(0)}%
          </text>
        </>
      )}
    </svg>
  );
}

/* ─── Aba: Fornecedores · comissão (com export XLSX) ─────────────
   Lista TODAS as linhas de venda com info do fornecedor. Permite drill
   por fornecedor específico e exporta planilha pronta pra repasse de
   comissão (codigo, nome, qty, preço, comissão = unit_cost). */
function ReportFornecedores({ from, to }) {
  const [suppliers,  setSuppliers]  = useS([]);
  const [supplierId, setSupplierId] = useS("");
  const [summary,    setSummary]    = useS([]);
  const [items,      setItems]      = useS([]);
  const [loading,    setLoading]    = useS(true);

  useE(() => {
    let alive = true;
    (async () => {
      try {
        const list = await dbListSuppliers();
        if (!alive) return;
        setSuppliers(list || []);
      } catch {}
    })();
    return () => { alive = false; };
  }, []);

  useE(() => {
    let alive = true;
    (async () => {
      setLoading(true);
      try {
        const [sum, lines] = await Promise.all([
          dbReportSuppliersSummary(from, to),
          dbReportSalesBySupplier(from, to, supplierId || null),
        ]);
        if (!alive) return;
        setSummary(sum); setItems(lines);
      } finally {
        if (alive) setLoading(false);
      }
    })();
    return () => { alive = false; };
  }, [from, to, supplierId]);

  const exportXLSX = () => {
    if (items.length === 0) {
      alert("Nenhuma venda no período pra exportar.");
      return;
    }
    // Só os valores que interessam pro repasse de comissão. Faturamento
    // da loja e lucro próprio ficam de fora.
    const rows = items.map(it => ({
      Data:                    new Date(it.sold_at).toLocaleDateString("pt-BR"),
      Venda:                   `#${it.sale_number}`,
      Código:                  it.product_code,
      Produto:                 it.product_name,
      Fornecedor:              it.supplier_name || "—",
      Quantidade:              Number(it.quantity),
      "Comissão unitária":     BRL(Number(it.unit_cost)),
      "Total comissão":        BRL(Number(it.line_cost)),
    }));
    // Linha de totalização — soma de quantidade + total da comissão em BRL.
    const totalQty       = items.reduce((s, it) => s + Number(it.quantity || 0), 0);
    const totalCommission = items.reduce((s, it) => s + Number(it.line_cost || 0), 0);
    rows.push({
      Data: "", Venda: "", Código: "", Produto: "", Fornecedor: "TOTAL",
      Quantidade:           totalQty,
      "Comissão unitária":  "",
      "Total comissão":     BRL(totalCommission),
    });

    const label = supplierId
      ? (suppliers.find(s => s.id === supplierId)?.name || "fornecedor").toLowerCase().replace(/\s+/g, "_")
      : "todos";
    exportToXLSX(rows, "Comissão por fornecedor",
      `comissao_${label}_${from}_${to}.xlsx`);
  };

  return (
    <>
      <div className="card card-pad" style={{ marginBottom: 16 }}>
        <div style={{ display: "flex", gap: 12, alignItems: "flex-end", flexWrap: "wrap" }}>
          <div className="field" style={{ marginBottom: 0, flex: 1, minWidth: 220 }}>
            <label className="label" htmlFor="rpt-fornec">Fornecedor</label>
            <select id="rpt-fornec" name="rpt-fornec" className="input"
                    value={supplierId} onChange={e => setSupplierId(e.target.value)}
                    style={{ width: "100%" }}>
              <option value="">— Todos —</option>
              {suppliers.map(s => (
                <option key={s.id} value={s.id}>{s.name}</option>
              ))}
            </select>
          </div>
          <button className="btn btn-primary" onClick={exportXLSX} disabled={items.length === 0}
                  style={{ flexShrink: 0 }}>
            <Icon name="download" size={14}/> Exportar XLSX ({items.length} linhas)
          </button>
        </div>
      </div>

      {!supplierId && (
        <div className="card card-flush" style={{ marginBottom: 16 }}>
          <table className="table">
            <thead><tr>
              <th>Fornecedor</th>
              <th style={{textAlign:"right"}}>SKUs</th>
              <th style={{textAlign:"right"}}>Unidades</th>
              <th style={{textAlign:"right"}}>Faturamento</th>
              <th style={{textAlign:"right"}}>Comissão (custo)</th>
              <th style={{textAlign:"right"}}>Lucro loja</th>
            </tr></thead>
            <tbody>
              {loading && <tr><td colSpan={6} style={{ textAlign: "center", padding: 20, color: "var(--text-mute)" }}>Carregando...</td></tr>}
              {!loading && summary.length === 0 && <tr><td colSpan={6} style={{ textAlign: "center", padding: 20, color: "var(--text-mute)" }}>Sem vendas no período.</td></tr>}
              {summary.map(s => (
                <tr key={s.supplier_id || "sem"}>
                  <td><b>{s.supplier_name || "— Sem fornecedor —"}</b></td>
                  <td className="tabular" style={{textAlign:"right"}}>{s.sku_count}</td>
                  <td className="tabular" style={{textAlign:"right"}}>{Number(s.units_sold)}</td>
                  <td className="tabular" style={{textAlign:"right",fontWeight:700,color:"var(--pink-600)"}}>{BRL(Number(s.revenue))}</td>
                  <td className="tabular" style={{textAlign:"right",color:"var(--warn-600)"}}>{BRL(Number(s.cost_total))}</td>
                  <td className="tabular" style={{textAlign:"right",color:"var(--ok-600)"}}>{BRL(Number(s.profit))}</td>
                </tr>
              ))}
            </tbody>
          </table>
        </div>
      )}

      <div className="card card-flush">
        <div style={{ padding: "12px 16px", borderBottom: "1px solid var(--border)", display: "flex", justifyContent: "space-between", alignItems: "center" }}>
          <h3 className="card-title" style={{ margin: 0 }}>
            Itens vendidos {supplierId && suppliers.find(s => s.id === supplierId) ? `· ${suppliers.find(s => s.id === supplierId).name}` : ""}
          </h3>
          <span style={{ fontSize: 12, color: "var(--text-mute)" }}>{items.length} linha{items.length !== 1 ? "s" : ""}</span>
        </div>
        <div style={{ maxHeight: 600, overflowY: "auto" }}>
          <table className="table">
            <thead style={{ position: "sticky", top: 0, background: "white", zIndex: 1 }}>
              <tr>
                <th style={{ width: 90 }}>Data</th>
                <th style={{ width: 70 }}>Venda</th>
                <th style={{ width: 100 }}>Código</th>
                <th>Produto</th>
                <th>Fornecedor</th>
                <th style={{textAlign:"right", width: 60}}>Qtd</th>
                <th style={{textAlign:"right", width: 90}}>Preço</th>
                <th style={{textAlign:"right", width: 90}}>Comissão</th>
                <th style={{textAlign:"right", width: 90}}>Lucro</th>
              </tr>
            </thead>
            <tbody>
              {loading && <tr><td colSpan={9} style={{ textAlign: "center", padding: 20, color: "var(--text-mute)" }}>Carregando...</td></tr>}
              {!loading && items.length === 0 && <tr><td colSpan={9} style={{ textAlign: "center", padding: 20, color: "var(--text-mute)" }}>Sem itens no período.</td></tr>}
              {items.map((it, i) => (
                <tr key={i}>
                  <td className="tabular" style={{ fontSize: 12 }}>{new Date(it.sold_at).toLocaleDateString("pt-BR")}</td>
                  <td className="tabular" style={{ color: "var(--pink-600)", fontWeight: 600 }}>#{it.sale_number}</td>
                  <td className="tabular" style={{ fontFamily: "var(--font-mono)", color: "var(--pink-600)" }}>{it.product_code}</td>
                  <td>{it.product_name}</td>
                  <td style={{ color: "var(--text-soft)" }}>{it.supplier_name || "—"}</td>
                  <td className="tabular" style={{textAlign:"right"}}>{Number(it.quantity)}</td>
                  <td className="tabular" style={{textAlign:"right"}}>{BRL(Number(it.unit_price))}</td>
                  <td className="tabular" style={{textAlign:"right",color:"var(--warn-600)"}}>{BRL(Number(it.line_cost))}</td>
                  <td className="tabular" style={{textAlign:"right",fontWeight:700,color:"var(--ok-600)"}}>{BRL(Number(it.line_profit))}</td>
                </tr>
              ))}
            </tbody>
          </table>
        </div>
      </div>
    </>
  );
}

/* ─── Aba: Cursos (receita + ranking + comissão por professor) ───── */
function ReportCursos({ from, to }) {
  const [sum,         setSum]         = useS(null);
  const [topRevenue,  setTopRevenue]  = useS([]);
  const [topStudents, setTopStudents] = useS([]);
  const [commissions, setCommissions] = useS([]);
  const [loading,     setLoading]     = useS(true);
  const [err,         setErr]         = useS(null);

  useE(() => {
    let alive = true;
    (async () => {
      setLoading(true); setErr(null);
      try {
        const [s, tr, ts, comm] = await Promise.all([
          dbReportCoursesSummary(from, to),
          dbReportCoursesTop(from, to, "revenue"),
          dbReportCoursesTop(from, to, "students"),
          dbReportTeacherCommissions(from, to),
        ]);
        if (!alive) return;
        setSum(s); setTopRevenue(tr || []); setTopStudents(ts || []); setCommissions(comm || []);
      } catch (e) {
        if (!alive) return;
        setErr(e?.message || String(e));
      } finally {
        if (alive) setLoading(false);
      }
    })();
    return () => { alive = false; };
  }, [from, to]);

  // Agrupa comissões por professor pra renderizar com subtotais.
  const byTeacher = useM(() => {
    const groups = new Map();
    (commissions || []).forEach(r => {
      const key = r.teacher_id || "_none";
      if (!groups.has(key)) {
        groups.set(key, {
          teacher_id:   r.teacher_id,
          teacher_name: r.teacher_name || "Sem professor",
          rows:         [],
          subtotal:     0,
        });
      }
      const g = groups.get(key);
      g.rows.push(r);
      g.subtotal += Number(r.commission) || 0;
    });
    return Array.from(groups.values()).sort((a, b) => b.subtotal - a.subtotal);
  }, [commissions]);

  const totalGeneral = (commissions || []).reduce((s, r) => s + (Number(r.commission) || 0), 0);

  const onExport = () => {
    const rows = (commissions || []).map(r => ({
      Professor:        r.teacher_name || "—",
      Curso:            r.course_name,
      "Modo comissão":  r.commission_mode === "pct" ? "%" : "R$ fixo",
      "Valor comissão": Number(r.commission_value) || 0,
      "Alunos pagantes": Number(r.paying_students) || 0,
      "Pagamentos":     Number(r.payments) || 0,
      "Receita (R$)":   Number(r.revenue) || 0,
      "Comissão (R$)":  Number(r.commission) || 0,
    }));
    exportToXLSX(rows, "Comissoes", `comissoes_${from}_${to}.xlsx`);
  };

  if (loading) return <div style={{ padding: 40, textAlign: "center", color: "var(--text-mute)" }}>Carregando...</div>;
  if (err) return <div style={{ padding: 20, color: "var(--pink-600)" }}>{err}</div>;

  const maxRev = Math.max(1, ...(topRevenue || []).map(r => Number(r.revenue) || 0));
  const maxStu = Math.max(1, ...(topStudents || []).map(r => Number(r.active_students) || 0));

  return (
    <>
      <div className="stats-grid">
        <Stat label="Receita de cursos"  value={BRL(Number(sum?.total_revenue) || 0)}      hint={`${Number(sum?.total_payments) || 0} mensalidade(s) pagas`} accent/>
        <Stat label="Mensalidades pagas" value={Number(sum?.total_payments) || 0}          hint="no período selecionado"/>
        <Stat label="Alunos ativos"      value={Number(sum?.active_students) || 0}         hint="matrículas com status 'ativo'"/>
        <Stat label="Comissão a pagar"   value={BRL(Number(sum?.total_commission) || 0)}   hint="soma das comissões do período"
              trendDir={(Number(sum?.total_commission) || 0) > 0 ? "dn" : null}/>
      </div>

      <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 16, marginTop: 16 }}>
        <div className="card card-pad">
          <h3 className="section-h" style={{ marginTop: 0 }}>Top cursos por receita</h3>
          {topRevenue.length === 0 ? (
            <div style={{ padding: 20, textAlign: "center", color: "var(--text-mute)", fontSize: 13 }}>Nenhuma receita no período.</div>
          ) : (
            <table className="table" style={{ marginTop: 8 }}>
              <thead>
                <tr>
                  <th style={{ textAlign: "left" }}>Curso</th>
                  <th style={{ textAlign: "left" }}>Professor</th>
                  <th style={{ textAlign: "right", width: 120 }}>Receita</th>
                </tr>
              </thead>
              <tbody>
                {topRevenue.slice(0, 10).map(r => (
                  <tr key={r.course_id}>
                    <td>{r.course_name}</td>
                    <td style={{ color: "var(--text-soft)" }}>{r.teacher_name || "—"}</td>
                    <td style={{ textAlign: "right" }}>
                      <div className="tabular" style={{ fontWeight: 600 }}>{BRL(Number(r.revenue) || 0)}</div>
                      <div style={{ height: 4, background: "var(--pink-100)", borderRadius: 2, marginTop: 4 }}>
                        <div style={{
                          height: "100%", borderRadius: 2,
                          background: "var(--pink-500)",
                          width: `${((Number(r.revenue) || 0) / maxRev * 100).toFixed(1)}%`,
                        }}/>
                      </div>
                    </td>
                  </tr>
                ))}
              </tbody>
            </table>
          )}
        </div>

        <div className="card card-pad">
          <h3 className="section-h" style={{ marginTop: 0 }}>Top cursos por alunos</h3>
          {topStudents.length === 0 ? (
            <div style={{ padding: 20, textAlign: "center", color: "var(--text-mute)", fontSize: 13 }}>Nenhum curso ativo.</div>
          ) : (
            <table className="table" style={{ marginTop: 8 }}>
              <thead>
                <tr>
                  <th style={{ textAlign: "left" }}>Curso</th>
                  <th style={{ textAlign: "left" }}>Professor</th>
                  <th style={{ textAlign: "right", width: 110 }}>Alunos</th>
                </tr>
              </thead>
              <tbody>
                {topStudents.slice(0, 10).map(r => (
                  <tr key={r.course_id}>
                    <td>{r.course_name}</td>
                    <td style={{ color: "var(--text-soft)" }}>{r.teacher_name || "—"}</td>
                    <td style={{ textAlign: "right" }}>
                      <div className="tabular" style={{ fontWeight: 600 }}>{Number(r.active_students) || 0}</div>
                      <div style={{ height: 4, background: "var(--pink-100)", borderRadius: 2, marginTop: 4 }}>
                        <div style={{
                          height: "100%", borderRadius: 2,
                          background: "var(--pink-500)",
                          width: `${((Number(r.active_students) || 0) / maxStu * 100).toFixed(1)}%`,
                        }}/>
                      </div>
                    </td>
                  </tr>
                ))}
              </tbody>
            </table>
          )}
        </div>
      </div>

      <div className="card card-pad" style={{ marginTop: 16 }}>
        <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: 12 }}>
          <h3 className="section-h" style={{ margin: 0 }}>Comissão por professor</h3>
          <button className="btn btn-secondary" onClick={onExport} disabled={commissions.length === 0}>
            <Icon name="download" size={14}/> Exportar XLSX
          </button>
        </div>
        {commissions.length === 0 ? (
          <div style={{ padding: 20, textAlign: "center", color: "var(--text-mute)", fontSize: 13 }}>
            Nenhuma comissão a pagar no período.
          </div>
        ) : (
          <div className="table-x-scroll">
            <table className="table">
              <thead>
                <tr>
                  <th style={{ textAlign: "left" }}>Professor / Curso</th>
                  <th style={{ textAlign: "center", width: 100 }}>Modo</th>
                  <th style={{ textAlign: "center", width: 100 }}>Alunos</th>
                  <th style={{ textAlign: "right", width: 110 }}>Pagamentos</th>
                  <th style={{ textAlign: "right", width: 130 }}>Receita</th>
                  <th style={{ textAlign: "right", width: 130 }}>Comissão</th>
                </tr>
              </thead>
              <tbody>
                {byTeacher.map(g => (
                  <React.Fragment key={g.teacher_id || "_none"}>
                    <tr style={{ background: "var(--bg-soft)" }}>
                      <td colSpan={5} style={{ fontWeight: 700 }}>{g.teacher_name}</td>
                      <td style={{ textAlign: "right", fontWeight: 700, color: "var(--pink-600)" }}>{BRL(g.subtotal)}</td>
                    </tr>
                    {g.rows.map(r => (
                      <tr key={r.course_id}>
                        <td style={{ paddingLeft: 24 }}>{r.course_name}</td>
                        <td style={{ textAlign: "center", fontSize: 12, color: "var(--text-soft)" }}>
                          {r.commission_mode === "pct"
                            ? `${Number(r.commission_value) || 0}%`
                            : `${BRL(Number(r.commission_value) || 0)}/aluno`}
                        </td>
                        <td className="tabular" style={{ textAlign: "center" }}>{Number(r.paying_students) || 0}</td>
                        <td className="tabular" style={{ textAlign: "right" }}>{Number(r.payments) || 0}</td>
                        <td className="tabular" style={{ textAlign: "right" }}>{BRL(Number(r.revenue) || 0)}</td>
                        <td className="tabular" style={{ textAlign: "right", fontWeight: 600 }}>{BRL(Number(r.commission) || 0)}</td>
                      </tr>
                    ))}
                  </React.Fragment>
                ))}
                <tr style={{ borderTop: "2px solid var(--border)" }}>
                  <td colSpan={5} style={{ fontWeight: 700, textAlign: "right" }}>Total geral</td>
                  <td className="tabular" style={{ textAlign: "right", fontWeight: 700, fontSize: 16, color: "var(--pink-600)" }}>
                    {BRL(totalGeneral)}
                  </td>
                </tr>
              </tbody>
            </table>
          </div>
        )}
      </div>
    </>
  );
}

/* ─── Aba: Estoque (filtro fornecedor + datas) ─────────────────── */
function ReportEstoque({ from, to }) {
  const [suppliers,  setSuppliers]  = useS([]);
  const [supplierId, setSupplierId] = useS("");
  const [applyDates, setApplyDates] = useS(false); // se false, ignora from/to (entries/exits = 0)
  const [rows,       setRows]       = useS([]);
  const [loading,    setLoading]    = useS(true);

  useE(() => {
    let alive = true;
    (async () => {
      try {
        const list = await dbListSuppliers();
        if (!alive) return;
        setSuppliers(list || []);
      } catch {}
    })();
    return () => { alive = false; };
  }, []);

  useE(() => {
    let alive = true;
    (async () => {
      setLoading(true);
      try {
        const r = await dbReportStock({
          supplierId: supplierId || null,
          from: applyDates ? from : null,
          to:   applyDates ? to   : null,
        });
        if (!alive) return;
        setRows(r);
      } finally {
        if (alive) setLoading(false);
      }
    })();
    return () => { alive = false; };
  }, [supplierId, applyDates, from, to]);

  const exportXLSX = () => {
    if (rows.length === 0) { alert("Sem produtos pra exportar."); return; }
    const data = rows.map(r => ({
      Código:        r.code,
      Produto:       r.name,
      Fornecedor:    r.supplier_name || "—",
      "Estoque atual":  Number(r.current_stock),
      "Estoque mínimo": Number(r.min_stock || 0),
      Status:        r.status_label,
      Custo:         Number(r.cost).toFixed(2),
      Preço:         Number(r.price).toFixed(2),
      "Valor estoque (custo)": Number(r.stock_value).toFixed(2),
      ...(applyDates ? {
        [`Entradas (${fmtBR(from)} → ${fmtBR(to)})`]: Number(r.entries_qty),
        [`Saídas (${fmtBR(from)} → ${fmtBR(to)})`]:   Number(r.exits_qty),
      } : {}),
    }));
    exportToXLSX(data, "Estoque", `estoque_${supplierId || "todos"}_${from}_${to}.xlsx`);
  };

  const totalValue = rows.reduce((s, r) => s + (Number(r.stock_value) || 0), 0);
  const counts = {
    saudavel: rows.filter(r => r.status_label === "saudavel").length,
    baixo:    rows.filter(r => r.status_label === "baixo").length,
    esgotado: rows.filter(r => r.status_label === "esgotado").length,
  };

  return (
    <>
      <div className="card card-pad" style={{ marginBottom: 16 }}>
        <div style={{ display: "flex", gap: 12, alignItems: "flex-end", flexWrap: "wrap" }}>
          <div className="field" style={{ marginBottom: 0, flex: 1, minWidth: 220 }}>
            <label className="label" htmlFor="est-fornec">Fornecedor</label>
            <select id="est-fornec" name="est-fornec" className="input"
                    value={supplierId} onChange={e => setSupplierId(e.target.value)}>
              <option value="">— Todos —</option>
              {suppliers.map(s => (
                <option key={s.id} value={s.id}>{s.name}</option>
              ))}
            </select>
          </div>
          <div className="field" style={{ marginBottom: 0 }}>
            <label style={{ display: "flex", alignItems: "center", gap: 6, cursor: "pointer" }}>
              <input type="checkbox" name="est-applydates"
                     checked={applyDates}
                     onChange={e => setApplyDates(e.target.checked)}/>
              <span style={{ fontSize: 13 }}>Mostrar entradas/saídas no período</span>
            </label>
          </div>
          <button className="btn btn-primary" onClick={exportXLSX}>
            <Icon name="download" size={14}/> Exportar XLSX
          </button>
        </div>
      </div>

      <div className="stats-grid" style={{ marginBottom: 16 }}>
        <Stat label="Produtos"        value={rows.length}/>
        <Stat label="Saudáveis"       value={counts.saudavel}/>
        <Stat label="Estoque baixo"   value={counts.baixo}/>
        <Stat label="Esgotados"       value={counts.esgotado}/>
        <Stat label="Valor parado"    value={BRL(totalValue)} accent/>
      </div>

      <div className="card card-flush">
        <div style={{ maxHeight: 600, overflowY: "auto" }}>
          <table className="table">
            <thead style={{ position: "sticky", top: 0, background: "white", zIndex: 1 }}>
              <tr>
                <th style={{ width: 100 }}>Código</th>
                <th>Produto</th>
                <th>Fornecedor</th>
                <th style={{textAlign:"center", width: 90}}>Status</th>
                <th style={{textAlign:"right", width: 80}}>Estoque</th>
                <th style={{textAlign:"right", width: 70}}>Mín</th>
                <th style={{textAlign:"right", width: 90}}>Custo</th>
                <th style={{textAlign:"right", width: 90}}>Preço</th>
                <th style={{textAlign:"right", width: 100}}>Valor</th>
                {applyDates && <>
                  <th style={{textAlign:"right", width: 80}}>Entradas</th>
                  <th style={{textAlign:"right", width: 80}}>Saídas</th>
                </>}
              </tr>
            </thead>
            <tbody>
              {loading && <tr><td colSpan={applyDates ? 11 : 9} style={{ textAlign: "center", padding: 20, color: "var(--text-mute)" }}>Carregando...</td></tr>}
              {!loading && rows.length === 0 && <tr><td colSpan={applyDates ? 11 : 9} style={{ textAlign: "center", padding: 20, color: "var(--text-mute)" }}>Sem produtos.</td></tr>}
              {rows.map(r => {
                const stCol = r.status_label === "esgotado" ? "var(--bad-500)"
                             : r.status_label === "baixo"    ? "var(--warn-500)"
                             :                                  "var(--ok-500)";
                return (
                  <tr key={r.product_id}>
                    <td className="tabular" style={{ fontFamily: "var(--font-mono)", color: "var(--pink-600)" }}>{r.code}</td>
                    <td><b>{r.name}</b></td>
                    <td style={{ color: "var(--text-soft)" }}>{r.supplier_name || "—"}</td>
                    <td style={{ textAlign: "center" }}>
                      <span style={{
                        display: "inline-block", padding: "2px 8px", borderRadius: 999,
                        fontSize: 10, fontWeight: 700, color: "white", background: stCol,
                        textTransform: "uppercase",
                      }}>{r.status_label}</span>
                    </td>
                    <td className="tabular" style={{textAlign:"right", fontWeight: 700}}>{Number(r.current_stock)}</td>
                    <td className="tabular" style={{textAlign:"right", color: "var(--text-soft)"}}>{Number(r.min_stock || 0)}</td>
                    <td className="tabular" style={{textAlign:"right", color: "var(--text-soft)"}}>{BRL(Number(r.cost))}</td>
                    <td className="tabular" style={{textAlign:"right"}}>{BRL(Number(r.price))}</td>
                    <td className="tabular" style={{textAlign:"right", fontWeight: 700}}>{BRL(Number(r.stock_value))}</td>
                    {applyDates && <>
                      <td className="tabular" style={{textAlign:"right", color:"var(--ok-600)"}}>+{Number(r.entries_qty)}</td>
                      <td className="tabular" style={{textAlign:"right", color:"var(--pink-600)"}}>−{Number(r.exits_qty)}</td>
                    </>}
                  </tr>
                );
              })}
            </tbody>
          </table>
        </div>
      </div>
    </>
  );
}

/* ─── Aba: Horário de pico (heat-map dia×hora) ─────────────────── */
function ReportHorarios({ from, to }) {
  const [rows,    setRows]    = useS([]);
  const [loading, setLoading] = useS(true);

  useE(() => {
    let alive = true;
    (async () => {
      setLoading(true);
      try {
        const r = await dbReportHourlySummary(from, to);
        if (!alive) return;
        setRows(r);
      } finally {
        if (alive) setLoading(false);
      }
    })();
    return () => { alive = false; };
  }, [from, to]);

  if (loading) return <div style={{ padding: 40, textAlign: "center", color: "var(--text-mute)" }}>Carregando...</div>;
  if (rows.length === 0) return <div className="card card-pad" style={{ textAlign: "center", color: "var(--text-mute)" }}>Sem vendas no período.</div>;

  // Preenche as horas faltantes (entre min e max) com zeros pra dar
  // continuidade visual no eixo X.
  const byHour = new Map(rows.map(r => [Number(r.hour), r]));
  const minH = Math.min(...rows.map(r => Number(r.hour)));
  const maxH = Math.max(...rows.map(r => Number(r.hour)));
  const filled = [];
  for (let h = minH; h <= maxH; h++) {
    filled.push(byHour.get(h) || { hour: h, sale_count: 0, avg_ticket: 0, profit: 0, revenue: 0 });
  }

  // Pico = hora com maior faturamento (acompanha o que o chart destaca).
  const peak = filled.reduce((m, r) => (Number(r.revenue) || 0) > (Number(m.revenue) || 0) ? r : m, filled[0]);
  const totalVendas = filled.reduce((s, r) => s + (Number(r.sale_count) || 0), 0);
  const totalFat    = filled.reduce((s, r) => s + (Number(r.revenue)    || 0), 0);
  const totalProfit = filled.reduce((s, r) => s + (Number(r.profit)     || 0), 0);
  const avgTicket   = totalVendas > 0 ? totalFat / totalVendas : 0;

  // Chart SVG: escala em Y vai de 0 até o próximo "round number" acima do max,
  // pra ter ticks bonitos (5k, 15k, 25k...) em vez de valor cru.
  const maxRevRaw = Math.max(1, ...filled.map(r => Number(r.revenue) || 0));
  const niceMax = (() => {
    if (maxRevRaw <= 100) return Math.ceil(maxRevRaw / 10) * 10;
    const mag = Math.pow(10, Math.floor(Math.log10(maxRevRaw)));
    return Math.ceil(maxRevRaw / (mag / 2)) * (mag / 2);
  })();
  const chartH = 260;
  const chartW = Math.max(600, filled.length * 56 + 80);
  const padL = 64, padR = 16, padT = 16, padB = 32;
  const innerW = chartW - padL - padR;
  const innerH = chartH - padT - padB;
  const yTicks = [0, 0.2, 0.4, 0.6, 0.8, 1.0].map(p => niceMax * p);
  const slotW = innerW / Math.max(1, filled.length);
  const barW = Math.min(40, slotW * 0.5);
  const fmtAxis = (v) => {
    if (v >= 1000) return Math.round(v / 1000) + "k";
    return String(Math.round(v));
  };

  return (
    <>
      <div className="stats-grid" style={{ marginBottom: 16 }}>
        <Stat label="Horário de pico"
              value={`${String(peak.hour).padStart(2,"0")}:00`}
              hint={`${peak.sale_count} vendas · ${BRL(Number(peak.revenue) || 0)}`}
              accent/>
        <Stat label="Faturamento total" value={BRL(totalFat)}/>
        <Stat label="Vendas totais"     value={totalVendas}/>
        <Stat label="Ticket médio"      value={BRL(avgTicket)}/>
      </div>

      {/* Chart de faturamento por hora */}
      <div className="card card-pad" style={{ marginBottom: 16, overflowX: "auto" }}>
        <h3 className="card-title" style={{ margin: 0, marginBottom: 12 }}>Faturamento por hora</h3>
        <svg width={chartW} height={chartH} style={{ display: "block" }}>
          {/* Y axis grid */}
          {yTicks.map((v, i) => {
            const y = padT + innerH - (niceMax > 0 ? (v / niceMax) * innerH : 0);
            return (
              <g key={i}>
                <line x1={padL} y1={y} x2={chartW - padR} y2={y}
                      stroke="var(--border)"
                      strokeDasharray={i === 0 ? "none" : "2,3"}/>
                <text x={padL - 8} y={y + 4} textAnchor="end" fontSize="11" fill="var(--text-mute)">
                  {fmtAxis(v)}
                </text>
              </g>
            );
          })}
          {/* Bars */}
          {filled.map((r, i) => {
            const value = Number(r.revenue) || 0;
            const barH = niceMax > 0 ? (value / niceMax) * innerH : 0;
            const x = padL + i * slotW + (slotW - barW) / 2;
            const y = padT + innerH - barH;
            const isPeak = r.hour === peak.hour && value > 0;
            return (
              <g key={r.hour}>
                <rect x={x} y={y} width={barW} height={barH}
                      fill={isPeak ? "var(--pink-600)" : "var(--pink-500)"}
                      rx="3" ry="3">
                  <title>{`${String(r.hour).padStart(2,"0")}:00 — ${r.sale_count} vendas · ${BRL(value)}`}</title>
                </rect>
                <text x={padL + i * slotW + slotW / 2} y={chartH - padB + 18}
                      textAnchor="middle" fontSize="11" fill="var(--text-soft)">
                  {String(r.hour).padStart(2, "0")}:00
                </text>
              </g>
            );
          })}
        </svg>
      </div>

      {/* Tabela detalhada */}
      <div className="card card-flush" style={{ overflow: "visible" }}>
        <div className="table-x-scroll">
          <table className="table">
            <thead>
              <tr>
                <th style={{ textAlign: "left",  width: 100 }}>Hora</th>
                <th style={{ textAlign: "center", width: 110 }}>Vendas</th>
                <th style={{ textAlign: "center", width: 140 }}>Ticket Médio</th>
                <th style={{ textAlign: "center", width: 160 }}>Lucro Bruto</th>
                <th style={{ textAlign: "right" }}>Faturamento</th>
              </tr>
            </thead>
            <tbody>
              {filled.map(r => {
                const isPeak = r.hour === peak.hour && (Number(r.revenue) || 0) > 0;
                return (
                  <tr key={r.hour} style={{ background: isPeak ? "var(--pink-50, #FFE1EC)" : undefined }}>
                    <td style={{ fontWeight: 600 }}>{String(r.hour).padStart(2, "0")}:00</td>
                    <td className="tabular" style={{ textAlign: "center" }}>{Number(r.sale_count) || 0}</td>
                    <td className="tabular" style={{ textAlign: "center" }}>{BRL(Number(r.avg_ticket) || 0)}</td>
                    <td className="tabular" style={{ textAlign: "center", color: "var(--ok-600)" }}>{BRL(Number(r.profit) || 0)}</td>
                    <td className="tabular" style={{ textAlign: "right", fontWeight: 700 }}>{BRL(Number(r.revenue) || 0)}</td>
                  </tr>
                );
              })}
            </tbody>
            <tfoot>
              <tr style={{ background: "var(--bg-soft, #FAFAFA)" }}>
                <td style={{ fontWeight: 700 }}>Total</td>
                <td className="tabular" style={{ textAlign: "center", fontWeight: 700 }}>{totalVendas}</td>
                <td className="tabular" style={{ textAlign: "center", fontWeight: 700 }}>{BRL(avgTicket)}</td>
                <td className="tabular" style={{ textAlign: "center", fontWeight: 700, color: "var(--ok-600)" }}>{BRL(totalProfit)}</td>
                <td className="tabular" style={{ textAlign: "right", fontWeight: 700, color: "var(--pink-600)" }}>{BRL(totalFat)}</td>
              </tr>
            </tfoot>
          </table>
        </div>
      </div>
    </>
  );
}

/* ─── Aba: Lucro / Margem ──────────────────────────────────────── */
function ReportLucro({ from, to }) {
  const [rows,    setRows]    = useS([]);
  const [loading, setLoading] = useS(true);

  useE(() => {
    let alive = true;
    (async () => {
      setLoading(true);
      try {
        // Top products já traz units, revenue, cost_total, profit — perfeito.
        const r = await dbReportTopProducts(from, to, 200);
        if (!alive) return;
        setRows(r);
      } finally {
        if (alive) setLoading(false);
      }
    })();
    return () => { alive = false; };
  }, [from, to]);

  if (loading) return <div style={{ padding: 40, textAlign: "center", color: "var(--text-mute)" }}>Carregando...</div>;

  // Ordena por lucro absoluto desc
  const sorted = [...rows].sort((a, b) => Number(b.profit) - Number(a.profit));
  const totalProfit  = sorted.reduce((s, r) => s + Number(r.profit), 0);
  const totalRevenue = sorted.reduce((s, r) => s + Number(r.revenue), 0);
  const avgMargin    = totalRevenue > 0 ? (totalProfit / totalRevenue * 100) : 0;

  const exportLucro = () => {
    exportToXLSX(sorted.map(r => {
      const rev = Number(r.revenue), prof = Number(r.profit);
      const margin = rev > 0 ? (prof / rev * 100) : 0;
      return {
        Código:      r.code,
        Produto:     r.name,
        Unidades:    Number(r.units),
        Faturamento: rev.toFixed(2),
        "Custo total": Number(r.cost_total).toFixed(2),
        Lucro:       prof.toFixed(2),
        "Margem %":  margin.toFixed(1),
      };
    }), "Lucro por produto", `lucro_${from}_${to}.xlsx`);
  };

  return (
    <>
      <div className="stats-grid" style={{ marginBottom: 16 }}>
        <Stat label="Lucro total"    value={BRL(totalProfit)} accent/>
        <Stat label="Faturamento"    value={BRL(totalRevenue)}/>
        <Stat label="Margem média"   value={avgMargin.toFixed(1) + "%"}/>
        <Stat label="Produtos"       value={sorted.length}/>
      </div>
      <div className="card card-flush">
        <div style={{ padding: "12px 16px", borderBottom: "1px solid var(--border)", display: "flex", justifyContent: "space-between", alignItems: "center" }}>
          <h3 className="card-title" style={{ margin: 0 }}>Lucro por produto</h3>
          <button className="btn btn-secondary" onClick={exportLucro}>
            <Icon name="download" size={14}/> XLSX
          </button>
        </div>
        <div style={{ maxHeight: 600, overflowY: "auto" }}>
          <table className="table">
            <thead style={{ position: "sticky", top: 0, background: "white", zIndex: 1 }}>
              <tr>
                <th>Produto</th>
                <th style={{textAlign:"right"}}>Unidades</th>
                <th style={{textAlign:"right"}}>Faturamento</th>
                <th style={{textAlign:"right"}}>Custo</th>
                <th style={{textAlign:"right"}}>Lucro</th>
                <th style={{textAlign:"right"}}>Margem</th>
              </tr>
            </thead>
            <tbody>
              {sorted.length === 0 && <tr><td colSpan={6} style={{ textAlign: "center", padding: 20, color: "var(--text-mute)" }}>Sem vendas no período.</td></tr>}
              {sorted.map(r => {
                const rev = Number(r.revenue), prof = Number(r.profit);
                const margin = rev > 0 ? (prof / rev * 100) : 0;
                return (
                  <tr key={r.product_id}>
                    <td>
                      <span className="dash-code-badge" style={{ marginRight: 8 }}>{r.code}</span>
                      <b>{r.name}</b>
                    </td>
                    <td className="tabular" style={{textAlign:"right"}}>{Number(r.units)}</td>
                    <td className="tabular" style={{textAlign:"right"}}>{BRL(rev)}</td>
                    <td className="tabular" style={{textAlign:"right", color: "var(--text-soft)"}}>{BRL(Number(r.cost_total))}</td>
                    <td className="tabular" style={{textAlign:"right", fontWeight: 700, color:"var(--pink-600)"}}>{BRL(prof)}</td>
                    <td className="tabular" style={{textAlign:"right", color: margin > 50 ? "var(--ok-600)" : "var(--warn-600)"}}>{margin.toFixed(0)}%</td>
                  </tr>
                );
              })}
            </tbody>
          </table>
        </div>
      </div>
    </>
  );
}

/* ════════════════════════════════════════════════════════════
   ETIQUETAS (placeholder — aguardando fotos dos modelos)
   ════════════════════════════════════════════════════════════ */
function Etiquetas() {
  const [model,    setModel]    = useS("modelo1");
  // items: [{ pid, code, name, price, qty }] — qty = quantas etiquetas pra esse produto
  const [items,    setItems]    = useS([]);
  const [products, setProducts] = useS(null);   // null = carregando
  const [loadErr,  setLoadErr]  = useS(null);
  // CSV state
  const [csvMsg,   setCsvMsg]   = useS(null);   // { type: 'ok'|'warn'|'err', text }
  // Manual add (com autocomplete por código ou nome)
  const [addCode,        setAddCode]        = useS("");
  const [addQty,         setAddQty]         = useS(1);
  const [addOpen,        setAddOpen]        = useS(false);
  const [addError,       setAddError]       = useS(null);
  const [addSelected,    setAddSelected]    = useS(null);   // produto já fixado
  const [addSuggestOpen, setAddSuggestOpen] = useS(false);
  // Lista de rascunho — produtos adicionados manualmente que ainda não
  // foram confirmados na lista principal de etiquetas. Permite o usuário
  // visualizar/ajustar antes de confirmar tudo de uma vez.
  const [addDraft, setAddDraft] = useS([]); // [{ pid, code, name, price, qty }]
  const fileRef = React.useRef(null);
  const qtyRef  = React.useRef(null);

  useE(() => {
    let cancelled = false;
    (async () => {
      try {
        const p = await dbListProducts("active");
        if (!cancelled) setProducts(p);
      } catch (e) {
        if (!cancelled) setLoadErr(e?.message || String(e));
      }
    })();
    return () => { cancelled = true; };
  }, []);

  const productByCode = (code) => (products || []).find(p => String(p.code) === String(code));
  //   modelo 1 → 3 por linha × 9 linhas = 27 etiquetas por A4
  //   modelo 2 → 5 por linha × 18 linhas = 90 etiquetas por A4
  const labelsPerPage = model === "modelo1" ? 27 : 90;

  // Adiciona/atualiza um item. Se já existir, soma qty.
  const upsertItem = (p, qty) => {
    const q = Math.max(1, Number(qty) || 1);
    setItems(curr => {
      const idx = curr.findIndex(it => it.pid === p.id);
      if (idx >= 0) {
        const next = [...curr];
        next[idx] = { ...next[idx], qty: next[idx].qty + q };
        return next;
      }
      return [...curr, { pid: p.id, code: p.code, name: p.name, price: Number(p.price) || 0, qty: q }];
    });
  };

  const setItemQty = (pid, qty) => {
    const q = Math.max(1, Number(qty) || 1);
    setItems(curr => curr.map(it => it.pid === pid ? { ...it, qty: q } : it));
  };
  const removeItem = (pid) => setItems(curr => curr.filter(it => it.pid !== pid));
  const clearAll   = () => { setItems([]); setCsvMsg(null); };

  const totalLabels = items.reduce((s, it) => s + it.qty, 0);
  const totalPages  = Math.max(1, Math.ceil(totalLabels / labelsPerPage));

  // Sugestões pro autocomplete: filtra por código OU nome do produto.
  // Limita a 6 pra não invadir a tela. Vazio quando o input está em branco
  // ou quando já há um produto selecionado (evita ficar abrindo de novo).
  const addSuggestions = (() => {
    if (addSelected) return [];
    const t = (addCode || "").trim().toLowerCase();
    if (!t) return [];
    return (products || [])
      .filter(p => String(p.code || "").toLowerCase().includes(t) ||
                   String(p.name || "").toLowerCase().includes(t))
      .slice(0, 6);
  })();

  // Selecionar do autocomplete: fixa o produto e foca em "Quantidade".
  const onPickSuggestion = (p) => {
    setAddSelected(p);
    setAddCode(p.code);
    setAddSuggestOpen(false);
    setAddError(null);
    // Move o foco pro campo de quantidade no próximo tick
    setTimeout(() => qtyRef.current?.select(), 0);
  };

  const resetManualAdd = () => {
    setAddCode(""); setAddQty(1); setAddSelected(null);
    setAddSuggestOpen(false); setAddError(null);
  };

  // Adicionar manual: empilha o produto na lista de rascunho (não vai
  // direto para a lista principal). Permite o usuário visualizar/ajustar
  // múltiplos produtos antes de confirmar tudo de uma vez.
  const onManualAdd = () => {
    setAddError(null);
    const p = addSelected || productByCode(String(addCode || "").trim());
    if (!p) {
      setAddError(`Código "${addCode}" não encontrado.`);
      return;
    }
    const q = Math.max(1, Number(addQty) || 1);
    setAddDraft(curr => {
      const idx = curr.findIndex(d => d.pid === p.id);
      if (idx >= 0) {
        const next = [...curr];
        next[idx] = { ...next[idx], qty: next[idx].qty + q };
        return next;
      }
      return [...curr, { pid: p.id, code: p.code, name: p.name, price: Number(p.price) || 0, qty: q }];
    });
    resetManualAdd();
  };

  const setDraftQty = (pid, qty) => {
    const q = Math.max(1, Number(qty) || 1);
    setAddDraft(curr => curr.map(d => d.pid === pid ? { ...d, qty: q } : d));
  };
  const removeDraft = (pid) => setAddDraft(curr => curr.filter(d => d.pid !== pid));

  // Confirma a lista de rascunho — empurra todos para a lista principal
  // (somando se já existirem) e fecha o painel de adicionar manualmente.
  const confirmDraft = () => {
    addDraft.forEach(d => upsertItem({ id: d.pid, code: d.code, name: d.name, price: d.price }, d.qty));
    setAddDraft([]);
    resetManualAdd();
    setAddOpen(false);
  };

  const cancelManualAdd = () => {
    setAddDraft([]);
    resetManualAdd();
    setAddOpen(false);
  };

  // CSV import. Aceita "codigo,qty" ou "codigo;qty", com ou sem cabeçalho.
  // Linhas inválidas (código inexistente) são removidas e reportadas.
  const onCsvFile = async (file) => {
    if (!file) return;
    setCsvMsg(null);
    try {
      const text = await file.text();
      // Detecta separador (vírgula ou ponto-e-vírgula)
      const lines = text.split(/\r?\n/).map(l => l.trim()).filter(Boolean);
      if (lines.length === 0) {
        setCsvMsg({ type: "err", text: "CSV vazio." });
        return;
      }
      const sep = lines[0].includes(";") ? ";" : ",";
      // Detecta cabeçalho — se a primeira linha não parece ter um número na 2ª coluna,
      // assume que é cabeçalho.
      const firstParts = lines[0].split(sep).map(s => s.trim());
      const hasHeader  = isNaN(parseInt(firstParts[1], 10));
      const dataLines  = hasHeader ? lines.slice(1) : lines;

      const found = [];     // produtos válidos
      const missing = [];   // códigos que não existem
      for (const line of dataLines) {
        const parts = line.split(sep).map(s => s.trim().replace(/^"|"$/g, ""));
        const code  = parts[0];
        const qty   = Math.max(1, parseInt(parts[1] || "1", 10) || 1);
        if (!code) continue;
        const p = productByCode(code);
        if (!p) { missing.push(code); continue; }
        found.push({ p, qty });
      }

      if (found.length === 0) {
        setCsvMsg({ type: "err",
          text: `Nenhum código válido encontrado no CSV. ${missing.length > 0 ? `Inexistentes: ${missing.join(", ")}` : ""}` });
        return;
      }

      // Mescla com a lista atual (soma quantidades pra códigos repetidos)
      let merged = [...items];
      for (const { p, qty } of found) {
        const idx = merged.findIndex(it => it.pid === p.id);
        if (idx >= 0) merged[idx] = { ...merged[idx], qty: merged[idx].qty + qty };
        else merged.push({ pid: p.id, code: p.code, name: p.name, price: Number(p.price) || 0, qty });
      }
      setItems(merged);

      if (missing.length > 0) {
        setCsvMsg({ type: "warn",
          text: `${found.length} ${found.length === 1 ? "produto adicionado" : "produtos adicionados"} · ${missing.length} código(s) inexistente(s) ignorado(s): ${missing.join(", ")}` });
      } else {
        setCsvMsg({ type: "ok",
          text: `${found.length} ${found.length === 1 ? "produto adicionado" : "produtos adicionados"} do CSV.` });
      }
    } catch (e) {
      setCsvMsg({ type: "err", text: "Falha ao ler CSV: " + (e?.message || String(e)) });
    }
  };

  // Imprime: aplica @media print pra esconder o resto da tela e renderizar
  // só as etiquetas em layout A4. window.print é nativo do browser.
  const onPrint = () => {
    if (items.length === 0) return;
    window.print();
  };

  // Expande items pelas qty pra renderizar 1 etiqueta por unidade no preview/print
  const flatLabels = items.flatMap(it =>
    Array.from({ length: it.qty }).map((_, i) => ({ ...it, key: `${it.pid}-${i}` })));
  // Quebra em páginas de N etiquetas
  const pages = [];
  for (let i = 0; i < flatLabels.length; i += labelsPerPage) {
    pages.push(flatLabels.slice(i, i + labelsPerPage));
  }

  return (
    <div className="screen etiquetas-screen">
      <PageHead title="Etiquetas de produtos"
        sub="Escolha o modelo e os produtos · imprima diretamente em folha A4"
        actions={
          <button className="btn btn-primary" onClick={onPrint} disabled={items.length === 0}>
            <Icon name="print" size={14}/> Imprimir ({totalLabels} {totalLabels === 1 ? "etiqueta" : "etiquetas"} · {totalPages} {totalPages === 1 ? "folha" : "folhas"})
          </button>
        }/>

      {loadErr && (
        <div style={{ padding: 16, color: "var(--pink-600)", fontWeight: 600 }}>
          Erro ao carregar produtos: {loadErr}
        </div>
      )}

      <div className="etiquetas-row">
        {/* ─── Coluna esquerda: configuração ───────────────────── */}
        <div className="card card-pad etiquetas-config">
          <h3 className="card-title">Configuração</h3>

          <div style={{ marginTop: 16 }}>
            <div className="label" style={{ marginBottom: 8 }}>Modelo da etiqueta</div>
            <div style={{ display: "grid", gap: 8 }}>
              <button className={"label-pick " + (model === "modelo1" ? "active" : "")}
                      onClick={() => setModel("modelo1")}>
                <div className="label-pick-thumb" style={{ background: "white", border: "1px solid var(--border)" }}>
                  <div style={{ fontSize: 8, fontWeight: 800, lineHeight: 1.1 }}>NOME PROD.</div>
                  <div style={{ display: "flex", justifyContent: "space-between", width: "100%", marginTop: 4 }}>
                    <div style={{ fontSize: 11, fontWeight: 800, color: "var(--pink-600)" }}>R$ 9,90</div>
                    <div style={{ fontSize: 7, fontFamily: "var(--font-mono)", letterSpacing: 1 }}>||||</div>
                  </div>
                </div>
                <div>
                  <div style={{ fontWeight: 700, fontSize: 13 }}>Modelo 1</div>
                  <div style={{ fontSize: 11, color: "var(--text-mute)" }}>Etiquetas para artesanatos · 3 por A4</div>
                </div>
              </button>

              <button className={"label-pick " + (model === "modelo2" ? "active" : "")}
                      onClick={() => setModel("modelo2")}>
                <div className="label-pick-thumb" style={{ background: "white", border: "1px solid var(--border)" }}>
                  <div style={{ fontSize: 7, fontFamily: "var(--font-mono)", letterSpacing: 1 }}>||||||||</div>
                  <div style={{ fontSize: 9, fontWeight: 700, fontFamily: "var(--font-mono)", marginTop: 3 }}>19001</div>
                </div>
                <div>
                  <div style={{ fontWeight: 700, fontSize: 13 }}>Modelo 2</div>
                  <div style={{ fontSize: 11, color: "var(--text-mute)" }}>Etiquetas para Bijuterias · 6 por A4</div>
                </div>
              </button>
            </div>
          </div>

          <div style={{ marginTop: 20 }}>
            <div className="label" style={{ marginBottom: 8 }}>Conteúdo</div>
            <ul style={{ margin: 0, padding: 0, listStyle: "none", fontSize: 13, color: "var(--text-soft)", lineHeight: 1.7 }}>
              {model === "modelo1" ? (
                <>
                  <li>• Logo Emporium</li>
                  <li>• Nome do produto</li>
                  <li>• Preço (destaque)</li>
                  <li>• Código de barras (Code 128)</li>
                  <li style={{ color: "var(--text-mute)", fontSize: 12, marginTop: 6 }}>27 etiquetas por folha A4 (3 × 9)</li>
                </>
              ) : (
                <>
                  <li>• Preço (destaque)</li>
                  <li>• Código de barras (Code 128)</li>
                  <li style={{ color: "var(--text-mute)", fontSize: 12, marginTop: 6 }}>90 etiquetas por folha A4 (5 × 18)</li>
                </>
              )}
            </ul>
          </div>

          <div style={{ marginTop: 20 }}>
            <div className="label" style={{ marginBottom: 8 }}>Importar CSV</div>
            <input ref={fileRef} type="file" accept=".csv,text/csv" style={{ display: "none" }}
                   onChange={(e) => { onCsvFile(e.target.files?.[0]); e.target.value = ""; }}/>
            <button className="btn btn-secondary" style={{ width: "100%" }}
                    onClick={() => fileRef.current?.click()} disabled={products == null}>
              <Icon name="download" size={14}/> Selecionar arquivo CSV
            </button>
            <div style={{ fontSize: 11, color: "var(--text-mute)", marginTop: 6, lineHeight: 1.5 }}>
              Colunas: <code style={{ fontFamily: "var(--font-mono)" }}>codigo,quantidade</code>
              {" "}(ou separado por <code style={{ fontFamily: "var(--font-mono)" }}>;</code>).
              Códigos inexistentes são ignorados com aviso.
            </div>
            {csvMsg && (
              <div style={{
                marginTop: 10, padding: "8px 12px", borderRadius: 8, fontSize: 12, fontWeight: 600,
                background: csvMsg.type === "ok"   ? "var(--ok-50, #ECFDF5)"
                          : csvMsg.type === "warn" ? "var(--warn-50, #FFFBEB)"
                          :                          "var(--bad-50, #FEF2F2)",
                color:      csvMsg.type === "ok"   ? "var(--ok-600, #047857)"
                          : csvMsg.type === "warn" ? "var(--warn-600, #B45309)"
                          :                          "var(--bad-500, #DC2626)",
              }}>{csvMsg.text}</div>
            )}
          </div>

          <div style={{ marginTop: 20 }}>
            <div className="label" style={{ marginBottom: 8, display: "flex", justifyContent: "space-between" }}>
              <span>Adicionar manualmente</span>
              {!addOpen && (
                <button className="btn-link" onClick={() => { setAddOpen(true); setAddError(null); }}>
                  <Icon name="plus" size={12}/> Novo
                </button>
              )}
            </div>
            {addOpen && (
              <div style={{ display: "flex", flexDirection: "column", gap: 8 }}>
                {/* Linha 1: campo de busca (com autocomplete) + quantidade.
                    Usamos `minWidth: 0` no container relativo pra que o
                    flex permita o filho encolher conforme a coluna. */}
                <div style={{ display: "flex", gap: 8, alignItems: "stretch" }}>
                  <div style={{ flex: 1, minWidth: 0, position: "relative" }}>
                    <input className="input tabular"
                           placeholder="Código ou nome do produto"
                           value={addCode}
                           onChange={(e) => {
                             setAddCode(e.target.value);
                             setAddError(null);
                             setAddSelected(null);
                             setAddSuggestOpen(true);
                           }}
                           onFocus={() => setAddSuggestOpen(true)}
                           onBlur={() => setTimeout(() => setAddSuggestOpen(false), 150)}
                           onKeyDown={(e) => {
                             if (e.key === "Enter") {
                               // Se há uma sugestão única exata, escolhe; senão tenta adicionar
                               if (addSuggestions.length === 1) onPickSuggestion(addSuggestions[0]);
                               else onManualAdd();
                             }
                             if (e.key === "Escape") setAddSuggestOpen(false);
                           }}
                           style={{ width: "100%", fontFamily: addSelected ? "inherit" : "var(--font-mono)" }}/>
                    {addSuggestOpen && addSuggestions.length > 0 && (
                      <div style={{
                        position: "absolute", top: "calc(100% + 4px)", left: 0, right: 0,
                        background: "white", border: "1px solid var(--border)",
                        borderRadius: "var(--r-md)", boxShadow: "var(--sh-md)",
                        zIndex: 50, maxHeight: 220, overflowY: "auto",
                      }}>
                        {addSuggestions.map(p => (
                          <button key={p.id}
                                  onMouseDown={(e) => e.preventDefault() /* evita o blur antes do click */}
                                  onClick={() => onPickSuggestion(p)}
                                  style={{
                                    width: "100%", textAlign: "left", background: "transparent",
                                    border: "none", padding: "8px 10px", cursor: "pointer",
                                    borderBottom: "1px solid var(--border)",
                                    display: "flex", gap: 8, alignItems: "center",
                                  }}>
                            <span style={{
                              fontFamily: "var(--font-mono)", fontWeight: 700, fontSize: 11,
                              color: "var(--pink-600)", background: "var(--pink-50)",
                              padding: "2px 6px", borderRadius: 4, flexShrink: 0,
                            }}>{p.code}</span>
                            <span style={{
                              fontSize: 12, fontWeight: 500, flex: 1, minWidth: 0,
                              overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap",
                            }}>{p.name}</span>
                          </button>
                        ))}
                      </div>
                    )}
                  </div>
                  <input ref={qtyRef} className="input tabular" type="number" min={1}
                         value={addQty}
                         onChange={(e) => setAddQty(Math.max(1, Number(e.target.value) || 1))}
                         onKeyDown={(e) => e.key === "Enter" && onManualAdd()}
                         style={{ width: 60, flexShrink: 0, textAlign: "center" }}
                         title="Quantidade"/>
                </div>
                {addSelected && (
                  <div style={{ fontSize: 11, color: "var(--text-soft)" }}>
                    Selecionado: <b>{addSelected.name}</b>
                  </div>
                )}
                {addError && (
                  <div style={{ fontSize: 12, color: "var(--pink-600)", fontWeight: 600 }}>{addError}</div>
                )}

                {/* Lista de rascunho — produtos prestes a serem confirmados */}
                {addDraft.length > 0 && (
                  <div style={{
                    border: "1px solid var(--border)", borderRadius: "var(--r-md)",
                    background: "var(--bg-soft, #FAFAFA)", padding: "8px 10px",
                    display: "flex", flexDirection: "column", gap: 6,
                  }}>
                    <div style={{ fontSize: 11, fontWeight: 700, color: "var(--text-soft)",
                                  textTransform: "uppercase", letterSpacing: "0.04em" }}>
                      Pré-visualização ({addDraft.length} {addDraft.length === 1 ? "produto" : "produtos"})
                    </div>
                    {addDraft.map(d => (
                      <div key={d.pid} style={{ display: "flex", gap: 8, alignItems: "center" }}>
                        <span style={{
                          fontFamily: "var(--font-mono)", fontWeight: 700, fontSize: 11,
                          color: "var(--pink-600)", background: "var(--pink-50)",
                          padding: "2px 6px", borderRadius: 4, flexShrink: 0,
                        }}>{d.code}</span>
                        <span style={{
                          fontSize: 12, fontWeight: 500, flex: 1, minWidth: 0,
                          overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap",
                        }}>{d.name}</span>
                        <input className="input tabular" type="number" min={1}
                               value={d.qty}
                               onChange={(e) => setDraftQty(d.pid, e.target.value)}
                               style={{ width: 52, textAlign: "center", padding: "4px 6px", fontSize: 12 }}
                               title="Quantidade"/>
                        <button className="btn-link"
                                onClick={() => removeDraft(d.pid)}
                                title="Remover"
                                style={{ padding: 4 }}>
                          <Icon name="trash" size={12}/>
                        </button>
                      </div>
                    ))}
                  </div>
                )}

                {/* Linha 1: Cancelar (largura natural) + "Adicionar à lista"
                    ocupando o resto. Linha 2: "Confirmar" full-width, só
                    aparece quando há itens no rascunho. */}
                <div style={{ display: "flex", flexDirection: "column", gap: 8 }}>
                  <div style={{ display: "flex", gap: 8, alignItems: "stretch" }}>
                    <button className="btn btn-secondary" onClick={cancelManualAdd}>
                      Cancelar
                    </button>
                    <button className="btn btn-secondary" onClick={onManualAdd}
                            style={{ flex: 1, justifyContent: "center" }}>
                      <Icon name="plus" size={12}/> Adicionar à lista
                    </button>
                  </div>
                  {addDraft.length > 0 && (
                    <button className="btn btn-primary" onClick={confirmDraft}
                            style={{ width: "100%", justifyContent: "center" }}>
                      Confirmar ({addDraft.length})
                    </button>
                  )}
                </div>
              </div>
            )}
          </div>
        </div>

        {/* ─── Coluna direita: lista de itens + preview ─────────── */}
        <div style={{ display: "flex", flexDirection: "column", gap: 16 }}>
          <div className="card card-pad">
            <div className="card-head">
              <h3 className="card-title">
                Produtos selecionados — {items.length} {items.length === 1 ? "item" : "itens"}
                <span style={{ color: "var(--text-mute)", fontWeight: 400, fontSize: 13, marginLeft: 8 }}>
                  · {totalLabels} {totalLabels === 1 ? "etiqueta" : "etiquetas"}
                </span>
              </h3>
              {items.length > 0 && (
                <button className="btn-link" onClick={clearAll}>
                  <Icon name="trash" size={12}/> Limpar
                </button>
              )}
            </div>

            {items.length === 0 ? (
              <div style={{ padding: "24px 12px", color: "var(--text-mute)", textAlign: "center", fontSize: 13 }}>
                Adicione produtos manualmente ou importe um CSV ao lado pra começar.
              </div>
            ) : (
              <div className="table-x-scroll">
                <table className="table" style={{ marginTop: 8 }}>
                  <thead>
                    <tr>
                      <th style={{ width: 110 }}>Código</th>
                      <th>Produto</th>
                      <th style={{ textAlign: "right", width: 110 }}>Preço</th>
                      <th style={{ textAlign: "center", width: 100 }}>Quantidade</th>
                      <th style={{ width: 60 }}></th>
                    </tr>
                  </thead>
                  <tbody>
                    {items.map(it => (
                      <tr key={it.pid}>
                        <td className="tabular" style={{ fontFamily: "var(--font-mono)", fontWeight: 700, color: "var(--pink-600)" }}>{it.code}</td>
                        <td style={{ fontWeight: 600 }}>{it.name}</td>
                        <td className="tabular" style={{ textAlign: "right" }}>{BRL(it.price)}</td>
                        <td style={{ textAlign: "center" }}>
                          <input className="input tabular" type="number" min={1}
                                 value={it.qty}
                                 onChange={(e) => setItemQty(it.pid, e.target.value)}
                                 style={{ width: 70, textAlign: "center", padding: "6px 8px" }}/>
                        </td>
                        <td style={{ textAlign: "center" }}>
                          <button className="btn-icon-mini" title="Remover"
                                  onClick={() => removeItem(it.pid)}>
                            <Icon name="close" size={14}/>
                          </button>
                        </td>
                      </tr>
                    ))}
                  </tbody>
                </table>
              </div>
            )}
          </div>

          {/* Preview / área de impressão. Cada div.lbl-page é uma folha A4. */}
          {flatLabels.length > 0 && (
            <div className="card card-pad">
              <div className="card-head">
                <h3 className="card-title">Pré-visualização — {totalPages} {totalPages === 1 ? "folha" : "folhas"} A4</h3>
                <span style={{ fontSize: 12, color: "var(--text-mute)" }}>
                  {labelsPerPage} etiquetas por folha
                </span>
              </div>

              <div className="lbl-print-area">
                {pages.map((page, pi) => (
                  <div key={pi} className={"lbl-page lbl-page-" + model}>
                    {page.map(lbl => (
                      model === "modelo1" ? (
                        <LabelModelo1 key={lbl.key} item={lbl}/>
                      ) : (
                        <LabelModelo2 key={lbl.key} item={lbl}/>
                      )
                    ))}
                    {/* Preenche os slots vazios da última folha pra manter o grid */}
                    {Array.from({ length: labelsPerPage - page.length }).map((_, i) => (
                      <div key={"empty-" + i} className="lbl-empty"/>
                    ))}
                  </div>
                ))}
              </div>

              {/* Em mobile o A4 preview é escondido via CSS (.lbl-print-area
                  display:none ≤700px). Mostra esse aviso curto pra explicar
                  que a impressão segue funcionando — o @media print força
                  o layout A4 correto independente do que está na tela. */}
              <div className="lbl-mobile-notice">
                Pré-visualização em folha A4 disponível em telas maiores.<br/>
                Toque em <b>Imprimir</b> pra gerar as {totalLabels} {totalLabels === 1 ? "etiqueta" : "etiquetas"} ({totalPages} {totalPages === 1 ? "folha" : "folhas"}).
              </div>
            </div>
          )}
        </div>
      </div>
    </div>
  );
}

/* ─── Etiqueta Modelo 1 — artesanatos ───────────────────────────
   Layout (igual à referência uploads/MODELO 1 - ETIQUETA.png):
   - Nome do produto centralizado no topo, peso regular (não negrito,
     não uppercase) — fica natural pra leitura
   - Logo + Preço lado-a-lado no canto inferior esquerdo
   - Código de barras ROTACIONADO 90° em uma coluna estreita na borda
     direita, ocupando toda a altura; código humano embaixo
   ─────────────────────────────────────────────────────────────── */
function LabelModelo1({ item }) {
  return (
    <div className="lbl lbl-1-v2">
      <div className="lbl1v2-name">{(item.name || "").toUpperCase()}</div>
      <div className="lbl1v2-price">R$ {Number(item.price || 0).toFixed(2).replace(".", ",")}</div>
      <div className="lbl1v2-barcode">
        <Barcode128 text={item.code} width={180} height={28} showText={true} barColor="#000" textSize={9}/>
      </div>
    </div>
  );
}

/* ─── Etiqueta Modelo 2 — bijuterias ────────────────────────────
   - Preço em destaque
   - Código de barras horizontal
   - Código do produto centralizado abaixo
   ─────────────────────────────────────────────────────────────── */
function LabelModelo2({ item }) {
  return (
    <div className="lbl lbl-2-v2">
      <div className="lbl2v2-price">R$ {Number(item.price || 0).toFixed(2).replace(".", ",")}</div>
      <div className="lbl2v2-stack">
        <Barcode128 text={item.code} width={90} height={20} showText={false} barColor="#000"/>
        <div className="lbl2v2-code">{item.code}</div>
      </div>
    </div>
  );
}

/* ════════════════════════════════════════════════════════════
   CLIENTES — espelha a tela de Fornecedores. Mesmas regras de
   cadastro (PJ/CNPJ ou PF/CPF), filtros de status, visualização
   em grid/lista, ações de excluir/inativar/reativar.
   ════════════════════════════════════════════════════════════ */
function Clientes() {
  const [editing,    setEditing]    = useS(null);   // customer | "new" | null
  const [viewing,    setViewing]    = useS(null);   // customer | null → CustomerDetailsModal
  const [openMenu,   setOpenMenu]   = useS(null);
  const [confirming, setConfirming] = useS(null);
  const [customers,  setCustomers]  = useS(null);
  const [loadError,  setLoadError]  = useS(null);
  const openEdit  = (c) => setEditing(c);
  const openNew   = ()  => setEditing("new");
  const closeEdit = ()  => setEditing(null);

  const [q,            setQ]            = useS("");
  const [statusFilter, setStatusFilter] = useS("ativo");
  const [viewMode,     setViewMode]     = useS("grid");
  const [sortBy,       setSortBy]       = useS("name");
  const [sortDir,      setSortDir]      = useS("asc");
  const toggleSort = (key) => {
    if (sortBy === key) setSortDir(d => d === "asc" ? "desc" : "asc");
    else { setSortBy(key); setSortDir("asc"); }
  };

  const reload = async () => {
    try {
      const c = await dbListCustomers();
      setCustomers(c);
      setLoadError(null);
    } catch (e) {
      setLoadError(e?.message || String(e));
      setCustomers([]);
    }
  };
  useE(() => { reload(); }, []);

  const list = customers || [];
  const countActive   = list.filter(c => c.status === "ativo").length;
  const countInactive = list.filter(c => c.status === "inativo").length;

  const filtered = list.filter(c => {
    if (c.status !== statusFilter) return false;
    if (q) {
      const t = q.toLowerCase();
      const blob = [c.name, c.taxId, c.email, c.phone, c.city,
                    c.zip, c.street, c.district, c.state]
                   .filter(Boolean).join(" ").toLowerCase();
      if (!blob.includes(t)) return false;
    }
    return true;
  });

  const sortKey = {
    name:   c => c.name   || "",
    taxId:  c => c.taxId  || "",
    city:   c => c.city   || "",
    phone:  c => c.phone  || "",
    email:  c => c.email  || "",
    zip:    c => c.zip    || "",
    street: c => c.street || "",
    district: c => c.district || "",
    state:  c => c.state  || "",
    status: c => c.status || "",
  };
  const cmp = (a, b) => {
    const fn = sortKey[sortBy] || sortKey.name;
    const va = fn(a), vb = fn(b);
    if (typeof va === "string") return va.localeCompare(vb, "pt-BR");
    return va - vb;
  };
  const sorted = [...filtered].sort((a, b) => sortDir === "asc" ? cmp(a, b) : -cmp(a, b));

  // Virtualização do modo lista (tabela). Modo grid usa cards com altura
  // variável e fica fora — vale virtualizar só quando passar de ~1k cards.
  const CLIENT_ROW_H = 52;
  const tbodyRef = React.useRef(null);
  const virt = useVirtualRows({
    tbodyRef,
    totalRows: sorted.length,
    rowHeight: CLIENT_ROW_H,
    overscan: 8,
    enabled: viewMode === "list" && sorted.length > 80,
  });
  const visibleRows = sorted.slice(virt.range.start, virt.range.end);

  const runConfirmAction = async () => {
    if (!confirming) return;
    const { customer, action } = confirming;
    try {
      if (action === "delete")          await dbDeleteCustomer(customer.id);
      else if (action === "deactivate") await dbSetCustomerStatus(customer.id, "inativo");
      else if (action === "reactivate") await dbSetCustomerStatus(customer.id, "ativo");
      setConfirming(null);
      await reload();
    } catch (e) {
      setConfirming({ ...confirming, error: e?.message || "Falha ao executar a ação." });
    }
  };

  return (
    <div className="screen">
      <PageHead
        title="Clientes"
        sub={customers == null
              ? "Carregando..."
              : `${countActive} ativos · ${list.length} cadastrados`}
        actions={<button className="btn btn-primary" onClick={openNew}><Icon name="plus" size={14}/> Novo cliente</button>}
      />

      {loadError && (
        <div style={{ padding: 16, color: "var(--pink-600)", fontWeight: 600 }}>
          Erro ao carregar: {loadError}
        </div>
      )}

      <div className="filter-bar">
        <div className="input-icon" style={{ flex: 1 }}>
          <Icon name="search" size={16}/>
          <input className="input" placeholder="Buscar por nome, documento, telefone, e-mail..."
                 value={q} onChange={e => setQ(e.target.value)}/>
        </div>
        <div className="seg-toggle" title="Filtrar por status">
          <button className={statusFilter === "ativo"   ? "active" : ""} onClick={() => setStatusFilter("ativo")}>Ativos · {countActive}</button>
          <button className={statusFilter === "inativo" ? "active" : ""} onClick={() => setStatusFilter("inativo")}>Inativos · {countInactive}</button>
        </div>
        <div className="seg-toggle" title="Modo de visualização">
          <button className={viewMode === "grid" ? "active" : ""} onClick={() => setViewMode("grid")} title="Cards"><Icon name="grid" size={14}/></button>
          <button className={viewMode === "list" ? "active" : ""} onClick={() => setViewMode("list")} title="Lista"><Icon name="list" size={14}/></button>
        </div>
      </div>

      {customers == null ? (
        <div className="card card-pad" style={{ textAlign: "center", padding: 40, color: "var(--text-mute)" }}>Carregando...</div>
      ) : list.length === 0 ? (
        <div className="card card-pad" style={{ textAlign: "center", padding: 40, color: "var(--text-mute)" }}>
          Nenhum cliente cadastrado ainda.
        </div>
      ) : sorted.length === 0 ? (
        <div className="card card-pad" style={{ textAlign: "center", padding: 40, color: "var(--text-mute)" }}>
          {q
            ? "Nada encontrado para essa busca."
            : statusFilter === "ativo"   ? "Nenhum cliente ativo."
            : "Nenhum cliente inativo."}
        </div>
      ) : viewMode === "grid" ? (
        /* ─── Visualização em CARDS ─────────────────────────────── */
        <div className="supplier-grid">
          {sorted.map(c => {
            const initials = (c.name || "").split(" ").map(w => w[0]).filter(Boolean).slice(0, 2).join("").toUpperCase();
            const isInactive = c.status === "inativo";
            return (
              <div key={c.id}
                   className={"supplier-card " + (isInactive ? "muted" : "")}
                   onClick={() => setViewing(c)}
                   style={{ cursor: "pointer", position: "relative" }}>
                <button className="btn-icon-mini" title="Ações"
                        style={{ position: "absolute", top: 12, right: 12 }}
                        onClick={(e) => {
                          e.stopPropagation();
                          setOpenMenu(openMenu?.row?.id === c.id
                            ? null
                            : { row: c, anchor: e.currentTarget });
                        }}>
                  <Icon name="moreV" size={16}/>
                </button>

                <div className="supplier-head">
                  <div className="supplier-avatar">{initials}</div>
                  <div style={{ flex: 1, minWidth: 0, paddingRight: 36 }}>
                    <div className="supplier-name">{c.name}</div>
                    <div className="supplier-cnpj">{c.taxId || "—"}</div>
                  </div>
                </div>
                <div className="supplier-contact" style={{ display: "flex", flexDirection: "column", gap: 4, fontSize: 12 }}>
                  <div style={{ display: "flex", gap: 6 }}>
                    <span style={{ color: "var(--text-mute)", fontWeight: 600, minWidth: 64, flexShrink: 0 }}>WhatsApp:</span>
                    <span style={{ color: c.phone ? "var(--text)" : "var(--text-mute)", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>
                      {c.phone || "—"}
                    </span>
                  </div>
                  <div style={{ display: "flex", gap: 6 }}>
                    <span style={{ color: "var(--text-mute)", fontWeight: 600, minWidth: 64, flexShrink: 0 }}>E-mail:</span>
                    <span style={{ color: c.email ? "var(--text)" : "var(--text-mute)", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>
                      {c.email || "—"}
                    </span>
                  </div>
                  <div style={{ display: "flex", gap: 6 }}>
                    <span style={{ color: "var(--text-mute)", fontWeight: 600, minWidth: 64, flexShrink: 0 }}>Cidade:</span>
                    <span style={{ color: c.city ? "var(--text)" : "var(--text-mute)", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>
                      {c.city || "—"}
                    </span>
                  </div>
                </div>
                <div style={{ display: "flex", gap: 8, marginTop: 12, alignItems: "center" }}>
                  <span className="badge" style={{ background: "var(--pink-50, #FFE1EC)", color: "var(--pink-700, #C9186F)" }}>
                    {c.taxIdType === "pj" ? "Pessoa jurídica" : "Pessoa física"}
                  </span>
                  {isInactive && <span className="badge">inativo</span>}
                </div>
              </div>
            );
          })}
        </div>
      ) : (
        /* ─── Visualização em LISTA (tabela) ─────────────────────── */
        /* Ordem: Nome | Telefone (largo) | E-mail | CPF | Cidade (estreita)
           e depois CEP / Logradouro / Número / Bairro / UF — esses ficam
           "fora da tela" no mobile e aparecem rolando horizontalmente.
           min-width na tabela força o overflow-x do .table-x-scroll. */
        <div className="card card-flush" style={{ overflow: "visible" }}>
         <div className="table-x-scroll">
          <table className="table" style={{ minWidth: 1280 }}>
            <thead>
              <tr>
                <SortableTh col="name"     align="left"  width={200}  sortBy={sortBy} sortDir={sortDir} onSort={toggleSort}>Nome</SortableTh>
                <SortableTh col="phone"    align="left"  width={170}  sortBy={sortBy} sortDir={sortDir} onSort={toggleSort}>Telefone</SortableTh>
                <SortableTh col="email"    align="left"  width={220}  sortBy={sortBy} sortDir={sortDir} onSort={toggleSort}>E-mail</SortableTh>
                <SortableTh col="taxId"    align="left"  width={160}  sortBy={sortBy} sortDir={sortDir} onSort={toggleSort}>CPF / CNPJ</SortableTh>
                <SortableTh col="city"     align="left"  width={120}  sortBy={sortBy} sortDir={sortDir} onSort={toggleSort}>Cidade</SortableTh>
                <SortableTh col="zip"      align="left"  width={110}  sortBy={sortBy} sortDir={sortDir} onSort={toggleSort}>CEP</SortableTh>
                <SortableTh col="street"   align="left"  width={200}  sortBy={sortBy} sortDir={sortDir} onSort={toggleSort}>Logradouro</SortableTh>
                <th style={{ textAlign: "left", width: 80 }}>Nº</th>
                <SortableTh col="district" align="left"  width={140}  sortBy={sortBy} sortDir={sortDir} onSort={toggleSort}>Bairro</SortableTh>
                <SortableTh col="state"    align="center" width={60}  sortBy={sortBy} sortDir={sortDir} onSort={toggleSort}>UF</SortableTh>
                <SortableTh col="status"   align="center" width={100} sortBy={sortBy} sortDir={sortDir} onSort={toggleSort}>Status</SortableTh>
                <th style={{ textAlign: "right", width: 80 }}>Ações</th>
              </tr>
            </thead>
            <tbody ref={tbodyRef}>
              {virt.topSpacer > 0 && (
                <tr aria-hidden="true"><td colSpan="12" style={{ height: virt.topSpacer, padding: 0, border: 0 }}/></tr>
              )}
              {visibleRows.map(c => {
                const isInactive = c.status === "inativo";
                return (
                  <tr key={c.id} onClick={() => setViewing(c)}
                      style={{ cursor: "pointer", opacity: isInactive ? 0.65 : 1 }}>
                    <td style={{ fontWeight: 600 }}>{c.name}</td>
                    <td className="tabular" style={{ fontFamily: "var(--font-mono)", fontSize: 13, color: "var(--text-soft)" }}>{c.phone || "—"}</td>
                    <td style={{ color: "var(--text-soft)", fontSize: 13 }}>{c.email || "—"}</td>
                    <td className="tabular" style={{ fontFamily: "var(--font-mono)", fontSize: 13, color: "var(--text-soft)" }}>{c.taxId || "—"}</td>
                    <td style={{ color: "var(--text-soft)", fontSize: 13 }}>{c.city || "—"}</td>
                    <td className="tabular" style={{ fontFamily: "var(--font-mono)", fontSize: 13, color: "var(--text-soft)" }}>{c.zip || "—"}</td>
                    <td style={{ color: "var(--text-soft)", fontSize: 13 }}>{c.street || "—"}</td>
                    <td className="tabular" style={{ fontSize: 13, color: "var(--text-soft)" }}>{c.streetNumber || "—"}</td>
                    <td style={{ color: "var(--text-soft)", fontSize: 13 }}>{c.district || "—"}</td>
                    <td className="tabular" style={{ textAlign: "center", fontFamily: "var(--font-mono)", fontSize: 13, color: "var(--text-soft)" }}>{c.state || "—"}</td>
                    <td style={{ textAlign: "center" }}>
                      {isInactive
                        ? <span className="badge">inativo</span>
                        : <span className="badge badge-ok"><span className="dot"/>ativo</span>}
                    </td>
                    <td style={{ textAlign: "right" }} onClick={e => e.stopPropagation()}>
                      <button className="btn-icon-mini" title="Ações"
                              onClick={(e) => {
                                e.stopPropagation();
                                setOpenMenu(openMenu?.row?.id === c.id
                                  ? null
                                  : { row: c, anchor: e.currentTarget });
                              }}>
                        <Icon name="more" size={16}/>
                      </button>
                    </td>
                  </tr>
                );
              })}
              {virt.bottomSpacer > 0 && (
                <tr aria-hidden="true"><td colSpan="12" style={{ height: virt.bottomSpacer, padding: 0, border: 0 }}/></tr>
              )}
            </tbody>
          </table>
         </div>
        </div>
      )}

      {openMenu && (() => {
        const c = openMenu.row;
        const isInactive = c.status === "inativo";
        return (
          <RowActionMenu
            anchor={openMenu.anchor}
            onClose={() => setOpenMenu(null)}
            items={[
              { icon: "edit", label: "Editar",
                onClick: () => openEdit(c) },
              { icon: isInactive ? "refresh" : "archive",
                label: isInactive ? "Reativar" : "Inativar",
                onClick: () => setConfirming({ customer: c, action: isInactive ? "reactivate" : "deactivate" }) },
              { icon: "trash", label: "Excluir", danger: true,
                onClick: () => setConfirming({ customer: c, action: "delete" }) },
            ]}
          />
        );
      })()}

      {viewing && (
        <CustomerDetailsModal
          customer={viewing}
          onClose={() => setViewing(null)}
          onEdit={(c) => { setViewing(null); openEdit(c); }}
        />
      )}

      {editing && (
        <CustomerForm
          customer={editing === "new" ? null : editing}
          onClose={closeEdit}
          onSaved={async () => { closeEdit(); await reload(); }}
          onRequestDelete={(c)     => { closeEdit(); setConfirming({ customer: c, action: "delete" }); }}
          onRequestDeactivate={(c) => { closeEdit(); setConfirming({ customer: c, action: "deactivate" }); }}
          onRequestReactivate={(c) => { closeEdit(); setConfirming({ customer: c, action: "reactivate" }); }}
        />
      )}

      {confirming && (
        <ConfirmModal
          title={
            confirming.action === "delete"     ? "Excluir cliente?" :
            confirming.action === "deactivate" ? "Inativar cliente?" :
                                                  "Reativar cliente?"
          }
          message={
            confirming.action === "delete"
              ? `Tem certeza que deseja excluir "${confirming.customer.name}"? Essa ação não pode ser desfeita.`
            : confirming.action === "deactivate"
              ? `"${confirming.customer.name}" deixará de aparecer como ativo, mas continuará no sistema. Pode reativar depois.`
              : `"${confirming.customer.name}" voltará a aparecer como ativo.`
          }
          confirmLabel={
            confirming.action === "delete"     ? "Excluir" :
            confirming.action === "deactivate" ? "Inativar" :
                                                  "Reativar"
          }
          danger={confirming.action === "delete"}
          error={confirming.error}
          onConfirm={runConfirmAction}
          onCancel={() => setConfirming(null)}
        />
      )}
    </div>
  );
}

/* ────────────────────────────────────────────────────────────
   CustomerDetailsModal — read-only.
   Mostra contato + endereço + histórico de compras (PDV + Online).
   Clica numa venda → abre o SaleDetailModal (que já existe, com botão
   "Imprimir recibo"). Botão "Editar" no header chama onEdit(customer).
   ──────────────────────────────────────────────────────────── */
function CustomerDetailsModal({ customer, onClose, onEdit }) {
  const [sales,      setSales]      = useS(null);
  const [loadError,  setLoadError]  = useS(null);
  const [viewingSale, setViewingSale] = useS(null);

  useE(() => {
    let alive = true;
    (async () => {
      try {
        const list = await dbListCustomerSales(customer.id);
        if (alive) setSales(list);
      } catch (e) {
        if (alive) { setLoadError(e?.message || String(e)); setSales([]); }
      }
    })();
    return () => { alive = false; };
  }, [customer.id]);

  const fmtDateTime = (s) => {
    if (!s) return "—";
    try {
      return new Date(s).toLocaleString("pt-BR", { day: "2-digit", month: "2-digit", year: "numeric", hour: "2-digit", minute: "2-digit" });
    } catch { return String(s); }
  };

  const isInactive = customer.status === "inativo";
  const list       = sales || [];
  const totalGasto = list.filter(s => s.status === "concluida").reduce((sum, s) => sum + Number(s.total || 0), 0);
  const totalCompras = list.filter(s => s.status === "concluida").length;

  return (
    <div className="modal-backdrop">
      <div className="modal" style={{ width: 760, maxHeight: "92vh", overflowY: "auto" }}>
        <div className="modal-head">
          <div>
            <h2 style={{ margin: 0, fontSize: 18, fontWeight: 700 }}>{customer.name}</h2>
            <div style={{ marginTop: 6, display: "flex", gap: 8, alignItems: "center" }}>
              <span className="badge" style={{ background: "var(--pink-50, #FFE1EC)", color: "var(--pink-700, #C9186F)" }}>
                {customer.taxIdType === "pj" ? "Pessoa jurídica" : "Pessoa física"}
              </span>
              {isInactive && <span className="badge">inativo</span>}
              {customer.taxId && (
                <span style={{ fontFamily: "var(--font-mono)", fontSize: 12, color: "var(--text-mute)" }}>
                  {customer.taxId}
                </span>
              )}
            </div>
          </div>
          <div style={{ display: "flex", gap: 6 }}>
            <button className="btn btn-secondary btn-sm" onClick={() => onEdit(customer)}>
              <Icon name="edit" size={14}/> Editar
            </button>
            <button className="btn-icon-mini" onClick={onClose}><Icon name="close" size={16}/></button>
          </div>
        </div>

        {/* Contato */}
        <div style={{ padding: "12px 14px", background: "var(--bg-soft, #FAFAFA)", borderRadius: "var(--r-md)", marginBottom: 14 }}>
          <div style={{ fontSize: 11, color: "var(--text-mute)", textTransform: "uppercase", letterSpacing: ".05em", marginBottom: 6 }}>
            Contato
          </div>
          <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 8, fontSize: 13 }}>
            <div>
              <div style={{ fontSize: 11, color: "var(--text-mute)" }}>Telefone</div>
              <div style={{ fontFamily: "var(--font-mono)" }}>{customer.phone || "—"}</div>
            </div>
            <div>
              <div style={{ fontSize: 11, color: "var(--text-mute)" }}>E-mail</div>
              <div>{customer.email || "—"}</div>
            </div>
          </div>
          {(customer.street || customer.city) && (
            <div style={{ marginTop: 10, fontSize: 13 }}>
              <div style={{ fontSize: 11, color: "var(--text-mute)" }}>Endereço</div>
              <div>
                {customer.street}{customer.streetNumber ? `, ${customer.streetNumber}` : ""}
                {customer.complement ? ` · ${customer.complement}` : ""}
              </div>
              <div style={{ color: "var(--text-soft)" }}>
                {customer.district && `${customer.district} · `}{customer.city}
                {customer.state ? `/${customer.state}` : ""}
                {customer.zip ? ` · CEP ${customer.zip}` : ""}
              </div>
            </div>
          )}
        </div>

        {/* KPIs */}
        <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 12, marginBottom: 14 }}>
          <div style={{ padding: "10px 12px", border: "1px solid var(--border)", borderRadius: "var(--r-sm)" }}>
            <div style={{ fontSize: 11, color: "var(--text-mute)" }}>Compras concluídas</div>
            <div className="tabular" style={{ fontWeight: 700, fontSize: 18 }}>{totalCompras}</div>
          </div>
          <div style={{ padding: "10px 12px", border: "1px solid var(--border)", borderRadius: "var(--r-sm)" }}>
            <div style={{ fontSize: 11, color: "var(--text-mute)" }}>Total gasto</div>
            <div className="tabular" style={{ fontWeight: 700, fontSize: 18, color: "var(--pink-600)" }}>{BRL(totalGasto)}</div>
          </div>
        </div>

        {/* Histórico */}
        <div>
          <h4 style={{ margin: "0 0 8px 0", fontSize: 13, fontWeight: 700, textTransform: "uppercase", color: "var(--text-soft)", letterSpacing: ".04em" }}>
            Histórico de compras
          </h4>
          {loadError && (
            <div style={{ padding: 10, color: "var(--pink-600)", fontWeight: 600, fontSize: 13 }}>
              Erro ao carregar: {loadError}
            </div>
          )}
          {sales == null ? (
            <div style={{ padding: 20, textAlign: "center", color: "var(--text-mute)", fontSize: 13 }}>Carregando…</div>
          ) : list.length === 0 ? (
            <div style={{ padding: 20, textAlign: "center", color: "var(--text-mute)", fontSize: 13 }}>
              Esse cliente ainda não fez compras.
            </div>
          ) : (
            <div style={{ border: "1px solid var(--border)", borderRadius: "var(--r-sm)", overflow: "hidden" }}>
              <table className="table" style={{ margin: 0 }}>
                <thead>
                  <tr>
                    <th style={{ textAlign: "center", width: 80 }}>#</th>
                    <th style={{ textAlign: "left",   width: 130 }}>Data</th>
                    <th style={{ textAlign: "center", width: 80 }}>Canal</th>
                    <th style={{ textAlign: "left" }}>Itens</th>
                    <th style={{ textAlign: "right",  width: 100 }}>Total</th>
                    <th style={{ textAlign: "center", width: 110 }}>Status</th>
                    <th style={{ textAlign: "center", width: 60 }}></th>
                  </tr>
                </thead>
                <tbody>
                  {list.map(s => {
                    const isOnline = s.channel === "online";
                    const isCancel = s.status === "cancelada";
                    const stLabel  = isCancel ? "cancelada"
                                   : isOnline ? (ONLINE_STATUS_LABELS[s.online_status] || "online")
                                   : "concluída";
                    const stColor  = isCancel ? { bg: "#FEE2E2", fg: "#B91C1C" }
                                   : isOnline ? (ONLINE_STATUS_COLORS[s.online_status] || { bg: "var(--bg-soft)", fg: "var(--text)" })
                                   : { bg: "#DCFCE7", fg: "#15803D" };
                    return (
                      <tr key={s.id}
                          onClick={() => setViewingSale(s.id)}
                          style={{ cursor: "pointer", opacity: isCancel ? 0.7 : 1 }}>
                        <td style={{ textAlign: "center", fontFamily: "var(--font-mono)", fontWeight: 600 }}>#{s.sale_number}</td>
                        <td style={{ fontSize: 12 }}>{fmtDateTime(s.sold_at)}</td>
                        <td style={{ textAlign: "center" }}>
                          <span className="badge" style={{
                            background: isOnline ? "#FCE7F3" : "var(--bg-soft, #F3F4F6)",
                            color:      isOnline ? "#BE185D" : "var(--text)",
                            fontSize: 10,
                          }}>
                            {isOnline ? "ONLINE" : "PDV"}
                          </span>
                        </td>
                        <td style={{ fontSize: 12, color: "var(--text-soft)" }}>
                          {(s.products || []).slice(0, 2).map(p => `${p.quantity}× ${p.name}`).join(", ")}
                          {(s.products || []).length > 2 && ` +${(s.products || []).length - 2}`}
                        </td>
                        <td className="tabular" style={{ textAlign: "right", fontWeight: 600 }}>{BRL(s.total)}</td>
                        <td style={{ textAlign: "center" }}>
                          <span className="badge" style={{ background: stColor.bg, color: stColor.fg, fontSize: 10 }}>
                            {stLabel}
                          </span>
                        </td>
                        <td style={{ textAlign: "center" }} title="Abrir e imprimir recibo">
                          <Icon name="print" size={14}/>
                        </td>
                      </tr>
                    );
                  })}
                </tbody>
              </table>
            </div>
          )}
        </div>

        <div style={{ marginTop: 14, display: "flex", justifyContent: "flex-end" }}>
          <button className="btn btn-secondary" onClick={onClose}>Fechar</button>
        </div>
      </div>

      {viewingSale && (
        <SaleDetailModal saleId={viewingSale} onClose={() => setViewingSale(null)}/>
      )}
    </div>
  );
}

/* ────────────────────────────────────────────────────────────
   CustomerForm — espelha SupplierForm sem o campo "Contato"
   (o cliente é a própria pessoa). Usa máscaras CPF/CNPJ e
   telefone, e valida e-mail quando preenchido.
   ──────────────────────────────────────────────────────────── */
function CustomerForm({ customer, onClose, onSaved, onRequestDelete, onRequestDeactivate, onRequestReactivate, hideDangerActions = false }) {
  const isNew      = !customer;
  const { user } = useApp();
  const [showHistory, setShowHistory] = useS(false);
  const isInactive = customer?.status === "inativo";

  // Default PF (a maioria dos clientes de uma loja são pessoas físicas)
  const [kind,    setKind]    = useS(customer?.taxIdType || "pf");
  const [name,    setName]    = useS(customer?.name  || "");
  const [taxId,   setTaxId]   = useS("");
  const [phone,   setPhone]   = useS("");
  const [email,   setEmail]   = useS(customer?.email || "");

  // Endereço (CEP via ViaCEP; demais auto-preenchidos)
  const [zip,           setZip]           = useS("");
  const [street,        setStreet]        = useS(customer?.street        || "");
  const [streetNumber,  setStreetNumber]  = useS(customer?.streetNumber  || "");
  const [complement,    setComplement]    = useS(customer?.complement    || "");
  const [district,      setDistrict]      = useS(customer?.district      || "");
  const [city,          setCity]          = useS(customer?.city          || "");
  const [state,         setState]         = useS(customer?.state         || "");
  const [cepLoading, setCepLoading] = useS(false);
  const [cepHint,    setCepHint]    = useS(null);   // {type:'ok'|'err', msg:string}

  const [errors,  setErrors]  = useS({});
  const [saving,  setSaving]  = useS(false);
  const [saveError, setSaveError] = useS(null);

  const errStyle = (field) => errors[field]
    ? { borderColor: "var(--pink-500)", boxShadow: "0 0 0 3px var(--pink-100)" }
    : undefined;
  const clearFieldError = (field) =>
    setErrors(prev => prev[field] ? { ...prev, [field]: undefined } : prev);

  useE(() => {
    setTaxId((customer?.taxIdType === "pj" ? maskCNPJ : maskCPF)(customer?.taxId || ""));
    setPhone(maskPhoneBR(customer?.phone || ""));
    setZip(maskCEP(customer?.zip || ""));
  }, [customer]);

  const isPF = kind === "pf";

  const onChangeKind = (k) => {
    setKind(k);
    const digits = String(taxId || "").replace(/\D/g, "");
    setTaxId((k === "pf" ? maskCPF : maskCNPJ)(digits));
  };

  // Quando o CEP atinge 8 dígitos, busca no ViaCEP e auto-preenche os
  // campos vazios (não sobrescreve o que o usuário já editou).
  const onChangeZip = async (raw) => {
    const masked = maskCEP(raw);
    setZip(masked);
    setCepHint(null);
    const digits = masked.replace(/\D/g, "");
    if (digits.length !== 8) return;
    setCepLoading(true);
    try {
      const data = await fetchCepViaCEP(digits);
      if (!data) {
        setCepHint({ type: "err", msg: "CEP não encontrado." });
        return;
      }
      // Sempre atualiza endereço com o que veio do ViaCEP — o usuário
      // ainda pode editar manualmente depois. Número e complemento ficam
      // por conta dele.
      if (data.street)   setStreet(data.street);
      if (data.district) setDistrict(data.district);
      if (data.city)     setCity(data.city);
      if (data.state)    setState(data.state);
      setCepHint({ type: "ok", msg: "Endereço preenchido pelo ViaCEP." });
    } finally {
      setCepLoading(false);
    }
  };

  const validate = () => {
    const e = {};
    if (!name.trim()) e.name = "Informe o " + (isPF ? "nome completo." : "nome / razão social.");
    if (email.trim() && !isValidEmail(email)) e.email = "E-mail inválido. Precisa conter @ e domínio (ex.: nome@dominio.com).";
    setErrors(e);
    return Object.keys(e).length === 0;
  };

  const submit = async () => {
    if (saving) return;
    if (!validate()) return;
    setSaving(true);
    setSaveError(null);
    try {
      await dbUpsertCustomer({
        id:           customer?.id || null,
        name:         name.trim(),
        taxId:        taxId.trim(),
        taxIdType:    kind,
        phone:        phone.trim(),
        email:        email.trim(),
        zip:          zip.trim(),
        street:       street.trim(),
        streetNumber: streetNumber.trim(),
        complement:   complement.trim(),
        district:     district.trim(),
        city:         city.trim(),
        state:        state.trim().toUpperCase(),
        user_id:      user?.id || null,
      });
      await onSaved?.();
    } catch (e) {
      setSaveError(e?.message || "Falha ao salvar o cliente.");
      setSaving(false);
    }
  };

  return (
    <div className="modal-backdrop">
      <div className="modal modal-form modal-split" onClick={e => e.stopPropagation()}>
        <div className="modal-head-pinned">
          <h2 style={{ margin: 0, fontSize: 22, fontWeight: 700 }}>
            {isNew ? "Cadastrar cliente" : customer.name}
          </h2>
          <button className="btn-icon-mini" onClick={onClose} disabled={saving}><Icon name="close" size={18}/></button>
        </div>

        <div className="modal-body">
          <div className="field" style={{ marginBottom: 16 }}>
            <label className="label">Tipo de cadastro</label>
            <div className="seg-toggle" style={{ alignSelf: "flex-start" }}>
              <button type="button" className={kind === "pf" ? "active" : ""} onClick={() => onChangeKind("pf")} disabled={saving}>Pessoa física</button>
              <button type="button" className={kind === "pj" ? "active" : ""} onClick={() => onChangeKind("pj")} disabled={saving}>Pessoa jurídica</button>
            </div>
          </div>

          <div className="form-grid">
            <div className="field" style={{ gridColumn: "1 / -1" }}>
              <label className="label">{isPF ? "Nome completo" : "Razão social"}</label>
              <input className="input" value={name}
                     onChange={e => { setName(e.target.value); clearFieldError("name"); }}
                     placeholder={isPF ? "Ex.: Maria Aparecida da Silva" : "Ex.: Estúdio Aurora Ltda."}
                     style={errStyle("name")} disabled={saving}/>
              {errors.name && (
                <div style={{ fontSize: 12, color: "var(--pink-600)", marginTop: 6, fontWeight: 600 }}>{errors.name}</div>
              )}
            </div>

            <div className="field">
              <label className="label">{isPF ? "CPF" : "CNPJ"} <span className="label-opt">(opcional)</span></label>
              <input className="input" value={taxId} inputMode="numeric"
                     onChange={e => setTaxId((isPF ? maskCPF : maskCNPJ)(e.target.value))}
                     placeholder={isPF ? "000.000.000-00" : "00.000.000/0000-00"}
                     style={{ fontFamily: "var(--font-mono)", fontSize: 16, letterSpacing: "0.5px" }}
                     disabled={saving}/>
            </div>

            <div className="field">
              <label className="label">Telefone {isPF && <span className="label-opt">/ WhatsApp</span>}</label>
              <input className="input" value={phone} inputMode="numeric"
                     onChange={e => setPhone(maskPhoneBR(e.target.value))}
                     placeholder="(11) 90000-0000"
                     style={{ fontFamily: "var(--font-mono)" }}
                     disabled={saving}/>
            </div>

            <div className="field" style={{ gridColumn: "1 / -1" }}>
              <label className="label">E-mail <span className="label-opt">(opcional)</span></label>
              <input className="input" value={email} type="email"
                     onChange={e => { setEmail(e.target.value); clearFieldError("email"); }}
                     placeholder="cliente@email.com"
                     style={errStyle("email")} disabled={saving}/>
              {errors.email && (
                <div style={{ fontSize: 12, color: "var(--pink-600)", marginTop: 6, fontWeight: 600 }}>{errors.email}</div>
              )}
            </div>

            {/* ─── Endereço — CEP busca pelo ViaCEP e preenche o resto ─ */}
            <div style={{ gridColumn: "1 / -1", marginTop: 8, marginBottom: -4 }}>
              <h3 style={{ margin: 0, fontSize: 13, fontWeight: 700, color: "var(--text-soft)", textTransform: "uppercase", letterSpacing: ".05em" }}>
                Endereço <span style={{ fontWeight: 400, textTransform: "none", color: "var(--text-mute)", letterSpacing: 0 }}>· opcional</span>
              </h3>
            </div>

            <div className="field">
              <label className="label">CEP</label>
              <input className="input tabular" value={zip} inputMode="numeric"
                     onChange={e => onChangeZip(e.target.value)}
                     placeholder="00000-000"
                     style={{ fontFamily: "var(--font-mono)" }}
                     disabled={saving}/>
              {cepLoading && (
                <div style={{ fontSize: 11, color: "var(--text-mute)", marginTop: 4 }}>Buscando endereço...</div>
              )}
              {cepHint && !cepLoading && (
                <div style={{
                  fontSize: 11, marginTop: 4, fontWeight: 600,
                  color: cepHint.type === "ok" ? "var(--ok-600, #047857)" : "var(--pink-600)",
                }}>
                  {cepHint.msg}
                </div>
              )}
            </div>

            <div className="field">
              <label className="label">UF</label>
              <input className="input tabular" value={state}
                     onChange={e => setState(e.target.value.toUpperCase().slice(0, 2))}
                     placeholder="SP"
                     style={{ fontFamily: "var(--font-mono)", textTransform: "uppercase" }}
                     disabled={saving}/>
            </div>

            <div className="field" style={{ gridColumn: "1 / -1" }}>
              <label className="label">Logradouro</label>
              <input className="input" value={street} onChange={e => setStreet(e.target.value)}
                     placeholder="Rua / Avenida..." disabled={saving}/>
            </div>

            <div className="field">
              <label className="label">Número</label>
              <input className="input" value={streetNumber}
                     onChange={e => setStreetNumber(e.target.value)}
                     placeholder="Ex.: 123 ou S/N" disabled={saving}/>
            </div>

            <div className="field">
              <label className="label">Complemento</label>
              <input className="input" value={complement}
                     onChange={e => setComplement(e.target.value)}
                     placeholder="Apto, bloco, sala..." disabled={saving}/>
            </div>

            <div className="field">
              <label className="label">Bairro</label>
              <input className="input" value={district}
                     onChange={e => setDistrict(e.target.value)}
                     placeholder="Bairro" disabled={saving}/>
            </div>

            <div className="field">
              <label className="label">Cidade</label>
              <input className="input" value={city}
                     onChange={e => setCity(e.target.value)}
                     placeholder="Cidade" disabled={saving}/>
            </div>
          </div>

          {saveError && (
            <div style={{ fontSize: 13, color: "var(--pink-600)", fontWeight: 600, marginTop: 12, textAlign: "center" }}>
              {saveError}
            </div>
          )}

          {!isNew && customer && (
            <AuditFooter entity="customers" entityId={customer.id}
                         createdAt={customer.created_at} createdByName={customer.created_by_name}
                         updatedAt={customer.updated_at} updatedByName={customer.updated_by_name}
                         onOpenHistory={() => setShowHistory(true)}/>
          )}
        </div>

        <div className="modal-foot-pinned" style={{ justifyContent: "space-between" }}>
          <button className="btn btn-secondary" onClick={onClose} disabled={saving}>Cancelar</button>

          {!isNew && !hideDangerActions && (
            <div style={{ display: "flex", gap: 10 }}>
              <button className="btn btn-danger" onClick={() => onRequestDelete?.(customer)} disabled={saving}
                      title="Remove o cliente do banco. Falha se houver vendas vinculadas.">
                <Icon name="trash" size={14}/> Excluir
              </button>
              {isInactive ? (
                <button className="btn btn-primary" onClick={() => onRequestReactivate?.(customer)} disabled={saving}>
                  <Icon name="refresh" size={14}/> Reativar
                </button>
              ) : (
                <button className="btn btn-secondary" onClick={() => onRequestDeactivate?.(customer)} disabled={saving}>
                  <Icon name="archive" size={14}/> Inativar
                </button>
              )}
            </div>
          )}

          <button className="btn btn-primary" onClick={submit} disabled={saving}>
            <Icon name="check" size={14}/> {saving ? "Salvando..." : (isNew ? "Cadastrar" : "Salvar")}
          </button>
        </div>

        {showHistory && !isNew && customer && (
          <AuditHistoryModal entity="customers" entityId={customer.id}
                             title={`Histórico — ${customer.name}`}
                             onClose={() => setShowHistory(false)}/>
        )}
      </div>
    </div>
  );
}


/* ════════════════════════════════════════════════════════════
   CONFIGURAÇÕES — taxas das maquininhas e parcelamento.
   Lê / grava em app_settings (key = 'payment_fees') no Supabase.
   ════════════════════════════════════════════════════════════ */
/* ════════════════════════════════════════════════════════════
   USUÁRIOS — admin (dono only). Lista usuários, edita cargos,
   ativa/inativa, troca PIN e ajusta permissões granulares.
   ════════════════════════════════════════════════════════════ */
function UsersAdminPanel() {
  const [users,    setUsers]    = useS([]);
  const [loading,  setLoading]  = useS(true);
  const [error,    setError]    = useS(null);
  const [editing,  setEditing]  = useS(null); // user em edição (ou objeto novo)
  const [savingId, setSavingId] = useS(null);

  const reload = async () => {
    setLoading(true); setError(null);
    try {
      const list = await dbListUsersAdmin();
      setUsers(list);
    } catch (e) {
      setError(e?.message || String(e));
    } finally {
      setLoading(false);
    }
  };
  useE(() => { reload(); }, []);

  const startNew = () => setEditing({
    id: null, name: "", role: "operador", is_active: true,
    permissions: { ...DEFAULT_PERMS_BY_ROLE.operador }, pin: "",
  });
  const startEdit = (u) => setEditing({
    id: u.id, name: u.name, role: u.role, is_active: u.is_active,
    permissions: u.permissions || { ...DEFAULT_PERMS_BY_ROLE[u.role] },
    pin: "",
  });
  const cancelEdit = () => setEditing(null);

  const onSave = async () => {
    if (!editing) return;
    if (!editing.name?.trim()) {
      setError("Nome é obrigatório.");
      return;
    }
    if (!editing.id && !editing.pin) {
      setError("PIN é obrigatório para novo usuário.");
      return;
    }
    setSavingId(editing.id || "new");
    setError(null);
    try {
      await dbUpsertUserAdmin({
        id:          editing.id,
        name:        editing.name.trim(),
        role:        editing.role,
        isActive:    editing.is_active,
        permissions: editing.permissions,
        pin:         editing.pin || "",
      });
      setEditing(null);
      await reload();
    } catch (e) {
      setError(e?.message || String(e));
    } finally {
      setSavingId(null);
    }
  };

  const onDeactivate = async (u) => {
    if (!confirm(`Inativar o usuário "${u.name}"? Ele não conseguirá fazer login até ser reativado.`)) return;
    setSavingId(u.id);
    try {
      await dbDeactivateUserAdmin(u.id);
      await reload();
    } catch (e) {
      setError(e?.message || String(e));
    } finally {
      setSavingId(null);
    }
  };

  const setEditingRole = (role) => setEditing(e => ({
    ...e,
    role,
    // se permissões ainda batem com defaults antigos, atualiza pra defaults
    // do novo role; senão preserva edits do usuário
    permissions: { ...DEFAULT_PERMS_BY_ROLE[role] },
  }));

  const togglePerm = (key) => setEditing(e => ({
    ...e,
    permissions: { ...(e.permissions || {}), [key]: !(e.permissions?.[key]) },
  }));

  return (
    <div>
      {error && (
        <div style={{ padding: 10, color: "var(--pink-600)", fontWeight: 600, marginBottom: 10 }}>
          {error}
        </div>
      )}

      {!editing && (
        <div style={{ display: "flex", justifyContent: "flex-end", marginBottom: 12 }}>
          <button className="btn btn-primary" onClick={startNew}>
            <Icon name="plus" size={14}/> Novo usuário
          </button>
        </div>
      )}

      {loading ? (
        <div style={{ padding: 20, textAlign: "center", color: "var(--text-mute)" }}>
          Carregando...
        </div>
      ) : (
        <div style={{ overflowX: "auto" }}>
          <table className="table" style={{ minWidth: 600 }}>
            <thead>
              <tr>
                <th style={{ textAlign: "left" }}>Nome</th>
                <th style={{ textAlign: "left", width: 120 }}>Cargo</th>
                <th style={{ textAlign: "center", width: 100 }}>Status</th>
                <th style={{ textAlign: "right", width: 160 }}>Ações</th>
              </tr>
            </thead>
            <tbody>
              {users.map(u => (
                <tr key={u.id}>
                  <td>
                    <div style={{ fontWeight: 600 }}>{u.name}</div>
                    {u.permissions && (
                      <div style={{ fontSize: 11, color: "var(--text-mute)" }}>
                        Permissões personalizadas
                      </div>
                    )}
                  </td>
                  <td style={{ textTransform: "capitalize" }}>{u.role}</td>
                  <td style={{ textAlign: "center" }}>
                    <span style={{
                      display: "inline-block", padding: "2px 10px", borderRadius: 999,
                      fontSize: 11, fontWeight: 700,
                      background: u.is_active ? "var(--ok-50, #ECFDF5)" : "var(--bg-soft, #F4F4F4)",
                      color: u.is_active ? "var(--ok-600, #059669)" : "var(--text-mute)",
                    }}>
                      {u.is_active ? "ativo" : "inativo"}
                    </span>
                  </td>
                  <td style={{ textAlign: "right" }}>
                    <button className="btn-link" onClick={() => startEdit(u)}>
                      <Icon name="edit" size={12}/> Editar
                    </button>
                    {u.is_active && (
                      <button className="btn-link"
                              onClick={() => onDeactivate(u)}
                              disabled={savingId === u.id}
                              style={{ marginLeft: 8, color: "var(--pink-600)" }}>
                        <Icon name="trash" size={12}/> Inativar
                      </button>
                    )}
                  </td>
                </tr>
              ))}
              {users.length === 0 && (
                <tr><td colSpan={4} style={{ textAlign: "center", color: "var(--text-mute)", padding: 20 }}>
                  Nenhum usuário cadastrado.
                </td></tr>
              )}
            </tbody>
          </table>
        </div>
      )}

      {editing && (
        <div style={{
          marginTop: 16, padding: 16, border: "1px solid var(--border)",
          borderRadius: "var(--r-md)", background: "var(--bg-soft, #FAFAFA)",
        }}>
          <h4 className="section-h" style={{ marginTop: 0, marginBottom: 12 }}>
            {editing.id ? "Editar usuário" : "Novo usuário"}
          </h4>

          <div className="form-grid" style={{ gridTemplateColumns: "1fr 1fr 1fr 1fr", gap: 12 }}>
            <div className="field">
              <label className="label" htmlFor="usr-name">Nome</label>
              <input id="usr-name" name="usr-name" className="input"
                     value={editing.name}
                     onChange={e => setEditing(s => ({ ...s, name: e.target.value }))}
                     disabled={savingId !== null}/>
            </div>
            <div className="field">
              <label className="label" htmlFor="usr-role">Cargo</label>
              <select id="usr-role" name="usr-role" className="input"
                      value={editing.role}
                      onChange={e => setEditingRole(e.target.value)}
                      disabled={savingId !== null}>
                <option value="dono">Dono</option>
                <option value="gerente">Gerente</option>
                <option value="operador">Operador</option>
              </select>
            </div>
            <div className="field">
              <label className="label" htmlFor="usr-pin">
                PIN {editing.id && <span style={{ fontWeight: 400, color: "var(--text-mute)" }}>(deixe vazio p/ manter)</span>}
              </label>
              <input id="usr-pin" name="usr-pin" className="input tabular"
                     type="password" autoComplete="new-password" inputMode="numeric"
                     value={editing.pin}
                     onChange={e => setEditing(s => ({ ...s, pin: e.target.value.replace(/\D/g, "").slice(0, 8) }))}
                     placeholder={editing.id ? "••••" : "1234"}
                     disabled={savingId !== null}/>
            </div>
            <div className="field" style={{ display: "flex", flexDirection: "column", justifyContent: "flex-end" }}>
              <label style={{ display: "flex", alignItems: "center", gap: 6, cursor: "pointer" }}>
                <input type="checkbox" name="usr-active"
                       checked={!!editing.is_active}
                       onChange={e => setEditing(s => ({ ...s, is_active: e.target.checked }))}
                       disabled={savingId !== null}/>
                <span>Usuário ativo</span>
              </label>
            </div>
          </div>

          <div style={{ marginTop: 16 }}>
            <div className="label" style={{ marginBottom: 8 }}>Permissões (telas/ações)</div>
            <div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fill, minmax(200px, 1fr))", gap: 8 }}>
              {PERMISSION_KEYS.map(p => (
                <label key={p.key}
                       style={{
                         display: "flex", alignItems: "center", gap: 8, cursor: "pointer",
                         padding: "6px 10px", border: "1px solid var(--border)",
                         borderRadius: "var(--r-sm, 6px)", background: "white",
                       }}>
                  <input type="checkbox" name={`usr-perm-${p.key}`}
                         checked={!!editing.permissions?.[p.key]}
                         onChange={() => togglePerm(p.key)}
                         disabled={savingId !== null || editing.role === "dono"}/>
                  <span style={{ fontSize: 13 }}>{p.label}</span>
                </label>
              ))}
            </div>
            {editing.role === "dono" && (
              <div style={{ fontSize: 11, color: "var(--text-mute)", marginTop: 6 }}>
                Donos têm acesso total automaticamente — permissões individuais não se aplicam.
              </div>
            )}
          </div>

          <div style={{ display: "flex", gap: 10, justifyContent: "flex-end", marginTop: 16 }}>
            <button className="btn btn-secondary" onClick={cancelEdit} disabled={savingId !== null}>
              Cancelar
            </button>
            <button className="btn btn-primary" onClick={onSave} disabled={savingId !== null}>
              <Icon name="check" size={14}/> {savingId !== null ? "Salvando..." : "Salvar"}
            </button>
          </div>
        </div>
      )}
    </div>
  );
}

/* ════════════════════════════════════════════════════════════
   BACKUP — dump local do banco em JSON. Usa File System
   Access API (showSaveFilePicker) pra escolher onde salvar.
   Fallback pro download tradicional em browsers sem suporte.
   Nome do arquivo: backup-DD-MM-AA.json
   ════════════════════════════════════════════════════════════ */
function backupFilename() {
  const d = new Date();
  const dd = String(d.getDate()).padStart(2, "0");
  const mm = String(d.getMonth() + 1).padStart(2, "0");
  const yy = String(d.getFullYear()).slice(-2);
  return `backup-${dd}-${mm}-${yy}.json`;
}

async function saveBackupFile(content, filename) {
  // API moderna (Chrome/Edge/Opera) — janela "Salvar como…" nativa
  if (typeof window.showSaveFilePicker === "function") {
    try {
      const handle = await window.showSaveFilePicker({
        suggestedName: filename,
        types: [{ description: "Backup JSON", accept: { "application/json": [".json"] } }],
      });
      const w = await handle.createWritable();
      await w.write(content);
      await w.close();
      return { ok: true, picker: true };
    } catch (e) {
      if (e?.name === "AbortError") return { ok: false, cancelled: true };
      // outras falhas (permissão, suporte parcial): cai pro fallback
    }
  }
  // Fallback: download tradicional (vai pra pasta padrão de downloads)
  const blob = new Blob([content], { type: "application/json" });
  const url = URL.createObjectURL(blob);
  const a = document.createElement("a");
  a.href = url;
  a.download = filename;
  document.body.appendChild(a);
  a.click();
  document.body.removeChild(a);
  URL.revokeObjectURL(url);
  return { ok: true, picker: false };
}

function BackupPanel() {
  const [busy,    setBusy]    = useS(null);   // 'json' | 'restore' | null
  const [error,   setError]   = useS(null);
  const [success, setSuccess] = useS(null);
  // Restore: arquivo selecionado + payload parseado + tela de confirmação
  const [restoreFile, setRestoreFile] = useS(null);  // { name, payload, stats }
  const fileInputRef = React.useRef(null);

  const runBackup = async () => {
    if (busy) return;
    setBusy("json"); setError(null); setSuccess(null);
    try {
      const data = await dbBackupDump();
      const content  = JSON.stringify(data, null, 2);
      const filename = backupFilename();
      const result = await saveBackupFile(content, filename);
      if (result.ok) {
        setSuccess(result.picker
          ? `Backup salvo: ${filename}`
          : `Backup baixado: ${filename} (pasta de downloads)`);
      } else if (result.cancelled) {
        setError("Backup cancelado.");
      }
    } catch (e) {
      setError(e?.message || String(e));
    } finally {
      setBusy(null);
    }
  };

  // Lê e valida o arquivo JSON escolhido. Não dispara restore — só prepara
  // a tela de confirmação com prévia do conteúdo.
  const onFilePicked = async (e) => {
    setError(null); setSuccess(null);
    const file = e.target.files?.[0];
    if (!file) return;
    try {
      const text = await file.text();
      const payload = JSON.parse(text);
      if (!payload || typeof payload !== "object") {
        throw new Error("Arquivo não é um JSON de backup válido.");
      }
      const version = Number(payload.version);
      if (![1, 2].includes(version)) {
        throw new Error(`Versão de backup incompatível: ${payload.version} (esperado 1 ou 2).`);
      }
      // Conta linhas por tabela pra prévia
      const stats = {};
      for (const k of Object.keys(payload)) {
        if (Array.isArray(payload[k])) stats[k] = payload[k].length;
      }
      setRestoreFile({ name: file.name, payload, stats, version });
    } catch (err) {
      setError(err?.message || String(err));
    } finally {
      // Reseta input pra permitir re-selecionar mesmo arquivo
      if (fileInputRef.current) fileInputRef.current.value = "";
    }
  };

  const confirmRestore = async () => {
    if (busy || !restoreFile) return;
    setBusy("restore"); setError(null); setSuccess(null);
    try {
      const result = await dbBackupRestore(restoreFile.payload);
      const total = Object.entries(result || {})
        .filter(([k, v]) => k !== "version_restored" && typeof v === "number")
        .reduce((s, [, v]) => s + Number(v), 0);
      setSuccess(`Restore concluído (versão ${result?.version_restored}): ${total.toLocaleString("pt-BR")} linhas restauradas. Atualize a tela para ver os dados.`);
      setRestoreFile(null);
    } catch (e) {
      setError(e?.message || String(e));
    } finally {
      setBusy(null);
    }
  };

  const supportsPicker = typeof window !== "undefined"
    && typeof window.showSaveFilePicker === "function";

  return (
    <div>
      <div style={{ fontSize: 13, color: "var(--text-soft)", marginBottom: 12, lineHeight: 1.5 }}>
        Snapshot completo do banco em <b>JSON</b> — usuários (sem PIN), produtos, clientes,
        fornecedores, vendas, caixas, estoque, cursos e configurações.
        {supportsPicker
          ? <><br/>Uma janela "Salvar como…" aparece pra você escolher a pasta de destino.</>
          : <><br/>⚠ Seu navegador não suporta seleção de pasta — o arquivo vai pra pasta padrão de downloads.</>}
      </div>

      {error && (
        <div style={{ padding: 10, marginBottom: 10, color: "var(--pink-600)",
                      background: "var(--pink-50, #FFE1EC)", borderRadius: "var(--r-sm, 6px)",
                      fontWeight: 600 }}>
          {error}
        </div>
      )}
      {success && (
        <div style={{ padding: 10, marginBottom: 10, color: "var(--ok-600, #059669)",
                      background: "var(--ok-50, #ECFDF5)", borderRadius: "var(--r-sm, 6px)",
                      fontWeight: 600 }}>
          {success}
        </div>
      )}

      <div style={{ display: "flex", gap: 10, flexWrap: "wrap" }}>
        <button className="btn btn-primary"
                onClick={runBackup} disabled={!!busy}>
          <Icon name="download" size={14}/>
          {busy === "json" ? "Gerando JSON..." : `Backup JSON (${backupFilename()})`}
        </button>
      </div>

      {/* ── Restaurar backup ─────────────────────────────────────── */}
      <div style={{
        marginTop: 24, paddingTop: 20,
        borderTop: "1px solid var(--border, #eee)",
      }}>
        <h4 style={{ margin: "0 0 8px", fontSize: 14, fontWeight: 700 }}>Restaurar backup</h4>
        <div style={{ fontSize: 13, color: "var(--text-soft)", lineHeight: 1.5, marginBottom: 12 }}>
          Restaura um arquivo <b>.json</b> gerado pelo botão "Backup JSON" acima.
          <span style={{ color: "var(--pink-700, #A40D4D)", fontWeight: 600 }}> Esta ação APAGA tudo
          que existe hoje no banco</span> e substitui pelos dados do arquivo.
          Faça um backup atual antes de prosseguir como segurança.
        </div>

        <input
          ref={fileInputRef}
          type="file"
          accept=".json,application/json"
          onChange={onFilePicked}
          style={{ display: "none" }}
        />
        <button
          className="btn btn-secondary"
          onClick={() => fileInputRef.current?.click()}
          disabled={!!busy}
        >
          <Icon name="archive" size={14}/> Escolher arquivo .json…
        </button>
      </div>

      {/* Modal de confirmação ao escolher arquivo */}
      {restoreFile && (
        <ConfirmModal
          title="Restaurar backup?"
          danger
          confirmLabel={busy === "restore" ? "Restaurando..." : "Sim, restaurar tudo"}
          onConfirm={confirmRestore}
          onCancel={() => !busy && setRestoreFile(null)}
          error={null}
          message={
            <div>
              <div style={{ marginBottom: 10 }}>
                Arquivo: <b>{restoreFile.name}</b><br/>
                Versão do backup: <b>{restoreFile.version}</b>
              </div>
              <div style={{
                background: "var(--pink-50, #FFE1EC)",
                border: "1px solid var(--pink-200, #FFB7CE)",
                borderRadius: 8, padding: 10, marginBottom: 10,
                fontSize: 13, color: "var(--pink-700, #A40D4D)", fontWeight: 600
              }}>
                ⚠ TUDO que existe hoje no banco será apagado e substituído.
                Não há como desfazer.
              </div>
              <div style={{ fontSize: 12, color: "var(--text-soft)" }}>
                Linhas no arquivo:
              </div>
              <ul style={{
                margin: "6px 0 0", paddingLeft: 18,
                maxHeight: 160, overflowY: "auto",
                fontSize: 12, color: "var(--text-soft)",
              }}>
                {Object.entries(restoreFile.stats)
                  .sort(([a], [b]) => a.localeCompare(b))
                  .map(([table, count]) => (
                    <li key={table}>
                      <code>{table}</code>: <b>{Number(count).toLocaleString("pt-BR")}</b>
                    </li>
                  ))}
              </ul>
            </div>
          }
        />
      )}
    </div>
  );
}

/* ────────────────────────────────────────────────────────────
   AppearancePanel — personalização da cor primária do PDV.

   Lê/grava `app_settings.appearance = { primary_color: "#rrggbb" }`.
   Usa os helpers globais expostos em window.* pelo Emporium PDV.html
   (applyPrimaryColor, hexToScale, DEFAULT_PRIMARY_COLOR) pra:
     1. Calcular a escala (50→900) a partir do hex base (=500)
     2. Sobrescrever `--pink-*` em runtime via <style id="theme-override">

   Preview é AO VIVO — quando o usuário muda a cor, o sistema inteiro
   reage imediatamente. Pra persistir, precisa clicar "Salvar".
   ──────────────────────────────────────────────────────────── */
const COLOR_PRESETS = [
  { label: "Rosa",     hex: "#E91E78" },
  { label: "Azul",     hex: "#3B82F6" },
  { label: "Roxo",     hex: "#8B5CF6" },
  { label: "Verde",    hex: "#10B981" },
  { label: "Laranja",  hex: "#F97316" },
  { label: "Vermelho", hex: "#EF4444" },
];

function AppearancePanel() {
  const defaultColor = (typeof window !== "undefined" && window.DEFAULT_PRIMARY_COLOR) || "#E91E78";
  const [primary,  setPrimary]  = useS(defaultColor);
  const [loaded,   setLoaded]   = useS(false);
  const [busy,     setBusy]     = useS(false);
  const [savedAt,  setSavedAt]  = useS(null);
  const [error,    setError]    = useS(null);
  const [hexInput, setHexInput] = useS(defaultColor); // valor cru do input de texto

  // Carrega cor salva no banco
  useE(() => {
    let alive = true;
    (async () => {
      try {
        const conf = await dbGetAppSetting("appearance");
        if (alive && conf?.primary_color) {
          setPrimary(conf.primary_color);
          setHexInput(conf.primary_color);
        }
      } catch {/* mantém default */}
      finally { if (alive) setLoaded(true); }
    })();
    return () => { alive = false; };
  }, []);

  // Live preview: aplica visualmente conforme o usuário muda
  useE(() => {
    if (loaded && typeof window !== "undefined" && window.applyPrimaryColor) {
      window.applyPrimaryColor(primary);
    }
  }, [primary, loaded]);

  const scale = (typeof window !== "undefined" && window.hexToScale)
    ? window.hexToScale(primary) : {};

  const onChangeHexText = (v) => {
    setHexInput(v);
    if (/^#[0-9a-fA-F]{6}$/.test(v)) setPrimary(v);
  };
  const onPickPreset = (hex) => { setPrimary(hex); setHexInput(hex); };

  const onSave = async () => {
    if (busy) return;
    setBusy(true); setError(null); setSavedAt(null);
    try {
      await dbSetAppSetting("appearance", { primary_color: primary });
      setSavedAt(Date.now());
    } catch (e) {
      setError(e?.message || "Falha ao salvar.");
    } finally {
      setBusy(false);
    }
  };

  const onReset = async () => {
    if (busy) return;
    setPrimary(defaultColor);
    setHexInput(defaultColor);
    setBusy(true); setError(null); setSavedAt(null);
    try {
      await dbSetAppSetting("appearance", { primary_color: defaultColor });
      setSavedAt(Date.now());
    } catch (e) {
      setError(e?.message || "Falha ao salvar.");
    } finally {
      setBusy(false);
    }
  };

  return (
    <div>
      <div style={{ fontSize: 13, color: "var(--text-soft)", marginBottom: 14, lineHeight: 1.5 }}>
        Cor de marca usada nos botões principais, badges, cabeçalhos de tabela e gráficos.
        A configuração vale pra todos os operadores do PDV.
      </div>

      {/* Presets */}
      <div style={{ marginBottom: 18 }}>
        <div className="label" style={{ marginBottom: 8 }}>Sugestões</div>
        <div style={{ display: "flex", gap: 8, flexWrap: "wrap" }}>
          {COLOR_PRESETS.map(p => {
            const active = primary.toLowerCase() === p.hex.toLowerCase();
            return (
              <button key={p.hex}
                      type="button"
                      onClick={() => onPickPreset(p.hex)}
                      style={{
                        background: p.hex,
                        color: "white",
                        border: active ? "3px solid var(--text)" : "1px solid var(--border)",
                        borderRadius: "var(--r-md)",
                        padding: active ? "6px 12px" : "8px 14px",
                        fontWeight: 700,
                        fontSize: 13,
                        cursor: "pointer",
                        minWidth: 84,
                      }}>
                {p.label}
              </button>
            );
          })}
        </div>
      </div>

      {/* Color picker custom */}
      <div className="field" style={{ marginBottom: 18 }}>
        <label className="label">Ou escolha qualquer cor</label>
        <div style={{ display: "flex", gap: 8, alignItems: "center" }}>
          <input type="color"
                 value={primary}
                 onChange={e => { setPrimary(e.target.value); setHexInput(e.target.value); }}
                 style={{ width: 56, height: 40, border: "1px solid var(--border)",
                          borderRadius: "var(--r-md)", cursor: "pointer", padding: 2 }}
                 disabled={busy}/>
          <input type="text"
                 className="input tabular"
                 value={hexInput}
                 onChange={e => onChangeHexText(e.target.value)}
                 placeholder="#E91E78"
                 style={{ width: 130, textTransform: "uppercase" }}
                 maxLength={7}
                 disabled={busy}/>
        </div>
      </div>

      {/* Preview da escala (50→900) */}
      <div style={{ marginBottom: 16 }}>
        <div className="label" style={{ marginBottom: 8 }}>Escala gerada</div>
        <div style={{ display: "flex", borderRadius: "var(--r-md)", overflow: "hidden",
                      border: "1px solid var(--border)" }}>
          {Object.entries(scale).map(([step, hex]) => (
            <div key={step}
                 title={`--pink-${step}: ${hex}`}
                 style={{ flex: 1, background: hex, padding: "18px 4px",
                          textAlign: "center", fontSize: 10, fontWeight: 700,
                          color: Number(step) >= 500 ? "white" : "rgba(0,0,0,0.6)",
                          fontFamily: "var(--font-mono)" }}>
              {step}
            </div>
          ))}
        </div>
        <div style={{ fontSize: 11, color: "var(--text-mute)", marginTop: 6 }}>
          O preview é aplicado em tempo real. Clique <b>Salvar</b> pra confirmar.
        </div>
      </div>

      {error && (
        <div style={{ padding: 10, marginBottom: 10, color: "var(--pink-600)",
                      background: "var(--pink-50)", borderRadius: "var(--r-sm)", fontWeight: 600 }}>
          {error}
        </div>
      )}
      {savedAt && (
        <div style={{ padding: 10, marginBottom: 10, color: "var(--ok-600, #059669)",
                      background: "var(--ok-50, #ECFDF5)", borderRadius: "var(--r-sm)", fontWeight: 600 }}>
          Aparência salva.
        </div>
      )}

      <div style={{ display: "flex", gap: 8, flexWrap: "wrap" }}>
        <button className="btn btn-primary" onClick={onSave} disabled={busy}>
          <Icon name="check" size={14}/> {busy ? "Salvando..." : "Salvar"}
        </button>
        <button className="btn btn-secondary" onClick={onReset} disabled={busy}>
          <Icon name="refresh" size={14}/> Restaurar padrão (rosa)
        </button>
      </div>
    </div>
  );
}


/* PctInput — campo de % com máscara "0,00" preenchendo da direita pra
   esquerda (estilo calculadora/máquina de cartão). Cada dígito digitado
   entra como o último cêntimo; tecla "1" sozinha vira 0,01; "199" vira
   1,99; "12345" vira 123,45. Aceita até 4 inteiros (até 9999,99%).
   Backspace remove o último dígito. NÃO use como sub-componente dentro
   da função pai — React desmontaria a cada render e o foco sumiria. */
function PctInput({ value, onChange, disabled }) {
  // Exibição: número → "0,00" com vírgula como separador decimal
  const display = (Math.max(0, Number(value) || 0)).toFixed(2).replace(".", ",");

  const handleChange = (e) => {
    // Pega só os dígitos do input — ignora vírgulas, pontos, etc.
    // O usuário pode estar digitando, apagando, colando — o valor final
    // de e.target.value pode ser qualquer coisa. Só nos importa os dígitos.
    const digits = e.target.value.replace(/\D/g, "").slice(0, 6); // máx 9999,99
    const num = digits === "" ? 0 : Number(digits) / 100;
    onChange(num);
  };

  return (
    <div style={{ display: "flex", alignItems: "center", gap: 6 }}>
      <input
        className="input tabular"
        type="text"
        inputMode="decimal"
        value={display}
        onChange={handleChange}
        style={{ width: 90, textAlign: "right" }}
        disabled={disabled}
      />
      <span style={{ color: "var(--text-mute)", fontWeight: 600 }}>%</span>
    </div>
  );
}

/* ════════════════════════════════════════════════════════════
   CUPONS DE DESCONTO — CRUD admin (vive em Configurações)
   ════════════════════════════════════════════════════════════ */

// Converte um timestamptz do banco em string yyyy-mm-ddThh:mm pro input
// type="datetime-local". Devolve "" se a entrada for falsy.
function isoToLocalInput(iso) {
  if (!iso) return "";
  const d = new Date(iso);
  if (isNaN(d.getTime())) return "";
  const pad = n => String(n).padStart(2, "0");
  return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}T${pad(d.getHours())}:${pad(d.getMinutes())}`;
}

// "yyyy-mm-ddThh:mm" → ISO. Devolve null se vazio.
function localInputToIso(s) {
  if (!s) return null;
  const d = new Date(s);
  return isNaN(d.getTime()) ? null : d.toISOString();
}

function fmtDateBR(iso) {
  if (!iso) return "—";
  const d = new Date(iso);
  if (isNaN(d.getTime())) return "—";
  return d.toLocaleDateString("pt-BR", { day: "2-digit", month: "2-digit", year: "numeric" });
}

function CouponsAdminPanel() {
  const { user } = useApp();
  const [list, setList]         = useS([]);
  const [loading, setLoading]   = useS(true);
  const [error, setError]       = useS(null);
  const [statusView, setStatus] = useS("all");
  const [editing, setEditing]   = useS(null); // null = fechado | {} = novo | objeto = edit
  const [confirmDel, setConfirmDel] = useS(null);

  const reload = async () => {
    setLoading(true); setError(null);
    try {
      const data = await dbListCouponsAdmin(statusView);
      setList(data);
    } catch (e) {
      setError(e?.message || String(e));
    } finally {
      setLoading(false);
    }
  };
  useE(() => { reload(); }, [statusView]);

  const toggleActive = async (c) => {
    try {
      await dbSetCouponActive(c.id, !c.is_active);
      await reload();
    } catch (e) {
      setError(e?.message || String(e));
    }
  };

  const onDelete = async () => {
    if (!confirmDel) return;
    try {
      await dbDeleteCoupon(confirmDel.id);
      setConfirmDel(null);
      await reload();
    } catch (e) {
      setConfirmDel(prev => ({ ...prev, error: e?.message || String(e) }));
    }
  };

  return (
    <div>
      {/* Toolbar: filtro + novo */}
      <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", gap: 12, marginBottom: 12, flexWrap: "wrap" }}>
        <div className="seg-toggle">
          <button type="button" className={statusView === "all"      ? "active" : ""} onClick={() => setStatus("all")}>Todos</button>
          <button type="button" className={statusView === "active"   ? "active" : ""} onClick={() => setStatus("active")}>Ativos</button>
          <button type="button" className={statusView === "inactive" ? "active" : ""} onClick={() => setStatus("inactive")}>Inativos</button>
        </div>
        <button className="btn btn-primary" onClick={() => setEditing({})}>
          <Icon name="plus" size={14}/> Novo cupom
        </button>
      </div>

      {error && (
        <div style={{ padding: 10, color: "var(--pink-600)", fontWeight: 600 }}>{error}</div>
      )}

      {loading ? (
        <div style={{ padding: 16, color: "var(--text-mute)" }}>Carregando…</div>
      ) : list.length === 0 ? (
        <div style={{ padding: 24, textAlign: "center", color: "var(--text-mute)" }}>
          Nenhum cupom cadastrado. Clique em "Novo cupom" para começar.
        </div>
      ) : (
        <div style={{ overflowX: "auto" }}>
          <table className="table" style={{ minWidth: 720 }}>
            <thead>
              <tr>
                <th style={{ textAlign: "left" }}>Código</th>
                <th style={{ textAlign: "left" }}>Desconto</th>
                <th style={{ textAlign: "left" }}>Validade</th>
                <th style={{ textAlign: "left" }}>Categorias</th>
                <th style={{ textAlign: "center" }}>Usos</th>
                <th style={{ textAlign: "center" }}>Status</th>
                <th style={{ textAlign: "right" }}>Ações</th>
              </tr>
            </thead>
            <tbody>
              {list.map(c => {
                const isPct      = c.discount_type === "percent";
                const isShipAll  = c.discount_type === "shipping_all";
                const isShipSJC  = c.discount_type === "shipping_sjc";
                const discLabel  = isShipAll ? "Frete grátis"
                                 : isShipSJC ? "Frete grátis (SJC)"
                                 : isPct     ? `${Number(c.discount_value).toFixed(2).replace(".", ",")}%`
                                 :             `R$ ${Number(c.discount_value).toFixed(2).replace(".", ",")}`;
                const validity = (() => {
                  if (!c.valid_from && !c.valid_until) return "Sem prazo";
                  if (c.valid_from && c.valid_until) return `${fmtDateBR(c.valid_from)} → ${fmtDateBR(c.valid_until)}`;
                  if (c.valid_until) return `Até ${fmtDateBR(c.valid_until)}`;
                  return `A partir de ${fmtDateBR(c.valid_from)}`;
                })();
                const usesLabel = c.max_uses ? `${c.usage_count}/${c.max_uses}` : `${c.usage_count}`;
                let statusLabel = c.is_active ? "Ativo" : "Inativo";
                let statusColor = c.is_active ? "var(--ok-600)" : "var(--text-mute)";
                if (c.is_active && c.is_expired)   { statusLabel = "Expirado";  statusColor = "var(--pink-600)"; }
                if (c.is_active && c.is_exhausted) { statusLabel = "Esgotado";  statusColor = "var(--pink-600)"; }
                return (
                  <tr key={c.id}>
                    <td>
                      <div style={{ fontWeight: 700, fontFamily: "var(--font-mono)", letterSpacing: 1 }}>{c.code}</div>
                      {c.description && <div style={{ fontSize: 11, color: "var(--text-mute)" }}>{c.description}</div>}
                    </td>
                    <td style={{ fontFamily: "var(--font-mono)" }}>{discLabel}</td>
                    <td style={{ fontSize: 12 }}>{validity}</td>
                    <td style={{ fontSize: 12 }}>
                      {(!c.category_names || c.category_names.length === 0)
                        ? <span style={{ color: "var(--text-mute)" }}>Todas</span>
                        : c.category_names.join(", ")}
                    </td>
                    <td style={{ textAlign: "center", fontFamily: "var(--font-mono)" }}>{usesLabel}</td>
                    <td style={{ textAlign: "center", color: statusColor, fontWeight: 700, fontSize: 12 }}>{statusLabel}</td>
                    <td style={{ textAlign: "right", whiteSpace: "nowrap" }}>
                      <button className="btn-icon-mini" title="Editar" onClick={() => setEditing(c)}>
                        <Icon name="edit" size={14}/>
                      </button>
                      <button className="btn-icon-mini" title={c.is_active ? "Inativar" : "Ativar"} onClick={() => toggleActive(c)}>
                        <Icon name={c.is_active ? "eye-off" : "eye"} size={14}/>
                      </button>
                      <button className="btn-icon-mini" title="Excluir" onClick={() => setConfirmDel(c)}>
                        <Icon name="trash" size={14}/>
                      </button>
                    </td>
                  </tr>
                );
              })}
            </tbody>
          </table>
        </div>
      )}

      {editing !== null && (
        <CouponModal
          coupon={editing}
          userId={user?.id || null}
          onClose={() => setEditing(null)}
          onSaved={async () => { setEditing(null); await reload(); }}
        />
      )}

      {confirmDel && (
        <ConfirmModal
          title="Excluir cupom"
          message={`Excluir o cupom "${confirmDel.code}" definitivamente? Cupons já usados em vendas não podem ser excluídos — nesse caso, inative em vez de excluir.`}
          confirmLabel="Excluir"
          danger
          error={confirmDel.error}
          onConfirm={onDelete}
          onCancel={() => setConfirmDel(null)}
        />
      )}
    </div>
  );
}

function CouponModal({ coupon, userId, onClose, onSaved }) {
  const isEdit = Boolean(coupon?.id);
  const [code, setCode]                 = useS(coupon?.code || "");
  const [description, setDescription]   = useS(coupon?.description || "");
  const [discountType, setDiscountType] = useS(coupon?.discount_type || "percent");
  // Valor sempre formatado calculator-style "0,00" (vale pra % e pra R$).
  const [discountValue, setDiscountValue] = useS(
    coupon?.discount_value != null && Number(coupon.discount_value) > 0
      ? maskBRL(String(Math.round(Number(coupon.discount_value) * 100)))
      : ""
  );
  const [validFrom, setValidFrom]   = useS(isoToLocalInput(coupon?.valid_from));
  const [validUntil, setValidUntil] = useS(isoToLocalInput(coupon?.valid_until));
  const [categoryIds, setCategoryIds] = useS(coupon?.category_ids || []);
  // Subtotal mínimo em formato BRL ("R$ 0,00"). Vazio = sem mínimo.
  const [minSubtotal, setMinSubtotal] = useS(
    coupon?.min_subtotal != null && Number(coupon.min_subtotal) > 0
      ? maskBRL(String(Math.round(Number(coupon.min_subtotal) * 100)))
      : ""
  );
  const [maxUses, setMaxUses]   = useS(coupon?.max_uses != null ? String(coupon.max_uses) : "");
  const [maxUsesPerUser, setMaxUsesPerUser] = useS(coupon?.max_uses_per_user != null ? String(coupon.max_uses_per_user) : "");
  const [firstPurchaseOnly, setFirstPurchaseOnly] = useS(Boolean(coupon?.first_purchase_only));
  const [isActive, setIsActive] = useS(coupon?.is_active !== false);
  const [notes, setNotes]       = useS(coupon?.notes || "");

  const [categories, setCategories] = useS([]);
  const [saving, setSaving] = useS(false);
  const [error, setError]   = useS(null);

  useE(() => {
    (async () => {
      try {
        const cats = await dbListCategories();
        setCategories(cats);
      } catch { /* ignore */ }
    })();
  }, []);

  const toggleCategory = (id) => {
    setCategoryIds(prev => prev.includes(id) ? prev.filter(x => x !== id) : [...prev, id]);
  };

  const submit = async () => {
    if (saving) return;
    const codeNorm = code.trim().toUpperCase();
    if (!codeNorm) { setError("Informe o código do cupom."); return; }
    const isShipping = discountType === "shipping_all" || discountType === "shipping_sjc";
    const dv = isShipping ? 0 : parseBRL(discountValue);
    if (!isShipping && dv <= 0) {
      setError("Preencha o valor do desconto (deve ser maior que zero)."); return;
    }
    if (discountType === "percent" && dv > 100) { setError("Desconto percentual não pode passar de 100%."); return; }
    const vf = localInputToIso(validFrom);
    const vu = localInputToIso(validUntil);
    if (vf && vu && new Date(vu) < new Date(vf)) {
      setError("Data final menor que a inicial."); return;
    }
    const msNum = minSubtotal.trim() ? parseBRL(minSubtotal) : null;
    const ms = msNum != null && msNum > 0 ? msNum : null;
    const mu = maxUses.trim() ? Math.max(1, parseInt(maxUses, 10) || 0) : null;
    if (maxUses.trim() && (!mu || mu < 1)) { setError("Máximo de usos inválido."); return; }
    const mupu = maxUsesPerUser.trim() ? Math.max(1, parseInt(maxUsesPerUser, 10) || 0) : null;
    if (maxUsesPerUser.trim() && (!mupu || mupu < 1)) { setError("Usos por cliente inválido."); return; }

    setSaving(true); setError(null);
    try {
      await dbUpsertCoupon({
        id: coupon?.id || null,
        code: codeNorm,
        description: description.trim() || null,
        discountType,
        discountValue: dv,
        validFrom: vf,
        validUntil: vu,
        categoryIds,
        minSubtotal: ms,
        maxUses: mu,
        maxUsesPerUser: mupu,
        firstPurchaseOnly,
        isActive,
        notes: notes.trim() || null,
        userId,
      });
      await onSaved?.();
    } catch (e) {
      setError(e?.message || String(e));
      setSaving(false);
    }
  };

  return (
    <div className="modal-backdrop">
      <div className="modal" style={{ width: 640, maxHeight: "90vh", overflowY: "auto", padding: 0 }} onClick={e => e.stopPropagation()}>
        {/* Cabeçalho sticky com mais respiro */}
        <div className="modal-head" style={{ position: "sticky", top: 0, background: "white", padding: "20px 24px", borderBottom: "1px solid var(--border, #eee)", margin: 0, zIndex: 1 }}>
          <h2 style={{ margin: 0, fontSize: 18, fontWeight: 700 }}>
            {isEdit ? `Editar cupom · ${coupon.code}` : "Novo cupom"}
          </h2>
          <button className="btn-icon-mini" onClick={onClose} disabled={saving}><Icon name="close" size={16}/></button>
        </div>

        {/* Corpo com gap consistente entre grupos */}
        <div style={{ padding: "20px 24px", display: "flex", flexDirection: "column", gap: 22 }}>

          {/* ── Grupo 1: Identidade ─────────────────────────────────── */}
          <div style={{ display: "flex", flexDirection: "column", gap: 14 }}>
            <SectionLabel>Identidade</SectionLabel>
            <div className="form-grid" style={{ gridTemplateColumns: "1fr 1fr", gap: 14 }}>
              <div className="field" style={{ marginBottom: 0 }}>
                <label className="label">Código *</label>
                <input className="input tabular" value={code}
                       onChange={e => setCode(e.target.value.toUpperCase().replace(/\s+/g, ""))}
                       placeholder="Ex: FEITOAMAO"
                       maxLength={32}
                       disabled={saving}/>
              </div>
              <div className="field" style={{ marginBottom: 0 }}>
                <label className="label">Status</label>
                <div className="seg-toggle" style={{ alignSelf: "flex-start" }}>
                  <button type="button" className={isActive  ? "active" : ""} onClick={() => setIsActive(true)}  disabled={saving}>Ativo</button>
                  <button type="button" className={!isActive ? "active" : ""} onClick={() => setIsActive(false)} disabled={saving}>Inativo</button>
                </div>
              </div>
            </div>
            <div className="field" style={{ marginBottom: 0 }}>
              <label className="label">Descrição (opcional)</label>
              <input className="input" value={description}
                     onChange={e => setDescription(e.target.value)}
                     placeholder="Ex.: 10% off de boas-vindas"
                     disabled={saving}/>
            </div>
          </div>

          <Divider/>

          {/* ── Grupo 2: Desconto ───────────────────────────────────── */}
          <div style={{ display: "flex", flexDirection: "column", gap: 14 }}>
            <SectionLabel>Desconto</SectionLabel>
            <div className="field" style={{ marginBottom: 0 }}>
              <label className="label">Tipo *</label>
              <div className="seg-toggle" style={{ alignSelf: "flex-start", flexWrap: "wrap", gap: 4 }}>
                <button type="button" className={discountType === "percent"      ? "active" : ""} onClick={() => setDiscountType("percent")}      disabled={saving}>% percentual</button>
                <button type="button" className={discountType === "fixed"        ? "active" : ""} onClick={() => setDiscountType("fixed")}        disabled={saving}>R$ valor fixo</button>
                <button type="button" className={discountType === "shipping_all" ? "active" : ""} onClick={() => setDiscountType("shipping_all")} disabled={saving}>Frete grátis · todos</button>
                <button type="button" className={discountType === "shipping_sjc" ? "active" : ""} onClick={() => setDiscountType("shipping_sjc")} disabled={saving}>Frete grátis · só SJC</button>
              </div>
              <div style={{ fontSize: 12, color: "var(--text-mute)", marginTop: 8, lineHeight: 1.5 }}>
                {discountType === "percent"      && "Desconta % do subtotal de produtos."}
                {discountType === "fixed"        && "Desconta R$ fixo do subtotal de produtos."}
                {discountType === "shipping_all" && "Zera o frete em qualquer entrega. O campo Valor é ignorado."}
                {discountType === "shipping_sjc" && "Zera o frete apenas quando o endereço de entrega for São José dos Campos. O campo Valor é ignorado."}
              </div>
            </div>

            {discountType !== "shipping_all" && discountType !== "shipping_sjc" && (
              <div className="field" style={{ marginBottom: 0 }}>
                <label className="label">Valor *</label>
                <div style={{ display: "flex", alignItems: "center", gap: 8 }}>
                  {discountType === "fixed" && (
                    <span style={{ color: "var(--text-mute)", fontWeight: 600 }}>R$</span>
                  )}
                  <input className="input tabular" value={discountValue || "0,00"}
                         onChange={e => setDiscountValue(maskBRL(e.target.value))}
                         onFocus={e => e.target.select()}
                         placeholder="0,00"
                         style={{ textAlign: "right", width: 140 }}
                         inputMode="numeric"
                         disabled={saving}/>
                  {discountType === "percent" && (
                    <span style={{ color: "var(--text-mute)", fontWeight: 600 }}>%</span>
                  )}
                </div>
              </div>
            )}
          </div>

          <Divider/>

          {/* ── Grupo 3: Validade ───────────────────────────────────── */}
          <div style={{ display: "flex", flexDirection: "column", gap: 14 }}>
            <SectionLabel>Validade</SectionLabel>
            <div className="form-grid" style={{ gridTemplateColumns: "1fr 1fr", gap: 14 }}>
              <div className="field" style={{ marginBottom: 0 }}>
                <label className="label">Válido a partir de</label>
                <input className="input" type="datetime-local"
                       value={validFrom} onChange={e => setValidFrom(e.target.value)}
                       disabled={saving}/>
                <div style={{ fontSize: 11, color: "var(--text-mute)", marginTop: 6 }}>Vazio = válido imediatamente</div>
              </div>
              <div className="field" style={{ marginBottom: 0 }}>
                <label className="label">Válido até</label>
                <input className="input" type="datetime-local"
                       value={validUntil} onChange={e => setValidUntil(e.target.value)}
                       disabled={saving}/>
                <div style={{ fontSize: 11, color: "var(--text-mute)", marginTop: 6 }}>Vazio = sem expiração</div>
              </div>
            </div>
          </div>

          <Divider/>

          {/* ── Grupo 4: Categorias ─────────────────────────────────── */}
          <div style={{ display: "flex", flexDirection: "column", gap: 10 }}>
            <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", gap: 12, flexWrap: "wrap" }}>
              <SectionLabel>Categorias permitidas</SectionLabel>
              {categories.length > 0 && (() => {
                const allSelected = categoryIds.length === categories.length;
                return (
                  <div style={{ display: "inline-flex", gap: 6 }}>
                    <button
                      type="button"
                      onClick={() => setCategoryIds(allSelected ? [] : categories.map(c => c.id))}
                      disabled={saving}
                      className="btn-link"
                      style={{
                        fontSize: 12, fontWeight: 600,
                        color: "var(--pink-700, #A40D4D)",
                        background: "transparent", border: 0, padding: "4px 8px",
                        cursor: "pointer",
                      }}
                    >
                      {allSelected ? "Limpar seleção" : "Selecionar todas"}
                    </button>
                  </div>
                );
              })()}
            </div>
            <div style={{ fontSize: 12, color: "var(--text-mute)", lineHeight: 1.5 }}>
              Selecione as categorias onde o cupom é válido. Deixe tudo desmarcado pra valer pra qualquer produto.
            </div>
            <div style={{
              display: "flex", flexWrap: "wrap", gap: 8,
              maxHeight: 160, overflowY: "auto",
              padding: 12, border: "1px solid var(--border, #eee)", borderRadius: 10,
              background: "var(--surface-soft, #fafafa)"
            }}>
              {categories.length === 0 && (
                <div style={{ color: "var(--text-mute)", fontSize: 12 }}>Sem categorias cadastradas.</div>
              )}
              {categories.map(cat => {
                const checked = categoryIds.includes(cat.id);
                return (
                  <button
                    key={cat.id}
                    type="button"
                    onClick={() => toggleCategory(cat.id)}
                    disabled={saving}
                    style={{
                      padding: "6px 12px",
                      borderRadius: 14,
                      border: checked ? "2px solid var(--pink-500, #E91E78)" : "1px solid var(--border, #ddd)",
                      background: checked ? "var(--pink-50, #FFE4F0)" : "white",
                      color: checked ? "var(--pink-700, #A40D4D)" : "var(--text)",
                      fontSize: 12,
                      fontWeight: checked ? 700 : 500,
                      cursor: "pointer",
                    }}
                  >
                    {cat.name}
                  </button>
                );
              })}
            </div>
          </div>

          <Divider/>

          {/* ── Grupo 5: Regras de uso ──────────────────────────────── */}
          <div style={{ display: "flex", flexDirection: "column", gap: 14 }}>
            <SectionLabel>Regras de uso</SectionLabel>
            <div className="form-grid" style={{ gridTemplateColumns: "1fr 1fr", gap: 14 }}>
              <div className="field" style={{ marginBottom: 0 }}>
                <label className="label">Subtotal mínimo (R$)</label>
                <div style={{ display: "flex", alignItems: "center", gap: 8 }}>
                  <span style={{ color: "var(--text-mute)", fontWeight: 600 }}>R$</span>
                  <input
                    className="input tabular"
                    value={minSubtotal}
                    onChange={e => {
                      const raw = e.target.value.replace(/\D/g, "");
                      setMinSubtotal(raw ? maskBRL(raw) : "");
                    }}
                    placeholder="Vazio = sem mínimo"
                    inputMode="numeric"
                    style={{ textAlign: "right" }}
                    disabled={saving}
                  />
                </div>
              </div>
              <div className="field" style={{ marginBottom: 0 }}>
                <label className="label">Máximo de usos (total)</label>
                <input className="input tabular" value={maxUses}
                       onChange={e => setMaxUses(e.target.value.replace(/\D/g, ""))}
                       placeholder="Vazio = ilimitado"
                       inputMode="numeric"
                       disabled={saving}/>
                {isEdit && (
                  <div style={{ fontSize: 11, color: "var(--text-mute)", marginTop: 6 }}>
                    Já usado {coupon.usage_count} {coupon.usage_count === 1 ? "vez" : "vezes"}.
                  </div>
                )}
              </div>
            </div>
            <div className="form-grid" style={{ gridTemplateColumns: "1fr 1fr", gap: 14 }}>
              <div className="field" style={{ marginBottom: 0 }}>
                <label className="label">Usos por cliente</label>
                <input className="input tabular" value={maxUsesPerUser}
                       onChange={e => setMaxUsesPerUser(e.target.value.replace(/\D/g, ""))}
                       placeholder="Vazio = ilimitado"
                       inputMode="numeric"
                       disabled={saving}/>
                <div style={{ fontSize: 11, color: "var(--text-mute)", marginTop: 6 }}>
                  Quantas vezes o mesmo cliente pode usar este cupom.
                </div>
              </div>
              <div className="field" style={{ marginBottom: 0 }}>
                <label className="label">Restrições</label>
                <label style={{ display: "flex", alignItems: "center", gap: 8, cursor: "pointer", padding: "10px 12px", border: "1px solid var(--border, #eee)", borderRadius: 10, background: firstPurchaseOnly ? "var(--pink-50, #FFE4F0)" : "white" }}>
                  <input
                    type="checkbox"
                    checked={firstPurchaseOnly}
                    onChange={e => setFirstPurchaseOnly(e.target.checked)}
                    disabled={saving}
                    style={{ accentColor: "var(--pink-500)" }}
                  />
                  <span style={{ fontSize: 13, fontWeight: firstPurchaseOnly ? 600 : 500, color: firstPurchaseOnly ? "var(--pink-700, #A40D4D)" : "var(--text)" }}>
                    Apenas primeira compra online
                  </span>
                </label>
                <div style={{ fontSize: 11, color: "var(--text-mute)", marginTop: 6 }}>
                  Válido só se o cliente nunca comprou no site antes.
                </div>
              </div>
            </div>
            <div className="field" style={{ marginBottom: 0 }}>
              <label className="label">Observações internas (opcional)</label>
              <input className="input" value={notes}
                     onChange={e => setNotes(e.target.value)}
                     placeholder="Notas pra você — não aparece pro cliente"
                     disabled={saving}/>
            </div>
          </div>

          {error && (
            <div style={{
              fontSize: 13, color: "var(--pink-600)", fontWeight: 600,
              textAlign: "center", padding: "10px 12px",
              background: "var(--pink-50, #FFE4F0)", borderRadius: 8
            }}>
              {error}
            </div>
          )}
        </div>

        {/* Rodapé sticky com botões */}
        <div style={{
          position: "sticky", bottom: 0, background: "white",
          padding: "16px 24px", borderTop: "1px solid var(--border, #eee)",
          display: "flex", gap: 10, justifyContent: "flex-end",
        }}>
          <button className="btn btn-secondary" onClick={onClose} disabled={saving}>Cancelar</button>
          <button className="btn btn-primary" onClick={submit} disabled={saving}>
            <Icon name="check" size={14}/> {saving ? "Salvando…" : (isEdit ? "Atualizar" : "Criar cupom")}
          </button>
        </div>
      </div>
    </div>
  );
}

// Componentes auxiliares para o CouponModal — extraídos pra manter o layout limpo
function SectionLabel({ children }) {
  return (
    <div style={{
      fontSize: 11,
      fontWeight: 700,
      textTransform: "uppercase",
      letterSpacing: "0.08em",
      color: "var(--pink-600, #C8125F)",
    }}>
      {children}
    </div>
  );
}

function Divider() {
  return <div style={{ height: 1, background: "var(--border, #eee)", margin: "0 -24px" }}/>;
}

function Configuracoes() {
  const { user } = useApp();
  const isDono = user?.role === "dono";

  const [fees,    setFees]    = useS(null);     // { pix, debito, credito }
  const [loading, setLoading] = useS(true);
  const [error,   setError]   = useS(null);
  const [saving,  setSaving]  = useS(false);
  const [savedAt, setSavedAt] = useS(null);

  const FALLBACK = {
    pix:    { fee_pct: 0 },
    debito: { fee_pct: 0 },
    credito: { max_installments: 6, installments: [
      { n: 1, fee_pct: 0 }, { n: 2, fee_pct: 0 }, { n: 3, fee_pct: 0 },
      { n: 4, fee_pct: 0 }, { n: 5, fee_pct: 0 }, { n: 6, fee_pct: 0 },
    ]},
  };

  useE(() => {
    let alive = true;
    (async () => {
      try {
        const v = await dbGetAppSetting("payment_fees");
        if (!alive) return;
        setFees(v || FALLBACK);
      } catch (e) {
        if (!alive) return;
        setError(e?.message || String(e));
        setFees(FALLBACK);
      } finally {
        if (alive) setLoading(false);
      }
    })();
    return () => { alive = false; };
  }, []);

  if (loading || !fees) {
    return (
      <div className="screen">
        <PageHead title="Configurações" sub="Carregando..."/>
      </div>
    );
  }

  const setPixFee    = (pct) => setFees(f => ({ ...f, pix:    { ...(f.pix    || {}), fee_pct: pct } }));
  const setDebitoFee = (pct) => setFees(f => ({ ...f, debito: { ...(f.debito || {}), fee_pct: pct } }));

  // Aumenta/diminui o número máximo de parcelas — popula com taxa 0 quando cresce
  const setMaxInstallments = (n) => setFees(f => {
    const cur = Array.isArray(f.credito?.installments) ? f.credito.installments : [];
    const next = [];
    for (let i = 1; i <= n; i++) {
      const found = cur.find(x => Number(x.n) === i);
      next.push({ n: i, fee_pct: found ? Number(found.fee_pct) : 0 });
    }
    return { ...f, credito: { ...(f.credito || {}), max_installments: n, installments: next } };
  });

  const setInstFee = (n, pct) => setFees(f => {
    const cur = Array.isArray(f.credito?.installments) ? f.credito.installments : [];
    const next = cur.map(x => Number(x.n) === n ? { ...x, fee_pct: pct } : x);
    return { ...f, credito: { ...(f.credito || {}), installments: next } };
  });

  const onSave = async () => {
    if (saving) return;
    setSaving(true);
    setError(null);
    setSavedAt(null);
    try {
      // normaliza valores antes de mandar — números >= 0
      const norm = {
        pix:    { fee_pct: Math.max(0, Number(fees.pix?.fee_pct)    || 0) },
        debito: { fee_pct: Math.max(0, Number(fees.debito?.fee_pct) || 0) },
        credito: {
          max_installments: Math.max(1, Math.min(24, Number(fees.credito?.max_installments) || 1)),
          installments: (fees.credito?.installments || []).map(x => ({
            n: Number(x.n), fee_pct: Math.max(0, Number(x.fee_pct) || 0),
          })),
        },
      };
      await dbSetAppSetting("payment_fees", norm);
      setSavedAt(Date.now());
      setFees(norm);
    } catch (e) {
      setError(e?.message || "Falha ao salvar.");
    } finally {
      setSaving(false);
    }
  };

  const maxInst = Number(fees.credito?.max_installments) || 6;
  const installments = fees.credito?.installments || [];

  return (
    <div className="screen">
      <PageHead title="Configurações"
                sub="Taxas das maquininhas e opções de parcelamento"/>

      <Collapsible
        title="Taxas da Maquininha"
        sub="Percentual cobrado pela adquirente em cada transação. Usado nos relatórios de margem — o cliente continua pagando o total cheio."
        defaultOpen={false}>
        {/* Pix · Débito */}
        <div className="form-grid" style={{ gridTemplateColumns: "1fr 1fr" }}>
          <div className="field">
            <label className="label" style={{ display: "flex", alignItems: "center", gap: 8 }}>
              <Icon name="pix" size={14}/> Pix
            </label>
            <PctInput value={fees.pix?.fee_pct} onChange={setPixFee} disabled={saving}/>
          </div>

          <div className="field">
            <label className="label" style={{ display: "flex", alignItems: "center", gap: 8 }}>
              <Icon name="card" size={14}/> Débito
            </label>
            <PctInput value={fees.debito?.fee_pct} onChange={setDebitoFee} disabled={saving}/>
          </div>
        </div>

        {/* Crédito · parcelamento — subseção dentro da mesma taxa */}
        <div style={{
          marginTop: 20,
          paddingTop: 16,
          borderTop: "1px solid var(--border, #eee)"
        }}>
          <div style={{
            display: "flex",
            alignItems: "center",
            justifyContent: "space-between",
            gap: 12,
            marginBottom: 12,
            flexWrap: "wrap",
          }}>
            <div>
              <div style={{ display: "flex", alignItems: "center", gap: 8, fontWeight: 700 }}>
                <Icon name="card" size={14}/> Crédito · parcelamento
              </div>
              <div style={{ color: "var(--text-mute)", fontSize: 12, marginTop: 2 }}>
                Quantas parcelas o PDV vai oferecer e a taxa cobrada em cada uma.
              </div>
            </div>
            <div className="field" style={{ marginBottom: 0 }}>
              <label className="label">Máximo de parcelas</label>
              <select className="input tabular"
                      value={maxInst}
                      onChange={e => setMaxInstallments(Math.max(1, Math.min(24, Number(e.target.value))))}
                      disabled={saving}
                      style={{ width: 100 }}>
                {Array.from({ length: 24 }).map((_, i) => (
                  <option key={i+1} value={i+1}>{i+1}x</option>
                ))}
              </select>
            </div>
          </div>

          <div style={{ overflowX: "auto" }}>
            <table className="table" style={{ minWidth: 320 }}>
              <thead>
                <tr>
                  <th style={{ textAlign: "center", width: 100 }}>Parcelas</th>
                  <th style={{ textAlign: "center", width: 140 }}>Taxa cobrada</th>
                  <th style={{ textAlign: "left" }}>Observação</th>
                </tr>
              </thead>
              <tbody>
                {installments.map(x => (
                  <tr key={x.n}>
                    <td style={{ textAlign: "center", fontWeight: 700, fontFamily: "var(--font-mono)" }}>
                      {x.n}x
                    </td>
                    <td style={{ textAlign: "center" }}>
                      <div style={{ display: "inline-flex" }}>
                        <PctInput value={x.fee_pct}
                                  onChange={(v) => setInstFee(x.n, v)}
                                  disabled={saving}/>
                      </div>
                    </td>
                    <td style={{ color: "var(--text-mute)", fontSize: 12 }}>
                      {x.n === 1
                        ? "à vista no crédito"
                        : (Number(x.fee_pct) > Number(installments[0]?.fee_pct || 0)
                            ? "com juros — diferença vs. 1x: " + (Number(x.fee_pct) - Number(installments[0]?.fee_pct || 0)).toFixed(2) + "%"
                            : "sem juros adicional")}
                    </td>
                  </tr>
                ))}
              </tbody>
            </table>
          </div>
        </div>
      </Collapsible>

      {/* Cupons de desconto — aplicados no site (e-commerce). */}
      <Collapsible
        title="Cupons de desconto"
        sub="Códigos promocionais aplicados pelo cliente no site. Configure validade, categorias e limite de usos."
        defaultOpen={false}>
        <CouponsAdminPanel/>
      </Collapsible>

      {/* Edição de usuários — só dono. */}
      {isDono && (
        <Collapsible
          title="Usuários"
          sub="Cargos, permissões e PIN dos usuários do PDV. Apenas donos podem editar."
          defaultOpen={false}>
          <UsersAdminPanel/>
        </Collapsible>
      )}

      {/* Aparência — só dono. Cor primária aplicada pra todos. */}
      {isDono && (
        <Collapsible
          title="Aparência"
          sub="Cor primária do sistema (afeta botões, badges, cabeçalhos e gráficos). Apenas donos podem alterar."
          defaultOpen={false}>
          <AppearancePanel/>
        </Collapsible>
      )}

      {/* Backup local — disponível pra qualquer usuário com acesso a Configurações */}
      <Collapsible
        title="Backup"
        sub="Salvar um snapshot completo do banco em um arquivo JSON local."
        defaultOpen={false}>
        <BackupPanel/>
      </Collapsible>

      {error && (
        <div style={{ padding: 12, color: "var(--pink-600)", fontWeight: 600, marginBottom: 12 }}>
          {error}
        </div>
      )}
      {savedAt && (
        <div style={{ padding: 12, color: "var(--ok-600)", fontWeight: 600, marginBottom: 12 }}>
          Configurações salvas.
        </div>
      )}

      <div style={{ display: "flex", gap: 10, justifyContent: "flex-end" }}>
        <button className="btn btn-primary btn-lg" onClick={onSave} disabled={saving}>
          <Icon name="check" size={16}/> {saving ? "Salvando..." : "Salvar configurações"}
        </button>
      </div>
    </div>
  );
}


/* ════════════════════════════════════════════════════════════
   CURSOS — turmas, matrículas (alunos), mensalidades, comissão.
   Pagamento de mensalidade cria sale com origin='curso' (entra
   no caixa mas separa nos relatórios).
   ════════════════════════════════════════════════════════════ */

const WEEKDAY_NAMES_SHORT = ["Dom", "Seg", "Ter", "Qua", "Qui", "Sex", "Sáb"];

const fmtWeekdays = (arr) => {
  if (!Array.isArray(arr) || arr.length === 0) return "—";
  return arr.slice().sort((a, b) => a - b).map(d => WEEKDAY_NAMES_SHORT[d]).join(", ");
};
const fmtTimeRange = (s, e) => {
  const t = (x) => x ? String(x).slice(0, 5) : "";
  if (!s && !e) return "";
  return `${t(s)}${t(e) ? "–" + t(e) : ""}`;
};
const fmtMonthLabel = (iso) => {
  if (!iso) return "";
  const [y, m] = String(iso).slice(0, 7).split("-");
  const names = ["Janeiro","Fevereiro","Março","Abril","Maio","Junho",
                 "Julho","Agosto","Setembro","Outubro","Novembro","Dezembro"];
  return `${names[Number(m) - 1]} / ${y}`;
};
const currentMonthISO = () => {
  const d = new Date();
  return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-01`;
};
const shiftMonthISO = (iso, delta) => {
  const [y, m] = iso.slice(0, 7).split("-").map(Number);
  const d = new Date(y, m - 1 + delta, 1);
  return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-01`;
};

function Cursos() {
  const { user, cashSession } = useApp();

  const [courses,       setCourses]       = useS(null);   // null = carregando
  const [enrollments,   setEnrollments]   = useS(null);
  const [monthlyStatus, setMonthlyStatus] = useS(null);
  const [customers,     setCustomers]     = useS([]);
  const [suppliers,     setSuppliers]     = useS([]);
  const [loadError,     setLoadError]     = useS(null);

  // Filtros persistidos
  const [statusView,      setStatusView]      = useStickyState("pdv:cursos.statusView",   "ativo");
  const [enrCourseFilter, setEnrCourseFilter] = useStickyState("pdv:cursos.enrCourse",    "all");
  const [enrStatusFilter, setEnrStatusFilter] = useStickyState("pdv:cursos.enrStatus",    "ativo");
  const [monthDate,       setMonthDate]       = useStickyState("pdv:cursos.month",        currentMonthISO());
  const [mCourseFilter,   setMCourseFilter]   = useStickyState("pdv:cursos.mCourse",      "all");
  const [mStatusFilter,   setMStatusFilter]   = useStickyState("pdv:cursos.mStatus",      "all");

  // Modais
  const [editingCourse,   setEditingCourse]   = useS(null);   // course ou "new" ou null
  const [editingEnrol,    setEditingEnrol]    = useS(null);   // "new" ou null (matrícula simples)
  const [payingFor,       setPayingFor]       = useS(null);   // row de monthlyStatus
  const [confirmLock,     setConfirmLock]     = useS(null);   // matrícula que vai ser trancada
  const [confirmError,    setConfirmError]    = useS(null);

  const reloadAll = async () => {
    try {
      const [cs, sup, cust] = await Promise.all([
        dbListCourses(statusView === "all" ? "all" : statusView),
        dbListSuppliers().catch(() => []),
        dbListCustomers().catch(() => []),
      ]);
      setCourses(cs);
      setSuppliers(sup);
      setCustomers(cust);
      setLoadError(null);
    } catch (e) {
      setLoadError(e?.message || String(e));
      setCourses([]);
    }
  };
  useE(() => { reloadAll(); }, [statusView]);

  const reloadEnrollments = async () => {
    try {
      const list = await dbListEnrollments({
        courseId: enrCourseFilter === "all" ? null : enrCourseFilter,
        status:   enrStatusFilter,
      });
      setEnrollments(list);
    } catch (e) {
      setLoadError(e?.message || String(e));
      setEnrollments([]);
    }
  };
  useE(() => { reloadEnrollments(); }, [enrCourseFilter, enrStatusFilter]);

  const reloadMonthly = async () => {
    try {
      const list = await dbListMonthlyStatus(monthDate);
      setMonthlyStatus(list);
    } catch (e) {
      setLoadError(e?.message || String(e));
      setMonthlyStatus([]);
    }
  };
  useE(() => { reloadMonthly(); }, [monthDate]);

  const monthFiltered = (monthlyStatus || []).filter(r => {
    if (mCourseFilter !== "all" && r.course_id !== mCourseFilter) return false;
    if (mStatusFilter === "pago"     && !r.is_paid) return false;
    if (mStatusFilter === "pendente" &&  r.is_paid) return false;
    return true;
  });

  const totalPendingValue = monthFiltered.reduce((s, r) => s + (r.is_paid ? 0 : Number(r.effective_value || 0)), 0);
  const totalPaidValue    = monthFiltered.reduce((s, r) => s + (r.is_paid ? Number(r.effective_value || 0) : 0), 0);

  return (
    <div className="screen">
      <PageHead title="Cursos"
        sub={courses == null
              ? "Carregando..."
              : `${(courses || []).length} cursos · ${(enrollments || []).filter(e => e.status === "ativo").length} matrículas ativas`}
        actions={<>
          <button className="btn btn-primary" onClick={() => setEditingCourse("new")}>
            <Icon name="plus" size={14}/> Novo curso
          </button>
        </>}/>

      {loadError && (
        <div className="card card-pad" style={{ marginBottom: 16, borderColor: "var(--pink-200)" }}>
          <div style={{ color: "var(--pink-600)", fontWeight: 600 }}>Erro</div>
          <div style={{ fontSize: 12, color: "var(--text-mute)", marginTop: 4 }}>{loadError}</div>
        </div>
      )}

      {/* ─── 1. Cursos cadastrados ───────────────────────────────── */}
      <Collapsible title="Cursos cadastrados"
                   sub="Turmas, professores (fornecedores), dias da semana, mensalidade e modo de comissão."
                   defaultOpen={true}>
        <div className="filter-bar" style={{ marginBottom: 12 }}>
          <div className="seg-toggle">
            <button className={statusView === "ativo"     ? "active" : ""} onClick={() => setStatusView("ativo")}>Ativos</button>
            <button className={statusView === "encerrado" ? "active" : ""} onClick={() => setStatusView("encerrado")}>Encerrados</button>
            <button className={statusView === "all"       ? "active" : ""} onClick={() => setStatusView("all")}>Todos</button>
          </div>
        </div>

        {courses === null ? (
          <div style={{ padding: 40, textAlign: "center", color: "var(--text-mute)" }}>Carregando cursos...</div>
        ) : (courses.length === 0) ? (
          <div style={{ padding: 40, textAlign: "center", color: "var(--text-mute)" }}>
            Nenhum curso {statusView === "encerrado" ? "encerrado" : statusView === "all" ? "cadastrado" : "ativo"}.
          </div>
        ) : (
          <div className="cursos-cards-grid">
            {courses.map(c => (
              <button key={c.id} type="button"
                      onClick={() => setEditingCourse(c)}
                      className="card card-pad"
                      style={{ textAlign: "left", cursor: "pointer", border: "1px solid var(--border)",
                               background: c.status === "encerrado" ? "var(--bg-soft, #FAFAFA)" : "var(--bg)" }}>
                <div style={{ display: "flex", justifyContent: "space-between", alignItems: "flex-start", gap: 8 }}>
                  <div style={{ fontWeight: 700, fontSize: 15, color: "var(--text)" }}>{c.name}</div>
                  {c.status === "encerrado" && (
                    <span className="badge" style={{ background: "var(--bg-soft)", color: "var(--text-mute)" }}>encerrado</span>
                  )}
                </div>
                <div style={{ fontSize: 12, color: "var(--text-mute)", marginTop: 4 }}>
                  {c.teacher_name || <span style={{ fontStyle: "italic" }}>Sem professor</span>}
                </div>
                <div style={{ fontSize: 12, color: "var(--text-soft)", marginTop: 8, display: "flex", gap: 12 }}>
                  <span><Icon name="receipt" size={12}/> {fmtWeekdays(c.weekdays)}</span>
                  {fmtTimeRange(c.start_time, c.end_time) && <span>{fmtTimeRange(c.start_time, c.end_time)}</span>}
                </div>
                <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", marginTop: 12 }}>
                  <div style={{ fontWeight: 700, fontSize: 16, color: "var(--pink-600)" }}>{BRL(Number(c.monthly_price) || 0)}</div>
                  <div style={{ fontSize: 12, color: "var(--text-mute)" }}>
                    <Icon name="user" size={12}/> {Number(c.active_students) || 0} aluno{(Number(c.active_students)||0) === 1 ? "" : "s"}
                  </div>
                </div>
                <div style={{ fontSize: 11, color: "var(--text-mute)", marginTop: 6 }}>
                  Comissão: {c.commission_mode === "pct"
                              ? `${Number(c.commission_value) || 0}% da receita`
                              : `${BRL(Number(c.commission_value) || 0)} por aluno`}
                </div>
              </button>
            ))}
          </div>
        )}
      </Collapsible>

      {/* ─── 2. Matrículas ───────────────────────────────────────── */}
      <Collapsible title="Matrículas"
                   sub="Alunos (clientes) vinculados a cursos. Override de valor opcional por matrícula.">
        <div className="filter-bar" style={{ marginBottom: 12 }}>
          <select className="input" value={enrCourseFilter} onChange={e => setEnrCourseFilter(e.target.value)}>
            <option value="all">Todos os cursos</option>
            {(courses || []).map(c => <option key={c.id} value={c.id}>{c.name}</option>)}
          </select>
          <div className="seg-toggle">
            <button className={enrStatusFilter === "ativo"     ? "active" : ""} onClick={() => setEnrStatusFilter("ativo")}>Ativos</button>
            <button className={enrStatusFilter === "trancado"  ? "active" : ""} onClick={() => setEnrStatusFilter("trancado")}>Trancados</button>
            <button className={enrStatusFilter === "cancelado" ? "active" : ""} onClick={() => setEnrStatusFilter("cancelado")}>Cancelados</button>
            <button className={enrStatusFilter === "all"       ? "active" : ""} onClick={() => setEnrStatusFilter("all")}>Todos</button>
          </div>
          <button className="btn btn-primary"
                  onClick={() => setEditingEnrol("new")}
                  disabled={(courses || []).length === 0 || customers.length === 0}>
            <Icon name="plus" size={14}/> Nova matrícula
          </button>
        </div>

        {enrollments === null ? (
          <div style={{ padding: 40, textAlign: "center", color: "var(--text-mute)" }}>Carregando matrículas...</div>
        ) : enrollments.length === 0 ? (
          <div style={{ padding: 40, textAlign: "center", color: "var(--text-mute)" }}>Nenhuma matrícula nesse filtro.</div>
        ) : (
          <div className="table-x-scroll">
            <table className="table">
              <thead>
                <tr>
                  <th style={{ textAlign: "left" }}>Aluno</th>
                  <th style={{ textAlign: "left" }}>Curso</th>
                  <th style={{ textAlign: "center", width: 130 }}>Valor mensal</th>
                  <th style={{ textAlign: "center", width: 130 }}>Matriculado</th>
                  <th style={{ textAlign: "center", width: 110 }}>Status</th>
                  <th style={{ textAlign: "center", width: 110 }}>Ações</th>
                </tr>
              </thead>
              <tbody>
                {enrollments.map(e => (
                  <tr key={e.id}>
                    <td>
                      <div style={{ fontWeight: 600 }}>{e.customer_name}</div>
                      {e.customer_phone && <div style={{ fontSize: 11, color: "var(--text-mute)" }}>{e.customer_phone}</div>}
                    </td>
                    <td>{e.course_name}</td>
                    <td className="tabular" style={{ textAlign: "center" }}>
                      <div style={{ fontWeight: 600 }}>{BRL(Number(e.effective_value) || 0)}</div>
                      {e.monthly_value != null && (
                        <div style={{ fontSize: 10, color: "var(--text-mute)" }}>override</div>
                      )}
                    </td>
                    <td className="tabular" style={{ textAlign: "center", color: "var(--text-soft)" }}>
                      {e.enrolled_at ? new Date(e.enrolled_at + "T12:00:00").toLocaleDateString("pt-BR") : "—"}
                    </td>
                    <td style={{ textAlign: "center" }}>
                      {e.status === "ativo"
                        ? <span className="badge badge-ok"><span className="dot"/>ativo</span>
                        : e.status === "trancado"
                          ? <span className="badge badge-warn"><span className="dot"/>trancado</span>
                          : <span className="badge" style={{ background: "var(--bg-soft)", color: "var(--text-mute)" }}>cancelado</span>}
                    </td>
                    <td style={{ textAlign: "center" }}>
                      {e.status === "ativo" ? (
                        <button className="btn-link"
                                onClick={() => { setConfirmError(null); setConfirmLock(e); }}>
                          Trancar
                        </button>
                      ) : (
                        <button className="btn-link"
                                onClick={async () => {
                                  try {
                                    await dbUpdateEnrollment({ id: e.id, monthlyValue: e.monthly_value, status: "ativo", notes: e.notes });
                                    reloadEnrollments(); reloadAll(); reloadMonthly();
                                  } catch (err) { alert(err.message || String(err)); }
                                }}>Reativar</button>
                      )}
                    </td>
                  </tr>
                ))}
              </tbody>
            </table>
          </div>
        )}
      </Collapsible>

      {/* ─── 3. Mensalidades do mês ───────────────────────────────── */}
      <Collapsible title="Mensalidades do mês"
                   sub="Controle de pagamento por aluno × curso. Clique em 'Lançar pagamento' pra registrar (entra no caixa)."
                   defaultOpen={true}>
        <div className="filter-bar" style={{ marginBottom: 12, alignItems: "center" }}>
          <div className="cursos-month-nav">
            <button className="btn-icon-mini" onClick={() => setMonthDate(shiftMonthISO(monthDate, -1))}>
              <Icon name="arrowL" size={16}/>
            </button>
            <div className="cursos-month-label">{fmtMonthLabel(monthDate)}</div>
            <button className="btn-icon-mini" onClick={() => setMonthDate(shiftMonthISO(monthDate, 1))}>
              <Icon name="arrowR" size={16}/>
            </button>
          </div>
          <select className="input" value={mCourseFilter} onChange={e => setMCourseFilter(e.target.value)} style={{ flex: 1, maxWidth: 260 }}>
            <option value="all">Todos os cursos</option>
            {(courses || []).filter(c => c.status === "ativo").map(c => (
              <option key={c.id} value={c.id}>{c.name}</option>
            ))}
          </select>
          <div className="seg-toggle">
            <button className={mStatusFilter === "all"      ? "active" : ""} onClick={() => setMStatusFilter("all")}>Todos</button>
            <button className={mStatusFilter === "pendente" ? "active" : ""} onClick={() => setMStatusFilter("pendente")}>Pendente</button>
            <button className={mStatusFilter === "pago"     ? "active" : ""} onClick={() => setMStatusFilter("pago")}>Pago</button>
          </div>
        </div>

        <div className="stats-grid" style={{ marginBottom: 12 }}>
          <Stat label="Matrículas no mês"  value={monthFiltered.length}      hint={`ativas em ${fmtMonthLabel(monthDate).split(" / ")[0]}`}/>
          <Stat label="Pago"               value={BRL(totalPaidValue)}        hint={`${monthFiltered.filter(r => r.is_paid).length} mensalidade(s) recebidas`} accent/>
          <Stat label="Pendente"           value={BRL(totalPendingValue)}     hint={`${monthFiltered.filter(r => !r.is_paid).length} ainda não pagou`}
                trendDir={totalPendingValue > 0 ? "dn" : null}/>
        </div>

        {monthlyStatus === null ? (
          <div style={{ padding: 40, textAlign: "center", color: "var(--text-mute)" }}>Carregando mensalidades...</div>
        ) : monthFiltered.length === 0 ? (
          <div style={{ padding: 40, textAlign: "center", color: "var(--text-mute)" }}>
            Nenhuma matrícula ativa nesse mês {mCourseFilter !== "all" ? "pra esse curso" : ""}.
          </div>
        ) : (
          <div className="table-x-scroll">
            <table className="table">
              <thead>
                <tr>
                  <th style={{ textAlign: "left" }}>Aluno</th>
                  <th style={{ textAlign: "left" }}>Curso</th>
                  <th style={{ textAlign: "center", width: 130 }}>Valor</th>
                  <th style={{ textAlign: "center", width: 130 }}>Pago em</th>
                  <th style={{ textAlign: "center", width: 110 }}>Status</th>
                  <th style={{ textAlign: "center", width: 170 }}>Ações</th>
                </tr>
              </thead>
              <tbody>
                {monthFiltered.map(r => (
                  <tr key={r.enrollment_id}>
                    <td>
                      <div style={{ fontWeight: 600 }}>{r.customer_name}</div>
                      {r.customer_phone && <div style={{ fontSize: 11, color: "var(--text-mute)" }}>{r.customer_phone}</div>}
                    </td>
                    <td>{r.course_name}</td>
                    <td className="tabular" style={{ textAlign: "center", fontWeight: 600 }}>{BRL(Number(r.effective_value) || 0)}</td>
                    <td className="tabular" style={{ textAlign: "center", color: "var(--text-soft)" }}>
                      {r.is_paid && r.paid_at ? new Date(r.paid_at).toLocaleDateString("pt-BR") : "—"}
                    </td>
                    <td style={{ textAlign: "center" }}>
                      {r.is_paid
                        ? <span className="badge badge-ok"><span className="dot"/>pago</span>
                        : <span className="badge badge-warn"><span className="dot"/>pendente</span>}
                    </td>
                    <td style={{ textAlign: "center" }}>
                      {r.is_paid ? (
                        <span style={{ fontSize: 11, color: "var(--text-mute)" }}>#{r.sale_number}</span>
                      ) : (
                        <button className="btn btn-primary"
                                style={{ padding: "4px 10px", fontSize: 12 }}
                                onClick={() => setPayingFor(r)}
                                disabled={!cashSession}>
                          Lançar pagamento
                        </button>
                      )}
                    </td>
                  </tr>
                ))}
              </tbody>
            </table>
          </div>
        )}
        {!cashSession && monthFiltered.some(r => !r.is_paid) && (
          <div style={{ marginTop: 12, fontSize: 12, color: "var(--warn-600, #B45309)" }}>
            Caixa não está aberto — não é possível lançar pagamentos. Abra o caixa primeiro.
          </div>
        )}
      </Collapsible>

      {editingCourse && (
        <CourseEditor
          initial={editingCourse === "new" ? null : editingCourse}
          suppliers={suppliers}
          onClose={() => setEditingCourse(null)}
          onSaved={() => { setEditingCourse(null); reloadAll(); reloadMonthly(); }}/>
      )}

      {editingEnrol === "new" && (
        <EnrollmentEditor
          courses={(courses || []).filter(c => c.status === "ativo")}
          customers={customers}
          onClose={() => setEditingEnrol(null)}
          onSaved={() => { setEditingEnrol(null); reloadEnrollments(); reloadAll(); reloadMonthly(); }}/>
      )}

      {payingFor && (
        <CoursePaymentModal
          enrollment={payingFor}
          monthDate={monthDate}
          session={cashSession}
          cashierId={user?.id}
          onClose={() => setPayingFor(null)}
          onPaid={() => { setPayingFor(null); reloadMonthly(); }}/>
      )}

      {confirmLock && (
        <ConfirmModal
          title="Trancar matrícula?"
          message={<>
            A matrícula de <b>{confirmLock.customer_name}</b> em <b>{confirmLock.course_name}</b> será trancada.
            <div style={{ fontSize: 12, color: "var(--text-mute)", marginTop: 8 }}>
              O aluno deixa de aparecer em "Mensalidades do mês" e nas listas de matrículas ativas.
              Você pode reativar a qualquer momento.
            </div>
          </>}
          confirmLabel="Trancar matrícula"
          danger
          error={confirmError}
          onCancel={() => { setConfirmLock(null); setConfirmError(null); }}
          onConfirm={async () => {
            try {
              await dbUpdateEnrollment({
                id: confirmLock.id,
                monthlyValue: confirmLock.monthly_value,
                status: "trancado",
                notes: confirmLock.notes,
              });
              setConfirmLock(null);
              setConfirmError(null);
              reloadEnrollments(); reloadAll(); reloadMonthly();
            } catch (err) {
              setConfirmError(err?.message || String(err));
            }
          }}/>
      )}
    </div>
  );
}

/* ─── Editor de curso (modal) ───────────────────────────────────── */
function CourseEditor({ initial, suppliers, onClose, onSaved }) {
  const isNew = !initial;
  const [name,             setName]           = useS(initial?.name || "");
  const [description,      setDescription]    = useS(initial?.description || "");
  const [monthlyPrice,     setMonthlyPrice]   = useS(initial ? String(Number(initial.monthly_price) || 0).replace(".", ",") : "0,00");
  const [weekdays,         setWeekdays]       = useS(initial?.weekdays || []);
  const [startTime,        setStartTime]      = useS(initial?.start_time?.slice(0, 5) || "");
  const [endTime,          setEndTime]        = useS(initial?.end_time?.slice(0, 5)   || "");
  const [teacherId,        setTeacherId]      = useS(initial?.teacher_supplier_id || "");
  const [commissionMode,   setCommissionMode] = useS(initial?.commission_mode || "pct");
  const [commissionValue,  setCommissionValue]= useS(initial ? String(Number(initial.commission_value) || 0).replace(".", ",") : "0");
  const [maxStudents,      setMaxStudents]    = useS(initial?.max_students ?? "");
  const [status,           setStatus]         = useS(initial?.status || "ativo");
  const [notes,            setNotes]          = useS(initial?.notes || "");
  const [saving,           setSaving]         = useS(false);
  const [error,            setError]          = useS(null);

  const toggleDay = (d) => {
    setWeekdays(prev => prev.includes(d) ? prev.filter(x => x !== d) : [...prev, d].sort((a, b) => a - b));
  };

  const onSubmit = async (e) => {
    e?.preventDefault?.();
    if (!name.trim()) { setError("Informe o nome do curso."); return; }
    setSaving(true); setError(null);
    try {
      const payload = {
        name: name.trim(),
        description: description.trim() || null,
        monthly_price: parseBRL(monthlyPrice),
        weekdays: weekdays.slice(),
        start_time: startTime || null,
        end_time:   endTime   || null,
        teacher_id: teacherId || null,
        commission_mode: commissionMode,
        commission_value: parseBRL(commissionValue),
        max_students: maxStudents === "" ? null : Number(maxStudents),
        status,
        notes: notes.trim() || null,
      };
      if (isNew) await dbCreateCourse(payload);
      else       await dbUpdateCourse({ id: initial.id, ...payload });
      onSaved();
    } catch (err) {
      setError(err.message || String(err));
    } finally {
      setSaving(false);
    }
  };

  return (
    <div className="modal-backdrop">
      <div className="modal" style={{ width: 560 }} onClick={e => e.stopPropagation()} role="dialog" aria-modal="true">
        <div className="modal-head">
          <h2 style={{ margin: 0, fontSize: 20, fontWeight: 700 }}>{isNew ? "Novo curso" : "Editar curso"}</h2>
          <button className="btn-icon-mini" onClick={onClose} aria-label="Fechar"><Icon name="close" size={18}/></button>
        </div>

        <form onSubmit={onSubmit}>
          <div className="form-grid" style={{ gridTemplateColumns: "1fr" }}>
            <div className="field">
              <label className="label">Nome do curso *</label>
              <input className="input" value={name} onChange={e => setName(e.target.value)} autoFocus/>
            </div>

            <div className="field">
              <label className="label">Professor (Fornecedor)</label>
              <select className="input" value={teacherId} onChange={e => setTeacherId(e.target.value)}>
                <option value="">Escolher professor</option>
                {suppliers.filter(s => s.status === "ativo").map(s => (
                  <option key={s.id} value={s.id}>{s.name}</option>
                ))}
              </select>
            </div>

            <div className="field">
              <label className="label">Dias da semana</label>
              <div style={{ display: "flex", gap: 6, flexWrap: "wrap" }}>
                {WEEKDAY_NAMES_SHORT.map((lbl, d) => {
                  const on = weekdays.includes(d);
                  return (
                    <button key={d} type="button"
                            onClick={() => toggleDay(d)}
                            className={"btn " + (on ? "btn-primary" : "btn-secondary")}
                            style={{ padding: "6px 12px", minWidth: 56 }}>
                      {lbl}
                    </button>
                  );
                })}
              </div>
            </div>

            <div className="cursos-form-2col">
              <div className="field">
                <label className="label">Início</label>
                <input className="input" type="time" value={startTime} onChange={e => setStartTime(e.target.value)}/>
              </div>
              <div className="field">
                <label className="label">Fim</label>
                <input className="input" type="time" value={endTime} onChange={e => setEndTime(e.target.value)}/>
              </div>
            </div>

            <div className="cursos-form-2col">
              <div className="field">
                <label className="label">Mensalidade (R$)</label>
                <input className="input tabular" value={monthlyPrice}
                       onChange={e => setMonthlyPrice(maskBRL(e.target.value))}/>
              </div>
              <div className="field">
                <label className="label">Capacidade máxima</label>
                <input className="input" type="number" min="1" placeholder="ilimitado"
                       value={maxStudents} onChange={e => setMaxStudents(e.target.value)}/>
              </div>
            </div>

            <div className="field">
              <label className="label">Comissão do professor</label>
              <div className="cursos-commission-row">
                <div className="seg-toggle">
                  <button type="button" className={commissionMode === "pct"   ? "active" : ""} onClick={() => setCommissionMode("pct")}>% da receita</button>
                  <button type="button" className={commissionMode === "fixed" ? "active" : ""} onClick={() => setCommissionMode("fixed")}>R$ por aluno</button>
                </div>
                <input className="input tabular"
                       value={commissionValue}
                       onChange={e => setCommissionValue(commissionMode === "pct"
                         ? e.target.value.replace(/[^\d.,]/g, "")
                         : maskBRL(e.target.value))}/>
              </div>
              <div style={{ fontSize: 11, color: "var(--text-mute)", marginTop: 4 }}>
                {commissionMode === "pct"
                  ? "Ex: 50 → professor recebe 50% do total pago pelos alunos no mês."
                  : "Ex: 80 → professor recebe R$ 80 por aluno que pagar."}
              </div>
            </div>

            {!isNew && (
              <div className="field">
                <label className="label">Status</label>
                <div className="seg-toggle">
                  <button type="button" className={status === "ativo"     ? "active" : ""} onClick={() => setStatus("ativo")}>Ativo</button>
                  <button type="button" className={status === "encerrado" ? "active" : ""} onClick={() => setStatus("encerrado")}>Encerrado</button>
                </div>
              </div>
            )}

            <div className="field">
              <label className="label">Descrição</label>
              <textarea className="input" rows={2} value={description} onChange={e => setDescription(e.target.value)}/>
            </div>

            <div className="field">
              <label className="label">Notas internas</label>
              <textarea className="input" rows={2} value={notes} onChange={e => setNotes(e.target.value)}/>
            </div>

            {error && <div style={{ color: "var(--pink-600)", fontSize: 13 }}>{error}</div>}
          </div>

          <div style={{ display: "flex", justifyContent: "flex-end", gap: 8, marginTop: 16 }}>
            <button type="button" className="btn btn-secondary" onClick={onClose} disabled={saving}>Cancelar</button>
            <button type="submit" className="btn btn-primary" disabled={saving}>
              {saving ? "Salvando..." : (isNew ? "Criar curso" : "Salvar alterações")}
            </button>
          </div>
        </form>
      </div>
    </div>
  );
}

/* ─── Nova matrícula (modal simples) ───────────────────────────── */
function EnrollmentEditor({ courses, customers, onClose, onSaved }) {
  const [courseId,   setCourseId]   = useS(courses[0]?.id || "");
  const [customerId, setCustomerId] = useS("");
  const [overrideVal,setOverrideVal]= useS("");
  const [useOverride,setUseOverride]= useS(false);
  const [notes,      setNotes]      = useS("");
  const [saving,     setSaving]     = useS(false);
  const [error,      setError]      = useS(null);
  const [q,          setQ]          = useS("");

  const course   = courses.find(c => c.id === courseId);
  const customer = customers.find(c => c.id === customerId);
  const activeCustomers = customers.filter(c => c.status === "ativo");

  const filteredCust = activeCustomers
    .filter(c => !q || (c.name || "").toLowerCase().includes(q.toLowerCase())
                    || (c.phone || "").includes(q))
    .slice(0, 30);

  const defaultMonthly = Number(course?.monthly_price) || 0;
  const effectiveValue = useOverride && overrideVal ? parseBRL(overrideVal) : defaultMonthly;

  const onSubmit = async (e) => {
    e?.preventDefault?.();
    if (!courseId || !customerId) { setError("Selecione curso e aluno."); return; }
    setSaving(true); setError(null);
    try {
      await dbCreateEnrollment({
        courseId,
        customerId,
        monthlyValue: useOverride && overrideVal ? parseBRL(overrideVal) : null,
        notes: notes.trim() || null,
      });
      onSaved();
    } catch (err) {
      setError(err.message || String(err));
    } finally {
      setSaving(false);
    }
  };

  // Cor base pra os passos: pink-500 quando "ok", text-mute quando "vazio".
  const stepColor = (done) => done ? "var(--pink-500)" : "var(--text-mute)";
  const StepBadge = ({ n, done }) => (
    <span style={{
      display: "inline-flex", alignItems: "center", justifyContent: "center",
      width: 22, height: 22, borderRadius: 999,
      background: done ? "var(--pink-500)" : "var(--bg-soft)",
      color: done ? "white" : "var(--text-mute)",
      fontSize: 12, fontWeight: 700, flexShrink: 0,
    }}>{done ? <Icon name="check" size={12}/> : n}</span>
  );

  return (
    <div className="modal-backdrop">
      <div className="modal" style={{ width: 600, maxHeight: "90vh", display: "flex", flexDirection: "column" }}
           onClick={e => e.stopPropagation()} role="dialog" aria-modal="true">
        <div className="modal-head" style={{ flexShrink: 0 }}>
          <div>
            <h2 style={{ margin: 0, fontSize: 20, fontWeight: 700 }}>Nova matrícula</h2>
            <div style={{ fontSize: 12, color: "var(--text-mute)", marginTop: 4 }}>
              Vincule um aluno (cliente cadastrado) a um curso ativo.
            </div>
          </div>
          <button className="btn-icon-mini" onClick={onClose} aria-label="Fechar"><Icon name="close" size={18}/></button>
        </div>

        <form onSubmit={onSubmit} style={{ overflow: "auto", flex: 1, paddingRight: 4 }}>
          {/* ─── Passo 1: Curso ─── */}
          <div style={{ marginBottom: 20 }}>
            <div style={{ display: "flex", alignItems: "center", gap: 8, marginBottom: 8 }}>
              <StepBadge n={1} done={!!courseId}/>
              <label className="label" style={{ margin: 0, color: stepColor(!!courseId) }}>Escolha o curso</label>
            </div>
            <select className="input" value={courseId} onChange={e => setCourseId(e.target.value)}>
              <option value="">— selecione um curso —</option>
              {courses.map(c => (
                <option key={c.id} value={c.id}>
                  {c.name} — {BRL(Number(c.monthly_price) || 0)}/mês
                </option>
              ))}
            </select>

            {course && (
              <div style={{
                marginTop: 10, padding: 12, borderRadius: 8,
                background: "var(--pink-50, #FFE1EC)",
                border: "1px solid var(--pink-200)",
              }}>
                <div style={{ fontWeight: 700, fontSize: 15 }}>{course.name}</div>
                <div style={{ fontSize: 12, color: "var(--text-soft)", marginTop: 4 }}>
                  Professor: {course.teacher_name || <span style={{ fontStyle: "italic" }}>sem professor</span>}
                </div>
                <div style={{ display: "flex", gap: 12, marginTop: 8, fontSize: 12, color: "var(--text-soft)" }}>
                  <span><b>Dias:</b> {fmtWeekdays(course.weekdays)}</span>
                  {fmtTimeRange(course.start_time, course.end_time) && (
                    <span><b>Horário:</b> {fmtTimeRange(course.start_time, course.end_time)}</span>
                  )}
                </div>
                <div style={{ marginTop: 8, fontSize: 18, fontWeight: 700, color: "var(--pink-600)" }}>
                  {BRL(defaultMonthly)} <span style={{ fontSize: 12, fontWeight: 500, color: "var(--text-mute)" }}>/ mês</span>
                </div>
              </div>
            )}
          </div>

          {/* ─── Passo 2: Aluno ─── */}
          <div style={{ marginBottom: 20 }}>
            <div style={{ display: "flex", alignItems: "center", gap: 8, marginBottom: 8 }}>
              <StepBadge n={2} done={!!customerId}/>
              <label className="label" style={{ margin: 0, color: stepColor(!!customerId) }}>Escolha o aluno</label>
            </div>

            {activeCustomers.length === 0 ? (
              <div style={{
                padding: 16, textAlign: "center",
                background: "var(--bg-soft)", borderRadius: 8,
                color: "var(--text-soft)", fontSize: 13,
              }}>
                Nenhum cliente ativo cadastrado.<br/>
                <span style={{ color: "var(--text-mute)", fontSize: 12 }}>
                  Cadastre o aluno em <b>Clientes</b> antes de matricular.
                </span>
              </div>
            ) : (
              <>
                <div className="input-icon" style={{ marginBottom: 8 }}>
                  <Icon name="search" size={14}/>
                  <input className="input" placeholder="Buscar por nome ou telefone..."
                         value={q} onChange={e => { setQ(e.target.value); setCustomerId(""); }}
                         autoFocus={!!courseId}/>
                </div>

                {customerId && customer ? (
                  // Seleção confirmada — mostra card do aluno escolhido + botão pra trocar
                  <div style={{
                    padding: 12, borderRadius: 8,
                    background: "var(--ok-50, #ECFDF5)",
                    border: "1px solid var(--ok-200, #BBF7D0)",
                    display: "flex", alignItems: "center", gap: 12,
                  }}>
                    <div style={{
                      width: 36, height: 36, borderRadius: "50%",
                      background: "var(--ok-500, #22c55e)", color: "white",
                      display: "flex", alignItems: "center", justifyContent: "center",
                      flexShrink: 0,
                    }}>
                      <Icon name="check" size={18}/>
                    </div>
                    <div style={{ flex: 1, minWidth: 0 }}>
                      <div style={{ fontWeight: 700 }}>{customer.name}</div>
                      {customer.phone && (
                        <div style={{ fontSize: 12, color: "var(--text-soft)" }}>{customer.phone}</div>
                      )}
                    </div>
                    <button type="button" className="btn-link" onClick={() => { setCustomerId(""); setQ(""); }}>
                      Trocar
                    </button>
                  </div>
                ) : (
                  // Lista clicável — cards de cliente
                  <div style={{
                    maxHeight: 220, overflowY: "auto",
                    border: "1px solid var(--border)", borderRadius: 8,
                    background: "var(--bg)",
                  }}>
                    {filteredCust.length === 0 ? (
                      <div style={{ padding: 16, textAlign: "center", color: "var(--text-mute)", fontSize: 13 }}>
                        Nenhum cliente encontrado pra "{q}".
                      </div>
                    ) : filteredCust.map(c => (
                      <button key={c.id} type="button"
                              onClick={() => setCustomerId(c.id)}
                              style={{
                                width: "100%", textAlign: "left",
                                padding: "10px 12px", background: "transparent",
                                border: 0, borderBottom: "1px solid var(--border)",
                                cursor: "pointer", display: "flex", gap: 12, alignItems: "center",
                              }}
                              onMouseEnter={e => e.currentTarget.style.background = "var(--bg-soft)"}
                              onMouseLeave={e => e.currentTarget.style.background = "transparent"}>
                        <div style={{
                          width: 32, height: 32, borderRadius: "50%",
                          background: "var(--pink-100)", color: "var(--pink-600)",
                          display: "flex", alignItems: "center", justifyContent: "center",
                          flexShrink: 0, fontSize: 13, fontWeight: 700,
                        }}>
                          {(c.name || "?").trim().slice(0, 2).toUpperCase()}
                        </div>
                        <div style={{ flex: 1, minWidth: 0 }}>
                          <div style={{ fontWeight: 600, fontSize: 14 }}>{c.name}</div>
                          {c.phone && (
                            <div style={{ fontSize: 11, color: "var(--text-mute)" }}>{c.phone}</div>
                          )}
                        </div>
                      </button>
                    ))}
                  </div>
                )}
              </>
            )}
          </div>

          {/* ─── Passo 3: Valor (opcional) ─── */}
          {course && customerId && (
            <div style={{ marginBottom: 20 }}>
              <div style={{ display: "flex", alignItems: "center", gap: 8, marginBottom: 8 }}>
                <StepBadge n={3} done={true}/>
                <label className="label" style={{ margin: 0, color: "var(--pink-500)" }}>Valor mensal</label>
              </div>

              <div style={{
                display: "flex", gap: 8, padding: 4, background: "var(--bg-soft)",
                borderRadius: 8,
              }}>
                <button type="button"
                        onClick={() => { setUseOverride(false); setOverrideVal(""); }}
                        className={"btn " + (!useOverride ? "btn-primary" : "btn-secondary")}
                        style={{ flex: 1, justifyContent: "center" }}>
                  Padrão do curso ({BRL(defaultMonthly)})
                </button>
                <button type="button"
                        onClick={() => setUseOverride(true)}
                        className={"btn " + (useOverride ? "btn-primary" : "btn-secondary")}
                        style={{ flex: 1, justifyContent: "center" }}>
                  Valor personalizado
                </button>
              </div>

              {useOverride && (
                <div style={{ marginTop: 8 }}>
                  <input className="input tabular"
                         placeholder="0,00"
                         value={overrideVal}
                         onChange={e => setOverrideVal(maskBRL(e.target.value))}
                         autoFocus/>
                  <div style={{ fontSize: 11, color: "var(--text-mute)", marginTop: 4 }}>
                    Esse aluno pagará <b>{BRL(effectiveValue)}</b>/mês — diferente do curso padrão.
                  </div>
                </div>
              )}
            </div>
          )}

          {/* ─── Notas (sempre visível, mas só se tiver curso + aluno) ─── */}
          {course && customerId && (
            <div className="field">
              <label className="label">Notas internas (opcional)</label>
              <textarea className="input" rows={2} value={notes} onChange={e => setNotes(e.target.value)}
                        placeholder="Ex: aluno bolsista, paga sempre adiantado, etc."/>
            </div>
          )}

          {error && (
            <div style={{
              padding: 10, borderRadius: 8,
              background: "var(--pink-50, #FFE1EC)", color: "var(--pink-600)",
              fontSize: 13, marginTop: 8,
            }}>{error}</div>
          )}
        </form>

        <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", gap: 8, marginTop: 16, paddingTop: 16, borderTop: "1px solid var(--border)", flexShrink: 0 }}>
          <div style={{ fontSize: 12, color: "var(--text-mute)" }}>
            {!courseId ? "Comece escolhendo o curso →"
              : !customerId ? "Agora escolha o aluno →"
              : "Pronto pra criar a matrícula"}
          </div>
          <div style={{ display: "flex", gap: 8 }}>
            <button type="button" className="btn btn-secondary" onClick={onClose} disabled={saving}>Cancelar</button>
            <button type="button" className="btn btn-primary"
                    onClick={onSubmit}
                    disabled={saving || !courseId || !customerId}>
              {saving ? "Salvando..." : "Criar matrícula"}
            </button>
          </div>
        </div>
      </div>
    </div>
  );
}

/* ─── Modal de pagamento de mensalidade ────────────────────────── */
function CoursePaymentModal({ enrollment, monthDate, session, cashierId, onClose, onPaid }) {
  const value = Number(enrollment.effective_value) || 0;
  const [method,   setMethod]   = useS("pix");
  const [received, setReceived] = useS(BRL(value).replace("R$ ", "").trim());
  const [instlmt,  setInstlmt]  = useS(1);
  const [saving,   setSaving]   = useS(false);
  const [error,    setError]    = useS(null);

  const recNum = parseBRL(received);
  const change = method === "dinheiro" ? Math.max(0, recNum - value) : 0;
  const amount = method === "dinheiro" ? value : recNum;

  const onSubmit = async (e) => {
    e?.preventDefault?.();
    if (!session?.id) { setError("Caixa não está aberto."); return; }
    if (recNum < value && method !== "dinheiro") { setError("Valor menor que a mensalidade."); return; }
    setSaving(true); setError(null);
    try {
      await dbRegisterCoursePayment({
        sessionId:      session.id,
        cashierId,
        enrollmentId:   enrollment.enrollment_id,
        referenceMonth: monthDate,
        payments: [{
          method,
          amount,
          installments: method === "credito" ? Number(instlmt) || 1 : 1,
          change_given: change,
        }],
      });
      onPaid();
    } catch (err) {
      setError(err.message || String(err));
    } finally {
      setSaving(false);
    }
  };

  return (
    <div className="modal-backdrop">
      <div className="modal" style={{ width: 460 }} onClick={e => e.stopPropagation()} role="dialog" aria-modal="true">
        <div className="modal-head">
          <h2 style={{ margin: 0, fontSize: 20, fontWeight: 700 }}>Lançar pagamento</h2>
          <button className="btn-icon-mini" onClick={onClose} aria-label="Fechar"><Icon name="close" size={18}/></button>
        </div>

        <div style={{ background: "var(--bg-soft)", borderRadius: 8, padding: 12, marginBottom: 16 }}>
          <div style={{ fontSize: 12, color: "var(--text-mute)" }}>{enrollment.course_name}</div>
          <div style={{ fontWeight: 700, fontSize: 16 }}>{enrollment.customer_name}</div>
          <div style={{ fontSize: 12, color: "var(--text-soft)", marginTop: 4 }}>Mensalidade de {fmtMonthLabel(monthDate)}</div>
          <div style={{ fontSize: 22, fontWeight: 700, color: "var(--pink-600)", marginTop: 8 }}>{BRL(value)}</div>
        </div>

        <form onSubmit={onSubmit}>
          <div className="field">
            <label className="label">Método</label>
            <div className="seg-toggle">
              <button type="button" className={method === "pix"      ? "active" : ""} onClick={() => setMethod("pix")}>Pix</button>
              <button type="button" className={method === "dinheiro" ? "active" : ""} onClick={() => setMethod("dinheiro")}>Dinheiro</button>
              <button type="button" className={method === "debito"   ? "active" : ""} onClick={() => setMethod("debito")}>Débito</button>
              <button type="button" className={method === "credito"  ? "active" : ""} onClick={() => setMethod("credito")}>Crédito</button>
            </div>
          </div>

          {method === "dinheiro" && (
            <div className="field">
              <label className="label">Recebido</label>
              <input className="input tabular" value={received} onChange={e => setReceived(maskBRL(e.target.value))}/>
              {change > 0 && (
                <div style={{ fontSize: 12, color: "var(--ok-600)", marginTop: 4 }}>
                  Troco: <b>{BRL(change)}</b>
                </div>
              )}
            </div>
          )}

          {method === "credito" && (
            <div className="field">
              <label className="label">Parcelas</label>
              <select className="input tabular" value={instlmt} onChange={e => setInstlmt(Number(e.target.value))}>
                {Array.from({ length: 12 }).map((_, i) => <option key={i+1} value={i+1}>{i+1}x</option>)}
              </select>
            </div>
          )}

          {error && <div style={{ color: "var(--pink-600)", fontSize: 13, marginBottom: 8 }}>{error}</div>}

          <div style={{ display: "flex", justifyContent: "flex-end", gap: 8, marginTop: 16 }}>
            <button type="button" className="btn btn-secondary" onClick={onClose} disabled={saving}>Cancelar</button>
            <button type="submit" className="btn btn-primary" disabled={saving || !session?.id}>
              {saving ? "Salvando..." : `Confirmar ${BRL(value)}`}
            </button>
          </div>
        </form>
      </div>
    </div>
  );
}


/* ════════════════════════════════════════════════════════════
   VENDAS ONLINE — painel admin pra pedidos vindos do e-commerce.
   Filtra por status, mostra dados de envio e permite avançar o
   ciclo de vida (em_separacao → enviado → entregue).
   ════════════════════════════════════════════════════════════ */

const ONLINE_STATUS_LABELS = {
  aguardando_pagamento: "Aguardando pagamento",
  pago:                 "Pago",
  em_separacao:         "Em separação",
  enviado:              "Enviado",
  entregue:             "Entregue",
  cancelado:            "Cancelado",
};

const ONLINE_STATUS_COLORS = {
  aguardando_pagamento: { bg: "var(--warn-50, #FFFBEB)", fg: "var(--warn-600, #B45309)" },
  pago:                 { bg: "var(--info-50, #EFF6FF)", fg: "var(--info-600, #2563EB)" },
  em_separacao:         { bg: "var(--pink-50, #FFE1EC)", fg: "var(--pink-600)"          },
  enviado:              { bg: "#E0E7FF",                  fg: "#4338CA"                  },
  entregue:             { bg: "var(--ok-50, #ECFDF5)",   fg: "var(--ok-600, #059669)"   },
  cancelado:            { bg: "var(--bg-soft, #F4F4F4)", fg: "var(--text-mute)"         },
};

// Próximo status sugerido no fluxo natural (botão "Avançar"). Não inclui
// 'cancelado' — esse é manual via botão separado.
const ONLINE_NEXT_STATUS = {
  pago:         "em_separacao",
  em_separacao: "enviado",
  enviado:      "entregue",
};

function VendasOnline() {
  const { user } = useApp();
  const [orders,    setOrders]    = useS(null);
  const [filter,    setFilter]    = useS("ativos"); // 'ativos' | 'todas' | <status>
  const [loadError, setLoadError] = useS(null);
  const [busyId,    setBusyId]    = useS(null);
  const [viewing,   setViewing]   = useS(null); // pedido aberto no modal

  const reload = async () => {
    setOrders(null); setLoadError(null);
    try {
      const list = await dbListOnlineSales({ limit: 500 });
      setOrders(list);
    } catch (e) {
      setLoadError(e?.message || String(e));
      setOrders([]);
    }
  };
  useE(() => { reload(); }, []);

  const list = orders || [];
  const countBy = (s) => list.filter(o => o.online_status === s).length;
  // "Ativos" = todos menos entregue + cancelado (pedidos que precisam de ação)
  const filtered = filter === "todas" ? list
                 : filter === "ativos" ? list.filter(o => o.online_status !== "entregue" && o.online_status !== "cancelado")
                 : list.filter(o => o.online_status === filter);

  const advance = async (order) => {
    const next = ONLINE_NEXT_STATUS[order.online_status];
    if (!next) return;
    let tracking = order.tracking_code;
    if (next === "enviado" && !tracking) {
      tracking = prompt("Código de rastreio (opcional):", "") || null;
    }
    setBusyId(order.id);
    try {
      await dbUpdateOnlineStatus({
        saleId: order.id, newStatus: next,
        trackingCode: tracking, userId: user?.id || null,
      });
      await reload();
    } catch (e) {
      alert(e?.message || String(e));
    } finally {
      setBusyId(null);
    }
  };

  const cancelOrder = async (order) => {
    if (!confirm(`Cancelar pedido #${order.sale_number}? O estoque será estornado.`)) return;
    setBusyId(order.id);
    try {
      await dbUpdateOnlineStatus({
        saleId: order.id, newStatus: "cancelado", userId: user?.id || null,
      });
      await reload();
    } catch (e) {
      alert(e?.message || String(e));
    } finally {
      setBusyId(null);
    }
  };

  const fmtDate = (s) => {
    if (!s) return "—";
    try { return new Date(s).toLocaleString("pt-BR", { day: "2-digit", month: "2-digit", year: "2-digit", hour: "2-digit", minute: "2-digit" }); }
    catch { return String(s); }
  };

  return (
    <div className="screen vendas-online-screen">
      <PageHead title="Vendas Online"
                sub={orders == null ? "Carregando..." : `${list.length} pedidos · ${countBy("aguardando_pagamento")} aguardando · ${countBy("pago")} pagos · ${countBy("em_separacao")} em separação · ${countBy("enviado")} enviados`}/>

      {loadError && (
        <div style={{ padding: 16, color: "var(--pink-600)", fontWeight: 600 }}>
          Erro ao carregar: {loadError}
        </div>
      )}

      <div className="filter-bar">
        <div className="seg-toggle" style={{ flexWrap: "wrap" }}>
          <button className={filter === "ativos" ? "active" : ""} onClick={() => setFilter("ativos")}>
            Ativos · {list.filter(o => o.online_status !== "entregue" && o.online_status !== "cancelado").length}
          </button>
          <button className={filter === "aguardando_pagamento" ? "active" : ""} onClick={() => setFilter("aguardando_pagamento")}>
            Aguardando · {countBy("aguardando_pagamento")}
          </button>
          <button className={filter === "pago" ? "active" : ""} onClick={() => setFilter("pago")}>
            Pagos · {countBy("pago")}
          </button>
          <button className={filter === "em_separacao" ? "active" : ""} onClick={() => setFilter("em_separacao")}>
            Em separação · {countBy("em_separacao")}
          </button>
          <button className={filter === "enviado" ? "active" : ""} onClick={() => setFilter("enviado")}>
            Enviados · {countBy("enviado")}
          </button>
          <button className={filter === "entregue" ? "active" : ""} onClick={() => setFilter("entregue")}>
            Entregues · {countBy("entregue")}
          </button>
          <button className={filter === "cancelado" ? "active" : ""} onClick={() => setFilter("cancelado")}>
            Cancelados · {countBy("cancelado")}
          </button>
          <button className={filter === "todas" ? "active" : ""} onClick={() => setFilter("todas")}>
            Todas · {list.length}
          </button>
        </div>
      </div>

      {/* Cards (visíveis só em mobile via CSS — `display: none` em desktop)
          Cada pedido vira um card com info empilhada, evita scroll horizontal
          desconfortável da tabela em telas pequenas. */}
      <div className="online-cards">
        {orders == null ? (
          <div className="online-card-empty">Carregando...</div>
        ) : filtered.length === 0 ? (
          <div className="online-card-empty">
            {list.length === 0 ? "Nenhum pedido online ainda." : "Nada nesse filtro."}
          </div>
        ) : filtered.map(o => {
          const st = ONLINE_STATUS_COLORS[o.online_status] || { bg: "var(--bg-soft)", fg: "var(--text)" };
          const addr = o.shipping_address || {};
          const next = ONLINE_NEXT_STATUS[o.online_status];
          return (
            <div key={o.id} className="online-card" onClick={() => setViewing(o)}>
              <div className="online-card-top">
                <div>
                  <div className="online-card-num">#{o.sale_number}</div>
                  <div className="online-card-date">{fmtDate(o.sold_at)}</div>
                </div>
                <span className="badge" style={{ background: st.bg, color: st.fg, fontSize: 11 }}>
                  {ONLINE_STATUS_LABELS[o.online_status] || o.online_status}
                </span>
              </div>
              <div className="online-card-customer">
                <div className="online-card-name">{o.customer_name || "—"}</div>
                {o.customer_phone && (
                  <div className="online-card-phone">{o.customer_phone}</div>
                )}
              </div>
              {addr.street && (
                <div className="online-card-addr">
                  {addr.street}{addr.street_number ? `, ${addr.street_number}` : ""}
                  {addr.district && ` · ${addr.district}`}
                  <br/>
                  {addr.city}{addr.state ? `/${addr.state}` : ""} · CEP {addr.zip || "—"}
                </div>
              )}
              <div className="online-card-foot">
                <div>
                  <span className="online-card-label">Itens</span>
                  <b className="tabular">{Number(o.units_total) || 0}</b>
                </div>
                <div style={{ textAlign: "right" }}>
                  <span className="online-card-label">Total</span>
                  <b className="tabular" style={{ color: "var(--pink-700)" }}>{BRL(Number(o.total) || 0)}</b>
                </div>
              </div>
              {(next || (o.online_status !== "cancelado" && o.online_status !== "entregue")) && (
                <div className="online-card-actions" onClick={e => e.stopPropagation()}>
                  {next && (
                    <button className="btn btn-primary btn-sm"
                            onClick={() => advance(o)}
                            disabled={busyId === o.id}
                            style={{ flex: 1, justifyContent: "center" }}>
                      → {ONLINE_STATUS_LABELS[next]}
                    </button>
                  )}
                  {o.online_status !== "cancelado" && o.online_status !== "entregue" && (
                    <button className="btn-link"
                            onClick={() => cancelOrder(o)}
                            disabled={busyId === o.id}
                            style={{ color: "var(--pink-600)", fontSize: 12, padding: "8px 4px" }}>
                      Cancelar
                    </button>
                  )}
                </div>
              )}
            </div>
          );
        })}
      </div>

      <div className="card card-flush online-table-wrap">
        <div className="table-x-scroll">
          <table className="table">
            <thead>
              <tr>
                <th style={{ textAlign: "center", width: 80 }}>Pedido</th>
                <th style={{ textAlign: "center", width: 130 }}>Data/Hora</th>
                <th style={{ textAlign: "left" }}>Cliente</th>
                <th style={{ textAlign: "left" }}>Endereço</th>
                <th style={{ textAlign: "center", width: 90 }}>Itens</th>
                <th style={{ textAlign: "right", width: 110 }}>Total</th>
                <th style={{ textAlign: "center", width: 150 }}>Status</th>
                <th style={{ textAlign: "right", width: 260 }}>Ações</th>
              </tr>
            </thead>
            <tbody>
              {orders == null ? (
                <tr><td colSpan="8" style={{ textAlign: "center", padding: 40, color: "var(--text-mute)" }}>Carregando...</td></tr>
              ) : filtered.length === 0 ? (
                <tr><td colSpan="8" style={{ textAlign: "center", padding: 40, color: "var(--text-mute)" }}>
                  {list.length === 0 ? "Nenhum pedido online ainda." : "Nada nesse filtro."}
                </td></tr>
              ) : filtered.map(o => {
                const st = ONLINE_STATUS_COLORS[o.online_status] || { bg: "var(--bg-soft)", fg: "var(--text)" };
                const addr = o.shipping_address || {};
                const next = ONLINE_NEXT_STATUS[o.online_status];
                return (
                  <tr key={o.id} onClick={() => setViewing(o)} style={{ cursor: "pointer" }}>
                    <td style={{ textAlign: "center" }}>
                      <b style={{ fontFamily: "var(--font-mono)" }}>#{o.sale_number}</b>
                    </td>
                    <td className="tabular" style={{ textAlign: "center", fontSize: 12 }}>{fmtDate(o.sold_at)}</td>
                    <td>
                      <div style={{ fontWeight: 600 }}>{o.customer_name || "—"}</div>
                      {o.customer_phone && (
                        <div style={{ fontSize: 11, color: "var(--text-mute)" }}>{o.customer_phone}</div>
                      )}
                    </td>
                    <td style={{ fontSize: 12 }}>
                      {addr.street ? (
                        <>
                          {addr.street}{addr.street_number ? `, ${addr.street_number}` : ""}
                          {addr.district && <> · {addr.district}</>}
                          <div style={{ color: "var(--text-mute)" }}>
                            {addr.city}{addr.state ? `/${addr.state}` : ""} · CEP {addr.zip || "—"}
                          </div>
                        </>
                      ) : <span style={{ color: "var(--text-mute)" }}>—</span>}
                    </td>
                    <td className="tabular" style={{ textAlign: "center" }}>{Number(o.units_total) || 0}</td>
                    <td className="tabular" style={{ textAlign: "right", fontWeight: 700 }}>{BRL(Number(o.total) || 0)}</td>
                    <td style={{ textAlign: "center" }}>
                      <span className="badge" style={{ background: st.bg, color: st.fg, fontSize: 11 }}>
                        {ONLINE_STATUS_LABELS[o.online_status] || o.online_status}
                      </span>
                    </td>
                    <td style={{ textAlign: "right", whiteSpace: "nowrap" }} onClick={e => e.stopPropagation()}>
                      {next && (
                        <button className="btn btn-primary btn-sm"
                                onClick={() => advance(o)}
                                disabled={busyId === o.id}
                                style={{ fontSize: 12, padding: "5px 10px", marginRight: 6 }}>
                          → {ONLINE_STATUS_LABELS[next]}
                        </button>
                      )}
                      {o.online_status !== "cancelado" && o.online_status !== "entregue" && (
                        <button className="btn-link"
                                onClick={() => cancelOrder(o)}
                                disabled={busyId === o.id}
                                style={{ color: "var(--pink-600)", fontSize: 12 }}>
                          Cancelar
                        </button>
                      )}
                    </td>
                  </tr>
                );
              })}
            </tbody>
          </table>
        </div>
      </div>

      {viewing && (
        <OnlineSaleDetailsModal
          order={viewing}
          onClose={() => setViewing(null)}
          onReload={async () => { setViewing(null); await reload(); }}
        />
      )}
    </div>
  );
}

/* Modal de detalhe do pedido online — mostra itens, endereço e botões
   de ação completos. */
function OnlineSaleDetailsModal({ order, onClose, onReload }) {
  const { user } = useApp();
  const [busy, setBusy] = useS(false);
  const [error, setError] = useS(null);
  const [tracking, setTracking] = useS(order.tracking_code || "");

  const addr = order.shipping_address || {};
  const st = ONLINE_STATUS_COLORS[order.online_status] || { bg: "var(--bg-soft)", fg: "var(--text)" };
  const next = ONLINE_NEXT_STATUS[order.online_status];

  const doUpdate = async (newStatus) => {
    if (busy) return;
    setBusy(true); setError(null);
    try {
      await dbUpdateOnlineStatus({
        saleId: order.id, newStatus,
        trackingCode: tracking || null, userId: user?.id || null,
      });
      await onReload?.();
    } catch (e) {
      setError(e?.message || String(e));
      setBusy(false);
    }
  };

  const fmtDateTime = (s) => {
    if (!s) return "—";
    try { return new Date(s).toLocaleString("pt-BR", { day: "2-digit", month: "2-digit", year: "numeric", hour: "2-digit", minute: "2-digit" }); }
    catch { return String(s); }
  };

  return (
    <div className="modal-backdrop">
      <div className="modal" style={{ width: 640, maxHeight: "90vh", overflowY: "auto" }} onClick={e => e.stopPropagation()}>
        <div className="modal-head">
          <div>
            <h2 style={{ margin: 0, fontSize: 18, fontWeight: 700 }}>Pedido #{order.sale_number}</h2>
            <div style={{ marginTop: 6 }}>
              <span className="badge" style={{ background: st.bg, color: st.fg }}>
                {ONLINE_STATUS_LABELS[order.online_status]}
              </span>
              <span style={{ marginLeft: 10, fontSize: 12, color: "var(--text-mute)" }}>{fmtDateTime(order.sold_at)}</span>
            </div>
          </div>
          <button className="btn-icon-mini" onClick={onClose} disabled={busy}><Icon name="close" size={16}/></button>
        </div>

        {error && <div style={{ padding: 10, color: "var(--pink-600)", fontWeight: 600, marginBottom: 10 }}>{error}</div>}

        {/* Cliente + endereço */}
        <div style={{ padding: "12px 14px", background: "var(--bg-soft, #FAFAFA)", borderRadius: "var(--r-md)", marginBottom: 14 }}>
          <div style={{ fontSize: 11, color: "var(--text-mute)", textTransform: "uppercase", letterSpacing: ".05em" }}>Cliente</div>
          <div style={{ fontWeight: 600, marginTop: 2 }}>{order.customer_name || "—"}</div>
          {order.customer_phone && <div style={{ fontSize: 13, color: "var(--text-soft)" }}>{order.customer_phone}</div>}
          {addr.street && (
            <div style={{ marginTop: 10, fontSize: 13 }}>
              <div style={{ fontSize: 11, color: "var(--text-mute)", textTransform: "uppercase", letterSpacing: ".05em" }}>Endereço de entrega</div>
              <div>{addr.street}{addr.street_number ? `, ${addr.street_number}` : ""}{addr.complement ? ` · ${addr.complement}` : ""}</div>
              <div>{addr.district && `${addr.district} · `}{addr.city}{addr.state ? `/${addr.state}` : ""} · CEP {addr.zip || "—"}</div>
            </div>
          )}
        </div>

        {/* Itens */}
        <div style={{ marginBottom: 14 }}>
          <h4 style={{ margin: "0 0 8px 0", fontSize: 13, fontWeight: 700, textTransform: "uppercase", color: "var(--text-soft)", letterSpacing: ".04em" }}>
            Produtos ({order.items_count || 0} {order.items_count === 1 ? "item" : "itens"} · {Number(order.units_total) || 0} unidades)
          </h4>
          <div style={{ border: "1px solid var(--border)", borderRadius: "var(--r-sm)" }}>
            {(order.products || []).map((p, i) => (
              <div key={i} style={{ padding: "8px 12px", borderBottom: i < (order.products.length - 1) ? "1px solid var(--border)" : "none",
                                     display: "flex", justifyContent: "space-between", fontSize: 13 }}>
                <span>{p.name}</span>
                <span className="tabular" style={{ color: "var(--text-soft)" }}>×{p.quantity}</span>
              </div>
            ))}
          </div>
        </div>

        {/* Totais + envio */}
        <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 12, marginBottom: 14 }}>
          <div style={{ padding: "10px 12px", border: "1px solid var(--border)", borderRadius: "var(--r-sm)" }}>
            <div style={{ fontSize: 11, color: "var(--text-mute)" }}>Frete</div>
            <div className="tabular" style={{ fontWeight: 600 }}>{BRL(Number(order.delivery_fee) || 0)}</div>
            {order.shipping_method && <div style={{ fontSize: 12, color: "var(--text-soft)", marginTop: 2 }}>{order.shipping_method}</div>}
          </div>
          <div style={{ padding: "10px 12px", border: "1px solid var(--border)", borderRadius: "var(--r-sm)" }}>
            <div style={{ fontSize: 11, color: "var(--text-mute)" }}>Total</div>
            <div className="tabular" style={{ fontWeight: 700, fontSize: 16, color: "var(--pink-600)" }}>{BRL(Number(order.total) || 0)}</div>
            <div style={{ fontSize: 11, color: "var(--text-mute)", marginTop: 2 }}>
              {(order.payment_methods || []).join(", ") || "—"}
            </div>
          </div>
        </div>

        {/* Tracking */}
        <div className="field" style={{ marginBottom: 14 }}>
          <label className="label" htmlFor="track-code">Código de rastreio</label>
          <input id="track-code" name="track-code" className="input tabular"
                 value={tracking} onChange={e => setTracking(e.target.value)}
                 placeholder="ex. AA123456789BR" disabled={busy}/>
        </div>

        {/* Ações */}
        <div style={{ display: "flex", gap: 8, justifyContent: "space-between", flexWrap: "wrap" }}>
          <div>
            {order.online_status !== "cancelado" && order.online_status !== "entregue" && (
              <button className="btn btn-secondary"
                      onClick={() => {
                        if (confirm(`Cancelar pedido #${order.sale_number}? O estoque será estornado e a venda marcada como cancelada.`)) {
                          doUpdate("cancelado");
                        }
                      }}
                      disabled={busy}
                      style={{ color: "var(--pink-600)" }}>
                Cancelar pedido
              </button>
            )}
          </div>
          <div style={{ display: "flex", gap: 8 }}>
            <button className="btn btn-secondary" onClick={onClose} disabled={busy}>Fechar</button>
            {next && (
              <button className="btn btn-primary" onClick={() => doUpdate(next)} disabled={busy}>
                {busy ? "Salvando..." : `Avançar → ${ONLINE_STATUS_LABELS[next]}`}
              </button>
            )}
          </div>
        </div>
      </div>
    </div>
  );
}

Object.assign(window, {
  Dashboard, Produtos, Estoque, Fornecedores, Clientes, Cursos, Vendas, Caixa, Etiquetas, Relatorios,
  Configuracoes, VendasOnline,
  QuickAddCategory, QuickAddSupplier,
  ONLINE_STATUS_LABELS, ONLINE_STATUS_COLORS,
});
