// duel-screen.jsx — GTO Duel mobile game screen (portrait + landscape)
// Exports (to window): DuelScreen
// Dark game theme on top of OctopiPoker tokens (colors_and_type.css).

const DUEL = {
  heroCards: ['5s', '5d'],
  geoCards: ['Kh', 'Qd'],
  flop: ['5c', '6s', 'As'],
  turn: '2h',
  river: '9d',
};

const SUITS = {
  s: { glyph: '\u2660', bg: '#3E4350' },
  c: { glyph: '\u2663', bg: '#27BF69' },
  d: { glyph: '\u2666', bg: '#4A8DF6' },
  h: { glyph: '\u2665', bg: '#E74C3C' },
};

const ACTION_COLORS = {
  Check: 'var(--op-action-check)',
  Call: 'var(--op-action-check)',
  Bet: 'var(--op-bet-2)',
  Fold: 'var(--op-action-fold)',
  'All-In': 'var(--op-all-in)',
};

const r1 = (x) => Math.round(x * 10) / 10;

// ─────────────────────────────────────────────────────────────
// Game state hook — scripted heads-up demo loop
// ─────────────────────────────────────────────────────────────
function initHand(hp1, hp2) {
  return {
    street: 0,            // 0 flop, 1 turn, 2 river
    pot: 16.9,
    hero: 52.7, geo: 52.7,
    mode: 'A',            // A = check/bet/all-in, B = fold/call/all-in
    callAmt: 0,
    phase: 'hero',        // hero | george | over
    heroChip: null, geoChip: null,
    geoSay: null,
    reveal: false,
    banner: null,
    hp1, hp2,
    clockKey: 0,
  };
}

function useDuel(mult) {
  const [s, setS] = React.useState(() => initHand(1000, 1000));
  const ref = React.useRef(s);
  ref.current = s;
  const timers = React.useRef([]);
  React.useEffect(() => () => timers.current.forEach(clearTimeout), []);
  const later = (ms, fn) => timers.current.push(setTimeout(fn, ms));
  const set = (patch) => setS((p) => ({ ...p, ...patch }));

  const board = [...DUEL.flop, ...(s.street >= 1 ? [DUEL.turn] : []), ...(s.street >= 2 ? [DUEL.river] : [])];

  const dealNext = () => {
    const c = ref.current;
    set({ street: c.street + 1, heroChip: null, geoChip: null, geoSay: null, mode: 'A', callAmt: 0, phase: 'hero', clockKey: c.clockKey + 1 });
  };

  const endHand = (heroWins) => {
    const c = ref.current;
    const dmg = Math.round(c.pot * 10 * mult);
    set({
      phase: 'over',
      reveal: heroWins ? true : c.reveal,
      banner: heroWins
        ? { title: `You win ${r1(c.pot)} bb`, sub: `George \u2212${dmg} HP`, win: true }
        : { title: `George wins ${r1(c.pot)} bb`, sub: `You \u2212${dmg} HP`, win: false },
      hp1: heroWins ? c.hp1 : Math.max(0, c.hp1 - dmg),
      hp2: heroWins ? Math.max(0, c.hp2 - dmg) : c.hp2,
    });
    later(3200, () => {
      const cc = ref.current;
      setS({ ...initHand(cc.hp1, cc.hp2), clockKey: cc.clockKey + 1 });
    });
  };

  const showdown = () => {
    set({ reveal: true, geoChip: null, heroChip: null, geoSay: null });
    later(700, () => endHand(true)); // pocket fives flop a set — hero holds
  };

  const afterStreet = () => {
    const c = ref.current;
    if (c.street >= 2) showdown(); else later(650, dealNext);
  };

  const heroAct = (kind) => {
    const c = ref.current;
    if (c.phase !== 'hero' || c.banner) return;
    const bet = r1(Math.max(1, c.pot / 3));

    if (kind === 'check') {
      set({ phase: 'george', heroChip: 'Check' });
      if (c.street === 0) {
        const gb = r1(Math.max(1, c.pot / 3));
        later(1500, () => set({
          geoChip: `Bet ${gb} bb`, geoSay: `I'll bet ${gb}.`, geo: r1(ref.current.geo - gb), pot: r1(ref.current.pot + gb),
          mode: 'B', callAmt: gb, phase: 'hero', heroChip: null, clockKey: ref.current.clockKey + 1,
        }));
      } else {
        later(1500, () => { set({ geoChip: 'Check', geoSay: 'Check.' }); later(900, afterStreet); });
      }
    } else if (kind === 'bet') {
      set({ phase: 'george', heroChip: `Bet ${bet} bb`, hero: r1(c.hero - bet), pot: r1(c.pot + bet) });
      later(1500, () => {
        set({ geoChip: `Call ${bet} bb`, geoSay: 'I call.', geo: r1(ref.current.geo - bet), pot: r1(ref.current.pot + bet) });
        later(1100, afterStreet);
      });
    } else if (kind === 'call') {
      set({ phase: 'george', heroChip: `Call ${c.callAmt} bb`, hero: r1(c.hero - c.callAmt), pot: r1(c.pot + c.callAmt) });
      later(800, afterStreet);
    } else if (kind === 'fold') {
      set({ phase: 'george', heroChip: 'Fold' });
      later(700, () => endHand(false));
    } else if (kind === 'allin') {
      const amt = c.hero;
      set({ phase: 'george', heroChip: 'All-In', hero: 0, pot: r1(c.pot + amt) });
      later(1500, () => {
        const cc = ref.current;
        const callAmt = Math.min(amt, cc.geo);
        set({ geoChip: 'Call \u2014 All-In', geoSay: "Call. Let's see it.", geo: r1(cc.geo - callAmt), pot: r1(cc.pot + callAmt), reveal: true });
        const left = 2 - cc.street;
        for (let i = 1; i <= left; i++) later(900 + i * 900, dealNext);
        later(900 + left * 900 + 900, () => endHand(true));
      });
    }
  };

  return { s, board, heroAct };
}

