// DbProjectDetail.jsx · R47 (2026-05-28)
//
// Live-DB project detail surface · sidesteps the static-fixture cockpit
// entirely. Lets the operator validate R44+R45 plumbing end-to-end via UI
// without R48 (the full cockpit rewire).
//
// Mounts when URL contains `?screen=db-project&project_id=<id>`. Renders:
//
//   ┌────────────────────────────────────────────────────────────────────┐
//   │  ← Back to fixture cockpit                       LIVE DB · refresh │
//   │                                                                    │
//   │  MB-P governance                                                   │
//   │  Workspace: org_3EIO8Y… · Status: active                           │
//   │  Description...                                                    │
//   │                                                                    │
//   │  ┌──────────────────────────────────────────────────────────────┐  │
//   │  │ Scope binding · 2 filters · combine any · ABC events match  │  │
//   │  │   • actor matches "claude-session-*, operator, mcp-test"    │  │
//   │  │   • source_tool in: claude, operator                        │  │
//   │  │ [Edit] [Clear]                                              │  │
//   │  └──────────────────────────────────────────────────────────────┘  │
//   │                                                                    │
//   │  Events (8) · refresh every 5s                                     │
//   │  ┌──────────────────────────────────────────────────────────────┐  │
//   │  │ evt_seed_007 · mcp-test · mbp · completed · 10m ago         │  │
//   │  │   Path A validation event · scope binding filter test       │  │
//   │  ├──────────────────────────────────────────────────────────────┤  │
//   │  │ evt_seed_006 · claude-session-def456 · claude · completed   │  │
//   │  │   R45.3 ProjectScopeBindingPanel widget · self-mounts...    │  │
//   │  └──────────────────────────────────────────────────────────────┘  │
//   └────────────────────────────────────────────────────────────────────┘
//
// Tries to be operator-readable, not pretty. Validates the system; the
// fixture cockpit gets prettified in R48.

