// Context Workspace — a Cowork-style CONTEXT for a pinned entity.
// 4 panels: Side (menu + create chat + Conversations + Related) | Chat (focus header + thread + follow-ups + input)
//           | Results (navigatable widgets + agent output) | Context (Global + Local + Related context + Collaterals)
// Self-contained; gated on the "workspace" rail icon. TEMPORARY placement — will be relocated later.

// ---------- sample data (focus entity = the "Epic set" collateral on Planning › Unified Search Revamp) ----------
const CTX_PEOPLE = {
  simon: { initials: "SS", tint: "#b6892e", name: "Simon" },
  maya:  { initials: "MC", tint: "#3b5cf6", name: "Maya" },
  tomas: { initials: "TB", tint: "#0e7c66", name: "Tomas" },
  lena:  { initials: "LH", tint: "#c2410c", name: "Lena" },
};
const CTX_FOCUS = {
  kind: "Epic", name: "Indexing epic",
  initiative: "Unified Search Revamp", step: "Planning",
  state: "Draft",                       // Draft · Published
  target: { system: "jira", label: "TEK · Indexing epic", url: "#" },
  collaborators: ["simon", "maya", "tomas"], agents: ["Planning agent"],
};

const CTX_CONVERSATIONS = [
  { id: "x1", title: "Story map workshop", people: ["simon", "maya"], agent: true, snippet: "Break the search epic into indexing, relevance, UI…", time: "2h", mine: true, needs: false, joined: true },
  { id: "x2", title: "Estimation pass on epics", people: ["simon", "tomas"], agent: true, snippet: "Reindex job is the long pole — 8 pts.", time: "1d", mine: false, needs: true, joined: true },
  { id: "x3", title: "Acceptance criteria · relevance", people: ["maya"], agent: true, snippet: "Proposing nDCG@10 ≥ 0.8 as the gate.", time: "2d", mine: false, needs: false, joined: false },
  { id: "x4", title: "Edge cases · multi-language", people: ["tomas"], agent: true, snippet: "CJK tokenization changes the analyzer.", time: "4d", mine: false, needs: false, joined: false },
];

// Related ENTITIES (side-panel navigation)
const RELATED = {
  Children: [
    { id: "r1", name: "TEK-512 · Reindex job", kind: "story" },
    { id: "r2", name: "TEK-518 · Language analyzer", kind: "story" },
    { id: "r3", name: "TEK-520 · Relevance eval harness", kind: "story" },
  ],
  Parents: [{ id: "r4", name: "Unified Search Revamp", kind: "initiative" }],
  Siblings: [
    { id: "r5", name: "Architecture · ADR", kind: "collateral" },
    { id: "r6", name: "PRD", kind: "collateral" },
  ],
};

const CTX_THREAD = [
  { role: "user", who: "simon", md: "Draft the epic breakdown from the approved PRD and architecture. Relevance needs a measurable acceptance criterion." },
  { role: "agent", md: "I split the work into **three epics** — **Indexing** (4 stories), **Relevance** (4 stories), and **Result UI** (3 stories) — **11 stories** total. The Relevance epic carries a measurable AC: `nDCG@10 ≥ 0.8` on the curated eval set.\n\nSee the **Epic breakdown** in the Results panel →" },
  { role: "user", who: "maya", md: "Good. Lay out the individual stories with point estimates so we can see the weight." },
  { role: "agent", md: "Here's the full **story table** — 11 stories with estimates. The **reindex job (TEK-512)** is the heaviest at **8 points**; the index schema and relevance ranker are 5 each, and the result-card UI and language analyzer are 3 each.\n\nThe **story table** is on the right →" },
  { role: "user", who: "tomas", md: "Which of these is the critical path? I'm worried the reindex blocks everything." },
  { role: "agent", md: "You're right. The **critical path** runs **Schema → Reindex → Ranker → Eval**. **TEK-512 (Reindex job, 8 pts)** is the long pole — the ranker and eval harness can't start until the reindex lands.\n\nSee the **critical-path map** →" },
  { role: "user", who: "maya", md: "Add a story for the multi-language analyzer — Tomas raised CJK tokenization in the edge-case chat." },
  { role: "agent", md: "Added **TEK-518 “Language-aware analyzer (incl. CJK)”** under the **Indexing** epic, estimated at **3 points**, and cited the multi-language edge-case conversation. The default analyzer mis-splits CJK, so we need a per-locale analyzer for indexing and query tokenization to match.\n\nThe **story card** is on the right →" },
  { role: "user", who: "simon", md: "Before we commit the plan, what's the implementation risk on the delivery branch?" },
  { role: "agent", md: "I ran an **implementation-risk** pass on `features/new-chat-frontend` — overall **68/100**. Two **high** risks: a race condition between streaming chunks and the optimistic message, and virtualization breaking scroll-to-bottom on tool-call expansion. Plus a medium-risk pinned-context migration with **no backfill**.\n\nFull breakdown — the **Implementation risk** widget on the right →" },
  { role: "user", who: "simon", md: "Understood. Once the Product Owner signs off we publish the epic set to Jira." },
  { role: "agent", md: "The **epic set** is ready for review and the **story map** is in sync. When the exit gate is met, publish the epic set to **TEK** and I'll keep the estimates and critical-path updated as stories move." },
];

const CTX_WIDGETS = [
  { id: "w1", title: "Epic breakdown", kind: "epics", turn: 1, msg: 1 },
  { id: "w2", title: "Story table (11)", kind: "stories", turn: 2, msg: 3 },
  { id: "w3", title: "Critical-path map", kind: "path", turn: 3, msg: 5 },
  { id: "w4", title: "Language analyzer story", kind: "story", turn: 4, msg: 7 },
  { id: "w0", title: "Implementation risk", kind: "risk", turn: 5, msg: 9 },
];
// data (JSON) preview for each widget — same widget/data concept as the chat-flow widgets
const CTX_WIDGET_DATA = {
  w0: { story: "TEK-Search", overall_risk: 42, top: [{ id: "r1", severity: "medium", area: "reindex throughput" }, { id: "r2", severity: "low", area: "analyzer locale coverage" }] },
  w1: { epics: [{ name: "Indexing", stories: 4 }, { name: "Relevance", stories: 4 }, { name: "Result UI", stories: 3 }] },
  w2: { total: 11, stories: [["TEK-512", "Reindex job", 8], ["TEK-513", "Index schema", 5], ["TEK-514", "Relevance ranker", 5], ["TEK-518", "Language analyzer", 3], ["TEK-521", "Result card UI", 3]] },
  w3: { path: ["Schema", "Reindex", "Ranker", "Eval"], critical: "TEK-512", points: 8 },
  w4: { id: "TEK-518", title: "Language-aware analyzer (incl. CJK)", epic: "Indexing", points: 3, cites: "edge-cases conversation" },
};
const CTX_FOLLOWUPS = ["Estimate the Result-UI epic", "Draft acceptance criteria for indexing", "Find overlapping Jira stories", "Generate a test plan for the reindex", "Summarize open decisions"];

