// ============================================================
// HUNGRY HOLE 3D — UI kit: bezel, icons, chips, stars, primitives
// ============================================================

// ---- icon set (rounded, friendly, 1.8 stroke) ----
const I = {
  back:  (p) => <svg viewBox="0 0 24 24" width="24" height="24" {...p}><path d="M15 5l-7 7 7 7" fill="none" stroke="currentColor" strokeWidth="2.2" strokeLinecap="round" strokeLinejoin="round"/></svg>,
  close: (p) => <svg viewBox="0 0 24 24" width="24" height="24" {...p}><path d="M6 6l12 12M18 6L6 18" fill="none" stroke="currentColor" strokeWidth="2.2" strokeLinecap="round"/></svg>,
  gear:  (p) => <svg viewBox="0 0 24 24" width="22" height="22" {...p}><path d="M13.6 2.6c.5.1.9.5 1 1l.2 1.5c.6.2 1.2.5 1.7 1l1.4-.6c.5-.2 1.1 0 1.4.4l1.6 2.6c.3.5.2 1.1-.2 1.4l-1.2 1c0 .3.1.6.1 1s0 .7-.1 1l1.2 1c.4.4.5 1 .2 1.4l-1.6 2.6c-.3.5-.9.6-1.4.4l-1.4-.6c-.5.4-1.1.7-1.7 1l-.2 1.5c-.1.5-.5.9-1 1h-3.2c-.5-.1-.9-.5-1-1l-.2-1.5c-.6-.2-1.2-.5-1.7-1l-1.4.6c-.5.2-1.1 0-1.4-.4l-1.6-2.6c-.3-.5-.2-1.1.2-1.4l1.2-1c0-.3-.1-.6-.1-1s0-.7.1-1l-1.2-1c-.4-.4-.5-1-.2-1.4l1.6-2.6c.3-.5.9-.6 1.4-.4l1.4.6c.5-.4 1.1-.7 1.7-1l.2-1.5c.1-.5.5-.9 1-1z" fill="currentColor" opacity="0.92"/><circle cx="12" cy="12" r="3.4" fill="#fff"/></svg>,
  pause: (p) => <svg viewBox="0 0 24 24" width="22" height="22" {...p}><rect x="6.6" y="5" width="4.2" height="14" rx="2.1" fill="currentColor"/><rect x="13.2" y="5" width="4.2" height="14" rx="2.1" fill="currentColor"/></svg>,
  play:  (p) => <svg viewBox="0 0 24 24" width="22" height="22" {...p}><path d="M8 5.5l11 6.5-11 6.5z" fill="currentColor"/></svg>,
  star:  (p) => <svg viewBox="0 0 24 24" width="22" height="22" {...p}><path d="M12 2.6l2.9 5.9 6.5.95-4.7 4.6 1.1 6.45L12 17.9l-5.8 3.05 1.1-6.45-4.7-4.6 6.5-.95z" fill="currentColor"/></svg>,
  lock:  (p) => <svg viewBox="0 0 24 24" width="18" height="18" {...p}><rect x="5" y="10.5" width="14" height="9.5" rx="3" fill="currentColor"/><path d="M8 10.5V8a4 4 0 018 0v2.5" fill="none" stroke="currentColor" strokeWidth="2"/></svg>,
  check: (p) => <svg viewBox="0 0 24 24" width="18" height="18" {...p}><path d="M5 12.5l4.5 4.5L19 7" fill="none" stroke="currentColor" strokeWidth="2.6" strokeLinecap="round" strokeLinejoin="round"/></svg>,
  ad:    (p) => <svg viewBox="0 0 24 24" width="20" height="20" {...p}><rect x="3" y="5" width="18" height="14" rx="4" fill="none" stroke="currentColor" strokeWidth="2"/><path d="M10.5 9.5l4 2.5-4 2.5z" fill="currentColor"/></svg>,
  share: (p) => <svg viewBox="0 0 24 24" width="20" height="20" {...p}><circle cx="6" cy="12" r="2.6" fill="currentColor"/><circle cx="17" cy="6" r="2.6" fill="currentColor"/><circle cx="17" cy="18" r="2.6" fill="currentColor"/><path d="M8.2 11l6.6-3.8M8.2 13l6.6 3.8" stroke="currentColor" strokeWidth="2"/></svg>,
  retry: (p) => <svg viewBox="0 0 24 24" width="22" height="22" {...p}><path d="M19 12a7 7 0 11-2-4.9M19 4v4h-4" fill="none" stroke="currentColor" strokeWidth="2.2" strokeLinecap="round" strokeLinejoin="round"/></svg>,
  map:   (p) => <svg viewBox="0 0 24 24" width="22" height="22" {...p}><path d="M9 4L4 6v14l5-2 6 2 5-2V4l-5 2-6-2z" fill="currentColor" fillOpacity="0.12" stroke="currentColor" strokeWidth="2" strokeLinejoin="round"/><path d="M9 4v14M15 6v14" stroke="currentColor" strokeWidth="2"/><circle cx="12" cy="10.5" r="1.7" fill="currentColor" stroke="none"/></svg>,
  bag:   (p) => <svg viewBox="0 0 24 24" width="22" height="22" {...p}><path d="M6 8h12l-1 12H7zM9 8V6a3 3 0 016 0v2" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/></svg>,
  gift:  (p) => <svg viewBox="0 0 24 24" width="22" height="22" {...p}><rect x="4" y="9" width="16" height="11" rx="2" fill="none" stroke="currentColor" strokeWidth="2"/><path d="M4 13h16M12 9v11M12 9S9 4 7 6s5 3 5 3zM12 9s3-5 5-3-5 3-5 3z" fill="none" stroke="currentColor" strokeWidth="2" strokeLinejoin="round"/></svg>,
  target:(p) => <svg viewBox="0 0 24 24" width="22" height="22" {...p}><circle cx="12" cy="12" r="8.5" fill="none" stroke="currentColor" strokeWidth="2"/><circle cx="12" cy="12" r="4" fill="none" stroke="currentColor" strokeWidth="2"/><circle cx="12" cy="12" r="1.2" fill="currentColor"/></svg>,
  sound: (p) => <svg viewBox="0 0 24 24" width="22" height="22" {...p}><path d="M5 9.5h3l4-3.5v12l-4-3.5H5z" fill="none" stroke="currentColor" strokeWidth="2" strokeLinejoin="round"/><path d="M16 9a4 4 0 010 6" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round"/></svg>,
  phone: (p) => <svg viewBox="0 0 24 24" width="22" height="22" {...p}><rect x="7" y="3.5" width="10" height="17" rx="3" fill="none" stroke="currentColor" strokeWidth="2"/><path d="M4 9v6M20 9v6" stroke="currentColor" strokeWidth="2" strokeLinecap="round"/></svg>,
  flag:  (p) => <svg viewBox="0 0 24 24" width="20" height="20" {...p}><path d="M6 21V4M6 5h11l-2 4 2 4H6" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/></svg>,
  bolt:  (p) => <svg viewBox="0 0 24 24" width="20" height="20" {...p}><path d="M13 2L4 14h7l-1 8 9-12h-7z" fill="currentColor"/></svg>,
  /* clean cartoon crown (replaces the 👑 emoji on boss nodes) — gold body + 3 jewel dots */
  crown: (p) => <svg viewBox="0 0 24 24" width="20" height="20" {...p}><path d="M3 18.5l-1.4-9.7 5.4 4.2L12 4.5l5 8.5 5.4-4.2L21 18.5z" fill="currentColor" stroke="rgba(120,80,0,0.35)" strokeWidth="0.8" strokeLinejoin="round"/><path d="M3.2 19.4h17.6" stroke="rgba(120,80,0,0.4)" strokeWidth="1.6" strokeLinecap="round"/><circle cx="6.6" cy="14.4" r="1" fill="#fff" opacity="0.85"/><circle cx="12" cy="12.4" r="1.1" fill="#fff" opacity="0.9"/><circle cx="17.4" cy="14.4" r="1" fill="#fff" opacity="0.85"/></svg>,
  /* clean cartoon skull — dark fill with cut-out eyes/nose & jaw notches (no emoji) */
  skull: (p) => <svg viewBox="0 0 24 24" width="18" height="18" {...p}><path fillRule="evenodd" clipRule="evenodd" d="M12 2.6c-4.7 0-7.9 3.2-7.9 7.7 0 2.3 1 4.1 2.4 5.3.4.3.5.6.5 1v1.2c0 .9.7 1.6 1.6 1.6h.3v-1.7a.65.65 0 011.3 0v1.7h1.1v-1.7a.65.65 0 011.3 0v1.7h1.1v-1.7a.65.65 0 011.3 0v1.7h.3c.9 0 1.6-.7 1.6-1.6v-1.2c0-.4.1-.7.5-1 1.4-1.2 2.4-3 2.4-5.3 0-4.5-3.2-7.7-7.9-7.7zM8.7 8.5a2.05 2.05 0 100 4.1 2.05 2.05 0 000-4.1zm6.6 0a2.05 2.05 0 100 4.1 2.05 2.05 0 000-4.1zM12 13.4c-.55 0-1 .55-1 1.2s.45 1.2 1 1.2 1-.55 1-1.2-.45-1.2-1-1.2z" fill="currentColor"/></svg>,
  /* friendly tap/drag hand — filled cartoon hand with a soft motion arc (replaces the 👆 emoji) */
  drag:  (p) => <svg viewBox="0 0 24 24" width="20" height="20" {...p}><path d="M8.8 12.4V6.1a1.55 1.55 0 013.1 0v4.1a1.45 1.45 0 012.9 0v.7a1.45 1.45 0 012.9 0v.6a1.45 1.45 0 012.9 0v3.6c0 3.3-2.4 5.7-5.7 5.7H16c-1.8 0-3.4-.8-4.5-2.3l-2.6-3.6c-.5-.7-.3-1.7.5-2.1.6-.3 1.4-.2 1.9.3l1.2 1.3z" fill="currentColor"/><path d="M5.4 8.2a4.4 4.4 0 017.6-2.6" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" opacity="0.55"/><path d="M4.7 5.4l.6 2.9 2.8-.8" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" opacity="0.55"/></svg>,
};

