// people-employee.jsx — employee "My time" shell
//
// Tabs:
//   My day        · live hero + today bar + recent timeline + balances + team-out
//   My timeline   · 14-day past timeline grouped by week
//   My leave      · leave hero + balances rings + my requests + holidays
//   Team calendar · read-only team calendar
//
// Class names match public/styles/people-employee.css verbatim
// (the prototype handoff bundle). Wiring is live to /api/people/*.

// ── Shared CSS injection (run once at file load) ─────────────
// The .pp-ts-* rules are the same visual language as the admin
// Timesheets card; we use them in both PpEmpTimelineFull (full
// page) and PpEmpRecentTimeline (My day widget). They were
// previously inside PpEmpTimelineFull's <style> tag, but that meant
// the My day widget rendered unstyled when the full page wasn't
// mounted. Injecting once here fixes both.
(function injectPpTsCss() {
  if (typeof document === "undefined") return;
  if (document.getElementById("pp-ts-shared-css")) return;
  const css = `
    .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); }
    .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-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(3, minmax(0,1fr)); }
      .pp-ts-stat:nth-child(3n) { 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-readonly { grid-template-columns:160px 78px 116px 1fr 58px !important; }
    .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; }
    .pp-ts-day-hrs { font-size:12.5px; color:var(--ink); text-align:right; }
    @media (max-width: 720px) {
      .pp-ts-day, .pp-ts-day-readonly { grid-template-columns:90px 70px 1fr auto !important; }
      .pp-ts-day-io, .pp-ts-day-hrs { display:none; }
    }
  `;
  const tag = document.createElement("style");
  tag.id = "pp-ts-shared-css";
  tag.textContent = css;
  document.head.appendChild(tag);
})();

// Visible window for the today bar: 07:00 – 19:00.
const PP_WIN_START = 7 * 60;
const PP_WIN_END   = 19 * 60;
const PP_WIN_SPAN  = PP_WIN_END - PP_WIN_START;

function ppM2P(m) {
  return Math.max(0, Math.min(100, ((m - PP_WIN_START) / PP_WIN_SPAN) * 100));
}
function ppParseHHMM(s) {
  if (!s) return null;
  const m = /^(\d{1,2}):(\d{2})$/.exec(String(s).trim());
  return m ? Number(m[1]) * 60 + Number(m[2]) : null;
}
function ppFmtDuration(mins) {
  if (mins == null || mins < 0) return "—";
  const h = Math.floor(mins / 60), m = Math.round(mins % 60);
  if (h && m) return `${h}h ${m}m`;
  if (h)      return `${h}h`;
  return `${m}m`;
}

// This week's logged hours (Mon → today). Sums the 14-day timeline for
// days before today, then adds the live `today.hoursLogged` so the
// figure stays current between the 60-second refreshes.
function ppWeekHours(me) {
  const today = (me && me.today) || {};
  const days = (me && me.timeline) || [];
  const now = new Date();
  const todayIso = now.toISOString().slice(0, 10);
  const dow = (now.getDay() + 6) % 7;                 // 0 = Monday
  const mon = new Date(now); mon.setDate(now.getDate() - dow);
  const monIso = mon.getFullYear() + "-" +
    String(mon.getMonth() + 1).padStart(2, "0") + "-" +
    String(mon.getDate()).padStart(2, "0");
  let sum = days
    .filter(d => d.iso >= monIso && d.iso < todayIso)
    .reduce((s, d) => s + (Number(d.hoursLogged) || 0), 0);
  sum += Number(today.hoursLogged) || 0;
  return sum;
}

// First name for the hero greeting (falls back to empty string).
function ppFirstName() {
  try {
    const u = window.api && window.api.getUser && window.api.getUser();
    return (u && (u.name || "").trim().split(/\s+/)[0]) || "";
  } catch { return ""; }
}

// Shared with the global HR greeter (punch-fab.jsx): the "acknowledged"
// key is the message's period + a content hash, stored under
// fb.hrAck:<userId>. So dismissing the card here (or the modal there)
// hides it everywhere until Ava's message actually CHANGES.
function ppHrHash(s) {
  let h = 5381;
  for (let i = 0; i < (s || "").length; i++) h = ((h << 5) + h + s.charCodeAt(i)) | 0;
  return (h >>> 0).toString(36);
}
function ppHrUserId() {
  try { const u = window.api && window.api.getUser && window.api.getUser(); return (u && u.id) || "anon"; }
  catch { return "anon"; }
}

// Live worked minutes + the "now" position on the today timeline.
// Worked = sum of WORK segment durations; for the open (live) work
// segment we use the wall-clock `now` instead of its stale `to`, so the
// figure ticks up second-by-second while clocked in (and freezes during
// a break, since the live segment is then a break, not work).
// `nowPct` mirrors PpTimeline's window maths (08:00–19:00, auto-widened)
// so a tag placed at nowPct% lines up exactly with the live dot.
function ppLiveWorked(today, now) {
  const segs = (today && today.segments) || [];
  const nowMin = now.getHours() * 60 + now.getMinutes() + now.getSeconds() / 60;
  let mins = 0;
  for (const s of segs) {
    if (s.kind === "work") mins += Math.max(0, (s.live ? nowMin : s.to) - s.from);
  }
  // Keep in sync with PpTimeline's window (08:00 → 19:00, auto-expanded).
  let ws = 480, we = 1140;
  if (segs.length) { ws = Math.min(ws, segs[0].from); we = Math.max(we, segs[segs.length - 1].to); }
  const span = Math.max(60, we - ws);
  const nowPct = Math.max(0, Math.min(100, ((nowMin - ws) / span) * 100));
  return { mins, nowPct };
}

function PpEmployeeApp({ activeTab, onTabChange, hideOwnSidebar = false } = {}) {
  const [internalTab, setInternalTab] = React.useState("day");
  const tab    = activeTab   || internalTab;
  const setTab = onTabChange || setInternalTab;
  const [me, setMe] = React.useState(null);
  const [requestOpen, setRequestOpen] = React.useState(false);

  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(); }, 60_000);
    return () => clearInterval(id);
  }, [reload]);
  // Cross-surface sync: when the floating FAB punches (or anyone
  // else dispatches the event), refresh immediately so the My day
  // hero shows the same state as the FAB popover.
  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]);

  return (
    <div className={"pp-app pp-app-emp" + (hideOwnSidebar ? " pp-app-bare" : "")}>
      {!hideOwnSidebar && (
        <PpSidebar
          brand="My time"
          subtitle={me && me.today && me.today.schedule
                      ? `${me.today.schedule.from}–${me.today.schedule.to}`
                      : null}
          items={[
            { id: "day",      label: "My day",        icon: "Home" },
            { id: "profile",  label: "My profile",    icon: "Users" },
            { id: "timeline", label: "My timeline",   icon: "Clock" },
            { id: "leave",    label: "My leave",      icon: "Calendar" },
            { id: "payroll",  label: "My payroll",    icon: "Coffee2" },
            { id: "calendar", label: "Team calendar", icon: "Users" },
          ]}
          active={tab} onChange={setTab}/>
      )}

      <div className="pp-body">
        <PpTopbar
          crumbs={["My time", empTabLabel(tab)]}
          actions={
            <button className="pp-btn pp-btn-primary" onClick={() => setRequestOpen(true)}>
              <PpIcon name="Plus"/> Apply for leave
            </button>
          }/>

        <div className="pp-content">
          {tab === "day"      && <PpEmpDay me={me} onReload={reload} onApply={() => setRequestOpen(true)}/>}
          {tab === "profile"  && (window.PpProfile
            ? React.createElement(window.PpProfile, { userId: null })
            : <div className="pp-card" style={{ padding: 20, color: "var(--ink-muted)" }}>Loading profile…</div>)}
          {tab === "timeline" && <PpEmpTimelineFull/>}
          {tab === "leave"    && <PpEmpLeave me={me} onApply={() => setRequestOpen(true)} onReload={reload}/>}
          {tab === "payroll"  && (window.PpEmpPayroll ? React.createElement(window.PpEmpPayroll) : null)}
          {tab === "calendar" && <PpEmpCalendar/>}
        </div>
      </div>

      {requestOpen && <PpRequestModal onClose={() => { setRequestOpen(false); reload(); }}/>}
    </div>
  );
}
function empTabLabel(t) {
  return ({ day: "My day", profile: "My profile", timeline: "My timeline", leave: "My leave", payroll: "My payroll", calendar: "Team calendar" })[t] || "My day";
}

