/* Mission Home — quickfire stack, hero, type-filtered list, separate routes */

const MISSIONS = [
  {
    id: 'm1',
    type: 'verify',
    title: 'Mori Tea House',
    hint: 'Verify · ground floor · Daikanyama',
    distance: '120m',
    bearing: 'NE',
    difficulty: 1,
    cp: 40,
    daysAgo: 243,
    seed: 'mori-tea-house-daikanyama',
    rare: false,
    lat: '35.6494',
    lng: '139.7036',
  },
  {
    id: 'm2',
    type: 'first-look',
    title: 'Unmarked storefront',
    hint: 'New POI · Sarugakucho 14-3',
    distance: '280m',
    bearing: 'E',
    difficulty: 2,
    cp: 90,
    daysAgo: 999,
    seed: 'sarugakucho-unmarked-14-3',
    rare: true,
    rareLabel: 'First Look',
    lat: '35.6502',
    lng: '139.7050',
  },
  {
    id: 'm3',
    type: 'refresh',
    title: 'Onibus Coffee',
    hint: 'Refresh hours & menu · Naka-Meguro',
    distance: '410m',
    bearing: 'SW',
    difficulty: 1,
    cp: 50,
    daysAgo: 91,
    seed: 'onibus-coffee-naka-meguro',
    rare: false,
    lat: '35.6443',
    lng: '139.6987',
  },
  {
    id: 'm4',
    type: 'angle',
    title: 'Tsutaya T-Site',
    hint: 'Capture: signage + interior · 4 angles',
    distance: '650m',
    bearing: 'N',
    difficulty: 2,
    cp: 70,
    daysAgo: 142,
    seed: 'tsutaya-tsite-daikanyama',
    rare: false,
    lat: '35.6510',
    lng: '139.7028',
  },
  {
    id: 'm5',
    type: 'route',
    title: 'Coffee crawl · 4 stops',
    hint: 'Route mission · Naka-Meguro',
    distance: '1.2km',
    bearing: '—',
    difficulty: 3,
    cp: 220,
    daysAgo: 0,
    seed: 'coffee-route-naka-meguro',
    rare: true,
    rareLabel: 'Route',
    lat: '35.6440',
    lng: '139.6990',
  },
  {
    id: 'm6',
    type: 'map-drop',
    title: 'Mission Coffee Map Drop',
    hint: 'Limited check-in · free cold brew · 18 left',
    distance: '520m',
    bearing: 'W',
    difficulty: 1,
    cp: 120,
    daysAgo: 0,
    seed: 'mission-coffee-map-drop',
    rare: true,
    rareLabel: 'Map Drop',
    lat: '35.6478',
    lng: '139.7012',
  },
];

const QUICKFIRES = [
  { id:'q1', poi:'Mori Tea House',  question:'Still open on Tuesdays?',           seed:'moriteahouse', cp:5 },
  { id:'q2', poi:'Onibus Coffee',   question:'Is outdoor seating still available?', seed:'onibuscoffee', cp:5 },
  { id:'q3', poi:'Cosme Kitchen',   question:'Menu updated since last visit?',      seed:'cosmekitch',   cp:5 },
];

const QUICKFIRE_DISMISSED_KEY = 'govibe.quickVerifies.dismissed';

const MAP_DROP_ACCESS = {
  completedPoi: 3,
  requiredPoi: 5,
  districtRank: 148,
  rankCutoff: 100,
  districtProgress: 72,
  districtTarget: 100,
};

const TYPE_CONFIG = {
  verify:       { label:'Verify',     color:'var(--status-done)' },
  'first-look': { label:'First Look', color:'var(--status-rare)' },
  refresh:      { label:'Refresh',    color:'var(--status-stale)' },
  angle:        { label:'Angle',      color:'var(--ink-2)' },
  route:        { label:'Route',      color:'var(--status-rare)' },
  'map-drop':   { label:'Map Drop',   color:'var(--warm)' },
};

function TypeChip({ type }) {
  const cfg = TYPE_CONFIG[type] || TYPE_CONFIG.verify;
  return (
    <span style={{
      display:'inline-flex', alignItems:'center', gap:5,
      fontSize:12, fontWeight:600, color:cfg.color,
    }}>
      <span style={{width:5, height:5, borderRadius:'50%', background:cfg.color, display:'inline-block', flexShrink:0}}/>
      {cfg.label}
    </span>
  );
}

/* ── Quickfire ──────────────────────────────────────── */

function QuickfireCard({ item, onAnswer, slideStyle }) {
  const photo = poiPhoto(item.seed, 800, 400);
  return (
    <div style={{
      background:'var(--bg-elev)', border:'1px solid var(--line)',
      borderRadius:16, overflow:'hidden',
      boxShadow:'0 4px 16px -4px rgba(20,32,31,0.10)',
      ...slideStyle,
    }}>
      {/* Photo strip */}
      <div style={{position:'relative', height:118}}>
        <img src={photo} alt={item.poi} className="photo-cover" style={{position:'absolute', inset:0}}/>
        <div style={{
          position:'absolute', inset:0,
          background:'linear-gradient(to top, rgba(10,16,16,0.72) 0%, rgba(10,16,16,0.1) 60%, transparent 100%)',
        }}/>
        <div style={{position:'absolute', bottom:11, left:14}}>
          <div style={{fontSize:15, fontWeight:700, color:'#fff', letterSpacing:'-0.01em', lineHeight:1.2}}>
            {item.poi}
          </div>
        </div>
        {/* Quick verify badge */}
        <div style={{
          position:'absolute', top:10, right:10,
          background:'rgba(10,16,16,0.55)', backdropFilter:'blur(6px)',
          border:'1px solid rgba(255,255,255,0.15)',
          borderRadius:5, padding:'3px 8px',
          display:'inline-flex', alignItems:'center', gap:4,
        }}>
          <span style={{width:4, height:4, borderRadius:'50%', background:'var(--status-fresh)', display:'inline-block'}}/>
          <span style={{fontFamily:'var(--font-mono)', fontSize:8, color:'#fff', letterSpacing:'0.05em', textTransform:'uppercase'}}>
            Quick verify
          </span>
        </div>
      </div>

      {/* Question + actions */}
      <div style={{padding:'14px 14px 14px'}}>
        <div style={{
          fontSize:15, fontWeight:600, letterSpacing:'-0.01em',
          lineHeight:1.35, marginBottom:14, color:'var(--ink)',
        }}>
          {item.question}
        </div>

        <div style={{display:'grid', gridTemplateColumns:'1fr 1fr', gap:8, marginBottom:8}}>
          <button onClick={() => onAnswer('no')} style={{
            height:44, borderRadius:10,
            background:'transparent', border:'1.5px solid var(--line)',
            fontFamily:'var(--font-sans)', fontSize:14, fontWeight:600,
            color:'var(--ink-2)', cursor:'pointer',
            display:'flex', alignItems:'center', justifyContent:'center', gap:6,
            transition:'border-color .15s',
          }}>
            <span style={{fontSize:15, lineHeight:1}}>✗</span> No
          </button>
          <button onClick={() => onAnswer('yes')} style={{
            height:44, borderRadius:10,
            background:'var(--teal)', border:0,
            fontFamily:'var(--font-sans)', fontSize:14, fontWeight:600,
            color:'#fff', cursor:'pointer',
            display:'flex', alignItems:'center', justifyContent:'center', gap:6,
          }}>
            <span style={{fontSize:15, lineHeight:1}}>✓</span> Yes
          </button>
        </div>

        <div style={{textAlign:'center'}}>
          <button onClick={() => onAnswer('skip')} style={{
            background:'transparent', border:0,
            fontFamily:'var(--font-sans)', fontSize:12, fontWeight:400,
            color:'var(--ink-3)', cursor:'pointer', padding:'4px 8px',
          }}>
            Not sure / skip
          </button>
        </div>
      </div>
    </div>
  );
}

