// people-manager.jsx — manager / owner shell for the People module
//
// Mounted when the current user's workspace role is `owner` or `admin`.
// Sibling file: people-employee.jsx (employee view).
// Shared atoms: people-shared.jsx (PpAvatar, PpStatusPill, PpSidebar, …).
//
// Tabs:
//   Overview     · live punch card + roster + activity feed
//   Attendance   · KPIs + punch + 14-day matrix
//   Working hours· KPIs + per-user hours table
//   Leave        · leave-request inbox + per-user balances
//   Team calendar· month calendar with leave overlays
//   (admin) Schedules · Policies · Reports — minimal CRUD stubs ready
//                       to flesh out without changing wiring.
//
// Pulls everything live from /api/people/* — the synthetic
// PP_PEOPLE / PP_LEAVE_REQUESTS data from the prototype is gone.

function PpManagerApp({ activeTab, onTabChange, hideOwnSidebar = false } = {}) {
  // Controlled / uncontrolled tab. When the main app sidebar passes
  // `activeTab` + `onTabChange`, the in-module PpSidebar is hidden
  // (the main sidebar is the only nav) and we route via the parent.
  // Without those props the module is self-contained — same shell,
  // works in isolation for tests / future standalone deploys.
  const [internalTab, setInternalTab] = React.useState("overview");
  const tab    = activeTab    || internalTab;
  const setTab = onTabChange  || setInternalTab;
  const [team, setTeam]             = React.useState(null);
  const [dashboard, setDashboard]   = React.useState(null);
  const [loading, setLoading]       = React.useState(true);
  const [err, setErr]               = React.useState("");
  const [refreshTick, setRefreshTick] = React.useState(0);
  const [requestOpen, setRequestOpen] = React.useState(false);

  const reload = React.useCallback(async () => {
    setLoading(true); setErr("");
    try {
      const [t, d] = await Promise.all([
        window.api.people.team(),
        window.api.people.dashboard(),
      ]);
      setTeam(t); setDashboard(d);
    } catch (e) {
      const msg = (e && e.body && (e.body.message || e.body.error)) || (e && e.message) || "Could not load People data.";
      setErr(msg);
    } finally {
      setLoading(false);
    }
  }, [refreshTick]);
  React.useEffect(() => { reload(); }, [reload]);

  // Live refresh every 60s while visible.
  React.useEffect(() => {
    const id = setInterval(() => {
      if (document.visibilityState === "visible") setRefreshTick(t => t + 1);
    }, 60_000);
    return () => clearInterval(id);
  }, []);

  const counts = {
    late:    (team && team.counts && team.counts.late) || 0,
    pending: (dashboard && dashboard.kpis && dashboard.kpis.pending_leave) || 0,
  };

  if (err && !team) {
    return (
      <div className="pp-app">
        <div className="pp-body" style={{ width: "100%", padding: 32 }}>
          <div className="pp-card" style={{ padding: 24, color: "var(--prio-critical, #c93636)" }}>
            {err} <button className="btn" onClick={reload} style={{ marginLeft: 12 }}>Retry</button>
          </div>
        </div>
      </div>
    );
  }

  return (
    <div className={"pp-app" + (hideOwnSidebar ? " pp-app-bare" : "")}>
      {!hideOwnSidebar && (
        <PpSidebar
          brand="People"
          subtitle={team ? `${team.people.length} people` : "Loading…"}
          items={[
            { id: "overview",   label: "Overview",      icon: "Home" },
            { id: "attendance", label: "Attendance",    icon: "Clock", count: counts.late },
            { id: "leave",      label: "Leave",         icon: "Calendar", count: counts.pending },
            { id: "calendar",   label: "Team calendar", icon: "Users" },
          ]}
          adminItems={[
            { id: "employees",  label: "Employees",        icon: "Users" },
            { id: "hrcoach",    label: "AI HR Manager",    icon: "Sparkle" },
            { id: "gifts",      label: "Gifts & rewards",  icon: "Heart" },
            { id: "timesheets", label: "Timesheets",       icon: "Clock" },
            { id: "schedules",  label: "Schedules",        icon: "Clock" },
            { id: "holidays",   label: "Holidays",         icon: "Calendar" },
            { id: "policies",   label: "Leave policies",   icon: "Coffee2" },
            { id: "balances",   label: "Leave balances",   icon: "Calendar" },
            { id: "payroll",    label: "Payroll",          icon: "Coffee2" },
            { id: "reports",    label: "Reports & exports",icon: "Download" },
          ]}
          active={tab}
          onChange={setTab}/>
      )}

      <div className="pp-body">
        <PpTopbar
          crumbs={["People", tabLabel(tab)]}
          actions={
            <>
              <button className="pp-btn" onClick={reload} disabled={loading} title="Refresh">
                <PpIcon name="Download"/> Export
              </button>
              <button className="pp-btn pp-btn-primary" onClick={() => setRequestOpen(true)}>
                <PpIcon name="Plus"/> Apply for leave
              </button>
            </>
          }/>

        <div className="pp-content">
          {tab === "overview"   && <PpOverview team={team} dashboard={dashboard} onReload={reload}/>}
          {tab === "attendance" && <PpAttendance team={team} dashboard={dashboard}/>}
          {tab === "leave"      && <PpLeaveScreen onApply={() => setRequestOpen(true)}/>}
          {tab === "calendar"   && <PpCalendarScreen/>}
          {tab === "employees"  && <PpEmployeesAdmin onReload={reload}/>}
          {tab === "hrcoach"    && <PpHrCoach/>}
          {tab === "gifts"      && (window.PpGiftsAdmin ? React.createElement(window.PpGiftsAdmin) : null)}
          {tab === "timesheets" && <PpTimesheets/>}
          {tab === "schedules"  && <PpSchedulesAdmin/>}
          {tab === "holidays"   && <PpHolidaysAdmin/>}
          {tab === "policies"   && <PpPoliciesAdmin/>}
          {tab === "balances"   && <PpBalancesAdmin/>}
          {tab === "payroll"    && (window.PpPayrollAdmin ? React.createElement(window.PpPayrollAdmin) : null)}
          {tab === "reports"    && <PpReportsAdmin/>}
        </div>
      </div>

      {requestOpen && <PpRequestModal onClose={() => { setRequestOpen(false); reload(); }}/>}
    </div>
  );
}

function tabLabel(t) {
  return ({
    overview: "Overview", attendance: "Attendance",
    leave: "Leave", calendar: "Team calendar", employees: "Employees", hrcoach: "AI HR Manager", gifts: "Gifts & rewards", timesheets: "Timesheets",
    schedules: "Schedules", holidays: "Holidays", policies: "Leave policies", balances: "Leave balances", payroll: "Payroll", reports: "Reports & exports",
  })[t] || "Overview";
}

// ── KPI strip (used by Overview + Attendance) ──────────────────
function PpKpiStrip({ dashboard }) {
  const k = (dashboard && dashboard.kpis) || {};
  return (
    <div className="pp-kpis">
      <PpKpiCard icon="Users"    label="In today"
        value={<span>{k.in_today || 0} <span style={{ fontSize: 14, color: "var(--ink-muted)", fontWeight: 500 }}>/ {k.total || 0}</span></span>}
        sub={k.remote ? `${k.remote} remote` : null}/>
      <PpKpiCard icon="Clock"    label="Late arrivals"
        value={k.late || 0}
        sub={k.late ? "today" : "all on time"}
        deltaTone={k.late ? "down" : "up"}/>
      <PpKpiCard icon="Calendar" label="On leave"
        value={k.on_leave || 0}
        sub={k.absent ? `${k.absent} unplanned absent` : null}/>
      <PpKpiCard icon="Bar"      label="Hours · today"
        value={<span>{ppFmtHours(k.hours_today || 0)}<span style={{ fontSize: 14, color: "var(--ink-muted)", fontWeight: 500 }}> / {ppFmtHours(k.hours_target || 0)}</span></span>}
        sub="logged so far"/>
    </div>
  );
}

// ── Live punch card (manager's own) ────────────────────────────
function PpPunchCard() {
  const [now, setNow] = React.useState(new Date());
  const [me, setMe] = React.useState(null);
  React.useEffect(() => {
    const id = setInterval(() => setNow(new Date()), 1000);
    return () => clearInterval(id);
  }, []);
  // Reload helper — fetched on mount, on a 30s poll, on tab focus,
  // and whenever any other surface (floating FAB, employee shell)
  // dispatches the `flowboard:punched` event. Without this, the
  // Overview hero stays stale showing "NOT PUNCHED IN YET" even
  // after the FAB confirms the user is working.
  const reload = React.useCallback(async () => {
    try { setMe(await window.api.people.me()); } catch {}
  }, []);
  React.useEffect(() => { reload(); }, [reload]);
  React.useEffect(() => {
    const id = setInterval(() => {
      if (document.visibilityState === "visible") reload();
    }, 30_000);
    return () => clearInterval(id);
  }, [reload]);
  React.useEffect(() => {
    function onVis() { if (document.visibilityState === "visible") reload(); }
    document.addEventListener("visibilitychange", onVis);
    window.addEventListener("focus", onVis);
    window.addEventListener("flowboard:punched", reload);
    return () => {
      document.removeEventListener("visibilitychange", onVis);
      window.removeEventListener("focus", onVis);
      window.removeEventListener("flowboard:punched", reload);
    };
  }, [reload]);
  // Non-tracked users (is_employee = 0, migration 048) don't punch —
  // hide this self-punch hero entirely, even for managers/owners who
  // aren't themselves marked as employees. They still see the team
  // roster, activity feed and admin tools below; just not their own
  // clock. `me` is null while loading, so we only hide once the
  // server has explicitly said isEmployee:false.
  if (me && me.isEmployee === false) return null;
  const hh = String(now.getHours()).padStart(2, "0");
  const mm = String(now.getMinutes()).padStart(2, "0");
  const ss = String(now.getSeconds()).padStart(2, "0");
  const today = (me && me.today) || {};

  async function punch(kind) {
    try {
      await window.api.people.punch({ kind });
      await reload();
      try { window.dispatchEvent(new CustomEvent("flowboard:punched", { detail: { kind } })); } catch {}
      if (window.fbToast) {
        window.fbToast(({ in: "Punched in", out: "Punched out",
                           break_start: "Break started", break_end: "Break ended" })[kind] || "Punch saved", 2200);
      }
    } catch (e) {
      const body = e && e.body;
      const msg = (body && (body.message || body.error)) || (e && e.message) || "Punch failed";
      const human = /migration_pending/i.test(String(body && body.error))
        ? "Punch storage isn't ready yet — apply migration 044 on the server, then try again."
        : msg;
      if (window.fbToast) window.fbToast(human, 5000);
      else alert(human);
      console.error("[people.punch]", e);
    }
  }
  const isIn = today.status === "in" || today.status === "late" || today.status === "remote";
  const onBreak = !!today.onBreak;
  const inLine = onBreak ? `On break · since ${today.in || ""}`
               : today.in && isIn ? `Clocked in · since ${today.in}`
               : today.out ? `Punched out · last at ${today.out}`
               : today.status === "leave" ? `On leave · ${today.leaveType || ""}`
               : today.status === "holiday" ? `Holiday · ${today.holiday || ""}`
               : "Not punched in yet";

  return (
    <div className="pp-punch">
      <div className="pp-punch-left">
        <div className="pp-punch-status"><span className="pp-dot"/> {inLine}</div>
        <div className="pp-punch-time">{hh}:{mm}<span style={{ fontSize: 22, opacity: .55 }}>:{ss}</span></div>
        <div className="pp-punch-meta">
          {today.schedule ? `Schedule ${today.schedule.from} – ${today.schedule.to}` : "No schedule set"}
        </div>
        <div className="pp-punch-actions">
          {onBreak ? (
            <>
              <button className="pp-punch-btn primary" onClick={() => punch("break_end")}>
                <PpIcon name="Play"/> End break · resume
              </button>
              <button className="pp-punch-btn danger" onClick={() => punch("out")}>
                <PpIcon name="Stop"/> Punch out
              </button>
            </>
          ) : isIn ? (
            <>
              <button className="pp-punch-btn warn" onClick={() => punch("break_start")}>
                <PpIcon name="Pause"/> Start break
              </button>
              <button className="pp-punch-btn danger" onClick={() => punch("out")}>
                <PpIcon name="Stop"/> Punch out
              </button>
            </>
          ) : (
            <button className="pp-punch-btn primary" onClick={() => punch("in")}>
              <PpIcon name="Play"/> {today.out ? "Punch back in" : "Punch in"}
            </button>
          )}
        </div>
      </div>
      <div className="pp-punch-right">
        <PunchStat label="Today" value={ppFmtHours(today.hoursLogged || 0)}
                   sub={today.breakMins ? `Net of ${today.breakMins} min break` : "—"}/>
        <PunchStat label="This week" value="—" sub="Aggregated daily"/>
        <PunchStat label="This pay period" value="—" sub="Monthly cycle"/>
        <PunchStat label="Overtime YTD" value="—" sub="From punches"/>
      </div>
    </div>
  );
}
function PunchStat({ label, value, sub }) {
  return (
    <div>
      <div className="pp-punch-stat-label">{label}</div>
      <div className="pp-punch-stat-value">{value}</div>
      <div className="pp-punch-stat-sub">{sub}</div>
    </div>
  );
}

// ── Roster ─────────────────────────────────────────────────────
function PpRoster({ team }) {
  const people = (team && team.people) || [];
  const [sel, setSel] = React.useState(null);   // clicked person → timeline
  return (
    <div className="pp-card">
      <div className="pp-card-head">
        <div>
          <div className="pp-card-title">Today's roster</div>
          <div className="pp-card-sub">{people.length} people · click anyone for their timeline</div>
        </div>
      </div>
      <div className="pp-roster">
        {people.map(p => {
          const t = p.today || {};
          const time = t.status === "leave" ? "Leave"
                     : t.status === "absent" ? "—"
                     : t.in || "—";
          const meta =
            t.status === "leave"  ? `${t.leaveType || "Leave"} today` :
            t.status === "absent" ? "No punch — auto-flagged" :
            t.status === "remote" ? `Remote · ${p.team}` :
            t.status === "late"   ? `${t.minsLate || 0} min late` :
            t.status === "holiday"? "Holiday" :
            t.status === "in"     ? `On time · ${p.team}` :
                                    `${p.team}`;
          return (
            <div className="pp-roster-card pp-roster-card-click" key={p.id}
                 role="button" tabIndex={0} title={`View ${p.name}'s timeline`}
                 onClick={() => setSel(p)}
                 onKeyDown={e => { if (e.key === "Enter" || e.key === " ") { e.preventDefault(); setSel(p); } }}>
              <div className={`pp-roster-status ${t.status}`}/>
              <PpAvatar person={p} size="sm"/>
              <div className="pp-roster-info">
                <div className="pp-roster-name">{p.name}</div>
                <div className="pp-roster-meta">{meta}</div>
              </div>
              <div className={`pp-roster-time ${t.status === "late" ? "late" : ""}`}>{time}</div>
            </div>
          );
        })}
        {people.length === 0 && (
          <div style={{ padding: 24, color: "var(--ink-muted)" }}>No team members yet.</div>
        )}
      </div>
      {sel && <PpPersonTimeline person={sel} onClose={() => setSel(null)}/>}
      <style>{`
        .pp-roster-card-click { cursor:pointer; transition:background .12s, box-shadow .12s; }
        .pp-roster-card-click:hover { background:var(--bg-subtle,#f6f8fc); box-shadow:0 0 0 1px var(--brand,#0073ea) inset; }
        .pp-roster-card-click:focus-visible { outline:2px solid var(--brand,#0073ea); outline-offset:1px; }
      `}</style>
    </div>
  );
}

// ── Per-person day timeline (roster click-through) ─────────────
// Modal showing one person's punch timeline for a given day: status,
// in/out, worked + break, a visual segment bar (work green / break
// amber) and a punch event list. Prev/Next/Today step through any day.
function PpPersonTimeline({ person, onClose }) {
  const todayYmd = () => {
    const d = new Date();
    return d.getFullYear() + "-" + String(d.getMonth() + 1).padStart(2, "0") + "-" + String(d.getDate()).padStart(2, "0");
  };
  const [date, setDate] = React.useState(todayYmd());
  const [day, setDay]   = React.useState(null);
  const [loading, setLoading] = React.useState(true);
  const [err, setErr]   = React.useState("");

  React.useEffect(() => {
    let alive = true;
    setLoading(true); setErr("");
    window.api.people.day({ userId: person.id, date })
      .then(r => { if (alive) { setDay(r.day || null); setLoading(false); } })
      .catch(e => { if (alive) { setErr((e && e.body && (e.body.message || e.body.error)) || "Could not load timeline"); setLoading(false); } });
    return () => { alive = false; };
  }, [person.id, date]);

  const isToday = date === todayYmd();
  function shiftDay(delta) {
    const [y, m, d] = date.split("-").map(Number);
    const nd = new Date(y, m - 1, d + delta);
    const ymd = nd.getFullYear() + "-" + String(nd.getMonth() + 1).padStart(2, "0") + "-" + String(nd.getDate()).padStart(2, "0");
    if (ymd > todayYmd()) return;   // no future days
    setDate(ymd);
  }
  const dateLabel = (() => {
    const [y, m, d] = date.split("-").map(Number);
    return new Date(y, m - 1, d).toLocaleDateString(undefined, { weekday: "short", day: "numeric", month: "short", year: "numeric" });
  })();

  const fmtMin = (mins) => {
    if (mins == null) return "—";
    const h = Math.floor(mins / 60), mm = mins % 60;
    const ap = h < 12 ? "AM" : "PM";
    const h12 = ((h + 11) % 12) + 1;
    return h12 + ":" + String(mm).padStart(2, "0") + " " + ap;
  };
  const fmtDur = (mins) => {
    if (!mins) return "0m";
    const h = Math.floor(mins / 60), mm = Math.round(mins % 60);
    return (h ? h + "h " : "") + mm + "m";
  };

  const segs = (day && day.segments) || [];
  // Bar window: schedule ±1h, else first/last segment, else 8:00–20:00.
  let winStart = 480, winEnd = 1200;
  if (day && day.schedule) {
    const toMin = (s) => { const [hh, mm] = String(s).split(":").map(Number); return hh * 60 + (mm || 0); };
    winStart = Math.max(0, toMin(day.schedule.from) - 60);
    winEnd   = Math.min(1440, toMin(day.schedule.to) + 60);
  }
  if (segs.length) {
    winStart = Math.min(winStart, segs[0].from);
    winEnd   = Math.max(winEnd, segs[segs.length - 1].to);
  }
  const span = Math.max(60, winEnd - winStart);
  const pct = (v) => ((v - winStart) / span) * 100;

  const statusLabel = {
    in: "Present", late: "Late", remote: "Remote", out: "Punched out",
    absent: "Absent", leave: "On leave", holiday: "Holiday",
  }[day && day.status] || (day && day.status) || "—";

  return (
    <div className="modal-backdrop" onClick={onClose}>
      <div className="pp-modal" onClick={ev => ev.stopPropagation()} style={{ maxWidth: 600 }}>
        <div className="pp-modal-head">
          <div style={{ display: "flex", alignItems: "center", gap: 10 }}>
            <PpAvatar person={person} size="md"/>
            <div>
              <div className="pp-modal-title" style={{ marginBottom: 0 }}>{person.name}</div>
              <div style={{ fontSize: 12, color: "var(--ink-muted)" }}>{person.team || "—"}</div>
            </div>
          </div>
          <button onClick={onClose} className="pp-modal-close">×</button>
        </div>

        <div className="pp-modal-body">
          {/* Date navigator */}
          <div style={{ display: "flex", alignItems: "center", gap: 8, marginBottom: 14 }}>
            <button className="pp-btn" onClick={() => shiftDay(-1)} title="Previous day"><PpIcon name="ChevronL"/></button>
            <div style={{ flex: 1, textAlign: "center", fontWeight: 600, fontSize: 14 }}>
              {dateLabel}{isToday && <span style={{ fontSize: 11, color: "var(--brand,#0073ea)", marginLeft: 6 }}>· Today</span>}
            </div>
            <button className="pp-btn" onClick={() => shiftDay(1)} disabled={isToday} title="Next day"><PpIcon name="ChevronR"/></button>
            {!isToday && <button className="pp-btn" onClick={() => setDate(todayYmd())}>Today</button>}
          </div>

          {loading ? (
            <div style={{ padding: 28, textAlign: "center", color: "var(--ink-muted)" }}>Loading timeline…</div>
          ) : err ? (
            <div style={{ padding: 28, textAlign: "center", color: "var(--prio-critical,#c93636)" }}>{err}</div>
          ) : !day ? (
            <div style={{ padding: 28, textAlign: "center", color: "var(--ink-muted)" }}>No data for this day.</div>
          ) : (
            <>
              {/* KPI row */}
              <div style={{ display: "grid", gridTemplateColumns: "repeat(4,1fr)", gap: 10, marginBottom: 16 }}>
                {[
                  ["Status", statusLabel],
                  ["In", day.in || "—"],
                  ["Out", day.out || "—"],
                  ["Worked", fmtDur(Math.round((day.hoursLogged || 0) * 60))],
                ].map(([k, v]) => (
                  <div key={k} style={{ background: "var(--bg-subtle,#f6f8fc)", borderRadius: 8, padding: "8px 10px" }}>
                    <div style={{ fontSize: 10.5, textTransform: "uppercase", letterSpacing: ".06em", color: "var(--ink-muted)" }}>{k}</div>
                    <div style={{ fontSize: 15, fontWeight: 700, color: "var(--ink-strong)" }}>{v}</div>
                  </div>
                ))}
              </div>

              <div style={{ fontSize: 12, color: "var(--ink-muted)", marginBottom: 8 }}>
                {day.schedule ? `Scheduled ${day.schedule.from} – ${day.schedule.to}` : "No scheduled shift"}
                {day.breakMins ? ` · ${fmtDur(day.breakMins)} break` : ""}
                {day.holiday ? ` · Holiday: ${day.holiday}` : ""}
                {day.leaveType ? ` · ${day.leaveType}` : ""}
              </div>

              {/* Graphical timeline (shared component) */}
              {segs.length > 0 ? (
                <>
                  <div style={{ margin: "4px 0 16px" }}>
                    <PpTimeline segments={segs} height={16}/>
                  </div>

                  {/* Event list */}
                  <div style={{ display: "flex", flexDirection: "column", gap: 6 }}>
                    {segs.map((s, i) => (
                      <div key={i} style={{ display: "flex", alignItems: "center", gap: 10, fontSize: 13 }}>
                        <span style={{ width: 9, height: 9, borderRadius: 99,
                                       background: s.kind === "break" ? "var(--prio-medium,#fdab3d)" : "var(--ok,#21a366)" }}/>
                        <span style={{ fontWeight: 600, color: "var(--ink-strong)", minWidth: 64, textTransform: "capitalize" }}>{s.kind === "break" ? "Break" : "Work"}</span>
                        <span style={{ color: "var(--ink)" }}>{fmtMin(s.from)} – {fmtMin(s.to)}</span>
                        <span style={{ marginLeft: "auto", color: "var(--ink-muted)" }}>{fmtDur(s.to - s.from)}</span>
                      </div>
                    ))}
                  </div>
                </>
              ) : (
                <div style={{ padding: 22, textAlign: "center", color: "var(--ink-muted)", background: "var(--bg-subtle,#f6f8fc)", borderRadius: 8 }}>
                  {day.status === "leave" ? `On leave${day.leaveType ? " · " + day.leaveType : ""}`
                   : day.status === "holiday" ? `Holiday${day.holiday ? " · " + day.holiday : ""}`
                   : day.status === "absent" ? "No punches recorded — marked absent"
                   : "No punches recorded for this day"}
                </div>
              )}
            </>
          )}
        </div>
      </div>
    </div>
  );
}

