/* Workspace — the priority-picking surface. */
/* global React, FRAMEWORK, ALL_LEAVES, GROUP_BY_ID, LEAF_BY_ID,
   nextPriority, prevPriority, PRIORITY_LABEL, PRIORITY_BLURB,
   priorityCounts, detectTradeoffs, PriorityTag, GroupPill */

// ============================================================================
// LINUS translate — POST to the /api/linus Pages Function which fronts Workers
// AI (Llama 3.3 70B). Falls back to a keyword matcher if the proxy is
// unreachable or the response can't be parsed (works against `npm run dev`
// where no Functions runtime is present).
// ============================================================================
function buildFrameworkSummary() {
  return FRAMEWORK.groups.map(g => {
    const lines = g.leaves.map(l =>
      `  ${l.id} ${l.name} — metric: ${l.metric} — dominates when: ${l.whenDominates}`
    ).join("\n");
    return `## ${String(g.id).padStart(2, "0")} ${g.name}\n${g.desc}\n${lines}`;
  }).join("\n\n");
}

async function translateWithLinus(userText) {
  const framework = buildFrameworkSummary();
  const t0 = performance.now();

  console.groupCollapsed(`%c[LINUS] →  request`, "color:#685ad9;font-weight:600");
  console.log("text:", userText);
  console.log(`framework chars: ${framework.length}`);
  console.groupEnd();

  let r;
  try {
    r = await fetch("/api/linus", {
      method: "POST",
      headers: { "content-type": "application/json" },
      body: JSON.stringify({ text: userText, framework }),
    });
  } catch (netErr) {
    console.error("[LINUS] network error contacting /api/linus:", netErr);
    throw netErr;
  }

  // Read body once — it's JSON whether status is 2xx or 4xx/5xx.
  let parsed;
  let rawText = "";
  try {
    rawText = await r.text();
    parsed = JSON.parse(rawText);
  } catch {
    parsed = null;
  }

  const ms = Math.round(performance.now() - t0);

  if (!r.ok) {
    console.groupCollapsed(`%c[LINUS] ←  HTTP ${r.status}  (${ms}ms)`, "color:#d97706;font-weight:600");
    console.log("body:", parsed || rawText);
    console.groupEnd();
    throw new Error(`/api/linus ${r.status}`);
  }

  if (!parsed) {
    console.error(`[LINUS] non-JSON 200 body (${ms}ms):`, rawText.slice(0, 400));
    throw new Error("/api/linus returned non-JSON 200");
  }

  // The server includes diagnostic fields when the model didn't produce a
  // clean JSON answer (rawSample, finishReason, usage). Surface them so the
  // browser shows exactly what GLM did instead of a silent empty-array fallback.
  const list = Array.isArray(parsed.leaves) ? parsed.leaves : [];
  const headerColor = list.length > 0
    ? "color:#059669;font-weight:600"
    : "color:#d97706;font-weight:600";
  const offTopicSuffix = parsed.off_topic ? " · off-topic" : "";
  console.groupCollapsed(`%c[LINUS] ←  ${list.length} leaves${offTopicSuffix}  (${ms}ms)`, headerColor);
  console.log("leaves:", list);
  if (parsed.off_topic) console.warn("off_topic: true — prompt was not about the QA framework");
  if (parsed.rawSample) console.log("rawSample:", parsed.rawSample);
  if (parsed.finishReason) console.log("finishReason:", parsed.finishReason);
  if (parsed.usage) console.log("usage:", parsed.usage);
  if (parsed.error) console.warn("error:", parsed.error);
  console.groupEnd();

  // Filter to valid leaf IDs from the live framework, dedupe via Set.
  const matched = new Set();
  const dropped = [];
  for (const id of list) {
    if (LEAF_BY_ID[id]) matched.add(id);
    else dropped.push(id);
  }
  if (dropped.length) {
    console.warn(`[LINUS] dropped ${dropped.length} unknown leaf id${dropped.length === 1 ? "" : "s"}:`, dropped);
  }
  return matched;
}

function keywordTranslate(userText) {
  const t = userText.toLowerCase();
  const matched = new Set();
  const kw = (substrings, ids) => {
    for (const s of substrings) if (t.includes(s)) { ids.forEach(i => { if (LEAF_BY_ID[i]) matched.add(i); }); return; }
  };
  kw(["stable", "stability", "thermo", "heat"], ["2.1", "2.2", "2.5"]);
  kw(["aggregat"], ["3.3"]);
  kw(["specific", "off-target", "cross-react"], ["1.2", "4.2", "6.1"]);
  kw(["affinity", "binding"], ["1.1"]);
  kw(["catalyt", "k_cat", "turnover"], ["1.3", "1.5"]);
  kw(["shelf", "storage", "cold chain"], ["2.5"]);
  kw(["immunogen", "anti-drug"], ["4.3"]);
  kw(["yield", "titer", "expression"], ["3.2", "5.1"]);
  kw(["pk", "half-life", "clearance"], ["6.2"]);
  kw(["valenc", "trimer", "trivalent", "oligomer"], ["3.4"]);
  kw(["interpret", "rationale"], ["8.4", "6.3"]);
  kw(["dual-use", "ethic", "consent", "governance"], ["10.1", "10.2", "10.3"]);
  return matched;
}

