// game-poker.jsx — Video Poker (Jacks or Better) · ~99% RTP with optimal play
// Standard 5-card draw. Player is dealt five cards from a freshly shuffled
// 52-card deck (no replacement), picks 0-5 to HOLD, then the remaining
// positions are replaced from the same deck's reserve. Final hand is
// evaluated against the 10/6 Jacks-or-Better paytable (royal=200, SF=30,
// FH=10 — operator squeeze).
//
// §5.70 — server-resolved. Two endpoints (multi-action template like
// mines/hilo): POST /play/poker/deal derives 10 cards server-side and
// returns the 5 dealt cards only (reserve held in server memory). POST
// /play/poker/draw takes the 5 holds and swaps in reserves for the rest,
// evaluates against the paytable, writes /plays. The browser can no longer
// peek at the reserve and play optimal swaps. Local `deriveCards`,
// `evaluatePoker`, `vpRankValue` removed; only `VP_PAYOUT` / `VP_LABEL`
// stay (used by the paytable + result row + history pills).

const { useState: useStateVP, useEffect: useEffectVP, useRef: useRefVP } = React;

// VP_SUITS / VP_RANKS / vpRankValue were used by the local deriveCards +
// evaluatePoker pre-§5.70 (server owns those now). Kept commented as a
// reference for what the card glyphs map to.
//   const VP_SUITS = ['♠','♥','♦','♣'];
//   const VP_RANKS = ['A','2','3','4','5','6','7','8','9','10','J','Q','K'];

// Custom 10/6 Jacks-or-Better paytable. Multiplier is gross return on stake
// (payout = stake * multiplier). Multiplier 0 means loss.
// Royal flush is capped at 200× (vs the canonical 800×) and the full-house
// multiplier is bumped 9→10 to recover part of the RTP the royal cap removes.
// Straight flush is held at 30× (below the canonical 50×) to bring theoretical
// optimal RTP to ~98.99% — sustainable for a small operator. Edge ~1.01%.
const VP_PAYOUT = {
  'royal-flush':     200,
  'straight-flush':   30,
  'four-kind':        25,
  'full-house':       10,
  'flush':             6,
  'straight':          4,
  'three-kind':        3,
  'two-pair':          2,
  'jacks-or-better':   1,
  'nothing':           0,
};
const VP_LABEL = {
  'royal-flush':     'ROYAL FLUSH',
  'straight-flush':  'STRAIGHT FLUSH',
  'four-kind':       'FOUR OF A KIND',
  'full-house':      'FULL HOUSE',
  'flush':           'FLUSH',
  'straight':        'STRAIGHT',
  'three-kind':      'THREE OF A KIND',
  'two-pair':        'TWO PAIR',
  'jacks-or-better': 'JACKS OR BETTER',
  'nothing':         '— NO PAY',
};