// goal-type glyph
function GoalGlyph({ id, size = 20, color = 'currentColor' }) {
  const p = { width: size, height: size, viewBox: '0 0 24 24', fill: 'none', stroke: color, strokeWidth: 2, strokeLinecap: 'round', strokeLinejoin: 'round' };
  switch (id) {
    case 'sweep': return <svg {...p}><path d="M3 21l6-6M9 15l3-7 4 4-7 3M13 8l5-5 3 3-5 5"/></svg>;
    case 'score': return <svg {...p}><path d="M12 3v18M4 8h16M4 16h16"/></svg>;
    case 'treasure': return <svg {...p}><path d="M12 2.6l2.9 5.9 6.5.95-4.7 4.6 1.1 6.45L12 17.9l-5.8 3.05 1.1-6.45-4.7-4.6 6.5-.95z" fill={color} stroke="none"/></svg>;
    case 'combo': return <svg {...p}><path d="M13 2L4 14h7l-1 8 9-12h-7z" fill={color} stroke="none"/></svg>;
    case 'bonus': return <svg {...p}><path d="M12 21s-7-4.5-7-10a4 4 0 017-2.5A4 4 0 0119 11c0 5.5-7 10-7 10z" fill={color} stroke="none"/></svg>;
    case 'avoid': return <svg {...p}><circle cx="12" cy="12" r="9"/><path d="M6 6l12 12"/></svg>;
    case 'rival': return <svg {...p}><circle cx="7" cy="9" r="3"/><circle cx="17" cy="9" r="3"/><path d="M2 20c0-3 2.5-5 5-5s5 2 5 5M12 20c0-3 2.5-5 5-5s5 2 5 5"/></svg>;
    case 'precision': return <svg {...p}><circle cx="12" cy="12" r="8.5"/><circle cx="12" cy="12" r="1.4" fill={color}/><path d="M12 1.5v3M12 19.5v3M1.5 12h3M19.5 12h3"/></svg>;
    case 'mega': return <svg {...p}><path d="M4 20V8l8-5 8 5v12M9 20v-6h6v6"/></svg>;
    case 'boss': return <svg {...p}><path d="M5 21V8l3-4 4 3 4-3 3 4v13M9 21v-5h6v5"/><circle cx="12" cy="12" r="1.5" fill={color}/></svg>;
    default: return <svg {...p}><circle cx="12" cy="12" r="9"/></svg>;
  }
}

