// SidePanel · right-side slide-out for evidence drills.
// Hosts kind-specific bodies (dependency, signal-evidence, ticket-filter, etc.)

function SidePanel({ panel, onClose, navigate }) {
  useEffect(() => {
    if (!panel) return;
    const h = (e) => { if (e.key === 'Escape') onClose(); };
    window.addEventListener('keydown', h);
    return () => window.removeEventListener('keydown', h);
  }, [panel]);

  if (!panel) return null;

  return (
    <>
      <div className="drill-scrim" onClick={onClose} />
      <div className="drill-panel">
        <div className="drill-head">
          <div>
            <div className="dh-title">
              <Icon name={panelIcon(panel)} size={14} />
              <span>{panel.title}</span>
            </div>
            <div className="dh-sub">{panelSubtitle(panel)}</div>
          </div>
          <button className="row-action" onClick={onClose} title="Close (esc)">
            <Icon name="x" size={14} />
          </button>
        </div>
        <div className="drill-body">
          {panel.kind === 'dependency'         && <DependencyDrill dep={panel.dep} navigate={navigate} />}
          {panel.kind === 'signal-evidence'   && <SignalEvidenceDrill signal={panel.signal} navigate={navigate} />}
          {panel.kind === 'signals-legend'    && <SignalsLegendDrill />}
          {panel.kind === 'gates-legend'      && <GatesLegendDrill />}
          {panel.kind === 'heatmap-legend'    && <HeatmapLegendDrill loadMode={panel.loadMode} />}
          {panel.kind === 'ticket-filter'     && <TicketFilterDrill filter={panel.filter} navigate={navigate} />}
          {panel.kind === 'carryover-bucket'  && <CarryoverBucketDrill windowId={panel.windowId} window={panel.window} cause={panel.cause} navigate={navigate} />}
          {panel.kind === 'meeting-detail'    && <MeetingDetailDrill />}
          {panel.kind === 'profile-metric'    && <ProfileMetricDrill devId={panel.devId} metric={panel.metric} />}
          {panel.kind === 'commitment'        && <CommitmentDrill commitment={panel.commitment} navigate={navigate} />}
          {panel.kind === 'eta-tickets'       && <EtaTicketsDrill devId={panel.devId} initialWeeks={panel.weeks} navigate={navigate} />}
          {panel.kind === 'ticket-update'     && <TicketUpdateDrill ticketId={panel.ticketId} context={panel.context} navigate={navigate} onClose={onClose} />}
        </div>
        <div className="drill-foot">
          <span>Evidence one click away · ground truth, never prediction</span>
        </div>
      </div>
    </>
  );
}

function panelIcon(p) {
  return {
    'dependency': 'link',
    'signal-evidence': 'sparkles',
    'signals-legend': 'info',
    'gates-legend': 'info',
    'heatmap-legend': 'info',
    'ticket-filter': 'filter',
    'carryover-bucket': 'shuffle',
    'meeting-detail': 'calendar',
    'profile-metric': 'activity',
    'commitment': 'flag',
    'eta-tickets': 'activity',
    'ticket-update': 'git-commit',
  }[p.kind] || 'info';
}
function panelSubtitle(p) {
  return {
    'dependency': 'Upstream dependency · evidence',
    'signal-evidence': 'Underlying tickets, PRs, commits',
    'signals-legend': 'How each signal is identified',
    'gates-legend': 'How the five gates are identified',
    'heatmap-legend': 'How team-load cells are coloured',
    'ticket-filter': 'Filtered ticket list',
    'carryover-bucket': 'Aged tickets in this bucket',
    'meeting-detail': 'Per-engineer breakdown',
    'profile-metric': 'Underlying events for this metric',
    'commitment': 'Stakeholder commitment · evidence',
    'eta-tickets': 'Per-ticket estimate vs actual cycle',
    'ticket-update': 'Recent activity · what changed',
  }[p.kind] || '';
}

