// ============================================================================
// Combat screen — Duelist Standoff (Direction B)
// ============================================================================

const { useState: cUseState, useEffect: cUseEffect, useRef: cUseRef, useMemo: cUseMemo } = React;

function makeInitialCombatState(cpuKey = 'gambler', playerName = 'You', playerTitle = 'The Drifter', playerKey = 'drifter') {
  return {
    phase: 'preDeal',
    round: 0,
    deck: [],
    community: [],
    playerHole: [],
    cpuHole: [],
    playerHP: COMBAT.MAX_HP,
    cpuHP: COMBAT.MAX_HP,
    playerEnergy: 0,
    cpuEnergy: 0,
    playerAttackUsed: false, playerBlockUsed: false,
    cpuAttackUsed:    false, cpuBlockUsed:    false,
    playerCommit: { attack: false, block: false },
    cpuCommit: { attack: false, block: false },
    playerStrength: 0,
    cpuStrength: 0,
    bet: null,
    hasBet: false,
    lastEval: null,
    events: [],
    foldedRound: null,
    gameWinner: null,
    cpuKey,
    playerKey,
    playerName,
    playerTitle,
  };
}

// CPU descriptors
function cpuSubtitle(key) {
  return {
    greenhorn: 'fresh off the train',
    gambler:   'lady-luck devotee',
    marshal:   'cold, lawful read',
    outlaw:    'all fire, no fear',
  }[key] || '';
}
function cpuDisplayName(key) {
  return {
    greenhorn: 'Greenhorn Pete',
    gambler:   'Lady Diamond',
    marshal:   'Marshal Crane',
    outlaw:    'Black Jack Roy',
  }[key] || 'Opponent';
}
function cpuFlagWord(key) {
  return {
    greenhorn: 'GREENHORN',
    gambler:   'WANTED',
    marshal:   'LAWMAN',
    outlaw:    'OUTLAW',
  }[key] || 'WANTED';
}
function playerFlagWord(key) {
  return {
    drifter:  'DRIFTER',
    preacher: 'PREACHER',
    gunhand:  'GUNHAND',
    widow:    'WIDOW',
  }[key] || 'DUELIST';
}

// Phase title text
function phaseTitleText(phase, bet) {
  if (bet) return 'CALLED OUT';
  switch (phase) {
    case 'preDeal':  return 'ANTE UP';
    case 'preFlop':  return 'HOLE CARDS';
    case 'flop':     return 'THE FLOP';
    case 'turn':     return 'THE TURN';
    case 'river':    return 'STAND OFF';
    case 'showdown': return 'DRAW';
    case 'roundEnd': return 'SETTLED';
    case 'gameOver': return 'FINAL';
    default: return '';
  }
}
function phaseTitleSize(phase, bet) {
  if (phase === 'river' || phase === 'showdown') return 'big';
  if (bet) return 'big';
  return 'med';
}

// Action button labels
function actionLabel(phase) {
  switch (phase) {
    case 'preDeal':  return ['DEAL', 'turn the next hand'];
    case 'preFlop':  return ['FLOP', 'reveal three on the felt'];
    case 'flop':     return ['TURN', 'reveal the fourth'];
    case 'turn':     return ['RIVER', 'reveal the final card'];
    case 'river':    return ['DRAW!', 'show your hand'];
    default: return ['', ''];
  }
}

