// This file is loaded via Babel Standalone directly in the browser (no build
// step). React, ReactDOM, Chart.js and XLSX come from the CDN <script> tags
// in index.html as browser globals.
const { useState, useEffect, useCallback, useMemo, useRef } = React;

// ---------- tiny icon set (replaces lucide-react, no extra dependency) ----------
const ICONS = {
  Plus: "+", Trash2: "🗑", Upload: "⬆", Lock: "🔒", LayoutDashboard: "📊",
  UtensilsCrossed: "🍴", Receipt: "🧾", CreditCard: "💳", Share2: "🔗",
  Search: "🔍", Check: "✓", X: "✕", ChevronLeft: "‹", ShoppingBasket: "🧺",
  Clock: "🕐", MapPin: "📍", Phone: "📞", User: "👤", Sheet: "📗",
  Printer: "🖨", RefreshCw: "↻", PackageCheck: "📦", PackageX: "📪", Eye: "👁",
};
function Icon({ name, size = 16, className = "" }) {
  return (
    <span className={`icon ${className}`} style={{ fontSize: size, lineHeight: 1, display: "inline-flex", verticalAlign: "middle" }}>
      {ICONS[name] || "•"}
    </span>
  );
}

// ---------- storage: talks to the Cloudflare Pages Function backed by KV ----------
const storage = (() => {
  const BASE = "/api/kv";
  async function get(key) {
    const res = await fetch(`${BASE}?action=get&key=${encodeURIComponent(key)}`);
    if (res.status === 404) return null;
    if (!res.ok) throw new Error(`storage.get failed (${res.status})`);
    return res.json();
  }
  async function set(key, value) {
    const res = await fetch(BASE, {
      method: "POST",
      headers: { "content-type": "application/json" },
      body: JSON.stringify({ action: "set", key, value }),
    });
    if (!res.ok) throw new Error(`storage.set failed (${res.status})`);
    return res.json();
  }
  async function del(key) {
    const res = await fetch(BASE, {
      method: "POST",
      headers: { "content-type": "application/json" },
      body: JSON.stringify({ action: "delete", key }),
    });
    if (!res.ok) throw new Error(`storage.delete failed (${res.status})`);
    return res.json();
  }
  async function list(prefix = "") {
    const res = await fetch(`${BASE}?action=list&prefix=${encodeURIComponent(prefix)}`);
    if (!res.ok) throw new Error(`storage.list failed (${res.status})`);
    return res.json();
  }
  return { get: (k) => get(k), set: (k, v) => set(k, v), delete: (k) => del(k), list: (p) => list(p) };
})();

// Change this PIN before sharing the link publicly — it is never shown on screen.
const ADMIN_PIN = "1234";

const LOGO_SRC = "data:image/jpeg;base64,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";

// ---------- helpers ----------

const SR = (n) => `$${Number(n || 0).toLocaleString("en-US")}`;

