/* global React, ReactDOM, Icon, Sparkline, AlignmentChart, Brand, BuyerSellerProgress, SEED_DEALS, DEAL_DIAGNOSIS, computeDealPipelineImpact, sortDealsByPipelineImpact, formatImpactDollars, getDealDefense, isBuyerUnverifiedDeal, trackNudgeEvent */

const STAGE_ORDER = ["Discovery", "Qualification", "Proposal", "Negotiation", "Closed"];
const STAGE_MATCH = (deal, stage) =>
  deal.stage === stage || (stage === "Closed" && isClosedDeal(deal));
const isClosedDeal = (deal) => /^closed/i.test(deal.stage || "") || /^closed/i.test(deal.statusLabel || "");
const isLostDeal = (deal) => /lost/i.test(`${deal.stage || ""} ${deal.statusLabel || ""} ${deal.status || ""}`);

function closedDealAnalysis(deal) {
  const diag = DEAL_DIAGNOSIS[deal.id] || {};
  const won = !isLostDeal(deal);
  if (won) {
    return {
      tone: "won",
      label: "Won analysis",
      headline: diag.headline || "Closed won",
      primary: diag.what || "Won through aligned executive sponsorship and clean procurement.",
      secondary: diag.fix || "Convert into QBR, reference, or expansion motion.",
      icon: "check",
    };
  }
  return {
    tone: "lost",
    label: "Loss analysis",
    headline: diag.headline || "Closed lost",
    primary: diag.what || "Lost after buyer momentum fell below seller activity.",
    secondary: diag.fix || "Capture loss reason and update the playbook.",
    icon: "flag",
  };
}

const DEAL_TRUTH_LENSES = [
  { k: "truth", label: "Deal Truth", icon: "target" },
  { k: "seller", label: "Seller Activity", icon: "bolt" },
  { k: "buyer", label: "Buyer Progression", icon: "users" },
];

const GAP_CATEGORIES = [
  { k: "champion-drift", label: "Champion drift", icon: "userx", short: "Champion", hint: "The internal owner is quiet, left, or looking elsewhere." },
  { k: "stakeholder-missing", label: "Missing stakeholder", icon: "users", short: "Stakeholder", hint: "A buyer, blocker, or signer is not in the deal." },
  { k: "process-blocker", label: "Process blocker", icon: "scale", short: "Process", hint: "Legal, security, procurement, or approval flow is stuck." },
  { k: "value-proof", label: "Value proof gap", icon: "target", short: "Value proof", hint: "The buyer has not validated ROI, urgency, or success proof." },
  { k: "timing-risk", label: "Timing risk", icon: "clock", short: "Timing", hint: "The buyer timeline is slipping or unconfirmed." },
];

const ACTION_CATEGORIES = [
  { k: "re-engage", label: "Re-engage", icon: "chat", short: "Re-engage", hint: "Restart the buyer conversation with relevant context." },
  { k: "map-stakeholders", label: "Map stakeholders", icon: "route", short: "Map people", hint: "Find the missing champion, blocker, or economic buyer." },
  { k: "unblock-process", label: "Unblock process", icon: "scale", short: "Unblock", hint: "Move legal, security, procurement, or approvals forward." },
  { k: "prove-value", label: "Prove value", icon: "trend", short: "Prove value", hint: "Send proof, ROI, or a business-case asset." },
  { k: "close-expand", label: "Close or expand", icon: "handshake", short: "Close/expand", hint: "Convert momentum into commit, QBR, or expansion." },
];

const GAP_CATEGORY_BY_DEAL = {
  atlas: "champion-drift",
  global: "champion-drift",
  techstart: "process-blocker",
  acme: "timing-risk",
  startupco: "stakeholder-missing",
  horizon: "value-proof",
  northstar: "value-proof",
  aurora: "stakeholder-missing",
  pacific: "champion-drift",
  rooted: "stakeholder-missing",
  bluefish: "timing-risk",
  ironbridge: "process-blocker",
};

const ACTION_CATEGORY_BY_DEAL = {
  atlas: "re-engage",
  global: "map-stakeholders",
  techstart: "unblock-process",
  acme: "close-expand",
  startupco: "map-stakeholders",
  horizon: "close-expand",
  northstar: "prove-value",
  aurora: "map-stakeholders",
  pacific: "re-engage",
  rooted: "prove-value",
  bluefish: "re-engage",
  ironbridge: "unblock-process",
};

const DEAL_SORT_OPTIONS = [
  { k: "impact", label: "Impact" },
  { k: "value", label: "Value" },
  { k: "close", label: "Close date" },
  { k: "company", label: "Company" },
];

function sortDealsForView(deals, sortBy = "impact") {
  const list = [...deals];
  if (sortBy === "value") return list.sort((a, b) => (b.value || 0) - (a.value || 0));
  if (sortBy === "close") {
    return list.sort((a, b) => {
      const at = a.closeDate ? new Date(a.closeDate).getTime() : Number.POSITIVE_INFINITY;
      const bt = b.closeDate ? new Date(b.closeDate).getTime() : Number.POSITIVE_INFINITY;
      return (Number.isNaN(at) ? Number.POSITIVE_INFINITY : at) - (Number.isNaN(bt) ? Number.POSITIVE_INFINITY : bt);
    });
  }
  if (sortBy === "company") return list.sort((a, b) => String(a.company || "").localeCompare(String(b.company || "")));
  return sortDealsByPipelineImpact(list);
}

