/* global React, ReactDOM, Icon, I, Chip, ACCENT, CopyButton, Variant, SkeletonRow,
   ConsensioMark, ConsensioWordmark, CONSENSIO_DATA,
   useTweaks, TweaksPanel, TweakSection, TweakRadio, TweakToggle, TweakColor */

const { useState, useEffect, useMemo, useRef } = React;
const D = window.CONSENSIO_DATA;

// ── Top-level route shape ────────────────────────────────────────────────────
// route: { zone, screen, projectId?, caseId?, variantId? }

const ROUTES = {
  dashboard: { crumbs: [{ label: 'Dashboard' }] },
  projects:  { crumbs: [{ label: 'Projects' }] },
  cases:     { crumbs: [{ label: 'Projects', to: 'projects' }, { label: 'RD Program — Cohort 7', to: 'cases' }] },
  family:    { crumbs: [{ label: 'Projects', to: 'projects' }, { label: 'RD Program — Cohort 7', to: 'cases' }, { label: 'Rowe family' }] },
  variants:  { crumbs: [{ label: 'Projects', to: 'projects' }, { label: 'RD Program — Cohort 7', to: 'cases' }, { label: 'Rowe family', to: 'family' }, { label: 'Variants' }] },
  updates:   { crumbs: [{ label: 'Reanalysis feed' }] },
  admin:     { crumbs: [{ label: 'Admin' }] },
  genomics:  { crumbs: [{ label: 'Genomics Lab' }] },
};

const FLAG_TO_TONE = {
  paper: 'paper', request: 'request', warning: 'warning', solved: 'success',
};

const STATUS_LABEL = {
  in_review: { tone: 'info', label: 'In review' },
  pending:   { tone: 'warning', label: 'Pending triage' },
  solved:    { tone: 'success', label: 'Solved' },
};
const PRIORITY = {
  urgent: { tone: 'danger',  label: 'Urgent' },
  high:   { tone: 'warning', label: 'High' },
  normal: { tone: 'neutral', label: 'Normal' },
};

// ── Sidebar ──────────────────────────────────────────────────────────────────
// Role → which sidebar keys are visible
const ROLE_NAV = {
  org_admin:      ['dashboard','projects','genomics','updates','admin'],
  doctor:         ['dashboard','projects','genomics','updates'],
  lab_analyst:    ['dashboard','projects','genomics','updates'],
  variant_analyst:['dashboard','projects','genomics'],
  viewer:         ['dashboard'],
};