// ── Activity feed ──────────────────────────────────────────────
function PpActivityFeed({ dashboard }) {
  const items = (dashboard && dashboard.activity) || [];
  return (
    <div className="pp-card">
      <div className="pp-card-head">
        <div className="pp-card-title">Punch activity</div>
        <div className="pp-card-sub" style={{ marginLeft: "auto" }}>Today</div>
      </div>
      <div style={{ padding: "4px 0" }}>
        {items.map((l, i) => (
          <div key={i} style={{ display: "flex", gap: 10, padding: "9px 16px", alignItems: "center",
                                borderBottom: i === items.length - 1 ? "none" : "1px solid var(--border-row, #eaecf1)" }}>
            <PpAvatar person={{ name: l.user_name, color: l.color }} size="sm"/>
            <div style={{ flex: 1, minWidth: 0 }}>
              <div style={{ fontSize: 13, color: "var(--ink-strong)" }}>
                <b style={{ fontWeight: 600 }}>{l.user_name}</b>
                <span style={{ color: "var(--ink-muted)", fontWeight: 500 }}> · {actionLabel(l)}</span>
              </div>
              {l.minsLate > 0 && (
                <div style={{ fontSize: 11.5, color: "#8a5a14", marginTop: 1 }}>{l.minsLate} min late</div>
              )}
            </div>
            <div style={{ fontSize: 12, fontWeight: 600, color: "var(--ink-strong)", fontVariantNumeric: "tabular-nums" }}>{l.at}</div>
          </div>
        ))}
        {items.length === 0 && (
          <div style={{ padding: 24, color: "var(--ink-muted)", textAlign: "center" }}>No activity yet today.</div>
        )}
      </div>
    </div>
  );
}
function actionLabel(l) {
  if (l.kind === "in")      return "punched in";
  if (l.kind === "late_in") return "punched in late";
  if (l.kind === "out")     return "punched out";
  return l.kind;
}

// ── Overview ──────────────────────────────────────────────────
function PpOverview({ team, dashboard, onReload }) {
  return (
    <>
      <PpKpiStrip dashboard={dashboard}/>
      <PpPunchCard/>
      <div style={{ display: "grid", gridTemplateColumns: "1.4fr 1fr", gap: 18 }}>
        <PpRoster team={team}/>
        <PpActivityFeed dashboard={dashboard}/>
      </div>
    </>
  );
}

// ── Attendance ────────────────────────────────────────────────
function PpAttendance({ team, dashboard }) {
  return (
    <>
      <PpKpiStrip dashboard={dashboard}/>
      <PpPunchCard/>
      <PpMatrix/>
      <PpRoster team={team}/>
    </>
  );
}