function DependencyDrill({ dep, navigate }) {
  const t = DATA.ticketById(dep.blockedTicket);
  return (
    <>
      <div className="drill-section">
        <div className="drill-section-h">Blocked ticket</div>
        <div className="ds-row" onClick={() => navigate('#/ticket/' + dep.blockedTicket)} style={{ cursor: 'pointer' }}>
          <span><span className="mono" style={{ color: 'var(--text-3)' }}>{dep.blockedTicket}</span> · {t?.title || 'unknown'}</span>
          <span className="ds-meta">stage {DATA.STAGE_LABEL[t?.currentStage || 'dev']}</span>
        </div>
      </div>
      <div className="drill-section">
        <div className="drill-section-h">Upstream dependency</div>
        <div className="ds-row">
          <span>{dep.counterTeam} · <span className="mono">{dep.counterTicket}</span></span>
          <span className="ds-meta">{fmtRelative(dep.lastActivityDaysAgo)}</span>
        </div>
        <div className="ds-row">
          <span>Their stated ETA</span>
          <span className="mono">{dep.eta}</span>
        </div>
      </div>
      <div className="drill-section">
        <div className="drill-section-h">Recent activity</div>
        <div style={{ fontSize: 12, color: 'var(--text-2)', lineHeight: 1.55 }}>{dep.note}</div>
      </div>
    </>
  );
}

function SignalEvidenceDrill({ signal, navigate }) {
  const dev = DATA.devById(signal.devId);
  return (
    <>
      <div className="drill-section">
        <div className="drill-section-h">Developer</div>
        <div className="ds-row" onClick={() => navigate('#/dev/' + dev.id)} style={{ cursor: 'pointer' }}>
          <span style={{ display: 'inline-flex', alignItems: 'center', gap: 8 }}>
            <Avatar initials={dev.initials} size={22} />
            <span>{dev.name} · {dev.role}</span>
          </span>
          <Icon name="chevron-right" size={13} />
        </div>
      </div>
      <div className="drill-section">
        <div className="drill-section-h">Signal</div>
        <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 6 }}>
          <span style={{ fontSize: 13 }}>{signal.name}</span>
          <span className={`tag tone-${signal.severity}`}>{signal.severity}</span>
        </div>
        <div style={{ fontSize: 12, color: 'var(--text-2)', lineHeight: 1.55 }}>{signal.description}</div>
      </div>
      <div className="drill-section">
        <div className="drill-section-h">Identified by</div>
        <div style={{ fontSize: 12, color: 'var(--text-2)', lineHeight: 1.55 }}>{signal.rule}</div>
      </div>
      <div className="drill-section">
        <div className="drill-section-h">Evidence</div>
        {signal.evidence.map((e, i) => (
          <div key={i} className="ds-row" style={{ cursor: e.kind === 'ticket' ? 'pointer' : 'default' }}
               onClick={() => e.kind === 'ticket' && navigate('#/ticket/' + e.ref)}>
            <span>
              <span className={`tag mono`} style={{ marginRight: 8 }}>{e.kind}</span>
              <span className="mono" style={{ color: 'var(--text-3)' }}>{e.ref}</span>
              <span style={{ marginLeft: 8 }}>{e.text}</span>
            </span>
            {e.kind === 'ticket' && <Icon name="chevron-right" size={12} />}
          </div>
        ))}
      </div>
      <div className="drill-section">
        <div className="drill-section-h">Suggested action</div>
        <div style={{ fontSize: 12, color: 'var(--text)', padding: '8px 10px', background: 'var(--accent-tint)', border: '1px solid var(--accent-border)', borderRadius: 'var(--r-sm)' }}>
          {signal.callout}
        </div>
      </div>
    </>
  );
}

function SignalsLegendDrill() {
  // Canonical signal definitions · independent of the week being viewed.
  const items = DATA.signalDefs;
  return (
    <div className="drill-section">
      <div className="drill-section-h">How signals are identified</div>
      {items.map((s, i) => (
        <div key={s.name} style={{ padding: '10px 0', borderTop: i === 0 ? 'none' : '1px solid var(--border)' }}>
          <div style={{ fontSize: 12.5, color: 'var(--text)' }}>{s.name}</div>
          <div style={{ fontSize: 11.5, color: 'var(--text-3)', marginTop: 3, lineHeight: 1.55 }}>{s.rule}</div>
        </div>
      ))}
    </div>
  );
}