function Sidebar({ screen, navigate, collapsed, setCollapsed, role }) {
  const ALL_ITEMS = [
    { key: 'dashboard', label: 'Dashboard',    icon: I.home   },
    { key: 'projects',  label: 'Projects',     icon: I.folder },
    { key: 'genomics',  label: 'Genomics Lab', icon: I.flask  },
    { key: 'updates',   label: 'Reanalysis',   icon: I.bolt   },
    { key: 'admin',     label: 'Admin',        icon: I.cog    },
  ];
  const allowed = role ? (ROLE_NAV[role] || ['dashboard']) : ALL_ITEMS.map(i => i.key);
  const items = ALL_ITEMS.filter(it => allowed.includes(it.key));
  const W = collapsed ? 64 : 220;
  return (
    <aside data-theme="dark" style={{
      width: W, flexShrink: 0, height: '100%',
      background: '#111827',
      borderRight: '1px solid rgba(255,255,255,0.08)',
      display: 'flex', flexDirection: 'column',
      transition: 'width 180ms ease',
      overflow: 'hidden',
    }}>
      <div style={{ height: 48, padding: collapsed ? '0 8px' : '0 10px', display: 'flex', alignItems: 'center', justifyContent: collapsed ? 'center' : 'flex-start', borderBottom: '1px solid rgba(255,255,255,0.08)', flexShrink: 0 }}>
        {collapsed ? (
          <img src="assets/logos/logo.png" alt="Consensio"
            style={{ width: 40, height: 40, objectFit: 'cover', objectPosition: 'left center', borderRadius: 4 }} />
        ) : (
          <img src="assets/logos/logo.png" alt="Consensio"
            style={{ height: 34, width: 'auto', objectFit: 'contain', maxWidth: '100%' }} />
        )}
      </div>

      <nav style={{ padding: 12, display: 'flex', flexDirection: 'column', gap: 4 }}>
        {items.map(it => {
          const active = (it.key === 'dashboard' && screen === 'dashboard')
            || (it.key === 'projects' && ['projects', 'cases', 'family', 'variants'].includes(screen))
            || (it.key === 'updates'   && screen === 'updates')
            || (it.key === 'admin'    && screen === 'admin')
            || (it.key === 'genomics' && screen === 'genomics');
          return (
            <div key={it.key}
                 className={`sb-item${active ? ' active' : ''}`}
                 onClick={() => navigate(it.key)}
                 title={collapsed ? it.label : ''}>
              <span className="sb-ico"><Icon d={it.icon} /></span>
              {!collapsed && <span>{it.label}</span>}
            </div>
          );
        })}
      </nav>

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

      {/* Project switcher */}
      {!collapsed && (
        <div style={{ padding: '0 12px 12px' }}>
          <div className="t-label" style={{ marginBottom: 8 }}>Current project</div>
          <div className="raised" style={{ padding: 8, display: 'flex', alignItems: 'center', gap: 8 }}>
            <div style={{ width: 6, height: 6, borderRadius: 3, background: 'var(--accent-info)' }} />
            <div style={{ minWidth: 0, flex: 1 }}>
              <div style={{ fontSize: 12, fontWeight: 500, color: 'var(--text-primary)', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
                RD Program
              </div>
              <div className="t-meta">86 cases · 31 solved</div>
            </div>
            <Icon d={I.chevronD} size={14} />
          </div>
        </div>
      )}

      <div style={{ padding: 12, borderTop: '1px solid var(--border)' }}>
        <div
          className="sb-item"
          onClick={() => setCollapsed(!collapsed)}
          title={collapsed ? 'Expand' : 'Collapse'}>
          <span className="sb-ico" style={{ transform: collapsed ? 'rotate(180deg)' : 'none' }}>
            <Icon d={I.chevronL} />
          </span>
          {!collapsed && <span>Collapse</span>}
        </div>
      </div>
    </aside>
  );
}

// ── Top bar with breadcrumb, search, bell, avatar ────────────────────────────
function TopBar({ screen, navigate, phiMask, setPhiMask, openHelp, openPalette, user, onSignOut, theme, onThemeChange }) {
  const route = ROUTES[screen] || ROUTES.dashboard;
  const crumbs = route.crumbs.map(c => ({
    ...c,
    label: phiMask && c.label === 'Rowe family' ? 'FAM-XXXX family' : c.label,
  }));
  return (
    <header style={{
      height: 48, flexShrink: 0,
      background: '#111827',
      borderBottom: '1px solid rgba(255,255,255,0.08)',
      padding: '0 16px',
      display: 'flex', alignItems: 'center', gap: 12,
    }}>
      <nav style={{ display: 'flex', alignItems: 'center', gap: 6, minWidth: 0, flex: 1 }}>
        {crumbs.map((c, i) => (
          <React.Fragment key={i}>
            {i > 0 && (
              <span style={{ color: '#64748B' }}><Icon d={I.chevronR} size={12} /></span>
            )}
            {c.to ? (
              <button className="btn-ghost"
                onClick={() => navigate(c.to)}
                style={{
                  background: 'transparent', border: 0, padding: '4px 6px', borderRadius: 4,
                  fontSize: 13, color: '#94A3B8', cursor: 'pointer',
                }}>
                {c.label}
              </button>
            ) : (
              <span style={{ fontSize: 13, fontWeight: 500, color: '#E2E8F0', padding: '4px 6px' }}>
                {c.label}
              </span>
            )}
          </React.Fragment>
        ))}
      </nav>

      <button className="btn"
              onClick={openPalette}
              style={{ minWidth: 240, justifyContent: 'flex-start', color: '#64748B', height: 30,
                       background: 'rgba(255,255,255,0.06)', border: '1px solid rgba(255,255,255,0.10)' }}>
        <Icon d={I.search} size={14} />
        <span style={{ flex: 1, textAlign: 'left' }}>Search cases, genes, variants…</span>
        <span className="kbd">⌘K</span>
      </button>

      {/* PHI mask toggle */}
      <button
        className={`btn btn-ghost`}
        onClick={() => setPhiMask(!phiMask)}
        title={phiMask ? 'PHI hidden — click to reveal' : 'Hide patient identifiable info'}
        style={{
          height: 30, padding: '0 8px',
          color: phiMask ? '#F59E0B' : '#94A3B8',
          background: phiMask ? 'rgba(245,158,11,0.14)' : 'transparent',
          border: '1px solid ' + (phiMask ? 'rgba(245,158,11,0.32)' : 'transparent'),
        }}>
        <svg width="14" height="14" viewBox="0 0 16 16" fill="none" stroke="currentColor"
             strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
          {phiMask ? (
            <>
              <path d="M2 2l12 12" />
              <path d="M5.5 5.5C3.5 6.7 2 8 2 8s2 4 6 4c1.1 0 2-.3 2.8-.7" />
              <path d="M9.5 9.5a2 2 0 01-3-3" />
              <path d="M8.5 4.05A6 6 0 0114 8c-.3.6-.8 1.3-1.4 1.9" />
            </>
          ) : (
            <>
              <path d="M2 8s2-4 6-4 6 4 6 4-2 4-6 4-6-4-6-4z" />
              <circle cx="8" cy="8" r="2" />
            </>
          )}
        </svg>
        <span style={{ fontSize: 11, fontWeight: 500 }}>{phiMask ? 'PHI off' : 'PHI on'}</span>
      </button>

      {/* Keyboard help */}
      <button className="btn btn-ghost"
        onClick={openHelp}
        title="Keyboard shortcuts (?)"
        style={{ height: 30, width: 30, padding: 0, justifyContent: 'center' }}>
        <span className="kbd" style={{ fontSize: 12 }}>?</span>
      </button>

      <button className="btn btn-ghost" style={{ position: 'relative', height: 30, width: 30, padding: 0, justifyContent: 'center' }}>
        <Icon d={I.bell} size={15} />
        <span style={{
          position: 'absolute', top: 4, right: 4,
          minWidth: 14, height: 14, padding: '0 3px',
          background: 'var(--accent-danger)', color: '#fff',
          borderRadius: 7, fontSize: 9, fontWeight: 600,
          display: 'flex', alignItems: 'center', justifyContent: 'center',
        }}>3</span>
      </button>

      <AccountMenu user={user} onSignOut={onSignOut} theme={theme} onThemeChange={onThemeChange} />
    </header>
  );
}

function AccountMenu({ user, onSignOut, theme, onThemeChange }) {
  const [open, setOpen]           = useState(false);
  const [settingsOpen, setSettings] = useState(false);
  const ref = useRef(null);
  const initials = user
    ? user.displayName.split(' ').map(w => w[0]).join('').toUpperCase().slice(0, 2)
    : D.user.avatar;

  useEffect(() => {
    if (!open) return;
    const handler = e => { if (ref.current && !ref.current.contains(e.target)) setOpen(false); };
    document.addEventListener('mousedown', handler);
    return () => document.removeEventListener('mousedown', handler);
  }, [open]);

  const menuItemStyle = {
    width: '100%', padding: '9px 16px',
    background: 'transparent', border: 'none',
    display: 'flex', alignItems: 'center', gap: 9,
    color: 'var(--text-primary)', fontSize: 13, fontWeight: 500,
    cursor: 'pointer', textAlign: 'left',
  };

  return (
    <div ref={ref} style={{ position: 'relative' }}>
      <button
        onClick={() => setOpen(o => !o)}
        style={{
          width: 30, height: 30, borderRadius: 15,
          background: 'linear-gradient(135deg, var(--brand-2), var(--brand-3))',
          color: '#fff', display: 'flex', alignItems: 'center', justifyContent: 'center',
          fontSize: 11, fontWeight: 600, letterSpacing: 0.02,
          border: open ? '2px solid var(--accent-info)' : '1px solid var(--border)',
          cursor: 'pointer', padding: 0, flexShrink: 0, outline: 'none',
        }}
        title={user ? user.displayName : D.user.name}
      >{initials}</button>

      {open && (
        <div style={{
          position: 'absolute', top: 38, right: 0,
          background: 'var(--bg-raised)',
          border: '1px solid var(--border-hover)',
          borderRadius: 10,
          boxShadow: '0 8px 32px rgba(0,0,0,0.4)',
          minWidth: 210, zIndex: 300, overflow: 'hidden',
        }}>
          {/* User info */}
          <div style={{ padding: '14px 16px 12px', borderBottom: '1px solid var(--border)' }}>
            <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
              <div style={{
                width: 36, height: 36, borderRadius: 18, flexShrink: 0,
                background: 'linear-gradient(135deg, var(--brand-2), var(--brand-3))',
                color: '#fff', display: 'flex', alignItems: 'center', justifyContent: 'center',
                fontSize: 13, fontWeight: 700,
              }}>{initials}</div>
              <div>
                <div style={{ fontSize: 13, fontWeight: 600, color: 'var(--text-primary)' }}>
                  {user ? user.displayName : D.user.name}
                </div>
                <div style={{ fontSize: 11, color: 'var(--text-tertiary)', marginTop: 1 }}>
                  {user ? `@${user.username}` : D.user.org}
                </div>
              </div>
            </div>
          </div>

          {/* Menu items */}
          <div style={{ padding: '6px 0', borderBottom: '1px solid var(--border)' }}>
            <button
              style={menuItemStyle}
              onClick={() => { setOpen(false); setSettings(true); }}
              onMouseEnter={e => e.currentTarget.style.background = 'var(--bg-overlay)'}
              onMouseLeave={e => e.currentTarget.style.background = 'transparent'}
            >
              <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
                <circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83-2.83l.06-.06A1.65 1.65 0 0 0 4.68 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 2.83-2.83l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z"/>
              </svg>
              Settings
            </button>
          </div>

          {/* Sign out */}
          <div style={{ padding: '6px 0' }}>
            <button
              onClick={() => { setOpen(false); onSignOut && onSignOut(); }}
              style={{ ...menuItemStyle, color: 'var(--accent-danger)' }}
              onMouseEnter={e => e.currentTarget.style.background = 'color-mix(in oklab, var(--accent-danger) 10%, transparent)'}
              onMouseLeave={e => e.currentTarget.style.background = 'transparent'}
            >
              <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
                <path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"/>
                <polyline points="16 17 21 12 16 7"/>
                <line x1="21" y1="12" x2="9" y2="12"/>
              </svg>
              Sign out
            </button>
          </div>
        </div>
      )}

      {settingsOpen && (
        <SettingsModal
          user={user}
          theme={theme}
          onThemeChange={onThemeChange}
          onClose={() => setSettings(false)}
        />
      )}
      {/* TwoFactorSetupModal is rendered by SettingsModal itself via portal-like pattern */}
    </div>
  );
}

function SettingsModal({ user, theme, onThemeChange, onClose }) {
  const [tab, setTab]           = useState('appearance');
  const [currentPw, setCurrentPw] = useState('');
  const [newPw, setNewPw]         = useState('');
  const [confirmPw, setConfirmPw] = useState('');
  const [pwMsg, setPwMsg]         = useState(null); // { type: 'success'|'error', text }
  const [pwLoading, setPwLoading] = useState(false);
  const [twoFactorOpen, setTwoFactorOpen] = useState(false);

  useEffect(() => {
    const onKey = e => { if (e.key === 'Escape') onClose(); };
    document.addEventListener('keydown', onKey);
    return () => document.removeEventListener('keydown', onKey);
  }, []);

  const handleChangePw = async e => {
    e.preventDefault();
    setPwMsg(null);
    if (newPw.length < 8) { setPwMsg({ type: 'error', text: 'New password must be at least 8 characters.' }); return; }
    if (newPw !== confirmPw) { setPwMsg({ type: 'error', text: 'New passwords do not match.' }); return; }
    setPwLoading(true);

    // ── Supabase path ──────────────────────────────────────────────────────
    if (window.__sb && window.__sbUser) {
      const { error: sbErr } = await window.__sb.auth.updateUser({ password: newPw });
      if (sbErr) {
        setPwMsg({ type: 'error', text: sbErr.message });
      } else {
        setCurrentPw(''); setNewPw(''); setConfirmPw('');
        setPwMsg({ type: 'success', text: 'Password updated successfully.' });
      }
      setPwLoading(false);
      return;
    }

    // ── SHA-256 fallback ───────────────────────────────────────────────────
    const auth = window.__consensioAuth;
    if (!currentPw) { setPwMsg({ type: 'error', text: 'Enter your current password.' }); setPwLoading(false); return; }
    const currentHash = await auth.sha256hex(currentPw);
    if (!auth.ACCOUNTS[user.username] || currentHash !== auth.ACCOUNTS[user.username].hash) {
      setPwMsg({ type: 'error', text: 'Current password is incorrect.' });
      setPwLoading(false);
      return;
    }
    const newHash = await auth.sha256hex(newPw);
    const overrides = JSON.parse(localStorage.getItem('consensio_pw_overrides') || '{}');
    overrides[user.username] = newHash;
    localStorage.setItem('consensio_pw_overrides', JSON.stringify(overrides));
    auth.ACCOUNTS[user.username].hash = newHash;
    setCurrentPw(''); setNewPw(''); setConfirmPw('');
    setPwMsg({ type: 'success', text: 'Password updated successfully.' });
    setPwLoading(false);
  };

  const inputStyle = {
    width: '100%', padding: '9px 12px',
    background: 'var(--bg-overlay)',
    border: '1px solid var(--border)',
    borderRadius: 7, color: 'var(--text-primary)',
    fontSize: 13, fontFamily: 'var(--font-ui)', outline: 'none',
    boxSizing: 'border-box',
  };

  const tabStyle = active => ({
    padding: '7px 16px', fontSize: 13, fontWeight: 500,
    background: active ? 'var(--bg-overlay)' : 'transparent',
    border: 'none', borderRadius: 7,
    color: active ? 'var(--text-primary)' : 'var(--text-secondary)',
    cursor: 'pointer',
  });

  return (
    <React.Fragment>
      {/* Backdrop */}
      <div onClick={onClose} style={{
        position: 'fixed', inset: 0, background: 'rgba(0,0,0,0.5)',
        zIndex: 400, backdropFilter: 'blur(2px)',
      }} />
      {/* Modal */}
      <div style={{
        position: 'fixed', top: '50%', left: '50%',
        transform: 'translate(-50%, -50%)',
        width: 460, background: 'var(--bg-surface)',
        border: '1px solid var(--border-hover)',
        borderRadius: 14,
        boxShadow: '0 24px 80px rgba(0,0,0,0.5)',
        zIndex: 401, overflow: 'hidden',
      }}>
        {/* Header */}
        <div style={{
          display: 'flex', alignItems: 'center', justifyContent: 'space-between',
          padding: '18px 20px 14px',
          borderBottom: '1px solid var(--border)',
        }}>
          <span style={{ fontSize: 15, fontWeight: 600, color: 'var(--text-primary)' }}>Settings</span>
          <button onClick={onClose} style={{
            background: 'none', border: 'none', cursor: 'pointer',
            color: 'var(--text-tertiary)', padding: 4, borderRadius: 6,
            display: 'flex', alignItems: 'center',
          }}>
            <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
              <line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/>
            </svg>
          </button>
        </div>

        {/* Tabs */}
        <div style={{ display: 'flex', gap: 4, padding: '10px 16px 0', borderBottom: '1px solid var(--border)' }}>
          {[['appearance', 'Appearance'], ['security', 'Security']].map(([key, label]) => (
            <button key={key} style={tabStyle(tab === key)} onClick={() => { setTab(key); setPwMsg(null); }}>
              {label}
            </button>
          ))}
        </div>

        {/* Body */}
        <div style={{ padding: '24px 24px 28px' }}>

          {tab === 'appearance' && (
            <div>
              <div style={{ fontSize: 11, fontWeight: 600, letterSpacing: '0.06em', textTransform: 'uppercase', color: 'var(--text-tertiary)', marginBottom: 14 }}>
                Theme
              </div>
              <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 10 }}>
                {[
                  { value: 'dark',  label: 'Dark',  bg: '#0A0E14', text: '#E2E8F0', border: '#243044' },
                  { value: 'light', label: 'Light', bg: '#F8FAFC', text: '#0F172A', border: '#E2E8F0' },
                ].map(opt => {
                  const active = theme === opt.value;
                  return (
                    <button
                      key={opt.value}
                      onClick={() => onThemeChange(opt.value)}
                      style={{
                        background: opt.bg, border: active ? '2px solid var(--accent-info)' : `1px solid ${opt.border}`,
                        borderRadius: 10, padding: '16px 14px', cursor: 'pointer',
                        display: 'flex', flexDirection: 'column', gap: 8, textAlign: 'left',
                        boxShadow: active ? '0 0 0 3px color-mix(in oklab, var(--accent-info) 20%, transparent)' : 'none',
                        transition: 'border 0.15s, box-shadow 0.15s',
                      }}
                    >
                      {/* Mini preview bars */}
                      <div style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>
                        <div style={{ height: 5, width: '60%', background: opt.border, borderRadius: 3 }} />
                        <div style={{ height: 5, width: '80%', background: opt.border, borderRadius: 3 }} />
                        <div style={{ height: 5, width: '45%', background: opt.border, borderRadius: 3 }} />
                      </div>
                      <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
                        <span style={{ fontSize: 12, fontWeight: 600, color: opt.text }}>{opt.label}</span>
                        {active && (
                          <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#3B82F6" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round">
                            <polyline points="20 6 9 17 4 12"/>
                          </svg>
                        )}
                      </div>
                    </button>
                  );
                })}
              </div>
            </div>
          )}

          {tab === 'security' && (
            <div style={{ display: 'flex', flexDirection: 'column', gap: 24 }}>
              {/* ── Change password ──────────────────────────────────────── */}
              <form onSubmit={handleChangePw}>
                <div style={{ fontSize: 11, fontWeight: 600, letterSpacing: '0.06em', textTransform: 'uppercase', color: 'var(--text-tertiary)', marginBottom: 14 }}>
                  Change password
                </div>
                <div style={{ display: 'flex', flexDirection: 'column', gap: 14 }}>
                  {/* Current password only needed for SHA-256 fallback */}
                  {!(window.__sb && window.__sbUser) && (
                    <div>
                      <label style={{ display: 'block', fontSize: 11, fontWeight: 600, color: 'var(--text-secondary)', marginBottom: 6 }}>
                        Current password
                      </label>
                      <input type="password" value={currentPw} onChange={e => { setCurrentPw(e.target.value); setPwMsg(null); }}
                        autoComplete="current-password" required={!(window.__sb && window.__sbUser)} style={inputStyle} />
                    </div>
                  )}
                  {[
                    { label: 'New password',         value: newPw,     set: setNewPw },
                    { label: 'Confirm new password', value: confirmPw, set: setConfirmPw },
                  ].map(({ label, value, set }) => (
                    <div key={label}>
                      <label style={{ display: 'block', fontSize: 11, fontWeight: 600, color: 'var(--text-secondary)', marginBottom: 6 }}>
                        {label}
                      </label>
                      <input type="password" value={value} onChange={e => { set(e.target.value); setPwMsg(null); }}
                        autoComplete="new-password" required style={inputStyle} />
                    </div>
                  ))}
                </div>
                {pwMsg && (
                  <div style={{
                    marginTop: 14, padding: '9px 12px', borderRadius: 7, fontSize: 12,
                    background: pwMsg.type === 'success'
                      ? 'color-mix(in oklab, var(--accent-success) 14%, transparent)'
                      : 'color-mix(in oklab, var(--accent-danger) 14%, transparent)',
                    color: pwMsg.type === 'success' ? 'var(--accent-success)' : 'var(--accent-danger)',
                    border: `1px solid ${pwMsg.type === 'success' ? 'color-mix(in oklab, var(--accent-success) 30%, transparent)' : 'color-mix(in oklab, var(--accent-danger) 30%, transparent)'}`,
                  }}>
                    {pwMsg.text}
                  </div>
                )}
                <button type="submit" disabled={pwLoading} style={{
                  marginTop: 16, padding: '10px 20px',
                  background: 'var(--accent-info)', color: '#fff',
                  border: 'none', borderRadius: 8, fontSize: 13, fontWeight: 600,
                  cursor: pwLoading ? 'not-allowed' : 'pointer', opacity: pwLoading ? 0.7 : 1,
                  fontFamily: 'var(--font-ui)',
                }}>
                  {pwLoading ? 'Updating…' : 'Update password'}
                </button>
              </form>

              {/* ── Two-factor authentication ─────────────────────────── */}
              <div style={{ borderTop: '1px solid var(--border)', paddingTop: 20 }}>
                <div style={{ fontSize: 11, fontWeight: 600, letterSpacing: '0.06em', textTransform: 'uppercase', color: 'var(--text-tertiary)', marginBottom: 12 }}>
                  Two-factor authentication (2FA)
                </div>
                {window.__sb ? (
                  <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
                    <div>
                      <div style={{ fontSize: 13, fontWeight: 500, color: 'var(--text-primary)' }}>
                        Authenticator app (TOTP)
                      </div>
                      <div style={{ fontSize: 12, color: 'var(--text-secondary)', marginTop: 3, lineHeight: 1.55 }}>
                        Use Google Authenticator, Authy, or any TOTP app for a second factor on sign-in.
                      </div>
                    </div>
                    <button className="btn btn-primary" style={{ flexShrink: 0, marginLeft: 16 }}
                      onClick={() => setTwoFactorOpen(true)}>
                      Manage 2FA
                    </button>
                  </div>
                ) : (
                  <div style={{ fontSize: 12, color: 'var(--text-tertiary)', lineHeight: 1.6 }}>
                    2FA requires Supabase. Configure <span className="mono" style={{ fontSize: 11 }}>supabase-config.js</span> to enable it.
                  </div>
                )}
              </div>
            </div>
          )}
        </div>
      </div>
      {twoFactorOpen && (
        <TwoFactorSetupModal
          onClose={() => setTwoFactorOpen(false)}
          onEnrolled={() => {}}
        />
      )}
    </React.Fragment>
  );
}