const GLOBAL_CONTEXT = [
  { id: "g1", name: "Corporate vision", version: "v4", proposed: false },
  { id: "g2", name: "Definition of Done", version: "v2", proposed: true },
  { id: "g3", name: "Epic / story template", version: "v7", proposed: false },
  { id: "g4", name: "Estimation rules", version: "v3", proposed: true },
];
const LOCAL_CONTEXT = [
  { id: "l1", name: "Relevance metric decision", kind: "md", snippet: "nDCG@10 ≥ 0.8 agreed on the curated eval set.",
    body: `## Relevance metric decision

We agreed to gate the **Relevance** epic on \`nDCG@10 ≥ 0.8\`, measured on the curated eval set.

- Eval set: **1,200** labelled queries
- Re-scored nightly after each ranker change
- Owner: Product Owner

> Rationale: nDCG@10 tracks the search-satisfaction survey (r = 0.72), so it is a defensible exit gate.

Open question: do we hold the gate for **per-locale** eval once the CJK analyzer lands?` },
  { id: "l2", name: "Reindex throughput model", kind: "html", snippet: "Interactive estimate · nightly off-peak window.",
    body: `<div style="font-family:inherit">
  <p style="font-size:13px;color:#3a3a3a;margin:0 0 12px">Estimated reindex throughput against the nightly off-peak window (4h). The full corpus is <strong>48M documents</strong>.</p>
  <table style="width:100%;border-collapse:collapse;font-size:12.5px">
    <thead><tr style="text-align:left;color:#9aa0a6;border-bottom:1px solid #ece7da">
      <th style="padding:6px 4px;font-weight:600">Workers</th><th style="padding:6px 4px;font-weight:600">docs/s</th><th style="padding:6px 4px;font-weight:600">Full reindex</th><th style="padding:6px 4px;font-weight:600">Fits window</th></tr></thead>
    <tbody>
      <tr style="border-bottom:1px solid #f4f1ea"><td style="padding:6px 4px">4</td><td style="padding:6px 4px">1,800</td><td style="padding:6px 4px">7.4 h</td><td style="padding:6px 4px;color:#c2410c">✗</td></tr>
      <tr style="border-bottom:1px solid #f4f1ea"><td style="padding:6px 4px">8</td><td style="padding:6px 4px">3,400</td><td style="padding:6px 4px">3.9 h</td><td style="padding:6px 4px;color:#15803d">✓</td></tr>
      <tr><td style="padding:6px 4px">12</td><td style="padding:6px 4px">4,900</td><td style="padding:6px 4px">2.7 h</td><td style="padding:6px 4px;color:#15803d">✓</td></tr>
    </tbody>
  </table>
  <div style="margin-top:12px;padding:10px;border-radius:8px;background:#fdf3e3;border:1px solid #f2dfbe;font-size:12px;color:#9a6516">Recommended: <strong>8 workers</strong> — clears the corpus with ~1h headroom.</div>
</div>` },
  { id: "l3", name: "CJK tokenization note", kind: "md", snippet: "Default analyzer mis-splits CJK; needs per-locale analyzer.",
    body: `## CJK tokenization note

The default analyzer splits CJK text on whitespace, which **mis-tokenizes** Chinese, Japanese and Korean queries.

\`\`\`
"検索エンジン" → ["検索エンジン"]   // wrong: one token
\`\`\`

We need a **per-locale analyzer** so indexing and query tokenization use the same rules:

- \`ja\` → kuromoji
- \`zh\` → smartcn
- \`ko\` → nori

This is tracked as **TEK-518** under the Indexing epic.` },
];
// Related CONTEXT (content surfaced from children)
const CHILD_CONTEXT = [
  { id: "cc1", name: "Throughput benchmark", from: "TEK-512 · Reindex job", kind: "html" },
  { id: "cc2", name: "Analyzer locale matrix", from: "TEK-518 · Language analyzer", kind: "md" },
];
const CTX_COLLATERALS = [
  { id: "c1", name: "Indexing epic", type: "Epic", state: "Draft", system: "jira", active: true, kind: "md", body: `## Indexing epic\n\nBuild the search index pipeline so queries return in < 200ms p95.\n\n**Stories (4)**\n- TEK-512 · Reindex job — 8 pts (critical path)\n- TEK-513 · Index schema — 5 pts\n- TEK-518 · Language-aware analyzer (incl. CJK) — 3 pts\n- TEK-520 · Relevance eval harness — 3 pts\n\n> Exit gate: a full reindex clears the nightly off-peak window with 8 workers.` },
  { id: "c2", name: "Relevance epic", type: "Epic", state: "Published", system: "jira", active: false, target: { label: "TEK · Relevance epic", url: "#" } },
  { id: "c3", name: "Result UI epic", type: "Epic", state: "Draft", system: "jira", active: false },
  { id: "c4", name: "PRD", type: "PRD", state: "Published", system: "confluence", active: false, target: { label: "Confluence · ENG", url: "#" } },
  { id: "c5", name: "Architecture ADR", type: "ADR", state: "Published", system: "confluence", active: false, target: { label: "Confluence · ARCH", url: "#" } },
];

const CTX_STATE = {
  Draft:     { bg: "#f1f1ee", text: "#5a5a5a", dot: "#98A2B3" },
  Published: { bg: "#e9f6ed", text: "#15803d", dot: "#16A34A" },
};
const CTX_SYS = { jira: { icon: "jira", color: "#2563eb", label: "Jira" }, confluence: { icon: "confluence", color: "#1c4f9c", label: "Confluence" }, git: { icon: "repo", color: "#0e0e10", label: "Git" }, teklens: { icon: "sparkles", color: "#8b5cf6", label: "Teklens" } };

function CtxAvatar({ who, size = 22 }) {
  const p = CTX_PEOPLE[who] || { initials: "?", tint: "#9aa0a6" };
  return <span className="inline-flex items-center justify-center rounded-full text-white font-medium shrink-0" style={{ width: size, height: size, background: p.tint, fontSize: size * 0.4 }}>{p.initials}</span>;
}
function CtxAgentAvatar({ size = 22 }) {
  return <span className="inline-flex items-center justify-center rounded-full text-white shrink-0" style={{ width: size, height: size, background: "linear-gradient(135deg,#3b5cf6,#8b5cf6 55%,#ec4899)" }}><Icon name="sparkles" size={size * 0.55} /></span>;
}
function CtxStatePill({ state, sm }) {
  const s = CTX_STATE[state] || CTX_STATE.Draft;
  return <span className="inline-flex items-center gap-1 rounded-full font-medium" style={{ background: s.bg, color: s.text, fontSize: sm ? 10 : 11, padding: sm ? "1px 6px" : "2px 8px" }}><span className="rounded-full" style={{ width: sm ? 5 : 6, height: sm ? 5 : 6, background: s.dot }} />{state}</span>;
}
const relIcon = (k) => k === "story" ? "story" : k === "initiative" ? "grid" : "file";