// coin & gem
function Coin({ size = 18 }) {
  return <img src="assets/ui-ai/icon-coin-3d.webp" alt="" style={{ width: size, height: size, objectFit: 'contain', display: 'block',
    filter: 'drop-shadow(0 2px 2px rgba(0,0,0,0.22))' }} />;
  /*
  // bright hole.io HUD coin: vivid gold dome, glossy rim, big specular glint + $ face
  return <svg viewBox="0 0 24 24" width={size} height={size}>
    <defs>
      <radialGradient id="hh-coin-face" cx="0.38" cy="0.32" r="0.85">
        <stop offset="0" stopColor="#FFF3B0"/><stop offset="0.45" stopColor="#FFD23C"/><stop offset="1" stopColor="#F39A12"/>
      </radialGradient>
      <linearGradient id="hh-coin-rim" x1="0" y1="0" x2="0" y2="1">
        <stop offset="0" stopColor="#FFD75E"/><stop offset="1" stopColor="#D27E0A"/>
      </linearGradient>
    </defs>
    <circle cx="12" cy="12" r="11.4" fill="url(#hh-coin-rim)"/>
    <circle cx="12" cy="12" r="11.4" fill="none" stroke="#B96F08" strokeOpacity="0.5" strokeWidth="0.7"/>
    <circle cx="12" cy="12" r="8.7" fill="url(#hh-coin-face)"/>
    <circle cx="12" cy="12" r="8.7" fill="none" stroke="#fff" strokeOpacity="0.6" strokeWidth="0.9"/>
    <path d="M5.6 8a8.6 8.6 0 016.6-3" fill="none" stroke="#fff" strokeOpacity="0.9" strokeWidth="1.8" strokeLinecap="round"/>
    <ellipse cx="9" cy="7.6" rx="1.7" ry="1.1" fill="#fff" opacity="0.8" transform="rotate(-30 9 7.6)"/>
    <text x="12" y="16.5" textAnchor="middle" fontFamily="Fredoka,sans-serif" fontWeight="800" fontSize="12" fill="#9C5C04">$</text>
  </svg>;
  */
}
function Gem({ size = 18 }) {
  return <img src="assets/ui-ai/icon-gem-3d.webp" alt="" style={{ width: size, height: size, objectFit: 'contain', display: 'block',
    filter: 'drop-shadow(0 2px 2px rgba(0,0,0,0.22))' }} />;
  /*
  // faceted grape gem: bright top facets, deep base, glossy highlight streak
  return <svg viewBox="0 0 24 24" width={size} height={size}>
    <defs>
      <linearGradient id="hh-gem-b" x1="0" y1="0" x2="0" y2="1">
        <stop offset="0" stopColor="#9B7BFF"/><stop offset="1" stopColor="#6A45F0"/>
      </linearGradient>
    </defs>
    <path d="M6 3h12l4 6-10 12L2 9z" fill="url(#hh-gem-b)"/>
    <path d="M6 3l-4 6h20l-4-6z" fill="#B6A0FF"/>
    <path d="M12 21L2 9h20z" fill="#6A45F0"/>
    <path d="M6 3l3 6-3 0zM18 3l-3 6 3 0z" fill="#D6CBFF" opacity="0.85"/>
    <path d="M2 9h20" stroke="#fff" strokeOpacity="0.45" strokeWidth="0.8"/>
    <path d="M9 9l3 12 3-12" fill="none" stroke="#fff" strokeOpacity="0.35" strokeWidth="0.8"/>
    <path d="M7.5 4.4l1.6 3" stroke="#fff" strokeOpacity="0.7" strokeWidth="1.1" strokeLinecap="round"/>
  </svg>;
  */
}
function CurChip({ kind, value, onClick }) {
  // premium warm-tinted pill: glossy top sheen, colored hairline, springy + badge.
  const gem = kind === 'gem';
  const tint = gem ? 'rgba(124,92,252,0.12)' : 'rgba(255,182,39,0.16)';
  const edge = gem ? 'rgba(124,92,252,0.38)' : 'rgba(232,165,22,0.48)';
  return <button onClick={onClick} className="chip hh-curchip" style={{ position: 'relative', overflow: 'hidden',
    cursor: onClick ? 'pointer' : 'default', padding: '7px 8px 7px 11px',
    background: `linear-gradient(180deg, #fff, ${tint})`, border: `1px solid ${edge}`,
    boxShadow: `0 2px 0 rgba(27,39,64,0.07), 0 0 0 1px rgba(255,255,255,0.6) inset, var(--shadow)` }}>
    {/* glossy top sheen */}
    <span aria-hidden style={{ position: 'absolute', top: 0, left: 0, right: 0, height: '46%',
      background: 'linear-gradient(180deg, rgba(255,255,255,0.7), rgba(255,255,255,0))', pointerEvents: 'none', borderRadius: '100px 100px 0 0' }} />
    {gem ? <Gem size={24} /> : <Coin size={24} />}
    <span className="num" style={{ minWidth: 30, textAlign: 'left', position: 'relative' }}>{value.toLocaleString()}</span>
    {onClick && <span style={{ width: 24, height: 24, borderRadius: '50%', color: '#fff', display: 'grid', placeItems: 'center', fontWeight: 800, fontSize: 16, lineHeight: 1, position: 'relative',
      background: gem ? 'linear-gradient(180deg,#9B7BFF,#7C5CFC)' : 'linear-gradient(180deg,#FFC94F,#E8A516)',
      boxShadow: `0 2px 5px ${gem ? 'rgba(124,92,252,0.4)' : 'rgba(232,165,22,0.45)'}, inset 0 1px 0 rgba(255,255,255,0.55), inset 0 -1px 0 rgba(0,0,0,0.12)` }}>
      <img src="assets/ui-ai/icon-plus-3d.webp" alt="" style={{ width: 22, height: 22, objectFit: 'contain' }} /></span>}
  </button>;
}

