// DrillView — letter-pair drilling for 3BLD (OP corners / M2 edges)
// Detection is move-based (smart cube required). Moves are applied to a
// virtual cube state independent of the physical cube.

// ─── helpers ──────────────────────────────────────────────────────────────────

// All Speffz stickers that belong to each piece, grouped by slot.
// Used to build the pair pool (exclude buffer-piece stickers from targets).
const CORNER_STICKERS_BY_SLOT = [
  ['C','J','M'], // UFR=0
  ['B','N','Q'], // UBR=1
  ['A','R','E'], // UBL=2
  ['D','I','F'], // UFL=3
  ['V','K','P'], // DFR=4
  ['W','T','O'], // DBR=5
  ['X','S','H'], // DBL=6
  ['U','L','G'], // DFL=7
];
const EDGE_STICKERS_BY_SLOT = [
  ['C','I'], // UF=0
  ['B','N'], // UR=1
  ['A','R'], // UB=2
  ['D','E'], // UL=3
  ['U','K'], // DF=4
  ['V','O'], // DR=5
  ['W','T'], // DB=6
  ['X','G'], // DL=7
  ['J','M'], // FR=8
  ['L','F'], // FL=9
  ['Q','P'], // BR=10
  ['S','H'], // BL=11
];

// Primary (U/D or F/B) letter for each slot — used to label pairs in the UI.
// Corners: UFR=C, UBR=B, UBL=A, UFL=D, DFR=V, DBR=W, DBL=X, DFL=U
// Edges: UF=C, UR=B, UB=A, UL=D, DF=U, DR=V, DB=W, DL=X, FR=J, FL=L, BR=Q, BL=S

function bufferSlotFor(letter, method) {
  return method === 'corners'
    ? _CORNER_LETTER_TO_SLOT[letter]
    : _EDGE_LETTER_TO_SLOT[letter];
}

// All valid target letters for the given method, excluding the buffer piece's stickers.
function validTargetLetters(bufferLetter, method) {
  const bufSlot = bufferSlotFor(bufferLetter, method);
  if (bufSlot == null) return [];
  const bySlot = method === 'corners' ? CORNER_STICKERS_BY_SLOT : EDGE_STICKERS_BY_SLOT;
  const bufStickers = new Set(bySlot[bufSlot]);
  return bySlot.flatMap((stickers, slot) =>
    slot === bufSlot ? [] : stickers.filter(s => !bufStickers.has(s))
  );
}

// All valid ORDERED letter pairs (L1, L2) excluding same-piece pairs.
function validPairs(bufferLetter, method) {
  const bySlot = method === 'corners' ? CORNER_STICKERS_BY_SLOT : EDGE_STICKERS_BY_SLOT;
  const lookup = method === 'corners' ? _CORNER_LETTER_TO_SLOT : _EDGE_LETTER_TO_SLOT;
  const bufSlot = bufferSlotFor(bufferLetter, method);
  if (bufSlot == null) return [];
  const bufStickers = new Set(bySlot[bufSlot]);
  const targets = bySlot.flatMap((stickers, slot) =>
    slot === bufSlot ? [] : stickers.filter(s => !bufStickers.has(s))
  );
  const pairs = [];
  for (const l1 of targets) {
    for (const l2 of targets) {
      if (l1 === l2) continue;
      if (lookup[l1] === lookup[l2]) continue; // same piece
      pairs.push(l1 + l2);
    }
  }
  return pairs;
}

// Check whether the two target slots of a pair are solved in the virtual pattern.
function checkPairSolved(method, l1, l2, virtualPattern) {
  const lookup = method === 'corners' ? _CORNER_LETTER_TO_SLOT : _EDGE_LETTER_TO_SLOT;
  const orbitName = method === 'corners' ? 'CORNERS' : 'EDGES';
  const orbit = virtualPattern.patternData[orbitName] || virtualPattern.patternData[orbitName.toLowerCase()];
  if (!orbit) return false;
  const s1 = lookup[l1];
  const s2 = lookup[l2];
  return (
    orbit.pieces[s1] === s1 && orbit.orientation[s1] === 0 &&
    orbit.pieces[s2] === s2 && orbit.orientation[s2] === 0
  );
}

