// payroll.jsx — Payroll module surfaces.
//
//   PpPayrollAdmin — admin tab inside People admin.
//     Two sub-tabs:
//       · Runs        — list monthly runs; Run payroll for a new
//                       month; open a run to see all employee slips,
//                       edit LOP, finalize, mark paid.
//       · Structures  — every employee with their current salary
//                       structure; click to set / edit.
//
//   PpEmpPayroll   — employee tab inside People employee module.
//     Lists every finalized/paid payslip for the current user, with
//     status pill (Pending vs Paid), expanded breakdown drawer, and
//     a "Download payslip" button that opens the printable HTML in
//     a new window (browser's Save-as-PDF takes care of the rest).
//
// Both surfaces are exposed via window.{PpPayrollAdmin,PpEmpPayroll}
// and wired into people-manager.jsx + people-employee.jsx and the
// main sidebar in shell.jsx.

(function () {
  const R = window.React;

  // ── one-time CSS injection ─────────────────────────────────────
  (function injectCss() {
    if (document.getElementById("pp-payroll-css")) return;
    const st = document.createElement("style"); st.id = "pp-payroll-css";
    st.textContent = `
      .pr-card { background: #fff; border: 1px solid var(--border-row,#eaecf1);
                 border-radius: 12px; padding: 14px 16px; margin-bottom: 12px; }
      .pr-h    { display: flex; align-items: center; gap: 10px; margin-bottom: 10px; }
      .pr-h h2 { margin: 0; font-size: 14px; font-weight: 800; color: var(--ink-strong,#0f1729); }
      .pr-tabs { display: flex; gap: 4px; border-bottom: 1px solid var(--border-row,#eaecf1);
                 margin-bottom: 12px; }
      .pr-tab  { padding: 8px 14px; border: 0; background: none; cursor: pointer;
                 color: var(--ink-muted); font-weight: 700; font-size: 13px;
                 border-bottom: 2px solid transparent; }
      .pr-tab.on { color: var(--ink-strong,#0f1729); border-bottom-color: #0073ea; }
      .pr-tbl  { width: 100%; border-collapse: collapse; font-size: 13px; }
      .pr-tbl th, .pr-tbl td { padding: 8px 10px; border-bottom: 1px solid var(--border-row,#f1f3f7);
                               text-align: left; }
      .pr-tbl th { color: var(--ink-muted); font-weight: 600; font-size: 11.5px;
                   text-transform: uppercase; letter-spacing: .04em; }
      .pr-tbl td.r, .pr-tbl th.r { text-align: right; font-variant-numeric: tabular-nums; }
      .pr-pill { display: inline-flex; align-items: center; gap: 4px; padding: 2px 9px;
                 border-radius: 99px; font-size: 11px; font-weight: 700; letter-spacing: .03em;
                 text-transform: uppercase; }
      .pr-pill.draft     { background: #f0f1f5; color: #5b6478; }
      .pr-pill.finalized { background: #e3eeff; color: #0a3d91; }
      .pr-pill.paid      { background: #d8f5e3; color: #126b3b; }
      .pr-rr { display: flex; align-items: center; gap: 12px; padding: 12px 14px;
               border: 1px solid var(--border-row,#eaecf1); border-radius: 10px;
               background: #fff; cursor: pointer; margin-bottom: 8px; }
      .pr-rr:hover { border-color: #c5d2e7; background: #fbfcff; }
      .pr-rr-period { font-size: 14.5px; font-weight: 800; color: var(--ink-strong,#0f1729);
                      min-width: 160px; }
      .pr-rr-meta   { font-size: 11.5px; color: var(--ink-muted); }
      .pr-rr-amt    { margin-left: auto; font-size: 14.5px; font-weight: 800;
                      color: #0a3d91; font-variant-numeric: tabular-nums; }
      .pr-num { font-variant-numeric: tabular-nums; }
      .pr-input { padding: 7px 9px; border-radius: 6px; border: 1px solid var(--bd,#d8dce4);
                  font-size: 13px; width: 100%; box-sizing: border-box; }
      .pr-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 10px 16px; }
      .pr-key  { font-size: 10.5px; text-transform: uppercase; letter-spacing: .04em;
                 color: var(--ink-muted); margin-bottom: 2px; font-weight: 700; }
      .pr-foot { background: #f4f6fb; border-top: 1px solid var(--border-row,#eaecf1);
                 padding: 10px 14px; display: flex; justify-content: space-between;
                 font-size: 14px; font-weight: 800; }
      .pr-net  { background: #e7f0fe; color: #0a3d91; }
      .pr-money-mask {
        display: inline-flex; align-items: center; gap: 6px;
        background: #f3f4f8; border: 1px dashed #c5cbd9; border-radius: 6px;
        padding: 2px 9px; cursor: pointer; font-size: 12px;
        font-family: inherit; color: #5b6478; line-height: 1.4;
        transition: background .12s ease, border-color .12s ease;
      }
      .pr-money-mask:hover { background: #e9ecf3; border-color: #a4adc1; color: #1c2233; }
      .pr-money-dots { font-weight: 800; letter-spacing: 2px; color: #1c2233; }
      .pr-money-hint { font-size: 10px; color: #8590a4; text-transform: uppercase; letter-spacing: .04em; }
      .pr-eye {
        display: inline-flex; align-items: center; justify-content: center;
        background: transparent; border: 1px solid var(--bd,#d8dce4);
        width: 28px; height: 26px; border-radius: 6px; cursor: pointer;
        font-size: 13px; padding: 0; margin-left: 6px;
      }
      .pr-eye:hover { background: #f4f6fb; }
      .pr-eye.on { background: #e3eeff; border-color: #0073ea; color: #0073ea; }
      .pr-gate {
        max-width: 480px; margin: 32px auto; padding: 28px 26px;
        background: #fff; border: 1px solid var(--border-row,#eaecf1);
        border-radius: 14px; text-align: center;
        box-shadow: 0 10px 30px rgba(15,23,41,.04);
      }
      .pr-gate-icon {
        width: 56px; height: 56px; border-radius: 14px;
        background: linear-gradient(135deg,#0a3d91,#0073ea); color: #fff;
        display: flex; align-items: center; justify-content: center;
        font-size: 26px; margin: 0 auto 12px;
      }
      .pr-gate h1 { margin: 4px 0 6px; font-size: 17px; font-weight: 800; color: var(--ink-strong,#0f1729); }
      .pr-gate p  { margin: 0; font-size: 13px; color: var(--ink-muted); line-height: 1.55; }
      .pr-code-input {
        font-size: 22px; letter-spacing: 8px; text-align: center;
        padding: 12px; border-radius: 8px; border: 1px solid var(--bd,#d8dce4);
        width: 100%; box-sizing: border-box; font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
        background: #fafbfd;
      }
      .pr-code-input:focus { outline: none; border-color: #0073ea; background: #fff; }
      .pr-qr-box { display: flex; justify-content: center; padding: 14px 0; }
      .pr-qr-box svg, .pr-qr-box img { max-width: 200px; height: auto; }
      .pr-secret { font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
                   font-size: 12px; background: #f4f6fb; padding: 8px 10px;
                   border-radius: 6px; word-break: break-all; letter-spacing: 1px; }
      .pr-backup-grid {
        display: grid; grid-template-columns: 1fr 1fr; gap: 8px 14px;
        background: #fffaf2; border: 1px solid #f3dca1; border-radius: 8px;
        padding: 12px 14px; margin: 12px 0; font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
        font-size: 13px;
      }
      .pr-backup-grid > div { letter-spacing: 1px; color: #5a3f00; }
      .pr-hero-mask {
        display: inline-flex; align-items: center; gap: 6px;
        background: rgba(255,255,255,.18); border: 1px dashed rgba(255,255,255,.4);
        color: #fff; padding: 4px 12px; border-radius: 8px; cursor: pointer;
        font-family: inherit; font-size: 13px; font-weight: 700; letter-spacing: 1.5px;
      }
      .pr-hero-mask:hover { background: rgba(255,255,255,.28); border-color: rgba(255,255,255,.6); }
    `;
    document.head.appendChild(st);
  })();

  function fmtINR(n) {
    return "₹" + (Number(n) || 0).toLocaleString("en-IN", { minimumFractionDigits: 2, maximumFractionDigits: 2 });
  }
  const MONTHS = ["January","February","March","April","May","June",
                  "July","August","September","October","November","December"];
  function periodLabel(y, m) { return `${MONTHS[(Number(m) - 1) | 0] || ""} ${y}`; }

  // ── Money cell with click-to-reveal ────────────────────────────
  // When `revealed` is false, the rupee amount is rendered as a
  // single masked pill the user can click. When `revealed`, the
  // actual number renders. The parent passes both the amount and
  // a click handler so it can flip the row's reveal state. The
  // visual fits inline in tables and structure cards.
  function Money({ value, revealed, onReveal, dim }) {
    if (revealed) {
      return <span className="pr-num">{fmtINR(value)}</span>;
    }
    return (
      <button className="pr-money-mask" onClick={(e) => { e.stopPropagation(); onReveal && onReveal(); }}
              title="Click to reveal" style={{ opacity: dim ? .7 : 1 }}>
        <span className="pr-money-dots">••••</span>
        <span className="pr-money-hint">tap to reveal</span>
      </button>
    );
  }

  // ── ADMIN VIEW ────────────────────────────────────────────────
  function PpPayrollAdmin() {
    const [sub, setSub] = R.useState("runs");
    return (
      <div className="pp-screen">
        <div className="pr-tabs">
          <button className={`pr-tab ${sub === "runs" ? "on" : ""}`} onClick={() => setSub("runs")}>Payroll runs</button>
          <button className={`pr-tab ${sub === "structures" ? "on" : ""}`} onClick={() => setSub("structures")}>Salary structures</button>
        </div>
        {sub === "runs"        && <PrRunsList/>}
        {sub === "structures"  && <PrStructuresList/>}
      </div>
    );
  }

  // ── Runs list + new-run modal ─────────────────────────────────
  function PrRunsList() {
    const [items, setItems] = R.useState(null);
    const [err, setErr]     = R.useState("");
    const [newOpen, setNew] = R.useState(false);
    const [openId, setOpen] = R.useState(null);

    const reload = R.useCallback(async () => {
      try { setItems((await window.api.payroll.runs()).items || []); }
      catch (e) { setErr((e && e.body && (e.body.message || e.body.error)) || "Could not load"); }
    }, []);
    R.useEffect(() => { reload(); }, [reload]);

    return (
      <div className="pr-card">
        <div className="pr-h">
          <h2>Payroll runs</h2>
          <div style={{ flex: 1 }}/>
          <button className="btn btn-primary" onClick={() => setNew(true)}>+ Run payroll</button>
        </div>
        {err && <div style={{ color: "#c0223a", fontSize: 12, marginBottom: 6 }}>{err}</div>}
        {items == null ? (
          <div style={{ padding: 22, color: "var(--ink-muted)", textAlign: "center" }}>Loading…</div>
        ) : items.length === 0 ? (
          <div style={{ padding: 28, color: "var(--ink-muted)", textAlign: "center",
                        border: "1px dashed var(--border-row,#eaecf1)", borderRadius: 10 }}>
            No runs yet. Click "Run payroll" to generate this month's payslips.
          </div>
        ) : items.map(r => (
          <div key={r.id} className="pr-rr" onClick={() => setOpen(r.id)}>
            <div className="pr-rr-period">{periodLabel(r.period_year, r.period_month)}</div>
            <div className="pr-rr-meta">
              {r.slip_count || 0} slips · {r.working_days || 0} working days
              <span style={{ marginLeft: 10 }}>
                <span className={`pr-pill ${r.status}`}>{r.status}</span>
              </span>
            </div>
            <div className="pr-rr-amt">{fmtINR(r.total_net)}</div>
          </div>
        ))}
        {newOpen && <PrNewRunModal onClose={() => setNew(false)} onSaved={(id) => { setNew(false); reload(); setOpen(id); }}/>}
        {openId && <PrRunModal id={openId} onClose={() => { setOpen(null); reload(); }}/>}
      </div>
    );
  }

  function PrNewRunModal({ onClose, onSaved }) {
    const today = new Date();
    // Default to previous month — that's typically what's being run on
    // payday in the first week of the new month.
    const prev = new Date(today.getFullYear(), today.getMonth() - 1, 1);
    const [year,  setYear]  = R.useState(prev.getFullYear());
    const [month, setMonth] = R.useState(prev.getMonth() + 1);
    const [wd,    setWd]    = R.useState(26);
    const [busy,  setBusy]  = R.useState(false);
    const [err,   setErr]   = R.useState("");

    async function save() {
      setBusy(true); setErr("");
      try {
        const r = await window.api.payroll.runCreate({
          year: Number(year), month: Number(month), working_days: Number(wd)
        });
        onSaved(r.id);
      } catch (e) {
        setErr((e && e.body && (e.body.message || e.body.error)) || "Could not create run");
        setBusy(false);
      }
    }
    return window.ReactDOM.createPortal(
      <div className="modal-backdrop" onMouseDown={onClose}>
        <div className="modal" onMouseDown={e => e.stopPropagation()} style={{ width: 420 }}>
          <div className="modal-header"><div style={{ fontWeight: 700 }}>Run payroll</div>
            <button className="modal-close" onClick={onClose}>×</button></div>
          <div className="modal-body" style={{ display: "flex", flexDirection: "column", gap: 12 }}>
            <div className="pr-grid">
              <label><div className="pr-key">Month</div>
                <select className="pr-input" value={month} onChange={e => setMonth(e.target.value)}>
                  {MONTHS.map((m, i) => <option key={i} value={i + 1}>{m}</option>)}
                </select></label>
              <label><div className="pr-key">Year</div>
                <input className="pr-input" type="number" value={year} onChange={e => setYear(e.target.value)}/></label>
            </div>
            <label><div className="pr-key">Working days in month</div>
              <input className="pr-input" type="number" min={1} max={31} value={wd}
                     onChange={e => setWd(e.target.value)}/>
              <div style={{ fontSize: 11, color: "var(--ink-muted)", marginTop: 4 }}>
                Used to pro-rate LOP. Indian standard is 26 (Mon–Sat).
              </div></label>
            {err && <div style={{ color: "#c0223a", fontSize: 12 }}>{err}</div>}
          </div>
          <div className="modal-footer">
            <button className="btn" onClick={onClose} disabled={busy}>Cancel</button>
            <button className="btn btn-primary" onClick={save} disabled={busy}>{busy ? "Generating…" : "Generate payslips"}</button>
          </div>
        </div>
      </div>, document.body);
  }

  // ── Single run — slip table + actions ─────────────────────────
  function PrRunModal({ id, onClose }) {
    const [data, setData] = R.useState(null);
    const [err, setErr]   = R.useState("");
    const [busy, setBusy] = R.useState(false);
    const [editSlipId, setEditSlipId] = R.useState(null);
    // Revealed slip IDs (array) — or the sentinel "ALL" to show every
    // row. Default: nothing revealed; rupees rendered as masked pills.
    const [revealedSlips, setRevealedSlips] = R.useState([]);

    const reload = R.useCallback(async () => {
      try { setData(await window.api.payroll.run(id)); }
      catch (e) { setErr((e && e.body && (e.body.message || e.body.error)) || "Could not load"); }
    }, [id]);
    R.useEffect(() => { reload(); }, [reload]);

    async function finalize() {
      if (!window.confirm("Finalize this run? Slips will be visible to employees and locked from edits.")) return;
      setBusy(true);
      try { await window.api.payroll.runFinalize(id); reload(); }
      catch (e) { setErr((e && e.body && (e.body.message || e.body.error)) || "Could not finalize"); }
      finally { setBusy(false); }
    }
    async function markPaid() {
      if (!window.confirm("Mark this run as Paid? Every slip's status moves to Paid.")) return;
      setBusy(true);
      try { await window.api.payroll.runMarkPaid(id); reload(); }
      catch (e) { setErr((e && e.body && (e.body.message || e.body.error)) || "Could not mark paid"); }
      finally { setBusy(false); }
    }
    async function discard() {
      if (!window.confirm("Discard this draft run? All draft slips will be deleted.")) return;
      setBusy(true);
      try { await window.api.payroll.runDelete(id); onClose(); }
      catch (e) { setErr((e && e.body && (e.body.message || e.body.error)) || "Could not delete"); setBusy(false); }
    }

    if (!data) {
      return window.ReactDOM.createPortal(
        <div className="modal-backdrop" onMouseDown={onClose}>
          <div className="modal" style={{ width: 600 }}>
            <div className="modal-body">{err || "Loading…"}</div>
          </div>
        </div>, document.body);
    }

    const r = data.run, slips = data.slips || [];
    const totalNet = slips.reduce((a, s) => a + (Number(s.net_pay) || 0), 0);
    const totalGross = slips.reduce((a, s) => a + (Number(s.gross) || 0), 0);
    const totalDed = slips.reduce((a, s) => a + (Number(s.total_deductions) || 0), 0);
    const editing = editSlipId ? slips.find(s => s.id === editSlipId) : null;
    // Per-row reveal: clicking a masked amount flips the slip-id ON
    // for the remainder of the modal session. There's also a global
    // "Reveal all / Hide all" toggle for spot-checking the total.
    function reveal(slipId) { setRevealedSlips(arr => arr.includes(slipId) ? arr : [...arr, slipId]); }
    const showAll = revealedSlips === "ALL";
    function isRevealed(slipId) { return showAll || (Array.isArray(revealedSlips) && revealedSlips.includes(slipId)); }

    return window.ReactDOM.createPortal(
      <div className="modal-backdrop" onMouseDown={onClose}>
        <div className="modal" onMouseDown={e => e.stopPropagation()}
             style={{ width: "min(960px, 96%)", maxHeight: "92vh", display: "flex", flexDirection: "column" }}>
          <div className="modal-header" style={{ alignItems: "center" }}>
            <div>
              <div style={{ fontSize: 16, fontWeight: 800 }}>
                {periodLabel(r.period_year, r.period_month)}
                <span style={{ marginLeft: 10 }}><span className={`pr-pill ${r.status}`}>{r.status}</span></span>
              </div>
              <div style={{ fontSize: 11.5, color: "var(--ink-muted)" }}>
                {slips.length} slips · {r.working_days} working days · Net total {showAll ? fmtINR(totalNet) : "•••"}
              </div>
            </div>
            <button
              className={`pr-eye ${showAll ? "on" : ""}`}
              title={showAll ? "Hide all amounts" : "Reveal all amounts"}
              onClick={() => setRevealedSlips(showAll ? [] : "ALL")}
              style={{ marginRight: 6 }}>
              {showAll ? "🙈" : "👁"}
            </button>
            <button className="modal-close" onClick={onClose}>×</button>
          </div>
          <div className="modal-body" style={{ flex: 1, overflow: "auto" }}>
            {err && <div style={{ color: "#c0223a", fontSize: 12, marginBottom: 8 }}>{err}</div>}
            <table className="pr-tbl">
              <thead>
                <tr>
                  <th>Employee</th>
                  <th className="r">Working</th>
                  <th className="r">LOP</th>
                  <th className="r">Gross</th>
                  <th className="r">Deductions</th>
                  <th className="r">Net</th>
                  <th></th>
                </tr>
              </thead>
              <tbody>
                {slips.map(s => {
                  const rev = isRevealed(s.id);
                  return (
                  <tr key={s.id}>
                    <td>
                      <div style={{ fontWeight: 600 }}>{s.user_name}</div>
                      <div style={{ fontSize: 11, color: "var(--ink-muted)" }}>{s.user_employee_code || s.user_email}</div>
                    </td>
                    <td className="r">{s.working_days}</td>
                    <td className="r">{s.lop_days}</td>
                    <td className="r"><Money value={s.gross}            revealed={rev} onReveal={() => reveal(s.id)} dim/></td>
                    <td className="r"><Money value={s.total_deductions} revealed={rev} onReveal={() => reveal(s.id)} dim/></td>
                    <td className="r" style={{ fontWeight: 700, color: rev ? "#0a3d91" : undefined }}>
                      <Money value={s.net_pay} revealed={rev} onReveal={() => reveal(s.id)}/>
                    </td>
                    <td className="r">
                      {r.status === "draft" ? (
                        <button className="btn" style={{ fontSize: 11 }} onClick={() => setEditSlipId(s.id)}>Edit</button>
                      ) : <span className={`pr-pill ${s.status}`}>{s.status}</span>}
                    </td>
                  </tr>);
                })}
                {slips.length === 0 && (
                  <tr><td colSpan={7} style={{ textAlign: "center", color: "var(--ink-muted)", padding: 24 }}>
                    No slips. Add salary structures and re-run.
                  </td></tr>
                )}
              </tbody>
              <tfoot>
                <tr style={{ fontWeight: 800 }}>
                  <td>Total</td><td></td><td></td>
                  <td className="r">{showAll ? fmtINR(totalGross) : "•••"}</td>
                  <td className="r">{showAll ? fmtINR(totalDed)   : "•••"}</td>
                  <td className="r" style={{ color: showAll ? "#0a3d91" : undefined }}>{showAll ? fmtINR(totalNet) : "•••"}</td>
                  <td></td>
                </tr>
              </tfoot>
            </table>
          </div>
          <div className="modal-footer">
            {r.status === "draft" && (
              <button className="btn" style={{ color: "#c0223a", borderColor: "#f3c0c4" }} onClick={discard} disabled={busy}>Discard run</button>
            )}
            <div style={{ flex: 1 }}/>
            {r.status === "draft" && <button className="btn btn-primary" onClick={finalize} disabled={busy}>Finalize</button>}
            {r.status === "finalized" && <button className="btn btn-primary" onClick={markPaid} disabled={busy}>Mark as paid</button>}
            <button className="btn" onClick={onClose}>Close</button>
          </div>
        </div>
        {editing && <PrEditSlipModal slip={editing} workingDays={r.working_days}
                                      onClose={() => setEditSlipId(null)}
                                      onSaved={() => { setEditSlipId(null); reload(); }}/>}
      </div>, document.body);
  }

  function PrEditSlipModal({ slip, workingDays, onClose, onSaved }) {
    const [f, setF] = R.useState({
      lop_days:         Number(slip.lop_days)        || 0,
      other_deduction:  Number(slip.other_deduction) || 0,
      basic:            Number(slip.basic)           || 0,
      hra:              Number(slip.hra)             || 0,
      special:          Number(slip.special)         || 0,
      conveyance:       Number(slip.conveyance)      || 0,
      pf_employee:      Number(slip.pf_employee)     || 0,
      professional_tax: Number(slip.professional_tax)|| 0,
      tds:              Number(slip.tds)             || 0,
      notes:            slip.notes || "",
    });
    const [busy, setBusy] = R.useState(false);
    const [err, setErr]   = R.useState("");
    function up(k, v) { setF(s => ({ ...s, [k]: v })); }
    async function save() {
      setBusy(true); setErr("");
      try { await window.api.payroll.slipPatch(slip.id, f); onSaved(); }
      catch (e) { setErr((e && e.body && (e.body.message || e.body.error)) || "Could not save"); setBusy(false); }
    }

    // Live preview of gross/net while editing.
    const gross = ["basic","hra","special","conveyance"].reduce((a,k)=>a+(Number(f[k])||0),0);
    const lopDed = workingDays > 0 ? Math.round((gross / workingDays) * (Number(f.lop_days)||0) * 100) / 100 : 0;
    const ded = (Number(f.pf_employee)||0) + (Number(f.professional_tax)||0) + (Number(f.tds)||0)
              + lopDed + (Number(f.other_deduction)||0);
    const net = Math.max(0, Math.round((gross - ded) * 100) / 100);

    function fld(k, label, kind) {
      return (
        <label><div className="pr-key">{label}</div>
          <input className="pr-input" type={kind || "number"} value={f[k]}
                 onChange={e => up(k, e.target.value)}/></label>
      );
    }

    return window.ReactDOM.createPortal(
      <div className="modal-backdrop" onMouseDown={onClose}>
        <div className="modal" onMouseDown={e => e.stopPropagation()} style={{ width: 560, maxHeight: "92vh", display: "flex", flexDirection: "column" }}>
          <div className="modal-header"><div style={{ fontWeight: 700 }}>Edit slip — {slip.user_name}</div>
            <button className="modal-close" onClick={onClose}>×</button></div>
          <div className="modal-body" style={{ flex: 1, overflow: "auto", display: "flex", flexDirection: "column", gap: 12 }}>
            <div className="pr-grid">
              {fld("lop_days", "LOP days")}
              {fld("other_deduction", "Other deduction (₹)")}
            </div>
            <div className="pr-grid">
              {fld("basic", "Basic")}
              {fld("hra", "HRA")}
              {fld("special", "Special")}
              {fld("conveyance", "Conveyance")}
            </div>
            <div className="pr-grid">
              {fld("pf_employee", "PF (Employee)")}
              {fld("professional_tax", "Professional Tax")}
              {fld("tds", "TDS")}
            </div>
            <label><div className="pr-key">Notes</div>
              <textarea className="pr-input" rows={2} value={f.notes} onChange={e => up("notes", e.target.value)}/></label>
            <div style={{ background: "#f4f6fb", padding: 10, borderRadius: 8, display: "flex", gap: 16, fontSize: 12.5 }}>
              <div><b>Gross</b>: {fmtINR(gross)}</div>
              <div><b>LOP ded</b>: {fmtINR(lopDed)}</div>
              <div><b>Deductions</b>: {fmtINR(ded)}</div>
              <div style={{ marginLeft: "auto", color: "#0a3d91", fontWeight: 800 }}>Net {fmtINR(net)}</div>
            </div>
            {err && <div style={{ color: "#c0223a", fontSize: 12 }}>{err}</div>}
          </div>
          <div className="modal-footer">
            <button className="btn" onClick={onClose} disabled={busy}>Cancel</button>
            <button className="btn btn-primary" onClick={save} disabled={busy}>{busy ? "Saving…" : "Save"}</button>
          </div>
        </div>
      </div>, document.body);
  }

  // ── Structures (admin) ────────────────────────────────────────
  function PrStructuresList() {
    const [items, setItems] = R.useState(null);
    const [err, setErr]     = R.useState("");
    const [editUserId, setEditId] = R.useState(null);
    // Revealed user IDs (set) — or sentinel "ALL".
    const [revealed, setRevealed] = R.useState([]);
    const showAll = revealed === "ALL";
    function isRevealed(uid) { return showAll || (Array.isArray(revealed) && revealed.includes(uid)); }
    function reveal(uid) { setRevealed(arr => (arr === "ALL" ? arr : (arr.includes(uid) ? arr : [...arr, uid]))); }
    const reload = R.useCallback(async () => {
      try { setItems((await window.api.payroll.structures()).items || []); }
      catch (e) { setErr((e && e.body && (e.body.message || e.body.error)) || "Could not load"); }
    }, []);
    R.useEffect(() => { reload(); }, [reload]);

    return (
      <div className="pr-card">
        <div className="pr-h">
          <h2>Salary structures</h2>
          <div style={{ flex: 1 }}/>
          <button
            className={`pr-eye ${showAll ? "on" : ""}`}
            title={showAll ? "Hide all amounts" : "Reveal all amounts"}
            onClick={() => setRevealed(showAll ? [] : "ALL")}>
            {showAll ? "🙈" : "👁"}
          </button>
        </div>
        {err && <div style={{ color: "#c0223a", fontSize: 12 }}>{err}</div>}
        {items == null ? (
          <div style={{ padding: 22, color: "var(--ink-muted)", textAlign: "center" }}>Loading…</div>
        ) : (
          <table className="pr-tbl">
            <thead>
              <tr>
                <th>Employee</th>
                <th className="r">Basic</th>
                <th className="r">HRA</th>
                <th className="r">Special</th>
                <th className="r">Conveyance</th>
                <th className="r">Gross / month</th>
                <th></th>
              </tr>
            </thead>
            <tbody>
              {items.map(it => {
                const s = it.structure;
                const rev = isRevealed(it.user.id);
                return (
                  <tr key={it.user.id}>
                    <td>
                      <div style={{ fontWeight: 600 }}>{it.user.name}</div>
                      <div style={{ fontSize: 11, color: "var(--ink-muted)" }}>
                        {it.user.employee_code || it.user.email}
                      </div>
                    </td>
                    <td className="r">{s ? <Money value={s.basic}      revealed={rev} onReveal={() => reveal(it.user.id)} dim/> : "—"}</td>
                    <td className="r">{s ? <Money value={s.hra}        revealed={rev} onReveal={() => reveal(it.user.id)} dim/> : "—"}</td>
                    <td className="r">{s ? <Money value={s.special}    revealed={rev} onReveal={() => reveal(it.user.id)} dim/> : "—"}</td>
                    <td className="r">{s ? <Money value={s.conveyance} revealed={rev} onReveal={() => reveal(it.user.id)} dim/> : "—"}</td>
                    <td className="r" style={{ fontWeight: 700 }}>
                      {s ? <Money value={it.gross_monthly} revealed={rev} onReveal={() => reveal(it.user.id)}/>
                         : <span style={{ color: "#b06b00" }}>Not set</span>}
                    </td>
                    <td className="r">
                      <button className="btn" style={{ fontSize: 11 }} onClick={() => setEditId(it.user.id)}>
                        {s ? "Edit" : "Set"}
                      </button>
                    </td>
                  </tr>
                );
              })}
            </tbody>
          </table>
        )}
        {editUserId && (
          <PrEditStructureModal userId={editUserId}
                                userName={(items.find(x => x.user.id === editUserId) || {}).user && (items.find(x => x.user.id === editUserId).user.name)}
                                onClose={() => setEditId(null)}
                                onSaved={() => { setEditId(null); reload(); }}/>
        )}
      </div>
    );
  }

  function PrEditStructureModal({ userId, userName, onClose, onSaved }) {
    const [s, setS] = R.useState(null);
    const [err, setErr] = R.useState("");
    const [busy, setBusy] = R.useState(false);

    R.useEffect(() => {
      (async () => {
        try {
          const r = await window.api.payroll.structure(userId);
          setS({
            basic: Number(r.structure.basic) || 0,
            hra: Number(r.structure.hra) || 0,
            special: Number(r.structure.special) || 0,
            conveyance: Number(r.structure.conveyance) || 0,
            pf_employee: Number(r.structure.pf_employee) || 0,
            professional_tax: Number(r.structure.professional_tax) || 0,
            tds: Number(r.structure.tds) || 0,
            bank_name: r.structure.bank_name || "",
            bank_account: r.structure.bank_account || "",
            ifsc: r.structure.ifsc || "",
            pan: r.structure.pan || "",
            uan: r.structure.uan || "",
            effective_from: r.structure.effective_from || "",
            notes: r.structure.notes || "",
          });
        } catch (e) { setErr((e && e.body && (e.body.message || e.body.error)) || "Could not load"); }
      })();
    }, [userId]);

    function up(k, v) { setS(x => ({ ...x, [k]: v })); }
    async function save() {
      setBusy(true); setErr("");
      try { await window.api.payroll.saveStructure(userId, s); onSaved(); }
      catch (e) { setErr((e && e.body && (e.body.message || e.body.error)) || "Could not save"); setBusy(false); }
    }

    if (!s) {
      return window.ReactDOM.createPortal(
        <div className="modal-backdrop" onMouseDown={onClose}>
          <div className="modal" style={{ width: 500 }}><div className="modal-body">{err || "Loading…"}</div></div>
        </div>, document.body);
    }

    const gross = (Number(s.basic)||0)+(Number(s.hra)||0)+(Number(s.special)||0)+(Number(s.conveyance)||0);
    const ded = (Number(s.pf_employee)||0)+(Number(s.professional_tax)||0)+(Number(s.tds)||0);
    function fld(k, label, kind) {
      return (
        <label><div className="pr-key">{label}</div>
          <input className="pr-input" type={kind || "number"} value={s[k]}
                 onChange={e => up(k, e.target.value)}/></label>
      );
    }
    return window.ReactDOM.createPortal(
      <div className="modal-backdrop" onMouseDown={onClose}>
        <div className="modal" onMouseDown={e => e.stopPropagation()} style={{ width: 620, maxHeight: "92vh", display: "flex", flexDirection: "column" }}>
          <div className="modal-header"><div style={{ fontWeight: 700 }}>Salary structure — {userName || ""}</div>
            <button className="modal-close" onClick={onClose}>×</button></div>
          <div className="modal-body" style={{ flex: 1, overflow: "auto", display: "flex", flexDirection: "column", gap: 14 }}>
            <div>
              <div style={{ fontWeight: 800, fontSize: 12, color: "var(--ink-muted)", textTransform: "uppercase", marginBottom: 6 }}>Earnings (monthly)</div>
              <div className="pr-grid">
                {fld("basic", "Basic")}
                {fld("hra", "HRA")}
                {fld("special", "Special Allowance")}
                {fld("conveyance", "Conveyance")}
              </div>
            </div>
            <div>
              <div style={{ fontWeight: 800, fontSize: 12, color: "var(--ink-muted)", textTransform: "uppercase", marginBottom: 6 }}>Deductions (monthly)</div>
              <div className="pr-grid">
                {fld("pf_employee", "PF (Employee)")}
                {fld("professional_tax", "Professional Tax")}
                {fld("tds", "TDS")}
                {fld("effective_from", "Effective From", "date")}
              </div>
            </div>
            <div>
              <div style={{ fontWeight: 800, fontSize: 12, color: "var(--ink-muted)", textTransform: "uppercase", marginBottom: 6 }}>Bank & statutory</div>
              <div className="pr-grid">
                {fld("bank_name", "Bank name", "text")}
                {fld("bank_account", "Account No.", "text")}
                {fld("ifsc", "IFSC", "text")}
                {fld("pan", "PAN", "text")}
                {fld("uan", "UAN", "text")}
              </div>
            </div>
            <label><div className="pr-key">Notes</div>
              <textarea className="pr-input" rows={2} value={s.notes} onChange={e => up("notes", e.target.value)}/></label>
            <div style={{ background: "#f0f5ff", border: "1px solid #d6e1f4", padding: 10, borderRadius: 8,
                          display: "flex", gap: 16, fontSize: 12.5 }}>
              <div><b>Gross</b>: {fmtINR(gross)}</div>
              <div><b>Deductions</b>: {fmtINR(ded)}</div>
              <div style={{ marginLeft: "auto", color: "#0a3d91", fontWeight: 800 }}>
                Take home (before LOP): {fmtINR(gross - ded)}
              </div>
            </div>
            {err && <div style={{ color: "#c0223a", fontSize: 12 }}>{err}</div>}
          </div>
          <div className="modal-footer">
            <button className="btn" onClick={onClose} disabled={busy}>Cancel</button>
            <button className="btn btn-primary" onClick={save} disabled={busy}>{busy ? "Saving…" : "Save"}</button>
          </div>
        </div>
      </div>, document.body);
  }

  // ── EMPLOYEE VIEW — TOTP gate ─────────────────────────────────
  // Wraps the actual payroll view with a step-up auth check. While
  // unlocked (cached in sessionStorage by api.payroll.auth.*), the
  // child is rendered; otherwise the gate shows enrollment or a
  // verify prompt.
  function PpEmpPayroll() {
    const [status, setStatus] = R.useState(null); // {enrolled, enrollment_pending}
    const [unlocked, setUnlocked] = R.useState(() => !!window.api.payroll.auth.getUnlockToken());
    const [showBackup, setShowBackup] = R.useState(null); // array of plaintext codes (just enrolled)

    const reloadStatus = R.useCallback(async () => {
      try { setStatus(await window.api.payroll.auth.status()); }
      catch (e) { setStatus({ enrolled: false, error: (e && e.body && e.body.error) || "load_failed" }); }
    }, []);
    R.useEffect(() => { reloadStatus(); }, [reloadStatus]);

    // If the cached token expires while the page is open, fall back
    // to the gate. Lightweight 1-second tick.
    R.useEffect(() => {
      const id = setInterval(() => {
        const ok = !!window.api.payroll.auth.getUnlockToken();
        setUnlocked(prev => prev !== ok ? ok : prev);
      }, 1000);
      return () => clearInterval(id);
    }, []);

    if (status == null) {
      return <div className="pp-screen"><div className="pr-card" style={{ textAlign: "center", color: "var(--ink-muted)" }}>Checking authenticator…</div></div>;
    }
    if (unlocked) {
      return (
        <div>
          <PpEmpPayrollInner onLock={() => { window.api.payroll.auth.clearUnlockToken(); setUnlocked(false); }}/>
          {showBackup && <PrBackupCodesModal codes={showBackup} onClose={() => setShowBackup(null)}/>}
        </div>
      );
    }
    if (status.enrolled) {
      return <PrVerifyGate onUnlocked={() => setUnlocked(true)}/>;
    }
    return (
      <PrEnrollGate
        onEnrolled={(codes) => { setUnlocked(true); setShowBackup(codes); reloadStatus(); }}
      />
    );
  }

  // ── TOTP enrollment gate ──────────────────────────────────────
  function PrEnrollGate({ onEnrolled }) {
    const [phase, setPhase] = R.useState("intro"); // intro → qr → confirm
    const [secret, setSecret] = R.useState(null);
    const [otpUrl, setOtpUrl] = R.useState("");
    const [code, setCode] = R.useState("");
    const [busy, setBusy] = R.useState(false);
    const [err, setErr] = R.useState("");

    async function start() {
      setBusy(true); setErr("");
      try {
        const r = await window.api.payroll.auth.enroll();
        setSecret(r.secret_base32);
        setOtpUrl(r.otpauth_url);
        setPhase("qr");
      } catch (e) {
        setErr((e && e.body && (e.body.message || e.body.error)) || "Could not start enrollment");
      } finally { setBusy(false); }
    }
    async function confirm() {
      setBusy(true); setErr("");
      try {
        const r = await window.api.payroll.auth.confirm(code.replace(/\s+/g, ""));
        window.api.payroll.auth.setUnlockToken(r.unlock_token, r.expires_in);
        onEnrolled(r.backup_codes || []);
      } catch (e) {
        setErr((e && e.body && (e.body.message || e.body.error)) === "invalid_code"
          ? "That code didn't work. Try the next 30-second code."
          : ((e && e.body && (e.body.message || e.body.error)) || "Could not confirm"));
      } finally { setBusy(false); }
    }

    if (phase === "intro") {
      return (
        <div className="pr-gate">
          <div className="pr-gate-icon">🔐</div>
          <h1>Protect your payroll</h1>
          <p>Before you can see your salary, set up Google Authenticator (or any TOTP app like Authy / 1Password). It takes about a minute.</p>
          {err && <div style={{ color: "#c0223a", marginTop: 10, fontSize: 12 }}>{err}</div>}
          <button className="btn btn-primary" style={{ marginTop: 16, width: "100%" }} onClick={start} disabled={busy}>
            {busy ? "Starting…" : "Set up Authenticator"}
          </button>
        </div>
      );
    }
    if (phase === "qr") {
      return (
        <div className="pr-gate" style={{ maxWidth: 520 }}>
          <h1>Scan in Google Authenticator</h1>
          <p>Open Google Authenticator (or Authy / 1Password) and tap "+ Add a code" → "Scan a QR code". Or enter the key below by hand.</p>
          <PrQrSvg text={otpUrl}/>
          <div className="pr-secret" style={{ marginTop: 4 }}>{secret}</div>
          <button className="btn btn-primary" style={{ marginTop: 16, width: "100%" }} onClick={() => setPhase("confirm")}>
            I've added it — next
          </button>
        </div>
      );
    }
    // confirm
    return (
      <div className="pr-gate">
        <h1>Enter the 6-digit code</h1>
        <p>Open Google Authenticator and type the current code for "ZeroProject Payroll".</p>
        <input className="pr-code-input" maxLength={7} inputMode="numeric"
               autoFocus
               value={code}
               onChange={e => setCode(e.target.value.replace(/[^0-9]/g, ""))}
               onKeyDown={e => { if (e.key === "Enter" && code.length === 6) confirm(); }}
               placeholder="000000"/>
        {err && <div style={{ color: "#c0223a", marginTop: 10, fontSize: 12 }}>{err}</div>}
        <button className="btn btn-primary" style={{ marginTop: 16, width: "100%" }}
                onClick={confirm} disabled={busy || code.length !== 6}>
          {busy ? "Verifying…" : "Confirm & unlock"}
        </button>
      </div>
    );
  }

  // ── Verify gate (after enrollment) ────────────────────────────
  function PrVerifyGate({ onUnlocked }) {
    const [code, setCode] = R.useState("");
    const [busy, setBusy] = R.useState(false);
    const [err, setErr] = R.useState("");
    async function verify() {
      setBusy(true); setErr("");
      try {
        const r = await window.api.payroll.auth.verify(code.replace(/\s+/g, ""));
        window.api.payroll.auth.setUnlockToken(r.unlock_token, r.expires_in);
        onUnlocked();
      } catch (e) {
        const body = (e && e.body && (e.body.message || e.body.error));
        setErr(body === "invalid_code"
          ? "That code didn't work. Try the next 30-second code, or paste a backup code."
          : (body || "Could not verify"));
      } finally { setBusy(false); }
    }
    return (
      <div className="pr-gate">
        <div className="pr-gate-icon">🔐</div>
        <h1>Authenticator required</h1>
        <p>Enter the 6-digit code from Google Authenticator for "ZeroProject Payroll", or paste a backup code.</p>
        <input className="pr-code-input" inputMode="text"
               autoFocus
               value={code}
               onChange={e => setCode(e.target.value)}
               onKeyDown={e => { if (e.key === "Enter") verify(); }}
               placeholder="000000"/>
        {err && <div style={{ color: "#c0223a", marginTop: 10, fontSize: 12 }}>{err}</div>}
        <button className="btn btn-primary" style={{ marginTop: 16, width: "100%" }}
                onClick={verify} disabled={busy || !code.trim()}>
          {busy ? "Verifying…" : "Unlock for 15 minutes"}
        </button>
        <div style={{ fontSize: 11, color: "var(--ink-muted)", marginTop: 8 }}>
          Lost your phone? Use a backup code, or ask an owner to reset.
        </div>
      </div>
    );
  }

  // ── QR code renderer (uses CDN'd qrcode-generator) ────────────
  function PrQrSvg({ text }) {
    const ref = R.useRef(null);
    R.useEffect(() => {
      if (!ref.current || typeof window.qrcode !== "function") return;
      try {
        const q = window.qrcode(0, "M");
        q.addData(text);
        q.make();
        // moduleCount-by-moduleCount, drawn as SVG rects so it scales
        const n = q.getModuleCount();
        const cell = 5;
        const margin = 10;
        const dim = n * cell + margin * 2;
        let inner = "";
        for (let r = 0; r < n; r++) {
          for (let c = 0; c < n; c++) {
            if (q.isDark(r, c)) {
              inner += `<rect x="${margin + c * cell}" y="${margin + r * cell}" width="${cell}" height="${cell}" fill="#1c2233"/>`;
            }
          }
        }
        ref.current.innerHTML =
          `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${dim} ${dim}" width="200" height="200">
             <rect width="100%" height="100%" fill="#fff"/>${inner}
           </svg>`;
      } catch (e) {
        ref.current.textContent = "QR generator failed — use the secret below instead.";
      }
    }, [text]);
    return <div className="pr-qr-box" ref={ref}/>;
  }

  // ── Backup codes display (shown once after enrollment) ────────
  function PrBackupCodesModal({ codes, onClose }) {
    function copyAll() {
      try { navigator.clipboard && navigator.clipboard.writeText(codes.join("\n")); } catch {}
    }
    return window.ReactDOM.createPortal(
      <div className="modal-backdrop" onMouseDown={onClose}>
        <div className="modal" onMouseDown={e => e.stopPropagation()} style={{ width: 480 }}>
          <div className="modal-header"><div style={{ fontWeight: 700 }}>Save your backup codes</div>
            <button className="modal-close" onClick={onClose}>×</button></div>
          <div className="modal-body">
            <div style={{ fontSize: 13, color: "#5b6478", marginBottom: 8, lineHeight: 1.6 }}>
              Print these or save them somewhere safe. <b>This is the only time we'll show them.</b> Each code lets you sign in once if you lose your phone.
            </div>
            <div className="pr-backup-grid">
              {codes.map((c, i) => <div key={i}>{c}</div>)}
            </div>
            <div style={{ fontSize: 11, color: "var(--ink-muted)" }}>
              An owner can reset your TOTP from People → Employees if you ever lose access entirely.
            </div>
          </div>
          <div className="modal-footer">
            <button className="btn" onClick={copyAll}>Copy all</button>
            <button className="btn btn-primary" onClick={onClose}>Done</button>
          </div>
        </div>
      </div>,
      document.body);
  }

  // The inner employee payroll view (former PpEmpPayroll body).
  function PpEmpPayrollInner({ onLock }) {
    const [slips, setSlips] = R.useState(null);
    const [struct, setStruct] = R.useState(null);
    const [err, setErr] = R.useState("");
    const [openId, setOpen] = R.useState(null);
    // Single tap-to-reveal toggle: amounts are masked by default so a
    // colleague glancing at the screen / a screen-share doesn't see
    // the user's salary. The user clicks "Show amounts" (or taps any
    // individual masked pill) to reveal.
    const [revealed, setRevealed] = R.useState(false);
    function show() { setRevealed(true); }

    const reload = R.useCallback(async () => {
      try {
        const [s, st] = await Promise.all([
          window.api.payroll.mySlips(),
          window.api.payroll.myStructure().catch(() => null),
        ]);
        setSlips(s.items || []);
        setStruct(st);
      } catch (e) {
        setErr((e && e.body && (e.body.message || e.body.error)) || "Could not load");
      }
    }, []);
    R.useEffect(() => { reload(); }, [reload]);

    const pendingCount = (slips || []).filter(s => s.status !== "paid").length;

    return (
      <div className="pp-screen">
        {/* Hero: gross monthly + take-home, plus pending count. Amounts
            are hidden behind a single "Show amounts" toggle by default
            so onlookers / screenshares don't see the salary. */}
        <div className="pr-card" style={{ background: "linear-gradient(135deg,#0a3d91 0%, #0073ea 100%)", color: "#fff", border: "0" }}>
          <div style={{ display: "flex", alignItems: "center", gap: 20, flexWrap: "wrap" }}>
            <div>
              <div style={{ fontSize: 11, opacity: .85, letterSpacing: ".05em", textTransform: "uppercase" }}>Gross / month</div>
              <div style={{ fontSize: 22, fontWeight: 800, fontVariantNumeric: "tabular-nums" }}>
                {struct
                  ? (revealed
                      ? fmtINR(struct.gross_monthly || 0)
                      : <button onClick={show} className="pr-hero-mask">•••• tap to reveal</button>)
                  : "—"}
              </div>
            </div>
            <div>
              <div style={{ fontSize: 11, opacity: .85, letterSpacing: ".05em", textTransform: "uppercase" }}>Take home (before LOP)</div>
              <div style={{ fontSize: 22, fontWeight: 800, fontVariantNumeric: "tabular-nums" }}>
                {struct
                  ? (revealed
                      ? fmtINR(struct.take_home || 0)
                      : <button onClick={show} className="pr-hero-mask">••••</button>)
                  : "—"}
              </div>
            </div>
            <div style={{ marginLeft: "auto" }}>
              <div style={{ fontSize: 11, opacity: .85, letterSpacing: ".05em", textTransform: "uppercase" }}>Pending payslips</div>
              <div style={{ fontSize: 22, fontWeight: 800 }}>{pendingCount}</div>
            </div>
            <button
              onClick={() => setRevealed(r => !r)}
              title={revealed ? "Hide all amounts" : "Show all amounts"}
              style={{ background: "rgba(255,255,255,.18)", color: "#fff",
                       border: "1px solid rgba(255,255,255,.32)", borderRadius: 8,
                       padding: "6px 10px", cursor: "pointer", fontWeight: 700, fontSize: 12 }}>
              {revealed ? "🙈 Hide" : "👁 Show"}
            </button>
            <button
              onClick={onLock}
              title="Lock the payroll page (requires Authenticator again)"
              style={{ background: "rgba(255,255,255,.18)", color: "#fff",
                       border: "1px solid rgba(255,255,255,.32)", borderRadius: 8,
                       padding: "6px 10px", cursor: "pointer", fontWeight: 700, fontSize: 12 }}>
              🔒 Lock
            </button>
          </div>
        </div>

        {/* Structure breakdown — components masked behind the same
            reveal toggle as the hero. Deductions render with a leading
            minus when revealed. */}
        {struct && struct.structure && (
          <div className="pr-card">
            <div className="pr-h">
              <h2>My salary structure</h2>
              <div style={{ flex: 1 }}/>
              <button
                className={`pr-eye ${revealed ? "on" : ""}`}
                title={revealed ? "Hide components" : "Show components"}
                onClick={() => setRevealed(r => !r)}>
                {revealed ? "🙈" : "👁"}
              </button>
            </div>
            <div className="pr-grid">
              <div><div className="pr-key">Basic</div>
                <div className="pr-num"><Money value={struct.structure.basic} revealed={revealed} onReveal={show}/></div></div>
              <div><div className="pr-key">HRA</div>
                <div className="pr-num"><Money value={struct.structure.hra} revealed={revealed} onReveal={show}/></div></div>
              <div><div className="pr-key">Special</div>
                <div className="pr-num"><Money value={struct.structure.special} revealed={revealed} onReveal={show}/></div></div>
              <div><div className="pr-key">Conveyance</div>
                <div className="pr-num"><Money value={struct.structure.conveyance} revealed={revealed} onReveal={show}/></div></div>
              <div><div className="pr-key">PF (Employee)</div>
                <div className="pr-num">{revealed ? "−" + fmtINR(struct.structure.pf_employee) : <Money value={struct.structure.pf_employee} revealed={false} onReveal={show}/>}</div></div>
              <div><div className="pr-key">Professional Tax</div>
                <div className="pr-num">{revealed ? "−" + fmtINR(struct.structure.professional_tax) : <Money value={struct.structure.professional_tax} revealed={false} onReveal={show}/>}</div></div>
              <div><div className="pr-key">TDS</div>
                <div className="pr-num">{revealed ? "−" + fmtINR(struct.structure.tds) : <Money value={struct.structure.tds} revealed={false} onReveal={show}/>}</div></div>
              <div><div className="pr-key">Effective from</div><div>{struct.structure.effective_from || "—"}</div></div>
            </div>
          </div>
        )}

        {/* Payslips */}
        <div className="pr-card">
          <div className="pr-h"><h2>My payslips</h2></div>
          {err && <div style={{ color: "#c0223a", fontSize: 12 }}>{err}</div>}
          {slips == null ? (
            <div style={{ padding: 22, color: "var(--ink-muted)", textAlign: "center" }}>Loading…</div>
          ) : slips.length === 0 ? (
            <div style={{ padding: 28, color: "var(--ink-muted)", textAlign: "center",
                          border: "1px dashed var(--border-row,#eaecf1)", borderRadius: 10 }}>
              No payslips yet. They'll appear here once the admin finalises a payroll run.
            </div>
          ) : slips.map(s => (
            <div key={s.id} className="pr-rr" onClick={() => setOpen(s.id)}>
              <div className="pr-rr-period">{periodLabel(s.period_year, s.period_month)}</div>
              <div className="pr-rr-meta">
                Worked {s.present_days} / {s.working_days} days · LOP {s.lop_days}
                <span style={{ marginLeft: 10 }}>
                  <span className={`pr-pill ${s.status}`}>{s.status === "paid" ? "paid" : "pending"}</span>
                </span>
              </div>
              <div className="pr-rr-amt">
                {revealed
                  ? fmtINR(s.net_pay)
                  : <button onClick={(e) => { e.stopPropagation(); show(); }} className="pr-money-mask">
                      <span className="pr-money-dots">••••</span>
                      <span className="pr-money-hint">tap to reveal</span>
                    </button>}
              </div>
            </div>
          ))}
        </div>

        {openId && <PrSlipDetail id={openId} onClose={() => setOpen(null)}/>}
      </div>
    );
  }

  function PrSlipDetail({ id, onClose }) {
    const [data, setData] = R.useState(null);
    const [err, setErr]   = R.useState("");
    const [downloading, setDl] = R.useState(false);
    // Per-modal reveal — same default-hidden behaviour as the
    // listing. Tapping any cell or the eye in the header flips it.
    const [revealed, setRevealed] = R.useState(false);
    const show = () => setRevealed(true);
    R.useEffect(() => {
      (async () => {
        try { setData(await window.api.payroll.mySlip(id)); }
        catch (e) { setErr((e && e.body && (e.body.message || e.body.error)) || "Could not load"); }
      })();
    }, [id]);
    async function download() {
      setDl(true);
      try { await window.api.payroll.openPayslip(id); }
      catch (e) { alert((e && e.message) || "Could not open payslip"); }
      finally { setDl(false); }
    }
    if (!data) {
      return window.ReactDOM.createPortal(
        <div className="modal-backdrop" onMouseDown={onClose}>
          <div className="modal" style={{ width: 480 }}><div className="modal-body">{err || "Loading…"}</div></div>
        </div>, document.body);
    }
    const s = data.slip;
    return window.ReactDOM.createPortal(
      <div className="modal-backdrop" onMouseDown={onClose}>
        <div className="modal" onMouseDown={e => e.stopPropagation()} style={{ width: 620 }}>
          <div className="modal-header" style={{ alignItems: "center" }}>
            <div>
              <div style={{ fontWeight: 800, fontSize: 15 }}>
                {periodLabel(s.period_year, s.period_month)}
              </div>
              <div style={{ fontSize: 11.5, color: "var(--ink-muted)" }}>
                <span className={`pr-pill ${s.status}`}>{s.status === "paid" ? "paid" : "pending"}</span>
                {s.paid_at ? <span style={{ marginLeft: 8 }}>Paid on {String(s.paid_at).slice(0, 10)}</span> : null}
              </div>
            </div>
            <button
              className={`pr-eye ${revealed ? "on" : ""}`}
              title={revealed ? "Hide amounts" : "Reveal all amounts"}
              onClick={() => setRevealed(r => !r)}
              style={{ marginRight: 6 }}>
              {revealed ? "🙈" : "👁"}
            </button>
            <button className="modal-close" onClick={onClose}>×</button>
          </div>
          <div className="modal-body">
            <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 0,
                          border: "1px solid var(--border-row,#eaecf1)", borderRadius: 10, overflow: "hidden" }}>
              <div style={{ padding: 12, borderRight: "1px solid var(--border-row,#eaecf1)" }}>
                <div style={{ fontWeight: 800, fontSize: 12, color: "var(--ink-muted)",
                              textTransform: "uppercase", marginBottom: 8 }}>Earnings</div>
                <table className="pr-tbl">
                  <tbody>
                    <tr><td>Basic</td><td className="r"><Money value={s.basic}      revealed={revealed} onReveal={show}/></td></tr>
                    <tr><td>HRA</td><td className="r"><Money value={s.hra}        revealed={revealed} onReveal={show}/></td></tr>
                    <tr><td>Special Allowance</td><td className="r"><Money value={s.special}    revealed={revealed} onReveal={show}/></td></tr>
                    <tr><td>Conveyance</td><td className="r"><Money value={s.conveyance} revealed={revealed} onReveal={show}/></td></tr>
                  </tbody>
                  <tfoot><tr style={{ fontWeight: 800 }}>
                    <td>Gross</td><td className="r"><Money value={s.gross} revealed={revealed} onReveal={show}/></td></tr></tfoot>
                </table>
              </div>
              <div style={{ padding: 12 }}>
                <div style={{ fontWeight: 800, fontSize: 12, color: "var(--ink-muted)",
                              textTransform: "uppercase", marginBottom: 8 }}>Deductions</div>
                <table className="pr-tbl">
                  <tbody>
                    <tr><td>PF (Employee)</td><td className="r"><Money value={s.pf_employee} revealed={revealed} onReveal={show}/></td></tr>
                    <tr><td>Professional Tax</td><td className="r"><Money value={s.professional_tax} revealed={revealed} onReveal={show}/></td></tr>
                    <tr><td>TDS</td><td className="r"><Money value={s.tds} revealed={revealed} onReveal={show}/></td></tr>
                    <tr><td>LOP Deduction</td><td className="r"><Money value={s.lop_deduction} revealed={revealed} onReveal={show}/></td></tr>
                    <tr><td>Other</td><td className="r"><Money value={s.other_deduction} revealed={revealed} onReveal={show}/></td></tr>
                  </tbody>
                  <tfoot><tr style={{ fontWeight: 800 }}>
                    <td>Total</td><td className="r"><Money value={s.total_deductions} revealed={revealed} onReveal={show}/></td></tr></tfoot>
                </table>
              </div>
            </div>
            <div className="pr-foot pr-net" style={{ borderRadius: 10, marginTop: 10 }}>
              <span>Net pay</span>
              <span className="pr-num">
                {revealed
                  ? fmtINR(s.net_pay)
                  : <button onClick={show} className="pr-money-mask">
                      <span className="pr-money-dots">••••</span>
                      <span className="pr-money-hint">tap to reveal</span>
                    </button>}
              </span>
            </div>
            <div style={{ fontSize: 12, color: "var(--ink-muted)", fontStyle: "italic", marginTop: 6 }}>
              {revealed ? data.net_pay_words : "— Tap above to reveal the amount in words —"}
            </div>
            <div style={{ fontSize: 12, color: "var(--ink-muted)", marginTop: 8 }}>
              Worked {s.present_days} of {s.working_days} days · LOP {s.lop_days}
            </div>
          </div>
          <div className="modal-footer">
            <button className="btn" onClick={onClose}>Close</button>
            <button className="btn btn-primary" onClick={download} disabled={downloading}>
              {downloading ? "Opening…" : "Download payslip"}
            </button>
          </div>
        </div>
      </div>, document.body);
  }

  Object.assign(window, { PpPayrollAdmin, PpEmpPayroll });
})();
