/* global React, Icon, BuyerSellerProgress, DEAL_DIAGNOSIS, EvidenceQuote, sortGapsByImpact, formatImpactDollars, trackNudgeEvent */

const DEAL_STAGE_OPTIONS = ["Discovery", "Qualification", "Proposal", "Negotiation", "Closed Won", "Closed Lost"];
const isClosedDetailDeal = (deal) => /^closed/i.test(deal.stage || "") || /^closed/i.test(deal.statusLabel || "");
const isLostDetailDeal = (deal) => /lost/i.test(`${deal.stage || ""} ${deal.statusLabel || ""} ${deal.status || ""}`);
const DIALOG_FOCUSABLE_SELECTOR = [
  "a[href]",
  "button:not([disabled])",
  "input:not([disabled])",
  "select:not([disabled])",
  "textarea:not([disabled])",
  "[tabindex]:not([tabindex='-1'])",
].join(",");

function getDialogFocusable(dialog) {
  return Array.from(dialog.querySelectorAll(DIALOG_FOCUSABLE_SELECTOR))
    .filter((el) => el.getClientRects().length > 0 || el === document.activeElement);
}

function useDialogFocus(dialogRef, onClose) {
  const onCloseRef = useRef(onClose);
  onCloseRef.current = onClose;

  useEffect(() => {
    const dialog = dialogRef.current;
    if (!dialog) return undefined;
    const previouslyFocused = document.activeElement;
    const focusables = getDialogFocusable(dialog);
    (focusables[0] || dialog).focus({ preventScroll: true });

    const onKeyDown = (e) => {
      const dialogs = Array.from(document.querySelectorAll("[data-dialog-root='true']"));
      if (dialogs[dialogs.length - 1] !== dialog) return;
      if (e.key === "Escape") {
        e.preventDefault();
        onCloseRef.current();
        return;
      }
      if (e.key !== "Tab") return;

      const items = getDialogFocusable(dialog);
      if (items.length === 0) {
        e.preventDefault();
        dialog.focus({ preventScroll: true });
        return;
      }
      const first = items[0];
      const last = items[items.length - 1];
      if (e.shiftKey && document.activeElement === first) {
        e.preventDefault();
        last.focus();
      } else if (!e.shiftKey && document.activeElement === last) {
        e.preventDefault();
        first.focus();
      }
    };

    document.addEventListener("keydown", onKeyDown);
    return () => {
      document.removeEventListener("keydown", onKeyDown);
      if (previouslyFocused && typeof previouslyFocused.focus === "function") {
        previouslyFocused.focus({ preventScroll: true });
      }
    };
  }, [dialogRef]);
}

// ---------- DEAL DETAIL MODAL ----------
// Two primary tabs (Deal Truth, Next move). The rest live behind "More".
function DealDetail({ deal: dealProp, onClose, onUpdateDeal, initialTab = "overview", managerMode = false }) {
  const [tab, setTab] = useState("overview");
  const [executed, setExecuted] = useState({});
  const modalRef = useRef(null);
  // Apply NudgeBackend overlay so the deal-room reflects any action's
  // buyer reply in real time (BSP bar, tone wash, gap, status all flow
  // through this single source). Subscription is component-scoped so the
  // modal redraws when state.actions changes.
  const [, forceNudge] = useState(0);
  useEffect(() => {
    if (!window.NudgeBackend) return;
    return window.NudgeBackend.subscribe(() => forceNudge((n) => n + 1));
  }, [dealProp.id]);
  const deal = window.NudgeBackend ? window.NudgeBackend.applyImpact(dealProp) : dealProp;
  const titleId = `deal-detail-title-${deal.id}`;
  useDialogFocus(modalRef, onClose);

  useEffect(() => {
    setTab(initialTab || "overview");
    setExecuted({});
  }, [deal.id, initialTab]);

  const primaryTabs = managerMode
    ? [
        { k: "overview", label: "Forecast read" },
        { k: "ai",       label: "Coaching moves" },
      ]
    : [
        { k: "overview", label: "Deal Truth" },
        { k: "ai",       label: "Next move" },
      ];
  const moreTabs = [
    { k: "note", label: "Create note" },
    { k: "edit", label: "Edit deal" },
    { k: "activity", label: "Activity" },
    { k: "stake", label: "Stakeholders" },
    { k: "meetings", label: "Meetings" },
    { k: "resources", label: "Resources" },
  ];

  const dealMeta = [
    `$${(deal.value / 1000).toFixed(0)}K`,
    deal.stage,
    deal.closeDate,
  ];
  const activeMore = moreTabs.find((t) => t.k === tab);

  return (
    <div className="modal-back" onClick={onClose}>
      <div
        ref={modalRef}
        className="modal"
        onClick={(e) => e.stopPropagation()}
        data-dialog-root="true"
        data-screen-label="03 Deal Detail"
        role="dialog"
        aria-modal="true"
        aria-labelledby={titleId}
        tabIndex={-1}
      >
        <div className="modal-head">
          <div className="modal-status">
            <div className="modal-title-row">
              <h2 className="h2" id={titleId}>{deal.company}</h2>
            </div>
            <div className="deal-meta-line" aria-label="Deal metadata">
              {dealMeta.join(" · ")}
            </div>
          </div>
          <button className="btn sm ghost" onClick={onClose} aria-label="Close deal room"><Icon name="close" size={14} /></button>
        </div>

        <div className="modal-body">
          <div className="dd-tabs">
            <div className="dd-tabs-l">
              {primaryTabs.map((t) => (
                <button
                  key={t.k}
                  onClick={() => setTab(t.k)}
                  className={`dd-tab ${tab === t.k ? "is-active" : ""}`}
                  data-tour={t.k === "ai" ? "next-move-tab" : undefined}
                >
                  {t.label}
                </button>
              ))}
            </div>
            <MoreTabsMenu
              tabs={moreTabs}
              activeKey={tab}
              activeLabel={activeMore?.label}
              onSelect={setTab}
            />
          </div>

          {tab === "overview" && (
            managerMode
              ? <ManagerOverview deal={deal} setTab={setTab} />
              : <OverviewTab deal={deal} setTab={setTab} />
          )}
          {tab === "ai" && <AIActionsTab deal={deal} executed={executed} setExecuted={setExecuted} setTab={setTab} />}
          {tab === "note" && <CreateNoteTab deal={deal} onUpdateDeal={onUpdateDeal} setTab={setTab} />}
          {tab === "edit" && <EditDealTab deal={deal} onUpdateDeal={onUpdateDeal} setTab={setTab} />}
          {tab === "activity" && <ActivityTab deal={deal} />}
          {tab === "stake" && <StakeholderTab deal={deal} />}
          {tab === "meetings" && <MeetingsTab deal={deal} />}
          {tab === "resources" && <ResourcesTab deal={deal} />}
        </div>
      </div>
    </div>
  );
}

function formatActivityStamp(date = new Date()) {
  return date.toLocaleString("en-US", {
    month: "short",
    day: "numeric",
    hour: "numeric",
    minute: "2-digit",
  });
}

function formatArrValue(value) {
  const amount = Number(value) || 0;
  if (amount >= 1000000) return `$${(amount / 1000000).toFixed(amount % 1000000 === 0 ? 0 : 1)}M ARR`;
  return `$${Math.round(amount / 1000)}K ARR`;
}

function closeDateToInput(value) {
  const parsed = new Date(value);
  if (Number.isNaN(parsed.getTime())) return "";
  const yyyy = parsed.getFullYear();
  const mm = String(parsed.getMonth() + 1).padStart(2, "0");
  const dd = String(parsed.getDate()).padStart(2, "0");
  return `${yyyy}-${mm}-${dd}`;
}

function inputToCloseDate(value) {
  if (!value) return "";
  const parsed = new Date(`${value}T12:00:00`);
  if (Number.isNaN(parsed.getTime())) return "";
  return parsed.toLocaleDateString("en-US", {
    month: "short",
    day: "numeric",
    year: "numeric",
  });
}

function MoreTabsMenu({ tabs, activeKey, activeLabel, onSelect }) {
  const [open, setOpen] = useState(false);
  const ref = useRef(null);

  useEffect(() => {
    if (!open) return;
    const handle = (e) => {
      if (ref.current && !ref.current.contains(e.target)) {
        // Keep the menu open during a guided tour — the tour clicks the
        // trigger and items programmatically, and its tooltip lives
        // outside this ref. Without this guard the tooltip mousedown
        // would close the menu before the tour could spotlight the
        // Activity option inside it.
        if (e.target.closest && e.target.closest(".tour-tooltip, .tour-root")) return;
        setOpen(false);
      }
    };
    document.addEventListener("mousedown", handle);
    return () => document.removeEventListener("mousedown", handle);
  }, [open]);

  return (
    <div className="dd-more" ref={ref}>
      <button
        type="button"
        className={`dd-more-btn ${activeLabel ? "is-active" : ""}`}
        data-tour="more-tab"
        onClick={() => setOpen((v) => !v)}
        aria-haspopup="menu"
        aria-expanded={open}
      >
        {activeLabel || "More"} <Icon name="chevron" size={12} />
      </button>
      {open && (
        <ul className="dd-more-menu" role="menu">
          {tabs.map((t) => (
            <li key={t.k}>
              <button
                type="button"
                role="menuitem"
                className={`dd-more-item ${t.k === activeKey ? "is-active" : ""}`}
                data-tour={`more-${t.k}`}
                onClick={() => { onSelect(t.k); setOpen(false); }}
              >
                {t.label}
              </button>
            </li>
          ))}
        </ul>
      )}
    </div>
  );
}