// ── Matrix (live from /matrix endpoint) ───────────────────────
function PpMatrix() {
  const [data, setData] = React.useState(null);
  const [err, setErr]   = React.useState("");
  React.useEffect(() => {
    (async () => {
      try { setData(await window.api.people.matrix({ days: 14 })); }
      catch (e) { setErr((e && e.message) || "Could not load matrix"); }
    })();
  }, []);
  if (err) return <div className="pp-card" style={{ padding: 20, color: "var(--prio-critical, #c93636)" }}>{err}</div>;
  if (!data) return <div className="pp-card" style={{ padding: 20, color: "var(--ink-muted)" }}>Loading attendance…</div>;
  const days = makeDayHeaders(data.from, data.to);
  return (
    <div className="pp-card">
      <div className="pp-card-head">
        <div>
          <div className="pp-card-title">Attendance · last 14 days</div>
          <div className="pp-card-sub">{ppFmtRange(data.from, data.to)} · derived from punches & schedules</div>
        </div>
      </div>
      <div className="pp-matrix-wrap">
        <div className="pp-matrix" style={{ "--days": days.length }}>
          <div className="pp-matrix-head">
            <div className="name">Person</div>
            {days.map((d, i) => (
              <div key={i} className={d.weekend ? "weekend" : ""}>
                <div style={{ fontSize: 10 }}>{d.dow[0]}</div>
                <div style={{ fontWeight: 700, color: d.today ? "var(--brand)" : null }}>{d.dnum}</div>
              </div>
            ))}
          </div>
          {data.people.map(p => (
            <div className="pp-matrix-row" key={p.id}>
              <div className="name">
                <PpAvatar person={p} size="sm"/>
                <span style={{ overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{p.name}</span>
              </div>
              {p.days.map((d, i) => {
                const glyph = ({ in: "✓", late: "L", remote: "R", leave: "V", sick: "S",
                                  absent: "✕", holiday: "H", out: "" })[d.status] || "";
                return <div key={i} className={`pp-matrix-cell ${d.status}`} title={`${d.iso} · ${d.status}`}>{glyph}</div>;
              })}
            </div>
          ))}
        </div>
        <div className="pp-matrix-legend">
          <span className="item"><span className="swatch" style={{ background: "#e8f8f0" }}/> Present</span>
          <span className="item"><span className="swatch" style={{ background: "#fdf3e0" }}/> Late</span>
          <span className="item"><span className="swatch" style={{ background: "#e1edff" }}/> Remote</span>
          <span className="item"><span className="swatch" style={{ background: "#f1e3fa" }}/> On leave</span>
          <span className="item"><span className="swatch" style={{ background: "#ffe1e6" }}/> Absent</span>
          <span className="item"><span className="swatch" style={{ background: "#fff3e0" }}/> Holiday</span>
          <span className="item"><span className="swatch" style={{ background: "var(--bg-subtle)" }}/> Weekend</span>
        </div>
      </div>
    </div>
  );
}
function makeDayHeaders(fromIso, toIso) {
  if (!fromIso || !toIso) return [];
  const out = [];
  const today = new Date().toISOString().slice(0, 10);
  for (let d = new Date(fromIso + "T00:00:00"); d <= new Date(toIso + "T00:00:00"); d.setDate(d.getDate() + 1)) {
    const dow = ["Sun","Mon","Tue","Wed","Thu","Fri","Sat"][d.getDay()];
    out.push({
      iso: d.toISOString().slice(0, 10),
      dow, dnum: d.getDate(),
      weekend: d.getDay() === 0 || d.getDay() === 6,
      today: d.toISOString().slice(0, 10) === today,
    });
  }
  return out;
}

// ── Working hours ─────────────────────────────────────────────
function PpHours() {
  const [data, setData] = React.useState(null);
  React.useEffect(() => {
    (async () => { try { setData(await window.api.people.hours({ days: 7 })); } catch {} })();
  }, []);
  if (!data) return <div className="pp-card" style={{ padding: 20, color: "var(--ink-muted)" }}>Loading hours…</div>;
  const max = Math.max(1, ...data.by_user.map(r => r.hours));
  return (
    <div className="pp-card">
      <div className="pp-card-head">
        <div>
          <div className="pp-card-title">Hours logged · this week</div>
          <div className="pp-card-sub">{ppFmtRange(data.from, data.to)} · {data.team_total}h of {data.team_target}h target</div>
        </div>
      </div>
      <div style={{ padding: "8px 0 14px" }}>
        {data.by_user.map(r => (
          <div key={r.id} style={{ display: "grid", gridTemplateColumns: "200px 1fr 70px 70px", gap: 12,
                                    alignItems: "center", padding: "8px 16px", borderBottom: "1px solid var(--border-row, #eaecf1)" }}>
            <div style={{ display: "flex", gap: 8, alignItems: "center" }}>
              <PpAvatar person={r} size="sm"/>
              <span style={{ fontSize: 13, fontWeight: 600, color: "var(--ink-strong)" }}>{r.name}</span>
            </div>
            <div style={{ height: 8, background: "var(--bg-subtle, #f5f6f8)", borderRadius: 99, overflow: "hidden" }}>
              <div style={{ height: "100%", width: ((r.hours / max) * 100) + "%",
                            background: "linear-gradient(90deg, #0073ea, #0061c9)" }}/>
            </div>
            <div style={{ textAlign: "right", fontSize: 13, fontWeight: 700,
                          color: "var(--ink-strong)", fontVariantNumeric: "tabular-nums" }}>{r.hours}h</div>
            <div style={{ textAlign: "right", fontSize: 12, color: "var(--ink-muted)" }}>/ {r.target}h</div>
          </div>
        ))}
      </div>
    </div>
  );
}

// ── Leave inbox ───────────────────────────────────────────────
function PpLeaveScreen({ onApply }) {
  const [items, setItems] = React.useState([]);
  const [types, setTypes] = React.useState([]);
  const [busy, setBusy]   = React.useState(null);
  const reload = React.useCallback(async () => {
    try {
      const [reqs, t] = await Promise.all([
        window.api.people.listLeaveRequests(),
        window.api.people.leaveTypes(),
      ]);
      setItems(reqs.items || []);
      setTypes(t.types || []);
    } catch {}
  }, []);
  React.useEffect(() => { reload(); }, [reload]);

  async function decide(id, outcome) {
    if (busy) return;
    let decisionNote = null;
    if (outcome === "rejected") {
      decisionNote = window.prompt("Reason for rejecting (the employee will see this):", "");
      if (decisionNote === null) return; // cancelled
    }
    setBusy(id);
    try { await window.api.people.decideLeaveRequest(id, { outcome, decisionNote }); await reload(); }
    finally { setBusy(null); }
  }
  const groups = {
    pending:  items.filter(i => i.status === "pending"),
    approved: items.filter(i => i.status === "approved"),
    rejected: items.filter(i => i.status === "rejected"),
  };
  return (
    <>
      <div className="pp-card">
        <div className="pp-card-head">
          <div>
            <div className="pp-card-title">Leave requests</div>
            <div className="pp-card-sub">{groups.pending.length} pending · {groups.approved.length} approved · {groups.rejected.length} rejected</div>
          </div>
          <button className="pp-btn pp-btn-primary" style={{ marginLeft: "auto" }} onClick={onApply}>
            <PpIcon name="Plus"/> New request
          </button>
        </div>
        {["pending","approved","rejected"].map(g => groups[g].length > 0 && (
          <div key={g}>
            <div style={{ padding: "10px 16px", fontSize: 11, fontWeight: 700,
                          letterSpacing: ".08em", textTransform: "uppercase",
                          color: "var(--ink-muted)", background: "var(--bg-subtle, #fafbfc)",
                          borderTop: "1px solid var(--border, #e6e9ef)" }}>
              {g}
            </div>
            {groups[g].map(r => (
              <div key={r.id} style={{ display: "flex", gap: 12, padding: "12px 16px",
                                        alignItems: "center", borderBottom: "1px solid var(--border-row, #eaecf1)" }}>
                <PpAvatar person={{ name: r.user_name, color: r.user_color, avatar: r.user_avatar }} size="md"/>
                <div style={{ flex: 1, minWidth: 0 }}>
                  <div style={{ fontSize: 13, fontWeight: 600, color: "var(--ink-strong)" }}>
                    {r.user_name} · {r.type_name}
                  </div>
                  <div style={{ fontSize: 11.5, color: "var(--ink-muted)", marginTop: 2 }}>
                    {ppFmtRange(r.from_date, r.to_date)} · {r.half_day
                      ? `½ day (${(r.half_day_period || "am").toUpperCase()})`
                      : `${Number(r.days)} day${Number(r.days) === 1 ? "" : "s"}`}
                    {r.note ? ` · "${String(r.note).slice(0, 80)}"` : ""}
                  </div>
                  <div style={{ display: "flex", gap: 8, marginTop: 4, flexWrap: "wrap", alignItems: "center" }}>
                    {r.certificate_required === 1 || r.certificate_required === true ? (
                      r.medical_certificate_url
                        ? <a href={r.medical_certificate_url} target="_blank" rel="noopener noreferrer"
                             style={{ fontSize: 11, fontWeight: 600, color: "#066039" }}>📎 View certificate</a>
                        : <span style={{ fontSize: 11, fontWeight: 600, color: "#b41f37" }}>⚠ Certificate pending</span>
                    ) : null}
                    {r.original_type_name && (
                      <span style={{ fontSize: 11, color: "#8c4a00" }}>↳ converted from {r.original_type_name}</span>
                    )}
                    {r.status === "rejected" && r.decision_note && (
                      <span style={{ fontSize: 11, color: "var(--ink-muted)", fontStyle: "italic" }}>
                        Reason: {String(r.decision_note).slice(0, 100)}
                      </span>
                    )}
                  </div>
                </div>
                {g === "pending" ? (
                  <div style={{ display: "flex", gap: 6 }}>
                    <button className="pp-btn" disabled={busy === r.id} onClick={() => decide(r.id, "rejected")}>
                      <PpIcon name="X"/> Reject
                    </button>
                    <button className="pp-btn pp-btn-primary" disabled={busy === r.id} onClick={() => decide(r.id, "approved")}>
                      <PpIcon name="Check"/> Approve
                    </button>
                  </div>
                ) : (
                  <PpStatusPill status={r.status}/>
                )}
              </div>
            ))}
          </div>
        ))}
        {items.length === 0 && (
          <div style={{ padding: 32, textAlign: "center", color: "var(--ink-muted)" }}>
            No leave requests yet.
          </div>
        )}
      </div>
    </>
  );
}

// ── Employees admin ───────────────────────────────────────────
// Owner/admin chooses who is a tracked employee. Non-employees stay in
// the workspace (projects, chat, etc.) but drop out of attendance,
// leave, payroll, calendars and HR reports.
function PpEmployeesAdmin({ onReload }) {
  const [members, setMembers] = React.useState(null);
  const [busy, setBusy]   = React.useState(null);
  const [q, setQ]         = React.useState("");
  const [err, setErr]     = React.useState("");
  const [profileId, setProfileId] = React.useState(null);
  const [insElig, setInsElig] = React.useState(null);
  const reload = React.useCallback(async () => {
    try { const r = await window.api.people.members(); setMembers(r.members || []); }
    catch (e) { setErr((e && e.body && (e.body.message || e.body.error)) || (e && e.message) || "Could not load members"); }
  }, []);
  React.useEffect(() => { reload(); }, [reload]);
  React.useEffect(() => {
    let alive = true;
    (async () => {
      try { const r = await window.api.people.insuranceEligible(); if (alive) setInsElig(r); } catch {}
    })();
    return () => { alive = false; };
  }, []);

  async function toggle(m) {
    if (busy) return;
    setBusy(m.id);
    try {
      await window.api.people.setMemberEmployment(m.id, !m.is_employee);
      setMembers(ms => ms.map(x => x.id === m.id ? { ...x, is_employee: !x.is_employee } : x));
      if (onReload) onReload();
    } catch (e) {
      if (window.fbToast) window.fbToast((e && e.body && e.body.message) || "Could not update", 3000);
    } finally { setBusy(null); }
  }

  if (err) return <div className="pp-card" style={{ padding: 24, color: "var(--prio-critical, #c93636)" }}>{err}</div>;
  if (!members) return <div className="pp-card" style={{ padding: 24, color: "var(--ink-muted)" }}>Loading members…</div>;

  const ql = q.trim().toLowerCase();
  const filtered = ql
    ? members.filter(m => (m.name || "").toLowerCase().includes(ql) || (m.email || "").toLowerCase().includes(ql))
    : members;
  const employees = filtered.filter(m => m.is_employee);
  const others    = filtered.filter(m => !m.is_employee);

  const Row = (m) => (
    <div key={m.id} style={{ display: "flex", gap: 12, padding: "11px 16px", alignItems: "center",
                             borderBottom: "1px solid var(--border-row, #eaecf1)" }}>
      <PpAvatar person={{ name: m.name, color: m.color, avatar: m.avatar }} size="md"/>
      <div style={{ flex: 1, minWidth: 0 }}>
        <div style={{ fontSize: 13, fontWeight: 600, color: "var(--ink-strong)" }}>
          {m.name} <span style={{ fontSize: 11, fontWeight: 500, color: "var(--ink-muted)", textTransform: "capitalize" }}>· {m.ws_role}</span>
        </div>
        <div style={{ fontSize: 11.5, color: "var(--ink-muted)", display: "flex", gap: 6, alignItems: "center", flexWrap: "wrap" }}>
          <span>{m.email || "—"}</span>
          {m.employee_code && (
            <span style={{ fontFamily: "ui-monospace,SFMono-Regular,Menlo,monospace", fontSize: 10.5, fontWeight: 700,
                           color: "var(--ink-strong)", background: "var(--bg-subtle,#eef1f6)", borderRadius: 4, padding: "1px 6px" }}>
              {m.employee_code}
            </span>
          )}
        </div>
      </div>
      <button type="button" className="pp-btn" style={{ marginRight: 4 }} onClick={() => setProfileId(m.id)} title="View profile">
        Profile
      </button>
      <button
        type="button"
        className={"pp-emp-switch" + (m.is_employee ? " is-on" : "")}
        disabled={busy === m.id}
        onClick={() => toggle(m)}
        title={m.is_employee ? "Tracked as an employee — click to exclude" : "Not tracked — click to track"}
        aria-pressed={m.is_employee}>
        <span className="pp-emp-switch-knob"/>
        <span className="pp-emp-switch-label">{m.is_employee ? "Employee" : "Not tracked"}</span>
      </button>
    </div>
  );

  return (
    <div className="pp-card">
      <div className="pp-card-head">
        <div>
          <div className="pp-card-title">Employees</div>
          <div className="pp-card-sub">
            {employees.length} tracked · {others.length} excluded — only employees appear in attendance, leave & payroll.
          </div>
        </div>
        <input className="pp-input" placeholder="Search…" value={q} onChange={e => setQ(e.target.value)}
               style={{ marginLeft: "auto", maxWidth: 220 }}/>
      </div>
      {insElig && insElig.items && insElig.items.length > 0 && (
        <div className="pp-ins-alert">
          <span className="pp-ins-alert-ico">🛡️</span>
          <div style={{ flex: 1, minWidth: 0 }}>
            <div className="pp-ins-alert-h">
              {insElig.items.length} employee{insElig.items.length === 1 ? "" : "s"} eligible for insurance
              <span className="pp-ins-alert-sub"> · {insElig.threshold}+ months, not yet enrolled</span>
            </div>
            <div className="pp-ins-alert-names">
              {insElig.items.slice(0, 8).map(it => (
                <button key={it.id} className="pp-ins-alert-chip" onClick={() => setProfileId(it.id)}
                        title={`${it.months} months · open profile`}>{it.name}</button>
              ))}
              {insElig.items.length > 8 && <span className="pp-ins-alert-more">+{insElig.items.length - 8}</span>}
            </div>
          </div>
          <style>{`
            .pp-ins-alert { display:flex; gap:12px; align-items:flex-start; margin:10px 16px; padding:11px 14px;
              background:#fff8ec; border:1px solid #fadc9b; border-radius:10px; }
            .pp-ins-alert-ico { font-size:18px; }
            .pp-ins-alert-h { font-size:13px; font-weight:700; color:#9a6a08; }
            .pp-ins-alert-sub { font-weight:500; color:#a8740f; }
            .pp-ins-alert-names { display:flex; gap:6px; flex-wrap:wrap; margin-top:7px; }
            .pp-ins-alert-chip { font:inherit; font-size:11.5px; font-weight:600; color:#8a5a0a;
              background:#fff; border:1px solid #f0d28a; border-radius:99px; padding:3px 10px; cursor:pointer; }
            .pp-ins-alert-chip:hover { background:#fdf2da; }
            .pp-ins-alert-more { font-size:11.5px; color:#a8740f; align-self:center; }
          `}</style>
        </div>
      )}
      {employees.map(Row)}
      {others.length > 0 && (
        <>
          <div style={{ padding: "10px 16px", fontSize: 11, fontWeight: 700, letterSpacing: ".08em",
                        textTransform: "uppercase", color: "var(--ink-muted)",
                        background: "var(--bg-subtle, #fafbfc)", borderTop: "1px solid var(--border, #e6e9ef)" }}>
            Not tracked
          </div>
          {others.map(Row)}
        </>
      )}
      {filtered.length === 0 && (
        <div style={{ padding: 28, textAlign: "center", color: "var(--ink-muted)" }}>No members match.</div>
      )}
      <style>{`
        .pp-emp-switch { display:inline-flex; align-items:center; gap:8px; cursor:pointer;
          border:1px solid var(--border,#e6e9ef); background:var(--bg-subtle,#f3f4f7);
          border-radius:999px; padding:4px 12px 4px 4px; font-size:12px; font-weight:600;
          color:var(--ink-muted); transition:background .12s, color .12s, border-color .12s; }
        .pp-emp-switch:hover:not(:disabled) { border-color:var(--brand,#0073ea); }
        .pp-emp-switch:disabled { opacity:.6; cursor:default; }
        .pp-emp-switch-knob { width:16px; height:16px; border-radius:99px; background:#c2c7d0;
          transition:background .12s, transform .12s; }
        .pp-emp-switch.is-on { background:#e6f9ef; border-color:#9be0b8; color:#066039; }
        .pp-emp-switch.is-on .pp-emp-switch-knob { background:#21a366; transform:translateX(2px); }
      `}</style>
      {profileId && window.PpProfileModal &&
        React.createElement(window.PpProfileModal, { userId: profileId, onClose: () => setProfileId(null) })}
    </div>
  );
}

// ── Timesheets (attendance editor) ────────────────────────────
// Owner/admin tool: pick an employee → day-wise attendance timeline →
// click any day to open a side editor where individual punch
// timestamps can be edited, deleted, or added (with a source + note).
// All writes are audit-stamped (edited_by/edited_at) on the server.

const PP_KIND_OPTS = [
  { id: "in",          label: "Punch in" },
  { id: "out",         label: "Punch out" },
  { id: "break_start", label: "Break start" },
  { id: "break_end",   label: "Break end" },
];
const PP_SOURCE_OPTS = [
  { key: "manual",    label: "Manual",          source: "manual" },
  { key: "biometric", label: "Biometric",       source: "biometric" },
  { key: "forgot",    label: "Forgot to punch", source: "manual", note: "Forgot to punch" },
  { key: "web",       label: "Web",             source: "web" },
  { key: "mobile",    label: "Mobile",          source: "mobile" },
];
function ppKindLabel(k) { return (PP_KIND_OPTS.find(o => o.id === k) || {}).label || k; }
function ppResolveSource(key, note) {
  const o = PP_SOURCE_OPTS.find(x => x.key === key) || PP_SOURCE_OPTS[0];
  const n = (note && note.trim()) ? note.trim() : (o.note || "");
  return { source: o.source, note: n };
}
function ppFmtDur(mins) {
  if (!mins) return "0m";
  const h = Math.floor(mins / 60), mm = Math.round(mins % 60);
  return (h ? h + "h " : "") + mm + "m";
}
function ppYmd(d) {
  return d.getFullYear() + "-" + String(d.getMonth() + 1).padStart(2, "0") + "-" + String(d.getDate()).padStart(2, "0");
}
// Date-range presets for the Timesheets filter. Returns { key, from, to }.
// Week starts on Monday (matches IST work-week convention).
function ppPresetRange(key) {
  const today = new Date();
  const dow = (today.getDay() + 6) % 7; // 0=Mon … 6=Sun
  let from = new Date(), to = new Date();
  if      (key === "today")     { /* both = today */ }
  else if (key === "yesterday") { from.setDate(today.getDate() - 1); to.setDate(today.getDate() - 1); }
  else if (key === "7")         from.setDate(today.getDate() - 6);
  else if (key === "20")        from.setDate(today.getDate() - 19);
  else if (key === "30")        from.setDate(today.getDate() - 29);
  else if (key === "thisweek")  { from = new Date(today); from.setDate(today.getDate() - dow); }
  else if (key === "lastweek")  { from = new Date(today); from.setDate(today.getDate() - dow - 7);
                                  to = new Date(from); to.setDate(from.getDate() + 6); }
  else if (key === "thismonth") from = new Date(today.getFullYear(), today.getMonth(), 1);
  else if (key === "lastmonth") { from = new Date(today.getFullYear(), today.getMonth() - 1, 1); to = new Date(today.getFullYear(), today.getMonth(), 0); }
  else if (key === "thisyear")  from = new Date(today.getFullYear(), 0, 1);
  return { key, from: ppYmd(from), to: ppYmd(to) };
}
const PP_RANGE_PRESETS = [
  { key: "today",     label: "Today" },
  { key: "yesterday", label: "Yesterday" },
  { key: "thisweek",  label: "This week" },
  { key: "lastweek",  label: "Last week" },
  { key: "7",         label: "Last 7 days" },
  { key: "20",        label: "Last 20 days" },
  { key: "30",        label: "Last 30 days" },
  { key: "thismonth", label: "This month" },
  { key: "lastmonth", label: "Last month" },
  { key: "thisyear",  label: "This year" },
];

// Aggregate stats for a list of day objects (as returned by people.timeline).
// Used to show a summary card above the day list.
//   shortDays: a worked day that logged less than 8 hours — useful for
//              spotting under-time without combing the list.
function ppRangeSummary(days) {
  const s = { worked: 0, lateMins: 0, present: 0, late: 0, absent: 0, leave: 0, holiday: 0, shortDays: 0, days: 0 };
  for (const d of (days || [])) {
    s.days++;
    if (d.hoursLogged) s.worked += d.hoursLogged * 60;
    if (d.minsLate)    s.lateMins += d.minsLate;
    const isWorked = d.status === "in" || d.status === "out" || d.status === "remote" || d.status === "late";
    if      (d.status === "in" || d.status === "out" || d.status === "remote") s.present++;
    else if (d.status === "late")    { s.present++; s.late++; }
    else if (d.status === "absent")  s.absent++;
    else if (d.status === "leave" || d.status === "sick") s.leave++;
    else if (d.status === "holiday") s.holiday++;
    // Count finished (out) days that fell short of 8h. Ignore the
    // ongoing day (still "in") so a person checked in but not yet
    // out at 5pm isn't flagged.
    if (isWorked && d.out && (d.hoursLogged || 0) < 8) s.shortDays++;
  }
  return s;
}
// Fixed window for the per-day timeline bar (06:00–22:00), so bars are
// comparable across rows. Minutes-of-day.
const PP_BAR_WS = 360, PP_BAR_WE = 1320;
function ppBarPct(v) { return Math.max(0, Math.min(100, ((v - PP_BAR_WS) / (PP_BAR_WE - PP_BAR_WS)) * 100)); }

function PpTimesheets() {
  const [members, setMembers] = React.useState(null);
  const [sel, setSel]         = React.useState(null);    // selected member
  const [days, setDays]       = React.useState(null);
  const [loadingDays, setLD]  = React.useState(false);
  const [editDate, setEditDate] = React.useState(null);
  const [q, setQ]             = React.useState("");
  const [err, setErr]         = React.useState("");
  const [range, setRange]     = React.useState(() => ppPresetRange("20"));  // default last 20 days
  const [tick, setTick]       = React.useState(0);       // bump to re-fetch after edits
  const [todayMap, setTodayMap] = React.useState({});    // user_id → today {status,in,out,leaveType}
  const [teamToday, setTeamToday] = React.useState(null);// full team list for the day summary card

  React.useEffect(() => {
    window.api.people.members()
      .then(r => setMembers((r.members || []).filter(m => m.is_employee)))
      .catch(e => setErr((e && e.body && (e.body.message || e.body.error)) || "Could not load employees"));
    // Pull today's in/out for everyone so each row can show it under the email.
    window.api.people.team()
      .then(r => {
        const map = {};
        for (const p of (r.people || [])) map[p.id] = p.today || {};
        setTodayMap(map);
        setTeamToday(r.people || []);
      })
      .catch(() => {});
  }, []);

  // Re-fetch whenever the selected person, the date range, or the
  // refresh tick changes.
  React.useEffect(() => {
    if (!sel) { setDays(null); return; }
    let alive = true;
    setLD(true);
    window.api.people.timeline({ userId: sel.id, from: range.from, to: range.to })
      .then(r => { if (alive) { setDays((r.days || []).slice().reverse()); setLD(false); } })
      .catch(() => { if (alive) { setDays([]); setLD(false); } });
    return () => { alive = false; };
  }, [sel, range, tick]);

  function setCustom(field, val) {
    setRange(r => ({ ...r, key: "custom", [field]: val }));
  }

  const STATUS_META = {
    in: ["Present", "#21a366"], late: ["Late", "#fdab3d"], remote: ["Remote", "#0073ea"],
    out: ["Worked", "#5b6172"], absent: ["Absent", "#c93636"], leave: ["Leave", "#a25ddc"],
    holiday: ["Holiday", "#e8862a"],
  };

  if (err) return <div className="pp-card" style={{ padding: 24, color: "var(--prio-critical,#c93636)" }}>{err}</div>;

  const ql = q.trim().toLowerCase();
  const filtered = (members || []).filter(m => !ql || (m.name || "").toLowerCase().includes(ql) || (m.email || "").toLowerCase().includes(ql));

  // ── Company "today" snapshot ────────────────────────────────
  // Derived from /people/team. Counts statuses (in/late/remote count
  // as Present), sums worked hours, picks out currently-working
  // (checked in but not out) and short-day stragglers (< 8h done so
  // far). Refreshes whenever the team payload comes back.
  const todaySum = React.useMemo(() => {
    if (!teamToday) return null;
    const s = {
      total: teamToday.length,
      present: 0, late: 0, leave: 0, holiday: 0, absent: 0,
      working: 0, doneShort: 0, doneFull: 0, hoursTotal: 0,
    };
    for (const p of teamToday) {
      const t = p.today || {};
      const st = t.status;
      // The backend's resolveDay returns status="out" for BOTH:
      //   (a) the person punched in, then out (real Present)
      //   (b) the person never punched in (no in/out timestamps)
      // Case (b) is really an Absent, but the status string doesn't
      // distinguish it. We use the presence of `t.in` to tell them
      // apart so the counts add up: every employee lands in exactly
      // one of Present / Late / Absent / On leave / Holiday.
      const hasIn = !!t.in;
      if (st === "leave" || st === "sick")       s.leave++;
      else if (st === "holiday")                 s.holiday++;
      else if (st === "absent")                  s.absent++;
      else if (st === "in" || st === "remote")   s.present++;
      else if (st === "late")                  { s.present++; s.late++; }
      else if (st === "out" && hasIn)            s.present++;
      else                                       s.absent++; // never punched in
      if (t.hoursLogged) s.hoursTotal += Number(t.hoursLogged);
      const isWorked = hasIn && (st === "in" || st === "out" || st === "remote" || st === "late");
      if (isWorked && !t.out) s.working++;
      if (isWorked && t.out) {
        if ((Number(t.hoursLogged) || 0) < 8) s.doneShort++;
        else s.doneFull++;
      }
    }
    return s;
  }, [teamToday]);

  const todayLabel = React.useMemo(() => {
    const d = new Date();
    return d.toLocaleDateString(undefined, { weekday: "long", day: "numeric", month: "long" });
  }, []);

  return (
    <>
      {/* Company "today" summary — shows above the Timesheets card so
          an admin can see the workforce status at a glance before
          drilling into any one person. */}
      {todaySum && (
        <div className="pp-card pp-co-today">
          <div className="pp-card-head">
            <div>
              <div className="pp-card-title">Company today</div>
              <div className="pp-card-sub">{todayLabel} · {todaySum.total} tracked employee{todaySum.total === 1 ? "" : "s"}</div>
            </div>
            <div className="pp-co-hours" title="Hours logged across the team today">
              <div className="pp-co-hours-v">{ppFmtDur(Math.round(todaySum.hoursTotal * 60))}</div>
              <div className="pp-co-hours-k">TEAM HOURS</div>
            </div>
          </div>
          <div className="pp-co-stats">
            <div className="pp-co-stat pp-co-stat-ok">
              <div className="pp-co-stat-v">{todaySum.present}</div>
              <div className="pp-co-stat-k">Present</div>
            </div>
            <div className="pp-co-stat pp-co-stat-live" title="Checked in, not yet checked out">
              <div className="pp-co-stat-v">{todaySum.working}</div>
              <div className="pp-co-stat-k">Working now</div>
            </div>
            <div className="pp-co-stat pp-co-stat-warn">
              <div className="pp-co-stat-v">{todaySum.late}</div>
              <div className="pp-co-stat-k">Late</div>
            </div>
            <div className="pp-co-stat pp-co-stat-short" title="Checked out before 8 hours">
              <div className="pp-co-stat-v">{todaySum.doneShort}</div>
              <div className="pp-co-stat-k">{"< 8h out"}</div>
            </div>
            <div className="pp-co-stat pp-co-stat-bad">
              <div className="pp-co-stat-v">{todaySum.absent}</div>
              <div className="pp-co-stat-k">Absent</div>
            </div>
            <div className="pp-co-stat pp-co-stat-leave">
              <div className="pp-co-stat-v">{todaySum.leave}</div>
              <div className="pp-co-stat-k">On leave</div>
            </div>
            <div className="pp-co-stat pp-co-stat-hol">
              <div className="pp-co-stat-v">{todaySum.holiday}</div>
              <div className="pp-co-stat-k">Holiday</div>
            </div>
          </div>
          <style>{`
            .pp-co-today { margin-bottom: 12px; }
            .pp-co-today .pp-card-head { align-items: center; }
            .pp-co-hours { margin-left: auto; text-align: right; padding: 4px 14px; border-radius: 9px;
              background: linear-gradient(135deg, #eef5ff, #f8fbff); border: 1px solid #cfe1f9; }
            .pp-co-hours-v { font-size: 18px; font-weight: 800; color: #0b5cad; font-variant-numeric: tabular-nums; line-height: 1.1; }
            .pp-co-hours-k { font-size: 9.5px; letter-spacing: .08em; color: #5b6172; font-weight: 700; margin-top: 1px; }
            .pp-co-stats { display: grid; grid-template-columns: repeat(7, minmax(0,1fr));
              border-top: 1px solid var(--border-row, #eef0f4); }
            .pp-co-stat { padding: 11px 14px; border-right: 1px solid var(--border-row, #eef0f4);
              display: flex; flex-direction: column; gap: 2px; }
            .pp-co-stat:last-child { border-right: none; }
            .pp-co-stat-v { font-size: 18px; font-weight: 800; line-height: 1.1;
              font-variant-numeric: tabular-nums; color: var(--ink-strong, #0f1729); }
            .pp-co-stat-k { font-size: 10.5px; letter-spacing: .04em; color: var(--ink-muted);
              text-transform: uppercase; font-weight: 700; }
            .pp-co-stat-ok    .pp-co-stat-v { color: #1a8a55; }
            .pp-co-stat-live  .pp-co-stat-v { color: #d97800; }
            .pp-co-stat-warn  .pp-co-stat-v { color: #c47d1a; }
            .pp-co-stat-short .pp-co-stat-v { color: #d97800; }
            .pp-co-stat-bad   .pp-co-stat-v { color: #c5354b; }
            .pp-co-stat-leave .pp-co-stat-v { color: #7a3acc; }
            .pp-co-stat-hol   .pp-co-stat-v { color: #a8740f; }
            @media (max-width: 900px) {
              .pp-co-stats { grid-template-columns: repeat(4, minmax(0,1fr)); }
              .pp-co-stat:nth-child(4n) { border-right: none; }
            }
          `}</style>
        </div>
      )}

    <div className="pp-card">
      <div className="pp-card-head">
        <div>
          <div className="pp-card-title">Timesheets</div>
          <div className="pp-card-sub">Pick an employee, choose a date range, then edit any day’s punches</div>
        </div>
      </div>

      <div className="pp-ts-wrap">
        {/* Employee picker */}
        <div className="pp-ts-people">
          <input className="pp-input" placeholder="Search employee…" value={q} onChange={e => setQ(e.target.value)} style={{ margin: "8px", width: "calc(100% - 16px)" }}/>
          {members == null ? (
            <div style={{ padding: 16, color: "var(--ink-muted)" }}>Loading…</div>
          ) : filtered.length === 0 ? (
            <div style={{ padding: 16, color: "var(--ink-muted)" }}>No tracked employees.</div>
          ) : filtered.map(m => {
            const t = todayMap[m.id] || {};
            let todayLine = null;
            if (t.in && t.out) {
              todayLine = (
                <span className="pp-ts-today">
                  <span className="pp-ts-in">In {t.in}</span>
                  <span className="pp-ts-sep">·</span>
                  <span className="pp-ts-out">Out {t.out}</span>
                </span>);
            } else if (t.in) {
              todayLine = (
                <span className="pp-ts-today">
                  <span className="pp-ts-in">In {t.in}</span>
                  <span className="pp-ts-sep">·</span>
                  <span className="pp-ts-live">still working</span>
                </span>);
            } else if (t.status === "leave" || t.status === "sick") {
              todayLine = <span className="pp-ts-today pp-ts-leave">On leave{t.leaveType ? " · " + t.leaveType : ""}</span>;
            } else if (t.status === "holiday") {
              todayLine = <span className="pp-ts-today pp-ts-holi">Holiday</span>;
            } else if (t.status === "absent") {
              todayLine = <span className="pp-ts-today pp-ts-abs">Absent</span>;
            } else if (t.status) {
              todayLine = <span className="pp-ts-today pp-ts-off">Not checked in</span>;
            }
            return (
              <div key={m.id} className={"pp-ts-person" + (sel && sel.id === m.id ? " is-on" : "")}
                   onClick={() => { setSel(m); setDays(null); }}>
                <PpAvatar person={{ name: m.name, color: m.color, avatar: m.avatar }} size="sm"/>
                <div style={{ minWidth: 0 }}>
                  <div className="pp-ts-person-name">{m.name}</div>
                  <div className="pp-ts-person-sub">{m.employee_code ? m.employee_code + " · " : ""}{m.email || "—"}</div>
                  {todayLine && <div className="pp-ts-person-today">{todayLine}</div>}
                </div>
              </div>
            );
          })}
        </div>

        {/* Day list */}
        <div className="pp-ts-days">
          {/* Date filter */}
          <div className="pp-ts-filter">
            <div className="pp-ts-filter-row">
              <select className="pp-ts-preset-sel"
                      value={range.key}
                      onChange={e => setRange(ppPresetRange(e.target.value))}
                      aria-label="Date preset">
                {PP_RANGE_PRESETS.map(p => (
                  <option key={p.key} value={p.key}>{p.label}</option>
                ))}
                <option value="custom">Custom</option>
              </select>
              <div className="pp-ts-custom">
                <input type="date" value={range.from} max={range.to} onChange={e => setCustom("from", e.target.value)} aria-label="From date"/>
                <span style={{ color: "var(--ink-muted)" }}>→</span>
                <input type="date" value={range.to} min={range.from} onChange={e => setCustom("to", e.target.value)} aria-label="To date"/>
              </div>
            </div>
            <div className="pp-ts-presets">
              {PP_RANGE_PRESETS.slice(0, 9).map(p => (
                <button key={p.key}
                        className={"pp-ts-chip" + (range.key === p.key ? " is-on" : "")}
                        onClick={() => setRange(ppPresetRange(p.key))}>
                  {p.label}
                </button>
              ))}
            </div>
          </div>

          {!sel ? (
            <div style={{ padding: 40, textAlign: "center", color: "var(--ink-muted)" }}>
              Select an employee to see their attendance timeline.
            </div>
          ) : loadingDays || days == null ? (
            <div style={{ padding: 40, textAlign: "center", color: "var(--ink-muted)" }}>Loading days…</div>
          ) : days.length === 0 ? (
            <div style={{ padding: 40, textAlign: "center", color: "var(--ink-muted)" }}>No days in this range.</div>
          ) : (
            <>
              {(() => {
                const sm = ppRangeSummary(days);
                const fmtRange = (() => {
                  const [fy, fm, fd] = range.from.split("-").map(Number);
                  const [ty, tm, td] = range.to.split("-").map(Number);
                  const f = new Date(fy, fm - 1, fd), t = new Date(ty, tm - 1, td);
                  const opt = { day: "numeric", month: "short" };
                  return f.toLocaleDateString(undefined, opt) + " – " + t.toLocaleDateString(undefined, { ...opt, year: "numeric" });
                })();
                return (
                  <div className="pp-ts-summary">
                    <div className="pp-ts-summary-head">
                      <span className="pp-ts-summary-name">{sel.name}</span>
                      <span className="pp-ts-summary-range">{fmtRange} · {sm.days} day{sm.days === 1 ? "" : "s"}</span>
                    </div>
                    <div className="pp-ts-summary-stats">
                      <div className="pp-ts-stat pp-ts-stat-ok">
                        <div className="pp-ts-stat-v">{sm.present}</div>
                        <div className="pp-ts-stat-k">Present</div>
                      </div>
                      <div className="pp-ts-stat pp-ts-stat-warn">
                        <div className="pp-ts-stat-v">{sm.late}</div>
                        <div className="pp-ts-stat-k">Late{sm.lateMins ? ` · ${ppFmtDur(sm.lateMins)}` : ""}</div>
                      </div>
                      <div className="pp-ts-stat pp-ts-stat-short" title="Worked days that logged less than 8 hours">
                        <div className="pp-ts-stat-v">{sm.shortDays}</div>
                        <div className="pp-ts-stat-k">{"< 8h days"}</div>
                      </div>
                      <div className="pp-ts-stat pp-ts-stat-bad">
                        <div className="pp-ts-stat-v">{sm.absent}</div>
                        <div className="pp-ts-stat-k">Absent</div>
                      </div>
                      <div className="pp-ts-stat pp-ts-stat-leave">
                        <div className="pp-ts-stat-v">{sm.leave}</div>
                        <div className="pp-ts-stat-k">On leave</div>
                      </div>
                      <div className="pp-ts-stat pp-ts-stat-hol">
                        <div className="pp-ts-stat-v">{sm.holiday}</div>
                        <div className="pp-ts-stat-k">Holiday</div>
                      </div>
                    </div>
                  </div>
                );
              })()}
              <div className="pp-ts-days-head">Days</div>
              {days.map(d => {
                const [lbl, col] = STATUS_META[d.status] || [d.status || "—", "#5b6172"];
                const dd = (() => { const [y, mo, da] = d.iso.split("-").map(Number); return new Date(y, mo - 1, da); })();
                // Date format "1 June 2026, Sun"
                const dlabel = (() => {
                  const main = dd.toLocaleDateString(undefined, { day: "numeric", month: "long", year: "numeric" });
                  const wd   = dd.toLocaleDateString(undefined, { weekday: "short" });
                  return `${main}, ${wd}`;
                })();
                const segs = d.segments || [];
                const inOut = d.in ? (d.in + (d.out ? " – " + d.out : " – …")) : "—";
                return (
                  <div key={d.iso} className="pp-ts-day">
                    <div className="pp-ts-day-date">{dlabel}</div>
                    <span className="pp-ts-day-pill" style={{ background: col + "1a", color: col }}>{lbl}</span>
                    <div className="pp-ts-day-io">
                      {inOut}
                      {d.minsLate ? <span className="pp-ts-day-late"> · {d.minsLate}m late</span> : null}
                    </div>
                    {/* Shared graphical timeline (dashed baseline + line + dots).
                        Leave/holiday days show a labeled band instead of an empty line. */}
                    <div title={inOut !== "—" ? inOut : (d.status === "leave" ? (d.leaveType || "Leave") : "No punches")}>
                      <PpTimeline segments={segs} band={ppDayBand(d)}/>
                    </div>
                    <span className="pp-ts-day-hrs">{d.hoursLogged ? ppFmtDur(Math.round(d.hoursLogged * 60)) : "—"}</span>
                    <button className="pp-btn" onClick={() => setEditDate(d.iso)}>Edit</button>
                  </div>
                );
              })}
            </>
          )}
        </div>
      </div>

      {editDate && sel && (
        <PpDayEditor person={sel} date={editDate}
                     onClose={() => { setEditDate(null); setTick(t => t + 1); }}/>
      )}

      <style>{`
        .pp-ts-wrap { display:grid; grid-template-columns:260px 1fr; gap:0; }
        .pp-ts-people { border-right:1px solid var(--border-row,#eaecf1); max-height:600px; overflow:auto; }
        .pp-ts-person { display:flex; gap:10px; align-items:center; padding:9px 12px; cursor:pointer; border-bottom:1px solid var(--border-row,#f1f2f5); }
        .pp-ts-person:hover { background:var(--bg-subtle,#f6f8fc); }
        .pp-ts-person.is-on { background:var(--bg-subtle,#eef4ff); box-shadow:inset 3px 0 0 var(--brand,#0073ea); }
        .pp-ts-person-name { font-size:13px; font-weight:600; color:var(--ink-strong); white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
        .pp-ts-person-sub { font-size:11px; color:var(--ink-muted); white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
        .pp-ts-person-today { font-size:11px; margin-top:2px; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; font-variant-numeric:tabular-nums; }
        .pp-ts-today { display:inline-flex; align-items:center; gap:6px; font-weight:600; }
        .pp-ts-in { color:#1a8a55; }
        .pp-ts-out { color:#c5354b; }
        .pp-ts-live { color:#d97800; font-weight:700; }
        .pp-ts-sep { color:var(--ink-faint,#9aa0ad); font-weight:400; }
        .pp-ts-leave { color:#5a2891; font-weight:600; }
        .pp-ts-holi { color:#a8740f; font-weight:600; }
        .pp-ts-abs { color:#c5354b; font-weight:600; }
        .pp-ts-off { color:var(--ink-muted,#8a90a0); }
        .pp-ts-days { max-height:600px; overflow:auto; }
        .pp-ts-filter { display:flex; flex-direction:column; gap:8px; padding:10px 16px; border-bottom:1px solid var(--border-row,#eaecf1); position:sticky; top:0; background:var(--bg-card,#fff); z-index:1; }
        .pp-ts-filter-row { display:flex; gap:10px; align-items:center; flex-wrap:wrap; }
        .pp-ts-preset-sel { padding:6px 10px; border:1px solid var(--border,#d8dce4); border-radius:7px; font-size:12.5px; background:var(--bg-card,#fff); font-weight:600; color:var(--ink-strong); cursor:pointer; min-width:140px; }
        .pp-ts-preset-sel:focus { outline:none; border-color:var(--brand,#0073ea); }
        .pp-ts-presets { display:flex; flex-wrap:wrap; gap:6px; }
        .pp-ts-chip { font-size:12px; padding:4px 11px; border-radius:99px; border:1px solid var(--border,#e6e9ef); background:var(--bg-subtle,#f3f4f7); color:var(--ink-muted); cursor:pointer; }
        .pp-ts-chip:hover { border-color:var(--brand,#0073ea); }
        .pp-ts-chip.is-on { background:var(--brand,#0073ea); border-color:var(--brand,#0073ea); color:#fff; font-weight:600; }
        .pp-ts-custom { display:flex; gap:6px; align-items:center; margin-left:auto; }
        .pp-ts-custom input { padding:5px 8px; border:1px solid var(--border,#d8dce4); border-radius:7px; font-size:12.5px; background:var(--bg-card,#fff); }
        /* ── Range summary card (shown above the day list) ──── */
        .pp-ts-summary { margin:12px 16px 4px; border:1px solid var(--border-row,#eaecf1); border-radius:10px; background:linear-gradient(180deg, #fbfcfe 0%, #f6f8fc 100%); overflow:hidden; }
        .pp-ts-summary-head { display:flex; justify-content:space-between; align-items:baseline; gap:10px; padding:10px 14px 6px; border-bottom:1px dashed var(--border-row,#e6e9ef); flex-wrap:wrap; }
        .pp-ts-summary-name { font-size:13px; font-weight:700; color:var(--ink-strong,#0f1729); }
        .pp-ts-summary-range { font-size:11.5px; color:var(--ink-muted); font-variant-numeric:tabular-nums; }
        .pp-ts-summary-stats { display:grid; grid-template-columns:repeat(6, minmax(0,1fr)); gap:0; }
        .pp-ts-stat { padding:10px 12px; border-right:1px solid var(--border-row,#eef0f4); display:flex; flex-direction:column; gap:2px; }
        .pp-ts-stat:last-child { border-right:none; }
        .pp-ts-stat-v { font-size:15px; font-weight:700; color:var(--ink-strong,#0f1729); font-variant-numeric:tabular-nums; line-height:1.15; }
        .pp-ts-stat-k { font-size:10.5px; color:var(--ink-muted); text-transform:uppercase; letter-spacing:.04em; font-weight:600; }
        .pp-ts-stat-worked .pp-ts-stat-v { color:#0b6ed1; }
        .pp-ts-stat-ok    .pp-ts-stat-v { color:#1a8a55; }
        .pp-ts-stat-warn  .pp-ts-stat-v { color:#c47d1a; }
        .pp-ts-stat-short .pp-ts-stat-v { color:#d97800; }
        .pp-ts-stat-bad   .pp-ts-stat-v { color:#c5354b; }
        .pp-ts-stat-leave .pp-ts-stat-v { color:#7a3acc; }
        .pp-ts-stat-hol   .pp-ts-stat-v { color:#a8740f; }
        @media (max-width: 900px) {
          .pp-ts-summary-stats { grid-template-columns:repeat(4, minmax(0,1fr)); }
          .pp-ts-stat:nth-child(4n) { border-right:none; }
        }
        .pp-ts-days-head { padding:9px 16px; font-size:12px; font-weight:700; color:var(--ink-muted); text-transform:uppercase; letter-spacing:.05em; border-bottom:1px solid var(--border-row,#eaecf1); }
        .pp-ts-day { display:grid; grid-template-columns:160px 78px 116px 1fr 58px auto; gap:10px; align-items:center; padding:9px 16px; border-bottom:1px solid var(--border-row,#f1f2f5); }
        .pp-ts-day-date { font-size:13px; font-weight:600; color:var(--ink-strong); }
        .pp-ts-day-pill { font-size:11px; font-weight:700; padding:2px 8px; border-radius:99px; text-align:center; }
        .pp-ts-day-io { font-size:12px; color:var(--ink); white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
        .pp-ts-day-late { color:#c47d1a; font-weight:600; }
        /* day timeline rendered by the shared <PpTimeline> component */
        .pp-ts-day-hrs { font-size:12.5px; color:var(--ink); text-align:right; }
        .pp-ts-day button { justify-self:end; }
        @media (max-width: 720px) {
          .pp-ts-wrap { grid-template-columns:1fr; }
          .pp-ts-people { max-height:200px; border-right:none; border-bottom:1px solid var(--border-row,#eaecf1); }
          .pp-ts-day { grid-template-columns:90px 70px 1fr auto; }
          .pp-ts-day-io, .pp-ts-day-hrs { display:none; }
        }
      `}</style>
    </div>
    </>
  );
}

// ── Day editor side panel ──────────────────────────────────────
function PpDayEditor({ person, date, onClose }) {
  const [punches, setPunches] = React.useState(null);
  const [err, setErr]   = React.useState("");
  const [busy, setBusy] = React.useState(false);
  const [adding, setAdding] = React.useState(false);
  const [editId, setEditId] = React.useState(null);

  const reload = React.useCallback(async () => {
    setErr("");
    try { const r = await window.api.people.punches({ userId: person.id, date }); setPunches(r.punches || []); }
    catch (e) { setErr((e && e.body && (e.body.message || e.body.error)) || "Could not load punches"); setPunches([]); }
  }, [person.id, date]);
  React.useEffect(() => { reload(); }, [reload]);

  const dlabel = (() => { const [y, m, d] = date.split("-").map(Number); return new Date(y, m - 1, d).toLocaleDateString(undefined, { weekday: "long", day: "numeric", month: "long", year: "numeric" }); })();

  async function saveNew(f) {
    setBusy(true);
    try {
      const { source, note } = ppResolveSource(f.srcKey, f.note);
      await window.api.people.addPunch({ userId: person.id, date, time: f.time, kind: f.kind, source, note });
      setAdding(false); await reload();
    } catch (e) { if (window.fbToast) window.fbToast((e && e.body && e.body.message) || "Could not add punch", 3500); }
    finally { setBusy(false); }
  }
  async function saveEdit(id, f) {
    setBusy(true);
    try {
      const { source, note } = ppResolveSource(f.srcKey, f.note);
      await window.api.people.editPunch(id, { date, time: f.time, kind: f.kind, source, note });
      setEditId(null); await reload();
    } catch (e) { if (window.fbToast) window.fbToast((e && e.body && e.body.message) || "Could not save", 3500); }
    finally { setBusy(false); }
  }
  async function del(id) {
    if (!window.confirm("Delete this punch? This can’t be undone.")) return;
    setBusy(true);
    try { await window.api.people.deletePunch(id); await reload(); }
    catch (e) { if (window.fbToast) window.fbToast((e && e.body && e.body.message) || "Could not delete", 3500); }
    finally { setBusy(false); }
  }

  return (
    <div className="modal-backdrop" onClick={onClose}>
      <div className="pp-pe" onClick={ev => ev.stopPropagation()}>
        <div className="pp-modal-head">
          <div style={{ display: "flex", alignItems: "center", gap: 10 }}>
            <PpAvatar person={person} size="md"/>
            <div>
              <div className="pp-modal-title" style={{ marginBottom: 0 }}>{person.name}</div>
              <div style={{ fontSize: 12, color: "var(--ink-muted)" }}>{dlabel}</div>
            </div>
          </div>
          <button onClick={onClose} className="pp-modal-close">×</button>
        </div>

        <div className="pp-pe-body">
          {err && <div style={{ color: "var(--prio-critical,#c93636)", marginBottom: 10 }}>{err}</div>}
          {punches == null ? (
            <div style={{ padding: 24, color: "var(--ink-muted)" }}>Loading…</div>
          ) : (
            <>
              {punches.length === 0 && !adding && (
                <div style={{ padding: 18, textAlign: "center", color: "var(--ink-muted)", background: "var(--bg-subtle,#f6f8fc)", borderRadius: 8, marginBottom: 12 }}>
                  No punches on this day. Add one below.
                </div>
              )}
              {punches.map(p => (
                editId === p.id ? (
                  <PpPunchForm key={p.id} busy={busy}
                               init={{ kind: p.kind, time: p.time, srcKey: (PP_SOURCE_OPTS.find(o => o.source === p.source) || {}).key || "manual", note: p.note }}
                               onSave={(f) => saveEdit(p.id, f)} onCancel={() => setEditId(null)}/>
                ) : (
                  <div key={p.id} className="pp-pe-row">
                    <span className={"pp-pe-dot " + (p.kind.startsWith("break") ? "is-break" : (p.kind === "out" ? "is-out" : "is-in"))}/>
                    <div style={{ minWidth: 0, flex: 1 }}>
                      <div className="pp-pe-row-top">
                        <span className="pp-pe-time">{p.time}</span>
                        <span className="pp-pe-kind">{ppKindLabel(p.kind)}</span>
                        <span className="pp-pe-src">{p.source}</span>
                      </div>
                      {(p.note || p.edited_at) && (
                        <div className="pp-pe-row-sub">
                          {p.note ? p.note : ""}{p.note && p.edited_at ? " · " : ""}{p.edited_at ? "edited" : ""}
                        </div>
                      )}
                    </div>
                    <button className="pp-btn" onClick={() => { setAdding(false); setEditId(p.id); }} title="Edit">Edit</button>
                    <button className="pp-btn pp-btn-danger" onClick={() => del(p.id)} title="Delete">✕</button>
                  </div>
                )
              ))}

              {adding ? (
                <PpPunchForm busy={busy}
                             init={{ kind: "in", time: "09:00", srcKey: "manual", note: "" }}
                             onSave={saveNew} onCancel={() => setAdding(false)}/>
              ) : (
                <button className="pp-btn pp-btn-primary" style={{ marginTop: 10 }}
                        onClick={() => { setEditId(null); setAdding(true); }}>
                  <PpIcon name="Plus"/> Add punch
                </button>
              )}
            </>
          )}
        </div>
      </div>

      <style>{`
        .pp-pe { margin-left:auto; height:100%; width:min(440px,94vw); background:var(--bg-card,#fff); display:flex; flex-direction:column; box-shadow:-8px 0 30px rgba(0,0,0,.18); }
        .pp-pe-body { padding:16px; overflow:auto; flex:1; }
        .pp-pe-row { display:flex; align-items:center; gap:10px; padding:10px 0; border-bottom:1px solid var(--border-row,#f1f2f5); }
        .pp-pe-dot { width:10px; height:10px; border-radius:99px; flex:none; }
        .pp-pe-dot.is-in { background:#21a366; } .pp-pe-dot.is-out { background:#5b6172; } .pp-pe-dot.is-break { background:#fdab3d; }
        .pp-pe-row-top { display:flex; align-items:baseline; gap:8px; }
        .pp-pe-time { font-size:15px; font-weight:700; color:var(--ink-strong); }
        .pp-pe-kind { font-size:13px; color:var(--ink); }
        .pp-pe-src { font-size:10.5px; text-transform:uppercase; letter-spacing:.04em; color:var(--ink-muted); background:var(--bg-subtle,#f0f2f6); padding:1px 6px; border-radius:4px; }
        .pp-pe-row-sub { font-size:11.5px; color:var(--ink-muted); margin-top:2px; }
        .pp-pe-form { background:var(--bg-subtle,#f6f8fc); border:1px solid var(--border,#e6e9ef); border-radius:10px; padding:12px; margin:8px 0; display:flex; flex-direction:column; gap:8px; }
        .pp-pe-form select, .pp-pe-form input { width:100%; padding:7px 9px; border:1px solid var(--border,#d8dce4); border-radius:7px; font-size:13px; background:var(--bg-card,#fff); }
        .pp-pe-form-row { display:grid; grid-template-columns:1fr 1fr; gap:8px; }
        .pp-pe-form-foot { display:flex; gap:8px; justify-content:flex-end; }
        .pp-btn-danger { color:var(--prio-critical,#c93636); }
      `}</style>
    </div>
  );
}

// Shared add/edit form for a single punch.
function PpPunchForm({ init, onSave, onCancel, busy }) {
  const [kind, setKind]     = React.useState(init.kind || "in");
  const [time, setTime]     = React.useState(init.time || "09:00");
  const [srcKey, setSrcKey] = React.useState(init.srcKey || "manual");
  const [note, setNote]     = React.useState(init.note || "");
  return (
    <div className="pp-pe-form">
      <div className="pp-pe-form-row">
        <select value={kind} onChange={e => setKind(e.target.value)} aria-label="Punch type">
          {PP_KIND_OPTS.map(o => <option key={o.id} value={o.id}>{o.label}</option>)}
        </select>
        <input type="time" value={time} onChange={e => setTime(e.target.value)} aria-label="Time"/>
      </div>
      <select value={srcKey} onChange={e => setSrcKey(e.target.value)} aria-label="Source">
        {PP_SOURCE_OPTS.map(o => <option key={o.key} value={o.key}>{o.label}</option>)}
      </select>
      <input type="text" placeholder="Reason / note (optional)" value={note} onChange={e => setNote(e.target.value)}/>
      <div className="pp-pe-form-foot">
        <button className="pp-btn" onClick={onCancel} disabled={busy}>Cancel</button>
        <button className="pp-btn pp-btn-primary" disabled={busy || !time} onClick={() => onSave({ kind, time, srcKey, note })}>Save</button>
      </div>
    </div>
  );
}

// ── Calendar screen ───────────────────────────────────────────
// Month view that overlays the company holidays + everyone's
// approved leave for that month. The cells are coloured: holiday →
// orange wash, anyone-on-leave → purple chip count.
function PpCalendarScreen() {
  const [cursor, setCursor]   = React.useState(() => {
    const d = new Date(); return new Date(d.getFullYear(), d.getMonth(), 1);
  });
  const [holidays, setHolidays] = React.useState([]);
  const [leaves, setLeaves]     = React.useState([]);
  React.useEffect(() => {
    (async () => {
      const first = new Date(cursor);
      const last  = new Date(cursor.getFullYear(), cursor.getMonth() + 1, 0);
      const fIso = first.toISOString().slice(0,10);
      const lIso = last.toISOString().slice(0,10);
      try {
        const hr = await window.api.people.holidays({ from: fIso, to: lIso });
        setHolidays(hr.items || []);
      } catch { setHolidays([]); }
      try {
        const lr = await window.api.people.listLeaveRequests({ status: "approved" });
        setLeaves((lr.items || []).filter(it =>
          it.from_date <= lIso && it.to_date >= fIso
        ));
      } catch { setLeaves([]); }
    })();
  }, [cursor]);

  const monthLabel = cursor.toLocaleDateString("en-US", { month: "long", year: "numeric" });
  const daysInMonth = new Date(cursor.getFullYear(), cursor.getMonth() + 1, 0).getDate();
  const startWeekday = new Date(cursor.getFullYear(), cursor.getMonth(), 1).getDay();
  const cells = [];
  for (let i = 0; i < startWeekday; i++) cells.push(null);
  for (let d = 1; d <= daysInMonth; d++) cells.push(d);
  while (cells.length % 7) cells.push(null);

  function iso(day) {
    return cursor.getFullYear() + "-" +
           String(cursor.getMonth() + 1).padStart(2, "0") + "-" +
           String(day).padStart(2, "0");
  }
  function holidaysFor(day) {
    const s = iso(day);
    return holidays.filter(h => String(h.date).slice(0,10) === s);
  }
  function leavesFor(day) {
    const s = iso(day);
    return leaves.filter(lv => lv.from_date <= s && lv.to_date >= s);
  }
  function go(delta) {
    setCursor(new Date(cursor.getFullYear(), cursor.getMonth() + delta, 1));
  }

  return (
    <div className="pp-card">
      <div className="pp-card-head">
        <div>
          <div className="pp-card-title">Team calendar — {monthLabel}</div>
          <div className="pp-card-sub">Holidays + approved leave overlaid</div>
        </div>
        <div style={{ marginLeft: "auto", display: "flex", gap: 6 }}>
          <button className="pp-btn" onClick={() => go(-1)}><PpIcon name="ChevronL"/></button>
          <button className="pp-btn" onClick={() => setCursor(new Date(new Date().getFullYear(), new Date().getMonth(), 1))}>Today</button>
          <button className="pp-btn" onClick={() => go(1)}><PpIcon name="ChevronR"/></button>
        </div>
      </div>
      <div style={{ padding: 12 }}>
        <div style={{ display: "grid", gridTemplateColumns: "repeat(7, 1fr)", gap: 4, fontSize: 11, fontWeight: 700,
                      color: "var(--ink-muted)", padding: "0 0 6px" }}>
          {["Sun","Mon","Tue","Wed","Thu","Fri","Sat"].map(d => <div key={d} style={{ textAlign: "center" }}>{d}</div>)}
        </div>
        <div style={{ display: "grid", gridTemplateColumns: "repeat(7, 1fr)", gap: 4 }}>
          {cells.map((d, i) => {
            if (d == null) return <div key={i} style={{ aspectRatio: "1.2 / 1", minHeight: 64 }}/>;
            const hols = holidaysFor(d);
            const lvs  = leavesFor(d);
            const isWeekend = (i % 7 === 0 || i % 7 === 6);
            const isToday = (() => {
              const n = new Date();
              return n.getFullYear() === cursor.getFullYear()
                  && n.getMonth() === cursor.getMonth()
                  && n.getDate() === d;
            })();
            return (
              <div key={i} style={{
                border: "1px solid var(--border-row, #eaecf1)",
                borderRadius: 8,
                padding: 6,
                minHeight: 64,
                background: hols.length ? "#fff3e0" : isWeekend ? "#fafbfd" : "#fff",
                outline: isToday ? "2px solid #0073ea" : "none",
                outlineOffset: -2,
                fontSize: 12,
              }}>
                <div style={{ fontWeight: 700, color: "var(--ink-strong)", marginBottom: 4 }}>{d}</div>
                {hols.map(h => (
                  <div key={h.id} style={{ fontSize: 11, color: "#8c4a00", fontWeight: 600 }} title={h.label}>
                    🎉 {h.label}
                  </div>
                ))}
                {lvs.length > 0 && (
                  <div style={{ marginTop: 2, fontSize: 11, color: "#5a2891", fontWeight: 600 }}>
                    {lvs.length} on leave
                  </div>
                )}
              </div>
            );
          })}
        </div>
      </div>
    </div>
  );
}

// ── Admin: Schedules ──────────────────────────────────────────
function PpSchedulesAdmin() {
  const [items, setItems]     = React.useState([]);
  const [editing, setEditing] = React.useState(null); // schedule row
  const [busy, setBusy]       = React.useState(false);
  const [applyOpen, setApplyOpen] = React.useState(false);

  const reload = async () => {
    try { const r = await window.api.people.schedules(); setItems(r.items || []); }
    catch {}
  };
  React.useEffect(() => { reload(); }, []);

  async function create() {
    if (busy) return; setBusy(true);
    try {
      const r = await window.api.people.createSchedule({ name: "New schedule" });
      await reload();
      const fresh = (items.find(s => s.id === r.id)) || { id: r.id, name: "New schedule" };
      setEditing(fresh);
    } catch (e) {
      if (window.fbToast) window.fbToast("Couldn't create schedule", 3500);
    } finally { setBusy(false); }
  }

  async function setDefault(id) {
    try {
      await window.api.people.updateSchedule(id, { is_default: true });
      if (window.fbToast) window.fbToast("Default schedule updated", 2200);
      await reload();
    } catch (e) {
      if (window.fbToast) window.fbToast("Could not set default", 3500);
    }
  }

  async function applyToAll(force) {
    setApplyOpen(false);
    try {
      const r = await window.api.people.applyScheduleToAll({ force });
      if (window.fbToast) window.fbToast(`Default applied to ${r.applied} employee${r.applied === 1 ? "" : "s"}`, 2500);
    } catch (e) {
      const msg = (e && e.body && (e.body.message || e.body.error)) || "Could not apply";
      if (window.fbToast) window.fbToast(msg, 3500);
    }
  }

  async function remove(id) {
    if (!window.confirm("Delete this schedule? Employees assigned to it must be reassigned first.")) return;
    try {
      await window.api.people.deleteSchedule(id);
      await reload();
    } catch (e) {
      const msg = (e && e.body && (e.body.message || e.body.error)) || "Could not delete";
      if (window.fbToast) window.fbToast(msg, 3500);
    }
  }

  return (
    <div className="pp-card">
      <div className="pp-card-head">
        <div>
          <div className="pp-card-title">Schedules</div>
          <div className="pp-card-sub">
            {items.length} schedule{items.length === 1 ? "" : "s"} · the default applies to new employees
          </div>
        </div>
        <div style={{ marginLeft: "auto", display: "flex", gap: 8 }}>
          <button className="pp-btn" onClick={() => setApplyOpen(true)}
                  disabled={!items.some(s => s.is_default)}
                  title={items.some(s => s.is_default) ? "Apply default to every employee" : "Set a default schedule first"}>
            <PpIcon name="Users"/> Apply default to all
          </button>
          <button className="pp-btn pp-btn-primary" onClick={create} disabled={busy}>
            <PpIcon name="Plus"/> New schedule
          </button>
        </div>
      </div>
      <div style={{ padding: "8px 0" }}>
        {items.map(s => (
          <div key={s.id} style={{ display: "flex", justifyContent: "space-between", padding: "10px 16px",
                                    borderBottom: "1px solid var(--border-row, #eaecf1)", alignItems: "center" }}>
            <div style={{ flex: 1, minWidth: 0 }}>
              <div style={{ fontWeight: 600, color: "var(--ink-strong)", fontSize: 13 }}>
                {s.name}
                {s.is_default ? <span style={{ marginLeft: 8 }}><PpStatusPill status="approved">Default</PpStatusPill></span> : null}
              </div>
              <div style={{ fontSize: 11.5, color: "var(--ink-muted)" }}>
                Mon-Fri {s.mon_start || "—"}–{s.mon_end || "—"} · {Number(s.weekly_hours)}h/wk · grace {s.late_grace_mins}m
              </div>
            </div>
            <div style={{ display: "flex", gap: 6 }}>
              {!s.is_default && (
                <button className="pp-btn" onClick={() => setDefault(s.id)}>Set default</button>
              )}
              <button className="pp-btn" onClick={() => setEditing(s)}>Edit</button>
              {!s.is_default && (
                <button className="pp-btn" onClick={() => remove(s.id)} title="Delete">Delete</button>
              )}
            </div>
          </div>
        ))}
        {items.length === 0 && (
          <div style={{ padding: 32, textAlign: "center", color: "var(--ink-muted)" }}>
            No schedules yet. Click <b>New schedule</b> to add one, then mark it as default.
          </div>
        )}
      </div>
      {editing && (
        <PpScheduleEditor row={editing} onClose={() => { setEditing(null); reload(); }}/>
      )}
      {applyOpen && (
        <PpApplyDefaultModal
          onForce={() => applyToAll(true)}
          onSafe={() => applyToAll(false)}
          onClose={() => setApplyOpen(false)}/>
      )}
    </div>
  );
}

// Schedule editor — per-day start/end, grace, weekly hours.
function PpScheduleEditor({ row, onClose }) {
  const [name, setName]   = React.useState(row.name || "");
  const [mon_start, setMonS] = React.useState(row.mon_start || "09:00");
  const [mon_end,   setMonE] = React.useState(row.mon_end   || "18:00");
  const [tue_start, setTueS] = React.useState(row.tue_start || "09:00");
  const [tue_end,   setTueE] = React.useState(row.tue_end   || "18:00");
  const [wed_start, setWedS] = React.useState(row.wed_start || "09:00");
  const [wed_end,   setWedE] = React.useState(row.wed_end   || "18:00");
  const [thu_start, setThuS] = React.useState(row.thu_start || "09:00");
  const [thu_end,   setThuE] = React.useState(row.thu_end   || "18:00");
  const [fri_start, setFriS] = React.useState(row.fri_start || "09:00");
  const [fri_end,   setFriE] = React.useState(row.fri_end   || "18:00");
  const [sat_start, setSatS] = React.useState(row.sat_start || "");
  const [sat_end,   setSatE] = React.useState(row.sat_end   || "");
  const [sun_start, setSunS] = React.useState(row.sun_start || "");
  const [sun_end,   setSunE] = React.useState(row.sun_end   || "");
  const [grace, setGrace]    = React.useState(Number(row.late_grace_mins) || 5);
  const [weekly, setWeekly]  = React.useState(Number(row.weekly_hours) || 45);
  const [busy, setBusy]      = React.useState(false);

  async function save() {
    if (busy) return; setBusy(true);
    try {
      await window.api.people.updateSchedule(row.id, {
        name,
        mon_start, mon_end, tue_start, tue_end, wed_start, wed_end,
        thu_start, thu_end, fri_start, fri_end,
        sat_start: sat_start || null, sat_end: sat_end || null,
        sun_start: sun_start || null, sun_end: sun_end || null,
        late_grace_mins: Number(grace) || 0,
        weekly_hours:    Number(weekly) || 0,
      });
      onClose();
    } catch (e) {
      if (window.fbToast) window.fbToast("Could not save schedule", 3500);
    } finally { setBusy(false); }
  }
  function Row({ label, s, e, setS, setE }) {
    return (
      <div style={{ display: "grid", gridTemplateColumns: "70px 1fr 1fr", gap: 8, alignItems: "center", marginBottom: 6 }}>
        <span style={{ fontSize: 12, color: "var(--ink-muted)" }}>{label}</span>
        <input type="time" value={s} onChange={ev => setS(ev.target.value)}/>
        <input type="time" value={e} onChange={ev => setE(ev.target.value)}/>
      </div>
    );
  }
  return ReactDOM.createPortal(
    <div className="modal-backdrop" onClick={onClose}>
      <div className="pp-modal" onClick={ev => ev.stopPropagation()} style={{ maxWidth: 520 }}>
        <div className="pp-modal-head">
          <div className="pp-modal-title">Edit schedule</div>
          <button onClick={onClose} className="pp-modal-close">×</button>
        </div>
        <div className="pp-modal-body">
          <label className="pp-field"><span>Name</span>
            <input value={name} onChange={ev => setName(ev.target.value)}/></label>
          <Row label="Mon" s={mon_start} e={mon_end} setS={setMonS} setE={setMonE}/>
          <Row label="Tue" s={tue_start} e={tue_end} setS={setTueS} setE={setTueE}/>
          <Row label="Wed" s={wed_start} e={wed_end} setS={setWedS} setE={setWedE}/>
          <Row label="Thu" s={thu_start} e={thu_end} setS={setThuS} setE={setThuE}/>
          <Row label="Fri" s={fri_start} e={fri_end} setS={setFriS} setE={setFriE}/>
          <Row label="Sat" s={sat_start} e={sat_end} setS={setSatS} setE={setSatE}/>
          <Row label="Sun" s={sun_start} e={sun_end} setS={setSunS} setE={setSunE}/>
          <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 12, marginTop: 8 }}>
            <label className="pp-field"><span>Late grace (mins)</span>
              <input type="number" value={grace} onChange={ev => setGrace(ev.target.value)}/></label>
            <label className="pp-field"><span>Weekly hours</span>
              <input type="number" value={weekly} onChange={ev => setWeekly(ev.target.value)}/></label>
          </div>
        </div>
        <div className="pp-modal-foot">
          <button className="pp-btn" onClick={onClose}>Cancel</button>
          <button className="pp-btn pp-btn-primary" onClick={save} disabled={busy}>
            {busy ? "Saving…" : "Save"}
          </button>
        </div>
      </div>
    </div>,
    document.body
  );
}