function Workspace({ project, session, setSession, setScreen, mode }) {
  const [view, setView] = useState("group"); // 'group' | 'grid'
  const [selected, setSelected] = useState(null);
  const [openGroups, setOpenGroups] = useState({ 1: true, 2: true, 3: true, 4: false, 5: false, 6: false, 7: false, 8: false, 9: false, 10: false });
  const [translateInput, setTranslateInput] = useState("");
  const [translateHighlight, setTranslateHighlight] = useState(new Set());
  const [llmThinking, setLlmThinking] = useState(false);
  const [linusOpen, setLinusOpen] = useState(false);
  const linusWrapRef = useRef(null);
  const linusInputRef = useRef(null);

  // Click-outside to close LINUS popover
  useEffect(() => {
    if (!linusOpen) return;
    const onDown = (e) => {
      if (linusWrapRef.current && !linusWrapRef.current.contains(e.target)) {
        setLinusOpen(false);
      }
    };
    document.addEventListener("mousedown", onDown);
    return () => document.removeEventListener("mousedown", onDown);
  }, [linusOpen]);

  // Focus input when popover opens
  useEffect(() => {
    if (linusOpen && linusInputRef.current) {
      const t = setTimeout(() => linusInputRef.current.focus(), 60);
      return () => clearTimeout(t);
    }
  }, [linusOpen]);

  const priorities = session.priorities;
  const counts = useMemo(() => priorityCounts(priorities), [priorities]);
  const detected = useMemo(() => detectTradeoffs(priorities), [priorities]);

  const cycleLeaf = (id, dir = 1) => {
    setSession(s => {
      const p = s.priorities[id] || "unset";
      const nextP = dir === 1 ? nextPriority(p) : prevPriority(p);
      return { ...s, priorities: { ...s.priorities, [id]: nextP } };
    });
  };

  const setLeafPriority = (id, p) => {
    setSession(s => ({ ...s, priorities: { ...s.priorities, [id]: p } }));
  };

  const updateNote = (id, key, value) => {
    setSession(s => ({
      ...s,
      notes: { ...s.notes, [id]: { ...(s.notes[id] || {}), [key]: value } }
    }));
  };

  const onTranslate = async () => {
    if (!translateInput.trim()) return;
    setLlmThinking(true);
    setTranslateHighlight(new Set());

    let matched;
    try {
      matched = await translateWithLinus(translateInput);
    } catch (err) {
      console.warn("[LINUS] LLM call failed, falling back to keyword match:", err);
      matched = keywordTranslate(translateInput);
      console.log(`[LINUS] keyword fallback picked ${matched.size} leaves`);
    }

    // Auto-open any collapsed groups that contain hits, so the highlight is visible.
    if (matched.size > 0) {
      const hitGroups = new Set();
      for (const id of matched) {
        const leaf = LEAF_BY_ID[id];
        if (leaf) hitGroups.add(leaf.groupId);
      }
      setOpenGroups(og => {
        const next = { ...og };
        hitGroups.forEach(gid => { next[gid] = true; });
        return next;
      });
    }

    setTranslateHighlight(matched);
    setLlmThinking(false);
  };

  // Auto-pick first leaf for detail on mount
  useEffect(() => {
    if (!selected) setSelected(ALL_LEAVES[0]);
  }, [selected]);

  return (
    <div style={{ maxWidth: 1440, margin: "0 auto", padding: "0 32px" }}>
      {/* Workspace top bar */}
      <header style={{ paddingTop: 28, paddingBottom: 20, display: "flex", justifyContent: "space-between", alignItems: "flex-end", gap: 24, flexWrap: "wrap" }}>
        <div style={{ minWidth: 0, flex: "1 1 auto" }}>
          <div className="eyebrow violet" style={{ marginBottom: 8 }}>Workspace · Pick the few that matter</div>
          <h1 style={{
            fontFamily: "var(--font-jakarta)",
            fontWeight: 700,
            fontSize: "clamp(1.6rem, 2.6vw, 2.2rem)",
            margin: 0,
            color: "var(--fg-1)",
            letterSpacing: "-0.02em",
            lineHeight: 1.1,
            textWrap: "balance",
          }}>{project || "Untitled project"}</h1>
        </div>

        {/* View toggle + LINUS launcher */}
        <div style={{ display: "flex", gap: 12, alignItems: "center" }}>
          <div ref={linusWrapRef} style={{ position: "relative" }}>
            <button
              onClick={() => setLinusOpen(o => !o)}
              aria-expanded={linusOpen}
              aria-label="Ask LINUS to translate plain language into priorities"
              style={{
                appearance: "none",
                display: "inline-flex", alignItems: "center", gap: 8,
                padding: translateHighlight.size > 0 ? "6px 8px 6px 12px" : "6px 12px",
                height: 38,
                border: "1px solid var(--border-default)",
                background: linusOpen ? "var(--c-violet)" : "rgba(255,255,255,0.6)",
                color: linusOpen ? "#fff" : "var(--c-violet-deep)",
                borderRadius: 999,
                cursor: "pointer",
                fontFamily: "var(--font-mono)", fontSize: 10, letterSpacing: "0.18em",
                transition: "all 160ms",
                boxShadow: linusOpen ? "0 6px 18px rgba(104,90,217,0.28)" : "none",
              }}
            >
              <SparkleIcon />
              <span>LINUS</span>
              {translateHighlight.size > 0 && (
                <span style={{
                  display: "inline-flex", alignItems: "center", justifyContent: "center",
                  minWidth: 22, height: 22, padding: "0 6px",
                  borderRadius: 999,
                  background: linusOpen ? "rgba(255,255,255,0.22)" : "var(--c-violet)",
                  color: "#fff",
                  fontFamily: "var(--font-mono)", fontSize: 10, fontWeight: 700,
                  letterSpacing: "0.04em",
                }}>{translateHighlight.size}</span>
              )}
            </button>
            {linusOpen && (
              <LinusPopover
                inputRef={linusInputRef}
                value={translateInput}
                setValue={setTranslateInput}
                onTranslate={onTranslate}
                thinking={llmThinking}
                matchCount={translateHighlight.size}
                onClear={() => { setTranslateInput(""); setTranslateHighlight(new Set()); }}
                onClose={() => setLinusOpen(false)}
              />
            )}
          </div>

          <span className="mono" style={{ color: "var(--fg-4)" }}>VIEW</span>
          <div style={{
            display: "flex", background: "rgba(255,255,255,0.6)",
            border: "1px solid var(--border-default)", borderRadius: 999, padding: 3,
          }}>
            {[
              { id: "group", label: "By group" },
              { id: "grid",  label: "Full grid" },
            ].map(v => (
              <button key={v.id}
                onClick={() => setView(v.id)}
                style={{
                  appearance: "none",
                  border: 0,
                  padding: "7px 16px",
                  borderRadius: 999,
                  background: view === v.id ? "var(--surface-navy)" : "transparent",
                  color: view === v.id ? "var(--fg-on-dark)" : "var(--fg-3)",
                  fontFamily: "var(--font-jakarta)", fontWeight: 600, fontSize: 12,
                  cursor: "pointer", transition: "all 160ms",
                }}>{v.label}</button>
            ))}
          </div>
        </div>
      </header>

      {/* Counters */}
      <div style={{ marginBottom: 20 }}>
        <CountStrip counts={counts} />
      </div>

      {/* Body — split layout */}
      <div style={{ display: "grid", gridTemplateColumns: "1fr 380px", gap: 22, paddingBottom: 64 }}>
        <div>
          {view === "group" ? (
            <GroupAccordion
              priorities={priorities}
              selected={selected}
              setSelected={setSelected}
              cycleLeaf={cycleLeaf}
              openGroups={openGroups}
              setOpenGroups={setOpenGroups}
              translateHighlight={translateHighlight}
            />
          ) : (
            <FullGrid
              priorities={priorities}
              selected={selected}
              setSelected={setSelected}
              cycleLeaf={cycleLeaf}
              translateHighlight={translateHighlight}
            />
          )}
        </div>
        <aside style={{ position: "sticky", top: 84, alignSelf: "start", maxHeight: "calc(100vh - 100px)", overflowY: "auto", paddingBottom: 16 }}>
          <DetailPanel
            leaf={selected}
            priorities={priorities}
            session={session}
            setLeafPriority={setLeafPriority}
            updateNote={updateNote}
            detected={detected}
          />
        </aside>
      </div>

      {/* Bottom bar — go to output */}
      <div style={{
        position: "sticky", bottom: 0,
        background: "linear-gradient(to bottom, transparent, var(--surface-cream) 30%)",
        padding: "16px 0",
        marginLeft: -32, marginRight: -32,
        paddingLeft: 32, paddingRight: 32,
      }}>
        <div style={{
          display: "flex", alignItems: "center", justifyContent: "space-between",
          gap: 16,
          background: "var(--surface-navy)",
          color: "var(--fg-on-dark)",
          padding: "16px 22px",
          borderRadius: "var(--radius-xl)",
          boxShadow: "0 12px 30px -10px rgba(34,49,83,0.4)",
        }}>
          <div style={{ display: "flex", alignItems: "center", gap: 14, flexWrap: "wrap" }}>
            <span style={{ fontFamily: "var(--font-mono)", fontSize: 11, color: "rgba(255,255,255,0.55)", letterSpacing: "0.18em" }}>READY?</span>
            <span style={{ fontFamily: "var(--font-jakarta)", fontWeight: 600, fontSize: 14 }}>
              {counts.lead} lead · {counts.follow} follow · {detected.length} trade-off{detected.length === 1 ? "" : "s"} detected
            </span>
            {counts.lead < 3 && (
              <span style={{ fontSize: 12, color: "rgba(255,255,255,0.6)", fontStyle: "italic" }}>
                — keep going. Most projects have 3–5 leads.
              </span>
            )}
            {counts.lead > 5 && (
              <span style={{ fontSize: 12, color: "var(--c-emerald-light)", fontStyle: "italic" }}>
                — lots of leads. If everything is a priority, nothing is.
              </span>
            )}
          </div>
          <div style={{ display: "flex", gap: 8 }}>
            <button className="btn btn-ghost" style={{ color: "#fff", borderColor: "rgba(255,255,255,0.25)" }} onClick={() => setScreen("heatmap")}>
              Heatmap
            </button>
            <button className="btn btn-primary" onClick={() => setScreen("pyramid")}>
              Make the poster
              <span aria-hidden style={{ fontFamily: "var(--font-mono)" }}>→</span>
            </button>
          </div>
        </div>
      </div>
    </div>
  );
}

