// ====== Dashboard page ======
const DashboardDateFilterDialog = ({ config, initialValue, onApply, onCancel }) => {
  const [value, setValue] = React.useState(initialValue || "");
  const [rangeValue, setRangeValue] = React.useState(initialValue || { from: "", to: "" });
  const pickerRef = React.useRef(null);
  const endPickerRef = React.useRef(null);

  React.useEffect(() => {
    if (config?.kind === "range") setRangeValue(initialValue || { from: "", to: "" });
    else setValue(initialValue || "");
  }, [initialValue, config?.id]);

  if (!config) return null;

  const openPicker = (ref = pickerRef) => {
    const input = ref.current;
    if (!input) return;
    try { input.showPicker(); } catch { input.focus(); }
  };

  React.useEffect(() => {
    const timer = window.setTimeout(() => openPicker(pickerRef), 80);
    return () => window.clearTimeout(timer);
  }, [config.id]);

  const canApply = config.kind === "range" ? Boolean(rangeValue.from && rangeValue.to) : Boolean(value);
  const invalidRange = config.kind === "range" && rangeValue.from && rangeValue.to && rangeValue.from > rangeValue.to;
  const apply = () => {
    if (!canApply || invalidRange) return;
    onApply(config.id, config.kind === "range" ? rangeValue : value);
  };

  return ReactDOM.createPortal(
    <div className="date-dialog-scrim" style={{ position: "fixed", inset: 0, zIndex: 2000, display: "grid", placeItems: "center", padding: 22, background: "rgba(17, 38, 27, 0.42)", backdropFilter: "blur(3px)" }} onClick={onCancel}>
      <div className="date-dialog" style={{ width: "min(390px, 100%)", padding: 22, border: "1px solid var(--line-gold)", borderRadius: "var(--r-lg)", background: "var(--cream-50)", boxShadow: "var(--shadow-lg)" }} onClick={e => e.stopPropagation()}>
        <div className="date-dialog-head">
          <div>
            <div className="page-eyebrow">{config.eyebrow}</div>
            <h2>{config.title}</h2>
          </div>
        </div>
        <div className="field">
          {config.kind === "range" ? (
            <div style={{ display: "grid", gap: 12 }}>
              <div>
                <label>Start day</label>
                <div className="date-dialog-input-row">
                  <input ref={pickerRef} autoFocus type="date" value={rangeValue.from} onChange={e => setRangeValue(v => ({ ...v, from: e.target.value }))} />
                  <button className="icon-btn" onClick={() => openPicker(pickerRef)} title="Open picker"><Ic name="calendar" size={15} /></button>
                </div>
              </div>
              <div>
                <label>End day</label>
                <div className="date-dialog-input-row">
                  <input ref={endPickerRef} type="date" value={rangeValue.to} onChange={e => setRangeValue(v => ({ ...v, to: e.target.value }))} />
                  <button className="icon-btn" onClick={() => openPicker(endPickerRef)} title="Open picker"><Ic name="calendar" size={15} /></button>
                </div>
              </div>
              {invalidRange && <div style={{ color: "var(--rose-500)", fontSize: 12 }}>End day should be after start day.</div>}
            </div>
          ) : (
            <React.Fragment>
              <label>{config.inputLabel}</label>
              <div className="date-dialog-input-row">
                <input ref={pickerRef} autoFocus type={config.type} value={value} onChange={e => setValue(e.target.value)} />
                <button className="icon-btn" onClick={() => openPicker(pickerRef)} title="Open picker"><Ic name="calendar" size={15} /></button>
              </div>
            </React.Fragment>
          )}
        </div>
        <div className="date-dialog-foot">
          <button className="btn btn-ghost" onClick={onCancel}>Cancel</button>
          <button className="btn btn-primary" disabled={!canApply || invalidRange} onClick={apply}>Apply</button>
        </div>
      </div>
    </div>,
    document.body
  );
};

const DASHBOARD_PAYMENT_LABELS = {
  card: "Card",
  cash: "Cash",
  apple_pay: "Apple Pay",
  google_pay: "Google Pay",
  zelle: "Zelle",
  not_recorded: "Not recorded",
};

const dashboardPaymentLabel = (method) => {
  const key = String(method || "not_recorded").trim().toLowerCase();
  return DASHBOARD_PAYMENT_LABELS[key] || key.replace(/_/g, " ").replace(/\b\w/g, c => c.toUpperCase());
};

const DASHBOARD_FILTER_STORAGE_KEY = "teaGangDashboardFiltersV1";
const DASHBOARD_FILTER_PREF_KEY = "dashboardFiltersV1";
const DASHBOARD_DATE_FILTERS = new Set(["all", "today", "week", "month", "custom-day", "custom-week", "custom-month", "custom-range"]);
const DASHBOARD_REPORT_SORT_FIELDS = new Set(["sold", "name", "stock"]);
const SHOW_HEATMAP = false;

const readDashboardFilters = () => {
  try {
    const raw = window.localStorage.getItem(DASHBOARD_FILTER_STORAGE_KEY);
    const saved = raw ? JSON.parse(raw) : {};
    // Migrate old stock-asc / stock-desc values
    let reportSort = saved.reportSort || "sold";
    let reportSortDir = saved.reportSortDir === "asc" ? "asc" : "desc";
    if (reportSort === "stock-asc")  { reportSort = "stock"; reportSortDir = "asc"; }
    if (reportSort === "stock-desc") { reportSort = "stock"; reportSortDir = "desc"; }
    if (!DASHBOARD_REPORT_SORT_FIELDS.has(reportSort)) reportSort = "sold";
    return {
      dateFilter: DASHBOARD_DATE_FILTERS.has(saved.dateFilter) || String(saved.dateFilter || "").startsWith("custom-range:")
        ? saved.dateFilter
        : "today",
      customDate: typeof saved.customDate === "string" ? saved.customDate : "",
      customRange: saved.customRange && typeof saved.customRange === "object"
        ? { from: saved.customRange.from || "", to: saved.customRange.to || "" }
        : { from: "", to: "" },
      customerRankLimit: [5, 10, 20].includes(saved.customerRankLimit) ? saved.customerRankLimit : 5,
      reportOpen: saved.reportOpen === true,
      reportSort,
      reportSortDir,
      reportSearch: typeof saved.reportSearch === "string" ? saved.reportSearch : "",
      reportBarStyle: saved.reportBarStyle === "row" ? "row" : "under",
      reportCategoryOrder: Array.isArray(saved.reportCategoryOrder) ? saved.reportCategoryOrder.filter(Boolean) : [],
      heatSortField: ["name", "total", "added", "scheduled"].includes(saved.heatSortField) ? saved.heatSortField : "total",
      heatSortDir: saved.heatSortDir === "asc" ? "asc" : "desc",
      heatSearch: typeof saved.heatSearch === "string" ? saved.heatSearch : "",
      reportCols: {
        pending: saved.reportCols && typeof saved.reportCols.pending === "boolean" ? saved.reportCols.pending : true,
        ready: saved.reportCols && typeof saved.reportCols.ready === "boolean" ? saved.reportCols.ready : true,
        inStock: saved.reportCols && typeof saved.reportCols.inStock === "boolean" ? saved.reportCols.inStock : true,
        soldUnits: saved.reportCols && typeof saved.reportCols.soldUnits === "boolean" ? saved.reportCols.soldUnits : false,
        salesAmount: saved.reportCols && typeof saved.reportCols.salesAmount === "boolean" ? saved.reportCols.salesAmount : false,
        gift: saved.reportCols && typeof saved.reportCols.gift === "boolean" ? saved.reportCols.gift : false,
      },
      heatHideCompleted: saved.heatHideCompleted === true,
      heatShowNames: saved.heatShowNames === true,
      reportShowCompleted: saved.reportShowCompleted === true,
    };
  } catch {
    return {
      dateFilter: "today",
      customDate: "",
      customRange: { from: "", to: "" },
      customerRankLimit: 5,
      reportOpen: false,
      reportSort: "sold",
      reportSortDir: "desc",
      reportSearch: "",
      reportBarStyle: "under",
      reportCategoryOrder: [],
      heatSortField: "total",
      heatSortDir: "desc",
      heatSearch: "",
      reportCols: { pending: true, ready: true, inStock: true, soldUnits: false, salesAmount: false, gift: false },
      heatHideCompleted: false,
      heatShowNames: false,
      reportShowCompleted: false,
    };
  }
};