function GatesLegendDrill() {
  const items = DATA.disengagement.gateDefs;
  return (
    <>
      <div className="drill-section">
        <div className="drill-section-h">The five gates</div>
        {items.map((g, i) => (
          <div key={g.name} style={{ padding: '10px 0', borderTop: i === 0 ? 'none' : '1px solid var(--border)' }}>
            <div style={{ fontSize: 12.5, color: 'var(--text)' }}>{g.name}</div>
            <div style={{ fontSize: 11.5, color: 'var(--text-3)', marginTop: 3, lineHeight: 1.55 }}>{g.rule}</div>
          </div>
        ))}
      </div>
      <div className="drill-section">
        <div className="drill-section-h">How the signal fires</div>
        <div style={{ fontSize: 12, color: 'var(--text-2)', lineHeight: 1.55 }}>
          A disengagement signal fires only when all five gates are open at once for several
          consecutive working days. Any one healthy gate keeps the signal from firing.
        </div>
      </div>
    </>
  );
}

const HEATMAP_LEGEND_ROWS = {
  today: [
    { label: 'Tickets',       good: '≤ 2',  warn: '3–4',  bad: '> 4'  },
    { label: 'Meetings',      good: '≤ 2h', warn: '3–4h', bad: '> 4h' },
    { label: 'Total focus',   good: '≥ 4h', warn: '2–3h', bad: '< 2h' },
    { label: 'Deep focus', good: '≥ 2h', warn: '1h',   bad: '< 1h' },
  ],
  week: [
    { label: 'Tickets',       good: '≤ 4',   warn: '5–8',    bad: '> 8'   },
    { label: 'Meetings',      good: '≤ 12h', warn: '13–20h', bad: '> 20h' },
    { label: 'Total focus',   good: '≥ 20h', warn: '12–19h', bad: '< 12h' },
    { label: 'Deep focus', good: '≥ 2h',  warn: '1h',     bad: '< 1h'  },
  ],
};
function HeatmapLegendDrill({ loadMode = 'today' }) {
  const rows = HEATMAP_LEGEND_ROWS[loadMode] || HEATMAP_LEGEND_ROWS.today;
  return (
    <>
      <div className="drill-section">
        <div className="drill-section-h">Color thresholds · {loadMode === 'today' ? 'today' : 'last 7 days'}</div>
        <div style={{ display: 'grid', gridTemplateColumns: 'minmax(96px, 1.4fr) repeat(3, 1fr)', alignItems: 'center', columnGap: 8, rowGap: 8, marginTop: 4 }}>
          <span></span>
          <span className="mono-caps" style={{ fontSize: 9.5, color: 'var(--good)', textAlign: 'center', letterSpacing: '0.1em' }}>Good</span>
          <span className="mono-caps" style={{ fontSize: 9.5, color: 'var(--warn)', textAlign: 'center', letterSpacing: '0.1em' }}>Watch</span>
          <span className="mono-caps" style={{ fontSize: 9.5, color: 'var(--bad)',  textAlign: 'center', letterSpacing: '0.1em' }}>At risk</span>
          {rows.map(r => (
            <React.Fragment key={r.label}>
              <span style={{ fontSize: 12.5, color: 'var(--text)' }}>{r.label}</span>
              <span className="heatmap-cell tone-good" style={{ textAlign: 'center', justifySelf: 'stretch' }}>{r.good}</span>
              <span className="heatmap-cell tone-warn" style={{ textAlign: 'center', justifySelf: 'stretch' }}>{r.warn}</span>
              <span className="heatmap-cell tone-bad"  style={{ textAlign: 'center', justifySelf: 'stretch' }}>{r.bad}</span>
            </React.Fragment>
          ))}
        </div>
      </div>
      <div className="drill-section">
        <div className="drill-section-h">How to read it</div>
        <div style={{ fontSize: 12, color: 'var(--text-2)', lineHeight: 1.55 }}>
          Each cell is coloured by the engineer's value for that metric in the selected window. Green is healthy,
          amber is worth watching, red is at-risk. Switch the window between today and the last 7 days on the heatmap.
        </div>
      </div>
    </>
  );
}