function PpApplyDefaultModal({ onForce, onSafe, onClose }) {
  return ReactDOM.createPortal(
    <div className="modal-backdrop" onClick={onClose}>
      <div className="pp-modal" onClick={ev => ev.stopPropagation()} style={{ maxWidth: 460 }}>
        <div className="pp-modal-head">
          <div className="pp-modal-title">Apply default schedule</div>
          <button onClick={onClose} className="pp-modal-close">×</button>
        </div>
        <div className="pp-modal-body">
          <p style={{ margin: 0, color: "var(--ink-strong)" }}>
            Apply the default schedule to every employee in this workspace?
          </p>
          <ul style={{ fontSize: 12.5, color: "var(--ink-muted)", marginTop: 10, paddingLeft: 18 }}>
            <li><b>Fill blanks only</b> assigns the default to anyone who doesn't yet have a schedule. Existing assignments are kept.</li>
            <li><b>Replace existing</b> overwrites every employee's schedule with the default.</li>
          </ul>
        </div>
        <div className="pp-modal-foot">
          <button className="pp-btn" onClick={onClose}>Cancel</button>
          <button className="pp-btn" onClick={onSafe}>Fill blanks only</button>
          <button className="pp-btn pp-btn-primary" onClick={onForce}>Replace existing</button>
        </div>
      </div>
    </div>,
    document.body
  );
}

