/* global React, Button, Icon, StatusBadge, ModelBadge, EmptyState, useApiResource, useAuth, apiFetch, formatDuration, formatDateTime, formatRelative, formatPercent, formatNumber, formatCost, shortId, displayModelName */
const { useEffect: useEffectR, useMemo: useMemoR, useRef: useRefR, useState: useStateR } = React;

const DASHBOARD_DEFAULT_RANGE = '48h';
const DASHBOARD_RELATIVE_RANGES = [
  { value: '15m', label: 'Past 15 minutes' },
  { value: '30m', label: 'Past 30 minutes' },
  { value: '1h', label: 'Past 1 hour' },
  { value: '4h', label: 'Past 4 hours' },
  { value: '12h', label: 'Past 12 hours' },
  { value: '24h', label: 'Past 24 hours' },
  { value: '48h', label: 'Past 48 hours' },
  { value: '7d', label: 'Past 7 days' },
  { value: '30d', label: 'Past 30 days' },
];
const DASHBOARD_RELATIVE_RANGE_VALUES = new Set(DASHBOARD_RELATIVE_RANGES.map((range) => range.value));
const RUN_ACTIVE_STATUSES = new Set([
  'queued',
  'running_tester',
  'tester_completed',
  'evaluating',
]);
const RUN_DETAIL_ACTIVE_REFRESH_MS = 15_000;
const SYSTEM_ISSUE_TRACE_HIDDEN_MESSAGE = 'Trace hidden because this run hit an internal system issue.';
const TRACE_FACT_COLLAPSE_CHARS = 80;
// Maximum time after a run finalizes that we keep polling the
// /analysis endpoint waiting for the worker to create the row.
// Generous so a backed-up ops queue doesn't strand the UI on the
// "No analysis" empty state, but bounded so benchmark runs and
// runs that the evaluator hook deliberately skipped don't poll
// forever. 5 minutes covers >99.9% of observed SQS latencies and
// any LLM-call durations.
const ANALYSIS_LATENCY_GRACE_MS = 5 * 60 * 1000;

const FRIENDLY_SCHEDULE_TYPE_LABELS = {
  hourly: 'Hourly',
  daily: 'Daily',
  weekly: 'Weekly',
};

// Workflow schedule labels for the run-detail sidebar. We translate the
// friendly schedule_types into capitalised words and only fall back to
// the raw cron_expression for legacy 'cron' rows so the UI never shows
// a cron string on top of a hourly/daily/weekly schedule.
function formatScheduleSummary(schedule) {
  if (!schedule || schedule.schedule_type === 'manual_only') return 'Manual';
  if (FRIENDLY_SCHEDULE_TYPE_LABELS[schedule.schedule_type]) {
    return FRIENDLY_SCHEDULE_TYPE_LABELS[schedule.schedule_type];
  }
  if (schedule.schedule_type === 'cron') {
    return schedule.cron_expression || 'Custom cron';
  }
  return schedule.schedule_type || 'unscheduled';
}

function DashboardPage({ navigate, queryString = '' }) {
  const auth = useAuth();
  const timeRange = useMemoR(() => parseDashboardTimeRange(queryString), [queryString]);
  const [previewTimeRange, setPreviewTimeRange] = useStateR(null);
  const timeRangeKey = dashboardTimeRangeKey(timeRange);
  const timeRangeLabel = formatDashboardTimeRangeLabel(timeRange);
  const previewTimeRangeLabel = previewTimeRange ? formatDashboardTimeRangeLabel(previewTimeRange) : null;
  const rangeQuery = buildDashboardRangeQuery(timeRange);
  const summary = useApiResource(`/api/dashboard/summary?${rangeQuery}`, [timeRangeKey]);
  const timeline = useApiResource(`/api/dashboard/run-timeline?${rangeQuery}&buckets=24`, [timeRangeKey]);
  const recent = useApiResource(`/api/dashboard/recent-runs?${rangeQuery}&limit=8`, [timeRangeKey]);
  const failing = useApiResource(`/api/dashboard/top-failing-workflows?${rangeQuery}`, [timeRangeKey]);
  const servers = useApiResource('/api/mcp-servers');
  const monitors = useApiResource('/api/tool-monitors');
  const workflows = useApiResource('/api/workflows');
  const stats = summary.data || {};
  const timelineRows = timeline.data?.rows || [];
  const recentRows = recent.data?.rows || [];
  const failingRows = failing.data?.rows || [];
  const maxFailures = Math.max(...failingRows.map(r => r.failure_count), 1);
  const summaryLoading = summary.loading && !summary.data;
  const timelineLoading = timeline.loading && !timeline.data;
  const recentLoading = recent.loading && !recent.data;
  const failingLoading = failing.loading && !failing.data;
  const hasAnyDashboardData = Boolean(summary.data || timeline.data || recent.data || failing.data);

  const sparkSeries = useMemoR(() => {
    if (!timelineRows.length) return { runs: [], successRate: [], failed: [] };
    return {
      runs: timelineRows.map(r => Number(r.totalRuns ?? r.total) || 0),
      successRate: timelineRows.map(r => {
        const total = Number(r.totalRuns ?? r.total) || 0;
        if (total === 0) return 0;
        const ok = (Number(r.successfulRuns ?? r.successful) || 0) + (Number(r.partialRuns ?? r.partial) || 0);
        return Math.max(0, Math.min(1, ok / total));
      }),
      failed: timelineRows.map(r => Number(r.failedRuns ?? r.failed) || 0),
    };
  }, [timelineRows]);

  function reloadDashboard() {
    summary.reload();
    timeline.reload();
    recent.reload();
    failing.reload();
  }

  function setDashboardTimeRange(nextTimeRange) {
    setPreviewTimeRange(null);
    const params = new URLSearchParams(queryString);
    params.delete('range');
    params.delete('from');
    params.delete('to');
    if (nextTimeRange.type === 'absolute') {
      params.set('from', new Date(nextTimeRange.from).toISOString());
      params.set('to', new Date(nextTimeRange.to).toISOString());
    } else {
      params.set('range', nextTimeRange.range);
    }
    const nextQuery = params.toString();
    navigate(`/dashboard${nextQuery ? `?${nextQuery}` : ''}`);
  }

  function confirmDashboardBrushRange(nextTimeRange) {
    setDashboardTimeRange(nextTimeRange);
  }

  useEffectR(() => {
    if (timeRange.type !== 'relative') return undefined;
    const intervalMs = getDashboardRefreshIntervalMs(timeRange.range);
    const timer = window.setInterval(() => {
      reloadDashboard();
    }, intervalMs);
    return () => window.clearInterval(timer);
  }, [timeRangeKey, summary.reload, timeline.reload, recent.reload, failing.reload]);

  const totalFailingWorkflows = failingRows.length;
  const successRateValue = stats.successRate;
  const successRateTone = typeof successRateValue !== 'number'
    ? 'neutral'
    : successRateValue >= 0.95
      ? 'ok'
      : successRateValue >= 0.8 ? 'warn' : 'bad';
  const failedTone = (stats.failedRuns || 0) > 0 ? 'bad' : 'ok';
  const refreshing = (summary.loading || timeline.loading || recent.loading || failing.loading) && hasAnyDashboardData;

  return (
    <div className="page-inner dash-page">
      <div className="ui-page-head">
        <h1 className="ui-page-title">Monitoring</h1>
        <div className="ui-page-actions">
          <DashboardTimePicker timeRange={timeRange} onChange={setDashboardTimeRange} />
          <Button
            size="sm"
            variant="ghost"
            loading={refreshing}
            loadingLabel="Refreshing"
            onClick={reloadDashboard}>
            <Icon name="refresh" size={13} />Refresh
          </Button>
        </div>
      </div>

      <DashboardOnboardingPanel navigate={navigate} />

      <DashboardInventoryStrip
        servers={servers}
        monitors={monitors}
        workflows={workflows}
        navigate={navigate} />

      <div className="dash-hero">
        <DashHeroStat
          label="Runs"
          value={summaryLoading ? null : formatNumber(stats.totalRuns || 0)}
          detail={timeRangeLabel}
          spark={sparkSeries.runs}
          tone="neutral"
          onClick={() => navigate('/runs')} />
        <DashHeroStat
          label="Success rate"
          value={summaryLoading ? null : formatPercent(stats.successRate || 0)}
          detail="passed or partial"
          spark={sparkSeries.successRate}
          tone={successRateTone}
          onClick={() => navigate('/runs?status=success')} />
        <DashHeroStat
          label="Failed runs"
          value={summaryLoading ? null : formatNumber(stats.failedRuns || 0)}
          detail={(stats.failedRuns || 0) > 0 ? 'needs review' : 'all green'}
          spark={sparkSeries.failed}
          tone={failedTone}
          onClick={() => navigate('/runs?status=failed')} />
        <DashHeroStat
          label="Median run"
          value={summaryLoading ? null : formatDuration(stats.p50DurationMs)}
          detail="P50 duration"
          spark={sparkSeries.runs}
          tone="neutral"
          dim />
      </div>

      <section className="dash-block">
        <div className="ui-section-head">
          <div className="ui-section-title">
            Activity
            {previewTimeRangeLabel && (
              <span className="ui-pill ui-pill-brand">selection · {previewTimeRangeLabel}</span>
            )}
          </div>
          <div className="ui-section-actions text-xs muted">{timeRangeLabel}</div>
        </div>
        <div className="card chart-card">
          <RunTimelineChart
            rows={timelineRows}
            loading={timelineLoading}
            onBrushChange={setPreviewTimeRange}
            onBrushConfirm={confirmDashboardBrushRange}
            onBrushDismiss={() => setPreviewTimeRange(null)} />
        </div>
      </section>

      <section className="dash-grid">
        <div className="dash-col">
          <div className="ui-section-head">
            <div className="ui-section-title">
              Top failing
              {totalFailingWorkflows > 0 && <span className="ui-section-count">{totalFailingWorkflows}</span>}
            </div>
            <div className="ui-section-actions">
              <a
                className="dash-link"
                href="/runs?status=failed"
                onClick={(e) => { e.preventDefault(); navigate('/runs?status=failed'); }}>
                All failures<Icon name="chevronRight" size={12} />
              </a>
            </div>
          </div>
          <div className="card dash-card">
            <div className="dash-failing-list">
              {failingLoading ? (
                <FailingWorkflowsLoadingRows />
              ) : failingRows.length === 0 ? (
                <div className="dash-empty">
                  <Icon name="check" size={16} />
                  <span>No failing workflows in this range.</span>
                </div>
              ) : failingRows.map((row, index) => (
                <button
                  type="button"
                  className="dash-failing-row"
                  key={`${row.workflow_id || 'workflow'}-${row.workflow_name || index}`}
                  title={row.workflow_id ? `Open failed runs for ${row.workflow_name}` : row.workflow_name}
                  onClick={() => row.workflow_id ? navigate(`/runs?workflowId=${row.workflow_id}&status=failed`) : null}
                  disabled={!row.workflow_id}>
                  <span className="dash-failing-name">{row.workflow_name}</span>
                  <span className="dash-failing-bar">
                    <span className="dash-failing-fill" style={{ width: `${(row.failure_count / maxFailures) * 100}%` }} />
                  </span>
                  <span className="dash-failing-count">{row.failure_count}</span>
                </button>
              ))}
            </div>
          </div>
        </div>

        <div className="dash-col">
          <div className="ui-section-head">
            <div className="ui-section-title">Recent runs</div>
            <div className="ui-section-actions">
              <a
                className="dash-link"
                href="/runs"
                onClick={(e) => { e.preventDefault(); navigate('/runs'); }}>
                All runs<Icon name="chevronRight" size={12} />
              </a>
            </div>
          </div>
          <div className="card dash-card">
            <div className="dash-recent-list">
              {recentLoading ? (
                <DashboardRecentLoadingRows />
              ) : recentRows.length === 0 ? (
                <div className="dash-empty">
                  <Icon name="list" size={16} />
                  <span>No runs in this range yet.</span>
                </div>
              ) : recentRows.map(run => (
                <button
                  key={run.id}
                  type="button"
                  className="dash-recent-row"
                  onClick={() => navigate(`/runs/${run.id}`)}>
                  <StatusBadge status={run.product_status} />
                  <span className="dash-recent-name" title={run.workflow_name || 'Untitled workflow'}>
                    {run.workflow_name || 'Untitled workflow'}
                  </span>
                  <ClassificationPill classification={run.evaluation_classification} />
                  <ScoreChip score={run.evaluation_score} />
                  <span className="dash-recent-meta mono">{formatDuration(run.duration_ms)}</span>
                  <span className="dash-recent-time muted text-xs">{formatRelative(run.started_at || run.created_at)}</span>
                </button>
              ))}
            </div>
          </div>
        </div>
      </section>
    </div>
  );
}

function DashHeroStat({ label, value, detail, spark, tone = 'neutral', dim = false, onClick = null }) {
  const isLoading = value === null;
  const Wrapper = onClick ? 'button' : 'div';
  return (
    <Wrapper
      type={onClick ? 'button' : undefined}
      className={`dash-hero-stat ${onClick ? 'dash-hero-stat-clickable' : ''} ${dim ? 'dash-hero-stat-dim' : ''}`}
      onClick={onClick}>
      <div className="dash-hero-label">{label}</div>
      <div className="dash-hero-value">
        {isLoading ? <span className="skel skel-mid" style={{ width: 80, height: 24 }} /> : value}
      </div>
      <div className="dash-hero-foot">
        <span className="dash-hero-detail muted text-xs">{detail}</span>
        <Sparkline values={spark} tone={tone} width={90} height={20} />
      </div>
    </Wrapper>
  );
}

function DashboardInventoryStrip({ servers, monitors, workflows, navigate }) {
  const serverRows = servers.data?.rows || [];
  const monitorRows = monitors.data?.rows || [];
  const workflowRows = workflows.data?.rows || [];
  const serversLoading = servers.loading && !servers.data;
  const monitorsLoading = monitors.loading && !monitors.data;
  const workflowsLoading = workflows.loading && !workflows.data;
  const failingServers = serverRows.filter((s) => {
    const status = s.last_connection_status || s.latest_connection_test?.status;
    return status === 'failed' || status === 'error';
  }).length;
  const monitorsFailing = monitorRows.filter((m) => m.last_status === 'fail' || m.last_status === 'error').length;
  const workflowsActive = workflowRows.filter((w) => w.is_active).length;

  return (
    <section className="dash-inventory">
      <DashInventoryItem
        icon="mcp"
        label="Servers"
        loading={serversLoading}
        primary={`${serverRows.length}`}
        meta={failingServers > 0 ? `${failingServers} failing` : 'all reachable'}
        tone={failingServers > 0 ? 'bad' : 'ok'}
        onClick={() => navigate('/sources/mcp-servers')} />
      <DashInventoryItem
        icon="zap"
        label="Monitors"
        loading={monitorsLoading}
        primary={`${monitorRows.length}`}
        meta={monitorsFailing > 0 ? `${monitorsFailing} failing` : 'all healthy'}
        tone={monitorsFailing > 0 ? 'bad' : 'ok'}
        onClick={() => navigate('/tool-monitors')} />
      <DashInventoryItem
        icon="layers"
        label="Workflows"
        loading={workflowsLoading}
        primary={`${workflowsActive}`}
        meta={`${workflowRows.length} total`}
        tone="neutral"
        onClick={() => navigate('/workflows')} />
    </section>
  );
}

function DashInventoryItem({ icon, label, primary, meta, tone = 'neutral', loading = false, onClick }) {
  return (
    <button
      type="button"
      className={`dash-inv-item dash-inv-${tone} ${loading ? 'loading-row' : ''}`}
      aria-busy={loading ? 'true' : undefined}
      onClick={onClick}>
      <span className="dash-inv-icon"><Icon name={icon} size={14} /></span>
      <span className="dash-inv-body">
        <span className="dash-inv-label">{label}</span>
        {loading
          ? <span className="skel skel-short" />
          : <span className="dash-inv-meta">{meta}</span>}
      </span>
      {loading
        ? <span className="skel skel-mid dash-inv-value-skel" />
        : <span className="dash-inv-value mono">{primary}</span>}
      <Icon name="chevronRight" size={12} className="dash-inv-caret" />
    </button>
  );
}