function QuickfireStack() {
  const [index, setIndex]       = React.useState(0);
  const [slideDir, setSlideDir] = React.useState(null);
  const [receipt, setReceipt]   = React.useState(null);
  const [totalCp, setTotalCp]   = React.useState(0);
  const [collapsing, setCollapsing] = React.useState(false);
  const [hidden, setHidden]     = React.useState(() => {
    try { return window.localStorage.getItem(QUICKFIRE_DISMISSED_KEY) === '1'; }
    catch (e) { return false; }
  });

  function closeStack(persist = true) {
    if (persist) {
      try { window.localStorage.setItem(QUICKFIRE_DISMISSED_KEY, '1'); }
      catch (e) {}
    }
    setCollapsing(true);
    window.setTimeout(() => setHidden(true), 260);
  }

  function answer(dir) {
    const earned = dir !== 'skip' ? QUICKFIRES[index].cp : 0;
    setSlideDir(dir);

    if (earned > 0) {
      const key = Date.now();
      setReceipt({ cp: earned, key });
      setTotalCp(t => t + earned);
      setTimeout(() => setReceipt(null), 1600);
    }

    setTimeout(() => {
      if (index + 1 >= QUICKFIRES.length) {
        closeStack();
      } else {
        setIndex(i => i + 1);
        setSlideDir(null);
      }
    }, 300);
  }

  const slideStyle = slideDir === 'yes'  ? {
    transform:'translateX(110%) rotate(7deg)', opacity:0,
    transition:'transform 0.28s ease, opacity 0.22s ease',
  } : slideDir === 'no' ? {
    transform:'translateX(-110%) rotate(-7deg)', opacity:0,
    transition:'transform 0.28s ease, opacity 0.22s ease',
  } : slideDir === 'skip' ? {
    transform:'translateY(-16px)', opacity:0,
    transition:'transform 0.22s ease, opacity 0.18s ease',
  } : {};

  const remaining = QUICKFIRES.length - index;

  if (hidden) return null;

  return (
    <div style={{
      padding:'0 20px 20px',
      opacity: collapsing ? 0 : 1,
      maxHeight: collapsing ? 0 : 380,
      overflow:'hidden',
      transition:'opacity .22s ease, max-height .26s ease, padding .26s ease',
    }}>
      <div className="sec-head" style={{marginBottom:12}}>
        <h3>Quick verifies</h3>
        <div style={{display:'flex', alignItems:'center', gap:8}}>
          <span className="label">{`${remaining} nearby`}</span>
          <button onClick={() => closeStack()} aria-label="Dismiss quick verifies" style={{
            width:22, height:22, borderRadius:6,
            background:'transparent', border:'1px solid var(--line)',
            color:'var(--ink-3)', cursor:'pointer',
            display:'flex', alignItems:'center', justifyContent:'center',
            fontSize:14, lineHeight:1, padding:0,
          }}>×</button>
        </div>
      </div>

      <div style={{position:'relative'}}>
        {/* +GP receipt pill */}
        {receipt && (
          <div key={receipt.key} style={{
            position:'absolute', top:-8, right:0, zIndex:20,
            background:'var(--teal)', color:'#fff',
            borderRadius:20, padding:'5px 14px',
            fontFamily:'var(--font-mono)', fontSize:12, fontWeight:700,
            animation:'receipt-pop 0.32s cubic-bezier(0.34, 1.56, 0.64, 1) both',
            pointerEvents:'none',
          }}>
            +{receipt.cp} GP
          </div>
        )}

        {/* Back card — depth effect */}
        {index < QUICKFIRES.length - 1 && (
          <div style={{
            position:'absolute', inset:0, borderRadius:16,
            background:'var(--bg-elev)', border:'1px solid var(--line)',
            transform:'translateY(8px) scale(0.96)',
            zIndex:0,
          }}/>
        )}

        {/* Current card */}
        <div style={{position:'relative', zIndex:1}}>
          <QuickfireCard
            item={QUICKFIRES[index]}
            onAnswer={answer}
            slideStyle={slideStyle}
          />
        </div>
      </div>
    </div>
  );
}

/* ── Hero mission ──────────────────────────────────── */

function HeroMission({ m, onStart }) {
  const photo = poiPhoto(m.seed, 800, 500);
  const accent = m.rare ? 'var(--status-rare)' : 'var(--teal)';

  return (
    <div className="hero-mission" onClick={() => onStart && onStart(m)}>
      <img src={photo} alt={m.title} className="photo-cover" style={{position:'absolute', inset:0}}/>
      <div className="photo-overlay"/>

      {m.rare && (
        <div style={{
          position:'absolute', top:14, left:14,
          background:'rgba(10,16,16,0.55)', backdropFilter:'blur(8px)',
          border:'1px solid rgba(255,255,255,0.18)',
          borderRadius:6, padding:'4px 10px',
          display:'inline-flex', alignItems:'center', gap:5,
        }}>
          <span style={{width:5, height:5, borderRadius:'50%', background:'var(--status-rare)', display:'inline-block'}}/>
          <span style={{fontFamily:'var(--font-mono)', fontSize:9, color:'#fff', letterSpacing:'0.06em', textTransform:'uppercase'}}>
            {m.rareLabel}
          </span>
        </div>
      )}

      <div style={{position:'absolute', bottom:0, left:0, right:0, padding:'16px 18px'}}>
        <div style={{fontSize:22, fontWeight:700, color:'#fff', letterSpacing:'-0.02em', lineHeight:1.15, marginBottom:8}}>
          {m.title}
        </div>
        <div style={{display:'flex', alignItems:'center', gap:8}}>
          <TypeChip type={m.type}/>
          <span style={{
            background:accent, borderRadius:6, padding:'4px 10px',
            fontSize:12, color:'#fff', fontWeight:600,
          }}>+{m.cp} GP</span>
          <span style={{marginLeft:'auto', fontSize:13, color:'rgba(255,255,255,0.7)', fontWeight:500}}>
            {m.distance} away
          </span>
        </div>
      </div>
    </div>
  );
}

/* ── Mission list card ─────────────────────────────── */

function MissionCard({ m, onStart }) {
  const photo = poiPhoto(m.seed, 160, 160);
  const accent = m.rare ? 'var(--status-rare)' : 'var(--teal)';

  return (
    <div className="mission-card" data-rare={m.rare} onClick={() => onStart && onStart(m)}>
      <div style={{
        width:72, height:72, borderRadius:10, overflow:'hidden', flexShrink:0,
        background:'var(--bg-sunk)', position:'relative',
      }}>
        <img src={photo} alt={m.title} className="photo-cover" style={{position:'absolute', inset:0}}/>
        {m.rare && (
          <div style={{
            position:'absolute', inset:0, borderRadius:10,
            background:`linear-gradient(135deg, ${accent}44, transparent)`,
          }}/>
        )}
      </div>

      <div style={{flex:1, minWidth:0, display:'flex', flexDirection:'column', gap:5, paddingTop:2}}>
        <TypeChip type={m.type}/>
        <div style={{
          fontSize:15, fontWeight:600, letterSpacing:'-0.015em', color:'var(--ink)',
          lineHeight:1.2, whiteSpace:'nowrap', overflow:'hidden', textOverflow:'ellipsis',
        }}>{m.title}</div>
        <div style={{fontSize:12, color:'var(--ink-3)', whiteSpace:'nowrap', overflow:'hidden', textOverflow:'ellipsis'}}>
          {m.hint}
        </div>
        <div style={{display:'flex', alignItems:'center', gap:6, marginTop:2}}>
          <Pill tone="teal">+{m.cp} GP</Pill>
          {m.rare && <Pill tone="rare">◇ {m.rareLabel}</Pill>}
          <span style={{marginLeft:'auto', fontSize:12, color:'var(--ink-3)'}}>{m.distance}</span>
        </div>
      </div>
    </div>
  );
}

/* ── Route mission card — separate, distinct ───────── */

function RouteMissionCard({ m, onStart }) {
  const photo = poiPhoto(m.seed, 160, 160);
  const stops = parseInt(m.title.match(/(\d+)/)?.[1]) || 4;

  return (
    <div onClick={() => onStart && onStart(m)} style={{
      background:'var(--bg-elev)',
      border:'1.5px solid oklch(0.88 0.07 65)',
      borderRadius:14, padding:12,
      display:'flex', gap:12, cursor:'pointer',
      position:'relative', overflow:'hidden',
    }}>
      {/* amber top stripe */}
      <div style={{
        position:'absolute', top:0, left:0, right:0, height:2,
        background:'var(--status-stale)', borderRadius:'14px 14px 0 0',
      }}/>

      <div style={{
        width:72, height:72, borderRadius:10, overflow:'hidden',
        flexShrink:0, position:'relative', background:'var(--bg-sunk)',
      }}>
        <img src={photo} alt={m.title} className="photo-cover" style={{position:'absolute', inset:0}}/>
      </div>

      <div style={{flex:1, minWidth:0, paddingTop:6}}>
        <div style={{display:'flex', alignItems:'center', gap:8, marginBottom:5}}>
          <span style={{fontSize:12, fontWeight:600, color:'var(--status-stale)'}}>Route</span>
          {/* Stop dots */}
          <div style={{display:'flex', gap:3, alignItems:'center'}}>
            {Array.from({length: stops}).map((_, i) => (
              <div key={i} style={{
                width:6, height:6, borderRadius:'50%',
                background: i === 0 ? 'var(--status-stale)' : 'transparent',
                border: '1.5px solid var(--status-stale)',
              }}/>
            ))}
          </div>
        </div>
        <div style={{
          fontSize:15, fontWeight:600, letterSpacing:'-0.01em',
          marginBottom:3,
          whiteSpace:'nowrap', overflow:'hidden', textOverflow:'ellipsis',
        }}>{m.title}</div>
        <div style={{fontSize:12, color:'var(--ink-3)', marginBottom:7}}>{m.hint}</div>
        <div style={{display:'flex', gap:6, alignItems:'center'}}>
          <Pill tone="teal">+{m.cp} GP</Pill>
          <span style={{fontSize:12, color:'var(--ink-3)'}}>{m.distance} · ~45 min</span>
        </div>
      </div>
    </div>
  );
}