// ============================================================================
// Modals
// ============================================================================
function BetModal({ state, onCommit, onCancel, onCall, onFold, onRaise }) {
  const bet = state.bet;
  const [stake, setStake] = cUseState(Math.min(20, Math.floor(state.cpuHP * 0.2)));

  // Initial — player initiating
  if (!bet) {
    const maxBet = Math.max(5, Math.min(state.playerHP - 1, state.cpuHP));
    const minBet = 5;
    return (
      <div className="scrim" onClick={onCancel}>
        <div className="panel-parch" data-tutorial="bet-modal" style={{padding:'18px 22px', width:300}} onClick={e=>e.stopPropagation()}>
          <div className="display" style={{fontSize:24, color:'var(--ink-stamp)', textAlign:'center'}}>
            Call 'Em Out
          </div>
          <Ornament width={220} />
          <div style={{fontSize:13, color:'#3a2a18', lineHeight:1.4, marginBottom:12}}>
            Wager part of your hide. If they call and lose, they pay the whole pot. If they fold, their commitment bleeds.
          </div>
          <div style={{display:'flex', justifyContent:'space-between', fontFamily:'var(--font-mono)', fontSize:12, color:'#3a2a18'}}>
            <span>Stake: <b style={{fontSize:18}}>{stake}</b> HP</span>
            <span style={{opacity:.6}}>max {maxBet}</span>
          </div>
          <input
            type="range" min={minBet} max={maxBet} value={stake}
            onChange={e => setStake(+e.target.value)}
            style={{width:'100%', marginTop:6, accentColor:'#8b1a1a'}}
          />
          <div style={{display:'grid', gridTemplateColumns:'repeat(4,1fr)', gap:6, marginTop:6}}>
            {[10, 20, 35, 50].filter(v => v <= maxBet).map(v => (
              <button key={v} className="btn btn-sm" style={{padding:'6px 4px', fontSize:11}} onClick={() => setStake(v)}>
                {v}
              </button>
            ))}
          </div>
          <div style={{display:'flex', gap:8, marginTop:14}}>
            <button className="btn btn-ghost" style={{flex:1}} onClick={onCancel}>Back Off</button>
            <button className="btn btn-primary" style={{flex:1.5}} onClick={() => onCommit(stake)}>Lay it down</button>
          </div>
        </div>
      </div>
    );
  }

  // Responding to opponent bet
  if (bet.awaiting === 'player') {
    const owed = bet.currentRaise - (bet.contributions.player || 0);
    const callDanger = owed >= state.playerHP * 0.5;
    const canRaise = bet.raises < COMBAT.MAX_RAISES;
    const lastAction = bet.log[bet.log.length-1];
    const opponentName = cpuDisplayName(state.cpuKey);
    return (
      <div className="scrim">
        <div className="panel-parch" style={{padding:'18px 22px', width:300}}>
          <div className="display" style={{fontSize:22, color:'var(--ink-stamp)', textAlign:'center'}}>
            {lastAction.action === 'bet' ? `${opponentName} Bets` : `${opponentName} Raises`}
          </div>
          <Ornament width={220} />
          <div style={{textAlign:'center', fontFamily:'var(--font-mono)', color:'#3a2a18'}}>
            <div style={{fontSize:13, opacity:.7}}>Pot</div>
            <div style={{fontSize:28, fontWeight:'bold'}}>{bet.pot} HP</div>
            <div style={{fontSize:12, marginTop:4}}>To call: <b>{owed}</b> HP</div>
          </div>
          <div style={{display:'flex', flexDirection:'column', gap:6, marginTop:14}}>
            <button className="btn" onClick={onFold}>
              Fold <span style={{opacity:.7, marginLeft:6, fontSize:12}}>(lose {bet.contributions.player || Math.floor(bet.currentRaise*0.5)} HP)</span>
            </button>
            <button className={`btn ${callDanger ? 'btn-primary' : 'btn-brass'}`} onClick={onCall}>
              Call ({owed} HP)
            </button>
            {canRaise && state.playerHP > owed + 10 && (
              <RaiseControl
                min={bet.currentRaise + 5}
                max={Math.min(state.playerHP - 1, state.cpuHP)}
                onRaise={onRaise}
              />
            )}
          </div>
        </div>
      </div>
    );
  }

  // Awaiting CPU response
  if (bet.awaiting === 'cpu') {
    return (
      <div className="scrim">
        <div className="panel-parch" data-tutorial="bet-result" style={{padding:'18px 22px', width:300, textAlign:'center'}}>
          <div className="display" style={{fontSize:22, color:'var(--ink-stamp)'}}>Pot: {bet.pot} HP</div>
          <Ornament width={220} />
          <div className="mono" style={{color:'#3a2a18', fontSize:14}}>
            <span className="thinking-dots">{cpuDisplayName(state.cpuKey)} squints</span>
          </div>
          <style>{`
            .thinking-dots::after { content: ''; animation: dots 1.2s steps(4,end) infinite; }
            @keyframes dots { 0%{content:''} 25%{content:'.'} 50%{content:'..'} 75%{content:'...'} }
          `}</style>
        </div>
      </div>
    );
  }

  return null;
}

function RaiseControl({ min, max, onRaise }) {
  const [v, setV] = cUseState(Math.min(min + 10, max));
  if (max < min) return null;
  return (
    <div style={{display:'flex', gap:6, alignItems:'center', marginTop:4, padding:8, background:'rgba(120,80,30,0.15)', borderRadius:3}}>
      <input type="range" min={min} max={max} value={v} onChange={e=>setV(+e.target.value)} style={{flex:1, accentColor:'#8b1a1a'}} />
      <button className="btn btn-sm btn-primary" style={{padding:'6px 10px', fontSize:11}} onClick={() => onRaise(v)}>
        Raise → {v}
      </button>
    </div>
  );
}