const uid = () => `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;

function resizeImage(file, maxWidth = 700, quality = 0.72) {
  return new Promise((resolve, reject) => {
    const reader = new FileReader();
    reader.onload = (e) => {
      const img = new Image();
      img.onload = () => {
        const scale = Math.min(1, maxWidth / img.width);
        const canvas = document.createElement("canvas");
        canvas.width = img.width * scale;
        canvas.height = img.height * scale;
        const ctx = canvas.getContext("2d");
        ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
        resolve(canvas.toDataURL("image/jpeg", quality));
      };
      img.onerror = reject;
      img.src = e.target.result;
    };
    reader.onerror = reject;
    reader.readAsDataURL(file);
  });
}

function todayStr() {
  return new Date().toISOString().slice(0, 10);
}

function fmtDate(iso) {
  if (!iso) return "—";
  try {
    return new Date(iso).toLocaleDateString("en-US", { day: "numeric", month: "short" });
  } catch {
    return iso;
  }
}

function fmtDateTime(iso) {
  if (!iso) return "—";
  try {
    return new Date(iso).toLocaleString("en-US", { day: "numeric", month: "short", hour: "2-digit", minute: "2-digit" });
  } catch {
    return iso;
  }
}

const DEFAULT_CONFIG = {
  orderOpen: true,
  menu: [],
  payment: { stcNumber: "", bankDetails: "", qrImage: "" },
};

const PAYMENT_LABELS = { unpaid: "Unpaid", pending: "Awaiting confirmation", paid: "Paid" };
const STATUS_LABELS = { pending: "Pending", preparing: "Preparing", ready: "Ready", delivered: "Delivered", cancelled: "Cancelled" };
const PAYMENT_COLORS = { unpaid: "#8C2F2F", pending: "#B08D4E", paid: "#1F4A34" };

// ---------- storage layer ----------

async function loadConfig() {
  try {
    const res = await storage.get("shop-config", true);
    return res ? JSON.parse(res.value) : DEFAULT_CONFIG;
  } catch {
    return DEFAULT_CONFIG;
  }
}

async function saveConfig(cfg) {
  await storage.set("shop-config", JSON.stringify(cfg), true);
}

async function loadOrders() {
  try {
    const list = await storage.list("order:", true);
    if (!list || !list.keys || list.keys.length === 0) return [];
    const results = await Promise.all(
      list.keys.map(async (k) => {
        try {
          const r = await storage.get(k, true);
          return r ? JSON.parse(r.value) : null;
        } catch {
          return null;
        }
      })
    );
    return results.filter(Boolean).sort((a, b) => (b.createdAt || "").localeCompare(a.createdAt || ""));
  } catch {
    return [];
  }
}

async function saveOrder(order) {
  await storage.set(`order:${order.id}`, JSON.stringify(order), true);
}

// ---------- shared UI bits ----------

function StampBadge({ qty }) {
  const out = qty <= 0;
  return (
    <div className={`stamp ${out ? "stamp-out" : ""}`}>
      <span>{out ? "Sold out" : `${qty} left`}</span>
    </div>
  );
}

function Toast({ text, onDone }) {
  useEffect(() => {
    const t = setTimeout(onDone, 2600);
    return () => clearTimeout(t);
  }, [onDone]);
  return <div className="toast">{text}</div>;
}

// ================= CUSTOMER VIEW =================

function CustomerView({ config, refreshConfig }) {
  const [cart, setCart] = useState({});
  const [checkoutOpen, setCheckoutOpen] = useState(false);
  const [confirmed, setConfirmed] = useState(null);
  const [toast, setToast] = useState(null);
  const [lookupPhone, setLookupPhone] = useState("");
  const [lookupResults, setLookupResults] = useState(null);
  const [lookupLoading, setLookupLoading] = useState(false);
  const [form, setForm] = useState({ name: "", phone: "", location: "", notes: "", paymentMethod: "" });
  const [submitting, setSubmitting] = useState(false);

  const menu = config.menu || [];
  const cartItems = menu.filter((m) => cart[m.id] > 0);
  const total = cartItems.reduce((s, m) => s + m.price * cart[m.id], 0);
  const totalQty = cartItems.reduce((s, m) => s + cart[m.id], 0);

  const setQty = (item, qty) => {
    const capped = Math.max(0, Math.min(qty, item.qty));
    setCart((c) => ({ ...c, [item.id]: capped }));
  };

  const submitOrder = async () => {
    if (!form.name.trim() || !form.phone.trim() || !form.location.trim()) {
      setToast("Please fill in your name, phone, and delivery location");
      return;
    }
    if (!form.paymentMethod) {
      setToast("Please choose a payment method");
      return;
    }
    setSubmitting(true);
    try {
      const fresh = await loadConfig();
      const items = cartItems.map((m) => {
        const liveItem = fresh.menu.find((x) => x.id === m.id) || m;
        return { menuId: m.id, name: m.name, price: m.price, qty: cart[m.id], available: liveItem.qty };
      });
      const insufficient = items.find((it) => it.qty > it.available);
      if (insufficient) {
        setToast(`Sorry, only ${insufficient.available} of "${insufficient.name}" left now`);
        await refreshConfig();
        setSubmitting(false);
        return;
      }
      const newMenu = fresh.menu.map((m) => {
        const it = items.find((i) => i.menuId === m.id);
        return it ? { ...m, qty: m.qty - it.qty } : m;
      });
      const newConfig = { ...fresh, menu: newMenu };
      await saveConfig(newConfig);
      const order = {
        id: uid(),
        name: form.name.trim(),
        phone: form.phone.trim(),
        location: form.location.trim(),
        notes: form.notes.trim(),
        paymentMethod: form.paymentMethod,
        items: items.map(({ menuId, name, price, qty }) => ({ menuId, name, price, qty })),
        total: items.reduce((s, i) => s + i.price * i.qty, 0),
        paymentStatus: "unpaid",
        orderStatus: "pending",
        receiptImage: "",
        createdAt: new Date().toISOString(),
      };
      await saveOrder(order);
      await refreshConfig();
      setConfirmed(order);
      setCart({});
      setCheckoutOpen(false);
    } catch (e) {
      setToast("Something went wrong, please try again");
    }
    setSubmitting(false);
  };

  const uploadReceipt = async (orderId, file) => {
    const dataUrl = await resizeImage(file, 800, 0.7);
    const res = await storage.get(`order:${orderId}`, true);
    if (!res) return;
    const order = JSON.parse(res.value);
    order.receiptImage = dataUrl;
    order.paymentStatus = "pending";
    await saveOrder(order);
    if (confirmed && confirmed.id === orderId) setConfirmed(order);
    setLookupResults((prev) => prev && prev.map((o) => (o.id === orderId ? order : o)));
    setToast("Receipt uploaded — awaiting payment confirmation");
  };

  const doLookup = async () => {
    if (!lookupPhone.trim()) return;
    setLookupLoading(true);
    const all = await loadOrders();
    setLookupResults(all.filter((o) => o.phone.includes(lookupPhone.trim())));
    setLookupLoading(false);
  };

  if (confirmed) {
    return (
      <div className="page">
        <ConfirmationScreen order={confirmed} payment={config.payment} onUpload={uploadReceipt} onBack={() => setConfirmed(null)} />
        {toast && <Toast text={toast} onDone={() => setToast(null)} />}
      </div>
    );
  }

  return (
    <div className="page">
      <div className="hero">
        <img src={LOGO_SRC} alt="Heritage Bites" className="hero-logo" />
        <div className="hero-eyebrow">This week's menu</div>
        <h1>Order your favourite dish</h1>
        <p className="hero-sub">Pick your dishes, fill in your details, and we'll deliver right on time.</p>
      </div>

      {!config.orderOpen && (
        <div className="banner-closed">
          <Icon name="PackageX" size={20} />
          <span>Ordering is closed right now — check back soon for the new menu</span>
        </div>
      )}

      {config.orderOpen && menu.length === 0 && (
        <div className="empty-state">No dishes have been added yet. Check back soon.</div>
      )}

      <div className="menu-grid">
        {menu.map((item) => (
          <div className="dish-card" key={item.id}>
            <div className="dish-photo-wrap">
              {item.photo ? <img src={item.photo} alt={item.name} /> : <div className="dish-photo-placeholder"><Icon name="UtensilsCrossed" size={28} /></div>}
              <StampBadge qty={item.qty} />
            </div>
            <div className="dish-body">
              <div className="dish-top">
                <h3>{item.name}</h3>
                <span className="dish-price">{SR(item.price)}</span>
              </div>
              {(item.deliveryDate) && (
                <div className="dish-meta"><Icon name="Clock" size={13} /> Delivery {fmtDate(item.deliveryDate)}{item.deliveryTime ? ` — ${item.deliveryTime}` : ""}</div>
              )}
              {config.orderOpen && (
                <div className="qty-row">
                  <button className="qty-btn" disabled={!cart[item.id]} onClick={() => setQty(item, (cart[item.id] || 0) - 1)}>−</button>
                  <span className="qty-val">{cart[item.id] || 0}</span>
                  <button className="qty-btn" disabled={item.qty <= 0 || (cart[item.id] || 0) >= item.qty} onClick={() => setQty(item, (cart[item.id] || 0) + 1)}>+</button>
                </div>
              )}
            </div>
          </div>
        ))}
      </div>

      <div className="lookup-block">
        <h4>Track your order</h4>
        <div className="lookup-row">
          <input placeholder="Phone number" value={lookupPhone} onChange={(e) => setLookupPhone(e.target.value)} />
          <button className="btn-outline" onClick={doLookup}><Icon name="Search" size={16} /> Search</button>
        </div>
        {lookupLoading && <div className="muted">Searching...</div>}
        {lookupResults && lookupResults.length === 0 && <div className="muted">No orders found for this number</div>}
        {lookupResults && lookupResults.map((o) => (
          <div className="lookup-card" key={o.id}>
            <div className="lookup-card-head">
              <span>{o.items.map((i) => `${i.name} ×${i.qty}`).join(", ")}</span>
              <b>{SR(o.total)}</b>
            </div>
            <div className="lookup-tags">
              <span className="tag" style={{ background: PAYMENT_COLORS[o.paymentStatus] }}>{PAYMENT_LABELS[o.paymentStatus]}</span>
              <span className="tag tag-dark">{STATUS_LABELS[o.orderStatus]}</span>
            </div>
            {o.paymentStatus === "unpaid" && (
              <label className="upload-mini">
                <Icon name="Upload" size={14} /> Upload payment receipt
                <input type="file" accept="image/*" hidden onChange={(e) => e.target.files[0] && uploadReceipt(o.id, e.target.files[0])} />
              </label>
            )}
          </div>
        ))}
      </div>

      {cartItems.length > 0 && !checkoutOpen && (
        <div className="cart-bar" onClick={() => setCheckoutOpen(true)}>
          <div><Icon name="ShoppingBasket" size={18} /> {totalQty} items</div>
          <div className="cart-bar-total">{SR(total)}</div>
          <div className="cart-bar-cta">Checkout <Icon name="ChevronLeft" size={16} /></div>
        </div>
      )}

      {checkoutOpen && (
        <div className="modal-overlay" onClick={() => setCheckoutOpen(false)}>
          <div className="modal" onClick={(e) => e.stopPropagation()}>
            <div className="modal-head">
              <h3>Checkout</h3>
              <button className="icon-btn" onClick={() => setCheckoutOpen(false)}><Icon name="X" size={18} /></button>
            </div>
            <div className="order-summary">
              {cartItems.map((m) => (
                <div className="summary-line" key={m.id}>
                  <span>{m.name} ×{cart[m.id]}</span>
                  <span>{SR(m.price * cart[m.id])}</span>
                </div>
              ))}
              <div className="summary-line summary-total"><span>Total</span><span>{SR(total)}</span></div>
            </div>
            <div className="form-grid">
              <label><Icon name="User" size={14} /> Name<input value={form.name} onChange={(e) => setForm({ ...form, name: e.target.value })} placeholder="Full name" /></label>
              <label><Icon name="Phone" size={14} /> Phone number<input value={form.phone} onChange={(e) => setForm({ ...form, phone: e.target.value })} placeholder="e.g. 555 0100" /></label>
              <label><Icon name="MapPin" size={14} /> Delivery location<input value={form.location} onChange={(e) => setForm({ ...form, location: e.target.value })} placeholder="Neighbourhood / city" /></label>
              <label>Notes<textarea value={form.notes} onChange={(e) => setForm({ ...form, notes: e.target.value })} placeholder="Optional" /></label>
              <label>Payment method
                <div className="pay-options">
                  {config.payment.stcNumber && (
                    <button type="button" className={`pay-opt ${form.paymentMethod === "stc" ? "active" : ""}`} onClick={() => setForm({ ...form, paymentMethod: "stc" })}>Mobile pay</button>
                  )}
                  {config.payment.bankDetails && (
                    <button type="button" className={`pay-opt ${form.paymentMethod === "bank" ? "active" : ""}`} onClick={() => setForm({ ...form, paymentMethod: "bank" })}>Bank transfer</button>
                  )}
                  {!config.payment.stcNumber && !config.payment.bankDetails && <span className="muted">Payment methods not set up yet</span>}
                </div>
              </label>
            </div>
            <button className="btn-primary btn-full" disabled={submitting} onClick={submitOrder}>
              {submitting ? "Submitting..." : "Confirm order"}
            </button>
          </div>
        </div>
      )}
      {toast && <Toast text={toast} onDone={() => setToast(null)} />}
    </div>
  );
}

function ConfirmationScreen({ order, payment, onUpload, onBack }) {
  const [uploading, setUploading] = useState(false);
  const handleFile = async (file) => {
    setUploading(true);
    await onUpload(order.id, file);
    setUploading(false);
  };
  return (
    <div className="confirm-screen">
      <div className="confirm-badge"><Icon name="Check" size={26} /></div>
      <h2>Order received!</h2>
      <p className="muted">Order #{order.id.slice(0, 8)}</p>
      <div className="ticket">
        {order.items.map((i) => (
          <div className="summary-line" key={i.menuId}><span>{i.name} ×{i.qty}</span><span>{SR(i.price * i.qty)}</span></div>
        ))}
        <div className="summary-line summary-total"><span>Total</span><span>{SR(order.total)}</span></div>
      </div>

      {order.paymentStatus === "unpaid" ? (
        <div className="pay-instructions">
          <h4>Complete payment</h4>
          {order.paymentMethod === "stc" && payment.stcNumber && <p>Send mobile pay to: <b>{payment.stcNumber}</b></p>}
          {order.paymentMethod === "bank" && payment.bankDetails && <p style={{ whiteSpace: "pre-line" }}>{payment.bankDetails}</p>}
          {payment.qrImage && <img className="qr-img" src={payment.qrImage} alt="QR code" />}
          <label className="btn-primary upload-btn">
            {uploading ? "Uploading..." : (<><Icon name="Upload" size={16} /> Paid — upload receipt</>)}
            <input type="file" accept="image/*" hidden disabled={uploading} onChange={(e) => e.target.files[0] && handleFile(e.target.files[0])} />
          </label>
        </div>
      ) : (
        <div className="pay-done"><Icon name="PackageCheck" size={18} /> Receipt received — awaiting confirmation</div>
      )}
      <button className="btn-outline" onClick={onBack}>Back to menu</button>
    </div>
  );
}

// ================= ADMIN VIEW =================

function AdminGate({ onUnlock }) {
  const [pin, setPin] = useState("");
  const [err, setErr] = useState(false);
  return (
    <div className="gate">
      <div className="gate-card">
        <div className="gate-icon"><Icon name="Lock" size={22} /></div>
        <h3>Admin sign-in</h3>
        <input
          type="password"
          placeholder="Enter PIN"
          value={pin}
          onChange={(e) => { setPin(e.target.value); setErr(false); }}
          onKeyDown={(e) => e.key === "Enter" && (pin === ADMIN_PIN ? onUnlock() : setErr(true))}
        />
        <button className="btn-primary btn-full" onClick={() => (pin === ADMIN_PIN ? onUnlock() : setErr(true))}>Enter</button>
        {err && <div className="err-text">Incorrect PIN</div>}
      </div>
    </div>
  );
}

function AdminView({ config, setConfig, refreshConfig }) {
  const [tab, setTab] = useState("dashboard");
  const [orders, setOrders] = useState([]);
  const [loadingOrders, setLoadingOrders] = useState(true);
  const [toast, setToast] = useState(null);

  const refreshOrders = useCallback(async () => {
    setLoadingOrders(true);
    setOrders(await loadOrders());
    setLoadingOrders(false);
  }, []);

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

  const patchOrder = async (id, patch) => {
    const o = orders.find((x) => x.id === id);
    if (!o) return;
    const updated = { ...o, ...patch };
    await saveOrder(updated);
    setOrders((prev) => prev.map((x) => (x.id === id ? updated : x)));
  };

  const tabs = [
    { id: "dashboard", label: "Dashboard", icon: "LayoutDashboard" },
    { id: "menu", label: "Weekly menu", icon: "UtensilsCrossed" },
    { id: "orders", label: "Orders", icon: "Receipt" },
    { id: "payment", label: "Payment", icon: "CreditCard" },
    { id: "share", label: "Share link", icon: "Share2" },
  ];

  return (
    <div className="admin">
      <div className="admin-tabs">
        {tabs.map((t) => (
          <button key={t.id} className={`admin-tab ${tab === t.id ? "active" : ""}`} onClick={() => setTab(t.id)}>
            <Icon name={t.icon} size={16} /> {t.label}
          </button>
        ))}
      </div>

      {tab === "dashboard" && <DashboardTab orders={orders} loading={loadingOrders} onRefresh={refreshOrders} />}
      {tab === "menu" && <MenuTab config={config} setConfig={setConfig} refreshConfig={refreshConfig} setToast={setToast} />}
      {tab === "orders" && <OrdersTab orders={orders} loading={loadingOrders} onRefresh={refreshOrders} patchOrder={patchOrder} />}
      {tab === "payment" && <PaymentTab config={config} setConfig={setConfig} refreshConfig={refreshConfig} setToast={setToast} />}
      {tab === "share" && <ShareTab config={config} />}

      {toast && <Toast text={toast} onDone={() => setToast(null)} />}
    </div>
  );
}

function StatCard({ label, value, accent }) {
  return (
    <div className="stat-card">
      <div className="stat-value" style={accent ? { color: accent } : undefined}>{value}</div>
      <div className="stat-label">{label}</div>
    </div>
  );
}

function BarChartCanvas({ data, labelKey, valueKey, color }) {
  const canvasRef = useRef(null);
  const chartRef = useRef(null);
  useEffect(() => {
    if (!canvasRef.current) return;
    if (chartRef.current) chartRef.current.destroy();
    chartRef.current = new Chart(canvasRef.current, {
      type: "bar",
      data: {
        labels: data.map((d) => d[labelKey]),
        datasets: [{ data: data.map((d) => d[valueKey]), backgroundColor: color, borderRadius: 6, maxBarThickness: 40 }],
      },
      options: {
        plugins: { legend: { display: false } },
        scales: { y: { beginAtZero: true, ticks: { precision: 0 } } },
      },
    });
    return () => chartRef.current && chartRef.current.destroy();
  }, [data]);
  return <div style={{ height: 220 }}><canvas ref={canvasRef}></canvas></div>;
}

function LineChartCanvas({ data, labelKey, valueKey, color }) {
  const canvasRef = useRef(null);
  const chartRef = useRef(null);
  useEffect(() => {
    if (!canvasRef.current) return;
    if (chartRef.current) chartRef.current.destroy();
    chartRef.current = new Chart(canvasRef.current, {
      type: "line",
      data: {
        labels: data.map((d) => d[labelKey]),
        datasets: [{ data: data.map((d) => d[valueKey]), borderColor: color, backgroundColor: color, tension: 0.35, pointRadius: 3 }],
      },
      options: {
        plugins: { legend: { display: false } },
        scales: { y: { beginAtZero: true } },
      },
    });
    return () => chartRef.current && chartRef.current.destroy();
  }, [data]);
  return <div style={{ height: 220 }}><canvas ref={canvasRef}></canvas></div>;
}

function DashboardTab({ orders, loading, onRefresh }) {
  const today = todayStr();
  const todayOrders = orders.filter((o) => (o.createdAt || "").slice(0, 10) === today);
  const totalSales = orders.reduce((s, o) => s + o.total, 0);
  const paidCount = orders.filter((o) => o.paymentStatus === "paid").length;
  const unpaidCount = orders.length - paidCount;

  const byProduct = useMemo(() => {
    const map = {};
    orders.forEach((o) => o.items.forEach((i) => { map[i.name] = (map[i.name] || 0) + i.qty; }));
    return Object.entries(map).map(([name, qty]) => ({ name, qty })).sort((a, b) => b.qty - a.qty);
  }, [orders]);

  const byDay = useMemo(() => {
    const map = {};
    orders.forEach((o) => {
      const d = (o.createdAt || "").slice(0, 10);
      map[d] = (map[d] || 0) + o.total;
    });
    return Object.entries(map).sort(([a], [b]) => a.localeCompare(b)).slice(-14).map(([date, sales]) => ({ date: fmtDate(date), sales }));
  }, [orders]);

  return (
    <div className="tab-panel">
      <div className="panel-head">
        <h2>Overview</h2>
        <button className="btn-outline sm" onClick={onRefresh}><Icon name="RefreshCw" size={14} className={loading ? "spin" : ""} /> Refresh</button>
      </div>
      <div className="stat-grid">
        <StatCard label="Orders today" value={todayOrders.length} />
        <StatCard label="Total orders" value={orders.length} />
        <StatCard label="Total sales" value={SR(totalSales)} />
        <StatCard label="Paid" value={paidCount} accent="#1F4A34" />
        <StatCard label="Unpaid" value={unpaidCount} accent="#8C2F2F" />
      </div>

      <div className="chart-block">
        <h4>Orders by dish</h4>
        {byProduct.length === 0 ? <div className="muted">No data yet</div> : (
          <BarChartCanvas data={byProduct} labelKey="name" valueKey="qty" color="#1F4A34" />
        )}
      </div>

      <div className="chart-block">
        <h4>Daily sales</h4>
        {byDay.length === 0 ? <div className="muted">No data yet</div> : (
          <LineChartCanvas data={byDay} labelKey="date" valueKey="sales" color="#B08D4E" />
        )}
      </div>
    </div>
  );
}

function MenuTab({ config, setConfig, setToast }) {
  const [items, setItems] = useState(config.menu);
  const [orderOpen, setOrderOpen] = useState(config.orderOpen);
  const [saving, setSaving] = useState(false);

  useEffect(() => { setItems(config.menu); setOrderOpen(config.orderOpen); }, [config]);

  const addItem = () => setItems((prev) => [...prev, { id: uid(), name: "", price: 0, qty: 0, photo: "", orderDate: todayStr(), deliveryDate: "", deliveryTime: "" }]);
  const updateItem = (id, patch) => setItems((prev) => prev.map((i) => (i.id === id ? { ...i, ...patch } : i)));
  const removeItem = (id) => setItems((prev) => prev.filter((i) => i.id !== id));

  const handlePhoto = async (id, file) => {
    const dataUrl = await resizeImage(file, 700, 0.72);
    updateItem(id, { photo: dataUrl });
  };

  const save = async () => {
    if (items.some((i) => !i.name.trim())) {
      setToast("Give every dish a name before saving");
      return;
    }
    setSaving(true);
    try {
      const cleaned = items.map((i) => ({ ...i, price: Number(i.price) || 0, qty: Number(i.qty) || 0 }));
      const newConfig = { ...config, menu: cleaned, orderOpen };
      await saveConfig(newConfig);
      setConfig(newConfig);
      setToast("Menu saved");
    } catch (e) {
      setToast("Couldn't save — check your connection and try again");
    } finally {
      setSaving(false);
    }
  };

  return (
    <div className="tab-panel">
      <div className="panel-head">
        <h2>Weekly menu</h2>
        <div className="toggle-row">
          <span>{orderOpen ? "Ordering open" : "Ordering closed"}</span>
          <button className={`switch ${orderOpen ? "on" : ""}`} onClick={() => setOrderOpen((v) => !v)}>
            <span className="knob" />
          </button>
        </div>
      </div>

      {items.map((item) => (
        <div className="menu-edit-card" key={item.id}>
          <label className="photo-upload">
            {item.photo ? <img src={item.photo} alt="" /> : <div className="photo-placeholder"><Icon name="Upload" size={20} /><span>Photo</span></div>}
            <input type="file" accept="image/*" hidden onChange={(e) => e.target.files[0] && handlePhoto(item.id, e.target.files[0])} />
          </label>
          <div className="menu-edit-fields">
            <input placeholder="Dish name" value={item.name} onChange={(e) => updateItem(item.id, { name: e.target.value })} />
            <div className="field-row">
              <label>Price<input type="number" value={item.price} onChange={(e) => updateItem(item.id, { price: e.target.value })} /></label>
              <label>Available quantity<input type="number" value={item.qty} onChange={(e) => updateItem(item.id, { qty: e.target.value })} /></label>
            </div>
            <div className="field-row">
              <label>Delivery date<input type="date" value={item.deliveryDate} onChange={(e) => updateItem(item.id, { deliveryDate: e.target.value })} /></label>
              <label>Delivery time<input type="time" value={item.deliveryTime} onChange={(e) => updateItem(item.id, { deliveryTime: e.target.value })} /></label>
            </div>
          </div>
          <button className="icon-btn danger" onClick={() => removeItem(item.id)}><Icon name="Trash2" size={16} /></button>
        </div>
      ))}

      <button className="btn-outline btn-full" onClick={addItem}><Icon name="Plus" size={16} /> Add dish</button>
      <button className="btn-primary btn-full" disabled={saving} onClick={save}>{saving ? "Saving..." : "Save menu"}</button>
    </div>
  );
}

function PaymentTab({ config, setConfig, setToast }) {
  const [stc, setStc] = useState(config.payment.stcNumber);
  const [bank, setBank] = useState(config.payment.bankDetails);
  const [qr, setQr] = useState(config.payment.qrImage);
  const [saving, setSaving] = useState(false);

  useEffect(() => { setStc(config.payment.stcNumber); setBank(config.payment.bankDetails); setQr(config.payment.qrImage); }, [config]);

  const handleQr = async (file) => setQr(await resizeImage(file, 500, 0.8));

  const save = async () => {
    setSaving(true);
    try {
      const newConfig = { ...config, payment: { stcNumber: stc, bankDetails: bank, qrImage: qr } };
      await saveConfig(newConfig);
      setConfig(newConfig);
      setToast("Payment details saved");
    } catch (e) {
      setToast("Couldn't save — check your connection and try again");
    } finally {
      setSaving(false);
    }
  };

  return (
    <div className="tab-panel">
      <h2>Payment methods</h2>
      <label className="stack-label">Mobile pay number<input value={stc} onChange={(e) => setStc(e.target.value)} placeholder="e.g. 555 0100" /></label>
      <label className="stack-label">Bank transfer details<textarea value={bank} onChange={(e) => setBank(e.target.value)} placeholder="Bank name, account/IBAN, account holder" /></label>
      <label className="stack-label">QR code image
        <div className="qr-upload">
          {qr && <img src={qr} alt="QR" className="qr-preview" />}
          <label className="btn-outline upload-inline"><Icon name="Upload" size={14} /> Upload image
            <input type="file" accept="image/*" hidden onChange={(e) => e.target.files[0] && handleQr(e.target.files[0])} />
          </label>
        </div>
      </label>
      <button className="btn-primary btn-full" disabled={saving} onClick={save}>{saving ? "Saving..." : "Save"}</button>
    </div>
  );
}

function ShareTab({ config }) {
  const [copied, setCopied] = useState(false);
  const message = `🍽️ This week's menu is ready!\n\n${(config.menu || []).map((m) => `• ${m.name} — ${SR(m.price)}`).join("\n")}\n\nTo order, open the link:\n[paste this site's link here after publishing]`;

  const copy = () => {
    navigator.clipboard?.writeText(message);
    setCopied(true);
    setTimeout(() => setCopied(false), 2000);
  };

  return (
    <div className="tab-panel">
      <h2>Share the menu</h2>
      <p className="muted">Publish this site (the publish/share button above), then copy the message below and send it to your WhatsApp groups instead of writing it out by hand each week.</p>
      <div className="share-preview">{message}</div>
      <button className="btn-primary btn-full" onClick={copy}>{copied ? "Copied ✓" : "Copy message"}</button>
    </div>
  );
}

function OrdersTab({ orders, loading, onRefresh, patchOrder }) {
  const [query, setQuery] = useState("");
  const [payFilter, setPayFilter] = useState("all");
  const [statusFilter, setStatusFilter] = useState("all");
  const [viewingReceipt, setViewingReceipt] = useState(null);

  const filtered = orders.filter((o) => {
    const q = query.trim();
    const matchesQ = !q || o.name.includes(q) || o.phone.includes(q);
    const matchesPay = payFilter === "all" || o.paymentStatus === payFilter;
    const matchesStatus = statusFilter === "all" || o.orderStatus === statusFilter;
    return matchesQ && matchesPay && matchesStatus;
  });

  const exportExcel = () => {
    const rows = orders.map((o) => ({
      "Name": o.name,
      "Phone": o.phone,
      "Order": o.items.map((i) => `${i.name} ×${i.qty}`).join(", "),
      "Total": o.total,
      "Location": o.location,
      "Payment status": PAYMENT_LABELS[o.paymentStatus],
      "Order status": STATUS_LABELS[o.orderStatus],
      "Date": fmtDateTime(o.createdAt),
    }));
    const ws = XLSX.utils.json_to_sheet(rows);
    const wb = XLSX.utils.book_new();
    XLSX.utils.book_append_sheet(wb, ws, "Orders");
    XLSX.writeFile(wb, `orders-${todayStr()}.xlsx`);
  };

  return (
    <div className="tab-panel">
      <div className="panel-head">
        <h2>Orders</h2>
        <div className="head-actions">
          <button className="btn-outline sm" onClick={onRefresh}><Icon name="RefreshCw" size={14} className={loading ? "spin" : ""} /></button>
          <button className="btn-outline sm" onClick={exportExcel}><Icon name="Sheet" size={14} /> Excel</button>
          <button className="btn-outline sm print-btn" onClick={() => window.print()}><Icon name="Printer" size={14} /> PDF</button>
        </div>
      </div>

      <div className="filters-row no-print">
        <div className="search-box"><Icon name="Search" size={15} /><input placeholder="Search by name or phone" value={query} onChange={(e) => setQuery(e.target.value)} /></div>
        <select value={payFilter} onChange={(e) => setPayFilter(e.target.value)}>
          <option value="all">All payment statuses</option>
          {Object.entries(PAYMENT_LABELS).map(([k, v]) => <option key={k} value={k}>{v}</option>)}
        </select>
        <select value={statusFilter} onChange={(e) => setStatusFilter(e.target.value)}>
          <option value="all">All order statuses</option>
          {Object.entries(STATUS_LABELS).map(([k, v]) => <option key={k} value={k}>{v}</option>)}
        </select>
      </div>

      <div className="orders-table-wrap print-area">
        <table className="orders-table">
          <thead>
            <tr>
              <th>Name</th><th>Phone</th><th>Order</th><th>Location</th><th>Total</th><th>Payment</th><th>Status</th><th className="no-print">Actions</th>
            </tr>
          </thead>
          <tbody>
            {filtered.map((o) => (
              <tr key={o.id}>
                <td>{o.name}</td>
                <td>{o.phone}</td>
                <td>{o.items.map((i) => `${i.name} ×${i.qty}`).join(", ")}</td>
                <td>{o.location}</td>
                <td>{SR(o.total)}</td>
                <td><span className="tag" style={{ background: PAYMENT_COLORS[o.paymentStatus] }}>{PAYMENT_LABELS[o.paymentStatus]}</span></td>
                <td>
                  <select className="status-select" value={o.orderStatus} onChange={(e) => patchOrder(o.id, { orderStatus: e.target.value })}>
                    {Object.entries(STATUS_LABELS).map(([k, v]) => <option key={k} value={k}>{v}</option>)}
                  </select>
                </td>
                <td className="no-print actions-cell">
                  {o.receiptImage && <button className="icon-btn" title="View receipt" onClick={() => setViewingReceipt(o.receiptImage)}><Icon name="Eye" size={15} /></button>}
                  {o.paymentStatus !== "paid" ? (
                    <button className="icon-btn success" title="Confirm payment" onClick={() => patchOrder(o.id, { paymentStatus: "paid" })}><Icon name="Check" size={15} /></button>
                  ) : (
                    <button className="icon-btn" title="Unconfirm payment" onClick={() => patchOrder(o.id, { paymentStatus: "unpaid" })}><Icon name="X" size={15} /></button>
                  )}
                </td>
              </tr>
            ))}
            {filtered.length === 0 && <tr><td colSpan={8} className="muted center">No matching orders</td></tr>}
          </tbody>
        </table>
      </div>

      {viewingReceipt && (
        <div className="modal-overlay" onClick={() => setViewingReceipt(null)}>
          <div className="modal receipt-modal" onClick={(e) => e.stopPropagation()}>
            <img src={viewingReceipt} alt="receipt" />
            <button className="btn-outline btn-full" onClick={() => setViewingReceipt(null)}>Close</button>
          </div>
        </div>
      )}
    </div>
  );
}

// ================= ROOT APP =================

function App() {
  const [view, setView] = useState("customer");
  const [adminUnlocked, setAdminUnlocked] = useState(false);
  const [config, setConfig] = useState(DEFAULT_CONFIG);
  const [ready, setReady] = useState(false);

  const refreshConfig = useCallback(async () => {
    const c = await loadConfig();
    setConfig(c);
    return c;
  }, []);

  useEffect(() => {
    refreshConfig().then(() => setReady(true));
  }, [refreshConfig]);

  return (
    <div className="app-root">
      <style>{CSS}</style>
      <div className="topbar">
        <div className="brand"><img src={LOGO_SRC} alt="" className="brand-logo" /> Heritage Bites</div>
        <div className="view-switch">
          <button className={view === "customer" ? "active" : ""} onClick={() => setView("customer")}>Order</button>
          <button className={view === "admin" ? "active" : ""} onClick={() => setView("admin")}>Admin</button>
        </div>
      </div>

      {!ready ? (
        <div className="loading-screen">Loading...</div>
      ) : view === "customer" ? (
        <CustomerView config={config} refreshConfig={refreshConfig} />
      ) : adminUnlocked ? (
        <AdminView config={config} setConfig={setConfig} refreshConfig={refreshConfig} />
      ) : (
        <AdminGate onUnlock={() => setAdminUnlocked(true)} />
      )}
    </div>
  );
}

// ---------- styles ----------

const CSS = `
@import url('https://fonts.googleapis.com/css2?family=Cormorant+Garamond:wght@600;700&family=Jost:wght@400;500;600&display=swap');