function DashboardOnboardingPanel({ navigate }) {
  const auth = useAuth();
  const me = auth?.me;
  const onboardingState = me?.profile?.onboardingState || {};
  const checklistDismissedItems = Array.isArray(onboardingState.checklistDismissedItems)
    ? onboardingState.checklistDismissedItems
    : [];
  const checklistDismissedItemsKey = checklistDismissedItems.join('\u001f');
  const [dismissed, setDismissed] = useStateR(Boolean(onboardingState.checklistDismissed));
  const [dismissedItemIds, setDismissedItemIds] = useStateR(() => new Set(checklistDismissedItems));
  const isOwner = me?.role === 'owner';
  const status = me?.organization?.subscriptionStatus;

  useEffectR(() => {
    setDismissed(Boolean(onboardingState.checklistDismissed));
  }, [onboardingState.checklistDismissed]);

  useEffectR(() => {
    setDismissedItemIds(new Set(checklistDismissedItems));
  }, [checklistDismissedItemsKey]);

  if (!me) return null;

  async function saveChecklistState(patch) {
    await apiFetch('/api/onboarding/state', {
      method: 'POST',
      body: JSON.stringify({ onboardingState: patch }),
    });
    await auth?.refreshMe?.();
  }

  async function dismiss() {
    const previousDismissed = dismissed;
    setDismissed(true);
    try {
      await saveChecklistState({ checklistDismissed: true });
    } catch (error) {
      setDismissed(previousDismissed);
      console.error('Failed to dismiss activation checklist', error);
    }
  }

  async function dismissItem(itemId) {
    const previousDismissedItemIds = dismissedItemIds;
    const nextIds = [...new Set([...dismissedItemIds, itemId])];
    setDismissedItemIds(new Set(nextIds));
    try {
      await saveChecklistState({ checklistDismissedItems: nextIds });
    } catch (error) {
      setDismissedItemIds(previousDismissedItemIds);
      console.error('Failed to dismiss activation checklist item', error);
    }
  }

  const skippedReadOnlyBanner = status === 'skipped';
  const ownerBillingBanner = isOwner && status === 'past_due';
  const defaultItems = [
    { id: 'connect-server', label: 'Connect MCP server', href: '/sources/mcp-servers', icon: 'mcp', done: Boolean(me.profile?.onboardingState?.lastServerId) },
    { id: 'create-workflow', label: 'Create first workflow', href: '/workflows', icon: 'layers', done: Boolean(me.profile?.onboardingState?.lastWorkflowId) },
    { id: 'setup-alerts', label: 'Set up alerts', href: '/settings/alerts', icon: 'bell', done: false },
    { id: 'invite-teammate', label: 'Invite a teammate', href: '/settings/invitations', icon: 'user', done: false },
  ];
  const inviteeItems = [
    { id: 'explore-monitors', label: 'Explore monitors', href: '/tool-monitors', icon: 'zap', done: false },
    { id: 'alert-preferences', label: 'Set up alert preferences', href: '/settings/alerts', icon: 'bell', done: false },
    { id: 'view-workflows', label: 'View workflows', href: '/workflows', icon: 'layers', done: false },
  ];
  const items = isOwner ? defaultItems : inviteeItems;
  const visibleItems = items.filter((item) => !dismissedItemIds.has(item.id));

  return (
    <div className="dashboard-onboarding-stack">
      {skippedReadOnlyBanner && (
        <div className="dashboard-banner warning">
          <span>This workspace is in read-only mode. Subscribe to create MCP servers, monitors, workflows, alerts, API keys, or invitations.</span>
          {isOwner && (
            <Button size="sm" variant="primary" onClick={() => navigate('/settings/billing')}>Choose a plan</Button>
          )}
        </div>
      )}
      {ownerBillingBanner && (
        <div className="dashboard-banner warning">
          <span>Add a payment method to keep your monitors running.</span>
          <Button size="sm" variant="primary" onClick={() => navigate('/settings/billing')}>Open billing</Button>
        </div>
      )}
      {!dismissed && visibleItems.length > 0 && (
        <div className="dashboard-checklist card">
          <div className="dashboard-checklist-head">
            <div className="dashboard-checklist-title-row">
              <div className="dashboard-checklist-mark"><Icon name="check" size={16} /></div>
              <div>
                <div className="card-title">Activation checklist</div>
                <div className="card-sub">Finish the core setup path and keep the next actions close.</div>
              </div>
            </div>
            <div className="dashboard-checklist-progress">
              <span>{visibleItems.filter((item) => item.done).length}</span> / {visibleItems.length} complete
            </div>
            <button className="icon-btn" aria-label="Dismiss checklist" onClick={dismiss}><Icon name="x" size={14} /></button>
          </div>
          <div className="dashboard-checklist-body">
            <div>
              <div className="dashboard-checklist-eyebrow">Onboarding</div>
              <h2>Get Armature production-ready</h2>
              <div className="card-sub">Deep links stay here until you dismiss them.</div>
            </div>
            <div className="dashboard-checklist-items">
              {visibleItems.map((item) => (
                <div className={`dashboard-checklist-item ${item.done ? 'done' : ''}`} key={item.id}>
                  <button className="dashboard-checklist-link" onClick={() => navigate(item.href)}>
                    <span className={`dashboard-checklist-status ${item.done ? 'done' : ''}`}>
                      <Icon name={item.done ? 'check' : 'chevronRight'} size={12} />
                    </span>
                    <span className="dashboard-checklist-item-icon"><Icon name={item.icon} size={14} /></span>
                    <span>{item.label}</span>
                  </button>
                  <button
                    className="dashboard-checklist-remove"
                    aria-label={`Remove ${item.label} from checklist`}
                    onClick={() => dismissItem(item.id)}
                  >
                    <Icon name="x" size={12} />
                  </button>
                </div>
              ))}
            </div>
          </div>
        </div>
      )}
    </div>
  );
}

function DashboardTimePicker({ timeRange, onChange, label = '' }) {
  const [open, setOpen] = useStateR(false);
  const [mode, setMode] = useStateR(timeRange.type);
  const [search, setSearch] = useStateR('');
  const [draftFrom, setDraftFrom] = useStateR(() => toDateTimeLocalValue(getTimeRangeInterval(timeRange).from));
  const [draftTo, setDraftTo] = useStateR(() => toDateTimeLocalValue(getTimeRangeInterval(timeRange).to));
  const containerRef = useRefR(null);
  const triggerRef = useRefR(null);
  const timezone = getDashboardTimezone();
  const filteredRanges = DASHBOARD_RELATIVE_RANGES.filter((range) => (
    range.label.toLowerCase().includes(search.trim().toLowerCase())
  ));
  const draftFromMs = parseDateTimeLocalValue(draftFrom);
  const draftToMs = parseDateTimeLocalValue(draftTo);
  const absoluteError = getAbsoluteTimeRangeError(draftFromMs, draftToMs);

  useEffectR(() => {
    if (!open) return undefined;
    function closeOnOutside(event) {
      if (!containerRef.current?.contains(event.target)) setOpen(false);
    }
    function closeOnEscape(event) {
      if (event.key === 'Escape') {
        setOpen(false);
        triggerRef.current?.focus();
      }
    }
    document.addEventListener('mousedown', closeOnOutside);
    document.addEventListener('keydown', closeOnEscape);
    return () => {
      document.removeEventListener('mousedown', closeOnOutside);
      document.removeEventListener('keydown', closeOnEscape);
    };
  }, [open]);

  useEffectR(() => {
    setMode(timeRange.type);
    const interval = getTimeRangeInterval(timeRange);
    setDraftFrom(toDateTimeLocalValue(interval.from));
    setDraftTo(toDateTimeLocalValue(interval.to));
  }, [timeRange.type, dashboardTimeRangeKey(timeRange)]);

  function applyAbsoluteRange() {
    if (absoluteError || draftFromMs === null || draftToMs === null) return;
    onChange({ type: 'absolute', from: draftFromMs, to: draftToMs });
    setOpen(false);
  }

  return (
    <div className={`time-picker ${open ? 'open' : ''}`} ref={containerRef}>
      <button
        ref={triggerRef}
        className="time-picker-trigger"
        type="button"
        title={formatDashboardTimeRangeLabel(timeRange)}
        aria-haspopup="dialog"
        aria-expanded={open}
        onClick={() => setOpen((value) => !value)}>
        <Icon name="calendar" size={13} />
        {label ? <span className="time-picker-prefix">{label}:</span> : null}
        <span className="time-picker-label">{formatDashboardTimeRangeLabel(timeRange)}</span>
        <span className="time-picker-timezone">{timezone}</span>
        <Icon name="chevronDown" size={13} className="time-picker-chevron" />
      </button>
      {open && (
        <div className="time-picker-menu" role="dialog" aria-label="Time range picker">
          <div className="seg time-picker-mode" role="tablist" aria-label="Time range type">
            <button
              type="button"
              className={mode === 'relative' ? 'active' : ''}
              onClick={() => setMode('relative')}>
              Relative time
            </button>
            <button
              type="button"
              className={mode === 'absolute' ? 'active' : ''}
              onClick={() => setMode('absolute')}>
              Absolute time
            </button>
          </div>
          {mode === 'relative' ? (
            <div className="time-picker-panel">
              <input
                className="input time-picker-search"
                placeholder="Search period"
                value={search}
                onChange={(event) => setSearch(event.target.value)} />
              <div className="time-picker-options" role="listbox" aria-label="Relative time ranges">
                {filteredRanges.map((range) => (
                  <button
                    key={range.value}
                    type="button"
                    className={`time-picker-option ${timeRange.type === 'relative' && timeRange.range === range.value ? 'selected' : ''}`}
                    role="option"
                    aria-selected={timeRange.type === 'relative' && timeRange.range === range.value}
                    onClick={() => {
                      onChange({ type: 'relative', range: range.value });
                      setOpen(false);
                    }}>
                    <span>{range.label}</span>
                    {timeRange.type === 'relative' && timeRange.range === range.value ? <Icon name="check" size={14} /> : null}
                  </button>
                ))}
                {filteredRanges.length === 0 && (
                  <div className="time-picker-empty">No results found</div>
                )}
              </div>
            </div>
          ) : (
            <div className="time-picker-panel">
              <label className="time-picker-field">
                <span>Start</span>
                <input
                  className="input"
                  type="text"
                  inputMode="numeric"
                  placeholder="YYYY-MM-DD HH:mm"
                  value={draftFrom}
                  onChange={(event) => setDraftFrom(event.target.value)} />
              </label>
              <label className="time-picker-field">
                <span>End</span>
                <input
                  className="input"
                  type="text"
                  inputMode="numeric"
                  placeholder="YYYY-MM-DD HH:mm"
                  value={draftTo}
                  onChange={(event) => setDraftTo(event.target.value)} />
              </label>
              <div className="time-picker-meta">Timezone {timezone}</div>
              {absoluteError ? <div className="form-error">{absoluteError}</div> : null}
              <div className="time-picker-actions">
                <Button size="sm" variant="ghost" onClick={() => setOpen(false)}>Cancel</Button>
                <Button size="sm" variant="primary" disabled={Boolean(absoluteError)} onClick={applyAbsoluteRange}>Apply</Button>
              </div>
            </div>
          )}
        </div>
      )}
    </div>
  );
}

function parseDashboardTimeRange(queryString) {
  const params = new URLSearchParams(queryString || '');
  const from = parseDashboardTimeParam(params.get('from'));
  const to = parseDashboardTimeParam(params.get('to'));
  if (from !== null && to !== null && from < to) {
    return { type: 'absolute', from, to };
  }
  const range = params.get('range') || DASHBOARD_DEFAULT_RANGE;
  return {
    type: 'relative',
    range: DASHBOARD_RELATIVE_RANGE_VALUES.has(range) ? range : DASHBOARD_DEFAULT_RANGE,
  };
}

function parseDashboardTimeParam(value) {
  if (!value) return null;
  const trimmed = String(value).trim();
  const millis = /^\d+$/.test(trimmed) ? Number(trimmed) : new Date(trimmed).getTime();
  return Number.isFinite(millis) ? millis : null;
}

function dashboardTimeRangeKey(timeRange) {
  return timeRange.type === 'absolute'
    ? `absolute:${timeRange.from}:${timeRange.to}`
    : `relative:${timeRange.range}`;
}

function buildDashboardRangeQuery(timeRange) {
  const params = new URLSearchParams();
  if (timeRange.type === 'absolute') {
    params.set('from', new Date(timeRange.from).toISOString());
    params.set('to', new Date(timeRange.to).toISOString());
  } else {
    params.set('range', timeRange.range);
  }
  return params.toString();
}

function formatDashboardTimeRangeLabel(timeRange) {
  if (timeRange.type === 'relative') {
    return DASHBOARD_RELATIVE_RANGES.find((range) => range.value === timeRange.range)?.label || 'Past 48 hours';
  }
  return `${formatCompactDateTime(timeRange.from)} - ${formatCompactDateTime(timeRange.to)}`;
}

function formatCompactDateTime(value) {
  const date = new Date(value);
  const currentYear = new Date().getFullYear();
  /** @type {Intl.DateTimeFormatOptions} */
  const options = {
    month: 'short',
    day: 'numeric',
    hour: 'numeric',
    minute: '2-digit',
  };
  if (date.getFullYear() !== currentYear) options.year = 'numeric';
  return date.toLocaleString([], options);
}

function getDashboardTimezone() {
  return Intl.DateTimeFormat().resolvedOptions().timeZone || 'Local';
}

function getTimeRangeInterval(timeRange) {
  if (timeRange.type === 'absolute') return { from: timeRange.from, to: timeRange.to };
  const to = Date.now();
  return { from: to - rangeToDurationMs(timeRange.range), to };
}

function rangeToDurationMs(range) {
  const match = String(range || DASHBOARD_DEFAULT_RANGE).match(/^(\d+)(m|h|d|w)$/);
  if (!match) return 48 * 60 * 60 * 1000;
  const count = Number.parseInt(match[1], 10);
  const unitMs = {
    m: 60 * 1000,
    h: 60 * 60 * 1000,
    d: 24 * 60 * 60 * 1000,
    w: 7 * 24 * 60 * 60 * 1000,
  }[match[2]];
  return count * unitMs;
}

function getDashboardRefreshIntervalMs(range) {
  const duration = rangeToDurationMs(range);
  if (duration <= 30 * 60 * 1000) return 60 * 1000;
  if (duration <= 3 * 60 * 60 * 1000) return 5 * 60 * 1000;
  if (duration <= 24 * 60 * 60 * 1000) return 30 * 60 * 1000;
  return 60 * 60 * 1000;
}

function toDateTimeLocalValue(value) {
  const date = new Date(value);
  if (Number.isNaN(date.getTime())) return '';
  const pad = (number) => String(number).padStart(2, '0');
  return [
    date.getFullYear(),
    '-',
    pad(date.getMonth() + 1),
    '-',
    pad(date.getDate()),
    ' ',
    pad(date.getHours()),
    ':',
    pad(date.getMinutes()),
  ].join('');
}

function parseDateTimeLocalValue(value) {
  if (!value) return null;
  const text = String(value).trim();
  const match = text.match(/^(\d{4})-(\d{2})-(\d{2})[ T](\d{2}):(\d{2})$/);
  const millis = match
    ? new Date(
      Number(match[1]),
      Number(match[2]) - 1,
      Number(match[3]),
      Number(match[4]),
      Number(match[5]),
    ).getTime()
    : new Date(text).getTime();
  return Number.isFinite(millis) ? millis : null;
}

function getAbsoluteTimeRangeError(from, to) {
  if (from === null || to === null) return 'Enter both start and end times';
  if (from >= to) return 'End must be after start';
  if (to > Date.now() + 60 * 1000) return 'End cannot be in the future';
  return '';
}

function RunTimelineChart({
  rows,
  loading = false,
  onBrushChange = (_timeRange) => {},
  onBrushConfirm = (_timeRange) => {},
  onBrushDismiss = () => {},
}) {
  const maxRuns = Math.max(...rows.map((row) => row.totalRuns), 1);
  const hasRuns = rows.some((row) => row.totalRuns > 0);
  const plotRef = useRefR(null);
  const [activeTooltipKey, setActiveTooltipKey] = useStateR(null);
  const timelineDomain = getTimelineDomain(rows);
  const gridStyle = { gridTemplateColumns: `repeat(${Math.max(rows.length, 1)}, minmax(10px, 1fr))` };
  const axisStep = Math.max(1, Math.ceil(rows.length / 6));

  if (loading) {
    return (
      <div className="chart-body">
        <DashboardChartSkeleton />
      </div>
    );
  }

  if (!hasRuns) {
    return (
      <div className="chart-body">
        <div className="chart-empty">No runs yet</div>
      </div>
    );
  }

  return (
    <div className="chart-body">
      <div className="run-chart">
        <div className="run-chart-y-axis" aria-hidden="true">
          <span>{formatNumber(maxRuns)}</span>
          <span>{formatNumber(Math.ceil(maxRuns / 2))}</span>
          <span>0</span>
        </div>
        <div className="run-chart-main">
          <div
            className={`run-chart-plot ${timelineDomain ? 'brush-ready' : ''}`}
            ref={plotRef}
            role="img"
            aria-label="Runs over time by status">
            <div className="run-chart-grid-line top"></div>
            <div className="run-chart-grid-line mid"></div>
            <div className="run-chart-grid-line bottom"></div>
            <div className="run-chart-bars" style={gridStyle}>
              {rows.map((row, index) => {
                const total = row.totalRuns || 0;
                const rowKey = `${row.bucketIndex}-${row.bucketStart}`;
                const stackHeight = `${Math.max(2, (total / maxRuns) * 100)}%`;
                const segments = [
                  ['failed', row.failedRuns || 0],
                  ['running', row.activeRuns || 0],
                  ['pending', row.pendingRuns || 0],
                  ['partial', row.partialRuns || 0],
                  ['success', row.successfulRuns || 0],
                ].filter(([, count]) => count > 0);
                const tooltip = chartTooltipLabel(row);
                return (
                  <div
                    key={rowKey}
                    className={`run-chart-bar ${total ? '' : 'empty'}`}>
                    {total > 0 ? (
                      <div
                        className="run-chart-bar-hit"
                        style={{ height: stackHeight }}
                        tabIndex={0}
                        aria-label={tooltip}
                        onMouseEnter={() => setActiveTooltipKey(rowKey)}
                        onMouseLeave={() => setActiveTooltipKey((key) => (key === rowKey ? null : key))}
                        onFocus={() => setActiveTooltipKey(rowKey)}
                        onBlur={() => setActiveTooltipKey((key) => (key === rowKey ? null : key))}>
                        <div className="run-chart-stack">
                          {segments.map(([status, count]) => (
                            <span
                              key={status}
                              className={`run-chart-segment ${status}`}
                              style={{ height: `${(count / total) * 100}%` }}></span>
                          ))}
                        </div>
                        {activeTooltipKey === rowKey ? (
                          <div className="run-chart-tooltip" role="tooltip">
                            <div className="tooltip-title">{formatBucketRange(row)}</div>
                            <div className="tooltip-grid">
                              <span>Total</span><strong>{formatNumber(total)}</strong>
                              {row.successfulRuns > 0 ? <><span>Success</span><strong>{formatNumber(row.successfulRuns)}</strong></> : null}
                              {row.partialRuns > 0 ? <><span>Partial</span><strong>{formatNumber(row.partialRuns)}</strong></> : null}
                              {row.failedRuns > 0 ? <><span>Failed</span><strong>{formatNumber(row.failedRuns)}</strong></> : null}
                              {row.activeRuns > 0 ? <><span>Running</span><strong>{formatNumber(row.activeRuns)}</strong></> : null}
                              {row.pendingRuns > 0 ? <><span>Pending</span><strong>{formatNumber(row.pendingRuns)}</strong></> : null}
                              <span>P50</span><strong>{formatDuration(row.p50DurationMs)}</strong>
                            </div>
                          </div>
                        ) : null}
                      </div>
                    ) : null}
                  </div>
                );
              })}
            </div>
            {timelineDomain ? (
              <DashboardGraphBrush
                plotRef={plotRef}
                domain={timelineDomain}
                onChange={onBrushChange}
                onConfirm={onBrushConfirm}
                onDismiss={onBrushDismiss} />
            ) : null}
          </div>
          <div className="run-chart-x-axis" style={gridStyle} aria-hidden="true">
            {rows.map((row, index) => (
              <span key={`${row.bucketIndex}-${row.bucketEnd}`}>
                {index === 0 || index === rows.length - 1 || index % axisStep === 0 ? formatAxisTime(row.bucketStart) : ''}
              </span>
            ))}
          </div>
        </div>
      </div>
      <div className="run-chart-legend" aria-hidden="true">
        <span><i className="success"></i>Successful</span>
        <span><i className="partial"></i>Partial</span>
        <span><i className="failed"></i>Failed</span>
        <span><i className="running"></i>Running</span>
        <span><i className="pending"></i>Pending</span>
      </div>
    </div>
  );
}

