/* Main App — renders tabs. Visual styling is driven by a `theme` prop so
   the same app can be shown three ways in the design canvas. */

const {
  LS, DEFAULT_LINEUP, DEFAULT_CHECKLIST, DEFAULT_SUNDAY_CHECKLIST, DEFAULT_OPENING_CHECKLIST,
  DEFAULT_CARNEVALE, DEFAULT_VEGFACTOR, DEFAULT_PILGRIM, DEFAULT_COBBLE_LANE,
  DEFAULT_KATER4, DEFAULT_KILOWINES, DEFAULT_MAJESTIC, DEFAULT_ROTHLEY, DEFAULT_ROUNDCORNER, DEFAULT_AMAZON,
  DEFAULT_FOH_OPENING, DEFAULT_FOH_OPENING_THURSDAY,
  DEFAULT_FOH_CLEANING,
  DEFAULT_FOH_CLOSING, DEFAULT_FOH_CLOSING_THURSDAY, DEFAULT_FOH_END_OF_NIGHT,
  DEFAULT_POTWASH_OPENING, DEFAULT_POTWASH_CLOSING,
  useLocal, useSharedStock,
  parseQty, stockShortfall, stockStatus, buildMailto, fmtDate, nextThursday
} = window;

// =========================================================
//   ICONS
// =========================================================
const Icon = {
  home: (s=20) => <svg width={s} height={s} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M3 10.5 12 3l9 7.5"/><path d="M5 9.5V20a1 1 0 0 0 1 1h4v-6h4v6h4a1 1 0 0 0 1-1V9.5"/></svg>,
  list: (s=20) => <svg width={s} height={s} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M8 6h13"/><path d="M8 12h13"/><path d="M8 18h13"/><circle cx="4" cy="6" r="1"/><circle cx="4" cy="12" r="1"/><circle cx="4" cy="18" r="1"/></svg>,
  broom: (s=20) => <svg width={s} height={s} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="m19.36 2.72 1.42 1.42-5.72 5.73"/><path d="m14.77 10.58-4.36 4.37"/><path d="M3 21s3-1 6-4"/><path d="M14 8l2 2"/><path d="M8 14l-5 5 2 2 5-5"/></svg>,
  box: (s=20) => <svg width={s} height={s} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M3 7v12a1 1 0 0 0 1 1h16a1 1 0 0 0 1-1V7"/><path d="M3 7l9-4 9 4"/><path d="M3 7h18"/><path d="M9 12h6"/></svg>,
  plus: (s=18) => <svg width={s} height={s} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.2" strokeLinecap="round"><path d="M12 5v14M5 12h14"/></svg>,
  trash: (s=16) => <svg width={s} height={s} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M3 6h18"/><path d="M8 6V4a1 1 0 0 1 1-1h6a1 1 0 0 1 1 1v2"/><path d="M6 6l1 14a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1l1-14"/></svg>,
  pencil: (s=16) => <svg width={s} height={s} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M12 20h9"/><path d="M16.5 3.5a2.12 2.12 0 0 1 3 3L7 19l-4 1 1-4z"/></svg>,
  mail: (s=18) => <svg width={s} height={s} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><rect x="2" y="4" width="20" height="16" rx="2"/><path d="m22 7-10 6L2 7"/></svg>,
  back: (s=18) => <svg width={s} height={s} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M15 18l-6-6 6-6"/></svg>,
  check: (s=14) => <svg width={s} height={s} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="3" strokeLinecap="round" strokeLinejoin="round"><path d="M5 12l5 5L20 7"/></svg>,
  flame: (s=20) => <svg width={s} height={s} viewBox="0 0 24 24" fill="currentColor"><path d="M12 2c.3 3.2-1.4 4.8-2.8 6.2-1.4 1.4-3 2.9-3 5.8 0 4.4 3.5 8 8 8s8-3.6 8-8c0-3-1.6-4.6-3-5.7-1.2-.9-1.8-2-1.2-3.8-1.7.3-3.2 1.2-4 2.5-.4-2-1-3.5-2-5z"/></svg>,
  hat: (s=20) => <svg width={s} height={s} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M12 3c1.9 0 3.5 1.35 3.9 3.15a4 4 0 0 1 2.1 7.72V21H6v-7.13a4 4 0 1 1 2.1-7.72A4 4 0 0 1 12 3z"/><path d="M6.2 17h11.6"/></svg>,
  lock: (s=18) => <svg width={s} height={s} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><rect x="5" y="11" width="14" height="9" rx="2"/><path d="M8 11V8a4 4 0 0 1 8 0v3"/></svg>,
  user: (s=18) => <svg width={s} height={s} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><circle cx="12" cy="8" r="4"/><path d="M5 21v-1a7 7 0 0 1 14 0v1"/></svg>,
  alert: (s=18) => <svg width={s} height={s} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M10.3 4.1 2.6 18a2 2 0 0 0 1.7 3h15.4a2 2 0 0 0 1.7-3L13.7 4.1a2 2 0 0 0-3.4 0z"/><path d="M12 9v4"/><path d="M12 17h.01"/></svg>,
  drop: (s=18) => <svg width={s} height={s} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M12 3s6 6.6 6 11a6 6 0 0 1-12 0c0-4.4 6-11 6-11z"/></svg>,
  up: (s=14) => <svg width={s} height={s} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.4" strokeLinecap="round" strokeLinejoin="round"><path d="M18 15l-6-6-6 6"/></svg>,
  down: (s=14) => <svg width={s} height={s} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.4" strokeLinecap="round" strokeLinejoin="round"><path d="M6 9l6 6 6-6"/></svg>,
  logout: (s=18) => <svg width={s} height={s} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M15 3h5a1 1 0 0 1 1 1v16a1 1 0 0 1-1 1h-5"/><path d="M10 17l5-5-5-5"/><path d="M15 12H3"/></svg>,
  clipboard: (s=18) => <svg width={s} height={s} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><rect x="9" y="2" width="6" height="4" rx="1"/><path d="M9 4H7a1 1 0 0 0-1 1v14a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1V5a1 1 0 0 0-1-1h-2"/></svg>
};

function BrandRule({ theme, style = {} }) {
  return (
    <div style={{ display: "flex", alignItems: "center", gap: 12, margin: "0 auto", ...style }}>
      <div style={{ flex: 1, height: 1, background: theme.accent, opacity: 0.4 }}/>
      <span style={{ color: theme.accent, display: "flex", opacity: 0.9 }}>{Icon.hat(14)}</span>
      <div style={{ flex: 1, height: 1, background: theme.accent, opacity: 0.4 }}/>
    </div>
  );
}

function Logo({ theme, size = "md" }) {
  const sz = { sm: { title: 18, sub: 10, gap: 4 }, md: { title: 28, sub: 13, gap: 6 }, lg: { title: 34, sub: 14, gap: 8 } }[size];
  return (
    <div style={{ textAlign: "center", color: theme.logo }}>
      <div style={{
        fontFamily: "'Playfair Display', Georgia, serif",
        fontWeight: 900, fontSize: sz.title, letterSpacing: "0.08em", lineHeight: 1
      }}>CHEF ALWAYS</div>
      <div style={{
        marginTop: sz.gap,
        fontFamily: "'Inter', sans-serif",
        fontWeight: 500, fontSize: sz.sub, letterSpacing: "0.32em",
        color: theme.logoSub
      }}>
        <span style={{ opacity: 0.6 }}>•</span> BRIGADE <span style={{ opacity: 0.6 }}>•</span>
      </div>
    </div>
  );
}

function Progress({ percent, theme }) {
  return (
    <div>
      <div style={{
        display: "flex", justifyContent: "space-between",
        fontSize: 12, fontWeight: 600, color: theme.muted,
        textTransform: "uppercase", letterSpacing: "0.1em", marginBottom: 8
      }}>
        <span>Progress</span><span style={{ color: theme.text }}>{percent}%</span>
      </div>
      <div style={{ height: 8, background: theme.progressTrack, borderRadius: 999, overflow: "hidden" }}>
        <div style={{ width: `${percent}%`, height: "100%", background: theme.accent, transition: "width .35s ease" }}/>
      </div>
    </div>
  );
}

function ChecklistRow({ label, checked, onToggle, onDelete, theme, editing, qty, onQty, onMove }) {
  return (
    <div style={{
      display: "flex", alignItems: "center", gap: 12,
      padding: "12px 14px", background: theme.row, borderRadius: 10,
      border: `1px solid ${theme.rowBorder}`,
      opacity: editing ? 1 : (checked ? 0.55 : 1), transition: "opacity .2s"
    }}>
      <button onClick={onToggle} aria-label="toggle" style={{
        width: 26, height: 26, borderRadius: 8,
        border: `2px solid ${checked ? theme.accent : theme.checkBorder}`,
        background: checked ? theme.accent : "transparent", color: theme.checkMark,
        display: "flex", alignItems: "center", justifyContent: "center",
        cursor: "pointer", flexShrink: 0, padding: 0
      }}>{checked && Icon.check(14)}</button>
      <div style={{
        flex: 1, fontSize: 16, fontWeight: 500, color: theme.text,
        textDecoration: checked ? "line-through" : "none", textDecorationColor: theme.muted
      }}>{label}</div>
      {onQty && (
        <input value={qty || ""} inputMode="numeric" placeholder="0"
          onChange={e => onQty(e.target.value.replace(/[^0-9]/g, "").slice(0, 3))}
          style={{
            width: 46, textAlign: "center", fontSize: 14, fontWeight: 600,
            border: `1px solid ${theme.rowBorder}`, borderRadius: 8, padding: "7px 4px",
            background: theme.input, color: theme.text, fontFamily: "inherit",
            outline: "none", flexShrink: 0
          }}/>
      )}
      {editing && onMove && (
        <>
          <button onClick={() => onMove(-1)} style={iconBtn(theme)} aria-label="Move up">{Icon.up(13)}</button>
          <button onClick={() => onMove(1)} style={iconBtn(theme)} aria-label="Move down">{Icon.down(13)}</button>
        </>
      )}
      {editing && (
        <button onClick={onDelete} style={iconBtn(theme)} aria-label="Delete">{Icon.trash(15)}</button>
      )}
    </div>
  );
}

const iconBtn = (theme) => ({
  width: 30, height: 30, borderRadius: 8,
  border: `1px solid ${theme.rowBorder}`, background: "transparent", color: theme.muted,
  display: "inline-flex", alignItems: "center", justifyContent: "center",
  cursor: "pointer", padding: 0
});

function AddRow({ onAdd, placeholder, theme }) {
  const [val, setVal] = useState("");
  const submit = () => { if (!val.trim()) return; onAdd(val.trim()); setVal(""); };
  return (
    <div style={{ display: "flex", gap: 8, marginTop: 8 }}>
      <input value={val} onChange={e => setVal(e.target.value)}
        onKeyDown={e => e.key === "Enter" && submit()}
        placeholder={placeholder}
        style={{
          flex: 1, padding: "11px 13px", borderRadius: 10,
          border: `1px solid ${theme.rowBorder}`,
          background: theme.input, color: theme.text,
          fontSize: 15, fontFamily: "inherit", outline: "none"
        }}/>
      <button onClick={submit} style={{
        padding: "0 16px", borderRadius: 10, border: "none",
        background: theme.accent, color: theme.accentText,
        fontWeight: 700, fontSize: 14, cursor: "pointer",
        display: "inline-flex", alignItems: "center", gap: 6
      }}>{Icon.plus(16)} Add</button>
    </div>
  );
}

function SectionHead({ children, count, theme, sub }) {
  return (
    <div style={{
      display: "flex", alignItems: "baseline", justifyContent: "space-between",
      marginTop: 22, marginBottom: 10
    }}>
      <div>
        <h3 style={{
          margin: 0, fontFamily: "'Playfair Display', serif",
          fontSize: 20, fontWeight: 700, color: theme.heading
        }}>{children}</h3>
        {sub && <div style={{ fontSize: 11, color: theme.muted, letterSpacing: "0.14em", textTransform: "uppercase", marginTop: 3 }}>{sub}</div>}
      </div>
      {count != null && <span style={{ fontSize: 12, color: theme.muted, letterSpacing: "0.1em" }}>{count}</span>}
    </div>
  );
}

function PageHeader({ title, onBack, editing, setEditing, theme }) {
  const showEdit = typeof setEditing === "function";
  return (
    <div style={{
      position: "sticky", top: 0, zIndex: 5,
      background: theme.headerBg, borderBottom: `1px solid ${theme.rowBorder}`
    }}>
      <div style={{
        display: "flex", alignItems: "center", justifyContent: "space-between",
        padding: "12px 20px 6px"
      }}>
        <button onClick={onBack} style={{
          ...iconBtn(theme), width: 34, height: 34,
          color: theme.muted, border: "none", background: "transparent"
        }} aria-label="Back">{Icon.back(18)}</button>
        <div style={{ textAlign: "center", color: theme.logo, lineHeight: 1 }}>
          <div style={{
            fontFamily: "'Playfair Display', Georgia, serif",
            fontWeight: 900, fontSize: 13, letterSpacing: "0.22em",
            color: theme.logo, opacity: 0.9
          }}>CHEF ALWAYS</div>
          <div style={{
            marginTop: 2,
            fontFamily: "'Inter', sans-serif", fontWeight: 600,
            fontSize: 8, letterSpacing: "0.42em",
            color: theme.logoSub, opacity: 0.9
          }}>• BRIGADE •</div>
        </div>
        {showEdit ? (
          <button onClick={() => setEditing(!editing)} style={{
            ...iconBtn(theme), width: 34, height: 34,
            color: editing ? theme.accentText : theme.text,
            background: editing ? theme.accent : "transparent",
            borderColor: editing ? theme.accent : theme.rowBorder
          }} aria-label="Edit">{Icon.pencil(15)}</button>
        ) : (
          <div style={{ width: 34 }}/>
        )}
      </div>
      <div style={{
        display: "flex", alignItems: "center", gap: 14,
        padding: "2px 24px 14px"
      }}>
        <div style={{ flex: 1, height: 1, background: theme.accent, opacity: 0.35 }}/>
        <h2 style={{
          margin: 0, fontFamily: "'Playfair Display', serif",
          fontSize: 22, fontWeight: 700, color: theme.heading,
          letterSpacing: "0.02em", whiteSpace: "nowrap"
        }}>{title}</h2>
        <div style={{ flex: 1, height: 1, background: theme.accent, opacity: 0.35 }}/>
      </div>
    </div>
  );
}

function BrandHero({ theme, subtitle }) {
  const sub = subtitle || "BRIGADE";
  return (
    <div style={{
      position: "relative", padding: "34px 20px 28px", textAlign: "center",
      background: `radial-gradient(circle at 50% 40%, ${theme.accent}18 0%, transparent 65%)`
    }}>
      <div style={{ maxWidth: 200, margin: "0 auto 22px" }}><BrandRule theme={theme}/></div>
      <div style={{
        display: "inline-flex", alignItems: "center", justifyContent: "center",
        width: 54, height: 54, borderRadius: 999,
        background: `${theme.accent}1a`, border: `1px solid ${theme.accent}55`,
        color: theme.accent, marginBottom: 16
      }}>{Icon.hat(28)}</div>
      <div style={{
        fontFamily: "'Playfair Display', Georgia, serif",
        fontWeight: 900, fontSize: 34, letterSpacing: "0.08em",
        lineHeight: 1, color: theme.logo, marginBottom: 10
      }}>CHEF ALWAYS</div>
      <div style={{
        fontFamily: "'Inter', sans-serif", fontWeight: 600,
        fontSize: 11, letterSpacing: "0.48em", color: theme.logoSub, marginBottom: 18
      }}>
        <span style={{ opacity: 0.55 }}>•</span>&nbsp;&nbsp;{sub}&nbsp;&nbsp;<span style={{ opacity: 0.55 }}>•</span>
      </div>
      <div style={{
        display: "inline-block", padding: "6px 14px", borderRadius: 999,
        border: `1px solid ${theme.accent}55`, fontSize: 11, fontWeight: 600,
        color: theme.text, opacity: 0.85, letterSpacing: "0.28em", textTransform: "uppercase"
      }}>{fmtDate()}</div>
      <div style={{ maxWidth: 200, margin: "22px auto 0" }}><BrandRule theme={theme}/></div>
    </div>
  );
}