/* ── Level badge ───────────────────────────────────── */

function LevelBadge() {
  const pct = 0.736;
  const r = 30;
  const circ = 2 * Math.PI * r;
  const filled = circ * pct;

  return (
    <div style={{
      background:'var(--bg-elev)', border:'1px solid var(--line)',
      borderRadius:16, padding:'14px 16px', marginBottom:18,
      display:'flex', alignItems:'center', gap:16,
    }}>
      <div style={{flexShrink:0}}>
        <svg width={76} height={76}>
          <circle cx={38} cy={38} r={r} className="level-ring-track"/>
          <circle cx={38} cy={38} r={r} className="level-ring-fill"
            strokeDasharray={`${filled} ${circ}`}
            transform="rotate(-90 38 38)"/>
          <text x={38} y={32} textAnchor="middle"
            fontFamily="var(--font-mono)" fontSize={10} fontWeight={600}
            fill="var(--ink-3)" letterSpacing="0.04em">LV</text>
          <text x={38} y={49} textAnchor="middle"
            fontFamily="var(--font-mono)" fontSize={18} fontWeight={700}
            fill="var(--ink)">04</text>
        </svg>
      </div>
      <div style={{flex:1, minWidth:0}}>
        <div style={{fontSize:16, fontWeight:600, letterSpacing:'-0.01em', marginBottom:3}}>Mapper</div>
        <div style={{fontSize:13, color:'var(--ink-3)', marginBottom:8, lineHeight:1.4}}>
          <strong style={{color:'var(--ink)', fontWeight:600}}>3 missions</strong> to Pathfinder
        </div>
        <div style={{height:3, background:'var(--bg-sunk)', borderRadius:2, overflow:'hidden', marginBottom:4}}>
          <div style={{height:'100%', width:'73.6%', background:'var(--teal)', borderRadius:2}}/>
        </div>
        <div style={{fontSize:11, color:'var(--ink-3)'}}>1,840 / 2,500 GP</div>
      </div>
    </div>
  );
}

function HomeHeader({ onProfileTap }) {
  return (
    <div style={{padding:'4px 20px 16px'}}>
      <div style={{display:'flex', justifyContent:'space-between', alignItems:'center', marginBottom:16}}>
        <div>
          <div style={{fontSize:26, fontWeight:700, letterSpacing:'-0.025em', lineHeight:1.1}}>
            Daikanyama
          </div>
          <div style={{fontSize:13, color:'var(--ink-3)', marginTop:3}}>
            12 missions · 4 hidden gems
          </div>
        </div>
        <button onClick={onProfileTap} style={{
          background:'transparent', border:0, padding:0,
          display:'flex', alignItems:'center', gap:9, cursor:'pointer',
          color:'var(--ink)', fontFamily:'var(--font-sans)',
        }}>
          <span style={{fontSize:13, fontWeight:600}}>Ren</span>
          <span style={{
            width:42, height:42, borderRadius:'50%',
            background:'var(--ink)', color:'var(--bg)',
            display:'flex', alignItems:'center', justifyContent:'center',
            fontSize:15, fontWeight:700,
          }}>R</span>
        </button>
      </div>
      <LevelBadge/>
    </div>
  );
}

function ModeToggle({ mode, setMode }) {
  return (
    <div style={{display:'inline-flex', background:'var(--bg-sunk)', borderRadius:999, padding:3}}>
      {['mission', 'discovery'].map(m => (
        <button key={m} onClick={() => setMode(m)} style={{
          background: mode === m ? 'var(--bg-elev)' : 'transparent',
          border:0, padding:'6px 16px', borderRadius:999,
          fontFamily:'var(--font-sans)', fontSize:13,
          fontWeight: mode === m ? 600 : 400,
          color: mode === m ? 'var(--ink)' : 'var(--ink-3)',
          cursor:'pointer',
          boxShadow: mode === m ? '0 1px 3px rgba(0,0,0,0.06)' : 'none',
        }}>
          {m === 'mission' ? 'Missions' : 'Discover'}
        </button>
      ))}
    </div>
  );
}

/* ── Type chip row ─────────────────────────────────── */

const TYPE_LABELS = {
  verify: 'Verify', 'first-look': 'First Look',
  refresh: 'Refresh', angle: 'Angle',
  'map-drop': 'Map Drop',
};

function ReferralSprintCard() {
  const tiers = [
    {label:'1', name:'Local Connector', active:true},
    {label:'5', name:'Hidden Layer', active:false},
    {label:'10', name:'City Connector', active:false},
  ];

  return (
    <div style={{
      border:'1px solid var(--line)', borderRadius:14,
      background:'var(--bg-elev)', padding:14, marginBottom:14,
    }}>
      <div style={{display:'flex', alignItems:'flex-start', justifyContent:'space-between', gap:12, marginBottom:12}}>
        <div>
          <div className="mono" style={{fontSize:9, color:'var(--teal-ink)', marginBottom:4}}>REFERRAL SPRINT</div>
          <div style={{fontSize:16, fontWeight:700, letterSpacing:'-0.015em'}}>Pioneer Pass · 9 days left</div>
        </div>
        <Pill tone="teal">2 / 5 verified</Pill>
      </div>
      <div style={{height:5, background:'var(--bg-sunk)', borderRadius:999, overflow:'hidden', marginBottom:12}}>
        <div style={{height:'100%', width:'40%', background:'var(--teal)', borderRadius:999}}/>
      </div>
      <div style={{display:'grid', gridTemplateColumns:'repeat(3, 1fr)', gap:7, marginBottom:12}}>
        {tiers.map(t => (
          <div key={t.label} style={{
            minHeight:58, borderRadius:10, padding:8,
            background:t.active ? 'var(--teal-soft)' : 'var(--bg-sunk)',
            border:'1px solid ' + (t.active ? 'transparent' : 'var(--line)'),
          }}>
            <div style={{fontFamily:'var(--font-mono)', fontSize:12, fontWeight:800, color:t.active ? 'var(--teal-ink)' : 'var(--ink-3)'}}>
              {t.label}
            </div>
            <div style={{fontSize:10.5, color:t.active ? 'var(--teal-ink)' : 'var(--ink-3)', lineHeight:1.25, marginTop:3}}>
              {t.name}
            </div>
          </div>
        ))}
      </div>
      <div style={{display:'grid', gridTemplateColumns:'1fr auto', gap:8}}>
        <div style={{
          height:40, borderRadius:10, background:'var(--bg-sunk)', border:'1px dashed var(--line-2)',
          display:'flex', alignItems:'center', padding:'0 12px',
          fontFamily:'var(--font-mono)', fontSize:11, color:'var(--ink-3)',
        }}>govibe.app/r/ren-72h</div>
        <button className="btn-secondary" style={{width:74, height:40}}>Share</button>
      </div>
      <div style={{fontSize:11, color:'var(--ink-3)', lineHeight:1.35, marginTop:9}}>
        Rewards unlock after your friend completes one GPS and photo verified place.
      </div>
    </div>
  );
}

