// SyntheticDomainsPanel.jsx · R49' LEM-v3 PR-2 · 2026-05-28
//
// Self-mounting widget for ?screen=synthetic-domains&workspace_id=<id>.
// Operator surface for managing synthetic domains (cross-cutting derived
// project memberships) + viewing their members.
//
//   ┌──────────────────────────────────────────────────────────────────────┐
//   │  ← cockpit              SYNTHETIC DOMAINS · org_3EIO…                │
//   │                                                                      │
//   │  ┌────────────────┐  ┌──────────────────────────────────────────────┐│
//   │  │ Data Room      │  │ Data Room Readiness                          ││
//   │  │   ⛓ active     │  │ 6 member projects · binding v3 · 2026-05-28  ││
//   │  │   6 members    │  │                                              ││
//   │  ├────────────────┤  │ Binding · combine ANY ·                      ││
//   │  │ Investor Wave  │  │   workspace_id_in: x-biz                     ││
//   │  │   ⛓ operator   │  │   status_in: active                          ││
//   │  │   12 members   │  │ [Edit binding] [Refresh] [Archive]           ││
//   │  ├────────────────┤  │                                              ││
//   │  │ + New domain   │  │ Members                                      ││
//   │  └────────────────┘  │   x-biz-investor-readiness · Investor Ready  ││
//   │                       │   x-biz-data-room · Data Room                ││
//   │                       │   x-biz-commercial-claims · Commercial …    ││
//   │                       └──────────────────────────────────────────────┘│
//   └──────────────────────────────────────────────────────────────────────┘
//
// Role gate: owner/operator can create + edit binding + archive + refresh.
// Member role sees read-only.