function DashboardGraphBrush({ plotRef, domain, onChange, onConfirm, onDismiss }) {
  const [brush, setBrush] = useStateR(null);
  const cleanupRef = useRefR(null);
  const domainStart = domain[0];
  const domainEnd = domain[1];
  const domainSpan = Math.max(1, domainEnd - domainStart);
  const hasValidBrush = Boolean(brush && brush.end > brush.start);

  useEffectR(() => {
    cleanupRef.current?.();
    setBrush(null);
    onDismiss();
  }, [domainStart, domainEnd]);

  useEffectR(() => () => {
    cleanupRef.current?.();
  }, []);

  useEffectR(() => {
    const plot = plotRef.current;
    if (!plot || domainEnd <= domainStart) return undefined;

    function handleMouseDown(event) {
      if (event.button !== 0) return;
      if (event.target?.closest?.('.run-chart-brush-control')) return;
      beginSelection(event);
    }

    plot.addEventListener('mousedown', handleMouseDown);
    return () => plot.removeEventListener('mousedown', handleMouseDown);
  }, [plotRef, domainStart, domainEnd]);

  function getPlotRect() {
    return plotRef.current?.getBoundingClientRect();
  }

  function clampTime(value) {
    return Math.max(domainStart, Math.min(domainEnd, value));
  }

  function getTimeForClientX(clientX) {
    const rect = getPlotRect();
    if (!rect || rect.width <= 0) return domainStart;
    const x = Math.max(0, Math.min(rect.width, clientX - rect.left));
    return Math.round(domainStart + (x / rect.width) * domainSpan);
  }

  function normalizeSelection(start, end) {
    const boundedStart = clampTime(start);
    const boundedEnd = clampTime(end);
    return {
      start: Math.min(boundedStart, boundedEnd),
      end: Math.max(boundedStart, boundedEnd),
    };
  }

  function emitSelection(nextBrush) {
    setBrush(nextBrush);
    if (nextBrush.end > nextBrush.start) {
      onChange({ type: 'absolute', from: nextBrush.start, to: nextBrush.end });
    }
  }

  function attachDrag(onMove, onEnd) {
    cleanupRef.current?.();
    const previousUserSelect = document.body.style.userSelect;
    document.body.style.userSelect = 'none';

    function cleanup() {
      document.body.style.userSelect = previousUserSelect;
      window.removeEventListener('mousemove', onMove);
      window.removeEventListener('mouseup', handleMouseUp);
      cleanupRef.current = null;
    }

    function handleMouseUp(event) {
      cleanup();
      onEnd(event);
    }

    window.addEventListener('mousemove', onMove);
    window.addEventListener('mouseup', handleMouseUp);
    cleanupRef.current = cleanup;
  }

  function beginSelection(event) {
    event.preventDefault();
    const startTime = getTimeForClientX(event.clientX);
    const startX = event.clientX;
    let latestBrush = null;

    function handleMove(moveEvent) {
      const moved = Math.abs(moveEvent.clientX - startX);
      const nextBrush = normalizeSelection(startTime, getTimeForClientX(moveEvent.clientX));
      if (moved < 4 || nextBrush.end <= nextBrush.start) return;
      latestBrush = nextBrush;
      emitSelection(nextBrush);
    }

    attachDrag(handleMove, () => {
      if (!latestBrush || latestBrush.end <= latestBrush.start) {
        setBrush(null);
        onDismiss();
      }
    });
  }

  function beginResize(boundary, event) {
    event.preventDefault();
    event.stopPropagation();
    if (!brush) return;
    const fixedBoundary = boundary === 'start' ? brush.end : brush.start;

    function handleMove(moveEvent) {
      const pointerTime = getTimeForClientX(moveEvent.clientX);
      const nextBrush = boundary === 'start'
        ? normalizeSelection(pointerTime, fixedBoundary)
        : normalizeSelection(fixedBoundary, pointerTime);
      emitSelection(nextBrush);
    }

    attachDrag(handleMove, () => {});
  }

  function beginMove(event) {
    event.preventDefault();
    event.stopPropagation();
    if (!brush) return;
    const rect = getPlotRect();
    if (!rect || rect.width <= 0) return;
    const initialX = event.clientX;
    const initialStart = brush.start;
    const initialEnd = brush.end;
    const selectionSpan = initialEnd - initialStart;

    function handleMove(moveEvent) {
      const deltaMs = ((moveEvent.clientX - initialX) / rect.width) * domainSpan;
      let nextStart = initialStart + deltaMs;
      let nextEnd = initialEnd + deltaMs;
      if (nextStart < domainStart) {
        nextStart = domainStart;
        nextEnd = domainStart + selectionSpan;
      }
      if (nextEnd > domainEnd) {
        nextEnd = domainEnd;
        nextStart = domainEnd - selectionSpan;
      }
      emitSelection(normalizeSelection(Math.round(nextStart), Math.round(nextEnd)));
    }

    attachDrag(handleMove, () => {});
  }

  function dismissBrush() {
    setBrush(null);
    onDismiss();
  }

  function confirmBrush() {
    if (!hasValidBrush) return;
    const nextRange = { type: 'absolute', from: brush.start, to: brush.end };
    setBrush(null);
    onConfirm(nextRange);
  }

  useEffectR(() => {
    if (!brush) return undefined;
    function handleKeyDown(event) {
      if (event.key === 'Escape') dismissBrush();
    }
    document.addEventListener('keydown', handleKeyDown);
    return () => document.removeEventListener('keydown', handleKeyDown);
  }, [brush]);

  if (!brush) return null;

  const startPercent = ((brush.start - domainStart) / domainSpan) * 100;
  const endPercent = ((brush.end - domainStart) / domainSpan) * 100;
  const widthPercent = Math.max(0, endPercent - startPercent);
  const controlsOnLeft = endPercent > 86;
  const labelOnRight = startPercent > 72;

  return (
    <div className="run-chart-brush-layer" aria-hidden="false">
      <div className="run-chart-brush-shade" style={{ left: 0, width: `${startPercent}%` }}></div>
      <div className="run-chart-brush-shade" style={{ left: `${endPercent}%`, right: 0 }}></div>
      <div
        className="run-chart-brush-selection run-chart-brush-control"
        style={{ left: `${startPercent}%`, width: `${widthPercent}%` }}
        onMouseDown={beginMove}>
        <button
          type="button"
          className="run-chart-brush-handle run-chart-brush-handle-start run-chart-brush-control"
          aria-label="Resize selection start"
          onMouseDown={(event) => beginResize('start', event)}></button>
        <button
          type="button"
          className="run-chart-brush-handle run-chart-brush-handle-end run-chart-brush-control"
          aria-label="Resize selection end"
          onMouseDown={(event) => beginResize('end', event)}></button>
      </div>
      <div
        className={`run-chart-brush-actions run-chart-brush-control ${controlsOnLeft ? 'left' : 'right'}`}
        style={{ left: controlsOnLeft ? `${startPercent}%` : `${endPercent}%` }}>
        <Button size="icon" className="run-chart-brush-button" title="Zoom to selection" aria-label="Zoom to selection" onClick={confirmBrush}>
          <Icon name="zoomIn" size={14} />
        </Button>
        <Button size="icon" className="run-chart-brush-button" title="Dismiss selection" aria-label="Dismiss selection" onClick={dismissBrush}>
          <Icon name="x" size={14} />
        </Button>
      </div>
      <div
        className={`run-chart-brush-label ${labelOnRight ? 'right' : ''}`}
        style={{ left: labelOnRight ? `${endPercent}%` : `${startPercent}%` }}>
        {formatBrushRangeLabel(brush)}
      </div>
    </div>
  );
}

function getTimelineDomain(rows) {
  if (!rows.length) return null;
  const firstStart = parseDashboardTimeParam(rows[0]?.bucketStart);
  const lastEnd = parseDashboardTimeParam(rows[rows.length - 1]?.bucketEnd);
  if (firstStart === null || lastEnd === null || firstStart >= lastEnd) return null;
  return [firstStart, lastEnd];
}

function formatBrushRangeLabel(brush) {
  return `${formatCompactDateTime(brush.start)} - ${formatCompactDateTime(brush.end)}`;
}

function chartTooltipLabel(row) {
  return [
    formatBucketRange(row),
    `${formatNumber(row.totalRuns || 0)} total tries`,
    `${formatNumber(row.successfulRuns || 0)} successful`,
    `${formatNumber(row.partialRuns || 0)} partial`,
    `${formatNumber(row.failedRuns || 0)} failed`,
    `${formatNumber(row.activeRuns || 0)} running`,
    `${formatNumber(row.pendingRuns || 0)} pending`,
    `P50 duration ${formatDuration(row.p50DurationMs)}`,
  ].join(', ');
}

function formatBucketRange(row) {
  return `${formatDateTime(row.bucketStart)} - ${formatDateTime(row.bucketEnd)}`;
}

function formatAxisTime(value) {
  if (!value) return '-';
  const date = new Date(value);
  if (Number.isNaN(date.getTime())) return '-';
  return date
    .toLocaleString([], { month: 'short', day: 'numeric', hour: 'numeric' })
    .replace(',', '')
    .replace(/\s([AP]M)$/i, '$1');
}

function DashboardChartSkeleton() {
  return (
    <div className="chart-loading" aria-label="Loading run chart">
      {Array.from({ length: 18 }).map((_, index) => (
        <span key={index} className="skel chart-loading-bar" style={{ height: `${32 + ((index * 17) % 58)}%` }}></span>
      ))}
    </div>
  );
}

function FailingWorkflowsLoadingRows() {
  return (
    <>
      {Array.from({ length: 5 }).map((_, index) => (
        <div className="dash-failing-row loading-row" key={index}>
          <span className="skel skel-long"></span>
          <span className="skel skel-barwrap" style={{ width: '100%' }}></span>
          <span className="skel skel-short"></span>
        </div>
      ))}
    </>
  );
}

function DashboardRecentLoadingRows() {
  return (
    <>
      {Array.from({ length: 6 }).map((_, index) => (
        <div key={index} className="dash-recent-row loading-row">
          <span className="skel skel-badge"></span>
          <span className="skel skel-long" style={{ flex: 1 }}></span>
          <span className="skel skel-short"></span>
          <span className="skel skel-short"></span>
        </div>
      ))}
    </>
  );
}

