// CompPrimitives.jsx — primitives for the Compliance page kit.
// Lifted directly from frontend/src/components/pages/CompliancePage.js.
// Identity color is teal (#14B8A6); status colors map green/amber/red onto
// "Meets/Exceeds Target", "Within 15% of Target", and "Below 15% of Target".

const { useState: useCompState, useRef: useCompRef } = React;

/* ── Tokens ──────────────────────────────────────────────────────
   Two layers:
   • Status — drives every percentage display + the worst-status
     ribbon on metric cards. Always one of three.
   • Category — owns the colored MetricBadge that flags which
     program a failing metric belongs to. */
const C_COLORS = {
  teal:    '#14B8A6',
  tealMid: '#5EEAD4',
  green:   '#10B981',
  amber:   '#F59E0B',
  red:     '#EF4444',
  sky:     '#0EA5E9',
  purple:  '#8B5CF6',
  orange:  '#F97316',
  slate:   '#64748B',
};

const STATUS_COLOR = {
  'Meets/Exceeds Target': C_COLORS.green,
  'Within 15% of Target': C_COLORS.amber,
  'Below 15% of Target':  C_COLORS.red,
};

const CATEGORY_COLORS = {
  'Vulnerability Management': C_COLORS.red,
  'Access & MFA':             C_COLORS.amber,
  'Logging & Monitoring':     C_COLORS.purple,
  'End-of-Life OS':           C_COLORS.orange,
  'Decommissioned Assets':    C_COLORS.slate,
  'Asset Data Quality':       C_COLORS.slate,
  'Application Security':     C_COLORS.sky,
  'Disaster Recovery':        C_COLORS.teal,
  'Endpoint Protection':      C_COLORS.orange,
};

const statusColor = s => STATUS_COLOR[s] || C_COLORS.red;
const pctDisplay = p => `${Math.round(p * 100)}%`;
const cAlpha = (hex, a) => {
  const h = hex.replace('#', '');
  return `rgba(${parseInt(h.slice(0,2),16)},${parseInt(h.slice(2,4),16)},${parseInt(h.slice(4,6),16)},${a})`;
};

/* ── PageHeader ──────────────────────────────────────────────────
   AEO Compliance — title in teal w/ glow, last-report meta beneath,
   refresh + upload-report on the right. Mirrors the KB / Reporting
   header pattern but with teal instead of green. */
function CompPageHeader({ title = 'AEO Compliance', lastReport, networkScore, verticalScore, onRefresh, onUpload, onRollback, isAdmin }) {
  return (
    <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', marginBottom: 24, gap: 16 }}>
      <div>
        <h2 style={{
          margin: '0 0 6px 0',
          fontFamily: 'var(--font-mono)', fontSize: 24, fontWeight: 700,
          color: C_COLORS.teal, textTransform: 'uppercase', letterSpacing: '0.1em',
          textShadow: `0 0 16px ${cAlpha(C_COLORS.teal, 0.4)}`,
        }}>{title}</h2>
        <div style={{ display: 'flex', alignItems: 'center', gap: 12, flexWrap: 'wrap', fontFamily: 'var(--font-mono)', fontSize: 11 }}>
          {lastReport ? (
            <>
              <span style={{ color: 'var(--fg-disabled)' }}>
                Last report: <span style={{ color: 'var(--fg-2)' }}>{lastReport}</span>
              </span>
              {isAdmin && (
                <button onClick={onRollback} style={{
                  background: 'transparent', border: '1px solid rgba(239,68,68,0.25)',
                  borderRadius: 4, padding: '2px 6px', cursor: 'pointer',
                  color: 'var(--fg-2)', display: 'inline-flex', alignItems: 'center', gap: 4,
                  fontSize: 10, fontFamily: 'var(--font-mono)',
                }}>
                  <CompIcon name="rotate" size={10} color="currentColor" /> Rollback
                </button>
              )}
            </>
          ) : (
            <span style={{ color: 'var(--fg-disabled)' }}>No reports uploaded</span>
          )}
          {networkScore != null && (
            <span style={{ color: 'var(--fg-2)' }}>Network: <span style={{ color: C_COLORS.teal }}>{networkScore}</span></span>
          )}
          {verticalScore != null && (
            <span style={{ color: 'var(--fg-2)' }}>Vertical: <span style={{ color: C_COLORS.teal }}>{verticalScore}</span></span>
          )}
        </div>
      </div>
      <div style={{ display: 'flex', gap: 8, flexShrink: 0 }}>
        <CompIconButton icon="refresh" onClick={onRefresh} />
        <CompButton variant="primary" icon="upload" onClick={onUpload}>Upload Report</CompButton>
      </div>
    </div>
  );
}