function CreateNoteTab({ deal, onUpdateDeal, setTab }) {
  const [title, setTitle] = useState("");
  const [body, setBody] = useState("");
  const canSave = title.trim().length > 0 && body.trim().length > 0;

  const saveNote = () => {
    if (!canSave) return;
    const note = {
      time: formatActivityStamp(),
      kind: "note",
      actor: "Ridha",
      title: title.trim(),
      body: body.trim(),
      tag: "internal",
    };
    onUpdateDeal(deal.id, (current) => ({
      ...current,
      activity: [note, ...(current.activity || [])],
    }));
    setTitle("");
    setBody("");
    setTab("activity");
  };

  return (
    <div className="dd-section dd-form-panel" style={{ paddingTop: 8 }}>
      <div className="dd-section-h">
        <span>Create note</span>
        <span className="count" style={{ color: "var(--text-3)" }}>{deal.company}</span>
      </div>

      <div className="deal-form">
        <label className="deal-field deal-field-full">
          <span>Note title</span>
          <input
            className="input"
            value={title}
            onChange={(e) => setTitle(e.target.value)}
            placeholder="Customer asked for rollout timeline"
          />
        </label>
        <label className="deal-field deal-field-full">
          <span>Note</span>
          <textarea
            className="input deal-textarea"
            value={body}
            onChange={(e) => setBody(e.target.value)}
            placeholder="Add the context you want saved to this deal."
          />
        </label>
      </div>

      <div className="deal-form-actions">
        <span className="deal-form-hint">Saves as an internal note in Activity.</span>
        <button type="button" className="btn ghost" onClick={() => setTab("overview")}>Cancel</button>
        <button type="button" className="btn accent" onClick={saveNote} disabled={!canSave}>
          <Icon name="note" size={14} /> Save note
        </button>
      </div>
    </div>
  );
}

function EditDealTab({ deal, onUpdateDeal, setTab }) {
  const [stage, setStage] = useState(deal.stage);
  const [amount, setAmount] = useState(String(deal.value || ""));
  const [closeDate, setCloseDate] = useState(closeDateToInput(deal.closeDate));
  const numericAmount = Number(amount);
  const canSave = Boolean(stage) && Number.isFinite(numericAmount) && numericAmount > 0 && Boolean(closeDate);

  useEffect(() => {
    setStage(deal.stage);
    setAmount(String(deal.value || ""));
    setCloseDate(closeDateToInput(deal.closeDate));
  }, [deal.id, deal.stage, deal.value, deal.closeDate]);

  const saveDeal = () => {
    if (!canSave) return;
    const formattedCloseDate = inputToCloseDate(closeDate);
    const amountLabel = `$${(numericAmount / 1000).toFixed(0)}K`;
    const updateNote = {
      time: formatActivityStamp(),
      kind: "note",
      actor: "Ridha",
      title: "Updated deal fields",
      body: `Stage set to ${stage}. Amount set to ${amountLabel}. Close date set to ${formattedCloseDate}.`,
      tag: "internal",
    };

    onUpdateDeal(deal.id, (current) => ({
      ...current,
      stage,
      value: numericAmount,
      arr: formatArrValue(numericAmount),
      closeDate: formattedCloseDate,
      activity: [updateNote, ...(current.activity || [])],
    }));
    setTab("overview");
  };

  return (
    <div className="dd-section dd-form-panel" style={{ paddingTop: 8 }}>
      <div className="dd-section-h">
        <span>Edit deal</span>
        <span className="count" style={{ color: "var(--text-3)" }}>{deal.dealName}</span>
      </div>

      <div className="deal-form">
        <label className="deal-field">
          <span>Stage</span>
          <select className="input deal-select" value={stage} onChange={(e) => setStage(e.target.value)}>
            {DEAL_STAGE_OPTIONS.map((option) => (
              <option key={option} value={option}>{option}</option>
            ))}
          </select>
        </label>
        <label className="deal-field">
          <span>Amount</span>
          <input
            className="input"
            type="number"
            min="1"
            step="1000"
            value={amount}
            onChange={(e) => setAmount(e.target.value)}
          />
        </label>
        <label className="deal-field">
          <span>Closing date</span>
          <input
            className="input"
            type="date"
            value={closeDate}
            onChange={(e) => setCloseDate(e.target.value)}
          />
        </label>
      </div>

      <div className="deal-form-summary">
        <span className="meta-chip-row">
          <span className="meta-chip">{stage}</span>
          <span className="meta-chip">{Number.isFinite(numericAmount) && numericAmount > 0 ? `$${numericAmount.toLocaleString()}` : "Invalid amount"}</span>
          <span className="meta-chip">{inputToCloseDate(closeDate) || "Select close date"}</span>
        </span>
      </div>

      <div className="deal-form-actions">
        <span className="deal-form-hint">Updates the deal card and logs a timeline note.</span>
        <button type="button" className="btn ghost" onClick={() => setTab("overview")}>Cancel</button>
        <button type="button" className="btn accent" onClick={saveDeal} disabled={!canSave}>
          <Icon name="check" size={14} /> Save changes
        </button>
      </div>
    </div>
  );
}

function OverviewTab({ deal, setTab }) {
  const gap = Math.abs(deal.gap || 0);
  const tone = gap >= 25 ? "warn" : gap >= 10 ? "amber" : "good";
  const ranked = useMemo(() => sortGapsByImpact(deal.nudges || [], deal), [deal]);
  const primary = ranked[0] || null;

  if (isClosedDetailDeal(deal)) return <ClosedDealOverview deal={deal} setTab={setTab} />;

  return (
    <div className="dd-rail-track">
      <DealDefenseCard
        deal={deal}
        setTab={setTab}
        primary={primary}
        gap={gap}
        tone={tone}
      />
    </div>
  );
}

function DealDefenseCard({ deal, setTab, primary, gap, tone }) {
  const [lens, setLens] = useState("truth");
  const [contextOpen, setContextOpen] = useState(false);
  const CategoryStrip = window.DealCategoryStrip;
  const draft = primary ? buildActionDraft(primary.n, deal) : null;
  const actionLabel = primary?.n.executeLabel || primary?.n.action || "Review buyer proof";

  const partition = useMemo(() => window.partitionLensActivity(deal), [deal.id]);
  const championRoles = useMemo(() => window.classifyChampions(deal), [deal.id]);

  const lead = computeLensLead(lens, deal, partition, championRoles, tone);

  useEffect(() => {
    trackNudgeEvent("deal_defense_viewed", {
      dealId: deal.id,
      primaryGap: primary?.n.kind || null,
    });
  }, [deal.id, primary?.n.kind]);

  const buyer = deal.buyerScore || 0;
  const seller = deal.sellerScore || 0;
  const gapAbs = Math.abs((deal.gap != null ? deal.gap : seller - buyer));
  const bspTone = gapAbs >= 25 ? "warn" : gapAbs >= 10 ? "amber" : "good";

  const lenses = [
    { k: "truth", label: "Full story" },
    { k: "seller", label: "Seller story" },
    { k: "buyer", label: "Buyer story" },
  ];
  const otherLenses = lenses.filter((l) => l.k !== lens);

  return (
    <div className={`dd-section deal-defense tone-${tone}`} data-tour="defense">
      <header className="deal-defense-head" data-tour="defense-categories">
        {CategoryStrip && <CategoryStrip deal={deal} compact className="deal-defense-cats" />}
      </header>

      {lead.title && (
        <div className="defense-lead">
          <h3 className="defense-lead-title">{lead.title}</h3>
          {lead.sub && <p className="defense-lead-sub">{lead.sub}</p>}
        </div>
      )}

      {lens === "truth" && primary && (
        <div className="deal-defense-action">
          <button type="button" className="coach-cta" data-tour="defense-cta" onClick={() => setTab("ai")}>
            <span className="coach-cta-text">{actionLabel}</span>
            {draft && (
              <span className="coach-cta-chip">{channelLabel(draft.channel)} · {draft.recipientName}</span>
            )}
            <span className="coach-cta-track" aria-hidden="true">
              <span className="coach-cta-dot" />
              <span className="coach-cta-dot" />
              <span className="coach-cta-dot" />
              <Icon name="arrow" size={14} className="coach-cta-arrow" />
            </span>
          </button>
        </div>
      )}

      {lens === "seller" && <SellerLensPanel partition={partition} setTab={setTab} />}
      {lens === "buyer" && <BuyerLensPanel partition={partition} setTab={setTab} />}

      <div className={`coach-prep ${contextOpen ? "is-open" : ""}`}>
        <button
          type="button"
          className="coach-prep-toggle"
          data-tour="defense-context"
          onClick={() => setContextOpen((v) => !v)}
          aria-expanded={contextOpen}
        >
          <Icon name="chevronR" size={12} className="coach-prep-caret" />
          <span>Deal context</span>
          <em>{gapAbs}pt gap</em>
        </button>
        <div className="coach-prep-body">
          <div className="coach-prep-inner">
            <div className="coach-prep-block">
              <span className="coach-mini-label">Buyer ↔ Seller alignment</span>
              <BuyerSellerProgress buyer={buyer} seller={seller} gap={gapAbs} tone={bspTone} />
            </div>
          </div>
        </div>
      </div>

      <footer className="deal-defense-lens-switch" data-tour="defense-lens-switch">
        <span className="deal-defense-lens-label">Switch view</span>
        {otherLenses.map((l) => (
          <button
            key={l.k}
            type="button"
            className="deal-defense-lens-link"
            onClick={() => setLens(l.k)}
          >
            {l.label}
          </button>
        ))}
      </footer>
    </div>
  );
}

// Compute the lead (Level 1 headline + Level 2 sub-line) for each lens.
// The lead answers the diagnostic question of the lens — not "here's some
// data" but "here's what's wrong / what's right".
function computeLensLead(lens, deal, partition, championRoles, dealTone) {
  if (lens === "truth") {
    const truthSummary = (deal.summary || "")
      .replace(/^\d+% (seller activity|buyer progression)\. /i, "")
      .split(/(?<=[.!?])\s+/)[0] || "";
    const buyer = deal.buyerScore || 0;
    const seller = deal.sellerScore || 0;
    const gap = Math.abs((deal.gap != null ? deal.gap : seller - buyer));
    return {
      title: truthSummary,
      sub: gap >= 10 ? `${gap}-point gap between buyer and seller activity` : null,
      tone: dealTone || null,
    };
  }
  if (lens === "seller") {
    const missing = partition.coveredPillars.filter((p) => !p.covered);
    const missingTop = missing.slice(0, 3).map((p) => p.label);
    const more = missing.length - missingTop.length;
    if (missing.length === 0) {
      return { title: "All MEDDPICC pillars evidenced", sub: "Seller motion is well-qualified", tone: "good" };
    }
    const tone = missing.length >= 3 ? "warn" : "amber";
    const title = `Missing ${missingTop.join(", ")}${more > 0 ? `, +${more} more` : ""}`;
    return {
      title,
      sub: `${8 - missing.length} of 8 pillars evidenced from seller activity`,
      tone,
    };
  }
  // buyer lens
  const ownedCount = partition.buyer.filter((it) => /agreed|confirmed|will|i'?ll|we'?ll|by /i.test(it.body || "")).length;
  const primary = championRoles.roles.find((r) => r.isPrimary);
  if (!primary) {
    return {
      title: "No primary champion identified",
      sub: `${championRoles.roles.length} stakeholders engaged · no one driving internally`,
      tone: "warn",
    };
  }
  if (ownedCount === 0) {
    return {
      title: "Buyer hasn't committed to a next step",
      sub: `${primary.name} · Primary champion${championRoles.expansion > 0 ? ` · ${championRoles.expansion} others engaged` : ""}`,
      tone: "warn",
    };
  }
  return {
    title: `${ownedCount} commitment${ownedCount === 1 ? "" : "s"} on the board`,
    sub: `${primary.name} · Primary champion${championRoles.expansion > 0 ? ` · ${championRoles.expansion} others engaged` : ""}`,
    tone: null,
  };
}

