import React, { useState, useEffect, useCallback, useRef, useMemo } from 'react';
import ReactDOM from 'react-dom';
import { RefreshCw, Loader, AlertCircle, PieChart, ChevronUp, ChevronDown, ChevronsUpDown, Settings2, GripVertical, Eye, EyeOff, Filter, Download, RotateCcw, Trash2, X, ListTodo, Upload, FileText, Check, AlertTriangle, CornerUpRight, Edit3, Square, CheckSquare, MinusSquare, Search, Database, Plus } from 'lucide-react';
import * as XLSX from 'xlsx';
import { useAuth } from '../../contexts/AuthContext';
import IvantiCountsChart from './IvantiCountsChart';
import AnomalyBanner from './AnomalyBanner';
import CveTooltip from '../CveTooltip';
import RedirectModal from '../RedirectModal';
import AtlasBadge from '../AtlasBadge';
import ConsolidationModal from '../ConsolidationModal';
import AtlasSlideOutPanel from '../AtlasSlideOutPanel';
import AtlasIcon from '../AtlasIcon';

const API_BASE = process.env.REACT_APP_API_BASE || 'http://localhost:3001/api';
const STORAGE_KEY = 'steam_findings_columns_v2';

// Sentinel used in filter Sets to represent cells with no value (blank / —)
const EMPTY_SENTINEL = '__EMPTY__';

// ---------------------------------------------------------------------------
// Column definitions — source of truth for labels, sort behaviour, rendering
// ---------------------------------------------------------------------------
const COLUMN_DEFS = {
    findingId:   { label: 'Finding ID',  sortable: true,  filterable: false                  },
    severity:    { label: 'Severity',    sortable: true,  filterable: true                   },
    title:       { label: 'Title',       sortable: true,  filterable: true                   },
    cves:        { label: 'CVEs',        sortable: true,  filterable: true,  multiValue: true },
    hostName:    { label: 'Host',        sortable: true,  filterable: true                   },
    ipAddress:   { label: 'IP Address',  sortable: true,  filterable: true                   },
    dns:         { label: 'DNS',         sortable: true,  filterable: true                   },
    dueDate:     { label: 'Due Date',    sortable: true,  filterable: true                   },
    slaStatus:   { label: 'SLA',         sortable: true,  filterable: true                   },
    buOwnership: { label: 'BU',          sortable: true,  filterable: true                   },
    workflow:    { label: 'Workflow',    sortable: true,  filterable: true                   },
    lastFoundOn: { label: 'Last Found',  sortable: true,  filterable: true                   },
    note:        { label: 'Notes',       sortable: false, filterable: false                  },
};

const DEFAULT_COLUMN_ORDER = [
    { key: 'findingId',   visible: true },
    { key: 'severity',    visible: true },
    { key: 'title',       visible: true },
    { key: 'cves',        visible: true },
    { key: 'hostName',    visible: true },
    { key: 'ipAddress',   visible: true },
    { key: 'dns',         visible: true },
    { key: 'dueDate',     visible: true },
    { key: 'slaStatus',   visible: true },
    { key: 'buOwnership', visible: true },
    { key: 'workflow',    visible: true },
    { key: 'lastFoundOn', visible: true },
    { key: 'note',        visible: true },
];

// ---------------------------------------------------------------------------
// Persist / load column config
// ---------------------------------------------------------------------------
function loadColumnOrder() {
    try {
        const saved = JSON.parse(localStorage.getItem(STORAGE_KEY) || 'null');
        if (saved && Array.isArray(saved)) {
            const savedKeys = new Set(saved.map((c) => c.key));
            const merged = saved.filter((c) => COLUMN_DEFS[c.key]);
            DEFAULT_COLUMN_ORDER.forEach((d) => {
                if (!savedKeys.has(d.key)) merged.push({ ...d });
            });
            return merged;
        }
    } catch { /* ignore */ }
    return DEFAULT_COLUMN_ORDER.map((c) => ({ ...c }));
}

function saveColumnOrder(order) {
    try { localStorage.setItem(STORAGE_KEY, JSON.stringify(order)); } catch { /* ignore */ }
}

// ---------------------------------------------------------------------------
// Persist / load hidden row IDs (row visibility feature)
// ---------------------------------------------------------------------------
const HIDDEN_ROWS_KEY = 'steam_findings_hidden_rows';

function loadHiddenRows() {
    try {
        const saved = JSON.parse(localStorage.getItem(HIDDEN_ROWS_KEY) || 'null');
        if (saved && Array.isArray(saved)) return new Set(saved);
    } catch { /* corrupted — treat as empty */ }
    return new Set();
}

function saveHiddenRows(hiddenSet) {
    try { localStorage.setItem(HIDDEN_ROWS_KEY, JSON.stringify([...hiddenSet])); } catch { /* ignore */ }
}

// ---------------------------------------------------------------------------
// Sort accessor by column key
// ---------------------------------------------------------------------------
function getVal(finding, key) {
    switch (key) {
        case 'findingId':   return finding.id ?? '';
        case 'severity':    return finding.severity ?? 0;
        case 'title':       return finding.title ?? '';
        case 'hostName':    return finding.overrides?.hostName || finding.hostName || '';
        case 'ipAddress':   return finding.ipAddress ?? '';
        case 'dns':         return finding.overrides?.dns || finding.dns || '';
        case 'dueDate':     return finding.dueDate ?? '';
        case 'slaStatus':   return finding.slaStatus ?? '';
        case 'cves':        return (finding.cves || []).length; // sort by CVE count
        case 'buOwnership': return finding.buOwnership ?? '';
        case 'workflow':    return finding.workflow?.id ?? '';
        case 'lastFoundOn': return finding.lastFoundOn ?? '';
        case 'note':        return finding.note ?? '';
        default:            return '';
    }
}

// ---------------------------------------------------------------------------
// Filter accessor — severity → vrrGroup label; cves handled as multi-value
// ---------------------------------------------------------------------------
function getFilterVal(finding, key) {
    if (key === 'severity') return finding.vrrGroup || '';
    if (key === 'cves')     return (finding.cves || []).join(','); // not used directly; see multiValue logic
    if (key === 'workflow') return finding.workflow?.id || '';
    return String(getVal(finding, key) ?? '');
}

// ---------------------------------------------------------------------------
// Export value accessor — plain text representation for CSV/XLSX
// ---------------------------------------------------------------------------
function getExportVal(finding, key) {
    switch (key) {
        case 'findingId':   return finding.id ?? '';
        case 'severity':    return finding.vrrGroup ? `${finding.severity?.toFixed(2)} ${finding.vrrGroup}` : String(finding.severity ?? '');
        case 'title':       return finding.title ?? '';
        case 'cves':        return (finding.cves || []).join(', ');
        case 'hostName':    return finding.overrides?.hostName || finding.hostName || '';
        case 'ipAddress':   return finding.ipAddress ?? '';
        case 'dns':         return finding.overrides?.dns || finding.dns || '';
        case 'dueDate':     return finding.dueDate ?? '';
        case 'slaStatus':   return finding.slaStatus ?? '';
        case 'buOwnership': return finding.buOwnership ?? '';
        case 'workflow':    return finding.workflow ? `${finding.workflow.id} (${finding.workflow.state})` : '';
        case 'lastFoundOn': return finding.lastFoundOn ?? '';
        case 'note':        return finding.note ?? '';
        default:            return '';
    }
}

// ---------------------------------------------------------------------------
// Action coverage classification — used by chart and filter
// ---------------------------------------------------------------------------
const EXC_PATTERN = /EXC-\d+/i;

function classifyFinding(finding) {
    if (finding.workflow != null)           return 'fp';
    if (EXC_PATTERN.test(finding.note || '')) return 'archer';
    return 'pending';
}

// ---------------------------------------------------------------------------
// Style helpers
// ---------------------------------------------------------------------------
function severityColor(vrrGroup) {
    switch ((vrrGroup || '').toUpperCase()) {
        case 'CRITICAL': return { bg: 'rgba(239,68,68,0.15)',   border: '#EF4444', text: '#EF4444' };
        case 'HIGH':     return { bg: 'rgba(245,158,11,0.15)',  border: '#F59E0B', text: '#F59E0B' };
        case 'MEDIUM':   return { bg: 'rgba(234,179,8,0.15)',   border: '#EAB308', text: '#EAB308' };
        default:         return { bg: 'rgba(100,116,139,0.15)', border: '#64748B', text: '#94A3B8' };
    }
}

function slaColor(slaStatus) {
    switch ((slaStatus || '').toUpperCase()) {
        case 'OVERDUE':    return '#EF4444';
        case 'AT_RISK':    return '#F59E0B';
        case 'WITHIN_SLA': return '#10B981';
        default:           return '#64748B';
    }
}

function dueDateColor(dueDate) {
    if (!dueDate) return '#64748B';
    const today = new Date();
    const due   = new Date(dueDate);
    const diffDays = Math.ceil((due - today) / (1000 * 60 * 60 * 24));
    if (diffDays < 0)   return '#EF4444';
    if (diffDays <= 30) return '#F59E0B';
    return '#94A3B8';
}

function workflowStyle(state) {
    // Colors reflect action urgency — all findings here are Open, so Approved won't appear.
    switch ((state || '').toLowerCase()) {
        case 'expired':     return { bg: 'rgba(239,68,68,0.12)',   border: 'rgba(239,68,68,0.4)',    text: '#EF4444' }; // overdue — renew FP
        case 'rejected':    return { bg: 'rgba(239,68,68,0.12)',   border: 'rgba(239,68,68,0.4)',    text: '#EF4444' }; // denied — must remediate
        case 'reworked':    return { bg: 'rgba(245,158,11,0.12)',  border: 'rgba(245,158,11,0.4)',   text: '#F59E0B' }; // challenged — resubmit FP
        case 'actionable':  return { bg: 'rgba(245,158,11,0.12)',  border: 'rgba(245,158,11,0.4)',   text: '#F59E0B' }; // needs action
        case 'requested':   return { bg: 'rgba(14,165,233,0.12)',  border: 'rgba(14,165,233,0.35)',  text: '#0EA5E9' }; // in flight — awaiting approval
        default:            return { bg: 'rgba(100,116,139,0.08)', border: 'rgba(100,116,139,0.2)',  text: '#64748B' }; // unknown state
    }
}

function lifecycleStatusBadge(status) {
    switch ((status || '').toLowerCase()) {
        case 'submitted':
        case 'resubmitted':
            return { bg: 'rgba(14,165,233,0.12)', border: 'rgba(14,165,233,0.4)', text: '#0EA5E9' };
        case 'approved':
            return { bg: 'rgba(16,185,129,0.12)', border: 'rgba(16,185,129,0.4)', text: '#10B981' };
        case 'rejected':
            return { bg: 'rgba(239,68,68,0.12)', border: 'rgba(239,68,68,0.4)', text: '#EF4444' };
        case 'rework':
            return { bg: 'rgba(245,158,11,0.12)', border: 'rgba(245,158,11,0.4)', text: '#F59E0B' };
        default:
            return { bg: 'rgba(100,116,139,0.08)', border: 'rgba(100,116,139,0.2)', text: '#64748B' };
    }
}

// ---------------------------------------------------------------------------
// SVG Donut Chart — Open vs Closed findings
// ---------------------------------------------------------------------------
function polarToCartesian(cx, cy, r, angleDeg) {
    const rad = ((angleDeg - 90) * Math.PI) / 180;
    return { x: cx + r * Math.cos(rad), y: cy + r * Math.sin(rad) };
}

function donutArcPath(cx, cy, outerR, innerR, startDeg, endDeg) {
    // Full circle must be split into two arcs (SVG can't render a 360° arc)
    if (Math.abs(endDeg - startDeg) >= 359.9) {
        const mid = startDeg + 180;
        return donutArcPath(cx, cy, outerR, innerR, startDeg, mid) + ' ' +
               donutArcPath(cx, cy, outerR, innerR, mid, endDeg);
    }
    const largeArc = endDeg - startDeg > 180 ? 1 : 0;
    const s  = polarToCartesian(cx, cy, outerR, startDeg);
    const e  = polarToCartesian(cx, cy, outerR, endDeg);
    const si = polarToCartesian(cx, cy, innerR, endDeg);
    const ei = polarToCartesian(cx, cy, innerR, startDeg);
    return [
        `M ${s.x.toFixed(2)} ${s.y.toFixed(2)}`,
        `A ${outerR} ${outerR} 0 ${largeArc} 1 ${e.x.toFixed(2)} ${e.y.toFixed(2)}`,
        `L ${si.x.toFixed(2)} ${si.y.toFixed(2)}`,
        `A ${innerR} ${innerR} 0 ${largeArc} 0 ${ei.x.toFixed(2)} ${ei.y.toFixed(2)}`,
        'Z',
    ].join(' ');
}

function StatusDonut({ open, closed, loading }) {
    const SIZE = 180;
    const CX = SIZE / 2;
    const CY = SIZE / 2;
    const OUTER = 72;
    const INNER = 48;

    if (loading) {
        return (
            <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', height: `${SIZE}px` }}>
                <Loader style={{ width: '24px', height: '24px', color: '#0EA5E9', animation: 'spin 1s linear infinite' }} />
            </div>
        );
    }

    const total = open + closed;
    if (total === 0) {
        return (
            <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', height: `${SIZE}px` }}>
                <p style={{ fontFamily: 'monospace', fontSize: '0.75rem', color: '#475569' }}>No data — click Sync to load</p>
            </div>
        );
    }

    const openDeg = (open / total) * 360;
    const segments = [
        { label: 'Open',   count: open,   color: '#0EA5E9', start: 0,       end: openDeg },
        { label: 'Closed', count: closed, color: '#475569', start: openDeg, end: 360     },
    ].filter((s) => s.count > 0);

    return (
        <div style={{ display: 'flex', alignItems: 'center', gap: '2rem' }}>
            <svg width={SIZE} height={SIZE} style={{ flexShrink: 0 }}>
                {/* Gap ring behind slices */}
                <circle cx={CX} cy={CY} r={OUTER + 1} fill="none" stroke="rgba(10,18,32,0.8)" strokeWidth="2" />
                {segments.map((seg) => (
                    <path
                        key={seg.label}
                        d={donutArcPath(CX, CY, OUTER, INNER, seg.start, seg.end)}
                        fill={seg.color}
                        opacity={0.88}
                    />
                ))}
                {/* Center total */}
                <text x={CX} y={CY - 10} textAnchor="middle" style={{ fontFamily: 'monospace', fontSize: '22px', fontWeight: '700', fill: '#E2E8F0' }}>
                    {total.toLocaleString()}
                </text>
                <text x={CX} y={CY + 10} textAnchor="middle" style={{ fontFamily: 'monospace', fontSize: '8.5px', fontWeight: '600', fill: '#475569', letterSpacing: '0.12em' }}>
                    TOTAL
                </text>
            </svg>

            {/* Legend */}
            <div style={{ display: 'flex', flexDirection: 'column', gap: '1rem' }}>
                {segments.map((seg) => (
                    <div key={seg.label} style={{ display: 'flex', alignItems: 'center', gap: '0.625rem' }}>
                        <div style={{ width: '10px', height: '10px', borderRadius: '2px', background: seg.color, flexShrink: 0 }} />
                        <div>
                            <div style={{ fontFamily: 'monospace', fontSize: '0.68rem', fontWeight: '600', color: '#64748B', textTransform: 'uppercase', letterSpacing: '0.08em' }}>
                                {seg.label}
                            </div>
                            <div style={{ fontFamily: 'monospace', fontSize: '0.9rem', fontWeight: '700', color: '#E2E8F0', lineHeight: 1.2 }}>
                                {seg.count.toLocaleString()}
                                <span style={{ fontSize: '0.68rem', fontWeight: '400', color: '#64748B', marginLeft: '0.4rem' }}>
                                    ({((seg.count / total) * 100).toFixed(1)}%)
                                </span>
                            </div>
                        </div>
                    </div>
                ))}
            </div>
        </div>
    );
}

// ---------------------------------------------------------------------------
// SVG Donut Chart — Action Coverage (FP Request | Archer Exception | Pending)
// ---------------------------------------------------------------------------
const ACTION_DEFS = [
    { key: 'fp',      label: 'FP Request',      color: '#0EA5E9' },
    { key: 'archer',  label: 'Archer Exception', color: '#F59E0B' },
    { key: 'pending', label: 'Pending',          color: '#EF4444' },
];

function ActionCoverageDonut({ findings, activeSegment, onSegmentClick }) {
    const SIZE = 180;
    const CX = SIZE / 2;
    const CY = SIZE / 2;
    const OUTER = 72;
    const INNER = 48;

    const counts = useMemo(() => {
        const map = { fp: 0, archer: 0, pending: 0 };
        findings.forEach((f) => { map[classifyFinding(f)]++; });
        return map;
    }, [findings]);

    const total = findings.length;

    if (total === 0) {
        return (
            <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', height: `${SIZE}px` }}>
                <p style={{ fontFamily: 'monospace', fontSize: '0.75rem', color: '#475569' }}>No data — click Sync to load</p>
            </div>
        );
    }

    let cursor = 0;
    const segments = ACTION_DEFS.map((def) => {
        const count = counts[def.key];
        const start = cursor;
        const end = count > 0 ? cursor + (count / total) * 360 : cursor;
        if (count > 0) cursor = end;
        return { ...def, count, start, end };
    });

    const hasActive = !!activeSegment;

    return (
        <div style={{ display: 'flex', alignItems: 'center', gap: '2rem' }}>
            <svg width={SIZE} height={SIZE} style={{ flexShrink: 0 }}>
                <circle cx={CX} cy={CY} r={OUTER + 1} fill="none" stroke="rgba(10,18,32,0.8)" strokeWidth="2" />
                {segments.filter((s) => s.count > 0).map((seg) => {
                    const isActive = activeSegment === seg.key;
                    return (
                        <path
                            key={seg.key}
                            d={donutArcPath(CX, CY, OUTER, INNER, seg.start, seg.end)}
                            fill={seg.color}
                            opacity={hasActive ? (isActive ? 1 : 0.25) : 0.88}
                            stroke={isActive ? 'rgba(255,255,255,0.6)' : 'none'}
                            strokeWidth={isActive ? 2 : 0}
                            style={{ cursor: 'pointer', transition: 'opacity 0.2s' }}
                            onClick={() => onSegmentClick(isActive ? null : seg.key)}
                        />
                    );
                })}
                <text x={CX} y={CY - 10} textAnchor="middle" style={{ fontFamily: 'monospace', fontSize: '22px', fontWeight: '700', fill: '#E2E8F0' }}>
                    {total.toLocaleString()}
                </text>
                <text x={CX} y={CY + 10} textAnchor="middle" style={{ fontFamily: 'monospace', fontSize: '8.5px', fontWeight: '600', fill: '#475569', letterSpacing: '0.12em' }}>
                    TOTAL
                </text>
            </svg>

            {/* Legend — always shows all 3 categories */}
            <div style={{ display: 'flex', flexDirection: 'column', gap: '0.6rem' }}>
                {segments.map((seg) => {
                    const isActive = activeSegment === seg.key;
                    const dimmed = hasActive && !isActive;
                    return (
                        <div
                            key={seg.key}
                            onClick={() => onSegmentClick(isActive ? null : seg.key)}
                            style={{ display: 'flex', alignItems: 'center', gap: '0.5rem', cursor: 'pointer', opacity: dimmed ? 0.35 : 1, transition: 'opacity 0.2s' }}
                        >
                            <div style={{ width: '8px', height: '8px', borderRadius: '2px', background: seg.color, flexShrink: 0, outline: isActive ? `2px solid ${seg.color}` : 'none', outlineOffset: '1px' }} />
                            <div>
                                <span style={{ fontFamily: 'monospace', fontSize: '0.68rem', fontWeight: '600', color: '#64748B', textTransform: 'uppercase', letterSpacing: '0.06em' }}>
                                    {seg.label}
                                </span>
                                <span style={{ fontFamily: 'monospace', fontSize: '0.8rem', fontWeight: '700', color: '#E2E8F0', marginLeft: '0.4rem' }}>
                                    {seg.count}
                                </span>
                                <span style={{ fontFamily: 'monospace', fontSize: '0.65rem', color: '#475569', marginLeft: '0.25rem' }}>
                                    ({total > 0 ? ((seg.count / total) * 100).toFixed(0) : 0}%)
                                </span>
                            </div>
                        </div>
                    );
                })}
                {hasActive && (
                    <button
                        onClick={() => onSegmentClick(null)}
                        style={{ marginTop: '0.25rem', background: 'none', border: 'none', fontFamily: 'monospace', fontSize: '0.65rem', color: '#475569', cursor: 'pointer', textAlign: 'left', padding: 0, textDecoration: 'underline' }}
                    >
                        clear filter
                    </button>
                )}
            </div>
        </div>
    );
}

// ---------------------------------------------------------------------------
// SVG Donut Chart — FP Workflow Status distribution
// ---------------------------------------------------------------------------
const FP_WORKFLOW_DEFS = [
    { key: 'Actionable', label: 'Actionable', color: '#F59E0B' },
    { key: 'Requested',  label: 'Requested',  color: '#0EA5E9' },
    { key: 'Reworked',   label: 'Reworked',   color: '#A855F7' },
    { key: 'Approved',   label: 'Approved',   color: '#22C55E' },
    { key: 'Rejected',   label: 'Rejected',   color: '#EF4444' },
    { key: 'Expired',    label: 'Expired',    color: '#64748B' },
    { key: 'Unknown',    label: 'Unknown',    color: '#334155' },
];

function FPWorkflowDonut({ counts, total, centerLabel = 'FP TOTAL' }) {
    const SIZE = 180;
    const CX = SIZE / 2;
    const CY = SIZE / 2;
    const OUTER = 72;
    const INNER = 48;

    if (total === 0) {
        return (
            <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', height: `${SIZE}px` }}>
                <p style={{ fontFamily: 'monospace', fontSize: '0.75rem', color: '#475569' }}>No FP workflows — click Sync to load</p>
            </div>
        );
    }

    let cursor = 0;
    const segments = FP_WORKFLOW_DEFS.map((def) => {
        const count = counts[def.key] || 0;
        const start = cursor;
        const end = count > 0 ? cursor + (count / total) * 360 : cursor;
        if (count > 0) cursor = end;
        return { ...def, count, start, end };
    }).filter(s => s.count > 0);

    return (
        <div style={{ display: 'flex', alignItems: 'center', gap: '2rem' }}>
            <svg width={SIZE} height={SIZE} style={{ flexShrink: 0 }}>
                <circle cx={CX} cy={CY} r={OUTER + 1} fill="none" stroke="rgba(10,18,32,0.8)" strokeWidth="2" />
                {segments.map((seg) => (
                    <path
                        key={seg.key}
                        d={donutArcPath(CX, CY, OUTER, INNER, seg.start, seg.end)}
                        fill={seg.color}
                        opacity={0.88}
                    />
                ))}
                <text x={CX} y={CY - 10} textAnchor="middle" style={{ fontFamily: 'monospace', fontSize: '22px', fontWeight: '700', fill: '#E2E8F0' }}>
                    {total.toLocaleString()}
                </text>
                <text x={CX} y={CY + 10} textAnchor="middle" style={{ fontFamily: 'monospace', fontSize: '8.5px', fontWeight: '600', fill: '#475569', letterSpacing: '0.12em' }}>
                    {centerLabel}
                </text>
            </svg>

            {/* Legend */}
            <div style={{ display: 'flex', flexDirection: 'column', gap: '0.6rem' }}>
                {segments.map((seg) => (
                    <div key={seg.key} style={{ display: 'flex', alignItems: 'center', gap: '0.5rem' }}>
                        <div style={{ width: '8px', height: '8px', borderRadius: '2px', background: seg.color, flexShrink: 0 }} />
                        <div>
                            <span style={{ fontFamily: 'monospace', fontSize: '0.68rem', fontWeight: '600', color: '#64748B', textTransform: 'uppercase', letterSpacing: '0.06em' }}>
                                {seg.label}
                            </span>
                            <span style={{ fontFamily: 'monospace', fontSize: '0.8rem', fontWeight: '700', color: '#E2E8F0', marginLeft: '0.4rem' }}>
                                {seg.count}
                            </span>
                            <span style={{ fontFamily: 'monospace', fontSize: '0.65rem', color: '#475569', marginLeft: '0.25rem' }}>
                                ({((seg.count / total) * 100).toFixed(0)}%)
                            </span>
                        </div>
                    </div>
                ))}
            </div>
        </div>
    );
}

// ---------------------------------------------------------------------------
// Atlas Donut Charts — Coverage, Plan Type, Plan Status
// ---------------------------------------------------------------------------
const PLAN_TYPE_DEFS = [
    { key: 'decommission',    label: 'Decommission',    color: '#EF4444' },
    { key: 'remediation',     label: 'Remediation',     color: '#0EA5E9' },
    { key: 'false_positive',  label: 'False Positive',  color: '#A855F7' },
    { key: 'risk_acceptance', label: 'Risk Acceptance',  color: '#F59E0B' },
    { key: 'scan_exclusion',  label: 'Scan Exclusion',  color: '#64748B' },
];

function getStatusColor(status) {
    if (status === 'active') return '#10B981';
    if (status === 'expired') return '#EF4444';
    if (status === 'completed') return '#0EA5E9';
    return '#64748B';
}

function AtlasCoverageDonut({ hostsWithPlans, hostsWithoutPlans, totalHosts }) {
    const SIZE = 180;
    const CX = SIZE / 2;
    const CY = SIZE / 2;
    const OUTER = 72;
    const INNER = 48;

    if (totalHosts === 0) {
        return (
            <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', height: `${SIZE}px` }}>
                <p style={{ fontFamily: 'monospace', fontSize: '0.75rem', color: '#475569' }}>No data — run Atlas Sync</p>
            </div>
        );
    }

    const segments = [
        { label: 'With Plans',    count: hostsWithPlans,    color: '#10B981', start: 0, end: (hostsWithPlans / totalHosts) * 360 },
        { label: 'Without Plans', count: hostsWithoutPlans, color: '#F59E0B', start: (hostsWithPlans / totalHosts) * 360, end: 360 },
    ].filter((s) => s.count > 0);

    return (
        <div style={{ display: 'flex', alignItems: 'center', gap: '2rem' }}>
            <svg width={SIZE} height={SIZE} style={{ flexShrink: 0 }}>
                <circle cx={CX} cy={CY} r={OUTER + 1} fill="none" stroke="rgba(10,18,32,0.8)" strokeWidth="2" />
                {segments.map((seg) => (
                    <path
                        key={seg.label}
                        d={donutArcPath(CX, CY, OUTER, INNER, seg.start, seg.end)}
                        fill={seg.color}
                        opacity={0.88}
                    />
                ))}
                <text x={CX} y={CY - 10} textAnchor="middle" style={{ fontFamily: 'monospace', fontSize: '22px', fontWeight: '700', fill: '#E2E8F0' }}>
                    {totalHosts.toLocaleString()}
                </text>
                <text x={CX} y={CY + 10} textAnchor="middle" style={{ fontFamily: 'monospace', fontSize: '8.5px', fontWeight: '600', fill: '#475569', letterSpacing: '0.12em' }}>
                    HOSTS
                </text>
            </svg>

            {/* Legend */}
            <div style={{ display: 'flex', flexDirection: 'column', gap: '1rem' }}>
                {segments.map((seg) => (
                    <div key={seg.label} style={{ display: 'flex', alignItems: 'center', gap: '0.625rem' }}>
                        <div style={{ width: '10px', height: '10px', borderRadius: '2px', background: seg.color, flexShrink: 0 }} />
                        <div>
                            <div style={{ fontFamily: 'monospace', fontSize: '0.68rem', fontWeight: '600', color: '#64748B', textTransform: 'uppercase', letterSpacing: '0.08em' }}>
                                {seg.label}
                            </div>
                            <div style={{ fontFamily: 'monospace', fontSize: '0.9rem', fontWeight: '700', color: '#E2E8F0', lineHeight: 1.2 }}>
                                {seg.count.toLocaleString()}
                                <span style={{ fontSize: '0.68rem', fontWeight: '400', color: '#64748B', marginLeft: '0.4rem' }}>
                                    ({((seg.count / totalHosts) * 100).toFixed(1)}%)
                                </span>
                            </div>
                        </div>
                    </div>
                ))}
            </div>
        </div>
    );
}

function AtlasPlanTypeDonut({ plansByType, totalPlans }) {
    const SIZE = 180;
    const CX = SIZE / 2;
    const CY = SIZE / 2;
    const OUTER = 72;
    const INNER = 48;

    if (totalPlans === 0) {
        return (
            <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', height: `${SIZE}px` }}>
                <p style={{ fontFamily: 'monospace', fontSize: '0.75rem', color: '#475569' }}>No plans — run Atlas Sync</p>
            </div>
        );
    }

    let cursor = 0;
    const segments = PLAN_TYPE_DEFS.map((def) => {
        const count = plansByType[def.key] || 0;
        const start = cursor;
        const end = count > 0 ? cursor + (count / totalPlans) * 360 : cursor;
        if (count > 0) cursor = end;
        return { ...def, count, start, end };
    }).filter(s => s.count > 0);

    return (
        <div style={{ display: 'flex', alignItems: 'center', gap: '2rem' }}>
            <svg width={SIZE} height={SIZE} style={{ flexShrink: 0 }}>
                <circle cx={CX} cy={CY} r={OUTER + 1} fill="none" stroke="rgba(10,18,32,0.8)" strokeWidth="2" />
                {segments.map((seg) => (
                    <path
                        key={seg.key}
                        d={donutArcPath(CX, CY, OUTER, INNER, seg.start, seg.end)}
                        fill={seg.color}
                        opacity={0.88}
                    />
                ))}
                <text x={CX} y={CY - 10} textAnchor="middle" style={{ fontFamily: 'monospace', fontSize: '22px', fontWeight: '700', fill: '#E2E8F0' }}>
                    {totalPlans.toLocaleString()}
                </text>
                <text x={CX} y={CY + 10} textAnchor="middle" style={{ fontFamily: 'monospace', fontSize: '8.5px', fontWeight: '600', fill: '#475569', letterSpacing: '0.12em' }}>
                    PLANS
                </text>
            </svg>

            {/* Legend */}
            <div style={{ display: 'flex', flexDirection: 'column', gap: '0.6rem' }}>
                {segments.map((seg) => (
                    <div key={seg.key} style={{ display: 'flex', alignItems: 'center', gap: '0.5rem' }}>
                        <div style={{ width: '8px', height: '8px', borderRadius: '2px', background: seg.color, flexShrink: 0 }} />
                        <div>
                            <span style={{ fontFamily: 'monospace', fontSize: '0.68rem', fontWeight: '600', color: '#64748B', textTransform: 'uppercase', letterSpacing: '0.06em' }}>
                                {seg.label}
                            </span>
                            <span style={{ fontFamily: 'monospace', fontSize: '0.8rem', fontWeight: '700', color: '#E2E8F0', marginLeft: '0.4rem' }}>
                                {seg.count}
                            </span>
                            <span style={{ fontFamily: 'monospace', fontSize: '0.65rem', color: '#475569', marginLeft: '0.25rem' }}>
                                ({((seg.count / totalPlans) * 100).toFixed(0)}%)
                            </span>
                        </div>
                    </div>
                ))}
            </div>
        </div>
    );
}

function AtlasPlanStatusDonut({ plansByStatus, totalPlans }) {
    const SIZE = 180;
    const CX = SIZE / 2;
    const CY = SIZE / 2;
    const OUTER = 72;
    const INNER = 48;

    if (totalPlans === 0) {
        return (
            <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', height: `${SIZE}px` }}>
                <p style={{ fontFamily: 'monospace', fontSize: '0.75rem', color: '#475569' }}>No plans — run Atlas Sync</p>
            </div>
        );
    }

    const entries = Object.entries(plansByStatus).filter(([, count]) => count > 0);

    let cursor = 0;
    const segments = entries.map(([status, count]) => {
        const start = cursor;
        const end = cursor + (count / totalPlans) * 360;
        cursor = end;
        return {
            key: status,
            label: status.charAt(0).toUpperCase() + status.slice(1),
            color: getStatusColor(status),
            count,
            start,
            end,
        };
    });

    return (
        <div style={{ display: 'flex', alignItems: 'center', gap: '2rem' }}>
            <svg width={SIZE} height={SIZE} style={{ flexShrink: 0 }}>
                <circle cx={CX} cy={CY} r={OUTER + 1} fill="none" stroke="rgba(10,18,32,0.8)" strokeWidth="2" />
                {segments.map((seg) => (
                    <path
                        key={seg.key}
                        d={donutArcPath(CX, CY, OUTER, INNER, seg.start, seg.end)}
                        fill={seg.color}
                        opacity={0.88}
                    />
                ))}
                <text x={CX} y={CY - 10} textAnchor="middle" style={{ fontFamily: 'monospace', fontSize: '22px', fontWeight: '700', fill: '#E2E8F0' }}>
                    {totalPlans.toLocaleString()}
                </text>
                <text x={CX} y={CY + 10} textAnchor="middle" style={{ fontFamily: 'monospace', fontSize: '8.5px', fontWeight: '600', fill: '#475569', letterSpacing: '0.12em' }}>
                    STATUS
                </text>
            </svg>

            {/* Legend */}
            <div style={{ display: 'flex', flexDirection: 'column', gap: '0.6rem' }}>
                {segments.map((seg) => (
                    <div key={seg.key} style={{ display: 'flex', alignItems: 'center', gap: '0.5rem' }}>
                        <div style={{ width: '8px', height: '8px', borderRadius: '2px', background: seg.color, flexShrink: 0 }} />
                        <div>
                            <span style={{ fontFamily: 'monospace', fontSize: '0.68rem', fontWeight: '600', color: '#64748B', textTransform: 'uppercase', letterSpacing: '0.06em' }}>
                                {seg.label}
                            </span>
                            <span style={{ fontFamily: 'monospace', fontSize: '0.8rem', fontWeight: '700', color: '#E2E8F0', marginLeft: '0.4rem' }}>
                                {seg.count}
                            </span>
                            <span style={{ fontFamily: 'monospace', fontSize: '0.65rem', color: '#475569', marginLeft: '0.25rem' }}>
                                ({((seg.count / totalPlans) * 100).toFixed(0)}%)
                            </span>
                        </div>
                    </div>
                ))}
            </div>
        </div>
    );
}

function SortIcon({ colKey, sort }) {
    if (sort.field !== colKey) return <ChevronsUpDown style={{ width: '11px', height: '11px', opacity: 0.3, marginLeft: '3px', flexShrink: 0 }} />;
    return sort.dir === 'asc'
        ? <ChevronUp   style={{ width: '11px', height: '11px', color: '#0EA5E9', marginLeft: '3px', flexShrink: 0 }} />
        : <ChevronDown style={{ width: '11px', height: '11px', color: '#0EA5E9', marginLeft: '3px', flexShrink: 0 }} />;
}

// ---------------------------------------------------------------------------
// OverrideCell — inline editable hostname/dns with amber dot when overridden
// ---------------------------------------------------------------------------
function OverrideCell({ findingId, field, originalValue, initialOverride, canWrite, suffix }) {
    const effective = initialOverride ?? originalValue ?? '';
    const [value, setValue]           = useState(effective);
    const [isOverridden, setOverridden] = useState(!!initialOverride);
    const [editing, setEditing]       = useState(false);
    const [saving, setSaving]         = useState(false);
    const lastSaved                   = useRef(effective);
    const inputRef                    = useRef(null);

    // Sync when the finding updates (e.g. after a full sync)
    useEffect(() => {
        const eff = initialOverride ?? originalValue ?? '';
        setValue(eff);
        setOverridden(!!initialOverride);
        lastSaved.current = eff;
    }, [initialOverride, originalValue]);

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

    const persist = useCallback(async (newVal) => {
        const trimmed = newVal.trim();
        if (trimmed === lastSaved.current) return;
        setSaving(true);
        try {
            const res = await fetch(`${API_BASE}/ivanti/findings/${findingId}/override`, {
                method: 'PUT',
                credentials: 'include',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify({ field, value: trimmed }),
            });
            if (res.ok) {
                const data = await res.json();
                const cleared = data.value === null;
                const displayed = cleared ? (originalValue ?? '') : trimmed;
                setValue(displayed);
                setOverridden(!cleared);
                lastSaved.current = displayed;
            } else {
                setValue(lastSaved.current); // revert on error
            }
        } catch {
            setValue(lastSaved.current);
        } finally {
            setSaving(false);
        }
    }, [findingId, field, originalValue]);

    const handleBlur = () => { setEditing(false); persist(value); };
    const handleKeyDown = (e) => {
        if (e.key === 'Enter')  { e.target.blur(); }
        if (e.key === 'Escape') { setValue(lastSaved.current); setEditing(false); }
    };
    const handleRevert = (e) => { e.stopPropagation(); setValue(''); persist(''); };

    if (editing) {
        return (
            <td style={{ padding: '0.3rem 0.5rem' }}>
                <input
                    ref={inputRef}
                    value={value}
                    onChange={(e) => setValue(e.target.value)}
                    onBlur={handleBlur}
                    onKeyDown={handleKeyDown}
                    style={{
                        width: '100%', minWidth: '120px',
                        background: 'rgba(14,165,233,0.08)',
                        border: '1px solid rgba(14,165,233,0.4)',
                        borderRadius: '0.25rem',
                        padding: '0.2rem 0.4rem',
                        color: '#E2E8F0', fontFamily: 'monospace', fontSize: '0.72rem',
                        outline: 'none',
                    }}
                />
            </td>
        );
    }

    return (
        <td style={{ padding: '0.45rem 0.75rem', whiteSpace: 'nowrap' }}>
            <span
                onClick={canWrite ? () => setEditing(true) : undefined}
                title={isOverridden ? `Ivanti value: ${originalValue || '—'}\nClick to edit` : canWrite ? 'Click to edit' : undefined}
                style={{
                    display: 'inline-flex', alignItems: 'center', gap: '0.3rem',
                    color: isOverridden ? '#E2E8F0' : '#94A3B8',
                    fontFamily: 'monospace', fontSize: '0.72rem',
                    cursor: canWrite ? 'text' : 'default',
                }}
            >
                {isOverridden && (
                    <span title="Local override active" style={{ width: '5px', height: '5px', borderRadius: '50%', background: '#F59E0B', flexShrink: 0, marginRight: '1px' }} />
                )}
                {value || '—'}
                {saving && <Loader style={{ width: '10px', height: '10px', color: '#475569', animation: 'spin 1s linear infinite', flexShrink: 0 }} />}
                {isOverridden && canWrite && !saving && (
                    <button
                        onClick={handleRevert}
                        title="Revert to Ivanti value"
                        style={{ background: 'none', border: 'none', padding: '0 1px', cursor: 'pointer', color: '#475569', lineHeight: 1, flexShrink: 0, display: 'inline-flex', alignItems: 'center' }}
                    >
                        <RotateCcw style={{ width: '10px', height: '10px' }} />
                    </button>
                )}
            </span>
            {suffix}
        </td>
    );
}