// =========================================================
//   HOME TAB
// =========================================================
// =========================================================
//   HEAD CHEF LOCK — gate for the Head Chef section.
//   The password is the dough password, checked server-side
//   against the /dough endpoint, so DevTools edits don't help.
//   Once unlocked it is kept in memory only (FS_SESSION.doughPw)
//   and reused by the Dough tab, so dough never re-prompts.
// =========================================================
function HeadChefLock({ theme, onUnlock }) {
  const [pw, setPw] = useState("");
  const [error, setError] = useState("");
  const [loading, setLoading] = useState(false);

  const submit = async () => {
    const v = pw.trim();
    if (!v) { setError("Enter the head chef password"); return; }
    setLoading(true); setError("");
    try {
      const base = getWorkerBase();
      if (!base) throw new Error("No server configured.");
      const res = await fetch(`${base}/dough`, { headers: { "X-Dough-Password": v } });
      if (res.status === 401) throw new Error("Wrong password.");
      if (!res.ok) throw new Error(`Couldn't check the password (HTTP ${res.status}).`);
      window.FS_SESSION = window.FS_SESSION || {};
      window.FS_SESSION.doughPw = v; // reused by the Dough tab — no second prompt
      onUnlock();
    } catch (e) {
      setError(e.message || "Couldn't check the password.");
    } finally {
      setLoading(false);
    }
  };

  return (
    <div style={{ maxWidth: 320, margin: "30px auto 0", textAlign: "center" }}>
      <div style={{ display: "flex", justifyContent: "center", color: theme.accent, marginBottom: 12 }}>{Icon.lock(30)}</div>
      <div style={{ fontSize: 15, fontWeight: 600, color: theme.text, marginBottom: 6 }}>
        Head Chef is locked
      </div>
      <div style={{ fontSize: 13, color: theme.muted, marginBottom: 16, lineHeight: 1.5 }}>
        Enter the head chef password to open dough recipes and stock.
      </div>
      <input
        type="password"
        value={pw}
        onChange={e => setPw(e.target.value)}
        onKeyDown={e => e.key === "Enter" && submit()}
        placeholder="Head chef password"
        style={{
          width: "100%", padding: "12px 14px", borderRadius: 10,
          border: `1px solid ${theme.rowBorder}`, background: theme.input,
          color: theme.text, fontSize: 15, fontFamily: "inherit",
          outline: "none", marginBottom: 10, textAlign: "center"
        }}
      />
      <button onClick={submit} disabled={loading} style={{
        ...primaryBtn(theme), opacity: loading ? 0.6 : 1,
        cursor: loading ? "wait" : "pointer"
      }}>
        {loading ? "Checking…" : "Unlock"}
      </button>
      {error && (
        <div style={{ color: theme.danger || "#c8413a", fontSize: 13, fontWeight: 600, marginTop: 12 }}>
          {error}
        </div>
      )}
    </div>
  );
}

