/* pipeline.jsx — Full Kanban pipeline page (P0 rewrite).
 * Requires: app.jsx, email-store.jsx, email-composer.jsx
 */

/* ── Local icon helpers ──────────────────────────────────────────────────── */
const IcMail     = (p) => (<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" {...p}><rect x="2" y="4" width="20" height="16" rx="2"/><path d="m2 7 10 7 10-7"/></svg>);
const IcPhone    = (p) => (<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" {...p}><path d="M22 16.9v3a2 2 0 0 1-2.2 2 19.8 19.8 0 0 1-8.6-3.1 19.5 19.5 0 0 1-6-6A19.8 19.8 0 0 1 2.1 4.2 2 2 0 0 1 4.1 2h3a2 2 0 0 1 2 1.7c.1 1 .4 2 .7 2.9a2 2 0 0 1-.5 2.1l-1.3 1.3a16 16 0 0 0 6 6l1.3-1.3a2 2 0 0 1 2.1-.5c.9.3 1.9.6 2.9.7A2 2 0 0 1 22 16.9z"/></svg>);
const IcMapPin   = (p) => (<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" {...p}><path d="M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0z"/><circle cx="12" cy="10" r="3"/></svg>);
const IcFileText = (p) => (<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" {...p}><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><path d="M14 2v6h6M16 13H8M16 17H8M10 9H8"/></svg>);
const IcTrash    = (p) => (<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" {...p}><path d="M3 6h18M19 6l-1 14H6L5 6M10 11v6M14 11v6M9 6V4h6v2"/></svg>);
const IcUser     = (p) => (<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" {...p}><path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/></svg>);
const IcMoreH    = (p) => (<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" {...p}><circle cx="5" cy="12" r="1"/><circle cx="12" cy="12" r="1"/><circle cx="19" cy="12" r="1"/></svg>);
const IcArrowRight = (p) => (<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" {...p}><path d="M5 12h14M12 5l7 7-7 7"/></svg>);
const IcLinkedIn = (p) => (<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" {...p}><path d="M16 8a6 6 0 0 1 6 6v7h-4v-7a2 2 0 0 0-2-2 2 2 0 0 0-2 2v7h-4v-7a6 6 0 0 1 6-6z"/><rect x="2" y="9" width="4" height="12"/><circle cx="4" cy="4" r="2"/></svg>);

/* ── Stage definitions ───────────────────────────────────────────────────── */

const KN_STAGES = [
  { id: "intro_screening", label: "Intro screening", hint: "30-min recruiter call",        accent: "var(--neutral-60)" },
  { id: "hiring_manager",  label: "Hiring manager",  hint: "First leadership interview",   accent: "var(--indigo-50)" },
  { id: "technical_test",  label: "Technical test",  hint: "Take-home or paired exercise", accent: "var(--violet-50)" },
  { id: "offer",           label: "Offer",           hint: "Offer extended",                accent: "var(--yellow-50)" },
  { id: "hired",           label: "Hired",           hint: "Signed and starting",           accent: "var(--green-50)" },
  { id: "rejected",        label: "Rejected",        hint: "Closed out of pipeline",        accent: "var(--red-50)" },
];

const KN_STAGE_MAP = {};
KN_STAGES.forEach(s => { KN_STAGE_MAP[s.id] = s; });

// Drawer-only stage map — includes "applied" so candidates triaged from the
// Candidates page can be moved into the pipeline. Not shown as a Kanban column.
KN_STAGE_MAP["applied"] = { id: "applied", label: "Applied", hint: "Awaiting screening", accent: "var(--neutral-40)" };

const CANONICAL_TO_STAGE_ID = {
  "Applied":   "applied",
  "Screening": "intro_screening",
  "Interview": "hiring_manager",
  "Take-home": "technical_test",
  "Offer":     "offer",
  "Hired":     "hired",
  "Rejected":  "rejected",
};

const STAGE_BG = {
  intro_screening: "var(--neutral-20)",
  hiring_manager:  "var(--indigo-10)",
  technical_test:  "var(--violet-10)",
  offer:           "var(--yellow-10)",
  hired:           "var(--green-10)",
  rejected:        "var(--red-10)",
};

const scoreClass = (s) => s >= 75 ? "high" : s >= 50 ? "mid" : "low";

/* ── Departments list (for new posting modal) ───────────────────────────── */

const DEPARTMENTS_LIST = [
  { id: "dept_eng",     name: "Engineering" },
  { id: "dept_design",  name: "Design" },
  { id: "dept_product", name: "Product" },
  { id: "dept_ops",     name: "Operations & Compliance" },
  { id: "dept_growth",  name: "Growth & Marketing" },
  { id: "dept_partner", name: "Partnerships" },
];

/* ── KnMoveMenu — kebab popover ─────────────────────────────────────────── */

function KnMoveMenu({ opp, onMove, onReject, onClose }) {
  const { useEffect, useRef } = React;
  const ref = useRef(null);

  useEffect(() => {
    const fn = (e) => { if (ref.current && !ref.current.contains(e.target)) onClose(); };
    document.addEventListener("mousedown", fn);
    return () => document.removeEventListener("mousedown", fn);
  }, [onClose]);

  const visibleStages = KN_STAGES.filter(s => s.id !== opp.stageId && s.id !== "rejected");

  return (
    <div ref={ref} className="kn-move-menu">
      <div className="kn-move-menu-section">Move to</div>
      {visibleStages.map(s => (
        <button key={s.id} className="kn-move-menu-item"
          onClick={(e) => { e.stopPropagation(); onMove(opp, s.id); onClose(); }}>
          <span className="kn-move-menu-dot" style={{ background: s.accent }} />
          {s.label}
        </button>
      ))}
      <div className="kn-move-menu-divider" />
      <button className="kn-move-menu-item kn-move-menu-item--danger"
        onClick={(e) => { e.stopPropagation(); onReject(opp); onClose(); }}>
        <IcTrash width="13" height="13" />
        Reject
      </button>
    </div>
  );
}

/* ── KnCard ──────────────────────────────────────────────────────────────── */

function KnCard({ opp, isDragging, onDragStart, onDragEnd, onClick, onMove, onReject }) {
  const { useState, useRef } = React;
  const [menuOpen, setMenuOpen] = useState(false);

  const tone = scoreClass(opp.score || 0);
  const scoreColor = tone === "high" ? "var(--green-70)" : tone === "mid" ? "var(--indigo-50)" : "var(--neutral-60)";

  return (
    <div
      className={`kn-card${isDragging ? " kn-card--dragging" : ""}`}
      draggable
      onDragStart={onDragStart}
      onDragEnd={onDragEnd}
      onClick={onClick}
      style={{ position: "relative" }}
    >
      {/* header row */}
      <div className="kn-card-head">
        <Avatar name={opp.name} size={32} tone="auto" />
        <div className="kn-card-info">
          <div className="kn-card-name">{opp.name}</div>
          <div className="kn-card-role">{opp.roleLabel || opp.role || ""}</div>
        </div>
        <div className="kn-card-score" style={{ color: scoreColor }}>{opp.score}</div>
        <button
          className="icon-btn kn-card-menu-btn"
          style={{ width: 28, height: 28 }}
          onClick={(e) => { e.stopPropagation(); setMenuOpen(v => !v); }}
        >
          <IcMoreH width="14" height="14" />
        </button>
      </div>

      {/* time + source */}
      <div className="kn-card-meta">
        <span className="kn-card-meta-item">
          <I.Clock width="11" height="11" />
          {opp.lastTouch || "—"}
        </span>
        {opp.loc && (
          <span className="kn-card-meta-item">
            <IcMapPin width="11" height="11" />
            {opp.loc}
          </span>
        )}
      </div>

      {/* reason tags */}
      {opp.reasons && opp.reasons.length > 0 && (
        <div className="kn-card-tags">
          {opp.reasons.slice(0, 2).map((r, i) => (
            <span key={i} className="kn-card-tag">{r}</span>
          ))}
        </div>
      )}

      {/* kebab menu */}
      {menuOpen && (
        <div style={{ position: "absolute", top: 36, right: 8, zIndex: 20 }}
          onClick={(e) => e.stopPropagation()}>
          <KnMoveMenu
            opp={opp}
            onMove={onMove}
            onReject={onReject}
            onClose={() => setMenuOpen(false)}
          />
        </div>
      )}
    </div>
  );
}

/* ── KnColumn ────────────────────────────────────────────────────────────── */

function KnColumn({ stage, opps, dragOver, draggingId, onDragOverCol, onDragLeaveCol, onDropCol, onDragStartCard, onDragEndCard, onCardClick, onMove, onReject }) {
  const count = opps.length;
  const isDragTarget = dragOver === stage.id;
  const stageAccent = stage.accent;

  return (
    <div
      className={`kn-col${isDragTarget ? " kn-col--drag-over" : ""}`}
      style={{ "--col-accent": stageAccent }}
      onDragOver={(e) => { e.preventDefault(); onDragOverCol(stage.id); }}
      onDragLeave={() => onDragLeaveCol(stage.id)}
      onDrop={(e) => { e.preventDefault(); onDropCol(stage.id); }}
    >
      {/* column header */}
      <div className="kn-col-head">
        <span className="kn-col-dot" style={{ background: stage.accent }} />
        <span className="kn-col-label">{stage.label}</span>
        <span className="kn-col-count">{count}</span>
      </div>
      <div className="kn-col-hint">{stage.hint}</div>

      {/* drop zone */}
      <div className="kn-col-body">
        {opps.map(opp => (
          <KnCard
            key={opp.id}
            opp={opp}
            isDragging={draggingId === opp.id}
            onDragStart={(e) => { e.stopPropagation(); onDragStartCard(e, opp); }}
            onDragEnd={onDragEndCard}
            onClick={() => onCardClick(opp)}
            onMove={onMove}
            onReject={onReject}
          />
        ))}
        {count === 0 && (
          <div className="kn-col-empty">
            {isDragTarget ? "Drop here to move" : "No candidates"}
          </div>
        )}
      </div>
    </div>
  );
}

/* ── PipelineDrawer ──────────────────────────────────────────────────────── */

/* Rating helpers — shared between the feedback list and the Add form */
const RATINGS = [
  { id: "strong_yes", label: "Strong yes", tone: "var(--green-70)",  bg: "var(--green-20)"  },
  { id: "yes",        label: "Yes",        tone: "var(--green-60)",  bg: "var(--green-10)"  },
  { id: "mixed",      label: "Mixed",      tone: "var(--yellow-70)", bg: "var(--yellow-10)" },
  { id: "no",         label: "No",         tone: "var(--red-60)",    bg: "var(--red-10)"    },
  { id: "strong_no",  label: "Strong no",  tone: "var(--red-70)",    bg: "var(--red-20)"    },
];
const ratingMeta = (id) => RATINGS.find(r => r.id === id) || RATINGS[2];

function AddFeedbackForm({ onCancel, onSubmit }) {
  const [rating, setRating] = React.useState("yes");
  const [notes,  setNotes]  = React.useState("");
  const [strengthsRaw, setStrengthsRaw] = React.useState("");
  const [concernsRaw,  setConcernsRaw]  = React.useState("");
  const [submitting, setSubmitting] = React.useState(false);

  const handleSubmit = () => {
    if (!notes.trim() || submitting) return;
    setSubmitting(true);
    const strengths = strengthsRaw.split("\n").map(s => s.trim()).filter(Boolean);
    const concerns  = concernsRaw .split("\n").map(s => s.trim()).filter(Boolean);
    onSubmit({ rating, notes: notes.trim(), strengths, concerns })
      .catch(err => { console.error("[ATS] add feedback failed", err); alert("Feedback failed to save — see console."); })
      .finally(() => setSubmitting(false));
  };

  return (
    <div style={{ padding: 12, border: "1px solid var(--stroke-secondary)", borderRadius: 10, background: "var(--bg-primary)", marginBottom: 10 }}>
      <div style={{ display: "flex", gap: 6, flexWrap: "wrap", marginBottom: 10 }}>
        {RATINGS.map(r => (
          <button
            key={r.id}
            type="button"
            onClick={() => setRating(r.id)}
            style={{
              padding: "4px 10px", borderRadius: 100, fontSize: 11, fontWeight: 600, cursor: "pointer",
              border: `1px solid ${rating === r.id ? r.tone : "var(--stroke-secondary)"}`,
              background: rating === r.id ? r.bg : "transparent",
              color: rating === r.id ? r.tone : "var(--content-secondary)",
            }}
          >{r.label}</button>
        ))}
      </div>
      <textarea
        value={notes}
        onChange={(e) => setNotes(e.target.value)}
        placeholder="What's your read on this candidate?"
        rows={3}
        style={{ width: "100%", padding: 8, borderRadius: 6, border: "1px solid var(--stroke-secondary)", fontSize: 13, fontFamily: "var(--font-sans)", marginBottom: 8 }}
      />
      <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 8, marginBottom: 10 }}>
        <textarea
          value={strengthsRaw}
          onChange={(e) => setStrengthsRaw(e.target.value)}
          placeholder="Strengths (one per line)"
          rows={3}
          style={{ width: "100%", padding: 8, borderRadius: 6, border: "1px solid var(--stroke-secondary)", fontSize: 12, fontFamily: "var(--font-sans)" }}
        />
        <textarea
          value={concernsRaw}
          onChange={(e) => setConcernsRaw(e.target.value)}
          placeholder="Concerns (one per line)"
          rows={3}
          style={{ width: "100%", padding: 8, borderRadius: 6, border: "1px solid var(--stroke-secondary)", fontSize: 12, fontFamily: "var(--font-sans)" }}
        />
      </div>
      <div style={{ display: "flex", gap: 8, justifyContent: "flex-end" }}>
        <button className="btn btn-outline btn-sm" onClick={onCancel}>Cancel</button>
        <button className="btn btn-brand btn-sm" onClick={handleSubmit} disabled={!notes.trim() || submitting}>
          {submitting ? "Posting…" : "Post feedback"}
        </button>
      </div>
    </div>
  );
}

function PipelineDrawer({ oppId, onClose, onMove, onReject }) {
  const { useState, useEffect } = React;
  const [data,  setData]  = useState(null);
  const [loading, setLoading] = useState(true);
  const [analyzing, setAnalyzing] = useState(false);
  const [adding,    setAdding]    = useState(false);
  const [aiSummary, setAiSummary] = useState(null);
  const [summarizing, setSummarizing] = useState(false);

  const refreshFeedback = React.useCallback(() => {
    if (!oppId) return;
    fetch(`/api/opportunities/${oppId}/feedback`)
      .then(r => r.ok ? r.json() : [])
      .then(list => setData(prev => prev ? { ...prev, feedback: list } : prev))
      .catch(() => {});
  }, [oppId]);

  const refreshAiSummary = React.useCallback(() => {
    if (!oppId) return;
    setSummarizing(true);
    fetch(`/api/opportunities/${oppId}/feedback-summary`)
      .then(r => r.ok ? r.json() : null)
      .then(s => setAiSummary(s))
      .catch(() => setAiSummary(null))
      .finally(() => setSummarizing(false));
  }, [oppId]);

  useEffect(() => {
    if (!oppId) return;
    setLoading(true);
    setData(null);
    setAiSummary(null);
    fetch(`/api/candidates/${oppId}`)
      .then(r => {
        if (r.status === 401) {
          // Session lost — redirect to login, preserving the current URL (incl. hash).
          const target = window.location.pathname + window.location.search + window.location.hash;
          window.location.href = `/auth/login?next=${encodeURIComponent(target || '/')}`;
          return null;
        }
        return r.ok ? r.json() : null;
      })
      .then(d => {
        if (!d) return;
        setData(d);
        // Auto-trigger criteria analysis if score is missing and we have a resume.
        const needsAnalysis = d.resumeKey && (!d.criteria || d.criteria.length === 0 || d._score == null);
        if (needsAnalysis) {
          setAnalyzing(true);
          fetch(`/api/candidates/${oppId}/analyze`, { method: "POST" })
            .then(r => r.ok ? r.json() : null)
            .then(a => {
              if (a) setData(prev => prev ? {
                ...prev,
                aiAnalysis:     a,
                criteria:       a.criteria || prev.criteria,
                criteriaScores: a.scores   || prev.criteriaScores,
                _score:         a.score ?? prev._score,
              } : prev);
            })
            .catch(err => console.error("[ATS] analyze failed", err))
            .finally(() => setAnalyzing(false));
        }
        // If there's feedback, fetch AI summary in parallel.
        if (d.feedback && d.feedback.length > 0) refreshAiSummary();
      })
      .catch(() => {})
      .finally(() => setLoading(false));
  }, [oppId, refreshAiSummary]);

  const handleRerunAnalysis = () => {
    if (!oppId || analyzing) return;
    setAnalyzing(true);
    fetch(`/api/candidates/${oppId}/analyze?force=1`, { method: "POST" })
      .then(r => r.ok ? r.json() : Promise.reject(new Error(`HTTP ${r.status}`)))
      .then(a => setData(prev => prev ? {
        ...prev,
        aiAnalysis: a, criteria: a.criteria, criteriaScores: a.scores, _score: a.score ?? prev._score,
      } : prev))
      .catch(err => { console.error("[ATS] analyze failed", err); alert("Analysis failed — see console."); })
      .finally(() => setAnalyzing(false));
  };

  const setCriterion = (criterionId, state) => {
    if (!oppId) return;
    // Optimistic update
    setData(prev => {
      if (!prev) return prev;
      const next = { ...(prev.criteriaScores || {}) };
      next[criterionId] = { ...(next[criterionId] || {}), state, source: "human" };
      return { ...prev, criteriaScores: next };
    });
    fetch(`/api/opportunities/${oppId}/criteria-scores`, {
      method: "PUT",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ criterionId, state }),
    })
      .then(r => r.ok ? r.json() : Promise.reject(new Error(`HTTP ${r.status}`)))
      .then(res => setData(prev => prev ? { ...prev, criteriaScores: res.scores, _score: res.score ?? prev._score } : prev))
      .catch(err => { console.error("[ATS] criteria save failed", err); alert("Criterion failed to save — see console."); });
  };

  const handleAddFeedback = (entry) => {
    return fetch(`/api/opportunities/${oppId}/feedback`, {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify(entry),
    })
      .then(r => r.ok ? r.json() : Promise.reject(new Error(`HTTP ${r.status}`)))
      .then(() => {
        setAdding(false);
        refreshFeedback();
        refreshAiSummary();
      });
  };

  /* Determine next stage for "move to next" */
  const currentStageId = data?.stageId;
  const stageOrder = ["applied", "intro_screening", "hiring_manager", "technical_test", "offer", "hired"];
  const currentIdx = stageOrder.indexOf(currentStageId);
  const nextStageId = currentIdx >= 0 && currentIdx < stageOrder.length - 1
    ? stageOrder[currentIdx + 1]
    : null;
  const nextStage = nextStageId ? KN_STAGE_MAP[nextStageId] : null;

  return (
    <div className="dr-scrim" onClick={(e) => { if (e.target === e.currentTarget) onClose(); }}>
      <div className="dr-panel">
        {/* header */}
        <div className="dr-head">
          {data ? (
            <>
              <Avatar name={data.name} size={44} tone="auto" />
              <div className="dr-head-info">
                <div className="dr-head-name">{data.name}</div>
                <div className="dr-head-role">{data.postingTitle || "—"}</div>
              </div>
            </>
          ) : (
            <div className="dr-head-name">{loading ? "Loading…" : "Candidate"}</div>
          )}
          <button
            className="icon-btn"
            onClick={() => {
              const url = `${window.location.origin}/#candidates/${oppId}`;
              navigator.clipboard.writeText(url).then(
                () => { const t = document.getElementById("dr-copied"); if (t) { t.style.opacity = "1"; setTimeout(() => { t.style.opacity = "0"; }, 1600); } },
                () => alert(url),
              );
            }}
            title="Copy shareable link"
            style={{ marginLeft: "auto" }}
          >
            <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
              <path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" />
              <path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" />
            </svg>
          </button>
          <span id="dr-copied" style={{ fontSize: 11, color: "var(--green-70)", opacity: 0, transition: "opacity 150ms", marginRight: 4 }}>Copied</span>
          <button className="icon-btn" onClick={onClose}>
            <I.X width="18" height="18" />
          </button>
        </div>

        {/* body */}
        <div className="dr-body">
          {loading && (
            <div className="dr-loading">Loading candidate details…</div>
          )}

          {!loading && !data && (
            <div className="dr-loading" style={{ color: "var(--red-70)" }}>
              Could not load candidate data.
            </div>
          )}

          {data && (
            <>
              {/* score */}
              <div className="dr-score-block">
                <ScoreRing score={data._score || 0} size={64} stroke={5} />
                <div className="dr-score-info">
                  <div className="dr-score-label">Airtm Rank</div>
                  <div className="dr-score-value">
                    {(data._score || 0) >= 75 ? "Strong fit" : (data._score || 0) >= 50 ? "Good fit" : "Possible fit"}
                  </div>
                  {data._reasons && data._reasons.length > 0 && (
                    <div className="dr-score-reasons">
                      {data._reasons.map((r, i) => (
                        <span key={i} className="dr-score-reason">
                          <I.Check width="11" height="11" style={{ color: "var(--green-50)" }} />
                          {r}
                        </span>
                      ))}
                    </div>
                  )}
                </div>
              </div>

              {/* section: Match score (criteria editor) */}
              {(analyzing || (data.criteria && data.criteria.length > 0)) && (
                <div className="dr-section">
                  <div className="dr-section-title" style={{ display: "flex", alignItems: "center", gap: 8 }}>
                    <I.Sparkle width="14" height="14" style={{ color: "var(--indigo-50)" }} />
                    <span>Match score</span>
                    {data.criteria && data.criteria.length > 0 && !analyzing && (
                      <button
                        onClick={handleRerunAnalysis}
                        title="Re-run analysis from resume"
                        style={{ marginLeft: "auto", border: 0, background: "transparent", cursor: "pointer", fontSize: 11, color: "var(--content-tertiary)" }}
                      >Re-run AI</button>
                    )}
                  </div>
                  {analyzing && (!data.criteria || data.criteria.length === 0) && (
                    <div style={{ fontSize: 13, color: "var(--content-tertiary)", padding: "8px 0" }}>
                      Reading resume and scoring against the posting…
                    </div>
                  )}
                  {data.aiAnalysis && data.aiAnalysis.summary && (
                    <div style={{ fontSize: 13, lineHeight: 1.5, marginBottom: 12, color: "var(--content-secondary)" }}>
                      {data.aiAnalysis.summary}
                    </div>
                  )}
                  {data.criteria && data.criteria.length > 0 && (
                    <div style={{ display: "flex", flexDirection: "column", gap: 6 }}>
                      {data.criteria.map(c => {
                        const s = (data.criteriaScores || {})[c.id] || {};
                        return (
                          <div key={c.id} style={{ padding: "10px 12px", borderRadius: 8, background: "var(--bg-secondary)" }}>
                            <div style={{ display: "flex", alignItems: "center", gap: 12 }}>
                              <div style={{ flex: 1, minWidth: 0 }}>
                                <div style={{ fontSize: 12, fontWeight: 600 }}>{c.label}</div>
                                <div style={{ fontSize: 11, color: "var(--content-tertiary)", marginTop: 2 }}>
                                  Weight · {c.weight}
                                  {s.source === "human" && <span style={{ marginLeft: 6 }}>· edited</span>}
                                </div>
                              </div>
                              <div style={{ display: "flex", gap: 4 }} role="radiogroup" aria-label={c.label}>
                                {["met", "partial", "missing"].map(v => {
                                  const active = s.state === v;
                                  const colors = v === "met"
                                    ? { fg: "var(--green-70)", bg: "var(--green-10)", brd: "var(--green-30)" }
                                    : v === "partial"
                                    ? { fg: "var(--yellow-70)", bg: "var(--yellow-10)", brd: "var(--yellow-30)" }
                                    : { fg: "var(--content-tertiary)", bg: "var(--bg-tertiary)", brd: "var(--stroke-secondary)" };
                                  return (
                                    <button
                                      key={v}
                                      type="button"
                                      role="radio"
                                      aria-checked={active}
                                      onClick={() => setCriterion(c.id, v)}
                                      style={{
                                        padding: "3px 10px", borderRadius: 100, fontSize: 11, fontWeight: 600, cursor: "pointer",
                                        border: `1px solid ${active ? colors.brd : "transparent"}`,
                                        background: active ? colors.bg : "transparent",
                                        color: active ? colors.fg : "var(--content-tertiary)",
                                      }}
                                    >
                                      {v === "met" ? "Met" : v === "partial" ? "Partial" : "—"}
                                    </button>
                                  );
                                })}
                              </div>
                            </div>
                            {s.evidence && (
                              <div style={{ fontSize: 11, color: "var(--content-tertiary)", marginTop: 6, lineHeight: 1.45 }}>
                                <em>"{s.evidence}"</em>
                              </div>
                            )}
                          </div>
                        );
                      })}
                    </div>
                  )}
                </div>
              )}

              {/* section: contact */}
              <div className="dr-section">
                <div className="dr-section-title">Contact</div>
                <div className="dr-contact-list">
                  {data.email && (
                    <a href={`mailto:${data.email}`} className="dr-contact-row">
                      <IcMail width="14" height="14" style={{ color: "var(--content-tertiary)" }} />
                      <span>{data.email}</span>
                    </a>
                  )}
                  {data.phone && (
                    <div className="dr-contact-row">
                      <IcPhone width="14" height="14" style={{ color: "var(--content-tertiary)" }} />
                      <span>{data.phone}</span>
                    </div>
                  )}
                  {data.linkedin && (
                    <a href={data.linkedin} target="_blank" rel="noreferrer" className="dr-contact-row">
                      <IcLinkedIn width="14" height="14" style={{ color: "var(--content-tertiary)" }} />
                      <span>LinkedIn profile</span>
                    </a>
                  )}
                </div>
              </div>

              {/* section: application */}
              <div className="dr-section">
                <div className="dr-section-title">Application</div>
                <div className="dr-stat-grid">
                  <div className="dr-stat">
                    <div className="dr-stat-label">Source</div>
                    <div className="dr-stat-value">{data.source || "—"}</div>
                  </div>
                  <div className="dr-stat">
                    <div className="dr-stat-label">Applied</div>
                    <div className="dr-stat-value">
                      {data.createdAt ? new Date(data.createdAt).toLocaleDateString("en-US", { month: "short", day: "numeric", year: "numeric" }) : "—"}
                    </div>
                  </div>
                  <div className="dr-stat">
                    <div className="dr-stat-label">Stage</div>
                    <div className="dr-stat-value">{data.stageLabel || "—"}</div>
                  </div>
                  <div className="dr-stat">
                    <div className="dr-stat-label">Last interaction</div>
                    <div className="dr-stat-value">
                      {data.lastInteractionAt ? new Date(data.lastInteractionAt).toLocaleDateString("en-US", { month: "short", day: "numeric" }) : "—"}
                    </div>
                  </div>
                </div>
                {data.resumeKey && (
                  <a
                    className="dr-resume-row"
                    href={`/api/resume/${oppId}`}
                    target="_blank"
                    rel="noopener noreferrer"
                    style={{ textDecoration: "none", color: "inherit", cursor: "pointer" }}
                  >
                    <IcFileText width="14" height="14" style={{ color: "var(--content-tertiary)" }} />
                    <span style={{ textDecoration: "underline", textUnderlineOffset: 2 }}>
                      View resume
                    </span>
                    <span style={{ marginLeft: "auto", fontSize: 11, color: "var(--content-tertiary)" }}>
                      ↗
                    </span>
                  </a>
                )}
              </div>

              {/* section: feedback / comments */}
              <div className="dr-section">
                <div className="dr-section-title" style={{ display: "flex", alignItems: "center", gap: 8 }}>
                  <span>Feedback</span>
                  {!adding && (
                    <button
                      onClick={() => setAdding(true)}
                      style={{ marginLeft: "auto", border: 0, background: "transparent", cursor: "pointer", fontSize: 12, color: "var(--content-highlight)", fontWeight: 600, display: "inline-flex", alignItems: "center", gap: 4 }}
                    >
                      <I.Sparkle width="12" height="12" />
                      Add feedback
                    </button>
                  )}
                </div>

                {adding && (
                  <AddFeedbackForm
                    onCancel={() => setAdding(false)}
                    onSubmit={handleAddFeedback}
                  />
                )}

                {(summarizing || aiSummary) && (
                  <div style={{ padding: 12, borderRadius: 10, background: `linear-gradient(135deg, var(--indigo-10), var(--violet-10))`, marginBottom: 10 }}>
                    <div style={{ display: "flex", alignItems: "center", gap: 6, marginBottom: 6 }}>
                      <I.Sparkle width="12" height="12" style={{ color: "var(--indigo-60)" }} />
                      <span style={{ fontSize: 11, fontWeight: 600, color: "var(--indigo-70)", textTransform: "uppercase", letterSpacing: ".06em" }}>AI summary</span>
                      {aiSummary && (
                        <span style={{
                          marginLeft: "auto", padding: "2px 8px", borderRadius: 100, fontSize: 10, fontWeight: 600,
                          background: ratingMeta(aiSummary.tone).bg, color: ratingMeta(aiSummary.tone).tone,
                        }}>{ratingMeta(aiSummary.tone).label}</span>
                      )}
                    </div>
                    {summarizing && !aiSummary && (
                      <div style={{ fontSize: 12, color: "var(--content-tertiary)" }}>Synthesizing feedback…</div>
                    )}
                    {aiSummary && (
                      <>
                        <div style={{ fontSize: 13, fontWeight: 600, marginBottom: 8 }}>{aiSummary.headline}</div>
                        {aiSummary.strengths && aiSummary.strengths.length > 0 && (
                          <div style={{ marginBottom: 6 }}>
                            <div style={{ fontSize: 10, fontWeight: 600, color: "var(--content-tertiary)", textTransform: "uppercase", letterSpacing: ".06em", marginBottom: 3 }}>Strengths</div>
                            <ul style={{ margin: 0, paddingLeft: 18, fontSize: 12 }}>
                              {aiSummary.strengths.map((s, i) => <li key={i}>{s}</li>)}
                            </ul>
                          </div>
                        )}
                        {aiSummary.concerns && aiSummary.concerns.length > 0 && (
                          <div style={{ marginBottom: 6 }}>
                            <div style={{ fontSize: 10, fontWeight: 600, color: "var(--content-tertiary)", textTransform: "uppercase", letterSpacing: ".06em", marginBottom: 3 }}>Concerns</div>
                            <ul style={{ margin: 0, paddingLeft: 18, fontSize: 12 }}>
                              {aiSummary.concerns.map((c, i) => <li key={i}>{c}</li>)}
                            </ul>
                          </div>
                        )}
                        {aiSummary.nextStep && (
                          <div style={{ fontSize: 12, color: "var(--content-secondary)", marginTop: 6, fontStyle: "italic" }}>
                            Next: {aiSummary.nextStep}
                          </div>
                        )}
                      </>
                    )}
                  </div>
                )}

                {data.feedback && data.feedback.length > 0 ? (
                  <div style={{ display: "flex", flexDirection: "column", gap: 8 }}>
                    {data.feedback.map(fb => {
                      const rm = ratingMeta(fb.rating);
                      return (
                        <div key={fb.id} style={{ padding: 12, border: "1px solid var(--stroke-secondary)", borderRadius: 10 }}>
                          <div style={{ display: "flex", alignItems: "center", gap: 8, marginBottom: 6 }}>
                            <Avatar name={fb.interviewerName || fb.interviewerEmail} size={24} tone="auto" />
                            <div style={{ flex: 1, minWidth: 0 }}>
                              <div style={{ fontSize: 12, fontWeight: 600 }}>{fb.interviewerName || fb.interviewerEmail}</div>
                              <div style={{ fontSize: 10, color: "var(--content-tertiary)" }}>
                                {fb.stageId} · {fb.submittedAt ? new Date(fb.submittedAt).toLocaleDateString("en-US", { month: "short", day: "numeric", year: "numeric" }) : "—"}
                              </div>
                            </div>
                            <span style={{
                              padding: "2px 8px", borderRadius: 100, fontSize: 10, fontWeight: 600,
                              background: rm.bg, color: rm.tone,
                            }}>{rm.label}</span>
                          </div>
                          {fb.notes && (
                            <div style={{ fontSize: 13, lineHeight: 1.5, marginBottom: 6 }}>{fb.notes}</div>
                          )}
                          {fb.strengths && fb.strengths.length > 0 && (
                            <div style={{ marginBottom: 4 }}>
                              <div style={{ fontSize: 10, fontWeight: 600, color: "var(--content-tertiary)", textTransform: "uppercase", letterSpacing: ".06em", marginBottom: 2 }}>Strengths</div>
                              <ul style={{ margin: 0, paddingLeft: 16, fontSize: 12, color: "var(--content-secondary)" }}>
                                {fb.strengths.map((s, i) => <li key={i}>{s}</li>)}
                              </ul>
                            </div>
                          )}
                          {fb.concerns && fb.concerns.length > 0 && (
                            <div>
                              <div style={{ fontSize: 10, fontWeight: 600, color: "var(--content-tertiary)", textTransform: "uppercase", letterSpacing: ".06em", marginBottom: 2 }}>Concerns</div>
                              <ul style={{ margin: 0, paddingLeft: 16, fontSize: 12, color: "var(--content-secondary)" }}>
                                {fb.concerns.map((c, i) => <li key={i}>{c}</li>)}
                              </ul>
                            </div>
                          )}
                        </div>
                      );
                    })}
                  </div>
                ) : !adding && (
                  <div style={{ padding: "16px 12px", borderRadius: 10, background: "var(--bg-secondary)", fontSize: 13, color: "var(--content-tertiary)", textAlign: "center" }}>
                    No feedback yet. Add the first review to start building the picture.
                  </div>
                )}
              </div>

              {/* Legacy recruiter notes — only shown if present */}
              {data.notes && (
                <div className="dr-section">
                  <div className="dr-section-title">Recruiter notes</div>
                  <div style={{ fontSize: 13, padding: 10, borderRadius: 8, background: "var(--bg-secondary)", whiteSpace: "pre-wrap" }}>
                    {data.notes}
                  </div>
                </div>
              )}
            </>
          )}
        </div>

        {/* footer actions */}
        {data && (
          <div className="dr-footer">
            <button
              className="btn btn-outline btn-sm"
              style={{ color: "var(--red-70)", borderColor: "var(--red-30)" }}
              onClick={() => onReject && onReject({ id: oppId, name: data.name, stageId: data.stageId, postingTitle: data.postingTitle, email: data.email })}
            >
              <IcTrash width="13" height="13" />
              Reject
            </button>
            {nextStage && (
              <button
                className="btn btn-brand btn-sm"
                style={{ flex: 1 }}
                onClick={() => onMove && onMove(
                  { id: oppId, name: data.name, stageId: data.stageId, postingTitle: data.postingTitle, email: data.email },
                  nextStageId
                )}
              >
                <IcArrowRight width="13" height="13" />
                Move to {nextStage.label}
              </button>
            )}
          </div>
        )}
      </div>
    </div>
  );
}

/* ── NewPostingModal ─────────────────────────────────────────────────────── */

function NewPostingModal({ onClose, onCreated }) {
  const { useState } = React;
  const [form, setForm] = useState({
    title: "", department: "dept_eng", location: "", type: "Full-time",
    team: "", salaryRange: "", overview: "",
  });
  const [responsibilities, setResponsibilities] = useState([""]);
  const [requirements, setRequirements] = useState([""]);
  const [saving, setSaving] = useState(false);
  const [error, setError] = useState(null);

  const setField = (k, v) => setForm(f => ({ ...f, [k]: v }));

  const addItem = (setter) => setter(a => [...a, ""]);
  const setItem = (setter, i, v) => setter(a => { const n = [...a]; n[i] = v; return n; });
  const removeItem = (setter, i) => setter(a => a.filter((_, idx) => idx !== i));

  const handleSubmit = (e) => {
    e.preventDefault();
    if (!form.title.trim()) { setError("Title is required."); return; }
    setSaving(true);
    setError(null);
    const body = {
      title: form.title.trim(),
      department_id: form.department,
      location: form.location.trim(),
      type: form.type,
      team: form.team.trim(),
      salary_range: form.salaryRange.trim(),
      overview: form.overview.trim(),
      responsibilities: responsibilities.filter(r => r.trim()),
      requirements: requirements.filter(r => r.trim()),
    };
    fetch("/api/postings", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify(body),
    })
      .then(r => r.ok ? r.json() : Promise.reject(r.status))
      .then((created) => {
        setSaving(false);
        onCreated && onCreated(created);
        onClose();
      })
      .catch((err) => {
        setSaving(false);
        setError(`Failed to create posting (${err}). Check server logs.`);
      });
  };

  return (
    <div className="np-scrim" onClick={(e) => { if (e.target === e.currentTarget) onClose(); }}>
      <div className="np-shell">
        <div className="np-head">
          <h2 className="np-title">New posting</h2>
          <button className="icon-btn" onClick={onClose}><I.X width="18" height="18" /></button>
        </div>

        <form className="np-body" onSubmit={handleSubmit}>
          {/* Title */}
          <div className="np-field">
            <label className="np-label">Title *</label>
            <input className="np-input" type="text" value={form.title}
              onChange={(e) => setField("title", e.target.value)}
              placeholder="e.g. Senior backend engineer, payments" required />
          </div>

          {/* Department + Type */}
          <div className="np-row-2">
            <div className="np-field">
              <label className="np-label">Department</label>
              <select className="np-input" value={form.department}
                onChange={(e) => setField("department", e.target.value)}>
                {DEPARTMENTS_LIST.map(d => (
                  <option key={d.id} value={d.id}>{d.name}</option>
                ))}
              </select>
            </div>
            <div className="np-field">
              <label className="np-label">Type</label>
              <select className="np-input" value={form.type}
                onChange={(e) => setField("type", e.target.value)}>
                {["Full-time", "Part-time", "Contract", "Internship"].map(t => (
                  <option key={t} value={t}>{t}</option>
                ))}
              </select>
            </div>
          </div>

          {/* Location + Team */}
          <div className="np-row-2">
            <div className="np-field">
              <label className="np-label">Location</label>
              <input className="np-input" type="text" value={form.location}
                onChange={(e) => setField("location", e.target.value)}
                placeholder="e.g. Remote · LatAm" />
            </div>
            <div className="np-field">
              <label className="np-label">Team</label>
              <input className="np-input" type="text" value={form.team}
                onChange={(e) => setField("team", e.target.value)}
                placeholder="e.g. Payments Platform" />
            </div>
          </div>

          {/* Salary range */}
          <div className="np-field">
            <label className="np-label">Salary range</label>
            <input className="np-input" type="text" value={form.salaryRange}
              onChange={(e) => setField("salaryRange", e.target.value)}
              placeholder="e.g. $110k–$160k USD" />
          </div>

          {/* Overview */}
          <div className="np-field">
            <label className="np-label">Overview</label>
            <textarea className="np-input np-textarea" value={form.overview}
              onChange={(e) => setField("overview", e.target.value)}
              rows={3} placeholder="Brief description of the role…" />
          </div>

          {/* Responsibilities */}
          <div className="np-field">
            <label className="np-label">Responsibilities</label>
            {responsibilities.map((r, i) => (
              <div key={i} className="np-list-row">
                <input className="np-input np-list-input" type="text" value={r}
                  onChange={(e) => setItem(setResponsibilities, i, e.target.value)}
                  placeholder={`Responsibility ${i + 1}`} />
                {responsibilities.length > 1 && (
                  <button type="button" className="icon-btn np-list-remove"
                    onClick={() => removeItem(setResponsibilities, i)}>
                    <I.X width="13" height="13" />
                  </button>
                )}
              </div>
            ))}
            <button type="button" className="btn btn-outline btn-sm np-add-btn"
              onClick={() => addItem(setResponsibilities)}>
              <I.Plus width="12" height="12" /> Add
            </button>
          </div>

          {/* Requirements */}
          <div className="np-field">
            <label className="np-label">Requirements</label>
            {requirements.map((r, i) => (
              <div key={i} className="np-list-row">
                <input className="np-input np-list-input" type="text" value={r}
                  onChange={(e) => setItem(setRequirements, i, e.target.value)}
                  placeholder={`Requirement ${i + 1}`} />
                {requirements.length > 1 && (
                  <button type="button" className="icon-btn np-list-remove"
                    onClick={() => removeItem(setRequirements, i)}>
                    <I.X width="13" height="13" />
                  </button>
                )}
              </div>
            ))}
            <button type="button" className="btn btn-outline btn-sm np-add-btn"
              onClick={() => addItem(setRequirements)}>
              <I.Plus width="12" height="12" /> Add
            </button>
          </div>

          {error && <div className="np-error">{error}</div>}

          <div className="np-footer">
            <button type="button" className="btn btn-outline" onClick={onClose}>Cancel</button>
            <button type="submit" className="btn btn-brand" disabled={saving}>
              {saving ? "Creating…" : "Create posting"}
            </button>
          </div>
        </form>
      </div>
    </div>
  );
}

/* ── PipelinePage ────────────────────────────────────────────────────────── */

function PipelinePage({ onNav }) {
  const { useState, useEffect, useCallback, useMemo } = React;

  /* ── State ── */
  const [opps,         setOpps]         = useState([]);
  const [searchQ,      setSearchQ]      = useState("");
  const [postingFilter,setPostingFilter] = useState("all");
  const [groupBy,      setGroupBy]      = useState("stage"); // "stage" | "posting"
  const [draggingId,   setDraggingId]   = useState(null);
  const [draggingOpp,  setDraggingOpp]  = useState(null);
  const [dragOverCol,  setDragOverCol]  = useState(null);
  const [drawerOppId,  setDrawerOppId]  = useState(null);
  const [composer,     setComposer]     = useState(null); // { opp, toStageId }
  const [newPostingOpen, setNewPostingOpen] = useState(false);
  const [searchOpen,   setSearchOpen]   = useState(false);
  const [settingsOpen, setSettingsOpen] = useState(false);

  /* ── Load data ── */
  useEffect(() => {
    fetch("/api/dashboard")
      .then(r => r.ok ? r.json() : null)
      .then(data => {
        if (data?.candidates?.length) {
          setOpps(
            data.candidates
              .map(c => ({
                ...c,
                stageId: CANONICAL_TO_STAGE_ID[c.stage] || "intro_screening",
              }))
              .filter(c => c.stageId !== "applied")
          );
        }
      })
      .catch(() => {
        const cands = window.CANDIDATES || [];
        setOpps(
          cands
            .map(c => ({
              ...c,
              stageId: CANONICAL_TO_STAGE_ID[c.stage] || "intro_screening",
            }))
            .filter(c => c.stageId !== "applied")
        );
      });
  }, []);

  /* ── Keyboard shortcuts ── */
  useEffect(() => {
    const fn = (e) => {
      if ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === "k") {
        e.preventDefault();
        setSearchOpen(true);
      }
      if (e.key === "Escape") {
        setSearchOpen(false);
        setSettingsOpen(false);
      }
    };
    window.addEventListener("keydown", fn);
    return () => window.removeEventListener("keydown", fn);
  }, []);

  /* ── Posting options for filter ── */
  const postingOptions = useMemo(() => {
    const map = {};
    opps.forEach(o => {
      if (o.role && o.roleLabel) map[o.role] = o.roleLabel;
    });
    return [
      { value: "all", label: "All postings" },
      ...Object.entries(map).map(([value, label]) => ({ value, label })),
    ];
  }, [opps]);

  /* ── Filter + search ── */
  const filteredOpps = useMemo(() => {
    let list = opps;
    if (postingFilter !== "all") {
      list = list.filter(o => o.role === postingFilter);
    }
    if (searchQ.trim()) {
      const q = searchQ.toLowerCase();
      list = list.filter(o =>
        (o.name || "").toLowerCase().includes(q) ||
        (o.roleLabel || "").toLowerCase().includes(q) ||
        (o.loc || "").toLowerCase().includes(q)
      );
    }
    return list;
  }, [opps, postingFilter, searchQ]);

  /* ── Stage commit (after email) ── */
  const commitStageMove = useCallback((opp, toStageId) => {
    /* Optimistic update */
    setOpps(prev => prev.map(o => o.id === opp.id ? { ...o, stageId: toStageId } : o));
    /* API call */
    fetch(`/api/opportunities/${opp.id}/stage`, {
      method: "PUT",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ stageId: toStageId }),
    }).catch(() => {
      /* rollback on error */
      setOpps(prev => prev.map(o => o.id === opp.id ? { ...o, stageId: opp.stageId } : o));
    });
  }, []);

  /* ── Trigger email composer before moving ── */
  const initiateMove = useCallback((opp, toStageId) => {
    setDrawerOppId(null);
    setComposer({ opp, toStageId });
  }, []);

  const handleReject = useCallback((opp) => {
    initiateMove(opp, "rejected");
  }, [initiateMove]);

  /* ── Composer callbacks ── */
  const handleComposerSendAndMove = useCallback((emailData) => {
    if (!composer) return;
    const oppId = composer.opp.id;
    fetch("/api/email/send", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({
        to:      emailData.to,
        subject: emailData.subject,
        body:    emailData.body,
        opportunityId: oppId,
      }),
    })
      .then(r => { if (!r.ok) console.error("[ATS] email send failed", r.status); })
      .catch(err => console.error("[ATS] email send error", err));
    commitStageMove(composer.opp, composer.toStageId);
    setComposer(null);
  }, [composer, commitStageMove]);

  const handleComposerDiscardAndMove = useCallback(() => {
    if (!composer) return;
    commitStageMove(composer.opp, composer.toStageId);
    setComposer(null);
  }, [composer, commitStageMove]);

  const handleComposerCancel = useCallback(() => {
    setComposer(null);
  }, []);

  /* ── Drag handlers ── */
  const handleDragStart = useCallback((e, opp) => {
    setDraggingId(opp.id);
    setDraggingOpp(opp);
    e.dataTransfer.effectAllowed = "move";
  }, []);

  const handleDragEnd = useCallback(() => {
    setDraggingId(null);
    setDraggingOpp(null);
    setDragOverCol(null);
  }, []);

  const handleDragOverCol = useCallback((stageId) => {
    setDragOverCol(stageId);
  }, []);

  const handleDragLeaveCol = useCallback(() => {
    /* we do NOT clear dragOverCol on leave to avoid flicker */
  }, []);

  const handleDropCol = useCallback((stageId) => {
    setDragOverCol(null);
    if (!draggingOpp) return;
    if (draggingOpp.stageId === stageId) {
      setDraggingId(null);
      setDraggingOpp(null);
      return;
    }
    const opp = draggingOpp;
    setDraggingId(null);
    setDraggingOpp(null);
    initiateMove(opp, stageId);
  }, [draggingOpp, initiateMove]);

  /* ── Build composer stage info ── */
  const composerFromStage = composer ? (KN_STAGE_MAP[composer.opp.stageId] || KN_STAGES[0]) : null;
  const composerToStage   = composer ? (KN_STAGE_MAP[composer.toStageId]   || KN_STAGES[0]) : null;

  /* ── Derived: opps by stage ── */
  const oppsByStage = useMemo(() => {
    const map = {};
    KN_STAGES.forEach(s => { map[s.id] = []; });
    filteredOpps.forEach(o => {
      const sid = o.stageId || "intro_screening";
      if (map[sid]) map[sid].push(o);
      else map["intro_screening"].push(o);
    });
    return map;
  }, [filteredOpps]);

  /* ── Derived: posting groups for matrix view ── */
  const postingGroups = useMemo(() => {
    const map = {};
    filteredOpps.forEach(o => {
      const key = o.role || "unknown";
      if (!map[key]) map[key] = { role: key, label: o.roleLabel || key, opps: [] };
      map[key].opps.push(o);
    });
    return Object.values(map);
  }, [filteredOpps]);

  return (
    <div style={{
      display: "flex", height: "100%",
      background: "var(--bg-secondary)",
      position: "relative", overflow: "hidden"
    }}>
      <Sidebar active="pipeline" onNav={onNav} />

      <div style={{ flex: 1, display: "flex", flexDirection: "column", minWidth: 0 }}>
        <Topbar
          title="Pipeline"
          onSearch={() => setSearchOpen(true)}
          onSettings={() => setSettingsOpen(true)}
        />

        {/* page body */}
        <div style={{ flex: 1, overflow: "auto" }}>
          <div style={{ maxWidth: 1600, margin: "0 auto", padding: "24px 28px 40px" }}>

            {/* page header */}
            <div style={{ display: "flex", alignItems: "flex-end", justifyContent: "space-between",
              gap: 16, marginBottom: 20, flexWrap: "wrap" }}>
              <div>
                <div style={{ fontSize: 13, color: "var(--content-tertiary)", marginBottom: 4 }}>
                  <button onClick={() => onNav && onNav("dashboard")} style={{
                    background: "none", border: 0, padding: 0, font: "inherit",
                    color: "var(--content-tertiary)", cursor: "pointer",
                  }}>Dashboard</button>
                  {" / "}
                  <span style={{ color: "var(--content-primary)", fontWeight: 600 }}>Pipeline</span>
                </div>
                <h2 style={{ margin: 0, fontSize: 26, fontWeight: 600, letterSpacing: "-0.01em" }}>
                  Pipeline
                </h2>
                <div style={{ fontSize: 13, color: "var(--content-tertiary)", marginTop: 4 }}>
                  {filteredOpps.length} candidate{filteredOpps.length !== 1 ? "s" : ""} in active stages
                </div>
              </div>

              {/* controls */}
              <div style={{ display: "flex", alignItems: "center", gap: 10, flexWrap: "wrap" }}>
                {/* search bar */}
                <div style={{ display: "flex", alignItems: "center", gap: 8,
                  padding: "8px 12px", border: "1px solid var(--stroke-secondary)",
                  borderRadius: 10, background: "var(--bg-primary)", minWidth: 200 }}>
                  <I.Search width="14" height="14" style={{ color: "var(--content-tertiary)" }} />
                  <input
                    type="text" value={searchQ}
                    onChange={(e) => setSearchQ(e.target.value)}
                    placeholder="Search candidates…"
                    style={{ border: 0, outline: 0, fontSize: 13, background: "transparent",
                      color: "var(--content-primary)", fontFamily: "var(--font-sans)", width: 160 }}
                  />
                </div>

                {/* posting filter */}
                <Select
                  value={postingFilter}
                  onChange={setPostingFilter}
                  options={postingOptions}
                  width={200}
                />

                {/* group by toggle */}
                <div style={{ display: "inline-flex", padding: 3, borderRadius: 10,
                  background: "var(--bg-primary)", border: "1px solid var(--stroke-secondary)" }}>
                  {[{ id: "stage", label: "Stage" }, { id: "posting", label: "Posting" }].map(v => (
                    <button key={v.id} onClick={() => setGroupBy(v.id)} style={{
                      padding: "5px 12px", borderRadius: 8, border: 0,
                      background: groupBy === v.id ? "var(--bg-secondary)" : "transparent",
                      color: "var(--content-primary)",
                      fontFamily: "var(--font-sans)", fontWeight: 600, fontSize: 12, cursor: "pointer",
                    }}>{v.label}</button>
                  ))}
                </div>

                {/* new posting button */}
                <button className="btn btn-brand btn-sm" onClick={() => setNewPostingOpen(true)}>
                  <I.Plus width="13" height="13" />
                  New posting
                </button>
              </div>
            </div>

            {/* kanban or matrix */}
            {groupBy === "stage" ? (
              /* Stage kanban */
              <div className="kn-board">
                {KN_STAGES.map(stage => (
                  <KnColumn
                    key={stage.id}
                    stage={stage}
                    opps={oppsByStage[stage.id] || []}
                    dragOver={dragOverCol}
                    draggingId={draggingId}
                    onDragOverCol={handleDragOverCol}
                    onDragLeaveCol={handleDragLeaveCol}
                    onDropCol={handleDropCol}
                    onDragStartCard={handleDragStart}
                    onDragEndCard={handleDragEnd}
                    onCardClick={(opp) => setDrawerOppId(opp.id)}
                    onMove={initiateMove}
                    onReject={handleReject}
                  />
                ))}
              </div>
            ) : (
              /* Posting matrix */
              <div className="kn-matrix">
                <div className="kn-matrix-head">
                  <div className="kn-matrix-row-label">Posting</div>
                  {KN_STAGES.map(s => (
                    <div key={s.id} className="kn-matrix-col-label">
                      <span className="kn-col-dot" style={{ background: s.accent }} />
                      {s.label}
                    </div>
                  ))}
                </div>
                {postingGroups.map(pg => (
                  <div key={pg.role} className="kn-matrix-row">
                    <div className="kn-matrix-row-label">{pg.label}</div>
                    {KN_STAGES.map(s => {
                      const cellOpps = pg.opps.filter(o => o.stageId === s.id);
                      return (
                        <div key={s.id} className="kn-matrix-cell">
                          {cellOpps.length === 0 ? (
                            <span className="kn-matrix-cell-empty">—</span>
                          ) : (
                            <button
                              className="kn-matrix-pill"
                              onClick={() => { setPostingFilter(pg.role); setGroupBy("stage"); }}
                              title={`${cellOpps.length} candidate${cellOpps.length === 1 ? "" : "s"} in ${s.label} for ${pg.label}`}
                              style={{ fontWeight: 700, fontVariantNumeric: "tabular-nums", fontSize: 14 }}
                            >
                              {cellOpps.length}
                            </button>
                          )}
                        </div>
                      );
                    })}
                  </div>
                ))}
                {postingGroups.length === 0 && (
                  <div className="kn-matrix-empty">No candidates match the current filters.</div>
                )}
              </div>
            )}

          </div>
        </div>
      </div>

      {/* ── Overlays ── */}

      {/* Candidate drawer */}
      {drawerOppId && (
        <PipelineDrawer
          oppId={drawerOppId}
          onClose={() => setDrawerOppId(null)}
          onMove={(opp, toStageId) => { setDrawerOppId(null); initiateMove(opp, toStageId); }}
          onReject={(opp) => { setDrawerOppId(null); handleReject(opp); }}
        />
      )}

      {/* Email composer */}
      {composer && composerFromStage && composerToStage && (
        <EmailComposer
          candidateName={composer.opp.name}
          candidateEmail={composer.opp.email || ""}
          candidateHue="auto"
          postingTitle={composer.opp.roleLabel || composer.opp.role || ""}
          fromStageId={composerFromStage.id}
          fromStageLabel={composerFromStage.label}
          toStageId={composerToStage.id}
          toStageLabel={composerToStage.label}
          toStageAccent={STAGE_BG[composerToStage.id] || "var(--indigo-10)"}
          fromStageAccent={STAGE_BG[composerFromStage.id] || "var(--neutral-20)"}
          onSendAndMove={handleComposerSendAndMove}
          onDiscardAndMove={handleComposerDiscardAndMove}
          onCancel={handleComposerCancel}
        />
      )}

      {/* New posting modal */}
      {newPostingOpen && (
        <NewPostingModal
          onClose={() => setNewPostingOpen(false)}
          onCreated={() => {}}
        />
      )}

      {/* AI search (reuse existing) */}
      <AISearchModal
        open={searchOpen}
        onClose={() => setSearchOpen(false)}
        onOpenCandidate={(c) => { setSearchOpen(false); setDrawerOppId(c.id); }}
      />

      <SettingsModal open={settingsOpen} onClose={() => setSettingsOpen(false)} />
    </div>
  );
}

window.PipelinePage     = PipelinePage;
window.NewPostingModal  = NewPostingModal;
window.PipelineDrawer   = PipelineDrawer;