// ---------- Lens content panels ----------

function MeddpiccChips({ codes }) {
  if (!codes || codes.length === 0) {
    return <span className="meddpicc-chip is-empty">No pillar match</span>;
  }
  return (
    <span className="meddpicc-chip-row">
      {codes.map((code) => {
        const pillar = window.MEDDPICC_PILLARS.find((p) => p.code === code);
        return (
          <span key={code} className="meddpicc-chip" title={pillar?.label || code}>
            {code}
          </span>
        );
      })}
    </span>
  );
}

function MeddpiccCoverage({ pillars }) {
  return (
    <div className="pillar-row" aria-label="MEDDPICC coverage from seller activity">
      {pillars.map((p) => (
        <span
          key={p.code}
          className={`pillar-pill ${p.covered ? "is-covered" : "is-thin"}`}
          title={`${p.label} — ${p.covered ? "covered" : "thin"}`}
        >
          {p.covered
            ? <Icon name="check" size={10} />
            : <span className="pillar-dot" aria-hidden="true" />}
          {p.label}
        </span>
      ))}
    </div>
  );
}

function activityIconName(item) {
  if (item.kind === "addy") return "sparkles";
  if (item.kind === "email") return "mail";
  if (item.kind === "meeting") return "calendar";
  return "note";
}

function SellerLensPanel({ partition, setTab }) {
  const items = partition.seller.slice(0, 3);
  return (
    <div className="lens-panel lens-panel-seller">
      <div className="lens-panel-label">Recent seller moves</div>
      <ul className="lens-rows">
        {items.length === 0 && <li className="lens-rows-empty">No seller activity yet.</li>}
        {items.map((it, i) => (
          <li key={i} className="lens-row">
            <span className="lens-row-time">{it.time}</span>
            <span className="lens-row-title">{it.title}</span>
            <MeddpiccChips codes={it.meddpicc} />
          </li>
        ))}
      </ul>
      <button type="button" className="lens-link" onClick={() => setTab("activity")}>
        See full activity <Icon name="arrow" size={11} />
      </button>
    </div>
  );
}