// Build a setup alg for the given pair using either the alg DB or a random scramble
// that contains the pair in its memo. Returns { setupAlg, scrambleAlg, source }.
async function buildSetupAlg(method, pair, bufferLetter, algDb) {
  const [l1, l2] = [pair[0], pair[1]];

  // Phase 2: algorithm database
  if (algDb) {
    const dbMethod = method === 'corners' ? 'corners' : 'edges';
    const alg = algDb[dbMethod]?.[pair];
    if (alg) {
      const setupAlg = invertAlg(alg);
      return { setupAlg, source: 'algdb' };
    }
  }

  // Phase 1: find a random scramble whose memo starts with (or contains) this pair
  const genMemo = method === 'corners' ? generateCornerMemo : generateEdgeMemo;
  for (let attempt = 0; attempt < 15; attempt++) {
    const scramble = window._cubingScramble
      ? await window._cubingScramble('333bf').catch(() => generateScramble(20))
      : generateScramble(20);
    try {
      const pd = window._cubing3x3.patternFromAlg(scramble).patternData;
      const memo = genMemo(pd, bufferLetter);
      if (!memo || memo.length < 2) continue;
      // Accept if the pair appears consecutively anywhere in the memo
      for (let i = 0; i < memo.length - 1; i += 2) {
        if (memo[i] === l1 && memo[i + 1] === l2) {
          return { setupAlg: scramble, source: 'random' };
        }
      }
    } catch {}
  }
  // Fallback: return any scramble (pair may not appear, but we still train detection)
  const fallback = window._cubingScramble
    ? await window._cubingScramble('333bf').catch(() => generateScramble(20))
    : generateScramble(20);
  return { setupAlg: fallback, source: 'fallback' };
}

// Per-pair stats derived from drillResults array.
function pairStats(results, method, pair) {
  const relevant = results.filter(r => r.method === method && r.pair === pair);
  if (!relevant.length) return null;
  const times = relevant.filter(r => !r.dnf && r.timeMs != null).map(r => r.timeMs);
  const avg = times.length ? Math.round(times.reduce((a, b) => a + b, 0) / times.length) : null;
  const successRate = Math.round((1 - relevant.filter(r => r.dnf).length / relevant.length) * 100);
  // Trend: compare avg of most recent 3 vs previous 3
  let trend = 0;
  if (times.length >= 6) {
    const recent = times.slice(-3).reduce((a, b) => a + b, 0) / 3;
    const prev   = times.slice(-6, -3).reduce((a, b) => a + b, 0) / 3;
    trend = recent < prev * 0.95 ? -1 : recent > prev * 1.05 ? 1 : 0;
  }
  return { attempts: relevant.length, avg, successRate, trend };
}

// ─── component ────────────────────────────────────────────────────────────────