function findCategory(items, key, fallbackKey) {
  return items.find((item) => item.k === key) || items.find((item) => item.k === fallbackKey) || items[0];
}

function dealText(deal) {
  const nudgeText = (deal.nudges || [])
    .map((nudge) => `${nudge.kind || ""} ${nudge.title || ""} ${nudge.sub || ""} ${nudge.action || ""}`)
    .join(" ");
  return `${deal.summary || ""} ${deal.stage || ""} ${deal.statusLabel || ""} ${nudgeText}`.toLowerCase();
}

function inferGapCategoryKey(deal) {
  if (GAP_CATEGORY_BY_DEAL[deal.id]) return GAP_CATEGORY_BY_DEAL[deal.id];
  const text = dealText(deal);
  if (/champion|quiet|silent|left|changed job|repost|competitor/.test(text)) return "champion-drift";
  if (/cfo|economic buyer|decision-maker|stakeholder|signer|owner|finance/.test(text)) return "stakeholder-missing";
  if (/legal|redline|security|procurement|compliance|approval/.test(text)) return "process-blocker";
  if (/close|date|timeline|timing|slip|scheduled/.test(text)) return "timing-risk";
  return "value-proof";
}

function inferActionCategoryKey(deal) {
  if (ACTION_CATEGORY_BY_DEAL[deal.id]) return ACTION_CATEGORY_BY_DEAL[deal.id];
  const text = dealText(deal);
  if (/identify|lookup|find|intro|stakeholder|cfo|economic buyer|decision-maker/.test(text)) return "map-stakeholders";
  if (/legal|redline|security|procurement|compliance|approval|unblock/.test(text)) return "unblock-process";
  if (/commit|close|qbr|expansion|reference/.test(text)) return "close-expand";
  if (/proof|roi|business case|value|case study/.test(text)) return "prove-value";
  return "re-engage";
}

function getDealsGapCategory(deal) {
  return findCategory(GAP_CATEGORIES, inferGapCategoryKey(deal), "value-proof");
}

function getDealsActionCategory(deal) {
  return findCategory(ACTION_CATEGORIES, inferActionCategoryKey(deal), "re-engage");
}

function DealsCategoryChip({ category, type, compact = false }) {
  if (!category) return null;
  const prefix = type === "gap" ? "Gap" : "Action";
  return (
    <span
      className={`deal-cat-chip deal-cat-${type} ${compact ? "is-compact" : ""}`}
      title={category.label}
      aria-label={`${prefix}: ${category.label}. ${category.hint}`}
    >
      <Icon name={category.icon} size={compact ? 12 : 13} />
      <span className="deal-cat-label" aria-hidden="true">{category.label}</span>
    </span>
  );
}

function DealsCategoryStrip({ deal, gapCategory, actionCategory, compact = false, className = "" }) {
  const gap = gapCategory || (deal ? getDealsGapCategory(deal) : null);
  const action = actionCategory || (deal ? getDealsActionCategory(deal) : null);
  if (!gap && !action) return null;
  return (
    <div className={`deal-cat-strip ${compact ? "is-compact" : ""} ${className}`} aria-label="Deal categories">
      {gap && <DealsCategoryChip category={gap} type="gap" compact={compact} />}
      {action && <DealsCategoryChip category={action} type="action" compact={compact} />}
    </div>
  );
}

function lensLabel(lens) {
  if (lens === "seller") return "Seller Activity";
  if (lens === "buyer") return "Buyer Progression";
  return "Deal Truth";
}

function lensShortLabel(lens) {
  if (lens === "seller") return "Seller";
  if (lens === "buyer") return "Buyer";
  return "Truth";
}

// Canonical lens metadata — icon, full label, short label. Anywhere a lens
// is shown in the UI it goes through <LensBadge> so the visual is the same.
const LENS_META = {
  truth:  { label: "Deal Truth",        short: "Truth",  icon: "target" },
  seller: { label: "Seller Activity",   short: "Seller", icon: "bolt"   },
  buyer:  { label: "Buyer Progression", short: "Buyer",  icon: "users"  },
};

function LensBadge({ lens, size = "md", short = false, interactive = false, active = false, onClick, role, ariaSelected }) {
  const meta = LENS_META[lens] || LENS_META.truth;
  const label = short ? meta.short : meta.label;
  const iconSize = size === "sm" ? 11 : 12;
  const className = `lens-badge lens-badge-${size} lens-badge-${lens}${active ? " is-active" : ""}`;
  const inner = (
    <>
      <Icon name={meta.icon} size={iconSize} className="lens-badge-icon" />
      <span>{label}</span>
    </>
  );
  if (interactive) {
    return (
      <button
        type="button"
        className={className}
        onClick={onClick}
        role={role}
        aria-selected={ariaSelected}
        aria-pressed={!role ? active : undefined}
      >
        {inner}
      </button>
    );
  }
  return <span className={className}>{inner}</span>;
}

function lensMetricValue(story) {
  return Number.parseInt(String(story.metric || "0"), 10) || 0;
}

