// theSitrep shared atoms + Lucide-style icon set
// (Self-contained · no link to theSitrep 2.1)

const { useState, useEffect, useRef, useMemo, useCallback } = React;

function Icon({ name, size = 16, className = '', strokeWidth = 1.75 }) {
  const props = { width: size, height: size, viewBox: '0 0 24 24', fill: 'none', stroke: 'currentColor', strokeWidth, strokeLinecap: 'round', strokeLinejoin: 'round', className };
  const paths = {
    'activity': <polyline points="22 12 18 12 15 21 9 3 6 12 2 12" />,
    'home': <><path d="M3 12 12 4l9 8" /><path d="M5 10v10h14V10" /></>,
    'users': <><circle cx="9" cy="8" r="3.5" /><path d="M2 21c0-3.4 3-6 7-6s7 2.6 7 6" /><circle cx="17" cy="9" r="2.5" /><path d="M22 19c0-2.4-2-4.3-5-4.3" /></>,
    'user': <><circle cx="12" cy="8" r="4" /><path d="M4 21c0-4 4-7 8-7s8 3 8 7" /></>,
    'search': <><circle cx="11" cy="11" r="7" /><path d="m20 20-3.5-3.5" /></>,
    'command': <path d="M18 6a3 3 0 1 0-3 3h6V6zM6 6a3 3 0 1 1 3 3H3V6zM18 18a3 3 0 1 1-3-3h6v3zM6 18a3 3 0 1 0 3-3H3v3zM9 9h6v6H9z" />,
    'chevron-down': <polyline points="6 9 12 15 18 9" />,
    'chevron-right': <polyline points="9 6 15 12 9 18" />,
    'chevron-left': <polyline points="15 6 9 12 15 18" />,
    'arrow-up': <><path d="M12 19V5" /><path d="m5 12 7-7 7 7" /></>,
    'arrow-down': <><path d="M12 5v14" /><path d="m19 12-7 7-7-7" /></>,
    'arrow-right': <><path d="M5 12h14" /><path d="m13 5 7 7-7 7" /></>,
    'sun': <><circle cx="12" cy="12" r="4" /><path d="M12 2v2M12 20v2M4.93 4.93l1.41 1.41M17.66 17.66l1.41 1.41M2 12h2M20 12h2M6.34 17.66l-1.41 1.41M19.07 4.93l-1.41 1.41" /></>,
    'moon': <path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z" />,
    'plus': <><path d="M12 5v14" /><path d="M5 12h14" /></>,
    'check': <polyline points="20 6 9 17 4 12" />,
    'x': <><path d="M18 6 6 18" /><path d="m6 6 12 12" /></>,
    'sliders': <><line x1="4" x2="4" y1="21" y2="14" /><line x1="4" x2="4" y1="10" y2="3" /><line x1="12" x2="12" y1="21" y2="12" /><line x1="12" x2="12" y1="8" y2="3" /><line x1="20" x2="20" y1="21" y2="16" /><line x1="20" x2="20" y1="12" y2="3" /><line x1="2" x2="6" y1="14" y2="14" /><line x1="10" x2="14" y1="8" y2="8" /><line x1="18" x2="22" y1="16" y2="16" /></>,
    'circle-dot': <><circle cx="12" cy="12" r="9" /><circle cx="12" cy="12" r="2.5" fill="currentColor" /></>,
    'circle': <circle cx="12" cy="12" r="9" />,
    'alert-triangle': <><path d="M10.3 3.86 1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z" /><path d="M12 9v4" /><path d="M12 17h.01" /></>,
    'info': <><circle cx="12" cy="12" r="9" /><path d="M12 8v.01M11 12h1v4h1" /></>,
    'flag': <><path d="M4 21V4h13l-2 4 2 4H4" /></>,
    'git-branch': <><line x1="6" x2="6" y1="3" y2="15" /><circle cx="18" cy="6" r="3" /><circle cx="6" cy="18" r="3" /><path d="M18 9a9 9 0 0 1-9 9" /></>,
    'git-pull': <><circle cx="18" cy="18" r="3" /><circle cx="6" cy="6" r="3" /><path d="M13 6h3a2 2 0 0 1 2 2v7" /><line x1="6" x2="6" y1="9" y2="21" /></>,
    'git-commit': <><line x1="3" x2="9" y1="12" y2="12" /><line x1="15" x2="21" y1="12" y2="12" /><circle cx="12" cy="12" r="3" /></>,
    'git-merge': <><circle cx="18" cy="18" r="3" /><circle cx="6" cy="6" r="3" /><path d="M6 21V9a9 9 0 0 0 9 9" /></>,
    'briefcase': <><rect x="2" y="7" width="20" height="14" rx="2" /><path d="M16 7V5a2 2 0 0 0-2-2h-4a2 2 0 0 0-2 2v2" /></>,
    'message-square': <path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z" />,
    'calendar': <><rect x="3" y="4" width="18" height="18" rx="2" /><line x1="16" x2="16" y1="2" y2="6" /><line x1="8" x2="8" y1="2" y2="6" /><line x1="3" x2="21" y1="10" y2="10" /></>,
    'video': <><path d="m22 8-6 4 6 4V8z" /><rect x="2" y="6" width="14" height="12" rx="2" /></>,
    'bell': <><path d="M6 8a6 6 0 0 1 12 0c0 7 3 9 3 9H3s3-2 3-9" /><path d="M10.3 21a1.94 1.94 0 0 0 3.4 0" /></>,
    'filter': <polygon points="22 3 2 3 10 12.46 10 19 14 21 14 12.46 22 3" />,
    'settings': <><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 0 2.83 2 2 0 0 1-2.83 0l-.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-1-1.51 1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09a1.65 1.65 0 0 0 1.51-1 1.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.33h0a1.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-.33 1.82v0a1.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" /></>,
    'sparkles': <><path d="M12 3 13.9 8.1 19 10 13.9 11.9 12 17 10.1 11.9 5 10 10.1 8.1 12 3z" /><path d="M19 17 19.7 18.9 21.5 19.6 19.7 20.3 19 22 18.3 20.3 16.6 19.6 18.3 18.9 19 17z" /></>,
    'flame': <path d="M8.5 14.5A2.5 2.5 0 0 0 11 17c1.5 0 3-1 3-3 0-2-2-3.5-3-5-1-1.5-1.5-3 .5-5-3 0-7 3-7 8a7 7 0 0 0 14 0c0-3-1-5-3-7" />,
    'zap': <polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2" />,
    'shield': <path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z" />,
    'layers': <><polygon points="12 2 2 7 12 12 22 7 12 2" /><polyline points="2 17 12 22 22 17" /><polyline points="2 12 12 17 22 12" /></>,
    'panel-left': <><rect x="3" y="3" width="18" height="18" rx="2" /><line x1="9" x2="9" y1="3" y2="21" /></>,
    'more': <><circle cx="5" cy="12" r="1.5" /><circle cx="12" cy="12" r="1.5" /><circle cx="19" cy="12" r="1.5" /></>,
    'trending-up': <><polyline points="22 7 13.5 15.5 8.5 10.5 2 17" /><polyline points="16 7 22 7 22 13" /></>,
    'trending-down': <><polyline points="22 17 13.5 8.5 8.5 13.5 2 7" /><polyline points="16 17 22 17 22 11" /></>,
    'rocket': <><path d="M4.5 16.5c-1.5 1.26-2 5-2 5s3.74-.5 5-2c.71-.84.7-2.13-.09-2.91a2.18 2.18 0 0 0-2.91-.09z" /><path d="M12 15l-3-3a22 22 0 0 1 2-3.95A12.88 12.88 0 0 1 22 2c0 2.72-.78 7.5-6 11a22.35 22.35 0 0 1-4 2z" /><path d="M9 12H4s.55-3.03 2-4c1.62-1.08 5 0 5 0M12 15v5s3.03-.55 4-2c1.08-1.62 0-5 0-5" /></>,
    'clock': <><circle cx="12" cy="12" r="9" /><polyline points="12 7 12 12 15 14" /></>,
    'link': <><path d="M10 14a4 4 0 0 0 5.66 0l3-3a4 4 0 0 0-5.66-5.66l-1 1" /><path d="M14 10a4 4 0 0 0-5.66 0l-3 3a4 4 0 0 0 5.66 5.66l1-1" /></>,
    'external': <><path d="M14 4h6v6" /><path d="M10 14 20 4" /><path d="M19 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V7a2 2 0 0 1 2-2h6" /></>,
    'heartbeat': <path d="M3 12h4l3-7 4 14 3-7h4" />,
    'award': <><circle cx="12" cy="9" r="6" /><path d="M8.5 14 7 22l5-3 5 3-1.5-8" /></>,
    'coffee': <><path d="M17 8h1a3 3 0 0 1 0 6h-1" /><path d="M3 8h14v8a4 4 0 0 1-4 4H7a4 4 0 0 1-4-4z" /><path d="M6 2v3M10 2v3M14 2v3" /></>,
    'bot': <><rect x="3" y="8" width="18" height="12" rx="2" /><circle cx="9" cy="14" r="1" fill="currentColor" /><circle cx="15" cy="14" r="1" fill="currentColor" /><path d="M12 4v4" /><circle cx="12" cy="3" r="1" /></>,
    'pen': <><path d="M12 19l7-7 3 3-7 7H12v-3z" /><path d="m18 13-1.5-7.5L2 2l3.5 14.5L13 18l5-5z" /><path d="m2 2 7.586 7.586" /><circle cx="11" cy="11" r="2" /></>,
    'pulse': <path d="M22 12h-4l-3 9L9 3l-3 9H2" />,
    'mic': <><rect x="9" y="2" width="6" height="11" rx="3" /><path d="M5 10v2a7 7 0 0 0 14 0v-2" /><line x1="12" x2="12" y1="19" y2="22" /></>,
    'shuffle': <><polyline points="16 3 21 3 21 8" /><line x1="4" x2="21" y1="20" y2="3" /><polyline points="21 16 21 21 16 21" /><line x1="15" x2="21" y1="15" y2="21" /><line x1="4" x2="9" y1="4" y2="9" /></>,
    'eye': <><path d="M2 12s3-7 10-7 10 7 10 7-3 7-10 7-10-7-10-7z" /><circle cx="12" cy="12" r="3" /></>,
    'check-circle': <><circle cx="12" cy="12" r="9" /><polyline points="9 12 11 14 15 10" /></>,
    'minus': <path d="M5 12h14" />,
    'lock': <><rect x="3" y="11" width="18" height="11" rx="2" /><path d="M7 11V7a5 5 0 0 1 10 0v4" /></>,
  };
  return <svg {...props}>{paths[name] || null}</svg>;
}