/* ── Buttons ───────────────────────────────────────────────────── */
function CompButton({ variant = 'neutral', icon, size = 'md', children, ...rest }) {
  const [hover, setHover] = useCompState(false);
  const v = {
    primary: { bg: hover ? cAlpha(C_COLORS.teal, 0.28) : cAlpha(C_COLORS.teal, 0.18), bd: C_COLORS.teal, fg: C_COLORS.teal },
    neutral: { bg: hover ? cAlpha(C_COLORS.teal, 0.10) : 'transparent', bd: cAlpha(C_COLORS.teal, 0.30), fg: C_COLORS.teal },
    danger:  { bg: hover ? 'rgba(239,68,68,0.18)' : 'rgba(239,68,68,0.10)', bd: C_COLORS.red, fg: C_COLORS.red },
    ghost:   { bg: hover ? 'rgba(255,255,255,0.04)' : 'transparent', bd: 'rgba(100,116,139,0.40)', fg: 'var(--fg-2)' },
  }[variant];
  const padX = size === 'sm' ? 10 : 16;
  const padY = size === 'sm' ? 4 : 8;
  const fs = size === 'sm' ? 11 : 12;
  return (
    <button
      onMouseEnter={() => setHover(true)} onMouseLeave={() => setHover(false)}
      style={{
        display: 'inline-flex', alignItems: 'center', gap: 6,
        padding: `${padY}px ${padX}px`, borderRadius: 6,
        background: v.bg, border: `1px solid ${v.bd}`, color: v.fg,
        fontFamily: 'var(--font-mono)', fontSize: fs, fontWeight: 600,
        textTransform: 'uppercase', letterSpacing: '0.05em',
        cursor: 'pointer', transition: 'all 160ms ease', whiteSpace: 'nowrap',
      }}
      {...rest}
    >
      {icon && <CompIcon name={icon} size={fs + 2} color={v.fg} />}
      {children}
    </button>
  );
}

function CompIconButton({ icon, onClick, color = C_COLORS.teal }) {
  const [hover, setHover] = useCompState(false);
  return (
    <button onClick={onClick}
      onMouseEnter={() => setHover(true)} onMouseLeave={() => setHover(false)}
      style={{
        background: hover ? cAlpha(color, 0.10) : 'transparent',
        border: `1px solid ${hover ? color : cAlpha(color, 0.25)}`,
        borderRadius: 6, padding: 8, cursor: 'pointer',
        color: hover ? color : 'var(--fg-2)',
        display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
        transition: 'all 160ms ease',
      }}>
      <CompIcon name={icon} size={16} color="currentColor" />
    </button>
  );
}

/* ── TeamTabs ──────────────────────────────────────────────────── */
function TeamTabs({ teams, active, onChange }) {
  return (
    <div style={{ display: 'flex', gap: 6, marginBottom: 24 }}>
      {teams.map(team => {
        const on = active === team;
        return (
          <button key={team} onClick={() => onChange(team)} style={{
            padding: '8px 18px', cursor: 'pointer',
            fontFamily: 'var(--font-mono)', fontSize: 12, fontWeight: 600,
            textTransform: 'uppercase', letterSpacing: '0.06em',
            borderRadius: 6,
            border: `1px solid ${on ? C_COLORS.teal : cAlpha(C_COLORS.teal, 0.20)}`,
            background: on ? cAlpha(C_COLORS.teal, 0.18) : 'transparent',
            color: on ? C_COLORS.teal : 'var(--fg-disabled)',
            transition: 'all 160ms ease',
          }}>{team}</button>
        );
      })}
    </div>
  );
}

/* ── VariantPill ─────────────────────────────────────────────────
   The compliance % pill that lives inside MetricHealthCard. One per
   priority/variant within a metric family. Dot only shown when the
   variant isn't already meeting target — green pills stay quiet. */