function dealTruthTone(deal) {
  const sellerLead = (deal.sellerScore || 0) - (deal.buyerScore || 0);
  const buyerLead = (deal.buyerScore || 0) - (deal.sellerScore || 0);
  if (sellerLead >= 25) return "seller-heavy";
  if (buyerLead >= 12) return "buyer-led";
  if ((deal.buyerScore || 0) >= 75 && (deal.sellerScore || 0) >= 75) return "aligned";
  if ((deal.buyerScore || 0) < 50 && (deal.sellerScore || 0) < 55) return "dormant";
  return "mixed";
}

function scoreBand(score) {
  if (score >= 80) return "high";
  if (score >= 60) return "active";
  if (score >= 45) return "thin";
  return "weak";
}

function storyEvidence(deal, tag) {
  const items = (deal.activity || []).filter((item) => {
    if (tag === "buyer") return item.tag === "buyer" || item.tag === "milestone";
    return item.tag !== "buyer";
  });
  return items[0]?.title || DEAL_DIAGNOSIS[deal.id]?.headline || deal.summary;
}

function getDealTruthNarrative(deal, lens = "truth") {
  const diag = DEAL_DIAGNOSIS[deal.id] || {};
  const seller = deal.sellerScore || 0;
  const buyer = deal.buyerScore || 0;
  const sellerLead = seller - buyer;
  const buyerLead = buyer - seller;
  const gap = Math.abs(sellerLead);
  const tone = dealTruthTone(deal);
  const sellerBand = scoreBand(seller);
  const buyerBand = scoreBand(buyer);

  const sellerStory = {
    title: sellerBand === "high"
      ? "Seller activity is loud"
      : sellerBand === "active"
        ? "Seller activity is present"
        : "Seller activity is light",
    body: `${seller}% seller activity. ${storyEvidence(deal, "seller")}`,
    metric: `${seller}%`,
  };

  const buyerStory = {
    title: buyerBand === "high"
      ? "Buyer progression is visible"
      : buyerBand === "active"
        ? "Buyer progression is forming"
        : "Buyer progression is weak",
    body: `${buyer}% buyer progression. ${storyEvidence(deal, "buyer")}`,
    metric: `${buyer}%`,
  };

  let truthTitle = "The two stories are aligned";
  let truthBody = `${deal.company} has seller motion and buyer proof moving together. The forecast story is supported by both sides.`;
  if (sellerLead >= 25) {
    truthTitle = "Seller activity is ahead of buyer proof";
    truthBody = `${deal.company} is being carried by seller motion. The buyer side is ${gap} points behind, so the deal truth is weaker than the activity level suggests.`;
  } else if (sellerLead >= 10) {
    truthTitle = "Seller motion is slightly ahead";
    truthBody = `${deal.company} has useful seller activity, but buyer progression has not fully caught up. The truth is mixed, not clean.`;
  } else if (buyerLead >= 12) {
    truthTitle = "Buyer progression is leading";
    truthBody = `${deal.company} is showing buyer-side movement faster than seller coverage. The truth is buyer-led and may be underworked.`;
  } else if (buyer < 50 && seller < 55) {
    truthTitle = "Both stories are quiet";
    truthBody = `${deal.company} has low seller motion and low buyer proof. The deal truth is dormant until either side creates a new signal.`;
  } else if (diag.why) {
    truthBody = diag.why;
  }

  const truthStory = {
    title: truthTitle,
    body: truthBody,
    metric: `${Math.round(buyer * 0.58 + Math.min(buyer, seller) * 0.32 + seller * 0.1)}%`,
  };

  if (lens === "seller") return { ...sellerStory, lens, tone, sellerStory, buyerStory, truthStory };
  if (lens === "buyer") return { ...buyerStory, lens, tone, sellerStory, buyerStory, truthStory };
  return { ...truthStory, lens, tone, sellerStory, buyerStory, truthStory };
}

window.getDealTruthNarrative = getDealTruthNarrative;

function stripStoryPrefix(text = "") {
  return text.replace(/^\d+% (seller activity|buyer progression)\. /i, "");
}

function DealLevelTruth({ deal, lens }) {
  const story = getDealTruthNarrative(deal, lens);
  const tone = lens === "truth" ? story.tone : null;

  return (
    <div className={`deal-level-truth lens-${lens}`} data-truth-tone={tone}>
      <div className="deal-level-head">
        <LensBadge lens={lens} size="sm" />
        <b>{story.title}</b>
      </div>
      <p>{stripStoryPrefix(story.body)}</p>
    </div>
  );
}

// LensMetricBar — the metric / bar for the current lens. The lens label
// is OWNED by DealLevelTruth (which sits directly above it), so it isn't
// repeated here. This bar carries only the numeric metric + visual.
function LensMetricBar({ deal, lens, compact = false }) {
  const story = getDealTruthNarrative(deal, lens);
  const value = lensMetricValue(story);
  const tone = lens === "truth" ? story.tone : null;
  const barLabel = lens === "seller"
    ? "Seller-side activity score"
    : lens === "buyer"
      ? "Buyer-side progression score"
      : "Buyer vs seller on the same track";

  if (lens === "truth") {
    const buyer = deal.buyerScore || 0;
    const seller = deal.sellerScore || 0;
    const gap = Math.abs(deal.gap || (seller - buyer));
    const bspTone = gap >= 25 ? "warn" : gap >= 10 ? "amber" : "good";
    return (
      <div
        className={`lens-metric lens-metric-bsp ${compact ? "is-compact" : ""} lens-truth`}
        data-truth-tone={tone}
        title={`${barLabel} — buyer ${buyer}% vs seller ${seller}%`}
      >
        <BuyerSellerProgress buyer={buyer} seller={seller} gap={gap} tone={bspTone} />
      </div>
    );
  }

  return (
    <div
      className={`lens-metric ${compact ? "is-compact" : ""} lens-${lens}`}
      data-truth-tone={tone}
      title={`${barLabel} — ${story.metric}`}
    >
      <div className="lens-metric-top">
        <b>{story.metric}</b>
      </div>
      <div
        className="lens-metric-track"
        role="img"
        aria-label={`${barLabel} ${story.metric}`}
      >
        <span style={{ width: `${value}%` }} />
      </div>
    </div>
  );
}