// =========================================================
//   DAY LOG — initials, End the Day, history helpers
// =========================================================
function todayKey() {
  const d = new Date();
  return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`;
}
function dayLabel(dateStr) {
  const [y, m, d] = dateStr.split("-").map(Number);
  return new Date(y, m - 1, d).toLocaleDateString("en-GB", { weekday: "short", day: "numeric", month: "short" });
}
function monthLabel(dateStr) {
  const [y, m] = dateStr.split("-").map(Number);
  return new Date(y, m - 1, 1).toLocaleDateString("en-GB", { month: "long", year: "numeric" });
}
const pctOfKeys = (items, checked) =>
  items.length ? Math.round(items.filter(n => checked[n]).length / items.length * 100) : 0;

// Swap an item with its neighbour (dir -1 = up, 1 = down). Returns a new array.
function moveItem(arr, i, dir) {
  const j = i + dir;
  if (j < 0 || j >= arr.length) return arr;
  const next = [...arr];
  const tmp = next[i]; next[i] = next[j]; next[j] = tmp;
  return next;
}

const DAYLOG_LABELS = { opening: "Opening", lineup: "Line Up", closing: "Closing", potwash: "Potwash", prep: "Prep Closing", sunday: "Sunday Extras", stock: "Stock Check" };

// Deep clean defaults (potwash stays Sundays-only; prep deep clean sits at
// the bottom of the recorded Prep Closing Checklist)
const DEFAULT_POTWASH_DEEP = ["Deep clean shelves under sink", "Reorganise window shelves"];
const DEFAULT_PREP_DEEP = ["Deep clean fridges", "Clean behind fridge wall + floor"];
// Prep opening is a working list (not recorded); prep closing is recorded.
// Closing uses a v2 storage key so devices holding the old four-item default
// pick up the new list.
const DEFAULT_PREP_OPEN = [
  "Turn on lights and power",
  "Check all vegetables",
  "Check the prep list for the day",
  "Start chopping the vegetables",
  "Oil and season the vegetables",
  "Start cooking the vegetables",
  "Check the fridge",
  "Check what is missing for the day",
  "Start prepping and cooking everything else: meatballs, spaghetti, etc.",
  "Switch on the pasta boiler and check the water tank",
  "Open the line for prep: salads, chop cherry tomatoes, take boxes out of the fridge",
  "All prep done for the evening",
  "Wait for customer orders",
  "Start making orders",
  "Check all sauces for the evening",
  "Make more sauce if needed",
  "Receive orders and prep food",
  "Be happy when loads of orders come in",
  "Kitchen closes at 21:30"
];
const DEFAULT_PREP_CLOSING = [
  "Check all boxes on the line",
  "Replace dirty boxes with clean ones",
  "Put everything in the fridge for the next working day",
  "Make a stock check list for the next day: veg and anything else needed",
  "Clean the working table thoroughly",
  "Sanitise the work area with antibacterial spray",
  "Turn off the pasta boiler",
  "Turn off all sockets and the screen",
  "Wipe the order screen and clean the microwave, inside as well",
  "Leave the work area tidy and clean",
  "Sweep the floor",
  "Think about what needs doing tomorrow",
  "Check the kitchen is properly clean"
];

// Daily prep list (working list — no initials, never recorded in History)
const DEFAULT_PREP_CATS = {
  "Sauces & Bases": ["Tomato Sauce", "Salsa Napoletana", "Bolognese Sauce", "Truffle Mushroom Sauce", "Garlic Butter"],
  "Vegetables": ["Roasted Peppers", "Onions", "Caramelised Onions", "Cherry Tomatoes", "Lettuce", "Rocket", "Courgette", "Potato", "Mushrooms (Cooked)", "Mushrooms (Raw)"],
  "Herbs": ["Basil", "Chives", "Parsley"],
  "Meat": ["Chicken", "Meatballs", "Nduja", "Guanciale"],
  "Cheese & Dairy": ["Stracciatella", "Bufala"],
  "Bread & Pasta": ["Cut Bread", "Croutons", "Pasta"],
  "Dessert": ["Tiramisu"]
};

// Gather a scope of the BOH day from localStorage into a record for the
// Worker, plus a small summary stored as KV metadata. Scope is "down"
// (Opening + Closing + Sunday Extras), "up" (Potwash + Prep Closing) or
// undefined for everything (used by the missed-day catch-up). Partial saves
// are merged server-side, so downstairs and upstairs can end their day at
// different times without overwriting each other.
// Line Up and the daily prep list are deliberately NOT part of any record —
// they're working lists, not accountability checklists.
function collectDayRecord(dateKey, scope) {
  const [ky, km, kd] = dateKey.split("-").map(Number);
  const isSunday = new Date(ky, km - 1, kd).getDay() === 0;
  const allIni = LS.get("fs.initials.v1", {})[dateKey] || {};
  const initials = {};
  const lists = {};
  const pct = {};

  if (scope !== "up") {
    const opening = { items: LS.get("fs.opening.v1", DEFAULT_OPENING_CHECKLIST), checked: LS.get("fs.opening.checked.v1", {}) };
    const closing = { items: LS.get("fs.checklist.v2", DEFAULT_CHECKLIST), checked: LS.get("fs.checklist.checked.v2", {}) };
    lists.opening = opening;
    lists.closing = closing;
    pct.opening = pctOfKeys(opening.items, opening.checked);
    pct.closing = pctOfKeys(closing.items, closing.checked);
    if (allIni.opening) initials.opening = allIni.opening;
    if (allIni.closing) initials.closing = allIni.closing;
    if (isSunday) {
      const sunday = { items: LS.get("fs.checklist.sunday.v1", DEFAULT_SUNDAY_CHECKLIST), checked: closing.checked };
      lists.sunday = sunday;
      pct.sunday = pctOfKeys(sunday.items, sunday.checked);
    }
  }
  if (scope !== "down") {
    const potwash = {
      opening: LS.get("fs.potwash.opening.v1", DEFAULT_POTWASH_OPENING),
      closing: LS.get("fs.potwash.closing.v1", DEFAULT_POTWASH_CLOSING),
      deep: isSunday ? LS.get("fs.potwash.deep.v1", DEFAULT_POTWASH_DEEP) : [],
      checked: LS.get("fs.potwash.checked.v1", {})
    };
    const prep = {
      closing: LS.get("fs.prep.closing.v2", DEFAULT_PREP_CLOSING),
      deep: LS.get("fs.prep.deep.v1", DEFAULT_PREP_DEEP),
      checked: LS.get("fs.prep.checked.v1", {})
    };
    lists.potwash = potwash;
    pct.potwash = pctOfKeys([...potwash.opening, ...potwash.closing, ...potwash.deep], potwash.checked);
    if (allIni.potwash) initials.potwash = allIni.potwash;
    const prepRecorded = [...prep.closing, ...prep.deep];
    if (prepRecorded.length > 0) {
      lists.prep = prep;
      pct.prep = pctOfKeys(prepRecorded, prep.checked);
      if (allIni.prep) initials.prep = allIni.prep;
    }
  }

  const record = { date: dateKey, savedAt: Date.now(), initials, lists };
  const summary = { initials, pct };
  return { record, summary };
}

// POST the day to the Worker. Throws on failure.
async function postDayRecord(dateKey, record, summary) {
  const base = getWorkerBase();
  if (!base) throw new Error("No server configured.");
  const res = await fetch(`${base}/daylog`, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({
      password: (window.FS_SESSION && window.FS_SESSION.pw) || "",
      date: dateKey, record, summary, merge: true
    })
  });
  if (!res.ok) throw new Error(`HTTP ${res.status}`);
}

// Clear the saved scope's ticks and initials after a successful save.
// Goes through Sync.set so the clear propagates to every device.
function clearDayLocal(dateKey, scope) {
  const all = !scope;
  if (all || scope === "down") {
    Sync.set("fs.opening.checked.v1", {});
    Sync.set("fs.lineup.checked.v2", {});
    Sync.set("fs.checklist.checked.v2", {});
  }
  if (all || scope === "up") {
    Sync.set("fs.potwash.checked.v1", {});
    Sync.set("fs.prep.checked.v1", {});
    Sync.set("fs.prep.qty.v1", {});
  }
  const ini = LS.get("fs.initials.v1", {});
  if (all) {
    Sync.set("fs.initials.v1", {});
  } else {
    const dayIni = { ...(ini[dateKey] || {}) };
    (scope === "down" ? ["opening", "closing"] : ["potwash", "prep"]).forEach(k => delete dayIni[k]);
    Sync.set("fs.initials.v1", { ...ini, [dateKey]: dayIni });
  }
  Sync.set("fs.daylog.lastSaved", dateKey);
  Sync.set("fs.daylog.activity", "");
}

function endDayConfirmText(dateKey, summary) {
  const lines = ["opening", "closing", "potwash", "prep", "sunday"]
    .filter(id => summary.pct[id] !== undefined)
    .map(id => `${DAYLOG_LABELS[id]}  ${summary.pct[id]}%  —  ${summary.initials[id] || "no initials"}`);
  return `End the day and save ${dayLabel(dateKey)}?\n\n${lines.join("\n")}\n\nSaving clears all ticks ready for tomorrow.`;
}

// Shared End the Day button. Scope "down" lives in the kitchen Closing
// Checklist (saves Opening/Closing/Sunday Extras); scope "up" lives in
// Potwash (saves Potwash + Prep Closing). Saves merge server-side.
function EndDayButton({ theme, scope, onSaved }) {
  const [saving, setSaving] = useState(false);
  const label = scope === "up" ? "Upstairs" : "Downstairs";
  const run = async () => {
    const dateKey = todayKey();
    const { record, summary } = collectDayRecord(dateKey, scope);
    if (!window.confirm(endDayConfirmText(dateKey, summary))) return;
    setSaving(true);
    try {
      await postDayRecord(dateKey, record, summary);
      clearDayLocal(dateKey, scope);
      window.alert(`${label} saved for ${dayLabel(dateKey)} ✓ — ticks cleared for tomorrow.`);
      if (onSaved) onSaved();
    } catch (e) {
      window.alert(`Couldn't save (${e.message}). Nothing was cleared — try again when you're back online.`);
    } finally {
      setSaving(false);
    }
  };
  return (
    <button onClick={run} disabled={saving} style={{
      ...primaryBtn(theme), opacity: saving ? 0.6 : 1, cursor: saving ? "wait" : "pointer"
    }}>{saving ? "Saving…" : `End the Day — Save ${label} & Clear`}</button>
  );
}

// Blocking sign-in gate at the top of each BOH checklist. The list is hidden
// until initials are entered (once per list per day). Initials are stored in
// localStorage and go into the end-of-day record.
function InitialsGate({ listId, label, theme, children }) {
  const [store, setStore] = useShared("fs.initials.v1", {});
  const day = todayKey();
  const current = (store[day] || {})[listId] || "";
  const [val, setVal] = useState("");
  const set = () => {
    const v = val.trim().toUpperCase().slice(0, 4);
    if (!v) return;
    setStore({ ...store, [day]: { ...(store[day] || {}), [listId]: v } });
    setVal("");
  };
  const clear = () => setStore({ ...store, [day]: { ...(store[day] || {}), [listId]: "" } });

  if (!current) {
    return (
      <div style={{ maxWidth: 320, margin: "36px auto 0", textAlign: "center" }}>
        <div style={{ display: "flex", justifyContent: "center", color: theme.accent, marginBottom: 12 }}>{Icon.user(30)}</div>
        <div style={{ fontSize: 15, fontWeight: 600, color: theme.text, marginBottom: 6 }}>
          Who's on {label} today?
        </div>
        <div style={{ fontSize: 13, color: theme.muted, marginBottom: 16, lineHeight: 1.5 }}>
          Enter your initials to open the list. They go on today's record.
        </div>
        <input value={val} onChange={e => setVal(e.target.value)}
          onKeyDown={e => e.key === "Enter" && set()}
          placeholder="AB" maxLength={4}
          style={{
            width: "100%", padding: "12px 14px", borderRadius: 10,
            border: `1px solid ${theme.rowBorder}`, background: theme.input,
            color: theme.text, fontSize: 16, fontWeight: 700, textAlign: "center",
            textTransform: "uppercase", fontFamily: "inherit", outline: "none",
            marginBottom: 10, letterSpacing: "0.2em"
          }}/>
        <button onClick={set} style={primaryBtn(theme)}>Start</button>
      </div>
    );
  }
  return (
    <>
      <div style={{
        display: "flex", alignItems: "center", gap: 8, padding: "8px 12px",
        borderRadius: 10, background: theme.row, border: `1px solid ${theme.rowBorder}`,
        marginBottom: 12, fontSize: 13
      }}>
        <span style={{ color: theme.muted }}>Signed in:</span>
        <span style={{ color: theme.accent, fontWeight: 800, letterSpacing: "0.1em" }}>{current}</span>
        <span style={{ flex: 1 }}/>
        <button onClick={clear} style={{
          background: "transparent", border: "none", color: theme.muted,
          fontSize: 12, cursor: "pointer", textDecoration: "underline", fontFamily: "inherit"
        }}>change</button>
      </div>
      {children}
    </>
  );
}

function HomeTab({ theme, go, stats, section, setSection, hcUnlocked, onUnlock, onDayEnded }) {
  const accents = theme.tabAccents || {};
  const isHC = section === "headchef";

  const allTiles = [
    { id: "downstairs", label: "Downstairs\nChecklist", icon: Icon.list(26),
      meta: `Opening ${stats.openingPct}% · Line Up ${stats.lineupPct}% · Closing ${stats.checklistPct}%`,
      colour: accents.opening || theme.accent },
    { id: "upstairs",   label: "Upstairs\nChecklist",   icon: Icon.box(26),
      meta: `Potwash ${stats.potwashPct}%${stats.prepPct !== undefined ? ` · Prep ${stats.prepPct}%` : ""}`,
      colour: accents.lineup || theme.accent },
    { id: "dough",     label: "Dough\nRecipes",      icon: Icon.hat(26), meta: "3 recipes",                         colour: accents.dough     || theme.accent, headOnly: true },
    { id: "stock",     label: "Stock",               icon: Icon.box(26),
      meta: stats.stockShort > 0 ? `${stats.stockShort} items to order` : "All stocked",
      colour: accents.stock || theme.accent, headOnly: true },
    { id: "history",   label: "History",             icon: Icon.clipboard(26), meta: "Who did what, day by day",
      colour: accents.checklist || theme.accent, headOnly: true }
  ];
  const tiles = isHC ? allTiles.filter(t => t.headOnly) : allTiles.filter(t => !t.headOnly);
  const subtitle = isHC ? "Head Chef" : "Kitchen";
  const locked = isHC && !hcUnlocked;

  // ---- End the Day: save everything to the Worker, then clear the ticks ----
  const [saving, setSaving] = useState(false);
  const [savedMsg, setSavedMsg] = useState("");
  const today = todayKey();
  const activity = LS.get("fs.daylog.activity", "");
  const lastSaved = LS.get("fs.daylog.lastSaved", "");
  const missedDay = (activity && activity !== today && activity !== lastSaved) ? activity : "";

  const saveMissedDay = async (dateKey) => {
    const { record, summary } = collectDayRecord(dateKey);
    if (!window.confirm(endDayConfirmText(dateKey, summary))) return;
    setSaving(true);
    setSavedMsg("");
    try {
      await postDayRecord(dateKey, record, summary);
      clearDayLocal(dateKey);
      setSavedMsg(`${dayLabel(dateKey)} saved ✓`);
      if (onDayEnded) onDayEnded();
    } catch (e) {
      window.alert(`Couldn't save the day (${e.message}). Nothing was cleared — try again when you're back online.`);
    } finally {
      setSaving(false);
    }
  };
  return (
    <div style={{ display: "flex", flexDirection: "column", flex: 1 }}>
      <BrandHero theme={theme} subtitle={subtitle}/>
      <div style={{ padding: "0 22px 28px", display: "flex", flexDirection: "column", flex: 1 }}>
        {/* BOH / Head Chef section switcher */}
        <div style={{
          display: "flex", gap: 6, padding: 4, marginBottom: 18,
          background: theme.row, border: `1px solid ${theme.rowBorder}`, borderRadius: 12
        }}>
          {[
            { id: "boh", label: <span>BOH</span> },
            { id: "headchef", label: (
              <span style={{ display: "inline-flex", alignItems: "center", gap: 6 }}>
                Head Chef {!hcUnlocked && Icon.lock(13)}
              </span>
            ) }
          ].map(s => {
            const on = s.id === section;
            return (
              <button key={s.id} onClick={() => setSection(s.id)} style={{
                flex: 1, padding: "10px 8px", borderRadius: 9, border: "none",
                background: on ? theme.accent : "transparent",
                color: on ? theme.accentText : theme.text,
                fontWeight: 700, fontSize: 14, cursor: "pointer", fontFamily: "inherit",
                display: "flex", alignItems: "center", justifyContent: "center"
              }}>{s.label}</button>
            );
          })}
        </div>
        <div style={{
          textAlign: "center", fontSize: 10, fontWeight: 700,
          color: theme.accent, letterSpacing: "0.42em",
          textTransform: "uppercase", marginBottom: 16
        }}>— Today's Stations —</div>
        {locked ? (
          <HeadChefLock theme={theme} onUnlock={onUnlock}/>
        ) : (
        <div style={{ display: "flex", flexDirection: "column", gap: 10 }}>
          {tiles.map(t => {
            const hex = t.colour.replace('#','');
            const r = parseInt(hex.substring(0,2), 16);
            const g = parseInt(hex.substring(2,4), 16);
            const b = parseInt(hex.substring(4,6), 16);
            const soft = `rgba(${r},${g},${b},0.14)`;
            const border = `rgba(${r},${g},${b},0.35)`;
            return (
              <button key={t.id} onClick={() => go(t.id)} style={{
                position: "relative", display: "flex", alignItems: "center", gap: 14,
                padding: "16px 16px", borderRadius: 14,
                background: theme.tile, color: theme.text,
                border: `1px solid ${theme.tileBorder}`,
                textAlign: "left", cursor: "pointer", fontFamily: "inherit",
                transition: "transform .15s ease", overflow: "hidden"
              }}
              onMouseDown={e => e.currentTarget.style.transform = "scale(0.995)"}
              onMouseUp={e => e.currentTarget.style.transform = "scale(1)"}
              onMouseLeave={e => e.currentTarget.style.transform = "scale(1)"}
              >
                <div style={{
                  position: "absolute", left: 0, top: 10, bottom: 10, width: 4,
                  borderRadius: 999, background: t.colour
                }}/>
                <div style={{
                  width: 48, height: 48, borderRadius: 12,
                  background: soft, color: t.colour,
                  border: `1px solid ${border}`,
                  display: "flex", alignItems: "center", justifyContent: "center", flexShrink: 0
                }}>{t.icon}</div>
                <div style={{ flex: 1 }}>
                  <div style={{
                    fontFamily: "'Playfair Display', serif", fontWeight: 700,
                    fontSize: 19, lineHeight: 1.1, color: theme.heading, whiteSpace: "pre-line"
                  }}>{t.label}</div>
                  <div style={{ fontSize: 12, color: theme.muted, marginTop: 5, letterSpacing: "0.02em" }}>{t.meta}</div>
                </div>
                <div style={{ color: t.colour, opacity: 0.8, transform: "rotate(180deg)" }}>{Icon.back(18)}</div>
              </button>
            );
          })}
        </div>
        )}
        {!isHC && (missedDay || savedMsg) && (
          <div style={{ marginTop: 16 }}>
            {missedDay && (
              <div style={{
                padding: "12px 14px", borderRadius: 10, marginBottom: 10,
                background: "#e8b05922", border: "1px solid #e8b059",
                fontSize: 13, color: theme.text
              }}>
                <div style={{ fontWeight: 700, marginBottom: 8, display: "flex", alignItems: "center", gap: 6 }}>
                  <span style={{ display: "flex", color: "#e8b059" }}>{Icon.alert(15)}</span>
                  {dayLabel(missedDay)} was never closed off
                </div>
                <button onClick={() => saveMissedDay(missedDay)} disabled={saving} style={{
                  ...primaryBtn(theme), marginBottom: 0,
                  opacity: saving ? 0.6 : 1, cursor: saving ? "wait" : "pointer"
                }}>Save {dayLabel(missedDay)} now</button>
              </div>
            )}
            {savedMsg && (
              <div style={{ textAlign: "center", fontSize: 13, fontWeight: 700, color: "#6fb58a", marginTop: 4 }}>
                {savedMsg}
              </div>
            )}
          </div>
        )}
        <div style={{ flex: 1, minHeight: 24 }}/>
        <div style={{ maxWidth: 180, margin: "24px auto 10px" }}><BrandRule theme={theme}/></div>
        <div style={{
          textAlign: "center", fontSize: 9, color: theme.muted,
          letterSpacing: "0.42em", textTransform: "uppercase", opacity: 0.7, fontWeight: 600
        }}>Est. Kitchen Tools</div>
        <button onClick={() => window.fsLogout && window.fsLogout()} style={{
          display: "flex", alignItems: "center", justifyContent: "center", gap: 6,
          margin: "20px auto 0", padding: "10px 20px", borderRadius: 10,
          background: "transparent", border: `1px solid ${theme.rowBorder}`,
          color: theme.muted, fontSize: 12, fontWeight: 600,
          cursor: "pointer", fontFamily: "inherit", letterSpacing: "0.06em"
        }}>{Icon.logout(14)} Sign out</button>
      </div>
    </div>
  );
}

// =========================================================
//   LINE UP TAB — list view + visual counter map
// =========================================================
// The map mirrors the real line: tub positions and sizes match the counter.
// Ticks share the same storage as the list view, keyed by category:name, so
// progress, clearing and End the Day behave identically in both views.
const TUB_SIZE = { sq: [54, 54], rt: [42, 50], bigrt: [116, 66], bigsq: [84, 84] };
const LINEUP_TUBS = {
  back: [
    { label: "Pep", name: "Pepperoni", kind: "sq" },
    { label: "Nduja", name: "Nduja Balls", kind: "sq" },
    { label: "Guan", name: "Guanciale", kind: "rt" },
    { label: "FDL", name: "Cheese", kind: "bigrt" },
    { label: "Tom Sauce", name: "Tomato Sauce", kind: "bigsq" },
    { label: "Peppers", name: "Peppers", kind: "sq" },
    { label: "Red On", name: "Red Onions", kind: "sq" },
    { label: "Mush", name: "Mushrooms", kind: "sq" },
    { label: "Potato", name: "Potato", kind: "sq" }
  ],
  frontLeft: [{ label: "Anch", name: "Anchovies", kind: "rt" }],
  frontMid: [
    { label: "Parm", name: "Parm Cheese", kind: "sq" },
    { label: "Basil", name: "Basil", kind: "sq" }
  ],
  frontRight: [
    { label: "Courg", name: "Courgettes", kind: "rt" },
    { label: "Carm On", name: "Caramelised Onions", kind: "rt" },
    { label: "Olives", name: "Olives", kind: "rt" },
    { label: "Capers", name: "Capers", kind: "rt" }
  ]
};
const ALL_TUBS = [...LINEUP_TUBS.back, ...LINEUP_TUBS.frontLeft, ...LINEUP_TUBS.frontMid, ...LINEUP_TUBS.frontRight];

function LineupMap({ theme, data, checked, onToggle }) {
  // Resolve each tub to its list key at render time, so list renames or the
  // category naming quirks never break the map.
  const keyFor = (name) => {
    const cat = Object.keys(data).find(c => (data[c] || []).includes(name));
    return cat ? `${cat}:${name}` : `map:${name}`;
  };
  const Tub = ({ t }) => {
    const [w, h] = TUB_SIZE[t.kind];
    const k = keyFor(t.name);
    const on = !!checked[k];
    return (
      <button onClick={() => onToggle(k)} style={{
        width: w, height: h, flexShrink: 0, borderRadius: 8,
        border: `2px solid ${on ? theme.accent : theme.checkBorder}`,
        background: on ? theme.accent : theme.row,
        color: on ? theme.accentText : theme.text,
        fontSize: 11, fontWeight: 600, fontFamily: "inherit", cursor: "pointer",
        padding: 2, display: "flex", alignItems: "center", justifyContent: "center",
        textAlign: "center", lineHeight: 1.2, overflow: "hidden"
      }}>{t.label}</button>
    );
  };
  const doneCount = ALL_TUBS.filter(t => checked[keyFor(t.name)]).length;
  return (
    <div>
      <div style={{
        textAlign: "center", fontSize: 10, fontWeight: 700, letterSpacing: "0.25em",
        color: theme.accent, margin: "14px 0 10px", textTransform: "uppercase"
      }}>Back of line</div>
      <div style={{ overflowX: "auto", paddingBottom: 4 }}>
        <div style={{ width: "max-content", margin: "0 auto" }}>
          <div style={{ display: "flex", gap: 6, alignItems: "flex-end" }}>
            {LINEUP_TUBS.back.map(t => <Tub key={t.label} t={t}/>)}
          </div>
          <div style={{ height: 14 }}/>
          <div style={{ display: "flex", gap: 6, alignItems: "flex-start" }}>
            {LINEUP_TUBS.frontLeft.map(t => <Tub key={t.label} t={t}/>)}
            <div style={{ flex: 1 }}/>
            {LINEUP_TUBS.frontMid.map(t => <Tub key={t.label} t={t}/>)}
            <div style={{ flex: 1 }}/>
            <div style={{ display: "grid", gridTemplateColumns: "42px 42px", gap: 6 }}>
              {LINEUP_TUBS.frontRight.map(t => <Tub key={t.label} t={t}/>)}
            </div>
          </div>
        </div>
      </div>
      <div style={{
        textAlign: "center", fontSize: 10, fontWeight: 700, letterSpacing: "0.25em",
        color: theme.muted, margin: "10px 0 10px", textTransform: "uppercase"
      }}>Front of line / chef side</div>
      <div style={{ fontSize: 12, fontWeight: 600, color: theme.text, marginBottom: 6 }}>
        {doneCount}/{ALL_TUBS.length} tubs stocked
      </div>
      <div style={{ fontSize: 11, color: theme.muted, lineHeight: 1.6 }}>
        Key: Pep = Pepperoni · Guan = Guanciale · FDL = Fior di Latte (Cheese) · Tom = Tomato ·
        Mush = Mushrooms · Red On = Red Onions · Anch = Anchovies · Parm = Parm Cheese ·
        Courg = Courgettes · Carm On = Caramelised Onions
      </div>
    </div>
  );
}

function LineupTab({ theme, goHome, subnav }) {
  const [data, setData] = useShared("fs.lineup.v2", DEFAULT_LINEUP);
  const [checked, setChecked] = useShared("fs.lineup.checked.v2", {});
  const [editing, setEditing] = useState(false);
  const [lview, setLview] = useLocal("fs.lineup.view.v1", "map");
  const toggleRaw = (k) => setChecked({ ...checked, [k]: !checked[k] });

  const allItems = useMemo(() =>
    Object.entries(data).flatMap(([cat, items]) => items.map(n => ({ cat, name: n })))
  , [data]);
  const checkedCount = allItems.filter(i => checked[`${i.cat}:${i.name}`]).length;
  const pct = allItems.length ? Math.round(checkedCount / allItems.length * 100) : 0;

  const toggle = (cat, name) => { const k = `${cat}:${name}`; setChecked({ ...checked, [k]: !checked[k] }); };
  const addItem = (cat, name) => setData({ ...data, [cat]: [...data[cat], name] });
  const deleteItem = (cat, name) => {
    setData({ ...data, [cat]: data[cat].filter(x => x !== name) });
    const nc = { ...checked }; delete nc[`${cat}:${name}`]; setChecked(nc);
  };
  const clearAll = () => setChecked({});

  return (
    <div style={{ display: "flex", flexDirection: "column", flex: 1, minHeight: 0 }}>
      <PageHeader title="Line Up" onBack={goHome} editing={editing} setEditing={setEditing} theme={theme} />
      {subnav}
      <div style={{ padding: "18px 20px 28px", overflowY: "auto", flex: 1 }}>
        <Progress percent={pct} theme={theme} />

        {/* Map / List view switcher */}
        <div style={{
          display: "flex", gap: 6, padding: 4, margin: "14px 0 4px",
          background: theme.row, border: `1px solid ${theme.rowBorder}`, borderRadius: 12
        }}>
          {[
            { id: "map", label: "Line Map" },
            { id: "list", label: "List" }
          ].map(v => {
            const on = v.id === lview;
            return (
              <button key={v.id} onClick={() => setLview(v.id)} style={{
                flex: 1, padding: "9px 6px", borderRadius: 9, border: "none",
                background: on ? theme.accent : "transparent",
                color: on ? theme.accentText : theme.text,
                fontWeight: 700, fontSize: 13, cursor: "pointer", fontFamily: "inherit"
              }}>{v.label}</button>
            );
          })}
        </div>

        {lview === "map" ? (
          <LineupMap theme={theme} data={data} checked={checked} onToggle={toggleRaw}/>
        ) : (
          <>
          {Object.keys(data).map(cat => (
            <div key={cat}>
              <SectionHead count={`${data[cat].filter(n => checked[`${cat}:${n}`]).length}/${data[cat].length}`} theme={theme}>{cat}</SectionHead>
              <div style={{ display: "flex", flexDirection: "column", gap: 8 }}>
                {data[cat].map((name, i) => (
                  <ChecklistRow key={name} label={name}
                    checked={!!checked[`${cat}:${name}`]}
                    onToggle={() => toggle(cat, name)}
                    onDelete={() => deleteItem(cat, name)}
                    onMove={d => setData({ ...data, [cat]: moveItem(data[cat], i, d) })}
                    editing={editing} theme={theme}/>
                ))}
                {editing && <AddRow placeholder={`Add to ${cat}…`} onAdd={n => addItem(cat, n)} theme={theme}/>}
              </div>
            </div>
          ))}
          </>
        )}
        <div style={{ height: 16 }}/>
        <button onClick={() => window.confirm("Clear all checks? This cannot be undone.") && clearAll()} style={ghostBtn(theme)}>Clear all checks</button>
      </div>
    </div>
  );
}

// =========================================================
//   CHECKLIST TAB
// =========================================================
function ChecklistTab({ theme, goHome, subnav }) {
  const [items, setItems] = useShared("fs.checklist.v2", DEFAULT_CHECKLIST);
  const [sundayItems, setSundayItems] = useShared("fs.checklist.sunday.v1", DEFAULT_SUNDAY_CHECKLIST);
  const [checked, setChecked] = useShared("fs.checklist.checked.v2", {});
  const [editing, setEditing] = useState(false);

  const allItems = [...items, ...sundayItems];
  const checkedCount = allItems.filter(n => checked[n]).length;
  const pct = allItems.length ? Math.round(checkedCount / allItems.length * 100) : 0;

  const toggle = (name) => setChecked({ ...checked, [name]: !checked[name] });
  const add = (name) => setItems([...items, name]);
  const addSunday = (name) => setSundayItems([...sundayItems, name]);
  const remove = (name) => {
    setItems(items.filter(x => x !== name));
    const nc = { ...checked }; delete nc[name]; setChecked(nc);
  };
  const removeSunday = (name) => {
    setSundayItems(sundayItems.filter(x => x !== name));
    const nc = { ...checked }; delete nc[name]; setChecked(nc);
  };

  const exportXlsx = () => {
    if (typeof XLSX === "undefined") { alert("Spreadsheet library failed to load"); return; }
    const TICK = "✓";
    const days = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"];
    const rows = [
      ["THE FIRE STATION PIZZERIA", "", "", "", "", "", "", ""],
      ["Closing Checklist",         "", "", "", "", "", "", ""],
      ["Name",                      "", "", "", "", "", "", ""],
      ["Date",                      "", "", "", "", "", "", ""],
      ["Closing Duty", ...days],
      ...items.map(name => [name, TICK, TICK, TICK, TICK, TICK, TICK, TICK]),
      ["Sunday Deep Clean Tasks", "", "", "", "", "", "", ""],
      ...sundayItems.map(name => [name, "", "", "", "", "", "", TICK]),
    ];
    const ws = XLSX.utils.aoa_to_sheet(rows);
    ws["!merges"] = [
      { s: { r: 0, c: 0 }, e: { r: 0, c: 7 } },
      { s: { r: 1, c: 0 }, e: { r: 1, c: 7 } },
    ];
    ws["!cols"] = [
      { wch: 34 }, { wch: 9 }, { wch: 9 }, { wch: 9 },
      { wch: 9 },  { wch: 9 }, { wch: 9 }, { wch: 9 },
    ];
    // Center the tick columns
    const range = XLSX.utils.decode_range(ws["!ref"]);
    for (let R = range.s.r; R <= range.e.r; R++) {
      for (let C = range.s.c; C <= range.e.c; C++) {
        const addr = XLSX.utils.encode_cell({ r: R, c: C });
        if (!ws[addr]) continue;
        ws[addr].s = ws[addr].s || {};
        if (C > 0) ws[addr].s.alignment = { horizontal: "center", vertical: "center" };
      }
    }
    const wb = XLSX.utils.book_new();
    XLSX.utils.book_append_sheet(wb, ws, "Closing Checklist");
    const d = new Date();
    const stamp = `${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,"0")}-${String(d.getDate()).padStart(2,"0")}`;
    XLSX.writeFile(wb, `Closing Checklist ${stamp}.xlsx`);
  };

  return (
    <div style={{ display: "flex", flexDirection: "column", flex: 1, minHeight: 0 }}>
      <PageHeader title="Closing Checklist" onBack={goHome} editing={editing} setEditing={setEditing} theme={theme} />
      {subnav}
      <div style={{ padding: "18px 20px 28px", overflowY: "auto", flex: 1 }}>
        <InitialsGate listId="closing" label="Closing" theme={theme}>
        <Progress percent={pct} theme={theme} />

        <SectionHead theme={theme} sub="Every night" count={`${items.filter(n => checked[n]).length}/${items.length}`}>Daily</SectionHead>
        <div style={{ display: "flex", flexDirection: "column", gap: 8 }}>
          {items.map((n, i) => (
            <ChecklistRow key={n} label={n} checked={!!checked[n]}
              onToggle={() => toggle(n)} onDelete={() => remove(n)}
              onMove={d => setItems(moveItem(items, i, d))}
              editing={editing} theme={theme}/>
          ))}
          {editing && <AddRow placeholder="Add daily task…" onAdd={add} theme={theme}/>}
        </div>

        <SectionHead theme={theme} sub="Weekly deep clean"
          count={`${sundayItems.filter(n => checked[n]).length}/${sundayItems.length}`}>
          Sunday Extras
        </SectionHead>
        <div style={{ display: "flex", flexDirection: "column", gap: 8 }}>
          {sundayItems.map((n, i) => (
            <ChecklistRow key={n} label={n} checked={!!checked[n]}
              onToggle={() => toggle(n)} onDelete={() => removeSunday(n)}
              onMove={d => setSundayItems(moveItem(sundayItems, i, d))}
              editing={editing} theme={theme}/>
          ))}
          {editing && <AddRow placeholder="Add Sunday task…" onAdd={addSunday} theme={theme}/>}
        </div>

        <div style={{ height: 20 }}/>
        <EndDayButton theme={theme} scope="down" onSaved={goHome}/>
        <div style={{ height: 10 }}/>
        <button onClick={exportXlsx} style={ghostBtn(theme)}>Export to Spreadsheet</button>
        </InitialsGate>
      </div>
    </div>
  );
}

// =========================================================
//   (Sunday tab removed — Sunday Extras live inside Closing)
// =========================================================
function SundayTab_UNUSED() { return null; }

// =========================================================
//   OPENING CHECKLIST TAB
// =========================================================
function OpeningTab({ theme, goHome, subnav }) {
  const [items, setItems] = useShared("fs.opening.v1", DEFAULT_OPENING_CHECKLIST);
  const [checked, setChecked] = useShared("fs.opening.checked.v1", {});
  const [editing, setEditing] = useState(false);

  const checkedCount = items.filter(n => checked[n]).length;
  const pct = items.length ? Math.round(checkedCount / items.length * 100) : 0;
  const toggle = (name) => setChecked({ ...checked, [name]: !checked[name] });
  const add = (name) => setItems([...items, name]);
  const remove = (name) => {
    setItems(items.filter(x => x !== name));
    const nc = { ...checked }; delete nc[name]; setChecked(nc);
  };

  return (
    <div style={{ display: "flex", flexDirection: "column", flex: 1, minHeight: 0 }}>
      <PageHeader title="Opening Checklist" onBack={goHome} editing={editing} setEditing={setEditing} theme={theme} />
      {subnav}
      <div style={{ padding: "18px 20px 28px", overflowY: "auto", flex: 1 }}>
        <InitialsGate listId="opening" label="Opening" theme={theme}>
        <Progress percent={pct} theme={theme} />
        <div style={{ marginTop: 18, display: "flex", flexDirection: "column", gap: 8 }}>
          {items.map((n, i) => (
            <ChecklistRow key={n} label={n} checked={!!checked[n]}
              onToggle={() => toggle(n)} onDelete={() => remove(n)}
              onMove={d => setItems(moveItem(items, i, d))}
              editing={editing} theme={theme}/>
          ))}
          {editing && <AddRow placeholder="Add opening task…" onAdd={add} theme={theme}/>}
        </div>
        <div style={{ height: 16 }}/>
        <button onClick={() => window.confirm("Clear all checks? This cannot be undone.") && setChecked({})} style={ghostBtn(theme)}>Clear all checks</button>
        </InitialsGate>
      </div>
    </div>
  );
}

function SyncIndicator({ syncState, theme }) {
  if (syncState === "idle") return null;
  const map = {
    loading: { dot: theme.muted,  text: "Loading shared stock…" },
    saving:  { dot: theme.accent, text: "Saving to shared stock…" },
    offline: { dot: "#d64545",    text: "Offline — changes saved locally, will sync when back online" },
    error:   { dot: "#d64545",    text: "Sync error — changes saved locally" }
  };
  const s = map[syncState] || map.offline;
  return (
    <div style={{
      display: "flex", alignItems: "center", gap: 8,
      padding: "8px 20px", fontSize: 12, fontWeight: 600,
      color: theme.muted, background: theme.headerBg,
      borderBottom: `1px solid ${theme.rowBorder}`
    }}>
      <span style={{
        display: "inline-block", width: 8, height: 8, borderRadius: 999,
        background: s.dot, flexShrink: 0
      }}/>
      <span>{s.text}</span>
    </div>
  );
}

// =========================================================
//   STOCK TAB — four suppliers
// =========================================================
// Sunday-only weekly stock check save (Carnevale + Veg Factor).
function SaveStockCheckButton({ theme, carn, veg }) {
  const [saving, setSaving] = useState(false);
  const [saved, setSaved] = useState(false);
  if (new Date().getDay() !== 0) return null;

  const run = async () => {
    const ini = (window.prompt("Initials for this stock check (e.g. AB)") || "").trim().toUpperCase().slice(0, 4);
    if (!ini) return;
    const short = [...carn, ...veg].filter(i => { const sh = stockShortfall(i); return sh && sh !== "0"; }).length;
    if (!window.confirm(`Save this week's stock check?\n\nCarnevale ${carn.length} items · Veg Factor ${veg.length} items\n${short} item${short === 1 ? "" : "s"} to order\nInitials: ${ini}`)) return;
    setSaving(true);
    try {
      const base = getWorkerBase();
      if (!base) throw new Error("No server configured.");
      const dateKey = todayKey();
      const record = { date: dateKey, savedAt: Date.now(), initials: ini, suppliers: { carnevale: carn, vegfactor: veg } };
      const summary = { initials: ini, items: carn.length + veg.length, toOrder: short };
      const res = await fetch(`${base}/stocklog`, {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({
          password: (window.FS_SESSION && window.FS_SESSION.pw) || "",
          date: dateKey, record, summary
        })
      });
      if (!res.ok) throw new Error(`HTTP ${res.status}`);
      setSaved(true);
    } catch (e) {
      window.alert(`Couldn't save the stock check (${e.message}). Try again when you're back online.`);
    } finally {
      setSaving(false);
    }
  };

  return (
    <button onClick={run} disabled={saving || saved} style={{
      ...primaryBtn(theme), marginBottom: 8,
      opacity: (saving || saved) ? 0.7 : 1, cursor: saving ? "wait" : "pointer"
    }}>
      {saved ? "Stock check saved ✓" : saving ? "Saving…" : "Save this week's stock check"}
    </button>
  );
}

