const { useState, useMemo } = React;
const T = window.ArbiterTokens;

// ── Shared case styles ──
const cs = {
  container: { flex: 1, overflow: 'auto', background: T.color.bg.primary },
  hero: { padding: '20px 24px 16px', borderBottom: `1px solid ${T.color.border.light}`, background: T.color.bg.card },
  heroTop: { display: 'flex', alignItems: 'flex-start', justifyContent: 'space-between', marginBottom: '12px' },
  matterName: { fontSize: T.font.size.xl, fontWeight: T.font.weight.bold, color: T.color.text.primary, letterSpacing: T.font.tracking.tight },
  matterId: { fontSize: T.font.size.sm, color: T.color.text.tertiary, marginTop: '2px' },
  metaRow: { display: 'flex', gap: '24px', fontSize: T.font.size.sm },
  metaItem: { display: 'flex', flexDirection: 'column', gap: '2px' },
  metaLabel: { fontSize: T.font.size.xs, color: T.color.text.tertiary, textTransform: 'uppercase', letterSpacing: T.font.tracking.caps, fontWeight: T.font.weight.medium },
  metaValue: { fontSize: T.font.size.base, color: T.color.text.primary, fontWeight: T.font.weight.medium },
  tabs: { display: 'flex', gap: '0', borderBottom: `1px solid ${T.color.border.light}`, background: T.color.bg.card, padding: '0 24px' },
  tab: { padding: '10px 16px', fontSize: T.font.size.sm, fontWeight: T.font.weight.medium, color: T.color.text.tertiary, cursor: 'pointer', border: 'none', background: 'none', borderBottom: '2px solid transparent', fontFamily: T.font.family, transition: 'all 0.15s', marginBottom: '-1px' },
  tabActive: { color: T.color.text.primary, borderBottomColor: T.color.navy800 },
  body2col: { padding: '20px 24px', display: 'grid', gridTemplateColumns: '1fr 340px', gap: '20px' },
  body1col: { padding: '20px 24px' },
  sec: { background: T.color.bg.card, border: `1px solid ${T.color.border.light}`, borderRadius: T.radius.lg, overflow: 'hidden', marginBottom: '16px' },
  secH: { padding: '12px 16px', borderBottom: `1px solid ${T.color.border.light}`, fontSize: T.font.size.sm, fontWeight: T.font.weight.semibold, color: T.color.text.primary, display: 'flex', alignItems: 'center', justifyContent: 'space-between' },
  row: { padding: '8px 16px', borderBottom: `1px solid ${T.color.border.light}`, display: 'flex', alignItems: 'center', gap: '10px', fontSize: T.font.size.sm, cursor: 'pointer', transition: 'background 0.1s' },
  aiBox: { padding: '12px 16px', background: 'rgba(37,99,235,0.03)', borderBottom: `1px solid ${T.color.border.light}`, fontSize: T.font.size.sm, color: T.color.text.secondary, lineHeight: 1.5 },
  aiLabel: { fontSize: T.font.size.xs, fontWeight: T.font.weight.semibold, color: T.color.accent.blue, textTransform: 'uppercase', letterSpacing: T.font.tracking.caps, marginBottom: '4px' },
  tlItem: { padding: '10px 16px', borderBottom: `1px solid ${T.color.border.light}`, display: 'flex', gap: '12px', position: 'relative' },
  tlLine: { position: 'absolute', left: '23px', top: '28px', bottom: '0', width: '1px', background: T.color.border.light },
  tlDot: { width: '8px', height: '8px', borderRadius: '50%', border: `2px solid ${T.color.navy400}`, background: T.color.bg.card, marginTop: '4px', flexShrink: 0, zIndex: 1 },
  docIcon: { width: '28px', height: '28px', borderRadius: T.radius.sm, background: T.color.bg.secondary, display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: '12px', color: T.color.text.tertiary, flexShrink: 0 },
  avatar: { width: '30px', height: '30px', borderRadius: '50%', display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: T.font.size.xs, fontWeight: T.font.weight.semibold, flexShrink: 0 },
  barTrack: { flex: 1, height: '8px', borderRadius: '4px', background: T.color.border.light },
  barFill: { height: '100%', borderRadius: '4px', transition: 'width 0.5s ease' },
  filterBtn: { padding: '4px 10px', borderRadius: T.radius.md, border: `1px solid ${T.color.border.light}`, background: T.color.bg.card, fontSize: T.font.size.xs, fontWeight: T.font.weight.medium, color: T.color.text.secondary, cursor: 'pointer', fontFamily: T.font.family },
  filterBtnActive: { background: T.color.navy800, color: T.color.ivory100, borderColor: T.color.navy700 },
  extBadge: { display: 'inline-flex', alignItems: 'center', justifyContent: 'center', fontSize: '9px', fontWeight: T.font.weight.bold, letterSpacing: '0.04em', padding: '2px 6px', borderRadius: '3px' },
};

function fmtCur(val) {
  if (val >= 1000000) return '$' + (val / 1000000).toFixed(1) + 'M';
  if (val >= 1000) return '$' + (val / 1000).toFixed(0) + 'K';
  return '$' + val;
}
function extColor(ext) {
  return { PDF: '#C23030', DOCX: '#2563EB', XLSX: '#1B7A4A', ZIP: '#6B21A8', MSG: '#B8860B' }[ext] || T.color.text.tertiary;
}