function VariantPill({ status, pct, label }) {
  const color = statusColor(status);
  const isOk = status === 'Meets/Exceeds Target';
  return (
    <span style={{
      display: 'inline-flex', alignItems: 'center', gap: 4,
      padding: '2px 7px',
      background: cAlpha(color, 0.12),
      border: `1px solid ${cAlpha(color, 0.25)}`,
      borderRadius: 3,
      fontFamily: 'var(--font-mono)', fontSize: 10,
      color: 'var(--fg-2)', whiteSpace: 'nowrap',
    }}>
      {!isOk && (
        <span style={{
          display: 'inline-block', width: 4, height: 4, borderRadius: '50%',
          background: color, boxShadow: `0 0 5px ${color}`,
        }} />
      )}
      {label && <span style={{ color: 'var(--fg-disabled)' }}>{label}</span>}
      <span style={{ color, fontWeight: 600 }}>{pctDisplay(pct)}</span>
    </span>
  );
}

/* ── StatusRibbon ────────────────────────────────────────────────
   The lozenge at the bottom of MetricHealthCard. "OK" when meeting,
   abbreviated status text otherwise. */
function StatusRibbon({ status }) {
  const color = statusColor(status);
  const isOk = status === 'Meets/Exceeds Target';
  return (
    <div style={{
      display: 'inline-flex', alignItems: 'center', gap: 5,
      fontFamily: 'var(--font-mono)', fontSize: 10,
      textTransform: 'uppercase', letterSpacing: '0.04em',
      color, padding: '3px 9px',
      background: cAlpha(color, 0.10),
      borderRadius: 999,
      border: `1px solid ${cAlpha(color, 0.30)}`,
    }}>
      <span style={{
        display: 'inline-block', width: 5, height: 5, borderRadius: '50%',
        background: color, boxShadow: isOk ? 'none' : `0 0 6px ${color}`,
      }} />
      {isOk ? 'OK' : status.replace(' of Target', '')}
    </div>
  );
}

/* ── MetricHealthCard ────────────────────────────────────────────
   The big clickable cards in the metric strip. Click to filter the
   device table; click the info "i" to open the metric definition
   panel. Border + ID color shift when active. */
function MetricHealthCard({ family, active, onClick, onInfoClick, onHover, onLeave }) {
  const [h, setH] = useCompState(false);
  const color = statusColor(family.worstStatus);
  return (
    <button
      onClick={onClick}
      onMouseEnter={(e) => { setH(true); onHover && onHover(e.currentTarget); }}
      onMouseLeave={() => { setH(false); onLeave && onLeave(); }}
      style={{
        position: 'relative', textAlign: 'left', cursor: 'pointer',
        background: active
          ? cAlpha(color, 0.15)
          : 'linear-gradient(135deg, rgba(30,41,59,0.95) 0%, rgba(15,23,42,0.98) 100%)',
        border: `1.5px solid ${active ? color : (h ? cAlpha(color, 0.50) : cAlpha(color, 0.25))}`,
        borderRadius: 8,
        padding: '14px 16px',
        minWidth: 160, flex: '1 1 0',
        transition: 'all 160ms ease',
      }}
    >
      <span
        onClick={(e) => { e.stopPropagation(); onInfoClick && onInfoClick(family.metricId); }}
        style={{
          position: 'absolute', top: 8, right: 8,
          display: 'inline-flex', cursor: 'pointer', padding: 2,
          color: 'var(--fg-disabled)', borderRadius: 3,
        }}
      >
        <CompIcon name="info" size={13} color="currentColor" />
      </span>
      <div style={{
        fontFamily: 'var(--font-mono)', fontSize: 15, fontWeight: 700,
        color: active ? color : 'var(--fg-1)', marginBottom: 4, paddingRight: 20,
      }}>{family.metricId}</div>
      <div style={{
        fontFamily: 'var(--font-mono)', fontSize: 10,
        color: 'var(--fg-disabled)', textTransform: 'uppercase', letterSpacing: '0.05em',
        marginBottom: 8,
      }}>{family.category}</div>
      <div style={{ display: 'flex', flexWrap: 'wrap', gap: 5, marginBottom: 8 }}>
        {family.entries.map((e, i) => (
          <VariantPill
            key={e.metric_id + '-' + i}
            status={e.status} pct={e.compliance_pct}
            label={family.entries.length > 1 ? (e.priority || `#${i + 1}`) : null}
          />
        ))}
      </div>
      <div style={{
        fontFamily: 'var(--font-mono)', fontSize: 10,
        color: 'var(--fg-disabled)', marginBottom: 8,
      }}>target {pctDisplay(family.target)}</div>
      <StatusRibbon status={family.worstStatus} />
    </button>
  );
}