function VideoPokerPage({ palette: M, wallet, fairness, onConnect }) {
  // Phases: idle = no hand; dealt = 5 cards shown, holds toggleable;
  // drawing = brief delay while replacements appear; done = final hand shown.
  const [phase, setPhase] = useStateVP('idle');
  const [hand, setHand] = useStateVP([]);          // 5 visible cards
  const [held, setHeld] = useStateVP([false,false,false,false,false]);
  const [last, setLast] = useStateVP(null);
  const [error, setError] = useBetError();
  const { bet, setBet, betStr, setBetStr } = useBetInput(0.1);
  const busyRef = useRefVP(false);
  // §5.70: server holds the reserve + final-hand evaluation. Browser tracks
  // roundId for the deal→draw handoff and roundHex for the result-row display.
  const roundIdRef  = useRefVP(null);
  const roundHexRef = useRefVP(null);

  const MIN_BET = coinMinBet(wallet.activeCoin);
  // Tighter per-coin cap than coin flip because the royal flush pays 800x.
  // 25 SOL max bet = 20,000 SOL max win at the top of the table.
  const MAX_BET_SOL = 25;
  const maxBet = maxBetForCoin(MAX_BET_SOL, wallet.activeCoin);

  useEffectVP(() => {
    if (bet < MIN_BET) setBet(MIN_BET);
    else if (bet > maxBet) setBet(maxBet);
  }, [wallet.activeCoin]); // eslint-disable-line

  async function deal() {
    if (busyRef.current) return;
    if (phase !== 'idle' && phase !== 'done') return;
    if (!wallet.canPlay) { onConnect(); return; }
    const bag = wallet.balance[wallet.activeCoin] || 0;
    if (bet < MIN_BET) { setError(`minimum bet is ${MIN_BET} ${wallet.activeCoin}`); return; }
    if (bet > maxBet)  { setError(`max bet on poker is ${maxBet.toFixed(2)} ${wallet.activeCoin}`); return; }
    if (bet > bag) {
      setError(`bet ${bet.toFixed(3)} ${wallet.activeCoin} exceeds bag (${bag.toFixed(3)} ${wallet.activeCoin} available)`);
      return;
    }
    busyRef.current = true;
    setError(null);
    setLast(null);
    if (!wallet.debit(wallet.activeCoin, bet)) {
      busyRef.current = false;
      setError(`bag changed under the bet -- try again`);
      return;
    }

    let server;
    try {
      server = await callServerGame('/play/poker/deal', {
        bet,
        clientSeed: fairness.clientSeed,
        coin: wallet.activeCoin,
        user: wallet.username,
      });
    } catch (err) {
      wallet.credit(wallet.activeCoin, bet, 'poker-refund-' + Date.now());
      busyRef.current = false;
      setError('server error: ' + (err && err.message ? err.message : String(err)));
      return;
    }

    const dealt = Array.isArray(server.dealt) ? server.dealt : [];
    setHand(dealt);
    setHeld([false,false,false,false,false]);
    roundIdRef.current = server.roundId;
    roundHexRef.current = String(server.serverHash || '');
    setPhase('dealt');
    busyRef.current = false;
    playHitSound();
  }

  function toggleHold(i) {
    if (phase !== 'dealt') return;
    setHeld(h => h.map((v, j) => j === i ? !v : v));
  }

  async function draw() {
    if (busyRef.current) return;
    if (phase !== 'dealt') return;
    if (!roundIdRef.current) return;
    busyRef.current = true;
    setPhase('drawing');

    let server;
    try {
      server = await callServerGame('/play/poker/draw', {
        roundId: roundIdRef.current,
        holds: held,
      });
    } catch (err) {
      const msg = err && err.message ? err.message : String(err);
      busyRef.current = false;
      if (msg.includes('round not found')) {
        wallet.credit(wallet.activeCoin, bet, 'poker-refund-' + (roundIdRef.current || Date.now()));
        roundIdRef.current = null;
        setPhase('idle');
        setError('round lost (server reset); bet refunded');
      } else {
        setPhase('dealt');
        setError('server error: ' + msg);
      }
      return;
    }

    const finalHand = Array.isArray(server.finalHand) ? server.finalHand : [];
    const type   = String(server.type || 'nothing');
    const mult   = Number(server.mult) || 0;
    const payout = Number(server.payout) || 0;
    const hex    = String(server.hex || roundHexRef.current || '');

    // Brief reveal delay so the swap-in animation feels deliberate.
    setHand(finalHand);
    setTimeout(() => {
      if (payout > 0) wallet.credit(wallet.activeCoin, payout, 'poker-cash-' + roundIdRef.current);
      setLast({ hand: finalHand, type, mult, payout, bet, hex: hex.slice(0, 12) });
      wallet.logPlay({
        game:'VideoPoker', bet, payout, win: payout > bet,
        coin: wallet.activeCoin, hex: hex.slice(0, 16),
        mult, outcome: type,
      });
      // NOTE: server wrote /plays already (rules step10 blocks browser writes for VideoPoker).
      roundIdRef.current = null;
      setPhase('done');
      busyRef.current = false;
      if (type === 'royal-flush')                 playJackpotSound();
      else if (type === 'straight-flush' || mult >= 9) playStreakSound();
      else                                        playOutcomeSound(payout > 0);
    }, 400);
  }

  const canDeal    = phase === 'idle' || phase === 'done';
  const canDraw    = phase === 'dealt';
  const heldCount  = held.filter(Boolean).length;

  return (
    <div className="rc-page" style={{flex:1, padding:'18px 26px 40px', overflowY:'auto'}}>
      <h1 className="rc-h-page" style={{fontSize:48, color:M.hot, margin:'4px 0', textShadow:`3px 3px 0 ${M.cyan}`}}>VIDEO POKER</h1>
      <div className="rc-h-sub" style={{color:M.ink2, fontSize:18, marginBottom:14}}>
        <span style={{color:M.green}}>99.10% RTP</span> · jacks or better · 10/6 paytable · 5-card draw · max bet {maxBet.toFixed(2)} {wallet.activeCoin}
      </div>

      <div className="rc-poker-playmode">
        <PlayModeBar palette={M} wallet={wallet} onConnect={onConnect}/>
      </div>
      <BetErrorToast error={error} onClose={()=>setError(null)} palette={M}/>

      <div className="rc-game-grid" style={{display:'grid', gridTemplateColumns:'320px 1fr', gap:18, alignItems:'start'}}>
        <Frame title="bet · paytable" accent={M.yellow} className="rc-poker-bet">
         <div className="rc-bet-panel">
          <div className="rc-poker-paytable">
            <PokerPaytable M={M} bet={bet} coin={wallet.activeCoin} winType={last?.type}/>
          </div>
          <div className="rc-bet-inputs" style={{opacity: canDeal ? 1 : 0.4, pointerEvents: canDeal ? 'auto' : 'none', transition:'opacity 0.15s', marginTop:12}}>
            <div style={{fontSize:14, color:M.ink2, marginBottom:4}}>bet ({wallet.activeCoin})</div>
            <input type="number" step={0.01} max={maxBet} value={betStr} onChange={e=>setBetStr(e.target.value)} disabled={!canDeal} style={{
              width:'100%', background:M.bg, border:`2px solid ${M.green}`, color:M.green, padding:'8px 10px', fontFamily:'inherit', fontSize:22, outline:'none',
            }}/>
            <div style={{marginTop:6}}>
              <QuickBetButtons current={bet} onPick={v=>setBet(v)} palette={M} disabled={!canDeal}/>
            </div>
            <div style={{display:'flex', gap:4, marginTop:6}}>
              <button onClick={()=>setBet(b=>Math.max(MIN_BET, +(b/2).toFixed(6)))} disabled={!canDeal} style={vpBtnSec(M)}>½</button>
              <button onClick={()=>setBet(b=>+Math.min(b*2, maxBet).toFixed(6))} disabled={!canDeal} style={vpBtnSec(M)}>2×</button>
              <button onClick={()=>setBet(+(Math.min(wallet.balance[wallet.activeCoin] || 0, maxBet)).toFixed(6))} disabled={!canDeal} title={`max bet: ${maxBet.toFixed(2)} ${wallet.activeCoin}`} style={vpBtnSec(M)}>max</button>
            </div>
          </div>
          <button className="rc-bet-action" onClick={deal} disabled={!canDeal} style={{
            marginTop:14, width:'100%', background:M.hot, color:M.bg, border:`3px solid ${M.bg}`, fontFamily:'inherit', fontSize:22, padding:'10px 0', fontWeight:700, cursor:canDeal?'pointer':'wait', boxShadow:`5px 5px 0 ${M.green}`,
          }}>{phase === 'idle' ? '► DEAL' : phase === 'done' ? '► DEAL AGAIN' : 'dealing…'}</button>
         </div>
        </Frame>

        <div style={{display:'flex', flexDirection:'column', gap:14, minWidth:0}}>
          <Frame title="the hand" accent={M.green} className="rc-poker-hand" style={{minHeight:340, display:'flex', flexDirection:'column', alignItems:'center', justifyContent:'center', gap:14, padding:'24px 16px'}}>
            {/* Cards row — ALWAYS rendered with 5 slots. Idle/post-shuffle
                slots render a dim placeholder so the row height never
                collapses; this keeps the action slot below from sliding
                up and down between phases. */}
            <div style={{display:'flex', gap:10, justifyContent:'center', flexWrap:'wrap'}}>
              {[0,1,2,3,4].map(i => {
                const card = hand[i];
                const showCard = !!card && phase !== 'idle';
                return (
                  <div key={i} style={{display:'flex', flexDirection:'column', alignItems:'center', gap:6}}>
                    {showCard
                      ? <VPCard card={card} M={M} accent={held[i] ? M.yellow : M.cyan}/>
                      : <VPCardPlaceholder M={M}/>}
                    {/* Status slot — same height in every phase so the
                        cards above never shift. Button when dealable,
                        label after draw, blank otherwise. */}
                    <div style={{height:28, display:'flex', alignItems:'center', justifyContent:'center', minWidth:72}}>
                      {phase === 'dealt' && (
                        <button onClick={()=>toggleHold(i)} style={{
                          background: held[i] ? M.yellow : M.bg,
                          color:      held[i] ? M.bg     : M.yellow,
                          border:`2px solid ${M.yellow}`, fontFamily:'inherit', fontSize:14, padding:'4px 12px',
                          cursor:'pointer', fontWeight:700, letterSpacing:'0.05em',
                          boxShadow: held[i] ? `2px 2px 0 ${M.bg}` : 'none',
                        }}>{held[i] ? '✓ HOLD' : 'HOLD'}</button>
                      )}
                      {phase === 'done' && (
                        <span style={{fontSize:11, color:M.ink2, letterSpacing:'0.05em'}}>
                          {held[i] ? 'HELD' : 'DRAWN'}
                        </span>
                      )}
                    </div>
                  </div>
                );
              })}
            </div>

            {/* Action / result slot — always rendered with reserved height
                so the cards stay put. DRAW button while dealable, result
                block (hand label, payout, hash) when done, prompt during
                idle, blank during the brief drawing transition. */}
            <div className="rc-resolve-slot-row" style={{minHeight:96, display:'flex', alignItems:'flex-start', justifyContent:'center', width:'100%'}}>
              {phase === 'idle' && (
                <div style={{color:M.ink2, fontSize:14, textAlign:'center'}}>place a bet and click DEAL to draw five cards.</div>
              )}
              {phase === 'dealt' && (
                <button onClick={draw} style={{
                  background:M.hot, color:M.bg, border:`3px solid ${M.bg}`, fontFamily:'inherit', fontSize:20, padding:'8px 20px', fontWeight:700,
                  cursor:'pointer', boxShadow:`4px 4px 0 ${M.green}`, letterSpacing:'0.05em',
                }}>► DRAW ({5 - heldCount} new)</button>
              )}
              {phase === 'done' && last && (
                <div style={{textAlign:'center'}}>
                  <div style={{fontSize:28, fontWeight:700, color: last.mult > 0 ? M.green : M.red, textShadow:`2px 2px 0 ${M.bg}`, letterSpacing:'0.05em'}}>
                    {VP_LABEL[last.type]}
                  </div>
                  {last.mult > 0 ? (
                    <div style={{fontSize:18, color:M.green, marginTop:2, fontWeight:700}}>
                      {last.mult}× = +{last.payout.toFixed(wallet.activeCoin==='SOL'?3:2)} {wallet.activeCoin}
                    </div>
                  ) : (
                    <div style={{fontSize:18, color:M.red, marginTop:2, fontWeight:700}}>−{last.bet.toFixed(wallet.activeCoin==='SOL'?3:2)} {wallet.activeCoin}</div>
                  )}
                  <div style={{fontSize:12, color:M.ink2, marginTop:4, fontFamily:'JetBrains Mono, monospace'}}>hash: {last.hex}…</div>
                </div>
              )}
            </div>
          </Frame>

          <PokerHistory M={M} wallet={wallet}/>
        </div>
      </div>

      <VideoPokerStatsPanel palette={M} wallet={wallet}/>
      <HashPanel palette={M} fairness={fairness}/>
      <PokerHowToPlay M={M}/>
    </div>
  );
}