function CommunityOpsCard() {
  const rows = [
    ['City Scout', '20 verifies + 5 recent sessions', 'Eligible'],
    ['City Captain', '25 referrals or Challenge Top 3', 'Next'],
    ['Ambassador', '6 months captain KPI', 'Locked'],
  ];

  return (
    <div style={{
      border:'1px solid var(--line)', borderRadius:14,
      background:'var(--bg-elev)', padding:14, marginBottom:18,
    }}>
      <div style={{display:'flex', justifyContent:'space-between', alignItems:'baseline', marginBottom:12}}>
        <div>
          <div className="mono" style={{fontSize:9, color:'var(--status-rare)', marginBottom:4}}>COMMUNITY OPS</div>
          <div style={{fontSize:16, fontWeight:700, letterSpacing:'-0.015em'}}>Toronto crew ladder</div>
        </div>
        <span className="label">HQ kit ready</span>
      </div>
      <div style={{display:'grid', gap:8, marginBottom:12}}>
        {rows.map((row, i) => (
          <div key={row[0]} style={{
            display:'grid', gridTemplateColumns:'82px 1fr auto', gap:8, alignItems:'center',
            padding:'9px 0', borderBottom:i === rows.length - 1 ? 'none' : '1px dashed var(--line)',
          }}>
            <div style={{fontSize:12, fontWeight:700}}>{row[0]}</div>
            <div style={{fontSize:11.5, color:'var(--ink-3)', lineHeight:1.25}}>{row[1]}</div>
            <Pill tone={row[2] === 'Eligible' ? 'teal' : undefined}>{row[2]}</Pill>
          </div>
        ))}
      </div>
      <div style={{display:'grid', gridTemplateColumns:'1fr 1fr', gap:8}}>
        <button className="btn-secondary" style={{height:40}}>Scout Day kit</button>
        <button className="btn-secondary" style={{height:40}}>Captain channel</button>
      </div>
    </div>
  );
}

function isMapDropQualified(access = MAP_DROP_ACCESS) {
  return access.completedPoi >= access.requiredPoi || access.districtRank <= access.rankCutoff;
}

function MapDropPromoCard({ mission, onOpenMap }) {
  const qualified = isMapDropQualified();
  const remaining = Math.max(0, MAP_DROP_ACCESS.requiredPoi - MAP_DROP_ACCESS.completedPoi);

  return (
    <div style={{
      border:'1.5px solid ' + (qualified ? 'var(--warm)' : 'var(--line)'),
      borderRadius:14, background:'var(--bg-elev)', padding:14,
      display:'grid', gap:12,
    }}>
      <div style={{display:'flex', justifyContent:'space-between', alignItems:'flex-start', gap:12}}>
        <div>
          <div className="mono" style={{fontSize:9, color:'var(--warm)', marginBottom:5}}>MAP DROP</div>
          <div style={{fontSize:16, fontWeight:700, letterSpacing:'-0.015em'}}>{mission.title}</div>
          <div style={{fontSize:12, color:'var(--ink-3)', marginTop:3}}>
            {qualified ? mission.hint : `${remaining} more POI missions or Top 100 district rank`}
          </div>
        </div>
        <Pill tone={qualified ? 'rare' : undefined}>{qualified ? 'Eligible' : 'Locked'}</Pill>
      </div>

      <div style={{display:'grid', gridTemplateColumns:'1fr 1fr', gap:8}}>
        <div style={{background:'var(--bg-sunk)', borderRadius:10, padding:10}}>
          <div style={{fontSize:10.5, color:'var(--ink-3)', marginBottom:5}}>POI missions</div>
          <div style={{fontFamily:'var(--font-mono)', fontSize:15, fontWeight:800}}>
            {MAP_DROP_ACCESS.completedPoi}/{MAP_DROP_ACCESS.requiredPoi}
          </div>
        </div>
        <div style={{background:'var(--bg-sunk)', borderRadius:10, padding:10}}>
          <div style={{fontSize:10.5, color:'var(--ink-3)', marginBottom:5}}>District rank</div>
          <div style={{fontFamily:'var(--font-mono)', fontSize:15, fontWeight:800}}>
            #{MAP_DROP_ACCESS.districtRank}
          </div>
        </div>
      </div>

      <button className={qualified ? 'btn-primary' : 'btn-secondary'} style={{height:40}} onClick={onOpenMap}>
        {qualified ? 'Open Map Drop' : 'View unlock progress'}
      </button>
    </div>
  );
}

function TypeFilterChips({ types, selected, onSelect }) {
  return (
    <div style={{display:'flex', gap:6, overflowX:'auto', paddingBottom:2}}>
      {types.map(({ type, count }) => (
        <button key={type} onClick={() => onSelect(type)} style={{
          padding:'6px 12px', borderRadius:999, whiteSpace:'nowrap', flexShrink:0,
          border: selected === type ? '1.5px solid var(--ink)' : '1px solid var(--line)',
          background: selected === type ? 'var(--ink)' : 'transparent',
          color: selected === type ? 'var(--bg)' : 'var(--ink-2)',
          fontFamily:'var(--font-sans)', fontSize:12,
          fontWeight: selected === type ? 600 : 400,
          cursor:'pointer',
        }}>
          {TYPE_LABELS[type] || type} · {count}
        </button>
      ))}
    </div>
  );
}

function MiniMap() {
  return (
    <div style={{
      position:'relative', height:130, borderRadius:12,
      border:'1px solid var(--line)', background:'var(--bg-elev)', overflow:'hidden',
    }}>
      <svg viewBox="0 0 350 130" width="100%" height="100%" preserveAspectRatio="none" style={{position:'absolute', inset:0}}>
        <defs>
          <pattern id="mmgrid" width="20" height="20" patternUnits="userSpaceOnUse">
            <path d="M 20 0 L 0 0 0 20" fill="none" stroke="var(--line)" strokeWidth="0.5"/>
          </pattern>
        </defs>
        <rect width="350" height="130" fill="url(#mmgrid)"/>
        <path d="M 0 80 Q 80 65 180 75 T 350 55" stroke="var(--line-2)" strokeWidth="1.5" fill="none"/>
        <path d="M 110 0 Q 120 45 135 80 T 155 130" stroke="var(--line-2)" strokeWidth="1.5" fill="none"/>
        <path d="M 230 0 L 240 130" stroke="var(--line-2)" strokeWidth="1.5" fill="none"/>
        <path d="M 30 55 Q 80 45 100 65 Q 105 90 70 92 Q 30 88 30 55Z" fill="var(--status-fresh)" opacity="0.12"/>
      </svg>
      <div style={{
        position:'absolute', left:'44%', top:'50%',
        width:10, height:10, borderRadius:'50%', background:'var(--ink)',
        boxShadow:'0 0 0 5px var(--bg-elev), 0 0 0 6px var(--ink)',
        transform:'translate(-50%,-50%)',
      }}/>
      {[
        {x:'28%', y:'30%', c:'var(--status-done)'},
        {x:'65%', y:'38%', c:'var(--status-rare)'},
        {x:'22%', y:'68%', c:'var(--status-stale)'},
        {x:'76%', y:'65%', c:'var(--status-done)'},
        {x:'52%', y:'22%', c:'var(--status-rare)'},
      ].map((p, i) => (
        <div key={i} style={{
          position:'absolute', left:p.x, top:p.y,
          width:8, height:8, borderRadius:'50%',
          background:p.c, transform:'translate(-50%,-50%)',
          boxShadow:'0 0 0 2px var(--bg-elev)',
        }}/>
      ))}
      <button style={{
        position:'absolute', bottom:8, right:8,
        background:'var(--bg-elev)', border:'1px solid var(--line)',
        borderRadius:6, padding:'5px 10px',
        fontFamily:'var(--font-sans)', fontSize:11, fontWeight:500,
        color:'var(--ink)', cursor:'pointer',
      }}>Open map →</button>
    </div>
  );
}

/* ── Explore: global map + contribution bottom sheet ─ */

const MAP_LAYERS = [
  { id:'default-2d', label:'Default 2D', short:'2D' },
  { id:'mesh-3d', label:'Contribution mesh', short:'3D' },
  { id:'progress-heatmap', label:'Progress heatmap', short:'Heat' },
];

const MAP_POIS = [
  {
    id:'m1', x:'34%', y:'54%', status:'available', label:'Mori Tea House', type:'Verify hours', gp:40,
    mesh:'User-built mesh · 76% complete', progress:'14 accepted updates', owner:true,
  },
  {
    id:'m2', x:'23%', y:'31%', status:'gap', label:'Sarugakucho 14-3', type:'Add place', gp:75,
    mesh:'No mesh shell yet', progress:'New POI candidate', owner:false,
  },
  {
    id:'m3', x:'66%', y:'36%', status:'stale', label:'Onibus Coffee', type:'Photo update', gp:30,
    mesh:'Facade mesh · 42% complete', progress:'7 stale visual nodes', owner:false,
  },
  {
    id:'m4', x:'74%', y:'61%', status:'campaign', label:'Retail survey', type:'Campaign', gp:90,
    mesh:'Commissioned building scan', progress:'3x GP campaign zone', owner:false,
  },
  {
    id:'m5', x:'49%', y:'75%', status:'verified', label:'Ivy Place', type:'Verified', gp:0,
    mesh:'Complete mesh · trusted', progress:'District anchor', owner:true,
  },
];

