/* global React, Icon, Brand, AgentChip, OPENINGS, CHANNEL_LABEL, CHANNEL_ICON, TRUST_TIERS */
// Openings — the Opener's AI-SDR engine, as a kanban by stage.
//
// This is the top of the funnel: cold prospects, no deal and no pipeline yet.
// The runtime flow: signal detected → scored → channel + message drafted →
// (human review, or not — you choose the autonomy) → sent → reply detected →
// sequence plays → meeting booked, or disqualified. Only a booked meeting
// graduates into Deal Rhythm — that's where a deal (and its clock) begins.
//
// The Nudge rule still holds: the automated outreach steps (detect / score /
// draft / send / sequence) stay visually QUIET. Prospect engagement — a reply,
// a booked meeting — is the only thing that gets color. Volume of touches is
// never the signal.

// The six lifecycle stages, left → right.
const LEAD_STAGES = [
  { id: "detected",     label: "Detected",     hint: "Signal fired · scored · drafting", tone: "cool" },
  { id: "needs-review", label: "Needs you",    hint: "Drafted — awaiting your sign-off",  tone: "cool" },
  { id: "in-sequence",  label: "In sequence",  hint: "Sent · sequence playing · no reply", tone: "muted" },
  { id: "replied",      label: "Replied",      hint: "Prospect replied — engaging",        tone: "info" },
  { id: "booked",       label: "Booked",       hint: "Meeting booked — graduate to Deal Rhythm", tone: "good" },
  { id: "disqualified", label: "Disqualified", hint: "No movement — recycle on a fresh trigger", tone: "warn" },
];

// The board's spine: the six stages group into three phases by *who's acting*.
// Machine runs itself (quiet, recessed); You is the human-action band (the only
// one that earns emphasis); Outcome is terminal. This is what gives the board
// structure — the eye reads machine → you → outcome left-to-right.
const LEAD_BANDS = [
  { id: "machine", label: "Machine", sub: "Runs itself",     stages: ["detected", "in-sequence"] },
  { id: "you",     label: "You",     sub: "Needs a human",   stages: ["needs-review", "replied"], primary: true },
  { id: "outcome", label: "Outcome", sub: "Booked or recycled", stages: ["booked", "disqualified"] },
];
const stageById = (id) => LEAD_STAGES.find((s) => s.id === id) || { id, label: id, hint: "", tone: "muted" };

// Each funnel step → the live stages that have reached it ("at least this
// far"), so clicking a funnel row in Insights drills the board into that
// step's live cohort. "Sourced" clears the filter (whole board); "Graduated"
// routes to Deal Rhythm, where graduated leads actually live.
const FUNNEL_STAGE_SETS = {
  Opened:  ["in-sequence", "replied", "booked"],
  Replied: ["replied", "booked"],
  Meeting: ["booked"],
  Booked:  ["booked"],
};

// Autonomy — the human-review gate, mapped to the shared trust ladder. When
// it's not "review each", drafted leads skip the Needs-you column and the
// Opener sends them straight into sequence.
const AUTONOMY_MODES = [
  { id: "review", tier: "propose",           label: "Review each",  short: "You approve every send" },
  { id: "window", tier: "send-with-window",  label: "Auto", short: "Sends after a 5-minute cancel window" },
  { id: "auto",   tier: "autonomous",        label: "Autonomous",   short: "Sends, surfaces only replies" },
];

// No pipeline exists until a lead books a meeting and graduates into a deal.
// Each lead carries an estimated value that only *counts* — and only shows —
// once it's booked; before that the board leads with intent, not dollars.
const LEAD_VALUE = {
  o1: 120000, o2: 90000, o3: 120000, o4: 0, o5: 60000, o6: 48000,
  o7: 40000, o8: 75000, o9: 55000, o10: 50000, o11: 95000, o12: 0,
};
const leadValue = (o) => (LEAD_VALUE[o.id] != null ? LEAD_VALUE[o.id] : 50000);
const fmtMoney = (v) => (v >= 1e6 ? `$${(v / 1e6).toFixed(1)}M` : `$${Math.round(v / 1000)}K`);

// Segments each lead by industry + persona so the board and Insights can track
// what's working across dimensions.
const LEAD_INDUSTRY = {
  o1: "Revenue planning", o2: "Logistics SaaS", o3: "Climate / ESG", o4: "Enterprise search",
  o5: "Productivity", o6: "Fintech", o7: "Data infra", o8: "Security",
  o9: "DevTools", o10: "DevTools", o11: "Logistics SaaS", o12: "Productivity",
};
const leadIndustry = (o) => LEAD_INDUSTRY[o.id] || "B2B SaaS";
const pctText = (v) => `${Math.round(v * 100)}%`;

// The Opener's research ("personalization waterfall") for a lead — explicit
// research[] if present, else synthesized from the intent triggers.
function leadResearch(o) {
  if (Array.isArray(o.research) && o.research.length) return o.research;
  return (o.intentTriggers || []).map((t) => ({ source: t.source, finding: t.text, when: "—" }));
}
// The 2–3 sources the draft is grounded in (drives the "no templates" footer).
function draftGrounding(o) {
  if (o.draft && Array.isArray(o.draft.grounding) && o.draft.grounding.length) return o.draft.grounding;
  return leadResearch(o).slice(0, 2).map((r) => ({ label: (r.finding || "").split(/[—.:]/)[0].trim().slice(0, 34), source: r.source }));
}

// Map a prospect's specific role to the persona group the Insights view tracks,
// so clicking a persona can filter the board.
function personaGroup(role = "") {
  const r = role.toLowerCase();
  if (/revops|revenue ops/.test(r)) return "Head of RevOps";
  if (/coo|ops|operations/.test(r)) return "COO / Ops";
  if (/vp revenue|head of revenue/.test(r)) return "VP Revenue";
  if (/cro|chief revenue|vp sales|head of sales|sales/.test(r)) return "CRO / VP Sales";
  return "Other";
}

// ---------- stage-transition + preview handlers ----------

// Review + send a cold open (the Needs-you gate). On send, the lead advances
// into the sequence.
function reviewLead(o, onSent) {
  const prospect = `${o.prospect.name} · ${o.prospect.role}`;
  const ro = window.resolveOutreach ? window.resolveOutreach(o) : { cta: "Send first beat", short: (CHANNEL_LABEL || {})[o.channel] || o.channel };
  const channels = window.buildChannelsPayload
    ? window.buildChannelsPayload(o)
    : { primary: o.channel, options: [o.channel, ...(o.altChannels || [])], contact: { name: o.prospect.name }, draft: o.draft };
  window.openActionChoice && window.openActionChoice({
    kicker: o.kind,
    title: `Open ${o.company}`,
    context: prospect,
    selfLabel: "Do it myself",
    selfSub: "I'll write and send the first beat",
    aiLabel: "Let Opener send it",
    aiSub: `${ro.short} picked for best reach — you approve before it goes`,
    onSelf: () => window.previewAction && window.previewAction({
      kicker: "Manual open",
      title: `Open ${o.company}`,
      meta: [{ label: "Prospect", value: o.prospect.name }, { label: "Why now", value: o.fitLabel }],
      body: { label: "The opening", text: `${o.why}\n\nSend the first beat yourself, then log it — Opener keeps watching for a reply either way.` },
      primaryLabel: "Mark as opened",
      onConfirm: () => { onSent && onSent(o.id); window.fireToast && window.fireToast(`Logged open for ${o.company} · in sequence`); },
    }),
    onAi: () => window.previewAction && window.previewAction({
      kicker: "Opener · first beat",
      title: `Open ${o.company}`,
      meta: [{ label: "Prospect", value: o.prospect.name }, { label: "Why now", value: o.fitLabel }],
      channels,
      grounding: draftGrounding(o).map((g) => `${g.label} · ${g.source}`),
      primaryLabel: ro.cta,
      onConfirm: (ch) => {
        const sent = window.outreachForChannel ? window.outreachForChannel(ch, o).short : ((CHANNEL_LABEL || {})[ch] || ch);
        onSent && onSent(o.id);
        window.fireToast && window.fireToast(`${sent} sent to ${o.company} · now in sequence`);
      },
    }),
  });
}

