// RULEBOOK OPPORTUNITIES — 18 components + 4 hubs (pattern: RiskOpps.jsx, CalendarOpps.jsx)
(function () {
  const { useState, useMemo, useEffect } = React;
  const T = window.ArbiterTokens;
  const rb = window.rb;
  const cal = window.cal;

  // ── Shared helpers ────────────────────────────────────────
  const statColor = (sev) => sev === 'critical' ? '#C23030' : sev === 'high' ? '#D97706' : '#6E7D9E';

  const RbSubTabs = ({ tabs, active, onChange }) => (
    <div style={{ display: 'flex', gap: '0', borderBottom: `1px solid ${T.color.border.light}`, marginBottom: '16px', overflowX: 'auto' }}>
      {tabs.map(t => (
        <button key={t.id} onClick={() => onChange(t.id)} style={{
          padding: '8px 14px', fontSize: '11px', fontWeight: active === t.id ? 600 : 500,
          color: active === t.id ? rb.navy : T.color.text.tertiary,
          cursor: 'pointer', border: 'none', background: 'none',
          borderBottom: active === t.id ? `2px solid ${rb.navy}` : '2px solid transparent',
          fontFamily: T.font.family, whiteSpace: 'nowrap', marginBottom: '-1px',
        }}>{t.label}</button>
      ))}
    </div>
  );
  window.RbSubTabs = RbSubTabs;

  const Card = ({ title, right, children }) => (
    <div style={rb.card}>
      <div style={rb.cardH}><span>{title}</span>{right && <span style={{ fontSize: '10px', color: T.color.text.tertiary }}>{right}</span>}</div>
      <div>{children}</div>
    </div>
  );

  const TH = ({ children, w }) => (<th style={{ padding: '8px 12px', fontSize: '9px', fontWeight: 600, color: T.color.text.tertiary, textTransform: 'uppercase', letterSpacing: '0.06em', textAlign: 'left', borderBottom: `1px solid ${T.color.border.light}`, background: T.color.bg.secondary, width: w || 'auto', whiteSpace: 'nowrap' }}>{children}</th>);
  const TD = ({ children, mono, color, bold, w }) => (<td style={{ padding: '8px 12px', fontSize: '11px', borderBottom: `1px solid ${T.color.border.light}`, fontFamily: mono ? T.font.mono : T.font.family, color: color || T.color.text.primary, fontWeight: bold ? 700 : 400, width: w || 'auto' }}>{children}</td>);

  const Pill = ({ children, color }) => (<span style={{ ...rb.tag, background: `${color}15`, color }}>{children}</span>);

  // ══════════════════════════════════════════════════════════
  //   KNOWLEDGE HUB — FRE, FRAP, Judge Orders, State Rules, Amendments
  // ══════════════════════════════════════════════════════════

  // #1 FRE Browser
  const RbFRE = () => {
    const data = window.RULEBOOK_DATA;
    const [search, setSearch] = useState('');
    const [cat, setCat] = useState('All');
    const [exp, setExp] = useState(null);
    const cats = ['All', ...new Set(data.fre.map(r => r.category))];
    const filtered = data.fre.filter(r =>
      (cat === 'All' || r.category === cat) &&
      (!search || (r.rule + ' ' + r.title + ' ' + r.summary).toLowerCase().includes(search.toLowerCase()))
    );
    return (
      <div>
        <div style={{ display: 'flex', gap: '8px', marginBottom: '12px', flexWrap: 'wrap', alignItems: 'center' }}>
          <input style={{ ...rb.input, width: '200px' }} placeholder="Search FRE…" value={search} onChange={e => setSearch(e.target.value)} />
          <select value={cat} onChange={e => setCat(e.target.value)} style={{ ...rb.btn, padding: '4px 8px' }}>
            {cats.map(c => <option key={c} value={c}>{c}</option>)}
          </select>
          <span style={{ fontSize: '10px', color: T.color.text.tertiary }}>{filtered.length} rules</span>
        </div>
        <Card title="Federal Rules of Evidence" right={`${data.fre.length} total · ${data.fre.filter(r => r.critical).length} critical`}>
          {filtered.map(r => {
            const open = exp === r.id;
            return (
              <div key={r.id} style={{ borderBottom: `1px solid ${T.color.border.light}` }}>
                <div style={{ padding: '10px 16px', display: 'flex', alignItems: 'center', gap: '10px', cursor: 'pointer' }} onClick={() => setExp(open ? null : r.id)}>
                  <span style={{ fontFamily: T.font.mono, fontWeight: 700, color: rb.navy, minWidth: '72px', fontSize: '11px' }}>{r.rule}</span>
                  <div style={{ flex: 1 }}>
                    <div style={{ fontWeight: 600, fontSize: '12px', color: T.color.text.primary }}>{r.title}</div>
                    <div style={{ fontSize: '10px', color: T.color.text.secondary, marginTop: '1px' }}>{r.summary.length > 100 ? r.summary.slice(0, 100) + '…' : r.summary}</div>
                  </div>
                  <Pill color={T.color.text.tertiary}>{r.category}</Pill>
                  {r.critical && <Pill color="#C23030">critical</Pill>}
                  <span style={{ fontSize: '12px', color: T.color.text.tertiary }}>{open ? '▴' : '▾'}</span>
                </div>
                {open && (
                  <div style={{ padding: '10px 16px 12px 96px', background: T.color.bg.secondary, fontSize: '11px', color: T.color.text.secondary, lineHeight: 1.5 }}>
                    <div style={{ marginBottom: '8px' }}>{r.summary}</div>
                    {r.practiceNote && <div style={{ padding: '6px 10px', background: rb.navyBg, borderRadius: '4px', borderLeft: `3px solid ${rb.navy}`, marginBottom: '8px' }}><b style={{ color: rb.navy }}>Practice: </b>{r.practiceNote}</div>}
                    {r.deadlines && <div>{r.deadlines.map((d, i) => <div key={i} style={{ padding: '4px 0', display: 'flex', gap: '10px' }}><span style={{ fontFamily: T.font.mono, color: rb.navy, fontWeight: 700 }}>{d.days > 0 ? '+' : ''}{d.days}d</span> <span>{d.desc}</span></div>)}</div>}
                  </div>
                )}
              </div>
            );
          })}
        </Card>
      </div>
    );
  };

  // #2 FRAP Expansion
  const RbFRAP = () => {
    const data = window.RULEBOOK_DATA;
    return (
      <div>
        <Card title="Federal Rules of Appellate Procedure" right={`${data.frap.length} rules covered · ALL jurisdictional deadlines`}>
          <table style={{ width: '100%', borderCollapse: 'collapse' }}>
            <thead>
              <tr>
                <TH w="80px">Rule</TH>
                <TH>Title</TH>
                <TH>Summary</TH>
                <TH w="200px">Deadlines</TH>
              </tr>
            </thead>
            <tbody>
              {data.frap.map(r => (
                <tr key={r.id}>
                  <TD mono bold color={rb.navy}>{r.rule}</TD>
                  <TD bold>{r.title}</TD>
                  <TD color={T.color.text.secondary}>{r.summary}</TD>
                  <TD>
                    {(r.deadlines || []).map((d, i) => (
                      <div key={i} style={{ fontSize: '10px', marginBottom: '2px' }}>
                        <span style={{ fontFamily: T.font.mono, fontWeight: 700, color: d.critical ? '#C23030' : rb.navy }}>{d.days > 0 ? '+' : ''}{d.days}d</span> <span style={{ color: T.color.text.secondary }}>{d.desc.slice(0, 60)}</span>
                      </div>
                    ))}
                    {(!r.deadlines || r.deadlines.length === 0) && <span style={{ color: T.color.text.tertiary, fontSize: '10px' }}>—</span>}
                  </TD>
                </tr>
              ))}
            </tbody>
          </table>
        </Card>
      </div>
    );
  };

  // #3 Judge Standing Orders
  const RbJudgeOrders = () => {
    const data = window.RULEBOOK_DATA;
    const [sel, setSel] = useState(data.judgeOrders[0].id);
    const judge = data.judgeOrders.find(j => j.id === sel);
    return (
      <div>
        <div style={{ display: 'flex', gap: '6px', marginBottom: '12px', flexWrap: 'wrap' }}>
          {data.judgeOrders.map(j => (
            <button key={j.id} onClick={() => setSel(j.id)} style={{ ...rb.btn, ...(sel === j.id ? rb.btnActive : {}) }}>{j.judge} · {j.court}</button>
          ))}
        </div>
        <div style={{ display: 'grid', gridTemplateColumns: '1fr 280px', gap: '16px', alignItems: 'start' }}>
          <Card title={`${judge.judge} — Standing Orders`} right={judge.chambers}>
            {judge.orders.map((o, i) => (
              <div key={i} style={{ padding: '10px 16px', borderBottom: `1px solid ${T.color.border.light}` }}>
                <div style={{ fontSize: '10px', fontWeight: 700, color: rb.navy, textTransform: 'uppercase', letterSpacing: '0.06em', marginBottom: '4px' }}>{o.topic}</div>
                <div style={{ fontSize: '11px', color: T.color.text.secondary, lineHeight: 1.5 }}>{o.text}</div>
              </div>
            ))}
          </Card>
          <Card title="Tendencies">
            {[
              ['MSJ Grant Rate', `${Math.round(judge.tendencies.msjGrantRate * 100)}%`, judge.tendencies.msjGrantRate > 0.35 ? '#C23030' : rb.green],
              ['Avg Days to Ruling', `${judge.tendencies.avgTimeToRuling}d`, rb.navy],
              ['MTC Grant Rate', `${Math.round(judge.tendencies.motionToCompelGrantRate * 100)}%`, rb.amber],
              ['Avg Trial Days', `${judge.tendencies.trialDaysAvg}`, T.color.text.primary],
            ].map(([k, v, c], i) => (
              <div key={i} style={{ padding: '10px 16px', borderBottom: `1px solid ${T.color.border.light}`, display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
                <span style={{ fontSize: '10px', color: T.color.text.tertiary, fontWeight: 600, textTransform: 'uppercase', letterSpacing: '0.06em' }}>{k}</span>
                <span style={{ fontFamily: T.font.mono, fontSize: '14px', fontWeight: 700, color: c }}>{v}</span>
              </div>
            ))}
          </Card>
        </div>
      </div>
    );
  };

  // #4 State Rules Comparison
  const RbStateRules = () => {
    const data = window.RULEBOOK_DATA;
    return (
      <Card title="State vs Federal Rules — Parallel Provisions" right="CA · NY · TX">
        <table style={{ width: '100%', borderCollapse: 'collapse' }}>
          <thead>
            <tr>
              <TH w="160px">Topic</TH>
              <TH>FRCP</TH>
              <TH>California</TH>
              <TH>New York</TH>
              <TH>Texas</TH>
            </tr>
          </thead>
          <tbody>
            {data.stateRules.map((s, i) => (
              <React.Fragment key={i}>
                <tr>
                  <TD bold color={rb.navy}>{s.topic}</TD>
                  <TD color={T.color.text.primary}>{s.frcp}</TD>
                  <TD color={T.color.text.primary}>{s.ca}</TD>
                  <TD color={T.color.text.primary}>{s.ny}</TD>
                  <TD color={T.color.text.primary}>{s.tx}</TD>
                </tr>
                <tr>
                  <td colSpan={5} style={{ padding: '4px 12px 10px', fontSize: '10px', color: T.color.text.tertiary, borderBottom: `1px solid ${T.color.border.light}`, background: T.color.bg.secondary, fontStyle: 'italic' }}>
                    {s.note}
                  </td>
                </tr>
              </React.Fragment>
            ))}
          </tbody>
        </table>
      </Card>
    );
  };

  // #5 Historical Amendments
  const RbAmendments = () => {
    const data = window.RULEBOOK_DATA;
    const sorted = [...data.amendments].sort((a, b) => b.year - a.year);
    return (
      <Card title="Historical FRCP Amendments" right="Tracked 2015-present">
        {sorted.map((a, i) => (
          <div key={i} style={{ padding: '10px 16px', borderBottom: `1px solid ${T.color.border.light}`, display: 'grid', gridTemplateColumns: '72px 96px 1fr 320px', gap: '12px', alignItems: 'start' }}>
            <span style={{ fontFamily: T.font.mono, fontSize: '13px', fontWeight: 700, color: rb.navy }}>{a.year}</span>
            <span style={{ fontFamily: T.font.mono, fontSize: '11px', fontWeight: 700, color: a.critical ? '#C23030' : T.color.text.primary }}>{a.rule}</span>
            <div style={{ fontSize: '11px', color: T.color.text.primary, lineHeight: 1.5 }}>{a.change}</div>
            <div style={{ fontSize: '10px', color: T.color.text.secondary, fontStyle: 'italic', lineHeight: 1.5 }}>
              <b style={{ color: a.critical ? '#C23030' : rb.navy, fontStyle: 'normal' }}>Impact: </b>{a.impact}
            </div>
          </div>
        ))}
      </Card>
    );
  };

  // ══════════════════════════════════════════════════════════
  //   TOOLS HUB — Response Wizard, Conflict Detector, Templates, Sanctions, R68, Service Calc
  // ══════════════════════════════════════════════════════════

  // #6 Deadline Conflict Detector
  const RbConflicts = () => {
    const [matterA, setMatterA] = useState('M-2024-0312 — Redstone v. Meridian');
    const [matterB, setMatterB] = useState('M-2025-0154 — Hawthorne');
    // Demo conflicts
    const conflicts = [
      { date: '2026-05-14', matterA: 'MSJ opposition — M-2024-0312', matterB: 'Daubert motion — M-2025-0154', severity: 'critical', staff: 'M. Kirkland', overlap: 'Same attorney, same day, both dispositive' },
      { date: '2026-05-21', matterA: 'Deposition — R. Davis', matterB: 'Deposition — K. Strand', severity: 'high', staff: 'L. Torres', overlap: 'Same attorney taking two depositions' },
      { date: '2026-06-02', matterA: 'Pretrial conf — Redstone', matterB: 'MSJ oral arg — Hawthorne', severity: 'critical', staff: 'M. Kirkland', overlap: 'Two in-court appearances, conflicting start times' },
    ];
    return (
      <div>
        <div style={{ display: 'flex', gap: '12px', marginBottom: '12px', flexWrap: 'wrap' }}>
          <div style={{ flex: 1, minWidth: '240px' }}>
            <div style={{ fontSize: '9px', color: T.color.text.tertiary, fontWeight: 600, textTransform: 'uppercase', marginBottom: '4px' }}>Matter A</div>
            <input style={{ ...rb.input, width: '100%' }} value={matterA} onChange={e => setMatterA(e.target.value)} />
          </div>
          <div style={{ flex: 1, minWidth: '240px' }}>
            <div style={{ fontSize: '9px', color: T.color.text.tertiary, fontWeight: 600, textTransform: 'uppercase', marginBottom: '4px' }}>Matter B</div>
            <input style={{ ...rb.input, width: '100%' }} value={matterB} onChange={e => setMatterB(e.target.value)} />
          </div>
        </div>
        <Card title="Detected Conflicts" right={`${conflicts.length} overlaps · ${conflicts.filter(c => c.severity === 'critical').length} critical`}>
          {conflicts.map((c, i) => (
            <div key={i} style={{ padding: '12px 16px', borderBottom: `1px solid ${T.color.border.light}`, borderLeft: `3px solid ${statColor(c.severity)}` }}>
              <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '6px' }}>
                <span style={{ fontFamily: T.font.mono, fontSize: '12px', fontWeight: 700, color: rb.navy }}>{c.date}</span>
                <Pill color={statColor(c.severity)}>{c.severity}</Pill>
              </div>
              <div style={{ fontSize: '11px', color: T.color.text.primary, marginBottom: '2px' }}><b>A:</b> {c.matterA}</div>
              <div style={{ fontSize: '11px', color: T.color.text.primary, marginBottom: '4px' }}><b>B:</b> {c.matterB}</div>
              <div style={{ fontSize: '10px', color: T.color.text.tertiary, fontStyle: 'italic' }}>{c.overlap} · Staff: {c.staff}</div>
            </div>
          ))}
        </Card>
      </div>
    );
  };

  // #7 Responsive Pleading Wizard
  const RbResponseWizard = () => {
    const [docType, setDocType] = useState('complaint');
    const [servedDate, setServedDate] = useState('2026-04-20');

    const WIZARDS = {
      complaint: { label: 'Complaint Served', tasks: [
        { rule: 'Rule 12(a)(1)', days: 21, desc: 'Answer due', critical: true },
        { rule: 'Rule 12(b)', days: 21, desc: 'Pre-answer motion (in lieu of answer)', critical: true },
        { rule: 'Rule 15(a)(1)', days: 21, desc: 'Plaintiff amendment as-of-right window closes', critical: false },
        { rule: 'Rule 38(b)', days: 14, desc: 'Jury demand (14 days after last pleading)', critical: true },
        { rule: 'Rule 13', days: 21, desc: 'Compulsory counterclaims must be pled', critical: true },
        { rule: 'Rule 7.1', days: 21, desc: 'Corporate disclosure statement', critical: false },
        { rule: 'Rule 14(a)', days: 35, desc: 'Third-party complaint (14 days post-answer)', critical: false },
      ]},
      msj: { label: 'MSJ Served', tasks: [
        { rule: 'Local Rule 56.1', days: 21, desc: 'Opposition due (counter-statement of facts)', critical: true },
        { rule: 'Rule 56(c)', days: 21, desc: 'Memorandum in opposition', critical: true },
        { rule: 'Rule 56(d)', days: 21, desc: 'Option: affidavit requesting discovery before ruling', critical: false },
        { rule: 'Rule 56', days: 28, desc: 'Reply brief (if permitted)', critical: false },
      ]},
      rfa: { label: 'RFA Served', tasks: [
        { rule: 'Rule 36(a)(3)', days: 30, desc: 'Responses due — DEEMED ADMITTED if missed', critical: true },
        { rule: 'Rule 36(a)(4)', days: 30, desc: 'Specific denials required; qualified responses permitted', critical: true },
      ]},
      subpoena: { label: 'Subpoena Received', tasks: [
        { rule: 'Rule 45(d)(2)(B)', days: 14, desc: 'Written objections (EARLIER of 14 days or compliance date)', critical: true },
        { rule: 'Rule 45(d)(3)', days: 0, desc: 'Motion to quash/modify — before compliance date', critical: true },
        { rule: 'Privilege log', days: 14, desc: 'Privilege log if withholding responsive docs', critical: false },
      ]},
      judgment: { label: 'Judgment Entered', tasks: [
        { rule: 'Rule 54(d)(2)', days: 14, desc: 'Attorney fees motion', critical: true },
        { rule: 'Rule 50(b)', days: 28, desc: 'Renewed JMOL', critical: true },
        { rule: 'Rule 52(b)', days: 28, desc: 'Amend findings (bench trial)', critical: true },
        { rule: 'Rule 59(b)', days: 28, desc: 'New trial motion', critical: true },
        { rule: 'Rule 59(e)', days: 28, desc: 'Motion to alter/amend judgment', critical: true },
        { rule: 'FRAP 4(a)', days: 30, desc: 'Notice of appeal — JURISDICTIONAL', critical: true },
        { rule: 'Rule 62(a)', days: 30, desc: 'Automatic stay of execution expires', critical: false },
        { rule: 'Rule 60(b)', days: 365, desc: 'Relief from judgment (up to 1 year)', critical: false },
      ]},
    };

    const w = WIZARDS[docType];
    const base = new Date(servedDate + 'T12:00:00');
    const rows = w.tasks.map(t => {
      const d = new Date(base);
      d.setDate(d.getDate() + t.days);
      return { ...t, dueDate: d.toISOString().slice(0, 10), dueDay: d.toLocaleDateString('en-US', { weekday: 'short' }) };
    });

    return (
      <div>
        <div style={{ display: 'flex', gap: '12px', marginBottom: '12px', alignItems: 'flex-end', flexWrap: 'wrap' }}>
          <div>
            <div style={{ fontSize: '9px', color: T.color.text.tertiary, fontWeight: 600, textTransform: 'uppercase', marginBottom: '4px' }}>Document Type</div>
            <select value={docType} onChange={e => setDocType(e.target.value)} style={{ ...rb.input, width: '200px' }}>
              {Object.entries(WIZARDS).map(([k, v]) => <option key={k} value={k}>{v.label}</option>)}
            </select>
          </div>
          <div>
            <div style={{ fontSize: '9px', color: T.color.text.tertiary, fontWeight: 600, textTransform: 'uppercase', marginBottom: '4px' }}>Served / Trigger Date</div>
            <input type="date" value={servedDate} onChange={e => setServedDate(e.target.value)} style={{ ...rb.input, width: '160px' }} />
          </div>
        </div>
        <Card title={`${w.label} — Response Checklist`} right={`${rows.length} deadlines · ${rows.filter(r => r.critical).length} critical`}>
          <table style={{ width: '100%', borderCollapse: 'collapse' }}>
            <thead>
              <tr><TH w="90px">Rule</TH><TH>Task</TH><TH w="60px">Days</TH><TH w="140px">Due</TH><TH w="70px">Severity</TH></tr>
            </thead>
            <tbody>
              {rows.map((r, i) => (
                <tr key={i} style={{ borderLeft: `3px solid ${r.critical ? '#C23030' : 'transparent'}` }}>
                  <TD mono bold color={rb.navy}>{r.rule}</TD>
                  <TD>{r.desc}</TD>
                  <TD mono color={T.color.text.tertiary}>+{r.days}d</TD>
                  <TD mono bold>{r.dueDay} {r.dueDate}</TD>
                  <TD>{r.critical ? <Pill color="#C23030">critical</Pill> : <Pill color={T.color.text.tertiary}>standard</Pill>}</TD>
                </tr>
              ))}
            </tbody>
          </table>
        </Card>
      </div>
    );
  };

  // #8 Motion Template Library
  const RbMotionLibrary = () => {
    const data = window.RULEBOOK_DATA;
    const [sel, setSel] = useState(data.motionTemplates[0].id);
    const m = data.motionTemplates.find(t => t.id === sel);
    return (
      <div>
        <div style={{ display: 'flex', gap: '6px', marginBottom: '12px', flexWrap: 'wrap' }}>
          {data.motionTemplates.map(t => (
            <button key={t.id} onClick={() => setSel(t.id)} style={{ ...rb.btn, ...(sel === t.id ? rb.btnActive : {}) }}>{t.rule}</button>
          ))}
        </div>
        <div style={{ display: 'grid', gridTemplateColumns: '1fr 260px', gap: '16px', alignItems: 'start' }}>
          <Card title={m.name} right={m.rule}>
            <div style={{ padding: '12px 16px', fontSize: '11px', color: T.color.text.secondary, background: rb.navyBg, borderBottom: `1px solid ${T.color.border.light}` }}>
              Standard length: <b>{m.standardLength} pages</b> · Meet-and-confer required: <b>{m.meetConferReq ? 'YES' : 'no'}</b>
              {m.critical && <div style={{ marginTop: '6px', color: '#C23030', fontWeight: 600 }}>! {m.critical}</div>}
              {m.localRequirements && <div style={{ marginTop: '6px', color: rb.navy }}>Local: {m.localRequirements}</div>}
            </div>
            <div style={{ padding: '0 16px' }}>
              <div style={{ fontSize: '10px', fontWeight: 700, color: T.color.text.tertiary, textTransform: 'uppercase', letterSpacing: '0.06em', padding: '12px 0 8px' }}>Standard Sections</div>
              {m.sections.map((s, i) => (
                <div key={i} style={{ padding: '8px 0', borderBottom: i < m.sections.length - 1 ? `1px solid ${T.color.border.light}` : 'none', display: 'flex', gap: '10px', alignItems: 'center' }}>
                  <span style={{ fontFamily: T.font.mono, fontSize: '10px', color: rb.navy, fontWeight: 700, minWidth: '20px' }}>{i + 1}.</span>
                  <span style={{ fontSize: '11px', color: T.color.text.primary }}>{s}</span>
                </div>
              ))}
            </div>
            <div style={{ padding: '12px 16px', borderTop: `1px solid ${T.color.border.light}`, display: 'flex', gap: '6px' }}>
              <button style={{ ...rb.btn, ...rb.btnActive }}> Copy Outline</button>
              <button style={rb.btn}> Export .docx</button>
            </div>
          </Card>
          <Card title="All Templates">
            {data.motionTemplates.map(t => (
              <div key={t.id} onClick={() => setSel(t.id)} style={{ padding: '8px 16px', borderBottom: `1px solid ${T.color.border.light}`, cursor: 'pointer', background: sel === t.id ? rb.navyBg : 'transparent' }}>
                <div style={{ fontFamily: T.font.mono, fontSize: '10px', fontWeight: 700, color: rb.navy }}>{t.rule}</div>
                <div style={{ fontSize: '11px', color: T.color.text.primary }}>{t.name}</div>
              </div>
            ))}
          </Card>
        </div>
      </div>
    );
  };

  // #9 Sanctions Risk Analyzer
  const RbSanctions = () => {
    const data = window.RULEBOOK_DATA;
    const [sel, setSel] = useState(data.sanctionsFactors[0].id);
    const [answers, setAnswers] = useState({});
    const tree = data.sanctionsFactors.find(t => t.id === sel);
    const score = tree.factors.reduce((s, f, i) => s + (answers[sel + '-' + i] ? f.weight : 0), 0);
    const tier = score >= tree.riskTiers.critical ? 'CRITICAL' : score >= tree.riskTiers.high ? 'HIGH' : 'LOW';
    const tierColor = tier === 'CRITICAL' ? '#C23030' : tier === 'HIGH' ? '#D97706' : rb.green;
    return (
      <div>
        <div style={{ display: 'flex', gap: '6px', marginBottom: '12px', flexWrap: 'wrap' }}>
          {data.sanctionsFactors.map(t => (
            <button key={t.id} onClick={() => { setSel(t.id); setAnswers({}); }} style={{ ...rb.btn, ...(sel === t.id ? rb.btnActive : {}) }}>{t.rule}</button>
          ))}
        </div>
        <div style={{ display: 'grid', gridTemplateColumns: '1fr 260px', gap: '16px', alignItems: 'start' }}>
          <Card title={`${tree.rule} Sanctions — Risk Factors`} right={tree.safeHarbor}>
            {tree.factors.map((f, i) => {
              const key = sel + '-' + i;
              return (
                <label key={i} style={{ padding: '10px 16px', borderBottom: `1px solid ${T.color.border.light}`, display: 'flex', gap: '10px', alignItems: 'center', cursor: 'pointer', borderLeft: f.critical ? `3px solid #C23030` : f.favorable ? `3px solid ${rb.green}` : '3px solid transparent' }}>
                  <input type="checkbox" checked={!!answers[key]} onChange={e => setAnswers({ ...answers, [key]: e.target.checked })} />
                  <span style={{ flex: 1, fontSize: '11px', color: T.color.text.primary }}>{f.q}</span>
                  <span style={{ fontFamily: T.font.mono, fontSize: '11px', fontWeight: 700, color: f.favorable ? rb.green : statColor(f.critical ? 'critical' : 'high') }}>
                    {f.favorable ? '' : '+'}{f.weight}
                  </span>
                </label>
              );
            })}
          </Card>
          <Card title="Risk Assessment">
            <div style={{ padding: '16px', textAlign: 'center' }}>
              <div style={{ fontSize: '10px', color: T.color.text.tertiary, fontWeight: 600, textTransform: 'uppercase', letterSpacing: '0.06em' }}>Score</div>
              <div style={{ fontSize: '42px', fontFamily: T.font.mono, fontWeight: 700, color: tierColor, lineHeight: 1 }}>{score}</div>
              <div style={{ padding: '4px 12px', background: `${tierColor}15`, color: tierColor, borderRadius: '20px', fontSize: '11px', fontWeight: 700, display: 'inline-block', marginTop: '8px' }}>{tier}</div>
              <div style={{ fontSize: '10px', color: T.color.text.tertiary, marginTop: '12px', lineHeight: 1.5 }}>
                Tiers: LOW &lt; {tree.riskTiers.high} · HIGH {tree.riskTiers.high}-{tree.riskTiers.critical - 1} · CRITICAL ≥ {tree.riskTiers.critical}
              </div>
            </div>
          </Card>
        </div>
      </div>
    );
  };

  // #10 Rule 68 Offer Calculator
  const RbRule68 = () => {
    const [offer, setOffer] = useState(250000);
    const [likelyVerdict, setLikelyVerdict] = useState(180000);
    const [postOfferCosts, setPostOfferCosts] = useState(75000);
    const [plaintiffFees, setPlaintiffFees] = useState(45000);
    const shouldAccept = likelyVerdict < offer;
    const plaintiffNet = shouldAccept ? offer : (likelyVerdict - postOfferCosts);
    const exposure = !shouldAccept ? (offer - likelyVerdict + postOfferCosts) : 0;
    return (
      <div>
        <Card title="Rule 68 Offer of Judgment — Economic Model" right="Defendant's perspective">
          <div style={{ padding: '16px', display: 'grid', gridTemplateColumns: 'repeat(2, 1fr)', gap: '16px' }}>
            <div>
              <div style={{ fontSize: '10px', color: T.color.text.tertiary, fontWeight: 600, textTransform: 'uppercase', marginBottom: '4px' }}>Offer Amount ($)</div>
              <input type="number" value={offer} onChange={e => setOffer(+e.target.value)} style={{ ...rb.input, width: '100%' }} />
            </div>
            <div>
              <div style={{ fontSize: '10px', color: T.color.text.tertiary, fontWeight: 600, textTransform: 'uppercase', marginBottom: '4px' }}>Likely Verdict ($)</div>
              <input type="number" value={likelyVerdict} onChange={e => setLikelyVerdict(+e.target.value)} style={{ ...rb.input, width: '100%' }} />
            </div>
            <div>
              <div style={{ fontSize: '10px', color: T.color.text.tertiary, fontWeight: 600, textTransform: 'uppercase', marginBottom: '4px' }}>Post-Offer Costs ($)</div>
              <input type="number" value={postOfferCosts} onChange={e => setPostOfferCosts(+e.target.value)} style={{ ...rb.input, width: '100%' }} />
            </div>
            <div>
              <div style={{ fontSize: '10px', color: T.color.text.tertiary, fontWeight: 600, textTransform: 'uppercase', marginBottom: '4px' }}>Plaintiff Atty Fees ($)</div>
              <input type="number" value={plaintiffFees} onChange={e => setPlaintiffFees(+e.target.value)} style={{ ...rb.input, width: '100%' }} />
            </div>
          </div>
          <div style={{ padding: '16px', borderTop: `1px solid ${T.color.border.light}`, background: shouldAccept ? rb.greenBg : rb.crimsonBg }}>
            <div style={{ fontSize: '10px', fontWeight: 700, color: shouldAccept ? rb.green : '#C23030', textTransform: 'uppercase', letterSpacing: '0.06em', marginBottom: '6px' }}>
              Recommendation: {shouldAccept ? 'OFFER LIKELY RATIONAL TO ACCEPT' : 'OFFER PRESSURES PLAINTIFF TO ACCEPT'}
            </div>
            <div style={{ fontSize: '11px', color: T.color.text.secondary, lineHeight: 1.6 }}>
              Plaintiff net if accept: <b style={{ color: T.color.text.primary, fontFamily: T.font.mono }}>${offer.toLocaleString()}</b><br/>
              Plaintiff net if reject (verdict - post-offer costs): <b style={{ color: T.color.text.primary, fontFamily: T.font.mono }}>${(likelyVerdict - postOfferCosts).toLocaleString()}</b><br/>
              {!shouldAccept && <>Cost-shifting exposure to plaintiff: <b style={{ color: '#C23030', fontFamily: T.font.mono }}>${exposure.toLocaleString()}</b></>}
            </div>
          </div>
          <div style={{ padding: '12px 16px', fontSize: '10px', color: T.color.text.tertiary, fontStyle: 'italic', borderTop: `1px solid ${T.color.border.light}`, background: T.color.bg.secondary }}>
            ! Rule 68 offer must be served ≥14 days before trial. Acceptance within 14 days. Rejection + plaintiff recovers less → plaintiff pays all post-offer costs (NOT attorney fees unless authorized by substantive statute).
          </div>
        </Card>
      </div>
    );
  };

  // #11 Service Calculator
  const RbServiceCalc = () => {
    const [serviceType, setServiceType] = useState('domestic-electronic');
    const [triggerDate, setTriggerDate] = useState('2026-04-22');
    const [responseDays, setResponseDays] = useState(21);

    const METHODS = {
      'domestic-electronic': { label: 'Domestic — Electronic (ECF)', addDays: 0, effective: 'On transmission', note: 'Post-2024 amendment: no 3-day rule for e-service.' },
      'domestic-mail': { label: 'Domestic — Mail (Rule 5(b)(2)(C))', addDays: 3, effective: 'On mailing', note: 'Rule 6(d) adds 3 days.' },
      'waiver': { label: 'Waiver of Service (Rule 4(d))', addDays: 60, effective: 'On request sent', note: 'Answer due 60 days after waiver request sent.' },
      'foreign-hague': { label: 'Foreign — Hague Convention', addDays: 90, effective: 'On completion', note: '90-day service window; no deadline under Rule 4(m).' },
      'foreign-waiver': { label: 'Foreign — Waiver', addDays: 90, effective: 'On request sent', note: 'Answer due 90 days after waiver request sent.' },
      'personal': { label: 'Domestic — Personal Service', addDays: 0, effective: 'On delivery', note: 'Effective immediately on delivery.' },
    };

    const m = METHODS[serviceType];
    const base = new Date(triggerDate + 'T12:00:00');
    const responseDate = new Date(base);
    responseDate.setDate(responseDate.getDate() + responseDays + m.addDays);

    return (
      <Card title="Service Calculator — Rule 4, 5, 6" right="FRCP service-of-process + response deadline">
        <div style={{ padding: '16px', display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: '12px' }}>
          <div>
            <div style={{ fontSize: '9px', color: T.color.text.tertiary, fontWeight: 600, textTransform: 'uppercase', marginBottom: '4px' }}>Service Method</div>
            <select value={serviceType} onChange={e => setServiceType(e.target.value)} style={{ ...rb.input, width: '100%' }}>
              {Object.entries(METHODS).map(([k, v]) => <option key={k} value={k}>{v.label}</option>)}
            </select>
          </div>
          <div>
            <div style={{ fontSize: '9px', color: T.color.text.tertiary, fontWeight: 600, textTransform: 'uppercase', marginBottom: '4px' }}>Trigger Date</div>
            <input type="date" value={triggerDate} onChange={e => setTriggerDate(e.target.value)} style={{ ...rb.input, width: '100%' }} />
          </div>
          <div>
            <div style={{ fontSize: '9px', color: T.color.text.tertiary, fontWeight: 600, textTransform: 'uppercase', marginBottom: '4px' }}>Response Days</div>
            <input type="number" value={responseDays} onChange={e => setResponseDays(+e.target.value)} style={{ ...rb.input, width: '100%' }} />
          </div>
        </div>
        <div style={{ padding: '16px', borderTop: `1px solid ${T.color.border.light}`, background: rb.navyBg }}>
          <div style={{ fontSize: '10px', color: rb.navy, fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.06em', marginBottom: '6px' }}>Calculation</div>
          <div style={{ fontSize: '11px', color: T.color.text.secondary, lineHeight: 1.7 }}>
            Trigger: <b style={{ fontFamily: T.font.mono, color: T.color.text.primary }}>{triggerDate}</b><br/>
            Effective: <b style={{ color: T.color.text.primary }}>{m.effective}</b><br/>
            Base response period: <b style={{ fontFamily: T.font.mono, color: T.color.text.primary }}>+{responseDays}d</b>{m.addDays > 0 && <> + <b style={{ fontFamily: T.font.mono, color: rb.amber }}>+{m.addDays}d (Rule 6(d))</b></>}<br/>
            <span style={{ fontSize: '13px', color: '#C23030', fontWeight: 700, fontFamily: T.font.mono }}>Response due: {responseDate.toISOString().slice(0, 10)} ({responseDate.toLocaleDateString('en-US', { weekday: 'long' })})</span>
          </div>
          <div style={{ marginTop: '10px', fontSize: '10px', color: T.color.text.tertiary, fontStyle: 'italic' }}>{m.note}</div>
        </div>
      </Card>
    );
  };

  // ══════════════════════════════════════════════════════════
  //   ANALYTICS HUB — Heatmap, Local Diff, Amendment Tracker, Judge Analytics
  // ══════════════════════════════════════════════════════════

  // #12 Deadline Heatmap
  const RbHeatmap = () => {
    useCrossRender(['incident.logged', 'incident.resolved']);
    const byRule = window.RulebookStore.incidentsByRule();
    const byCourt = window.RulebookStore.incidentsByCourt();
    const maxRule = Math.max(...byRule.map(r => r.total), 1);
    return (
      <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '16px' }}>
        <Card title="Missed-Deadline Heatmap — By Rule" right={`${byRule.length} rules tracked`}>
          {byRule.slice(0, 10).map((r, i) => (
            <div key={i} style={{ padding: '8px 16px', borderBottom: `1px solid ${T.color.border.light}`, display: 'flex', alignItems: 'center', gap: '12px' }}>
              <span style={{ fontFamily: T.font.mono, fontSize: '11px', fontWeight: 700, color: rb.navy, minWidth: '110px' }}>{r.rule}</span>
              <div style={{ flex: 1, height: '14px', background: T.color.bg.secondary, borderRadius: '3px', position: 'relative', overflow: 'hidden' }}>
                <div style={{ position: 'absolute', left: 0, top: 0, bottom: 0, width: `${(r.total / maxRule) * 100}%`, background: `linear-gradient(90deg, ${rb.navy} 0%, #C23030 100%)` }} />
              </div>
              <span style={{ fontFamily: T.font.mono, fontSize: '11px', fontWeight: 700, color: T.color.text.primary, minWidth: '24px', textAlign: 'right' }}>{r.total}</span>
              {r.critical > 0 && <Pill color="#C23030">{r.critical} crit</Pill>}
              {r.unresolved > 0 && <Pill color="#D97706">{r.unresolved} open</Pill>}
            </div>
          ))}
        </Card>
        <Card title="By Jurisdiction" right={`${byCourt.length} courts`}>
          {byCourt.map((c, 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: '11px', fontWeight: 600, color: T.color.text.primary }}>{c.court}</span>
              <div style={{ display: 'flex', gap: '6px', alignItems: 'center' }}>
                <span style={{ fontFamily: T.font.mono, fontSize: '11px', color: T.color.text.primary }}>{c.total}</span>
                {c.critical > 0 && <Pill color="#C23030">{c.critical} crit</Pill>}
              </div>
            </div>
          ))}
        </Card>
      </div>
    );
  };

  // #13 Local Rule Diff
  const RbLocalDiff = () => {
    const COURTS = ['S.D.N.Y.', 'E.D.N.Y.', 'N.D. Cal.', 'D.N.J.', 'E.D. Va.', 'E.D. Tex.'];
    const [a, setA] = useState('S.D.N.Y.');
    const [b, setB] = useState('N.D. Cal.');
    const TOPICS = [
      { topic: 'MSJ', 'S.D.N.Y.': 'Local Rule 56.1 — separate statement of undisputed facts required', 'E.D.N.Y.': 'Local Rule 56.1 — similar to SDNY', 'N.D. Cal.': 'Civil L.R. 56-2 — JOINT statement of undisputed facts', 'D.N.J.': 'L.Civ.R. 56.1 — separately numbered paragraphs with citations', 'E.D. Va.': 'Local Rule 56(B) — 21-day opposition, 30-page brief limit', 'E.D. Tex.': 'Local Rule CV-56 — appendix with evidence required' },
      { topic: 'Discovery Disputes', 'S.D.N.Y.': 'Local Rule 37.2 — pre-motion conference letter required', 'E.D.N.Y.': 'Local Rule 37.3 — similar', 'N.D. Cal.': 'Joint letter, 5 pages max', 'D.N.J.': 'L.Civ.R. 37.1 — meet-and-confer certification', 'E.D. Va.': 'No formal requirement; file directly', 'E.D. Tex.': 'No formal requirement; file directly' },
      { topic: 'Motion Page Limit', 'S.D.N.Y.': '25 pages', 'E.D.N.Y.': '25 pages', 'N.D. Cal.': '25 pages (Civil L.R. 7-2)', 'D.N.J.': '40 pages dispositive, 15 pages non-dispositive', 'E.D. Va.': '30 pages', 'E.D. Tex.': 'Standard 30 pages' },
      { topic: 'Initial CMC Timing', 'S.D.N.Y.': '120 days', 'E.D.N.Y.': '120 days', 'N.D. Cal.': '90 days', 'D.N.J.': '90 days', 'E.D. Va.': '60 days — Rocket Docket', 'E.D. Tex.': '90 days — firm trial date at CMC' },
      { topic: 'Discovery Length', 'S.D.N.Y.': 'Judge-set', 'E.D.N.Y.': 'Judge-set', 'N.D. Cal.': 'Judge-set (typically 12 months)', 'D.N.J.': 'Judge-set', 'E.D. Va.': '120 days — compressed', 'E.D. Tex.': 'Judge-set; patent cases on docket control order' },
      { topic: 'ADR / Mediation', 'S.D.N.Y.': 'Judge discretion', 'E.D.N.Y.': 'Judge discretion', 'N.D. Cal.': 'Civil L.R. 16-10 — MANDATORY ADR within 150 days', 'D.N.J.': 'Mandatory at initial conference', 'E.D. Va.': 'Judge discretion', 'E.D. Tex.': 'Judge discretion' },
    ];
    return (
      <div>
        <div style={{ display: 'flex', gap: '12px', marginBottom: '12px', flexWrap: 'wrap' }}>
          <div>
            <div style={{ fontSize: '9px', color: T.color.text.tertiary, fontWeight: 600, textTransform: 'uppercase', marginBottom: '4px' }}>Court A</div>
            <select value={a} onChange={e => setA(e.target.value)} style={{ ...rb.input, width: '180px' }}>{COURTS.map(c => <option key={c} value={c}>{c}</option>)}</select>
          </div>
          <div>
            <div style={{ fontSize: '9px', color: T.color.text.tertiary, fontWeight: 600, textTransform: 'uppercase', marginBottom: '4px' }}>Court B</div>
            <select value={b} onChange={e => setB(e.target.value)} style={{ ...rb.input, width: '180px' }}>{COURTS.map(c => <option key={c} value={c}>{c}</option>)}</select>
          </div>
        </div>
        <Card title={`${a} vs ${b} — Local Rule Diff`} right={`${TOPICS.length} topics compared`}>
          <table style={{ width: '100%', borderCollapse: 'collapse' }}>
            <thead>
              <tr><TH w="180px">Topic</TH><TH>{a}</TH><TH>{b}</TH></tr>
            </thead>
            <tbody>
              {TOPICS.map((t, i) => {
                const diff = t[a] !== t[b];
                return (
                  <tr key={i} style={{ background: diff ? rb.amberBg : 'transparent' }}>
                    <TD bold color={rb.navy}>{t.topic}</TD>
                    <TD>{t[a]}</TD>
                    <TD>{t[b]}</TD>
                  </tr>
                );
              })}
            </tbody>
          </table>
        </Card>
      </div>
    );
  };

  // #14 Amendment Tracker
  const RbAmendmentTracker = () => {
    const data = window.RULEBOOK_DATA;
    return (
      <Card title="Proposed & Recent Amendments" right={`${data.proposedAmendments.length} tracked`}>
        {data.proposedAmendments.map((a, i) => (
          <div key={i} style={{ padding: '12px 16px', borderBottom: `1px solid ${T.color.border.light}` }}>
            <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '6px' }}>
              <span style={{ fontFamily: T.font.mono, fontSize: '13px', fontWeight: 700, color: rb.navy }}>{a.rule}</span>
              <div style={{ display: 'flex', gap: '6px', alignItems: 'center' }}>
                <Pill color={a.status === 'Adopted' ? rb.green : a.status === 'Pending' ? rb.amber : rb.navy}>{a.status}</Pill>
                <span style={{ fontSize: '10px', color: T.color.text.tertiary, fontFamily: T.font.mono }}>Eff: {a.effective}</span>
              </div>
            </div>
            <div style={{ fontSize: '11px', color: T.color.text.primary, lineHeight: 1.5, marginBottom: '4px' }}>{a.change}</div>
            <div style={{ fontSize: '10px', color: T.color.text.tertiary }}>Stage: {a.stage}</div>
          </div>
        ))}
      </Card>
    );
  };

  // #15 Judge Analytics (aggregated)
  const RbJudgeAnalytics = () => {
    const data = window.RULEBOOK_DATA;
    const sorted = [...data.judgeOrders].sort((a, b) => b.tendencies.msjGrantRate - a.tendencies.msjGrantRate);
    return (
      <div style={{ display: 'grid', gridTemplateColumns: 'repeat(4, 1fr)', gap: '12px', marginBottom: '12px' }}>
        {[
          { label: 'Avg MSJ Grant Rate', v: `${Math.round(data.judgeOrders.reduce((s, j) => s + j.tendencies.msjGrantRate, 0) / data.judgeOrders.length * 100)}%`, c: rb.amber },
          { label: 'Avg Days to Ruling', v: `${Math.round(data.judgeOrders.reduce((s, j) => s + j.tendencies.avgTimeToRuling, 0) / data.judgeOrders.length)}d`, c: rb.navy },
          { label: 'Avg MTC Grant Rate', v: `${Math.round(data.judgeOrders.reduce((s, j) => s + j.tendencies.motionToCompelGrantRate, 0) / data.judgeOrders.length * 100)}%`, c: rb.green },
          { label: 'Avg Trial Days', v: `${(data.judgeOrders.reduce((s, j) => s + j.tendencies.trialDaysAvg, 0) / data.judgeOrders.length).toFixed(1)}`, c: '#C23030' },
        ].map((s, i) => (
          <div key={i} style={rb.stat}>
            <span style={rb.statLabel}>{s.label}</span>
            <span style={{ ...rb.statValue, color: s.c }}>{s.v}</span>
          </div>
        ))}
        <div style={{ gridColumn: '1 / -1' }}>
          <Card title="Judge Analytics — Ranked by MSJ Grant Rate">
            <table style={{ width: '100%', borderCollapse: 'collapse' }}>
              <thead>
                <tr><TH>Judge</TH><TH w="100px">Court</TH><TH w="100px">MSJ Grant</TH><TH w="110px">Days to Ruling</TH><TH w="100px">MTC Grant</TH><TH w="90px">Trial Days</TH></tr>
              </thead>
              <tbody>
                {sorted.map(j => (
                  <tr key={j.id}>
                    <TD bold color={rb.navy}>{j.judge}</TD>
                    <TD>{j.court}</TD>
                    <TD mono bold color={j.tendencies.msjGrantRate > 0.35 ? '#C23030' : rb.green}>{Math.round(j.tendencies.msjGrantRate * 100)}%</TD>
                    <TD mono>{j.tendencies.avgTimeToRuling}d</TD>
                    <TD mono>{Math.round(j.tendencies.motionToCompelGrantRate * 100)}%</TD>
                    <TD mono>{j.tendencies.trialDaysAvg}</TD>
                  </tr>
                ))}
              </tbody>
            </table>
          </Card>
        </div>
      </div>
    );
  };

  // ══════════════════════════════════════════════════════════
  //   INTEGRATIONS HUB — Pins, Calendar Bridge, Discovery Bridge
  // ══════════════════════════════════════════════════════════

  // hook for cross-platform re-render
  const useCrossRender = (topics) => {
    const [, force] = React.useReducer(x => x + 1, 0);
    React.useEffect(() => {
      const offs = (Array.isArray(topics) ? topics : [topics]).map(t => window.RulebookStore.bus.on(t, () => force()));
      return () => offs.forEach(off => off && off());
    }, []);
  };

  // #16 Rule Pins (matter-level rule library)
  const RbPins = () => {
    useCrossRender(['pin.added', 'pin.removed', 'subscription.added', 'subscription.removed']);
    const pins = window.RulebookStore.state.matterPins;
    const subs = window.RulebookStore.state.subscriptions;
    const [matterId, setMatterId] = useState('M-2024-0312');
    const [ruleId, setRuleId] = useState('R36');
    const [reason, setReason] = useState('');

    const handlePin = () => {
      if (!matterId || !ruleId) return;
      window.RulebookStore.pinRule({ matterId, ruleId, reason, pinnedBy: 'M. Kirkland', context: 'Active matter' });
      setReason('');
    };

    return (
      <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '16px' }}>
        <Card title="Matter Pins" right={`${pins.length} pinned`}>
          <div style={{ padding: '12px 16px', background: rb.navyBg, borderBottom: `1px solid ${T.color.border.light}`, display: 'flex', gap: '6px', flexWrap: 'wrap', alignItems: 'center' }}>
            <input value={matterId} onChange={e => setMatterId(e.target.value)} placeholder="Matter ID" style={{ ...rb.input, width: '130px', height: '28px' }} />
            <input value={ruleId} onChange={e => setRuleId(e.target.value)} placeholder="Rule ID" style={{ ...rb.input, width: '90px', height: '28px' }} />
            <input value={reason} onChange={e => setReason(e.target.value)} placeholder="Reason" style={{ ...rb.input, flex: 1, height: '28px', minWidth: '120px' }} />
            <button onClick={handlePin} style={{ ...rb.btn, ...rb.btnActive, height: '28px' }}>+ Pin</button>
          </div>
          {pins.map(p => (
            <div key={p.id} style={{ padding: '10px 16px', borderBottom: `1px solid ${T.color.border.light}`, display: 'flex', justifyContent: 'space-between', alignItems: 'center', gap: '10px' }}>
              <div style={{ flex: 1 }}>
                <div style={{ fontSize: '11px', color: T.color.text.primary }}>
                  <span style={{ fontFamily: T.font.mono, fontWeight: 700, color: rb.navy }}>{p.ruleId}</span> → <b>{p.matterId}</b>
                </div>
                <div style={{ fontSize: '10px', color: T.color.text.tertiary, marginTop: '2px' }}>{p.reason} · {p.pinnedBy} · {p.date}</div>
              </div>
              <button onClick={() => window.RulebookStore.unpinRule(p.id, 'M. Kirkland')} style={{ ...rb.btn, fontSize: '9px', padding: '2px 6px', color: '#C23030' }}><Icons.X size={11}/></button>
            </div>
          ))}
        </Card>
        <Card title="Rule Subscriptions" right={`${subs.length} active`}>
          {subs.map(s => (
            <div key={s.id} style={{ padding: '10px 16px', borderBottom: `1px solid ${T.color.border.light}`, display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
              <div>
                <div style={{ fontSize: '11px', color: T.color.text.primary }}>
                  <span style={{ fontFamily: T.font.mono, fontWeight: 700, color: rb.navy }}>{s.ruleId}</span> · <span style={{ color: T.color.text.tertiary }}>{s.topic}</span>
                </div>
                <div style={{ fontSize: '10px', color: T.color.text.tertiary, marginTop: '2px' }}>{s.user} · since {s.created}</div>
              </div>
              <button onClick={() => window.RulebookStore.unsubscribeRule(s.id, 'M. Kirkland')} style={{ ...rb.btn, fontSize: '9px', padding: '2px 6px', color: '#C23030' }}><Icons.X size={11}/></button>
            </div>
          ))}
        </Card>
      </div>
    );
  };

  // #17 Calendar Bridge
  const RbCalendarBridge = () => {
    useCrossRender(['calendar.push']);
    const [log, setLog] = useState([]);
    React.useEffect(() => {
      const off = window.RulebookStore.bus.on('calendar.push', (e) => setLog(prev => [e, ...prev].slice(0, 10)));
      return () => off && off();
    }, []);

    const DEADLINES = [
      { ruleId: 'R36', ruleLabel: 'RFA responses (30d)', days: 30, matter: 'M-2024-0312' },
      { ruleId: 'R33', ruleLabel: 'Interrogatory responses (30d)', days: 30, matter: 'M-2024-0312' },
      { ruleId: 'R56', ruleLabel: 'MSJ opposition (21d)', days: 21, matter: 'M-2024-0312' },
      { ruleId: 'FRAP4', ruleLabel: 'Notice of appeal (30d)', days: 30, matter: 'M-2025-0154' },
      { ruleId: 'R26', ruleLabel: 'Expert disclosure (−90d)', days: -90, matter: 'M-2024-0451' },
    ];

    const handlePush = (d) => {
      const date = new Date();
      date.setDate(date.getDate() + d.days);
      window.RulebookStore.pushToCalendar({
        ruleId: d.ruleId, ruleLabel: d.ruleLabel,
        date: date.toISOString().slice(0, 10),
        matter: d.matter, assignee: 'M. Kirkland', priority: 'high',
        notes: `Generated from Rulebook: ${d.ruleLabel}`,
      });
    };

    return (
      <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '16px' }}>
        <Card title="Send Rule Deadlines → Calendar" right="arbiter:rb.calendar.push">
          {DEADLINES.map((d, i) => (
            <div key={i} style={{ padding: '10px 16px', borderBottom: `1px solid ${T.color.border.light}`, display: 'flex', justifyContent: 'space-between', alignItems: 'center', gap: '10px' }}>
              <div style={{ flex: 1 }}>
                <div style={{ fontSize: '11px', color: T.color.text.primary }}>
                  <span style={{ fontFamily: T.font.mono, fontWeight: 700, color: rb.navy }}>{d.ruleId}</span> {d.ruleLabel}
                </div>
                <div style={{ fontSize: '10px', color: T.color.text.tertiary, marginTop: '2px' }}>→ {d.matter}</div>
              </div>
              <button onClick={() => handlePush(d)} style={{ ...rb.btn, ...rb.btnActive }}>→ Calendar</button>
            </div>
          ))}
        </Card>
        <Card title="Bridge Log" right={`${log.length} events · live`}>
          {log.length === 0 && <div role="status" style={{ padding: '24px', textAlign: 'center', color: T.color.text.tertiary, fontSize: '11px' }}>No events yet. Click a "→ Calendar" button.</div>}
          {log.map((e, i) => (
            <div key={i} style={{ padding: '8px 16px', borderBottom: `1px solid ${T.color.border.light}`, fontSize: '10px' }}>
              <div style={{ display: 'flex', justifyContent: 'space-between' }}>
                <span style={{ fontFamily: T.font.mono, fontWeight: 700, color: rb.navy }}>{e.ruleId}</span>
                <span style={{ fontFamily: T.font.mono, color: T.color.text.tertiary }}>{e.date}</span>
              </div>
              <div style={{ color: T.color.text.secondary, marginTop: '2px' }}>{e.ruleLabel} → {e.matter}</div>
            </div>
          ))}
        </Card>
      </div>
    );
  };

  // #18 Discovery Bridge
  const RbDiscoveryBridge = () => {
    useCrossRender(['discovery.push']);
    const [log, setLog] = useState([]);
    React.useEffect(() => {
      const off = window.RulebookStore.bus.on('discovery.push', (e) => setLog(prev => [e, ...prev].slice(0, 10)));
      return () => off && off();
    }, []);

    const SHELLS = [
      { type: 'RFP (Rule 34)', ruleId: 'R34', notes: 'ESI format & scope per Rule 26(f) protocol' },
      { type: 'Interrogatories (Rule 33)', ruleId: 'R33', notes: '25-interrogatory limit; verified under oath' },
      { type: 'RFA (Rule 36)', ruleId: 'R36', notes: '30-day response — deemed admitted trap' },
      { type: 'Deposition Notice (Rule 30)', ruleId: 'R30', notes: 'Reasonable notice (14+ days); 7-hour limit' },
      { type: 'Subpoena (Rule 45)', ruleId: 'R45', notes: '100-mile rule; tender witness fees' },
      { type: 'Litigation Hold (Rule 37(e))', ruleId: 'R37', notes: 'Preservation letter — spoliation safe harbor' },
    ];

    const handlePush = (s) => {
      window.RulebookStore.pushDiscoveryShell({ type: s.type, ruleId: s.ruleId, matter: 'M-2024-0312', notes: s.notes, requestedBy: 'M. Kirkland' });
    };

    return (
      <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '16px' }}>
        <Card title="Generate Discovery Shell → Discovery Platform" right="arbiter:rb.discovery.push">
          {SHELLS.map((s, i) => (
            <div key={i} style={{ padding: '10px 16px', borderBottom: `1px solid ${T.color.border.light}`, display: 'flex', justifyContent: 'space-between', alignItems: 'center', gap: '10px' }}>
              <div style={{ flex: 1, minWidth: 0 }}>
                <div style={{ fontSize: '11px', color: T.color.text.primary, fontWeight: 600 }}>{s.type}</div>
                <div style={{ fontSize: '10px', color: T.color.text.tertiary, marginTop: '2px' }}>{s.notes}</div>
              </div>
              <button onClick={() => handlePush(s)} style={{ ...rb.btn, ...rb.btnActive }}>→ Discovery</button>
            </div>
          ))}
        </Card>
        <Card title="Bridge Log" right={`${log.length} events · live`}>
          {log.length === 0 && <div role="status" style={{ padding: '24px', textAlign: 'center', color: T.color.text.tertiary, fontSize: '11px' }}>No events yet. Click a "→ Discovery" button.</div>}
          {log.map((e, i) => (
            <div key={i} style={{ padding: '8px 16px', borderBottom: `1px solid ${T.color.border.light}`, fontSize: '10px' }}>
              <div style={{ display: 'flex', justifyContent: 'space-between' }}>
                <span style={{ fontWeight: 700, color: rb.navy }}>{e.type}</span>
                <span style={{ fontFamily: T.font.mono, color: T.color.text.tertiary }}>{e.date}</span>
              </div>
              <div style={{ color: T.color.text.secondary, marginTop: '2px' }}>{e.matter} · {e.requestedBy}</div>
            </div>
          ))}
        </Card>
      </div>
    );
  };

  // ══════════════════════════════════════════════════════════
  //   HUBS — consolidate sub-tabs
  // ══════════════════════════════════════════════════════════

  const RulebookKnowledgeHub = () => {
    const [sub, setSub] = useState('fre');
    const tabs = [
      { id: 'fre', label: 'FRE' },
      { id: 'frap', label: 'FRAP' },
      { id: 'judges', label: 'Judge Orders' },
      { id: 'state', label: 'State Rules' },
      { id: 'amendments', label: 'Amendments' },
    ];
    return (
      <div>
        <RbSubTabs tabs={tabs} active={sub} onChange={setSub} />
        {sub === 'fre' && <RbFRE />}
        {sub === 'frap' && <RbFRAP />}
        {sub === 'judges' && <RbJudgeOrders />}
        {sub === 'state' && <RbStateRules />}
        {sub === 'amendments' && <RbAmendments />}
      </div>
    );
  };

  const RulebookToolsHub = () => {
    const [sub, setSub] = useState('response');
    const tabs = [
      { id: 'response', label: 'Response Wizard' },
      { id: 'conflicts', label: 'Conflict Detector' },
      { id: 'templates', label: 'Motion Templates' },
      { id: 'sanctions', label: 'Sanctions' },
      { id: 'rule68', label: 'Rule 68' },
      { id: 'service', label: 'Service Calc' },
    ];
    return (
      <div>
        <RbSubTabs tabs={tabs} active={sub} onChange={setSub} />
        {sub === 'response' && <RbResponseWizard />}
        {sub === 'conflicts' && <RbConflicts />}
        {sub === 'templates' && <RbMotionLibrary />}
        {sub === 'sanctions' && <RbSanctions />}
        {sub === 'rule68' && <RbRule68 />}
        {sub === 'service' && <RbServiceCalc />}
      </div>
    );
  };

  const RulebookAnalyticsHub = () => {
    const [sub, setSub] = useState('heatmap');
    const tabs = [
      { id: 'heatmap', label: 'Heatmap' },
      { id: 'diff', label: 'Local Rule Diff' },
      { id: 'tracker', label: 'Amendment Tracker' },
      { id: 'judges', label: 'Judge Analytics' },
    ];
    return (
      <div>
        <RbSubTabs tabs={tabs} active={sub} onChange={setSub} />
        {sub === 'heatmap' && <RbHeatmap />}
        {sub === 'diff' && <RbLocalDiff />}
        {sub === 'tracker' && <RbAmendmentTracker />}
        {sub === 'judges' && <RbJudgeAnalytics />}
      </div>
    );
  };

  const RulebookIntegrationsHub = () => {
    const [sub, setSub] = useState('pins');
    const tabs = [
      { id: 'pins', label: 'Matter Pins' },
      { id: 'cal', label: 'Calendar Bridge' },
      { id: 'disc', label: 'Discovery Bridge' },
    ];
    return (
      <div>
        <RbSubTabs tabs={tabs} active={sub} onChange={setSub} />
        {sub === 'pins' && <RbPins />}
        {sub === 'cal' && <RbCalendarBridge />}
        {sub === 'disc' && <RbDiscoveryBridge />}
      </div>
    );
  };

  Object.assign(window, {
    RbFRE, RbFRAP, RbJudgeOrders, RbStateRules, RbAmendments,
    RbConflicts, RbResponseWizard, RbMotionLibrary, RbSanctions, RbRule68, RbServiceCalc,
    RbHeatmap, RbLocalDiff, RbAmendmentTracker, RbJudgeAnalytics,
    RbPins, RbCalendarBridge, RbDiscoveryBridge,
    RulebookKnowledgeHub, RulebookToolsHub, RulebookAnalyticsHub, RulebookIntegrationsHub,
  });
})();