function MapLayerIcon({ id, active }) {
  const stroke = active ? 'var(--bg)' : 'currentColor';
  if (id === 'mesh-3d') {
    return (
      <svg width="20" height="20" viewBox="0 0 20 20" fill="none">
        <path d="M4 7 L10 3 L16 7 L10 11 Z" stroke={stroke} strokeWidth="1.4" fill="none"/>
        <path d="M4 7 V13 L10 17 L16 13 V7 M10 11 V17" stroke={stroke} strokeWidth="1.4"/>
      </svg>
    );
  }
  if (id === 'progress-heatmap') {
    return (
      <svg width="20" height="20" viewBox="0 0 20 20" fill="none">
        {[0,1,2].map((r) => [0,1,2].map((c) => (
          <rect key={`${r}-${c}`} x={4 + c * 4.3} y={4 + r * 4.3} width="3.2" height="3.2" rx="0.8"
            fill={active ? 'var(--bg)' : 'currentColor'} opacity={0.35 + (r + c) * 0.12}/>
        )))}
      </svg>
    );
  }
  return (
    <svg width="20" height="20" viewBox="0 0 20 20" fill="none">
      <path d="M3 5 L8 3 L13 5 L17 3 V15 L12 17 L7 15 L3 17 Z" stroke={stroke} strokeWidth="1.35" fill="none"/>
      <path d="M8 3 V15 M13 5 V17" stroke={stroke} strokeWidth="1.35"/>
    </svg>
  );
}

function MapFloatingControls({ tier, layer, setLayer, zoom, setZoom, onReset }) {
  const [open, setOpen] = React.useState(false);

  if (tier === 'full') return null;

  function changeLayer(id) {
    setLayer(id);
    setOpen(false);
  }

  return (
    <>
      <div style={{
        position:'absolute', right:16, bottom:tier === 'peek' ? 116 : tier === 'half' ? 'calc(42% + 16px)' : 'calc(92% - 52px)',
        zIndex:11, display:'grid', gap:8,
        transition:'bottom .28s cubic-bezier(0.22, 1, 0.36, 1)',
      }}>
        <button className="map-fab" data-tier={tier} aria-label="Select map layer" onClick={() => setOpen(o => !o)} style={{position:'static'}}>
          <MapLayerIcon id={layer}/>
        </button>
        <button className="map-fab" data-tier={tier} aria-label="Current location" onClick={onReset} style={{position:'static'}}>◎</button>
      </div>

      {open && (
        <div style={{
          position:'absolute', right:66, bottom:tier === 'peek' ? 116 : tier === 'half' ? 'calc(42% + 16px)' : 'calc(92% - 52px)',
          zIndex:13, width:188, border:'1px solid var(--line)', borderRadius:14,
          background:'var(--bg-elev)', boxShadow:'0 14px 36px rgba(20,32,31,0.14)',
          padding:8, display:'grid', gap:6,
          transition:'bottom .28s cubic-bezier(0.22, 1, 0.36, 1)',
        }}>
          {MAP_LAYERS.map(item => (
            <button key={item.id} onClick={() => changeLayer(item.id)} style={{
              border:0, borderRadius:10, minHeight:42, padding:'7px 9px',
              background:layer === item.id ? 'var(--ink)' : 'transparent',
              color:layer === item.id ? 'var(--bg)' : 'var(--ink)',
              display:'grid', gridTemplateColumns:'24px 1fr', gap:9, alignItems:'center',
              textAlign:'left', fontFamily:'var(--font-sans)', cursor:'pointer',
            }}>
              <MapLayerIcon id={item.id} active={layer === item.id}/>
              <span style={{fontSize:12.5, fontWeight:700, lineHeight:1.15}}>{item.label}</span>
            </button>
          ))}
        </div>
      )}

      <div style={{
        position:'absolute', left:16, bottom:tier === 'peek' ? 116 : tier === 'half' ? 'calc(42% + 16px)' : 'calc(92% - 52px)',
        zIndex:11, display:'grid', gap:6,
        transition:'bottom .28s cubic-bezier(0.22, 1, 0.36, 1)',
      }}>
        <button aria-label="Zoom in" onClick={() => setZoom(z => Math.min(1.45, +(z + 0.12).toFixed(2)))} style={{
          width:36, height:36, borderRadius:10, border:'1px solid var(--line)', background:'var(--bg-elev)',
          boxShadow:'0 8px 22px rgba(20,32,31,0.08)', fontSize:18, cursor:'pointer',
        }}>+</button>
        <button aria-label="Zoom out" onClick={() => setZoom(z => Math.max(0.78, +(z - 0.12).toFixed(2)))} style={{
          width:36, height:36, borderRadius:10, border:'1px solid var(--line)', background:'var(--bg-elev)',
          boxShadow:'0 8px 22px rgba(20,32,31,0.08)', fontSize:20, cursor:'pointer',
        }}>−</button>
      </div>
    </>
  );
}