function TicketFilterDrill({ filter, navigate }) {
  const weeks = filter.weeks || DATA.DEFAULT_WEEKS;
  let rows = DATA.ticketsInWindow(weeks);
  if (filter.assignee) rows = rows.filter(t => t.assignee === filter.assignee);
  if (filter.ids) rows = rows.filter(t => filter.ids.includes(t.id));
  if (filter.state) rows = rows.filter(t => t.state === filter.state);
  if (filter.atRisk) rows = rows.filter(t => t.rootCause);
  if (filter.aged) {
    const startOffset = -(weeks * 7 - 1);
    rows = rows.filter(t => t.createdAtDays < startOffset && t.state !== 'shipped' && t.currentStage !== 'done');
  }
  return (
    <>
      <div className="drill-section">
        <div className="drill-section-h">{rows.length} ticket{rows.length === 1 ? '' : 's'}</div>
        {rows.map(t => {
          const dev = DATA.devById(t.assignee);
          return (
            <div key={t.id} className="ds-row" style={{ cursor: 'pointer' }} onClick={() => navigate('#/ticket/' + t.id)}>
              <span>
                <span className="mono" style={{ color: 'var(--text-3)', marginRight: 8 }}>{t.id}</span>
                {t.title}
              </span>
              <span className="ds-meta" style={{ display: 'inline-flex', gap: 6, alignItems: 'center' }}>
                {dev && <Avatar initials={dev.initials} size={18} />}
                {DATA.STAGE_LABEL[t.currentStage]}
              </span>
            </div>
          );
        })}
        {rows.length === 0 && <div className="drill-empty">No tickets match.</div>}
      </div>
    </>
  );
}

function CarryoverBucketDrill({ windowId, window: win, cause, navigate }) {
  // Tickets whose first appearance falls within the bucket window. For the
  // demo, surface tickets created inside the [start, end] range · this is
  // what "window of first appearance" means in the new model.
  const startDate = win?.startDate;
  const endDate = win?.endDate;
  const ms = (d) => d.getTime();
  const rows = (startDate && endDate)
    ? DATA.tickets.filter(t => ms(t.createdAt) >= ms(startDate) && ms(t.createdAt) <= ms(endDate))
    : [];
  const causeLabel = { blocker: 'Blocker', rework: 'Rework', underest: 'Underestimate', interrupt: 'Interrupt' }[cause] || cause;
  return (
    <>
      <div className="drill-section">
        <div className="drill-section-h">{win?.dateLabel || windowId} · {causeLabel}</div>
        {rows.length === 0 && <div className="drill-empty">No matching tickets in this bucket.</div>}
        {rows.map(t => {
          const dev = DATA.devById(t.assignee);
          return (
            <div key={t.id} className="ds-row" style={{ cursor: 'pointer' }} onClick={() => navigate('#/ticket/' + t.id)}>
              <span>
                <span className="mono" style={{ color: 'var(--text-3)', marginRight: 8 }}>{t.id}</span>
                {t.title}
              </span>
              <span className="ds-meta" style={{ display: 'inline-flex', gap: 6, alignItems: 'center' }}>
                {dev && <Avatar initials={dev.initials} size={18} />}
                {causeLabel}
              </span>
            </div>
          );
        })}
      </div>
    </>
  );
}

function MeetingDetailDrill() {
  return (
    <>
      <div className="drill-section">
        <div className="drill-section-h">Hours by engineer · this week</div>
        {DATA.engineers.map(e => {
          const tone = e.meetingHrs > 8 ? 'bad' : e.meetingHrs > 6 ? 'warn' : 'good';
          return (
            <div key={e.id} className={`ds-row ${tone === 'warn' ? 'tone-warn' : ''}`}>
              <span style={{ display: 'inline-flex', alignItems: 'center', gap: 8 }}>
                <Avatar initials={e.initials} size={20} />
                {e.name}
              </span>
              <span className="ds-meta" style={{ display: 'inline-flex', gap: 8, alignItems: 'center' }}>
                <span className={`mono`} style={{ color: tone === 'bad' ? 'var(--bad)' : tone === 'warn' ? 'var(--warn)' : 'var(--text-2)' }}>{e.meetingHrs}h</span>
                <span style={{ color: 'var(--text-3)' }}>focus {e.focusBlockHrs}h</span>
              </span>
            </div>
          );
        })}
      </div>
    </>
  );
}

function ProfileMetricDrill({ devId, metric }) {
  return (
    <>
      <div className="drill-section">
        <div className="drill-section-h">{metric} · evidence</div>
        <div className="drill-empty">Underlying events for this metric would appear here.</div>
      </div>
    </>
  );
}