// ---------------------------------------------------------------------------
// NoteCell — inline editable, saves on blur
// ---------------------------------------------------------------------------
function NoteCell({ findingId, initialNote }) {
    const [value, setValue]   = useState(initialNote || '');
    const [saving, setSaving] = useState(false);
    const lastSaved           = useRef(initialNote || '');

    useEffect(() => {
        setValue(initialNote || '');
        lastSaved.current = initialNote || '';
    }, [initialNote]);

    const save = useCallback(async () => {
        if (value === lastSaved.current) return;
        setSaving(true);
        try {
            await fetch(`${API_BASE}/ivanti/findings/${encodeURIComponent(findingId)}/note`, {
                method: 'PUT',
                headers: { 'Content-Type': 'application/json' },
                credentials: 'include',
                body: JSON.stringify({ note: value })
            });
            lastSaved.current = value;
        } catch (e) {
            console.error('Failed to save note:', e);
        } finally {
            setSaving(false);
        }
    }, [findingId, value]);

    return (
        <div style={{ position: 'relative' }}>
            <input
                type="text"
                value={value}
                maxLength={255}
                onChange={(e) => setValue(e.target.value)}
                onBlur={save}
                placeholder="Add note…"
                style={{
                    width: '100%', minWidth: '160px',
                    background: 'rgba(14,165,233,0.05)',
                    border: '1px solid rgba(14,165,233,0.2)',
                    borderRadius: '4px', padding: '4px 8px',
                    color: '#CBD5E1', fontSize: '0.75rem',
                    fontFamily: 'inherit', outline: 'none', boxSizing: 'border-box'
                }}
                onFocus={(e) => { e.target.style.borderColor = 'rgba(14,165,233,0.6)'; e.target.style.background = 'rgba(14,165,233,0.1)'; }}
                onBlurCapture={(e) => { e.target.style.borderColor = 'rgba(14,165,233,0.2)'; e.target.style.background = 'rgba(14,165,233,0.05)'; }}
            />
            {saving && <Loader style={{ width: '10px', height: '10px', position: 'absolute', right: '6px', top: '6px', color: '#0EA5E9' }} />}
        </div>
    );
}

// ---------------------------------------------------------------------------
// ColumnManager — popover with drag-to-reorder and show/hide toggles
// ---------------------------------------------------------------------------
function ColumnManager({ columnOrder, onChange }) {
    const [open, setOpen]       = useState(false);
    const [dragIdx, setDragIdx] = useState(null);
    const [overIdx, setOverIdx] = useState(null);
    const panelRef              = useRef(null);
    const btnRef                = useRef(null);

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

    const toggleVisible = (key) => {
        onChange(columnOrder.map((c) => c.key === key ? { ...c, visible: !c.visible } : c));
    };

    const handleDragStart = (idx) => setDragIdx(idx);
    const handleDragOver  = (e, idx) => { e.preventDefault(); setOverIdx(idx); };
    const handleDrop      = (idx) => {
        if (dragIdx === null || dragIdx === idx) { setDragIdx(null); setOverIdx(null); return; }
        const updated = [...columnOrder];
        const [moved] = updated.splice(dragIdx, 1);
        updated.splice(idx, 0, moved);
        onChange(updated);
        setDragIdx(null);
        setOverIdx(null);
    };

    const visibleCount = columnOrder.filter((c) => c.visible).length;

    return (
        <div style={{ position: 'relative' }}>
            <button
                ref={btnRef}
                onClick={() => setOpen((p) => !p)}
                style={{
                    display: 'flex', alignItems: 'center', gap: '0.375rem',
                    padding: '0.375rem 0.75rem',
                    background: open ? 'rgba(14,165,233,0.15)' : 'rgba(14,165,233,0.07)',
                    border: `1px solid rgba(14,165,233,${open ? '0.5' : '0.25'})`,
                    borderRadius: '0.375rem',
                    color: '#0EA5E9', cursor: 'pointer',
                    fontFamily: 'monospace', fontSize: '0.75rem', fontWeight: '600',
                    textTransform: 'uppercase', letterSpacing: '0.05em'
                }}
            >
                <Settings2 style={{ width: '13px', height: '13px' }} />
                Columns
                <span style={{ fontSize: '0.65rem', opacity: 0.7 }}>({visibleCount}/{columnOrder.length})</span>
            </button>

            {open && (
                <div
                    ref={panelRef}
                    style={{
                        position: 'absolute', top: 'calc(100% + 8px)', right: 0,
                        width: '220px', zIndex: 100,
                        background: 'linear-gradient(180deg, #0F1A2E 0%, #0A1628 100%)',
                        border: '1px solid rgba(14,165,233,0.25)',
                        borderRadius: '0.5rem',
                        boxShadow: '0 8px 24px rgba(0,0,0,0.6)',
                        padding: '0.5rem'
                    }}
                >
                    <div style={{ fontSize: '0.65rem', color: '#475569', fontFamily: 'monospace', textTransform: 'uppercase', letterSpacing: '0.1em', padding: '0.25rem 0.5rem 0.5rem', borderBottom: '1px solid rgba(255,255,255,0.05)', marginBottom: '0.375rem' }}>
                        Drag to reorder · click to toggle
                    </div>
                    {columnOrder.map((col, idx) => {
                        const def        = COLUMN_DEFS[col.key];
                        const isDragging = dragIdx === idx;
                        const isOver     = overIdx === idx && dragIdx !== null && dragIdx !== idx;
                        return (
                            <div
                                key={col.key}
                                draggable
                                onDragStart={() => handleDragStart(idx)}
                                onDragOver={(e) => handleDragOver(e, idx)}
                                onDrop={() => handleDrop(idx)}
                                onDragEnd={() => { setDragIdx(null); setOverIdx(null); }}
                                style={{
                                    display: 'flex', alignItems: 'center', gap: '0.5rem',
                                    padding: '0.4rem 0.5rem', borderRadius: '0.25rem', cursor: 'grab',
                                    opacity: isDragging ? 0.4 : 1,
                                    background: isOver ? 'rgba(14,165,233,0.12)' : 'transparent',
                                    borderTop: isOver ? '2px solid #0EA5E9' : '2px solid transparent',
                                    transition: 'background 0.1s'
                                }}
                            >
                                <GripVertical style={{ width: '14px', height: '14px', color: '#334155', flexShrink: 0 }} />
                                <span style={{ flex: 1, fontSize: '0.78rem', color: col.visible ? '#CBD5E1' : '#475569', fontFamily: 'monospace' }}>
                                    {def?.label || col.key}
                                </span>
                                <button
                                    onClick={(e) => { e.stopPropagation(); toggleVisible(col.key); }}
                                    style={{ background: 'none', border: 'none', cursor: 'pointer', padding: '2px', color: col.visible ? '#0EA5E9' : '#334155', lineHeight: 1 }}
                                >
                                    {col.visible ? <Eye style={{ width: '14px', height: '14px' }} /> : <EyeOff style={{ width: '14px', height: '14px' }} />}
                                </button>
                            </div>
                        );
                    })}
                </div>
            )}
        </div>
    );
}

