'use client';

import { useState, useEffect, useCallback, useRef, lazy, Suspense } from 'react';
import { signOut } from 'next-auth/react';
const TaskBoard = lazy(() => import('./TaskBoard'));
const TaskDetail = lazy(() => import('./TaskDetail'));
const TunnelToggle = lazy(() => import('./TunnelToggle'));
import type { Task } from '@/src/types';
import type { WebTerminalHandle } from './WebTerminal';

const WebTerminal = lazy(() => import('./WebTerminal'));
const DocsViewer = lazy(() => import('./DocsViewer'));
const CodeViewer = lazy(() => import('./CodeViewer'));
const ProjectManager = lazy(() => import('./ProjectManager'));
const BrowserPanel = lazy(() => import('./BrowserPanel'));
const PipelineView = lazy(() => import('./PipelineView'));
const JobsView = lazy(() => import('./JobsView'));
const SchedulesView = lazy(() => import('./SchedulesView'));
const HelpDialog = lazy(() => import('./HelpDialog'));
const LogViewer = lazy(() => import('./LogViewer'));
const SkillsPanel = lazy(() => import('./SkillsPanel'));
const UsagePanel = lazy(() => import('./UsagePanel'));
const SessionView = lazy(() => import('./SessionView'));
const NewTaskModal = lazy(() => import('./NewTaskModal'));
const SettingsModal = lazy(() => import('./SettingsModal'));
const MonitorPanel = lazy(() => import('./MonitorPanel'));
const LoginStatusPanel = lazy(() => import('./LoginStatusPanel'));
const ActivityPanel = lazy(() => import('./ActivityPanel'));
const EnterpriseBadge = lazy(() => import('./EnterpriseBadge'));
const WorkspaceView = lazy(() => import('./WorkspaceView'));
// WorkspaceTree moved into ProjectDetail — no longer needed at Dashboard level
import { OnboardingBanner, OnboardingDrawer } from './OnboardingWizard';

interface UsageSummary {
  provider: string;
  totalInput: number;
  totalOutput: number;
  totalCost: number;
}

interface ProviderInfo {
  name: string;
  displayName: string;
  hasKey: boolean;
  enabled: boolean;
}

interface ProjectInfo {
  name: string;
  path: string;
  language: string | null;
}

function FloatingBrowser({ onClose }: { onClose: () => void }) {
  const [pos, setPos] = useState({ x: 60, y: 60 });
  const [size, setSize] = useState({ w: 700, h: 500 });
  const dragRef = useRef<{ startX: number; startY: number; origX: number; origY: number } | null>(null);
  const resizeRef = useRef<{ startX: number; startY: number; origW: number; origH: number } | null>(null);

  return (
    <div
      className="fixed z-50 bg-[var(--bg-secondary)] border border-[var(--border)] rounded-lg shadow-2xl flex flex-col overflow-hidden"
      style={{ left: pos.x, top: pos.y, width: size.w, height: size.h }}
    >
      <div
        className="flex items-center gap-2 px-3 py-1.5 bg-[var(--bg-tertiary)] border-b border-[var(--border)] cursor-move shrink-0 select-none"
        onMouseDown={(e) => {
          e.preventDefault();
          dragRef.current = { startX: e.clientX, startY: e.clientY, origX: pos.x, origY: pos.y };
          const onMove = (ev: MouseEvent) => {
            if (!dragRef.current) return;
            setPos({ x: Math.max(0, dragRef.current.origX + ev.clientX - dragRef.current.startX), y: Math.max(0, dragRef.current.origY + ev.clientY - dragRef.current.startY) });
          };
          const onUp = () => { dragRef.current = null; window.removeEventListener('mousemove', onMove); window.removeEventListener('mouseup', onUp); };
          window.addEventListener('mousemove', onMove);
          window.addEventListener('mouseup', onUp);
        }}
      >
        <span className="text-[11px] font-semibold text-[var(--text-primary)]">Browser</span>
        <button onClick={onClose} className="ml-auto text-[var(--text-secondary)] hover:text-[var(--red)] text-sm leading-none">✕</button>
      </div>
      <div className="flex-1 min-h-0 flex flex-col">
        <BrowserPanel />
      </div>
      <div
        onMouseDown={(e) => {
          e.preventDefault();
          e.stopPropagation();
          resizeRef.current = { startX: e.clientX, startY: e.clientY, origW: size.w, origH: size.h };
          const onMove = (ev: MouseEvent) => {
            if (!resizeRef.current) return;
            setSize({ w: Math.max(400, resizeRef.current.origW + ev.clientX - resizeRef.current.startX), h: Math.max(300, resizeRef.current.origH + ev.clientY - resizeRef.current.startY) });
          };
          const onUp = () => { resizeRef.current = null; window.removeEventListener('mousemove', onMove); window.removeEventListener('mouseup', onUp); };
          window.addEventListener('mousemove', onMove);
          window.addEventListener('mouseup', onUp);
        }}
        className="absolute bottom-0 right-0 w-4 h-4 cursor-se-resize"
        style={{ background: 'linear-gradient(135deg, transparent 50%, var(--border) 50%)' }}
      />
    </div>
  );
}