// ── Per-matter data generator (seeded from matter props) ──
function getMatterData(m) {
  const id = m.id;
  const seed = parseInt(id.replace(/\D/g, '')) || 312;

  // Documents
  const docSets = {
    'M-2024-0312': [
      { name: 'Opposition to MSJ — Draft v3', type: 'Brief', size: '2.4 MB', modified: 'Apr 18', author: 'M. Kirkland', ext: 'DOCX', status: 'Draft' },
      { name: 'Harrington Deposition Transcript', type: 'Transcript', size: '847 KB', modified: 'Apr 15', author: 'Court Reporter', ext: 'PDF', status: 'Final' },
      { name: 'Economic Damages Report — Mitchell', type: 'Expert Report', size: '5.1 MB', modified: 'Apr 02', author: 'S. Mitchell', ext: 'PDF', status: 'Final' },
      { name: 'Financial Records 2020–2024', type: 'Production', size: '142 MB', modified: 'Apr 12', author: 'Meridian Holdings', ext: 'ZIP', status: 'Received' },
      { name: 'Interrogatory Responses — Set 2', type: 'Discovery', size: '1.8 MB', modified: 'Apr 18', author: 'L. Torres', ext: 'DOCX', status: 'Filed' },
      { name: 'Privilege Log — Updated', type: 'Log', size: '320 KB', modified: 'Apr 16', author: 'J. Park', ext: 'XLSX', status: 'Draft' },
      { name: 'Amended Complaint', type: 'Pleading', size: '1.1 MB', modified: 'Mar 15', author: 'M. Kirkland', ext: 'PDF', status: 'Filed' },
      { name: 'Defendant Answer & Counterclaims', type: 'Pleading', size: '980 KB', modified: 'Feb 28', author: 'Defense Counsel', ext: 'PDF', status: 'Received' },
      { name: 'Motion to Compel — Granted', type: 'Motion', size: '2.1 MB', modified: 'Apr 10', author: 'M. Kirkland', ext: 'PDF', status: 'Filed' },
      { name: 'Case Management Order', type: 'Order', size: '340 KB', modified: 'Jan 20', author: 'Judge Reynolds', ext: 'PDF', status: 'Final' },
      { name: 'Expert CV — Dr. Sarah Mitchell', type: 'Expert Report', size: '120 KB', modified: 'Mar 28', author: 'S. Mitchell', ext: 'PDF', status: 'Final' },
      { name: 'Deposition Notice — Controller Davis', type: 'Notice', size: '85 KB', modified: 'Apr 19', author: 'L. Torres', ext: 'DOCX', status: 'Draft' },
    ],
    // ── U.S. v. Harrell — Wire Fraud ──
    'M-2025-0384': [
      { name: 'Indictment — 8 counts (§§ 1343 / 1957 / 1028A)', type: 'Pleading', size: '2.1 MB', modified: 'Oct 28 2025', author: 'USAO SDNY', ext: 'PDF', status: 'Filed' },
      { name: 'Bond Application & Order — $2M, 3 cosigners', type: 'Order', size: '420 KB', modified: 'Nov 02 2025', author: 'Judge Hellerstein', ext: 'PDF', status: 'Final' },
      { name: 'Grand Jury Transcripts (redacted)', type: 'Transcript', size: '3.4 MB', modified: 'Sep 15 2025', author: 'Court Reporter', ext: 'PDF', status: 'Final' },
      { name: 'Brady Request — 14 categories', type: 'Motion', size: '680 KB', modified: 'Jan 18 2026', author: 'M. Kirkland', ext: 'DOCX', status: 'Filed' },
      { name: 'Brady Production — Tranche 1 (8 of 14 302s)', type: 'Production', size: '86 MB', modified: 'Feb 14 2026', author: 'USAO SDNY', ext: 'ZIP', status: 'Received' },
      { name: 'Motion to Compel — 6 withheld 302s', type: 'Motion', size: '1.2 MB', modified: 'Feb 24 2026', author: 'L. Torres', ext: 'DOCX', status: 'Filed' },
      { name: 'Motion to Sever Count 4 from Counts 1-3', type: 'Motion', size: '920 KB', modified: 'Mar 14 2026', author: 'L. Torres', ext: 'DOCX', status: 'Filed' },
      { name: 'Government\'s Opposition to Sever', type: 'Brief', size: '1.4 MB', modified: 'Apr 02 2026', author: 'USAO SDNY', ext: 'PDF', status: 'Received' },
      { name: 'Order Denying Motion to Sever', type: 'Order', size: '340 KB', modified: 'Apr 14 2026', author: 'Judge Hellerstein', ext: 'PDF', status: 'Final' },
      { name: 'Plea Offer Letter — Counts 1 & 4 accepted', type: 'Correspondence', size: '210 KB', modified: 'Mar 28 2026', author: 'AUSA Chen', ext: 'PDF', status: 'Received' },
      { name: 'Cooperator Proffers — CW-2 Jensen, CW-3 Doe 1', type: 'Discovery', size: '4.8 MB', modified: 'Jan 20 2026', author: 'USAO SDNY', ext: 'PDF', status: 'Received' },
      { name: 'Expert Damages Report — Dr. S. Mitchell', type: 'Expert Report', size: '5.6 MB', modified: 'Mar 22 2026', author: 'S. Mitchell', ext: 'PDF', status: 'Final' },
    ],
    // ── U.S. v. Venkataraman — Health Care Fraud ──
    'M-2025-0218': [
      { name: 'Indictment — 12 counts (§ 1347 + § 1320a-7b)', type: 'Pleading', size: '2.8 MB', modified: 'Dec 08 2025', author: 'USAO EDPA', ext: 'PDF', status: 'Filed' },
      { name: 'Bond Order — Conditions of Release', type: 'Order', size: '380 KB', modified: 'Dec 18 2025', author: 'Magistrate Sitarski', ext: 'PDF', status: 'Final' },
      { name: 'Motion to Dismiss — HHS-OIG Estoppel', type: 'Motion', size: '1.8 MB', modified: 'Feb 18 2026', author: 'L. Torres', ext: 'DOCX', status: 'Filed' },
      { name: 'Motion in Limine — Exclude Patient Narrative', type: 'Motion', size: '1.1 MB', modified: 'Mar 28 2026', author: 'L. Torres', ext: 'DOCX', status: 'Filed' },
      { name: 'Brady Request — HHS-OIG Audit Letters', type: 'Motion', size: '520 KB', modified: 'Jan 28 2026', author: 'L. Torres', ext: 'DOCX', status: 'Filed' },
      { name: 'Brady Production — 2 of 5 OIG Audits', type: 'Production', size: '24 MB', modified: 'Feb 24 2026', author: 'USAO EDPA', ext: 'ZIP', status: 'Received' },
      { name: 'Government\'s Opposition to MTD', type: 'Brief', size: '1.6 MB', modified: 'Mar 20 2026', author: 'USAO EDPA', ext: 'PDF', status: 'Received' },
      { name: 'Defense Expert Report — Dr. W. McKinley', type: 'Expert Report', size: '3.8 MB', modified: 'Apr 02 2026', author: 'W. McKinley', ext: 'PDF', status: 'Final' },
      { name: 'Medicare Billing Data — Production', type: 'Production', size: '284 MB', modified: 'Jan 15 2026', author: 'USAO EDPA', ext: 'ZIP', status: 'Received' },
      { name: 'Billing Anomaly Reconciliation — 18 → 4 anomalies', type: 'Expert Workpaper', size: '1.4 MB', modified: 'Mar 30 2026', author: 'LedgerTrace', ext: 'XLSX', status: 'Draft' },
    ],
    // ── U.S. v. Chen — FCPA ──
    'M-2026-0042': [
      { name: 'Indictment — 6 counts (§§ 78dd-1 / 371 / 1956(h))', type: 'Pleading', size: '2.4 MB', modified: 'Feb 10 2026', author: 'DOJ FCPA Unit', ext: 'PDF', status: 'Filed' },
      { name: 'Detention Order — Judge Bates', type: 'Order', size: '380 KB', modified: 'Feb 24 2026', author: 'Judge Bates', ext: 'PDF', status: 'Final' },
      { name: 'Motion for Bail Review — § 3145(b)', type: 'Motion', size: '1.9 MB', modified: 'Mar 04 2026', author: 'M. Kirkland', ext: 'DOCX', status: 'Filed' },
      { name: 'Notice of Appeal — D.C. Circuit', type: 'Pleading', size: '180 KB', modified: 'Mar 22 2026', author: 'M. Kirkland', ext: 'PDF', status: 'Filed' },
      { name: 'Appellate Brief — Detention', type: 'Brief', size: '2.8 MB', modified: 'Apr 04 2026', author: 'M. Kirkland', ext: 'PDF', status: 'Filed' },
      { name: 'Brady Request — Sterling Compliance/DOJ Comms', type: 'Motion', size: '740 KB', modified: 'Mar 10 2026', author: 'L. Torres', ext: 'DOCX', status: 'Filed' },
      { name: 'Cooperator Agreement — CW-1 Ramirez', type: 'Discovery', size: '680 KB', modified: 'Mar 18 2026', author: 'USAO DDC', ext: 'PDF', status: 'Received' },
      { name: 'SEC Civil Investigative Demand', type: 'Notice', size: '240 KB', modified: 'Mar 10 2026', author: 'SEC Enforcement', ext: 'PDF', status: 'Received' },
      { name: 'Defense Memo — Foreign Officials Definition', type: 'Memo', size: '1.3 MB', modified: 'Apr 08 2026', author: 'M. Kirkland', ext: 'DOCX', status: 'Draft' },
      { name: 'Hague Evidence Convention Letter of Request', type: 'Discovery', size: '620 KB', modified: 'Apr 12 2026', author: 'L. Torres', ext: 'DOCX', status: 'Draft' },
    ],
    // ── U.S. v. Barnwell — Securities Fraud (post-verdict) ──
    'M-2025-0721': [
      { name: 'Verdict of Guilty — Counts 1-3', type: 'Order', size: '120 KB', modified: 'Mar 14 2026', author: 'Jury / Judge Engelmayer', ext: 'PDF', status: 'Final' },
      { name: 'Presentence Report (PSR) — Draft', type: 'PSR', size: '4.2 MB', modified: 'May 20 2026', author: 'USPO SDNY', ext: 'PDF', status: 'Draft' },
      { name: 'Sentencing Memorandum — § 3553(a) Variance', type: 'Brief', size: '2.4 MB', modified: 'Apr 10 2026', author: 'L. Torres', ext: 'DOCX', status: 'Draft' },
      { name: 'Motion for New Trial — Rule 33 (Brady)', type: 'Motion', size: '1.6 MB', modified: 'Mar 30 2026', author: 'L. Torres', ext: 'DOCX', status: 'Filed' },
      { name: 'SEC Parallel File Production', type: 'Production', size: '148 MB', modified: 'Nov 20 2025', author: 'SEC Enforcement', ext: 'ZIP', status: 'Received' },
      { name: 'Indictment — 5 counts securities fraud', type: 'Pleading', size: '1.8 MB', modified: 'Sep 02 2025', author: 'USAO SDNY', ext: 'PDF', status: 'Filed' },
      { name: 'Trial Transcripts — 10 court days', type: 'Transcript', size: '18 MB', modified: 'Mar 14 2026', author: 'Court Reporter', ext: 'PDF', status: 'Final' },
      { name: 'Victim Impact Statements — 8 filed', type: 'Statement', size: '1.2 MB', modified: 'Apr 18 2026', author: 'USPO', ext: 'PDF', status: 'Received' },
      { name: 'Letters of Support — 34 received', type: 'Correspondence', size: '4.8 MB', modified: 'Apr 16 2026', author: 'Various', ext: 'PDF', status: 'Compiling' },
      { name: 'Objections to PSR', type: 'Filing', size: '980 KB', modified: 'May 28 2026', author: 'L. Torres', ext: 'DOCX', status: 'Drafting' },
    ],
    // ── U.S. v. Miller — Narcotics + § 924(c) ──
    'M-2025-0302': [
      { name: 'Indictment — 3 counts (§§ 841(b)(1)(A) / 924(c) / 846)', type: 'Pleading', size: '1.2 MB', modified: 'Oct 14 2025', author: 'USAO EDNY', ext: 'PDF', status: 'Filed' },
      { name: 'Motion to Suppress — 4th Amendment', type: 'Motion', size: '1.4 MB', modified: 'Feb 20 2026', author: 'J. Park', ext: 'DOCX', status: 'Filed' },
      { name: 'Franks Hearing Motion', type: 'Motion', size: '1.8 MB', modified: 'Feb 20 2026', author: 'J. Park', ext: 'DOCX', status: 'Filed' },
      { name: 'Order Granting Franks Hearing', type: 'Order', size: '240 KB', modified: 'Apr 18 2026', author: 'Judge Matsumoto', ext: 'PDF', status: 'Final' },
      { name: 'Sentinel Investigator Report — CI-2 IA Records', type: 'Investigation', size: '3.6 MB', modified: 'Apr 10 2026', author: 'D. Sandoval (Sentinel)', ext: 'PDF', status: 'Received' },
      { name: 'Body-Camera Footage Index', type: 'Log', size: '340 KB', modified: 'Mar 10 2026', author: 'J. Park', ext: 'DOCX', status: 'Filed' },
      { name: 'Warrant Affidavit — Officer T. Reeves', type: 'Affidavit', size: '680 KB', modified: 'Aug 2025', author: 'Officer T. Reeves', ext: 'PDF', status: 'Final' },
      { name: 'Safety-Valve Proffer Letter — Draft', type: 'Correspondence', size: '210 KB', modified: 'Apr 15 2026', author: 'J. Park', ext: 'DOCX', status: 'Drafting' },
    ],
    _default: [
      { name: 'Initial Complaint', type: 'Pleading', size: '1.2 MB', modified: 'Mar 10', author: m.lead, ext: 'PDF', status: 'Filed' },
      { name: 'Discovery Request — Set 1', type: 'Discovery', size: '900 KB', modified: 'Mar 25', author: m.lead, ext: 'DOCX', status: 'Filed' },
      { name: 'Case Strategy Memo', type: 'Memo', size: '450 KB', modified: 'Apr 01', author: m.lead, ext: 'DOCX', status: 'Draft' },
      { name: 'Client Engagement Letter', type: 'Correspondence', size: '220 KB', modified: 'Feb 15', author: m.lead, ext: 'PDF', status: 'Final' },
      { name: 'Research — Key Authorities', type: 'Research', size: '1.5 MB', modified: 'Apr 08', author: m.lead, ext: 'DOCX', status: 'Draft' },
      { name: 'Privilege Log', type: 'Log', size: '280 KB', modified: 'Apr 12', author: m.lead, ext: 'XLSX', status: 'Draft' },
    ],
  };

  // Timeline
  const timelineSets = {
    'M-2024-0312': [
      { date: 'Apr 19, 2026', event: 'Deposition notice served', detail: 'Controller R. Davis — scheduled May 1', type: 'notice' },
      { date: 'Apr 18, 2026', event: 'Discovery response filed', detail: 'Responded to Defendant\'s Second Set of Interrogatories', type: 'filing' },
      { date: 'Apr 15, 2026', event: 'Deposition completed', detail: 'Witness: CFO James Harrington — 4h 23m transcript', type: 'deposition' },
      { date: 'Apr 12, 2026', event: 'Production received', detail: 'Meridian financial records 2020–2024 (142 MB, 3,200+ docs)', type: 'production' },
      { date: 'Apr 10, 2026', event: 'Motion to Compel granted', detail: 'Court ordered production of financial records 2020–2024', type: 'order' },
      { date: 'Apr 02, 2026', event: 'Expert report received', detail: 'Dr. Sarah Mitchell — Economic damages analysis ($14.2M–$18.7M)', type: 'document' },
      { date: 'Mar 28, 2026', event: 'Status conference', detail: 'Judge Reynolds set discovery cutoff for May 14', type: 'hearing' },
      { date: 'Mar 22, 2026', event: 'Motion to Compel filed', detail: 'Defendant failed to produce financial records per RFP Set 1', type: 'filing' },
      { date: 'Mar 15, 2026', event: 'Amended complaint filed', detail: 'Added breach of fiduciary duty claim (Count IV)', type: 'filing' },
      { date: 'Mar 01, 2026', event: 'Discovery served — Set 2', detail: 'Interrogatories and RFPs targeting insider transactions', type: 'filing' },
      { date: 'Feb 28, 2026', event: 'Answer & counterclaims received', detail: 'Defendant asserts business judgment rule defense', type: 'filing' },
      { date: 'Jan 20, 2026', event: 'Case management order entered', detail: 'Discovery deadline May 14, dispositive motions Jun 15, trial Jul 15', type: 'order' },
      { date: 'Jan 05, 2026', event: 'Complaint filed', detail: 'Breach of contract and fraud claims — $18M in damages sought', type: 'filing' },
    ],
    // ── Harrell ──
    'M-2025-0384': [
      { date: 'May 14, 2026', event: 'Plea decision deadline', detail: 'Judge Hellerstein requires accept/reject of plea offer or trial set', type: 'hearing' },
      { date: 'Apr 24, 2026', event: 'Pretrial status conference', detail: 'Plea deadline set May 14; court inquired re plea progress', type: 'hearing' },
      { date: 'Apr 14, 2026', event: 'Order DENYING Motion to Sever', detail: 'Rule 14 balance — joinder of § 1028A with fraud counts not unduly prejudicial', type: 'order' },
      { date: 'Apr 10, 2026', event: 'Oral argument — Motion to Compel Brady', detail: 'Argued by L. Torres; decision under submission', type: 'hearing' },
      { date: 'Apr 02, 2026', event: 'Government\'s Opposition to Sever filed', detail: 'AUSA Chen filed opposition citing Zafiro joinder framework', type: 'filing' },
      { date: 'Mar 28, 2026', event: 'Plea offer tendered', detail: 'Counts 1 & 4 accepted, 2,3,5-8 dismissed; USSG range 51-63 mo; $14.2M restitution', type: 'notice' },
      { date: 'Mar 22, 2026', event: 'Defense expert damages report', detail: 'Dr. S. Mitchell methodology paper delivered', type: 'document' },
      { date: 'Mar 14, 2026', event: 'Motion to Sever Count 4 filed', detail: '§ 1028A severance from Counts 1-3 — Rule 14 prejudicial joinder', type: 'filing' },
      { date: 'Feb 24, 2026', event: 'Motion to Compel Brady filed', detail: '6 withheld FBI 302s — Kyles materiality argument', type: 'filing' },
      { date: 'Feb 14, 2026', event: 'Brady Production Tranche 1 received', detail: '8 of 14 FBI 302s produced by USAO SDNY', type: 'production' },
      { date: 'Jan 20, 2026', event: 'Cooperator proffers received', detail: 'CW-2 Jensen (immunity) & CW-3 Doe 1 (5K1.1 + Rule 35) agreements', type: 'document' },
      { date: 'Jan 18, 2026', event: 'Brady request filed', detail: '14 categories of exculpatory + impeachment material', type: 'filing' },
      { date: 'Nov 02, 2025', event: 'Bond hearing', detail: 'Judge Hellerstein — $2M bond, 3 cosigners, passport surrendered', type: 'hearing' },
      { date: 'Oct 28, 2025', event: 'Grand jury returns indictment', detail: '8 counts — §§ 1343 (wire fraud × 2), 1957 (monetary transactions), 1028A (agg ID theft)', type: 'filing' },
      { date: 'Sep 10, 2025', event: 'Target letter received', detail: 'USAO SDNY target letter re Redstone Capital transaction', type: 'notice' },
    ],
    // ── Venkataraman ──
    'M-2025-0218': [
      { date: 'Jun 10, 2026', event: 'Trial — Day 1 (if MTD denied)', detail: 'Judge Pappert, Courtroom 6A; estimated 8 court days', type: 'hearing' },
      { date: 'Apr 28, 2026', event: 'Hearing — MTD + MIL', detail: 'Argue Motion to Dismiss + Motion in Limine on patient testimony', type: 'hearing' },
      { date: 'Apr 21, 2026', event: 'Argued Motion in Limine', detail: 'FRE 403 exclusion of patient-narrative testimony', type: 'hearing' },
      { date: 'Apr 02, 2026', event: 'Defense expert report filed', detail: 'Dr. W. McKinley — pharmacology methodology rebutting medical-necessity theory', type: 'document' },
      { date: 'Mar 28, 2026', event: 'Motion in Limine filed', detail: 'Exclude 9 alleged-victim patient witnesses — Rule 403 prejudice', type: 'filing' },
      { date: 'Mar 20, 2026', event: 'Government\'s MTD opposition filed', detail: 'AUSA opposes estoppel framework; argues OIG-audit scope limited', type: 'filing' },
      { date: 'Feb 24, 2026', event: 'Brady Production Tranche 1', detail: '2 of 5 OIG audit letters produced; 3 withheld as privileged', type: 'production' },
      { date: 'Feb 18, 2026', event: 'Motion to Dismiss filed', detail: 'HHS-OIG pre-clearance estoppel (Counts 1-6) — Heckler v. Chaney framework', type: 'filing' },
      { date: 'Jan 28, 2026', event: 'Brady request filed', detail: 'HHS-OIG audit letters 2020-2022 — finding "no material non-compliance"', type: 'filing' },
      { date: 'Jan 15, 2026', event: 'Medicare billing data received', detail: '284 MB of claims data, Jan 2019 – Sep 2024', type: 'production' },
      { date: 'Dec 18, 2025', event: 'Bond hearing', detail: 'Magistrate Sitarski — released on $500K bond + license surrender', type: 'hearing' },
      { date: 'Dec 08, 2025', event: 'Indicted', detail: '12 counts — HCF (§ 1347) + Anti-Kickback (§ 1320a-7b)', type: 'filing' },
      { date: 'Nov 14, 2025', event: 'HHS-OIG civil audit closed', detail: 'Finding: "no material non-compliance" — the basis for estoppel MTD', type: 'admin' },
      { date: 'Sep 02, 2025', event: 'Target letter received', detail: 'USAO EDPA notice of grand jury investigation', type: 'notice' },
    ],
    // ── Chen ──
    'M-2026-0042': [
      { date: 'Apr 28, 2026', event: 'D.C. Circuit oral argument', detail: 'Detention appeal before 3-judge panel', type: 'hearing' },
      { date: 'Apr 24, 2026', event: 'Status conference — Judge Bates', detail: 'Brady dispute + trial scheduling', type: 'hearing' },
      { date: 'Apr 12, 2026', event: 'Hague Evidence Convention draft', detail: 'Letter of Request prepared for APAC subsidiary records', type: 'filing' },
      { date: 'Apr 08, 2026', event: 'Foreign-Officials Definition memo', detail: 'Internal memo — Esquenazi framework applied to our facts', type: 'document' },
      { date: 'Apr 04, 2026', event: 'Appellate Brief filed — D.C. Cir.', detail: 'Opening brief on detention appeal', type: 'filing' },
      { date: 'Mar 22, 2026', event: 'Notice of Appeal', detail: 'Appeal of detention order to D.C. Circuit', type: 'filing' },
      { date: 'Mar 18, 2026', event: 'Bail review DENIED', detail: 'Judge Bates — flight risk found; bail appeal to follow', type: 'order' },
      { date: 'Mar 18, 2026', event: 'Cooperator Agreement produced', detail: 'CW-1 Ramirez full agreement + deportation-consequences memo', type: 'production' },
      { date: 'Mar 10, 2026', event: 'SEC Civil Investigative Demand', detail: 'Parallel SEC FCPA enforcement; negotiating use-immunity', type: 'notice' },
      { date: 'Mar 10, 2026', event: 'Brady request filed', detail: 'Sterling pre-charge compliance communications with DOJ', type: 'filing' },
      { date: 'Mar 04, 2026', event: 'Motion for Bail Review filed', detail: '§ 3145(b) — proposed $5M bond + 3rd-party custodian + GPS', type: 'filing' },
      { date: 'Feb 24, 2026', event: 'Detention hearing', detail: 'Judge Bates DENIED release — "no conditions adequate"', type: 'hearing' },
      { date: 'Feb 14, 2026', event: 'Arraignment', detail: 'Pleaded not guilty on 6 counts', type: 'hearing' },
      { date: 'Feb 10, 2026', event: 'Indicted', detail: '§§ 78dd-1 (×2), 371 (×2), 1956(h) (×2)', type: 'filing' },
      { date: 'Jan 22, 2026', event: 'Arrested at Dulles', detail: 'Arrested upon arrival from Shanghai on sealed warrant', type: 'notice' },
    ],
    // ── Barnwell ──
    'M-2025-0721': [
      { date: 'Jun 04, 2026', event: 'Sentencing hearing', detail: 'Judge Engelmayer, Courtroom 17C — Counts 1-3', type: 'hearing' },
      { date: 'May 28, 2026', event: 'Objections to PSR due', detail: 'Challenging § 3B1.1(a) organizer enhancement (Bonilla framework)', type: 'filing' },
      { date: 'May 20, 2026', event: 'PSR delivered', detail: 'Probation Office draft — offense level 30, CHC I, range 97-121 mo', type: 'document' },
      { date: 'Apr 28, 2026', event: 'Motion for New Trial argued', detail: 'Rule 33 — post-verdict Brady impeachment of CW-2', type: 'hearing' },
      { date: 'Apr 18, 2026', event: 'Victim impact statements received', detail: '8 statements from alleged victims filed with USPO', type: 'document' },
      { date: 'Apr 16, 2026', event: 'Letters of support compiled', detail: '34 collected; targeting 50+ for sentencing', type: 'document' },
      { date: 'Apr 10, 2026', event: 'Sentencing memo drafting', detail: '§ 3553(a) variance — age, charitable works, collateral consequences', type: 'drafting' },
      { date: 'Mar 30, 2026', event: 'Motion for New Trial filed (Rule 33)', detail: 'SEC parallel files impeach CW-2 on Q3 2022 materiality', type: 'filing' },
      { date: 'Mar 14, 2026', event: 'VERDICT — GUILTY on Counts 1-3', detail: 'Jury convicted; deliberated 2.5 days', type: 'order' },
      { date: 'Mar 02 – 14, 2026', event: 'Trial — 10 court days', detail: 'Lead: L. Torres; 3 expert witnesses called by defense', type: 'hearing' },
      { date: 'Nov 20, 2025', event: 'SEC parallel files produced', detail: 'Voluntary production from SEC files — post-verdict Brady source', type: 'production' },
      { date: 'Sep 02, 2025', event: 'Indicted', detail: '5 counts securities fraud (10b-5)', type: 'filing' },
      { date: 'Jul 14, 2025', event: 'Bond hearing', detail: '$10M bond, 5 cosigners, passport surrendered', type: 'hearing' },
      { date: 'Jun 22, 2025', event: 'SEC Wells notice received', detail: 'SEC enforcement staff recommending civil action', type: 'notice' },
    ],
    // ── Miller ──
    'M-2025-0302': [
      { date: 'May 08, 2026', event: 'Franks evidentiary hearing', detail: 'Judge Matsumoto — test Officer Reeves\'s affidavit statements', type: 'hearing' },
      { date: 'Apr 30, 2026', event: 'Suppression argument', detail: '4th Amendment + consent-search coercion', type: 'hearing' },
      { date: 'Apr 18, 2026', event: 'Franks Hearing GRANTED', detail: 'Judge Matsumoto — 3 affidavit misstatements warrant evidentiary hearing', type: 'order' },
      { date: 'Apr 15, 2026', event: 'Safety-valve proffer drafting', detail: '§ 3553(f) — backup if Franks fails', type: 'drafting' },
      { date: 'Apr 10, 2026', event: 'Sentinel investigator report', detail: '3 prior CI-2 IA investigations (2018, 2020, 2022) documented', type: 'document' },
      { date: 'Mar 10, 2026', event: 'Body-camera footage index filed', detail: 'Cross-referenced with warrant-execution narrative', type: 'filing' },
      { date: 'Feb 20, 2026', event: 'Motion to Suppress + Franks Motion filed', detail: 'J. Park — 4th Amendment + affidavit-misstatement challenges', type: 'filing' },
      { date: 'Jan 18, 2026', event: 'CI-2 records obtained', detail: 'FOIA + § 1983 request for Internal Affairs records', type: 'document' },
      { date: 'Nov 08, 2025', event: 'Bond hearing', detail: 'Magistrate Gold — $250K bond, GPS home detention', type: 'hearing' },
      { date: 'Oct 28, 2025', event: 'Arraignment', detail: 'Pleaded not guilty on 3 counts', type: 'hearing' },
      { date: 'Oct 14, 2025', event: 'Indicted', detail: '§ 841(b)(1)(A) PWID cocaine 500g+, § 924(c) firearm, § 846 conspiracy', type: 'filing' },
      { date: 'Aug 28, 2025', event: 'Arrested', detail: 'Warrant executed — 500g cocaine + firearm seized', type: 'notice' },
    ],
    _default: [
      { date: 'Apr 15, 2026', event: 'Status update', detail: 'Matter progressing as scheduled', type: 'status' },
      { date: 'Apr 01, 2026', event: 'Discovery initiated', detail: 'First set of requests served', type: 'filing' },
      { date: 'Mar 15, 2026', event: 'Initial strategy meeting', detail: 'Team aligned on approach', type: 'meeting' },
      { date: 'Feb 20, 2026', event: 'Matter opened', detail: 'Engagement confirmed', type: 'admin' },
    ],
  };

  // Research
  const researchSets = {
    'M-2024-0312': [
      { title: 'Breach of Fiduciary Duty — Delaware Standards', type: 'Memo', author: 'J. Park', date: 'Apr 16', status: 'Complete', citations: 14, summary: 'Analysis of duty of loyalty and care under Delaware law. Key cases: Stone v. Ritter, In re Caremark.' },
      { title: 'Fraud Damages Calculation — Lost Profits vs. Benefit of Bargain', type: 'Research', author: 'L. Torres', date: 'Apr 08', status: 'Complete', citations: 22, summary: 'Comparative analysis of damages theories. Recommendation: pursue both with expert testimony supporting lost profits model.' },
      { title: 'Motion to Compel — Discovery Sanctions Precedent', type: 'Brief Research', author: 'J. Park', date: 'Mar 20', status: 'Complete', citations: 8, summary: 'Supporting authority for Rule 37 sanctions. Identified 4 on-point decisions from this district.' },
      { title: 'Business Judgment Rule — Rebuttal Arguments', type: 'Memo', author: 'M. Kirkland', date: 'Apr 12', status: 'In Progress', citations: 11, summary: 'Counter to defendant\'s anticipated BJR defense. Focus on self-dealing exception and waste doctrine.' },
      { title: 'Expert Witness Qualification — Daubert Standards', type: 'Research', author: 'L. Torres', date: 'Mar 25', status: 'Complete', citations: 9, summary: 'Preparation for anticipated Daubert challenge to Dr. Mitchell. Strong basis for admissibility.' },
      { title: 'Summary Judgment Opposition — Key Authorities', type: 'Brief Research', author: 'M. Kirkland', date: 'Apr 18', status: 'In Progress', citations: 17, summary: 'Compiling authorities showing genuine disputes of material fact on all four counts.' },
    ],
    'M-2025-0384': [
      { title: 'Wire Fraud Mens Rea — Ciminelli / Kousisis Framework', type: 'Memo', author: 'L. Torres', date: 'Feb 10 2026', status: 'Complete', citations: 16, summary: 'Specific-intent threshold post-Ciminelli; tangible-property bright line limits § 1343 reach. Strong argument for judgment of acquittal on counts alleging only loss of accurate information.' },
      { title: '§ 1028A Severance Framework — Rule 14 Prejudice', type: 'Brief Research', author: 'L. Torres', date: 'Mar 12 2026', status: 'Complete', citations: 11, summary: 'Surveyed 2d Cir. severance rulings 2018-2025. Aggravated ID theft joinder disfavored where base-offense elements diverge. Court denied — preserved for appeal.' },
      { title: 'Kyles v. Whitley — Brady Materiality for Undisclosed 302s', type: 'Memo', author: 'M. Kirkland', date: 'Feb 22 2026', status: 'Complete', citations: 14, summary: 'Cumulative-materiality framework; identified 6 withheld 302s almost certainly material if they contain third-party attribution. Supports compel motion.' },
      { title: 'Cooperator Impeachment — Giglio Scope Post-Banks', type: 'Memo', author: 'L. Torres', date: 'Feb 28 2026', status: 'Complete', citations: 9, summary: 'Banks v. Dretke extends Giglio to deportation-leverage for non-citizen cooperators. N/A for Jensen/Doe 1 but establishes framework for future CWs.' },
      { title: 'USSG § 2B1.1 Loss Calculation — Actual vs. Intended Loss', type: 'Research', author: 'M. Kirkland', date: 'Mar 18 2026', status: 'Complete', citations: 18, summary: 'Loss-methodology dispute: government\'s $14.2M actual-loss calculation vs. our $8.4M (net of recovery). 4-level differential at sentencing.' },
      { title: '§ 3553(a) Variance Arguments — First Offender, Age 54', type: 'Memo', author: 'M. Kirkland', date: 'Apr 08 2026', status: 'In Progress', citations: 12, summary: 'Compiling support for 18-24 month below-guideline variance. Judge Hellerstein historical trend: 18% average variance on acceptance + cooperation.' },
    ],
    'M-2025-0218': [
      { title: 'HHS-OIG Pre-Clearance as Estoppel — Heckler v. Chaney', type: 'Memo', author: 'L. Torres', date: 'Feb 12 2026', status: 'Complete', citations: 14, summary: 'Novel but supportable theory. 2022 OIG audit "no material non-compliance" letter is a form of agency acquiescence. Ties to Valenzuela Gallardo estoppel line.' },
      { title: 'Anti-Kickback Statute — Commercial Reasonableness Safe Harbor', type: 'Research', author: 'L. Torres', date: 'Feb 24 2026', status: 'Complete', citations: 11, summary: '42 U.S.C. § 1320a-7b(b)(3)(B) safe harbor; § 1001.952(d) PPS payments. Clinician consulting fees arguably within safe harbor if commercially reasonable.' },
      { title: 'Patient Narrative Testimony — FRE 403 Prejudice', type: 'Memo', author: 'L. Torres', date: 'Mar 24 2026', status: 'Complete', citations: 9, summary: 'Prejudicial-patient narrative excluded in U.S. v. Nicholson (3d Cir.); analogous here. Motion in Limine filed.' },
      { title: 'Physician Intent — Ruan / Salvati Knowledge Standard', type: 'Memo', author: 'L. Torres', date: 'Mar 02 2026', status: 'Complete', citations: 12, summary: 'Ruan establishes subjective-knowledge standard for physician intent. Supports mens-rea defense on 7 of 12 counts where prescribing records show care decisions.' },
      { title: 'USSG § 2B1.1 Healthcare Loss — Intended vs. Overpayment', type: 'Research', author: 'M. Kirkland', date: 'Mar 18 2026', status: 'Complete', citations: 8, summary: 'Healthcare-specific loss calculation; government inflated intended-loss figure. Expert reconciliation brings claimed $4.2M down to $1.8M actual overpayment.' },
    ],
    'M-2026-0042': [
      { title: 'FCPA Foreign-Officials Definition Post-Esquenazi', type: 'Memo', author: 'M. Kirkland', date: 'Apr 05 2026', status: 'In Progress', citations: 13, summary: '11th Cir. Esquenazi framework; applied to SOE employees. 3 of 6 counts involve SOE-adjacent recipients; mixed-ownership analysis needed.' },
      { title: 'Bail Pending Trial — § 3142(e) Flight-Risk Standards', type: 'Memo', author: 'L. Torres', date: 'Mar 02 2026', status: 'Complete', citations: 11, summary: 'D.C. Cir. standard requires individualized consideration. Judge Bates applied categorical PRC-national presumption — potentially reversible.' },
      { title: 'DOJ Opinion Release 04-02 — Anti-Bribery Safe Harbor', type: 'Research', author: 'M. Kirkland', date: 'Apr 02 2026', status: 'Complete', citations: 7, summary: 'Opinion Release 04-02 pre-approved similar consulting-fee structures. Evidence that Sterling\'s compliance team cited this release in 2019 memo is exculpatory.' },
      { title: 'Parallel DOJ/SEC — Use-Immunity Protections', type: 'Memo', author: 'M. Kirkland', date: 'Mar 14 2026', status: 'Complete', citations: 9, summary: 'SEC CID + criminal charges trigger use-immunity framework per Kastigar. Negotiating proffer agreement with both agencies.' },
      { title: 'Conspiracy — Ocasio Individual Knowledge Element', type: 'Memo', author: 'J. Park', date: 'Apr 08 2026', status: 'In Progress', citations: 10, summary: 'Ocasio requires individual knowledge of specific corrupt intent; defense posture: Chen was executing Iyer\'s directives without knowledge of ultimate recipient.' },
      { title: 'Extraterritorial Evidence — Hague Convention + MLAT', type: 'Research', author: 'L. Torres', date: 'Apr 12 2026', status: 'In Progress', citations: 8, summary: 'Hague letter of request route vs. DOJ OIA MLAT channel. PRC authorities routinely decline Hague; MLAT more likely for criminal-enforcement context.' },
    ],
    'M-2025-0721': [
      { title: 'USSG § 2B1.1 — Fraud Loss + 10+ Victims Enhancement', type: 'Research', author: 'L. Torres', date: 'Mar 18 2026', status: 'Complete', citations: 16, summary: 'Loss calculation: $3.8M (government) vs $3.5M (ours) — matters for 16 vs 18 level enhancement. 10+ victims enhancement conceded.' },
      { title: '§ 3B1.1 Aggravating Role — Organizer vs. Supervisor', type: 'Memo', author: 'L. Torres', date: 'Mar 22 2026', status: 'Complete', citations: 11, summary: 'Bonilla framework distinguishes organizer (5+ participants) from supervisor. Our facts: Barnwell directed 3 subordinates — arguably § 3B1.1(c) 2-level not § 3B1.1(a) 4-level.' },
      { title: '§ 3B1.3 Abuse of Trust — Advisor Context', type: 'Memo', author: 'M. Kirkland', date: 'Mar 28 2026', status: 'Complete', citations: 9, summary: 'SEC registered investment adviser status triggers § 3B1.3 fiduciary-capacity enhancement. Limited basis to contest — accept 2 levels.' },
      { title: '§ 3553(a) Variance — Collateral Consequences Framework', type: 'Memo', author: 'L. Torres', date: 'Apr 02 2026', status: 'Complete', citations: 14, summary: 'Robertson / Nesbeth line recognizing collateral consequences in § 3553(a)(2)(D). SEC bar + Series 7 forfeiture + director ineligibility = significant non-sentence consequences.' },
      { title: 'Rule 33 — Post-Verdict Brady / Wearry Framework', type: 'Memo', author: 'L. Torres', date: 'Mar 22 2026', status: 'Complete', citations: 10, summary: 'Wearry v. Cain confirms Brady obligation continues post-verdict. SEC files\' impeachment of CW-2 satisfies materiality threshold for Rule 33.' },
      { title: 'First-Offender Sentencing Mitigation Precedent', type: 'Research', author: 'J. Park', date: 'Apr 06 2026', status: 'Complete', citations: 12, summary: 'SDNY sentencing database: first-time Class B felony defendants with acceptance + restitution + charitable records receive average 18% variance.' },
    ],
    'M-2025-0302': [
      { title: 'Franks v. Delaware — Officer Affiant Misstatement', type: 'Memo', author: 'J. Park', date: 'Feb 18 2026', status: 'Complete', citations: 13, summary: 'Franks two-prong: (1) material misstatement made knowingly/recklessly; (2) remainder insufficient for PC. Officer Reeves omitted CI-2\'s 3 prior false-report IA investigations — clear Franks trigger.' },
      { title: '§ 924(c) Consecutive Sentencing — Dean Framework', type: 'Memo', author: 'J. Park', date: 'Mar 10 2026', status: 'Complete', citations: 8, summary: 'Dean v. United States permits predicate-count variance to account for § 924(c) consecutive min. If firearm count survives, variance on predicate mitigates.' },
      { title: 'Safety-Valve Eligibility — § 3553(f) Five Factors', type: 'Memo', author: 'J. Park', date: 'Apr 10 2026', status: 'Complete', citations: 11, summary: 'Post-First-Step-Act § 3553(f) eligibility. Client likely meets all 5 factors if he debriefs; triggers safety-valve relief from 120-mo min on Count 1.' },
      { title: 'Confidential-Informant Reliability — Gates Totality Test', type: 'Research', author: 'J. Park', date: 'Feb 26 2026', status: 'Complete', citations: 9, summary: 'Gates totality-of-circumstances; CI-2\'s 3 prior false-report IA investigations undermine veracity. Strong Franks support.' },
      { title: 'Consent-Search Coercion — Schneckloth Totality', type: 'Memo', author: 'J. Park', date: 'Mar 04 2026', status: 'Complete', citations: 7, summary: 'Backup suppression theory if Franks fails. Body-cam timing + officer-positioning evidence supports coercion finding.' },
    ],
    _default: [
      { title: 'Initial Legal Research — Key Issues', type: 'Memo', author: m.lead, date: 'Mar 20', status: 'Complete', citations: 8, summary: 'Preliminary analysis of claims and defenses.' },
      { title: 'Jurisdictional Analysis', type: 'Research', author: m.lead, date: 'Mar 10', status: 'Complete', citations: 5, summary: 'Confirmed proper jurisdiction and venue.' },
    ],
  };

  // Billing
  const billingData = {
    'M-2024-0312': {
      team: [
        { name: 'M. Kirkland', role: 'Lead Partner', rate: 950, hours: 142, billed: 134900 },
        { name: 'L. Torres', role: 'Senior Associate', rate: 550, hours: 298, billed: 163900 },
        { name: 'J. Park', role: 'Associate', rate: 420, hours: 186, billed: 78120 },
      ],
      phases: [
        { phase: 'Case Assessment', hours: 48, amount: 42000, pct: 100 },
        { phase: 'Pleadings', hours: 86, amount: 76000, pct: 100 },
        { phase: 'Discovery', hours: 380, amount: 340000, pct: 72 },
        { phase: 'Expert Work', hours: 62, amount: 58000, pct: 85 },
        { phase: 'Motion Practice', hours: 50, amount: 46000, pct: 40 },
      ],
      expenses: [
        { desc: 'Court filing fees', amount: 4500, date: 'Jan 08' },
        { desc: 'Deposition transcript — Harrington', amount: 3200, date: 'Apr 15' },
        { desc: 'Expert retainer — Dr. Mitchell', amount: 25000, date: 'Mar 01' },
        { desc: 'Document hosting — Relativity', amount: 8400, date: 'Monthly' },
        { desc: 'Travel — Court appearances (3)', amount: 2800, date: 'Various' },
      ],
      invoices: [
        { number: 'INV-2026-0041', period: 'Mar 2026', amount: 186000, status: 'Paid' },
        { number: 'INV-2026-0028', period: 'Feb 2026', amount: 142000, status: 'Paid' },
        { number: 'INV-2026-0015', period: 'Jan 2026', amount: 118000, status: 'Paid' },
        { number: 'INV-2026-0055', period: 'Apr 2026', amount: 0, status: 'Pending' },
      ],
    },
    'M-2025-0384': {
      team: [
        { name: 'M. Kirkland', role: 'Lead Partner', rate: 950, hours: 180, billed: 171000 },
        { name: 'L. Torres', role: 'Senior Associate', rate: 550, hours: 340, billed: 187000 },
        { name: 'J. Park', role: 'Associate', rate: 420, hours: 220, billed: 92400 },
        { name: 'K. Walsh', role: 'Paralegal', rate: 180, hours: 98, billed: 17640 },
      ],
      phases: [
        { phase: 'Pre-Indictment / Target', hours: 92, amount: 78000, pct: 100 },
        { phase: 'Bond & Detention', hours: 38, amount: 31000, pct: 100 },
        { phase: 'Discovery', hours: 184, amount: 162000, pct: 90 },
        { phase: 'Motion Practice', hours: 246, amount: 212000, pct: 80 },
        { phase: 'Plea Analysis', hours: 94, amount: 82000, pct: 60 },
        { phase: 'Trial Prep', hours: 48, amount: 44000, pct: 20 },
        { phase: 'Sentencing Prep', hours: 36, amount: 30000, pct: 10 },
      ],
      expenses: [
        { desc: 'Expert retainer — Dr. S. Mitchell', amount: 35000, date: 'Mar 01' },
        { desc: 'Investigator — Garrison Investigations', amount: 28000, date: 'Ongoing' },
        { desc: 'Transcripts (grand jury, motions)', amount: 6200, date: 'Various' },
        { desc: 'Travel — SDNY court appearances', amount: 4800, date: 'Various' },
        { desc: 'Court filings (PACER / sealed docs)', amount: 2100, date: 'Various' },
      ],
      invoices: [
        { number: 'INV-2025-0892', period: 'Q4 2025', amount: 312000, status: 'Paid' },
        { number: 'INV-2026-0018', period: 'Jan 2026', amount: 142000, status: 'Paid' },
        { number: 'INV-2026-0034', period: 'Feb 2026', amount: 128000, status: 'Paid' },
        { number: 'INV-2026-0052', period: 'Mar 2026', amount: 102000, status: 'Paid' },
        { number: 'INV-2026-0078', period: 'Apr 2026', amount: 0, status: 'Pending' },
      ],
    },
    'M-2025-0218': {
      team: [
        { name: 'L. Torres', role: 'Lead Sr. Associate', rate: 550, hours: 280, billed: 154000 },
        { name: 'M. Kirkland', role: 'Partner — Strategy', rate: 950, hours: 64, billed: 60800 },
        { name: 'J. Park', role: 'Associate — Research', rate: 420, hours: 186, billed: 78120 },
        { name: 'S. Chen', role: 'Associate — Billing Data', rate: 420, hours: 142, billed: 59640 },
        { name: 'K. Walsh', role: 'Paralegal', rate: 180, hours: 64, billed: 11520 },
      ],
      phases: [
        { phase: 'Pre-Indictment / OIG', hours: 48, amount: 42000, pct: 100 },
        { phase: 'Bond & Intake', hours: 22, amount: 18000, pct: 100 },
        { phase: 'Discovery', hours: 180, amount: 148000, pct: 70 },
        { phase: 'Motion Practice (MTD, MIL)', hours: 210, amount: 174000, pct: 90 },
        { phase: 'Trial Prep', hours: 48, amount: 42000, pct: 15 },
      ],
      expenses: [
        { desc: 'Expert retainer — Dr. W. McKinley', amount: 28000, date: 'Mar 20' },
        { desc: 'Medicare-data consultant (LedgerTrace)', amount: 22000, date: 'Jan-Apr' },
        { desc: 'Transcripts', amount: 3400, date: 'Various' },
        { desc: 'Travel — EDPA court appearances', amount: 1800, date: 'Various' },
      ],
      invoices: [
        { number: 'INV-2025-0924', period: 'Q4 2025', amount: 186000, status: 'Paid' },
        { number: 'INV-2026-0022', period: 'Jan 2026', amount: 112000, status: 'Paid' },
        { number: 'INV-2026-0041', period: 'Feb 2026', amount: 94000, status: 'Paid' },
        { number: 'INV-2026-0060', period: 'Mar 2026', amount: 108000, status: 'Paid' },
        { number: 'INV-2026-0082', period: 'Apr 2026', amount: 0, status: 'Pending' },
      ],
    },
    'M-2026-0042': {
      team: [
        { name: 'M. Kirkland', role: 'Lead Partner', rate: 950, hours: 160, billed: 152000 },
        { name: 'L. Torres', role: 'Senior Associate', rate: 550, hours: 240, billed: 132000 },
        { name: 'J. Park', role: 'Associate', rate: 420, hours: 164, billed: 68880 },
        { name: 'S. Chen', role: 'Associate', rate: 420, hours: 88, billed: 36960 },
        { name: 'A. Liang', role: 'Of Counsel — APAC', rate: 800, hours: 46, billed: 36800 },
        { name: 'K. Walsh', role: 'Paralegal', rate: 180, hours: 18, billed: 3240 },
      ],
      phases: [
        { phase: 'Arrest & Arraignment', hours: 42, amount: 38000, pct: 100 },
        { phase: 'Bond / Detention Appeal', hours: 184, amount: 162000, pct: 95 },
        { phase: 'Discovery (incl. APAC Hague)', hours: 146, amount: 128000, pct: 60 },
        { phase: 'Motion Practice', hours: 68, amount: 62000, pct: 40 },
        { phase: 'Trial Prep', hours: 18, amount: 16000, pct: 5 },
      ],
      expenses: [
        { desc: 'APAC co-counsel retainer', amount: 18000, date: 'Feb-Apr' },
        { desc: 'Mandarin translator/interpreter', amount: 24000, date: 'Ongoing' },
        { desc: 'MDC-Alexandria facility visits', amount: 2800, date: 'Various' },
        { desc: 'Transcripts', amount: 1400, date: 'Various' },
        { desc: 'Hague Convention filing fees (draft)', amount: 0, date: 'Pending' },
      ],
      invoices: [
        { number: 'INV-2026-0044', period: 'Feb 2026', amount: 168000, status: 'Paid' },
        { number: 'INV-2026-0062', period: 'Mar 2026', amount: 124000, status: 'Paid' },
        { number: 'INV-2026-0084', period: 'Apr 2026', amount: 0, status: 'Pending' },
      ],
    },
    'M-2025-0721': {
      team: [
        { name: 'L. Torres', role: 'Lead Sr. Associate', rate: 550, hours: 440, billed: 242000 },
        { name: 'M. Kirkland', role: 'Partner', rate: 950, hours: 128, billed: 121600 },
        { name: 'J. Park', role: 'Associate', rate: 420, hours: 260, billed: 109200 },
        { name: 'K. Walsh', role: 'Paralegal', rate: 180, hours: 118, billed: 21240 },
      ],
      phases: [
        { phase: 'Pre-Indictment / SEC Parallel', hours: 42, amount: 38000, pct: 100 },
        { phase: 'Discovery', hours: 180, amount: 148000, pct: 100 },
        { phase: 'Motion Practice', hours: 146, amount: 128000, pct: 90 },
        { phase: 'Trial (10 court days)', hours: 420, amount: 368000, pct: 100 },
        { phase: 'Post-Trial Motions (Rule 33)', hours: 78, amount: 68000, pct: 60 },
        { phase: 'Sentencing Prep', hours: 80, amount: 64000, pct: 40 },
      ],
      expenses: [
        { desc: 'Trial expert witnesses (3)', amount: 68000, date: 'Feb-Mar' },
        { desc: 'Jury consultant — Axis Diligence', amount: 44000, date: 'Jan-Mar' },
        { desc: 'Trial graphics & demonstratives', amount: 12000, date: 'Feb-Mar' },
        { desc: 'Transcripts (trial + motions)', amount: 16000, date: 'Various' },
        { desc: 'Court filings + sealed docs', amount: 3400, date: 'Various' },
      ],
      invoices: [
        { number: 'INV-2025-0614', period: 'Q3 2025', amount: 142000, status: 'Paid' },
        { number: 'INV-2025-0888', period: 'Q4 2025', amount: 248000, status: 'Paid' },
        { number: 'INV-2026-0016', period: 'Jan 2026', amount: 98000, status: 'Paid' },
        { number: 'INV-2026-0032', period: 'Feb 2026', amount: 128000, status: 'Paid' },
        { number: 'INV-2026-0061', period: 'Mar 2026', amount: 0, status: 'Pending' },
      ],
    },
    'M-2025-0302': {
      team: [
        { name: 'J. Park', role: 'Lead Associate (CJA)', rate: 420, hours: 280, billed: 117600 },
        { name: 'M. Kirkland', role: 'Partner — Strategy', rate: 950, hours: 28, billed: 26600 },
        { name: 'K. Walsh', role: 'Paralegal', rate: 180, hours: 88, billed: 15840 },
      ],
      phases: [
        { phase: 'Bond / Intake', hours: 38, amount: 28000, pct: 100 },
        { phase: 'Discovery', hours: 94, amount: 62000, pct: 80 },
        { phase: 'Investigator Work (Sentinel)', hours: 62, amount: 44000, pct: 90 },
        { phase: 'Motion Practice (Suppress + Franks)', hours: 184, amount: 134000, pct: 95 },
        { phase: 'Trial Prep', hours: 0, amount: 0, pct: 0 },
      ],
      expenses: [
        { desc: 'Sentinel investigator engagement', amount: 18000, date: 'Ongoing' },
        { desc: 'Body-camera footage reconstruction', amount: 4200, date: 'Mar 2026' },
        { desc: 'FOIA / § 1983 fees (IA records)', amount: 1800, date: 'Jan-Feb' },
        { desc: 'Transcripts', amount: 2100, date: 'Various' },
      ],
      invoices: [
        { number: 'INV-2025-0914', period: 'Q4 2025', amount: 68000, status: 'Paid' },
        { number: 'INV-2026-0024', period: 'Jan 2026', amount: 42000, status: 'Paid' },
        { number: 'INV-2026-0043', period: 'Feb 2026', amount: 34000, status: 'Paid' },
        { number: 'INV-2026-0064', period: 'Mar 2026', amount: 40000, status: 'Paid' },
        { number: 'INV-2026-0086', period: 'Apr 2026', amount: 0, status: 'Pending' },
      ],
    },
    _default: {
      team: [{ name: m.lead, role: 'Lead', rate: 800, hours: Math.round(m.billed / 800), billed: m.billed }],
      phases: [{ phase: 'General', hours: Math.round(m.billed / 600), amount: m.billed, pct: 50 }],
      expenses: [{ desc: 'Filing fees', amount: 2500, date: 'Various' }],
      invoices: [{ number: 'INV-2026-0001', period: 'Q1 2026', amount: m.billed, status: 'Paid' }],
    },
  };

  // Notes
  const notesSets = {
    'M-2024-0312': [
      { id: 1, author: 'M. Kirkland', initials: 'MK', date: 'Apr 19, 2026 · 3:42 PM', content: 'Reviewed Harrington deposition transcript. Key admissions on pp. 87–92 regarding board awareness of the Meridian transaction. Need to cross-reference with financial records production.', pinned: true, replies: [
        { author: 'L. Torres', initials: 'LT', date: 'Apr 19, 2026 · 4:15 PM', content: 'Flagged those pages. Also found corroborating emails in the production — Bates MH-0042881 through MH-0042903.' },
        { author: 'M. Kirkland', initials: 'MK', date: 'Apr 19, 2026 · 4:30 PM', content: 'Excellent. Add to the fact chronology and flag for the MSJ opposition brief.' },
      ]},
      { id: 2, author: 'J. Park', initials: 'JP', date: 'Apr 18, 2026 · 11:20 AM', content: 'Privilege log updated. 23 documents withheld on attorney-client privilege, 8 on work product. Defense may challenge the work product claims — see notes in log.', pinned: false, replies: [] },
      { id: 3, author: 'L. Torres', initials: 'LT', date: 'Apr 16, 2026 · 2:05 PM', content: 'Dr. Mitchell\'s damages range ($14.2M–$18.7M) is strong. The low end alone exceeds their last settlement signal. Recommend we prepare a detailed rebuttal for their inevitable Daubert challenge.', pinned: true, replies: [
        { author: 'M. Kirkland', initials: 'MK', date: 'Apr 16, 2026 · 3:10 PM', content: 'Agreed. Torres — draft the Daubert response framework. Park — compile the case law from your earlier research.' },
      ]},
      { id: 4, author: 'M. Kirkland', initials: 'MK', date: 'Apr 12, 2026 · 9:45 AM', content: 'Received Meridian\'s financial production — 3,200+ documents. Initial review shows several gaps in the 2022 Q3 records. May need follow-up request or motion.', pinned: false, replies: [] },
      { id: 5, author: 'L. Torres', initials: 'LT', date: 'Apr 10, 2026 · 4:50 PM', content: 'Motion to Compel granted in full. Judge Reynolds was clearly skeptical of Meridian\'s proportionality arguments. Good precedent for any future discovery disputes in this matter.', pinned: false, replies: [] },
      { id: 6, author: 'J. Park', initials: 'JP', date: 'Apr 02, 2026 · 10:30 AM', content: 'BJR defense research update: Found 4 recent Delaware cases where self-dealing exception applied on similar facts. Memo draft in progress — will circulate by EOD Thursday.', pinned: false, replies: [
        { author: 'M. Kirkland', initials: 'MK', date: 'Apr 02, 2026 · 11:00 AM', content: 'Good. Focus especially on the Gentile v. Rossette line — most analogous to our facts.' },
      ]},
    ],
    'M-2025-0384': [
      { id: 1, author: 'M. Kirkland', initials: 'MK', date: 'Apr 19, 2026 · 2:14 PM', content: 'Plea-decision meeting with Harrell set for Apr 30. Client leaning toward plea but wants Brady compel ruling first. Walked him through probability-adjusted math: trial @ 55% conviction × 86 mo = 47 mo expected vs plea 57 mo certain. With Kyles materiality on the 6 withheld 302s, the calculus could flip.', pinned: true, replies: [
        { author: 'L. Torres', initials: 'LT', date: 'Apr 19, 2026 · 3:02 PM', content: '29-month savings at probability-adjusted is real. But AUSA Chen said today the offer expires May 14 regardless of Brady ruling. So we need our own read on those withheld 302s before the 14th.' },
        { author: 'M. Kirkland', initials: 'MK', date: 'Apr 19, 2026 · 3:30 PM', content: 'Agreed. If the Brady compel comes back unfavorable, we should accept. I want Harrell to make the call, but paint it with eyes open.' },
      ]},
      { id: 2, author: 'J. Park', initials: 'JP', date: 'Apr 17, 2026 · 11:20 AM', content: 'Reviewed all 8 produced Brady 302s. CW-3 Doe 1 mentions Harrell only twice, both on hearsay from CW-2 Jensen. If the 6 withheld 302s follow the same pattern (third-party attribution only), we have a strong Kyles argument that those 302s are material to guilt.', pinned: false, replies: [] },
      { id: 3, author: 'L. Torres', initials: 'LT', date: 'Apr 15, 2026 · 4:05 PM', content: 'Cooperator Jensen (CW-2) has 3 prior inconsistent statements identified from his 2024 SEC deposition. Flagging for trial cross and demanding full Jencks Act production 30 days pre-trial. Key impeachment: his timeline of the Redstone transaction contradicts the indictment by 6 months.', pinned: true, replies: [
        { author: 'M. Kirkland', initials: 'MK', date: 'Apr 15, 2026 · 4:40 PM', content: 'Good work. Put this in the Giglio matrix and reference it on Apr 30 client meeting — this is the single biggest lever we have for trial posture.' },
      ]},
      { id: 4, author: 'K. Walsh', initials: 'KW', date: 'Apr 10, 2026 · 10:15 AM', content: 'Finalized Bates index for all Brady productions. Cross-referenced with CW-3 proffer ¶¶ 22-41 — confirmed no overlap with Dr. Mitchell\'s damages methodology. Also logged gaps: the 6 withheld 302s by date show an interesting pattern — all from Sep-Oct 2022, which is the period of the disputed transactions.', pinned: false, replies: [] },
      { id: 5, author: 'M. Kirkland', initials: 'MK', date: 'Apr 08, 2026 · 9:30 AM', content: 'Judge Hellerstein track record for first-time wire-fraud defendants with acceptance + cooperation: 18% average below-guideline variance. If we accept plea AND the § 1028A gets dismissed (no more 24-mo consecutive), we could see 42-48 mo. That\'s a meaningful delta on life outcome.', pinned: false, replies: [] },
    ],
    'M-2025-0218': [
      { id: 1, author: 'L. Torres', initials: 'LT', date: 'Apr 19, 2026 · 3:12 PM', content: 'MTD hearing Apr 28 is our best shot to kill Counts 1-6 outright. The 2022 HHS-OIG civil audit closure letter stating "no material non-compliance" is the linchpin. Pappert is receptive to novel theories but skeptical — we need to frame it as straight agency-acquiescence estoppel, not novel constitutional theory. Cite: Heckler v. Chaney + Valenzuela Gallardo line.', pinned: true, replies: [
        { author: 'M. Kirkland', initials: 'MK', date: 'Apr 19, 2026 · 3:45 PM', content: 'Yes. Lead with the OIG letter on the first page of the brief. Make the government explain why we\'re being prosecuted for conduct OIG explicitly cleared.' },
      ]},
      { id: 2, author: 'L. Torres', initials: 'LT', date: 'Apr 14, 2026 · 11:05 AM', content: 'If MTD denied, Dr. McKinley\'s pharmacology framework is our anchor. He can rebut 9 of 12 counts on medical-necessity theory under Ruan. The remaining 3 counts are the Anti-Kickback allegations — different framework, but we have commercially-reasonable safe harbor per § 1001.952(d).', pinned: false, replies: [] },
      { id: 3, author: 'S. Chen', initials: 'SC', date: 'Apr 09, 2026 · 9:30 AM', content: 'Compiled LedgerTrace analysis on government\'s 18 "billing anomalies." After variance normalization: only 4 are actually anomalous (outside 2σ of peer cohort). The other 14 are within peer-distribution norms. Prepping demonstrative chart for cross of government forensic-accounting witness.', pinned: true, replies: [
        { author: 'L. Torres', initials: 'LT', date: 'Apr 09, 2026 · 10:00 AM', content: 'This is critical for trial. Share with McKinley — he can incorporate into his report rebuttal.' },
      ]},
      { id: 4, author: 'J. Park', initials: 'JP', date: 'Mar 28, 2026 · 2:40 PM', content: 'Brady in-camera review granted on 3 withheld OIG audit letters. Decision expected mid-May before MTD argument. If those letters corroborate the 2022 "no material non-compliance" finding, our estoppel argument becomes much stronger.', pinned: false, replies: [] },
    ],
    'M-2026-0042': [
      { id: 1, author: 'M. Kirkland', initials: 'MK', date: 'Apr 19, 2026 · 4:00 PM', content: 'Detention is our highest-priority fight. D.C. Circuit panel draw on Apr 28 is critical — if we get Katsas/Rao pairing, flight-risk precedent is more favorable. Bail-package we\'re offering: $5M bond, third-party custodian (brother-in-law in Rockville, retired federal employee), GPS, US + PRC passport surrender, daily check-in. Judge Bates rejected as "not adequate" — clear error argument strong on the categorical PRC-national presumption.', pinned: true, replies: [
        { author: 'L. Torres', initials: 'LT', date: 'Apr 19, 2026 · 4:28 PM', content: 'Drafting reply brief. Focus on § 3142(g) individualized-consideration requirement — Judge Bates skipped directly to flight risk based on nationality without addressing the specific bail package.' },
      ]},
      { id: 2, author: 'L. Torres', initials: 'LT', date: 'Apr 16, 2026 · 10:45 AM', content: 'Brady dispute: Sterling\'s internal compliance-team emails with DOJ pre-charge could show that DOJ was focused on Arnav Iyer (not Chen) as the directing party. If true, material to our "following directives without knowledge" defense. In-camera review agreed; ruling mid-May.', pinned: false, replies: [] },
      { id: 3, author: 'A. Liang', initials: 'AL', date: 'Apr 12, 2026 · 3:20 PM', content: 'APAC subsidiary financial records obtainable via Hague Evidence Convention letter of request, but PRC Ministry of Justice routinely declines to compel under Hague Art. 11-12. Recommending parallel MLAT request via DOJ OIA — MLAT has better compliance rate for criminal enforcement than Hague. Timeline: 4-6 months for MLAT response.', pinned: true, replies: [
        { author: 'M. Kirkland', initials: 'MK', date: 'Apr 12, 2026 · 4:00 PM', content: 'Let\'s run parallel tracks — Hague is our formal record, MLAT our realistic channel. Brief DOJ OIA first week of May.' },
      ]},
      { id: 4, author: 'M. Kirkland', initials: 'MK', date: 'Apr 05, 2026 · 11:15 AM', content: 'Joint-defense privilege with Sterling Pharma is holding but fragile. If co-defendant Iyer cooperates, the JDP fractures on Iyer\'s testimony only — but we preserve against government intrusion on Chen\'s behalf. Arranged JDP-preservation memo with Sterling outside counsel.', pinned: false, replies: [] },
    ],
    'M-2025-0721': [
      { id: 1, author: 'L. Torres', initials: 'LT', date: 'Apr 19, 2026 · 5:00 PM', content: 'Sentencing strategy has two prongs running in parallel: (1) Rule 33 motion for new trial based on post-verdict Brady discovery from SEC files that impeaches CW-2 on the Q3 2022 materiality testimony; (2) Sentencing variance under § 3553(a) targeting 72 months from 97-121 month guideline range. Factors: age 52, no priors, $4.8M restitution arranged, 20+ years charitable foundation board service. Asking for 23-mo variance — aggressive but supportable.', pinned: true, replies: [
        { author: 'M. Kirkland', initials: 'MK', date: 'Apr 19, 2026 · 5:30 PM', content: 'Engelmayer generally grants 15-25% variance when § 3553(a) is well-supported. Letters of support are critical — we\'re at 34 collected, target 50+. Get Hollis (former CFO) to write one, that\'s powerful.' },
      ]},
      { id: 2, author: 'L. Torres', initials: 'LT', date: 'Apr 15, 2026 · 2:10 PM', content: 'PSR objection strategy: § 2B1.1(b)(1)(I) 18-level loss enhancement is locked — government\'s $3.5M+ loss figure is well-supported and conceding this avoids Judge Engelmayer\'s irritation at unmeritorious objections. BUT § 3B1.1(a) organizer enhancement (+4 levels) is vulnerable under Bonilla — Barnwell directed 3 subordinates, not 5+ participants. Aim to get this reduced to § 3B1.1(c) (+2 levels) — that\'s a 2-level delta = 18 months off range.', pinned: false, replies: [] },
      { id: 3, author: 'J. Park', initials: 'JP', date: 'Apr 12, 2026 · 10:30 AM', content: 'Completed Rule 33 / Wearry framework memo. Key Brady item: SEC deposition of former CFO Hollis directly contradicts CW-2\'s trial testimony on the materiality of the Q3 2022 disclosure. This is post-verdict Brady because SEC parallel files were produced Nov 2025 but only became material when CW-2\'s trial testimony focused on materiality (Feb 2026). Strong Wearry argument.', pinned: true, replies: [] },
      { id: 4, author: 'M. Kirkland', initials: 'MK', date: 'Apr 08, 2026 · 11:45 AM', content: 'Appellate issue preservation: file notice of appeal protectively right after sentencing regardless of Rule 33 outcome. If New Trial motion is granted, we withdraw the appeal. If denied, we have appellate issues on (a) CW-2 impeachment adequacy under Napue; (b) § 3B1.1 organizer enhancement.', pinned: false, replies: [] },
    ],
    'M-2025-0302': [
      { id: 1, author: 'J. Park', initials: 'JP', date: 'Apr 19, 2026 · 6:15 PM', content: 'Franks hearing granted is the whole game. Officer Reeves\' affidavit in support of the warrant contained 3 demonstrable misstatements about CI-2 Marcus Hudson: claimed "no prior false-report history" but Sentinel pulled 3 IA investigations (2018 fabricated-complaint; 2020 false-report in drug case; 2022 perjury-investigation that was closed without discipline). If the court strikes those 3 statements from the affidavit, no probable cause remains — suppression of 500g cocaine + firearm follows automatically. And § 924(c) goes away without the firearm.', pinned: true, replies: [
        { author: 'M. Kirkland', initials: 'MK', date: 'Apr 19, 2026 · 7:00 PM', content: 'This is a textbook Franks case. Matsumoto rarely grants Franks — her willingness to grant the hearing on the papers alone is the most favorable signal we could get. Prep for May 8 like your career depends on it, because Miller\'s liberty does.' },
      ]},
      { id: 2, author: 'J. Park', initials: 'JP', date: 'Apr 15, 2026 · 11:30 AM', content: 'Safety-valve proffer draft complete as a backup. If Franks/suppression fail, client debriefs with DEA and meets § 3553(f) 5-factor test: (1) not more than 1 CH point — YES; (2) no violence — YES; (3) no death/injury — YES; (4) not an organizer — YES; (5) truthful debrief — if he complies. That drops us below the 10-yr mandatory min for Count 1, eligible for guideline range 63-78 mo instead of 121-151 mo.', pinned: false, replies: [] },
      { id: 3, author: 'M. Kirkland', initials: 'MK', date: 'Apr 12, 2026 · 3:20 PM', content: 'Backup suppression theory on consent-search coercion. Body-cam footage shows Officer Reeves blocking the doorway when he asked for consent. Standard Schneckloth totality-of-circumstances. If Franks doesn\'t deliver full suppression, consent argument keeps the firearm out at minimum.', pinned: false, replies: [] },
      { id: 4, author: 'J. Park', initials: 'JP', date: 'Apr 10, 2026 · 9:00 AM', content: 'Sentinel delivered final investigator report on CI-2 today. 62 pages, 4 exhibits: (1) 2018 IA complaint — fabricated-complaint finding; (2) 2020 IA report on CI-2 giving false info in a narcotics investigation that resulted in case dismissal; (3) 2022 perjury investigation — CI-2 admitted fabricating testimony, case closed without discipline; (4) CI-2\'s fee history showing $45K paid over 14 months. All of this was known to or reasonably discoverable by Officer Reeves when he prepared the affidavit.', pinned: true, replies: [
        { author: 'M. Kirkland', initials: 'MK', date: 'Apr 10, 2026 · 10:00 AM', content: 'Document everything. Sentinel\'s chain of custody on those IA records has to be airtight. Garrison Investigations will want to attest at the hearing.' },
      ]},
    ],
    _default: [
      { id: 1, author: m.lead, initials: m.lead.split(' ').map(w=>w[0]).join(''), date: 'Apr 15, 2026 · 10:00 AM', content: 'Initial case assessment complete. Strategy memo circulated to team.', pinned: false, replies: [] },
    ],
  };

  return {
    docs: docSets[id] || docSets._default,
    timeline: timelineSets[id] || timelineSets._default,
    research: researchSets[id] || researchSets._default,
    billing: billingData[id] || billingData._default,
    notes: notesSets[id] || notesSets._default,
  };
}