// ---------------------------------------------------------------------------
// FilterDropdown — portal-based so it escapes overflow:auto clipping
// ---------------------------------------------------------------------------
function FilterDropdown({ anchorEl, colKey, findings, activeFilter, onFilterChange, onClose }) {
    const [pos, setPos]       = useState({ top: 0, left: 0 });
    const [search, setSearch] = useState('');
    const panelRef            = useRef(null);
    const inputRef            = useRef(null);

    // Compute fixed position from anchor button's viewport rect
    useEffect(() => {
        if (!anchorEl) return;
        const r = anchorEl.getBoundingClientRect();
        setPos({ top: r.bottom + 4, left: r.left });
        setTimeout(() => inputRef.current?.focus(), 0);
    }, [anchorEl]);

    // Close on outside click
    useEffect(() => {
        const handler = (e) => {
            if (panelRef.current && !panelRef.current.contains(e.target) &&
                !(anchorEl && anchorEl.contains(e.target))) {
                onClose();
            }
        };
        document.addEventListener('mousedown', handler);
        return () => document.removeEventListener('mousedown', handler);
    }, [anchorEl, onClose]);

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

    // Unique values from the full (unfiltered) findings list.
    // Multi-value columns (e.g. cves) expand their array so each item is a separate option.
    // EMPTY_SENTINEL is prepended when any finding has a blank/null cell.
    const allValues = useMemo(() => {
        const def  = COLUMN_DEFS[colKey];
        const vals = new Set();
        let hasEmpty = false;
        findings.forEach((f) => {
            if (def?.multiValue) {
                const arr = f[colKey] || [];
                if (arr.length === 0) { hasEmpty = true; return; }
                arr.forEach((v) => { if (String(v).trim()) vals.add(String(v).trim()); });
            } else {
                const v = getFilterVal(f, colKey).trim();
                if (v) vals.add(v); else hasEmpty = true;
            }
        });
        const sorted = [...vals].sort((a, b) => a.localeCompare(b, undefined, { numeric: true }));
        if (hasEmpty) sorted.unshift(EMPTY_SENTINEL);
        return sorted;
    }, [findings, colKey]);

    const displayed = search.trim()
        ? allValues.filter((v) => v === EMPTY_SENTINEL || v.toLowerCase().includes(search.toLowerCase()))
        : allValues;

    const isChecked    = (val) => !activeFilter || activeFilter.has(val);
    const activeCount  = activeFilter ? activeFilter.size : allValues.length;

    const toggle = (val) => {
        let next;
        if (!activeFilter) {
            next = new Set(allValues);
            next.delete(val);
        } else {
            next = new Set(activeFilter);
            if (next.has(val)) next.delete(val); else next.add(val);
        }
        // If all values selected again, remove the filter entirely
        onFilterChange(next.size >= allValues.length ? null : next);
    };

    return ReactDOM.createPortal(
        <div
            ref={panelRef}
            style={{
                position: 'fixed', top: pos.top, left: pos.left,
                width: '220px', zIndex: 9999,
                background: 'linear-gradient(180deg, #0F1A2E 0%, #0A1628 100%)',
                border: '1px solid rgba(14,165,233,0.3)',
                borderRadius: '0.5rem',
                boxShadow: '0 8px 32px rgba(0,0,0,0.8)',
                padding: '0.5rem',
            }}
        >
            {/* Search */}
            <input
                ref={inputRef}
                type="text"
                value={search}
                onChange={(e) => setSearch(e.target.value)}
                placeholder="Search values…"
                style={{
                    width: '100%', marginBottom: '0.375rem',
                    background: 'rgba(14,165,233,0.05)',
                    border: '1px solid rgba(14,165,233,0.2)',
                    borderRadius: '0.25rem', padding: '0.3rem 0.5rem',
                    color: '#CBD5E1', fontSize: '0.72rem',
                    fontFamily: 'monospace', outline: 'none', boxSizing: 'border-box',
                }}
            />

            {/* Select All / Clear */}
            <div style={{ display: 'flex', gap: '0.375rem', marginBottom: '0.375rem', paddingBottom: '0.375rem', borderBottom: '1px solid rgba(255,255,255,0.06)' }}>
                <button
                    onClick={() => onFilterChange(null)}
                    style={{ flex: 1, padding: '0.2rem', background: 'rgba(14,165,233,0.08)', border: '1px solid rgba(14,165,233,0.2)', borderRadius: '0.25rem', color: '#0EA5E9', cursor: 'pointer', fontSize: '0.65rem', fontFamily: 'monospace', textTransform: 'uppercase', letterSpacing: '0.05em' }}
                >
                    Select All
                </button>
                <button
                    onClick={() => onFilterChange(new Set())}
                    style={{ flex: 1, padding: '0.2rem', background: 'rgba(239,68,68,0.08)', border: '1px solid rgba(239,68,68,0.2)', borderRadius: '0.25rem', color: '#EF4444', cursor: 'pointer', fontSize: '0.65rem', fontFamily: 'monospace', textTransform: 'uppercase', letterSpacing: '0.05em' }}
                >
                    Clear
                </button>
            </div>

            {/* Value checkboxes */}
            <div style={{ maxHeight: '200px', overflowY: 'auto' }}>
                {displayed.length === 0 ? (
                    <div style={{ fontSize: '0.68rem', color: '#475569', textAlign: 'center', padding: '0.5rem 0' }}>No values</div>
                ) : displayed.map((val) => (
                    <label
                        key={val}
                        style={{ display: 'flex', alignItems: 'center', gap: '0.5rem', padding: '0.25rem 0.375rem', borderRadius: '0.25rem', cursor: 'pointer', color: isChecked(val) ? '#CBD5E1' : '#475569', fontSize: '0.72rem', fontFamily: 'monospace' }}
                        onMouseEnter={(e) => e.currentTarget.style.background = 'rgba(14,165,233,0.08)'}
                        onMouseLeave={(e) => e.currentTarget.style.background = 'transparent'}
                    >
                        <input
                            type="checkbox"
                            checked={isChecked(val)}
                            onChange={() => toggle(val)}
                            style={{ accentColor: '#0EA5E9', width: '12px', height: '12px', flexShrink: 0, cursor: 'pointer' }}
                        />
                        {val === EMPTY_SENTINEL
                            ? <span style={{ fontStyle: 'italic', color: '#64748B', whiteSpace: 'nowrap' }}>— empty —</span>
                            : <span style={{ overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{val}</span>
                        }
                    </label>
                ))}
            </div>

            {/* Status footer */}
            <div style={{ marginTop: '0.375rem', paddingTop: '0.375rem', borderTop: '1px solid rgba(255,255,255,0.06)', fontSize: '0.65rem', color: '#475569', textAlign: 'center', fontFamily: 'monospace' }}>
                {activeCount} / {allValues.length} selected
            </div>
        </div>,
        document.body
    );
}

// ---------------------------------------------------------------------------
// Render a single table cell by column key
// ---------------------------------------------------------------------------
function TableCell({ colKey, finding, canWrite, onCveMouseEnter, onCveMouseLeave, fpSubmissions, onEditSubmission, atlasStatusMap, onAtlasBadgeClick }) {
    switch (colKey) {
        case 'findingId':
            return (
                <td style={{ padding: '0.45rem 0.75rem', whiteSpace: 'nowrap', color: '#475569', fontFamily: 'monospace', fontSize: '0.68rem' }}>
                    {finding.id || '—'}
                </td>
            );
        case 'severity': {
            const sc = severityColor(finding.vrrGroup);
            return (
                <td style={{ padding: '0.45rem 0.75rem', whiteSpace: 'nowrap' }}>
                    <span style={{ display: 'inline-flex', alignItems: 'center', gap: '0.3rem', padding: '0.2rem 0.45rem', borderRadius: '0.25rem', background: sc.bg, border: `1px solid ${sc.border}40`, fontFamily: 'monospace', fontWeight: '700', color: sc.text, fontSize: '0.72rem' }}>
                        {finding.severity?.toFixed(2)}
                        <span style={{ fontSize: '0.6rem', opacity: 0.75 }}>{finding.vrrGroup}</span>
                    </span>
                </td>
            );
        }
        case 'title':
            return (
                <td style={{ padding: '0.45rem 0.75rem', maxWidth: '280px' }}>
                    <span style={{ color: '#CBD5E1', display: 'block', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }} title={finding.title}>
                        {finding.title}
                    </span>
                </td>
            );
        case 'cves': {
            const cves = finding.cves || [];
            if (cves.length === 0) return <td style={{ padding: '0.45rem 0.75rem', color: '#475569' }}>—</td>;
            const shown = cves.slice(0, 2);
            const rest  = cves.length - shown.length;
            return (
                <td style={{ padding: '0.45rem 0.75rem', minWidth: '160px', maxWidth: '240px' }}>
                    <div style={{ display: 'flex', flexWrap: 'wrap', gap: '0.2rem' }}>
                        {shown.map((cve) => (
                            <span
                                key={cve}
                                onMouseEnter={onCveMouseEnter ? (e) => onCveMouseEnter(cve, e) : undefined}
                                onMouseLeave={onCveMouseLeave || undefined}
                                style={{ padding: '0.1rem 0.35rem', borderRadius: '0.2rem', background: 'rgba(139,92,246,0.1)', border: '1px solid rgba(139,92,246,0.3)', color: '#A78BFA', fontFamily: 'monospace', fontSize: '0.65rem', fontWeight: '600', whiteSpace: 'nowrap' }}
                            >
                                {cve}
                            </span>
                        ))}
                        {rest > 0 && (
                            <span title={cves.slice(2).join('\n')} style={{ padding: '0.1rem 0.35rem', borderRadius: '0.2rem', background: 'rgba(100,116,139,0.12)', border: '1px solid rgba(100,116,139,0.25)', color: '#64748B', fontFamily: 'monospace', fontSize: '0.65rem', cursor: 'help', whiteSpace: 'nowrap' }}>
                                +{rest} more
                            </span>
                        )}
                    </div>
                </td>
            );
        }
        case 'hostName':
            return (
                <OverrideCell
                    findingId={finding.id}
                    field="hostName"
                    originalValue={finding.hostName}
                    initialOverride={finding.overrides?.hostName ?? null}
                    canWrite={canWrite}
                    suffix={
                        <AtlasBadge
                            hostId={finding.hostId}
                            atlasStatus={atlasStatusMap ? atlasStatusMap.get(finding.hostId) : undefined}
                            onClick={onAtlasBadgeClick}
                        />
                    }
                />
            );
        case 'ipAddress':
            return (
                <td style={{ padding: '0.45rem 0.75rem', whiteSpace: 'nowrap', color: '#94A3B8', fontFamily: 'monospace', fontSize: '0.72rem' }}>
                    {finding.ipAddress || '—'}
                </td>
            );
        case 'dns':
            return (
                <OverrideCell
                    findingId={finding.id}
                    field="dns"
                    originalValue={finding.dns}
                    initialOverride={finding.overrides?.dns ?? null}
                    canWrite={canWrite}
                />
            );
        case 'dueDate': {
            const color = dueDateColor(finding.dueDate);
            return (
                <td style={{ padding: '0.45rem 0.75rem', whiteSpace: 'nowrap', fontFamily: 'monospace', fontSize: '0.72rem', fontWeight: '600', color }}>
                    {finding.dueDate || '—'}
                </td>
            );
        }
        case 'slaStatus':
            return (
                <td style={{ padding: '0.45rem 0.75rem', whiteSpace: 'nowrap', fontFamily: 'monospace', fontSize: '0.68rem', fontWeight: '600', color: slaColor(finding.slaStatus) }}>
                    {finding.slaStatus || '—'}
                </td>
            );
        case 'buOwnership': {
            const bu      = finding.buOwnership || '';
            const isSteam = bu.toUpperCase().includes('STEAM');
            return (
                <td style={{ padding: '0.45rem 0.75rem', whiteSpace: 'nowrap' }}>
                    {bu ? (
                        <span
                            title={bu}
                            style={{
                                display: 'inline-block', padding: '0.15rem 0.4rem',
                                borderRadius: '0.25rem',
                                background: isSteam ? 'rgba(14,165,233,0.1)' : 'rgba(245,158,11,0.1)',
                                border: `1px solid ${isSteam ? 'rgba(14,165,233,0.3)' : 'rgba(245,158,11,0.3)'}`,
                                color: isSteam ? '#0EA5E9' : '#F59E0B',
                                fontFamily: 'monospace', fontSize: '0.68rem', fontWeight: '600',
                            }}
                        >
                            {bu.replace('NTS-AEO-', '')}
                        </span>
                    ) : (
                        <span style={{ color: '#475569' }}>—</span>
                    )}
                </td>
            );
        }
        case 'workflow': {
            const wf = finding.workflow;
            if (!wf || !wf.id) return <td style={{ padding: '0.45rem 0.75rem', color: '#334155' }}>—</td>;
            const ws = workflowStyle(wf.state);
            return (
                <td style={{ padding: '0.45rem 0.75rem', whiteSpace: 'nowrap' }}>
                    <span
                        title={`${wf.id} · ${wf.state || 'Unknown'} · ${wf.type || ''}`}
                        style={{
                            display: 'inline-flex', alignItems: 'center', gap: '0.3rem',
                            padding: '0.15rem 0.45rem', borderRadius: '0.25rem',
                            background: ws.bg, border: `1px solid ${ws.border}`,
                            fontFamily: 'monospace', fontSize: '0.68rem', fontWeight: '600',
                            color: ws.text, cursor: 'default',
                        }}
                    >
                        {wf.id}
                        <span style={{ fontSize: '0.58rem', opacity: 0.8, textTransform: 'uppercase', letterSpacing: '0.04em' }}>
                            {wf.state}
                        </span>
                    </span>
                </td>
            );
        }
        case 'lastFoundOn':
            return (
                <td style={{ padding: '0.45rem 0.75rem', whiteSpace: 'nowrap', color: '#64748B', fontFamily: 'monospace', fontSize: '0.72rem' }}>
                    {finding.lastFoundOn || '—'}
                </td>
            );
        case 'note':
            return (
                <td style={{ padding: '0.45rem 0.75rem' }}>
                    <NoteCell findingId={finding.id} initialNote={finding.note} />
                </td>
            );
        default:
            return <td style={{ padding: '0.45rem 0.75rem', color: '#64748B' }}>—</td>;
    }
}

// ---------------------------------------------------------------------------
// AddToQueuePopover — portal-based popover for adding a finding to the queue
// ---------------------------------------------------------------------------
function AddToQueuePopover({ finding, anchorRect, queueForm, setQueueForm, onAdd, onCancel }) {
    const panelRef  = useRef(null);
    const inputRef  = useRef(null);
    const [pos, setPos] = useState({ top: 0, left: 0 });

    useEffect(() => {
        if (!anchorRect) return;
        const PANEL_W = 260;
        const PANEL_H = 360; // conservative estimate (3 workflow buttons)
        const spaceBelow = window.innerHeight - anchorRect.bottom - 6;
        const top = spaceBelow >= PANEL_H
            ? anchorRect.bottom + 6
            : Math.max(8, anchorRect.top - PANEL_H - 6);
        const left = Math.min(anchorRect.left, window.innerWidth - PANEL_W - 8);
        setPos({ top, left });
        setTimeout(() => inputRef.current?.focus(), 0);
    }, [anchorRect]);

    // Close on outside click
    useEffect(() => {
        const handler = (e) => {
            if (panelRef.current && !panelRef.current.contains(e.target)) onCancel();
        };
        document.addEventListener('mousedown', handler);
        return () => document.removeEventListener('mousedown', handler);
    }, [onCancel]);

    // Close on Escape
    useEffect(() => {
        const handler = (e) => { if (e.key === 'Escape') onCancel(); };
        document.addEventListener('keydown', handler);
        return () => document.removeEventListener('keydown', handler);
    }, [onCancel]);

    const isCard    = queueForm.workflowType === 'CARD' || queueForm.workflowType === 'GRANITE' || queueForm.workflowType === 'DECOM';
    const canSubmit = isCard || queueForm.vendor.trim().length > 0;

    return ReactDOM.createPortal(
        <div
            ref={panelRef}
            style={{
                position: 'fixed', top: pos.top, left: pos.left,
                width: '260px', zIndex: 9999,
                background: 'linear-gradient(180deg, #0F1A2E 0%, #0A1628 100%)',
                border: '1px solid rgba(14,165,233,0.35)',
                borderRadius: '0.5rem',
                boxShadow: '0 8px 32px rgba(0,0,0,0.8)',
                padding: '0.875rem',
            }}
        >
            {/* Header */}
            <div style={{ fontFamily: 'monospace', fontSize: '0.68rem', fontWeight: '600', color: '#64748B', textTransform: 'uppercase', letterSpacing: '0.1em', marginBottom: '0.625rem', paddingBottom: '0.5rem', borderBottom: '1px solid rgba(255,255,255,0.06)' }}>
                Add to Ivanti Queue
            </div>
            <div style={{ fontFamily: 'monospace', fontSize: '0.68rem', color: '#94A3B8', marginBottom: '0.75rem', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }} title={finding.id}>
                {finding.id}
            </div>

            {/* Vendor input — hidden for CARD */}
            {isCard ? (
                <div style={{
                    marginBottom: '0.625rem', padding: '0.4rem 0.5rem',
                    background: 'rgba(16,185,129,0.06)',
                    border: '1px solid rgba(16,185,129,0.2)',
                    borderRadius: '0.25rem',
                    fontFamily: 'monospace', fontSize: '0.68rem', color: '#10B981',
                }}>
                    No vendor required — disposition handled in CARD
                </div>
            ) : (
                <label style={{ display: 'block', marginBottom: '0.625rem' }}>
                    <span style={{ display: 'block', fontFamily: 'monospace', fontSize: '0.62rem', fontWeight: '600', color: '#64748B', textTransform: 'uppercase', letterSpacing: '0.08em', marginBottom: '0.3rem' }}>
                        Vendor / Platform
                    </span>
                    <input
                        ref={inputRef}
                        type="text"
                        value={queueForm.vendor}
                        onChange={(e) => setQueueForm((f) => ({ ...f, vendor: e.target.value }))}
                        placeholder="Juniper, Cisco, ADTRAN…"
                        style={{
                            width: '100%', boxSizing: 'border-box',
                            background: 'rgba(14,165,233,0.05)',
                            border: '1px solid rgba(14,165,233,0.2)',
                            borderRadius: '0.25rem', padding: '0.35rem 0.5rem',
                            color: '#CBD5E1', fontSize: '0.78rem',
                            fontFamily: 'monospace', outline: 'none',
                        }}
                        onKeyDown={(e) => { if (e.key === 'Enter' && canSubmit) onAdd(); }}
                    />
                </label>
            )}

            {/* Workflow type toggle */}
            <div style={{ marginBottom: '0.875rem' }}>
                <span style={{ display: 'block', fontFamily: 'monospace', fontSize: '0.62rem', fontWeight: '600', color: '#64748B', textTransform: 'uppercase', letterSpacing: '0.08em', marginBottom: '0.3rem' }}>
                    Workflow Type
                </span>
                <div style={{ display: 'flex', gap: '0.375rem' }}>
                    {[
                        { key: 'FP',      col: '#F59E0B', rgb: '245,158,11' },
                        { key: 'Archer',  col: '#0EA5E9', rgb: '14,165,233' },
                        { key: 'CARD',    col: '#10B981', rgb: '16,185,129' },
                        { key: 'GRANITE', col: '#A1887F', rgb: '161,136,127' },
                        { key: 'DECOM',   col: '#EF4444', rgb: '239,68,68' },
                    ].map(({ key, col, rgb }) => {
                        const active = queueForm.workflowType === key;
                        return (
                            <button
                                key={key}
                                onClick={() => setQueueForm((f) => ({ ...f, workflowType: key }))}
                                style={{
                                    flex: 1, padding: '0.3rem',
                                    background: active ? `rgba(${rgb},0.15)` : 'transparent',
                                    border: `1px solid ${active ? col : 'rgba(255,255,255,0.1)'}`,
                                    borderRadius: '0.25rem',
                                    color: active ? col : '#475569',
                                    fontFamily: 'monospace', fontSize: '0.72rem', fontWeight: '700',
                                    cursor: 'pointer', transition: 'all 0.12s',
                                }}
                            >
                                {key}
                            </button>
                        );
                    })}
                </div>
            </div>

            {/* Actions */}
            <div style={{ display: 'flex', gap: '0.5rem', alignItems: 'center' }}>
                <button
                    onClick={onAdd}
                    disabled={!canSubmit}
                    style={{
                        flex: 1, padding: '0.4rem',
                        background: canSubmit ? 'rgba(14,165,233,0.15)' : 'rgba(14,165,233,0.05)',
                        border: `1px solid ${canSubmit ? 'rgba(14,165,233,0.4)' : 'rgba(14,165,233,0.1)'}`,
                        borderRadius: '0.25rem',
                        color: canSubmit ? '#0EA5E9' : '#334155',
                        fontFamily: 'monospace', fontSize: '0.72rem', fontWeight: '700',
                        cursor: canSubmit ? 'pointer' : 'not-allowed',
                        textTransform: 'uppercase', letterSpacing: '0.05em',
                    }}
                >
                    Add to Queue
                </button>
                <button
                    onClick={onCancel}
                    style={{
                        padding: '0.4rem 0.625rem',
                        background: 'none', border: 'none',
                        color: '#475569', fontFamily: 'monospace', fontSize: '0.72rem',
                        cursor: 'pointer',
                    }}
                >
                    Cancel
                </button>
            </div>
        </div>,
        document.body
    );
}

// ---------------------------------------------------------------------------
// QueuePanel — fixed slide-out panel showing the user's Ivanti queue
// ---------------------------------------------------------------------------
function QueuePanel({ open, items, onClose, onUpdate, onDelete, onDeleteMany, onClearCompleted, onCreateFpWorkflow, onRedirectComplete, canWrite, fpSubmissions, onEditSubmission, onDismissSubmission, cardConfigured, cardTeams, onQueueRefresh }) {
    const pendingCount   = items.filter((i) => i.status === 'pending').length;
    const completedCount = items.filter((i) => i.status === 'complete').length;

    const [selectedIds, setSelectedIds] = useState(new Set());
    const [redirectItem, setRedirectItem] = useState(null);
    const [redirectSuccess, setRedirectSuccess] = useState(null);

    // CARD action state — tracks which item has an active action form
    const [cardAction, setCardAction] = useState(null); // { itemId, type: 'confirm'|'decline'|'redirect' }
    const [cardFormTeam, setCardFormTeam] = useState('');
    const [cardFormComment, setCardFormComment] = useState('');
    const [cardFormFromTeam, setCardFormFromTeam] = useState('');
    const [cardFormToTeam, setCardFormToTeam] = useState('');
    const [cardActionLoading, setCardActionLoading] = useState(false);
    const [cardActionError, setCardActionError] = useState(null);

    // Create Jira modal state
    const [createJiraOpen, setCreateJiraOpen] = useState(false);
    const [createJiraForm, setCreateJiraForm] = useState({ summary: '', cve_id: '', vendor: '', source_context: 'ivanti_queue', description: '', project_key: '', issue_type: '' });
    const [createJiraError, setCreateJiraError] = useState(null);
    const [createJiraSaving, setCreateJiraSaving] = useState(false);
    const [createJiraSummaryError, setCreateJiraSummaryError] = useState(null);

    // Consolidated Jira ticket modal state (multi-item → 1 ticket)
    const [showConsolidationModal, setShowConsolidationModal] = useState(false);
    const [consolidationSuccess, setConsolidationSuccess] = useState(null); // { ticket_key, jira_url }

    // CARD Asset Search state
    const [assetSearchOpen, setAssetSearchOpen] = useState(false);
    const [assetSearchTeam, setAssetSearchTeam] = useState('');
    const [assetSearchDisposition, setAssetSearchDisposition] = useState('confirmed');
    const [assetSearchResults, setAssetSearchResults] = useState(null);
    const [assetSearchLoading, setAssetSearchLoading] = useState(false);
    const [assetSearchError, setAssetSearchError] = useState(null);
    const [assetSearchPage, setAssetSearchPage] = useState(1);

    // Drop any selected IDs that no longer exist in items
    useEffect(() => {
        setSelectedIds((prev) => {
            if (prev.size === 0) return prev;
            const valid = new Set(items.map((i) => i.id));
            const next  = new Set([...prev].filter((id) => valid.has(id)));
            return next.size === prev.size ? prev : next;
        });
    }, [items]);

    const toggleSelect = (id) => {
        setSelectedIds((prev) => {
            const next = new Set(prev);
            if (next.has(id)) next.delete(id); else next.add(id);
            return next;
        });
    };

    const handleDeleteSelected = () => {
        onDeleteMany([...selectedIds]);
        setSelectedIds(new Set());
    };

    // Submissions section — collapsible state (Task 6)
    const [submissionsCollapsed, setSubmissionsCollapsed] = useState(() => localStorage.getItem('steam_submissions_collapsed') === 'true');
    const [dismissError, setDismissError] = useState(null);

    const toggleSubmissionsCollapsed = () => {
        setSubmissionsCollapsed(prev => {
            const next = !prev;
            try { localStorage.setItem('steam_submissions_collapsed', String(next)); } catch { /* ignore */ }
            return next;
        });
    };

    // Dismiss handler (Task 5)
    const handleDismiss = async (e, submissionId) => {
        e.stopPropagation();
        setDismissError(null);
        try {
            const res = await fetch(`${API_BASE}/ivanti/fp-workflow/submissions/${submissionId}/dismiss`, {
                method: 'PATCH',
                credentials: 'include',
            });
            if (!res.ok) {
                const data = await res.json().catch(() => ({}));
                setDismissError(data.error || 'Failed to dismiss submission');
                return;
            }
            if (onDismissSubmission) onDismissSubmission(submissionId);
        } catch (err) {
            setDismissError('Network error — could not dismiss submission');
        }
    };
    const handleRedirectSuccess = (newItem) => {
        if (onRedirectComplete) onRedirectComplete(newItem);
        setRedirectItem(null);
        setRedirectSuccess(`Redirected to ${newItem.workflow_type}`);
        setTimeout(() => setRedirectSuccess(null), 3000);
    };

    // CARD action handlers
    const openCardAction = (itemId, type) => {
        setCardAction({ itemId, type });
        setCardFormTeam('');
        setCardFormComment('');
        setCardFormFromTeam('');
        setCardFormToTeam('');
        setCardActionError(null);
    };

    const closeCardAction = () => {
        setCardAction(null);
        setCardFormTeam('');
        setCardFormComment('');
        setCardFormFromTeam('');
        setCardFormToTeam('');
        setCardActionError(null);
        setCardActionLoading(false);
    };

    const handleCardConfirmDecline = async (item, actionType) => {
        if (!cardFormTeam) return;
        setCardActionLoading(true);
        setCardActionError(null);
        try {
            const res = await fetch(`${API_BASE}/card/queue/${item.id}/${actionType}`, {
                method: 'POST',
                credentials: 'include',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify({
                    teamName: cardFormTeam,
                    assetId: item.ip_address,
                    comment: cardFormComment || '',
                }),
            });
            const data = await res.json();
            if (!res.ok) {
                setCardActionError(data.error || `${actionType} failed.`);
                setCardActionLoading(false);
                return;
            }
            // Update local state to complete without full refresh
            onUpdate(item.id, { status: 'complete' });
            closeCardAction();
        } catch (err) {
            setCardActionError(err.message || 'Network error.');
            setCardActionLoading(false);
        }
    };

    const handleCardRedirect = async (item) => {
        if (!cardFormFromTeam || !cardFormToTeam) return;
        setCardActionLoading(true);
        setCardActionError(null);
        try {
            const res = await fetch(`${API_BASE}/card/queue/${item.id}/redirect`, {
                method: 'POST',
                credentials: 'include',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify({
                    fromTeam: cardFormFromTeam,
                    toTeam: cardFormToTeam,
                    assetId: item.ip_address,
                }),
            });
            const data = await res.json();
            if (!res.ok) {
                setCardActionError(data.error || 'Redirect failed.');
                setCardActionLoading(false);
                return;
            }
            onUpdate(item.id, { status: 'complete' });
            closeCardAction();
        } catch (err) {
            setCardActionError(err.message || 'Network error.');
            setCardActionLoading(false);
        }
    };

    // CARD Asset Search handler
    const handleAssetSearch = async (page = 1) => {
        if (!assetSearchTeam || !assetSearchDisposition) return;
        setAssetSearchLoading(true);
        setAssetSearchError(null);
        setAssetSearchPage(page);
        try {
            const res = await fetch(
                `${API_BASE}/card/teams/${encodeURIComponent(assetSearchTeam)}/assets?disposition=${encodeURIComponent(assetSearchDisposition)}&page_size=50&page=${page}`,
                { credentials: 'include' }
            );
            const data = await res.json();
            if (!res.ok) {
                setAssetSearchError(data.error || 'Search failed.');
                setAssetSearchLoading(false);
                return;
            }
            setAssetSearchResults(data);
        } catch (err) {
            setAssetSearchError(err.message || 'Network error.');
        } finally {
            setAssetSearchLoading(false);
        }
    };

    // Open Create Jira modal pre-populated from a queue item
    const openCreateJiraFromQueue = (item) => {
        // Parse cves_json — it may be a JSON string or already an array
        let cves = [];
        if (item.cves_json) {
            try { cves = typeof item.cves_json === 'string' ? JSON.parse(item.cves_json) : item.cves_json; } catch { cves = []; }
        } else if (item.cves && Array.isArray(item.cves)) {
            cves = item.cves;
        }
        const firstCve = (Array.isArray(cves) && cves.length > 0) ? cves[0] : '';
        const summary = (item.finding_title || '').slice(0, 255);
        setCreateJiraForm({
            summary,
            cve_id: firstCve,
            vendor: item.vendor || '',
            source_context: 'ivanti_queue',
            description: '',
            project_key: '',
            issue_type: '',
        });
        setCreateJiraError(null);
        setCreateJiraSummaryError(null);
        setCreateJiraOpen(true);
    };

    // Submit the Create Jira form
    const submitCreateJira = async () => {
        const trimmedSummary = (createJiraForm.summary || '').trim();
        if (!trimmedSummary) {
            setCreateJiraSummaryError('Summary is required.');
            return;
        }
        if (trimmedSummary.length > 255) {
            setCreateJiraSummaryError('Summary must be 255 characters or fewer.');
            return;
        }
        setCreateJiraSummaryError(null);
        setCreateJiraError(null);
        setCreateJiraSaving(true);
        try {
            const payload = { ...createJiraForm };
            if (!payload.source_context) delete payload.source_context;
            const res = await fetch(`${API_BASE}/jira-tickets/create-in-jira`, {
                method: 'POST',
                headers: { 'Content-Type': 'application/json' },
                credentials: 'include',
                body: JSON.stringify(payload),
            });
            const data = await res.json();
            if (!res.ok) throw new Error(data.error || `HTTP ${res.status}`);
            setCreateJiraOpen(false);
            setCreateJiraForm({ summary: '', cve_id: '', vendor: '', source_context: 'ivanti_queue', description: '', project_key: '', issue_type: '' });
        } catch (err) {
            setCreateJiraError(err.message);
        } finally {
            setCreateJiraSaving(false);
        }
    };

    // Render a single queue item row
    const renderQueueItem = (item, { done, selectedIds, toggleSelect, onUpdate, onDelete, setRedirectItem, canWrite }) => {
        const wfColor = item.workflow_type === 'FP'      ? { col: '#F59E0B', rgb: '245,158,11' }
                      : item.workflow_type === 'Archer'   ? { col: '#0EA5E9', rgb: '14,165,233' }
                      : item.workflow_type === 'GRANITE'  ? { col: '#A1887F', rgb: '161,136,127' }
                      : item.workflow_type === 'DECOM'    ? { col: '#EF4444', rgb: '239,68,68' }
                      :                                     { col: '#10B981', rgb: '16,185,129' };
        const cves  = item.cves || [];
        const cveDisplay = cves.length > 0
            ? cves.slice(0, 3).join(', ') + (cves.length > 3 ? ` +${cves.length - 3}` : '')
            : '—';
        const isInventoryItem = item.workflow_type === 'CARD' || item.workflow_type === 'GRANITE' || item.workflow_type === 'DECOM';
        return (
            <div
                key={item.id}
                style={{
                    display: 'flex', alignItems: 'flex-start', gap: '0.5rem',
                    padding: '0.5rem 0.625rem',
                    marginBottom: '0.25rem',
                    borderRadius: '0.375rem',
                    background: done ? 'rgba(16,185,129,0.04)' : 'rgba(14,165,233,0.04)',
                    border: `1px solid ${done ? 'rgba(16,185,129,0.12)' : 'rgba(14,165,233,0.1)'}`,
                    opacity: done ? 0.55 : 1,
                    transition: 'opacity 0.15s',
                }}
            >
                {/* Selection checkbox — for bulk delete */}
                <input
                    type="checkbox"
                    checked={selectedIds.has(item.id)}
                    onChange={() => toggleSelect(item.id)}
                    style={{ accentColor: '#EF4444', width: '12px', height: '12px', flexShrink: 0, marginTop: '3px', cursor: 'pointer', opacity: selectedIds.has(item.id) ? 1 : 0.35 }}
                    title="Select for deletion"
                />

                {/* Complete checkbox */}
                <input
                    type="checkbox"
                    checked={done}
                    onChange={() => onUpdate(item.id, { status: done ? 'pending' : 'complete' })}
                    style={{ accentColor: '#10B981', width: '14px', height: '14px', flexShrink: 0, marginTop: '2px', cursor: 'pointer' }}
                />

                {/* Content */}
                <div style={{ flex: 1, minWidth: 0 }}>
                    <div style={{
                        fontFamily: 'monospace', fontSize: '0.72rem', fontWeight: '600',
                        color: done ? '#475569' : '#CBD5E1',
                        textDecoration: done ? 'line-through' : 'none',
                        overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap',
                    }} title={item.finding_id}>
                        {item.finding_id}
                    </div>
                    {isInventoryItem ? (
                        <>
                            {item.hostname && (
                                <div style={{
                                    fontFamily: 'monospace', fontSize: '0.68rem', fontWeight: '600',
                                    color: done ? '#334155' : '#94A3B8',
                                    textDecoration: done ? 'line-through' : 'none',
                                    marginTop: '2px',
                                }}>
                                    {item.hostname}
                                </div>
                            )}
                            {item.ip_address && (
                                <div style={{
                                    fontFamily: 'monospace', fontSize: '0.68rem', fontWeight: '600',
                                    color: done ? '#334155' : '#10B981',
                                    textDecoration: done ? 'line-through' : 'none',
                                    marginTop: '2px',
                                }}>
                                    {item.ip_address}
                                </div>
                            )}
                            {cves.length > 0 && (
                                <div style={{
                                    fontFamily: 'monospace', fontSize: '0.62rem',
                                    color: done ? '#334155' : '#64748B',
                                    textDecoration: done ? 'line-through' : 'none',
                                    overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap',
                                    marginTop: '2px',
                                }} title={cves.join(', ')}>
                                    {cveDisplay}
                                </div>
                            )}
                        </>
                    ) : (
                        <>
                            {cves.length > 0 && (
                                <div style={{
                                    fontFamily: 'monospace', fontSize: '0.62rem',
                                    color: done ? '#334155' : '#64748B',
                                    textDecoration: done ? 'line-through' : 'none',
                                    overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap',
                                    marginTop: '1px',
                                }} title={cves.join(', ')}>
                                    {cveDisplay}
                                </div>
                            )}
                            {item.hostname && (
                                <div style={{
                                    fontFamily: 'monospace', fontSize: '0.62rem',
                                    color: done ? '#334155' : '#94A3B8',
                                    textDecoration: done ? 'line-through' : 'none',
                                    marginTop: '1px',
                                }}>
                                    {item.hostname}
                                </div>
                            )}
                            {item.ip_address && (
                                <div style={{
                                    fontFamily: 'monospace', fontSize: '0.68rem', fontWeight: '600',
                                    color: done ? '#334155' : '#10B981',
                                    textDecoration: done ? 'line-through' : 'none',
                                    marginTop: '1px',
                                }}>
                                    {item.ip_address}
                                </div>
                            )}
                        </>
                    )}
                </div>

                {/* Workflow type badge */}
                <span style={{
                    flexShrink: 0,
                    padding: '0.1rem 0.35rem',
                    borderRadius: '0.2rem',
                    background: `rgba(${wfColor.rgb},0.12)`,
                    border: `1px solid rgba(${wfColor.rgb},0.3)`,
                    color: wfColor.col,
                    fontFamily: 'monospace', fontSize: '0.6rem', fontWeight: '700',
                }}>
                    {item.workflow_type}
                </span>

                {/* CARD action buttons — pending CARD items only */}
                {item.workflow_type === 'CARD' && item.status === 'pending' && canWrite && (
                    <div style={{ display: 'flex', gap: '0.25rem', flexShrink: 0 }}>
                        <button
                            onClick={() => openCardAction(item.id, 'confirm')}
                            disabled={!cardConfigured || cardActionLoading}
                            title={!cardConfigured ? 'CARD integration not configured' : 'Confirm asset'}
                            style={{
                                background: cardConfigured ? 'rgba(16,185,129,0.1)' : 'transparent',
                                border: `1px solid ${cardConfigured ? 'rgba(16,185,129,0.3)' : 'rgba(255,255,255,0.05)'}`,
                                borderRadius: '0.2rem',
                                padding: '0.15rem 0.3rem',
                                fontFamily: 'monospace', fontSize: '0.55rem', fontWeight: '700',
                                color: cardConfigured ? '#10B981' : '#334155',
                                cursor: cardConfigured ? 'pointer' : 'not-allowed',
                                textTransform: 'uppercase', letterSpacing: '0.04em',
                                transition: 'all 0.12s',
                            }}
                        >
                            Confirm
                        </button>
                        <button
                            onClick={() => openCardAction(item.id, 'decline')}
                            disabled={!cardConfigured || cardActionLoading}
                            title={!cardConfigured ? 'CARD integration not configured' : 'Decline asset'}
                            style={{
                                background: cardConfigured ? 'rgba(239,68,68,0.1)' : 'transparent',
                                border: `1px solid ${cardConfigured ? 'rgba(239,68,68,0.3)' : 'rgba(255,255,255,0.05)'}`,
                                borderRadius: '0.2rem',
                                padding: '0.15rem 0.3rem',
                                fontFamily: 'monospace', fontSize: '0.55rem', fontWeight: '700',
                                color: cardConfigured ? '#EF4444' : '#334155',
                                cursor: cardConfigured ? 'pointer' : 'not-allowed',
                                textTransform: 'uppercase', letterSpacing: '0.04em',
                                transition: 'all 0.12s',
                            }}
                        >
                            Decline
                        </button>
                        <button
                            onClick={() => openCardAction(item.id, 'redirect')}
                            disabled={!cardConfigured || cardActionLoading}
                            title={!cardConfigured ? 'CARD integration not configured' : 'Redirect asset'}
                            style={{
                                background: cardConfigured ? 'rgba(14,165,233,0.1)' : 'transparent',
                                border: `1px solid ${cardConfigured ? 'rgba(14,165,233,0.3)' : 'rgba(255,255,255,0.05)'}`,
                                borderRadius: '0.2rem',
                                padding: '0.15rem 0.3rem',
                                fontFamily: 'monospace', fontSize: '0.55rem', fontWeight: '700',
                                color: cardConfigured ? '#0EA5E9' : '#334155',
                                cursor: cardConfigured ? 'pointer' : 'not-allowed',
                                textTransform: 'uppercase', letterSpacing: '0.04em',
                                transition: 'all 0.12s',
                            }}
                        >
                            Redirect
                        </button>
                    </div>
                )}

                {/* Redirect button — completed items only */}
                {canWrite && done && (
                    <button
                        onClick={() => setRedirectItem(item)}
                        style={{ background: 'none', border: 'none', cursor: 'pointer', color: '#334155', padding: '1px', lineHeight: 1, flexShrink: 0 }}
                        onMouseEnter={(e) => e.currentTarget.style.color = '#0EA5E9'}
                        onMouseLeave={(e) => e.currentTarget.style.color = '#334155'}
                        title="Redirect to another workflow"
                    >
                        <CornerUpRight style={{ width: '13px', height: '13px' }} />
                    </button>
                )}

                {/* Create Jira Ticket button — pending items only */}
                {canWrite && !done && (
                    <button
                        onClick={() => openCreateJiraFromQueue(item)}
                        style={{
                            background: 'rgba(14, 165, 233, 0.08)',
                            border: '1px solid rgba(14, 165, 233, 0.25)',
                            borderRadius: '0.2rem',
                            padding: '0.15rem 0.35rem',
                            cursor: 'pointer',
                            display: 'inline-flex',
                            alignItems: 'center',
                            gap: '0.2rem',
                            color: '#7DD3FC',
                            fontFamily: 'monospace',
                            fontSize: '0.55rem',
                            fontWeight: '700',
                            textTransform: 'uppercase',
                            letterSpacing: '0.04em',
                            flexShrink: 0,
                            transition: 'all 0.12s',
                        }}
                        onMouseEnter={(e) => { e.currentTarget.style.background = 'rgba(14, 165, 233, 0.18)'; e.currentTarget.style.borderColor = 'rgba(14, 165, 233, 0.45)'; }}
                        onMouseLeave={(e) => { e.currentTarget.style.background = 'rgba(14, 165, 233, 0.08)'; e.currentTarget.style.borderColor = 'rgba(14, 165, 233, 0.25)'; }}
                        title="Create Jira ticket from this queue item"
                    >
                        <Plus style={{ width: '10px', height: '10px' }} />
                        Jira
                    </button>
                )}

                {/* Delete button */}
                <button
                    onClick={() => onDelete(item.id)}
                    style={{ background: 'none', border: 'none', cursor: 'pointer', color: '#334155', padding: '1px', lineHeight: 1, flexShrink: 0 }}
                    onMouseEnter={(e) => e.currentTarget.style.color = '#EF4444'}
                    onMouseLeave={(e) => e.currentTarget.style.color = '#334155'}
                    title="Remove from queue"
                >
                    <Trash2 style={{ width: '13px', height: '13px' }} />
                </button>
            </div>
        );
    };

    // Render CARD action inline form below a queue item
    const renderCardActionForm = (item) => {
        if (!cardAction || cardAction.itemId !== item.id) return null;
        const { type } = cardAction;

        if (type === 'confirm' || type === 'decline') {
            const accentColor = type === 'confirm' ? '#10B981' : '#EF4444';
            const accentRgb = type === 'confirm' ? '16,185,129' : '239,68,68';
            const canSubmit = !cardActionLoading && cardFormTeam.length > 0;
            return (
                <div style={{
                    padding: '0.5rem 0.625rem',
                    marginBottom: '0.25rem',
                    borderRadius: '0.375rem',
                    background: `rgba(${accentRgb},0.04)`,
                    border: `1px solid rgba(${accentRgb},0.15)`,
                }}>
                    <div style={{ display: 'flex', alignItems: 'center', gap: '0.375rem', marginBottom: '0.375rem' }}>
                        <span style={{ fontFamily: 'monospace', fontSize: '0.62rem', fontWeight: '700', color: accentColor, textTransform: 'uppercase', letterSpacing: '0.08em' }}>
                            {type === 'confirm' ? 'Confirm Asset' : 'Decline Asset'}
                        </span>
                    </div>
                    <select
                        value={cardFormTeam}
                        onChange={(e) => setCardFormTeam(e.target.value)}
                        disabled={cardActionLoading}
                        style={{
                            width: '100%', boxSizing: 'border-box',
                            background: 'rgba(15, 23, 42, 0.6)',
                            border: `1px solid rgba(${accentRgb}, 0.25)`,
                            borderRadius: '0.25rem',
                            padding: '0.35rem 0.5rem',
                            fontFamily: "'JetBrains Mono', monospace", fontSize: '0.7rem',
                            color: '#E2E8F0',
                            outline: 'none',
                            marginBottom: '0.375rem',
                        }}
                    >
                        <option value="">Select team…</option>
                        {cardTeams.map(t => <option key={t} value={t}>{t}</option>)}
                    </select>
                    <input
                        type="text"
                        value={cardFormComment}
                        onChange={(e) => setCardFormComment(e.target.value)}
                        disabled={cardActionLoading}
                        placeholder="Comment (optional)"
                        maxLength={500}
                        style={{
                            width: '100%', boxSizing: 'border-box',
                            background: 'rgba(15, 23, 42, 0.6)',
                            border: `1px solid rgba(${accentRgb}, 0.15)`,
                            borderRadius: '0.25rem',
                            padding: '0.35rem 0.5rem',
                            fontFamily: "'JetBrains Mono', monospace", fontSize: '0.7rem',
                            color: '#E2E8F0',
                            outline: 'none',
                            marginBottom: '0.375rem',
                        }}
                    />
                    {cardActionError && (
                        <div style={{
                            display: 'flex', alignItems: 'center', gap: '0.375rem',
                            padding: '0.3rem 0.5rem',
                            background: 'rgba(239, 68, 68, 0.1)',
                            border: '1px solid rgba(239, 68, 68, 0.25)',
                            borderRadius: '0.25rem',
                            marginBottom: '0.375rem',
                        }}>
                            <AlertCircle style={{ width: '12px', height: '12px', color: '#EF4444', flexShrink: 0 }} />
                            <span style={{ fontFamily: 'monospace', fontSize: '0.62rem', color: '#FCA5A5' }}>{cardActionError}</span>
                        </div>
                    )}
                    <div style={{ display: 'flex', gap: '0.375rem' }}>
                        <button
                            onClick={() => handleCardConfirmDecline(item, type)}
                            disabled={!canSubmit}
                            style={{
                                flex: 1, padding: '0.3rem',
                                background: canSubmit ? `rgba(${accentRgb},0.12)` : 'transparent',
                                border: `1px solid ${canSubmit ? `rgba(${accentRgb},0.35)` : 'rgba(255,255,255,0.05)'}`,
                                borderRadius: '0.25rem',
                                color: canSubmit ? accentColor : '#334155',
                                fontFamily: 'monospace', fontSize: '0.65rem', fontWeight: '600',
                                cursor: canSubmit ? 'pointer' : 'not-allowed',
                                textTransform: 'uppercase', letterSpacing: '0.04em',
                                display: 'flex', alignItems: 'center', justifyContent: 'center', gap: '0.25rem',
                            }}
                        >
                            {cardActionLoading && <Loader style={{ width: '10px', height: '10px', animation: 'spin 1s linear infinite' }} />}
                            {cardActionLoading ? 'Submitting…' : type === 'confirm' ? 'Confirm' : 'Decline'}
                        </button>
                        <button
                            onClick={closeCardAction}
                            disabled={cardActionLoading}
                            style={{
                                padding: '0.3rem 0.5rem',
                                background: 'transparent',
                                border: '1px solid rgba(255,255,255,0.08)',
                                borderRadius: '0.25rem',
                                color: '#64748B',
                                fontFamily: 'monospace', fontSize: '0.65rem', fontWeight: '600',
                                cursor: 'pointer',
                                textTransform: 'uppercase', letterSpacing: '0.04em',
                            }}
                        >
                            Cancel
                        </button>
                    </div>
                </div>
            );
        }

        if (type === 'redirect') {
            const canSubmit = !cardActionLoading && cardFormFromTeam.length > 0 && cardFormToTeam.length > 0;
            return (
                <div style={{
                    padding: '0.5rem 0.625rem',
                    marginBottom: '0.25rem',
                    borderRadius: '0.375rem',
                    background: 'rgba(14,165,233,0.04)',
                    border: '1px solid rgba(14,165,233,0.15)',
                }}>
                    <div style={{ display: 'flex', alignItems: 'center', gap: '0.375rem', marginBottom: '0.375rem' }}>
                        <span style={{ fontFamily: 'monospace', fontSize: '0.62rem', fontWeight: '700', color: '#0EA5E9', textTransform: 'uppercase', letterSpacing: '0.08em' }}>
                            Redirect Asset
                        </span>
                    </div>
                    <select
                        value={cardFormFromTeam}
                        onChange={(e) => setCardFormFromTeam(e.target.value)}
                        disabled={cardActionLoading}
                        style={{
                            width: '100%', boxSizing: 'border-box',
                            background: 'rgba(15, 23, 42, 0.6)',
                            border: '1px solid rgba(14, 165, 233, 0.25)',
                            borderRadius: '0.25rem',
                            padding: '0.35rem 0.5rem',
                            fontFamily: "'JetBrains Mono', monospace", fontSize: '0.7rem',
                            color: '#E2E8F0',
                            outline: 'none',
                            marginBottom: '0.375rem',
                        }}
                    >
                        <option value="">From team…</option>
                        {cardTeams.map(t => <option key={t} value={t}>{t}</option>)}
                    </select>
                    <select
                        value={cardFormToTeam}
                        onChange={(e) => setCardFormToTeam(e.target.value)}
                        disabled={cardActionLoading}
                        style={{
                            width: '100%', boxSizing: 'border-box',
                            background: 'rgba(15, 23, 42, 0.6)',
                            border: '1px solid rgba(14, 165, 233, 0.25)',
                            borderRadius: '0.25rem',
                            padding: '0.35rem 0.5rem',
                            fontFamily: "'JetBrains Mono', monospace", fontSize: '0.7rem',
                            color: '#E2E8F0',
                            outline: 'none',
                            marginBottom: '0.375rem',
                        }}
                    >
                        <option value="">To team…</option>
                        {cardTeams.map(t => <option key={t} value={t}>{t}</option>)}
                    </select>
                    {cardActionError && (
                        <div style={{
                            display: 'flex', alignItems: 'center', gap: '0.375rem',
                            padding: '0.3rem 0.5rem',
                            background: 'rgba(239, 68, 68, 0.1)',
                            border: '1px solid rgba(239, 68, 68, 0.25)',
                            borderRadius: '0.25rem',
                            marginBottom: '0.375rem',
                        }}>
                            <AlertCircle style={{ width: '12px', height: '12px', color: '#EF4444', flexShrink: 0 }} />
                            <span style={{ fontFamily: 'monospace', fontSize: '0.62rem', color: '#FCA5A5' }}>{cardActionError}</span>
                        </div>
                    )}
                    <div style={{ display: 'flex', gap: '0.375rem' }}>
                        <button
                            onClick={() => handleCardRedirect(item)}
                            disabled={!canSubmit}
                            style={{
                                flex: 1, padding: '0.3rem',
                                background: canSubmit ? 'rgba(14,165,233,0.12)' : 'transparent',
                                border: `1px solid ${canSubmit ? 'rgba(14,165,233,0.35)' : 'rgba(255,255,255,0.05)'}`,
                                borderRadius: '0.25rem',
                                color: canSubmit ? '#0EA5E9' : '#334155',
                                fontFamily: 'monospace', fontSize: '0.65rem', fontWeight: '600',
                                cursor: canSubmit ? 'pointer' : 'not-allowed',
                                textTransform: 'uppercase', letterSpacing: '0.04em',
                                display: 'flex', alignItems: 'center', justifyContent: 'center', gap: '0.25rem',
                            }}
                        >
                            {cardActionLoading && <Loader style={{ width: '10px', height: '10px', animation: 'spin 1s linear infinite' }} />}
                            {cardActionLoading ? 'Redirecting…' : 'Redirect'}
                        </button>
                        <button
                            onClick={closeCardAction}
                            disabled={cardActionLoading}
                            style={{
                                padding: '0.3rem 0.5rem',
                                background: 'transparent',
                                border: '1px solid rgba(255,255,255,0.08)',
                                borderRadius: '0.25rem',
                                color: '#64748B',
                                fontFamily: 'monospace', fontSize: '0.65rem', fontWeight: '600',
                                cursor: 'pointer',
                                textTransform: 'uppercase', letterSpacing: '0.04em',
                            }}
                        >
                            Cancel
                        </button>
                    </div>
                </div>
            );
        }

        return null;
    };

    // Inventory items (CARD + GRANITE + DECOM) are their own top section; everything else groups by vendor
    const grouped = useMemo(() => {
        const inventoryItems = items.filter((i) => i.workflow_type === 'CARD' || i.workflow_type === 'GRANITE' || i.workflow_type === 'DECOM');
        const cardItems    = inventoryItems.filter((i) => i.workflow_type === 'CARD');
        const graniteItems = inventoryItems.filter((i) => i.workflow_type === 'GRANITE');
        const decomItems   = inventoryItems.filter((i) => i.workflow_type === 'DECOM');
        const otherItems   = items.filter((i) => i.workflow_type !== 'CARD' && i.workflow_type !== 'GRANITE' && i.workflow_type !== 'DECOM');

        const map = {};
        otherItems.forEach((item) => {
            const v = item.vendor || 'Unknown';
            if (!map[v]) map[v] = [];
            map[v].push(item);
        });
        const vendorGroups = Object.keys(map).sort().map((vendor) => ({
            key: vendor, label: vendor, items: map[vendor], isInventory: false,
        }));

        return inventoryItems.length > 0
            ? [{ key: '__INVENTORY__', label: 'Inventory', cardItems, graniteItems, decomItems, items: inventoryItems, isInventory: true }, ...vendorGroups]
            : vendorGroups;
    }, [items]);

    return (
        <>
            {/* Backdrop */}
            {open && (
                <div
                    onClick={onClose}
                    style={{
                        position: 'fixed', inset: 0,
                        background: 'rgba(0,0,0,0.45)',
                        zIndex: 9998,
                    }}
                />
            )}

            {/* Panel */}
            <div
                style={{
                    position: 'fixed', top: 0, right: 0,
                    height: '100vh', width: '600px',
                    zIndex: 9999,
                    display: 'flex', flexDirection: 'column',
                    background: 'linear-gradient(180deg, #0D1929 0%, #080F1C 100%)',
                    borderLeft: '1px solid rgba(14,165,233,0.2)',
                    boxShadow: '-8px 0 40px rgba(0,0,0,0.7)',
                    transform: open ? 'translateX(0)' : 'translateX(100%)',
                    transition: 'transform 0.25s cubic-bezier(0.4,0,0.2,1)',
                }}
            >
                {/* Header */}
                <div style={{
                    display: 'flex', alignItems: 'center', justifyContent: 'space-between',
                    padding: '1rem 1.25rem',
                    borderBottom: '1px solid rgba(14,165,233,0.15)',
                    flexShrink: 0,
                }}>
                    <div style={{ display: 'flex', alignItems: 'center', gap: '0.625rem' }}>
                        <ListTodo style={{ width: '18px', height: '18px', color: '#0EA5E9' }} />
                        <span style={{ fontFamily: 'monospace', fontSize: '0.95rem', fontWeight: '700', color: '#E2E8F0', textTransform: 'uppercase', letterSpacing: '0.08em' }}>
                            Ivanti Queue
                        </span>
                        {pendingCount > 0 && (
                            <span style={{
                                display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
                                minWidth: '20px', height: '20px', padding: '0 5px',
                                background: 'rgba(14,165,233,0.2)',
                                border: '1px solid rgba(14,165,233,0.4)',
                                borderRadius: '999px',
                                fontFamily: 'monospace', fontSize: '0.65rem', fontWeight: '700', color: '#0EA5E9',
                            }}>
                                {pendingCount}
                            </span>
                        )}
                    </div>
                    <button
                        onClick={onClose}
                        style={{ background: 'none', border: 'none', cursor: 'pointer', color: '#475569', padding: '4px', lineHeight: 1 }}
                    >
                        <X style={{ width: '18px', height: '18px' }} />
                    </button>
                </div>

                {/* Body */}
                <div style={{ flex: 1, overflowY: 'auto', padding: '0.75rem 1.25rem' }}>
                    {items.length === 0 ? (
                        <div style={{ textAlign: 'center', padding: '3rem 0', fontFamily: 'monospace', fontSize: '0.75rem', color: '#334155' }}>
                            No items in queue.<br />
                            <span style={{ fontSize: '0.68rem', color: '#1E293B', marginTop: '0.5rem', display: 'block' }}>
                                Check a row in the findings table to add it.
                            </span>
                        </div>
                    ) : grouped.map(({ key, label, items: groupItems, isInventory, cardItems, graniteItems, decomItems }) => (
                        <div key={key} style={{ marginBottom: '1.25rem' }}>
                            {/* Group header */}
                            <div style={{
                                display: 'flex', alignItems: 'center', justifyContent: 'space-between',
                                padding: '0.3rem 0', marginBottom: '0.375rem',
                                borderBottom: `1px solid ${isInventory ? 'rgba(16,185,129,0.2)' : 'rgba(255,255,255,0.06)'}`,
                            }}>
                                <span style={{ fontFamily: 'monospace', fontSize: '0.68rem', fontWeight: '700', color: isInventory ? '#10B981' : '#94A3B8', textTransform: 'uppercase', letterSpacing: '0.1em' }}>
                                    {label}
                                </span>
                                <span style={{ fontFamily: 'monospace', fontSize: '0.62rem', color: '#334155' }}>
                                    {groupItems.length}
                                </span>
                            </div>

                            {/* Items — Inventory section renders CARD then GRANITE then DECOM with optional sub-dividers */}
                            {isInventory ? (
                                <>
                                    {cardItems.map((item) => (
                                        <React.Fragment key={item.id}>
                                            {renderQueueItem(item, { done: item.status === 'complete', selectedIds, toggleSelect, onUpdate, onDelete, setRedirectItem, canWrite })}
                                            {renderCardActionForm(item)}
                                        </React.Fragment>
                                    ))}
                                    {cardItems.length > 0 && graniteItems.length > 0 && (
                                        <div style={{
                                            height: '1px',
                                            background: 'rgba(161,136,127,0.18)',
                                            margin: '0.5rem 0.625rem',
                                        }} />
                                    )}
                                    {graniteItems.map((item) => renderQueueItem(item, { done: item.status === 'complete', selectedIds, toggleSelect, onUpdate, onDelete, setRedirectItem, canWrite }))}
                                    {(cardItems.length > 0 || graniteItems.length > 0) && decomItems.length > 0 && (
                                        <div style={{
                                            height: '1px',
                                            background: 'rgba(239,68,68,0.18)',
                                            margin: '0.5rem 0.625rem',
                                        }} />
                                    )}
                                    {decomItems.map((item) => renderQueueItem(item, { done: item.status === 'complete', selectedIds, toggleSelect, onUpdate, onDelete, setRedirectItem, canWrite }))}
                                </>
                            ) : (
                                groupItems.map((item) => renderQueueItem(item, { done: item.status === 'complete', selectedIds, toggleSelect, onUpdate, onDelete, setRedirectItem, canWrite }))
                            )}
                        </div>
                    ))}
                </div>

                {/* CARD Asset Search section */}
                {cardConfigured && (
                    <div style={{ padding: '0 1.25rem 0.75rem' }}>
                        <div
                            onClick={() => setAssetSearchOpen(!assetSearchOpen)}
                            style={{
                                display: 'flex', alignItems: 'center', justifyContent: 'space-between',
                                padding: '0.3rem 0', marginBottom: '0.375rem',
                                borderBottom: '1px solid rgba(14,165,233,0.2)',
                                cursor: 'pointer',
                            }}
                        >
                            <div style={{ display: 'flex', alignItems: 'center', gap: '0.375rem' }}>
                                <Database style={{ width: '12px', height: '12px', color: '#0EA5E9' }} />
                                <span style={{ fontFamily: 'monospace', fontSize: '0.68rem', fontWeight: '700', color: '#0EA5E9', textTransform: 'uppercase', letterSpacing: '0.1em' }}>
                                    CARD Asset Search
                                </span>
                            </div>
                            {assetSearchOpen
                                ? <ChevronUp style={{ width: '14px', height: '14px', color: '#475569' }} />
                                : <ChevronDown style={{ width: '14px', height: '14px', color: '#475569' }} />
                            }
                        </div>

                        {assetSearchOpen && (
                            <div style={{ display: 'flex', flexDirection: 'column', gap: '0.375rem' }}>
                                <select
                                    value={assetSearchTeam}
                                    onChange={(e) => { setAssetSearchTeam(e.target.value); setAssetSearchResults(null); setAssetSearchError(null); }}
                                    style={{
                                        width: '100%', boxSizing: 'border-box',
                                        background: 'rgba(15, 23, 42, 0.6)',
                                        border: '1px solid rgba(14, 165, 233, 0.25)',
                                        borderRadius: '0.25rem',
                                        padding: '0.35rem 0.5rem',
                                        fontFamily: "'JetBrains Mono', monospace", fontSize: '0.7rem',
                                        color: '#E2E8F0',
                                        outline: 'none',
                                    }}
                                >
                                    <option value="">Select team…</option>
                                    {cardTeams.map(t => <option key={t} value={t}>{t}</option>)}
                                </select>
                                <select
                                    value={assetSearchDisposition}
                                    onChange={(e) => { setAssetSearchDisposition(e.target.value); setAssetSearchResults(null); setAssetSearchError(null); }}
                                    style={{
                                        width: '100%', boxSizing: 'border-box',
                                        background: 'rgba(15, 23, 42, 0.6)',
                                        border: '1px solid rgba(14, 165, 233, 0.25)',
                                        borderRadius: '0.25rem',
                                        padding: '0.35rem 0.5rem',
                                        fontFamily: "'JetBrains Mono', monospace", fontSize: '0.7rem',
                                        color: '#E2E8F0',
                                        outline: 'none',
                                    }}
                                >
                                    <option value="confirmed">Confirmed</option>
                                    <option value="unconfirmed">Unconfirmed</option>
                                    <option value="declined">Declined</option>
                                    <option value="candidate">Candidate</option>
                                </select>
                                <button
                                    onClick={() => handleAssetSearch(1)}
                                    disabled={!assetSearchTeam || assetSearchLoading}
                                    style={{
                                        padding: '0.35rem',
                                        background: assetSearchTeam ? 'rgba(14,165,233,0.12)' : 'transparent',
                                        border: `1px solid ${assetSearchTeam ? 'rgba(14,165,233,0.35)' : 'rgba(255,255,255,0.05)'}`,
                                        borderRadius: '0.25rem',
                                        color: assetSearchTeam ? '#0EA5E9' : '#334155',
                                        fontFamily: 'monospace', fontSize: '0.68rem', fontWeight: '600',
                                        cursor: assetSearchTeam ? 'pointer' : 'not-allowed',
                                        textTransform: 'uppercase', letterSpacing: '0.05em',
                                        display: 'flex', alignItems: 'center', justifyContent: 'center', gap: '0.375rem',
                                    }}
                                >
                                    {assetSearchLoading
                                        ? <Loader style={{ width: '12px', height: '12px', animation: 'spin 1s linear infinite' }} />
                                        : <Search style={{ width: '12px', height: '12px' }} />
                                    }
                                    {assetSearchLoading ? 'Searching…' : 'Search'}
                                </button>

                                {/* Error */}
                                {assetSearchError && (
                                    <div style={{
                                        display: 'flex', alignItems: 'center', gap: '0.375rem',
                                        padding: '0.3rem 0.5rem',
                                        background: 'rgba(239, 68, 68, 0.1)',
                                        border: '1px solid rgba(239, 68, 68, 0.25)',
                                        borderRadius: '0.25rem',
                                    }}>
                                        <AlertCircle style={{ width: '12px', height: '12px', color: '#EF4444', flexShrink: 0 }} />
                                        <span style={{ fontFamily: 'monospace', fontSize: '0.62rem', color: '#FCA5A5' }}>{assetSearchError}</span>
                                    </div>
                                )}

                                {/* Results */}
                                {assetSearchResults && (
                                    <div>
                                        <div style={{
                                            fontFamily: 'monospace', fontSize: '0.62rem', fontWeight: '600',
                                            color: '#64748B', textTransform: 'uppercase', letterSpacing: '0.08em',
                                            marginBottom: '0.25rem',
                                        }}>
                                            {assetSearchResults.total != null ? `${assetSearchResults.total} asset${assetSearchResults.total !== 1 ? 's' : ''} found` : 'Results'}
                                        </div>

                                        {/* Results table */}
                                        {Array.isArray(assetSearchResults.assets) && assetSearchResults.assets.length > 0 ? (
                                            <div style={{
                                                maxHeight: '200px', overflowY: 'auto',
                                                border: '1px solid rgba(14,165,233,0.12)',
                                                borderRadius: '0.25rem',
                                            }}>
                                                <table style={{
                                                    width: '100%', borderCollapse: 'collapse',
                                                    fontFamily: "'JetBrains Mono', monospace", fontSize: '0.62rem',
                                                }}>
                                                    <thead>
                                                        <tr style={{ background: 'rgba(14,165,233,0.06)' }}>
                                                            <th style={{ padding: '0.3rem 0.5rem', textAlign: 'left', color: '#94A3B8', fontWeight: '700', textTransform: 'uppercase', letterSpacing: '0.06em', borderBottom: '1px solid rgba(14,165,233,0.12)' }}>
                                                                Asset ID
                                                            </th>
                                                            {assetSearchResults.assets[0] && Object.keys(assetSearchResults.assets[0]).filter(k => k !== 'asset_id' && k !== '_id').slice(0, 3).map(k => (
                                                                <th key={k} style={{ padding: '0.3rem 0.5rem', textAlign: 'left', color: '#94A3B8', fontWeight: '700', textTransform: 'uppercase', letterSpacing: '0.06em', borderBottom: '1px solid rgba(14,165,233,0.12)' }}>
                                                                    {k.replace(/_/g, ' ')}
                                                                </th>
                                                            ))}
                                                        </tr>
                                                    </thead>
                                                    <tbody>
                                                        {assetSearchResults.assets.map((asset, idx) => {
                                                            const extraKeys = Object.keys(asset).filter(k => k !== 'asset_id' && k !== '_id').slice(0, 3);
                                                            return (
                                                                <tr key={asset.asset_id || asset._id || idx} style={{ borderBottom: '1px solid rgba(255,255,255,0.03)' }}>
                                                                    <td style={{ padding: '0.25rem 0.5rem', color: '#CBD5E1', fontWeight: '600' }}>
                                                                        {asset.asset_id || asset._id || '—'}
                                                                    </td>
                                                                    {extraKeys.map(k => (
                                                                        <td key={k} style={{ padding: '0.25rem 0.5rem', color: '#94A3B8' }}>
                                                                            {typeof asset[k] === 'object' ? JSON.stringify(asset[k]) : String(asset[k] ?? '—')}
                                                                        </td>
                                                                    ))}
                                                                </tr>
                                                            );
                                                        })}
                                                    </tbody>
                                                </table>
                                            </div>
                                        ) : (
                                            <div style={{ fontFamily: 'monospace', fontSize: '0.65rem', color: '#475569', padding: '0.5rem 0' }}>
                                                No assets found.
                                            </div>
                                        )}

                                        {/* Pagination */}
                                        {assetSearchResults.total != null && assetSearchResults.total > (assetSearchResults.page_size || 50) && (
                                            <div style={{
                                                display: 'flex', alignItems: 'center', justifyContent: 'center', gap: '0.5rem',
                                                marginTop: '0.375rem',
                                            }}>
                                                <button
                                                    onClick={() => handleAssetSearch(assetSearchPage - 1)}
                                                    disabled={assetSearchPage <= 1 || assetSearchLoading}
                                                    style={{
                                                        padding: '0.2rem 0.4rem',
                                                        background: assetSearchPage > 1 ? 'rgba(14,165,233,0.08)' : 'transparent',
                                                        border: `1px solid ${assetSearchPage > 1 ? 'rgba(14,165,233,0.25)' : 'rgba(255,255,255,0.05)'}`,
                                                        borderRadius: '0.2rem',
                                                        color: assetSearchPage > 1 ? '#0EA5E9' : '#334155',
                                                        fontFamily: 'monospace', fontSize: '0.6rem', fontWeight: '600',
                                                        cursor: assetSearchPage > 1 ? 'pointer' : 'not-allowed',
                                                    }}
                                                >
                                                    ← Prev
                                                </button>
                                                <span style={{ fontFamily: 'monospace', fontSize: '0.6rem', color: '#64748B' }}>
                                                    Page {assetSearchPage} of {Math.ceil(assetSearchResults.total / (assetSearchResults.page_size || 50))}
                                                </span>
                                                <button
                                                    onClick={() => handleAssetSearch(assetSearchPage + 1)}
                                                    disabled={assetSearchPage >= Math.ceil(assetSearchResults.total / (assetSearchResults.page_size || 50)) || assetSearchLoading}
                                                    style={{
                                                        padding: '0.2rem 0.4rem',
                                                        background: assetSearchPage < Math.ceil(assetSearchResults.total / (assetSearchResults.page_size || 50)) ? 'rgba(14,165,233,0.08)' : 'transparent',
                                                        border: `1px solid ${assetSearchPage < Math.ceil(assetSearchResults.total / (assetSearchResults.page_size || 50)) ? 'rgba(14,165,233,0.25)' : 'rgba(255,255,255,0.05)'}`,
                                                        borderRadius: '0.2rem',
                                                        color: assetSearchPage < Math.ceil(assetSearchResults.total / (assetSearchResults.page_size || 50)) ? '#0EA5E9' : '#334155',
                                                        fontFamily: 'monospace', fontSize: '0.6rem', fontWeight: '600',
                                                        cursor: assetSearchPage < Math.ceil(assetSearchResults.total / (assetSearchResults.page_size || 50)) ? 'pointer' : 'not-allowed',
                                                    }}
                                                >
                                                    Next →
                                                </button>
                                            </div>
                                        )}
                                    </div>
                                )}
                            </div>
                        )}
                    </div>
                )}

                {/* Submissions section */}
                {fpSubmissions && fpSubmissions.length > 0 && (
                    <div style={{ padding: '0 1.25rem 0.75rem' }}>
                        <div
                            onClick={toggleSubmissionsCollapsed}
                            style={{
                                display: 'flex', alignItems: 'center', justifyContent: 'space-between',
                                padding: '0.3rem 0', marginBottom: '0.375rem',
                                borderBottom: '1px solid rgba(245,158,11,0.2)',
                                cursor: 'pointer', userSelect: 'none',
                            }}
                        >
                            <div style={{ display: 'flex', alignItems: 'center', gap: '0.4rem' }}>
                                {submissionsCollapsed
                                    ? <ChevronUp style={{ width: '12px', height: '12px', color: '#F59E0B' }} />
                                    : <ChevronDown style={{ width: '12px', height: '12px', color: '#F59E0B' }} />
                                }
                                <span style={{ fontFamily: 'monospace', fontSize: '0.68rem', fontWeight: '700', color: '#F59E0B', textTransform: 'uppercase', letterSpacing: '0.1em' }}>
                                    Submissions
                                </span>
                            </div>
                            <span style={{ fontFamily: 'monospace', fontSize: '0.62rem', color: '#334155' }}>
                                {fpSubmissions.length}
                            </span>
                        </div>
                        {dismissError && (
                            <div style={{ fontFamily: 'monospace', fontSize: '0.62rem', color: '#EF4444', marginBottom: '0.375rem', padding: '0.25rem 0.5rem', background: 'rgba(239,68,68,0.08)', borderRadius: '0.25rem' }}>
                                {dismissError}
                            </div>
                        )}
                        {!submissionsCollapsed && fpSubmissions.map((sub) => {
                            const lsBadge = lifecycleStatusBadge(sub.lifecycle_status);
                            const findingCount = (() => {
                                try { return JSON.parse(sub.finding_ids_json || '[]').length; } catch { return 0; }
                            })();
                            const clickable = canWrite && onEditSubmission;
                            const showDismiss = sub.lifecycle_status === 'rejected' && !sub.dismissed_at;
                            return (
                                <div
                                    key={sub.id}
                                    onClick={clickable ? () => onEditSubmission(sub) : undefined}
                                    style={{
                                        display: 'flex', alignItems: 'center', gap: '0.5rem',
                                        padding: '0.45rem 0.625rem',
                                        marginBottom: '0.25rem',
                                        borderRadius: '0.375rem',
                                        background: 'rgba(245,158,11,0.04)',
                                        border: '1px solid rgba(245,158,11,0.1)',
                                        cursor: clickable ? 'pointer' : 'default',
                                        transition: 'all 0.15s',
                                    }}
                                    onMouseEnter={clickable ? (e) => {
                                        e.currentTarget.style.borderColor = 'rgba(245,158,11,0.3)';
                                        e.currentTarget.style.background = 'rgba(245,158,11,0.08)';
                                    } : undefined}
                                    onMouseLeave={clickable ? (e) => {
                                        e.currentTarget.style.borderColor = 'rgba(245,158,11,0.1)';
                                        e.currentTarget.style.background = 'rgba(245,158,11,0.04)';
                                    } : undefined}
                                >
                                    <div style={{ flex: 1, minWidth: 0 }}>
                                        <div style={{
                                            fontFamily: 'monospace', fontSize: '0.72rem', fontWeight: '600',
                                            color: '#CBD5E1',
                                            overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap',
                                        }} title={sub.workflow_name}>
                                            {sub.workflow_name || `Batch ${sub.ivanti_workflow_batch_id}`}
                                        </div>
                                        <div style={{ display: 'flex', alignItems: 'center', gap: '0.5rem', marginTop: '2px' }}>
                                            <span style={{ fontFamily: 'monospace', fontSize: '0.62rem', color: '#64748B' }}>
                                                #{sub.ivanti_workflow_batch_id}
                                            </span>
                                            <span style={{ fontFamily: 'monospace', fontSize: '0.62rem', color: '#475569' }}>
                                                {findingCount} finding{findingCount !== 1 ? 's' : ''}
                                            </span>
                                            <span style={{ fontFamily: 'monospace', fontSize: '0.62rem', color: '#334155' }}>
                                                {sub.created_at ? new Date(sub.created_at).toLocaleDateString() : ''}
                                            </span>
                                        </div>
                                    </div>
                                    <span style={{
                                        flexShrink: 0,
                                        padding: '0.1rem 0.35rem',
                                        borderRadius: '0.2rem',
                                        background: lsBadge.bg,
                                        border: `1px solid ${lsBadge.border}`,
                                        color: lsBadge.text,
                                        fontFamily: 'monospace', fontSize: '0.6rem', fontWeight: '700',
                                        textTransform: 'uppercase', letterSpacing: '0.04em',
                                    }}>
                                        {sub.lifecycle_status || 'submitted'}
                                    </span>
                                    {showDismiss && (
                                        <button
                                            onClick={(e) => handleDismiss(e, sub.id)}
                                            title="Dismiss rejected submission"
                                            style={{
                                                flexShrink: 0, display: 'flex', alignItems: 'center', justifyContent: 'center',
                                                width: '20px', height: '20px',
                                                background: 'rgba(239,68,68,0.08)',
                                                border: '1px solid rgba(239,68,68,0.2)',
                                                borderRadius: '0.2rem',
                                                cursor: 'pointer',
                                                padding: 0,
                                                transition: 'all 0.15s',
                                            }}
                                            onMouseEnter={(e) => {
                                                e.currentTarget.style.background = 'rgba(239,68,68,0.2)';
                                                e.currentTarget.style.borderColor = 'rgba(239,68,68,0.4)';
                                            }}
                                            onMouseLeave={(e) => {
                                                e.currentTarget.style.background = 'rgba(239,68,68,0.08)';
                                                e.currentTarget.style.borderColor = 'rgba(239,68,68,0.2)';
                                            }}
                                        >
                                            <X style={{ width: '12px', height: '12px', color: '#EF4444' }} />
                                        </button>
                                    )}
                                </div>
                            );
                        })}
                    </div>
                )}

                {/* Footer */}
                <div style={{
                    padding: '0.75rem 1.25rem',
                    borderTop: '1px solid rgba(255,255,255,0.06)',
                    flexShrink: 0,
                    display: 'flex', gap: '0.5rem',
                }}>
                    {/* Create FP Workflow — visible for editor/admin only */}
                    {canWrite && (() => {
                        const fpEnabled = isCreateFpButtonEnabled(items, selectedIds);
                        return (
                            <button
                                onClick={() => onCreateFpWorkflow([...selectedIds])}
                                disabled={!fpEnabled}
                                title={!fpEnabled ? 'Select pending FP items to create a workflow' : ''}
                                style={{
                                    flex: 1, padding: '0.45rem',
                                    background: fpEnabled ? 'rgba(245,158,11,0.12)' : 'transparent',
                                    border: `1px solid ${fpEnabled ? 'rgba(245,158,11,0.35)' : 'rgba(255,255,255,0.05)'}`,
                                    borderRadius: '0.375rem',
                                    color: fpEnabled ? '#F59E0B' : '#334155',
                                    fontFamily: 'monospace', fontSize: '0.72rem', fontWeight: '600',
                                    cursor: fpEnabled ? 'pointer' : 'not-allowed',
                                    textTransform: 'uppercase', letterSpacing: '0.05em',
                                    transition: 'all 0.12s',
                                }}
                            >
                                Create FP Workflow
                            </button>
                        );
                    })()}
                    {/* Delete selected — only shown when items are selected */}
                    {selectedIds.size > 0 && (
                        <button
                            onClick={() => {
                                // Get selected pending items for consolidated Jira ticket
                                const selectedPendingItems = items.filter(i => selectedIds.has(i.id) && i.status === 'pending');
                                if (selectedPendingItems.length >= 2) {
                                    setShowConsolidationModal(true);
                                } else if (selectedPendingItems.length === 1) {
                                    openCreateJiraFromQueue(selectedPendingItems[0]);
                                }
                            }}
                            disabled={(() => {
                                const selectedPendingItems = items.filter(i => selectedIds.has(i.id) && i.status === 'pending');
                                return selectedPendingItems.length === 0;
                            })()}
                            style={{
                                flex: 1, padding: '0.45rem',
                                background: 'rgba(14,165,233,0.1)',
                                border: '1px solid rgba(14,165,233,0.35)',
                                borderRadius: '0.375rem',
                                color: '#0EA5E9',
                                fontFamily: 'monospace', fontSize: '0.72rem', fontWeight: '600',
                                cursor: 'pointer',
                                textTransform: 'uppercase', letterSpacing: '0.05em',
                                transition: 'all 0.12s',
                            }}
                            title={`Create a single Jira ticket from ${selectedIds.size} selected item(s)`}
                        >
                            + Jira ({items.filter(i => selectedIds.has(i.id) && i.status === 'pending').length})
                        </button>
                    )}
                    {selectedIds.size > 0 && (
                        <button
                            onClick={handleDeleteSelected}
                            style={{
                                flex: 1, padding: '0.45rem',
                                background: 'rgba(239,68,68,0.1)',
                                border: '1px solid rgba(239,68,68,0.35)',
                                borderRadius: '0.375rem',
                                color: '#EF4444',
                                fontFamily: 'monospace', fontSize: '0.72rem', fontWeight: '600',
                                cursor: 'pointer',
                                textTransform: 'uppercase', letterSpacing: '0.05em',
                                transition: 'all 0.12s',
                            }}
                        >
                            Delete ({selectedIds.size})
                        </button>
                    )}
                    <button
                        onClick={onClearCompleted}
                        disabled={completedCount === 0}
                        style={{
                            flex: 1, padding: '0.45rem',
                            background: completedCount > 0 ? 'rgba(16,185,129,0.08)' : 'transparent',
                            border: `1px solid ${completedCount > 0 ? 'rgba(16,185,129,0.25)' : 'rgba(255,255,255,0.05)'}`,
                            borderRadius: '0.375rem',
                            color: completedCount > 0 ? '#10B981' : '#334155',
                            fontFamily: 'monospace', fontSize: '0.72rem', fontWeight: '600',
                            cursor: completedCount > 0 ? 'pointer' : 'not-allowed',
                            textTransform: 'uppercase', letterSpacing: '0.05em',
                            transition: 'all 0.12s',
                        }}
                    >
                        Clear Completed {completedCount > 0 ? `(${completedCount})` : ''}
                    </button>
                </div>
            </div>

            {/* Redirect success notification */}
            {redirectSuccess && (
                <div style={{
                    position: 'fixed', top: '1rem', right: '440px',
                    zIndex: 10001,
                    display: 'flex', alignItems: 'center', gap: '0.5rem',
                    padding: '0.5rem 1rem',
                    background: 'rgba(16, 185, 129, 0.15)',
                    border: '1px solid rgba(16, 185, 129, 0.4)',
                    borderRadius: '0.375rem',
                    boxShadow: '0 4px 12px rgba(0, 0, 0, 0.4)',
                    fontFamily: "'JetBrains Mono', monospace",
                    fontSize: '0.75rem', fontWeight: '600',
                    color: '#10B981',
                }}>
                    <Check style={{ width: '14px', height: '14px' }} />
                    {redirectSuccess}
                </div>
            )}

            {/* Consolidated Jira ticket success notification */}
            {consolidationSuccess && (
                <div style={{
                    position: 'fixed', top: '1rem', right: '440px',
                    zIndex: 10001,
                    display: 'flex', alignItems: 'center', gap: '0.5rem',
                    padding: '0.625rem 1rem',
                    background: 'rgba(14, 165, 233, 0.15)',
                    border: '1px solid rgba(14, 165, 233, 0.4)',
                    borderRadius: '0.375rem',
                    boxShadow: '0 4px 12px rgba(0, 0, 0, 0.4)',
                    fontFamily: "'JetBrains Mono', monospace",
                    fontSize: '0.75rem', fontWeight: '600',
                    color: '#0EA5E9',
                }}>
                    <Check style={{ width: '14px', height: '14px' }} />
                    Jira ticket created:&nbsp;
                    <a
                        href={consolidationSuccess.jira_url}
                        target="_blank"
                        rel="noopener noreferrer"
                        style={{ color: '#7DD3FC', textDecoration: 'underline' }}
                    >
                        {consolidationSuccess.ticket_key}
                    </a>
                </div>
            )}

            {/* Redirect modal */}
            {redirectItem && (
                <RedirectModal
                    item={redirectItem}
                    onClose={() => setRedirectItem(null)}
                    onRedirect={handleRedirectSuccess}
                />
            )}

            {/* Create Jira Ticket modal */}
            {createJiraOpen && (
                <div style={{ position: 'fixed', inset: 0, zIndex: 10100, display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
                    <div style={{ position: 'fixed', inset: 0, background: 'rgba(0,0,0,0.7)', backdropFilter: 'blur(4px)' }} onClick={() => setCreateJiraOpen(false)} />
                    <div style={{
                        position: 'relative',
                        background: 'linear-gradient(135deg, #1E293B, #0F172A)',
                        border: '1px solid rgba(14, 165, 233, 0.25)',
                        borderRadius: '16px',
                        padding: '2rem',
                        width: '90%',
                        maxWidth: '520px',
                        maxHeight: '85vh',
                        overflowY: 'auto',
                        zIndex: 10101,
                    }}>
                        <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '1.25rem' }}>
                            <h3 style={{ margin: 0, color: '#F8FAFC', fontSize: '1rem' }}>Create Issue in Jira</h3>
                            <button onClick={() => setCreateJiraOpen(false)} style={{ background: 'none', border: 'none', color: '#94A3B8', cursor: 'pointer' }}><X size={18} /></button>
                        </div>
                        <p style={{ fontSize: '0.8rem', color: '#94A3B8', marginTop: 0, marginBottom: '1rem' }}>
                            Creates a new Jira issue from this Ivanti queue item.
                        </p>
                        {createJiraError && <div style={{ color: '#FCA5A5', fontSize: '0.85rem', marginBottom: '0.75rem', padding: '0.5rem', background: 'rgba(239,68,68,0.08)', border: '1px solid rgba(239,68,68,0.2)', borderRadius: '0.375rem' }}>{createJiraError}</div>}
                        <div style={{ display: 'flex', flexDirection: 'column', gap: '0.75rem' }}>
                            <div>
                                <label style={{ fontSize: '0.75rem', color: '#94A3B8', display: 'block', marginBottom: '0.25rem' }}>Summary <span style={{ color: '#F59E0B' }}>*</span></label>
                                <input
                                    style={{
                                        background: 'rgba(15, 23, 42, 0.8)',
                                        border: `1px solid ${createJiraSummaryError ? 'rgba(239, 68, 68, 0.6)' : 'rgba(14, 165, 233, 0.2)'}`,
                                        borderRadius: '8px',
                                        padding: '0.5rem 0.75rem',
                                        color: '#F8FAFC',
                                        fontSize: '0.85rem',
                                        width: '100%',
                                        outline: 'none',
                                        boxSizing: 'border-box',
                                    }}
                                    placeholder="Issue summary (max 255 chars)"
                                    value={createJiraForm.summary}
                                    onChange={e => { setCreateJiraForm(f => ({ ...f, summary: e.target.value })); if (createJiraSummaryError) setCreateJiraSummaryError(null); }}
                                    maxLength={255}
                                />
                                {createJiraSummaryError && <div style={{ color: '#FCA5A5', fontSize: '0.75rem', marginTop: '0.25rem' }}>{createJiraSummaryError}</div>}
                            </div>
                            <div>
                                <label style={{ fontSize: '0.75rem', color: '#94A3B8', display: 'block', marginBottom: '0.25rem' }}>CVE ID (optional)</label>
                                <input
                                    style={{
                                        background: 'rgba(15, 23, 42, 0.8)',
                                        border: '1px solid rgba(14, 165, 233, 0.2)',
                                        borderRadius: '8px',
                                        padding: '0.5rem 0.75rem',
                                        color: '#F8FAFC',
                                        fontSize: '0.85rem',
                                        width: '100%',
                                        outline: 'none',
                                        boxSizing: 'border-box',
                                    }}
                                    placeholder="e.g. CVE-2024-12345"
                                    value={createJiraForm.cve_id}
                                    onChange={e => setCreateJiraForm(f => ({ ...f, cve_id: e.target.value }))}
                                />
                            </div>
                            <div>
                                <label style={{ fontSize: '0.75rem', color: '#94A3B8', display: 'block', marginBottom: '0.25rem' }}>Vendor (optional)</label>
                                <input
                                    style={{
                                        background: 'rgba(15, 23, 42, 0.8)',
                                        border: '1px solid rgba(14, 165, 233, 0.2)',
                                        borderRadius: '8px',
                                        padding: '0.5rem 0.75rem',
                                        color: '#F8FAFC',
                                        fontSize: '0.85rem',
                                        width: '100%',
                                        outline: 'none',
                                        boxSizing: 'border-box',
                                    }}
                                    placeholder="e.g. Microsoft"
                                    value={createJiraForm.vendor}
                                    onChange={e => setCreateJiraForm(f => ({ ...f, vendor: e.target.value }))}
                                />
                            </div>
                            <div>
                                <label style={{ fontSize: '0.75rem', color: '#94A3B8', display: 'block', marginBottom: '0.25rem' }}>Source Context</label>
                                <select
                                    style={{
                                        background: 'rgba(15, 23, 42, 0.8)',
                                        border: '1px solid rgba(14, 165, 233, 0.2)',
                                        borderRadius: '8px',
                                        padding: '0.5rem 0.75rem',
                                        color: '#F8FAFC',
                                        fontSize: '0.85rem',
                                        width: '100%',
                                        outline: 'none',
                                        boxSizing: 'border-box',
                                        cursor: 'not-allowed',
                                        opacity: 0.7,
                                    }}
                                    value={createJiraForm.source_context}
                                    disabled
                                >
                                    <option value="ivanti_queue">Ivanti Queue</option>
                                </select>
                            </div>
                            <div>
                                <label style={{ fontSize: '0.75rem', color: '#94A3B8', display: 'block', marginBottom: '0.25rem' }}>Description (optional)</label>
                                <textarea
                                    style={{
                                        background: 'rgba(15, 23, 42, 0.8)',
                                        border: '1px solid rgba(14, 165, 233, 0.2)',
                                        borderRadius: '8px',
                                        padding: '0.5rem 0.75rem',
                                        color: '#F8FAFC',
                                        fontSize: '0.85rem',
                                        width: '100%',
                                        outline: 'none',
                                        boxSizing: 'border-box',
                                        minHeight: '80px',
                                        resize: 'vertical',
                                    }}
                                    placeholder="Detailed description..."
                                    value={createJiraForm.description}
                                    onChange={e => setCreateJiraForm(f => ({ ...f, description: e.target.value }))}
                                />
                            </div>
                            <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '0.75rem' }}>
                                <div>
                                    <label style={{ fontSize: '0.75rem', color: '#94A3B8', display: 'block', marginBottom: '0.25rem' }}>Project Key (optional)</label>
                                    <input
                                        style={{
                                            background: 'rgba(15, 23, 42, 0.8)',
                                            border: '1px solid rgba(14, 165, 233, 0.2)',
                                            borderRadius: '8px',
                                            padding: '0.5rem 0.75rem',
                                            color: '#F8FAFC',
                                            fontSize: '0.85rem',
                                            width: '100%',
                                            outline: 'none',
                                            boxSizing: 'border-box',
                                        }}
                                        placeholder="Uses .env default"
                                        value={createJiraForm.project_key}
                                        onChange={e => setCreateJiraForm(f => ({ ...f, project_key: e.target.value.toUpperCase() }))}
                                    />
                                </div>
                                <div>
                                    <label style={{ fontSize: '0.75rem', color: '#94A3B8', display: 'block', marginBottom: '0.25rem' }}>Issue Type (optional)</label>
                                    <input
                                        style={{
                                            background: 'rgba(15, 23, 42, 0.8)',
                                            border: '1px solid rgba(14, 165, 233, 0.2)',
                                            borderRadius: '8px',
                                            padding: '0.5rem 0.75rem',
                                            color: '#F8FAFC',
                                            fontSize: '0.85rem',
                                            width: '100%',
                                            outline: 'none',
                                            boxSizing: 'border-box',
                                        }}
                                        placeholder="Task"
                                        value={createJiraForm.issue_type}
                                        onChange={e => setCreateJiraForm(f => ({ ...f, issue_type: e.target.value }))}
                                    />
                                </div>
                            </div>
                            <button
                                style={{
                                    padding: '0.5rem 1rem',
                                    borderRadius: '8px',
                                    border: '1px solid rgba(16, 185, 129, 0.3)',
                                    background: 'rgba(16, 185, 129, 0.1)',
                                    color: '#6EE7B7',
                                    cursor: createJiraSaving ? 'not-allowed' : 'pointer',
                                    fontSize: '0.8rem',
                                    fontWeight: 600,
                                    display: 'inline-flex',
                                    alignItems: 'center',
                                    justifyContent: 'center',
                                    gap: '0.4rem',
                                    transition: 'all 0.2s',
                                    marginTop: '0.5rem',
                                    width: '100%',
                                }}
                                onClick={submitCreateJira}
                                disabled={createJiraSaving}
                            >
                                {createJiraSaving ? <Loader size={14} style={{ animation: 'spin 1s linear infinite' }} /> : <Plus size={14} />}
                                Create in Jira
                            </button>
                        </div>
                    </div>
                </div>
            )}

            {/* Consolidated Jira Ticket modal (multi-item → 1 ticket) */}
            {showConsolidationModal && (
                <ConsolidationModal
                    items={items.filter(i => selectedIds.has(i.id) && i.status === 'pending')}
                    onClose={() => setShowConsolidationModal(false)}
                    onSuccess={(ticketData) => {
                        setShowConsolidationModal(false);
                        setSelectedIds(new Set());
                        setConsolidationSuccess({ ticket_key: ticketData.ticket_key, jira_url: ticketData.jira_url });
                        setTimeout(() => setConsolidationSuccess(null), 8000);
                        if (onQueueRefresh) onQueueRefresh();
                    }}
                />
            )}
        </>
    );
}

