// ============ KNOWLEDGE TAB ============
// Documents, sheets, exports your agents have read. Each doc is taught to
// specific agents — `taught[agentId] = ISO timestamp` records when. Adding
// a new agent later doesn't retroactively teach them; you decide who reads
// what. Data is persisted via the server (/knowledge API).

const KIND_ICON = { doc: "✎", pdf: "▤", sheet: "▦", csv: "▤" };

const FLAG_COPY = {
  pricing_financial_commitment: "This section contains pricing or financial information — verify it's current before activating.",
  expiry_or_version: "This section looks date- or version-sensitive — confirm it has not expired.",
  agent_or_employee_instruction: "This section tells an agent or employee how to behave — make sure it belongs in knowledge, not the agent brief.",
  legal_regulatory_claim: "This section may create a legal, warranty, compliance, or liability expectation — verify approved wording.",
  competitor_mention: "This section mentions competitors or positioning — confirm the comparison is approved and current.",
  contact_information: "This section includes contact details — verify they are current before activating.",
  behavior_override_attempt: "This section appears to override agent behavior — Citrus must decide whether it belongs in the brief instead.",
  prompt_injection: "This section contains instructions that try to override the agent system prompt, so the document was rejected.",
};

function plainFlagReason(flag) {
  return FLAG_COPY[flag?.category] || "This passage needs review before an agent can learn from it.";
}

function probeTone(status) {
  if (status === "passed") return { color: "#166534", bg: "rgba(22,101,52,.08)", border: "rgba(22,101,52,.18)" };
  if (status === "pending") return { color: "#92400e", bg: "rgba(245,158,11,.08)", border: "rgba(245,158,11,.22)" };
  return { color: "#b91c1c", bg: "rgba(239,68,68,.08)", border: "rgba(239,68,68,.18)" };
}

const DOC_STATE_META = {
  uploaded:     { label: "Uploaded", tone: "neutral", next: "Received and waiting for automated processing." },
  scanning:     { label: "Scanning", tone: "pending", next: "Automated checks are running; agents cannot access it yet." },
  needs_review: { label: "Needs review", tone: "warning", next: "Clear each flag explicitly before teaching this document." },
  approved:     { label: "Approved", tone: "approved", next: "Flags are cleared. Teach it to at least one agent to make it active." },
  active:       { label: "Active", tone: "active", next: "Taught to at least one agent and available in prompt context." },
  rejected:     { label: "Rejected", tone: "rejected", next: "Cannot be made active. Fix the issues and re-upload a revised document." },
};

function knowledgeDocState(doc) {
  const explicit = String(doc?.state || "").toLowerCase();
  if (DOC_STATE_META[explicit]) return explicit;
  const reviewStatus = String(doc?.reviewStatus || "").toLowerCase();
  if (reviewStatus.includes("rejected")) return "rejected";
  if (doc?.taught && Object.keys(doc.taught).length > 0) return "active";
  if (["pending_master_review", "pending_red_flag_review", "pending"].includes(reviewStatus) || doc?.requiresMasterApproval || doc?.hasRedFlags) return "needs_review";
  if (["approved", "approved_auto", "approved_by_master"].includes(reviewStatus) || doc?.teachStatus === "approved") return "approved";
  return "uploaded";
}

function flagRef(flag, chunk) {
  const parts = [];
  if (flag?.pageLabel) parts.push(`sheet ${flag.pageLabel}`);
  else if (flag?.page) parts.push(`page ${flag.page}`);
  if (flag?.chunkId) parts.push(flag.chunkId);
  if (Number.isFinite(flag?.position)) parts.push(`pos ${flag.position}`);
  else if (Number.isFinite(chunk?.position)) parts.push(`pos ${chunk.position}`);
  return parts.join(" · ") || "chunk reference";
}

const SERVER_URL_KN = () =>
  (typeof window !== "undefined" && window.CITRUS_CONFIG && window.CITRUS_CONFIG.SERVER_URL) ||
  "http://localhost:3001";

function activeEnvHeaders() {
  const headers = {};
  try {
    const activeEnvId = localStorage.getItem("citrus_active_env");
    const activeEnvData = JSON.parse(localStorage.getItem("citrus_active_env_data") || "null");
    if (activeEnvId && activeEnvData && activeEnvData.type !== "production") {
      headers["X-Env-Id"] = activeEnvId;
    }
  } catch {}
  if (window.__citrusTenantSlug) {
    headers["X-Tenant-Slug"] = window.__citrusTenantSlug;
  }
  return headers;
}

async function kFetch(path, opts = {}) {
  const headers = { "content-type": "application/json", ...activeEnvHeaders(), ...(opts.headers || {}) };
  const r = await fetch(SERVER_URL_KN() + path, {
    ...opts,
    headers,
  });
  if (!r.ok) {
    const t = await r.text().catch(() => "");
    let msg = t;
    try { msg = JSON.parse(t).error || t; } catch {}
    throw new Error(`${r.status}: ${String(msg).slice(0, 160)}`);
  }
  return r.json();
}