// ─────────────────────────────────────────────────────────────
// Atoms
// ─────────────────────────────────────────────────────────────
function PlayingCard({ code, w = 46, lift = false, deal = false, delay = 0, glow = false }) {
  const rank = code.slice(0, -1);
  const suit = SUITS[code.slice(-1)];
  const anims = [];
  if (deal) anims.push(`cardDeal 0.5s ${delay}ms cubic-bezier(0.2, 0.8, 0.3, 1) backwards`);
  if (glow) anims.push('goldPulse 1.2s ease-in-out infinite');
  return (
    <div style={{
      width: w, height: Math.round(w * 1.38), borderRadius: Math.max(6, w * 0.16),
      background: suit.bg, color: '#fff', position: 'relative',
      display: 'flex', flexDirection: 'column', boxSizing: 'border-box',
      padding: `${Math.round(w * 0.08)}px ${Math.round(w * 0.12)}px`,
      boxShadow: lift ? '0 6px 16px rgba(0,0,0,0.45)' : '0 2px 8px rgba(0,0,0,0.35)',
      border: '1px solid rgba(255,255,255,0.22)',
      animation: anims.length ? anims.join(', ') : undefined,
    }}>
      <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', fontSize: Math.max(9, w * 0.24), fontWeight: 700, lineHeight: 1, fontFamily: 'var(--op-font-ui)' }}>
        <span>{rank}</span>
        <span>{suit.glyph}</span>
      </div>
      <div style={{ flex: 1, display: 'flex', alignItems: 'center', justifyContent: 'center', fontFamily: 'var(--op-font-display)', fontWeight: 700, fontSize: w * 0.62, lineHeight: 1, paddingBottom: w * 0.04 }}>
        <span>{rank}</span>
      </div>
    </div>
  );
}

function CardBack({ w = 42 }) {
  return (
    <div style={{
      width: w, height: Math.round(w * 1.38), borderRadius: Math.max(6, w * 0.16),
      border: '1.5px solid rgba(255,255,255,0.55)', boxSizing: 'border-box',
      background: `#262A35 repeating-linear-gradient(45deg, rgba(255,255,255,0.16) 0 1px, transparent 1px 6px),
                   repeating-linear-gradient(-45deg, rgba(255,255,255,0.16) 0 1px, transparent 1px 6px)`,
      boxShadow: '0 2px 8px rgba(0,0,0,0.35)',
    }}></div>
  );
}

function CardSlot({ w = 46 }) {
  return (
    <div style={{
      width: w, height: Math.round(w * 1.38), borderRadius: Math.max(6, w * 0.16), boxSizing: 'border-box',
      border: '1.5px dashed rgba(255,255,255,0.18)', background: 'rgba(255,255,255,0.05)',
    }}></div>
  );
}

function HPBar({ val, max = 1000, h = 18, fontSize = 11, flash = false }) {
  return (
    <div style={{ position: 'relative', height: h, borderRadius: 999, background: 'rgba(255,255,255,0.10)', overflow: 'hidden', width: '100%' }}>
      <div style={{
        position: 'absolute', top: 0, left: 0, bottom: 0, width: `${(val / max) * 100}%`,
        background: 'linear-gradient(90deg, #27BF69, #4DDB8A)', borderRadius: 999,
        transition: 'width 0.6s cubic-bezier(.2,.8,.3,1)',
      }}></div>
      {flash && <div style={{ position: 'absolute', inset: 0, background: '#E74C3C', borderRadius: 999, animation: 'hpFlash 0.6s ease-out forwards' }}></div>}
      <div style={{
        position: 'absolute', inset: 0, display: 'flex', alignItems: 'center', justifyContent: 'center',
        fontFamily: 'var(--op-font-ui)', fontWeight: 700, fontSize, color: '#0B2417', letterSpacing: '0.02em',
      }}>
        <span>{val} / {max}</span>
      </div>
    </div>
  );
}

function Bolt({ size = 12, color = '#ABA2F1' }) {
  return (
    <svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke={color} strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
      <path d="M13 2 3 14h9l-1 8 10-12h-9l1-8z"></path>
    </svg>
  );
}

function PowerPill() {
  return (
    <div style={{
      display: 'flex', alignItems: 'center', gap: 5, padding: '3px 9px', borderRadius: 999,
      background: 'rgba(108,92,231,0.14)', border: '1px solid rgba(108,92,231,0.35)',
    }}>
      <Bolt size={11} />
      <span style={{ fontFamily: 'var(--op-font-ui)', fontWeight: 600, fontSize: 10.5, color: '#ABA2F1' }}>0 / 10</span>
    </div>
  );
}

function PowerBar({ val = 0, max = 10, compact = false }) {
  const h = compact ? 11 : 14;
  return (
    <div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
      <Bolt size={compact ? 11 : 13} color="#ABA2F1" />
      <div style={{ flex: 1, height: h, borderRadius: 999, background: 'rgba(255,255,255,0.08)', display: 'flex', gap: 2, padding: 2, boxSizing: 'border-box' }}>
        {Array.from({ length: max }).map((_, i) => (
          <span key={i} style={{ flex: 1, borderRadius: 2, background: i < val ? 'linear-gradient(90deg, #7869E9, #ABA2F1)' : 'rgba(255,255,255,0.07)' }}></span>
        ))}
      </div>
      <span style={{ fontFamily: 'var(--op-font-ui)', fontWeight: 700, fontSize: compact ? 9 : 10.5, color: '#ABA2F1', minWidth: 28, textAlign: 'right' }}>{val}/{max}</span>
    </div>
  );
}