// ── Admin: Holidays ───────────────────────────────────────────
function PpHolidaysAdmin() {
  const [items, setItems] = React.useState([]);
  const [openAdd, setOpenAdd] = React.useState(false);
  const reload = async () => {
    try {
      const yr = new Date().getFullYear();
      const r = await window.api.people.holidays({ from: yr + "-01-01", to: (yr + 1) + "-12-31" });
      setItems(r.items || []);
    } catch {}
  };
  React.useEffect(() => { reload(); }, []);
  async function remove(id) {
    if (!window.confirm("Remove this holiday from the team calendar?")) return;
    try { await window.api.people.deleteHoliday(id); await reload(); } catch {}
  }
  return (
    <div className="pp-card">
      <div className="pp-card-head">
        <div>
          <div className="pp-card-title">Holidays</div>
          <div className="pp-card-sub">{items.length} day{items.length === 1 ? "" : "s"} · visible to everyone on the team calendar</div>
        </div>
        <button className="pp-btn pp-btn-primary" style={{ marginLeft: "auto" }} onClick={() => setOpenAdd(true)}>
          <PpIcon name="Plus"/> Add holiday
        </button>
      </div>
      <div style={{ padding: "8px 0" }}>
        {items.map(h => (
          <div key={h.id} style={{ display: "flex", padding: "10px 16px", alignItems: "center",
                                    borderBottom: "1px solid var(--border-row, #eaecf1)" }}>
            <div style={{ width: 56, textAlign: "center", marginRight: 12 }}>
              <div style={{ fontSize: 11, color: "var(--ink-muted)" }}>{new Date(h.date + "T00:00:00").toLocaleDateString("en-US", { month: "short" })}</div>
              <div style={{ fontSize: 18, fontWeight: 700, color: "var(--ink-strong)" }}>
                {new Date(h.date + "T00:00:00").getDate()}
              </div>
            </div>
            <div style={{ flex: 1 }}>
              <div style={{ fontWeight: 600, color: "var(--ink-strong)", fontSize: 13 }}>{h.label}</div>
              <div style={{ fontSize: 11.5, color: "var(--ink-muted)" }}>
                {new Date(h.date + "T00:00:00").toLocaleDateString("en-US", { weekday: "long", year: "numeric" })}
                {h.is_optional ? " · optional" : ""}
              </div>
            </div>
            <button className="pp-btn" onClick={() => remove(h.id)} title="Delete">Delete</button>
          </div>
        ))}
        {items.length === 0 && (
          <div style={{ padding: 32, textAlign: "center", color: "var(--ink-muted)" }}>
            No holidays added yet. Add the company holidays so they show on every team calendar.
          </div>
        )}
      </div>
      {openAdd && (
        <PpHolidayModal onClose={() => { setOpenAdd(false); reload(); }}/>
      )}
    </div>
  );
}

function PpHolidayModal({ onClose }) {
  const [date, setDate] = React.useState(() => new Date().toISOString().slice(0, 10));
  const [label, setLabel] = React.useState("");
  const [optional, setOptional] = React.useState(false);
  const [busy, setBusy] = React.useState(false);
  const [err, setErr]   = React.useState("");
  async function save() {
    if (!date || !label) { setErr("Date + label are required"); return; }
    setBusy(true); setErr("");
    try {
      await window.api.people.createHoliday({ date, label, is_optional: optional });
      onClose();
    } catch (e) {
      setErr((e && e.body && (e.body.message || e.body.error)) || "Could not save");
    } finally { setBusy(false); }
  }
  return ReactDOM.createPortal(
    <div className="modal-backdrop" onClick={onClose}>
      <div className="pp-modal" onClick={ev => ev.stopPropagation()} style={{ maxWidth: 420 }}>
        <div className="pp-modal-head">
          <div className="pp-modal-title">Add holiday</div>
          <button onClick={onClose} className="pp-modal-close">×</button>
        </div>
        <div className="pp-modal-body">
          <label className="pp-field"><span>Date</span>
            <input type="date" value={date} onChange={ev => setDate(ev.target.value)}/></label>
          <label className="pp-field"><span>Label</span>
            <input value={label} onChange={ev => setLabel(ev.target.value)} placeholder="e.g. Diwali"/></label>
          <label style={{ display: "flex", alignItems: "center", gap: 8, marginTop: 8, fontSize: 13 }}>
            <input type="checkbox" checked={optional} onChange={ev => setOptional(ev.target.checked)}/>
            Optional / floating holiday
          </label>
          {err && <div className="pp-modal-err">{err}</div>}
        </div>
        <div className="pp-modal-foot">
          <button className="pp-btn" onClick={onClose}>Cancel</button>
          <button className="pp-btn pp-btn-primary" onClick={save} disabled={busy}>
            {busy ? "Saving…" : "Add"}
          </button>
        </div>
      </div>
    </div>,
    document.body
  );
}

