// Nuance ATS — shared chrome and primitives

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

// =================== Ephemeral toast =============
// Lightweight hook: renders one inline toast inside the current screen.
// Usage:
//   const [toast, showToast, ToastHost] = useAtsToast();
//   <button onClick={() => showToast("Saved view", { tone: "success" })}>…</button>
//   {ToastHost}
function useAtsToast() {
  const [toast, setToast] = useState(null);
  const timerRef = React.useRef(null);
  const show = useCallback((message, opts = {}) => {
    if (timerRef.current) clearTimeout(timerRef.current);
    setToast({ message, tone: opts.tone || "neutral", icon: opts.icon });
    timerRef.current = setTimeout(() => setToast(null), opts.duration || 2800);
  }, []);
  useEffect(() => () => timerRef.current && clearTimeout(timerRef.current), []);
  const host = toast ? (
    <div className="ats-toast" style={{
      background: toast.tone === "success" ? "var(--nl-ink)" : "var(--nl-ink)",
      color: "var(--fg-on-dark)",
    }}>
      <span className="ats-toast__dot" style={{
        background: toast.tone === "success" ? "var(--nl-sage)"
                  : toast.tone === "warn"    ? "var(--nl-amber)"
                  : "var(--nl-blue)"
      }} />
      <span>{toast.message}</span>
    </div>
  ) : null;
  return [toast, show, host];
}