function PokerHowToPlay({ M }) {
  const step = (n, body) => (
    <div style={{display:'flex', gap:10, alignItems:'flex-start'}}>
      <div style={{
        flexShrink:0, width:22, height:22, borderRadius:'50%',
        background:M.hot, color:M.bg, display:'flex', alignItems:'center', justifyContent:'center',
        fontSize:13, fontWeight:700,
      }}>{n}</div>
      <div style={{color:M.ink, fontSize:14, lineHeight:1.5}}>{body}</div>
    </div>
  );
  return (
    <Frame title="how to play" accent={M.cyan} style={{marginTop:18}}>
      <div style={{color:M.ink2, fontSize:14, lineHeight:1.5, marginBottom:12}}>
        five-card draw poker against the paytable — no opponents. You win when your
        final hand is a pair of <span style={{color:M.yellow}}>jacks or better</span>;
        bigger hands pay more.
      </div>
      <div style={{display:'flex', flexDirection:'column', gap:8}}>
        {step(1, <>set your bet, then click <span style={{color:M.hot, fontWeight:700}}>► DEAL</span> to receive five cards from a shuffled 52-card deck.</>)}
        {step(2, <>click <span style={{color:M.yellow, fontWeight:700}}>HOLD</span> on any cards you want to keep. unheld cards will be replaced.</>)}
        {step(3, <>click <span style={{color:M.hot, fontWeight:700}}>► DRAW</span> — new cards replace the unheld positions. your final five-card hand is scored.</>)}
        {step(4, <>payout = bet × multiplier from the paytable on the left. nothing below jacks-or-better loses the bet.</>)}
      </div>
      <div style={{marginTop:12, paddingTop:10, borderTop:`1px dashed ${M.ink2}`, fontSize:13, color:M.ink2, lineHeight:1.6}}>
        <div style={{color:M.cyan, marginBottom:4, letterSpacing:'0.05em'}}>{'>'} TIPS</div>
        <div>· <span style={{color:M.ink}}>holding a pair</span> is usually better than chasing a flush or straight on a single high card.</div>
        <div>· four cards to a <span style={{color:M.yellow}}>royal flush</span> or <span style={{color:M.yellow}}>straight flush</span> are worth chasing — drop everything else.</div>
        <div>· low pairs (2s–10s) still pay if you upgrade to two pair, trips, or better — keep them.</div>
        <div>· the deck is committed at deal time via the fairness hash above; the cards you'd draw are fixed before you choose what to hold.</div>
      </div>
    </Frame>
  );
}

