/* eslint-disable */
// Baccarat — main app: betting, the dealing timeline, settlement, stats and the three layouts.

const { useState, useEffect, useRef, useMemo } = React;

const BAC_TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
  "playerName": "",
  "dealerName": "Melissa",
  "showHints": true,
  "soundOn": false,
  "quickDeal": false
}/*EDITMODE-END*/;

const BAC_STATS_KEY = 'baccaratStats';
const BAC_MIN_BET = 5;
const BAC_MAX_SPOT = 500;
const BAC_EMPTY = { player: 0, banker: 0, tie: 0, playerPair: 0, bankerPair: 0, dragon7: 0 };
const bacSum = b => BAC_RULES.SPOTS.reduce((t, k) => t + (b[k] || 0), 0);
const bacMain = b => (b.player || 0) + (b.banker || 0) + (b.tie || 0);

function App() {
  const R = BAC_RULES;
  const [tweaks, setTweak] = useTweaks(BAC_TWEAK_DEFAULTS);
  const [bankroll, setBankroll] = useState(() => (window.CASINO_BANKROLL ? window.CASINO_BANKROLL.read() : 1000));
  const [stats, setStats] = useState(() => {
    try {
      const s = JSON.parse(localStorage.getItem(BAC_STATS_KEY) || 'null');
      if (s) return { played: s.played || 0, won: s.won || 0, peak: s.peak || 1000, biggestWin: s.biggestWin || 0 };
    } catch (e) {}
    return { played: 0, won: 0, peak: 1000, biggestWin: 0 };
  });
  const [streak, setStreak] = useState(0);
  const [showBrokeModal, setShowBrokeModal] = useState(false);
  const [showNameModal, setShowNameModal] = useState(false);
  const [showPays, setShowPays] = useState(false);

  // Layout: index.html publishes window.BAC_CANVAS ({ w, h, portrait, tight }) and fires "bac:canvas".
  const readCanvas = () => window.BAC_CANVAS || { w: 1380, h: 860, portrait: false, tight: false };
  const [canvas, setCanvas] = useState(readCanvas);
  useEffect(() => {
    const onCanvas = () => setCanvas({ ...readCanvas() });
    window.addEventListener('bac:canvas', onCanvas);
    onCanvas();
    return () => window.removeEventListener('bac:canvas', onCanvas);
  }, []);
  const portrait = !!canvas.portrait;
  const tight = !portrait && !!canvas.tight;

  useEffect(() => { if (window.CASINO_BANKROLL) window.CASINO_BANKROLL.write(bankroll); }, [bankroll]);
  // Bonus chips (casino/casino-bonus.js) land on the stored bankroll mid-session: add them here too.
  useEffect(() => {
    const onBonus = (e) => { const d = e.detail || {}; d.seen = true; const n = Number(d.amount) || 0; if (n > 0) setBankroll(br => br + n); };   // seen: see casino-bonus.js
    window.addEventListener('casino:bonus', onBonus);
    return () => window.removeEventListener('casino:bonus', onBonus);
  }, []);
  useEffect(() => { try { localStorage.setItem(BAC_STATS_KEY, JSON.stringify(stats)); } catch (e) {} }, [stats]);

  // Optional name, asked once across the casino.
  useEffect(() => {
    const stored = window.CASINO_PLAYER.read();
    if (stored) { if (stored !== tweaks.playerName) setTweak('playerName', stored); }
    else if (!window.CASINO_PLAYER.get('nameSkipped')) setShowNameModal('first');
  }, []);
  function savePlayerName(name) {
    const trimmed = window.CASINO_PLAYER.write(name);
    if (trimmed) setTweak('playerName', trimmed);
    else window.CASINO_PLAYER.set('nameSkipped', true);
    setShowNameModal(false);
  }

  // ── Round state ──
  // phase: 'betting' | 'dealing' | 'resolved'
  const [phase, setPhase] = useState('betting');
  const [shoe, setShoe] = useState(() => R.newShoe());
  const [history, setHistory] = useState([]);
  const [chip, setChip] = useState(25);
  const [bets, setBets] = useState(BAC_EMPTY);
  const [lastBets, setLastBets] = useState(null);
  const [coup, setCoup] = useState(null);
  const [dealt, setDealt] = useState({ player: 0, banker: 0 });
  const [shown, setShown] = useState({ player: 0, banker: 0 });
  const [settled, setSettled] = useState(null);
  // Timers and "Rebet & Deal" run from closures of earlier renders; these refs always hold the live values.
  const shoeRef = useRef(shoe); shoeRef.current = shoe;
  const bankrollRef = useRef(bankroll); bankrollRef.current = bankroll;
  const timers = useRef([]);
  useEffect(() => () => timers.current.forEach(clearTimeout), []);
  const later = (fn, ms) => { timers.current.push(setTimeout(fn, ms)); };

  // ── Dealer ──
  const [expression, setExpression] = useState('idle');
  const [message, setMessage] = useState('');
  const [isIdle, setIsIdle] = useState(false);
  const [mood, setMood] = useState(0);
  const idleTimerRef = useRef(null);
  const ctx = { player: tweaks.playerName, dealer: tweaks.dealerName };
  function say(key, expr) {
    setMessage(bacPickLine(key, ctx));
    if (expr) {
      setExpression(expr);
      const d = expr === 'happy' ? 0.18 : expr === 'shocked' ? 0.10 : expr === 'sad' ? -0.18 : 0;
      if (d) setMood(m => Math.max(-1, Math.min(1, m + d)));
    }
  }
  useEffect(() => {
    const id = setInterval(() => setMood(m => Math.abs(m) < 0.02 ? 0 : m * 0.94), 2500);
    return () => clearInterval(id);
  }, []);
  useEffect(() => {
    setMessage(bacPickLine('greet', { player: tweaks.playerName, dealer: tweaks.dealerName }));
    setExpression('idle');
  }, [tweaks.dealerName, tweaks.playerName]);

  const staked = bacSum(bets);
  useEffect(() => {
    if (idleTimerRef.current) clearTimeout(idleTimerRef.current);
    if (phase === 'betting' && staked === 0) {
      idleTimerRef.current = setTimeout(() => { setIsIdle(true); setMessage(bacPickLine('idle_long', ctx)); }, 12000);
    } else setIsIdle(false);
    return () => { if (idleTimerRef.current) clearTimeout(idleTimerRef.current); };
  }, [phase, staked, message]);

  // Out of chips → the shared reset modal (casino/casino-reload.js), which also asks for feedback.
  useEffect(() => {
    if (phase === 'betting' && bankroll < BAC_MIN_BET && staked === 0) setShowBrokeModal(true);
    else if (bankroll >= BAC_MIN_BET && showBrokeModal) setShowBrokeModal(false);
  }, [phase, bankroll, staked]);
  useEffect(() => {
    if (!showBrokeModal || !window.CASINO_RELOAD) return;
    window.CASINO_RELOAD.open({
      game: 'baccarat', minBet: BAC_MIN_BET,
      message: `The shoe cleaned you out, ${tweaks.playerName || 'friend'}.`,
      onReset: v => setBankroll(v),
      onClose: () => setShowBrokeModal(false)
    });
  }, [showBrokeModal]);

  // The selected chip steps down when the bankroll can no longer cover it.
  useEffect(() => {
    if (phase !== 'betting' || bankroll >= chip) return;
    const fit = [...BAC_CHIP_DEFS].reverse().find(c => c.value <= bankroll);
    if (fit) setChip(fit.value);
  }, [bankroll, phase]);

  // ── Betting ──
  function placeBet(spot) {
    if (phase !== 'betting') return;
    if (bankroll < chip) return;
    if (bets[spot] + chip > BAC_MAX_SPOT) { setMessage(`The limit on ${R.LABELS[spot]} is $${BAC_MAX_SPOT}.`); return; }
    const next = { ...bets, [spot]: bets[spot] + chip };
    setBets(next);
    setBankroll(br => br - chip);
    if (tweaks.soundOn) BAC_SFX.chip();
    setIsIdle(false);
    const total = bacSum(next);
    if (spot === 'tie' && bets.tie === 0) say('bet_tie', 'happy');
    else if ((spot === 'playerPair' || spot === 'bankerPair' || spot === 'dragon7') && bets[spot] === 0) say('bet_side', 'happy');
    else if (total >= 500) say('bet_huge', 'shocked');
    else if (total >= 100) say('bet_high', 'happy');
    else say('bet_low', 'idle');
  }

  function clearBets() {
    if (phase !== 'betting') return;
    setBankroll(br => br + bacSum(bets));
    setBets(BAC_EMPTY);
    say('idle', 'idle');
  }

  // Lays the last hand's bets on an empty table. Returns the bets placed (or null).
  function rebet() {
    if (!lastBets) return null;
    const need = bacSum(lastBets);
    if (bankroll < need) return null;
    setBets(lastBets);
    setBankroll(br => br - need);
    say('rebet', 'happy');
    return lastBets;
  }

  // ── The deal ──
  function deal(withBets) {
    const b = withBets || bets;
    if (bacMain(b) < BAC_MIN_BET) return;
    if (window.LGPlayNext) window.LGPlayNext.started();
    timers.current.forEach(clearTimeout); timers.current = [];
    const r = R.coup(shoeRef.current);
    setShoe(r.rest);
    setLastBets(b);
    setCoup(r);
    setSettled(null);
    setDealt({ player: 2, banker: 2 });
    setShown({ player: 0, banker: 0 });
    setPhase('dealing');
    say('deal', 'deal');
    const sfx = tweaks.soundOn;
    if (sfx) { BAC_SFX.deal(); [0, 120, 240, 360].forEach(d => setTimeout(() => BAC_SFX.card(), d)); }

    const k = tweaks.quickDeal ? 0.45 : 1;
    let t = 1000 * k;
    later(() => { setShown(s => ({ ...s, player: 2 })); if (sfx) BAC_SFX.flip(); }, t);
    t += 700 * k;
    later(() => {
      setShown(s => ({ ...s, banker: 2 }));
      if (sfx) BAC_SFX.flip();
      const p2 = R.total(r.player.slice(0, 2)), b2 = R.total(r.banker.slice(0, 2));
      if (r.natural) say(p2 >= 8 && p2 >= b2 ? 'natural_player' : 'natural_banker', 'shocked');
    }, t);
    t += 900 * k;
    for (const step of r.steps.slice(4)) {
      const hand = step.hand;
      later(() => {
        say(hand === 'player' ? 'player_draws' : 'banker_draws', 'deal');
        setDealt(d => ({ ...d, [hand]: 3 }));
        setShown(s => ({ ...s, [hand]: 3 }));
        if (sfx) BAC_SFX.card();
      }, t);
      t += 1100 * k;
    }
    later(() => resolve(r, b), t);
  }

  function resolve(r, b) {
    const s = R.settle(r, b);
    setBankroll(br => br + s.returned);
    const after = bankrollRef.current + s.returned; // the stakes came off as the chips went down
    setSettled(s);
    setPhase('resolved');
    setHistory(h => h.concat([{ winner: r.winner, playerPair: r.playerPair, bankerPair: r.bankerPair, natural: r.natural }]));

    setStats(st => ({
      played: st.played + 1,
      won: st.won + (s.net > 0 ? 1 : 0),
      peak: Math.max(st.peak, after),
      biggestWin: Math.max(st.biggestWin, s.net)
    }));
    if (window.CASINO_STATS) {
      window.CASINO_STATS.recordEvent('baccarat', { won: s.net > 0, payout: Math.max(0, s.net) });
      window.CASINO_STATS.recordPeak(after);
    }
    const o = s.outcome;
    const bonus = o.tie === 'win' || o.dragon7 === 'win' || o.playerPair === 'win' || o.bankerPair === 'win';
    if (s.net > 0) { setStreak(x => x + 1); if (tweaks.soundOn) (bonus ? BAC_SFX.bonus : BAC_SFX.win)(); }
    else if (s.net < 0) { setStreak(0); if (tweaks.soundOn) BAC_SFX.lose(); }
    else if (tweaks.soundOn) BAC_SFX.push();

    later(() => {
      if (o.dragon7 === 'win') return say('dragon_win', 'shocked');
      if (o.tie === 'win') return say('win_tie', 'shocked');
      if (o.playerPair === 'win' || o.bankerPair === 'win') return say('pair_win', 'shocked');
      if (o.banker === 'push' && r.dragon7) return say('dragon_push', 'idle');
      if (r.winner === 'tie' && (o.player === 'push' || o.banker === 'push')) return say('push_tie', 'idle');
      if (o.player === 'win' && s.net > 0) return say('win_player', 'sad');
      if (o.banker === 'win' && s.net > 0) return say('win_banker', 'sad');
      if (s.net < 0) return say('lose', 'happy');
      say('idle', 'idle');
    }, 450);
  }

  function nextRound() {
    timers.current.forEach(clearTimeout); timers.current = [];
    setBets(BAC_EMPTY);
    setCoup(null);
    setSettled(null);
    setDealt({ player: 0, banker: 0 });
    setShown({ player: 0, banker: 0 });
    setPhase('betting');
    setShowPays(false);
    if (shoeRef.current.length < R.CUT) {
      shoeRef.current = R.newShoe();
      setShoe(shoeRef.current);
      setHistory([]);
      say('new_shoe', 'deal');
    } else {
      setMessage(bacPickLine('idle', ctx));
      setExpression('idle');
    }
  }

  // "Rebet & Deal": a fresh table, last hand's bets, and the deal, in one tap.
  function rebetAndDeal() {
    nextRound();
    if (!lastBets || bankroll < bacSum(lastBets)) return;
    setBets(lastBets);
    setBankroll(br => br - bacSum(lastBets));
    setTimeout(() => deal(lastBets), 60);
  }

  const canRebet = !!lastBets && bankroll >= bacSum(lastBets);
  const hint = tweaks.showHints && phase === 'betting' ? bacHint(bets) : null;
  const words = useMemo(() => (coup && settled ? bacResultWords(coup, settled, lastBets || bets) : null), [coup, settled]);
  const totals = {
    player: coup && shown.player ? R.total(coup.player.slice(0, shown.player)) : null,
    banker: coup && shown.banker ? R.total(coup.banker.slice(0, shown.banker)) : null
  };
  const handCards = who => (coup ? coup[who].slice(0, dealt[who]) : []);
  const winner = phase === 'resolved' && coup ? coup.winner : null;
  const naturalOn = who => !!(coup && coup.natural && shown.banker >= 2 && coup[who].length === 2 && R.total(coup[who]) >= 8);
  const dealerGender = CASINO_DEALERS.get(tweaks.dealerName).art;
  const shownBets = phase === 'betting' ? bets : (lastBets || bets);
  const mainShort = phase === 'betting' && staked > 0 && bacMain(bets) < BAC_MIN_BET;

  const nameModal = showNameModal && ReactDOM.createPortal(
    <NameModal initialName={tweaks.playerName} firstVisit={showNameModal === 'first'} onSave={savePlayerName}
      onSkip={() => savePlayerName('')} onCancel={() => setShowNameModal(false)} />,
    document.body
  );

  const tweaksPanel = (
    <TweaksPanel title="Tweaks">
      <TweakSection title="Players">
        <TweakText label="Your name" value={tweaks.playerName} onChange={v => setTweak('playerName', v)} />
        <TweakSelect label="Dealer" value={tweaks.dealerName} onChange={v => setTweak('dealerName', v)} options={CASINO_DEALERS.options} />
      </TweakSection>
      <TweakSection title="Game">
        <TweakToggle label="Strategy hints" value={tweaks.showHints} onChange={v => setTweak('showHints', v)} />
        <TweakToggle label="Quick deal" value={tweaks.quickDeal} onChange={v => setTweak('quickDeal', v)} />
        <TweakToggle label="Sound" value={tweaks.soundOn} onChange={v => setTweak('soundOn', v)} />
      </TweakSection>
    </TweaksPanel>
  );

  const goldBtn = (on, extra) => ({
    background: on ? 'linear-gradient(180deg, #f5d896, #c9a26a)' : 'rgba(40,28,18,.45)',
    color: on ? '#1a1208' : 'rgba(255,255,255,.32)',
    border: `1px solid ${on ? 'rgba(245,216,150,1)' : 'rgba(201,162,106,.2)'}`,
    borderRadius: 12, fontFamily: "'Playfair Display', serif", fontStyle: 'italic', fontWeight: 700,
    cursor: on ? 'pointer' : 'not-allowed', boxShadow: on ? '0 12px 26px rgba(230,197,144,.4), inset 0 1px 0 rgba(255,255,255,.5)' : 'none',
    transition: 'all .2s', ...extra
  });
  const darkBtn = (on, extra) => ({
    background: 'linear-gradient(180deg, rgba(40,28,18,.95), rgba(20,12,6,.95))',
    color: on ? 'var(--ivory)' : 'rgba(255,255,255,.3)', border: '1px solid rgba(201,162,106,.4)', borderRadius: 12,
    fontFamily: 'inherit', fontWeight: 600, letterSpacing: '.14em', textTransform: 'uppercase',
    cursor: on ? 'pointer' : 'not-allowed', ...extra
  });
  const status = (text, size) => (
    <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 12, fontSize: size, letterSpacing: '.2em', color: 'var(--ivory-dim)', textTransform: 'uppercase' }}>
      <span style={{ width: 10, height: 10, borderRadius: '50%', background: 'var(--brass)', animation: 'glowPulse 1s ease-in-out infinite' }} />
      {text}
    </div>
  );
  const dealingText = !coup ? 'Dealing…'
    : dealt.player === 3 && dealt.banker === 3 ? 'Banker draws…'
    : dealt.player === 3 ? 'Player draws…'
    : dealt.banker === 3 ? 'Banker draws…'
    : 'Dealing…';

  // ── Upright phone column (460 design px wide, 780–1000 tall) ──
  if (portrait) {
    const extra = Math.max(0, (canvas.h || 780) - 780);
    const cardH = Math.round(Math.min(118, 80 + extra * 0.2)); // three overlapped cards per hand still fit 210px
    const cardW = Math.round(cardH * 78 / 110);
    let band = null;
    if (phase === 'betting') {
      band = (
        <div style={{ textAlign: 'center', padding: '0 6px' }}>
          <div style={{ fontSize: 15, lineHeight: 1.35, color: 'var(--ivory)' }}>
            {mainShort ? 'Side bets need a Player, Banker or Tie bet.' : staked >= BAC_MIN_BET ? 'Tap Deal when you’re ready.' : 'Pick a chip, then tap a spot.'}
          </div>
          {hint && <div style={{ fontSize: 14, lineHeight: 1.35, color: 'var(--brass-2)', marginTop: 5, fontWeight: 600 }}>Hint: {hint.action}. {hint.explanation}</div>}
        </div>
      );
    } else if (phase === 'dealing') band = status(dealingText, 14);
    else band = <ResultPanel words={words} size="portrait" />;

    let controls;
    if (phase === 'betting') {
      controls = (
        <>
          <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
            {BAC_CHIP_DEFS.map(c => <Chip key={c.value} value={c.value} size={56} selected={chip === c.value} onClick={() => setChip(c.value)} disabled={bankroll < c.value} />)}
            <button onClick={clearBets} disabled={!staked} style={darkBtn(!!staked, { flex: 1, minHeight: 48, fontSize: 13 })}>Clear</button>
          </div>
          <div style={{ display: 'flex', gap: 10 }}>
            <button onClick={rebet} disabled={!canRebet || staked > 0} style={darkBtn(canRebet && !staked, { flex: '0 0 112px', minHeight: 56, fontSize: 13 })}>Rebet ↺</button>
            <button onClick={() => deal()} disabled={bacMain(bets) < BAC_MIN_BET} style={goldBtn(bacMain(bets) >= BAC_MIN_BET, { flex: 1, minHeight: 56, fontSize: 21 })}>
              Deal{staked > 0 ? ` · $${staked}` : ''} →
            </button>
          </div>
        </>
      );
    } else if (phase === 'resolved') {
      controls = (
        <div style={{ display: 'flex', gap: 12 }}>
          <button onClick={nextRound} style={canRebet ? darkBtn(true, { flex: 1, minHeight: 56, fontSize: 14 }) : goldBtn(true, { flex: 1, minHeight: 56, fontSize: 21 })}>New Hand</button>
          {canRebet && <button onClick={rebetAndDeal} style={goldBtn(true, { flex: 1.4, minHeight: 56, fontSize: 21 })}>Rebet &amp; Deal →</button>}
        </div>
      );
    }

    return (
      <div style={{ width: '100%', height: '100%', padding: 10, display: 'flex', flexDirection: 'column', gap: 8, position: 'relative' }}>
        <PortraitRail bankroll={bankroll} streak={streak} showHints={tweaks.showHints}
          onToggleHints={() => setTweak('showHints', !tweaks.showHints)} onPays={() => setShowPays(true)}
          playerName={tweaks.playerName} onOpenProfile={() => setShowNameModal('edit')} />
        <DealerStrip name={tweaks.dealerName} gender={dealerGender} message={message} />
        <div style={{ flex: 1, minHeight: 0, position: 'relative' }}>
          <FeltBackdrop />
          <div style={{ position: 'absolute', inset: 0, padding: '8px 10px 10px', display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 8 }}>
            <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 5 }}>
              <BigRoad history={history} cell={12} cols={34} />
              <RoadCounts history={history} shoeLeft={shoe.length} compact />
            </div>
            <div style={{ display: 'flex', justifyContent: 'space-around', width: '100%' }}>
              <HandBox who="player" compact cards={handCards('player')} shownCount={shown.player} total={totals.player} winner={winner} cardW={cardW} cardH={cardH} natural={naturalOn('player')} />
              <HandBox who="banker" compact cards={handCards('banker')} shownCount={shown.banker} total={totals.banker} winner={winner} cardW={cardW} cardH={cardH} natural={naturalOn('banker')} />
            </div>
            <div style={{ flex: 1, minHeight: 0, width: '100%', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>{band}</div>
            <BetLayout compact width={420} bets={shownBets} settled={phase === 'resolved' ? settled : null}
              onBet={phase === 'betting' ? placeBet : null} />
          </div>
        </div>
        <div style={{ height: 126, flexShrink: 0, display: 'flex', flexDirection: 'column', justifyContent: 'flex-end', gap: 10 }}>
          {phase === 'dealing' ? <div style={{ height: 56, display: 'flex', alignItems: 'center', justifyContent: 'center' }}>{status('Cards out', 13)}</div> : controls}
        </div>
        {showPays && <PaySheet big onClose={() => setShowPays(false)} />}
        {tweaksPanel}
        {nameModal}
      </div>
    );
  }

  // ── Desktop / tablet, and phones held sideways (tight) ──
  const cardW = tight ? 92 : 78, cardH = tight ? 130 : 110;
  return (
    <div style={{ width: '100%', height: '100%', display: 'flex', alignItems: 'center', justifyContent: 'center', padding: 24, position: 'relative' }}>
      <div style={{ width: 1380, maxWidth: '100%', height: '100%', maxHeight: 860, display: 'flex', gap: 20, position: 'relative' }}>
        <DealerPanel name={tweaks.dealerName} gender={dealerGender} expression={expression} message={message}
          isIdle={isIdle} mood={mood} tight={tight} />

        <div style={{ flex: 1, position: 'relative', minWidth: 800 }}>
          <FeltBackdrop />
          <div style={{ position: 'absolute', inset: 0, display: 'flex', flexDirection: 'column' }}>
            <BrassRail bankroll={bankroll} streak={streak} played={stats.played} won={stats.won}
              showHints={tweaks.showHints} tight={tight} onPays={() => setShowPays(true)}
              onToggleHints={() => setTweak('showHints', !tweaks.showHints)}
              playerName={tweaks.playerName} onOpenProfile={() => setShowNameModal('edit')} />

            <div style={{ flex: 1, minHeight: 0, position: 'relative', padding: tight ? '8px 22px 0' : '14px 22px 0', display: 'flex', flexDirection: 'column', alignItems: 'center', gap: tight ? 8 : 14 }}>
              <FeltLogo />
              <Scoreboard history={history} shoeLeft={shoe.length} tight={tight} />
              <div style={{ display: 'flex', justifyContent: 'center', gap: tight ? 70 : 90, position: 'relative', zIndex: 2 }}>
                <HandBox who="player" tight={tight} cards={handCards('player')} shownCount={shown.player} total={totals.player} winner={winner} cardW={cardW} cardH={cardH} natural={naturalOn('player')} />
                <HandBox who="banker" tight={tight} cards={handCards('banker')} shownCount={shown.banker} total={totals.banker} winner={winner} cardW={cardW} cardH={cardH} natural={naturalOn('banker')} />
              </div>
              <div style={{ position: 'relative', zIndex: 2, marginTop: tight ? 4 : 8 }}>
                <BetLayout width={tight ? 860 : 720} tight={tight} showEdge={tweaks.showHints} bets={shownBets}
                  settled={phase === 'resolved' ? settled : null} onBet={phase === 'betting' ? placeBet : null} />
              </div>
            </div>

            <div style={{ position: 'relative', zIndex: 5, padding: tight ? '10px 22px 14px' : '14px 26px 20px', minHeight: tight ? 150 : 170, display: 'flex', flexDirection: 'column', justifyContent: 'center', gap: 10 }}>
              {phase === 'betting' && (
                <>
                  <div style={{ minHeight: tight ? 26 : 18, textAlign: 'center', fontSize: tight ? 19 : 13, color: mainShort ? '#f2b28c' : 'var(--brass-2)', lineHeight: 1.35 }}>
                    {mainShort ? 'Side bets need a Player, Banker or Tie bet to deal.'
                      : hint ? <span><b>Hint: {hint.action}.</b> <span style={{ color: 'var(--ivory-dim)' }}>{hint.explanation}</span></span>
                      : staked === 0 ? <span style={{ color: 'var(--ivory-dim)' }}>Pick a chip, then click a spot on the felt.</span> : null}
                  </div>
                  <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 18 }}>
                    <div style={{ display: 'flex', gap: tight ? 12 : 12, alignItems: 'center' }}>
                      {BAC_CHIP_DEFS.map(c => <Chip key={c.value} value={c.value} size={tight ? 76 : 58} selected={chip === c.value} onClick={() => setChip(c.value)} disabled={bankroll < c.value} />)}
                    </div>
                    <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 2, fontSize: tight ? 15 : 10, letterSpacing: '.28em', color: 'var(--brass)', textTransform: 'uppercase' }}>
                      <span>Total bet</span>
                      <span style={{ fontFamily: "'Playfair Display', serif", fontSize: tight ? 38 : 26, color: staked ? 'var(--brass-2)' : 'rgba(230,197,144,.4)', fontStyle: 'italic', fontWeight: 700, letterSpacing: 0 }}>${staked}</span>
                    </div>
                    <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
                      <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
                        <button onClick={clearBets} disabled={!staked} style={darkBtn(!!staked, { padding: tight ? '0 18px' : '8px 14px', minHeight: tight ? 58 : undefined, fontSize: tight ? 16 : 11 })}>Clear</button>
                        <button onClick={rebet} disabled={!canRebet || staked > 0} style={darkBtn(canRebet && !staked, { padding: tight ? '0 18px' : '8px 14px', minHeight: tight ? 58 : undefined, fontSize: tight ? 16 : 11 })}>Rebet ↺</button>
                      </div>
                      <button onClick={() => deal()} disabled={bacMain(bets) < BAC_MIN_BET}
                        style={goldBtn(bacMain(bets) >= BAC_MIN_BET, { padding: tight ? '0 40px' : '18px 44px', minHeight: tight ? 124 : undefined, fontSize: tight ? 32 : 24, letterSpacing: '.04em' })}>Deal →</button>
                    </div>
                  </div>
                </>
              )}
              {phase === 'dealing' && <div style={{ height: 100, display: 'flex', alignItems: 'center', justifyContent: 'center' }}>{status(dealingText, tight ? 22 : 13)}</div>}
              {phase === 'resolved' && (
                <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 18 }}>
                  <div style={{ flex: 1, maxWidth: tight ? 620 : 600, minWidth: 0 }}><ResultPanel words={words} size={tight ? 'tight' : 'wide'} /></div>
                  <div style={{ display: 'flex', flexDirection: 'column', gap: 10, flexShrink: 0 }}>
                    {canRebet && <button onClick={rebetAndDeal} style={goldBtn(true, { width: tight ? 260 : 210, minHeight: tight ? 64 : 50, fontSize: tight ? 26 : 18 })}>Rebet &amp; Deal →</button>}
                    <button onClick={nextRound} style={darkBtn(true, { width: tight ? 260 : 210, minHeight: tight ? 64 : 50, fontSize: tight ? 17 : 12 })}>New Hand</button>
                  </div>
                </div>
              )}
            </div>
          </div>
          {showPays && <PaySheet big={tight} onClose={() => setShowPays(false)} />}
        </div>
      </div>
      {tweaksPanel}
      {nameModal}
    </div>
  );
}