// =================== Viewer avatar menu =========
function ViewerAvatarMenu() {
  const [open, setOpen] = useState(false);
  const buttonRef = useRef(null);
  const menuRef = useRef(null);
  // Use ref to ensure portal container is stable (fixes React 18 portal cleanup issue)
  const portalRootRef = useRef(null);
  if (!portalRootRef.current) {
    portalRootRef.current = document.getElementById('portal-root') || document.body;
  }

  // Get current viewer from window
  const currentViewer = typeof window.__atsCurrentViewer !== 'undefined'
    ? window.__atsCurrentViewer
    : VIEWERS[0];

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

  const handleViewerChange = (viewer) => {
    if (typeof window.__atsSetViewer === 'function') {
      window.__atsSetViewer(viewer);
    }
    setOpen(false);
  };

  const handleLogout = () => {
    setOpen(false);
    if (typeof window.__atsLogout === 'function') {
      window.__atsLogout();
    }
  };

  const isAuthenticated = !!localStorage.getItem('ats_token');
  const authUser = window.__atsAuthUser;
  const isRealAdmin = authUser && authUser.role === 'admin';

  const viewAsRoles = [
    { role: "admin",       label: "Admin",          desc: "Full access", accent: ROLES.admin?.accent || "#1D3140" },
    { role: "interviewer", label: "Interviewer",     desc: "Limited visibility", accent: ROLES.interviewer?.accent || "#7595D5" },
    { role: "external",    label: "External Agent",  desc: "Jon Luzha's view", accent: ROLES.external?.accent || "#9C6B4F" },
  ];

  const switchRole = (role) => {
    if (!authUser || typeof window.__atsSetViewer !== 'function') return;
    if (role === "admin") {
      const nameParts = (authUser.full_name || "").split(" ");
      const initials = nameParts.map(p => p[0] || "").join("").slice(0, 2).toUpperCase();
      window.__atsSetViewer({
        id: authUser.id, name: authUser.full_name, initials,
        color: "#7595D5", role: "admin", title: authUser.email,
        avatar_url: authUser.avatar_url,
      });
    } else if (role === "interviewer") {
      const nameParts = (authUser.full_name || "").split(" ");
      const initials = nameParts.map(p => p[0] || "").join("").slice(0, 2).toUpperCase();
      window.__atsSetViewer({
        id: authUser.id, name: authUser.full_name, initials,
        color: "#7595D5", role: "interviewer", title: authUser.email,
        avatar_url: authUser.avatar_url,
      });
    } else if (role === "external") {
      window.__atsSetViewer({
        id: "ext-jon-luzha", name: "Jon Luzha", initials: "JL",
        color: "#9C6B4F", role: "external", title: "Alexander Chapman",
        sourceTag: "Jon Luzha",
      });
    }
    setOpen(false);
  };

  // Get button position for dropdown placement
  const [buttonRect, setButtonRect] = useState(null);
  useEffect(() => {
    if (open && buttonRef.current) {
      setButtonRect(buttonRef.current.getBoundingClientRect());
    }
  }, [open]);

  return (
    <>
      <button
        ref={buttonRef}
        onClick={() => setOpen(!open)}
        style={{
          background: 'transparent',
          border: 'none',
          cursor: 'pointer',
          padding: 0,
          display: 'flex',
          alignItems: 'center',
        }}
        title={isAuthenticated ? currentViewer.name : "Switch viewer"}
      >
        <Avatar
          name={currentViewer.name}
          src={currentViewer.avatar_url || null}
          size={28}
          ring
        />
      </button>

      {open && buttonRect && ReactDOM.createPortal(
        <div
          ref={menuRef}
          style={{
            position: 'fixed',
            top: buttonRect.bottom + 8,
            right: window.innerWidth - buttonRect.right,
            background: 'rgba(245, 241, 234, 0.98)',
            backdropFilter: 'blur(12px)',
            border: '1px solid var(--border-1)',
            borderRadius: 8,
            boxShadow: '0 4px 20px rgba(29, 49, 64, 0.12), 0 1px 3px rgba(29, 49, 64, 0.08)',
            minWidth: 240,
            zIndex: 999999,
          }}
        >
          <div style={{
            padding: '12px 14px',
            display: 'flex',
            alignItems: 'center',
            gap: 10,
          }}>
            <Avatar
              name={authUser ? authUser.full_name : currentViewer.name}
              src={authUser ? authUser.avatar_url : (currentViewer.avatar_url || null)}
              size={32}
            />
            <div style={{ flex: 1 }}>
              <div style={{ fontSize: 13, fontWeight: 500, color: 'var(--nl-ink)' }}>{authUser ? authUser.full_name : currentViewer.name}</div>
              <div style={{ fontSize: 11, color: 'var(--fg-3)' }}>{authUser ? authUser.email : currentViewer.title}</div>
            </div>
          </div>
          {isRealAdmin && (
            <div style={{ borderTop: '1px solid var(--border-1)' }}>
              <div style={{
                padding: '8px 14px 4px',
                fontSize: 10, fontWeight: 600, color: 'var(--fg-3)',
                textTransform: 'uppercase', letterSpacing: '0.5px',
              }}>View as</div>
              {viewAsRoles.map(v => (
                <button
                  key={v.role}
                  onClick={() => switchRole(v.role)}
                  style={{
                    width: '100%',
                    display: 'flex',
                    alignItems: 'center',
                    gap: 10,
                    padding: '8px 14px',
                    background: currentViewer.role === v.role ? 'rgba(29, 49, 64, 0.06)' : 'transparent',
                    border: 'none',
                    cursor: 'pointer',
                    textAlign: 'left',
                    transition: 'background 0.15s',
                  }}
                  onMouseEnter={(e) => { if (currentViewer.role !== v.role) e.currentTarget.style.background = 'rgba(29, 49, 64, 0.04)'; }}
                  onMouseLeave={(e) => { if (currentViewer.role !== v.role) e.currentTarget.style.background = 'transparent'; }}
                >
                  <span style={{
                    width: 8, height: 8, borderRadius: '50%',
                    background: v.accent, flexShrink: 0,
                  }} />
                  <div style={{ flex: 1 }}>
                    <div style={{ fontSize: 13, fontWeight: 500, color: 'var(--nl-ink)' }}>{v.label}</div>
                    <div style={{ fontSize: 11, color: 'var(--fg-3)' }}>{v.desc}</div>
                  </div>
                  {currentViewer.role === v.role && (
                    <span style={{ fontSize: 14, color: 'var(--nl-blue)' }}>✓</span>
                  )}
                </button>
              ))}
            </div>
          )}
          <div style={{ borderTop: '1px solid var(--border-1)' }}>
            <button
              onClick={handleLogout}
              style={{
                width: '100%',
                padding: '10px 14px',
                background: 'transparent',
                border: 'none',
                cursor: 'pointer',
                textAlign: 'left',
                fontSize: 13,
                color: '#B4553D',
                transition: 'background 0.15s',
              }}
              onMouseEnter={(e) => e.currentTarget.style.background = 'rgba(180, 85, 61, 0.06)'}
              onMouseLeave={(e) => e.currentTarget.style.background = 'transparent'}
            >
              Sign out
            </button>
          </div>
        </div>,
        portalRootRef.current
      )}
    </>
  );
}