function vpBtnSec(M){ return { flex:1, background:M.bg, color:M.green, border:`1px solid ${M.green}`, fontFamily:'inherit', fontSize:14, padding:'4px 0', cursor:'pointer' }; }

function PokerPaytable({ M, bet, coin, winType }) {
  const dp = coin === 'SOL' ? 3 : 2;
  const rows = [
    ['royal-flush',     'ROYAL FLUSH'],
    ['straight-flush',  'STRAIGHT FLUSH'],
    ['four-kind',       'FOUR OF A KIND'],
    ['full-house',      'FULL HOUSE'],
    ['flush',           'FLUSH'],
    ['straight',        'STRAIGHT'],
    ['three-kind',      'THREE OF A KIND'],
    ['two-pair',        'TWO PAIR'],
    ['jacks-or-better', 'JACKS OR BETTER'],
  ];
  return (
    <div style={{fontFamily:'JetBrains Mono, monospace', fontSize:12, color:M.ink2, lineHeight:1.6}}>
      <div style={{color:M.cyan, marginBottom:4, letterSpacing:'0.05em'}}>{'>'} PAYTABLE</div>
      {rows.map(([k, label]) => {
        const m   = VP_PAYOUT[k];
        const win = (bet * m).toFixed(dp);
        const isTop = k === 'royal-flush' || k === 'straight-flush';
        const isHit = winType === k;
        const baseColor = isHit ? M.green : isTop ? M.yellow : M.ink;
        return (
          <div key={k} style={{
            display:'flex', justifyContent:'space-between',
            color: baseColor,
            background: isHit ? `${M.green}22` : 'transparent',
            fontWeight: isHit ? 700 : 400,
            padding: isHit ? '0 4px' : 0,
          }}>
            <span>{label}</span>
            <span><span style={{color:isHit?M.green:M.cyan}}>{m}×</span> <span style={{color:M.ink2}}>={win}</span></span>
          </div>
        );
      })}
    </div>
  );
}