// ── My Day ────────────────────────────────────────────────────
function PpEmpDay({ me, onReload, onApply }) {
  // Compact dashboard: a slim blue punch hero (AM/PM clock + status +
  // Break/Check-out, with the live today timeline running underneath),
  // then three small stat tiles, then a two-column grid — left = recent
  // days timeline, right = leave balance + who's out today.
  return (
    <>
      <PpEmpHero me={me} onReload={onReload}/>
      <PpEmpHrCard/>
      <div className="pp-emp-2col">
        <div className="pp-emp-stack">
          <PpEmpRecentTimeline me={me}/>
        </div>
        <div className="pp-emp-stack">
          <PpEmpBalancesCompact me={me} onApply={onApply}/>
          <PpEmpTeamOut me={me}/>
        </div>
      </div>
      <PpEmpFabToggle/>
    </>
  );
}

// ── AI HR Manager card ("Ava") — personal attendance coaching ──
// Attractive, dismissible card. "Got it, hide" remembers this exact
// message (period + content hash) so it stays hidden until Ava's
// message changes — shared with the check-in greeter modal.
function PpEmpHrCard() {
  const [insight, setInsight] = React.useState(undefined); // undefined = loading
  const [coach, setCoach] = React.useState("Ava");
  const [hidden, setHidden] = React.useState(false);

  const ackKey = "fb.hrAck:" + ppHrUserId();
  const msgKey = (ins) => (ins.period_key || "") + ":" + ppHrHash(ins.message || "");

  React.useEffect(() => {
    let alive = true;
    (async () => {
      try {
        const r = await window.api.people.hr.me();
        if (!alive) return;
        const ins = r && r.insight ? r.insight : null;
        if (r && r.coach) setCoach(r.coach);
        setInsight(ins);
        if (ins) {
          let acked = ""; try { acked = localStorage.getItem(ackKey) || ""; } catch {}
          if (acked === msgKey(ins)) setHidden(true);
        }
      } catch { if (alive) setInsight(null); }
    })();
    return () => { alive = false; };
  }, [ackKey]);

  function dismiss() {
    if (insight) { try { localStorage.setItem(ackKey, msgKey(insight)); } catch {} }
    setHidden(true);
  }

  if (insight === undefined || insight === null || hidden) return null;
  const sev = insight.severity || "info";
  const sevLabel = ({ ok: "On track", info: "Heads-up", watch: "Worth a look", concern: "Needs attention" })[sev] || "Note";
  return (
    <div className={"pp-hr-card pp-hr-" + sev}>
      <div className="pp-hr-accent"/>
      <div className="pp-hr-main">
        <div className="pp-hr-top">
          <div className="pp-hr-avatar"><PpIcon name="Sparkle" size={17}/></div>
          <div className="pp-hr-id">
            <div className="pp-hr-name">{coach}</div>
            <div className="pp-hr-tag">AI HR Manager</div>
          </div>
          <span className={"pp-hr-sev pp-hr-sev-" + sev}>
            <span className="pp-hr-sev-dot"/>{sevLabel}
          </span>
        </div>
        {insight.headline && <div className="pp-hr-headline">{insight.headline}</div>}
        <div className="pp-hr-msg">{insight.message}</div>
        <div className="pp-hr-foot">
          {insight.period_key && <span className="pp-hr-when">Updated {insight.period_key}</span>}
          <button className="pp-hr-dismiss" onClick={dismiss}>Got it, hide</button>
        </div>
      </div>
    </div>
  );
}

// ── Stat row — three compact metric tiles under the hero ──────
function PpEmpStatRow({ me }) {
  const today = (me && me.today) || {};
  const weekHours = ppWeekHours(me);
  const weeklyTarget = Number(today.weeklyHours) || 45;
  const breakMins = Number(today.breakMins) || 0;
  const pct = weeklyTarget ? Math.round(Math.min(100, (weekHours / weeklyTarget) * 100)) : 0;
  return (
    <div className="pp-md-stats">
      <div className="pp-md-stat">
        <div className="pp-md-stat-l">Hours today</div>
        <div className="pp-md-stat-v">{ppFmtHours(today.hoursLogged || 0)}</div>
        <div className="pp-md-stat-s">{today.in ? "Since " + today.in : "Not punched in"}</div>
      </div>
      <div className="pp-md-stat">
        <div className="pp-md-stat-l">This week</div>
        <div className="pp-md-stat-v">
          {ppFmtHours(weekHours)}
          <span className="pp-md-stat-of"> / {ppFmtHours(weeklyTarget)}</span>
        </div>
        <div className="pp-md-stat-s">{pct}% of target</div>
      </div>
      <div className="pp-md-stat">
        <div className="pp-md-stat-l">Break today</div>
        <div className="pp-md-stat-v">{breakMins}m</div>
        <div className="pp-md-stat-s">{breakMins ? "logged" : "none yet"}</div>
      </div>
    </div>
  );
}

// ── Compact leave-balance card (My Day right column) ──────────
function PpEmpBalancesCompact({ me, onApply }) {
  const items = (me && me.balances) || [];
  return (
    <div className="pp-card pp-md-bal">
      <div className="pp-card-head">
        <div className="pp-card-title">Leave balance</div>
        <div className="pp-card-sub" style={{ marginLeft: "auto" }}>{items.length}</div>
      </div>
      <div className="pp-md-bal-list">
        {items.map(b => {
          const total = Number(b.total) || 0;
          const used = Number(b.used) || 0;
          const remaining = Math.max(0, total - used);
          return (
            <div key={b.id} className="pp-md-bal-row">
              <span className="pp-md-bal-dot" style={{ background: b.color || "#0073ea" }}/>
              <span className="pp-md-bal-name">{b.name}</span>
              <span className="pp-md-bal-num">{remaining}</span>
            </div>
          );
        })}
        {items.length === 0 && (
          <div className="pp-md-empty">No leave types yet — ask an admin to set them up.</div>
        )}
      </div>
      {onApply && (
        <button className="pp-md-bal-btn" onClick={onApply}>
          <PpIcon name="Plus"/> Request leave
        </button>
      )}
    </div>
  );
}

// ── Floating widget show/hide toggle ─────────────────────────
// Lives at the bottom of My Day so users can suppress the floating
// check-in/out button (handy on screens where it overlaps something).
// Hide preference is per-user, persisted in localStorage. We dispatch
// `flowboard:punchFab:visibility` so the FAB updates immediately
// without a reload.
function PpEmpFabToggle() {
  const userId = React.useMemo(() => {
    try {
      const u = window.api && window.api.getUser && window.api.getUser();
      return (u && u.id) || null;
    } catch { return null; }
  }, []);
  const hiddenKey = userId ? `fb.punchFab.hidden:${userId}` : "fb.punchFab.hidden";
  const [hidden, setHidden] = React.useState(() => {
    try { return localStorage.getItem(hiddenKey) === "1"; } catch { return false; }
  });
  React.useEffect(() => {
    try { setHidden(localStorage.getItem(hiddenKey) === "1"); } catch {}
  }, [hiddenKey]);

  function toggle() {
    const next = !hidden;
    setHidden(next);
    try {
      if (next) localStorage.setItem(hiddenKey, "1");
      else localStorage.removeItem(hiddenKey);
    } catch {}
    try {
      window.dispatchEvent(new CustomEvent("flowboard:punchFab:visibility", {
        detail: { hidden: next },
      }));
    } catch {}
    if (window.fbToast) {
      window.fbToast(next ? "Floating widget hidden" : "Floating widget shown", 2200);
    }
  }
  return (
    <div style={{
      marginTop: 18, padding: "12px 16px",
      border: "1px solid var(--border-row, #eaecf1)",
      borderRadius: 10, background: "var(--bg-card, #fff)",
      display: "flex", alignItems: "center", gap: 12,
    }}>
      <div style={{ flex: 1, minWidth: 0 }}>
        <div style={{ fontWeight: 600, color: "var(--ink-strong, #0f1729)", fontSize: 13 }}>
          Floating check-in widget
        </div>
        <div style={{ fontSize: 11.5, color: "var(--ink-muted, #6c7385)", marginTop: 2 }}>
          The small green pill that lets you punch in/out from any page.
        </div>
      </div>
      <button className="pp-btn pp-btn-primary" onClick={toggle} style={{ flexShrink: 0 }}>
        {hidden ? "Show widget" : "Hide widget"}
      </button>
    </div>
  );
}