function StockTab({ theme, goHome }) {
  const [carn, setCarn, carnSync] = useSharedStock("carnevale", DEFAULT_CARNEVALE);
  const [veg, setVeg, vegSync] = useSharedStock("vegfactor", DEFAULT_VEGFACTOR);
  const [pil, setPil, pilSync] = useSharedStock("pilgrim", DEFAULT_PILGRIM);
  const [cob, setCob, cobSync] = useSharedStock("cobblelane", DEFAULT_COBBLE_LANE);
  const [editing, setEditing] = useState(false);
  const [supplier, setSupplier] = useState("carnevale");

  const suppliers = {
    carnevale:  { data: carn, set: setCarn, sync: carnSync, label: "Carnevale",   sub: "with codes", hasCode: true,  prefix: "carn" },
    vegfactor:  { data: veg,  set: setVeg,  sync: vegSync,  label: "Veg Factor",  sub: "produce",    hasCode: false, prefix: "veg" },
    pilgrim:    { data: pil,  set: setPil,  sync: pilSync,  label: "Pilgrim",     sub: "supplier",   hasCode: false, prefix: "pil" },
    cobblelane: { data: cob,  set: setCob,  sync: cobSync,  label: "Cobble Lane", sub: "cured",      hasCode: false, prefix: "cob" }
  };
  const sup = suppliers[supplier] || suppliers.carnevale;
  const current = sup.data;
  const setCurrent = sup.set;
  const hasCode = sup.hasCode;
  const supplierName = sup.label;

  const update = (id, patch) => setCurrent(current.map(s => s.id === id ? { ...s, ...patch } : s));
  const remove = (id) => setCurrent(current.filter(s => s.id !== id));
  const add = (name) => {
    const base = { id: `${sup.prefix}_${Date.now()}`, name, inStock: "0", mustHave: "1" };
    setCurrent([...current, hasCode ? { ...base, code: "" } : base]);
  };

  const needsOrder = (i) => {
    if (i.excludeFromEmail) return false;
    const sh = stockShortfall(i);
    if (sh && sh !== "0") return true;
    const stock = parseQty(i.inStock);
    if (stock.num <= 0 && i.name) return true;
    return false;
  };

  const needCount = current.filter(needsOrder).length;
  const excludedCount = current.filter(i => i.excludeFromEmail).length;

  const buildOrderLines = () => {
    const needed = current.filter(needsOrder);
    const orderDate = nextThursday();
    const lines = [];
    lines.push(`THE FIRE STATION PIZZERIA`);
    lines.push(`Stock Order For Thursday (${orderDate})`);
    lines.push(``);
    if (needed.length === 0) {
      lines.push(`No items to order.`);
    } else {
      needed.forEach(it => {
        const sh = stockShortfall(it);
        const qty = (sh && sh !== "0" && sh !== "") ? sh : "—";
        if (hasCode) {
          const code = it.code ? ` [${it.code}]` : "";
          lines.push(`• ${it.name}${code}  —  ${qty}`);
        } else {
          lines.push(`• ${it.name}  —  ${qty}`);
        }
      });
    }
    if (excludedCount > 0) {
      lines.push(``);
      lines.push(`(${excludedCount} item${excludedCount === 1 ? "" : "s"} skipped at staff request)`);
    }
    return { lines, orderDate };
  };

  const emailOrder = () => {
    const { lines, orderDate } = buildOrderLines();
    window.location.href = buildMailto(`${supplierName} Order — Thursday ${orderDate}`, lines, sup.email);
  };

  const [copied, setCopied] = useState(false);
  const copyOrder = async () => {
    const { lines } = buildOrderLines();
    try {
      await navigator.clipboard.writeText(lines.join("\n"));
      setCopied(true);
      setTimeout(() => setCopied(false), 2000);
    } catch { alert("Couldn't copy — try the email button instead."); }
  };

  return (
    <div style={{ display: "flex", flexDirection: "column", flex: 1, minHeight: 0 }}>
      <PageHeader title="Stock" onBack={goHome} editing={editing} setEditing={setEditing} theme={theme}/>
      <SyncIndicator
        theme={theme}
        syncState={sup.sync.syncState}
      />

      {/* Supplier tabs */}
      <div style={{
        display: "grid", gridTemplateColumns: "1fr 1fr", gap: 6,
        padding: "10px 20px 4px", background: theme.headerBg
      }}>
        {Object.entries(suppliers).map(([id, s]) => {
          const active = supplier === id;
          return (
            <button key={id} onClick={() => setSupplier(id)} style={{
              padding: "8px 8px", borderRadius: 10,
              border: `1px solid ${active ? theme.accent : theme.rowBorder}`,
              background: active ? theme.accentSoft : "transparent",
              color: active ? theme.accent : theme.muted,
              fontFamily: "inherit", cursor: "pointer", textAlign: "center"
            }}>
              <div style={{
                fontFamily: "'Playfair Display', serif", fontWeight: 700, fontSize: 14
              }}>{s.label}</div>
              <div style={{ fontSize: 9, letterSpacing: "0.14em", textTransform: "uppercase", marginTop: 2, opacity: 0.8 }}>{s.sub}</div>
            </button>
          );
        })}
      </div>

      <div style={{ padding: "10px 20px 28px", overflowY: "auto", flex: 1 }}>
        {/* Summary */}
        <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 8, marginTop: 14, marginBottom: 14 }}>
          <StatCard theme={theme} label="Items" value={current.length}/>
          <StatCard theme={theme} label="To Order" value={needCount} accent={needCount > 0}/>
        </div>

        {/* Column legend */}
        <div style={{
          display: "grid",
          gridTemplateColumns: hasCode ? "1fr 60px 60px 54px" : "1fr 60px 60px 54px",
          gap: 6, padding: "0 10px",
          fontSize: 10, fontWeight: 700, color: theme.muted,
          textTransform: "uppercase", letterSpacing: "0.12em", marginBottom: 6
        }}>
          <span>Item {hasCode && <span style={{opacity:0.6}}> / code</span>}</span>
          <span style={{ textAlign: "center" }}>Have</span>
          <span style={{ textAlign: "center" }}>Must</span>
          <span style={{ textAlign: "center", color: theme.accent }}>Order</span>
        </div>

        <div style={{ display: "flex", flexDirection: "column", gap: 6 }}>
          {current.map(item => (
            <StockRow key={item.id} item={item} theme={theme}
              editing={editing} hasCode={hasCode}
              onUpdate={patch => update(item.id, patch)}
              onRemove={() => remove(item.id)}
              onToggleExclude={() => update(item.id, { excludeFromEmail: !item.excludeFromEmail })}/>
          ))}
          {editing && <AddRow placeholder={`Add ${supplierName} item…`} onAdd={add} theme={theme}/>}
        </div>

        <div style={{ height: 18 }}/>
        <SaveStockCheckButton theme={theme} carn={carn} veg={veg}/>
        <button onClick={emailOrder} style={primaryBtn(theme)}>
          {Icon.mail(18)} Email {supplierName} Order
        </button>
        <button onClick={copyOrder} style={{ ...ghostBtn(theme), marginTop: 8 }}>
          {Icon.clipboard(16)} {copied ? "Copied!" : "Copy order to clipboard"}
        </button>
      </div>
    </div>
  );
}