function VPCard({ card, M, accent }) {
  if (!card) return null;
  const red = card.s === '♥' || card.s === '♦';
  return (
    <div style={{
      width:72, height:104, background:M.ink, color: red ? '#cc0000' : '#000',
      border:`3px solid ${accent}`, padding:8, position:'relative', fontFamily:'serif',
      transition:'border-color 0.15s',
    }}>
      <div style={{position:'absolute', top:4, left:6, fontSize:16, fontWeight:700, lineHeight:1}}>{card.r}</div>
      <div style={{position:'absolute', top:22, left:6, fontSize:14, lineHeight:1}}>{card.s}</div>
      <div style={{position:'absolute', bottom:4, right:6, fontSize:16, fontWeight:700, transform:'rotate(180deg)', lineHeight:1}}>{card.r}</div>
      <div style={{position:'absolute', bottom:22, right:6, fontSize:14, lineHeight:1, transform:'rotate(180deg)'}}>{card.s}</div>
      <div style={{position:'absolute', top:'50%', left:'50%', transform:'translate(-50%,-50%)', fontSize:30}}>{card.s}</div>
    </div>
  );
}

// Dim placeholder with the same footprint as VPCard. Used to hold the
// cards row at fixed height during idle / between deals so the action
// slot below never shifts.
function VPCardPlaceholder({ M }) {
  return (
    <div style={{
      width:72, height:104, background:M.bg,
      border:`3px dashed ${M.ink2}55`,
      display:'flex', alignItems:'center', justifyContent:'center',
      color:`${M.ink2}55`, fontSize:30, fontFamily:'serif',
    }}>?</div>
  );
}