function RunsPage({ navigate, queryString = '' }) {
  const initialFilters = useMemoR(() => {
    const sp = new URLSearchParams(queryString || '');
    return {
      workflow: sp.get('workflowId') || 'all',
      model: sp.get('modelId') || 'all',
      status: sp.get('status') || 'all',
      classification: (sp.get('classification') || '').trim(),
      range: sp.get('range') || '24h',
    };
  }, [queryString]);
  const [filters, setFilters] = useStateR(initialFilters);
  const [search, setSearch] = useStateR(() => {
    const sp = new URLSearchParams(queryString || '');
    return sp.get('search') || '';
  });
  const [page, setPage] = useStateR(1);
  useEffectR(() => {
    setFilters(initialFilters);
    const sp = new URLSearchParams(queryString || '');
    setSearch(sp.get('search') || '');
    setPage(1);
  }, [initialFilters, queryString]);
  const pageSize = 25;
  const workflows = useApiResource('/api/workflows');
  const catalog = useApiResource('/api/catalog/models');
  const params = new URLSearchParams({
    page: String(page),
    pageSize: String(pageSize),
    status: filters.status,
    range: filters.range,
    search,
  });
  if (filters.workflow !== 'all') params.set('workflowId', filters.workflow);
  if (filters.model !== 'all') params.set('modelId', filters.model);
  if (filters.classification) params.set('classification', filters.classification);
  const runs = useApiResource(`/api/runs?${params.toString()}`, [filters.workflow, filters.model, filters.status, filters.classification, filters.range, search, page]);
  const [archivedIds, setArchivedIds] = useStateR(() => new Set());
  const [archivingId, setArchivingId] = useStateR(null);
  const visibleRows = (runs.data?.rows || []).filter((r) => !archivedIds.has(r.id));
  const pagination = runs.data?.pagination || { page: 1, pageSize, total: 0, pageCount: 0 };
  const totalRuns = pagination.total || 0;
  const pageCount = Math.max(1, pagination.pageCount || Math.ceil(totalRuns / (pagination.pageSize || pageSize)) || 1);
  const currentPage = Math.min(page, pageCount);
  const rangeStart = totalRuns === 0 ? 0 : ((currentPage - 1) * (pagination.pageSize || pageSize)) + 1;
  const rows = runs.data?.rows || [];
  const rangeEnd = totalRuns === 0 ? 0 : Math.min(totalRuns, rangeStart + rows.length - 1);
  const showInitialRunsLoading = runs.loading && !runs.data;
  const setFilter = (k, v) => {
    setPage(1);
    setFilters(f => ({ ...f, [k]: v }));
  };

  async function archiveRun(e, runId) {
    e.stopPropagation();
    if (!window.confirm('Archive this run? It will be hidden from the run list and excluded from all stats.')) return;
    setArchivingId(runId);
    try {
      await apiFetch(`/api/runs/${runId}/archive`, { method: 'PATCH', body: JSON.stringify({ archived: true }) });
      setArchivedIds((prev) => new Set([...prev, runId]));
    } catch (err) {
      window.alert(`Archive failed: ${err.message}`);
    } finally {
      setArchivingId(null);
    }
  }

  const filtersDirty = filters.workflow !== 'all' || filters.model !== 'all' || filters.status !== 'all' || filters.range !== '24h' || Boolean(search);
  const refreshing = runs.loading && Boolean(runs.data);
  const workflowOptions = [
    { value: 'all', label: workflows.loading ? 'Loading…' : 'All workflows' },
    ...(workflows.data?.rows || []).map((w) => ({ value: w.id, label: w.name })),
  ];
  const modelOptions = [
    { value: 'all', label: catalog.loading ? 'Loading…' : 'All models' },
    ...(catalog.data?.models || []).map((m) => ({ value: m.id, label: m.display_name })),
  ];
  const statusOptions = [
    { value: 'all', label: 'All statuses' },
    { value: 'success', label: 'Success', icon: 'check' },
    { value: 'partial', label: 'Partial', icon: 'alert' },
    { value: 'failed', label: 'Failed', icon: 'alert' },
    { value: 'running', label: 'Running', icon: 'play' },
    { value: 'pending', label: 'Pending', icon: 'clock' },
  ];
  const rangeOptions = [
    { value: '1h', label: 'Last hour' },
    { value: '24h', label: 'Last 24 hours' },
    { value: '7d', label: 'Last 7 days' },
    { value: '30d', label: 'Last 30 days' },
    { value: 'all', label: 'All time' },
  ];

  return (
    <div className="page-inner runs-page">
      <div className="ui-page-head">
        <h1 className="ui-page-title">Run history</h1>
        <div className="ui-page-actions">
          <Button
            size="sm"
            variant="ghost"
            disabled={runs.loading && !runs.data}
            loading={refreshing}
            loadingLabel="Refreshing"
            onClick={runs.reload}>
            <Icon name="refresh" size={13} />Refresh
          </Button>
        </div>
      </div>

      <div className="runs-toolbar">
        <div className="runs-search">
          <Icon name="search" size={14} />
          <input
            placeholder="Search by run id, workflow, server…"
            value={search}
            onChange={(e) => { setPage(1); setSearch(e.target.value); }} />
          {search && (
            <button
              type="button"
              className="runs-search-clear"
              onClick={() => { setSearch(''); setPage(1); }}
              aria-label="Clear search">
              <Icon name="x" size={12} />
            </button>
          )}
        </div>
        <div className="runs-filters">
          <Select
            label="Workflow"
            value={filters.workflow}
            options={workflowOptions}
            disabled={workflows.loading}
            searchable
            onChange={(v) => setFilter('workflow', v)} />
          <Select
            label="Model"
            value={filters.model}
            options={modelOptions}
            disabled={catalog.loading}
            searchable
            onChange={(v) => setFilter('model', v)} />
          <Select
            label="Status"
            value={filters.status}
            options={statusOptions}
            onChange={(v) => setFilter('status', v)} />
          <Select
            label="Range"
            value={filters.range}
            options={rangeOptions}
            onChange={(v) => setFilter('range', v)} />
          {filtersDirty && (
            <Button
              size="sm"
              variant="ghost"
              onClick={() => {
                setSearch('');
                setPage(1);
                setFilters({ workflow: 'all', model: 'all', status: 'all', classification: 'all', range: '24h' });
              }}>
              <Icon name="x" size={11} />Clear
            </Button>
          )}
        </div>
        <div className="runs-toolbar-meta">
          <span className="mono">{formatNumber(totalRuns)}</span>
          <span className="muted text-xs">runs</span>
        </div>
      </div>

      <div className="runs-table-wrap">
        <table className="runs-table">
          <thead>
            <tr>
              <th className="col-status">Status</th>
              <th className="col-id">Run</th>
              <th>Workflow</th>
              <th>Harness</th>
              <th className="num">Duration</th>
              <th className="num">Tools</th>
              <th className="num">Tokens</th>
              <th>Started</th>
              <th className="col-actions"></th>
            </tr>
          </thead>
          <tbody>
            {showInitialRunsLoading ? (
              <RunsLoadingRows />
            ) : visibleRows.length === 0 ? (
              <tr className="runs-empty-row">
                <td colSpan={9}>
                  <EmptyState
                    icon="list"
                    title={runs.loading ? 'Loading runs…' : (filtersDirty ? 'No runs match these filters' : 'No runs yet')}
                    body={runs.error ? runs.error.message : (filtersDirty ? 'Try clearing filters or widening the range.' : 'Runs will appear here once a workflow executes.')} />
                </td>
              </tr>
            ) : visibleRows.map((run) => (
              <tr key={run.id} className="runs-row" onClick={() => navigate(`/runs/${run.id}`)}>
                <td className="col-status">
                  <span className="runs-status-cell">
                    <StatusBadge status={run.product_status} />
                    <ClassificationPill classification={run.evaluation_classification} />
                    <ScoreChip score={run.evaluation_score} />
                  </span>
                </td>
                <td className="col-id mono">{shortId(run.id)}</td>
                <td className="runs-row-workflow">
                  <span className="runs-row-name">{run.workflow_name || 'Untitled workflow'}</span>
                  <span className="runs-row-server text-xs muted">
                    <Icon name="mcp" size={10} />{run.mcp_server_name || 'MCP server'}
                  </span>
                </td>
                <td><HarnessCell sdkKey={run.tester_sdk_key} sdkName={run.tester_sdk_display_name} modelName={run.tester_model_name} /></td>
                <td className="num">{formatDuration(run.duration_ms)}</td>
                <td className="num">{run.tool_call_count}</td>
                <td className="num">{formatNumber(run.tokens_total)}</td>
                <td className="muted">{formatRelative(run.started_at || run.created_at)}</td>
                <td className="col-actions">
                  <button
                    type="button"
                    className="btn btn-sm btn-ghost runs-archive-btn"
                    disabled={archivingId === run.id}
                    title="Archive run"
                    onClick={(e) => archiveRun(e, run.id)}>
                    {archivingId === run.id ? <Icon name="refresh" size={13} /> : <Icon name="archive" size={13} />}
                  </button>
                </td>
              </tr>
            ))}
          </tbody>
        </table>
        {totalRuns > 0 && (
          <div className="runs-pagination">
            <span className="text-xs muted">
              {formatNumber(rangeStart)}–{formatNumber(rangeEnd)} of {formatNumber(totalRuns)}
            </span>
            <div className="runs-pagination-controls">
              <button
                className="btn btn-sm btn-ghost"
                title="First page"
                disabled={page <= 1}
                onClick={() => setPage(1)}>
                <Icon name="chevronsLeft" size={13} />
              </button>
              <button
                className="btn btn-sm btn-ghost"
                disabled={page <= 1}
                onClick={() => setPage((p) => Math.max(1, p - 1))}>
                <Icon name="chevronLeft" size={12} />Prev
              </button>
              <span className="runs-pagination-pill mono">{currentPage}/{pageCount}</span>
              <button
                className="btn btn-sm btn-ghost"
                disabled={page >= pageCount}
                onClick={() => setPage((p) => Math.min(pageCount, p + 1))}>
                Next<Icon name="chevronRight" size={12} />
              </button>
              <button
                className="btn btn-sm btn-ghost"
                title="Last page"
                disabled={page >= pageCount}
                onClick={() => setPage(pageCount)}>
                <Icon name="chevronsRight" size={13} />
              </button>
            </div>
          </div>
        )}
      </div>
    </div>
  );
}

const HARNESS_LOGOS = {
  codex: { src: '/frontend/assets/logos/codex.png', label: 'Codex' },
  claude: { src: '/frontend/assets/logos/claude-code.png', label: 'Claude Code' },
  anthropic_api: { src: '/frontend/assets/logos/anthropic.svg.png', label: 'Claude' },
  openai_api: { src: '/frontend/assets/logos/chatgpt.png', label: 'ChatGPT' },
  gemini: { src: '/frontend/assets/logos/gemini.png', label: 'Gemini CLI' },
  cursor: { src: '/frontend/assets/logos/cursor.png', label: 'Cursor' },
  openclaw: { src: '/frontend/assets/logos/openclaw.svg', label: 'Openclaw' },
  opencode: { src: '/frontend/assets/logos/opencode.svg', label: 'OpenCode' },
};

function HarnessCell({ sdkKey, sdkName, modelName }) {
  if (!sdkKey && !sdkName && !modelName) return <span className="muted text-xs">—</span>;
  const logo = sdkKey ? HARNESS_LOGOS[sdkKey] : null;
  const harnessLabel = logo?.label || sdkName || sdkKey || 'Harness';
  return (
    <span className="harness-cell">
      <span className="harness-cell-glyph" aria-hidden>
        {logo
          ? <img src={logo.src} alt="" className="harness-cell-logo" width="14" height="14" />
          : <Icon name="cpu" size={12} />}
      </span>
      <span className="harness-cell-name">{harnessLabel}</span>
      {modelName && <span className="harness-cell-model mono">{displayModelName(modelName)}</span>}
    </span>
  );
}

function RunsLoadingRows() {
  return (
    <>
      {Array.from({ length: 8 }).map((_, index) => (
        <tr key={index} className="loading-row">
          <td><span className="skel skel-badge"></span></td>
          <td><span className="skel skel-mid"></span></td>
          <td><span className="skel skel-long"></span></td>
          <td><span className="skel skel-mid"></span></td>
          <td><span className="skel skel-short"></span></td>
          <td><span className="skel skel-short"></span></td>
          <td><span className="skel skel-mid"></span></td>
          <td><span className="skel skel-mid"></span></td>
        </tr>
      ))}
    </>
  );
}