function Avatar({ initials, size = 24, glow = false }) {
  const palette = ['#6b6fbb','#ad6b9a','#7e9d6a','#bb9a6b','#6b9dbb','#9a6bbb','#bb6b6b','#6bbb9a','#9abb6b','#bb8a6b'];
  let h = 0; for (const c of initials) h = (h * 31 + c.charCodeAt(0)) >>> 0;
  const bg = palette[h % palette.length];
  return <span style={{
    width: size, height: size, borderRadius: 999,
    background: `linear-gradient(135deg, ${bg}, ${bg}cc)`,
    color: 'white', display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
    fontSize: size * 0.42, fontWeight: 600, letterSpacing: '-0.02em',
    flexShrink: 0,
    boxShadow: glow ? '0 0 0 2px var(--bg), 0 0 0 3px var(--accent)' : 'none',
  }}>{initials}</span>;
}

function StatusCircle({ kind = 'todo', size = 14 }) {
  const c = { todo: 'var(--text-3)', progress: 'var(--warn)', review: 'var(--accent)', done: 'var(--good)', bug: 'var(--bad)', blocked: 'var(--bad)' };
  const stroke = c[kind] || 'var(--text-3)';
  const radius = size / 2 - 1.2;
  return <svg width={size} height={size} viewBox={`0 0 ${size} ${size}`} style={{ flexShrink: 0 }}>
    {kind === 'done' ? (
      <>
        <circle cx={size/2} cy={size/2} r={radius+0.5} fill={stroke} />
        <polyline points={`${size*0.30},${size*0.52} ${size*0.45},${size*0.66} ${size*0.70},${size*0.38}`} fill="none" stroke="var(--bg)" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
      </>
    ) : kind === 'progress' ? (
      <>
        <circle cx={size/2} cy={size/2} r={radius} fill="none" stroke={stroke} strokeWidth="1.5" strokeDasharray={`${radius*2*Math.PI*0.66} 999`} transform={`rotate(-90 ${size/2} ${size/2})`} />
        <circle cx={size/2} cy={size/2} r={radius} fill="none" stroke={stroke} strokeOpacity="0.25" strokeWidth="1.5" />
      </>
    ) : kind === 'review' ? (
      <circle cx={size/2} cy={size/2} r={radius} fill="none" stroke={stroke} strokeWidth="1.5" strokeDasharray="2 2" />
    ) : kind === 'bug' || kind === 'blocked' ? (
      <>
        <circle cx={size/2} cy={size/2} r={radius+0.5} fill={stroke} />
        <text x={size/2} y={size*0.7} textAnchor="middle" fontSize={size*0.7} fontWeight="700" fill="var(--bg)">!</text>
      </>
    ) : (
      <circle cx={size/2} cy={size/2} r={radius} fill="none" stroke={stroke} strokeWidth="1.5" strokeDasharray="2 2.5" />
    )}
  </svg>;
}

