// screens-marketing.jsx — Research → Marketing Materials.
//
// Built 2026-09-03 from the Claude Design package "Marketing Materials"
// (Marketing Materials.dc.html) and Marketing_Materials_Screen_Build.docx.
// Replaces the Marketing Deck Progression SOON row.
//
// One MATERIAL per client × kind (sql_224), grouped by the client owner's
// sub-team the way Research Plan groups its cards. The LINK is what Development
// counts, by whoever pasted it — so every save carries the signed-in email.
//
//   Marketing Deck     five process steps, one OneDrive link, review state.
//                      Final Approved decks retire to a list; Reactivate undoes.
//   Outreach Forms     one chip per form (M1 … CE); a saved link turns it green.
//                      13 of 13 or a Full Batch link = Full Set. Retire keeps it.
//   Marketing Account  reply-draft timeline per client; drafts marked ACCEPTED;
//                      the set retires after the client accepts.
//
// Review moves none → Team Leader → Module Leader → Approved. Anyone submits;
// a sub-team lead passes it up; the module lead (or firm scope) approves.
(function () {
  const { useState, useEffect, useMemo } = React;
  const STEPS = ["Create Internal Deck Draft", "Marketing Team in Process", "Waiting Client Feedback", "Applying Client Redlines", "Waiting for Final Approval"];
  const FORMS = ["M1", "eM1", "E1", "E2", "E3", "E4", "E5", "E6", "E7", "E8", "E9", "M2", "eM2", "TS", "CE"];
  const FLABELS = { TS: "Tradeshow", CE: "Custom Announcement", FB: "Full Batch" };
  const ALLFORMS = FORMS.concat(["FB"]);
  const CORE = FORMS.filter(f => f !== "TS" && f !== "CE");
  const REVIEW = {
    none:     { label: "Not Submitted",              cls: "" },
    team:     { label: "In Review · Team Leader",   cls: "gold" },
    module:   { label: "In Review · Module Leader", cls: "gold" },
    approved: { label: "Approved",                   cls: "ok" },
  };
  const REVIEW_ORDER = ["none", "team", "module", "approved"];
  const TABS = [["deck", "Marketing Deck"], ["forms", "Outreach Forms"], ["acct", "Marketing Account"]];
  const KIND_OF_TAB = { deck: "deck", forms: "form_set", acct: "reply_set" };

  const norm = (id) => (window.VaultOrg && window.VaultOrg.normalizeId) ? window.VaultOrg.normalizeId(id) : String(id || "");
  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 lastName = (id) => { const p = personOf(id); return p ? String(p.name || "").split(" ").slice(-1)[0] : String(id || ""); };
  const fmt = (iso) => iso && window.VaultDate ? window.VaultDate(String(iso).slice(0, 10)) : (iso ? String(iso).slice(0, 10) : "—");
  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.");
  const whoOf = (email) => { const pid = window.VaultAPI && window.VaultAPI.personIdForLogin ? window.VaultAPI.personIdForLogin(email) : null; return pid || null; };
  const fileNameOf = (url) => { try { const n = decodeURIComponent(String(url).split("/").pop().split("?")[0]); return n && n.length >= 5 ? n : ""; } catch (e) { return ""; } };

  // Who may move a review forward. Anyone submits (none → team); a sub-team
  // lead passes it to the module lead; the module lead or firm scope approves.
  function canAdvance(review, user, moduleHandle) {
    const meId = norm(user && (user.personId || user.id));
    const tier = window.VaultOrg && window.VaultOrg.viewerTier ? window.VaultOrg.viewerTier(user) : null;
    const firm = tier === "developer" || tier === "administration";
    const moduleLead = meId === moduleHandle;
    const subLead = !!(window.VaultOrg && window.VaultOrg.leadsASubTeam && window.VaultOrg.leadsASubTeam(meId));
    if (review === "none") return true;
    if (review === "team") return subLead || moduleLead || firm;
    if (review === "module") return moduleLead || firm;
    return subLead || moduleLead || firm; // approved → back to none
  }
  function Pill({ cls, children, onClick, title }) {
    return <span className={"pill " + (cls || "")} onClick={onClick} title={title} style={onClick ? { cursor: "pointer" } : undefined}><span className="dot"/>{children}</span>;
  }
  function ReviewPill({ m, user, moduleHandle, onChange }) {
    const r = REVIEW[m.review] || REVIEW.none;
    const ok = canAdvance(m.review, user, moduleHandle);
    const next = REVIEW_ORDER[(REVIEW_ORDER.indexOf(m.review) + 1) % 4];
    return <Pill cls={r.cls} onClick={ok ? () => onChange(next) : undefined}
      title={ok ? "Click to move to " + REVIEW[next].label : "Only the " + (m.review === "team" ? "sub-team or module lead" : "module lead") + " can move this on"}>{r.label}</Pill>;
  }
  const inputStyle = { flex: 1, minWidth: 0 };

  // =========================================================================
  // SCOPE, the Research Plan way (screens-research.jsx moduleHandle): the
  // module comes from the shared app scope -- sub-team, sub-sub-team, or an
  // individual's team -- and is CLAMPED by resolveModule() to something this
  // viewer may hold. Firm scope also gets the module select from the design.
  function moduleFromScope(scope, user) {
    const F = window.VAULT_FIRM || {}, ORG = window.VaultOrg;
    const clamp = (h) => ORG && ORG.resolveModule ? ORG.resolveModule(user, h) : h;
    const sc = scope || {};
    if (sc.subteam && sc.subteam.length >= 1) return clamp(String(sc.subteam[0]).replace(/^st-/, ""));
    if (sc.subSubteam && sc.subSubteam.length >= 1) {
      const sst = (F.SUBSUBTEAMS || []).find(x => x.id === sc.subSubteam[0]);
      if (sst && sst.parentSubteam) return clamp(String(sst.parentSubteam).replace(/^st-/, ""));
    }
    if (sc.individuals && sc.individuals.length >= 1) {
      const p = (F.PEOPLE_BY_ID || {})[sc.individuals[0]];
      if (p) return clamp(p.subteam ? String(p.subteam).replace(/^st-/, "") : p.team);
    }
    return ORG ? ORG.defaultModule(user) : null;
  }

  function MarketingScreen({ user, scope }) {
    const API = window.VaultAPI, ORG = window.VaultOrg;
    const meId = norm(user && (user.personId || user.id));
    const myEmail = String((user && (user.email || user.userEmail)) || "").toLowerCase() || null;
    const modules = ORG ? ORG.selectableModules(user) : [];
    const scopeModule = useMemo(() => moduleFromScope(scope, user), [JSON.stringify(scope && scope.subteam), JSON.stringify(scope && scope.subSubteam), JSON.stringify(scope && scope.individuals), user]);
    const [moduleSel, setModuleSel] = useState(null);
    useEffect(() => { setModuleSel(null); }, [scopeModule]);
    const moduleHandle = ORG ? ORG.resolveModule(user, moduleSel || scopeModule) : (moduleSel || scopeModule);
    const moduleLabel = moduleHandle ? lastName(moduleHandle) + " Module" : "No Module";
    const [tab, setTab] = useState("deck");
    const [clients, setClients] = useState([]);
    const [mats, setMats] = useState(null);
    const [error, setError] = useState(null);
    const [newOpen, setNewOpen] = useState(false);
    const [editor, setEditor] = useState(null);   // { matId, code }
    const [input, setInput] = useState("");
    const [activeSet, setActiveSet] = useState(null);
    const [hl, setHl] = useState(null);           // { matId, email }

    async function load() {
      if (!moduleHandle) { setMats([]); return; }
      try {
        setError(null);
        const [active, pending, assigns, list] = await Promise.all([
          API.listActiveClients(moduleHandle), API.listPendingProjects(moduleHandle), API.listClientAssignments(moduleHandle), API.listMarketingMaterials(moduleHandle),
        ]);
        const ownerByProject = {}; (assigns || []).forEach(a => { ownerByProject[a.projectId] = a.ownerId; });
        const cl = (active || []).map(c => ({ subjectKind: "project", subjectKey: c.projectId, name: c.buyerName || c.detail || ("Project " + c.projectId), sub: c.category || "", ownerId: ownerByProject[c.projectId] || null }))
          .concat((pending || []).map(p => ({ subjectKind: "pending", subjectKey: String(p.id), name: p.projectName || p.buyerName || "Pending project", sub: p.buyerName && p.projectName ? p.buyerName : "Pending Project", ownerId: p.ownerId || null })));
        setClients(cl); setMats(list || []);
      } catch (e) { setError(errMsg(e)); setMats([]); }
    }
    useEffect(() => { load(); setEditor(null); setInput(""); }, [moduleHandle]);

    const clientOf = (m) => clients.find(c => c.subjectKind === m.subjectKind && c.subjectKey === m.subjectKey) || null;
    const subTeamOf = (ownerId) => {
      if (!ownerId) return { key: "zz-unassigned", label: "Unassigned" };
      const p = personOf(ownerId); if (!p) return { key: "zz-unassigned", label: "Unassigned" };
      const lead = p.subTeamLeadId && p.subTeamLeadId !== p.id ? p.subTeamLeadId : p.id;
      return { key: lead, label: lastName(lead) + " Team" };
    };
    const groupBy = (list) => {
      const g = {};
      list.forEach(m => { const c = clientOf(m); const st = subTeamOf(c ? c.ownerId : null); (g[st.key] = g[st.key] || { label: st.label, items: [] }).items.push(m); });
      return Object.keys(g).sort().map(k => g[k]);
    };
    const upd = (m) => setMats(prev => prev.map(x => x.id === m.id ? Object.assign({}, m, { links: x.links }) : x));

    async function patch(m, p, msg) {
      try { const r = await API.updateMarketingMaterial(m.id, p, myEmail); upd(r); if (msg) toast("success", msg); }
      catch (e) { toast("error", errMsg(e)); }
    }
    async function saveLink(m, code, url, replaceId) {
      const u = String(url || "").trim();
      try {
        if (!u) {
          if (replaceId) { await API.deleteMarketingLink(replaceId); await load(); }
          setEditor(null); setInput(""); return;
        }
        const l = await API.saveMarketingLink({ materialId: m.id, code, url: u, name: fileNameOf(u), authorEmail: myEmail, replaceId: replaceId || null });
        setMats(prev => prev.map(x => x.id !== m.id ? x : Object.assign({}, x, { savedAt: new Date().toISOString(), links: [l].concat(x.links.filter(y => y.id !== l.id)) })));
        setEditor(null); setInput("");
        toast("success", "Link saved.");
      } catch (e) { toast("error", errMsg(e)); }
    }
    async function removeLink(m, link) {
      const ok = await window.VaultUI.confirm({ title: "Remove this link?", message: "The OneDrive file is untouched; only Vault's record goes." });
      if (!ok) return;
      try { await API.deleteMarketingLink(link.id); setMats(prev => prev.map(x => x.id !== m.id ? x : Object.assign({}, x, { links: x.links.filter(y => y.id !== link.id) }))); setEditor(null); setInput(""); }
      catch (e) { toast("error", errMsg(e)); }
    }
    async function createMaterial(c) {
      try {
        const m = await API.upsertMarketingMaterial({ module: moduleHandle, subjectKind: c.subjectKind, subjectKey: c.subjectKey, clientName: c.name, kind: KIND_OF_TAB[tab], createdBy: myEmail });
        setMats(prev => prev.some(x => x.id === m.id) ? prev : prev.concat([m]));
        setNewOpen(false);
        if (tab === "acct") setActiveSet(m.id);
        toast("success", c.name + " added.");
      } catch (e) { toast("error", errMsg(e)); }
    }

    const kindMats = (mats || []).filter(m => m.kind === KIND_OF_TAB[tab]);
    const cta = { deck: "+ New Deck", forms: "+ New Form Set", acct: "+ New Reply Set" }[tab];
    const shared = { user, moduleHandle, myEmail, meId, clientOf, groupBy, patch, saveLink, removeLink, editor, setEditor, input, setInput, hl, setHl };

    return (
      <div style={{ padding: "18px 22px", background: "var(--bg)", minHeight: "100%" }}>
        <window.PageToolbar title="Marketing Materials" subtitle={"Client Marketing Tracking · " + moduleLabel}>
          {modules.length > 1 && (
            <select value={moduleHandle || ""} onChange={e => setModuleSel(e.target.value)} title="Module scope">
              {modules.map(h => <option key={h} value={h}>{lastName(h)} Module</option>)}
            </select>
          )}
          <window.VaultSeg options={TABS} value={tab} onChange={(t) => { setTab(t); setEditor(null); setInput(""); }}/>
          {moduleHandle && <button className="btn primary sm" onClick={() => setNewOpen(true)}>{cta}</button>}
        </window.PageToolbar>
        {error && <div className="v-card" style={{ padding: "12px 16px", marginBottom: 14, borderColor: "var(--risk)", color: "var(--risk)" }}>{error}</div>}
        {mats === null ? <window.VaultLoader/>
          : !moduleHandle ? <window.EmptyState title="No module in scope." hint="Your login is not on a deal module."/>
          : kindMats.length === 0 ? <window.EmptyState title={"No " + { deck: "decks", forms: "form sets", acct: "reply sets" }[tab] + " tracked for the " + moduleLabel + " yet."} hint="Start one from the button above; it is tied to an Active Client or Pending Project." actionLabel={cta} onAction={() => setNewOpen(true)}/>
          : tab === "deck" ? <DeckTab mats={kindMats} {...shared}/>
          : tab === "forms" ? <FormsTab mats={kindMats} {...shared}/>
          : <AcctTab mats={kindMats} activeSet={activeSet} setActiveSet={setActiveSet} {...shared}/>}
        {newOpen && <NewMaterialModal tab={tab} clients={clients.filter(c => !kindMats.some(m => m.subjectKind === c.subjectKind && m.subjectKey === c.subjectKey))} onClose={() => setNewOpen(false)} onPick={createMaterial}/>}
      </div>
    );
  }

  // ---- Marketing Deck ------------------------------------------------------
  function DeckTab({ mats, user, moduleHandle, myEmail, clientOf, groupBy, patch, saveLink, removeLink, editor, setEditor, input, setInput }) {
    const live = mats.filter(m => m.review !== "approved");
    const retired = mats.filter(m => m.review === "approved");
    const kpi = [
      ["Decks in Process", live.length, "var(--ink)"],
      ["Awaiting Review", mats.filter(m => m.review === "team" || m.review === "module").length, "var(--gold-ink)"],
      ["Waiting on Client", live.filter(m => m.step === 2).length, "var(--ink)"],
      ["Final Approved", retired.length, "var(--ok)"],
    ];
    const deckLink = (m) => m.links.find(l => l.code === "deck") || null;
    return (
      <div>
        <div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(11rem, 1fr))", gap: 12, marginBottom: 18 }}>
          {kpi.map(([k, v, c]) => (
            <div key={k} className="v-card" style={{ padding: "11px 14px" }}>
              <div className="v-kicker">{k}</div>
              <div className="num" style={{ fontSize: "var(--t-page)", fontWeight: "var(--w-medium)", color: c, marginTop: 2 }}>{v}</div>
            </div>
          ))}
        </div>
        {groupBy(live).map(g => (
          <div key={g.label} style={{ marginBottom: 20 }}>
            <div className="row" style={{ gap: 8, alignItems: "baseline", marginBottom: 10 }}>
              <span className="v-kicker">Sub-Team</span>
              <span style={{ fontSize: "var(--t-section)", fontWeight: "var(--w-medium)", color: "var(--ink)" }}>{g.label}</span>
              <span className="micro" style={{ textTransform: "none", letterSpacing: 0 }}>{g.items.length} {g.items.length === 1 ? "deck" : "decks"}</span>
            </div>
            <div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fill, minmax(20rem, 1fr))", gap: 12 }}>
              {g.items.map(m => {
                const c = clientOf(m), link = deckLink(m), last = m.step === 4;
                const editing = editor && editor.matId === m.id && editor.code === "deck";
                const author = link ? whoOf(link.authorEmail) : null;
                return (
                  <div key={m.id} className="v-card" style={{ padding: 14, display: "flex", flexDirection: "column" }}>
                    <div className="row" style={{ justifyContent: "space-between", alignItems: "flex-start", gap: 10 }}>
                      <div style={{ minWidth: 0 }}>
                        <div style={{ fontWeight: "var(--w-medium)", color: "var(--ink)", fontSize: "var(--t-body-lg)" }}>{m.clientName}</div>
                        <div style={{ fontSize: "var(--t-small)", color: "var(--accent)", fontWeight: "var(--w-medium)", marginTop: 2 }}>{c ? c.sub : ""}</div>
                      </div>
                      {c && c.ownerId ? <window.Avatar id={c.ownerId}/> : null}
                    </div>
                    <div style={{ background: last ? "var(--ok-soft)" : "var(--accent-soft)", borderRadius: "var(--r-ctl)", padding: "8px 11px", margin: "12px 0", fontSize: "var(--t-body)" }}>
                      <span style={{ color: "var(--muted)" }}>Next: </span><span style={{ fontWeight: "var(--w-medium)", color: last ? "var(--ok)" : "var(--accent)" }}>{STEPS[m.step]}</span>
                    </div>
                    <div className="col">
                      {STEPS.map((label, i) => (
                        <div key={label} onClick={() => patch(m, { step: i })} title="Click to make this the current step" className="row" style={{ gap: 10, padding: "3px 0", cursor: "pointer" }}>
                          <div className="col" style={{ alignItems: "center", width: 14 }}>
                            <span style={{ width: 11, height: 11, borderRadius: "50%", boxSizing: "border-box", border: "2px solid " + (i < m.step ? "var(--ok)" : i === m.step ? "var(--accent)" : "var(--line-strong)"), background: i < m.step ? "var(--ok)" : "transparent" }}/>
                            {i < 4 && <span style={{ width: 2, height: 10, background: "var(--line)" }}/>}
                          </div>
                          <span style={{ fontSize: "var(--t-body)", fontWeight: i === m.step ? "var(--w-medium)" : "var(--w-regular)", color: i < m.step ? "var(--muted)" : i === m.step ? "var(--accent)" : "var(--ink-2)" }}>{label}</span>
                        </div>
                      ))}
                    </div>
                    <div className="row" style={{ gap: 8, marginTop: "auto", paddingTop: 11, borderTop: "1px solid var(--line-2)", marginTop: 12 }}>
                      {link ? (
                        <React.Fragment>
                          {author ? <window.Avatar id={author} px={18}/> : null}
                          <a href={link.url} target="_blank" rel="noopener noreferrer" style={{ fontSize: "var(--t-small)", fontWeight: "var(--w-medium)", color: "var(--accent)", textDecoration: "none" }}>OneDrive · Deck ↗</a>
                          <button className="btn sm" onClick={() => { setEditor({ matId: m.id, code: "deck" }); setInput(link.url); }}>Edit</button>
                          <button className="btn ghost sm" style={{ color: "var(--risk)" }} onClick={() => removeLink(m, link)}>Remove</button>
                        </React.Fragment>
                      ) : (
                        <button className="btn primary sm" onClick={() => { setEditor({ matId: m.id, code: "deck" }); setInput(""); }}>+ Add OneDrive Link</button>
                      )}
                      <span className="micro" style={{ marginLeft: "auto", textTransform: "none", letterSpacing: 0 }}>Saved {fmt(m.savedAt)}</span>
                    </div>
                    {editing && (
                      <div className="row" style={{ gap: 6, marginTop: 9 }}>
                        <input style={inputStyle} value={input} onChange={e => setInput(e.target.value)} placeholder="Paste OneDrive link…" autoFocus/>
                        <button className="btn primary sm" onClick={() => saveLink(m, "deck", input, link ? link.id : null)}>Save</button>
                        <button className="btn sm" onClick={() => { setEditor(null); setInput(""); }}>×</button>
                      </div>
                    )}
                    <div className="row" style={{ gap: 8, marginTop: 9 }}>
                      <ReviewPill m={m} user={user} moduleHandle={moduleHandle} onChange={(r) => patch(m, { review: r }, r === "approved" ? m.clientName + " deck approved and retired." : null)}/>
                    </div>
                  </div>
                );
              })}
            </div>
          </div>
        ))}
        {retired.length > 0 && (
          <div>
            <div className="v-kicker" style={{ marginBottom: 8 }}>Retired — Final Approved</div>
            <div className="v-card" style={{ overflow: "hidden" }}>
              {retired.map(m => {
                const c = clientOf(m), link = deckLink(m);
                return (
                  <div key={m.id} className="row" style={{ gap: 12, padding: "10px 16px", borderBottom: "1px solid var(--line-2)", fontSize: "var(--t-body)" }}>
                    {c && c.ownerId ? <window.Avatar id={c.ownerId} px={20}/> : null}
                    <div style={{ minWidth: 0 }}>
                      <div style={{ fontWeight: "var(--w-medium)", color: "var(--ink)" }}>{m.clientName}</div>
                      <div className="micro" style={{ textTransform: "none", letterSpacing: 0 }}>{c ? c.sub : ""}</div>
                    </div>
                    {link && <a href={link.url} target="_blank" rel="noopener noreferrer" style={{ fontSize: "var(--t-small)", color: "var(--accent)", textDecoration: "none" }}>OneDrive · Deck ↗</a>}
                    <span className="micro" style={{ marginLeft: "auto", textTransform: "none", letterSpacing: 0 }}>Saved {fmt(m.savedAt)}</span>
                    <Pill cls="ok">Approved</Pill>
                    {canAdvance("approved", user, moduleHandle) && <button className="btn sm" onClick={() => patch(m, { review: "none" })}>Reactivate</button>}
                  </div>
                );
              })}
            </div>
          </div>
        )}
      </div>
    );
  }

  // ---- Outreach Forms ------------------------------------------------------
  function FormsTab({ mats, user, moduleHandle, myEmail, clientOf, groupBy, patch, saveLink, removeLink, editor, setEditor, input, setInput, hl, setHl }) {
    const COLS = "minmax(8.5rem, 13rem) minmax(16rem, 1fr) 4rem 6.5rem max-content";
    const live = mats.filter(m => !m.retiredAt), retired = mats.filter(m => !!m.retiredAt);
    const groups = groupBy(live); if (retired.length) groups.push({ label: "Retired", items: retired });
    const row = (m) => {
      const c = clientOf(m);
      const byCode = {}; m.links.forEach(l => { if (!byCode[l.code]) byCode[l.code] = l; });
      const n = CORE.filter(f => byCode[f]).length;
      const full = n === CORE.length || !!byCode.FB;
      const uploaders = Array.from(new Set(m.links.map(l => String(l.authorEmail).toLowerCase())));
      const editing = editor && editor.matId === m.id; const code = editing ? editor.code : null;
      const cur = code ? byCode[code] : null;
      return (
        <div key={m.id} style={{ borderBottom: "1px solid var(--line-2)", opacity: m.retiredAt ? 0.6 : 1 }}>
          <div style={{ display: "grid", gridTemplateColumns: COLS, gap: "0 12px", alignItems: "center", padding: "10px 16px", fontSize: "var(--t-body)" }}>
            <div style={{ minWidth: 0 }}>
              <div style={{ fontWeight: "var(--w-medium)", color: "var(--ink)" }}>{m.clientName}</div>
              <div className="micro" style={{ textTransform: "none", letterSpacing: 0 }}>{c ? c.sub : ""}</div>
            </div>
            <div className="row" style={{ gap: 4, flexWrap: "wrap" }}>
              {ALLFORMS.map(f => {
                const l = byCode[f], has = !!l, active = editing && code === f;
                const hlRow = hl && hl.matId === m.id, mine = hlRow && has && String(l.authorEmail).toLowerCase() === hl.email;
                return (
                  <span key={f} onClick={() => { setEditor({ matId: m.id, code: f }); setInput(l ? l.url : ""); }}
                    title={has ? (FLABELS[f] || f) + " — uploaded by " + nameOf(whoOf(l.authorEmail) || l.authorEmail) + " (" + fmt(l.createdAt) + "). Click to view or replace." : (FLABELS[f] || f) + " — not started. Click to paste its OneDrive link."}
                    style={{ minWidth: 26, textAlign: "center", padding: "2px 5px", borderRadius: "var(--r-chip)", fontSize: "var(--t-micro)", fontWeight: "var(--w-bold)", cursor: "pointer", userSelect: "none",
                      background: has ? "var(--ok-soft)" : "var(--surface-2)", color: has ? "var(--ok)" : "var(--muted-2)",
                      border: "1px solid " + (mine || active ? "var(--accent)" : has ? "var(--ok-line)" : "var(--line-2)"),
                      boxShadow: mine || active ? "0 0 0 1px var(--accent)" : "none", opacity: hlRow && !mine ? 0.3 : 1 }}>{FLABELS[f] || f}</span>
                );
              })}
            </div>
            <span style={{ fontSize: "var(--t-small)", fontWeight: full ? "var(--w-medium)" : "var(--w-regular)", color: full ? "var(--ok)" : "var(--muted-2)" }}>{full ? "✓ Full Set" : n + " of " + CORE.length}</span>
            <div className="row" style={{ gap: 7 }}>
              <div className="row">
                {uploaders.map((u, i) => {
                  const pid = whoOf(u), on = hl && hl.matId === m.id && hl.email === u;
                  return (
                    <span key={u} onClick={() => setHl(on ? null : { matId: m.id, email: u })} title={(on ? "Showing forms uploaded by " : "Click to highlight forms uploaded by ") + nameOf(pid || u)}
                      style={{ marginLeft: i ? -6 : 0, borderRadius: "50%", boxShadow: on ? "0 0 0 2.5px var(--accent)" : "0 0 0 2px var(--surface)", cursor: "pointer", display: "inline-flex" }}>
                      {pid ? <window.Avatar id={pid} px={20}/> : <span className="avatar" style={{ width: 20, height: 20, fontSize: 9 }}>{String(u).slice(0, 2).toUpperCase()}</span>}
                    </span>
                  );
                })}
              </div>
              <span className="micro" style={{ textTransform: "none", letterSpacing: 0 }}>{fmt(m.savedAt)}</span>
            </div>
            <div className="col" style={{ alignItems: "flex-start", gap: 5 }}>
              <ReviewPill m={m} user={user} moduleHandle={moduleHandle} onChange={(r) => patch(m, { review: r })}/>
              <button className="btn sm" onClick={() => patch(m, { retiredAt: m.retiredAt ? null : new Date().toISOString() })}>{m.retiredAt ? "Reactivate" : "Retire"}</button>
            </div>
          </div>
          {editing && (
            <div className="row" style={{ gap: 8, padding: "10px 16px", background: "var(--surface-2)", borderTop: "1px solid var(--line-2)" }}>
              <span style={{ fontSize: "var(--t-body)", fontWeight: "var(--w-medium)", color: "var(--accent)", whiteSpace: "nowrap" }}>{(FLABELS[code] || code) + " · " + m.clientName + (cur ? " — uploaded by " + nameOf(whoOf(cur.authorEmail) || cur.authorEmail) + ", " + fmt(cur.createdAt) : "")}</span>
              <input style={inputStyle} value={input} onChange={e => setInput(e.target.value)} placeholder="Paste the OneDrive link for this form (empty clears it)…" autoFocus/>
              {cur && <a href={cur.url} target="_blank" rel="noopener noreferrer" style={{ fontSize: "var(--t-small)", color: "var(--accent)", textDecoration: "none", whiteSpace: "nowrap" }}>Open ↗</a>}
              {cur && <button className="btn ghost sm" style={{ color: "var(--risk)" }} onClick={() => removeLink(m, cur)}>Remove</button>}
              <button className="btn primary sm" onClick={() => saveLink(m, code, input, cur ? cur.id : null)}>Save</button>
              <button className="btn sm" onClick={() => { setEditor(null); setInput(""); }}>Cancel</button>
            </div>
          )}
        </div>
      );
    };
    return (
      <div>
        <div className="row" style={{ gap: 10, marginBottom: 14 }}>
          <span className="micro" style={{ textTransform: "none", letterSpacing: 0, display: "inline-flex", alignItems: "center", gap: 6 }}><span style={{ width: 10, height: 10, borderRadius: 3, background: "var(--ok-soft)", border: "1px solid var(--ok-line)" }}/>Link saved</span>
          <span className="micro" style={{ textTransform: "none", letterSpacing: 0, display: "inline-flex", alignItems: "center", gap: 6 }}><span style={{ width: 10, height: 10, borderRadius: 3, background: "var(--surface-2)", border: "1px solid var(--line-2)" }}/>Not started — click any block to paste its OneDrive link</span>
        </div>
        {groups.map(g => (
          <div key={g.label} style={{ marginBottom: 18 }}>
            <div className="v-kicker" style={{ marginBottom: 8 }}>{g.label}</div>
            <div className="v-card" style={{ overflow: "hidden" }}>
              <div style={{ overflowX: "auto" }}>
                <div style={{ minWidth: "56rem" }}>
                  <div className="v-thead" style={{ display: "grid", gridTemplateColumns: COLS, gap: "0 12px", padding: "9px 16px" }}>
                    <span>Client</span><span>Forms</span><span>Full Set</span><span>User / Saved</span><span>Review</span>
                  </div>
                  {g.items.map(row)}
                </div>
              </div>
            </div>
          </div>
        ))}
      </div>
    );
  }

  // ---- Marketing Account ---------------------------------------------------
  function AcctTab({ mats, user, moduleHandle, myEmail, clientOf, groupBy, patch, saveLink, removeLink, activeSet, setActiveSet }) {
    const [draftInput, setDraftInput] = useState("");
    const live = mats.filter(m => !m.retiredAt), retired = mats.filter(m => !!m.retiredAt);
    const groups = groupBy(live); if (retired.length) groups.push({ label: "Retired", items: retired });
    const ac = mats.find(m => m.id === activeSet) || live[0] || mats[0];
    const c = ac ? clientOf(ac) : null;
    const drafts = ac ? ac.links.filter(l => l.code === "draft") : [];
    const setRow = (m) => {
      const cm = clientOf(m), active = ac && m.id === ac.id, ds = m.links.filter(l => l.code === "draft");
      const st = cm && cm.ownerId ? lastName(personOf(cm.ownerId) && personOf(cm.ownerId).subTeamLeadId !== cm.ownerId && personOf(cm.ownerId).subTeamLeadId ? personOf(cm.ownerId).subTeamLeadId : cm.ownerId) : null;
      return (
        <div key={m.id} onClick={() => setActiveSet(m.id)} className="row" style={{ gap: 10, padding: "11px 14px", cursor: "pointer", background: active ? "var(--accent-soft)" : "transparent", borderLeft: "2px solid " + (active ? "var(--accent)" : "transparent"), borderTop: "1px solid var(--line-2)", opacity: m.retiredAt ? 0.6 : 1 }}>
          <div className="stretch" style={{ minWidth: 0 }}>
            <div style={{ fontWeight: active ? "var(--w-medium)" : "var(--w-regular)", color: m.retiredAt ? "var(--ink-3)" : "var(--ink)", fontSize: "var(--t-body)" }}>{(st ? st + " - " : "") + m.clientName}</div>
            <div className="micro" style={{ textTransform: "none", letterSpacing: 0 }}>{ds.length} {ds.length === 1 ? "Draft" : "Drafts"} | Last {ds[0] ? fmt(ds[0].createdAt) : "—"}</div>
          </div>
          {cm && cm.ownerId ? <window.Avatar id={cm.ownerId} px={20}/> : null}
        </div>
      );
    };
    async function saveDraft() {
      const url = draftInput.trim(); if (!url || !ac) return;
      await saveLink(ac, "draft", url, null); setDraftInput("");
    }
    async function toggleAccept(l) {
      try { const r = await window.VaultAPI.updateMarketingLink(l.id, { accepted: !l.accepted }, myEmail); patch(ac, { savedAt: new Date().toISOString() }); }
      catch (e) { toast("error", errMsg(e)); }
    }
    return (
      <div style={{ display: "grid", gridTemplateColumns: "minmax(16rem, 20rem) minmax(0, 1fr)", gap: 14, alignItems: "start" }}>
        <div className="v-card" style={{ overflow: "hidden" }}>
          {groups.map(g => (
            <div key={g.label}>
              <div className="v-kicker" style={{ padding: "10px 14px 4px" }}>{g.label}</div>
              {g.items.map(setRow)}
            </div>
          ))}
        </div>
        {ac ? (
          <div className="v-card" style={{ padding: 16 }}>
            <div className="row" style={{ justifyContent: "space-between", alignItems: "flex-start", gap: 12, marginBottom: 4 }}>
              <div style={{ minWidth: 0 }}>
                <div style={{ fontSize: "var(--t-section)", fontWeight: "var(--w-medium)", color: "var(--ink)" }}>{ac.clientName} — Reply Drafts</div>
                <div className="row" style={{ gap: 6, marginTop: 6 }}>
                  {c && c.sub ? <span className="pill">{c.sub}</span> : null}
                  <ReviewPill m={ac} user={user} moduleHandle={moduleHandle} onChange={(r) => patch(ac, { review: r })}/>
                </div>
              </div>
              <button className="btn sm" onClick={() => patch(ac, { retiredAt: ac.retiredAt ? null : new Date().toISOString() })}>{ac.retiredAt ? "Reactivate Set" : "Retire Set"}</button>
            </div>
            <div className="micro" style={{ textTransform: "none", letterSpacing: 0, marginBottom: 14 }}>Drafts are retired after the client accepts. All module users can log drafts here.</div>
            <div className="row" style={{ gap: 8, marginBottom: 16 }}>
              <input style={inputStyle} value={draftInput} onChange={e => setDraftInput(e.target.value)} placeholder="Paste a OneDrive link to a reply draft…" onKeyDown={e => { if (e.key === "Enter") saveDraft(); }}/>
              <button className="btn primary" onClick={saveDraft}>Save Draft</button>
            </div>
            <div className="v-kicker" style={{ marginBottom: 10 }}>Draft Timeline</div>
            {drafts.length === 0 && <div className="v-empty">No drafts logged yet.</div>}
            <div className="col" style={{ gap: 12 }}>
              {drafts.map(d => {
                const pid = whoOf(d.authorEmail);
                return (
                  <div key={d.id} style={{ borderLeft: "2px solid var(--line)", padding: "3px 0 3px 12px" }}>
                    <div className="row" style={{ gap: 8, minWidth: 0 }}>
                      <span onClick={() => toggleAccept(d)} title="Click to mark accepted / draft" style={{ fontWeight: "var(--w-medium)", color: d.accepted ? "var(--ok)" : "var(--accent)", background: d.accepted ? "var(--ok-soft)" : "var(--accent-soft)", padding: "1px 7px", borderRadius: "var(--r-chip)", fontSize: "var(--t-micro)", flexShrink: 0, cursor: "pointer", userSelect: "none" }}>{d.accepted ? "ACCEPTED" : "DRAFT"}</span>
                      <a href={d.url} target="_blank" rel="noopener noreferrer" title={d.name || d.url} className="stretch" style={{ minWidth: 0, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap", fontSize: "var(--t-body)", color: "var(--accent)", textDecoration: "none" }}>{d.name || d.url}</a>
                      <span className="micro" style={{ textTransform: "none", letterSpacing: 0, flexShrink: 0 }}>{fmt(d.createdAt)}</span>
                      {pid ? <window.Avatar id={pid} px={18}/> : null}
                      <span onClick={() => removeLink(ac, d)} title="Delete this draft link" style={{ color: "var(--risk)", cursor: "pointer", flexShrink: 0, padding: "0 2px" }}>×</span>
                    </div>
                  </div>
                );
              })}
            </div>
          </div>
        ) : <window.EmptyState title="No reply set selected."/>}
      </div>
    );
  }

  // ---- New material --------------------------------------------------------
  function NewMaterialModal({ tab, clients: raw, onClose, onPick }) {
    // Alphabetical (Brian, 2026-09-03); the roster arrives in Harvey order.
    const clients = raw.slice().sort((a, b) => String(a.name).localeCompare(String(b.name), undefined, { sensitivity: "base" }));
    const [sel, setSel] = useState(clients[0] ? clients[0].subjectKind + ":" + clients[0].subjectKey : "");
    const drag = window.useVaultDrag ? window.useVaultDrag() : null;
    const title = { deck: "New Marketing Deck", forms: "New Outreach Form Set", acct: "New Reply Set" }[tab];
    const pick = clients.find(c => c.subjectKind + ":" + c.subjectKey === sel);
    return (
      <div onClick={drag ? drag.guardClick(onClose) : onClose} 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: 480, 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 }}>
            {clients.length === 0 ? <div className="v-empty">Every Active Client and Pending Project already has one.</div> : (
              <React.Fragment>
                <div className="v-kicker" style={{ marginBottom: 5 }}>Client</div>
                <select style={{ width: "100%", boxSizing: "border-box" }} value={sel} onChange={e => setSel(e.target.value)}>
                  {clients.map(c => <option key={c.subjectKind + ":" + c.subjectKey} value={c.subjectKind + ":" + c.subjectKey}>{c.name}{c.sub ? " · " + c.sub : ""}{c.subjectKind === "pending" ? " (Pending)" : ""}</option>)}
                </select>
                <div className="micro" style={{ textTransform: "none", letterSpacing: 0, margin: "10px 0 14px" }}>Grouped under the client owner's sub-team. Links you paste are recorded under your name for Development.</div>
                <div className="row" style={{ justifyContent: "flex-end", gap: 8 }}>
                  <button className="btn" onClick={onClose}>Cancel</button>
                  <button className="btn primary" disabled={!pick} onClick={() => pick && onPick(pick)}>Add</button>
                </div>
              </React.Fragment>
            )}
          </div>
        </div>
      </div>
    );
  }

  window.MarketingScreen = MarketingScreen;
})();