const Dashboard = () => {
  const initialFilters = React.useMemo(readDashboardFilters, []);
  const [reportOpen, setReportOpen] = React.useState(initialFilters.reportOpen);
  const [reportSort, setReportSort] = React.useState(initialFilters.reportSort);
  const [reportSortDir, setReportSortDir] = React.useState(initialFilters.reportSortDir);
  const [reportSortOpen, setReportSortOpen] = React.useState(false);
  const reportSortRef = React.useRef(null);
  const [reportDisplayOpen, setReportDisplayOpen] = React.useState(false);
  const reportDisplayRef = React.useRef(null);
  const [reportSearch, setReportSearch] = React.useState(initialFilters.reportSearch);
  const [reportBarStyle, setReportBarStyle] = React.useState(initialFilters.reportBarStyle);
  const [reportCategoryOrder, setReportCategoryOrder] = React.useState(initialFilters.reportCategoryOrder);
  const [reportCategoryDialog, setReportCategoryDialog] = React.useState(false);
  const [reportCategoryDrag, setReportCategoryDrag] = React.useState(null);
  const [reportStockEdit, setReportStockEdit] = React.useState(null); // { id, item, value, amount, saving }
  const [heatSortField, setHeatSortField] = React.useState(initialFilters.heatSortField);
  const [heatSortDir, setHeatSortDir] = React.useState(initialFilters.heatSortDir);
  const [heatSearch, setHeatSearch] = React.useState(initialFilters.heatSearch);
  const [reportCols, setReportCols] = React.useState(initialFilters.reportCols);
  const [heatColOrder, setHeatColOrder] = React.useState(() => {
    try { return JSON.parse(window.localStorage.getItem("teaGangHeatColOrder") || "null"); } catch { return null; }
  });
  const [heatDragOver, setHeatDragOver] = React.useState(null);
  const [heatReorderMode, setHeatReorderMode] = React.useState(false);
  const [heatHideCompleted, setHeatHideCompleted] = React.useState(initialFilters.heatHideCompleted);
  const [reportShowCompleted, setReportShowCompleted] = React.useState(initialFilters.reportShowCompleted);
  const heatDragSrc = React.useRef(null);
  const [orders, setOrders] = React.useState([]);
  const [menuItems, setMenuItems] = React.useState([]);
  const [cats, setCats] = React.useState([]);
  const [customerRankLimit, setCustomerRankLimit] = React.useState(initialFilters.customerRankLimit);
  const [dateFilter, setDateFilter] = React.useState(initialFilters.dateFilter);
  const [customDate, setCustomDate] = React.useState(initialFilters.customDate);
  const [customRange, setCustomRange] = React.useState(initialFilters.customRange);
  const [customDateRanges, setCustomDateRanges] = React.useState(() => window.TeaGangCustomDateRanges?.readLocal?.() || []);
  const [dateDialog, setDateDialog] = React.useState(null);
  const [heatHover, setHeatHover] = React.useState(null); // { name, x, y }
  const dashboardPrefsLoadedRef = React.useRef(false);

  const getRange = (filter, date, range) => {
    const namedRange = window.TeaGangCustomDateRanges?.findByFilter?.(customDateRanges, filter);
    if (namedRange) return window.TeaGangCustomDateRanges.range(namedRange);
    const now = new Date();
    const today = new Date(now.getFullYear(), now.getMonth(), now.getDate());
    const day = 864e5;
    switch (filter) {
      case "all": return {};
      case "today": return { dateFrom: today, dateTo: new Date(today.getTime() + day) };
      case "week": { const s = new Date(today); s.setDate(today.getDate() - ((today.getDay() || 7) - 1)); return { dateFrom: s, dateTo: new Date(today.getTime() + day) }; }
      case "month": return { dateFrom: new Date(now.getFullYear(), now.getMonth()), dateTo: new Date(now.getFullYear(), now.getMonth() + 1) };
      case "custom-day": if (!date) return {}; { const d = new Date(date + "T00:00:00"); return { dateFrom: d, dateTo: new Date(d.getTime() + day) }; }
      case "custom-week": if (!date) return {}; { const d = new Date(date + "T00:00:00"); const s = new Date(d); s.setDate(d.getDate() - ((d.getDay() || 7) - 1)); return { dateFrom: s, dateTo: new Date(s.getTime() + 7 * day) }; }
      case "custom-month": if (!date) return {}; { const [y, m] = date.split("-").map(Number); return { dateFrom: new Date(y, m - 1), dateTo: new Date(y, m) }; }
      case "custom-range": {
        if (!range.from || !range.to || range.from > range.to) return {};
        const from = new Date(range.from + "T00:00:00");
        const to = new Date(range.to + "T00:00:00");
        return { dateFrom: from, dateTo: new Date(to.getTime() + day) };
      }
      default: return {};
    }
  };
  const rangeRef = React.useRef(getRange("today", "", customRange));

  React.useEffect(() => {
    let disposed = false;
    const loadRanges = async () => {
      const ranges = await window.TeaGangCustomDateRanges?.load?.();
      if (!disposed) setCustomDateRanges(ranges || []);
    };
    loadRanges();
    const onChanged = () => loadRanges();
    window.addEventListener(window.TeaGangCustomDateRanges?.event || "teaGangCustomDateRangesChanged", onChanged);
    return () => {
      disposed = true;
      window.removeEventListener(window.TeaGangCustomDateRanges?.event || "teaGangCustomDateRangesChanged", onChanged);
    };
  }, []);

  const loadDashboard = React.useCallback(async () => {
    const backend = window.TeaGangBackend;
    if (!backend?.enabled) return;
    return Promise.all([
      backend.fetchOrders(rangeRef.current),
      backend.fetchMenu(),
    ]).then(([orderRows, menu]) => {
      setOrders(orderRows || []);
      setMenuItems(menu.items || []);
      setCats(menu.cats || []);
    }).catch((error) => {
      console.error("Dashboard backend unavailable.", error);
      setOrders([]);
      setMenuItems([]);
    });
  }, []);

  React.useEffect(() => {
    rangeRef.current = getRange(dateFilter, customDate, customRange);
    loadDashboard();
  }, [dateFilter, customDate, customRange, customDateRanges]); // eslint-disable-line

  React.useEffect(() => {
    let disposed = false;
    const backend = window.TeaGangBackend;
    if (!backend?.enabled || !backend.fetchUserPreferences) {
      dashboardPrefsLoadedRef.current = true;
      return;
    }
    backend.fetchUserPreferences("web").then(remote => {
      if (disposed) return;
      const saved = remote?.[DASHBOARD_FILTER_PREF_KEY];
      if (!saved || typeof saved !== "object") return;
      window.localStorage.setItem(DASHBOARD_FILTER_STORAGE_KEY, JSON.stringify(saved));
      const next = readDashboardFilters();
      setDateFilter(next.dateFilter);
      setCustomDate(next.customDate);
      setCustomRange(next.customRange);
      setCustomerRankLimit(next.customerRankLimit);
      setReportOpen(next.reportOpen);
      setReportSort(next.reportSort);
      setReportSortDir(next.reportSortDir);
      setReportSearch(next.reportSearch);
      setReportBarStyle(next.reportBarStyle);
      setReportCategoryOrder(next.reportCategoryOrder);
      setHeatSortField(next.heatSortField);
      setHeatSortDir(next.heatSortDir);
      setHeatSearch(next.heatSearch);
      setReportCols(next.reportCols);
      setHeatHideCompleted(next.heatHideCompleted);
      setReportShowCompleted(next.reportShowCompleted);
    }).catch(err => {
      console.warn("Could not load dashboard prefs from Supabase.", err);
    }).finally(() => {
      if (!disposed) dashboardPrefsLoadedRef.current = true;
    });
    return () => { disposed = true; };
  }, []);

  React.useEffect(() => {
    const dashboardPrefs = {
      dateFilter,
      customDate,
      customRange,
      customerRankLimit,
      reportOpen,
      reportSort,
      reportSortDir,
      reportSearch,
      reportBarStyle,
      reportCategoryOrder,
      heatSortField,
      heatSortDir,
      heatSearch,
      reportCols,
      heatHideCompleted,
      reportShowCompleted,
    };
    window.localStorage.setItem(DASHBOARD_FILTER_STORAGE_KEY, JSON.stringify(dashboardPrefs));

    const backend = window.TeaGangBackend;
    if (!backend?.enabled || !backend.fetchUserPreferences || !backend.upsertUserPreferences) return;
    if (!dashboardPrefsLoadedRef.current) return;
    const timer = window.setTimeout(async () => {
      try {
        const remote = await backend.fetchUserPreferences("web") || {};
        await backend.upsertUserPreferences({ ...remote, [DASHBOARD_FILTER_PREF_KEY]: dashboardPrefs }, "web");
      } catch (err) {
        console.warn("Could not save dashboard prefs to Supabase.", err);
      }
    }, 1000);
    return () => window.clearTimeout(timer);
  }, [dateFilter, customDate, customRange, customerRankLimit, reportOpen, reportSort, reportSortDir, reportSearch, reportBarStyle, reportCategoryOrder, heatSortField, heatSortDir, heatSearch, reportCols, heatHideCompleted, reportShowCompleted]);

  React.useEffect(() => {
    if (heatColOrder) window.localStorage.setItem("teaGangHeatColOrder", JSON.stringify(heatColOrder));
    else window.localStorage.removeItem("teaGangHeatColOrder");
  }, [heatColOrder]);

  React.useEffect(() => {
    if (!reportSortOpen) return;
    const handler = (e) => { if (!reportSortRef.current?.contains(e.target)) setReportSortOpen(false); };
    document.addEventListener("mousedown", handler);
    return () => document.removeEventListener("mousedown", handler);
  }, [reportSortOpen]);

  React.useEffect(() => {
    if (!reportDisplayOpen) return;
    const handler = (e) => { if (!reportDisplayRef.current?.contains(e.target)) setReportDisplayOpen(false); };
    document.addEventListener("mousedown", handler);
    return () => document.removeEventListener("mousedown", handler);
  }, [reportDisplayOpen]);

  React.useEffect(() => {
    const backend = window.TeaGangBackend;
    if (!backend?.subscribeToStoreData) return undefined;
    let disposed = false;
    let subscription = null;

    const refreshSoon = () => {
      if (!disposed) loadDashboard();
    };

    backend
      .subscribeToStoreData(refreshSoon, ["orders", "order_items"])
      .then((nextSubscription) => {
        if (disposed) {
          nextSubscription?.unsubscribe?.();
          return;
        }
        subscription = nextSubscription;
      })
      .catch((error) => {
        console.warn("Dashboard realtime unavailable.", error);
      });

    return () => {
      disposed = true;
      subscription?.unsubscribe?.();
    };
  }, [loadDashboard]);

  const startReportStockEdit = (item) => {
    const current = Number(item.stock_count || 0);
    setReportStockEdit({ id: item.id, item, value: current, amount: 1, saving: false });
  };

  const cancelReportStockEdit = () => setReportStockEdit(null);

  const saveReportStockValue = async (item, value) => {
    const stockCount = Math.max(0, Number(value || 0));
    const updated = { ...item, stock_count: stockCount };
    setReportStockEdit(edit => edit?.id === item.id ? { ...edit, saving: true } : edit);
    setMenuItems(prev => prev.map(m => m.id === item.id ? { ...m, stock_count: stockCount } : m));
    try {
      if (window.TeaGangBackend?.setProductStockWithInventory) {
        await window.TeaGangBackend.setProductStockWithInventory({
          menuItemId: updated.id,
          stockCount,
          notes: "Dashboard stock edit",
        });
      } else {
        await window.TeaGangBackend?.saveMenuItem?.(updated);
      }
      setReportStockEdit(edit => edit?.id === item.id ? null : edit);
    } catch (error) {
      console.error("Could not update stock from dashboard.", error);
      setMenuItems(prev => prev.map(m => m.id === item.id ? item : m));
      setReportStockEdit(edit => edit?.id === item.id ? { id: item.id, value: Number(item.stock_count || 0), saving: false } : edit);
      window.alert?.(`Could not update stock: ${error.message || error}`);
    }
  };

  const saveReportStockEdit = async (item, value = reportStockEdit?.value) => {
    if (!reportStockEdit || reportStockEdit.id !== item.id || reportStockEdit.saving) return;
    await saveReportStockValue(item, value);
  };

  const applyReportStockEdit = async (action) => {
    if (!reportStockEdit || reportStockEdit.saving) return;
    const item = reportStockEdit.item;
    const current = Number(item.stock_count || 0);
    const amount = Math.max(0, Number(reportStockEdit.amount || 0));
    if (action === "add") return saveReportStockEdit(item, current + amount);
    return saveReportStockEdit(item, reportStockEdit.value);
  };

  const today = new Date();
  const todayLabel = today.toLocaleDateString("en-US", {
    weekday: "long",
    month: "short",
    day: "numeric",
  });
  const revenueData = orders.length
    ? orders.reduce((points, order) => {
        const last = points[points.length - 1] || 0;
        return [...points, last + Number(order.total || 0)];
      }, [])
    : [0, 0];
  const revenueSpark = revenueData.length > 1 ? revenueData : [0, revenueData[0] || 0];

  const channels = [
    { label: "Pickup", v: orders.filter(o => o.channel === "pickup").length, color: "#c8a25c" },
    { label: "Delivery", v: orders.filter(o => o.channel === "delivery").length, color: "#6d8c79" },
    { label: "Other", v: orders.filter(o => !["pickup", "delivery"].includes(o.channel)).length, color: "#1f3a2a" },
  ].filter(c => c.v > 0);
  const money = (value) => Math.round((Number(value) || 0) * 100) / 100;
  const isRegularSaleItem = (item) => (item.pricing_mode || "normal") === "normal" && (item.line_type_name || "Regular Sale") === "Regular Sale";
  const itemLineTotal = (item) => Number(item.line_total ?? (Number(item.price || 0) * Number(item.qty || 0)));
  const orderItemTotal = (order) => money((order.items || []).reduce((sum, item) => sum + itemLineTotal(item), 0));
  const orderDiscountTotal = (order) => money(Number(order.discount_amount || order.discount || 0));
  const finalOrderTotal = (order) => money(Number(order.total || 0));
  const nonRegularLossAmount = (item) => {
    const savedDiscount = Number(item.line_discount_amount || 0);
    if (savedDiscount > 0) return savedDiscount;
    const qty = Number(item.qty || 0);
    const original = Number(item.original_unit_price ?? item.price ?? 0);
    const adjusted = Number(item.adjusted_unit_price ?? item.price ?? 0);
    return Math.max(0, original - adjusted) * qty;
  };
  const paymentRows = Object.values(orders.filter(o => o.status === "completed").reduce((acc, order) => {
    const method = order.payment?.method || "not_recorded";
    const key = String(method).trim().toLowerCase();
    if (!acc[key]) {
      acc[key] = {
        key,
        label: dashboardPaymentLabel(key),
        count: 0,
        total: 0,
      };
    }
    acc[key].count += 1;
    acc[key].total += finalOrderTotal(order);
    return acc;
  }, {})).sort((a, b) => b.total - a.total);
  const paymentTotal = paymentRows.reduce((sum, row) => sum + row.total, 0);
  const completedItemRevenue = orders
    .filter(order => order.status === "completed")
    .reduce((sum, order) => sum + orderItemTotal(order), 0);
  const completedNonRegularLoss = orders
    .filter(order => order.status === "completed")
    .reduce((sum, order) => {
      return sum + (order.items || []).reduce((itemSum, item) => {
        return isRegularSaleItem(item) ? itemSum : itemSum + nonRegularLossAmount(item);
      }, 0);
    }, 0);
  const completedNonRegularLossRows = Object.values(orders
    .filter(order => order.status === "completed")
    .reduce((acc, order) => {
      (order.items || []).forEach(item => {
        if (isRegularSaleItem(item)) return;
        const amount = nonRegularLossAmount(item);
        if (amount <= 0) return;
        const name = item.line_type_name || "Non-regular";
        if (!acc[name]) acc[name] = { name, total: 0 };
        acc[name].total += amount;
      });
      return acc;
    }, {})).sort((a, b) => b.total - a.total || a.name.localeCompare(b.name));
  const completedDiscountTotal = orders
    .filter(order => order.status === "completed")
    .reduce((sum, order) => sum + orderDiscountTotal(order), 0);
  const completedRevenue = orders
    .filter(order => order.status === "completed")
    .reduce((sum, order) => sum + finalOrderTotal(order), 0);
  const pendingRevenue = orders
    .filter(order => order.status === "pending")
    .reduce((sum, order) => sum + Number(order.total || 0), 0);
  const readyRevenue = orders
    .filter(order => order.status === "ready")
    .reduce((sum, order) => sum + Number(order.total || 0), 0);
  const grossRevenue = completedRevenue + pendingRevenue + readyRevenue;
  const openOrderCount = orders.filter(order => order.status === "pending" || order.status === "ready").length;
  const customerRows = Object.values(orders.reduce((acc, order) => {
    const name = order.customer || "Walk-in guest";
    const isAnon = !name || name === "Walk-in guest" || name === "Walk-in";
    const key = isAnon ? `anon__${order.dbId || order.id}` : (order.customer_id || `${name}__${order.phone || ""}`);
    if (!acc[key]) {
      acc[key] = {
        key,
        name: isAnon ? "Walk-in guest" : name,
        phone: order.phone && order.phone !== "—" ? order.phone : "",
        count: 0,
        total: 0,
        isAnon,
      };
    }
    acc[key].count += 1;
    acc[key].total += Number(order.total || 0);
    return acc;
  }, {})).sort((a, b) => b.total - a.total);
  const visibleCustomerRows = customerRows.slice(0, customerRankLimit);

  return (
    <div>
      <div className="page-head">
        <div>
          <div className="page-eyebrow">Store dashboard</div>
          <h1 className="page-title">Sales Report</h1>
          <div className="page-meta" style={{ marginTop: 8 }}>
            <span className="ornament"></span>
            <span>{todayLabel} · filtered backend data</span>
          </div>
        </div>
        <div className="row" style={{ gap: 10 }}>
          <button className="btn btn-ghost"><Ic name="filter" /> All channels</button>
          <button className="btn btn-primary"><Ic name="arrow-down" size={14} /> Export</button>
        </div>
      </div>

      {/* Date filter bar */}
      {(() => {
        const chips = [
          { id: "all", label: "All" },
          { id: "today", label: "Today" },
          { id: "week", label: "This week" },
          { id: "month", label: "This month" },
        ];
        const fmtDay = v => new Date(v + "T00:00:00").toLocaleDateString("en-US", { month: "short", day: "numeric" });
        const fmtWeek = v => { const d = new Date(v + "T00:00:00"); const s = new Date(d); s.setDate(d.getDate() - d.getDay()); return "Wk of " + s.toLocaleDateString("en-US", { month: "short", day: "numeric" }); };
        const fmtMonth = v => { const [y, m] = v.split("-"); return new Date(+y, +m - 1).toLocaleDateString("en-US", { month: "short", year: "numeric" }); };
        const fmtRange = r => r.from && r.to ? `${fmtDay(r.from)} - ${fmtDay(r.to)}` : "Date range";
        const chipStyle = (id) => ({ display: "inline-flex", alignItems: "center", gap: 5, padding: "5px 13px", borderRadius: 999, fontSize: 12.5, fontWeight: 600, cursor: "pointer", border: `1px solid ${dateFilter === id ? "var(--green-700)" : "var(--line)"}`, background: dateFilter === id ? "var(--green-700)" : "var(--cream-50)", color: dateFilter === id ? "white" : "var(--ink-700)" });
        const customPickers = [
          { id: "custom-day", label: dateFilter === "custom-day" && customDate ? fmtDay(customDate) : "Day", icon: "calendar", type: "date", eyebrow: "Dashboard filter", title: "Choose a day", inputLabel: "Day" },
          { id: "custom-week", label: dateFilter === "custom-week" && customDate ? fmtWeek(customDate) : "Week", icon: "calendar", type: "date", eyebrow: "Dashboard filter", title: "Choose a week", inputLabel: "Any day in the week" },
          { id: "custom-month", label: dateFilter === "custom-month" && customDate ? fmtMonth(customDate) : "Month", icon: "calendar", type: "month", eyebrow: "Dashboard filter", title: "Choose a month", inputLabel: "Month" },
          { id: "custom-range", label: dateFilter === "custom-range" ? fmtRange(customRange) : "Date range", icon: "calendar", type: "date", eyebrow: "Dashboard filter", title: "Choose a date range", inputLabel: "Range", kind: "range" },
        ];
        const namedRanges = customDateRanges.map(range => ({
          id: window.TeaGangCustomDateRanges.filterId(range),
          label: range.name,
        }));
        const selectCustomDate = (id, value) => {
          if (!value) return;
          setDateFilter(id);
          if (id === "custom-range") {
            setCustomRange(value);
            setCustomDate("");
          } else {
            setCustomDate(value);
          }
          setDateDialog(null);
        };
        return (
          <div className="dashboard-date-filters" style={{ display: "flex", flexWrap: "wrap", gap: 8, marginBottom: 16 }}>
            {chips.map(c => (
              <button key={c.id} style={chipStyle(c.id)} onClick={() => { setDateFilter(c.id); setCustomDate(""); setDateDialog(null); }}>{c.label}</button>
            ))}
            {namedRanges.map(c => (
              <button key={c.id} style={chipStyle(c.id)} onClick={() => { setDateFilter(c.id); setCustomDate(""); setDateDialog(null); }}>
                <Ic name="calendar" size={12} /> {c.label}
              </button>
            ))}
            {customPickers.map(p => (
              <span key={p.id} style={{ display: "inline-flex" }}>
                <button style={chipStyle(p.id)} onClick={() => setDateDialog(p)}>
                  <Ic name={p.icon} size={12} /> {p.label}
                </button>
              </span>
            ))}
            {dateDialog && (
              <DashboardDateFilterDialog
                config={dateDialog}
                initialValue={dateDialog.kind === "range" ? customRange : (dateFilter === dateDialog.id ? customDate : "")}
                onApply={selectCustomDate}
                onCancel={() => setDateDialog(null)}
              />
            )}
          </div>
        );
      })()}


      {/* Today at a glance — open ordered items vs. total stock */}
      {(() => {
        const pending = {};
        const ready = {};
        const sold = {};
        const revenue = {};
        const pendingNonRegular = {};
        const readyNonRegular = {};
        const soldNonRegular = {};
        const firstMenuIdByName = menuItems.reduce((acc, m) => {
          if (m.name && !acc[m.name]) acc[m.name] = m.id;
          return acc;
        }, {});
        const itemKey = (it) => it.menuItemId || firstMenuIdByName[it.name] || `name:${it.name}`;
        const menuKey = (m) => m.id || `name:${m.name}`;
        const stockQty = (it) => Number(it.stock_quantity_delta ?? it.qty ?? 0);
        orders.filter(o => o.status === "pending").forEach(o => o.items.forEach(it => {
          const key = itemKey(it);
          pending[key] = (pending[key] || 0) + stockQty(it);
          if (!isRegularSaleItem(it)) {
            const typeName = it.line_type_name || "Non-regular";
            if (!pendingNonRegular[key]) pendingNonRegular[key] = {};
            pendingNonRegular[key][typeName] = (pendingNonRegular[key][typeName] || 0) + (it.qty || 0);
          }
        }));
        orders.filter(o => o.status === "ready").forEach(o => o.items.forEach(it => {
          const key = itemKey(it);
          ready[key] = (ready[key] || 0) + stockQty(it);
          if (!isRegularSaleItem(it)) {
            const typeName = it.line_type_name || "Non-regular";
            if (!readyNonRegular[key]) readyNonRegular[key] = {};
            readyNonRegular[key][typeName] = (readyNonRegular[key][typeName] || 0) + (it.qty || 0);
          }
        }));
        orders
          .filter(o => o.status === "completed")
          .forEach(o => o.items.forEach(it => {
            const key = itemKey(it);
            sold[key] = (sold[key] || 0) + stockQty(it);
            if (!isRegularSaleItem(it)) {
              const typeName = it.line_type_name || "Non-regular";
              if (!soldNonRegular[key]) soldNonRegular[key] = {};
              soldNonRegular[key][typeName] = (soldNonRegular[key][typeName] || 0) + (it.qty || 0);
            }
            revenue[key] = (revenue[key] || 0) + Number(it.line_total ?? (Number(it.price || 0) * Number(it.qty || 0)));
          }));

        const catMap = {};
        menuItems.forEach(m => {
          const cat = cats.find(c => c.id === m.cat);
          const label = cat?.label || "Uncategorized";
          if (!catMap[label]) catMap[label] = 0;
          catMap[label] += (pending[menuKey(m)] || 0) + (ready[menuKey(m)] || 0);
        });
        const catTags = Object.entries(catMap).filter(([, v]) => v > 0).sort((a, b) => b[1] - a[1]);

        const stockFor = (m) => {
          const val = Number(m.stock_count || 0);
          return { val, par: Math.max(val, 1) };
        };
        const categoryFor = (m) => cats.find(c => c.id === m.cat) || { id: m.cat || "uncategorized", label: "Uncategorized", sort_order: 999999 };
        const categoryRank = (m) => {
          const cat = categoryFor(m);
          const savedIndex = reportCategoryOrder.indexOf(cat.id);
          if (savedIndex !== -1) return savedIndex;
          return 100000 + Number(cat.sort_order || 0);
        };
        const inStockFor = (m) => Math.max(0, Number(m.stock_count || 0));
        const ranked = menuItems
          .filter(m => (pending[menuKey(m)] || 0) > 0 || (ready[menuKey(m)] || 0) > 0 || inStockFor(m) > 0 || ((reportShowCompleted || reportCols.soldUnits || reportCols.salesAmount) && (sold[menuKey(m)] || 0) > 0))
          .map(m => ({ m, key: menuKey(m), pendingQty: pending[menuKey(m)] || 0, readyQty: ready[menuKey(m)] || 0, inStockQty: inStockFor(m), soldQty: sold[menuKey(m)] || 0, pendingNonRegular: pendingNonRegular[menuKey(m)] || {}, readyNonRegular: readyNonRegular[menuKey(m)] || {}, soldNonRegular: soldNonRegular[menuKey(m)] || {}, stock: stockFor(m) }))
          .sort((a, b) => {
            const catCmp = categoryRank(a.m) - categoryRank(b.m);
            if (catCmp !== 0) return catCmp;
            const asc = reportSortDir === "asc";
            if (reportSort === "name")  return asc ? a.m.name.localeCompare(b.m.name) : b.m.name.localeCompare(a.m.name);
            if (reportSort === "stock") return asc ? a.inStockQty - b.inStockQty : b.inStockQty - a.inStockQty;
            return asc ? a.pendingQty - b.pendingQty : b.pendingQty - a.pendingQty;
          });

        const searchQ = normalizeVi(reportSearch.trim());
        const filteredRanked = searchQ ? ranked.filter(({ m }) => normalizeVi(m.name).includes(searchQ)) : ranked;
        const visibleRanked = reportOpen ? filteredRanked : filteredRanked.slice(0, 8);
        const productCategories = (() => {
          const byId = {};
          ranked.forEach(({ m }) => {
            const cat = categoryFor(m);
            if (!byId[cat.id]) byId[cat.id] = { id: cat.id, label: cat.label || "Uncategorized", sort_order: Number(cat.sort_order || 0) };
          });
          return Object.values(byId).sort((a, b) => {
            const aSaved = reportCategoryOrder.indexOf(a.id);
            const bSaved = reportCategoryOrder.indexOf(b.id);
            if (aSaved !== -1 && bSaved !== -1 && aSaved !== bSaved) return aSaved - bSaved;
            if (aSaved !== -1) return -1;
            if (bSaved !== -1) return 1;
            const orderCmp = Number(a.sort_order || 0) - Number(b.sort_order || 0);
            return orderCmp || a.label.localeCompare(b.label);
          });
        })();
        const thStyle = { padding: "8px 16px", fontSize: 11, letterSpacing: ".1em", textTransform: "uppercase", color: "var(--ink-500)", fontWeight: 600, fontFamily: "var(--font-body)" };

        // Heatmap: customer × item quantity matrix
        const heatOrders = heatHideCompleted ? orders.filter(o => o.status !== "completed") : orders;
        const heatCustomers = [];
        const heatCustomerIndex = {};
        heatOrders.forEach(o => {
          const name = o.customer || "Walk-in";
          if (!(name in heatCustomerIndex)) {
            heatCustomerIndex[name] = heatCustomers.length;
            heatCustomers.push({ name, items: {}, latestAdded: null, latestScheduled: null, orderCount: 0, completedCount: 0 });
          }
          const row = heatCustomers[heatCustomerIndex[name]];
          row.orderCount++;
          if (o.status === "completed") row.completedCount++;
          (o.items || []).forEach(it => {
            const key = itemKey(it);
            row.items[key] = (row.items[key] || 0) + (it.qty || 1);
          });
          if (o.created_at) {
            const t = new Date(o.created_at).getTime();
            if (!row.latestAdded || t > row.latestAdded) row.latestAdded = t;
          }
          if (o.scheduled_at) {
            const t = new Date(o.scheduled_at).getTime();
            if (!row.latestScheduled || t > row.latestScheduled) row.latestScheduled = t;
          }
        });
        const heatItemTotals = heatOrders.reduce((acc, o) => {
          (o.items || []).forEach(it => {
            const key = itemKey(it);
            acc[key] = (acc[key] || 0) + (it.qty || 1);
          });
          return acc;
        }, {});
        const heatBaseItems = menuItems
          .filter(m => heatItemTotals[menuKey(m)] > 0)
          .map(m => ({ m, key: menuKey(m), qty: heatItemTotals[menuKey(m)] || 0, stock: stockFor(m) }))
          .sort((a, b) => {
            const catCmp = categoryRank(a.m) - categoryRank(b.m);
            if (catCmp !== 0) return catCmp;
            return b.qty - a.qty;
          });
        // Columns follow category organization first; custom item order applies within each category.
        const heatItems = (() => {
          const baseIndex = new Map(heatBaseItems.map((r, index) => [r.m.id, index]));
          const customIndex = new Map((heatColOrder || []).map((id, index) => [id, index]));
          return [...heatBaseItems].sort((a, b) => {
            const catCmp = categoryRank(a.m) - categoryRank(b.m);
            if (catCmp !== 0) return catCmp;
            const aCustom = customIndex.has(a.m.id) ? customIndex.get(a.m.id) : 100000 + (baseIndex.get(a.m.id) || 0);
            const bCustom = customIndex.has(b.m.id) ? customIndex.get(b.m.id) : 100000 + (baseIndex.get(b.m.id) || 0);
            return aCustom - bCustom;
          });
        })();
        const heatMax = Math.max(1, ...heatCustomers.flatMap(c => heatItems.map(r => c.items[r.key] || 0)));
        const heatRowMax = Math.max(1, ...heatCustomers.map(c => heatItems.reduce((s, r) => s + (c.items[r.key] || 0), 0)));
        const heatColTotals = heatItems.reduce((acc, { m }) => {
          const key = menuKey(m);
          acc[key] = heatCustomers.reduce((s, c) => s + (c.items[key] || 0), 0);
          return acc;
        }, {});
        const heatGrandTotal = heatCustomers.reduce((s, c) => s + heatItems.reduce((r, item) => r + (c.items[item.key] || 0), 0), 0);
        const heatColMax = Math.max(1, ...Object.values(heatColTotals));
        const heatBg = (val) => {
          if (!val) return "var(--cream-100)";
          return `rgba(44,74,30,${(0.12 + (val / heatMax) * 0.73).toFixed(2)})`;
        };
        const heatFg = (val) => val / heatMax > 0.48 ? "white" : "var(--green-800)";
        const heatTotalBg = (val, max) => {
          if (!val) return "var(--cream-100)";
          return `rgba(200,162,92,${(0.14 + (val / max) * 0.72).toFixed(2)})`;
        };
        const heatTotalFg = (val, max) => val / max > 0.48 ? "white" : "var(--gold-700)";
        const heatBgCompleted = (val) => {
          if (!val) return "rgba(180,60,60,0.07)";
          return `rgba(160,40,40,${(0.12 + (val / heatMax) * 0.73).toFixed(2)})`;
        };
        const heatFgCompleted = (val) => val / heatMax > 0.48 ? "white" : "#8b2020";
        const heatThStyle = { fontSize: 11, letterSpacing: ".08em", textTransform: "uppercase", color: "var(--ink-500)", fontWeight: 600, fontFamily: "var(--font-body)", background: "var(--cream-50)", borderBottom: "2px solid var(--line-gold)" };

        // Heatmap sort + filter
        const heatQ = normalizeVi(heatSearch.trim());
        const filteredHeatCustomers = heatQ
          ? heatCustomers.filter(c => normalizeVi(c.name).includes(heatQ))
          : heatCustomers;
        const sortedHeatCustomers = [...filteredHeatCustomers].sort((a, b) => {
          const asc = heatSortDir === "asc";
          if (heatSortField === "name") return asc ? a.name.localeCompare(b.name) : b.name.localeCompare(a.name);
          if (heatSortField === "added") {
            const at = a.latestAdded ?? (asc ? Infinity : -Infinity);
            const bt = b.latestAdded ?? (asc ? Infinity : -Infinity);
            return asc ? at - bt : bt - at;
          }
          if (heatSortField === "scheduled") {
            const at = a.latestScheduled ?? (asc ? Infinity : -Infinity);
            const bt = b.latestScheduled ?? (asc ? Infinity : -Infinity);
            return asc ? at - bt : bt - at;
          }
          const aT = heatItems.reduce((s, r) => s + (a.items[r.key] || 0), 0);
          const bT = heatItems.reduce((s, r) => s + (b.items[r.key] || 0), 0);
          return asc ? aT - bT : bT - aT;
        });

        const controlHeight = 36;
        const ctrlRow = { display: "flex", alignItems: "center", gap: 6, marginBottom: 8, flexWrap: "wrap" };
        const ctrlSearch = { width: 160, height: controlHeight, boxSizing: "border-box", margin: 0, padding: "0 12px" };
        const segBtn = (active) => ({ height: controlHeight, padding: "0 14px", display: "inline-flex", alignItems: "center", justifyContent: "center", lineHeight: 1, fontSize: 11.5, borderRadius: 999, border: `1px solid ${active ? "var(--green-700)" : "var(--line)"}`, background: active ? "var(--green-700)" : "var(--cream-50)", color: active ? "white" : "var(--ink-700)", cursor: "pointer", fontWeight: 600 });
        const dirBtn = { height: controlHeight, padding: "0 14px", display: "inline-flex", alignItems: "center", justifyContent: "center", lineHeight: 1, fontSize: 12, minWidth: 0 };
        const toggleReportCol = (key) => setReportCols(cols => ({ ...cols, [key]: !cols[key] }));
        const reportColOptions = [
          ["pending", "Pending"],
          ["ready", "Ready"],
          ["inStock", "In stock"],
          ["soldUnits", "Sold unit"],
          ["salesAmount", "Sale amount"],
          ["gift", "Gift"],
        ];

        return (
          <React.Fragment>
          <div className="report-strip dashboard-product-report" style={{ marginTop: 16, padding: "14px 18px 16px" }}>
            <div style={{ marginBottom: 8 }}>
              <div className="report-eyebrow">Today at a glance</div>
              {catTags.length > 0 && (
                <div style={{ display: "flex", flexWrap: "wrap", gap: 6, marginTop: 5 }}>
                  {catTags.map(([label, qty]) => (
                    <span key={label} style={{ display: "inline-flex", alignItems: "center", gap: 5, padding: "3px 10px", background: "var(--cream-200)", border: "1px solid var(--line-gold)", borderRadius: 999, fontSize: 12, fontWeight: 600, color: "var(--green-800)" }}>
                      {label}
                      <span style={{ fontFamily: "var(--font-mono)", color: "var(--gold-700)" }}>{qty}</span>
                    </span>
                  ))}
                </div>
              )}
            </div>

            <div className="dashboard-product-layout" style={{ display: "grid", gridTemplateColumns: "2fr 1fr", gap: 24, alignItems: "start" }}>
              <div>
                {/* Item table controls */}
                <div className="dashboard-report-controls" style={ctrlRow}>
                  <div className="search dashboard-report-search" style={ctrlSearch}>
                    <Ic name="search" size={14} />
                    <input placeholder="Search items…" value={reportSearch} onChange={e => setReportSearch(e.target.value)} />
                    {reportSearch && (
                      <button className="icon-btn" style={{ width: 22, height: 22, border: "none", background: "transparent", padding: 0 }} onClick={() => setReportSearch("")}>
                        <Ic name="x" size={12} />
                      </button>
                    )}
                  </div>
                  <div ref={reportSortRef} style={{ position: "relative" }}>
                    <button style={{ ...segBtn(true), position: "relative", minWidth: 112, fontSize: 11, textAlign: "center" }} onClick={() => setReportSortOpen(v => !v)}>
                      <span aria-hidden="true" style={{ position: "absolute", left: 14, top: "50%", transform: "translateY(-50%)", display: "inline-flex", alignItems: "center", justifyContent: "center" }}>
                        <Ic name="filter" size={12} />
                      </span>
                      <span style={{ display: "inline-flex", alignItems: "center", justifyContent: "center", gap: 3, whiteSpace: "nowrap", lineHeight: 1, width: "100%" }}>
                        <span>{{ sold: "Pending", name: "A-Z", stock: "In stock" }[reportSort]}</span>
                        <span aria-hidden="true">{reportSortDir === "asc" ? "↑" : "↓"}</span>
                      </span>
                    </button>
                    {reportSortOpen && (
                      <div style={{ position: "absolute", top: "calc(100% + 6px)", left: 0, zIndex: 300, background: "var(--cream-50)", border: "1px solid var(--line)", borderRadius: "var(--r-md)", boxShadow: "0 4px 20px rgba(0,0,0,0.13)", minWidth: 150, padding: "8px 0" }}>
                        <div style={{ padding: "4px 12px 6px", fontSize: 10, letterSpacing: ".12em", textTransform: "uppercase", color: "var(--ink-400)", fontWeight: 700 }}>Sort by</div>
                        {[{ id: "sold", label: "Pending" }, { id: "name", label: "A-Z" }, { id: "stock", label: "In stock" }].map(s => (
                          <button key={s.id} onClick={() => { setReportSort(s.id); }} style={{ display: "flex", alignItems: "center", justifyContent: "space-between", width: "100%", padding: "7px 14px", fontSize: 12.5, fontWeight: reportSort === s.id ? 700 : 500, color: reportSort === s.id ? "var(--green-700)" : "var(--ink-700)", background: "none", border: "none", cursor: "pointer", gap: 8 }}>
                            {s.label}
                            {reportSort === s.id && <Ic name="check" size={12} />}
                          </button>
                        ))}
                        <div style={{ borderTop: "1px solid var(--line-soft)", margin: "6px 0 4px", padding: "4px 12px 2px", fontSize: 10, letterSpacing: ".12em", textTransform: "uppercase", color: "var(--ink-400)", fontWeight: 700 }}>Direction</div>
                        {[{ id: "desc", label: "↓ Descending" }, { id: "asc", label: "↑ Ascending" }].map(d => (
                          <button key={d.id} onClick={() => setReportSortDir(d.id)} style={{ display: "flex", alignItems: "center", justifyContent: "space-between", width: "100%", padding: "7px 14px", fontSize: 12.5, fontWeight: reportSortDir === d.id ? 700 : 500, color: reportSortDir === d.id ? "var(--green-700)" : "var(--ink-700)", background: "none", border: "none", cursor: "pointer", gap: 8 }}>
                            {d.label}
                            {reportSortDir === d.id && <Ic name="check" size={12} />}
                          </button>
                        ))}
                      </div>
                    )}
                  </div>
                  {[{ id: "under", label: "Under" }, { id: "row", label: "Row" }].map(s => (
                    <button key={s.id} style={segBtn(reportBarStyle === s.id)} onClick={() => setReportBarStyle(s.id)}>{s.label}</button>
                  ))}
                  <button
                    style={{ ...segBtn(reportShowCompleted), fontSize: 11 }}
                    onClick={() => setReportShowCompleted(v => !v)}
                    title={reportShowCompleted ? "Hide completed items" : "Show completed items"}
                  >
                    {reportShowCompleted ? "Completed shown" : "Show completed"}
                  </button>
                  <div ref={reportDisplayRef} style={{ position: "relative" }}>
                    <button style={{ ...segBtn(reportDisplayOpen), fontSize: 11, gap: 6 }} onClick={() => setReportDisplayOpen(v => !v)}>
                      <Ic name="settings" size={12} />
                      Display
                    </button>
                    {reportDisplayOpen && (
                      <div className="dashboard-display-menu" style={{ position: "absolute", top: "calc(100% + 6px)", right: 0, zIndex: 320, minWidth: 210, padding: "8px 0", background: "var(--cream-50)", border: "1px solid var(--line)", borderRadius: "var(--r-md)", boxShadow: "0 4px 20px rgba(0,0,0,0.13)" }}>
                        <div style={{ padding: "4px 12px 6px", fontSize: 10, letterSpacing: ".12em", textTransform: "uppercase", color: "var(--ink-400)", fontWeight: 700 }}>Show columns</div>
                        {reportColOptions.map(([key, label]) => (
                          <button key={key} onClick={() => toggleReportCol(key)} style={{ display: "flex", alignItems: "center", justifyContent: "space-between", width: "100%", padding: "8px 14px", fontSize: 12.5, fontWeight: reportCols[key] ? 700 : 500, color: reportCols[key] ? "var(--green-700)" : "var(--ink-700)", background: "none", border: "none", cursor: "pointer", gap: 12 }}>
                            <span>{label}</span>
                            {reportCols[key] && <Ic name="check" size={12} />}
                          </button>
                        ))}
                      </div>
                    )}
                  </div>
                  <button className="btn btn-ghost" style={{ ...dirBtn, fontSize: 11 }} onClick={() => setReportCategoryDialog(true)}>
                    Categories
                  </button>
                </div>

                {reportCategoryDialog && ReactDOM.createPortal(
                  <div style={{ position: "fixed", inset: 0, zIndex: 2100, display: "grid", placeItems: "center", padding: 20, background: "rgba(17, 38, 27, 0.42)", backdropFilter: "blur(3px)" }} onClick={() => { setReportCategoryDialog(false); setReportCategoryDrag(null); }}>
                    <div style={{ width: "min(390px, 100%)", maxHeight: "min(620px, 90vh)", overflowY: "auto", padding: 20, border: "1px solid var(--line-gold)", borderRadius: "var(--r-lg)", background: "var(--cream-50)", boxShadow: "var(--shadow-lg)" }} onClick={e => e.stopPropagation()}>
                      <div className="date-dialog-head" style={{ marginBottom: 12 }}>
                        <div>
                          <div className="page-eyebrow">Product Sales</div>
                          <h2>Organize categories</h2>
                        </div>
                      </div>
                      {productCategories.length <= 1 ? (
                        <div style={{ padding: "24px 8px", color: "var(--ink-500)", fontSize: 13, textAlign: "center" }}>Only one category appears in this report.</div>
                      ) : (
                        <div style={{ display: "grid", gap: 8 }}>
                          {productCategories.map((cat, index) => {
                            const isDragOver = reportCategoryDrag?.over === cat.id;
                            return (
                              <div
                                key={cat.id}
                                draggable
                                onDragStart={() => setReportCategoryDrag({ src: cat.id, over: null })}
                                onDragOver={e => { e.preventDefault(); setReportCategoryDrag(d => ({ src: d?.src || cat.id, over: cat.id })); }}
                                onDragLeave={() => setReportCategoryDrag(d => d?.src ? ({ ...d, over: null }) : null)}
                                onDrop={() => {
                                  const src = reportCategoryDrag?.src;
                                  setReportCategoryDrag(null);
                                  if (!src || src === cat.id) return;
                                  const ids = productCategories.map(c => c.id);
                                  const from = ids.indexOf(src);
                                  const to = ids.indexOf(cat.id);
                                  if (from === -1 || to === -1) return;
                                  ids.splice(from, 1);
                                  ids.splice(to, 0, src);
                                  setReportCategoryOrder(ids);
                                }}
                                onDragEnd={() => setReportCategoryDrag(null)}
                                style={{ display: "grid", gridTemplateColumns: "34px 1fr auto", alignItems: "center", gap: 10, padding: "10px 12px", border: `1px solid ${isDragOver ? "var(--gold-500)" : "var(--line)"}`, borderRadius: 8, background: isDragOver ? "var(--cream-200)" : "white", cursor: "grab" }}
                              >
                                <span style={{ width: 26, height: 26, borderRadius: 999, display: "grid", placeItems: "center", background: "rgba(200, 162, 92, .16)", color: "var(--gold-700)", fontFamily: "var(--font-mono)", fontSize: 12, fontWeight: 700 }}>{index + 1}</span>
                                <span style={{ color: "var(--green-800)", fontSize: 13, fontWeight: 700, minWidth: 0, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{cat.label}</span>
                                <Ic name="menu" size={16} />
                              </div>
                            );
                          })}
                        </div>
                      )}
                      <div className="date-dialog-foot" style={{ marginTop: 16 }}>
                        <button className="btn btn-ghost" onClick={() => setReportCategoryOrder([])}>Reset</button>
                        <button className="btn btn-primary" onClick={() => { setReportCategoryDialog(false); setReportCategoryDrag(null); }}>Done</button>
                      </div>
                    </div>
                  </div>,
                  document.body
                )}

                {reportStockEdit && ReactDOM.createPortal(
                  <div style={{ position: "fixed", inset: 0, zIndex: 2100, display: "grid", placeItems: "center", padding: 20, background: "rgba(17, 38, 27, 0.42)", backdropFilter: "blur(3px)" }} onClick={cancelReportStockEdit}>
                    <div style={{ width: "min(360px, 100%)", padding: 20, border: "1px solid var(--line-gold)", borderRadius: "var(--r-lg)", background: "var(--cream-50)", boxShadow: "var(--shadow-lg)" }} onClick={e => e.stopPropagation()}>
                      <div className="date-dialog-head" style={{ marginBottom: 14 }}>
                        <div style={{ minWidth: 0 }}>
                          <div className="page-eyebrow">Total Stock</div>
                          <h2 style={{ overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{reportStockEdit.item?.name || "Item"}</h2>
                        </div>
                        <button className="icon-btn" onClick={cancelReportStockEdit}><Ic name="x" size={13} /></button>
                      </div>
                      <div style={{ display: "grid", gap: 12 }}>
                        <div className="spread" style={{ padding: "8px 10px", border: "1px solid var(--line)", borderRadius: 8, background: "white" }}>
                          <span style={{ fontSize: 12, color: "var(--ink-500)" }}>Current total</span>
                          <span className="mono" style={{ fontSize: 16, fontWeight: 800, color: "var(--green-800)" }}>{Number(reportStockEdit.item?.stock_count || 0)}</span>
                        </div>
                        <div className="field">
                          <label>Amount</label>
                          <input
                            autoFocus
                            type="number"
                            min="0"
                            step="1"
                            value={reportStockEdit.amount}
                            disabled={reportStockEdit.saving}
                            onChange={e => setReportStockEdit(edit => edit ? { ...edit, amount: e.target.value } : edit)}
                            onKeyDown={e => {
                              if (e.key === "Enter") applyReportStockEdit("add");
                              if (e.key === "Escape") cancelReportStockEdit();
                            }}
                          />
                        </div>
                        <div>
                          <button className="btn btn-primary" style={{ width: "100%" }} disabled={reportStockEdit.saving} onClick={() => applyReportStockEdit("add")}>Add amount</button>
                        </div>
                        <div className="field">
                          <label>Enter total number</label>
                          <div style={{ display: "flex", gap: 8 }}>
                            <input
                              type="number"
                              min="0"
                              step="1"
                              value={reportStockEdit.value}
                              disabled={reportStockEdit.saving}
                              onChange={e => setReportStockEdit(edit => edit ? { ...edit, value: e.target.value } : edit)}
                              onKeyDown={e => {
                                if (e.key === "Enter") applyReportStockEdit("set");
                                if (e.key === "Escape") cancelReportStockEdit();
                              }}
                            />
                            <button className="btn btn-primary" disabled={reportStockEdit.saving} onClick={() => applyReportStockEdit("set")}>Set</button>
                          </div>
                        </div>
                        {reportStockEdit.saving && <div style={{ fontSize: 12, color: "var(--ink-500)", textAlign: "center" }}>Saving total...</div>}
                      </div>
                    </div>
                  </div>,
                  document.body
                )}

                <div className="dashboard-product-table-wrap">
                <table className="dashboard-product-table" style={{ borderCollapse: "collapse", width: "100%" }}>
                  <thead>
                    <tr style={{ borderBottom: "2px solid var(--line-gold)" }}>
                      <th style={{ ...thStyle, textAlign: "left", padding: "5px 12px" }}>Item</th>
                      {reportCols.pending && <th style={{ ...thStyle, textAlign: "center", padding: "5px 20px", whiteSpace: "nowrap" }}>Pending</th>}
                      {reportCols.ready && <th style={{ ...thStyle, textAlign: "center", padding: "5px 20px", whiteSpace: "nowrap" }}>Ready</th>}
                      {reportCols.inStock && <th style={{ ...thStyle, textAlign: "center", padding: "5px 20px", whiteSpace: "nowrap" }}>In stock</th>}
                      {reportCols.soldUnits && <th style={{ ...thStyle, textAlign: "center", padding: "5px 20px", whiteSpace: "nowrap" }}>Sold unit</th>}
                      {reportCols.salesAmount && <th style={{ ...thStyle, textAlign: "center", padding: "5px 20px 5px 20px", whiteSpace: "nowrap" }}>Sale amount</th>}
                    </tr>
                  </thead>
                  <tbody>
                    {visibleRanked.length === 0 && (
                      <tr>
                        <td colSpan={1 + Object.entries(reportCols).filter(([key, value]) => key !== "gift" && value).length} style={{ padding: "28px 12px", textAlign: "center", color: "var(--ink-400)", fontSize: 13, fontStyle: "italic" }}>
                          No ordered or sold items in this date range.
                        </td>
                      </tr>
                    )}
                    {visibleRanked.map(({ m, key, pendingQty, readyQty, inStockQty, soldQty, pendingNonRegular, readyNonRegular, soldNonRegular, stock }, index) => {
                      const cat = categoryFor(m);
                      const prevCat = index > 0 ? categoryFor(visibleRanked[index - 1].m) : null;
                      const showCategoryHeader = !prevCat || prevCat.id !== cat.id;
                      const pendingStockPct = inStockQty > 0 ? Math.min(100, (pendingQty / inStockQty) * 100) : (pendingQty > 0 ? 100 : 0);
                      const leftRatio = stock.val > 0 ? inStockQty / stock.val : 0;
                      const critical = stock.val <= 0 || inStockQty === 0 || leftRatio < 0.2;
                      const low = leftRatio < 0.4 && !critical;
                      const stockColor = critical ? "var(--rose-500)" : low ? "var(--gold-700)" : "var(--green-700)";
                      const visibleColCount = 1 + Object.entries(reportCols).filter(([key, value]) => key !== "gift" && value).length;
                      const giftSuffix = (entries) => {
                        const list = Object.entries(entries || {}).filter(([, count]) => Number(count) > 0).sort((a, b) => a[0].localeCompare(b[0]));
                        return reportCols.gift && list.length ? ` ${list.map(([name, count]) => `${name} ${count}`).join(" ")}` : "";
                      };
                      const rowFillPct = Math.max(2, pendingStockPct);
                      const rowFillColor = critical
                        ? "rgba(178, 75, 57, 0.14)"
                        : low
                          ? "rgba(184, 126, 42, 0.16)"
                          : "rgba(200, 162, 92, 0.16)";
                      const rowProgressStyle = reportBarStyle === "row" && pendingQty > 0
                        ? { background: `linear-gradient(90deg, ${rowFillColor} 0%, ${rowFillColor} ${rowFillPct}%, transparent ${rowFillPct}%, transparent 100%)` }
                        : {};
                      return (
                        <React.Fragment key={m.id}>
                          {showCategoryHeader && (
                            <tr className="dashboard-product-category-row">
                              <td colSpan={visibleColCount} style={{ padding: index === 0 ? "2px 12px 6px" : "12px 12px 6px", color: "var(--gold-700)", fontSize: 11.5, fontWeight: 800, letterSpacing: ".08em", textTransform: "uppercase" }}>
                                {cat.label || "Uncategorized"}
                              </td>
                            </tr>
                          )}
                          <tr className="dashboard-product-row" style={{ borderTop: "1px solid var(--line-soft)", ...rowProgressStyle }}>
                            <td className="dashboard-product-item-cell" style={{ padding: "5px 12px" }}>
                              <span className="row" style={{ gap: 8 }}>
                                <span className="report-glyph" style={{ flexShrink: 0, overflow: "hidden", position: "relative" }}>
                                  {m.image_url
                                    ? <MenuCachedImage itemId={m.id} imageUrl={m.image_url} alt={m.name} loading="eager" style={{ position: "absolute", inset: 0, width: "100%", height: "100%", objectFit: "contain", padding: 4, transform: `scale(${Number(m.image_zoom || 1)})`, transformOrigin: "center" }} />
                                    : m.glyph}
                                </span>
                                <span className="report-name">{m.name}</span>
                              </span>
                            </td>
                            {reportCols.pending && <td data-label="Pending" style={{ padding: "5px 20px", textAlign: "center", fontFamily: "var(--font-mono)", fontSize: 13, fontWeight: 700, color: "var(--gold-700)", whiteSpace: "nowrap", width: 1 }}>{pendingQty}{giftSuffix(pendingNonRegular)}</td>}
                            {reportCols.ready && <td data-label="Ready" style={{ padding: "5px 20px", textAlign: "center", fontFamily: "var(--font-mono)", fontSize: 13, fontWeight: 700, color: "var(--gold-700)", whiteSpace: "nowrap", width: 1 }}>{readyQty}{giftSuffix(readyNonRegular)}</td>}
                            {reportCols.inStock && <td data-label="In stock" style={{ padding: "5px 20px", textAlign: "center", fontFamily: "var(--font-mono)", fontSize: 13, fontWeight: 800, color: inStockQty === 0 ? "var(--rose-500)" : "var(--green-800)", whiteSpace: "nowrap", width: 1 }}>{inStockQty}</td>}
                            {reportCols.soldUnits && <td data-label="Sold unit" style={{ padding: "5px 20px", textAlign: "center", fontFamily: "var(--font-mono)", fontSize: 13, fontWeight: 700, color: "var(--ink-600)", whiteSpace: "nowrap", width: 1 }}>{soldQty}{giftSuffix(soldNonRegular)}</td>}
                            {reportCols.salesAmount && <td data-label="Sale" style={{ padding: "5px 20px", textAlign: "center", fontFamily: "var(--font-mono)", fontSize: 13, fontWeight: 700, color: "var(--green-800)", whiteSpace: "nowrap", width: 1 }}>${(revenue[key] || 0).toFixed(2)}</td>}
                          </tr>
                          {reportBarStyle === "under" && pendingQty > 0 && (
                            <tr className="dashboard-product-bar-row">
                              <td colSpan={visibleColCount} style={{ padding: "1px 12px 4px" }}>
                                <div className="report-bar">
                                  <div className={`report-bar-stock ${critical ? "critical" : low ? "low" : ""}`} style={{ width: `${rowFillPct}%` }} />
                                </div>
                              </td>
                            </tr>
                          )}
                        </React.Fragment>
                      );
                    })}
                  </tbody>
                </table>
                </div>

                {filteredRanked.length > 8 && (
                  <div className={`report-toggle ${reportOpen ? "open" : ""}`} onClick={() => setReportOpen(!reportOpen)}>
                    {reportOpen ? "Show less" : `Show all ${filteredRanked.length} items`}
                    <span className="chev">▾</span>
                  </div>
                )}
              </div>

              <div className="dashboard-side-metrics" style={{ display: "flex", flexDirection: "column", gap: 12 }}>
                  <div className="card kpi" style={{ minWidth: 0, display: "flex", flexDirection: "column" }}>
                    <div className="kpi-label">Revenue</div>
                    <div style={{ display: "grid", gap: 8, marginTop: 12 }}>
                      <div className="spread" style={{ padding: 0 }}>
                        <span style={{ fontSize: 15, color: "var(--ink-500)" }}>Item</span>
                        <span className="mono" style={{ fontSize: 15, fontWeight: 800, color: "var(--green-800)" }}>${completedItemRevenue.toFixed(2)}</span>
                      </div>
                      <div className="spread" style={{ padding: 0 }}>
                        <span style={{ fontSize: 15, color: "var(--ink-500)" }}>Discount</span>
                        <span className="mono" style={{ fontSize: 15, fontWeight: 800, color: "var(--rose-500)" }}>−${completedDiscountTotal.toFixed(2)}</span>
                      </div>
                    </div>
                    <div style={{ height: 1, background: "var(--line-soft)", margin: "16px 0" }} />
                    <div style={{ display: "grid", gap: 8 }}>
                      <div className="spread" style={{ padding: 0 }}>
                        <span style={{ fontSize: 15, color: "var(--ink-500)" }}>Completed Orders</span>
                        <span className="mono" style={{ fontSize: 15, fontWeight: 800, color: "var(--green-800)" }}>${completedRevenue.toFixed(2)}</span>
                      </div>
                      <div className="spread" style={{ padding: 0 }}>
                        <span style={{ fontSize: 15, color: "var(--ink-500)" }}>Pending</span>
                        <span className="mono" style={{ fontSize: 15, fontWeight: 800, color: "var(--gold-700)" }}>${pendingRevenue.toFixed(2)}</span>
                      </div>
                      <div className="spread" style={{ padding: 0 }}>
                        <span style={{ fontSize: 15, color: "var(--ink-500)" }}>Ready</span>
                        <span className="mono" style={{ fontSize: 15, fontWeight: 800, color: "var(--gold-700)" }}>${readyRevenue.toFixed(2)}</span>
                      </div>
                    </div>
                    <div style={{ marginTop: 14, padding: "13px 14px 12px", borderRadius: 14, border: "1px solid rgba(184, 135, 47, 0.28)", background: "linear-gradient(135deg, rgba(184, 135, 47, 0.13), rgba(176, 82, 67, 0.08))", color: "var(--ink-700)" }}>
                      <div className="spread" style={{ padding: 0, alignItems: "start" }}>
                        <div>
                          <div style={{ fontSize: 12, letterSpacing: ".12em", textTransform: "uppercase", color: "var(--gold-700)", fontWeight: 900 }}>Non-regular loss</div>
                          <div style={{ marginTop: 3, fontSize: 12.5, color: "var(--ink-500)", fontWeight: 600 }}>Promotion value given away</div>
                        </div>
                        <div className="mono" style={{ fontSize: 25, lineHeight: 1, fontWeight: 900, color: "var(--gold-700)" }}>${completedNonRegularLoss.toFixed(2)}</div>
                      </div>
                      {completedNonRegularLossRows.length > 0 && (
                        <div style={{ display: "grid", gap: 6, marginTop: 12, paddingTop: 10, borderTop: "1px solid rgba(184, 135, 47, 0.2)" }}>
                          {completedNonRegularLossRows.map(row => (
                            <div key={row.name} className="spread" style={{ padding: 0 }}>
                              <span style={{ fontSize: 13, color: "var(--ink-600)", fontWeight: 700 }}>{row.name}</span>
                              <span className="mono" style={{ fontSize: 13, fontWeight: 800, color: "var(--gold-700)" }}>${row.total.toFixed(2)}</span>
                            </div>
                          ))}
                        </div>
                      )}
                    </div>
                    <div style={{ marginTop: "auto", paddingTop: 18 }}>
                      <div style={{ padding: "14px 14px 12px", borderRadius: 14, background: "var(--green-700)", color: "white" }}>
                        <div style={{ fontSize: 12, letterSpacing: ".12em", textTransform: "uppercase", opacity: 0.72, fontWeight: 800 }}>Potential Revenue</div>
                        <div className="mono" style={{ marginTop: 4, fontSize: 30, lineHeight: 1, fontWeight: 900 }}>${grossRevenue.toFixed(2)}</div>
                      </div>
                    </div>
                  </div>

                  <div className="card payment-report-card" style={{ minWidth: 0 }}>
                    <div className="card-head">
                      <div>
                        <div className="card-title">Sales by Payment Method</div>
                        <div className="card-sub">
                          {paymentTotal > 0
                            ? `$${paymentTotal.toFixed(2)} across ${paymentRows.reduce((s, r) => s + r.count, 0)} completed order${paymentRows.reduce((s, r) => s + r.count, 0) !== 1 ? "s" : ""}`
                            : "No payment data in this date range"}
                        </div>
                      </div>
                    </div>
                    <div className="payment-report-list">
                      {paymentRows.length === 0 && (
                        <div className="payment-report-empty">No orders found for this date range.</div>
                      )}
                      {paymentRows.map(row => {
                        const pct = paymentTotal > 0 ? (row.total / paymentTotal) * 100 : 0;
                        const isMissing = row.key === "not_recorded";
                        return (
                          <div key={row.key} className={`payment-report-row ${isMissing ? "muted" : ""}`}>
                            <div className="payment-report-main">
                              <span className="payment-report-icon"><Ic name="money" size={14} /></span>
                              <div>
                                <div className="payment-report-label">{row.label}</div>
                                <div className="payment-report-meta">{row.count} order{row.count !== 1 ? "s" : ""}</div>
                              </div>
                            </div>
                            <div className="payment-report-amount">
                              <span>${row.total.toFixed(2)}</span>
                              <span>{pct.toFixed(1)}%</span>
                            </div>
                            <div className="payment-report-bar">
                              <div style={{ width: `${Math.max(paymentTotal > 0 ? 3 : 0, pct)}%` }} />
                            </div>
                          </div>
                        );
                      })}
                    </div>
                  </div>

                  <div className="card kpi" style={{ minWidth: 0 }}>
                    <div className="kpi-label">Sales Channels</div>
                    <div style={{ display: "flex", alignItems: "center", gap: 14, marginTop: 6 }}>
                      <Donut slices={channels} size={96} />
                      <div style={{ display: "flex", flexDirection: "column", gap: 10, flex: 1, minWidth: 0 }}>
                        {(channels.length ? channels : [{ label: "No orders", v: 0, color: "#7d8472" }]).map(c => (
                          <div key={c.label} className="spread" style={{ padding: "3px 0" }}>
                            <span className="row" style={{ gap: 8 }}>
                              <span style={{ background: c.color, width: 12, height: 12, borderRadius: 3, flexShrink: 0 }} />
                              <span style={{ fontSize: 18 }}>{c.label}</span>
                            </span>
                            <span className="mono" style={{ fontSize: 18, fontWeight: 700, color: "var(--green-800)" }}>{c.v}</span>
                          </div>
                        ))}
                      </div>
                    </div>
                  </div>
              </div>
            </div>
          </div>

          {/* Heatmap — hidden, not deleted. Flip SHOW_HEATMAP to restore. */}
          {SHOW_HEATMAP && (
          <div className="report-strip" style={{ padding: "14px 18px 16px" }}>
            <div style={{ marginBottom: 8 }}>
              <div className="report-eyebrow">Customer order heatmap</div>
            </div>
            <div style={ctrlRow}>
              <div className="search" style={ctrlSearch}>
                <Ic name="search" size={14} />
                <input placeholder="Search customers…" value={heatSearch} onChange={e => setHeatSearch(e.target.value)} />
                {heatSearch && (
                  <button className="icon-btn" style={{ width: 22, height: 22, border: "none", background: "transparent", padding: 0 }} onClick={() => setHeatSearch("")}>
                    <Ic name="x" size={12} />
                  </button>
                )}
              </div>
              {[{ id: "total", label: "Total" }, { id: "name", label: "A–Z" }, { id: "added", label: "Added" }, { id: "scheduled", label: "Pickup/delivery" }].map(s => (
                <button key={s.id} style={segBtn(heatSortField === s.id)} onClick={() => setHeatSortField(s.id)}>{s.label}</button>
              ))}
              <button className="btn btn-ghost" style={{ ...dirBtn, minWidth: 36, padding: "0 10px" }} onClick={() => setHeatSortDir(d => d === "asc" ? "desc" : "asc")} title={heatSortDir === "asc" ? "Ascending" : "Descending"}>
                {heatSortDir === "asc" ? "↑" : "↓"}
              </button>
              <button
                style={{ ...segBtn(heatHideCompleted), fontSize: 11, padding: "5px 10px" }}
                onClick={() => setHeatHideCompleted(v => !v)}
                title={heatHideCompleted ? "Show completed orders" : "Hide completed orders"}
              >
                {heatHideCompleted ? "Completed hidden" : "Hide completed"}
              </button>
              <button
                style={{ ...segBtn(heatReorderMode), fontSize: 11, padding: "5px 10px" }}
                onClick={() => setHeatReorderMode(m => !m)}
                title={heatReorderMode ? "Exit reorder mode" : "Reorder columns by dragging"}
              >
                ⇄ Move columns
              </button>
              {heatReorderMode && heatColOrder && (
                <button className="btn btn-ghost" style={{ ...dirBtn, fontSize: 11, padding: "5px 10px" }} onClick={() => setHeatColOrder(null)}>
                  Reset order
                </button>
              )}
            </div>
            <div style={{ overflowX: "auto", borderRadius: "var(--r-lg)", border: "1px solid var(--line-gold)" }}>
              {heatCustomers.length === 0 ? (
                <div style={{ padding: "32px 20px", textAlign: "center", color: "var(--ink-400)", fontSize: 13, fontStyle: "italic" }}>
                  No orders yet
                </div>
              ) : (
                <table style={{ borderCollapse: "collapse", fontSize: 12, width: "100%" }}>
                  <thead>
                    {/* Row 1 — images (drag handles live here) */}
                    <tr>
                      <th style={{ ...heatThStyle, padding: "5px 10px" }} />
                      {heatItems.map(({ m }) => {
                        const isDragOver = heatDragOver === m.id;
                        return (
                          <th key={m.id}
                            draggable={heatReorderMode}
                            style={{ ...heatThStyle, padding: "6px 4px 2px", textAlign: "center", minWidth: 48, cursor: heatReorderMode ? "grab" : "default", userSelect: heatReorderMode ? "none" : "auto", outline: isDragOver ? "2px solid var(--gold-500)" : "none", outlineOffset: -2, transition: "outline .1s" }}
                            onDragStart={heatReorderMode ? () => { heatDragSrc.current = m.id; } : undefined}
                            onDragOver={heatReorderMode ? e => { e.preventDefault(); setHeatDragOver(m.id); } : undefined}
                            onDragLeave={heatReorderMode ? () => setHeatDragOver(null) : undefined}
                            onDrop={heatReorderMode ? () => {
                              setHeatDragOver(null);
                              const srcId = heatDragSrc.current;
                              if (!srcId || srcId === m.id) return;
                              const ids = heatItems.map(r => r.m.id);
                              const from = ids.indexOf(srcId);
                              const to = ids.indexOf(m.id);
                              if (from === -1 || to === -1) return;
                              const next = [...ids];
                              next.splice(from, 1);
                              next.splice(to, 0, srcId);
                              setHeatColOrder(next);
                            } : undefined}
                            onDragEnd={heatReorderMode ? () => { heatDragSrc.current = null; setHeatDragOver(null); } : undefined}
                            onMouseEnter={e => { const r = e.currentTarget.getBoundingClientRect(); setHeatHover({ name: m.name, x: r.left + r.width / 2, y: r.bottom + 6 }); }}
                            onMouseLeave={() => setHeatHover(null)}
                          >
                            <span style={{ width: 40, height: 40, borderRadius: 8, background: "var(--cream-100)", border: "1px solid var(--line-gold)", display: "inline-grid", placeItems: "center", fontSize: 15, overflow: "hidden", position: "relative" }}>
                              {m.image_url
                                ? <MenuCachedImage itemId={m.id} imageUrl={m.image_url} alt={m.name} loading="eager" style={{ position: "absolute", inset: 0, width: "100%", height: "100%", objectFit: "contain", padding: 4, transform: `scale(${Number(m.image_zoom || 1)})`, transformOrigin: "center" }} />
                                : m.glyph}
                            </span>
                          </th>
                        );
                      })}
                      <th style={{ ...heatThStyle, padding: "6px 10px 2px", textAlign: "right", whiteSpace: "nowrap" }}>Total</th>
                    </tr>
                    {/* Row 2 — names */}
                    <tr style={{ borderBottom: "2px solid var(--line-gold)" }}>
                      <th style={{ ...heatThStyle, padding: "2px 10px 6px", textAlign: "left", whiteSpace: "nowrap" }}>Customer</th>
                      {heatItems.map(({ m }) => (
                        <th key={m.id} style={{ ...heatThStyle, padding: "2px 4px 6px", textAlign: "center" }}>
                          <span style={{ display: "-webkit-box", WebkitBoxOrient: "vertical", WebkitLineClamp: 3, overflow: "hidden", fontSize: 9.5, fontWeight: 600, color: "var(--green-800)", maxWidth: 64, lineHeight: 1.35, wordBreak: "break-word", textTransform: "capitalize", textAlign: "center", margin: "0 auto" }}>
                            {m.name.toLowerCase()}
                          </span>
                        </th>
                      ))}
                      <th style={{ ...heatThStyle, padding: "2px 10px 6px" }} />
                    </tr>
                  </thead>
                  <tbody>
                    {sortedHeatCustomers.map(c => {
                      const rowTotal = heatItems.reduce((s, r) => s + (c.items[r.key] || 0), 0);
                      const allCompleted = !heatHideCompleted && c.orderCount > 0 && c.completedCount === c.orderCount;
                      const someCompleted = !heatHideCompleted && c.completedCount > 0 && c.completedCount < c.orderCount;
                      return (
                        <tr key={c.name} style={{ borderTop: "1px solid var(--line-soft)" }}>
                          <td style={{ padding: "4px 10px", background: allCompleted ? "rgba(180,60,60,0.07)" : someCompleted ? "rgba(180,60,60,0.04)" : "transparent" }}>
                            <div style={{ maxWidth: 180, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap", fontSize: 12, color: allCompleted ? "#8b2020" : someCompleted ? "#a03030" : "var(--green-800)", fontWeight: 500, display: "flex", alignItems: "center", gap: 5 }}>
                              {allCompleted && <span style={{ fontSize: 9, color: "#b94040", fontWeight: 800 }}>✓</span>}
                              {someCompleted && <span style={{ width: 6, height: 6, borderRadius: "50%", background: "#c05050", flexShrink: 0, display: "inline-block" }} />}
                              {c.name}
                            </div>
                          </td>
                          {heatItems.map(({ m, key }) => {
                            const val = c.items[key] || 0;
                            const bg = allCompleted ? heatBgCompleted(val) : someCompleted ? (val ? `rgba(150,50,50,${(0.07 + (val / heatMax) * 0.4).toFixed(2)})` : "rgba(180,60,60,0.04)") : heatBg(val);
                            const fg = allCompleted ? (val ? heatFgCompleted(val) : "rgba(139,32,32,0.4)") : someCompleted ? (val ? "#8b2020" : "rgba(139,32,32,0.3)") : (val ? heatFg(val) : "var(--ink-300)");
                            return (
                              <td key={key} style={{ padding: "4px 3px", textAlign: "center", background: bg, color: fg, fontFamily: "var(--font-mono)", fontSize: 11.5, fontWeight: val ? 700 : 400, transition: "background .15s" }}>
                                {val || "·"}
                              </td>
                            );
                          })}
                          <td style={{ padding: "4px 10px", textAlign: "right", fontFamily: "var(--font-mono)", fontSize: 11.5, fontWeight: 700, color: heatTotalFg(rowTotal, heatRowMax), borderLeft: "1px solid var(--line-soft)", background: heatTotalBg(rowTotal, heatRowMax) }}>
                            {rowTotal}
                          </td>
                        </tr>
                      );
                    })}
                  </tbody>
                  <tfoot>
                    <tr style={{ borderTop: "2px solid var(--line-gold)" }}>
                      <td style={{ padding: "5px 10px", fontSize: 10.5, letterSpacing: ".1em", textTransform: "uppercase", color: "var(--ink-500)", fontWeight: 600, fontFamily: "var(--font-body)" }}>Total</td>
                      {heatItems.map(({ m }) => {
                        const colTotal = heatColTotals[menuKey(m)] || 0;
                        return (
                          <td key={m.id} style={{ padding: "5px 3px", textAlign: "center", fontFamily: "var(--font-mono)", fontSize: 11.5, fontWeight: 700, color: heatTotalFg(colTotal, heatColMax), background: heatTotalBg(colTotal, heatColMax) }}>
                            {colTotal}
                          </td>
                        );
                      })}
                      <td style={{ padding: "5px 10px", textAlign: "right", fontFamily: "var(--font-mono)", fontSize: 11.5, fontWeight: 700, color: heatTotalFg(heatGrandTotal, Math.max(1, heatGrandTotal)), borderLeft: "1px solid var(--line-soft)", background: heatTotalBg(heatGrandTotal, Math.max(1, heatGrandTotal)) }}>
                        {heatGrandTotal}
                      </td>
                    </tr>
                  </tfoot>
                </table>
              )}
            </div>
            {heatHover && ReactDOM.createPortal(
              <div style={{ position: "fixed", top: heatHover.y, left: heatHover.x, transform: "translateX(-50%)", background: "var(--green-800)", color: "white", padding: "4px 10px", borderRadius: 6, fontSize: 12, fontWeight: 500, pointerEvents: "none", zIndex: 9999, whiteSpace: "nowrap", boxShadow: "0 2px 8px rgba(0,0,0,0.22)" }}>
                {heatHover.name}
              </div>,
              document.body
            )}
          </div>
          )}
          </React.Fragment>
        );
      })()}
    </div>
  );
};

window.Dashboard = Dashboard;