// thin collapsed-panel rail
function CtxRail({ icon, label, onExpand }) {
  return (
    <div className="w-10 shrink-0 h-full bg-[#faf8f3] border-r border-[#e8e3d8] flex flex-col items-center pt-2.5 gap-3">
      <button onClick={onExpand} className="p-1.5 rounded hover:bg-black/[0.06] text-[#6b6b6b]" title={`Expand ${label}`}><Icon name="panel-left-open" size={16} /></button>
      <Icon name={icon} size={16} className="text-[#9aa0a6]" />
      <span className="text-[10px] text-[#9aa0a6] font-semibold uppercase tracking-[0.1em] mt-1" style={{ writingMode: "vertical-rl" }}>{label}</span>
    </div>
  );
}
const clamp = (v, lo, hi) => Math.max(lo, Math.min(hi, v));

// Follow-up chip
function FollowChip({ label, onClick }) {
  return (
    <button onClick={onClick} className="flex items-center gap-1 pl-2 pr-2.5 py-1 rounded-full border border-[#ece7da] bg-white text-[12px] text-[#1a1a1a] hover:border-[var(--accent)] hover:text-[var(--accent)] whitespace-nowrap shrink-0">
      <Icon name="wand" size={11} /> {label}
    </button>
  );
}

// Follow-ups row: render as many chips as fit one line; overflow into a … menu
function CtxFollowUps({ items }) {
  const rowRef = useRef(null);
  const measRef = useRef(null);
  const [vis, setVis] = useState(items.length);
  const [open, setOpen] = useState(false);
  useEffect(() => {
    const compute = () => {
      const row = rowRef.current, meas = measRef.current;
      if (!row || !meas) return;
      const avail = row.clientWidth;
      const widths = [...meas.children].map((c) => c.offsetWidth);
      const gap = 6, moreW = 38;
      let used = 0, n = 0;
      for (let i = 0; i < widths.length; i++) {
        const add = widths[i] + (i > 0 ? gap : 0);
        const needMore = i < widths.length - 1;
        if (used + add + (needMore ? gap + moreW : 0) <= avail) { used += add; n++; }
        else break;
      }
      setVis(Math.max(1, n));
    };
    compute();
    const ro = new ResizeObserver(compute);
    if (rowRef.current) ro.observe(rowRef.current);
    return () => ro.disconnect();
  }, [items]);
  const shown = items.slice(0, vis);
  const hidden = items.slice(vis);
  return (
    <div ref={rowRef} className="relative flex items-center gap-1.5">
      {/* hidden measurer */}
      <div ref={measRef} className="absolute opacity-0 pointer-events-none flex items-center gap-1.5 overflow-hidden" style={{ left: -9999, top: 0 }} aria-hidden="true">
        {items.map((f, i) => <FollowChip key={i} label={f} />)}
      </div>
      {shown.map((f, i) => <FollowChip key={i} label={f} />)}
      {hidden.length > 0 && (
        <div className="relative shrink-0">
          <button onClick={() => setOpen((o) => !o)} title={`${hidden.length} more`}
            className="flex items-center justify-center h-[26px] px-2 rounded-full border border-[#ece7da] bg-white text-[#6b6b6b] hover:border-[var(--accent)] hover:text-[var(--accent)]">
            <Icon name="more" size={15} />
          </button>
          {open && (
            <>
              <div className="fixed inset-0 z-30" onClick={() => setOpen(false)} />
              <div className="absolute right-0 bottom-full mb-1.5 w-[240px] bg-white rounded-lg border border-[#ece7da] shadow-xl z-40 py-1">
                {hidden.map((f, i) => (
                  <button key={i} onClick={() => setOpen(false)} className="w-full flex items-center gap-2 px-3 py-1.5 text-[12.5px] text-[#1a1a1a] hover:bg-black/[0.04] text-left">
                    <Icon name="wand" size={12} className="text-[#8b5cf6] shrink-0" /> <span className="truncate">{f}</span>
                  </button>
                ))}
              </div>
            </>
          )}
        </div>
      )}
    </div>
  );
}