function RunDetailPage({ runId, navigate }) {
  const toast = useToast();
  const auth = useAuth();
  // Per-org `mcp_design_insights` flag (resolved server-side in
  // /api/me from `organization_feature_flags`). When OFF we don't
  // render the "MCP design insights" card and we skip the
  // /api/runs/:id/analysis fetch + polling loop entirely — the
  // backend gate would 404 the call and pollute the network tab
  // with useless errors otherwise.
  const mcpDesignInsightsEnabled = auth?.me?.featureFlags?.mcpDesignInsights === true;
  const [activeTab, setActiveTab] = useStateR('overview');
  const [traceFilter, setTraceFilter] = useStateR('');
  const [toolWaterfallExpanded, setToolWaterfallExpanded] = useStateR(false);
  const [workflowPeek, setWorkflowPeek] = useStateR(null);
  const runResource = useApiResource(`/api/runs/${runId}`, [runId]);
  const traceResource = useApiResource(`/api/runs/${runId}/trace`, [runId]);
  const toolsResource = useApiResource(`/api/runs/${runId}/tool-calls`, [runId]);
  const evalResource = useApiResource(`/api/runs/${runId}/evaluation`, [runId]);
  const analysisResource = useApiResource(
    mcpDesignInsightsEnabled ? `/api/runs/${runId}/analysis` : null,
    [runId, mcpDesignInsightsEnabled],
  );
  const run = runResource.data?.run;
  const traceHiddenFromRun = run?.product_status === 'system_issue' || run?.evaluation_classification === 'product_bug';
  const traceHidden = traceHiddenFromRun || Boolean(traceResource.data?.traceHidden);
  const traceHiddenMessage = traceResource.data?.message ?? SYSTEM_ISSUE_TRACE_HIDDEN_MESSAGE;
  const rawTraceEvents = traceResource.data?.traceEvents || [];
  const traceEvents = traceHidden ? [] : rawTraceEvents;
  const traceEmptyMessage = traceHidden
    ? traceHiddenMessage
    : traceResource.loading
      ? 'Loading trace…'
      : (traceFilter ? 'No matching events.' : 'No trace data captured for this run.');
  const toolCalls = toolsResource.data?.rows || [];
  const evaluation = evalResource.data?.evaluation;
  const criteriaResults = evalResource.data?.criteriaResults || [];
  const testerTimedOutWithoutEvaluation =
    run?.status === 'timed_out' && run?.error_code === 'tester_orphaned' && !evaluation;
  const hideEvaluationSummary = run?.product_status === 'system_issue' || run?.evaluation_classification === 'product_bug';
  const analysis = analysisResource.data?.analysis || null;
  const analysisPending = analysis ? ['queued', 'running'].includes(analysis.status) : false;
  // After a non-benchmark run finalizes, the evaluator enqueues a
  // run_analysis SQS job. There is a latency gap between that
  // enqueue and the worker INSERTing the run_analyses row. During
  // that gap the API returns `analysis: null` — the same shape it
  // returns for runs that were skipped (benchmark / no activity).
  // If we only polled while `analysisPending` is true we'd stop
  // polling immediately on finalize, land on the "No analysis"
  // empty state, and require a manual refresh to recover. To bridge
  // the gap we keep polling for a bounded period after finalize:
  // - benchmark runs: never poll for analysis (they get
  //   benchmark_recaps instead, and the API would always return
  //   null for them).
  // - skipped/failed/completed runs without a row: poll until we
  //   either receive a row OR the run is older than
  //   `ANALYSIS_LATENCY_GRACE_MS`. Production SQS worker latency is
  //   well under this in practice.
  const isBenchmarkRunForPolling = Boolean(run?.benchmark_batch_id);
  const runFinishedAt = run?.completed_at ? new Date(run.completed_at).getTime() : null;
  const withinAnalysisGracePeriod =
    runFinishedAt !== null && Date.now() - runFinishedAt < ANALYSIS_LATENCY_GRACE_MS;
  const awaitingFirstAnalysisRow =
    mcpDesignInsightsEnabled && !analysis && !isBenchmarkRunForPolling && withinAnalysisGracePeriod;
  const traceBounds = useMemoR(() => getTraceTimeBounds(run, traceEvents, toolCalls), [run, traceEvents, toolCalls]);
  const toolCallByTraceEventId = useMemoR(() => getToolCallByTraceEventId(toolCalls), [toolCalls]);
  const displayedTraceEvents = useMemoR(() => dedupeTraceEvents(traceEvents), [traceEvents]);
  const filteredTraceEvents = useMemoR(() => {
    if (!traceFilter.trim()) return displayedTraceEvents;
    const q = traceFilter.toLowerCase();
    return displayedTraceEvents.filter((e) => {
      const summary = (e.summary || '').toLowerCase();
      const name = (e.span_name || e.name || e.event_type || '').toLowerCase();
      const projected = customerTraceSearchText(e);
      return summary.includes(q) || name.includes(q) || projected.includes(q);
    });
  }, [displayedTraceEvents, traceFilter]);
  const runIsActive = run?.status && RUN_ACTIVE_STATUSES.has(run.status);
  // Continue polling while the AI analysis is still queued/running so
  // the card flips from "Analyzing…" to the rendered findings without
  // the user having to refresh. The analysis is enqueued AFTER the
  // run finalizes, so this can extend the poll window past `runIsActive`.
  // `awaitingFirstAnalysisRow` covers the SQS latency gap between
  // finalize and the worker creating the row.
  const shouldPoll = runIsActive || analysisPending || awaitingFirstAnalysisRow;

  useEffectR(() => {
    if (!runId || !shouldPoll) return undefined;

    function reloadActiveRunDetail() {
      if (document.visibilityState !== 'hidden') {
        if (runIsActive) {
          runResource.reload();
          traceResource.reload();
          toolsResource.reload();
          evalResource.reload();
        }
        analysisResource.reload();
      }
    }

    const timer = window.setInterval(reloadActiveRunDetail, RUN_DETAIL_ACTIVE_REFRESH_MS);
    document.addEventListener('visibilitychange', reloadActiveRunDetail);
    return () => {
      window.clearInterval(timer);
      document.removeEventListener('visibilitychange', reloadActiveRunDetail);
    };
  }, [runId, shouldPoll, runIsActive, runResource.reload, traceResource.reload, toolsResource.reload, evalResource.reload, analysisResource.reload, awaitingFirstAnalysisRow]);

  async function copyId() {
    if (!runId) return;
    try {
      await navigator.clipboard.writeText(runId);
      toast.show({ tone: 'ok', title: 'Run ID copied' });
    } catch (_e) {
      toast.show({ tone: 'bad', title: 'Copy failed', description: 'Select the run ID text manually.' });
    }
  }

  const runTargetKind = run?.mcp_server_target_kind === 'cli' ? 'cli' : 'mcp';
  const runTargetLabel = runTargetKind === 'cli' ? 'CLI' : 'MCP';
  const runTargetIcon = runTargetKind === 'cli' ? 'terminal' : 'mcp';
  const runCostLabel = run?.cost_usd === null || run?.cost_usd === undefined ? '' : formatCost(run.cost_usd);
  const tabs = [
    { key: 'overview', label: 'Overview', icon: 'activity' },
    { key: 'trace', label: 'Trace', icon: 'list', count: displayedTraceEvents.length || null },
    { key: 'snapshot', label: 'Snapshot', icon: 'layers' },
  ];

  // Benchmark runs deep-link back to their batch instead of /runs (which
  // intentionally hides them). The pill makes it obvious this isn't a
  // production monitoring run, so users don't second-guess why it's missing
  // from dashboards/alerts.
  //
  // Batch detail is rendered inline under the workflow detail page
  // (`/benchmark/workflows/:workflowId?batch=:batchId`) — the standalone
  // `/benchmark/batches/:id` route was removed when the benchmark UI was
  // restructured. All deep links from this page must use the inline form.
  const benchmarkBatchId = run?.benchmark_batch_id || null;
  const isBenchmarkRun = Boolean(benchmarkBatchId);
  const benchmarkWorkflowHref = run?.workflow_id ? `/benchmark/workflows/${run.workflow_id}` : null;
  const benchmarkBatchHref = benchmarkBatchId && benchmarkWorkflowHref
    ? `${benchmarkWorkflowHref}?batch=${benchmarkBatchId}`
    : null;

  return (
    <div className="page-inner run-detail-page">
      <div className="run-detail-back">
        {benchmarkBatchHref ? (
          <button
            type="button"
            className="btn btn-ghost btn-sm"
            onClick={() => navigate(benchmarkBatchHref)}>
            <Icon name="chevronLeft" size={13} />Back to batch
          </button>
        ) : (
          <button
            type="button"
            className="btn btn-ghost btn-sm"
            onClick={() => navigate('/runs')}>
            <Icon name="chevronLeft" size={13} />All runs
          </button>
        )}
      </div>

      <div className="window run-detail-window">
        <div className="window-head">
          <span>RUN</span>
          <span className="window-head-url">/runs/{runId || '—'}</span>
        </div>
        <header className="run-detail-hero">
          <StatusBadge status={run?.product_status || 'pending'} />
          <div className="run-detail-title-block">
            {run?.workflow_id ? (
              <button
                type="button"
                className="run-detail-workflow-link"
                onClick={() => setWorkflowPeek({ id: run.workflow_id, name: run.workflow_name })}
                title="Open workflow details">
                <span>{run.workflow_name || 'Untitled workflow'}</span>
                <Icon name="chevronRight" size={11} />
              </button>
            ) : (
              <span className="run-detail-title">
                {run?.workflow_name || (runResource.loading ? 'Loading run…' : 'Run detail')}
              </span>
            )}
            <span className="run-detail-id mono" title={runId || ''}>{shortId(runId) || '—'}</span>
            {benchmarkBatchId && (
              <span
                className="ui-pill ui-pill-warn"
                title="This run is part of a benchmark batch and is excluded from production dashboards/alerts."
                style={{ marginLeft: 8 }}>
                Benchmark
              </span>
            )}
          </div>
          <div className="run-detail-actions">
            {benchmarkBatchHref && (
              <Button
                size="sm"
                variant="ghost"
                onClick={() => navigate(benchmarkBatchHref)}>
                <Icon name="chart" size={13} />Open batch
              </Button>
            )}
            <Button size="sm" variant="ghost" onClick={copyId}>
              <Icon name="copy" size={13} />Copy ID
            </Button>
          </div>
        </header>

        <div className="run-detail-meta">
          <RunMeta label={runTargetLabel} icon={runTargetIcon} value={run?.mcp_server_name} sub={run?.mcp_environment} />
          <RunHarnessModelMeta run={run} />
          <RunMeta label="Started" value={formatDateTime(run?.started_at)} sub={formatRelative(run?.started_at)} />
          <RunMeta label="Duration" value={formatDuration(run?.duration_ms)} sub={run?.status || ''} />
          <RunMeta label="Tokens" value={formatNumber(run?.tokens_total)} sub={runCostLabel} />
          <RunMeta label="Tools" value={String(toolCalls.length || 0)} sub="" />
        </div>
      </div>

      <div className="run-detail-tabs">
        {tabs.map((t) => (
          <button
            key={t.key}
            type="button"
            className={`run-detail-tab ${activeTab === t.key ? 'active' : ''}`}
            onClick={() => setActiveTab(t.key)}>
            <Icon name={t.icon} size={13} />
            <span>{t.label}</span>
            {t.count != null && <span className="run-detail-tab-count">{t.count}</span>}
          </button>
        ))}
      </div>

      <div className="run-detail-pane">
        {activeTab === 'overview' && (
          <div className="run-detail-overview ui-stack-4">
            <div className="run-detail-card">
              <div className="ui-section-head">
                <div className="ui-section-title">Verdict</div>
                {criteriaResults.length > 0 && (
                  <span className="text-xs muted">{criteriaResults.length} criteria</span>
                )}
              </div>
              {evaluation ? (
                <>
                  <div className={`verdict ${verdictTone(run?.product_status, run?.evaluation_score)}`}>
                    <div className="verdict-icon"><Icon name={verdictIcon(run?.product_status, run?.evaluation_score)} size={16} /></div>
                    <div>
                      <div className="verdict-title">
                        {verdictHeadline(run?.product_status, run?.evaluation_score)}
                      </div>
                      {hideEvaluationSummary && (
                        <div className="verdict-system muted">
                          Evaluation unavailable — we're investigating. This run is excluded from your quality metrics.
                        </div>
                      )}
                      {!hideEvaluationSummary && evaluation.summary && (
                        <div className="verdict-body">{evaluation.summary}</div>
                      )}
                      <div className="verdict-meta mono">{formatTraceTimestamp(evaluation.created_at)}</div>
                    </div>
                  </div>
                  {criteriaResults.length === 0 ? (
                    <div className="criteria-empty">No criteria results captured.</div>
                  ) : (
                    <div className="criteria-results">
                      <h4 className="section-heading">Criteria</h4>
                      <table className="criteria-table">
                        <thead>
                          <tr>
                            <th>Criterion</th>
                            <th>Status</th>
                            <th>Reason</th>
                          </tr>
                        </thead>
                        <tbody>
                          {criteriaResults.map((row) => (
                            <tr key={row.workflow_criterion_id}>
                              <td>
                                {row.criterion_text}
                                {row.is_required && <span className="badge badge-required">required</span>}
                              </td>
                              <td>
                                <span className={`ui-pill pill-${criterionTone(row.status)}`}>{row.status}</span>
                              </td>
                              <td className="criterion-reason">{row.explanation}</td>
                            </tr>
                          ))}
                        </tbody>
                      </table>
                    </div>
                  )}
                </>
              ) : (
                <div className="judge-empty">
                  {testerTimedOutWithoutEvaluation
                    ? 'Timed out after 15m before evaluation. The tester trace is still available.'
                    : (evalResource.loading ? 'Loading verdict…' : 'No verdict captured for this run.')}
                </div>
              )}
            </div>

            {mcpDesignInsightsEnabled && (
              <RunAnalysisCard
                analysis={analysis}
                loading={analysisResource.loading && !analysisResource.data}
                error={analysisResource.error}
                runId={runId}
                isBenchmarkRun={isBenchmarkRun}
                runIsActive={runIsActive}
                awaitingFirstRow={awaitingFirstAnalysisRow}
                onJumpToEvidence={(ref) => setActiveTab('trace')}
                reload={analysisResource.reload}
                toast={toast} />
            )}

            <RunConfigurationCard run={run} navigate={navigate} />

            <div className="run-detail-card">
              <div className="ui-section-head">
                <div className="ui-section-title">
                  Tool calls
                  {toolCalls.length > 0 && <span className="ui-section-count">{toolCalls.length}</span>}
                </div>
              </div>
              {toolCalls.length === 0 ? (
                <div className="run-context-empty">{toolsResource.loading ? 'Loading tool calls…' : 'No tools were called.'}</div>
              ) : (
                <>
                  <ToolCallWaterfall
                    toolCalls={toolCalls}
                    traceBounds={traceBounds}
                    expanded={toolWaterfallExpanded}
                    onToggleExpanded={() => setToolWaterfallExpanded((value) => !value)} />
                </>
              )}
            </div>

            <div className="run-detail-card">
              <div className="ui-section-head">
                <div className="ui-section-title">Run metadata</div>
              </div>
              <div className="metadata-strip">
                <MetaItem label="Run" value={shortId(run?.id)} title={run?.id || ''} />
                <MetaItem label="Trace" value={run?.trace_id || '—'} />
                <MetaItem label="Trigger" value={run?.trigger_type || '—'} />
                <MetaItem label="Version" value={run?.workflow_version_number ? `v${run.workflow_version_number}` : '—'} />
                <MetaItem label="Error" value={run?.error_code || '—'} tone={run?.error_code ? 'fail' : ''} />
              </div>
            </div>
          </div>
        )}

        {activeTab === 'trace' && (
          <div className="run-detail-trace">
            <div className="trace-toolbar">
              <div className="search-input run-detail-trace-search">
                <Icon name="search" size={13} />
                <input
                  placeholder="Filter trace by name, payload…"
                  value={traceFilter}
                  onChange={(e) => setTraceFilter(e.target.value)} />
                {traceFilter && (
                  <button
                    type="button"
                    className="runs-search-clear"
                    onClick={() => setTraceFilter('')}
                    aria-label="Clear filter">
                    <Icon name="x" size={11} />
                  </button>
                )}
              </div>
              <span className="text-xs muted mono trace-range">
                {formatTraceTimestamp(traceBounds.startIso)} → {formatTraceTimestamp(traceBounds.endIso)}
              </span>
            </div>
            <div className="trace">
              <div className="trace-header">
                <div></div>
                <div>Span context</div>
                <div>Timestamp</div>
                <div style={{ textAlign: 'right' }}>Seq</div>
                <div className="trace-axis">
                  <span>{formatTraceOffset(traceBounds.startMs, traceBounds.startMs)}</span>
                  <span>{formatTraceOffset(traceBounds.startMs + traceBounds.durationMs / 2, traceBounds.startMs)}</span>
                  <span>{formatTraceOffset(traceBounds.endMs, traceBounds.startMs)}</span>
                </div>
              </div>
              {filteredTraceEvents.length === 0 ? (
                <div className="trace-empty">
                  {traceEmptyMessage}
                </div>
              ) : filteredTraceEvents.map((event) => (
                <TraceEventRow
                  key={event.id}
                  event={event}
                  traceBounds={traceBounds}
                  toolCall={toolCallByTraceEventId[event.id]} />
              ))}
            </div>
          </div>
        )}

        {activeTab === 'snapshot' && (
          <div className="run-detail-card run-detail-snapshot">
            <div className="ui-section-head">
              <div className="ui-section-title">Workflow snapshot</div>
              <span className="text-xs muted">at run time · {run?.workflow_version_number ? `v${run.workflow_version_number}` : 'unversioned'}</span>
            </div>
            <div className="field">
              <div className="field-label">Tester prompt</div>
              <pre className="code run-detail-pre">{run?.tester_prompt || 'No prompt snapshot'}</pre>
            </div>
            <div className="field" style={{ marginTop: 14 }}>
              <div className="field-label">Success criteria</div>
              <pre className="code run-detail-pre">{JSON.stringify(run?.success_criteria_snapshot || [], null, 2)}</pre>
            </div>
          </div>
        )}
      </div>

      <Drawer
        open={Boolean(workflowPeek)}
        onClose={() => setWorkflowPeek(null)}
        title={workflowPeek?.name || 'Workflow'}
        subtitle="Quick view"
        footer={(
          <>
            <span className="spacer" />
            <Button
              variant="primary"
              size="sm"
              onClick={() => {
                if (!workflowPeek?.id) return;
                navigate(isBenchmarkRun
                  ? `/benchmark/workflows/${workflowPeek.id}`
                  : `/workflows/${workflowPeek.id}`);
              }}>
              Open workflow
              <Icon name="chevronRight" size={11} />
            </Button>
          </>
        )}>
        {workflowPeek?.id && <WorkflowPeekContents workflowId={workflowPeek.id} navigate={navigate} isBenchmark={isBenchmarkRun} />}
      </Drawer>
    </div>
  );
}

function WorkflowPeekContents({ workflowId, navigate, isBenchmark = false }) {
  // Benchmark workflows are 404'd by the production GET handler as defense
  // in depth — fetch them through the benchmark surface instead so the
  // peek drawer keeps working when the parent run is a benchmark cell.
  const workflowApiPath = isBenchmark
    ? `/api/benchmark/workflows/${workflowId}`
    : `/api/workflows/${workflowId}`;
  const wf = useApiResource(workflowApiPath, [workflowApiPath]);
  const recent = useApiResource(`/api/runs?workflowId=${workflowId}&pageSize=5`, [workflowId]);
  if (wf.loading && !wf.data) {
    return <div className="muted text-sm">Loading…</div>;
  }
  if (wf.error) {
    return <div className="error-text text-sm">{wf.error.message}</div>;
  }
  const workflow = wf.data?.workflow;
  if (!workflow) return <div className="muted text-sm">Workflow not found.</div>;
  const schedule = wf.data?.schedule;
  const lastRun = wf.data?.recentHistory?.[0];
  const rows = recent.data?.rows || [];
  const scheduleLabelText = formatScheduleSummary(schedule);
  return (
    <div className="ui-stack-5">
      <div className="ui-col-3">
        <div>
          <div className="field-label">Schedule</div>
          <div className="text-sm">{scheduleLabelText}</div>
        </div>
        <div>
          <div className="field-label">Last run</div>
          <div className="text-sm">{lastRun ? formatRelative(lastRun.started_at || lastRun.created_at) : '—'}</div>
        </div>
      </div>
      <div>
        <div className="ui-section-head">
          <div className="ui-section-title">Recent runs</div>
          <a
            href={`/runs?workflowId=${workflowId}`}
            className="dash-link"
            onClick={(e) => { e.preventDefault(); navigate(`/runs?workflowId=${workflowId}`); }}>
            All<Icon name="chevronRight" size={11} />
          </a>
        </div>
        {rows.length === 0 ? (
          <div className="muted text-sm">No runs yet.</div>
        ) : (
          <div className="ui-col">
            {rows.map((r) => (
              <button
                key={r.id}
                type="button"
                className="dash-recent-row"
                onClick={() => navigate(`/runs/${r.id}`)}>
                <StatusBadge status={r.product_status} />
                <span className="dash-recent-name mono">{shortId(r.id)}</span>
                <span className="dash-recent-meta mono">{formatDuration(r.duration_ms)}</span>
                <span className="dash-recent-time muted text-xs">{formatRelative(r.started_at || r.created_at)}</span>
              </button>
            ))}
          </div>
        )}
      </div>
    </div>
  );
}

function RunHarnessModelMeta({ run }) {
  const logo = run?.tester_sdk_key ? HARNESS_LOGOS[run.tester_sdk_key] : null;
  const harnessLabel = logo?.label || run?.tester_sdk_display_name || run?.tester_sdk_key || '—';
  const modelLabel = displayModelName(run?.tester_model_name) || '—';
  return (
    <div className="run-meta-cell run-meta-harness-model">
      <div className="run-meta-label">
        <Icon name="cpu" size={11} />
        Harness / Model
      </div>
      <div className="run-meta-harness-line">
        <span className="run-meta-harness-glyph" aria-hidden>
          {logo
            ? <img src={logo.src} alt="" className="run-meta-harness-logo" width="14" height="14" />
            : <Icon name="cpu" size={12} />}
        </span>
        <span className="run-meta-value">{harnessLabel}</span>
      </div>
      <div className="run-meta-model-line">
        <Icon name="brain" size={11} />
        <span>{modelLabel}</span>
      </div>
    </div>
  );
}

function RunMeta({ label, value, sub, icon = null }) {
  return (
    <div className="run-meta-cell">
      <div className="run-meta-label">
        {icon ? <Icon name={icon} size={11} /> : null}
        {label}
      </div>
      <div className="run-meta-value">{value || '—'}</div>
      {sub ? <div className="run-meta-sub">{sub}</div> : null}
    </div>
  );
}

function RunConfigurationCard({ run, navigate }) {
  // Replaces the small per-cell drawer that used to surface harness +
  // pinned-target identity for benchmark runs only. The same fields are
  // populated for traditional production runs (fetchRun joins the SDK
  // catalog regardless of benchmark_batch_id), so this card renders
  // identically on /runs/:id whether the run came from a schedule, a
  // manual trigger, or a benchmark batch cell.
  //
  // Benchmark cells (`benchmark_batch_id` set) MUST route the workflow
  // and batch links to the benchmark surface. The production
  // `/workflows/:id` page exposes scheduling, archive, and other prod
  // settings that benchmark workflows must never get — this is the
  // user-facing half of the defense-in-depth fix that also gates the
  // production GET /api/workflows/:id by is_benchmark.
  const harnessLabel = run?.tester_sdk_display_name || run?.tester_sdk_key || null;
  const versionLabel = run?.workflow_version_number ? `v${run.workflow_version_number}` : null;
  const canLinkWorkflow = Boolean(run?.workflow_id);
  const benchmarkBatchId = run?.benchmark_batch_id || null;
  const isBenchmarkRun = Boolean(benchmarkBatchId);
  const workflowHref = run?.workflow_id
    ? (isBenchmarkRun
      ? `/benchmark/workflows/${run.workflow_id}`
      : `/workflows/${run.workflow_id}`)
    : null;
  const batchHref = benchmarkBatchId && run?.workflow_id
    ? `/benchmark/workflows/${run.workflow_id}?batch=${benchmarkBatchId}`
    : null;
  return (
    <div className="run-detail-card">
      <div className="ui-section-head">
        <div className="ui-section-title">Configuration</div>
        {isBenchmarkRun && (
          <span
            className="ui-pill ui-pill-warn"
            title="This run is part of a benchmark batch and is excluded from production dashboards/alerts.">
            Benchmark cell
          </span>
        )}
      </div>
      <div className="run-config-harness">
        <HarnessCell
          sdkKey={run?.tester_sdk_key}
          sdkName={harnessLabel}
          modelName={run?.tester_model_name} />
      </div>
      <div className="metadata-strip">
        <MetaItem label="MCP" value={run?.mcp_server_name || '—'} sub={run?.mcp_environment || ''} title={run?.mcp_server_name || ''} />
        <MetaItem label="Tester model" value={displayModelName(run?.tester_model_name) || '—'} />
        <MetaItem label="Workflow" value={run?.workflow_name || '—'} title={run?.workflow_name || ''} />
        <MetaItem label="Version" value={versionLabel || '—'} />
        <MetaItem label="Trigger" value={run?.trigger_type || '—'} />
      </div>
      {(canLinkWorkflow || batchHref) && (
        <div className="run-config-links">
          {canLinkWorkflow && workflowHref && (
            <Button size="sm" variant="ghost" onClick={() => navigate(workflowHref)}>
              <Icon name="chevronRight" size={11} />Open workflow
            </Button>
          )}
          {batchHref && (
            <Button size="sm" variant="ghost" onClick={() => navigate(batchHref)}>
              <Icon name="chart" size={11} />Open batch
            </Button>
          )}
        </div>
      )}
    </div>
  );
}