// stars (1..3 of total, with empty)
function Stars({ n, of = 3, size = 22, gap = 3, stamp = false }) {
  return <div style={{ display: 'flex', gap }}>
    {Array.from({ length: of }).map((_, i) => (
      <span key={i} className={stamp && i < n ? 'hh-anim' : ''} style={{ color: i < n ? 'var(--sun)' : 'rgba(27,39,64,0.16)',
        filter: i < n ? 'drop-shadow(0 2px 4px rgba(255,182,39,0.5))' : 'none',
        animation: stamp && i < n ? `hh-stamp .44s cubic-bezier(.34,1.56,.64,1) ${0.15 + i * 0.18}s both` : 'none', display: 'inline-flex' }}>
        <I.star width={size} height={size} />
      </span>
    ))}
  </div>;
}

// goal chip
function GoalChip({ goal, dark = false }) {
  return <div style={{ display: 'inline-flex', alignItems: 'center', gap: 8, padding: '8px 14px 8px 10px', borderRadius: 100,
    background: dark ? 'rgba(27,39,64,0.55)' : '#fff', color: dark ? '#fff' : 'var(--ink)', border: dark ? 'none' : '1px solid var(--line)',
    boxShadow: dark ? 'none' : 'var(--shadow)', backdropFilter: dark ? 'blur(8px)' : 'none' }}>
    <span style={{ width: 28, height: 28, borderRadius: '50%', background: goal.color, color: '#fff', display: 'grid', placeItems: 'center', flexShrink: 0 }}>
      <GoalGlyph id={goal.id} size={17} color="#fff" />
    </span>
    <span style={{ fontWeight: 800, fontSize: 13.5 }}>{goal.label}</span>
  </div>;
}

// timer ring — hole.io HUD chip: small green clock dot + mm:ss in a navy pill
function TimerRing({ frac, label, size = 30, color = 'var(--mint)' }) {
  const f = Math.max(0, Math.min(1, frac == null ? 1 : frac));
  const r = size / 2 - 3, circ = 2 * Math.PI * r;
  // when time runs low the ring shifts to coral as a warning
  const ringCol = f < 0.18 ? 'var(--coral)' : color;
  return <div style={{ display: 'inline-flex', alignItems: 'center', gap: 8, padding: '5px 13px 5px 6px', borderRadius: 100,
    background: 'rgba(20,30,52,0.62)', border: '1px solid rgba(255,255,255,0.12)', backdropFilter: 'blur(6px)',
    boxShadow: '0 4px 12px rgba(0,0,0,0.25)' }}>
    <div style={{ position: 'relative', width: size, height: size, flexShrink: 0 }}>
      <svg width={size} height={size} style={{ transform: 'rotate(-90deg)', display: 'block' }}>
        <circle cx={size/2} cy={size/2} r={r} fill="rgba(255,255,255,0.96)" stroke="rgba(20,30,52,0.35)" strokeWidth="3"/>
        <circle cx={size/2} cy={size/2} r={r} fill="none" stroke={ringCol} strokeWidth="3" strokeLinecap="round"
          strokeDasharray={circ} strokeDashoffset={circ * (1 - f)} style={{ transition: 'stroke-dashoffset .3s linear, stroke .3s' }}/>
      </svg>
      {/* clock hands */}
      <svg width={size} height={size} viewBox="0 0 24 24" style={{ position: 'absolute', inset: 0 }}>
        <path d="M12 7.5V12l3 2" fill="none" stroke={ringCol} strokeWidth="2.2" strokeLinecap="round" strokeLinejoin="round"/>
      </svg>
    </div>
    <div className="num" style={{ fontWeight: 700, fontSize: 16, color: '#fff', letterSpacing: 0.2,
      textShadow: '0 1px 2px rgba(0,0,0,0.45)' }}>{label}</div>
  </div>;
}

