// Deal Dashboard — Overview. NEW screen, mounts alongside the legacy Overview.
//
// Scope is the LOGGED-IN USER's own module. Two grains:
//   Module    — aggregate single view of their module.
//   Sub-team  — COMPARATIVE: every sub-team of their module shown side-by-side
//               (e.g. Deal Fees: Scheftz $1.35M | Burton $770K). Text shrinks
//               with the number of sub-teams. (Individual lives on another tab.)
//
// REAL: Deal Fees, Signed LOIs, Offers, Lead Progression & Outreach boxes + deltas,
//   both charts, BHAG closed-vs-target, Top Projects (live per-project activity).
// PENDING (amber-flagged — wire after pipeline/projects Supabase tables, 7B-2):
//   Deal Fee Projection box, BHAG pipeline-EV segment.

const { useState, useEffect, useMemo } = React;

function _money(n) {
  if (n == null) return "—"; if (n === 0) return "$0";
  const a = Math.abs(n);
  if (a >= 1000000) return `$${(n / 1000000).toFixed(2)}M`;
  if (a >= 1000) return `$${(n / 1000).toFixed(0)}K`;
  return `$${Math.round(n).toLocaleString()}`;
}
function _num(n) { return (Number(n) || 0).toLocaleString(); }
function _d10(s) { return (s == null ? "" : String(s)).slice(0, 10); }
function _inRange(d, from, to) { const x = _d10(d); return x && x >= from && x <= to; }
function _mondayRange(weeks) {
  if (!weeks || !weeks.length) return null;
  const toMon = (f) => { const d = new Date(f + "T00:00:00Z"); d.setUTCDate(d.getUTCDate() - 4); return d.toISOString().slice(0, 10); };
  const m = weeks.map(toMon).sort();
  return { from: m[0], to: m[m.length - 1] };
}
function _calRange(period, year) {
  const P = (window.VAULT_FIRM.PERIODS || []).find(p => p.id === period);
  if (P && P.quarter) { const q = P.quarter; const st = ["01-01", "04-01", "07-01", "10-01"][q - 1], en = ["03-31", "06-30", "09-30", "12-31"][q - 1]; return { from: `${year}-${st}`, to: `${year}-${en}` }; }
  return { from: `${year}-01-01`, to: `${year}-12-31` };
}
const MONTHS = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
const ENTITY_COLORS = ["#196EA7", "#5AA4D2", "#0D3E5C", "#84BBDE"];
function _shortLbl(s) { return String(s || "").replace(/\s*(Sub-?team|Team)\s*$/i, "").replace(/^.*— /, "").trim() || s; }

function resolveUserScope(user) {
  const F = window.VAULT_FIRM;
  const pid = user && (user.personId || user.id);
  const all = (F.DEAL_PEOPLE || F.PEOPLE || (F.PEOPLE_BY_ID ? Object.values(F.PEOPLE_BY_ID) : []));
  let me = (pid && F.PEOPLE_BY_ID) ? F.PEOPLE_BY_ID[pid] : null;
  if (!me && pid) me = F.PEOPLE_BY_ID[pid.replace(/^p_/, "")] || null;
  if (!me) me = all.find(p => p && (
        p.id === pid || p.id === (pid || "").replace(/^p_/, "") ||
        (user && user.email && p.email && p.email.toLowerCase() === user.email.toLowerCase()) ||
        (user && user.name && p.name === user.name))) || null;
  const meId = (me && me.id) || pid || null;
  const moduleId = me ? me.subteam : null;
  const moduleLabel = ((F.SUBTEAMS.find(s => s.id === moduleId) || {}).label || "My module").replace(/^.*— /, "");
  const mySubteams = (F.SUBSUBTEAMS || []).filter(s => s.parentSubteam === moduleId)
    .map(s => ({ id: s.id, label: _shortLbl(s.label), memberIds: s.memberIds || [] }));
  return { moduleId, moduleLabel, mySubteams, meId };
}
function scopeParams(grain, id, U) {
  const none = { modules: null, subteams: null, individuals: null, scope: { subteam: [], subSubteam: [], individuals: [] } };
  if (grain === "subteam") { if (!id) return none; return { modules: null, subteams: [id], individuals: null, scope: { subteam: [], subSubteam: [id], individuals: [] } }; }
  if (!U.moduleId) return none;
  return { modules: [U.moduleId.replace(/^st-/, "")], subteams: null, individuals: null, scope: { subteam: [U.moduleId], subSubteam: [], individuals: [] } };
}