// =====================================================================
function ContextWorkspace({ sideOpen = true }) {
  const [activeConvo, setActiveConvo] = useState("x1");
  const [activeWidget, setActiveWidget] = useState(0);
  const [agentOpen, setAgentOpen] = useState(false);
  const [wView, setWView] = useState("widget"); // widget | data preview (same concept as chat widgets)
  const [composer, setComposer] = useState("");
  const [relOpen, setRelOpen] = useState(true);
  const [chatsOpen, setChatsOpen] = useState(true);
  const [sec, setSec] = useState({ global: true, local: true, related: false, collaterals: true });
  const tSec = (k) => setSec((o) => ({ ...o, [k]: !o[k] }));
  // panel collapse + resize
  const [col, setCol] = useState({ side: false, chat: false, panel: false });
  const tCol = (k) => setCol((o) => ({ ...o, [k]: !o[k] }));
  const [pw, setPw] = useState({ side: 280, panel: 468 });
  // combined right panel: tabs + local-context preview
  const [tab, setTab] = useState("results");
  const [preview, setPreview] = useState(null);
  const openPreview = (item) => { setPreview(item); setTab("preview"); };
  const previewItem = preview || CTX_COLLATERALS.find((c) => c.active);
  const compactTabs = pw.panel < 560;
  const convo = CTX_CONVERSATIONS.find((c) => c.id === activeConvo);
  // navigating a result widget scrolls the chat to the message that produced it
  const chatScrollRef = useRef(null);
  useEffect(() => {
    const el = chatScrollRef.current; if (!el) return;
    const wi = CTX_WIDGETS[activeWidget]; if (!wi) return;
    const target = el.querySelector(`[data-msg="${wi.msg}"]`);
    if (target) {
      const top = target.getBoundingClientRect().top - el.getBoundingClientRect().top + el.scrollTop - 16;
      el.scrollTop = Math.max(0, top);
      target.setAttribute("data-flash", "1");
      setTimeout(() => target.removeAttribute("data-flash"), 900);
    }
  }, [activeWidget]);

  return (
    <div className="flex flex-1 min-w-0 bg-white border-l border-[#e8e3d8]">
      {/* ============ PANEL 1 — SIDE: menu + create chat + Conversations + Related ============ */}
      {sideOpen && <>
      <div style={{ width: pw.side }} className="relative shrink-0 h-full flex flex-col bg-[#f4f1ea] border-r border-[#e8e3d8]">
        <div className="px-4 pt-3 pb-2 shrink-0">
          <TeklensLogo />
          <div className="mt-2 flex items-center gap-1.5 text-[12.5px] text-[#5a5a5a]">
            <Icon name="folder" size={13} className="text-[#9b8866]" />
            <span className="font-medium text-[#1f1f1f] truncate">Teklens AI</span>
          </div>
        </div>
        <div className="px-2 mt-1 shrink-0">
          <button className="w-full flex items-center justify-center gap-2 px-3 py-2 rounded-md text-[13.5px] font-medium text-white bg-[var(--accent)] shadow-[0_1px_0_rgba(0,0,0,0.08)] hover:brightness-110">
            <Icon name="plus" size={16} /> New Chat
          </button>
        </div>
        <div className="mx-3 my-3 h-px bg-[#e8e3d8] shrink-0" />
        <div className="flex-1 min-h-0 overflow-y-auto px-2 pb-2 [scrollbar-gutter:stable]">
          {/* Chats */}
          <button onClick={() => setChatsOpen((o) => !o)} className="w-full flex items-center gap-1.5 px-1 py-1 text-[11px] font-semibold uppercase tracking-[0.06em] text-[#6b6b6b]">
            <Icon name={chatsOpen ? "chevron-down" : "chevron-right"} size={12} className="text-[#9aa0a6]" />
            <Icon name="message" size={12} className="text-[#9b8866]" /> Chats
          </button>
          {chatsOpen && (
          <div className="flex flex-col gap-0.5 mt-0.5">
            {CTX_CONVERSATIONS.map((c) => (
              <div key={c.id} role="button" tabIndex={0} onClick={() => setActiveConvo(c.id)}
                className={`group w-full text-left rounded-md px-2 py-1.5 border cursor-pointer ${activeConvo === c.id ? "bg-white border-[#e6dfcd] shadow-[0_1px_2px_rgba(0,0,0,0.03)]" : "border-transparent hover:bg-white/60"}`}>
                <div className="flex items-center gap-1.5">
                  {c.needs && <span className="w-1.5 h-1.5 rounded-full bg-[#D97706] shrink-0" title="Requires interaction" />}
                  <span className="text-[12.5px] font-medium text-[#0e0e10] truncate flex-1">{c.title}</span>
                  {c.mine && <Icon name="user" size={11} className="text-[#9aa0a6] shrink-0" title="Started by you" />}
                  <span className="text-[10.5px] text-[#9aa0a6] shrink-0 group-hover:hidden">{c.time}</span>
                  <div className="hidden group-hover:flex items-center gap-0.5 shrink-0">
                    {c.mine
                      ? <button onClick={(e) => e.stopPropagation()} className="p-0.5 rounded text-[#9aa0a6] hover:bg-black/[0.08] hover:text-[#c2410c]" title="Delete"><Icon name="x" size={13} /></button>
                      : c.joined
                        ? <button onClick={(e) => e.stopPropagation()} className="p-0.5 rounded text-[#9aa0a6] hover:bg-black/[0.08] hover:text-[#c2410c]" title="Leave"><Icon name="x" size={13} /></button>
                        : <button onClick={(e) => e.stopPropagation()} className="p-0.5 rounded text-[var(--accent)] hover:bg-[var(--accent)]/[0.1]" title="Join"><Icon name="plus" size={13} /></button>}
                  </div>
                </div>
                <div className="mt-0.5 text-[11.5px] text-[#6b6b6b] line-clamp-1 leading-snug">{c.snippet}</div>
              </div>
            ))}
          </div>
          )}

          <div className="mx-1 my-3 h-px bg-[#e8e3d8]" />

          {/* Related entities (navigation) */}
          <button onClick={() => setRelOpen((o) => !o)} className="w-full flex items-center gap-1.5 px-1 py-1 text-[11px] font-semibold uppercase tracking-[0.06em] text-[#6b6b6b]">
            <Icon name={relOpen ? "chevron-down" : "chevron-right"} size={12} className="text-[#9aa0a6]" />
            <Icon name="branch" size={12} className="text-[#9b8866]" /> Related
          </button>
          {relOpen && Object.entries(RELATED).map(([group, items]) => (
            <div key={group} className="mt-1">
              <div className="text-[10px] uppercase tracking-[0.06em] text-[#9aa0a6] font-semibold px-1 mb-0.5">{group}</div>
              {items.map((r) => (
                <div key={r.id} className="group flex items-center gap-1.5 rounded-md px-2 py-1 hover:bg-white">
                  <Icon name={relIcon(r.kind)} size={13} className="text-[#9aa0a6] shrink-0" />
                  <button className="text-[12px] text-[#2a2a2a] truncate flex-1 text-left hover:text-[#0e0e10]" title="Move there (change context)">{r.name}</button>
                  <div className="hidden group-hover:flex items-center gap-0.5 shrink-0">
                    <button className="p-1 rounded hover:bg-black/[0.08] text-[#9aa0a6] hover:text-[#0e0e10]" title="Pin"><Icon name="pin" size={11} /></button>
                    <button className="p-1 rounded hover:bg-black/[0.08] text-[#9aa0a6] hover:text-[#0e0e10]" title="Move there"><Icon name="external" size={11} /></button>
                  </div>
                </div>
              ))}
            </div>
          ))}
        </div>
      </div>
      <ResizeHandle onResize={(dx) => setPw((p) => ({ ...p, side: clamp(p.side + dx, 200, 620) }))} />
      </>}

      {/* ============ PANEL 2 — CHAT: focus header + thread + follow-ups + input ============ */}
      {col.chat ? <><CtxRail icon="message" label="Chat" onExpand={() => tCol("chat")} /><div className="flex-1 bg-white border-r border-[#e8e3d8]" /></> : <>
      <div className="relative flex-1 min-w-0 flex flex-col border-r border-[#e8e3d8]">
        <button onClick={() => tCol("chat")} className="absolute top-3 right-3 z-20 flex items-center justify-center w-7 h-7 rounded-md hover:bg-black/[0.08] text-[#9aa0a6]" title="Collapse panel"><Icon name="panel-left-close" size={16} /></button>
        {/* focus / pinned entity header — shown as a pinned badge */}
        <div className="shrink-0 border-b border-[#e8e3d8] bg-white px-4 py-2.5 pr-12 flex items-center gap-3">
          <span className="inline-flex items-center gap-2 pl-2 pr-1 py-1 rounded-md border border-[#ece7da] bg-[#faf8f3] min-w-0">
            <Icon name="pin" size={12} className="text-[var(--accent)] shrink-0" />
            <Icon name={CTX_SYS[CTX_FOCUS.target.system].icon} size={13} style={{ color: CTX_SYS[CTX_FOCUS.target.system].color }} className="shrink-0" />
            <span className="text-[13px] font-medium text-[#0e0e10] truncate" title={CTX_FOCUS.name}>{CTX_FOCUS.name}</span>
            <CtxStatePill state={CTX_FOCUS.state} sm />
            <span className="w-px h-4 bg-[#e8e3d8] mx-0.5 shrink-0" />
            <div className="flex items-center shrink-0">
              {CTX_FOCUS.collaborators.map((w, i) => <span key={i} style={{ marginLeft: i === 0 ? 0 : -6 }} className="ring-2 ring-[#faf8f3] rounded-full"><CtxAvatar who={w} size={18} /></span>)}
              <span style={{ marginLeft: -6 }} className="ring-2 ring-[#faf8f3] rounded-full"><CtxAgentAvatar size={18} /></span>
            </div>
          </span>
          <div className="flex-1" />
        </div>

        {/* markdown conversation (no inline widgets) */}
        <div ref={chatScrollRef} className="flex-1 overflow-y-auto">
          <div className="max-w-[680px] mx-auto px-5 py-6 flex flex-col gap-5">
            {CTX_THREAD.map((m, i) => m.role === "user" ? (
              <div key={i} data-msg={i} className="flex items-start gap-2.5 justify-end ctx-msg rounded-xl">
                <div className="max-w-[80%] bg-[#f4f1ea] rounded-2xl rounded-tr-sm px-3.5 py-2 text-[13.5px] text-[#1a1a1a] leading-relaxed border border-[#ece7da]">{m.md}</div>
                <CtxAvatar who={m.who} size={26} />
              </div>
            ) : (
              <div key={i} data-msg={i} className="flex items-start gap-2.5 ctx-msg rounded-xl">
                <CtxAgentAvatar size={26} />
                <div className="max-w-[84%]">
                  <div className="text-[11px] text-[#9aa0a6] mb-1 font-medium">Planning agent</div>
                  <div className="text-[13.5px] text-[#1a1a1a] leading-[1.65] ctx-md" dangerouslySetInnerHTML={{ __html: mdLite(m.md) }} />
                </div>
              </div>
            ))}
          </div>
        </div>

        {/* follow-up actions — show as many as fit one line, rest in a … menu */}
        <div className="shrink-0 px-4 pt-2 pb-1 border-t border-[#f0ebdd]">
          <div className="max-w-[680px] mx-auto">
            <CtxFollowUps items={CTX_FOLLOWUPS} />
          </div>
        </div>

        {/* input — pinned sources/filters/agents (NOT the focus entity) */}
        <div className="shrink-0 px-4 pb-3 pt-1">
          <div className="max-w-[680px] mx-auto bg-white rounded-2xl border border-[#ece7da] shadow-[0_1px_2px_rgba(0,0,0,0.03)] focus-within:border-[var(--accent)]">
            <div className="flex flex-wrap items-center gap-1.5 px-3 pt-2.5">
              <span className="inline-flex items-center gap-1.5 pl-1.5 pr-1 py-0.5 rounded-md border text-[12px]" style={{ background: "#e6f6f0", borderColor: "#c4ead8" }}><Icon name="robot" size={12} style={{ color: "#0e7c66" }} /> Planning agent <Icon name="x" size={10} className="text-[#9aa0a6]" /></span>
              <span className="inline-flex items-center gap-1.5 pl-1.5 pr-1 py-0.5 rounded-md border text-[12px]" style={{ background: "#f3eefe", borderColor: "#e3d6fb" }}><Icon name="wand" size={12} style={{ color: "#8b5cf6" }} /> Story Drafter <Icon name="x" size={10} className="text-[#9aa0a6]" /></span>
              <span className="inline-flex items-center gap-1.5 pl-1.5 pr-1 py-0.5 rounded-md border text-[12px]" style={{ background: "#eef3fe", borderColor: "#d6e2fb" }}><Icon name="jira" size={12} style={{ color: "#2563eb" }} /> TEK backlog <Icon name="x" size={10} className="text-[#9aa0a6]" /></span>
            </div>
            <textarea value={composer} onChange={(e) => setComposer(e.target.value)} rows={1} placeholder="Message the Planning agent…" className="w-full resize-none px-4 pt-2 pb-1.5 text-[14px] outline-none bg-transparent text-[#0e0e10] placeholder:text-[#9aa0a6]" style={{ minHeight: 36 }} />
            <div className="flex items-center gap-1 px-2.5 pb-2">
              <button className="p-1.5 rounded-md text-[#5a5a5a] hover:bg-black/[0.04]"><Icon name="plus" size={16} /></button>
              <button className="p-1.5 rounded-md text-[#5a5a5a] hover:bg-black/[0.04]"><Icon name="filter" size={16} /></button>
              <button className="p-1.5 rounded-md text-[#5a5a5a] hover:bg-black/[0.04]"><Icon name="paperclip" size={16} /></button>
              <div className="flex-1" />
              <button className="w-8 h-8 rounded-full bg-[var(--accent)] text-white flex items-center justify-center hover:brightness-110"><Icon name="send" size={15} /></button>
            </div>
          </div>
        </div>
      </div>
      </>}

      {/* ============ PANEL 3 — COMBINED: Results / Context / Preview tabs ============ */}
      {col.panel ? <CtxRail icon="book" label="Workspace" onExpand={() => tCol("panel")} /> : <>
      <ResizeHandle onResize={(dx) => setPw((p) => ({ ...p, panel: clamp(p.panel - dx, 360, Math.max(680, window.innerWidth - 560)) }))} />
      <div style={{ width: pw.panel }} className="relative shrink-0 h-full flex flex-col bg-white">
        <button onClick={() => tCol("panel")} className="absolute top-3 right-3 z-20 flex items-center justify-center w-7 h-7 rounded-md hover:bg-black/[0.08] text-[#9aa0a6]" title="Collapse panel"><Icon name="panel-left-close" size={16} /></button>
        {/* Tab bar */}
        <div className="shrink-0 h-11 px-1.5 pr-12 flex items-center gap-0.5 border-b border-[#e8e3d8]">
          <CtxTab icon="image" label="Results" active={tab === "results"} onClick={() => setTab("results")} compact={compactTabs} />
          <CtxTab icon="book" label="Context" active={tab === "context"} onClick={() => setTab("context")} compact={compactTabs} />
          <CtxTab icon="file" label="Collaterals" active={tab === "collaterals"} onClick={() => setTab("collaterals")} compact={compactTabs} />
          <CtxTab icon="eye" label="Preview" active={tab === "preview"} onClick={() => setTab("preview")} compact={compactTabs} />
          <CtxTab icon="tool" label="Log" active={tab === "log"} onClick={() => setTab("log")} compact={compactTabs} />
        </div>

        {/* RESULTS TAB */}
        {tab === "results" && (
          <div className="flex-1 min-h-0 flex flex-col">
            <div className="shrink-0 h-9 px-3 flex items-center gap-2 border-b border-[#f0ebdd] bg-[#fcfbf8]">
              <span className="text-[11px] text-[#9aa0a6] flex-1">{activeWidget + 1} / {CTX_WIDGETS.length}</span>
              <div className="inline-flex items-center bg-[#f4f1ea] rounded-md p-0.5">
                <button onClick={() => setWView("widget")} title="Widget view" className={`px-1.5 py-0.5 rounded ${wView === "widget" ? "bg-white shadow-sm text-[#0e0e10]" : "text-[#6b6b6b]"}`}><Icon name="eye" size={13} /></button>
                <button onClick={() => setWView("data")} title="Data view" className={`px-1.5 py-0.5 rounded ${wView === "data" ? "bg-white shadow-sm text-[#0e0e10]" : "text-[#6b6b6b]"}`}><Icon name="json" size={13} /></button>
              </div>
              <button onClick={() => setActiveWidget((i) => Math.max(0, i - 1))} className="p-1 rounded hover:bg-black/[0.05] text-[#5a5a5a] disabled:opacity-30" disabled={activeWidget === 0}><Icon name="chevron-left" size={16} /></button>
              <button onClick={() => setActiveWidget((i) => Math.min(CTX_WIDGETS.length - 1, i + 1))} className="p-1 rounded hover:bg-black/[0.05] text-[#5a5a5a] disabled:opacity-30" disabled={activeWidget === CTX_WIDGETS.length - 1}><Icon name="chevron-right" size={16} /></button>
            </div>
            <div className="flex-1 overflow-y-auto p-3 min-h-0">
              {wView === "widget"
                ? <WidgetView w={CTX_WIDGETS[activeWidget]} />
                : <pre className="rounded-xl border border-[#ece7da] bg-white p-3.5 font-mono text-[11.5px] text-[#1a1a1a] whitespace-pre-wrap leading-relaxed">{JSON.stringify(CTX_WIDGET_DATA[CTX_WIDGETS[activeWidget].id], null, 2)}</pre>}
            </div>
          </div>
        )}

        {/* LOG TAB */}
        {tab === "log" && (
          <div className="flex-1 overflow-y-auto bg-white p-3">
            <div className="rounded-lg border border-[#ece7da] bg-[#faf8f3] px-3 py-2.5 font-mono text-[11px] text-[#5a5a5a] leading-relaxed whitespace-pre-wrap">{`> fetch_prd(TEK) → ok
> read_adr(architecture) → ok
> plan_epics(scope=search)
  · Indexing (4 stories)
  · Relevance (4 stories)
  · Result UI (3 stories)
> write_ac(relevance, metric="nDCG@10≥0.8")
> flag_critical_path(TEK-512, 8pts)`}</div>
          </div>
        )}

        {/* CONTEXT TAB */}
        {tab === "context" && (
          <div className="flex-1 overflow-y-auto bg-[#faf8f3]">
            {/* Global versioned context */}
            <CtxSection icon="globe" tint="#0e7c66" title="Global context" count={GLOBAL_CONTEXT.length} open={sec.global} onToggle={() => tSec("global")}>
              <div className="flex flex-col gap-1">
                {GLOBAL_CONTEXT.map((g) => (
                  <div key={g.id} className="group flex items-center gap-2 rounded-md px-2 py-1.5 hover:bg-white border border-transparent hover:border-[#ece7da]">
                    <span className={`w-2 h-2 rounded-full shrink-0 ${g.proposed ? "bg-[#D97706]" : "bg-transparent"}`} title={g.proposed ? "Teklens proposed a change — needs feedback" : ""} />
                    <Icon name="book" size={13} className="text-[#9aa0a6] shrink-0" />
                    <span className="text-[12.5px] text-[#1a1a1a] truncate flex-1">{g.name}</span>
                    <span className="text-[10px] text-[#9aa0a6] tabular-nums shrink-0">{g.version}</span>
                    <div className="hidden group-hover:flex items-center gap-0.5 shrink-0">
                      {g.proposed && <button className="text-[10.5px] font-medium text-[#15803d] px-1.5 py-0.5 rounded hover:bg-[#e9f6ed]" title="Approve without moving there">Approve</button>}
                      <button className="p-1 rounded hover:bg-black/[0.08] text-[#9aa0a6] hover:text-[#0e0e10]" title="Edit"><Icon name="edit" size={12} /></button>
                      <button className="p-1 rounded hover:bg-black/[0.08] text-[#9aa0a6] hover:text-[#0e0e10]" title="Go (change context)"><Icon name="external" size={12} /></button>
                    </div>
                  </div>
                ))}
              </div>
            </CtxSection>
            <CtxDivider />
            {/* Local context (memories) — click opens Preview tab */}
            <CtxSection icon="memory" tint="#d97706" title="Local context" count={LOCAL_CONTEXT.length} open={sec.local} onToggle={() => tSec("local")}>
              <div className="flex flex-col gap-1">
                {LOCAL_CONTEXT.map((l) => (
                  <div key={l.id} role="button" tabIndex={0} onClick={() => openPreview(l)} title={l.snippet}
                    className={`group flex items-center gap-2 rounded-md px-2 py-1.5 cursor-pointer border ${preview?.id === l.id && tab === "preview" ? "bg-white border-[#e6dfcd]" : "border-transparent hover:bg-white hover:border-[#ece7da]"}`}>
                    <Icon name="memory" size={13} className="text-[#9aa0a6] shrink-0" />
                    <span className="text-[12.5px] text-[#1a1a1a] truncate flex-1">{l.name}</span>
                    <span className="text-[10px] uppercase tracking-[0.04em] text-[#9aa0a6] tabular-nums shrink-0">{l.kind}</span>
                    <div className="hidden group-hover:flex items-center gap-0.5 shrink-0">
                      <button onClick={(e) => { e.stopPropagation(); openPreview(l); }} className="p-1 rounded hover:bg-black/[0.08] text-[#9aa0a6] hover:text-[#0e0e10]" title="Preview"><Icon name="eye" size={12} /></button>
                      <button onClick={(e) => e.stopPropagation()} className="p-1 rounded hover:bg-black/[0.08] text-[#9aa0a6] hover:text-[#0e0e10]" title="Edit"><Icon name="edit" size={12} /></button>
                      <button onClick={(e) => e.stopPropagation()} className="p-1 rounded hover:bg-black/[0.08] text-[#9aa0a6] hover:text-[#c2410c]" title="Delete"><Icon name="x" size={12} /></button>
                    </div>
                  </div>
                ))}
              </div>
            </CtxSection>
            <CtxDivider />
            {/* Related context — from children */}
            <CtxSection icon="branch" tint="#6b7280" title="Related context" count={CHILD_CONTEXT.length} open={sec.related} onToggle={() => tSec("related")}>
              <div className="text-[10px] uppercase tracking-[0.06em] text-[#9aa0a6] font-semibold px-2 mb-1">From children</div>
              <div className="flex flex-col gap-1">
                {CHILD_CONTEXT.map((c) => (
                  <div key={c.id} title={`from ${c.from}`} className="group flex items-center gap-2 rounded-md px-2 py-1.5 hover:bg-white border border-transparent hover:border-[#ece7da]">
                    <Icon name="story" size={13} className="text-[#9aa0a6] shrink-0" />
                    <span className="text-[12.5px] text-[#1a1a1a] truncate flex-1">{c.name}</span>
                    <span className="text-[10px] uppercase tracking-[0.04em] text-[#9aa0a6] shrink-0">{c.kind}</span>
                    <div className="hidden group-hover:flex items-center gap-0.5 shrink-0">
                      <button className="p-1 rounded hover:bg-black/[0.08] text-[#9aa0a6] hover:text-[#0e0e10]" title="Preview"><Icon name="eye" size={12} /></button>
                      <button className="p-1 rounded hover:bg-black/[0.08] text-[#9aa0a6] hover:text-[#0e0e10]" title="Go to child"><Icon name="external" size={12} /></button>
                    </div>
                  </div>
                ))}
              </div>
            </CtxSection>
          </div>
        )}

        {/* COLLATERALS TAB */}
        {tab === "collaterals" && (
          <div className="flex-1 overflow-y-auto bg-[#faf8f3] p-3">
            <div className="flex flex-col gap-2">
              {CTX_COLLATERALS.map((c) => (
                <div key={c.id} className={`rounded-lg p-2.5 ${c.active ? "border-2 border-[var(--accent)] bg-white shadow-[0_2px_8px_rgba(31,61,246,0.14)]" : "border border-[#e2ddd0] bg-[#efece4]"}`}>
                  <div className="flex items-center gap-2">
                    <Icon name={CTX_SYS[c.system].icon} size={14} style={{ color: CTX_SYS[c.system].color }} className="shrink-0" />
                    <span className={`text-[12.5px] truncate ${c.active ? "font-semibold text-[#0e0e10]" : "font-medium text-[#0e0e10]"}`}>{c.name}</span>
                    <CtxStatePill state={c.state} sm />
                    <div className="flex-1" />
                    <div className="flex items-center gap-0.5 shrink-0">
                      {!c.active && <button className="p-1 rounded text-[#9aa0a6] hover:bg-black/[0.08] hover:text-[var(--accent)]" title="Make active"><Icon name="circle-dot" size={13} /></button>}
                      <button className="p-1 rounded text-[#9aa0a6] hover:bg-black/[0.08] hover:text-[var(--accent)]" title={c.state === "Published" ? "Update in source system" : "Publish to source system"}><Icon name="upload" size={13} /></button>
                      {c.state === "Published" && c.target && (
                        <a href={c.target.url} className="p-1 rounded text-[#9aa0a6] hover:bg-black/[0.08] hover:text-[var(--accent)]" title={`Open ${c.target.label} — leaves this context`}><Icon name="external" size={13} /></a>
                      )}
                      <button className="p-1 rounded text-[#9aa0a6] hover:bg-black/[0.08] hover:text-[#c2410c]" title="Unpin"><Icon name="x" size={13} /></button>
                    </div>
                  </div>
                </div>
              ))}
            </div>
          </div>
        )}

        {/* PREVIEW TAB */}
        {tab === "preview" && previewItem && (
          <div className="flex-1 min-h-0 flex flex-col bg-white">
            <div className="shrink-0 px-4 py-2.5 border-b border-[#f0ebdd] flex items-center gap-2">
              <Icon name={previewItem.system ? CTX_SYS[previewItem.system].icon : "memory"} size={14} style={previewItem.system ? { color: CTX_SYS[previewItem.system].color } : { color: "#d97706" }} />
              <span className="text-[13px] font-semibold text-[#0e0e10] flex-1 truncate">{previewItem.name}</span>
              <span className="text-[9px] font-semibold uppercase tracking-[0.06em] px-1.5 py-0.5 rounded bg-[#f1f1ee] text-[#9aa0a6]">{previewItem.kind}</span>
              <button className="p-1 rounded hover:bg-black/[0.05] text-[#9aa0a6] hover:text-[#0e0e10]" title="Edit"><Icon name="edit" size={14} /></button>
            </div>
            <div className="flex-1 overflow-y-auto px-5 py-4">
              {previewItem.kind === "html"
                ? <div dangerouslySetInnerHTML={{ __html: previewItem.body }} />
                : <div className="ctx-doc" dangerouslySetInnerHTML={{ __html: mdDoc(previewItem.body) }} />}
            </div>
          </div>
        )}
      </div>
      </>}
    </div>
  );
}

