/* ============================================================
   BrainTrail · Shared components
   ============================================================ */
const { useState, useEffect, useRef, useMemo } = React;

/* ---------- Topographic contour background ---------- */
function TopoBG({ tint = '#0f8f7e', dense = false }) {
  // Generate concentric organic ovals — looks like trail contour lines.
  const lines = [];
  const cx = 230, cy = 440;
  const count = dense ? 9 : 7;
  for (let i = 0; i < count; i++) {
    const r = 80 + i * 70;
    lines.push(
      <ellipse key={i}
        cx={cx} cy={cy}
        rx={r * 1.1}
        ry={r * 0.85 + (i % 2 === 0 ? 14 : -14)}
        fill="none"
        stroke={tint}
        strokeWidth="1.2"
        opacity={0.10 + (i % 3) * 0.03}
        transform={`rotate(${-12 + i * 4} ${cx} ${cy})`}
      />
    );
  }
  return (
    <svg className="bt-topo" viewBox="0 0 460 880" preserveAspectRatio="none">
      {lines}
    </svg>
  );
}

/* ---------- Tablet frame ---------- */
function StatusBar({ time = '9:41', light = false }) {
  const color = light ? '#fff' : 'var(--ink)';
  return (
    <div className="bt-status" style={{ color }}>
      <span>{time}</span>
      <div className="bt-status-right">
        <svg width="16" height="11" viewBox="0 0 16 11" fill="none">
          <path d="M1 6.5 A 8 8 0 0 1 15 6.5" stroke={color} strokeWidth="1.5" strokeLinecap="round"/>
          <path d="M3.5 8 A 4.5 4.5 0 0 1 12.5 8" stroke={color} strokeWidth="1.5" strokeLinecap="round"/>
          <circle cx="8" cy="9.5" r="1" fill={color}/>
        </svg>
        <svg width="22" height="11" viewBox="0 0 22 11" fill="none">
          <rect x="0.5" y="0.5" width="18" height="10" rx="2.5" stroke={color}/>
          <rect x="2" y="2" width="13" height="7" rx="1" fill={color}/>
          <rect x="19.5" y="3.5" width="1.5" height="4" rx="0.75" fill={color}/>
        </svg>
      </div>
    </div>
  );
}

function TabletFrame({ children, topo = true, topoTint, statusLight, bg, label }) {
  return (
    <div className="bt-app" style={bg ? { background: bg } : null} data-screen-label={label}>
      {topo && <TopoBG tint={topoTint} />}
      <StatusBar light={statusLight} />
      {children}
    </div>
  );
}