// ── Year-to-date attendance card ─────────────────────────────
// Shows the user's working-days breakdown for this calendar year:
//   "94 worked · 100 working days so far · 150 days into the year"
// Data comes from /api/people/attendance-stats which walks the
// resolveRange output day-by-day, so the numbers match what's on
// the timeline + calendar exactly.
function PpEmpAttendanceCard() {
  const [stats, setStats] = React.useState(null);
  const [err, setErr]     = React.useState(null);
  React.useEffect(() => {
    let alive = true;
    (async () => {
      try {
        const r = await window.api.people.attendanceStats({});
        if (alive) setStats(r);
      } catch (e) {
        if (alive) setErr((e && e.body && e.body.error) || "stats_failed");
      }
    })();
    return () => { alive = false; };
  }, []);
  if (err) return null;
  if (!stats) {
    return (
      <div className="pp-emp-att">
        <div className="pp-emp-att-load">Loading attendance…</div>
      </div>
    );
  }
  const worked   = Number(stats.worked_days)   || 0;
  const working  = Number(stats.working_days)  || 0;
  const calendar = Number(stats.calendar_days) || 0;
  const leaveD   = Number(stats.leave_days)    || 0;
  const holiday  = Number(stats.holiday_days)  || 0;
  const absent   = Number(stats.absent_days)   || 0;
  const pct = working > 0 ? Math.min(100, Math.round((worked / working) * 100)) : 0;
  return (
    <div className="pp-emp-att">
      <div className="pp-emp-att-head">
        <div>
          <div className="pp-emp-att-eyebrow">ATTENDANCE · {stats.year}</div>
          <div className="pp-emp-att-headline">
            <span className="pp-emp-att-big">{worked}</span>
            <span className="pp-emp-att-of">/ {working} working days</span>
          </div>
          <div className="pp-emp-att-sub">
            {pct}% attendance · {calendar} days into the year
          </div>
        </div>
        <div className="pp-emp-att-ring">
          <PpEmpRingMini pct={pct}/>
        </div>
      </div>
      <div className="pp-emp-att-bar">
        <span className="pp-emp-att-bar-worked" style={{ width: pct + "%" }}/>
      </div>
      <div className="pp-emp-att-stats">
        <PpEmpAttStat label="Worked"    value={worked}  tone="ok"/>
        <PpEmpAttStat label="On leave"  value={leaveD}  tone="leave"/>
        <PpEmpAttStat label="Holidays"  value={holiday} tone="holiday"/>
        <PpEmpAttStat label="Absent"    value={absent}  tone={absent ? "warn" : null}/>
      </div>
      <style>{`
        .pp-emp-att {
          background: var(--bg-card, #fff);
          border: 1px solid var(--border-row, #eaecf1);
          border-radius: 12px;
          padding: 16px 18px;
          margin: 14px 0;
          color: var(--ink-strong, #0f1729);
        }
        .pp-emp-att-load {
          color: var(--ink-muted, #6c7385);
          font-size: 12.5px;
        }
        .pp-emp-att-head {
          display: flex; align-items: center; gap: 14px;
        }
        .pp-emp-att-eyebrow {
          font-size: 10.5px; font-weight: 700; letter-spacing: .12em;
          color: var(--ink-muted, #6c7385);
        }
        .pp-emp-att-headline {
          margin-top: 2px; display: flex; align-items: baseline; gap: 8px;
        }
        .pp-emp-att-big {
          font-size: 32px; font-weight: 800;
          font-variant-numeric: tabular-nums;
          color: var(--ink-strong, #0f1729);
          letter-spacing: -0.02em;
        }
        .pp-emp-att-of {
          font-size: 14px; font-weight: 600; color: var(--ink-muted, #6c7385);
        }
        .pp-emp-att-sub {
          margin-top: 2px; font-size: 12.5px; color: var(--ink-muted, #6c7385);
        }
        .pp-emp-att-ring { margin-left: auto; }
        .pp-emp-att-bar {
          margin-top: 12px;
          height: 6px; border-radius: 999px;
          background: #eef0f5; overflow: hidden;
        }
        .pp-emp-att-bar-worked {
          display: block; height: 100%;
          background: linear-gradient(90deg, #0ea15c, #066039);
        }
        .pp-emp-att-stats {
          display: grid; grid-template-columns: repeat(4, 1fr);
          gap: 10px; margin-top: 12px;
        }
        .pp-emp-att-stat {
          padding: 8px 10px; border-radius: 8px;
          background: #f7f8fb;
          border: 1px solid #eef0f5;
        }
        .pp-emp-att-stat.tone-ok      { background: #ecfaf1; border-color: #b9eccc; }
        .pp-emp-att-stat.tone-leave   { background: #f5ecfa; border-color: #ddc4ee; }
        .pp-emp-att-stat.tone-holiday { background: #fff3e0; border-color: #fbb461; }
        .pp-emp-att-stat.tone-warn    { background: #ffe9ec; border-color: #ffb4be; }
        .pp-emp-att-stat-label {
          font-size: 10px; font-weight: 700; letter-spacing: .08em;
          color: var(--ink-muted, #6c7385); text-transform: uppercase;
        }
        .pp-emp-att-stat-value {
          font-size: 18px; font-weight: 800;
          font-variant-numeric: tabular-nums;
          color: var(--ink-strong, #0f1729);
        }
        @media (max-width: 720px) {
          .pp-emp-att-stats { grid-template-columns: repeat(2, 1fr); }
        }
      `}</style>
    </div>
  );
}

function PpEmpAttStat({ label, value, tone }) {
  return (
    <div className={`pp-emp-att-stat ${tone ? "tone-" + tone : ""}`}>
      <div className="pp-emp-att-stat-label">{label}</div>
      <div className="pp-emp-att-stat-value">{value}</div>
    </div>
  );
}

function PpEmpRingMini({ pct, size = 56, stroke = 6, color = "#0ea15c" }) {
  const r = (size - stroke) / 2;
  const c = 2 * Math.PI * r;
  const p = Math.max(0, Math.min(100, Number(pct) || 0));
  const dash = (p / 100) * c;
  return (
    <svg width={size} height={size} viewBox={`0 0 ${size} ${size}`}>
      <circle cx={size/2} cy={size/2} r={r}
              stroke="#eef0f5" strokeWidth={stroke} fill="none"/>
      <circle cx={size/2} cy={size/2} r={r}
              stroke={color} strokeWidth={stroke} fill="none"
              strokeDasharray={`${dash} ${c}`}
              strokeLinecap="round"
              transform={`rotate(-90 ${size/2} ${size/2})`}/>
      <text x={size/2} y={size/2 + 4} textAnchor="middle"
            fontSize="13" fontWeight="800" fill="#0f1729"
            style={{ fontVariantNumeric: "tabular-nums" }}>
        {p}%
      </text>
    </svg>
  );
}