// ── AI summaries per matter ──
const AI_SUMMARIES = {
  'M-2024-0312': 'Discovery phase nearing cutoff. Key depositions completed; financial records production received Apr 12. Expert damages report estimates $14.2M–$18.7M range. Opposition to summary judgment due May 14 — draft v3 in review. Consider scheduling follow-up deposition with controller re: newly produced records.',
  'M-2024-0298': 'Complex multi-party antitrust case. 5-member team managing substantial document review (4,500+ docs). Depositions ongoing. Budget at 60% — on track. Key risk: class certification hearing pending.',
  'M-2024-0284': 'Trial in 12 days. Expert reports finalized. Focus on witness preparation and exhibit organization. Budget nearing cap at 79% — request budget extension if trial exceeds 4 days.',
  'M-2024-0271': 'Patent claim construction hearing anticipated. Claim chart analysis complete. Prior art search identified 3 strong references. Discovery progressing — 63% of budget used.',
  'M-2024-0265': 'Settlement negotiations active. Last offer: $4.2M from Atlas Financial. Client authorized up to $5M acceptance. Recommend counter at $6.8M based on damages analysis.',
  'M-2024-0259': 'FCPA investigation ongoing. DOJ cooperation credit strategy in place. Internal audit complete — 3 problematic transactions identified. Budget healthy at 45%.',
  'M-2025-0384': 'Wire-fraud matter in active plea negotiations. 8 counts over an alleged $14.2M scheme. Plea offer on the table (accept Counts 1 & 4, dismiss 2,3,5-8) with guideline range 51-63 mo vs. trial-equivalent 78-97 mo. Decision deadline May 14. Motion to Compel 6 withheld Brady 302s under submission — ruling could shift plea calculus. Cooperator Jensen has 3 prior inconsistent statements identified for cross-examination. USSG § 2B1.1 base level 24 plus loss enhancement; anticipate § 3553(a) variance for age (54), no priors, and voluntary remediation.',
  'M-2025-0218': 'Health-care-fraud matter entering critical pretrial window. Motion to Dismiss on HHS-OIG pre-clearance estoppel argued Apr 28 — our strongest counter to Counts 1-6. If MTD denied, trial June 10 (8-day estimate). Expert strategy: Dr. McKinley on pharmacology methodology + LedgerTrace billing-anomaly rebuttal (reducing claimed 18 anomalies to 4). 12 counts over § 1347 and § 1320a-7b; client on $500K bond. Brady in-camera review pending on 3 withheld OIG audit letters. Anticipated plea offer only after MTD ruling.',
  'M-2026-0042': 'FCPA matter with defendant Chen Wei-Long detained pending trial. D.C. Circuit oral argument on detention appeal Apr 28 — highest-priority fight. Parallel SEC Civil Investigative Demand received; negotiating use-immunity framework under Kastigar. 6 counts — § 78dd-1 anti-bribery + § 371 conspiracy + § 1956(h) laundering. USSG range 63-78 mo. APAC evidence recovery ongoing via Hague Convention + parallel MLAT request. Joint defense with Sterling Pharma holding but fragile — may fracture if co-defendant Iyer cooperates. Winning detention appeal would significantly shift plea leverage.',
  'M-2025-0721': 'Securities-fraud matter post-verdict, entering sentencing phase. Convicted on 3 counts (10b-5) after 10-day trial. USSG 97-121 mo (offense level 30, CHC I). Motion for New Trial (Rule 33) under Wearry framework pending — post-verdict SEC files impeach CW-2 on Q3 2022 materiality testimony. Sentencing Jun 04; our ask is 72 mo below-guideline via § 3553(a) variance (age 52, charitable works, $4.8M restitution arranged). Government seeking top of range. PSR objections due May 28 — challenging § 3B1.1(a) organizer enhancement under Bonilla. Appeal protectively noted.',
  'M-2025-0302': 'Narcotics + § 924(c) matter with Franks hearing granted — the case\'s turning point. Evidentiary hearing May 8 on Officer Reeves\'s affidavit misstatements (3 CI-2 prior false-reports documented by Sentinel but omitted from the warrant affidavit). If Franks succeeds, warrant quashed and all physical evidence (500g cocaine + firearm) suppressed; § 924(c) falls with the firearm. Mandatory min exposure: 120 mo on Count 1 + 60 mo consecutive § 924(c) = 180 mo. Backup: safety-valve proffer under § 3553(f) drops below mandatory min to 63-78 mo. CJA appointment; Sentinel investigator has been crucial.',
  _default: 'Matter proceeding as planned. Review recent activity and upcoming deadlines for current priorities.',
};