// Prospect replied → propose the meeting. On confirm the lead moves to Booked.
function bookLead(o, onBooked) {
  window.previewAction && window.previewAction({
    kicker: "Opener · reply detected",
    title: `Propose a meeting — ${o.company}`,
    meta: [{ label: "Prospect", value: o.prospect.name }, { label: "Engagement", value: o.movementLabel }],
    body: { label: "What happens", text: `${o.prospect.name} replied. Opener proposes a working session with an owned agenda. It only counts once the prospect takes a dated, owned step — that's what makes it bookable, not the reply alone.` },
    primaryLabel: "Send meeting proposal",
    onConfirm: () => { onBooked && onBooked(o.id); window.fireToast && window.fireToast(`Meeting proposed to ${o.company} · Opener watches for a confirmed slot`); },
  });
}

// Booked → graduate into Deal Rhythm (a deal is born, clock starts, handed to Scout).
function graduateLead(o, onGraduated) {
  window.previewAction && window.previewAction({
    kicker: "Opener · hand-off",
    title: `Graduate ${o.company} into Deal Rhythm`,
    meta: [{ label: "Prospect", value: o.prospect.name }, { label: "Engagement", value: o.movementLabel }],
    body: { label: "What happens", text: `${o.company} took a dated, owned next step. It enters Deal Rhythm as a new deal — this is where a deal, and its clock, actually begins. Scout takes the rhythm from here.` },
    primaryLabel: "Graduate · start the clock",
    onConfirm: () => {
      if (typeof window.graduateOpening === "function" && o.graduatesTo) window.graduateOpening(o.graduatesTo);
      onGraduated && onGraduated(o.id);
      window.fireToast && window.fireToast(`${o.company} entered Deal Rhythm — clock started, handed to Scout`);
    },
  });
}

// Disqualified → stand down (Pass 1). Recycle-to-campaign lands in Pass 2.
function standDownLead(o) {
  window.previewAction && window.previewAction({
    kicker: "Opener · restraint",
    title: `Stand down on ${o.company}`,
    meta: [{ label: "Touches", value: String((o.touches || []).length) }, { label: "Engagement", value: o.movementLabel }],
    body: { label: "Why stop", text: `${o.disqualifyReason || "No response."} Sent volume isn't a relationship — escalating just burns the door. Opener stands down and re-queues only on a fresh trigger.` },
    primaryLabel: "Stand down",
    onConfirm: () => window.fireToast && window.fireToast(`Stood down on ${o.company} — re-queues on a fresh trigger`),
  });
}

// Reply inbox — review the Opener's drafted response to a prospect reply. Sending
// it advances the relationship toward a booked meeting.
function replyToLead(o, onHandled) {
  const r = o.reply || {};
  window.previewAction && window.previewAction({
    kicker: `Opener · ${r.classification || "reply"}`,
    title: `Reply to ${o.company}`,
    meta: [{ label: "Prospect", value: o.prospect.name }, { label: "Channel", value: (CHANNEL_LABEL || {})[o.channel] || o.channel }],
    body: { label: "Your reply", text: r.suggestedResponse },
    primaryLabel: "Send reply",
    onConfirm: () => { onHandled && onHandled(o.id); window.fireToast && window.fireToast(`Reply sent to ${o.company} · Opener watches for a booked slot`); },
  });
}

// Company monogram — 1–2 letters as a visual identity anchor.
function leadInitials(company) {
  const w = (company || "").trim().split(/\s+/);
  return (w.length > 1 ? (w[0][0] + w[1][0]) : (company || "?").slice(0, 2)).toUpperCase();
}

// A uniform 4-dot sequence read for every stage (so the section is identical
// across cards). Uses the real sequence when present, else synthesizes one
// from the stage + touch count.
function leadSequence(o, stage) {
  const seq = o.sequence;
  if (seq && Array.isArray(seq.steps)) {
    return {
      dots: seq.steps.map((s) => s.status),
      label: seq.paused ? "Paused — prospect replied" : `Touch ${seq.step}/${seq.total} · next: ${seq.nextLabel}`,
    };
  }
  const sent = (o.touches || []).length;
  const filled = (n) => [0, 1, 2, 3].map((i) => (i < n ? "sent" : "queued"));
  if (stage === "detected")     return { dots: filled(0), label: "Drafting first touch" };
  if (stage === "needs-review") return { dots: filled(0), label: "First touch ready to send" };
  if (stage === "booked")       return { dots: filled(4), label: "Sequence complete · booked" };
  if (stage === "disqualified") return { dots: filled(sent), label: `${sent} sent · 0 replies` };
  return { dots: filled(sent), label: sent ? `${sent} sent` : "Not started" };
}

// Shared per-stage derivations (used by both the compact card and the room).
function leadTone(stage) {
  return stage === "booked" ? "good"
    : stage === "replied" ? "info"
    : stage === "disqualified" ? "warn"
    : (stage === "detected" || stage === "needs-review") ? "cool" : "muted";
}
function leadMovement(o, stage, graduated) {
  const tone = graduated ? "good"
    : stage === "booked" ? "good"
    : stage === "replied" ? "info"
    : stage === "disqualified" ? "warn"
    : "muted";
  const text = graduated ? "In Deal Rhythm"
    : stage === "detected" ? "Not contacted yet"
    : stage === "needs-review" ? `Draft ready · ${window.resolveOutreach ? window.resolveOutreach(o).short : ((CHANNEL_LABEL || {})[o.channel] || o.channel)}`
    : o.movementLabel;
  return { tone, text };
}

// ---------- compact lead card ----------
// Deliberately minimal — like a deal card. Identity + intent score + the one
// focal engagement line. Everything else opens in the lead room on click.
function LeadCard({ o, stage, graduated, fromSlack, onOpen }) {
  const mono = leadTone(stage);
  const mv = leadMovement(o, stage, graduated);
  return (
    <article
      className={`lead-card stage-${stage} ${graduated ? "is-graduated" : ""} ${fromSlack ? "is-from-slack" : ""}`}
      role="button"
      tabIndex={0}
      aria-label={`Open ${o.company} lead`}
      onClick={() => onOpen(o.id)}
      onKeyDown={(e) => { if (e.key === "Enter" || e.key === " ") { e.preventDefault(); onOpen(o.id); } }}
    >
      <div className="lead-card-top">
        <div className="lead-id-text">
          <b className="lead-company">{o.company}</b>
          <span className="lead-prospect">{o.prospect.name}</span>
        </div>
        {(stage === "booked" || graduated) && leadValue(o) > 0
          ? <span className="lead-value-badge" title={`Booked · ~${fmtMoney(leadValue(o))} entering pipeline`}>{fmtMoney(leadValue(o))}</span>
          : <span className="lead-score-badge" title={`Intent ${o.intentScore} · Fit ${o.fitScore}`}>{o.intentScore}</span>}
      </div>
      <div className="lead-tags">
        <span className="lead-tag is-persona">{o.prospect.role}</span>
        <span className="lead-tag">{leadIndustry(o)}</span>
      </div>
      <div className={`lead-move-row tone-${mv.tone}`}>
        <span className="lead-movement-dot" aria-hidden="true" />
        <b className="lead-movement-val">{mv.text}</b>
        {fromSlack && (
          <span className="lead-from-slack" title="Last moved by a Slack command">
            <Icon name="bolt" size={9} /> Slack
          </span>
        )}
      </div>
    </article>
  );
}