// ── Hero: compact blue panel — live clock + status + punch ───
function PpEmpHero({ me, onReload }) {
  const [now, setNow] = React.useState(new Date());
  const [punching, setPunching] = React.useState(false);
  React.useEffect(() => {
    const id = setInterval(() => setNow(new Date()), 1000);
    return () => clearInterval(id);
  }, []);
  const today = (me && me.today) || {};
  const mm = String(now.getMinutes()).padStart(2, "0");
  const ss = String(now.getSeconds()).padStart(2, "0");
  // 12-hour display for the hero clock.
  const h12  = ((now.getHours() + 11) % 12) + 1;
  const ampm = now.getHours() < 12 ? "AM" : "PM";

  // Derive state from server's segments / status + lastPunchKind so
  // holiday / leave days don't hide the check-out + break buttons when
  // the employee actually has an open punch.
  const live = (today.segments || []).find(s => s.live);
  const isOnBreak = (live && live.kind === "break") || today.lastPunchKind === "break_start";
  const isClockedIn = today.status === "in" || today.status === "remote" || today.status === "late"
    || (!!today.in && (today.lastPunchKind === "in" || today.lastPunchKind === "break_end"));
  const state = isOnBreak ? "break" : (isClockedIn ? "in" : "out");

  const chip =
    state === "in"    ? { tone: "ok",   label: "Clocked in", meta: today.in ? `since ${today.in}` : "live" } :
    state === "break" ? { tone: "warn", label: "On break",   meta: "paused" } :
                        { tone: "off",  label: today.status === "leave" ? "On leave"
                                              : today.status === "holiday" ? "Holiday"
                                              : "Not clocked in",
                                        meta: today.status === "holiday" ? (today.holiday || "")
                                              : today.status === "leave" ? (today.leaveType || "")
                                              : "punch in to start" };

  async function punch(kind) {
    if (punching) return;            // guard against double-tap → duplicate punches
    setPunching(true);
    try {
      await window.api.people.punch({ kind });
      onReload && onReload();
      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 errCode = body && body.error;
      const msg = (body && (body.message || body.error)) || (e && e.message) || "Punch failed";
      const human = /migration_pending/i.test(String(errCode))
        ? "Punch storage isn't ready yet — apply migration 044 on the server, then try again."
        : /timeout|network/i.test(String(errCode))
        ? "Punch didn’t save — check your connection and tap again."
        : msg;
      if (window.fbToast) window.fbToast(human, 5000);
      else alert(human);
      console.error("[people.punch]", e);
    } finally {
      setPunching(false);
    }
  }

  const greet = now.getHours() < 12 ? "Good morning"
              : now.getHours() < 17 ? "Good afternoon" : "Good evening";
  const name = ppFirstName();
  const segs = today.segments || [];
  const showLive = state === "in" || state === "break";
  // Running worked-hours total. Live from segments while clocked in;
  // falls back to the server's hoursLogged if segments aren't present.
  const { mins: liveWorkedMins, nowPct } = ppLiveWorked(today, now);
  const workedMins = segs.length ? liveWorkedMins : (Number(today.hoursLogged) || 0) * 60;

  return (
    <div className="pp-md-hero">
      <div className="pp-md-hero-top">
        <div className="pp-md-hero-clockcol">
          <div className="pp-md-hero-eyebrow">CURRENT TIME</div>
          <div className="pp-md-hero-clock">
            {h12}:{mm}<span className="pp-md-hero-secs">:{ss} {ampm}</span>
          </div>
          <div className="pp-md-hero-greet">{greet}{name ? ", " + name : ""}.</div>
          <div className="pp-md-hero-worked">
            <span className="pp-md-hero-worked-lbl">Hours today</span>
            <span className="pp-md-hero-worked-val">{ppFmtDuration(workedMins)}</span>
            {today.in && <span className="pp-md-hero-worked-since">since {today.in}</span>}
          </div>
        </div>

        <div className="pp-md-hero-actcol">
          <div className={`pp-md-chip pp-md-chip-${chip.tone}`}>
            <span className="pp-md-chip-dot"/>
            <span>{chip.label}{chip.meta ? " · " + chip.meta : ""}</span>
          </div>
          <div className="pp-md-hero-btns">
            {state === "in" && (
              <>
                <button className="pp-md-btn pp-md-btn-ghost" disabled={punching} onClick={() => punch("break_start")}>
                  <PpIcon name="Pause"/> Break
                </button>
                <button className="pp-md-btn pp-md-btn-warn" disabled={punching} onClick={() => punch("out")}>
                  <PpIcon name="Logout"/> {punching ? "Saving…" : "Check out"}
                </button>
              </>
            )}
            {state === "break" && (
              <>
                <button className="pp-md-btn pp-md-btn-go" disabled={punching} onClick={() => punch("break_end")}>
                  <PpIcon name="Play"/> {punching ? "Saving…" : "Resume"}
                </button>
                <button className="pp-md-btn pp-md-btn-warn" disabled={punching} onClick={() => punch("out")}>
                  <PpIcon name="Logout"/> Check out
                </button>
              </>
            )}
            {state === "out" && (
              <button className="pp-md-btn pp-md-btn-go" disabled={punching} onClick={() => punch("in")}>
                <PpIcon name="Login"/> {punching ? "Saving…" : (today.status === "leave" ? "Override · check in" : "Check in")}
              </button>
            )}
          </div>
        </div>
      </div>

      {/* Live today timeline — dashed baseline + work/break line + dots.
          Override the timeline's --border / --bg-card tokens so the
          dashed line and dot rings read on the blue panel. */}
      <div className="pp-md-hero-tl" style={{ "--border": "rgba(255,255,255,.32)", "--bg-card": "#0b5cad" }}>
        <div className="pp-md-tl-strip">
          <PpTimeline segments={segs} height={22}/>
          {showLive && (
            <div className="pp-md-tl-tag" style={{ left: Math.min(94, Math.max(6, nowPct)) + "%" }}>
              {ppFmtDuration(workedMins)}
            </div>
          )}
        </div>
        <div className="pp-md-hero-tl-labels">
          <span>{today.in ? "In " + today.in : "Not punched in"}</span>
          <span>
            {showLive
              ? <b style={{ color: "#ffd9a8" }}>● Live · {String(now.getHours()).padStart(2, "0")}:{mm}</b>
              : (today.out ? "Out " + today.out : "")}
          </span>
        </div>
      </div>
    </div>
  );
}

// ── Today's hours bar ────────────────────────────────────────
function PpEmpTodayBar({ me }) {
  const today = (me && me.today) || {};
  const segs = today.segments || [];
  const schS = today.schedule && ppParseHHMM(today.schedule.from);
  const schE = today.schedule && ppParseHHMM(today.schedule.to);
  const inM  = ppParseHHMM(today.in);
  const outM = ppParseHHMM(today.out);

  const now = new Date();
  const nowM = now.getHours() * 60 + now.getMinutes();
  const showNow = nowM >= PP_WIN_START && nowM <= PP_WIN_END && (today.status === "in" || today.status === "late" || today.status === "remote");

  const ticks = [7, 9, 11, 13, 15, 17, 19];

  // Worked / remaining / expected out summary numbers.
  const workedMins = (today.hoursLogged || 0) * 60;
  const targetMins = today.schedule && schE && schS ? (schE - schS) : 0;
  const breakMins = today.breakMins || 0;
  const remainingMins = Math.max(0, targetMins - workedMins - breakMins);
  const expectedOut = schE != null ? `${String(Math.floor(schE/60)).padStart(2,"0")}:${String(schE%60).padStart(2,"0")}` : "—";

  return (
    <div className="pp-card pp-emp-bar-card">
      <div className="pp-card-head">
        <div>
          <div className="pp-card-title">Today's hours</div>
          <div className="pp-card-sub">Live · saves automatically with each punch</div>
        </div>
        <div className="pp-emp-bar-legend">
          <span><i className="work"/>Working</span>
          <span><i className="brk"/>Break</span>
          <span><i className="sched"/>Scheduled window</span>
          <span><i className="now"/>Now</span>
        </div>
      </div>

      <div className="pp-emp-bar-wrap">
        {/* Shared graphical timeline. When clocked in, the open segment
            marches and the "now" dot pulses orange so it reads as live. */}
        <div style={{ padding: "10px 0 2px" }}>
          <PpTimeline segments={segs} height={18}/>
        </div>
        <div style={{ display: "flex", justifyContent: "space-between", fontSize: 11.5, color: "var(--ink-muted)", marginTop: 6 }}>
          <span>{today.in ? "In " + today.in : "Not punched in"}</span>
          <span>
            {showNow
              ? <b style={{ color: "#d97800" }}>● Live · {String(now.getHours()).padStart(2, "0")}:{String(now.getMinutes()).padStart(2, "0")}</b>
              : (today.out ? "Out " + today.out : "")}
          </span>
        </div>
      </div>

      {/* Summary strip */}
      <div className="pp-emp-bar-summary">
        <div><div className="pp-emp-mini-label">Worked today</div>
             <div className="pp-emp-mini-val">{ppFmtDuration(workedMins)}</div></div>
        <div><div className="pp-emp-mini-label">Break</div>
             <div className="pp-emp-mini-val">{breakMins}m</div></div>
        <div><div className="pp-emp-mini-label">Remaining</div>
             <div className="pp-emp-mini-val">{remainingMins ? ppFmtDuration(remainingMins) : "—"}</div></div>
        <div><div className="pp-emp-mini-label">Expected out</div>
             <div className="pp-emp-mini-val">{expectedOut}</div></div>
        <div><div className="pp-emp-mini-label">Status</div>
             <div className="pp-emp-mini-val"><span className={`pp-pill-status ${today.status || "out"}`}>
               {today.status === "in" ? "On track"
                : today.status === "late" ? "Late"
                : today.status === "leave" ? "On leave"
                : today.status === "holiday" ? "Holiday"
                : "—"}
             </span></div></div>
      </div>
    </div>
  );
}

function PpEmpMarker({ pct, icon, primary, secondary, muted }) {
  return (
    <div className={`pp-emp-marker ${muted ? "muted" : ""}`} style={{ left: pct + "%" }}>
      <div className="pp-emp-marker-card">
        <div className="pp-emp-marker-ico"><PpIcon name={icon}/></div>
        <div>
          <div className="pp-emp-marker-time">{primary}</div>
          <div className="pp-emp-marker-lbl">{secondary}</div>
        </div>
      </div>
      <div className="pp-emp-marker-stem"/>
    </div>
  );
}