// ── Screens ──────────────────────────────────────────────────────────────────

function useDashboardStats() {
  const [stats, setStats] = useState(null);
  useEffect(() => {
    fetch('patient_data/json/dashboard_stats.json')
      .then(r => r.ok ? r.json() : null)
      .then(d => { if (d) setStats(d); })
      .catch(() => {});
  }, []);
  return stats;
}

function DashboardScreen({ navigate, user }) {
  const live    = useDashboardStats();
  const firstName = user?.firstName || 'Kenan';
  const org       = user?.org       || 'Genomix Lab';
  const today     = new Date().toLocaleDateString('en-US', { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' });
  const passRate  = live ? `${Math.round((live.pass_count / live.total_samples) * 100)}%` : '—';
  const tiles = live ? [
    { label: 'Total samples',        value: live.total_samples,              tone: 'info'    },
    { label: 'Total variants',       value: live.total_variants.toLocaleString(), tone: 'neutral' },
    { label: 'HIGH impact',          value: live.total_high.toLocaleString(),    tone: 'warning' },
    { label: 'Sequencing runs',      value: live.total_runs,                 tone: 'paper'   },
    { label: 'QC pass',              value: `${live.pass_count} / ${live.total_samples}`, tone: 'success' },
    { label: 'Avg mean depth',       value: `${live.avg_depth}×`,            tone: 'neutral' },
  ] : [
    { label: 'Total samples',  value: '—', tone: 'info'    },
    { label: 'Total variants', value: '—', tone: 'neutral' },
    { label: 'HIGH impact',    value: '—', tone: 'warning' },
    { label: 'Runs',           value: '—', tone: 'paper'   },
    { label: 'QC pass',        value: '—', tone: 'success' },
    { label: 'Avg depth',      value: '—', tone: 'neutral' },
  ];
  return (
    <div style={{ padding: 32, maxWidth: 1280, margin: '0 auto' }}>
      <div style={{ display: 'flex', alignItems: 'baseline', justifyContent: 'space-between', marginBottom: 24 }}>
        <div>
          <h1 className="t-page" style={{ margin: 0 }}>Welcome back, {firstName}</h1>
          <div className="t-meta" style={{ marginTop: 4 }}>
            {org} · {today}
          </div>
        </div>
        <div style={{ display: 'flex', gap: 8 }}>
          <button className="btn"><Icon d={I.filter} size={13} />My cases</button>
          <button className="btn btn-primary"><Icon d={I.plus} size={13} />New case</button>
        </div>
      </div>

      {/* Stat tiles */}
      <div style={{ display: 'grid', gridTemplateColumns: 'repeat(6, 1fr)', gap: 12, marginBottom: 24 }}>
        {tiles.map((t, i) => (
          <div key={i} className="surface" style={{ padding: 16 }}>
            <div className="t-label" style={{ marginBottom: 8, color: ACCENT[t.tone] }}>
              {t.label}
            </div>
            <div style={{ fontSize: 18, fontWeight: 600, fontVariantNumeric: 'tabular-nums' }}>{t.value}</div>
          </div>
        ))}
      </div>

      {/* Two-column: sequencing overview + run breakdown */}
      <div style={{ display: 'grid', gridTemplateColumns: '1.4fr 1fr', gap: 16 }}>
        <section className="surface" style={{ padding: 0, overflow: 'hidden' }}>
          <header style={{
            padding: '12px 16px', borderBottom: '1px solid var(--border)',
            display: 'flex', justifyContent: 'space-between', alignItems: 'center',
          }}>
            <div>
              <div className="t-card">Sample overview</div>
              <div className="t-meta" style={{ marginTop: 2 }}>
                {live ? `${live.pass_count} QC pass · ${live.fail_count} QC fail · sorted by run` : 'Loading…'}
              </div>
            </div>
            <button className="btn btn-ghost btn-sm" onClick={() => navigate('cases')}>
              View all <Icon d={I.chevronR} size={11} />
            </button>
          </header>
          <table className="cx">
            <thead>
              <tr>
                <th>Run</th>
                <th style={{ textAlign: 'right' }}>Samples</th>
                <th style={{ textAlign: 'right' }}>Variants</th>
                <th style={{ textAlign: 'right' }}>HIGH</th>
                <th style={{ textAlign: 'right' }}>Avg depth</th>
                <th>QC</th>
              </tr>
            </thead>
            <tbody>
              {live ? Object.entries(live.runs).sort().map(([run, count]) => (
                <tr key={run} onClick={() => navigate('cases')}>
                  <td><div className="mono" style={{ fontWeight: 500, fontSize: 12 }}>{run}</div></td>
                  <td style={{ textAlign: 'right', fontVariantNumeric: 'tabular-nums' }}>{count}</td>
                  <td style={{ textAlign: 'right', fontVariantNumeric: 'tabular-nums', color: 'var(--text-secondary)' }}>—</td>
                  <td style={{ textAlign: 'right', fontVariantNumeric: 'tabular-nums', color: 'var(--text-secondary)' }}>—</td>
                  <td style={{ textAlign: 'right', fontVariantNumeric: 'tabular-nums', color: 'var(--text-secondary)' }}>—</td>
                  <td><Chip tone="success">Complete</Chip></td>
                </tr>
              )) : (
                <tr><td colSpan={6} style={{ color: 'var(--text-tertiary)', textAlign: 'center', padding: 24 }}>Loading…</td></tr>
              )}
            </tbody>
          </table>
        </section>

        <section className="surface" style={{ padding: 0, overflow: 'hidden' }}>
          <header style={{ padding: '12px 16px', borderBottom: '1px solid var(--border)' }}>
            <div className="t-card">Cohort summary</div>
            <div className="t-meta" style={{ marginTop: 2 }}>Variant impact distribution across all samples</div>
          </header>
          <ul style={{ listStyle: 'none', margin: 0, padding: 0 }}>
            {(live ? [
              { tone: 'warning', label: 'HIGH impact',          value: live.total_high.toLocaleString(),     pct: Math.round(live.total_high / live.total_variants * 100) },
              { tone: 'info',    label: 'MODERATE impact',      value: live.total_moderate.toLocaleString(), pct: Math.round(live.total_moderate / live.total_variants * 100) },
              { tone: 'neutral', label: 'LOW impact',           value: live.total_low.toLocaleString(),      pct: Math.round(live.total_low / live.total_variants * 100) },
              { tone: 'neutral', label: 'MODIFIER',             value: live.total_modifier.toLocaleString(), pct: Math.round(live.total_modifier / live.total_variants * 100) },
              { tone: 'success', label: 'Avg tech confidence',  value: `${live.avg_tech_confidence}/100`,    pct: live.avg_tech_confidence },
              { tone: 'success', label: 'Avg clin confidence',  value: `${live.avg_clin_confidence}/100`,    pct: live.avg_clin_confidence },
            ] : []).map((u, i) => (
              <li key={i} style={{ padding: '12px 16px', borderBottom: '1px solid var(--border)', display: 'flex', alignItems: 'center', gap: 12 }}>
                <div style={{ flex: 1, minWidth: 0 }}>
                  <div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: 5 }}>
                    <span style={{ fontSize: 12, color: 'var(--text-secondary)' }}>{u.label}</span>
                    <span className="mono" style={{ fontSize: 13, fontWeight: 600, color: ACCENT[u.tone] }}>{u.value}</span>
                  </div>
                  <div style={{ height: 4, borderRadius: 2, background: 'var(--border)' }}>
                    <div style={{ height: 4, borderRadius: 2, width: `${Math.min(u.pct, 100)}%`, background: ACCENT[u.tone], transition: 'width 600ms ease' }} />
                  </div>
                </div>
              </li>
            ))}
            {!live && (
              <li style={{ padding: 24, textAlign: 'center', color: 'var(--text-tertiary)' }}>Loading…</li>
            )}
          </ul>
        </section>
      </div>
    </div>
  );
}

