// CRUCIBLE — The Argument Forge.
// Visual language matches Risk / Calendar / Nexus (cr.* style object exposed
// via window.cr). Drag-and-drop uses the namespaced HTML5 contract from
// EsBuilder. Right-click menus via window.Arbiter.ContextMenu. Empty states
// via window.Arbiter.EmptyState. Data lives in CrucibleData.jsx; reactive
// hook via window.useCrucibleStore.
//
// Eight sub-tabs:
//   workbench · adversary · recon · authorities · wargame · dialectic ·
//   briefbank · telemetry
(function () {
  const { useState, useMemo, useEffect, useRef, useCallback } = React;
  const T  = window.ArbiterTokens;
  const A  = window.Arbiter || {};
  const Icons = window.Icons || {};
  const NODE_TYPES = window.CRUCIBLE_NODE_TYPES;
  const DATA = window.CRUCIBLE_DATA;
  const Store = window.CrucibleStore;

  // ── Crucible palette ────────────────────────────────────────────────────
  const CR = {
    flame:   '#DC2626', flameDk: '#991B1B', forge:   '#EA580C',
    steel:   '#475569', ember:   '#D97706', edge:    '#2563EB',
    edgeDk:  '#1E40AF', weld:    '#059669', gold:    '#C9A84C',
    ash:     '#6B7280',
    flameBg: 'rgba(220,38,38,0.06)',
    edgeBg:  'rgba(37,99,235,0.06)',
    weldBg:  'rgba(5,150,105,0.06)',
    emberBg: 'rgba(217,119,6,0.06)',
  };

  // ── cr style object — same shape as window.rk / window.cal ──────────────
  const cr = {
    container:   { flex: 1, overflow: 'auto', background: T.color.bg.primary },
    header:      { padding: '16px 24px', borderBottom: `1px solid ${T.color.border.light}`,
                   background: T.color.bg.card, display: 'flex', alignItems: 'center',
                   justifyContent: 'space-between' },
    headerTitle: { display: 'flex', alignItems: 'center', gap: '12px' },
    crIcon:      { width: '32px', height: '32px', borderRadius: '6px',
                   background: `linear-gradient(135deg, ${CR.flame} 0%, ${CR.flameDk} 100%)`,
                   display: 'flex', alignItems: 'center', justifyContent: 'center',
                   color: '#fff' },
    title:       { fontSize: '18px', fontWeight: 700, color: T.color.text.primary, letterSpacing: '-0.02em' },
    subtitle:    { fontSize: '12px', color: T.color.text.tertiary, marginTop: '1px' },
    tabs:        { display: 'flex', gap: '0', borderBottom: `1px solid ${T.color.border.light}`,
                   background: T.color.bg.card, padding: '0 24px' },
    tab:         { padding: '10px 16px', fontSize: '12px', fontWeight: 500,
                   color: T.color.text.tertiary, cursor: 'pointer', border: 'none', background: 'none',
                   borderBottom: '2px solid transparent', fontFamily: T.font.family,
                   transition: 'all 0.15s', marginBottom: '-1px' },
    tabActive:   { color: CR.flame, borderBottomColor: CR.flame, fontWeight: 600 },
    body:        { padding: '20px 24px' },
    card:        { background: T.color.bg.card, border: `1px solid ${T.color.border.light}`,
                   borderRadius: T.radius.lg, overflow: 'hidden', marginBottom: '16px' },
    cardH:       { padding: '10px 16px', borderBottom: `1px solid ${T.color.border.light}`,
                   fontSize: '12px', fontWeight: 600, color: T.color.text.primary,
                   display: 'flex', alignItems: 'center', justifyContent: 'space-between' },
    stat:        { display: 'flex', flexDirection: 'column', gap: '2px',
                   padding: '12px 16px', background: T.color.bg.secondary,
                   borderRadius: '6px', border: `1px solid ${T.color.border.light}` },
    statLabel:   { fontSize: '10px', fontWeight: 600, color: T.color.text.tertiary,
                   textTransform: 'uppercase', letterSpacing: '0.08em' },
    statValue:   { fontSize: '22px', fontWeight: 700, letterSpacing: '-0.02em',
                   lineHeight: 1.1, color: T.color.text.primary },
    statHint:    { fontSize: '10px', color: T.color.text.tertiary, marginTop: '2px' },
    tag:         { display: 'inline-flex', alignItems: 'center', padding: '2px 8px',
                   borderRadius: '10px', fontSize: '10px', fontWeight: 600 },
    filterBtn:   { padding: '4px 10px', borderRadius: T.radius.md,
                   border: `1px solid ${T.color.border.light}`, background: T.color.bg.card,
                   fontSize: '11px', fontWeight: 500, color: T.color.text.secondary,
                   cursor: 'pointer', fontFamily: T.font.family },
    filterBtnActive: { background: CR.flame, color: '#fff', borderColor: CR.flame },
    btnPrimary:  { padding: '6px 14px', borderRadius: '6px', background: CR.flame,
                   border: 'none', color: '#fff', fontSize: '12px', fontWeight: 700,
                   cursor: 'pointer', fontFamily: T.font.family },
    btnSecondary:{ padding: '6px 12px', borderRadius: '6px',
                   border: `1px solid ${T.color.border.medium}`, background: 'transparent',
                   color: T.color.text.secondary, fontSize: '11px', fontWeight: 500,
                   cursor: 'pointer', fontFamily: T.font.family },
    pill:        { display: 'inline-flex', alignItems: 'center', gap: '8px',
                   padding: '6px 14px', borderRadius: '8px',
                   background: T.color.bg.secondary, border: `1px solid ${T.color.border.light}` },
    pillLabel:   { fontSize: '10px', fontWeight: 600, color: T.color.text.tertiary,
                   textTransform: 'uppercase', letterSpacing: '0.06em' },
    pillValue:   { fontSize: '14px', fontWeight: 700, color: T.color.text.primary, fontFamily: T.font.mono },
    flame: CR.flame, flameBg: CR.flameBg,
    edge: CR.edge,   edgeBg: CR.edgeBg,
    weld: CR.weld,   weldBg: CR.weldBg,
    ember: CR.ember, emberBg: CR.emberBg,
    gold: CR.gold,   ash: CR.ash, steel: CR.steel,
  };
  window.cr = cr;

  // ── Bar meter (visual parity with rk / cal) ─────────────────────────────
  const Bar = ({ value, max = 100, color, glow }) => {
    const pct = Math.min(100, Math.max(0, (value / max) * 100));
    return (
      <div style={{ width: '100%', height: 6, background: T.color.bg.tertiary, borderRadius: 3, overflow: 'hidden' }}>
        <div style={{
          width: pct + '%', height: '100%', background: color, borderRadius: 3,
          boxShadow: glow ? `0 0 8px ${color}88` : 'none',
          transition: 'width 0.4s ease',
        }} />
      </div>
    );
  };

  // ── Stat strip helper (rk.stat shape) ───────────────────────────────────
  const Stat = ({ label, value, color, hint }) => (
    <div style={cr.stat}>
      <span style={cr.statLabel}>{label}</span>
      <span style={{ ...cr.statValue, color: color || T.color.text.primary, fontFamily: T.font.mono }}>{value}</span>
      {hint && <span style={cr.statHint}>{hint}</span>}
    </div>
  );
  const StatRow = ({ children, cols }) => (
    <div style={{ display: 'grid', gridTemplateColumns: cols || 'repeat(auto-fit, minmax(160px, 1fr))', gap: '12px', marginBottom: '16px' }}>
      {children}
    </div>
  );

  const Tag = ({ children, color, strong }) => (
    <span style={{
      ...cr.tag,
      background: strong ? color : (color || T.color.text.tertiary) + '18',
      color: strong ? '#fff' : (color || T.color.text.secondary),
    }}>{children}</span>
  );

  const Card = ({ title, right, children, pad = true }) => (
    <div style={cr.card}>
      {title && <div style={cr.cardH}><span>{title}</span>{right}</div>}
      <div style={{ padding: pad ? '12px 16px' : 0 }}>{children}</div>
    </div>
  );

  const Empty = ({ title, description }) =>
    A.EmptyState
      ? React.createElement(A.EmptyState, { title, description })
      : <div role="status" style={{ padding: 24, textAlign: 'center', color: T.color.text.tertiary, fontSize: 12 }}>{title}</div>;

  // ════════════════════════════════════════════════════════════════════════
  //  1. WORKBENCH — drag-drop argument graph
  // ════════════════════════════════════════════════════════════════════════
  // Strength tiers — used as both display + context-menu options
  const STRENGTH_TIERS = [
    { label: 'Critical', value: 90 },
    { label: 'Strong',   value: 75 },
    { label: 'Moderate', value: 60 },
    { label: 'Weak',     value: 40 },
  ];

  function Workbench() {
    A.useTrack?.('crucible.workbench', {}, []);
    // ── State ────────────────────────────────────────────────────────────
    const [argumentName, setArgumentName] = useState('Redstone v. Meridian — MSJ Count I');
    const [nodes, setNodes]               = useState(() => DATA.argumentGraph.nodes.slice());
    const [edges, setEdges]               = useState(() => DATA.argumentGraph.edges.slice());
    const [selectedId, setSelectedId]     = useState(null);
    const [linkStart, setLinkStart]       = useState(null);
    const [mode, setMode]                 = useState('select');           // select · connect · annotate
    const [search, setSearch]             = useState('');
    const [filterType, setFilterType]     = useState('All');
    const [flash, setFlashMsg]            = useState(null);
    const canvasRef = useRef(null);

    const node = (id) => nodes.find(n => n.id === id);
    const selected = selectedId ? node(selectedId) : null;

    // KPIs derived
    const overallStrength = nodes.length === 0 ? 0 :
      Math.round(nodes.reduce((s, n) => s + (n.strength || 0), 0) / nodes.length);
    const counterCount = nodes.filter(n => n.type === 'counter').length;
    const supportCount = nodes.filter(n => n.type === 'support').length;
    const claimCount = nodes.filter(n => n.type === 'claim').length;
    const unsupportedClaims = nodes.filter(n => n.type === 'claim' && !edges.some(e => e.to === n.id)).length;

    // Type counts on canvas
    const typeOnCanvas = useMemo(() => {
      const c = {}; nodes.forEach(n => { c[n.type] = (c[n.type] || 0) + 1; });
      return c;
    }, [nodes]);

    // Palette filter — 6 types as filter chips + a free-text search
    const typeOptions = ['All', ...Object.keys(NODE_TYPES)];
    const filteredPalette = useMemo(() => {
      const q = search.trim().toLowerCase();
      return Object.entries(NODE_TYPES).filter(([type, spec]) =>
        (filterType === 'All' || filterType === type) &&
        (!q || type.includes(q) || spec.label.toLowerCase().includes(q) || spec.hint.toLowerCase().includes(q))
      );
    }, [search, filterType]);

    // ── Flash toast (in-canvas, like Nexus) ──────────────────────────────
    const flashToast = useCallback((msg) => {
      setFlashMsg(msg);
      setTimeout(() => setFlashMsg(prev => prev === msg ? null : prev), 2000);
    }, []);

    // ── DnD: namespaced HTML5 (same contract as EsBuilder) ───────────────
    const onPaletteDragStart = (e, type) => {
      e.dataTransfer.setData('crucible/node-type', type);
      e.dataTransfer.effectAllowed = 'copy';
    };
    const onCanvasDragOver = (e) => {
      e.preventDefault();
      e.dataTransfer.dropEffect = e.dataTransfer.types.includes('crucible/node-id') ? 'move' : 'copy';
    };
    const onCanvasDrop = (e) => {
      e.preventDefault();
      const rect = canvasRef.current.getBoundingClientRect();
      const newType = e.dataTransfer.getData('crucible/node-type');
      const moveId  = e.dataTransfer.getData('crucible/node-id');
      if (newType) {
        const spec = NODE_TYPES[newType];
        const id = `n-${Math.random().toString(36).slice(2, 7)}`;
        const x = Math.max(8, Math.min(rect.width - spec.w - 8, e.clientX - rect.left - spec.w / 2));
        const y = Math.max(8, Math.min(rect.height - spec.h - 8, e.clientY - rect.top - spec.h / 2));
        const fresh = { id, type: newType, x, y, label: defaultLabel(newType), strength: defaultStrength(newType), citations: [], notes: '' };
        setNodes(ns => [...ns, fresh]);
        setSelectedId(id);
        Store?.addArgumentNode?.(fresh);
        flashToast(`${spec.label} added`);
      } else if (moveId) {
        const ox = parseFloat(e.dataTransfer.getData('offsetX') || '0');
        const oy = parseFloat(e.dataTransfer.getData('offsetY') || '0');
        setNodes(ns => ns.map(n => {
          if (n.id !== moveId) return n;
          const spec = NODE_TYPES[n.type];
          return { ...n,
            x: Math.max(8, Math.min(rect.width - spec.w - 8, e.clientX - rect.left - ox)),
            y: Math.max(8, Math.min(rect.height - spec.h - 8, e.clientY - rect.top - oy)),
          };
        }));
      }
    };
    const onNodeDragStart = (e, n) => {
      e.stopPropagation();
      e.dataTransfer.setData('crucible/node-id', n.id);
      e.dataTransfer.effectAllowed = 'move';
      const r = e.currentTarget.getBoundingClientRect();
      e.dataTransfer.setData('offsetX', String(e.clientX - r.left));
      e.dataTransfer.setData('offsetY', String(e.clientY - r.top));
    };

    // Double-click palette → quick-add to top-left of canvas
    const quickAdd = (type) => {
      const spec = NODE_TYPES[type];
      const id = `n-${Math.random().toString(36).slice(2, 7)}`;
      const c = typeOnCanvas[type] || 0;
      const fresh = { id, type, x: 60 + (c * 40) % 320, y: 60 + Math.floor(c / 8) * 120,
        label: defaultLabel(type), strength: defaultStrength(type), citations: [], notes: '' };
      setNodes(ns => [...ns, fresh]);
      setSelectedId(id);
      Store?.addArgumentNode?.(fresh);
      flashToast(`${spec.label} added`);
    };

    // ── Click handlers — mode-aware ──────────────────────────────────────
    const onNodeClick = (e, n) => {
      e.stopPropagation();
      if (mode === 'connect') {
        if (!linkStart) {
          setLinkStart(n.id);
          flashToast(`Pick target for "${n.label.slice(0, 28)}"`);
        } else if (linkStart === n.id) {
          setLinkStart(null);
        } else {
          const exists = edges.some(x => x.from === linkStart && x.to === n.id);
          if (!exists) {
            const newEdge = { id: `e-${Date.now()}`, from: linkStart, to: n.id };
            setEdges(es => [...es, newEdge]);
            Store?.linkArgumentNodes?.(linkStart, n.id);
            flashToast('Linked');
          } else {
            flashToast('Already linked');
          }
          setLinkStart(null);
        }
      } else {
        setSelectedId(n.id === selectedId ? null : n.id);
      }
    };

    // ── Mutations ────────────────────────────────────────────────────────
    const removeNode = (id) => {
      setNodes(ns => ns.filter(n => n.id !== id));
      setEdges(es => es.filter(e => e.from !== id && e.to !== id));
      if (selectedId === id) setSelectedId(null);
      if (linkStart === id) setLinkStart(null);
      Store?.removeArgumentNode?.(id);
      flashToast('Node removed');
    };
    const removeEdge = (id) => {
      setEdges(es => es.filter(e => e.id !== id));
      Store?.unlinkArgumentNodes?.(id);
    };
    const setStrength = (id, v) => {
      setNodes(ns => ns.map(n => n.id === id ? { ...n, strength: v } : n));
      Store?.updateArgumentNode?.(id, { strength: v });
    };
    const setLabel = (id, label) => {
      setNodes(ns => ns.map(n => n.id === id ? { ...n, label } : n));
      Store?.updateArgumentNode?.(id, { label });
    };
    const addCitation = (id) => {
      setNodes(ns => ns.map(n => n.id === id
        ? { ...n, citations: [...(n.citations || []), { short: 'NEW', full: 'click to edit', binding: 'binding' }] }
        : n));
      Store?.addCitation?.(id, { short: 'NEW' });
    };
    const bringToFront = (id) => setNodes(ns => [...ns.filter(n => n.id !== id), ns.find(n => n.id === id)].filter(Boolean));
    const sendToBack   = (id) => setNodes(ns => [ns.find(n => n.id === id), ...ns.filter(n => n.id !== id)].filter(Boolean));
    const clearCanvas  = () => {
      setNodes([]); setEdges([]); setSelectedId(null); setLinkStart(null);
      flashToast('Canvas cleared');
    };
    const saveArgument = () => {
      A.toast?.({ kind: 'active', title: 'Argument saved',
        message: `${argumentName} · ${nodes.length} nodes · ${edges.length} edges` });
      flashToast(`Saved "${argumentName.slice(0, 32)}"`);
    };
    const resetSeed = () => {
      setNodes(DATA.argumentGraph.nodes.slice());
      setEdges(DATA.argumentGraph.edges.slice());
      setSelectedId(null); setLinkStart(null);
      flashToast('Seed argument restored');
    };

    // ── Keyboard ─────────────────────────────────────────────────────────
    useEffect(() => {
      const onKey = (e) => {
        if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA') return;
        if (e.key === 'Escape') { setLinkStart(null); setSelectedId(null); }
        if ((e.key === 'Delete' || e.key === 'Backspace') && selectedId) { removeNode(selectedId); }
        if (e.key === 'l' || e.key === 'L') setMode('connect');
        if (e.key === 's' || e.key === 'S') setMode('select');
      };
      window.addEventListener('keydown', onKey);
      return () => window.removeEventListener('keydown', onKey);
    }, [selectedId]);

    // ── Right-click context menu ─────────────────────────────────────────
    const onNodeContextMenu = (e, n) => {
      e.preventDefault(); e.stopPropagation();
      if (!A.ContextMenu) return;
      A.ContextMenu.open(e, [
        { heading: `${NODE_TYPES[n.type].label} · ${n.label.slice(0, 32)}` },
        { label: 'Inspect',         icon: '◉', kbd: 'click', onSelect: () => { setMode('select'); setSelectedId(n.id); } },
        { label: 'Set strength',    icon: '◆',
          submenu: STRENGTH_TIERS.map(s => ({
            label: `${s.label} (${s.value})`, hint: n.strength === s.value ? 'current' : undefined,
            onSelect: () => setStrength(n.id, s.value) })) },
        { label: 'Add citation',    icon: '+',  onSelect: () => addCitation(n.id) },
        { label: 'Link from here',  icon: '↻',  kbd: 'L', onSelect: () => { setMode('connect'); setLinkStart(n.id); } },
        { separator: true },
        { label: 'Bring to front',  icon: '↑',  onSelect: () => bringToFront(n.id) },
        { label: 'Send to back',    icon: '↓',  onSelect: () => sendToBack(n.id) },
        { separator: true },
        { label: 'Remove', icon: '×', danger: true, kbd: 'Del', onSelect: () => removeNode(n.id) },
      ]);
    };
    const onCanvasContextMenu = (e) => {
      // Background right-click
      if (e.target !== canvasRef.current) return;
      e.preventDefault();
      if (!A.ContextMenu) return;
      A.ContextMenu.open(e, [
        { heading: 'Canvas' },
        { label: 'Restore seed argument', icon: '⟳', onSelect: resetSeed },
        { label: 'Clear canvas',          icon: '×', danger: true, disabled: nodes.length === 0, onSelect: clearCanvas },
        { separator: true },
        { label: 'Switch mode',           icon: '⎇',
          submenu: [
            { label: 'Select',  hint: 'S', onSelect: () => setMode('select') },
            { label: 'Connect', hint: 'L', onSelect: () => setMode('connect') },
          ] },
      ]);
    };

    // ── Helpers for the new layout ───────────────────────────────────────
    const modes = [{ k: 'select', l: 'Select' }, { k: 'connect', l: 'Connect' }];
    const labelClr = T.color.text.tertiary;

    // Eyebrow label — small uppercase tag used to group toolbar controls
    const eyebrow = { fontSize: '9.5px', color: labelClr, textTransform: 'uppercase',
      letterSpacing: '0.08em', fontWeight: 700, marginRight: 6 };
    const divider = { width: 1, height: 22, background: T.color.border.light, margin: '0 4px' };
    const strengthColor = overallStrength >= 75 ? CR.weld : overallStrength >= 60 ? CR.ember : overallStrength >= 40 ? CR.flame : CR.ash;

    return (
      <div>
        {/* ── TOOLBAR · three zones (name | mode | stats+actions) ─────── */}
        <div style={{ ...cr.card, marginBottom: '14px', padding: '8px 14px',
          display: 'flex', alignItems: 'center', gap: '10px', flexWrap: 'wrap' }}>

          {/* Zone 1 · Argument name */}
          <div style={{ display: 'flex', alignItems: 'center', flex: '1 1 320px', minWidth: 0 }}>
            <span style={eyebrow}>Argument</span>
            <input value={argumentName} onChange={e => setArgumentName(e.target.value)}
              spellCheck={false}
              style={{ flex: 1, padding: '5px 9px', fontSize: '12.5px', fontWeight: 600,
                border: `1px solid ${T.color.border.light}`, borderRadius: '5px',
                fontFamily: T.font.family, color: T.color.text.primary,
                background: T.color.bg.card, outline: 'none', minWidth: '220px' }} />
          </div>

          <div style={divider} />

          {/* Zone 2 · Mode segmented control with inline glyphs */}
          <div style={{ display: 'flex', gap: '2px', padding: '2px',
            background: T.color.bg.secondary, borderRadius: '6px',
            border: `1px solid ${T.color.border.light}` }}>
            {modes.map(m => {
              const active = mode === m.k;
              const glyph = m.k === 'connect'
                ? <svg width="11" height="11" viewBox="0 0 12 12" aria-hidden="true">
                    <path d="M2 6 C 4 2, 8 10, 10 6" fill="none" stroke="currentColor" strokeWidth="1.6" />
                    <circle cx="2" cy="6" r="1.5" fill="currentColor" />
                    <circle cx="10" cy="6" r="1.5" fill="currentColor" />
                  </svg>
                : <svg width="11" height="11" viewBox="0 0 12 12" aria-hidden="true">
                    <path d="M3 2 L3 10 L5.5 8 L7.5 11 L9 10 L7 7 L10 7 Z" fill="currentColor" />
                  </svg>;
              return (
                <button key={m.k} onClick={() => { setMode(m.k); setLinkStart(null); }}
                  title={m.k === 'connect' ? 'L · Click two nodes to draw a directed edge' : 'S · Click a node to inspect'}
                  style={{ display: 'inline-flex', alignItems: 'center', gap: 5,
                    padding: '4px 10px', borderRadius: '4px', border: 'none',
                    background: active ? T.color.bg.card : 'transparent',
                    boxShadow: active ? '0 1px 2px rgba(10,22,40,0.08)' : 'none',
                    color: active ? CR.flame : T.color.text.secondary,
                    fontSize: '11px', fontWeight: active ? 700 : 500,
                    cursor: 'pointer', fontFamily: T.font.family }}>
                  {glyph}{m.l}
                </button>
              );
            })}
          </div>

          {/* Preset / restore — kept compact */}
          <select defaultValue="" onChange={e => { if (e.target.value === 'seed') { resetSeed(); e.target.value = ''; } }}
            title="Restore a preset argument"
            style={{ padding: '4px 8px', fontSize: '11px',
              border: `1px solid ${T.color.border.light}`, borderRadius: '5px',
              background: T.color.bg.card, color: T.color.text.secondary,
              fontFamily: T.font.family, cursor: 'pointer' }}>
            <option value="">Preset…</option>
            <option value="seed">MSJ seed · Pelican Bay</option>
          </select>

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

          {/* Zone 3 · live counts + actions */}
          <div style={{ display: 'flex', alignItems: 'center', gap: 12,
            padding: '2px 10px', background: T.color.bg.secondary,
            borderRadius: 6, border: `1px solid ${T.color.border.light}`,
            fontFamily: T.font.mono, fontSize: 11 }}>
            <span title="Nodes on canvas" style={{ color: T.color.text.secondary }}>
              <b style={{ color: T.color.text.primary, fontWeight: 700 }}>{nodes.length}</b>
              <span style={{ ...eyebrow, marginLeft: 4, marginRight: 0 }}>nodes</span>
            </span>
            <span title="Directed edges" style={{ color: T.color.text.secondary }}>
              <b style={{ color: T.color.text.primary, fontWeight: 700 }}>{edges.length}</b>
              <span style={{ ...eyebrow, marginLeft: 4, marginRight: 0 }}>edges</span>
            </span>
            <span title={`Average node strength`} style={{ color: T.color.text.secondary }}>
              <b style={{ color: strengthColor, fontWeight: 700 }}>{overallStrength}%</b>
              <span style={{ ...eyebrow, marginLeft: 4, marginRight: 0 }}>strength</span>
            </span>
          </div>
          <button onClick={clearCanvas} style={cr.btnSecondary} disabled={nodes.length === 0}
            title="Clear all nodes and edges">Clear</button>
          <button onClick={saveArgument} style={cr.btnPrimary}
            title="Persist this argument">Save argument</button>
        </div>

        {/* ── 3-PANEL WORKSPACE ───────────────────────────────────────── */}
        <div style={{ display: 'grid', gridTemplateColumns: '260px 1fr 280px', gap: '14px', height: 'calc(100vh - 380px)', minHeight: '540px' }}>

          {/* LEFT RAIL — search + filter chips + grouped palette + footer */}
          <div style={{ ...cr.card, marginBottom: 0, display: 'flex', flexDirection: 'column', overflow: 'hidden' }}>
            <div style={{ padding: '10px 12px', borderBottom: `1px solid ${T.color.border.light}` }}>
              <div style={{ fontSize: '10px', fontWeight: 700, color: labelClr, textTransform: 'uppercase', letterSpacing: '0.08em', marginBottom: '6px' }}>Argument parts</div>
              <input value={search} onChange={e => setSearch(e.target.value)} placeholder="Search 6 part types…"
                style={{ width: '100%', height: '28px', border: `1px solid ${T.color.border.light}`, borderRadius: '5px', padding: '0 8px', fontSize: '11px', fontFamily: T.font.family, background: T.color.bg.primary, color: T.color.text.primary, outline: 'none', marginBottom: '6px', boxSizing: 'border-box' }} />
              <div style={{ display: 'flex', flexWrap: 'wrap', gap: '3px' }}>
                {typeOptions.map(t => {
                  const isActive = filterType === t;
                  const c = t === 'All' ? CR.flame : NODE_TYPES[t]?.color;
                  return (
                    <button key={t} onClick={() => setFilterType(t)}
                      style={{ padding: '2px 6px', borderRadius: '8px',
                        border: `1px solid ${isActive ? c : T.color.border.light}`,
                        background: isActive ? c + '14' : T.color.bg.card,
                        color: isActive ? c : T.color.text.secondary,
                        fontSize: '9px', fontWeight: 600, cursor: 'pointer',
                        textTransform: 'capitalize', fontFamily: T.font.family }}>{t}</button>
                  );
                })}
              </div>
            </div>
            <div style={{ flex: 1, overflowY: 'auto' }}>
              {filteredPalette.length === 0 && (
                <div role="status" style={{ padding: '16px 12px', textAlign: 'center', fontSize: 11, color: labelClr }}>
                  No part types match.
                </div>
              )}
              {filteredPalette.map(([type, spec]) => {
                const onCanvasCount = typeOnCanvas[type] || 0;
                return (
                  <div key={type}
                    draggable
                    onDragStart={e => onPaletteDragStart(e, type)}
                    onDoubleClick={() => quickAdd(type)}
                    title="Drag to canvas · or double-click to add at top-left"
                    style={{ padding: '10px 12px', borderBottom: `1px solid ${T.color.border.light}`, cursor: 'grab', display: 'flex', alignItems: 'flex-start', gap: '8px', userSelect: 'none', transition: 'background 0.1s' }}
                    onMouseEnter={e => { e.currentTarget.style.background = spec.color + '08'; }}
                    onMouseLeave={e => { e.currentTarget.style.background = 'transparent'; }}>
                    <span style={{
                      width: 12, height: 12, marginTop: 2, flexShrink: 0,
                      borderRadius: spec.shape === 'diamond' ? 0 : 3,
                      transform: spec.shape === 'diamond' ? 'rotate(45deg)' : 'none',
                      background: spec.color,
                    }} />
                    <div style={{ flex: 1, minWidth: 0 }}>
                      <div style={{ display: 'flex', alignItems: 'baseline', gap: 6 }}>
                        <span style={{ fontSize: '11.5px', fontWeight: 600, color: spec.color }}>{spec.label}</span>
                        {onCanvasCount > 0 && (
                          <span style={{ fontSize: 9, color: spec.color, fontFamily: T.font.mono, fontWeight: 600 }}>×{onCanvasCount}</span>
                        )}
                      </div>
                      <div style={{ fontSize: '10px', color: labelClr, marginTop: '2px' }}>{spec.hint}</div>
                    </div>
                    <span style={{ color: labelClr, fontSize: 11, marginTop: 2, fontFamily: T.font.mono }}>⠿</span>
                  </div>
                );
              })}
            </div>
            <div style={{ padding: '8px 12px', borderTop: `1px solid ${T.color.border.light}`, background: T.color.bg.secondary, fontSize: '10px', color: labelClr }}>
              Drag or double-click to add · {filteredPalette.length} of {Object.keys(NODE_TYPES).length}
            </div>
          </div>

          {/* CENTER CANVAS */}
          <div ref={canvasRef}
            onDragOver={onCanvasDragOver}
            onDrop={onCanvasDrop}
            onClick={() => { setSelectedId(null); setLinkStart(null); }}
            onContextMenu={onCanvasContextMenu}
            style={{ position: 'relative', overflow: 'auto',
              // Subtle flame-tinted dot grid (matches Nexus's fuchsia dots)
              background: `${T.color.bg.card} radial-gradient(circle at 25px 25px, rgba(220,38,38,0.07) 2px, transparent 3px) 0 0/40px 40px`,
              border: `1px solid ${T.color.border.light}`, borderRadius: T.radius.lg }}>

            {/* SVG edges — direction-aware cubic Bézier with white-pill labels.
                Vertical-major edges pull control points along Y; horizontal-major
                pull along X. The result is a clean S/I curve that follows the
                edge's natural travel — no more upward-arching curves regardless
                of direction. Endpoints clip to each rect's edge. */}
            <svg width="100%" height="100%"
              style={{ position: 'absolute', inset: 0, pointerEvents: 'none', overflow: 'visible' }}>
              <defs>
                <marker id="cr-arrow" markerWidth="8" markerHeight="8" refX="7.2" refY="4" orient="auto-start-reverse">
                  <path d="M0,0 L8,4 L0,8 z" fill={T.color.text.tertiary} />
                </marker>
                <marker id="cr-arrow-hot" markerWidth="8" markerHeight="8" refX="7.2" refY="4" orient="auto-start-reverse">
                  <path d="M0,0 L8,4 L0,8 z" fill={CR.flame} />
                </marker>
                <style>{`
                  .cr-edge { pointer-events: auto; }
                  .cr-edge .cr-edge-line { transition: stroke-width 140ms, opacity 140ms; }
                  .cr-edge .cr-edge-rm   { opacity: 0; transition: opacity 140ms; pointer-events: none; }
                  .cr-edge:hover .cr-edge-line { stroke-width: 2.4; opacity: 1; }
                  .cr-edge:hover .cr-edge-rm   { opacity: 1; pointer-events: auto; }
                `}</style>
              </defs>
              {edges.map(e => {
                const from = node(e.from), to = node(e.to);
                if (!from || !to) return null;
                const fSpec = NODE_TYPES[from.type], tSpec = NODE_TYPES[to.type];
                const fcx = from.x + fSpec.w / 2, fcy = from.y + fSpec.h / 2;
                const tcx = to.x   + tSpec.w / 2, tcy = to.y   + tSpec.h / 2;
                const dx = tcx - fcx, dy = tcy - fcy;
                if (Math.abs(dx) < 0.5 && Math.abs(dy) < 0.5) return null;
                // Clip line to rect-edge of each box (Liang-Barsky style, axis-only).
                const clipOffset = (h, w, ddx, ddy) => {
                  const tx = ddx === 0 ? Infinity : Math.abs(w / ddx);
                  const ty = ddy === 0 ? Infinity : Math.abs(h / ddy);
                  const t = Math.min(tx, ty);
                  return { ox: ddx * t, oy: ddy * t };
                };
                const inset = 4; // arrow breathing room on target
                const fA = clipOffset(fSpec.h / 2,  fSpec.w / 2,  dx,  dy);
                const tA = clipOffset(tSpec.h / 2 + inset, tSpec.w / 2 + inset, -dx, -dy);
                const x1 = fcx + fA.ox, y1 = fcy + fA.oy;
                const x2 = tcx + tA.ox, y2 = tcy + tA.oy;
                // Direction-aware control points
                const edx = x2 - x1, edy = y2 - y1;
                const vertical = Math.abs(edy) >= Math.abs(edx);
                const pull = Math.min(120, Math.max(40, Math.hypot(edx, edy) * 0.45));
                const sgnX = edx >= 0 ? 1 : -1, sgnY = edy >= 0 ? 1 : -1;
                const c1x = vertical ? x1                : x1 + sgnX * pull;
                const c1y = vertical ? y1 + sgnY * pull  : y1;
                const c2x = vertical ? x2                : x2 - sgnX * pull;
                const c2y = vertical ? y2 - sgnY * pull  : y2;
                const d = `M ${x1} ${y1} C ${c1x} ${c1y}, ${c2x} ${c2y}, ${x2} ${y2}`;
                // True cubic midpoint at t = 0.5
                const mx = (x1 + 3 * c1x + 3 * c2x + x2) / 8;
                const my = (y1 + 3 * c1y + 3 * c2y + y2) / 8;
                const hot = from.type === 'counter' || to.type === 'counter';
                const rel = relationshipLabel(from.type, to.type);
                const stroke = hot ? CR.flame : T.color.text.tertiary;
                const labelW = rel.length * 5.6 + 14;
                return (
                  <g key={e.id} className="cr-edge">
                    {/* Wide invisible hit target so hover is forgiving */}
                    <path d={d} fill="none" stroke="transparent" strokeWidth="14" />
                    {/* Visible curve */}
                    <path className="cr-edge-line" d={d} fill="none" stroke={stroke}
                      strokeWidth="1.6" opacity={hot ? 0.9 : 0.6}
                      markerEnd={hot ? 'url(#cr-arrow-hot)' : 'url(#cr-arrow)'} />
                    {/* Always-visible relationship pill */}
                    <g transform={`translate(${mx - labelW / 2}, ${my - 8})`}>
                      <rect width={labelW} height="16" rx="8" ry="8"
                        fill={T.color.bg.card}
                        stroke={hot ? CR.flame + '55' : T.color.border.light}
                        strokeWidth="1" />
                      <text x={labelW / 2} y="11" textAnchor="middle" fontSize="9.5"
                        fill={hot ? CR.flame : T.color.text.secondary}
                        fontFamily={T.font.family} fontWeight="600"
                        style={{ userSelect: 'none' }}>{rel}</text>
                    </g>
                    {/* Hover-only × remove handle, off to the right of the pill */}
                    <g className="cr-edge-rm"
                      transform={`translate(${mx + labelW / 2 + 6}, ${my})`}
                      onClick={(ev) => { ev.stopPropagation(); removeEdge(e.id); }}
                      style={{ cursor: 'pointer' }}>
                      <circle r="8" fill={CR.flame} />
                      <text textAnchor="middle" y="3.5" fontSize="11" fontWeight="700" fill="#fff"
                        style={{ userSelect: 'none' }}>×</text>
                    </g>
                  </g>
                );
              })}
            </svg>

            {/* Empty state — anvil-and-flame glyph (Crucible motif) */}
            {nodes.length === 0 && (
              <div style={{ position: 'absolute', inset: 0,
                display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center',
                pointerEvents: 'none', color: labelClr, fontSize: 12.5, gap: 8 }}>
                <svg width="56" height="56" viewBox="0 0 56 56" aria-hidden="true">
                  {/* flame */}
                  <path d="M28 6 C 22 14, 32 18, 28 26 C 26 22, 22 24, 22 30 C 22 36, 28 38, 28 38 C 28 38, 34 36, 34 30 C 34 24, 30 22, 28 26 C 24 18, 34 14, 28 6 Z"
                    fill={CR.flame} opacity="0.35" />
                  {/* anvil */}
                  <path d="M12 42 L44 42 L40 46 L16 46 Z" fill={T.color.text.tertiary} opacity="0.45" />
                  <rect x="22" y="46" width="12" height="3" fill={T.color.text.tertiary} opacity="0.45" />
                  <rect x="18" y="49" width="20" height="2.5" rx="1" fill={T.color.text.tertiary} opacity="0.45" />
                </svg>
                <div style={{ fontSize: 13, fontWeight: 700, color: T.color.text.secondary, letterSpacing: '-0.01em' }}>Forge an argument</div>
                <div style={{ fontSize: 11.5, color: labelClr }}>Drag a part from the left rail · double-click to quick-add</div>
              </div>
            )}

            {/* Nodes — clean white cards (Nexus pattern) with type accent */}
            {nodes.map(n => {
              const spec = NODE_TYPES[n.type];
              const isSel = selectedId === n.id;
              const isLinkSrc = linkStart === n.id;
              const sv = n.strength || 0;
              const sColor = sv >= 75 ? CR.weld
                          : sv >= 60 ? CR.ember
                          : sv >= 40 ? CR.flame
                          :            CR.ash;
              return (
                <div key={n.id} draggable
                  onDragStart={e => onNodeDragStart(e, n)}
                  onClick={e => onNodeClick(e, n)}
                  onContextMenu={e => onNodeContextMenu(e, n)}
                  style={{
                    position: 'absolute', left: n.x, top: n.y,
                    width: spec.w, minHeight: spec.h,
                    background: T.color.bg.card,
                    border: `1px solid ${isLinkSrc ? CR.flame : isSel ? spec.color : T.color.border.light}`,
                    borderLeft: `3px solid ${spec.color}`,
                    borderRadius: 8, padding: '9px 12px 10px',
                    cursor: mode === 'connect' ? 'crosshair' : 'grab',
                    boxShadow: isLinkSrc
                      ? `0 0 0 3px ${CR.flame}26, 0 6px 16px ${CR.flame}33`
                      : isSel
                        ? `0 0 0 2px ${spec.color}33, 0 4px 14px rgba(10,22,40,0.10)`
                        : '0 1px 2px rgba(10,22,40,0.04), 0 1px 3px rgba(10,22,40,0.06)',
                    transition: 'box-shadow 160ms, border-color 140ms, transform 140ms',
                    userSelect: 'none', zIndex: isSel || isLinkSrc ? 5 : 1,
                  }}
                  onMouseEnter={(ev) => { if (!isSel && !isLinkSrc) ev.currentTarget.style.boxShadow = `0 2px 4px rgba(10,22,40,0.06), 0 6px 16px rgba(10,22,40,0.10)`; }}
                  onMouseLeave={(ev) => { if (!isSel && !isLinkSrc) ev.currentTarget.style.boxShadow = '0 1px 2px rgba(10,22,40,0.04), 0 1px 3px rgba(10,22,40,0.06)'; }}>

                  {/* Header — type label + strength + remove */}
                  <div style={{ display: 'flex', alignItems: 'center', gap: 6, marginBottom: 4 }}>
                    <span style={{
                      display: 'inline-flex', alignItems: 'center', gap: 4,
                      fontSize: 9, fontWeight: 700, color: spec.color,
                      textTransform: 'uppercase', letterSpacing: '0.1em',
                    }}>
                      <span style={{
                        width: 6, height: 6, background: spec.color,
                        borderRadius: spec.shape === 'diamond' ? 0 : 1,
                        transform: spec.shape === 'diamond' ? 'rotate(45deg)' : 'none',
                      }} />
                      {spec.label}
                    </span>
                    <span style={{ flex: 1 }} />
                    {n.strength != null && (
                      <span title={`Strength ${n.strength}/100`} style={{
                        display: 'inline-flex', alignItems: 'center', gap: 3,
                        padding: '0 6px', borderRadius: 8,
                        fontSize: 9.5, fontWeight: 700, fontFamily: T.font.mono,
                        background: sColor + '14', color: sColor,
                      }}>
                        <span style={{ width: 4, height: 4, borderRadius: '50%', background: sColor }} />
                        {n.strength}
                      </span>
                    )}
                    <button onClick={(ev) => { ev.stopPropagation(); removeNode(n.id); }}
                      title="Remove (Del)" aria-label="Remove node"
                      style={{ background: 'transparent', border: 'none', color: T.color.text.tertiary,
                        cursor: 'pointer', padding: 0, fontSize: 14, lineHeight: 1, opacity: 0.6 }}>×</button>
                  </div>

                  {/* Body — primary label */}
                  <div style={{
                    fontSize: 12, fontWeight: 500, color: T.color.text.primary,
                    lineHeight: 1.4, letterSpacing: '-0.005em',
                  }}>{n.label}</div>

                  {/* Citation chips — gold pill, inline (Nexus-grade) */}
                  {n.citations && n.citations.length > 0 && (
                    <div style={{ display: 'flex', flexWrap: 'wrap', gap: 4, marginTop: 6 }}>
                      {n.citations.slice(0, 3).map((c, i) => (
                        <span key={i} title={c.full || ''} style={{
                          display: 'inline-flex', alignItems: 'center',
                          padding: '1px 6px', borderRadius: 3,
                          fontSize: 9, fontWeight: 600, fontFamily: T.font.mono,
                          background: CR.gold + '18', color: CR.gold,
                          maxWidth: 100, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap',
                        }}>{c.short || c}</span>
                      ))}
                      {n.citations.length > 3 && (
                        <span style={{ fontSize: 9, color: labelClr, fontFamily: T.font.mono, padding: '1px 4px' }}>+{n.citations.length - 3}</span>
                      )}
                    </div>
                  )}
                </div>
              );
            })}

            {/* Mode hint pill (top-left) — inline SVG glyph, no ASCII shapes */}
            <div style={{ position: 'absolute', top: '12px', left: '12px',
              display: 'inline-flex', alignItems: 'center', gap: 6,
              padding: '4px 10px 4px 8px', borderRadius: '12px',
              background: mode === 'connect' ? CR.flameBg : T.color.bg.secondary,
              border: `1px solid ${mode === 'connect' ? CR.flame + '88' : T.color.border.light}`,
              fontSize: '10px', color: mode === 'connect' ? CR.flame : T.color.text.secondary,
              fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.06em',
              boxShadow: mode === 'connect' ? `0 0 0 3px ${CR.flame}14` : 'none',
              transition: 'background 140ms, border-color 140ms, box-shadow 140ms' }}>
              {mode === 'connect'
                ? <svg width="11" height="11" viewBox="0 0 12 12" aria-hidden="true">
                    <path d="M2 6 C 4 2, 8 10, 10 6" fill="none" stroke="currentColor" strokeWidth="1.6" />
                    <circle cx="2" cy="6" r="1.5" fill="currentColor" />
                    <circle cx="10" cy="6" r="1.5" fill="currentColor" />
                  </svg>
                : <svg width="11" height="11" viewBox="0 0 12 12" aria-hidden="true">
                    <path d="M3 2 L3 10 L5.5 8 L7.5 11 L9 10 L7 7 L10 7 Z" fill="currentColor" />
                  </svg>}
              {mode === 'connect'
                ? (linkStart ? 'pick a target node' : 'connect · click two nodes')
                : 'select · click to inspect'}
            </div>

            {/* Flash toast — softer shadow, slimmer pill */}
            {flash && (
              <div style={{ position: 'absolute', bottom: '14px', left: '50%', transform: 'translateX(-50%)',
                padding: '6px 14px', background: CR.flame, color: '#fff',
                borderRadius: '20px', fontSize: '11.5px', fontWeight: 600,
                letterSpacing: '-0.005em',
                boxShadow: `0 1px 2px rgba(10,22,40,0.10), 0 6px 20px ${CR.flame}55` }}>
                {flash}
              </div>
            )}
          </div>

          {/* RIGHT RAIL — current argument · inspector · shortcuts */}
          <div style={{ display: 'flex', flexDirection: 'column', gap: '14px', overflow: 'auto' }}>

            {/* Argument summary — full-width type rows with mini-bars */}
            <div style={{ ...cr.card, marginBottom: 0 }}>
              <div style={{ ...cr.cardH, color: CR.flame }}>
                <span>Current argument</span>
                <span style={{ fontFamily: T.font.mono, fontSize: 10, color: strengthColor, fontWeight: 700 }}>{overallStrength}%</span>
              </div>
              <div style={{ padding: '12px 14px' }}>
                <div style={{ fontSize: '12.5px', fontWeight: 700, color: T.color.text.primary,
                  marginBottom: 8, lineHeight: 1.35, letterSpacing: '-0.01em' }}>{argumentName}</div>
                {/* Strength bar — single dominant signal */}
                <div style={{ marginBottom: 12 }}>
                  <div style={{ display: 'flex', alignItems: 'baseline', justifyContent: 'space-between', marginBottom: 3 }}>
                    <span style={eyebrow}>Overall strength</span>
                    <span style={{ fontSize: 10.5, fontFamily: T.font.mono, color: T.color.text.tertiary }}>
                      {nodes.length}n · {edges.length}e
                    </span>
                  </div>
                  <Bar value={overallStrength} color={strengthColor} glow={overallStrength >= 75} />
                </div>
                {/* Per-type mini-rows */}
                {nodes.length > 0 && (() => {
                  const maxC = Math.max(1, ...Object.values(typeOnCanvas));
                  return (
                    <div style={{ display: 'flex', flexDirection: 'column', gap: 5 }}>
                      {Object.entries(NODE_TYPES).map(([t, spec]) => {
                        const c = typeOnCanvas[t] || 0;
                        const dim = c === 0;
                        return (
                          <div key={t} style={{ display: 'grid',
                            gridTemplateColumns: '10px 1fr 60px 22px', alignItems: 'center', gap: 6,
                            opacity: dim ? 0.4 : 1 }}>
                            <span style={{
                              width: 10, height: 10, background: spec.color,
                              borderRadius: spec.shape === 'diamond' ? 0 : 2,
                              transform: spec.shape === 'diamond' ? 'rotate(45deg)' : 'none',
                            }} />
                            <span style={{ fontSize: 10.5, color: T.color.text.secondary, fontWeight: 500 }}>{spec.label}</span>
                            <div style={{ height: 4, background: T.color.bg.tertiary, borderRadius: 2, overflow: 'hidden' }}>
                              <div style={{ width: `${(c / maxC) * 100}%`, height: '100%', background: spec.color, borderRadius: 2,
                                transition: 'width 240ms' }} />
                            </div>
                            <span style={{ fontFamily: T.font.mono, fontSize: 10.5, color: dim ? labelClr : spec.color,
                              fontWeight: 700, textAlign: 'right' }}>{c}</span>
                          </div>
                        );
                      })}
                    </div>
                  );
                })()}
                {unsupportedClaims > 0 && (
                  <div style={{ marginTop: 10, padding: '7px 10px',
                    background: CR.flameBg, border: `1px solid ${CR.flame}33`,
                    borderRadius: 6, fontSize: 10.5, color: CR.flame, lineHeight: 1.45 }}>
                    <b>Warning:</b> {unsupportedClaims} unsupported claim{unsupportedClaims === 1 ? '' : 's'} will fail under attack.
                  </div>
                )}
              </div>
            </div>

            {/* Selected node inspector */}
            {selected && (
              <div style={{ ...cr.card, marginBottom: 0 }}>
                <div style={cr.cardH}>
                  <span>Selected node</span>
                  <span style={{ fontFamily: T.font.mono, fontSize: 10, color: T.color.text.tertiary }}>{selected.id}</span>
                </div>
                <div style={{ padding: '12px 14px' }}>
                  {/* Type chip header */}
                  <div style={{ display: 'inline-flex', alignItems: 'center', gap: 5,
                    padding: '2px 8px', borderRadius: 10,
                    background: NODE_TYPES[selected.type].color + '14',
                    color: NODE_TYPES[selected.type].color,
                    fontSize: 9.5, fontWeight: 700, textTransform: 'uppercase',
                    letterSpacing: '0.1em', marginBottom: 8 }}>
                    <span style={{
                      width: 7, height: 7, background: NODE_TYPES[selected.type].color,
                      borderRadius: NODE_TYPES[selected.type].shape === 'diamond' ? 0 : 1,
                      transform: NODE_TYPES[selected.type].shape === 'diamond' ? 'rotate(45deg)' : 'none',
                    }} />
                    {NODE_TYPES[selected.type].label}
                  </div>
                  <textarea value={selected.label}
                    onChange={e => setLabel(selected.id, e.target.value)}
                    spellCheck={false}
                    style={{ width: '100%', padding: '8px 10px', fontSize: 12,
                      border: `1px solid ${T.color.border.medium}`, borderRadius: 6,
                      fontFamily: T.font.family, color: T.color.text.primary,
                      background: T.color.bg.card, outline: 'none', resize: 'vertical',
                      minHeight: 64, marginBottom: 12, boxSizing: 'border-box',
                      lineHeight: 1.4 }} />

                  <Inspect label="Strength">
                    {(() => {
                      const sv = selected.strength || 0;
                      const sc = sv >= 75 ? CR.weld : sv >= 60 ? CR.ember : sv >= 40 ? CR.flame : CR.ash;
                      return (
                        <>
                          <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
                            <input type="range" min="0" max="100" value={sv}
                              onChange={e => setStrength(selected.id, +e.target.value)}
                              style={{ flex: 1, accentColor: sc }} />
                            <span style={{ fontSize: 13, fontWeight: 700, fontFamily: T.font.mono,
                              minWidth: 36, textAlign: 'right', color: sc }}>{sv}</span>
                          </div>
                          {/* tier scale: weak | moderate | strong | critical */}
                          <div style={{ position: 'relative', height: 12, marginTop: 2,
                            display: 'grid', gridTemplateColumns: '40fr 20fr 15fr 25fr',
                            fontSize: 8.5, color: labelClr, fontWeight: 600,
                            textTransform: 'uppercase', letterSpacing: '0.06em' }}>
                            <span style={{ textAlign: 'left',  color: sv < 40 ? CR.ash   : labelClr }}>weak</span>
                            <span style={{ textAlign: 'left',  color: sv >= 40 && sv < 60 ? CR.flame : labelClr }}>mod</span>
                            <span style={{ textAlign: 'left',  color: sv >= 60 && sv < 75 ? CR.ember : labelClr }}>strong</span>
                            <span style={{ textAlign: 'right', color: sv >= 75 ? CR.weld  : labelClr }}>critical</span>
                          </div>
                        </>
                      );
                    })()}
                  </Inspect>

                  <Inspect label="Citations">
                    {(selected.citations || []).length === 0 && (
                      <div style={{ fontSize: 10.5, color: labelClr, fontStyle: 'italic' }}>None yet — add from Authority Stack.</div>
                    )}
                    {(selected.citations || []).map((c, i) => (
                      <div key={i} style={{ display: 'flex', alignItems: 'center', gap: 6, padding: '4px 0' }}>
                        <span style={{ fontSize: 10, color: CR.gold, fontFamily: T.font.mono, minWidth: 60, fontWeight: 700 }}>{c.short}</span>
                        <span style={{ flex: 1, fontSize: 10.5, color: T.color.text.secondary, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{c.full || ''}</span>
                        <Tag color={c.binding === 'binding' ? CR.weld : c.binding === 'persuasive' ? CR.ember : CR.ash}>{c.binding || 'binding'}</Tag>
                      </div>
                    ))}
                    <button onClick={() => addCitation(selected.id)} style={{ ...cr.filterBtn, marginTop: 6, fontSize: 10 }}>+ Add citation</button>
                  </Inspect>

                  <Inspect label="Links">
                    <div style={{ display: 'flex', gap: 12, fontSize: 10.5, color: T.color.text.secondary }}>
                      <div>incoming <span style={{ fontFamily: T.font.mono, fontWeight: 700, color: T.color.text.primary }}>{edges.filter(e => e.to === selected.id).length}</span></div>
                      <div>outgoing <span style={{ fontFamily: T.font.mono, fontWeight: 700, color: T.color.text.primary }}>{edges.filter(e => e.from === selected.id).length}</span></div>
                    </div>
                  </Inspect>

                  <button onClick={() => removeNode(selected.id)}
                    style={{ ...cr.btnSecondary, width: '100%', borderColor: CR.flame + '55',
                      background: CR.flameBg, color: CR.flame, marginTop: 8, fontWeight: 600 }}>Remove node</button>
                </div>
              </div>
            )}

            {/* Shortcuts — compact 2-col kbd grid */}
            <div style={{ ...cr.card, marginBottom: 0 }}>
              <div style={cr.cardH}>Shortcuts</div>
              <div style={{ padding: '10px 14px', display: 'grid',
                gridTemplateColumns: 'auto 1fr', gap: '6px 10px',
                fontSize: '10.5px', color: T.color.text.secondary, alignItems: 'baseline' }}>
                {[
                  ['Drag',         'palette → canvas'],
                  ['Dbl-click',    'palette · quick-add'],
                  ['Drag node',    'reposition'],
                  ['Right-click',  'context menu'],
                  ['S / L',        'select / connect'],
                  ['Esc',          'cancel · deselect'],
                  ['Del',          'remove selected'],
                ].map(([k, v]) => (
                  <React.Fragment key={k}>
                    <kbd style={{ display: 'inline-block', padding: '1px 6px', borderRadius: 3,
                      background: T.color.bg.secondary, border: `1px solid ${T.color.border.light}`,
                      fontFamily: T.font.mono, fontSize: 10, fontWeight: 600,
                      color: T.color.text.primary, whiteSpace: 'nowrap' }}>{k}</kbd>
                    <span>{v}</span>
                  </React.Fragment>
                ))}
              </div>
            </div>
          </div>
        </div>
      </div>
    );
  }
  const Inspect = ({ label, children }) => (
    <div style={{ marginBottom: 12 }}>
      <div style={{ fontSize: 9, fontWeight: 700, color: T.color.text.tertiary,
        textTransform: 'uppercase', letterSpacing: '0.08em', marginBottom: 4 }}>{label}</div>
      {children}
    </div>
  );
  function defaultLabel(t) {
    return ({ issue: 'New issue — describe the legal question', claim: 'New claim — state our position',
      premise: 'New premise — supporting logic', support: 'New support — citation or evidence',
      counter: 'Opposition argument', concession: 'Acknowledged weakness' })[t] || 'Untitled';
  }
  function defaultStrength(t) {
    return ({ issue: 100, claim: 60, premise: 60, support: 70, counter: 40, concession: 20 })[t] || 50;
  }
  // Argument-graph relationship labels — what the edge actually says
  function relationshipLabel(fromType, toType) {
    const map = {
      'issue→claim':         'frames',
      'premise→claim':       'supports',
      'support→premise':     'evidences',
      'support→claim':       'evidences',
      'counter→claim':       'rebuts',
      'counter→premise':     'weakens',
      'counter→support':     'attacks',
      'concession→claim':    'qualifies',
      'concession→premise':  'qualifies',
      'claim→issue':         'addresses',
    };
    return map[`${fromType}→${toType}`] || 'relates to';
  }

  // ════════════════════════════════════════════════════════════════════════
  //  2. ADVERSARY — red-team simulator
  // ════════════════════════════════════════════════════════════════════════
  function Adversary() {
    A.useTrack?.('crucible.adversary', {}, []);
    const args_ = DATA.arguments;
    const [selectedId, setSelectedId] = useState(args_[0].id);
    const sel = args_.find(a => a.id === selectedId);
    const attacks = makeAttacks(sel);
    const cross   = makeCrossExam(sel);
    const steel   = makeSteelman(sel);
    const overall = Math.round(attacks.reduce((s, a) => s + a.survival, 0) / attacks.length);
    const verdict = overall >= 75 ? { label: 'BATTLE-READY',           color: CR.weld } :
                    overall >= 60 ? { label: 'NEEDS HARDENING',        color: CR.ember } :
                                    { label: 'WILL FAIL UNDER ATTACK', color: CR.flame };

    const runAttack = () => { Store?.runAttack?.(sel.id); A.toast?.({ kind: 'pending', title: 'Attack run', message: `${verdict.label} · survival ${overall}%` }); };

    return (
      <div>
        <StatRow cols="repeat(4, 1fr)">
          <Stat label="Argument" value={sel.label.split('—')[0].trim()} hint={sel.matter} />
          <Stat label="Survival score" value={overall + '%'} color={verdict.color} hint={verdict.label.toLowerCase()} />
          <Stat label="Critical weaknesses" value={attacks.filter(a => a.survival < 60).length} color={CR.flame} hint="below 60% survival" />
          <Stat label="Cross-exam landmines" value={cross.filter(c => c.severity === 'critical').length} color={CR.flame} hint="hostile-question scenarios" />
        </StatRow>

        <div style={{ display: 'grid', gridTemplateColumns: '260px 1fr', gap: '14px' }}>
          <Card title="Pick argument" pad={false}>
            {args_.map(a => (
              <div key={a.id} onClick={() => setSelectedId(a.id)}
                style={{
                  padding: '10px 14px',
                  borderLeft: `3px solid ${selectedId === a.id ? CR.flame : 'transparent'}`,
                  background: selectedId === a.id ? CR.flameBg : 'transparent',
                  borderBottom: `1px solid ${T.color.border.light}`,
                  cursor: 'pointer',
                }}>
                <div style={{ fontSize: 11.5, fontWeight: 600, color: T.color.text.primary }}>{a.label}</div>
                <div style={{ fontSize: 9.5, color: T.color.text.tertiary, marginTop: 2 }}>
                  {a.matter} · <span style={{ fontFamily: T.font.mono }}>{a.strength}</span> draft strength · {a.filed}
                </div>
              </div>
            ))}
          </Card>

          <Card title="Attack vectors"
            right={<div style={{ display: 'flex', gap: 8, alignItems: 'center' }}>
              <Tag color={verdict.color} strong>{verdict.label}</Tag>
              <button onClick={runAttack} style={cr.btnPrimary}>Re-run attack</button>
            </div>}>
            <div style={{ display: 'grid', gap: '10px' }}>
              {attacks.map(a => (
                <div key={a.id} style={{
                  padding: '10px 12px', borderRadius: 8,
                  border: `1px solid ${a.survival < 60 ? CR.flame + '55' : T.color.border.light}`,
                  background: a.survival < 60 ? CR.flameBg : T.color.bg.card,
                }}>
                  <div style={{ display: 'flex', alignItems: 'center', gap: 10, marginBottom: 6 }}>
                    <div style={{
                      width: 28, height: 28, borderRadius: '50%',
                      background: a.survival >= 75 ? CR.weldBg : a.survival >= 60 ? CR.emberBg : CR.flameBg,
                      color: a.survival >= 75 ? CR.weld : a.survival >= 60 ? CR.ember : CR.flame,
                      display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
                      fontSize: 12, fontWeight: 700, fontFamily: T.font.mono,
                    }}>{a.survival}</div>
                    <div style={{ flex: 1 }}>
                      <div style={{ fontSize: 12, fontWeight: 600, color: T.color.text.primary }}>{a.label}</div>
                      <div style={{ fontSize: 9.5, color: T.color.text.tertiary, marginTop: 1 }}>{a.basis}</div>
                    </div>
                    <Tag color={a.survival >= 75 ? CR.weld : a.survival >= 60 ? CR.ember : CR.flame}>
                      {a.survival >= 75 ? 'survives' : a.survival >= 60 ? 'at risk' : 'fails'}
                    </Tag>
                  </div>
                  <Bar value={a.survival} color={a.survival >= 75 ? CR.weld : a.survival >= 60 ? CR.ember : CR.flame} glow={a.survival < 60} />
                  <div style={{ fontSize: 10.5, color: T.color.text.secondary, marginTop: 6, lineHeight: 1.5 }}>
                    <b style={{ color: CR.flame }}>weakness:</b> {a.weakness}
                  </div>
                  <div style={{ fontSize: 10.5, color: T.color.text.secondary, marginTop: 3, lineHeight: 1.5 }}>
                    <b style={{ color: CR.weld }}>remediation:</b> {a.remediation}
                  </div>
                </div>
              ))}
            </div>
          </Card>
        </div>

        <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '14px' }}>
          <Card title="Steelman — strongest opposition">
            <div style={{
              fontSize: 11, color: T.color.text.secondary, lineHeight: 1.6,
              fontFamily: '"Georgia", serif',
              padding: '10px 14px', background: CR.flameBg,
              borderLeft: `3px solid ${CR.flame}`, borderRadius: 4,
            }}>
              {steel.body}
            </div>
            <div style={{ ...cr.statLabel, marginTop: 8 }}>Strongest authority for opposition</div>
            <div style={{ fontFamily: T.font.mono, fontSize: 11, color: CR.gold, marginTop: 4 }}>{steel.cite}</div>
          </Card>
          <Card title="Hostile cross-exam — top scenarios">
            {cross.map((c, i) => (
              <div key={i} style={{ padding: '8px 0',
                borderBottom: i < cross.length - 1 ? `1px solid ${T.color.border.light}` : 'none' }}>
                <div style={{ display: 'flex', alignItems: 'flex-start', gap: 8 }}>
                  <span style={{ fontSize: 11, fontWeight: 700, fontFamily: T.font.mono,
                    color: c.severity === 'critical' ? CR.flame : c.severity === 'serious' ? CR.ember : T.color.text.tertiary,
                    minWidth: 18 }}>{i + 1}.</span>
                  <div style={{ flex: 1 }}>
                    <div style={{ fontSize: 11.5, color: T.color.text.primary, lineHeight: 1.45, fontStyle: 'italic' }}>"{c.q}"</div>
                    <div style={{ fontSize: 9.5, color: T.color.text.tertiary, marginTop: 2 }}>
                      <Tag color={c.severity === 'critical' ? CR.flame : c.severity === 'serious' ? CR.ember : T.color.text.tertiary}>{c.severity}</Tag>
                      <span style={{ marginLeft: 6 }}>· {c.angle}</span>
                    </div>
                  </div>
                </div>
              </div>
            ))}
          </Card>
        </div>
      </div>
    );
  }
  function makeAttacks(arg) {
    const seed = arg.id.charCodeAt(arg.id.length - 1);
    const base = arg.strength;
    return [
      { id: 'daubert', label: 'Daubert challenge to expert reliability', basis: 'FRE 702 — methodology & fit',
        survival: clamp(base - 12 + seed % 7),
        weakness: 'Expert\'s methodology has not been peer-reviewed; relies on novel statistical inference.',
        remediation: 'Add corroborating expert and methodology citation; pre-empt Kumho Tire arguments.' },
      { id: 'mtd', label: '12(b)(6) — failure to state a claim', basis: 'Twombly/Iqbal plausibility',
        survival: clamp(base - 4 + seed % 5),
        weakness: 'Pleading conclusory on scienter element; lacks specific factual allegations.',
        remediation: 'Amend complaint to add deposition-confirmed facts; cite Tellabs particularity exception.' },
      { id: 'msj', label: 'Cross MSJ on the same record', basis: 'FRCP 56 — no genuine dispute',
        survival: clamp(base + 2 - seed % 4),
        weakness: 'Defendant\'s counter-affidavit creates triable issue on intent.',
        remediation: 'File supplemental Rule 56(d) declaration; depose affiant before MSJ briefing closes.' },
      { id: 'standing', label: 'Standing — Article III injury', basis: 'Lujan / TransUnion v. Ramirez',
        survival: clamp(base + 8 - seed % 3),
        weakness: 'Concrete injury for derivative plaintiffs is contested; Spokeo applies.',
        remediation: 'Plead specific economic injury with dollar amount; tie to consummated harm not future risk.' },
      { id: 'sanc', label: 'Discovery sanctions counterstrike', basis: 'FRCP 37(b)(2)(A)',
        survival: clamp(base - 2 + seed % 6),
        weakness: 'Two interrogatory responses were 8 days late; no protective order on file.',
        remediation: 'File nunc pro tunc supplemental responses; meet-and-confer letter to opposing counsel preemptive.' },
    ];
  }
  function clamp(v) { return Math.max(8, Math.min(98, Math.round(v))); }
  function makeCrossExam(arg) {
    return [
      { q: 'You concede that disclosure was eventually made — so where exactly is the breach?', severity: 'critical', angle: 'frames the timing question against you' },
      { q: 'Isn\'t it true that your own expert valued the transaction within the contested range three years earlier?', severity: 'critical', angle: 'expert impeachment' },
      { q: 'Can you point to a single document showing my client knew of the alternative bid before signing?', severity: 'serious', angle: 'shifts proof burden on knowledge element' },
      { q: 'Your damages calculation assumes a market that did not exist in 2024 — true?', severity: 'serious', angle: 'attacks reliability of expert methodology' },
      { q: 'Doesn\'t the entire-fairness framework only apply to controlled transactions, which this was not?', severity: 'serious', angle: 'doctrine misapplication trap' },
      { q: 'How many of the prior board approvals you cite were challenged in any court — be specific.', severity: 'moderate', angle: 'analogous-authority weakness' },
      { q: 'You learned of these emails when?', severity: 'moderate', angle: 'opens spoliation timing line' },
      { q: 'And you have no evidence of any payment back to my client personally, correct?', severity: 'moderate', angle: 'narrows breach to disclosure not enrichment' },
    ];
  }
  function makeSteelman() {
    return {
      body: 'The transaction underwent independent committee review with three disinterested directors, was disclosed in the 10-Q within the safe-harbor window, and resulted in price terms within the contemporaneous comparable range. Plaintiffs cherry-pick a single email to manufacture intent where none exists; the business judgment rule presumption is not rebutted by mere market-timing critique. Without a controlling-shareholder allegation, entire fairness review is unavailable, and Plaintiffs cannot meet the gross-negligence threshold In re Caremark imposes for oversight claims.',
      cite: 'In re Walt Disney Co. Derivative Litig., 906 A.2d 27 (Del. 2006); Aronson v. Lewis, 473 A.2d 805 (Del. 1984)',
    };
  }

  // ════════════════════════════════════════════════════════════════════════
  //  3. RECON — opposing-counsel intelligence
  // ════════════════════════════════════════════════════════════════════════
  function Recon() {
    A.useTrack?.('crucible.recon', {}, []);
    const targets = DATA.attorneys;
    const [sel, setSel] = useState(targets[0].id);
    const t = targets.find(x => x.id === sel);
    useEffect(() => { Store?.viewRecon?.(sel); }, [sel]);
    const motions = DATA.motionPlaybook;
    const judges = DATA.judges;
    const witnessVulns = DATA.witnessVulns;
    const linguistic = DATA.linguistic;
    const overallThreat = Math.round((t.winRate * 0.6 + (motions.reduce((s,m)=>s+m.rate,0)/motions.length) * 0.4));

    return (
      <div>
        <StatRow cols="repeat(5, 1fr)">
          <Stat label="Target" value={t.lead} hint={t.firm} />
          <Stat label="Threat score" value={overallThreat + '%'} color={overallThreat >= 70 ? CR.flame : overallThreat >= 55 ? CR.ember : CR.weld} hint="composite" />
          <Stat label="Career cases" value={t.cases} />
          <Stat label="Career win-rate" value={t.winRate + '%'} color={t.winRate >= 70 ? CR.flame : t.winRate >= 55 ? CR.ember : CR.weld} />
          <Stat label="Bar admitted" value={t.bar} />
        </StatRow>

        <div style={{ display: 'grid', gridTemplateColumns: '260px 1fr', gap: '14px' }}>
          <Card title="Adversary panel" pad={false}>
            {targets.map(x => (
              <div key={x.id} onClick={() => setSel(x.id)}
                style={{
                  padding: '10px 12px', display: 'flex', alignItems: 'center', gap: 10,
                  borderLeft: `3px solid ${sel === x.id ? CR.flame : 'transparent'}`,
                  background: sel === x.id ? CR.flameBg : 'transparent',
                  borderBottom: `1px solid ${T.color.border.light}`,
                  cursor: 'pointer',
                }}>
                <div style={{
                  width: 32, height: 32, borderRadius: '50%',
                  background: `linear-gradient(135deg, ${CR.flame} 0%, ${CR.flameDk} 100%)`,
                  color: '#fff', fontSize: 11, fontWeight: 700,
                  display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
                }}>{x.avatar}</div>
                <div style={{ flex: 1, minWidth: 0 }}>
                  <div style={{ fontSize: 12, fontWeight: 600, color: T.color.text.primary }}>{x.lead}</div>
                  <div style={{ fontSize: 9.5, color: T.color.text.tertiary, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>{x.firm}</div>
                </div>
                <span style={{ fontSize: 11, fontWeight: 700, fontFamily: T.font.mono,
                  color: x.winRate >= 70 ? CR.flame : T.color.text.secondary }}>{x.winRate}%</span>
              </div>
            ))}
          </Card>

          <div>
            <Card title={`Motion playbook · ${t.lead}`}
              right={<span style={{ fontSize: 10, color: T.color.text.tertiary }}>{t.style}</span>}>
              <table style={{ width: '100%', borderCollapse: 'collapse' }}>
                <thead>
                  <tr style={{ background: T.color.bg.secondary }}>
                    <th style={th}>Motion type</th>
                    <th style={{ ...th, textAlign: 'right' }}>Filed</th>
                    <th style={{ ...th, textAlign: 'right' }}>Won</th>
                    <th style={th}>Rate</th>
                    <th style={th}>Signature move</th>
                  </tr>
                </thead>
                <tbody>{motions.map(m => (
                  <tr key={m.type}>
                    <td style={td}>{m.type}</td>
                    <td style={{ ...td, textAlign: 'right', fontFamily: T.font.mono }}>{m.filed}</td>
                    <td style={{ ...td, textAlign: 'right', fontFamily: T.font.mono, color: T.color.text.secondary }}>{m.won}</td>
                    <td style={td}>
                      <div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
                        <span style={{ minWidth: 36, fontFamily: T.font.mono,
                          color: m.rate >= 70 ? CR.flame : m.rate >= 55 ? CR.ember : CR.weld,
                          fontWeight: 700 }}>{m.rate}%</span>
                        <div style={{ flex: 1, minWidth: 60 }}>
                          <Bar value={m.rate} color={m.rate >= 70 ? CR.flame : m.rate >= 55 ? CR.ember : CR.weld} />
                        </div>
                      </div>
                    </td>
                    <td style={{ ...td, fontSize: 10.5, color: T.color.text.tertiary, fontStyle: 'italic' }}>{m.signature}</td>
                  </tr>
                ))}</tbody>
              </table>
            </Card>

            <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '14px' }}>
              <Card title="Judge alignment">
                {judges.map(j => (
                  <div key={j.name} style={{ padding: '8px 0', borderBottom: `1px solid ${T.color.border.light}` }}>
                    <div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: 4 }}>
                      <span style={{ fontSize: 11.5, fontWeight: 600, color: T.color.text.primary }}>{j.name}</span>
                      <span style={{ fontSize: 11, fontWeight: 700, fontFamily: T.font.mono,
                        color: j.alignment >= 70 ? CR.flame : j.alignment >= 50 ? CR.ember : CR.weld }}>{j.alignment}%</span>
                    </div>
                    <Bar value={j.alignment} color={j.alignment >= 70 ? CR.flame : j.alignment >= 50 ? CR.ember : CR.weld} />
                    <div style={{ fontSize: 10, color: T.color.text.tertiary, marginTop: 3 }}>{j.prior} prior appearances · {j.ruling}<br />{j.note}</div>
                  </div>
                ))}
              </Card>
              <Card title="Witness vulnerability map">
                {witnessVulns.map(w => (
                  <div key={w.name} style={{ padding: '8px 0', borderBottom: `1px solid ${T.color.border.light}` }}>
                    <div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: 4 }}>
                      <div>
                        <div style={{ fontSize: 11.5, fontWeight: 600, color: T.color.text.primary }}>{w.name}</div>
                        <div style={{ fontSize: 9.5, color: T.color.text.tertiary }}>{w.role}</div>
                      </div>
                      <div style={{ textAlign: 'right' }}>
                        <span style={{ fontSize: 13, fontWeight: 700, fontFamily: T.font.mono,
                          color: w.exposure >= 70 ? CR.flame : w.exposure >= 50 ? CR.ember : CR.weld }}>{w.exposure}</span>
                        <div style={{ fontSize: 9, color: T.color.text.tertiary }}>exposure</div>
                      </div>
                    </div>
                    <Bar value={w.exposure} color={w.exposure >= 70 ? CR.flame : w.exposure >= 50 ? CR.ember : CR.weld} glow={w.exposure >= 80} />
                    <div style={{ fontSize: 10, color: T.color.text.tertiary, marginTop: 4, fontStyle: 'italic' }}>{w.basis}</div>
                  </div>
                ))}
              </Card>
            </div>

            <Card title="Linguistic fingerprint">
              <div style={{ display: 'grid', gridTemplateColumns: 'repeat(4, 1fr)', gap: 10, fontSize: 11 }}>
                {Object.entries(linguistic).map(([k, v]) => (
                  <div key={k} style={{ padding: '8px 10px', background: T.color.bg.secondary, borderRadius: 6 }}>
                    <div style={{ ...cr.statLabel }}>{k.replace(/([A-Z])/g, ' $1').trim()}</div>
                    <div style={{ fontSize: 13, fontWeight: 600, fontFamily: T.font.mono, color: T.color.text.primary, marginTop: 3 }}>{v}</div>
                  </div>
                ))}
              </div>
            </Card>
          </div>
        </div>
      </div>
    );
  }
  const th = { textAlign: 'left', padding: '8px 12px', fontSize: 9.5, fontWeight: 700,
    color: T.color.text.tertiary, textTransform: 'uppercase', letterSpacing: '0.08em',
    borderBottom: `1px solid ${T.color.border.light}` };
  const td = { padding: '8px 12px', fontSize: 11.5, color: T.color.text.primary,
    borderBottom: `1px solid ${T.color.border.light}` };

  // ════════════════════════════════════════════════════════════════════════
  //  4. AUTHORITIES — citation library
  // ════════════════════════════════════════════════════════════════════════
  function Authorities() {
    A.useTrack?.('crucible.authorities', {}, []);
    const cites = DATA.citations;
    const [filter, setFilter] = useState('all');
    const [search, setSearch] = useState('');
    const filtered = cites.filter(c =>
      (filter === 'all' || c.binding === filter) &&
      (search === '' || (c.short + c.full + c.topic).toLowerCase().includes(search.toLowerCase())));
    const goodCount = cites.filter(c => c.status === 'good').length;
    const avgStrength = Math.round(cites.reduce((s, c) => s + c.strength, 0) / cites.length);

    return (
      <div>
        <StatRow cols="repeat(4, 1fr)">
          <Stat label="Citations" value={cites.length} />
          <Stat label="Good law" value={goodCount} color={CR.weld} />
          <Stat label="Distinguishable" value={cites.filter(c => c.status === 'distinguishable').length} color={CR.ember} />
          <Stat label="Avg strength" value={avgStrength} hint="weighted by binding tier" />
        </StatRow>
        <Card title="Authority stack"
          right={<div style={{ display: 'flex', gap: 6 }}>
            {['all', 'binding', 'persuasive', 'evidentiary'].map(f =>
              <button key={f} onClick={() => setFilter(f)}
                style={filter === f ? { ...cr.filterBtn, ...cr.filterBtnActive } : cr.filterBtn}>{f}</button>)}
            <input className="arb-input" placeholder="search…" value={search} onChange={e => setSearch(e.target.value)}
              style={{ height: 24, fontSize: 11, padding: '0 8px', width: 140 }} />
          </div>}>
          {filtered.length === 0 && <Empty title="No citations match" description="Adjust filter or clear the search." />}
          <div style={{ display: 'grid', gap: 8 }}>
            {filtered.map(c => (
              <div key={c.id} style={{
                padding: '10px 14px', borderRadius: 8,
                background: T.color.bg.card, border: `1px solid ${T.color.border.light}`,
                borderLeft: `3px solid ${c.status === 'good' ? CR.weld : c.status === 'narrowed' ? CR.ember : CR.flame}`,
                display: 'grid', gridTemplateColumns: '90px 1fr 90px 90px 60px', gap: 12, alignItems: 'center',
              }}>
                <span style={{ fontSize: 11, fontWeight: 700, fontFamily: T.font.mono, color: CR.gold }}>{c.short}</span>
                <div style={{ minWidth: 0 }}>
                  <div style={{ fontSize: 11.5, color: T.color.text.primary, fontFamily: '"Georgia", serif',
                    whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>{c.full}</div>
                  <div style={{ fontSize: 9.5, color: T.color.text.tertiary, marginTop: 2 }}>{c.topic} · used {c.uses}× firm-wide</div>
                </div>
                <Tag color={c.binding === 'binding' ? CR.weld : c.binding === 'persuasive' ? CR.ember : CR.ash}>{c.binding}</Tag>
                <Tag color={c.status === 'good' ? CR.weld : c.status === 'narrowed' ? CR.ember : CR.flame}>{c.status}</Tag>
                <span style={{ fontSize: 13, fontWeight: 700, fontFamily: T.font.mono, textAlign: 'right',
                  color: c.strength >= 80 ? CR.weld : c.strength >= 60 ? CR.ember : CR.flame }}>{c.strength}</span>
              </div>
            ))}
          </div>
        </Card>
      </div>
    );
  }

  // ════════════════════════════════════════════════════════════════════════
  //  5. WARGAME — decision tree
  // ════════════════════════════════════════════════════════════════════════
  function Wargame() {
    A.useTrack?.('crucible.wargame', {}, []);
    const root = DATA.wargameTree;
    const kindColor = { weld: CR.weld, edge: CR.edge, ember: CR.ember, flame: CR.flame, ash: CR.ash };
    const computeEV = (n) =>
      n.terminal || !n.children ? n.prob * n.ev : n.prob * n.children.reduce((s, c) => s + computeEV(c), 0);
    const totalEV = root.children.reduce((s, c) => s + computeEV(c), 0);
    const fmt = (n) => n >= 0 ? '$' + (n / 1e6).toFixed(1) + 'M' : '-$' + (Math.abs(n) / 1e6).toFixed(1) + 'M';

    const Branch = ({ n, depth = 0 }) => {
      const c = kindColor[n.kind] || T.color.text.secondary;
      return (
        <div style={{ marginLeft: depth ? 24 : 0, marginTop: 8 }}>
          <div style={{
            display: 'flex', alignItems: 'center', gap: 10,
            padding: '10px 12px', borderRadius: 8,
            background: T.color.bg.card, border: `1px solid ${T.color.border.light}`,
            borderLeft: `3px solid ${c}`,
          }}>
            <div style={{ flex: 1 }}>
              <div style={{ fontSize: 12, fontWeight: 600, color: T.color.text.primary }}>{n.label}</div>
              <div style={{ fontSize: 9.5, fontFamily: T.font.mono, color: T.color.text.tertiary, marginTop: 2 }}>
                prob {(n.prob * 100).toFixed(0)}% · EV {fmt(n.ev)}{n.terminal && ' · terminal'}
              </div>
            </div>
            <div style={{ width: 100 }}><Bar value={n.prob * 100} max={100} color={c} /></div>
            <span style={{ fontSize: 12, fontWeight: 700, fontFamily: T.font.mono,
              color: n.ev >= 0 ? CR.weld : CR.flame, minWidth: 90, textAlign: 'right' }}>{fmt(n.ev)}</span>
          </div>
          {n.children && n.children.map((c2, i) => <Branch key={i} n={c2} depth={depth + 1} />)}
        </div>
      );
    };

    return (
      <div>
        <StatRow cols="repeat(5, 1fr)">
          <Stat label="Decision" value="MSJ on Count I" />
          <Stat label="Expected value" value={fmt(totalEV)} color={totalEV >= 0 ? CR.weld : CR.flame} />
          <Stat label="Most-likely" value="Denied" hint="36% probability" color={CR.flame} />
          <Stat label="Best case" value={fmt(14200000)} color={CR.weld} />
          <Stat label="Worst case" value={fmt(-2400000)} color={CR.flame} />
        </StatRow>
        <Card title="Decision tree — probability-weighted outcomes">
          <Branch n={root} />
        </Card>
        <Card title="Recommendation">
          <div style={{ fontSize: 12, lineHeight: 1.6, color: T.color.text.secondary }}>
            <b style={{ color: CR.weld }}>FILE THE MSJ.</b> Weighted EV is positive ({fmt(totalEV)}); even the denial branch carries a 75% probability of acceptable settlement-or-better outcomes. The tail risk (trial after denial, –$2.4M) is bounded by the 9% joint probability. Recommend pre-emptive settlement outreach within 14 days of MSJ ruling regardless of direction.
          </div>
        </Card>
      </div>
    );
  }

  // ════════════════════════════════════════════════════════════════════════
  //  6. DIALECTIC — transcript analyzer
  // ════════════════════════════════════════════════════════════════════════
  function Dialectic() {
    A.useTrack?.('crucible.dialectic', {}, []);
    const [text, setText] = useState(DATA.transcript);
    const stats = analyzeTranscript(text);
    return (
      <div>
        <StatRow cols="repeat(5, 1fr)">
          <Stat label="Words" value={stats.words} />
          <Stat label="Sentences" value={stats.sentences} hint={`avg ${stats.avgSentenceLen}w`} />
          <Stat label="Filler words" value={stats.fillers} color={stats.fillers > 8 ? CR.flame : CR.weld} hint="um · uh · like · basically" />
          <Stat label="Citation density" value={stats.citeRate + '/100w'} color={stats.citeRate >= 1.5 ? CR.weld : CR.ember} />
          <Stat label="Hedging" value={stats.hedge} color={stats.hedge >= 6 ? CR.flame : CR.weld} hint="might · could · perhaps" />
        </StatRow>
        <div style={{ display: 'grid', gridTemplateColumns: '1fr 320px', gap: '14px' }}>
          <Card title="Transcript">
            <textarea value={text} onChange={e => setText(e.target.value)}
              style={{
                width: '100%', minHeight: 300, padding: 14, boxSizing: 'border-box',
                fontFamily: '"Georgia", serif', fontSize: 12.5, lineHeight: 1.6,
                border: `1px solid ${T.color.border.medium}`, borderRadius: 6,
                background: T.color.bg.card, color: T.color.text.primary, outline: 'none',
              }} />
          </Card>
          <Card title="Findings">
            {stats.flags.map((f, i) => (
              <div key={i} style={{
                padding: '8px 10px', marginBottom: 6, borderRadius: 6,
                background: f.kind === 'warn' ? CR.flameBg : f.kind === 'tip' ? CR.emberBg : CR.weldBg,
                borderLeft: `3px solid ${f.kind === 'warn' ? CR.flame : f.kind === 'tip' ? CR.ember : CR.weld}`,
              }}>
                <div style={{ ...cr.statLabel,
                  color: f.kind === 'warn' ? CR.flame : f.kind === 'tip' ? CR.ember : CR.weld }}>{f.kind}</div>
                <div style={{ fontSize: 11.5, color: T.color.text.primary, marginTop: 2 }}>{f.msg}</div>
              </div>
            ))}
          </Card>
        </div>
      </div>
    );
  }
  function analyzeTranscript(text) {
    const words = (text.match(/\b[\w']+\b/g) || []).length;
    const sentences = (text.match(/[.!?]+(?=\s|$)/g) || []).length || 1;
    const avgSentenceLen = Math.round(words / sentences * 10) / 10;
    const fillers = (text.match(/\b(um|uh|like|basically|you know|kind of|sort of)\b/gi) || []).length;
    const citations = (text.match(/\b(v\.|§|F\.|U\.S\.|A\.\d|F\.\dd)\b/g) || []).length;
    const citeRate = Math.round((citations / Math.max(1, words)) * 100 * 10) / 10;
    const hedge = (text.match(/\b(might|could|perhaps|possibly|arguably|seems|appears)\b/gi) || []).length;
    const flags = [];
    if (fillers > 8) flags.push({ kind: 'warn', msg: `${fillers} filler words detected. Practice the cold open without crutch words.` });
    else flags.push({ kind: 'good', msg: `Filler words at ${fillers} — within the under-8 target.` });
    if (citeRate < 1.5) flags.push({ kind: 'tip', msg: 'Citation density is below 1.5 per 100 words. Consider adding two more controlling citations.' });
    else flags.push({ kind: 'good', msg: `Citation density (${citeRate}/100w) meets benchmark.` });
    if (hedge >= 6) flags.push({ kind: 'warn', msg: `${hedge} hedging words. Replace "we believe" with "the record establishes".` });
    if (avgSentenceLen > 28) flags.push({ kind: 'tip', msg: `Average sentence length (${avgSentenceLen}w) is long. Break long sentences for emphasis.` });
    return { words, sentences, avgSentenceLen, fillers, citeRate, hedge, flags };
  }

  // ════════════════════════════════════════════════════════════════════════
  //  7. BRIEF BANK — argument templates
  // ════════════════════════════════════════════════════════════════════════
  function BriefBank() {
    A.useTrack?.('crucible.briefbank', {}, []);
    const briefs = DATA.briefs;
    return (
      <div>
        <StatRow cols="repeat(3, 1fr)">
          <Stat label="Templates" value={briefs.length} />
          <Stat label="Aggregate uses" value={briefs.reduce((s, b) => s + b.uses, 0)} />
          <Stat label="Avg win rate" value={Math.round(briefs.reduce((s, b) => s + b.wr, 0) / briefs.length) + '%'} color={CR.weld} />
        </StatRow>
        <Card title="Argument templates from prior wins">
          <div style={{ display: 'grid', gap: 8 }}>
            {briefs.map(b => (
              <div key={b.id} style={{
                padding: '12px 14px', borderRadius: 8,
                background: T.color.bg.card, border: `1px solid ${T.color.border.light}`,
                display: 'grid', gridTemplateColumns: '1fr 80px 80px 80px 80px 90px',
                gap: 14, alignItems: 'center',
              }}>
                <div>
                  <div style={{ fontSize: 12.5, fontWeight: 600, color: T.color.text.primary }}>{b.name}</div>
                  <div style={{ fontSize: 9.5, color: T.color.text.tertiary, marginTop: 2 }}>{b.jurisdictions} · {b.judges} judges seen</div>
                </div>
                <Field label="Uses" value={b.uses} />
                <Field label="Wins" value={b.wins} />
                <Field label="Win rate" value={b.wr + '%'} color={b.wr >= 70 ? CR.weld : b.wr >= 55 ? CR.ember : CR.flame} />
                <Bar value={b.wr} color={b.wr >= 70 ? CR.weld : b.wr >= 55 ? CR.ember : CR.flame} />
                <button style={cr.btnPrimary} onClick={() => { Store?.forkBrief?.(b.id); A.toast?.({ kind: 'active', title: 'Forked', message: b.name }); }}>Fork →</button>
              </div>
            ))}
          </div>
        </Card>
      </div>
    );
  }
  const Field = ({ label, value, color }) => (
    <div style={{ textAlign: 'center' }}>
      <div style={cr.statLabel}>{label}</div>
      <div style={{ fontSize: 14, fontWeight: 700, fontFamily: T.font.mono, color: color || T.color.text.primary, marginTop: 2 }}>{value}</div>
    </div>
  );

  // ════════════════════════════════════════════════════════════════════════
  //  8. TELEMETRY — outcome analytics
  // ════════════════════════════════════════════════════════════════════════
  function Telemetry() {
    A.useTrack?.('crucible.telemetry', {}, []);
    const { byType, trend, authors } = DATA.telemetry;
    return (
      <div>
        <StatRow cols="repeat(4, 1fr)">
          <Stat label="Firm win rate" value="72%" color={CR.weld} hint="trailing 12mo" />
          <Stat label="Arguments filed" value={byType.reduce((s, t) => s + t.total, 0)} />
          <Stat label="Best argument type" value="Daubert" hint="75% — n=24" color={CR.weld} />
          <Stat label="Hardest type" value="Class Cert" hint="63% — n=19" color={CR.ember} />
        </StatRow>
        <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '14px' }}>
          <Card title="Win rate by argument type">
            {byType.map(t => (
              <div key={t.type} style={{ display: 'grid', gridTemplateColumns: '90px 1fr 60px', gap: 10, alignItems: 'center', padding: '8px 0', borderBottom: `1px solid ${T.color.border.light}` }}>
                <span style={{ fontSize: 11.5, fontWeight: 600, color: T.color.text.primary }}>{t.type}</span>
                <Bar value={t.rate} color={t.rate >= 70 ? CR.weld : t.rate >= 55 ? CR.ember : CR.flame} />
                <span style={{ fontSize: 13, fontWeight: 700, fontFamily: T.font.mono, textAlign: 'right',
                  color: t.rate >= 70 ? CR.weld : t.rate >= 55 ? CR.ember : CR.flame }}>{t.rate}%</span>
              </div>
            ))}
          </Card>
          <Card title="Win rate by author">
            {authors.map(a => (
              <div key={a.name} style={{ display: 'grid', gridTemplateColumns: '120px 1fr 60px', gap: 10, alignItems: 'center', padding: '8px 0', borderBottom: `1px solid ${T.color.border.light}` }}>
                <div>
                  <div style={{ fontSize: 11.5, fontWeight: 600, color: T.color.text.primary }}>{a.name}</div>
                  <div style={{ fontSize: 9.5, fontFamily: T.font.mono, color: T.color.text.tertiary }}>{a.wins} of {a.total}</div>
                </div>
                <Bar value={a.rate} color={a.rate >= 70 ? CR.weld : a.rate >= 55 ? CR.ember : CR.flame} />
                <span style={{ fontSize: 13, fontWeight: 700, fontFamily: T.font.mono, textAlign: 'right',
                  color: a.rate >= 70 ? CR.weld : a.rate >= 55 ? CR.ember : CR.flame }}>{a.rate}%</span>
              </div>
            ))}
          </Card>
        </div>
        <Card title="Win-rate trend · 12-month rolling">
          <svg viewBox="0 0 600 100" width="100%" height="100" aria-label="Win rate trend">
            <defs>
              <linearGradient id="cr-trend-g" x1="0" x2="0" y1="0" y2="1">
                <stop offset="0%" stopColor={CR.weld} stopOpacity="0.30" />
                <stop offset="100%" stopColor={CR.weld} stopOpacity="0" />
              </linearGradient>
            </defs>
            {(() => {
              const w = 600, h = 100, pad = 8;
              const min = Math.min(...trend), max = Math.max(...trend);
              const sx = (i) => pad + (i / (trend.length - 1)) * (w - pad * 2);
              const sy = (v) => h - pad - ((v - min) / (max - min)) * (h - pad * 2);
              const d = trend.map((v, i) => `${i === 0 ? 'M' : 'L'}${sx(i).toFixed(1)},${sy(v).toFixed(1)}`).join(' ');
              const a = `${d} L${sx(trend.length-1)},${h - pad} L${sx(0)},${h - pad} Z`;
              return <>
                <path d={a} fill="url(#cr-trend-g)" />
                <path d={d} stroke={CR.weld} strokeWidth="2.5" fill="none" strokeLinecap="round" strokeLinejoin="round" />
                {trend.map((v, i) => <circle key={i} cx={sx(i)} cy={sy(v)} r={i === trend.length-1 ? 4 : 2} fill={CR.weld} />)}
                {trend.map((v, i) => i % 2 === 0 && <text key={'l'+i} x={sx(i)} y={sy(v) - 8} fontSize="9" textAnchor="middle" fill={T.color.text.tertiary} fontFamily={T.font.mono}>{v}%</text>)}
              </>;
            })()}
          </svg>
        </Card>
      </div>
    );
  }

  // ════════════════════════════════════════════════════════════════════════
  //  Root — CruciblePlatform
  // ════════════════════════════════════════════════════════════════════════
  const TABS = [
    { id: 'workbench',   label: 'Workbench',   sub: 'Argument graph',          render: () => <Workbench /> },
    { id: 'adversary',   label: 'Adversary',   sub: 'Red-team simulator',      render: () => <Adversary /> },
    { id: 'recon',       label: 'Recon',       sub: 'Opposing-counsel intel',  render: () => <Recon /> },
    { id: 'authorities', label: 'Authorities', sub: 'Citation library',        render: () => <Authorities /> },
    { id: 'wargame',     label: 'Wargame',     sub: 'Decision tree',           render: () => <Wargame /> },
    { id: 'dialectic',   label: 'Dialectic',   sub: 'Transcript analyzer',     render: () => <Dialectic /> },
    { id: 'briefbank',   label: 'Brief Bank',  sub: 'Templates from wins',     render: () => <BriefBank /> },
    { id: 'telemetry',   label: 'Telemetry',   sub: 'Outcome analytics',       render: () => <Telemetry /> },
  ];

  function CruciblePlatform() {
    const [activeTab, setActiveTab] = useState('workbench');
    A.useTrack?.('crucible.open', {}, []);

    // Listen for crucible-tab deep-link events from command palette
    useEffect(() => {
      const onTab = (e) => { if (e.detail?.tab) setActiveTab(e.detail.tab); };
      window.addEventListener('arbiter:crucible-tab', onTab);
      return () => window.removeEventListener('arbiter:crucible-tab', onTab);
    }, []);

    const tab = TABS.find(t => t.id === activeTab);
    return (
      <div style={cr.container}>
        <div style={cr.header}>
          <div style={cr.headerTitle}>
            <div style={cr.crIcon}>
              {Icons.Sparkle
                ? <Icons.Sparkle size={16} color="#fff" strokeWidth={1.75} />
                : <span style={{ fontSize: '14px', fontWeight: 700 }}>◈</span>}
            </div>
            <div>
              <div style={cr.title}>Crucible</div>
              <div style={cr.subtitle}>The Argument Forge · Build · Attack · Harden · File</div>
            </div>
          </div>
          <div style={{ display: 'flex', alignItems: 'center', gap: '12px' }}>
            <div style={cr.pill}>
              <span style={cr.pillLabel}>Active</span>
              <span style={cr.pillValue}>14</span>
            </div>
            <div style={{ ...cr.pill, background: CR.weldBg, border: `1px solid ${CR.weld}33` }}>
              <span style={{ ...cr.pillLabel, color: CR.weld }}>Battle-ready</span>
              <span style={{ ...cr.pillValue, color: CR.weld }}>9</span>
            </div>
            <div style={{ ...cr.pill, background: CR.emberBg, border: `1px solid ${CR.ember}33` }}>
              <span style={{ ...cr.pillLabel, color: CR.ember }}>Hardening</span>
              <span style={{ ...cr.pillValue, color: CR.ember }}>3</span>
            </div>
            <div style={{ ...cr.pill, background: CR.flameBg, border: `1px solid ${CR.flame}33` }}>
              <span style={{ ...cr.pillLabel, color: CR.flame }}>At risk</span>
              <span style={{ ...cr.pillValue, color: CR.flame }}>2</span>
            </div>
            <button style={cr.btnPrimary}>+ New argument</button>
          </div>
        </div>

        {/* Flat tabs — matches Risk / Calendar / Nexus */}
        <div style={{ ...cr.tabs, overflowX: 'auto' }}>
          {TABS.map(t => {
            const isActive = activeTab === t.id;
            return (
              <button key={t.id}
                onClick={() => setActiveTab(t.id)}
                title={t.sub}
                style={{ ...cr.tab, ...(isActive ? cr.tabActive : {}), whiteSpace: 'nowrap' }}>
                {t.label}
              </button>
            );
          })}
        </div>

        <div style={cr.body}>{tab.render()}</div>
      </div>
    );
  }

  window.CruciblePlatform = CruciblePlatform;

  // ── Command palette: nav.crucible + per-tab deep links ───────────────────
  if (window.Arbiter?.CommandRegistry) {
    const R = window.Arbiter.CommandRegistry;
    R.register('nav.crucible', {
      label: 'Go to Crucible — argument forge',
      group: 'Navigation', keywords: 'crucible argument adversary recon',
      run: () => window.dispatchEvent(new CustomEvent('arbiter:navigate', { detail: { view: 'crucible' } })),
    });
    TABS.forEach(t => {
      R.register(`crucible.${t.id}`, {
        label: `Crucible: ${t.label}`,
        group: 'Crucible',
        keywords: `crucible ${t.id} ${t.sub}`,
        run: () => {
          window.dispatchEvent(new CustomEvent('arbiter:navigate', { detail: { view: 'crucible' } }));
          // small delay to let the view mount before the tab event fires
          setTimeout(() => window.dispatchEvent(new CustomEvent('arbiter:crucible-tab', { detail: { tab: t.id } })), 50);
        },
      });
    });
  }
})();