// Renders the per-run MCP-developer-insights AI analysis. Distinct from
// the Verdict card above: the verdict says "did the agent succeed?";
// this card says "what does this run teach the MCP developer about
// their tool surface?". Fields are populated by
// workers/src/handlers/run-analysis.ts -> details.
function RunAnalysisCard({ analysis, loading, error, runId, isBenchmarkRun, runIsActive, awaitingFirstRow, onJumpToEvidence, reload, toast }) {
  const [regenerating, setRegenerating] = useStateR(false);
  // After a manual "Generate analysis" / "Regenerate" click we need
  // to keep polling until the worker has had a chance to insert and
  // then complete the row. The parent component's polling loop
  // doesn't help here: it only fires while the run is active, the
  // analysis row is in `queued`/`running`, or the run was finalized
  // within `ANALYSIS_LATENCY_GRACE_MS`. Right after a manual click on
  // an older run all three are false (the analysis is still null),
  // polling stops, and the card sits on the "No analysis" empty
  // state forever even though the worker is busy. The local poll
  // window below is the bridge.
  const [pollUntil, setPollUntil] = useStateR(0);
  const MANUAL_POLL_WINDOW_MS = 3 * 60 * 1000;
  const MANUAL_POLL_INTERVAL_MS = 3000;
  // Stash `reload` in a ref so the polling effect doesn't tear the
  // interval down and recreate it every time `useApiResource` returns
  // a fresh callback identity. `useApiResource` memoizes `reload`
  // today, but we don't want a future change to its dep list to
  // silently churn this interval (and risk missing a tick around the
  // teardown/recreate boundary).
  const reloadRef = useRefR(reload);
  useEffectR(() => { reloadRef.current = reload; }, [reload]);

  useEffectR(() => {
    if (!pollUntil) return undefined;
    if (Date.now() >= pollUntil) {
      setPollUntil(0);
      return undefined;
    }
    // Stop early once the worker has produced (or failed to produce)
    // a final answer. `queued`/`running` keep us polling.
    if (analysis && ['completed', 'failed', 'skipped'].includes(analysis.status)) {
      setPollUntil(0);
      return undefined;
    }
    const intervalId = window.setInterval(() => {
      if (Date.now() >= pollUntil) {
        window.clearInterval(intervalId);
        setPollUntil(0);
        return;
      }
      reloadRef.current();
    }, MANUAL_POLL_INTERVAL_MS);
    return () => window.clearInterval(intervalId);
  }, [pollUntil, analysis?.status]);

  async function handleRegenerate() {
    setRegenerating(true);
    try {
      await apiFetch(`/api/runs/${runId}/analysis`, { method: 'POST' });
      toast.show({ tone: 'ok', title: 'Analyzing run', description: 'A fresh MCP design analysis is being generated.' });
      // Kick off the local poll window. The first reload happens
      // immediately for snappy feedback; the interval (above) takes
      // over for the next ~3 minutes or until the analysis settles.
      reload();
      setPollUntil(Date.now() + MANUAL_POLL_WINDOW_MS);
    } catch (err) {
      toast.show({ tone: 'bad', title: 'Could not regenerate', description: err?.message || 'Unknown error.' });
    } finally {
      setRegenerating(false);
    }
  }

  const Header = ({ children = null }) => (
    <div className="ui-section-head">
      <div className="ui-section-title">
        <Icon name="sparkle" size={13} style={{ marginRight: 6 }} />
        MCP design insights
      </div>
      <div style={{ display: 'flex', gap: 8, alignItems: 'center' }}>{children}</div>
    </div>
  );

  if (loading) {
    return (
      <div className="run-detail-card">
        <Header />
        <div className="run-context-empty">Loading analysis…</div>
      </div>
    );
  }

  if (error) {
    return (
      <div className="run-detail-card">
        <Header />
        <div className="run-context-empty">Could not load analysis: {error.message || 'unknown error'}</div>
      </div>
    );
  }

  // No row yet — five sub-cases:
  //   1. Benchmark run (never gets a row, surfaced in batch recap).
  //   2. Run is still active (will get a row once it finalizes).
  //   3. Run just finalized and the SQS worker hasn't inserted the
  //      row yet (`awaitingFirstRow`). We show the analyzing state
  //      so the user doesn't think analysis was skipped during the
  //      latency gap. (Mostly historical now that auto-enqueue is
  //      off — kept so a future opt-in path can rely on it.)
  //   4. User clicked "Generate analysis" but the worker hasn't yet
  //      inserted the row (`pollUntil` set). Same UX as case 3.
  //   5. Historical run / never analyzed — offer manual Generate.
  const manualPollActive = pollUntil !== 0 && Date.now() < pollUntil;
  if (!analysis) {
    return (
      <div className="run-detail-card">
        <Header>
          {!isBenchmarkRun && !runIsActive && !awaitingFirstRow && !manualPollActive && (
            <Button size="sm" variant="ghost" onClick={handleRegenerate} disabled={regenerating}>
              <Icon name="sparkle" size={11} />{regenerating ? 'Queuing…' : 'Generate analysis'}
            </Button>
          )}
        </Header>
        <div className="run-context-empty">
          {isBenchmarkRun
            ? 'Benchmark runs are summarized in the batch comparison instead.'
            : runIsActive
              ? 'Analysis will appear here once the run finishes.'
              : awaitingFirstRow || manualPollActive
                ? 'Analyzing the run for MCP design insights…'
                : 'No analysis has been generated for this run yet.'}
        </div>
      </div>
    );
  }

  if (analysis.status === 'queued' || analysis.status === 'running') {
    return (
      <div className="run-detail-card">
        <Header />
        <div className="run-context-empty">Analyzing the run for MCP design insights…</div>
      </div>
    );
  }

  if (analysis.status === 'failed') {
    return (
      <div className="run-detail-card">
        <Header>
          <Button size="sm" variant="ghost" onClick={handleRegenerate} disabled={regenerating}>
            <Icon name="refresh" size={11} />{regenerating ? 'Retrying…' : 'Retry'}
          </Button>
        </Header>
        <div className="run-context-empty">
          Analysis failed: {analysis.error_message || 'unknown error'}
        </div>
      </div>
    );
  }

  if (analysis.status === 'skipped') {
    return (
      <div className="run-detail-card">
        <Header />
        <div className="run-context-empty">
          {analysis.headline || 'No tool activity to analyze for this run.'}
        </div>
      </div>
    );
  }

  // status === 'completed'
  const details = analysis.details || {};
  const findings = Array.isArray(details.findings) ? details.findings : [];
  const idealPath = details.ideal_tool_path || null;
  const positiveSignals = Array.isArray(details.positive_signals) ? details.positive_signals : [];
  const agentJourney = typeof details.agent_journey === 'string' ? details.agent_journey : '';

  return (
    <div className="run-detail-card">
      <Header>
        <span className="text-xs muted">{formatRelative(analysis.updated_at || analysis.created_at)}</span>
        <Button size="sm" variant="ghost" onClick={handleRegenerate} disabled={regenerating}>
          <Icon name="refresh" size={11} />{regenerating ? 'Regenerating…' : 'Regenerate'}
        </Button>
      </Header>

      {analysis.headline && (
        <div className="run-analysis-headline">{analysis.headline}</div>
      )}
      {agentJourney && (
        <p className="run-analysis-journey text-sm muted">{agentJourney}</p>
      )}

      {idealPath && (
        <div className="run-analysis-ideal">
          <div className="run-analysis-ideal__stat">
            <span className="run-analysis-ideal__num">{numberOr(idealPath.tool_calls_used, '—')}</span>
            <span className="run-analysis-ideal__label">tool calls used</span>
          </div>
          <Icon name="chevronRight" size={12} className="muted" />
          <div className="run-analysis-ideal__stat">
            <span className="run-analysis-ideal__num">{numberOr(idealPath.tool_calls_ideal, '—')}</span>
            <span className="run-analysis-ideal__label">ideal</span>
          </div>
          {idealPath.gap_summary && (
            <span className="text-xs muted run-analysis-ideal__gap">{idealPath.gap_summary}</span>
          )}
        </div>
      )}

      {findings.length === 0 ? (
        <div className="run-analysis-empty text-sm muted">
          No findings — the agent navigated this MCP cleanly.
        </div>
      ) : (
        <ul className="run-analysis-findings">
          {findings.map((finding, idx) => (
            <RunAnalysisFinding
              key={finding.id || `finding-${idx}`}
              finding={finding}
              onJumpToEvidence={onJumpToEvidence} />
          ))}
        </ul>
      )}

      {Array.isArray(idealPath?.ideal_sequence) && idealPath.ideal_sequence.length > 0 && (
        <details className="run-analysis-ideal-sequence">
          <summary className="text-xs muted">Suggested ideal flow ({idealPath.ideal_sequence.length} steps)</summary>
          <ol>
            {idealPath.ideal_sequence.map((step, idx) => (
              <li key={idx} className="text-sm">{step}</li>
            ))}
          </ol>
        </details>
      )}

      {positiveSignals.length > 0 && (
        <div className="run-analysis-positive">
          <div className="run-analysis-positive__label">What worked well</div>
          <ul>
            {positiveSignals.map((signal, idx) => (
              <li key={idx} className="text-sm muted">{signal}</li>
            ))}
          </ul>
        </div>
      )}

      {details._format_error === 'non_json_response' && (
        <div className="text-xs muted run-analysis-format-warn">
          The model returned this analysis in an unexpected format. The raw output is preserved; click Regenerate to retry.
        </div>
      )}
    </div>
  );
}

function RunAnalysisFinding({ finding, onJumpToEvidence }) {
  const [open, setOpen] = useStateR(false);
  const severity = String(finding?.severity || 'medium').toLowerCase();
  const category = String(finding?.category || 'other');
  const evidence = Array.isArray(finding?.evidence) ? finding.evidence : [];
  const tone = severity === 'high' ? 'ui-pill-bad' : severity === 'low' ? 'ui-pill-ok' : 'ui-pill-warn';
  return (
    <li className={`run-analysis-finding run-analysis-finding--${severity}`}>
      <button
        type="button"
        className="run-analysis-finding__head"
        onClick={() => setOpen((o) => !o)}>
        <Icon name={open ? 'chevronDown' : 'chevronRight'} size={11} />
        <span className={`ui-pill ${tone}`}>{severity}</span>
        <span className="ui-pill run-analysis-finding__category">{category.replace(/_/g, ' ')}</span>
        <span className="run-analysis-finding__title">{finding?.title || 'Finding'}</span>
      </button>
      {open && (
        <div className="run-analysis-finding__body">
          {finding?.observation && (
            <p className="text-sm"><strong>Observed:</strong> {finding.observation}</p>
          )}
          {finding?.recommendation && (
            <p className="text-sm"><strong>Suggested:</strong> {finding.recommendation}</p>
          )}
          {evidence.length > 0 && (
            <div className="run-analysis-finding__evidence">
              <span className="text-xs muted">Evidence:</span>
              {evidence.map((ev, idx) => (
                <button
                  key={idx}
                  type="button"
                  className="run-analysis-finding__evidence-link"
                  onClick={() => onJumpToEvidence && onJumpToEvidence(ev?.ref)}>
                  {ev?.kind || 'ref'}:{shortId(ev?.ref) || ev?.ref || '—'}
                </button>
              ))}
            </div>
          )}
        </div>
      )}
    </li>
  );
}

function numberOr(value, fallback) {
  const n = typeof value === 'number' ? value : (typeof value === 'string' && /^-?\d+(\.\d+)?$/.test(value)) ? Number(value) : null;
  return n === null || Number.isNaN(n) ? fallback : formatNumber(n);
}

function MetaItem({ label, value, tone = '', title = '', sub = '' }) {
  return (
    <div className={`metadata-item ${tone}`} title={title || String(value || '')}>
      <span className="metadata-label">{label}</span>
      <span className="metadata-value">{value || '—'}</span>
      {sub ? <span className="metadata-value muted text-xs">{sub}</span> : null}
    </div>
  );
}

function ToolCallWaterfall({ toolCalls, traceBounds, expanded, onToggleExpanded }) {
  const [hoveredToolCall, setHoveredToolCall] = useStateR(null);
  const tooltipCloseTimer = useRefR(null);
  useEffectR(() => () => {
    if (tooltipCloseTimer.current) window.clearTimeout(tooltipCloseTimer.current);
  }, []);
  if (!toolCalls || toolCalls.length === 0) return null;
  const rows = toolCalls.map((call) => {
    const startedMs = parseDateMs(call.started_at);
    const completedMs = parseDateMs(call.completed_at);
    const rawDurationMs = Number(call.duration_ms);
    const durationMs = Number.isFinite(rawDurationMs) && rawDurationMs > 0
      ? rawDurationMs
      : (startedMs != null && completedMs != null ? Math.max(0, completedMs - startedMs) : 0);
    const effectiveStartMs = startedMs ?? completedMs ?? traceBounds?.startMs ?? Date.now();
    const effectiveEndMs = Math.max(
      completedMs ?? (effectiveStartMs + durationMs),
      effectiveStartMs + Math.max(durationMs, 1),
    );
    const displayName = humanizeToolName(call.tool_name, { payload: { arguments: call.arguments_redacted || call.arguments || null } });
    return {
      call,
      startedMs: effectiveStartMs,
      endMs: effectiveEndMs,
      durationMs: Math.max(effectiveEndMs - effectiveStartMs, 1),
      displayName,
    };
  }).sort((a, b) => a.startedMs - b.startedMs);
  const boundValues = [
    traceBounds?.startMs,
    traceBounds?.endMs,
    ...rows.flatMap((row) => [row.startedMs, row.endMs]),
  ].filter(Number.isFinite);
  const startMs = boundValues.length ? Math.min(...boundValues) : Date.now();
  const endMs = Math.max(boundValues.length ? Math.max(...boundValues) : startMs + 1000, startMs + 1000);
  const totalMs = Math.max(endMs - startMs, 1000);
  const midMs = startMs + (totalMs / 2);
  const visibleCount = rows.length;
  const hoveredRow = hoveredToolCall ? rows.find((row) => row.call.id === hoveredToolCall.id) || null : null;
  const hoveredArguments = hoveredRow ? formatToolArgs(hoveredRow.call.arguments_redacted || hoveredRow.call.arguments || null, null) : null;
  const timelineWidthPx = clampPercent(Math.ceil(totalMs / 1000) * 5, 1120, 3600);
  const renderedRows = [
    {
      id: 'run-duration',
      displayName: 'Run duration',
      startedMs: startMs,
      endMs,
      durationMs: totalMs,
      tone: 'root',
      icon: 'activity',
      isRoot: true,
    },
    ...rows.map((row) => {
      const status = row.call.status || 'unknown';
      return {
        ...row,
        id: row.call.id,
        tone: status === 'completed' || status === 'success'
          ? 'success'
          : status === 'started' || status === 'running' ? 'running' : 'failed',
        icon: 'tool',
      };
    }),
  ];

  function cancelWaterfallTooltipClose() {
    if (!tooltipCloseTimer.current) return;
    window.clearTimeout(tooltipCloseTimer.current);
    tooltipCloseTimer.current = null;
  }

  function scheduleWaterfallTooltipClose() {
    cancelWaterfallTooltipClose();
    tooltipCloseTimer.current = window.setTimeout(() => {
      setHoveredToolCall(null);
      tooltipCloseTimer.current = null;
    }, 180);
  }

  return (
    <div className={`tool-waterfall ${expanded ? 'expanded' : ''}`}>
      <div className="tool-waterfall-head">
        <div className="tool-waterfall-title">
          <Icon name="clock" size={12} />
          Waterfall
          <span>{visibleCount} calls</span>
        </div>
        <button type="button" className="tool-waterfall-toggle" onClick={onToggleExpanded}>
          <Icon name={expanded ? 'panelCollapse' : 'panelExpand'} size={12} />
          {expanded ? 'Collapse' : 'Expand'}
        </button>
      </div>
      {hoveredRow ? (
        <div
          className="tool-waterfall-tooltip"
          role="tooltip"
          style={{
            left: `${hoveredToolCall.x}px`,
            top: `${hoveredToolCall.y - 10}px`,
          }}
          onMouseEnter={cancelWaterfallTooltipClose}
          onMouseLeave={scheduleWaterfallTooltipClose}>
          <div className="tool-waterfall-tooltip-title">{hoveredRow.displayName}</div>
          <div className="tool-waterfall-tooltip-label">Arguments</div>
          <div className="tool-waterfall-tooltip-value">{hoveredArguments}</div>
        </div>
      ) : null}
      <div className="tool-waterfall-help">
        Hover a tool call to inspect its arguments.
      </div>
      <div className="tool-waterfall-scroll">
        <div className="tool-waterfall-canvas" style={getToolWaterfallCanvasStyle(timelineWidthPx)}>
          <div className="tool-waterfall-axis" aria-hidden="true">
            <span></span>
            <span className="tool-waterfall-axis-track">
              <span>{formatTraceOffset(startMs, startMs)}</span>
              <span>{formatTraceOffset(midMs, startMs)}</span>
              <span>{formatTraceOffset(endMs, startMs)}</span>
            </span>
            <span></span>
          </div>
          <div className="tool-waterfall-rows">
            {renderedRows.map((row) => {
          const left = clampPercent(((row.startedMs - startMs) / totalMs) * 100);
          const width = row.isRoot ? 100 : clampPercent((row.durationMs / totalMs) * 100, 0.3, 100 - left);
          const offset = formatTraceOffset(row.startedMs, startMs);
          const duration = formatDuration(row.durationMs) || '—';
          const timestamp = row.isRoot ? `${formatTraceTimestamp(startMs)} → ${formatTraceTimestamp(endMs)}` : formatTraceTimestamp(row.call.started_at);
          const titleSuffix = !row.isRoot && row.displayName !== row.call.tool_name && row.call.tool_name ? ` (${row.call.tool_name})` : '';
          return (
            <div
              key={row.id}
              className={`tool-waterfall-row ${row.isRoot ? 'root' : ''}`}
              title={`${row.displayName}${titleSuffix} · ${timestamp} · ${duration}`}>
              <div className="tool-waterfall-label">
                <Icon name={row.icon} size={11} />
                <span className="tool-waterfall-name">{row.displayName}</span>
                <span className="tool-waterfall-offset">{offset}</span>
              </div>
              <div className="tool-waterfall-track">
                <div
                  className={`tool-waterfall-bar ${row.tone}`}
                  style={{ left: `${left}%`, width: `${width}%` }}
                  tabIndex={row.isRoot ? undefined : 0}
                  onMouseEnter={(event) => {
                    if (row.isRoot) return;
                    cancelWaterfallTooltipClose();
                    setHoveredToolCall(getWaterfallTooltipState(event, row.id));
                  }}
                  onMouseMove={(event) => {
                    if (row.isRoot) return;
                    setHoveredToolCall(getWaterfallTooltipState(event, row.id));
                  }}
                  onMouseLeave={() => { if (!row.isRoot) scheduleWaterfallTooltipClose(); }}
                  onFocus={(event) => {
                    if (row.isRoot) return;
                    cancelWaterfallTooltipClose();
                    setHoveredToolCall(getWaterfallTooltipState(event, row.id));
                  }}
                  onBlur={() => { if (!row.isRoot) scheduleWaterfallTooltipClose(); }}></div>
              </div>
              <span className="tool-waterfall-duration">
                {duration}
              </span>
            </div>
          );
        })}
          </div>
        </div>
      </div>
    </div>
  );
}