* { box-sizing: border-box; }
.app-root {
  font-family: 'Jost', sans-serif;
  background: #F7F1E3;
  color: #24352B;
  min-height: 100vh;
}
.app-root h1, .app-root h2, .app-root h3, .app-root h4 { font-family: 'Cormorant Garamond', serif; margin: 0; font-weight: 700; }

.topbar {
  display: flex; justify-content: space-between; align-items: center;
  padding: 12px 18px; background: #1F4A34; color: #F7F1E3;
  position: sticky; top: 0; z-index: 20;
}
.brand { display: flex; gap: 8px; align-items: center; font-family: 'Cormorant Garamond'; font-weight: 700; font-size: 19px; letter-spacing: 0.5px; }
.brand-logo { width: 30px; height: 30px; border-radius: 50%; object-fit: cover; border: 1px solid #B08D4E; }
.view-switch { display: flex; background: rgba(255,255,255,0.08); border-radius: 999px; padding: 3px; }
.view-switch button { background: none; border: none; color: #E6DCC3; padding: 6px 16px; border-radius: 999px; font-family: 'Jost'; font-weight: 500; font-size: 13px; cursor: pointer; }
.view-switch button.active { background: #B08D4E; color: #1F4A34; font-weight: 700; }

.page { max-width: 640px; margin: 0 auto; padding: 20px 16px 90px; }
.loading-screen { text-align: center; padding: 60px 0; color: #8a8069; }

.hero { text-align: center; padding: 10px 0 22px; }
.hero-logo { width: 72px; height: 72px; border-radius: 50%; object-fit: cover; border: 2px solid #B08D4E; margin-bottom: 10px; }
.hero-eyebrow { font-size: 12px; letter-spacing: 2.5px; color: #B08D4E; font-weight: 600; text-transform: uppercase; }
.hero h1 { font-size: 28px; margin-top: 6px; color: #1F4A34; }
.hero-sub { color: #7a715a; font-size: 14px; margin-top: 6px; }

.banner-closed { display: flex; gap: 8px; align-items: center; justify-content: center; background: #EFE0BE; color: #7a5a1a; padding: 12px; border-radius: 12px; font-size: 13px; margin-bottom: 16px; }
.empty-state { text-align: center; color: #8a8069; padding: 30px 0; }

.menu-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(230px, 1fr)); gap: 16px; }
.dish-card { background: #fff; border-radius: 16px; overflow: hidden; box-shadow: 0 2px 10px rgba(31,74,52,0.08); border: 1px solid #e9e0c8; }
.dish-photo-wrap { position: relative; height: 140px; background: #EFE0BE; }
.dish-photo-wrap img { width: 100%; height: 100%; object-fit: cover; }
.dish-photo-placeholder { width: 100%; height: 100%; display: flex; align-items: center; justify-content: center; color: #c9b47e; }
.stamp {
  position: absolute; top: 8px; right: 8px; background: #fff; border: 2px dashed #1F4A34; color: #1F4A34;
  border-radius: 999px; padding: 4px 10px; font-size: 10px; font-weight: 700; transform: rotate(6deg);
  letter-spacing: 0.5px;
}
.stamp-out { border-color: #8a8069; color: #8a8069; }
.dish-body { padding: 12px 14px 14px; }
.dish-top { display: flex; justify-content: space-between; align-items: baseline; gap: 8px; }
.dish-top h3 { font-size: 18px; color: #1F4A34; }
.dish-price { font-weight: 600; color: #B08D4E; font-size: 14px; white-space: nowrap; font-family: 'Jost'; }
.dish-meta { display: flex; gap: 4px; align-items: center; color: #8a8069; font-size: 12px; margin-top: 6px; }
.qty-row { display: flex; align-items: center; gap: 12px; margin-top: 10px; justify-content: center; border-top: 1px dashed #e9e0c8; padding-top: 10px; }
.qty-btn { width: 30px; height: 30px; border-radius: 50%; border: 1px solid #1F4A34; background: #fff; color: #1F4A34; font-size: 16px; cursor: pointer; }
.qty-btn:disabled { opacity: 0.3; cursor: default; }
.qty-val { min-width: 20px; text-align: center; font-weight: 700; }

.cart-bar { position: fixed; bottom: 14px; left: 16px; right: 16px; max-width: 608px; margin: 0 auto; background: #1F4A34; color: #fff; border-radius: 14px; padding: 14px 18px; display: flex; justify-content: space-between; align-items: center; cursor: pointer; box-shadow: 0 6px 20px rgba(0,0,0,0.25); z-index: 15; font-size: 13px; }
.cart-bar-total { font-weight: 700; }
.cart-bar-cta { display: flex; align-items: center; gap: 4px; color: #B08D4E; font-weight: 700; }

.modal-overlay { position: fixed; inset: 0; background: rgba(36,53,43,0.5); display: flex; align-items: flex-end; justify-content: center; z-index: 30; }
.modal { background: #F7F1E3; width: 100%; max-width: 480px; border-radius: 20px 20px 0 0; padding: 20px; max-height: 88vh; overflow-y: auto; }
.modal-head { display: flex; justify-content: space-between; align-items: center; margin-bottom: 12px; }
.icon-btn { background: none; border: none; cursor: pointer; padding: 6px; border-radius: 8px; color: #24352B; }
.icon-btn.danger { color: #8C2F2F; }
.icon-btn.success { color: #1F4A34; }
.icon-btn:hover { background: rgba(0,0,0,0.06); }

.order-summary { background: #fff; border-radius: 12px; padding: 12px 14px; margin-bottom: 14px; border: 1px dashed #e9e0c8; }
.summary-line { display: flex; justify-content: space-between; font-size: 13px; padding: 4px 0; }
.summary-total { font-weight: 700; border-top: 1px dashed #ddd; margin-top: 6px; padding-top: 8px; }

.form-grid { display: flex; flex-direction: column; gap: 12px; }
.form-grid label, .stack-label { font-size: 13px; font-weight: 500; color: #5a5340; display: flex; flex-direction: column; gap: 5px; }
input, textarea, select { font-family: 'Jost'; border: 1px solid #e2d7b4; border-radius: 10px; padding: 10px 12px; font-size: 14px; background: #fff; color: #24352B; width: 100%; }
textarea { min-height: 70px; resize: vertical; }
.pay-options { display: flex; gap: 8px; }
.pay-opt { flex: 1; padding: 10px; border: 1px solid #e2d7b4; border-radius: 10px; background: #fff; cursor: pointer; font-family: 'Jost'; font-weight: 500; }
.pay-opt.active { background: #1F4A34; color: #fff; border-color: #1F4A34; }

.btn-primary { background: #1F4A34; color: #fff; border: none; border-radius: 12px; padding: 12px 18px; font-family: 'Jost'; font-weight: 600; font-size: 14px; cursor: pointer; }
.btn-primary:disabled { opacity: 0.6; }
.btn-outline { background: #fff; color: #24352B; border: 1px solid #e2d7b4; border-radius: 12px; padding: 10px 16px; font-family: 'Jost'; font-weight: 500; font-size: 13px; cursor: pointer; display: inline-flex; align-items: center; gap: 6px; }
.btn-outline.sm { padding: 7px 12px; font-size: 12px; }
.btn-full { width: 100%; margin-top: 8px; display: flex; align-items: center; justify-content: center; gap: 6px; }

.lookup-block { margin-top: 30px; padding-top: 20px; border-top: 1px dashed #e2d7b4; }
.lookup-block h4 { margin-bottom: 10px; color: #1F4A34; }
.lookup-row { display: flex; gap: 8px; }
.lookup-card { background: #fff; border-radius: 12px; padding: 10px 12px; margin-top: 10px; border: 1px solid #e9e0c8; }
.lookup-card-head { display: flex; justify-content: space-between; font-size: 13px; }
.lookup-tags { display: flex; gap: 6px; margin-top: 6px; }
.tag { color: #fff; font-size: 11px; padding: 3px 9px; border-radius: 999px; font-weight: 700; }
.tag-dark { background: #24352B; }
.upload-mini { margin-top: 8px; display: inline-flex; align-items: center; gap: 4px; font-size: 12px; color: #1F4A34; cursor: pointer; font-weight: 700; }

.confirm-screen { max-width: 480px; margin: 30px auto; text-align: center; padding: 0 8px; }
.confirm-badge { width: 52px; height: 52px; background: #1F4A34; color: #fff; border-radius: 50%; display: flex; align-items: center; justify-content: center; margin: 0 auto 12px; }
.ticket { background: #fff; border-radius: 14px; padding: 14px 18px; margin: 16px 0; text-align: left; border: 1px dashed #e2d7b4; }
.pay-instructions { background: #fff; border-radius: 14px; padding: 16px; text-align: left; margin-bottom: 14px; }
.qr-img, .qr-preview { max-width: 180px; display: block; margin: 10px auto; border-radius: 10px; }
.upload-btn { display: flex; align-items: center; justify-content: center; gap: 6px; cursor: pointer; margin-top: 10px; }
.pay-done { background: #e6ede8; color: #1F4A34; padding: 12px; border-radius: 12px; display: flex; gap: 8px; align-items: center; justify-content: center; font-weight: 700; font-size: 13px; margin-bottom: 14px; }

.gate { display: flex; align-items: center; justify-content: center; min-height: 70vh; padding: 20px; }
.gate-card { background: #fff; border-radius: 18px; padding: 28px; max-width: 320px; width: 100%; text-align: center; box-shadow: 0 4px 20px rgba(0,0,0,0.06); }
.gate-icon { width: 44px; height: 44px; background: #EFE0BE; color: #1F4A34; border-radius: 50%; display: flex; align-items: center; justify-content: center; margin: 0 auto 12px; }
.gate-card input { margin: 14px 0 10px; text-align: center; }
.err-text { color: #8C2F2F; font-size: 12px; margin-top: 8px; }

.admin { max-width: 760px; margin: 0 auto; padding: 16px 14px 60px; }
.admin-tabs { display: flex; gap: 6px; overflow-x: auto; padding-bottom: 12px; margin-bottom: 6px; }
.admin-tab { flex-shrink: 0; display: flex; align-items: center; gap: 6px; background: #fff; border: 1px solid #e9e0c8; border-radius: 999px; padding: 8px 14px; font-family: 'Jost'; font-size: 12px; font-weight: 500; cursor: pointer; color: #5a5340; }
.admin-tab.active { background: #1F4A34; color: #EFE0BE; border-color: #1F4A34; }

.tab-panel { background: transparent; }
.tab-panel h2 { color: #1F4A34; }
.panel-head { display: flex; justify-content: space-between; align-items: center; margin-bottom: 14px; flex-wrap: wrap; gap: 8px; }
.head-actions { display: flex; gap: 6px; }
.toggle-row { display: flex; align-items: center; gap: 8px; font-size: 13px; font-weight: 500; }
.switch { width: 42px; height: 24px; border-radius: 999px; background: #ddd; border: none; position: relative; cursor: pointer; padding: 0; }
.switch.on { background: #1F4A34; }
.knob { position: absolute; top: 3px; left: 3px; width: 18px; height: 18px; border-radius: 50%; background: #fff; transition: 0.2s; }
.switch.on .knob { left: 21px; }

.stat-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); gap: 10px; margin-bottom: 20px; }
.stat-card { background: #fff; border-radius: 14px; padding: 14px; text-align: center; border: 1px solid #e9e0c8; }
.stat-value { font-family: 'Cormorant Garamond'; font-size: 22px; font-weight: 700; color: #1F4A34; }
.stat-label { font-size: 11px; color: #8a8069; margin-top: 4px; }
.chart-block { background: #fff; border-radius: 14px; padding: 14px; margin-bottom: 16px; border: 1px solid #e9e0c8; }
.chart-block h4 { margin-bottom: 10px; font-size: 15px; color: #1F4A34; }

.menu-edit-card { display: flex; gap: 12px; background: #fff; border-radius: 14px; padding: 12px; margin-bottom: 12px; border: 1px solid #e9e0c8; align-items: flex-start; }
.photo-upload { width: 80px; height: 80px; border-radius: 10px; background: #EFE0BE; flex-shrink: 0; overflow: hidden; cursor: pointer; display: flex; align-items: center; justify-content: center; }
.photo-upload img { width: 100%; height: 100%; object-fit: cover; }
.photo-placeholder { display: flex; flex-direction: column; align-items: center; gap: 4px; color: #a9925c; font-size: 10px; }
.menu-edit-fields { flex: 1; display: flex; flex-direction: column; gap: 8px; }
.field-row { display: flex; gap: 8px; }
.field-row label { flex: 1; font-size: 11px; color: #8a8069; display: flex; flex-direction: column; gap: 3px; }

.qr-upload { display: flex; flex-direction: column; align-items: center; gap: 8px; }
.upload-inline { display: inline-flex; align-items: center; gap: 5px; cursor: pointer; }

.share-preview { background: #fff; border: 1px dashed #e2d7b4; border-radius: 12px; padding: 14px; white-space: pre-line; font-size: 13px; margin-bottom: 12px; }

.filters-row { display: flex; gap: 8px; margin-bottom: 12px; flex-wrap: wrap; }
.search-box { flex: 1; min-width: 160px; display: flex; align-items: center; gap: 6px; background: #fff; border: 1px solid #e2d7b4; border-radius: 10px; padding: 0 10px; }
.search-box input { border: none; padding: 9px 4px; }
.filters-row select { width: auto; font-size: 12px; padding: 9px 10px; }

.orders-table-wrap { overflow-x: auto; background: #fff; border-radius: 14px; border: 1px solid #e9e0c8; }
.orders-table { width: 100%; border-collapse: collapse; font-size: 12px; min-width: 640px; }
.orders-table th { text-align: left; padding: 10px; background: #1F4A34; color: #EFE0BE; font-weight: 600; white-space: nowrap; }
.orders-table td { padding: 9px 10px; border-bottom: 1px solid #f2ecd8; }
.status-select { font-size: 11px; padding: 5px 6px; }
.actions-cell { display: flex; gap: 4px; }
.center { text-align: center; }

.muted { color: #8a8069; font-size: 13px; }
.muted.small { font-size: 11px; margin-top: 10px; }

.toast { position: fixed; bottom: 20px; left: 50%; transform: translateX(-50%); background: #24352B; color: #fff; padding: 12px 20px; border-radius: 999px; font-size: 13px; z-index: 50; box-shadow: 0 4px 16px rgba(0,0,0,0.3); }

.receipt-modal { max-width: 400px; }
.receipt-modal img { width: 100%; border-radius: 10px; margin-bottom: 12px; }

.spin { animation: spin 1s linear infinite; }
@keyframes spin { to { transform: rotate(360deg); } }

@media print {
  .no-print, .topbar, .admin-tabs, .panel-head { display: none !important; }
  .print-area { border: none !important; }
  body, .app-root { background: #fff !important; }
}

@media (max-width: 480px) {
  .hero h1 { font-size: 22px; }
  .menu-grid { grid-template-columns: 1fr 1fr; }
}
`;

ReactDOM.createRoot(document.getElementById("root")).render(<App />);