function CommitmentDrill({ commitment, navigate }) {
  const sig = DATA.commitmentSignals(commitment);
  return (
    <>
      <div className="drill-section">
        <div className="drill-section-h">Commitment</div>
        <div style={{ fontSize: 13, fontWeight: 500 }}>{commitment.label}</div>
        <div style={{ fontSize: 11.5, color: 'var(--text-3)', marginTop: 4 }}>{commitment.audience}</div>
      </div>
      <div className="drill-section">
        <div className="drill-section-h">Confidence</div>
        <div className="kv-grid">
          <span className="kv-label">Committed</span>
          <span className="mono">{commitment.committedDate}</span>
          <span className="kv-label">Confidence</span>
          <span><span className={`tag mono tone-${sig.level}`}>{sig.label}</span></span>
        </div>
      </div>
      <div className="drill-section">
        <div className="drill-section-h">Why this confidence · observed signals</div>
        {sig.evidence.length === 0 && <div className="drill-empty">No risk signals observed on the backing ticket.</div>}
        {sig.evidence.map((e, i) => (
          <div key={i} className={`ds-row ${e.tone === 'warn' ? 'tone-warn' : ''}`}
               style={{ cursor: e.kind === 'ticket' ? 'pointer' : 'default' }}
               onClick={() => e.kind === 'ticket' && navigate('#/ticket/' + e.ref)}>
            <span>
              <span className="tag mono" style={{ marginRight: 8 }}>{e.kind}</span>
              <span style={{ color: e.tone === 'good' ? 'var(--good)' : e.tone === 'bad' ? 'var(--bad)' : 'var(--text)' }}>{e.text}</span>
            </span>
            {e.kind === 'ticket' && <Icon name="chevron-right" size={12} />}
          </div>
        ))}
      </div>
      <div className="drill-section">
        <div className="drill-section-h">Backing ticket</div>
        <div className="ds-row" style={{ cursor: 'pointer' }} onClick={() => navigate('#/ticket/' + commitment.ticketId)}>
          <span><span className="mono" style={{ color: 'var(--text-3)' }}>{commitment.ticketId}</span></span>
          <Icon name="chevron-right" size={13} />
        </div>
      </div>
    </>
  );
}