function getToolWaterfallCanvasStyle(timelineWidthPx) {
  return { width: 'max-content', '--tool-waterfall-timeline-width': `${timelineWidthPx}px` };
}

function getWaterfallTooltipState(event, id) {
  const container = event.currentTarget.closest('.tool-waterfall');
  const rect = container?.getBoundingClientRect();
  if (!rect) return { id, x: 240, y: 80 };
  const targetRect = event.currentTarget.getBoundingClientRect?.();
  const fallbackX = targetRect ? targetRect.left + (targetRect.width / 2) : rect.left + 240;
  const fallbackY = targetRect ? targetRect.top + (targetRect.height / 2) : rect.top + 80;
  const clientX = Number.isFinite(event.clientX) ? event.clientX : fallbackX;
  const clientY = Number.isFinite(event.clientY) ? event.clientY : fallbackY;
  return {
    id,
    x: clampPercent(clientX - rect.left, 220, Math.max(220, rect.width - 220)),
    y: clampPercent(clientY - rect.top, 58, Math.max(58, rect.height - 8)),
  };
}

function ToolCallsTable({ toolCalls, traceBounds }) {
  if (!toolCalls || toolCalls.length === 0) return null;
  return (
    <div className="tool-call-table-wrap">
      <table className="criteria-table tool-call-table">
        <thead>
          <tr>
            <th>Tool</th>
            <th className="num">Offset</th>
            <th className="num">Duration</th>
            <th>Status</th>
            <th>Arguments</th>
          </tr>
        </thead>
        <tbody>
          {toolCalls.map((call) => (
            <ToolCallRow key={call.id} call={call} traceBounds={traceBounds} />
          ))}
        </tbody>
      </table>
    </div>
  );
}

function ToolCallRow({ call, traceBounds }) {
  const startedMs = parseDateMs(call.started_at);
  const offset = startedMs != null && traceBounds?.startMs != null
    ? formatTraceOffset(startedMs, traceBounds.startMs)
    : '—';
  const status = call.status || 'unknown';
  const tone = status === 'completed' ? 'pass' : status === 'started' ? 'warn' : 'fail';
  const displayName = humanizeToolName(call.tool_name, { payload: { arguments: call.arguments_redacted || call.arguments || null } });
  const argsLabel = formatToolArgs(call.arguments_redacted || call.arguments || null);
  const argsTitle = argsLabel === '—' ? '' : argsLabel;
  return (
    <tr>
      <td className="tool-call-table-name mono" title={call.tool_name || ''}>{displayName}</td>
      <td className="num mono">{offset}</td>
      <td className="num mono">{formatDuration(call.duration_ms) || '—'}</td>
      <td>
        <span className={`ui-pill pill-${tone}`}>{status}</span>
      </td>
      <td className="tool-call-table-args" title={argsTitle}>{argsLabel}</td>
    </tr>
  );
}

function formatToolArgs(args, maxLength = 120) {
  if (args === null || args === undefined) return '—';
  try {
    const normalized = normalizeToolArgValue(args);
    const label = formatToolArgReadable(normalized);
    if (!label) return '—';
    if (!Number.isFinite(maxLength) || maxLength <= 0 || label.length <= maxLength) return label;
    return `${label.slice(0, Math.max(0, maxLength - 1))}…`;
  } catch (_err) {
    return '—';
  }
}

function normalizeToolArgValue(value) {
  if (typeof value !== 'string') return value;
  const raw = value.trim();
  if (!raw) return '';
  if ((raw.startsWith('{') && raw.endsWith('}')) || (raw.startsWith('[') && raw.endsWith(']'))) {
    try {
      return JSON.parse(raw);
    } catch (_err) {
      return cleanToolArgText(value);
    }
  }
  return cleanToolArgText(value);
}

function formatToolArgReadable(value) {
  if (value === null || value === undefined || value === '') return '';
  if (Array.isArray(value)) {
    if (value.length === 0) return 'none';
    if (value.every((item) => item === null || ['string', 'number', 'boolean'].includes(typeof item))) {
      return value.map((item) => formatToolArgReadable(item)).filter(Boolean).join(', ');
    }
    return `${formatNumber(value.length)} ${value.length === 1 ? 'item' : 'items'}`;
  }
  if (typeof value === 'object') {
    return Object.entries(value)
      .map(([key, child]) => {
        const childLabel = formatToolArgReadable(normalizeToolArgValue(child));
        return childLabel ? `${traceFactLabel(key)}: ${childLabel}` : '';
      })
      .filter(Boolean)
      .join('\n');
  }
  if (typeof value === 'number') return formatNumber(value);
  if (typeof value === 'boolean') return value ? 'yes' : 'no';
  return cleanToolArgText(String(value));
}

