// CRIMINAL JUSTICE PLATFORM — Enterprise enhancements
// Adds 4 new tabs + detail drawer + right-click context + dashboard alerts.
//
//   1. Speedy Trial Act tracker  (18 U.S.C. § 3161)
//   2. Suppression Motion Engine (4A / 5A / 6A / Franks / Title III)
//   3. USSG Sentencing Calculator + § 3553(a) themes
//   4. Crimmigration / Padilla   (8 U.S.C. § 1227 / § 1182)
//   + Defendant detail drawer
//   + Defendant right-click factory
//   + Dashboard critical-path alert ribbon

(function () {
  const { useState, useMemo } = React;
  const T = window.ArbiterTokens;
  const A = window.Arbiter || {};

  // Data extensions — STA, immigration, suppression, forfeiture
  const STA = {
    'D-2026-0142': { indictmentDate: '2026-01-22', staDeadline: '2026-04-02', excludedDays: 28, excludedReasons: ['Defense MTC Brady (45 days)', 'Joint motion to continue (continuity of counsel, 14 days)'], status: 'On extension' },
    'D-2026-0141': { indictmentDate: '2025-12-04', staDeadline: '2026-02-12', excludedDays: 96, excludedReasons: ['MTD pending (60 days)', 'Pretrial motions briefing (36 days)'], status: 'Tolled' },
    'D-2026-0140': { indictmentDate: '2026-02-10', staDeadline: '2026-04-21', excludedDays: 14, excludedReasons: ['Bail appeal pending (D.C. Cir.)'], status: 'CRITICAL' },
    'D-2026-0139': { indictmentDate: '2025-10-08', staDeadline: '2025-12-17', excludedDays: 142, excludedReasons: ['Suppression hearing (32 days)', 'Daubert motion (24 days)', 'Joint continuance (60 days)', 'Calendar congestion (26 days)'], status: 'Trial set 2026-05-06' },
    'D-2026-0138': { indictmentDate: '2025-11-20', staDeadline: '2026-01-29', excludedDays: 88, excludedReasons: ['Ongoing plea negotiations (54 days)', 'Cooperation evaluation (34 days)'], status: 'Plea pending' },
    'D-2026-0137': { indictmentDate: '2025-08-14', staDeadline: '2025-10-23', excludedDays: 0, excludedReasons: ['Trial commenced 2025-12-01 — STA satisfied'], status: 'Convicted' },
    'D-2026-0136': { indictmentDate: '2026-01-08', staDeadline: '2026-03-19', excludedDays: 38, excludedReasons: ['Suppression motion (28 days)', 'Franks hearing (10 days)'], status: 'Tolled — suppression' },
    'D-2026-0135': { indictmentDate: null, staDeadline: null, excludedDays: 0, excludedReasons: ['Pre-charge — STA inapplicable'], status: 'N/A — pre-charge' },
    'D-2026-0134': { indictmentDate: '2025-07-22', staDeadline: '2025-09-30', excludedDays: 0, excludedReasons: ['Plea entered 2025-09-12 — STA satisfied'], status: 'Sentenced' },
    'D-2026-0133': { indictmentDate: '2024-04-10', staDeadline: '2024-06-19', excludedDays: 0, excludedReasons: ['Convicted at trial 2025-09-15'], status: 'Post-conviction' },
  };

  const IMMIG = {
    'D-2026-0142': { citizenship: 'US Citizen', status: 'N/A',                   risk: 'none' },
    'D-2026-0141': { citizenship: 'India',      status: 'LPR (Green Card)',      risk: 'severe',  cimt: true,  aggFelony: false, controlledSub: false, note: 'Health care fraud > $10K = aggravated felony per 8 USC § 1101(a)(43)(M)(i) — mandatory deportation' },
    'D-2026-0140': { citizenship: 'PRC',        status: 'L-1B Visa (expired)',   risk: 'severe',  cimt: true,  aggFelony: true,  controlledSub: false, note: 'FCPA + money laundering > $10K = aggravated felony — automatic removal; ineligible for cancellation' },
    'D-2026-0139': { citizenship: 'US Citizen', status: 'N/A',                   risk: 'none' },
    'D-2026-0138': { citizenship: 'Taiwan',     status: 'EB-5 Investor (pending)', risk: 'high', cimt: true,  aggFelony: true,  controlledSub: false, note: 'Mail fraud loss > $10K + cooperation = downward — but aggravated felony bar to relief' },
    'D-2026-0137': { citizenship: 'US Citizen', status: 'N/A',                   risk: 'none' },
    'D-2026-0136': { citizenship: 'US Citizen', status: 'N/A',                   risk: 'none' },
    'D-2026-0135': { citizenship: 'Ireland',    status: 'O-1 Visa',              risk: 'unknown', cimt: null, aggFelony: null, controlledSub: null, note: 'Pre-charge — provide full Padilla advisal before any cooperation discussion' },
    'D-2026-0134': { citizenship: 'US Citizen', status: 'N/A',                   risk: 'none' },
    'D-2026-0133': { citizenship: 'Mexico',     status: 'LPR (since 1998)',      risk: 'severe',  cimt: true, aggFelony: true, controlledSub: false, note: 'RICO conviction = aggravated felony; 96-mo sentence triggers mandatory removal post-release' },
  };

  // Suppression grounds — per defendant
  const SUPPRESSION = [
    { defendantId: 'D-2026-0136', amend: '4A', ground: 'No probable cause for stop',           strength: 'strong',   evidence: 'Officer affidavit conclusory; no corroboration of CI tip', filed: '2026-02-20', status: 'Pending', exposureMonths: 60 },
    { defendantId: 'D-2026-0136', amend: '4A', ground: 'Consent search coerced',                strength: 'moderate', evidence: 'Schneckloth factors disputed; show-of-force; no Miranda', filed: '2026-02-20', status: 'Pending', exposureMonths: 60 },
    { defendantId: 'D-2026-0136', amend: 'Franks', ground: 'Material misstatements in affidavit', strength: 'strong', evidence: 'CI-2 had 3 prior false-report allegations omitted from affidavit', filed: '2026-02-20', status: 'GRANTED — hearing 5/8', exposureMonths: 60 },
    { defendantId: 'D-2026-0140', amend: '4A', ground: 'Email seizure exceeded warrant scope',   strength: 'moderate', evidence: 'Warrant authorized 2022 emails; gov\'t pulled 2018-2024',  filed: '2026-03-04', status: 'Pending', exposureMonths: 36 },
    { defendantId: 'D-2026-0140', amend: '5A', ground: 'Custodial interrogation w/o Miranda',    strength: 'weak',     evidence: 'Detention in conference room arguably non-custodial',     filed: '2026-03-04', status: 'Pending', exposureMonths: 24 },
    { defendantId: 'D-2026-0140', amend: 'Title III', ground: 'Inadequate minimization',         strength: 'moderate', evidence: 'No contemporaneous minimization log; broad subject-matter capture', filed: '2026-03-10', status: 'Pending', exposureMonths: 36 },
    { defendantId: 'D-2026-0142', amend: '6A', ground: 'Mass. v. Sheppard / Strickland conflict', strength: 'weak',    evidence: 'Prior counsel\'s alleged conflict — speculative',          filed: '2026-02-24', status: 'Withdrawn', exposureMonths: 0 },
    { defendantId: 'D-2026-0141', amend: '4A', ground: 'Patient-record subpoena overbroad',       strength: 'strong',   evidence: 'HIPAA + 5,400 charts swept w/o particularity',           filed: '2026-03-08', status: 'Pending', exposureMonths: 48 },
  ];

  // USSG calc — extended per active defendant
  const USSG = {
    'D-2026-0142': { base: 7, encs: [
        { code: '§2B1.1(b)(1)(H)', label: 'Loss > $1.5M', add: 16, on: true },
        { code: '§2B1.1(b)(2)(B)', label: '50+ victims', add: 4, on: true },
        { code: '§3B1.1(b)',       label: 'Manager (5+ participants)', add: 3, on: true },
        { code: '§3B1.3',          label: 'Abuse of position of trust', add: 2, on: false, contested: true },
      ], reds: [
        { code: '§3E1.1(a)+(b)',   label: 'Acceptance (full plea)', sub: 3, on: true },
        { code: '§5C1.2',           label: 'Safety valve', sub: 2, on: false },
      ], chc: 'I', themes: ['First-time offender', 'Charitable activity', 'Pretrial compliance', 'Family caregiver'] },
    'D-2026-0140': { base: 7, encs: [
        { code: '§2B1.1(b)(10)',    label: 'Sophisticated means', add: 2, on: true },
        { code: '§2B1.1(b)(20)',    label: 'Bribery scheme (FCPA)', add: 4, on: true },
        { code: '§3B1.1(a)',         label: 'Organizer/leader', add: 4, on: true, contested: true },
        { code: '§2S1.1(b)(2)(B)',   label: 'Money laundering offense', add: 2, on: true },
      ], reds: [
        { code: '§3E1.1(a)',         label: 'Acceptance — partial', sub: 2, on: false },
        { code: '§5K1.1',            label: 'Substantial assistance', sub: 4, on: false, conditional: 'If cooperator' },
      ], chc: 'II', themes: ['No domestic assets', 'Family hardship (US citizen children)', 'Health condition', 'Cooperation potential'] },
    'D-2026-0136': { base: 32, encs: [
        { code: '§2D1.1(b)(1)',      label: 'Firearm possession (drug)', add: 2, on: true, contested: true },
        { code: '§3B1.1(c)',         label: 'Minor manager role', add: 2, on: false },
      ], reds: [
        { code: '§3E1.1(a)+(b)',     label: 'Acceptance', sub: 3, on: true },
        { code: '§5C1.2',            label: 'Safety valve (§ 841 only)', sub: 2, on: true, conditional: 'If § 924(c) dismissed' },
        { code: '§5K1.1',            label: 'Substantial assistance', sub: 4, on: true, conditional: 'Plea + cooperation' },
      ], chc: 'III', themes: ['Coercion narrative', 'Substance use treatment record', 'Single parent', 'Acceptance of responsibility'] },
    'D-2026-0138': { base: 7, encs: [
        { code: '§2B1.1(b)(1)(I)',   label: 'Loss $3.5M-$9.5M', add: 18, on: true },
        { code: '§2B1.1(b)(2)(A)',   label: '10+ victims', add: 2, on: true },
        { code: '§2B1.1(b)(10)',     label: 'Sophisticated means', add: 2, on: true },
      ], reds: [
        { code: '§3E1.1(a)+(b)',     label: 'Acceptance', sub: 3, on: true },
        { code: '§5K1.1',            label: 'Substantial assistance', sub: 6, on: true },
      ], chc: 'I', themes: ['Cooperation in 2 related matters', 'No prior record', 'Voluntary repatriation of assets', 'Restitution paid in full'] },
  };

  // Restitution / forfeiture
  const RESTITUTION = [
    { defendantId: 'D-2026-0142', amount: 14_200_000, paid: 0,         victims: 412,  asset: 'Real estate ($8.2M), accounts ($1.4M)', status: 'Pending sentencing' },
    { defendantId: 'D-2026-0137', amount: 31_400_000, paid: 4_800_000, victims: 11,   asset: 'Frozen brokerage accounts',              status: 'Schedule entered' },
    { defendantId: 'D-2026-0138', amount: 4_800_000,  paid: 4_800_000, victims: 38,   asset: 'Repatriated from Cayman Islands',        status: 'Paid in full' },
    { defendantId: 'D-2026-0141', amount: 8_900_000,  paid: 0,         victims: 1,    asset: 'CMS — Medicare overbilling',             status: 'Disputed amount' },
    { defendantId: 'D-2026-0133', amount: 12_600_000, paid: 1_200_000, victims: 'Multiple', asset: 'Forfeited fleet ($4.8M)',          status: 'Ongoing post-conviction' },
  ];

  // Compassionate release / § 3582 motions
  const COMPASSIONATE = [
    { id: 'CR-001', defendant: '— prior client (FCI Lompoc) —', filed: '2026-03-18', basis: 'Terminal illness (stage IV)', warden: 'Denied — exhausted', status: 'Filed under § 3582(c)(1)(A)', hearing: '2026-05-14', likelihood: 'Moderate' },
    { id: 'CR-002', defendant: '— prior client (FCI Schuylkill) —', filed: '2026-02-04', basis: 'Caregiver — only living parent of minor', warden: 'Denied — exhausted', status: 'Filed', hearing: '2026-05-22', likelihood: 'Moderate-low' },
    { id: 'CR-003', defendant: '— prior client (FCI Tallahassee) —', filed: '2026-01-22', basis: 'Sentencing disparity (post-Booker)', warden: 'Denied — exhausted', status: 'Granted in part — 24 mo reduction', hearing: '2026-04-12 (complete)', likelihood: 'Granted' },
  ];

  // Bail violations
  const BAIL_VIOLATIONS = [
    { id: 'BV-001', defendantId: 'D-2026-0142', date: '2026-03-14', type: 'Unauthorized travel',  detail: 'Travel to NJ for child\'s graduation — 4hr lapse',  cured: true, action: 'Self-reported; warning' },
    { id: 'BV-002', defendantId: 'D-2026-0136', date: '2026-04-02', type: 'Failed drug test',      detail: 'Marijuana metabolites detected',                     cured: false, action: 'Modification motion filed; treatment program' },
    { id: 'BV-003', defendantId: 'D-2026-0136', date: '2026-04-15', type: 'Curfew violation',      detail: 'GPS shows 2:14 AM departure — 47 min',               cured: false, action: 'Show-cause hearing 2026-04-29' },
    { id: 'BV-004', defendantId: 'D-2026-0141', date: '2026-03-08', type: 'Practiced medicine',    detail: 'Reviewed colleague\'s patient chart',                cured: true, action: 'No charge; admonishment' },
  ];

  const burgundy = '#9D2235', orange = '#C7501F', amber = '#D97706', cobalt = '#2563EB', teal = '#0F766E', emerald = '#059669', violet = '#7C3AED', slate = '#475569', crit = '#C23030';

  // ─────────────────────────────────────────────────────────────────────────
  // Common helpers
  // ─────────────────────────────────────────────────────────────────────────
  const flash = (kind, message, title) => A.toast?.({ kind, title, message });
  const fmtDate = (s) => s ? new Date(s).toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' }) : '—';
  const daysBetween = (a, b) => Math.ceil((new Date(a) - new Date(b)) / 86400000);

  // ─────────────────────────────────────────────────────────────────────────
  // 1) SPEEDY TRIAL ACT TRACKER
  // ─────────────────────────────────────────────────────────────────────────
  function CriminalSpeedyTrial({ data }) {
    const cj = window.__cj;
    const today = new Date('2026-04-26');

    const rows = data.defendants.map(d => {
      const sta = STA[d.id] || { indictmentDate: null, staDeadline: null, excludedDays: 0, excludedReasons: [], status: '—' };
      const days = sta.staDeadline ? daysBetween(sta.staDeadline, today) + sta.excludedDays : null;
      const adjusted = sta.staDeadline ? new Date(sta.staDeadline) : null;
      if (adjusted) adjusted.setDate(adjusted.getDate() + sta.excludedDays);
      const adjustedDays = adjusted ? daysBetween(adjusted, today) : null;
      return { d, sta, days, adjusted, adjustedDays };
    });

    const atRisk = rows.filter(r => r.adjustedDays != null && r.adjustedDays < 30 && r.adjustedDays >= 0).length;
    const critical = rows.filter(r => r.adjustedDays != null && r.adjustedDays < 14 && r.adjustedDays >= 0).length;
    const violated = rows.filter(r => r.adjustedDays != null && r.adjustedDays < 0).length;
    const totalExcluded = rows.reduce((s, r) => s + (r.sta.excludedDays || 0), 0);

    return (
      <div>
        {/* Statute banner */}
        <div style={{ ...cj.card, marginBottom: 12, borderLeft: `3px solid ${cobalt}` }}>
          <div style={{ padding: '10px 16px', background: `${cobalt}10`, fontSize: 12, color: T.color.text.secondary, lineHeight: 1.5 }}>
            <span style={{ fontSize: 10, fontWeight: 700, color: cobalt, textTransform: 'uppercase', letterSpacing: '0.06em', marginRight: 8 }}>18 U.S.C. § 3161</span>
            70-day clock runs from indictment or first appearance, whichever is later. Excludable time under § 3161(h) tolls automatically for pretrial motions, mental competency, joinder, plea negotiations, and ends-of-justice continuances. Dismissal under § 3162(a)(2) on a violation can be with or without prejudice.
          </div>
        </div>

        {/* KPI strip */}
        <div style={{ display: 'flex', gap: 8, marginBottom: 12, flexWrap: 'wrap' }}>
          <div style={cj.stat}><span style={cj.statLabel}>Defendants tracked</span><span style={cj.statValue}>{rows.filter(r => r.sta.staDeadline).length}</span></div>
          <div style={cj.stat}><span style={cj.statLabel}>At risk (&lt;30d)</span><span style={{ ...cj.statValue, color: amber }}>{atRisk}</span></div>
          <div style={cj.stat}><span style={cj.statLabel}>Critical (&lt;14d)</span><span style={{ ...cj.statValue, color: crit }}>{critical}</span></div>
          <div style={cj.stat}><span style={cj.statLabel}>Potential violation</span><span style={{ ...cj.statValue, color: violated ? crit : T.color.text.tertiary }}>{violated}</span></div>
          <div style={cj.stat}><span style={cj.statLabel}>Total excludable days</span><span style={{ ...cj.statValue, color: cobalt }}>{totalExcluded}</span></div>
        </div>

        <div style={cj.card}>
          <div style={cj.cardH}>
            <span>Speedy Trial Act clock — {rows.filter(r => r.sta.staDeadline).length} active</span>
            <button style={cj.btnSecondary}>Export STA report</button>
          </div>
          <table style={{ width: '100%', borderCollapse: 'collapse' }}>
            <thead style={{ background: T.color.bg.secondary }}>
              <tr>
                <th style={cj.th}>Defendant</th>
                <th style={cj.th}>Indictment</th>
                <th style={cj.th}>STA deadline (raw)</th>
                <th style={cj.th}>+ Excluded</th>
                <th style={cj.th}>Adjusted deadline</th>
                <th style={cj.th}>Days remaining</th>
                <th style={cj.th}>Status</th>
              </tr>
            </thead>
            <tbody>
              {rows.map(({ d, sta, adjusted, adjustedDays }) => {
                const tone = adjustedDays == null ? T.color.text.tertiary
                  : adjustedDays < 0  ? crit
                  : adjustedDays < 14 ? crit
                  : adjustedDays < 30 ? amber
                  : T.color.text.tertiary;
                return (
                  <tr key={d.id}>
                    <td style={{ ...cj.td, fontWeight: 600 }}>{d.name}<div style={{ fontSize: 10, color: T.color.text.tertiary, fontFamily: T.font.mono }}>{d.docket}</div></td>
                    <td style={{ ...cj.td, fontFamily: T.font.mono, fontSize: 11, color: T.color.text.secondary }}>{fmtDate(sta.indictmentDate)}</td>
                    <td style={{ ...cj.td, fontFamily: T.font.mono, fontSize: 11, color: T.color.text.secondary }}>{fmtDate(sta.staDeadline)}</td>
                    <td style={{ ...cj.td, fontFamily: T.font.mono, fontSize: 11, color: cobalt, fontWeight: 700 }}>{sta.excludedDays > 0 ? `+${sta.excludedDays}d` : '—'}</td>
                    <td style={{ ...cj.td, fontFamily: T.font.mono, fontSize: 11, fontWeight: 700 }}>{adjusted ? fmtDate(adjusted.toISOString().slice(0, 10)) : '—'}</td>
                    <td style={{ ...cj.td, fontFamily: T.font.mono, fontWeight: 700, color: tone }}>
                      {adjustedDays == null ? '—' : adjustedDays < 0 ? `${-adjustedDays}d OVER` : `${adjustedDays}d`}
                    </td>
                    <td style={cj.td}><span style={{ ...cj.tag, background: tone + '14', color: tone }}>{sta.status}</span></td>
                  </tr>
                );
              })}
            </tbody>
          </table>
        </div>

        {/* Excludable time log */}
        <div style={cj.card}>
          <div style={cj.cardH}>
            <span>Excludable time log — § 3161(h)</span>
            <span style={{ fontSize: 10, color: T.color.text.tertiary, fontWeight: 500, textTransform: 'none' }}>Pretrial motions, competency, joinder, ends-of-justice</span>
          </div>
          <div style={{ padding: 12 }}>
            {rows.filter(r => r.sta.excludedReasons && r.sta.excludedReasons.length).map(({ d, sta }) => (
              <div key={d.id} style={{ padding: '10px 12px', marginBottom: 6, borderRadius: 6,
                background: T.color.bg.secondary, borderLeft: `3px solid ${cobalt}` }}>
                <div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: 6 }}>
                  <span style={{ fontSize: 11, fontWeight: 700, color: T.color.text.primary }}>{d.name}</span>
                  <span style={{ fontSize: 11, fontWeight: 700, color: cobalt, fontFamily: T.font.mono }}>+{sta.excludedDays}d total</span>
                </div>
                <ul style={{ margin: 0, paddingLeft: 18, fontSize: 11, color: T.color.text.secondary, lineHeight: 1.6 }}>
                  {sta.excludedReasons.map((r, i) => <li key={i}>{r}</li>)}
                </ul>
              </div>
            ))}
          </div>
        </div>

        {/* Motion to dismiss generator */}
        <div style={{ ...cj.card, marginBottom: 0, borderLeft: `3px solid ${crit}` }}>
          <div style={{ ...cj.cardH, color: crit }}>Motion to dismiss — Speedy Trial Act § 3162(a)(2)</div>
          <div style={{ padding: 16 }}>
            <div style={{ fontSize: 11, color: T.color.text.secondary, lineHeight: 1.6, marginBottom: 10 }}>
              On a § 3162(a)(2) violation the court <b>must</b> dismiss the indictment. Dismissal is with or without prejudice based on three factors: <b>(1)</b> seriousness of the offense, <b>(2)</b> facts and circumstances of the delay, <b>(3)</b> impact on STA and administration of justice. <i>United States v. Taylor</i>, 487 U.S. 326 (1988).
            </div>
            <div style={{ display: 'flex', gap: 8 }}>
              <button style={cj.btnPrimary}
                onClick={() => flash('success', 'STA dismissal motion drafted in Studio')}>
                Draft motion (with prejudice argument)
              </button>
              <button style={cj.btnSecondary}
                onClick={() => flash('info', 'STA worksheet exported')}>
                Export day-by-day worksheet
              </button>
            </div>
          </div>
        </div>
      </div>
    );
  }

  // ─────────────────────────────────────────────────────────────────────────
  // 2) SUPPRESSION MOTION ENGINE
  // ─────────────────────────────────────────────────────────────────────────
  function CriminalSuppression({ data }) {
    const cj = window.__cj;
    const [amend, setAmend] = useState('all');
    const filtered = SUPPRESSION.filter(s => amend === 'all' || s.amend === amend);

    const grounds = ['all', '4A', '5A', '6A', 'Franks', 'Title III'];
    const totalExposure = filtered.reduce((s, x) => s + x.exposureMonths, 0);

    const strengthColor = (s) => s === 'strong' ? emerald : s === 'moderate' ? amber : s === 'weak' ? crit : T.color.text.tertiary;
    const statusColor = (s) => /granted|GRANTED/i.test(s) ? emerald : /denied/i.test(s) ? crit : /pending/i.test(s) ? amber : T.color.text.tertiary;

    return (
      <div>
        <div style={{ display: 'flex', gap: 8, marginBottom: 12, flexWrap: 'wrap' }}>
          <div style={cj.stat}><span style={cj.statLabel}>Suppression grounds</span><span style={cj.statValue}>{filtered.length}</span></div>
          <div style={cj.stat}><span style={cj.statLabel}>Strong</span><span style={{ ...cj.statValue, color: emerald }}>{filtered.filter(s => s.strength === 'strong').length}</span></div>
          <div style={cj.stat}><span style={cj.statLabel}>Granted</span><span style={{ ...cj.statValue, color: emerald }}>{filtered.filter(s => /granted/i.test(s.status)).length}</span></div>
          <div style={cj.stat}><span style={cj.statLabel}>Pending</span><span style={{ ...cj.statValue, color: amber }}>{filtered.filter(s => /pending/i.test(s.status)).length}</span></div>
          <div style={cj.stat}><span style={cj.statLabel}>Months at stake</span><span style={{ ...cj.statValue, color: cobalt }}>{totalExposure}</span></div>
        </div>

        <div style={cj.card}>
          <div style={{ ...cj.cardH, gap: 8, flexWrap: 'wrap' }}>
            <span>Suppression motion engine</span>
            <div style={{ display: 'flex', gap: 4 }}>
              {grounds.map(g => (
                <button key={g} onClick={() => setAmend(g)}
                  style={{ ...(amend === g ? { ...cj.filterBtn, ...cj.filterBtnActive } : cj.filterBtn) }}>
                  {g === 'all' ? 'All' : g}
                </button>
              ))}
            </div>
          </div>
          <table style={{ width: '100%', borderCollapse: 'collapse' }}>
            <thead style={{ background: T.color.bg.secondary }}>
              <tr>
                <th style={cj.th}>Defendant</th>
                <th style={cj.th}>Amend.</th>
                <th style={cj.th}>Ground</th>
                <th style={cj.th}>Strength</th>
                <th style={cj.th}>Status</th>
                <th style={{ ...cj.th, textAlign: 'right' }}>Exposure</th>
              </tr>
            </thead>
            <tbody>
              {filtered.map((s, i) => {
                const def = data.defendants.find(d => d.id === s.defendantId);
                return (
                  <tr key={i}>
                    <td style={{ ...cj.td, fontWeight: 600 }}>{def?.name || s.defendantId}</td>
                    <td style={cj.td}>
                      <span style={{ ...cj.tag, background: cobalt + '14', color: cobalt, fontFamily: T.font.mono }}>{s.amend}</span>
                    </td>
                    <td style={{ ...cj.td, color: T.color.text.primary, fontSize: 12 }}>
                      {s.ground}
                      <div style={{ fontSize: 10, color: T.color.text.tertiary, marginTop: 2 }}>{s.evidence}</div>
                    </td>
                    <td style={cj.td}>
                      <span style={{ ...cj.tag, background: strengthColor(s.strength) + '14', color: strengthColor(s.strength), textTransform: 'uppercase', letterSpacing: '0.06em' }}>
                        {s.strength}
                      </span>
                    </td>
                    <td style={cj.td}>
                      <span style={{ fontSize: 11, color: statusColor(s.status), fontWeight: 600 }}>{s.status}</span>
                      <div style={{ fontSize: 10, color: T.color.text.tertiary, fontFamily: T.font.mono }}>filed {fmtDate(s.filed)}</div>
                    </td>
                    <td style={{ ...cj.td, textAlign: 'right', fontFamily: T.font.mono, fontWeight: 700, color: s.exposureMonths > 0 ? crit : T.color.text.tertiary }}>
                      {s.exposureMonths > 0 ? `${s.exposureMonths} mo` : '—'}
                    </td>
                  </tr>
                );
              })}
            </tbody>
          </table>
        </div>

        {/* Franks factor checklist */}
        <div style={{ ...cj.card, marginBottom: 0, borderLeft: `3px solid ${burgundy}` }}>
          <div style={{ ...cj.cardH, color: burgundy }}>Franks v. Delaware (438 U.S. 154) — factor checklist</div>
          <div style={{ padding: 16, display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 12 }}>
            {[
              { factor: 'Material misstatement OR omission in affidavit', met: true, note: 'CI-2 prior false-report history omitted' },
              { factor: 'Made knowingly and intentionally OR with reckless disregard', met: true, note: 'IA records show officer awareness' },
              { factor: 'Substantial preliminary showing made', met: true, note: 'Threshold for hearing satisfied' },
              { factor: 'Without misstatement, affidavit lacks probable cause', met: 'partial', note: 'Tip-only basis insufficient' },
              { factor: 'Hearing granted', met: true, note: 'Set for 2026-05-08' },
              { factor: 'Good-faith Leon exception applies', met: false, note: 'Officer affidavit was not facially sufficient' },
            ].map((f, i) => (
              <div key={i} style={{ display: 'flex', gap: 8, alignItems: 'flex-start' }}>
                <span style={{ width: 16, height: 16, borderRadius: '50%', flexShrink: 0,
                  background: f.met === true ? emerald : f.met === 'partial' ? amber : T.color.bg.secondary,
                  color: '#fff', fontSize: 10, fontWeight: 700,
                  display: 'flex', alignItems: 'center', justifyContent: 'center',
                  border: f.met === false ? `1px solid ${T.color.border.medium}` : 'none' }}>
                  {f.met === true ? '✓' : f.met === 'partial' ? '~' : '?'}
                </span>
                <div>
                  <div style={{ fontSize: 11, fontWeight: 600, color: T.color.text.primary }}>{f.factor}</div>
                  <div style={{ fontSize: 10, color: T.color.text.tertiary, marginTop: 2 }}>{f.note}</div>
                </div>
              </div>
            ))}
          </div>
        </div>
      </div>
    );
  }

  // ─────────────────────────────────────────────────────────────────────────
  // 3) USSG SENTENCING CALCULATOR + § 3553(a) THEMES
  // ─────────────────────────────────────────────────────────────────────────
  function CriminalUSSG({ data }) {
    const cj = window.__cj;
    const candidates = data.defendants.filter(d => USSG[d.id]);
    const [selectedId, setSelectedId] = useState(candidates[0]?.id);
    const def = candidates.find(d => d.id === selectedId);
    const initial = USSG[selectedId];

    // Local toggle state for enhancements/reductions
    const [encs, setEncs] = useState(initial?.encs.map(e => ({ ...e })));
    const [reds, setReds] = useState(initial?.reds.map(r => ({ ...r })));
    const [chc, setChc] = useState(initial?.chc);

    React.useEffect(() => {
      const u = USSG[selectedId];
      setEncs(u?.encs.map(e => ({ ...e })));
      setReds(u?.reds.map(r => ({ ...r })));
      setChc(u?.chc);
    }, [selectedId]);

    if (!initial || !encs || !reds) return <div>Loading…</div>;

    const totalAdd = encs.filter(e => e.on).reduce((s, e) => s + e.add, 0);
    const totalSub = reds.filter(r => r.on).reduce((s, r) => s + r.sub, 0);
    const finalLevel = Math.max(0, initial.base + totalAdd - totalSub);

    // USSG range computed from offense level & criminal history (rough zone-based approximation)
    const computeRange = (lvl, c) => {
      const baseLow = Math.max(0, lvl * 4 - 18);
      const chcMult = { I: 1.0, II: 1.18, III: 1.35, IV: 1.52, V: 1.70, VI: 1.90 };
      const lo = Math.round(baseLow * chcMult[c]);
      const hi = Math.round(lo * 1.25);
      return { lo, hi };
    };
    const range = computeRange(finalLevel, chc);

    const toggle = (which, code) => {
      const setter = which === 'enc' ? setEncs : setReds;
      const arr = which === 'enc' ? encs : reds;
      setter(arr.map(x => x.code === code ? { ...x, on: !x.on } : x));
    };

    return (
      <div>
        <div style={{ display: 'flex', gap: 8, marginBottom: 12, alignItems: 'center', flexWrap: 'wrap' }}>
          <span style={{ fontSize: 10, fontWeight: 700, color: T.color.text.tertiary, textTransform: 'uppercase', letterSpacing: '0.08em' }}>Defendant</span>
          <select value={selectedId} onChange={e => setSelectedId(e.target.value)}
            style={{ padding: '5px 10px', fontSize: 12, borderRadius: 5, border: `1px solid ${T.color.border.light}`, background: T.color.bg.card, color: T.color.text.primary, fontFamily: T.font.family, cursor: 'pointer', minWidth: 280 }}>
            {candidates.map(d => <option key={d.id} value={d.id}>{d.name} — {d.leadCount}</option>)}
          </select>
        </div>

        <div style={{ display: 'grid', gridTemplateColumns: '1fr 320px', gap: 12, alignItems: 'start' }}>
          {/* Calculator */}
          <div style={cj.card}>
            <div style={cj.cardH}>
              <span>USSG calculator — {def?.name}</span>
              <span style={{ fontSize: 10, color: T.color.text.tertiary, fontWeight: 500, textTransform: 'none' }}>
                Click rows to toggle
              </span>
            </div>
            <div style={{ padding: 16 }}>
              <div style={{ fontSize: 10, fontWeight: 700, color: emerald, textTransform: 'uppercase', letterSpacing: '0.06em', marginBottom: 6 }}>
                Base offense level
              </div>
              <div style={{ padding: '8px 12px', background: emerald + '0F', borderRadius: 6, fontSize: 13, fontWeight: 700, color: emerald, marginBottom: 14, fontFamily: T.font.mono }}>
                Level {initial.base}
              </div>

              <div style={{ fontSize: 10, fontWeight: 700, color: crit, textTransform: 'uppercase', letterSpacing: '0.06em', marginBottom: 6 }}>
                Specific offense + role enhancements
              </div>
              {encs.map(e => (
                <div key={e.code} onClick={() => toggle('enc', e.code)}
                  style={{ padding: '8px 12px', marginBottom: 4, borderRadius: 6, cursor: 'pointer',
                    background: e.on ? crit + '0E' : 'transparent',
                    border: `1px solid ${e.on ? crit + '40' : T.color.border.light}`,
                    display: 'flex', alignItems: 'center', gap: 10,
                    transition: 'all 120ms' }}>
                  <input type="checkbox" checked={e.on} readOnly style={{ accentColor: crit, cursor: 'pointer' }} />
                  <div style={{ flex: 1 }}>
                    <div style={{ fontSize: 11, fontWeight: 600, color: T.color.text.primary }}>{e.label}</div>
                    <div style={{ fontSize: 9, color: T.color.text.tertiary, fontFamily: T.font.mono }}>
                      {e.code}{e.contested ? ' · contested' : ''}
                    </div>
                  </div>
                  <span style={{ fontFamily: T.font.mono, fontWeight: 700, color: crit, fontSize: 12 }}>+{e.add}</span>
                </div>
              ))}

              <div style={{ fontSize: 10, fontWeight: 700, color: emerald, textTransform: 'uppercase', letterSpacing: '0.06em', marginTop: 14, marginBottom: 6 }}>
                Reductions / departures
              </div>
              {reds.map(r => (
                <div key={r.code} onClick={() => toggle('red', r.code)}
                  style={{ padding: '8px 12px', marginBottom: 4, borderRadius: 6, cursor: 'pointer',
                    background: r.on ? emerald + '0E' : 'transparent',
                    border: `1px solid ${r.on ? emerald + '40' : T.color.border.light}`,
                    display: 'flex', alignItems: 'center', gap: 10 }}>
                  <input type="checkbox" checked={r.on} readOnly style={{ accentColor: emerald, cursor: 'pointer' }} />
                  <div style={{ flex: 1 }}>
                    <div style={{ fontSize: 11, fontWeight: 600, color: T.color.text.primary }}>{r.label}</div>
                    <div style={{ fontSize: 9, color: T.color.text.tertiary, fontFamily: T.font.mono }}>
                      {r.code}{r.conditional ? ` · ${r.conditional}` : ''}
                    </div>
                  </div>
                  <span style={{ fontFamily: T.font.mono, fontWeight: 700, color: emerald, fontSize: 12 }}>−{r.sub}</span>
                </div>
              ))}

              {/* Criminal history */}
              <div style={{ fontSize: 10, fontWeight: 700, color: cobalt, textTransform: 'uppercase', letterSpacing: '0.06em', marginTop: 14, marginBottom: 6 }}>
                Criminal history category
              </div>
              <div style={{ display: 'flex', gap: 4 }}>
                {['I','II','III','IV','V','VI'].map(c => (
                  <button key={c} onClick={() => setChc(c)}
                    style={{ flex: 1, padding: '8px 4px', borderRadius: 5,
                      border: `1px solid ${chc === c ? cobalt : T.color.border.light}`,
                      background: chc === c ? cobalt : 'transparent',
                      color: chc === c ? '#fff' : T.color.text.secondary,
                      fontSize: 12, fontWeight: 700, cursor: 'pointer', fontFamily: T.font.mono }}>
                    {c}
                  </button>
                ))}
              </div>
            </div>
          </div>

          {/* Result + 3553(a) themes */}
          <div>
            <div style={{ ...cj.card, borderTop: `4px solid ${cobalt}` }}>
              <div style={cj.cardH}><span>Result</span></div>
              <div style={{ padding: 16, textAlign: 'center' }}>
                <div style={{ fontSize: 9, fontWeight: 700, color: T.color.text.tertiary, textTransform: 'uppercase', letterSpacing: '0.1em' }}>
                  Final offense level
                </div>
                <div style={{ fontSize: 56, fontWeight: 800, color: cobalt, lineHeight: 1, fontFamily: T.font.mono, letterSpacing: '-0.04em' }}>
                  {finalLevel}
                </div>
                <div style={{ display: 'flex', justifyContent: 'center', gap: 16, marginTop: 8, fontSize: 11 }}>
                  <span><span style={{ color: T.color.text.tertiary }}>Base </span>{initial.base}</span>
                  <span style={{ color: crit }}>+ {totalAdd}</span>
                  <span style={{ color: emerald }}>− {totalSub}</span>
                </div>
                <div style={{ marginTop: 16, padding: '12px 16px', background: cobalt + '0F', borderRadius: 6 }}>
                  <div style={{ fontSize: 9, fontWeight: 700, color: cobalt, textTransform: 'uppercase', letterSpacing: '0.06em' }}>
                    Guideline range (CHC {chc})
                  </div>
                  <div style={{ fontSize: 22, fontWeight: 700, color: cobalt, fontFamily: T.font.mono, marginTop: 4 }}>
                    {range.lo}–{range.hi} mo
                  </div>
                </div>
              </div>
            </div>

            <div style={{ ...cj.card, marginBottom: 0, borderLeft: `3px solid ${violet}` }}>
              <div style={{ ...cj.cardH, color: violet }}>§ 3553(a) variance themes</div>
              <div style={{ padding: 12 }}>
                {initial.themes.map((t, i) => (
                  <div key={i} style={{ display: 'flex', alignItems: 'center', gap: 8, padding: '6px 0',
                    borderBottom: i < initial.themes.length - 1 ? `1px solid ${T.color.border.light}` : 'none' }}>
                    <span style={{ width: 6, height: 6, borderRadius: '50%', background: violet, flexShrink: 0 }} />
                    <span style={{ fontSize: 11, color: T.color.text.primary }}>{t}</span>
                  </div>
                ))}
                <button style={{ ...cj.btnPrimary, width: '100%', marginTop: 12 }}
                  onClick={() => flash('success', `Variance memo drafted for ${def?.name}`)}>
                  Draft variance memo
                </button>
              </div>
            </div>
          </div>
        </div>
      </div>
    );
  }

  // ─────────────────────────────────────────────────────────────────────────
  // 4) CRIMMIGRATION / PADILLA ADVISAL
  // ─────────────────────────────────────────────────────────────────────────
  function CriminalCrimmigration({ data }) {
    const cj = window.__cj;
    const nonCitizens = data.defendants.filter(d => IMMIG[d.id] && IMMIG[d.id].risk !== 'none');
    const severe = nonCitizens.filter(d => IMMIG[d.id].risk === 'severe').length;
    const advised = nonCitizens.length;

    const riskColor = (r) => r === 'severe' ? crit : r === 'high' ? orange : r === 'unknown' ? amber : T.color.text.tertiary;

    return (
      <div>
        {/* Padilla v. Kentucky banner */}
        <div style={{ ...cj.card, marginBottom: 12, borderLeft: `3px solid ${violet}` }}>
          <div style={{ padding: '10px 16px', background: violet + '0E', fontSize: 12, color: T.color.text.secondary, lineHeight: 1.5 }}>
            <span style={{ fontSize: 10, fontWeight: 700, color: violet, textTransform: 'uppercase', letterSpacing: '0.06em', marginRight: 8 }}>Padilla v. Kentucky · 559 U.S. 356 (2010)</span>
            Counsel must advise non-citizen clients of <b>specific</b> deportation consequences before any plea. Failure to do so is ineffective assistance per Sixth Amendment. <i>Aggravated felony</i> determinations under 8 U.S.C. § 1101(a)(43) trigger mandatory removal, ineligibility for cancellation, and permanent bars to relief. Categorical and modified-categorical analysis controls.
          </div>
        </div>

        <div style={{ display: 'flex', gap: 8, marginBottom: 12, flexWrap: 'wrap' }}>
          <div style={cj.stat}><span style={cj.statLabel}>Non-citizen defendants</span><span style={cj.statValue}>{nonCitizens.length}</span></div>
          <div style={cj.stat}><span style={cj.statLabel}>Severe consequences</span><span style={{ ...cj.statValue, color: crit }}>{severe}</span></div>
          <div style={cj.stat}><span style={cj.statLabel}>Aggravated felony</span><span style={{ ...cj.statValue, color: crit }}>{nonCitizens.filter(d => IMMIG[d.id].aggFelony === true).length}</span></div>
          <div style={cj.stat}><span style={cj.statLabel}>CIMT</span><span style={{ ...cj.statValue, color: orange }}>{nonCitizens.filter(d => IMMIG[d.id].cimt === true).length}</span></div>
          <div style={cj.stat}><span style={cj.statLabel}>Advised</span><span style={{ ...cj.statValue, color: emerald }}>{advised}</span></div>
        </div>

        <div style={cj.card}>
          <div style={cj.cardH}>
            <span>Per-defendant Padilla advisal</span>
            <button style={cj.btnSecondary}>+ Generate batch advisal letters</button>
          </div>
          <div style={{ padding: 12 }}>
            {nonCitizens.map(d => {
              const im = IMMIG[d.id];
              return (
                <div key={d.id} style={{ padding: '12px 14px', marginBottom: 8, borderRadius: 8,
                  background: T.color.bg.secondary,
                  borderLeft: `3px solid ${riskColor(im.risk)}` }}>
                  <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', gap: 12, marginBottom: 6 }}>
                    <div style={{ flex: 1 }}>
                      <div style={{ fontSize: 13, fontWeight: 700, color: T.color.text.primary }}>{d.name}</div>
                      <div style={{ fontSize: 11, color: T.color.text.secondary, marginTop: 2 }}>
                        {d.leadCount} · {d.matter}
                      </div>
                    </div>
                    <div style={{ display: 'flex', gap: 6 }}>
                      <span style={{ ...cj.tag, background: cobalt + '14', color: cobalt }}>{im.citizenship}</span>
                      <span style={{ ...cj.tag, background: violet + '14', color: violet }}>{im.status}</span>
                      <span style={{ ...cj.tag, background: riskColor(im.risk) + '14', color: riskColor(im.risk), fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.06em' }}>
                        {im.risk}
                      </span>
                    </div>
                  </div>
                  <div style={{ display: 'flex', gap: 14, marginTop: 8, flexWrap: 'wrap' }}>
                    {im.cimt != null && (
                      <span style={{ display: 'flex', alignItems: 'center', gap: 4, fontSize: 10 }}>
                        <span style={{ width: 8, height: 8, borderRadius: '50%',
                          background: im.cimt ? crit : emerald }} />
                        <span style={{ color: T.color.text.secondary }}>CIMT: <b>{im.cimt ? 'Yes' : 'No'}</b></span>
                      </span>
                    )}
                    {im.aggFelony != null && (
                      <span style={{ display: 'flex', alignItems: 'center', gap: 4, fontSize: 10 }}>
                        <span style={{ width: 8, height: 8, borderRadius: '50%',
                          background: im.aggFelony ? crit : emerald }} />
                        <span style={{ color: T.color.text.secondary }}>Aggravated felony: <b>{im.aggFelony ? 'Yes' : 'No'}</b></span>
                      </span>
                    )}
                    {im.controlledSub != null && (
                      <span style={{ display: 'flex', alignItems: 'center', gap: 4, fontSize: 10 }}>
                        <span style={{ width: 8, height: 8, borderRadius: '50%',
                          background: im.controlledSub ? crit : emerald }} />
                        <span style={{ color: T.color.text.secondary }}>Controlled sub.: <b>{im.controlledSub ? 'Yes' : 'No'}</b></span>
                      </span>
                    )}
                  </div>
                  {im.note && (
                    <div style={{ marginTop: 10, padding: '8px 10px', borderRadius: 4,
                      background: '#fff', border: `1px solid ${T.color.border.light}`,
                      fontSize: 11, color: T.color.text.secondary, lineHeight: 1.5 }}>
                      <span style={{ fontWeight: 700, color: riskColor(im.risk) }}>⚠ </span>{im.note}
                    </div>
                  )}
                  <div style={{ display: 'flex', gap: 6, marginTop: 10 }}>
                    <button style={{ ...cj.btnSecondary, fontSize: 10 }}
                      onClick={() => flash('success', `Padilla advisal letter generated for ${d.name}`)}>
                      Generate advisal letter
                    </button>
                    <button style={{ ...cj.btnSecondary, fontSize: 10 }}
                      onClick={() => flash('info', `ICE detainer check queued — ${d.name}`)}>
                      Check ICE detainer
                    </button>
                    <button style={{ ...cj.btnSecondary, fontSize: 10 }}
                      onClick={() => flash('info', `Categorical analysis: ${d.leadCount}`)}>
                      Categorical analysis
                    </button>
                  </div>
                </div>
              );
            })}
          </div>
        </div>
      </div>
    );
  }

  // ─────────────────────────────────────────────────────────────────────────
  // DETAIL DRAWER — click a defendant row to see full record
  // ─────────────────────────────────────────────────────────────────────────
  function CriminalDefendantDrawer({ defendant, data, onClose }) {
    if (!defendant) return null;
    const cj = window.__cj;
    const d = defendant;
    const charges = data.charges.filter(c => c.defendantId === d.id);
    const motions = data.motions.filter(m => m.defendantId === d.id);
    const hearings = data.hearings.filter(h => h.defendant === d.name).sort((a,b) => a.date.localeCompare(b.date));
    const sup = SUPPRESSION.filter(s => s.defendantId === d.id);
    const sta = STA[d.id];
    const im = IMMIG[d.id];
    const restitution = RESTITUTION.find(r => r.defendantId === d.id);
    const violations = BAIL_VIOLATIONS.filter(v => v.defendantId === d.id);
    const sentencing = data.sentencing.find(s => s.defendantId === d.id);
    const ussg = USSG[d.id];

    return (
      <div onClick={onClose}
        style={{ position: 'fixed', inset: 0, background: 'rgba(10,22,40,0.5)', zIndex: 999,
          display: 'flex', justifyContent: 'flex-end' }}>
        <div onClick={(e) => e.stopPropagation()}
          style={{ width: 720, maxWidth: '95vw', height: '100vh', background: T.color.bg.primary,
            overflow: 'auto', boxShadow: '-8px 0 32px rgba(0,0,0,0.2)' }}>
          {/* Header */}
          <div style={{ padding: '16px 24px', background: T.color.bg.card,
            borderBottom: `1px solid ${T.color.border.light}`, position: 'sticky', top: 0, zIndex: 1 }}>
            <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', gap: 12 }}>
              <div>
                <div style={{ fontSize: 10, color: T.color.text.tertiary, fontFamily: T.font.mono, fontWeight: 700, letterSpacing: '0.06em' }}>
                  {d.id} · {d.docket}
                </div>
                <div style={{ fontSize: 18, fontWeight: 700, color: T.color.text.primary, marginTop: 2 }}>{d.name}</div>
                <div style={{ fontSize: 12, color: T.color.text.secondary, marginTop: 4 }}>
                  {d.matter} · {d.court} · {d.judge}
                </div>
              </div>
              <button onClick={onClose} style={{ background: 'none', border: 'none', cursor: 'pointer',
                fontSize: 18, color: T.color.text.tertiary }}>×</button>
            </div>
            <div style={{ display: 'flex', gap: 6, marginTop: 10, flexWrap: 'wrap' }}>
              <span style={{ ...cj.tag, background: cj.stageColor(d.stage).bg, color: cj.stageColor(d.stage).color }}>{d.stage}</span>
              <span style={{ ...cj.tag, background: cj.bailColor(d.bail).bg, color: cj.bailColor(d.bail).color }}>{d.bail}</span>
              <span style={{ ...cj.tag, background: cobalt + '14', color: cobalt }}>{charges.length || d.charges} counts</span>
              <span style={{ ...cj.tag, background: burgundy + '14', color: burgundy }}>USSG {d.guideRange}</span>
              {d.coop && <span style={{ ...cj.tag, background: teal + '14', color: teal }}>cooperator</span>}
              {im && im.risk !== 'none' && <span style={{ ...cj.tag, background: violet + '14', color: violet }}>{im.citizenship} · {im.risk} risk</span>}
            </div>
          </div>

          {/* Body */}
          <div style={{ padding: '16px 24px' }}>
            {/* Speedy Trial card */}
            {sta && sta.staDeadline && (
              <div style={{ ...cj.card, borderLeft: `3px solid ${cobalt}` }}>
                <div style={{ ...cj.cardH, color: cobalt }}>Speedy Trial Act § 3161</div>
                <div style={{ padding: 12, display: 'grid', gridTemplateColumns: 'repeat(4, 1fr)', gap: 8 }}>
                  <div><div style={{ fontSize: 9, color: T.color.text.tertiary, textTransform: 'uppercase', letterSpacing: '0.06em' }}>Indictment</div><div style={{ fontSize: 12, fontWeight: 700, fontFamily: T.font.mono }}>{fmtDate(sta.indictmentDate)}</div></div>
                  <div><div style={{ fontSize: 9, color: T.color.text.tertiary, textTransform: 'uppercase', letterSpacing: '0.06em' }}>Raw deadline</div><div style={{ fontSize: 12, fontWeight: 700, fontFamily: T.font.mono }}>{fmtDate(sta.staDeadline)}</div></div>
                  <div><div style={{ fontSize: 9, color: T.color.text.tertiary, textTransform: 'uppercase', letterSpacing: '0.06em' }}>Excluded</div><div style={{ fontSize: 12, fontWeight: 700, fontFamily: T.font.mono, color: cobalt }}>+{sta.excludedDays}d</div></div>
                  <div><div style={{ fontSize: 9, color: T.color.text.tertiary, textTransform: 'uppercase', letterSpacing: '0.06em' }}>Status</div><div style={{ fontSize: 11, fontWeight: 600 }}>{sta.status}</div></div>
                </div>
              </div>
            )}

            {/* Charges card */}
            {charges.length > 0 && (
              <div style={cj.card}>
                <div style={cj.cardH}>Charges · {charges.length}</div>
                <table style={{ width: '100%', borderCollapse: 'collapse' }}>
                  <thead style={{ background: T.color.bg.secondary }}>
                    <tr>
                      <th style={cj.th}>Ct.</th><th style={cj.th}>Statute</th><th style={cj.th}>Title</th>
                      <th style={cj.th}>Class</th><th style={{ ...cj.th, textAlign: 'right' }}>Stat max</th>
                      <th style={{ ...cj.th, textAlign: 'right' }}>Mand min</th>
                    </tr>
                  </thead>
                  <tbody>
                    {charges.map(c => (
                      <tr key={c.id}>
                        <td style={{ ...cj.td, fontFamily: T.font.mono, fontWeight: 700 }}>{c.count}</td>
                        <td style={{ ...cj.td, fontFamily: T.font.mono, fontSize: 11 }}>{c.statute}</td>
                        <td style={{ ...cj.td, fontSize: 11 }}>{c.title}</td>
                        <td style={cj.td}><span style={{ ...cj.tag, background: cj.classColor(c.class).bg, color: cj.classColor(c.class).color }}>{c.class}</span></td>
                        <td style={{ ...cj.td, textAlign: 'right', fontFamily: T.font.mono, fontWeight: 700 }}>{c.statMax === 'life' ? 'life' : `${c.statMax}mo`}</td>
                        <td style={{ ...cj.td, textAlign: 'right', fontFamily: T.font.mono, fontWeight: 700, color: c.mandMin > 0 ? crit : T.color.text.tertiary }}>{c.mandMin > 0 ? `${c.mandMin}mo` : '—'}</td>
                      </tr>
                    ))}
                  </tbody>
                </table>
              </div>
            )}

            {/* Suppression card */}
            {sup.length > 0 && (
              <div style={{ ...cj.card, borderLeft: `3px solid ${burgundy}` }}>
                <div style={{ ...cj.cardH, color: burgundy }}>Suppression grounds · {sup.length}</div>
                <div style={{ padding: 12 }}>
                  {sup.map((s, i) => (
                    <div key={i} style={{ padding: '8px 10px', marginBottom: 6, borderRadius: 4,
                      background: T.color.bg.secondary, fontSize: 11 }}>
                      <span style={{ ...cj.tag, background: cobalt + '14', color: cobalt, marginRight: 6 }}>{s.amend}</span>
                      <span style={{ fontWeight: 600 }}>{s.ground}</span>
                      <span style={{ marginLeft: 8, fontSize: 10, color: s.strength === 'strong' ? emerald : s.strength === 'weak' ? crit : amber }}>
                        ({s.strength})
                      </span>
                      <div style={{ fontSize: 10, color: T.color.text.tertiary, marginTop: 2 }}>{s.status}</div>
                    </div>
                  ))}
                </div>
              </div>
            )}

            {/* Motions */}
            {motions.length > 0 && (
              <div style={cj.card}>
                <div style={cj.cardH}>Motions · {motions.length}</div>
                <div style={{ padding: 12 }}>
                  {motions.map(m => (
                    <div key={m.id} style={{ padding: '6px 0', fontSize: 11, borderBottom: `1px solid ${T.color.border.light}` }}>
                      <div style={{ fontWeight: 600 }}>{m.type}</div>
                      <div style={{ color: T.color.text.tertiary, fontFamily: T.font.mono, fontSize: 10 }}>
                        filed {fmtDate(m.filed)} · {m.status}
                      </div>
                    </div>
                  ))}
                </div>
              </div>
            )}

            {/* Hearings */}
            {hearings.length > 0 && (
              <div style={cj.card}>
                <div style={cj.cardH}>Hearings · next {hearings.length}</div>
                <div style={{ padding: 12 }}>
                  {hearings.slice(0, 5).map(h => (
                    <div key={h.id} style={{ padding: '6px 0', fontSize: 11, borderBottom: `1px solid ${T.color.border.light}` }}>
                      <div style={{ display: 'flex', justifyContent: 'space-between' }}>
                        <span style={{ fontWeight: 600 }}>{h.type}</span>
                        <span style={{ fontFamily: T.font.mono, color: T.color.text.tertiary }}>{fmtDate(h.date)} {h.time}</span>
                      </div>
                      <div style={{ color: T.color.text.tertiary, fontSize: 10 }}>{h.location}</div>
                    </div>
                  ))}
                </div>
              </div>
            )}

            {/* Bail violations */}
            {violations.length > 0 && (
              <div style={{ ...cj.card, borderLeft: `3px solid ${amber}` }}>
                <div style={{ ...cj.cardH, color: amber }}>Bail violations · {violations.length}</div>
                <div style={{ padding: 12 }}>
                  {violations.map(v => (
                    <div key={v.id} style={{ padding: '6px 0', fontSize: 11, borderBottom: `1px solid ${T.color.border.light}` }}>
                      <div style={{ display: 'flex', justifyContent: 'space-between' }}>
                        <span style={{ fontWeight: 600 }}>{v.type}</span>
                        <span style={{ ...cj.tag, background: v.cured ? emerald + '14' : crit + '14', color: v.cured ? emerald : crit }}>
                          {v.cured ? 'cured' : 'open'}
                        </span>
                      </div>
                      <div style={{ color: T.color.text.tertiary, fontSize: 10 }}>{fmtDate(v.date)} · {v.detail}</div>
                      <div style={{ fontSize: 10, color: T.color.text.secondary, marginTop: 2 }}>↳ {v.action}</div>
                    </div>
                  ))}
                </div>
              </div>
            )}

            {/* Restitution */}
            {restitution && (
              <div style={cj.card}>
                <div style={cj.cardH}>Restitution / forfeiture (MVRA)</div>
                <div style={{ padding: 12, display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: 8 }}>
                  <div><div style={{ fontSize: 9, color: T.color.text.tertiary, textTransform: 'uppercase' }}>Owed</div><div style={{ fontSize: 14, fontWeight: 700, color: crit, fontFamily: T.font.mono }}>${(restitution.amount / 1e6).toFixed(1)}M</div></div>
                  <div><div style={{ fontSize: 9, color: T.color.text.tertiary, textTransform: 'uppercase' }}>Paid</div><div style={{ fontSize: 14, fontWeight: 700, color: emerald, fontFamily: T.font.mono }}>${(restitution.paid / 1e6).toFixed(1)}M</div></div>
                  <div><div style={{ fontSize: 9, color: T.color.text.tertiary, textTransform: 'uppercase' }}>Victims</div><div style={{ fontSize: 14, fontWeight: 700, fontFamily: T.font.mono }}>{restitution.victims}</div></div>
                </div>
                <div style={{ padding: '0 12px 12px', fontSize: 11, color: T.color.text.secondary }}>{restitution.asset} · {restitution.status}</div>
              </div>
            )}

            {/* Sentencing */}
            {(sentencing || ussg) && (
              <div style={{ ...cj.card, borderLeft: `3px solid ${cobalt}` }}>
                <div style={{ ...cj.cardH, color: cobalt }}>USSG sentencing posture</div>
                <div style={{ padding: 12, fontSize: 11, color: T.color.text.secondary, lineHeight: 1.6 }}>
                  Stat max <b>{d.exposure}</b> · Guideline range <b style={{ color: cobalt }}>{d.guideRange}</b>
                  {sentencing && <><br />{sentencing.govtPosition} <span style={{ color: T.color.text.tertiary }}>(govt)</span> vs. {sentencing.ourPosition} <span style={{ color: T.color.text.tertiary }}>(defense)</span></>}
                </div>
              </div>
            )}
          </div>
        </div>
      </div>
    );
  }

  // ─────────────────────────────────────────────────────────────────────────
  // RIGHT-CLICK CONTEXT FACTORY for defendant rows
  // ─────────────────────────────────────────────────────────────────────────
  function makeDefendantContextMenu(d, data, openDetail) {
    return (e) => {
      const im = IMMIG[d.id];
      const sta = STA[d.id];
      A.ContextMenu?.open(e, [
        { heading: `${d.name} · ${d.docket.split(':').slice(-1)[0]}` },
        { label: 'Open detail',                onSelect: () => openDetail(d) },
        { label: 'Open matter file',           onSelect: () => flash('info', `Open ${d.matter}`) },
        { separator: true },
        { label: 'Speedy Trial worksheet',     hint: sta ? `${sta.excludedDays}d excluded` : 'N/A',
          onSelect: () => flash('info', `STA worksheet for ${d.name}`) },
        { label: 'File suppression motion',    submenu: [
          { label: '4th Amend. (search)',      onSelect: () => flash('info', '4A suppression draft started') },
          { label: '5th Amend. (statements)',  onSelect: () => flash('info', '5A suppression draft started') },
          { label: '6th Amend. (counsel)',     onSelect: () => flash('info', '6A suppression draft started') },
          { label: 'Franks hearing motion',    onSelect: () => flash('info', 'Franks motion drafted') },
          { label: 'Title III (wiretap)',      onSelect: () => flash('info', 'Wiretap suppression drafted') },
        ]},
        { label: 'USSG calculator',            onSelect: () => flash('info', `Open USSG calculator for ${d.name}`) },
        { label: 'Padilla advisal',            disabled: !im || im.risk === 'none',
          hint: im && im.risk !== 'none' ? im.citizenship : 'US citizen',
          onSelect: () => flash('success', `Padilla letter drafted — ${d.name}`) },
        { separator: true },
        { label: 'New filing', submenu: [
          { label: 'Sentencing memorandum',    onSelect: () => flash('info', '§ 3553(a) memo drafted') },
          { label: 'Plea agreement',           onSelect: () => flash('info', 'Plea agreement template opened') },
          { label: 'Discovery letter',         onSelect: () => flash('info', 'Brady/Giglio letter drafted') },
          { label: 'Bail reconsideration',     onSelect: () => flash('info', '§ 3145(b) motion drafted') },
          { label: 'Motion to dismiss (STA)',  disabled: !sta || sta.status !== 'CRITICAL',
            onSelect: () => flash('info', 'STA dismissal motion drafted') },
        ]},
        { label: 'Schedule', submenu: [
          { label: 'Status conference',        onSelect: () => flash('info', 'Status conference requested') },
          { label: 'Plea conference',          onSelect: () => flash('info', 'Plea conference scheduled') },
          { label: 'Trial-prep meeting',       onSelect: () => flash('info', 'Trial-prep meeting scheduled') },
          { label: 'Client interview',         onSelect: () => flash('info', `Interview scheduled — ${d.name}`) },
        ]},
        { separator: true },
        { label: 'Add note to file',           onSelect: () => flash('info', 'Note editor opened') },
        { label: 'Check ICE detainer',         disabled: !im || im.risk === 'none',
          onSelect: () => flash('warn', `ICE detainer check queued — ${d.name}`) },
        { label: 'Conflict check',             onSelect: () => flash('info', `Conflict check run — ${d.name}`) },
        { separator: true },
        { label: 'Copy docket',                onSelect: () => { try { navigator.clipboard?.writeText(d.docket); A.toast?.({ kind: 'success', message: `Copied ${d.docket}` }); } catch {} } },
        { label: 'Copy defendant ID',          onSelect: () => { try { navigator.clipboard?.writeText(d.id); A.toast?.({ kind: 'success', message: `Copied ${d.id}` }); } catch {} } },
      ]);
    };
  }

  // ─────────────────────────────────────────────────────────────────────────
  // DASHBOARD ALERT RIBBON — STA + filing deadlines
  // ─────────────────────────────────────────────────────────────────────────
  function CriminalDashboardAlerts({ data }) {
    const cj = window.__cj;
    const today = new Date('2026-04-26');
    const alerts = [];

    // STA at risk
    Object.entries(STA).forEach(([id, sta]) => {
      if (!sta.staDeadline) return;
      const adjusted = new Date(sta.staDeadline);
      adjusted.setDate(adjusted.getDate() + sta.excludedDays);
      const daysLeft = daysBetween(adjusted, today);
      if (daysLeft >= 0 && daysLeft < 30) {
        const def = data.defendants.find(d => d.id === id);
        alerts.push({
          severity: daysLeft < 14 ? 'critical' : 'warning',
          icon: '⏰',
          title: `STA ${daysLeft}d`,
          text: `${def?.name} — Speedy Trial deadline in ${daysLeft} days`,
        });
      }
    });

    // Hearings next 7 days
    data.hearings.filter(h => {
      const dt = new Date(h.date);
      const days = daysBetween(dt, today);
      return days >= 0 && days <= 7;
    }).forEach(h => {
      alerts.push({
        severity: h.urgency === 'critical' ? 'critical' : 'warning',
        icon: 'fly',
        title: `${daysBetween(new Date(h.date), today)}d`,
        text: `${h.type} — ${h.defendant} (${h.court})`,
      });
    });

    if (alerts.length === 0) return null;

    return (
      <div style={{ marginBottom: 12 }}>
        <div style={{ display: 'flex', gap: 6, flexWrap: 'wrap' }}>
          {alerts.slice(0, 8).map((a, i) => (
            <div key={i} style={{
              padding: '6px 10px', borderRadius: 6,
              background: a.severity === 'critical' ? crit + '0E' : amber + '0E',
              border: `1px solid ${a.severity === 'critical' ? crit + '40' : amber + '40'}`,
              fontSize: 11, color: T.color.text.primary,
              display: 'flex', alignItems: 'center', gap: 6, maxWidth: 360,
            }}>
              <span style={{ fontSize: 11, fontWeight: 700,
                color: a.severity === 'critical' ? crit : amber,
                fontFamily: T.font.mono, minWidth: 36 }}>{a.title}</span>
              <span style={{ overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
                {a.text}
              </span>
            </div>
          ))}
          {alerts.length > 8 && (
            <span style={{ alignSelf: 'center', fontSize: 11, color: T.color.text.tertiary, padding: '0 8px' }}>
              + {alerts.length - 8} more
            </span>
          )}
        </div>
      </div>
    );
  }

  // Expose
  window.CriminalSpeedyTrial = CriminalSpeedyTrial;
  window.CriminalSuppression = CriminalSuppression;
  window.CriminalUSSG = CriminalUSSG;
  window.CriminalCrimmigration = CriminalCrimmigration;
  window.CriminalDefendantDrawer = CriminalDefendantDrawer;
  window.CriminalDashboardAlerts = CriminalDashboardAlerts;
  window.makeCriminalDefendantContextMenu = makeDefendantContextMenu;
})();
