import ArchetypeBadge from '@/components/vyndr/ArchetypeBadge';
import GradeBadge from '@/components/vyndr/GradeBadge';
import Sparkline from '@/components/vyndr/Sparkline';
import PlayerAvatar from '@/components/vyndr/PlayerAvatar';
import { nextRunLabelET } from '@/lib/pipelineSchedule';
// A1 S3 — BOOK IT is a real per-book deep link now (organic until the
// affiliate config flips a book on). rel MUST stay BOOK_LINK_REL.
import { buildBookLink, BOOK_LINK_REL } from '@/lib/bookLinks';
import { bookInfo } from '@/lib/books';
import TeamChip from '@/components/vyndr/TeamChip';

export interface StatCell {
  label: string;
  value: string;
}
export interface StripProp {
  stat: string;
  line: number | string;
  side: string; // O / U / Over / Under
  grade: string | null;
  // Session 45 — pre-graded snapshot model: the locked grade + market movement.
  gradedAt?: { line: number; odds?: number | null; timestamp?: string; ago?: string } | null;
  delta?: { delta: number; direction: 'toward' | 'away'; currentLine: number } | null;
  awaiting?: boolean; // not yet graded by a snapshot → "Awaiting next read"
  // Session 55 — settled outcome from the self-learning loop (once the game is final).
  outcome?: { result: 'hit' | 'miss' | 'push' | string; actual?: number | null } | null;
  // Session 60 (Phase 2.5) — intraday movement + public revision.
  movement?: { kind: 'steam' | 'value' | 'against' | 'revised' | string; delta: number; currentLine: number } | null;
  revisedFrom?: string | null;
  // A1 S3 — the prop's source book + the best available price across books
  // (only set when ≥2 books post the same line and prices differ).
  book?: string | null;
  bestBook?: { book: string; odds: number } | null;
  // Session 64 (A1-S5) — NOT-IN-LINEUP kills the grade display (struck
  // through, actions suppressed). The locked ledger read is untouched.
  dead?: boolean;
  // S6 (A1 board) — ROW-GRAMMAR sub-line data. `history` = REAL captured
  // {t, line} points from the intraday refresh (sparkline needs ≥3);
  // `last10Dots` = last-10 games vs tonight's locked line, newest first
  // (true = cleared). Both absent → nothing renders.
  history?: Array<{ t: string; line: number }> | null;
  last10Dots?: boolean[] | null;
  // A1 S11 — the canonical stat key (live-tracking join; `stat` is the short
  // display label) + the live proto-outcome computed by lib/liveProgress.
  // GRADES NEVER CHANGE IN-GAME — `live` is TRACKING in the outcome slot.
  statType?: string;
  live?: {
    current: number;
    line: number | null;
    state: 'hit' | 'on_pace' | 'needs' | 'holding' | 'past' | string;
    label: string;
    needs?: number;
    progressLabel?: string | null;
    progressFraction?: number | null;
  } | null;
}

/** A1 S11 — LIVE TRACKING mark (ROW-GRAMMAR §2 slot 6, proto-outcome).
 *  `1/2 TB · ▲6th` + a small game-progress bar + the state chip. COLOR LAW:
 *  green = hit/on-pace/holding, amber = needs-more/line-passed. NEVER red —
 *  an in-progress prop has settled nothing. Data → mono, never glitches. */