function IconBtn({ children, onClick, light = false, style }) {
  // 50px thumb target; true 3D candy face — glossy white top, thick navy bottom edge, inner sheen,
  // and a real "sink onto the shadow" press (via .hh-iconbtn:active in CSS). High-standard, not flat.
  return <button onClick={onClick} className="hh-iconbtn" style={{ width: 50, height: 50, borderRadius: 17, display: 'grid', placeItems: 'center', cursor: 'pointer',
    background: light ? 'linear-gradient(180deg,#ffffff 0%,#eaf1fb 100%)' : 'linear-gradient(180deg, var(--tile-bg), var(--card))',
    border: '1.5px solid rgba(255,255,255,0.95)', color: 'var(--ink)',
    boxShadow: '0 5px 0 rgba(20,32,58,0.16), 0 9px 18px rgba(20,32,58,0.24), inset 0 2px 0 rgba(255,255,255,0.95), inset 0 -3px 6px rgba(20,32,58,0.10)',
    transition: 'transform .1s cubic-bezier(.2,.8,.3,1), box-shadow .1s cubic-bezier(.2,.8,.3,1)', ...style }}>{children}</button>;
}

function AIIcon({ id, size = 32, style }) {
  return <img src={`assets/ui-ai/icon-${id}.webp`} alt="" style={{ width: size, height: size, objectFit: 'contain', display: 'block',
    filter: 'drop-shadow(0 3px 4px rgba(0,0,0,0.28))', ...style }} />;
}

function Bar({ v, color = 'var(--sky)', h = 8, track = 'var(--line)' }) {
  const pct = Math.max(0, Math.min(1, v || 0)) * 100;
  return <div style={{ height: h, borderRadius: 100, background: track, overflow: 'hidden', boxShadow: 'inset 0 2px 3px rgba(27,39,64,0.18), inset 0 0 0 1px rgba(255,255,255,0.06)' }}>
    {/* glossy 3D fill: a translucent top-sheen → bottom-shade gradient layered over the colour */}
    <div style={{ width: `${pct}%`, height: '100%', borderRadius: 100,
      background: `linear-gradient(180deg, rgba(255,255,255,0.5) 0%, rgba(255,255,255,0.08) 46%, rgba(0,0,0,0.12) 100%), ${color}`,
      boxShadow: pct > 0 ? 'inset 0 1px 0 rgba(255,255,255,0.6)' : 'none',
      transition: 'width .4s cubic-bezier(.22,1,.36,1)' }} />
  </div>;
}

// ---- phone bezel ----
function Bezel({ children }) {
  return <div style={{ width: 392, height: 844, borderRadius: 52, padding: 9,
    background: 'linear-gradient(150deg,#2c3357,#11152a)', boxShadow: '0 40px 110px rgba(0,0,0,0.55), inset 0 0 2px rgba(255,255,255,0.4)' }}>
    <div style={{ position: 'relative', width: '100%', height: '100%', borderRadius: 44, overflow: 'hidden', background: '#0a0712', border: '2px solid #000' }}>
      {children}
      {/* Android punch-hole camera */}
      <div style={{ position: 'absolute', top: 10, left: '50%', transform: 'translateX(-50%)', width: 14, height: 14, borderRadius: '50%', background: '#000', zIndex: 96, pointerEvents: 'none', boxShadow: 'inset 0 0 3px rgba(80,110,160,0.8), 0 0 0 2px rgba(0,0,0,0.6)' }} />
      <div style={{ position: 'absolute', bottom: 7, left: '50%', transform: 'translateX(-50%)', width: 124, height: 5, borderRadius: 3, background: 'rgba(255,255,255,0.5)', zIndex: 95, pointerEvents: 'none' }} />
    </div>
  </div>;
}

// Real device: the OS draws the actual status bar. Never render fake time/battery/camera chrome.
function StatusBar() { return null; }

// top bar for menu screens
function TopBar({ title, sub, onBack, right }) {
  return <div style={{ display: 'flex', alignItems: 'center', gap: 12, padding: 'max(8px, env(safe-area-inset-top)) 18px 12px' }}>
    {onBack && <IconBtn onClick={onBack}><I.back /></IconBtn>}
    <div style={{ flex: 1 }}>
      {sub && <div className="eyebrow" style={{ marginBottom: 2 }}>{sub}</div>}
      <h1 className="disp" style={{ margin: 0, fontSize: 26, lineHeight: 1 }}>{title}</h1>
    </div>
    {right}
  </div>;
}

function Label({ children, style }) { return <div className="eyebrow" style={{ margin: '20px 4px 10px', ...style }}>{children}</div>; }

// ============================================================
// NEW shared components (hole.io look) — see brief §3 contract
// ============================================================