function ResultBanner({ state, onContinue }) {
  if (state.foldedRound) {
    const f = state.foldedRound;
    const youWon = f.winner === 'player';
    return (
      <div className="scrim">
        <div className="panel-parch fade-up" style={{padding:'18px 22px', width:300, textAlign:'center'}}>
          <div className="display" style={{fontSize:26, color: youWon ? '#1a5a3a' : '#8b1a1a'}}>
            {youWon ? 'They Folded' : 'You Folded'}
          </div>
          <Ornament width={220} />
          <div style={{color:'#3a2a18', fontSize:14}}>
            {youWon ? `${cpuDisplayName(state.cpuKey)} backed off the table.` : 'You let the bet slide off your hand.'}
          </div>
          <div className="mono" style={{marginTop:8, color:'#3a2a18'}}>
            −{f.forfeit} HP to {youWon ? cpuDisplayName(state.cpuKey) : 'you'}
          </div>
          <button className="btn btn-brass" style={{marginTop:14, width:'100%'}} onClick={onContinue}>Next Hand</button>
        </div>
      </div>
    );
  }
  if (!state.lastEval) return null;
  const { playerEval, cpuEval, winner } = state.lastEval;
  const youWon = winner === 'player';
  const tied = !winner;
  return (
    <div className="scrim" data-tutorial="damage-display">
      <div className="panel-parch fade-up" style={{padding:'18px 22px', width:320, textAlign:'center'}}>
        <div className="display" style={{fontSize:28, color: tied ? '#3a2a18' : youWon ? '#1a5a3a' : '#8b1a1a'}}>
          {tied ? 'A Split Pot' : youWon ? 'You Take It' : 'They Take It'}
        </div>
        <Ornament width={250} />
        <div style={{display:'flex', flexDirection:'column', gap:10, marginTop:6}}>
          <div>
            <div style={{display:'flex', justifyContent:'space-between', alignItems:'baseline'}}>
              <span style={{fontSize:11, color:'#5a4a30', textTransform:'uppercase', letterSpacing:'.1em'}}>{youWon ? 'You · winner' : tied ? 'You' : 'You'}</span>
              <span className="head" style={{fontSize:14, color:'var(--ink-stamp)'}}>{Poker.HAND_NAME[playerEval.rank]}</span>
            </div>
            <div style={{display:'flex', gap:3, marginTop:4, justifyContent:'center'}}>
              {playerEval.cards.map((c,i) => <PCard key={i} card={c} size="sm" />)}
            </div>
          </div>
          <div>
            <div style={{display:'flex', justifyContent:'space-between', alignItems:'baseline'}}>
              <span style={{fontSize:11, color:'#5a4a30', textTransform:'uppercase', letterSpacing:'.1em'}}>{winner === 'cpu' ? 'Them · winner' : 'Them'}</span>
              <span className="head" style={{fontSize:14, color:'var(--ink-stamp)'}}>{Poker.HAND_NAME[cpuEval.rank]}</span>
            </div>
            <div style={{display:'flex', gap:3, marginTop:4, justifyContent:'center'}}>
              {cpuEval.cards.map((c,i) => <PCard key={i} card={c} size="sm" />)}
            </div>
          </div>
        </div>
        {state.lastEval && state.lastEval.totalDmg !== undefined && (
          <div style={{marginTop:14, padding:10, background:'rgba(120,80,30,0.12)', borderRadius:3, border:'1px dashed #8b6914'}}>
            <div className="mono" style={{fontSize:13, color:'#3a2a18'}}>
              {state.lastEval.totalDmg} dmg
              {state.lastEval.potBonus ? ` (incl. ${state.lastEval.potBonus} from pot)` : ''}
            </div>
          </div>
        )}
        <button className="btn btn-brass" style={{marginTop:14, width:'100%'}} onClick={onContinue}>
          {state.phase === 'gameOver' ? 'See Outcome' : 'Next Hand'}
        </button>
      </div>
    </div>
  );
}

// ============================================================================
// Sub-components for Standoff layout
// ============================================================================

function SlimHPRow({ hp, maxHp, shield, side, hit }) {
  const pct = Math.max(0, Math.min(100, (hp / maxHp) * 100));
  const sPct = Math.max(0, Math.min(100 - pct, (shield / maxHp) * 100));
  return (
    <div className={`s-hp-row ${hit ? 'hp-hit' : ''}`} style={side === 'cpu' ? {flexDirection:'row-reverse'} : {}}>
      <div className={`s-hp-num ${side === 'cpu' ? '' : 'right'}`}>
        {Math.max(0, Math.ceil(hp))}
      </div>
      <div className="s-hp-track">
        <div className="s-hp-fill" style={{width: pct + '%'}} />
        {sPct > 0 && <div className="s-hp-shield" style={{left: pct + '%', width: sPct + '%'}} />}
      </div>
    </div>
  );
}