/* ---------- Icon set (hand-drawn-ish, single source of truth) ---------- */
const Icon = {
  Home:   (p) => <svg viewBox="0 0 24 24" {...p}><path d="M3 11.5 L12 4 L21 11.5 V20 a1 1 0 0 1-1 1h-4v-6h-6v6H4 a1 1 0 0 1-1-1z" strokeLinejoin="round" strokeLinecap="round"/></svg>,
  Map:    (p) => <svg viewBox="0 0 24 24" {...p}><path d="M9 4 L3 6 V20 L9 18 L15 20 L21 18 V4 L15 6 Z" strokeLinejoin="round" strokeLinecap="round"/><path d="M9 4 V18 M15 6 V20"/></svg>,
  Star:   (p) => <svg viewBox="0 0 24 24" {...p}><path d="M12 3 L14.5 9 L21 9.5 L16 14 L17.5 21 L12 17.5 L6.5 21 L8 14 L3 9.5 L9.5 9 Z" strokeLinejoin="round" strokeLinecap="round"/></svg>,
  Trophy: (p) => <svg viewBox="0 0 24 24" {...p}><path d="M7 4 H17 V10 a5 5 0 0 1-10 0z M5 5 H3 a2 2 0 0 0 2 4 M19 5 H21 a2 2 0 0 1-2 4 M9 14 V18 M15 14 V18 M7 20 H17 L16 18 H8 Z" strokeLinejoin="round" strokeLinecap="round"/></svg>,
  Person: (p) => <svg viewBox="0 0 24 24" {...p}><circle cx="12" cy="8" r="4"/><path d="M4 21 a8 8 0 0 1 16 0" strokeLinecap="round"/></svg>,
  Flame:  (p) => <svg viewBox="0 0 24 24" {...p}><path d="M12 3 c0 4 4 5 4 9 a4 4 0 1 1-8 0 c0-2 1-3 1-5 c2 1 3 0 3-4 Z" strokeLinejoin="round" strokeLinecap="round"/></svg>,
  Lightbulb: (p) => <svg viewBox="0 0 24 24" {...p}><path d="M9 18 H15 M10 21 H14 M12 3 a6 6 0 0 1 4 10 c-1 1-1 2-1 3 H9 c0-1 0-2-1-3 a6 6 0 0 1 4-10 Z" strokeLinejoin="round" strokeLinecap="round"/></svg>,
  Lock:   (p) => <svg viewBox="0 0 24 24" {...p}><rect x="5" y="11" width="14" height="10" rx="2"/><path d="M8 11 V7 a4 4 0 0 1 8 0 V11"/></svg>,
  Check:  (p) => <svg viewBox="0 0 24 24" {...p}><path d="M5 12.5 L10 17 L19 7" strokeLinecap="round" strokeLinejoin="round"/></svg>,
  Mountain:(p)=> <svg viewBox="0 0 24 24" {...p}><path d="M2 20 L9 9 L13 15 L16 11 L22 20 Z" strokeLinejoin="round" strokeLinecap="round"/></svg>,
  Arrow:  (p) => <svg viewBox="0 0 24 24" {...p}><path d="M5 12 H19 M13 6 L19 12 L13 18" strokeLinecap="round" strokeLinejoin="round"/></svg>,
  Back:   (p) => <svg viewBox="0 0 24 24" {...p}><path d="M19 12 H5 M11 6 L5 12 L11 18" strokeLinecap="round" strokeLinejoin="round"/></svg>,
  Clock:  (p) => <svg viewBox="0 0 24 24" {...p}><circle cx="12" cy="12" r="9"/><path d="M12 7 V12 L15 14" strokeLinecap="round"/></svg>,
  Plus:   (p) => <svg viewBox="0 0 24 24" {...p}><path d="M12 5 V19 M5 12 H19" strokeLinecap="round"/></svg>,
  Sparkle:(p) => <svg viewBox="0 0 24 24" {...p}><path d="M12 3 L13.5 10 L20 12 L13.5 14 L12 21 L10.5 14 L4 12 L10.5 10 Z" strokeLinejoin="round"/></svg>,
  Clipboard:(p)=> <svg viewBox="0 0 24 24" {...p}><rect x="5" y="4.5" width="14" height="16.5" rx="2.5"/><path d="M9 4.5 V3.6 a1.4 1.4 0 0 1 1.4-1.4 h3.2 a1.4 1.4 0 0 1 1.4 1.4 V4.5 Z" strokeLinejoin="round"/><path d="M9 11 H15 M9 14.5 H13" strokeLinecap="round"/></svg>,
  Trash:  (p) => <svg viewBox="0 0 24 24" {...p}><path d="M4 7 H20 M9 7 V5 a1 1 0 0 1 1-1 h4 a1 1 0 0 1 1 1 V7 M6 7 L7 20 a1 1 0 0 0 1 1 h8 a1 1 0 0 0 1-1 L18 7 M10 11 V17 M14 11 V17" strokeLinecap="round" strokeLinejoin="round"/></svg>,
  Camera: (p) => <svg viewBox="0 0 24 24" {...p}><path d="M3 8 a2 2 0 0 1 2-2 h2 l1.4-2 h5.2 L17 6 h2 a2 2 0 0 1 2 2 v9 a2 2 0 0 1-2 2 H5 a2 2 0 0 1-2-2 Z" strokeLinejoin="round"/><circle cx="12" cy="12.5" r="3.4"/></svg>,
};