// ---------- reach-out section (lead room) ----------
// Where the first beat actually lands: are you connected to this prospect on
// LinkedIn, which concrete act the channel resolves to (connection request /
// LinkedIn DM / email — one channel per CTA), why that channel wins on
// reach-out rate (your history + market), and the campaign's channel mix.
const CAMPAIGN_LABEL = { "forecast-cro": "Forecast-pain · new CRO", upmarket: "Upmarket friction" };
function campaignChannels(o) {
  const steps = (o.sequence && o.sequence.steps) || ((window.OPENER_BRIEF || {}).sequence || []);
  const set = steps.map((s) => s.channel).filter((c, i, a) => c && a.indexOf(c) === i);
  return set.length ? set : [o.channel, ...(o.altChannels || [])].filter((c, i, a) => c && a.indexOf(c) === i);
}

function LeadReach({ o }) {
  const ro = window.resolveOutreach ? window.resolveOutreach(o) : null;
  if (!ro) return null;
  const ch = (c) => (CHANNEL_LABEL || {})[c] || c;
  const net = o.network || {};
  const mut = net.mutuals ? ` · ${net.mutuals} mutual${net.mutuals > 1 ? "s" : ""}` : "";
  const li = net.linkedin === "connected"
    ? { tone: "good", text: `Connected on LinkedIn · ${net.degree || "1st"}${mut}` }
    : net.linkedin === "pending"
      ? { tone: "muted", text: `LinkedIn invite pending${net.pendingSince ? ` since ${net.pendingSince}` : ""}${mut}` }
      : { tone: "muted", text: `Not connected on LinkedIn${net.degree ? ` · ${net.degree}` : ""}${mut}` };
  const reason = window.channelReason ? window.channelReason(o.channel, o) : "";
  const chs = campaignChannels(o);
  return (
    <div className="lead-sec lead-reach">
      <em className="lead-sec-label">Reach out</em>
      {o.network && (
        <div className={`lead-net tone-${li.tone}`}>
          <span className="lead-net-dot" aria-hidden="true" />
          <Icon name="users" size={12} />
          <span className="lead-net-text">{li.text}</span>
        </div>
      )}
      <div className="lead-reach-pick">
        <span className="lead-reach-ch">
          <Icon name={(CHANNEL_ICON || {})[o.channel] || "mail"} size={12} /> {ro.short}
        </span>
        {reason && <p className="lead-reach-why">{reason}</p>}
      </div>
      {chs.length > 1 && (
        <p className="lead-reach-campaign">
          Campaign · {CAMPAIGN_LABEL[o.campaignId] || o.campaignId || "Outbound"} — runs on {chs.map(ch).join(" + ")}, one channel per touch.
        </p>
      )}
    </div>
  );
}