// ────────────────────────────────
// TAB: OVERVIEW
// ────────────────────────────────
function OverviewTab({ m, data, onOpenWarRoom, onOpenEvidence, onOpenRisk, onOpenESI }) {
  const [hoveredDoc, setHoveredDoc] = useState(null);
  const budgetPct = Math.round((m.billed / m.budget) * 100);
  const summary = AI_SUMMARIES[m.id] || AI_SUMMARIES._default;
  return (
    <div style={cs.body2col}>
      <div>
        <div style={cs.sec}>
          <div style={cs.aiBox}>
            <div style={cs.aiLabel}>◆ AI Summary</div>
            {summary}
          </div>
        </div>
        <div style={cs.sec}>
          <div style={cs.secH}><span>Case Timeline</span><span style={{fontSize:T.font.size.xs,color:T.color.text.tertiary,fontWeight:T.font.weight.normal}}>{data.timeline.length} events</span></div>
          {data.timeline.slice(0,6).map((e,i) => (
            <div key={i} style={cs.tlItem}>
              {i < 5 && <div style={cs.tlLine}/>}
              <div style={cs.tlDot}/>
              <div style={{flex:1}}>
                <div style={{display:'flex',justifyContent:'space-between',alignItems:'baseline'}}>
                  <span style={{fontWeight:T.font.weight.medium,fontSize:T.font.size.sm,color:T.color.text.primary}}>{e.event}</span>
                  <span style={{fontSize:T.font.size.xs,color:T.color.text.tertiary,fontFamily:T.font.mono}}>{e.date}</span>
                </div>
                <div style={{fontSize:T.font.size.sm,color:T.color.text.secondary,marginTop:'2px'}}>{e.detail}</div>
              </div>
            </div>
          ))}
        </div>
        <div style={cs.sec}>
          <div style={cs.secH}><span>Recent Documents</span><span style={{fontSize:T.font.size.xs,color:T.color.text.tertiary,fontWeight:T.font.weight.normal}}>{data.docs.length} total</span></div>
          {data.docs.slice(0,5).map((d,i) => (
            <div key={i} style={{...cs.row, background: hoveredDoc===i?T.color.bg.hover:'transparent'}} onMouseEnter={()=>setHoveredDoc(i)} onMouseLeave={()=>setHoveredDoc(null)}>
              <div style={cs.docIcon}>{d.ext==='PDF'?'▤':d.ext==='XLSX'?'▦':'▧'}</div>
              <div style={{flex:1}}>
                <div style={{fontWeight:T.font.weight.medium,color:T.color.text.primary}}>{d.name}</div>
                <div style={{fontSize:T.font.size.xs,color:T.color.text.tertiary}}>{d.type} · {d.size} · {d.author}</div>
              </div>
              <span style={{fontSize:T.font.size.xs,color:T.color.text.tertiary,fontFamily:T.font.mono}}>{d.modified}</span>
            </div>
          ))}
        </div>
      </div>
      <div>
        <RightSidebar m={m} data={data} onOpenWarRoom={onOpenWarRoom} onOpenEvidence={onOpenEvidence} onOpenRisk={onOpenRisk} onOpenESI={onOpenESI} />
      </div>
    </div>
  );
}