// Under-hole "Size n" light pill: white capsule, blue dot, bold Fredoka number.
// Reusable as a generic value pill (label = "Size", "x3", "Lv 4"…).
function GoalPill({ size, label = 'Size' }) {
  return <div style={{ position: 'relative', overflow: 'hidden', display: 'inline-flex', alignItems: 'center', gap: 8, padding: '8px 16px 8px 10px', borderRadius: 100,
    background: 'linear-gradient(180deg,#ffffff 0%,#eef4fc 100%)', border: '1.5px solid rgba(255,255,255,0.95)',
    boxShadow: '0 4px 0 rgba(22,34,60,0.12), 0 8px 18px rgba(22,34,60,0.26), inset 0 2px 0 rgba(255,255,255,0.95)' }}>
    <span aria-hidden style={{ position: 'absolute', top: 0, left: 0, right: 0, height: '46%', background: 'linear-gradient(180deg,rgba(255,255,255,0.7),rgba(255,255,255,0))', pointerEvents: 'none' }} />
    <span style={{ width: 16, height: 16, borderRadius: '50%', flexShrink: 0, position: 'relative',
      background: 'radial-gradient(circle at 35% 28%, #6FD0FF, var(--sky))',
      boxShadow: '0 2px 4px rgba(34,166,242,0.6), inset 0 1.5px 1.5px rgba(255,255,255,0.85), inset 0 -1px 2px rgba(13,90,150,0.4)' }} />
    <span style={{ fontFamily: 'var(--ui)', fontWeight: 800, fontSize: 14, color: 'var(--ink)', letterSpacing: 0.2, position: 'relative' }}>
      {label}{size != null && ' '}<span className="num" style={{ fontWeight: 700, fontSize: 16 }}>{size != null ? size : ''}</span>
    </span>
  </div>;
}

// hole.io goal bar: translucent track, green→yellow gradient fill, a sparkle/star
// riding the leading edge, and a value/target fraction. Clamps safely.
function ProgressBar({ value, target, h = 16 }) {
  const tgt = Math.max(1, Number(target) || 1);
  const val = Math.max(0, Math.min(tgt, Number(value) || 0));
  const frac = val / tgt;
  const pct = frac * 100;
  const near = frac >= 0.001; // show sparkle once any progress exists
  return <div style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>
    <div style={{ position: 'relative', height: h, borderRadius: 100,
      background: 'rgba(16,24,42,0.40)', border: '1px solid rgba(255,255,255,0.18)',
      boxShadow: 'inset 0 2px 4px rgba(0,0,0,0.28)', overflow: 'visible' }}>
      <div style={{ position: 'absolute', inset: 2, borderRadius: 100, overflow: 'hidden' }}>
        <div style={{ width: `${pct}%`, height: '100%', borderRadius: 100,
          background: 'linear-gradient(90deg,#7CFC4A 0%,#A7F23C 55%,#FFD24A 100%)',
          boxShadow: 'inset 0 1px 0 rgba(255,255,255,0.55), 0 0 10px rgba(124,252,74,0.5)',
          transition: 'width .45s cubic-bezier(.22,1,.36,1)' }} />
      </div>
      {/* sparkle riding the leading edge */}
      {near && <div style={{ position: 'absolute', top: '50%', left: `${pct}%`,
        transform: 'translate(-50%,-50%)', pointerEvents: 'none',
        transition: 'left .45s cubic-bezier(.22,1,.36,1)' }}>
        <svg width={h + 8} height={h + 8} viewBox="0 0 24 24" style={{ display: 'block',
          filter: 'drop-shadow(0 0 5px rgba(255,234,140,0.95))' }}>
          <path d="M12 1.5l2.6 6.4 6.9.5-5.3 4.5 1.7 6.7L12 16.4 6.1 20.1l1.7-6.7L2.5 8.9l6.9-.5z"
            fill="#FFF6C8" stroke="#FFC23D" strokeWidth="1" strokeLinejoin="round"/>
        </svg>
      </div>}
    </div>
    {/* value / target text — white, outlined to read in-world, target highlighted */}
    <div className="num" style={{ alignSelf: 'flex-end', fontWeight: 700, fontSize: 15, color: '#fff',
      letterSpacing: 0.3, textShadow: '0 1px 3px rgba(0,0,0,0.55), 0 0 1px rgba(0,0,0,0.6)' }}>
      {val.toLocaleString()}<span style={{ opacity: 0.7 }}>/</span>
      <span style={{ color: '#9DFF7A' }}>{tgt.toLocaleString()}</span>
    </div>
  </div>;
}

// Standard centered popup shell. Wraps all popups: frosted .panel card,
// bold Baloo title, optional close (X). Centered overlay with scrim.
function Dialog({ title, children, onClose }) {
  return <div style={{ position: 'absolute', inset: 0, zIndex: 90, display: 'grid', placeItems: 'center', padding: 22,
    background: 'rgba(12,16,32,0.46)', backdropFilter: 'blur(3px)', WebkitBackdropFilter: 'blur(3px)',
    animation: 'hh-fadeup .22s ease both' }}
    onClick={(e) => { if (onClose && e.target === e.currentTarget) onClose(); }}>
    <div className="panel" style={{ position: 'relative', width: '100%', maxWidth: 340, padding: '26px 22px 22px',
      animation: 'hh-pop .34s cubic-bezier(.34,1.56,.64,1) both' }}>
      {onClose && <button onClick={onClose} aria-label="Close" style={{ position: 'absolute', top: 14, right: 14,
        width: 34, height: 34, borderRadius: '50%', display: 'grid', placeItems: 'center', cursor: 'pointer',
        background: '#fff', border: '1px solid var(--line)', color: 'var(--slate)', boxShadow: 'var(--shadow)' }}>
        <I.close width={18} height={18} />
      </button>}
      {title && <h2 className="disp" style={{ margin: '0 0 14px', fontSize: 24, lineHeight: 1.05,
        paddingRight: onClose ? 30 : 0, color: 'var(--ink)' }}>{title}</h2>}
      {children}
    </div>
  </div>;
}