// ---------- DEAL ROOMS ----------
function DealsScreen({
  deals,
  openDeal,
  view,
  setView,
  embedded = false,
  showPageHeader = true,
  headerEyebrow = "Deal Truth",
  headerTitle = "Deal Truth",
  headerSubtitle = "Seller activity tells one side. Buyer progression tells the other. Deal Truth shows the full story.",
}) {
  const [query, setQuery] = useState("");
  const [truthLens, setTruthLens] = useState("truth");
  const [selected, setSelected] = useState(() => new Set()); // empty => show all
  const [buyerUnverified, setBuyerUnverified] = useState(false);
  const [statusFilters, setStatusFilters] = useState(() => new Set());
  const [gapFilters, setGapFilters] = useState(() => new Set());
  const [actionFilters, setActionFilters] = useState(() => new Set());
  const [closingSoon, setClosingSoon] = useState(false);
  const [sortBy, setSortBy] = useState("impact");
  const [searchOpen, setSearchOpen] = useState(false);
  const [filtersOpen, setFiltersOpen] = useState(false);
  const [viewOptionsOpen, setViewOptionsOpen] = useState(false);
  const searchRef = useRef(null);
  const filtersRef = useRef(null);
  const viewOptionsRef = useRef(null);
  const [headerSlot, setHeaderSlot] = useState(null);

  useEffect(() => {
    if (showPageHeader || typeof document === "undefined") {
      setHeaderSlot(null);
      return;
    }
    setHeaderSlot(document.getElementById("workspace-header-slot"));
  }, [showPageHeader, embedded]);

  useEffect(() => {
    if (searchOpen && searchRef.current) searchRef.current.focus();
  }, [searchOpen]);

  useEffect(() => {
    if (!filtersOpen) return;
    const handle = (e) => {
      if (filtersRef.current && !filtersRef.current.contains(e.target)) setFiltersOpen(false);
    };
    document.addEventListener("mousedown", handle);
    return () => document.removeEventListener("mousedown", handle);
  }, [filtersOpen]);

  useEffect(() => {
    if (!viewOptionsOpen) return;
    const handleClick = (e) => {
      if (viewOptionsRef.current && !viewOptionsRef.current.contains(e.target)) {
        setViewOptionsOpen(false);
      }
    };
    const handleKey = (e) => { if (e.key === "Escape") setViewOptionsOpen(false); };
    document.addEventListener("mousedown", handleClick);
    document.addEventListener("keydown", handleKey);
    return () => {
      document.removeEventListener("mousedown", handleClick);
      document.removeEventListener("keydown", handleKey);
    };
  }, [viewOptionsOpen]);

  const matchesStage = (d) => {
    if (selected.size === 0) return true;
    return [...selected].some((s) => STAGE_MATCH(d, s));
  };
  const matchesStatus = (d) => statusFilters.size === 0 || statusFilters.has(d.status);
  const matchesGapCategory = (d) => gapFilters.size === 0 || gapFilters.has(getDealsGapCategory(d).k);
  const matchesActionCategory = (d) => actionFilters.size === 0 || actionFilters.has(getDealsActionCategory(d).k);
  const matchesClosing = (d) => {
    if (!closingSoon) return true;
    if (!d.closeDate) return false;
    const date = new Date(d.closeDate);
    if (Number.isNaN(date.getTime())) return false;
    const days = (date.getTime() - Date.now()) / (1000 * 60 * 60 * 24);
    return days >= 0 && days <= 30;
  };
  const filtered = deals.filter((d) => {
    if (!matchesStage(d)) return false;
    if (buyerUnverified && !isBuyerUnverifiedDeal(d)) return false;
    if (!matchesStatus(d)) return false;
    if (!matchesGapCategory(d)) return false;
    if (!matchesActionCategory(d)) return false;
    if (!matchesClosing(d)) return false;
    const gapCategory = getDealsGapCategory(d);
    const actionCategory = getDealsActionCategory(d);
    const searchText = `${d.company} ${d.dealName} ${d.champion} ${gapCategory.label} ${actionCategory.label}`.toLowerCase();
    if (query && !searchText.includes(query.toLowerCase())) return false;
    return true;
  });

  const toggleStage = (stage) => {
    setSelected((prev) => {
      const next = new Set(prev);
      if (next.has(stage)) next.delete(stage);
      else next.add(stage);
      return next;
    });
  };
  const toggleStatus = (s) => setStatusFilters((prev) => {
    const next = new Set(prev);
    if (next.has(s)) next.delete(s);
    else next.add(s);
    return next;
  });
  const toggleGapCategory = (k) => setGapFilters((prev) => {
    const next = new Set(prev);
    if (next.has(k)) next.delete(k);
    else next.add(k);
    return next;
  });
  const toggleActionCategory = (k) => setActionFilters((prev) => {
    const next = new Set(prev);
    if (next.has(k)) next.delete(k);
    else next.add(k);
    return next;
  });
  const clearFilters = () => {
    setBuyerUnverified(false);
    setStatusFilters(new Set());
    setGapFilters(new Set());
    setActionFilters(new Set());
    setClosingSoon(false);
  };

  const activeFilterCount = (buyerUnverified ? 1 : 0) + statusFilters.size + gapFilters.size + actionFilters.size + (closingSoon ? 1 : 0);

  const STATUS_OPTIONS = [
    { k: "at-risk",        label: "At risk" },
    { k: "needs-attention", label: "Needs attention" },
    { k: "healthy",        label: "Healthy" },
  ];
  const sortedFiltered = sortDealsForView(filtered, sortBy);

  // Only search lives in the workspace header now. Everything else — lens,
  // filters, sort, view — collapses into a single "view options" popover on
  // the page so the pipeline gets the visual space.
  const lensBadgesJSX = DEAL_TRUTH_LENSES.map((item) => (
    <LensBadge
      key={item.k}
      lens={item.k}
      interactive
      active={truthLens === item.k}
      role="tab"
      ariaSelected={truthLens === item.k}
      onClick={() => {
        setTruthLens(item.k);
        if (item.k === "seller") setBuyerUnverified(false);
      }}
    />
  ));
  const lensSwitchJSX = (
    <div className="deal-lens-switch" role="tablist" aria-label="Deal truth lens">
      <span className="deal-lens-switch-label">Show by</span>
      {lensBadgesJSX}
    </div>
  );

  const searchJSX = (
    <div className={`deals-search ${searchOpen ? "is-open" : ""}`}>
      {searchOpen ? (
        <div className="deals-search-field">
          <span className="deals-search-ic"><Icon name="search" size={13} /></span>
          <input
            ref={searchRef}
            className="deals-search-input"
            placeholder="Search deals, companies, or champions"
            value={query}
            onChange={(e) => setQuery(e.target.value)}
            onBlur={() => { if (!query) setSearchOpen(false); }}
            onKeyDown={(e) => {
              if (e.key === "Escape") { setQuery(""); setSearchOpen(false); }
            }}
          />
        </div>
      ) : (
        <button
          type="button"
          className={`btn sm ghost deals-search-btn ${query ? "has-value" : ""}`}
          onClick={() => setSearchOpen(true)}
          aria-label="Search deals"
          title="Search deals"
        >
          <Icon name="search" size={13} />
          {query && <span className="deals-search-dot" aria-hidden="true" />}
        </button>
      )}
    </div>
  );

  const filterRowsJSX = (
    <>
      {truthLens !== "seller" && (
        <div className="deals-filter-group">
          <span className="deals-filter-l">Buyer proof</span>
          <label className="deals-filter-row">
            <input
              type="checkbox"
              checked={buyerUnverified}
              onChange={(e) => {
                setBuyerUnverified(e.target.checked);
                if (e.target.checked) trackNudgeEvent("buyer_unverified_filter_used", { view });
              }}
            />
            <span>Buyer-unverified</span>
          </label>
        </div>
      )}
      <div className="deals-filter-group">
        <span className="deals-filter-l">Status</span>
        {STATUS_OPTIONS.map((s) => (
          <label key={s.k} className="deals-filter-row">
            <input
              type="checkbox"
              checked={statusFilters.has(s.k)}
              onChange={() => toggleStatus(s.k)}
            />
            <span>{s.label}</span>
          </label>
        ))}
      </div>
      <div className="deals-filter-group">
        <span className="deals-filter-l">Gap reason</span>
        {GAP_CATEGORIES.map((category) => (
          <label key={category.k} className="deals-filter-row">
            <input
              type="checkbox"
              checked={gapFilters.has(category.k)}
              onChange={() => toggleGapCategory(category.k)}
            />
            <Icon name={category.icon} size={13} />
            <span>{category.label}</span>
          </label>
        ))}
      </div>
      <div className="deals-filter-group">
        <span className="deals-filter-l">Action type</span>
        {ACTION_CATEGORIES.map((category) => (
          <label key={category.k} className="deals-filter-row">
            <input
              type="checkbox"
              checked={actionFilters.has(category.k)}
              onChange={() => toggleActionCategory(category.k)}
            />
            <Icon name={category.icon} size={13} />
            <span>{category.label}</span>
          </label>
        ))}
      </div>
      <div className="deals-filter-group">
        <span className="deals-filter-l">Timing</span>
        <label className="deals-filter-row">
          <input
            type="checkbox"
            checked={closingSoon}
            onChange={(e) => setClosingSoon(e.target.checked)}
          />
          <span>Closing in 30 days</span>
        </label>
      </div>
      {activeFilterCount > 0 && (
        <button type="button" className="deals-filter-clear" onClick={clearFilters}>
          Clear filters
        </button>
      )}
    </>
  );

  const filtersJSX = (
    <div className="deals-filters" ref={filtersRef}>
      <button
        type="button"
        className={`btn sm ${activeFilterCount > 0 ? "accent" : ""}`}
        onClick={() => setFiltersOpen((v) => !v)}
        aria-expanded={filtersOpen}
        aria-haspopup="dialog"
      >
        <Icon name="filter" size={13} /> Filters
        {activeFilterCount > 0 && <span className="deals-filter-count">{activeFilterCount}</span>}
      </button>
      {filtersOpen && (
        <div className="deals-filters-pop" role="dialog" aria-label="Filter deals">
          {filterRowsJSX}
        </div>
      )}
    </div>
  );

  const sortJSX = (
    <label className="deals-sort-control">
      <span>Sort</span>
      <select value={sortBy} onChange={(e) => setSortBy(e.target.value)} aria-label="Sort deals">
        {DEAL_SORT_OPTIONS.map((option) => (
          <option key={option.k} value={option.k}>{option.label}</option>
        ))}
      </select>
    </label>
  );

  const viewTabsJSX = setView ? (
    <div className="tabs view-tabs view-tabs-sm" aria-label="Deal room view">
      <button
        className={view === "cards" ? "active" : ""}
        onClick={() => setView("cards")}
      >
        <Icon name="grid" size={12} /> Kanban
      </button>
      <button
        className={view === "table" ? "active" : ""}
        onClick={() => setView("table")}
      >
        <Icon name="list" size={12} /> List
      </button>
    </div>
  ) : null;

  // Legacy / embedded path: title + controls in a single page row, so all
  // controls collapse together.
  const controlCluster = (
    <div className="deals-header-controls">
      {lensSwitchJSX}
      {searchJSX}
      {filtersJSX}
      {sortJSX}
      {viewTabsJSX}
    </div>
  );

  // Header slot keeps just search. Everything else (lens switch, filters,
  // sort, view tabs) lives on the page next to the list it operates on.
  // Rendered bare (no panel wrapper) so a single control doesn't look like
  // an empty cluster pill in the header.
  const headerControlCluster = searchJSX;

  // Collapsed toolbar: a single quiet "View options" pill on the right.
  // The pill shows a one-line summary of the current state (active lens,
  // active sort, active view, active filter count) so the user knows
  // what's set without opening it. Clicking opens a popover that contains
  // the full set of controls. This gives the pipeline below maximum
  // visual room — the row no longer competes with the columns.
  const activeLensMeta = LENS_META[truthLens] || LENS_META.truth;
  const activeSortLabel = (DEAL_SORT_OPTIONS.find((s) => s.k === sortBy) || DEAL_SORT_OPTIONS[0]).label;
  const activeViewLabel = view === "cards" ? "Kanban" : "List";

  const pageControlCluster = (
    <div className="deals-view-options-bar" data-tour="deals-controls">
      <div className="deals-view-options" ref={viewOptionsRef}>
        <button
          type="button"
          className="deals-view-options-trigger"
          onClick={() => setViewOptionsOpen((v) => !v)}
          aria-expanded={viewOptionsOpen}
          aria-haspopup="dialog"
          aria-label="Open view options"
        >
          <span className={`deals-view-options-lens-dot is-${truthLens}`} aria-hidden="true" />
          <span className="deals-view-options-summary">
            <span>{activeLensMeta.short}</span>
            <span className="deals-view-options-sep" aria-hidden="true">·</span>
            <span>{activeSortLabel}</span>
            <span className="deals-view-options-sep" aria-hidden="true">·</span>
            <span>{activeViewLabel}</span>
          </span>
          {activeFilterCount > 0 && (
            <span className="deals-view-options-badge" aria-label={`${activeFilterCount} active filters`}>
              {activeFilterCount}
            </span>
          )}
          <Icon name="chevron" size={11} className="deals-view-options-caret" />
        </button>
        {viewOptionsOpen && (
          <div className="deals-view-options-pop" role="dialog" aria-label="View options">
            <section className="deals-view-options-section">
              <span className="deals-view-options-section-label">Show by</span>
              <div className="deals-view-options-lens-row" role="tablist" aria-label="Deal truth lens">
                {lensBadgesJSX}
              </div>
            </section>
            <section className="deals-view-options-section">
              <span className="deals-view-options-section-label">View</span>
              {viewTabsJSX}
            </section>
            <section className="deals-view-options-section">
              <span className="deals-view-options-section-label">Sort</span>
              {sortJSX}
            </section>
            <section className="deals-view-options-section">
              <span className="deals-view-options-section-label">Filters</span>
              <div className="deals-view-options-filters">
                {filterRowsJSX}
              </div>
            </section>
          </div>
        )}
      </div>
    </div>
  );

  const controlsInHeader = headerSlot && typeof ReactDOM !== "undefined";

  return (
    <div className="scroll" data-screen-label="02 Deal Rooms" data-tour="deals-page">
      {controlsInHeader && ReactDOM.createPortal(headerControlCluster, headerSlot)}

      {!embedded && showPageHeader && (
        <div className="topbar">
          <Brand />
        </div>
      )}

      {(showPageHeader || (!embedded && !controlsInHeader)) && (
        <div className={`deals-header ${showPageHeader ? "" : "deals-header-controls-only"}`}>
          {showPageHeader && (
            <div>
              <span className="platform-eyebrow">
                <span className="cp-eyebrow-dot good" aria-hidden />
                {headerEyebrow}
              </span>
              <h1 className="h1">{headerTitle}</h1>
              <p className="deal-truth-sub">{headerSubtitle}</p>
            </div>
          )}
          {!embedded && !controlsInHeader && controlCluster}
          {!embedded && (
            <button className="btn accent" disabled title="Prototype only — create flow is not wired yet">
              <Icon name="plus" size={14} /> New Deal
            </button>
          )}
        </div>
      )}

      {!controlsInHeader && !showPageHeader && (
        <div className="deals-toolbar">
          {controlCluster}
        </div>
      )}

      {controlsInHeader && pageControlCluster}
      {view === "cards" ? (
        <Pipeline
          deals={sortedFiltered}
          allDeals={deals}
          openDeal={openDeal}
          selected={selected}
          onToggleStage={toggleStage}
          lens={truthLens}
          sortBy={sortBy}
        />
      ) : (
        <DealTable deals={sortedFiltered} openDeal={openDeal} lens={truthLens} />
      )}
    </div>
  );
}