// ── Shared right sidebar ──
function RightSidebar({ m, data, onOpenWarRoom, onOpenEvidence, onOpenRisk, onOpenESI }) {
  const budgetPct = Math.round((m.billed / m.budget) * 100);
  const team = data.billing.team;
  return (
    <>
      <div style={cs.sec}>
        <div style={cs.secH}><span>Case Team</span></div>
        {team.map((t,i) => (
          <div key={i} style={{padding:'10px 16px',borderBottom:`1px solid ${T.color.border.light}`,display:'flex',alignItems:'center',gap:'10px'}}>
            <div style={{...cs.avatar, background: [T.color.navy700,'#4A5A80','#6E7D9E'][i%3], color:T.color.ivory100}}>{t.name.split(' ').map(w=>w[0]).join('')}</div>
            <div style={{flex:1}}>
              <div style={{fontWeight:T.font.weight.medium,fontSize:T.font.size.sm,color:T.color.text.primary}}>{t.name}</div>
              <div style={{fontSize:T.font.size.xs,color:T.color.text.tertiary}}>{t.role}</div>
            </div>
            <span style={{fontSize:T.font.size.xs,color:T.color.text.tertiary,fontFamily:T.font.mono}}>{t.hours}h</span>
          </div>
        ))}
      </div>
      <div style={cs.sec}>
        <div style={cs.secH}><span>Budget</span></div>
        <div style={{padding:'16px'}}>
          <div style={{display:'flex',justifyContent:'space-between',marginBottom:'8px'}}>
            <span style={{fontSize:T.font.size.sm,fontWeight:T.font.weight.medium,color:T.color.text.primary}}>{fmtCur(m.billed)}</span>
            <span style={{fontSize:T.font.size.sm,color:T.color.text.tertiary}}>of {fmtCur(m.budget)}</span>
          </div>
          <div style={cs.barTrack}><div style={{...cs.barFill, width:`${budgetPct}%`, background: budgetPct>85?T.color.status.warning:T.color.navy600}}/></div>
          <div style={{fontSize:T.font.size.xs,color:T.color.text.tertiary,marginTop:'6px'}}>{budgetPct}% utilized</div>
        </div>
      </div>
      <div style={cs.sec}>
        <div style={cs.secH}><span>Key Dates</span></div>
        {[
          {label:'Discovery Cutoff',date:'May 14, 2026',urgent:true},
          {label:'MSJ Opposition Due',date:'May 14, 2026',urgent:true},
          {label:'Pretrial Conference',date:'Jun 02, 2026',urgent:false},
          {label:'Trial Date',date:new Date(m.dueDate).toLocaleDateString('en-US',{month:'long',day:'numeric',year:'numeric'}),urgent:false},
        ].map((d,i)=>(
          <div key={i} style={{padding:'8px 16px',borderBottom:`1px solid ${T.color.border.light}`,display:'flex',justifyContent:'space-between',alignItems:'center'}}>
            <span style={{fontSize:T.font.size.sm,fontWeight:T.font.weight.medium,color:d.urgent?T.color.status.critical:T.color.text.primary}}>{d.label}</span>
            <span style={{fontSize:T.font.size.xs,fontFamily:T.font.mono,color:T.color.text.tertiary}}>{d.date}</span>
          </div>
        ))}
      </div>
      <div style={cs.sec}>
        <div style={cs.secH}><span>Platforms</span></div>
        {[
          { label: 'War Room', icon: '•', desc: 'Real-time strategy & collaboration', gradient: 'linear-gradient(135deg, #EF4444 0%, #B91C1C 100%)', handler: () => onOpenWarRoom && onOpenWarRoom(m) },
          { label: 'Evidence', icon: '◆', desc: 'Evidence vault & review pipeline', gradient: 'linear-gradient(135deg, #0D9488 0%, #0F766E 100%)', handler: () => onOpenEvidence && onOpenEvidence(m) },
          { label: 'Risk', icon: 'flag', desc: 'Risk register & scenario analysis', gradient: 'linear-gradient(135deg, #B91C1C 0%, #991B1B 100%)', handler: () => onOpenRisk && onOpenRisk(m) },
          { label: 'ESI', icon: '◇', desc: 'Data sources · forensics · processing · cost', gradient: 'linear-gradient(135deg, #0891B2 0%, #0E7490 100%)', handler: () => onOpenESI && onOpenESI(m) },
        ].map((p, i) => (
          <div key={i} onClick={p.handler} style={{ padding: '10px 16px', borderBottom: `1px solid ${T.color.border.light}`, display: 'flex', alignItems: 'center', gap: '10px', cursor: 'pointer', transition: 'background 0.1s' }}
            onMouseEnter={e => e.currentTarget.style.background = T.color.bg.hover}
            onMouseLeave={e => e.currentTarget.style.background = 'transparent'}>
            <div style={{ width: '28px', height: '28px', borderRadius: '6px', background: p.gradient, display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: '12px', color: '#fff', fontWeight: 700, flexShrink: 0 }}>{p.icon}</div>
            <div style={{ flex: 1 }}>
              <div style={{ fontWeight: T.font.weight.medium, fontSize: T.font.size.sm, color: T.color.text.primary }}>{p.label}</div>
              <div style={{ fontSize: T.font.size.xs, color: T.color.text.tertiary }}>{p.desc}</div>
            </div>
            <span style={{ fontSize: '12px', color: T.color.text.tertiary }}>→</span>
          </div>
        ))}
      </div>
    </>
  );
}

