const { useState, useMemo, useEffect, useCallback, useRef } = React;

// ── API helper ────────────────────────────────────────────────────────────────

const API = "/api";

async function apiFetch(path, options = {}) {
  const res = await fetch(API + path, {
    ...options,
    headers: { "Content-Type": "application/json", ...(options.headers || {}) },
    credentials: "same-origin",
  });
  let data = null;
  try { data = await res.json(); } catch (e) {}
  if (!res.ok) {
    const err = new Error((data && data.error) || `Request failed (${res.status})`);
    err.status = res.status;
    throw err;
  }
  return data;
}

// ── Constants (mirrors README design tokens / handoff prototype) ───────────────

const STAGES = [
  { id: 1, key: "advance_confirmed",   label: "Advance Confirmed",          icon: "💰", description: "Advance payment received, project confirmed" },
  { id: 2, key: "site_measurement",    label: "Final Site Measurement",     icon: "📐", description: "Final on-site measurements taken" },
  { id: 3, key: "order_materials",     label: "Order Special Materials",    icon: "📦", description: "Place orders for any special/custom materials" },
  { id: 4, key: "final_signoff",       label: "Final Sign-off",             icon: "✅", description: "Customer approves final design after measurement" },
  { id: 5, key: "production_check",    label: "Production Timeline Check", icon: "🏭", description: "Confirm schedule with production team" },
  { id: 6, key: "installation_track",  label: "Track Installation Timeline",icon: "📅", description: "Monitor and coordinate install schedule" },
  { id: 7, key: "pre_line_work",       label: "Pre-line Work Check",        icon: "🔌", description: "Confirm electrical, plumbing, gas prep with customer" },
  { id: 8, key: "installing",          label: "Installation Complete",      icon: "🔨", description: "Kitchen installation finished" },
  { id: 9, key: "stone_template",      label: "Stone Template",             icon: "🪨", description: "Send stone guy for countertop template" },
  { id: 10, key: "countertop_followup",label: "Countertop Follow-up",       icon: "📞", description: "Follow up with customer on countertop installation" },
  { id: 11, key: "finishing",          label: "Finishing Day",              icon: "🏠", description: "Arrange installation team for kitchen finishing" },
  { id: 12, key: "final_handover",     label: "Final Walkthrough & Handover",icon: "🤝", description: "Customer sign-off that everything is complete" },
];

const STATUS = {
  pending:     { label: "Pending",     color: "#94a3b8", bg: "#f1f5f9" },
  in_progress: { label: "In Progress", color: "#d97706", bg: "#fffbeb" },
  done:        { label: "Done",        color: "#16a34a", bg: "#f0fdf4" },
  skipped:     { label: "Skipped",     color: "#9333ea", bg: "#faf5ff" },
};

const PRIORITY = {
  none:   { label: "No Priority", color: "#94a3b8", bg: "#f8fafc", dot: "#cbd5e1" },
  low:    { label: "Low",         color: "#16a34a", bg: "#f0fdf4", dot: "#4ade80" },
  medium: { label: "Medium",      color: "#d97706", bg: "#fffbeb", dot: "#fbbf24" },
  high:   { label: "High",        color: "#dc2626", bg: "#fef2f2", dot: "#f87171" },
  urgent: { label: "Urgent",      color: "#ffffff", bg: "#dc2626", dot: "#dc2626" },
};

const PAYMENT_STATUS = {
  advance_paid:    { label: "Advance Paid",     color: "#0369a1", bg: "#e0f2fe" },
  balance_pending: { label: "Balance Pending",  color: "#d97706", bg: "#fffbeb" },
  fully_paid:      { label: "Fully Paid",       color: "#16a34a", bg: "#f0fdf4" },
};

const TIME_SLOTS = [
  "7:00 AM","7:30 AM","8:00 AM","8:30 AM","9:00 AM","9:30 AM",
  "10:00 AM","10:30 AM","11:00 AM","11:30 AM","12:00 PM","12:30 PM",
  "1:00 PM","1:30 PM","2:00 PM","2:30 PM","3:00 PM","3:30 PM",
  "4:00 PM","4:30 PM","5:00 PM","5:30 PM","6:00 PM",
];

const EMPTY_SD = { status: "pending", note: "", date: "", time: "" };

// ── Helpers ───────────────────────────────────────────────────────────────────

function today() { return new Date().toISOString().slice(0, 10); }
function addDays(dateStr, n) {
  const d = new Date(dateStr + "T12:00:00"); d.setDate(d.getDate() + n);
  return d.toISOString().slice(0, 10);
}
function fmtDate(dateStr) {
  if (!dateStr) return "";
  const d = new Date(dateStr + "T12:00:00");
  return d.toLocaleDateString("en-US", { weekday: "short", month: "short", day: "numeric" });
}
function fmtDateLong(dateStr) {
  if (!dateStr) return "";
  const d = new Date(dateStr + "T12:00:00");
  return d.toLocaleDateString("en-US", { weekday: "long", month: "long", day: "numeric", year: "numeric" });
}
function isOverdue(dateStr, status) {
  if (!dateStr || status === "done" || status === "skipped") return false;
  return dateStr < today();
}
function isDueToday(dateStr, status) {
  if (!dateStr || status === "done" || status === "skipped") return false;
  return dateStr === today();
}
function isDueSoon(dateStr, status) {
  if (!dateStr || status === "done" || status === "skipped") return false;
  const t = today(); return dateStr > t && dateStr <= addDays(t, 3);
}
function fmtMoney(n) {
  const num = parseFloat(n);
  if (isNaN(num)) return "";
  return num.toLocaleString("en-US");
}
// Monday-based week start, locale-independent (does not rely on Date.getDay() quirks around Sunday=0)
function mondayOf(dateStr) {
  const d = new Date(dateStr + "T12:00:00");
  const dow = d.getDay(); // 0=Sun..6=Sat
  const diff = dow === 0 ? -6 : 1 - dow;
  d.setDate(d.getDate() + diff);
  return d.toISOString().slice(0, 10);
}

const PRIORITY_ORDER = ["none", "low", "medium", "high", "urgent"];
function nextPriority(p) {
  const i = PRIORITY_ORDER.indexOf(p);
  return PRIORITY_ORDER[(i + 1) % PRIORITY_ORDER.length];
}

function debounce(fn, ms) {
  let t;
  return (...args) => {
    clearTimeout(t);
    t = setTimeout(() => fn(...args), ms);
  };
}

// ── Small UI atoms ────────────────────────────────────────────────────────────

function Badge({ label, color, bg, border }) {
  return (
    <span style={{ fontSize: 10, fontWeight: 700, padding: "2px 7px", borderRadius: 20,
      color, background: bg, border: `1px solid ${border || color + "40"}`, whiteSpace: "nowrap" }}>
      {label}
    </span>
  );
}
function Label({ children }) {
  return <div style={{ fontSize: 10, fontWeight: 700, color: "#374151", textTransform: "uppercase", letterSpacing: 0.8, marginBottom: 5 }}>{children}</div>;
}
function Spinner({ size = 16, color = "#94a3b8" }) {
  return (
    <span style={{
      display: "inline-block", width: size, height: size, borderRadius: "50%",
      border: `2px solid ${color}30`, borderTopColor: color,
      animation: "kpt-spin 0.7s linear infinite",
    }} />
  );
}

// Inject keyframes once
if (!document.getElementById("kpt-keyframes")) {
  const style = document.createElement("style");
  style.id = "kpt-keyframes";
  style.textContent = `@keyframes kpt-spin { to { transform: rotate(360deg); } }
  @keyframes kpt-fade-in { from { opacity: 0; } to { opacity: 1; } }`;
  document.head.appendChild(style);
}

function Toast({ toasts, onDismiss }) {
  if (toasts.length === 0) return null;
  return (
    <div style={{ position: "fixed", bottom: 18, right: 18, zIndex: 1000, display: "flex", flexDirection: "column", gap: 8, maxWidth: 320 }}>
      {toasts.map(t => (
        <div key={t.id} style={{
          background: t.type === "error" ? "#fef2f2" : "#1e293b", color: t.type === "error" ? "#b91c1c" : "#fff",
          border: t.type === "error" ? "1.5px solid #fca5a5" : "none",
          borderRadius: 8, padding: "10px 14px", fontSize: 12.5, fontWeight: 600, boxShadow: "0 4px 14px rgba(0,0,0,0.15)",
          display: "flex", alignItems: "flex-start", gap: 8, animation: "kpt-fade-in 0.15s ease",
        }}>
          <span style={{ flex: 1 }}>{t.message}</span>
          {t.retry && (
            <button onClick={t.retry} style={{ background: "none", border: "1px solid currentColor", color: "inherit", borderRadius: 5, padding: "2px 8px", fontSize: 11, cursor: "pointer" }}>Retry</button>
          )}
          <button onClick={() => onDismiss(t.id)} style={{ background: "none", border: "none", color: "inherit", cursor: "pointer", fontSize: 14, opacity: 0.7, padding: 0 }}>×</button>
        </div>
      ))}
    </div>
  );
}