export function LiveTracker({ p }: { p: StripProp }) {
  const lv = p.live;
  if (!lv || p.outcome) return null;
  const green = lv.state === 'hit' || lv.state === 'on_pace' || lv.state === 'holding';
  const color = green ? 'var(--g-a, #00D4A0)' : 'var(--amber, #FFB347)';
  const filled = lv.state === 'hit';
  const frac = typeof lv.progressFraction === 'number' ? Math.min(1, Math.max(0, lv.progressFraction)) : null;
  const titles: Record<string, string> = {
    hit: 'The over has already cleared the locked line — settles when the game is final',
    on_pace: 'Current pace projects past the locked line',
    needs: 'Behind the locked line at the current pace',
    holding: 'The under holds if the count stays below the line — nothing is final until the game is',
    past: 'The count reached the line — the under can no longer clear; settles when final',
  };
  return (
    <span
      className="mono"
      title={`TRACKING — read locked pre-game. ${titles[lv.state] || ''}`}
      style={{ display: 'inline-flex', alignItems: 'center', gap: 6, fontSize: 10.5 }}
    >
      <span style={{ color: 'var(--text-0, #F0F0F5)' }}>
        {lv.current}/{lv.line != null ? lv.line : '—'} {p.stat}
        {lv.progressLabel ? <span style={{ color: 'var(--text-1, #7A7A8E)' }}> · {lv.progressLabel}</span> : null}
      </span>
      {frac != null && (
        <span aria-hidden style={{ width: 34, height: 3, borderRadius: 2, background: 'var(--bg-2, #12121A)', overflow: 'hidden', display: 'inline-block' }}>
          <span style={{ display: 'block', height: '100%', width: `${Math.round(frac * 100)}%`, background: color, opacity: 0.85 }} />
        </span>
      )}
      <span
        style={{
          fontSize: 9, fontWeight: 800, letterSpacing: '0.07em', padding: '1px 5px', borderRadius: 3,
          color: filled ? '#06060B' : color,
          background: filled ? color : 'transparent',
          border: `1px solid color-mix(in srgb, ${color} 55%, transparent)`,
        }}
      >
        {lv.label}
      </span>
    </span>
  );
}

/** ROW-GRAMMAR §4 — ●/○ last-10 dot strip. Filled green = that game's stat
 *  cleared TONIGHT'S locked line; hollow dim = it didn't. Newest first.
 *  Real game logs only — no log, no dots. */
export function DotStrip({ dots }: { dots?: boolean[] | null }) {
  if (!Array.isArray(dots) || dots.length === 0) return null;
  const hits = dots.filter(Boolean).length;
  return (
    <span
      className="mono"
      title={`Last ${dots.length} vs tonight's line — ${hits} of ${dots.length} cleared (newest first)`}
      aria-label={`Last ${dots.length} games versus tonight's line: ${hits} cleared`}
      style={{ fontSize: 10, letterSpacing: '0.12em', whiteSpace: 'nowrap' }}
    >
      {dots.map((hit, i) => (
        <span key={i} style={{ color: hit ? 'var(--g-a, #00D4A0)' : 'var(--text-2, #4A4A5E)' }}>
          {hit ? '●' : '○'}
        </span>
      ))}
    </span>
  );
}

/** ROW-GRAMMAR §4 — line-movement sparkline from REAL captured {t, line}
 *  points. Renders only at ≥3 points. Color law: green = net move TOWARD the
 *  graded side, amber = net AGAINST, dim = flat. Never red — nothing settled. */
export function LineSparkline({ p }: { p: StripProp }) {
  const h = p.history;
  if (!Array.isArray(h) || h.length < 3) return null;
  const lines = h.map((pt) => pt && pt.line).filter((v): v is number => typeof v === 'number' && Number.isFinite(v));
  if (lines.length < 3) return null;
  const raw = lines[lines.length - 1] - lines[0];
  const toward = String(p.side || 'O').toUpperCase().startsWith('U') ? -raw : raw;
  const color = toward > 0 ? 'var(--g-a)' : toward < 0 ? 'var(--amber)' : 'var(--text-1)';
  return (
    <span title={`Line movement since lock — ${lines[0]} → ${lines[lines.length - 1]} (${lines.length} captures)`} style={{ display: 'inline-flex', alignItems: 'center' }}>
      <Sparkline data={lines} color={color} w={64} h={16} />
    </span>
  );
}