// ============================================================
// ROUND-2 shared components (cream pop-card shell + candy toggle row + bottom nav)
// Uses DESIGN-SYSTEM classes (.hh-popcard / .hh-toggle / .hh-bottomnav families)
// AND inlines premium fallback styling so they look right regardless of CSS state.
// ============================================================

// Cream pop-card dialog shell — warm cream panel, thick soft border, bold brown
// title, round ✕ chip top-right, pop-in. accent tints the title underline + close ring.
function PopCard({ title, onClose, children, accent = 'var(--sun)' }) {
  // Scrim scrolls; inner flex wrapper has min-height:100% so the card centers when short
  // and the whole card (incl. SAVE) scrolls into reach when taller than the viewport.
  return <div className="hh-popcard-scrim" onClick={(e) => { if (onClose && !(e.target.closest && e.target.closest('.hh-popcard'))) onClose(); }}
    style={{ position: 'absolute', inset: 0, zIndex: 92, overflowY: 'auto', WebkitOverflowScrolling: 'touch',
      background: 'rgba(20,16,32,0.50)', backdropFilter: 'blur(4px)', WebkitBackdropFilter: 'blur(4px)',
      animation: 'hh-fadeup .22s ease both' }}>
    <div style={{ minHeight: '100%', display: 'flex', alignItems: 'center', justifyContent: 'center',
      padding: 'calc(24px + env(safe-area-inset-top,0px)) 22px calc(24px + env(safe-area-inset-bottom,0px))' }}>
    <div className="hh-popcard" style={{ position: 'relative', width: '100%', maxWidth: 348, padding: '26px 22px 24px',
      borderRadius: 28, background: 'var(--pc-bg)',
      border: '3px solid var(--pc-border-col)', boxShadow: '0 2px 0 rgba(255,255,255,0.12) inset, 0 26px 60px rgba(10,8,24,0.4), 0 6px 0 rgba(0,0,0,0.18)',
      animation: 'hh-pop .36s cubic-bezier(.34,1.56,.64,1) both' }}>
      {onClose && <button className="hh-popcard-close" onClick={onClose} aria-label="Close"
        style={{ position: 'absolute', top: 14, right: 14, width: 38, height: 38, borderRadius: '50%',
          display: 'grid', placeItems: 'center', cursor: 'pointer', color: 'var(--pc-close-text)',
          background: 'var(--pc-close-bg)', border: '2px solid var(--pc-close-border-col)',
          boxShadow: `0 3px 0 rgba(0,0,0,0.18), 0 0 0 3px ${accent}22` }}>
        <I.close width={19} height={19} />
      </button>}
      {title && <h2 className="hh-popcard-title disp" style={{ margin: '2px 0 16px', fontSize: 25, lineHeight: 1.06,
        color: 'var(--pc-title)', paddingRight: onClose ? 34 : 0 }}>
        {title}
        <span aria-hidden style={{ display: 'block', width: 44, height: 5, marginTop: 8, borderRadius: 100,
          background: accent, boxShadow: '0 1px 0 rgba(255,255,255,0.5) inset' }} />
      </h2>}
      {children}
    </div>
    </div>
  </div>;
}

// Green candy pill TOGGLE ROW — icon + label on the left, the .hh-toggle switch on the
// right. on=true → green track + knob slid right + check. Whole row is the hit target.
function Toggle2({ on, onClick, label, icon }) {
  return <button className="hh-toggle-row" onClick={onClick} role="switch" aria-checked={!!on} aria-label={label}
    style={{ display: 'flex', alignItems: 'center', gap: 12, width: '100%', textAlign: 'left', cursor: 'pointer',
      padding: '11px 12px 11px 14px', borderRadius: 18, background: 'var(--tg-row-bg)',
      border: '1px solid var(--line)', boxShadow: '0 2px 0 rgba(22,34,60,0.05)', marginBottom: 10 }}>
    {icon && <span style={{ width: 34, height: 34, borderRadius: 12, flexShrink: 0, display: 'grid', placeItems: 'center',
      color: on ? '#fff' : 'var(--slate)', background: on ? 'linear-gradient(180deg,#46E0A0,#22C9A0)' : 'var(--card)',
      boxShadow: on ? '0 2px 6px rgba(34,201,160,0.45)' : 'none', transition: 'all .18s' }}>{icon}</span>}
    <span style={{ flex: 1, fontFamily: 'var(--ui)', fontWeight: 800, fontSize: 15.5, color: 'var(--ink)' }}>{label}</span>
    <span className={'hh-toggle' + (on ? ' on' : '')} aria-hidden
      style={{ position: 'relative', width: 54, height: 31, borderRadius: 100, flexShrink: 0, transition: 'background .2s',
        background: on ? 'linear-gradient(180deg,#3FDE9A,#1FB98F)' : '#CBD6E6',
        boxShadow: on ? 'inset 0 2px 4px rgba(13,120,90,0.35), 0 1px 0 rgba(255,255,255,0.4)' : 'inset 0 2px 4px rgba(22,34,60,0.22)' }}>
      <span style={{ position: 'absolute', top: 3, left: on ? 26 : 3, width: 25, height: 25, borderRadius: '50%',
        background: 'linear-gradient(180deg,#fff,#F0F4FA)', boxShadow: '0 2px 5px rgba(22,34,60,0.3)',
        display: 'grid', placeItems: 'center', color: '#22C9A0', transition: 'left .2s cubic-bezier(.34,1.56,.64,1)' }}>
        {on && <I.check width={15} height={15} />}
      </span>
    </span>
  </button>;
}