// One tab in the combined right panel
function CtxTab({ icon, label, active, onClick, compact }) {
  return (
    <button onClick={onClick} title={label} className={`flex items-center gap-1.5 ${compact ? "px-2" : "px-2.5"} py-1.5 rounded-md text-[12.5px] font-medium ${active ? "bg-[#f4f1ea] text-[#0e0e10]" : "text-[#6b6b6b] hover:text-[#0e0e10]"}`}>
      <Icon name={icon} size={14} />{!compact && <span>{label}</span>}
    </button>
  );
}

// richer markdown for the preview tab (headings, lists, blockquote, code, fences, bold/inline-code)
function mdDoc(s) {
  const esc = (t) => t.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
  const inline = (t) => esc(t).replace(/\*\*([^*]+)\*\*/g, "<strong>$1</strong>").replace(/`([^`]+)`/g, '<code>$1</code>');
  const lines = s.split("\n");
  let html = "", i = 0, inList = false;
  const closeList = () => { if (inList) { html += "</ul>"; inList = false; } };
  while (i < lines.length) {
    const ln = lines[i];
    if (ln.startsWith("```")) {
      closeList(); const buf = [];
      i++;
      while (i < lines.length && !lines[i].startsWith("```")) { buf.push(lines[i]); i++; }
      i++;
      html += `<pre class="ctx-pre"><code>${esc(buf.join("\n"))}</code></pre>`;
      continue;
    }
    if (/^##\s+/.test(ln)) { closeList(); html += `<h3>${inline(ln.replace(/^##\s+/, ""))}</h3>`; }
    else if (/^#\s+/.test(ln)) { closeList(); html += `<h2>${inline(ln.replace(/^#\s+/, ""))}</h2>`; }
    else if (/^>\s+/.test(ln)) { closeList(); html += `<blockquote>${inline(ln.replace(/^>\s+/, ""))}</blockquote>`; }
    else if (/^[-*]\s+/.test(ln)) { if (!inList) { html += "<ul>"; inList = true; } html += `<li>${inline(ln.replace(/^[-*]\s+/, ""))}</li>`; }
    else if (ln.trim() === "") { closeList(); }
    else { closeList(); html += `<p>${inline(ln)}</p>`; }
    i++;
  }
  closeList();
  return html;
}