function MetaRow({ energy, poison, shield, side, canChallenge, onChallenge, isThinking, actions, tutHighlightFn }) {
  const th = tutHighlightFn || (() => ({}));
  return (
    <div className="s-meta" style={{flexDirection: side === 'cpu' ? 'row-reverse' : 'row'}}>
      <div data-tutorial="energy-bar" style={{display:'flex', alignItems:'center', gap:6, ...th('energy-bar')}}>
        <EnergyBar energy={energy} pips={6} />
        <span style={{fontSize:9}}>{Math.round(energy)}/100</span>
        <ActionCounter count={actions} side={side} />
      </div>
      <div style={{display:'flex', alignItems:'center', gap:6}}>
        {side === 'player' && canChallenge && (
          <button className="s-challenge" data-tutorial="challenge-btn" style={th('challenge-btn')} onClick={onChallenge}>⚐ Call'em out</button>
        )}
        {side === 'player' && !canChallenge && (
          <span style={{opacity:.6, fontSize:9, fontStyle:'italic'}}>need full candles</span>
        )}
        {side === 'cpu' && isThinking && (
          <span style={{color:'var(--candle)', fontSize:9, fontStyle:'italic'}}>⚠ ready to raise</span>
        )}
      </div>
    </div>
  );
}

function HandPlate({ holes, community, faceDown }) {
  if (faceDown || holes.length < 2 || holes.length + community.length < 5) {
    return (
      <div className="s-hand-plate" style={{opacity: 0.55}}>
        <div>
          <div className="lbl">YOUR BEST</div>
          <div className="hand" style={{color:'var(--parch-faded)'}}>—</div>
        </div>
        <div>
          <div className="lbl" style={{textAlign:'right'}}>DAMAGE</div>
          <div className="dmg" style={{color:'var(--parch-faded)'}}>—</div>
        </div>
      </div>
    );
  }
  const ev = Poker.evaluateBest([...holes, ...community]);
  return (
    <div className="s-hand-plate">
      <div>
        <div className="lbl">YOUR BEST</div>
        <div className="hand">{Poker.HAND_NAME[ev.rank]}</div>
      </div>
      <div>
        <div className="lbl" style={{textAlign:'right'}}>DAMAGE</div>
        <div className="dmg">{Poker.HAND_DAMAGE[ev.rank]}</div>
      </div>
    </div>
  );
}

function CommunityRow({ community, bestSet, onTap }) {
  return (
    <div className="s-community">
      {Array.from({length: 5}).map((_, i) => {
        const c = community[i];
        if (!c) return <div key={i} className="slot"></div>;
        return (
          <PCard
            key={i}
            card={c}
            inBest={bestSet.has(c)}
            style={{width:42, height:60, animationDelay:`${i*60}ms`}}
            className="deal-in"
            onClick={() => onTap(c)}
          />
        );
      })}
    </div>
  );
}

// ============================================================================
// Main combat
// ============================================================================
const STREET_WINDOW_SECONDS = 10;

function ActionRow({ secondsLeft, playerAttackUsed, playerBlockUsed, playerCommit, onAttack, onBlock, gapPositive, tutHighlightFn }) {
  const th = tutHighlightFn || (() => ({}));
  const attackDisabled = playerAttackUsed || playerCommit.attack || !gapPositive;
  const blockDisabled  = playerBlockUsed  || playerCommit.block;
  return (
    <div
      data-tutorial="action-area"
      style={{
        display: 'flex', alignItems: 'center', justifyContent: 'space-between',
        gap: 10, padding: '8px 12px',
        background: 'rgba(30,15,8,0.85)', border: '1px solid rgba(255,138,31,0.18)',
        borderRadius: 4,
        ...th('action-area'),
      }}
    >
      <button
        className="btn btn-primary"
        disabled={attackDisabled}
        onClick={onAttack}
        data-tutorial="attack-btn"
        style={{ flex: 1, padding: '12px 0', opacity: attackDisabled ? 0.4 : 1, ...th('attack-btn') }}
      >
        ATTACK {playerCommit.attack && <span style={{fontSize:10}}>· locked</span>}
      </button>
      <div data-tutorial="timer-ring" style={th('timer-ring')}>
        <TimerRing secondsLeft={secondsLeft} totalSeconds={STREET_WINDOW_SECONDS} />
      </div>
      <button
        className="btn btn-brass"
        disabled={blockDisabled}
        onClick={onBlock}
        data-tutorial="block-btn"
        style={{ flex: 1, padding: '12px 0', opacity: blockDisabled ? 0.4 : 1, ...th('block-btn') }}
      >
        BLOCK {playerCommit.block && <span style={{fontSize:10}}>· locked</span>}
      </button>
    </div>
  );
}