function StatCard({ theme, label, value, accent }) {
  return (
    <div style={{
      padding: "12px 10px", borderRadius: 12,
      background: accent ? theme.accentSoft : theme.tile,
      border: `1px solid ${accent ? theme.accent : theme.tileBorder}`,
      textAlign: "center"
    }}>
      <div style={{
        fontFamily: "'Playfair Display', serif",
        fontWeight: 700, fontSize: 22,
        color: accent ? theme.accent : theme.text, lineHeight: 1
      }}>{value}</div>
      <div style={{ fontSize: 10, color: theme.muted, letterSpacing: "0.14em", textTransform: "uppercase", marginTop: 6 }}>{label}</div>
    </div>
  );
}

const StockTextField = React.memo(function StockTextField({ value, onChange, theme, align = "center", width }) {
  return (
    <input value={value || ""} onChange={e => onChange(e.target.value)}
      style={{
        width: width || "100%", textAlign: align, fontSize: 14, fontWeight: 600,
        border: `1px solid ${theme.rowBorder}`, borderRadius: 8,
        padding: "6px 4px",
        background: theme.input, color: theme.text,
        fontFamily: "inherit", outline: "none", boxSizing: "border-box"
      }}/>
  );
});

function StockRow({ item, theme, editing, hasCode, onUpdate, onRemove, onToggleExclude }) {
  const status = stockStatus(item);
  const short = stockShortfall(item);
  const excluded = !!item.excludeFromEmail;
  const dotColor = excluded ? theme.muted : ({
    out: "#d64545", low: "#e8b059", partial: "#e8b059",
    ok: theme.muted, full: "#6fb58a"
  }[status]);

  return (
    <div style={{
      display: "grid",
      gridTemplateColumns: "1fr 60px 60px 54px",
      gap: 6, alignItems: "center",
      padding: "10px 10px",
      background: theme.row, borderRadius: 10,
      border: `1px solid ${theme.rowBorder}`,
      borderLeftWidth: 3, borderLeftColor: dotColor,
      opacity: excluded ? 0.55 : 1
    }}>
      <div style={{ minWidth: 0 }}>
        {editing ? (
          <input value={item.name} onChange={e => onUpdate({ name: e.target.value })}
            style={{
              width: "100%", border: "none", background: "transparent",
              color: theme.text, fontSize: 15, fontWeight: 600,
              fontFamily: "inherit", outline: "none", padding: 0
            }}/>
        ) : (
          <div style={{
            fontSize: 15, fontWeight: 600, color: theme.text,
            whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis",
            textDecoration: excluded ? "line-through" : "none"
          }}>{item.name}</div>
        )}
        {hasCode && (
          editing ? (
            <input value={item.code || ""} onChange={e => onUpdate({ code: e.target.value })}
              placeholder="Product code" style={{
                width: "100%", border: "none", background: "transparent",
                color: theme.accent, fontSize: 11, fontWeight: 600,
                letterSpacing: "0.14em", textTransform: "uppercase",
                fontFamily: "inherit", outline: "none", padding: 0, marginTop: 3
              }}/>
          ) : (item.code ? (
            <div style={{
              fontSize: 10, fontWeight: 700, color: theme.accent,
              letterSpacing: "0.16em", marginTop: 3
            }}>{item.code}</div>
          ) : null)
        )}
        <div style={{ display: "flex", gap: 12, marginTop: 4, alignItems: "center" }}>
          <button onClick={onToggleExclude} title={excluded ? "Include in email" : "Don't include in email"}
            style={{
              background: "transparent", border: "none",
              color: excluded ? "#6fb58a" : theme.muted,
              fontSize: 10, fontWeight: 700, cursor: "pointer", padding: 0,
              letterSpacing: "0.08em", textTransform: "uppercase"
            }}>
            {excluded ? "✓ Include in email" : "✕ Skip in email"}
          </button>
          {editing && (
            <button onClick={onRemove} style={{
              background: "transparent", border: "none",
              color: "#d64545", fontSize: 10, fontWeight: 700,
              cursor: "pointer", padding: 0, letterSpacing: "0.08em",
              textTransform: "uppercase"
            }}>Remove</button>
          )}
        </div>
      </div>
      <StockTextField theme={theme} value={item.inStock} onChange={v => onUpdate({ inStock: v })}/>
      <StockTextField theme={theme} value={item.mustHave} onChange={v => onUpdate({ mustHave: v })}/>
      <div style={{
        textAlign: "center", fontSize: 15, fontWeight: 800,
        color: short && short !== "0" && !excluded ? theme.accent : theme.muted,
        fontFamily: "'Playfair Display', serif"
      }}>{short || "—"}</div>
    </div>
  );
}

// =========================================================
//   BUTTON STYLES
// =========================================================
const primaryBtn = (theme) => ({
  width: "100%", padding: "14px 16px", borderRadius: 12,
  border: "none", background: theme.accent, color: theme.accentText,
  fontSize: 15, fontWeight: 700, letterSpacing: "0.04em",
  fontFamily: "inherit", cursor: "pointer",
  display: "inline-flex", alignItems: "center", justifyContent: "center", gap: 8,
  marginBottom: 6
});
const ghostBtn = (theme) => ({
  width: "100%", padding: "12px 16px", borderRadius: 12,
  border: `1px solid ${theme.rowBorder}`, background: "transparent",
  color: theme.muted, fontSize: 14, fontWeight: 500,
  fontFamily: "inherit", cursor: "pointer"
});

// =========================================================
//   APP SHELL
// =========================================================
// =========================================================
//   POTWASH / KITCHEN PORTER TAB
// =========================================================
function PotwashTab({ theme, goHome, subnav }) {
  const [openItems, setOpenItems] = useShared("fs.potwash.opening.v1", DEFAULT_POTWASH_OPENING);
  const [closeItems, setCloseItems] = useShared("fs.potwash.closing.v1", DEFAULT_POTWASH_CLOSING);
  const [deepItems, setDeepItems] = useShared("fs.potwash.deep.v1", DEFAULT_POTWASH_DEEP);
  const [checked, setChecked] = useShared("fs.potwash.checked.v1", {});
  const [editing, setEditing] = useState(false);

  const isSunday = new Date().getDay() === 0;
  const allItems = isSunday ? [...openItems, ...closeItems, ...deepItems] : [...openItems, ...closeItems];
  const pct = allItems.length ? Math.round(allItems.filter(n => checked[n]).length / allItems.length * 100) : 0;

  const toggle = (name) => setChecked({ ...checked, [name]: !checked[name] });
  const addOpen = (name) => setOpenItems([...openItems, name]);
  const removeOpen = (name) => { setOpenItems(openItems.filter(x => x !== name)); const nc = { ...checked }; delete nc[name]; setChecked(nc); };
  const addClose = (name) => setCloseItems([...closeItems, name]);
  const removeClose = (name) => { setCloseItems(closeItems.filter(x => x !== name)); const nc = { ...checked }; delete nc[name]; setChecked(nc); };
  const addDeep = (name) => setDeepItems([...deepItems, name]);
  const removeDeep = (name) => { setDeepItems(deepItems.filter(x => x !== name)); const nc = { ...checked }; delete nc[name]; setChecked(nc); };

  return (
    <div style={{ display: "flex", flexDirection: "column", flex: 1, minHeight: 0 }}>
      <PageHeader title="Potwash" onBack={goHome} editing={editing} setEditing={setEditing} theme={theme} />
      {subnav}
      <div style={{ padding: "18px 20px 28px", overflowY: "auto", flex: 1 }}>
        <InitialsGate listId="potwash" label="Potwash" theme={theme}>
        <Progress percent={pct} theme={theme} />

        <SectionHead theme={theme} sub="Start of shift" count={`${openItems.filter(n => checked[n]).length}/${openItems.length}`}>Opening</SectionHead>
        <div style={{ display: "flex", flexDirection: "column", gap: 8 }}>
          {openItems.map((n, i) => (
            <ChecklistRow key={n} label={n} checked={!!checked[n]}
              onToggle={() => toggle(n)} onDelete={() => removeOpen(n)}
              onMove={d => setOpenItems(moveItem(openItems, i, d))}
              editing={editing} theme={theme}/>
          ))}
          {editing && <AddRow placeholder="Add opening task…" onAdd={addOpen} theme={theme}/>}
        </div>

        <SectionHead theme={theme} sub="End of shift" count={`${closeItems.filter(n => checked[n]).length}/${closeItems.length}`}>Closing</SectionHead>
        <div style={{ display: "flex", flexDirection: "column", gap: 8 }}>
          {closeItems.map((n, i) => (
            <ChecklistRow key={n} label={n} checked={!!checked[n]}
              onToggle={() => toggle(n)} onDelete={() => removeClose(n)}
              onMove={d => setCloseItems(moveItem(closeItems, i, d))}
              editing={editing} theme={theme}/>
          ))}
          {editing && <AddRow placeholder="Add closing task…" onAdd={addClose} theme={theme}/>}
        </div>

        {(isSunday || editing) && <>
          <SectionHead theme={theme} sub="Sundays only" count={`${deepItems.filter(n => checked[n]).length}/${deepItems.length}`}>Deep Clean</SectionHead>
          <div style={{ display: "flex", flexDirection: "column", gap: 8 }}>
            {deepItems.map((n, i) => (
              <ChecklistRow key={n} label={n} checked={!!checked[n]}
                onToggle={() => toggle(n)} onDelete={() => removeDeep(n)}
                onMove={d => setDeepItems(moveItem(deepItems, i, d))}
                editing={editing} theme={theme}/>
            ))}
            {editing && <AddRow placeholder="Add deep clean task…" onAdd={addDeep} theme={theme}/>}
          </div>
        </>}

        <div style={{ height: 16 }}/>
        <EndDayButton theme={theme} scope="up" onSaved={goHome}/>
        </InitialsGate>
      </div>
    </div>
  );
}

// =========================================================
//   PREP CHEF TAB — daily prep tasks (added in-app) plus a
//   Sundays-only deep clean section.
// =========================================================
function PrepChefTab({ theme, goHome, subnav }) {
  const [openItems, setOpenItems] = useShared("fs.prep.open.v1", DEFAULT_PREP_OPEN);
  const [data, setData] = useShared("fs.prep.cats.v1", DEFAULT_PREP_CATS);
  const [closingItems, setClosingItems] = useShared("fs.prep.closing.v2", DEFAULT_PREP_CLOSING);
  const [deepItems, setDeepItems] = useShared("fs.prep.deep.v1", DEFAULT_PREP_DEEP);
  const [checked, setChecked] = useShared("fs.prep.checked.v1", {});
  const [qty, setQty] = useShared("fs.prep.qty.v1", {});
  const [editing, setEditing] = useState(false);

  // Three views inside Prep Chef: Opening (working list), Prep List (working
  // list) and the recorded Closing Checklist (incl. Deep Clean).
  const [view, setView] = useLocal("fs.prep.view.v1", "open");
  const dailyKeys = Object.entries(data).flatMap(([c, its]) => its.map(n => `${c}:${n}`));
  const recordedKeys = [...closingItems, ...deepItems];
  const openPct = openItems.length ? Math.round(openItems.filter(k => checked[k]).length / openItems.length * 100) : 0;
  const dailyPct = dailyKeys.length ? Math.round(dailyKeys.filter(k => checked[k]).length / dailyKeys.length * 100) : 0;
  const recordedPct = recordedKeys.length ? Math.round(recordedKeys.filter(k => checked[k]).length / recordedKeys.length * 100) : 0;

  const toggleKey = (k) => setChecked({ ...checked, [k]: !checked[k] });
  const clearDaily = () => { const nc = { ...checked }; dailyKeys.forEach(k => delete nc[k]); setChecked(nc); setQty({}); };
  const clearOpen = () => { const nc = { ...checked }; openItems.forEach(k => delete nc[k]); setChecked(nc); };
  const addOpen = (name) => setOpenItems([...openItems, name]);
  const removeOpen = (name) => { setOpenItems(openItems.filter(x => x !== name)); const nc = { ...checked }; delete nc[name]; setChecked(nc); };
  const addItem = (cat, name) => setData({ ...data, [cat]: [...data[cat], name] });
  const deleteItem = (cat, name) => {
    setData({ ...data, [cat]: data[cat].filter(x => x !== name) });
    const nc = { ...checked }; delete nc[`${cat}:${name}`]; setChecked(nc);
  };
  const addClosing = (name) => setClosingItems([...closingItems, name]);
  const removeClosing = (name) => { setClosingItems(closingItems.filter(x => x !== name)); const nc = { ...checked }; delete nc[name]; setChecked(nc); };
  const addDeep = (name) => setDeepItems([...deepItems, name]);
  const removeDeep = (name) => { setDeepItems(deepItems.filter(x => x !== name)); const nc = { ...checked }; delete nc[name]; setChecked(nc); };

  return (
    <div style={{ display: "flex", flexDirection: "column", flex: 1, minHeight: 0 }}>
      <PageHeader title="Prep Chef" onBack={goHome} editing={editing} setEditing={setEditing} theme={theme} />
      {subnav}
      <div style={{ padding: "18px 20px 28px", overflowY: "auto", flex: 1 }}>
        {/* Prep List / Closing Checklist view switcher */}
        <div style={{
          display: "flex", gap: 6, padding: 4, marginBottom: 16,
          background: theme.row, border: `1px solid ${theme.rowBorder}`, borderRadius: 12
        }}>
          {[
            { id: "open", label: "Opening" },
            { id: "prep", label: "Prep List" },
            { id: "closing", label: "Closing" }
          ].map(v => {
            const on = v.id === view;
            return (
              <button key={v.id} onClick={() => setView(v.id)} style={{
                flex: 1, padding: "9px 6px", borderRadius: 9, border: "none",
                background: on ? theme.accent : "transparent",
                color: on ? theme.accentText : theme.text,
                fontWeight: 700, fontSize: 13, cursor: "pointer", fontFamily: "inherit"
              }}>{v.label}</button>
            );
          })}
        </div>

        {view === "open" && <>
        <Progress percent={openPct} theme={theme} />
        <div style={{ marginTop: 18, display: "flex", flexDirection: "column", gap: 8 }}>
          {openItems.map((n, i) => (
            <ChecklistRow key={n} label={n} checked={!!checked[n]}
              onToggle={() => toggleKey(n)} onDelete={() => removeOpen(n)}
              onMove={d => setOpenItems(moveItem(openItems, i, d))}
              editing={editing} theme={theme}/>
          ))}
          {editing && <AddRow placeholder="Add opening task…" onAdd={addOpen} theme={theme}/>}
        </div>
        <div style={{ height: 16 }}/>
        <button onClick={() => window.confirm("Clear all opening ticks? This cannot be undone.") && clearOpen()} style={ghostBtn(theme)}>Clear all checks</button>
        </>}

        {view === "prep" && <>
        <Progress percent={dailyPct} theme={theme} />
        {Object.keys(data).map(cat => (
          <div key={cat}>
            <SectionHead theme={theme} count={`${data[cat].filter(n => checked[`${cat}:${n}`]).length}/${data[cat].length}`}>{cat}</SectionHead>
            <div style={{ display: "flex", flexDirection: "column", gap: 8 }}>
              {data[cat].map((name, i) => (
                <ChecklistRow key={name} label={name}
                  checked={!!checked[`${cat}:${name}`]}
                  onToggle={() => toggleKey(`${cat}:${name}`)}
                  onDelete={() => deleteItem(cat, name)}
                  qty={qty[`${cat}:${name}`]}
                  onQty={v => setQty({ ...qty, [`${cat}:${name}`]: v })}
                  onMove={d => setData({ ...data, [cat]: moveItem(data[cat], i, d) })}
                  editing={editing} theme={theme}/>
              ))}
              {editing && <AddRow placeholder={`Add to ${cat}…`} onAdd={n => addItem(cat, n)} theme={theme}/>}
            </div>
          </div>
        ))}
        <div style={{ height: 16 }}/>
        <button onClick={() => window.confirm("Clear all prep ticks? This cannot be undone.") && clearDaily()} style={ghostBtn(theme)}>Clear all checks</button>
        </>}

        {view === "closing" && <>
        <InitialsGate listId="prep" label="Prep Closing" theme={theme}>
        <Progress percent={recordedPct} theme={theme} />
        <SectionHead theme={theme} sub="End of shift — recorded" count={`${closingItems.filter(n => checked[n]).length}/${closingItems.length}`}>Closing Checklist</SectionHead>
        <div style={{ display: "flex", flexDirection: "column", gap: 8 }}>
          {closingItems.map((n, i) => (
            <ChecklistRow key={n} label={n} checked={!!checked[n]}
              onToggle={() => toggleKey(n)} onDelete={() => removeClosing(n)}
              onMove={d => setClosingItems(moveItem(closingItems, i, d))}
              editing={editing} theme={theme}/>
          ))}
          {editing && <AddRow placeholder="Add closing task…" onAdd={addClosing} theme={theme}/>}
        </div>

        <SectionHead theme={theme} sub="Recorded" count={`${deepItems.filter(n => checked[n]).length}/${deepItems.length}`}>Deep Clean</SectionHead>
        <div style={{ display: "flex", flexDirection: "column", gap: 8 }}>
          {deepItems.map((n, i) => (
            <ChecklistRow key={n} label={n} checked={!!checked[n]}
              onToggle={() => toggleKey(n)} onDelete={() => removeDeep(n)}
              onMove={d => setDeepItems(moveItem(deepItems, i, d))}
              editing={editing} theme={theme}/>
          ))}
          {editing && <AddRow placeholder="Add deep clean task…" onAdd={addDeep} theme={theme}/>}
        </div>
        </InitialsGate>
        </>}
      </div>
    </div>
  );
}