function DrillView({ puzzleId, bldLettering, drillResults, onDrillComplete, algDb, btConnected }) {
  const [method, setMethod] = React.useState('corners');
  const [drillPhase, setDrillPhase] = React.useState('idle'); // idle|loading|ready|running|done
  const [currentPair, setCurrentPair] = React.useState(null);
  const [setupSource, setSetupSource] = React.useState(null);
  const [startTime, setStartTime] = React.useState(null);
  const [elapsedMs, setElapsedMs] = React.useState(0);
  const [moveCount, setMoveCount] = React.useState(0);
  const [lastResult, setLastResult] = React.useState(null); // { pair, timeMs, dnf }
  const [showStats, setShowStats] = React.useState(false);

  const virtualPatternRef = React.useRef(null);
  const setupAlgRef = React.useRef('');
  const rafRef = React.useRef(null);
  const hostRef = React.useRef(null);
  const playerRef = React.useRef(null);
  const [playerReady, setPlayerReady] = React.useState(!!window.TwistyPlayer);

  const bufferLetter = method === 'corners'
    ? (bldLettering?.cornerBuffer || DEFAULT_BLD_LETTERING.cornerBuffer)
    : (bldLettering?.edgeBuffer   || DEFAULT_BLD_LETTERING.edgeBuffer);

  const allPairs = React.useMemo(
    () => validPairs(bufferLetter, method),
    [bufferLetter, method]
  );

  // Filter to only primary-sticker pairs for cleaner UX (23 corner pairs, 11 edge pairs)
  const primaryPairs = React.useMemo(() => {
    const primaryCorners = new Set(_CORNER_PRIMARY);
    const primaryEdges   = new Set(_EDGE_PRIMARY);
    const primary = method === 'corners' ? primaryCorners : primaryEdges;
    const bufSlot = bufferSlotFor(bufferLetter, method);
    const bufPrimary = method === 'corners'
      ? _CORNER_PRIMARY[bufSlot] : _EDGE_PRIMARY[bufSlot];
    return allPairs.filter(p =>
      primary.has(p[0]) && primary.has(p[1]) &&
      p[0] !== bufPrimary && p[1] !== bufPrimary
    );
  }, [allPairs, bufferLetter, method]);

  // Cycle through pairs in session — pick randomly (excluding recently done pairs later)
  const pickPair = React.useCallback(() => {
    if (!primaryPairs.length) return null;
    return primaryPairs[Math.floor(Math.random() * primaryPairs.length)];
  }, [primaryPairs]);

  // TwistyPlayer setup
  React.useEffect(() => {
    if (window.TwistyPlayer) { setPlayerReady(true); return; }
    const t = setInterval(() => { if (window.TwistyPlayer) { setPlayerReady(true); clearInterval(t); } }, 100);
    return () => clearInterval(t);
  }, []);

  React.useEffect(() => {
    if (!playerReady || !hostRef.current) return;
    if (playerRef.current) { playerRef.current.remove(); playerRef.current = null; }
    const player = new window.TwistyPlayer({
      puzzle: '3x3x3', alg: '',
      experimentalSetupAnchor: 'start',
      background: 'none', controlPanel: 'none',
      hintFacelets: 'none', visualization: '3D',
    });
    player.style.width = '100%';
    player.style.height = '220px';
    hostRef.current.appendChild(player);
    player.alg = '';
    player.experimentalSetupAlg = setupAlgRef.current;
    playerRef.current = player;
    return () => { player.remove(); playerRef.current = null; };
  }, [playerReady]);

  function updatePlayer(alg) {
    setupAlgRef.current = alg;
    if (playerRef.current) {
      playerRef.current.alg = '';
      playerRef.current.experimentalSetupAlg = alg;
    }
  }

  // Start a drill for the given pair
  async function startDrill(pair) {
    if (!window._cubing3x3) return;
    setDrillPhase('loading');
    setCurrentPair(pair);
    setMoveCount(0);
    setElapsedMs(0);
    setLastResult(null);

    const { setupAlg, source } = await buildSetupAlg(method, pair, bufferLetter, algDb);
    setSetupSource(source);

    try {
      virtualPatternRef.current = window._cubing3x3.patternFromAlg(setupAlg);
    } catch {
      virtualPatternRef.current = window._cubing3x3.defaultPattern();
    }
    updatePlayer(setupAlg);
    setDrillPhase('ready');
  }

  function beginRunning() {
    setDrillPhase('running');
    const t = Date.now();
    setStartTime(t);
    rafRef.current = requestAnimationFrame(function tick() {
      setElapsedMs(Date.now() - t);
      rafRef.current = requestAnimationFrame(tick);
    });
  }

  function finishDrill(dnf) {
    cancelAnimationFrame(rafRef.current);
    const timeMs = dnf ? null : elapsedMs;
    const result = {
      id: Date.now(), at: Date.now(),
      puzzleId, method,
      pair: currentPair,
      timeMs,
      dnf,
      moveCount,
    };
    setLastResult({ pair: currentPair, timeMs, dnf });
    setDrillPhase('done');
    onDrillComplete(result);
  }

  // Listen to cube:move events while running
  React.useEffect(() => {
    if (drillPhase !== 'running') return;
    function onMove(e) {
      const move = e.detail?.move;
      if (!move || !virtualPatternRef.current || !currentPair) return;
      try {
        virtualPatternRef.current = window._cubing3x3.applyMove(virtualPatternRef.current, move);
        setMoveCount(n => n + 1);
        const [l1, l2] = [currentPair[0], currentPair[1]];
        if (checkPairSolved(method, l1, l2, virtualPatternRef.current)) {
          finishDrill(false);
        }
      } catch {}
    }
    window.addEventListener('cube:move', onMove);
    return () => window.removeEventListener('cube:move', onMove);
  }, [drillPhase, currentPair, method]);

  // Keyboard: Space = start (ready→running) or DNF (running→done), N = next pair (done→ready)
  React.useEffect(() => {
    function onKey(e) {
      if (e.key === ' ') {
        e.preventDefault();
        if (drillPhase === 'ready') beginRunning();
        else if (drillPhase === 'running') finishDrill(true);
      }
      if (e.key === 'n' || e.key === 'N') {
        if (drillPhase === 'done' || drillPhase === 'idle') {
          const pair = pickPair();
          if (pair) startDrill(pair);
        }
      }
    }
    window.addEventListener('keydown', onKey);
    return () => window.removeEventListener('keydown', onKey);
  }, [drillPhase, elapsedMs, pickPair]);

  // Cleanup RAF on unmount
  React.useEffect(() => () => cancelAnimationFrame(rafRef.current), []);

  // ── render ─────────────────────────────────────────────────────────────────

  const pairLetters = currentPair ? [currentPair[0], currentPair[1]] : null;

  return (
    <div style={drillStyles.root}>
      {/* Header */}
      <div style={drillStyles.header}>
        <span style={drillStyles.title}>DRILL</span>
        <div style={drillStyles.methodTabs}>
          {['corners', 'edges'].map(m => (
            <button key={m} style={{
              ...drillStyles.methodTab,
              ...(method === m ? drillStyles.methodTabActive : {}),
            }} onClick={() => { setMethod(m); setDrillPhase('idle'); setCurrentPair(null); }}>
              {m === 'corners' ? `Corners (buf ${bufferLetter})` : `Edges (buf ${bldLettering?.edgeBuffer || DEFAULT_BLD_LETTERING.edgeBuffer})`}
            </button>
          ))}
        </div>
        <button
          style={{...drillStyles.statsToggle, ...(showStats ? drillStyles.statsToggleActive : {})}}
          onClick={() => setShowStats(s => !s)}
        >
          {showStats ? '← Drill' : 'Stats'}
        </button>
      </div>

      {showStats ? (
        <StatsPanel results={drillResults} method={method} pairs={primaryPairs} />
      ) : (
        <div style={drillStyles.body}>
          {/* Cube visualizer */}
          <div style={drillStyles.playerShell}>
            {playerReady
              ? <div ref={hostRef} style={{ width: '100%', height: '220px' }} />
              : <div style={drillStyles.playerFallback}>Loading cube…</div>
            }
          </div>

          {/* Pair display */}
          <div style={drillStyles.pairSection}>
            {pairLetters ? (
              <div style={drillStyles.pairLetters}>
                <span style={{
                  ...drillStyles.pairLetter,
                  ...(method === 'corners' ? drillStyles.pairLetterCorner : drillStyles.pairLetterEdge),
                }}>{pairLetters[0]}</span>
                <span style={{
                  ...drillStyles.pairLetter,
                  ...(method === 'corners' ? drillStyles.pairLetterCorner : drillStyles.pairLetterEdge),
                }}>{pairLetters[1]}</span>
              </div>
            ) : (
              <span style={drillStyles.pairEmpty}>Pick a pair to start</span>
            )}
            {setupSource === 'random' && currentPair && (
              <span style={drillStyles.pairNote}>random scramble · pair in memo</span>
            )}
            {setupSource === 'fallback' && currentPair && (
              <span style={drillStyles.pairNote}>random scramble · add algorithm for pure setup</span>
            )}
          </div>

          {/* Timer */}
          <div style={drillStyles.timerRow}>
            <span style={{
              ...drillStyles.timer,
              ...(drillPhase === 'running' ? drillStyles.timerRunning : {}),
              ...(lastResult?.dnf ? drillStyles.timerDnf : {}),
            }}>
              {lastResult?.dnf ? 'DNF'
                : lastResult?.timeMs != null ? formatTime(lastResult.timeMs)
                : formatTime(drillPhase === 'running' ? elapsedMs : 0)}
            </span>
            {drillPhase === 'running' && (
              <span style={drillStyles.moveCount}>{moveCount} moves</span>
            )}
          </div>

          {/* Controls */}
          <div style={drillStyles.controls}>
            {(drillPhase === 'idle' || drillPhase === 'done') && (
              <button
                style={drillStyles.primaryBtn}
                onClick={() => { const p = pickPair(); if (p) startDrill(p); }}
                disabled={!window._cubing3x3 || !btConnected}
              >
                {drillPhase === 'done' ? 'Next pair  N' : 'Start  N'}
              </button>
            )}
            {drillPhase === 'loading' && (
              <span style={drillStyles.loadingMsg}>Setting up…</span>
            )}
            {drillPhase === 'ready' && (
              <>
                <button style={drillStyles.primaryBtn} onClick={beginRunning}>
                  Go  Space
                </button>
                <button style={drillStyles.ghostBtn} onClick={() => { const p = pickPair(); if (p) startDrill(p); }}>
                  Shuffle
                </button>
              </>
            )}
            {drillPhase === 'running' && (
              <button style={drillStyles.dnfBtn} onClick={() => finishDrill(true)}>
                DNF  Space
              </button>
            )}

            {!btConnected && drillPhase === 'idle' && (
              <span style={drillStyles.noCubeNote}>Connect a smart cube to use the drill</span>
            )}
            {!window._cubing3x3 && (
              <span style={drillStyles.noCubeNote}>cubing.js loading…</span>
            )}
          </div>

          {/* Last result quick stats */}
          {lastResult && (() => {
            const s = pairStats(drillResults, method, lastResult.pair);
            if (!s) return null;
            return (
              <div style={drillStyles.resultStats}>
                <span style={drillStyles.resultStat}>{lastResult.pair} · {s.attempts} attempt{s.attempts !== 1 ? 's' : ''}</span>
                {s.avg != null && <span style={drillStyles.resultStat}>avg {formatTime(s.avg)}</span>}
                <span style={drillStyles.resultStat}>{s.successRate}% ok</span>
                {s.trend !== 0 && <span style={drillStyles.resultStat}>{s.trend < 0 ? '↓ faster' : '↑ slower'}</span>}
              </div>
            );
          })()}
        </div>
      )}
    </div>
  );
}