function TimebankPill({ value }) {
  return (
    <div style={{ display: 'flex', alignItems: 'baseline', gap: 5 }}>
      <span style={{ fontFamily: 'var(--op-font-display)', fontWeight: 600, fontSize: 13, color: '#fff' }}>{value}</span>
      <span style={{ fontFamily: 'var(--op-font-ui)', fontWeight: 600, fontSize: 9, letterSpacing: '0.1em', color: 'rgba(255,255,255,0.45)' }}>TIMEBANK</span>
    </div>
  );
}

function ActionChip({ text }) {
  if (!text) return null;  const base = text.split(' ')[0].replace(',', '');
  const bg = ACTION_COLORS[base] || ACTION_COLORS[text] || 'var(--op-action-fold)';
  return (
    <div style={{
      padding: '4px 12px', borderRadius: 999, background: bg, color: '#1A1D23',
      fontFamily: 'var(--op-font-ui)', fontWeight: 700, fontSize: 11.5, whiteSpace: 'nowrap',
      boxShadow: '0 3px 10px rgba(0,0,0,0.4)',
    }}>
      <span>{text}</span>
    </div>
  );
}

function Avatar({ who, size = 34 }) {  if (who === 'geo') {
    return (
      <div style={{
        width: size, height: size, borderRadius: '50%', background: 'var(--op-exodus-20)',
        display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0,
        border: '2px solid rgba(255,255,255,0.18)', boxSizing: 'border-box', overflow: 'hidden',
      }}>
        <img src="assets/logo-mark.svg" alt="George" style={{ width: '68%', height: '68%' }} />
      </div>
    );
  }
  return (
    <div style={{
      width: size, height: size, borderRadius: '50%', background: 'var(--op-exodus-70)',
      display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0,
      border: '2px solid rgba(255,255,255,0.25)', boxSizing: 'border-box',
      fontFamily: 'var(--op-font-display)', fontWeight: 700, fontSize: size * 0.36, color: '#fff',
    }}>
      <span>U8</span>
    </div>
  );
}

function Badge({ text, gold = false }) {
  return (
    <span style={{
      padding: '2px 7px', borderRadius: 999, fontFamily: 'var(--op-font-ui)', fontWeight: 700, fontSize: 9.5,
      letterSpacing: '0.06em', flexShrink: 0,
      background: gold ? 'rgba(233,182,99,0.18)' : 'rgba(255,255,255,0.10)',
      color: gold ? 'var(--op-gold-50)' : 'rgba(255,255,255,0.65)',
      border: gold ? '1px solid rgba(233,182,99,0.4)' : '1px solid rgba(255,255,255,0.14)',
    }}>{text}</span>
  );
}

function ThinkingDots() {
  return (
    <div style={{
      display: 'flex', gap: 4, alignItems: 'center', padding: '7px 13px', borderRadius: 999,
      background: 'rgba(255,255,255,0.10)', border: '1px solid rgba(255,255,255,0.14)',
    }}>
      {[0, 1, 2].map((i) => (
        <span key={i} style={{
          width: 5, height: 5, borderRadius: '50%', background: '#ABA2F1',
          animation: `duelPulse 1s ${i * 0.18}s infinite ease-in-out`,
        }}></span>
      ))}
    </div>
  );
}

function GeoBubble({ text, below = false }) {
  const pos = below
    ? { top: 'calc(100% + 8px)', left: '50%', transform: 'translateX(-50%) rotate(-2deg)' }
    : { left: 'calc(50% + 56px)', top: 2, transform: 'rotate(-2deg)' };
  return (
    <div style={{
      position: 'absolute', ...pos,
      background: '#F8F9FC', color: '#25282D', borderRadius: '12px 12px 12px 3px',
      padding: '5px 12px', fontFamily: 'var(--op-font-dialog)', fontSize: 14.5, lineHeight: 1.25,
      whiteSpace: 'nowrap', boxShadow: '0 6px 16px rgba(0,0,0,0.4)', zIndex: 3,
    }}>
      <span>{text}</span>
    </div>
  );
}

// ─────────────────────────────────────────────────────────────
// FX — keyframes, particles, confetti, stamp
// ─────────────────────────────────────────────────────────────
function DuelFX() {
  return (
    <style>{`
      @keyframes duelPulse { 0%, 100% { opacity: 0.25; } 50% { opacity: 1; } }
      @keyframes ringPulse {
        0%, 100% { box-shadow: 0 0 40px rgba(0,0,0,0.5), inset 0 0 60px rgba(0,0,0,0.55), 0 0 26px -2px currentColor; }
        50% { box-shadow: 0 0 40px rgba(0,0,0,0.5), inset 0 0 60px rgba(0,0,0,0.55), 0 0 58px 6px currentColor; }
      }
      @keyframes cardDeal { from { transform: translateY(-16px) rotateY(85deg) scale(0.82); opacity: 0; } }
      @keyframes potBump { 30% { transform: scale(1.22); } }
      @keyframes dmgFloat { 0% { transform: translateY(0); opacity: 0; } 15% { opacity: 1; } 100% { transform: translateY(-30px); opacity: 0; } }
      @keyframes panelShake { 0%, 100% { transform: translateX(0); } 20% { transform: translateX(-5px); } 40% { transform: translateX(4px); } 60% { transform: translateX(-3px); } 80% { transform: translateX(2px); } }
      @keyframes hpFlash { from { opacity: 0.75; } to { opacity: 0; } }
      @keyframes bannerIn { 0% { transform: scale(0.6); opacity: 0; } 70% { transform: scale(1.05); opacity: 1; } 100% { transform: scale(1); } }
      @keyframes confettiFly { to { transform: translate(var(--dx), var(--dy)) rotate(var(--rot)); opacity: 0; } }
      @keyframes allinDim { from { opacity: 0; } to { opacity: 1; } }
      @keyframes shockwave { 0% { transform: scale(0.2); opacity: 1; } 100% { transform: scale(3.4); opacity: 0; } }
      @keyframes stampSlam { 0% { transform: scale(3.4) rotate(-16deg); opacity: 0; } 55% { transform: scale(0.88) rotate(-7deg); opacity: 1; } 75% { transform: scale(1.08) rotate(-8.5deg); } 100% { transform: scale(1) rotate(-8deg); } }
      @keyframes sparkFly { 0% { transform: rotate(var(--a)) translateX(12px) scaleX(0.4); opacity: 0; } 15% { opacity: 1; } 100% { transform: rotate(var(--a)) translateX(var(--d)) scaleX(1); opacity: 0; } }
      @keyframes coreGlow { 0% { transform: scale(0.3); opacity: 0; } 30% { opacity: 0.9; } 100% { transform: scale(1.7); opacity: 0; } }
      @keyframes tableImpact { 0%, 100% { translate: 0 0; } 20% { translate: -6px 3px; } 40% { translate: 5px -3px; } 60% { translate: -4px 2px; } 80% { translate: 2px -1px; } }
      @keyframes chipFly { 0% { transform: translate(var(--cx0), var(--cy0)) scale(0.55); opacity: 0; } 12% { opacity: 1; } 72% { transform: translate(0px, 0px) scale(1); opacity: 1; } 100% { transform: translate(0px, -6px) scale(0.85); opacity: 0; } }
      @keyframes feltBreath { 0%, 100% { opacity: 0.5; } 50% { opacity: 1; } }
      @keyframes seatBreath { 0%, 100% { box-shadow: 0 0 0 3px rgba(108,92,231,0.14); } 50% { box-shadow: 0 0 0 5px rgba(108,92,231,0.30), 0 0 22px rgba(108,92,231,0.35); } }
      @keyframes goldPulse { 0%, 100% { box-shadow: 0 0 10px 1px rgba(233,182,99,0.55); } 50% { box-shadow: 0 0 22px 5px rgba(233,182,99,0.9); } }
      @keyframes btnPop { 40% { transform: scale(1.05); } }
      @keyframes dotPulse { 0%, 100% { transform: scale(1); } 50% { transform: scale(1.45); } }
      @keyframes bob { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-3px); } }
    `}</style>
  );
}