function EtaTicketsDrill({ devId, initialWeeks, navigate }) {
  const [weeks, setWeeks] = useState(initialWeeks || DATA.DEFAULT_WEEKS);
  const dev = DATA.devById(devId);
  const cal = DATA.etaCalibration[devId];
  const mult = cal?.multiplier || 1;

  // Tickets touched within the rolling window, assigned to this engineer,
  // that have a real cycle (history with at least one segment).
  const rows = DATA.ticketsInWindow(weeks)
    .filter(t => t.assignee === devId)
    .map(t => {
      const segs = t.history || [];
      if (segs.length === 0) return null;
      const actual = Math.max(1, segs[segs.length - 1].endDay - segs[0].startDay);
      const est = Math.max(1, Math.round(actual / mult));
      const ratio = actual / est;
      const tone = ratio > 1.3 ? 'bad' : ratio > 1.1 ? 'warn' : 'good';
      const stageLabel = DATA.STAGE_LABEL[t.currentStage] || t.currentStage;
      return { t, est, actual, ratio, tone, stageLabel };
    })
    .filter(Boolean)
    .sort((a, b) => b.ratio - a.ratio);

  const avgEst = rows.length ? rows.reduce((s, r) => s + r.est, 0) / rows.length : 0;
  const avgAct = rows.length ? rows.reduce((s, r) => s + r.actual, 0) / rows.length : 0;
  const maxScale = Math.max(avgEst, avgAct, ...rows.map(r => Math.max(r.est, r.actual)), 1) * 1.05;

  const weekOptions = [1, 2, 3, 4, 6, 8];

  return (
    <>
      <div className="drill-section">
        <div className="drill-section-h" style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
          <span>Engineer</span>
          <span className="mono" style={{ fontSize: 10, color: 'var(--text-3)' }}>multiplier {mult}×</span>
        </div>
        <div className="ds-row" style={{ cursor: 'pointer' }} onClick={() => navigate('#/dev/' + devId)}>
          <span style={{ display: 'inline-flex', alignItems: 'center', gap: 8 }}>
            <Avatar initials={dev.initials} size={22} />
            <span>{dev.name}</span>
          </span>
          <Icon name="chevron-right" size={13} />
        </div>
      </div>

      <div className="drill-section">
        <div className="drill-section-h" style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
          <span>Window</span>
          <div style={{ display: 'inline-flex', gap: 10 }}>
            {weekOptions.map(w => (
              <button
                key={w}
                onClick={() => setWeeks(w)}
                className={`tag mono${weeks === w ? ' tone-accent' : ''}`}
                style={{
                  cursor: 'pointer',
                  background: 'transparent',
                  border: 0,
                  padding: 0,
                  fontWeight: weeks === w ? 600 : 400,
                }}
              >{w}W</button>
            ))}
          </div>
        </div>
        <div style={{ fontSize: 11.5, color: 'var(--text-3)', marginTop: 4 }}>
          Last {weeks} week{weeks === 1 ? '' : 's'} · {rows.length} ticket{rows.length === 1 ? '' : 's'} touched
          {rows.length > 0 && (
            <> · avg est <span className="mono" style={{ color: 'var(--text-2)' }}>{avgEst.toFixed(1)}d</span> · avg actual <span className="mono" style={{ color: 'var(--text-2)' }}>{avgAct.toFixed(1)}d</span></>
          )}
        </div>
      </div>

      <div className="drill-section">
        <div className="drill-section-h">Per-ticket estimate vs actual</div>
        {rows.length === 0 && <div className="drill-empty">No tickets touched in this window.</div>}
        {rows.map(({ t, est, actual, ratio, tone, stageLabel }) => {
          const estPct = (est / maxScale) * 100;
          const actPct = (actual / maxScale) * 100;
          const actColor = tone === 'bad' ? 'var(--bad)' : tone === 'warn' ? 'var(--warn)' : 'var(--good)';
          return (
            <div
              key={t.id}
              className="ds-row"
              style={{ cursor: 'pointer', display: 'block', padding: '10px 12px' }}
              onClick={() => navigate('#/ticket/' + t.id)}
            >
              <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 8, marginBottom: 6 }}>
                <span style={{ minWidth: 0, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
                  <span className="mono" style={{ color: 'var(--text-3)', marginRight: 8 }}>{t.id}</span>
                  {t.title}
                </span>
                <span className={`tag mono tone-${tone}`} style={{ flexShrink: 0 }}>{ratio.toFixed(2)}×</span>
              </div>
              <div style={{ display: 'grid', gap: 4 }}>
                <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
                  <em className="mono" style={{ fontStyle: 'normal', fontSize: 9.5, color: 'var(--text-3)', width: 42, letterSpacing: '0.06em' }}>EST</em>
                  <div style={{ flex: 1, position: 'relative', height: 8, background: 'var(--surface-2)', borderRadius: 3 }}>
                    <div style={{ position: 'absolute', inset: 0, width: estPct + '%', background: 'var(--accent)', borderRadius: 3, opacity: 0.78 }} />
                  </div>
                  <span className="mono" style={{ fontSize: 11, width: 36, textAlign: 'right' }}>{est}d</span>
                </div>
                <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
                  <em className="mono" style={{ fontStyle: 'normal', fontSize: 9.5, color: 'var(--text-3)', width: 42, letterSpacing: '0.06em' }}>ACTUAL</em>
                  <div style={{ flex: 1, position: 'relative', height: 8, background: 'var(--surface-2)', borderRadius: 3 }}>
                    <div style={{ position: 'absolute', inset: 0, width: estPct + '%', background: 'var(--accent)', borderRadius: 3, opacity: 0.18 }} />
                    <div style={{ position: 'absolute', inset: 0, width: actPct + '%', background: actColor, borderRadius: 3 }} />
                  </div>
                  <span className="mono" style={{ fontSize: 11, width: 36, textAlign: 'right' }}>{actual}d</span>
                </div>
              </div>
              <div style={{ fontSize: 10.5, color: 'var(--text-3)', marginTop: 4, display: 'flex', justifyContent: 'space-between' }}>
                <span>stage · {stageLabel}</span>
                <span>{t.state}</span>
              </div>
            </div>
          );
        })}
      </div>
    </>
  );
}

