// screens-development.jsx — Training → Development.
//
// Built 2026-09-03 from the Claude Design package "Vault Development Screen"
// (Development Screen.dc.html) and Development_Screen_Build.docx. The rules
// (what is visible, met, complete, ready) live in lib-dev-track.js and are
// NOT re-derived here; this file only renders the model and calls the API.
//
// Who sees what (sql_216, server-enforced):
//   analyst        their own track: Progression and Tracking
//   sub-team lead  everyone who points at them: all three tabs, Set-Up included
//   module lead    everyone on the module: same
//   admin          everything
// Nobody else. `bundle.canLead` is the server's answer and is what gates the
// Set-Up tab and every write button; the client never decides that itself.
//
// Only the Research Analyst track exists. The other four titles render as
// locked cards so the shape of the page is right on day one.
(function () {
  const { useState, useEffect, useMemo, useRef } = React;
  const TRACKS = ["Research Analyst"];
  const TITLE_CARDS = [
    { name: "Research Analyst", desc: "Three tiers: Research, Outreach, and Lead Management. 12-week tier periods.", ready: true },
    { name: "Analyst I",        desc: "Tier taxonomy defined; screen not yet built." },
    { name: "Analyst II",       desc: "To be built after Analyst I." },
    { name: "Senior Analyst",   desc: "To be built after Analyst II." },
    { name: "Management",       desc: "Will later cover Associate, Vice President, and Director." },
  ];

  const norm = (id) => (window.VaultOrg && window.VaultOrg.normalizeId) ? window.VaultOrg.normalizeId(id) : String(id || "").replace(/^p_/, "").replace(/_gmail$/, "");
  const personOf = (id) => (window.VAULT_FIRM && window.VAULT_FIRM.PEOPLE_BY_ID && window.VAULT_FIRM.PEOPLE_BY_ID[norm(id)]) || null;
  const nameOf = (id) => { const p = personOf(id); return p ? p.name : (id || ""); };
  const todayIso = () => new Date().toLocaleDateString("en-CA", { timeZone: window.VaultTz ? window.VaultTz() : "America/Los_Angeles" });
  const fmt = (iso, opts) => window.VaultDate ? window.VaultDate(iso, opts) : String(iso || "");
  const fmtLong = (iso) => fmt(iso, { month: "short", day: "numeric", year: "numeric" });
  // Authorizations and actions are stamped with the LOGIN email; resolve it to
  // a person one way (VaultAPI.personIdForLogin), falling back to the email.
  const whoOf = (email) => { const pid = window.VaultAPI && window.VaultAPI.personIdForLogin ? window.VaultAPI.personIdForLogin(email) : null; return (pid && nameOf(pid)) || String(email || ""); };
  const toast = (kind, msg) => { if (window.VaultUI && window.VaultUI.toast) window.VaultUI.toast(kind, msg); };
  const errMsg = (e) => String((e && e.message) || e || "Something went wrong.");

  // The people THIS viewer may enrol: those pointing at them as sub-team lead,
  // or on the module they lead. Admin may enrol anyone on a deal module.
  function leadableFor(meId, isAdmin) {
    const F = window.VAULT_FIRM; if (!F || !F.PEOPLE) return [];
    return F.PEOPLE.filter(p => p && p.id && p.active !== false && p.id !== meId
      && (isAdmin || p.subTeamLeadId === meId || p.team === meId));
  }
  function isLeadViewer(meId, tier) {
    if (tier === "developer" || tier === "administration") return true;
    const p = personOf(meId);
    if (!p) return false;
    if (p.team && p.team === p.id) return true;
    return !!(window.VaultOrg && window.VaultOrg.leadsASubTeam && window.VaultOrg.leadsASubTeam(p.id));
  }
  function subTeamLabel(personId) {
    const p = personOf(personId); if (!p) return "";
    const lead = p.subTeamLeadId && p.subTeamLeadId !== p.id ? personOf(p.subTeamLeadId) : null;
    const mod = p.team ? personOf(p.team) : null;
    const modName = mod ? (String(mod.name || "").split(" ").slice(-1)[0] || mod.id).toUpperCase() + " Module" : "";
    return [p.role || "", lead ? (String(lead.name || "").split(" ").slice(-1)[0] + " Team") : "", modName].filter(Boolean).join(" · ");
  }
  const STATUS = {
    met:      { label: "Met",             cls: "ok" },
    complete: { label: "Complete",        cls: "ok" },
    ok:       { label: "Within Limit",    cls: "ok" },
    over:     { label: "Over Limit",      cls: "risk" },
    missed:   { label: "Missed",          cls: "risk" },
    behind:   { label: "Behind",          cls: "risk" },
    progress: { label: "In Progress",     cls: "info" },
    open:     { label: "Open",            cls: "" },
    awaiting: { label: "Awaiting Review", cls: "gold" },
    locked:   { label: "Locked",          cls: "" },
  };
  const barFillFor = (st) => st === "met" || st === "complete" || st === "ok" ? "var(--ok)"
    : st === "missed" || st === "behind" || st === "over" ? "var(--risk)"
    : st === "awaiting" ? "var(--gold)" : "var(--accent)";

  function Pill({ cls, children }) {
    return <span className={"pill " + (cls || "")}><span className="dot"/>{children}</span>;
  }
  function Lock() {
    return (
      <svg width="13" height="13" viewBox="0 0 16 16" fill="none" aria-hidden="true">
        <rect x="3.5" y="7" width="9" height="6.5" rx="1.2" stroke="currentColor" strokeWidth="1.3"/>
        <path d="M5.5 7V5.5a2.5 2.5 0 0 1 5 0V7" stroke="currentColor" strokeWidth="1.3"/>
      </svg>
    );
  }
  // VaultRing sets a 22px numeral regardless of size, which overflows the
  // 56px tier rings and the 76px hero ring (seen 2026-09-04). This ring
  // scales its text with its size; colours are tokens only.
  function Ring({ pct, size, color, label }) {
    const S = size || 56, sw = Math.max(4, Math.round(S / 10)), r = (S - sw) / 2, C = 2 * Math.PI * r;
    const v = Math.max(0, Math.min(100, Math.round(pct || 0)));
    const fs = Math.round(S * 0.24);
    return (
      <div style={{ position: "relative", width: S, height: S, flexShrink: 0 }}>
        <svg width={S} height={S} viewBox={"0 0 " + S + " " + S} aria-hidden="true">
          <circle cx={S / 2} cy={S / 2} r={r} fill="none" stroke="var(--surface-3)" strokeWidth={sw}/>
          <circle cx={S / 2} cy={S / 2} r={r} fill="none" stroke={color || "var(--accent)"} strokeWidth={sw} strokeLinecap="round"
            strokeDasharray={C} strokeDashoffset={C * (1 - v / 100)} transform={"rotate(-90 " + S / 2 + " " + S / 2 + ")"}/>
        </svg>
        <div style={{ position: "absolute", inset: 0, display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center", lineHeight: 1 }}>
          <span className="num" style={{ fontSize: fs, fontWeight: "var(--w-medium)", color: "var(--ink)" }}>{v}<span style={{ fontSize: Math.round(fs * 0.6), color: "var(--muted)" }}>%</span></span>
          {label ? <span className="micro" style={{ marginTop: 2, fontSize: Math.max(8, Math.round(S * 0.12)) }}>{label}</span> : null}
        </div>
      </div>
    );
  }
  function Bar({ pct, fill, height }) {
    return (
      <div style={{ height: height || 5, borderRadius: 3, background: "var(--surface-3)", overflow: "hidden" }}>
        <div style={{ height: "100%", width: Math.max(0, Math.min(100, pct || 0)) + "%", background: fill || "var(--accent)", borderRadius: 3 }}/>
      </div>
    );
  }
  function Modal({ title, onClose, children, width }) {
    const drag = window.useVaultDrag ? window.useVaultDrag() : null;
    const onBackdrop = drag ? drag.guardClick(onClose) : onClose;
    return (
      <div onClick={onBackdrop} style={{ position: "fixed", inset: 0, background: "var(--scrim)", zIndex: 200, display: "grid", placeItems: "center", padding: 24 }}>
        <div ref={drag ? drag.ref : null} className="v-modal" onClick={e => e.stopPropagation()}
          style={Object.assign({ width: width || 520, maxWidth: "94vw", padding: 0, overflow: "hidden" }, drag ? drag.style : {})}>
          <div {...(drag ? drag.handleProps : {})} style={Object.assign({ display: "flex", alignItems: "center", justifyContent: "space-between", padding: "14px 18px", borderBottom: "1px solid var(--line)" }, drag ? drag.handleStyle : {})}>
            <div style={{ fontSize: "var(--t-card)", fontWeight: "var(--w-medium)", color: "var(--ink)" }}>{title}</div>
            <button className="btn ghost sm" onClick={onClose} aria-label="Close">×</button>
          </div>
          <div style={{ padding: 18 }}>{children}</div>
        </div>
      </div>
    );
  }
  const Field = ({ label, children }) => (
    <label style={{ display: "block", marginBottom: 12 }}>
      <div className="v-kicker" style={{ marginBottom: 5 }}>{label}</div>
      {children}
    </label>
  );
  const inputStyle = { width: "100%", boxSizing: "border-box" };
  const selectStyle = { width: "100%", boxSizing: "border-box", background: "var(--surface)", border: "1px solid var(--line-strong)", color: "var(--ink)", borderRadius: "var(--r-ctl)", padding: "7px 11px", fontSize: "var(--t-body-lg)" };

  // =========================================================================
  function DevelopmentScreen({ user }) {
    const T = window.VaultDevTrack;
    const API = window.VaultAPI;
    const meId = norm(user && (user.personId || user.id));
    const tier = window.VaultOrg && window.VaultOrg.viewerTier ? window.VaultOrg.viewerTier(user) : null;
    const isAdmin = tier === "developer";
    const leadViewer = isLeadViewer(meId, tier);

    const [enrollments, setEnrollments] = useState(null);
    const [bundles, setBundles] = useState({});
    const [error, setError] = useState(null);
    const [screen, setScreen] = useState("titles");   // titles | track
    const [selId, setSelId] = useState(null);
    const [tab, setTab] = useState("progression");
    const [trackTier, setTrackTier] = useState(null);
    const [weekIndex, setWeekIndex] = useState(null);
    const [grantOpen, setGrantOpen] = useState(false);
    const [authOpen, setAuthOpen] = useState(null);   // { enrollmentId, tier, kind }
    const [congrats, setCongrats] = useState(null);   // { auth, model }
    const [busy, setBusy] = useState(false);
    const today = todayIso();

    // VIEW AS (Brian, 2026-09-04): impersonation is client-side, so the server
    // returns the real login's rows. Mirror dev_can_see_person here so a lead
    // viewed-as sees only their own people. Cosmetic -- the server is the gate.
    const canSeeAsViewer = (personId) => {
      if (isAdmin || tier === "administration") return true;
      const p = personOf(personId); if (!p) return false;
      return p.id === meId || p.subTeamLeadId === meId || p.team === meId;
    };
    async function loadAll() {
      try {
        setError(null);
        const list = (await API.listDevEnrollments()).filter(e => canSeeAsViewer(e.personId));
        setEnrollments(list);
        const next = {};
        await Promise.all(list.map(async e => { try { next[e.id] = await API.getDevBundle(e.id); } catch (err) { console.error("[development] bundle", e.id, err); } }));
        setBundles(next);
      } catch (e) { setError(errMsg(e)); setEnrollments([]); }
    }
    async function reload(id) {
      try { const b = await API.getDevBundle(id); setBundles(m => Object.assign({}, m, { [id]: b })); }
      catch (e) { toast("error", errMsg(e)); }
    }
    useEffect(() => {
      loadAll();
      const on = () => loadAll();
      window.addEventListener("vault:dev-updated", on);
      return () => window.removeEventListener("vault:dev-updated", on);
    }, []);

    // Refresh when the Notification Center completes something — the loop
    // this screen exists to close.
    useEffect(() => {
      const un = window.VaultNotifications && window.VaultNotifications.subscribe
        ? window.VaultNotifications.subscribe(() => { if (selId) reload(selId); }) : null;
      return () => { if (typeof un === "function") un(); };
    }, [selId]);

    const models = useMemo(() => {
      const out = {};
      Object.keys(bundles).forEach(id => { try { out[id] = T.compute(bundles[id], today); } catch (e) { console.error("[development] compute", id, e); } });
      return out;
    }, [bundles, today]);

    // NOTICES (Brian, 2026-09-03): the Development page tells the analyst when
    // a tier opens or a chained goal unlocks. The rules are computed here, so
    // this is where the fact is first known; the server dedups (sql_219), so
    // raising from either viewer is safe. Runs once per bundle load.
    const raisedRef = useRef(new Set());
    useEffect(() => {
      Object.keys(models).forEach(async (id) => {
        const m = models[id], b = bundles[id];
        if (!m || !b || b.enrollment.status !== "active") return;
        const has = (pred) => (b.ncItems || []).some(pred);
        const todo = [];
        m.tiers.filter(t => t.status === "active").forEach(t => {
          if (!has(it => it.kind === "tier" && it.notes === "tier:" + t.n)) todo.push(["tier", t.n]);
          const tr = m.trackingFor(t.n, Math.max(1, t.weekIndex));
          tr.sections.forEach(s => s.rows.forEach(r => {
            // Rule of 24 ceiling hit: both leads get a Training action to
            // restart the count or leave it failed (sql_227). One per cycle.
            if (!r.locked && r.status === "over" && r.goal.direction === "max") todo.push(["rule24", r.count]);
            if (r.locked || r.goal.appearMode !== "after" || r.goal.tier !== t.n) return;
            if (!has(it => it.goalId === r.goal.id && (it.kind === "reminder" || it.kind === "unlock"))) todo.push(["unlock", r.goal.id]);
          }));
        });
        const fresh = todo.filter(x => !raisedRef.current.has(id + ":" + x[0] + ":" + x[1]));
        if (!fresh.length) return;
        fresh.forEach(x => raisedRef.current.add(id + ":" + x[0] + ":" + x[1]));
        try {
          for (const [kind, key] of fresh) {
            if (kind === "tier") await API.devNotifyTier(id, key);
            else if (kind === "rule24") await API.devNotifyRule24(id, key);
            else await API.devNotifyUnlock(id, key);
          }
          await reload(id);
        } catch (e) { console.error("[development] notice", e); }
      });
    }, [models]);

    const mine = (enrollments || []).find(e => norm(e.personId) === meId) || null;
    const sel = selId ? (enrollments || []).find(e => e.id === selId) : null;
    const bundle = sel ? bundles[sel.id] : null;
    const model = sel ? models[sel.id] : null;
    const canLead = !!(bundle && bundle.canLead);

    // Congratulations: the analyst's, not the lead's. Shown once per
    // authorization, remembered in localStorage.
    useEffect(() => {
      if (!mine || !bundles[mine.id] || !models[mine.id]) return;
      const seenKey = "vault:dev-congrats-seen";
      let seen = []; try { seen = JSON.parse(localStorage.getItem(seenKey) || "[]"); } catch (e) {}
      const fresh = (bundles[mine.id].authorizations || []).find(a => !seen.includes(a.id));
      if (fresh && !congrats) setCongrats({ auth: fresh, model: models[mine.id], enrollment: mine });
    }, [bundles, models, mine && mine.id]);
    const closeCongrats = () => {
      if (!congrats) return;
      try {
        const seenKey = "vault:dev-congrats-seen";
        const seen = JSON.parse(localStorage.getItem(seenKey) || "[]");
        localStorage.setItem(seenKey, JSON.stringify(seen.concat([congrats.auth.id])));
      } catch (e) {}
      setCongrats(null);
    };

    function openTrack(e, nextTab) {
      setSelId(e.id); setScreen("track"); setTab(nextTab || "progression");
      const m = models[e.id];
      if (m) { const t = m.active || m.tiers.filter(x => x.status !== "upcoming" && !x.skipped).slice(-1)[0] || m.tiers[0]; setTrackTier(t.n); setWeekIndex(Math.max(1, t.weekIndex || 1)); }
      else { setTrackTier(1); setWeekIndex(1); }
    }
    function openTier(n) {
      const t = model.tiers.find(x => x.n === n);
      if (!t || t.status === "upcoming" || t.skipped) return;
      setTrackTier(n); setWeekIndex(Math.max(1, t.lastWeekIndex || t.weekIndex || 1)); setTab("tracking");
    }

    // ---- writes ----------------------------------------------------------
    async function doAction(goal, kind, opts, okMsg) {
      if (!sel) return;
      setBusy(true);
      try { await API.devGoalAction(sel.id, goal.id, kind, opts); await reload(sel.id); if (okMsg) toast("success", okMsg); }
      catch (e) { toast("error", errMsg(e)); }
      finally { setBusy(false); }
    }
    async function sendReminder(row) {
      if (!sel) return;
      setBusy(true);
      try {
        const due = row.weekFrom ? T.addDays(row.weekFrom, 4) : today;
        await API.devSendReminder(sel.id, row.goal.id, due, null);
        await reload(sel.id);
        toast("success", "Reminder sent to " + nameOf(sel.personId) + "’s Notification Center.");
      } catch (e) { toast("error", errMsg(e)); }
      finally { setBusy(false); }
    }

    // ---- render ----------------------------------------------------------
    if (enrollments === null) return <div style={{ padding: "18px 22px" }}><window.VaultLoader/></div>;

    return (
      <div style={{ padding: "18px 22px", background: "var(--bg)", minHeight: "100%" }}>
        {error && <div className="v-card" style={{ padding: "12px 16px", marginBottom: 14, borderColor: "var(--risk)", color: "var(--risk)" }}>{error}</div>}
        {screen === "titles" ? (
          <TitlesView user={user} meId={meId} leadViewer={leadViewer} isAdmin={isAdmin} enrollments={enrollments} models={models} bundles={bundles}
            mine={mine} onOpen={openTrack} onGrant={() => setGrantOpen(true)}/>
        ) : (
          <TrackView sel={sel} bundle={bundle} model={model} canLead={canLead} tab={tab} setTab={setTab}
            trackTier={trackTier} setTrackTier={setTrackTier} weekIndex={weekIndex} setWeekIndex={setWeekIndex}
            onBack={() => setScreen("titles")} openTier={openTier} enrollments={enrollments} models={models} bundles={bundles}
            onOpenOther={openTrack} onGrant={() => setGrantOpen(true)} onAuthorize={(t, kind) => setAuthOpen({ enrollmentId: sel.id, tier: t, kind })}
            doAction={doAction} sendReminder={sendReminder} busy={busy} today={today} meId={meId}/>
        )}
        {grantOpen && (
          <GrantAccessModal meId={meId} isAdmin={isAdmin} enrollments={enrollments} onClose={() => setGrantOpen(false)}
            onDone={async () => { setGrantOpen(false); await loadAll(); }}/>
        )}
        {authOpen && sel && model && (
          <AuthorizeModal enrollment={sel} model={model} tier={authOpen.tier} kind={authOpen.kind} onClose={() => setAuthOpen(null)}
            onDone={async () => { setAuthOpen(null); await reload(sel.id); }}/>
        )}
        {congrats && <CongratsModal data={congrats} onClose={closeCongrats}/>}
      </div>
    );
  }

  // =========================================================================
  function TitlesView({ user, meId, leadViewer, isAdmin, enrollments, models, bundles, mine, onOpen, onGrant }) {
    const others = enrollments.filter(e => norm(e.personId) !== meId);
    const nAwait = others.reduce((a, e) => a + courtItems(e, models[e.id], bundles[e.id]).filter(i => i.kind === "review").length, 0);
    const nBehind = others.filter(e => courtItems(e, models[e.id], bundles[e.id]).some(i => i.kind === "behind")).length;
    const nAuth = others.filter(e => models[e.id] && models[e.id].active && models[e.id].active.ready).length;
    const hour = new Date().getHours();
    const greet = hour < 12 ? "Good Morning" : hour < 17 ? "Good Afternoon" : "Good Evening";
    const first = String((user && user.name) || nameOf(meId)).split(" ")[0];
    return (
      <div>
        <window.PageToolbar title="Development" subtitle={leadViewer ? "Team Development — Who Needs Your Review" : "Development Progression by Deal Team Title"}>
          {leadViewer && <button className="btn primary sm" onClick={onGrant}>+ Grant Access</button>}
        </window.PageToolbar>

        {leadViewer && (
          <div style={{ margin: "4px 0 18px" }}>
            <div style={{ fontSize: "var(--t-page)", fontWeight: "var(--w-medium)", letterSpacing: "-.022em", color: "var(--ink)" }}>{greet}, {first}.</div>
            <div className="row" style={{ gap: 8, marginTop: 8, flexWrap: "wrap" }}>
              <Pill cls="gold">{nAwait} Awaiting Your Review</Pill>
              <Pill cls="risk">{nBehind} Behind Goal</Pill>
              <Pill cls="info">{nAuth} Authorization Pending</Pill>
              <Pill>{others.length} People in Progress</Pill>
            </div>
            {others.length === 0 ? (
              <window.EmptyState style={{ marginTop: 14 }} title="Nobody on your team has a development track yet."
                hint="Grant access to start someone on Research Analyst development." actionLabel="Grant Access" onAction={onGrant}/>
            ) : (
              <div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(23.75rem, 1fr))", gap: 14, marginTop: 14 }}>
                {others.map(e => <PersonCard key={e.id} e={e} model={models[e.id]} bundle={bundles[e.id]} onOpen={onOpen}/>)}
              </div>
            )}
          </div>
        )}

        <div className="v-kicker" style={{ margin: "6px 0 10px" }}>{leadViewer ? "Titles" : "Your Development"}</div>
        <div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(14.375rem, 1fr))", gap: 14 }}>
          {TITLE_CARDS.map(t => {
            const enrolled = t.ready && mine && mine.track === t.name;
            const clickable = t.ready && (enrolled || (leadViewer && enrollments.some(e => e.track === t.name)));
            const onClick = () => {
              if (enrolled) return onOpen(mine);
              if (leadViewer) { const e = enrollments.find(x => x.track === t.name); if (e) onOpen(e); }
            };
            return (
              <div key={t.name} className="v-card" onClick={clickable ? onClick : undefined}
                style={{ padding: "18px 18px 16px", cursor: clickable ? "pointer" : "default", opacity: t.ready ? 1 : 0.6,
                  borderColor: t.ready ? "var(--accent-soft-2)" : "var(--line)", position: "relative" }}>
                <div className="row" style={{ justifyContent: "flex-end", marginBottom: 10, minHeight: 20 }}>
                  {t.ready ? <Pill cls="accent">{enrolled ? "Active" : "Built"}</Pill> : <span style={{ display: "inline-flex", color: "var(--muted-2)" }}><Lock/></span>}
                </div>
                <div style={{ fontSize: "var(--t-section)", fontWeight: "var(--w-medium)", color: "var(--ink)", letterSpacing: "-.01em" }}>{t.name}</div>
                <div style={{ fontSize: "var(--t-body)", color: "var(--muted)", marginTop: 4, minHeight: 36 }}>{t.desc}</div>
                <div className="micro" style={{ marginTop: 10 }}>
                  {t.ready && !enrolled && !leadViewer
                    ? "Not started — your Sub-Team Lead or Module Lead grants access"
                    : "Scope-locked — visible only to you, your Sub-Team Lead, and your Module Lead"}
                </div>
              </div>
            );
          })}
        </div>
      </div>
    );
  }

  // What is waiting on the lead for one person: reviews, behind-goal weeks,
  // and a tier ready to authorize.
  function courtItems(e, model, bundle) {
    if (!model || !bundle) return [];
    const out = [];
    const act = model.active;
    if (act) {
      const t = model.trackingFor(act.n, act.weekIndex || 1);
      t.sections.forEach(s => s.rows.forEach(r => {
        if (r.pending && r.pending.length) out.push({ kind: "review", short: r.goal.name + " — submitted, awaiting review", dot: "var(--gold)" });
        else if (r.status === "behind" || r.status === "missed" || r.status === "over") out.push({ kind: "behind", short: r.goal.name + " — " + r.count + " of " + r.target + " this week", dot: "var(--risk)" });
      }));
      if (act.ready) out.push({ kind: "auth", short: "Tier " + act.n + " → " + (act.n < 3 ? "Tier " + (act.n + 1) : "Development Review") + " authorization", dot: "var(--accent)" });
    }
    return out;
  }

  function PersonCard({ e, model, bundle, onOpen }) {
    const items = courtItems(e, model, bundle);
    const act = model && model.active;
    const pos = act ? "Tier " + act.n + " · Week " + act.weekIndex + " of 12" : (model && model.allComplete ? "Development Complete" : "Not Started");
    const pct = model ? model.overallPct : 0;
    const att = items.length ? items.length + " In Your Court" : "On Track";
    return (
      <div className="v-card" style={{ padding: 0, overflow: "hidden", borderColor: items.length ? "var(--accent-soft-2)" : "var(--line)" }}>
        <div style={{ display: "flex", alignItems: "center", gap: 12, padding: "16px 18px 12px" }}>
          <window.Avatar id={e.personId} size="xl"/>
          <div className="stretch" style={{ minWidth: 0 }}>
            <div style={{ fontSize: "var(--t-card)", fontWeight: "var(--w-medium)", color: "var(--ink)" }}>{nameOf(e.personId)}</div>
            <div className="micro" style={{ textTransform: "none", letterSpacing: 0 }}>Development — {e.track} · {pos}</div>
          </div>
          <Pill cls={items.length ? "gold" : "ok"}>{att}</Pill>
        </div>
        <div style={{ padding: "0 18px 14px" }}>
          <div className="row" style={{ gap: 8 }}>
            <div className="stretch"><Bar pct={pct} height={6}/></div>
            <span className="num small" style={{ color: "var(--ink-3)" }}>{pct}%</span>
          </div>
        </div>
        <div style={{ borderTop: "1px solid var(--line-2)", padding: "12px 18px 14px" }}>
          <div className="v-kicker" style={{ marginBottom: 8 }}>In Your Court</div>
          {items.length ? (
            <div className="col" style={{ gap: 8 }}>
              {items.slice(0, 4).map((a, i) => (
                <div className="row" key={i} style={{ gap: 10 }}>
                  <span style={{ width: 7, height: 7, borderRadius: "50%", background: a.dot, flexShrink: 0 }}/>
                  <span className="stretch" style={{ fontSize: "var(--t-body)", color: "var(--ink-2)", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{a.short}</span>
                  <button className="btn sm" onClick={() => onOpen(e, a.kind === "behind" ? "tracking" : "setup")}>Review</button>
                </div>
              ))}
            </div>
          ) : <div className="small" style={{ color: "var(--muted)" }}>Nothing waiting on you — on track.</div>}
        </div>
        <div style={{ borderTop: "1px solid var(--line-2)", padding: "12px 18px", display: "flex", gap: 8, flexWrap: "wrap" }}>
          <button className="btn primary sm" onClick={() => onOpen(e, "progression")}>Development</button>
          <button className="btn primary sm" onClick={() => onOpen(e, "tracking")}>Tracking</button>
          <button className="btn primary sm" onClick={() => onOpen(e, "setup")}>Set-Up</button>
        </div>
      </div>
    );
  }

  // =========================================================================
  function TrackView(props) {
    const { sel, bundle, model, canLead, tab, setTab, onBack } = props;
    if (!sel) return <window.EmptyState title="Nothing selected." actionLabel="Back to Titles" onAction={onBack}/>;
    if (!bundle || !model) return <div><button className="btn ghost sm" onClick={onBack}>← All Titles</button><window.VaultLoader/></div>;
    const act = model.active;
    const subtitle = act ? "Tier " + act.n + " — " + act.name + " · Week " + act.weekIndex + " of 12"
      : model.allComplete ? "Development Complete — Ready for Review" : "Not Yet Started";
    const tabs = [["progression", "Progression"], ["tracking", "Tracking"]].concat(canLead ? [["setup", "Set-Up"]] : []);
    const effTab = tab === "setup" && !canLead ? "progression" : tab;
    return (
      <div>
        <window.PageToolbar title={sel.track + " Development"} subtitle={subtitle}>
          <window.VaultSeg options={tabs} value={effTab} onChange={setTab}/>
        </window.PageToolbar>
        <div style={{ marginTop: -8, marginBottom: 12 }}>
          <button className="btn ghost sm" onClick={onBack} style={{ marginLeft: -8, color: "var(--muted)" }}>← All Titles</button>
        </div>
        {effTab === "progression" && <ProgressionTab {...props}/>}
        {effTab === "tracking" && <TrackingTab {...props}/>}
        {effTab === "setup" && canLead && <SetupTab {...props}/>}
      </div>
    );
  }

  // ---- Progression ---------------------------------------------------------
  function ProgressionTab({ sel, model, openTier }) {
    const act = model.active;
    const heroPill = act ? (act.ready ? { cls: "gold", text: "Tier " + act.n + " — Ready to Authorize" }
      : act.n === 3 && act.weekIndex >= 10 ? { cls: "gold", text: "Development Review — " + Math.max(0, 12 - act.weekIndex) + " Weeks Out" }
      : { cls: "accent", text: "Tier " + act.n + " · Week " + act.weekIndex + " of 12" })
      : model.allComplete ? { cls: "ok", text: "Development Complete" } : { cls: "", text: "Not Started" };
    return (
      <div>
        <div className="v-card" style={{ padding: "16px 20px", marginBottom: 14, display: "flex", alignItems: "center", gap: 24, flexWrap: "wrap" }}>
          <div className="row" style={{ gap: 12, minWidth: 220 }}>
            <window.Avatar id={sel.personId} size="xl"/>
            <div>
              <div style={{ fontSize: "var(--t-card)", fontWeight: "var(--w-medium)", color: "var(--ink)" }}>{nameOf(sel.personId)}</div>
              <div style={{ fontSize: "var(--t-small)", color: "var(--muted)" }}>{subTeamLabel(sel.personId)}</div>
              <div className="micro" style={{ marginTop: 2 }}>Development Started {fmtLong(sel.startedOn)}</div>
            </div>
          </div>
          <div className="stretch" style={{ minWidth: 260 }}>
            <div className="row" style={{ justifyContent: "space-between", marginBottom: 6 }}>
              <span className="v-kicker">Development Period · 36 Weeks</span>
              <span className="micro">Week {Math.min(model.weeksElapsed, 36)} of 36</span>
            </div>
            <div className="row" style={{ gap: 4 }}>
              {model.tiers.map(t => (
                <div key={t.n} style={{ flex: 1 }}>
                  <Bar height={8} fill={t.status === "complete" ? "var(--ok)" : "var(--accent)"}
                    pct={t.status === "complete" ? 100 : t.status === "active" ? Math.min(100, Math.round(100 * t.weekIndex / 12)) : 0}/>
                  <div className="micro" style={{ marginTop: 4 }}>Tier {t.n} · {t.name.replace("Management", "Mgmt")}</div>
                </div>
              ))}
            </div>
          </div>
          <div className="row" style={{ gap: 16 }}>
            <Ring pct={model.overallPct} size={76} label="Overall" color={model.overallPct >= 100 ? "var(--ok)" : "var(--accent)"}/>
            <Pill cls={heroPill.cls}>{heroPill.text}</Pill>
          </div>
        </div>
        <div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(17.5rem, 1fr))", gap: 14 }}>
          {model.tiers.map(t => {
            const st = t.status === "complete" ? { label: t.skipped ? "Prior Experience" : "Complete", cls: "ok" } : t.ready ? { label: "Ready to Authorize", cls: "gold" } : t.status === "active" ? { label: "Active", cls: "accent" } : { label: "Upcoming", cls: "" };
            const sub = t.status === "complete" ? (t.skipped ? "Prior experience · credited by " + whoOf(t.auth.authorizedBy) + " at start" : "Authorized by " + whoOf(t.auth.authorizedBy) + " · " + fmtLong(String(t.auth.authorizedAt).slice(0, 10)))
              : t.status === "active" ? fmt(t.startsOn) + " – " + fmtLong(t.endsOn) + " · Week " + t.weekIndex + " of 12"
              : t.n === 1 ? "Opens " + fmtLong(t.startsOn) : "Opens on Tier " + (t.n - 1) + " authorization";
            const clickable = t.status !== "upcoming" && !t.skipped;
            return (
              <div key={t.n} className="v-card" onClick={clickable ? () => openTier(t.n) : undefined} title={clickable ? "Open in Tracking" : undefined}
                style={{ padding: "16px 18px", cursor: clickable ? "pointer" : "default", borderColor: t.status === "active" ? (t.ready ? "var(--gold-line)" : "var(--accent-soft-2)") : "var(--line)" }}>
                <div className="row" style={{ justifyContent: "space-between", marginBottom: 2 }}>
                  <span style={{ fontSize: "var(--t-section)", fontWeight: "var(--w-medium)", color: "var(--ink)", letterSpacing: "-.01em" }}>Tier {t.n}</span>
                  <div className="row" style={{ gap: 9 }}>
                    <Ring pct={t.status === "complete" ? 100 : t.pct} size={56} color={t.status === "complete" || t.pct >= 100 ? "var(--ok)" : "var(--accent)"}/>
                    <Pill cls={st.cls}>{st.label}</Pill>
                  </div>
                </div>
                <div style={{ fontSize: "var(--t-card)", fontWeight: "var(--w-medium)", color: "var(--ink)" }}>{t.name}</div>
                <div className="micro" style={{ marginBottom: 12 }}>{sub}</div>
                <div className="col" style={{ gap: 10 }}>
                  {t.sections.map(s => (
                    <div key={s.name}>
                      <div className="row" style={{ justifyContent: "space-between", marginBottom: 4 }}>
                        <span style={{ fontSize: "var(--t-body)", color: "var(--ink-2)" }}>{s.name}</span>
                        <span className="num small" style={{ color: "var(--ink-3)" }}>{s.done} of {s.total} goals</span>
                      </div>
                      <Bar pct={s.total ? 100 * s.done / s.total : 0} fill={s.total && s.done >= s.total ? "var(--ok)" : "var(--accent)"}/>
                    </div>
                  ))}
                </div>
              </div>
            );
          })}
        </div>
        <div className="micro" style={{ marginTop: 12 }}>Tiers populate when all needed statistics are met, or on override by the Module Lead or Sub-Team Lead. New actions appear when their time threshold is reached or a prior action completes.</div>
      </div>
    );
  }

  // ---- Tracking ------------------------------------------------------------
  function TrackingTab({ sel, model, canLead, trackTier, setTrackTier, weekIndex, setWeekIndex, doAction, sendReminder, busy, onAuthorize, bundle, today }) {
    const started = model.tiers.filter(t => t.status !== "upcoming" && !t.skipped);
    const frame = model.tiers.find(t => t.n === trackTier) || started[0];
    if (!frame || frame.status === "upcoming") return <window.EmptyState title="Development has not started yet." hint={"Tier 1 opens " + fmtLong(sel.startedOn) + "."}/>;
    const maxWeek = Math.max(1, frame.status === "complete" ? frame.lastWeekIndex : frame.weekIndex);
    const w = Math.max(1, Math.min(weekIndex || 1, maxWeek));
    const t = model.trackingFor(frame.n, w);
    const rows = t.sections.flatMap(s => s.rows);
    const n = (pred) => rows.filter(pred).length;
    const nMet = n(r => r.status === "met" || r.status === "complete" || r.status === "ok");
    const nProg = n(r => r.status === "progress" || r.status === "open");
    const nBehind = n(r => r.status === "behind" || r.status === "missed" || r.status === "over");
    const nWait = n(r => r.status === "awaiting");
    const isCurrent = frame.status === "active" && w === frame.weekIndex;
    const remindedThisWeek = (goalId) => (bundle.ncItems || []).some(it => (!it.kind || it.kind === "reminder") && it.goalId === goalId && !it.done && it.dueDate && it.dueDate >= t.weekFrom && it.dueDate <= t.weekTo);
    return (
      <div>
        <div className="v-card" style={{ padding: "14px 18px", marginBottom: 14, display: "flex", alignItems: "center", gap: 16, flexWrap: "wrap" }}>
          <window.VaultSeg options={started.map(x => [x.n, "Tier " + x.n])} value={frame.n}
            onChange={(v) => { const f = model.tiers.find(x => x.n === v); setTrackTier(v); setWeekIndex(Math.max(1, f.status === "complete" ? f.lastWeekIndex : f.weekIndex)); }}/>
          <div className="row" style={{ gap: 6 }}>
            <button className="btn sm" onClick={() => setWeekIndex(Math.max(1, w - 1))} disabled={w <= 1} title="Previous week">
              <svg width="10" height="10" viewBox="0 0 10 10" fill="none"><path d="M6.5 2L3.5 5L6.5 8" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round"/></svg>
            </button>
            <button className="btn sm" onClick={() => setWeekIndex(Math.min(maxWeek, w + 1))} disabled={w >= maxWeek} title="Next week">
              <svg width="10" height="10" viewBox="0 0 10 10" fill="none"><path d="M3.5 2L6.5 5L3.5 8" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round"/></svg>
            </button>
            {w < maxWeek && <button className="btn ghost sm" onClick={() => setWeekIndex(maxWeek)} style={{ color: "var(--accent)" }}>Current Week</button>}
          </div>
          <div style={{ minWidth: 0 }}>
            <div className="v-kicker">Tier {frame.n} · Week {w} of 12</div>
            <div style={{ fontSize: "var(--t-card)", fontWeight: "var(--w-bold)", color: "var(--ink)", marginTop: 2 }}>{fmt(t.weekFrom)} – {fmt(t.weekTo)}, {String(t.weekTo).slice(0, 4)}</div>
          </div>
          <div className="stretch"/>
          <div className="row" style={{ gap: 8, flexWrap: "wrap" }}>
            <Pill cls="ok">{nMet} Met</Pill>
            <Pill cls="info">{nProg} In Progress</Pill>
            <Pill cls="risk">{nBehind} Behind</Pill>
            <Pill cls="gold">{nWait} Awaiting Review</Pill>
          </div>
        </div>

        {frame.ready && (
          <div className="v-card" style={{ display: "flex", alignItems: "center", gap: 14, padding: "14px 18px", background: "var(--ok-soft)", borderColor: "var(--ok-line)", flexWrap: "wrap", marginBottom: 14 }}>
            <Pill cls="ok">All Statistics Met</Pill>
            <span className="stretch" style={{ fontSize: "var(--t-body)", color: "var(--ink-2)", minWidth: 220 }}>
              Tier {frame.n} is complete. {frame.n < 3 ? "Authorize to open Tier " + (frame.n + 1) + "." : "Authorize to close development and open the promotion discussion."}
            </span>
            {canLead && <button className="btn primary sm" onClick={() => onAuthorize(frame.n, "met")}>Authorize Tier {frame.n} Completion</button>}
          </div>
        )}

        <div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(22.5rem, 1fr))", gap: 14, alignItems: "start", marginBottom: 14 }}>
          {t.sections.map(sec => (
            <div key={sec.name} className="v-card" style={{ overflow: "hidden" }}>
              <div style={{ padding: "12px 16px 10px", borderBottom: "1px solid var(--line)" }}>
                <span className="v-kicker">{sec.name}</span>
                <span className="micro" style={{ marginLeft: 10, textTransform: "none", letterSpacing: 0 }}>{sec.rows.length} goals this tier</span>
              </div>
              {sec.rows.length === 0 && <div className="v-empty" style={{ padding: "14px 16px" }}>Nothing in this section for Tier {frame.n}.</div>}
              {sec.rows.map(r => {
                const st = STATUS[r.status] || STATUS.open;
                const g = r.goal;
                if (r.locked) {
                  return (
                    <div key={g.id} style={{ display: "grid", gridTemplateColumns: "minmax(0,1fr) 4.5rem 8.5rem", gap: 12, alignItems: "center", padding: "10px 16px", borderBottom: "1px solid var(--line-2)", opacity: 0.6 }}>
                      <div style={{ minWidth: 0 }}>
                        <div style={{ fontSize: "var(--t-body-lg)", color: "var(--ink-3)" }}>{canLead ? g.name : "Locked Goal"}</div>
                        <div className="micro" style={{ textTransform: "none", letterSpacing: 0 }}>{r.lockNote}</div>
                      </div>
                      <span className="num" style={{ fontSize: 13, color: "var(--muted-2)", textAlign: "right" }}>{canLead ? "— / " + r.target : "—"}</span>
                      <div style={{ textAlign: "right" }}>
                        <span style={{ display: "inline-flex", alignItems: "center", justifyContent: "center", width: 26, height: 26, borderRadius: 7, background: "var(--surface-2)", border: "1px solid var(--line)", color: "var(--muted-2)" }}><Lock/></span>
                        {canLead && <div><button className="btn ghost sm" disabled={busy} onClick={() => doAction(g, "unlock", null, g.name + " unlocked.")} style={{ color: "var(--accent)", padding: "2px 6px", marginTop: 2 }}>Unlock</button></div>}
                      </div>
                    </div>
                  );
                }
                const modeLabel = g.countMode === "weekly" ? (g.spanWeeks ? "per week · " + (r.metWeeks || 0) + " of " + g.spanWeeks + " weeks" : "per week") : (g.direction === "max" ? "ceiling · total" + (r.restart ? " · restarted " + fmt(r.restart.weekFrom) : "") : "total");
                const canOverride = canLead && !(r.status === "met" || r.status === "complete" || r.status === "ok") && (g.countMode === "total" || w <= maxWeek);
                return (
                  <div key={g.id} style={{ display: "grid", gridTemplateColumns: "minmax(0,1fr) 4.5rem 8.5rem", gap: 12, alignItems: "center", padding: "10px 16px", borderBottom: "1px solid var(--line-2)" }}>
                    <div style={{ minWidth: 0 }}>
                      <div style={{ fontSize: "var(--t-body-lg)", color: "var(--ink)" }}>{g.name}{r.override ? <span className="micro" style={{ marginLeft: 8, color: "var(--gold-ink)", textTransform: "none", letterSpacing: 0 }}>override</span> : null}</div>
                      <div className="micro" style={{ textTransform: "none", letterSpacing: 0, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{g.screenLabel} · {modeLabel}</div>
                      <div style={{ marginTop: 5 }}><Bar pct={r.target ? 100 * r.count / r.target : 0} fill={barFillFor(r.status)} height={4}/></div>
                    </div>
                    <span className="num" style={{ fontSize: 13, color: barFillFor(r.status), textAlign: "right" }}>{r.count} / {r.target}</span>
                    <div style={{ textAlign: "right" }}>
                      <Pill cls={st.cls}>{st.label}</Pill>
                      {canLead && r.pending && r.pending.length > 0 && (
                        <div className="row" style={{ justifyContent: "flex-end", gap: 4, marginTop: 4 }}>
                          <button className="btn primary sm" disabled={busy} onClick={() => doAction(g, "approve", { userActionId: r.pending[0].id }, g.name + " approved.")}>Approve</button>
                          <button className="btn sm" disabled={busy} onClick={() => doAction(g, "send_back", { userActionId: r.pending[0].id }, g.name + " sent back.")}>Send Back</button>
                        </div>
                      )}
                      {canLead && g.direction === "max" && r.count > 0 && (
                        <div><button className="btn ghost sm" disabled={busy} onClick={() => doAction(g, "restart", { weekFrom: today }, "Count restarted for " + g.name + ".")} style={{ color: "var(--accent)", padding: "2px 6px", marginTop: 2 }}>Restart Count</button></div>
                      )}
                      {canLead && g.source === "manual" && (
                        <div><button className="btn ghost sm" disabled={busy} onClick={() => doAction(g, "approve", { weekFrom: t.weekFrom }, "Logged one for " + g.name + ".")} style={{ color: "var(--accent)", padding: "2px 6px", marginTop: 2 }}>Log One</button></div>
                      )}
                      {canLead && g.source === "nc" && (r.pending || []).length === 0 && (
                        <div><button className="btn ghost sm" disabled={busy || remindedThisWeek(g.id)} onClick={() => sendReminder(r)} style={{ color: "var(--accent)", padding: "2px 6px", marginTop: 2 }}>{remindedThisWeek(g.id) ? "Reminder Sent ✓" : "Send Reminder"}</button></div>
                      )}
                      {canOverride && (
                        <div><button className="btn ghost sm" disabled={busy} onClick={() => doAction(g, "override_met", { weekFrom: g.countMode === "weekly" ? t.weekFrom : null }, g.name + " marked met.")} style={{ color: "var(--gold-ink)", padding: "2px 6px", marginTop: 2 }}>Mark Met (Override)</button></div>
                      )}
                    </div>
                  </div>
                );
              })}
            </div>
          ))}
        </div>
        <div className="micro" style={{ marginBottom: 4 }}>
          {canLead
            ? "Only the active week is visible to the analyst. Send Reminder puts the goal in their Notification Center; completing it there counts here. Goals marked as tests or presentations wait for your approval."
            : "Only the active week is visible. Reminders from your Sub-Team Lead arrive in your Notification Center; completing them there counts here."}
        </div>
        {!isCurrent && frame.status === "active" && <div className="micro">Viewing a past week. Weekly goals there are final.</div>}
      </div>
    );
  }

  // ---- Set-Up --------------------------------------------------------------
  function SetupTab({ sel, model, bundle, enrollments, models, bundles, onOpenOther, onGrant, onAuthorize, doAction, busy, setTab, today }) {
    const COLS = "minmax(0,1.2fr) 1fr 5.5rem 4rem 4rem 4rem 1fr 6rem";
    const act = model.active;
    const items = courtItems(sel, model, bundle);
    const reviewRows = [];
    if (act) {
      const t = model.trackingFor(act.n, act.weekIndex || 1);
      t.sections.forEach(s => s.rows.forEach(r => {
        if (r.pending && r.pending.length) reviewRows.push({ r, kind: "review" });
        else if (r.status === "behind" || r.status === "missed" || r.status === "over") reviewRows.push({ r, kind: "behind", weekFrom: t.weekFrom });
      }));
    }
    const authRows = model.tiers.map(t => ({
      title: "Tier " + t.n + " — " + t.name + (t.n < 3 ? " → Tier " + (t.n + 1) : " → Development Review"),
      sub: t.status === "complete"
        ? (t.skipped ? "Credited as prior experience by " + whoOf(t.auth.authorizedBy) + " · " + fmtLong(String(t.auth.authorizedAt).slice(0, 10)) : "Authorized by " + whoOf(t.auth.authorizedBy) + " · " + fmtLong(String(t.auth.authorizedAt).slice(0, 10)) + (t.auth.kind === "override" ? " · Override" : " · All statistics met")) + (t.auth.note && !t.skipped ? " · " + t.auth.note : "")
        : t.status === "active" ? (t.ready ? "All " + t.total + " statistics met · ready to authorize" : t.remaining + " of " + t.total + " goals remaining · Week " + t.weekIndex + " of 12")
        : "Opens on Tier " + (t.n - 1) + " authorization",
      state: t.status === "complete" ? (t.skipped ? "Prior Experience" : "Authorized") : t.status === "active" ? (t.ready ? "Ready" : t.remaining + " Goals Remaining") : "Upcoming",
      cls: t.status === "complete" ? "ok" : t.ready ? "gold" : t.status === "active" ? "info" : "",
      t,
    }));
    return (
      <div>
        <div className="v-card" style={{ marginBottom: 14, overflow: "hidden" }}>
          <div style={{ padding: "12px 18px 10px", borderBottom: "1px solid var(--line)", display: "flex", alignItems: "center", justifyContent: "space-between", gap: 10, flexWrap: "wrap" }}>
            <div>
              <span className="v-kicker">Access</span>
              <span className="micro" style={{ marginLeft: 10, textTransform: "none", letterSpacing: 0 }}>People you lead with a Development track · visible only to the Module Lead, Sub-Team Lead, and the analyst</span>
            </div>
            <button className="btn primary sm" onClick={onGrant}>+ Grant Access</button>
          </div>
          <div style={{ overflowX: "auto" }}>
            <div style={{ minWidth: "52rem" }}>
              <div className="v-thead" style={{ display: "grid", gridTemplateColumns: COLS, gap: 14, padding: "9px 18px" }}>
                <span>User</span><span>Track</span><span>Start</span><span>Tier 1</span><span>Tier 2</span><span>Tier 3</span><span>Current Tier</span><span>Status</span>
              </div>
              {enrollments.map(e => {
                const m = models[e.id];
                const cell = (n) => {
                  if (!m) return { v: "—", c: "var(--muted-2)" };
                  const t = m.tiers[n - 1];
                  return t.status === "complete" ? { v: "✓", c: "var(--ok)" } : t.status === "active" ? { v: t.pct + "%", c: "var(--accent)" } : { v: "—", c: "var(--muted-2)" };
                };
                const pos = m && m.active ? "Tier " + m.active.n + " · Week " + m.active.weekIndex + " of 12" : m && m.allComplete ? "Complete" : "Not Started";
                const stMap = { active: ["Active", "accent"], complete: ["Complete", "ok"], paused: ["Paused", "gold"], withdrawn: ["Withdrawn", ""] };
                const st = stMap[e.status] || [e.status, ""];
                const isSel = e.id === sel.id;
                return (
                  <div key={e.id} className="v-trow" onClick={() => isSel ? setTab("progression") : onOpenOther(e, "setup")} title={isSel ? "Open Progression" : "Open " + nameOf(e.personId)}
                    style={{ display: "grid", gridTemplateColumns: COLS, gap: 14, alignItems: "center", padding: "11px 18px", cursor: "pointer", background: isSel ? "var(--surface-2)" : undefined }}>
                    <div className="row" style={{ gap: 9 }}><window.Avatar id={e.personId}/><span style={{ color: "var(--ink)", fontSize: "var(--t-body-lg)" }}>{nameOf(e.personId)}</span></div>
                    <span>{e.track}</span>
                    <span className="num">{fmtLong(e.startedOn)}</span>
                    {[1, 2, 3].map(n => { const c = cell(n); return <span key={n} className="num" style={{ color: c.c }}>{c.v}</span>; })}
                    <span>{pos}</span>
                    <span><Pill cls={st[1]}>{st[0]}</Pill></span>
                  </div>
                );
              })}
            </div>
          </div>
          <div className="row" style={{ padding: "10px 18px", gap: 10, flexWrap: "wrap" }}>
            <span className="micro" style={{ textTransform: "none", letterSpacing: 0 }}>Start date (Monday, moves every week with it):</span>
            <input type="date" value={sel.startedOn} onChange={e => e.target.value && changeStart(sel, e.target.value)} style={{ padding: "3px 8px" }}/>
          </div>
          <div className="micro" style={{ padding: "0 18px 10px", textTransform: "none", letterSpacing: 0 }}>
            {sel.status === "active"
              ? <span>Pause or withdraw {nameOf(sel.personId)}: <button className="btn ghost sm" disabled={busy} onClick={() => changeStatus(sel, "paused")} style={{ padding: "1px 6px" }}>Pause</button> <button className="btn ghost sm" disabled={busy} onClick={() => changeStatus(sel, "withdrawn")} style={{ padding: "1px 6px", color: "var(--risk)" }}>Withdraw</button></span>
              : <span>{nameOf(sel.personId)} is {sel.status}. <button className="btn ghost sm" disabled={busy} onClick={() => changeStatus(sel, "active")} style={{ padding: "1px 6px", color: "var(--accent)" }}>Reactivate</button></span>}
          </div>
        </div>

        <div className="v-card" style={{ overflow: "hidden" }}>
          <div style={{ padding: "12px 18px 10px", borderBottom: "1px solid var(--line)" }}>
            <span className="v-kicker">Approvals &amp; Overrides</span>
            <span className="micro" style={{ marginLeft: 10, textTransform: "none", letterSpacing: 0 }}>Line-item goal approvals and tier progression authorization for {nameOf(sel.personId)}</span>
          </div>
          {reviewRows.length === 0 && (!act || !act.ready) && <div className="v-empty" style={{ padding: "14px 18px" }}>Nothing waiting on you this week.</div>}
          {reviewRows.map(({ r, kind, weekFrom }) => (
            <div key={r.goal.id} style={{ display: "flex", alignItems: "center", gap: 14, padding: "13px 18px", borderBottom: "1px solid var(--line-2)", flexWrap: "wrap" }}>
              <div className="stretch" style={{ minWidth: 220 }}>
                <div style={{ fontSize: "var(--t-body-lg)", color: "var(--ink)" }}>{r.goal.name} — {nameOf(sel.personId)}</div>
                <div className="micro" style={{ textTransform: "none", letterSpacing: 0 }}>
                  {kind === "review" ? "Submitted " + fmtLong(r.pending[0].doneDate) + (r.pending[0].required ? " · " + (r.pending[0].action || "") + " · " + r.pending[0].approvals + " of " + r.pending[0].required + " lead approvals" : " · managed in Notification Center") : r.count + " of " + r.target + " this week · counts toward Tier " + r.tier + " " + (r.goal.countMode === "weekly" ? "weekly" : "total") + " goals"}
                </div>
              </div>
              <Pill cls={kind === "review" ? "gold" : "risk"}>{kind === "review" ? "Awaiting Review" : STATUS[r.status].label}</Pill>
              <div className="row" style={{ gap: 7 }}>
                {kind === "review" ? (
                  <React.Fragment>
                    <button className="btn primary sm" disabled={busy} onClick={() => doAction(r.goal, "approve", { userActionId: r.pending[0].id }, r.goal.name + " approved.")}>Approve</button>
                    <button className="btn sm" disabled={busy} onClick={() => doAction(r.goal, "send_back", { userActionId: r.pending[0].id }, r.goal.name + " sent back.")}>Send Back</button>
                  </React.Fragment>
                ) : (
                  r.goal.direction === "max"
                    ? <button className="btn primary sm" disabled={busy} onClick={() => doAction(r.goal, "restart", { weekFrom: today }, "Count restarted for " + r.goal.name + ".")}>Restart Count</button>
                    : <button className="btn primary sm" disabled={busy} onClick={() => doAction(r.goal, "override_met", { weekFrom: r.goal.countMode === "weekly" ? weekFrom : null }, r.goal.name + " marked met.")}>Goal Met (Override)</button>
                )}
              </div>
            </div>
          ))}
          {act && (
            <div style={{ display: "flex", alignItems: "center", gap: 14, padding: "13px 18px", borderBottom: "1px solid var(--line-2)", flexWrap: "wrap" }}>
              <div className="stretch" style={{ minWidth: 220 }}>
                <div style={{ fontSize: "var(--t-body-lg)", color: "var(--ink)" }}>Tier {act.n} Completion → {act.n < 3 ? "Tier " + (act.n + 1) : "Development Review"}</div>
                <div className="micro" style={{ textTransform: "none", letterSpacing: 0 }}>{act.ready ? "All " + act.total + " statistics met · ready to authorize" : act.done + " of " + act.total + " Tier " + act.n + " goals met · authorization unlocks when all statistics are met, or on override"}</div>
              </div>
              <Pill cls={act.ready ? "gold" : "info"}>{act.ready ? "Ready" : act.remaining + " Goals Remaining"}</Pill>
              <div className="row" style={{ gap: 7 }}>
                <button className="btn primary sm" disabled={busy || !act.ready} onClick={() => onAuthorize(act.n, "met")}>Authorize</button>
                <button className="btn sm" disabled={busy} onClick={() => onAuthorize(act.n, "override")}>Override</button>
              </div>
            </div>
          )}
          <div className="micro" style={{ padding: "11px 18px", textTransform: "none", letterSpacing: 0 }}>Authorization is spurred two ways: all needed statistics for the tier are met, or an override by the Module Lead or Sub-Team Lead.</div>
        </div>

        <div className="v-card" style={{ marginTop: 14, overflow: "hidden" }}>
          <div style={{ padding: "12px 18px 10px", borderBottom: "1px solid var(--line)" }}><span className="v-kicker">Tier Authorization Log</span></div>
          {authRows.map(a => (
            <div key={a.title} style={{ display: "flex", alignItems: "center", gap: 14, padding: "12px 18px", borderBottom: "1px solid var(--line-2)", flexWrap: "wrap" }}>
              <div className="stretch" style={{ minWidth: 200 }}>
                <div style={{ fontSize: "var(--t-body-lg)", color: "var(--ink)" }}>{a.title}</div>
                <div className="micro" style={{ textTransform: "none", letterSpacing: 0 }}>{a.sub}</div>
              </div>
              <Pill cls={a.cls}>{a.state}</Pill>
            </div>
          ))}
          {(bundle.actions || []).length > 0 && (
            <div style={{ padding: "10px 18px" }}>
              <div className="v-kicker" style={{ marginBottom: 6 }}>Line-Item Actions</div>
              {(bundle.actions || []).slice().reverse().slice(0, 12).map(a => {
                const g = (bundle.goals || []).find(x => x.id === a.goalId);
                const kindLabel = { unlock: "Unlocked", override_met: "Marked Met (Override)", approve: "Approved", send_back: "Sent Back", restart: "Count Restarted" }[a.kind] || a.kind;
                return <div key={a.id} className="small" style={{ color: "var(--ink-2)", padding: "3px 0" }}>{kindLabel} · {g ? g.name : a.goalId}{a.weekFrom ? " · week of " + fmt(a.weekFrom) : ""} · {whoOf(a.actedBy)} · {fmtLong(String(a.actedAt).slice(0, 10))}</div>;
              })}
            </div>
          )}
        </div>
      </div>
    );
    async function changeStart(e, iso) {
      if (iso === e.startedOn) return;
      const T = window.VaultDevTrack;
      if (T.weekMonday(iso) !== iso) { toast("info", "Pick a Monday."); return; }
      try { await window.VaultAPI.devUpdateEnrollment(e.id, { startedOn: iso }); toast("success", "Start moved to " + fmtLong(iso) + "."); window.dispatchEvent(new CustomEvent("vault:dev-updated")); }
      catch (err) { toast("error", errMsg(err)); }
    }
    async function changeStatus(e, status) {
      const ok = await window.VaultUI.confirm({ title: (status === "active" ? "Reactivate " : status === "paused" ? "Pause " : "Withdraw ") + nameOf(e.personId) + "?", message: status === "withdrawn" ? "Their history stays; the track stops counting." : "" });
      if (!ok) return;
      try { await window.VaultAPI.devUpdateEnrollment(e.id, { status }); toast("success", nameOf(e.personId) + " is now " + status + "."); window.dispatchEvent(new CustomEvent("vault:dev-updated")); }
      catch (err) { toast("error", errMsg(err)); }
    }
  }

  // ---- modals --------------------------------------------------------------
  function GrantAccessModal({ meId, isAdmin, enrollments, onClose, onDone }) {
    const T = window.VaultDevTrack;
    const already = new Set(enrollments.map(e => norm(e.personId) + "|" + e.track));
    const candidates = leadableFor(meId, isAdmin).filter(p => p.role === "Research Analyst" || isAdmin);
    const [personId, setPersonId] = useState(candidates[0] ? candidates[0].id : "");
    const [track] = useState(TRACKS[0]);
    const [startedOn, setStartedOn] = useState(T.weekMonday(todayIso()));
    const [startTier, setStartTier] = useState(1);
    const [note, setNote] = useState("");
    const [saving, setSaving] = useState(false);
    const dup = already.has(norm(personId) + "|" + track);
    async function save() {
      if (!personId) return toast("info", "Pick a person.");
      if (dup) return toast("info", nameOf(personId) + " already has this track.");
      setSaving(true);
      try { await window.VaultAPI.devGrantAccess(personId, track, startedOn, note || null, startTier); toast("success", nameOf(personId) + " granted " + track + " development" + (startTier > 1 ? ", starting at Tier " + startTier : "") + "."); onDone(); }
      catch (e) { toast("error", errMsg(e)); }
      finally { setSaving(false); }
    }
    return (
      <Modal title="Grant Development Access" onClose={onClose}>
        {candidates.length === 0 ? (
          <div className="v-empty">Nobody on your team is eligible. Research Analysts who report to you appear here.</div>
        ) : (
          <div>
            <Field label="Person">
              <select style={selectStyle} value={personId} onChange={e => setPersonId(e.target.value)}>
                {candidates.map(p => <option key={p.id} value={p.id}>{p.name}{p.role ? " · " + p.role : ""}</option>)}
              </select>
            </Field>
            <Field label="Track">
              <select style={selectStyle} value={track} disabled>{TRACKS.map(t => <option key={t} value={t}>{t}</option>)}</select>
            </Field>
            <Field label="Starting Tier">
              <select style={selectStyle} value={startTier} onChange={e => setStartTier(Number(e.target.value))}>
                <option value={1}>Tier 1 — Research Management</option>
                <option value={2}>Tier 2 — Outreach Management (Tier 1 credited as prior experience)</option>
                <option value={3}>Tier 3 — Lead Management (Tiers 1 and 2 credited as prior experience)</option>
              </select>
            </Field>
            <Field label={"Tier " + startTier + " Starts (Monday)"}>
              <input type="date" style={inputStyle} value={startedOn} onChange={e => setStartedOn(e.target.value)}/>
            </Field>
            <Field label="Note (Optional)">
              <input type="text" style={inputStyle} value={note} onChange={e => setNote(e.target.value)} placeholder="Why now, or anything the module lead should know"/>
            </Field>
            {dup && <div className="small" style={{ color: "var(--risk)", marginBottom: 8 }}>{nameOf(personId)} already has this track.</div>}
            <div className="micro" style={{ textTransform: "none", letterSpacing: 0, marginBottom: 12 }}>Visible only to {nameOf(personId) || "the analyst"}, their Sub-Team Lead, and their Module Lead. Week 1 begins on the Monday you choose; a back-dated start counts the weeks already worked. The start date can be changed later in Set-Up.</div>
            <div className="row" style={{ justifyContent: "flex-end", gap: 8 }}>
              <button className="btn" onClick={onClose}>Cancel</button>
              <button className="btn primary" disabled={saving || dup} onClick={save}>Grant Access</button>
            </div>
          </div>
        )}
      </Modal>
    );
  }

  function AuthorizeModal({ enrollment, model, tier, kind, onClose, onDone }) {
    const T = window.VaultDevTrack;
    const t = model.tiers.find(x => x.n === tier);
    const [nextOn, setNextOn] = useState(T.addDays(T.weekMonday(todayIso()), 7));
    const [note, setNote] = useState("");
    const [saving, setSaving] = useState(false);
    async function save() {
      setSaving(true);
      try { await window.VaultAPI.devAuthorizeTier(enrollment.id, tier, kind, tier < 3 ? nextOn : null, note || null); toast("success", "Tier " + tier + " authorized for " + nameOf(enrollment.personId) + "."); onDone(); }
      catch (e) { toast("error", errMsg(e)); }
      finally { setSaving(false); }
    }
    return (
      <Modal title={(kind === "override" ? "Override and Authorize" : "Authorize") + " Tier " + tier + " — " + t.name} onClose={onClose}>
        <div style={{ fontSize: "var(--t-body)", color: "var(--ink-2)", marginBottom: 12 }}>
          {kind === "override"
            ? nameOf(enrollment.personId) + " has " + t.remaining + " of " + t.total + " Tier " + tier + " goals outstanding. Authorizing now records an override in the log."
            : "All " + t.total + " Tier " + tier + " statistics are met for " + nameOf(enrollment.personId) + "."}
        </div>
        {tier < 3 && (
          <Field label={"Tier " + (tier + 1) + " Starts (Monday)"}>
            <input type="date" style={inputStyle} value={nextOn} onChange={e => setNextOn(e.target.value)}/>
          </Field>
        )}
        <Field label={kind === "override" ? "Reason" : "Note (Optional)"}>
          <input type="text" style={inputStyle} value={note} onChange={e => setNote(e.target.value)} placeholder={kind === "override" ? "e.g. Override on Weekly Mailings (Wk 11)" : ""}/>
        </Field>
        <div className="row" style={{ justifyContent: "flex-end", gap: 8 }}>
          <button className="btn" onClick={onClose}>Cancel</button>
          <button className="btn primary" disabled={saving || (kind === "override" && !note.trim())} onClick={save}>{tier < 3 ? "Authorize and Open Tier " + (tier + 1) : "Authorize Development Review"}</button>
        </div>
      </Modal>
    );
  }

  // The congratulations belongs to the analyst. Confetti colours come from
  // theme tokens (BRANDING rule 1), read once from the document.
  function CongratsModal({ data, onClose }) {
    const { auth, model, enrollment } = data;
    const t = model.tiers.find(x => x.n === auth.tier);
    const canvasRef = useRef(null);
    useEffect(() => {
      const canvas = canvasRef.current; if (!canvas) return;
      if (window.matchMedia && window.matchMedia("(prefers-reduced-motion: reduce)").matches) return;
      canvas.width = window.innerWidth; canvas.height = window.innerHeight;
      const css = getComputedStyle(document.documentElement);
      const colors = ["--accent", "--accent-2", "--gold", "--ok", "--risk"].map(k => css.getPropertyValue(k).trim()).filter(Boolean);
      const parts = [];
      const x = canvas.width / 2, y = canvas.height / 2.6;
      for (let i = 0; i < 140; i++) {
        const a = Math.random() * Math.PI * 2, sp = 4 + Math.random() * 7;
        parts.push({ x, y, vx: Math.cos(a) * sp, vy: Math.sin(a) * sp - 3.5, w: 4 + Math.random() * 5, h: 6 + Math.random() * 6, rot: Math.random() * Math.PI, vr: (Math.random() - .5) * .3, c: colors[i % colors.length], life: 1 });
      }
      let raf = 0;
      const ctx = canvas.getContext("2d");
      const loop = () => {
        ctx.clearRect(0, 0, canvas.width, canvas.height);
        let alive = 0;
        parts.forEach(p => {
          if (p.life <= 0) return; alive++;
          p.vy += 0.16; p.x += p.vx; p.y += p.vy; p.vx *= 0.985; p.rot += p.vr; p.life -= 0.008;
          ctx.save(); ctx.translate(p.x, p.y); ctx.rotate(p.rot); ctx.globalAlpha = Math.max(0, Math.min(1, p.life * 1.4));
          ctx.fillStyle = p.c; ctx.fillRect(-p.w / 2, -p.h / 2, p.w, p.h); ctx.restore();
        });
        if (alive) raf = requestAnimationFrame(loop); else ctx.clearRect(0, 0, canvas.width, canvas.height);
      };
      raf = requestAnimationFrame(loop);
      return () => cancelAnimationFrame(raf);
    }, []);
    const first = String(nameOf(enrollment.personId)).split(" ")[0];
    const by = whoOf(auth.authorizedBy);
    const next = auth.tier < 3 ? model.tiers[auth.tier] : null;
    const cell = (icon, kicker, body) => (
      <div style={{ display: "flex", flexDirection: "column", alignItems: "center", gap: 6, padding: "0 10px" }}>
        {icon}
        <div className="v-kicker">{kicker}</div>
        <div style={{ fontSize: "var(--t-body)", color: "var(--ink)", fontWeight: "var(--w-bold)", lineHeight: 1.4 }}>{body}</div>
      </div>
    );
    const ic = (d) => <svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="var(--accent)" strokeWidth="2" aria-hidden="true">{d}</svg>;
    return (
      <React.Fragment>
        <canvas ref={canvasRef} style={{ position: "fixed", inset: 0, pointerEvents: "none", zIndex: 300 }}/>
        <div onClick={onClose} style={{ position: "fixed", inset: 0, background: "var(--scrim)", zIndex: 200 }}/>
        <div style={{ position: "fixed", inset: 0, zIndex: 201, display: "grid", placeItems: "center", overflow: "auto", padding: 24, pointerEvents: "none" }}>
          <div className="v-modal" style={{ pointerEvents: "auto", position: "relative", width: 600, maxWidth: "92vw", padding: "30px 36px 26px", textAlign: "center" }}>
            <div onClick={onClose} style={{ position: "absolute", top: 12, right: 18, color: "var(--muted-2)", fontSize: 20, lineHeight: 1, cursor: "pointer" }}>×</div>
            <div style={{ width: 96, height: 96, borderRadius: "50%", background: "var(--accent-soft)", display: "grid", placeItems: "center", margin: "0 auto" }}>
              <svg width="50" height="50" viewBox="0 0 52 52" aria-hidden="true"><path d="M13 27 L22.5 36 L39 17" fill="none" stroke="var(--accent)" strokeWidth="5" strokeLinecap="round" strokeLinejoin="round"/></svg>
            </div>
            <div style={{ marginTop: 12 }}>
              <span style={{ display: "inline-block", background: "var(--accent-soft)", color: "var(--accent)", fontSize: "var(--t-kicker)", fontWeight: "var(--w-bold)", letterSpacing: 1.2, textTransform: "uppercase", padding: "6px 14px", borderRadius: "var(--r-pill)" }}>★ Tier {auth.tier} Complete</span>
            </div>
            <h1 className="v-h1" style={{ margin: "14px 0 10px" }}>Congratulations, {first}!</h1>
            <div style={{ fontSize: "var(--t-body)", color: "var(--ink-2)", lineHeight: 1.65 }}>
              You have completed <strong style={{ color: "var(--ink)" }}>Tier {auth.tier} — {t.name}</strong>.<br/>
              {t.done} of {t.total} goals were completed across Research, Outreach, and Knowledge.<br/>
              {next ? "You have unlocked the next phase of your development." : "Your development review is open."}
            </div>
            <div style={{ marginTop: 18, border: "1px solid var(--line)", borderRadius: "var(--r-card)", padding: "16px 6px 14px", display: "grid", gridTemplateColumns: "repeat(4, 1fr)" }}>
              {cell(ic(<React.Fragment><circle cx="12" cy="12" r="9"/><circle cx="12" cy="12" r="4"/></React.Fragment>), "Milestone Completed", "Tier " + auth.tier + " — " + t.name)}
              {cell(ic(<React.Fragment><rect x="3.5" y="5" width="17" height="15.5" rx="2"/><path d="M3.5 9.5h17M8 3v4M16 3v4"/></React.Fragment>), "Time Period", fmt(t.startsOn) + " – " + fmtLong(String(auth.authorizedAt).slice(0, 10)))}
              {cell(ic(<React.Fragment><circle cx="12" cy="12" r="9"/><path d="M8.5 12.5l2.5 2.5 5-5.5"/></React.Fragment>), "Goals Achieved", t.done + " of " + t.total)}
              {cell(ic(<React.Fragment><circle cx="12" cy="8.5" r="3.5"/><path d="M5.5 20c1.3-3.2 3.8-4.8 6.5-4.8s5.2 1.6 6.5 4.8"/></React.Fragment>), "Approved By", by)}
            </div>
            <div style={{ marginTop: 12, background: "var(--gold-soft)", border: "1px solid var(--gold-line)", color: "var(--gold-ink)", borderRadius: "var(--r-ctl)", padding: "9px 14px", fontSize: "var(--t-small)", fontWeight: "var(--w-medium)" }}>
              ✦ Great work. Your focus and consistency set the foundation for {next ? "success in Tier " + next.n : "the promotion discussion"}.
            </div>
            <button className="btn primary" onClick={onClose} style={{ margin: "18px auto 0", padding: "11px 22px" }}>{next ? "Start Tier " + next.n + " — " + next.name + " →" : "Continue"}</button>
          </div>
        </div>
      </React.Fragment>
    );
  }

  window.DevelopmentScreen = DevelopmentScreen;
})();