function ProjectsScreen({ navigate }) {
  const [projects, setProjects] = useState(null);
  useEffect(() => {
    fetch('patient_data/json/projects.json')
      .then(r => r.ok ? r.json() : null)
      .then(d => { if (d) setProjects(d); })
      .catch(() => {});
  }, []);

  const confColor = v => v == null ? '' : v >= 90 ? 'var(--accent-success)' : v >= 75 ? 'var(--accent-warning)' : 'var(--accent-danger)';

  return (
    <div style={{ padding: 32, maxWidth: 1280, margin: '0 auto' }}>
      <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline', marginBottom: 24 }}>
        <div>
          <h1 className="t-page" style={{ margin: 0 }}>Sequencing Runs</h1>
          <div className="t-meta" style={{ marginTop: 4 }}>
            {projects ? `${projects.length} runs · ${projects.reduce((s, p) => s + p.sample_count, 0)} samples total` : 'Loading…'}
          </div>
        </div>
      </div>

      <div className="surface" style={{ padding: 0, overflow: 'hidden' }}>
        <table className="cx">
          <thead>
            <tr>
              <th>Run ID</th>
              <th style={{ textAlign: 'right' }}>Samples</th>
              <th style={{ textAlign: 'right' }}>Variants</th>
              <th style={{ textAlign: 'right' }}>HIGH</th>
              <th style={{ textAlign: 'right' }}>MODERATE</th>
              <th style={{ textAlign: 'right' }}>Avg depth</th>
              <th style={{ textAlign: 'right' }}>Tech conf.</th>
              <th style={{ textAlign: 'right' }}>Clin conf.</th>
              <th>QC pass</th>
            </tr>
          </thead>
          <tbody>
            {projects ? projects.map(p => (
              <tr key={p.id} onClick={() => navigate('cases')}>
                <td><div className="mono" style={{ fontWeight: 500, fontSize: 12 }}>{p.run_id}</div></td>
                <td style={{ textAlign: 'right', fontVariantNumeric: 'tabular-nums' }}>{p.sample_count}</td>
                <td style={{ textAlign: 'right', fontVariantNumeric: 'tabular-nums' }}>{p.total_variants.toLocaleString()}</td>
                <td style={{ textAlign: 'right', fontVariantNumeric: 'tabular-nums', color: 'var(--accent-warning)' }}>{p.high_count.toLocaleString()}</td>
                <td style={{ textAlign: 'right', fontVariantNumeric: 'tabular-nums', color: 'var(--text-secondary)' }}>{p.moderate_count.toLocaleString()}</td>
                <td style={{ textAlign: 'right', fontVariantNumeric: 'tabular-nums' }}>{p.avg_depth != null ? `${p.avg_depth}×` : '—'}</td>
                <td style={{ textAlign: 'right', fontVariantNumeric: 'tabular-nums', color: confColor(p.avg_tech_confidence) }}>{p.avg_tech_confidence != null ? `${p.avg_tech_confidence}/100` : '—'}</td>
                <td style={{ textAlign: 'right', fontVariantNumeric: 'tabular-nums', color: confColor(p.avg_clin_confidence) }}>{p.avg_clin_confidence != null ? `${p.avg_clin_confidence}/100` : '—'}</td>
                <td>
                  <Chip tone={p.pass_count === p.sample_count ? 'success' : p.fail_count > 0 ? 'warning' : 'neutral'}>
                    {p.pass_count}/{p.sample_count}
                  </Chip>
                </td>
              </tr>
            )) : (
              <tr><td colSpan={9} style={{ padding: 32, textAlign: 'center', color: 'var(--text-tertiary)' }}>Loading…</td></tr>
            )}
          </tbody>
        </table>
      </div>
    </div>
  );
}