(function () {
  'use strict';

  if (typeof window === 'undefined') return;
  if (window.__DbProjectDetailMounted) return;
  window.__DbProjectDetailMounted = 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-db-project-detail-portal';
    container.style.cssText = 'position:fixed;inset:0;z-index:99990;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));
  }

  // ──────────────────────────────────────────────────────────────────────
  // Gate · renders only on ?screen=db-project URLs
  // ──────────────────────────────────────────────────────────────────────
  function Gate() {
    var st = React.useState(parseUrl());
    var url = st[0]; var setUrl = st[1];
    React.useEffect(function () {
      function onChange() { setUrl(parseUrl()); }
      window.addEventListener('popstate', onChange);
      window.addEventListener('xcp:nav', onChange);
      var iv = setInterval(onChange, 1500);
      return function () {
        window.removeEventListener('popstate', onChange);
        window.removeEventListener('xcp:nav', onChange);
        clearInterval(iv);
      };
    }, []);
    if (url.screen !== 'db-project') return null;
    if (!url.projectId) return el('div', { style: emptyStyle() },
      el('div', { style: { maxWidth: 600, padding: 32, textAlign: 'center' } },
        el('h1', { style: titleH1Style() }, 'Live DB project view'),
        el('p', null, 'Missing ', el('code', null, 'project_id'), ' query param.'),
        el('p', { style: { opacity: 0.6, fontSize: 13 } },
          'Try: ',
          el('code', null, '?screen=db-project&project_id=proj_mbp_governance')),
        backLink(),
      ),
    );
    return el(LiveDbProjectView, { projectId: url.projectId });
  }

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

  // ──────────────────────────────────────────────────────────────────────
  // LiveDbProjectView
  // ──────────────────────────────────────────────────────────────────────
  function LiveDbProjectView(props) {
    var projectId = props.projectId;
    var [project, setProject] = useState(null);
    var [events, setEvents] = useState([]);
    var [loadingProject, setLoadingProject] = useState(true);
    var [loadingEvents, setLoadingEvents] = useState(true);
    var [error, setError] = useState(null);
    var [editing, setEditing] = useState(false);
    var [draft, setDraft] = useState(null);
    var [saving, setSaving] = useState(false);
    var [lastRefresh, setLastRefresh] = useState(null);
    var [autoRefresh, setAutoRefresh] = useState(true);

    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';

    React.useEffect(function () {
      var cancelled = false;
      function loadAll() {
        Promise.all([fetchProject(projectId), fetchEvents(projectId)])
          .then(function (results) {
            if (cancelled) return;
            setProject(results[0]);
            setEvents(results[1]);
            setLoadingProject(false);
            setLoadingEvents(false);
            setError(null);
            setLastRefresh(new Date());
          })
          .catch(function (e) {
            if (cancelled) return;
            setError(e && e.message ? e.message : String(e));
            setLoadingProject(false);
            setLoadingEvents(false);
          });
      }
      loadAll();
      if (!autoRefresh) return function () { cancelled = true; };
      var iv = setInterval(loadAll, 5000);
      return function () { cancelled = true; clearInterval(iv); };
    }, [projectId, autoRefresh]);

    function refreshEvents() {
      setLoadingEvents(true);
      fetchEvents(projectId).then(function (rows) {
        setEvents(rows);
        setLoadingEvents(false);
        setLastRefresh(new Date());
      }).catch(function (e) {
        setError(e && e.message ? e.message : String(e));
        setLoadingEvents(false);
      });
    }

    // R47.3 · child projects (sub-domains)
    var [children, setChildren] = useState([]);
    var [loadingChildren, setLoadingChildren] = useState(false);
    var [showAddChild, setShowAddChild] = useState(false);
    var [newChildName, setNewChildName] = useState('');
    var [newChildDesc, setNewChildDesc] = useState('');
    var [creatingChild, setCreatingChild] = useState(false);

    React.useEffect(function () {
      if (!projectId) return;
      setLoadingChildren(true);
      fetchChildren(projectId).then(function (rows) {
        setChildren(rows);
        setLoadingChildren(false);
      }).catch(function () {
        setLoadingChildren(false);
      });
    }, [projectId]);

    function createChild() {
      if (!newChildName.trim()) { window.alert('Sub-domain name required.'); return; }
      setCreatingChild(true);
      postNewChild(project.workspace_id, projectId, newChildName.trim(), newChildDesc.trim())
        .then(function (created) {
          setChildren(function (prev) { return [created].concat(prev); });
          setNewChildName(''); setNewChildDesc('');
          setShowAddChild(false);
          setCreatingChild(false);
        })
        .catch(function (e) {
          setCreatingChild(false);
          setError('Create sub-domain failed: ' + (e && e.message ? e.message : String(e)));
        });
    }
    function openChild(c) {
      var u = new URL(window.location.href);
      u.searchParams.set('screen', 'db-project');
      u.searchParams.set('project_id', c.id);
      window.history.pushState({}, '', u.toString());
      window.dispatchEvent(new Event('popstate'));
    }

    function startEdit() {
      setDraft(project && project.scope_binding ? cloneBinding(project.scope_binding) : emptyBinding());
      setEditing(true);
    }
    function cancelEdit() { setDraft(null); setEditing(false); }
    function saveBinding(payload) {
      setSaving(true);
      patchScope(projectId, payload).then(function (updatedProject) {
        setProject(updatedProject);
        setEditing(false);
        setDraft(null);
        setSaving(false);
        refreshEvents();
      }).catch(function (e) {
        setSaving(false);
        setError('Save failed: ' + (e && e.message ? e.message : String(e)));
      });
    }
    function clearBinding() {
      if (!window.confirm('Clear scope binding? Project will show only directly-linked events.')) return;
      saveBinding(null);
    }

    if (loadingProject) {
      return el('div', { style: shellStyle() },
        topBar({ backLink: true, refreshing: true, autoRefresh: autoRefresh }),
        el('div', { style: bodyContainerStyle() },
          el('div', { style: { opacity: 0.6, padding: 24 } }, 'Loading project from /api/v1/projects/' + escapeText(projectId) + ' …')),
      );
    }
    if (error && !project) {
      return el('div', { style: shellStyle() },
        topBar({ backLink: true, refreshing: false, autoRefresh: autoRefresh }),
        el('div', { style: bodyContainerStyle() },
          el('div', { style: { padding: 24 } },
            el('div', { style: errStyle() }, error),
            el('div', { style: { marginTop: 12, fontSize: 12, opacity: 0.5 } },
              'Possible causes: not signed in · project not in your workspace · session expired · network')),
        ),
      );
    }
    if (!project) return null;

    var binding = project.scope_binding;
    var filters = binding && binding.filters ? binding.filters : [];

    return el('div', { style: shellStyle() },
      topBar({
        backLink: true,
        refreshing: loadingEvents,
        autoRefresh: autoRefresh,
        onToggleAuto: function () { setAutoRefresh(!autoRefresh); },
        onRefresh: refreshEvents,
        lastRefresh: lastRefresh,
      }),
      el('div', { style: bodyContainerStyle() },
        // Header card
        el('div', { style: headerCardStyle() },
          el('div', { style: headerLineStyle() },
            el('span', { style: { opacity: 0.5, fontSize: 12 } }, 'LIVE DB · /api/v1/projects/' + project.id),
          ),
          el('h1', { style: titleH1Style() }, project.name),
          el('div', { style: { display: 'flex', gap: 16, opacity: 0.6, fontSize: 13, marginTop: 6 } },
            el('span', null, 'workspace: ', el('code', null, project.workspace_id)),
            el('span', null, 'status: ', el('b', null, project.status)),
            project.scope_binding_updated_at
              ? el('span', null, 'scope updated: ' + relativeTime(project.scope_binding_updated_at))
              : null,
          ),
          project.description ? el('p', { style: { marginTop: 12, opacity: 0.75 } }, project.description) : null,
        ),

        // Scope binding card
        el('div', { style: cardStyle() },
          el('div', { style: cardHeaderStyle() },
            el('span', { style: cardTitleStyle() }, 'Scope binding'),
            el('span', { style: { opacity: 0.5, fontSize: 12 } },
              filters.length === 0
                ? 'unconfigured · only direct-linked events appear below'
                : filters.length + ' filter' + (filters.length === 1 ? '' : 's') + ' · combine ' + binding.combine),
          ),
          editing
            ? el(EditForm, {
                draft: draft, setDraft: setDraft,
                onSave: saveBinding, onCancel: cancelEdit, saving: saving,
              })
            : el(ViewBinding, {
                filters: filters,
                canEdit: canEdit,
                onEdit: startEdit, onClear: clearBinding,
              }),
        ),

        // Events card
        el('div', { style: cardStyle() },
          el('div', { style: cardHeaderStyle() },
            el('span', { style: cardTitleStyle() }, 'Events · matched by direct project_id OR scope_binding'),
            el('span', { style: countBadgeStyle() }, events.length + ' row' + (events.length === 1 ? '' : 's')),
            loadingEvents
              ? el('span', { style: { opacity: 0.5, fontSize: 11 } }, '↻')
              : null,
          ),
          events.length === 0
            ? el('div', { style: { padding: 16, opacity: 0.6, textAlign: 'center', fontSize: 13 } },
                'No matching events. Either no events exist in this workspace yet, ',
                'or the scope binding excludes them. ',
                'Use ', el('code', null, 'xlooop.event.append'), ' from a Claude Code MCP session to add one.')
            : el('div', { style: { padding: 0 } },
                events.slice(0, 50).map(function (ev) {
                  return el(EventRow, { key: ev.id, ev: ev });
                })),
        ),

        // R47.3 · Sub-domains card (children)
        el('div', { style: cardStyle() },
          el('div', { style: cardHeaderStyle() },
            el('span', { style: cardTitleStyle() }, 'Sub-domains · child projects nested under this domain'),
            el('span', { style: countBadgeStyle() }, children.length + ' child' + (children.length === 1 ? '' : 'ren')),
            loadingChildren
              ? el('span', { style: { opacity: 0.5, fontSize: 11 } }, '↻')
              : null,
          ),
          children.length === 0 && !showAddChild
            ? el('div', { style: { padding: '8px 0', opacity: 0.6, fontSize: 13 } },
                'No sub-domains yet. ',
                canEdit
                  ? el('span', null, 'Add one to organize this domain into a tree (e.g. mbp-life → personal, family, finances).')
                  : el('span', null, 'Contact an owner/operator to add sub-domains.'))
            : el('div', { style: childGridStyle() },
                children.map(function (c) {
                  return el(ChildTile, { key: c.id, child: c, onOpen: function () { openChild(c); } });
                })),
          showAddChild
            ? el(AddChildForm, {
                name: newChildName, setName: setNewChildName,
                desc: newChildDesc, setDesc: setNewChildDesc,
                creating: creatingChild,
                onCreate: createChild,
                onCancel: function () { setShowAddChild(false); setNewChildName(''); setNewChildDesc(''); },
              })
            : (canEdit
                ? el('div', { style: { marginTop: 10 } },
                    el('button', { type: 'button', onClick: function () { setShowAddChild(true); }, style: btn('secondary') },
                      '+ Add sub-domain'))
                : null),
        ),

        // Footer · context
        el('div', { style: { padding: '12px 8px', opacity: 0.4, fontSize: 11, fontFamily: 'JetBrains Mono, ui-monospace, monospace' } },
          'R47 · live-DB view · backed by api.xlooop.com · ',
          'R48 will migrate the main cockpit to this read path.'),
      ),
    );
  }

  // ──────────────────────────────────────────────────────────────────────
  // ViewBinding · read-only view of current binding
  // ──────────────────────────────────────────────────────────────────────
  function ViewBinding(props) {
    var filters = props.filters;
    return el('div', null,
      filters.length === 0
        ? el('div', { style: { padding: '8px 0', opacity: 0.6, fontSize: 13 } },
            'No filters configured. Click ', el('b', null, 'Configure scope'),
            ' to filter workspace events into this project view.')
        : el('div', null,
            filters.map(function (f, i) {
              return el('div', { key: i, style: filterRowReadStyle() },
                el('span', { style: filterTypeStyle() }, f.type),
                el('span', { style: filterValuesStyle() }, (f.values || []).join(', ')),
              );
            })),
      props.canEdit
        ? el('div', { style: { marginTop: 12, display: 'flex', gap: 8 } },
            el('button', { type: 'button', onClick: props.onEdit, style: btn('primary') },
              filters.length === 0 ? 'Configure scope' : 'Edit binding'),
            filters.length > 0
              ? el('button', { type: 'button', onClick: props.onClear, style: btn('danger') }, 'Clear')
              : null)
        : el('div', { style: { marginTop: 8, fontSize: 11, opacity: 0.4 } },
            'Role ' + (props.role || 'viewer') + ' · contact an owner/operator to change.'),
    );
  }

  // ──────────────────────────────────────────────────────────────────────
  // EditForm
  // ──────────────────────────────────────────────────────────────────────
  function EditForm(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: 'actor_in', values: [] }]) }); }
    function attemptSave() {
      var clean = sanitize(draft);
      if (clean.filters.length === 0) { window.alert('Add at least one filter or click Cancel.'); return; }
      props.onSave(clean);
    }
    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', name: 'combine', checked: draft.combine === 'any',
            onChange: function () { update({ combine: 'any' }); } }),
          el('span', null, 'any match')),
        el('label', { style: rl() },
          el('input', { type: 'radio', name: 'combine', 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: 'actor_in' }, 'actor matches (agent_id)'),
            el('option', { value: 'source_tool_in' }, 'source_tool in'),
            el('option', { value: 'status_in' }, 'status in'),
            el('option', { value: 'visibility_in' }, 'visibility in')),
          el('input', { type: 'text', value: (f.values || []).join(', '),
            placeholder: f.type === 'actor_in' ? 'claude-session-*, operator' : 'comma-separated',
            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'),
        el('button', { type: 'button', onClick: props.onCancel, disabled: props.saving, style: btn('secondary') }, 'Cancel'),
      ));
  }

  // ──────────────────────────────────────────────────────────────────────
  // EventRow
  // ──────────────────────────────────────────────────────────────────────
  function EventRow(props) {
    var ev = props.ev;
    return el('div', { style: evRowStyle() },
      el('div', { style: evMetaStyle() },
        el('span', { style: evIdStyle() }, ev.id),
        el('span', { style: chipStyle('agent') }, ev.agent_id || '—'),
        el('span', { style: chipStyle('tool') }, ev.source_tool || '—'),
        el('span', { style: chipStyle(ev.status === 'completed' ? 'ok' : ev.status === 'failed' || ev.status === 'blocked' ? 'bad' : 'warn') }, ev.status),
        ev.project_id ? el('span', { style: chipStyle('proj') }, ev.project_id) : null,
        el('span', { style: { opacity: 0.4, fontSize: 11, marginLeft: 'auto' } }, relativeTime(ev.occurred_at)),
      ),
      el('div', { style: evSummaryStyle() }, ev.summary),
      ev.body ? el('div', { style: evBodyStyle() }, ev.body) : null,
    );
  }

  // ──────────────────────────────────────────────────────────────────────
  // ChildTile · single sub-domain tile (R47.3)
  // ──────────────────────────────────────────────────────────────────────
  function ChildTile(props) {
    var c = props.child;
    var binding = c.scope_binding;
    var hasBinding = !!binding && Array.isArray(binding.filters) && binding.filters.length > 0;
    return el('div', {
      style: childTileStyle(), onClick: props.onOpen, role: 'button', tabIndex: 0,
      onKeyDown: function (e) { if (e.key === 'Enter') props.onOpen(); },
    },
      el('div', { style: { display: 'flex', alignItems: 'baseline', gap: 8 } },
        el('span', { style: { fontSize: 14, fontWeight: 600, flex: 1 } }, c.name),
        hasBinding ? el('span', { style: bindChip() }, '⛓ scoped') : null,
      ),
      c.description ? el('div', { style: { fontSize: 11, opacity: 0.55, marginTop: 4, lineHeight: 1.4 } }, c.description) : null,
      el('div', { style: { display: 'flex', gap: 6, marginTop: 8, alignItems: 'center' } },
        el('span', { style: chipStyle(c.status === 'archived' ? 'tool' : 'ok') }, c.status),
        el('span', { style: { marginLeft: 'auto', color: '#7df0a8', fontSize: 11 } }, 'open ›'),
      ),
    );
  }

  // ──────────────────────────────────────────────────────────────────────
  // AddChildForm · inline form to create a sub-domain (R47.3)
  // ──────────────────────────────────────────────────────────────────────
  function AddChildForm(props) {
    return el('div', { style: { marginTop: 12, padding: '12px 14px', background: 'rgba(125,240,168,0.04)', border: '1px solid rgba(125,240,168,0.18)', borderRadius: 6 } },
      el('div', { style: { fontSize: 12, opacity: 0.7, marginBottom: 8 } }, 'New sub-domain under this project'),
      el('input', {
        type: 'text', value: props.name, placeholder: 'name (e.g. mbp-life-finances)',
        onChange: function (e) { props.setName(e.target.value); },
        style: Object.assign({}, inp(), { width: '100%', marginBottom: 6 }),
      }),
      el('input', {
        type: 'text', value: props.desc, placeholder: 'description (optional)',
        onChange: function (e) { props.setDesc(e.target.value); },
        style: Object.assign({}, inp(), { width: '100%', marginBottom: 8 }),
      }),
      el('div', { style: { display: 'flex', gap: 8 } },
        el('button', { type: 'button', onClick: props.onCreate, disabled: props.creating || !props.name.trim(), style: btn('primary') },
          props.creating ? 'Creating…' : 'Create sub-domain'),
        el('button', { type: 'button', onClick: props.onCancel, disabled: props.creating, style: btn('secondary') }, 'Cancel'),
      ),
    );
  }

  // ──────────────────────────────────────────────────────────────────────
  // Top bar
  // ──────────────────────────────────────────────────────────────────────
  function topBar(opts) {
    return el('div', { style: topBarStyle() },
      opts.backLink ? backLink() : null,
      el('span', { style: { flex: 1 } }),
      opts.lastRefresh
        ? el('span', { style: { opacity: 0.5, fontSize: 11, fontFamily: 'JetBrains Mono, ui-monospace, monospace' } },
            'last refresh ' + relativeTime(opts.lastRefresh.toISOString()))
        : null,
      el('label', { style: rl() },
        el('input', { type: 'checkbox', checked: opts.autoRefresh, onChange: opts.onToggleAuto || (function(){}) }),
        el('span', { style: { fontSize: 12 } }, 'auto-refresh 5s')),
      el('button', { type: 'button', onClick: opts.onRefresh || (function(){}), disabled: opts.refreshing, style: btn('secondary') },
        opts.refreshing ? '↻ refreshing…' : '↻ refresh'),
    );
  }
  function backLink() {
    return el('a', { href: '?', style: { color: '#7df0a8', textDecoration: 'none', fontSize: 13 } }, '← back to fixture cockpit');
  }

  // ──────────────────────────────────────────────────────────────────────
  // 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 fetchProject(projectId) {
    var headers = await authHeaders();
    var res = await fetch(apiBase() + '/api/v1/projects/' + encodeURIComponent(projectId), { headers: headers });
    if (!res.ok) {
      var body = await res.json().catch(function () { return null; });
      throw new Error((body && body.error) || ('HTTP ' + res.status));
    }
    var data = await res.json();
    return data.project;
  }
  async function fetchEvents(projectId) {
    var headers = await authHeaders();
    var res = await fetch(apiBase() + '/api/v1/projects/' + encodeURIComponent(projectId) + '/events?limit=100', { headers: headers });
    if (!res.ok) return [];
    var data = await res.json();
    return Array.isArray(data.events) ? data.events : [];
  }
  async function patchScope(projectId, payload) {
    var headers = await authHeaders();
    var res = await fetch(apiBase() + '/api/v1/projects/' + encodeURIComponent(projectId) + '/scope', {
      method: 'PATCH', headers: headers, body: JSON.stringify({ scope_binding: payload }),
    });
    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 d.project;
  }
  // R47.3 · project nesting · list children + create sub-domain
  async function fetchChildren(parentProjectId) {
    var headers = await authHeaders();
    var res = await fetch(apiBase() + '/api/v1/projects/' + encodeURIComponent(parentProjectId) + '/children', { headers: headers });
    if (!res.ok) return [];
    var data = await res.json();
    return Array.isArray(data.projects) ? data.projects : [];
  }
  async function postNewChild(workspaceId, parentProjectId, name, description) {
    var headers = await authHeaders();
    var res = await fetch(apiBase() + '/api/v1/projects', {
      method: 'POST', headers: headers,
      body: JSON.stringify({
        workspace_id: workspaceId,
        parent_project_id: parentProjectId,
        name: name,
        description: description || undefined,
        status: 'active',
      }),
    });
    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 d.project;
  }

  // ──────────────────────────────────────────────────────────────────────
  // Helpers
  // ──────────────────────────────────────────────────────────────────────
  var useState = React.useState;
  function el() { return React.createElement.apply(React, arguments); }
  function emptyBinding() { return { version: 1, combine: 'any', filters: [{ type: 'actor_in', values: [] }] }; }
  function cloneBinding(b) { return JSON.parse(JSON.stringify(b)); }
  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, 50) }; }),
    };
  }
  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; }
  }
  function escapeText(s) { return String(s == null ? '' : s); }

  // ──────────────────────────────────────────────────────────────────────
  // Styles
  // ──────────────────────────────────────────────────────────────────────
  function shellStyle() {
    return {
      position: 'fixed', inset: 0,
      background: '#0a0a0e',
      color: '#f0f0f4',
      fontFamily: 'Geist, ui-sans-serif, system-ui, sans-serif',
      pointerEvents: 'auto',
      overflowY: 'auto',
      zIndex: 99990,
    };
  }
  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)',
      backdropFilter: 'blur(6px)', WebkitBackdropFilter: 'blur(6px)',
      position: 'sticky', top: 0, zIndex: 2,
    };
  }
  function bodyContainerStyle() { return { maxWidth: 980, margin: '0 auto', padding: '20px 16px 80px' }; }
  function emptyStyle() { return Object.assign({}, shellStyle(), { display: 'flex', alignItems: 'center', justifyContent: 'center' }); }
  function headerCardStyle() {
    return {
      background: 'linear-gradient(135deg, rgba(125,240,168,0.04), rgba(255,255,255,0.02))',
      border: '1px solid rgba(125,240,168,0.18)',
      borderRadius: 10, padding: '20px 22px', marginBottom: 16,
    };
  }
  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 titleH1Style() { return { fontSize: 28, margin: '4px 0', fontWeight: 600, letterSpacing: '-0.01em' }; }
  function headerLineStyle() { return { fontSize: 11, fontFamily: 'JetBrains Mono, ui-monospace, monospace', letterSpacing: '0.06em', textTransform: 'uppercase' }; }
  function countBadgeStyle() { return { background: 'rgba(125,240,168,0.18)', color: '#7df0a8', borderRadius: 4, padding: '2px 8px', fontSize: 11, fontFamily: 'JetBrains Mono, ui-monospace, monospace', marginLeft: 'auto' }; }
  function evRowStyle() { return { padding: '12px 4px', borderTop: '1px solid rgba(255,255,255,0.06)' }; }
  function evMetaStyle() { return { display: 'flex', gap: 8, flexWrap: 'wrap', alignItems: 'center', marginBottom: 6 }; }
  function evIdStyle() { return { fontSize: 11, opacity: 0.5, fontFamily: 'JetBrains Mono, ui-monospace, monospace' }; }
  function evSummaryStyle() { return { fontSize: 13, fontWeight: 500, marginBottom: 4 }; }
  function evBodyStyle() { return { fontSize: 12, opacity: 0.6, lineHeight: 1.5 }; }
  function filterRowReadStyle() { 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 childGridStyle() { return { display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(220px, 1fr))', gap: 10, marginTop: 4 }; }
  function childTileStyle() {
    return {
      background: 'rgba(125,240,168,0.04)',
      border: '1px solid rgba(125,240,168,0.18)',
      borderRadius: 8,
      padding: '10px 12px',
      cursor: 'pointer',
      transition: 'border-color 0.15s, transform 0.15s',
      display: 'flex', flexDirection: 'column',
      minHeight: 80,
    };
  }
  function bindChip() { return { background: 'rgba(125,240,168,0.16)', color: '#7df0a8', padding: '1px 6px', borderRadius: 3, fontSize: 10, fontFamily: 'JetBrains Mono, ui-monospace, monospace' }; }
  function chipStyle(kind) {
    var palette = {
      agent: { bg: 'rgba(125,200,240,0.15)', fg: '#7dc8f0' },
      tool: { bg: 'rgba(180,180,180,0.12)', fg: '#cdcdcd' },
      ok: { bg: 'rgba(125,240,168,0.16)', fg: '#7df0a8' },
      warn: { bg: 'rgba(240,200,125,0.15)', fg: '#f0c87d' },
      bad: { bg: 'rgba(240,138,138,0.15)', fg: '#f08a8a' },
      proj: { bg: 'rgba(180,140,240,0.15)', fg: '#b48cf0' },
    };
    var p = palette[kind] || palette.tool;
    return { display: 'inline-block', background: p.bg, color: p.fg, borderRadius: 3, padding: '1px 7px', fontSize: 11, fontFamily: 'JetBrains Mono, ui-monospace, monospace' };
  }
  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: 180, 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 }; }

  window.DbProjectDetail = LiveDbProjectView;
})();
