// NEXUS PLATFORM — Canvas tab (drag-and-drop, SVG curves, modes, thread save)
const { useState: useNxCanvasState, useRef: useNxCanvasRef, useMemo: useNxCanvasMemo } = React;
const Tnc = window.ArbiterTokens;

function NexusCanvas({ data }) {
  const nx = window.__nx;

  // ── State ────────────────────────────────
  const [search, setSearch] = useNxCanvasState('');
  const [filterType, setFilterType] = useNxCanvasState('All');
  const [mode, setMode] = useNxCanvasState('connect'); // 'select' | 'connect' | 'annotate'
  const [linkType, setLinkType] = useNxCanvasState('references');
  const [pinned, setPinned] = useNxCanvasState([]); // [{entityId, x, y}]
  const [links, setLinks] = useNxCanvasState([]);    // [{id, from, to, type}]
  const [selected, setSelected] = useNxCanvasState(null); // entityId in canvas
  const [linkStart, setLinkStart] = useNxCanvasState(null); // entityId
  const [draggingId, setDraggingId] = useNxCanvasState(null);
  const [threadName, setThreadName] = useNxCanvasState('Untitled narrative');
  const [toast, setToast] = useNxCanvasState(null);

  const canvasRef = useNxCanvasRef(null);

  // ── Seed canvas with the "Harrington Hub" thread on first render ──
  React.useEffect(() => {
    if (pinned.length === 0 && links.length === 0) {
      const seed = data.threads[0]; // TH-01 Harrington hub
      const positions = [
        { x: 420, y: 240 }, // center
        { x: 140, y: 100 }, { x: 700, y: 100 },
        { x: 140, y: 380 }, { x: 700, y: 380 },
        { x: 60,  y: 240 }, { x: 780, y: 240 },
        { x: 260, y: 60 },  { x: 580, y: 60 },
        { x: 420, y: 440 },
      ];
      const initial = seed.entityIds.slice(0, 10).map((id, i) => ({
        entityId: id, ...positions[i],
      }));
      const canvasIds = new Set(initial.map(p => p.entityId));
      const initialLinks = data.links
        .filter(l => canvasIds.has(l.from) && canvasIds.has(l.to))
        .map(l => ({ id: l.id, from: l.from, to: l.to, type: l.type }));
      setPinned(initial);
      setLinks(initialLinks);
      setThreadName(seed.name);
    }
  }, []);

  // ── Derived ──
  const typeOptions = ['All', 'person', 'org', 'document', 'event', 'issue', 'artifact'];
  const linkTypeOptions = ['references','contradicts','corroborates','sent-to','owns','occurred-before','authored','caused','supports'];

  const filteredEntities = useNxCanvasMemo(() => {
    const q = search.toLowerCase();
    return data.entities.filter(e =>
      (filterType === 'All' || e.type === filterType) &&
      (!q || e.name.toLowerCase().includes(q) || e.summary.toLowerCase().includes(q) || (e.aliases || []).some(a => a.toLowerCase().includes(q)))
    );
  }, [data.entities, search, filterType]);

  const groupedEntities = useNxCanvasMemo(() => {
    const groups = {};
    filteredEntities.forEach(e => { (groups[e.type] = groups[e.type] || []).push(e); });
    return groups;
  }, [filteredEntities]);

  const entityById = useNxCanvasMemo(() => {
    const m = {};
    data.entities.forEach(e => m[e.id] = e);
    return m;
  }, [data.entities]);

  const pinnedByIds = new Set(pinned.map(p => p.entityId));

  // Flash a toast
  const flash = (msg) => { setToast(msg); setTimeout(() => setToast(null), 2000); };

  // ── Drag handlers ────────────────────────
  // Rail-to-canvas drag: HTML5 DnD works even under Babel-standalone
  const onRailDragStart = (e, entity) => {
    if (pinnedByIds.has(entity.id)) { e.preventDefault(); return; }
    e.dataTransfer.setData('text/plain', entity.id);
    e.dataTransfer.effectAllowed = 'copy';
  };
  const onCanvasDragOver = (e) => {
    e.preventDefault();
    e.dataTransfer.dropEffect = draggingId ? 'move' : 'copy';
  };
  const onCanvasDrop = (e) => {
    e.preventDefault();
    const entityId = e.dataTransfer.getData('text/plain');
    if (!entityId || pinnedByIds.has(entityId)) return;
    const rect = canvasRef.current?.getBoundingClientRect();
    if (!rect) return;
    const x = Math.max(20, Math.min(rect.width - 180, e.clientX - rect.left - 80));
    const y = Math.max(20, Math.min(rect.height - 80, e.clientY - rect.top - 30));
    setPinned(p => [...p, { entityId, x, y }]);
    flash(`Pinned ${entityById[entityId].name}`);
  };

  // Canvas-card drag to reposition
  const onCardDragStart = (e, id) => {
    e.dataTransfer.setData('text/plain', id);
    e.dataTransfer.effectAllowed = 'move';
    setDraggingId(id);
  };
  const onCardDragEnd = (e, id) => {
    const rect = canvasRef.current?.getBoundingClientRect();
    if (!rect) { setDraggingId(null); return; }
    const x = Math.max(20, Math.min(rect.width - 180, e.clientX - rect.left - 80));
    const y = Math.max(20, Math.min(rect.height - 80, e.clientY - rect.top - 30));
    setPinned(p => p.map(pp => pp.entityId === id ? { ...pp, x, y } : pp));
    setDraggingId(null);
  };

  // ── Click handlers for connect-mode and select-mode ──
  const onCardClick = (id) => {
    if (mode === 'select') {
      setSelected(id === selected ? null : id);
    } else if (mode === 'connect') {
      if (!linkStart) {
        setLinkStart(id);
        flash(`Select a target for "${entityById[id].name}"`);
      } else if (linkStart === id) {
        setLinkStart(null);
      } else {
        // Don't duplicate
        const exists = links.some(l => (l.from === linkStart && l.to === id) || (l.from === id && l.to === linkStart));
        if (!exists) {
          const newLink = { id: `L-NEW-${Date.now()}`, from: linkStart, to: id, type: linkType };
          setLinks(l => [...l, newLink]);
          const ls = nx.linkStyle(linkType);
          flash(`${entityById[linkStart].name} ${ls.label} ${entityById[id].name}`);
        } else {
          flash('Connection already exists');
        }
        setLinkStart(null);
      }
    }
  };

  const removePinned = (id) => {
    setPinned(p => p.filter(pp => pp.entityId !== id));
    setLinks(l => l.filter(ll => ll.from !== id && ll.to !== id));
    if (selected === id) setSelected(null);
    if (linkStart === id) setLinkStart(null);
  };

  const removeLink = (linkId) => setLinks(l => l.filter(ll => ll.id !== linkId));

  const clearCanvas = () => { setPinned([]); setLinks([]); setSelected(null); setLinkStart(null); setThreadName('Untitled narrative'); flash('Canvas cleared'); };

  const saveThread = () => {
    flash(`Thread "${threadName}" saved · ${pinned.length} entities · ${links.length} links`);
  };

  // Load a preset thread
  const loadThread = (threadId) => {
    const t = data.threads.find(x => x.id === threadId);
    if (!t) return;
    // Generate grid positions
    const positions = [];
    const n = t.entityIds.length;
    const cols = Math.min(4, Math.ceil(Math.sqrt(n)));
    const rows = Math.ceil(n / cols);
    for (let i = 0; i < n; i++) {
      const c = i % cols, r = Math.floor(i / cols);
      positions.push({ x: 60 + c * 200, y: 50 + r * 120 });
    }
    const newPinned = t.entityIds.map((id, i) => ({ entityId: id, ...positions[i] }));
    const canvasIds = new Set(t.entityIds);
    const loadedLinks = data.links
      .filter(l => canvasIds.has(l.from) && canvasIds.has(l.to))
      .map(l => ({ id: l.id, from: l.from, to: l.to, type: l.type }));
    setPinned(newPinned);
    setLinks(loadedLinks);
    setThreadName(t.name);
    setLinkStart(null);
    setSelected(null);
    flash(`Loaded "${t.name}"`);
  };

  const entityRows = pinned.map(p => ({ ...entityById[p.entityId], x: p.x, y: p.y, pinnedEntityId: p.entityId }));

  // ── Right-click context-menu dispatch ─────────────────────────────────
  // All mutations route through setPinned/setLinks/etc., so menu factories
  // stay pure. See components/NexusCanvasActions.jsx for the item lists.
  const buildCtx = (overrides) => ({
    pointer: { x: 0, y: 0 },
    state: { pinned, links, threadName, linkType, mode, selected },
    actions: {
      setPinned, setLinks, setThreadName, setLinkType, setMode,
      setSelected, setLinkStart, flash, clearCanvas, saveThread, loadThread,
    },
    data, entityById, nx,
    ...overrides,
  });
  const openCardMenu = (e, id) => {
    const CM = window.Arbiter?.ContextMenu;
    const F = window.NexusCanvasActions;
    if (!CM || !F) return;
    e.preventDefault(); e.stopPropagation();
    CM.open(e, F.cardMenu(buildCtx({ entityId: id, pointer: { x: e.clientX, y: e.clientY } })));
  };
  const openLinkMenu = (e, id) => {
    const CM = window.Arbiter?.ContextMenu;
    const F = window.NexusCanvasActions;
    if (!CM || !F) return;
    e.preventDefault(); e.stopPropagation();
    CM.open(e, F.linkMenu(buildCtx({ linkId: id, pointer: { x: e.clientX, y: e.clientY } })));
  };
  const openBackgroundMenu = (e) => {
    const CM = window.Arbiter?.ContextMenu;
    const F = window.NexusCanvasActions;
    if (!CM || !F) return;
    e.preventDefault();
    CM.open(e, F.backgroundMenu(buildCtx({ pointer: { x: e.clientX, y: e.clientY } })));
  };

  return (
    <div>
      {/* Toolbar */}
      <div style={{ ...nx.card, marginBottom: '14px', padding: '10px 16px', display: 'flex', alignItems: 'center', gap: '12px', flexWrap: 'wrap' }}>
        {/* Thread name */}
        <div style={{ display: 'flex', alignItems: 'center', gap: '6px' }}>
          <span style={{ fontSize: '10px', color: Tnc.color.text.tertiary, textTransform: 'uppercase', letterSpacing: '0.06em', fontWeight: 600 }}>Thread</span>
          <input value={threadName} onChange={e => setThreadName(e.target.value)}
            style={{ padding: '4px 8px', fontSize: '12px', border: `1px solid ${Tnc.color.border.light}`, borderRadius: '5px', fontFamily: Tnc.font.family, color: Tnc.color.text.primary, background: Tnc.color.bg.card, minWidth: '260px' }} />
        </div>

        {/* Mode toggle */}
        <div style={{ display: 'flex', gap: '4px', padding: '3px', background: Tnc.color.bg.secondary, borderRadius: '6px' }}>
          {[{ k: 'select', l: 'Select' }, { k: 'connect', l: 'Connect' }, { k: 'annotate', l: 'Annotate' }].map(m => (
            <button key={m.k} onClick={() => { setMode(m.k); setLinkStart(null); }}
              style={{ padding: '3px 10px', borderRadius: '4px', border: 'none',
                background: mode === m.k ? Tnc.color.bg.card : 'transparent',
                color: mode === m.k ? nx.fuchsia : Tnc.color.text.secondary,
                fontSize: '11px', fontWeight: mode === m.k ? 700 : 500, cursor: 'pointer', fontFamily: Tnc.font.family }}>{m.l}</button>
          ))}
        </div>

        {/* Link-type dropdown */}
        {mode === 'connect' && (
          <div style={{ display: 'flex', alignItems: 'center', gap: '6px' }}>
            <span style={{ fontSize: '10px', color: Tnc.color.text.tertiary, textTransform: 'uppercase', letterSpacing: '0.06em', fontWeight: 600 }}>Link type</span>
            <select value={linkType} onChange={e => setLinkType(e.target.value)}
              style={{ padding: '4px 8px', fontSize: '11px', border: `1px solid ${Tnc.color.border.light}`, borderRadius: '5px', background: Tnc.color.bg.card, color: nx.linkStyle(linkType).color, fontWeight: 600 }}>
              {linkTypeOptions.map(t => <option key={t} value={t}>{nx.linkStyle(t).label}</option>)}
            </select>
          </div>
        )}

        {/* Preset load */}
        <div style={{ display: 'flex', alignItems: 'center', gap: '6px' }}>
          <span style={{ fontSize: '10px', color: Tnc.color.text.tertiary, textTransform: 'uppercase', letterSpacing: '0.06em', fontWeight: 600 }}>Load</span>
          <select defaultValue="" onChange={e => { if (e.target.value) { loadThread(e.target.value); e.target.value = ''; } }}
            style={{ padding: '4px 8px', fontSize: '11px', border: `1px solid ${Tnc.color.border.light}`, borderRadius: '5px', background: Tnc.color.bg.card, color: Tnc.color.text.secondary }}>
            <option value="">Choose thread…</option>
            {data.threads.map(t => <option key={t.id} value={t.id}>{t.name}</option>)}
          </select>
        </div>

        <div style={{ flex: 1 }} />

        {/* Counts + actions */}
        <span style={{ fontSize: '11px', color: Tnc.color.text.secondary, fontFamily: Tnc.font.mono }}>{pinned.length} pinned · {links.length} links</span>
        <button onClick={clearCanvas} style={nx.btnSecondary}>Clear</button>
        <button onClick={saveThread} style={nx.btnPrimary}>Save thread</button>
      </div>

      {/* 3-panel workspace */}
      <div style={{ display: 'grid', gridTemplateColumns: '260px 1fr 280px', gap: '14px', height: 'calc(100vh - 380px)', minHeight: '540px' }}>
        {/* LEFT RAIL — entity search + palette */}
        <div style={{ ...nx.card, marginBottom: 0, display: 'flex', flexDirection: 'column', overflow: 'hidden' }}>
          <div style={{ padding: '10px 12px', borderBottom: `1px solid ${Tnc.color.border.light}` }}>
            <div style={{ fontSize: '10px', fontWeight: 700, color: Tnc.color.text.tertiary, textTransform: 'uppercase', letterSpacing: '0.08em', marginBottom: '6px' }}>Entity palette</div>
            <input value={search} onChange={e => setSearch(e.target.value)} placeholder="Search 4,820 entities…"
              style={{ width: '100%', height: '28px', border: `1px solid ${Tnc.color.border.light}`, borderRadius: '5px', padding: '0 8px', fontSize: '11px', fontFamily: Tnc.font.family, background: Tnc.color.bg.primary, color: Tnc.color.text.primary, outline: 'none', marginBottom: '6px' }} />
            <div style={{ display: 'flex', flexWrap: 'wrap', gap: '3px' }}>
              {typeOptions.map(t => (
                <button key={t} onClick={() => setFilterType(t)}
                  style={{ padding: '2px 6px', borderRadius: '8px', border: `1px solid ${filterType === t ? nx.fuchsia : Tnc.color.border.light}`,
                    background: filterType === t ? nx.fuchsiaBg : Tnc.color.bg.card, color: filterType === t ? nx.fuchsia : Tnc.color.text.secondary,
                    fontSize: '9px', fontWeight: 600, cursor: 'pointer', textTransform: 'capitalize', fontFamily: Tnc.font.family }}>{t}</button>
              ))}
            </div>
          </div>
          <div style={{ flex: 1, overflowY: 'auto' }}>
            {Object.entries(groupedEntities).map(([type, ents]) => {
              const ts = nx.typeStyle(type);
              return (
                <div key={type}>
                  <div style={{ padding: '6px 12px', background: Tnc.color.bg.secondary, display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
                    <span style={{ display: 'flex', alignItems: 'center', gap: '6px', fontSize: '10px', fontWeight: 700, color: ts.color, textTransform: 'uppercase', letterSpacing: '0.06em' }}>
                      <span>{ts.icon}</span>{ts.label}
                    </span>
                    <span style={{ fontSize: '10px', color: Tnc.color.text.tertiary, fontFamily: Tnc.font.mono }}>{ents.length}</span>
                  </div>
                  {ents.map(e => {
                    const isPinned = pinnedByIds.has(e.id);
                    return (
                      <div key={e.id}
                        draggable={!isPinned}
                        onDragStart={(ev) => onRailDragStart(ev, e)}
                        onDoubleClick={() => {
                          if (isPinned) return;
                          setPinned(p => [...p, { entityId: e.id, x: 60 + (p.length % 4) * 190, y: 50 + Math.floor(p.length / 4) * 120 }]);
                          flash(`Pinned ${e.name}`);
                        }}
                        title={isPinned ? 'Already on canvas' : 'Drag to canvas · or double-click to pin'}
                        style={{ padding: '6px 12px', borderBottom: `1px solid ${Tnc.color.border.light}`, cursor: isPinned ? 'not-allowed' : 'grab', opacity: isPinned ? 0.4 : 1, display: 'flex', alignItems: 'flex-start', gap: '6px' }}
                      >
                        <span style={{ color: ts.color, fontSize: '10px', marginTop: '2px' }}>{ts.icon}</span>
                        <div style={{ flex: 1, minWidth: 0 }}>
                          <div style={{ fontSize: '11px', fontWeight: 600, color: Tnc.color.text.primary, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{e.name}</div>
                          <div style={{ fontSize: '10px', color: Tnc.color.text.tertiary, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{e.linkCount} links</div>
                        </div>
                        {isPinned && <span style={{ fontSize: '9px', color: nx.fuchsia, fontWeight: 700 }}><Icons.Check size={11}/></span>}
                      </div>
                    );
                  })}
                </div>
              );
            })}
          </div>
          <div style={{ padding: '8px 12px', borderTop: `1px solid ${Tnc.color.border.light}`, background: Tnc.color.bg.secondary, fontSize: '10px', color: Tnc.color.text.tertiary }}>
            ⤶ Drag or double-click to pin · {filteredEntities.length} of {data.entities.length}
          </div>
        </div>

        {/* CENTER CANVAS */}
        <div ref={canvasRef}
          onDragOver={onCanvasDragOver}
          onDrop={onCanvasDrop}
          onContextMenu={openBackgroundMenu}
          style={{ position: 'relative', background: `${Tnc.color.bg.card} radial-gradient(circle at 25px 25px, rgba(192,38,211,0.05) 2px, transparent 3px) 0 0/40px 40px`, border: `1px solid ${Tnc.color.border.light}`, borderRadius: Tnc.radius.lg, overflow: 'hidden' }}
        >
          {/* SVG overlay for links */}
          <svg style={{ position: 'absolute', inset: 0, pointerEvents: 'none', width: '100%', height: '100%' }}>
            <defs>
              <marker id="arrow" markerWidth="8" markerHeight="8" refX="6" refY="4" orient="auto-start-reverse">
                <path d="M0,0 L8,4 L0,8 z" fill="#6E7D9E" />
              </marker>
            </defs>
            {links.map(l => {
              const from = pinned.find(p => p.entityId === l.from);
              const to = pinned.find(p => p.entityId === l.to);
              if (!from || !to) return null;
              const fx = from.x + 80, fy = from.y + 30;
              const tx = to.x + 80,   ty = to.y + 30;
              const ls = nx.linkStyle(l.type);
              const midX = (fx + tx) / 2, midY = (fy + ty) / 2;
              const dx = tx - fx, dy = ty - fy;
              const ctrlOffset = Math.min(100, Math.hypot(dx, dy) / 3);
              const d = `M ${fx} ${fy} C ${fx + ctrlOffset} ${fy - 30}, ${tx - ctrlOffset} ${ty - 30}, ${tx} ${ty}`;
              return (
                <g key={l.id}>
                  <path d={d} stroke={ls.color} strokeWidth="2" fill="none" opacity="0.5"
                    markerEnd="url(#arrow)" style={{ pointerEvents: 'stroke' }} />
                  <text x={midX} y={midY - 6} fontSize="9" fill={ls.color} textAnchor="middle"
                    style={{ pointerEvents: 'auto', cursor: 'pointer', fontFamily: Tnc.font.family, fontWeight: 600 }}
                    onClick={() => removeLink(l.id)}
                    onContextMenu={(e) => openLinkMenu(e, l.id)}>
                    {ls.label} ×
                  </text>
                </g>
              );
            })}
          </svg>

          {/* Pinned entity cards */}
          {entityRows.map(e => {
            const ts = nx.typeStyle(e.type);
            const isSelected = selected === e.pinnedEntityId;
            const isLinkStart = linkStart === e.pinnedEntityId;
            return (
              <div key={e.pinnedEntityId}
                draggable
                onDragStart={(ev) => onCardDragStart(ev, e.pinnedEntityId)}
                onDragEnd={(ev) => onCardDragEnd(ev, e.pinnedEntityId)}
                onClick={() => onCardClick(e.pinnedEntityId)}
                onContextMenu={(ev) => openCardMenu(ev, e.pinnedEntityId)}
                style={{
                  position: 'absolute', left: `${e.x}px`, top: `${e.y}px`, width: '160px',
                  background: Tnc.color.bg.card, border: `2px solid ${isLinkStart ? nx.fuchsia : isSelected ? ts.color : Tnc.color.border.light}`,
                  borderRadius: '8px', padding: '8px 10px', cursor: mode === 'connect' ? 'crosshair' : 'grab',
                  boxShadow: isLinkStart ? `0 0 0 3px ${nx.fuchsiaBg}, 0 4px 12px rgba(192,38,211,0.2)` : isSelected ? `0 4px 12px rgba(10,22,40,0.15)` : '0 1px 3px rgba(10,22,40,0.08)',
                  transition: 'box-shadow 0.15s, border-color 0.15s', zIndex: isSelected || isLinkStart ? 2 : 1,
                }}
              >
                <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: '3px' }}>
                  <span style={{ display: 'inline-flex', alignItems: 'center', gap: '3px', fontSize: '9px', fontWeight: 700, color: ts.color, textTransform: 'uppercase', letterSpacing: '0.06em' }}>
                    <span>{ts.icon}</span>{ts.label}
                  </span>
                  <button onClick={(ev) => { ev.stopPropagation(); removePinned(e.pinnedEntityId); }}
                    style={{ border: 'none', background: 'transparent', color: Tnc.color.text.tertiary, fontSize: '12px', cursor: 'pointer', padding: 0, lineHeight: 1 }}>×</button>
                </div>
                <div style={{ fontSize: '12px', fontWeight: 700, color: Tnc.color.text.primary, lineHeight: 1.2, marginBottom: '3px', overflow: 'hidden', textOverflow: 'ellipsis', display: '-webkit-box', WebkitLineClamp: 2, WebkitBoxOrient: 'vertical' }}>{e.name}</div>
                <div style={{ fontSize: '9px', color: Tnc.color.text.tertiary, fontFamily: Tnc.font.mono }}>{e.pinnedEntityId}</div>
              </div>
            );
          })}

          {/* Empty-state hint */}
          {pinned.length === 0 && (
            <div style={{ position: 'absolute', inset: 0, display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', pointerEvents: 'none', color: Tnc.color.text.tertiary, fontSize: '13px', gap: '4px' }}>
              <div style={{ fontSize: '32px', color: nx.fuchsia, opacity: 0.4 }}>⟐</div>
              <div style={{ fontWeight: 700, color: Tnc.color.text.secondary }}>Canvas is empty</div>
              <div>Drag entities from the left rail to pin them · double-click for quick-add</div>
            </div>
          )}

          {/* Toast */}
          {toast && (
            <div style={{ position: 'absolute', bottom: '14px', left: '50%', transform: 'translateX(-50%)', padding: '6px 14px', background: nx.fuchsia, color: '#fff', borderRadius: '20px', fontSize: '11px', fontWeight: 600, boxShadow: '0 2px 8px rgba(10,22,40,0.25)' }}>
              {toast}
            </div>
          )}

          {/* Mode hint */}
          <div style={{ position: 'absolute', top: '12px', left: '12px', padding: '4px 10px', background: mode === 'connect' ? nx.fuchsiaBg : Tnc.color.bg.secondary, border: `1px solid ${mode === 'connect' ? nx.fuchsia : Tnc.color.border.light}`, borderRadius: '12px', fontSize: '10px', color: mode === 'connect' ? nx.fuchsia : Tnc.color.text.secondary, fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.06em' }}>
            {mode === 'connect' ? (linkStart ? `◆ Click target to finish "${nx.linkStyle(linkType).label}"` : '◆ Connect mode · click two cards') : mode === 'select' ? '● Select mode · click to inspect' : ' Annotate mode'}
          </div>
        </div>

        {/* RIGHT RAIL — thread + inspector */}
        <div style={{ display: 'flex', flexDirection: 'column', gap: '14px' }}>
          {/* Thread bundle */}
          <div style={{ ...nx.card, marginBottom: 0 }}>
            <div style={{ ...nx.cardH, color: nx.fuchsia }}>Current thread</div>
            <div style={{ padding: '12px 14px' }}>
              <div style={{ fontSize: '13px', fontWeight: 700, color: Tnc.color.text.primary, marginBottom: '4px' }}>{threadName}</div>
              <div style={{ display: 'flex', gap: '6px', marginBottom: '10px' }}>
                <span style={{ ...nx.tag, background: nx.fuchsiaBg, color: nx.fuchsia }}>{pinned.length} entities</span>
                <span style={{ ...nx.tag, background: nx.orgBg, color: nx.org }}>{links.length} links</span>
              </div>
              <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '6px', marginBottom: '10px' }}>
                {(() => {
                  const counts = {};
                  pinned.forEach(p => { const t = entityById[p.entityId]?.type; if (t) counts[t] = (counts[t] || 0) + 1; });
                  return Object.entries(counts).map(([t, n]) => {
                    const ts = nx.typeStyle(t);
                    return (
                      <div key={t} style={{ display: 'flex', alignItems: 'center', gap: '4px', fontSize: '10px' }}>
                        <span style={{ color: ts.color }}>{ts.icon}</span>
                        <span style={{ color: Tnc.color.text.secondary }}>{ts.label}</span>
                        <span style={{ flex: 1 }} />
                        <span style={{ fontFamily: Tnc.font.mono, color: ts.color, fontWeight: 700 }}>{n}</span>
                      </div>
                    );
                  });
                })()}
              </div>
            </div>
          </div>

          {/* Selected inspector */}
          {selected && (
            <div style={{ ...nx.card, marginBottom: 0 }}>
              <div style={nx.cardH}>Selected entity</div>
              <div style={{ padding: '12px 14px' }}>
                {(() => {
                  const e = entityById[selected];
                  const ts = nx.typeStyle(e.type);
                  const related = links.filter(l => l.from === selected || l.to === selected);
                  return (
                    <>
                      <span style={{ ...nx.tag, background: ts.bg, color: ts.color, marginBottom: '6px' }}>{ts.label}</span>
                      <div style={{ fontSize: '13px', fontWeight: 700, color: Tnc.color.text.primary, marginTop: '6px' }}>{e.name}</div>
                      <div style={{ fontSize: '11px', color: Tnc.color.text.secondary, marginTop: '4px', lineHeight: 1.5 }}>{e.summary}</div>
                      <div style={{ fontSize: '10px', color: Tnc.color.text.tertiary, marginTop: '6px' }}>Matter: {e.matter}</div>
                      <div style={{ marginTop: '10px', paddingTop: '10px', borderTop: `1px solid ${Tnc.color.border.light}` }}>
                        <div style={{ fontSize: '10px', fontWeight: 700, color: Tnc.color.text.tertiary, textTransform: 'uppercase', letterSpacing: '0.06em', marginBottom: '4px' }}>Links on canvas · {related.length}</div>
                        {related.map(l => {
                          const other = l.from === selected ? l.to : l.from;
                          const ls = nx.linkStyle(l.type);
                          const arrow = l.from === selected ? '→' : '←';
                          return (
                            <div key={l.id} style={{ fontSize: '11px', color: Tnc.color.text.secondary, padding: '3px 0' }}>
                              <span style={{ color: ls.color, fontWeight: 700 }}>{arrow} {ls.label}</span> {entityById[other]?.name}
                            </div>
                          );
                        })}
                        {related.length === 0 && <div style={{ fontSize: '11px', color: Tnc.color.text.tertiary, fontStyle: 'italic' }}>No links yet — switch to Connect mode.</div>}
                      </div>
                    </>
                  );
                })()}
              </div>
            </div>
          )}

          {/* Shortcuts */}
          <div style={{ ...nx.card, marginBottom: 0 }}>
            <div style={nx.cardH}>Shortcuts</div>
            <div style={{ padding: '10px 14px', fontSize: '11px', color: Tnc.color.text.secondary, lineHeight: 1.7 }}>
              <div><b style={{ color: Tnc.color.text.primary }}>Drag</b> rail item → canvas</div>
              <div><b style={{ color: Tnc.color.text.primary }}>Double-click</b> rail item · quick-add</div>
              <div><b style={{ color: Tnc.color.text.primary }}>Drag</b> canvas card · reposition</div>
              <div><b style={{ color: Tnc.color.text.primary }}>Connect mode</b> · click 2 cards → link</div>
              <div><b style={{ color: Tnc.color.text.primary }}>Click link label</b> · × removes link</div>
              <div><b style={{ color: Tnc.color.text.primary }}>Load</b> a preset to hydrate canvas</div>
            </div>
          </div>
        </div>
      </div>
    </div>
  );
}

window.NexusCanvas = NexusCanvas;