/* ---------- Trailblazer avatars (illustrated faces) ---------- */
const AVATARS = [
  { id: 'fox',    name: 'Fox',    bg: '#f08a4b', fg: '#fff', emoji: '🦊' },
  { id: 'owl',    name: 'Owl',    bg: '#7c5cd3', fg: '#fff', emoji: '🦉' },
  { id: 'bear',   name: 'Bear',   bg: '#a06a36', fg: '#fff', emoji: '🐻' },
  { id: 'rabbit', name: 'Rabbit', bg: '#e9b8c8', fg: '#fff', emoji: '🐰' },
  { id: 'frog',   name: 'Frog',   bg: '#5aaa6a', fg: '#fff', emoji: '🐸' },
  { id: 'panda',  name: 'Panda',  bg: '#3d5b54', fg: '#fff', emoji: '🐼' },
];

/* ---------- Drawn-feeling Avatar circle (text monogram inside) ---------- */
function Avatar({ id = 'fox', size = 44, ring = false }) {
  const a = AVATARS.find(x => x.id === id) || AVATARS[0];
  return (
    <div
      className="bt-avatar bt-avatar-illus"
      style={{
        width: size, height: size, fontSize: size * 0.55,
        background: a.bg,
        boxShadow: ring ? `0 0 0 3px var(--cream), 0 0 0 5px ${a.bg}` : '0 3px 0 rgba(0,0,0,0.12)',
      }}
      title={a.name}
    >
      <span style={{ filter: 'saturate(1.1)' }}>{a.emoji}</span>
    </div>
  );
}

/* ---------- Badge medallion ---------- */
function Badge({ name, kind = 'earned', emoji = '⭐', tint = 'teal' }) {
  const tints = {
    teal:   { bg: 'var(--teal-100)',   stroke: 'var(--teal-700)' },
    orange: { bg: 'var(--orange-100)', stroke: 'var(--orange-700)' },
    plum:   { bg: 'var(--plum-100)',   stroke: 'var(--plum-700)' },
    gold:   { bg: '#fde0a4',           stroke: '#8a5a10' },
  };
  const t = tints[tint] || tints.teal;
  const locked = kind === 'locked';
  return (
    <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 4, width: 80 }}>
      <div style={{
        width: 64, height: 64, borderRadius: '50%',
        background: locked ? 'var(--cream-2)' : t.bg,
        border: `2.5px dashed ${locked ? '#c9bda1' : t.stroke}`,
        display: 'flex', alignItems: 'center', justifyContent: 'center',
        fontSize: 30,
        filter: locked ? 'grayscale(1) opacity(0.4)' : 'none',
        position: 'relative',
      }}>
        {locked ? '🔒' : emoji}
      </div>
      <div style={{
        fontSize: 11, fontWeight: 700, fontFamily: 'Fredoka',
        color: locked ? 'var(--ink-mute)' : 'var(--ink)',
        textAlign: 'center', lineHeight: 1.2,
      }}>{name}</div>
    </div>
  );
}

/* ---------- Difficulty chip ---------- */
function DifficultyChip({ level }) {
  const dots = { easy: 1, medium: 2, hard: 3, challenge: 4 }[level] || 1;
  const labels = { easy: 'Easy', medium: 'Medium', hard: 'Hard', challenge: 'Challenge' };
  return (
    <span className={`bt-chip ${level}`}>
      <span style={{ display: 'flex', gap: 2 }}>
        {[1,2,3,4].map(i => (
          <span key={i} style={{
            width: 6, height: 6, borderRadius: '50%',
            background: i <= dots ? 'currentColor' : 'transparent',
            border: '1.5px solid currentColor',
            opacity: i <= dots ? 1 : 0.35,
          }}/>
        ))}
      </span>
      {labels[level]}
    </span>
  );
}