// ────────────────────────────────
// TAB: DOCUMENTS
// ────────────────────────────────
function DocumentsTab({ m, data }) {
  const [filter, setFilter] = useState('All');
  const [search, setSearch] = useState('');
  const [hovered, setHovered] = useState(null);
  const types = ['All', ...new Set(data.docs.map(d=>d.type))];
  const filtered = useMemo(() => {
    let d = data.docs;
    if (filter !== 'All') d = d.filter(x=>x.type===filter);
    if (search) { const q = search.toLowerCase(); d = d.filter(x=>x.name.toLowerCase().includes(q)); }
    return d;
  }, [filter, search, data.docs]);

  return (
    <div style={cs.body1col}>
      <div style={{display:'flex',gap:'8px',marginBottom:'12px',alignItems:'center',flexWrap:'wrap'}}>
        <input style={{height:'32px',border:`1px solid ${T.color.border.light}`,borderRadius:T.radius.md,padding:'0 12px',fontSize:T.font.size.base,fontFamily:T.font.family,background:T.color.bg.card,color:T.color.text.primary,outline:'none',width:'220px'}} placeholder="Filter documents..." value={search} onChange={e=>setSearch(e.target.value)}/>
        {types.map(t=>(
          <button key={t} style={{...cs.filterBtn,...(filter===t?cs.filterBtnActive:{})}} onClick={()=>setFilter(t)}>{t}</button>
        ))}
        <div style={{flex:1}}/>
        <button style={{...window.sharedStyles.btn,...window.sharedStyles.btnPrimary}}>+ Upload</button>
      </div>
      <div style={cs.sec}>
        <div style={{display:'grid',gridTemplateColumns:'1fr 100px 80px 70px 60px',padding:'6px 16px',background:T.color.bg.secondary,borderBottom:`1px solid ${T.color.border.light}`,fontSize:T.font.size.xs,fontWeight:T.font.weight.medium,color:T.color.text.tertiary,textTransform:'uppercase',letterSpacing:T.font.tracking.caps,gap:'8px'}}>
          <span>Document</span><span>Status</span><span>Author</span><span>Date</span><span>Type</span>
        </div>
        {filtered.map((d,i)=>(
          <div key={i} style={{display:'grid',gridTemplateColumns:'1fr 100px 80px 70px 60px',padding:'8px 16px',borderBottom:`1px solid ${T.color.border.light}`,alignItems:'center',gap:'8px',cursor:'pointer',background:hovered===i?T.color.bg.hover:'transparent',transition:'background 0.1s',fontSize:T.font.size.sm}} onMouseEnter={()=>setHovered(i)} onMouseLeave={()=>setHovered(null)}>
            <div style={{display:'flex',alignItems:'center',gap:'8px'}}>
              <div style={cs.docIcon}>{d.ext==='PDF'?'▤':d.ext==='XLSX'?'▦':'▧'}</div>
              <div>
                <div style={{fontWeight:T.font.weight.medium,color:T.color.text.primary}}>{d.name}</div>
                <div style={{fontSize:T.font.size.xs,color:T.color.text.tertiary}}>{d.type} · {d.size}</div>
              </div>
            </div>
            <span><StatusTag status={d.status}/></span>
            <span style={{color:T.color.text.secondary,fontSize:T.font.size.xs}}>{d.author}</span>
            <span style={{color:T.color.text.tertiary,fontFamily:T.font.mono,fontSize:T.font.size.xs}}>{d.modified}</span>
            <span style={{...cs.extBadge,color:extColor(d.ext),background:`${extColor(d.ext)}10`}}>{d.ext}</span>
          </div>
        ))}
      </div>
    </div>
  );
}