// =========================================================
//   DOWNSTAIRS / UPSTAIRS WRAPPERS — a section switcher
//   rendered under each list's header via the subnav prop.
// =========================================================
function SectionSwitcher({ options, value, onChange, theme }) {
  return (
    <div style={{
      display: "flex", gap: 6, padding: 4, margin: "10px 20px 0",
      background: theme.row, border: `1px solid ${theme.rowBorder}`, borderRadius: 12
    }}>
      {options.map(o => {
        const on = o.id === value;
        return (
          <button key={o.id} onClick={() => onChange(o.id)} style={{
            flex: 1, padding: "9px 6px", borderRadius: 9, border: "none",
            background: on ? theme.accent : "transparent",
            color: on ? theme.accentText : theme.text,
            fontWeight: 700, fontSize: 13, cursor: "pointer", fontFamily: "inherit"
          }}>{o.label}</button>
        );
      })}
    </div>
  );
}

function DownstairsTab({ theme, goHome }) {
  const [sec, setSec] = useLocal("fs.down.section.v1", "opening");
  const subnav = (
    <SectionSwitcher theme={theme} value={sec} onChange={setSec} options={[
      { id: "opening", label: "Opening" },
      { id: "lineup", label: "Line Up" },
      { id: "checklist", label: "Closing" }
    ]}/>
  );
  if (sec === "lineup") return <LineupTab theme={theme} goHome={goHome} subnav={subnav}/>;
  if (sec === "checklist") return <ChecklistTab theme={theme} goHome={goHome} subnav={subnav}/>;
  return <OpeningTab theme={theme} goHome={goHome} subnav={subnav}/>;
}

function UpstairsTab({ theme, goHome }) {
  const [sec, setSec] = useLocal("fs.up.section.v1", "potwash");
  const subnav = (
    <SectionSwitcher theme={theme} value={sec} onChange={setSec} options={[
      { id: "potwash", label: "Potwash" },
      { id: "prep", label: "Prep Chef" }
    ]}/>
  );
  if (sec === "prep") return <PrepChefTab theme={theme} goHome={goHome} subnav={subnav}/>;
  return <PotwashTab theme={theme} goHome={goHome} subnav={subnav}/>;
}

// =========================================================
//   HISTORY TAB — Head Chef view of saved day logs
// =========================================================
function HistoryStaticRow({ label, done, theme }) {
  return (
    <div style={{
      display: "flex", alignItems: "center", gap: 10,
      padding: "8px 12px", background: theme.row, borderRadius: 8,
      border: `1px solid ${theme.rowBorder}`, opacity: done ? 1 : 0.55
    }}>
      <span style={{
        width: 20, height: 20, borderRadius: 6, flexShrink: 0,
        display: "flex", alignItems: "center", justifyContent: "center",
        border: `2px solid ${done ? theme.accent : theme.checkBorder}`,
        background: done ? theme.accent : "transparent", color: theme.checkMark
      }}>{done && Icon.check(11)}</span>
      <span style={{
        fontSize: 14, color: theme.text,
        textDecoration: done ? "none" : "none"
      }}>{label}</span>
    </div>
  );
}

function HistoryListSection({ id, initials, pct, children, theme }) {
  return (
    <div style={{ marginBottom: 18 }}>
      <div style={{
        display: "flex", alignItems: "baseline", gap: 10, marginBottom: 8,
        paddingBottom: 6, borderBottom: `1px solid ${theme.accent}`
      }}>
        <span style={{
          fontSize: 13, fontWeight: 700, color: theme.accent,
          textTransform: "uppercase", letterSpacing: "0.1em"
        }}>{DAYLOG_LABELS[id] || id}</span>
        <span style={{ fontSize: 12, color: theme.muted }}>{pct}%</span>
        <span style={{ flex: 1 }}/>
        <span style={{
          fontSize: 13, fontWeight: 800, letterSpacing: "0.1em",
          color: initials ? theme.accent : theme.muted
        }}>{initials || "—"}</span>
      </div>
      <div style={{ display: "flex", flexDirection: "column", gap: 5 }}>{children}</div>
    </div>
  );
}

function HistoryTab({ theme, goHome }) {
  const [days, setDays] = useState(null);   // null = loading
  const [stockDays, setStockDays] = useState({});
  const [error, setError] = useState("");
  const [detail, setDetail] = useState(null); // { date, record, stock }
  const [loadingDay, setLoadingDay] = useState("");

  const doughPw = () => (window.FS_SESSION && window.FS_SESSION.doughPw) || "";

  const loadAll = async () => {
    try {
      const base = getWorkerBase();
      if (!base) throw new Error("No server configured.");
      const hdrs = { headers: { "X-Dough-Password": doughPw() } };
      const [dRes, sRes] = await Promise.all([
        fetch(`${base}/daylogs`, hdrs),
        fetch(`${base}/stocklogs`, hdrs)
      ]);
      if (!dRes.ok) throw new Error(`Couldn't load history (HTTP ${dRes.status}).`);
      const dData = await dRes.json();
      setDays(Array.isArray(dData.days) ? dData.days : []);
      if (sRes.ok) {
        const sData = await sRes.json();
        const map = {};
        (sData.days || []).forEach(d => { map[d.date] = d; });
        setStockDays(map);
      }
    } catch (e) {
      setError(e.message || "Couldn't load history.");
      setDays([]);
    }
  };

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

  // Delete a wrong record (checklists or stock check) for a date, then refresh.
  const deleteLog = async (kind, date) => {
    const what = kind === "daylog" ? "checklist record" : "stock check";
    if (!window.confirm(`Delete the ${what} for ${dayLabel(date)}? This cannot be undone.`)) return;
    try {
      const base = getWorkerBase();
      const res = await fetch(`${base}/${kind}?date=${date}`, {
        method: "DELETE",
        headers: { "X-Dough-Password": doughPw() }
      });
      if (!res.ok) throw new Error(`HTTP ${res.status}`);
      setDetail(null);
      setDays(null); // show loading while the list refreshes
      await loadAll();
    } catch (e) {
      window.alert(`Couldn't delete (${e.message}).`);
    }
  };

  const openDay = async (date) => {
    setLoadingDay(date);
    try {
      const base = getWorkerBase();
      const hdrs = { headers: { "X-Dough-Password": doughPw() } };
      const [dR, sR] = await Promise.all([
        fetch(`${base}/daylog?date=${date}`, hdrs),
        fetch(`${base}/stocklog?date=${date}`, hdrs)
      ]);
      const rec = dR.ok ? (await dR.json()).record : null;
      const stockRec = sR.ok ? (await sR.json()).record : null;
      if (!rec && !stockRec) throw new Error(`Couldn't load ${date}.`);
      setDetail({ date, record: rec, stock: stockRec });
    } catch (e) {
      alert(e.message || "Couldn't load that day.");
    } finally {
      setLoadingDay("");
    }
  };

  // Fill in unsaved gaps between the newest and oldest saved day so missing
  // days are obvious. Today is skipped unless it has been saved.
  const rows = useMemo(() => {
    const dayMap = {};
    (days || []).forEach(d => { dayMap[d.date] = d; });
    const allDates = [...new Set([...Object.keys(dayMap), ...Object.keys(stockDays)])].sort();
    if (!allDates.length) return [];
    const oldest = allDates[0];
    const out = [];
    const cur = new Date(); cur.setHours(0, 0, 0, 0);
    const toKey = (d) => `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`;
    if (!dayMap[toKey(cur)] && !stockDays[toKey(cur)]) cur.setDate(cur.getDate() - 1); // skip unsaved today
    while (toKey(cur) >= oldest) {
      const key = toKey(cur);
      const d = dayMap[key];
      const s = stockDays[key] || null;
      out.push({ date: key, saved: !!d, summary: d ? d.summary : null, stock: s });
      cur.setDate(cur.getDate() - 1);
    }
    return out;
  }, [days, stockDays]);

  // ---- Detail view ----
  if (detail) {
    const rec = detail.record || {};
    const lists = rec.lists || {};
    const initials = rec.initials || {};
    const sections = [];
    if (lists.opening) sections.push({ id: "opening", pct: pctOfKeys(lists.opening.items || [], lists.opening.checked || {}),
      rows: (lists.opening.items || []).map(n => ({ label: n, done: !!(lists.opening.checked || {})[n] })) });
    if (lists.closing) sections.push({ id: "closing", pct: pctOfKeys(lists.closing.items || [], lists.closing.checked || {}),
      rows: (lists.closing.items || []).map(n => ({ label: n, done: !!(lists.closing.checked || {})[n] })) });
    if (lists.sunday) sections.push({ id: "sunday", pct: pctOfKeys(lists.sunday.items || [], lists.sunday.checked || {}),
      rows: (lists.sunday.items || []).map(n => ({ label: n, done: !!(lists.sunday.checked || {})[n] })) });
    if (lists.potwash) {
      const pw = lists.potwash;
      const pwItems = [...(pw.opening || []), ...(pw.closing || []), ...(pw.deep || [])];
      sections.push({ id: "potwash", pct: pctOfKeys(pwItems, pw.checked || {}),
        rows: pwItems.map(n => ({ label: n, done: !!(pw.checked || {})[n] })) });
    }
    if (lists.prep) {
      const pr = lists.prep;
      const prItems = [...(pr.closing || []), ...(pr.items || []), ...(pr.deep || [])];
      sections.push({ id: "prep", pct: pctOfKeys(prItems, pr.checked || {}),
        rows: prItems.map(n => ({ label: n, done: !!(pr.checked || {})[n] })) });
    }
    const stockRec = detail.stock;
    return (
      <div style={{ display: "flex", flexDirection: "column", flex: 1, minHeight: 0 }}>
        <PageHeader title={dayLabel(detail.date)} onBack={() => setDetail(null)} theme={theme}/>
        <div style={{ padding: "18px 20px 28px", overflowY: "auto", flex: 1 }}>
          {sections.map(s => (
            <HistoryListSection key={s.id} id={s.id} initials={initials[s.id]} pct={s.pct} theme={theme}>
              {s.rows.map((r, i) => <HistoryStaticRow key={i} label={r.label} done={r.done} theme={theme}/>)}
            </HistoryListSection>
          ))}
          {stockRec && Object.entries(stockRec.suppliers || {}).map(([supId, items]) => {
            const label = supId === "carnevale" ? "Stock Check — Carnevale" : supId === "vegfactor" ? "Stock Check — Veg Factor" : `Stock Check — ${supId}`;
            return (
              <div key={supId} style={{ marginBottom: 18 }}>
                <div style={{
                  display: "flex", alignItems: "baseline", gap: 10, marginBottom: 8,
                  paddingBottom: 6, borderBottom: `1px solid ${theme.accent}`
                }}>
                  <span style={{
                    fontSize: 13, fontWeight: 700, color: theme.accent,
                    textTransform: "uppercase", letterSpacing: "0.1em"
                  }}>{label}</span>
                  <span style={{ flex: 1 }}/>
                  <span style={{
                    fontSize: 13, fontWeight: 800, letterSpacing: "0.1em",
                    color: stockRec.initials ? theme.accent : theme.muted
                  }}>{stockRec.initials || "—"}</span>
                </div>
                <div style={{ display: "flex", flexDirection: "column", gap: 5 }}>
                  {(items || []).map((it, i) => {
                    const sh = stockShortfall(it);
                    const needs = sh && sh !== "0";
                    return (
                      <div key={i} style={{
                        display: "flex", alignItems: "center", gap: 10,
                        padding: "8px 12px", background: theme.row, borderRadius: 8,
                        border: `1px solid ${theme.rowBorder}`,
                        borderLeftWidth: 3, borderLeftColor: needs ? (theme.danger || "#d64545") : (theme.positive || "#7d9c6a")
                      }}>
                        <span style={{ flex: 1, fontSize: 14, color: theme.text }}>{it.name}</span>
                        <span style={{ fontSize: 12, color: theme.muted }}>
                          have {it.inStock || "0"} · need {it.mustHave || "0"}{needs ? ` · order ${sh}` : ""}
                        </span>
                      </div>
                    );
                  })}
                </div>
              </div>
            );
          })}

          <div style={{ height: 10 }}/>
          {detail.record && (
            <button onClick={() => deleteLog("daylog", detail.date)} style={{
              ...ghostBtn(theme), color: theme.danger || "#d64545",
              borderColor: theme.danger || "#d64545", marginBottom: 8
            }}>Delete checklist record</button>
          )}
          {stockRec && (
            <button onClick={() => deleteLog("stocklog", detail.date)} style={{
              ...ghostBtn(theme), color: theme.danger || "#d64545",
              borderColor: theme.danger || "#d64545"
            }}>Delete stock check</button>
          )}
        </div>
      </div>
    );
  }

  // ---- Day list ----
  let lastMonth = "";
  return (
    <div style={{ display: "flex", flexDirection: "column", flex: 1, minHeight: 0 }}>
      <PageHeader title="History" onBack={goHome} theme={theme}/>
      <div style={{ padding: "14px 20px 28px", overflowY: "auto", flex: 1 }}>
        {days === null && <div style={{ textAlign: "center", color: theme.muted, marginTop: 30 }}>Loading…</div>}
        {days !== null && error && (
          <div style={{ textAlign: "center", color: theme.danger || "#c8413a", fontWeight: 600, marginTop: 30, fontSize: 14 }}>{error}</div>
        )}
        {days !== null && !error && !rows.length && (
          <div style={{ textAlign: "center", color: theme.muted, marginTop: 30, fontSize: 14, lineHeight: 1.6 }}>
            No days saved yet.<br/>Records appear here after the first “End the Day”.
          </div>
        )}
        {rows.map(r => {
          const mLabel = monthLabel(r.date);
          const showMonth = mLabel !== lastMonth;
          lastMonth = mLabel;
          const sum = r.summary || {};
          const pct = sum.pct || {};
          const ini = sum.initials || {};
          return (
            <React.Fragment key={r.date}>
              {showMonth && (
                <div style={{
                  fontSize: 12, fontWeight: 700, color: theme.accent,
                  textTransform: "uppercase", letterSpacing: "0.14em",
                  margin: "18px 0 8px", paddingBottom: 6,
                  borderBottom: `1px solid ${theme.accent}`
                }}>{mLabel}</div>
              )}
              {(r.saved || r.stock) ? (
                <button onClick={() => openDay(r.date)} disabled={loadingDay === r.date} style={{
                  display: "block", width: "100%", textAlign: "left",
                  padding: "12px 14px", borderRadius: 10, marginBottom: 6,
                  background: theme.row, border: `1px solid ${theme.rowBorder}`,
                  cursor: "pointer", fontFamily: "inherit",
                  opacity: loadingDay === r.date ? 0.6 : 1
                }}>
                  <div style={{ fontSize: 15, fontWeight: 700, color: theme.text, marginBottom: 4 }}>
                    {dayLabel(r.date)}
                  </div>
                  <div style={{ display: "flex", flexWrap: "wrap", gap: "4px 14px" }}>
                    {["opening", "closing", "potwash", "prep", "sunday"]
                      .filter(id => pct[id] !== undefined)
                      .map(id => (
                        <span key={id} style={{ fontSize: 12, color: theme.muted }}>
                          {DAYLOG_LABELS[id]} {pct[id]}% · <b style={{ color: ini[id] ? theme.accent : theme.muted, letterSpacing: "0.06em" }}>{ini[id] || "—"}</b>
                        </span>
                      ))}
                    {!r.saved && (
                      <span style={{ fontSize: 12, color: theme.muted, fontStyle: "italic" }}>Checklists not saved</span>
                    )}
                    {r.stock && (
                      <span style={{ fontSize: 12, color: theme.positive || "#7d9c6a", fontWeight: 600 }}>
                        Stock ✓{(r.stock.summary && r.stock.summary.toOrder !== undefined) ? ` ${r.stock.summary.toOrder} to order` : ""} · <b style={{ letterSpacing: "0.06em" }}>{(r.stock.summary && r.stock.summary.initials) || "—"}</b>
                      </span>
                    )}
                  </div>
                </button>
              ) : (
                <div style={{
                  padding: "8px 14px", borderRadius: 10, marginBottom: 6,
                  border: `1px dashed ${theme.rowBorder}`,
                  fontSize: 13, color: theme.muted, opacity: 0.7
                }}>
                  {dayLabel(r.date)} — not saved
                </div>
              )}
            </React.Fragment>
          );
        })}
      </div>
    </div>
  );
}