(function () {
  'use strict';

  if (typeof window === 'undefined') return;
  if (window.__SyntheticDomainsPanelMounted) return;
  window.__SyntheticDomainsPanelMounted = true;

  var React = window.React;
  var ReactDOM = window.ReactDOM;
  if (!React || !ReactDOM) {
    window.addEventListener('xcp:data-ready', mount, { once: true });
    return;
  }
  mount();

  function mount() {
    var container = document.createElement('div');
    container.id = 'xcp-synthetic-domains-portal';
    container.style.cssText = 'position:fixed;inset:0;z-index:99988;pointer-events:none;';
    document.body.appendChild(container);
    var root = ReactDOM.createRoot ? ReactDOM.createRoot(container) : null;
    if (!root) { try { ReactDOM.render(React.createElement(Gate), container); } catch (e) { void e; } return; }
    root.render(React.createElement(Gate));
  }

  function Gate() {
    var st = useState(parseUrl());
    var url = st[0]; var setUrl = st[1];
    useEffect(function () {
      function onChange() { setUrl(parseUrl()); }
      window.addEventListener('popstate', onChange);
      var iv = setInterval(onChange, 1500);
      return function () { window.removeEventListener('popstate', onChange); clearInterval(iv); };
    }, []);
    if (url.screen !== 'synthetic-domains') return null;
    if (!url.workspaceId) return el(EmptyState);
    return el(SyntheticDomainsView, { workspaceId: url.workspaceId, key: url.workspaceId });
  }

  function parseUrl() {
    try {
      var u = new URL(window.location.href);
      return {
        screen: u.searchParams.get('screen'),
        workspaceId: u.searchParams.get('workspace_id') || u.searchParams.get('ws'),
      };
    } catch (_) { return {}; }
  }

  function EmptyState() {
    return el('div', { style: emptyStyle() },
      el('div', { style: { maxWidth: 560, textAlign: 'center', padding: 32 } },
        el('h1', { style: titleH1Style() }, 'Synthetic domains'),
        el('p', null, 'Missing ', el('code', null, 'workspace_id'), ' query param.'),
        el('p', { style: { opacity: 0.5, fontSize: 13 } },
          'Try: ', el('code', null, '?screen=synthetic-domains&workspace_id=x-biz')),
        backLink(),
      ),
    );
  }

  // ──────────────────────────────────────────────────────────────────────
  // Main view
  // ──────────────────────────────────────────────────────────────────────
  function SyntheticDomainsView(props) {
    var workspaceId = props.workspaceId;
    var [domains, setDomains] = useState([]);
    var [crossDomains, setCrossDomains] = useState([]);
    var [selectedId, setSelectedId] = useState(null);
    var [loading, setLoading] = useState(true);
    var [error, setError] = useState(null);
    var [showCreate, setShowCreate] = useState(false);

    var session = window.xcp && window.xcp.store && window.xcp.store.useSession
      ? window.xcp.store.useSession() : null;
    var role = session && session.role ? String(session.role).toLowerCase() : null;
    var canEdit = role === 'owner' || role === 'operator';
    var isOperator = role === 'owner' || role === 'operator';

    function loadAll() {
      setLoading(true);
      Promise.all([
        fetchDomains(workspaceId),
        isOperator ? fetchDomains('__cross__').catch(function () { return []; }) : Promise.resolve([]),
      ]).then(function (r) {
        setDomains(r[0]);
        setCrossDomains(r[1]);
        setLoading(false);
        if (!selectedId && r[0].length > 0) setSelectedId(r[0][0].id);
      }).catch(function (e) {
        setError(e && e.message ? e.message : String(e));
        setLoading(false);
      });
    }

    useEffect(function () { loadAll(); }, [workspaceId]);

    var allDomains = domains.concat(crossDomains);
    var selected = allDomains.find(function (d) { return d.id === selectedId; }) || null;

    return el('div', { style: shellStyle() },
      el('div', { style: topBarStyle() },
        backLink(),
        el('span', { style: { flex: 1 } }),
        el('span', { style: { opacity: 0.5, fontSize: 12 } },
          'SYNTHETIC DOMAINS · ' + workspaceId.slice(0, 16) + (workspaceId.length > 16 ? '…' : '')),
      ),
      el('div', { style: bodyStyle() },
        el('div', { style: { marginBottom: 18 } },
          el('div', { style: { fontSize: 11, opacity: 0.5, fontFamily: 'JetBrains Mono, ui-monospace, monospace', letterSpacing: '0.06em', textTransform: 'uppercase' } },
            'LIVE DB · /api/v1/synthetic-domains'),
          el('h1', { style: titleH1Style() }, 'Synthetic domains'),
          el('div', { style: { opacity: 0.55, fontSize: 13, marginTop: 4 } },
            domains.length + ' workspace domain' + (domains.length === 1 ? '' : 's') +
            (isOperator ? ' · ' + crossDomains.length + ' cross-workspace (operator)' : '') +
            ' · role: ' + (role || '—')),
        ),
        loading
          ? el('div', { style: { padding: 40, opacity: 0.5 } }, 'Loading synthetic domains…')
          : error
            ? el('div', { style: errStyle() }, error)
            : el('div', { style: layoutStyle() },
                el(DomainList, {
                  domains: domains, crossDomains: crossDomains,
                  selectedId: selectedId, onSelect: setSelectedId,
                  canEdit: canEdit, onNew: function () { setShowCreate(true); },
                }),
                el('div', { style: detailColStyle() },
                  selected
                    ? el(DomainDetail, {
                        domain: selected, canEdit: canEdit,
                        onRefresh: function () { loadAll(); },
                        onArchive: function () { loadAll(); setSelectedId(null); },
                      })
                    : el('div', { style: { opacity: 0.5, padding: 24 } }, 'Select a domain on the left.'),
                ),
              ),
        showCreate
          ? el(CreateDomainModal, {
              workspaceId: workspaceId, isOperator: isOperator,
              onClose: function () { setShowCreate(false); },
              onCreated: function () { setShowCreate(false); loadAll(); },
            })
          : null,
        el('div', { style: footerStyle() },
          'R49 LEM-v3 PR-2 · live-DB synthetic-domains panel · PR-3+ adds roadmap + goals + recommendations'),
      ),
    );
  }

  // ──────────────────────────────────────────────────────────────────────
  // Left rail · domain list
  // ──────────────────────────────────────────────────────────────────────
  function DomainList(props) {
    return el('div', { style: leftRailStyle() },
      el('div', { style: railSectionHeaderStyle() }, 'Workspace domains'),
      props.domains.length === 0
        ? el('div', { style: { padding: '8px 4px', opacity: 0.5, fontSize: 12 } }, 'None yet')
        : props.domains.map(function (d) { return el(DomainListItem, { key: d.id, domain: d, selected: d.id === props.selectedId, onSelect: props.onSelect }); }),
      props.crossDomains.length > 0
        ? el(React.Fragment, null,
            el('div', { style: Object.assign({}, railSectionHeaderStyle(), { marginTop: 14 }) }, 'Cross-workspace (operator-only)'),
            props.crossDomains.map(function (d) { return el(DomainListItem, { key: d.id, domain: d, selected: d.id === props.selectedId, onSelect: props.onSelect }); }))
        : null,
      props.canEdit
        ? el('button', { type: 'button', onClick: props.onNew, style: Object.assign({}, btn('secondary'), { width: '100%', marginTop: 14 }) }, '+ New domain')
        : null,
    );
  }

  function DomainListItem(props) {
    var d = props.domain;
    return el('div', {
      style: domainItemStyle(props.selected),
      onClick: function () { props.onSelect(d.id); },
      role: 'button', tabIndex: 0,
      onKeyDown: function (e) { if (e.key === 'Enter') props.onSelect(d.id); },
    },
      el('div', { style: { fontSize: 13, fontWeight: 600 } }, d.label),
      el('div', { style: { fontSize: 11, opacity: 0.55, marginTop: 3 } },
        d.slug + ' · binding v' + d.binding_version),
      el('div', { style: { display: 'flex', gap: 6, marginTop: 6, alignItems: 'center' } },
        el('span', { style: visBadge(d.visibility) }, d.visibility),
        el('span', { style: { fontSize: 10, opacity: 0.5 } }, d.status),
      ),
    );
  }

  // ──────────────────────────────────────────────────────────────────────
  // Center · domain detail (binding + members + actions)
  // ──────────────────────────────────────────────────────────────────────
  function DomainDetail(props) {
    var d = props.domain;
    var [tab, setTab] = useState('overview'); // 'overview' | 'roadmap' | 'goals'
    var [editing, setEditing] = useState(false);
    var [draft, setDraft] = useState(null);
    var [members, setMembers] = useState([]);
    var [loadingMembers, setLoadingMembers] = useState(true);
    var [saving, setSaving] = useState(false);
    var [busy, setBusy] = useState(false);
    var [err, setErr] = useState(null);

    useEffect(function () {
      setLoadingMembers(true); setErr(null);
      fetchMembers(d.id).then(function (rows) { setMembers(rows); setLoadingMembers(false); })
        .catch(function (e) { setErr(e.message); setLoadingMembers(false); });
    }, [d.id, d.binding_version]);

    function startEdit() { setDraft(cloneBinding(d.binding)); setEditing(true); }
    function cancelEdit() { setEditing(false); setDraft(null); }
    function saveBinding() {
      setSaving(true);
      patchBinding(d.id, sanitize(draft))
        .then(function () { setSaving(false); setEditing(false); setDraft(null); props.onRefresh(); })
        .catch(function (e) { setSaving(false); setErr(e.message); });
    }
    function refresh() {
      setBusy(true);
      postRefresh(d.id).then(function () { setBusy(false); props.onRefresh(); })
        .catch(function (e) { setBusy(false); setErr(e.message); });
    }
    function archive() {
      if (!window.confirm('Archive synthetic domain "' + d.label + '"? Membership rows preserved; binding intact.')) return;
      setBusy(true);
      postArchive(d.id).then(function () { setBusy(false); props.onArchive(); })
        .catch(function (e) { setBusy(false); setErr(e.message); });
    }

    return el('div', { style: { padding: '4px 8px' } },
      el('div', { style: { marginBottom: 14 } },
        el('h2', { style: { fontSize: 22, margin: '0 0 4px' } }, d.label),
        el('div', { style: { opacity: 0.55, fontSize: 12, fontFamily: 'JetBrains Mono, ui-monospace, monospace' } },
          d.id + ' · slug ' + d.slug + ' · binding v' + d.binding_version),
        d.description ? el('p', { style: { opacity: 0.75, marginTop: 8 } }, d.description) : null,
      ),
      err ? el('div', { style: errStyle() }, err) : null,
      // Tab bar
      el('div', { style: tabBarStyle() },
        tabButton('overview', 'Overview · binding + members', tab, setTab),
        tabButton('roadmap', 'Roadmap' + (d.has_roadmap ? ' · ✓' : ''), tab, setTab),
        tabButton('goals', 'Goals' + (d.goal_count > 0 ? ' · ' + d.goal_count : ''), tab, setTab),
      ),
      tab === 'overview'
        ? el(React.Fragment, null,
            // Binding card
            el('div', { style: cardStyle() },
              el('div', { style: cardHeaderStyle() },
                el('span', { style: cardTitleStyle() }, 'Binding'),
                el('span', { style: { opacity: 0.55, fontSize: 12 } },
                  'combine ' + d.binding.combine + ' · ' + d.binding.filters.length + ' filter' + (d.binding.filters.length === 1 ? '' : 's')),
              ),
              editing
                ? el(BindingEditor, { draft: draft, setDraft: setDraft, onSave: saveBinding, onCancel: cancelEdit, saving: saving })
                : el(BindingReadOnly, { binding: d.binding, canEdit: props.canEdit, onEdit: startEdit, onRefresh: refresh, onArchive: archive, busy: busy }),
            ),
            // Members card
            el('div', { style: cardStyle() },
              el('div', { style: cardHeaderStyle() },
                el('span', { style: cardTitleStyle() }, 'Members'),
                el('span', { style: { fontSize: 11, opacity: 0.5 } }, members.length + ' project' + (members.length === 1 ? '' : 's')),
              ),
              loadingMembers
                ? el('div', { style: { padding: 8, opacity: 0.5, fontSize: 12 } }, 'Loading members…')
                : members.length === 0
                  ? el('div', { style: { padding: 8, opacity: 0.55, fontSize: 13 } }, 'No matching projects. Edit binding to broaden.')
                  : el('div', null,
                      members.slice(0, 100).map(function (p) {
                        return el('div', { key: p.id, style: memberRowStyle() },
                          el('div', { style: { fontSize: 13, fontWeight: 500 } }, p.name),
                          el('div', { style: { fontSize: 11, opacity: 0.55, fontFamily: 'JetBrains Mono, ui-monospace, monospace' } },
                            p.id + ' · ws ' + p.workspace_id + ' · ' + p.status),
                          p.description ? el('div', { style: { fontSize: 12, opacity: 0.65, marginTop: 3 } }, p.description) : null,
                        );
                      })),
            ),
            // Status counters
            el('div', { style: futureCardStyle() },
              el('div', { style: { fontSize: 11, opacity: 0.55, fontFamily: 'JetBrains Mono, ui-monospace, monospace', textTransform: 'uppercase', letterSpacing: '0.06em', marginBottom: 6 } },
                'Planning surface · LEM-v3'),
              el('div', { style: { fontSize: 12, opacity: 0.7 } },
                'Roadmap (' + (d.has_roadmap ? '✓ exists' : 'none yet') + ') · ',
                'Goals (' + d.goal_count + (d.goal_count === 0 ? ' · open to create' : ' active') + ') · ',
                'Open recommendations (' + d.open_recommendation_count + ' · ' + (d.open_recommendation_count === 0 ? 'PR-6' : 'live') + ')'),
            ),
          )
        : tab === 'roadmap'
          ? el(RoadmapPanel, { domain: d, canEdit: props.canEdit, onRefresh: props.onRefresh })
          : el(GoalsPanel, { domain: d, canEdit: props.canEdit, onRefresh: props.onRefresh }),
    );
  }

  // ──────────────────────────────────────────────────────────────────────
  // RoadmapPanel · roadmap list + items + create/edit/reorder (PR-4)
  // ──────────────────────────────────────────────────────────────────────
  function RoadmapPanel(props) {
    var d = props.domain;
    var [roadmaps, setRoadmaps] = useState([]);
    var [selectedRoadmapId, setSelectedRoadmapId] = useState(null);
    var [roadmapDetail, setRoadmapDetail] = useState(null); // { roadmap, items }
    var [loading, setLoading] = useState(true);
    var [showCreate, setShowCreate] = useState(false);
    var [err, setErr] = useState(null);

    function loadList() {
      setLoading(true); setErr(null);
      fetchRoadmaps(d.id).then(function (rows) {
        setRoadmaps(rows); setLoading(false);
        if (!selectedRoadmapId && rows.length > 0) setSelectedRoadmapId(rows[0].id);
      }).catch(function (e) { setErr(e.message); setLoading(false); });
    }
    useEffect(loadList, [d.id]);
    useEffect(function () {
      if (!selectedRoadmapId) { setRoadmapDetail(null); return; }
      fetchRoadmapDetail(selectedRoadmapId).then(function (r) { setRoadmapDetail(r); })
        .catch(function (e) { setErr(e.message); });
    }, [selectedRoadmapId]);

    return el('div', null,
      err ? el('div', { style: errStyle() }, err) : null,
      el('div', { style: cardStyle() },
        el('div', { style: cardHeaderStyle() },
          el('span', { style: cardTitleStyle() }, 'Roadmaps'),
          el('span', { style: { opacity: 0.5, fontSize: 11 } }, roadmaps.length + ' total'),
          props.canEdit
            ? el('button', { type: 'button', onClick: function () { setShowCreate(true); }, style: Object.assign({}, btn('secondary'), { marginLeft: 'auto' }) }, '+ New roadmap')
            : null,
        ),
        loading
          ? el('div', { style: { padding: 8, opacity: 0.5, fontSize: 12 } }, 'Loading roadmaps…')
          : roadmaps.length === 0
            ? el('div', { style: { padding: 8, opacity: 0.55, fontSize: 13 } }, 'No roadmaps yet. ' + (props.canEdit ? 'Click +New roadmap to seed one.' : 'Contact owner/operator to create.'))
            : el('div', { style: { display: 'flex', gap: 6, flexWrap: 'wrap', marginBottom: 8 } },
                roadmaps.map(function (r) {
                  return el('button', {
                    key: r.id, type: 'button',
                    onClick: function () { setSelectedRoadmapId(r.id); },
                    style: Object.assign({},
                      r.id === selectedRoadmapId ? roadmapTabActive() : roadmapTabIdle()),
                  },
                    el('span', { style: { fontWeight: 600, marginRight: 6 } }, r.title),
                    el('span', { style: { opacity: 0.55, fontSize: 10 } }, r.status));
                })),
        showCreate
          ? el(CreateRoadmapInline, {
              domainId: d.id,
              onClose: function () { setShowCreate(false); },
              onCreated: function () { setShowCreate(false); loadList(); props.onRefresh(); },
            })
          : null,
      ),
      roadmapDetail
        ? el('div', { style: cardStyle() },
            el('div', { style: cardHeaderStyle() },
              el('span', { style: cardTitleStyle() }, roadmapDetail.roadmap.title),
              el('span', { style: { opacity: 0.55, fontSize: 11 } },
                'v' + roadmapDetail.roadmap.version + ' · ' + roadmapDetail.roadmap.status +
                (roadmapDetail.roadmap.target_date ? ' · target ' + roadmapDetail.roadmap.target_date : '')),
            ),
            roadmapDetail.roadmap.description
              ? el('p', { style: { opacity: 0.7, fontSize: 13, marginBottom: 12 } }, roadmapDetail.roadmap.description)
              : null,
            el(RoadmapItemsList, {
              roadmapId: roadmapDetail.roadmap.id,
              items: roadmapDetail.items,
              canEdit: props.canEdit,
              onChanged: function () {
                fetchRoadmapDetail(roadmapDetail.roadmap.id).then(setRoadmapDetail);
                props.onRefresh();
              },
            }),
          )
        : null,
    );
  }

  function CreateRoadmapInline(props) {
    var [title, setTitle] = useState('');
    var [description, setDescription] = useState('');
    var [targetDate, setTargetDate] = useState('');
    var [creating, setCreating] = useState(false);
    var [err, setErr] = useState(null);
    function create() {
      if (!title.trim()) { setErr('title required'); return; }
      setCreating(true);
      postCreateRoadmap(props.domainId, {
        title: title.trim(),
        description: description.trim() || null,
        target_date: targetDate || null,
        status: 'active',
      }).then(function () { setCreating(false); props.onCreated(); })
        .catch(function (e) { setCreating(false); setErr(e.message); });
    }
    return el('div', { style: { padding: '10px 0', borderTop: '1px solid rgba(255,255,255,0.06)', marginTop: 8 } },
      err ? el('div', { style: Object.assign({}, errStyle(), { marginBottom: 8 }) }, err) : null,
      el('div', { style: fieldRow() },
        el('label', { style: fieldLabel() }, 'title'),
        el('input', { type: 'text', value: title, placeholder: 'Q3 wave roadmap',
          onChange: function (e) { setTitle(e.target.value); }, style: inp() })),
      el('div', { style: fieldRow() },
        el('label', { style: fieldLabel() }, 'description'),
        el('input', { type: 'text', value: description, placeholder: 'optional',
          onChange: function (e) { setDescription(e.target.value); }, style: inp() })),
      el('div', { style: fieldRow() },
        el('label', { style: fieldLabel() }, 'target date'),
        el('input', { type: 'date', value: targetDate,
          onChange: function (e) { setTargetDate(e.target.value); }, style: inp() })),
      el('div', { style: { display: 'flex', gap: 8, marginTop: 10 } },
        el('button', { type: 'button', onClick: create, disabled: creating, style: btn('primary') },
          creating ? 'Creating…' : 'Create roadmap'),
        el('button', { type: 'button', onClick: props.onClose, disabled: creating, style: btn('secondary') }, 'Cancel'),
      ),
    );
  }

  function RoadmapItemsList(props) {
    var [showAdd, setShowAdd] = useState(false);
    var [newTitle, setNewTitle] = useState('');
    var [newStatus, setNewStatus] = useState('planned');
    var [busy, setBusy] = useState(false);
    var [err, setErr] = useState(null);

    function addItem() {
      if (!newTitle.trim()) { setErr('title required'); return; }
      setBusy(true);
      postAddRoadmapItem(props.roadmapId, { title: newTitle.trim(), status: newStatus })
        .then(function () { setBusy(false); setNewTitle(''); setShowAdd(false); props.onChanged(); })
        .catch(function (e) { setBusy(false); setErr(e.message); });
    }
    function flipStatus(item, next) {
      patchRoadmapItem(item.id, { status: next })
        .then(function () { props.onChanged(); })
        .catch(function (e) { setErr(e.message); });
    }
    function deleteItem(item) {
      if (!window.confirm('Delete item "' + item.title + '"?')) return;
      deleteRoadmapItem(item.id)
        .then(function () { props.onChanged(); })
        .catch(function (e) { setErr(e.message); });
    }

    return el('div', null,
      err ? el('div', { style: errStyle() }, err) : null,
      props.items.length === 0
        ? el('div', { style: { padding: 8, opacity: 0.55, fontSize: 13 } }, 'No items yet.')
        : el('div', null,
            props.items.map(function (it) {
              return el('div', { key: it.id, style: roadmapItemRowStyle(it.status) },
                el('span', { style: roadmapItemStatusChip(it.status) }, it.status),
                el('div', { style: { flex: 1, minWidth: 0 } },
                  el('div', { style: { fontSize: 13, fontWeight: 500 } }, it.title),
                  it.target_date
                    ? el('div', { style: { fontSize: 10, opacity: 0.5, fontFamily: 'JetBrains Mono, ui-monospace, monospace' } }, 'target ' + it.target_date)
                    : null,
                ),
                props.canEdit
                  ? el(React.Fragment, null,
                      el('select', {
                        value: it.status,
                        onChange: function (e) { flipStatus(it, e.target.value); },
                        style: Object.assign({}, sel(), { minWidth: 110 }),
                      },
                        el('option', { value: 'planned' }, 'planned'),
                        el('option', { value: 'in_progress' }, 'in_progress'),
                        el('option', { value: 'blocked' }, 'blocked'),
                        el('option', { value: 'done' }, 'done'),
                        el('option', { value: 'skipped' }, 'skipped')),
                      el('button', { type: 'button', onClick: function () { deleteItem(it); }, style: btn('icon') }, '×'),
                    )
                  : null,
              );
            })),
      props.canEdit
        ? (showAdd
            ? el('div', { style: { display: 'flex', gap: 8, marginTop: 10, alignItems: 'center' } },
                el('input', { type: 'text', value: newTitle, placeholder: 'item title',
                  onChange: function (e) { setNewTitle(e.target.value); }, style: inp() }),
                el('select', { value: newStatus, onChange: function (e) { setNewStatus(e.target.value); }, style: sel() },
                  el('option', { value: 'planned' }, 'planned'),
                  el('option', { value: 'in_progress' }, 'in_progress'),
                  el('option', { value: 'done' }, 'done')),
                el('button', { type: 'button', onClick: addItem, disabled: busy, style: btn('primary') }, busy ? '…' : 'Add'),
                el('button', { type: 'button', onClick: function () { setShowAdd(false); setNewTitle(''); }, style: btn('secondary') }, 'Cancel'),
              )
            : el('div', { style: { marginTop: 10 } },
                el('button', { type: 'button', onClick: function () { setShowAdd(true); }, style: btn('secondary') }, '+ Add item')))
        : null,
    );
  }

  // ──────────────────────────────────────────────────────────────────────
  // GoalsPanel · goal cards with progress + recompute + create (PR-4)
  // ──────────────────────────────────────────────────────────────────────
  function GoalsPanel(props) {
    var d = props.domain;
    var [goals, setGoals] = useState([]);
    var [loading, setLoading] = useState(true);
    var [showCreate, setShowCreate] = useState(false);
    var [err, setErr] = useState(null);

    function load() {
      setLoading(true); setErr(null);
      fetchGoals(d.id).then(function (rows) { setGoals(rows); setLoading(false); })
        .catch(function (e) { setErr(e.message); setLoading(false); });
    }
    useEffect(load, [d.id]);

    return el('div', null,
      err ? el('div', { style: errStyle() }, err) : null,
      el('div', { style: cardStyle() },
        el('div', { style: cardHeaderStyle() },
          el('span', { style: cardTitleStyle() }, 'Goals'),
          el('span', { style: { opacity: 0.5, fontSize: 11 } }, goals.length + ' total'),
          props.canEdit
            ? el('button', { type: 'button', onClick: function () { setShowCreate(true); }, style: Object.assign({}, btn('secondary'), { marginLeft: 'auto' }) }, '+ New goal')
            : null,
        ),
        loading
          ? el('div', { style: { padding: 8, opacity: 0.5, fontSize: 12 } }, 'Loading goals…')
          : goals.length === 0
            ? el('div', { style: { padding: 8, opacity: 0.55, fontSize: 13 } },
                'No goals yet. ' + (props.canEdit ? 'Click +New goal to define a measurable target.' : 'Contact owner/operator to add goals.'))
            : el('div', null,
                goals.map(function (g) {
                  return el(GoalCard, {
                    key: g.id, goal: g, canEdit: props.canEdit,
                    onChanged: function () { load(); props.onRefresh(); },
                  });
                })),
        showCreate
          ? el(CreateGoalInline, {
              domainId: d.id,
              onClose: function () { setShowCreate(false); },
              onCreated: function () { setShowCreate(false); load(); props.onRefresh(); },
            })
          : null,
      ),
    );
  }

  function GoalCard(props) {
    var g = props.goal;
    var current = g.current_value == null ? 0 : Number(g.current_value);
    var target = Number(g.target_value);
    var pct = target > 0 ? Math.min(100, Math.round((current / target) * 100)) : 0;
    var [busy, setBusy] = useState(false);
    var [err, setErr] = useState(null);

    function recompute() {
      setBusy(true);
      postRecomputeGoal(g.id).then(function () { setBusy(false); props.onChanged(); })
        .catch(function (e) { setBusy(false); setErr(e.message); });
    }
    function flipStatus(next) {
      patchGoal(g.id, { status: next }).then(function () { props.onChanged(); })
        .catch(function (e) { setErr(e.message); });
    }

    return el('div', { style: goalCardStyle(g.status) },
      err ? el('div', { style: errStyle() }, err) : null,
      el('div', { style: { display: 'flex', alignItems: 'baseline', gap: 10, marginBottom: 6 } },
        el('span', { style: { fontSize: 14, fontWeight: 600 } }, g.title),
        el('span', { style: goalStatusChip(g.status) }, g.status),
        el('span', { style: { marginLeft: 'auto', fontSize: 12, opacity: 0.7, fontFamily: 'JetBrains Mono, ui-monospace, monospace' } },
          current + ' / ' + target + (g.metric_unit ? ' ' + g.metric_unit : '')),
      ),
      g.description
        ? el('div', { style: { fontSize: 12, opacity: 0.6, marginBottom: 8 } }, g.description)
        : null,
      el('div', { style: progressBarTrack() },
        el('div', { style: progressBarFill(pct, g.status) })),
      el('div', { style: { display: 'flex', gap: 6, marginTop: 8, alignItems: 'center', fontSize: 11, opacity: 0.65 } },
        el('span', { style: { fontFamily: 'JetBrains Mono, ui-monospace, monospace' } }, 'derivation ' + g.derivation.kind),
        g.current_value_updated_at
          ? el('span', null, ' · updated ' + relativeTime(g.current_value_updated_at))
          : el('span', null, ' · not yet computed'),
        g.target_date ? el('span', { style: { marginLeft: 'auto' } }, 'target ' + g.target_date) : null,
      ),
      props.canEdit
        ? el('div', { style: { display: 'flex', gap: 6, marginTop: 10 } },
            el('button', { type: 'button', onClick: recompute, disabled: busy, style: btn('primary') },
              busy ? 'Recomputing…' : 'Recompute now'),
            g.status === 'proposed'
              ? el('button', { type: 'button', onClick: function () { flipStatus('active'); }, style: btn('secondary') }, 'Activate')
              : null,
            g.status === 'active' && pct >= 100
              ? el('button', { type: 'button', onClick: function () { flipStatus('achieved'); }, style: btn('secondary') }, 'Mark achieved')
              : null,
            g.status === 'active'
              ? el('button', { type: 'button', onClick: function () { if (window.confirm('Abandon goal?')) flipStatus('abandoned'); }, style: btn('danger') }, 'Abandon')
              : null,
          )
        : null,
    );
  }

  function CreateGoalInline(props) {
    var [title, setTitle] = useState('');
    var [metricName, setMetricName] = useState('member_project_count');
    var [targetValue, setTargetValue] = useState('5');
    var [derivationKind, setDerivationKind] = useState('member_project_count');
    var [derivationFilter, setDerivationFilter] = useState('');
    var [targetDate, setTargetDate] = useState('');
    var [creating, setCreating] = useState(false);
    var [err, setErr] = useState(null);
    function create() {
      if (!title.trim() || !metricName.trim()) { setErr('title and metric_name required'); return; }
      var target = Number(targetValue);
      if (Number.isNaN(target)) { setErr('target_value must be a number'); return; }
      var derivation = { kind: derivationKind };
      if (derivationFilter.trim()) {
        var eqIdx = derivationFilter.indexOf('=');
        if (eqIdx > 0) {
          var key = derivationFilter.slice(0, eqIdx).trim();
          var val = derivationFilter.slice(eqIdx + 1).trim();
          derivation.filter = { [key]: val };
        }
      }
      setCreating(true);
      postCreateGoal(props.domainId, {
        title: title.trim(),
        metric_name: metricName.trim(),
        target_value: target,
        target_date: targetDate || null,
        status: 'active',
        derivation: derivation,
      }).then(function () { setCreating(false); props.onCreated(); })
        .catch(function (e) { setCreating(false); setErr(e.message); });
    }
    return el('div', { style: { padding: '10px 0', borderTop: '1px solid rgba(255,255,255,0.06)', marginTop: 8 } },
      err ? el('div', { style: Object.assign({}, errStyle(), { marginBottom: 8 }) }, err) : null,
      el('div', { style: fieldRow() },
        el('label', { style: fieldLabel() }, 'title'),
        el('input', { type: 'text', value: title, placeholder: 'Active member projects',
          onChange: function (e) { setTitle(e.target.value); }, style: inp() })),
      el('div', { style: fieldRow() },
        el('label', { style: fieldLabel() }, 'metric'),
        el('input', { type: 'text', value: metricName,
          onChange: function (e) { setMetricName(e.target.value); }, style: inp() })),
      el('div', { style: fieldRow() },
        el('label', { style: fieldLabel() }, 'target'),
        el('input', { type: 'number', value: targetValue, step: '1',
          onChange: function (e) { setTargetValue(e.target.value); }, style: inp() })),
      el('div', { style: fieldRow() },
        el('label', { style: fieldLabel() }, 'derivation'),
        el('select', { value: derivationKind, onChange: function (e) { setDerivationKind(e.target.value); }, style: sel() },
          el('option', { value: 'member_project_count' }, 'member_project_count'),
          el('option', { value: 'project_status_count' }, 'project_status_count'),
          el('option', { value: 'event_count' }, 'event_count'),
          el('option', { value: 'sign_off_approved_count' }, 'sign_off_approved_count'))),
      el('div', { style: fieldRow() },
        el('label', { style: fieldLabel() }, 'filter'),
        el('input', { type: 'text', value: derivationFilter,
          placeholder: 'e.g. status=completed (optional · key=value)',
          onChange: function (e) { setDerivationFilter(e.target.value); }, style: inp() })),
      el('div', { style: fieldRow() },
        el('label', { style: fieldLabel() }, 'target date'),
        el('input', { type: 'date', value: targetDate,
          onChange: function (e) { setTargetDate(e.target.value); }, style: inp() })),
      el('div', { style: { display: 'flex', gap: 8, marginTop: 10 } },
        el('button', { type: 'button', onClick: create, disabled: creating, style: btn('primary') },
          creating ? 'Creating…' : 'Create goal'),
        el('button', { type: 'button', onClick: props.onClose, disabled: creating, style: btn('secondary') }, 'Cancel'),
      ),
    );
  }

  function tabButton(key, label, currentTab, setTab) {
    return el('button', {
      type: 'button',
      onClick: function () { setTab(key); },
      style: currentTab === key ? tabActiveStyle() : tabIdleStyle(),
    }, label);
  }

  function relativeTime(iso) {
    if (!iso) return '';
    try {
      var ms = Date.now() - new Date(iso).getTime();
      var s = Math.floor(ms / 1000);
      if (s < 60) return s + 's ago';
      if (s < 3600) return Math.floor(s / 60) + 'm ago';
      if (s < 86400) return Math.floor(s / 3600) + 'h ago';
      return Math.floor(s / 86400) + 'd ago';
    } catch (_) { return iso; }
  }

  // ──────────────────────────────────────────────────────────────────────
  // Binding read-only + editor
  // ──────────────────────────────────────────────────────────────────────
  function BindingReadOnly(props) {
    return el('div', null,
      props.binding.filters.length === 0
        ? el('div', { style: { opacity: 0.6, fontSize: 13, padding: '6px 0' } }, 'No filters · binding does nothing')
        : props.binding.filters.map(function (f, i) {
            return el('div', { key: i, style: filterRowStyle() },
              el('span', { style: filterTypeStyle() }, f.type),
              el('span', { style: filterValuesStyle() }, (f.values || []).join(', ')),
            );
          }),
      props.canEdit
        ? el('div', { style: { display: 'flex', gap: 8, marginTop: 12 } },
            el('button', { type: 'button', onClick: props.onEdit, style: btn('primary') }, 'Edit binding'),
            el('button', { type: 'button', onClick: props.onRefresh, disabled: props.busy, style: btn('secondary') }, props.busy ? 'Refreshing…' : 'Refresh membership'),
            el('button', { type: 'button', onClick: props.onArchive, disabled: props.busy, style: btn('danger') }, 'Archive'),
          )
        : el('div', { style: { fontSize: 11, opacity: 0.5, marginTop: 8 } }, 'Read-only · contact owner/operator to edit.'),
    );
  }

  function BindingEditor(props) {
    var draft = props.draft;
    function update(patch) { props.setDraft(Object.assign({}, draft, patch)); }
    function updateFilter(i, patch) {
      var next = draft.filters.slice();
      next[i] = Object.assign({}, next[i], patch);
      update({ filters: next });
    }
    function removeFilter(i) { var next = draft.filters.slice(); next.splice(i, 1); update({ filters: next }); }
    function addFilter() { update({ filters: draft.filters.concat([{ type: 'workspace_id_in', values: [] }]) }); }
    function attemptSave() {
      var clean = sanitize(draft);
      if (clean.filters.length === 0) { window.alert('Add at least one filter.'); return; }
      props.onSave();
    }
    return el('div', { style: { paddingTop: 4 } },
      el('div', { style: { display: 'flex', gap: 12, marginBottom: 10, alignItems: 'center' } },
        el('span', { style: { opacity: 0.65, fontSize: 12 } }, 'Combine:'),
        el('label', { style: rl() },
          el('input', { type: 'radio', checked: draft.combine === 'any', onChange: function () { update({ combine: 'any' }); } }),
          el('span', null, 'any match')),
        el('label', { style: rl() },
          el('input', { type: 'radio', checked: draft.combine === 'all', onChange: function () { update({ combine: 'all' }); } }),
          el('span', null, 'all match')),
      ),
      draft.filters.map(function (f, i) {
        return el('div', { key: i, style: { display: 'flex', gap: 8, marginBottom: 6, alignItems: 'center' } },
          el('select', { value: f.type, onChange: function (e) { updateFilter(i, { type: e.target.value }); }, style: sel() },
            el('option', { value: 'workspace_id_in' }, 'workspace_id in'),
            el('option', { value: 'parent_project_id_in' }, 'parent_project_id in'),
            el('option', { value: 'status_in' }, 'status in'),
            el('option', { value: 'tag_in' }, 'tag in (metadata.tags[])'),
            el('option', { value: 'metadata_path' }, 'metadata key=value')),
          el('input', { type: 'text', value: (f.values || []).join(', '),
            placeholder: placeholderFor(f.type),
            onChange: function (e) { updateFilter(i, { values: e.target.value.split(',').map(function (s) { return s.trim(); }).filter(Boolean) }); },
            style: inp() }),
          el('button', { type: 'button', onClick: function () { removeFilter(i); }, style: btn('icon') }, '×'),
        );
      }),
      el('div', { style: { marginTop: 6 } },
        el('button', { type: 'button', onClick: addFilter, style: btn('secondary') }, '+ Add filter')),
      el('div', { style: { marginTop: 14, display: 'flex', gap: 8 } },
        el('button', { type: 'button', onClick: attemptSave, disabled: props.saving, style: btn('primary') },
          props.saving ? 'Saving…' : 'Save binding'),
        el('button', { type: 'button', onClick: props.onCancel, disabled: props.saving, style: btn('secondary') }, 'Cancel'),
      ));
  }

  function placeholderFor(type) {
    switch (type) {
      case 'workspace_id_in': return 'x-biz, xlooop, xcp-platform';
      case 'parent_project_id_in': return 'proj_xyz, proj_abc';
      case 'status_in': return 'active, paused';
      case 'tag_in': return 'commercial, investor_facing';
      case 'metadata_path': return 'vertical=software_dev';
      default: return 'comma-separated values';
    }
  }

  // ──────────────────────────────────────────────────────────────────────
  // Create-domain modal
  // ──────────────────────────────────────────────────────────────────────
  function CreateDomainModal(props) {
    var [slug, setSlug] = useState('');
    var [label, setLabel] = useState('');
    var [description, setDescription] = useState('');
    var [crossWorkspace, setCrossWorkspace] = useState(false);
    var [creating, setCreating] = useState(false);
    var [err, setErr] = useState(null);

    function create() {
      if (!slug.trim() || !label.trim()) { setErr('slug and label required'); return; }
      setCreating(true); setErr(null);
      var body = {
        slug: slug.trim().toLowerCase(),
        label: label.trim(),
        description: description.trim() || null,
        workspace_id: crossWorkspace ? null : props.workspaceId,
        visibility: crossWorkspace ? 'operator_only' : 'workspace',
        binding: {
          version: 1, combine: 'any',
          filters: [{ type: 'workspace_id_in', values: crossWorkspace ? [props.workspaceId] : [props.workspaceId] }],
        },
      };
      postCreate(body).then(function () { setCreating(false); props.onCreated(); })
        .catch(function (e) { setCreating(false); setErr(e.message); });
    }

    return el('div', { style: modalBackdrop(), onClick: props.onClose },
      el('div', { style: modalStyle(), onClick: function (e) { e.stopPropagation(); } },
        el('h3', { style: { margin: '0 0 14px' } }, 'New synthetic domain'),
        err ? el('div', { style: Object.assign({}, errStyle(), { marginBottom: 10 }) }, err) : null,
        el('div', { style: fieldRow() },
          el('label', { style: fieldLabel() }, 'slug'),
          el('input', { type: 'text', value: slug, onChange: function (e) { setSlug(e.target.value); }, placeholder: 'wave-q3-priority', style: inp() })),
        el('div', { style: fieldRow() },
          el('label', { style: fieldLabel() }, 'label'),
          el('input', { type: 'text', value: label, onChange: function (e) { setLabel(e.target.value); }, placeholder: 'Q3 Priority Wave', style: inp() })),
        el('div', { style: fieldRow() },
          el('label', { style: fieldLabel() }, 'description'),
          el('input', { type: 'text', value: description, onChange: function (e) { setDescription(e.target.value); }, placeholder: 'optional', style: inp() })),
        props.isOperator
          ? el('div', { style: { display: 'flex', alignItems: 'center', gap: 8, marginTop: 10, padding: '8px 0' } },
              el('input', { type: 'checkbox', checked: crossWorkspace, onChange: function (e) { setCrossWorkspace(e.target.checked); } }),
              el('span', { style: { fontSize: 12, opacity: 0.85 } },
                'Cross-workspace (operator-only) · domain spans multiple workspaces, visible only to operators'))
          : null,
        el('div', { style: { marginTop: 18, display: 'flex', gap: 8, justifyContent: 'flex-end' } },
          el('button', { type: 'button', onClick: props.onClose, disabled: creating, style: btn('secondary') }, 'Cancel'),
          el('button', { type: 'button', onClick: create, disabled: creating, style: btn('primary') }, creating ? 'Creating…' : 'Create'),
        )));
  }

  // ──────────────────────────────────────────────────────────────────────
  // API
  // ──────────────────────────────────────────────────────────────────────
  function apiBase() { return window.XLOOOP_API_BASE_URL || 'https://api.xlooop.com'; }
  async function authHeaders() {
    var clerk = window.XcpClerk;
    if (!clerk || !clerk.instance || !clerk.instance.session) throw new Error('not signed in');
    var token = await clerk.getToken({ template: 'xlooop-workers' });
    if (!token) throw new Error('no token');
    return { Authorization: 'Bearer ' + token, 'Content-Type': 'application/json' };
  }
  async function fetchDomains(workspaceIdOrCross) {
    var headers = await authHeaders();
    var qs = workspaceIdOrCross === '__cross__'
      ? '__cross__'
      : encodeURIComponent(workspaceIdOrCross);
    var res = await fetch(apiBase() + '/api/v1/synthetic-domains?workspace_id=' + qs, { headers: headers });
    if (!res.ok) {
      var body = await res.json().catch(function () { return null; });
      throw new Error((body && body.error) || ('HTTP ' + res.status));
    }
    var d = await res.json();
    return Array.isArray(d.synthetic_domains) ? d.synthetic_domains : [];
  }
  async function fetchMembers(id) {
    var headers = await authHeaders();
    var res = await fetch(apiBase() + '/api/v1/synthetic-domains/' + encodeURIComponent(id) + '/members', { headers: headers });
    if (!res.ok) return [];
    var d = await res.json();
    return Array.isArray(d.projects) ? d.projects : [];
  }
  async function patchBinding(id, binding) {
    var headers = await authHeaders();
    var res = await fetch(apiBase() + '/api/v1/synthetic-domains/' + encodeURIComponent(id) + '/binding', {
      method: 'PATCH', headers: headers, body: JSON.stringify({ binding: binding }),
    });
    if (!res.ok) {
      var body = await res.json().catch(function () { return null; });
      throw new Error((body && body.error) || ('HTTP ' + res.status));
    }
    return await res.json();
  }
  async function postRefresh(id) {
    var headers = await authHeaders();
    var res = await fetch(apiBase() + '/api/v1/synthetic-domains/' + encodeURIComponent(id) + '/refresh-membership', { method: 'POST', headers: headers });
    if (!res.ok) {
      var body = await res.json().catch(function () { return null; });
      throw new Error((body && body.error) || ('HTTP ' + res.status));
    }
    return await res.json();
  }
  async function postArchive(id) {
    var headers = await authHeaders();
    var res = await fetch(apiBase() + '/api/v1/synthetic-domains/' + encodeURIComponent(id) + '/archive', { method: 'PATCH', headers: headers });
    if (!res.ok) {
      var body = await res.json().catch(function () { return null; });
      throw new Error((body && body.error) || ('HTTP ' + res.status));
    }
    return await res.json();
  }
  async function postCreate(body) {
    var headers = await authHeaders();
    var res = await fetch(apiBase() + '/api/v1/synthetic-domains', { method: 'POST', headers: headers, body: JSON.stringify(body) });
    if (!res.ok) {
      var b = await res.json().catch(function () { return null; });
      throw new Error((b && b.error) || ('HTTP ' + res.status));
    }
    return await res.json();
  }
  // PR-4 · roadmap + goal API helpers
  async function fetchRoadmaps(domainId) {
    var headers = await authHeaders();
    var res = await fetch(apiBase() + '/api/v1/synthetic-domains/' + encodeURIComponent(domainId) + '/roadmaps', { headers: headers });
    if (!res.ok) return [];
    var d = await res.json();
    return Array.isArray(d.roadmaps) ? d.roadmaps : [];
  }
  async function fetchRoadmapDetail(roadmapId) {
    var headers = await authHeaders();
    var res = await fetch(apiBase() + '/api/v1/synthetic-domain-roadmaps/' + encodeURIComponent(roadmapId), { headers: headers });
    if (!res.ok) {
      var b = await res.json().catch(function () { return null; });
      throw new Error((b && b.error) || ('HTTP ' + res.status));
    }
    return await res.json();
  }
  async function postCreateRoadmap(domainId, body) {
    var headers = await authHeaders();
    var res = await fetch(apiBase() + '/api/v1/synthetic-domains/' + encodeURIComponent(domainId) + '/roadmaps', {
      method: 'POST', headers: headers, body: JSON.stringify(body),
    });
    if (!res.ok) {
      var b = await res.json().catch(function () { return null; });
      throw new Error((b && b.error) || ('HTTP ' + res.status));
    }
    return await res.json();
  }
  async function postAddRoadmapItem(roadmapId, body) {
    var headers = await authHeaders();
    var res = await fetch(apiBase() + '/api/v1/synthetic-domain-roadmaps/' + encodeURIComponent(roadmapId) + '/items', {
      method: 'POST', headers: headers, body: JSON.stringify(body),
    });
    if (!res.ok) {
      var b = await res.json().catch(function () { return null; });
      throw new Error((b && b.error) || ('HTTP ' + res.status));
    }
    return await res.json();
  }
  async function patchRoadmapItem(itemId, body) {
    var headers = await authHeaders();
    var res = await fetch(apiBase() + '/api/v1/synthetic-domain-roadmap-items/' + encodeURIComponent(itemId), {
      method: 'PATCH', headers: headers, body: JSON.stringify(body),
    });
    if (!res.ok) {
      var b = await res.json().catch(function () { return null; });
      throw new Error((b && b.error) || ('HTTP ' + res.status));
    }
    return await res.json();
  }
  async function deleteRoadmapItem(itemId) {
    var headers = await authHeaders();
    var res = await fetch(apiBase() + '/api/v1/synthetic-domain-roadmap-items/' + encodeURIComponent(itemId), {
      method: 'DELETE', headers: headers,
    });
    if (!res.ok) {
      var b = await res.json().catch(function () { return null; });
      throw new Error((b && b.error) || ('HTTP ' + res.status));
    }
    return await res.json();
  }
  async function fetchGoals(domainId) {
    var headers = await authHeaders();
    var res = await fetch(apiBase() + '/api/v1/synthetic-domains/' + encodeURIComponent(domainId) + '/goals', { headers: headers });
    if (!res.ok) return [];
    var d = await res.json();
    return Array.isArray(d.goals) ? d.goals : [];
  }
  async function postCreateGoal(domainId, body) {
    var headers = await authHeaders();
    var res = await fetch(apiBase() + '/api/v1/synthetic-domains/' + encodeURIComponent(domainId) + '/goals', {
      method: 'POST', headers: headers, body: JSON.stringify(body),
    });
    if (!res.ok) {
      var b = await res.json().catch(function () { return null; });
      throw new Error((b && b.error) || ('HTTP ' + res.status));
    }
    return await res.json();
  }
  async function patchGoal(goalId, body) {
    var headers = await authHeaders();
    var res = await fetch(apiBase() + '/api/v1/synthetic-domain-goals/' + encodeURIComponent(goalId), {
      method: 'PATCH', headers: headers, body: JSON.stringify(body),
    });
    if (!res.ok) {
      var b = await res.json().catch(function () { return null; });
      throw new Error((b && b.error) || ('HTTP ' + res.status));
    }
    return await res.json();
  }
  async function postRecomputeGoal(goalId) {
    var headers = await authHeaders();
    var res = await fetch(apiBase() + '/api/v1/synthetic-domain-goals/' + encodeURIComponent(goalId) + '/recompute', {
      method: 'POST', headers: headers,
    });
    if (!res.ok) {
      var b = await res.json().catch(function () { return null; });
      throw new Error((b && b.error) || ('HTTP ' + res.status));
    }
    return await res.json();
  }

  // ──────────────────────────────────────────────────────────────────────
  // Helpers + styles
  // ──────────────────────────────────────────────────────────────────────
  var useState = React.useState;
  var useEffect = React.useEffect;
  function el() { return React.createElement.apply(React, arguments); }
  function cloneBinding(b) { return JSON.parse(JSON.stringify(b || { version: 1, combine: 'any', filters: [] })); }
  function sanitize(d) {
    return {
      version: 1,
      combine: d.combine === 'all' ? 'all' : 'any',
      filters: (d.filters || [])
        .filter(function (f) { return f && f.type && Array.isArray(f.values) && f.values.length > 0; })
        .map(function (f) { return { type: f.type, values: f.values.filter(Boolean).slice(0, 100) }; }),
    };
  }
  function backLink() {
    return el('a', { href: '?', style: { color: '#7df0a8', textDecoration: 'none', fontSize: 13 } }, '← back to fixture cockpit');
  }
  function shellStyle() {
    return { position: 'fixed', inset: 0, background: '#0a0a0e', color: '#f0f0f4',
      fontFamily: 'Geist, ui-sans-serif, system-ui, sans-serif', overflowY: 'auto', pointerEvents: 'auto', zIndex: 99988 };
  }
  function topBarStyle() {
    return { display: 'flex', alignItems: 'center', gap: 12, padding: '10px 16px',
      borderBottom: '1px solid rgba(255,255,255,0.08)', background: 'rgba(14,14,18,0.96)',
      position: 'sticky', top: 0, zIndex: 2 };
  }
  function bodyStyle() { return { maxWidth: 1240, margin: '0 auto', padding: '20px 16px 80px' }; }
  function emptyStyle() { return Object.assign({}, shellStyle(), { display: 'flex', alignItems: 'center', justifyContent: 'center' }); }
  function titleH1Style() { return { fontSize: 28, margin: '4px 0', fontWeight: 600, letterSpacing: '-0.01em' }; }
  function layoutStyle() { return { display: 'grid', gridTemplateColumns: '260px 1fr', gap: 16, alignItems: 'start' }; }
  function leftRailStyle() {
    return { background: 'rgba(18,18,22,0.95)', border: '1px solid rgba(255,255,255,0.08)',
      borderRadius: 10, padding: '14px 12px', position: 'sticky', top: 72 };
  }
  function railSectionHeaderStyle() {
    return { fontSize: 10, opacity: 0.5, fontFamily: 'JetBrains Mono, ui-monospace, monospace',
      textTransform: 'uppercase', letterSpacing: '0.08em', marginBottom: 6, padding: '0 4px' };
  }
  function detailColStyle() { return { minHeight: 360 }; }
  function domainItemStyle(selected) {
    return { padding: '8px 10px', borderRadius: 6, cursor: 'pointer', marginBottom: 4,
      background: selected ? 'rgba(125,240,168,0.10)' : 'transparent',
      border: selected ? '1px solid rgba(125,240,168,0.30)' : '1px solid transparent',
      transition: 'background 0.15s, border-color 0.15s' };
  }
  function visBadge(v) {
    var bg = 'rgba(180,180,180,0.10)', fg = '#cdcdcd';
    if (v === 'operator_only') { bg = 'rgba(240,200,125,0.15)'; fg = '#f0c87d'; }
    if (v === 'public_safe') { bg = 'rgba(125,240,168,0.13)'; fg = '#7df0a8'; }
    return { background: bg, color: fg, padding: '1px 6px', borderRadius: 3, fontSize: 10,
      fontFamily: 'JetBrains Mono, ui-monospace, monospace' };
  }
  function cardStyle() {
    return { background: 'rgba(18,18,22,0.95)', border: '1px solid rgba(255,255,255,0.08)',
      borderRadius: 10, padding: '16px 18px', marginBottom: 14 };
  }
  function cardHeaderStyle() { return { display: 'flex', alignItems: 'baseline', gap: 12, marginBottom: 10 }; }
  function cardTitleStyle() { return { fontSize: 14, fontWeight: 600, letterSpacing: '0.01em' }; }
  function filterRowStyle() {
    return { padding: '6px 0', fontSize: 12, fontFamily: 'JetBrains Mono, ui-monospace, monospace',
      borderBottom: '1px solid rgba(255,255,255,0.05)' };
  }
  function filterTypeStyle() { return { opacity: 0.5, marginRight: 10 }; }
  function filterValuesStyle() { return { color: '#a0e8d0' }; }
  function memberRowStyle() { return { padding: '8px 4px', borderTop: '1px solid rgba(255,255,255,0.06)' }; }
  function futureCardStyle() {
    return { background: 'rgba(125,200,240,0.04)', border: '1px dashed rgba(125,200,240,0.20)',
      borderRadius: 8, padding: '10px 14px', marginTop: 10 };
  }
  function errStyle() {
    return { color: '#f08a8a', fontSize: 13, padding: '12px 14px',
      background: 'rgba(240,138,138,0.06)', border: '1px solid rgba(240,138,138,0.2)', borderRadius: 6 };
  }
  function btn(variant) {
    var base = { border: 'none', borderRadius: 4, padding: '6px 14px', fontSize: 12, cursor: 'pointer',
      fontFamily: 'inherit', letterSpacing: '0.02em' };
    if (variant === 'primary') return Object.assign({}, base, { background: '#f0f0f4', color: '#0a0a0a', fontWeight: 500 });
    if (variant === 'danger') return Object.assign({}, base, { background: 'transparent', color: '#f08a8a', border: '1px solid rgba(240,138,138,0.35)' });
    if (variant === 'icon') return Object.assign({}, base, { background: 'transparent', color: '#999', padding: '2px 8px', fontSize: 16 });
    return Object.assign({}, base, { background: 'transparent', color: '#aaa', border: '1px solid rgba(255,255,255,0.15)' });
  }
  function sel() {
    return { background: '#0d0d10', color: '#f0f0f4', border: '1px solid rgba(255,255,255,0.12)',
      borderRadius: 4, padding: '5px 8px', fontSize: 12, minWidth: 200, fontFamily: 'inherit' };
  }
  function inp() {
    return { flex: 1, background: '#0d0d10', color: '#f0f0f4', border: '1px solid rgba(255,255,255,0.12)',
      borderRadius: 4, padding: '5px 10px', fontSize: 12, fontFamily: 'JetBrains Mono, ui-monospace, monospace' };
  }
  function rl() { return { display: 'inline-flex', alignItems: 'center', gap: 5, fontSize: 12 }; }
  function fieldRow() { return { marginBottom: 8, display: 'flex', alignItems: 'center', gap: 10 }; }
  function fieldLabel() { return { width: 90, fontSize: 12, opacity: 0.65 }; }
  function modalBackdrop() {
    return { position: 'fixed', inset: 0, background: 'rgba(0,0,0,0.6)',
      display: 'flex', alignItems: 'center', justifyContent: 'center', zIndex: 99999 };
  }
  function modalStyle() {
    return { background: '#16161c', border: '1px solid rgba(255,255,255,0.10)', borderRadius: 10,
      padding: 24, minWidth: 480, maxWidth: 600, color: '#f0f0f4' };
  }
  // PR-4 · tab + roadmap + goal styles
  function tabBarStyle() {
    return { display: 'flex', gap: 4, borderBottom: '1px solid rgba(255,255,255,0.08)', marginBottom: 14, paddingBottom: 0 };
  }
  function tabIdleStyle() {
    return { background: 'transparent', color: '#aaa', border: 'none', borderBottom: '2px solid transparent',
      padding: '8px 14px', fontSize: 12, fontWeight: 500, cursor: 'pointer', letterSpacing: '0.02em', fontFamily: 'inherit' };
  }
  function tabActiveStyle() {
    return Object.assign({}, tabIdleStyle(), { color: '#f0f0f4', borderBottom: '2px solid #7df0a8' });
  }
  function roadmapTabIdle() {
    return { background: 'rgba(255,255,255,0.04)', color: '#cdcdcd', border: '1px solid rgba(255,255,255,0.10)',
      borderRadius: 4, padding: '5px 10px', fontSize: 12, cursor: 'pointer', fontFamily: 'inherit' };
  }
  function roadmapTabActive() {
    return Object.assign({}, roadmapTabIdle(), {
      background: 'rgba(125,240,168,0.12)', borderColor: 'rgba(125,240,168,0.35)', color: '#f0f0f4' });
  }
  function roadmapItemRowStyle(status) {
    var bg = 'rgba(255,255,255,0.02)';
    if (status === 'done') bg = 'rgba(125,240,168,0.06)';
    else if (status === 'in_progress') bg = 'rgba(125,200,240,0.06)';
    else if (status === 'blocked') bg = 'rgba(240,138,138,0.06)';
    return { display: 'flex', alignItems: 'center', gap: 10,
      padding: '8px 10px', marginBottom: 4, borderRadius: 4, background: bg,
      border: '1px solid rgba(255,255,255,0.05)' };
  }
  function roadmapItemStatusChip(status) {
    var bg = 'rgba(180,180,180,0.10)', fg = '#cdcdcd';
    if (status === 'done') { bg = 'rgba(125,240,168,0.16)'; fg = '#7df0a8'; }
    else if (status === 'in_progress') { bg = 'rgba(125,200,240,0.15)'; fg = '#7dc8f0'; }
    else if (status === 'blocked') { bg = 'rgba(240,138,138,0.15)'; fg = '#f08a8a'; }
    else if (status === 'skipped') { bg = 'rgba(240,200,125,0.13)'; fg = '#f0c87d'; }
    return { display: 'inline-block', minWidth: 90, textAlign: 'center',
      background: bg, color: fg, padding: '2px 8px', borderRadius: 3, fontSize: 10,
      fontFamily: 'JetBrains Mono, ui-monospace, monospace' };
  }
  function goalCardStyle(status) {
    var border = 'rgba(255,255,255,0.10)';
    if (status === 'achieved') border = 'rgba(125,240,168,0.30)';
    else if (status === 'abandoned') border = 'rgba(240,138,138,0.20)';
    return { background: 'rgba(255,255,255,0.02)', border: '1px solid ' + border,
      borderRadius: 8, padding: '12px 14px', marginBottom: 10 };
  }
  function goalStatusChip(status) {
    var bg = 'rgba(180,180,180,0.10)', fg = '#cdcdcd';
    if (status === 'active') { bg = 'rgba(125,200,240,0.15)'; fg = '#7dc8f0'; }
    else if (status === 'achieved') { bg = 'rgba(125,240,168,0.16)'; fg = '#7df0a8'; }
    else if (status === 'abandoned') { bg = 'rgba(240,138,138,0.13)'; fg = '#f08a8a'; }
    else if (status === 'proposed') { bg = 'rgba(240,200,125,0.13)'; fg = '#f0c87d'; }
    return { display: 'inline-block', background: bg, color: fg,
      padding: '1px 7px', borderRadius: 3, fontSize: 10, fontFamily: 'JetBrains Mono, ui-monospace, monospace' };
  }
  function progressBarTrack() {
    return { width: '100%', height: 6, background: 'rgba(255,255,255,0.06)', borderRadius: 3, overflow: 'hidden' };
  }
  function progressBarFill(pct, status) {
    var color = '#7df0a8';
    if (status === 'abandoned') color = '#f08a8a';
    else if (status === 'proposed') color = '#f0c87d';
    return { width: pct + '%', height: '100%', background: color, transition: 'width 0.3s ease' };
  }
  function footerStyle() {
    return { marginTop: 24, padding: '14px 8px', opacity: 0.4, fontSize: 11,
      fontFamily: 'JetBrains Mono, ui-monospace, monospace', borderTop: '1px solid rgba(255,255,255,0.05)' };
  }

  window.SyntheticDomainsPanel = SyntheticDomainsView;
})();