function CasesScreen({ navigate, viewState = 'normal' }) {
  const [cases, setCases]   = useState(null);
  const [filter, setFilter] = useState('all');
  const [search, setSearch] = useState('');
  const [selected, setSelected] = useState(new Set());
  const [localState, setLocalState] = useState(viewState);

  useEffect(() => { setLocalState(viewState); }, [viewState]);
  useEffect(() => {
    fetch('patient_data/json/cases.json')
      .then(r => r.ok ? r.json() : null)
      .then(d => { if (d) setCases(d); })
      .catch(() => {});
  }, []);

  const sorted = useMemo(() => {
    if (!cases) return [];
    return [...cases].sort((a, b) => a.run_id.localeCompare(b.run_id) || a.sample_id.localeCompare(b.sample_id, undefined, { numeric: true }));
  }, [cases]);

  const filtered = useMemo(() => {
    let list = sorted;
    if (filter !== 'all') list = list.filter(c => filter.startsWith('run:') ? c.run_id === filter.slice(4) : c.status === filter);
    if (search.trim()) {
      const q = search.toLowerCase();
      list = list.filter(c => c.sample_id.includes(q) || c.run_id.toLowerCase().includes(q));
    }
    return list;
  }, [filter, sorted, search]);

  const filtersActive = filter !== 'all' || search.trim().length > 0;
  const runs = useMemo(() => [...new Set((sorted).map(c => c.run_id))].sort(), [sorted]);

  const tabs = [
    { key: 'all',      label: 'All',     count: sorted.length },
    { key: 'pass',     label: 'QC pass', count: sorted.filter(c => c.status === 'pass').length },
    { key: 'fail',     label: 'QC fail', count: sorted.filter(c => c.status === 'fail').length },
    ...runs.map(r => ({ key: `run:${r}`, label: r, count: sorted.filter(c => c.run_id === r).length })),
  ];

  const toggle = (id) => {
    const next = new Set(selected);
    next.has(id) ? next.delete(id) : next.add(id);
    setSelected(next);
  };
  const toggleAll = () => {
    if (selected.size === filtered.length) setSelected(new Set());
    else setSelected(new Set(filtered.map(c => c.id)));
  };

  const bulkBarVisible = selected.size > 0;
  const confColor = v => v == null ? '' : v >= 90 ? 'var(--accent-success)' : v >= 75 ? 'var(--accent-warning)' : 'var(--accent-danger)';

  return (
    <div style={{ padding: 32, maxWidth: 1480, margin: '0 auto', position: 'relative' }}>
      <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline', marginBottom: 16 }}>
        <div>
          <h1 className="t-page" style={{ margin: 0 }}>Sample Cases</h1>
          <div className="t-meta" style={{ marginTop: 4 }}>
            {cases ? `${cases.length} samples · ${runs.length} sequencing runs · GRCh38 WES` : 'Loading…'}
          </div>
        </div>
        <div style={{ display: 'flex', gap: 8 }}>
          <button className="btn" onClick={() => {
            if (!cases) return;
            const cols = 'SampleID\tRunID\tStatus\tVariants\tHIGH\tMODERATE\tMeanDepth\tTechConf\tClinConf';
            const rows = cases.map(c => [
              c.sample_id, c.run_id, c.status,
              c.variants_included, c.high, c.moderate,
              c.qc.mean_depth ?? '', c.qc.technical_confidence ?? '', c.qc.clinical_confidence ?? '',
            ].join('\t'));
            const a = document.createElement('a');
            a.href = URL.createObjectURL(new Blob([[cols, ...rows].join('\n')], { type: 'text/tab-separated-values' }));
            a.download = 'cases_export.tsv';
            a.click();
            URL.revokeObjectURL(a.href);
          }}>
            <Icon d={I.external} size={13} />Export TSV
          </button>
        </div>
      </div>

      {/* Tabs */}
      <div style={{ display: 'flex', gap: 4, borderBottom: '1px solid var(--border)', marginBottom: 0, alignItems: 'flex-end' }}>
        {tabs.map(t => (
          <button key={t.key} onClick={() => setFilter(t.key)}
            style={{
              background: 'transparent', border: 0, cursor: 'pointer',
              padding: '8px 12px', display: 'flex', alignItems: 'center', gap: 6,
              fontSize: 13, fontWeight: 500,
              color: filter === t.key ? 'var(--text-primary)' : 'var(--text-secondary)',
              borderBottom: `2px solid ${filter === t.key ? 'var(--accent-info)' : 'transparent'}`,
              marginBottom: -1,
            }}>
            {t.label}
            <span style={{
              fontSize: 11, padding: '0 6px', borderRadius: 8, lineHeight: '16px',
              background: 'var(--bg-raised)', color: 'var(--text-secondary)',
              fontVariantNumeric: 'tabular-nums',
            }}>{t.count}</span>
          </button>
        ))}
        <div style={{ flex: 1 }} />
        <div style={{ display: 'flex', alignItems: 'center', gap: 6, paddingBottom: 6 }}>
          <Icon d={I.search} size={12} />
          <input
            value={search}
            onChange={e => setSearch(e.target.value)}
            placeholder="Filter by family, ID, phenotype…"
            style={{
              background: 'transparent', border: 0, outline: 'none',
              fontSize: 12, color: 'var(--text-primary)', width: 200,
            }} />
        </div>
      </div>

      {/* Bulk actions bar */}
      <div style={{
        height: bulkBarVisible ? 48 : 0,
        overflow: 'hidden',
        transition: 'height 150ms ease-out',
      }}>
        <div style={{
          height: 48, display: 'flex', alignItems: 'center', justifyContent: 'space-between',
          padding: '0 16px',
          background: 'color-mix(in oklab, var(--accent-info) 14%, var(--bg-surface))',
          borderRadius: 6, border: '1px solid color-mix(in oklab, var(--accent-info) 28%, transparent)',
          marginTop: 12,
        }}>
          <div style={{ display: 'flex', alignItems: 'center', gap: 16 }}>
            <span style={{ fontSize: 13, fontWeight: 500 }}>
              {selected.size} sample{selected.size === 1 ? '' : 's'} selected
            </span>
            <button className="btn btn-ghost btn-sm" onClick={() => setSelected(new Set())}>
              <Icon d={I.x} size={11} />Clear
            </button>
          </div>
          <div style={{ display: 'flex', gap: 8 }}>
            <button className="btn btn-sm" onClick={() => {
              if (!cases) return;
              const ids = [...selected];
              const cols = 'SampleID\tRunID\tStatus\tVariants\tHIGH\tMeanDepth\tTechConf\tClinConf';
              const rows = cases.filter(c => ids.includes(c.id)).map(c => [
                c.sample_id, c.run_id, c.status, c.variants_included, c.high,
                c.qc.mean_depth ?? '', c.qc.technical_confidence ?? '', c.qc.clinical_confidence ?? '',
              ].join('\t'));
              const a = document.createElement('a');
              a.href = URL.createObjectURL(new Blob([[cols, ...rows].join('\n')], { type: 'text/tab-separated-values' }));
              a.download = 'selected_cases.tsv'; a.click(); URL.revokeObjectURL(a.href);
            }}>
              <Icon d={I.external} size={11} />Export selected
            </button>
          </div>
        </div>
      </div>

      {/* Body */}
      {!cases ? (
        <div style={{ marginTop: 16 }}>
          <ListSkeleton rows={8} cols={[40, 100, 160, 100, 100, 100, 100, 100, 100]} />
        </div>
      ) : filtered.length === 0 && filtersActive ? (
        <div style={{ marginTop: 16 }}>
          <EmptyFiltered subject="samples" onClearFilters={() => { setFilter('all'); setSearch(''); }} />
        </div>
      ) : (
      <div className="surface" style={{ padding: 0, overflow: 'hidden', marginTop: bulkBarVisible ? 0 : 16 }}>
        <table className="cx">
          <thead>
            <tr>
              <th style={{ width: 32, paddingLeft: 16 }}>
                <input type="checkbox"
                  checked={selected.size > 0 && selected.size === filtered.length}
                  ref={el => { if (el) el.indeterminate = selected.size > 0 && selected.size < filtered.length; }}
                  onChange={toggleAll}
                  onClick={e => e.stopPropagation()}
                  style={{ accentColor: 'var(--accent-info)' }} />
              </th>
              <th>Sample</th>
              <th>Run</th>
              <th style={{ textAlign: 'right' }}>Variants</th>
              <th style={{ textAlign: 'right' }}>HIGH</th>
              <th style={{ textAlign: 'right' }}>MODERATE</th>
              <th style={{ textAlign: 'right' }}>Mean depth</th>
              <th style={{ textAlign: 'right' }}>Tech conf.</th>
              <th style={{ textAlign: 'right' }}>Clin conf.</th>
              <th>QC</th>
            </tr>
          </thead>
          <tbody>
            {filtered.map(c => {
              const isSel = selected.has(c.id);
              return (
                <tr key={c.id} className={isSel ? 'selected' : ''}
                    onClick={() => navigate('genomics', { sample: c.sample_id })}>
                  <td style={{ paddingLeft: 16 }} onClick={e => e.stopPropagation()}>
                    <input type="checkbox" checked={isSel}
                      onChange={() => toggle(c.id)}
                      style={{ accentColor: 'var(--accent-info)' }} />
                  </td>
                  <td>
                    <div className="mono" style={{ fontWeight: 600 }}>Sample {c.sample_id}</div>
                    <div className="mono t-meta">{c.id}</div>
                  </td>
                  <td className="mono" style={{ fontSize: 11, color: 'var(--text-secondary)' }}>{c.run_id}</td>
                  <td className="mono" style={{ textAlign: 'right' }}>{c.variants_included.toLocaleString()}</td>
                  <td className="mono" style={{ textAlign: 'right', color: 'var(--accent-warning)' }}>{c.high.toLocaleString()}</td>
                  <td className="mono" style={{ textAlign: 'right', color: 'var(--text-secondary)' }}>{c.moderate.toLocaleString()}</td>
                  <td className="mono" style={{ textAlign: 'right' }}>{c.qc.mean_depth != null ? `${c.qc.mean_depth.toFixed(0)}×` : '—'}</td>
                  <td className="mono" style={{ textAlign: 'right', color: confColor(c.qc.technical_confidence) }}>{c.qc.technical_confidence != null ? `${c.qc.technical_confidence.toFixed(1)}` : '—'}</td>
                  <td className="mono" style={{ textAlign: 'right', color: confColor(c.qc.clinical_confidence) }}>{c.qc.clinical_confidence != null ? `${c.qc.clinical_confidence.toFixed(1)}` : '—'}</td>
                  <td><Chip tone={c.status === 'pass' ? 'success' : c.status === 'marginal' ? 'warning' : 'danger'}>{c.status}</Chip></td>
                </tr>
              );
            })}
          </tbody>
        </table>
      </div>
      )}
    </div>
  );
}