// ── Admin: Policies ───────────────────────────────────────────
function PpPoliciesAdmin() {
  const [items, setItems]   = React.useState([]);
  const [editing, setEditing] = React.useState(null);
  const reload = async () => { try { const r = await window.api.people.leaveTypes(); setItems(r.types || []); } catch {} };
  React.useEffect(() => { reload(); }, []);
  async function create() {
    const name = window.prompt("Leave type name (e.g. Vacation):");
    if (!name) return;
    try {
      await window.api.people.createLeaveType({ name, annual_default: 12, reset_period_months: 12 });
      await reload();
    } catch {}
  }
  const RESET_OPTIONS = [
    { v: 12, label: "Once a year (Jan – Dec)" },
    { v:  6, label: "Every 6 months (Jan-Jun, Jul-Dec)" },
    { v:  3, label: "Quarterly" },
    { v:  1, label: "Monthly" },
  ];
  return (
    <div className="pp-card">
      <div className="pp-card-head">
        <div>
          <div className="pp-card-title">Leave policies</div>
          <div className="pp-card-sub">Per-type entitlement, reset period, carry-forward, sick-leave rules</div>
        </div>
        <button className="pp-btn pp-btn-primary" onClick={create} style={{ marginLeft: "auto" }}>
          <PpIcon name="Plus"/> New type
        </button>
      </div>
      <div style={{ padding: "8px 0" }}>
        {items.map(t => {
          const reset = RESET_OPTIONS.find(o => o.v === Number(t.reset_period_months || 12));
          return (
            <div key={t.id} style={{ display: "flex", padding: "10px 16px", alignItems: "center",
                                      borderBottom: "1px solid var(--border-row, #eaecf1)" }}>
              <span style={{ width: 10, height: 10, borderRadius: 999, background: t.color, marginRight: 10 }}/>
              <div style={{ flex: 1 }}>
                <div style={{ fontWeight: 600, color: "var(--ink-strong)", fontSize: 13 }}>
                  {t.name}
                  {Number(t.is_sick_leave) === 1 && <span style={{ marginLeft: 8 }}><PpStatusPill status="sick">Sick</PpStatusPill></span>}
                </div>
                <div style={{ fontSize: 11.5, color: "var(--ink-muted)" }}>
                  {Number(t.annual_default)} {t.unit}/period · {reset ? reset.label : "Once a year"}
                  {Number(t.carry_forward) === 1 ? ` · carry forward${t.max_carry_forward != null ? ` (max ${t.max_carry_forward})` : ""}` : ""}
                  {Number(t.requires_medical_cert) === 1 ? " · medical cert required" : ""}
                </div>
              </div>
              <button className="pp-btn" onClick={() => setEditing(t)}>Edit</button>
            </div>
          );
        })}
        {items.length === 0 && (
          <div style={{ padding: 32, textAlign: "center", color: "var(--ink-muted)" }}>
            No leave types configured yet. Add at least one to enable leave requests.
          </div>
        )}
      </div>
      {editing && (
        <PpPolicyEditor row={editing} types={items}
          onClose={() => { setEditing(null); reload(); }}/>
      )}
    </div>
  );
}

function PpPolicyEditor({ row, types, onClose }) {
  const [name, setName]     = React.useState(row.name || "");
  const [color, setColor]   = React.useState(row.color || "#0073ea");
  const [annual, setAnnual] = React.useState(Number(row.annual_default) || 0);
  const [unit, setUnit]     = React.useState(row.unit || "days");
  const [reset, setReset]   = React.useState(Number(row.reset_period_months || 12));
  const [carry, setCarry]   = React.useState(Number(row.carry_forward) === 1);
  const [maxCarry, setMaxCarry] = React.useState(row.max_carry_forward != null ? Number(row.max_carry_forward) : "");
  const [requiresCert, setRequiresCert] = React.useState(Number(row.requires_medical_cert) === 1);
  const [isSick, setIsSick] = React.useState(Number(row.is_sick_leave) === 1);
  const [fallback, setFallback] = React.useState(row.fallback_type_id || "");
  const [busy, setBusy]     = React.useState(false);
  const [err, setErr]       = React.useState("");

  async function save() {
    if (!name) { setErr("Name required"); return; }
    setBusy(true); setErr("");
    try {
      await window.api.people.updateLeaveType(row.id, {
        name, color, annual_default: Number(annual), unit,
        reset_period_months: Number(reset) || 12,
        carry_forward: carry ? 1 : 0,
        max_carry_forward: maxCarry === "" ? null : Number(maxCarry),
        requires_medical_cert: requiresCert ? 1 : 0,
        is_sick_leave: isSick ? 1 : 0,
        fallback_type_id: fallback || null,
      });
      onClose();
    } catch (e) {
      setErr((e && e.body && (e.body.message || e.body.error)) || "Could not save");
    } finally { setBusy(false); }
  }
  async function archive() {
    if (!window.confirm("Archive this leave type? Employees won't see it any more.")) return;
    try { await window.api.people.updateLeaveType(row.id, { archived: 1 }); onClose(); } catch {}
  }
  const otherTypes = types.filter(t => t.id !== row.id);
  return ReactDOM.createPortal(
    <div className="modal-backdrop" onClick={onClose}>
      <div className="pp-modal" onClick={ev => ev.stopPropagation()} style={{ maxWidth: 540 }}>
        <div className="pp-modal-head">
          <div className="pp-modal-title">Edit leave policy</div>
          <button onClick={onClose} className="pp-modal-close">×</button>
        </div>
        <div className="pp-modal-body">
          <div style={{ display: "grid", gridTemplateColumns: "1fr 100px", gap: 12 }}>
            <label className="pp-field"><span>Name</span>
              <input value={name} onChange={ev => setName(ev.target.value)}/></label>
            <label className="pp-field"><span>Color</span>
              <input type="color" value={color} onChange={ev => setColor(ev.target.value)}/></label>
          </div>
          <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 12 }}>
            <label className="pp-field"><span>Entitlement (per period)</span>
              <input type="number" min="0" step="0.5" value={annual} onChange={ev => setAnnual(ev.target.value)}/></label>
            <label className="pp-field"><span>Unit</span>
              <select value={unit} onChange={ev => setUnit(ev.target.value)}>
                <option value="days">days</option>
                <option value="hours">hours</option>
              </select></label>
          </div>
          <label className="pp-field"><span>Reset period</span>
            <select value={reset} onChange={ev => setReset(Number(ev.target.value))}>
              <option value={12}>Once a year (Jan – Dec)</option>
              <option value={6}>Every 6 months (Jan-Jun, Jul-Dec)</option>
              <option value={3}>Quarterly</option>
              <option value={1}>Monthly</option>
            </select></label>
          <div style={{ display: "grid", gridTemplateColumns: "auto 1fr", gap: 12, alignItems: "center" }}>
            <label style={{ display: "flex", alignItems: "center", gap: 8, fontSize: 13 }}>
              <input type="checkbox" checked={carry} onChange={ev => setCarry(ev.target.checked)}/>
              Carry forward unused
            </label>
            <label className="pp-field" style={{ opacity: carry ? 1 : 0.5 }}>
              <span>Max carry (blank = unlimited)</span>
              <input type="number" min="0" step="0.5" disabled={!carry}
                     value={maxCarry} onChange={ev => setMaxCarry(ev.target.value)}/>
            </label>
          </div>
          <div style={{ marginTop: 10, padding: "10px 12px", background: "var(--bg-soft, #f7f8fb)",
                        borderRadius: 8, border: "1px solid var(--border-row, #eaecf1)" }}>
            <div style={{ fontWeight: 600, color: "var(--ink-strong)", fontSize: 13, marginBottom: 6 }}>
              Sick-leave rules
            </div>
            <label style={{ display: "flex", alignItems: "center", gap: 8, fontSize: 13 }}>
              <input type="checkbox" checked={isSick} onChange={ev => setIsSick(ev.target.checked)}/>
              Treat as Sick leave
            </label>
            <label style={{ display: "flex", alignItems: "center", gap: 8, fontSize: 13, marginTop: 4 }}>
              <input type="checkbox" checked={requiresCert} onChange={ev => setRequiresCert(ev.target.checked)}/>
              Require medical certificate
            </label>
            <label className="pp-field" style={{ marginTop: 8, opacity: requiresCert ? 1 : 0.5 }}>
              <span>If previous certificate missing, auto-convert to</span>
              <select value={fallback} disabled={!requiresCert} onChange={ev => setFallback(ev.target.value)}>
                <option value="">— don't auto-convert —</option>
                {otherTypes.map(t => <option key={t.id} value={t.id}>{t.name}</option>)}
              </select>
            </label>
            <div style={{ fontSize: 11.5, color: "var(--ink-muted)", marginTop: 6 }}>
              Example: pick <i>Casual</i> here. After an employee takes a sick leave without
              uploading a certificate, the very next sick leave they file will land as
              <i> Casual</i> instead — they only get sick leave back once the certificate is
              submitted.
            </div>
          </div>
          {err && <div className="pp-modal-err">{err}</div>}
        </div>
        <div className="pp-modal-foot">
          <button className="pp-btn" onClick={archive} style={{ marginRight: "auto" }}>Archive</button>
          <button className="pp-btn" onClick={onClose}>Cancel</button>
          <button className="pp-btn pp-btn-primary" onClick={save} disabled={busy}>
            {busy ? "Saving…" : "Save"}
          </button>
        </div>
      </div>
    </div>,
    document.body
  );
}

// ── Admin: Leave balances ─────────────────────────────────────
// Per-employee × per-type table. Owner/admin can click any cell to
// open the adjuster — bump entitled days up, reset used to zero
// after a correction, or just inspect what's left. Edits go through
// PATCH /api/people/leave/balances and the row reloads on save.
function PpBalancesAdmin() {
  const [members, setMembers] = React.useState(null);
  const [types, setTypes]     = React.useState([]);
  const [balByUser, setBalByUser] = React.useState({});  // userId → typeId → bal
  const [q, setQ]             = React.useState("");
  const [err, setErr]         = React.useState("");
  const [loading, setLoading] = React.useState(true);
  const [edit, setEdit]       = React.useState(null);    // { user, type, bal }
  // editAll: opens the "edit every leave type at once" modal for one
  // employee. Set when the user clicks the employee name; cleared on
  // close. Carries the user row + the types list + the current
  // per-type balance map so the modal renders the right rows.
  const [editAll, setEditAll] = React.useState(null);    // { user, balances: {typeId: bal} }

  const reload = React.useCallback(async () => {
    setLoading(true); setErr("");
    try {
      const [m, t] = await Promise.all([
        window.api.people.members(),
        window.api.people.leaveTypes(),
      ]);
      const emps = (m.members || []).filter(x => x.is_employee);
      setMembers(emps);
      setTypes(t.types || []);
      // Pull balances for everyone in parallel — small workspaces only.
      const map = {};
      await Promise.all(emps.map(async u => {
        try {
          const b = await window.api.people.balances({ userId: u.id });
          const inner = {};
          for (const it of (b.items || [])) inner[it.id] = it;
          map[u.id] = inner;
        } catch { map[u.id] = {}; }
      }));
      setBalByUser(map);
    } catch (e) {
      setErr((e && e.body && (e.body.message || e.body.error)) || (e && e.message) || "Could not load balances");
    } finally { setLoading(false); }
  }, []);
  React.useEffect(() => { reload(); }, [reload]);

  const ql = q.trim().toLowerCase();
  const filtered = (members || []).filter(m => !ql
    || (m.name || "").toLowerCase().includes(ql)
    || (m.email || "").toLowerCase().includes(ql)
    || (m.employee_code || "").toLowerCase().includes(ql));

  if (err) return <div className="pp-card" style={{ padding: 24, color: "var(--prio-critical,#c93636)" }}>{err}</div>;

  return (
    <div className="pp-card">
      <div className="pp-card-head">
        <div>
          <div className="pp-card-title">Leave balances</div>
          <div className="pp-card-sub">Per-employee × per-type. Click a cell to adjust one type, or click the employee name to edit every leave type at once.</div>
        </div>
        <input className="pp-input" placeholder="Search employees…"
               value={q} onChange={e => setQ(e.target.value)}
               style={{ maxWidth: 240, marginLeft: "auto" }}/>
      </div>
      {loading ? (
        <div style={{ padding: 32, color: "var(--ink-muted)", textAlign: "center" }}>Loading balances…</div>
      ) : !members || members.length === 0 ? (
        <div style={{ padding: 32, color: "var(--ink-muted)", textAlign: "center" }}>No tracked employees.</div>
      ) : (
        <div className="pp-bal-wrap">
          <table className="pp-bal-tbl">
            <thead>
              <tr>
                <th className="pp-bal-sticky">Employee</th>
                {types.map(t => (
                  <th key={t.id} style={{ borderBottomColor: t.color || "#0073ea" }}>
                    {t.name}
                  </th>
                ))}
              </tr>
            </thead>
            <tbody>
              {filtered.map(m => (
                <tr key={m.id}>
                  <td className="pp-bal-sticky pp-bal-name-cell"
                      onClick={() => setEditAll({ user: m, balances: balByUser[m.id] || {} })}
                      title={`Click to edit every leave type for ${m.name}`}>
                    <div style={{ display: "flex", gap: 8, alignItems: "center" }}>
                      <PpAvatar person={{ name: m.name, color: m.color, avatar: m.avatar }} size="sm"/>
                      <div style={{ minWidth: 0 }}>
                        <div className="pp-bal-name">{m.name}</div>
                        <div className="pp-bal-sub">{m.employee_code ? m.employee_code + " · " : ""}{m.email || "—"}</div>
                      </div>
                    </div>
                  </td>
                  {types.map(t => {
                    const bal = (balByUser[m.id] || {})[t.id];
                    const remaining = bal ? Number(bal.remaining || 0) : 0;
                    const used      = bal ? Number(bal.used || 0) : 0;
                    const total     = bal ? Number(bal.total || 0) : 0;
                    const tone = !bal ? "blank"
                               : remaining <= 0 ? "zero"
                               : remaining < 2  ? "low"
                               : "ok";
                    return (
                      <td key={t.id} className={"pp-bal-cell pp-bal-cell-" + tone}
                          onClick={() => setEdit({ user: m, type: t, bal: bal || null })}
                          title={`Click to adjust ${t.name} for ${m.name}`}>
                        <div className="pp-bal-cell-rem">{remaining}</div>
                        <div className="pp-bal-cell-frac">{used}/{total}</div>
                      </td>
                    );
                  })}
                </tr>
              ))}
            </tbody>
          </table>
        </div>
      )}

      {edit && <PpBalanceEditor edit={edit} onClose={(saved) => { setEdit(null); if (saved) reload(); }}/>}
      {editAll && <PpAllBalancesEditor
        user={editAll.user}
        types={types}
        balances={editAll.balances}
        onClose={(saved) => { setEditAll(null); if (saved) reload(); }}/>}

      <style>{`
        .pp-bal-wrap { overflow: auto; max-height: 70vh; }
        .pp-bal-tbl { width: 100%; border-collapse: separate; border-spacing: 0; font-size: 13px; }
        .pp-bal-tbl th, .pp-bal-tbl td { padding: 10px 12px; text-align: center;
          border-bottom: 1px solid var(--border-row, #f1f2f5); }
        .pp-bal-tbl th { background: var(--bg-subtle, #fafbfc); font-weight: 700;
          font-size: 11.5px; text-transform: uppercase; letter-spacing: .04em;
          color: var(--ink-muted); position: sticky; top: 0; border-bottom: 2px solid; }
        .pp-bal-sticky { position: sticky; left: 0; background: var(--bg-card, #fff);
          z-index: 1; text-align: left; min-width: 200px; }
        .pp-bal-tbl thead .pp-bal-sticky { z-index: 2; background: var(--bg-subtle, #fafbfc); }
        .pp-bal-name { font-weight: 600; color: var(--ink-strong); font-size: 13px; }
        .pp-bal-sub { font-size: 11px; color: var(--ink-muted); }
        .pp-bal-name-cell { cursor: pointer; transition: background .12s ease; }
        .pp-bal-name-cell:hover { background: var(--bg-subtle, #f6f8fc); }
        .pp-bal-name-cell:hover .pp-bal-name { color: #0073ea; }
        .pp-bal-cell { cursor: pointer; transition: background .12s ease; }
        .pp-bal-cell:hover { background: var(--bg-subtle, #f6f8fc); }
        .pp-bal-cell-rem { font-weight: 700; font-size: 16px; line-height: 1.1;
          font-variant-numeric: tabular-nums; }
        .pp-bal-cell-frac { font-size: 10.5px; color: var(--ink-muted);
          font-variant-numeric: tabular-nums; margin-top: 2px; }
        .pp-bal-cell-ok    .pp-bal-cell-rem { color: #1a8a55; }
        .pp-bal-cell-low   .pp-bal-cell-rem { color: #c47d1a; }
        .pp-bal-cell-zero  .pp-bal-cell-rem { color: #c5354b; }
        .pp-bal-cell-blank .pp-bal-cell-rem { color: var(--ink-faint, #a3a8b6); }
      `}</style>
    </div>
  );
}

// Adjuster modal — owner/admin edits entitled + used for one
// (user, type) bucket. Quick ± buttons for fast nudges. Submit hits
// PATCH /api/people/leave/balances which upserts the row.
function PpBalanceEditor({ edit, onClose }) {
  const { user, type, bal } = edit;
  const [entitled, setEntitled] = React.useState(bal ? Number(bal.entitled != null ? bal.entitled : (bal.total - (bal.carry_in || 0))) : 0);
  const [used,     setUsed]     = React.useState(bal ? Number(bal.used) : 0);
  const [note,     setNote]     = React.useState("");
  const [busy,     setBusy]     = React.useState(false);
  const [err,      setErr]      = React.useState("");
  const remaining = Math.max(0, entitled - used);

  async function save() {
    if (busy) return;
    setBusy(true); setErr("");
    try {
      await window.api.people.setLeaveBalance({
        userId: user.id, typeId: type.id,
        entitled: Number(entitled), used: Number(used),
        note: note || null,
      });
      onClose(true);
    } catch (e) {
      setErr((e && e.body && (e.body.message || e.body.error)) || (e && e.message) || "Could not save");
      setBusy(false);
    }
  }
  return ReactDOM.createPortal(
    <div className="modal-backdrop" onClick={() => onClose(false)}>
      <div className="pp-modal" onClick={ev => ev.stopPropagation()} style={{ maxWidth: 460 }}>
        <div className="pp-modal-head">
          <div className="pp-modal-title">Adjust · {type.name}</div>
          <button onClick={() => onClose(false)} className="pp-modal-close">×</button>
        </div>
        <div className="pp-modal-body">
          <div style={{ fontSize: 12.5, color: "var(--ink-muted)", marginBottom: 10 }}>
            <b style={{ color: "var(--ink-strong)" }}>{user.name}</b>
            {user.employee_code ? " · " + user.employee_code : ""}
          </div>
          <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 12 }}>
            <label className="pp-field">
              <span>Entitled</span>
              <div style={{ display: "flex", gap: 4, alignItems: "center" }}>
                <button className="pp-btn" onClick={() => setEntitled(v => Math.max(0, Number(v) - 1))}>−</button>
                <input type="number" min="0" step="0.5" value={entitled}
                       onChange={e => setEntitled(e.target.value)}
                       style={{ flex: 1, textAlign: "center" }}/>
                <button className="pp-btn" onClick={() => setEntitled(v => Number(v) + 1)}>+</button>
              </div>
            </label>
            <label className="pp-field">
              <span>Used</span>
              <div style={{ display: "flex", gap: 4, alignItems: "center" }}>
                <button className="pp-btn" onClick={() => setUsed(v => Math.max(0, Number(v) - 1))}>−</button>
                <input type="number" min="0" step="0.5" value={used}
                       onChange={e => setUsed(e.target.value)}
                       style={{ flex: 1, textAlign: "center" }}/>
                <button className="pp-btn" onClick={() => setUsed(v => Number(v) + 1)}>+</button>
              </div>
            </label>
          </div>
          <div style={{ marginTop: 10, padding: "10px 12px", background: "var(--bg-subtle,#f6f8fc)",
                        borderRadius: 8, fontSize: 12.5, color: "var(--ink-muted)" }}>
            Remaining after save: <b style={{ color: remaining <= 0 ? "#c5354b" : "#1a8a55", fontSize: 14 }}>{remaining}</b> {type.unit || "days"}
          </div>
          <label className="pp-field" style={{ marginTop: 10 }}>
            <span>Reason / note (optional, logged)</span>
            <textarea rows={2} value={note} onChange={e => setNote(e.target.value)}
                      placeholder="e.g. Granted 2 extra days for project go-live"/>
          </label>
          {err && <div className="pp-modal-err">{err}</div>}
        </div>
        <div className="pp-modal-foot">
          <button className="pp-btn" onClick={() => onClose(false)}>Cancel</button>
          <button className="pp-btn pp-btn-primary" onClick={save} disabled={busy}>
            {busy ? "Saving…" : "Save"}
          </button>
        </div>
      </div>
    </div>,
    document.body
  );
}