Object.assign(window, { useAtsToast, ViewerAvatarMenu });

// =================== Brand mark =================
function Mark({ size = 22 }) {
  return (
    <img
      src="assets/nuance-logomark.png?v=2"
      alt="Nuance Labs"
      width={size}
      height={size}
      style={{ display: "block", objectFit: "contain" }}
    />
  );
}

// =================== Avatar =====================
function Avatar({ src, name, size = 24, ring = false }) {
  const initials = (name || "?")
    .split(" ").map(s => s[0]).slice(0, 2).join("");
  const [err, setErr] = useState(false);
  const dim = { width: size, height: size, fontSize: Math.max(9, size * 0.42) };
  if (!src || err) {
    return (
      <span className="ats-avatar ats-avatar--fallback" style={dim}>
        {initials}
      </span>
    );
  }
  return (
    <span className={"ats-avatar" + (ring ? " ats-avatar--ring" : "")} style={dim}>
      <img src={src} alt="" onError={() => setErr(true)} />
    </span>
  );
}

// Tiny initials disc (recruiter etc.)
function InitialDisc({ initials, color, size = 18, title, avatar }) {
  const [imgErr, setImgErr] = useState(false);
  if (avatar && !imgErr) {
    return (
      <span className="ats-disc" title={title} style={{ width: size, height: size, padding: 0, overflow: "hidden" }}>
        <img src={avatar} alt="" onError={() => setImgErr(true)} style={{ width: "100%", height: "100%", objectFit: "cover", borderRadius: "50%" }} />
      </span>
    );
  }
  return (
    <span
      className="ats-disc"
      title={title}
      style={{
        width: size, height: size,
        background: color, color: "#fff",
        fontSize: Math.max(8, size * 0.5),
      }}
    >
      {initials}
    </span>
  );
}

// =================== Stage chip =================
function StageChip({ stage, dense = false }) {
  return (
    <span
      className={"ats-stage-chip" + (dense ? " is-dense" : "")}
      style={{ background: stage.color, color: stage.fg }}
    >
      <span className="ats-stage-dot" style={{ background: stage.accent }} />
      {dense ? stage.short : stage.label}
    </span>
  );
}

// =================== Source pill ================
function SourcePill({ source }) {
  const s = SOURCES.find(x => x.id === source) || SOURCES[0];
  return <span className="ats-source-pill" data-src={source}>{s.label}</span>;
}