// ────────────────────────────────
// TAB: TIMELINE
// ────────────────────────────────
function TimelineTab({ m, data }) {
  const typeColors = { filing: T.color.accent.blue, deposition: '#6B21A8', order: T.color.status.warning, document: T.color.status.active, hearing: T.color.navy500, production: '#B8860B', notice: T.color.text.tertiary, meeting: T.color.navy400, admin: T.color.text.tertiary, status: T.color.navy300 };
  return (
    <div style={cs.body1col}>
      <div style={cs.sec}>
        <div style={cs.secH}><span>Full Case Timeline</span><span style={{fontSize:T.font.size.xs,color:T.color.text.tertiary,fontWeight:T.font.weight.normal}}>{data.timeline.length} events</span></div>
        {data.timeline.map((e,i) => (
          <div key={i} style={{...cs.tlItem, padding:'12px 16px'}}>
            {i < data.timeline.length-1 && <div style={{...cs.tlLine, left:'23px', top:'32px'}}/>}
            <div style={{...cs.tlDot, borderColor: typeColors[e.type]||T.color.navy400}}/>
            <div style={{flex:1}}>
              <div style={{display:'flex',justifyContent:'space-between',alignItems:'baseline',marginBottom:'2px'}}>
                <span style={{fontWeight:T.font.weight.semibold,fontSize:T.font.size.sm,color:T.color.text.primary}}>{e.event}</span>
                <span style={{fontSize:T.font.size.xs,color:T.color.text.tertiary,fontFamily:T.font.mono}}>{e.date}</span>
              </div>
              <div style={{fontSize:T.font.size.sm,color:T.color.text.secondary,lineHeight:1.4}}>{e.detail}</div>
              <div style={{marginTop:'4px'}}>
                <span style={{fontSize:'10px',textTransform:'uppercase',letterSpacing:'0.06em',fontWeight:T.font.weight.medium,color:typeColors[e.type]||T.color.text.tertiary,background:`${typeColors[e.type]||T.color.text.tertiary}10`,padding:'1px 6px',borderRadius:'8px'}}>{e.type}</span>
              </div>
            </div>
          </div>
        ))}
      </div>
    </div>
  );
}

// ────────────────────────────────
// TAB: RESEARCH
// ────────────────────────────────
function ResearchTab({ m, data }) {
  const [hovered, setHovered] = useState(null);
  return (
    <div style={cs.body1col}>
      <div style={cs.sec}>
        <div style={cs.aiBox}>
          <div style={cs.aiLabel}>◆ AI Research Suggestions</div>
          Based on recent case activity, consider researching: summary judgment standards for fraud claims in this jurisdiction, recent damages award ranges in comparable commercial litigation, and admissibility of financial expert testimony post-Daubert.
        </div>
      </div>
      <div style={cs.sec}>
        <div style={cs.secH}><span>Research & Memos</span><span style={{fontSize:T.font.size.xs,color:T.color.text.tertiary,fontWeight:T.font.weight.normal}}>{data.research.length} items</span></div>
        {data.research.map((r,i)=>(
          <div key={i} style={{padding:'12px 16px',borderBottom:`1px solid ${T.color.border.light}`,cursor:'pointer',background:hovered===i?T.color.bg.hover:'transparent',transition:'background 0.1s'}} onMouseEnter={()=>setHovered(i)} onMouseLeave={()=>setHovered(null)}>
            <div style={{display:'flex',justifyContent:'space-between',alignItems:'baseline',marginBottom:'4px'}}>
              <span style={{fontWeight:T.font.weight.semibold,fontSize:T.font.size.sm,color:T.color.text.primary}}>{r.title}</span>
              <StatusTag status={r.status}/>
            </div>
            <div style={{fontSize:T.font.size.sm,color:T.color.text.secondary,lineHeight:1.4,marginBottom:'6px'}}>{r.summary}</div>
            <div style={{display:'flex',gap:'12px',fontSize:T.font.size.xs,color:T.color.text.tertiary}}>
              <span>{r.type}</span>
              <span>{r.author}</span>
              <span>{r.citations} citations</span>
              <span>{r.date}</span>
            </div>
          </div>
        ))}
      </div>
    </div>
  );
}