// AI authorship dual-band: AI % + Hand-written %
function AuthorshipSplit({ aiPct, label = true, height = 6, period }) {
  const ai = Math.max(0, Math.min(100, Math.round(aiPct || 0)));
  const human = 100 - ai;
  const tip = `AI: ${ai}% · Hand-written: ${human}%${period ? ' · ' + period : ''}`;
  return (
    <div className="authorship-split" title={tip} style={{ display: 'flex', flexDirection: 'column', gap: 4, minWidth: 120 }}>
      {label && (
        <div style={{ display: 'flex', justifyContent: 'space-between', fontSize: 11 }}>
          <span style={{ color: 'var(--accent)' }}>AI <strong className="tabular" style={{ marginLeft: 3 }}>{ai}%</strong></span>
          <span style={{ color: 'var(--human)' }}>Hand-written <strong className="tabular" style={{ marginLeft: 3 }}>{human}%</strong></span>
        </div>
      )}
      <div style={{ display: 'flex', width: '100%', height, background: 'var(--surface-3)', borderRadius: 'var(--r-pill)', overflow: 'hidden' }}>
        <div style={{ width: `${ai}%`, background: 'var(--accent)', height: '100%' }} />
        <div style={{ width: `${human}%`, background: 'var(--human)', height: '100%' }} />
      </div>
    </div>
  );
}