function CountStrip({ counts }) {
  const items = [
    { id: "lead", label: "Lead",   v: counts.lead, target: "3–5", color: "var(--c-violet)" },
    { id: "follow", label: "Follow", v: counts.follow, target: "5–10", color: "var(--c-emerald)" },
    { id: "out", label: "Out",  v: counts.out, target: "the rest", color: "rgba(34,49,83,0.35)" },
    { id: "unset", label: "Unset", v: counts.unset, target: "", color: "rgba(34,49,83,0.20)" },
  ];
  return (
    <div style={{
      display: "grid",
      gridTemplateColumns: "repeat(4, 1fr)",
      background: "rgba(255,255,255,0.6)",
      border: "1px solid var(--border-default)",
      borderRadius: "var(--radius-pill)",
      overflow: "hidden",
    }}>
      {items.map((it, i) => (
        <div key={it.id} style={{
          padding: "8px 14px",
          borderRight: i < items.length - 1 ? "1px solid var(--border-default)" : "0",
          display: "flex", alignItems: "center", gap: 10,
        }}>
          <span style={{ width: 8, height: 8, borderRadius: 999, background: it.color }}></span>
          <div style={{ display: "flex", flexDirection: "column", lineHeight: 1.1 }}>
            <span style={{ fontFamily: "var(--font-jakarta)", fontWeight: 700, fontSize: 15, color: "var(--fg-1)" }}>{it.v}</span>
            <span style={{ fontFamily: "var(--font-mono)", fontSize: 9, color: "var(--fg-4)", letterSpacing: "0.12em", textTransform: "uppercase", marginTop: 2 }}>
              {it.label}{it.target && <span style={{ marginLeft: 4, opacity: 0.7 }}>· {it.target}</span>}
            </span>
          </div>
        </div>
      ))}
    </div>
  );
}