function CtxSection({ icon, tint, title, count, open, onToggle, children }) {
  return (
    <div className="px-3 py-3">
      <button onClick={onToggle} className="w-full flex items-center gap-2 mb-2.5">
        <span className="w-6 h-6 rounded-md flex items-center justify-center" style={{ background: tint + "1f", color: tint }}><Icon name={icon} size={13} /></span>
        <span className="text-[12.5px] font-semibold text-[#0e0e10] whitespace-nowrap">{title}</span>
        {count != null && <span className="text-[11px] text-[#9aa0a6] tabular-nums">{count}</span>}
        <Icon name={open ? "chevron-down" : "chevron-right"} size={14} className="ml-auto text-[#9aa0a6]" />
      </button>
      {open && children}
    </div>
  );
}
function CtxDivider() { return <div className="h-px bg-[#e8e3d8] mx-3" />; }

function WidgetView({ w }) {
  if (!w) return null;
  if (w.kind === "risk") return <div className="rounded-xl border border-[#ece7da] bg-white overflow-hidden"><RiskAnalyzer /></div>;
  if (w.kind === "epics") {
    const epics = [{ n: "Indexing", s: 4, c: "#3b5cf6" }, { n: "Relevance", s: 4, c: "#16A34A" }, { n: "Result UI", s: 3, c: "#d97706" }];
    return (
      <div className="rounded-xl border border-[#ece7da] bg-white p-3.5">
        <div className="text-[13px] font-semibold text-[#0e0e10] mb-2.5">{w.title}</div>
        <div className="flex flex-col gap-2">
          {epics.map((e) => (
            <div key={e.n} className="flex items-center gap-2.5">
              <span className="w-2.5 h-2.5 rounded-sm" style={{ background: e.c }} />
              <span className="text-[12.5px] text-[#1a1a1a] flex-1">{e.n}</span>
              <span className="text-[11px] text-[#9aa0a6]">{e.s} stories</span>
              <div className="w-20 h-1.5 rounded-full bg-[#f1efe8] overflow-hidden"><div className="h-full rounded-full" style={{ width: `${e.s / 4 * 100}%`, background: e.c }} /></div>
            </div>
          ))}
        </div>
      </div>
    );
  }
  if (w.kind === "stories") {
    const rows = [["TEK-512", "Reindex job", "8", "#D97706"], ["TEK-513", "Index schema", "5", "#98A2B3"], ["TEK-514", "Relevance ranker", "5", "#98A2B3"], ["TEK-518", "Language analyzer", "3", "#98A2B3"], ["TEK-521", "Result card UI", "3", "#98A2B3"]];
    return (
      <div className="rounded-xl border border-[#ece7da] bg-white overflow-hidden">
        <div className="text-[13px] font-semibold text-[#0e0e10] px-3.5 py-2.5 border-b border-[#f0ebdd]">{w.title}</div>
        <table className="w-full text-[12px]">
          <tbody>
            {rows.map((r) => (
              <tr key={r[0]} className="border-b border-[#f4f1ea] last:border-0">
                <td className="px-3 py-1.5 font-mono text-[11px] text-[#6b6b6b]">{r[0]}</td>
                <td className="px-2 py-1.5 text-[#1a1a1a]">{r[1]}</td>
                <td className="px-3 py-1.5 text-right"><span className="inline-flex items-center justify-center w-5 h-5 rounded text-white text-[10px] font-semibold" style={{ background: r[3] }}>{r[2]}</span></td>
              </tr>
            ))}
          </tbody>
        </table>
      </div>
    );
  }
  if (w.kind === "path") {
    return (
      <div className="rounded-xl border border-[#ece7da] bg-white p-3.5">
        <div className="text-[13px] font-semibold text-[#0e0e10] mb-3">{w.title}</div>
        <div className="flex items-center gap-1.5 text-[11px]">
          {["Schema", "Reindex", "Ranker", "Eval"].map((n, i, a) => (
            <Fragment key={n}>
              <span className={`px-2 py-1 rounded-md ${i === 1 ? "bg-[#fdf3e3] text-[#9a6516] font-semibold border border-[#f2dfbe]" : "bg-[#f4f1ea] text-[#5a5a5a]"}`}>{n}</span>
              {i < a.length - 1 && <span className="w-3 h-px bg-[#d6cfbc]" />}
            </Fragment>
          ))}
        </div>
        <div className="mt-3 text-[11.5px] text-[#9a6516] flex items-center gap-1.5"><Icon name="info" size={12} /> Reindex (TEK-512) is the critical path · 8 pts</div>
      </div>
    );
  }
  return (
    <div className="rounded-xl border border-[#ece7da] bg-white p-3.5">
      <div className="flex items-center gap-2 mb-1.5"><Icon name="story" size={14} className="text-[#3b5cf6]" /><span className="font-mono text-[11px] text-[#6b6b6b]">TEK-518</span></div>
      <div className="text-[13px] font-semibold text-[#0e0e10]">Language-aware analyzer (incl. CJK)</div>
      <div className="mt-1.5 text-[12px] text-[#5a5a5a] leading-snug">Default analyzer mis-splits CJK text; add a per-locale analyzer so indexing and query tokenization match. Cited from the multi-language edge-case conversation.</div>
      <div className="mt-2.5 flex items-center gap-2 text-[11px] text-[#9aa0a6]"><span className="px-1.5 py-0.5 rounded bg-[#f4f1ea]">Indexing epic</span><span>·</span><span>3 pts</span></div>
    </div>
  );
}

function mdLite(s) {
  return s
    .replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;")
    .replace(/\*\*([^*]+)\*\*/g, "<strong>$1</strong>")
    .replace(/`([^`]+)`/g, '<code class="font-mono text-[12px] bg-[#f4f1ea] px-1 py-0.5 rounded">$1</code>')
    .replace(/\n\n/g, "<br/><br/>").replace(/\n/g, "<br/>");
}