// Persisted state hook
function usePersistedState(key, initial) {
  const [v, setV] = useState(() => {
    try { const s = localStorage.getItem(key); return s !== null ? JSON.parse(s) : initial; } catch { return initial; }
  });
  useEffect(() => { try { localStorage.setItem(key, JSON.stringify(v)); } catch {} }, [key, v]);
  return [v, setV];
}

// Per-widget window preference, namespaced under `thesitrep.windowPrefs`.
// `widgetKey` is a stable identifier (e.g., 'windowState', 'carryover.bucketSize').
function useWindowPref(widgetKey, defaultValue) {
  const root = 'thesitrep.windowPrefs';
  const [v, setV] = useState(() => {
    try {
      const all = JSON.parse(localStorage.getItem(root) || '{}');
      return widgetKey in all ? all[widgetKey] : defaultValue;
    } catch { return defaultValue; }
  });
  useEffect(() => {
    try {
      const all = JSON.parse(localStorage.getItem(root) || '{}');
      all[widgetKey] = v;
      localStorage.setItem(root, JSON.stringify(all));
    } catch {}
  }, [widgetKey, v]);
  return [v, setV];
}

// WindowSizeChip · small mono-caps pill rendered in a widget header to let
// the user pick the rolling-window size. Options are weeks (1, 2, 3, 4).
function WindowSizeChip({ value, onChange, options = [1, 2, 3, 4] }) {
  return (
    <span className="window-chip" role="group" aria-label="Window size">
      {options.map(w => (
        <button key={w}
                className={`window-chip-opt ${w === value ? 'active' : ''}`}
                onClick={() => onChange(w)}
                title={`Last ${w * 7} days`}>
          {w}w
        </button>
      ))}
    </span>
  );
}