// ── Auth screens ──────────────────────────────────────────────────────────────

function AuthScreen({ onAuthed }) {
  const [mode, setMode] = useState("signin"); // signin | signup
  const [email, setEmail] = useState("");
  const [password, setPassword] = useState("");
  const [name, setName] = useState("");
  const [error, setError] = useState("");
  const [loading, setLoading] = useState(false);

  async function submit(e) {
    e.preventDefault();
    setError("");
    setLoading(true);
    try {
      const path = mode === "signin" ? "/auth/login" : "/auth/signup";
      const body = mode === "signin" ? { email, password } : { email, password, name };
      const user = await apiFetch(path, { method: "POST", body: JSON.stringify(body) });
      onAuthed(user);
    } catch (err) {
      setError(err.message);
    } finally {
      setLoading(false);
    }
  }

  return (
    <div style={{ minHeight: "100vh", background: "#f8fafc", display: "flex", alignItems: "center", justifyContent: "center", padding: 20 }}>
      <div style={{ width: 400, maxWidth: "100%", background: "#fff", border: "1.5px solid #e2e8f0", borderRadius: 14, padding: "28px 26px", boxShadow: "0 4px 20px rgba(0,0,0,0.04)" }}>
        <div style={{ textAlign: "center", marginBottom: 20 }}>
          <div style={{ fontSize: 28, marginBottom: 4 }}>🏠</div>
          <div style={{ fontSize: 19, fontWeight: 800, color: "#1e293b" }}>Kitchen Project Tracker</div>
          <div style={{ fontSize: 12.5, color: "#64748b", marginTop: 3 }}>
            {mode === "signin" ? "Sign in to your workspace" : "Create your account"}
          </div>
        </div>

        <button type="button" disabled style={{
          width: "100%", display: "flex", alignItems: "center", justifyContent: "center", gap: 8,
          border: "1px solid #e2e8f0", background: "#f8fafc", color: "#94a3b8", borderRadius: 8,
          padding: "9px 12px", fontSize: 13, fontWeight: 600, cursor: "not-allowed", marginBottom: 14,
        }} title="Google sign-in is not configured for this deployment yet">
          <span style={{ fontSize: 15 }}>G</span> Sign in with Google
        </button>
        <div style={{ display: "flex", alignItems: "center", gap: 10, marginBottom: 14 }}>
          <div style={{ flex: 1, height: 1, background: "#e2e8f0" }} />
          <span style={{ fontSize: 11, color: "#94a3b8" }}>or use email</span>
          <div style={{ flex: 1, height: 1, background: "#e2e8f0" }} />
        </div>

        <form onSubmit={submit}>
          {mode === "signup" && (
            <div style={{ marginBottom: 11 }}>
              <div style={{ fontSize: 10, color: "#94a3b8", marginBottom: 3, fontWeight: 700, textTransform: "uppercase", letterSpacing: 0.5 }}>Your Name</div>
              <input value={name} onChange={e => setName(e.target.value)} placeholder="Jane Smith"
                style={inputStyle} />
            </div>
          )}
          <div style={{ marginBottom: 11 }}>
            <div style={{ fontSize: 10, color: "#94a3b8", marginBottom: 3, fontWeight: 700, textTransform: "uppercase", letterSpacing: 0.5 }}>Email</div>
            <input type="email" required value={email} onChange={e => setEmail(e.target.value)} placeholder="you@example.com"
              style={inputStyle} />
          </div>
          <div style={{ marginBottom: 6 }}>
            <div style={{ fontSize: 10, color: "#94a3b8", marginBottom: 3, fontWeight: 700, textTransform: "uppercase", letterSpacing: 0.5 }}>Password</div>
            <input type="password" required minLength={8} value={password} onChange={e => setPassword(e.target.value)} placeholder="At least 8 characters"
              style={inputStyle} />
          </div>

          {mode === "signin" && (
            <div style={{ textAlign: "right", marginBottom: 14 }}>
              <span title="Password reset requires an email provider to be configured; please contact your workspace owner for now."
                style={{ fontSize: 11.5, color: "#2563eb", cursor: "help", textDecoration: "underline dotted" }}>
                Forgot password?
              </span>
            </div>
          )}

          {error && (
            <div style={{ background: "#fef2f2", border: "1px solid #fca5a5", color: "#b91c1c", borderRadius: 7, padding: "8px 10px", fontSize: 12, marginBottom: 12, marginTop: mode === "signup" ? 8 : 0 }}>
              {error}
            </div>
          )}

          <button type="submit" disabled={loading} style={{
            width: "100%", background: "#2563eb", color: "#fff", border: "none", borderRadius: 8,
            padding: "10px", fontSize: 13.5, fontWeight: 700, cursor: loading ? "default" : "pointer",
            display: "flex", alignItems: "center", justifyContent: "center", gap: 8, opacity: loading ? 0.7 : 1, marginTop: 4,
          }}>
            {loading && <Spinner size={14} color="#fff" />}
            {mode === "signin" ? "Sign In" : "Create Account"}
          </button>
        </form>

        <div style={{ textAlign: "center", marginTop: 16, fontSize: 12.5, color: "#64748b" }}>
          {mode === "signin" ? "Don't have an account? " : "Already have an account? "}
          <button onClick={() => { setMode(mode === "signin" ? "signup" : "signin"); setError(""); }}
            style={{ background: "none", border: "none", color: "#2563eb", fontWeight: 700, cursor: "pointer", fontSize: 12.5, padding: 0 }}>
            {mode === "signin" ? "Sign up" : "Sign in"}
          </button>
        </div>
      </div>
    </div>
  );
}

const inputStyle = { width: "100%", border: "1px solid #e2e8f0", borderRadius: 7, padding: "8px 10px", fontSize: 13, boxSizing: "border-box" };

// ── Workspace picker + settings ───────────────────────────────────────────────

function WorkspacePicker({ workspaces, currentId, onSwitch }) {
  const [open, setOpen] = useState(false);
  const ref = useRef(null);
  useEffect(() => {
    function onDoc(e) { if (ref.current && !ref.current.contains(e.target)) setOpen(false); }
    document.addEventListener("mousedown", onDoc);
    return () => document.removeEventListener("mousedown", onDoc);
  }, []);
  if (workspaces.length <= 1) return null;
  const current = workspaces.find(w => w.id === currentId);
  return (
    <div ref={ref} style={{ position: "relative" }}>
      <button onClick={() => setOpen(o => !o)} style={{
        background: "#334155", color: "#e2e8f0", border: "1px solid #475569", borderRadius: 7,
        padding: "5px 10px", fontSize: 12, fontWeight: 600, cursor: "pointer", display: "flex", alignItems: "center", gap: 6,
      }}>
        🏢 {current ? current.name : "Workspace"} <span style={{ fontSize: 9 }}>▾</span>
      </button>
      {open && (
        <div style={{ position: "absolute", top: "calc(100% + 4px)", right: 0, background: "#fff", border: "1px solid #e2e8f0",
          borderRadius: 8, boxShadow: "0 6px 20px rgba(0,0,0,0.12)", minWidth: 190, zIndex: 50, overflow: "hidden" }}>
          {workspaces.map(w => (
            <button key={w.id} onClick={() => { onSwitch(w.id); setOpen(false); }} style={{
              display: "block", width: "100%", textAlign: "left", background: w.id === currentId ? "#eff6ff" : "#fff",
              color: w.id === currentId ? "#2563eb" : "#374151", border: "none", padding: "8px 12px", fontSize: 12.5,
              fontWeight: w.id === currentId ? 700 : 500, cursor: "pointer", borderBottom: "1px solid #f1f5f9",
            }}>
              {w.name} <span style={{ fontSize: 10, color: "#94a3b8", fontWeight: 400 }}>· {w.role}</span>
            </button>
          ))}
        </div>
      )}
    </div>
  );
}