const DATASETS = {
  lead: { label: "Lead Progression", def: "confCalls", metrics: [["leads", "Leads"], ["confCalls", "Conf Calls"], ["visits", "Visits"], ["offers", "Offers"]] },
  outreach: { label: "Outreach", def: "mailings", metrics: [["ntps", "New Target Profiles"], ["mailings", "Mailings"], ["calls", "Calls"]] },
};

function DealOverviewScreen({ period, setPeriod, year, setYear, user }) {
  const B = window.VAULT_BHAG;
  const U = useMemo(() => resolveUserScope(user), [user && (user.personId || user.id)]);

  const [grain, setGrain] = useState("module");      // module | subteam
  const [dataset, setDataset] = useState("lead");
  const [metric, setMetric] = useState(DATASETS.lead.def);
  const [mode, setMode] = useState("annual");        // annual | quarterly
  const [offersVer, setOffersVer] = useState(0);
  useEffect(() => { const h = () => setOffersVer(v => v + 1); window.addEventListener("vault:offers-updated", h); return () => window.removeEventListener("vault:offers-updated", h); }, []);

  const entities = useMemo(() => {
    if (grain === "subteam" && U.mySubteams.length) {
      return U.mySubteams.map((s, i) => ({ id: s.id, label: s.label, color: ENTITY_COLORS[i % 4], params: scopeParams("subteam", s.id, U) }));
    }
    return [{ id: U.moduleId || "module", label: U.moduleLabel, color: ENTITY_COLORS[0], params: scopeParams("module", null, U) }];
  }, [grain, U]);

  const projEV = useMemo(() => entities.reduce((sum, e) =>
    sum + ((window.VaultOffersEV && B) ? window.VaultOffersEV.forScope(B.scopePersonSet(e.params.scope), year).potential : 0), 0), [entities, offersVer, year]);
  const signedByEntity = useMemo(() => Object.fromEntries(entities.map(e => [e.id, (window.VaultOffersEV && B) ? window.VaultOffersEV.forScope(B.scopePersonSet(e.params.scope), year).signed.count : 0])), [entities, offersVer, year]);

  const weeks = window.VaultPeriod.periodWeeks(period, year);
  const range = _mondayRange(weeks);
  const prevRange = _mondayRange(window.VaultPeriod.periodWeeks(period, year - 1));

  const [data, setData] = useState({});
  const [projStats, setProjStats] = useState([]);
  const [loading, setLoading] = useState(false);
  const eids = entities.map(e => e.id).join(",");

  useEffect(() => {
    if (!range) return;
    let cancel = false; setLoading(true);
    const yFrom = `${year}-01-01`, yTo = `${year}-12-31`;
    const modParams = scopeParams("module", null, U);
    const per = entities.map(e =>
      Promise.all([
        window.VaultAPI.getOverviewTotals(e.params.modules, e.params.subteams, e.params.individuals, range.from, range.to, null),
        window.VaultAPI.getOverviewTotals(e.params.modules, e.params.subteams, e.params.individuals, prevRange.from, prevRange.to, null),
        window.VaultAPI.getOverviewTimeseries(e.params.modules, e.params.subteams, e.params.individuals, yFrom, yTo, null),
      ]).then(([tc, tp, ts]) => [e.id, { tc, tp, ts }]).catch(() => [e.id, { tc: null, tp: null, ts: [] }]));
    Promise.all([
      Promise.all(per),
      window.VaultAPI.getClosedDeals(),
      window.VaultAPI.getProjectsList(range.from, range.to).catch(() => []),
    ]).then(([pairs, allDeals, projList]) => {
      if (cancel) return;
      const map = {}; pairs.forEach(([id, d]) => { map[id] = d; });
      const cal = _calRange(period, year), calPrev = _calRange(period, year - 1);
      entities.forEach(e => {
        const dmSet = B ? B.scopePersonSet(e.params.scope) : null;
        const scoped = (allDeals || []).filter(dd => B.dealInScope(dd, dmSet));
        const inYear = scoped.filter(dd => _inRange(dd.closeDate, cal.from, cal.to));
        map[e.id].feesCur = inYear.reduce((s, dd) => s + (Number(dd.fee) || 0), 0);
        map[e.id].closedCount = inYear.length;
        map[e.id].feesPrev = scoped.filter(dd => _inRange(dd.closeDate, calPrev.from, calPrev.to)).reduce((s, dd) => s + (Number(dd.fee) || 0), 0);
      });
      setData(map);

      // ---- Top Projects: real per-project metric totals (Phase 7B-2) ----
      // Pre-rank candidates cheaply by activityRows, fetch per-project activity
      // for the top 15, and sum every metric. Cached in projStats for this range,
      // so flipping the metric toggle re-ranks client-side and never refetches.
      //
      // This card is ALWAYS a single module-level card (it does not split by
      // sub-team the way the fee KPIs do), so scope = director ∈ module set.
      // The director (dm1) is the module owner (bscott for Scott), so this is
      // the authoritative owner test. It also drops cross-module bleed where one
      // of our people sits in another module's dm2/dm3 slot (e.g. ayacoel/nlittle
      // on a zdutra/bkaneko project). We deliberately do NOT match on dm2/dm3
      // here — that broad match is right for sub-team FEE credit, but wrong for a
      // module-level project list.
      const modDm = B ? B.scopePersonSet(modParams.scope) : null;
      const inScope = (projList || []).filter(p => !modDm || modDm.has(p.directorId));
      const candidates = inScope.slice().sort((a, b) => b.activityRows - a.activityRows).slice(0, 15);
      const ZERO = { leads: 0, confCalls: 0, visits: 0, offers: 0, ntps: 0, mailings: 0, calls: 0, dealFees: 0 };
      Promise.all(candidates.map(p =>
        window.VaultAPI.getProjectActivity(p.projectId, range.from, range.to)
          .then(rows => {
            const t = { ...ZERO };
            (rows || []).forEach(r => { Object.keys(ZERO).forEach(k => { t[k] += Number(r[k]) || 0; }); });
            return { projectId: p.projectId, name: p.projectName || "", ...t };
          })
          .catch(() => ({ projectId: p.projectId, name: p.projectName || "", ...ZERO }))
      )).then(stats => { if (!cancel) setProjStats(stats); });

      setLoading(false);
    }).catch(() => { if (!cancel) setLoading(false); });
    return () => { cancel = true; };
  }, [eids, range && range.from, range && range.to, year]);

  const periodLabel = ((window.VAULT_FIRM.PERIODS.find(p => p.id === period)) || {}).label || period;
  const onAccent = (cells) => cells; // identity helper for readability
  const cellsFor = (valOf, prevOf) => entities.map(e => ({
    label: e.label, color: e.color,
    value: valOf(data[e.id]), cur: valOf(data[e.id]), prev: prevOf ? prevOf(data[e.id]) : null,
  }));

  const ds = DATASETS[dataset];

  return (
    <div className="deal-ov" style={{ padding: "20px 28px 60px" }}>
      <style>{`
        .deal-ov .card{ box-shadow: var(--shadow-lg); }
        .deal-ov .seclabel{ position:relative; padding-left:11px; }
        .deal-ov .seclabel::before{ content:""; position:absolute; left:0; top:50%; transform:translateY(-50%); width:3px; height:12px; border-radius:2px; background:var(--accent); }
        .deal-ov .well{ background:var(--surface-2); border-radius:var(--radius); padding:14px; }
        @keyframes ocBarRise{ from{ transform:scaleY(0); } to{ transform:scaleY(1); } }
        @keyframes ocLineDraw{ to{ stroke-dashoffset:0; } }
        .deal-ov .oc-bar{ transform-box:fill-box; transform-origin:bottom; animation:ocBarRise .7s cubic-bezier(.22,1,.36,1) both; }
        .deal-ov .oc-line{ stroke-dasharray:1; stroke-dashoffset:1; animation:ocLineDraw .7s ease-out both; }
        .deal-ov .well .track{ background:var(--surface); }
        .deal-ov .tintcard{ background:var(--accent-soft); border-color:var(--accent-soft-2); }
        .deal-ov .snap{ font-size:9px; letter-spacing:.05em; padding:1px 6px; border-radius:4px; background:var(--accent-soft); color:var(--accent); border:1px solid var(--accent-soft-2); }
        .deal-ov .pending{ background:#FBF3E2; border:1px solid #E8D3A0 !important; }
        .deal-ov .pending .pmark{ color:#9A6B12; }
        .deal-ov .ptag{ font-size:9px; letter-spacing:.05em; padding:1px 6px; border-radius:4px; background:#F4E2BC; color:#8A5E10; border:1px solid #E4CC8F; }
        .deal-ov .gbtn{ padding:5px 12px; font-size:12.5px; border:0; background:var(--surface); color:var(--muted); cursor:pointer; }
        .deal-ov .gbtn.on{ background:var(--accent); color:var(--accent-ink); }
        .deal-ov .statbox{ cursor:pointer; transition:border-color .12s, box-shadow .12s; }
        .deal-ov .statbox.sel{ border-color:var(--accent) !important; box-shadow:0 0 0 1px var(--accent) inset; }
      `}</style>

      {/* Scope: Module (aggregate) | Sub-team (comparative) + period */}
      <div className="row" style={{ justifyContent: "space-between", flexWrap: "wrap", gap: 14, marginBottom: 18 }}>
        <div className="row" style={{ gap: 0, borderRadius: 8, overflow: "hidden", border: "1px solid var(--line)" }}>
          {[["module", U.moduleLabel], ["subteam", "Sub-team"]].map(([g, lbl]) => (
            <button key={g} className={"gbtn" + (grain === g ? " on" : "")} onClick={() => setGrain(g)}>{lbl}</button>
          ))}
        </div>
        <window.VaultPeriod.PeriodSelector value={period} onChange={setPeriod} year={year} onYearChange={setYear}/>
      </div>

      {/* Row 1 — four KPI boxes (current snapshots; no % deltas here) */}
      <div className="kpi-grid" style={{ display: "grid", gridTemplateColumns: "repeat(4, 1fr)", gap: 14, marginBottom: 16 }}>
        <div style={{ background: "var(--accent)", borderRadius: "var(--radius-lg)", padding: "16px 18px", color: "var(--accent-ink)", boxShadow: "var(--shadow-md)" }}>
          <span style={{ fontSize: 12.5, opacity: .85 }}>Current Deal Fees</span>
          <SplitCells cells={cellsFor(d => d && d.feesCur || 0)} fmt={_money} onAccent/>
        </div>
        <div className="card" style={{ padding: "16px 18px" }}>
          <div className="row" style={{ justifyContent: "space-between" }}><span className="micro">Deal Fee Projection</span><span className="snap">EST</span></div>
          <div className="display mono" style={{ fontSize: 26, lineHeight: 1.1, margin: "5px 0 4px", color: "var(--accent)" }}>{_money(projEV)}</div>
          <div className="small" style={{ fontSize: 11, color: "var(--muted)" }}>open pipeline · full value if all close</div>
        </div>
        <div className="card tintcard" style={{ padding: "16px 18px" }}>
          <div className="micro">Closed Deals</div>
          <SplitCells cells={cellsFor(d => d && d.closedCount != null ? d.closedCount : 0)} fmt={_num}/>
        </div>
        <div className="card tintcard" style={{ padding: "16px 18px" }}>
          <div className="micro">Signed LOIs</div>
          <SplitCells cells={entities.map(e => ({ label: e.label, color: e.color, value: signedByEntity[e.id] || 0 }))} fmt={_num}/>
        </div>
      </div>

      {/* Row 2 — BHAG progress (per entity) */}
      <BhagSection entities={entities} data={data} period={period} year={year} periodLabel={periodLabel}/>

      {/* Row 3 — combined Lead Progression / Outreach + separate Top Projects */}
      <div className="kpi-grid one-col" style={{ display: "grid", gridTemplateColumns: "minmax(0,3fr) minmax(0,1fr)", gap: 14, alignItems: "start" }}>
        <div className="card card-pad">
          <div className="row" style={{ justifyContent: "space-between", alignItems: "center", marginBottom: 14, flexWrap: "wrap", gap: 10 }}>
            <select value={dataset} onChange={e => { const v = e.target.value; setDataset(v); setMetric(DATASETS[v].def); }}
              style={{ fontSize: 15, fontWeight: 600, fontFamily: "var(--f-display)", padding: "6px 10px", border: "1px solid var(--line)", borderRadius: 8, color: "var(--ink)" }}>
              <option value="lead">Lead Progression</option>
              <option value="outreach">Outreach</option>
            </select>
            <div className="row" style={{ gap: 0, borderRadius: 7, overflow: "hidden", border: "1px solid var(--line)" }}>
              {[["annual", "Annual"], ["quarterly", "Quarterly"]].map(([m, l]) => (
                <button key={m} className={"gbtn" + (mode === m ? " on" : "")} style={{ padding: "4px 11px", fontSize: 11.5 }} onClick={() => setMode(m)}>{l}</button>
              ))}
            </div>
          </div>

          <div className="kpi-grid" style={{ display: "grid", gridTemplateColumns: `repeat(${ds.metrics.length}, 1fr)`, gap: 10, marginBottom: 16 }}>
            {ds.metrics.map(([k, lbl]) => (
              <div key={k} className={"well statbox" + (metric === k ? " sel" : "")} onClick={() => setMetric(k)}>
                <div className="micro" style={{ marginBottom: 2 }}>{lbl}</div>
                <SplitCells cells={cellsFor(d => d && d.tc ? (d.tc[k] || 0) : 0, d => d && d.tp ? (d.tp[k] || 0) : 0)} fmt={_num} compact/>
              </div>
            ))}
          </div>

          <div className="well"><Chart entities={entities} data={data} metric={metric} mode={mode} period={period}/></div>
          <div className="muted" style={{ fontSize: 10.5, marginTop: 8, lineHeight: 1.45 }}>
            Reflects activities logged in the CRM. A stage a deal reached but that wasn't recorded as an activity won't appear here — so these counts can trail the live pipeline.
          </div>
        </div>

        <TopProjects projects={projStats} metric={metric} metricLabel={ds.metrics.find(m => m[0] === metric)[1]} datasetLabel={ds.label}/>
      </div>
    </div>
  );
}