// Collapsed limit per stage column. When a stage has more than this many
// deals, only the top-N (sorted by pipeline impact) are shown by default.
// A "Show N more" affordance below expands the column inline — keeps the
// rest of the columns aligned and the page from becoming an infinite scroll.
const PIPELINE_COLLAPSED_LIMIT = 5;

function Pipeline({ deals, allDeals, openDeal, selected, onToggleStage, lens }) {
  const [expandedStages, setExpandedStages] = useState(() => new Set());
  const toggleStageExpansion = (name) => setExpandedStages((prev) => {
    const next = new Set(prev);
    if (next.has(name)) next.delete(name);
    else next.add(name);
    return next;
  });

  const byStage = STAGE_ORDER.map((s) => ({
    name: s,
      deals: deals.filter((d) => STAGE_MATCH(d, s)),
      fullDeals: allDeals.filter((d) => STAGE_MATCH(d, s)),
  }));
  const firstVisibleDealId = deals[0]?.id;

  return (
    <div className="pipeline" role="list" data-tour="deals-pipeline">
      <div className="pipeline-stage-header" role="presentation">
        {byStage.map((s, i) => {
          const stageValue = s.fullDeals.reduce((sum, d) => sum + d.value, 0);
          const isSelected = selected.has(s.name);
          const stepNumber = String(i + 1).padStart(2, "0");
          const isLast = i === byStage.length - 1;
          return (
            <div
              key={`${s.name}-head`}
              className={`pipeline-stage pipeline-stage-head-cell ${isSelected ? "is-selected" : ""}`}
              data-stage-index={i + 1}
            >
              <button
                type="button"
                className="pipeline-head"
                onClick={() => onToggleStage(s.name)}
                aria-pressed={isSelected}
                title={isSelected ? `Showing ${s.name} — click to clear` : `Show ${s.name}`}
              >
                <span className="pipeline-head-name">{s.name}</span>
                <span className="pipeline-head-rail" aria-hidden="true">
                  <span className="pipeline-head-rail-line" />
                  <span className="pipeline-head-rail-node">
                    {isLast ? (
                      <Icon name="check" size={11} />
                    ) : (
                      <span className="pipeline-head-rail-num">{stepNumber}</span>
                    )}
                  </span>
                </span>
                <span className="pipeline-head-meta">
                  <span className="pipeline-head-count">
                    {s.fullDeals.length} {s.fullDeals.length === 1 ? "deal" : "deals"}
                  </span>
                  <span className="pipeline-head-dot" aria-hidden="true">·</span>
                  <span className="pipeline-head-value">${(stageValue / 1000).toFixed(0)}K</span>
                </span>
              </button>
            </div>
          );
        })}
      </div>

      <div className="pipeline-stage-columns">
        {byStage.map((s, i) => {
          const isExpanded = expandedStages.has(s.name);
          const visibleDeals = isExpanded
            ? s.deals
            : s.deals.slice(0, PIPELINE_COLLAPSED_LIMIT);
          const hiddenCount = s.deals.length - visibleDeals.length;
          return (
          <div
            key={s.name}
            className="pipeline-stage pipeline-stage-column"
            data-stage-index={i + 1}
            role="listitem"
          >
            <div className="pipeline-deals">
              {s.deals.length === 0 && <div className="pipeline-empty">— none</div>}
              {visibleDeals.map((d) => {
                const closed = isClosedDeal(d);
                const outcome = closed ? closedDealAnalysis(d) : null;
                const riskLabel = closed ? outcome.label : d.statusLabel;
                const riskTone = closed ? `closed-${outcome.tone}` : d.status;
                const defense = getDealDefense(d);
                const story = getDealTruthNarrative(d, lens);
                const gapCategory = getDealsGapCategory(d);
                const actionCategory = getDealsActionCategory(d);
                return (
                  <button
                    key={d.id}
                    className={`pipeline-card ${closed ? "is-closed" : ""}`}
                    data-status={d.status}
                    data-outcome={outcome?.tone}
                    data-tour={d.id === firstVisibleDealId ? "deals-first-card" : undefined}
                    onClick={() => openDeal(d.id)}
                  >
                    <div className="pipeline-card-top">
                      <span className="pipeline-card-co">{d.company}</span>
                      <span className="pipeline-card-meta">
                        <span className="meta-chip pipeline-card-v">${(d.value / 1000).toFixed(0)}K</span>
                        <span
                          className={`risk-meter tone-${riskTone}`}
                          role="img"
                          aria-label={`Deal health: ${riskLabel}`}
                          title={riskLabel}
                        >
                          <span /><span /><span />
                        </span>
                      </span>
                    </div>
                    <DealsCategoryStrip gapCategory={gapCategory} actionCategory={actionCategory} />
                    <div className="pipeline-card-body" data-tour={d.id === firstVisibleDealId ? "deals-card-proof" : undefined} aria-hidden="true">
                      <div className="pipeline-card-body-inner">
                        {closed ? (
                          <div className="pipeline-outcome">
                            <div className="pipeline-card-line">{outcome.headline}</div>
                            <p>{outcome.primary}</p>
                            <span>{outcome.secondary}</span>
                          </div>
                        ) : (
                          <>
                            <DealLevelTruth deal={d} lens={lens} />
                            <LensMetricBar deal={d} lens={lens} />
                          </>
                        )}
                      </div>
                    </div>
                  </button>
                );
              })}
              {(hiddenCount > 0 || isExpanded && s.deals.length > PIPELINE_COLLAPSED_LIMIT) && (
                <button
                  type="button"
                  className={`pipeline-deals-toggle ${isExpanded ? "is-expanded" : ""}`}
                  onClick={() => toggleStageExpansion(s.name)}
                  aria-expanded={isExpanded}
                  aria-label={
                    hiddenCount > 0
                      ? `Show ${hiddenCount} more deal${hiddenCount === 1 ? "" : "s"} in ${s.name}`
                      : `Show top ${PIPELINE_COLLAPSED_LIMIT} deals in ${s.name}`
                  }
                >
                  {hiddenCount > 0 ? (
                    <>
                      <span>Show <b>{hiddenCount}</b> more</span>
                      <Icon name="chevron" size={11} />
                    </>
                  ) : (
                    <>
                      <span>Show top {PIPELINE_COLLAPSED_LIMIT}</span>
                      <Icon name="chevron" size={11} className="is-flipped" />
                    </>
                  )}
                </button>
              )}
            </div>
          </div>
          );
        })}
      </div>
    </div>
  );
}