// Google row on the name card: a guest gets a "Sign in with Google" button
// (it leads to the account card on /profile/, the one place that loads
// Google's script, the same link the out-of-chips modal uses); a signed-in
// player sees who they are signed in as. lgAccount is written by lg-auth.js.
function bacAccount() {
  try { const a = JSON.parse(localStorage.getItem('lgAccount') || 'null'); return a && a.provider ? a : null; } catch (e) { return null; }
}
function bacTrackSignIn() {
  try { if (typeof gtag === 'function') gtag('event', 'sign_in_link', { placement: 'table_seat', game: 'baccarat' }); } catch (e) {}
}
function BacSignInNote({ fontSize = 13, big = false }) {
  const acct = bacAccount();
  const note = { marginTop: 8, fontSize, lineHeight: 1.45, color: 'var(--ivory-dim)' };
  if (acct) {
    return (
      <div style={{ ...note, marginTop: 12 }}>
        <span style={{ color: 'var(--brass-2)' }}>✓ Signed in with Google{acct.email ? ` as ${acct.email}` : ''}</span>
        {' · '}<a href="/profile/#prof-account" style={{ color: 'var(--brass-2)' }}>Manage</a>
      </div>
    );
  }
  return (
    <div style={{ marginTop: 14 }}>
      <a href="/profile/#prof-account" onClick={bacTrackSignIn} style={{
        display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 10,
        minHeight: big ? 50 : 44, padding: '0 16px', borderRadius: 999,
        background: '#fff', color: '#1f1f1f',
        fontFamily: 'system-ui, -apple-system, sans-serif', fontSize: big ? 16 : 14, fontWeight: 600,
        letterSpacing: 0, textTransform: 'none', textDecoration: 'none',
        border: '1px solid rgba(201,162,106,.5)', boxShadow: '0 2px 8px rgba(0,0,0,.35)'
      }}>
        <svg viewBox="0 0 48 48" width="18" height="18" aria-hidden="true" style={{ flex: 'none' }}>
          <path fill="#EA4335" d="M24 9.5c3.54 0 6.71 1.22 9.21 3.6l6.85-6.85C35.9 2.38 30.47 0 24 0 14.62 0 6.51 5.38 2.56 13.22l7.98 6.19C12.43 13.72 17.74 9.5 24 9.5z"/>
          <path fill="#4285F4" d="M46.98 24.55c0-1.57-.15-3.09-.38-4.55H24v9.02h12.94c-.58 2.96-2.26 5.48-4.78 7.18l7.73 6c4.51-4.18 7.09-10.36 7.09-17.65z"/>
          <path fill="#FBBC05" d="M10.53 28.59c-.48-1.45-.76-2.99-.76-4.59s.27-3.14.76-4.59l-7.98-6.19C.92 16.46 0 20.12 0 24c0 3.88.92 7.54 2.56 10.78l7.97-6.19z"/>
          <path fill="#34A853" d="M24 48c6.48 0 11.93-2.13 15.89-5.81l-7.73-6c-2.15 1.45-4.92 2.3-8.16 2.3-6.26 0-11.57-4.22-13.47-9.91l-7.98 6.19C6.51 42.62 14.62 48 24 48z"/>
        </svg>
        Sign in with Google
      </a>
      <div style={note}>
        <b style={{ color:'var(--brass-2, #e6c590)', fontWeight: 600 }}>+$5,000 in chips</b> the first time. Keeps your chips and progress on every device.
      </div>
    </div>
  );
}