function Confetti() {
  const colors = ['#FFDC35', '#FB81CA', '#36C591', '#6C5CE7', '#E9B663', '#50B5FF'];
  return (
    <div style={{ position: 'absolute', inset: 0, pointerEvents: 'none', zIndex: 6, display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
      {Array.from({ length: 20 }, (_, i) => {
        const a = (i / 20) * Math.PI * 2;
        const r = 90 + (i % 4) * 38;
        return (
          <span key={i} style={{
            position: 'absolute', width: 7, height: 11, borderRadius: 2,
            background: colors[i % 6],
            '--dx': `${Math.round(Math.cos(a) * r)}px`,
            '--dy': `${Math.round(Math.sin(a) * r - 50)}px`,
            '--rot': `${(i * 97) % 360}deg`,
            animation: `confettiFly 0.9s ${(i % 5) * 60}ms cubic-bezier(0.15, 0.6, 0.4, 1) forwards`,
          }}></span>
        );
      })}
    </div>
  );
}

function AllInFX() {
  const sparkColors = ['#FB81CA', '#FF53BA', '#FFD7EC', '#FB81CA'];
  return (
    <div style={{ position: 'absolute', inset: 0, display: 'flex', alignItems: 'center', justifyContent: 'center', zIndex: 4, pointerEvents: 'none' }}>
      {/* dim vignette */}
      <div style={{
        position: 'absolute', inset: 0, borderRadius: 999,
        background: 'radial-gradient(closest-side, transparent 40%, rgba(8,9,14,0.6))',
        animation: 'allinDim 0.4s ease-out forwards',
      }}></div>
      {/* glow core */}
      <div style={{
        position: 'absolute', width: 240, height: 240, borderRadius: '50%',
        background: 'radial-gradient(circle, rgba(251,129,202,0.5), rgba(251,129,202,0.12) 55%, transparent 72%)',
        animation: 'coreGlow 0.9s ease-out forwards',
      }}></div>
      {/* shockwaves */}
      <span style={{
        position: 'absolute', width: 110, height: 110, borderRadius: '50%',
        border: '3px solid rgba(251,129,202,0.9)', boxShadow: '0 0 24px rgba(251,129,202,0.7)',
        animation: 'shockwave 0.7s 0.12s cubic-bezier(0.1, 0.7, 0.3, 1) both',
      }}></span>
      <span style={{
        position: 'absolute', width: 110, height: 110, borderRadius: '50%',
        border: '2px solid rgba(255,215,236,0.8)',
        animation: 'shockwave 0.8s 0.28s cubic-bezier(0.1, 0.7, 0.3, 1) both',
      }}></span>
      {/* sparks */}
      {Array.from({ length: 16 }, (_, i) => (
        <span key={i} style={{
          position: 'absolute', width: 22, height: 3, borderRadius: 2,
          background: sparkColors[i % 4], boxShadow: `0 0 8px ${sparkColors[i % 4]}`,
          transformOrigin: 'left center',
          '--a': `${i * 22.5}deg`,
          '--d': `${110 + (i % 4) * 34}px`,
          animation: `sparkFly 0.65s ${0.14 + (i % 3) * 0.05}s cubic-bezier(0.1, 0.7, 0.3, 1) both`,
        }}></span>
      ))}
      {/* stamp */}
      <span style={{
        position: 'relative',
        fontFamily: 'var(--op-font-display)', fontWeight: 700, fontSize: 44, letterSpacing: '0.06em',
        color: '#FB81CA', border: '3px solid #FB81CA', borderRadius: 12, padding: '2px 20px',
        background: 'rgba(20,10,18,0.55)', backdropFilter: 'blur(2px)',
        textShadow: '0 0 18px rgba(251,129,202,0.9)',
        boxShadow: '0 0 34px rgba(251,129,202,0.55), inset 0 0 18px rgba(251,129,202,0.3)',
        animation: 'stampSlam 0.5s cubic-bezier(0.2, 0.8, 0.3, 1) backwards',
      }}>ALL-IN</span>
    </div>
  );
}

function PokerChip({ color, size = 17 }) {
  return (
    <span style={{
      width: size, height: size, borderRadius: '50%', display: 'inline-block', boxSizing: 'border-box',
      background: color, border: '2.5px dashed rgba(255,255,255,0.9)',
      boxShadow: `0 0 10px ${color}, inset 0 0 0 2px rgba(0,0,0,0.18)`,
    }}></span>
  );
}

function ChipFlight({ from, landscape }) {
  const hero = from === 'hero';
  const color = hero ? '#6C5CE7' : '#E9B663';
  const sx = landscape ? (hero ? 300 : -300) : 0;
  const sy = landscape ? (hero ? 44 : -44) : (hero ? 175 : -175);
  return (
    <div style={{ position: 'absolute', inset: 0, display: 'flex', alignItems: 'center', justifyContent: 'center', pointerEvents: 'none', zIndex: 3 }}>
      {[0, 1, 2].map((i) => (
        <span key={i} style={{
          position: 'absolute', marginLeft: (i - 1) * 12, marginTop: -34, display: 'flex',
          '--cx0': `${sx + (i - 1) * 18}px`,
          '--cy0': `${sy + (i % 2) * 14}px`,
          animation: `chipFly 0.65s ${i * 80}ms cubic-bezier(0.25, 0.7, 0.3, 1) both`,
        }}>
          <PokerChip color={color} />
        </span>
      ))}
    </div>
  );
}

// ─────────────────────────────────────────────────────────────
// Shot clock — counts down while it's hero's turn (loops, no auto-action)
// ─────────────────────────────────────────────────────────────
function ShotClock({ running, duration, resetKey, big = false }) {
  const [t, setT] = React.useState(duration);
  React.useEffect(() => { setT(duration); }, [resetKey, duration]);
  React.useEffect(() => {
    if (!running) return undefined;
    const id = setInterval(() => {
      setT((p) => (p <= 0.1 ? duration : r1(p - 0.1)));
    }, 100);
    return () => clearInterval(id);
  }, [running, resetKey, duration]);
  const danger = t <= 5 && running;
  return (
    <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 1 }}>
      <span style={{
        fontFamily: 'var(--op-font-display)', fontWeight: 700, fontSize: big ? 24 : 19, lineHeight: 1.1,
        color: danger ? 'var(--op-red-40)' : '#fff', fontVariantNumeric: 'tabular-nums',
        transition: 'color 0.3s',
      }}>{running ? t.toFixed(1) : duration.toFixed(1)}s</span>
      <span style={{ fontFamily: 'var(--op-font-ui)', fontWeight: 600, fontSize: 9, letterSpacing: '0.14em', color: 'rgba(255,255,255,0.45)' }}>SHOTCLOCK</span>
    </div>
  );
}

