// ============================================================================
// IMS — Payments (Admin full view + role-scoped view)
// Cycle list, cycle detail, CSV export, mark paid (individual + bulk)
// Filters, search, pagination, minimum-threshold banner
// ============================================================================

function PaymentsPage({ variant }) {
  const { data, role, notify, activePersonId } = useApp();
  const allCycles = variant === "os" ? data.osPaymentCycles : data.cmPaymentCycles;
  // Non-admin sees only cycles that contain their own items
  const cycles = role === "admin"
    ? allCycles
    : allCycles.filter(c => (c.items || []).some(it => it.specialistId === activePersonId));
  const [openCycle, setOpenCycle] = useState(null);
  const [searchQ, setSearchQ] = useState("");
  const [pg, setPg] = useState(1);
  const perPage = 8;

  const title = variant === "os" ? "Outreach Specialist Payments" : "Collaboration Manager Payments";
  const subtitle = role === "admin"
    ? "Every Friday 2 PM UK, the system snapshots the past 7 days. 52 cycles/year. Export to CSV, send to finance, then mark Paid."
    : "Your weekly payment cycles. You see only your own payments.";

  if (openCycle) {
    return <CycleDetail cycle={openCycle} onBack={() => setOpenCycle(null)} variant={variant} />;
  }

  if (!cycles || cycles.length === 0) {
    return (
      <div className="page">
        <div className="page-head">
          <div>
            <h1>{title}</h1>
            <div className="subtitle">{subtitle}</div>
          </div>
        </div>
        <div style={{ textAlign: "center", padding: "80px 0", color: "var(--ink-3)", fontFamily: "var(--font-mono)", fontSize: 12, letterSpacing: "0.06em" }}>
          NO PAYMENT CYCLES YET — cycles are generated every Friday at 14:00 UK
        </div>
      </div>
    );
  }

  const filteredCycles = cycles.filter(c =>
    !searchQ || c.id.toLowerCase().includes(searchQ.toLowerCase()) || c.range.toLowerCase().includes(searchQ.toLowerCase())
  );
  const totalPages = Math.max(1, Math.ceil(filteredCycles.length / perPage));
  const pagedCycles = filteredCycles.slice((pg - 1) * perPage, pg * perPage);

  return (
    <div className="page">
      <div className="page-head">
        <div>
          <h1>{title}</h1>
          <div className="subtitle">{subtitle}</div>
        </div>
        <div className="meta">
          <span>Snapshot · Friday 14:00 UK</span>
          <span>·</span>
          <span>Next: Apr 24, 2026</span>
        </div>
      </div>

      <div className="kpi-row">
        <div className="kpi"><div className="label">This Cycle</div><div className="value">${cycles[0].total.toFixed(2)}</div><div className="delta">{cycles[0].range}</div></div>
        <div className="kpi"><div className="label">Pending Pay</div><div className="value">{cycles[0].unpaid}</div><div className="delta">line items</div></div>
        <div className="kpi"><div className="label">Paid</div><div className="value">{cycles[0].paid}</div><div className="delta">line items</div></div>
        <div className="kpi featured"><div className="label">Cycle ID</div><div className="value" style={{ fontSize: 18 }}>{cycles[0].id}</div><div className="delta">generated {cycles[0].generatedAt}</div></div>
      </div>

      <div className="toolbar">
        <div className="spacer" />
        <div className="search">
          <Ic.search />
          <input placeholder="Search cycle ID or date range…" value={searchQ} onChange={e => { setSearchQ(e.target.value); setPg(1); }} />
          {searchQ && <button className="search-clear" onClick={() => { setSearchQ(""); setPg(1); }}><Ic.x /></button>}
        </div>
      </div>

      <div className="table-wrap">
        <table className="ims">
          <thead>
            <tr>
              <th style={{ width: 36, color: "var(--ink-3)", fontSize: 11 }}>#</th>
              <th>Cycle ID</th>
              <th>Range</th>
              <th>Generated</th>
              <th className="num">Total</th>
              <th className="num">Paid</th>
              <th className="num">Unpaid / Rolled</th>
              <th>Status</th>
              <th></th>
            </tr>
          </thead>
          <tbody>
            {pagedCycles.map((c, idx) => {
              const globalIdx = (pg - 1) * perPage + idx;
              const cycleNumber = cycles.length - globalIdx;
              const isCurrent = globalIdx === 0 && !searchQ;
              const rolledCount = (c.items || []).filter(it => it.status === "Rolled Over").length;
              const trueUnpaid = (c.items || []).filter(it => it.status === "Unpaid").length;
              const allPaid = c.paid > 0 && trueUnpaid === 0 && rolledCount === 0;
              const cycleStatus = isCurrent
                ? { tone: "info", text: "Open · Current" }
                : allPaid
                  ? { tone: "ok", text: "Closed · Paid" }
                  : rolledCount > 0
                    ? { tone: "warn", text: `${rolledCount} Rolled Over` }
                    : { tone: "neutral", text: "Closed" };
              return (
                <tr key={c.id} className="clickable" onClick={() => setOpenCycle(c)}
                  style={isCurrent ? { background: "var(--info-bg, #f0f7ff)" } : {}}>
                  <td style={{ fontFamily: "var(--font-mono)", fontSize: 11, color: "var(--ink-3)", textAlign: "center" }}>
                    {cycleNumber}
                  </td>
                  <td style={{ whiteSpace: "nowrap" }}><span className="pill">{c.id}</span></td>
                  <td>{c.range}</td>
                  <td style={{ fontFamily: "var(--mono)", fontSize: 11, color: "var(--ink-3)" }}>{c.generatedAt}</td>
                  <td className="num">${c.total.toFixed(2)}</td>
                  <td className="num"><Chip tone="ok" xs>{c.paid}</Chip></td>
                  <td className="num">
                    {trueUnpaid > 0 && <Chip tone="warn" xs>{trueUnpaid} unpaid</Chip>}
                    {rolledCount > 0 && <Chip tone="neutral" xs>{rolledCount} rolled</Chip>}
                    {trueUnpaid === 0 && rolledCount === 0 && <span className="text-mute">—</span>}
                  </td>
                  <td><Chip tone={cycleStatus.tone} xs>{cycleStatus.text}</Chip></td>
                  <td style={{ textAlign: "right" }}><button className="btn sm ghost">Open <Ic.arrowRight /></button></td>
                </tr>
              );
            })}
            {pagedCycles.length === 0 && (
              <tr><td colSpan="8" style={{ textAlign: "center", padding: 32, color: "var(--ink-3)" }}>No cycles match your search.</td></tr>
            )}
          </tbody>
        </table>
      </div>

      <TablePagination page={pg} totalPages={totalPages} onPage={setPg} total={filteredCycles.length} perPage={perPage} />
    </div>
  );
}