// Optional name, rendered into <body> so it is full size on every screen (the same card as the other tables).
// First visit: the name is optional — an empty field, Escape or a click on the dimmed backdrop all continue
// to the table without one. Opened later from the profile pill, Cancel / Escape / backdrop just close it.
function NameModal({ initialName = '', firstVisit, onSave, onSkip, onCancel }) {
  const [name, setName] = React.useState(initialName);
  const inputRef = React.useRef(null);
  const downOnBackdrop = React.useRef(false);
  const dismiss = firstVisit ? onSkip : onCancel;
  const dismissRef = React.useRef(dismiss);
  dismissRef.current = dismiss;
  React.useEffect(() => {
    const id = setTimeout(() => { if (inputRef.current) { inputRef.current.focus(); inputRef.current.select(); } }, 50);
    const onKey = e => { if (e.key === 'Escape' || e.key === 'Esc') { e.preventDefault(); dismissRef.current(); } };
    window.addEventListener('keydown', onKey);
    return () => { clearTimeout(id); window.removeEventListener('keydown', onKey); };
  }, []);

  function submit(e) {
    if (e) e.preventDefault();
    if (name.trim()) onSave(name);
    else onSkip();
  }

  const pillBtn = { minHeight: 44, borderRadius: 999, fontSize: 11, fontWeight: 700, letterSpacing: '.18em', textTransform: 'uppercase', cursor: 'pointer' };
  return (
    <div
      onMouseDown={e => { downOnBackdrop.current = e.target === e.currentTarget; }}
      onClick={e => { if (e.target === e.currentTarget && downOnBackdrop.current) dismiss(); }}
      style={{
        position: 'fixed', inset: 0, zIndex: 9000, background: 'rgba(8,5,2,.65)', backdropFilter: 'blur(8px)',
        display: 'flex', alignItems: 'center', justifyContent: 'center', padding: 16
      }}>
      <form onSubmit={submit} style={{
        background: 'linear-gradient(180deg, rgba(35,22,10,.95), rgba(20,12,6,.98))', border: '1px solid rgba(201,162,106,.5)',
        borderRadius: 16, padding: '30px 36px 26px', boxShadow: '0 30px 80px rgba(0,0,0,.7), inset 0 1px 0 rgba(230,197,144,.15)',
        width: 440, maxWidth: '100%', textAlign: 'center'
      }}>
        <div style={{ fontFamily: "'Playfair Display', serif", fontStyle: 'italic', fontSize: 22, color: 'var(--brass-2)', marginBottom: 20, lineHeight: 1.3 }}>
          {initialName ? 'Going by something different tonight?' : 'What should the dealer call you?'}
        </div>
        <input ref={inputRef} value={name} onChange={e => setName(e.target.value)} maxLength={20}
          placeholder="Your first name (optional)" aria-label="Your first name (optional)" autoComplete="off"
          style={{
            width: '100%', padding: '12px 16px', background: 'rgba(10,6,3,.6)', border: '1px solid rgba(201,162,106,.4)', borderRadius: 10,
            color: 'var(--ivory)', fontFamily: "'Playfair Display', serif", fontSize: 19, fontStyle: 'italic', textAlign: 'center', outline: 'none', boxSizing: 'border-box'
          }} />
        <div style={{ marginTop: 10, fontSize: 13, lineHeight: 1.45, color: 'var(--ivory-dim)' }}>
          First name only · shown on the leaderboards
        </div>
        <BacSignInNote />
        <div style={{ display: 'flex', gap: 10, marginTop: 18, justifyContent: 'center' }}>
          {!firstVisit && (
            <button type="button" onClick={onCancel} style={{ ...pillBtn, padding: '0 20px', background: 'rgba(20,12,6,.6)', border: '1px solid rgba(201,162,106,.3)', color: 'var(--ivory-dim)' }}>Cancel</button>
          )}
          <button type="submit" style={{ ...pillBtn, padding: '0 24px', background: 'linear-gradient(180deg, #e6c590, #c9a26a)', border: '1px solid rgba(201,162,106,.5)', color: '#1a1208', boxShadow: '0 4px 12px rgba(230,197,144,.4)' }}>
            {firstVisit ? 'Take a seat →' : 'Save'}
          </button>
        </div>
        <a href="/profile/" style={{ display: 'inline-block', marginTop: 14, fontSize: 10, letterSpacing: '.12em', textTransform: 'uppercase', color: 'var(--ivory-dim)', textDecoration: 'none' }}>Full profile →</a>
      </form>
    </div>
  );
}

ReactDOM.createRoot(document.getElementById('root')).render(<App />);