function ExploreMapCanvas({ selectedMission, onSelectMission, layer, zoom = 1, resetKey }) {
  const [pan, setPan] = React.useState({ x:0, y:0 });
  const drag = React.useRef(null);
  const color = (status) => ({
    available:'var(--teal)',
    gap:'var(--status-rare)',
    stale:'var(--status-stale)',
    campaign:'var(--warm)',
    verified:'var(--ink-4)',
  })[status] || 'var(--teal)';

  React.useEffect(() => {
    setPan({ x:0, y:0 });
  }, [resetKey, layer]);

  function startDrag(e) {
    drag.current = { x:e.clientX, y:e.clientY, startX:pan.x, startY:pan.y };
    if (e.currentTarget.setPointerCapture && e.pointerId !== undefined) {
      e.currentTarget.setPointerCapture(e.pointerId);
    }
  }

  function moveDrag(e) {
    if (!drag.current) return;
    setPan({
      x: drag.current.startX + e.clientX - drag.current.x,
      y: drag.current.startY + e.clientY - drag.current.y,
    });
  }

  function endDrag() {
    drag.current = null;
  }

  const mapTransform = `translate(${pan.x}px, ${pan.y}px) scale(${zoom})`;
  const imageLayer = layer === 'mesh-3d'
    ? 'uploads/contribution-highlight-map-background-only.png'
    : layer === 'progress-heatmap'
      ? 'uploads/zoomout-square-tile-progress-background-only.png'
      : null;

  return (
    <div
      onPointerDown={startDrag}
      onPointerMove={moveDrag}
      onPointerUp={endDrag}
      onPointerCancel={endDrag}
      style={{position:'absolute', inset:0, overflow:'hidden', background:'var(--bg-elev)', touchAction:'none', cursor:drag.current ? 'grabbing' : 'grab'}}
    >
      <div style={{
        position:'absolute', inset:'-10%',
        transform:mapTransform, transformOrigin:'50% 48%',
        transition:drag.current ? 'none' : 'transform .18s ease',
      }}>
        {imageLayer ? (
          <img src={imageLayer} alt={MAP_LAYERS.find(l => l.id === layer)?.label || 'Map layer'} className="photo-cover" style={{
            position:'absolute', inset:0, width:'100%', height:'100%',
            objectFit:'cover', opacity:layer === 'mesh-3d' ? 0.94 : 0.9,
            filter:layer === 'mesh-3d' ? 'saturate(1.05) contrast(1.02)' : 'saturate(1.04) contrast(0.98)',
          }}/>
        ) : (
          <svg viewBox="0 0 390 760" width="100%" height="100%" preserveAspectRatio="none" style={{position:'absolute', inset:0}}>
            <defs>
              <pattern id="explore-mapgrid" width="24" height="24" patternUnits="userSpaceOnUse">
                <path d="M 24 0 L 0 0 0 24" fill="none" stroke="var(--line)" strokeWidth="0.55"/>
              </pattern>
            </defs>
            <rect width="390" height="760" fill="url(#explore-mapgrid)"/>
            <path d="M -20 168 Q 80 138 176 188 T 410 166" stroke="var(--line-2)" strokeWidth="2" fill="none"/>
            <path d="M -20 466 Q 92 430 210 452 T 420 428" stroke="var(--line-2)" strokeWidth="2" fill="none"/>
            <path d="M 92 -20 Q 102 210 130 440 T 158 780" stroke="var(--line-2)" strokeWidth="2" fill="none"/>
            <path d="M 284 -20 Q 294 210 270 444 T 304 780" stroke="var(--line-2)" strokeWidth="2" fill="none"/>
            <path d="M 34 318 Q 102 294 146 338 Q 156 400 104 412 Q 44 398 34 318 Z"
              fill="oklch(0.92 0.04 155)" opacity="0.42"/>
            {[['88','292','70','92'], ['218','244','88','76'], ['246','492','70','98'], ['98','502','94','72']].map((r, i) => (
              <rect key={i} x={r[0]} y={r[1]} width={r[2]} height={r[3]} rx="10" fill="var(--bg-elev)" stroke="var(--line)" opacity="0.86"/>
            ))}
          </svg>
        )}

        {layer === 'mesh-3d' && (
          <div style={{
            position:'absolute', left:'15%', right:'10%', top:'30%', height:270,
            background:'radial-gradient(circle at 35% 45%, color-mix(in oklch, var(--teal), transparent 68%), transparent 58%), radial-gradient(circle at 72% 56%, color-mix(in oklch, var(--warm), transparent 68%), transparent 55%)',
            filter:'blur(2px)', opacity:0.68, pointerEvents:'none',
          }}/>
        )}

        <div style={{
          position:'absolute', left:'42%', top:'49%',
          width:14, height:14, borderRadius:'50%', background:'var(--ink)',
          boxShadow:'0 0 0 4px var(--bg-elev), 0 0 0 5px var(--ink), 0 0 0 18px rgba(20,32,31,0.06)',
          transform:'translate(-50%,-50%)', zIndex:4,
        }}/>

        {MAP_POIS.map(pin => (
          <button
            key={pin.id}
            onPointerDown={e => e.stopPropagation()}
            onClick={() => onSelectMission && onSelectMission(pin)}
            style={{
              position:'absolute', left:pin.x, top:pin.y, transform:'translate(-50%,-50%)',
              border:0, background:'transparent', padding:0, cursor:'pointer', zIndex:5,
            }}
          >
            <span style={{
              display:'block', width:selectedMission?.id === pin.id ? 22 : 16, height:selectedMission?.id === pin.id ? 22 : 16,
              borderRadius:layer === 'mesh-3d' ? 5 : '50%',
              background:color(pin.status), border:'2px solid var(--bg-elev)',
              boxShadow:selectedMission?.id === pin.id ? `0 0 0 6px color-mix(in oklch, ${color(pin.status)}, transparent 68%)` : '0 4px 12px rgba(20,32,31,0.14)',
              transform:layer === 'mesh-3d' ? 'rotate(45deg)' : 'none',
            }}/>
            {pin.status === 'campaign' && (
              <span style={{
                position:'absolute', top:18, left:'50%', transform:'translateX(-50%)',
                background:'var(--bg-elev)', border:'1px solid var(--line)', borderRadius:999,
                padding:'2px 7px', fontFamily:'var(--font-mono)', fontSize:8.5,
                color:'var(--warm)', whiteSpace:'nowrap', fontWeight:700,
              }}>3x GP</span>
            )}
          </button>
        ))}
      </div>
    </div>
  );
}

function SearchOverlayCard({ onFocus }) {
  return (
    <div style={{
      position:'absolute', left:16, right:16, top:8, zIndex:10,
      display:'grid', gap:8,
    }}>
      <button aria-label="Search places" style={{
        height:44, background:'var(--bg-elev)', border:'1px solid var(--line)',
        borderRadius:13, display:'flex', alignItems:'center', gap:10, padding:'0 12px',
        boxShadow:'0 8px 24px rgba(20,32,31,0.08)',
        cursor:'text', width:'100%', fontFamily:'var(--font-sans)',
      }} onClick={onFocus}>
        <svg width="15" height="15" viewBox="0 0 15 15" fill="none">
          <circle cx="6.5" cy="6.5" r="4.7" stroke="var(--ink-3)" strokeWidth="1.4"/>
          <path d="M10 10 L13.2 13.2" stroke="var(--ink-3)" strokeWidth="1.4" strokeLinecap="round"/>
        </svg>
        <span style={{fontSize:14, color:'var(--ink-3)', flex:1}}>Search places, campaigns, data gaps</span>
        <span style={{
          width:28, height:28, borderRadius:'50%', background:'var(--ink)', color:'var(--bg)',
          display:'grid', placeItems:'center', fontSize:12, fontWeight:700,
        }}>R</span>
      </button>
      <div style={{display:'flex', gap:6, overflowX:'auto'}}>
        {['Restaurants', 'Cafes', 'Parks', 'Needs photos', 'Unverified hours'].map(label => (
          <button key={label} style={{
            flexShrink:0, border:'1px solid var(--line)', background:'var(--bg-elev)', color:'var(--ink-2)',
            borderRadius:999, padding:'6px 10px', fontFamily:'var(--font-sans)', fontSize:11.5, cursor:'pointer',
          }}>{label}</button>
        ))}
      </div>
    </div>
  );
}

const SHEET_TIERS = ['peek', 'half', 'full'];

function nextSheetTier(tier) {
  const i = SHEET_TIERS.indexOf(tier);
  return SHEET_TIERS[Math.min(SHEET_TIERS.length - 1, i + 1)] || 'half';
}

function prevSheetTier(tier) {
  const i = SHEET_TIERS.indexOf(tier);
  return SHEET_TIERS[Math.max(0, i - 1)] || 'half';
}

function cycleSheetTier(tier) {
  return tier === 'full' ? 'half' : nextSheetTier(tier);
}

function MapBottomSheet({ tier = 'half', setTier, summary, meta, action, children }) {
  const drag = React.useRef(null);
  const suppressClick = React.useRef(false);

  function yFromEvent(e) {
    return e.clientY ?? e.touches?.[0]?.clientY ?? 0;
  }

  function startDrag(e) {
    drag.current = { y: yFromEvent(e), moved: false };
    if (e.currentTarget.setPointerCapture && e.pointerId !== undefined) {
      e.currentTarget.setPointerCapture(e.pointerId);
    }
  }

  function moveDrag(e) {
    if (!drag.current) return;
    if (Math.abs(yFromEvent(e) - drag.current.y) > 8) drag.current.moved = true;
  }

  function endDrag(e) {
    if (!drag.current) return;
    const dy = yFromEvent(e) - drag.current.y;
    const moved = drag.current.moved;
    drag.current = null;
    suppressClick.current = moved;

    if (dy < -28) setTier && setTier(nextSheetTier(tier));
    else if (dy > 28) setTier && setTier(prevSheetTier(tier));
  }

  function tapSheet() {
    if (suppressClick.current) {
      suppressClick.current = false;
      return;
    }
    setTier && setTier(cycleSheetTier(tier));
  }

  return (
    <div className="map-bottom-sheet" data-tier={tier}>
      <button
        className="map-sheet-grabber"
        aria-label="Change sheet tier"
        onPointerDown={startDrag}
        onPointerMove={moveDrag}
        onPointerUp={endDrag}
        onClick={tapSheet}
      >
        <span/>
      </button>
      {summary && (
        <button
          className="map-sheet-summary"
          onPointerDown={startDrag}
          onPointerMove={moveDrag}
          onPointerUp={endDrag}
          onClick={tapSheet}
        >
          <span>
            {meta && <span className="mono" style={{fontSize:9, color:'var(--teal-ink)', display:'block', marginBottom:4}}>{meta}</span>}
            <span className="map-sheet-summary-title">{summary}</span>
          </span>
          {action}
        </button>
      )}
      <div className="map-sheet-body">
        {children}
      </div>
    </div>
  );
}

function GlobalMapPanel({ activeTab, onTabChange, tier = 'full', setTier, summary, meta, action, children }) {
  const [layer, setLayer] = React.useState('default-2d');
  const [zoom, setZoom] = React.useState(1);
  const [resetKey, setResetKey] = React.useState(0);
  const [selectedMission, setSelectedMission] = React.useState(null);

  return (
    <>
      <StatusBar/>
      <div className="map-shell">
        <ExploreMapCanvas
          selectedMission={selectedMission}
          onSelectMission={setSelectedMission}
          layer={layer}
          zoom={zoom}
          resetKey={resetKey}
        />
        <SearchOverlayCard onFocus={() => tier === 'full' && setTier && setTier('half')}/>
        <MapFloatingControls
          tier={tier}
          layer={layer}
          setLayer={setLayer}
          zoom={zoom}
          setZoom={setZoom}
          onReset={() => { setZoom(1); setResetKey(k => k + 1); }}
        />
        <MapBottomSheet
          tier={tier}
          setTier={setTier}
          summary={summary}
          meta={meta}
          action={action}
        >
          {children}
        </MapBottomSheet>
      </div>
      <TabBar active={activeTab} onChange={onTabChange}/>
    </>
  );
}