function App({ theme, storageNs = "default" }) {
  const [tab, setTab] = useLocal(`fs.tab.${storageNs}`, "home");

  const lineupData = LS.get("fs.lineup.v2", DEFAULT_LINEUP);
  const lineupChecked = LS.get("fs.lineup.checked.v2", {});
  const allLineup = Object.entries(lineupData).flatMap(([c, its]) => its.map(n => `${c}:${n}`));
  const lineupPct = allLineup.length ? Math.round(allLineup.filter(k => lineupChecked[k]).length / allLineup.length * 100) : 0;

  const checklistItems = LS.get("fs.checklist.v2", DEFAULT_CHECKLIST);
  const checklistChecked = LS.get("fs.checklist.checked.v2", {});
  const cpct = checklistItems.length ? Math.round(checklistItems.filter(n => checklistChecked[n]).length / checklistItems.length * 100) : 0;

  const openingItems = LS.get("fs.opening.v1", DEFAULT_OPENING_CHECKLIST);
  const openingChecked = LS.get("fs.opening.checked.v1", {});
  const openingPct = openingItems.length ? Math.round(openingItems.filter(n => openingChecked[n]).length / openingItems.length * 100) : 0;

  const sundayItems = LS.get("fs.checklist.sunday.v1", DEFAULT_SUNDAY_CHECKLIST);
  const sundayChecked = LS.get("fs.checklist.sunday.checked.v1", {});
  const sundayPct = sundayItems.length ? Math.round(sundayItems.filter(n => sundayChecked[n]).length / sundayItems.length * 100) : 0;

  const stockCarn = LS.get("fs.stock.carnevale.v1", DEFAULT_CARNEVALE);
  const stockVeg = LS.get("fs.stock.vegfactor.v1", DEFAULT_VEGFACTOR);
  const stockShort = [...stockCarn, ...stockVeg].filter(i => {
    const sh = stockShortfall(i); return sh && sh !== "0";
  }).length;

  const isSundayToday = new Date().getDay() === 0;
  const potwashOpen = LS.get("fs.potwash.opening.v1", DEFAULT_POTWASH_OPENING);
  const potwashClose = LS.get("fs.potwash.closing.v1", DEFAULT_POTWASH_CLOSING);
  const potwashDeep = isSundayToday ? LS.get("fs.potwash.deep.v1", DEFAULT_POTWASH_DEEP) : [];
  const potwashChecked = LS.get("fs.potwash.checked.v1", {});
  const potwashAll = [...potwashOpen, ...potwashClose, ...potwashDeep];
  const potwashPct = potwashAll.length ? Math.round(potwashAll.filter(n => potwashChecked[n]).length / potwashAll.length * 100) : 0;

  const prepData = LS.get("fs.prep.cats.v1", DEFAULT_PREP_CATS);
  const prepDaily = Object.entries(prepData).flatMap(([c, its]) => its.map(n => `${c}:${n}`));
  const prepOpen = LS.get("fs.prep.open.v1", DEFAULT_PREP_OPEN);
  const prepClosing = LS.get("fs.prep.closing.v2", DEFAULT_PREP_CLOSING);
  const prepDeep = LS.get("fs.prep.deep.v1", DEFAULT_PREP_DEEP);
  const prepChecked = LS.get("fs.prep.checked.v1", {});
  const prepAllKeys = [...prepOpen, ...prepDaily, ...prepClosing, ...prepDeep];
  const prepPct = prepAllKeys.length ? Math.round(prepAllKeys.filter(k => prepChecked[k]).length / prepAllKeys.length * 100) : undefined;

  const stats = { lineupPct, checklistPct: cpct, openingPct, sundayPct, stockShort, potwashPct, prepPct };

  const goHome = () => setTab("home");
  const go = (t) => setTab(t);
  // BOH / Head Chef section. The section choice persists per device, but the
  // unlock does not survive a reload — the password lives in memory only.
  const [section, setSection] = useLocal(`fs.section.${storageNs}`, "boh");
  const [hcUnlocked, setHcUnlocked] = useState(() => !!(window.FS_SESSION && window.FS_SESSION.doughPw));
  // Bumped after End the Day so the home stats recompute immediately.
  const [, forceRefresh] = useState(0);
  // Legacy tab ids from before the Downstairs/Upstairs split.
  const LEGACY_TABS = { opening: "downstairs", lineup: "downstairs", checklist: "downstairs", potwash: "upstairs" };
  const mappedTab = LEGACY_TABS[tab] || tab;
  // If a gated tab was left open and the unlock is gone (fresh load), fall back home.
  const safeTab = (!hcUnlocked && (mappedTab === "dough" || mappedTab === "stock" || mappedTab === "history")) ? "home" : mappedTab;

  // Mark that the app was worked in today, so the home screen can warn if a
  // previous day was never closed off with End the Day.
  useEffect(() => {
    if (tab && tab !== "home") Sync.set("fs.daylog.activity", todayKey());
  }, [tab]);

  return (
    <div data-screen-label={`App · ${safeTab}`} style={{
      height: "100%", width: "100%", background: theme.bg, color: theme.text,
      fontFamily: "'Inter', system-ui, sans-serif",
      display: "flex", flexDirection: "column", overflow: "hidden"
    }}>
      {safeTab === "home" && <HomeTab theme={theme} go={go} stats={stats} section={section} setSection={setSection} hcUnlocked={hcUnlocked} onUnlock={() => setHcUnlocked(true)} onDayEnded={() => forceRefresh(x => x + 1)}/>}
      {safeTab === "downstairs" && <DownstairsTab theme={theme} goHome={goHome}/>}
      {safeTab === "upstairs" && <UpstairsTab theme={theme} goHome={goHome}/>}
      {hcUnlocked && safeTab === "dough" && <DoughTab theme={theme} goHome={goHome}/>}
      {hcUnlocked && safeTab === "stock" && <StockTab theme={theme} goHome={goHome}/>}
      {hcUnlocked && safeTab === "history" && <HistoryTab theme={theme} goHome={goHome}/>}
    </div>
  );
}

// =========================================================
//   FOH — FRONT OF HOUSE APP
// =========================================================

function FOHOpeningTab({ theme, goHome }) {
  const [items, setItems] = useShared("foh.opening.v1", DEFAULT_FOH_OPENING);
  const [thuItems, setThuItems] = useShared("foh.opening.thu.v1", DEFAULT_FOH_OPENING_THURSDAY);
  const [checked, setChecked] = useShared("foh.opening.checked.v1", {});
  const [editing, setEditing] = useState(false);

  const isThursday = new Date().getDay() === 4;
  const allItems = isThursday ? [...items, ...thuItems] : items;
  const checkedCount = allItems.filter(n => checked[n]).length;
  const pct = allItems.length ? Math.round(checkedCount / allItems.length * 100) : 0;

  const toggle = (name) => setChecked({ ...checked, [name]: !checked[name] });
  const add = (name) => setItems([...items, name]);
  const remove = (name) => { setItems(items.filter(x => x !== name)); const nc = { ...checked }; delete nc[name]; setChecked(nc); };
  const addThu = (name) => setThuItems([...thuItems, name]);
  const removeThu = (name) => { setThuItems(thuItems.filter(x => x !== name)); const nc = { ...checked }; delete nc[name]; setChecked(nc); };

  return (
    <div style={{ display: "flex", flexDirection: "column", flex: 1, minHeight: 0 }}>
      <PageHeader title="Opening" onBack={goHome} editing={editing} setEditing={setEditing} theme={theme}/>
      <div style={{ padding: "18px 20px 28px", overflowY: "auto", flex: 1 }}>
        <Progress percent={pct} theme={theme}/>
        <SectionHead theme={theme} sub="Every service" count={`${items.filter(n => checked[n]).length}/${items.length}`}>Daily</SectionHead>
        <div style={{ display: "flex", flexDirection: "column", gap: 8 }}>
          {items.map((n, i) => (
            <ChecklistRow key={n} label={n} checked={!!checked[n]}
              onToggle={() => toggle(n)} onDelete={() => remove(n)}
              onMove={d => setItems(moveItem(items, i, d))}
              editing={editing} theme={theme}/>
          ))}
          {editing && <AddRow placeholder="Add opening task…" onAdd={add} theme={theme}/>}
        </div>
        {(isThursday || editing) && <>
          <SectionHead theme={theme} sub="Thursday only" count={`${thuItems.filter(n => checked[n]).length}/${thuItems.length}`}>Thursday</SectionHead>
          <div style={{ display: "flex", flexDirection: "column", gap: 8 }}>
            {thuItems.map((n, i) => (
              <ChecklistRow key={n} label={n} checked={!!checked[n]}
                onToggle={() => toggle(n)} onDelete={() => removeThu(n)}
                onMove={d => setThuItems(moveItem(thuItems, i, d))}
                editing={editing} theme={theme}/>
            ))}
            {editing && <AddRow placeholder="Add Thursday task…" onAdd={addThu} theme={theme}/>}
          </div>
        </>}
        <div style={{ height: 16 }}/>
        <button onClick={() => window.confirm("Clear all checks? This cannot be undone.") && setChecked({})} style={ghostBtn(theme)}>Clear all checks</button>
      </div>
    </div>
  );
}

function FOHCleaningTab({ theme, goHome }) {
  const [items, setItems] = useShared("foh.cleaning.v1", DEFAULT_FOH_CLEANING);
  const [checked, setChecked] = useShared("foh.cleaning.checked.v1", {});
  const [editing, setEditing] = useState(false);

  const checkedCount = items.filter(n => checked[n]).length;
  const pct = items.length ? Math.round(checkedCount / items.length * 100) : 0;
  const toggle = (name) => setChecked({ ...checked, [name]: !checked[name] });
  const add = (name) => setItems([...items, name]);
  const remove = (name) => { setItems(items.filter(x => x !== name)); const nc = { ...checked }; delete nc[name]; setChecked(nc); };

  return (
    <div style={{ display: "flex", flexDirection: "column", flex: 1, minHeight: 0 }}>
      <PageHeader title="Deep Clean" onBack={goHome} editing={editing} setEditing={setEditing} theme={theme}/>
      <div style={{ padding: "18px 20px 28px", overflowY: "auto", flex: 1 }}>
        <Progress percent={pct} theme={theme}/>
        <div style={{ marginTop: 18, display: "flex", flexDirection: "column", gap: 8 }}>
          {items.map((n, i) => (
            <ChecklistRow key={n} label={n} checked={!!checked[n]}
              onToggle={() => toggle(n)} onDelete={() => remove(n)}
              onMove={d => setItems(moveItem(items, i, d))}
              editing={editing} theme={theme}/>
          ))}
          {editing && <AddRow placeholder="Add deep clean task…" onAdd={add} theme={theme}/>}
        </div>
        <div style={{ height: 16 }}/>
        <button onClick={() => window.confirm("Clear all checks? This cannot be undone.") && setChecked({})} style={ghostBtn(theme)}>Clear all checks</button>
      </div>
    </div>
  );
}

