(function() {
  var useState  = React.useState;
  var useEffect = React.useEffect;
  var useRef    = React.useRef;
  var Icon      = window.CBIcon;

  /* Wide serpentine over the aerial city plate — the trail sweeps across the
     full width the way a road would, instead of hugging a narrow column. */
  var MAP_W = 640;
  var ROW_H = 118;
  var PAD_TOP = 74;
  var PAD_BOT = 104;
  var COLS = [0.20, 0.46, 0.80, 0.54];

  function nodeXY(i) {
    return {
      x: Math.round(MAP_W * COLS[i % COLS.length]),
      y: PAD_TOP + i * ROW_H,
    };
  }

  function buildTrail(n) {
    if (n < 1) return '';
    var p0 = nodeXY(0);
    var d = 'M ' + p0.x + ' ' + p0.y;
    for (var i = 1; i < n; i++) {
      var a = nodeXY(i - 1);
      var b = nodeXY(i);
      var midY = (a.y + b.y) / 2;
      d += ' C ' + a.x + ' ' + midY + ', ' + b.x + ' ' + midY + ', ' + b.x + ' ' + b.y;
    }
    return d;
  }

  function QuestsPage() {
    var _tab      = useState('path');     var tab = _tab[0], setTab = _tab[1];
    var _tick     = useState(0);          var tick = _tick[0], setTick = _tick[1];
    var _reward   = useState(null);       var rewardQuest = _reward[0], setRewardQuest = _reward[1];
    var _selected = useState(null);       var selectedId = _selected[0], setSelectedId = _selected[1];
    var mapRef = useRef(null);
    var scrollRef = useRef(null);

    var qs = window.CBQuests.state();
    var QUESTS = window.CBQuests.all();
    var BADGES = window.CBQuests.badges();
    var TITLES = window.CBQuests.titles();
    var GIFTS  = window.CBQuests.gifts();

    useEffect(function() {
      window.CBQuests.track('visit-quests');
      var off = window.CBQuests.on(function(ev) {
        setTick(function(t) { return t + 1; });
        if (ev.type === 'quests-ready' && ev.quests && ev.quests.length) {
          window.CBSfx && window.CBSfx.play('whoosh');
        }
      });
      return off;
    }, []);

    var hover = function() { document.body.classList.add('hov'); };
    var leave = function() { document.body.classList.remove('hov'); };

    function pct(q) {
      return Math.min(100, Math.round((window.CBQuests.progress(q.id) / q.target) * 100));
    }

    function handleClaim(q) {
      var claimed = window.CBQuests.claim(q.id);
      if (claimed) {
        setRewardQuest(claimed);
        window.CBSfx && window.CBSfx.play('xp');
      }
    }

    function goAction(q) {
      var map = {
        'project-open': function() { window.navigate('projects'); },
        'ar-visit': function() { window.navigate('ar-demo'); },
        'visit-community': function() { window.navigate('community'); },
        'submit-vision': function() { window.navigate('submit'); },
        'fund-intent': function() { window.navigate('funding'); },
        'generate-3d': function() { window.navigate('profile'); },
        'chest-claim': function() { window.navigate('community'); },
        'visit-quests': function() {},
        'earn-xp': function() { window.navigate('home'); },
        'streak-day': function() {},
        'daily-complete': function() {},
        'reach-mayor': function() {},
      };
      var fn = map[q.action];
      if (fn) fn();
    }

    function renderRewards(q) {
      var items = [];
      if (q.xp) items.push(<span key="xp" className="quest-pill xp"><Icon name="zap" size={10} /> +{q.xp} XP</span>);
      if (q.badge && BADGES[q.badge]) items.push(<span key="b" className="quest-pill">{BADGES[q.badge].emoji} Badge</span>);
      if (q.titleReward && TITLES[q.titleReward]) items.push(<span key="t" className="quest-pill"><Icon name="crown" size={10} /> Title</span>);
      if (q.gift && GIFTS[q.gift]) items.push(<span key="g" className="quest-pill gift"><Icon name="gift" size={10} /> {GIFTS[q.gift].value}</span>);
      return items;
    }

    var pathQuests = QUESTS.filter(function(q) { return q.cat === 'path'; });
    var dailyQuests = QUESTS.filter(function(q) { return q.cat === 'daily'; });
    var weeklyQuests = QUESTS.filter(function(q) { return q.cat === 'weekly'; });
    var badgeList = Object.keys(BADGES).map(function(k) { return BADGES[k]; });
    var titleList = Object.keys(TITLES).map(function(k) { return TITLES[k]; }).sort(function(a,b){ return a.tier - b.tier; });
    var giftList = Object.keys(GIFTS).map(function(k) { return GIFTS[k]; });
    var unlockedBadges = Object.keys(qs.badges).length;
    var unlockedGifts = Object.keys(qs.gifts).length;

    var currentPathId = null;
    var currentIndex = 0;
    for (var ci = 0; ci < pathQuests.length; ci++) {
      var cq = pathQuests[ci];
      var cPrev = ci > 0 ? pathQuests[ci - 1] : null;
      var cLocked = cPrev && !window.CBQuests.claimed(cPrev.id);
      if (!cLocked && !window.CBQuests.claimed(cq.id)) {
        currentPathId = cq.id;
        currentIndex = ci;
        break;
      }
      if (ci === pathQuests.length - 1) currentIndex = ci;
    }

    useEffect(function() {
      if (selectedId) return;
      if (currentPathId) setSelectedId(currentPathId);
      else if (pathQuests.length) setSelectedId(pathQuests[pathQuests.length - 1].id);
    }, [currentPathId, selectedId, pathQuests.length]);

    useEffect(function() {
      if (tab !== 'path' || !mapRef.current) return;
      var el = mapRef.current.querySelector('.quest-node.current, .quest-node.is-selected');
      if (!el || !el.scrollIntoView) return;
      var r = el.getBoundingClientRect();
      var vh = window.innerHeight || document.documentElement.clientHeight;
      // Only scroll when the node is out of the comfortable viewport band
      if (r.top < 90 || r.bottom > vh - 40) {
        try {
          el.scrollIntoView({ block: 'center', behavior: 'smooth', inline: 'nearest' });
        } catch (e) {
          el.scrollIntoView(true);
        }
      }
    }, [tab, currentPathId, selectedId]);

    /* The city plate used to live on the fixed frame, so it stayed put
       while the road scrolled. It now scrolls inside the track; this
       drives it at a fraction of the scroll so it reads as distance. */
    useEffect(function() {
      var sc = scrollRef.current;
      var view = sc && sc.closest ? sc.closest('.quest-map-view') : null;
      if (tab !== 'path' || !sc || !view) return;

      /* Travel a fixed distance across the whole scroll rather than a
         multiple of scrollTop: the plate is absolutely positioned inside
         an overflow:visible track, so an unbounded offset grows
         scrollHeight, which allows more scroll, which grows it again. */
      /* Parallax is a vestibular trigger — the plate still scrolls with
         the road, it just stops drifting at a different rate. */
      var calm = false;
      try {
        calm = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
      } catch (e) {}

      var PARALLAX_PX = calm ? 0 : 190;
      var ticking = false;
      function apply() {
        ticking = false;
        var max = sc.scrollHeight - sc.clientHeight;
        var ratio = max > 0 ? sc.scrollTop / max : 0;
        view.style.setProperty('--q-par', (ratio * PARALLAX_PX).toFixed(1) + 'px');
        view.style.setProperty('--q-scrolled', ratio.toFixed(4));
      }
      function onScroll() {
        if (ticking) return;
        ticking = true;
        window.requestAnimationFrame(apply);
      }

      apply();
      sc.addEventListener('scroll', onScroll, { passive: true });
      return function() { sc.removeEventListener('scroll', onScroll); };
    }, [tab, pathQuests.length]);

    var tabs = [
      { id: 'path', label: 'Path' },
      { id: 'daily', label: 'Daily' },
      { id: 'weekly', label: 'Weekly' },
      { id: 'badges', label: 'Badges' },
      { id: 'rewards', label: 'Rewards' },
    ];

    var mapH = PAD_TOP + Math.max(0, pathQuests.length - 1) * ROW_H + PAD_BOT;
    var trailD = buildTrail(pathQuests.length);
    var progressN = 0;
    for (var pi = 0; pi < pathQuests.length; pi++) {
      if (window.CBQuests.claimed(pathQuests[pi].id)) progressN = pi + 1;
      else break;
    }
    var progressTrail = buildTrail(Math.max(1, Math.min(pathQuests.length, progressN + (currentPathId ? 1 : 0))));

    var selectedQuest = null;
    var selectedIndex = -1;
    for (var si = 0; si < pathQuests.length; si++) {
      if (pathQuests[si].id === (selectedId || currentPathId)) {
        selectedQuest = pathQuests[si];
        selectedIndex = si;
        break;
      }
    }

    function levelState(q, i) {
      var complete = window.CBQuests.complete(q.id);
      var claimed = window.CBQuests.claimed(q.id);
      var ready = complete && !claimed;
      var prev = i > 0 ? pathQuests[i - 1] : null;
      var locked = prev && !window.CBQuests.claimed(prev.id);
      var current = !locked && q.id === currentPathId;
      return { complete: complete, claimed: claimed, ready: ready, locked: locked, current: current };
    }

    /* Next title on the ladder — first tier not yet unlocked */
    var nextTitle = null;
    for (var ti = 0; ti < titleList.length; ti++) {
      if (!qs.titles[titleList[ti].id]) { nextTitle = titleList[ti].label; break; }
    }
    var unlockedTitles = titleList.filter(function(t){ return qs.titles[t.id]; }).length;

    return (
      <div className="fx-shell fx-shell--quests">
        {window.CBFeedSideRail && <window.CBFeedSideRail active="" />}
      <div className="quest-studio fx-quests">
        <div className="quest-studio__fx" aria-hidden="true">
          <div className="quest-studio__aurora" />
          <div className="quest-studio__meadow" />
        </div>

        <div className="quest-studio__inner">
          <header className="quest-hero">
            <div className="quest-hero__top">
              <button type="button" className="quest-hero__back"
                onMouseEnter={hover} onMouseLeave={leave}
                onClick={function(){ window.navigate('home'); }}>
                ← Back
              </button>
              <div className="quest-hero__badge">
                <span className="quest-hero__badge-dot" />
                Missions
              </div>
            </div>

            <div className="quest-hero__copy">
              <h1 className="quest-hero__title">City Missions</h1>
              <p className="quest-hero__sub">
                Follow the winding path. Clear levels, claim rewards, climb the city.
              </p>
            </div>

            <div className="quest-stats" data-tour="quest-stats">
              <div className="quest-stat fxq-stat">
                <span className="fxq-stat-ico fxq-stat-ico--violet"><Icon name="zap" size={20} /></span>
                <div className="fxq-stat-meta">
                  <div className="quest-stat__lbl">Day Streak</div>
                  <div className="quest-stat__val quest-stat__val--accent">
                    {qs.streak} {qs.streak === 1 ? 'Day' : 'Days'}</div>
                  <div className="fxq-stat-sub">{qs.streak > 0 ? 'Keep it up!' : 'Check in daily'}</div>
                </div>
                <span className="fxq-stat-bar fxq-stat-bar--violet">
                  <span style={{ width: Math.min(100, Math.round((qs.streak / 14) * 100)) + '%' }} /></span>
              </div>
              <div className="quest-stat fxq-stat">
                <span className="fxq-stat-ico fxq-stat-ico--gold"><Icon name="gift" size={20} /></span>
                <div className="fxq-stat-meta">
                  <div className="quest-stat__lbl">Ready to Claim</div>
                  <div className="quest-stat__val quest-stat__val--green">{qs.pending}</div>
                  <div className="fxq-stat-sub">{qs.pending > 0 ? 'Rewards waiting!' : 'Play levels to earn'}</div>
                </div>
                {qs.pending > 0 && <span className="fxq-stat-chev"><Icon name="arrow-right" size={16} /></span>}
              </div>
              <div className="quest-stat fxq-stat">
                <span className="fxq-stat-ico fxq-stat-ico--cyan"><Icon name="trophy" size={20} /></span>
                <div className="fxq-stat-meta">
                  <div className="quest-stat__lbl">Badges</div>
                  <div className="quest-stat__val quest-stat__val--blue">{unlockedBadges} / {badgeList.length}</div>
                  <div className="fxq-stat-sub">Collect more badges</div>
                </div>
                <span className="fxq-stat-bar fxq-stat-bar--cyan">
                  <span style={{ width: Math.max(3, Math.round((unlockedBadges / badgeList.length) * 100)) + '%' }} /></span>
              </div>
              <div className="quest-stat fxq-stat">
                <span className="fxq-stat-ico fxq-stat-ico--green"><Icon name="crown" size={20} /></span>
                <div className="fxq-stat-meta">
                  <div className="quest-stat__lbl">Active Title</div>
                  <div className="quest-stat__val" style={{ fontSize: qs.activeTitle && qs.activeTitle.length > 10 ? 16 : 22, paddingTop: 4 }}>
                    {qs.activeTitle || '—'}
                  </div>
                  <div className="fxq-stat-sub">{nextTitle ? 'Next: ' + nextTitle : 'All titles earned'}</div>
                </div>
                <span className="fxq-stat-bar fxq-stat-bar--green">
                  <span style={{ width: Math.max(3, Math.round((unlockedTitles / Math.max(1, titleList.length)) * 100)) + '%' }} /></span>
              </div>
            </div>
          </header>

          <div className="quest-seg" role="tablist" aria-label="Mission sections">
            {tabs.map(function(t) {
              return (
                <button
                  key={t.id}
                  type="button"
                  role="tab"
                  aria-selected={tab === t.id}
                  className={'quest-seg__btn' + (tab === t.id ? ' on' : '')}
                  onMouseEnter={hover}
                  onMouseLeave={leave}
                  onClick={function(){ setTab(t.id); window.CBSfx && window.CBSfx.play('whoosh'); }}
                >
                  {t.label}
                </button>
              );
            })}
          </div>

          {/* ── Candy Crush winding roadmap ── */}
          {tab === 'path' && (
            <div className="quest-map-wrap" data-tour="quest-path">
              <div className="quest-map-head">
                <span className="quest-map-head__chip">
                  <Icon name="star" size={12} /> {progressN} / {pathQuests.length} levels cleared
                </span>
              </div>

              <div className="quest-map-view">
              <div className="quest-map-scroll" ref={scrollRef}>
              <div
                className="quest-map"
                ref={mapRef}
                style={{ aspectRatio: MAP_W + ' / ' + mapH }}
              >
                {/* City plate + blueprint grid — travel with the road */}
                <div className="quest-map__plate" aria-hidden="true" />
                <div className="quest-map__grid" aria-hidden="true" />
                <div className="quest-map__terrain" aria-hidden="true">
                  <span className="quest-hill quest-hill--a" />
                  <span className="quest-hill quest-hill--b" />
                  <span className="quest-hill quest-hill--c" />
                  <span className="quest-hill quest-hill--d" />
                  <span className="quest-spark quest-spark--1" />
                  <span className="quest-spark quest-spark--2" />
                  <span className="quest-spark quest-spark--3" />
                  <span className="quest-spark quest-spark--4" />
                  <span className="quest-spark quest-spark--5" />
                </div>

                <svg className="quest-map__svg" viewBox={'0 0 ' + MAP_W + ' ' + mapH} preserveAspectRatio="xMidYMin meet" aria-hidden="true">
                  <defs>
                    <linearGradient id="questTrailBase" x1="0" y1="0" x2="0" y2="1">
                      <stop offset="0%" stopColor="#5ac8fa" stopOpacity="0.55" />
                      <stop offset="50%" stopColor="#0a84ff" stopOpacity="0.45" />
                      <stop offset="100%" stopColor="#ff9f0a" stopOpacity="0.4" />
                    </linearGradient>
                    <linearGradient id="questTrailDone" x1="0" y1="0" x2="0" y2="1">
                      <stop offset="0%" stopColor="#30d158" />
                      <stop offset="100%" stopColor="#64d2ff" />
                    </linearGradient>
                    <filter id="questTrailGlow" x="-20%" y="-20%" width="140%" height="140%">
                      <feGaussianBlur stdDeviation="3" result="b" />
                      <feMerge><feMergeNode in="b" /><feMergeNode in="SourceGraphic" /></feMerge>
                    </filter>
                  </defs>
                  <path className="quest-map__trail-bg" d={trailD} fill="none" stroke="url(#questTrailBase)" strokeWidth="14" strokeLinecap="round" strokeLinejoin="round" />
                  <path className="quest-map__trail-shine" d={trailD} fill="none" stroke="rgba(255,255,255,0.22)" strokeWidth="5" strokeLinecap="round" strokeLinejoin="round" />
                  <path className="quest-map__trail-dots" d={trailD} fill="none" stroke="rgba(255,255,255,0.35)" strokeWidth="3" strokeLinecap="round" strokeDasharray="2 18" />
                  {progressN > 0 && (
                    <path className="quest-map__trail-done" d={progressTrail} pathLength="1000" fill="none" stroke="url(#questTrailDone)" strokeWidth="8" strokeLinecap="round" strokeLinejoin="round" filter="url(#questTrailGlow)" />
                  )}
                </svg>

                {pathQuests.map(function(q, i) {
                  var st = levelState(q, i);
                  var p = nodeXY(i);
                  var isSel = (selectedId || currentPathId) === q.id;
                  /* locked outranks ready: a gated level can be complete but
                     not claimable, and gold-disc-plus-padlock reads as a bug */
                  var cls = 'quest-node'
                    + (st.claimed ? ' done' : st.locked ? ' locked' : st.ready ? ' ready' : st.current ? ' current' : '')
                    + (isSel ? ' is-selected' : '');

                  return (
                    <button
                      key={q.id}
                      type="button"
                      className={cls}
                      style={{
                        left: ((p.x / MAP_W) * 100) + '%',
                        top: ((p.y / mapH) * 100) + '%',
                        animationDelay: (i * 0.05) + 's',
                      }}
                      aria-label={'Level ' + (i + 1) + ': ' + q.title}
                      aria-current={st.current ? 'step' : undefined}
                      data-title={st.locked ? 'Locked' : q.title}
                      disabled={st.locked}
                      onMouseEnter={hover}
                      onMouseLeave={leave}
                      onClick={function() {
                        if (st.locked) return;
                        setSelectedId(q.id);
                        window.CBSfx && window.CBSfx.play('whoosh');
                        if (st.ready) handleClaim(q);
                      }}
                    >
                      <span className="quest-node__ring" aria-hidden="true" />
                      {st.claimed && (
                        <span className="quest-node__stars" aria-hidden="true">
                          <Icon name="star" size={12} />
                          <Icon name="star" size={15} />
                          <Icon name="star" size={12} />
                        </span>
                      )}
                      <span className="quest-node__core">
                        {st.claimed
                          ? <Icon name="check" size={26} />
                          : st.locked
                            ? <Icon name="lock" size={22} />
                            : <Icon name={q.icon} size={26} />}
                      </span>
                      <span className="quest-node__n">{i + 1}</span>
                      {st.current && <span className="quest-node__you">YOU</span>}
                    </button>
                  );
                })}

                <div
                  className="quest-map__finish"
                  aria-hidden="true"
                  style={{
                    left: ((nodeXY(pathQuests.length - 1).x / MAP_W) * 100) + '%',
                    top: (((nodeXY(pathQuests.length - 1).y + 62) / mapH) * 100) + '%',
                  }}
                >
                  <Icon name="trophy" size={14} /> Finish
                </div>
              </div>
              </div>

              <div className="fxq-legend" aria-hidden="true">
                <span><i className="fxq-dot fxq-dot--done" /> Completed</span>
                <span><i className="fxq-dot fxq-dot--cur" /> Current</span>
                <span><i className="fxq-dot fxq-dot--lock" /> Locked</span>
              </div>
              </div>

              {selectedQuest && (function() {
                var st = levelState(selectedQuest, selectedIndex);
                var p = nodeXY(selectedIndex);
                var tipSide = p.x < MAP_W * 0.5 ? 'right' : 'left';
                return (
                  <div
                    key={selectedQuest.id}
                    className={'quest-sheet quest-sheet--' + tipSide + (st.ready ? ' is-ready' : '') + (st.locked ? ' is-locked' : '')}
                  >
                    <div className="quest-sheet__head">
                      <span className={'quest-sheet__lvlchip' + (st.claimed ? ' is-done' : st.ready ? ' is-ready' : '')}>
                        {selectedIndex + 1}
                      </span>
                      <div className="quest-sheet__lvl">
                        Level {selectedIndex + 1}
                        {st.claimed ? ' · Cleared' : st.ready ? ' · Reward ready' : st.locked ? ' · Locked' : ' · In play'}
                      </div>
                    </div>
                    <div className="fxq-sheet-toprow">
                      <div className="quest-sheet__title">{selectedQuest.title}</div>
                      {selectedQuest.xp > 0 && (
                        <span className="fxq-xp-chip"><Icon name="zap" size={13} /> +{selectedQuest.xp} XP</span>
                      )}
                    </div>
                    <div className="quest-sheet__desc">{selectedQuest.desc}</div>
                    {!st.locked && (
                      <>
                        <div className="fxq-progress-row">
                          <span>Progress</span>
                          <span>{st.claimed ? selectedQuest.target : Math.min(selectedQuest.target, window.CBQuests.progress(selectedQuest.id))} / {selectedQuest.target}</span>
                        </div>
                        <div className="quest-sheet__bar">
                          <div
                            className={'quest-sheet__fill' + (st.claimed ? ' is-done' : '')}
                            style={{ width: (st.claimed ? 100 : pct(selectedQuest)) + '%' }}
                          />
                        </div>
                        <div className="fxq-obj-label">Objectives</div>
                        <div className={'fxq-obj-row' + (st.complete ? ' done' : '')}>
                          <span className="fxq-obj-check"><Icon name={st.complete ? 'check' : 'circle-dot'} size={13} /></span>
                          <span className="fxq-obj-text">{selectedQuest.desc}</span>
                          <span className="fxq-obj-count">
                            {st.claimed ? selectedQuest.target : Math.min(selectedQuest.target, window.CBQuests.progress(selectedQuest.id))}/{selectedQuest.target}
                          </span>
                        </div>
                      </>
                    )}
                    <div className="fxq-obj-label">Reward Preview</div>
                    <div className="fxq-preview">
                      {selectedQuest.xp > 0 && (
                        <span className="fxq-preview-tile fxq-preview-tile--xp">
                          <span className="fxq-preview-ico"><Icon name="zap" size={18} /></span>
                          <b>{selectedQuest.xp}</b><i>XP</i></span>
                      )}
                      {selectedQuest.badge && BADGES[selectedQuest.badge] && (
                        <span className="fxq-preview-tile fxq-preview-tile--badge">
                          <span className="fxq-preview-ico">{BADGES[selectedQuest.badge].emoji}</span>
                          <b>{BADGES[selectedQuest.badge].name}</b><i>Badge</i></span>
                      )}
                      {selectedQuest.titleReward && TITLES[selectedQuest.titleReward] && (
                        <span className="fxq-preview-tile fxq-preview-tile--title">
                          <span className="fxq-preview-ico"><Icon name="crown" size={18} /></span>
                          <b>{TITLES[selectedQuest.titleReward].label}</b><i>Title</i></span>
                      )}
                      {selectedQuest.gift && GIFTS[selectedQuest.gift] && (
                        <span className="fxq-preview-tile fxq-preview-tile--gift">
                          <span className="fxq-preview-ico">{GIFTS[selectedQuest.gift].emoji}</span>
                          <b>{GIFTS[selectedQuest.gift].value}</b><i>Gift Card</i></span>
                      )}
                    </div>
                    <div className="quest-sheet__actions">
                      {st.ready && (
                        <button type="button" className="quest-claim" onClick={function(){ handleClaim(selectedQuest); }}>
                          <Icon name="gift" size={15} /> Claim Reward
                        </button>
                      )}
                      {!st.locked && !st.claimed && !st.ready && (
                        <button type="button" className="quest-go" onClick={function(){ goAction(selectedQuest); }}>
                          Play level
                        </button>
                      )}
                      {st.ready && (
                        <button type="button" className="fxq-details-btn"
                          onClick={function(){ goAction(selectedQuest); }}
                          onMouseEnter={hover} onMouseLeave={leave}>
                          View Mission Details <Icon name="arrow-right" size={14} />
                        </button>
                      )}
                      {st.claimed && <span className="quest-done-chip">Completed</span>}
                      {st.locked && <span className="quest-done-chip" style={{ color:'var(--q-tertiary)' }}>Locked — clear the previous level</span>}
                    </div>
                  </div>
                );
              })()}
            </div>
          )}

          {tab === 'daily' && (
            <div className="quest-list">
              {dailyQuests.map(function(q, qi) {
                var complete = window.CBQuests.complete(q.id);
                var claimed = window.CBQuests.claimed(q.id);
                var ready = complete && !claimed;
                return (
                  <div key={q.id} className={'quest-card' + (claimed ? ' done' : ready ? ' ready' : '')}
                    data-tour={qi === 0 ? 'quest-card' : undefined}
                    onMouseEnter={hover} onMouseLeave={leave}>
                    <div className="quest-card-icon"><Icon name={q.icon} size={22} /></div>
                    <div className="quest-card-body">
                      <div className="quest-card-title">{q.title}</div>
                      <div className="quest-card-desc">{q.desc}</div>
                      <div className="quest-card-meta">
                        {renderRewards(q)}
                        <span className="quest-pill">{pct(q)}%</span>
                      </div>
                    </div>
                    {ready ? (
                      <button className="quest-claim-btn" type="button" onClick={function(){ handleClaim(q); }}>Claim</button>
                    ) : claimed ? (
                      <span className="quest-done-chip">Done</span>
                    ) : (
                      <button className="quest-go" type="button" onClick={function(){ goAction(q); }}>Go</button>
                    )}
                  </div>
                );
              })}
            </div>
          )}

          {tab === 'weekly' && (
            <div className="quest-list">
              {weeklyQuests.map(function(q) {
                var complete = window.CBQuests.complete(q.id);
                var claimed = window.CBQuests.claimed(q.id);
                var ready = complete && !claimed;
                return (
                  <div key={q.id} className={'quest-card' + (claimed ? ' done' : ready ? ' ready' : '')}
                    onMouseEnter={hover} onMouseLeave={leave}>
                    <div className="quest-card-icon" style={{ background:'oklch(0.75 0.14 55 / 0.15)', color:'var(--q-ready)', borderColor:'oklch(0.8 0.16 55 / 0.3)' }}>
                      <Icon name={q.icon} size={22} />
                    </div>
                    <div className="quest-card-body">
                      <div className="quest-card-title">{q.title}</div>
                      <div className="quest-card-desc">{q.desc}</div>
                      <div className="quest-card-meta">
                        {renderRewards(q)}
                        <span className="quest-pill">{window.CBQuests.progress(q.id)}/{q.target}</span>
                      </div>
                    </div>
                    {ready ? (
                      <button className="quest-claim-btn" type="button" onClick={function(){ handleClaim(q); }}>Claim</button>
                    ) : claimed ? (
                      <span className="quest-done-chip">Claimed</span>
                    ) : (
                      <button className="quest-go" type="button" onClick={function(){ goAction(q); }}>Start</button>
                    )}
                  </div>
                );
              })}
            </div>
          )}

          {tab === 'badges' && (
            <div className="badge-grid">
              {badgeList.map(function(b) {
                var unlocked = !!qs.badges[b.id];
                return (
                  <div key={b.id} className={'badge-card' + (unlocked ? ' unlocked' : ' locked')}
                    onMouseEnter={hover} onMouseLeave={leave}
                    title={unlocked ? 'Unlocked' : 'Complete quests to unlock'}>
                    <div className="badge-emoji">{b.emoji}</div>
                    <div className="badge-name">{b.name}</div>
                    <div className={'badge-rarity ' + b.rarity}>{b.rarity}</div>
                  </div>
                );
              })}
            </div>
          )}

          {tab === 'rewards' && (
            <>
              <p className="quest-section-label">
                Titles · {titleList.filter(function(t){ return qs.titles[t.id]; }).length}/{titleList.length} unlocked
              </p>
              <div className="title-showcase">
                {titleList.map(function(t) {
                  var unlocked = !!qs.titles[t.id];
                  var active = qs.activeTitle === t.label;
                  return (
                    <div key={t.id} className={'title-row' + (unlocked ? ' unlocked' : ' locked') + (active ? ' active' : '')}
                      onMouseEnter={hover} onMouseLeave={leave}>
                      <span className="title-crown">{active ? '◆' : unlocked ? '✦' : '○'}</span>
                      <span className="title-label">{t.label}</span>
                      <span className="title-tier">Tier {t.tier}{active ? ' · Active' : ''}</span>
                    </div>
                  );
                })}
              </div>

              <p className="quest-section-label">
                Gift cards · {unlockedGifts}/{giftList.length} earned
              </p>
              <div className="rewards-grid">
                {giftList.map(function(g) {
                  var claimed = !!qs.gifts[g.id];
                  return (
                    <div key={g.id} className={'gift-card' + (claimed ? ' claimed' : '')}
                      onMouseEnter={hover} onMouseLeave={leave}>
                      {claimed && <span className="gift-card-status">Earned</span>}
                      <div className="gift-card-emoji">{g.emoji}</div>
                      <div className="gift-card-value">{g.value}</div>
                      <div className="gift-card-label">{g.label}</div>
                      <div className="gift-card-partner">{g.partner}</div>
                    </div>
                  );
                })}
              </div>
              <p className="quest-footnote">
                Complete weekly and milestone quests to unlock rewards. Gift cards are demo placeholders until a city partner API is connected.
              </p>
            </>
          )}
        </div>

        {rewardQuest && (
          <window.CBQuestRewardOverlay quest={rewardQuest} onClose={function(){ setRewardQuest(null); }} />
        )}
      </div>
      </div>
    );
  }

  window.UserQuestsPage = QuestsPage;
})();