function MissionCategoryRail({ active, onChange }) {
  const cats = [
    ['nearby', 'Nearby Missions', '12'],
    ['campaign', 'Campaign Tasks', '3x'],
    ['verify', 'Quick Verify', '5'],
    ['photo', 'Photo Update', '8'],
  ];
  return (
    <div style={{display:'flex', gap:8, overflowX:'auto', padding:'0 20px 14px'}}>
      {cats.map(c => (
        <button key={c[0]} onClick={() => onChange(c[0])} style={{
          flex:'0 0 132px', minHeight:58, borderRadius:13, padding:'10px 11px',
          border:active === c[0] ? '1.5px solid var(--ink)' : '1px solid var(--line)',
          background:active === c[0] ? 'var(--ink)' : 'var(--bg-elev)',
          color:active === c[0] ? 'var(--bg)' : 'var(--ink)',
          cursor:'pointer', textAlign:'left', fontFamily:'var(--font-sans)',
        }}>
          <div style={{fontSize:12.5, fontWeight:700, letterSpacing:'-0.01em'}}>{c[1]}</div>
          <div style={{fontFamily:'var(--font-mono)', fontSize:9, marginTop:5, opacity:0.72}}>{c[2]} OPEN</div>
        </button>
      ))}
    </div>
  );
}

function CampaignMissionCard({ onStart }) {
  const campaign = {
    type:'campaign',
    title:'Verify status of retail storefronts',
    hint:'Kensington Market campaign · platform verified',
    distance:'120m',
    cp:90,
    daysAgo:210,
    seed:'campaign-retail-kensington',
    rare:true,
    rareLabel:'Campaign',
  };
  return (
    <div style={{
      border:'1.5px solid var(--warm)', borderRadius:15, padding:14,
      background:'var(--bg-elev)', margin:'0 20px 12px',
      boxShadow:'0 8px 22px rgba(20,32,31,0.06)',
    }}>
      <div style={{display:'flex', justifyContent:'space-between', gap:12, marginBottom:10}}>
        <div>
          <div className="mono" style={{fontSize:9, color:'var(--warm)', marginBottom:4}}>SURVEY REQUEST</div>
          <div style={{fontSize:17, fontWeight:800, letterSpacing:'-0.02em', lineHeight:1.15}}>Retail status check</div>
        </div>
        <Pill tone="rare">3x GP</Pill>
      </div>
      <div style={{fontSize:12.5, color:'var(--ink-3)', lineHeight:1.45, marginBottom:12}}>
        Verify open/closed status for commissioned retail data. Guaranteed credit after AI validation.
      </div>
      <div style={{display:'grid', gridTemplateColumns:'1fr 1fr 1fr', gap:7, marginBottom:12}}>
        {[
          ['Distance', '120m'],
          ['Reward', '+90 GP'],
          ['Window', '5d 14h'],
        ].map(row => (
          <div key={row[0]} style={{background:'var(--bg-sunk)', borderRadius:10, padding:8}}>
            <div style={{fontSize:10.5, color:'var(--ink-3)', marginBottom:4}}>{row[0]}</div>
            <div style={{fontFamily:'var(--font-mono)', fontSize:12.5, fontWeight:800}}>{row[1]}</div>
          </div>
        ))}
      </div>
      <button className="btn-primary" style={{height:40}} onClick={() => onStart && onStart(campaign)}>Accept campaign mission</button>
    </div>
  );
}

function OpportunityCard({ title, body, reward, tone, onStart }) {
  return (
    <button onClick={onStart} style={{
      width:'100%', border:'1px solid var(--line)', borderRadius:13, background:'var(--bg-elev)',
      padding:12, display:'grid', gridTemplateColumns:'1fr auto', gap:10,
      textAlign:'left', cursor:'pointer', fontFamily:'var(--font-sans)',
    }}>
      <div>
        <div style={{fontSize:14, fontWeight:700, letterSpacing:'-0.01em', marginBottom:4}}>{title}</div>
        <div style={{fontSize:12, color:'var(--ink-3)', lineHeight:1.35}}>{body}</div>
      </div>
      <span style={{
        alignSelf:'start', borderRadius:999, padding:'4px 8px',
        background:tone === 'warm' ? 'var(--warm-soft)' : 'var(--teal-soft)',
        color:tone === 'warm' ? 'var(--warm)' : 'var(--teal-ink)',
        fontFamily:'var(--font-mono)', fontSize:9, fontWeight:800,
      }}>{reward}</span>
    </button>
  );
}