function KnowledgeTab({ agents = [], conflicts = [], globalLearnings = null, onAskCitrus = null }) {
  const [q, setQ]           = useState("");
  const [docs, setDocs]     = useState([]);
  const [genericLearnings, setGenericLearnings] = useState([]);
  const [teaching, setTeaching] = useState(null);
  const [pickerAgents, setPickerAgents] = useState({});
  const [teachInsight, setTeachInsight] = useState(null);
  const [previewing, setPreviewing] = useState(null); // docId with expanded preview
  const [expandedDocs, setExpandedDocs] = useState({});
  const [dragging, setDragging] = useState(false);
  const [uploadingDocs, setUploadingDocs] = useState([]);
  const fileRef             = useRef(null);
  const filt = q ? docs.filter(d => d.name.toLowerCase().includes(q.toLowerCase())) : docs;
  const visibleDocs = [...uploadingDocs, ...filt];
  const toggleDocExpanded = (docId) => setExpandedDocs((prev) => ({ ...prev, [docId]: !prev[docId] }));

  const askCitrus = (question) => {
    const clean = String(question || "").trim();
    if (!clean) return;
    if (typeof onAskCitrus === "function") onAskCitrus(clean);
    else window.toast && window.toast("Citrus chat is not available here yet.", "warn");
  };

  // Load docs and generic learnings from server on mount
  useEffect(() => {
    kFetch("/knowledge")
      .then(setDocs)
      .catch((e) => window.toast && window.toast("Couldn't load knowledge docs: " + e.message, "warn"));
    kFetch("/learnings")
      .then(setGenericLearnings)
      .catch(() => {});
  }, []);

  // Re-fetch docs when master admin approves or rejects a document
  useEffect(() => {
    const handler = () => kFetch("/knowledge").then(setDocs).catch(() => {});
    window.addEventListener("citrus-knowledge-review", handler);
    return () => window.removeEventListener("citrus-knowledge-review", handler);
  }, []);

  // Keep generic learnings in sync with real-time updates pushed from app.jsx
  // (e.g. when auto-teach disables a conflicting lesson in the background).
  useEffect(() => {
    if (globalLearnings !== null) setGenericLearnings(globalLearnings);
  }, [globalLearnings]);

  const addDocs = (fileList) => {
    if (!fileList || fileList.length === 0) return;

    const kindFor = (n) => {
      const ext = n.toLowerCase().split(".").pop();
      if (ext === "pdf") return "pdf";
      if (ext === "csv") return "csv";
      if (["xlsx", "xls", "numbers"].includes(ext)) return "sheet";
      if (["docx", "doc"].includes(ext)) return "doc";
      return "doc";
    };

    // Binary types that need server-side extraction — send as base64
    const BINARY_EXTS = new Set(["pdf", "docx", "doc", "xlsx", "xls"]);
    const isBinary = (f) => BINARY_EXTS.has(f.name.toLowerCase().split(".").pop());

    const fmt = (b) => b > 1e6 ? (b / 1e6).toFixed(1) + " MB" : Math.max(1, Math.round(b / 1024)) + " KB";
    const files = Array.from(fileList);

    const pendingUploads = files.map((file) => ({
      id: `upload-${Date.now()}-${Math.random().toString(36).slice(2)}`,
      name: file.name,
      kind: kindFor(file.name),
      size: fmt(file.size || 0),
      state: "scanning",
      addedAt: new Date().toISOString(),
      uploadProgress: { label: "Uploading and scanning", detail: "Automated checks usually finish within a minute." },
    }));
    setUploadingDocs((prev) => [...pendingUploads, ...prev]);

    Promise.all(files.map((f) =>
      new Promise((resolve, reject) => {
        const reader = new FileReader();
        if (isBinary(f)) {
          reader.onload = (e) => {
            // readAsDataURL returns "data:<type>;base64,<data>" — strip the prefix
            const b64 = e.target.result.split(",")[1];
            resolve({ file: f, content: b64, encoding: "base64" });
          };
          reader.readAsDataURL(f);
        } else {
          reader.onload = (e) => resolve({ file: f, content: e.target.result, encoding: "text" });
          reader.readAsText(f);
        }
        reader.onerror = reject;
      })
    )).then((results) => {
      return Promise.all(results.map(({ file, content, encoding }) =>
        kFetch("/knowledge", {
          method: "POST",
          body: JSON.stringify({
            name:        file.name,
            kind:        kindFor(file.name),
            size:        fmt(file.size || 0),
            content:     content,
            contentType: file.type || "text/plain",
            encoding:    encoding === "base64" ? "base64" : undefined,
          }),
        })
      ));
    }).then((newDocs) => {
      setUploadingDocs((prev) => prev.filter((u) => !pendingUploads.some((p) => p.id === u.id)));
      setDocs((prev) => [...newDocs, ...prev]);
      const pending = newDocs.filter((d) => ["pending_master_review", "pending_red_flag_review"].includes(d.reviewStatus)).length;
      const flagged = newDocs.filter((d) => (d.redFlagReport?.flags || []).length > 0).length;
      const active = newDocs.length - pending;
      const msg = pending > 0 && active > 0
        ? `Added ${active} active document${active === 1 ? "" : "s"}; ${pending} pending human review${flagged ? ` (${flagged} flagged)` : ""}.`
        : pending > 0
          ? `Added ${pending} document${pending === 1 ? "" : "s"}; pending human review${flagged ? ` (${flagged} flagged)` : ""}.`
          : `Added ${active} active document${active === 1 ? "" : "s"}.`;
      window.toast && window.toast(msg, pending > 0 ? "warn" : "good");
    }).catch((e) => {
      setUploadingDocs((prev) => prev.filter((u) => !pendingUploads.some((p) => p.id === u.id)));
      window.toast && window.toast("Upload failed: " + e.message, "warn");
    });
  };

  const removeDoc = (doc) => {
    if (doc?.adminLocked) {
      window.toast && window.toast("This document is locked by Master Admin and can't be removed here.", "warn");
      return;
    }
    if (!window.confirm("Remove this document from your agents' knowledge?")) return;
    kFetch("/knowledge/" + doc.id, { method: "DELETE" })
      .then(() => {
        setDocs((prev) => prev.filter((d) => d.id !== doc.id));
        window.toast && window.toast("Document removed", "warn");
      })
      .catch((e) => window.toast && window.toast("Couldn't remove: " + e.message, "warn"));
  };

  const teachAgents = (docId, agentIds) => {
    kFetch("/knowledge/" + docId + "/teach", {
      method: "POST",
      body: JSON.stringify({ agentIds, force: true }),
    })
      .then((updated) => {
        setDocs((prev) => prev.map((d) => d.id === docId ? updated : d));
        setTeaching(null);
        const latestEvent = Array.isArray(updated.taughtLearningEvents) ? updated.taughtLearningEvents[0] : null;
        if (latestEvent && latestEvent.agentIds && latestEvent.agentIds.length) {
          setTeachInsight({ ...latestEvent, documentName: updated.name });
        }
        const named = agentIds
          .map((id) => agents.find((a) => a.id === id)?.name || id.toUpperCase())
          .join(", ");
        window.toast && window.toast(named ? `Taught to ${named}` : "Removed from all agents", "good");
      })
      .catch((e) => {
        const msg = String(e?.message || "");
        if (msg.includes("Brief consistency check")) {
          window.toast && window.toast("Brief consistency risk found. The probe is shown; you can still activate intentionally.", "warn");
          return;
        }
        if (msg.includes("not available in the active environment")) {
          window.toast && window.toast("Selected agent is outside the active environment. Refresh and pick from the current environment only.", "warn");
          return;
        }
        window.toast && window.toast("Couldn't update: " + msg, "warn");
      });
  };

  const reviewDocument = (doc, mode) => {
    const redFlags = doc.redFlagReport?.flags || [];
    const reject = mode === "reject";
    const note = reject
      ? window.prompt("Why is this document rejected? The client will see this reason.", doc.rejectionReason || "")
      : "";
    if (note === null) return;
    const decisions = redFlags.map((flag, index) => ({
      flagId: `${flag.chunkId || ""}:${flag.category || ""}:${index}`,
      chunkId: flag.chunkId,
      category: flag.category,
      decision: reject ? "document_rejected" : mode,
      note: note || "",
    }));
    kFetch("/knowledge/" + doc.id + "/review", {
      method: "POST",
      body: JSON.stringify({
        decisions,
        rejectReason: reject ? (note || "Document rejected during human review.") : undefined,
      }),
    })
      .then((updated) => {
        setDocs((prev) => prev.map((d) => d.id === doc.id ? updated : d));
        window.toast && window.toast(reject ? "Document rejected" : "Document approved for teaching", reject ? "warn" : "good");
      })
      .catch((e) => window.toast && window.toast("Review failed: " + e.message, "warn"));
  };

  return (
    <div
      className={`kn-page${dragging ? " kn-dragging" : ""}`}
      onDragOver={(e) => { e.preventDefault(); setDragging(true); }}
      onDragLeave={(e) => { if (!e.currentTarget.contains(e.relatedTarget)) setDragging(false); }}
      onDrop={(e) => {
        e.preventDefault();
        setDragging(false);
        addDocs(e.dataTransfer.files);
      }}
    >
      <div className="kn-head">
        <div>
          <h2 className="kn-h">Knowledge documents</h2>
          <p className="kn-s">Upload documents for your agents. Low-risk documents become active automatically; high-risk documents wait for Master Admin approval.</p>
        </div>
        <div className="kn-actions">
          <button className="btn btn-primary btn-sm" onClick={() => fileRef.current && fileRef.current.click()}>+ Add document</button>
          <input
            ref={fileRef}
            type="file"
            multiple
            accept=".txt,.md,.csv,.json,.html,.htm,.xml,.yaml,.yml,.js,.ts,.jsx,.tsx,.py,.rb,.java,.c,.cpp,.h,.cs,.go,.rs,.sh,.log,.pdf,.docx,.doc,.xlsx,.xls"
            style={{ display: "none" }}
            onChange={(e) => { addDocs(e.target.files); e.target.value = ""; }}
          />
        </div>
      </div>

      <div className="kn-search">
        <svg width="14" height="14" viewBox="0 0 14 14" fill="none" stroke="currentColor" strokeWidth="1.5"><circle cx="6" cy="6" r="4.5"/><path d="m9.5 9.5 3 3"/></svg>
        <input className="kn-search-i" value={q} onChange={(e) => setQ(e.target.value)} placeholder="Search documents…" />
      </div>


      {teachInsight ? (
        <div style={{ margin: "0 0 14px", border: "1px solid rgba(109,40,217,.22)", background: "linear-gradient(135deg, rgba(109,40,217,.08), rgba(255,255,255,.92))", borderRadius: 16, padding: 16, boxShadow: "0 10px 30px rgba(109,40,217,.08)" }}>
          <div style={{ display: "flex", justifyContent: "space-between", gap: 12, alignItems: "flex-start" }}>
            <div>
              <div style={{ fontSize: 12, fontWeight: 800, letterSpacing: .06, textTransform: "uppercase", color: "#6d28d9" }}>Citrus ready</div>
              <div style={{ fontSize: 17, fontWeight: 800, color: "var(--ink)", marginTop: 3 }}>New learnings taught from {teachInsight.documentName}</div>
              <div style={{ fontSize: 13, color: "var(--ink-2)", marginTop: 4 }}>
                {teachInsight.agentNames?.join(", ") || "Selected agents"} learned this so they can serve the company mission with approved, document-grounded answers.
              </div>
            </div>
            <button className="btn btn-ghost btn-sm" onClick={() => setTeachInsight(null)}>Dismiss</button>
          </div>
          {Array.isArray(teachInsight.learnings) && teachInsight.learnings.length ? (
            <ul style={{ margin: "12px 0 0", paddingLeft: 18, display: "grid", gap: 6, fontSize: 13, color: "var(--ink)" }}>
              {teachInsight.learnings.slice(0, 4).map((l, i) => <li key={i}>{l}</li>)}
            </ul>
          ) : null}
          <div style={{ marginTop: 12, padding: 10, borderRadius: 12, background: "rgba(255,255,255,.65)", fontSize: 13, color: "var(--ink-2)" }}>
            <button className="kn-ask-pill" type="button" onClick={() => askCitrus((teachInsight.suggestedQuestions || [])[0] || `What did ${teachInsight.documentName} teach the agents?`)}><b>Ask Citrus:</b> {(teachInsight.suggestedQuestions || [])[0] || `What did ${teachInsight.documentName} teach the agents?`}</button>
          </div>
        </div>
      ) : null}

      <div className="kn-list">
        {visibleDocs.length === 0 ? (
          <div className="kn-empty">
            No documents yet.
            {" "}<button className="kn-empty-link" onClick={() => fileRef.current && fileRef.current.click()}>Upload one</button>{" "}
            or drag & drop files here. Low-risk uploads become active automatically.
          </div>
        ) : filt.length === 0 && uploadingDocs.length === 0 ? (
          <div className="kn-empty">No documents match "{q}".</div>
        ) : visibleDocs.map(d => {
          const taughtAgents = agents.filter((a) => !!d.taught?.[a.id]);
          const masterReviewOnly = !!d.requiresMasterApproval;
          const redFlags = masterReviewOnly ? [] : (d.redFlagReport?.flags || []);
          const stateKey = knowledgeDocState(d);
          const stateMeta = DOC_STATE_META[stateKey] || DOC_STATE_META.uploaded;
          const detailCount = [
            !d.global,
            d.learningSummary && taughtAgents.length > 0,
            d.knowledgeProbe,
            (redFlags.length > 0),
            Array.isArray(d.taughtLearningEvents) && d.taughtLearningEvents.length > 0,
            d.preview,
          ].filter(Boolean).length;
          const isExpanded = !!expandedDocs[d.id] || !!d.uploadProgress || teaching === d.id || previewing === d.id;
          return (
            <div key={d.id} className={`kn-row kn-row-state-${stateKey}`}>
              {/* Top line: icon + name + actions */}
              <div className="kn-row-line">
                <div className="kn-row-icon">{KIND_ICON[d.kind] || "✎"}</div>
                <div className="kn-row-body">
                  <div className="kn-row-title">
                    <div className="kn-row-n">{d.name}</div>
                    <span className={`kn-state-badge kn-state-${stateMeta.tone}`}>{stateMeta.label}</span>
                    {redFlags.length > 0 && stateKey === "needs_review" ? <span className="kn-warning-badge">⚠ {redFlags.length} flag{redFlags.length === 1 ? "" : "s"}</span> : null}
                  </div>
                  <div className="kn-row-m">
                    {d.charCount ? `${(d.charCount / 1000).toFixed(1)}k chars` : d.size}
                    {" · "}added {timeAgo(d.addedAt)}
                    {d.global ? <span className="kn-row-global"> · global</span> : null}
                  </div>
                </div>
                <div className="kn-row-actions">
                  {stateKey === "approved" || stateKey === "active" ? (
                    <button
                      className="btn btn-ghost btn-sm"
                      onClick={() => {
                        if (stateKey === "active") {
                          teachAgents(d.id, []);
                        } else {
                          setTeaching(d.id);
                          setPickerAgents(Object.fromEntries(agents.map((a) => [a.id, true])));
                          setExpandedDocs((prev) => ({ ...prev, [d.id]: true }));
                        }
                      }}
                    >{stateKey === "active" ? "Deactivate" : "Teach agents"}</button>
                  ) : null}
                  {d.preview ? (
                    <button
                      className="btn btn-ghost btn-sm"
                      onClick={() => {
                        setPreviewing(previewing === d.id ? null : d.id);
                        setExpandedDocs((prev) => ({ ...prev, [d.id]: true }));
                      }}
                    >{previewing === d.id ? "Hide" : "Preview"}</button>
                  ) : null}
                  <button
                    type="button"
                    className="kn-row-toggle"
                    aria-expanded={isExpanded}
                    aria-controls={`knowledge-doc-details-${d.id}`}
                    onClick={() => toggleDocExpanded(d.id)}
                  >
                    {isExpanded ? "Collapse" : "Expand"}{detailCount ? ` details (${detailCount})` : " details"}
                  </button>
                  <button
                    className="kn-row-more"
                    title={d.adminLocked ? "Locked by Master Admin" : "Remove"}
                    disabled={!!d.adminLocked || !!d.uploadProgress}
                    onClick={() => removeDoc(d)}
                    style={(d.adminLocked || d.uploadProgress) ? { opacity: 0.45, cursor: "not-allowed" } : null}
                  >
                    {d.adminLocked ? "🔒" : (d.uploadProgress ? "…" : "×")}
                  </button>
                </div>
              </div>

              {isExpanded ? (
                <div id={`knowledge-doc-details-${d.id}`} className="kn-row-details">
              {teaching === d.id ? (
                <div className="kn-teach-picker">
                  <div className="kn-teach-picker-label">Choose which agents to teach:</div>
                  <div className="kn-teach-picker-agents">
                    {agents.map((a) => (
                      <label key={a.id} className="kn-teach-agent-check">
                        <input
                          type="checkbox"
                          checked={!!pickerAgents[a.id]}
                          onChange={(e) => setPickerAgents((p) => ({ ...p, [a.id]: e.target.checked }))}
                        />
                        <span className="kn-teach-agent-dot" style={{ background: a.palette?.skin || "#999" }} />
                        {a.name}
                      </label>
                    ))}
                  </div>
                  <div className="kn-teach-picker-actions">
                    <button
                      className="btn btn-primary btn-sm"
                      onClick={() => teachAgents(d.id, Object.keys(pickerAgents).filter((id) => pickerAgents[id]))}
                      disabled={!Object.values(pickerAgents).some(Boolean)}
                    >Teach selected</button>
                    <button className="btn btn-ghost btn-sm" onClick={() => setTeaching(null)}>Cancel</button>
                  </div>
                </div>
              ) : null}

              <div className={`kn-review-panel kn-review-${stateMeta.tone}`}>
                <div>
                  <div className="kn-review-title">{stateMeta.label}</div>
                  <div className="kn-review-copy">
                    {masterReviewOnly ? "Citrus safety review is handling this document. No client action is required unless it is rejected with next steps." : stateMeta.next}
                    {d.rejectionReason ? ` Reason: ${d.rejectionReason}` : ""}
                    {d.reviewRequestedByMasterReason ? ` Reviewer note: ${d.reviewRequestedByMasterReason}` : ""}
                  </div>
                </div>
                {stateKey === "needs_review" && !masterReviewOnly ? (
                  <div className="kn-review-actions">
                    <button className="btn btn-ghost btn-sm" onClick={() => reviewDocument(d, "acknowledged_safe")}>Clear flags</button>
                    <button className="btn btn-ghost btn-sm" onClick={() => fileRef.current && fileRef.current.click()}>Edit & re-upload</button>
                    <button className="btn btn-ghost btn-sm" onClick={() => reviewDocument(d, "reject")}>Reject document</button>
                  </div>
                ) : null}
              </div>

              {/* Bottom line: taught-to chips, on their own row so they wrap cleanly */}
              {!d.global && (
                <div className="kn-row-tags">
                  {taughtAgents.length > 0 ? (
                    <>
                      <span className="kn-row-used-l">Taught to</span>
                      {taughtAgents.map((a) => (
                        <span key={a.id} className="kn-row-chip" style={{ background: a.palette?.skin, color: "#fff" }}>{a.name}</span>
                      ))}
                    </>
                  ) : (
                    <span className="kn-row-used-l kn-row-untaught">Not taught to any agent yet</span>
                  )}
                </div>
              )}

              {d.learningSummary && taughtAgents.length > 0 ? (
                <div style={{ margin: "8px 16px 0 56px", padding: 12, borderRadius: 12, background: "rgba(109,40,217,.06)", border: "1px solid rgba(109,40,217,.12)" }}>
                  <div style={{ fontSize: 12, fontWeight: 800, color: "#6d28d9", marginBottom: 6 }}>Extracted learnings added</div>
                  <div style={{ display: "grid", gap: 5, fontSize: 13, color: "var(--ink)" }}>
                    {(d.learningSummary.learnings || []).slice(0, 3).map((l, i) => <div key={i}>• {l}</div>)}
                  </div>
                  <div style={{ marginTop: 8, fontSize: 12, color: "var(--ink-2)" }}>{d.learningSummary.whyImportant}</div>
                  {Array.isArray(d.learningSummary.suggestedQuestions) ? (
                    <div style={{ marginTop: 8, display: "flex", flexWrap: "wrap", gap: 6 }}>
                      {d.learningSummary.suggestedQuestions.slice(0, 3).map((q, i) => <button key={i} type="button" className="kn-ask-pill" onClick={() => askCitrus(q)}>Ask Citrus: {q}</button>)}
                    </div>
                  ) : null}
                </div>
              ) : null}

              {d.knowledgeProbe ? (() => {
                const tone = probeTone(d.knowledgeProbe.status);
                return (
                  <details className="kn-probe-section" style={{ margin: "10px 16px 0 56px", border: `1px solid ${tone.border}`, background: tone.bg, borderRadius: 12, padding: "10px 12px" }}>
                    <summary style={{ cursor: "pointer", fontSize: 13, fontWeight: 800, color: tone.color }}>
                      Probe result · {d.knowledgeProbe.summary || `Your agent answered ${d.knowledgeProbe.correctAnswers || 0} of ${d.knowledgeProbe.totalQuestions || 0} test questions correctly from this document.`}
                    </summary>
                    {Array.isArray(d.knowledgeProbe.chunkScores) && d.knowledgeProbe.chunkScores.length ? (
                      <div style={{ display: "flex", flexWrap: "wrap", gap: 6, marginTop: 8 }}>
                        {d.knowledgeProbe.chunkScores.slice(0, 8).map((score) => (
                          <span key={score.chunkId} style={{ fontSize: 12, padding: "4px 7px", borderRadius: 999, background: "rgba(255,255,255,.7)", border: "1px solid rgba(0,0,0,.08)", color: "var(--ink-2)" }}>
                            {score.chunkId}: {score.confidence == null ? "not probed" : `${score.confidence}%`}
                          </span>
                        ))}
                      </div>
                    ) : null}
                    {Array.isArray(d.knowledgeProbe.questions) && d.knowledgeProbe.questions.length ? (
                      <ul style={{ margin: "8px 0 0", paddingLeft: 18, fontSize: 12, color: "var(--ink-2)" }}>
                        {d.knowledgeProbe.questions.slice(0, 3).map((q) => <li key={q.id}>{q.question}</li>)}
                      </ul>
                    ) : null}
                  </details>
                );
              })() : null}

              {d.uploadProgress ? (
                <div className="kn-progress" role="status" aria-live="polite">
                  <span className="kn-progress-spinner" />
                  <div><b>{d.uploadProgress.label}</b><span>{d.uploadProgress.detail}</span></div>
                </div>
              ) : null}

              {redFlags.length > 0 ? (
                <details className="kn-flag-section" open={stateKey === "needs_review"}>
                  <summary className="kn-flag-summary">
                    <span className="kn-flag-icon">!</span>
                    <span className="kn-flag-label">{redFlags.length} flagged passage{redFlags.length === 1 ? "" : "s"} with clear next steps</span>
                    <span className="kn-flag-time">{d.redFlagReport?.reviewed ? "reviewed" : "needs verification"}</span>
                  </summary>
                  <div className="kn-flag-body">
                    {redFlags.slice(0, 8).map((flag, i) => {
                      const chunk = (d.chunks || []).find((c) => c.id === flag.chunkId);
                      return (
                        <div key={`${flag.chunkId || "flag"}-${i}`} className="kn-flag-event">
                          <div className="kn-flag-head">
                            <span className="kn-flag-cat">{String(flag.category || "red flag").replaceAll("_", " ")}</span>
                            <span className="kn-flag-sev">{flag.severity || "review"}</span>
                            <span className="kn-flag-ref">{flagRef(flag, chunk)}</span>
                          </div>
                          <div className="kn-flag-reason">{plainFlagReason(flag)}</div>
                          {flag.evidence ? <div className="kn-flag-evidence">“{flag.evidence}”</div> : null}
                          <div className="kn-flag-actions">
                            <button className="btn btn-ghost btn-sm" disabled={stateKey !== "needs_review" || masterReviewOnly} onClick={() => reviewDocument(d, "acknowledged_safe")}>Clear all flags</button>
                            <button className="btn btn-ghost btn-sm" disabled={stateKey !== "needs_review" || masterReviewOnly} onClick={() => fileRef.current && fileRef.current.click()}>Edit & re-upload</button>
                            <button className="btn btn-ghost btn-sm" disabled={stateKey !== "needs_review" || masterReviewOnly} onClick={() => reviewDocument(d, "reject")}>Reject document</button>
                          </div>
                        </div>
                      );
                    })}
                  </div>
                </details>
              ) : null}

              {Array.isArray(d.taughtLearningEvents) && d.taughtLearningEvents.length > 0 ? (() => {
                // Most recent teach event first
                const latest = d.taughtLearningEvents[0];
                const allEvents = d.taughtLearningEvents;
                const totalAgents = new Set(allEvents.flatMap((e) => e.agentIds || [])).size;
                return (
                  <details className="kn-learned-section">
                    <summary className="kn-learned-summary">
                      <span className="kn-learned-icon">◎</span>
                      <span className="kn-learned-label">
                        {totalAgents === 1 ? `1 agent learned from this document` : `${totalAgents} agents learned from this document`}
                      </span>
                      <span className="kn-learned-time">{timeAgo(latest.taughtAt)}</span>
                    </summary>
                    <div className="kn-learned-body">
                      {allEvents.slice(0, 3).map((evt, ei) => (
                        <div key={evt.id || ei} className="kn-learned-event">
                          <div className="kn-learned-agents">
                            {(evt.agentNames || evt.agentIds || []).join(", ") || "Agent"}
                          </div>
                          {Array.isArray(evt.learnings) && evt.learnings.length > 0 ? (
                            <ul className="kn-learned-list">
                              {evt.learnings.slice(0, 4).map((l, li) => (
                                <li key={li}>{l}</li>
                              ))}
                            </ul>
                          ) : null}
                          {evt.whyImportant ? (
                            <div className="kn-learned-why">{evt.whyImportant}</div>
                          ) : null}
                        </div>
                      ))}
                    </div>
                  </details>
                );
              })() : null}

              {previewing === d.id && d.preview ? (
                <div className="kn-preview">{d.preview}{d.charCount > 300 ? "…" : ""}</div>
              ) : null}
                </div>
              ) : null}
            </div>
          );
        })}
      </div>

      {window.CitrusLearningsGuide ? <window.CitrusLearningsGuide mode="full" /> : null}

      <LearningsSection
        agents={agents}
        conflicts={conflicts}
        genericLearnings={genericLearnings}
        onGenericChange={setGenericLearnings}
      />

      <div className="kn-tip">
        <div className="kn-tip-mark">i</div>
        <div>
          <div className="kn-tip-t">How approval works</div>
          <div className="kn-tip-s">Upload any document — PDF, Word (.docx), Excel (.xlsx), CSV, or plain text. The server extracts the text automatically. Low-risk documents become active automatically. High-risk or red-flagged documents stay pending until a human reviews them.</div>
        </div>
      </div>

    </div>
  );
}