// ============================================================================
// Group accordion view
// ============================================================================
function GroupAccordion({ priorities, selected, setSelected, cycleLeaf, openGroups, setOpenGroups, translateHighlight }) {
  return (
    <div style={{ display: "flex", flexDirection: "column", gap: 8 }}>
      {FRAMEWORK.groups.map(g => {
        const open = openGroups[g.id];
        const groupLeaves = g.leaves;
        const leadCount = groupLeaves.filter(l => priorities[l.id] === "lead").length;
        const followCount = groupLeaves.filter(l => priorities[l.id] === "follow").length;
        const hitCount = groupLeaves.filter(l => translateHighlight.has(l.id)).length;

        return (
          <div key={g.id} style={{
            background: "rgba(255,255,255,0.55)",
            border: "1px solid var(--border-default)",
            borderRadius: "var(--radius-lg)",
            overflow: "hidden",
            transition: "all 200ms var(--ease-out)",
          }}>
            <button
              onClick={() => setOpenGroups(og => ({ ...og, [g.id]: !og[g.id] }))}
              style={{
                appearance: "none", background: "transparent", border: 0, width: "100%",
                display: "flex", alignItems: "center", gap: 16,
                padding: "16px 20px", cursor: "pointer", textAlign: "left",
              }}
            >
              <span style={{
                fontFamily: "var(--font-mono)", fontSize: 12, color: "var(--fg-4)",
                letterSpacing: "0.12em", width: 28,
              }}>{String(g.id).padStart(2, "0")}</span>
              <h3 style={{
                fontFamily: "var(--font-jakarta)", fontSize: 17, fontWeight: 700,
                margin: 0, letterSpacing: "-0.01em",
                color: "var(--fg-1)", flex: 1,
              }}>{g.name}</h3>
              <span style={{ fontSize: 13, color: "var(--fg-4)", fontFamily: "var(--font-mono)" }}>
                {groupLeaves.length} leaves
              </span>
              {(leadCount + followCount > 0) && (
                <span style={{ display: "flex", gap: 4 }}>
                  {leadCount > 0 && <Pip color="var(--c-violet)" n={leadCount} />}
                  {followCount > 0 && <Pip color="var(--c-emerald)" n={followCount} />}
                </span>
              )}
              {hitCount > 0 && (
                <span style={{
                  display: "inline-flex", alignItems: "center", gap: 4,
                  padding: "2px 7px", borderRadius: 999,
                  background: "var(--c-gold)", color: "#fff",
                  fontFamily: "var(--font-jakarta)", fontWeight: 700, fontSize: 11,
                  boxShadow: "0 0 0 2px rgba(245,158,11,0.20)",
                }} title={`${hitCount} leaf${hitCount === 1 ? "" : "es"} matched by LINUS`}>
                  <SparkleDot /> {hitCount}
                </span>
              )}
              <span style={{
                fontFamily: "var(--font-mono)", fontSize: 14, color: "var(--fg-3)",
                transform: open ? "rotate(90deg)" : "rotate(0)", transition: "transform 200ms",
                marginLeft: 8,
              }}>›</span>
            </button>
            {open && (
              <div style={{
                padding: "0 12px 16px",
                display: "grid",
                gridTemplateColumns: "repeat(auto-fill, minmax(170px, 1fr))",
                gap: 6,
              }}>
                {groupLeaves.map(leaf => (
                  <LeafTile
                    key={leaf.id}
                    leaf={leaf}
                    priority={priorities[leaf.id]}
                    selected={selected && selected.id === leaf.id}
                    onSelect={() => setSelected(leaf)}
                    onCycle={() => cycleLeaf(leaf.id)}
                    onCyclePrev={() => cycleLeaf(leaf.id, -1)}
                    highlight={translateHighlight.has(leaf.id)}
                    accent={g.accent}
                    layout="card"
                  />
                ))}
              </div>
            )}
          </div>
        );
      })}
    </div>
  );
}