// ---------- lead room ----------
// The full detail, opened on click — same logic as the deal room. All the
// data lives here: signals, scores, sequence, the drafted first beat, prospect
// engagement, and the stage action.
function LeadRoom({ o, stage, graduated, onSent, onBooked, onGraduated, onClose }) {
  const [researching, setResearching] = useState(false);
  useEffect(() => {
    const onKey = (e) => { if (e.key === "Escape") onClose(); };
    document.addEventListener("keydown", onKey);
    return () => document.removeEventListener("keydown", onKey);
  }, [onClose]);

  const reResearch = () => {
    setResearching(true);
    setTimeout(() => {
      setResearching(false);
      window.fireToast && window.fireToast(`Opener re-ran research on ${o.company} · draft refreshed`);
    }, 950);
  };

  const research = leadResearch(o);
  const grounding = draftGrounding(o);
  const nudgeLike = { id: o.id, kind: o.kind, title: o.company, sub: o.why };
  const meters = [{ label: "Intent", value: o.intentScore }, { label: "Fit", value: o.fitScore }];
  const sq = leadSequence(o, stage);
  const mv = leadMovement(o, stage, graduated);
  const ch = (c) => (CHANNEL_LABEL || {})[c] || c;
  const stageLabel = (LEAD_STAGES.find((s) => s.id === stage) || {}).label || stage;
  const metaParts = [stageLabel, `${o.prospect.name} · ${o.prospect.role}`];
  if ((stage === "booked" || graduated) && leadValue(o) > 0) metaParts.push(`~${fmtMoney(leadValue(o))}`);
  const meta = metaParts.join("  ·  ");
  const fallbackLabels = ["First touch", "Follow-up", "Proof point", "Break-up"];
  const steps = (o.sequence && o.sequence.steps)
    ? o.sequence.steps
    : sq.dots.map((st, i) => ({ n: i + 1, status: st, label: fallbackLabels[i] || `Touch ${i + 1}`, channel: o.channel }));

  // The single stage action (or the quiet machine-state note when the Opener
  // is the one acting). Lives in the hero so the next step is never buried.
  const heroAction =
    stage === "needs-review" ? (
      <button className="btn accent" onClick={() => reviewLead(o, (id) => { onSent(id); onClose(); })}>
        {(window.resolveOutreach && window.resolveOutreach(o).cta) || "Review & send"} <Icon name="arrow" size={13} />
      </button>
    ) : stage === "replied" ? (
      <button className="btn accent" onClick={() => bookLead(o, (id) => { onBooked(id); onClose(); })}>Book meeting <Icon name="arrow" size={13} /></button>
    ) : stage === "booked" ? (
      graduated
        ? <span className="chip tone-good"><Icon name="check" size={12} /> In Deal Rhythm</span>
        : <button className="btn accent" onClick={() => graduateLead(o, (id) => { onGraduated(id); onClose(); })}>Graduate to Deal Rhythm <Icon name="arrow" size={13} /></button>
    ) : stage === "disqualified" ? (
      <button className="btn ghost" onClick={() => standDownLead(o)}>Stand down</button>
    ) : stage === "in-sequence" ? (
      <span className="lead-cta-note">Opener runs the sequence — pauses the moment the prospect replies.</span>
    ) : (
      <span className="lead-cta-note">Signal scored · Opener is drafting the first beat.</span>
    );

  return (
    <div className="modal-back" onClick={onClose}>
      <div className="modal lead-room" onClick={(e) => e.stopPropagation()} role="dialog" aria-modal="true" aria-label={`${o.company} lead`}>
        <div className="modal-head">
          <div className="modal-status">
            <div className="modal-title-row">
              <h2 className="h2">{o.company}</h2>
              <span className="lead-kind">{o.kind}</span>
            </div>
            <div className="deal-meta-line">{meta}</div>
          </div>
          <AgentChip agentId="opener" nudge={nudgeLike} label="Opener" />
          <button className="btn sm ghost" onClick={onClose} aria-label="Close lead"><Icon name="close" size={14} /></button>
        </div>

        <div className="modal-body">
          {/* Focal band: engagement (the only thing that earns color) and
             the one next action, side by side — read the state, act on it. */}
          <div className={`lead-room-hero tone-${mv.tone}`}>
            <div className="lead-hero-move">
              <em className="lead-sec-label">Engagement</em>
              <div className="lead-movement">
                <span className="lead-movement-dot" aria-hidden="true" />
                <b className="lead-movement-val">{mv.text}</b>
              </div>
            </div>
            <div className="lead-hero-cta">{heroAction}</div>
          </div>

          <div className="lead-room-grid">
            {/* Main column — the work: why this lead, the drafted beat, the plan. */}
            <div className="lead-room-main">
              <div className="lead-sec">
                <em className="lead-sec-label">Why now</em>
                <p className="lead-why-full">{o.why}</p>
              </div>

              {o.draft && (
                <div className="lead-sec">
                  <div className="lead-research-head">
                    <em className="lead-sec-label">Drafted first beat · {ch(o.draft.channel)}</em>
                    <span className="lead-draft-badge"><Icon name="sparkles" size={10} /> Unique · no template</span>
                  </div>
                  {o.draft.subject && <p className="lead-draft-subj">{o.draft.subject}</p>}
                  <div className="lead-draft-body">
                    {o.draft.body.split("\n\n").map((para, i) => (
                      <p key={i} className={i === 1 ? "lead-draft-para is-personalized" : "lead-draft-para"}>{para}</p>
                    ))}
                  </div>
                  {grounding.length > 0 && (
                    <div className="lead-draft-grounding">
                      <em>Grounded in</em>
                      {grounding.map((g, i) => (
                        <span key={i} className="lead-ground-chip" title={`from ${g.source}`}>{g.label}<i>{g.source}</i></span>
                      ))}
                    </div>
                  )}
                </div>
              )}

              <div className="lead-sec">
                <div className="lead-research-head">
                  <em className="lead-sec-label">Sequence</em>
                  <span className="lead-seq-status">{sq.label}</span>
                </div>
                <div className="lead-steps">
                  {steps.map((s) => (
                    <div key={s.n} className="lead-step">
                      <span className={`lead-seq-dot is-${s.status}`} aria-hidden="true" />
                      <span className="lead-step-label">{s.label}</span>
                      <span className="lead-step-ch">{ch(s.channel)}</span>
                      <span className={`lead-step-status is-${s.status}`}>{s.status}</span>
                    </div>
                  ))}
                </div>
              </div>
            </div>

            {/* Side rail — the supporting evidence: how we reach them, how the
               lead scores, what the research found. Context, not the story. */}
            <aside className="lead-room-side">
              <LeadReach o={o} />

              <div className="lead-sec">
                <em className="lead-sec-label">Lead score</em>
                {meters.map((m) => (
                  <div key={m.label} className="lead-meter">
                    <span className="lead-meter-name">{m.label}</span>
                    <span className="lead-meter-bar" aria-hidden="true"><i style={{ width: `${Math.max(0, Math.min(100, m.value))}%` }} /></span>
                    <b className="lead-meter-val">{m.value}</b>
                  </div>
                ))}
              </div>

              {research.length > 0 && (
                <div className={`lead-sec lead-research ${researching ? "is-researching" : ""}`}>
                  <div className="lead-research-head">
                    <em className="lead-sec-label">Research · {research.length} sources</em>
                    <button type="button" className="lead-research-btn" onClick={reResearch} disabled={researching}>
                      <Icon name="sparkles" size={11} /> {researching ? "Researching…" : "Re-research"}
                    </button>
                  </div>
                  <ul className="lead-research-list">
                    {research.map((r, i) => (
                      <li key={i} className="lead-research-item">
                        <span className="lead-research-src">{r.source}{r.when && r.when !== "—" ? <em className="lead-research-when"> · {r.when}</em> : null}</span>
                        <span className="lead-research-find" title={r.finding}>{r.finding}</span>
                      </li>
                    ))}
                  </ul>
                </div>
              )}
            </aside>
          </div>
        </div>
      </div>
    </div>
  );
}

// ---------- insights view ----------
// Growth helpers — week-over-week change as readable points / arrows.
const ptsText = (d) => `${d > 0 ? "+" : d < 0 ? "−" : ""}${Math.abs(Math.round(d * 1000) / 10)} pts`;
const deltaTone = (d) => (d > 0.001 ? "up" : d < -0.001 ? "down" : "flat");
const deltaArrow = (d) => (d > 0.001 ? "▲" : d < -0.001 ? "▼" : "–");

// A ranked bar list for one dimension (angle / persona / industry / channel).
// Sorted by reply rate, top row highlighted as the winner; bar width is
// relative to the best in the panel. With showDelta, each row also shows its
// week-over-week movement so you see what's growing, not just what's ahead.
function InsightBar({ rows, showAngle, showSourced, showDelta, dim, onPick }) {
  const sorted = [...rows].sort((a, b) => b.replyRate - a.replyRate);
  const max = Math.max(...sorted.map((r) => r.replyRate), 0.0001);
  return (
    <div className="ins-rows">
      {sorted.map((r, i) => {
        const clickable = !!(dim && onPick);
        const inner = (
          <>
            <span className="ins-row-label" title={r.key}>{r.key}</span>
            <span className="ins-row-bar" aria-hidden="true"><i style={{ width: `${Math.round((r.replyRate / max) * 100)}%` }} /></span>
            <b className="ins-row-val">{pctText(r.replyRate)}</b>
            <span className="ins-row-sub">
              {showDelta && r.delta != null && (
                <span className={`ins-row-delta tone-${deltaTone(r.delta)}`} title="Week-over-week reply rate">{deltaArrow(r.delta)} {ptsText(r.delta)}</span>
              )}
              {pctText(r.meetingRate)} mtg · n={r.sent}
              {showAngle && r.bestAngle ? ` · ${r.bestAngle}` : ""}
              {showSourced && r.sourced ? ` · ${fmtMoney(r.sourced)}` : ""}
            </span>
            {clickable && <Icon name="arrow" size={12} className="ins-row-go" />}
          </>
        );
        const cls = `ins-row ${i === 0 ? "is-best" : ""} ${clickable ? "is-clickable" : ""}`;
        return clickable
          ? <button key={r.key} type="button" className={cls} onClick={() => onPick(dim, r.key)} title={`Analyze ${r.key}`}>{inner}</button>
          : <div key={r.key} className={cls}>{inner}</div>;
      })}
    </div>
  );
}

// One growth metric: current value, WoW delta chip, and a sparkline of the
// six-week trend. The "everything growth" headline strip.
function GrowthTile({ label, value, delta, deltaLabel, series, tone }) {
  return (
    <div className="grow-tile">
      <div className="grow-tile-top">
        <em>{label}</em>
        {delta != null && (
          <span className={`grow-delta tone-${deltaTone(delta)}`}>{deltaArrow(delta)} {deltaLabel}</span>
        )}
      </div>
      <b className="grow-tile-val">{value}</b>
      {series && <Sparkline data={series} height={26} color={tone === "down" ? "var(--warn)" : "var(--good)"} />}
    </div>
  );
}