/* ── MetricBadge ─────────────────────────────────────────────────
   Compact category-tinted ID chip used in device-row "Failing Metrics"
   columns and inside detail panels. */
function MetricBadge({ metricId, category }) {
  const color = CATEGORY_COLORS[category] || C_COLORS.slate;
  return (
    <span style={{
      display: 'inline-flex', alignItems: 'center',
      padding: '2px 7px',
      background: cAlpha(color, 0.12),
      border: `1px solid ${cAlpha(color, 0.30)}`,
      borderRadius: 3, color,
      fontFamily: 'var(--font-mono)', fontSize: 10, fontWeight: 600,
      whiteSpace: 'nowrap',
    }}>{metricId}</span>
  );
}

/* ── SeenBadge ───────────────────────────────────────────────────
   "1×" / "3×" / "5×" — how many cycles a host has been failing the
   same set of metrics. Color escalates: slate → amber → red. */
function SeenBadge({ count }) {
  const color = count > 3 ? C_COLORS.red : count > 1 ? C_COLORS.amber : C_COLORS.slate;
  return (
    <span style={{
      fontFamily: 'var(--font-mono)', fontSize: 10, fontWeight: 700,
      color, padding: '2px 7px',
      background: cAlpha(color, 0.10),
      border: `1px solid ${cAlpha(color, 0.30)}`,
      borderRadius: 3, whiteSpace: 'nowrap',
    }}>{count}×</span>
  );
}

/* ── DeviceTable + DeviceRow ─────────────────────────────────────
   The non-compliant host list. Toolbar has Active/Resolved tabs +
   hostname search. Rows show hostname, IP, type, failing metric
   badges, seen count, and a notes indicator. */
function DeviceTable({ children }) {
  return (
    <div style={{
      background: 'linear-gradient(135deg, rgba(30,41,59,0.95) 0%, rgba(15,23,42,0.98) 100%)',
      border: '1px solid rgba(20,184,166,0.15)',
      borderRadius: 8, overflow: 'hidden',
    }}>{children}</div>
  );
}

function DeviceTableToolbar({ tab, onTabChange, count, search, onSearchChange }) {
  return (
    <div style={{
      display: 'flex', justifyContent: 'space-between', alignItems: 'center',
      padding: '14px 16px', borderBottom: '1px solid rgba(255,255,255,0.05)',
    }}>
      <div style={{ display: 'flex', gap: 4 }}>
        {['active', 'resolved'].map(t => {
          const on = tab === t;
          return (
            <button key={t} onClick={() => onTabChange(t)} style={{
              padding: '6px 14px', cursor: 'pointer',
              fontFamily: 'var(--font-mono)', fontSize: 11, fontWeight: 600,
              textTransform: 'uppercase', letterSpacing: '0.04em',
              borderRadius: 4,
              border: `1px solid ${on ? cAlpha(C_COLORS.teal, 0.40) : 'transparent'}`,
              background: on ? cAlpha(C_COLORS.teal, 0.10) : 'transparent',
              color: on ? C_COLORS.teal : 'var(--fg-disabled)',
            }}>
              {t}
              {on && <span style={{ marginLeft: 6, color: 'var(--fg-2)' }}>({count})</span>}
            </button>
          );
        })}
      </div>
      <CompSearchInput value={search} onChange={onSearchChange} placeholder="Search hostname…" width={220} />
    </div>
  );
}

function CompSearchInput({ value, onChange, placeholder, width = 240 }) {
  const [focus, setFocus] = useCompState(false);
  return (
    <input
      value={value} onChange={onChange} placeholder={placeholder}
      onFocus={() => setFocus(true)} onBlur={() => setFocus(false)}
      style={{
        background: 'rgba(15,23,42,0.85)',
        border: `1px solid ${focus ? cAlpha(C_COLORS.teal, 0.60) : cAlpha(C_COLORS.teal, 0.20)}`,
        borderRadius: 4, color: 'var(--fg-1)', outline: 'none',
        padding: '6px 12px', fontSize: 12, fontFamily: 'var(--font-mono)',
        width, transition: 'border-color 160ms ease',
        boxShadow: focus ? `0 0 0 3px ${cAlpha(C_COLORS.teal, 0.10)}` : 'none',
      }}
    />
  );
}

