// ── Settlement Analytics — KPI Drill Popups ─────────────────────────────────
// Every KPI tile / chart element in Vendor & Transport Settlement Analytics
// opens one of these instead of scrolling to the ledger. Content is 100%
// derived from window.SettlementIntel + live Store data passed in via `ctx`
// (see buildSECtx in vendor-settlement-analytics.jsx / transport-*). No
// hardcoded numbers. Reuses window.DashBTable/DashKPIs (dashboard-drilldown.jsx)
// for tabular breakdowns so styling matches the rest of the ERP.
const { useState: skSt, useEffect: skEf } = React;

// ── Ripple micro-interaction — attach via onMouseDown={window.seRipple} ────
window.seRipple = function (e) {
  const el = e.currentTarget;
  // Buttons / toggle-segmented controls / action chips must have a pixel-stable
  // footprint on press — no scaling ripple bloom (reads as an expand/contract
  // "pop"). They get a pure opacity/background dip via their :active CSS instead.
  // The bloom is kept only on large surfaces (rows, cards, chart bars/slices).
  if (el.matches && el.matches('.se-timechip,.se-graintab,.se-statuschip,.se-legenditem,.se-exportbtn,.se-execchip,button')) return;
  const rect = el.getBoundingClientRect();
  if (rect.width < 2 || rect.height < 2) return;
  // Pointer position — resolved identically for mouse and touch. Touch events
  // carry coordinates on `touches`/`changedTouches`; a mouse event carries them
  // on the event itself. Fall back to the element centre for keyboard/synthetic
  // activation that has no pointer position.
  const pt = (e.touches && e.touches[0]) || (e.changedTouches && e.changedTouches[0]) || e;
  const hasPos = typeof pt.clientX === 'number';
  const cx = hasPos ? pt.clientX : rect.left + rect.width / 2;
  const cy = hasPos ? pt.clientY : rect.top + rect.height / 2;
  // The ripple is an absolutely-positioned child, so its offsets are only
  // correct when the clicked element is itself the positioned containing block
  // AND clips its overflow. KPI tiles (.se-kpi) aren't relative/clipped by
  // default, which is what made the ripple anchor to a distant ancestor and
  // bloom from a corner / spill outside the card. Promote the element in place
  // (only if it isn't already) so the ripple always originates from the exact
  // click/tap point and stays contained.
  const cs = getComputedStyle(el);
  if (cs.position === 'static') el.style.position = 'relative';
  if (cs.overflow !== 'hidden') el.style.overflow = 'hidden';
  const span = document.createElement('span');
  const size = Math.max(rect.width, rect.height) * 1.6;
  span.className = 'se-ripple';
  span.style.width = span.style.height = size + 'px';
  span.style.left = (cx - rect.left - size / 2) + 'px';
  span.style.top = (cy - rect.top - size / 2) + 'px';
  el.appendChild(span);
  setTimeout(() => span.remove(), 520);
};

// ── Glass popup shell — Apple-style: blur backdrop, scale+fade in, ESC /
// outside-click close, mobile responsive (see CSS in settlement-performance-center.jsx).
function SEKpiPopup({ title, subtitle, icon, accent, onClose, onExport, onPrint, children, wide }) {
  const [show, setShow] = skSt(false);
  skEf(() => {
    const t = setTimeout(() => setShow(true), 10);
    function onKey(e) { if (e.key === 'Escape') onClose(); }
    window.addEventListener('keydown', onKey);
    return () => { clearTimeout(t); window.removeEventListener('keydown', onKey); };
    // eslint-disable-next-line
  }, []);
  return ReactDOM.createPortal(
    <div className={`sekp-bg ${show ? 'sekp-in' : ''}`} onClick={e => { if (e.target === e.currentTarget) onClose(); }}>
      <div className={`sekp-modal ${wide ? 'sekp-wide' : ''}`} role="dialog" aria-modal="true">
        <div className="sekp-hd">
          <div className="sekp-hd-ic" style={{ background: accent + '17', color: accent }}><window.SEIcon name={icon || 'BarChart3'} size={20} /></div>
          <div style={{ flex: 1, minWidth: 0 }}>
            <div className="sekp-title">{title}</div>
            {subtitle && <div className="sekp-sub">{subtitle}</div>}
          </div>
          <div className="sekp-actions">
            {onExport && <button className="se-exportbtn" onMouseDown={window.seRipple} onClick={onExport}><window.SEIcon name="Download" size={13} /> Export</button>}
            {onPrint && <button className="se-exportbtn" onMouseDown={window.seRipple} onClick={onPrint}><window.SEIcon name="Printer" size={13} /> Print</button>}
            <button className="mod-x" onClick={onClose}>×</button>
          </div>
        </div>
        <div className="sekp-body">{children}</div>
      </div>
    </div>,
    document.body
  );
}
window.SEKpiPopup = SEKpiPopup;