function Pip({ color, n }) {
  return (
    <span style={{
      display: "inline-flex", alignItems: "center", gap: 4,
      padding: "2px 7px", borderRadius: 999,
      background: color, color: "#fff",
      fontFamily: "var(--font-jakarta)", fontWeight: 700, fontSize: 11,
    }}>{n}</span>
  );
}

function SparkleDot() {
  return (
    <svg width="9" height="9" viewBox="0 0 16 16" style={{ display: "block" }}>
      <path d="M8 1.5 L9.2 6.8 L14.5 8 L9.2 9.2 L8 14.5 L6.8 9.2 L1.5 8 L6.8 6.8 Z" fill="currentColor" />
    </svg>
  );
}

// ============================================================================
// Full periodic-table grid view
// ============================================================================
function FullGrid({ priorities, selected, setSelected, cycleLeaf, translateHighlight }) {
  return (
    <div style={{ display: "flex", flexDirection: "column", gap: 14 }}>
      {FRAMEWORK.groups.map(g => (
        <div key={g.id} style={{ display: "grid", gridTemplateColumns: "150px 1fr", gap: 14, alignItems: "flex-start" }}>
          <div style={{ paddingTop: 6 }}>
            <div style={{
              fontFamily: "var(--font-mono)", fontSize: 10, color: "var(--fg-5)",
              letterSpacing: "0.18em", marginBottom: 4,
            }}>{String(g.id).padStart(2, "0")}</div>
            <div style={{
              fontFamily: "var(--font-jakarta)", fontWeight: 700, fontSize: 14,
              color: g.accent === "violet" ? "var(--c-violet-deep)" : "var(--c-emerald-700)",
              letterSpacing: "-0.01em", lineHeight: 1.2,
            }}>{g.name}</div>
            <div style={{ marginTop: 4, fontSize: 11, color: "var(--fg-4)", lineHeight: 1.4, fontStyle: "italic" }}>{g.desc}</div>
          </div>
          <div style={{
            display: "grid",
            gridTemplateColumns: "repeat(8, 1fr)",
            gap: 4,
          }}>
            {g.leaves.map(leaf => (
              <LeafTile
                key={leaf.id}
                leaf={leaf}
                priority={priorities[leaf.id]}
                selected={selected && selected.id === leaf.id}
                onSelect={() => setSelected(leaf)}
                onCycle={() => cycleLeaf(leaf.id)}
                onCyclePrev={() => cycleLeaf(leaf.id, -1)}
                highlight={translateHighlight.has(leaf.id)}
                accent={g.accent}
                layout="cell"
              />
            ))}
          </div>
        </div>
      ))}
    </div>
  );
}