/* ---------- Decorative mountain silhouette ---------- */
function MountainArt({ width = 460, height = 220, palette = 'sunrise' }) {
  const palettes = {
    sunrise: { sky: ['#fcd9a1', '#f7ba78'], far: '#7c5cd3', mid: '#1eb29c', near: '#0f8f7e', sun: '#fff2c9' },
    forest:  { sky: ['#cbe9e0', '#a4d9c9'], far: '#5d40b8', mid: '#0f8f7e', near: '#0a4a42', sun: '#fff7d6' },
  };
  const p = palettes[palette];
  return (
    <svg viewBox="0 0 460 220" width={width} height={height} style={{ display: 'block' }}>
      <defs>
        <linearGradient id={`sky-${palette}`} x1="0" y1="0" x2="0" y2="1">
          <stop offset="0" stopColor={p.sky[0]} />
          <stop offset="1" stopColor={p.sky[1]} />
        </linearGradient>
      </defs>
      <rect width="460" height="220" fill={`url(#sky-${palette})`} />
      <circle cx="340" cy="70" r="32" fill={p.sun} opacity="0.9" />
      {/* far range */}
      <path d="M-20 180 L60 110 L130 150 L210 90 L290 140 L370 80 L480 150 L480 220 L-20 220 Z"
            fill={p.far} opacity="0.5" />
      {/* mid range */}
      <path d="M-20 200 L40 140 L120 180 L200 120 L280 170 L360 130 L480 190 L480 220 L-20 220 Z"
            fill={p.mid} opacity="0.7" />
      {/* near range — main peak */}
      <path d="M-20 220 L80 170 L180 110 L240 60 L300 130 L380 170 L480 200 L480 220 Z"
            fill={p.near} />
      {/* snow caps */}
      <path d="M220 80 L240 60 L260 85 L250 90 L245 84 L240 92 L232 84 Z" fill="#fff" opacity="0.9"/>
      {/* trail dashes */}
      <path d="M40 210 Q 130 195, 180 170 T 240 130" stroke="#fbf5e9" strokeWidth="3"
            strokeDasharray="6 8" fill="none" strokeLinecap="round" opacity="0.85"/>
    </svg>
  );
}