// ─── Stats panel ─────────────────────────────────────────────────────────────

function StatsPanel({ results, method, pairs }) {
  const rows = pairs.map(pair => {
    const s = pairStats(results, method, pair);
    return { pair, ...s };
  }).filter(r => r.attempts > 0)
    .sort((a, b) => (b.avg ?? 0) - (a.avg ?? 0)); // slowest first

  if (!rows.length) {
    return (
      <div style={drillStyles.statsEmpty}>
        No drill results yet for {method}. Start drilling to see stats here.
      </div>
    );
  }

  return (
    <div style={drillStyles.statsTable}>
      <div style={drillStyles.statsHead}>
        <span style={drillStyles.statsCell}>Pair</span>
        <span style={drillStyles.statsCell}>Attempts</span>
        <span style={drillStyles.statsCell}>Avg</span>
        <span style={drillStyles.statsCell}>OK%</span>
        <span style={drillStyles.statsCell}>Trend</span>
      </div>
      {rows.map(r => (
        <div key={r.pair} style={{
          ...drillStyles.statsRow,
          ...(r.avg == null ? {} : r.avg < 3000 ? drillStyles.statsRowFast : r.avg > 6000 ? drillStyles.statsRowSlow : {}),
        }}>
          <span style={{...drillStyles.statsCell, ...drillStyles.statsPair}}>{r.pair}</span>
          <span style={drillStyles.statsCell}>{r.attempts}</span>
          <span style={drillStyles.statsCell}>{r.avg != null ? formatTime(r.avg) : '—'}</span>
          <span style={drillStyles.statsCell}>{r.successRate}%</span>
          <span style={drillStyles.statsCell}>
            {r.trend < 0 ? '↓' : r.trend > 0 ? '↑' : '→'}
          </span>
        </div>
      ))}
    </div>
  );
}