// ============================================================================
// Leaf tile — two layouts: card (accordion) and cell (periodic-table)
// ============================================================================
function LeafTile({ leaf, priority, selected, onSelect, onCycle, onCyclePrev, highlight, accent, layout }) {
  const isCell = layout === "cell";
  const isLead = priority === "lead";
  const isFollow = priority === "follow";
  const isOut = priority === "out";
  const isUnset = priority === "unset";

  let bg, color, border, shadow;
  if (isLead)   { bg = "var(--c-violet)"; color = "#fff"; border = "transparent"; shadow = "0 8px 18px -8px rgba(104,90,217,0.5)"; }
  else if (isFollow) { bg = "var(--c-emerald)"; color = "#fff"; border = "transparent"; shadow = "0 6px 14px -8px rgba(16,185,129,0.45)"; }
  else if (isOut) { bg = "rgba(34,49,83,0.025)"; color = "rgba(34,49,83,0.36)"; border = "rgba(34,49,83,0.05)"; shadow = "none"; }
  else { bg = "rgba(255,255,255,0.7)"; color = "var(--fg-2)"; border = "var(--border-default)"; shadow = "none"; }

  if (highlight) {
    border = "var(--c-gold)";
    shadow = "0 0 0 2px rgba(245,158,11,0.25)";
  }

  if (selected) {
    border = isLead ? "var(--c-violet-deep)" : isFollow ? "var(--c-emerald-700)" : "var(--fg-1)";
    shadow = `${shadow}, 0 0 0 2px ${isLead ? "rgba(104,90,217,0.35)" : isFollow ? "rgba(16,185,129,0.35)" : "rgba(34,49,83,0.18)"}`;
  }

  const onClick = (e) => {
    if (e.shiftKey) { onCyclePrev(); return; }
    if (selected) { onCycle(); return; }
    onSelect();
  };

  return (
    <button
      onClick={onClick}
      onContextMenu={(e) => { e.preventDefault(); onCyclePrev(); }}
      title={`${leaf.id} ${leaf.name} — click to select, click again to cycle priority`}
      style={{
        appearance: "none",
        background: bg,
        color,
        border: `1px solid ${border}`,
        borderRadius: isCell ? 6 : 10,
        padding: isCell ? "8px 7px" : "10px 12px",
        textAlign: "left",
        cursor: "pointer",
        transition: "transform 140ms var(--ease-out), background 140ms, color 140ms, border-color 140ms, box-shadow 140ms",
        boxShadow: shadow,
        position: "relative",
        overflow: "hidden",
        fontFamily: "var(--font-jakarta)",
        minHeight: isCell ? 64 : 60,
        display: "flex", flexDirection: "column", justifyContent: "space-between",
      }}
      onMouseEnter={(e) => { if (!selected) e.currentTarget.style.transform = "translateY(-2px)"; }}
      onMouseLeave={(e) => { e.currentTarget.style.transform = "translateY(0)"; }}
    >
      <div style={{ display: "flex", alignItems: "baseline", justifyContent: "space-between", gap: 6 }}>
        <span style={{
          fontFamily: "var(--font-mono)", fontSize: 10,
          opacity: 0.7,
        }}>{leaf.id}</span>
        {(isLead || isFollow) && (
          <span style={{
            fontFamily: "var(--font-jakarta)", fontWeight: 700, fontSize: 9,
            letterSpacing: "0.14em", opacity: 0.85, textTransform: "uppercase",
          }}>{isLead ? "Lead" : "Follow"}</span>
        )}
      </div>
      <div style={{
        fontSize: isCell ? 11.5 : 13,
        fontWeight: 600,
        letterSpacing: "-0.005em",
        lineHeight: 1.15,
        marginTop: isCell ? 4 : 6,
        textWrap: "balance",
      }}>{isCell ? leaf.short : leaf.short}</div>
      {!isCell && (
        <div style={{
          fontSize: 11, marginTop: 4, opacity: 0.7,
          fontFamily: "var(--font-serif)", fontStyle: "italic",
          lineHeight: 1.3,
        }}>{leaf.name !== leaf.short ? leaf.name : ""}</div>
      )}
    </button>
  );
}