// ─────────────────────────────────────────────────────────────
// Seat panel — player plaque with HP, timebank, power-ups
// ─────────────────────────────────────────────────────────────
function SeatPanel({ who, name, badges, stack, hp, isTurn, showPower, compact = false, fx = true }) {
  const prevHp = React.useRef(hp);
  const [dmg, setDmg] = React.useState(null);
  React.useEffect(() => {
    const d = prevHp.current - hp;
    prevHp.current = hp;
    if (d > 0) {
      setDmg(d);
      const t = setTimeout(() => setDmg(null), 1300);
      return () => clearTimeout(t);
    }
    return undefined;
  }, [hp]);
  return (
    <div style={{
      background: 'rgba(255,255,255,0.045)', borderRadius: 16, boxSizing: 'border-box',
      border: isTurn ? '1px solid rgba(108,92,231,0.65)' : '1px solid rgba(255,255,255,0.08)',
      boxShadow: isTurn ? '0 0 0 3px rgba(108,92,231,0.18)' : 'none',
      padding: compact ? '6px 10px' : '9px 12px',
      display: 'flex', flexDirection: 'column', gap: compact ? 4 : 7,
      transition: 'border-color 0.3s, box-shadow 0.3s', minWidth: 0,
      position: 'relative',
      animation: fx && dmg ? 'panelShake 0.5s' : fx && isTurn ? 'seatBreath 2.4s ease-in-out infinite' : undefined,
    }}>
      {fx && dmg && (
        <span style={{
          position: 'absolute', top: -10, right: 12, zIndex: 5,
          fontFamily: 'var(--op-font-display)', fontWeight: 700, fontSize: 16, color: '#EC7265',
          textShadow: '0 0 12px rgba(231,76,60,0.7)', whiteSpace: 'nowrap',
          animation: 'dmgFloat 1.25s ease-out forwards',
        }}>&#8722;{dmg} HP</span>
      )}
      <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
        <span style={{ display: 'flex', animation: fx && who === 'geo' && isTurn ? 'bob 1.1s ease-in-out infinite' : undefined }}>
          <Avatar who={who} size={compact ? 26 : 34} />
        </span>
        <div style={{ display: 'flex', alignItems: 'center', gap: 6, minWidth: 0, flex: 1 }}>
          <span style={{
            fontFamily: 'var(--op-font-ui)', fontWeight: 700, fontSize: compact ? 12.5 : 14, color: '#fff',
            whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis',
          }}>{name}</span>
          {badges.map((b) => <Badge key={b.text} text={b.text} gold={b.gold} />)}
        </div>
        <span style={{ fontFamily: 'var(--op-font-display)', fontWeight: 700, fontSize: compact ? 13 : 15, color: '#fff', whiteSpace: 'nowrap' }}>
          {stack} bb
        </span>
      </div>
      <HPBar val={hp} h={compact ? 13 : 17} fontSize={compact ? 9.5 : 11} flash={fx && !!dmg} />
      {showPower
        ? <PowerBar val={0} max={10} compact={compact} />
        : <div style={{ display: 'flex', alignItems: 'center', gap: 6, height: compact ? 12 : 14 }}><Bolt size={compact ? 11 : 12} color="rgba(255,255,255,0.28)" /><span style={{ fontFamily: 'var(--op-font-ui)', fontSize: 10, color: 'rgba(255,255,255,0.3)', fontStyle: 'italic' }}>No power-ups</span></div>}
      <div style={{ display: 'flex', justifyContent: 'flex-end' }}>
        <TimebankPill value="60s" />
      </div>
    </div>
  );
}