/* ---------- Trail climb progress (animated) ----------
   Replaces flat progress dots with a winding mountain trail.
   The avatar walks the path; finished markers get checks; the
   summit holds a waving flag and twinkling sparkles.
---------------------------------------------------------------- */
function TrailClimb({ current = 3, total = 10, avatarId = 'fox' }) {
  // Hand-tuned marker positions along a winding ascent (viewBox 460×200).
  // Trail starts at lower-left, switches back twice, summits upper-right.
  const allPts = [
    [28, 178], [70, 168], [108, 174], [148, 158],
    [186, 146], [222, 152], [258, 134], [292, 116],
    [328, 96],  [358, 76],
  ];
  // If total != 10, sample evenly from the 10 baked points
  const points = total === 10
    ? allPts
    : Array.from({ length: total }, (_, i) =>
        allPts[Math.round((i / (total - 1)) * (allPts.length - 1))]);

  const summit = [410, 44];
  const avatar = AVATARS.find(a => a.id === avatarId) || AVATARS[0];
  const idx = Math.max(0, Math.min(current - 1, points.length - 1));
  const here = points[idx];

  // Build a smooth path through the markers (quadratic between each pair),
  // ending at the summit so the line literally climbs the mountain.
  const all = [...points, summit];
  let trailPath = `M ${all[0][0]} ${all[0][1]}`;
  for (let i = 1; i < all.length; i++) {
    const prev = all[i - 1];
    const cur  = all[i];
    const cx = (prev[0] + cur[0]) / 2;
    const cy = Math.min(prev[1], cur[1]) - 6;
    trailPath += ` Q ${cx} ${cy}, ${cur[0]} ${cur[1]}`;
  }

  // Split path into walked vs remaining (visual fill of progress).
  let walkedPath = `M ${all[0][0]} ${all[0][1]}`;
  for (let i = 1; i <= idx; i++) {
    const prev = all[i - 1];
    const cur  = all[i];
    const cx = (prev[0] + cur[0]) / 2;
    const cy = Math.min(prev[1], cur[1]) - 6;
    walkedPath += ` Q ${cx} ${cy}, ${cur[0]} ${cur[1]}`;
  }

  // Decorative pine positions (independent of trail)
  const pines = [
    [45, 188, 8],  [88, 184, 7],  [132, 188, 6], [175, 174, 7],
    [205, 168, 5], [248, 165, 6], [280, 152, 5], [340, 122, 5],
  ];

  return (
    <div style={{ position: 'relative', width: '100%' }}>
      <svg viewBox="0 0 460 210" style={{ width: '100%', height: 'auto', display: 'block' }}>
        <defs>
          <linearGradient id="trail-sky" x1="0" y1="0" x2="0" y2="1">
            <stop offset="0"   stopColor="#fde6c0"/>
            <stop offset="0.7" stopColor="#fbf5e9" stopOpacity="0.2"/>
            <stop offset="1"   stopColor="#fbf5e9" stopOpacity="0"/>
          </linearGradient>
          <linearGradient id="trail-far" x1="0" y1="0" x2="0" y2="1">
            <stop offset="0" stopColor="#b8a4ed"/>
            <stop offset="1" stopColor="#7c5cd3"/>
          </linearGradient>
        </defs>

        {/* sky wash */}
        <rect width="460" height="210" fill="url(#trail-sky)" rx="14"/>

        {/* drifting clouds */}
        <ellipse cx="90"  cy="32" rx="22" ry="5" fill="#fff" opacity="0.75"/>
        <ellipse cx="78"  cy="29" rx="12" ry="4" fill="#fff" opacity="0.75"/>
        <ellipse cx="260" cy="22" rx="26" ry="5" fill="#fff" opacity="0.7"/>
        <ellipse cx="278" cy="19" rx="14" ry="4" fill="#fff" opacity="0.7"/>

        {/* sun glow behind summit */}
        <circle cx={summit[0] - 8} cy={summit[1] - 14} r="18" fill="#fff2c9" opacity="0.85"/>

        {/* far mountain range */}
        <path d="M -10 200 L 50 110 L 110 150 L 180 80 L 250 120 L 320 70 L 400 100 L 470 140 L 470 210 L -10 210 Z"
              fill="url(#trail-far)" opacity="0.55"/>
        {/* mid range */}
        <path d="M -10 210 L 40 160 L 110 180 L 180 130 L 260 165 L 340 115 L 420 150 L 470 175 L 470 210 Z"
              fill="#7c5cd3" opacity="0.45"/>

        {/* The destination peak */}
        <path d={`M ${summit[0] - 38} ${summit[1] + 60}
                  L ${summit[0]}      ${summit[1] - 18}
                  L ${summit[0] + 38} ${summit[1] + 60} Z`}
              fill="#0f8f7e"/>
        {/* snow cap */}
        <path d={`M ${summit[0] - 14} ${summit[1] + 10}
                  L ${summit[0] - 8}  ${summit[1] + 2}
                  L ${summit[0] - 2}  ${summit[1] + 8}
                  L ${summit[0] + 3}  ${summit[1] - 2}
                  L ${summit[0] + 10} ${summit[1] + 6}
                  L ${summit[0]}      ${summit[1] - 18}
                  L ${summit[0] - 8}  ${summit[1] - 6} Z`}
              fill="#ffffff" opacity="0.95"/>

        {/* near rolling hill */}
        <path d="M -10 210 L -10 188 Q 80 198, 180 184 T 360 178 L 470 188 L 470 210 Z"
              fill="#1eb29c" opacity="0.7"/>

        {/* pine trees */}
        {pines.map(([x, y, h], i) => (
          <g key={`pine-${i}`}>
            <path d={`M ${x - h * 0.8} ${y}
                      L ${x} ${y - h * 2}
                      L ${x + h * 0.8} ${y} Z`}
                  fill="#0a4a42" opacity="0.85"/>
            <rect x={x - 1} y={y} width="2" height={h * 0.4} fill="#5b3a1f" opacity="0.7"/>
          </g>
        ))}

        {/* Trail — full (faint) then walked portion (bright, animated dashes) */}
        <path d={trailPath} stroke="#fbf5e9" strokeWidth="7" fill="none" strokeLinecap="round"/>
        <path d={trailPath} stroke="#143a33" strokeWidth="1.5" fill="none"
              strokeDasharray="2 6" strokeLinecap="round" opacity="0.22"/>
        <path d={walkedPath} stroke="#f08a4b" strokeWidth="3" fill="none"
              strokeDasharray="6 4" strokeLinecap="round"
              className="bt-trail-anim"/>

        {/* Markers */}
        {points.map((p, i) => {
          const done = i < idx;
          const isHere = i === idx;
          if (isHere) return null;
          return (
            <g key={`m-${i}`}>
              <circle cx={p[0]} cy={p[1] + 1} r={done ? 5.5 : 4.5}
                fill={done ? '#0f8f7e' : '#fbf5e9'}
                stroke={done ? '#0a4a42' : '#c9bda1'}
                strokeWidth={done ? 2 : 1.5}/>
              {done && (
                <path d={`M ${p[0] - 2.3} ${p[1] + 1}
                          L ${p[0] - 0.4} ${p[1] + 2.6}
                          L ${p[0] + 2.6} ${p[1] - 1.4}`}
                  stroke="#fff" strokeWidth="1.5" fill="none"
                  strokeLinecap="round" strokeLinejoin="round"/>
              )}
            </g>
          );
        })}

        {/* Summit flag — pole + waving cloth */}
        <line x1={summit[0]} y1={summit[1] - 18} x2={summit[0]} y2={summit[1] - 2}
              stroke="#143a33" strokeWidth="1.8" strokeLinecap="round"/>
        <g style={{ transformOrigin: `${summit[0]}px ${summit[1] - 18}px` }} className="bt-flag-wave">
          <path d={`M ${summit[0]} ${summit[1] - 18}
                    L ${summit[0] + 14} ${summit[1] - 15}
                    L ${summit[0] + 10} ${summit[1] - 12}
                    L ${summit[0] + 14} ${summit[1] - 8}
                    L ${summit[0]} ${summit[1] - 9} Z`}
                fill="#f08a4b"/>
        </g>
        {/* sparkles around summit */}
        <g>
          <circle cx={summit[0] + 22} cy={summit[1] - 20} r="2" fill="#f4c25b"
                  className="bt-twinkle" style={{ animationDelay: '0s' }}/>
          <circle cx={summit[0] - 18} cy={summit[1] - 14} r="1.6" fill="#f4c25b"
                  className="bt-twinkle" style={{ animationDelay: '0.6s' }}/>
          <circle cx={summit[0] + 6} cy={summit[1] - 32} r="1.4" fill="#f4c25b"
                  className="bt-twinkle" style={{ animationDelay: '1.1s' }}/>
        </g>

        {/* hiker shadow + dust puff anchored to current marker (live with bob) */}
        <ellipse cx={here[0]} cy={here[1] + 8} rx="9" ry="2.4"
                 fill="#143a33" className="bt-hiker-shade"/>
        <circle cx={here[0] - 6} cy={here[1] + 6} r="2.5" fill="#fbf5e9"
                opacity="0" className="bt-puff" style={{ animationDelay: '0.2s' }}/>
        <circle cx={here[0] - 8} cy={here[1] + 5} r="1.8" fill="#fbf5e9"
                opacity="0" className="bt-puff" style={{ animationDelay: '0.7s' }}/>
      </svg>

      {/* Hiker avatar — DOM overlay so we can use the colored emoji avatar */}
      <div style={{
        position: 'absolute',
        left: `${(here[0] / 460) * 100}%`,
        top:  `${(here[1] / 210) * 100}%`,
        transform: 'translate(-50%, -76%)',
        pointerEvents: 'none',
      }}>
        <div className="bt-hiker-bob" style={{ position: 'relative' }}>
          <div style={{
            width: 34, height: 34, borderRadius: '50%',
            background: avatar.bg, color: '#fff',
            display: 'flex', alignItems: 'center', justifyContent: 'center',
            fontSize: 19,
            boxShadow: '0 2px 0 rgba(20,58,51,0.18), 0 0 0 2.5px #fbf5e9',
          }}>
            <span>{avatar.emoji}</span>
          </div>
          {/* tiny hiking stick */}
          <div style={{
            position: 'absolute', right: -4, bottom: 2,
            width: 2, height: 16, background: '#5b3a1f',
            borderRadius: 1, transform: 'rotate(18deg)',
            transformOrigin: 'bottom right',
          }}/>
        </div>
        {/* "You are here" pill */}
        <div className="bt-pop" style={{
          position: 'absolute',
          bottom: 'calc(100% + 4px)', left: '50%',
          transform: 'translateX(-50%)',
          background: 'var(--ink)', color: '#fff',
          fontFamily: 'Fredoka', fontWeight: 600, fontSize: 10,
          padding: '3px 8px', borderRadius: 999, whiteSpace: 'nowrap',
          letterSpacing: '0.04em',
        }}>
          {current} / {total}
          <span style={{
            position: 'absolute', left: '50%', bottom: -3,
            transform: 'translateX(-50%) rotate(45deg)',
            width: 6, height: 6, background: 'var(--ink)',
          }}/>
        </div>
      </div>
    </div>
  );
}