// ---- CONFLICT CARD ----
function ConflictCard({ conflict, onResolve }) {
  const [busy, setBusy]         = useState(false);
  const [deleteLoser, setDelete] = useState(false);
  const locked = !!conflict.lockedPrecedence;

  const resolve = (action) => {
    setBusy(true);
    kFetch(`/conflicts/${conflict.id}/resolve`, {
      method: "POST",
      body: JSON.stringify({ action, deleteLoser }),
    })
      .then(() => {
        window.toast && window.toast(action === "dismiss" ? "Conflict dismissed" : "Conflict resolved", "good");
        onResolve && onResolve(conflict.id);
      })
      .catch((e) => window.toast && window.toast("Couldn't resolve: " + e.message, "warn"))
      .finally(() => setBusy(false));
  };

  return (
    <div className="cf-card">
      <div className="cf-header">
        <span className="cf-badge">Conflict</span>
        <span className="cf-reason">{conflict.type === "document_document" ? "Which is correct?" : conflict.reason}</span>
      </div>
      <div className="cf-pair">
        <div className="cf-learning">
          <div className="cf-learning-source">{conflict.aDocumentName || "First"}</div>
          <div className="cf-learning-what">{conflict.aQuote || conflict.aWhat}</div>
          {conflict.aWhy && <div className="cf-learning-why">Why: {conflict.aWhy}</div>}
          {conflict.aQuote && !conflict.aDocumentName && <div className="cf-learning-quote">"{conflict.aQuote}"</div>}
        </div>
        <div className="cf-vs">vs</div>
        <div className="cf-learning">
          <div className="cf-learning-source">{conflict.bDocumentName || "Second"}</div>
          <div className="cf-learning-what">{conflict.bQuote || conflict.bWhat}</div>
          {conflict.bWhy && <div className="cf-learning-why">Why: {conflict.bWhy}</div>}
          {conflict.bQuote && !conflict.bDocumentName && <div className="cf-learning-quote">"{conflict.bQuote}"</div>}
        </div>
      </div>
      <div className="cf-actions">
        {locked ? (
          <div className="cf-delete-toggle" style={{ color: "#b91c1c" }}>
            Master Admin locked policy takes precedence. This contradiction is auto-managed.
          </div>
        ) : (
          <>
            <label className="cf-delete-toggle">
              <input type="checkbox" checked={deleteLoser} onChange={(e) => setDelete(e.target.checked)} />
              Delete loser
            </label>
            <button className="cf-btn cf-btn-a" disabled={busy} onClick={() => resolve("keep_a")}>First is correct</button>
            <button className="cf-btn cf-btn-b" disabled={busy} onClick={() => resolve("keep_b")}>Second is correct</button>
            <button className="cf-btn cf-btn-dismiss" disabled={busy} onClick={() => resolve("dismiss")}>Dismiss</button>
          </>
        )}
      </div>
    </div>
  );
}