function SpecialistThresholdPanel({ specialistTotals, getThreshold }) {
  const [showModal, setShowModal] = React.useState(false);
  const [sortKey,   setSortKey]   = React.useState("status");
  const [sortDir,   setSortDir]   = React.useState(1);
  const [search,    setSearch]    = React.useState("");
  const [pg,        setPg]        = React.useState(1);
  const PER_PAGE = 8;

  const rows = React.useMemo(() => Object.entries(specialistTotals).map(([spId, totals]) => {
    const threshold   = getThreshold(spId);
    const below       = totals.unpaid > 0 && totals.unpaid < threshold;
    const eligible    = totals.unpaid >= threshold;
    const pct         = Math.min(100, threshold > 0 ? Math.round((totals.unpaid / threshold) * 100) : 0);
    const statusOrder = below ? 0 : eligible ? 1 : 2;
    return { spId, name: totals.name, unpaid: totals.unpaid, threshold, below, eligible, pct, statusOrder };
  }), [specialistTotals, getThreshold]);

  const belowCount    = rows.filter(r => r.below).length;
  const eligibleCount = rows.filter(r => r.eligible).length;
  const totalCount    = rows.length;
  const hasWarning    = belowCount > 0;

  if (totalCount === 0) return null;

  // Single specialist — compact inline card only, no modal needed
  if (totalCount === 1) {
    const r = rows[0];
    return (
      <div style={{ display: "flex", marginBottom: 14 }}>
        <div style={{
          display: "flex", alignItems: "center", gap: 12, padding: "10px 16px",
          borderRadius: 10, border: `1px solid ${r.below ? "var(--warn-line, #f5c842)" : "var(--line)"}`,
          background: r.below ? "var(--warn-bg, #fffbea)" : "var(--surface)",
        }}>
          <div>
            <div style={{ fontWeight: 600, fontSize: 13 }}>{r.name}</div>
            <div style={{ fontSize: 11, color: "var(--ink-3)", fontFamily: "var(--font-mono)", marginTop: 2 }}>
              ${r.unpaid.toFixed(2)} unpaid · threshold ${r.threshold}
            </div>
          </div>
          {r.below ? <Chip tone="warn" xs>Below threshold</Chip>
            : r.eligible ? <Chip tone="ok" xs>Eligible</Chip>
            : <Chip tone="neutral" xs>No unpaid</Chip>}
        </div>
      </div>
    );
  }

  const sortedFiltered = React.useMemo(() => {
    const q = search.trim().toLowerCase();
    const f = q ? rows.filter(r => r.name.toLowerCase().includes(q)) : rows;
    return [...f].sort((a, b) => {
      const av = sortKey === "status" ? a.statusOrder : a[sortKey];
      const bv = sortKey === "status" ? b.statusOrder : b[sortKey];
      if (typeof av === "string") return av.localeCompare(bv) * sortDir;
      return (av - bv) * sortDir;
    });
  }, [rows, search, sortKey, sortDir]);

  const totalPages = Math.max(1, Math.ceil(sortedFiltered.length / PER_PAGE));
  const paged      = sortedFiltered.slice((pg - 1) * PER_PAGE, pg * PER_PAGE);

  const toggleSort = (key) => {
    if (sortKey === key) setSortDir(d => -d);
    else { setSortKey(key); setSortDir(1); }
    setPg(1);
  };

  const handleSearch = (v) => { setSearch(v); setPg(1); };

  const openModal = () => {
    setSearch(""); setSortKey("status"); setSortDir(1); setPg(1);
    setShowModal(true);
  };

  const ChevronIcon = ({ up }) => (
    <svg width="10" height="10" viewBox="0 0 10 10" fill="none" style={{ display: "inline", verticalAlign: "middle", marginLeft: 2 }}>
      <path d={up ? "M2 7 L5 3 L8 7" : "M2 3 L5 7 L8 3"} stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round"/>
    </svg>
  );

  const SortTh = ({ k, label, right }) => {
    const active = sortKey === k;
    return (
      <th onClick={() => toggleSort(k)} className={right ? "num" : ""} style={{ cursor: "pointer", userSelect: "none", whiteSpace: "nowrap", color: active ? "var(--ink-1)" : "var(--ink-3)" }}>
        {label}
        {active
          ? <ChevronIcon up={sortDir === 1} />
          : <svg width="10" height="10" viewBox="0 0 10 10" fill="none" style={{ display: "inline", verticalAlign: "middle", marginLeft: 2, opacity: 0.25 }}><path d="M2 4 L5 2 L8 4M2 6 L5 8 L8 6" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round"/></svg>}
      </th>
    );
  };

  const borderColor = hasWarning ? "var(--warn-line, #f5c842)" : "var(--ok-line, #86efac)";

  return (
    <>
      {/* ── Compact banner — no layout shift ── */}
      <div style={{ marginBottom: 16, borderRadius: 10, border: `1px solid ${borderColor}`, background: hasWarning ? "var(--warn-bg, #fffbea)" : "var(--ok-bg, #f0fdf4)", overflow: "hidden" }}>
        <div style={{ display: "flex", alignItems: "center", gap: 12, padding: "11px 16px" }}>
          <div style={{ width: 32, height: 32, borderRadius: 8, flexShrink: 0, display: "flex", alignItems: "center", justifyContent: "center", background: hasWarning ? "rgba(245,200,66,0.2)" : "rgba(134,239,172,0.25)" }}>
            {hasWarning
              ? <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#b45309" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M10.29 3.86L1.82 18a2 2 0 001.71 3h16.94a2 2 0 001.71-3L13.71 3.86a2 2 0 00-3.42 0z"/><line x1="12" y1="9" x2="12" y2="13"/><line x1="12" y1="17" x2="12.01" y2="17"/></svg>
              : <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#16a34a" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round"><polyline points="20 6 9 17 4 12"/></svg>}
          </div>
          <div style={{ flex: 1, minWidth: 0 }}>
            <div style={{ fontWeight: 600, fontSize: 13, color: "var(--ink-1)", lineHeight: 1.3 }}>
              {hasWarning ? `${belowCount} specialist${belowCount > 1 ? "s" : ""} below payout threshold` : `All ${totalCount} specialists eligible for payout`}
            </div>
            <div style={{ fontSize: 11, color: "var(--ink-3)", marginTop: 2, display: "flex", gap: 12 }}>
              <span style={{ color: "#16a34a", fontWeight: 500 }}>{eligibleCount} eligible</span>
              {belowCount > 0 && <span style={{ color: "#b45309", fontWeight: 500 }}>{belowCount} below threshold</span>}
              {totalCount - eligibleCount - belowCount > 0 && <span>{totalCount - eligibleCount - belowCount} no unpaid</span>}
              <span>{totalCount} total</span>
            </div>
          </div>
          <button onClick={openModal} className="btn sm ghost" style={{ flexShrink: 0 }}>
            View all <Ic.arrowRight />
          </button>
        </div>
      </div>

      {/* ── Modal — no page layout shift ── */}
      {showModal && (
        <Modal open={true} onClose={() => setShowModal(false)}
          title={hasWarning ? `Payout threshold — ${belowCount} below` : "Payout threshold — all eligible"}
          sub={`${totalCount} specialists · minimum threshold check`}
          size="wide"
        >
          {/* Toolbar */}
          <div style={{ display: "flex", alignItems: "center", gap: 10, marginBottom: 12 }}>
            <div className="search" style={{ flex: 1, maxWidth: 280 }}>
              <Ic.search />
              <input placeholder="Filter specialists…" value={search} onChange={e => handleSearch(e.target.value)} />
              {search && <button className="search-clear" onClick={() => handleSearch("")}><Ic.x /></button>}
            </div>
            <div style={{ fontSize: 11, color: "var(--ink-3)", fontFamily: "var(--font-mono)", flexShrink: 0 }}>
              {sortedFiltered.length === 0 ? "0 results" : `${(pg - 1) * PER_PAGE + 1}–${Math.min(pg * PER_PAGE, sortedFiltered.length)} of ${sortedFiltered.length}`}
            </div>
          </div>

          {/* Table */}
          <div className="table-wrap">
            <table className="ims" style={{ fontSize: 12 }}>
              <thead>
                <tr>
                  <SortTh k="name"      label="Specialist" />
                  <SortTh k="unpaid"    label="Unpaid"     right />
                  <SortTh k="threshold" label="Threshold"  right />
                  <th style={{ minWidth: 130 }}>Progress to threshold</th>
                  <SortTh k="status"    label="Status" />
                </tr>
              </thead>
              <tbody>
                {paged.map(r => (
                  <tr key={r.spId}>
                    <td style={{ fontWeight: 500 }}>{r.name}</td>
                    <td className="num" style={{ fontFamily: "var(--font-mono)", fontWeight: 600, color: r.below ? "#b45309" : r.eligible ? "#16a34a" : "var(--ink-2)" }}>
                      ${r.unpaid.toFixed(2)}
                    </td>
                    <td className="num" style={{ fontFamily: "var(--font-mono)", color: "var(--ink-3)" }}>
                      ${r.threshold}
                    </td>
                    <td>
                      <div style={{ display: "flex", alignItems: "center", gap: 8 }}>
                        <div style={{ flex: 1, height: 6, borderRadius: 99, background: "var(--line)", overflow: "hidden", minWidth: 80 }}>
                          <div style={{ height: "100%", borderRadius: 99, width: `${r.pct}%`, background: r.below ? "#f59e0b" : "#22c55e", transition: "width 0.3s ease" }} />
                        </div>
                        <span style={{ fontSize: 10, color: "var(--ink-3)", fontFamily: "var(--font-mono)", minWidth: 30, textAlign: "right" }}>{r.pct}%</span>
                      </div>
                    </td>
                    <td>
                      {r.below    ? <Chip tone="warn"    xs>Below threshold</Chip>
                      : r.eligible ? <Chip tone="ok"      xs>Eligible</Chip>
                                   : <Chip tone="neutral" xs>No unpaid</Chip>}
                    </td>
                  </tr>
                ))}
                {paged.length === 0 && (
                  <tr><td colSpan="5" style={{ textAlign: "center", color: "var(--ink-3)", padding: 28 }}>
                    {search ? `No specialists match "${search}".` : "No specialists."}
                  </td></tr>
                )}
              </tbody>
            </table>
          </div>

          <TablePagination page={pg} totalPages={totalPages} onPage={setPg} total={sortedFiltered.length} perPage={PER_PAGE} />
        </Modal>
      )}
    </>
  );
}

function CycleDetail({ cycle, onBack, variant }) {
  const { role, notify, updatePaymentItem, bulkUpdatePayments, activePersonId, data } = useApp();

  // Old archived cycles may not have itemized data
  if (!cycle.items || cycle.items.length === 0) {
    return (
      <div className="page">
        <button className="btn ghost sm" onClick={onBack}><Ic.arrowLeft /> Back to cycles</button>
        <div className="page-head" style={{ marginTop: 12 }}>
          <div>
            <h1>Cycle {cycle.id}</h1>
            <div className="subtitle">{cycle.range} · generated {cycle.generatedAt}</div>
          </div>
        </div>
        <div className="kpi-row">
          <div className="kpi"><div className="label">Total</div><div className="value">${cycle.total.toFixed(2)}</div></div>
          <div className="kpi"><div className="label">Paid</div><div className="value">{cycle.paid}</div><div className="delta">line items</div></div>
          <div className="kpi"><div className="label">Unpaid</div><div className="value">{cycle.unpaid || 0}</div><div className="delta">line items</div></div>
        </div>
        <div style={{ textAlign: "center", padding: "60px 0", color: "var(--ink-3)", fontFamily: "var(--font-mono)", fontSize: 12, letterSpacing: "0.06em", border: "1px solid var(--line)", borderRadius: 10, background: "var(--surface)" }}>
          NO ITEMIZED BREAKDOWN — this cycle predates line-item tracking
        </div>
      </div>
    );
  }

  // Non-admin sees only their own items
  const visibleItems = role === "admin" ? cycle.items : cycle.items.filter(it => it.specialistId === activePersonId);
  const [items, setItems] = useState(visibleItems);
  const [manual, setManual] = useState(false);
  const [selected, setSelected] = useState(new Set());
  const [noteFor, setNoteFor] = useState(null);
  const [noteText, setNoteText] = useState("");
  const [statusFilter, setStatusFilter] = useState(null);
  const [kindFilter, setKindFilter] = useState(null);
  const [searchQ, setSearchQ] = useState("");
  const [pg, setPg] = useState(1);
  const perPage = 5;

  // Build per-specialist total map and threshold lookup
  const specialistTotals = useMemo(() => {
    const map = {};
    items.forEach(it => {
      if (!map[it.specialistId]) map[it.specialistId] = { name: it.specialist, total: 0, unpaid: 0 };
      map[it.specialistId].total += it.amount;
      if (it.status !== "Paid") map[it.specialistId].unpaid += it.amount;
    });
    return map;
  }, [items]);

  const getThreshold = (specialistId) => {
    const people = variant === "os"
      ? (data?.outreachSpecialists || [])
      : (data?.collabManagers || []);
    const person = people.find(p => p.id === specialistId);
    return person?.minPayoutThreshold ?? 50;
  };

  const isBelowThreshold = (specialistId) => {
    const totals = specialistTotals[specialistId];
    if (!totals) return false;
    return totals.unpaid < getThreshold(specialistId);
  };

  const markPaid = (id) => {
    const item = items.find(it => it.id === id);
    if (item && isBelowThreshold(item.specialistId)) {
      const threshold = getThreshold(item.specialistId);
      const unpaid = specialistTotals[item.specialistId]?.unpaid || 0;
      notify(`⚠️ ${item.specialist}'s unpaid total ($${unpaid.toFixed(2)}) is below the $${threshold} threshold. Leave Unpaid — it will roll over to the next cycle.`, "warn");
      return;
    }
    const update = { status: "Paid" };
    setItems(prev => prev.map(it => it.id === id ? { ...it, ...update } : it));
    updatePaymentItem?.(cycle.id, id, update);
    notify(`Payment ${id} marked as Paid.`);
  };

  const bulkMarkPaid = () => {
    const ids = [...selected];
    // Check if any selected specialist is below threshold
    const belowThresholdNames = [...new Set(
      items.filter(it => selected.has(it.id) && isBelowThreshold(it.specialistId)).map(it => it.specialist)
    )];
    if (belowThresholdNames.length > 0) {
      notify(`⚠️ ${belowThresholdNames.join(", ")} is below the minimum threshold. Remove their items from selection or wait for rollover.`, "warn");
      return;
    }
    const update = { status: "Paid" };
    setItems(prev => prev.map(it => selected.has(it.id) ? { ...it, ...update } : it));
    bulkUpdatePayments?.(cycle.id, ids, update);
    const n = ids.length;
    setSelected(new Set());
    setManual(false);
    notify(`${n} payments changed to Paid.`);
  };

  const exportCsv = () => notify(`Cycle ${cycle.id} exported to CSV. Sent to finance@company.com.`);

  const toggle = (id) => {
    const n = new Set(selected);
    n.has(id) ? n.delete(id) : n.add(id);
    setSelected(n);
  };

  const { statuses, kinds } = useMemo(() => ({
    statuses: [...new Set(items.map(it => it.status))],
    kinds:    [...new Set(items.map(it => it.kind))],
  }), [items]);

  const filtered = useMemo(() => {
    const q = searchQ.toLowerCase();
    return items.filter(it => {
      if (statusFilter && it.status !== statusFilter) return false;
      if (kindFilter && it.kind !== kindFilter) return false;
      if (q && !it.influencer.toLowerCase().includes(q) && !it.specialist.toLowerCase().includes(q) && !it.id.toLowerCase().includes(q) && !(it.campaignName || "").toLowerCase().includes(q)) return false;
      return true;
    });
  }, [items, statusFilter, kindFilter, searchQ]);

  const totalPages = Math.max(1, Math.ceil(filtered.length / perPage));
  const paged = filtered.slice((pg - 1) * perPage, pg * perPage);

  return (
    <div className="page">
      <button className="btn ghost sm" onClick={onBack}><Ic.arrowLeft /> Back to cycles</button>
      <div className="page-head" style={{ marginTop: 12 }}>
        <div>
          <h1>Cycle {cycle.id}</h1>
          <div className="subtitle">{cycle.range} · generated {cycle.generatedAt}</div>
        </div>
        <div style={{ display: "flex", gap: 8 }}>
          {role === "admin" && (
            <>
              <button className="btn" onClick={exportCsv}><Ic.copy /> Export CSV</button>
              {!manual && <button className="btn" onClick={() => setManual(true)}>Bulk Mark Paid</button>}
            </>
          )}
        </div>
      </div>

      <SpecialistThresholdPanel specialistTotals={specialistTotals} getThreshold={getThreshold} />

      {manual && (
        <div className="sticky-widget">
          <span className="count">{selected.size} selected</span>
          <span className="hint">Tick processed payments → mark Paid in bulk</span>
          <div className="spacer" />
          <button className="btn primary" disabled={!selected.size} onClick={bulkMarkPaid}>Change to Paid</button>
          <button className="x" onClick={() => { setManual(false); setSelected(new Set()); }}><Ic.x /></button>
        </div>
      )}

      <div className="toolbar">
        {statuses.map(s => (
          <button key={s} className={`btn sm ${statusFilter === s ? "primary" : ""}`} onClick={() => { setStatusFilter(statusFilter === s ? null : s); setPg(1); }}>
            {s}
          </button>
        ))}
        {kinds.length > 1 && kinds.map(k => (
          <button key={k} className={`btn sm ${kindFilter === k ? "primary" : ""}`} onClick={() => { setKindFilter(kindFilter === k ? null : k); setPg(1); }}>
            {k}
          </button>
        ))}
        {(statusFilter || kindFilter) && <button className="btn sm ghost" onClick={() => { setStatusFilter(null); setKindFilter(null); setPg(1); }}><Ic.x /> Clear</button>}
        <div className="spacer" />
        <div className="search">
          <Ic.search />
          <input placeholder="Search payments…" value={searchQ} onChange={e => { setSearchQ(e.target.value); setPg(1); }} />
          {searchQ && <button className="search-clear" onClick={() => { setSearchQ(""); setPg(1); }}><Ic.x /></button>}
        </div>
      </div>

      <div className="table-wrap">
        <table className="ims">
          <thead>
            <tr>
              {manual && <th style={{ width: 36 }}></th>}
              <th style={{ whiteSpace: "nowrap" }}>Payment ID</th>
              <th>Influencer</th>
              <th>{variant === "os" ? "Specialist" : "Manager"}</th>
              <th>Kind</th>
              {role === "admin" && <th>Campaign</th>}
              <th className="num">Amount</th>
              <th>Status</th>
              {role === "admin" && <th>Notes</th>}
              {role === "admin" && !manual && <th style={{ width: 100 }}></th>}
            </tr>
          </thead>
          <tbody>
            {paged.map(it => (
              <tr key={it.id} className={manual ? (selected.has(it.id) ? "selected" : "") : ""} onClick={manual ? () => toggle(it.id) : undefined} style={manual ? { cursor: "pointer" } : undefined}>
                {manual && (
                  <td>
                    <label className="checkbox" onClick={e => e.stopPropagation()}>
                      <input type="checkbox" checked={selected.has(it.id)} onChange={() => toggle(it.id)} />
                      <span className="box" />
                    </label>
                  </td>
                )}
                <td style={{ whiteSpace: "nowrap" }}><span className="pill">{it.id}</span></td>
                <td>
                  <div style={{ display: "flex", alignItems: "center", gap: 8 }}>
                    <InfCell name={it.influencer} handle={it.handle} />
                    {it.aiFlag && <span className="warn-tri" title="AI flag on evidence"><Ic.warn /></span>}
                  </div>
                </td>
                <td style={{ fontSize: 12.5 }}>{it.specialist}</td>
                <td><Chip tone={it.kind === "Bonus" ? "accent" : "info"} xs>{it.kind}</Chip></td>
                {role === "admin" && <td className="text-xs text-mute">{it.campaignName || "—"}</td>}
                <td className="num" style={{ fontFamily: "var(--mono)", fontWeight: 600 }}>${it.amount.toFixed(2)}</td>
                <td>
                  <StatusChip status={it.status} />
                  {it.rolledOverFrom && <div style={{ fontSize: 10, color: "var(--ink-3)", fontFamily: "var(--font-mono)", marginTop: 2 }}>from {it.rolledOverFrom}</div>}
                </td>
                {role === "admin" && (
                  <td className="notes-cell">
                    {it.note ? (
                      <div style={{ cursor: "pointer" }} onClick={(e) => { e.stopPropagation(); setNoteFor(it); setNoteText(it.note || ""); }} title="Click to edit note">
                        <div className="note-preview">{it.note}</div>
                        <div className="note-tooltip">{it.note}</div>
                      </div>
                    ) : (
                      <button className="btn ghost sm" onClick={(e) => { e.stopPropagation(); setNoteFor(it); setNoteText(""); }} style={{ color: "var(--ink-3)", fontSize: 11 }}>
                        <Ic.plus /> Note
                      </button>
                    )}
                    {it.rolledOverFrom && (
                      <div style={{ fontSize: 10, color: "var(--ink-3)", marginTop: 2, fontFamily: "var(--font-mono)" }}>
                        ↑ from {it.rolledOverFrom}
                      </div>
                    )}
                  </td>
                )}
                {role === "admin" && !manual && (
                  <td style={{ textAlign: "right" }}>
                    {it.status !== "Paid" && (() => {
                      // Fix 9: disable Mark Paid when specialist is below threshold
                      const belowThreshold = isBelowThreshold(it.specialistId);
                      const threshold = getThreshold(it.specialistId);
                      const unpaid = specialistTotals[it.specialistId]?.unpaid || 0;
                      return (
                        <button
                          className="btn sm"
                          disabled={belowThreshold}
                          title={belowThreshold ? `Below $${threshold} threshold (unpaid: $${unpaid.toFixed(2)}) — will roll over to next cycle` : undefined}
                          onClick={(e) => { e.stopPropagation(); markPaid(it.id); }}
                        >
                          Mark Paid
                        </button>
                      );
                    })()}
                  </td>
                )}
              </tr>
            ))}
            {paged.length === 0 && (
              <tr><td colSpan={manual ? 9 : 8} style={{ textAlign: "center", padding: 32, color: "var(--ink-3)" }}>No payments match filters.</td></tr>
            )}
          </tbody>
        </table>
      </div>

      <TablePagination page={pg} totalPages={totalPages} onPage={setPg} total={filtered.length} perPage={perPage} />

      {noteFor && (
        <Modal open={true} onClose={() => { setNoteFor(null); setNoteText(""); }} title={`Payment note · ${noteFor.id}`} sub="Admin-only"
          foot={
            <>
              <span className="text-xs text-mute">Only Admin can read payment notes</span>
              <button className="btn primary" disabled={!noteText.trim()} onClick={() => {
                const update = { note: noteText.trim() };
                setItems(prev => prev.map(it => it.id === noteFor.id ? { ...it, ...update } : it));
                updatePaymentItem?.(cycle.id, noteFor.id, update);
                notify(`Note saved for ${noteFor.id}.`);
                setNoteFor(null);
                setNoteText("");
              }}>Save Note</button>
            </>
          }>
          {noteFor.rolledOverFrom && (
            <div style={{ fontSize: 11, color: "var(--ink-3)", marginBottom: 8, padding: "6px 10px", background: "var(--surface-2, #f5f5f5)", borderRadius: 6, fontFamily: "var(--font-mono)" }}>
              Rolled over from cycle {noteFor.rolledOverFrom}
            </div>
          )}
          <textarea
            className="textarea"
            placeholder="e.g. Hold — under wire threshold. Roll to next cycle."
            autoFocus
            value={noteText}
            onChange={e => setNoteText(e.target.value)}
          />
        </Modal>
      )}
    </div>
  );
}

Object.assign(window, { PaymentsPage, CycleDetail });