/* ---------- Confetti burst ---------- */
function Confetti({ count = 24, active = true }) {
  if (!active) return null;
  const colors = ['#f08a4b', '#1eb29c', '#7c5cd3', '#f4c25b', '#e9748b'];
  return (
    <div style={{ position: 'absolute', inset: 0, pointerEvents: 'none', overflow: 'hidden' }}>
      {Array.from({ length: count }).map((_, i) => {
        const left = Math.random() * 100;
        const delay = Math.random() * 0.8;
        const size = 6 + Math.random() * 8;
        const color = colors[i % colors.length];
        const round = Math.random() > 0.5;
        return (
          <span key={i} style={{
            position: 'absolute',
            left: `${left}%`, top: -20,
            width: size, height: size * (round ? 1 : 0.4),
            background: color,
            borderRadius: round ? '50%' : 2,
            animation: `bt-confetti ${2.4 + Math.random() * 1.2}s ${delay}s linear infinite`,
          }}/>
        );
      })}
    </div>
  );
}

/* ---------- XP bar ---------- */
function XPBar({ current, max, label = 'XP' }) {
  const pct = Math.max(0, Math.min(100, (current / max) * 100));
  return (
    <div style={{ width: '100%' }}>
      <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline', marginBottom: 4 }}>
        <span style={{ fontFamily: 'Fredoka', fontSize: 12, fontWeight: 600, color: 'var(--ink-soft)' }}>{label}</span>
        <span style={{ fontFamily: 'Fredoka', fontSize: 12, fontWeight: 600, color: 'var(--ink)' }}>
          {current} / {max}
        </span>
      </div>
      <div className="bt-progress">
        <div className="bt-progress-fill" style={{ width: `${pct}%` }}/>
      </div>
    </div>
  );
}