// ---------- segment analysis (drill-down from Insights) ----------
// Clicking a row in Insights opens this read — the "why" behind the number —
// rather than dumping straight onto the board. The board handoff is the
// modal's single CTA. Trend series is derived from the row's current rate and
// WoW delta so the chart always agrees with the badge.
function SegmentAnalysis({ dim, val, cohort, onClose, onApply }) {
  const ins = (typeof OPENINGS_INSIGHTS !== "undefined" && OPENINGS_INSIGHTS) || window.OPENINGS_INSIGHTS;
  if (!ins) return null;
  const t = ins.totals;
  const blended = t.replies / t.sent;
  const dimRows = dim === "channel" ? ins.byChannel : dim === "persona" ? ins.byPersona : dim === "industry" ? ins.byIndustry : null;
  const row = dimRows ? dimRows.find((r) => r.key === val) : null;
  const fn = ins.funnel || [];
  const fi = fn.findIndex((s) => s.key === val);
  const step = fi >= 0 ? fn[fi] : null;
  const above = fi > 0 ? fn[fi - 1] : null;
  const conv = above ? Math.round((step.count / above.count) * 100) : null;

  const deltaPhrase = (r) => (r.delta > 0.001 ? `Growing ${ptsText(r.delta)} week-over-week — lean in.`
    : r.delta < -0.001 ? `Cooling ${ptsText(r.delta)} week-over-week — watch it.`
    : "Flat week-over-week.");
  const read = row
    ? (dim === "channel"
        ? (() => { const other = dimRows.find((r) => r.key !== val); return `First beats on ${val} convert at ${pctText(row.replyRate)} — ${row.replyRate >= blended ? "above" : "below"} the ${pctText(blended)} blended average${other ? `, ${row.replyRate >= other.replyRate ? "ahead of" : "behind"} ${other.key} (${pctText(other.replyRate)})` : ""}. ${deltaPhrase(row)}`; })()
        : dim === "persona"
          ? `${val} replies at ${pctText(row.replyRate)} vs ${pctText(blended)} blended${row.bestAngle ? ` — best paired with the “${row.bestAngle}” angle` : ""}. ${deltaPhrase(row)}`
          : `${val} replies at ${pctText(row.replyRate)} vs ${pctText(blended)} blended${row.sourced ? ` and has sourced ${fmtMoney(row.sourced)} of pipeline` : ""}. ${deltaPhrase(row)}`)
    : step
      ? (val === "Sourced"
          ? `${step.count} relationships matched the brief — the whole live board flows from here.`
          : `${step.count} of ${above ? `${above.count} ${above.key.toLowerCase()}` : "the cohort"} reached ${val.toLowerCase()} — ${conv != null ? `${conv}% step conversion` : ""}${step.note ? ` (${step.note})` : ""}.`)
      : null;

  // Six weeks of reply rate, anchored so last − prev = the WoW delta badge.
  const series = row ? [0, 1, 2, 3, 4, 5].map((i) => Math.max(0.005, row.replyRate - (row.delta || 0) * (5 - i))) : null;
  const tone = row ? deltaTone(row.delta || 0) : "flat";
  const ctaLabel = dim === "funnel" && val === "Graduated" ? "Open Deal Rhythm"
    : dim === "funnel" && val === "Sourced" ? "Open the board"
    : `See ${cohort.length} on the board`;

  return (
    <div className="modal-back" onClick={onClose}>
      <div className="modal ana" onClick={(e) => e.stopPropagation()} role="dialog" aria-modal="true" aria-label={`${val} analysis`}>
        <div className="modal-head">
          <div className="modal-status">
            <div className="modal-title-row"><h2 className="h2">{val}</h2></div>
            <div className="deal-meta-line">{dim === "funnel" ? "Funnel step" : `By ${dim}`} · Opener analysis</div>
          </div>
          <button className="btn sm ghost" onClick={onClose} aria-label="Close analysis"><Icon name="close" size={14} /></button>
        </div>
        <div className="modal-body">
          <div className="ana-stats">
            {row ? (
              <>
                <div className="ana-stat"><em>Reply rate</em><b>{pctText(row.replyRate)}</b>{row.delta != null && <span className={`ins-row-delta tone-${deltaTone(row.delta)}`}>{deltaArrow(row.delta)} {ptsText(row.delta)}</span>}</div>
                <div className="ana-stat"><em>Meeting rate</em><b>{pctText(row.meetingRate)}</b></div>
                <div className="ana-stat"><em>First beats</em><b>{row.sent}</b></div>
                {row.sourced != null && <div className="ana-stat"><em>Sourced</em><b>{fmtMoney(row.sourced)}</b></div>}
              </>
            ) : step ? (
              <>
                <div className="ana-stat"><em>Reached</em><b>{step.count}</b></div>
                <div className="ana-stat"><em>Step conversion</em><b>{conv != null ? `${conv}%` : "—"}</b></div>
                <div className="ana-stat"><em>Of sourced</em><b>{Math.round((step.count / (fn[0]?.count || step.count)) * 100)}%</b></div>
              </>
            ) : null}
          </div>
          {series && (
            <div className="ana-trend">
              <em className="lead-sec-label">Reply rate · six weeks</em>
              <Sparkline data={series} height={30} color={tone === "down" ? "var(--warn)" : tone === "up" ? "var(--good)" : "var(--cool)"} />
            </div>
          )}
          {read && (
            <div className="ana-sec">
              <em className="lead-sec-label">The read</em>
              <p className="ana-read">{read}</p>
            </div>
          )}
          <div className="ana-sec">
            <em className="lead-sec-label">On the board now ({cohort.length})</em>
            {cohort.length ? (
              <div className="ana-cohort">
                {cohort.map((o) => (
                  <span key={o.id} className="ana-lead">
                    <span className="ana-lead-co">{o.company}</span>
                    <span className="ana-lead-stage">{stageById(o.__stage).label}</span>
                    {o.__stage === "booked" && leadValue(o) > 0 && <b>{fmtMoney(leadValue(o))}</b>}
                  </span>
                ))}
              </div>
            ) : (
              <p className="ana-read">{dim === "funnel" && val === "Graduated" ? "Graduated relationships live in Deal Rhythm." : "None live on the board right now."}</p>
            )}
          </div>
          <button type="button" className="ana-cta" onClick={() => onApply(dim, val)}>
            {ctaLabel} <Icon name="arrow" size={13} />
          </button>
        </div>
      </div>
    </div>
  );
}