/** Session 64 (A1-S5) — lineup + injury viability chips (real feeds only). */
export function ViabilityChips({ lineup, injury }: {
  lineup?: { status: string; slot?: number } | null;
  injury?: { status: string; detail?: string | null } | null;
}) {
  const chips: Array<{ label: string; color: string; title?: string }> = [];
  if (lineup) {
    if (lineup.status === 'confirmed') chips.push({ label: `CONFIRMED${lineup.slot ? ` · #${lineup.slot}` : ''}`, color: 'var(--g-a, #00D4A0)', title: 'In the posted lineup' });
    else if (lineup.status === 'not_in') chips.push({ label: 'NOT IN LINEUP', color: 'var(--miss, #FF5252)', title: 'Lineup posted without this player — the read is dead' });
    else if (lineup.status === 'projected') chips.push({ label: 'PROJ', color: 'var(--text-2, #4A4A5E)', title: 'Lineup not posted yet' });
  }
  if (injury) {
    const color = injury.status === 'OUT' ? 'var(--miss, #FF5252)' : injury.status === 'GTD' ? 'var(--amber, #FFB347)' : 'var(--text-1, #7A7A8E)';
    chips.push({ label: injury.status, color, title: injury.detail || undefined });
  }
  if (chips.length === 0) return null;
  return (
    <span style={{ display: 'inline-flex', gap: 5, marginLeft: 7 }}>
      {chips.map((c, i) => (
        <span key={i} className="mono" title={c.title} style={{ fontSize: 9, fontWeight: 800, letterSpacing: '0.07em', padding: '1px 5px', borderRadius: 3, color: c.color, border: `1px solid color-mix(in srgb, ${c.color} 45%, transparent)` }}>
          {c.label}
        </span>
      ))}
    </span>
  );
}

/** Phase 2.5 movement chip: STEAM ▲ (market chasing), VALUE ▲ (better
 *  entry, read holds). Revisions render on the grade itself. */
function MovementChip({ p }: { p: StripProp }) {
  const m = p.movement;
  if (!m || (m.kind !== 'steam' && m.kind !== 'value')) return null;
  const steam = m.kind === 'steam';
  return (
    <span
      className="mono"
      title={steam ? 'Market moved toward the graded side since lock' : 'Line moved against the grade — re-graded and the read holds: better entry'}
      style={{ fontSize: 9.5, fontWeight: 800, letterSpacing: '0.06em', padding: '1px 5px', borderRadius: 3,
        color: steam ? 'var(--amber, #FFB347)' : 'var(--g-a, #00D4A0)',
        border: `1px solid ${steam ? 'rgba(255,179,71,.4)' : 'rgba(0,212,160,.4)'}` }}
    >
      {steam ? `STEAM ▲ ${m.delta > 0 ? '+' : ''}${m.delta}` : 'VALUE ▲ better entry'}
    </span>
  );
}
export interface StripArchetype {
  primary: string;
  secondary?: string | null;
}

interface StatStripProps {
  player: string;
  team: string;
  sport?: string;
  // Wave 2A — real headshot ids threaded from the snapshot grade. MLB → MLBAM
  // playerId; NBA/WNBA → ESPN espnId. Absent → PlayerAvatar monogram.
  playerId?: string | number | null;
  espnId?: string | number | null;
  // Wave 2B — a resolved absolute ESPN headshot URL (wins over a constructed one).
  headshotUrl?: string | null;
  archetype?: StripArchetype;
  // Session 64 (A1-S5) — viability (lineup confirmation + injury wire).
  lineup?: { status: string; slot?: number } | null;
  injury?: { status: string; detail?: string | null } | null;
  stats: StatCell[];
  last10?: StatCell[] | string;
  props?: StripProp[];
  meta?: string; // expanded: "ATL · 3B · #27"
  variant?: 'compact' | 'expanded';
  onPlayerClick?: () => void;
  // Session 50 — Parlay Lab "+" per graded prop (wired by the card via useParlay).
  onAddLeg?: (p: StripProp) => void;
  isLegActive?: (p: StripProp) => boolean;
}

const Sep = ({ ch = '|' }: { ch?: string }) => (
  <span style={{ color: '#3A3A48', margin: '0 8px' }}>{ch}</span>
);

/**
 * StatStrip (Session 42) — the horizontal player line. Ported from the design's
 * Stat Strip section. The player name appears ONCE; stats flow horizontally as
 * a JetBrains Mono run (never stacked with the name repeated per stat).
 *   compact  = game cards / inline
 *   expanded = profile hero / grade result
 */
export default function StatStrip({
  player,
  team,
  sport,
  playerId,
  espnId,
  headshotUrl,
  archetype,
  lineup,
  injury,
  stats,
  last10,
  props,
  meta,
  variant = 'compact',
  onPlayerClick,
  onAddLeg,
  isLegActive,
}: StatStripProps) {
  const ParlayBtn = ({ p }: { p: StripProp }) => {
    if (!onAddLeg || !p.grade) return null;
    const active = isLegActive ? isLegActive(p) : false;
    return (
      <button
        type="button"
        onClick={(e) => { e.preventDefault(); e.stopPropagation(); onAddLeg(p); }}
        title={active ? 'Remove from Parlay' : 'Add to Parlay'}
        aria-label={active ? 'Remove from Parlay' : 'Add to Parlay'}
        className="mono"
        style={{
          cursor: 'pointer', width: 20, height: 20, borderRadius: 5, lineHeight: 1,
          background: active ? 'color-mix(in srgb, var(--g-a) 18%, transparent)' : 'var(--bg-2)',
          border: `1px solid ${active ? 'var(--g-a)' : 'var(--border-hi)'}`,
          color: 'var(--g-a)', fontSize: 13, fontWeight: 700,
          display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
        }}
      >
        {active ? '✓' : '+'}
      </button>
    );
  };
  // Session 55 — settled outcome chip. The self-learning loop's transparency
  // moment: show HIT and MISS, with the real stat. Data → mono, never glitches.
  const OutcomeChip = ({ p }: { p: StripProp }) => {
    if (!p.outcome || !p.outcome.result) return null;
    const r = String(p.outcome.result).toLowerCase();
    const map: Record<string, { label: string; color: string; bg: string }> = {
      hit: { label: '✓ HIT', color: 'var(--hit, #00D4A0)', bg: 'color-mix(in srgb, var(--g-a, #00D4A0) 16%, transparent)' },
      miss: { label: '✕ MISS', color: 'var(--miss, #FF4757)', bg: 'color-mix(in srgb, #FF4757 16%, transparent)' },
      push: { label: 'PUSH', color: 'var(--text-1, #B8BCC8)', bg: 'var(--bg-2, #12121A)' },
    };
    const s = map[r] || map.push;
    const actual = p.outcome.actual != null ? ` (${p.outcome.actual})` : '';
    return (
      <span className="mono" title="Settled against the real result" style={{ fontSize: 10, fontWeight: 700, letterSpacing: '0.04em', color: s.color, background: s.bg, padding: '2px 6px', borderRadius: 4 }}>
        {s.label}{actual}
      </span>
    );
  };
  // A1 S3 — BOOK IT is a real sportsbook deep link (was the Session 52
  // teaser). Targets the best-priced book when one is known, else the
  // prop's source book, else DraftKings. Links are ORGANIC until the
  // operator flips a book in affiliateConfig — the anchor is identical
  // either way, and rel is always BOOK_LINK_REL (sponsored noopener
  // noreferrer) for affiliate compliance.
  const BookItTeaser = ({ p }: { p: StripProp }) => {
    if (!p.grade) return null;
    const target = (p.bestBook && p.bestBook.book) || p.book || 'draftkings';
    const link = buildBookLink({ book: target, player, sport });
    if (!link) return null;
    return (
      <a
        className="mono"
        href={link.url}
        target="_blank"
        rel={BOOK_LINK_REL}
        onClick={(e) => e.stopPropagation()}
        title={`Open ${bookInfo(target).name} — search lands on ${player}`}
        style={{ fontSize: 9, fontWeight: 700, letterSpacing: '0.06em', color: 'var(--text-1)', cursor: 'pointer', padding: '2px 5px', borderRadius: 4, border: '1px solid var(--border)', textDecoration: 'none' }}
      >
        BOOK IT ⟶
      </a>
    );
  };
  // A1 S3 — best available price marker. ONE meaning: this prop's line is
  // posted at a better price at `bestBook` than at least one other book.
  // Renders only when the adapter verified ≥2 books at the same line.
  const BestPriceDot = ({ p }: { p: StripProp }) => {
    if (!p.bestBook) return null;
    const odds = p.bestBook.odds;
    const oddsStr = typeof odds === 'number' && odds > 0 ? `+${odds}` : String(odds);
    return (
      <span
        title={`Best available price · ${bookInfo(p.bestBook.book).name} ${oddsStr}`}
        aria-label={`Best available price at ${bookInfo(p.bestBook.book).name}, ${oddsStr}`}
        style={{ width: 6, height: 6, borderRadius: '50%', display: 'inline-block', flexShrink: 0,
          background: 'var(--g-a)', boxShadow: '0 0 0 2px rgba(0,212,160,.22)' }}
      />
    );
  };
  const last10Str = typeof last10 === 'string'
    ? last10
    : Array.isArray(last10)
      ? last10.map((c) => `${c.value} ${c.label}`).join(' · ')
      : '';

  const nameStyle: React.CSSProperties = onPlayerClick
    ? { cursor: 'pointer', textDecoration: 'none', color: 'inherit' }
    : {};

  const PlayerName = ({ children, ...rest }: { children: React.ReactNode } & React.HTMLAttributes<HTMLSpanElement>) =>
    onPlayerClick ? (
      <span role="link" tabIndex={0} onClick={onPlayerClick} onKeyDown={(e) => { if (e.key === 'Enter') onPlayerClick(); }} style={{ ...nameStyle, ...(rest.style || {}) }}>
        {children}
      </span>
    ) : (
      <span {...rest}>{children}</span>
    );

  if (variant === 'expanded') {
    return (
      <div style={{ background: 'var(--bg-1)', border: '1px solid var(--border)', borderRadius: 12, padding: 20 }}>
        <div style={{ display: 'flex', alignItems: 'baseline', justifyContent: 'space-between', gap: 12, flexWrap: 'wrap' }}>
          <PlayerName style={{ fontWeight: 800, fontSize: 22, letterSpacing: '-0.01em', textTransform: 'uppercase', ...nameStyle }}>
            {player}
          </PlayerName>
          <span className="mono" style={{ fontSize: 12, color: 'var(--text-1)' }}>{meta || team}</span>
        </div>
        {archetype && (
          <div style={{ marginTop: 10, display: 'flex', alignItems: 'center', gap: 10, flexWrap: 'wrap' }}>
            <ArchetypeBadge archetype={archetype.primary} size="md" showDesc />
            {archetype.secondary && <ArchetypeBadge archetype={archetype.secondary} size="md" variant="ghost" />}
          </div>
        )}
        <div style={{ height: 1, background: 'var(--border)', margin: '16px 0' }} />
        <div className="mono" style={{ display: 'flex', gap: 26, flexWrap: 'wrap', marginBottom: 10 }}>
          {stats.map((s, i) => (
            <div key={i}>
              <span style={{ fontSize: 18, color: '#fff', fontWeight: 600 }}>{s.value}</span>{' '}
              <span style={{ fontSize: 11, color: 'var(--text-1)' }}>{s.label}</span>
            </div>
          ))}
        </div>
        {last10Str && (
          <div className="mono" style={{ fontSize: 12, color: 'var(--text-1)' }}>
            <span style={{ color: 'var(--g-a)', letterSpacing: '0.04em' }}>LAST 10</span>
            <Sep ch="·" />
            {last10Str}
          </div>
        )}
      </div>
    );
  }

  // compact
  return (
    <div style={{ background: 'var(--bg-1)', border: '1px solid var(--border)', borderRadius: 12, padding: '14px 16px', display: 'flex', flexDirection: 'column', gap: 9 }}>
      <div style={{ display: 'flex', alignItems: 'center', gap: 9, flexWrap: 'wrap' }}>
        {/* DS0 — player identity block: real headshot / team-colored monogram
            (never the gray silhouette). Team drives the accent color. */}
        <PlayerAvatar name={player} sport={sport} playerId={playerId} espnId={espnId} headshotUrl={headshotUrl} team={team} size={30} />
        <PlayerName style={{ fontWeight: 700, fontSize: 14, color: '#fff', ...nameStyle }}>{player}</PlayerName>
        {/* Rev 3 — team context anchored by a real team chip (logo / monogram). */}
        <TeamChip team={team} sport={sport} size={12} />
        {archetype && <ArchetypeBadge archetype={archetype.primary} size="sm" variant="full" />}
        {archetype?.secondary && (
          <>
            <span className="mono" style={{ color: '#3A3A48', fontSize: 11 }}>/</span>
            <ArchetypeBadge archetype={archetype.secondary} size="sm" variant="ghost" />
          </>
        )}
        {/* ROW-GRAMMAR §2 — identity is one contiguous run (name → team →
            archetype); viability status chips FOLLOW it. Session 64 chips. */}
        <ViabilityChips lineup={lineup} injury={injury} />
      </div>
      <div className="mono game-lines-grid" style={{ fontSize: 12, color: 'var(--text-0)', letterSpacing: '0.02em' }}>
        {stats.map((s, i) => (
          <span key={i}>
            {i > 0 && <Sep />}
            {s.value} {s.label}
          </span>
        ))}
      </div>
      {props && props.length > 0 && (() => {
        // Session 45 — snapshot mode renders each prop as a block with its
        // locked grade + market-movement sub-line; otherwise the inline chip row.
        const snapshotMode = props.some((p) => p.gradedAt || p.delta || p.awaiting);
        if (!snapshotMode) {
          return (
            <div className="game-lines-grid" style={{ display: 'flex', alignItems: 'center', gap: 10, flexWrap: 'wrap', overflowX: 'auto' }}>
              <span className="mono" style={{ fontSize: 11, color: 'var(--text-1)', letterSpacing: '0.06em' }}>PROPS</span>
              {props.map((p, i) => (
                <span key={i} style={{ display: 'inline-flex', alignItems: 'center', gap: 10 }}>
                  {i > 0 && <span style={{ color: '#3A3A48' }}>·</span>}
                  <span className="mono" style={{ display: 'inline-flex', alignItems: 'center', gap: 6, fontSize: 11, color: '#B8BCC8' }}>
                    {p.stat} {p.side}{p.line} <BestPriceDot p={p} /> {p.grade && <GradeBadge grade={p.grade} size="sm" />}
                    <ParlayBtn p={p} />
                    <BookItTeaser p={p} />
                  </span>
                </span>
              ))}
            </div>
          );
        }
        return (
          <div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
            {props.map((p, i) => {
              if (p.awaiting) {
                // Session 59 (work-order 2.3) — the honest waiting state: the
                // real next pipeline run, not passive mystery.
                const next = nextRunLabelET();
                return (
                  <div key={i} className="mono" style={{ fontSize: 11, color: 'var(--text-2)', display: 'flex', alignItems: 'center', gap: 8 }}>
                    <span style={{ color: 'var(--text-1)' }}>{p.stat} {p.line}</span>
                    <BestPriceDot p={p} />
                    <span style={{ color: 'var(--text-2)', fontStyle: 'italic' }}>{next ? `Grades post ${next}` : 'Awaiting next read'}</span>
                  </div>
                );
              }
              const toward = p.delta?.direction === 'toward';
              return (
                <div key={i} style={{ display: 'flex', flexDirection: 'column', gap: 2 }}>
                  <div className="mono" style={{ display: 'inline-flex', alignItems: 'center', gap: 7, fontSize: 12, color: '#B8BCC8', flexWrap: 'wrap' }}>
                    <span style={{ color: '#fff' }}>{p.stat} {p.side}{p.line}</span>
                    {/* ROW-GRAMMAR slot 4 — market context: best-price dot,
                        then movement chip, BEFORE any model output. */}
                    <BestPriceDot p={p} />
                    {!p.dead && <MovementChip p={p} />}
                    {/* ROW-GRAMMAR slot 5 — model output. Phase 2.5: a revised
                        grade is PUBLIC — original struck through, then current. */}
                    {p.revisedFrom && (
                      <span className="mono" title="Grade revised after the line moved against the read — original preserved" style={{ fontSize: 10.5, color: 'var(--text-2)', textDecoration: 'line-through' }}>{p.revisedFrom}</span>
                    )}
                    {p.grade && (
                      p.dead ? (
                        // Session 64 (A1-S5) — NOT IN LINEUP: the grade is dead.
                        // Struck through, never deleted — the lock is history.
                        <span className="mono" title="Player is not in the posted lineup — this read is dead" style={{ display: 'inline-flex', alignItems: 'center', gap: 5 }}>
                          <span style={{ textDecoration: 'line-through', color: 'var(--text-2)', fontWeight: 800, fontSize: 12 }}>{p.grade}</span>
                          <span style={{ fontSize: 9, fontWeight: 800, letterSpacing: '0.07em', color: 'var(--miss, #FF5252)', border: '1px solid color-mix(in srgb, var(--miss, #FF5252) 45%, transparent)', borderRadius: 3, padding: '1px 5px' }}>NOT IN LINEUP</span>
                        </span>
                      ) : (
                        <GradeBadge grade={p.grade} size="sm" />
                      )
                    )}
                    {/* ROW-GRAMMAR slot 6 — the outcome slot: live TRACKING
                        proto-outcome while in-progress, settled chip once
                        final (mutually exclusive — LiveTracker self-hides on
                        outcome). A1 S11. */}
                    {!p.dead && <LiveTracker p={p} />}
                    <OutcomeChip p={p} />
                    {/* ROW-GRAMMAR slot 7 — actions are suppressed once the
                        game is LIVE (the pre-game market for the locked line
                        is closed), dead, or settled. */}
                    {!p.outcome && !p.dead && !p.live && <ParlayBtn p={p} />}
                    {!p.outcome && !p.dead && !p.live && <BookItTeaser p={p} />}
                    {p.gradedAt?.ago && (
                      <span style={{ color: 'var(--text-2)' }}>
                        Graded {p.gradedAt.ago}{p.gradedAt.odds != null ? ` at ${p.gradedAt.odds}` : ''}
                      </span>
                    )}
                  </div>
                  {/* ROW-GRAMMAR sub-line — stat + market context, fixed order:
                      last-10 dots · line sparkline · current-line delta. Each
                      renders only on real data; all absent → no sub-line. */}
                  {(p.last10Dots?.length || (p.history && p.history.length >= 3) || p.delta) ? (
                    <div className="mono" style={{ fontSize: 11, display: 'flex', alignItems: 'center', gap: 10, flexWrap: 'wrap' }}>
                      <DotStrip dots={p.last10Dots} />
                      <LineSparkline p={p} />
                      {p.delta && (
                        <span style={{ color: toward ? 'var(--g-a)' : 'var(--amber)' }}>
                          Current {p.delta.currentLine} · {p.delta.delta > 0 ? '▲' : '▼'} {toward ? 'TOWARD' : 'AWAY'} {p.delta.delta > 0 ? '+' : ''}{p.delta.delta}
                        </span>
                      )}
                    </div>
                  ) : null}
                </div>
              );
            })}
          </div>
        );
      })()}
    </div>
  );
}