// ---- LEARNINGS SECTION ----
//  1. Generic lessons — manually added by the owner, apply to every agent.
//  2. Per-agent lessons — auto-extracted from conversations, per agent.
function LearningsSection({ agents = [], conflicts = [], genericLearnings = [], onGenericChange }) {
  const [addingGeneric, setAddingGeneric] = useState(false);
  const [draft, setDraft] = useState({ what: "", why: "" });
  const [saving, setSaving] = useState(false);
  const [removing, setRemoving] = useState(null);
  const [agentCollapsed, setAgentCollapsed] = useState({});
  const [detecting, setDetecting] = useState(false);
  const [customerNameMap, setCustomerNameMap] = useState({});

  // Fetch customer profiles once on mount to resolve raw IDs to names
  useEffect(() => {
    kFetch("/customers/memory")
      .then((profiles) => {
        const map = {};
        (profiles || []).forEach((p) => { if (p.from) map[p.from] = p.name; });
        setCustomerNameMap(map);
      })
      .catch(() => {}); // silently ignore — fallback to raw IDs
  }, []);

  const agentConflictsFor = (id) => conflicts.filter((c) => c.status === "open" && c.scope === "agent" && c.agentId === id);

  const detectConflicts = (agentId) => {
    setDetecting(true);
    kFetch("/conflicts/detect", {
      method: "POST",
      body: JSON.stringify(agentId ? { agentId } : {}),
    })
      .then((data) => {
        if (data.autoResolved > 0) {
          window.toast && window.toast(`Auto-resolved ${data.autoResolved} conflict${data.autoResolved === 1 ? "" : "s"}`, "good");
        } else if (data.found > 0) {
          window.toast && window.toast(`Found ${data.found} conflict${data.found === 1 ? "" : "s"} — review below`, "warn");
        } else {
          window.toast && window.toast("No conflicts found", "good");
        }
      })
      .catch((e) => window.toast && window.toast("Detection failed: " + e.message, "warn"))
      .finally(() => setDetecting(false));
  };

  const agentsWithLearnings = agents.filter((a) => (a.learnings || []).length > 0);
  const toggleAgent = (id) => setAgentCollapsed((p) => ({ ...p, [id]: !p[id] }));

  const addGeneric = () => {
    if (!draft.what.trim()) { window.toast && window.toast("Describe the lesson first", "warn"); return; }
    setSaving(true);
    kFetch("/learnings", {
      method: "POST",
      body: JSON.stringify({ what: draft.what.trim(), why: draft.why.trim() || null }),
    })
      .then((l) => {
        onGenericChange && onGenericChange([...genericLearnings, l]);
        setDraft({ what: "", why: "" });
        setAddingGeneric(false);
        window.toast && window.toast("Generic lesson added — all agents will use it", "good");
      })
      .catch((e) => window.toast && window.toast("Couldn't add: " + e.message, "warn"))
      .finally(() => setSaving(false));
  };

  const removeGeneric = (id) => {
    if (!window.confirm("Remove this lesson? All agents will stop using it.")) return;
    setRemoving(id);
    kFetch("/learnings/" + id, { method: "DELETE" })
      .then(() => {
        onGenericChange && onGenericChange(genericLearnings.filter((l) => l.id !== id));
        window.toast && window.toast("Lesson removed", "warn");
      })
      .catch((e) => window.toast && window.toast("Couldn't remove: " + e.message, "warn"))
      .finally(() => setRemoving(null));
  };

  const toggleGenericPriority = (id, current) => {
    const next = current === "high" ? "low" : "high";
    kFetch("/learnings/" + id, {
      method: "PATCH",
      body: JSON.stringify({ priority: next }),
    })
      .then((data) => {
        onGenericChange && onGenericChange(genericLearnings.map((l) => l.id === id ? { ...l, priority: next } : l));
        window.toast && window.toast(
          next === "high" ? "Marked high priority" : "Marked low priority",
          "good",
        );
      })
      .catch(() => window.toast && window.toast("Couldn't update priority", "warn"));
  };

  const unlearnAgent = (agentId, learningId) => {
    if (!window.confirm("Remove this learning? The agent will stop using it.")) return;
    setRemoving(learningId);
    kFetch(`/agents/${agentId}/learnings/${learningId}`, { method: "DELETE" })
      .then(() => window.toast && window.toast("Learning removed", "warn"))
      .catch((e) => window.toast && window.toast("Couldn't remove: " + e.message, "warn"))
      .finally(() => setRemoving(null));
  };

  const totalAgentLearnings = agentsWithLearnings.reduce((n, a) => n + (a.learnings || []).length, 0);

  const [knStatsRange, setKnStatsRange] = useState("alltime");

  const renderKnStats = () => {
    const allLearnings = agents.flatMap((a) => a.learnings || []);
    const todayStr = new Date().toLocaleDateString();
    const scoped = knStatsRange === "today"
      ? allLearnings.filter((l) => l.at && new Date(l.at).toLocaleDateString() === todayStr)
      : allLearnings;
    const disabled   = scoped.filter((l) => l.disabled).length;
    const active     = scoped.length - disabled;
    const high       = scoped.filter((l) => l.priority === "high").length;
    const low        = scoped.filter((l) => l.priority !== "high" && !l.disabled).length;
    const openConfs  = (conflicts || []).filter((c) => c.status === "open").length;
    return (
      <div className="kn-lstats-wrap">
        <div className="kn-lstats-header">
          <span className="kn-lstats-title">Learning stats</span>
          <div className="kn-lstats-toggle">
            <button className={`ad-lstats-btn${knStatsRange === "today" ? " is-on" : ""}`} onClick={() => setKnStatsRange("today")}>Today</button>
            <button className={`ad-lstats-btn${knStatsRange === "alltime" ? " is-on" : ""}`} onClick={() => setKnStatsRange("alltime")}>All time</button>
          </div>
        </div>
        <div className="kn-lstats-grid">
          {[
            ["Total lessons",   scoped.length, false],
            ["Active",          active,        false],
            ["Disabled",        disabled,      disabled > 0],
            ["High priority",   high,          false],
            ["Low priority",    low,           false],
            ["Contradictions",  openConfs,     openConfs > 0],
          ].map(([label, val, warn]) => (
            <div key={label} className="kn-lstats-cell">
              <div className={`kn-lstats-val${warn ? " kn-lstats-val--warn" : ""}`}>{val}</div>
              <div className="kn-lstats-label">{label}</div>
            </div>
          ))}
        </div>
      </div>
    );
  };

  return (
    <div className="kn-learn-wrap">
      {renderKnStats()}

      {/* ── Generic lessons ── */}
      <div className="kn-learn-section-hd">
        <div>
          <div className="kn-learn-title">Generic lessons</div>
          <div className="kn-learn-sub">Apply to every agent. Add lessons you want all agents to follow regardless of which conversations they've had.</div>
        </div>
        <button className="btn btn-ghost btn-sm" onClick={() => setAddingGeneric(!addingGeneric)}>
          {addingGeneric ? "Cancel" : "+ Add lesson"}
        </button>
      </div>

      {addingGeneric && (
        <div className="kn-learn-add">
          <input
            className="kn-learn-add-input"
            placeholder="The lesson (e.g. Always mention the 30-day return policy)"
            value={draft.what}
            onChange={(e) => setDraft((d) => ({ ...d, what: e.target.value }))}
            onKeyDown={(e) => { if (e.key === "Enter") addGeneric(); if (e.key === "Escape") setAddingGeneric(false); }}
            autoFocus
          />
          <input
            className="kn-learn-add-input"
            placeholder="Why (optional — e.g. Customers often ask about returns)"
            value={draft.why}
            onChange={(e) => setDraft((d) => ({ ...d, why: e.target.value }))}
            onKeyDown={(e) => { if (e.key === "Enter") addGeneric(); if (e.key === "Escape") setAddingGeneric(false); }}
          />
          <div style={{ display: "flex", gap: 8 }}>
            <button className="btn btn-primary btn-sm" onClick={addGeneric} disabled={saving}>{saving ? "Saving…" : "Add"}</button>
            <button className="btn btn-ghost btn-sm" onClick={() => { setAddingGeneric(false); setDraft({ what: "", why: "" }); }}>Cancel</button>
          </div>
        </div>
      )}

      {genericLearnings.length === 0 ? (
        <div className="kn-learn-empty">No generic lessons yet. Add one above and every agent will follow it.</div>
      ) : (
        <div className="kn-learn-list kn-learn-list-generic">
          {[...genericLearnings].reverse().map((l) => (
            <div key={l.id} className="kn-learn-row">
              <div className="kn-learn-bullet kn-learn-bullet-generic" />
              <div className="kn-learn-body">
                <div className="kn-learn-what" style={{ display: "flex", alignItems: "center", gap: 6, flexWrap: "wrap" }}>
                  <span>{l.what}</span>
                  <span
                    className={`ls-priority-badge ls-priority-${l.priority || "high"}`}
                    title={l.priority === "low"
                      ? "Low priority — memory can override this rule"
                      : "High priority — memory conflicts drop the memory fact"}
                  >{(l.priority || "high") === "high" ? "HIGH" : "LOW"}</span>
                  {(l.conflictCount || 0) > 0 && (
                    <span className="ls-conflict-count" title={`${l.conflictCount} customer memory fact${l.conflictCount === 1 ? "" : "s"} dropped to protect this rule`}>
                      {l.conflictCount} {l.conflictCount === 1 ? "customer" : "customers"}
                    </span>
                  )}
                </div>
                {l.why && <div className="kn-learn-why">Why: {l.why}</div>}
                <div className="kn-learn-meta">{l.at ? `Added ${timeAgo(l.at)}` : ""}<span className="kn-learn-badge">all agents</span></div>
              </div>
              <div style={{ display: "flex", gap: 4, alignItems: "center", flexShrink: 0 }}>
                {l.adminLocked ? (
                  <span className="kn-learn-badge" title="Set by your administrator — cannot be edited">admin</span>
                ) : (
                  <>
                    <button
                      className={`ls-priority-toggle ls-priority-toggle-${l.priority || "high"}`}
                      title={`Switch to ${(l.priority || "high") === "high" ? "low" : "high"} priority`}
                      onClick={() => toggleGenericPriority(l.id, l.priority || "high")}
                    >{(l.priority || "high") === "high" ? "↓ Low" : "↑ High"}</button>
                    <button
                      className="kn-learn-del"
                      title="Remove this lesson"
                      disabled={removing === l.id}
                      onClick={() => removeGeneric(l.id)}
                    >{removing === l.id ? "…" : "×"}</button>
                  </>
                )}
              </div>
            </div>
          ))}
        </div>
      )}

      {/* ── Per-agent lessons ── */}
      <div className="kn-learn-section-hd" style={{ marginTop: 24 }}>
        <div>
          <div className="kn-learn-title">Agent learnings</div>
          <div className="kn-learn-sub">
            Auto-extracted from each agent's conversations every 30 min.
            {totalAgentLearnings > 0 && ` ${totalAgentLearnings} lesson${totalAgentLearnings !== 1 ? "s" : ""} across ${agentsWithLearnings.length} agent${agentsWithLearnings.length !== 1 ? "s" : ""}.`}
            {" "}Remove any that seem wrong.
          </div>
        </div>
        <button className="btn btn-ghost btn-sm" disabled={detecting} onClick={() => detectConflicts(null)}>
          {detecting ? "Checking…" : "Check conflicts"}
        </button>
      </div>

      {agentsWithLearnings.length === 0 ? (
        <div className="kn-learn-empty">No learnings yet — agents start learning automatically once they've handled a few conversations.</div>
      ) : (
        <div className="kn-learn-agents">
          {agentsWithLearnings.map((a) => {
            const isOpen = !agentCollapsed[a.id];
            const learnings = [...(a.learnings || [])].reverse();
            const pinnedCount = learnings.filter((l) => !!l.pinned && !l.disabled).length;
            const aConflicts = agentConflictsFor(a.id);
            const conflictBadge = aConflicts.length > 0 ? ` · ${aConflicts.length} conflict${aConflicts.length !== 1 ? "s" : ""}` : "";
            return (
              <div key={a.id} className="kn-learn-agent">
                <button className="kn-learn-agent-hd" onClick={() => toggleAgent(a.id)}>
                  <span className="kn-learn-agent-dot" style={{ background: a.palette?.skin || "#999" }} />
                  <span className="kn-learn-agent-name">{a.name}</span>
                  <span className="kn-learn-agent-ct">
                    {learnings.length} lesson{learnings.length !== 1 ? "s" : ""}
                    {pinnedCount > 0 ? ` · ${pinnedCount} pinned` : ""}
                    {conflictBadge && <span className="cf-agent-badge">{conflictBadge}</span>}
                  </span>
                  <span className="kn-learn-agent-chev">{isOpen ? "▲" : "▼"}</span>
                </button>

                {isOpen && (
                  <div style={{ padding: "0 14px 14px" }}>
                    {aConflicts.length > 0 && (
                      <div className="cf-list" style={{ marginTop: 10 }}>
                        {aConflicts.map((c) => <ConflictCard key={c.id} conflict={c} />)}
                      </div>
                    )}
                    <div className="kn-learn-list" style={{ marginTop: aConflicts.length > 0 ? 10 : 0 }}>
                      {learnings.map((l, idx) => (
                        <div key={l.id || idx} className={`kn-learn-row${l.disabled ? " kn-learn-row--disabled" : ""}`}>
                          <div className="kn-learn-bullet" style={{ background: a.palette?.skin || "#999" }} />
                          <div className="kn-learn-body">
                            <div className="kn-learn-what" style={{ display: "flex", alignItems: "center", gap: 6, flexWrap: "wrap" }}>
                              <span>{l.what}</span>
                              {l.pinned && !l.disabled ? (
                                <span
                                  style={{
                                    fontSize: 10,
                                    fontWeight: 700,
                                    textTransform: "uppercase",
                                    letterSpacing: 0.05,
                                    padding: "2px 7px",
                                    borderRadius: 20,
                                    background: "color-mix(in oklab, #7c3aed 14%, transparent)",
                                    color: "#6d28d9",
                                  }}
                                  title="Pinned learning — protected from cap trimming"
                                >
                                  pinned
                                </span>
                              ) : null}
                            </div>
                            {l.why && <div className="kn-learn-why">Why: {l.why}</div>}
                            {l.sourceQuote && <div className="kn-learn-quote">"{l.sourceQuote}"</div>}
                            <div className="kn-learn-meta">
                              {l.disabled ? <span className="cf-disabled-label">disabled</span> : (l.at ? `Learned ${timeAgo(l.at)}` : "")}
                              {l.sourceConvos && l.sourceConvos.length > 0 && (
                                <span className="kn-learn-src">
                                  {" · "}
                                  {l.sourceConvos.length} conversation{l.sourceConvos.length !== 1 ? "s" : ""}
                                  {" ("}
                                  {l.sourceConvos
                                    .filter((c, i, arr) => arr.findIndex((x) => x.from === c.from) === i)
                                    .map((c) => customerNameMap[c.from] || (c.from ? c.from.replace(/^[^:]+:/, "") : null) || c.channel || "?")
                                    .join(", ")}
                                  {")"}
                                </span>
                              )}
                            </div>
                          </div>
                          {l.id ? (
                            <button
                              className="kn-learn-del"
                              title="Remove this learning"
                              disabled={removing === l.id}
                              onClick={() => unlearnAgent(a.id, l.id)}
                            >{removing === l.id ? "…" : "×"}</button>
                          ) : null}
                        </div>
                      ))}
                    </div>
                  </div>
                )}
              </div>
            );
          })}
        </div>
      )}
    </div>
  );
}

function timeAgo(iso) {
  if (!iso) return "—";
  const ms = Date.now() - new Date(iso).getTime();
  const min = Math.floor(ms / 60000);
  if (min < 1) return "just now";
  if (min < 60) return `${min}m ago`;
  const hr = Math.floor(min / 60);
  if (hr < 24) return `${hr}h ago`;
  return `${Math.floor(hr / 24)}d ago`;
}

window.KnowledgeTab = KnowledgeTab;