/* ---------- Bottom nav ---------- */
function BottomNav({ active = 'home', onNav }) {
  const items = [
    { id: 'home',  label: 'Home',  Ico: Icon.Home },
    { id: 'map',   label: 'Trails',Ico: Icon.Map  },
    { id: 'badge', label: 'Badges',Ico: Icon.Trophy},
    { id: 'me',    label: 'Me',    Ico: Icon.Person },
  ];
  return (
    <div className="bt-nav">
      {items.map(it => (
        <div key={it.id}
          className={`bt-nav-item ${active === it.id ? 'active' : ''}`}
          onClick={() => onNav && onNav(it.id)}>
          <it.Ico />
          <span>{it.label}</span>
        </div>
      ))}
    </div>
  );
}

/* ---------- App-bar (top of inner screens) ---------- */
function AppBar({ title, onBack, right }) {
  return (
    <div style={{
      display: 'flex', alignItems: 'center', gap: 12,
      padding: '8px 18px 16px',
      flexShrink: 0, position: 'relative', zIndex: 4,
    }}>
      {onBack && (
        <button onClick={onBack} style={{
          width: 40, height: 40, borderRadius: 999, border: 'none',
          background: 'var(--paper)', boxShadow: 'var(--shadow-sm)',
          display: 'flex', alignItems: 'center', justifyContent: 'center',
          cursor: 'pointer',
        }}>
          <Icon.Back width="20" height="20" stroke="var(--ink)" strokeWidth="2.5" fill="none"/>
        </button>
      )}
      <h2 style={{ fontSize: 20, flex: 1 }}>{title}</h2>
      {right}
    </div>
  );
}

/* ---------- Export to window so other scripts see them ---------- */
Object.assign(window, {
  Icon, Avatar, AVATARS, Badge, DifficultyChip, MountainArt,
  Confetti, XPBar, BottomNav, AppBar, TabletFrame, TopoBG, StatusBar,
  TrailClimb,
});