function Modal({ title, onClose, children, width = 480 }) {
  return (
    <div style={{ position: "fixed", inset: 0, background: "rgba(15,23,42,0.45)", zIndex: 200, display: "flex", alignItems: "center", justifyContent: "center", padding: 16 }}
      onMouseDown={(e) => { if (e.target === e.currentTarget) onClose(); }}>
      <div style={{ background: "#fff", borderRadius: 12, width, maxWidth: "100%", maxHeight: "85vh", overflowY: "auto",
        boxShadow: "0 12px 40px rgba(0,0,0,0.2)", animation: "kpt-fade-in 0.15s ease" }}>
        <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", padding: "14px 18px", borderBottom: "1px solid #e2e8f0" }}>
          <div style={{ fontSize: 14.5, fontWeight: 800, color: "#1e293b" }}>{title}</div>
          <button onClick={onClose} style={{ background: "none", border: "none", fontSize: 18, color: "#94a3b8", cursor: "pointer", padding: 2 }}>×</button>
        </div>
        <div style={{ padding: "16px 18px" }}>{children}</div>
      </div>
    </div>
  );
}

function WorkspaceSettingsModal({ workspace, myRole, onClose, onRenamed, onToast }) {
  const [name, setName] = useState(workspace.name);
  const [members, setMembers] = useState([]);
  const [invites, setInvites] = useState([]);
  const [loading, setLoading] = useState(true);
  const [inviteEmail, setInviteEmail] = useState("");
  const [inviteRole, setInviteRole] = useState("member");
  const isOwner = myRole === "owner";

  const load = useCallback(async () => {
    setLoading(true);
    try {
      const data = await apiFetch(`/workspaces/${workspace.id}/members`);
      setMembers(data.members); setInvites(data.invites);
    } catch (err) { onToast(err.message, "error"); }
    finally { setLoading(false); }
  }, [workspace.id]);

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

  async function saveName() {
    if (!name.trim() || name === workspace.name) return;
    try {
      await apiFetch(`/workspaces/${workspace.id}`, { method: "PUT", body: JSON.stringify({ name }) });
      onRenamed(workspace.id, name);
      onToast("Workspace renamed");
    } catch (err) { onToast(err.message, "error"); }
  }

  async function sendInvite(e) {
    e.preventDefault();
    if (!inviteEmail.trim()) return;
    try {
      const res = await apiFetch(`/workspaces/${workspace.id}/invite`, {
        method: "POST", body: JSON.stringify({ email: inviteEmail.trim(), role: inviteRole }),
      });
      setInviteEmail("");
      onToast(res.joined ? "Member added" : "Invite sent — they'll join automatically when they sign up");
      load();
    } catch (err) { onToast(err.message, "error"); }
  }

  async function removeMember(userId) {
    try {
      await apiFetch(`/workspaces/${workspace.id}/members/${userId}`, { method: "DELETE" });
      onToast("Member removed");
      load();
    } catch (err) { onToast(err.message, "error"); }
  }

  async function cancelInvite(id) {
    try {
      await apiFetch(`/workspaces/${workspace.id}/invites/${id}`, { method: "DELETE" });
      load();
    } catch (err) { onToast(err.message, "error"); }
  }

  return (
    <Modal title="⚙️ Workspace Settings" onClose={onClose} width={520}>
      <Label>Workspace Name</Label>
      <div style={{ display: "flex", gap: 6, marginBottom: 20 }}>
        <input value={name} onChange={e => setName(e.target.value)} disabled={!isOwner}
          style={{ ...inputStyle, flex: 1, opacity: isOwner ? 1 : 0.6 }} />
        {isOwner && <button onClick={saveName} style={primaryBtnSm}>Save</button>}
      </div>

      <Label>Members</Label>
      {loading ? <div style={{ padding: 10 }}><Spinner /></div> : (
        <div style={{ marginBottom: 16 }}>
          {members.map(m => (
            <div key={m.id} style={{ display: "flex", alignItems: "center", justifyContent: "space-between", padding: "7px 0", borderBottom: "1px solid #f1f5f9" }}>
              <div>
                <div style={{ fontSize: 13, fontWeight: 600, color: "#1e293b" }}>{m.name || m.email}</div>
                <div style={{ fontSize: 11, color: "#94a3b8" }}>{m.email}</div>
              </div>
              <div style={{ display: "flex", alignItems: "center", gap: 8 }}>
                <Badge label={m.role} color={m.role === "owner" ? "#2563eb" : "#64748b"} bg={m.role === "owner" ? "#eff6ff" : "#f1f5f9"} />
                {isOwner && m.role !== "owner" && (
                  <button onClick={() => removeMember(m.id)} style={{ background: "none", border: "1px solid #fecaca", color: "#ef4444", borderRadius: 5, padding: "3px 8px", fontSize: 11, cursor: "pointer" }}>Remove</button>
                )}
              </div>
            </div>
          ))}
          {invites.map(inv => (
            <div key={inv.id} style={{ display: "flex", alignItems: "center", justifyContent: "space-between", padding: "7px 0", borderBottom: "1px solid #f1f5f9" }}>
              <div>
                <div style={{ fontSize: 13, fontWeight: 600, color: "#94a3b8" }}>{inv.email}</div>
                <div style={{ fontSize: 11, color: "#cbd5e1" }}>Pending invite</div>
              </div>
              <div style={{ display: "flex", alignItems: "center", gap: 8 }}>
                <Badge label={inv.role} color="#d97706" bg="#fffbeb" />
                {isOwner && <button onClick={() => cancelInvite(inv.id)} style={{ background: "none", border: "1px solid #fecaca", color: "#ef4444", borderRadius: 5, padding: "3px 8px", fontSize: 11, cursor: "pointer" }}>Cancel</button>}
              </div>
            </div>
          ))}
        </div>
      )}

      {isOwner && (
        <>
          <Label>Invite by Email</Label>
          <form onSubmit={sendInvite} style={{ display: "flex", gap: 6, flexWrap: "wrap" }}>
            <input type="email" placeholder="colleague@example.com" value={inviteEmail} onChange={e => setInviteEmail(e.target.value)}
              style={{ ...inputStyle, flex: "1 1 180px" }} />
            <select value={inviteRole} onChange={e => setInviteRole(e.target.value)} style={{ ...inputStyle, flex: "0 0 110px" }}>
              <option value="member">Member</option>
              <option value="viewer">Viewer</option>
            </select>
            <button type="submit" style={primaryBtnSm}>Invite</button>
          </form>
        </>
      )}
    </Modal>
  );
}

const primaryBtnSm = { background: "#2563eb", color: "#fff", border: "none", borderRadius: 7, padding: "8px 14px", fontSize: 12.5, fontWeight: 700, cursor: "pointer" };

// ── Project Priority ──────────────────────────────────────────────────────────

function ProjectPriority({ project, onUpdate, readOnly }) {
  return (
    <div style={{ background: "#fff", border: "1.5px solid #e2e8f0", borderRadius: 10, padding: "11px 15px", marginBottom: 16 }}>
      <Label>🚦 Project Priority</Label>
      <div style={{ display: "flex", gap: 5, flexWrap: "wrap" }}>
        {Object.entries(PRIORITY).map(([key, val]) => (
          <button key={key} disabled={readOnly} onClick={() => onUpdate({ priority: key })} style={{
            display: "flex", alignItems: "center", gap: 4, padding: "4px 10px", borderRadius: 20,
            fontSize: 12, fontWeight: 600, cursor: readOnly ? "default" : "pointer", border: `1.5px solid ${project.priority === key ? val.dot : "#e2e8f0"}`,
            background: project.priority === key ? val.bg : "#fff",
            color: project.priority === key ? (val.color === "#ffffff" ? "#dc2626" : val.color) : "#64748b",
            opacity: readOnly && project.priority !== key ? 0.6 : 1,
          }}>
            <span style={{ width: 8, height: 8, borderRadius: "50%", background: val.dot, display: "inline-block" }} />
            {val.label}
          </button>
        ))}
      </div>
    </div>
  );
}

// ── Stage Row ─────────────────────────────────────────────────────────────────