// ─────────────────────────────────────────────────────────────
// Table — capsule felt with seats, board, pot
// ─────────────────────────────────────────────────────────────
function Table({ s, board, ring, landscape, cardW, fx = true, onFinish }) {
  const slots = 5 - board.length;
  const allin = s.heroChip === 'All-In' && !s.banner;
  const [flights, setFlights] = React.useState([]);
  const prevHero = React.useRef(s.heroChip);
  const prevGeo = React.useRef(s.geoChip);
  React.useEffect(() => {
    const isBet = (t) => t && /Bet|Call|All-In/.test(t);
    const spawn = (from) => {
      const key = `${from}-${Date.now()}`;
      setFlights((f) => [...f, { key, from }]);
      setTimeout(() => setFlights((f) => f.filter((x) => x.key !== key)), 950);
    };
    if (fx && s.heroChip !== prevHero.current && isBet(s.heroChip)) spawn('hero');
    if (fx && s.geoChip !== prevGeo.current && isBet(s.geoChip)) spawn('geo');
    prevHero.current = s.heroChip;
    prevGeo.current = s.geoChip;
  }, [s.heroChip, s.geoChip, fx]);
  return (
    <div style={{
      position: 'relative', flex: 1, minHeight: 0, borderRadius: 999,
      border: `2px solid ${ring}`,
      background: 'radial-gradient(ellipse at 50% 40%, #14161F 0%, #0B0C12 80%)',
      boxShadow: `0 0 40px rgba(0,0,0,0.5), inset 0 0 60px rgba(0,0,0,0.55)`,
      color: ring,
      animation: fx ? `ringPulse 3.2s ease-in-out infinite${allin ? ', tableImpact 0.5s 0.2s' : ''}` : undefined,
      display: 'flex', flexDirection: landscape ? 'row' : 'column', alignItems: 'center', justifyContent: 'space-between',
      padding: landscape ? '12px 70px' : '20px 24px', boxSizing: 'border-box', overflow: 'hidden',
    }}>
      <style>{`@keyframes duelPulse { 0%, 100% { opacity: 0.25; } 50% { opacity: 1; } }`}</style>
      {fx && (
        <div style={{
          position: 'absolute', inset: '12%', pointerEvents: 'none', borderRadius: 999,
          background: 'radial-gradient(closest-side, rgba(108,92,231,0.16), transparent 75%)',
          animation: 'feltBreath 4s ease-in-out infinite',
        }}></div>
      )}
      <div style={{
        position: 'absolute', inset: 12, borderRadius: 999, pointerEvents: 'none',
        border: '1px dashed rgba(108,92,231,0.30)',
      }}></div>

      {/* George (top) */}
      <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 6, position: 'relative', zIndex: 1 }}>
        <div style={{ display: 'flex', gap: 5 }}>
          {s.reveal
            ? DUEL.geoCards.map((c, i) => <PlayingCard key={c} code={c} w={cardW - 6} deal={fx} delay={i * 120} />)
            : [0, 1].map((i) => <CardBack key={i} w={cardW - 8} />)}
        </div>
        <div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
          <span style={{
            width: 20, height: 20, borderRadius: '50%', background: '#E8734A', color: '#fff',
            fontFamily: 'var(--op-font-ui)', fontWeight: 700, fontSize: 10,
            display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0,
          }}>D</span>
          {s.phase === 'george' && !s.geoChip && !s.banner
            ? <ThinkingDots />
            : <ActionChip text={s.geoChip} />}
        </div>
        {s.geoSay && !s.banner && <GeoBubble text={s.geoSay} below={landscape} />}
      </div>

      {/* board + pot */}
      <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: landscape ? 6 : 10, position: 'relative', zIndex: 1 }}>
        <div style={{ display: 'flex', alignItems: 'baseline', gap: 7 }}>
          <span style={{ fontFamily: 'var(--op-font-ui)', fontWeight: 700, fontSize: 10, letterSpacing: '0.16em', color: 'rgba(255,255,255,0.45)' }}>POT</span>
          <span key={fx ? `pot-${s.pot}` : 'pot'} style={{ display: 'inline-block', animation: fx ? 'potBump 0.35s' : undefined, whiteSpace: 'nowrap', fontFamily: 'var(--op-font-display)', fontWeight: 700, fontSize: 19, color: '#fff', fontVariantNumeric: 'tabular-nums' }}>{s.pot} bb</span>
        </div>
        <div style={{ display: 'flex', gap: 6 }}>
          {board.map((c, i) => <PlayingCard key={c} code={c} w={cardW} deal={fx} delay={i * 90} glow={fx && s.banner && s.banner.win && c === '5c'} />)}
          {Array.from({ length: slots }).map((_, i) => <CardSlot key={i} w={cardW} />)}
        </div>
      </div>

      {/* Hero (bottom) */}
      <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 6, position: 'relative', zIndex: 1 }}>
        <ActionChip text={s.heroChip} />
        <div style={{ display: 'flex', gap: 5 }}>
          {DUEL.heroCards.map((c) => <PlayingCard key={c} code={c} w={cardW + 6} lift glow={fx && s.banner && s.banner.win} />)}
        </div>
      </div>

      {/* chip flights */}
      {flights.map((f) => <ChipFlight key={f.key} from={f.from} landscape={landscape} />)}

      {/* all-in fx */}
      {fx && s.heroChip === 'All-In' && !s.banner && <AllInFX />}

      {/* result banner */}
      {s.banner && (
        <div style={{
          position: 'absolute', inset: 0, zIndex: 5, display: 'flex', alignItems: 'center', justifyContent: 'center',
          background: 'rgba(8,9,14,0.55)', backdropFilter: 'blur(3px)', borderRadius: 999,
        }}>
          {fx && s.banner.win && <Confetti />}
          <div style={{
            display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 4,
            background: '#16181F', border: `1.5px solid ${ring}`, borderRadius: 16,
            padding: '16px 26px',
            boxShadow: s.banner.win ? '0 16px 48px rgba(0,0,0,0.6), 0 0 40px rgba(233,182,99,0.3)' : '0 16px 48px rgba(0,0,0,0.6)',
            animation: fx ? 'bannerIn 0.45s cubic-bezier(0.2, 0.8, 0.3, 1) backwards' : undefined,
          }}>
            <span style={{ fontFamily: 'var(--op-font-display)', fontWeight: 700, fontSize: 19, color: '#fff', whiteSpace: 'nowrap' }}>{s.banner.title}</span>
            <span style={{
              fontFamily: 'var(--op-font-ui)', fontWeight: 700, fontSize: 13,
              color: s.banner.win ? 'var(--op-green-30)' : 'var(--op-red-40)',
            }}>{s.banner.sub}</span>
            {onFinish && (
              <button onClick={onFinish} style={{
                marginTop: 10, border: 'none', cursor: 'pointer',
                background: ring, color: '#fff', borderRadius: 999,
                padding: '7px 16px', fontFamily: 'var(--op-font-ui)', fontWeight: 700, fontSize: 12.5,
                boxShadow: '0 6px 16px rgba(0,0,0,0.4)', whiteSpace: 'nowrap',
              }}>See match results &#8250;</button>
            )}
          </div>
        </div>
      )}
    </div>
  );
}