// ============================================================================
// Detail panel — right column
// ============================================================================
function DetailPanel({ leaf, priorities, session, setLeafPriority, updateNote, detected }) {
  if (!leaf) return null;
  const priority = priorities[leaf.id] || "unset";
  const group = GROUP_BY_ID[leaf.groupId] || GROUP_BY_ID[Number(String(leaf.id).split(".")[0])];
  const note = session.notes[leaf.id] || {};

  return (
    <div style={{
      background: "rgba(255,255,255,0.85)",
      border: "1px solid var(--border-default)",
      borderRadius: "var(--radius-xl)",
      padding: 24,
      boxShadow: "var(--shadow-card)",
    }}>
      <div style={{ display: "flex", alignItems: "center", gap: 8, marginBottom: 14 }}>
        <GroupPill group={group} />
        <span style={{ fontFamily: "var(--font-mono)", fontSize: 11, color: "var(--fg-5)", marginLeft: "auto" }}>{leaf.id}</span>
      </div>
      <h3 style={{
        fontFamily: "var(--font-jakarta)",
        fontWeight: 700,
        fontSize: 20, lineHeight: 1.2, margin: 0,
        letterSpacing: "-0.015em",
        color: "var(--fg-1)", textWrap: "balance",
      }}>{leaf.name}</h3>

      {/* Priority selector */}
      <div style={{ marginTop: 18 }}>
        <div className="eyebrow muted" style={{ marginBottom: 8, fontSize: 10 }}>Priority for this project</div>
        <div style={{ display: "grid", gridTemplateColumns: "repeat(4, 1fr)", gap: 4 }}>
          {["unset", "out", "follow", "lead"].map(p => {
            const active = priority === p;
            const accents = {
              unset:  { bg: "rgba(255,255,255,0.5)", fg: "var(--fg-3)", border: "var(--border-default)", abg: "var(--surface-navy)", afg: "#fff" },
              out:    { bg: "rgba(34,49,83,0.04)", fg: "var(--fg-3)", border: "var(--border-default)", abg: "rgba(34,49,83,0.85)", afg: "#fff" },
              follow: { bg: "rgba(16,185,129,0.08)", fg: "var(--c-emerald-700)", border: "rgba(16,185,129,0.20)", abg: "var(--c-emerald)", afg: "#fff" },
              lead:   { bg: "rgba(104,90,217,0.10)", fg: "var(--c-violet-deep)", border: "rgba(104,90,217,0.22)", abg: "var(--c-violet)", afg: "#fff" },
            }[p];
            return (
              <button
                key={p}
                onClick={() => setLeafPriority(leaf.id, p)}
                style={{
                  appearance: "none",
                  background: active ? accents.abg : accents.bg,
                  color: active ? accents.afg : accents.fg,
                  border: `1px solid ${active ? accents.abg : accents.border}`,
                  borderRadius: 8, padding: "9px 6px",
                  fontFamily: "var(--font-jakarta)", fontWeight: 700,
                  fontSize: 10, letterSpacing: "0.12em",
                  textTransform: "uppercase",
                  cursor: "pointer",
                  transition: "all 140ms",
                }}
              >{PRIORITY_LABEL[p]}</button>
            );
          })}
        </div>
        <div style={{ marginTop: 6, fontSize: 11.5, color: "var(--fg-4)", fontStyle: "italic", lineHeight: 1.4 }}>
          {PRIORITY_BLURB[priority]}
        </div>
      </div>

      {/* Metric / threshold note */}
      {(priority === "lead" || priority === "follow") && (
        <div style={{ marginTop: 16 }}>
          <div className="eyebrow muted" style={{ marginBottom: 8, fontSize: 10 }}>Metric / threshold (optional)</div>
          <input
            className="cb-input"
            placeholder={`e.g. ${leaf.example.split(":").slice(-1)[0].trim() || leaf.metric}`}
            value={note.threshold || ""}
            onChange={(e) => updateNote(leaf.id, "threshold", e.target.value)}
            style={{ fontSize: 13 }}
          />
        </div>
      )}

      {/* Knowledge */}
      <div style={{ marginTop: 20, paddingTop: 18, borderTop: "1px solid var(--border-default)" }}>
        <DefRow label="What it is" body={leaf.name === leaf.short ? leaf.name : leaf.name} hideIfSame />
        <DefRow label="Metric" body={leaf.metric} mono />
        <DefRow label="When it dominates" body={leaf.whenDominates} italic />
        <DefRow label="Example" body={leaf.example} />
      </div>

      {/* Trade-offs touching this leaf */}
      {(() => {
        const linked = FRAMEWORK.tradeoffs.filter(t => t.a === leaf.id || t.b === leaf.id);
        if (linked.length === 0) return null;
        return (
          <div style={{ marginTop: 18, paddingTop: 16, borderTop: "1px solid var(--border-default)" }}>
            <div className="eyebrow muted" style={{ marginBottom: 8, fontSize: 10 }}>Trade-offs</div>
            {linked.map(t => {
              const isActive = detected.some(d => d.id === t.id);
              const other = LEAF_BY_ID[t.a === leaf.id ? t.b : t.a];
              return (
                <div key={t.id} style={{
                  padding: "10px 12px", marginBottom: 6,
                  background: isActive ? "rgba(245,158,11,0.08)" : "rgba(34,49,83,0.03)",
                  border: `1px solid ${isActive ? "rgba(245,158,11,0.28)" : "var(--border-default)"}`,
                  borderRadius: 8,
                  fontSize: 12.5,
                  lineHeight: 1.45,
                }}>
                  <div style={{ display: "flex", justifyContent: "space-between", alignItems: "baseline", gap: 8 }}>
                    <span style={{ fontFamily: "var(--font-jakarta)", fontWeight: 700, color: "var(--fg-1)" }}>
                      {t.title}
                    </span>
                    <span style={{ fontFamily: "var(--font-mono)", fontSize: 10, color: "var(--fg-4)" }}>
                      vs {other.id} {other.short}
                    </span>
                  </div>
                  <div style={{ marginTop: 4, color: "var(--fg-3)", fontStyle: "italic" }}>{t.note}</div>
                  {isActive && (
                    <div style={{ marginTop: 6, fontFamily: "var(--font-mono)", fontSize: 10, color: "var(--c-gold-700)", letterSpacing: "0.14em" }}>
                      ● ACTIVE FOR THIS PROJECT
                    </div>
                  )}
                </div>
              );
            })}
          </div>
        );
      })()}
    </div>
  );
}