// ── Recent timeline (compact widget on My day) ─────────────
// Compact sibling of PpEmpTimelineFull. Same row layout (status
// pill · in/out · segments · hours) AND the same date filter, so the
// My day widget behaves identically to the dedicated My timeline page.
// Default range is "This month".
function PpEmpRecentTimeline({ me }) {
  const presets = window.PP_RANGE_PRESETS || [];
  const presetRange = window.ppPresetRange || (() => ({ key: "thismonth", from: "", to: "" }));
  const rangeSummary = window.ppRangeSummary;
  const fmtDur = window.ppFmtDur;

  const [range, setRange] = React.useState(() => presetRange("thismonth"));
  const [days, setDays]   = React.useState(() => ((me && me.timeline) || []).slice().reverse());
  const [loading, setLoading] = React.useState(false);

  // Re-fetch whenever the range changes. The initial render uses the
  // bootstrap payload so the card is never empty during the first
  // round-trip.
  React.useEffect(() => {
    let alive = true;
    setLoading(true);
    window.api.people.timeline({ from: range.from, to: range.to })
      .then(r => { if (alive) { setDays((r.days || []).slice().reverse()); setLoading(false); } })
      .catch(() => { if (alive) setLoading(false); });
    return () => { alive = false; };
  }, [range]);

  function setCustom(field, val) {
    setRange(r => ({ ...r, key: "custom", [field]: val }));
  }
  const sm = days.length && rangeSummary ? rangeSummary(days) : null;

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

  return (
    <div className="pp-card">
      <div className="pp-card-head">
        <div>
          <div className="pp-card-title">My timeline</div>
          <div className="pp-card-sub">Pick a date range to see your attendance</div>
        </div>
      </div>

      <div className="pp-ts-filter">
        <div className="pp-ts-filter-row">
          <select className="pp-ts-preset-sel"
                  value={range.key}
                  onChange={e => setRange(presetRange(e.target.value))}
                  aria-label="Date preset">
            {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">
          {presets.slice(0, 9).map(p => (
            <button key={p.key}
                    className={"pp-ts-chip" + (range.key === p.key ? " is-on" : "")}
                    onClick={() => setRange(presetRange(p.key))}>
              {p.label}
            </button>
          ))}
        </div>
      </div>

      {loading && days.length === 0 ? (
        <div style={{ padding: 32, textAlign: "center", color: "var(--ink-muted)" }}>Loading…</div>
      ) : days.length === 0 ? (
        <div style={{ padding: 32, textAlign: "center", color: "var(--ink-muted)" }}>
          No days in this range.
        </div>
      ) : (
        <>
          {sm && (
            <div className="pp-ts-summary">
              <div className="pp-ts-summary-head">
                <span className="pp-ts-summary-name">Recent</span>
                <span className="pp-ts-summary-range">{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 ? ` · ${fmtDur(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 pp-ts-day-readonly">
                <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>
                <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 ? fmtDur(Math.round(d.hoursLogged * 60)) : "—"}</span>
              </div>
            );
          })}
        </>
      )}
    </div>
  );
}

// ── My timeline — read-only mirror of the admin Timesheets layout
// Uses the same date presets, range summary card and per-day rows as
// the admin Timesheets, but scoped to the current user (no employee
// picker) and without the Edit button (employees can't edit their
// own punches).
function PpEmpTimelineFull() {
  const presets = window.PP_RANGE_PRESETS || [];
  const presetRange = window.ppPresetRange || (() => ({ key: "thismonth", from: "", to: "" }));
  const rangeSummary = window.ppRangeSummary;
  const fmtDur = window.ppFmtDur;

  const [range, setRange] = React.useState(() => presetRange("thismonth"));
  const [days, setDays]   = React.useState(null);
  const [loading, setLoading] = React.useState(false);

  React.useEffect(() => {
    let alive = true;
    setLoading(true);
    window.api.people.timeline({ from: range.from, to: range.to })
      .then(r => { if (alive) { setDays((r.days || []).slice().reverse()); setLoading(false); } })
      .catch(() => { if (alive) { setDays([]); setLoading(false); } });
    return () => { alive = false; };
  }, [range]);

  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"],
  };

  const sm = days && rangeSummary ? rangeSummary(days) : null;
  const fmtRange = (() => {
    if (!range.from || !range.to) return "";
    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-card">
      <div className="pp-card-head">
        <div>
          <div className="pp-card-title">My timeline</div>
          <div className="pp-card-sub">Choose a date range to see your attendance history</div>
        </div>
      </div>

      <div className="pp-ts-filter">
        <div className="pp-ts-filter-row">
          <select className="pp-ts-preset-sel"
                  value={range.key}
                  onChange={e => setRange(presetRange(e.target.value))}
                  aria-label="Date preset">
            {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">
          {presets.slice(0, 9).map(p => (
            <button key={p.key}
                    className={"pp-ts-chip" + (range.key === p.key ? " is-on" : "")}
                    onClick={() => setRange(presetRange(p.key))}>
              {p.label}
            </button>
          ))}
        </div>
      </div>

      {loading || days == null ? (
        <div style={{ padding: 40, textAlign: "center", color: "var(--ink-muted)" }}>Loading…</div>
      ) : days.length === 0 ? (
        <div style={{ padding: 40, textAlign: "center", color: "var(--ink-muted)" }}>No days in this range.</div>
      ) : (
        <>
          {sm && (
            <div className="pp-ts-summary">
              <div className="pp-ts-summary-head">
                <span className="pp-ts-summary-name">My attendance</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 ? ` · ${fmtDur(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 pp-ts-day-readonly">
                <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>
                <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 ? fmtDur(Math.round(d.hoursLogged * 60)) : "—"}</span>
              </div>
            );
          })}
        </>
      )}

    </div>
  );
}

function PpEmpWeekGroupedTimeline({ days, title, subtitle }) {
  // Group days into ISO-week buckets (Mon start).
  const groups = React.useMemo(() => {
    const out = new Map();
    for (const d of days) {
      const dt = new Date(d.iso + "T00:00:00");
      const day = (dt.getDay() + 6) % 7; // Mon=0..Sun=6
      const monday = new Date(dt); monday.setDate(dt.getDate() - day);
      const key = monday.toISOString().slice(0, 10);
      const label = (() => {
        const today = new Date(); today.setHours(0,0,0,0);
        const diff = Math.round((today.getTime() - monday.getTime()) / 86_400_000);
        if (diff < 7)  return "This week";
        if (diff < 14) return "Last week";
        if (diff < 21) return "2 weeks ago";
        return monday.toLocaleDateString("en-US", { month: "short", day: "numeric" });
      })();
      if (!out.has(key)) out.set(key, { key, label, days: [], monday });
      out.get(key).days.push(d);
    }
    return Array.from(out.values()).sort((a, b) => b.monday - a.monday);
  }, [days]);

  return (
    <div className="pp-card">
      <div className="pp-card-head">
        <div>
          <div className="pp-card-title">{title}</div>
          <div className="pp-card-sub">{subtitle}</div>
        </div>
        <div className="pp-emp-bar-legend">
          <span><i className="work"/>Worked</span>
          <span><i className="brk"/>Break</span>
        </div>
      </div>

      <div className="pp-emp-wk-grid">
        <div className="pp-emp-wk-header">
          <div></div>
          <div className="pp-emp-rows-ticks">
            {[7, 9, 11, 13, 15, 17, 19].map(h => (
              <div key={h} style={{ left: ppM2P(h * 60) + "%" }}>{h}</div>
            ))}
          </div>
          <div className="pp-emp-wk-totlbl">Total</div>
        </div>
        {groups.length === 0 && (
          <div style={{ padding: 24, textAlign: "center", color: "var(--ink-muted)" }}>
            No history yet — your first punch starts the clock.
          </div>
        )}
        {groups.map(g => {
          const wkTotal = g.days.reduce((s, d) => s + (Number(d.hoursLogged) || 0), 0);
          const first = g.days[g.days.length - 1];
          const last  = g.days[0];
          const range = first && last
            ? `${shortDate(first.iso)} – ${shortDate(last.iso)}`
            : "";
          return (
            <React.Fragment key={g.key}>
              <div className="pp-emp-wk-sep">
                <div className="pp-emp-wk-sep-l">
                  <span className="pp-emp-wk-name">{g.label}</span>
                  <span className="pp-emp-wk-range">{range}</span>
                </div>
                <div/>
                <div className="pp-emp-wk-sum">
                  <span>{ppFmtHours(wkTotal)}</span>
                </div>
              </div>
              {g.days.map(d => <PpEmpDayRow key={d.iso} day={d}/>)}
            </React.Fragment>
          );
        })}
      </div>
    </div>
  );
}

function shortDate(iso) {
  const d = new Date(iso + "T00:00:00");
  return d.toLocaleDateString("en-US", { month: "short", day: "numeric" });
}

function PpEmpDayRow({ day }) {
  const isToday = day.iso === new Date().toISOString().slice(0, 10);
  if (day.status === "holiday") {
    return (
      <div className="pp-emp-row is-off">
        <div className="pp-emp-row-date">
          <div className="pp-emp-row-dow">{(["Sun","Mon","Tue","Wed","Thu","Fri","Sat"])[new Date(day.iso+"T00:00:00").getDay()]}</div>
          <div className="pp-emp-row-d">{new Date(day.iso+"T00:00:00").getDate()}</div>
        </div>
        <div className="pp-emp-row-bar">
          <div className="pp-emp-row-off hol">{day.holiday || "Holiday"}</div>
        </div>
        <div className="pp-emp-row-total"><span className="pp-emp-row-off-lbl">Holiday</span></div>
      </div>
    );
  }
  if (day.status === "out") {
    // Weekend / no shift — render light off-row.
    return (
      <div className="pp-emp-row is-off">
        <div className="pp-emp-row-date">
          <div className="pp-emp-row-dow">{(["Sun","Mon","Tue","Wed","Thu","Fri","Sat"])[new Date(day.iso+"T00:00:00").getDay()]}</div>
          <div className="pp-emp-row-d">{new Date(day.iso+"T00:00:00").getDate()}</div>
        </div>
        <div className="pp-emp-row-bar">
          <div className="pp-emp-row-off">No shift</div>
        </div>
        <div className="pp-emp-row-total"><span className="pp-emp-row-off-lbl">—</span></div>
      </div>
    );
  }
  if (day.status === "absent") {
    return (
      <div className="pp-emp-row is-off">
        <div className="pp-emp-row-date">
          <div className="pp-emp-row-dow">{(["Sun","Mon","Tue","Wed","Thu","Fri","Sat"])[new Date(day.iso+"T00:00:00").getDay()]}</div>
          <div className="pp-emp-row-d">{new Date(day.iso+"T00:00:00").getDate()}</div>
        </div>
        <div className="pp-emp-row-bar">
          <div className="pp-emp-row-off" style={{ color: "#8a1d2e" }}>Absent — no punch</div>
        </div>
        <div className="pp-emp-row-total"><span className="pp-emp-row-off-lbl">—</span></div>
      </div>
    );
  }

  // For days with punches the server returns hoursLogged but not the
  // segment detail — we approximate a bar from in/out times when
  // present. For days where we only have aggregate, show a single
  // continuous work segment proportional to hours logged.
  const total = Number(day.hoursLogged) || 0;
  return (
    <div className={`pp-emp-row ${isToday ? "is-today" : ""}`}>
      <div className="pp-emp-row-date">
        <div className="pp-emp-row-dow">{(["Sun","Mon","Tue","Wed","Thu","Fri","Sat"])[new Date(day.iso+"T00:00:00").getDay()]}</div>
        <div className="pp-emp-row-d">{new Date(day.iso+"T00:00:00").getDate()}</div>
        {isToday && (<span className="pp-pill-status in pp-emp-today-pill">Today</span>)}
      </div>
      <div className="pp-emp-row-bar" style={{ display: "flex", alignItems: "center" }}>
        {/* Real per-day segments (work green / break amber) via the
            shared graphical timeline — dashed baseline + line + dots. */}
        <div style={{ width: "100%" }}>
          <PpTimeline segments={day.segments || []} band={ppDayBand(day)}/>
        </div>
      </div>
      <div className="pp-emp-row-total">
        {ppFmtHours(total)}
      </div>
    </div>
  );
}

// ── Balances ────────────────────────────────────────────────
function PpEmpBalances({ me, stacked = false }) {
  const items = (me && me.balances) || [];
  return (
    <div className="pp-card">
      <div className="pp-card-head">
        <div>
          <div className="pp-card-title">Leave balances</div>
          <div className="pp-card-sub">{items.length} categor{items.length === 1 ? "y" : "ies"}</div>
        </div>
      </div>
      <div className={"pp-emp-balances " + (stacked ? "stacked" : "row")}>
        {items.map(b => {
          const total = Number(b.total) || 0;
          const used = Number(b.used) || 0;
          const remaining = Math.max(0, total - used);
          const pct = total > 0 ? Math.min(1, remaining / total) : 0;
          return (
            <div key={b.id} className="pp-emp-bal">
              <div className="pp-emp-bal-ring">
                <PpRing pct={pct * 100} color={b.color || "#0073ea"} size={stacked ? 64 : 92} stroke={stacked ? 6 : 9}
                        label={remaining}
                        sublabel={(b.unit || "days") + " left"}/>
              </div>
              <div className="pp-emp-bal-meta">
                <div className="pp-emp-bal-name">
                  <span style={{ display: "inline-block", width: 8, height: 8, borderRadius: 99,
                                  background: b.color || "#0073ea", marginRight: 6 }}/>
                  {b.name}
                </div>
                <div className="pp-emp-bal-strip">
                  <span><b>{used}</b> used</span>
                  <span><b>{Math.round(b.accrued || 0)}</b> accrued</span>
                  <span><b>{total}</b> annual</span>
                </div>
              </div>
            </div>
          );
        })}
        {items.length === 0 && (
          <div style={{ padding: 16, color: "var(--ink-muted)", fontSize: 12.5 }}>
            No leave types yet — ask an admin to set them up.
          </div>
        )}
      </div>
    </div>
  );
}

// ── Team out ────────────────────────────────────────────────
function PpEmpTeamOut({ me }) {
  const items = (me && me.teamOut) || [];
  return (
    <div className="pp-card">
      <div className="pp-card-head">
        <div className="pp-card-title">Team out</div>
        <div className="pp-card-sub" style={{ marginLeft: "auto" }}>Today</div>
      </div>
      <div style={{ padding: "6px 0" }}>
        {items.length === 0 && (
          <div style={{ padding: 16, color: "var(--ink-muted)", fontSize: 12.5 }}>Everyone's in today.</div>
        )}
        {items.map(t => (
          <div key={t.id} style={{ display: "flex", gap: 10, padding: "8px 16px", alignItems: "center",
                                    borderBottom: "1px solid var(--border-row, #eaecf1)" }}>
            <PpAvatar person={t} size="sm"/>
            <div style={{ flex: 1, minWidth: 0 }}>
              <div style={{ fontSize: 13, fontWeight: 600, color: "var(--ink-strong)" }}>{t.name}</div>
              <div style={{ fontSize: 11.5, color: "var(--ink-muted)" }}>
                <span style={{ display: "inline-block", width: 7, height: 7, borderRadius: 99,
                                background: t.leaveColor || "#0073ea", marginRight: 4 }}/>
                {t.leaveType || "Leave"} · until {ppFmtDateShort(t.to)}
              </div>
            </div>
          </div>
        ))}
      </div>
    </div>
  );
}

// ── My Leave tab ────────────────────────────────────────────
function PpEmpLeave({ me, onApply, onReload }) {
  const [requests, setRequests] = React.useState([]);
  const [holidays, setHolidays] = React.useState([]);
  const reloadInner = React.useCallback(async () => {
    try {
      const [r, h] = await Promise.all([
        window.api.people.listLeaveRequests({ userId: (window.api.getUser && window.api.getUser().id) || null }),
        window.api.people.holidays({}),
      ]);
      setRequests(r.items || []);
      setHolidays(h.items || []);
    } catch {}
  }, []);
  React.useEffect(() => { reloadInner(); }, [reloadInner]);

  return (
    <>
      <PpEmpLeaveHero me={me} onApply={onApply}/>
      <PpEmpBalances me={me}/>
      <div className="pp-emp-2col">
        <PpEmpMyRequests items={requests} onReload={() => { reloadInner(); onReload && onReload(); }}/>
        <PpEmpHolidays items={holidays}/>
      </div>
    </>
  );
}

function PpEmpLeaveHero({ me, onApply }) {
  const balances = (me && me.balances) || [];
  const remaining = balances.reduce((s, b) => s + Math.max(0, (b.total || 0) - (b.used || 0)), 0);
  const total = balances.reduce((s, b) => s + (b.total || 0), 0);
  const used = total - remaining;
  const pctUsed = total > 0 ? (used / total) * 100 : 0;
  return (
    <div className="pp-emp-hero pp-emp-leave-hero">
      <div className="pp-emp-hero-progress"><span style={{ width: pctUsed + "%" }}/></div>
      <div className="pp-emp-hero-top">
        <div className="pp-emp-hero-meta-row">
          <span>LEAVE · {new Date().getFullYear()}</span>
          <span className="pp-emp-hero-meta-dot"/>
          <span>{used} USED · {remaining} REMAINING OF {total}</span>
        </div>
        <div className="pp-emp-leave-hero-row">
          <div className="pp-emp-leave-hero-main">
            <div className="pp-emp-leave-hero-num">
              {remaining}<span className="pp-emp-leave-hero-num-sub">days</span>
            </div>
            <div className="pp-emp-leave-hero-lbl">
              available across <b>{balances.length} categor{balances.length === 1 ? "y" : "ies"}</b>
            </div>
            <div className="pp-emp-leave-hero-meta">
              {used} used · {remaining} remaining of {total} annual
            </div>
            <div className="pp-emp-hero-actions" style={{ marginTop: 18 }}>
              <button className="pp-emp-btn pp-emp-btn-primary" onClick={onApply}>
                <PpIcon name="Plus"/> Apply for leave
              </button>
            </div>
          </div>
        </div>
      </div>
    </div>
  );
}

function PpEmpMyRequests({ items, onReload }) {
  async function cancel(id, isApproved) {
    const prompt = isApproved
      ? "Cancel this approved leave? Your balance will be refunded."
      : "Cancel this leave request?";
    if (!window.confirm(prompt)) return;
    try { await window.api.people.cancelLeaveRequest(id); onReload(); } catch {}
  }
  // Employees can self-cancel:
  //   • pending requests, always
  //   • approved requests ONLY before the leave starts (today < from_date).
  // Once an approved leave has started or ended, only an admin can undo it
  // — silent self-cancellation of past or in-progress leave isn't allowed.
  function canCancel(r) {
    const today = new Date().toISOString().slice(0, 10);
    if (r.status === "pending") return true;
    if (r.status === "approved" && r.from_date > today) return true;
    return false;
  }
  return (
    <div className="pp-card">
      <div className="pp-card-head">
        <div className="pp-card-title">My requests</div>
        <div className="pp-card-sub" style={{ marginLeft: "auto" }}>{items.length}</div>
      </div>
      <div style={{ padding: "4px 0" }}>
        {items.map(r => (
          <div key={r.id} style={{ display: "flex", padding: "10px 16px", alignItems: "center", gap: 10,
                                    borderBottom: "1px solid var(--border-row, #eaecf1)" }}>
            <div style={{ width: 8, height: 8, borderRadius: 99, background: r.type_color || "#0073ea" }}/>
            <div style={{ flex: 1, minWidth: 0 }}>
              <div style={{ fontSize: 13, fontWeight: 600, color: "var(--ink-strong)" }}>{r.type_name}</div>
              <div style={{ fontSize: 11.5, color: "var(--ink-muted)" }}>
                {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"}`}
              </div>
              {r.status === "rejected" && r.decision_note && (
                <div style={{ fontSize: 11.5, color: "#b41f37", marginTop: 2, fontStyle: "italic" }}>
                  Rejected: {r.decision_note}
                </div>
              )}
              {r.certificate_required && !r.certificate_submitted && (
                <div style={{ fontSize: 11.5, color: "#8c4a00", marginTop: 2 }}>⚠ Medical certificate pending</div>
              )}
            </div>
            <PpStatusPill status={r.status}/>
            {canCancel(r) && (
              <button className="pp-btn" onClick={() => cancel(r.id, r.status === "approved")}>Cancel</button>
            )}
          </div>
        ))}
        {items.length === 0 && (
          <div style={{ padding: 24, textAlign: "center", color: "var(--ink-muted)" }}>No requests yet.</div>
        )}
      </div>
    </div>
  );
}

function PpEmpHolidays({ items }) {
  const today = new Date().toISOString().slice(0, 10);
  return (
    <div className="pp-card">
      <div className="pp-card-head">
        <div className="pp-card-title">Company holidays</div>
        <div className="pp-card-sub" style={{ marginLeft: "auto" }}>{items.length}</div>
      </div>
      <div style={{ padding: "4px 0" }}>
        {items.map(h => {
          const past = h.date < today;
          const next = !past && h.date >= today;
          return (
            <div key={h.id} style={{ display: "flex", padding: "10px 16px", alignItems: "center",
                                      borderBottom: "1px solid var(--border-row, #eaecf1)",
                                      opacity: past ? 0.5 : 1,
                                      background: next ? "#fff7e8" : null }}>
              <div style={{ width: 70 }}>
                <div style={{ fontSize: 13, fontWeight: 700, color: "var(--ink-strong)" }}>{ppFmtDateShort(h.date)}</div>
                <div style={{ fontSize: 11, color: "var(--ink-muted)" }}>{ppDow(new Date(h.date + "T00:00:00"))}</div>
              </div>
              <div style={{ flex: 1, fontSize: 13, color: "var(--ink-strong)" }}>{h.label}</div>
              {past ? <span style={{ fontSize: 11, color: "var(--ink-muted)" }}>Past</span>
                    : next ? <PpStatusPill status="pending">Next</PpStatusPill> : null}
            </div>
          );
        })}
        {items.length === 0 && (
          <div style={{ padding: 24, textAlign: "center", color: "var(--ink-muted)" }}>No holidays configured.</div>
        )}
      </div>
    </div>
  );
}

function PpEmpCalendar() {
  // Reuse the manager's month calendar (holidays + everyone's approved
  // leave). It's read-only data, so it's safe for employees too. Falls
  // back to a friendly message if the manager bundle hasn't loaded yet.
  if (typeof window !== "undefined" && typeof window.PpCalendarScreen === "function") {
    return React.createElement(window.PpCalendarScreen);
  }
  return (
    <div className="pp-card" style={{ padding: 20 }}>
      <div className="pp-card-title">Team calendar</div>
      <div className="pp-card-sub" style={{ marginTop: 4 }}>Loading team calendar…</div>
    </div>
  );
}

// Bare-mode shell tweaks (only used when the main app sidebar
// suppresses the in-module sidebar via hideOwnSidebar) + the compact
// "My Day" redesign (pp-md-*). Kept here (not in people-employee.css)
// so the redesign ships with the JS and never desyncs from a stale CSS
// cache.
const PP_EMP_EXTRA_CSS = `
.pp-app.pp-app-bare { grid-template-columns: 1fr !important; }
.pp-app.pp-app-bare > .pp-body { width: 100%; }

/* ── Compact My Day hero ─────────────────────────────────── */
.pp-md-hero {
  background: #0b5cad;
  border-radius: 16px;
  padding: 18px 22px 16px;
  color: #fff;
  margin-bottom: 14px;
  position: relative;
  overflow: hidden;
}
.pp-md-hero::after {
  content: ""; position: absolute; top: -100px; right: -100px;
  width: 320px; height: 320px;
  background: radial-gradient(circle, rgba(255,255,255,.12) 0%, transparent 60%);
  pointer-events: none;
}
.pp-md-hero-top {
  position: relative; z-index: 1;
  display: flex; justify-content: space-between; align-items: flex-start;
  gap: 16px; flex-wrap: wrap;
}
.pp-md-hero-eyebrow {
  font-size: 10.5px; font-weight: 700; letter-spacing: .14em;
  color: rgba(255,255,255,.6);
}
.pp-md-hero-clock {
  font-size: 40px; font-weight: 800; line-height: 1.02;
  letter-spacing: -.03em; font-variant-numeric: tabular-nums; margin-top: 2px;
}
.pp-md-hero-secs {
  font-size: 19px; font-weight: 600; color: rgba(255,255,255,.65); letter-spacing: 0;
}
.pp-md-hero-greet { font-size: 13px; color: rgba(255,255,255,.78); margin-top: 5px; }
.pp-md-hero-worked { display: flex; align-items: baseline; gap: 9px; margin-top: 12px; flex-wrap: wrap; }
.pp-md-hero-worked-lbl {
  font-size: 10.5px; font-weight: 700; letter-spacing: .12em; text-transform: uppercase;
  color: rgba(255,255,255,.55);
}
.pp-md-hero-worked-val {
  font-size: 24px; font-weight: 800; color: #fff;
  font-variant-numeric: tabular-nums; letter-spacing: -.015em; line-height: 1;
}
.pp-md-hero-worked-since { font-size: 11.5px; color: rgba(255,255,255,.5); }
.pp-md-hero-actcol { display: flex; flex-direction: column; align-items: flex-end; gap: 10px; }
.pp-md-chip {
  display: inline-flex; align-items: center; gap: 7px;
  background: rgba(255,255,255,.16); color: #fff;
  font-size: 12px; font-weight: 600; padding: 5px 11px; border-radius: 8px;
}
.pp-md-chip-dot { width: 8px; height: 8px; border-radius: 50%; background: #cfd8e6; flex: none; }
.pp-md-chip-ok   .pp-md-chip-dot { background: #37d27f; animation: ppPulse 1.8s infinite; }
.pp-md-chip-warn .pp-md-chip-dot { background: #ffc24d; }
.pp-md-hero-btns { display: flex; gap: 8px; }
.pp-md-btn {
  border: none; border-radius: 8px; font: inherit; font-size: 13px; font-weight: 700;
  cursor: pointer; display: inline-flex; align-items: center; gap: 6px;
  padding: 8px 15px; transition: transform .1s ease, filter .15s ease;
}
.pp-md-btn:hover { transform: translateY(-1px); }
.pp-md-btn:disabled { opacity: .6; cursor: default; transform: none; }
.pp-md-btn svg { width: 14px; height: 14px; }
.pp-md-btn-ghost { background: rgba(255,255,255,.16); color: #fff; }
.pp-md-btn-ghost:hover { background: rgba(255,255,255,.24); }
.pp-md-btn-go { background: #2bd07f; color: #063d23; }
.pp-md-btn-warn { background: #f0b429; color: #3a2a00; }
.pp-md-btn-primary { background: #fff; color: #0b5cad; }
.pp-md-hero-tl { position: relative; z-index: 1; margin-top: 28px; }
.pp-md-tl-strip { position: relative; }
/* Running worked-hours tag — an amber pill above the live dot with a
   little downward pointer, ticking up while clocked in. */
.pp-md-tl-tag {
  position: absolute; top: 50%;
  transform: translate(-50%, calc(-100% - 8px));
  background: #ff8c1a; color: #3a2400;
  font-size: 10.5px; font-weight: 800; line-height: 1;
  padding: 3px 7px; border-radius: 6px; white-space: nowrap;
  box-shadow: 0 1px 5px rgba(0,0,0,.28);
  font-variant-numeric: tabular-nums; pointer-events: none;
}
.pp-md-tl-tag::after {
  content: ""; position: absolute; top: 100%; left: 50%;
  transform: translateX(-50%);
  border: 4px solid transparent; border-top-color: #ff8c1a;
}
.pp-md-hero-tl-labels {
  display: flex; justify-content: space-between;
  font-size: 11px; color: rgba(255,255,255,.6); margin-top: 7px;
  font-variant-numeric: tabular-nums;
}

/* ── Stat tiles ──────────────────────────────────────────── */
.pp-md-stats {
  display: grid; grid-template-columns: repeat(3, 1fr);
  gap: 12px; margin-bottom: 18px;
}
.pp-md-stat {
  background: var(--bg-card, #fff);
  border: 1px solid var(--border-row, #eaecf1);
  border-radius: 12px; padding: 12px 14px;
}
.pp-md-stat-l { font-size: 12px; color: var(--ink-muted, #6c7385); }
.pp-md-stat-v {
  font-size: 22px; font-weight: 800; color: var(--ink-strong, #0f1729);
  margin-top: 2px; font-variant-numeric: tabular-nums; letter-spacing: -.01em;
}
.pp-md-stat-of { font-size: 12px; font-weight: 600; color: var(--ink-muted, #9aa0ad); }
.pp-md-stat-s { font-size: 11px; color: var(--ink-muted, #9aa0ad); margin-top: 3px; }

/* ── Compact leave balance card ──────────────────────────── */
.pp-md-bal-list { padding: 4px 0 2px; }
.pp-md-bal-row { display: flex; align-items: center; gap: 9px; padding: 7px 16px; font-size: 13px; }
.pp-md-bal-dot { width: 8px; height: 8px; border-radius: 50%; flex: none; }
.pp-md-bal-name { color: var(--ink-strong, #0f1729); flex: 1; min-width: 0; }
.pp-md-bal-num { font-weight: 800; color: var(--ink-strong, #0f1729); font-variant-numeric: tabular-nums; }
.pp-md-bal-btn {
  display: flex; align-items: center; justify-content: center; gap: 6px;
  width: calc(100% - 24px); margin: 6px 12px 12px; padding: 8px;
  border: 1px solid var(--border-row, #eaecf1); border-radius: 8px;
  background: var(--bg-soft, #f7f8fb);
  font: inherit; font-size: 12.5px; font-weight: 700; color: var(--ink-strong, #0f1729);
  cursor: pointer;
}
.pp-md-bal-btn:hover { background: #eef0f5; }
.pp-md-bal-btn svg { width: 13px; height: 13px; }
.pp-md-empty { padding: 14px 16px; color: var(--ink-muted, #6c7385); font-size: 12.5px; }

@media (max-width: 720px) {
  .pp-md-stats { grid-template-columns: 1fr; }
  .pp-md-hero-actcol { align-items: flex-start; width: 100%; }
}

/* ── AI HR Manager card ───────────────────────────────────── */
.pp-hr-card {
  position: relative; display: flex; gap: 0;
  background: var(--bg-card, #fff);
  border: 1px solid var(--border-row, #eaecf1);
  border-radius: 14px; margin-bottom: 14px; overflow: hidden;
  box-shadow: 0 1px 3px rgba(15,23,41,.05);
}
.pp-hr-accent { width: 5px; flex: none; background: #0073ea; }
.pp-hr-ok      .pp-hr-accent { background: #21a366; }
.pp-hr-info    .pp-hr-accent { background: #0073ea; }
.pp-hr-watch   .pp-hr-accent { background: #f0b429; }
.pp-hr-concern .pp-hr-accent { background: #e2445c; }
.pp-hr-main { flex: 1; min-width: 0; padding: 14px 16px; }
.pp-hr-top { display: flex; align-items: center; gap: 10px; }
.pp-hr-avatar {
  width: 36px; height: 36px; border-radius: 50%; flex: none;
  display: flex; align-items: center; justify-content: center;
  background: #eaf1ff; color: #0073ea;
}
.pp-hr-ok      .pp-hr-avatar { background: #e6f7ee; color: #1a8a55; }
.pp-hr-watch   .pp-hr-avatar { background: #fdf1d8; color: #a8740f; }
.pp-hr-concern .pp-hr-avatar { background: #fde7ea; color: #c5354b; }
.pp-hr-id { line-height: 1.15; min-width: 0; }
.pp-hr-name { font-size: 13.5px; font-weight: 800; color: var(--ink-strong, #0f1729); }
.pp-hr-tag { font-size: 9.5px; font-weight: 700; letter-spacing: .1em; text-transform: uppercase; color: var(--ink-muted, #8a90a0); }
.pp-hr-sev {
  margin-left: auto; display: inline-flex; align-items: center; gap: 5px;
  font-size: 11px; font-weight: 800; padding: 3px 9px; border-radius: 99px; background: #f2f4f8;
}
.pp-hr-sev-dot { width: 7px; height: 7px; border-radius: 50%; background: currentColor; flex: none; }
.pp-hr-sev-ok      { color: #1a8a55; background: #e9f7ef; }
.pp-hr-sev-info    { color: #0073ea; background: #eaf1ff; }
.pp-hr-sev-watch   { color: #a8740f; background: #fdf2da; }
.pp-hr-sev-concern { color: #c5354b; background: #fde7ea; }
.pp-hr-headline { font-size: 14.5px; font-weight: 800; color: var(--ink-strong, #0f1729); margin-top: 11px; letter-spacing: -0.01em; }
.pp-hr-msg { font-size: 13px; line-height: 1.55; color: var(--ink-muted, #4a4f5e); margin-top: 4px; }
.pp-hr-foot { display: flex; align-items: center; gap: 10px; margin-top: 13px; }
.pp-hr-when { font-size: 11px; color: var(--ink-muted, #9aa0ad); }
.pp-hr-dismiss {
  margin-left: auto; border: 1px solid var(--border-row, #dfe3ea);
  background: var(--bg-soft, #f7f8fb); color: var(--ink-strong, #0f1729);
  font: inherit; font-size: 12px; font-weight: 700; padding: 7px 14px;
  border-radius: 8px; cursor: pointer;
}
.pp-hr-dismiss:hover { background: #eef0f5; }
`;
(function injectEmpCss() {
  if (typeof document === "undefined") return;
  if (document.getElementById("pp-emp-extra-css")) return;
  const tag = document.createElement("style");
  tag.id = "pp-emp-extra-css";
  tag.textContent = PP_EMP_EXTRA_CSS;
  document.head.appendChild(tag);
})();

Object.assign(window, { PpEmployeeApp });