// Short outcome history — last 8 hands in the active coin. Each pill shows
// the hand-type abbreviation; color tracks win/loss. Read from the same
// wallet.history feed the stats panel filters.
const VP_ABBREV = {
  'royal-flush':     'ROYAL',
  'straight-flush':  'ST.FL',
  'four-kind':       '4-KIND',
  'full-house':      'FULL',
  'flush':           'FLUSH',
  'straight':        'STR',
  'three-kind':      '3-KIND',
  'two-pair':        '2-PAIR',
  'jacks-or-better': 'JOB',
  'nothing':         '—',
};
function PokerHistory({ M, wallet }) {
  const coin = wallet.activeCoin;
  const rounds = (wallet.history || [])
    .filter(h => h.game === 'VideoPoker' && h.coin === coin)
    .slice(0, 8);
  return (
    <Frame title="recent" accent={M.cyan} className="rc-history rc-poker-recent">
      <div style={{display:'flex', gap:6, flexWrap:'wrap', minHeight:30, alignItems:'center'}}>
        {rounds.length === 0 ? (
          <span style={{color:M.ink2, fontSize:13}}>no hands yet in {coin}.</span>
        ) : rounds.map((r, i) => {
          const isWin = (r.mult || 0) > 0;
          const isTop = r.outcome === 'royal-flush' || r.outcome === 'straight-flush';
          const bg     = isTop ? M.yellow : isWin ? M.green : M.panel;
          const color  = isTop ? M.bg     : isWin ? M.bg    : M.ink2;
          const border = isTop ? M.yellow : isWin ? M.green : `${M.ink2}55`;
          return (
            <div key={i} title={`${VP_LABEL[r.outcome] || r.outcome} · ${r.mult || 0}x`} style={{
              background: bg, color, border:`1px solid ${border}`,
              padding:'3px 8px', fontSize:11, fontWeight:700, letterSpacing:'0.05em',
              fontFamily:'JetBrains Mono, monospace', whiteSpace:'nowrap',
            }}>
              {VP_ABBREV[r.outcome] || '—'}
              {(r.mult || 0) > 0 && <span style={{marginLeft:4, opacity:0.8}}>{r.mult}×</span>}
            </div>
          );
        })}
      </div>
    </Frame>
  );
}