// ─── styles ──────────────────────────────────────────────────────────────────

const drillStyles = {
  root: {
    display: 'flex', flexDirection: 'column', gap: '0',
    background: 'oklch(12% 0.01 250)',
    border: '1px solid oklch(20% 0.01 250)',
    borderRadius: '10px', overflow: 'hidden',
  },
  header: {
    display: 'flex', alignItems: 'center', gap: '10px',
    padding: '10px 16px',
    borderBottom: '1px solid oklch(18% 0.01 250)',
    background: 'oklch(13% 0.01 250)',
  },
  title: {
    fontFamily: "'JetBrains Mono', monospace",
    fontSize: '10px', fontWeight: '700', letterSpacing: '0.14em',
    color: 'oklch(40% 0.01 250)', flexShrink: 0,
  },
  methodTabs: { display: 'flex', gap: '4px', flex: 1 },
  methodTab: {
    padding: '4px 12px', borderRadius: '5px',
    border: '1px solid transparent',
    background: 'transparent', color: 'oklch(45% 0.01 250)',
    fontSize: '12px', cursor: 'pointer', fontFamily: "'Inter', sans-serif",
  },
  methodTabActive: {
    background: 'oklch(20% 0.10 165)', color: 'oklch(68% 0.18 165)',
    border: '1px solid oklch(30% 0.12 165)',
  },
  statsToggle: {
    padding: '4px 12px', borderRadius: '5px',
    border: '1px solid oklch(22% 0.01 250)',
    background: 'transparent', color: 'oklch(42% 0.01 250)',
    fontSize: '12px', cursor: 'pointer', flexShrink: 0,
  },
  statsToggleActive: {
    background: 'oklch(18% 0.08 200)', color: 'oklch(65% 0.12 200)',
    border: '1px solid oklch(28% 0.08 200)',
  },
  body: {
    display: 'flex', flexDirection: 'column', gap: '0',
  },
  playerShell: {
    background: 'oklch(9% 0.01 250)',
    borderBottom: '1px solid oklch(16% 0.01 250)',
    minHeight: '220px',
  },
  playerFallback: {
    height: '220px', display: 'flex', alignItems: 'center', justifyContent: 'center',
    color: 'oklch(38% 0.01 250)', fontSize: '13px',
  },
  pairSection: {
    display: 'flex', flexDirection: 'column', alignItems: 'center', gap: '4px',
    padding: '20px 16px 10px',
  },
  pairLetters: { display: 'flex', gap: '12px' },
  pairLetter: {
    fontFamily: "'JetBrains Mono', monospace",
    fontSize: '72px', fontWeight: '700', lineHeight: 1,
    width: '80px', textAlign: 'center',
  },
  pairLetterCorner: { color: 'oklch(75% 0.18 80)' },
  pairLetterEdge:   { color: 'oklch(72% 0.14 200)' },
  pairEmpty: {
    fontFamily: "'Inter', sans-serif",
    fontSize: '14px', color: 'oklch(38% 0.01 250)',
  },
  pairNote: {
    fontFamily: "'JetBrains Mono', monospace",
    fontSize: '10px', color: 'oklch(35% 0.01 250)',
    letterSpacing: '0.06em',
  },
  timerRow: {
    display: 'flex', alignItems: 'baseline', justifyContent: 'center', gap: '12px',
    padding: '4px 16px',
  },
  timer: {
    fontFamily: "'JetBrains Mono', monospace",
    fontSize: '40px', fontWeight: '300', color: 'oklch(60% 0.01 250)',
    transition: 'color 0.1s',
  },
  timerRunning: { color: 'oklch(80% 0.01 250)' },
  timerDnf: { color: 'oklch(65% 0.12 25)' },
  moveCount: {
    fontFamily: "'JetBrains Mono', monospace",
    fontSize: '12px', color: 'oklch(38% 0.01 250)',
  },
  controls: {
    display: 'flex', alignItems: 'center', justifyContent: 'center', gap: '10px',
    padding: '12px 16px',
    flexWrap: 'wrap',
  },
  primaryBtn: {
    padding: '10px 28px', borderRadius: '8px', border: 'none',
    background: 'oklch(65% 0.18 165)', color: 'oklch(10% 0.01 250)',
    fontSize: '14px', fontWeight: '700', cursor: 'pointer',
    fontFamily: "'Inter', sans-serif",
  },
  ghostBtn: {
    padding: '10px 20px', borderRadius: '8px',
    border: '1px solid oklch(25% 0.01 250)',
    background: 'transparent', color: 'oklch(55% 0.01 250)',
    fontSize: '13px', cursor: 'pointer', fontFamily: "'Inter', sans-serif",
  },
  dnfBtn: {
    padding: '10px 24px', borderRadius: '8px',
    border: '1px solid oklch(28% 0.10 25)',
    background: 'oklch(20% 0.08 25)', color: 'oklch(68% 0.14 25)',
    fontSize: '13px', fontWeight: '600', cursor: 'pointer',
    fontFamily: "'Inter', sans-serif",
  },
  loadingMsg: {
    fontSize: '13px', color: 'oklch(42% 0.01 250)',
    fontFamily: "'Inter', sans-serif",
  },
  noCubeNote: {
    fontSize: '12px', color: 'oklch(40% 0.01 250)',
    fontFamily: "'Inter', sans-serif",
    textAlign: 'center',
  },
  resultStats: {
    display: 'flex', gap: '14px', justifyContent: 'center', flexWrap: 'wrap',
    padding: '8px 16px 14px',
    borderTop: '1px solid oklch(16% 0.01 250)',
  },
  resultStat: {
    fontFamily: "'JetBrains Mono', monospace",
    fontSize: '11px', color: 'oklch(45% 0.01 250)',
  },
  statsEmpty: {
    padding: '32px 24px', textAlign: 'center',
    fontSize: '13px', color: 'oklch(40% 0.01 250)',
    fontFamily: "'Inter', sans-serif",
  },
  statsTable: {
    display: 'flex', flexDirection: 'column',
    maxHeight: '400px', overflowY: 'auto',
  },
  statsHead: {
    display: 'grid', gridTemplateColumns: '60px 80px 80px 60px 60px',
    padding: '8px 16px',
    borderBottom: '1px solid oklch(18% 0.01 250)',
    background: 'oklch(13% 0.01 250)',
    position: 'sticky', top: 0,
  },
  statsRow: {
    display: 'grid', gridTemplateColumns: '60px 80px 80px 60px 60px',
    padding: '7px 16px',
    borderBottom: '1px solid oklch(15% 0.01 250)',
  },
  statsRowFast: { background: 'oklch(14% 0.06 140)' },
  statsRowSlow: { background: 'oklch(14% 0.05 25)' },
  statsCell: {
    fontFamily: "'JetBrains Mono', monospace",
    fontSize: '12px', color: 'oklch(55% 0.01 250)',
  },
  statsPair: {
    fontSize: '14px', fontWeight: '700', color: 'oklch(78% 0.01 250)',
  },
};

Object.assign(window, { DrillView });