// WeekStepper · prev/next control for browsing previous weeks. `value` is the
// week offset (0 = current); higher offsets are older. `weeks` is DATA.weeks.
function WeekStepper({ weeks, value, onChange }) {
  const w = weeks[value] || weeks[0];
  const atOldest = value >= weeks.length - 1;
  const atNewest = value <= 0;
  return (
    <span className="window-chip" role="group" aria-label="Week">
      <button className="window-chip-opt" disabled={atOldest}
              style={{ opacity: atOldest ? 0.4 : 1 }}
              onClick={() => onChange(Math.min(weeks.length - 1, value + 1))}
              title="Previous week">
        <Icon name="chevron-left" size={12} />
      </button>
      <span className="window-chip-opt active" style={{ cursor: 'default' }}>
        {w.label} · {w.range}
      </span>
      <button className="window-chip-opt" disabled={atNewest}
              style={{ opacity: atNewest ? 0.4 : 1 }}
              onClick={() => onChange(Math.max(0, value - 1))}
              title="Next week">
        <Icon name="chevron-right" size={12} />
      </button>
    </span>
  );
}

// Tiny hash router
function useRoute() {
  const [hash, setHash] = useState(() => window.location.hash || '#/');
  useEffect(() => {
    const h = () => setHash(window.location.hash || '#/');
    window.addEventListener('hashchange', h);
    return () => window.removeEventListener('hashchange', h);
  }, []);
  const navigate = useCallback((p) => { window.location.hash = p.startsWith('#') ? p : '#' + p; }, []);
  // path[0]=view, path[1]=id (e.g. /dev/eng-04 → view=dev, id=eng-04)
  const stripped = hash.replace(/^#\/?/, '');
  const parts = stripped.split('/').filter(Boolean);
  const view = parts[0] || 'dashboard';
  const id = parts[1] || null;
  return { hash, view, id, navigate, parts };
}

// Format helpers
function fmtDays(n) { return n === 1 ? '1d' : `${n}d`; }
function fmtHours(n) { return n === 1 ? '1h' : `${n}h`; }
function fmtTimestamp(d) {
  const dt = (d instanceof Date) ? d : new Date(d);
  return dt.toLocaleString(undefined, { month: 'short', day: 'numeric', hour: 'numeric', minute: '2-digit' });
}
function fmtRelative(daysAgo) {
  if (daysAgo === 0) return 'today';
  if (daysAgo === 1) return 'yesterday';
  if (daysAgo > 0) return `${daysAgo}d ago`;
  if (daysAgo === -1) return 'tomorrow';
  return `in ${-daysAgo}d`;
}

// Tone helper from a 0-100 score-like value (only used for AI %, never as a score)
function toneFromPct(v, goodMin = 30, warnMin = 60) {
  if (v < goodMin) return 'good';
  if (v < warnMin) return 'warn';
  return 'bad';
}

// TeamFilter · single-select searchable dropdown for picking a team (or "all").
// Lives here so multiple pages can share it.
function TeamFilter({ teams, value, onChange }) {
  const [open, setOpen] = useState(false);
  const [q, setQ] = useState('');
  const wrapRef = useRef(null);
  const inputRef = useRef(null);

  useEffect(() => {
    if (!open) return;
    setQ('');
    setTimeout(() => inputRef.current?.focus(), 30);
    const close = (e) => { if (!wrapRef.current?.contains(e.target)) setOpen(false); };
    const esc = (e) => { if (e.key === 'Escape') setOpen(false); };
    document.addEventListener('mousedown', close);
    document.addEventListener('keydown', esc);
    return () => { document.removeEventListener('mousedown', close); document.removeEventListener('keydown', esc); };
  }, [open]);

  const Q = q.toLowerCase();
  const filtered = teams.filter(t => t.toLowerCase().includes(Q));
  const label = value === 'all' ? 'All teams' : value;

  return (
    <div ref={wrapRef} style={{ position: 'relative', display: 'inline-block' }}>
      <button
        onClick={() => setOpen(o => !o)}
        style={{
          display: 'inline-flex', alignItems: 'center', gap: 6,
          padding: '6px 10px', borderRadius: 'var(--r-md)',
          background: 'var(--surface-2)', border: '1px solid var(--border)',
          color: 'var(--text)', fontSize: 12, cursor: 'pointer',
          minWidth: 180, justifyContent: 'space-between',
        }}>
        <span style={{ display: 'inline-flex', alignItems: 'center', gap: 6 }}>
          <Icon name="users" size={12} className="ci-icon" />
          <span>{label}</span>
        </span>
        <Icon name="chevron-down" size={12} />
      </button>
      {open && (
        <div style={{
          position: 'absolute', top: 'calc(100% + 4px)', left: 0, zIndex: 30,
          width: 240, background: 'var(--bg-elev)',
          border: '1px solid var(--border)', borderRadius: 'var(--r-md)',
          boxShadow: 'var(--shadow-md)', overflow: 'hidden',
        }}>
          <div style={{ display: 'flex', alignItems: 'center', gap: 6, padding: '8px 10px', borderBottom: '1px solid var(--border)' }}>
            <Icon name="search" size={12} />
            <input
              ref={inputRef}
              value={q}
              onChange={e => setQ(e.target.value)}
              placeholder="Search teams…"
              style={{ flex: 1, background: 'transparent', border: 0, outline: 0, fontSize: 12.5, color: 'var(--text)' }}
            />
          </div>
          <div style={{ maxHeight: 240, overflowY: 'auto', padding: '4px 0' }}>
            {[{ key: 'all', label: 'All teams' }, ...filtered.map(t => ({ key: t, label: t }))].map(opt => (
              <button
                key={opt.key}
                onClick={() => { onChange(opt.key); setOpen(false); }}
                style={{
                  width: '100%', textAlign: 'left',
                  display: 'flex', alignItems: 'center', justifyContent: 'space-between',
                  padding: '7px 12px', background: opt.key === value ? 'var(--surface-2)' : 'transparent',
                  color: 'var(--text)', border: 0, fontSize: 12.5, cursor: 'pointer',
                }}>
                <span>{opt.label}</span>
                {opt.key === value && <Icon name="check" size={12} />}
              </button>
            ))}
            {filtered.length === 0 && q && (
              <div style={{ padding: '10px 12px', fontSize: 11.5, color: 'var(--text-3)' }}>No matches</div>
            )}
          </div>
        </div>
      )}
    </div>
  );
}

// DevMultiSelect · searchable multi-select dropdown for engineers.
// `value` is an array of engineer ids. Empty array = "All developers".
function DevMultiSelect({ devs, value, onChange }) {
  const [open, setOpen] = useState(false);
  const [q, setQ] = useState('');
  const wrapRef = useRef(null);
  const inputRef = useRef(null);

  useEffect(() => {
    if (!open) return;
    setQ('');
    setTimeout(() => inputRef.current?.focus(), 30);
    const close = (e) => { if (!wrapRef.current?.contains(e.target)) setOpen(false); };
    const esc = (e) => { if (e.key === 'Escape') setOpen(false); };
    document.addEventListener('mousedown', close);
    document.addEventListener('keydown', esc);
    return () => { document.removeEventListener('mousedown', close); document.removeEventListener('keydown', esc); };
  }, [open]);

  const Q = q.toLowerCase();
  const filtered = devs.filter(d =>
    d.name.toLowerCase().includes(Q) ||
    (d.level || '').toLowerCase().includes(Q) ||
    (d.role || '').toLowerCase().includes(Q)
  );

  const label = (() => {
    if (value.length === 0) return 'All developers';
    if (value.length === 1) {
      const d = devs.find(x => x.id === value[0]);
      return d ? d.name : '1 developer';
    }
    return `${value.length} developers`;
  })();

  const toggle = (id) => {
    if (value.includes(id)) onChange(value.filter(v => v !== id));
    else onChange([...value, id]);
  };

  return (
    <div ref={wrapRef} style={{ position: 'relative', display: 'inline-block' }}>
      <button
        onClick={() => setOpen(o => !o)}
        style={{
          display: 'inline-flex', alignItems: 'center', gap: 6,
          padding: '6px 10px', borderRadius: 'var(--r-md)',
          background: 'var(--surface-2)', border: '1px solid var(--border)',
          color: 'var(--text)', fontSize: 12, cursor: 'pointer',
          minWidth: 200, justifyContent: 'space-between',
        }}>
        <span style={{ display: 'inline-flex', alignItems: 'center', gap: 6 }}>
          <Icon name="users" size={12} className="ci-icon" />
          <span>{label}</span>
        </span>
        <Icon name="chevron-down" size={12} />
      </button>
      {open && (
        <div style={{
          position: 'absolute', top: 'calc(100% + 4px)', left: 0, zIndex: 30,
          width: 280, background: 'var(--bg-elev)',
          border: '1px solid var(--border)', borderRadius: 'var(--r-md)',
          boxShadow: 'var(--shadow-md)', overflow: 'hidden',
        }}>
          <div style={{ display: 'flex', alignItems: 'center', gap: 6, padding: '8px 10px', borderBottom: '1px solid var(--border)' }}>
            <Icon name="search" size={12} />
            <input
              ref={inputRef}
              value={q}
              onChange={e => setQ(e.target.value)}
              placeholder="Search developers…"
              style={{ flex: 1, background: 'transparent', border: 0, outline: 0, fontSize: 12.5, color: 'var(--text)' }}
            />
            {value.length > 0 && (
              <button
                onClick={() => onChange([])}
                style={{ background: 'transparent', border: 0, color: 'var(--text-3)', fontSize: 11, cursor: 'pointer', padding: 2 }}
                title="Clear selection"
              >clear</button>
            )}
          </div>
          <div style={{ maxHeight: 280, overflowY: 'auto', padding: '4px 0' }}>
            {filtered.map(d => {
              const selected = value.includes(d.id);
              return (
                <button
                  key={d.id}
                  onClick={() => toggle(d.id)}
                  style={{
                    width: '100%', textAlign: 'left',
                    display: 'flex', alignItems: 'center', justifyContent: 'space-between',
                    padding: '7px 12px', background: selected ? 'var(--surface-2)' : 'transparent',
                    color: 'var(--text)', border: 0, fontSize: 12.5, cursor: 'pointer',
                  }}>
                  <span style={{ display: 'inline-flex', alignItems: 'center', gap: 8 }}>
                    <Avatar initials={d.initials} size={20} />
                    <span>{d.name}</span>
                  </span>
                  {selected && <Icon name="check" size={12} />}
                </button>
              );
            })}
            {filtered.length === 0 && (
              <div style={{ padding: '10px 12px', fontSize: 11.5, color: 'var(--text-3)' }}>No matches</div>
            )}
          </div>
        </div>
      )}
    </div>
  );
}

Object.assign(window, {
  Icon, Avatar, StatusCircle, AuthorshipSplit,
  usePersistedState, useWindowPref, useRoute,
  WindowSizeChip,
  TeamFilter, DevMultiSelect,
  fmtDays, fmtHours, fmtTimestamp, fmtRelative, toneFromPct,
});