function VideoPokerStatsPanel({ palette: M, wallet }) {
  const coin = wallet.activeCoin;
  const rounds = (wallet.history || []).filter(h => h.game === 'VideoPoker' && h.coin === coin);
  const total = rounds.length;
  const wins  = rounds.filter(r => r.win).length;
  const winRate = total ? (wins / total) * 100 : 0;
  const wagered = rounds.reduce((s, r) => s + (r.bet || 0), 0);
  const netPL   = rounds.reduce((s, r) => s + ((r.payout || 0) - (r.bet || 0)), 0);
  const bestWin = rounds.reduce((m, r) => Math.max(m, (r.payout || 0) - (r.bet || 0)), 0);
  // Best hand type — highest paytable multiplier the player ever hit.
  const bestType = rounds.reduce((best, r) => {
    const m = VP_PAYOUT[r.outcome] || 0;
    return m > best.mult ? { type: r.outcome, mult: m } : best;
  }, { type: 'nothing', mult: 0 });
  const dp = coin === 'SOL' ? 3 : 2;
  const fmt    = n => `${n>=0?'+':''}${n.toFixed(dp)} ${coin}`;
  const fmtAbs = n => `${n.toFixed(dp)} ${coin}`;
  const tile = (label, value, color) => (
    <div style={{textAlign:'center', padding:'4px 0'}}>
      <div style={{fontSize:11, color:M.ink2, textTransform:'uppercase', letterSpacing:'0.05em'}}>{label}</div>
      <div style={{fontSize:20, color, fontWeight:700, lineHeight:1.1, marginTop:4, fontFamily:'JetBrains Mono, monospace'}}>{value}</div>
    </div>
  );
  // Display abbreviation for "best hand" tile so it fits the narrow column.
  const bestHandLabel = bestType.mult > 0 ? VP_LABEL[bestType.type].split(' ').slice(0, 2).join(' ') : '—';
  return (
    <Frame title="video poker stats" accent={M.cyan} className="rc-poker-stats" style={{marginTop:18}}>
      {total === 0 ? (
        <div style={{color:M.ink2, fontSize:14, padding:'4px 0'}}>{'>'} no hands yet in {coin}. deal a round.</div>
      ) : (
        <div className="rc-stat-6" style={{display:'grid', gridTemplateColumns:'repeat(6, 1fr)', gap:14}}>
          {tile('hands',         total,                              M.green)}
          {tile('win rate',      `${winRate.toFixed(1)}%`,           M.cyan)}
          {tile('total wagered', fmtAbs(wagered),                    M.yellow)}
          {tile('net P/L',       fmt(netPL),                         netPL >= 0 ? M.green : M.red)}
          {tile('best win',      `+${bestWin.toFixed(dp)} ${coin}`,  M.green)}
          {tile('best hand',     bestHandLabel,                      M.hot)}
        </div>
      )}
    </Frame>
  );
}

Object.assign(window, { VideoPokerPage });