// =================== Search bar ====================
function SearchBar() {
  const [query, setQuery] = useState("");
  const [open, setOpen] = useState(false);
  const [selected, setSelected] = useState(0);
  const inputRef = useRef(null);
  const wrapRef = useRef(null);
  const portalRef = useRef(null);
  if (!portalRef.current) {
    portalRef.current = document.getElementById('portal-root') || document.body;
  }

  const results = useMemo(() => {
    const q = query.trim().toLowerCase();
    if (q.length < 2) return [];
    const terms = q.split(/\s+/);
    const matched = [];
    for (const c of CANDIDATES) {
      const hay = c._searchHay;
      if (terms.every(t => hay.includes(t))) {
        matched.push(c);
        if (matched.length >= 12) break;
      }
    }
    return matched;
  }, [query]);

  const onChange = (e) => {
    setQuery(e.target.value);
    setOpen(true);
    setSelected(0);
  };

  useEffect(() => {
    const handler = (e) => {
      if ((e.metaKey || e.ctrlKey) && e.key === 'k') {
        e.preventDefault();
        inputRef.current?.focus();
        inputRef.current?.select();
        setOpen(true);
      }
    };
    document.addEventListener('keydown', handler);
    return () => document.removeEventListener('keydown', handler);
  }, []);

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

  const goTo = (c) => {
    setOpen(false);
    setQuery("");
    if (typeof window.__atsNavigate === "function") {
      window.__atsNavigate("pipeline");
    }
    setTimeout(() => {
      window.location.hash = "#candidate/" + c.id;
      window.dispatchEvent(new CustomEvent('ats-open-candidate', { detail: c.id }));
    }, 50);
  };

  const onKeyDown = (e) => {
    if (!open || results.length === 0) {
      if (e.key === 'Escape') { setOpen(false); inputRef.current?.blur(); }
      return;
    }
    if (e.key === 'ArrowDown') {
      e.preventDefault();
      setSelected(s => Math.min(s + 1, results.length - 1));
    } else if (e.key === 'ArrowUp') {
      e.preventDefault();
      setSelected(s => Math.max(s - 1, 0));
    } else if (e.key === 'Enter') {
      e.preventDefault();
      if (results[selected]) goTo(results[selected]);
    } else if (e.key === 'Escape') {
      setOpen(false);
      inputRef.current?.blur();
    }
  };

  const showDropdown = open && query.trim().length >= 2;
  const inputRect = showDropdown && wrapRef.current
    ? wrapRef.current.getBoundingClientRect() : null;

  return (
    <div className="ats-search" ref={wrapRef}>
      <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.7">
        <circle cx="11" cy="11" r="7" /><path d="m20 20-3.5-3.5" strokeLinecap="round"/>
      </svg>
      <input
        ref={inputRef}
        placeholder="Search candidates…"
        value={query}
        onChange={onChange}
        onKeyDown={onKeyDown}
        onFocus={() => { if (query.trim().length >= 2) setOpen(true); }}
      />
      {!open && <kbd>⌘K</kbd>}

      {showDropdown && inputRect && ReactDOM.createPortal(
        <div
          className="ats-search-dropdown"
          style={{
            position: 'fixed',
            top: inputRect.bottom + 6,
            left: inputRect.left,
            width: Math.max(inputRect.width, 400),
          }}
        >
          {results.length === 0 ? (
            <div className="ats-search-empty">No candidates found</div>
          ) : (
            results.map((c, i) => (
              <button
                key={c.id}
                className={"ats-search-result" + (i === selected ? " is-selected" : "")}
                onClick={() => goTo(c)}
                onMouseEnter={() => setSelected(i)}
              >
                <Avatar name={c.name} src={c.avatar} size={28} />
                <div className="ats-search-result__info">
                  <div className="ats-search-result__name">{c.name}{c.duplicateIds && c.duplicateIds.length > 0 && <span className="ats-card__dup" style={{ marginLeft: 4, display: "inline-flex" }} title="Duplicate"><svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.2" strokeLinecap="round" strokeLinejoin="round"><path d="M10.29 3.86L1.82 18a2 2 0 001.71 3h16.94a2 2 0 001.71-3L13.71 3.86a2 2 0 00-3.42 0z"/><line x1="12" y1="9" x2="12" y2="13"/><line x1="12" y1="17" x2="12.01" y2="17"/></svg></span>}</div>
                  <div className="ats-search-result__meta">
                    {c.title !== "N/A" && c.company !== "N/A"
                      ? c.title + " · " + c.company
                      : c.title !== "N/A" ? c.title
                      : c.company !== "N/A" ? c.company
                      : c.email}
                    {c.jobTitle ? " — " + c.jobTitle : ""}
                  </div>
                </div>
                <span className="ats-search-result__stage">{(STAGE_BY_ID[c.stage] || {}).short || c.stage}</span>
              </button>
            ))
          )}
        </div>,
        portalRef.current
      )}
    </div>
  );
}

// =================== Top nav =====================
function TopNav({ activeTab = "Pipeline" }) {
  const viewer = useViewer();
  const isAdmin = viewer && viewer.role === "admin";
  const tabs = isAdmin
    ? ["My candidates", "Pipeline", "Permissions"]
    : ["My candidates", "Pipeline"];
  const resolvedActive =
    (activeTab === "Candidates" || activeTab === "All candidates")
      ? "Pipeline"
      : activeTab;
  const handleTabClick = (t) => {
    if (typeof window.__atsNavigate === "function") {
      if (t === "Pipeline") window.__atsNavigate("pipeline");
      else if (t === "My candidates") window.__atsNavigate("my-candidates");
      else if (t === "Permissions") window.__atsNavigate("settings");
    }
  };
  return (
    <header className="ats-topnav">
      <div className="ats-topnav__left">
        <div className="ats-brand">
          <Mark size={20} color="var(--nl-ink)" />
          <span className="ats-brand__word">Nuance</span>
        </div>
        <nav className="ats-tabs">
          {tabs.map(t => (
            <button key={t}
                    className={"ats-tab" + (t === resolvedActive ? " is-active" : "")}
                    onClick={() => handleTabClick(t)}>
              {t}
            </button>
          ))}
        </nav>
      </div>
      <div className="ats-topnav__right">
        <SearchBar />
        <ViewerAvatarMenu />
      </div>
    </header>
  );
}

// =================== Sidebar (jobs nav) ==========
function ViewIcon({ id }) {
  const stroke = { fill: "none", stroke: "currentColor", strokeWidth: 1.6, strokeLinecap: "round", strokeLinejoin: "round" };
  switch (id) {
    case "board": // kanban columns
      return (
        <svg viewBox="0 0 16 16" {...stroke}>
          <rect x="2" y="3" width="3.2" height="10" rx="0.8" />
          <rect x="6.4" y="3" width="3.2" height="6.5" rx="0.8" />
          <rect x="10.8" y="3" width="3.2" height="8.2" rx="0.8" />
        </svg>
      );
    case "table": // rows
      return (
        <svg viewBox="0 0 16 16" {...stroke}>
          <rect x="2" y="3" width="12" height="10" rx="1.2" />
          <path d="M2 6.6h12M2 9.7h12" />
        </svg>
      );
    case "calendar":
      return (
        <svg viewBox="0 0 16 16" {...stroke}>
          <rect x="2" y="3.4" width="12" height="10" rx="1.4" />
          <path d="M2 6.6h12M5.5 2.2v2.4M10.5 2.2v2.4" />
        </svg>
      );
    case "stalled": // clock
      return (
        <svg viewBox="0 0 16 16" {...stroke}>
          <circle cx="8" cy="8" r="5.6" />
          <path d="M8 5v3l2 1.4" />
        </svg>
      );
    case "referrals": // sparkle / source
      return (
        <svg viewBox="0 0 16 16" {...stroke}>
          <path d="M8 2.4l1.5 3.6L13 7.5l-3.5 1.5L8 12.6 6.5 9 3 7.5l3.5-1.5z" />
        </svg>
      );
    case "highrating": // star
      return (
        <svg viewBox="0 0 16 16" fill="currentColor" stroke="none">
          <path d="M8 1.8 L 9.85 5.6 L 14 6.2 L 11 9.05 L 11.85 13.2 L 8 11.1 L 4.15 13.2 L 5 9.05 L 2 6.2 L 6.15 5.6 Z" />
        </svg>
      );
    default:
      return null;
  }
}

const PIPELINE_VIEWS = [
  { id: "board",     label: "Board view",    color: "var(--nl-ink)" },
  { id: "table",     label: "Table view",    color: "var(--nl-ink)" },
  { id: "calendar",  label: "Calendar view", color: "var(--nl-blue)" },
];

function SavedViewRow({ saved, isActive, onSelect, onRename, onDelete }) {
  const [editing, setEditing] = useState(false);
  const [name, setName] = useState(saved.name);
  const [menuOpen, setMenuOpen] = useState(false);
  const inputRef = useRef(null);
  const menuRef = useRef(null);

  useEffect(() => { setName(saved.name); }, [saved.name]);

  useEffect(() => {
    if (editing && inputRef.current) {
      inputRef.current.focus();
      inputRef.current.select();
    }
  }, [editing]);

  useEffect(() => {
    if (!menuOpen) return;
    const onDoc = (e) => {
      if (menuRef.current && !menuRef.current.contains(e.target)) setMenuOpen(false);
    };
    document.addEventListener("mousedown", onDoc);
    return () => document.removeEventListener("mousedown", onDoc);
  }, [menuOpen]);

  const commit = () => {
    const trimmed = name.trim();
    if (!trimmed) {
      setName(saved.name);
      setEditing(false);
      return;
    }
    if (trimmed !== saved.name) onRename(trimmed);
    setEditing(false);
  };

  return (
    <div className={"ats-side-item ats-saved-view-row" + (isActive ? " is-active" : "")}>
      <span className="ats-side-item__icon">
        <svg viewBox="0 0 16 16" width="14" height="14" fill="none" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round" strokeLinejoin="round">
          <path d="M3.5 2.5h7L13 5v8.5a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1v-10a1 1 0 0 1 .5-.5z" />
          <path d="M10.5 2.5V5H13" />
        </svg>
      </span>
      {editing ? (
        <input
          ref={inputRef}
          className="ats-saved-view-row__input"
          value={name}
          onChange={(e) => setName(e.target.value)}
          onBlur={commit}
          onKeyDown={(e) => {
            if (e.key === "Enter") commit();
            else if (e.key === "Escape") { setName(saved.name); setEditing(false); }
          }}
          maxLength={48}
        />
      ) : (
        <button
          className="ats-saved-view-row__label"
          onClick={onSelect}
          onDoubleClick={() => setEditing(true)}
          title="Double-click to rename"
        >{saved.name}</button>
      )}
      <span className="ats-saved-view-row__menu-wrap" ref={menuRef}>
        <button
          className="ats-mini-btn ats-saved-view-row__menu-btn"
          onClick={(e) => { e.stopPropagation(); setMenuOpen(o => !o); }}
          title="More"
        >⋯</button>
        {menuOpen && (
          <div className="ats-saved-view-row__menu">
            <button
              className="ats-saved-view-row__menu-item"
              onClick={() => { setMenuOpen(false); setEditing(true); }}
            >Rename</button>
            <button
              className="ats-saved-view-row__menu-item ats-saved-view-row__menu-item--danger"
              onClick={() => { setMenuOpen(false); onDelete(); }}
            >Remove from sidebar</button>
          </div>
        )}
      </span>
    </div>
  );
}

function timeAgo(iso) {
  const s = Math.floor((Date.now() - new Date(iso).getTime()) / 1000);
  if (s < 60) return "just now";
  const m = Math.floor(s / 60);
  if (m < 60) return m + "m ago";
  const h = Math.floor(m / 60);
  if (h < 24) return h + "h ago";
  const d = Math.floor(h / 24);
  return d + "d ago";
}

function JobSidebar({ activeJobId, onSelect, view, onViewSelect, savedViews, activeSavedViewId, onSavedViewRename, onSavedViewDelete }) {
  const viewer = useViewer();
  const isAdmin = viewer && viewer.role === "admin";
  const [jobTick, setJobTick] = useState(0);
  useEffect(() => {
    const h = () => setJobTick(t => t + 1);
    window.addEventListener("jobs-updated", h);
    return () => window.removeEventListener("jobs-updated", h);
  }, []);
  const openJobs = JOBS.filter(j => j.status === "open" || j.status === "active");
  const inactiveJobs = JOBS.filter(j => j.status !== "open" && j.status !== "active");
  const [showInactive, setShowInactive] = useState(false);
  const [newJobFlash, setNewJobFlash] = useState(false);
  const [dropTarget, setDropTarget] = useState(null);
  const [intStatus, setIntStatus] = useState(null);
  const [tick, setTick] = useState(0);
  const [retrying, setRetrying] = useState(false);
  const loadStatus = () => fetchIntegrationStatus().then(d => { if (d) setIntStatus(d); });
  useEffect(() => {
    loadStatus();
    const iv = setInterval(() => { loadStatus(); setTick(t => t + 1); }, 30000);
    return () => clearInterval(iv);
  }, []);
  // Local active state — always tracked so clicks give visual feedback
  // even when the parent screen doesn't (yet) re-render on selection.
  const [localActive, setLocalActive] = useState(activeJobId);
  const effectiveActive = localActive !== undefined ? localActive : activeJobId;
  const handleSelect = (id) => {
    setLocalActive(id);
    if (onSelect) onSelect(id);
  };

  const filledLabel = (j) => {
    if (j.status === "filled") return j.filledBy;
    if (j.status === "paused") return j.pauseReason || "Paused";
    return "Closed";
  };

  return (
    <aside className="ats-sidebar">
      {/* Views — Notion-style view tabs, vertical */}
      {onViewSelect && (
        <div className="ats-sidebar__section">
          <div className="ats-sidebar__heading">
            <span className="nl-eyebrow">Views</span>
          </div>
          {PIPELINE_VIEWS.map(v => (
            <button
              key={v.id}
              className={"ats-side-item" + ((v.savedView ? activeJobId === v.id : view === v.id && !activeSavedViewId) ? " is-active" : "")}
              onClick={() => v.savedView ? (onSelect && onSelect(v.id)) : onViewSelect(v.id)}
            >
              <span className="ats-side-item__icon" style={{ color: v.color }}>
                <ViewIcon id={v.id} />
              </span>
              <span className="ats-side-item__label">{v.label}</span>
            </button>
          ))}

          {savedViews && savedViews.length > 0 && (
            <div className="ats-saved-views">
              <div className="ats-saved-views__heading">
                <span className="nl-eyebrow">Saved</span>
              </div>
              {savedViews.map(sv => (
                <SavedViewRow
                  key={sv.id}
                  saved={sv}
                  isActive={activeSavedViewId === sv.id}
                  onSelect={() => onSelect && onSelect(sv.id)}
                  onRename={(name) => onSavedViewRename && onSavedViewRename(sv.id, name)}
                  onDelete={() => onSavedViewDelete && onSavedViewDelete(sv.id)}
                />
              ))}
            </div>
          )}
        </div>
      )}

      {onViewSelect && <div className="ats-sidebar__rule" aria-hidden="true" />}

      {/* Top — global views */}
      <div className="ats-sidebar__section">
        <div className="ats-sidebar__heading">
          <span className="nl-eyebrow">Roles</span>
          <button
            className="ats-mini-btn"
            title={newJobFlash ? "Job-post draft created" : "New job"}
            onClick={() => { setNewJobFlash(true); setTimeout(() => setNewJobFlash(false), 1600); }}
          >{newJobFlash ? "✓" : "＋"}</button>
        </div>
        <button
          className={"ats-side-item" + (effectiveActive === "__all__" ? " is-active" : "")}
          onClick={() => handleSelect("__all__")}
        >
          <span className="ats-side-item__dot" style={{ background: "var(--nl-ink)" }} />
          <span className="ats-side-item__label">All roles</span>
          <span className="ats-side-item__count">{JOBS.length}</span>
        </button>
        <button
          className={"ats-side-item" + (!effectiveActive ? " is-active" : "")}
          onClick={() => handleSelect(null)}
        >
          <span className="ats-side-item__dot" style={{ background: "var(--nl-blue)" }} />
          <span className="ats-side-item__label">Open roles</span>
          <span className="ats-side-item__count">{openJobs.length}</span>
        </button>
      </div>

      {/* Open roles list */}
      <div className="ats-sidebar__section"
        onDragOver={isAdmin ? (e) => { e.preventDefault(); setDropTarget("open"); } : undefined}
        onDragLeave={isAdmin ? () => setDropTarget(null) : undefined}
        onDrop={isAdmin ? (e) => {
          e.preventDefault();
          const jobId = e.dataTransfer.getData("text/job-id");
          if (jobId) { updateJobStatus(jobId, "open"); }
          setDropTarget(null);
        } : undefined}
        style={dropTarget === "open" ? { background: "rgba(117,149,213,0.08)", borderRadius: 8 } : undefined}
      >
        <div className="ats-sidebar__heading">
          <span className="nl-eyebrow">Open · {openJobs.length}</span>
        </div>
        {openJobs.map(j => {
          const count = candidatesByJob(j.id).length;
          const active = effectiveActive === j.id;
          return (
            <button
              key={j.id}
              className={"ats-side-item" + (active ? " is-active" : "")}
              onClick={() => handleSelect(j.id)}
              draggable={isAdmin}
              onDragStart={isAdmin ? (e) => { e.dataTransfer.setData("text/job-id", j.id); e.dataTransfer.effectAllowed = "move"; } : undefined}
            >
              <span
                className="ats-side-item__dot"
                style={{
                  background:
                    j.priority === "P0" ? "var(--nl-amber)" :
                    j.priority === "P1" ? "var(--nl-blue)" :
                    "var(--nl-sage)"
                }}
              />
              <span className="ats-side-item__label">{j.title}</span>
              <span className="ats-side-item__count">{count}</span>
            </button>
          );
        })}
      </div>

      {/* Inactive roles — collapsible */}
      <div className="ats-sidebar__section"
        onDragOver={isAdmin ? (e) => { e.preventDefault(); setDropTarget("inactive"); if (!showInactive) setShowInactive(true); } : undefined}
        onDragLeave={isAdmin ? () => setDropTarget(null) : undefined}
        onDrop={isAdmin ? (e) => {
          e.preventDefault();
          const jobId = e.dataTransfer.getData("text/job-id");
          if (jobId) { updateJobStatus(jobId, "closed"); }
          setDropTarget(null);
        } : undefined}
        style={dropTarget === "inactive" ? { background: "rgba(29,49,64,0.06)", borderRadius: 8 } : undefined}
      >
        <button
          className="ats-sidebar__heading ats-sidebar__heading--toggle"
          onClick={() => setShowInactive(s => !s)}
        >
          <span className="nl-eyebrow">
            <span className="ats-sidebar__caret">{showInactive ? "▾" : "▸"}</span>
            Inactive · {inactiveJobs.length}
          </span>
        </button>
        {showInactive && inactiveJobs.map(j => {
          const active = effectiveActive === j.id;
          return (
            <button
              key={j.id}
              className={"ats-side-item ats-side-item--muted" + (active ? " is-active" : "")}
              onClick={() => handleSelect(j.id)}
              draggable={isAdmin}
              onDragStart={isAdmin ? (e) => { e.dataTransfer.setData("text/job-id", j.id); e.dataTransfer.effectAllowed = "move"; } : undefined}
              title={j.status === "filled" ? `Filled · ${j.filledBy}` : j.status === "paused" ? "Paused" : "Closed"}
            >
              <span
                className="ats-side-item__dot"
                style={{
                  background:
                    j.status === "filled" ? "var(--nl-sage)" :
                    j.status === "paused" ? "var(--fg-4)" : "var(--fg-4)"
                }}
              />
              <span className="ats-side-item__label">{j.title}</span>
              <span className="ats-side-item__inactive-tag" data-status={j.status}>
                {j.status === "filled" ? "✓" : j.status === "paused" ? "‖" : "·"}
              </span>
            </button>
          );
        })}
      </div>

      <div className="ats-sidebar__footer" key={tick}>
        {(() => {
          const gh = intStatus?.greenhouse;
          const sl = intStatus?.slack;
          const pf = intStatus?.paraform;
          const gm = intStatus?.gmail;
          const ghTs = gh && [gh.last_checked_at, gh.last_outbound_sync_at, gh.last_webhook_at].filter(Boolean).sort().pop();
          const ghOk = gh?.connected && ghTs;
          const slOk = sl?.connected && sl?.last_event_at;
          const pfOk = pf?.connected && pf?.last_candidate_at;
          const gmTs = gm && (gm.last_checked_at || gm.last_email_at);
          const gmOk = gm?.connected && gmTs;
          const retryFailed = () => {
            setRetrying(true);
            fetch(API_BASE_URL + "/greenhouse/reset-failed", { method: "POST" })
              .then(() => fetch(API_BASE_URL + "/greenhouse/sync-pending", { method: "POST" }))
              .then(() => loadStatus())
              .finally(() => setRetrying(false));
          };
          return (
            <>
              <div className="ats-integration-row">
                <span className={"ats-integration-icon ats-integration-icon--" + (ghOk ? "ok" : "off")}>{ghOk ? "✓" : "✗"}</span>
                Greenhouse <span className="ats-integration-meta">
                  {ghOk ? timeAgo(ghTs) : (gh?.connected ? "No events yet" : "Not connected")}
                </span>
                {gh?.failed_sync_count > 0 && (
                  <button className={"ats-integration-badge" + (retrying ? " is-spinning" : "")} onClick={retryFailed} disabled={retrying} title="Retry failed syncs">
                    {retrying ? "↻" : gh.failed_sync_count + " ↻"}
                  </button>
                )}
              </div>
              <div className="ats-integration-row">
                <span className={"ats-integration-icon ats-integration-icon--" + (pfOk ? "ok" : "off")}>{pfOk ? "✓" : "✗"}</span>
                Paraform <span className="ats-integration-meta">
                  {pfOk ? timeAgo(pf.last_candidate_at) : "No candidates yet"}
                </span>
              </div>
              <div className="ats-integration-row">
                <span className={"ats-integration-icon ats-integration-icon--" + (slOk ? "ok" : "off")}>{slOk ? "✓" : "✗"}</span>
                Slack <span className="ats-integration-meta">
                  {slOk ? timeAgo(sl.last_event_at) : (sl?.connected ? "No events yet" : "Not connected")}
                </span>
              </div>
              <div className="ats-integration-row">
                <span className={"ats-integration-icon ats-integration-icon--" + (gmOk ? "ok" : "off")}>{gmOk ? "✓" : "✗"}</span>
                Gmail <span className="ats-integration-meta">
                  {gmOk ? timeAgo(gmTs) : (gm?.connected ? "No emails yet" : "Not connected")}
                </span>
              </div>
            </>
          );
        })()}
      </div>
    </aside>
  );
}

Object.assign(window, {
  Mark, Avatar, InitialDisc, StageChip, SourcePill, SearchBar, TopNav, JobSidebar
});