// ── Admin: edit every leave type for one employee at once ────
// Opens from Leave balances when the admin clicks an employee's
// name. Renders one row per leave type with entitled/used inputs
// and a live "remaining" preview. On save, only the rows whose
// values actually changed are PATCHed (in parallel), so an admin
// can correct a single type without touching the rest. A single
// note is attached to every changed row's audit entry.
function PpAllBalancesEditor({ user, types, balances, onClose }) {
  // rows[i] = { typeId, entitled, used }
  const initial = React.useMemo(() => (types || []).map(t => {
    const b = (balances || {})[t.id];
    const ent = b ? Number(b.entitled != null ? b.entitled : ((b.total || 0) - (b.carry_in || 0))) : 0;
    const usd = b ? Number(b.used || 0) : 0;
    return { typeId: t.id, name: t.name, color: t.color, unit: t.unit || "days",
             entitled: ent, used: usd,
             orig_entitled: ent, orig_used: usd };
  }), [types, balances]);
  const [rows, setRows] = React.useState(initial);
  const [note, setNote] = React.useState("");
  const [busy, setBusy] = React.useState(false);
  const [err, setErr]   = React.useState("");

  // Keep state in sync if parent passes a new user
  React.useEffect(() => { setRows(initial); }, [initial]);

  function up(i, k, v) {
    setRows(arr => arr.map((r, idx) => idx === i ? { ...r, [k]: Math.max(0, Number(v) || 0) } : r));
  }
  function bump(i, k, delta) {
    setRows(arr => arr.map((r, idx) => idx === i ? { ...r, [k]: Math.max(0, (Number(r[k]) || 0) + delta) } : r));
  }
  function reset() { setRows(initial); }

  const changedRows = rows.filter(r => r.entitled !== r.orig_entitled || r.used !== r.orig_used);
  const totalEntitled = rows.reduce((s, r) => s + (Number(r.entitled) || 0), 0);
  const totalUsed     = rows.reduce((s, r) => s + (Number(r.used) || 0), 0);

  async function save() {
    if (busy) return;
    if (changedRows.length === 0) { onClose(false); return; }
    setBusy(true); setErr("");
    try {
      // PATCH each changed row in parallel; if any fail, surface the
      // first error but report partial success in the message so the
      // admin can see what got through.
      const results = await Promise.allSettled(changedRows.map(r =>
        window.api.people.setLeaveBalance({
          userId: user.id, typeId: r.typeId,
          entitled: Number(r.entitled), used: Number(r.used),
          note: note || null,
        })
      ));
      const failed = results.filter(x => x.status === "rejected");
      if (failed.length) {
        const first = failed[0].reason;
        const msg = (first && first.body && (first.body.message || first.body.error))
                 || (first && first.message)
                 || "Some changes failed to save";
        setErr(`${msg} — ${results.length - failed.length} of ${results.length} saved.`);
        setBusy(false);
        return;
      }
      onClose(true);
    } catch (e) {
      setErr((e && e.body && (e.body.message || e.body.error)) || (e && e.message) || "Could not save");
      setBusy(false);
    }
  }

  return ReactDOM.createPortal(
    <div className="modal-backdrop" onClick={() => onClose(false)}>
      <div className="pp-modal" onClick={ev => ev.stopPropagation()}
           style={{ maxWidth: 640, width: "94vw", maxHeight: "92vh", display: "flex", flexDirection: "column" }}>
        <div className="pp-modal-head">
          <div>
            <div className="pp-modal-title">Edit all leave balances</div>
            <div style={{ fontSize: 12, color: "var(--ink-muted)", marginTop: 2 }}>
              <b style={{ color: "var(--ink-strong)" }}>{user.name}</b>
              {user.employee_code ? " · " + user.employee_code : ""}
              {user.email ? " · " + user.email : ""}
            </div>
          </div>
          <button onClick={() => onClose(false)} className="pp-modal-close">×</button>
        </div>
        <div className="pp-modal-body" style={{ flex: 1, overflow: "auto" }}>
          <div className="pp-all-bal">
            <div className="pp-all-bal-head">
              <div>Leave type</div>
              <div style={{ textAlign: "center" }}>Entitled</div>
              <div style={{ textAlign: "center" }}>Used</div>
              <div style={{ textAlign: "right" }}>Remaining</div>
            </div>
            {rows.length === 0 ? (
              <div style={{ padding: 18, color: "var(--ink-muted)", textAlign: "center" }}>
                No leave types configured yet.
              </div>
            ) : rows.map((r, i) => {
              const remaining = Math.max(0, (Number(r.entitled) || 0) - (Number(r.used) || 0));
              const changed = r.entitled !== r.orig_entitled || r.used !== r.orig_used;
              return (
                <div key={r.typeId} className={"pp-all-bal-row" + (changed ? " is-changed" : "")}>
                  <div>
                    <span className="pp-all-bal-swatch" style={{ background: r.color || "#0073ea" }}/>
                    <b style={{ fontSize: 13 }}>{r.name}</b>
                    <span style={{ marginLeft: 6, fontSize: 11, color: "var(--ink-muted)" }}>{r.unit}</span>
                  </div>
                  <div style={{ display: "flex", alignItems: "center", gap: 3, justifyContent: "center" }}>
                    <button className="pp-btn pp-btn-icon" onClick={() => bump(i, "entitled", -1)}>−</button>
                    <input type="number" min="0" step="0.5" value={r.entitled}
                           onChange={e => up(i, "entitled", e.target.value)}
                           style={{ width: 64, textAlign: "center" }}/>
                    <button className="pp-btn pp-btn-icon" onClick={() => bump(i, "entitled", +1)}>+</button>
                  </div>
                  <div style={{ display: "flex", alignItems: "center", gap: 3, justifyContent: "center" }}>
                    <button className="pp-btn pp-btn-icon" onClick={() => bump(i, "used", -1)}>−</button>
                    <input type="number" min="0" step="0.5" value={r.used}
                           onChange={e => up(i, "used", e.target.value)}
                           style={{ width: 64, textAlign: "center" }}/>
                    <button className="pp-btn pp-btn-icon" onClick={() => bump(i, "used", +1)}>+</button>
                  </div>
                  <div style={{ textAlign: "right" }}>
                    <b style={{
                      fontSize: 15, fontVariantNumeric: "tabular-nums",
                      color: remaining <= 0 ? "#c5354b" : remaining < 2 ? "#c47d1a" : "#1a8a55"
                    }}>{remaining}</b>
                  </div>
                </div>
              );
            })}
            <div className="pp-all-bal-foot">
              <div><b>Totals</b></div>
              <div style={{ textAlign: "center", fontVariantNumeric: "tabular-nums" }}>{totalEntitled}</div>
              <div style={{ textAlign: "center", fontVariantNumeric: "tabular-nums" }}>{totalUsed}</div>
              <div style={{ textAlign: "right", fontVariantNumeric: "tabular-nums" }}><b>{Math.max(0, totalEntitled - totalUsed)}</b></div>
            </div>
          </div>
          <label className="pp-field" style={{ marginTop: 14 }}>
            <span>Reason / note (optional, logged on every change)</span>
            <textarea rows={2} value={note} onChange={e => setNote(e.target.value)}
                      placeholder="e.g. Annual reset / Mid-year adjustment"/>
          </label>
          {err && <div className="pp-modal-err">{err}</div>}
        </div>
        <div className="pp-modal-foot">
          <button className="pp-btn" onClick={reset} disabled={busy || changedRows.length === 0}>Reset</button>
          <div style={{ flex: 1 }}/>
          <button className="pp-btn" onClick={() => onClose(false)} disabled={busy}>Cancel</button>
          <button className="pp-btn pp-btn-primary" onClick={save} disabled={busy}>
            {busy
              ? "Saving…"
              : changedRows.length > 0
                ? `Save ${changedRows.length} change${changedRows.length === 1 ? "" : "s"}`
                : "Save"}
          </button>
        </div>
        <style>{`
          .pp-all-bal { border: 1px solid var(--border-row, #eaecf1); border-radius: 10px; overflow: hidden; }
          .pp-all-bal-head, .pp-all-bal-row, .pp-all-bal-foot {
            display: grid; grid-template-columns: 1.4fr 1fr 1fr .8fr;
            gap: 10px; align-items: center; padding: 9px 12px;
          }
          .pp-all-bal-head {
            background: var(--bg-subtle, #fafbfc);
            font-size: 11px; text-transform: uppercase; letter-spacing: .05em;
            color: var(--ink-muted); font-weight: 700;
            border-bottom: 1px solid var(--border-row, #eaecf1);
          }
          .pp-all-bal-row {
            border-bottom: 1px solid var(--border-row, #f1f3f7);
            transition: background .12s ease;
          }
          .pp-all-bal-row:last-of-type { border-bottom: 0; }
          .pp-all-bal-row.is-changed { background: #fffaf0; }
          .pp-all-bal-row.is-changed::before { content: ""; }
          .pp-all-bal-foot {
            background: var(--bg-subtle, #fafbfc);
            border-top: 1px solid var(--border-row, #eaecf1);
            font-size: 13px;
          }
          .pp-all-bal-swatch {
            display: inline-block; width: 8px; height: 8px; border-radius: 99px;
            margin-right: 8px; vertical-align: middle;
          }
          .pp-btn-icon {
            width: 26px; height: 26px; padding: 0; min-width: unset;
            display: inline-flex; align-items: center; justify-content: center;
            font-weight: 800;
          }
        `}</style>
      </div>
    </div>,
    document.body
  );
}

// ── Admin: Reports stub ───────────────────────────────────────
function PpReportsAdmin() {
  return (
    <div className="pp-card" style={{ padding: 20 }}>
      <div className="pp-card-title">Reports & exports</div>
      <div className="pp-card-sub" style={{ marginTop: 4 }}>
        Coming next — CSV export for attendance, hours, leave by date range.
      </div>
    </div>
  );
}

// ── Apply-for-leave modal (shared with employee shell) ────────
// Two-step flow:
//   step 1: pick type/dates + optional note + optional cert file
//   step 2 (only if server converted or marked cert_required): show
//          the result with the conversion banner OR an upload prompt.
function PpRequestModal({ onClose, defaultUserId = null }) {
  const [types, setTypes] = React.useState([]);
  const [typeId, setTypeId] = React.useState("");
  const [from, setFrom]   = React.useState(() => new Date().toISOString().slice(0,10));
  const [to, setTo]       = React.useState(() => new Date().toISOString().slice(0,10));
  const [note, setNote]   = React.useState("");
  const [cert, setCert]   = React.useState(null);     // File object
  const [halfDay, setHalfDay]       = React.useState(false);
  const [halfPeriod, setHalfPeriod] = React.useState("am"); // am | pm
  const [busy, setBusy]   = React.useState(false);
  const [err, setErr]     = React.useState("");
  const [result, setResult] = React.useState(null);   // server response after submit
  const [taskSum, setTaskSum] = React.useState(null);  // heads-up: tasks due during leave
  // Balance map for the current employee: typeId → { remaining, total, used, unit }.
  // Fetched alongside the leave types so the modal can show "X days left"
  // next to each option and warn when the request exceeds the balance.
  const [balances, setBalances] = React.useState({});
  // Loss-of-Pay opt-in. Defaults to true automatically when the request
  // can't fit in the available balance, but the employee must still
  // confirm by leaving the toggle on. The note gets a "[LOP: N days]"
  // marker on submit so approvers see it.
  const [allowLop, setAllowLop] = React.useState(false);
  React.useEffect(() => {
    (async () => {
      try {
        const r = await window.api.people.leaveTypes();
        setTypes(r.types || []);
        if ((r.types || []).length) setTypeId(r.types[0].id);
      } catch {}
      try {
        const b = await window.api.people.balances({ userId: defaultUserId || undefined });
        const m = {};
        for (const it of (b.items || [])) m[it.id] = it;
        setBalances(m);
      } catch {}
    })();
  }, [defaultUserId]);
  // Pull the tasks due during the selected window so the employee can
  // finish or hand them off before going on leave.
  React.useEffect(() => {
    if (!from || !to) { setTaskSum(null); return; }
    let alive = true;
    (async () => {
      try {
        const r = await window.api.people.myTasksDue({ from, to: halfDay ? from : to });
        if (alive) setTaskSum(r);
      } catch { if (alive) setTaskSum(null); }
    })();
    return () => { alive = false; };
  }, [from, to, halfDay]);
  const days = halfDay
    ? 0.5
    : (from && to ? Math.max(1, Math.round((new Date(to) - new Date(from)) / 86_400_000) + 1) : 0);
  const chosen = types.find(t => t.id === typeId);
  const needsCert = chosen && Number(chosen.requires_medical_cert) === 1;
  // Balance math for the chosen type. exceedsBy is how many days fall
  // outside the available balance — i.e. the LOP shortfall.
  const bal       = chosen ? balances[chosen.id] : null;
  const remaining = bal ? Number(bal.remaining || 0) : null;
  const unit      = (bal && bal.unit) || "days";
  const exceedsBy = remaining != null ? Math.max(0, days - remaining) : 0;
  const noBalance = remaining != null && remaining <= 0;
  const lopDays   = (allowLop && exceedsBy > 0) ? exceedsBy : 0;
  // Auto-prompt LOP when the request can't fit. The employee still has
  // to leave the toggle ON for the request to go through as LOP.
  React.useEffect(() => {
    if (exceedsBy > 0) setAllowLop(true);
    else setAllowLop(false);
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [typeId, from, to, halfDay]);

  async function submit() {
    if (busy) return;
    if (!typeId || !from || !to) { setErr("Pick a type and dates"); return; }
    if (exceedsBy > 0 && !allowLop) {
      setErr(`Only ${remaining} ${unit} available — turn on Loss of Pay to apply for the extra ${exceedsBy}.`);
      return;
    }
    setBusy(true); setErr("");
    try {
      // Append a clear LOP marker to the note so approvers see the
      // breakdown without us needing a schema change.
      let finalNote = note || "";
      if (lopDays > 0) {
        const marker = `[LOP: ${lopDays} ${unit}${remaining > 0 ? ` · paid: ${days - lopDays} ${unit}` : ""}]`;
        finalNote = finalNote ? (finalNote + "\n\n" + marker) : marker;
      }
      const r = await window.api.people.createLeaveRequest({
        type_id: typeId,
        from_date: from,
        to_date: halfDay ? from : to,
        note: finalNote,
        half_day: halfDay,
        half_day_period: halfDay ? halfPeriod : null,
      });
      // If the user attached a certificate AND the server didn't convert
      // the request (i.e. the chosen type still wants a cert), upload it
      // immediately.
      if (cert && r && r.id && !r.original_type_id && r.certificate_required) {
        try { await window.api.people.uploadLeaveCertificate(r.id, cert); }
        catch (e) { /* swallow — they can upload later from My Leave */ }
      }
      setResult(r || { ok: true });
    } catch (e) {
      setErr((e && e.body && (e.body.message || e.body.error)) || (e && e.message) || "Could not submit");
    } finally { setBusy(false); }
  }

  // Result step — show conversion notice or cert reminder.
  if (result) {
    const convertedTypeName = result.original_type_id
      ? (types.find(t => t.id === result.type_id)?.name || "fallback type")
      : null;
    const originalTypeName = result.original_type_id
      ? (types.find(t => t.id === result.original_type_id)?.name || "Sick")
      : null;
    return ReactDOM.createPortal(
      <div className="modal-backdrop" onClick={onClose}>
        <div className="pp-modal" onClick={ev => ev.stopPropagation()} style={{ maxWidth: 460 }}>
          <div className="pp-modal-head">
            <div className="pp-modal-title">Leave submitted</div>
            <button onClick={onClose} className="pp-modal-close">×</button>
          </div>
          <div className="pp-modal-body">
            {result.original_type_id ? (
              <div style={{ padding: 12, borderRadius: 8, background: "#fff3e0",
                            border: "1px solid #fbb461", color: "#7a4a00", fontSize: 13 }}>
                <b>Heads up — converted to {convertedTypeName}.</b><br/>
                {result.converted_reason || `Your last ${originalTypeName} leave didn't have a medical certificate, so this request was filed as ${convertedTypeName} instead.`}
                <div style={{ marginTop: 6, fontSize: 12, opacity: 0.85 }}>
                  Upload the certificate from My leave to restore {originalTypeName} eligibility next time.
                </div>
              </div>
            ) : result.certificate_required ? (
              <div style={{ padding: 12, borderRadius: 8, background: "#e0f0ff",
                            border: "1px solid #6fb0ff", color: "#0b3e7a", fontSize: 13 }}>
                <b>Medical certificate needed.</b><br/>
                Please upload your certificate so this leave keeps its sick-leave status.
              </div>
            ) : (
              <div style={{ padding: 12, borderRadius: 8, background: "#e6f9ef",
                            border: "1px solid #4fcb84", color: "#066039", fontSize: 13 }}>
                Request sent for approval — {days} day{days === 1 ? "" : "s"}.
              </div>
            )}
          </div>
          <div className="pp-modal-foot">
            <button className="pp-btn pp-btn-primary" onClick={onClose}>Done</button>
          </div>
        </div>
      </div>,
      document.body
    );
  }

  return ReactDOM.createPortal(
    <div className="modal-backdrop" onClick={onClose}>
      <div className="pp-modal" onClick={ev => ev.stopPropagation()}>
        <div className="pp-modal-head">
          <div className="pp-modal-title">Apply for leave</div>
          <button onClick={onClose} className="pp-modal-close">×</button>
        </div>
        <div className="pp-modal-body">
          <label className="pp-field">
            <span>Type</span>
            <select value={typeId} onChange={ev => setTypeId(ev.target.value)}>
              {types.map(t => {
                const b = balances[t.id];
                const rem = b ? Number(b.remaining || 0) : null;
                const u = (b && b.unit) || "days";
                return (
                  <option key={t.id} value={t.id}>
                    {t.name}{rem != null ? ` · ${rem} ${u} left` : ""}
                  </option>
                );
              })}
            </select>
          </label>
          <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 12 }}>
            <label className="pp-field"><span>From</span>
              <input type="date" value={from} onChange={ev => setFrom(ev.target.value)}/></label>
            <label className="pp-field"><span>To</span>
              <input type="date" value={to} disabled={halfDay}
                     onChange={ev => setTo(ev.target.value)}/></label>
          </div>
          <label style={{ display: "flex", alignItems: "center", gap: 8, marginTop: 10, fontSize: 13,
                          color: "var(--ink-strong)", cursor: "pointer" }}>
            <input type="checkbox" checked={halfDay}
                   onChange={ev => { setHalfDay(ev.target.checked); if (ev.target.checked) setTo(from); }}/>
            Half day only
            {halfDay && (
              <select value={halfPeriod} onChange={ev => setHalfPeriod(ev.target.value)}
                      style={{ marginLeft: 8, padding: "3px 8px", borderRadius: 6,
                               border: "1px solid var(--border, #e6e9ef)", fontSize: 12 }}>
                <option value="am">Morning (AM)</option>
                <option value="pm">Afternoon (PM)</option>
              </select>
            )}
          </label>
          <div style={{ fontSize: 12, color: "var(--ink-muted)", marginTop: 6 }}>
            {halfDay ? "½ day" : `${days} day${days === 1 ? "" : "s"}`} requested
          </div>

          {/* ── Balance card + LOP toggle ─────────────────────────
              Shows the available balance for the selected type, the
              breakdown when the request fits, and an inline Loss-of-Pay
              switch when the request exceeds what's available. The
              switch is auto-enabled when over-balance so the employee
              just needs to leave it on (or pick a shorter range). */}
          {chosen && bal && (
            <div className={"pp-leave-bal" + (exceedsBy > 0 ? " pp-leave-bal-over" : noBalance ? " pp-leave-bal-zero" : "")}>
              <div className="pp-leave-bal-row">
                <span className="pp-leave-bal-k">Available</span>
                <span className="pp-leave-bal-v">{remaining} {unit}</span>
              </div>
              <div className="pp-leave-bal-row pp-leave-bal-row-sub">
                <span>Used this period</span>
                <span>{Number(bal.used || 0)} / {Number(bal.total || 0)} {unit}</span>
              </div>
              {exceedsBy > 0 && (
                <>
                  <div className="pp-leave-bal-warn">
                    {remaining > 0
                      ? <>This request needs <b>{days} {unit}</b>, but you only have <b>{remaining} {unit}</b> left. The extra <b>{exceedsBy} {unit}</b> will be filed as Loss of Pay.</>
                      : <>You have no {chosen.name} balance left. The full <b>{days} {unit}</b> will be filed as Loss of Pay.</>}
                  </div>
                  <label className="pp-leave-lop">
                    <input type="checkbox" checked={allowLop}
                           onChange={e => setAllowLop(e.target.checked)}/>
                    <span>Apply <b>{exceedsBy} {unit}</b> as Loss of Pay (unpaid)</span>
                  </label>
                </>
              )}
              <style>{`
                .pp-leave-bal { margin-top: 10px; padding: 10px 12px; border-radius: 9px;
                  border: 1px solid #c9e0fd; background: #eef5ff; font-size: 12.5px; }
                .pp-leave-bal-over { border-color: #fbb461; background: #fff5e6; }
                .pp-leave-bal-zero { border-color: #f5a3a3; background: #ffefef; }
                .pp-leave-bal-row { display:flex; justify-content:space-between; align-items:baseline; }
                .pp-leave-bal-k { font-weight: 700; color: var(--ink-strong, #0f1729); }
                .pp-leave-bal-v { font-weight: 700; font-variant-numeric: tabular-nums; color: #0b5cad; }
                .pp-leave-bal-over .pp-leave-bal-v { color: #a8740f; }
                .pp-leave-bal-zero .pp-leave-bal-v { color: #c5354b; }
                .pp-leave-bal-row-sub { color: var(--ink-muted, #6c7385); font-size: 11.5px; margin-top: 2px; font-weight: 500; }
                .pp-leave-bal-row-sub span { font-weight: 500; color: var(--ink-muted); }
                .pp-leave-bal-warn { margin-top: 9px; padding-top: 9px; border-top: 1px dashed rgba(0,0,0,.08);
                  color: #7a4a00; font-size: 12.5px; line-height: 1.45; }
                .pp-leave-bal-zero .pp-leave-bal-warn { color: #8a1d2e; }
                .pp-leave-lop { display:flex; align-items:flex-start; gap: 8px; margin-top: 9px;
                  padding: 8px 10px; border-radius: 7px; background: rgba(255,255,255,.7); cursor: pointer; }
                .pp-leave-lop input { margin-top: 2px; }
              `}</style>
            </div>
          )}

          {taskSum && (taskSum.counts.dueInRange > 0 || taskSum.counts.overdue > 0) && (
            <div className="pp-leave-tasks">
              <div className="pp-leave-tasks-h">
                <span>⚠️</span> Tasks that need your attention before you go
              </div>
              {taskSum.dueInRange.length > 0 && (
                <div className="pp-leave-tasks-grp">
                  <div className="pp-leave-tasks-lbl">Due during your leave ({taskSum.counts.dueInRange})</div>
                  {taskSum.dueInRange.slice(0, 6).map(t => (
                    <div key={t.id} className="pp-leave-task">
                      <span className="pp-leave-task-name">{t.name}</span>
                      {t.due && <span className="pp-leave-task-due">{t.due}</span>}
                    </div>
                  ))}
                  {taskSum.counts.dueInRange > 6 && <div className="pp-leave-task-more">+{taskSum.counts.dueInRange - 6} more</div>}
                </div>
              )}
              {taskSum.overdue.length > 0 && (
                <div className="pp-leave-tasks-grp">
                  <div className="pp-leave-tasks-lbl pp-leave-tasks-lbl-red">Already overdue ({taskSum.counts.overdue})</div>
                  {taskSum.overdue.slice(0, 4).map(t => (
                    <div key={t.id} className="pp-leave-task">
                      <span className="pp-leave-task-name">{t.name}</span>
                      {t.due && <span className="pp-leave-task-due">{t.due}</span>}
                    </div>
                  ))}
                  {taskSum.counts.overdue > 4 && <div className="pp-leave-task-more">+{taskSum.counts.overdue - 4} more</div>}
                </div>
              )}
              <div className="pp-leave-tasks-hint">Consider finishing or reassigning these before your leave.</div>
              <style>{`
                .pp-leave-tasks { margin-top: 12px; padding: 11px 13px; background: #fff8ec;
                  border: 1px solid #fadc9b; border-radius: 10px; }
                .pp-leave-tasks-h { font-size: 12.5px; font-weight: 700; color: #9a6a08; display: flex; align-items: center; gap: 6px; }
                .pp-leave-tasks-grp { margin-top: 9px; }
                .pp-leave-tasks-lbl { font-size: 10.5px; font-weight: 700; letter-spacing: .04em; text-transform: uppercase; color: #a8740f; margin-bottom: 5px; }
                .pp-leave-tasks-lbl-red { color: #c5354b; }
                .pp-leave-task { display: flex; align-items: center; gap: 8px; padding: 4px 0; font-size: 12.5px; }
                .pp-leave-task-name { flex: 1; min-width: 0; color: var(--ink-strong, #0f1729); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
                .pp-leave-task-due { font-size: 11px; color: #8a5a0a; flex: none; }
                .pp-leave-task-more { font-size: 11px; color: #a8740f; padding-top: 2px; }
                .pp-leave-tasks-hint { font-size: 11.5px; color: #8a5a0a; margin-top: 9px; font-style: italic; }
              `}</style>
            </div>
          )}

          <label className="pp-field" style={{ marginTop: 10 }}>
            <span>Note (optional)</span>
            <textarea rows={3} value={note} onChange={ev => setNote(ev.target.value)}/>
          </label>
          {needsCert && (
            <label className="pp-field" style={{ marginTop: 10 }}>
              <span>Medical certificate (optional, can upload later)</span>
              <input type="file" accept="image/*,application/pdf"
                     onChange={ev => setCert(ev.target.files && ev.target.files[0] || null)}/>
              <div style={{ fontSize: 11.5, color: "var(--ink-muted)", marginTop: 4 }}>
                If you skip this and your last sick leave also had no certificate, this request will be
                filed as a casual leave instead.
              </div>
            </label>
          )}
          {err && <div className="pp-modal-err">{err}</div>}
        </div>
        <div className="pp-modal-foot">
          <button className="pp-btn" onClick={onClose}>Cancel</button>
          <button className="pp-btn pp-btn-primary" onClick={submit} disabled={busy}>
            {busy ? "Submitting…" : "Submit"}
          </button>
        </div>
      </div>
    </div>,
    document.body
  );
}