// TicketUpdateDrill · a lightweight, in-context peek at a ticket's recent
// update — surfaced from a standup item. Shows status, why it's in standup,
// root cause, and the latest commit/PR activity, with the full forensics page
// one click away. A peek before the deep dive.
function TicketUpdateDrill({ ticketId, context = {}, navigate, onClose }) {
  const t = DATA.ticketById(ticketId);
  if (!t) return <div className="drill-empty">Ticket {ticketId} not found.</div>;
  const dev = DATA.devById(t.assignee);

  const statusKind = t.state === 'blocked' ? 'blocked'
    : t.currentStage === 'done' || t.state === 'shipped' ? 'done'
    : t.currentStage === 'bug' ? 'bug'
    : (t.currentStage === 'review' || t.state === 'review') ? 'review'
    : 'progress';

  const kindIcon = { open: 'git-pull', comment: 'message-square', change: 'alert-triangle', approve: 'check-circle', merge: 'git-merge' };
  const kindLabel = { open: 'opened the PR', comment: 'commented', change: 'requested changes', approve: 'approved', merge: 'merged' };

  // Merge commits + PR events into one newest-first activity stream.
  const activity = [
    ...(t.commits || []).map(c => ({ dayOffset: c.dayOffset, who: c.author, icon: 'git-commit', label: c.message, sub: `${c.aiPct}% AI · ${c.tool}` })),
    ...((t.pr && t.pr.events) || []).map(e => ({ dayOffset: e.dayOffset, who: e.by, icon: kindIcon[e.kind] || 'git-commit', label: kindLabel[e.kind] || e.kind, sub: e.body || null })),
  ].sort((a, b) => b.dayOffset - a.dayOffset).slice(0, 6);

  const goFull = () => { navigate('#/ticket/' + ticketId); if (onClose) onClose(); };

  return (
    <>
      <div className="drill-section">
        <div className="drill-section-h">Status</div>
        <div className="ds-row">
          <span style={{ display: 'inline-flex', alignItems: 'center', gap: 8 }}>
            <StatusCircle kind={statusKind} size={14} />
            {dev && <Avatar initials={dev.initials} size={20} />}
            <span>{dev ? dev.name : t.assignee}</span>
          </span>
          <span className="ds-meta">{DATA.STAGE_LABEL[t.currentStage]} · {fmtDays(t.daysInStage)} in stage</span>
        </div>
      </div>

      {context.reason && (
        <div className="drill-section">
          <div className="drill-section-h">Why it's in standup</div>
          <div style={{ marginBottom: 6 }}>
            <span className={`tag tone-${context.tone || 'accent'}`}>{context.bucketLabel || 'Standup'}</span>
          </div>
          <div style={{ fontSize: 12, color: 'var(--text-2)', lineHeight: 1.55 }}>{context.reason}</div>
        </div>
      )}

      {t.rootCause && (
        <div className="drill-section">
          <div className="drill-section-h">Root cause</div>
          <div style={{ marginBottom: t.evidence ? 6 : 0 }}>
            <span className="tag tone-warn">{t.rootCause}</span>
          </div>
          {t.evidence && <div style={{ fontSize: 12, color: 'var(--text-2)', lineHeight: 1.55 }}>{t.evidence}</div>}
        </div>
      )}

      <div className="drill-section">
        <div className="drill-section-h">Recent activity</div>
        {activity.length === 0 && <div className="drill-empty">No recent commits or PR activity.</div>}
        {activity.map((a, i) => {
          const who = DATA.devById(a.who);
          return (
            <div key={i} className="ds-row" style={{ alignItems: 'flex-start' }}>
              <span style={{ display: 'inline-flex', alignItems: 'flex-start', gap: 8, minWidth: 0 }}>
                <span style={{ marginTop: 2, color: 'var(--text-3)', flexShrink: 0, display: 'inline-flex' }}><Icon name={a.icon} size={12} /></span>
                <span style={{ minWidth: 0 }}>
                  <span><strong style={{ color: 'var(--text-2)' }}>{who ? who.name.split(' ')[0] : a.who}</strong> {a.label}</span>
                  {a.sub && <span style={{ display: 'block', fontSize: 11, color: 'var(--text-3)', marginTop: 2, lineHeight: 1.4 }}>{a.sub}</span>}
                </span>
              </span>
              <span className="ds-meta" style={{ flexShrink: 0 }}>{fmtRelative(-a.dayOffset)}</span>
            </div>
          );
        })}
      </div>

      <div className="drill-section">
        <div className="ds-row" style={{ cursor: 'pointer' }} onClick={goFull}>
          <span style={{ display: 'inline-flex', alignItems: 'center', gap: 8 }}>
            <Icon name="external" size={13} />
            <span>Open full forensics</span>
          </span>
          <Icon name="chevron-right" size={13} />
        </div>
      </div>
    </>
  );
}

window.SidePanel = SidePanel;