// ────────────────────────────────
// TAB: BILLING
// ────────────────────────────────
function BillingTab({ m, data }) {
  const b = data.billing;
  const budgetPct = Math.round((m.billed / m.budget) * 100);
  const totalHours = b.team.reduce((s,t)=>s+t.hours,0);
  const totalExpenses = b.expenses.reduce((s,e)=>s+e.amount,0);

  return (
    <div style={cs.body1col}>
      <div style={{display:'grid',gridTemplateColumns:'repeat(4,1fr)',gap:'12px',marginBottom:'16px'}}>
        <StatCard label="Total Billed" value={fmtCur(m.billed)} delta={`${budgetPct}% of budget`} deltaType=""/>
        <StatCard label="Total Hours" value={totalHours.toString()} delta={`${b.team.length} timekeepers`} deltaType=""/>
        <StatCard label="Expenses" value={fmtCur(totalExpenses)} delta="" deltaType=""/>
        <StatCard label="Budget Remaining" value={fmtCur(m.budget - m.billed)} delta={`${100-budgetPct}% available`} deltaType=""/>
      </div>

      <div style={{display:'grid',gridTemplateColumns:'1fr 1fr',gap:'16px',marginBottom:'16px'}}>
        {/* Timekeeper breakdown */}
        <div style={cs.sec}>
          <div style={cs.secH}><span>Timekeeper Summary</span></div>
          <div style={{display:'grid',gridTemplateColumns:'1fr 60px 60px 80px',padding:'6px 16px',background:T.color.bg.secondary,borderBottom:`1px solid ${T.color.border.light}`,fontSize:T.font.size.xs,fontWeight:T.font.weight.medium,color:T.color.text.tertiary,textTransform:'uppercase',letterSpacing:T.font.tracking.caps,gap:'8px'}}>
            <span>Name</span><span>Rate</span><span>Hours</span><span style={{textAlign:'right'}}>Billed</span>
          </div>
          {b.team.map((t,i)=>(
            <div key={i} style={{display:'grid',gridTemplateColumns:'1fr 60px 60px 80px',padding:'8px 16px',borderBottom:`1px solid ${T.color.border.light}`,fontSize:T.font.size.sm,alignItems:'center',gap:'8px'}}>
              <div>
                <div style={{fontWeight:T.font.weight.medium,color:T.color.text.primary}}>{t.name}</div>
                <div style={{fontSize:T.font.size.xs,color:T.color.text.tertiary}}>{t.role}</div>
              </div>
              <span style={{fontFamily:T.font.mono,color:T.color.text.secondary,fontSize:T.font.size.xs}}>${t.rate}</span>
              <span style={{fontFamily:T.font.mono,color:T.color.text.secondary,fontSize:T.font.size.xs}}>{t.hours}</span>
              <span style={{fontFamily:T.font.mono,fontWeight:T.font.weight.medium,color:T.color.text.primary,textAlign:'right',fontSize:T.font.size.xs}}>{fmtCur(t.billed)}</span>
            </div>
          ))}
        </div>

        {/* Phase breakdown */}
        <div style={cs.sec}>
          <div style={cs.secH}><span>Phase Breakdown</span></div>
          {b.phases.map((p,i)=>(
            <div key={i} style={{padding:'8px 16px',borderBottom:`1px solid ${T.color.border.light}`,fontSize:T.font.size.sm}}>
              <div style={{display:'flex',justifyContent:'space-between',marginBottom:'4px'}}>
                <span style={{fontWeight:T.font.weight.medium,color:T.color.text.primary}}>{p.phase}</span>
                <span style={{fontFamily:T.font.mono,fontSize:T.font.size.xs,color:T.color.text.secondary}}>{fmtCur(p.amount)} · {p.hours}h</span>
              </div>
              <div style={cs.barTrack}><div style={{...cs.barFill, width:`${p.pct}%`, background: p.pct===100?T.color.status.active:T.color.navy500}}/></div>
              <div style={{fontSize:T.font.size.xs,color:T.color.text.tertiary,marginTop:'2px'}}>{p.pct}% complete</div>
            </div>
          ))}
        </div>
      </div>

      <div style={{display:'grid',gridTemplateColumns:'1fr 1fr',gap:'16px'}}>
        {/* Expenses */}
        <div style={cs.sec}>
          <div style={cs.secH}><span>Expenses</span></div>
          {b.expenses.map((e,i)=>(
            <div key={i} style={{padding:'8px 16px',borderBottom:`1px solid ${T.color.border.light}`,display:'flex',justifyContent:'space-between',fontSize:T.font.size.sm,alignItems:'center'}}>
              <div>
                <div style={{fontWeight:T.font.weight.medium,color:T.color.text.primary}}>{e.desc}</div>
                <div style={{fontSize:T.font.size.xs,color:T.color.text.tertiary}}>{e.date}</div>
              </div>
              <span style={{fontFamily:T.font.mono,fontWeight:T.font.weight.medium}}>{fmtCur(e.amount)}</span>
            </div>
          ))}
        </div>

        {/* Invoices */}
        <div style={cs.sec}>
          <div style={cs.secH}><span>Invoices</span></div>
          {b.invoices.map((inv,i)=>(
            <div key={i} style={{padding:'8px 16px',borderBottom:`1px solid ${T.color.border.light}`,display:'flex',justifyContent:'space-between',fontSize:T.font.size.sm,alignItems:'center'}}>
              <div>
                <div style={{fontWeight:T.font.weight.medium,color:T.color.text.primary}}>{inv.number}</div>
                <div style={{fontSize:T.font.size.xs,color:T.color.text.tertiary}}>{inv.period}</div>
              </div>
              <div style={{textAlign:'right'}}>
                <div style={{fontFamily:T.font.mono,fontWeight:T.font.weight.medium}}>{inv.amount?fmtCur(inv.amount):'—'}</div>
                <StatusTag status={inv.status}/>
              </div>
            </div>
          ))}
        </div>
      </div>
    </div>
  );
}

// ────────────────────────────────
// TAB: NOTES
// ────────────────────────────────
function NotesTab({ m, data }) {
  const [newNote, setNewNote] = useState('');
  const notes = data.notes;
  const pinned = notes.filter(n=>n.pinned);
  const unpinned = notes.filter(n=>!n.pinned);

  const NoteCard = ({ note }) => (
    <div style={{padding:'14px 16px',borderBottom:`1px solid ${T.color.border.light}`}}>
      <div style={{display:'flex',gap:'10px',alignItems:'flex-start'}}>
        <div style={{...cs.avatar, background:T.color.navy700, color:T.color.ivory100, width:'28px',height:'28px',fontSize:'10px'}}>{note.initials}</div>
        <div style={{flex:1}}>
          <div style={{display:'flex',justifyContent:'space-between',alignItems:'baseline',marginBottom:'4px'}}>
            <span style={{fontWeight:T.font.weight.semibold,fontSize:T.font.size.sm,color:T.color.text.primary}}>{note.author}</span>
            <span style={{fontSize:T.font.size.xs,color:T.color.text.tertiary}}>{note.date}{note.pinned?' · ':''}</span>
          </div>
          <div style={{fontSize:T.font.size.sm,color:T.color.text.secondary,lineHeight:1.5}}>{note.content}</div>
          {note.replies.length > 0 && (
            <div style={{marginTop:'10px',paddingLeft:'12px',borderLeft:`2px solid ${T.color.border.light}`}}>
              {note.replies.map((r,j)=>(
                <div key={j} style={{marginBottom: j<note.replies.length-1?'10px':'0'}}>
                  <div style={{display:'flex',gap:'6px',alignItems:'baseline',marginBottom:'2px'}}>
                    <span style={{fontWeight:T.font.weight.semibold,fontSize:T.font.size.xs,color:T.color.text.primary}}>{r.author}</span>
                    <span style={{fontSize:'10px',color:T.color.text.tertiary}}>{r.date}</span>
                  </div>
                  <div style={{fontSize:T.font.size.sm,color:T.color.text.secondary,lineHeight:1.4}}>{r.content}</div>
                </div>
              ))}
            </div>
          )}
        </div>
      </div>
    </div>
  );

  return (
    <div style={cs.body1col}>
      {/* Compose */}
      <div style={{...cs.sec, marginBottom:'16px'}}>
        <div style={{padding:'12px 16px'}}>
          <textarea
            value={newNote}
            onChange={e=>setNewNote(e.target.value)}
            placeholder="Add a note to this matter..."
            style={{width:'100%',minHeight:'64px',border:`1px solid ${T.color.border.light}`,borderRadius:T.radius.md,padding:'8px 12px',fontSize:T.font.size.sm,fontFamily:T.font.family,color:T.color.text.primary,background:T.color.bg.primary,outline:'none',resize:'vertical'}}
          />
          <div style={{display:'flex',justifyContent:'flex-end',marginTop:'8px',gap:'8px'}}>
            <button style={window.sharedStyles.btn}>@ Mention</button>
            <button style={{...window.sharedStyles.btn,...window.sharedStyles.btnPrimary}}>Post Note</button>
          </div>
        </div>
      </div>

      {pinned.length > 0 && (
        <div style={cs.sec}>
          <div style={cs.secH}><span> Pinned</span></div>
          {pinned.map(n => <NoteCard key={n.id} note={n}/>)}
        </div>
      )}

      <div style={cs.sec}>
        <div style={cs.secH}><span>All Notes</span><span style={{fontSize:T.font.size.xs,color:T.color.text.tertiary,fontWeight:T.font.weight.normal}}>{notes.length} notes</span></div>
        {[...pinned,...unpinned].map(n => <NoteCard key={n.id} note={n}/>)}
      </div>
    </div>
  );
}

// ────────────────────────────────
// MAIN CASE DETAIL
// ────────────────────────────────
function CaseDetail({ matter, onBack, onOpenWarRoom, onOpenEvidence, onOpenRisk, onOpenESI }) {
  const [activeTab, setActiveTab] = useState('overview');
  const tabs = ['Overview','Documents','Timeline','Research','Billing','Notes'];
  const m = matter;
  const data = useMemo(() => getMatterData(m), [m.id]);
  const budgetPct = Math.round((m.billed / m.budget) * 100);

  const renderTab = () => {
    switch(activeTab) {
      case 'documents': return <DocumentsTab m={m} data={data}/>;
      case 'timeline': return <TimelineTab m={m} data={data}/>;
      case 'research': return <ResearchTab m={m} data={data}/>;
      case 'billing': return <BillingTab m={m} data={data}/>;
      case 'notes': return <NotesTab m={m} data={data}/>;
      default: return <OverviewTab m={m} data={data} onOpenWarRoom={onOpenWarRoom} onOpenEvidence={onOpenEvidence} onOpenRisk={onOpenRisk} onOpenESI={onOpenESI}/>;
    }
  };

  return (
    <div style={cs.container}>
      <div style={cs.hero}>
        <div style={cs.heroTop}>
          <div>
            <div style={cs.matterName}>{m.name}</div>
            <div style={cs.matterId}>{m.id} · {m.type} · {m.client}</div>
          </div>
          <div style={{display:'flex',gap:'8px',alignItems:'center'}}>
            <StatusTag status={m.status}/>
            <button onClick={() => onOpenWarRoom && onOpenWarRoom(m)} style={{
              padding: '6px 14px', borderRadius: '6px',
              background: 'linear-gradient(135deg, #EF4444 0%, #B91C1C 100%)',
              border: 'none', color: '#fff', fontSize: '12px', fontWeight: 700,
              cursor: 'pointer', fontFamily: T.font.family, letterSpacing: '0.04em',
              textTransform: 'uppercase', display: 'flex', alignItems: 'center', gap: '6px',
            }}> War Room</button>
            <button onClick={() => onOpenEvidence && onOpenEvidence(m)} style={{
              padding: '6px 14px', borderRadius: '6px',
              background: 'linear-gradient(135deg, #0D9488 0%, #0F766E 100%)',
              border: 'none', color: '#fff', fontSize: '12px', fontWeight: 700,
              cursor: 'pointer', fontFamily: T.font.family, letterSpacing: '0.04em',
              textTransform: 'uppercase', display: 'flex', alignItems: 'center', gap: '6px',
            }}>◆ Evidence</button>
            <button onClick={() => onOpenRisk && onOpenRisk(m)} style={{
              padding: '6px 14px', borderRadius: '6px',
              background: 'linear-gradient(135deg, #B91C1C 0%, #991B1B 100%)',
              border: 'none', color: '#fff', fontSize: '12px', fontWeight: 700,
              cursor: 'pointer', fontFamily: T.font.family, letterSpacing: '0.04em',
              textTransform: 'uppercase', display: 'flex', alignItems: 'center', gap: '6px',
            }}>flag Risk</button>
            <button onClick={() => onOpenESI && onOpenESI(m)} style={{
              padding: '6px 14px', borderRadius: '6px',
              background: 'linear-gradient(135deg, #0891B2 0%, #0E7490 100%)',
              border: 'none', color: '#fff', fontSize: '12px', fontWeight: 700,
              cursor: 'pointer', fontFamily: T.font.family, letterSpacing: '0.04em',
              textTransform: 'uppercase', display: 'flex', alignItems: 'center', gap: '6px',
            }}>◇ ESI</button>
            <button style={{...window.sharedStyles.btn,...window.sharedStyles.btnPrimary}}>Actions ▾</button>
          </div>
        </div>
        <div style={cs.metaRow}>
          {[
            ['Lead', m.lead],
            ['Team', `${m.team} members`],
            ['Billed', `${fmtCur(m.billed)} / ${fmtCur(m.budget)}`],
            ['Budget Used', budgetPct+'%', budgetPct>85],
            ['Documents', m.docs.toLocaleString()],
            ['Next Deadline', new Date(m.dueDate).toLocaleDateString('en-US',{month:'short',day:'numeric',year:'numeric'})],
          ].map(([label,val,warn],i)=>(
            <div key={i} style={cs.metaItem}>
              <span style={cs.metaLabel}>{label}</span>
              <span style={{...cs.metaValue,...(warn?{color:T.color.status.warning}:{})}}>{val}</span>
            </div>
          ))}
        </div>
      </div>
      <div style={cs.tabs}>
        {tabs.map(t=>(
          <button key={t} onClick={()=>setActiveTab(t.toLowerCase())} style={{...cs.tab,...(activeTab===t.toLowerCase()?cs.tabActive:{})}}>{t}</button>
        ))}
      </div>
      {renderTab()}
    </div>
  );
}

window.CaseDetail = CaseDetail;