const DEVICE_GRID = '2.5fr 1.1fr 1fr 2fr 0.5fr 0.4fr';

function DeviceTableHeader() {
  return (
    <div style={{
      display: 'grid', gridTemplateColumns: DEVICE_GRID,
      padding: '8px 16px',
      borderBottom: '1px solid rgba(255,255,255,0.05)',
      fontFamily: 'var(--font-mono)', fontSize: 10, fontWeight: 600,
      color: 'var(--fg-disabled)', textTransform: 'uppercase', letterSpacing: '0.08em',
    }}>
      <span>Hostname</span><span>IP Address</span><span>Type</span>
      <span>Failing Metrics</span><span>Seen</span><span></span>
    </div>
  );
}

function DeviceRow({ hostname, ip, type, failingMetrics, seenCount, hasNotes, selected, onClick }) {
  const [hover, setHover] = useCompState(false);
  return (
    <div
      onClick={onClick}
      onMouseEnter={() => setHover(true)} onMouseLeave={() => setHover(false)}
      style={{
        display: 'grid', gridTemplateColumns: DEVICE_GRID,
        padding: '10px 16px',
        borderBottom: '1px solid rgba(255,255,255,0.04)',
        cursor: 'pointer',
        background: selected ? cAlpha(C_COLORS.teal, 0.08) : (hover ? 'rgba(255,255,255,0.025)' : 'transparent'),
        borderLeft: selected ? `2px solid ${C_COLORS.teal}` : '2px solid transparent',
        transition: 'all 160ms ease', alignItems: 'center',
      }}
    >
      <div style={{
        fontFamily: 'var(--font-mono)', fontSize: 12,
        color: selected ? C_COLORS.teal : 'var(--fg-1)',
        overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap',
      }}>{hostname}</div>
      <div style={{ fontFamily: 'var(--font-mono)', fontSize: 11, color: 'var(--fg-2)' }}>{ip || '—'}</div>
      <div style={{ fontSize: 11, color: 'var(--fg-disabled)', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{type || '—'}</div>
      <div style={{ display: 'flex', flexWrap: 'wrap', gap: 4 }}>
        {failingMetrics.map(m => <MetricBadge key={m.metric_id} metricId={m.metric_id} category={m.category} />)}
      </div>
      <div><SeenBadge count={seenCount} /></div>
      <div style={{ display: 'flex', justifyContent: 'center' }}>
        {hasNotes && <CompIcon name="message" size={13} color={cAlpha(C_COLORS.teal, 0.7)} />}
      </div>
    </div>
  );
}

/* ── EmptyState — for table body when there's nothing to show. ── */
function CompEmpty({ children }) {
  return (
    <div style={{
      padding: 48, textAlign: 'center',
      color: 'var(--fg-disabled)', fontFamily: 'var(--font-mono)', fontSize: 12,
    }}>{children}</div>
  );
}

/* ── ChartCard — wrapper around any of the 6 charts on the page. ── */
function ChartCard({ title, subtitle, children, height = 240 }) {
  return (
    <div style={{
      background: 'linear-gradient(135deg, rgba(30,41,59,0.95) 0%, rgba(15,23,42,0.98) 100%)',
      border: '1px solid rgba(20,184,166,0.15)',
      borderRadius: 8, padding: 16,
    }}>
      <div style={{
        fontFamily: 'var(--font-mono)', fontSize: 11, fontWeight: 600,
        color: 'var(--fg-1)', textTransform: 'uppercase', letterSpacing: '0.08em',
        marginBottom: subtitle ? 2 : 12,
      }}>{title}</div>
      {subtitle && (
        <div style={{
          fontFamily: 'var(--font-mono)', fontSize: 10,
          color: 'var(--fg-disabled)', marginBottom: 12,
        }}>{subtitle}</div>
      )}
      <div style={{ height }}>{children}</div>
    </div>
  );
}

/* ── ChartLegend — shared legend row used at the top of stacked charts. ── */
function ChartLegend({ items }) {
  return (
    <div style={{ display: 'flex', gap: 12, flexWrap: 'wrap', marginBottom: 8 }}>
      {items.map(it => (
        <span key={it.label} style={{
          display: 'inline-flex', alignItems: 'center', gap: 6,
          fontFamily: 'var(--font-mono)', fontSize: 10, color: 'var(--fg-2)',
        }}>
          <span style={{
            display: 'inline-block', width: 10, height: 10, borderRadius: 2,
            background: it.color,
          }} />
          {it.label}
        </span>
      ))}
    </div>
  );
}

/* ── DefinitionTooltip ───────────────────────────────────────────
   The hover popover that surfaces a metric's title + business
   justification + data sources. */
function DefinitionTooltip({ title, justification, sources }) {
  return (
    <div style={{
      width: 300,
      background: 'linear-gradient(135deg, rgba(30,41,59,0.98) 0%, rgba(15,23,42,0.99) 100%)',
      border: `1px solid ${cAlpha(C_COLORS.teal, 0.30)}`,
      borderRadius: 8,
      boxShadow: '0 8px 32px rgba(0,0,0,0.6)',
      padding: '12px 14px',
    }}>
      <div style={{ fontFamily: 'var(--font-mono)', fontSize: 13, fontWeight: 700, color: 'var(--fg-1)', marginBottom: 6, lineHeight: 1.3 }}>{title}</div>
      {justification && (
        <div style={{ fontFamily: 'var(--font-display)', fontSize: 12, color: 'var(--fg-2)', lineHeight: 1.4, marginBottom: 6 }}>{justification}</div>
      )}
      {sources && (
        <div style={{ fontFamily: 'var(--font-mono)', fontSize: 10, color: 'var(--fg-disabled)' }}>Sources: {sources}</div>
      )}
    </div>
  );
}

/* ── RollbackDialog ──────────────────────────────────────────────
   Centered modal w/ red identity. "Reverses the most recent upload"
   message + danger confirm. */
function RollbackDialog({ reportLabel, onCancel, onConfirm, loading }) {
  return (
    <div style={{
      position: 'absolute', inset: 0, zIndex: 60,
      background: 'rgba(10,14,39,0.92)', backdropFilter: 'blur(8px)',
      display: 'flex', alignItems: 'center', justifyContent: 'center', padding: 16,
    }}>
      <div style={{
        background: 'linear-gradient(135deg, rgba(30,41,59,0.98) 0%, rgba(15,23,42,0.99) 100%)',
        border: '1px solid rgba(239,68,68,0.30)', borderRadius: 12,
        boxShadow: '0 20px 60px rgba(0,0,0,0.7)',
        width: '100%', maxWidth: 420, padding: 28,
      }}>
        <div style={{ fontFamily: 'var(--font-mono)', fontSize: 15, fontWeight: 700, color: C_COLORS.red, textTransform: 'uppercase', letterSpacing: '0.1em', marginBottom: 12 }}>
          Rollback Upload
        </div>
        <div style={{ fontSize: 13, color: 'var(--fg-1)', lineHeight: 1.5, marginBottom: 8, fontFamily: 'var(--font-display)' }}>
          This will reverse the most recent upload:
        </div>
        <div style={{
          fontFamily: 'var(--font-mono)', fontSize: 12, color: 'var(--fg-2)',
          background: 'rgba(15,23,42,0.6)', borderRadius: 6,
          padding: '10px 12px', marginBottom: 18,
          border: '1px solid rgba(239,68,68,0.15)',
        }}>
          <div><span style={{ color: 'var(--fg-disabled)' }}>File:</span> {reportLabel}</div>
          <div style={{ marginTop: 4, fontSize: 10, color: 'var(--fg-disabled)' }}>
            New items will be deleted, resolved items will be reactivated, and the upload record will be removed.
          </div>
        </div>
        <div style={{ display: 'flex', gap: 10 }}>
          <CompButton variant="ghost" onClick={onCancel} style={{ flex: 1, justifyContent: 'center' }}>Cancel</CompButton>
          <button onClick={onConfirm} disabled={loading} style={{
            flex: 2, padding: 10,
            background: loading ? 'rgba(239,68,68,0.05)' : 'rgba(239,68,68,0.10)',
            border: `1px solid ${C_COLORS.red}`, borderRadius: 6,
            color: C_COLORS.red, cursor: loading ? 'wait' : 'pointer',
            fontFamily: 'var(--font-mono)', fontSize: 12, fontWeight: 600,
            textTransform: 'uppercase', letterSpacing: '0.05em',
            display: 'inline-flex', alignItems: 'center', justifyContent: 'center', gap: 6,
            opacity: loading ? 0.6 : 1,
          }}>
            <CompIcon name="rotate" size={13} color="currentColor" />
            {loading ? 'Rolling back…' : 'Confirm Rollback'}
          </button>
        </div>
      </div>
    </div>
  );
}

/* ── RollbackToast — bottom-right confirmation/error toast. ── */
function RollbackToast({ tone = 'success', message, detail, onDismiss }) {
  const c = tone === 'error' ? C_COLORS.red : C_COLORS.green;
  return (
    <div onClick={onDismiss} style={{
      position: 'absolute', bottom: 24, right: 24, zIndex: 70,
      background: 'linear-gradient(135deg, rgba(30,41,59,0.98) 0%, rgba(15,23,42,0.99) 100%)',
      border: `1px solid ${cAlpha(c, 0.40)}`, borderRadius: 8,
      boxShadow: '0 8px 32px rgba(0,0,0,0.5)',
      padding: '14px 20px', maxWidth: 360,
      fontFamily: 'var(--font-mono)', fontSize: 12, color: c, cursor: 'pointer',
    }}>
      <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: detail ? 4 : 0 }}>
        <CompIcon name={tone === 'error' ? 'alert' : 'rotate'} size={14} color="currentColor" />
        {message}
      </div>
      {detail && <div style={{ fontSize: 10, color: 'var(--fg-2)' }}>{detail}</div>}
    </div>
  );
}