// Persistent 3-tab bottom nav: Locked-levels (lock → 'map') · Home (house, raised center
// → 'home') · Holes (hole → 'shop'). active ∈ 'map'|'home'|'shop'. Safe-area aware.
function BottomNav({ active, go }) {
  const tabs = [
    { key: 'map', label: 'Levels', icon: (sel) => { const GI = window.GameIcon; const c = sel ? '#fff' : 'var(--slate)'; return GI ? <GI id="levels" size={sel ? 34 : 30} tone={sel ? 'sky' : 'dark'} /> : <svg width="27" height="27" viewBox="0 0 24 24"><path d="M5.5 18.5h4.2c1.6 0 2.9-1.3 2.9-2.9s1.3-2.9 2.9-2.9h2.7" fill="none" stroke={c} strokeWidth="2.2" strokeLinecap="round" strokeDasharray="0.2 3.6"/><circle cx="5.5" cy="18.5" r="2.6" fill={c}/><path d="M18.3 4.5l1.4 2.9 3.2.46-2.3 2.25.55 3.18-2.85-1.5-2.85 1.5.55-3.18-2.3-2.25 3.2-.46z" fill={c}/></svg>; } },
    { key: 'home', label: 'Home', center: true,
      icon: (sel) => { const GI = window.GameIcon; return GI ? <GI id="home" size={sel ? 38 : 32} tone={sel ? 'sky' : 'dark'} /> : <svg width="30" height="30" viewBox="0 0 24 24"><path d="M3 11.2L12 4l9 7.2" fill="none" stroke={sel ? '#fff' : 'var(--slate)'} strokeWidth="2.2" strokeLinecap="round" strokeLinejoin="round"/><path d="M5 10v9h14v-9" fill="none" stroke={sel ? '#fff' : 'var(--slate)'} strokeWidth="2.2" strokeLinecap="round" strokeLinejoin="round"/><rect x="10" y="13.5" width="4" height="5.5" rx="1" fill={sel ? '#fff' : 'var(--slate)'}/></svg>; } },
    { key: 'shop', label: 'Holes', icon: (sel) => { const GI = window.GameIcon; return GI ? <GI id="hole" size={sel ? 34 : 30} tone={sel ? 'sky' : 'dark'} /> : <svg width="28" height="28" viewBox="0 0 24 24"><ellipse cx="12" cy="13" rx="9" ry="6" fill="none" stroke={sel ? '#fff' : 'var(--slate)'} strokeWidth="2.2"/><ellipse cx="12" cy="13" rx="4" ry="2.6" fill={sel ? '#fff' : 'var(--slate)'}/></svg>; } },
  ];
  return <nav className="hh-bottomnav" style={{ position: 'absolute', left: 0, right: 0, bottom: 0, zIndex: 60,
    display: 'flex', alignItems: 'center', justifyContent: 'space-around', gap: 6,
    padding: '11px 10px calc(11px + env(safe-area-inset-bottom, 0px))',
    background: 'linear-gradient(180deg,#173D70,#071B34)',
    backdropFilter: 'blur(16px)', WebkitBackdropFilter: 'blur(16px)',
    border: '2px solid rgba(87,151,224,0.55)', borderRadius: 0,
    boxShadow: '0 -4px 0 rgba(255,255,255,0.12) inset, 0 -16px 28px rgba(0,0,0,0.36)' }}>
    {tabs.map((t) => {
      const sel = active === t.key;
      return <button key={t.key} className={'hh-navtab' + (sel ? ' active' : '') + (t.center ? ' center' : '')}
        onClick={() => go && go(t.key)} aria-label={t.label} aria-current={sel ? 'page' : undefined}
        style={{ flex: 1, minWidth: 0, display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 4, cursor: 'pointer',
          background: 'none', border: 'none', padding: 0, color: sel ? '#fff' : 'rgba(255,255,255,0.58)',
          transform: t.center ? 'translateY(-12px)' : 'none' }}>
        {/* icon dead-centred in the tab face via flex centring (grid placeItems left a
            top-heavy gap against the tab-shape art); label sits just below with an even gap */}
        <span style={{ width: t.center ? 104 : 86, maxWidth: '100%', height: t.center ? 80 : 66, borderRadius: 18,
          display: 'flex', alignItems: 'center', justifyContent: 'center', transition: 'all .2s cubic-bezier(.34,1.56,.64,1)',
          background: 'transparent',
          backgroundImage: `url(${sel ? 'assets/ui-ai/tab-blue-center.webp' : 'assets/ui-ai/tab-dark-left.webp'})`,
          backgroundSize: '100% 100%', backgroundRepeat: 'no-repeat', backgroundPosition: 'center',
          border: '0', boxShadow: 'none', paddingBottom: 2 }}>
          {t.icon(sel)}
        </span>
        <span style={{ fontFamily: 'var(--ui)', fontWeight: 900, fontSize: 13, letterSpacing: 0,
          marginTop: t.center ? -2 : 0, opacity: sel ? 1 : 0.78, textTransform: 'uppercase' }}>{t.label}</span>
      </button>;
    })}
  </nav>;
}

Object.assign(window, { I, GoalGlyph, Coin, Gem, CurChip, AIIcon, Stars, GoalChip, TimerRing, IconBtn, Bar, Bezel, StatusBar, TopBar, Label, GoalPill, ProgressBar, Dialog, PopCard, Toggle2, BottomNav });