function OpeningsInsights({ onPick }) {
  const ins = (typeof OPENINGS_INSIGHTS !== "undefined" && OPENINGS_INSIGHTS) || window.OPENINGS_INSIGHTS;
  if (!ins) return null;
  const t = ins.totals;
  const replyRate = t.replies / t.sent;
  const angles = [...ins.byAngle].sort((a, b) => b.replyRate - a.replyRate);
  const top = angles[0];
  const worst = angles[angles.length - 1];
  const multiple = worst.replyRate ? Math.round(top.replyRate / worst.replyRate) : null;

  // Week-over-week deltas off the trend series (last vs prev week).
  const tr = ins.trend || { weeks: [], sent: [], replyRate: [], meetings: [], sourced: [] };
  const last = (a) => a[a.length - 1];
  const prev = (a) => a[a.length - 2];
  const d = (a) => (a.length > 1 ? last(a) - prev(a) : 0);
  const dRR = d(tr.replyRate), dSent = d(tr.sent), dMtg = d(tr.meetings), dSrc = d(tr.sourced);

  // Biggest growth mover across every dimension — what to double down on.
  const movers = [
    ...ins.byAngle.map((r) => ({ ...r, dim: "angle" })),
    ...ins.byPersona.map((r) => ({ ...r, dim: "persona" })),
    ...ins.byIndustry.map((r) => ({ ...r, dim: "industry" })),
  ].filter((r) => r.delta != null).sort((a, b) => b.delta - a.delta);
  const mover = movers[0];
  const cooling = movers[movers.length - 1];

  // Funnel — conversion at each step relative to the one above it.
  const fn = ins.funnel || [];
  const fnMax = Math.max(...fn.map((s) => s.count), 1);

  return (
    <div className="ins">
      {/* Growth strip — momentum at a glance, the "everything growth" headline. */}
      <div className="grow-strip">
        <GrowthTile label="Reply rate" value={pctText(last(tr.replyRate) || replyRate)} delta={dRR} deltaLabel={ptsText(dRR)} series={tr.replyRate} />
        <GrowthTile label="Opens / week" value={String(last(tr.sent) || 0)} delta={dSent} deltaLabel={`${dSent > 0 ? "+" : ""}${dSent}`} series={tr.sent} />
        <GrowthTile label="Meetings / week" value={String(last(tr.meetings) || 0)} delta={dMtg} deltaLabel={`${dMtg > 0 ? "+" : ""}${dMtg}`} series={tr.meetings} />
        <GrowthTile label="Sourced / week" value={fmtMoney(last(tr.sourced) || 0)} delta={dSrc} deltaLabel={`${dSrc >= 0 ? "+" : "−"}${fmtMoney(Math.abs(dSrc))}`} series={tr.sourced} tone={dSrc < 0 ? "down" : "up"} />
      </div>

      {/* What's growing — the single highest-leverage callout. */}
      {mover && (
        <div className="grow-callout">
          <span className="grow-callout-ic" aria-hidden="true"><Icon name="trend" size={14} /></span>
          <span className="grow-callout-text">
            <b>{mover.key}</b> is your fastest-growing {mover.dim} — <span className="tone-up">{deltaArrow(mover.delta)} {ptsText(mover.delta)} WoW</span> on reply rate. Double down.
            {cooling && cooling.delta < 0 && <> &nbsp;·&nbsp; <b>{cooling.key}</b> is cooling (<span className="tone-down">{deltaArrow(cooling.delta)} {ptsText(cooling.delta)}</span>).</>}
          </span>
        </div>
      )}

      <div className="ins-grid">
        <section className="ins-panel ins-panel-wide">
          <header className="ins-panel-head">
            <h3>What message works</h3>
            {multiple && <span className="ins-panel-note">“{top.key}” converts {multiple}× the cold-no-trigger baseline</span>}
          </header>
          <InsightBar rows={ins.byAngle} showDelta />
          <div className="ins-best-msg">
            <em>Top-performing opener</em>
            <p>“{top.example}”</p>
          </div>
        </section>

        {/* Conversion funnel — sourced relationships down to graduated deals.
           Rows open the step's analysis (stats, read, live cohort); the board
           handoff is the modal's CTA. */}
        <section className="ins-panel">
          <header className="ins-panel-head"><h3>Conversion funnel</h3><span className="ins-panel-hint">tap to analyze</span></header>
          <div className="ins-funnel">
            {fn.map((s, i) => {
              const above = i > 0 ? fn[i - 1].count : null;
              const conv = above ? Math.round((s.count / above) * 100) : null;
              return (
                <button key={s.key} type="button" className="fn-row is-clickable" onClick={() => onPick && onPick("funnel", s.key)} title={`Analyze ${s.key}`}>
                  <span className="fn-label">{s.key}</span>
                  <span className="fn-bar" aria-hidden="true"><i style={{ width: `${Math.round((s.count / fnMax) * 100)}%` }} /></span>
                  <b className="fn-count">{s.count}</b>
                  <span className="fn-conv">{conv != null ? `${conv}%` : "—"}</span>
                </button>
              );
            })}
          </div>
        </section>

        {ins.byChannel && (
          <section className="ins-panel">
            <header className="ins-panel-head"><h3>By channel</h3><span className="ins-panel-hint">tap to analyze</span></header>
            <InsightBar rows={ins.byChannel} showDelta dim="channel" onPick={onPick} />
          </section>
        )}

        <section className="ins-panel">
          <header className="ins-panel-head"><h3>By persona</h3><span className="ins-panel-hint">tap to analyze</span></header>
          <InsightBar rows={ins.byPersona} showAngle showDelta dim="persona" onPick={onPick} />
        </section>

        <section className="ins-panel">
          <header className="ins-panel-head"><h3>By industry</h3><span className="ins-panel-hint">tap to analyze</span></header>
          <InsightBar rows={ins.byIndustry} showSourced showDelta dim="industry" onPick={onPick} />
        </section>
      </div>
    </div>
  );
}

// ---------- reply inbox ----------
function LeadInbox({ leads, onReplyHandled }) {
  if (!leads.length) {
    return <div className="inbox-empty">No replies yet — the Opener surfaces them here the moment a prospect responds.</div>;
  }
  return (
    <div className="inbox">
      {leads.map((o) => {
        const r = o.reply || { text: o.movementLabel, classification: "Reply", suggestedResponse: "" };
        return (
          <article key={o.id} className="inbox-row">
            <div className="inbox-id">
              <span className="lead-mono tone-info" aria-hidden="true">{leadInitials(o.company)}</span>
              <div className="lead-id-text">
                <b className="lead-company">{o.company}</b>
                <span className="lead-prospect">{o.prospect.name} · {o.prospect.role}</span>
              </div>
              <span className={`inbox-class is-${(r.classification || "reply").toLowerCase().replace(/\s+/g, "-")}`}>{r.classification}</span>
            </div>
            <blockquote className="inbox-reply">“{r.text}”</blockquote>
            {r.rebuttal && (
              <div className="inbox-rebuttal"><em>Objection handling</em> {r.rebuttal}</div>
            )}
            <div className="inbox-draft">
              <div className="lead-research-head">
                <em className="lead-sec-label">Opener drafted reply</em>
                <span className="lead-draft-badge"><Icon name="sparkles" size={10} /> grounded in thread</span>
              </div>
              <p className="inbox-draft-body">{r.suggestedResponse}</p>
            </div>
            <div className="inbox-actions">
              <button type="button" className="btn sm accent" onClick={() => replyToLead(o, onReplyHandled)}>Review &amp; send <Icon name="arrow" size={12} /></button>
              <button type="button" className="btn sm ghost" onClick={() => window.fireToast && window.fireToast(`Snoozed ${o.company} — Opener will follow up`)}>Snooze</button>
            </div>
          </article>
        );
      })}
    </div>
  );
}