// ─────────────────────────────────────────────────────────────
// Action buttons
// ─────────────────────────────────────────────────────────────
function ActionButton({ label, sub, color, hover, disabled, onClick, h = 58 }) {
  const [press, setPress] = React.useState(false);
  const [pop, setPop] = React.useState(false);
  const wasDisabled = React.useRef(disabled);
  React.useEffect(() => {
    const was = wasDisabled.current;
    wasDisabled.current = disabled;
    if (was && !disabled) {
      setPop(true);
      const t = setTimeout(() => setPop(false), 350);
      return () => clearTimeout(t);
    }
    return undefined;
  }, [disabled]);
  return (
    <button
      onClick={onClick}
      disabled={disabled}
      onPointerDown={() => setPress(true)}
      onPointerUp={() => setPress(false)}
      onPointerLeave={() => setPress(false)}
      style={{
        flex: 1, height: h, borderRadius: 14, border: 'none', cursor: disabled ? 'default' : 'pointer',
        background: press && !disabled ? hover : color,
        opacity: disabled ? 0.38 : 1,
        display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', gap: 1,
        transform: press && !disabled ? 'translateY(1px)' : 'none',
        animation: pop ? 'btnPop 0.3s' : undefined,
        transition: 'background 0.15s ease-out, opacity 0.25s, transform 0.1s',
        boxShadow: disabled ? 'none' : '0 6px 18px rgba(0,0,0,0.35)',
        padding: 0, minWidth: 0,
      }}>
      <span style={{ fontFamily: 'var(--op-font-display)', fontWeight: 600, fontSize: 16, color: '#1A1D23', lineHeight: 1.15 }}>{label}</span>
      {sub && <span style={{ whiteSpace: 'nowrap', fontFamily: 'var(--op-font-ui)', fontWeight: 700, fontSize: 12.5, color: 'rgba(26,29,35,0.75)', lineHeight: 1.1 }}>{sub}</span>}
    </button>
  );
}

function ActionRow({ s, heroAct, h = 58 }) {
  const myTurn = s.phase === 'hero' && !s.banner;
  const bet = r1(Math.max(1, s.pot / 3));
  const btns = s.mode === 'A'
    ? [
      { label: 'Check', sub: null, kind: 'check', color: 'var(--op-action-check)', hover: 'var(--op-action-check-hover)' },
      { label: 'Bet', sub: `${bet} bb`, kind: 'bet', color: 'var(--op-bet-2)', hover: 'var(--op-bet-2-hover)' },
      { label: 'All-In', sub: `${s.hero} bb`, kind: 'allin', color: 'var(--op-all-in)', hover: 'var(--op-all-in-hover)' },
    ]
    : [
      { label: 'Fold', sub: null, kind: 'fold', color: 'var(--op-action-fold)', hover: 'var(--op-action-fold-hover)' },
      { label: 'Call', sub: `${s.callAmt} bb`, kind: 'call', color: 'var(--op-action-check)', hover: 'var(--op-action-check-hover)' },
      { label: 'All-In', sub: `${s.hero} bb`, kind: 'allin', color: 'var(--op-all-in)', hover: 'var(--op-all-in-hover)' },
    ];
  return (
    <div style={{ display: 'flex', gap: 10, width: '100%' }}>
      {btns.map((b) => (
        <ActionButton key={b.kind} label={b.label} sub={b.sub} color={b.color} hover={b.hover}
          disabled={!myTurn} onClick={() => heroAct(b.kind)} h={h} />
      ))}
    </div>
  );
}