// Inline drawer row for PAPER flag — expandable, must not navigate
function PaperDrawerRow({ flag, caseRef, colSpan, onClose }) {
  return (
    <tr style={{ cursor: 'default' }} onClick={e => e.stopPropagation()}>
      <td colSpan={colSpan} style={{
        padding: 0,
        background: 'color-mix(in oklab, var(--accent-paper) 8%, var(--bg-surface))',
        borderBottom: '1px solid var(--border)',
      }}>
        <div style={{ padding: '16px 24px', display: 'flex', gap: 16, alignItems: 'flex-start' }}>
          <div style={{
            width: 32, height: 32, borderRadius: 6, flexShrink: 0,
            background: 'color-mix(in oklab, var(--accent-paper) 22%, transparent)',
            color: 'var(--accent-paper)',
            display: 'flex', alignItems: 'center', justifyContent: 'center',
          }}>
            <Icon d={I.paper} size={16} />
          </div>
          <div style={{ flex: 1, minWidth: 0 }}>
            <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 4 }}>
              <span className="mono" style={{ fontSize: 12, fontWeight: 600 }}>{flag.gene}</span>
              <span className="t-meta">{flag.journal}</span>
              <span className="mono t-meta">PMID {flag.pmid}</span>
              <span style={{
                fontSize: 10, padding: '1px 5px', borderRadius: 2, fontWeight: 700, letterSpacing: 0.04,
                background: `color-mix(in oklab, ${ACCENT[MATCH_TONE[flag.match]]} 22%, transparent)`,
                color: ACCENT[MATCH_TONE[flag.match]],
              }}>{flag.match.toUpperCase()} MATCH</span>
            </div>
            <div style={{ fontSize: 13, fontWeight: 500, marginBottom: 6 }}>
              {flag.title}
            </div>
            <p style={{ margin: 0, fontSize: 12, color: 'var(--text-secondary)', lineHeight: 1.6 }}>
              {flag.excerpt}
            </p>
            <div style={{ display: 'flex', gap: 8, marginTop: 12 }}>
              <button className="btn btn-primary btn-sm"><Icon d={I.bolt} size={11} />Start reanalysis</button>
              <button className="btn btn-sm"><Icon d={I.external} size={11} />Open paper</button>
              <button className="btn btn-sm">Dismiss flag</button>
            </div>
          </div>
          <button className="btn btn-ghost btn-sm" onClick={onClose} style={{ flexShrink: 0 }}>
            <Icon d={I.x} size={12} />
          </button>
        </div>
      </td>
    </tr>
  );
}