function BuyerLensPanel({ partition, setTab }) {
  const items = partition.buyer.slice(0, 3);
  return (
    <div className="lens-panel lens-panel-buyer">
      <div className="lens-panel-label">Recent buyer moves</div>
      <ul className="lens-rows">
        {items.length === 0 && <li className="lens-rows-empty">Buyer hasn't moved yet.</li>}
        {items.map((it, i) => (
          <li key={i} className="lens-row">
            <span className="lens-row-time">{it.time}</span>
            <span className="lens-row-title">{it.title}</span>
            <span className="lens-row-meta">{it.commitment}</span>
          </li>
        ))}
      </ul>
      <button type="button" className="lens-link" onClick={() => setTab("stake")}>
        Open account map <Icon name="arrow" size={11} />
      </button>
    </div>
  );
}

// Shared champion roster — used by both the defense card buyer lens and
// the activity tab buyer commitments view. Filters out plain "supporter"
// rows and rolls them into a "+N supporters" footer to keep the grid lean.
function ChampionRoster({ championRoles, ownedCount }) {
  const featured = championRoles.roles.filter((s) => s.category !== "supporter");
  const supporters = championRoles.roles.filter((s) => s.category === "supporter");
  return (
    <>
      <div className="lens-panel-head">
        <span className="lens-panel-eyebrow">Stakeholders</span>
        <span className="lens-panel-meta">
          {typeof ownedCount === "number" && (
            <>
              {ownedCount > 0
                ? <>Buyer owns <b>{ownedCount}</b></>
                : <>No buyer commitments</>}
              <em>·</em>
            </>
          )}
          <b>{championRoles.expansion}</b> beyond champion
        </span>
      </div>
      <div className="champion-class-grid">
        {featured.length === 0 && supporters.length === 0 && (
          <div className="lens-activity-empty">No engaged stakeholders mapped yet.</div>
        )}
        {featured.map((s) => {
          const tone = window.CHAMPION_ROLE_TONE[s.category] || "muted";
          const label = window.CHAMPION_ROLE_LABEL[s.category] || "Supporter";
          return (
            <div key={s.name} className={`champion-class-card tone-${tone}`} data-role={s.category}>
              <div className="champion-class-head">
                <span className="avatar sm">{s.name.split(" ").map((n) => n[0]).join("")}</span>
                <div>
                  <div className="champion-class-name">{s.name}</div>
                  <div className="champion-class-role">{s.role}</div>
                </div>
                <span className={`champion-class-tag tone-${tone}`}>{label}</span>
              </div>
              <div className="champion-class-meters">
                <span>Power <b>{s.power}/5</b></span>
                <span>Support <b>{s.support}/5</b></span>
              </div>
              <div className="champion-class-move">{s.move}</div>
            </div>
          );
        })}
      </div>
      {supporters.length > 0 && (
        <div className="champion-supporters">
          <span className="champion-supporters-l">Supporters</span>
          {supporters.map((s) => (
            <span key={s.name} className="champion-supporters-chip" title={s.role}>
              {s.name}
            </span>
          ))}
        </div>
      )}
    </>
  );
}

function ClosedDealOverview({ deal, setTab }) {
  const diag = DEAL_DIAGNOSIS[deal.id] || {};
  const lost = isLostDetailDeal(deal);
  const label = lost ? "Loss analysis" : "Won analysis";
  const outcome = lost ? "Closed Lost" : "Closed Won";
  const nextAction = lost ? "Review loss pattern" : "Plan post-sale motion";

  return (
    <>
      <div className={`dd-section closed-analysis ${lost ? "is-lost" : "is-won"}`}>
        <div className="closed-analysis-kicker">
          <Icon name={lost ? "flag" : "check"} size={14} />
          {label}
        </div>
        <h2 className="hero-title" style={{ fontSize: 22, marginTop: 8 }}>
          {diag.headline || outcome}
        </h2>
        <div className="dd-prose" style={{ marginTop: 8 }}>
          {diag.why || deal.summary}
        </div>
        <div className="closed-analysis-actions">
          <button className="btn accent" onClick={() => setTab(lost ? "activity" : "meetings")}>
            {nextAction} <Icon name="arrow" size={14} />
          </button>
          <span className="meta-chip-row">
            <span className="meta-chip">{deal.closeDate}</span>
            <span className="meta-chip">${(deal.value / 1000).toFixed(0)}K</span>
          </span>
        </div>
      </div>

      <div className="dd-section">
        <div className="dd-section-h">
          <span>Outcome drivers</span>
        </div>
        <div className="closed-analysis-grid">
          <div>
            <span>What happened</span>
            <p>{diag.what || deal.summary}</p>
          </div>
          <div>
            <span>{lost ? "Pattern to avoid" : "Pattern to repeat"}</span>
            <p>{diag.fix || (lost ? "Bring finance validation forward before proposal." : "Convert momentum into QBR and reference motion.")}</p>
          </div>
          <div>
            <span>Addy context</span>
            <p>
              {lost
                ? "Addy should reason about loss reasons, playbook updates, and early warning signals."
                : "Addy should reason about handoff risk, expansion timing, and reference asks."}
            </p>
          </div>
        </div>
      </div>
    </>
  );
}

// ManagerOverview — the manager-lens take on the deal-room Overview.
// Same data, different lens: forecast probability gap, inflation in $, the
// coaching prompt + questions to bring to the rep. Powered by the global
// ForecastEngine (so the math here matches the recalc the manager already
// trusts elsewhere) and the manager-shape lookup exposed by ManagerView.
function ManagerOverview({ deal, setTab }) {
  const engine = window.ForecastEngine;
  const ProbabilityProgress = window.ProbabilityProgress;
  const recommendForRow = window.recommendForRow;
  const managerDeal = (typeof window.findManagerDeal === "function")
    ? window.findManagerDeal(deal.id)
    : null;

  // Run the engine on this single deal so we always have a fresh row even
  // for AE-shape inputs (e.g. when the manager jumps in mid-session).
  const row = useMemo(() => {
    if (!engine) return null;
    const model = engine.defaultModel();
    const summary = engine.summarize([deal], model);
    return summary?.rows?.[0] || null;
  }, [engine, deal]);

  if (!row) {
    return (
      <div className="dd-section">
        <p>Manager forecast lens isn't available — falling back to the deal narrative.</p>
        <OverviewTab deal={deal} setTab={setTab} />
      </div>
    );
  }

  const declaredPct = Math.round((row.declaredProbability || 0) * 100);
  const adjustedPct = Math.round((row.adjustedProbability || 0) * 100);
  const gap = Math.abs(declaredPct - adjustedPct);
  const tone = gap >= 20 ? "warn" : gap >= 10 ? "amber" : "good";
  const declaredFcst = Math.round((row.declaredAmount || 0) * (row.declaredProbability || 0));
  const adjustedFcst = row.nudgeAdjustedAmount || 0;
  const inflation = Math.max(0, declaredFcst - adjustedFcst);
  const fmtK = (n) => n >= 1000000 ? `$${(n / 1000000).toFixed(1)}M` : `$${Math.round(n / 1000)}K`;
  const reco = recommendForRow ? recommendForRow(row) : null;

  return (
    <>
      <div className="dd-section mgr-overview-hero" data-tour="forecast-read">
        <span className="coach-mini-label">Forecast lens · manager</span>
        <h2 className="hero-title" style={{ fontSize: 22, marginTop: 8 }}>
          {reco ? reco.label : "Forecast read"}
        </h2>
        <p className="dd-prose" style={{ marginTop: 8 }}>
          {reco ? reco.text : `Declared ${declaredPct}% vs evidence-adjusted ${adjustedPct}%. The gap is how much air is still in the call.`}
        </p>

        {ProbabilityProgress && (
          <div className="mgr-overview-bar">
            <ProbabilityProgress declared={declaredPct} adjusted={adjustedPct} tone={tone} />
          </div>
        )}

        <div className="mgr-overview-stats">
          <div>
            <span>Declared forecast</span>
            <b>{fmtK(declaredFcst)}</b>
            <em>amount × declared prob.</em>
          </div>
          <div>
            <span>Evidence-adjusted</span>
            <b>{fmtK(adjustedFcst)}</b>
            <em>after Nudge buffers</em>
          </div>
          <div className={`mgr-overview-gap tone-${tone}`}>
            <span>Inflation in the call</span>
            <b>{inflation > 0 ? `−${fmtK(inflation)}` : "—"}</b>
            <em>{inflation > 0 ? "lift if recalled to evidence" : "no inflation"}</em>
          </div>
        </div>

        {(row.declaredCategory && row.recommendedCategory && row.declaredCategory !== row.recommendedCategory) && (
          <div className="mgr-overview-cat-move">
            <span className="coach-mini-label">Category recommendation</span>
            <p>
              Move from <b>{row.declaredCategory}</b> to <b>{row.recommendedCategory}</b>.
            </p>
          </div>
        )}
      </div>

      {row.reasons && row.reasons.length > 0 && (
        <div className="dd-section">
          <div className="dd-section-h">
            <span>Why the evidence lowers it</span>
          </div>
          <ul className="mgr-reasons">
            {row.reasons.map((r, i) => <li key={i}>{r}</li>)}
          </ul>
        </div>
      )}

      {row.bufferReasons && row.bufferReasons.length > 0 && (
        <div className="dd-section">
          <div className="dd-section-h">
            <span>Buffers fired</span>
          </div>
          <div className="coach-chips">
            {row.bufferReasons.map((b, i) => (
              <span key={i} className="coach-chip tone-warn">{b}</span>
            ))}
          </div>
        </div>
      )}

      {managerDeal && (
        <div className="dd-section" data-tour="coaching-prompt">
          <div className="dd-section-h">
            <span>Coaching prompt · for {managerDeal.aeName}</span>
          </div>
          <h3 style={{ margin: "2px 0 4px", fontSize: 17, fontWeight: 650, color: "var(--text)" }}>
            {managerDeal.suggestedManagerAction || "Coach the rep on this deal"}
          </h3>
          {managerDeal.managerNextAction && (
            <p className="dd-prose">{managerDeal.managerNextAction}</p>
          )}
          {managerDeal.coachingQuestions && managerDeal.coachingQuestions.length > 0 && (
            <ul className="mgr-coaching-questions">
              {managerDeal.coachingQuestions.slice(0, 3).map((q, i) => (
                <li key={i}>{q}</li>
              ))}
            </ul>
          )}
          <button
            type="button"
            className="coach-cta"
            style={{ marginTop: 14 }}
            onClick={() => setTab("ai")}
          >
            <span className="coach-cta-text">Open coaching moves</span>
            <span className="coach-cta-track" aria-hidden="true">
              <span className="coach-cta-dot" />
              <span className="coach-cta-dot" />
              <span className="coach-cta-dot" />
              <Icon name="arrow" size={14} className="coach-cta-arrow" />
            </span>
          </button>
        </div>
      )}
    </>
  );
}

function AIActionsTab({ deal, executed, setExecuted, setTab }) {
  const [previewIdx, setPreviewIdx] = useState(null);
  const [openSecondaryId, setOpenSecondaryId] = useState(null);
  const ranked = useMemo(() => sortGapsByImpact(deal.nudges || [], deal), [deal]);
  const primary = ranked[0] || null;
  const secondary = ranked.slice(1);
  const primaryDraft = primary ? buildActionDraft(primary.n, deal) : null;
  const primaryId = primary ? `ai-${deal.id}-${primary.originalIndex}` : null;
  const primaryDone = primaryId ? executed[primaryId] : false;

  const onSend = (id, n, draft) => {
    if (window.NudgeBackend && n) {
      window.NudgeBackend.execute(deal, {
        kind: n.kind || "Recommended action",
        channel: draft?.channel || null,
        recipient: draft?.recipientName || null,
        action: n.executeLabel || n.action,
        subject: draft?.subject || null,
        body: draft?.body || null,
        outcome: n.outcome || null,
      });
    }
    setExecuted((s) => ({ ...s, [id]: true }));
    setPreviewIdx(null);
  };

  if (!primary) {
    return (
      <div className="dd-section" style={{ paddingTop: 8 }}>
        <div className="placeholder-stripe" style={{ minHeight: 180 }}>
          Buyer proof is complete for this stage. No next move needed right now.
        </div>
      </div>
    );
  }

  return (
    <div className="dd-rail-track ai-action-rail">
      <div className="dd-section is-primary ai-priority-action">
        <div className="hero-eyebrow" style={{ display: "flex", alignItems: "center", gap: 8, flexWrap: "wrap" }}>
          Highest impact gap · {primary.n.kind}
          <AgentChip nudge={primary.n} deal={deal} compact />
        </div>
        <div className="ai-gap-action">
          <div>
            <span>Gap</span>
            <b>{primary.n.title}</b>
            {primary.n.sub && <em>{primary.n.sub}</em>}
          </div>
          <div>
            <span>Corrective action</span>
            <b>{primary.n.executeLabel || primary.n.action}</b>
            <em>{channelLabel(primaryDraft.channel)} · {primaryDraft.recipientName}</em>
          </div>
        </div>
        <EvidenceQuote recommendation={primary.n} deal={deal} draft={primaryDraft} />
        <div className="dd-hero-actions">
          {primaryDone ? (
            <span className="chip good"><Icon name="check" size={12} /> Done</span>
          ) : (
            <button className="btn accent" data-tour="review-action" onClick={() => setPreviewIdx(primary.originalIndex)}>
              <Icon name="bolt" size={14} /> Review action
            </button>
          )}
          <button className="btn ghost" onClick={() => setTab("overview")}>
            Check buyer proof
          </button>
        </div>
      </div>

      {secondary.length > 0 && (
        <div className="dd-section" style={{ paddingTop: 16 }}>
          <div className="mtg-section-label">Other actions · {secondary.length}</div>
          <ol className="ai-act-list">
            {secondary.map(({ n, originalIndex }) => {
              const id = `ai-${deal.id}-${originalIndex}`;
              const isDone = executed[id];
              const draft = buildActionDraft(n, deal);
              const isFinanceLookup = n.automation?.type === "economic-buyer-lookup";
              const actionLabel = isFinanceLookup ? (n.executeLabel || "Run lookup") : (n.executeLabel || n.action);
              const isOpen = openSecondaryId === id;
              return (
                <li key={originalIndex} className={`ai-act-row ${isOpen ? "is-open" : ""}`}>
                  <div
                    role="button"
                    tabIndex={0}
                    aria-expanded={isOpen}
                    className="ai-act-row-head"
                    onClick={() => setOpenSecondaryId(isOpen ? null : id)}
                    onKeyDown={(e) => {
                      if (e.key === "Enter" || e.key === " ") {
                        e.preventDefault();
                        setOpenSecondaryId(isOpen ? null : id);
                      }
                    }}
                  >
                    <div className="ai-act-row-ic"><Icon name={channelIcon(draft.channel)} size={14} /></div>
                    <div className="ai-act-row-body">
                      <div className="ai-act-row-kind">
                        <span>{n.kind}</span>
                        <AgentChip nudge={n} deal={deal} compact />
                      </div>
                      <div className="ai-act-row-title">{n.title}</div>
                      <div className="ai-act-row-sub">{actionLabel} · {channelLabel(draft.channel)}</div>
                    </div>
                    <div className="ai-act-row-aside">
                      {isDone ? (
                        <span className="chip good"><Icon name="check" size={12} /> Done</span>
                      ) : (
                        <button
                          type="button"
                          className="btn sm accent"
                          onClick={(e) => { e.stopPropagation(); setPreviewIdx(originalIndex); }}
                        >
                          Review
                        </button>
                      )}
                      <Icon name="chevronR" size={12} className="ai-act-row-caret" />
                    </div>
                  </div>
                  {isOpen && (
                    <div className="ai-act-row-expand">
                      <div className="mtg-flat">
                        <div className="mtg-flat-block">
                          <span className="coach-mini-label">Evidence</span>
                          <EvidenceQuote recommendation={n} deal={deal} draft={draft} />
                        </div>
                      </div>
                    </div>
                  )}
                </li>
              );
            })}
          </ol>
        </div>
      )}

      {previewIdx !== null && (() => {
        const previewN = deal.nudges[previewIdx];
        const previewDraft = previewN ? buildActionDraft(previewN, deal) : null;
        return (
          <ActionPreview
            n={previewN}
            deal={deal}
            nudgeIdx={previewIdx}
            onClose={() => setPreviewIdx(null)}
            onSend={() => onSend(`ai-${deal.id}-${previewIdx}`, previewN, previewDraft)}
          />
        );
      })()}
    </div>
  );
}

function channelIcon(channel) {
  if (channel === "linkedin") return "users";
  if (channel === "calendar") return "calendar";
  if (channel === "whatsapp") return "chat";
  if (channel === "slack")    return "hash";
  if (channel === "phone")    return "phone";
  if (channel === "crm")      return "note";
  return "mail";
}
function channelLabel(channel) {
  if (channel === "linkedin") return "LinkedIn message";
  if (channel === "calendar") return "Calendar invite";
  if (channel === "whatsapp") return "WhatsApp message";
  if (channel === "slack")    return "Slack message";
  if (channel === "phone")    return "Phone talking points";
  if (channel === "crm")      return "CRM task";
  return "Email";
}
function channelSendLabel(channel) {
  if (channel === "calendar") return "Send invite";
  if (channel === "whatsapp" || channel === "slack" || channel === "linkedin") return "Send message";
  if (channel === "phone") return "Log call plan";
  if (channel === "crm")   return "Save CRM task";
  return "Send email";
}
// Map a Nudge channel string to the activity-item `kind` taxonomy
// (email/meeting/note/addy). Drives the kind filter pills and the per-item
// kind class. Channels arrive in mixed cases (e.g. "Email", "Slack DM"),
// so normalise before matching.
function channelToActivityKind(channel) {
  if (!channel) return "email";
  const c = String(channel).toLowerCase();
  if (c.includes("calendar") || c.includes("meeting") || c.includes("call") || c.includes("phone")) return "meeting";
  if (c.includes("note") || c.includes("crm")) return "note";
  return "email";
}

function initialsFor(name = "") {
  return name
    .split(/\s+/)
    .filter(Boolean)
    .slice(0, 2)
    .map((part) => part[0])
    .join("")
    .toUpperCase();
}

function automationRecipient(n) {
  const contact = n.automation?.foundContact;
  if (!contact) return null;
  return {
    name: contact.name,
    role: contact.role,
    email: contact.email,
    signal: contact.signal,
  };
}

// Build a realistic mock draft from the nudge + deal context. Cheap heuristic
// — picks channel by kind/action keywords, fills in fields from the
// stakeholder list, and writes a short body that reads like Addy wrote it.
// Accepts an optional channelOverride so the user-driven channel switch can
// reshape the draft (chat-style channels collapse to 1–2 sentences via
// adaptDraftToChannel).
function buildActionDraft(n, deal, channelOverride) {
  const text = `${n.kind} ${n.action}`.toLowerCase();
  const automatedContact = automationRecipient(n);
  const isLinkedIn = !automatedContact && /(intro request|surface decision|economic buyer|missing stakeholders)/.test(text);
  const isCalendar = /(qbr|close plan|deep-dive|workshop)/.test(text);
  const channel = channelOverride || (automatedContact ? "email" : isLinkedIn ? "linkedin" : isCalendar ? "calendar" : "email");

  const recipient =
    automatedContact ||
    (deal.stakeholders || []).find((s) => /cfo|chief financial/i.test(s.role) && /intro|economic|missing/i.test(text)) ||
    (deal.stakeholders || []).find((s) => /counsel|legal/i.test(s.role) && /redline|legal/i.test(text)) ||
    (deal.stakeholders || []).find((s) => !s.missing) ||
    { name: deal.champion, role: deal.championRole };

  const firstName = recipient.name.split(" ")[0];
  const senderFirst = "Ridha";
  const dealCo = deal.company;

  // Subject + body templates
  let subject;
  let body;

  if (n.automation?.type === "economic-buyer-lookup") {
    subject = `Budget validation for ${dealCo}`;
    body = `Hi ${firstName},\n\n${deal.champion} mentioned that finance will need to validate the business case before this moves forward. I wanted to introduce myself directly and share the short ROI view we've prepared from the discovery context.\n\nWould you be open to a 20-min walkthrough next week? I can keep it focused on budget impact, approval path, and where teams similar to ${dealCo} usually see payback.\n\nThanks,\n${senderFirst}`;
  } else if (n.automation?.type === "replacement-champion-lookup") {
    subject = `${dealCo} evaluation ownership`;
    body = `Hi ${firstName},\n\nI saw that ${deal.champion} has moved on from ${dealCo}, and I don't want to let the evaluation lose context if the operations priorities are still active.\n\nWe had been discussing the strategic partnership around operational visibility and rollout timing. Would you be the right person to pick this up, or should I route the summary to someone else on your team?\n\nI can send a short recap with the open questions, proposed next step, and finance items so you do not have to restart from scratch.\n\nThanks,\n${senderFirst}`;
  } else if (n.automation?.type === "call-mentioned-expansion-contact") {
    subject = `${dealCo} — Q3 expansion conversation`;
    body = `Hi ${firstName},\n\nMaya mentioned you during the contract-signing call as the right person to include for the Q3 expansion conversation. We are preparing the first QBR and a short expansion option set based on the rollout plan.\n\nWould it make sense to include you in the June QBR, or schedule a quick intro beforehand so we can understand the Sales Ops requirements directly?\n\nThanks,\n${senderFirst}`;
  } else if (/competitor|linnworks|displacement/.test(text)) {
    subject = `Atlas and Linnworks comparison`;
    body = `Hi ${firstName},\n\nI noticed the Linnworks warehouse automation post you shared and wanted to make the comparison easier while the Atlas team is weighing options.\n\nThe main difference I would call out is where Nudge helps: not just routing workflow, but surfacing buyer momentum, stakeholder gaps, and next-best actions before revenue risk shows up late in the cycle.\n\nI pulled a short side-by-side with the three places Atlas asked us to prove fit: procurement visibility, exception handling, and executive reporting. Worth 15 minutes this week to pressure-test it against the Linnworks path?\n\nThanks,\n${senderFirst}`;
  } else if (/redline/.test(text)) {
    subject = `Redline summary — ${dealCo}`;
    body = `Hi ${firstName},\n\nQuick recap of the open redlines so we can wrap legal review:\n\n• Indemnity cap — accepting your draft language at 1× annual fees.\n• Data residency — confirmed EU-only processing per your security review.\n• Renewal — flexible on the cancellation window; happy to mirror your standard.\n\nIf there's anything else outstanding, can you share by EOD Thursday so we can stay on the May 8 close target?\n\nThanks,\n${senderFirst}`;
  } else if (channel === "linkedin") {
    subject = "";
    body = `Hi ${firstName} — ${deal.champion} mentioned you're closest to the budget conversation on the ${dealCo} side. Would you be open to a 20-min chat next week so I can share how teams in similar shape have used us, and answer any ROI questions directly? Happy to share materials beforehand.`;
  } else if (channel === "calendar") {
    subject = `${dealCo} — close plan workshop`;
    body = `Hi ${firstName},\n\nProposing a 30-min working session to walk through the close plan together — security review is cleared and momentum is strong, so this is mostly to align on the last 2 milestones.\n\nAgenda:\n• Final stakeholder map (incl. signing authority)\n• Procurement + redline cadence\n• Internal kickoff timing once signed\n\nFriday 3pm PT or Monday 11am PT — let me know which works.\n\nThanks,\n${senderFirst}`;
  } else if (/discovery/.test(text)) {
    subject = `Quick follow-up after our discovery call — ${dealCo}`;
    body = `Hi ${firstName},\n\nThanks again for the discovery conversation last week — your priorities around revops alignment lined up almost exactly with what we've helped similar teams unlock.\n\nTo move this forward, would it be possible to bring in your CFO (or whoever holds the budget conversation) for a 20-min ROI walkthrough? I can prepare a tailored case based on what you shared.\n\nLet me know what works.\n\nThanks,\n${senderFirst}`;
  } else if (/expansion|qbr|reference/.test(text)) {
    subject = /reference/.test(text) ? `Quick ask — case study for ${dealCo}` : `${dealCo} QBR + Q3 expansion preview`;
    body = /reference/.test(text)
      ? `Hi ${firstName},\n\nNow that the contract is signed — congrats again on the rollout — I wanted to follow up on something you mentioned during pricing.\n\nWould you be open to co-authoring a short case study, or letting us list ${dealCo} as a reference logo? Happy to draft both and have you redline anything you don't love.\n\nWe'd also love to feature your team's results once you're 60 days in.\n\nThanks,\n${senderFirst}`
      : `Hi ${firstName},\n\nWith the rollout settling in, I'd love to schedule our first QBR for the week of June 9. I'll bring a 60-day adoption snapshot, the early ROI signals from the pilot teams, and the Q3 expansion options we discussed during pricing.\n\nDoes Tuesday or Thursday that week work?\n\nThanks,\n${senderFirst}`;
  } else {
    // Default — re-engagement / commitment / generic follow-up
    subject = `Following up on ${dealCo}`;
    body = `Hi ${firstName},\n\nIt's been a few days since our last exchange — wanted to check whether anything has shifted on your side, or if there are open questions I can help unblock.\n\nIf it's helpful, I can put 15 minutes on the calendar this week to walk through whatever is most useful (timeline, pricing, implementation).\n\nLet me know what works.\n\nThanks,\n${senderFirst}`;
  }

  const toAddress = (channel === "linkedin" || channel === "whatsapp" || channel === "slack" || channel === "phone" || channel === "crm")
    ? `${recipient.name} · ${recipient.role}`
    : channel === "calendar"
      ? `${recipient.name} <${addressFor(recipient.name, dealCo)}>`
      : `${recipient.name} <${recipient.email || addressFor(recipient.name, dealCo)}>`;

  let draft = {
    channel,
    to: toAddress,
    subject,
    body,
    recipientName: recipient.name,
    recipientRole: recipient.role,
    workflow: n.automation?.steps || null,
    workflowSummary: n.automation?.foundContact
      ? `${n.automation.foundContact.name} found via ${n.automation.source}.`
      : null,
    workflowSource: n.automation?.source || null,
    contactSignal: automatedContact?.signal || null,
  };

  // Adapt body length/tone for chat-style channels. Email + calendar keep
  // the long form because they need durable buyer proof.
  if (window.adaptDraftToChannel && (channel === "whatsapp" || channel === "slack" || channel === "linkedin" || channel === "phone" || channel === "crm")) {
    draft = window.adaptDraftToChannel(channel, draft, recipient);
  }
  return draft;
}

function addressFor(name, company) {
  const local = name.toLowerCase().split(" ").join(".");
  const domain = company.toLowerCase().replace(/[^a-z0-9]+/g, "") + ".com";
  return `${local}@${domain}`;
}

function ActionPreview({ n, deal, nudgeIdx, onClose, onSend }) {
  const previewRef = useRef(null);
  const titleId = `action-preview-title-${deal.id}-${(n.kind || "draft").toLowerCase().replace(/[^a-z0-9]+/g, "-")}`;
  useDialogFocus(previewRef, onClose);
  // Post-send "verifying" moment — keeps the modal on screen for ~1.4s
  // after click so the AE feels Nudge engage with the proof watch before
  // the modal closes. The parent's onSend is invoked on the trailing edge.
  const [sent, setSent] = useState(false);
  const handleSend = () => {
    if (sent) return;
    setSent(true);
    window.setTimeout(() => onSend(), 1400);
  };
  // Channel state lives in the modal so the user can switch in place and
  // see the draft adapt (length / tone). Resolver picks the default.
  const rec = useMemo(() => {
    if (!window.recommendChannel || !window.executionContextFor || nudgeIdx === undefined || nudgeIdx === null) return null;
    return window.recommendChannel(window.executionContextFor(deal, nudgeIdx));
  }, [deal.id, nudgeIdx]);
  const [channel, setChannel] = useState(null);
  useEffect(() => { setChannel(rec ? rec.primary : null); }, [rec && rec.primary]);
  const draft = useMemo(() => buildActionDraft(n, deal, channel || undefined), [n, deal, channel]);
  const channelOptions = rec ? [rec.primary, ...rec.alternatives] : [];

  return (
    <div className="ap-back" onClick={onClose}>
      <div
        ref={previewRef}
        className="ap"
        onClick={(e) => e.stopPropagation()}
        data-dialog-root="true"
        role="dialog"
        aria-modal="true"
        aria-labelledby={titleId}
        tabIndex={-1}
      >
        <header className="ap-head">
          <div className="ap-channel">
            <span className="ap-channel-ic"><Icon name={channelIcon(draft.channel)} size={14} /></span>
            <span className="ap-channel-label" id={titleId}>{draft.workflow ? "Nudge workflow" : `${channelLabel(draft.channel)} draft`}</span>
            <span className="ap-channel-sep" />
            <span className="ap-channel-meta">{n.kind}</span>
          </div>
          <button type="button" className="btn sm ghost" onClick={onClose} aria-label="Close preview">
            <Icon name="close" size={14} />
          </button>
        </header>

        {channelOptions.length > 1 && !draft.workflow && (
          <div className="ap-channel-switch" data-tour="channel-switch">
            <ChannelSwitch value={channel} options={channelOptions} onChange={setChannel} />
          </div>
        )}

        <div className="ap-fields" key={`fields-${draft.channel}`}>
          <div className="ap-field">
            <span className="ap-field-l">{draft.channel === "linkedin" || draft.channel === "whatsapp" || draft.channel === "slack" || draft.channel === "phone" || draft.channel === "crm" ? "Recipient" : "To"}</span>
            <span className="ap-field-v">{draft.to}</span>
          </div>
          {draft.subject && (
            <div className="ap-field">
              <span className="ap-field-l">Subject</span>
              <span className="ap-field-v">{draft.subject}</span>
            </div>
          )}
        </div>

        {draft.workflow && (
          <div className={`ap-found ${n.automation?.type === "economic-buyer-lookup" ? "is-finance" : ""}`}>
            <span className="ap-found-avatar">{initialsFor(draft.recipientName)}</span>
            <div className="ap-found-body">
              <div className="ap-found-kicker">{draft.workflowSource}</div>
              <div className="ap-found-name">{draft.recipientName}</div>
              <div className="ap-found-role">{draft.recipientRole}</div>
              {draft.contactSignal && <p>{draft.contactSignal}</p>}
            </div>
          </div>
        )}

        {draft.workflow && (
          <div className="ap-workflow" aria-label="Automation steps">
            {draft.workflow.map((step, i) => (
              <div key={i} className="ap-workflow-step">
                <span className="ap-workflow-num">{String(i + 1).padStart(2, "0")}</span>
                <span className="ap-workflow-copy">
                  <b>{step.label}</b>
                  <em>{step.detail}</em>
                </span>
              </div>
            ))}
          </div>
        )}

        <div className="ap-body" key={`body-${draft.channel}`}>
          {n.automation?.type === "economic-buyer-lookup" && (
            <div className="ap-buyer-brief">
              <div className="ap-buyer-brief-top">
                <span className="ap-buyer-brief-label">Use case</span>
                <span className="ap-buyer-brief-chip">Find the finance buyer</span>
              </div>
              <p>
                Addy is identifying the person who can validate budget and approve the business case, then
                enriching the contact path before the intro goes out.
              </p>
            </div>
          )}
          {draft.body.split("\n").map((line, i) =>
            line === "" ? <br key={i} /> : <p key={i}>{line}</p>
          )}
        </div>

        {sent && (
          <div className="ap-watching" role="status" aria-live="polite">
            <span className="ap-watching-pulse" aria-hidden="true">
              <span className="ap-watching-dot" />
              <span className="ap-watching-ring" />
            </span>
            <div className="ap-watching-text">
              <b>Nudge is watching for buyer proof.</b>
              <span>Reply, stakeholder mention, or a confirmed next step will land here.</span>
            </div>
          </div>
        )}

        <footer className="ap-foot">
          <span className="ap-foot-meta" data-tour="verify-line">
            <Icon name="sparkles" size={12} /> Drafted by Addy · Nudge will verify buyer proof after send
          </span>
          <div className="ap-foot-actions">
            <button type="button" className="btn ghost" onClick={onClose} disabled={sent}>Cancel</button>
            <button type="button" className="btn" disabled={sent}>Edit</button>
            <button type="button" className="btn accent" onClick={handleSend} disabled={sent}>
              {sent ? (
                <><Icon name="check" size={14} /> Sent</>
              ) : (
                <><Icon name="bolt" size={14} /> {channelSendLabel(draft.channel)}</>
              )}
            </button>
          </div>
        </footer>
      </div>
    </div>
  );
}

// One activity row — clickable to expand into a reading view of the full
// message body / debrief / note. Closed state shows title + actor + brief
// snippet; open state reveals subject (if any), the body in larger reading
// type, channel + recipient meta, and (for Nudge entries) impact details.
function ActivityRow({ it, isOpen, onToggle }) {
  const channelText = it.nudge && it.nudge.channel ? channelLabel(it.nudge.channel) : null;
  const recipientText = it.nudge && it.nudge.recipient ? it.nudge.recipient : null;
  return (
    <div className={`dd-act kind-${it.kind} side-${it.side} ${it.nudge ? "is-nudge" : ""} ${isOpen ? "is-open" : ""}`}>
      <button
        type="button"
        className="dd-act-head"
        onClick={onToggle}
        aria-expanded={isOpen}
      >
        <div className="ic">
          <Icon name={activityIconName(it)} size={14} />
        </div>
        <div className="body">
          <div className="t">{it.title}</div>
          <div className="s">{it.actor}</div>
          <p className="b">{it.body}</p>
          {it.nudge && (
            <div className="dd-act-nudge">
              {it._phase === "sent" && (
                <>
                  <span className="dd-act-badge badge-nudge-recommended">
                    <Icon name="sparkles" size={9} /> Recommended by Nudge
                  </span>
                  <span className="dd-act-badge badge-nudge-executed">
                    <Icon name="bolt" size={9} /> Executed via Nudge
                  </span>
                </>
              )}
              {it._phase === "replied" && (
                <>
                  <span className="dd-act-badge badge-nudge-verified">
                    <Icon name="check" size={9} /> Buyer-verified movement
                  </span>
                  <span className="dd-act-impact">
                    +{Math.abs(it.nudge.gapDelta)}pt buyer alignment · gap {it.nudge.gapBefore} → {it.nudge.gapAfter}
                  </span>
                </>
              )}
            </div>
          )}
        </div>
        <div className="dd-act-aside">
          <span className="when">{it.time}</span>
          {it.meddpicc.length > 0 && <MeddpiccChips codes={it.meddpicc} />}
          <Icon name="chevronR" size={12} className="dd-act-caret" />
        </div>
      </button>

      {isOpen && (
        <div className="dd-act-expand">
          <div className="dd-act-expand-meta">
            {channelText && (
              <span className="dd-act-meta-item">
                <span className="coach-mini-label">Channel</span> {channelText}
              </span>
            )}
            {recipientText && (
              <span className="dd-act-meta-item">
                <span className="coach-mini-label">{it._phase === "replied" ? "From" : "To"}</span> {recipientText}
              </span>
            )}
            <span className="dd-act-meta-item">
              <span className="coach-mini-label">Time</span> {it.time}
            </span>
          </div>
          {it.nudge && it._phase === "sent" && it.nudge.subject && (
            <div className="dd-act-expand-subject">{it.nudge.subject}</div>
          )}
          <div className="dd-act-expand-body">
            {it.nudge && it._phase === "sent" && it.nudge.body
              ? it.nudge.body
              : it.nudge && it._phase === "replied" && it.nudge.replyPreview
                ? it.nudge.replyPreview
                : it.body || "No content to display."}
          </div>
          {it.nudge && it._phase === "replied" && (
            <div className="dd-act-expand-impact">
              <span className="coach-mini-label">Impact on deal</span>
              <p>
                Buyer alignment lifted by <b>{Math.abs(it.nudge.gapDelta)} points</b> (gap {it.nudge.gapBefore} → {it.nudge.gapAfter}).
                {it.nudge.buyerBefore !== it.nudge.buyerAfter && (
                  <> Buyer score moved from <b>{it.nudge.buyerBefore}%</b> to <b>{it.nudge.buyerAfter}%</b>.</>
                )}
              </p>
            </div>
          )}
        </div>
      )}
    </div>
  );
}

function ActivityTab({ deal }) {
  const [view, setView] = useState("timeline");
  const [filter, setFilter] = useState("All");
  const [openId, setOpenId] = useState(null);
  const partition = useMemo(() => window.partitionLensActivity(deal), [deal.id]);
  const championRoles = useMemo(() => window.classifyChampions(deal), [deal.id]);

  const items = deal.activity || [];
  const seedItems = items.map((it) => ({
    ...it,
    side: window.activitySide(it, deal),
    meddpicc: window.getActivityMeddpicc(it, deal),
  }));
  // Pull Nudge-executed actions and synthesize activity entries. Each action
  // becomes a "sent" entry; when the buyer replies, a second "replied" entry
  // appears with the gap-closing impact attached. Nudge provenance carries
  // on the item as `nudge` so the renderer can attach badges + impact.
  const nudgeRecords = (window.NudgeBackend && window.NudgeBackend.getActions(deal.id)) || [];
  const nudgeItems = [];
  nudgeRecords.forEach((r) => {
    nudgeItems.push({
      kind: channelToActivityKind(r.channel),
      side: "seller",
      title: r.action,
      actor: "You · via Nudge",
      body: r.channel
        ? `Sent via ${channelLabel(r.channel)}${r.recipient ? ` to ${r.recipient}` : ""}`
        : "Sent",
      time: formatActivityStamp(r.sentAt),
      meddpicc: [],
      nudge: r,
      _phase: "sent",
      _at: r.sentAt instanceof Date ? r.sentAt.getTime() : Date.now(),
    });
    if (r.status === "replied") {
      nudgeItems.push({
        kind: channelToActivityKind(r.channel),
        side: "buyer",
        title: r.recipient ? `${r.recipient} replied` : "Buyer replied",
        actor: r.recipient || "Buyer",
        body: r.replyPreview || `Buyer engaged — gap closing by ${Math.abs(r.gapDelta)}pt.`,
        time: formatActivityStamp(r.repliedAt),
        meddpicc: [],
        nudge: r,
        _phase: "replied",
        _at: r.repliedAt instanceof Date ? r.repliedAt.getTime() : Date.now(),
      });
    }
  });
  // Newest first — nudge items go on top of the seed timeline. Seed items
  // keep their authored order beneath.
  nudgeItems.sort((a, b) => b._at - a._at);
  const enrichedItems = [...nudgeItems, ...seedItems];

  const types = ["All", "Email", "Meeting", "Note", "Addy"];
  const visibleItems = filter === "All"
    ? enrichedItems
    : filter === "Addy"
      ? enrichedItems.filter((it) => it.kind === "addy" || it.nudge)
      : enrichedItems.filter((it) => it.kind === filter.toLowerCase());

  return (
    <div className="dd-section" style={{ paddingTop: 8 }} data-tour="activity-tab">
      <div className="dd-section-h activity-section-h">
        <span>Activity</span>
        <div className="activity-lens-switch" role="tablist" aria-label="Activity view">
          {[
            { k: "timeline", label: "Timeline" },
            { k: "seller", label: "Seller" },
            { k: "buyer", label: "Buyer" },
          ].map((v) => (
            <button
              key={v.k}
              type="button"
              className={view === v.k ? "is-active" : ""}
              onClick={() => setView(v.k)}
              role="tab"
              aria-selected={view === v.k}
            >
              {v.label}
            </button>
          ))}
        </div>
      </div>

      {view === "timeline" && (
        <div className="activity-kind-filter" role="group" aria-label="Filter by kind">
          <span className="activity-kind-label">Filter</span>
          {types.map((t) => (
            <button
              key={t}
              type="button"
              className={`activity-kind-btn ${filter === t ? "is-active" : ""}`}
              onClick={() => setFilter(t)}
            >
              {t}
            </button>
          ))}
        </div>
      )}

      {view === "timeline" && (
        <>
          <div className="flush">
            {visibleItems.length === 0 ? (
              <div className="placeholder-stripe" style={{ minHeight: 160 }}>
                No {filter.toLowerCase()} activity yet.
              </div>
            ) : (
              visibleItems.map((it, i) => {
                const key = it.nudge ? `${it.nudge.id}-${it._phase}` : `seed-${i}`;
                const isOpen = openId === key;
                return (
                  <ActivityRow
                    key={key}
                    it={it}
                    isOpen={isOpen}
                    onToggle={() => setOpenId(isOpen ? null : key)}
                  />
                );
              })
            )}
          </div>
        </>
      )}

      {view === "seller" && <SellerActivityFull partition={partition} />}
      {view === "buyer" && <BuyerCommitmentsFull deal={deal} partition={partition} championRoles={championRoles} allItems={enrichedItems} />}
    </div>
  );
}

function SellerActivityFull({ partition }) {
  const items = partition.seller;
  const coveredCount = partition.coveredPillars.filter((p) => p.covered).length;
  return (
    <div className="lens-panel lens-panel-seller lens-panel-inline">
      <div className="lens-panel-head">
        <span className="lens-panel-eyebrow">MEDDPICC coverage</span>
        <span className="lens-panel-meta"><b>{coveredCount}</b>/8 pillars covered</span>
      </div>
      <MeddpiccCoverage pillars={partition.coveredPillars} />
      <ul className="lens-activity">
        {items.length === 0 && (
          <li className="lens-activity-empty">No seller activity logged for this deal.</li>
        )}
        {items.map((it, i) => (
          <li key={i} className={`lens-activity-row kind-${it.kind} side-${it.side}`}>
            <span className="lens-activity-ic"><Icon name={activityIconName(it)} size={13} /></span>
            <span className="lens-activity-body">
              <span className="lens-activity-title">{it.title}</span>
              <span className="lens-activity-sub">{it.actor} · {it.time}</span>
            </span>
            <MeddpiccChips codes={it.meddpicc} />
          </li>
        ))}
      </ul>
    </div>
  );
}

function BuyerCommitmentsFull({ deal, partition, championRoles, allItems }) {
  const items = partition.buyer;
  const followed = items
    .map((it) => ({ it, ft: window.findFollowThrough(it.commitment, it, allItems) }))
    .filter(({ ft }) => ft && ft.matched).length;
  const pending = items.length - followed;

  return (
    <div className="lens-panel lens-panel-buyer lens-panel-inline">
      <ChampionRoster championRoles={championRoles} />

      <div className="lens-panel-subhead">
        Commitments · <b>{followed}</b> followed through · <b>{pending}</b> awaiting proof
      </div>
      <ul className="lens-activity">
        {items.length === 0 && (
          <li className="lens-activity-empty">No buyer-side activity yet — they're not driving the process.</li>
        )}
        {items.map((it, i) => {
          const ft = window.findFollowThrough(it.commitment, it, allItems);
          return (
            <li key={i} className={`lens-activity-row kind-${it.kind} side-${it.side}`}>
              <span className="lens-activity-ic"><Icon name={activityIconName(it)} size={13} /></span>
              <span className="lens-activity-body">
                <span className="lens-activity-title">{it.title}</span>
                <span className="lens-activity-sub">{it.actor} · {it.time}</span>
                <span className="buyer-commitment-text">"{it.commitment}"</span>
              </span>
              <FollowThroughBadge ft={ft} />
            </li>
          );
        })}
      </ul>
    </div>
  );
}

function FollowThroughBadge({ ft }) {
  if (!ft) {
    return (
      <span className="follow-badge tone-muted">
        <Icon name="note" size={11} />
        <span className="follow-badge-l">No commitment</span>
      </span>
    );
  }
  if (ft.matched) {
    return (
      <span className="follow-badge tone-good" title={`Matched topic "${ft.topic}" in ${ft.item.title}`}>
        <Icon name="check" size={11} />
        <span className="follow-badge-l">
          <b>Followed through</b>
          <em>{ft.item.title} · {ft.item.time}</em>
        </span>
      </span>
    );
  }
  return (
    <span className="follow-badge tone-warn" title={`No follow-up activity for: ${ft.topics.join(", ")}`}>
      <Icon name="flag" size={11} />
      <span className="follow-badge-l">
        <b>Awaiting proof</b>
        <em>No follow-up on {ft.topics.slice(0, 2).join(", ")}</em>
      </span>
    </span>
  );
}

// Quadrant classification: power × support. Used by both the map (background
// zone tint) and the roster (per-card quadrant tag + suggested move).
const QUADRANTS = {
  champion:  { label: "Champion",  tone: "good",   move: "Maintain — keep them in the loop" },
  blocker:   { label: "Blocker",   tone: "warn",   move: "Address — surface concerns directly" },
  ally:      { label: "Ally",      tone: "info",   move: "Amplify — request an intro upward" },
  bystander: { label: "Bystander", tone: "muted",  move: "Re-evaluate — confirm relevance" },
};
function quadrantOf(s) {
  if (s.power >= 3 && s.support >= 3) return "champion";
  if (s.power >= 3 && s.support < 3)  return "blocker";
  if (s.power < 3  && s.support >= 3) return "ally";
  return "bystander";
}

// Account map — 2D scatter of stakeholders across power × support. Quadrant
// labels are corner-tagged (subtle) so they never collide with nodes. Below
// the map: a roster of richer cards (quadrant + suggested next move).
function StakeholderTab({ deal }) {
  const [hoverId, setHoverId] = useState(null);
  const [openId, setOpenId] = useState(null);

  const placed = deal.stakeholders.map((s) => {
    // Power = Y (top is high), Support = X (right is high).
    // Inset 10% so nodes near edges don't crowd the corner labels.
    const x = 10 + (Math.max(0, Math.min(5, s.support)) / 5) * 80;
    const y = 10 + ((5 - Math.max(0, Math.min(5, s.power))) / 5) * 80;
    return { ...s, x, y, quadrant: quadrantOf(s) };
  });
  const engaged = deal.stakeholders.filter((s) => !s.missing);
  const missing = deal.stakeholders.filter((s) => s.missing);

  return (
    <div className="dd-section" style={{ paddingTop: 8 }}>
      <div className="dd-section-h">
        <span>Account map</span>
        <span className="count" style={{ color: "var(--text-3)" }}>
          {engaged.length} engaged · {missing.length} missing
        </span>
      </div>

      <div className="acct-map">
        <div className="acct-map-grid" aria-hidden="true">
          <div className="acct-zone acct-z-tl" />
          <div className="acct-zone acct-z-tr" />
          <div className="acct-zone acct-z-bl" />
          <div className="acct-zone acct-z-br" />
          <div className="acct-axis acct-axis-y" />
          <div className="acct-axis acct-axis-x" />
          <div className="acct-quad acct-q-tl">Blockers</div>
          <div className="acct-quad acct-q-tr">Champions</div>
          <div className="acct-quad acct-q-bl">Bystanders</div>
          <div className="acct-quad acct-q-br">Allies</div>
          <div className="acct-axis-label acct-axis-label-y">
            <span className="hi">Power</span>
          </div>
          <div className="acct-axis-label acct-axis-label-x">
            <span className="hi">Support →</span>
          </div>
        </div>

        {placed.map((s) => {
          const initials = s.name.split(" ").map((n) => n[0]).join("");
          const id = s.name;
          const isHover = hoverId === id;
          return (
            <button
              key={id}
              type="button"
              className={`acct-node ${s.missing ? "is-missing" : ""} ${isHover ? "is-hover" : ""}`}
              data-quadrant={s.quadrant}
              style={{ left: `${s.x}%`, top: `${s.y}%` }}
              onMouseEnter={() => setHoverId(id)}
              onMouseLeave={() => setHoverId(null)}
              onFocus={() => setHoverId(id)}
              onBlur={() => setHoverId(null)}
              aria-label={`${s.name}, ${s.role}`}
            >
              <span className="acct-node-dot">{initials}</span>
              <span className="acct-node-name">{s.name.split(" ")[0]}</span>
              <span className="acct-node-tip">
                <b>{s.name}</b>
                <span>{s.role}</span>
                <em>
                  {s.missing
                    ? "Not engaged · reach out to bring them in"
                    : `Power ${s.power}/5 · Support ${s.support}/5`}
                </em>
              </span>
            </button>
          );
        })}
      </div>

      <div className="mtg-section-label">Roster · {deal.stakeholders.length}</div>
      <ol className="stk-list">
        {[...deal.stakeholders].sort((a, b) => (b.power * 2 + b.support) - (a.power * 2 + a.support)).map((s) => {
          const initials = s.name.split(" ").map((n) => n[0]).join("");
          const q = quadrantOf(s);
          const meta = QUADRANTS[q];
          const move = s.missing
            ? "Reach out — bring them into the deal"
            : meta.move;
          const isOpen = openId === s.name;
          return (
            <li
              key={s.name}
              className={`stk-row tone-${meta.tone} ${s.missing ? "is-missing" : ""} ${isOpen ? "is-open" : ""}`}
              data-quadrant={q}
            >
              <button
                type="button"
                className="stk-row-head"
                onClick={() => setOpenId(isOpen ? null : s.name)}
                aria-expanded={isOpen}
              >
                <span className="avatar sm">{initials}</span>
                <span className="stk-row-id">
                  <span className="stk-row-name">{s.name}</span>
                  <span className="stk-row-role">{s.role}</span>
                </span>
                <span className={`stk-row-tag tone-${meta.tone}`}>
                  {s.missing ? "Missing" : meta.label}
                </span>
                <Icon name="chevronR" size={12} className="stk-row-caret" />
              </button>
              {isOpen && (
                <div className="stk-row-body">
                  <div className="mtg-flat">
                    {!s.missing && (
                      <>
                        <div className="mtg-flat-block">
                          <span className="coach-mini-label">Power</span>
                          <div className="stk-meter">
                            <span className="stk-meter-track" aria-hidden="true">
                              <span className="stk-meter-fill" style={{ width: `${(s.power / 5) * 100}%` }} />
                            </span>
                            <span className="stk-meter-v">{s.power}<em>/5</em></span>
                          </div>
                        </div>
                        <div className="mtg-flat-block">
                          <span className="coach-mini-label">Support</span>
                          <div className="stk-meter">
                            <span className="stk-meter-track" aria-hidden="true">
                              <span className="stk-meter-fill" style={{ width: `${(s.support / 5) * 100}%` }} />
                            </span>
                            <span className="stk-meter-v">{s.support}<em>/5</em></span>
                          </div>
                        </div>
                      </>
                    )}
                    <div className="mtg-flat-block">
                      <span className="coach-mini-label">Next move</span>
                      <p>{move}</p>
                    </div>
                  </div>
                </div>
              )}
            </li>
          );
        })}
      </ol>
    </div>
  );
}

function MeetingsTab({ deal }) {
  // Use seeded meetings if present; otherwise derive from activity entries.
  const meetings = deal.meetings && deal.meetings.length
    ? deal.meetings
    : (deal.activity || [])
        .filter((a) => a.kind === "meeting")
        .map((a, i) => ({
          id: `${deal.id}-mtg-${i}`,
          title: a.title,
          date: a.time,
          isPast: true,
          attendees: (a.actor || "").split(/\s*\+\s*/),
          debrief: {
            sentiment: "—",
            summary: a.body || "",
            transcriptExcerpt: "Transcript not yet available for this meeting.",
            videoUrl: "#",
            nextSteps: [],
            addyNotes: [],
          },
        }));

  const upcoming = meetings.filter((m) => !m.isPast);
  const past = meetings.filter((m) => m.isPast);
  const [openId, setOpenId] = useState(upcoming[0]?.id || past[0]?.id || null);

  if (meetings.length === 0) {
    return (
      <div className="dd-section" style={{ paddingTop: 8 }}>
        <div className="dd-section-h"><span>Meetings</span></div>
        <div className="placeholder-stripe" style={{ minHeight: 200 }}>
          No meetings logged yet for this deal.
        </div>
      </div>
    );
  }

  return (
    <div className="dd-section" style={{ paddingTop: 8 }}>
      <div className="dd-section-h">
        <span>Meetings</span>
        <span className="count" style={{ color: "var(--text-3)" }}>
          {upcoming.length} upcoming · {past.length} past
        </span>
      </div>

      {upcoming.length > 0 && (
        <>
          <div className="mtg-section-label">Upcoming · {upcoming.length}</div>
          <ol className="mtg-list">
            {upcoming.map((m) => (
              <MeetingRow
                key={m.id}
                meeting={m}
                isOpen={openId === m.id}
                onToggle={() => setOpenId(openId === m.id ? null : m.id)}
              />
            ))}
          </ol>
        </>
      )}

      {past.length > 0 && (
        <>
          <div className="mtg-section-label">Past · {past.length}</div>
          <ol className="mtg-list">
            {past.map((m) => (
              <MeetingRow
                key={m.id}
                meeting={m}
                isOpen={openId === m.id}
                onToggle={() => setOpenId(openId === m.id ? null : m.id)}
              />
            ))}
          </ol>
        </>
      )}
    </div>
  );
}

function MeetingRow({ meeting, isOpen, onToggle }) {
  const initials = (name) => name.split(" ").map((n) => n[0]).join("");
  const shown = meeting.attendees.slice(0, 3);
  const extra = meeting.attendees.length - shown.length;
  return (
    <li className={`mtg-row ${meeting.isPast ? "is-past" : "is-upcoming"} ${isOpen ? "is-open" : ""}`}>
      <button type="button" className="mtg-row-head" onClick={onToggle} aria-expanded={isOpen}>
        <span className="mtg-row-when">
          {meeting.date}
          {meeting.duration && <em> · {meeting.duration}</em>}
        </span>
        <span className="mtg-row-title">{meeting.title}</span>
        <span className="mtg-row-att">
          {shown.map((a) => (
            <span key={a} className="mtg-row-avatar" title={a}>{initials(a)}</span>
          ))}
          {extra > 0 && (
            <span className="mtg-row-avatar is-more" title={meeting.attendees.slice(3).join(", ")}>
              +{extra}
            </span>
          )}
        </span>
        <Icon name="chevronR" size={12} className="mtg-row-caret" />
      </button>

      {isOpen && (
        <div className="mtg-row-body">
          {meeting.isPast ? <MeetingDebrief m={meeting} /> : <MeetingIntel m={meeting} />}
        </div>
      )}
    </li>
  );
}

// Flat block recipe shared by intel + debrief — no `*-detail-grid` /
// `*-block` wrappers, just stacked named sections. Same `coach-mini-label`
// the Deal context expand uses, so the visual language is consistent.
function MeetingBlock({ label, children }) {
  return (
    <div className="mtg-flat-block">
      <span className="coach-mini-label">{label}</span>
      {children}
    </div>
  );
}

function MeetingIntel({ m }) {
  const intel = m.intel || {};
  return (
    <div className="mtg-flat">
      {m.goal && <MeetingBlock label="Goal"><p>{m.goal}</p></MeetingBlock>}
      {intel.pulse && <MeetingBlock label="Buyer pulse"><p>{intel.pulse}</p></MeetingBlock>}
      {intel.risks && intel.risks.length > 0 && (
        <MeetingBlock label="Risks to surface">
          <ul className="mtg-bullets">
            {intel.risks.map((r, i) => <li key={i}>{r}</li>)}
          </ul>
        </MeetingBlock>
      )}
      {intel.talkingPoints && intel.talkingPoints.length > 0 && (
        <MeetingBlock label="Suggested talking points">
          <ul className="mtg-bullets">
            {intel.talkingPoints.map((t, i) => <li key={i}>{t}</li>)}
          </ul>
        </MeetingBlock>
      )}
      {intel.docs && intel.docs.length > 0 && (
        <MeetingBlock label="Docs to bring">
          <div className="mtg-chips">
            {intel.docs.map((d, i) => (
              <span key={i} className="mtg-chip"><Icon name="paperclip" size={11} /> {d}</span>
            ))}
          </div>
        </MeetingBlock>
      )}
      <div className="mtg-flat-actions">
        <button className="btn sm accent"><Icon name="sparkles" size={12} /> Generate prep brief</button>
        <button className="btn sm ghost"><Icon name="calendar" size={12} /> Open in calendar</button>
      </div>
    </div>
  );
}

function MeetingDebrief({ m }) {
  const d = m.debrief || {};
  return (
    <div className="mtg-flat">
      <div className="mtg-flat-recording">
        <a className="mtg-recording-link" href={d.videoUrl || "#"} onClick={(e) => e.preventDefault()}>
          <span className="mtg-recording-thumb" aria-hidden="true">▶</span>
          <span className="mtg-recording-meta">
            <b>Recording</b>
            <span>{m.duration ? `${m.duration} · auto-transcribed` : "auto-transcribed"}</span>
          </span>
        </a>
        {d.sentiment && (
          <span className="mtg-flat-sentiment">
            <span className="coach-mini-label">Sentiment</span>
            <span className="mtg-flat-sentiment-v">{d.sentiment}</span>
          </span>
        )}
      </div>
      {d.summary && <MeetingBlock label="Summary"><p>{d.summary}</p></MeetingBlock>}
      {d.nextSteps && d.nextSteps.length > 0 && (
        <MeetingBlock label="Next steps">
          <ul className="mtg-bullets">
            {d.nextSteps.map((s, i) => <li key={i}>{s}</li>)}
          </ul>
        </MeetingBlock>
      )}
      {d.addyNotes && d.addyNotes.length > 0 && (
        <MeetingBlock label={<><Icon name="sparkles" size={11} /> Addy noticed</>}>
          <ul className="mtg-bullets">
            {d.addyNotes.map((n, i) => <li key={i}>{n}</li>)}
          </ul>
        </MeetingBlock>
      )}
      {d.transcriptExcerpt && (
        <MeetingBlock label="Transcript excerpt">
          <blockquote className="mtg-transcript">"{d.transcriptExcerpt}"</blockquote>
        </MeetingBlock>
      )}
      <div className="mtg-flat-actions">
        <button className="btn sm accent"><Icon name="bolt" size={12} /> Convert next steps to actions</button>
        <button className="btn sm ghost"><Icon name="mail" size={12} /> Email recap to attendees</button>
      </div>
    </div>
  );
}

function ResourcesTab() {
  return (
    <div className="dd-section" style={{ paddingTop: 8 }}>
      <div className="dd-section-h"><span>Resources</span></div>
      <div className="placeholder-stripe" style={{ minHeight: 240 }}>Resources — proposal docs, redlines, decks</div>
    </div>
  );
}

window.DealDetail = DealDetail;