function CombatScreen({
  persona = 'gambler', playerProfile, onGameOver, onExit,
  scriptedDeck, scriptedPlayerEnergy, scriptedCpuEnergy,
  cpuScript, // used in timer interval and respondCpu (wired in next task)
  tutorialStep, onTutorialAdvance,
}) {
  const [state, setState] = cUseState(() => {
    const s = makeInitialCombatState(
      persona,
      playerProfile?.name || 'You',
      playerProfile?.title || 'cold hand',
      playerProfile?.key || 'drifter',
    );
    if (scriptedPlayerEnergy != null) s.playerEnergy = scriptedPlayerEnergy;
    if (scriptedCpuEnergy   != null) s.cpuEnergy    = scriptedCpuEnergy;
    return s;
  });
  const [showBetModal, setShowBetModal] = cUseState(false);
  const [hitFlash, setHitFlash] = cUseState({ player: false, cpu: false });
  const [floats, setFloats] = cUseState([]);
  const [showBeam, setShowBeam] = cUseState(false);
  const floatId = cUseRef(0);
  const tutorialStepRef = cUseRef(tutorialStep);
  cUseEffect(() => { tutorialStepRef.current = tutorialStep; }, [tutorialStep]);

  const tutHighlight = (key) => {
    if (!tutorialStep || tutorialStep.highlight !== key) return {};
    return {
      position: 'relative',
      zIndex: 51,
      boxShadow: '0 0 0 9999px rgba(15,8,4,0.82)',
    };
  };

  cUseEffect(() => {
    if (state.phase === 'gameOver' && onGameOver) {
      const t = setTimeout(() => onGameOver(state.gameWinner, state), 1800);
      return () => clearTimeout(t);
    }
  }, [state.phase]);

  cUseEffect(() => {
    if (!state.events || state.events.length === 0) return;
    const newFloats = [];
    const flashes = { player: false, cpu: false };
    for (const ev of state.events) {
      if (ev.kind === 'damage') flashes[ev.side] = true;
      const id = ++floatId.current;
      newFloats.push({ id, value: ev.value, kind: ev.kind, side: ev.side });
    }
    setHitFlash(flashes);
    setFloats(f => [...f, ...newFloats]);
    const t = setTimeout(() => setHitFlash({ player: false, cpu: false }), 500);
    return () => clearTimeout(t);
  }, [state.events]);

  const removeFloat = id => setFloats(f => f.filter(x => x.id !== id));

  // ---------- Actions ----------

  const onContinueRound = () => {
    setState(s => ({
      ...s,
      phase: 'preDeal',
      lastEval: null, foldedRound: null, events: [],
    }));
    setTimeout(() => setState(s => s.phase === 'preDeal' ? COMBAT.startRound(s) : s), 250);
  };

  const maybeCpuBet = () => {
    setTimeout(() => {
      setState(s => {
        if (s.bet) return s;
        if (s.cpuEnergy < COMBAT.CHALLENGE_COST) return s;
        if (s.hasBet) return s;
        if (Math.random() < 0.35) return s;
        const decision = CPU.cpuInitiateBet(s, CPU.CPU_PERSONAS[s.cpuKey]);
        if (decision.action === 'pass') return s;
        return COMBAT.initiateBet(s, 'cpu', Math.min(decision.amount, s.cpuHP - 1, s.playerHP - 1));
      });
    }, 800);
  };

  const onChallenge = () => {
    setShowBetModal(true);
    if (tutorialStep?.waitFor === 'challenge-click') onTutorialAdvance?.();
  };
  const onBetCommit = (amount) => {
    setShowBetModal(false);
    setState(s => COMBAT.initiateBet(s, 'player', Math.min(amount, s.playerHP - 1, s.cpuHP - 1)));
    setTimeout(() => respondCpu(), 1400);
    if (tutorialStep?.waitFor === 'bet-commit') onTutorialAdvance?.();
  };
  const onBetCancel = () => setShowBetModal(false);

  const onCall  = () => setState(s => COMBAT.callBet(s));
  const onFold  = () => setState(s => COMBAT.foldBet(s));
  const onRaise = (amount) => {
    setState(s => COMBAT.raiseBet(s, amount));
    setTimeout(() => respondCpu(), 1400);
  };

  const respondCpu = () => {
    setState(s => {
      if (!s.bet || s.bet.awaiting !== 'cpu') return s;
      const cpuPhaseScript = cpuScript?.find(sc => sc.phase === s.phase);
      if (cpuPhaseScript?.betResponse === 'call') return COMBAT.callBet(s);
      if (cpuPhaseScript?.betResponse === 'fold') return COMBAT.foldBet(s);
      const decision = CPU.cpuRespondBet(s, s.bet.currentRaise, CPU.CPU_PERSONAS[s.cpuKey]);
      if (decision.action === 'fold')  return COMBAT.foldBet(s);
      if (decision.action === 'call')  return COMBAT.callBet(s);
      if (decision.action === 'raise') return COMBAT.raiseBet(s, decision.amount);
      return s;
    });
  };

  cUseEffect(() => {
    if (state.bet && state.bet.awaiting === 'cpu' && !state.bet.resolved) {
      const t = setTimeout(respondCpu, 1500);
      return () => clearTimeout(t);
    }
  }, [state.bet?.awaiting, state.bet?.raises]);

  const [secondsLeft, setSecondsLeft] = cUseState(STREET_WINDOW_SECONDS);
  const cpuActionPickedAt = cUseRef(null);
  const streetResolvedRef = cUseRef(false);

  // Reset resolved flag at start of each new street.
  cUseEffect(() => {
    streetResolvedRef.current = false;
  }, [state.phase]);

  // Pick CPU commit once per street, at a random offset within the window.
  cUseEffect(() => {
    if (!['preFlop','flop','turn','river'].includes(state.phase)) return;
    if (state.bet && !state.bet.resolved) return; // pause only while bet is pending
    setSecondsLeft(STREET_WINDOW_SECONDS);
    cpuActionPickedAt.current = (Math.random() * 6) + 1; // pick between 1s and 7s
    const interval = setInterval(() => {
      const ts = tutorialStepRef.current;
      // Freeze timer when tutorial is waiting for a player action
      if (ts && ts.waitFor !== 'auto') return;
      setSecondsLeft(prev => {
        // Fast-forward during auto steps so the window closes in ~1s
        const decrement = (ts?.waitFor === 'auto') ? 1.0 : 0.1;
        const next = prev - decrement;
        if (cpuActionPickedAt.current != null && next <= cpuActionPickedAt.current) {
          const cpuPhaseScript = cpuScript?.find(sc => sc.phase === state.phase);
          const choice = cpuPhaseScript
            ? { attack: cpuPhaseScript.attack, block: cpuPhaseScript.block }
            : CPU.cpuDecideActions(state, CPU.CPU_PERSONAS[state.cpuKey] || CPU.CPU_PERSONAS.gambler);
          setState(s => ({
            ...s,
            cpuCommit: {
              attack: choice.attack && !s.cpuAttackUsed,
              block:  choice.block  && !s.cpuBlockUsed,
            },
          }));
          cpuActionPickedAt.current = null;
        }
        if (next <= 0) {
          clearInterval(interval);
          resolveStreet();
          return 0;
        }
        return next;
      });
    }, 100);
    return () => clearInterval(interval);
  }, [state.phase, state.bet]);

  const resolveStreet = () => {
    streetResolvedRef.current = true;
    setState(s => {
      const resolved = COMBAT.resolveActions(s);
      // Advance to next street after resolve
      if (resolved.phase === 'gameOver') return resolved;
      if (s.phase === 'preFlop') return { ...COMBAT.dealCommunity(resolved, 3), phase: 'flop' };
      if (s.phase === 'flop')    return { ...COMBAT.dealCommunity(resolved, 1), phase: 'turn' };
      if (s.phase === 'turn')    return { ...COMBAT.dealCommunity(resolved, 1), phase: 'river' };
      if (s.phase === 'river') {
        // River → showdown after small reveal delay
        setShowBeam(true);
        setTimeout(() => setShowBeam(false), 800);
        setTimeout(() => setState(s2 => COMBAT.resolveShowdown(s2)), 700);
        return { ...resolved, phase: 'showdown' };
      }
      return resolved;
    });
  };

  const onPlayerAttack = () => {
    if (streetResolvedRef.current) return;
    setState(s => {
      if (s.playerAttackUsed || s.playerCommit.attack) return s;
      if (s.playerStrength <= s.cpuStrength) return s;
      return { ...s, playerCommit: { ...s.playerCommit, attack: true } };
    });
    if (tutorialStep?.waitFor === 'attack-click') onTutorialAdvance?.();
  };

  const onPlayerBlock = () => {
    if (streetResolvedRef.current) return;
    setState(s => {
      if (s.playerBlockUsed || s.playerCommit.block) return s;
      return { ...s, playerCommit: { ...s.playerCommit, block: true } };
    });
    if (tutorialStep?.waitFor === 'block-click') onTutorialAdvance?.();
  };

  // ---------- Derived ----------
  const inActiveHand = ['preFlop','flop','turn','river'].includes(state.phase);
  const canChallenge = state.playerEnergy >= COMBAT.CHALLENGE_COST && inActiveHand && !state.hasBet && !state.bet;
  const cpuChallengingPlayer = state.bet && state.bet.awaiting === 'player';
  const cpuAwaiting = state.bet && state.bet.awaiting === 'cpu';
  const showResult = state.phase === 'roundEnd' || (state.phase === 'gameOver' && state.lastEval) || state.foldedRound;
  const revealCpuCards = state.phase === 'showdown' || state.phase === 'roundEnd' || state.phase === 'gameOver';

  const bestSet = cUseMemo(() => {
    if (!state.lastEval) return new Set();
    return new Set(state.lastEval.winnerEval?.cards || []);
  }, [state.lastEval]);

  const cpuName  = cpuDisplayName(state.cpuKey);
  const cpuFlag  = cpuFlagWord(state.cpuKey);
  const cpuSub   = cpuSubtitle(state.cpuKey);
  const plFlag   = playerFlagWord(state.playerKey);

  return (
    <div className="standoff" style={tutorialStep ? { paddingBottom: 110 } : undefined}>
      <button className="s-exit" onClick={onExit}>✕ leave table</button>
      <div className="s-round" style={{whiteSpace:'nowrap'}}>ROUND {romanRound(Math.max(1, state.round))}</div>

      {/* ===================== OPPONENT ZONE ===================== */}
      <div className="s-zone opp" style={{marginTop: 10}}>
        <div className="s-portrait-row">
          <div className="s-side hp">
            <SlimHPRow hp={state.cpuHP} maxHp={COMBAT.MAX_HP} shield={0} side="cpu" hit={hitFlash.cpu} />
            <MetaRow
              energy={state.cpuEnergy}
              poison={0}
              shield={0}
              side="cpu"
              isThinking={state.cpuEnergy >= COMBAT.CHALLENGE_COST && !state.hasBet}
              actions={(state.cpuAttackUsed ? 0 : 1) + (state.cpuBlockUsed ? 0 : 1)}
            />
          </div>
          <div className="s-side portrait">
            <div className={`s-silhouette ${hitFlash.cpu ? 'hit' : ''}`}>
              <Silhouette kind={state.cpuKey} size={64} color="#3a1818" />
            </div>
            <div className="s-flag">— {cpuFlag} —</div>
            <div className="s-name small">{cpuName}</div>
            <div className="s-sub">{cpuSub}</div>
          </div>
        </div>
        <div className="s-card-row" style={{marginTop:6}}>
          {state.cpuHole.length === 0 ? (
            <>
              <div className="pcard sm" style={{visibility:'hidden'}}></div>
              <div className="pcard sm" style={{visibility:'hidden'}}></div>
            </>
          ) : (
            state.cpuHole.map((c, i) => (
              <PCard
                key={i}
                card={c}
                size="sm"
                faceDown={!revealCpuCards}
                inBest={bestSet.has(c)}
                className="deal-in"
                style={{
                  animationDelay: `${i * 80}ms`,
                  transform: revealCpuCards ? 'none' : `rotate(${i === 0 ? -6 : 6}deg)`,
                }}
              />
            ))
          )}
          {floats.filter(f => f.side === 'cpu').map(f => (
            <FloatNum key={f.id} value={f.value} kind={f.kind} x={50} y={50} onDone={() => removeFloat(f.id)} />
          ))}
        </div>
      </div>

      {/* ===================== CENTER STAGE ===================== */}
      <div className="s-stage" data-tutorial="center-stage" style={tutHighlight('center-stage')}>
        {showBeam && <div className="s-beam"></div>}

        <div>
          <div className={`s-phase-title ${phaseTitleSize(state.phase, state.bet)}`}>
            {phaseTitleText(state.phase, state.bet)}
          </div>
          <div className="s-phase-sub">— {romanRound(state.round)} —</div>
        </div>

        {state.bet ? (
          <div className="s-pot">
            <div className="pot-num">{state.bet.pot} HP</div>
            <div className="pot-lbl">{state.bet.initiator === 'player' ? 'YOU CALLED' : `${cpuName.toUpperCase()} CALLED`}</div>
          </div>
        ) : (
          <HandPlate holes={state.playerHole} community={state.community} />
        )}

        <CommunityRow community={state.community} bestSet={bestSet} onTap={() => {}} />
      </div>

      {/* ===================== PLAYER ZONE ===================== */}
      <div className="s-zone player">
        <div className="s-card-row" data-tutorial="hole-cards" style={{minHeight: 76, ...tutHighlight('hole-cards')}}>
          {state.playerHole.length === 0 ? (
            <>
              <div className="pcard" style={{visibility:'hidden'}}></div>
              <div className="pcard" style={{visibility:'hidden'}}></div>
            </>
          ) : (
            state.playerHole.map((c, i) => (
              <PCard
                key={i}
                card={c}
                size=""
                inBest={bestSet.has(c)}
                className="deal-in"
                style={{
                  animationDelay: `${i * 80}ms`,
                  transform: `translateY(${i === 0 ? -2 : 2}px) rotate(${i === 0 ? -4 : 4}deg)`,
                }}
              />
            ))
          )}
          {floats.filter(f => f.side === 'player').map(f => (
            <FloatNum key={f.id} value={f.value} kind={f.kind} x={50} y={50} onDone={() => removeFloat(f.id)} />
          ))}
        </div>

        <div className="s-portrait-row mirror" style={{marginTop:6}}>
          <div className="s-side portrait">
            <div className={`s-silhouette player ${hitFlash.player ? 'hit' : ''}`}>
              <Silhouette kind={state.playerKey} size={56} color="#4a2e1e" />
            </div>
            <div className="s-flag brass">— {plFlag} —</div>
            <div className="s-name small">{state.playerName}</div>
            <div className="s-sub">{state.playerTitle}</div>
          </div>
          <div className="s-side hp" data-tutorial="hp-bars" style={tutHighlight('hp-bars')}>
            <SlimHPRow hp={state.playerHP} maxHp={COMBAT.MAX_HP} shield={0} side="player" hit={hitFlash.player} />
            <MetaRow
              energy={state.playerEnergy}
              poison={0}
              shield={0}
              side="player"
              canChallenge={canChallenge}
              onChallenge={onChallenge}
              actions={(state.playerAttackUsed ? 0 : 1) + (state.playerBlockUsed ? 0 : 1)}
              tutHighlightFn={tutHighlight}
            />
          </div>
        </div>
      </div>

      {/* ===================== ACTION ===================== */}
      {state.phase === 'preDeal' && !state.bet && !showResult && (
        <button
          className="s-draw brass"
          data-tutorial="deal-btn"
          onClick={() => {
            setState(s => {
              if (scriptedDeck) {
                const deck = [...scriptedDeck];
                const playerHole = [deck.pop(), deck.pop()];
                const cpuHole    = [deck.pop(), deck.pop()];
                const playerStrength = COMBAT.streetStrength(playerHole, [], 'preFlop');
                const cpuStrength    = COMBAT.streetStrength(cpuHole,    [], 'preFlop');
                return {
                  ...s, deck, playerHole, cpuHole,
                  community: [], phase: 'preFlop',
                  bet: null, hasBet: false, lastEval: null, lastWinner: null, events: [],
                  round: s.round + 1, playerStrength, cpuStrength,
                  playerAttackUsed: false, playerBlockUsed: false,
                  cpuAttackUsed: false, cpuBlockUsed: false,
                  playerCommit: { attack: false, block: false },
                  cpuCommit:    { attack: false, block: false },
                };
              }
              return COMBAT.startRound(s);
            });
            if (tutorialStep?.waitFor === 'deal-click') onTutorialAdvance?.();
          }}
          style={{marginTop:8, ...tutHighlight('deal-btn')}}
        >
          DEAL <span className="sub">turn the next hand</span>
        </button>
      )}
      {inActiveHand && (!state.bet || state.bet.resolved) && !showResult && (
        <ActionRow
          secondsLeft={secondsLeft}
          playerAttackUsed={state.playerAttackUsed}
          playerBlockUsed={state.playerBlockUsed}
          playerCommit={state.playerCommit}
          onAttack={onPlayerAttack}
          onBlock={onPlayerBlock}
          gapPositive={state.playerStrength > state.cpuStrength}
          tutHighlightFn={tutHighlight}
        />
      )}

      {/* Modals */}
      {showBetModal && <BetModal state={state} onCommit={onBetCommit} onCancel={onBetCancel} />}
      {cpuChallengingPlayer && (
        <BetModal state={state} onCall={onCall} onFold={onFold} onRaise={onRaise} />
      )}
      {cpuAwaiting && state.bet.initiator === 'player' && (
        <BetModal state={state} />
      )}
      {showResult && state.phase !== 'gameOver' && (
        <ResultBanner state={state} onContinue={onContinueRound} />
      )}
    </div>
  );
}

function romanRound(n) {
  const map = ['Ø','I','II','III','IV','V','VI','VII','VIII','IX','X','XI','XII','XIII','XIV','XV','XVI','XVII','XVIII','XIX','XX'];
  return map[Math.min(n, 20)] || String(n);
}

Object.assign(window, { CombatScreen, makeInitialCombatState, cpuSubtitle, cpuDisplayName });