function StageRow({ stage, data, onChange, readOnly, saving }) {
  const [open, setOpen] = useState(false);
  const st = STATUS[data.status];
  const isDone = data.status === "done";
  const isSkipped = data.status === "skipped";
  const overdue = isOverdue(data.date, data.status);
  const dueToday = isDueToday(data.date, data.status);
  const dueSoon = isDueSoon(data.date, data.status);

  const leftBorder = overdue ? "#ef4444" : dueToday ? "#f59e0b" : isDone ? "#4ade80" : "#cbd5e1";
  const rowBg = overdue ? "#fff5f5" : dueToday ? "#fffbeb" : isDone ? "#f0fdf4" : isSkipped ? "#faf5ff" : "#fff";

  return (
    <div style={{
      border: `1.5px solid ${overdue ? "#fca5a5" : dueToday ? "#fde68a" : "#e2e8f0"}`,
      borderLeft: `4px solid ${leftBorder}`,
      borderRadius: 10, marginBottom: 8, background: rowBg, transition: "all 0.2s",
      opacity: readOnly ? 0.85 : 1,
    }}>
      <div style={{ display: "flex", alignItems: "center", gap: 9, padding: "9px 12px", flexWrap: "wrap" }}>
        <button
          disabled={readOnly}
          onClick={() => onChange({ ...data, status: isDone ? "pending" : "done" })}
          title={isDone ? "Mark as pending" : "Mark as done"}
          style={{
            width: 22, height: 22, minWidth: 22, borderRadius: "50%", cursor: readOnly ? "default" : "pointer",
            border: `2px solid ${isDone ? "#16a34a" : "#cbd5e1"}`,
            background: isDone ? "#16a34a" : "#fff",
            display: "flex", alignItems: "center", justifyContent: "center", padding: 0,
          }}>
          {isDone && <span style={{ color: "#fff", fontSize: 12, fontWeight: 900 }}>✓</span>}
        </button>

        <span style={{ fontSize: 17, minWidth: 22 }}>{stage.icon}</span>

        <div style={{ minWidth: 150, flex: "1 1 180px" }}>
          <div style={{ fontWeight: 600, fontSize: 13,
            color: isDone ? "#15803d" : isSkipped ? "#7e22ce" : overdue ? "#b91c1c" : "#1e293b",
            textDecoration: isSkipped ? "line-through" : "none" }}>
            {stage.label}
          </div>
          <div style={{ fontSize: 10.5, color: "#64748b" }}>{stage.description}</div>
        </div>

        <select disabled={readOnly} value={data.status} onChange={e => onChange({ ...data, status: e.target.value })}
          style={{ border: `1.5px solid ${st.color}`, color: st.color, background: st.bg, borderRadius: 6,
            padding: "4px 7px", fontSize: 11, fontWeight: 700, cursor: readOnly ? "default" : "pointer" }}>
          {Object.entries(STATUS).map(([k, v]) => <option key={k} value={k}>{v.label}</option>)}
        </select>

        <input type="date" disabled={readOnly} value={data.date} onChange={e => onChange({ ...data, date: e.target.value })}
          style={{ border: "1px solid #e2e8f0", borderRadius: 6, padding: "4px 7px", fontSize: 11.5, color: "#374151" }} />

        <select disabled={readOnly} value={data.time} onChange={e => onChange({ ...data, time: e.target.value })}
          style={{ border: "1px solid #e2e8f0", borderRadius: 6, padding: "4px 7px", fontSize: 11.5, color: data.time ? "#374151" : "#94a3b8" }}>
          <option value="">— Time —</option>
          {TIME_SLOTS.map(t => <option key={t} value={t}>{t}</option>)}
        </select>

        {!readOnly && (
          <div style={{ display: "flex", gap: 4 }}>
            <button onClick={() => onChange({ ...data, date: today() })}
              style={{ border: "1px solid #fde68a", background: "#fffbeb", color: "#92400e", borderRadius: 6, padding: "4px 8px", fontSize: 10.5, fontWeight: 600, cursor: "pointer" }}>
              Today
            </button>
            <button onClick={() => onChange({ ...data, date: addDays(data.date || today(), 1) })}
              style={{ border: "1px solid #e2e8f0", background: "#f8fafc", color: "#374151", borderRadius: 6, padding: "4px 8px", fontSize: 10.5, cursor: "pointer" }}>
              +1d
            </button>
            {data.date && <button onClick={() => onChange({ ...data, date: "", time: "" })}
              style={{ border: "1px solid #fecaca", background: "#fff5f5", color: "#ef4444", borderRadius: 6, padding: "4px 8px", fontSize: 10.5, cursor: "pointer" }}>
              Clear
            </button>}
          </div>
        )}

        {overdue && <Badge label="⚠ OVERDUE" color="#b91c1c" bg="#fee2e2" />}
        {dueToday && !overdue && <Badge label="📌 TODAY" color="#92400e" bg="#fef3c7" />}
        {dueSoon && <Badge label="⏰ Soon" color="#0369a1" bg="#e0f2fe" />}
        {saving && <Spinner size={12} />}

        <button onClick={() => setOpen(o => !o)}
          style={{ marginLeft: "auto", background: "none", border: "none", color: "#94a3b8", fontSize: 11, cursor: "pointer", padding: "3px 4px" }}>
          {open ? "▲ note" : "▼ note"}
        </button>
      </div>

      {open && (
        <div style={{ padding: "0 12px 11px" }}>
          <input type="text" placeholder="Add a note…" value={data.note} disabled={readOnly}
            onChange={e => onChange({ ...data, note: e.target.value })}
            style={{ width: "100%", border: "1px solid #e2e8f0", borderRadius: 6, padding: "6px 9px", fontSize: 12, boxSizing: "border-box" }} />
        </div>
      )}
    </div>
  );
}

// ── Payment Tracker ───────────────────────────────────────────────────────────

function PaymentTracker({ project, onUpdate, readOnly }) {
  const total = parseFloat(project.totalAmount) || 0;
  const advance = parseFloat(project.advanceAmount) || 0;
  const balance = total - advance;
  const ps = PAYMENT_STATUS[project.paymentStatus] || PAYMENT_STATUS.advance_paid;
  const pct = total > 0 ? Math.min(100, Math.round((advance / total) * 100)) : 0;

  return (
    <div style={{ background: "#fff", border: "1.5px solid #e2e8f0", borderRadius: 10, padding: "13px 15px", marginBottom: 16 }}>
      <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: 11 }}>
        <Label>💰 Payment Tracking</Label>
        <Badge label={ps.label} color={ps.color} bg={ps.bg} />
      </div>
      <div style={{ display: "flex", gap: 10, flexWrap: "wrap", marginBottom: 10 }}>
        <div style={{ flex: 1, minWidth: 110 }}>
          <div style={{ fontSize: 10, color: "#94a3b8", marginBottom: 3 }}>Total Amount</div>
          <input type="number" placeholder="0" disabled={readOnly} value={project.totalAmount}
            onChange={e => onUpdate({ totalAmount: e.target.value })}
            style={{ width: "100%", border: "1px solid #e2e8f0", borderRadius: 6, padding: "6px 9px", fontSize: 13, fontWeight: 700, boxSizing: "border-box" }} />
        </div>
        <div style={{ flex: 1, minWidth: 110 }}>
          <div style={{ fontSize: 10, color: "#94a3b8", marginBottom: 3 }}>Advance Received</div>
          <input type="number" placeholder="0" disabled={readOnly} value={project.advanceAmount}
            onChange={e => onUpdate({ advanceAmount: e.target.value })}
            style={{ width: "100%", border: "1px solid #e2e8f0", borderRadius: 6, padding: "6px 9px", fontSize: 13, fontWeight: 700, color: "#16a34a", boxSizing: "border-box" }} />
        </div>
        <div style={{ flex: 1, minWidth: 110 }}>
          <div style={{ fontSize: 10, color: "#94a3b8", marginBottom: 3 }}>Balance Due</div>
          <div style={{ border: "1px solid #e2e8f0", borderRadius: 6, padding: "6px 9px", fontSize: 13, fontWeight: 700,
            color: balance > 0 ? "#dc2626" : "#16a34a", background: "#f8fafc" }}>
            {total > 0 ? `${fmtMoney(balance)}` : "—"}
          </div>
        </div>
      </div>
      {total > 0 && (
        <div style={{ marginBottom: 10 }}>
          <div style={{ height: 6, background: "#e2e8f0", borderRadius: 3, overflow: "hidden" }}>
            <div style={{ height: "100%", width: `${pct}%`, background: pct === 100 ? "#16a34a" : "#2563eb", borderRadius: 3, transition: "width 0.4s" }} />
          </div>
          <div style={{ fontSize: 10, color: "#94a3b8", marginTop: 3 }}>{pct}% collected</div>
        </div>
      )}
      <div style={{ display: "flex", gap: 5, flexWrap: "wrap" }}>
        {Object.entries(PAYMENT_STATUS).map(([key, val]) => (
          <button key={key} disabled={readOnly} onClick={() => onUpdate({ paymentStatus: key })} style={{
            padding: "4px 10px", borderRadius: 20, fontSize: 11, fontWeight: 600, cursor: readOnly ? "default" : "pointer",
            border: `1.5px solid ${project.paymentStatus === key ? val.color : "#e2e8f0"}`,
            background: project.paymentStatus === key ? val.bg : "#fff",
            color: project.paymentStatus === key ? val.color : "#64748b",
          }}>{val.label}</button>
        ))}
      </div>
    </div>
  );
}

// ── Schedule cards ────────────────────────────────────────────────────────────

const navBtn = { border: "1px solid #e2e8f0", background: "#fff", borderRadius: 6, padding: "5px 12px", cursor: "pointer", fontSize: 12, color: "#374151" };

