// CALENDAR — Practice · Motion Pipeline (full-scope build-out).
// Overrides window.CalMotionsTab (defined first by CalendarUpgrades.jsx).
//
// Architecture
//   <MotionPipeline>
//     ├─ <PipelineHeader>         KPI strip (5 tiles with sparklines)
//     ├─ <PipelineToolbar>        search · attorney chips · view toggle · new-motion
//     ├─ <PipelineBoard>          7-stage Kanban with drag-drop
//     │    └─ <StageColumn>           stage SLA header + stage KPIs
//     │         └─ <MotionCard>           rich card (pages / cites / judge / SLA dot)
//     ├─ <PipelineSwimlanes>      alt view: rows grouped by matter
//     ├─ <PipelineAnalytics>      cycle time per stage + win rate by type
//     └─ <MotionDrawer>           slide-in detail panel with timeline
//
// Data — builds on top of window.MOTIONS and synthesizes missing fields
// (pages, page-limit, cites, words, attorney, priority, blocksTrial, winProb)
// deterministically from motion id so values are stable across renders.

(function () {
  const { useState, useMemo, useCallback, useEffect, useRef } = React;
  const T = window.ArbiterTokens;
  const cal = window.cal;
  const A = window.Arbiter || {};
  const TODAY = new Date('2026-04-24');

  // ── Stage definitions ────────────────────────────────────────────────────
  const STAGES = [
    { id: 'drafting',  label: 'Drafting',    color: '#2563EB', slaDays: 21, hint: 'research · draft · partner review' },
    { id: 'filed',     label: 'Filed',       color: '#7C3AED', slaDays: 3,  hint: 'served on opposing counsel' },
    { id: 'opp',       label: 'Opp Pending', color: '#D97706', slaDays: 14, hint: 'awaiting opposition brief' },
    { id: 'reply',     label: 'Reply Due',   color: '#0891B2', slaDays: 7,  hint: 'our reply window' },
    { id: 'arg',       label: 'Argument',    color: '#EA580C', slaDays: 14, hint: 'oral argument scheduled' },
    { id: 'submitted', label: 'Submitted',   color: '#64748B', slaDays: 90, hint: 'under advisement' },
    { id: 'ruled',     label: 'Ruled',       color: '#059669', slaDays: 0,  hint: 'decision entered' },
  ];
  const stageById = Object.fromEntries(STAGES.map(s => [s.id, s]));

  const classify = (m) => {
    if (/ruled/i.test(m.status) || m.ruling) return 'ruled';
    if (/submit/i.test(m.status))            return 'submitted';
    if (m.argument && new Date(m.argument) <= TODAY) return 'arg';
    if (m.argument)                          return 'arg';
    if (m.replyDue && new Date(m.replyDue) >= TODAY && /reply/i.test(m.status))
                                              return 'reply';
    if (/reply/i.test(m.status))             return 'reply';
    if (/opp/i.test(m.status))               return 'opp';
    if (m.filed)                             return 'filed';
    return 'drafting';
  };

  const ATTORNEYS = [
    { id: 'mk', name: 'M. Kirkland', initials: 'MK', color: '#2563EB', role: 'Partner' },
    { id: 'sc', name: 'S. Chen',     initials: 'SC', color: '#7C3AED', role: 'Senior' },
    { id: 'jp', name: 'J. Park',     initials: 'JP', color: '#059669', role: 'Associate' },
    { id: 'rt', name: 'R. Torres',   initials: 'RT', color: '#D97706', role: 'Associate' },
    { id: 'lt', name: 'L. Torres',   initials: 'LT', color: '#EA580C', role: 'Senior' },
  ];

  // Deterministic hash
  const seed = (s) => Array.from(String(s || '')).reduce((a, c) => a * 31 + c.charCodeAt(0), 7);

  const enrich = (m) => {
    const s = seed(m.id);
    const stage = classify(m);
    const pageLimit = { 'Motion for Summary Judgment': 25, 'Motion in Limine': 10,
      'Motion to Compel': 15, 'Motion to Dismiss': 20, 'Motion for Class Certification': 30,
      'Motion for Protective Order': 10 }[m.type] || 15;
    const pages   = 6 + (s % 22);
    const cites   = 8 + (s % 28);
    const wordsK  = 1.2 + (s % 47) / 10;
    const complexity = ['routine', 'standard', 'complex'][s % 3];
    const blocksTrial = [true, false, false][s % 3];
    const winProb = 38 + (s % 53);
    const attorney = ATTORNEYS[s % ATTORNEYS.length];
    const priority = m.argument ? 'critical' : (blocksTrial ? 'high' : 'medium');
    return {
      ...m, stage, pageLimit, pages, cites, wordsK, complexity, blocksTrial,
      winProb, attorney, priority,
    };
  };

  // Days in current stage — synthetic based on relevant date field.
  const ageInStage = (m) => {
    const refDate = (
      m.stage === 'drafting'  ? null :
      m.stage === 'filed'     ? m.filed :
      m.stage === 'opp'       ? m.filed :
      m.stage === 'reply'     ? m.oppDue :
      m.stage === 'arg'       ? (m.replyDue || m.argument) :
      m.stage === 'submitted' ? m.argument :
      m.stage === 'ruled'     ? m.argument
      : null
    );
    if (!refDate) return 14 + (seed(m.id) % 12);
    const d = Math.ceil((TODAY - new Date(refDate)) / 86400000);
    return Math.max(0, d);
  };

  // ── Shared style helpers ─────────────────────────────────────────────────
  const priorityColor = (p) =>
    p === 'critical' ? '#C23030' : p === 'high' ? '#D97706' :
    p === 'medium'   ? '#2563EB' : '#6E7D9E';
  const rulingColor = (r) =>
    /granted in part/i.test(r || '') ? '#D97706' :
    /granted/i.test(r || '')         ? '#059669' :
    /denied/i.test(r || '')          ? '#C23030' :
    T.color.text.tertiary;
  const fmtDate = (d) => d ? new Date(d).toLocaleDateString('en-US', { month: 'short', day: 'numeric' }) : '—';
  const fmtDateY = (d) => d ? new Date(d).toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: '2-digit' }) : '—';
  const daysTo = (d) => d ? Math.ceil((new Date(d) - TODAY) / 86400000) : null;

  const CARD = {
    background: T.color.bg.card,
    border: `1px solid ${T.color.border.light}`,
    borderRadius: 8,
  };

  // ══════════════════════════════════════════════════════════════════════════
  //  1. Pipeline Header — Bloomberg-density KPIs with sparklines
  // ══════════════════════════════════════════════════════════════════════════
  function Sparkline({ points, color, w = 70, h = 22 }) {
    if (!points?.length) return null;
    const min = Math.min(...points), max = Math.max(...points);
    const span = max - min || 1;
    const step = w / Math.max(1, points.length - 1);
    const d = points.map((p, i) => `${i === 0 ? 'M' : 'L'}${(i * step).toFixed(1)},${(h - ((p - min) / span) * h).toFixed(1)}`).join(' ');
    const lastX = (points.length - 1) * step;
    const lastY = h - ((points[points.length - 1] - min) / span) * h;
    return (
      <svg width={w} height={h} aria-hidden="true" style={{ display: 'block' }}>
        <path d={d + ` L${w},${h} L0,${h} Z`} fill={color} opacity="0.10" />
        <path d={d} fill="none" stroke={color} strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
        <circle cx={lastX} cy={lastY} r="2.2" fill={color} />
      </svg>
    );
  }

  function PipelineHeader({ motions }) {
    const active = motions.filter(m => m.stage !== 'ruled').length;
    const ruled = motions.filter(m => m.stage === 'ruled');
    const wonYtd = ruled.filter(m => /granted/i.test(m.ruling || '')).length;
    const winRate = ruled.length ? Math.round((wonYtd / ruled.length) * 100) : 0;
    const overdue = motions.filter(m => {
      const dd = daysTo(m.oppDue) ?? daysTo(m.replyDue) ?? daysTo(m.argument);
      return dd != null && dd < 0 && m.stage !== 'ruled';
    }).length;
    // Synthesize averages for cycle time per stage across all motions
    const avgCycle = Math.round(motions.reduce((s, m) => s + ageInStage(m), 0) / Math.max(1, motions.length));

    const tiles = [
      { label: 'Active motions', value: active, color: T.color.text.primary, delta: '+2', hint: 'across all matters',
        trend: [5, 6, 6, 7, 6, 7, 7, 8, 8, 7, 8, active] },
      { label: 'Filed YTD', value: motions.filter(m => m.filed).length, color: '#2563EB', delta: '+3', hint: 'since Jan 1',
        trend: [1, 2, 2, 3, 3, 4, 4, 4, 5, 5, 6, 6] },
      { label: 'Win rate', value: winRate + '%', color: winRate >= 70 ? '#059669' : winRate >= 50 ? '#D97706' : '#C23030',
        hint: `${wonYtd} of ${ruled.length} ruled`,
        trend: [61, 63, 65, 68, 70, 72, 74, 76, 78, 80, 82, winRate] },
      { label: 'Avg cycle', value: avgCycle + 'd', color: '#7C3AED', hint: 'across all stages',
        trend: [42, 40, 38, 36, 38, 34, 32, 30, 30, 28, 30, avgCycle] },
      { label: 'Overdue', value: overdue, color: overdue ? '#C23030' : '#059669',
        hint: overdue ? 'action required' : 'clear',
        trend: [4, 3, 5, 3, 4, 2, 3, 2, 1, 2, 1, overdue] },
    ];

    return (
      <div style={{ display: 'grid', gridTemplateColumns: 'repeat(5, 1fr)', gap: 10, marginBottom: 14 }}>
        {tiles.map(t => (
          <div key={t.label} className="arb-elev-1"
            style={{
              background: T.color.bg.card, padding: '12px 14px', borderRadius: 8,
              display: 'flex', flexDirection: 'column', gap: 4,
              transition: 'transform 160ms, box-shadow 160ms',
            }}
            onMouseEnter={e => { e.currentTarget.style.transform = 'translateY(-1px)'; e.currentTarget.style.boxShadow = '0 4px 12px rgba(10,22,40,0.10), 0 0 0 1px var(--color-border-medium)'; }}
            onMouseLeave={e => { e.currentTarget.style.transform = 'none'; e.currentTarget.style.boxShadow = ''; }}>
            <div style={{ display: 'flex', justifyContent: 'space-between' }}>
              <span style={{ fontSize: 9.5, fontWeight: 600, color: T.color.text.tertiary, textTransform: 'uppercase', letterSpacing: '0.1em' }}>{t.label}</span>
              {t.delta && <span className="arb-num" style={{ fontSize: 10, color: t.delta.startsWith('-') ? '#C23030' : t.delta.startsWith('+') ? '#059669' : T.color.text.tertiary, fontWeight: 600 }}>{t.delta}</span>}
            </div>
            <div style={{ display: 'flex', alignItems: 'baseline', justifyContent: 'space-between', gap: 8 }}>
              <span className="arb-num" style={{ fontSize: 24, fontWeight: 700, color: t.color, letterSpacing: '-0.025em', lineHeight: 1 }}>{t.value}</span>
              <Sparkline points={t.trend} color={t.color} />
            </div>
            <div style={{ fontSize: 10, color: T.color.text.tertiary, marginTop: 2 }}>{t.hint}</div>
          </div>
        ))}
      </div>
    );
  }

  // ══════════════════════════════════════════════════════════════════════════
  //  2. Toolbar — search, attorney chips, view toggle, density, + new
  // ══════════════════════════════════════════════════════════════════════════
  function PipelineToolbar({ search, setSearch, attFilter, setAttFilter, priFilter, setPriFilter, view, setView, density, setDensity, onNew }) {
    const Chip = ({ active, onClick, children, color }) => (
      <button onClick={onClick} style={{
        padding: '4px 10px', fontSize: 11, borderRadius: 12,
        border: `1px solid ${active ? (color || '#2563EB') : T.color.border.medium}`,
        background: active ? (color || '#2563EB') + '14' : 'transparent',
        color: active ? (color || '#2563EB') : T.color.text.secondary,
        cursor: 'pointer', fontFamily: T.font.family, whiteSpace: 'nowrap',
        fontWeight: active ? 600 : 500,
      }}>{children}</button>
    );

    return (
      <div style={{
        display: 'flex', gap: 10, alignItems: 'center', flexWrap: 'wrap',
        marginBottom: 14, padding: '10px 12px',
        background: T.color.bg.card, borderRadius: 8,
        border: `1px solid ${T.color.border.light}`,
      }}>
        <input className="arb-input"
          placeholder="Search motions…"
          value={search} onChange={e => setSearch(e.target.value)}
          style={{ width: 200, height: 28, fontSize: 11.5 }} />

        <span style={{ width: 1, height: 18, background: T.color.border.light }} />

        <span style={{ fontSize: 9.5, fontWeight: 600, color: T.color.text.tertiary, textTransform: 'uppercase', letterSpacing: '0.08em' }}>Attorney</span>
        <Chip active={attFilter === 'all'} onClick={() => setAttFilter('all')}>All</Chip>
        {ATTORNEYS.map(a =>
          <Chip key={a.id} active={attFilter === a.id} onClick={() => setAttFilter(a.id)} color={a.color}>
            {a.initials}
          </Chip>
        )}

        <span style={{ width: 1, height: 18, background: T.color.border.light }} />

        <span style={{ fontSize: 9.5, fontWeight: 600, color: T.color.text.tertiary, textTransform: 'uppercase', letterSpacing: '0.08em' }}>Priority</span>
        {['all', 'critical', 'high', 'medium'].map(p =>
          <Chip key={p} active={priFilter === p} onClick={() => setPriFilter(p)} color={p === 'all' ? undefined : priorityColor(p)}>
            {p[0].toUpperCase() + p.slice(1)}
          </Chip>
        )}

        <div style={{ flex: 1 }} />

        <div style={{ display: 'flex', background: T.color.bg.secondary, padding: 3, borderRadius: 6, gap: 2 }}>
          {[{ id: 'pipeline', l: 'Pipeline' }, { id: 'swimlane', l: 'Swimlane' }, { id: 'analytics', l: 'Analytics' }].map(v =>
            <button key={v.id} onClick={() => setView(v.id)} style={{
              padding: '4px 10px', fontSize: 11, borderRadius: 4, border: 'none',
              background: view === v.id ? T.color.bg.card : 'transparent',
              color: view === v.id ? T.color.text.primary : T.color.text.tertiary,
              fontWeight: view === v.id ? 600 : 500, cursor: 'pointer', fontFamily: T.font.family,
              boxShadow: view === v.id ? '0 1px 2px rgba(10,22,40,0.06)' : 'none',
            }}>{v.l}</button>
          )}
        </div>

        <button onClick={() => setDensity(d => d === 'compact' ? 'comfortable' : 'compact')}
          title="Toggle density"
          style={{
            padding: '4px 10px', fontSize: 11, borderRadius: 6,
            border: `1px solid ${T.color.border.medium}`, background: 'transparent',
            color: T.color.text.secondary, cursor: 'pointer', fontFamily: T.font.family,
          }}>{density === 'compact' ? '≡' : '☰'}</button>

        <button onClick={onNew} style={{
          padding: '6px 14px', fontSize: 11.5, borderRadius: 6, border: 'none',
          background: `linear-gradient(180deg, #2563EB 0%, #1E40AF 100%)`,
          color: '#fff', fontWeight: 600, cursor: 'pointer', fontFamily: T.font.family,
          letterSpacing: '-0.005em',
          boxShadow: '0 1px 2px rgba(10,22,40,0.1), inset 0 1px 0 rgba(255,255,255,0.12)',
        }}>+ New motion</button>
      </div>
    );
  }

  // ══════════════════════════════════════════════════════════════════════════
  //  3. Motion Card
  // ══════════════════════════════════════════════════════════════════════════
  function MotionCard({ m, stage, selected, compact, onSelect, onDragStart, onDragEnd }) {
    const c = stage.color;
    const age = ageInStage(m);
    const aging = stage.slaDays && age > stage.slaDays;
    const nextDate =
      m.stage === 'filed' ? m.oppDue :
      m.stage === 'opp'   ? m.oppDue :
      m.stage === 'reply' ? m.replyDue :
      m.stage === 'arg'   ? m.argument : null;
    const nextDays = daysTo(nextDate);
    const pagePct = Math.min(100, (m.pages / m.pageLimit) * 100);
    const pageOver = m.pages > m.pageLimit;
    const pagePctColor = pageOver ? '#C23030' : pagePct >= 85 ? '#D97706' : '#2563EB';

    return (
      <div draggable
        onDragStart={(e) => onDragStart(e, m)}
        onDragEnd={onDragEnd}
        onClick={() => onSelect(m)}
        className="arb-row"
        style={{
          ...CARD,
          padding: compact ? '8px 10px' : '10px 12px',
          marginBottom: 6,
          cursor: 'grab',
          borderLeft: `3px solid ${c}`,
          boxShadow: selected ? `0 0 0 2px ${c}55, 0 2px 8px ${c}22` : '0 1px 2px rgba(10,22,40,0.04)',
          transition: 'box-shadow 140ms, transform 100ms',
          outline: aging ? `1px dashed ${c}88` : 'none',
          outlineOffset: aging ? 2 : 0,
        }}>
        {/* Title + priority */}
        <div style={{ display: 'flex', alignItems: 'flex-start', gap: 8, marginBottom: compact ? 3 : 5 }}>
          <div style={{ flex: 1, minWidth: 0 }}>
            <div style={{
              fontSize: 11.5, fontWeight: 600, color: T.color.text.primary,
              lineHeight: 1.3, letterSpacing: '-0.005em',
              whiteSpace: compact ? 'nowrap' : 'normal',
              overflow: compact ? 'hidden' : 'visible',
              textOverflow: compact ? 'ellipsis' : 'clip',
            }}>{m.type}</div>
            <div style={{ fontSize: 9.5, color: T.color.text.tertiary, marginTop: 1, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>{m.matter}</div>
          </div>
          {m.blocksTrial && (
            <span title="Blocks trial date" style={{
              padding: '1px 5px', borderRadius: 3, fontSize: 8.5, fontWeight: 700,
              background: '#C2303014', color: '#C23030', textTransform: 'uppercase', letterSpacing: '0.06em',
              whiteSpace: 'nowrap', flexShrink: 0,
            }}>★ trial</span>
          )}
        </div>

        {!compact && (
          <>
            {/* Page meter */}
            <div style={{ marginBottom: 5 }}>
              <div style={{ display: 'flex', justifyContent: 'space-between', fontSize: 9, color: T.color.text.tertiary, marginBottom: 2 }}>
                <span className="arb-num">{m.pages}/{m.pageLimit}pp · {m.cites} cites</span>
                <span className="arb-num" style={{ color: pageOver ? '#C23030' : T.color.text.tertiary }}>
                  {pageOver ? `+${m.pages - m.pageLimit}pp over` : `${m.pageLimit - m.pages}pp left`}
                </span>
              </div>
              <div style={{ height: 3, borderRadius: 2, background: T.color.bg.tertiary, overflow: 'hidden' }}>
                <div style={{
                  width: pagePct + '%', height: '100%',
                  background: `linear-gradient(90deg, ${pagePctColor}B3, ${pagePctColor})`,
                  boxShadow: pageOver ? `0 0 4px ${pagePctColor}88` : 'none',
                }} />
              </div>
            </div>

            {/* Dates */}
            <div style={{ display: 'flex', flexWrap: 'wrap', gap: 4, marginBottom: 5 }}>
              {m.oppDue && (
                <span className="arb-num" title="Opposition due" style={{ fontSize: 9.5, padding: '1px 5px', borderRadius: 3, background: '#D9770612', color: '#D97706' }}>
                  opp {fmtDate(m.oppDue)}
                </span>
              )}
              {m.replyDue && (
                <span className="arb-num" title="Reply due" style={{ fontSize: 9.5, padding: '1px 5px', borderRadius: 3, background: '#0891B212', color: '#0891B2' }}>
                  reply {fmtDate(m.replyDue)}
                </span>
              )}
              {m.argument && (
                <span className="arb-num" title="Oral argument" style={{ fontSize: 9.5, padding: '1px 5px', borderRadius: 3, background: '#EA580C12', color: '#EA580C', fontWeight: 600 }}>
                  arg {fmtDate(m.argument)}
                </span>
              )}
            </div>
          </>
        )}

        {/* Ruling or footer */}
        <div style={{ display: 'flex', alignItems: 'center', gap: 6, fontSize: 10 }}>
          <span title={`${m.attorney.name} · ${m.attorney.role}`} style={{
            width: 18, height: 18, borderRadius: '50%', background: m.attorney.color,
            color: '#fff', fontSize: 8.5, fontWeight: 700,
            display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
            flexShrink: 0,
          }}>{m.attorney.initials}</span>
          <span title={m.judge} style={{ fontSize: 9.5, color: T.color.text.tertiary, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis', flex: 1 }}>
            {m.judge.replace(/^Hon\.\s*/, '')}
          </span>
          {m.ruling ? (
            <span style={{ fontSize: 9.5, fontWeight: 700, color: rulingColor(m.ruling) }}>✓ {m.ruling}</span>
          ) : nextDays != null ? (
            <span className="arb-num" style={{
              fontSize: 9.5, fontWeight: 700,
              color: nextDays < 0 ? '#C23030' : nextDays <= 3 ? '#D97706' : T.color.text.secondary,
            }}>
              {nextDays < 0 ? `${-nextDays}d late` : nextDays === 0 ? 'today' : `T-${nextDays}d`}
            </span>
          ) : null}
        </div>

        {/* Aging indicator */}
        {!compact && aging && (
          <div style={{
            marginTop: 5, padding: '3px 6px', borderRadius: 3,
            background: c + '0F', fontSize: 9, color: c, fontWeight: 600,
            display: 'flex', alignItems: 'center', gap: 4,
          }}>
            <span style={{ width: 5, height: 5, borderRadius: '50%', background: c, boxShadow: `0 0 4px ${c}88` }} />
            <span>aging · {age}d in stage (SLA {stage.slaDays}d)</span>
          </div>
        )}
      </div>
    );
  }

  // ══════════════════════════════════════════════════════════════════════════
  //  4. Stage Column
  // ══════════════════════════════════════════════════════════════════════════
  function StageColumn({ stage, items, onDrop, onDragOver, onDragLeave, over, selectedId, compact, onSelect, onDragStart, onDragEnd }) {
    const avg = items.length ? Math.round(items.reduce((s, m) => s + ageInStage(m), 0) / items.length) : 0;
    const atRisk = items.filter(m => ageInStage(m) > stage.slaDays).length;
    return (
      <div style={{
        display: 'flex', flexDirection: 'column', minHeight: 420,
        background: over ? stage.color + '08' : stage.color + '04',
        border: `1px solid ${over ? stage.color + '66' : stage.color + '22'}`,
        borderRadius: 8,
        transition: 'background 140ms, border-color 140ms',
      }}
      onDragOver={onDragOver} onDrop={onDrop} onDragLeave={onDragLeave}>
        {/* Stage header */}
        <div style={{
          padding: '10px 12px', borderBottom: `1px solid ${stage.color}22`,
          background: stage.color + '0A', display: 'flex', flexDirection: 'column', gap: 4,
        }}>
          <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
            <span style={{ fontSize: 10.5, fontWeight: 700, color: stage.color, textTransform: 'uppercase', letterSpacing: '0.08em' }}>{stage.label}</span>
            <span className="arb-num" style={{ fontSize: 12, fontWeight: 700, color: stage.color }}>{items.length}</span>
          </div>
          <div style={{ fontSize: 9, color: T.color.text.tertiary, lineHeight: 1.3 }}>{stage.hint}</div>
          {stage.slaDays > 0 && (
            <div style={{ display: 'flex', alignItems: 'center', gap: 6, fontSize: 9, marginTop: 2 }}>
              <span className="arb-num" style={{ color: T.color.text.tertiary }}>SLA {stage.slaDays}d</span>
              <span style={{ flex: 1, height: 3, borderRadius: 2, background: T.color.bg.tertiary, position: 'relative', overflow: 'hidden' }}>
                <span style={{
                  position: 'absolute', left: 0, top: 0, bottom: 0,
                  width: Math.min(100, (avg / stage.slaDays) * 100) + '%',
                  background: avg > stage.slaDays ? '#C23030' : stage.color,
                  borderRadius: 2,
                }} />
              </span>
              <span className="arb-num" style={{ color: avg > stage.slaDays ? '#C23030' : T.color.text.tertiary, fontWeight: 600 }}>
                avg {avg}d
              </span>
              {atRisk > 0 && <span className="arb-num" style={{ color: '#C23030', fontWeight: 700 }}>· {atRisk} aging</span>}
            </div>
          )}
        </div>

        {/* Drop zone body */}
        <div style={{ flex: 1, padding: 6, overflowY: 'auto' }}>
          {items.length === 0 && (
            <div role="status" style={{
              textAlign: 'center', fontSize: 10, color: T.color.text.tertiary,
              padding: 20, fontStyle: 'italic',
              border: `1px dashed ${stage.color}44`, borderRadius: 6, margin: 4,
            }}>
              Drop motions here<br />
              or click “+ New motion”
            </div>
          )}
          {items.map(m => (
            <MotionCard key={m.id} m={m} stage={stage}
              selected={selectedId === m.id}
              compact={compact}
              onSelect={onSelect}
              onDragStart={onDragStart}
              onDragEnd={onDragEnd}
            />
          ))}
        </div>
      </div>
    );
  }

  // ══════════════════════════════════════════════════════════════════════════
  //  5. Motion Drawer — slide-in detail
  // ══════════════════════════════════════════════════════════════════════════
  function MotionDrawer({ m, onClose, onStageChange }) {
    if (!m) return null;
    const stage = stageById[m.stage];
    const timeline = [
      m.filed     && { date: m.filed,     label: 'Motion filed',             icon: '⊡', color: '#2563EB' },
      m.oppDue    && { date: m.oppDue,    label: 'Opposition due',           icon: '◇', color: '#D97706' },
      m.replyDue  && { date: m.replyDue,  label: 'Reply due',                icon: '◇', color: '#0891B2' },
      m.argument  && { date: m.argument,  label: 'Oral argument',            icon: '◉', color: '#EA580C' },
      m.ruling    && { date: null,        label: `Ruling: ${m.ruling}`,      icon: '✓', color: rulingColor(m.ruling) },
    ].filter(Boolean).sort((a, b) => a.date && b.date ? a.date.localeCompare(b.date) : (a.date ? -1 : 1));

    return (
      <div role="dialog" aria-label="Motion detail"
        style={{
          position: 'fixed', top: 0, right: 0, bottom: 0, width: 420,
          background: T.color.bg.card,
          boxShadow: '-12px 0 40px rgba(10,22,40,0.18), -1px 0 0 var(--color-border-medium)',
          zIndex: 1100,
          display: 'flex', flexDirection: 'column',
          animation: 'arb-drawer-in 240ms cubic-bezier(0.2,0,0,1)',
        }}>
        {/* Header */}
        <div style={{
          padding: '14px 18px', borderBottom: `1px solid ${T.color.border.light}`,
          display: 'flex', alignItems: 'flex-start', gap: 10,
          background: stage.color + '08',
        }}>
          <div style={{ flex: 1 }}>
            <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 4 }}>
              <span style={{
                padding: '2px 8px', borderRadius: 10, fontSize: 9.5, fontWeight: 700,
                background: stage.color, color: '#fff', textTransform: 'uppercase', letterSpacing: '0.08em',
              }}>{stage.label}</span>
              <span className="arb-num" style={{ fontSize: 10, color: T.color.text.tertiary }}>{m.id}</span>
              {m.blocksTrial && <span style={{ fontSize: 9, color: '#C23030', fontWeight: 700 }}>★ BLOCKS TRIAL</span>}
            </div>
            <div style={{ fontSize: 15, fontWeight: 600, color: T.color.text.primary, letterSpacing: '-0.01em', lineHeight: 1.25 }}>{m.type}</div>
            <div style={{ fontSize: 11, color: T.color.text.tertiary, marginTop: 2 }}>{m.matter}</div>
          </div>
          <button onClick={onClose} aria-label="Close"
            style={{
              padding: '4px 10px', fontSize: 16, fontWeight: 400,
              border: `1px solid ${T.color.border.medium}`, background: 'transparent',
              color: T.color.text.secondary, borderRadius: 5, cursor: 'pointer',
              lineHeight: 1,
            }}>×</button>
        </div>

        {/* Body */}
        <div style={{ flex: 1, overflowY: 'auto', padding: 16 }}>
          {/* Quick facts grid */}
          <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 8, marginBottom: 16 }}>
            {[
              { label: 'Movant',       value: m.movant },
              { label: 'Attorney',     value: m.attorney.name, badge: m.attorney.color },
              { label: 'Judge',        value: m.judge.replace(/^Hon\.\s*/, '') },
              { label: 'Complexity',   value: m.complexity },
              { label: 'Pages',        value: `${m.pages}/${m.pageLimit}`, color: m.pages > m.pageLimit ? '#C23030' : T.color.text.primary },
              { label: 'Words',        value: `${m.wordsK.toFixed(1)}K` },
              { label: 'Cites',        value: m.cites },
              { label: 'Priority',     value: m.priority, color: priorityColor(m.priority) },
              { label: 'Win prob',     value: m.winProb + '%', color: m.winProb >= 70 ? '#059669' : m.winProb >= 50 ? '#D97706' : '#C23030' },
              { label: 'Age in stage', value: `${ageInStage(m)}d`, color: ageInStage(m) > stage.slaDays ? '#C23030' : T.color.text.primary },
            ].map(f => (
              <div key={f.label} style={{
                padding: '8px 10px', borderRadius: 6,
                background: T.color.bg.secondary,
                border: `1px solid ${T.color.border.light}`,
              }}>
                <div style={{ fontSize: 9, fontWeight: 600, color: T.color.text.tertiary, textTransform: 'uppercase', letterSpacing: '0.08em' }}>{f.label}</div>
                <div className="arb-num" style={{ fontSize: 12, fontWeight: 600, color: f.color || T.color.text.primary, marginTop: 2, textTransform: 'capitalize' }}>
                  {f.badge && <span style={{ width: 6, height: 6, borderRadius: '50%', background: f.badge, display: 'inline-block', marginRight: 5 }} />}
                  {f.value}
                </div>
              </div>
            ))}
          </div>

          {/* Timeline */}
          <div style={{ fontSize: 9.5, fontWeight: 700, color: T.color.text.tertiary, textTransform: 'uppercase', letterSpacing: '0.1em', marginBottom: 8 }}>Timeline</div>
          <div style={{ position: 'relative', paddingLeft: 20, marginBottom: 16 }}>
            <div style={{ position: 'absolute', left: 6, top: 8, bottom: 8, width: 2, background: T.color.border.medium }} />
            {timeline.map((e, i) => (
              <div key={i} style={{ position: 'relative', padding: '8px 0' }}>
                <div style={{
                  position: 'absolute', left: -18, top: 10, width: 12, height: 12,
                  borderRadius: '50%', background: e.color,
                  boxShadow: `0 0 0 3px ${e.color}22, 0 0 0 4px ${T.color.bg.card}`,
                }} />
                <div style={{ fontSize: 12, fontWeight: 500, color: T.color.text.primary }}>{e.label}</div>
                {e.date && <div className="arb-num" style={{ fontSize: 10, color: T.color.text.tertiary, marginTop: 1 }}>{fmtDateY(e.date)}</div>}
              </div>
            ))}
          </div>

          {/* Stage changer */}
          <div style={{ fontSize: 9.5, fontWeight: 700, color: T.color.text.tertiary, textTransform: 'uppercase', letterSpacing: '0.1em', marginBottom: 8 }}>Move to stage</div>
          <div style={{ display: 'grid', gridTemplateColumns: 'repeat(2, 1fr)', gap: 6, marginBottom: 16 }}>
            {STAGES.filter(s => s.id !== m.stage).map(s => (
              <button key={s.id} onClick={() => onStageChange(m.id, s.id)}
                className="arb-row"
                style={{
                  padding: '8px 10px', fontSize: 11, fontWeight: 500,
                  border: `1px solid ${s.color}55`, background: s.color + '08',
                  color: s.color, borderRadius: 6, cursor: 'pointer',
                  textAlign: 'left', fontFamily: T.font.family,
                  display: 'flex', alignItems: 'center', gap: 8,
                }}>
                <span style={{ width: 6, height: 6, borderRadius: '50%', background: s.color }} />
                <span style={{ flex: 1 }}>{s.label}</span>
                <span style={{ fontSize: 12, color: s.color }}>→</span>
              </button>
            ))}
          </div>

          {/* Actions */}
          <div style={{ fontSize: 9.5, fontWeight: 700, color: T.color.text.tertiary, textTransform: 'uppercase', letterSpacing: '0.1em', marginBottom: 8 }}>Actions</div>
          <div style={{ display: 'grid', gap: 4 }}>
            {[
              { id: 'open', label: 'Open motion in Motions Platform', icon: '↗' },
              { id: 'dup',  label: 'Duplicate as template',            icon: '⎘' },
              { id: 'esig', label: 'Route for e-signature',            icon: '✎' },
              { id: 'file', label: 'File via CM/ECF',                  icon: '⬆' },
              { id: 'ext',  label: 'Request extension / continuance',  icon: '⏲' },
            ].map(a => (
              <button key={a.id}
                onClick={() => A.toast?.({ title: a.label })}
                className="arb-row"
                style={{
                  padding: '8px 10px', fontSize: 11.5, border: 'none',
                  background: 'transparent', textAlign: 'left',
                  color: T.color.text.primary, borderRadius: 5, cursor: 'pointer',
                  display: 'flex', alignItems: 'center', gap: 10, fontFamily: T.font.family,
                }}>
                <span style={{ width: 20, textAlign: 'center', color: T.color.text.tertiary }}>{a.icon}</span>
                <span>{a.label}</span>
              </button>
            ))}
          </div>
        </div>

        <div style={{ padding: 12, borderTop: `1px solid ${T.color.border.light}`, background: T.color.bg.secondary, fontSize: 10, color: T.color.text.tertiary }}>
          Esc to close · drag card to any column to move · right-click for more
        </div>
      </div>
    );
  }

  // ══════════════════════════════════════════════════════════════════════════
  //  6. Swimlane view — rows grouped by matter
  // ══════════════════════════════════════════════════════════════════════════
  function PipelineSwimlanes({ motions, onSelect, selectedId, compact }) {
    const matters = useMemo(() => {
      const map = {};
      motions.forEach(m => { (map[m.matter] = map[m.matter] || []).push(m); });
      return Object.entries(map).sort((a, b) => a[0].localeCompare(b[0]));
    }, [motions]);
    return (
      <div style={{ ...CARD, overflow: 'hidden' }}>
        <div style={{
          display: 'grid', gridTemplateColumns: `200px repeat(${STAGES.length}, 1fr)`,
          background: T.color.bg.secondary, padding: '6px 0',
          borderBottom: `1px solid ${T.color.border.light}`,
          fontSize: 9.5, fontWeight: 700, color: T.color.text.tertiary,
          textTransform: 'uppercase', letterSpacing: '0.08em',
        }}>
          <div style={{ padding: '4px 14px' }}>Matter</div>
          {STAGES.map(s => (
            <div key={s.id} style={{ padding: '4px 8px', color: s.color }}>{s.label}</div>
          ))}
        </div>
        {matters.map(([matter, items]) => (
          <div key={matter} style={{
            display: 'grid', gridTemplateColumns: `200px repeat(${STAGES.length}, 1fr)`,
            borderBottom: `1px solid ${T.color.border.light}`, minHeight: 60,
          }}>
            <div style={{
              padding: '10px 14px', fontSize: 12, fontWeight: 600, color: T.color.text.primary,
              borderRight: `1px solid ${T.color.border.light}`, background: T.color.bg.card,
              display: 'flex', flexDirection: 'column', justifyContent: 'center',
            }}>
              <span>{matter}</span>
              <span className="arb-num" style={{ fontSize: 10, color: T.color.text.tertiary, marginTop: 2 }}>
                {items.length} motion{items.length === 1 ? '' : 's'}
              </span>
            </div>
            {STAGES.map(s => {
              const stageItems = items.filter(m => m.stage === s.id);
              return (
                <div key={s.id} style={{ padding: 6, borderRight: `1px solid ${T.color.border.light}`, background: stageItems.length ? s.color + '04' : 'transparent' }}>
                  {stageItems.map(m => (
                    <MotionCard key={m.id} m={m} stage={s}
                      selected={selectedId === m.id}
                      compact={true}
                      onSelect={onSelect}
                      onDragStart={() => {}} onDragEnd={() => {}}
                    />
                  ))}
                </div>
              );
            })}
          </div>
        ))}
      </div>
    );
  }

  // ══════════════════════════════════════════════════════════════════════════
  //  7. Analytics — cycle time per stage + win rate by type
  // ══════════════════════════════════════════════════════════════════════════
  function PipelineAnalytics({ motions }) {
    const cycleByStage = STAGES.map(s => {
      const items = motions.filter(m => m.stage === s.id);
      const avg = items.length ? Math.round(items.reduce((a, m) => a + ageInStage(m), 0) / items.length) : 0;
      return { stage: s, avg, count: items.length, target: s.slaDays };
    });
    const byType = {};
    motions.forEach(m => {
      byType[m.type] = byType[m.type] || { ruled: 0, won: 0 };
      if (m.stage === 'ruled') {
        byType[m.type].ruled++;
        if (/granted/i.test(m.ruling || '')) byType[m.type].won++;
      }
    });
    const typeRows = Object.entries(byType).filter(([_, v]) => v.ruled > 0)
      .map(([type, v]) => ({ type, ruled: v.ruled, won: v.won, rate: Math.round((v.won / v.ruled) * 100) }))
      .sort((a, b) => b.rate - a.rate);
    const maxCycle = Math.max(...cycleByStage.map(c => Math.max(c.avg, c.target)));
    return (
      <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 14 }}>
        <div style={{ ...CARD }}>
          <div style={{ padding: '10px 14px', borderBottom: `1px solid ${T.color.border.light}`, fontSize: 11, fontWeight: 600, color: T.color.text.secondary, textTransform: 'uppercase', letterSpacing: '0.08em' }}>
            Cycle time per stage
          </div>
          <div style={{ padding: 14, display: 'grid', gap: 10 }}>
            {cycleByStage.map(c => (
              <div key={c.stage.id} style={{ display: 'grid', gridTemplateColumns: '100px 1fr 60px', gap: 10, alignItems: 'center' }}>
                <div style={{ fontSize: 11, fontWeight: 600, color: c.stage.color }}>{c.stage.label}</div>
                <div style={{ position: 'relative', height: 16, background: T.color.bg.tertiary, borderRadius: 4 }}>
                  {c.target > 0 && (
                    <div style={{ position: 'absolute', left: (c.target / maxCycle) * 100 + '%', top: -2, bottom: -2, width: 1, background: '#C23030', opacity: 0.5 }} />
                  )}
                  <div style={{
                    width: (c.avg / maxCycle) * 100 + '%', height: '100%', borderRadius: 4,
                    background: `linear-gradient(90deg, ${c.stage.color}B3, ${c.stage.color})`,
                    boxShadow: c.avg > c.target && c.target > 0 ? `0 0 6px ${c.stage.color}66` : 'none',
                  }} />
                </div>
                <span className="arb-num" style={{ fontSize: 11, fontWeight: 700, color: c.avg > c.target && c.target > 0 ? '#C23030' : T.color.text.primary, textAlign: 'right' }}>
                  {c.avg}d
                </span>
              </div>
            ))}
          </div>
        </div>
        <div style={{ ...CARD }}>
          <div style={{ padding: '10px 14px', borderBottom: `1px solid ${T.color.border.light}`, fontSize: 11, fontWeight: 600, color: T.color.text.secondary, textTransform: 'uppercase', letterSpacing: '0.08em' }}>
            Win rate by motion type
          </div>
          <div style={{ padding: 14, display: 'grid', gap: 10 }}>
            {typeRows.length === 0 && <div role="status" style={{ textAlign: 'center', color: T.color.text.tertiary, fontSize: 11, padding: 16 }}>No rulings yet.</div>}
            {typeRows.map(r => {
              const c = r.rate >= 70 ? '#059669' : r.rate >= 50 ? '#D97706' : '#C23030';
              return (
                <div key={r.type} style={{ display: 'grid', gridTemplateColumns: '1fr 1fr 50px', gap: 10, alignItems: 'center' }}>
                  <div style={{ fontSize: 11, fontWeight: 600, color: T.color.text.primary }}>
                    {r.type}
                    <div className="arb-num" style={{ fontSize: 9, color: T.color.text.tertiary }}>{r.won} of {r.ruled} granted</div>
                  </div>
                  <div style={{ height: 10, background: T.color.bg.tertiary, borderRadius: 5, overflow: 'hidden' }}>
                    <div style={{ width: r.rate + '%', height: '100%', background: `linear-gradient(90deg, ${c}B3, ${c})` }} />
                  </div>
                  <span className="arb-num" style={{ fontSize: 13, fontWeight: 700, color: c, textAlign: 'right' }}>{r.rate}%</span>
                </div>
              );
            })}
          </div>
        </div>
      </div>
    );
  }

  // ══════════════════════════════════════════════════════════════════════════
  //  8. Root — MotionPipeline (overrides CalMotionsTab)
  // ══════════════════════════════════════════════════════════════════════════
  function MotionPipeline() {
    const base = (window.MOTIONS || []).map(enrich);
    const [overrides, setOverrides] = useState({});        // { id: stageId }
    const [newMotions, setNewMotions] = useState([]);      // created in session
    const motions = useMemo(() =>
      [...base, ...newMotions].map(m => overrides[m.id] ? { ...m, stage: overrides[m.id] } : m),
      [base, newMotions, overrides]
    );
    const [search, setSearch]       = useState('');
    const [attFilter, setAttFilter] = useState('all');
    const [priFilter, setPriFilter] = useState('all');
    const [view, setView]           = useState('pipeline');
    const [density, setDensity]     = useState('comfortable');
    const [selectedId, setSelectedId] = useState(null);
    const [dragging, setDragging]   = useState(null);
    const [dragOver, setDragOver]   = useState(null);

    A.useTrack?.('cal.practice.pipeline', {}, []);

    // Escape to close drawer
    useEffect(() => {
      const h = (e) => { if (e.key === 'Escape') setSelectedId(null); };
      window.addEventListener('keydown', h);
      return () => window.removeEventListener('keydown', h);
    }, []);

    // Filter
    const filtered = useMemo(() => motions.filter(m => {
      if (attFilter !== 'all' && m.attorney.id !== attFilter) return false;
      if (priFilter !== 'all' && m.priority !== priFilter) return false;
      if (search) {
        const q = search.toLowerCase();
        return (m.type + ' ' + m.matter + ' ' + m.judge + ' ' + m.movant).toLowerCase().includes(q);
      }
      return true;
    }), [motions, attFilter, priFilter, search]);

    const byStage = useMemo(() => {
      const map = Object.fromEntries(STAGES.map(s => [s.id, []]));
      filtered.forEach(m => { (map[m.stage] || map.drafting).push(m); });
      return map;
    }, [filtered]);

    // Drag handlers
    const onDragStart = (e, m) => { setDragging(m); e.dataTransfer.setData('text/plain', m.id); e.dataTransfer.effectAllowed = 'move'; };
    const onDragEnd   = () => { setDragging(null); setDragOver(null); };
    const onDragOver  = (stageId) => (e) => { e.preventDefault(); if (dragOver !== stageId) setDragOver(stageId); };
    const onDragLeave = (stageId) => (e) => { if (dragOver === stageId) setDragOver(null); };
    const onDrop      = (stageId) => (e) => {
      e.preventDefault();
      if (dragging && dragging.stage !== stageId) {
        handleStageChange(dragging.id, stageId);
      }
      setDragOver(null);
    };

    const handleStageChange = (id, stageId) => {
      const prev = motions.find(m => m.id === id)?.stage;
      setOverrides(o => ({ ...o, [id]: stageId }));
      const s = stageById[stageId];
      A.toast?.({ kind: 'active', title: `Moved to ${s.label}`, message: `${id} · ${prev} → ${stageId}` });
      A.track?.('cal.practice.pipeline.move', { id, from: prev, to: stageId });
      setSelectedId(null);
    };

    const createMotion = () => {
      const now = Date.now();
      const stub = {
        id: `MO-NEW-${now % 10000}`,
        matter: 'New Matter — select',
        type: 'Motion (draft)',
        movant: 'Plaintiff',
        filed: null, oppDue: null, replyDue: null, argument: null,
        status: 'drafting',
        ruling: null,
        judge: 'Hon. TBD',
      };
      setNewMotions(n => [...n, stub]);
      A.toast?.({ title: 'Draft created', message: stub.id });
    };

    const selected = selectedId ? motions.find(m => m.id === selectedId) : null;

    // Inject drawer keyframe once
    useEffect(() => {
      if (document.getElementById('arb-drawer-kf')) return;
      const s = document.createElement('style'); s.id = 'arb-drawer-kf';
      s.textContent = `@keyframes arb-drawer-in { from { transform: translateX(30px); opacity: 0; } to { transform: none; opacity: 1; } }`;
      document.head.appendChild(s);
    }, []);

    return (
      <div>
        <PipelineHeader motions={motions} />
        <PipelineToolbar
          search={search} setSearch={setSearch}
          attFilter={attFilter} setAttFilter={setAttFilter}
          priFilter={priFilter} setPriFilter={setPriFilter}
          view={view} setView={setView}
          density={density} setDensity={setDensity}
          onNew={createMotion} />

        {view === 'pipeline' && (
          <div style={{ overflowX: 'auto', paddingBottom: 4 }}>
            <div style={{
              display: 'grid',
              gridTemplateColumns: `repeat(${STAGES.length}, minmax(240px, 1fr))`,
              gap: 10, minWidth: STAGES.length * 250,
            }}>
              {STAGES.map(s => (
                <StageColumn key={s.id} stage={s} items={byStage[s.id]}
                  over={dragOver === s.id}
                  selectedId={selectedId}
                  compact={density === 'compact'}
                  onDragOver={onDragOver(s.id)}
                  onDrop={onDrop(s.id)}
                  onDragLeave={onDragLeave(s.id)}
                  onSelect={(m) => setSelectedId(m.id)}
                  onDragStart={onDragStart}
                  onDragEnd={onDragEnd}
                />
              ))}
            </div>
          </div>
        )}
        {view === 'swimlane' && (
          <PipelineSwimlanes motions={filtered} onSelect={(m) => setSelectedId(m.id)} selectedId={selectedId} />
        )}
        {view === 'analytics' && (
          <PipelineAnalytics motions={filtered} />
        )}

        <MotionDrawer m={selected} onClose={() => setSelectedId(null)} onStageChange={handleStageChange} />
      </div>
    );
  }

  // ── Override ─────────────────────────────────────────────────────────────
  // Also re-define CalDocketHub so the sub-tab dispatch reads from
  // window.CalMotionsTab at render time (CalendarUpgrades.jsx closure-bound
  // an earlier version).
  function CalDocketHub({ onCascade }) {
    const [sub, setSub] = useState('motions');
    const CalSubTabs = window.CalSubTabs;
    const tabs = [
      { id: 'motions',      label: 'Motion Pipeline' },
      { id: 'service',      label: 'Service' },
      { id: 'continuances', label: 'Continuances' },
      { id: 'pacer',        label: 'PACER Sync' },
      { id: 'cascade',      label: 'Cascade Runs' },
    ];
    const map = {
      motions:      MotionPipeline,
      service:      window.CalServiceTab,
      continuances: window.CalContinuancesTab,
      pacer:        window.CalPacerTab,
      cascade:      window.CalCascadeRunsTab,
    };
    const Comp = map[sub] || (() => null);
    return (
      <div>
        <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
          {CalSubTabs && <CalSubTabs tabs={tabs} active={sub} onChange={setSub} />}
          {sub === 'cascade' && <button onClick={onCascade} style={{ ...cal.filterBtn, ...cal.filterBtnActive, fontSize: 10 }}>+ Run cascade</button>}
        </div>
        <Comp />
      </div>
    );
  }

  window.CalMotionsTab = MotionPipeline;
  window.CalDocketHub  = CalDocketHub;
  window.MotionPipeline = MotionPipeline;
})();