function DealTable({ deals, openDeal, lens }) {
  const metricLabel = lensLabel(lens);
  const firstVisibleDealId = deals[0]?.id;
  return (
    <div className="deal-table-wrap">
      <table className="tbl">
        <thead>
          <tr>
            <th>Company</th>
            <th>{metricLabel} story</th>
            <th className="tbl-category-col">Categories</th>
            <th>Stage</th>
            <th className="tbl-num">Value</th>
            <th className="tbl-progress">{metricLabel}</th>
            <th>Close Date</th>
            <th className="tbl-status-col">Health</th>
          </tr>
        </thead>
        <tbody>
          {deals.map((d) => {
            const closed = isClosedDeal(d);
            const outcome = closed ? closedDealAnalysis(d) : null;
            const defense = getDealDefense(d);
            const story = getDealTruthNarrative(d, lens);
            const statusTone = closed ? `closed-${outcome.tone}` : d.status;
            const gapCategory = getDealsGapCategory(d);
            const actionCategory = getDealsActionCategory(d);
            const statusTitle = [
              closed ? outcome.label : d.statusLabel,
              !closed && defense.state !== "Verified" ? defense.riskLabel : null,
            ].filter(Boolean).join(" · ");
            return (
              <tr
                key={d.id}
                className="row"
                role="button"
                tabIndex={0}
                aria-label={`Open ${d.company} deal room`}
                data-tour={d.id === firstVisibleDealId ? "deals-first-card" : undefined}
                onClick={() => openDeal(d.id)}
                onKeyDown={(e) => {
                  if (e.key === "Enter" || e.key === " ") {
                    e.preventDefault();
                    openDeal(d.id);
                  }
                }}
              >
                <td><div className="cell-strong">{d.company}</div><div className="sub table-champion">{d.champion}</div></td>
                <td>
                  <div className="cell-strong table-truth-title" data-tour={d.id === firstVisibleDealId ? "deals-card-proof" : undefined}>{story.title}</div>
                  <div className="sub table-truth-body">{stripStoryPrefix(story.body)}</div>
                </td>
                <td><DealsCategoryStrip gapCategory={gapCategory} actionCategory={actionCategory} compact /></td>
                <td><span className="meta-chip">{d.stage}</span></td>
                <td className="tbl-num"><span className="meta-chip">${(d.value / 1000).toFixed(0)}K</span></td>
                <td>
                  <LensMetricBar deal={d} lens={lens} compact />
                </td>
                <td><span className="meta-chip">{d.closeDate}</span></td>
                <td className="tbl-status-cell">
                  <span
                    className={`risk-meter table-status-meter tone-${statusTone}`}
                    role="img"
                    aria-label={`Deal health: ${statusTitle}`}
                    title={statusTitle}
                  >
                    <span /><span /><span />
                  </span>
                </td>
              </tr>
            );
          })}
        </tbody>
      </table>
    </div>
  );
}

window.DealsScreen = DealsScreen;