function WeekCard({ item }) {
  const { project, stage, data } = item;
  const pr = PRIORITY[project.priority] || PRIORITY.none;
  const overdue = isOverdue(data.date, data.status);
  const isDone = data.status === "done";
  return (
    <div style={{ background: isDone ? "#f0fdf4" : overdue ? "#fef2f2" : "#f8fafc", border: `1.5px solid ${pr.dot}`, borderRadius: 6, padding: "4px 6px", marginBottom: 5, fontSize: 10 }}>
      <div style={{ fontWeight: 700, color: "#1e293b", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{stage.icon} {stage.label}</div>
      <div style={{ color: "#2563eb", fontWeight: 600, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{project.customer || "Unnamed"}</div>
      {data.time && <div style={{ color: "#64748b" }}>⏰ {data.time}</div>}
    </div>
  );
}

function ScheduleCard({ item, overdue: forceOverdue }) {
  const { project, stage, data } = item;
  const pr = PRIORITY[project.priority] || PRIORITY.none;
  const st = STATUS[data.status];
  const overdue = forceOverdue || isOverdue(data.date, data.status);
  const isDone = data.status === "done";
  return (
    <div style={{ display: "flex", gap: 10, alignItems: "flex-start", marginBottom: 8 }}>
      <div style={{ minWidth: 60, textAlign: "right", paddingTop: 10 }}>
        <span style={{ fontSize: 11, fontWeight: 700, color: data.time ? "#2563eb" : "#cbd5e1" }}>{data.time || "Any time"}</span>
      </div>
      <div style={{ display: "flex", flexDirection: "column", alignItems: "center", paddingTop: 13 }}>
        <div style={{ width: 10, height: 10, borderRadius: "50%", background: pr.dot, border: "2px solid #fff", boxShadow: `0 0 0 2px ${pr.dot}`, flexShrink: 0 }} />
        <div style={{ width: 2, flex: 1, background: "#e2e8f0", minHeight: 24, marginTop: 3 }} />
      </div>
      <div style={{ flex: 1, border: `1.5px solid ${overdue ? "#fca5a5" : pr.dot + "50"}`, borderLeft: `4px solid ${overdue ? "#ef4444" : pr.dot}`, borderRadius: 9, padding: "8px 12px", background: isDone ? "#f0fdf4" : overdue ? "#fff5f5" : "#fff" }}>
        <div style={{ display: "flex", justifyContent: "space-between", flexWrap: "wrap", gap: 5 }}>
          <div>
            <div style={{ fontSize: 13, fontWeight: 700, color: overdue ? "#b91c1c" : "#1e293b" }}>{stage.icon} {stage.label}</div>
            <div style={{ fontSize: 12, color: "#2563eb", fontWeight: 600 }}>{project.customer || "Unnamed"}{project.address ? ` · ${project.address}` : ""}</div>
            {overdue && <div style={{ fontSize: 11, color: "#b91c1c", fontWeight: 600 }}>Was due: {fmtDate(data.date)}</div>}
          </div>
          <div style={{ display: "flex", gap: 4, alignItems: "flex-start", flexWrap: "wrap" }}>
            {project.priority !== "none" && <Badge label={pr.label} color={pr.color === "#ffffff" ? "#dc2626" : pr.color} bg={pr.bg} />}
            <Badge label={st.label} color={st.color} bg={st.bg} />
          </div>
        </div>
        {data.note && <div style={{ fontSize: 11, color: "#64748b", marginTop: 5, fontStyle: "italic" }}>📝 {data.note}</div>}
      </div>
    </div>
  );
}

// ── Work Schedule (Week) View ─────────────────────────────────────────────────

function WorkSchedule({ projects }) {
  const [weekStart, setWeekStart] = useState(() => mondayOf(today()));
  const days = Array.from({ length: 7 }, (_, i) => addDays(weekStart, i));

  const byDate = useMemo(() => {
    const map = {};
    projects.forEach(project => {
      STAGES.forEach(stage => {
        const d = project.stages[stage.key] || EMPTY_SD;
        if (!d.date || d.status === "skipped") return;
        if (!map[d.date]) map[d.date] = [];
        map[d.date].push({ project, stage, data: d });
      });
    });
    Object.values(map).forEach(arr => arr.sort((a, b) =>
      (TIME_SLOTS.indexOf(a.data.time) + 1 || 999) - (TIME_SLOTS.indexOf(b.data.time) + 1 || 999)));
    return map;
  }, [projects]);

  const overdueItems = useMemo(() => {
    const items = [];
    projects.forEach(project => {
      STAGES.forEach(stage => {
        const d = project.stages[stage.key] || EMPTY_SD;
        if (!d.date || d.status === "done" || d.status === "skipped") return;
        if (d.date >= weekStart) return;
        items.push({ project, stage, data: d });
      });
    });
    return items.sort((a, b) => a.data.date.localeCompare(b.data.date));
  }, [projects, weekStart]);

  function shiftWeek(n) { setWeekStart(w => addDays(w, n * 7)); }
  const td = today();

  return (
    <div>
      <div style={{ display: "flex", alignItems: "center", gap: 10, marginBottom: 16, flexWrap: "wrap" }}>
        <button onClick={() => shiftWeek(-1)} style={navBtn}>‹ Prev</button>
        <button onClick={() => setWeekStart(mondayOf(today()))} style={{ ...navBtn, background: "#eff6ff", color: "#2563eb", border: "1px solid #bfdbfe", fontWeight: 700 }}>This Week</button>
        <button onClick={() => shiftWeek(1)} style={navBtn}>Next ›</button>
        <span style={{ fontSize: 14, fontWeight: 700, color: "#1e293b" }}>
          {fmtDate(weekStart)} — {fmtDate(addDays(weekStart, 6))}
        </span>
      </div>

      {overdueItems.length > 0 && (
        <div style={{ background: "#fef2f2", border: "1.5px solid #fca5a5", borderRadius: 10, padding: "10px 14px", marginBottom: 16 }}>
          <div style={{ fontWeight: 700, color: "#b91c1c", fontSize: 13, marginBottom: 8 }}>
            ⚠️ {overdueItems.length} Overdue Task{overdueItems.length > 1 ? "s" : ""}
          </div>
          {overdueItems.map((item, i) => <ScheduleCard key={i} item={item} overdue />)}
        </div>
      )}

      <div style={{ display: "grid", gridTemplateColumns: "repeat(7, 1fr)", gap: 8 }}>
        {days.map(day => {
          const items = byDate[day] || [];
          const isToday = day === td;
          const isWeekend = new Date(day + "T12:00:00").getDay() % 6 === 0;
          return (
            <div key={day} style={{
              background: isToday ? "#eff6ff" : isWeekend ? "#f8fafc" : "#fff",
              border: `2px solid ${isToday ? "#2563eb" : "#e2e8f0"}`,
              borderRadius: 10, padding: "8px 7px", minHeight: 120,
            }}>
              <div style={{ marginBottom: 7 }}>
                <div style={{ fontSize: 10, fontWeight: 700, color: isToday ? "#2563eb" : isWeekend ? "#94a3b8" : "#374151", textTransform: "uppercase", letterSpacing: 0.7 }}>
                  {new Date(day + "T12:00:00").toLocaleDateString("en-US", { weekday: "short" })}
                </div>
                <div style={{ fontSize: 16, fontWeight: 800, color: isToday ? "#2563eb" : "#1e293b" }}>
                  {new Date(day + "T12:00:00").getDate()}
                </div>
              </div>
              {items.length === 0
                ? <div style={{ fontSize: 10, color: "#cbd5e1", textAlign: "center", paddingTop: 10 }}>—</div>
                : items.map((item, i) => <WeekCard key={i} item={item} />)}
            </div>
          );
        })}
      </div>

      <div style={{ marginTop: 24 }}>
        <div style={{ fontSize: 12, fontWeight: 700, color: "#374151", textTransform: "uppercase", letterSpacing: 1, marginBottom: 10 }}>
          All Tasks This Week — Detail
        </div>
        {days.map(day => {
          const items = byDate[day] || [];
          if (items.length === 0) return null;
          const isToday = day === td;
          return (
            <div key={day} style={{ marginBottom: 16 }}>
              <div style={{ display: "flex", alignItems: "center", gap: 8, marginBottom: 7 }}>
                <div style={{
                  fontWeight: 700, fontSize: 13, color: isToday ? "#2563eb" : "#374151",
                  background: isToday ? "#eff6ff" : "#f1f5f9",
                  border: isToday ? "1px solid #bfdbfe" : "1px solid #e2e8f0",
                  borderRadius: 6, padding: "3px 10px"
                }}>{isToday ? "📌 " : ""}{fmtDateLong(day)}</div>
                <span style={{ fontSize: 11, color: "#94a3b8" }}>{items.length} task{items.length > 1 ? "s" : ""}</span>
              </div>
              {items.map((item, i) => <ScheduleCard key={i} item={item} />)}
            </div>
          );
        })}
        {days.every(d => !byDate[d]) && !overdueItems.length && (
          <div style={{ textAlign: "center", padding: "40px 20px", color: "#94a3b8" }}>
            <div style={{ fontSize: 32, marginBottom: 8 }}>📭</div>
            <div style={{ fontWeight: 600 }}>Nothing scheduled this week</div>
            <div style={{ fontSize: 12, marginTop: 4 }}>Go to a project stage and add a date to see it here</div>
          </div>
        )}
      </div>
    </div>
  );
}

function DayView({ projects }) {
  const [selDate, setSelDate] = useState(today);
  const items = useMemo(() => {
    const arr = [];
    projects.forEach(project => {
      STAGES.forEach(stage => {
        const d = project.stages[stage.key] || EMPTY_SD;
        if (d.date === selDate && d.status !== "skipped") arr.push({ project, stage, data: d });
      });
    });
    return arr.sort((a, b) => (TIME_SLOTS.indexOf(a.data.time) + 1 || 999) - (TIME_SLOTS.indexOf(b.data.time) + 1 || 999));
  }, [projects, selDate]);

  return (
    <div>
      <div style={{ display: "flex", alignItems: "center", gap: 8, marginBottom: 18, flexWrap: "wrap" }}>
        <button onClick={() => setSelDate(d => addDays(d, -1))} style={navBtn}>‹</button>
        <input type="date" value={selDate} onChange={e => setSelDate(e.target.value)}
          style={{ border: "1px solid #e2e8f0", borderRadius: 6, padding: "5px 9px", fontSize: 13 }} />
        <button onClick={() => setSelDate(d => addDays(d, 1))} style={navBtn}>›</button>
        <button onClick={() => setSelDate(today())} style={{ ...navBtn, background: "#eff6ff", color: "#2563eb", border: "1px solid #bfdbfe", fontWeight: 700 }}>Today</button>
        <span style={{ fontSize: 14, fontWeight: 700, color: "#1e293b" }}>{fmtDateLong(selDate)}</span>
      </div>
      {items.length === 0
        ? <div style={{ textAlign: "center", padding: "50px 20px", color: "#94a3b8" }}>
            <div style={{ fontSize: 32, marginBottom: 8 }}>📭</div>
            <div style={{ fontWeight: 600 }}>Nothing scheduled for this day</div>
          </div>
        : items.map((item, i) => <ScheduleCard key={i} item={item} />)}
    </div>
  );
}

function ProjectCard({ project, selected, onSelect }) {
  const total = STAGES.length;
  const done = STAGES.filter(s => project.stages[s.key]?.status === "done").length;
  const pct = Math.round((done / total) * 100);
  const isUrgent = project.priority === "urgent" && pct < 100;
  const hasOverdue = STAGES.some(s => isOverdue(project.stages[s.key]?.date, project.stages[s.key]?.status));
  const ps = PAYMENT_STATUS[project.paymentStatus] || PAYMENT_STATUS.advance_paid;
  const pr = PRIORITY[project.priority] || PRIORITY.none;

  return (
    <div onClick={() => onSelect(project.id)} style={{
      border: `2px solid ${selected ? "#2563eb" : hasOverdue ? "#fca5a5" : isUrgent ? "#fca5a5" : "#e2e8f0"}`,
      borderRadius: 11, padding: "11px 13px", cursor: "pointer",
      background: selected ? "#eff6ff" : hasOverdue ? "#fff5f5" : "#fff",
      transition: "all 0.2s", marginBottom: 9
    }}>
      <div style={{ display: "flex", justifyContent: "space-between", alignItems: "flex-start" }}>
        <div style={{ flex: 1, minWidth: 0 }}>
          <div style={{ display: "flex", alignItems: "center", gap: 4 }}>
            {hasOverdue && <span style={{ fontSize: 9, fontWeight: 700, color: "#fff", background: "#ef4444", borderRadius: 3, padding: "1px 4px" }}>!</span>}
            {project.priority !== "none" && <span style={{ width: 8, height: 8, borderRadius: "50%", background: pr.dot, display: "inline-block" }} title={`Priority: ${pr.label}`} />}
            <div style={{ fontWeight: 700, fontSize: 13, color: "#1e293b", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>
              {project.customer || "Unnamed Customer"}
            </div>
          </div>
          {project.address && <div style={{ fontSize: 11, color: "#64748b", marginTop: 1, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>📍 {project.address}</div>}
        </div>
        <span style={{ fontSize: 11, fontWeight: 700, color: pct === 100 ? "#16a34a" : "#2563eb",
          background: pct === 100 ? "#f0fdf4" : "#eff6ff", padding: "2px 7px", borderRadius: 20,
          border: `1px solid ${pct === 100 ? "#bbf7d0" : "#bfdbfe"}`, flexShrink: 0, marginLeft: 5 }}>{pct}%</span>
      </div>
      <div style={{ marginTop: 7 }}>
        <div style={{ height: 4, background: "#e2e8f0", borderRadius: 3, overflow: "hidden" }}>
          <div style={{ height: "100%", borderRadius: 3, background: pct === 100 ? "#16a34a" : "#2563eb", width: `${pct}%`, transition: "width 0.4s" }} />
        </div>
        <div style={{ display: "flex", justifyContent: "space-between", marginTop: 4 }}>
          <span style={{ fontSize: 10, color: "#94a3b8" }}>{done}/{total} stages</span>
          <Badge label={ps.label} color={ps.color} bg={ps.bg} />
        </div>
      </div>
    </div>
  );
}

function ProjectCardSkeleton() {
  return (
    <div style={{ border: "2px solid #e2e8f0", borderRadius: 11, padding: "11px 13px", marginBottom: 9 }}>
      <div style={{ height: 13, width: "60%", background: "#f1f5f9", borderRadius: 4, marginBottom: 8 }} />
      <div style={{ height: 10, width: "40%", background: "#f1f5f9", borderRadius: 4, marginBottom: 10 }} />
      <div style={{ height: 4, background: "#f1f5f9", borderRadius: 3 }} />
    </div>
  );
}

// ── Delete confirmation ───────────────────────────────────────────────────────

function ConfirmDeleteModal({ name, onCancel, onConfirm }) {
  return (
    <Modal title="Delete Project" onClose={onCancel} width={400}>
      <div style={{ fontSize: 13.5, color: "#374151", marginBottom: 18, lineHeight: 1.5 }}>
        Delete project <b>"{name || "Unnamed Customer"}"</b>? This cannot be undone.
      </div>
      <div style={{ display: "flex", gap: 8, justifyContent: "flex-end" }}>
        <button onClick={onCancel} style={{ background: "#f1f5f9", color: "#64748b", border: "none", borderRadius: 7, padding: "8px 14px", fontSize: 12.5, fontWeight: 600, cursor: "pointer" }}>Cancel</button>
        <button onClick={onConfirm} style={{ background: "#dc2626", color: "#fff", border: "none", borderRadius: 7, padding: "8px 14px", fontSize: 12.5, fontWeight: 700, cursor: "pointer" }}>Delete</button>
      </div>
    </Modal>
  );
}

// ── Main App (post-auth) ──────────────────────────────────────────────────────

function MainApp({ user, onSignOut }) {
  const [workspaces, setWorkspaces] = useState([]);
  const [currentWorkspaceId, setCurrentWorkspaceId] = useState(user.currentWorkspaceId);
  const [projects, setProjects] = useState([]);
  const [loadingProjects, setLoadingProjects] = useState(true);
  const [selectedId, setSelectedId] = useState(null);
  const [showNew, setShowNew] = useState(false);
  const [newCust, setNewCust] = useState("");
  const [newAddr, setNewAddr] = useState("");
  const [view, setView] = useState("project");
  const [sidebarOpen, setSidebarOpen] = useState(false);
  const [showSettings, setShowSettings] = useState(false);
  const [deleteTarget, setDeleteTarget] = useState(null);
  const [toasts, setToasts] = useState([]);
  const [offline, setOffline] = useState(false);
  const [savingKeys, setSavingKeys] = useState({});

  const pushToast = useCallback((message, type = "info", retry) => {
    const id = Math.random().toString(36).slice(2);
    setToasts(t => [...t, { id, message, type, retry }]);
    if (type !== "error") setTimeout(() => setToasts(t => t.filter(x => x.id !== id)), 3500);
  }, []);
  const dismissToast = useCallback((id) => setToasts(t => t.filter(x => x.id !== id)), []);

  const currentWorkspace = workspaces.find(w => w.id === currentWorkspaceId);
  const myRole = currentWorkspace?.role || "member";
  const readOnly = myRole === "viewer";

  const loadWorkspaces = useCallback(async () => {
    try {
      const data = await apiFetch("/workspaces");
      setWorkspaces(data);
      if (!currentWorkspaceId && data.length > 0) setCurrentWorkspaceId(data[0].id);
    } catch (err) { pushToast(err.message, "error"); }
  }, [currentWorkspaceId, pushToast]);

  const loadProjects = useCallback(async () => {
    setLoadingProjects(true);
    try {
      const data = await apiFetch("/projects");
      setProjects(data);
      setOffline(false);
      setSelectedId(prev => {
        if (prev && data.find(p => p.id === prev)) return prev;
        return data[0]?.id || null;
      });
    } catch (err) {
      setOffline(true);
      pushToast("Failed to load projects.", "error", loadProjects);
    } finally {
      setLoadingProjects(false);
    }
  }, [pushToast]);

  useEffect(() => { loadWorkspaces(); }, [loadWorkspaces]);
  useEffect(() => { loadProjects(); }, [currentWorkspaceId]);

  // Re-check carry-forward periodically by refetching (server recomputes on GET)
  useEffect(() => {
    const id = setInterval(() => { loadProjects(); }, 60 * 60 * 1000);
    return () => clearInterval(id);
  }, [loadProjects]);

  const selected = projects.find(p => p.id === selectedId);

  // Debounced project-field save (~500ms) to avoid hammering the DB on every keystroke
  const debouncedSaveProjectRef = useRef({});
  function saveProjectField(projectId, updates) {
    setProjects(ps => ps.map(p => p.id === projectId ? { ...p, ...updates } : p));
    if (!debouncedSaveProjectRef.current[projectId]) {
      debouncedSaveProjectRef.current[projectId] = debounce(async (payload) => {
        setSavingKeys(s => ({ ...s, [projectId]: true }));
        try {
          await apiFetch(`/projects/${projectId}`, { method: "PUT", body: JSON.stringify(payload) });
          setOffline(false);
        } catch (err) {
          setOffline(true);
          pushToast("Change couldn't be saved — will retry.", "error", () => saveProjectField(projectId, payload));
        } finally {
          setSavingKeys(s => { const n = { ...s }; delete n[projectId]; return n; });
        }
      }, 500);
    }
    debouncedSaveProjectRef.current[projectId](updates);
  }

  function updateProject(updates) {
    if (!selected) return;
    saveProjectField(selected.id, updates);
  }

  // Stage updates: immediate optimistic local update, debounced network write per stage
  const debouncedSaveStageRef = useRef({});
  function updateStage(key, data) {
    if (!selected) return;
    const projectId = selected.id;
    setProjects(ps => ps.map(p => p.id === projectId ? { ...p, stages: { ...p.stages, [key]: data } } : p));
    const cacheKey = `${projectId}:${key}`;
    if (!debouncedSaveStageRef.current[cacheKey]) {
      debouncedSaveStageRef.current[cacheKey] = debounce(async (payload) => {
        setSavingKeys(s => ({ ...s, [cacheKey]: true }));
        try {
          await apiFetch(`/projects/${projectId}/stages/${key}`, { method: "PUT", body: JSON.stringify(payload) });
          setOffline(false);
        } catch (err) {
          setOffline(true);
          pushToast("Change couldn't be saved — will retry.", "error", () => updateStage(key, payload));
        } finally {
          setSavingKeys(s => { const n = { ...s }; delete n[cacheKey]; return n; });
        }
      }, 500);
    }
    debouncedSaveStageRef.current[cacheKey](data);
  }

  async function addProject() {
    try {
      const np = await apiFetch("/projects", { method: "POST", body: JSON.stringify({ customer: newCust, address: newAddr }) });
      setProjects(ps => [...ps, np]);
      setSelectedId(np.id);
      setNewCust(""); setNewAddr(""); setShowNew(false);
      setView("project");
    } catch (err) { pushToast(err.message, "error"); }
  }

  async function confirmDeleteProject() {
    if (!deleteTarget) return;
    try {
      await apiFetch(`/projects/${deleteTarget.id}`, { method: "DELETE" });
      const rem = projects.filter(p => p.id !== deleteTarget.id);
      setProjects(rem);
      setSelectedId(rem[0]?.id || null);
      setDeleteTarget(null);
      pushToast("Project deleted");
    } catch (err) { pushToast(err.message, "error"); }
  }

  async function switchWorkspace(id) {
    try {
      await apiFetch("/workspaces/switch", { method: "POST", body: JSON.stringify({ workspaceId: id }) });
      setCurrentWorkspaceId(id);
      setSelectedId(null);
    } catch (err) { pushToast(err.message, "error"); }
  }

  function renameWorkspaceLocal(id, name) {
    setWorkspaces(ws => ws.map(w => w.id === id ? { ...w, name } : w));
  }

  async function signOut() {
    try { await apiFetch("/auth/logout", { method: "POST" }); } catch (e) {}
    onSignOut();
  }

  const totalOverdue = projects.reduce((acc, p) =>
    acc + STAGES.filter(s => isOverdue(p.stages[s.key]?.date, p.stages[s.key]?.status)).length, 0);
  const totalToday = projects.reduce((acc, p) =>
    acc + STAGES.filter(s => isDueToday(p.stages[s.key]?.date, p.stages[s.key]?.status)).length, 0);
  const totalBalancePending = projects.filter(p => p.paymentStatus === "balance_pending").length;

  const VIEWS = [
    { key: "project",  label: "📋 Project" },
    { key: "day",      label: "📅 Day" },
    { key: "schedule", label: "🗓 Week Schedule" },
  ];

  return (
    <div style={{ fontFamily: "'Inter', system-ui, sans-serif", minHeight: "100vh", background: "#f8fafc" }}>
      <Toast toasts={toasts} onDismiss={dismissToast} />
      {offline && (
        <div style={{ background: "#fffbeb", borderBottom: "1px solid #fde68a", color: "#92400e", fontSize: 12, fontWeight: 600, textAlign: "center", padding: "6px 10px" }}>
          ⚠️ Offline or save failed — changes will retry automatically when reconnected.
        </div>
      )}
      <div style={{ background: "#1e293b", color: "#fff", padding: "13px 22px",
        display: "flex", alignItems: "center", justifyContent: "space-between", gap: 10, flexWrap: "wrap" }}>
        <div style={{ display: "flex", alignItems: "center", gap: 10 }}>
          <button onClick={() => setSidebarOpen(o => !o)} className="kpt-hamburger" style={{
            display: "none", background: "#334155", border: "none", color: "#fff", borderRadius: 6,
            width: 30, height: 30, fontSize: 15, cursor: "pointer",
          }}>☰</button>
          <div>
            <div style={{ fontWeight: 800, fontSize: 17 }}>🏠 Kitchen Project Tracker</div>
            <div style={{ fontSize: 11, color: "#94a3b8", marginTop: 1 }}>
              {projects.length} confirmed project{projects.length !== 1 ? "s" : ""}
              {totalOverdue > 0 && <span style={{ color: "#fca5a5", fontWeight: 700 }}> · ⚠️ {totalOverdue} overdue</span>}
              {totalToday > 0 && <span style={{ color: "#fde68a", fontWeight: 700 }}> · 📌 {totalToday} due today</span>}
              {totalBalancePending > 0 && <span style={{ color: "#7dd3fc", fontWeight: 700 }}> · 💰 {totalBalancePending} balance pending</span>}
            </div>
          </div>
        </div>
        <div style={{ display: "flex", gap: 5, alignItems: "center", flexWrap: "wrap" }}>
          {VIEWS.map(v => (
            <button key={v.key} onClick={() => setView(v.key)} style={{
              padding: "5px 13px", borderRadius: 7, fontSize: 12, fontWeight: 600, cursor: "pointer", border: "none",
              background: view === v.key ? "#2563eb" : "#334155", color: view === v.key ? "#fff" : "#94a3b8",
            }}>{v.label}</button>
          ))}
          <div style={{ width: 1, height: 20, background: "#475569", margin: "0 4px" }} />
          <WorkspacePicker workspaces={workspaces} currentId={currentWorkspaceId} onSwitch={switchWorkspace} />
          <button onClick={() => setShowSettings(true)} title="Workspace settings" style={{
            background: "#334155", border: "none", color: "#e2e8f0", borderRadius: 7, width: 28, height: 28, fontSize: 13, cursor: "pointer",
          }}>⚙️</button>
          <div style={{ display: "flex", alignItems: "center", gap: 6, marginLeft: 4 }}>
            <div style={{ width: 26, height: 26, borderRadius: "50%", background: "#2563eb", color: "#fff", fontSize: 11, fontWeight: 700,
              display: "flex", alignItems: "center", justifyContent: "center" }}>
              {(user.name || user.email || "?").slice(0, 1).toUpperCase()}
            </div>
            <button onClick={signOut} style={{ background: "none", border: "1px solid #475569", color: "#cbd5e1", borderRadius: 6, padding: "4px 9px", fontSize: 11, cursor: "pointer" }}>
              Sign out
            </button>
          </div>
        </div>
      </div>

      <div style={{ display: "flex", maxWidth: 1280, margin: "0 auto", position: "relative" }}>
        {sidebarOpen && (
          <div onClick={() => setSidebarOpen(false)} className="kpt-drawer-overlay" style={{
            position: "fixed", inset: 0, background: "rgba(15,23,42,0.4)", zIndex: 40,
          }} />
        )}
        <div className={"kpt-sidebar" + (sidebarOpen ? " kpt-sidebar-open" : "")} style={{ width: 240, minWidth: 190, padding: "16px 12px",
          borderRight: "1px solid #e2e8f0", minHeight: "calc(100vh - 60px)", background: "#fff" }}>
          <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: 11 }}>
            <span style={{ fontWeight: 700, fontSize: 11, color: "#374151", textTransform: "uppercase", letterSpacing: 1 }}>Projects</span>
            {!readOnly && <button onClick={() => setShowNew(f => !f)} style={{
              background: "#2563eb", color: "#fff", border: "none", borderRadius: 6,
              padding: "3px 9px", fontSize: 11, fontWeight: 600, cursor: "pointer" }}>+ New</button>}
          </div>
          {showNew && (
            <div style={{ background: "#f8fafc", border: "1px solid #e2e8f0", borderRadius: 9, padding: 9, marginBottom: 11 }}>
              <input placeholder="Customer name" value={newCust} onChange={e => setNewCust(e.target.value)}
                style={{ width: "100%", border: "1px solid #e2e8f0", borderRadius: 5, padding: "5px 8px", fontSize: 12, marginBottom: 5, boxSizing: "border-box" }} />
              <input placeholder="Address" value={newAddr} onChange={e => setNewAddr(e.target.value)}
                style={{ width: "100%", border: "1px solid #e2e8f0", borderRadius: 5, padding: "5px 8px", fontSize: 12, marginBottom: 7, boxSizing: "border-box" }} />
              <div style={{ display: "flex", gap: 5 }}>
                <button onClick={addProject} style={{ flex: 1, background: "#2563eb", color: "#fff", border: "none", borderRadius: 5, padding: "5px", fontSize: 12, fontWeight: 600, cursor: "pointer" }}>Add</button>
                <button onClick={() => setShowNew(false)} style={{ flex: 1, background: "#f1f5f9", color: "#64748b", border: "none", borderRadius: 5, padding: "5px", fontSize: 12, cursor: "pointer" }}>Cancel</button>
              </div>
            </div>
          )}
          {loadingProjects
            ? [1,2,3].map(i => <ProjectCardSkeleton key={i} />)
            : projects.map(p => <ProjectCard key={p.id} project={p} selected={p.id === selectedId} onSelect={(id) => { setSelectedId(id); setSidebarOpen(false); }} />)}
          {!loadingProjects && projects.length === 0 && (
            <div style={{ textAlign: "center", padding: "24px 10px", color: "#94a3b8", fontSize: 12 }}>No projects yet.</div>
          )}
        </div>

        <div style={{ flex: 1, padding: "20px 24px", minWidth: 0 }}>
          {loadingProjects ? (
            <div style={{ display: "flex", justifyContent: "center", padding: 60 }}><Spinner size={24} /></div>
          ) : (
            <React.Fragment>
              {view === "schedule" && <WorkSchedule projects={projects} />}
              {view === "day" && <DayView projects={projects} />}
              {view === "project" && selected && (
                <div>
                  <input placeholder="Customer name" value={selected.customer} disabled={readOnly}
                    onChange={e => updateProject({ customer: e.target.value })}
                    style={{ fontSize: 20, fontWeight: 800, color: "#1e293b", border: "none",
                      borderBottom: "2px solid #e2e8f0", background: "transparent", padding: "2px 0",
                      width: "100%", outline: "none", marginBottom: 9 }} />
                  <div style={{ display: "flex", gap: 9, flexWrap: "wrap", marginBottom: 16 }}>
                    <input placeholder="📍 Address" value={selected.address} disabled={readOnly}
                      onChange={e => updateProject({ address: e.target.value })}
                      style={{ border: "1px solid #e2e8f0", borderRadius: 6, padding: "5px 9px", fontSize: 12, flex: 1, minWidth: 130 }} />
                    <input type="date" value={selected.startDate} disabled={readOnly}
                      onChange={e => updateProject({ startDate: e.target.value })}
                      style={{ border: "1px solid #e2e8f0", borderRadius: 6, padding: "5px 9px", fontSize: 12 }} />
                    {!readOnly && (
                      <button onClick={() => setDeleteTarget(selected)} disabled={projects.length === 1}
                        style={{ background: "none", border: "1px solid #fecaca", color: "#ef4444", borderRadius: 6,
                          padding: "5px 11px", fontSize: 11, cursor: projects.length > 1 ? "pointer" : "not-allowed", opacity: projects.length === 1 ? 0.4 : 1 }}>
                        Delete
                      </button>
                    )}
                  </div>

                  <ProjectPriority project={selected} onUpdate={updateProject} readOnly={readOnly} />
                  <PaymentTracker project={selected} onUpdate={updateProject} readOnly={readOnly} />

                  <div style={{ fontSize: 11, fontWeight: 700, color: "#374151", textTransform: "uppercase", letterSpacing: 1, marginBottom: 7 }}>Workflow Stages — Schedule & Status</div>
                  {STAGES.map(stage => (
                    <StageRow key={stage.key} stage={stage} data={selected.stages[stage.key] || EMPTY_SD}
                      onChange={d => updateStage(stage.key, d)} readOnly={readOnly}
                      saving={!!savingKeys[`${selected.id}:${stage.key}`]} />
                  ))}
                  <div style={{ marginTop: 16 }}>
                    <div style={{ fontSize: 11, fontWeight: 700, color: "#374151", textTransform: "uppercase", letterSpacing: 1, marginBottom: 6 }}>Project Notes</div>
                    <textarea placeholder="General notes…" value={selected.notes} disabled={readOnly} onChange={e => updateProject({ notes: e.target.value })} rows={3}
                      style={{ width: "100%", border: "1px solid #e2e8f0", borderRadius: 8, padding: "9px 11px", fontSize: 12, resize: "vertical", fontFamily: "inherit", boxSizing: "border-box" }} />
                  </div>
                </div>
              )}
              {view === "project" && !selected && !loadingProjects && (
                <div style={{ textAlign: "center", padding: "60px 20px", color: "#94a3b8" }}>Select or create a project to get started.</div>
              )}
            </React.Fragment>
          )}
        </div>
      </div>

      {showSettings && currentWorkspace && (
        <WorkspaceSettingsModal workspace={currentWorkspace} myRole={myRole}
          onClose={() => setShowSettings(false)} onRenamed={renameWorkspaceLocal} onToast={pushToast} />
      )}
      {deleteTarget && (
        <ConfirmDeleteModal name={deleteTarget.customer} onCancel={() => setDeleteTarget(null)} onConfirm={confirmDeleteProject} />
      )}

      <style>{`
        @media (max-width: 768px) {
          .kpt-hamburger { display: inline-flex !important; align-items: center; justify-content: center; }
          .kpt-sidebar {
            position: fixed !important; top: 0; left: -260px; height: 100vh !important; z-index: 41;
            transition: left 0.2s ease; box-shadow: 4px 0 16px rgba(0,0,0,0.1);
          }
          .kpt-sidebar-open { left: 0 !important; }
        }
      `}</style>
    </div>
  );
}

// ── Root App: handles auth bootstrap ─────────────────────────────────────────

function Root() {
  const [user, setUser] = useState(undefined); // undefined = loading, null = signed out
  useEffect(() => {
    apiFetch("/auth/me").then(setUser).catch(() => setUser(null));
  }, []);

  if (user === undefined) {
    return <div style={{ minHeight: "100vh", display: "flex", alignItems: "center", justifyContent: "center", background: "#f8fafc" }}><Spinner size={28} /></div>;
  }
  if (!user) {
    return <AuthScreen onAuthed={setUser} />;
  }
  return <MainApp user={user} onSignOut={() => setUser(null)} />;
}

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