// ── Small building blocks reused across every popup ─────────────────────────
function PopStats({ items }) {
  return (
    <div className="se-pop-stats">
      {items.map((it, i) => (
        <div className="se-pop-stat" key={i}>
          <div className="se-pop-stat-lbl">{it.label}</div>
          <div className="se-pop-stat-val" style={{ color: it.color }}>{it.value}</div>
          {it.sub && <div className="se-pop-stat-sub">{it.sub}</div>}
        </div>
      ))}
    </div>
  );
}
function PopSection({ title, right, children }) {
  return <div className="se-pop-section"><div className="se-pop-section-hd"><span>{title}</span>{right}</div>{children}</div>;
}
function PopBars({ rows, C, accent, onClick }) {
  if (!rows || !rows.length) return <div style={{ fontSize: 12, color: 'var(--txt3)', padding: '14px 0' }}>No records.</div>;
  const max = Math.max(1, ...rows.map(r => r.value));
  return (
    <div style={{ display: 'flex', flexDirection: 'column', gap: 9 }}>
      {rows.map((r, i) => (
        <div key={i} onMouseDown={window.seRipple} onClick={() => onClick && onClick(r)} style={{ cursor: onClick ? 'pointer' : 'default', position: 'relative', overflow: 'hidden', borderRadius: 6 }}>
          <div style={{ display: 'flex', justifyContent: 'space-between', fontSize: 11.5, marginBottom: 3 }}>
            <span style={{ fontWeight: 600, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', maxWidth: 220 }}>{r.name}</span>
            <span style={{ fontWeight: 700, color: 'var(--txt2)' }}>{C(r.value)}</span>
          </div>
          <window.PremiumProgress pct={Math.max(2, r.value / max * 100)} color={accent} height={7} />
        </div>
      ))}
    </div>
  );
}
function RiskPill({ risk }) {
  const m = { High: { bg: '#FEE2E2', c: '#991B1B' }, Medium: { bg: '#FEF3C7', c: '#92400E' }, Low: { bg: '#DCFCE7', c: '#166534' } };
  const s = m[risk] || m.Low;
  return <span style={{ fontSize: 10, fontWeight: 800, padding: '2px 7px', borderRadius: 20, background: s.bg, color: s.c }}>{risk}</span>;
}

// ── Shared calc helpers — pure, operate on the `ctx` bundle built by the caller ──
function groupSum(records, keyFn, nameFn, amtFn) {
  const m = {};
  (records || []).forEach(r => {
    const k = keyFn(r) || '—';
    if (!m[k]) m[k] = { name: nameFn(r) || k, amount: 0, count: 0 };
    m[k].amount += amtFn(r); m[k].count++;
  });
  return Object.values(m).sort((a, b) => b.amount - a.amount);
}
function fmtPeriodLabel(key) { return key || '—'; }

// ── Dispatcher — returns the JSX body for a given popup kind ───────────────
function SERenderKpiPopup({ kind, payload, ctx }) {
  const { C, SE, accent, partyLabel, idKey, nameKey, live, scoped, table, stats, prevStats, aging, rankRows, companyId, range } = ctx;
  const plLower = partyLabel.toLowerCase();

  if (kind === 'payable') {
    const companyBreak = companyId === 'group' ? groupSum(live, r => r.companyId, r => Store.name('companies', r.companyId), r => SE.num(r.netPayable)) : null;
    const fy = SE.streamStats(scoped, { companyId, ...SE.periodRange('fy') });
    const py = SE.streamStats(scoped, { companyId, ...SE.periodRange('prevyear') });
    const q = SE.trendByGranularity(live, 'quarterly');
    return (
      <>
        <PopStats items={[
          { label: 'Current Payable', value: C(stats.totalPayable), color: accent },
          { label: `Largest ${partyLabel}`, value: table[0] ? table[0].name : '—', sub: table[0] ? C(table[0].pendingAmount) : null },
          { label: 'FY vs Previous Year', value: C(fy.totalPayable), sub: `Prev FY ${C(py.totalPayable)}` },
          { label: 'Settlement Efficiency', value: stats.efficiencyPct.toFixed(1) + '%', color: 'var(--ok)' },
        ]} />
        {companyBreak && <PopSection title="Company-wise Breakup"><window.DashBTable rows={companyBreak} cols={[{ k: 'name', h: 'Company' }, { k: 'amount', h: 'Payable', r: true, b: true, cl: accent, f: v => C(v) }, { k: 'count', h: 'Records', r: true }]} /></PopSection>}
        <PopSection title={`Top 10 Payable ${partyLabel}s`}><window.DashBTable rows={table.slice(0, 10)} cols={[{ k: 'name', h: partyLabel }, { k: 'pendingAmount', h: 'Pending', r: true, b: true, cl: '#B45309', f: v => C(v) }, { k: 'settlementPct', h: 'Settled %', r: true, f: v => v.toFixed(0) + '%' }]} /></PopSection>
        <PopSection title="Quarterly Comparison"><window.SEDualLineChart data={q} aKey="settled" bKey="pending" aLabel="Settled" bLabel="Pending" aColor="#166534" bColor="#DC2626" visible={{ a: true, b: true }} C={C} /></PopSection>
        <PopSection title="Payment Aging Distribution"><window.SEAgingBars buckets={aging} onClick={() => {}} activeId={null} C={C} /></PopSection>
      </>
    );
  }

  if (kind === 'settled') {
    const daily = SE.trendByGranularity(live, 'daily').slice(-14).map(d => ({ name: d.month.slice(5), value: d.settled }));
    const settledRows = table.filter(r => r.settled > 0).sort((a, b) => b.settled - a.settled).slice(0, 10);
    const largest = live.filter(r => SE.isFullySettled(r)).sort((a, b) => SE.num(b.netPayable) - SE.num(a.netPayable))[0];
    const avgSize = stats.settledCount > 0 ? stats.settledAmount / stats.settledCount : 0;
    return (
      <>
        <PopStats items={[
          { label: 'Total Settled', value: C(stats.settledAmount), color: 'var(--ok)' },
          { label: 'Settlement Success', value: stats.efficiencyPct.toFixed(1) + '%' },
          { label: 'Avg Settlement Size', value: C(avgSize) },
          { label: 'Largest Settlement', value: largest ? C(SE.num(largest.netPayable)) : '—', sub: largest ? (largest[nameKey] || '—') : null },
          { label: 'vs Previous Period', value: C(prevStats.settledAmount), sub: stats.settledAmount >= prevStats.settledAmount ? 'up' : 'down' },
        ]} />
        <PopSection title="Daily Settlement Trend (last 14)"><PopBars rows={daily} C={C} accent="#166534" /></PopSection>
        <PopSection title={`Top Settled ${partyLabel}s`}><window.DashBTable rows={settledRows} cols={[{ k: 'name', h: partyLabel }, { k: 'settled', h: 'Settled Bills', r: true, b: true }, { k: 'avgDays', h: 'Avg Days', r: true }, { k: 'lastSettlement', h: 'Last Settlement', f: v => v ? window.fmtDate(v) : '—' }]} /></PopSection>
      </>
    );
  }

  if (kind === 'pending') {
    const companyBreak = companyId === 'group' ? groupSum(live.filter(r => !SE.isFullySettled(r) && !SE.isCancelled(r)), r => r.companyId, r => Store.name('companies', r.companyId), r => SE.num(r.outstandingBalance != null ? r.outstandingBalance : r.netPayable)) : null;
    const critical = table.filter(r => r.overdue).sort((a, b) => b.pendingAmount - a.pendingAmount);
    return (
      <>
        <PopStats items={[
          { label: 'Pending Amount', value: C(stats.pendingAmount), color: '#B45309' },
          { label: `Pending ${partyLabel}s`, value: stats.pendingPartyCount },
          { label: 'Oldest Pending', value: stats.oldestPendingDays + 'd' },
          { label: 'Critical (Overdue)', value: critical.length, color: '#DC2626' },
        ]} />
        <div className="se-chart-grid" style={{ marginBottom: 0 }}>
          <div className="se-chart-card"><div className="se-chart-hd">{partyLabel} Distribution</div><PopBars rows={rankRows.map(r => ({ name: r.name, value: r.pendingAmount }))} C={C} accent="#B45309" /></div>
          <div className="se-chart-card"><div className="se-chart-hd">Aging Buckets</div><window.SEAgingBars buckets={aging} onClick={() => {}} activeId={null} C={C} /></div>
        </div>
        {companyBreak && <PopSection title="Pending by Company"><window.DashBTable rows={companyBreak} cols={[{ k: 'name', h: 'Company' }, { k: 'amount', h: 'Pending', r: true, b: true, cl: '#B45309', f: v => C(v) }]} /></PopSection>}
        <PopSection title="High-Risk / Overdue Accounts"><window.DashBTable rows={critical} cols={[{ k: 'name', h: partyLabel }, { k: 'pendingAmount', h: 'Pending', r: true, b: true, cl: '#DC2626', f: v => C(v) }, { k: 'avgDays', h: 'Avg Days', r: true }]} /></PopSection>
      </>
    );
  }

  if (kind === 'efficiency') {
    const q = SE.trendByGranularity(live, 'quarterly').map(d => ({ name: d.month, value: d.settled + d.pending > 0 ? d.settled / (d.settled + d.pending) * 100 : 100 }));
    const py = SE.streamStats(ctx.scoped, { companyId, ...SE.periodRange('prevyear') });
    const suggestions = [];
    if (stats.pendingPartyCount > 0) suggestions.push(`Prioritize the ${stats.pendingPartyCount} ${plLower}(s) still pending — clearing them lifts efficiency directly.`);
    if (stats.oldestPendingDays > SE.TARGET_DAYS) suggestions.push(`Oldest pending bill is ${stats.oldestPendingDays}d old — ${stats.oldestPendingDays - SE.TARGET_DAYS}d past the ${SE.TARGET_DAYS}d target cycle.`);
    if (stats.efficiencyPct < 90) suggestions.push('Efficiency is below the 90% healthy benchmark — review approval turnaround time.');
    if (!suggestions.length) suggestions.push('Efficiency is tracking well against the target settlement cycle.');
    return (
      <>
        <PopStats items={[
          { label: 'Current Efficiency', value: stats.efficiencyPct.toFixed(1) + '%', color: accent },
          { label: 'Previous Period', value: prevStats.efficiencyPct.toFixed(1) + '%' },
          { label: 'Previous Year', value: py.efficiencyPct.toFixed(1) + '%' },
          { label: 'Target Cycle', value: SE.TARGET_DAYS + ' days' },
        ]} />
        <PopSection title="Quarterly Efficiency Trend (settled-ratio proxy)"><PopBars rows={q} C={v => v.toFixed(0) + '%'} accent={accent} /></PopSection>
        <PopSection title="Improvement Suggestions">
          <ul style={{ margin: 0, paddingLeft: 18, fontSize: 12.5, color: 'var(--txt2)', lineHeight: 1.7 }}>{suggestions.map((s, i) => <li key={i}>{s}</li>)}</ul>
        </PopSection>
      </>
    );
  }

  if (kind === 'avgdays') {
    const buckets = [{ label: '0-3d', min: 0, max: 3 }, { label: '4-7d', min: 4, max: 7 }, { label: '8-14d', min: 8, max: 14 }, { label: '15d+', min: 15, max: Infinity }];
    const withDays = table.filter(r => r.avgDays > 0);
    const dist = buckets.map(b => ({ name: b.label, value: withDays.filter(r => r.avgDays >= b.min && r.avgDays <= b.max).length }));
    const fastest = [...withDays].sort((a, b) => a.avgDays - b.avgDays).slice(0, 5);
    const slowest = [...withDays].sort((a, b) => b.avgDays - a.avgDays).slice(0, 5);
    return (
      <>
        <PopStats items={[
          { label: 'Avg Settlement Days', value: stats.avgSettlementDays.toFixed(1) + 'd', color: accent },
          { label: 'Previous Period', value: prevStats.avgSettlementDays.toFixed(1) + 'd' },
          { label: 'Fastest', value: fastest[0] ? fastest[0].avgDays + 'd' : '—', sub: fastest[0] ? fastest[0].name : null },
          { label: 'Slowest', value: slowest[0] ? slowest[0].avgDays + 'd' : '—', sub: slowest[0] ? slowest[0].name : null },
        ]} />
        <PopSection title="Distribution"><PopBars rows={dist} C={v => v + ' ' + plLower + '(s)'} accent={accent} /></PopSection>
        <div className="se-chart-grid" style={{ marginBottom: 0 }}>
          <div className="se-chart-card"><div className="se-chart-hd">Fastest Settlements</div><window.DashBTable rows={fastest} cols={[{ k: 'name', h: partyLabel }, { k: 'avgDays', h: 'Avg Days', r: true, b: true, cl: 'var(--ok)' }]} /></div>
          <div className="se-chart-card"><div className="se-chart-hd">Slowest Settlements</div><window.DashBTable rows={slowest} cols={[{ k: 'name', h: partyLabel }, { k: 'avgDays', h: 'Avg Days', r: true, b: true, cl: '#DC2626' }]} /></div>
        </div>
      </>
    );
  }

  if (kind === 'oldest') {
    const rec = stats.oldestPendingRec;
    const similar = table.filter(r => r.overdue && r.id !== (rec && (rec[idKey]))).sort((a, b) => b.avgDays - a.avgDays).slice(0, 8);
    return (
      <>
        <PopStats items={[
          { label: partyLabel, value: rec ? (rec[nameKey] || '—') : '—' },
          { label: 'Age', value: stats.oldestPendingDays + 'd', color: '#DC2626' },
          { label: 'Amount', value: rec ? C(SE.num(rec.outstandingBalance != null ? rec.outstandingBalance : rec.netPayable)) : '—' },
          { label: 'Company', value: rec ? (Store.name('companies', rec.companyId) || '—') : '—' },
          { label: 'Due Date', value: rec ? window.fmtDate(SE.periodEnd(rec)) : '—' },
        ]} />
        <PopSection title="Similar Overdue Bills"><window.DashBTable rows={similar} cols={[{ k: 'name', h: partyLabel }, { k: 'pendingAmount', h: 'Pending', r: true, b: true, cl: '#DC2626', f: v => C(v) }, { k: 'avgDays', h: 'Age (d)', r: true }]} /></PopSection>
      </>
    );
  }

  if (kind === 'pendingvendors' || kind === 'partylist') {
    const rows = table.filter(r => r.pending > 0).map(r => ({ ...r, risk: r.overdue ? 'High' : (r.pendingAmount > 0 ? 'Medium' : 'Low') }));
    return (
      <PopSection title={`All Pending ${partyLabel}s (${rows.length}) — click a row to open its full history`}>
        <window.DashBTable rows={rows} cols={[
          { k: 'name', h: partyLabel, b: true },
          { k: 'pending', h: 'Bills', r: true },
          { k: 'pendingAmount', h: 'Pending Amount', r: true, b: true, cl: '#B45309', f: v => C(v) },
          { k: 'settlementPct', h: 'Settled %', r: true, f: v => v.toFixed(0) + '%' },
          { k: 'risk', h: 'Risk', f: v => <RiskPill risk={v} /> },
        ]} maxH={420} />
      </PopSection>
    );
  }

  if (kind === 'accuracy') {
    const cancelled = ctx.scoped.filter(r => SE.isCancelled(r));
    return (
      <>
        <PopStats items={[
          { label: 'Settlement Accuracy', value: stats.accuracyPct.toFixed(1) + '%', color: accent },
          { label: 'Cancelled / Corrected', value: stats.cancelledCount, color: '#DC2626' },
          { label: 'Total Records', value: ctx.scoped.length },
        ]} />
        <PopSection title="Audit Trail — Cancelled / Corrected Records"><window.DashBTable rows={cancelled.map(r => ({ name: r[nameKey] || '—', period: window.fmtDate(SE.periodEnd(r)), amount: SE.num(r.netPayable), created: r.createdDate ? window.fmtDate(r.createdDate) : '—' }))} cols={[{ k: 'name', h: partyLabel }, { k: 'period', h: 'Period' }, { k: 'amount', h: 'Amount', r: true, f: v => C(v) }, { k: 'created', h: 'Created' }]} maxH={320} /></PopSection>
      </>
    );
  }

  if (kind === 'summary') {
    const split = ctx.execSplit;
    const coBreak = companyId === 'group' ? groupSum(live.filter(r => !SE.isCancelled(r)), r => r.companyId, r => Store.name('companies', r.companyId), r => SE.num(r.netPayable)) : null;
    return (
      <>
        <PopStats items={[
          { label: 'Total Payable', value: C(stats.totalPayable), color: accent },
          { label: 'Settled', value: C(split.settled), color: 'var(--ok)' },
          { label: 'Pending', value: C(split.inflight), color: accent },
          { label: 'Overdue', value: C(split.overdue), color: '#DC2626' },
          { label: 'Efficiency', value: stats.efficiencyPct.toFixed(1) + '%' },
          { label: 'Accuracy', value: stats.accuracyPct.toFixed(1) + '%' },
        ]} />
        {coBreak && <PopSection title="Company Contribution"><window.DashBTable rows={coBreak} cols={[{ k: 'name', h: 'Company' }, { k: 'amount', h: 'Value', r: true, b: true, cl: accent, f: v => C(v) }, { k: 'count', h: 'Records', r: true }]} /></PopSection>}
        <PopSection title={`${partyLabel} Contribution (Top 10)`}><window.DashBTable rows={table.slice(0, 10)} cols={[{ k: 'name', h: partyLabel }, { k: 'pendingAmount', h: 'Pending', r: true, b: true, cl: '#B45309', f: v => C(v) }, { k: 'settlementPct', h: 'Settled %', r: true, f: v => v.toFixed(0) + '%' }]} /></PopSection>
      </>
    );
  }

  if (kind === 'aging') {
    const b = payload.bucket;
    const recs = live.filter(r => !SE.isCancelled(r) && !SE.isFullySettled(r)).filter(r => { const d = SE.daysBetween(SE.periodEnd(r), SE.todayStr()); return d >= b.min && d <= b.max; });
    const avgAge = recs.length ? Math.round(recs.reduce((s, r) => s + SE.daysBetween(SE.periodEnd(r), SE.todayStr()), 0) / recs.length) : 0;
    const risk = b.min >= 31 ? 'High' : b.min >= 8 ? 'Medium' : 'Low';
    return (
      <>
        <PopStats items={[
          { label: 'Bucket', value: b.label },
          { label: 'Bills', value: recs.length },
          { label: 'Amount', value: C(recs.reduce((s, r) => s + SE.num(r.outstandingBalance != null ? r.outstandingBalance : r.netPayable), 0)) },
          { label: 'Avg Age', value: avgAge + 'd' },
          { label: 'Risk', value: '', color: undefined },
        ]} />
        <div style={{ marginTop: -8, marginBottom: 16 }}><RiskPill risk={risk} /></div>
        <PopSection title="Bills in this Bucket"><window.DashBTable rows={recs.map(r => ({ name: r[nameKey] || '—', company: Store.name('companies', r.companyId) || '—', amount: SE.num(r.outstandingBalance != null ? r.outstandingBalance : r.netPayable), age: SE.daysBetween(SE.periodEnd(r), SE.todayStr()), due: window.fmtDate(SE.periodEnd(r)) }))} cols={[{ k: 'name', h: partyLabel }, { k: 'company', h: 'Company' }, { k: 'amount', h: 'Amount', r: true, b: true, f: v => C(v) }, { k: 'age', h: 'Age (d)', r: true }, { k: 'due', h: 'Due' }]} maxH={360} /></PopSection>
      </>
    );
  }

  if (kind === 'profile') {
    const row = payload.party;
    const recs = live.filter(r => r[idKey] === row.id).sort((a, b) => (b.createdDate || '').localeCompare(a.createdDate || ''));
    return (
      <>
        <PopStats items={[
          { label: partyLabel, value: row.name },
          { label: 'Total Bills', value: row.bills },
          { label: 'Pending', value: C(row.pendingAmount), color: row.pendingAmount > 0 ? '#B45309' : 'var(--ok)' },
          { label: 'Settlement %', value: row.settlementPct.toFixed(0) + '%' },
          { label: 'Performance Score', value: row.settlementPct.toFixed(0), color: row.settlementPct >= 80 ? 'var(--ok)' : row.settlementPct >= 50 ? '#B45309' : '#DC2626' },
        ]} />
        <PopSection title="Complete Settlement History"><window.DashBTable rows={recs.map(r => ({ period: window.fmtDate(r.periodFrom) + ' – ' + window.fmtDate(r.periodTo), net: SE.num(r.netPayable), outstanding: SE.num(r.outstandingBalance), created: r.createdDate ? window.fmtDate(r.createdDate) : '—', status: r.status || '—' }))} cols={[{ k: 'period', h: 'Period' }, { k: 'net', h: 'Net Payable', r: true, f: v => C(v) }, { k: 'outstanding', h: 'Outstanding', r: true, b: true, cl: '#B45309', f: v => C(v) }, { k: 'created', h: 'Created' }, { k: 'status', h: 'Status' }]} maxH={360} /></PopSection>
      </>
    );
  }

  if (kind === 'period') {
    const d = payload.point;
    // Records whose bucket matches this point's label — recompute via same granularity key.
    const matched = live.filter(r => { const dt = SE.recordDate(r); return dt && SE.trendByGranularity([r], ctx.grain)[0] && SE.trendByGranularity([r], ctx.grain)[0].month === d.month; });
    const vendorsInPeriod = SE.partyTable(matched, idKey, nameKey).slice(0, 10);
    return (
      <>
        <PopStats items={[
          { label: 'Period', value: d.month },
          { label: 'Settled', value: C(d.settled), color: 'var(--ok)' },
          { label: 'Pending', value: C(d.pending), color: '#B45309' },
          { label: 'Records', value: d.count },
        ]} />
        <PopSection title={`${partyLabel}s Active This Period`}><window.DashBTable rows={vendorsInPeriod} cols={[{ k: 'name', h: partyLabel }, { k: 'settled', h: 'Settled', r: true }, { k: 'pending', h: 'Pending', r: true }, { k: 'pendingAmount', h: 'Pending Amount', r: true, b: true, cl: '#B45309', f: v => C(v) }]} /></PopSection>
      </>
    );
  }

  return null;
}
window.SERenderKpiPopup = SERenderKpiPopup;

// ── Meta (title/subtitle/icon) per popup kind — dynamic, uses ctx/payload ───
window.SE_POPUP_META = function (kind, payload, ctx) {
  const pl = ctx.partyLabel;
  const map = {
    payable: { title: `Total ${pl} Payable`, subtitle: 'Company · party · trend · aging breakdown', icon: 'Wallet' },
    settled: { title: 'Settled Amount', subtitle: 'Trend · top performers · comparison', icon: 'CheckCircle2' },
    pending: { title: 'Pending Amount', subtitle: 'Distribution · aging · high-risk accounts', icon: 'Clock' },
    efficiency: { title: 'Settlement Efficiency', subtitle: 'Historical trend · benchmark · suggestions', icon: 'Gauge' },
    avgdays: { title: 'Avg Settlement Days', subtitle: 'Distribution · fastest & slowest', icon: 'CalendarClock' },
    oldest: { title: 'Oldest Pending Bill', subtitle: 'Bill detail · escalation context', icon: 'AlertTriangle' },
    pendingvendors: { title: `Pending ${pl}s`, subtitle: 'Complete list · risk indicator', icon: 'Users' },
    accuracy: { title: 'Settlement Accuracy', subtitle: 'Audit trail · corrections', icon: 'ShieldCheck' },
    summary: { title: 'Executive Summary', subtitle: 'Settled · pending · overdue · contribution', icon: 'LayoutDashboard' },
    aging: { title: 'Settlement Aging — ' + (payload.bucket ? payload.bucket.label : ''), subtitle: 'Bills in this aging bucket', icon: 'Hourglass' },
    profile: { title: payload.party ? payload.party.name : `${pl} Profile`, subtitle: `Complete ${pl.toLowerCase()} settlement profile`, icon: 'UserCircle2' },
    period: { title: payload.point ? payload.point.month : 'Period Analysis', subtitle: 'Drill into this time bucket', icon: 'CalendarRange' },
  };
  return map[kind] || { title: 'Analytics', subtitle: '', icon: 'BarChart3' };
};