// ---------- the board ----------
function OpeningsScreen({ showPageHeader = true }) {
  const openings = (typeof OPENINGS !== "undefined" && OPENINGS) || window.OPENINGS || [];
  // Live pipeline state (stage transitions, graduated, autonomy) lives in the
  // OpeningsStore — persisted, shared with the home cockpit, and the platform
  // half of the 2-way Slack sync. Subscribing re-renders on any change,
  // including transitions that arrive from a Slack command.
  const store = window.NudgeOpenings;
  store && store.useOpenings && store.useOpenings();
  const autonomy = store ? store.getAutonomy() : "review";
  const [openId, setOpenId] = useState(null);       // lead room
  const [view, setView] = useState("board");        // board | insights | inbox
  const [filter, setFilter] = useState(null);       // { dim:"persona"|"industry"|"channel"|"funnel", val }
  const [analysis, setAnalysis] = useState(null);   // { dim, val } — segment drill-down modal
  const [briefOpen, setBriefOpen] = useState(false);
  const srcg = (typeof OPENER_SOURCING !== "undefined" && OPENER_SOURCING) || window.OPENER_SOURCING || null;

  const segmentMatch = (o, dim, val) => {
    if (dim === "persona") return personaGroup(o.prospect.role) === val;
    if (dim === "industry") return leadIndustry(o) === val;
    if (dim === "channel") return ((window.CHANNEL_LABEL || {})[o.channel] || o.channel) === val;
    if (dim === "funnel") {
      const set = FUNNEL_STAGE_SETS[val];
      return !set || set.includes(effectiveStage(o));
    }
    return true;
  };
  const matchesFilter = (o) => !filter || segmentMatch(o, filter.dim, filter.val);
  // Insights rows open the analysis first; the board handoff is the modal CTA.
  const pickSegment = (dim, val) => setAnalysis({ dim, val });
  const applySegment = (dim, val) => {
    setAnalysis(null);
    // Funnel endpoints that don't map to a board filter: "Sourced" is the
    // whole board, "Graduated" leads live in Deal Rhythm.
    if (dim === "funnel" && val === "Graduated") { window.goToRoute && window.goToRoute("deals"); return; }
    if (dim === "funnel" && val === "Sourced") { setFilter(null); setView("board"); return; }
    setFilter({ dim, val });
    setView("board");
  };
  // Transitions go through the store, which persists + mirrors them to Slack.
  const setAutonomy = (mode) => store && store.setAutonomy(mode);
  const onSent = (id) => store && store.recordLocal(id, "approve");
  const onBooked = (id) => store && store.recordLocal(id, "book");
  const onGraduated = (id) => store && store.recordLocal(id, "graduate");
  const isGraduated = (id) => (store ? store.isGraduated(id) : false);

  // Effective stage = transition override → seed stage, with the autonomy gate
  // applied (handled inside the store).
  const effectiveStage = (o) => (store ? store.effectiveStage(o) : o.stage);

  // Live cohort behind the open analysis modal (matches what its board CTA shows).
  const analysisCohort = analysis
    ? (analysis.dim === "funnel" && analysis.val === "Graduated"
        ? openings.filter((o) => isGraduated(o.id)).map((o) => ({ ...o, __stage: "booked" }))
        : openings
            .filter((o) => segmentMatch(o, analysis.dim, analysis.val))
            .map((o) => ({ ...o, __stage: effectiveStage(o) }))
            .sort((a, b) => (b.intentScore || 0) - (a.intentScore || 0)))
    : [];

  // Board columns honor the active segment filter. No deal exists yet, so
  // there's no pipeline value to rank on — highest-intent leads first, so the
  // board reads top-down by who's most likely to respond.
  const byStage = (stageId) =>
    openings
      .filter((o) => effectiveStage(o) === stageId && matchesFilter(o))
      .sort((a, b) => (b.intentScore || 0) - (a.intentScore || 0));
  const byStageAll = (stageId) => openings.filter((o) => effectiveStage(o) === stageId);
  const needsYou = byStageAll("needs-review").length;
  const firstNeedsYou = byStageAll("needs-review")[0];
  const repliedLeads = byStageAll("replied");
  // AI-SDR output — the engine's campaign-to-date production. There is no deal
  // and no pipeline $ until a lead books a meeting and graduates, so the hero
  // counts engagement (worked → replied → booked → graduated), never dollars.
  const ins = (typeof OPENINGS_INSIGHTS !== "undefined" && OPENINGS_INSIGHTS) || window.OPENINGS_INSIGHTS || null;
  const fn = (ins && ins.funnel) || [];
  const fcount = (k) => { const r = fn.find((s) => s.key === k); return r ? r.count : 0; };
  const output = [
    { val: fcount("Opened") || openings.length, cap: "leads worked" },
    { val: fcount("Replied"), cap: "replied" },
    { val: fcount("Booked"), cap: "meetings booked", tone: "good" },
    { val: fcount("Graduated"), cap: "graduated into Deal Rhythm", tone: "good" },
  ];

  return (
    <div className="scroll" data-screen-label="Openings" data-tour="openings-page">
      {showPageHeader && (
        <>
          <div className="topbar"><Brand /></div>
          <header className="platform-page-header">
            <div className="platform-page-copy">
              <span className="platform-eyebrow">
                <span className="cp-eyebrow-dot cool" aria-hidden />
                Opener · AI SDR
              </span>
              <h1 className="platform-page-title">Openings</h1>
              <p className="platform-page-subtitle">
                Every lead the Opener is working, by stage — signal to booked. The automated steps run themselves; the only thing that earns color is prospect engagement. A booked meeting graduates into Deal Rhythm — where a deal actually begins.
              </p>
            </div>
          </header>
        </>
      )}

      <div className="openings-hero">
        <div className="openings-hero-main">
          <div className="openings-hero-output" role="group" aria-label="Opener output">
            {output.map((s, i) => (
              <React.Fragment key={s.cap}>
                {i > 0 && <span className="openings-hero-arrow" aria-hidden="true"><Icon name="arrow" size={12} /></span>}
                <div className={`openings-hero-stat ${s.tone ? `is-${s.tone}` : ""}`}>
                  <b className="openings-hero-stat-val">{s.val}</b>
                  <span className="openings-hero-stat-cap">{s.cap}</span>
                </div>
              </React.Fragment>
            ))}
          </div>
          {srcg && (
            <div className="openings-sourcing">
              <Icon name="sparkles" size={12} />
              <span>Opener sourced <b>{srcg.sourcedThisWeek}</b> this week · {srcg.enriched} enriched · {srcg.queuedResearch} queued</span>
              <button type="button" className="openings-source-more" onClick={() => window.fireToast && window.fireToast("Opener is sourcing more leads matching your brief…")}>Source more</button>
            </div>
          )}
        </div>

        <div className="openings-hero-side">
          {firstNeedsYou && (
            <button className="btn accent openings-hero-cta" onClick={() => setOpenId(firstNeedsYou.id)}>
              Review {needsYou} draft{needsYou > 1 ? "s" : ""} <Icon name="arrow" size={13} />
            </button>
          )}
          {(() => {
            const acct = window.OPENER_CHANNEL_ACCOUNTS;
            const mbx = (typeof OPENER_MAILBOXES !== "undefined" && OPENER_MAILBOXES) || window.OPENER_MAILBOXES;
            if (!acct) return null;
            return (
              <div className="openings-conn" title="The Opener reaches out as you, from your connected accounts">
                <em className="openings-conn-label">Sends as you</em>
                {acct.linkedin && acct.linkedin.connected && (
                  <span className="openings-conn-item">
                    <i className="openings-conn-dot" aria-hidden="true" /> LinkedIn · {acct.linkedin.profile}
                  </span>
                )}
                {acct.email && acct.email.connected && (
                  <span className="openings-conn-item">
                    <i className="openings-conn-dot" aria-hidden="true" /> Email · {mbx ? `${mbx.inboxes.length} inboxes` : "connected"}
                  </span>
                )}
              </div>
            );
          })()}
        </div>
      </div>

      <div className="openings-tabs" role="tablist" aria-label="Openings view">
        <button type="button" role="tab" aria-selected={view === "board"} className={`openings-tab ${view === "board" ? "is-active" : ""}`} onClick={() => setView("board")}>
          <Icon name="grid" size={13} /> Board
        </button>
        <button type="button" role="tab" aria-selected={view === "insights"} className={`openings-tab ${view === "insights" ? "is-active" : ""}`} onClick={() => setView("insights")}>
          <Icon name="trend" size={13} /> Insights
        </button>
        <button type="button" role="tab" aria-selected={view === "inbox"} className={`openings-tab ${view === "inbox" ? "is-active" : ""}`} onClick={() => setView("inbox")}>
          <Icon name="mail" size={13} /> Inbox{repliedLeads.length > 0 && <span className="openings-tab-count">{repliedLeads.length}</span>}
        </button>

        <div className="openings-toolbar">
          {filter && view === "board" && (
            <span className="openings-filter-chip">
              <em>{filter.dim}</em>
              {filter.dim === "funnel" ? `reached ${filter.val}` : filter.val}
              <button type="button" onClick={() => setFilter(null)} aria-label="Clear filter"><Icon name="close" size={11} /></button>
            </span>
          )}
          <button type="button" className="openings-brief-btn" onClick={() => setBriefOpen(true)}>
            <Icon name="gear" size={13} /> Brief
          </button>
        </div>
      </div>

      {view === "board" && (
        <div className="openings-controls">
          <div className="autonomy-control">
            <span className="autonomy-control-label">Opener autonomy</span>
            <div className="autonomy" role="tablist" aria-label="Send autonomy">
              {AUTONOMY_MODES.map((m) => (
                <button
                  key={m.id}
                  role="tab"
                  aria-selected={autonomy === m.id}
                  className={`autonomy-opt ${autonomy === m.id ? "is-active" : ""}`}
                  title={(TRUST_TIERS && TRUST_TIERS[m.tier] && TRUST_TIERS[m.tier].desc) || m.short}
                  onClick={() => setAutonomy(m.id)}
                >
                  {m.label}
                </button>
              ))}
            </div>
            <span className="autonomy-control-desc">{(AUTONOMY_MODES.find((m) => m.id === autonomy) || {}).short}</span>
          </div>
        </div>
      )}

      {view === "insights" && <OpeningsInsights onPick={pickSegment} />}

      {view === "inbox" && <LeadInbox leads={repliedLeads} onReplyHandled={onBooked} />}

      {view === "board" && (
      <div className="lead-bands">
        {LEAD_BANDS.map((band) => (
          <section key={band.id} className={`lead-band band-${band.id} ${band.primary ? "is-primary" : ""}`}>
            <header className="lead-band-head">
              <span className="lead-band-label">{band.label}</span>
              <span className="lead-band-sub">{band.sub}</span>
            </header>
            <div className="lead-band-cols">
              {band.stages.map((stageId) => {
                const st = stageById(stageId);
                const items = byStage(st.id);
                // Dollars only exist once a meeting is booked (a deal is forming);
                // earlier columns lead with count, never a pipeline figure.
                const colVal = st.id === "booked" ? items.reduce((s, o) => s + leadValue(o), 0) : 0;
                const isAction = st.id === "needs-review" && items.length > 0;
                const isEmpty = items.length === 0;
                return (
                  <section key={st.id} className={`lead-col tone-${st.tone} ${isAction ? "is-action" : ""} ${isEmpty ? "is-empty" : ""}`}>
                    <header className="lead-col-head">
                      <div className="lead-col-head-top">
                        <span className="lead-col-title">{st.label}</span>
                        <span className="lead-col-count">{items.length}</span>
                        {colVal > 0 && <span className="lead-col-val">{fmtMoney(colVal)}</span>}
                      </div>
                      <span className="lead-col-hint">{st.hint}</span>
                    </header>
                    <div className="lead-col-body">
                      {isEmpty ? (
                        <div className="lead-col-empty">
                          {st.id === "needs-review" && autonomy !== "review"
                            ? "Auto-sending"
                            : "—"}
                        </div>
                      ) : (
                        items.map((o) => (
                          <LeadCard
                            key={o.id}
                            o={o}
                            stage={st.id}
                            graduated={isGraduated(o.id)}
                            fromSlack={store ? store.cameFromSlack(o.id) : false}
                            onOpen={setOpenId}
                          />
                        ))
                      )}
                    </div>
                  </section>
                );
              })}
            </div>
          </section>
        ))}
      </div>
      )}

      {view === "board" && (
        <div className="op-infra">
          {typeof window.OpenerMailboxes === "function" && <window.OpenerMailboxes />}
          {typeof window.OpenerActivity === "function" && <window.OpenerActivity />}
        </div>
      )}

      {briefOpen && typeof window.OpenerBrief === "function" && <window.OpenerBrief onClose={() => setBriefOpen(false)} />}

      {analysis && (
        <SegmentAnalysis
          dim={analysis.dim}
          val={analysis.val}
          cohort={analysisCohort}
          onClose={() => setAnalysis(null)}
          onApply={applySegment}
        />
      )}

      {(() => {
        const lead = openings.find((x) => x.id === openId);
        if (!lead) return null;
        return (
          <LeadRoom
            o={lead}
            stage={effectiveStage(lead)}
            graduated={isGraduated(lead.id)}
            onSent={onSent}
            onBooked={onBooked}
            onGraduated={onGraduated}
            onClose={() => setOpenId(null)}
          />
        );
      })()}
    </div>
  );
}