export default function Dashboard({ user }: { user: any }) {
  const [viewMode, setViewMode] = useState<'tasks' | 'sessions' | 'terminal' | 'docs' | 'projects' | 'pipelines' | 'jobs' | 'schedules' | 'workspace' | 'skills' | 'logs' | 'usage'>('terminal');

  // Honour `?view=<mode>` from the URL so external links (eg the VSCode
  // extension) can deep-link straight into a section. Only views that have a
  // top-level render branch are accepted. `workspace` and `sessions` live
  // inside ProjectDetail, so they alias to `projects` (where you pick the
  // project that contains the workspace/session).
  useEffect(() => {
    const params = new URLSearchParams(window.location.search);
    const raw = params.get('view');
    if (raw) {
      const aliases: Record<string, string> = { workspace: 'projects', sessions: 'projects' };
      const v = aliases[raw] || raw;
      const valid = ['tasks', 'terminal', 'docs', 'projects', 'pipelines', 'jobs', 'schedules', 'skills', 'logs', 'usage'];
      if (valid.includes(v)) setViewMode(v as any);
    }
    // Optional deep-link to a specific pipeline run — used by the extension
    // Jobs tab when surfacing a dispatch's target pipeline.
    const pid = params.get('pipelineId');
    if (pid) setPendingPipelineId(pid);
  }, []);
  // workspaceProject state kept for forge:open-terminal event compatibility
  const [workspaceProject, setWorkspaceProject] = useState<{ name: string; path: string } | null>(null);
  const [browserMode, setBrowserMode] = useState<'none' | 'float' | 'right' | 'left'>('none');
  const [showBrowserMenu, setShowBrowserMenu] = useState(false);
  const [browserWidth, setBrowserWidth] = useState(600);
  const browserDragRef = useRef<{ startX: number; startW: number } | null>(null);
  const [browserDragging, setBrowserDragging] = useState(false);
  const [tasks, setTasks] = useState<Task[]>([]);
  const [activeTaskId, setActiveTaskId] = useState<string | null>(null);
  const [showNewTask, setShowNewTask] = useState(false);
  const [showSettings, setShowSettings] = useState(false);
  const [needsOnboarding, setNeedsOnboarding] = useState(false);
  const [showOnboarding, setShowOnboarding] = useState(false);
  // E4: when EnterpriseBadge fires forge:open-onboarding after a fresh
  // key add, the source id rides along so the wizard scopes to that
  // new tenant immediately instead of resolving the priority chain.
  const [onboardingSourceId, setOnboardingSourceId] = useState<string | null>(null);
  useEffect(() => {
    const handler = (e: Event) => {
      const detail = (e as CustomEvent).detail || {};
      setOnboardingSourceId(detail.source_id || null);
      setShowOnboarding(true);
    };
    window.addEventListener('forge:open-onboarding', handler);
    return () => window.removeEventListener('forge:open-onboarding', handler);
  }, []);
  useEffect(() => {
    fetch('/api/onboarding').then(r => r.json()).then(j => {
      const need = !!(j?.ok && !j.onboardingCompleted);
      setNeedsOnboarding(need);
      // Auto-open the drawer when setup is needed — saves the user a
      // click. When there's no enterprise yet, the wizard's first page
      // is a splash asking "Add enterprise key, or continue with
      // public?" so opt-out is one click away.
      if (need) setShowOnboarding(true);
    }).catch(() => {});
  }, []);
  const [showMonitor, setShowMonitor] = useState(false);
  const [showLoginStatus, setShowLoginStatus] = useState(false);
  const [loginBadge, setLoginBadge] = useState<{ broken: number; total: number } | null>(null);
  const [showHelp, setShowHelp] = useState(false);
  const [usage, setUsage] = useState<UsageSummary[]>([]);
  const [providers, setProviders] = useState<ProviderInfo[]>([]);
  const [projects, setProjects] = useState<ProjectInfo[]>([]);
  const [onlineCount, setOnlineCount] = useState<{ total: number; remote: number }>({ total: 0, remote: 0 });
  const [versionInfo, setVersionInfo] = useState<{ current: string; latest: string; hasUpdate: boolean } | null>(null);
  const [notifications, setNotifications] = useState<any[]>([]);
  const [unreadCount, setUnreadCount] = useState(0);
  const [showNotifications, setShowNotifications] = useState(false);
  const [expandedNotif, setExpandedNotif] = useState<string | null>(null);
  const [showUserMenu, setShowUserMenu] = useState(false);
  const [theme, setTheme] = useState<'dark' | 'light'>('dark');
  const [cacheInfo, setCacheInfo] = useState<{ entries: { name: string; bytes: number; category?: 'cloned-projects' | 'tmp' | 'scratch' }[]; total_bytes: number } | null>(null);
  const [showCacheMenu, setShowCacheMenu] = useState(false);
  const [cacheBusy, setCacheBusy] = useState(false);
  const [displayName, setDisplayName] = useState(user?.name || 'Forge');
  const [profileDept, setProfileDept] = useState('');
  const terminalRef = useRef<WebTerminalHandle>(null);

  // Cache load + clear helpers. Two categories live under the same
  // endpoint: `cloned-projects` (pipeline git clones) and `tmp` (chat's
  // save_tmp_file output). Entries arrive flat with a `category` field;
  // the UI groups them visually and offers per-category Clear in
  // addition to the catch-all Clear All.
  const loadCacheInfo = async () => {
    try {
      const r = await fetch('/api/cache');
      if (!r.ok) return;
      const data = await r.json();
      setCacheInfo({ entries: data.entries || [], total_bytes: data.total_bytes || 0 });
    } catch { /* leave previous */ }
  };
  const humanBytes = (n: number) => {
    if (!n) return '0 B';
    if (n < 1024) return `${n} B`;
    if (n < 1024 ** 2) return `${(n / 1024).toFixed(1)} KB`;
    if (n < 1024 ** 3) return `${(n / 1024 ** 2).toFixed(1)} MB`;
    return `${(n / 1024 ** 3).toFixed(2)} GB`;
  };
  // name + category together identify an entry (cloned-projects/<name>
  // and tmp/<name> are distinct files). Omit both → wipe all. Omit name
  // but pass category → wipe that whole category.
  const clearCache = async (name?: string, category?: 'cloned-projects' | 'tmp' | 'scratch') => {
    if (cacheBusy) return;
    const labelFor = (cat?: string) =>
      cat === 'tmp' ? 'tmp file'
      : cat === 'scratch' ? 'legacy scratch file'
      : cat === 'cloned-projects' ? 'cached repo'
      : 'cache entry';
    const prompt = name
      ? `Delete ${labelFor(category)} "${name}"?`
      : category === 'tmp'
        ? 'Delete ALL chat-saved tmp/ files? They are ephemeral by design.'
        : category === 'scratch'
          ? 'Delete ALL legacy files at scratch/ root? (Task workdirs and CLAUDE.md marker are NOT touched.)'
          : category === 'cloned-projects'
            ? 'Delete ALL cached repos? They will be re-cloned on next pipeline run.'
            : 'Delete ALL cached files (cloned repos + tmp/ + scratch/ root)? Cloned repos will be re-cloned; tmp/ and scratch/ files are gone for good.';
    if (!confirm(prompt)) return;
    setCacheBusy(true);
    try {
      const params = new URLSearchParams();
      if (name) params.set('name', name);
      if (category) params.set('category', category);
      const q = params.toString() ? `?${params}` : '';
      const r = await fetch(`/api/cache${q}`, { method: 'DELETE' });
      const data = await r.json();
      if (data?.ok) {
        await loadCacheInfo();
      } else {
        alert(`Cache clear failed: ${data?.error || `HTTP ${r.status}`}`);
      }
    } catch (e) {
      alert(`Cache clear failed: ${(e as Error).message}`);
    } finally {
      setCacheBusy(false);
    }
  };

  // Theme: load from localStorage + apply
  useEffect(() => {
    const saved = localStorage.getItem('forge-theme') as 'dark' | 'light' | null;
    if (saved) {
      setTheme(saved);
      document.documentElement.setAttribute('data-theme', saved === 'light' ? 'light' : '');
    }
  }, []);

  const toggleTheme = () => {
    const next = theme === 'dark' ? 'light' : 'dark';
    setTheme(next);
    document.documentElement.setAttribute('data-theme', next === 'light' ? 'light' : '');
    localStorage.setItem('forge-theme', next);
  };

  // Fetch display name from settings
  const refreshDisplayName = useCallback(() => {
    fetch('/api/settings').then(r => r.json())
      .then((s: any) => {
        if (s.displayName) setDisplayName(s.displayName);
        if (typeof s.dept === 'string') setProfileDept(s.dept);
      })
      .catch(() => {});
  }, []);
  useEffect(() => { refreshDisplayName(); }, [refreshDisplayName]);

  // Reflect the current user's name in the browser tab title
  useEffect(() => {
    document.title = displayName && displayName !== 'Forge' ? `Forge — ${displayName}` : 'Forge';
  }, [displayName]);

  // Listen for open-terminal events from ProjectManager
  useEffect(() => {
    const handler = (e: Event) => {
      const { projectPath, projectName, agentId, resumeMode, sessionId, profileEnv } = (e as CustomEvent).detail;
      setViewMode('terminal');
      setTimeout(() => {
        terminalRef.current?.openProjectTerminal?.(projectPath, projectName, agentId, resumeMode, sessionId, profileEnv);
      }, 300);
    };
    window.addEventListener('forge:open-terminal', handler);
    return () => window.removeEventListener('forge:open-terminal', handler);
  }, []);

  // Listen for navigation events (e.g. from ProjectDetail → Pipelines, or
  // JobsView → Logs with a search pre-filter).
  const [pendingPipelineId, setPendingPipelineId] = useState<string | null>(null);
  const [pendingLogSearch, setPendingLogSearch] = useState<string>('');
  useEffect(() => {
    const handler = (e: Event) => {
      const { view, pipelineId, logSearch } = (e as CustomEvent).detail;
      if (view) setViewMode(view);
      if (pipelineId) setPendingPipelineId(pipelineId);
      if (logSearch != null) setPendingLogSearch(String(logSearch));
    };
    window.addEventListener('forge:navigate', handler);
    return () => window.removeEventListener('forge:navigate', handler);
  }, []);

  // Version check (on mount + every 10 min)
  useEffect(() => {
    const check = () => fetch('/api/version').then(r => r.json()).then(setVersionInfo).catch(() => {});
    check();
    const id = setInterval(check, 10 * 60 * 1000);
    return () => clearInterval(id);
  }, []);

  // Login status badge — poll cached results on a 10s interval so the
  // red-dot count picks up changes from the Test button / wizard apply /
  // Reinstall without forcing the user to open + close the full panel.
  // GET is cheap (reads JSON file, no probe), so this is fine.
  useEffect(() => {
    const refresh = () => {
      fetch('/api/login-status')
        .then((r) => r.json())
        .then((j) => {
          const rows = (j.rows || []) as Array<{ result: { ok: boolean } | null }>;
          const broken = rows.filter((r) => r.result && !r.result.ok).length;
          setLoginBadge({ broken, total: rows.length });
        })
        .catch(() => {});
    };
    refresh();
    const id = setInterval(refresh, 10_000);
    return () => clearInterval(id);
  }, []);

  // Notifications: poll unread count at 30s, full fetch when panel opens
  const fetchNotifications = useCallback(() => {
    fetch('/api/notifications').then(r => r.json()).then(data => {
      setNotifications(data.notifications || []);
      setUnreadCount(data.unread || 0);
    }).catch(() => {});
  }, []);

  useEffect(() => {
    fetchNotifications();
    const id = setInterval(fetchNotifications, 30000);
    return () => clearInterval(id);
  }, [fetchNotifications]);

  // Refresh full list when notification panel opens
  useEffect(() => {
    if (showNotifications) fetchNotifications();
  }, [showNotifications, fetchNotifications]);

  // Heartbeat for online user tracking
  useEffect(() => {
    const ping = () => {
      fetch('/api/online', { method: 'POST' })
        .then(r => r.json())
        .then(setOnlineCount)
        .catch(() => {});
    };
    ping();
    const id = setInterval(ping, 60_000); // every 60s
    return () => clearInterval(id);
  }, []);

  const fetchData = useCallback(async () => {
    try {
      // Only fetch what's needed for current view
      const fetches: Promise<void>[] = [
        fetch('/api/projects').then(async r => { if (r.ok) setProjects(await r.json()); }),
      ];
      // Tasks + status only when relevant tabs are active
      if (viewMode === 'tasks' || viewMode === 'terminal') {
        fetches.push(
          fetch('/api/tasks').then(async r => { if (r.ok) setTasks(await r.json()); }),
        );
      }
      if (viewMode === 'usage') {
        fetches.push(
          fetch('/api/status').then(async r => { if (r.ok) { const s = await r.json(); setProviders(s.providers); setUsage(s.usage); } }),
        );
      }
      await Promise.all(fetches);
    } catch {}
  }, [viewMode]);

  useEffect(() => {
    fetchData();
    // Poll less aggressively: 10s instead of 5s
    const interval = setInterval(fetchData, 10000);
    return () => clearInterval(interval);
  }, [fetchData]);

  const activeTask = tasks.find(t => t.id === activeTaskId);
  const running = tasks.filter(t => t.status === 'running');
  const queued = tasks.filter(t => t.status === 'queued');

  return (
    <div className="h-screen flex flex-col">
      {needsOnboarding && (
        <OnboardingBanner onOpen={() => setShowOnboarding(true)} />
      )}
      {showOnboarding && (
        <OnboardingDrawer
          initialSourceId={onboardingSourceId}
          onClose={() => { setShowOnboarding(false); setOnboardingSourceId(null); }}
          onComplete={() => {
            setShowOnboarding(false);
            setOnboardingSourceId(null);
            setNeedsOnboarding(false);
            // Reload settings so chat picks up new API profile etc.
            fetchData();
          }}
        />
      )}
      <div className="flex-1 flex min-h-0">
      {/* Browser — left side */}
      {browserMode === 'left' && (
        <>
        <div style={{ width: browserWidth }} className="shrink-0 flex flex-col relative">
          <Suspense fallback={null}><BrowserPanel onClose={() => setBrowserMode('none')} /></Suspense>
          {browserDragging && <div className="absolute inset-0 z-10" />}
        </div>
        <div
          onMouseDown={(e) => {
            e.preventDefault();
            browserDragRef.current = { startX: e.clientX, startW: browserWidth };
            setBrowserDragging(true);
            const onMove = (ev: MouseEvent) => {
              if (!browserDragRef.current) return;
              setBrowserWidth(Math.max(320, Math.min(1200, browserDragRef.current.startW + (ev.clientX - browserDragRef.current.startX))));
            };
            const onUp = () => { browserDragRef.current = null; setBrowserDragging(false); window.removeEventListener('mousemove', onMove); window.removeEventListener('mouseup', onUp); };
            window.addEventListener('mousemove', onMove);
            window.addEventListener('mouseup', onUp);
          }}
          className="w-1 bg-[var(--border)] cursor-col-resize shrink-0 hover:bg-[var(--accent)]/50"
        />
        </>
      )}

      {/* Forge main area */}
      <div className="flex-1 flex flex-col min-w-0 min-h-0 overflow-hidden">
      {/* Top bar */}
      <header className="h-12 border-b-2 border-[var(--border)] flex items-center justify-between px-4 shrink-0 bg-[var(--bg-secondary)]">
        <div className="flex items-center gap-4">
          <img src="/icon.png" alt="Forge" width={28} height={28} className="rounded" />
          <span className="text-sm font-bold text-[var(--accent)]">Forge</span>
          {/* Enterprise sources — popover with per-tenant Re-sync / Reinstall /
              Remove rows + inline Add tenant. Sits where the displayName used
              to be so it's the first signal you see on cold open. */}
          <Suspense fallback={null}>
            <EnterpriseBadge onOpenSettings={() => setShowSettings(true)} />
          </Suspense>
          {versionInfo && (
            <span className="flex items-center gap-1.5">
              <span className="text-[10px] text-[var(--text-secondary)]">v{versionInfo.current}</span>
              {versionInfo.hasUpdate && (
                <span
                  className="text-[9px] px-1.5 py-0.5 bg-[var(--accent)]/15 text-[var(--accent)] rounded cursor-default"
                  title={`forge upgrade\nnpm install -g @aion0/forge@latest`}
                >
                  v{versionInfo.latest} available
                </span>
              )}
              <button
                onClick={async () => {
                  const res = await fetch('/api/version?force=1');
                  const data = await res.json();
                  setVersionInfo(data);
                  if (data.hasUpdate) fetchNotifications();
                }}
                className="text-[9px] px-1 py-0.5 text-[var(--text-secondary)] hover:text-[var(--text-primary)]"
                title="Check for updates"
              >
                ↻
              </button>
            </span>
          )}

          {/* View mode toggle */}
          <div className="flex items-center bg-[var(--bg-tertiary)] rounded p-0.5">
            {/* Workspace */}
            {(['terminal', 'projects'] as const).map(mode => (
              <button
                key={mode}
                onClick={() => setViewMode(mode)}
                className={`text-[12px] px-2.5 py-0.5 rounded transition-colors ${
                  viewMode === mode
                    ? 'bg-[var(--accent)]/15 text-[var(--accent)] shadow-sm'
                    : 'text-[var(--text-secondary)] hover:text-[var(--text-primary)]'
                }`}
              >
                {{ terminal: 'Vibe Coding', projects: 'Projects' }[mode]}
              </button>
            ))}
            <span className="w-[2px] h-4 bg-[var(--text-secondary)]/30 mx-1.5" />
            {/* Docs */}
            <button
              onClick={() => setViewMode('docs')}
              className={`text-[12px] px-2.5 py-0.5 rounded transition-colors ${
                viewMode === 'docs'
                  ? 'bg-[var(--accent)]/15 text-[var(--accent)] shadow-sm'
                  : 'text-[var(--text-secondary)] hover:text-[var(--text-primary)]'
              }`}
            >
              Docs
            </button>
            <span className="w-[2px] h-4 bg-[var(--text-secondary)]/30 mx-1.5" />
            {/* Automation — sub-tabs: tasks / pipelines / schedules.
                Jobs is deprecated and hidden from the nav (backend still
                present in case of reversion, but no UI entry point). */}
            <button
              onClick={() => {
                if (!['tasks', 'pipelines', 'schedules'].includes(viewMode)) setViewMode('schedules');
              }}
              className={`text-[12px] px-2.5 py-0.5 rounded transition-colors ${
                ['tasks', 'pipelines', 'schedules'].includes(viewMode)
                  ? 'bg-[var(--accent)]/15 text-[var(--accent)] shadow-sm'
                  : 'text-[var(--text-secondary)] hover:text-[var(--text-primary)]'
              }`}
            >
              Automation
            </button>
            {/* Activity sub-pill — sits next to Automation since its content
                (running pipelines + upcoming schedules + recent runs) is the
                live read-side of Automation. Click anywhere → dropdown with
                3 sections + a "view" jump to the run. */}
            <Suspense fallback={null}><ActivityPanel /></Suspense>
            <span className="w-[2px] h-4 bg-[var(--text-secondary)]/30 mx-1.5" />
            {/* Marketplace */}
            <button
              onClick={() => setViewMode('skills')}
              className={`text-[12px] px-2.5 py-0.5 rounded transition-colors ${
                viewMode === 'skills'
                  ? 'bg-[var(--accent)]/15 text-[var(--accent)] shadow-sm'
                  : 'text-[var(--text-secondary)] hover:text-[var(--text-primary)]'
              }`}
            >
              Marketplace
            </button>
          </div>

        </div>
        <div className="flex items-center gap-2.5">
          {/* (Automation context actions moved to the secondary toolbar row below) */}
          {/* Help / Browser moved into the user dropdown — see below. */}
          <Suspense fallback={null}><TunnelToggle /></Suspense>
          {onlineCount.total > 0 && (
            <span className="text-[10px] text-[var(--text-secondary)] flex items-center gap-1" title={`${onlineCount.total} online${onlineCount.remote > 0 ? `, ${onlineCount.remote} remote` : ''}`}>
              <span className="text-green-500">●</span>
              {onlineCount.total}
            </span>
          )}
          <span className="w-[2px] h-4 bg-[var(--text-secondary)]/30" />
          {/* Alerts — bell glyph keeps the same role; unread pill rides the bell. */}
          <div className="relative">
            <button
              onClick={() => { setShowNotifications(v => !v); setShowUserMenu(false); }}
              className="text-[14px] leading-none text-[var(--text-secondary)] hover:text-[var(--text-primary)] relative px-1"
              title="Alerts"
              aria-label="Alerts"
            >
              <span aria-hidden>🔔</span>
              {unreadCount > 0 && (
                <span className="absolute -top-1.5 -right-1.5 min-w-[14px] h-[14px] rounded-full bg-[var(--red)] text-[8px] text-white flex items-center justify-center px-1 font-bold">
                  {unreadCount > 99 ? '99+' : unreadCount}
                </span>
              )}
            </button>
            {showNotifications && (
              <>
              <div className="fixed inset-0 z-40" onClick={() => setShowNotifications(false)} />
              <div className="absolute right-0 top-8 w-[360px] max-h-[480px] bg-[var(--bg-secondary)] border border-[var(--border)] rounded-lg shadow-xl z-50 flex flex-col">
                <div className="flex items-center justify-between px-3 py-2 border-b border-[var(--border)]">
                  <span className="text-xs font-bold text-[var(--text-primary)]">Notifications</span>
                  <div className="flex items-center gap-2">
                    {unreadCount > 0 && (
                      <button
                        onClick={async () => {
                          await fetch('/api/notifications', {
                            method: 'POST',
                            headers: { 'Content-Type': 'application/json' },
                            body: JSON.stringify({ action: 'markAllRead' }),
                          });
                          fetchNotifications();
                        }}
                        className="text-[9px] text-[var(--accent)] hover:underline"
                      >
                        Mark all read
                      </button>
                    )}
                    <button
                      onClick={() => setShowNotifications(false)}
                      className="text-[10px] text-[var(--text-secondary)] hover:text-[var(--text-primary)]"
                    >
                      Close
                    </button>
                  </div>
                </div>
                <div className="flex-1 overflow-y-auto">
                  {notifications.length === 0 ? (
                    <div className="p-6 text-center text-xs text-[var(--text-secondary)]">No notifications</div>
                  ) : (
                    notifications.map((n: any) => {
                      const expanded = expandedNotif === n.id;
                      return (
                      <div
                        key={n.id}
                        onClick={async () => {
                          setExpandedNotif(expanded ? null : n.id);
                          if (!expanded && !n.read) {
                            await fetch('/api/notifications', {
                              method: 'POST',
                              headers: { 'Content-Type': 'application/json' },
                              body: JSON.stringify({ action: 'markRead', id: n.id }),
                            });
                            fetchNotifications();
                          }
                        }}
                        className={`group px-3 py-2 border-b border-[var(--border)]/50 hover:bg-[var(--bg-tertiary)] cursor-pointer ${!n.read ? 'bg-[var(--accent)]/5' : ''}`}
                      >
                        <div className="flex items-start gap-2">
                          <span className="text-[10px] mt-0.5 shrink-0">
                            {n.type === 'task_done' ? '✅' : n.type === 'task_failed' ? '❌' : n.type === 'pipeline_done' ? '🔗' : n.type === 'pipeline_failed' ? '💔' : n.type === 'tunnel' ? '🌐' : 'ℹ️'}
                          </span>
                          <div className="flex-1 min-w-0">
                            <div className="flex items-center gap-1">
                              <span className={`text-[11px] ${expanded ? 'break-words whitespace-pre-wrap' : 'truncate'} ${!n.read ? 'font-semibold text-[var(--text-primary)]' : 'text-[var(--text-secondary)]'}`}>
                                {n.title}
                              </span>
                              {!n.read && <span className="w-1.5 h-1.5 rounded-full bg-[var(--accent)] shrink-0" />}
                            </div>
                            {n.body && (
                              <p className={`text-[9px] text-[var(--text-secondary)] mt-0.5 ${expanded ? 'break-words whitespace-pre-wrap' : 'truncate'}`}>{n.body}</p>
                            )}
                            <span className="text-[8px] text-[var(--text-secondary)]">
                              {new Date(n.createdAt).toLocaleString()}
                              <span className="ml-1 opacity-50">· {expanded ? 'click to collapse' : 'click for detail'}</span>
                            </span>
                          </div>
                          <div className="hidden group-hover:flex items-center gap-1 shrink-0">
                            {!n.read && (
                              <button
                                onClick={async (e) => {
                                  e.stopPropagation();
                                  await fetch('/api/notifications', {
                                    method: 'POST',
                                    headers: { 'Content-Type': 'application/json' },
                                    body: JSON.stringify({ action: 'markRead', id: n.id }),
                                  });
                                  fetchNotifications();
                                }}
                                className="text-[8px] px-1 py-0.5 text-[var(--accent)] hover:underline"
                              >
                                read
                              </button>
                            )}
                            <button
                              onClick={async (e) => {
                                e.stopPropagation();
                                await fetch('/api/notifications', {
                                  method: 'POST',
                                  headers: { 'Content-Type': 'application/json' },
                                  body: JSON.stringify({ action: 'delete', id: n.id }),
                                });
                                fetchNotifications();
                              }}
                              className="text-[8px] px-1 py-0.5 text-red-400 hover:underline"
                            >
                              del
                            </button>
                          </div>
                        </div>
                      </div>
                      );
                    })
                  )}
                </div>
              </div>
              </>
            )}
          </div>
          {/* User menu */}
          <div className="relative">
            <button
              onClick={() => {
                const next = !showUserMenu;
                setShowUserMenu(next);
                setShowNotifications(false);
                // Lazy-load cache size whenever the menu opens — keeps
                // the cache row's "· N MB" current without polling.
                if (next) loadCacheInfo();
              }}
              className="text-[var(--text-secondary)] hover:text-[var(--text-primary)] flex items-center gap-1 px-1"
            >
              <span className="flex flex-col items-end leading-tight">
                {profileDept && (
                  <span className="text-[8px] px-1 rounded bg-emerald-500/15 text-emerald-500 border border-emerald-500/30" title="Active department">
                    {profileDept}
                  </span>
                )}
                <span className="text-[9px]">{displayName}</span>
              </span>
              <span className="text-[8px]">▾</span>
            </button>
            {showUserMenu && (
              <>
                <div className="fixed inset-0 z-40" onClick={() => setShowUserMenu(false)} />
                <div className="absolute right-0 top-8 w-[170px] bg-[var(--bg-secondary)] border border-[var(--border)] rounded-lg shadow-xl z-50 py-1">
                  {/* Settings promoted to the top — most-reached item in this
                      menu. Each row leads with a glyph so it's scannable. */}
                  <button
                    onClick={() => { setShowSettings(true); setShowUserMenu(false); }}
                    className="w-full text-left text-[11px] px-3 py-1.5 text-[var(--text-primary)] hover:bg-[var(--bg-tertiary)] flex items-center gap-2"
                  >
                    <span className="w-3 text-center">⚙</span><span>Settings</span>
                  </button>
                  {/* Chat (web) is a separate route — open in a new tab so
                      the dashboard isn't replaced. Promoted up here so it's
                      one of the prominent items, not buried below Logs. */}
                  <a
                    href="/chat"
                    target="_blank"
                    rel="noopener noreferrer"
                    className="block w-full text-left text-[11px] px-3 py-1.5 text-[var(--text-primary)] hover:bg-[var(--bg-tertiary)] flex items-center gap-2"
                  >
                    <span className="w-3 text-center">💬</span>
                    <span className="flex-1">Chat (web)</span>
                    <span className="text-[9px] text-[var(--text-secondary)]">↗</span>
                  </a>
                  <div className="border-t border-[var(--border)] my-1" />
                  <button
                    onClick={() => { setShowMonitor(true); setShowUserMenu(false); }}
                    className="w-full text-left text-[11px] px-3 py-1.5 text-[var(--text-secondary)] hover:text-[var(--text-primary)] hover:bg-[var(--bg-tertiary)] flex items-center gap-2"
                  >
                    <span className="w-3 text-center">📊</span><span>Monitor</span>
                  </button>
                  <button
                    onClick={() => { setShowLoginStatus(true); setShowUserMenu(false); }}
                    className="w-full text-left text-[11px] px-3 py-1.5 text-[var(--text-secondary)] hover:text-[var(--text-primary)] hover:bg-[var(--bg-tertiary)] flex items-center gap-2"
                  >
                    <span className="w-3 text-center">🔐</span>
                    <span className="flex-1">Login Status</span>
                    {loginBadge && loginBadge.broken > 0 && (
                      <span className="text-[9px] text-red-400">{loginBadge.broken}/{loginBadge.total}</span>
                    )}
                  </button>
                  <button
                    onClick={() => { setViewMode('usage'); setShowUserMenu(false); }}
                    className="w-full text-left text-[11px] px-3 py-1.5 text-[var(--text-secondary)] hover:text-[var(--text-primary)] hover:bg-[var(--bg-tertiary)] flex items-center gap-2"
                  >
                    <span className="w-3 text-center">💰</span><span>Usage</span>
                  </button>
                  <button
                    onClick={() => { setViewMode('logs'); setShowUserMenu(false); }}
                    className="w-full text-left text-[11px] px-3 py-1.5 text-[var(--text-secondary)] hover:text-[var(--text-primary)] hover:bg-[var(--bg-tertiary)] flex items-center gap-2"
                  >
                    <span className="w-3 text-center">📜</span><span>Logs</span>
                  </button>
                  <a
                    href="/mobile"
                    target="_blank"
                    rel="noopener noreferrer"
                    className="block w-full text-left text-[11px] px-3 py-1.5 text-[var(--text-secondary)] hover:text-[var(--text-primary)] hover:bg-[var(--bg-tertiary)] flex items-center gap-2"
                  >
                    <span className="w-3 text-center">📱</span>
                    <span className="flex-1">Mobile View</span>
                    <span className="text-[9px] text-[var(--text-secondary)]">↗</span>
                  </a>
                  {/* Browser — toggling reveals the layout options inline so the
                      sub-menu lives inside the user dropdown (no nested popup). */}
                  <button
                    onClick={() => setShowBrowserMenu(v => !v)}
                    className="w-full text-left text-[11px] px-3 py-1.5 text-[var(--text-secondary)] hover:text-[var(--text-primary)] hover:bg-[var(--bg-tertiary)] flex items-center gap-2"
                  >
                    <span className="w-3 text-center">🌐</span>
                    <span className="flex-1">Browser{browserMode !== 'none' ? ` · ${browserMode}` : ''}</span>
                    <span className="text-[9px] text-[var(--text-secondary)]">{showBrowserMenu ? '▾' : '▸'}</span>
                  </button>
                  {showBrowserMenu && (
                    <div className="pl-6 pr-1 py-0.5 border-l border-[var(--border)] ml-3 mb-1">
                      <button onClick={() => { setBrowserMode('float'); setShowBrowserMenu(false); setShowUserMenu(false); }} className={`w-full text-left px-2 py-1 text-[10px] hover:bg-[var(--bg-tertiary)] ${browserMode === 'float' ? 'text-[var(--accent)]' : 'text-[var(--text-primary)]'}`}>
                        Floating Window
                      </button>
                      <button onClick={() => { setBrowserMode('right'); setShowBrowserMenu(false); setShowUserMenu(false); }} className={`w-full text-left px-2 py-1 text-[10px] hover:bg-[var(--bg-tertiary)] ${browserMode === 'right' ? 'text-[var(--accent)]' : 'text-[var(--text-primary)]'}`}>
                        Right Side
                      </button>
                      <button onClick={() => { setBrowserMode('left'); setShowBrowserMenu(false); setShowUserMenu(false); }} className={`w-full text-left px-2 py-1 text-[10px] hover:bg-[var(--bg-tertiary)] ${browserMode === 'left' ? 'text-[var(--accent)]' : 'text-[var(--text-primary)]'}`}>
                        Left Side
                      </button>
                      <button onClick={() => {
                        const url = localStorage.getItem('forge-browser-url');
                        if (url) window.open(url, '_blank');
                        else { const u = prompt('Enter URL to open:'); if (u) window.open(u.trim(), '_blank'); }
                        setShowBrowserMenu(false); setShowUserMenu(false);
                      }} className="w-full text-left px-2 py-1 text-[10px] text-[var(--text-primary)] hover:bg-[var(--bg-tertiary)]">
                        New Tab
                      </button>
                      {browserMode !== 'none' && (
                        <button onClick={() => { setBrowserMode('none'); setShowBrowserMenu(false); setShowUserMenu(false); }} className="w-full text-left px-2 py-1 text-[10px] text-red-400 hover:bg-[var(--bg-tertiary)]">
                          Close Browser
                        </button>
                      )}
                    </div>
                  )}
                  {/* Cache — two on-disk pools: cloned-projects (auto-cloned
                      gitlab repos for pipelines) + tmp (chat's save_tmp_file
                      output). Groups visually + offers per-category Clear
                      plus Clear All. Same backend as `forge clean cache`. */}
                  <button
                    onClick={() => setShowCacheMenu(v => !v)}
                    className="w-full text-left text-[11px] px-3 py-1.5 text-[var(--text-secondary)] hover:text-[var(--text-primary)] hover:bg-[var(--bg-tertiary)] flex items-center gap-2"
                  >
                    <span className="w-3 text-center">💾</span>
                    <span className="flex-1">Cache</span>
                    <span className="text-[9px] text-[var(--text-secondary)]">
                      {cacheInfo ? humanBytes(cacheInfo.total_bytes) : '…'}
                    </span>
                    <span className="text-[9px] text-[var(--text-secondary)]">{showCacheMenu ? '▾' : '▸'}</span>
                  </button>
                  {showCacheMenu && (
                    <div className="pl-6 pr-1 py-0.5 border-l border-[var(--border)] ml-3 mb-1 max-h-48 overflow-y-auto">
                      {!cacheInfo && (
                        <div className="text-[10px] text-[var(--text-secondary)] py-1 px-2">Loading…</div>
                      )}
                      {cacheInfo && cacheInfo.entries.length === 0 && (
                        <div className="text-[10px] text-[var(--text-secondary)] py-1 px-2">Empty.</div>
                      )}
                      {/* Render entries grouped by category (cloned-projects
                          then tmp) with a category header row + per-category
                          "Clear category" link. Entries without an explicit
                          category (older API responses) fall under cloned-
                          projects for back-compat. */}
                      {cacheInfo && cacheInfo.entries.length > 0 && (() => {
                        const groups: { key: 'cloned-projects' | 'tmp' | 'scratch'; label: string; entries: typeof cacheInfo.entries }[] = [
                          { key: 'cloned-projects', label: 'Cloned repos', entries: cacheInfo.entries.filter(e => (e.category || 'cloned-projects') === 'cloned-projects') },
                          { key: 'tmp', label: 'Chat tmp/', entries: cacheInfo.entries.filter(e => e.category === 'tmp') },
                          { key: 'scratch', label: 'Legacy scratch/', entries: cacheInfo.entries.filter(e => e.category === 'scratch') },
                        ];
                        return groups.filter(g => g.entries.length > 0).map((g) => {
                          const groupBytes = g.entries.reduce((s, e) => s + e.bytes, 0);
                          return (
                            <div key={g.key} className="mb-1">
                              <div className="flex items-center gap-2 px-2 py-0.5 text-[9px] uppercase tracking-wide text-[var(--text-secondary)] border-b border-[var(--border)]">
                                <span className="flex-1">{g.label}</span>
                                <span>{humanBytes(groupBytes)}</span>
                                <button
                                  disabled={cacheBusy}
                                  onClick={() => clearCache(undefined, g.key)}
                                  className="text-[9px] text-red-400 hover:underline disabled:opacity-50"
                                  title={`Clear all ${g.label.toLowerCase()}`}
                                >
                                  clear
                                </button>
                              </div>
                              {g.entries.map((e) => (
                                <button
                                  key={`${g.key}/${e.name}`}
                                  disabled={cacheBusy}
                                  onClick={() => clearCache(e.name, g.key)}
                                  className="w-full text-left px-2 py-1 text-[10px] text-[var(--text-primary)] hover:bg-[var(--bg-tertiary)] flex items-center gap-2 disabled:opacity-50"
                                  title={`Click to delete ${g.key}/${e.name}`}
                                >
                                  <span className="flex-1 truncate">{e.name}</span>
                                  <span className="text-[9px] text-[var(--text-secondary)] flex-shrink-0">{humanBytes(e.bytes)}</span>
                                </button>
                              ))}
                            </div>
                          );
                        });
                      })()}
                      {cacheInfo && cacheInfo.entries.length > 0 && (
                        <button
                          disabled={cacheBusy}
                          onClick={() => clearCache()}
                          className="w-full text-left px-2 py-1 mt-1 text-[10px] text-red-400 hover:bg-[var(--bg-tertiary)] disabled:opacity-50 border-t border-[var(--border)]"
                        >
                          Clear All ({humanBytes(cacheInfo.total_bytes)})
                        </button>
                      )}
                    </div>
                  )}
                  <div className="border-t border-[var(--border)] my-1" />
                  <button
                    onClick={() => { setShowHelp(v => !v); setShowUserMenu(false); }}
                    className="w-full text-left text-[11px] px-3 py-1.5 text-[var(--text-secondary)] hover:text-[var(--text-primary)] hover:bg-[var(--bg-tertiary)] flex items-center gap-2"
                  >
                    <span className="w-3 text-center">?</span><span>Help</span>
                  </button>
                  <button
                    onClick={() => signOut({ callbackUrl: '/login' })}
                    className="w-full text-left text-[11px] px-3 py-1.5 text-[var(--text-secondary)] hover:text-[var(--red)] hover:bg-[var(--bg-tertiary)] flex items-center gap-2"
                  >
                    <span className="w-3 text-center">⏻</span><span>Logout</span>
                  </button>
                </div>
              </>
            )}
          </div>
        </div>
      </header>

      {/* Automation secondary toolbar — sub-tabs + context actions.
          Lives below the main header so it never squishes the top nav. */}
      {['tasks', 'pipelines', 'schedules'].includes(viewMode) && (
        <div className="h-8 border-b border-[var(--border)] flex items-center justify-between px-4 shrink-0 bg-[var(--bg-tertiary)]/40">
          <div className="flex items-center gap-1">
            {(['schedules', 'pipelines', 'tasks'] as const).map((m) => (
              <button
                key={m}
                onClick={() => setViewMode(m)}
                className={`text-[12px] px-2.5 py-0.5 rounded transition-colors ${
                  viewMode === m
                    ? 'bg-[var(--accent)]/15 text-[var(--accent)] shadow-sm'
                    : 'text-[var(--text-secondary)] hover:text-[var(--text-primary)]'
                }`}
              >
                {{ tasks: 'Tasks', pipelines: 'Pipelines', schedules: 'Schedules' }[m]}
              </button>
            ))}
            {viewMode === 'tasks' && (
              <span className="ml-3 text-[10px] text-[var(--text-secondary)]">
                {running.length} running · {queued.length} queued · {tasks.filter(t => t.status === 'done').length} done
              </span>
            )}
          </div>
          <div className="flex items-center gap-2">
            {viewMode === 'tasks' && (
              <button
                onClick={() => setShowNewTask(true)}
                className="text-[10px] px-2.5 py-1 bg-[var(--accent)] text-white rounded hover:opacity-90"
              >
                + New Task
              </button>
            )}
          </div>
        </div>
      )}

      {/* Main content */}
      <div className="flex-1 flex min-h-0">
        {viewMode === 'tasks' ? (
          <>
            {/* Left — Task list */}
            <aside className="w-72 border-r border-[var(--border)] flex flex-col shrink-0">
              <Suspense fallback={null}><TaskBoard tasks={tasks} activeId={activeTaskId} onSelect={setActiveTaskId} onRefresh={fetchData} /></Suspense>
            </aside>

            {/* Center — Task detail / empty state */}
            <main className="flex-1 flex flex-col min-w-0">
              {activeTask ? (
                <Suspense fallback={null}><TaskDetail
                  task={activeTask}
                  onRefresh={fetchData}
                  onFollowUp={async (data) => {
                    const res = await fetch('/api/tasks', {
                      method: 'POST',
                      headers: { 'Content-Type': 'application/json' },
                      body: JSON.stringify(data),
                    });
                    const newTask = await res.json();
                    setActiveTaskId(newTask.id);
                    fetchData();
                  }}
                /></Suspense>
              ) : (
                <div className="flex-1 flex items-center justify-center text-[var(--text-secondary)]">
                  <div className="text-center space-y-2">
                    <p className="text-lg">Select a task or create a new one</p>
                    <p className="text-xs">Submit tasks for Claude Code to work on autonomously</p>
                  </div>
                </div>
              )}
            </main>

            {/* Right — Status panel */}
            <aside className="w-56 border-l border-[var(--border)] flex flex-col shrink-0 p-3 space-y-4">
              {/* Providers */}
              <div>
                <h3 className="text-[10px] font-semibold text-[var(--text-secondary)] uppercase mb-2">Providers</h3>
                <div className="space-y-1">
                  {providers.map(p => (
                    <div key={p.name} className="flex items-center justify-between text-xs">
                      <span className={p.hasKey && p.enabled ? 'text-[var(--text-primary)]' : 'text-[var(--text-secondary)]'}>
                        {p.displayName}
                      </span>
                      <span className={`text-[10px] ${p.hasKey && p.enabled ? 'text-[var(--green)]' : 'text-[var(--red)]'}`}>
                        {p.hasKey && p.enabled ? '● active' : '○ off'}
                      </span>
                    </div>
                  ))}
                </div>
              </div>

              {/* Usage */}
              {usage.length > 0 && (
                <div>
                  <h3 className="text-[10px] font-semibold text-[var(--text-secondary)] uppercase mb-2">Usage (30d)</h3>
                  <div className="space-y-1">
                    {usage.map((u, i) => (
                      <div key={i} className="text-xs">
                        <div className="flex justify-between">
                          <span className="text-[var(--text-secondary)]">{u.provider}</span>
                          <span className="text-[var(--text-primary)]">{((u.totalInput + u.totalOutput) / 1000).toFixed(0)}k tokens</span>
                        </div>
                      </div>
                    ))}
                  </div>
                </div>
              )}

              {/* Running tasks */}
              {running.length > 0 && (
                <div>
                  <h3 className="text-[10px] font-semibold text-[var(--text-secondary)] uppercase mb-2">Running</h3>
                  <div className="space-y-1">
                    {running.map(t => (
                      <button
                        key={t.id}
                        onClick={() => { setViewMode('tasks'); setActiveTaskId(t.id); }}
                        className="w-full text-left px-2 py-1 rounded text-xs hover:bg-[var(--bg-tertiary)]"
                      >
                        <span className="text-[var(--green)] text-[10px]">● </span>
                        <span className="truncate">{t.projectName}</span>
                      </button>
                    ))}
                  </div>
                </div>
              )}
            </aside>
          </>
        ) : null}

        {/* Projects — keep alive to preserve state across tab switches */}
        <div className={`flex-1 flex flex-col min-h-0 ${viewMode !== 'projects' ? 'hidden' : ''}`}>
          <Suspense fallback={<div className="flex-1 flex items-center justify-center text-[var(--text-secondary)]">Loading...</div>}>
            <ProjectManager />
          </Suspense>
        </div>

        {/* Pipelines */}
        {viewMode === 'pipelines' && (
          <Suspense fallback={<div className="flex-1 flex items-center justify-center text-[var(--text-secondary)]">Loading...</div>}>
            <PipelineView
              onViewTask={(taskId) => { setViewMode('tasks'); setActiveTaskId(taskId); }}
              focusPipelineId={pendingPipelineId}
              onFocusHandled={() => setPendingPipelineId(null)}
            />
          </Suspense>
        )}

        {/* Jobs — scheduled connector polls (Forge web read-mostly; create via extension) */}
        {viewMode === 'jobs' && (
          <Suspense fallback={<div className="flex-1 flex items-center justify-center text-[var(--text-secondary)]">Loading...</div>}>
            <JobsView
              onViewPipeline={(pipelineId) => {
                setPendingPipelineId(pipelineId);
                setViewMode('pipelines');
              }}
            />
          </Suspense>
        )}

        {/* Schedules — pipeline triggers (new path, complements Jobs) */}
        {viewMode === 'schedules' && (
          <Suspense fallback={<div className="flex-1 flex items-center justify-center text-[var(--text-secondary)]">Loading...</div>}>
            <SchedulesView
              onViewPipeline={(pipelineId) => {
                setPendingPipelineId(pipelineId);
                setViewMode('pipelines');
              }}
            />
          </Suspense>
        )}

        {/* Skills */}
        {viewMode === 'skills' && (
          <Suspense fallback={<div className="flex-1 flex items-center justify-center text-[var(--text-secondary)]">Loading...</div>}>
            <SkillsPanel />
          </Suspense>
        )}


        {/* Usage */}
        {viewMode === 'usage' && (
          <Suspense fallback={<div className="flex-1 flex items-center justify-center text-[var(--text-secondary)]">Loading...</div>}>
            <UsagePanel />
          </Suspense>
        )}

        {/* Logs */}
        {viewMode === 'logs' && (
          <Suspense fallback={<div className="flex-1 flex items-center justify-center text-[var(--text-secondary)]">Loading...</div>}>
            <LogViewer initialSearch={pendingLogSearch} key={pendingLogSearch || 'all'} />
          </Suspense>
        )}

        {/* Docs — always mounted to keep terminal session alive */}
        <div className={viewMode === 'docs' ? 'flex-1 min-h-0 flex' : 'hidden'}>
          <Suspense fallback={<div className="flex-1 flex items-center justify-center text-[var(--text-secondary)]">Loading...</div>}>
            <DocsViewer />
          </Suspense>
        </div>

        {/* Code — terminal + file browser, always mounted to keep terminal sessions alive */}
        <div className={viewMode === 'terminal' ? 'flex-1 min-h-0 flex' : 'hidden'}>
          <Suspense fallback={<div className="flex-1 flex items-center justify-center text-[var(--text-secondary)]">Loading...</div>}>
            <CodeViewer terminalRef={terminalRef} />
          </Suspense>
        </div>
      </div>
      </div>{/* close Forge main area */}

      {/* Browser — right side */}
      {browserMode === 'right' && (
        <>
        <div
          onMouseDown={(e) => {
            e.preventDefault();
            browserDragRef.current = { startX: e.clientX, startW: browserWidth };
            setBrowserDragging(true);
            const onMove = (ev: MouseEvent) => {
              if (!browserDragRef.current) return;
              setBrowserWidth(Math.max(320, Math.min(1200, browserDragRef.current.startW - (ev.clientX - browserDragRef.current.startX))));
            };
            const onUp = () => { browserDragRef.current = null; setBrowserDragging(false); window.removeEventListener('mousemove', onMove); window.removeEventListener('mouseup', onUp); };
            window.addEventListener('mousemove', onMove);
            window.addEventListener('mouseup', onUp);
          }}
          className="w-1 bg-[var(--border)] cursor-col-resize shrink-0 hover:bg-[var(--accent)]/50"
        />
        <div style={{ width: browserWidth }} className="shrink-0 flex flex-col relative">
          <Suspense fallback={null}><BrowserPanel onClose={() => setBrowserMode('none')} /></Suspense>
          {browserDragging && <div className="absolute inset-0 z-10" />}
        </div>
        </>
      )}

      {/* Browser — floating window */}
      {browserMode === 'float' && (
        <Suspense fallback={null}>
          <FloatingBrowser onClose={() => setBrowserMode('none')} />
        </Suspense>
      )}

      {showNewTask && (
        <Suspense fallback={null}>
          <NewTaskModal
            onClose={() => setShowNewTask(false)}
            onCreate={async (data) => {
              await fetch('/api/tasks', {
                method: 'POST',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify(data),
              });
              setShowNewTask(false);
              fetchData();
            }}
          />
        </Suspense>
      )}

      </div>{/* /flex-1 inner container */}
      {showMonitor && <Suspense fallback={null}><MonitorPanel onClose={() => setShowMonitor(false)} /></Suspense>}
      {showLoginStatus && <Suspense fallback={null}><LoginStatusPanel onClose={() => { setShowLoginStatus(false); /* refresh badge after panel actions */ fetch('/api/login-status').then(r => r.json()).then(j => { const rows = j.rows || []; const broken = rows.filter((r: any) => r.result && !r.result.ok).length; setLoginBadge({ broken, total: rows.length }); }).catch(() => {}); }} /></Suspense>}

      {showSettings && (
        <Suspense fallback={null}>
          <SettingsModal onClose={() => { setShowSettings(false); fetchData(); refreshDisplayName(); }} />
        </Suspense>
      )}
      {showHelp && (
        <Suspense fallback={null}>
          <HelpDialog onClose={() => setShowHelp(false)} />
        </Suspense>
      )}
    </div>
  );
}