function ExploreLayerContent({ layer, selectedMission, category, setCategory, heroMission, nearbyMissions, cardStyle, onStart, onTabChange }) {
  if (layer === 'progress-heatmap') {
    return (
      <div style={{padding:'0 20px 22px', display:'grid', gap:12}}>
        <div className="sec-head" style={{marginBottom:0}}>
          <h3>Regional progress</h3>
          <span className="label">Daikanyama grid</span>
        </div>
        <div style={{border:'1px solid var(--line)', borderRadius:14, background:'var(--bg-elev)', padding:14}}>
          <div style={{display:'grid', gridTemplateColumns:'1fr 1fr 1fr', gap:8, marginBottom:12}}>
            {[
              ['Tiles live', '72%'],
              ['POI fresh', '61%'],
              ['Open gaps', '27'],
            ].map(row => (
              <div key={row[0]} style={{background:'var(--bg-sunk)', borderRadius:10, padding:10}}>
                <div style={{fontFamily:'var(--font-mono)', fontSize:15, fontWeight:800}}>{row[1]}</div>
                <div style={{fontSize:10.5, color:'var(--ink-3)', marginTop:4}}>{row[0]}</div>
              </div>
            ))}
          </div>
          <div style={{height:7, borderRadius:999, background:'var(--bg-sunk)', overflow:'hidden'}}>
            <div style={{height:'100%', width:'72%', borderRadius:999, background:'var(--teal)'}}/>
          </div>
        </div>
        <div className="sec-head" style={{marginBottom:0}}>
          <h3>Leaderboard</h3>
          <span className="label">this week</span>
        </div>
        {[
          ['Ren Tanaka', '28 updates', '+455 GP'],
          ['Aki Sato', '21 updates', '+320 GP'],
          ['Mika Chen', '18 updates', '+275 GP'],
        ].map((row, i) => (
          <div key={row[0]} style={{
            border:'1px solid var(--line)', borderRadius:13, background:'var(--bg-elev)',
            padding:12, display:'grid', gridTemplateColumns:'32px 1fr auto', gap:10, alignItems:'center',
          }}>
            <div style={{
              width:32, height:32, borderRadius:10, background:i === 0 ? 'var(--teal-soft)' : 'var(--bg-sunk)',
              color:i === 0 ? 'var(--teal-ink)' : 'var(--ink-3)', display:'grid', placeItems:'center',
              fontFamily:'var(--font-mono)', fontWeight:800,
            }}>{i + 1}</div>
            <div>
              <div style={{fontSize:14, fontWeight:800}}>{row[0]}</div>
              <div style={{fontSize:12, color:'var(--ink-3)', marginTop:2}}>{row[1]}</div>
            </div>
            <Pill tone={i === 0 ? 'teal' : undefined}>{row[2]}</Pill>
          </div>
        ))}
      </div>
    );
  }

  if (layer === 'mesh-3d') {
    const poi = selectedMission || MAP_POIS[0];
    return (
      <div style={{padding:'0 20px 22px', display:'grid', gap:12}}>
        <div style={{border:'1px solid var(--line)', borderRadius:14, background:'var(--bg-elev)', padding:14}}>
          <div className="mono" style={{fontSize:9, color:'var(--status-rare)', marginBottom:5}}>3D MESH STATUS</div>
          <div style={{fontSize:20, fontWeight:800, letterSpacing:'-0.025em', lineHeight:1.1}}>{poi.label}</div>
          <div style={{fontSize:12.5, color:'var(--ink-3)', marginTop:6, lineHeight:1.45}}>{poi.mesh}</div>
          <div style={{display:'grid', gridTemplateColumns:'repeat(3, 1fr)', gap:8, marginTop:12}}>
            {[
              ['Facade', poi.owner ? '92%' : '44%'],
              ['Interior', poi.owner ? '64%' : '18%'],
              ['Freshness', poi.status === 'stale' ? 'Low' : 'Good'],
            ].map(row => (
              <div key={row[0]} style={{background:'var(--bg-sunk)', borderRadius:10, padding:9}}>
                <div style={{fontSize:10.5, color:'var(--ink-3)', marginBottom:4}}>{row[0]}</div>
                <div style={{fontFamily:'var(--font-mono)', fontSize:13, fontWeight:800}}>{row[1]}</div>
              </div>
            ))}
          </div>
        </div>
        {poi.owner && (
          <div style={{border:'1px solid var(--teal)', borderRadius:13, background:'var(--teal-soft)', padding:12}}>
            <div style={{fontSize:13, fontWeight:800, color:'var(--teal-ink)'}}>Built by you</div>
            <div style={{fontSize:12, color:'var(--teal-ink)', lineHeight:1.4, marginTop:4}}>
              Your scans own the primary facade mesh. Add interior context to unlock the full building profile.
            </div>
          </div>
        )}
        <OpportunityCard
          title="Add missing mesh angles"
          body="Capture storefront corner, entrance depth, and sign plane for a stronger 3D record."
          reward={`+${poi.gp || 60} GP`}
          onStart={() => onStart && onStart({...poi, type:'add-photo', cp:poi.gp || 60})}
        />
      </div>
    );
  }

  return (
    <>
      <MissionCategoryRail active={category} onChange={setCategory}/>
      {selectedMission && <PoiDetailPanel poi={selectedMission} onStart={onStart}/>}
      <div style={{padding:'0 20px 22px'}}>
        <div className="sec-head" style={{marginBottom:10}}>
          <h3>{selectedMission ? 'Suggested updates' : category === 'verify' ? 'Quick verify' : category === 'photo' ? 'Photo updates' : 'Mission stack'}</h3>
          <span className="label">AI validated</span>
        </div>
        <div style={{display:'grid', gap:8}}>
          {!selectedMission && category !== 'campaign' && (
            <OpportunityCard
              title={heroMission.title}
              body="Missing storefront photo and hours verification. Recommended because you are 2 minutes away."
              reward="+75 GP"
              onStart={() => onStart && onStart({...heroMission, type:'add-place', cp:75})}
            />
          )}
          {selectedMission ? (
            <>
              <OpportunityCard
                title={`Refresh ${selectedMission.label}`}
                body={`${selectedMission.type}. Add current evidence, hours, and storefront details.`}
                reward={`+${selectedMission.gp || 30} GP`}
                onStart={() => onStart && onStart({
                  ...selectedMission,
                  type:selectedMission.status === 'gap' ? 'add-place' : selectedMission.status === 'stale' ? 'add-photo' : 'update-place',
                  title:selectedMission.label,
                  distance:'120m',
                  cp:selectedMission.gp || 30,
                })}
              />
              <OpportunityCard
                title="Add photo evidence"
                body="Upload a clean storefront or menu photo from camera or gallery."
                reward="+30 GP"
                onStart={() => onStart && onStart({...selectedMission, type:'add-photo', title:selectedMission.label, cp:30})}
              />
            </>
          ) : (
            nearbyMissions.map(m => (
              <MissionCard key={m.id} m={m} cardStyle={cardStyle}
                onStart={() => onStart && onStart(m)}/>
            ))
          )}
          <OpportunityCard
            title="Coverage gap · West block"
            body="12 POIs have stale photos or no verified address. Turn any gap into a contribution task."
            reward="+30 GP"
            tone="warm"
            onStart={() => onTabChange && onTabChange('contribute')}
          />
        </div>
      </div>
    </>
  );
}

function PoiDetailPanel({ poi, onStart }) {
  return (
    <div style={{padding:'0 20px 12px'}}>
      <div style={{
        border:'1px solid var(--line)', borderRadius:14, padding:12,
        background:'var(--bg-elev)', display:'grid', gap:10,
      }}>
        <div style={{display:'grid', gridTemplateColumns:'52px 1fr', gap:11, alignItems:'center'}}>
          <CoordGlyph seed={poi.id + poi.label} size={52} accent="var(--teal)"/>
          <div>
            <div style={{fontSize:15, fontWeight:800, letterSpacing:'-0.01em'}}>{poi.label}</div>
            <div style={{fontSize:12, color:'var(--ink-3)', marginTop:2}}>
              {poi.type} · Data quality {poi.status === 'verified' ? 88 : 62}
            </div>
            <div style={{display:'flex', gap:6, marginTop:7}}>
              <Pill tone="teal">+{poi.gp} GP</Pill>
              <Pill>{poi.progress}</Pill>
            </div>
          </div>
        </div>
        <div style={{display:'grid', gridTemplateColumns:'1fr 1fr 1fr', gap:8}}>
          {['Contribute', 'Save', 'Share'].map((label, i) => (
            <button key={label} className={i === 0 ? 'btn-primary' : 'btn-secondary'} style={{height:38}}
              onClick={() => i === 0 && onStart && onStart({
                type:poi.status === 'gap' ? 'add-place' : poi.status === 'campaign' ? 'update-place' : 'update-place',
                title:poi.label,
                distance:'120m',
                cp:poi.gp,
                daysAgo:210,
              })}>
              {label}
            </button>
          ))}
        </div>
      </div>
    </div>
  );
}

function MissionHome({ onStart, mode = 'mission', setMode, cardStyle = 'default', onTabChange, onProfileTap, showQuickfire = true }) {
  const [category, setCategory] = React.useState('nearby');
  const [layer, setLayer] = React.useState('default-2d');
  const [zoom, setZoom] = React.useState(1);
  const [resetKey, setResetKey] = React.useState(0);
  const [selectedMission, setSelectedMission] = React.useState(null);
  const [sheetTier, setSheetTier] = React.useState('peek');
  const heroMission = MISSIONS.find(m => m.id === 'm2') || MISSIONS[0];
  const nearbyMissions = MISSIONS.filter(m => ['verify', 'refresh', 'angle', 'first-look'].includes(m.type)).slice(0, 3);

  return (
    <>
      <StatusBar/>
      <div className="map-shell">
        <ExploreMapCanvas
          selectedMission={selectedMission}
          onSelectMission={(poi) => { setSelectedMission(poi); setSheetTier('half'); }}
          layer={layer}
          zoom={zoom}
          resetKey={resetKey}
        />
        <SearchOverlayCard onFocus={() => sheetTier === 'full' && setSheetTier('half')}/>
        <MapFloatingControls
          tier={sheetTier}
          layer={layer}
          setLayer={(id) => { setLayer(id); setSelectedMission(null); setSheetTier(id === 'default-2d' ? 'peek' : 'half'); }}
          zoom={zoom}
          setZoom={setZoom}
          onReset={() => { setZoom(1); setResetKey(k => k + 1); }}
        />

        <MapBottomSheet
          tier={sheetTier}
          setTier={setSheetTier}
          meta="EXPLORE"
          summary={layer === 'mesh-3d' ? 'Mesh status' : layer === 'progress-heatmap' ? 'Regional progress' : selectedMission ? selectedMission.label : 'POI updates'}
          action={<Pill tone="teal">{layer === 'progress-heatmap' ? '72%' : layer === 'mesh-3d' ? '3D' : '+455 GP'}</Pill>}
        >
          {category === 'campaign' && layer === 'default-2d' && <CampaignMissionCard onStart={onStart}/>}
          <ExploreLayerContent
            layer={layer}
            selectedMission={selectedMission}
            category={category}
            setCategory={setCategory}
            heroMission={heroMission}
            nearbyMissions={nearbyMissions}
            cardStyle={cardStyle}
            onStart={onStart}
            onTabChange={onTabChange}
          />
        </MapBottomSheet>
      </div>
      <TabBar active="explore" onChange={onTabChange}/>
    </>
  );
}

window.MissionHome = MissionHome;
window.MissionCard = MissionCard;
window.GlobalMapPanel = GlobalMapPanel;
window.MapBottomSheet = MapBottomSheet;
window.MISSIONS    = MISSIONS;
window.MAP_DROP_ACCESS = MAP_DROP_ACCESS;
window.isMapDropQualified = isMapDropQualified;