function FOHClosingTab({ theme, goHome }) {
  const [items, setItems] = useShared("foh.closing.v1", DEFAULT_FOH_CLOSING);
  const [thuItems, setThuItems] = useShared("foh.closing.thu.v1", DEFAULT_FOH_CLOSING_THURSDAY);
  const [endItems, setEndItems] = useShared("foh.endofnight.v1", DEFAULT_FOH_END_OF_NIGHT);
  const [checked, setChecked] = useShared("foh.closing.checked.v1", {});
  const [editing, setEditing] = useState(false);

  const isThursday = new Date().getDay() === 4;
  const allItems = isThursday ? [...items, ...thuItems, ...endItems] : [...items, ...endItems];
  const checkedCount = allItems.filter(n => checked[n]).length;
  const pct = allItems.length ? Math.round(checkedCount / allItems.length * 100) : 0;

  const toggle = (name) => setChecked({ ...checked, [name]: !checked[name] });
  const add = (name) => setItems([...items, name]);
  const remove = (name) => { setItems(items.filter(x => x !== name)); const nc = { ...checked }; delete nc[name]; setChecked(nc); };
  const addThu = (name) => setThuItems([...thuItems, name]);
  const removeThu = (name) => { setThuItems(thuItems.filter(x => x !== name)); const nc = { ...checked }; delete nc[name]; setChecked(nc); };
  const addEnd = (name) => setEndItems([...endItems, name]);
  const removeEnd = (name) => { setEndItems(endItems.filter(x => x !== name)); const nc = { ...checked }; delete nc[name]; setChecked(nc); };

  return (
    <div style={{ display: "flex", flexDirection: "column", flex: 1, minHeight: 0 }}>
      <PageHeader title="Closing" onBack={goHome} editing={editing} setEditing={setEditing} theme={theme}/>
      <div style={{ padding: "18px 20px 28px", overflowY: "auto", flex: 1 }}>
        <Progress percent={pct} theme={theme}/>
        <SectionHead theme={theme} sub="Every night" count={`${items.filter(n => checked[n]).length}/${items.length}`}>Closing Duties</SectionHead>
        <div style={{ display: "flex", flexDirection: "column", gap: 8 }}>
          {items.map((n, i) => (
            <ChecklistRow key={n} label={n} checked={!!checked[n]}
              onToggle={() => toggle(n)} onDelete={() => remove(n)}
              onMove={d => setItems(moveItem(items, i, d))}
              editing={editing} theme={theme}/>
          ))}
          {editing && <AddRow placeholder="Add closing task…" onAdd={add} theme={theme}/>}
        </div>
        {(isThursday || editing) && <>
          <SectionHead theme={theme} sub="Thursday only" count={`${thuItems.filter(n => checked[n]).length}/${thuItems.length}`}>Thursday</SectionHead>
          <div style={{ display: "flex", flexDirection: "column", gap: 8 }}>
            {thuItems.map((n, i) => (
              <ChecklistRow key={n} label={n} checked={!!checked[n]}
                onToggle={() => toggle(n)} onDelete={() => removeThu(n)}
                onMove={d => setThuItems(moveItem(thuItems, i, d))}
                editing={editing} theme={theme}/>
            ))}
            {editing && <AddRow placeholder="Add Thursday task…" onAdd={addThu} theme={theme}/>}
          </div>
        </>}
        <SectionHead theme={theme} sub="Final lockup" count={`${endItems.filter(n => checked[n]).length}/${endItems.length}`}>End of Night</SectionHead>
        <div style={{ display: "flex", flexDirection: "column", gap: 8 }}>
          {endItems.map((n, i) => (
            <ChecklistRow key={n} label={n} checked={!!checked[n]}
              onToggle={() => toggle(n)} onDelete={() => removeEnd(n)}
              onMove={d => setEndItems(moveItem(endItems, i, d))}
              editing={editing} theme={theme}/>
          ))}
          {editing && <AddRow placeholder="Add end of night task…" onAdd={addEnd} theme={theme}/>}
        </div>
        <div style={{ height: 16 }}/>
        <button onClick={() => window.confirm("Clear all checks? This cannot be undone.") && setChecked({})} style={ghostBtn(theme)}>Clear all checks</button>
      </div>
    </div>
  );
}

function FOHHomeTab({ theme, go, stats }) {
  const accents = theme.tabAccents || {};
  const role = localStorage.getItem("fs_role_v1") || "foh";
  const isManager = role === "manager";
  const allTiles = [
    { id: "opening",  label: "Opening\nChecklist",  icon: Icon.list(26),  meta: `${stats.openPct}% Completed`,  colour: accents.opening  || theme.accent },
    { id: "cleaning", label: "Deep\nClean",          icon: Icon.broom(26), meta: `${stats.cleanPct}% Completed`, colour: accents.checklist || theme.accent },
    { id: "closing",  label: "Closing\nChecklist",   icon: Icon.list(26),  meta: `${stats.closePct}% Completed`, colour: accents.dough    || theme.accent },
    { id: "stock",    label: "Bar\nStock",            icon: Icon.box(26),   meta: "6 suppliers",                   colour: accents.stock    || theme.accent, managerOnly: true }
  ];
  const tiles = isManager ? allTiles : allTiles.filter(t => !t.managerOnly);
  const subtitle = isManager ? "Manager" : "Front of House";
  return (
    <div style={{ display: "flex", flexDirection: "column", flex: 1 }}>
      <BrandHero theme={theme} subtitle={subtitle}/>
      <div style={{ padding: "0 22px 28px", display: "flex", flexDirection: "column", flex: 1 }}>
        <div style={{
          textAlign: "center", fontSize: 10, fontWeight: 700,
          color: theme.accent, letterSpacing: "0.42em",
          textTransform: "uppercase", marginBottom: 16
        }}>— Today's Stations —</div>
        <div style={{ display: "flex", flexDirection: "column", gap: 10 }}>
          {tiles.map(t => {
            const hex = t.colour.replace('#','');
            const r = parseInt(hex.substring(0,2), 16);
            const g = parseInt(hex.substring(2,4), 16);
            const b = parseInt(hex.substring(4,6), 16);
            const soft = `rgba(${r},${g},${b},0.14)`;
            const bdr = `rgba(${r},${g},${b},0.35)`;
            return (
              <button key={t.id} onClick={() => go(t.id)} style={{
                position: "relative", display: "flex", alignItems: "center", gap: 14,
                padding: "16px 16px", borderRadius: 14,
                background: theme.tile, color: theme.text,
                border: `1px solid ${theme.tileBorder}`,
                textAlign: "left", cursor: "pointer", fontFamily: "inherit",
                transition: "transform .15s ease", overflow: "hidden"
              }}
              onMouseDown={e => e.currentTarget.style.transform = "scale(0.995)"}
              onMouseUp={e => e.currentTarget.style.transform = "scale(1)"}
              onMouseLeave={e => e.currentTarget.style.transform = "scale(1)"}
              >
                <div style={{ position: "absolute", left: 0, top: 10, bottom: 10, width: 4, borderRadius: 999, background: t.colour }}/>
                <div style={{
                  width: 48, height: 48, borderRadius: 12,
                  background: soft, color: t.colour, border: `1px solid ${bdr}`,
                  display: "flex", alignItems: "center", justifyContent: "center", flexShrink: 0
                }}>{t.icon}</div>
                <div style={{ flex: 1 }}>
                  <div style={{
                    fontFamily: "'Playfair Display', serif", fontWeight: 700,
                    fontSize: 19, lineHeight: 1.1, color: theme.heading, whiteSpace: "pre-line"
                  }}>{t.label}</div>
                  <div style={{ fontSize: 12, color: theme.muted, marginTop: 5 }}>{t.meta}</div>
                </div>
                <div style={{ color: t.colour, opacity: 0.8, transform: "rotate(180deg)" }}>{Icon.back(18)}</div>
              </button>
            );
          })}
        </div>
        <div style={{ flex: 1, minHeight: 24 }}/>
        <div style={{ maxWidth: 180, margin: "24px auto 10px" }}><BrandRule theme={theme}/></div>
        <div style={{
          textAlign: "center", fontSize: 9, color: theme.muted,
          letterSpacing: "0.42em", textTransform: "uppercase", opacity: 0.7, fontWeight: 600
        }}>Front of House</div>
        <button onClick={() => window.fsLogout && window.fsLogout()} style={{
          display: "flex", alignItems: "center", justifyContent: "center", gap: 6,
          margin: "20px auto 0", padding: "10px 20px", borderRadius: 10,
          background: "transparent", border: `1px solid ${theme.rowBorder}`,
          color: theme.muted, fontSize: 12, fontWeight: 600,
          cursor: "pointer", fontFamily: "inherit", letterSpacing: "0.06em"
        }}>{Icon.logout(14)} Sign out</button>
      </div>
    </div>
  );
}

function FOHStockTab({ theme, goHome }) {
  const [k4,  setK4,  k4Sync]  = useSharedStock("kater4",      DEFAULT_KATER4);
  const [kw,  setKw,  kwSync]  = useSharedStock("kilowines",   DEFAULT_KILOWINES);
  const [mj,  setMj,  mjSync]  = useSharedStock("majestic",    DEFAULT_MAJESTIC);
  const [rw,  setRw,  rwSync]  = useSharedStock("rothley",     DEFAULT_ROTHLEY);
  const [rc,  setRc,  rcSync]  = useSharedStock("roundcorner", DEFAULT_ROUNDCORNER);
  const [az,  setAz,  azSync]  = useSharedStock("amazon",      DEFAULT_AMAZON);
  const [editing, setEditing] = useState(false);
  const [supplier, setSupplier] = useState("kater4");

  const suppliers = {
    kater4:      { data: k4, set: setK4, sync: k4Sync, label: "Kater 4",       sub: "main order",     prefix: "k4", email: "Sales@kater4.co.uk" },
    kilowines:   { data: kw, set: setKw, sync: kwSync, label: "Kilo Wines",    sub: "multiples of 6", prefix: "kw", email: "Sales@kilowines.co.uk" },
    majestic:    { data: mj, set: setMj, sync: mjSync, label: "Majestic",      sub: "wines",          prefix: "mj" },
    rothley:     { data: rw, set: setRw, sync: rwSync, label: "Rothley",       sub: "wines",          prefix: "rw" },
    roundcorner: { data: rc, set: setRc, sync: rcSync, label: "Round Corner",  sub: "brewing",        prefix: "rc" },
    amazon:      { data: az, set: setAz, sync: azSync, label: "Amazon",        sub: "supplies",       prefix: "az" }
  };
  const sup = suppliers[supplier] || suppliers.kater4;
  const current = sup.data;
  const setCurrent = sup.set;
  const hasCode = false;
  const supplierName = sup.label;

  const update = (id, patch) => setCurrent(current.map(s => s.id === id ? { ...s, ...patch } : s));
  const remove = (id) => setCurrent(current.filter(s => s.id !== id));
  const add = (name) => {
    const base = { id: `${sup.prefix}_${Date.now()}`, name, inStock: "0", mustHave: "1" };
    setCurrent([...current, base]);
  };

  const needsOrder = (i) => {
    if (i.excludeFromEmail) return false;
    const sh = stockShortfall(i);
    if (sh && sh !== "0") return true;
    const stock = parseQty(i.inStock);
    if (stock.num <= 0 && i.name) return true;
    return false;
  };

  const needCount = current.filter(needsOrder).length;
  const excludedCount = current.filter(i => i.excludeFromEmail).length;

  const buildOrderLines = () => {
    const needed = current.filter(needsOrder);
    const orderDate = nextThursday();
    const lines = [];
    lines.push(`THE FIRE STATION PIZZERIA`);
    lines.push(`${supplierName} Order For Thursday (${orderDate})`);
    lines.push(``);
    if (needed.length === 0) {
      lines.push(`No items to order.`);
    } else {
      needed.forEach(it => {
        const sh = stockShortfall(it);
        const qty = (sh && sh !== "0" && sh !== "") ? sh : "—";
        lines.push(`• ${it.name}  —  ${qty}`);
      });
    }
    if (excludedCount > 0) {
      lines.push(``);
      lines.push(`(${excludedCount} item${excludedCount === 1 ? "" : "s"} skipped at staff request)`);
    }
    return { lines, orderDate };
  };

  const emailOrder = () => {
    const { lines, orderDate } = buildOrderLines();
    window.location.href = buildMailto(`${supplierName} Order — Thursday ${orderDate}`, lines, sup.email);
  };

  const [copied, setCopied] = useState(false);
  const copyOrder = async () => {
    const { lines } = buildOrderLines();
    try {
      await navigator.clipboard.writeText(lines.join("\n"));
      setCopied(true);
      setTimeout(() => setCopied(false), 2000);
    } catch { alert("Couldn't copy — try the email button instead."); }
  };

  return (
    <div style={{ display: "flex", flexDirection: "column", flex: 1, minHeight: 0 }}>
      <PageHeader title="Bar Stock" onBack={goHome} editing={editing} setEditing={setEditing} theme={theme}/>
      <SyncIndicator theme={theme} syncState={sup.sync.syncState}/>

      {/* Supplier tabs — 3x2 grid */}
      <div style={{
        display: "grid", gridTemplateColumns: "1fr 1fr 1fr", gap: 5,
        padding: "10px 16px 4px", background: theme.headerBg
      }}>
        {Object.entries(suppliers).map(([id, s]) => {
          const active = supplier === id;
          return (
            <button key={id} onClick={() => setSupplier(id)} style={{
              padding: "7px 4px", borderRadius: 9,
              border: `1px solid ${active ? theme.accent : theme.rowBorder}`,
              background: active ? theme.accentSoft : "transparent",
              color: active ? theme.accent : theme.muted,
              fontFamily: "inherit", cursor: "pointer", textAlign: "center"
            }}>
              <div style={{
                fontFamily: "'Playfair Display', serif", fontWeight: 700, fontSize: 12
              }}>{s.label}</div>
              <div style={{ fontSize: 8, letterSpacing: "0.12em", textTransform: "uppercase", marginTop: 1, opacity: 0.8 }}>{s.sub}</div>
            </button>
          );
        })}
      </div>

      <div style={{ padding: "10px 20px 28px", overflowY: "auto", flex: 1 }}>
        <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 8, marginTop: 14, marginBottom: 14 }}>
          <StatCard theme={theme} label="Items" value={current.length}/>
          <StatCard theme={theme} label="To Order" value={needCount} accent={needCount > 0}/>
        </div>

        <div style={{
          display: "grid", gridTemplateColumns: "1fr 60px 60px 54px",
          gap: 6, padding: "0 10px",
          fontSize: 10, fontWeight: 700, color: theme.muted,
          textTransform: "uppercase", letterSpacing: "0.12em", marginBottom: 6
        }}>
          <span>Item</span>
          <span style={{ textAlign: "center" }}>Have</span>
          <span style={{ textAlign: "center" }}>Must</span>
          <span style={{ textAlign: "center", color: theme.accent }}>Order</span>
        </div>

        <div style={{ display: "flex", flexDirection: "column", gap: 6 }}>
          {current.map(item => (
            <StockRow key={item.id} item={item} theme={theme}
              editing={editing} hasCode={false}
              onUpdate={patch => update(item.id, patch)}
              onRemove={() => remove(item.id)}
              onToggleExclude={() => update(item.id, { excludeFromEmail: !item.excludeFromEmail })}/>
          ))}
          {editing && <AddRow placeholder={`Add ${supplierName} item…`} onAdd={add} theme={theme}/>}
        </div>

        <div style={{ height: 18 }}/>
        <button onClick={emailOrder} style={primaryBtn(theme)}>
          {Icon.mail(18)} Email {supplierName} Order
        </button>
        <button onClick={copyOrder} style={{ ...ghostBtn(theme), marginTop: 8 }}>
          {Icon.clipboard(16)} {copied ? "Copied!" : "Copy order to clipboard"}
        </button>
      </div>
    </div>
  );
}

function FOHApp({ theme, storageNs = "foh" }) {
  const [tab, setTab] = useLocal(`foh.tab.${storageNs}`, "home");

  const openItems = LS.get("foh.opening.v1", DEFAULT_FOH_OPENING);
  const openChecked = LS.get("foh.opening.checked.v1", {});
  const openPct = openItems.length ? Math.round(openItems.filter(n => openChecked[n]).length / openItems.length * 100) : 0;

  const cleanItems = LS.get("foh.cleaning.v1", DEFAULT_FOH_CLEANING);
  const cleanChecked = LS.get("foh.cleaning.checked.v1", {});
  const cleanPct = cleanItems.length ? Math.round(cleanItems.filter(n => cleanChecked[n]).length / cleanItems.length * 100) : 0;

  const closeItems = LS.get("foh.closing.v1", DEFAULT_FOH_CLOSING);
  const endItems = LS.get("foh.endofnight.v1", DEFAULT_FOH_END_OF_NIGHT);
  const closeChecked = LS.get("foh.closing.checked.v1", {});
  const allClose = [...closeItems, ...endItems];
  const closePct = allClose.length ? Math.round(allClose.filter(n => closeChecked[n]).length / allClose.length * 100) : 0;

  const stats = { openPct, cleanPct, closePct };
  const goHome = () => setTab("home");
  const go = (t) => setTab(t);
  const role = localStorage.getItem("fs_role_v1") || "foh";
  const isManager = role === "manager";

  return (
    <div data-screen-label={`FOH · ${tab}`} style={{
      height: "100%", width: "100%", background: theme.bg, color: theme.text,
      fontFamily: "'Inter', system-ui, sans-serif",
      display: "flex", flexDirection: "column", overflow: "hidden"
    }}>
      {tab === "home" && <FOHHomeTab theme={theme} go={go} stats={stats}/>}
      {tab === "opening" && <FOHOpeningTab theme={theme} goHome={goHome}/>}
      {tab === "cleaning" && <FOHCleaningTab theme={theme} goHome={goHome}/>}
      {tab === "closing" && <FOHClosingTab theme={theme} goHome={goHome}/>}
      {isManager && tab === "stock" && <FOHStockTab theme={theme} goHome={goHome}/>}
    </div>
  );
}

Object.assign(window, { App, FOHApp, Logo });