/* ── CompIcon — every icon used by the compliance page. ── */
function CompIcon({ name, size = 16, color = 'currentColor' }) {
  const p = {
    width: size, height: size, viewBox: '0 0 24 24', fill: 'none',
    stroke: color, strokeWidth: 2, strokeLinecap: 'round', strokeLinejoin: 'round',
    style: { display: 'inline-block', verticalAlign: 'middle' },
  };
  switch (name) {
    case 'upload':   return <svg {...p}><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="17 8 12 3 7 8"/><line x1="12" y1="3" x2="12" y2="15"/></svg>;
    case 'refresh': return <svg {...p}><path d="M3 12a9 9 0 0 1 15-6.7L21 8"/><path d="M21 3v5h-5"/><path d="M21 12a9 9 0 0 1-15 6.7L3 16"/><path d="M3 21v-5h5"/></svg>;
    case 'rotate':   return <svg {...p}><path d="M3 12a9 9 0 1 0 9-9"/><polyline points="3 4 3 9 8 9"/></svg>;
    case 'message':  return <svg {...p}><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/></svg>;
    case 'info':     return <svg {...p}><circle cx="12" cy="12" r="10"/><line x1="12" y1="16" x2="12" y2="12"/><line x1="12" y1="8" x2="12.01" y2="8"/></svg>;
    case 'alert':    return <svg {...p}><circle cx="12" cy="12" r="10"/><line x1="12" y1="8" x2="12" y2="12"/><line x1="12" y1="16" x2="12.01" y2="16"/></svg>;
    case 'check':    return <svg {...p}><circle cx="12" cy="12" r="10"/><path d="m9 12 2 2 4-4"/></svg>;
    case 'loader':   return <svg {...p}><line x1="12" y1="2" x2="12" y2="6"/><line x1="12" y1="18" x2="12" y2="22"/><line x1="4.93" y1="4.93" x2="7.76" y2="7.76"/><line x1="16.24" y1="16.24" x2="19.07" y2="19.07"/><line x1="2" y1="12" x2="6" y2="12"/><line x1="18" y1="12" x2="22" y2="12"/><line x1="4.93" y1="19.07" x2="7.76" y2="16.24"/><line x1="16.24" y1="7.76" x2="19.07" y2="4.93"/></svg>;
    case 'x':        return <svg {...p}><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>;
    default:         return <svg {...p}><circle cx="12" cy="12" r="10"/></svg>;
  }
}

window.COMP = {
  COLORS: C_COLORS, STATUS_COLOR, CATEGORY_COLORS,
  statusColor, pctDisplay, cAlpha,
  CompPageHeader, CompButton, CompIconButton, TeamTabs,
  VariantPill, StatusRibbon, MetricHealthCard, MetricBadge, SeenBadge,
  DeviceTable, DeviceTableToolbar, DeviceTableHeader, DeviceRow, CompSearchInput, CompEmpty,
  ChartCard, ChartLegend,
  DefinitionTooltip, RollbackDialog, RollbackToast,
  CompIcon,
};