// Inline drawer row for REQUEST flag
function RequestDrawerRow({ flag, colSpan, onClose }) {
  return (
    <tr style={{ cursor: 'default' }} onClick={e => e.stopPropagation()}>
      <td colSpan={colSpan} style={{
        padding: 0,
        background: 'color-mix(in oklab, var(--accent-request) 8%, var(--bg-surface))',
        borderBottom: '1px solid var(--border)',
      }}>
        <div style={{ padding: '16px 24px', display: 'flex', gap: 16, alignItems: 'flex-start' }}>
          <div style={{
            width: 32, height: 32, borderRadius: 6, flexShrink: 0,
            background: 'color-mix(in oklab, var(--accent-request) 22%, transparent)',
            color: 'var(--accent-request)',
            display: 'flex', alignItems: 'center', justifyContent: 'center',
          }}>
            <Icon d={I.user} size={16} />
          </div>
          <div style={{ flex: 1, minWidth: 0 }}>
            <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 6 }}>
              <span style={{ fontSize: 13, fontWeight: 500 }}>{flag.requester}</span>
              <span className="t-meta">requested {flag.date}</span>
            </div>
            <p style={{ margin: 0, fontSize: 13, color: 'var(--text-secondary)', lineHeight: 1.6, fontStyle: 'italic' }}>
              "{flag.message}"
            </p>
            <div style={{ display: 'flex', gap: 8, marginTop: 12 }}>
              <button className="btn btn-primary btn-sm"><Icon d={I.user} size={11} />Reply</button>
              <button className="btn btn-sm">Mark in progress</button>
              <button className="btn btn-sm">Dismiss</button>
            </div>
          </div>
          <button className="btn btn-ghost btn-sm" onClick={onClose} style={{ flexShrink: 0 }}>
            <Icon d={I.x} size={12} />
          </button>
        </div>
      </td>
    </tr>
  );
}