window.OpeningsScreen = OpeningsScreen;
// Effective stage helper — reads the OpeningsStore overlay so the home cockpit
// and sidebar reflect live transitions (incl. ones from Slack), not seed data.
const effStage = (x) => (window.NudgeOpenings ? window.NudgeOpenings.effectiveStage(x) : x.stage);
window.openingsCounts = () => {
  const o = window.OPENINGS || [];
  return {
    awaiting: o.filter((x) => effStage(x) === "needs-review").length,
    engaged: o.filter((x) => ["replied", "booked"].includes(effStage(x))).length,
    standDown: o.filter((x) => effStage(x) === "disqualified").length,
  };
};
// AI-SDR output read for the home cockpit widget. No deal exists until a lead
// books and graduates, so this reports engagement counts — and the only dollar
// figure is booked pipeline (the first point a real deal is forming).
window.fmtLeadMoney = fmtMoney;
window.openingsPipeline = () => {
  const o = window.OPENINGS || [];
  const active = o.filter((x) => effStage(x) !== "disqualified");
  const cnt = (st) => o.filter((x) => effStage(x) === st).length;
  const graduated = window.NudgeOpenings
    ? o.filter((x) => window.NudgeOpenings.isGraduated(x.id)).length
    : 0;
  const bookedVal = o.filter((x) => effStage(x) === "booked")
    .reduce((s, x) => s + leadValue(x), 0);
  return {
    leadsWorked: active.length,
    needsYou: cnt("needs-review"),
    replied: cnt("replied"),
    booked: cnt("booked"),
    graduated,
    // The only dollars allowed pre-graduation: pipeline from booked meetings.
    bookedVal,
  };
};