// ── AI HR Manager (admin) ──────────────────────────────────────
// "Ava" — configure the ongoing watch instructions + tone, see the
// team's latest attendance alerts, and trigger a fresh run on demand.
function PpHrCoach() {
  const [config, setConfig]   = React.useState(null);
  const [items, setItems]     = React.useState([]);
  const [aiOn, setAiOn]       = React.useState(false);
  const [coach, setCoach]     = React.useState("Ava");
  const [loading, setLoading] = React.useState(true);
  const [saving, setSaving]   = React.useState(false);
  const [running, setRunning] = React.useState(false);

  const load = React.useCallback(async () => {
    setLoading(true);
    try {
      const [cfg, team] = await Promise.all([
        window.api.people.hr.getConfig().catch(() => ({})),
        window.api.people.hr.team().catch(() => ({})),
      ]);
      setConfig((cfg && cfg.config) || { enabled: 1, tone: "supportive", instructions: "", window_days: 30 });
      setItems((team && team.items) || []);
      setAiOn(!!((cfg && cfg.aiConfigured) || (team && team.aiConfigured)));
      if (team && team.coach) setCoach(team.coach);
    } catch {}
    setLoading(false);
  }, []);
  React.useEffect(() => { load(); }, [load]);

  async function save() {
    if (!config) return;
    setSaving(true);
    try {
      await window.api.people.hr.setConfig({
        enabled: config.enabled ? 1 : 0,
        tone: config.tone || "supportive",
        instructions: config.instructions || "",
        window_days: Number(config.window_days) || 30,
        insurance_eligible_months: Number(config.insurance_eligible_months) || 20,
      });
      if (window.fbToast) window.fbToast("HR coach settings saved", 2200);
    } catch (e) {
      if (window.fbToast) window.fbToast("Couldn't save settings", 3000);
    }
    setSaving(false);
  }

  async function runNow() {
    setRunning(true);
    if (window.fbToast) window.fbToast(`${coach} is reviewing the team…`, 2500);
    try {
      const r = await window.api.people.hr.run();
      if (window.fbToast) window.fbToast(`Reviewed ${r.done || 0} employee(s) · ${r.flagged || 0} flagged`, 3500);
      await load();
    } catch (e) {
      const msg = (e && e.body && e.body.error) === "migration_pending"
        ? "Apply migration 053 on the server first."
        : "Run failed — check the server logs.";
      if (window.fbToast) window.fbToast(msg, 4000);
    }
    setRunning(false);
  }

  if (loading) {
    return <div className="pp-card" style={{ padding: 20, color: "var(--ink-muted)" }}>Loading AI HR Manager…</div>;
  }

  const sevLabel = { ok: "On track", info: "Heads-up", watch: "Worth a look", concern: "Needs attention" };
  const flagged = items.filter(i => i.severity && i.severity !== "ok");

  return (
    <div className="pp-hrm">
      {/* Header / status */}
      <div className="pp-card pp-hrm-head">
        <div className="pp-hrm-avatar"><PpIcon name="Sparkle" size={20}/></div>
        <div style={{ flex: 1, minWidth: 0 }}>
          <div className="pp-card-title">{coach} · AI HR Manager</div>
          <div className="pp-card-sub">
            Watches everyone's attendance and writes a supportive nudge for each person, plus alerts for you.
            {" "}{aiOn
              ? <span style={{ color: "#1a8a55", fontWeight: 700 }}>AI model connected</span>
              : <span style={{ color: "#a8740f", fontWeight: 700 }}>Rule-based (no AI key set)</span>}
          </div>
        </div>
        <button className="pp-btn pp-btn-primary" onClick={runNow} disabled={running}>
          <PpIcon name="Sparkle"/> {running ? "Reviewing…" : "Run now"}
        </button>
      </div>

      {/* Instructions / settings */}
      <div className="pp-card" style={{ padding: 16 }}>
        <div className="pp-card-title" style={{ marginBottom: 8 }}>What should {coach} watch for?</div>
        <div className="pp-card-sub" style={{ marginBottom: 10 }}>
          Plain-English rules — add as many as you like, one per line. The coach follows these every run.
        </div>
        <textarea
          className="pp-hrm-instr"
          value={config.instructions || ""}
          onChange={e => setConfig({ ...config, instructions: e.target.value })}
          placeholder={"e.g.\nFlag anyone taking leave on Mondays\nWatch for being late every Monday\nFlag leaving early too often"}
          rows={7}/>
        <div className="pp-hrm-row">
          <label className="pp-hrm-field">
            <span>Tone</span>
            <select value={config.tone || "supportive"} onChange={e => setConfig({ ...config, tone: e.target.value })}>
              <option value="supportive">Supportive coach</option>
              <option value="firm">Firm &amp; professional</option>
              <option value="mixed">Mix by severity</option>
            </select>
          </label>
          <label className="pp-hrm-field">
            <span>Look-back window (days)</span>
            <input type="number" min="7" max="120" value={config.window_days || 30}
                   onChange={e => setConfig({ ...config, window_days: e.target.value })}/>
          </label>
          <label className="pp-hrm-field">
            <span>Insurance eligible after (months)</span>
            <input type="number" min="0" max="120" value={config.insurance_eligible_months != null ? config.insurance_eligible_months : 20}
                   onChange={e => setConfig({ ...config, insurance_eligible_months: e.target.value })}/>
          </label>
          <label className="pp-hrm-check">
            <input type="checkbox" checked={!!Number(config.enabled)}
                   onChange={e => setConfig({ ...config, enabled: e.target.checked ? 1 : 0 })}/>
            <span>Enabled</span>
          </label>
          <button className="pp-btn pp-btn-primary" style={{ marginLeft: "auto" }} onClick={save} disabled={saving}>
            {saving ? "Saving…" : "Save settings"}
          </button>
        </div>
        {!aiOn && (
          <div className="pp-hrm-note">
            Tip: set <code>ANTHROPIC_API_KEY</code> (or <code>OPENAI_API_KEY</code>) in the server environment to switch from
            rule-based messages to AI-written ones.
          </div>
        )}
      </div>

      {/* Team alerts */}
      <div className="pp-card">
        <div className="pp-card-head">
          <div>
            <div className="pp-card-title">Team alerts</div>
            <div className="pp-card-sub">{flagged.length} flagged · {items.length} reviewed{items[0] && items[0].period_key ? " · " + items[0].period_key : ""}</div>
          </div>
        </div>
        <div className="pp-hrm-list">
          {items.length === 0 && (
            <div style={{ padding: 22, textAlign: "center", color: "var(--ink-muted)" }}>
              No insights yet — hit “Run now” to have {coach} review the team.
            </div>
          )}
          {items.map(it => (
            <div key={it.user_id} className={"pp-hrm-item pp-hrm-" + (it.severity || "info")}>
              <PpAvatar person={{ name: it.name, color: it.color, avatar: it.avatar }} size="sm"/>
              <div style={{ flex: 1, minWidth: 0 }}>
                <div className="pp-hrm-item-top">
                  <span className="pp-hrm-item-name">{it.name}</span>
                  <span className={"pp-hrm-badge pp-hrm-badge-" + (it.severity || "info")}>{sevLabel[it.severity] || "Note"}</span>
                </div>
                <div className="pp-hrm-item-msg">{it.message}</div>
              </div>
            </div>
          ))}
        </div>
      </div>
    </div>
  );
}

// Mount-friendly helpers + simple CSS for modal/field that aren't in
// people.css (the prototype didn't ship a modal — we add one here).
const PP_MANAGER_EXTRA_CSS = `
/* ── AI HR Manager (admin tab) ───────────────────────────── */
.pp-hrm { display: flex; flex-direction: column; gap: 16px; }
.pp-hrm-head { display: flex; align-items: center; gap: 14px; padding: 16px; }
.pp-hrm-avatar {
  width: 42px; height: 42px; border-radius: 50%; flex: none;
  display: flex; align-items: center; justify-content: center;
  background: #eaf1ff; color: #0073ea;
}
.pp-hrm-instr {
  width: 100%; box-sizing: border-box; resize: vertical;
  border: 1px solid var(--border-row, #d6dae3); border-radius: 8px;
  padding: 10px 12px; font: inherit; font-size: 13px; line-height: 1.5;
  color: var(--ink-strong, #0f1729); background: var(--bg-soft, #fbfbfc);
}
.pp-hrm-instr:focus { outline: none; border-color: #0073ea; box-shadow: 0 0 0 3px rgba(0,115,234,.12); }
.pp-hrm-row { display: flex; align-items: flex-end; gap: 14px; margin-top: 12px; flex-wrap: wrap; }
.pp-hrm-field { display: flex; flex-direction: column; gap: 4px; font-size: 11.5px; font-weight: 700; color: var(--ink-muted, #6c7385); }
.pp-hrm-field select, .pp-hrm-field input {
  font: inherit; font-size: 13px; padding: 7px 9px; border: 1px solid var(--border-row, #d6dae3);
  border-radius: 7px; background: #fff; color: var(--ink-strong, #0f1729); min-width: 120px;
}
.pp-hrm-check { display: flex; align-items: center; gap: 6px; font-size: 13px; font-weight: 600; color: var(--ink-strong, #0f1729); padding-bottom: 8px; }
.pp-hrm-note { margin-top: 12px; font-size: 12px; color: var(--ink-muted, #6c7385); background: #fff8e8; border: 1px solid #f6e2b3; border-radius: 8px; padding: 9px 12px; }
.pp-hrm-note code { background: #fff; padding: 1px 5px; border-radius: 4px; border: 1px solid #eadfc0; font-size: 11.5px; }
.pp-hrm-list { padding: 4px 0; }
.pp-hrm-item { display: flex; gap: 11px; align-items: flex-start; padding: 11px 16px; border-bottom: 1px solid var(--border-row, #eef0f5); border-left: 3px solid transparent; }
.pp-hrm-ok      { border-left-color: #21a366; }
.pp-hrm-info    { border-left-color: #0073ea; }
.pp-hrm-watch   { border-left-color: #f0b429; }
.pp-hrm-concern { border-left-color: #e2445c; }
.pp-hrm-item-top { display: flex; align-items: center; gap: 8px; }
.pp-hrm-item-name { font-size: 13px; font-weight: 700; color: var(--ink-strong, #0f1729); }
.pp-hrm-item-msg { font-size: 12.5px; color: var(--ink-muted, #4a4f5e); margin-top: 2px; line-height: 1.45; }
.pp-hrm-badge { font-size: 10px; font-weight: 800; letter-spacing: .03em; padding: 2px 8px; border-radius: 99px; }
.pp-hrm-badge-ok      { background: #e6f7ee; color: #1a8a55; }
.pp-hrm-badge-info    { background: #eaf1ff; color: #0073ea; }
.pp-hrm-badge-watch   { background: #fdf1d8; color: #a8740f; }
.pp-hrm-badge-concern { background: #fde7ea; color: #c5354b; }

/* App shell layout — fills the .main column from the host app
   (which is flex-column with overflow:hidden), with a sidebar +
   scrollable body. The prototype's CSS uses .people-app for this
   role; we rebuild it under .pp-app here so JSX class names stay
   aligned. Without this rule, .pp-body's flex:1 + overflow:auto
   never engages and the page can't scroll. */
.pp-app {
  flex: 1;
  min-height: 0;
  display: flex;
  overflow: hidden;
  background: var(--bg-app, #f5f6f8);
  font-family: var(--font-sans, "Figtree", system-ui, sans-serif);
  color: var(--ink-strong, #0f1729);
}
.pp-app > .pp-body {
  flex: 1; min-width: 0;
  display: flex; flex-direction: column;
  overflow: hidden;       /* topbar sticks; .pp-content scrolls */
}
.pp-app > .pp-body > .pp-topbar { flex: none; }
.pp-app > .pp-body > .pp-content {
  flex: 1; min-height: 0;
  overflow-y: auto;
  display: flex; flex-direction: column; gap: 18px;
  padding: 18px 24px 28px;
}
/* Flex children inside a flex-column container default to
   flex-shrink:1 + min-height:auto, which CRUSHED the punch card
   from its natural ~280px down to ~67px so the page seemed to fit
   the viewport with nothing to scroll. Force every direct child to
   keep its natural size so the scroll engages. */
.pp-app > .pp-body > .pp-content > * { flex: none; }
.pp-app.pp-app-bare { /* main app sidebar is doing nav for us */ }
.pp-app.pp-app-bare > .pp-body { width: 100%; }
.pp-modal {
  position: relative;
  background: white;
  border-radius: 14px;
  width: min(520px, 92vw);
  max-height: 86vh;
  box-shadow: 0 20px 60px rgba(15,22,40,.30);
  overflow: hidden;
  display: flex; flex-direction: column;
}
.pp-modal-head {
  display: flex; align-items: center; justify-content: space-between;
  padding: 14px 18px; border-bottom: 1px solid var(--border, #e6e9ef);
}
.pp-modal-title { font-weight: 700; color: var(--ink-strong, #0f1729); font-size: 15px; }
.pp-modal-close {
  background: transparent; border: 0; font-size: 22px; cursor: pointer; color: var(--ink-muted, #676879);
  line-height: 1; padding: 4px 8px; border-radius: 6px;
}
.pp-modal-close:hover { background: rgba(0,0,0,.04); color: var(--ink-strong, #0f1729); }
.pp-modal-body { padding: 16px 18px; overflow-y: auto; flex: 1; }
.pp-modal-foot {
  display: flex; justify-content: flex-end; gap: 8px;
  padding: 12px 18px; border-top: 1px solid var(--border, #e6e9ef);
  background: var(--bg-subtle, #fafbfc);
}
.pp-field { display: block; margin-bottom: 10px; }
.pp-field > span {
  display: block; font-size: 11px; font-weight: 700; letter-spacing: .04em;
  text-transform: uppercase; color: var(--ink-muted, #676879);
  margin-bottom: 4px;
}
.pp-field input[type="date"],
.pp-field select,
.pp-field textarea {
  width: 100%; padding: 8px 10px; border: 1px solid var(--border, #e6e9ef);
  border-radius: 8px; font-family: inherit; font-size: 13px;
  background: white; color: var(--ink-strong, #0f1729);
}
.pp-field textarea { resize: vertical; min-height: 60px; }
.pp-modal-err {
  margin-top: 8px; padding: 8px 10px; border-radius: 6px;
  background: rgba(226,68,92,.10); color: #8a1024; font-size: 12.5px;
}
`;
// Inject once.
(function injectManagerCss() {
  if (typeof document === "undefined") return;
  if (document.getElementById("pp-manager-css")) return;
  const tag = document.createElement("style");
  tag.id = "pp-manager-css";
  tag.textContent = PP_MANAGER_EXTRA_CSS;
  document.head.appendChild(tag);
})();

Object.assign(window, {
  PpManagerApp, PpRequestModal, PpCalendarScreen,
  // Re-used by the employee module (My timeline) so it can show the
  // same preset chips + range summary card without duplicating logic.
  ppPresetRange, PP_RANGE_PRESETS, ppRangeSummary, ppFmtDur,
});