// ---- delta chip (stock-ticker style) ------------------------------------
function Delta({ cur, prev, onAccent, small }) {
  if (prev == null) return null;
  if (prev === 0) return null;
  const pct = ((cur - prev) / prev) * 100, up = pct >= 0;
  const col = onAccent ? (up ? "#CFE8D6" : "#F4C6C6") : (up ? "var(--ok)" : "var(--risk)");
  return <span className="mono" style={{ fontSize: small ? 10.5 : 12, fontWeight: 600, color: col }}>{up ? "↗" : "↘"} {Math.abs(pct).toFixed(1)}%</span>;
}

// ---- split value cells (1 = big; N = comparative columns) ----------------
function SplitCells({ cells, fmt, onAccent, compact }) {
  const n = cells.length;
  const tcol = onAccent ? "rgba(255,255,255,.96)" : "var(--ink)";
  if (n === 1) {
    const c = cells[0];
    return (<div style={{ marginTop: compact ? 2 : 4 }}>
      <div className="display mono" style={{ fontSize: compact ? 21 : 26, lineHeight: 1.1, color: tcol, marginBottom: 5 }}>{fmt(c.value)}</div>
      <Delta cur={c.cur} prev={c.prev} onAccent={onAccent} small={compact}/>
    </div>);
  }
  const vSize = compact ? (n <= 2 ? 16 : n === 3 ? 14 : 12) : (n <= 2 ? 18 : n === 3 ? 15 : 12.5);
  return (
    <div className="row" style={{ gap: n > 3 ? 6 : 9, alignItems: "flex-start", marginTop: 6 }}>
      {cells.map((c, i) => (
        <div key={i} style={{ flex: 1, minWidth: 0, paddingLeft: i ? (n > 3 ? 6 : 9) : 0, borderLeft: i ? "1px solid " + (onAccent ? "rgba(255,255,255,.25)" : "var(--line)") : "none" }}>
          <div style={{ fontSize: 9.5, fontWeight: 600, marginBottom: 2, color: onAccent ? "rgba(255,255,255,.82)" : c.color, whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>{c.label}</div>
          <div className="display mono" style={{ fontSize: vSize, lineHeight: 1.1, color: tcol }}>{fmt(c.value)}</div>
          <Delta cur={c.cur} prev={c.prev} onAccent={onAccent} small/>
        </div>
      ))}
    </div>
  );
}

// ---- BHAG section (per entity) ------------------------------------------
function BhagSection({ entities, data, period, year, periodLabel }) {
  const B = window.VAULT_BHAG;
  const rows = entities.map(e => {
    const key = B ? B.resolveKey(e.params.scope) : null;
    const target = (B && key && Number(year) === B.YEAR) ? B.annualTarget(key, "conservative", "fees") : null;
    return { label: e.label, color: e.color, target, fees: (data[e.id] && data[e.id].feesCur) || 0, scope: e.params.scope };
  }).filter(r => r.target != null);

  return (
    <div className="card card-pad" style={{ marginBottom: 26 }}>
      <div className="row" style={{ justifyContent: "space-between", alignItems: "center", marginBottom: rows.length ? 12 : 0 }}>
        <span className="micro seclabel">Progress to annual goal · {periodLabel}</span>
        <span className="small mono" style={{ color: "var(--accent)" }}>▨ Open pipeline (potential)</span>
      </div>
      {!rows.length && <div className="muted small" style={{ marginTop: 8 }}>No goal set for this scope. BHAG targets exist only for the Scott module and its Scheftz / Burton sub-teams.</div>}
      {rows.map((r, i) => {
        const closedPct = Math.min(1, r.target > 0 ? r.fees / r.target : 0);
        const ev = (window.VAULT_BHAG && window.VaultOffersEV) ? window.VaultOffersEV.forScope(window.VAULT_BHAG.scopePersonSet(r.scope), year).potential : 0;
        const evPct = Math.min(Math.max(0, 1 - closedPct), r.target > 0 ? ev / r.target : 0);
        const pctTxt = r.target > 0 ? Math.round((r.fees / r.target) * 100) : 0;
        return (
          <div key={i} style={{ marginTop: i ? 12 : 0 }}>
            {rows.length > 1 && <div className="row" style={{ justifyContent: "space-between", marginBottom: 3 }}>
              <span style={{ fontSize: 11.5, fontWeight: 600, color: r.color }}>{r.label}</span>
              <span className="mono small" style={{ color: "var(--muted)" }}>{_money(r.fees)} / {_money(r.target)}</span>
            </div>}
            <div style={{ display: "flex", height: 22, borderRadius: 7, background: "var(--surface-2)", overflow: "hidden" }}>
              <div style={{ width: `${closedPct * 100}%`, background: "var(--accent)" }}/>
              <div style={{ width: `${evPct * 100}%`, background: "repeating-linear-gradient(45deg,#9CC3EA,#9CC3EA 6px,#C9DFEE 6px,#C9DFEE 12px)" }}/>
            </div>
            <div className="row" style={{ justifyContent: "flex-end", marginTop: 4 }}>
              <span className="mono" style={{ fontSize: 11.5, fontWeight: 600, color: "var(--accent)" }}>{pctTxt}%{rows.length === 1 ? ` to ${_money(r.target)}` : ""}</span>
            </div>
          </div>
        );
      })}
    </div>
  );
}

// ---- chart (grouped bars, 1..4 entities) --------------------------------
function Chart({ entities, data, metric, mode, period }) {
  const activeQuarter = (window.VAULT_FIRM.PERIODS.find(p => p.id === period) || {}).quarter || (Math.floor(new Date().getMonth() / 3) + 1);
  const { labels, datasets } = useMemo(() => {
    const labels = [];
    const datasets = entities.map((e, i) => {
      const rows = (data[e.id] && data[e.id].ts) || [];
      let pts;
      if (mode === "annual") {
        const b = Array(12).fill(0);
        rows.forEach(r => { const mo = parseInt(_d10(r.week).slice(5, 7), 10) - 1; if (mo >= 0 && mo < 12) b[mo] += (Number(r[metric]) || 0); });
        pts = b; if (i === 0) MONTHS.forEach(m => labels.push(m));
      } else {
        const lo = (activeQuarter - 1) * 3, hi = lo + 2;
        const wk = rows.filter(r => { const mo = parseInt(_d10(r.week).slice(5, 7), 10) - 1; return mo >= lo && mo <= hi; })
          .sort((a, b) => _d10(a.week) < _d10(b.week) ? -1 : 1);
        pts = wk.map(r => Number(r[metric]) || 0);
        if (i === 0) wk.forEach(r => labels.push(_d10(r.week).slice(5)));
      }
      return { label: e.label, color: e.color, data: pts };
    });
    return { labels, datasets };
  }, [entities, data, metric, mode, activeQuarter]);

  if (!labels.length) return <div className="muted small" style={{ padding: "34px 0", textAlign: "center" }}>No data for this selection.</div>;
  const w = 1100, h = 248, pad = { l: 46, r: 10, t: 12, b: 26 };
  const innerW = w - pad.l - pad.r, innerH = h - pad.t - pad.b;
  const rawMax = Math.max(1, ...datasets.flatMap(d => d.data));
  const maxV = rawMax <= 6 ? Math.max(1, Math.ceil(rawMax)) : rawMax;
  const groupW = innerW / labels.length;
  const barW = Math.min(groupW * 0.74 / datasets.length, 26);
  const fy = (v) => v >= 1000000 ? (v / 1000000).toFixed(1) + "M" : v >= 1000 ? Math.round(v / 1000) + "K" : Math.round(v);
  const ext = datasets.map(d => {
    let mn = Infinity, mx = -Infinity, minI = -1, maxI = -1;
    d.data.forEach((v, i) => { if (v > 0) { if (v < mn) { mn = v; minI = i; } if (v > mx) { mx = v; maxI = i; } } });
    if (minI === maxI) minI = -1;
    return { minI, maxI };
  });
  const barFill = (di, gi, base) => gi === ext[di].maxI ? "var(--ok)" : gi === ext[di].minI ? "var(--risk)" : base;
  const barCenter = (di, gi) => pad.l + gi * groupW + groupW / 2 - (datasets.length * barW) / 2 + di * barW + barW / 2;
  const animKey = `${metric}|${mode}|${entities.map(e => e.id).join(",")}|${datasets.map(d => Math.round(d.data.reduce((a, b) => a + b, 0))).join("_")}`;
  const single = datasets.length === 1;
  const primaryTotal = single ? datasets[0].data.reduce((a, b) => a + b, 0) : 0;
  const coverage = mode === "annual" ? `${period === "ytd" ? "YTD" : "full year"} · by month` : `Q${activeQuarter} · by week`;
  const ticks = maxV <= 6 ? Array.from({ length: maxV + 1 }, (_, k) => k) : [0, maxV / 2, maxV];
  return (
    <div>
      <div className="row" style={{ justifyContent: "space-between", alignItems: "center", marginBottom: 8, flexWrap: "wrap", gap: 8 }}>
        <div className="row" style={{ gap: 14, flexWrap: "wrap" }}>
          {datasets.length > 1 && datasets.map((d, i) => <span key={i} className="row" style={{ gap: 5, alignItems: "center", fontSize: 11.5, color: "var(--ink-2)" }}><span style={{ width: 10, height: 10, borderRadius: 2, background: d.color }}/>{d.label}</span>)}
        </div>
        <span className="muted" style={{ fontSize: 11 }}>{coverage}{single ? ` · ${primaryTotal} total` : ""}</span>
      </div>
      <svg key={animKey} viewBox={`0 0 ${w} ${h}`} preserveAspectRatio="none" style={{ width: "100%", height: h, display: "block" }}>
        {ticks.map((tv, i) => { const t = tv / maxV; return (<g key={i}>
          <line x1={pad.l} x2={w - pad.r} y1={pad.t + innerH * (1 - t)} y2={pad.t + innerH * (1 - t)} stroke="var(--line-2)"/>
          <text x={pad.l - 6} y={pad.t + innerH * (1 - t) + 3} fontSize="10" fill="var(--muted)" textAnchor="end" fontFamily="var(--f-mono)">{fy(tv)}</text>
        </g>); })}
        {labels.map((lab, gi) => {
          const gx = pad.l + gi * groupW;
          return <g key={gi}>
            {datasets.map((d, di) => {
              const v = d.data[gi] || 0, bh = (v / maxV) * innerH;
              const x = gx + groupW / 2 - (datasets.length * barW) / 2 + di * barW;
              return <rect key={di} className="oc-bar" style={{ animationDelay: `${120 + gi * 100}ms` }} x={x + 1} y={pad.t + innerH - bh} width={barW - 2} height={Math.max(0, bh)} rx="2.5" fill={barFill(di, gi, d.color)}/>;
            })}
            <text x={gx + groupW / 2} y={h - 9} fontSize="9.5" fill="var(--muted)" textAnchor="middle" fontFamily="var(--f-mono)">{lab}</text>
          </g>;
        })}
        {datasets.map((d, di) => {
          const pts = d.data.map((v, gi) => ({ gi, v })).filter(p => p.v > 0).map(p => `${barCenter(di, p.gi).toFixed(1)},${(pad.t + innerH - (p.v / maxV) * innerH).toFixed(1)}`).join(" ");
          return pts ? <polyline key={"ln" + di} className="oc-line" style={{ animationDelay: `${120 + labels.length * 100}ms` }} pathLength="1" points={pts} fill="none" stroke={d.color} strokeWidth="2" strokeLinejoin="round" strokeLinecap="round" opacity=".5"/> : null;
        })}
      </svg>
    </div>
  );
}

// ---- Top Projects (separate card, Transactions-style) — live per-project totals
function TopProjects({ projects, metric, metricLabel, datasetLabel }) {
  const rows = (projects || [])
    .map(p => ({ name: p.name || "Unnamed project", v: Number(p[metric]) || 0 }))
    .filter(r => r.v > 0)
    .sort((a, b) => b.v - a.v)
    .slice(0, 5);
  return (
    <div className="card" style={{ padding: "16px 18px" }}>
      <div className="row" style={{ justifyContent: "space-between", alignItems: "center", marginBottom: 4 }}>
        <h2 className="display" style={{ margin: 0, fontSize: 17, fontWeight: 600 }}>Top Projects</h2>
      </div>
      <div className="muted small" style={{ marginBottom: 12 }}>by {metricLabel}</div>
      {rows.length === 0 ? (
        <div className="muted small" style={{ padding: "14px 0" }}>No project activity in this period.</div>
      ) : rows.map((r, i) => (
        <div key={i} className="row" style={{ alignItems: "center", gap: 11, padding: "9px 0", borderTop: i ? "1px solid var(--line-2)" : "none" }}>
          <span className="mono" style={{ width: 30, height: 30, flex: "none", borderRadius: 9, background: "var(--accent-soft)", display: "flex", alignItems: "center", justifyContent: "center", color: "var(--accent)", fontWeight: 700, fontSize: 13 }}>{i + 1}</span>
          <div style={{ flex: 1, minWidth: 0 }}>
            <div style={{ fontSize: 13, fontWeight: 600, color: "var(--ink)", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{r.name}</div>
            <div className="muted" style={{ fontSize: 11 }}>{datasetLabel}</div>
          </div>
          <span className="mono" style={{ fontSize: 13.5, fontWeight: 600, color: "var(--ink)" }}>{r.v.toLocaleString()}</span>
        </div>
      ))}
    </div>
  );
}

window.DealOverviewScreen = DealOverviewScreen;
window.VaultDealScope = { resolveUserScope, scopeParams };