function StatusLabel({ s }) {
  const yours = s.phase === 'hero' && !s.banner;
  return (
    <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 8 }}>
      <span style={{
        width: 7, height: 7, borderRadius: '50%', flexShrink: 0,
        background: yours ? 'var(--op-green-30)' : 'rgba(255,255,255,0.25)',
        boxShadow: yours ? '0 0 8px rgba(77,219,138,0.8)' : 'none', transition: 'all 0.3s',
        animation: yours ? 'dotPulse 1.4s ease-in-out infinite' : undefined,
      }}></span>
      <span style={{
        fontFamily: 'var(--op-font-ui)', fontWeight: 700, fontSize: 12, letterSpacing: '0.18em',
        color: yours ? '#fff' : 'rgba(255,255,255,0.45)', transition: 'color 0.3s',
      }}>{s.banner ? 'HAND OVER' : yours ? 'YOUR TURN' : "GEORGE'S TURN"}</span>
    </div>
  );
}

function RoundLabel({ mult, center = false }) {
  return (
    <div style={{ display: 'flex', flexDirection: 'column', alignItems: center ? 'center' : 'flex-start', gap: 1 }}>
      <span style={{ whiteSpace: 'nowrap', fontFamily: 'var(--op-font-display)', fontWeight: 700, fontSize: 18, lineHeight: 1.1, color: 'var(--op-exodus-50)', letterSpacing: '0.04em' }}>ROUND 1</span>
      <span style={{ whiteSpace: 'nowrap', fontFamily: 'var(--op-font-ui)', fontWeight: 700, fontSize: 10, letterSpacing: '0.2em', color: 'rgba(255,255,255,0.7)' }}>{mult}&#215; DAMAGE</span>
    </div>
  );
}

// ─────────────────────────────────────────────────────────────
// DuelScreen — root, portrait & landscape layouts
// ─────────────────────────────────────────────────────────────
function DuelScreen({ landscape = false, cfg }) {
  const { ring, shot, mult, power } = cfg;
  const fx = cfg.fx !== false;
  const onExit = cfg.onExit;
  const onFinish = cfg.onFinish;
  const { s, board, heroAct } = useDuel(mult);
  const myTurn = s.phase === 'hero' && !s.banner;

  const bg = 'radial-gradient(ellipse 120% 90% at 50% 0%, #1D2030 0%, #12131D 45%, #0B0C13 100%)';

  if (landscape) {
    return (
      <div style={{
        height: '100%', boxSizing: 'border-box', background: bg, color: '#fff',
        display: 'flex', flexDirection: 'column', gap: 8, position: 'relative',
        padding: '14px 24px 14px 58px', fontFamily: 'var(--op-font-ui)',
      }}>
        <DuelFX />
        {/* top: opponent + round + clock */}
        <div style={{ display: 'flex', alignItems: 'center', gap: 16 }}>
          {onExit && <div style={{ position: 'absolute', top: 12, left: 14, zIndex: 20 }}><FlowBack onClick={onExit} /></div>}
          <div style={{ width: 320, flexShrink: 0 }}>
            <SeatPanel compact who="geo" name="George" badges={[{ text: 'BN', gold: true }]} stack={s.geo} hp={s.hp2} isTurn={!myTurn && !s.banner} showPower={power} fx={fx} />
          </div>
          <div style={{ flex: 1, display: 'flex', justifyContent: 'center' }}>
            <RoundLabel mult={mult} center />
          </div>
          <div style={{ width: 320, flexShrink: 0, display: 'flex', justifyContent: 'flex-end' }}>
            <ShotClock running={myTurn} duration={shot} resetKey={s.clockKey} big />
          </div>
        </div>

        <Table s={s} board={board} ring={ring} landscape cardW={44} fx={fx} onFinish={onFinish} />

        {/* bottom: hero + status + actions */}
        <div style={{ display: 'flex', alignItems: 'center', gap: 16 }}>
          <div style={{ width: 320, flexShrink: 0 }}>
            <SeatPanel compact who="hero" name="You" badges={[{ text: 'UTG8' }]} stack={s.hero} hp={s.hp1} isTurn={myTurn} showPower={power} fx={fx} />
          </div>
          <div style={{ flex: 1, display: 'flex', justifyContent: 'center' }}>
            <StatusLabel s={s} />
          </div>
          <div style={{ width: 320, flexShrink: 0 }}>
            <ActionRow s={s} heroAct={heroAct} h={52} />
          </div>
        </div>
      </div>
    );
  }

  return (
    <div style={{
      height: '100%', boxSizing: 'border-box', background: bg, color: '#fff',
      display: 'flex', flexDirection: 'column', gap: 10, position: 'relative',
      padding: '64px 14px 40px', fontFamily: 'var(--op-font-ui)',
    }}>
      <DuelFX />
      {/* header */}
      <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', padding: '0 4px' }}>
        <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
          {onExit && <FlowBack onClick={onExit} />}
          <RoundLabel mult={mult} />
        </div>
        <ShotClock running={myTurn} duration={shot} resetKey={s.clockKey} big />
      </div>

      <SeatPanel who="geo" name="George" badges={[{ text: 'BN', gold: true }]} stack={s.geo} hp={s.hp2} isTurn={!myTurn && !s.banner} showPower={power} fx={fx} />

      <Table s={s} board={board} ring={ring} cardW={48} fx={fx} onFinish={onFinish} />

      <SeatPanel who="hero" name="You" badges={[{ text: 'UTG8' }]} stack={s.hero} hp={s.hp1} isTurn={myTurn} showPower={power} fx={fx} />

      <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
        <StatusLabel s={s} />
        <ActionRow s={s} heroAct={heroAct} />
      </div>
    </div>
  );
}

function FlowBack({ onClick }) {
  return (
    <button onClick={onClick} aria-label="Back" style={{
      width: 34, height: 34, borderRadius: 999, flexShrink: 0, cursor: 'pointer',
      background: 'rgba(255,255,255,0.06)', border: '1px solid rgba(255,255,255,0.12)',
      color: '#fff', display: 'flex', alignItems: 'center', justifyContent: 'center', padding: 0,
    }}>
      <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.2" strokeLinecap="round" strokeLinejoin="round"><path d="M15 18l-6-6 6-6" /></svg>
    </button>
  );
}

Object.assign(window, { DuelScreen });