function DefRow({ label, body, mono, italic }) {
  if (!body) return null;
  return (
    <div style={{ marginBottom: 12 }}>
      <div className="eyebrow muted" style={{ fontSize: 9, marginBottom: 3 }}>{label}</div>
      <div style={{
        fontFamily: mono ? "var(--font-mono)" : "var(--font-serif)",
        fontSize: mono ? 12 : 13.5,
        lineHeight: 1.5,
        color: "var(--fg-2)",
        fontStyle: italic ? "italic" : "normal",
      }}>{body}</div>
    </div>
  );
}

window.Workspace = Workspace;

// ============================================================================
// LINUS — compact icon + popover
// ============================================================================
function SparkleIcon() {
  return (
    <svg width="14" height="14" viewBox="0 0 16 16" fill="none" style={{ display: "block" }}>
      <path d="M8 1.5 L9.2 6.8 L14.5 8 L9.2 9.2 L8 14.5 L6.8 9.2 L1.5 8 L6.8 6.8 Z"
            fill="currentColor" />
      <circle cx="13" cy="3" r="1" fill="currentColor" />
      <circle cx="3" cy="13" r="0.7" fill="currentColor" opacity="0.6" />
    </svg>
  );
}

function LinusPopover({ inputRef, value, setValue, onTranslate, thinking, matchCount, onClear, onClose }) {
  return (
    <div
      role="dialog"
      aria-label="LINUS — translate"
      style={{
        position: "absolute",
        top: "calc(100% + 10px)",
        right: 0,
        width: 460,
        background: "var(--surface-paper)",
        border: "1px solid var(--border-strong)",
        borderRadius: 16,
        boxShadow: "0 24px 60px rgba(34,49,83,0.18), 0 4px 14px rgba(34,49,83,0.06)",
        padding: 16,
        zIndex: 50,
      }}
    >
      {/* tip arrow */}
      <span style={{
        position: "absolute", top: -7, right: 28,
        width: 12, height: 12, transform: "rotate(45deg)",
        background: "var(--surface-paper)",
        borderTop: "1px solid var(--border-strong)",
        borderLeft: "1px solid var(--border-strong)",
      }} />

      <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", marginBottom: 10 }}>
        <div style={{ display: "flex", alignItems: "center", gap: 8, color: "var(--c-violet-deep)" }}>
          <SparkleIcon />
          <span style={{ fontFamily: "var(--font-mono)", fontSize: 10, letterSpacing: "0.18em" }}>LINUS · TRANSLATE</span>
        </div>
        <button
          onClick={onClose}
          aria-label="Close"
          style={{
            appearance: "none", border: 0, background: "transparent",
            color: "var(--fg-4)", cursor: "pointer", fontSize: 16, lineHeight: 1,
            padding: 4,
          }}
        >×</button>
      </div>

      <div style={{
        fontFamily: "var(--font-serif)", fontSize: 13, color: "var(--fg-3)",
        lineHeight: 1.5, marginBottom: 12,
      }}>
        Describe the project in your own words. LINUS will surface the QA leaves that most likely matter.
      </div>

      <textarea
        ref={inputRef}
        className="cb-input"
        placeholder={'e.g. "we need it stable in the field, no cold chain" or "should not aggregate at high concentration"'}
        value={value}
        onChange={(e) => setValue(e.target.value)}
        onKeyDown={(e) => {
          if (e.key === "Enter" && (e.metaKey || e.ctrlKey)) onTranslate();
          if (e.key === "Escape") onClose();
        }}
        rows={3}
        style={{
          width: "100%",
          fontFamily: "var(--font-serif)",
          fontSize: 14,
          padding: "10px 12px",
          border: "1px solid var(--border-default)",
          borderRadius: 10,
          resize: "vertical",
          outline: "none",
          background: "rgba(255,255,255,0.65)",
        }}
      />

      <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", marginTop: 12, gap: 8 }}>
        <div style={{ fontFamily: "var(--font-mono)", fontSize: 10, color: "var(--fg-4)", letterSpacing: "0.08em" }}>
          {matchCount > 0
            ? `${matchCount} LEAF${matchCount === 1 ? "" : "ES"} HIGHLIGHTED`
            : "⌘⏎ TO HIGHLIGHT"}
        </div>
        <div style={{ display: "flex", gap: 8 }}>
          {matchCount > 0 && !thinking && (
            <button
              className="btn btn-ghost"
              onClick={onClear}
              style={{ padding: "8px 14px", fontSize: 12 }}
            >Clear</button>
          )}
          <button
            className="btn btn-primary"
            onClick={onTranslate}
            disabled={thinking || !value.trim()}
            style={{ padding: "8px 18px", fontSize: 12, opacity: (thinking || !value.trim()) ? 0.5 : 1 }}
          >
            {thinking ? "Reading…" : "Highlight"}
          </button>
        </div>
      </div>
    </div>
  );
}