// TIME flag confirm-reanalysis modal
function TimeFlagModal({ flag, onClose }) {
  return (
    <div onClick={onClose} style={{
      position: 'fixed', inset: 0, background: 'rgba(10,14,20,0.6)',
      display: 'flex', alignItems: 'center', justifyContent: 'center',
      zIndex: 200, backdropFilter: 'blur(2px)',
    }}>
      <div onClick={e => e.stopPropagation()}
           style={{
             background: 'var(--bg-raised)', width: 440, maxWidth: '90vw',
             borderRadius: 8, overflow: 'hidden',
             border: '1px solid var(--border-hover)',
             boxShadow: '0 24px 64px rgba(0,0,0,0.5)',
           }}>
        <div style={{ padding: '16px 20px', borderBottom: '1px solid var(--border)' }}>
          <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
            <Icon d={I.clock} size={14} />
            <div className="t-card">Time-based reanalysis</div>
          </div>
          <div className="t-meta" style={{ marginTop: 4 }}>
            Last analyzed {flag.months} months ago. Re-run with current evidence?
          </div>
        </div>
        <div style={{ padding: 20, display: 'flex', flexDirection: 'column', gap: 12 }}>
          <div className="raised" style={{ padding: 12, display: 'flex', justifyContent: 'space-between' }}>
            <span className="t-meta">Estimated time</span>
            <span className="mono" style={{ fontWeight: 500 }}>~4 minutes</span>
          </div>
          <div className="raised" style={{ padding: 12, display: 'flex', justifyContent: 'space-between' }}>
            <span className="t-meta">New evidence since last run</span>
            <span style={{ color: 'var(--accent-paper)', fontWeight: 500 }}>3 papers · 1 db update</span>
          </div>
        </div>
        <div style={{
          padding: 16, borderTop: '1px solid var(--border)',
          display: 'flex', gap: 8, justifyContent: 'flex-end',
        }}>
          <button className="btn btn-sm" onClick={onClose}>Remind me in 30 days</button>
          <button className="btn btn-sm" onClick={onClose}>Cancel</button>
          <button className="btn btn-primary btn-sm" onClick={onClose}>
            <Icon d={I.bolt} size={11} />Trigger reanalysis
          </button>
        </div>
      </div>
    </div>
  );
}

Object.assign(window, {
  DashboardScreen, ProjectsScreen, CasesScreen,
  ROUTES, FLAG_TO_TONE, STATUS_LABEL, PRIORITY,
  Sidebar, TopBar,
});