function cleanToolArgText(value) {
  return String(value || '')
    .replace(/\\r\\n/g, '\n')
    .replace(/\\n/g, '\n')
    .replace(/\\t/g, '  ')
    .replace(/\\+"/g, '"')
    .replace(/\r\n/g, '\n')
    .replace(/\r/g, '\n')
    .trim();
}

function ToolCallChip({ call }) {
  const tone = call.status === 'completed' ? 'success' : call.status === 'started' ? 'running' : 'failed';
  const displayName = humanizeToolName(call.tool_name, { payload: { arguments: call.arguments_redacted || call.arguments || null } });
  const titleSuffix = displayName !== call.tool_name && call.tool_name ? ` (${call.tool_name})` : '';
  return (
    <div className={`tool-call-chip ${tone}`} title={`${displayName}${titleSuffix} · ${call.status} · ${formatTraceTimestamp(call.started_at)}`}>
      <span className="tool-call-name"><Icon name="tool" size={11} />{displayName}</span>
      <span className="tool-call-meta">{formatTraceTimestamp(call.started_at)} · {formatDuration(call.duration_ms)}</span>
    </div>
  );
}

const TRACE_LIFECYCLE_LABELS = {
  'tester:root': 'Tester started',
  'tester:llm_call': 'Model turn',
  'tester:llm_response': 'Model response',
  'tester:reasoning_summary': 'Reasoning',
  'evaluator:root': 'Evaluator started',
  'judge_reasoning': 'Evaluator reasoning',
  'judge_result': 'Evaluation result',
  'codex_thread_started': 'Codex session started',
  'codex_turn_completed': 'Turn completed',
  'codex_error': 'Codex error',
  'codex_item_error': 'Codex item error',
  'claude_init': 'Claude session started',
  'claude_result': 'Session completed',
};

const CODEX_ITEM_LABELS = {
  command_execution: 'Shell command',
  local_shell_call: 'Shell command',
  web_search: 'Web search',
  web_search_call: 'Web search',
  file_search: 'File search',
  file_search_call: 'File search',
  image_generation: 'Image generation',
  computer_use: 'Computer use',
  message: 'Message',
};

function customerTracePayload(event) {
  const inner = event?.payload?.payload;
  return inner && typeof inner === 'object' && inner.schema === 'customer_trace.v1' ? inner : null;
}

function customerTraceSearchText(event) {
  const payload = customerTracePayload(event);
  if (!payload) {
    return [
      event?.event_type,
      event?.payload?.name,
      event?.payload?.summary,
      boundedTraceSearchText(event?.payload),
    ].filter(Boolean).join(' ').toLowerCase();
  }
  return [
    payload.label,
    payload.headline,
    payload.kind,
    payload.status,
    payload.arguments?.summary,
    payload.result?.content_type,
    payload.result?.output_label,
    ...(Array.isArray(payload.omitted) ? payload.omitted : []),
    ...(Array.isArray(payload.redactions) ? payload.redactions : []),
  ].filter(Boolean).join(' ').toLowerCase();
}

function boundedTraceSearchText(value, depth = 0) {
  if (depth > 4 || value == null) return '';
  if (typeof value === 'string') return value.slice(0, 200);
  if (typeof value === 'number' || typeof value === 'boolean') return String(value);
  if (Array.isArray(value)) return value.slice(0, 8).map((item) => boundedTraceSearchText(item, depth + 1)).filter(Boolean).join(' ');
  if (typeof value !== 'object') return '';
  return Object.entries(value)
    .slice(0, 30)
    .filter(([key]) => !/authorization|cookie|password|passwd|secret|token|api[_-]?key|access[_-]?key|private[_-]?key|credential|signature|call_id|tool_use_id|message_id|session_id|thread_id|raw_item|command|cmd/i.test(key))
    .map(([key, child]) => `${key} ${boundedTraceSearchText(child, depth + 1)}`)
    .filter(Boolean)
    .join(' ');
}

function titleWords(value) {
  return String(value).replace(/\b[a-z]/g, (char) => char.toUpperCase());
}

function humanizeToolName(rawName, spanPayload) {
  const inner = (spanPayload && typeof spanPayload === 'object' && spanPayload.payload && typeof spanPayload.payload === 'object')
    ? spanPayload.payload
    : (spanPayload && typeof spanPayload === 'object' ? spanPayload : {});
  const innerTool = typeof inner.tool === 'string' ? inner.tool : null;
  if (
    (rawName === 'tool' || rawName === 'tool_result' || !rawName) &&
    innerTool &&
    innerTool !== 'tool' &&
    innerTool !== 'tool_result' &&
    innerTool !== rawName
  ) {
    return humanizeToolName(innerTool, spanPayload);
  }
  const args = (inner && typeof inner.arguments === 'object' && inner.arguments) ? inner.arguments : null;
  const cmd =
    args && typeof args.command === 'string' ? args.command :
    args && Array.isArray(args.command) ? args.command.join(' ') :
    args && typeof args.cmd === 'string' ? args.cmd : null;

  const shellNames = new Set(['shell', 'bash', 'local_shell', 'local_shell_call', 'exec', 'command_execution']);
  const looksLikeShell = (shellNames.has(String(rawName || '').toLowerCase()) || shellNames.has(String(innerTool || '').toLowerCase())) && cmd;
  if (looksLikeShell) {
    const description = args && typeof args.description === 'string' ? args.description : '';
    if (description) return `Shell: ${description}`;
    const executable = cmd.trim().split(/\s+/)[0] || 'command';
    return `Shell: ran ${executable}`;
  }

  const name = rawName || innerTool || '';
  const mcpMatch = name.match(/^mcp__(.+?)__(.+)$/);
  if (mcpMatch) {
    const server = mcpMatch[1].replace(/(?:_staging)?_mcp$/, '').replace(/_/g, ' ');
    const tool = mcpMatch[2].replace(/[_-]+/g, ' ');
    return `${titleWords(server)}: ${tool}`;
  }
  if (!name || name === 'tool' || name === 'tool_result') {
    if (cmd) {
      const executable = cmd.trim().split(/\s+/)[0] || 'command';
      return `Shell: ran ${executable}`;
    }
    // Last-resort: try to recover a name from the raw upstream item we now capture.
    const rawItem = inner && typeof inner.raw_item === 'object' ? inner.raw_item : null;
    if (rawItem) {
      const composed = (rawItem.server_name && rawItem.tool_name) ? `mcp__${rawItem.server_name}__${rawItem.tool_name}` : null;
      const recovered = composed || rawItem.name || rawItem.tool || rawItem.tool_name || rawItem.function_name ||
        (rawItem.function && rawItem.function.name) || null;
      if (typeof recovered === 'string' && recovered.trim() && recovered !== 'tool' && recovered !== 'tool_result') return humanizeToolName(recovered, spanPayload);
    }
    return 'Tool call';
  }
  return name;
}

function describeSpan(event) {
  const spanPayload = (event && event.payload && typeof event.payload === 'object') ? event.payload : {};
  const innerPayload = (spanPayload.payload && typeof spanPayload.payload === 'object') ? spanPayload.payload : {};
  const rawName = (typeof spanPayload.name === 'string' && spanPayload.name) || '';
  const spanType = typeof spanPayload.type === 'string' ? spanPayload.type : '';
  const eventType = event?.event_type || '';
  const role = event?.role;
  const customer = customerTracePayload(event);
  if (customer) {
    const kind =
      customer.kind?.includes('tool') ? 'tool' :
        customer.kind === 'evaluation_result' ? 'judge' :
          customer.kind === 'harness_error' ? 'error' :
            (customer.role === 'system' || customer.kind === 'lifecycle') ? 'system' :
              'reasoning';
    return {
      label: customer.label || customer.headline || rawName || eventType || 'Trace event',
      sub: customer.headline && customer.headline !== customer.label ? customer.headline : null,
      kind,
      technicalName: rawName,
    };
  }

  if (spanType === 'tool_call' || spanType === 'tool_result' || eventType.startsWith('tool.call') || eventType === 'tool.result') {
    return { label: humanizeToolName(rawName, spanPayload), sub: null, kind: 'tool', technicalName: rawName };
  }

  if (eventType === 'agent.reasoning_summary' || spanType === 'reasoning') {
    return { label: 'Reasoning', sub: null, kind: 'reasoning', technicalName: rawName };
  }

  if (spanType === 'judge_reasoning' || rawName === 'judge_reasoning') {
    return { label: 'Evaluator reasoning', sub: null, kind: 'judge', technicalName: rawName };
  }
  if (spanType === 'judge_result' || rawName === 'judge_result' || eventType === 'evaluation.result') {
    return { label: 'Evaluation result', sub: null, kind: 'judge', technicalName: rawName };
  }

  if (eventType === 'log.error' || spanType === 'error' || eventType === 'lifecycle.failed') {
    return { label: TRACE_LIFECYCLE_LABELS[rawName] || 'Error', sub: null, kind: 'error', technicalName: rawName };
  }

  if (eventType === 'agent.input') {
    const step = innerPayload?.step;
    return { label: step != null ? `Model turn ${step}` : 'Model turn', sub: null, kind: 'reasoning', technicalName: rawName };
  }
  if (eventType === 'agent.output') {
    return { label: 'Model response', sub: null, kind: 'reasoning', technicalName: rawName };
  }

  if (TRACE_LIFECYCLE_LABELS[rawName]) {
    const kind = role === 'evaluator' ? 'judge' : (rawName.includes('error') ? 'error' : 'system');
    return { label: TRACE_LIFECYCLE_LABELS[rawName], sub: null, kind, technicalName: rawName };
  }

  if (rawName.startsWith('codex_item_')) {
    const itemType = rawName.slice('codex_item_'.length);
    const label = CODEX_ITEM_LABELS[itemType] || itemType.charAt(0).toUpperCase() + itemType.replace(/_/g, ' ').slice(1);
    const item = (innerPayload?.item && typeof innerPayload.item === 'object') ? innerPayload.item : {};
    const cmd =
      typeof item.command === 'string' ? item.command :
      Array.isArray(item.command) ? item.command.join(' ') :
      (item.action && typeof item.action.command === 'string') ? item.action.command :
      Array.isArray(item.action?.command) ? item.action.command.join(' ') :
      typeof item.query === 'string' ? item.query :
      null;
    const sub = cmd ? cmd.length > 80 ? `${cmd.slice(0, 79)}…` : cmd : null;
    const isToolLike = /shell|command|search|computer/.test(itemType);
    return { label, sub, kind: isToolLike ? 'tool' : 'system', technicalName: rawName };
  }

  return {
    label: rawName || eventType || 'Trace event',
    sub: null,
    kind: role === 'evaluator' ? 'judge' : role === 'system' ? 'system' : 'reasoning',
    technicalName: rawName,
  };
}

function dedupeTraceEvents(events) {
  const byKey = new Map();
  const ungrouped = [];
  for (const event of events) {
    const inner = event?.payload?.payload || {};
    const spanType = event?.payload?.type;
    const key =
      (typeof inner.correlation_id === 'string' && inner.correlation_id) ||
      (typeof inner.call_id === 'string' && inner.call_id) ||
      (typeof inner.tool_use_id === 'string' && inner.tool_use_id) ||
      null;
    const isToolFamily = spanType === 'tool_call' || spanType === 'tool_result';
    if (!key || !isToolFamily) {
      ungrouped.push(event);
      continue;
    }
    if (!byKey.has(key)) byKey.set(key, []);
    byKey.get(key).push(event);
  }
  const collapsed = [];
  for (const group of byKey.values()) {
    const sorted = [...group].sort((a, b) => Number(a.sequence_number) - Number(b.sequence_number));
    const completed = sorted.find(e => e.payload?.type === 'tool_call' && e.payload?.status !== 'running');
    const result = sorted.find(e => e.payload?.type === 'tool_result');
    const running = sorted.find(e => e.payload?.type === 'tool_call' && e.payload?.status === 'running');
    const canonical = completed || result || running || sorted[0];
    const startEvent = running || canonical;
    const endEvent = completed || result || canonical;
    // event.timestamp mirrors span.started_at (see workers/src/shared/db.ts:insertTraceSpans),
    // so the real call duration must come from payload.ended_at on the closing span.
    collapsed.push({
      ...canonical,
      _grouped: true,
      _startedAt: startEvent.payload?.started_at || startEvent.timestamp,
      _endedAt: endEvent.payload?.ended_at || endEvent.payload?.started_at || endEvent.timestamp,
      _siblingCount: sorted.length,
    });
  }
  return [...ungrouped, ...collapsed].sort((a, b) => Number(a.sequence_number) - Number(b.sequence_number));
}

function TracePayloadDetail({ event, toolCall }) {
  const customer = customerTracePayload(event);
  if (!customer) {
    const payload = cleanTraceDetailValue(event.payload || {});
    return (
      <div>
        <h5>Payload</h5>
        <div className="code">{JSON.stringify(payload || {}, null, 2)}</div>
      </div>
    );
  }
  const metrics = cleanTraceDetailValue(customer.metrics || {});
  const args = cleanTraceDetailValue(customer.arguments);
  const result = cleanTraceDetailValue(customer.result);
  const rawResultBody = toolCall?.raw_result_body ?? toolCall?.rawResultBody;
  return (
    <div className="trace-projected-detail">
      <div>
        <h5>Overview</h5>
        <TraceFactList
          facts={traceDetailFacts({
            kind: customer.kind,
            status: customer.status || 'unknown',
            headline: customer.headline || '—',
            duration: formatDuration(metrics?.duration_ms) || '—',
          })} />
      </div>
      <TraceFactSection title="Arguments" value={args} />
      <TraceFactSection title="Result" value={result} />
      <TraceRawResultSection value={rawResultBody} />
      <TraceFactSection title="Metrics" value={metrics} />
      <TraceFactSection
        title="Omitted and redacted"
        value={(customer.omitted?.length || customer.redactions?.length)
          ? { omitted: customer.omitted || [], redactions: customer.redactions || [] }
          : null} />
    </div>
  );
}

function TraceRawResultSection({ value }) {
  if (value === null || value === undefined) return null;
  const raw = typeof value === 'string' ? value : JSON.stringify(value, null, 2);
  if (!raw) return null;
  return (
    <div>
      <h5>Raw result body</h5>
      <TraceFactList facts={[{ label: 'Body', value: raw }]} />
    </div>
  );
}

function TraceFactSection({ title, value }) {
  const facts = traceDetailFacts(value);
  if (facts.length === 0) return null;
  return (
    <div>
      <h5>{title}</h5>
      <TraceFactList facts={facts} />
    </div>
  );
}

function TraceFactList({ facts }) {
  const [expanded, setExpanded] = useStateR({});
  return (
    <dl className="trace-detail-list">
      {facts.map((fact, index) => {
        const rowKey = `${fact.label}:${index}`;
        const canExpand = fact.value.length > TRACE_FACT_COLLAPSE_CHARS;
        const isExpanded = Boolean(expanded[rowKey]);
        const title = isExpanded ? '' : fact.value.length > 500 ? `${fact.value.slice(0, 500)}...` : fact.value;
        return (
          <div key={rowKey} className={isExpanded ? 'expanded' : ''}>
            <dt>{fact.label}</dt>
            <dd title={title}>
              <span>{fact.value}</span>
              {canExpand ? (
                <button
                  type="button"
                  className="trace-fact-toggle"
                  onClick={(event) => {
                    event.stopPropagation();
                    setExpanded((current) => ({ ...current, [rowKey]: !current[rowKey] }));
                  }}>
                  {isExpanded ? 'Show less' : 'Show more'}
                </button>
              ) : null}
            </dd>
          </div>
        );
      })}
    </dl>
  );
}

function traceDetailFacts(value) {
  if (value === null || value === undefined) return [];
  if (typeof value !== 'object' || Array.isArray(value)) {
    return [{ label: 'Value', value: formatTraceFactValue(value) }];
  }
  return Object.entries(value)
    .map(([key, child]) => ({
      label: traceFactLabel(key),
      value: formatTraceFactValue(child, key),
    }))
    .filter((fact) => fact.value !== '');
}

function traceFactLabel(key) {
  return String(key || 'Value')
    .replace(/[_-]+/g, ' ')
    .replace(/\b\w/g, (char) => char.toUpperCase());
}

function formatTraceFactValue(value, key = '') {
  if (value === null || value === undefined) return '';
  const keyText = String(key || '').toLowerCase();
  if (typeof value === 'number') {
    if (keyText.includes('duration') && keyText.endsWith('_ms')) return formatDuration(value);
    if (keyText.includes('byte')) return formatBytes(value);
    if (keyText.includes('char')) return `${formatNumber(value)} chars`;
    return formatNumber(value);
  }
  if (typeof value === 'boolean') return value ? 'yes' : 'no';
  if (typeof value === 'string') return value || '—';
  if (Array.isArray(value)) {
    if (value.length === 0) return 'none';
    if (value.every((item) => item === null || ['string', 'number', 'boolean'].includes(typeof item))) {
      return value.map((item) => formatTraceFactValue(item)).join(', ');
    }
    return `${formatNumber(value.length)} ${value.length === 1 ? 'item' : 'items'}`;
  }
  if (typeof value === 'object') {
    const primitiveEntries = Object.entries(value)
      .filter(([, child]) => child === null || ['string', 'number', 'boolean'].includes(typeof child))
      .map(([childKey, child]) => `${traceFactLabel(childKey)}: ${formatTraceFactValue(child, childKey)}`);
    if (primitiveEntries.length > 0) return primitiveEntries.join(', ');
    return `${formatNumber(Object.keys(value).length)} ${Object.keys(value).length === 1 ? 'field' : 'fields'}`;
  }
  return String(value);
}

function cleanTraceDetailValue(value) {
  if (value === null || value === undefined) return undefined;
  if (typeof value === 'string') {
    return value === '[REDACTED]' || value.startsWith('[REDACTED_') ? undefined : value;
  }
  if (typeof value === 'number' || typeof value === 'boolean') return value;
  if (Array.isArray(value)) {
    const cleaned = value
      .map((item) => cleanTraceDetailValue(item))
      .filter((item) => item !== undefined);
    return cleaned.length ? cleaned : undefined;
  }
  if (typeof value === 'object') {
    const entries = Object.entries(value)
      .map(([key, child]) => [key, cleanTraceDetailValue(child)])
      .filter(([, child]) => child !== undefined);
    return entries.length ? Object.fromEntries(entries) : undefined;
  }
  return value;
}

function formatCompactNumber(value) {
  const n = Number(value);
  if (!Number.isFinite(n)) return '—';
  if (n >= 1000000) return `${(n / 1000000).toFixed(1)}M`;
  if (n >= 1000) return `${(n / 1000).toFixed(1)}k`;
  return String(Math.round(n));
}

function isFiniteNumberLike(value) {
  if (value === null || value === undefined || value === '') return false;
  return Number.isFinite(Number(value));
}

function formatBytes(value) {
  const n = Number(value);
  if (!Number.isFinite(n)) return '—';
  if (n >= 1024 * 1024) return `${(n / (1024 * 1024)).toFixed(1)} MB`;
  if (n >= 1024) return `${(n / 1024).toFixed(1)} KB`;
  return `${Math.round(n)} B`;
}

function TraceEventRow({ event, traceBounds, toolCall }) {
  const [open, setOpen] = useStateR(false);
  const spanPayload = event.payload || {};
  const description = describeSpan(event);
  const customer = customerTracePayload(event);
  const status = typeof spanPayload.status === 'string' ? spanPayload.status : null;
  const spanSummary = typeof spanPayload.summary === 'string' ? spanPayload.summary : null;
  const groupStartMs = parseDateMs(event._startedAt);
  const groupEndMs = parseDateMs(event._endedAt);
  const eventMs = parseDateMs(event.timestamp) || traceBounds.startMs;
  const fallbackToolStart = parseDateMs(toolCall?.started_at);
  const fallbackToolEnd = parseDateMs(toolCall?.completed_at);
  const barStartMs = groupStartMs || fallbackToolStart || eventMs;
  const barEndMs = groupEndMs || fallbackToolEnd || barStartMs;
  const spanDurationMs = Math.max(0, barEndMs - barStartMs);
  const hasError = status === 'failed' ||
    description.kind === 'error' ||
    event.event_type?.includes('fail') ||
    spanPayload.payload?.is_error === true;
  const subBits = [];
  if (description.kind === 'tool' && status && status !== 'success') subBits.push(status);
  if (description.kind === 'tool' && spanDurationMs > 0) subBits.push(formatDuration(spanDurationMs));
  if (description.sub) subBits.push(description.sub);
  if (spanSummary && spanSummary !== description.label && !subBits.includes(spanSummary)) subBits.push(spanSummary);
  const spanContext = subBits.join(' · ');
  const metricBits = [];
  if (isFiniteNumberLike(customer?.metrics?.estimated_result_tokens)) metricBits.push(`~${formatCompactNumber(customer.metrics.estimated_result_tokens)} result tokens`);
  if (isFiniteNumberLike(customer?.result?.item_count)) metricBits.push(`${customer.result.item_count} items`);
  if (isFiniteNumberLike(customer?.result?.byte_length)) metricBits.push(formatBytes(customer.result.byte_length));
  const projectedContext = [spanContext, ...metricBits].filter(Boolean).join(' · ');
  const left = clampPercent(((barStartMs - traceBounds.startMs) / traceBounds.durationMs) * 100);
  const naturalWidth = clampPercent(((Math.max(barEndMs - barStartMs, 750) / traceBounds.durationMs) * 100), 0.8, 42);
  const eventOffset = formatTraceOffset(eventMs, traceBounds.startMs);
  const technicalBit = description.technicalName && description.technicalName !== description.label
    ? description.technicalName
    : event.event_type;
  const barTitle = [
    `${formatTraceTimestamp(event.timestamp)} (${eventOffset})`,
    technicalBit,
  ].filter(Boolean).join('\n');
  const iconName = description.kind === 'tool' ? 'tool' : description.kind === 'judge' ? 'gavel' : 'activity';
  return (
    <>
      <div className={`trace-row ${open ? 'expanded' : ''} ${hasError ? 'error' : ''}`} onClick={() => setOpen(!open)}>
        <div className="trace-caret">›</div>
        <div className="span-name">
          <span className={`span-icon ${hasError ? 'error' : description.kind}`}><Icon name={iconName} size={11} /></span>
          <span className="span-text">
            <span className="span-label" title={description.technicalName && description.technicalName !== description.label ? description.technicalName : ''}>{description.label}</span>
            {projectedContext ? <span className="span-sub">{projectedContext}</span> : null}
          </span>
        </div>
        <div className="span-time">
          <span>{formatTraceTimestamp(event.timestamp)}</span>
          <span>{eventOffset}</span>
        </div>
        <div className="span-duration">#{event.sequence_number}</div>
        <div className="span-bar-track" title={barTitle}>
          <span className="span-bar-tick-75" aria-hidden="true"></span>
          <div className={`span-bar ${hasError ? 'error' : description.kind}`} style={{ left: `${left}%`, width: `max(${naturalWidth}%, 12px)` }}></div>
        </div>
      </div>
      {open && (
        <div className="trace-detail">
          <TracePayloadDetail event={event} toolCall={toolCall} />
          <TraceFactSection
            title="Metadata"
            value={{ id: event.id, agent_run_id: event.agent_run_id, timestamp: event.timestamp, ...(event._grouped ? { grouped_events: event._siblingCount } : {}) }} />
        </div>
      )}
    </>
  );
}

function parseDateMs(value) {
  if (!value) return null;
  const ms = new Date(value).getTime();
  return Number.isFinite(ms) ? ms : null;
}

function clampPercent(value, min = 0, max = 100) {
  if (!Number.isFinite(value)) return min;
  return Math.max(min, Math.min(max, value));
}

function getTraceTimeBounds(run, traceEvents, toolCalls) {
  const values = [
    run?.started_at,
    run?.completed_at,
    ...traceEvents.map(event => event.timestamp),
    ...toolCalls.flatMap(call => [call.started_at, call.completed_at]),
  ].map(parseDateMs).filter(ms => ms !== null);
  const now = Date.now();
  const startMs = values.length ? Math.min(...values) : now;
  const endMs = Math.max(values.length ? Math.max(...values) : now, startMs + 1000);
  return {
    startMs,
    endMs,
    durationMs: Math.max(endMs - startMs, 1000),
    startIso: new Date(startMs).toISOString(),
    endIso: new Date(endMs).toISOString(),
  };
}

function getToolCallByTraceEventId(toolCalls) {
  const byId = {};
  toolCalls.forEach(call => {
    if (call.trace_event_start_id) byId[call.trace_event_start_id] = call;
    if (call.trace_event_end_id) byId[call.trace_event_end_id] = call;
  });
  return byId;
}

function formatTraceTimestamp(value) {
  if (!value) return '—';
  const date = new Date(value);
  if (Number.isNaN(date.getTime())) return '—';
  return date.toLocaleString([], { month: 'short', day: 'numeric', hour: 'numeric', minute: '2-digit', second: '2-digit' });
}

function formatTraceOffset(valueMs, startMs) {
  if (!Number.isFinite(valueMs) || !Number.isFinite(startMs)) return '—';
  const seconds = Math.max(0, (valueMs - startMs) / 1000);
  if (seconds < 10) return `+${seconds.toFixed(1)}s`;
  if (seconds < 60) return `+${Math.round(seconds)}s`;
  return `+${(seconds / 60).toFixed(1)}m`;
}

function verdictHeadline(productStatus, score) {
  if (productStatus === 'success') return score !== null && score !== undefined ? `Pass · ${score}/5` : 'Pass';
  if (productStatus === 'partial') return score !== null && score !== undefined ? `Partial · ${score}/5` : 'Partial';
  if (productStatus === 'failed') return score !== null && score !== undefined ? `Fail · ${score}/5` : 'Fail';
  if (productStatus === 'system_issue') return 'Evaluation unavailable';
  if (productStatus === 'running' || productStatus === 'pending') return 'In progress';
  return 'Result';
}

function verdictTone(productStatus, score) {
  if (productStatus === 'success') return 'pass';
  if (productStatus === 'partial') return 'warn';
  if (productStatus === 'failed') return 'fail';
  if (productStatus === 'system_issue') return 'muted';
  return 'neutral';
}

function verdictIcon(productStatus, score) {
  if (productStatus === 'success') return 'check';
  if (productStatus === 'partial') return 'alert';
  if (productStatus === 'failed') return 'x';
  if (productStatus === 'system_issue') return 'wrench';
  return 'clock';
}

function formatConfidence(confidence) {
  if (confidence === null || confidence === undefined || confidence === '') return 'confidence unavailable';
  const numeric = Number(confidence);
  if (!Number.isFinite(numeric)) return 'confidence unavailable';
  const normalized = numeric > 1 && numeric <= 100 ? numeric / 100 : numeric;
  return `${Math.round(Math.min(Math.max(normalized, 0), 1) * 100)}% confidence`;
}

function criterionTone(status) {
  if (status === 'passed' || status === 'pass') return 'pass';
  if (status === 'failed' || status === 'fail') return 'fail';
  if (status === 'not_applicable' || status === 'n/a') return 'muted';
  return 'warn';
}

function criterionIcon(status) {
  if (status === 'passed') return 'check';
  if (status === 'failed') return 'x';
  return 'alert';
}

function ScoreChip({ score }) {
  if (score === null || score === undefined) return null;
  const tone = score >= 4 ? 'pass' : score >= 2 ? 'warn' : 'fail';
  return (
    <span className={`score-chip score-chip-${tone}`} title={`Score ${score}/5`}>
      {score}/5
    </span>
  );
}

function ClassificationPill({ classification }) {
  if (classification !== 'product_bug' && classification !== 'environmental') return null;
  const label = classification === 'product_bug'
    ? 'Evaluation unavailable'
    : 'Environmental';
  return <span className={`pill pill-system pill-${classification}`}>{label}</span>;
}

window.DashboardPage = DashboardPage;
window.DashboardTimePicker = DashboardTimePicker;
window.dashboardTimeRangeKey = dashboardTimeRangeKey;
window.buildDashboardRangeQuery = buildDashboardRangeQuery;
window.parseDashboardTimeParam = parseDashboardTimeParam;
window.cleanTraceDetailValue = cleanTraceDetailValue;
window.formatToolArgs = formatToolArgs;
window.isFiniteNumberLike = isFiniteNumberLike;
window.RunsPage = RunsPage;
window.RunDetailPage = RunDetailPage;