// ---------------------------------------------------------------------------
// FP Workflow helpers (pure functions, exported for testing)
// ---------------------------------------------------------------------------
function isCreateFpButtonEnabled(items, selectedIds) {
    return items.some(item =>
        selectedIds.has(item.id) &&
        item.workflow_type === 'FP' &&
        item.status === 'pending'
    );
}

function filterFpItems(items) {
    return items.filter(item => item.workflow_type === 'FP');
}



// ---------------------------------------------------------------------------
// FpWorkflowModal — submit FP workflows to Ivanti API
// ---------------------------------------------------------------------------
const ALLOWED_EXTENSIONS = ['.pdf', '.png', '.jpg', '.jpeg', '.gif', '.doc', '.docx', '.xlsx', '.csv', '.txt', '.zip'];
const MAX_FILE_SIZE = 10 * 1024 * 1024; // 10 MB

// ---------------------------------------------------------------------------
// AttachmentSourcePicker — shared component for local + library attachments
// ---------------------------------------------------------------------------
function AttachmentSourcePicker({ files, onFilesChange, libraryDocs, onLibraryDocsChange, disabled }) {
    const [mode, setMode] = useState('local');
    const [searchQuery, setSearchQuery] = useState('');
    const [searchResults, setSearchResults] = useState([]);
    const [searching, setSearching] = useState(false);
    const [searchError, setSearchError] = useState(null);
    const [fileErrors, setFileErrors] = useState(null);
    const fileInputRef = useRef(null);
    const dropRef = useRef(null);
    const debounceRef = useRef(null);

    // Format file size helper
    const formatSize = (bytes) => {
        const n = Number(bytes);
        if (isNaN(n) || n < 0) return '0 B';
        if (n < 1024) return n + ' B';
        if (n < 1024 * 1024) return (n / 1024).toFixed(1) + ' KB';
        return (n / (1024 * 1024)).toFixed(1) + ' MB';
    };

    // File validation
    const isAllowedExtension = (filename) => {
        const ext = '.' + filename.split('.').pop().toLowerCase();
        return ALLOWED_EXTENSIONS.includes(ext);
    };

    const addFiles = (newFiles) => {
        if (disabled) return;
        const errors = [];
        const valid = [];
        Array.from(newFiles).forEach(f => {
            if (!isAllowedExtension(f.name)) {
                errors.push(`"${f.name}" — file type not allowed. Accepted: ${ALLOWED_EXTENSIONS.join(', ')}`);
            } else if (f.size > MAX_FILE_SIZE) {
                errors.push(`"${f.name}" — exceeds 10 MB limit`);
            } else {
                valid.push(f);
            }
        });
        if (errors.length) {
            setFileErrors(errors.join('; '));
        } else {
            setFileErrors(null);
        }
        if (valid.length) onFilesChange([...files, ...valid]);
    };

    const removeFile = (idx) => {
        if (disabled) return;
        onFilesChange(files.filter((_, i) => i !== idx));
        setFileErrors(null);
    };

    const removeLibraryDoc = (docId) => {
        if (disabled) return;
        onLibraryDocsChange(libraryDocs.filter(d => d.id !== docId));
    };

    const handleDrop = (e) => {
        e.preventDefault();
        e.stopPropagation();
        if (disabled) return;
        if (e.dataTransfer.files?.length) addFiles(e.dataTransfer.files);
    };

    const handleDragOver = (e) => { e.preventDefault(); e.stopPropagation(); };

    // Library search with debounce
    useEffect(() => {
        if (mode !== 'library') return;
        if (debounceRef.current) clearTimeout(debounceRef.current);

        debounceRef.current = setTimeout(async () => {
            setSearching(true);
            setSearchError(null);
            try {
                const url = searchQuery.trim()
                    ? `${API_BASE}/ivanti/fp-workflow/documents/search?q=${encodeURIComponent(searchQuery.trim())}`
                    : `${API_BASE}/ivanti/fp-workflow/documents/search`;
                const res = await fetch(url, { credentials: 'include' });
                if (!res.ok) throw new Error(`Search failed (${res.status})`);
                const data = await res.json();
                setSearchResults(data);
            } catch (err) {
                setSearchError(err.message || 'Failed to search documents');
                setSearchResults([]);
            } finally {
                setSearching(false);
            }
        }, 300);

        return () => { if (debounceRef.current) clearTimeout(debounceRef.current); };
    }, [searchQuery, mode]);

    const selectLibraryDoc = (doc) => {
        if (disabled) return;
        if (libraryDocs.some(d => d.id === doc.id)) return;
        onLibraryDocsChange([...libraryDocs, {
            id: doc.id,
            cve_id: doc.cve_id,
            vendor: doc.vendor,
            name: doc.name,
            file_size: doc.file_size,
            mime_type: doc.mime_type,
        }]);
    };

    const selectedIds = new Set(libraryDocs.map(d => d.id));

    // ---- Styles ----
    const tabBtnStyle = (active) => ({
        flex: 1,
        padding: '0.45rem 0.5rem',
        background: 'none',
        border: 'none',
        borderBottom: active ? '2px solid #0EA5E9' : '2px solid transparent',
        color: active ? '#0EA5E9' : '#475569',
        fontFamily: 'monospace',
        fontSize: '0.72rem',
        fontWeight: '600',
        cursor: disabled ? 'not-allowed' : 'pointer',
        textTransform: 'uppercase',
        letterSpacing: '0.05em',
        transition: 'all 0.12s',
    });

    const dropZoneStyle = {
        border: '1px dashed rgba(14,165,233,0.25)',
        borderRadius: '0.375rem',
        padding: '1rem',
        textAlign: 'center',
        cursor: disabled ? 'not-allowed' : 'pointer',
        background: 'rgba(14,165,233,0.03)',
        transition: 'border-color 0.15s',
    };

    const searchInputStyle = {
        width: '100%',
        boxSizing: 'border-box',
        background: 'rgba(14,165,233,0.05)',
        border: '1px solid rgba(14,165,233,0.2)',
        borderRadius: '0.25rem',
        padding: '0.45rem 0.6rem 0.45rem 2rem',
        color: '#CBD5E1',
        fontSize: '0.78rem',
        fontFamily: 'monospace',
        outline: 'none',
    };

    const resultItemStyle = (isSelected) => ({
        display: 'flex',
        alignItems: 'center',
        gap: '0.5rem',
        padding: '0.4rem 0.5rem',
        borderBottom: '1px solid rgba(255,255,255,0.03)',
        cursor: disabled || isSelected ? 'default' : 'pointer',
        opacity: isSelected ? 0.5 : 1,
        background: isSelected ? 'rgba(14,165,233,0.04)' : 'transparent',
        transition: 'background 0.1s',
    });

    const badgeStyle = (type) => ({
        display: 'inline-block',
        padding: '0.1rem 0.3rem',
        borderRadius: '0.15rem',
        fontFamily: 'monospace',
        fontSize: '0.58rem',
        fontWeight: '700',
        textTransform: 'uppercase',
        letterSpacing: '0.04em',
        ...(type === 'local'
            ? { background: 'rgba(14,165,233,0.15)', color: '#0EA5E9', border: '1px solid rgba(14,165,233,0.3)' }
            : { background: 'rgba(245,158,11,0.15)', color: '#F59E0B', border: '1px solid rgba(245,158,11,0.3)' }
        ),
    });

    const totalAttachments = files.length + libraryDocs.length;

    return (
        <div>
            {/* Mode toggle tabs */}
            <div style={{ display: 'flex', borderBottom: '1px solid rgba(255,255,255,0.06)', marginBottom: '0.625rem' }}>
                <button
                    style={tabBtnStyle(mode === 'local')}
                    onClick={() => !disabled && setMode('local')}
                    disabled={disabled}
                >
                    Local Upload
                </button>
                <button
                    style={tabBtnStyle(mode === 'library')}
                    onClick={() => !disabled && setMode('library')}
                    disabled={disabled}
                >
                    Library
                </button>
            </div>

            {/* Local Upload mode */}
            {mode === 'local' && (
                <div>
                    <div
                        ref={dropRef}
                        onDrop={handleDrop}
                        onDragOver={handleDragOver}
                        onClick={() => !disabled && fileInputRef.current?.click()}
                        style={dropZoneStyle}
                    >
                        <Upload size={20} style={{ color: '#475569', marginBottom: '0.35rem' }} />
                        <div style={{ fontSize: '0.75rem', color: '#64748B' }}>
                            Drop files here or click to browse
                        </div>
                        <div style={{ fontSize: '0.62rem', color: '#475569', marginTop: '0.2rem' }}>
                            Max 10 MB per file · PDF, PNG, JPG, DOC, XLSX, CSV, TXT, ZIP
                        </div>
                    </div>
                    <input
                        ref={fileInputRef}
                        type="file"
                        multiple
                        style={{ display: 'none' }}
                        onChange={e => { if (e.target.files?.length) addFiles(e.target.files); e.target.value = ''; }}
                        accept={ALLOWED_EXTENSIONS.join(',')}
                        disabled={disabled}
                    />
                    {fileErrors && (
                        <div style={{ fontSize: '0.68rem', color: '#EF4444', marginTop: '0.3rem' }}>{fileErrors}</div>
                    )}
                </div>
            )}

            {/* Library mode */}
            {mode === 'library' && (
                <div>
                    {/* Search input */}
                    <div style={{ position: 'relative', marginBottom: '0.5rem' }}>
                        <Search size={14} style={{ position: 'absolute', left: '0.5rem', top: '50%', transform: 'translateY(-50%)', color: '#475569' }} />
                        <input
                            type="text"
                            value={searchQuery}
                            onChange={e => setSearchQuery(e.target.value)}
                            placeholder="Search documents by name, CVE, or vendor..."
                            disabled={disabled}
                            style={searchInputStyle}
                        />
                        {searching && (
                            <Loader size={14} style={{ position: 'absolute', right: '0.5rem', top: '50%', transform: 'translateY(-50%)', color: '#0EA5E9', animation: 'spin 1s linear infinite' }} />
                        )}
                    </div>

                    {/* Search results */}
                    <div style={{
                        maxHeight: '200px',
                        overflowY: 'auto',
                        border: '1px solid rgba(14,165,233,0.1)',
                        borderRadius: '0.25rem',
                        background: 'rgba(15,23,42,0.5)',
                    }}>
                        {searchError && (
                            <div style={{ padding: '0.75rem', textAlign: 'center', fontSize: '0.72rem', color: '#EF4444', display: 'flex', alignItems: 'center', justifyContent: 'center', gap: '0.375rem' }}>
                                <AlertCircle size={13} />
                                {searchError}
                            </div>
                        )}
                        {!searchError && !searching && searchResults.length === 0 && (
                            <div style={{ padding: '0.75rem', textAlign: 'center', fontSize: '0.72rem', color: '#475569' }}>
                                No documents found
                            </div>
                        )}
                        {!searchError && searching && searchResults.length === 0 && (
                            <div style={{ padding: '0.75rem', textAlign: 'center', fontSize: '0.72rem', color: '#64748B', display: 'flex', alignItems: 'center', justifyContent: 'center', gap: '0.375rem' }}>
                                <Loader size={13} style={{ animation: 'spin 1s linear infinite' }} />
                                Searching...
                            </div>
                        )}
                        {!searchError && searchResults.map(doc => {
                            const isSelected = selectedIds.has(doc.id);
                            return (
                                <div
                                    key={doc.id}
                                    style={resultItemStyle(isSelected)}
                                    onClick={() => !isSelected && selectLibraryDoc(doc)}
                                >
                                    {isSelected ? (
                                        <Check size={13} style={{ color: '#10B981', flexShrink: 0 }} />
                                    ) : (
                                        <Database size={13} style={{ color: '#F59E0B', flexShrink: 0 }} />
                                    )}
                                    <div style={{ flex: 1, minWidth: 0 }}>
                                        <div style={{ fontSize: '0.72rem', color: '#CBD5E1', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', fontFamily: 'monospace' }}>
                                            {doc.name}
                                        </div>
                                        <div style={{ fontSize: '0.62rem', color: '#64748B', fontFamily: 'monospace', display: 'flex', gap: '0.5rem', marginTop: '0.1rem' }}>
                                            {doc.cve_id && <span style={{ color: '#0EA5E9' }}>{doc.cve_id}</span>}
                                            {doc.vendor && <span>{doc.vendor}</span>}
                                            <span>{formatSize(doc.file_size)}</span>
                                        </div>
                                    </div>
                                </div>
                            );
                        })}
                    </div>
                </div>
            )}

            {/* Unified attachment list */}
            {totalAttachments > 0 && (
                <div style={{ marginTop: '0.625rem' }}>
                    <div style={{
                        fontSize: '0.68rem',
                        fontWeight: '600',
                        color: '#64748B',
                        textTransform: 'uppercase',
                        letterSpacing: '0.08em',
                        marginBottom: '0.35rem',
                        fontFamily: 'monospace',
                    }}>
                        Attachments ({totalAttachments})
                    </div>
                    {/* Local files */}
                    {files.map((f, i) => (
                        <div key={`local-${i}`} style={{
                            display: 'flex',
                            alignItems: 'center',
                            gap: '0.5rem',
                            padding: '0.3rem 0.25rem',
                            borderBottom: '1px solid rgba(255,255,255,0.03)',
                        }}>
                            <span style={badgeStyle('local')}>LOCAL</span>
                            <FileText size={13} style={{ color: '#64748B', flexShrink: 0 }} />
                            <span style={{
                                flex: 1,
                                fontSize: '0.72rem',
                                color: '#CBD5E1',
                                fontFamily: 'monospace',
                                overflow: 'hidden',
                                textOverflow: 'ellipsis',
                                whiteSpace: 'nowrap',
                            }}>
                                {f.name}
                            </span>
                            <span style={{ fontSize: '0.62rem', color: '#475569', fontFamily: 'monospace', flexShrink: 0 }}>
                                {formatSize(f.size)}
                            </span>
                            <button
                                onClick={() => removeFile(i)}
                                disabled={disabled}
                                style={{
                                    background: 'none',
                                    border: 'none',
                                    color: '#64748B',
                                    cursor: disabled ? 'not-allowed' : 'pointer',
                                    padding: '0.15rem',
                                    lineHeight: 1,
                                }}
                            >
                                <Trash2 size={12} />
                            </button>
                        </div>
                    ))}
                    {/* Library docs */}
                    {libraryDocs.map(doc => (
                        <div key={`lib-${doc.id}`} style={{
                            display: 'flex',
                            alignItems: 'center',
                            gap: '0.5rem',
                            padding: '0.3rem 0.25rem',
                            borderBottom: '1px solid rgba(255,255,255,0.03)',
                        }}>
                            <span style={badgeStyle('library')}>LIBRARY</span>
                            <Database size={13} style={{ color: '#F59E0B', flexShrink: 0 }} />
                            <div style={{ flex: 1, minWidth: 0 }}>
                                <div style={{
                                    fontSize: '0.72rem',
                                    color: '#CBD5E1',
                                    fontFamily: 'monospace',
                                    overflow: 'hidden',
                                    textOverflow: 'ellipsis',
                                    whiteSpace: 'nowrap',
                                }}>
                                    {doc.name}
                                </div>
                                <div style={{ fontSize: '0.6rem', color: '#64748B', fontFamily: 'monospace', display: 'flex', gap: '0.5rem' }}>
                                    {doc.cve_id && <span style={{ color: '#0EA5E9' }}>{doc.cve_id}</span>}
                                    {doc.vendor && <span>{doc.vendor}</span>}
                                </div>
                            </div>
                            <span style={{ fontSize: '0.62rem', color: '#475569', fontFamily: 'monospace', flexShrink: 0 }}>
                                {formatSize(doc.file_size)}
                            </span>
                            <button
                                onClick={() => removeLibraryDoc(doc.id)}
                                disabled={disabled}
                                style={{
                                    background: 'none',
                                    border: 'none',
                                    color: '#64748B',
                                    cursor: disabled ? 'not-allowed' : 'pointer',
                                    padding: '0.15rem',
                                    lineHeight: 1,
                                }}
                            >
                                <Trash2 size={12} />
                            </button>
                        </div>
                    ))}
                </div>
            )}
        </div>
    );
}

function FpWorkflowModal({ open, onClose, selectedItems, onSuccess }) {
    const [name, setName] = useState('');
    const [reason, setReason] = useState('');
    const [description, setDescription] = useState('');
    const [expirationDate, setExpirationDate] = useState('');
    const [scopeOverride, setScopeOverride] = useState('Authorized');
    const [files, setFiles] = useState([]);
    const [libraryDocs, setLibraryDocs] = useState([]);
    const [submitting, setSubmitting] = useState(false);
    const [progress, setProgress] = useState({ step: '', current: 0, total: 0 });
    const [errors, setErrors] = useState({});
    const [result, setResult] = useState(null);

    // Reset form when modal opens
    useEffect(() => {
        if (open) {
            setName('');
            setReason('');
            setDescription('');
            setExpirationDate('');
            setScopeOverride('Authorized');
            setFiles([]);
            setLibraryDocs([]);
            setSubmitting(false);
            setProgress({ step: '', current: 0, total: 0 });
            setErrors({});
            setResult(null);
        }
    }, [open]);

    // Close on Escape
    useEffect(() => {
        if (!open) return;
        const handler = (e) => { if (e.key === 'Escape' && !submitting) onClose(); };
        document.addEventListener('keydown', handler);
        return () => document.removeEventListener('keydown', handler);
    }, [open, submitting, onClose]);

    const validate = () => {
        const errs = {};
        if (!name.trim()) errs.name = 'Workflow name is required';
        else if (name.trim().length > 255) errs.name = 'Name must be 255 characters or fewer';
        if (!reason.trim()) errs.reason = 'Reason is required';
        if (description.length > 2000) errs.description = 'Description must be 2000 characters or fewer';
        if (!expirationDate) errs.expirationDate = 'Expiration date is required';
        else {
            const today = new Date();
            today.setHours(0, 0, 0, 0);
            const exp = new Date(expirationDate + 'T00:00:00');
            if (exp <= today) errs.expirationDate = 'Expiration date must be in the future';
            else {
                const maxDate = new Date(today);
                maxDate.setDate(maxDate.getDate() + 120);
                if (exp > maxDate) errs.expirationDate = 'Expiration date cannot be more than 120 days from today';
            }
        }
        setErrors(errs);
        return Object.keys(errs).length === 0;
    };

    const handleSubmit = async () => {
        if (!validate()) return;
        setSubmitting(true);
        setProgress({ step: 'Creating workflow...', current: 0, total: 0 });
        setResult(null);

        try {
            const formData = new FormData();
            formData.append('name', name.trim());
            formData.append('reason', reason.trim());
            if (description.trim()) formData.append('description', description.trim());
            formData.append('expirationDate', expirationDate);
            formData.append('scopeOverride', scopeOverride);
            formData.append('findingIds', JSON.stringify(selectedItems.map(i => i.finding_id)));
            formData.append('queueItemIds', JSON.stringify(selectedItems.map(i => i.id)));
            files.forEach(f => formData.append('attachments', f));
            if (libraryDocs.length > 0) {
                formData.append('libraryDocIds', JSON.stringify(libraryDocs.map(d => d.id)));
            }

            const totalAttachments = files.length + libraryDocs.length;
            if (totalAttachments > 0) {
                setProgress({ step: 'Creating workflow and uploading attachments...', current: 0, total: totalAttachments });
            }

            const res = await fetch(`${API_BASE}/ivanti/fp-workflow`, {
                method: 'POST',
                credentials: 'include',
                body: formData,
            });

            const data = await res.json();

            if (res.ok && data.success) {
                setResult({
                    success: true,
                    workflowBatchId: data.workflowBatchId,
                    generatedId: data.generatedId,
                    attachmentResults: data.attachmentResults || [],
                    status: data.status || 'success',
                });
                onSuccess();
            } else {
                let errorMsg = data.error || 'Workflow creation failed';
                if (res.status === 401) errorMsg = 'Ivanti API key is invalid or missing. Contact your administrator.';
                else if (res.status === 429) errorMsg = 'Ivanti API rate limit reached. Please try again in a few minutes.';

                setResult({
                    success: false,
                    error: errorMsg,
                    workflowBatchId: data.workflowBatchId || null,
                    generatedId: data.generatedId || null,
                    attachmentResults: data.attachmentResults || [],
                    status: data.status || 'failed',
                });
            }
        } catch (err) {
            setResult({
                success: false,
                error: err.message || 'Network error — could not reach the server',
                status: 'failed',
            });
        } finally {
            setSubmitting(false);
        }
    };

    if (!open) return null;

    // ---- Styles ----
    const overlayStyle = {
        position: 'fixed', inset: 0, zIndex: 10000,
        background: 'rgba(0,0,0,0.6)',
        display: 'flex', alignItems: 'center', justifyContent: 'center',
    };
    const modalStyle = {
        width: '640px', maxHeight: '90vh', overflow: 'auto',
        background: 'linear-gradient(180deg, #0D1929 0%, #080F1C 100%)',
        border: '1px solid rgba(245,158,11,0.3)',
        borderRadius: '0.75rem',
        boxShadow: '0 12px 48px rgba(0,0,0,0.8)',
        fontFamily: 'monospace',
    };
    const headerStyle = {
        display: 'flex', alignItems: 'center', justifyContent: 'space-between',
        padding: '1rem 1.25rem',
        borderBottom: '1px solid rgba(245,158,11,0.2)',
    };
    const sectionStyle = {
        padding: '0.875rem 1.25rem',
        borderBottom: '1px solid rgba(255,255,255,0.04)',
    };
    const labelStyle = {
        display: 'block', fontSize: '0.68rem', fontWeight: '600',
        color: '#64748B', textTransform: 'uppercase', letterSpacing: '0.08em',
        marginBottom: '0.35rem',
    };
    const inputStyle = {
        width: '100%', boxSizing: 'border-box',
        background: 'rgba(14,165,233,0.05)',
        border: '1px solid rgba(14,165,233,0.2)',
        borderRadius: '0.25rem', padding: '0.45rem 0.6rem',
        color: '#CBD5E1', fontSize: '0.82rem', fontFamily: 'monospace',
        outline: 'none',
    };
    const inputErrorStyle = { ...inputStyle, borderColor: '#EF4444' };
    const textareaStyle = { ...inputStyle, minHeight: '60px', resize: 'vertical' };
    const textareaErrorStyle = { ...textareaStyle, borderColor: '#EF4444' };
    const errorTextStyle = { fontSize: '0.68rem', color: '#EF4444', marginTop: '0.2rem' };
    const footerStyle = {
        display: 'flex', alignItems: 'center', justifyContent: 'flex-end', gap: '0.625rem',
        padding: '0.875rem 1.25rem',
    };

    // ---- Result views ----
    if (result) {
        return ReactDOM.createPortal(
            <div style={overlayStyle} onClick={() => { if (!submitting) onClose(); }}>
                <div style={modalStyle} onClick={e => e.stopPropagation()}>
                    <div style={headerStyle}>
                        <span style={{ fontSize: '0.88rem', fontWeight: '700', color: result.success ? '#10B981' : '#EF4444' }}>
                            {result.success ? 'Workflow Created' : 'Submission Failed'}
                        </span>
                        <button onClick={onClose} style={{ background: 'none', border: 'none', color: '#64748B', cursor: 'pointer' }}>
                            <X size={16} />
                        </button>
                    </div>
                    <div style={{ padding: '1.5rem 1.25rem', textAlign: 'center' }}>
                        {result.success ? (
                            <>
                                <div style={{ marginBottom: '1rem' }}>
                                    <Check size={36} style={{ color: '#10B981' }} />
                                </div>
                                <div style={{ fontSize: '1.1rem', fontWeight: '700', color: '#F59E0B', marginBottom: '0.5rem' }}>
                                    {result.generatedId || `Batch #${result.workflowBatchId}`}
                                </div>
                                <div style={{ fontSize: '0.78rem', color: '#94A3B8', marginBottom: '1rem' }}>
                                    FP workflow created successfully with {selectedItems.length} finding{selectedItems.length !== 1 ? 's' : ''}.
                                </div>
                                {result.attachmentResults.length > 0 && (
                                    <div style={{ textAlign: 'left', marginBottom: '1rem' }}>
                                        <div style={labelStyle}>Attachments</div>
                                        {result.attachmentResults.map((a, i) => (
                                            <div key={i} style={{ display: 'flex', alignItems: 'center', gap: '0.5rem', fontSize: '0.75rem', color: a.success ? '#10B981' : '#EF4444', marginBottom: '0.25rem' }}>
                                                {a.success ? <Check size={12} /> : <AlertTriangle size={12} />}
                                                <span style={{
                                                    display: 'inline-block',
                                                    fontSize: '0.6rem',
                                                    fontWeight: '600',
                                                    padding: '0.1rem 0.3rem',
                                                    borderRadius: '0.2rem',
                                                    background: (a.source || 'local') === 'library' ? 'rgba(168,85,247,0.12)' : 'rgba(14,165,233,0.12)',
                                                    color: (a.source || 'local') === 'library' ? '#A855F7' : '#0EA5E9',
                                                    border: `1px solid ${(a.source || 'local') === 'library' ? 'rgba(168,85,247,0.3)' : 'rgba(14,165,233,0.3)'}`,
                                                    textTransform: 'uppercase',
                                                    letterSpacing: '0.04em',
                                                    flexShrink: 0,
                                                }}>{(a.source || 'local') === 'library' ? 'Library' : 'Local'}</span>
                                                <span>{a.filename}</span>
                                            </div>
                                        ))}
                                    </div>
                                )}
                            </>
                        ) : (
                            <>
                                <div style={{ marginBottom: '1rem' }}>
                                    <AlertTriangle size={36} style={{ color: '#EF4444' }} />
                                </div>
                                <div style={{ fontSize: '0.88rem', fontWeight: '600', color: '#E2E8F0', marginBottom: '0.5rem' }}>
                                    {result.error}
                                </div>
                                {result.generatedId && (
                                    <div style={{ fontSize: '0.78rem', color: '#F59E0B', marginBottom: '0.5rem' }}>
                                        Workflow was created: {result.generatedId}
                                    </div>
                                )}
                                {result.attachmentResults?.length > 0 && (
                                    <div style={{ textAlign: 'left', marginBottom: '1rem' }}>
                                        <div style={labelStyle}>Attachment Results</div>
                                        {result.attachmentResults.map((a, i) => (
                                            <div key={i} style={{ display: 'flex', alignItems: 'center', gap: '0.5rem', fontSize: '0.75rem', color: a.success ? '#10B981' : '#EF4444', marginBottom: '0.25rem' }}>
                                                {a.success ? <Check size={12} /> : <AlertTriangle size={12} />}
                                                <span style={{
                                                    display: 'inline-block',
                                                    fontSize: '0.6rem',
                                                    fontWeight: '600',
                                                    padding: '0.1rem 0.3rem',
                                                    borderRadius: '0.2rem',
                                                    background: (a.source || 'local') === 'library' ? 'rgba(168,85,247,0.12)' : 'rgba(14,165,233,0.12)',
                                                    color: (a.source || 'local') === 'library' ? '#A855F7' : '#0EA5E9',
                                                    border: `1px solid ${(a.source || 'local') === 'library' ? 'rgba(168,85,247,0.3)' : 'rgba(14,165,233,0.3)'}`,
                                                    textTransform: 'uppercase',
                                                    letterSpacing: '0.04em',
                                                    flexShrink: 0,
                                                }}>{(a.source || 'local') === 'library' ? 'Library' : 'Local'}</span>
                                                <span>{a.filename}</span>
                                            </div>
                                        ))}
                                    </div>
                                )}
                            </>
                        )}
                    </div>
                    <div style={footerStyle}>
                        {!result.success && (
                            <button
                                onClick={() => setResult(null)}
                                style={{
                                    padding: '0.45rem 1rem',
                                    background: 'rgba(245,158,11,0.1)',
                                    border: '1px solid rgba(245,158,11,0.3)',
                                    borderRadius: '0.375rem',
                                    color: '#F59E0B', fontSize: '0.78rem', fontWeight: '600',
                                    cursor: 'pointer', fontFamily: 'monospace',
                                }}
                            >
                                Retry
                            </button>
                        )}
                        <button
                            onClick={onClose}
                            style={{
                                padding: '0.45rem 1rem',
                                background: result.success ? 'rgba(16,185,129,0.12)' : 'rgba(255,255,255,0.04)',
                                border: `1px solid ${result.success ? 'rgba(16,185,129,0.3)' : 'rgba(255,255,255,0.1)'}`,
                                borderRadius: '0.375rem',
                                color: result.success ? '#10B981' : '#94A3B8',
                                fontSize: '0.78rem', fontWeight: '600',
                                cursor: 'pointer', fontFamily: 'monospace',
                            }}
                        >
                            Done
                        </button>
                    </div>
                </div>
            </div>,
            document.body
        );
    }

    // ---- Form view ----
    return ReactDOM.createPortal(
        <div style={overlayStyle} onClick={() => { if (!submitting) onClose(); }}>
            <div style={modalStyle} onClick={e => e.stopPropagation()}>
                {/* Header */}
                <div style={headerStyle}>
                    <span style={{ fontSize: '0.88rem', fontWeight: '700', color: '#F59E0B' }}>
                        Create FP Workflow
                    </span>
                    <button onClick={() => { if (!submitting) onClose(); }} style={{ background: 'none', border: 'none', color: '#64748B', cursor: 'pointer' }}>
                        <X size={16} />
                    </button>
                </div>

                {/* Selected findings summary */}
                <div style={sectionStyle}>
                    <div style={labelStyle}>Selected Findings ({selectedItems.length})</div>
                    <div style={{ maxHeight: '120px', overflow: 'auto' }}>
                        {selectedItems.map((item, i) => (
                            <div key={item.id || i} style={{ display: 'flex', gap: '0.5rem', alignItems: 'baseline', fontSize: '0.75rem', color: '#94A3B8', marginBottom: '0.3rem' }}>
                                <span style={{ color: '#F59E0B', fontWeight: '600', flexShrink: 0 }}>{item.finding_id}</span>
                                <span style={{ color: '#CBD5E1', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', flex: 1 }}>{item.finding_title || '—'}</span>
                                {item.cves_json && (() => {
                                    try {
                                        const cves = JSON.parse(item.cves_json);
                                        return cves.length > 0 ? <span style={{ color: '#64748B', flexShrink: 0 }}>{cves.join(', ')}</span> : null;
                                    } catch { return null; }
                                })()}
                            </div>
                        ))}
                    </div>
                </div>

                {/* Form fields */}
                <div style={sectionStyle}>
                    {/* Name */}
                    <label style={{ display: 'block', marginBottom: '0.75rem' }}>
                        <span style={labelStyle}>Workflow Name <span style={{ color: '#EF4444' }}>*</span></span>
                        <input
                            type="text"
                            value={name}
                            onChange={e => setName(e.target.value)}
                            placeholder="FP — CVE-2024-XXXX — Vendor"
                            disabled={submitting}
                            maxLength={255}
                            style={errors.name ? inputErrorStyle : inputStyle}
                        />
                        {errors.name && <div style={errorTextStyle}>{errors.name}</div>}
                    </label>

                    {/* Reason */}
                    <label style={{ display: 'block', marginBottom: '0.75rem' }}>
                        <span style={labelStyle}>Reason / Justification <span style={{ color: '#EF4444' }}>*</span></span>
                        <textarea
                            value={reason}
                            onChange={e => setReason(e.target.value)}
                            placeholder="Explain why these findings are false positives..."
                            disabled={submitting}
                            style={errors.reason ? textareaErrorStyle : textareaStyle}
                        />
                        {errors.reason && <div style={errorTextStyle}>{errors.reason}</div>}
                    </label>

                    {/* Description */}
                    <label style={{ display: 'block', marginBottom: '0.75rem' }}>
                        <span style={labelStyle}>Description (optional)</span>
                        <textarea
                            value={description}
                            onChange={e => setDescription(e.target.value)}
                            placeholder="Additional context or details..."
                            disabled={submitting}
                            maxLength={2000}
                            style={errors.description ? textareaErrorStyle : textareaStyle}
                        />
                        {errors.description && <div style={errorTextStyle}>{errors.description}</div>}
                        <div style={{ fontSize: '0.62rem', color: '#475569', textAlign: 'right', marginTop: '0.15rem' }}>{description.length}/2000</div>
                    </label>

                    {/* Expiration date */}
                    <label style={{ display: 'block', marginBottom: '0.75rem' }}>
                        <span style={labelStyle}>Expiration Date <span style={{ color: '#EF4444' }}>*</span></span>
                        <input
                            type="date"
                            value={expirationDate}
                            onChange={e => setExpirationDate(e.target.value)}
                            min={(() => { const d = new Date(); d.setDate(d.getDate() + 1); return d.toISOString().split('T')[0]; })()}
                            max={(() => { const d = new Date(); d.setDate(d.getDate() + 120); return d.toISOString().split('T')[0]; })()}
                            disabled={submitting}
                            style={errors.expirationDate ? inputErrorStyle : inputStyle}
                        />
                        {errors.expirationDate && <div style={errorTextStyle}>{errors.expirationDate}</div>}
                    </label>

                    {/* Scope override toggle */}
                    <div style={{ marginBottom: '0.25rem' }}>
                        <span style={labelStyle}>Scope Override Authorization</span>
                        <div style={{ display: 'flex', gap: '0.5rem' }}>
                            {['Authorized', 'None'].map(val => {
                                const active = scopeOverride === val;
                                return (
                                    <button
                                        key={val}
                                        onClick={() => setScopeOverride(val)}
                                        disabled={submitting}
                                        style={{
                                            flex: 1, padding: '0.35rem',
                                            background: active ? 'rgba(245,158,11,0.12)' : 'transparent',
                                            border: `1px solid ${active ? 'rgba(245,158,11,0.4)' : 'rgba(255,255,255,0.08)'}`,
                                            borderRadius: '0.25rem',
                                            color: active ? '#F59E0B' : '#475569',
                                            fontFamily: 'monospace', fontSize: '0.75rem', fontWeight: '600',
                                            cursor: submitting ? 'not-allowed' : 'pointer',
                                            transition: 'all 0.12s',
                                        }}
                                    >
                                        {val}
                                    </button>
                                );
                            })}
                        </div>
                    </div>
                </div>

                {/* Attachments */}
                <div style={sectionStyle}>
                    <div style={labelStyle}>Attachments</div>
                    <AttachmentSourcePicker
                        files={files}
                        onFilesChange={setFiles}
                        libraryDocs={libraryDocs}
                        onLibraryDocsChange={setLibraryDocs}
                        disabled={submitting}
                    />
                </div>

                {/* Footer */}
                <div style={footerStyle}>
                    {submitting && (
                        <div style={{ flex: 1, display: 'flex', alignItems: 'center', gap: '0.5rem', fontSize: '0.75rem', color: '#F59E0B' }}>
                            <Loader size={14} style={{ animation: 'spin 1s linear infinite' }} />
                            <span>{progress.step}</span>
                        </div>
                    )}
                    <button
                        onClick={() => { if (!submitting) onClose(); }}
                        disabled={submitting}
                        style={{
                            padding: '0.45rem 1rem',
                            background: 'none',
                            border: '1px solid rgba(255,255,255,0.08)',
                            borderRadius: '0.375rem',
                            color: '#64748B', fontSize: '0.78rem', fontWeight: '600',
                            cursor: submitting ? 'not-allowed' : 'pointer',
                            fontFamily: 'monospace',
                        }}
                    >
                        Cancel
                    </button>
                    <button
                        onClick={handleSubmit}
                        disabled={submitting}
                        style={{
                            padding: '0.45rem 1.25rem',
                            background: submitting ? 'rgba(245,158,11,0.08)' : 'rgba(245,158,11,0.15)',
                            border: `1px solid ${submitting ? 'rgba(245,158,11,0.15)' : 'rgba(245,158,11,0.4)'}`,
                            borderRadius: '0.375rem',
                            color: submitting ? '#92700C' : '#F59E0B',
                            fontSize: '0.78rem', fontWeight: '700',
                            cursor: submitting ? 'not-allowed' : 'pointer',
                            fontFamily: 'monospace', textTransform: 'uppercase', letterSpacing: '0.05em',
                        }}
                    >
                        {submitting ? 'Submitting...' : 'Submit'}
                    </button>
                </div>
            </div>
        </div>,
        document.body
    );
}

// ---------------------------------------------------------------------------
// RequeueConfirmDialog — confirmation dialog for re-queuing rejected FP findings
// ---------------------------------------------------------------------------
const REQUEUE_WORKFLOW_OPTIONS = [
    { key: 'FP',      label: 'FP',      col: '#F59E0B', rgb: '245,158,11' },
    { key: 'Archer',  label: 'Archer',  col: '#0EA5E9', rgb: '14,165,233' },
    { key: 'CARD',    label: 'CARD',    col: '#10B981', rgb: '16,185,129' },
    { key: 'GRANITE', label: 'GRANITE', col: '#A1887F', rgb: '161,136,127' },
];

function RequeueConfirmDialog({ submission, onClose, onSuccess }) {
    const [workflowType, setWorkflowType] = useState('FP');
    const [vendor, setVendor] = useState(() => {
        // Pre-fill vendor from submission's queue items if available
        try {
            const items = JSON.parse(submission.queue_item_ids_json || '[]');
            if (items.length > 0 && submission.vendor) return submission.vendor;
        } catch { /* ignore */ }
        return '';
    });
    const [loading, setLoading] = useState(false);
    const [error, setError] = useState('');

    const needsVendor = workflowType === 'FP' || workflowType === 'Archer';
    const canSubmit = !loading && (!needsVendor || vendor.trim().length > 0);

    // Count findings
    const findingCount = (() => {
        try {
            const queueIds = JSON.parse(submission.queue_item_ids_json || '[]');
            if (queueIds.length > 0) return queueIds.length;
            const findingIds = JSON.parse(submission.finding_ids_json || '[]');
            return findingIds.length;
        } catch { return 0; }
    })();

    const handleConfirm = async () => {
        if (!canSubmit) return;
        setLoading(true);
        setError('');
        try {
            const body = { workflow_type: workflowType };
            if (needsVendor) body.vendor = vendor.trim();

            const res = await fetch(`${API_BASE}/ivanti/fp-workflow/submissions/${submission.id}/requeue`, {
                method: 'POST',
                credentials: 'include',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify(body),
            });

            const data = await res.json();
            if (!res.ok) {
                setError(data.error || 'Re-queue failed.');
                setLoading(false);
                return;
            }
            onSuccess(data);
        } catch (err) {
            setError(err.message || 'Network error.');
            setLoading(false);
        }
    };

    return ReactDOM.createPortal(
        <div
            onClick={onClose}
            style={{
                position: 'fixed', inset: 0, zIndex: 10020,
                background: 'rgba(10, 14, 39, 0.92)',
                backdropFilter: 'blur(8px)',
                display: 'flex', alignItems: 'center', justifyContent: 'center',
                padding: '1rem',
            }}
        >
            <div
                onClick={(e) => e.stopPropagation()}
                style={{
                    width: '100%', maxWidth: '460px',
                    background: 'linear-gradient(135deg, rgba(30, 41, 59, 0.98), rgba(51, 65, 85, 0.95))',
                    border: '2px solid rgba(245, 158, 11, 0.4)',
                    borderRadius: '0.75rem',
                    boxShadow: '0 20px 60px rgba(0, 0, 0, 0.7), 0 0 28px rgba(245, 158, 11, 0.12)',
                    display: 'flex', flexDirection: 'column',
                    overflow: 'hidden',
                }}
            >
                {/* Top accent line */}
                <div style={{
                    height: '2px',
                    background: 'linear-gradient(90deg, transparent, #F59E0B, transparent)',
                    boxShadow: '0 0 8px rgba(245, 158, 11, 0.4)',
                }} />

                {/* Header */}
                <div style={{
                    display: 'flex', alignItems: 'center', justifyContent: 'space-between',
                    padding: '1rem 1.25rem',
                    borderBottom: '1px solid rgba(245, 158, 11, 0.15)',
                }}>
                    <div style={{ display: 'flex', alignItems: 'center', gap: '0.625rem' }}>
                        <RotateCcw style={{ width: '18px', height: '18px', color: '#F59E0B' }} />
                        <span style={{
                            fontFamily: 'monospace', fontSize: '0.95rem', fontWeight: '700',
                            color: '#E2E8F0', textTransform: 'uppercase', letterSpacing: '0.08em',
                        }}>
                            Re-queue Findings
                        </span>
                    </div>
                    <button
                        onClick={onClose}
                        style={{ background: 'none', border: 'none', cursor: 'pointer', color: '#475569', padding: '4px', lineHeight: 1 }}
                    >
                        <X style={{ width: '18px', height: '18px' }} />
                    </button>
                </div>

                {/* Body */}
                <div style={{ padding: '1.25rem', display: 'flex', flexDirection: 'column', gap: '1rem' }}>
                    {/* Finding count info */}
                    <div style={{
                        background: 'rgba(15, 23, 42, 0.6)',
                        border: '1px solid rgba(245, 158, 11, 0.15)',
                        borderRadius: '0.5rem',
                        padding: '0.75rem 1rem',
                    }}>
                        <span style={{ fontFamily: 'monospace', fontSize: '0.75rem', color: '#CBD5E1' }}>
                            <strong style={{ color: '#F59E0B' }}>{findingCount}</strong> finding{findingCount !== 1 ? 's' : ''} will be re-queued from this rejected submission.
                        </span>
                    </div>

                    {/* Workflow type selector */}
                    <div>
                        <label style={{
                            display: 'block', fontFamily: 'monospace', fontSize: '0.68rem', fontWeight: '600',
                            color: '#94A3B8', textTransform: 'uppercase', letterSpacing: '0.1em',
                            marginBottom: '0.5rem',
                        }}>
                            Target Workflow Type
                        </label>
                        <div style={{ display: 'flex', gap: '0.5rem' }}>
                            {REQUEUE_WORKFLOW_OPTIONS.map(({ key, label, col, rgb }) => {
                                const active = workflowType === key;
                                return (
                                    <label
                                        key={key}
                                        style={{
                                            flex: 1, display: 'flex', alignItems: 'center', justifyContent: 'center',
                                            gap: '0.375rem', padding: '0.45rem 0.5rem', borderRadius: '0.375rem',
                                            background: active ? `rgba(${rgb}, 0.15)` : 'transparent',
                                            border: `1.5px solid ${active ? `rgba(${rgb}, 0.5)` : 'rgba(255,255,255,0.08)'}`,
                                            color: active ? col : '#475569',
                                            fontFamily: 'monospace', fontSize: '0.72rem', fontWeight: '700',
                                            cursor: 'pointer', transition: 'all 0.15s',
                                        }}
                                    >
                                        <input
                                            type="radio"
                                            name="requeue-workflow-type"
                                            value={key}
                                            checked={active}
                                            onChange={() => setWorkflowType(key)}
                                            style={{ display: 'none' }}
                                        />
                                        <span style={{
                                            width: '8px', height: '8px', borderRadius: '50%',
                                            background: active ? col : 'rgba(255,255,255,0.1)',
                                            boxShadow: active ? `0 0 6px ${col}` : 'none',
                                            transition: 'all 0.15s',
                                        }} />
                                        {label}
                                    </label>
                                );
                            })}
                        </div>
                    </div>

                    {/* Vendor input — conditional */}
                    {needsVendor && (
                        <div>
                            <label style={{
                                display: 'block', fontFamily: 'monospace', fontSize: '0.68rem', fontWeight: '600',
                                color: '#94A3B8', textTransform: 'uppercase', letterSpacing: '0.1em',
                                marginBottom: '0.5rem',
                            }}>
                                Vendor <span style={{ color: '#EF4444' }}>*</span>
                            </label>
                            <input
                                type="text"
                                value={vendor}
                                onChange={(e) => setVendor(e.target.value)}
                                placeholder="e.g. Cisco, Juniper, ADTRAN…"
                                maxLength={200}
                                style={{
                                    width: '100%', boxSizing: 'border-box',
                                    background: 'rgba(30, 41, 59, 0.6)',
                                    border: '1px solid rgba(245, 158, 11, 0.25)',
                                    borderRadius: '0.375rem', padding: '0.5rem 0.75rem',
                                    fontFamily: 'monospace', fontSize: '0.8rem', color: '#E2E8F0',
                                    outline: 'none', transition: 'border-color 0.2s',
                                }}
                                onFocus={(e) => { e.target.style.borderColor = '#F59E0B'; }}
                                onBlur={(e) => { e.target.style.borderColor = 'rgba(245, 158, 11, 0.25)'; }}
                            />
                        </div>
                    )}

                    {/* Error message */}
                    {error && (
                        <div style={{
                            display: 'flex', alignItems: 'flex-start', gap: '0.5rem',
                            padding: '0.625rem 0.75rem',
                            background: 'rgba(239, 68, 68, 0.1)',
                            border: '1px solid rgba(239, 68, 68, 0.35)',
                            borderRadius: '0.375rem',
                        }}>
                            <AlertCircle style={{ width: '16px', height: '16px', color: '#EF4444', flexShrink: 0, marginTop: '1px' }} />
                            <span style={{ fontFamily: 'monospace', fontSize: '0.75rem', color: '#FCA5A5' }}>
                                {error}
                            </span>
                        </div>
                    )}
                </div>

                {/* Footer */}
                <div style={{
                    display: 'flex', justifyContent: 'flex-end', gap: '0.625rem',
                    padding: '0.875rem 1.25rem',
                    borderTop: '1px solid rgba(245, 158, 11, 0.1)',
                }}>
                    <button
                        onClick={onClose}
                        style={{
                            padding: '0.45rem 1rem', background: 'transparent',
                            border: '1px solid rgba(255,255,255,0.1)', borderRadius: '0.375rem',
                            color: '#94A3B8', fontFamily: 'monospace', fontSize: '0.75rem', fontWeight: '600',
                            textTransform: 'uppercase', letterSpacing: '0.05em', cursor: 'pointer',
                            transition: 'all 0.15s',
                        }}
                    >
                        Cancel
                    </button>
                    <button
                        onClick={handleConfirm}
                        disabled={!canSubmit}
                        style={{
                            display: 'flex', alignItems: 'center', gap: '0.375rem',
                            padding: '0.45rem 1.1rem',
                            background: canSubmit
                                ? 'linear-gradient(135deg, rgba(245, 158, 11, 0.15), rgba(245, 158, 11, 0.1))'
                                : 'transparent',
                            border: `1.5px solid ${canSubmit ? '#F59E0B' : 'rgba(255,255,255,0.06)'}`,
                            borderRadius: '0.375rem',
                            color: canSubmit ? '#FBBF24' : '#334155',
                            fontFamily: 'monospace', fontSize: '0.75rem', fontWeight: '600',
                            textTransform: 'uppercase', letterSpacing: '0.05em',
                            cursor: canSubmit ? 'pointer' : 'not-allowed',
                            transition: 'all 0.15s',
                        }}
                    >
                        {loading ? (
                            <Loader style={{ width: '14px', height: '14px', animation: 'spin 1s linear infinite' }} />
                        ) : (
                            <RotateCcw style={{ width: '14px', height: '14px' }} />
                        )}
                        {loading ? 'Re-queuing…' : 'Confirm Re-queue'}
                    </button>
                </div>
            </div>
        </div>,
        document.body
    );
}

// ---------------------------------------------------------------------------
// FpEditModal — edit existing FP submissions (tabbed modal)
// ---------------------------------------------------------------------------
function FpEditModal({ open, onClose, submission, queueItems, onSuccess }) {
    const [activeTab, setActiveTab] = useState('details');
    const [name, setName] = useState('');
    const [reason, setReason] = useState('');
    const [description, setDescription] = useState('');
    const [expirationDate, setExpirationDate] = useState('');
    const [scopeOverride, setScopeOverride] = useState('');
    const [saving, setSaving] = useState(false);
    const [errors, setErrors] = useState({});
    const [result, setResult] = useState(null);
    const [files, setFiles] = useState([]);
    const [libraryDocs, setLibraryDocs] = useState([]);
    const [additionalFindingIds, setAdditionalFindingIds] = useState(new Set());
    const [statusValue, setStatusValue] = useState('');
    const [showRequeueDialog, setShowRequeueDialog] = useState(false);

    // Reset form when submission changes
    useEffect(() => {
        if (submission) {
            setName(submission.workflow_name || '');
            setReason(submission.reason || '');
            setDescription(submission.description || '');
            setExpirationDate(submission.expiration_date || '');
            setScopeOverride(submission.scope_override || '');
            setStatusValue(submission.lifecycle_status || 'submitted');
            setActiveTab('details');
            setErrors({});
            setResult(null);
            setFiles([]);
            setLibraryDocs([]);
            setAdditionalFindingIds(new Set());
        }
    }, [submission]);

    if (!open || !submission) return null;

    const isApproved = (submission.lifecycle_status || '').toLowerCase() === 'approved';
    const currentFindings = (() => {
        try { return JSON.parse(submission.finding_ids_json || '[]'); } catch { return []; }
    })();
    const existingAttachments = (() => {
        try { return JSON.parse(submission.attachment_results_json || '[]'); } catch { return []; }
    })();
    const history = submission.history || [];

    const pendingFpQueue = (queueItems || []).filter(i =>
        i.workflow_type === 'FP' && i.status === 'pending' && !currentFindings.includes(String(i.finding_id))
    );

    const handleSaveDetails = async () => {
        setSaving(true); setErrors({}); setResult(null);
        try {
            const res = await fetch(`${API_BASE}/ivanti/fp-workflow/submissions/${submission.id}`, {
                method: 'PUT', credentials: 'include',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify({ name, reason, description, expirationDate, scopeOverride }),
            });
            const data = await res.json();
            if (res.ok) {
                setResult({ type: 'success', message: 'Details saved successfully.' });
                if (onSuccess) onSuccess();
            } else {
                setResult({ type: 'error', message: data.error || 'Failed to save details.' });
            }
        } catch (e) {
            setResult({ type: 'error', message: 'Network error saving details.' });
        } finally { setSaving(false); }
    };

    const handleAddFindings = async () => {
        if (additionalFindingIds.size === 0) return;
        setSaving(true); setResult(null);
        const selectedItems = pendingFpQueue.filter(i => additionalFindingIds.has(i.id));
        const findingIds = selectedItems.map(i => String(i.finding_id));
        const queueItemIds = selectedItems.map(i => i.id);
        try {
            const res = await fetch(`${API_BASE}/ivanti/fp-workflow/submissions/${submission.id}/findings`, {
                method: 'POST', credentials: 'include',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify({ findingIds, queueItemIds }),
            });
            const data = await res.json();
            if (res.ok) {
                setResult({ type: 'success', message: `Added ${findingIds.length} finding(s).` });
                setAdditionalFindingIds(new Set());
                if (onSuccess) onSuccess();
            } else {
                setResult({ type: 'error', message: data.error || 'Failed to add findings.' });
            }
        } catch (e) {
            setResult({ type: 'error', message: 'Network error adding findings.' });
        } finally { setSaving(false); }
    };

    const handleUploadAttachments = async () => {
        if (files.length === 0 && libraryDocs.length === 0) return;
        setSaving(true); setResult(null);
        const formData = new FormData();
        files.forEach(f => formData.append('attachments', f));
        if (libraryDocs.length > 0) {
            formData.append('libraryDocIds', JSON.stringify(libraryDocs.map(d => d.id)));
        }
        try {
            const res = await fetch(`${API_BASE}/ivanti/fp-workflow/submissions/${submission.id}/attachments`, {
                method: 'POST', credentials: 'include', body: formData,
            });
            const data = await res.json();
            if (res.ok) {
                const successCount = (data.attachmentResults || []).filter(r => r.success).length;
                setResult({ type: 'success', message: `Uploaded ${successCount} file(s).` });
                setFiles([]);
                setLibraryDocs([]);
                if (onSuccess) onSuccess();
            } else {
                setResult({ type: 'error', message: data.error || 'Failed to upload attachments.' });
            }
        } catch (e) {
            setResult({ type: 'error', message: 'Network error uploading attachments.' });
        } finally { setSaving(false); }
    };

    const handleStatusChange = async (newStatus) => {
        setSaving(true); setResult(null);
        try {
            const res = await fetch(`${API_BASE}/ivanti/fp-workflow/submissions/${submission.id}/status`, {
                method: 'PATCH', credentials: 'include',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify({ lifecycle_status: newStatus }),
            });
            const data = await res.json();
            if (res.ok) {
                setResult({ type: 'success', message: `Status changed to ${newStatus}.` });
                setStatusValue(newStatus);
                if (onSuccess) onSuccess();
            } else {
                setResult({ type: 'error', message: data.error || 'Failed to change status.' });
            }
        } catch (e) {
            setResult({ type: 'error', message: 'Network error changing status.' });
        } finally { setSaving(false); }
    };

    const lsBadge = lifecycleStatusBadge(statusValue);
    const tabs = ['details', 'findings', 'attachments', 'history'];

    const inputStyle = {
        width: '100%', boxSizing: 'border-box',
        background: isApproved ? 'rgba(100,116,139,0.06)' : 'rgba(14,165,233,0.05)',
        border: `1px solid ${isApproved ? 'rgba(100,116,139,0.15)' : 'rgba(14,165,233,0.2)'}`,
        borderRadius: '0.25rem', padding: '0.4rem 0.5rem',
        color: isApproved ? '#64748B' : '#CBD5E1',
        fontSize: '0.78rem', fontFamily: 'monospace', outline: 'none',
    };
    const labelStyle = { display: 'block', fontFamily: 'monospace', fontSize: '0.68rem', fontWeight: '600', color: '#94A3B8', marginBottom: '0.25rem', textTransform: 'uppercase', letterSpacing: '0.06em' };

    const portal = ReactDOM.createPortal(
        <div style={{ position: 'fixed', inset: 0, zIndex: 10010, display: 'flex', alignItems: 'center', justifyContent: 'center', background: 'rgba(0,0,0,0.6)' }} onClick={onClose}>
            <div onClick={(e) => e.stopPropagation()} style={{
                width: '640px', maxHeight: '85vh', display: 'flex', flexDirection: 'column',
                background: 'linear-gradient(180deg, #0D1929 0%, #080F1C 100%)',
                border: '1px solid rgba(14,165,233,0.2)',
                borderRadius: '0.5rem',
                boxShadow: '0 20px 60px rgba(0,0,0,0.8)',
            }}>
                {/* Header */}
                <div style={{ padding: '1rem 1.25rem', borderBottom: '1px solid rgba(14,165,233,0.15)', flexShrink: 0 }}>
                    <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
                        <div style={{ display: 'flex', alignItems: 'center', gap: '0.625rem' }}>
                            <Edit3 style={{ width: '18px', height: '18px', color: '#F59E0B' }} />
                            <span style={{ fontFamily: 'monospace', fontSize: '0.9rem', fontWeight: '700', color: '#E2E8F0' }}>
                                Edit FP Workflow
                            </span>
                            <span style={{
                                padding: '0.1rem 0.4rem', borderRadius: '0.2rem',
                                background: lsBadge.bg, border: `1px solid ${lsBadge.border}`,
                                color: lsBadge.text, fontFamily: 'monospace', fontSize: '0.6rem', fontWeight: '700',
                                textTransform: 'uppercase',
                            }}>
                                {statusValue}
                            </span>
                            {submission.lifecycle_status === 'rejected' && (
                                submission.requeued_at ? (
                                    <span style={{ fontFamily: 'monospace', fontSize: '0.68rem', color: '#64748B', fontStyle: 'italic' }}>
                                        Already re-queued
                                    </span>
                                ) : (
                                    <button
                                        onClick={() => setShowRequeueDialog(true)}
                                        style={{
                                            display: 'inline-flex', alignItems: 'center', gap: '0.3rem',
                                            padding: '0.15rem 0.45rem', borderRadius: '0.25rem',
                                            background: 'rgba(245,158,11,0.08)',
                                            border: '1px solid rgba(245,158,11,0.3)',
                                            color: '#F59E0B', fontFamily: 'monospace', fontSize: '0.68rem', fontWeight: '600',
                                            cursor: 'pointer', lineHeight: 1.2,
                                        }}
                                    >
                                        <RotateCcw style={{ width: '12px', height: '12px' }} />
                                        Re-queue Findings
                                    </button>
                                )
                            )}
                        </div>
                        <button onClick={onClose} style={{ background: 'none', border: 'none', cursor: 'pointer', color: '#475569', padding: '4px', lineHeight: 1 }}>
                            <X style={{ width: '18px', height: '18px' }} />
                        </button>
                    </div>
                    <div style={{ fontFamily: 'monospace', fontSize: '0.72rem', color: '#64748B', marginTop: '0.25rem' }}>
                        {submission.workflow_name || `Batch #${submission.ivanti_workflow_batch_id}`}
                    </div>
                    {isApproved && (
                        <div style={{ marginTop: '0.5rem', padding: '0.35rem 0.5rem', borderRadius: '0.25rem', background: 'rgba(16,185,129,0.08)', border: '1px solid rgba(16,185,129,0.2)', fontFamily: 'monospace', fontSize: '0.68rem', color: '#10B981' }}>
                            This submission is finalized and cannot be edited.
                        </div>
                    )}
                </div>

                {/* Tab bar */}
                <div style={{ display: 'flex', borderBottom: '1px solid rgba(255,255,255,0.06)', flexShrink: 0 }}>
                    {tabs.map(tab => (
                        <button key={tab} onClick={() => { setActiveTab(tab); setResult(null); }} style={{
                            flex: 1, padding: '0.5rem', background: 'none',
                            border: 'none', borderBottom: activeTab === tab ? '2px solid #0EA5E9' : '2px solid transparent',
                            color: activeTab === tab ? '#0EA5E9' : '#475569',
                            fontFamily: 'monospace', fontSize: '0.72rem', fontWeight: '600',
                            cursor: 'pointer', textTransform: 'uppercase', letterSpacing: '0.05em',
                            transition: 'all 0.12s',
                        }}>
                            {tab}
                        </button>
                    ))}
                </div>

                {/* Status change row */}
                {!isApproved && (
                    <div style={{ padding: '0.5rem 1.25rem', borderBottom: '1px solid rgba(255,255,255,0.04)', display: 'flex', alignItems: 'center', gap: '0.5rem', flexShrink: 0 }}>
                        <span style={{ fontFamily: 'monospace', fontSize: '0.68rem', color: '#64748B' }}>Status:</span>
                        <select
                            value={statusValue}
                            onChange={(e) => handleStatusChange(e.target.value)}
                            disabled={saving}
                            style={{
                                background: 'rgba(14,165,233,0.05)', border: '1px solid rgba(14,165,233,0.2)',
                                borderRadius: '0.25rem', padding: '0.25rem 0.4rem',
                                color: '#CBD5E1', fontSize: '0.72rem', fontFamily: 'monospace', outline: 'none',
                                cursor: saving ? 'not-allowed' : 'pointer',
                            }}
                        >
                            {['submitted', 'approved', 'rejected', 'rework', 'resubmitted'].map(s => (
                                <option key={s} value={s}>{s}</option>
                            ))}
                        </select>
                    </div>
                )}

                {/* Result banner */}
                {result && (
                    <div style={{
                        margin: '0.5rem 1.25rem 0', padding: '0.35rem 0.5rem', borderRadius: '0.25rem',
                        background: result.type === 'success' ? 'rgba(16,185,129,0.1)' : 'rgba(239,68,68,0.1)',
                        border: `1px solid ${result.type === 'success' ? 'rgba(16,185,129,0.3)' : 'rgba(239,68,68,0.3)'}`,
                        fontFamily: 'monospace', fontSize: '0.72rem',
                        color: result.type === 'success' ? '#10B981' : '#EF4444',
                        display: 'flex', alignItems: 'center', gap: '0.375rem',
                    }}>
                        {result.type === 'success' ? <Check style={{ width: '12px', height: '12px' }} /> : <AlertCircle style={{ width: '12px', height: '12px' }} />}
                        {result.message}
                    </div>
                )}

                {/* Tab content */}
                <div style={{ flex: 1, overflowY: 'auto', padding: '1rem 1.25rem' }}>
                    {/* Details tab */}
                    {activeTab === 'details' && (
                        <div style={{ display: 'flex', flexDirection: 'column', gap: '0.75rem' }}>
                            <div>
                                <label style={labelStyle}>Workflow Name</label>
                                <input type="text" value={name} onChange={(e) => setName(e.target.value)} disabled={isApproved} style={inputStyle} />
                            </div>
                            <div>
                                <label style={labelStyle}>Reason</label>
                                <select value={reason} onChange={(e) => setReason(e.target.value)} disabled={isApproved} style={inputStyle}>
                                    <option value="">Select reason…</option>
                                    <option value="Scanner false positive">Scanner false positive</option>
                                    <option value="Compensating control">Compensating control</option>
                                    <option value="Risk accepted">Risk accepted</option>
                                    <option value="Not applicable">Not applicable</option>
                                    <option value="Other">Other</option>
                                </select>
                            </div>
                            <div>
                                <label style={labelStyle}>Description</label>
                                <textarea value={description} onChange={(e) => setDescription(e.target.value)} disabled={isApproved} rows={3} style={{ ...inputStyle, resize: 'vertical' }} />
                            </div>
                            <div style={{ display: 'flex', gap: '0.75rem' }}>
                                <div style={{ flex: 1 }}>
                                    <label style={labelStyle}>Expiration Date</label>
                                    <input type="date" value={expirationDate} onChange={(e) => setExpirationDate(e.target.value)} disabled={isApproved} min={(() => { const d = new Date(); d.setDate(d.getDate() + 1); return d.toISOString().split('T')[0]; })()} max={(() => { const d = new Date(); d.setDate(d.getDate() + 120); return d.toISOString().split('T')[0]; })()} style={inputStyle} />
                                </div>
                                <div style={{ flex: 1 }}>
                                    <label style={labelStyle}>Scope Override</label>
                                    <select value={scopeOverride} onChange={(e) => setScopeOverride(e.target.value)} disabled={isApproved} style={inputStyle}>
                                        <option value="">Default</option>
                                        <option value="Authorized">Authorized</option>
                                        <option value="Unauthorized">Unauthorized</option>
                                    </select>
                                </div>
                            </div>
                            {!isApproved && (
                                <button onClick={handleSaveDetails} disabled={saving} style={{
                                    alignSelf: 'flex-end', padding: '0.4rem 1rem',
                                    background: saving ? 'rgba(245,158,11,0.08)' : 'rgba(245,158,11,0.15)',
                                    border: `1px solid ${saving ? 'rgba(245,158,11,0.15)' : 'rgba(245,158,11,0.4)'}`,
                                    borderRadius: '0.375rem', color: saving ? '#92700C' : '#F59E0B',
                                    fontSize: '0.75rem', fontWeight: '700', cursor: saving ? 'not-allowed' : 'pointer',
                                    fontFamily: 'monospace', textTransform: 'uppercase', letterSpacing: '0.05em',
                                }}>
                                    {saving ? 'Saving…' : 'Save Details'}
                                </button>
                            )}
                        </div>
                    )}

                    {/* Findings tab */}
                    {activeTab === 'findings' && (
                        <div>
                            <div style={{ marginBottom: '0.75rem' }}>
                                <span style={labelStyle}>Current Findings ({currentFindings.length})</span>
                                <div style={{ display: 'flex', flexWrap: 'wrap', gap: '0.25rem', marginTop: '0.25rem' }}>
                                    {currentFindings.length === 0 ? (
                                        <span style={{ fontFamily: 'monospace', fontSize: '0.72rem', color: '#475569' }}>No findings mapped.</span>
                                    ) : currentFindings.map(fid => (
                                        <span key={fid} style={{
                                            padding: '0.1rem 0.35rem', borderRadius: '0.2rem',
                                            background: 'rgba(14,165,233,0.08)', border: '1px solid rgba(14,165,233,0.2)',
                                            fontFamily: 'monospace', fontSize: '0.65rem', color: '#0EA5E9',
                                        }}>
                                            {fid}
                                        </span>
                                    ))}
                                </div>
                            </div>
                            {!isApproved && pendingFpQueue.length > 0 && (
                                <div>
                                    <span style={labelStyle}>Add Pending FP Queue Items</span>
                                    <div style={{ maxHeight: '200px', overflowY: 'auto', marginTop: '0.25rem' }}>
                                        {pendingFpQueue.map(item => (
                                            <label key={item.id} style={{
                                                display: 'flex', alignItems: 'center', gap: '0.5rem',
                                                padding: '0.35rem 0.5rem', marginBottom: '0.15rem',
                                                borderRadius: '0.25rem',
                                                background: additionalFindingIds.has(item.id) ? 'rgba(245,158,11,0.08)' : 'transparent',
                                                border: `1px solid ${additionalFindingIds.has(item.id) ? 'rgba(245,158,11,0.2)' : 'rgba(255,255,255,0.04)'}`,
                                                cursor: 'pointer',
                                            }}>
                                                <input type="checkbox" checked={additionalFindingIds.has(item.id)}
                                                    onChange={() => setAdditionalFindingIds(prev => {
                                                        const next = new Set(prev);
                                                        if (next.has(item.id)) next.delete(item.id); else next.add(item.id);
                                                        return next;
                                                    })}
                                                    style={{ accentColor: '#F59E0B', width: '13px', height: '13px' }}
                                                />
                                                <span style={{ fontFamily: 'monospace', fontSize: '0.72rem', color: '#CBD5E1' }}>{item.finding_id}</span>
                                                {item.hostname && <span style={{ fontFamily: 'monospace', fontSize: '0.62rem', color: '#64748B' }}>{item.hostname}</span>}
                                            </label>
                                        ))}
                                    </div>
                                    <button onClick={handleAddFindings} disabled={saving || additionalFindingIds.size === 0} style={{
                                        marginTop: '0.5rem', padding: '0.4rem 1rem',
                                        background: additionalFindingIds.size > 0 ? 'rgba(245,158,11,0.15)' : 'transparent',
                                        border: `1px solid ${additionalFindingIds.size > 0 ? 'rgba(245,158,11,0.4)' : 'rgba(255,255,255,0.05)'}`,
                                        borderRadius: '0.375rem',
                                        color: additionalFindingIds.size > 0 ? '#F59E0B' : '#334155',
                                        fontSize: '0.75rem', fontWeight: '700', cursor: additionalFindingIds.size > 0 ? 'pointer' : 'not-allowed',
                                        fontFamily: 'monospace', textTransform: 'uppercase', letterSpacing: '0.05em',
                                    }}>
                                        {saving ? 'Adding…' : `Add ${additionalFindingIds.size} Finding(s)`}
                                    </button>
                                </div>
                            )}
                            {!isApproved && pendingFpQueue.length === 0 && (
                                <span style={{ fontFamily: 'monospace', fontSize: '0.72rem', color: '#475569' }}>No pending FP queue items available to add.</span>
                            )}
                        </div>
                    )}

                    {/* Attachments tab */}
                    {activeTab === 'attachments' && (
                        <div>
                            <div style={{ marginBottom: '0.75rem' }}>
                                <span style={labelStyle}>Attachments from Initial Submission ({existingAttachments.length})</span>
                                {existingAttachments.length === 0 ? (
                                    <div style={{ fontFamily: 'monospace', fontSize: '0.72rem', color: '#475569', marginTop: '0.25rem' }}>No attachments were included in the original submission.</div>
                                ) : (
                                    <div style={{ marginTop: '0.25rem' }}>
                                        {existingAttachments.map((att, idx) => (
                                            <div key={idx} style={{
                                                display: 'flex', alignItems: 'center', gap: '0.5rem',
                                                padding: '0.3rem 0.5rem', marginBottom: '0.15rem',
                                                borderRadius: '0.25rem',
                                                background: 'rgba(14,165,233,0.04)',
                                                border: '1px solid rgba(14,165,233,0.1)',
                                            }}>
                                                <FileText style={{ width: '12px', height: '12px', color: '#0EA5E9', flexShrink: 0 }} />
                                                <span style={{ fontFamily: 'monospace', fontSize: '0.72rem', color: '#CBD5E1', flex: 1 }}>{att.filename}</span>
                                                <span style={{
                                                    fontFamily: 'monospace', fontSize: '0.6rem', fontWeight: '600',
                                                    color: att.success ? '#10B981' : '#EF4444',
                                                }}>
                                                    {att.success ? 'OK' : 'FAILED'}
                                                </span>
                                            </div>
                                        ))}
                                    </div>
                                )}
                            </div>
                            {!isApproved && (
                                <div style={{ marginTop: '0.75rem' }}>
                                    <AttachmentSourcePicker
                                        files={files}
                                        onFilesChange={setFiles}
                                        libraryDocs={libraryDocs}
                                        onLibraryDocsChange={setLibraryDocs}
                                        disabled={isApproved}
                                    />
                                    {(files.length > 0 || libraryDocs.length > 0) && (
                                        <button
                                            onClick={handleUploadAttachments}
                                            disabled={saving}
                                            style={{
                                                marginTop: '0.5rem',
                                                padding: '0.4rem 1rem',
                                                background: saving ? 'rgba(245,158,11,0.08)' : 'rgba(245,158,11,0.15)',
                                                border: `1px solid ${saving ? 'rgba(245,158,11,0.15)' : 'rgba(245,158,11,0.4)'}`,
                                                borderRadius: '0.375rem',
                                                color: saving ? '#92700C' : '#F59E0B',
                                                fontSize: '0.75rem',
                                                fontWeight: '700',
                                                cursor: saving ? 'not-allowed' : 'pointer',
                                                fontFamily: 'monospace',
                                                textTransform: 'uppercase',
                                                letterSpacing: '0.05em',
                                            }}
                                        >
                                            {saving ? 'Uploading…' : `Upload ${files.length + libraryDocs.length} Attachment(s)`}
                                        </button>
                                    )}
                                </div>
                            )}
                        </div>
                    )}

                    {/* History tab */}
                    {activeTab === 'history' && (
                        <div>
                            {/* Ivanti reviewer notes (rework/approval/previous state feedback) */}
                            {(() => {
                                const safeText = (val) => {
                                    if (!val) return null;
                                    if (typeof val === 'string') return val;
                                    return null;
                                };
                                const notes = [
                                    safeText(submission.ivanti_rework_note) && { label: 'Rework Note', text: safeText(submission.ivanti_rework_note) },
                                    safeText(submission.ivanti_approval_note) && { label: 'Approval Note', text: safeText(submission.ivanti_approval_note) },
                                    safeText(submission.ivanti_current_state_notes) && { label: 'Current State Notes', text: safeText(submission.ivanti_current_state_notes) },
                                    safeText(submission.ivanti_previous_state_notes) && { label: 'Previous State Notes', text: safeText(submission.ivanti_previous_state_notes) },
                                ].filter(Boolean);
                                return notes.length > 0 ? notes.map((note, idx) => (
                                    <div key={idx} style={{
                                        padding: '0.625rem 0.75rem', marginBottom: '0.5rem',
                                        borderRadius: '0.375rem',
                                        background: 'rgba(245,158,11,0.06)', border: '1px solid rgba(245,158,11,0.2)',
                                    }}>
                                        <div style={{ fontFamily: 'monospace', fontSize: '0.68rem', fontWeight: '600', color: '#F59E0B', textTransform: 'uppercase', letterSpacing: '0.06em', marginBottom: '0.35rem' }}>
                                            {note.label}
                                        </div>
                                        <div style={{ fontFamily: 'monospace', fontSize: '0.72rem', color: '#CBD5E1', whiteSpace: 'pre-wrap', lineHeight: 1.5 }}>
                                            {note.text}
                                        </div>
                                    </div>
                                )) : null;
                            })()}
                            {history.length === 0 ? (
                                <div style={{ fontFamily: 'monospace', fontSize: '0.72rem', color: '#64748B', textAlign: 'center', padding: '2rem 0' }}>
                                    {/* ⚠️ CONVENTION: Use lucide-react icons instead of raw HTML entities/emoji */}
                                    <div style={{ fontSize: '1.5rem', marginBottom: '0.5rem', opacity: 0.4 }}>&#128203;</div>
                                    No history entries yet.
                                    <div style={{ fontSize: '0.65rem', color: '#475569', marginTop: '0.35rem' }}>
                                        Changes to this submission will appear here.
                                    </div>
                                </div>
                            ) : history.map((entry, idx) => {
                                const details = (() => {
                                    try { return JSON.parse(entry.change_details_json || '{}'); } catch { return {}; }
                                })();
                                return (
                                    <div key={entry.id || idx} style={{
                                        padding: '0.5rem 0.625rem', marginBottom: '0.35rem',
                                        borderRadius: '0.25rem',
                                        background: 'rgba(14,165,233,0.04)',
                                        border: '1px solid rgba(14,165,233,0.08)',
                                    }}>
                                        <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
                                            <span style={{
                                                fontFamily: 'monospace', fontSize: '0.68rem', fontWeight: '600',
                                                color: '#0EA5E9', textTransform: 'uppercase',
                                            }}>
                                                {(entry.change_type || '').replace(/_/g, ' ')}
                                            </span>
                                            <span style={{ fontFamily: 'monospace', fontSize: '0.62rem', color: '#475569' }}>
                                                {entry.created_at ? new Date(entry.created_at).toLocaleString() : ''}
                                            </span>
                                        </div>
                                        <div style={{ fontFamily: 'monospace', fontSize: '0.65rem', color: '#64748B', marginTop: '0.2rem' }}>
                                            by {entry.username || 'unknown'}
                                        </div>
                                        {entry.change_type === 'status_changed' && details.from && (
                                            <div style={{ fontFamily: 'monospace', fontSize: '0.65rem', color: '#94A3B8', marginTop: '0.15rem' }}>
                                                {details.from} → {details.to}
                                            </div>
                                        )}
                                        {entry.change_type === 'findings_added' && details.addedFindingIds && (
                                            <div style={{ marginTop: '0.15rem' }}>
                                                <div style={{ fontFamily: 'monospace', fontSize: '0.65rem', color: '#94A3B8' }}>
                                                    +{details.addedFindingIds.length} finding(s):
                                                </div>
                                                <div style={{ display: 'flex', flexWrap: 'wrap', gap: '0.2rem', marginTop: '0.2rem' }}>
                                                    {details.addedFindingIds.map(fid => (
                                                        <span key={fid} style={{
                                                            padding: '0.05rem 0.3rem', borderRadius: '0.15rem',
                                                            background: 'rgba(14,165,233,0.08)', border: '1px solid rgba(14,165,233,0.2)',
                                                            fontFamily: 'monospace', fontSize: '0.6rem', color: '#0EA5E9',
                                                        }}>
                                                            {fid}
                                                        </span>
                                                    ))}
                                                </div>
                                            </div>
                                        )}
                                        {entry.change_type === 'attachments_added' && details.files && (
                                            <div style={{ fontFamily: 'monospace', fontSize: '0.65rem', color: '#94A3B8', marginTop: '0.15rem' }}>
                                                {details.files.filter(f => f.success).length} of {details.files.length} file(s) uploaded
                                            </div>
                                        )}
                                    </div>
                                );
                            })}
                        </div>
                    )}
                </div>
            </div>
        </div>,
        document.body
    );

    return (
        <>
            {portal}
            {showRequeueDialog && <RequeueConfirmDialog
                submission={submission}
                onClose={() => setShowRequeueDialog(false)}
                onSuccess={(data) => {
                    setShowRequeueDialog(false);
                    setResult({ type: 'success', message: `Re-queued ${data.count} finding(s) as ${data.items[0]?.workflow_type || 'new workflow'}` });
                    if (onSuccess) onSuccess();
                }}
            />}
        </>
    );
}

// ---------------------------------------------------------------------------
// SelectionToolbar — batch action bar for multi-selected findings
// ---------------------------------------------------------------------------
function SelectionToolbar({ count, workflowType, vendor, submitting, error, onWorkflowChange, onVendorChange, onSubmit, onClear }) {
    const isCard = workflowType === 'CARD' || workflowType === 'GRANITE' || workflowType === 'DECOM';
    const canSubmit = !submitting && (isCard || vendor.trim().length > 0);

    return (
        <div style={{
            position: 'sticky', top: 0, zIndex: 20,
            display: 'flex', alignItems: 'center', gap: '0.75rem', flexWrap: 'wrap',
            padding: '0.625rem 1rem',
            background: 'linear-gradient(180deg, #0F1A2E 0%, #0A1628 100%)',
            border: '1px solid rgba(14,165,233,0.25)',
            borderRadius: '0.375rem',
            marginBottom: '0.5rem',
        }}>
            {/* Count badge */}
            <span style={{
                display: 'inline-flex', alignItems: 'center', gap: '0.375rem',
                fontFamily: 'monospace', fontSize: '0.75rem', fontWeight: '700', color: '#E2E8F0',
            }}>
                <span style={{
                    display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
                    minWidth: '22px', height: '22px', padding: '0 6px',
                    background: 'rgba(14,165,233,0.2)', border: '1px solid rgba(14,165,233,0.4)',
                    borderRadius: '999px', fontFamily: 'monospace', fontSize: '0.7rem', fontWeight: '700', color: '#0EA5E9',
                }}>
                    {count}
                </span>
                selected
            </span>

            {/* Workflow type toggles */}
            <div style={{ display: 'flex', gap: '0.25rem' }}>
                {[
                    { type: 'FP', color: '#F59E0B', rgb: '245,158,11' },
                    { type: 'Archer', color: '#0EA5E9', rgb: '14,165,233' },
                    { type: 'CARD', color: '#10B981', rgb: '16,185,129' },
                    { type: 'GRANITE', color: '#A1887F', rgb: '161,136,127' },
                    { type: 'DECOM', color: '#EF4444', rgb: '239,68,68' },
                ].map(({ type, color, rgb }) => {
                    const active = workflowType === type;
                    return (
                        <button
                            key={type}
                            onClick={() => onWorkflowChange(type)}
                            style={{
                                padding: '0.25rem 0.5rem',
                                background: active ? `rgba(${rgb},0.2)` : 'transparent',
                                border: `1px solid rgba(${rgb},${active ? '0.5' : '0.15'})`,
                                borderRadius: '0.25rem',
                                color: active ? color : '#475569',
                                fontFamily: 'monospace', fontSize: '0.68rem', fontWeight: '700',
                                cursor: 'pointer', textTransform: 'uppercase', letterSpacing: '0.05em',
                                transition: 'all 0.12s',
                            }}
                        >
                            {type}
                        </button>
                    );
                })}
            </div>

            {/* Vendor input or CARD indicator */}
            {isCard ? (
                <span style={{
                    fontFamily: 'monospace', fontSize: '0.68rem', color: '#10B981',
                    padding: '0.25rem 0.5rem',
                    background: 'rgba(16,185,129,0.06)', border: '1px solid rgba(16,185,129,0.2)',
                    borderRadius: '0.25rem',
                }}>
                    No vendor required
                </span>
            ) : (
                <input
                    type="text"
                    value={vendor}
                    onChange={(e) => onVendorChange(e.target.value)}
                    placeholder="Vendor / Platform"
                    style={{
                        width: '160px', boxSizing: 'border-box',
                        background: 'rgba(14,165,233,0.05)', border: '1px solid rgba(14,165,233,0.2)',
                        borderRadius: '0.25rem', padding: '0.3rem 0.5rem',
                        color: '#CBD5E1', fontSize: '0.75rem', fontFamily: 'monospace', outline: 'none',
                    }}
                    onKeyDown={(e) => { if (e.key === 'Enter' && canSubmit) onSubmit(); }}
                />
            )}

            {/* Add to Queue button */}
            <button
                onClick={onSubmit}
                disabled={!canSubmit}
                style={{
                    padding: '0.3rem 0.75rem',
                    background: canSubmit ? 'rgba(14,165,233,0.15)' : 'transparent',
                    border: `1px solid rgba(14,165,233,${canSubmit ? '0.4' : '0.1'})`,
                    borderRadius: '0.25rem',
                    color: canSubmit ? '#0EA5E9' : '#334155',
                    fontFamily: 'monospace', fontSize: '0.72rem', fontWeight: '600',
                    cursor: canSubmit ? 'pointer' : 'not-allowed',
                    textTransform: 'uppercase', letterSpacing: '0.05em',
                    transition: 'all 0.12s',
                }}
            >
                {submitting ? 'Adding…' : 'Add to Queue'}
            </button>

            {/* Clear selection */}
            <button
                onClick={onClear}
                style={{
                    background: 'none', border: 'none', cursor: 'pointer',
                    color: '#475569', padding: '4px', lineHeight: 1,
                }}
                title="Clear selection"
            >
                <X style={{ width: '16px', height: '16px' }} />
            </button>

            {/* Error message */}
            {error && (
                <span style={{
                    fontFamily: 'monospace', fontSize: '0.68rem', color: '#EF4444',
                    display: 'flex', alignItems: 'center', gap: '0.25rem',
                }}>
                    <AlertCircle style={{ width: '12px', height: '12px' }} />
                    {error}
                </span>
            )}
        </div>
    );
}

// ---------------------------------------------------------------------------
// RowVisibilityManager — popover for viewing and restoring hidden rows
// ---------------------------------------------------------------------------
function RowVisibilityManager({ hiddenRowIds, findings, onRestore, onRestoreAll }) {
    const [open, setOpen] = useState(false);
    const panelRef        = useRef(null);
    const btnRef          = useRef(null);

    // Close on outside click (same pattern as ColumnManager)
    useEffect(() => {
        if (!open) return;
        const handler = (e) => {
            if (!panelRef.current?.contains(e.target) && !btnRef.current?.contains(e.target)) setOpen(false);
        };
        document.addEventListener('mousedown', handler);
        return () => document.removeEventListener('mousedown', handler);
    }, [open]);

    const hiddenCount = hiddenRowIds.size;

    // Build list of hidden findings with title lookup
    const hiddenEntries = useMemo(() => {
        const ids = [...hiddenRowIds];
        return ids.map(id => {
            const finding = findings.find(f => String(f.id) === String(id));
            return { id, title: finding ? finding.title : null };
        });
    }, [hiddenRowIds, findings]);

    return (
        <div style={{ position: 'relative' }}>
            <button
                ref={btnRef}
                onClick={() => setOpen(p => !p)}
                style={{
                    display: 'flex', alignItems: 'center', gap: '0.375rem',
                    padding: '0.35rem 0.75rem',
                    background: open ? 'rgba(14,165,233,0.15)' : 'rgba(14,165,233,0.08)',
                    border: `1px solid rgba(14,165,233,${open ? '0.5' : '0.2'})`,
                    borderRadius: '0.375rem',
                    color: '#94a3b8', cursor: 'pointer',
                    fontFamily: 'monospace', fontSize: '0.65rem', fontWeight: '600',
                    textTransform: 'uppercase', letterSpacing: '0.05em',
                }}
            >
                <EyeOff style={{ width: '13px', height: '13px' }} />
                Hidden ({hiddenCount})
            </button>

            {open && (
                <div
                    ref={panelRef}
                    style={{
                        position: 'absolute', top: 'calc(100% + 8px)', right: 0,
                        width: '300px', zIndex: 100,
                        background: 'linear-gradient(135deg, rgba(15,23,42,0.98), rgba(30,41,59,0.98))',
                        border: '1px solid rgba(14,165,233,0.25)',
                        borderRadius: '8px',
                        boxShadow: '0 8px 32px rgba(0,0,0,0.5)',
                        padding: '0.5rem',
                        maxHeight: '320px',
                        overflowY: 'auto',
                    }}
                >
                    {/* Header */}
                    <div style={{
                        fontSize: '0.65rem', color: '#475569', fontFamily: 'monospace',
                        textTransform: 'uppercase', letterSpacing: '0.1em',
                        padding: '0.25rem 0.5rem 0.5rem',
                        borderBottom: '1px solid rgba(255,255,255,0.05)',
                        marginBottom: '0.375rem',
                    }}>
                        Hidden Rows
                    </div>

                    {hiddenCount === 0 ? (
                        <div style={{
                            padding: '1rem 0.5rem',
                            textAlign: 'center',
                            fontFamily: 'monospace', fontSize: '0.7rem', color: '#475569',
                        }}>
                            No rows hidden
                        </div>
                    ) : (
                        <>
                            {hiddenEntries.map(entry => (
                                <div
                                    key={entry.id}
                                    style={{
                                        display: 'flex', alignItems: 'center', gap: '0.5rem',
                                        padding: '0.4rem 0.5rem', borderRadius: '0.25rem',
                                        transition: 'background 0.1s',
                                    }}
                                >
                                    <div style={{ flex: 1, minWidth: 0 }}>
                                        <div style={{
                                            fontFamily: 'monospace', fontSize: '0.7rem', fontWeight: '600',
                                            color: '#CBD5E1', whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis',
                                        }}>
                                            {entry.id}
                                        </div>
                                        {entry.title && (
                                            <div style={{
                                                fontFamily: 'monospace', fontSize: '0.62rem', color: '#64748B',
                                                whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis',
                                                marginTop: '1px',
                                            }}>
                                                {entry.title}
                                            </div>
                                        )}
                                    </div>
                                    <button
                                        onClick={() => onRestore(entry.id)}
                                        title="Restore row"
                                        style={{
                                            background: 'none', border: 'none', cursor: 'pointer',
                                            padding: '2px', color: '#334155', lineHeight: 1,
                                            transition: 'color 0.15s',
                                        }}
                                        onMouseEnter={e => { e.currentTarget.style.color = '#0EA5E9'; }}
                                        onMouseLeave={e => { e.currentTarget.style.color = '#334155'; }}
                                    >
                                        <Eye style={{ width: '14px', height: '14px' }} />
                                    </button>
                                </div>
                            ))}

                            {/* Restore All button */}
                            <div style={{
                                borderTop: '1px solid rgba(255,255,255,0.05)',
                                marginTop: '0.375rem',
                                paddingTop: '0.375rem',
                            }}>
                                <button
                                    onClick={onRestoreAll}
                                    style={{
                                        width: '100%',
                                        display: 'flex', alignItems: 'center', justifyContent: 'center', gap: '0.375rem',
                                        padding: '0.4rem 0.5rem',
                                        background: 'rgba(14,165,233,0.08)',
                                        border: '1px solid rgba(14,165,233,0.2)',
                                        borderRadius: '0.25rem',
                                        color: '#0EA5E9', cursor: 'pointer',
                                        fontFamily: 'monospace', fontSize: '0.65rem', fontWeight: '600',
                                        textTransform: 'uppercase', letterSpacing: '0.05em',
                                        transition: 'all 0.15s',
                                    }}
                                >
                                    <RotateCcw style={{ width: '12px', height: '12px' }} />
                                    Restore All
                                </button>
                            </div>
                        </>
                    )}
                </div>
            )}
        </div>
    );
}

// ---------------------------------------------------------------------------
// BulkHideToolbar — appears when rows are selected for bulk hiding
// ---------------------------------------------------------------------------
function BulkHideToolbar({ count, onHide, onClear, onAtlasBulk, canWrite }) {
    return (
        <div style={{
            display: 'flex', alignItems: 'center', gap: '0.75rem',
            padding: '0.5rem 1rem',
            background: 'linear-gradient(135deg, rgba(15,23,42,0.95), rgba(30,41,59,0.95))',
            border: '1px solid rgba(14,165,233,0.3)',
            borderRadius: '6px',
            marginBottom: '0.5rem',
        }}>
            {/* Count label */}
            <span style={{
                fontFamily: 'monospace', fontSize: '0.7rem', fontWeight: '600', color: '#e2e8f0',
            }}>
                {count} row{count !== 1 ? 's' : ''} selected
            </span>

            {/* Hide Selected button */}
            <button
                onClick={onHide}
                style={{
                    display: 'inline-flex', alignItems: 'center', gap: '0.375rem',
                    padding: '0.3rem 0.625rem',
                    background: 'rgba(14,165,233,0.12)',
                    border: '1px solid rgba(79,195,247,0.35)',
                    borderRadius: '0.25rem',
                    color: '#4fc3f7',
                    fontFamily: 'monospace', fontSize: '0.7rem', fontWeight: '600',
                    cursor: 'pointer',
                    transition: 'all 0.15s',
                }}
            >
                <EyeOff style={{ width: '12px', height: '12px' }} />
                Hide Selected
            </button>

            {/* Bulk Atlas Action Plan button */}
            {canWrite && onAtlasBulk && (
                <button
                    onClick={onAtlasBulk}
                    style={{
                        display: 'inline-flex', alignItems: 'center', gap: '0.375rem',
                        padding: '0.3rem 0.625rem',
                        background: 'rgba(14,165,233,0.12)',
                        border: '1px solid rgba(79,195,247,0.35)',
                        borderRadius: '0.25rem',
                        color: '#4fc3f7',
                        fontFamily: 'monospace', fontSize: '0.7rem', fontWeight: '600',
                        cursor: 'pointer',
                        transition: 'all 0.15s',
                    }}
                >
                    <Database style={{ width: '12px', height: '12px' }} />
                    Atlas Action Plan
                </button>
            )}

            {/* Clear button */}
            <button
                onClick={onClear}
                style={{
                    background: 'none', border: 'none',
                    fontFamily: 'monospace', fontSize: '0.7rem', color: '#64748B',
                    cursor: 'pointer', padding: '0.3rem 0.375rem',
                    transition: 'color 0.15s',
                }}
            >
                Clear
            </button>
        </div>
    );
}

// ---------------------------------------------------------------------------
// BulkAtlasModal — modal for creating action plans on multiple hosts at once
// ---------------------------------------------------------------------------
const BULK_PLAN_TYPES = ['decommission', 'remediation', 'false_positive', 'risk_acceptance', 'scan_exclusion'];
const BULK_PLAN_TYPE_COLORS = {
    remediation: '#0EA5E9', decommission: '#EF4444', false_positive: '#F59E0B',
    risk_acceptance: '#A855F7', scan_exclusion: '#64748B',
};
const NEEDS_QUALYS = new Set(['remediation', 'false_positive', 'risk_acceptance']);

function BulkAtlasModal({ selectedFindings, onClose, onSuccess }) {
    const [planType, setPlanType]       = useState('risk_acceptance');
    const [commitDate, setCommitDate]   = useState('');
    const [jiraVnr, setJiraVnr]         = useState('');
    const [archerExc, setArcherExc]     = useState('');
    const [submitting, setSubmitting]   = useState(false);
    const [error, setError]             = useState(null);
    const [result, setResult]           = useState(null);
    const [typeOpen, setTypeOpen]       = useState(false);
    const typeRef = useRef(null);

    // Vulnerability loading state
    const [vulnsLoading, setVulnsLoading] = useState(false);
    const [vulnsError, setVulnsError]     = useState(null);
    const [availableQualys, setAvailableQualys] = useState([]);
    const [selectedQualys, setSelectedQualys]   = useState(new Set());

    // Close type dropdown on outside click
    useEffect(() => {
        if (!typeOpen) return;
        const handler = (e) => { if (typeRef.current && !typeRef.current.contains(e.target)) setTypeOpen(false); };
        document.addEventListener('mousedown', handler);
        return () => document.removeEventListener('mousedown', handler);
    }, [typeOpen]);

    // Deduplicate host IDs from selected findings
    const hostEntries = useMemo(() => {
        const seen = new Map();
        for (const f of selectedFindings) {
            if (f.hostId && !seen.has(f.hostId)) {
                seen.set(f.hostId, {
                    hostId: f.hostId,
                    hostName: f.overrides?.hostName || f.hostName || f.ipAddress || String(f.hostId),
                    findingId: f.id ? Number(f.id) : null,
                });
            }
        }
        return [...seen.values()];
    }, [selectedFindings]);

    const hostIds = useMemo(() => hostEntries.map(h => h.hostId), [hostEntries]);

    // Fetch vulnerabilities from Atlas when modal opens
    useEffect(() => {
        if (hostIds.length === 0) return;
        let cancelled = false;
        const fetchVulns = async () => {
            setVulnsLoading(true);
            setVulnsError(null);
            try {
                const res = await fetch(`${API_BASE}/atlas/hosts/vulnerabilities`, {
                    method: 'POST',
                    credentials: 'include',
                    headers: { 'Content-Type': 'application/json' },
                    body: JSON.stringify({ host_ids: hostIds }),
                });
                if (!res.ok) {
                    const data = await res.json().catch(() => ({}));
                    throw new Error(data.error || `Failed to fetch vulnerabilities (${res.status})`);
                }
                const data = await res.json();
                if (cancelled) return;

                // Parse response — Atlas returns { "host_id": [ { qualys_id, title, ... }, ... ], ... }
                const qualysMap = new Map();
                if (data && typeof data === 'object' && !Array.isArray(data)) {
                    for (const [, vulnList] of Object.entries(data)) {
                        if (!Array.isArray(vulnList)) continue;
                        for (const vuln of vulnList) {
                            const qid = vuln.qualys_id || vuln.sourceId;
                            if (!qid) continue;
                            if (!qualysMap.has(qid)) {
                                qualysMap.set(qid, {
                                    qualys_id: qid,
                                    title: vuln.title || qid,
                                    count: 1,
                                });
                            } else {
                                qualysMap.get(qid).count++;
                            }
                        }
                    }
                }

                const sorted = [...qualysMap.values()].sort((a, b) => b.count - a.count);
                setAvailableQualys(sorted);
                setSelectedQualys(new Set(sorted.map(q => q.qualys_id)));
            } catch (err) {
                if (!cancelled) setVulnsError(err.message);
            } finally {
                if (!cancelled) setVulnsLoading(false);
            }
        };
        fetchVulns();
        return () => { cancelled = true; };
    }, [hostIds]);

    const toggleQualys = (qid) => {
        setSelectedQualys(prev => {
            const next = new Set(prev);
            if (next.has(qid)) next.delete(qid); else next.add(qid);
            return next;
        });
    };

    const toggleAllQualys = () => {
        if (selectedQualys.size === availableQualys.length) {
            setSelectedQualys(new Set());
        } else {
            setSelectedQualys(new Set(availableQualys.map(q => q.qualys_id)));
        }
    };

    const handleSubmit = async () => {
        if (!commitDate) { setError('Commit date is required'); return; }
        if (hostIds.length === 0) { setError('No valid host IDs in selection'); return; }
        const needsQualys = NEEDS_QUALYS.has(planType);
        if (needsQualys && selectedQualys.size === 0 && availableQualys.length > 0) {
            setError(`At least one Qualys ID is required for ${planType.replace(/_/g, ' ')} plans`);
            return;
        }

        setSubmitting(true);
        setError(null);
        try {
            // If qualys IDs are selected, iterate per-qualys; otherwise send one request without qualys_id
            const qualysIds = (needsQualys && selectedQualys.size > 0) ? [...selectedQualys] : [null];
            const results = [];

            for (const qid of qualysIds) {
                const body = { host_ids: hostIds, plan_type: planType, commit_date: commitDate };
                if (qid) body.qualys_id = qid;
                // When no qualys_id is available, include the first finding ID per host
                // so Atlas can associate the plan with a specific vulnerability
                if (!qid && needsQualys) {
                    const firstWithFinding = hostEntries.find(h => h.findingId);
                    if (firstWithFinding) body.active_host_findings_id = firstWithFinding.findingId;
                }
                if (jiraVnr.trim()) body.jira_vnr = jiraVnr.trim();
                if (archerExc.trim()) body.archer_exc = archerExc.trim();

                const res = await fetch(`${API_BASE}/atlas/hosts/bulk-action-plans`, {
                    method: 'POST',
                    credentials: 'include',
                    headers: { 'Content-Type': 'application/json' },
                    body: JSON.stringify(body),
                });
                const data = await res.json().catch(() => ({}));
                if (!res.ok) {
                    results.push({ qualys_id: qid, success: false, error: data.error || data.detail || `Failed (${res.status})` });
                } else {
                    results.push({ qualys_id: qid, success: true, data });
                }
            }

            const succeeded = results.filter(r => r.success).length;
            const failed = results.filter(r => !r.success);

            if (failed.length > 0 && succeeded === 0) {
                throw new Error(failed[0].error);
            }

            setResult({ succeeded, failed: failed.length, total: results.length, details: results });
            if (onSuccess) onSuccess();
        } catch (err) {
            setError(err.message);
        } finally {
            setSubmitting(false);
        }
    };

    const inputSt = {
        width: '100%', boxSizing: 'border-box',
        background: 'rgba(14,165,233,0.06)', border: '1px solid rgba(14,165,233,0.2)',
        borderRadius: '0.375rem', color: '#E2E8F0', padding: '0.5rem 0.625rem',
        fontSize: '0.78rem', fontFamily: "'JetBrains Mono', monospace", outline: 'none',
    };
    const labelSt = {
        display: 'block', fontSize: '0.68rem', fontFamily: "'JetBrains Mono', monospace",
        color: '#94A3B8', marginBottom: '0.3rem', textTransform: 'uppercase', letterSpacing: '0.05em',
    };

    return ReactDOM.createPortal(
        <>
            {/* Backdrop */}
            <div onClick={onClose} style={{ position: 'fixed', inset: 0, background: 'rgba(0,0,0,0.5)', zIndex: 60 }} />

            {/* Modal */}
            <div style={{
                position: 'fixed', top: '50%', left: '50%', transform: 'translate(-50%, -50%)',
                width: '520px', maxHeight: '80vh', overflowY: 'auto',
                background: '#0A1220',
                border: '1px solid rgba(14,165,233,0.25)',
                borderRadius: '0.5rem',
                boxShadow: '0 16px 48px rgba(0,0,0,0.6)',
                zIndex: 61,
                fontFamily: "'JetBrains Mono', monospace",
            }}>
                {/* Header */}
                <div style={{
                    display: 'flex', justifyContent: 'space-between', alignItems: 'center',
                    padding: '1rem 1.25rem',
                    borderBottom: '1px solid rgba(255,255,255,0.06)',
                }}>
                    <div style={{ display: 'flex', alignItems: 'center', gap: '0.5rem' }}>
                        <Database style={{ width: 16, height: 16, color: '#0EA5E9' }} />
                        <span style={{ fontSize: '0.85rem', fontWeight: 700, color: '#E2E8F0' }}>
                            Bulk Atlas Action Plan
                        </span>
                    </div>
                    <button onClick={onClose} style={{ background: 'none', border: 'none', cursor: 'pointer', color: '#475569', padding: '0.25rem' }}>
                        <X style={{ width: 18, height: 18 }} />
                    </button>
                </div>

                {/* Success state */}
                {result ? (
                    <div style={{ padding: '1.5rem 1.25rem', textAlign: 'center' }}>
                        <Check style={{ width: 32, height: 32, color: '#10B981', margin: '0 auto 0.75rem' }} />
                        <div style={{ fontSize: '0.85rem', color: '#E2E8F0', fontWeight: 600, marginBottom: '0.5rem' }}>
                            Action plans created
                        </div>
                        <div style={{ fontSize: '0.72rem', color: '#94A3B8', marginBottom: '0.5rem' }}>
                            {hostIds.length} host{hostIds.length !== 1 ? 's' : ''} — {planType.replace(/_/g, ' ')}
                        </div>
                        {result.total > 1 && (
                            <div style={{ fontSize: '0.72rem', color: '#94A3B8', marginBottom: '0.5rem' }}>
                                {result.succeeded} of {result.total} Qualys ID{result.total !== 1 ? 's' : ''} succeeded
                                {result.failed > 0 && <span style={{ color: '#F87171' }}> — {result.failed} failed</span>}
                            </div>
                        )}
                        {result.details?.filter(d => !d.success).map((d, i) => (
                            <div key={i} style={{ fontSize: '0.68rem', color: '#F87171', marginTop: '0.25rem' }}>
                                {d.qualys_id}: {d.error}
                            </div>
                        ))}
                        <button onClick={onClose} style={{
                            marginTop: '1rem',
                            padding: '0.5rem 1.25rem',
                            background: 'rgba(14,165,233,0.15)', border: '1px solid #0EA5E9',
                            borderRadius: '0.375rem', color: '#38BDF8',
                            fontSize: '0.75rem', fontWeight: 600, cursor: 'pointer',
                        }}>
                            Close
                        </button>
                    </div>
                ) : (
                    <div style={{ padding: '1rem 1.25rem' }}>
                        {/* Host summary */}
                        <div style={{
                            marginBottom: '1rem', padding: '0.625rem 0.75rem',
                            background: 'rgba(14,165,233,0.06)',
                            border: '1px solid rgba(14,165,233,0.15)',
                            borderRadius: '0.375rem',
                        }}>
                            <div style={{ fontSize: '0.68rem', color: '#94A3B8', textTransform: 'uppercase', letterSpacing: '0.05em', marginBottom: '0.4rem' }}>
                                {hostEntries.length} unique host{hostEntries.length !== 1 ? 's' : ''} from {selectedFindings.length} selected finding{selectedFindings.length !== 1 ? 's' : ''}
                            </div>
                            <div style={{ maxHeight: '100px', overflowY: 'auto', fontSize: '0.72rem', color: '#CBD5E1', lineHeight: 1.6 }}>
                                {hostEntries.map(h => (
                                    <div key={h.hostId} style={{ display: 'flex', justifyContent: 'space-between', gap: '0.5rem' }}>
                                        <span style={{ overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{h.hostName}</span>
                                        <span style={{ color: '#475569', flexShrink: 0 }}>{h.hostId}</span>
                                    </div>
                                ))}
                            </div>
                        </div>

                        {/* Plan type dropdown */}
                        <div style={{ marginBottom: '0.75rem' }}>
                            <label style={labelSt}>Plan Type</label>
                            <div ref={typeRef} style={{ position: 'relative' }}>
                                <button type="button" onClick={() => setTypeOpen(!typeOpen)} style={{
                                    ...inputSt, display: 'flex', alignItems: 'center', justifyContent: 'space-between',
                                    cursor: 'pointer', textAlign: 'left',
                                    borderColor: typeOpen ? 'rgba(14,165,233,0.5)' : 'rgba(14,165,233,0.2)',
                                }}>
                                    <span style={{ color: BULK_PLAN_TYPE_COLORS[planType], fontWeight: 600, textTransform: 'uppercase', letterSpacing: '0.03em' }}>
                                        {planType.replace(/_/g, ' ')}
                                    </span>
                                    <ChevronDown style={{ width: 14, height: 14, color: '#475569', transform: typeOpen ? 'rotate(180deg)' : 'none', transition: 'transform 0.15s' }} />
                                </button>
                                {typeOpen && (
                                    <div style={{
                                        position: 'absolute', top: '100%', left: 0, right: 0, marginTop: '4px',
                                        background: '#0F1A2E', border: '1px solid rgba(14,165,233,0.25)',
                                        borderRadius: '0.375rem', boxShadow: '0 8px 24px rgba(0,0,0,0.5)',
                                        zIndex: 65, overflow: 'hidden',
                                    }}>
                                        {BULK_PLAN_TYPES.map(t => (
                                            <div key={t} onClick={() => { setPlanType(t); setTypeOpen(false); }} style={{
                                                padding: '0.5rem 0.625rem', cursor: 'pointer',
                                                background: t === planType ? 'rgba(14,165,233,0.12)' : 'transparent',
                                                color: BULK_PLAN_TYPE_COLORS[t], fontSize: '0.78rem',
                                                fontWeight: 600, textTransform: 'uppercase', letterSpacing: '0.03em',
                                            }}
                                                onMouseEnter={e => { if (t !== planType) e.currentTarget.style.background = 'rgba(14,165,233,0.06)'; }}
                                                onMouseLeave={e => { if (t !== planType) e.currentTarget.style.background = 'transparent'; }}
                                            >
                                                {t.replace(/_/g, ' ')}
                                            </div>
                                        ))}
                                    </div>
                                )}
                            </div>
                        </div>

                        {/* Commit date */}
                        <div style={{ marginBottom: '0.75rem' }}>
                            <label style={labelSt}>Commit Date</label>
                            <input type="date" value={commitDate} onChange={e => setCommitDate(e.target.value)}
                                style={{ ...inputSt, colorScheme: 'dark' }} />
                        </div>

                        {/* Optional fields — shown based on plan type */}
                        {/* Qualys ID multi-select — shown for plan types that require it */}
                        {NEEDS_QUALYS.has(planType) && (
                            <div style={{ marginBottom: '0.75rem' }}>
                                <label style={labelSt}>
                                    Qualys IDs
                                    <span style={{ color: '#475569', textTransform: 'none', marginLeft: '0.3rem' }}>
                                        ({selectedQualys.size} of {availableQualys.length} selected)
                                    </span>
                                </label>

                                {vulnsLoading && (
                                    <div style={{ display: 'flex', alignItems: 'center', gap: '0.4rem', padding: '0.5rem 0', color: '#475569', fontSize: '0.72rem' }}>
                                        <Loader style={{ width: 12, height: 12, animation: 'spin 1s linear infinite' }} />
                                        Loading vulnerabilities from Atlas...
                                    </div>
                                )}

                                {vulnsError && (
                                    <div style={{
                                        padding: '0.5rem 0.75rem',
                                        background: 'rgba(239,68,68,0.1)', border: '1px solid rgba(239,68,68,0.3)',
                                        borderRadius: '0.375rem', color: '#F87171', fontSize: '0.72rem',
                                        display: 'flex', alignItems: 'center', gap: '0.4rem',
                                    }}>
                                        <AlertCircle style={{ width: 12, height: 12, flexShrink: 0 }} />{vulnsError}
                                    </div>
                                )}

                                {!vulnsLoading && !vulnsError && availableQualys.length === 0 && (
                                    <div style={{ color: '#475569', fontSize: '0.72rem', fontStyle: 'italic', padding: '0.5rem 0' }}>
                                        No vulnerabilities found in Atlas for these hosts — Qualys ID will be omitted
                                    </div>
                                )}

                                {!vulnsLoading && availableQualys.length > 0 && (
                                    <div style={{
                                        background: 'rgba(14,165,233,0.04)',
                                        border: '1px solid rgba(14,165,233,0.15)',
                                        borderRadius: '0.375rem',
                                        maxHeight: '180px', overflowY: 'auto',
                                    }}>
                                        <div
                                            onClick={toggleAllQualys}
                                            style={{
                                                display: 'flex', alignItems: 'center', gap: '0.5rem',
                                                padding: '0.45rem 0.625rem',
                                                borderBottom: '1px solid rgba(14,165,233,0.1)',
                                                cursor: 'pointer', fontSize: '0.72rem', color: '#94A3B8',
                                            }}
                                        >
                                            <input type="checkbox" readOnly
                                                checked={selectedQualys.size === availableQualys.length && availableQualys.length > 0}
                                                style={{ accentColor: '#0EA5E9', cursor: 'pointer' }}
                                            />
                                            <span style={{ fontWeight: 600 }}>Select All</span>
                                        </div>

                                        {availableQualys.map(q => (
                                            <div
                                                key={q.qualys_id}
                                                onClick={() => toggleQualys(q.qualys_id)}
                                                style={{
                                                    display: 'flex', alignItems: 'center', gap: '0.5rem',
                                                    padding: '0.4rem 0.625rem',
                                                    cursor: 'pointer', fontSize: '0.72rem',
                                                    background: selectedQualys.has(q.qualys_id) ? 'rgba(14,165,233,0.08)' : 'transparent',
                                                    transition: 'background 0.1s',
                                                }}
                                                onMouseEnter={e => { if (!selectedQualys.has(q.qualys_id)) e.currentTarget.style.background = 'rgba(14,165,233,0.04)'; }}
                                                onMouseLeave={e => { if (!selectedQualys.has(q.qualys_id)) e.currentTarget.style.background = 'transparent'; }}
                                            >
                                                <input type="checkbox" readOnly
                                                    checked={selectedQualys.has(q.qualys_id)}
                                                    style={{ accentColor: '#0EA5E9', cursor: 'pointer', flexShrink: 0 }}
                                                />
                                                <div style={{ minWidth: 0, flex: 1 }}>
                                                    <span style={{ color: '#E2E8F0', fontWeight: 600 }}>{q.qualys_id}</span>
                                                    <span style={{ color: '#475569', marginLeft: '0.4rem' }}>
                                                        ({q.count} host{q.count !== 1 ? 's' : ''})
                                                    </span>
                                                    <div style={{ color: '#64748B', fontSize: '0.65rem', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
                                                        {q.title}
                                                    </div>
                                                </div>
                                            </div>
                                        ))}
                                    </div>
                                )}
                            </div>
                        )}
                        {planType === 'false_positive' && (
                            <div style={{ marginBottom: '0.75rem' }}>
                                <label style={labelSt}>Jira VNR <span style={{ color: '#475569', textTransform: 'none' }}>(optional)</span></label>
                                <input value={jiraVnr} onChange={e => setJiraVnr(e.target.value)}
                                    placeholder="VNR-67890" style={inputSt} />
                            </div>
                        )}
                        {(planType === 'risk_acceptance' || planType === 'scan_exclusion') && (
                            <div style={{ marginBottom: '0.75rem' }}>
                                <label style={labelSt}>Archer EXC <span style={{ color: '#475569', textTransform: 'none' }}>(optional)</span></label>
                                <input value={archerExc} onChange={e => setArcherExc(e.target.value)}
                                    placeholder="EXC-54321" style={inputSt} />
                            </div>
                        )}

                        {/* Error */}
                        {error && (
                            <div style={{
                                marginBottom: '0.75rem', padding: '0.5rem 0.75rem',
                                background: 'rgba(239,68,68,0.1)', border: '1px solid rgba(239,68,68,0.3)',
                                borderRadius: '0.375rem', color: '#F87171', fontSize: '0.75rem',
                                display: 'flex', alignItems: 'center', gap: '0.4rem',
                            }}>
                                <AlertCircle style={{ width: 14, height: 14, flexShrink: 0 }} />{error}
                            </div>
                        )}

                        {/* Submit */}
                        <button onClick={handleSubmit} disabled={submitting || vulnsLoading} style={{
                            width: '100%', display: 'flex', alignItems: 'center', justifyContent: 'center', gap: '0.4rem',
                            padding: '0.6rem 1rem',
                            background: (submitting || vulnsLoading) ? 'rgba(14,165,233,0.08)' : 'rgba(14,165,233,0.15)',
                            border: '1px solid #0EA5E9', borderRadius: '0.375rem',
                            color: (submitting || vulnsLoading) ? '#475569' : '#38BDF8',
                            fontSize: '0.78rem', fontWeight: 600, cursor: (submitting || vulnsLoading) ? 'not-allowed' : 'pointer',
                            textTransform: 'uppercase', letterSpacing: '0.05em',
                        }}>
                            {submitting ? <Loader style={{ width: 14, height: 14, animation: 'spin 1s linear infinite' }} /> : <Database style={{ width: 14, height: 14 }} />}
                            {submitting ? 'Creating...' : `Create Plans for ${hostEntries.length} Host${hostEntries.length !== 1 ? 's' : ''}${NEEDS_QUALYS.has(planType) && selectedQualys.size > 0 ? ` × ${selectedQualys.size} QID${selectedQualys.size !== 1 ? 's' : ''}` : ''}`}
                        </button>
                    </div>
                )}
            </div>
        </>,
        document.body
    );
}

// ---------------------------------------------------------------------------
// Main ReportingPage
// ---------------------------------------------------------------------------
export default function VulnerabilityTriagePage({ filterDate, filterEXC }) {
    const { canWrite, getActiveTeamsParam, hasTeams, isAdmin, adminScope } = useAuth();
    const [findings, setFindings]         = useState([]);
    const [total, setTotal]               = useState(null);
    const [syncedAt, setSyncedAt]         = useState(null);
    const [syncStatus, setSyncStatus]     = useState(null);
    const [syncError, setSyncError]       = useState(null);
    const [loading, setLoading]           = useState(false);
    const [syncing, setSyncing]           = useState(false);
    const [statusCounts, setStatusCounts] = useState({ open: 0, closed: 0 });
    const [countsLoading, setCountsLoading] = useState(true);
    const [fpCounts, setFPCounts] = useState({ findingCounts: {}, findingTotal: 0, idCounts: {}, idTotal: 0 });
    const [sort, setSort]                 = useState({ field: 'severity', dir: 'desc' });
    const [columnOrder, setColumnOrder]   = useState(loadColumnOrder);
    const [columnFilters, setColumnFilters] = useState(() =>
        filterDate ? { dueDate: new Set([filterDate]) } : {}
    );
    const [openFilter, setOpenFilter]     = useState(null);
    const filterBtnRefs                   = useRef({});
    const [actionFilter, setActionFilter] = useState(null);
    const [excFilter, setExcFilter]       = useState(filterEXC || null);

    const [selectedIds, setSelectedIds] = useState(new Set());
    const [lastClickedId, setLastClickedId] = useState(null);
    const [batchSubmitting, setBatchSubmitting] = useState(false);
    const [batchError, setBatchError] = useState(null);
    const [batchWorkflowType, setBatchWorkflowType] = useState('FP');
    const [batchVendor, setBatchVendor] = useState('');

    // CVE tooltip state & refs
    const [tooltipCveId, setTooltipCveId]           = useState(null);
    const [tooltipAnchorRect, setTooltipAnchorRect] = useState(null);
    const tooltipCacheRef                           = useRef(new Map());
    const hoverTimerRef                             = useRef(null);

    // Atlas action plan state
    const [metricsTab, setMetricsTab] = useState('ivanti');
    const [atlasStatusMap, setAtlasStatusMap] = useState(new Map());
    const [atlasSyncing, setAtlasSyncing] = useState(false);
    const [atlasError, setAtlasError] = useState(null);
    const [atlasPanelOpen, setAtlasPanelOpen] = useState(false);
    const [atlasSelectedHostId, setAtlasSelectedHostId] = useState(null);
    const [atlasSelectedHostName, setAtlasSelectedHostName] = useState(null);
    const [atlasSelectedFindingId, setAtlasSelectedFindingId] = useState(null);
    const [bulkAtlasOpen, setBulkAtlasOpen] = useState(false);

    // Atlas metrics state (for Atlas Coverage tab donut charts)
    const [atlasMetrics, setAtlasMetrics] = useState(null);
    const [atlasMetricsLoading, setAtlasMetricsLoading] = useState(false);
    const [atlasMetricsError, setAtlasMetricsError] = useState(null);

    // CARD API state — session-level caching for teams list
    const [cardConfigured, setCardConfigured] = useState(false);
    const [cardTeams, setCardTeams] = useState([]);

    const updateColumns = useCallback((newOrder) => {
        setColumnOrder(newOrder);
        saveColumnOrder(newOrder);
    }, []);

    // Hidden row state (row visibility feature)
    const [hiddenRowIds, setHiddenRowIds] = useState(loadHiddenRows);

    const hideRow = useCallback((findingId) => {
        setHiddenRowIds(prev => {
            const next = new Set(prev);
            next.add(String(findingId));
            saveHiddenRows(next);
            return next;
        });
    }, []);

    const restoreRow = useCallback((findingId) => {
        setHiddenRowIds(prev => {
            const next = new Set(prev);
            next.delete(String(findingId));
            saveHiddenRows(next);
            return next;
        });
    }, []);

    const restoreAllRows = useCallback(() => {
        setHiddenRowIds(new Set());
        saveHiddenRows(new Set());
    }, []);

    // Selection state (row visibility feature — bulk hide)
    const [selectedRowIds, setSelectedRowIds] = useState(new Set());

    const toggleRowSelection = useCallback((findingId) => {
        setSelectedRowIds(prev => {
            const next = new Set(prev);
            const id = String(findingId);
            if (next.has(id)) next.delete(id); else next.add(id);
            return next;
        });
    }, []);

    const hideSelectedRows = useCallback(() => {
        setHiddenRowIds(prev => {
            const next = new Set(prev);
            selectedRowIds.forEach(id => next.add(String(id)));
            saveHiddenRows(next);
            return next;
        });
        setSelectedRowIds(new Set());
    }, [selectedRowIds]);

    // CVE tooltip hover handlers
    const handleCveMouseEnter = useCallback((cveId, e) => {
        clearTimeout(hoverTimerRef.current);
        hoverTimerRef.current = setTimeout(() => {
            setTooltipCveId(cveId);
            setTooltipAnchorRect(e.target.getBoundingClientRect());
        }, 300);
    }, []);

    const handleCveMouseLeave = useCallback(() => {
        clearTimeout(hoverTimerRef.current);
        setTooltipCveId(null);
        setTooltipAnchorRect(null);
    }, []);

    const applyState = (data) => {
        setTotal(data.total ?? 0);
        setFindings(data.findings || []);
        setSyncedAt(data.synced_at || null);
        setSyncStatus(data.sync_status || null);
        setSyncError(data.error_message || null);
    };

    const fetchCounts = async () => {
        setCountsLoading(true);
        try {
            // Fetch counts from server — Postgres provides per-BU open+closed counts
            const teamsParam = getActiveTeamsParam();
            const url = teamsParam
                ? `${API_BASE}/ivanti/findings/counts?teams=${encodeURIComponent(teamsParam)}`
                : `${API_BASE}/ivanti/findings/counts`;
            const res  = await fetch(url, { credentials: 'include' });
            const data = await res.json();
            if (res.ok) setStatusCounts({ open: data.open ?? 0, closed: data.closed ?? 0 });
        } catch (e) {
            console.error('Error loading status counts:', e);
        } finally {
            setCountsLoading(false);
        }
    };

    const fetchFPWorkflowCounts = async () => {
        try {
            const teamsParam = getActiveTeamsParam();
            const url = teamsParam
                ? `${API_BASE}/ivanti/findings/fp-workflow-counts?teams=${encodeURIComponent(teamsParam)}`
                : `${API_BASE}/ivanti/findings/fp-workflow-counts`;
            const res  = await fetch(url, { credentials: 'include' });
            const data = await res.json();
            if (res.ok) setFPCounts({
                findingCounts: data.findingCounts || {},
                findingTotal:  data.findingTotal  || 0,
                idCounts:      data.idCounts      || {},
                idTotal:       data.idTotal       || 0,
            });
        } catch (e) {
            console.error('Error loading FP workflow counts:', e);
        }
    };

    const fetchAtlasStatus = useCallback(async () => {
        try {
            const res = await fetch(`${API_BASE}/atlas/status`, { credentials: 'include' });
            if (res.ok) {
                const data = await res.json();
                const map = new Map();
                data.forEach(row => map.set(row.host_id, row));
                setAtlasStatusMap(map);
            }
        } catch (err) {
            console.error('[Atlas] Failed to fetch status:', err.message);
        }
    }, []);

    const fetchAtlasMetrics = useCallback(async () => {
        setAtlasMetricsLoading(true);
        setAtlasMetricsError(null);
        try {
            const res = await fetch(`${API_BASE}/atlas/metrics`, { credentials: 'include' });
            if (res.ok) {
                const data = await res.json();
                setAtlasMetrics(data);
            } else {
                const err = await res.json().catch(() => ({}));
                setAtlasMetricsError(err.error || 'Failed to fetch Atlas metrics');
            }
        } catch (err) {
            setAtlasMetricsError(err.message);
        } finally {
            setAtlasMetricsLoading(false);
        }
    }, []);

    // CARD API — fetch status and teams (session-level caching)
    const cardTeamsFetchedRef = useRef(false);
    const fetchCardStatus = useCallback(async () => {
        try {
            const res = await fetch(`${API_BASE}/card/status`, { credentials: 'include' });
            if (res.ok) {
                const data = await res.json();
                setCardConfigured(data.configured === true);
                if (data.configured && !cardTeamsFetchedRef.current) {
                    cardTeamsFetchedRef.current = true;
                    const teamsRes = await fetch(`${API_BASE}/card/teams`, { credentials: 'include' });
                    if (teamsRes.ok) {
                        const teamsData = await teamsRes.json();
                        const teams = Array.isArray(teamsData)
                            ? teamsData.map(t => t.card_team_name || t._id).filter(Boolean).sort()
                            : [];
                        setCardTeams(teams);
                    }
                }
            }
        } catch (err) {
            console.error('[card-api] Failed to fetch CARD status:', err.message);
        }
    }, []);

    const fetchFindings = async () => {
        setLoading(true);
        try {
            // Always fetch ALL findings — filtering happens client-side for instant scope switching
            const res  = await fetch(`${API_BASE}/ivanti/findings`, { credentials: 'include' });
            const data = await res.json();
            if (res.ok) {
                applyState(data);
                tooltipCacheRef.current.clear();
            }
        } catch (e) {
            console.error('Error loading findings:', e);
        } finally {
            setLoading(false);
        }
    };

    const syncFindings = async () => {
        setSyncing(true);
        try {
            const res  = await fetch(`${API_BASE}/ivanti/findings/sync`, { method: 'POST', credentials: 'include' });
            const data = await res.json();
            if (res.ok) {
                applyState(data);
                tooltipCacheRef.current.clear();
                fetchCounts();           // refresh counts after sync
                fetchFPWorkflowCounts(); // refresh FP workflow counts after sync
            }
        } catch (e) {
            console.error('Error syncing findings:', e);
        } finally {
            setSyncing(false);
        }
    };

    useEffect(() => {
        fetchFindings();
        fetchCounts();
        fetchFPWorkflowCounts();
        fetchQueue();
        fetchFpSubmissions();
        fetchAtlasStatus();
        fetchAtlasMetrics();
        fetchCardStatus();
    }, []); // eslint-disable-line

    // Re-fetch counts when admin scope changes (per-BU counts from Postgres)
    // Silent fetch — no loading spinner, just update the numbers
    useEffect(() => {
        const teamsParam = getActiveTeamsParam();
        const url = teamsParam
            ? `${API_BASE}/ivanti/findings/counts?teams=${encodeURIComponent(teamsParam)}`
            : `${API_BASE}/ivanti/findings/counts`;
        fetch(url, { credentials: 'include' })
            .then(r => r.ok ? r.json() : null)
            .then(data => { if (data) setStatusCounts({ open: data.open ?? 0, closed: data.closed ?? 0 }); })
            .catch(() => {});
        // Also refresh FP workflow counts for the new scope
        fetchFPWorkflowCounts();
    }, [adminScope]); // eslint-disable-line

    // Set/clear a single column filter
    const setColFilter = useCallback((colKey, vals) => {
        setColumnFilters((prev) => {
            if (!vals) {
                const next = { ...prev };
                delete next[colKey];
                return next;
            }
            return { ...prev, [colKey]: vals };
        });
    }, []);

    // Scope findings by selected BU teams (client-side filtering for instant switching)
    const scopedFindings = useMemo(() => {
        const teamsParam = getActiveTeamsParam();
        if (!teamsParam) return findings; // no filter = show all
        const teams = teamsParam.split(',').map(t => t.trim().toUpperCase()).filter(Boolean);
        if (teams.length === 0) return findings;
        return findings.filter(f =>
            teams.some(t => (f.buOwnership || '').toUpperCase().includes(t))
        );
    }, [findings, adminScope]); // eslint-disable-line react-hooks/exhaustive-deps

    // Visible findings — hidden rows removed before any other filtering
    const visibleFindings = useMemo(() => {
        if (hiddenRowIds.size === 0) return scopedFindings;
        return scopedFindings.filter(f => !hiddenRowIds.has(String(f.id)));
    }, [scopedFindings, hiddenRowIds]);

    // Apply all active filters to produce the visible row set
    const filtered = useMemo(() => {
        let result = visibleFindings;

        // Column filters
        const active = Object.entries(columnFilters);
        if (active.length > 0) {
            result = result.filter((f) =>
                active.every(([key, vals]) => {
                    if (!vals || vals.size === 0) return false;
                    const def = COLUMN_DEFS[key];
                    if (def?.multiValue) {
                        const arr = f[key] || [];
                        if (arr.length === 0) return vals.has(EMPTY_SENTINEL);
                        return arr.some((v) => vals.has(String(v).trim()));
                    }
                    const fval = getFilterVal(f, key).trim();
                    return fval === '' ? vals.has(EMPTY_SENTINEL) : vals.has(fval);
                })
            );
        }

        // Action coverage filter (chart segment click)
        if (actionFilter) {
            result = result.filter((f) => classifyFinding(f) === actionFilter);
        }

        // EXC filter (navigated from home page Archer ticket)
        if (excFilter) {
            const upper = excFilter.toUpperCase();
            result = result.filter((f) => (f.note || '').toUpperCase().includes(upper));
        }

        return result;
    }, [visibleFindings, columnFilters, actionFilter, excFilter]);

    // Visible columns in current order
    const visibleCols = columnOrder.filter((c) => c.visible && COLUMN_DEFS[c.key]);

    // Sort filtered results
    const sorted = useMemo(() => [...filtered].sort((a, b) => {
        const av = getVal(a, sort.field);
        const bv = getVal(b, sort.field);
        let cmp = 0;
        if (typeof av === 'number' && typeof bv === 'number') {
            cmp = av - bv;
        } else {
            cmp = String(av).localeCompare(String(bv), undefined, { numeric: true });
        }
        return sort.dir === 'asc' ? cmp : -cmp;
    }), [filtered, sort]);

    // Select/deselect all visible rows
    const toggleSelectAll = useCallback(() => {
        const allVisibleIds = sorted.map(f => String(f.id));
        setSelectedRowIds(prev => {
            if (prev.size === allVisibleIds.length) return new Set(); // deselect all
            return new Set(allVisibleIds); // select all
        });
    }, [sorted]);

    // Prune selection to only include IDs present in the current sorted (visible) rows
    useEffect(() => {
        setSelectedRowIds(prev => {
            const visibleIds = new Set(sorted.map(f => String(f.id)));
            const next = new Set([...prev].filter(id => visibleIds.has(id)));
            return next.size === prev.size ? prev : next;
        });
    }, [sorted]);

    const toggleSort = (key) => {
        setSort((prev) =>
            prev.field === key
                ? { field: key, dir: prev.dir === 'asc' ? 'desc' : 'asc' }
                : { field: key, dir: 'asc' }
        );
    };

    const activeFilterCount = Object.keys(columnFilters).length + (actionFilter ? 1 : 0) + (excFilter ? 1 : 0);

    // Queue state
    const [queueItems, setQueueItems]       = useState([]);
    const [queueOpen, setQueueOpen]         = useState(false);
    const [queueLoading, setQueueLoading]   = useState(false);
    const [addPopover, setAddPopover]       = useState(null); // { finding, anchorRect }
    const [queueForm, setQueueForm]         = useState({ vendor: '', workflowType: 'FP' });

    // FP Workflow modal state
    const [fpModalOpen, setFpModalOpen]     = useState(false);
    const [fpModalItems, setFpModalItems]   = useState([]);

    // FP Submission editing state
    const [fpSubmissionsRaw, setFpSubmissions] = useState([]);
    const [editSubmission, setEditSubmission] = useState(null);

    // Enrich submissions with actual Ivanti workflow state from findings data
    const fpSubmissions = useMemo(() => {
        if (fpSubmissionsRaw.length === 0 || findings.length === 0) return fpSubmissionsRaw;
        const stateMap = {
            'reworked': 'rework', 'rejected': 'rejected', 'expired': 'rejected',
            'approved': 'approved', 'requested': 'submitted', 'actionable': 'submitted',
        };
        return fpSubmissionsRaw.map(sub => {
            let findingIds;
            try { findingIds = JSON.parse(sub.finding_ids_json || '[]'); } catch { return sub; }
            if (findingIds.length === 0) return sub;
            const matchedFinding = findings.find(f =>
                f.workflow && findingIds.includes(String(f.id))
            );
            if (!matchedFinding || !matchedFinding.workflow) return sub;
            const ivantiState = (matchedFinding.workflow.state || '').toLowerCase();
            const mappedStatus = stateMap[ivantiState];
            if (mappedStatus && mappedStatus !== sub.lifecycle_status) {
                return { ...sub, lifecycle_status: mappedStatus };
            }
            return sub;
        });
    }, [fpSubmissionsRaw, findings]);

    // Filtered submissions for QueuePanel display — hide approved and dismissed
    const fpSubmissionsFiltered = useMemo(() => {
        return fpSubmissions.filter(s => s.lifecycle_status !== 'approved' && !s.dismissed_at);
    }, [fpSubmissions]);

    // Queue API helpers
    const fetchQueue = useCallback(async () => {
        setQueueLoading(true);
        try {
            const res  = await fetch(`${API_BASE}/ivanti/todo-queue`, { credentials: 'include' });
            const data = await res.json();
            if (res.ok) setQueueItems(data);
        } catch (e) {
            console.error('Error fetching queue:', e);
        } finally {
            setQueueLoading(false);
        }
    }, []);

    const fetchFpSubmissions = useCallback(async () => {
        try {
            const res = await fetch(`${API_BASE}/ivanti/fp-workflow/submissions`, { credentials: 'include' });
            const data = await res.json();
            if (res.ok) setFpSubmissions(data);
        } catch (e) {
            console.error('Error fetching FP submissions:', e);
        }
    }, []);

    // FP Workflow handlers
    const handleCreateFpWorkflow = useCallback((selectedIds) => {
        const selectedSet = new Set(selectedIds);
        const fpItems = filterFpItems(
            queueItems.filter(item => selectedSet.has(item.id) && item.status === 'pending')
        );
        if (fpItems.length > 0) {
            setFpModalItems(fpItems);
            setFpModalOpen(true);
        }
    }, [queueItems]);

    const handleFpWorkflowSuccess = useCallback(() => {
        fetchQueue();
    }, [fetchQueue]);

    const handleEditSubmission = useCallback((submission) => {
        setEditSubmission(submission);
    }, []);

    const handleEditSuccess = useCallback(() => {
        fetchFpSubmissions();
        fetchQueue();
        fetchFindings();
    }, [fetchFpSubmissions, fetchQueue]); // eslint-disable-line

    const handleDismissSubmission = useCallback((submissionId) => {
        // Optimistically remove the dismissed submission from local state
        setFpSubmissions(prev => prev.filter(s => s.id !== submissionId));
    }, []);

    const addToQueue = useCallback(async () => {
        if (!addPopover) return;
        const { finding } = addPopover;
        try {
            const res = await fetch(`${API_BASE}/ivanti/todo-queue`, {
                method: 'POST',
                credentials: 'include',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify({
                    finding_id:    finding.id,
                    finding_title: finding.title      || null,
                    cves:          finding.cves        || [],
                    ip_address:    finding.ipAddress   || null,
                    hostname:      finding.overrides?.hostName || finding.hostName || null,
                    vendor:        queueForm.vendor.trim(),
                    workflow_type: queueForm.workflowType,
                }),
            });
            const data = await res.json();
            if (res.ok) {
                setQueueItems((prev) => [...prev, data].sort((a, b) =>
                    a.vendor.localeCompare(b.vendor) || a.id - b.id
                ));

                // DECOM: auto-set note and auto-hide the finding
                if (queueForm.workflowType === 'DECOM') {
                    // Set note to DECOM
                    fetch(`${API_BASE}/ivanti/findings/${finding.id}/note`, {
                        method: 'PUT',
                        credentials: 'include',
                        headers: { 'Content-Type': 'application/json' },
                        body: JSON.stringify({ note: 'DECOM' }),
                    }).catch(() => {});
                    // Update local findings state
                    setFindings(prev => prev.map(f =>
                        f.id === finding.id ? { ...f, note: 'DECOM' } : f
                    ));
                    // Auto-hide the row
                    hideRow(finding.id);
                }
            }
        } catch (e) {
            console.error('Error adding to queue:', e);
        }
        setAddPopover(null);
        setQueueForm({ vendor: '', workflowType: 'FP' });
    }, [addPopover, queueForm, hideRow]);

    // Prune selection when filters change — keep only IDs still in filtered set
    useEffect(() => {
        setSelectedIds((prev) => {
            if (prev.size === 0) return prev;
            const visibleIds = new Set(filtered.map((f) => f.id));
            const next = new Set([...prev].filter((id) => visibleIds.has(id)));
            return next.size === prev.size ? prev : next;
        });
    }, [filtered]);

    // Escape key clears selection
    useEffect(() => {
        if (selectedIds.size === 0) return;
        const handler = (e) => {
            if (e.key === 'Escape' && selectedIds.size > 0 && !addPopover) {
                setSelectedIds(new Set());
                setBatchError(null);
            }
        };
        document.addEventListener('keydown', handler);
        return () => document.removeEventListener('keydown', handler);
    }, [selectedIds, addPopover]);

    const submitBatch = useCallback(async () => {
        if (selectedIds.size === 0) return;
        setBatchSubmitting(true);
        setBatchError(null);
        try {
            const findingsPayload = [...selectedIds].map((id) => {
                const f = findings.find((ff) => ff.id === id);
                return f ? {
                    finding_id:    f.id,
                    finding_title: f.title      || null,
                    cves:          f.cves        || [],
                    ip_address:    f.ipAddress   || null,
                    hostname:      f.overrides?.hostName || f.hostName || null,
                } : { finding_id: id };
            });
            const res = await fetch(`${API_BASE}/ivanti/todo-queue/batch`, {
                method: 'POST',
                credentials: 'include',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify({
                    findings:      findingsPayload,
                    workflow_type: batchWorkflowType,
                    vendor:        batchWorkflowType === 'CARD' || batchWorkflowType === 'GRANITE' || batchWorkflowType === 'DECOM' ? '' : batchVendor.trim(),
                }),
            });
            const data = await res.json();
            if (res.ok) {
                setQueueItems((prev) => [...prev, ...(data.items || [])].sort((a, b) =>
                    (a.vendor || '').localeCompare(b.vendor || '') || a.id - b.id
                ));

                // DECOM: auto-set note and auto-hide all selected findings
                if (batchWorkflowType === 'DECOM') {
                    const ids = [...selectedIds];
                    // Set notes to DECOM in parallel (fire-and-forget)
                    ids.forEach(id => {
                        fetch(`${API_BASE}/ivanti/findings/${id}/note`, {
                            method: 'PUT',
                            credentials: 'include',
                            headers: { 'Content-Type': 'application/json' },
                            body: JSON.stringify({ note: 'DECOM' }),
                        }).catch(() => {});
                    });
                    // Update local findings state
                    setFindings(prev => prev.map(f =>
                        ids.includes(f.id) ? { ...f, note: 'DECOM' } : f
                    ));
                    // Auto-hide all
                    setHiddenRowIds(prev => {
                        const next = new Set(prev);
                        ids.forEach(id => next.add(String(id)));
                        saveHiddenRows(next);
                        return next;
                    });
                }

                setSelectedIds(new Set());
                setBatchWorkflowType('FP');
                setBatchVendor('');
                setBatchError(null);
            } else {
                setBatchError(data.error || 'Failed to add findings to queue.');
            }
        } catch (e) {
            console.error('Error in batch add:', e);
            setBatchError('Network error — please try again.');
        } finally {
            setBatchSubmitting(false);
        }
    }, [selectedIds, findings, batchWorkflowType, batchVendor]);

    const updateQueueItem = useCallback(async (id, changes) => {
        try {
            const res = await fetch(`${API_BASE}/ivanti/todo-queue/${id}`, {
                method: 'PUT',
                credentials: 'include',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify(changes),
            });
            const data = await res.json();
            if (res.ok) {
                setQueueItems((prev) => prev.map((item) => item.id === id ? data : item));
            }
        } catch (e) {
            console.error('Error updating queue item:', e);
        }
    }, []);

    const deleteQueueItem = useCallback(async (id) => {
        try {
            const res = await fetch(`${API_BASE}/ivanti/todo-queue/${id}`, {
                method: 'DELETE',
                credentials: 'include',
            });
            if (res.ok) setQueueItems((prev) => prev.filter((item) => item.id !== id));
        } catch (e) {
            console.error('Error deleting queue item:', e);
        }
    }, []);

    const deleteQueueItems = useCallback(async (ids) => {
        try {
            await Promise.all(ids.map((id) =>
                fetch(`${API_BASE}/ivanti/todo-queue/${id}`, { method: 'DELETE', credentials: 'include' })
            ));
            const removed = new Set(ids);
            setQueueItems((prev) => prev.filter((item) => !removed.has(item.id)));
        } catch (e) {
            console.error('Error bulk-deleting queue items:', e);
        }
    }, []);

    const clearCompleted = useCallback(async () => {
        try {
            const res = await fetch(`${API_BASE}/ivanti/todo-queue/completed`, {
                method: 'DELETE',
                credentials: 'include',
            });
            if (res.ok) setQueueItems((prev) => prev.filter((item) => item.status !== 'complete'));
        } catch (e) {
            console.error('Error clearing completed queue items:', e);
        }
    }, []);

    const isQueued = useCallback((findingId) =>
        queueItems.some((item) => item.finding_id === findingId),
    [queueItems]);

    const pendingQueueCount = queueItems.filter((i) => i.status === 'pending').length;

    const [exportMenuOpen, setExportMenuOpen] = useState(false);
    const exportBtnRef = useRef(null);

    // Close export menu on outside click
    useEffect(() => {
        if (!exportMenuOpen) return;
        const handler = (e) => {
            if (exportBtnRef.current && !exportBtnRef.current.contains(e.target)) {
                setExportMenuOpen(false);
            }
        };
        document.addEventListener('mousedown', handler);
        return () => document.removeEventListener('mousedown', handler);
    }, [exportMenuOpen]);

    const buildExportRows = useCallback(() => {
        const cols = visibleCols.filter((c) => COLUMN_DEFS[c.key]);
        const headers = cols.map((c) => COLUMN_DEFS[c.key].label);
        const rows = sorted.map((finding) =>
            cols.map((c) => getExportVal(finding, c.key))
        );
        return [headers, ...rows];
    }, [sorted, visibleCols]);

    const exportCSV = useCallback(() => {
        setExportMenuOpen(false);
        const rows = buildExportRows();
        const csvContent = rows.map((row) =>
            row.map((cell) => {
                const s = String(cell ?? '');
                // Quote if it contains comma, double-quote, or newline
                if (s.includes(',') || s.includes('"') || s.includes('\n')) {
                    return `"${s.replace(/"/g, '""')}"`;
                }
                return s;
            }).join(',')
        ).join('\r\n');
        const blob = new Blob(['\uFEFF' + csvContent], { type: 'text/csv;charset=utf-8;' });
        const url = URL.createObjectURL(blob);
        const a = document.createElement('a');
        a.href = url;
        a.download = `findings-export-${new Date().toISOString().slice(0, 10)}.csv`;
        document.body.appendChild(a);
        a.click();
        document.body.removeChild(a);
        URL.revokeObjectURL(url);
    }, [buildExportRows]);

    const exportXLSX = useCallback(() => {
        setExportMenuOpen(false);
        const rows = buildExportRows();
        const ws = XLSX.utils.aoa_to_sheet(rows);
        // Auto-fit column widths
        const colWidths = rows[0].map((_, ci) =>
            Math.min(60, Math.max(10, ...rows.map((r) => String(r[ci] ?? '').length)))
        );
        ws['!cols'] = colWidths.map((w) => ({ wch: w }));
        const wb = XLSX.utils.book_new();
        XLSX.utils.book_append_sheet(wb, ws, 'Findings');
        XLSX.writeFile(wb, `findings-export-${new Date().toISOString().slice(0, 10)}.xlsx`);
    }, [buildExportRows]);

    const syncedDisplay = syncedAt
        ? `Synced ${new Date(syncedAt).toLocaleString()}`
        : 'Never synced';

    // -------------------------------------------------------------------------
    // Render
    // -------------------------------------------------------------------------
    return (
        <div style={{ display: 'flex', flexDirection: 'column', gap: '1.5rem' }}>

            {/* ----------------------------------------------------------------
                Panel 1 — Metrics placeholder
            ---------------------------------------------------------------- */}
            <div style={{
                background: 'linear-gradient(135deg, rgba(15,26,46,0.95) 0%, rgba(10,22,40,0.9) 100%)',
                border: '1px solid rgba(245,158,11,0.2)',
                borderLeft: '3px solid #F59E0B',
                borderRadius: '0.5rem',
                padding: '1.5rem',
                boxShadow: '0 4px 16px rgba(0,0,0,0.4)'
            }}>
                <div style={{ display: 'flex', alignItems: 'center', gap: '0.625rem', marginBottom: '1rem' }}>
                    <PieChart style={{ width: '20px', height: '20px', color: '#F59E0B' }} />
                    <h2 style={{ fontFamily: 'monospace', fontSize: '1rem', fontWeight: '600', color: '#F59E0B', textTransform: 'uppercase', letterSpacing: '0.1em', textShadow: '0 0 12px rgba(245,158,11,0.4)', margin: 0 }}>
                        Metric Graphs
                    </h2>
                    <div style={{ marginLeft: 'auto', display: 'flex', gap: '0.25rem' }} role="tablist">
                        {[{ key: 'ivanti', label: 'Ivanti Findings' }, { key: 'atlas', label: 'Atlas Coverage' }].map(tab => {
                            const isActive = metricsTab === tab.key;
                            return (
                                <button
                                    key={tab.key}
                                    role="tab"
                                    aria-selected={isActive}
                                    tabIndex={0}
                                    onClick={() => setMetricsTab(tab.key)}
                                    onKeyDown={(e) => { if (e.key === 'Enter') setMetricsTab(tab.key); }}
                                    style={{
                                        background: 'transparent',
                                        border: 'none',
                                        borderBottom: isActive ? '2px solid #F59E0B' : '2px solid transparent',
                                        color: isActive ? '#F59E0B' : '#64748B',
                                        fontFamily: 'monospace',
                                        fontSize: '0.7rem',
                                        textTransform: 'uppercase',
                                        letterSpacing: '0.08em',
                                        padding: '0.375rem 0.75rem',
                                        cursor: 'pointer',
                                        transition: 'background 0.15s, color 0.15s'
                                    }}
                                    onMouseEnter={(e) => { if (!isActive) e.currentTarget.style.background = 'rgba(245, 158, 11, 0.06)'; }}
                                    onMouseLeave={(e) => { e.currentTarget.style.background = 'transparent'; }}
                                >
                                    {tab.label}
                                </button>
                            );
                        })}
                    </div>
                </div>
                <div role="tabpanel">
                {metricsTab === 'ivanti' && (
                <div style={{ display: 'flex', gap: '3rem', flexWrap: 'wrap', alignItems: 'flex-start' }}>
                    {/* Open vs Closed donut — per-BU counts from Postgres */}
                    <div style={{ flex: '0 0 auto' }}>
                        <div style={{ fontFamily: 'monospace', fontSize: '0.68rem', fontWeight: '600', color: '#64748B', textTransform: 'uppercase', letterSpacing: '0.1em', marginBottom: '0.75rem' }}>
                            Open vs Closed
                        </div>
                        <StatusDonut
                            open={statusCounts.open}
                            closed={statusCounts.closed}
                            loading={countsLoading}
                        />
                    </div>

                    {/* Divider */}
                    <div style={{ width: '1px', background: 'rgba(255,255,255,0.06)', alignSelf: 'stretch', flexShrink: 0 }} />

                    {/* Action Coverage donut */}
                    <div style={{ flex: '0 0 auto' }}>
                        <div style={{ fontFamily: 'monospace', fontSize: '0.68rem', fontWeight: '600', color: '#64748B', textTransform: 'uppercase', letterSpacing: '0.1em', marginBottom: '0.75rem' }}>
                            Action Coverage
                            {actionFilter && <span style={{ marginLeft: '0.5rem', color: ACTION_DEFS.find(d => d.key === actionFilter)?.color, fontSize: '0.6rem' }}>● filtered</span>}
                        </div>
                        <ActionCoverageDonut
                            findings={visibleFindings}
                            activeSegment={actionFilter}
                            onSegmentClick={(key) => {
                                setExcFilter(null);
                                setActionFilter(key);
                            }}
                        />
                    </div>

                    {/* Divider */}
                    <div style={{ width: '1px', background: 'rgba(255,255,255,0.06)', alignSelf: 'stretch', flexShrink: 0 }} />

                    {/* FP Finding Status donut — # of findings per FP workflow state */}
                    <div style={{ flex: '0 0 auto' }}>
                        <div style={{ fontFamily: 'monospace', fontSize: '0.68rem', fontWeight: '600', color: '#64748B', textTransform: 'uppercase', letterSpacing: '0.1em', marginBottom: '0.75rem' }}>
                            FP Finding Status
                        </div>
                        <FPWorkflowDonut counts={fpCounts.findingCounts} total={fpCounts.findingTotal} centerLabel="FINDINGS" />
                    </div>

                    {/* Divider */}
                    <div style={{ width: '1px', background: 'rgba(255,255,255,0.06)', alignSelf: 'stretch', flexShrink: 0 }} />

                    {/* FP Workflow Status donut — # of unique FP# ticket IDs per state */}
                    <div style={{ flex: '0 0 auto' }}>
                        <div style={{ fontFamily: 'monospace', fontSize: '0.68rem', fontWeight: '600', color: '#64748B', textTransform: 'uppercase', letterSpacing: '0.1em', marginBottom: '0.75rem' }}>
                            FP Workflow Status
                        </div>
                        <FPWorkflowDonut counts={fpCounts.idCounts} total={fpCounts.idTotal} centerLabel="FP TICKETS" />
                    </div>
                </div>
                )}
                {metricsTab === 'atlas' && (
                    (atlasMetricsLoading || (!atlasMetrics && !atlasMetricsError)) ? (
                        <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', padding: '3rem 0', gap: '0.5rem' }}>
                            <Loader style={{ width: '24px', height: '24px', color: '#0EA5E9', animation: 'spin 1s linear infinite' }} />
                        </div>
                    ) : atlasMetricsError ? (
                        <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', padding: '2rem', gap: '0.375rem' }}>
                            <AlertCircle style={{ width: '14px', height: '14px', color: '#EF4444', flexShrink: 0 }} />
                            <span style={{ fontFamily: 'monospace', fontSize: '0.75rem', color: '#FCA5A5' }}>{atlasMetricsError}</span>
                        </div>
                    ) : (
                        <div style={{ display: 'flex', gap: '3rem', flexWrap: 'wrap', alignItems: 'flex-start' }}>
                            {/* Host Coverage donut */}
                            <div style={{ flex: '0 0 auto' }}>
                                <div style={{ fontFamily: 'monospace', fontSize: '0.68rem', fontWeight: '600', color: '#64748B', textTransform: 'uppercase', letterSpacing: '0.1em', marginBottom: '0.75rem' }}>
                                    Host Coverage
                                </div>
                                <AtlasCoverageDonut
                                    hostsWithPlans={atlasMetrics.hostsWithPlans}
                                    hostsWithoutPlans={atlasMetrics.hostsWithoutPlans}
                                    totalHosts={atlasMetrics.totalHosts}
                                />
                            </div>

                            {/* Divider */}
                            <div style={{ width: '1px', background: 'rgba(255,255,255,0.06)', alignSelf: 'stretch', flexShrink: 0 }} />

                            {/* Plan Types donut */}
                            <div style={{ flex: '0 0 auto' }}>
                                <div style={{ fontFamily: 'monospace', fontSize: '0.68rem', fontWeight: '600', color: '#64748B', textTransform: 'uppercase', letterSpacing: '0.1em', marginBottom: '0.75rem' }}>
                                    Plan Types
                                </div>
                                <AtlasPlanTypeDonut
                                    plansByType={atlasMetrics.plansByType}
                                    totalPlans={atlasMetrics.totalPlans}
                                />
                            </div>

                            {/* Divider */}
                            <div style={{ width: '1px', background: 'rgba(255,255,255,0.06)', alignSelf: 'stretch', flexShrink: 0 }} />

                            {/* Plan Status donut */}
                            <div style={{ flex: '0 0 auto' }}>
                                <div style={{ fontFamily: 'monospace', fontSize: '0.68rem', fontWeight: '600', color: '#64748B', textTransform: 'uppercase', letterSpacing: '0.1em', marginBottom: '0.75rem' }}>
                                    Plan Status
                                </div>
                                <AtlasPlanStatusDonut
                                    plansByStatus={atlasMetrics.plansByStatus}
                                    totalPlans={atlasMetrics.totalPlans}
                                />
                            </div>
                        </div>
                    )
                )}
                </div>
            </div>

            {/* ----------------------------------------------------------------
                Panel 1.5 — Open vs Closed trend over time
            ---------------------------------------------------------------- */}
            {metricsTab === 'ivanti' && <AnomalyBanner />}
            {metricsTab === 'ivanti' && <IvantiCountsChart teamsParam={getActiveTeamsParam()} />}

            {/* ----------------------------------------------------------------
                Panel 2 — Findings table
            ---------------------------------------------------------------- */}
            <div style={{
                background: 'linear-gradient(135deg, rgba(15,26,46,0.95) 0%, rgba(10,22,40,0.9) 100%)',
                border: '1px solid rgba(14,165,233,0.2)',
                borderLeft: '3px solid #0EA5E9',
                borderRadius: '0.5rem',
                padding: '1.5rem',
                boxShadow: '0 4px 16px rgba(0,0,0,0.4)'
            }}>
                {/* Panel header */}
                <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', marginBottom: '0.5rem' }}>
                    <div>
                        <h2 style={{ fontFamily: 'monospace', fontSize: '1rem', fontWeight: '600', color: '#0EA5E9', textTransform: 'uppercase', letterSpacing: '0.1em', textShadow: '0 0 12px rgba(14,165,233,0.4)', margin: '0 0 4px 0' }}>
                            Host Findings
                        </h2>
                        <div style={{ fontFamily: 'monospace', fontSize: '0.7rem', color: '#475569' }}>
                            {syncedDisplay}
                            {syncStatus === 'success' && total !== null && (
                                <span style={{ marginLeft: '0.75rem', color: '#64748B' }}>
                                    {activeFilterCount > 0 ? `${filtered.length} of ${total}` : total} findings
                                    {activeFilterCount > 0 && (
                                        <span style={{ marginLeft: '0.5rem', color: '#F59E0B' }}>
                                            ({activeFilterCount} filter{activeFilterCount > 1 ? 's' : ''} active)
                                        </span>
                                    )}
                                </span>
                            )}
                        </div>
                    </div>

                    {/* Action buttons */}
                    <div style={{ display: 'flex', gap: '0.5rem', alignItems: 'center' }}>
                        {/* EXC filter badge (from home page navigation) */}
                        {excFilter && (
                            <button
                                onClick={() => setExcFilter(null)}
                                style={{
                                    display: 'flex', alignItems: 'center', gap: '0.375rem',
                                    padding: '0.375rem 0.75rem',
                                    background: 'rgba(245,158,11,0.08)',
                                    border: '1px solid rgba(245,158,11,0.3)',
                                    borderRadius: '0.375rem',
                                    color: '#F59E0B', cursor: 'pointer',
                                    fontFamily: 'monospace', fontSize: '0.75rem', fontWeight: '600',
                                    letterSpacing: '0.05em'
                                }}
                            >
                                <Filter style={{ width: '11px', height: '11px' }} />
                                {excFilter} ×
                            </button>
                        )}
                        {/* Action coverage filter badge (from chart click) */}
                        {actionFilter && (
                            <button
                                onClick={() => setActionFilter(null)}
                                style={{
                                    display: 'flex', alignItems: 'center', gap: '0.375rem',
                                    padding: '0.375rem 0.75rem',
                                    background: actionFilter === 'fp' ? 'rgba(14,165,233,0.08)' : actionFilter === 'archer' ? 'rgba(245,158,11,0.08)' : 'rgba(239,68,68,0.08)',
                                    border: `1px solid ${actionFilter === 'fp' ? 'rgba(14,165,233,0.3)' : actionFilter === 'archer' ? 'rgba(245,158,11,0.3)' : 'rgba(239,68,68,0.3)'}`,
                                    borderRadius: '0.375rem',
                                    color: actionFilter === 'fp' ? '#0EA5E9' : actionFilter === 'archer' ? '#F59E0B' : '#EF4444',
                                    cursor: 'pointer',
                                    fontFamily: 'monospace', fontSize: '0.75rem', fontWeight: '600',
                                    letterSpacing: '0.05em'
                                }}
                            >
                                <Filter style={{ width: '11px', height: '11px' }} />
                                {ACTION_DEFS.find(d => d.key === actionFilter)?.label} ×
                            </button>
                        )}
                        {Object.keys(columnFilters).length > 0 && (
                            <button
                                onClick={() => setColumnFilters({})}
                                style={{
                                    display: 'flex', alignItems: 'center', gap: '0.375rem',
                                    padding: '0.375rem 0.75rem',
                                    background: 'rgba(245,158,11,0.08)',
                                    border: '1px solid rgba(245,158,11,0.3)',
                                    borderRadius: '0.375rem',
                                    color: '#F59E0B', cursor: 'pointer',
                                    fontFamily: 'monospace', fontSize: '0.75rem', fontWeight: '600',
                                    textTransform: 'uppercase', letterSpacing: '0.05em'
                                }}
                            >
                                <Filter style={{ width: '11px', height: '11px' }} />
                                Clear Filters
                            </button>
                        )}
                        {/* Export dropdown */}
                        <div ref={exportBtnRef} style={{ position: 'relative' }}>
                            <button
                                onClick={() => setExportMenuOpen((o) => !o)}
                                disabled={sorted.length === 0}
                                style={{
                                    display: 'flex', alignItems: 'center', gap: '0.375rem',
                                    padding: '0.375rem 0.75rem',
                                    background: 'rgba(16,185,129,0.08)',
                                    border: '1px solid rgba(16,185,129,0.3)',
                                    borderRadius: '0.375rem',
                                    color: '#10B981', cursor: sorted.length === 0 ? 'not-allowed' : 'pointer',
                                    fontFamily: 'monospace', fontSize: '0.75rem', fontWeight: '600',
                                    textTransform: 'uppercase', letterSpacing: '0.05em',
                                    opacity: sorted.length === 0 ? 0.4 : 1,
                                }}
                            >
                                <Download style={{ width: '11px', height: '11px' }} />
                                Export
                                <ChevronDown style={{ width: '10px', height: '10px', marginLeft: '1px' }} />
                            </button>
                            {exportMenuOpen && (
                                <div style={{
                                    position: 'absolute', top: 'calc(100% + 4px)', right: 0, zIndex: 200,
                                    background: 'rgb(12,22,40)', border: '1px solid rgba(16,185,129,0.3)',
                                    borderRadius: '0.375rem', overflow: 'hidden',
                                    boxShadow: '0 4px 16px rgba(0,0,0,0.5)',
                                    minWidth: '120px',
                                }}>
                                    {[
                                        { label: 'CSV (.csv)', action: exportCSV },
                                        { label: 'Excel (.xlsx)', action: exportXLSX },
                                    ].map(({ label, action }) => (
                                        <button
                                            key={label}
                                            onClick={action}
                                            style={{
                                                display: 'block', width: '100%', textAlign: 'left',
                                                padding: '0.5rem 0.875rem',
                                                background: 'none', border: 'none',
                                                fontFamily: 'monospace', fontSize: '0.73rem', fontWeight: '600',
                                                color: '#10B981', cursor: 'pointer',
                                                textTransform: 'uppercase', letterSpacing: '0.04em',
                                                transition: 'background 0.1s',
                                            }}
                                            onMouseEnter={(e) => e.currentTarget.style.background = 'rgba(16,185,129,0.1)'}
                                            onMouseLeave={(e) => e.currentTarget.style.background = 'none'}
                                        >
                                            {label}
                                        </button>
                                    ))}
                                </div>
                            )}
                        </div>
                        {/* Queue button */}
                        <button
                            onClick={() => setQueueOpen((o) => !o)}
                            style={{
                                position: 'relative',
                                display: 'flex', alignItems: 'center', gap: '0.375rem',
                                padding: '0.375rem 0.75rem',
                                background: queueOpen ? 'rgba(14,165,233,0.15)' : 'rgba(14,165,233,0.08)',
                                border: `1px solid rgba(14,165,233,${queueOpen ? '0.5' : '0.25'})`,
                                borderRadius: '0.375rem',
                                color: '#0EA5E9', cursor: 'pointer',
                                fontFamily: 'monospace', fontSize: '0.75rem', fontWeight: '600',
                                textTransform: 'uppercase', letterSpacing: '0.05em',
                            }}
                        >
                            <ListTodo style={{ width: '13px', height: '13px' }} />
                            Queue
                            {pendingQueueCount > 0 && (
                                <span style={{
                                    display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
                                    minWidth: '16px', height: '16px', padding: '0 4px',
                                    background: '#0EA5E9', borderRadius: '999px',
                                    fontFamily: 'monospace', fontSize: '0.58rem', fontWeight: '700', color: '#0A1628',
                                    marginLeft: '1px',
                                }}>
                                    {pendingQueueCount}
                                </span>
                            )}
                        </button>
                        <ColumnManager columnOrder={columnOrder} onChange={updateColumns} />
                        <RowVisibilityManager hiddenRowIds={hiddenRowIds} findings={findings} onRestore={restoreRow} onRestoreAll={restoreAllRows} />
                        <button
                            onClick={async () => {
                                setAtlasSyncing(true);
                                setAtlasError(null);
                                try {
                                    const res = await fetch(`${API_BASE}/atlas/sync`, { method: 'POST', credentials: 'include' });
                                    if (!res.ok) {
                                        const data = await res.json().catch(() => ({}));
                                        throw new Error(data.error || 'Atlas sync failed');
                                    }
                                    await fetchAtlasStatus();
                                    await fetchAtlasMetrics();
                                } catch (err) {
                                    setAtlasError(err.message);
                                } finally {
                                    setAtlasSyncing(false);
                                }
                            }}
                            disabled={atlasSyncing || !canWrite()}
                            title={!canWrite() ? 'Insufficient permissions' : 'Sync Atlas action plan status'}
                            style={{
                                display: 'inline-flex', alignItems: 'center', gap: '0.4rem',
                                padding: '0.4rem 0.75rem',
                                background: atlasSyncing ? 'rgba(14,165,233,0.08)' : 'rgba(14,165,233,0.06)',
                                border: '1px solid rgba(14,165,233,0.2)',
                                borderRadius: '0.375rem',
                                color: atlasSyncing ? '#475569' : '#0EA5E9',
                                fontSize: '0.72rem',
                                fontFamily: "'JetBrains Mono', monospace",
                                fontWeight: 600,
                                cursor: atlasSyncing || !canWrite() ? 'not-allowed' : 'pointer',
                                opacity: !canWrite() ? 0.5 : 1,
                                textTransform: 'uppercase',
                                letterSpacing: '0.05em',
                            }}
                        >
                            {atlasSyncing
                                ? <Loader style={{ width: 13, height: 13, animation: 'spin 1s linear infinite' }} />
                                : <AtlasIcon style={{ width: 13, height: 13 }} />}
                            Atlas
                        </button>
                        <button
                            onClick={syncFindings}
                            disabled={syncing || loading}
                            style={{
                                display: 'flex', alignItems: 'center', gap: '0.375rem',
                                padding: '0.375rem 0.75rem',
                                background: 'rgba(14,165,233,0.1)',
                                border: '1px solid rgba(14,165,233,0.35)',
                                borderRadius: '0.375rem',
                                color: '#0EA5E9', cursor: 'pointer',
                                fontFamily: 'monospace', fontSize: '0.75rem', fontWeight: '600',
                                textTransform: 'uppercase', letterSpacing: '0.05em',
                                opacity: (syncing || loading) ? 0.6 : 1
                            }}
                        >
                            <RefreshCw style={{ width: '13px', height: '13px', animation: syncing ? 'spin 1s linear infinite' : 'none' }} />
                            {syncing ? 'Syncing…' : 'Sync'}
                        </button>
                    </div>
                </div>

                {/* Error banner */}
                {syncStatus === 'error' && syncError && (
                    <div style={{ display: 'flex', alignItems: 'flex-start', gap: '0.5rem', padding: '0.625rem 0.875rem', background: 'rgba(239,68,68,0.08)', border: '1px solid rgba(239,68,68,0.25)', borderRadius: '0.375rem', marginBottom: '1rem' }}>
                        <AlertCircle style={{ width: '15px', height: '15px', color: '#EF4444', flexShrink: 0, marginTop: '1px' }} />
                        <span style={{ fontSize: '0.75rem', color: '#FCA5A5', fontFamily: 'monospace' }}>{syncError}</span>
                    </div>
                )}
                {atlasError && (
                    <div style={{ display: 'flex', alignItems: 'flex-start', gap: '0.5rem', padding: '0.625rem 0.875rem', background: 'rgba(239,68,68,0.08)', border: '1px solid rgba(239,68,68,0.25)', borderRadius: '0.375rem', marginBottom: '1rem' }}>
                        <AlertCircle style={{ width: '15px', height: '15px', color: '#EF4444', flexShrink: 0, marginTop: '1px' }} />
                        <span style={{ fontSize: '0.75rem', color: '#FCA5A5', fontFamily: 'monospace' }}>Atlas: {atlasError}</span>
                    </div>
                )}

                {/* Content */}
                {loading ? (
                    <div style={{ textAlign: 'center', padding: '3rem 0' }}>
                        <Loader style={{ width: '28px', height: '28px', color: '#0EA5E9', animation: 'spin 1s linear infinite', margin: '0 auto 0.75rem' }} />
                        <p style={{ fontFamily: 'monospace', fontSize: '0.75rem', color: '#475569' }}>Loading findings…</p>
                    </div>
                ) : syncStatus === 'never' ? (
                    <div style={{ textAlign: 'center', padding: '3rem 0' }}>
                        <p style={{ fontFamily: 'monospace', fontSize: '0.75rem', color: '#475569' }}>Click Sync to load findings data</p>
                    </div>
                ) : (
                    <div style={{ overflowX: 'auto', overflowY: 'auto', maxHeight: 'calc(100vh - 420px)', minHeight: '200px', marginTop: '0.75rem' }}>
                        {selectedIds.size > 0 && canWrite() && (
                            <SelectionToolbar
                                count={selectedIds.size}
                                workflowType={batchWorkflowType}
                                vendor={batchVendor}
                                submitting={batchSubmitting}
                                error={batchError}
                                onWorkflowChange={setBatchWorkflowType}
                                onVendorChange={setBatchVendor}
                                onSubmit={submitBatch}
                                onClear={() => { setSelectedIds(new Set()); setBatchError(null); }}
                            />
                        )}
                        {selectedRowIds.size > 0 && (
                            <BulkHideToolbar
                                count={selectedRowIds.size}
                                onHide={hideSelectedRows}
                                onClear={() => setSelectedRowIds(new Set())}
                                onAtlasBulk={() => setBulkAtlasOpen(true)}
                                canWrite={canWrite()}
                            />
                        )}
                        <table style={{ width: '100%', borderCollapse: 'collapse', fontSize: '0.75rem', fontFamily: 'sans-serif' }}>
                            <thead>
                                <tr style={{ borderBottom: '1px solid rgba(14,165,233,0.2)' }}>
                                    {/* Fixed selection checkbox column — row visibility feature */}
                                    <th
                                        style={{
                                            width: '36px', minWidth: '36px', padding: '0.5rem 0.5rem',
                                            background: 'rgb(10, 20, 36)',
                                            position: 'sticky', top: 0, zIndex: 10,
                                            boxShadow: '0 1px 0 rgba(14,165,233,0.2)',
                                            textAlign: 'center',
                                            cursor: 'pointer',
                                        }}
                                        onClick={toggleSelectAll}
                                    >
                                        {(() => {
                                            const allVisibleIds = sorted.map(f => String(f.id));
                                            const selectedCount = allVisibleIds.filter(id => selectedRowIds.has(id)).length;
                                            const allSelected = allVisibleIds.length > 0 && selectedCount === allVisibleIds.length;
                                            const someSelected = selectedCount > 0 && !allSelected;
                                            if (allSelected) return <CheckSquare style={{ width: '13px', height: '13px', color: '#4fc3f7' }} />;
                                            if (someSelected) return <MinusSquare style={{ width: '13px', height: '13px', color: '#4fc3f7' }} />;
                                            return <Square style={{ width: '13px', height: '13px', color: '#8892a2' }} />;
                                        })()}
                                    </th>
                                    {/* Fixed hide button column — row visibility feature */}
                                    <th
                                        style={{
                                            width: '36px', minWidth: '36px', padding: '0.5rem 0.5rem',
                                            background: 'rgb(10, 20, 36)',
                                            position: 'sticky', top: 0, zIndex: 10,
                                            boxShadow: '0 1px 0 rgba(14,165,233,0.2)',
                                        }}
                                    />
                                    {/* Fixed checkbox column — not part of column manager */}
                                    <th
                                        style={{
                                            width: '36px', minWidth: '36px', padding: '0.5rem 0.5rem',
                                            background: 'rgb(10, 20, 36)',
                                            position: 'sticky', top: 0, zIndex: 10,
                                            boxShadow: '0 1px 0 rgba(14,165,233,0.2)',
                                            textAlign: 'center',
                                        }}
                                    >
                                        {canWrite() && (
                                            <input
                                                type="checkbox"
                                                checked={sorted.length > 0 && sorted.filter((f) => !isQueued(f.id)).length > 0 && sorted.filter((f) => !isQueued(f.id)).every((f) => selectedIds.has(f.id))}
                                                onChange={() => {
                                                    const nonQueued = sorted.filter((f) => !isQueued(f.id));
                                                    const allSelected = nonQueued.length > 0 && nonQueued.every((f) => selectedIds.has(f.id));
                                                    if (allSelected) {
                                                        setSelectedIds(new Set());
                                                    } else {
                                                        setSelectedIds(new Set(nonQueued.map((f) => f.id)));
                                                    }
                                                }}
                                                style={{
                                                    accentColor: '#0EA5E9',
                                                    width: '13px', height: '13px',
                                                    cursor: 'pointer',
                                                }}
                                                title="Select all visible findings"
                                            />
                                        )}
                                    </th>
                                    {visibleCols.map((col) => {
                                        const def        = COLUMN_DEFS[col.key];
                                        const active     = sort.field === col.key;
                                        const isFiltered = !!columnFilters[col.key];
                                        return (
                                            <th
                                                key={col.key}
                                                onClick={def?.sortable ? () => toggleSort(col.key) : undefined}
                                                style={{
                                                    padding: '0.5rem 0.75rem', textAlign: 'left',
                                                    fontFamily: 'monospace', fontSize: '0.68rem', fontWeight: '600',
                                                    color: active ? '#0EA5E9' : '#64748B',
                                                    textTransform: 'uppercase', letterSpacing: '0.08em',
                                                    whiteSpace: 'nowrap',
                                                    cursor: def?.sortable ? 'pointer' : 'default',
                                                    userSelect: 'none',
                                                    background: 'rgb(10, 20, 36)',
                                                    position: 'sticky', top: 0, zIndex: 10,
                                                    boxShadow: '0 1px 0 rgba(14,165,233,0.2)',
                                                }}
                                            >
                                                <span style={{ display: 'inline-flex', alignItems: 'center' }}>
                                                    {def?.label || col.key}
                                                    {def?.sortable && <SortIcon colKey={col.key} sort={sort} />}
                                                    {def?.filterable && (
                                                        <button
                                                            ref={(el) => { filterBtnRefs.current[col.key] = el; }}
                                                            onClick={(e) => {
                                                                e.stopPropagation();
                                                                setOpenFilter(openFilter === col.key ? null : col.key);
                                                            }}
                                                            title={`Filter ${def.label}`}
                                                            style={{
                                                                background: 'none', border: 'none',
                                                                cursor: 'pointer', padding: '1px 1px 1px 3px',
                                                                color: isFiltered ? '#F59E0B' : '#334155',
                                                                lineHeight: 1, flexShrink: 0,
                                                                transition: 'color 0.15s',
                                                            }}
                                                        >
                                                            <Filter style={{ width: '10px', height: '10px' }} />
                                                        </button>
                                                    )}
                                                </span>
                                            </th>
                                        );
                                    })}
                                </tr>
                            </thead>
                            <tbody>
                                {sorted.map((finding, idx) => {
                                    const isSelected = selectedIds.has(finding.id);
                                    const rowBg  = isSelected ? 'rgba(14,165,233,0.12)' : (idx % 2 === 0 ? 'rgba(15,26,46,0.4)' : 'rgba(10,18,32,0.4)');
                                    const queued = isQueued(finding.id);
                                    return (
                                        <tr
                                            key={finding.id}
                                            style={{ borderBottom: '1px solid rgba(255,255,255,0.04)', background: rowBg }}
                                            onMouseEnter={(e) => { if (!isSelected) e.currentTarget.style.background = 'rgba(14,165,233,0.05)'; }}
                                            onMouseLeave={(e) => { e.currentTarget.style.background = rowBg; }}
                                        >
                                            {/* Selection checkbox cell — row visibility feature */}
                                            <td
                                                style={{ padding: '0.45rem 0.5rem', textAlign: 'center', width: '36px', cursor: 'pointer' }}
                                                onClick={() => toggleRowSelection(finding.id)}
                                            >
                                                {selectedRowIds.has(String(finding.id))
                                                    ? <CheckSquare style={{ width: '13px', height: '13px', color: '#4fc3f7' }} />
                                                    : <Square style={{ width: '13px', height: '13px', color: '#8892a2' }} />
                                                }
                                            </td>
                                            {/* Hide button cell — row visibility feature */}
                                            <td
                                                style={{ padding: '0.45rem 0.5rem', textAlign: 'center', width: '36px' }}
                                            >
                                                <button
                                                    onClick={() => hideRow(finding.id)}
                                                    title="Hide this row"
                                                    style={{
                                                        background: 'none', border: 'none', padding: 0,
                                                        cursor: 'pointer', display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
                                                    }}
                                                    onMouseEnter={(e) => { e.currentTarget.querySelector('svg').style.color = '#4fc3f7'; }}
                                                    onMouseLeave={(e) => { e.currentTarget.querySelector('svg').style.color = '#8892a2'; }}
                                                >
                                                    <EyeOff style={{ width: '13px', height: '13px', color: '#8892a2' }} />
                                                </button>
                                            </td>
                                            {/* Checkbox cell */}
                                            <td
                                                style={{ padding: '0.45rem 0.5rem', textAlign: 'center', width: '36px' }}
                                                onClick={(e) => {
                                                    if (queued) return;
                                                    // Shift-click range select
                                                    if (e.shiftKey && lastClickedId) {
                                                        const lastIdx = sorted.findIndex((f) => f.id === lastClickedId);
                                                        const currIdx = sorted.findIndex((f) => f.id === finding.id);
                                                        if (lastIdx !== -1 && currIdx !== -1) {
                                                            const start = Math.min(lastIdx, currIdx);
                                                            const end = Math.max(lastIdx, currIdx);
                                                            setSelectedIds((prev) => {
                                                                const next = new Set(prev);
                                                                for (let i = start; i <= end; i++) {
                                                                    if (!isQueued(sorted[i].id)) next.add(sorted[i].id);
                                                                }
                                                                return next;
                                                            });
                                                        }
                                                    } else {
                                                        // Regular click — toggle selection
                                                        setSelectedIds((prev) => {
                                                            const next = new Set(prev);
                                                            if (next.has(finding.id)) next.delete(finding.id); else next.add(finding.id);
                                                            return next;
                                                        });
                                                    }
                                                    setLastClickedId(finding.id);
                                                }}
                                            >
                                                <input
                                                    type="checkbox"
                                                    readOnly
                                                    checked={queued || isSelected}
                                                    style={{
                                                        accentColor: queued ? '#10B981' : '#0EA5E9',
                                                        width: '13px', height: '13px',
                                                        cursor: queued ? 'default' : 'pointer',
                                                        pointerEvents: 'none',
                                                    }}
                                                />
                                            </td>
                                            {visibleCols.map((col) => (
                                                <TableCell key={col.key} colKey={col.key} finding={finding} canWrite={canWrite()} onCveMouseEnter={handleCveMouseEnter} onCveMouseLeave={handleCveMouseLeave} fpSubmissions={fpSubmissions} onEditSubmission={handleEditSubmission} atlasStatusMap={atlasStatusMap} onAtlasBadgeClick={(hostId) => { setAtlasSelectedHostId(hostId); setAtlasSelectedHostName(finding.hostName || finding.ipAddress || ''); setAtlasSelectedFindingId(finding.id || null); setAtlasPanelOpen(true); }} />
                                            ))}
                                        </tr>
                                    );
                                })}
                                {sorted.length === 0 && (
                                    <tr>
                                        <td colSpan={visibleCols.length + 3} style={{ textAlign: 'center', padding: '2rem', color: '#475569', fontFamily: 'monospace', fontSize: '0.75rem' }}>
                                            {activeFilterCount > 0 ? 'No findings match the current filters' : 'No findings found'}
                                        </td>
                                    </tr>
                                )}
                            </tbody>
                        </table>
                    </div>
                )}
            </div>

            {/* Filter dropdown — rendered via portal at document.body */}
            {openFilter && COLUMN_DEFS[openFilter]?.filterable && (
                <FilterDropdown
                    anchorEl={filterBtnRefs.current[openFilter]}
                    colKey={openFilter}
                    findings={visibleFindings}
                    activeFilter={columnFilters[openFilter] || null}
                    onFilterChange={(vals) => setColFilter(openFilter, vals)}
                    onClose={() => setOpenFilter(null)}
                />
            )}

            {/* Add-to-queue popover — portal */}
            {addPopover && (
                <AddToQueuePopover
                    finding={addPopover.finding}
                    anchorRect={addPopover.anchorRect}
                    queueForm={queueForm}
                    setQueueForm={setQueueForm}
                    onAdd={addToQueue}
                    onCancel={() => {
                        setAddPopover(null);
                        setQueueForm({ vendor: '', workflowType: 'FP' });
                    }}
                />
            )}

            {/* Queue panel — fixed slide-out */}
            <QueuePanel
                open={queueOpen}
                items={queueItems}
                onClose={() => setQueueOpen(false)}
                onUpdate={updateQueueItem}
                onDelete={deleteQueueItem}
                onDeleteMany={deleteQueueItems}
                onClearCompleted={clearCompleted}
                onCreateFpWorkflow={handleCreateFpWorkflow}
                onRedirectComplete={(newItem) => {
                    setQueueItems((prev) => [...prev, newItem].sort((a, b) =>
                        (a.vendor || '').localeCompare(b.vendor || '') || a.id - b.id
                    ));
                }}
                canWrite={canWrite}
                fpSubmissions={fpSubmissionsFiltered}
                onEditSubmission={handleEditSubmission}
                onDismissSubmission={handleDismissSubmission}
                cardConfigured={cardConfigured}
                cardTeams={cardTeams}
                onQueueRefresh={fetchQueue}
            />
            <FpWorkflowModal
                open={fpModalOpen}
                onClose={() => setFpModalOpen(false)}
                selectedItems={fpModalItems}
                onSuccess={handleFpWorkflowSuccess}
            />
            <FpEditModal
                open={!!editSubmission}
                onClose={() => setEditSubmission(null)}
                submission={editSubmission}
                queueItems={queueItems}
                onSuccess={handleEditSuccess}
            />
            <CveTooltip
                cveId={tooltipCveId}
                anchorRect={tooltipAnchorRect}
                cache={tooltipCacheRef}
            />
            {atlasPanelOpen && atlasSelectedHostId && (
                <AtlasSlideOutPanel
                    hostId={atlasSelectedHostId}
                    hostName={atlasSelectedHostName}
                    findingId={atlasSelectedFindingId}
                    onClose={() => {
                        setAtlasPanelOpen(false);
                        setAtlasSelectedHostId(null);
                        setAtlasSelectedHostName(null);
                        setAtlasSelectedFindingId(null);
                    }}
                    canWrite={canWrite()}
                    onPlanChange={fetchAtlasStatus}
                />
            )}
            {bulkAtlasOpen && (
                <BulkAtlasModal
                    selectedFindings={sorted.filter(f => selectedRowIds.has(String(f.id)))}
                    onClose={() => setBulkAtlasOpen(false)}
                    onSuccess={() => { fetchAtlasStatus(); }}
                />
            )}
        </div>
    );
}
