// DiveDex — Screens B: Recap, Dex, Creature Detail, Team, Achievements, Side Quests, Memories, Story Card, Settings

// Pulls a dive's photos out of IndexedDB and renders them as a thumbnail
// grid. Self-contained — handles loading, empty, and revocation of the
// object URLs on unmount. Used by Recap (single dive) and Memories
// (per-day inline strips).
// Aggregated trip gallery — pulls photos for the given list of dive ids,
// concatenates them newest-dive-first, and renders as a tap-to-zoom grid.
// Used on the Memories screen.
function TripGallery({ diveIds }) {
  const [photos, setPhotos] = React.useState(null);
  const [lightbox, setLightbox] = React.useState(null);
  React.useEffect(() => {
    let cancelled = false;
    if (!window.DD_PHOTOS) { setPhotos([]); return; }
    Promise.all(diveIds.map(id => window.DD_PHOTOS.getPhotosByDive(id).catch(() => [])))
      .then(lists => {
        if (cancelled) return;
        const flat = [];
        lists.forEach((list, i) => list.forEach(p => flat.push({ ...p, _order: lists.length - i })));
        flat.sort((a, b) => b._order - a._order || (a.createdAt < b.createdAt ? 1 : -1));
        setPhotos(flat);
      });
    return () => { cancelled = true; };
  }, [diveIds.join('|')]);
  const urls = React.useMemo(() => (photos || []).map(p => URL.createObjectURL(p.blob)), [photos]);
  React.useEffect(() => () => urls.forEach(u => URL.revokeObjectURL(u)), [urls]);
  if (!photos || photos.length === 0) return null;
  return (
    <>
      <div className="dd-h-section" style={{ marginTop: 18, marginBottom: 8 }}>Trip gallery</div>
      <div style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: 6 }}>
        {photos.map((p, i) => (
          <div key={p.id} onClick={() => setLightbox(urls[i])} style={{
            aspectRatio: '1 / 1', borderRadius: 10, overflow: 'hidden',
            background: 'rgba(0,0,0,0.3)', border: '1px solid rgba(130,236,255,0.10)',
            cursor: 'zoom-in',
          }}>
            <img src={urls[i]} style={{ width: '100%', height: '100%', objectFit: 'cover', display: 'block' }} loading="lazy"/>
          </div>
        ))}
      </div>
      <div style={{ marginTop: 6, fontSize: 10.5, color: 'var(--ink-3)' }}>
        {photos.length} {photos.length === 1 ? 'photo' : 'photos'} across {diveIds.length} {diveIds.length === 1 ? 'dive' : 'dives'}.
      </div>
      {lightbox && (
        <div onClick={() => setLightbox(null)} style={{
          position: 'fixed', inset: 0, zIndex: 50, background: 'rgba(0,0,0,0.92)',
          display: 'grid', placeItems: 'center',
          padding: 'env(safe-area-inset-top, 0px) 12px env(safe-area-inset-bottom, 0px)',
          cursor: 'zoom-out',
        }}>
          <img src={lightbox} style={{ maxWidth: '100%', maxHeight: '100%', objectFit: 'contain', borderRadius: 12 }}/>
        </div>
      )}
    </>
  );
}

function DivePhotoStrip({ diveId, title = 'Photos', compact = false }) {
  const [photos, setPhotos] = React.useState(null); // null = loading, [] = none, [{id, blob, ...}] = ready
  const [lightbox, setLightbox] = React.useState(null); // url currently zoomed
  React.useEffect(() => {
    let cancelled = false;
    if (!window.DD_PHOTOS || !diveId) { setPhotos([]); return; }
    window.DD_PHOTOS.getPhotosByDive(diveId).then(arr => {
      if (!cancelled) setPhotos(arr || []);
    }).catch(() => { if (!cancelled) setPhotos([]); });
    return () => { cancelled = true; };
  }, [diveId]);

  const urls = React.useMemo(() => (photos || []).map(p => URL.createObjectURL(p.blob)), [photos]);
  React.useEffect(() => () => urls.forEach(u => URL.revokeObjectURL(u)), [urls]);

  if (!photos || photos.length === 0) return null;
  return (
    <>
      <GlassCard style={{ marginTop: 12, padding: 12, borderRadius: 18 }}>
        <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline', marginBottom: 8 }}>
          <div className="dd-h-section">{title}</div>
          <span className="dd-num" style={{ fontSize: 10.5, color: 'var(--ink-3)' }}>{photos.length} · tap to zoom</span>
        </div>
        <div style={{
          display: 'grid',
          gridTemplateColumns: compact ? 'repeat(4, 1fr)' : 'repeat(3, 1fr)',
          gap: 6,
        }}>
          {photos.map((p, i) => (
            <div key={p.id} onClick={() => setLightbox(urls[i])} style={{
              aspectRatio: '1 / 1', borderRadius: 10, overflow: 'hidden',
              background: 'rgba(0,0,0,0.3)', border: '1px solid rgba(130,236,255,0.10)',
              cursor: 'zoom-in',
            }}>
              <img src={urls[i]} style={{ width: '100%', height: '100%', objectFit: 'cover', display: 'block' }} loading="lazy"/>
            </div>
          ))}
        </div>
      </GlassCard>
      {lightbox && (
        <div onClick={() => setLightbox(null)} style={{
          position: 'fixed', inset: 0, zIndex: 50,
          background: 'rgba(0,0,0,0.92)',
          display: 'grid', placeItems: 'center',
          padding: 'env(safe-area-inset-top, 0px) 12px env(safe-area-inset-bottom, 0px)',
          cursor: 'zoom-out',
        }}>
          <img src={lightbox} style={{ maxWidth: '100%', maxHeight: '100%', objectFit: 'contain', borderRadius: 12 }}/>
        </div>
      )}
    </>
  );
}

function RecapScreen({ nav, id }) {
  const { state } = useDiveDex();
  const dive = id ? selectDiveById(state, id) : selectLatestDive(state);

  // Audio cues — fire once per dive (the first time its Recap is opened).
  // The audio engine queues these on the "celebration" channel, so:
  //   - sharknado / hammerhead-reveal / achievement.unlock and airZen.bonus
  //     play one after another instead of stepping on each other
  //   - re-mounting Recap (back-nav) is a no-op while a cue is already in
  //     flight, and a guard below skips replay once the dive has been seen
  //   - we deliberately do NOT yank the queue on unmount: navigating away
  //     mid-jingle lets the cue finish naturally instead of cutting it off
  React.useEffect(() => {
    if (!dive || !window.DiveDexAudio) return;
    const seen = (window.__DD_RECAP_AUDIO_SEEN__ = window.__DD_RECAP_AUDIO_SEEN__ || new Set());
    if (seen.has(dive.id)) return;
    seen.add(dive.id);
    const A = window.DiveDexAudio;
    const unlocked = dive.unlockedAchievementIds || [];
    const sightingIds = (state.sightings || []).filter(s => s.diveId === dive.id).map(s => s.creatureId);
    const sawLegendary = sightingIds.some(sid => {
      const c = state.creatures.find(x => x.id === sid);
      return c && (c.rarity === 'legendary' || c.rarity === 'mythic');
    });

    if (unlocked.indexOf('sharknado') >= 0) A.playSfx('achievement.sharknado');
    else if (sawLegendary) A.playSfx('creature.hammerheadReveal');
    else if (unlocked.length > 0) A.playSfx('achievement.unlock');

    if (dive.airZen) A.playSfx('airZen.bonus');
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [dive && dive.id]);

  if (!dive) {
    return (
      <div className="dd-screen" style={{ paddingBottom: 110 }}>
        <TopBar title="Dive Recap" left={<IconBtn onClick={() => nav('dashboard')}>‹</IconBtn>}/>
        <div style={{ padding: '20px 18px', color: 'var(--ink-3)', fontSize: 13 }}>
          No dives logged yet. Log a dive to see its recap.
        </div>
      </div>
    );
  }

  // Resolve hero creature using the shared selector — keeps the Story Card
  // in sync with the Recap on which species gets the spotlight.
  const diveSightings = state.sightings.filter(s => s.diveId === dive.id);
  const { creature: heroCreature } = selectDiveHero(state, dive);
  const heroLabel = heroCreature
    ? (heroCreature.rarity === 'mythic' || heroCreature.rarity === 'legendary' ? 'LEGENDARY ENCOUNTER'
       : heroCreature.rarity === 'epic' ? 'EPIC SIGHTING' : 'BEST SIGHTING')
    : 'DIVE COMPLETE';
  const heroName = heroCreature ? heroCreature.commonName : (dive.site || 'Dive');
  const heroQuote = dive.notes && dive.notes.trim()
    ? `"${dive.notes.trim()}" — ${(dive.buddy || 'You').split('&')[0].trim()}`
    : '"Log after surfacing. Sharks can wait."';

  const rick = state.users.find(u => u.id === 'rick');
  const siet = state.users.find(u => u.id === 'siet');

  // Unlocked-this-dive: from the dive snapshot
  const unlocks = (dive.unlockedAchievementIds || []).map(aid =>
    state.achievements.find(a => a.id === aid)
  ).filter(Boolean);

  const xpRows = dive.xpRows || [];
  const fmt = (n) => (n >= 0 ? '+' : '') + n.toLocaleString();

  return (
    <div className="dd-screen" style={{ paddingBottom: 110 }}>
      <TopBar
        title="Dive Recap"
        sub={`${dive.site || 'Dive'} · Dive ${dive.no}${dive.createdAt ? ' · ' + new Date(dive.createdAt).toLocaleDateString(undefined, { month: 'short', day: 'numeric' }) + ' ' + new Date(dive.createdAt).toLocaleTimeString(undefined, { hour: '2-digit', minute: '2-digit' }) : ''}`}
        left={<IconBtn onClick={() => nav('dashboard')}>‹</IconBtn>}
        right={<IconBtn>↗</IconBtn>}
      />
      <div style={{ padding: '4px 18px' }}>

        {/* Hero card */}
        <div className="dd-glass dd-reveal" style={{ padding: 0, borderRadius: 22, overflow: 'hidden', borderColor: 'rgba(215,180,106,0.36)', background: 'linear-gradient(160deg, rgba(215,180,106,0.18), rgba(11,42,68,0.5) 60%, rgba(5,11,20,0.8))' }}>
          <div style={{ position: 'relative', padding: '20px 18px 16px', overflow: 'hidden' }}>
            {heroCreature && <div style={{ position: 'absolute', top: -10, right: -30, opacity: 0.6 }}><CrSilhouette id={heroCreature.id} color="#D7B46A" size={180}/></div>}
            <div style={{ position: 'relative' }}>
              <div style={{ fontSize: 11, fontWeight: 700, color: '#D7B46A', letterSpacing: '0.16em' }}>{heroLabel}</div>
              <div className="dd-h1 dd-shimmer" style={{ fontSize: 26, lineHeight: 1.05, marginTop: 4 }}>{heroName}</div>
              <div style={{ fontSize: 12, color: 'var(--ink-2)', marginTop: 6 }}>{heroQuote}</div>
            </div>
          </div>
          <div style={{ padding: '0 18px 18px', display: 'grid', gridTemplateColumns: '1fr 1fr 1fr', gap: 10, position: 'relative' }}>
            <StatCell label="Max depth" value={dive.maxDepth} unit="m"/>
            <StatCell label="Dive time" value={dive.time} unit="min"/>
            <StatCell label="Species" value={diveSightings.length} color="#63FFD2"/>
          </div>
        </div>

        {/* Dive site map — only shows when we have geo data for the site. */}
        {(() => {
          const geo = window.resolveDiveSiteGeo && window.resolveDiveSiteGeo(dive.site);
          if (!geo) return null;
          return (
            <GlassCard style={{ marginTop: 12, padding: 12, borderRadius: 18 }}>
              <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline', marginBottom: 8 }}>
                <div className="dd-h-section">Dive site</div>
                <span className="dd-num" style={{ fontSize: 10.5, color: 'var(--ink-3)' }}>
                  {geo.lat.toFixed(3)}°N · {Math.abs(geo.lng).toFixed(3)}°{geo.lng >= 0 ? 'E' : 'W'}
                </span>
              </div>
              <DiveSiteMap region={geo.region} sites={[{ name: geo.name, lat: geo.lat, lng: geo.lng, highlight: true }]} height={210}/>
              <div style={{ marginTop: 8, fontSize: 12.5, color: 'var(--ink-2)', fontWeight: 600 }}>{geo.name}</div>
            </GlassCard>
          );
        })()}

        {/* Dive photos — pulled from IndexedDB by dive id. */}
        <DivePhotoStrip diveId={dive.id}/>

        {/* XP breakdown — from saved dive snapshot */}
        <GlassCard style={{ marginTop: 12, padding: 14, borderRadius: 18 }}>
          <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline' }}>
            <div className="dd-h-section">XP earned</div>
            <span className="dd-num" style={{ fontSize: 22, fontWeight: 700, color: '#D7B46A' }}>{fmt(dive.totalXp)}</span>
          </div>
          <div style={{ display: 'flex', flexDirection: 'column', gap: 8, marginTop: 12 }}>
            {xpRows.map((r, i) => (
              <XpRow key={i} label={r.label} value={r.value} hi={r.tone === 'gold'} foam={r.tone === 'foam'}/>
            ))}
          </div>
        </GlassCard>

        {/* Pearls */}
        <GlassCard style={{ marginTop: 8, padding: 12, borderRadius: 14, background: 'linear-gradient(160deg, rgba(234,245,255,0.08), rgba(11,42,68,0.4))' }}>
          <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline' }}>
            <div className="dd-h-section">Pearls earned</div>
            <span className="dd-num" style={{ fontSize: 18, fontWeight: 700, color: '#EAF5FF' }}>+{dive.pearls}</span>
          </div>
        </GlassCard>

        {/* Rick / Siet split */}
        <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 10, marginTop: 12 }}>
          <PersonGain person={{ name: rick.displayName, color: rick.color }} xp={dive.perUserXp.rick} note={rick.defaultTitle}/>
          <PersonGain person={{ name: siet.displayName, color: siet.color }} xp={dive.perUserXp.siet} note={siet.defaultTitle}/>
        </div>

        {/* Unlocked badges from this dive */}
        {unlocks.length > 0 && (
          <>
            <div className="dd-h-section" style={{ marginTop: 18, marginBottom: 8 }}>Unlocked</div>
            <div style={{ display: 'grid', gridTemplateColumns: 'repeat(2, 1fr)', gap: 8 }}>
              {unlocks.map(b => (
                <RarityFrame key={b.id} rarity={b.rarity} style={{}}>
                  <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
                    <div style={{ width: 38, height: 38, flex: '0 0 38px', borderRadius: 12, background: 'rgba(0,0,0,0.35)', border: '1px solid var(--r-color)', display: 'grid', placeItems: 'center', color: 'var(--r-color)', boxShadow: '0 0 12px var(--r-glow)', fontSize: 16, fontWeight: 700 }}>{b.icon || '✦'}</div>
                    <div style={{ flex: 1, minWidth: 0 }}>
                      <div style={{ fontSize: 12, fontWeight: 700, lineHeight: 1.15, overflowWrap: 'break-word', hyphens: 'auto' }}>{b.name}</div>
                      <div style={{ fontSize: 9.5, color: 'var(--r-color)', textTransform: 'uppercase', letterSpacing: '0.1em', fontWeight: 700, marginTop: 2 }}>{b.rarity}</div>
                    </div>
                  </div>
                </RarityFrame>
              ))}
            </div>
          </>
        )}

        {/* Cosmetics granted by the bridge — only shows on the dive that earned them */}
        {(dive.bridgedItemIds && dive.bridgedItemIds.length > 0) && (
          <>
            <div className="dd-h-section" style={{ marginTop: 18, marginBottom: 8 }}>Cosmetics unlocked</div>
            <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
              {dive.bridgedItemIds.map(iid => {
                const it = (window.BOUTIQUE_ITEMS || []).find(x => x.id === iid);
                if (!it) return null;
                return (
                  <div key={iid} onClick={() => nav('item', iid)} className={`dd-rarity-${it.rarity}`} style={{
                    cursor: 'pointer', padding: 10, borderRadius: 14,
                    background: 'linear-gradient(180deg, rgba(16,30,46,0.92), rgba(7,18,30,0.95))',
                    border: '1px solid var(--r-color)',
                    boxShadow: '0 4px 14px -8px var(--r-glow)',
                    display: 'flex', alignItems: 'center', gap: 10,
                  }}>
                    <div style={{ width: 44, height: 44, borderRadius: 10, background: 'radial-gradient(60% 60% at 50% 40%, var(--r-glow), transparent 70%)', display: 'grid', placeItems: 'center' }}>
                      <ItemArt kind={it.art} rarity={it.rarity} size={40}/>
                    </div>
                    <div style={{ flex: 1, minWidth: 0 }}>
                      <div style={{ fontSize: 12, fontWeight: 700, lineHeight: 1.15, overflowWrap: 'break-word', hyphens: 'auto' }}>{it.name}</div>
                      <div style={{ fontSize: 9.5, color: 'var(--r-color)', textTransform: 'uppercase', letterSpacing: '0.1em', fontWeight: 700, marginTop: 2 }}>{it.rarity} · added</div>
                    </div>
                  </div>
                );
              })}
            </div>
          </>
        )}

        {/* CTAs */}
        <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 10, marginTop: 14 }}>
          <button onClick={() => nav('story', dive.id)} className="dd-btn dd-btn-primary" style={{ height: 48, borderRadius: 14, fontSize: 13.5 }}>Generate Story Card</button>
          <button onClick={() => nav('dex')} className="dd-btn dd-btn-ghost" style={{ height: 48, borderRadius: 14, fontSize: 13.5 }}>View in Dex</button>
        </div>
        <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 8, marginTop: 8 }}>
          <button onClick={() => nav('edit-dive', dive.id)} className="dd-btn dd-btn-ghost" style={{
            height: 40, borderRadius: 12, fontSize: 12.5, fontWeight: 700,
          }}>✎  Edit dive</button>
          <button onClick={() => nav('log-similar', dive.id)} className="dd-btn dd-btn-ghost" style={{
            height: 40, borderRadius: 12, fontSize: 12.5, fontWeight: 700,
          }}>＋  Log similar</button>
        </div>
      </div>
    </div>
  );
}

function XpRow({ label, value, hi, foam }) {
  return (
    <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', padding: '4px 0', borderBottom: '1px dashed rgba(130,236,255,0.06)' }}>
      <span style={{ fontSize: 12.5, color: hi ? '#fff' : 'var(--ink-2)', fontWeight: hi ? 600 : 500 }}>{label}</span>
      <span className="dd-num" style={{ fontSize: 13, color: hi ? '#D7B46A' : foam ? '#63FFD2' : 'var(--ink)', fontWeight: 700 }}>+{value}</span>
    </div>
  );
}

function PersonGain({ person, xp, note }) {
  return (
    <GlassCard style={{ padding: 12, borderRadius: 14 }}>
      <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
        <Avatar name={person.name} color={person.color} size={28}/>
        <div>
          <div style={{ fontSize: 11, color: 'var(--ink-3)', fontWeight: 600 }}>{person.name}</div>
          <div className="dd-num" style={{ fontSize: 15, fontWeight: 700, color: '#fff' }}>+{xp}</div>
        </div>
      </div>
      <div style={{ fontSize: 10.5, color: person.color, marginTop: 8, fontWeight: 600 }}>{note}</div>
    </GlassCard>
  );
}

function DexScreen({ nav }) {
  const { state } = useDiveDex();
  const expedition = state.expeditions.find(e => e.id === state.activeExpeditionId);
  const region = expedition ? expedition.region : null;
  // Show the active-expedition's regional set when there is one.
  const regional = region
    ? state.creatures.filter(c => (c.regions || []).indexOf(region) >= 0)
    : state.creatures;
  const [tab, setTab] = useState('all');
  const familyCounts = regional.reduce((acc, c) => {
    acc[c.familyType] = (acc[c.familyType] || 0) + 1;
    return acc;
  }, {});
  const tabs = [
    { id: 'all',     label: 'All',     count: regional.length, filter: () => true },
    { id: 'shark',   label: 'Sharks',  count: familyCounts.shark || 0,    filter: (c) => c.familyType === 'shark' },
    { id: 'ray',     label: 'Rays',    count: familyCounts.ray || 0,      filter: (c) => c.familyType === 'ray' },
    { id: 'reef',    label: 'Reef',    count: familyCounts.reefFish || 0, filter: (c) => c.familyType === 'reefFish' },
    { id: 'macro',   label: 'Macro',   count: familyCounts.macro || 0,    filter: (c) => c.familyType === 'macro' },
    { id: 'pelagic', label: 'Pelagic', count: familyCounts.pelagic || 0,  filter: (c) => c.familyType === 'pelagic' },
    { id: 'coral',   label: 'Coral',   count: familyCounts.coral || 0,    filter: (c) => c.familyType === 'coral' },
    { id: 'sponge',  label: 'Sponges', count: familyCounts.sponge || 0,   filter: (c) => c.familyType === 'sponge' },
    { id: 'wreck',   label: 'Wrecks',  count: familyCounts.wreck || 0,    filter: (c) => c.familyType === 'wreck' },
    { id: 'aircraft',label: 'Aircraft',count: familyCounts.aircraft || 0, filter: (c) => c.familyType === 'aircraft' },
  ].filter(t => t.id === 'all' || t.count > 0);
  const activeTab = tabs.find(t => t.id === tab) || tabs[0];
  const [search, setSearch] = useState('');
  const [searchOpen, setSearchOpen] = useState(false);
  const [sort, setSort] = useState('rarity'); // 'rarity' | 'name' | 'sightings'
  const matchesSearch = (c) => {
    if (!search) return true;
    const q = search.toLowerCase();
    return (c.commonName || '').toLowerCase().indexOf(q) >= 0
        || (c.scientificName || '').toLowerCase().indexOf(q) >= 0;
  };
  const sortRank = { mythic: 6, legendary: 5, epic: 4, rare: 3, uncommon: 2, common: 1 };
  const sortFn = {
    rarity: (a, b) => (sortRank[b.rarity] || 0) - (sortRank[a.rarity] || 0)
                   || (a.commonName || '').localeCompare(b.commonName || ''),
    name:   (a, b) => (a.commonName || '').localeCompare(b.commonName || ''),
    sightings: (a, b) => (b.sightingsCount || 0) - (a.sightingsCount || 0)
                       || (a.commonName || '').localeCompare(b.commonName || ''),
  };
  const filtered = regional.filter(activeTab.filter).filter(matchesSearch).slice().sort(sortFn[sort] || sortFn.rarity);
  const discovered = regional.filter(c => c.discovered).length;
  const legendaryFound = regional.filter(c => c.discovered && (c.rarity === 'legendary' || c.rarity === 'mythic')).length;
  const stillLocked = regional.length - discovered;
  const pct = regional.length ? Math.round((discovered / regional.length) * 100) : 0;
  const toggleSearch = () => {
    setSearchOpen(v => {
      if (v) setSearch('');
      return !v;
    });
  };
  return (
    <div className="dd-screen" style={{ paddingBottom: 110 }}>
      <TopBar title="Dex" sub={`${discovered} of ${regional.length} discovered`} left={<IconBtn onClick={() => nav('dashboard')}>‹</IconBtn>} right={<IconBtn onClick={toggleSearch}>⌕</IconBtn>}/>
      <div style={{ padding: '0 18px 4px' }}>
        <GlassCard style={{ padding: 14, borderRadius: 16, marginBottom: 12 }}>
          <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline' }}>
            <span style={{ fontSize: 12, color: 'var(--ink-2)', fontWeight: 600 }}>{expedition ? expedition.title : 'OceanDex'}</span>
            <span className="dd-num" style={{ fontSize: 13, color: '#63FFD2', fontWeight: 700 }}>{pct}%</span>
          </div>
          <div style={{ marginTop: 8 }}><XPBar value={discovered} max={regional.length || 1} color="#63FFD2" height={6} animate={false}/></div>
          <div style={{ display: 'flex', gap: 6, marginTop: 10, flexWrap: 'wrap' }}>
            <Chip variant="default">{discovered} discovered</Chip>
            {legendaryFound > 0 && <Chip variant="gold">{legendaryFound} legendary</Chip>}
            <Chip variant="coral">{stillLocked} locked</Chip>
          </div>
        </GlassCard>

        {searchOpen && (
          <div style={{ marginBottom: 10, display: 'flex', alignItems: 'center', gap: 8 }}>
            <input
              autoFocus
              value={search}
              onChange={e => setSearch(e.target.value)}
              placeholder="Search by common or scientific name…"
              style={{
                flex: 1, padding: '10px 12px', borderRadius: 12, fontSize: 13,
                background: 'rgba(255,255,255,0.04)', border: '1px solid rgba(130,236,255,0.18)',
                color: '#EAF5FF', outline: 'none',
              }}
            />
            {search && (
              <button onClick={() => setSearch('')} className="dd-btn" style={{
                width: 36, height: 36, borderRadius: 10, background: 'rgba(255,255,255,0.04)',
                color: 'var(--ink-3)', border: '1px solid rgba(130,236,255,0.12)', fontSize: 14,
              }}>×</button>
            )}
          </div>
        )}

        <div style={{ display: 'flex', gap: 6, overflowX: 'auto', margin: '0 -18px 12px', padding: '0 18px', scrollbarWidth: 'none' }}>
          {tabs.map(t => (
            <button key={t.id} onClick={() => setTab(t.id)} className="dd-btn" style={{
              flex: '0 0 auto', padding: '8px 12px', borderRadius: 999, fontSize: 12,
              background: tab === t.id ? 'rgba(49,215,255,0.16)' : 'rgba(255,255,255,0.04)',
              color: tab === t.id ? '#82ECFF' : 'var(--ink-2)',
              border: tab === t.id ? '1px solid rgba(49,215,255,0.32)' : '1px solid rgba(130,236,255,0.10)',
              fontWeight: 600,
            }}>{t.label} <span style={{ opacity: 0.55, marginLeft: 4 }}>{t.count}</span></button>
          ))}
        </div>

        <div style={{ display: 'flex', alignItems: 'center', gap: 6, marginBottom: 10, fontSize: 10.5 }}>
          <span style={{ color: 'var(--ink-3)', letterSpacing: '0.1em', fontWeight: 700, textTransform: 'uppercase' }}>Sort</span>
          {[{ id: 'rarity', label: 'Rarity' }, { id: 'name', label: 'A–Z' }, { id: 'sightings', label: 'Sightings' }].map(opt => (
            <button key={opt.id} onClick={() => setSort(opt.id)} className="dd-btn" style={{
              padding: '4px 9px', borderRadius: 999, fontSize: 10.5, fontWeight: 700,
              background: sort === opt.id ? 'rgba(49,215,255,0.14)' : 'rgba(255,255,255,0.04)',
              color: sort === opt.id ? '#82ECFF' : 'var(--ink-2)',
              border: sort === opt.id ? '1px solid rgba(49,215,255,0.32)' : '1px solid rgba(130,236,255,0.10)',
            }}>{opt.label}</button>
          ))}
        </div>

        {filtered.length === 0 ? (
          <GlassCard style={{ padding: 16, borderRadius: 14, fontSize: 12, color: 'var(--ink-3)', textAlign: 'center', lineHeight: 1.5 }}>
            {search ? `No matches for "${search}" in ${activeTab.label.toLowerCase()}.` : 'No creatures in this family yet.'}
          </GlassCard>
        ) : (
          <div style={{ display: 'grid', gridTemplateColumns: 'repeat(2, 1fr)', gap: 10 }}>
            {filtered.map(c => (
              <CreatureCard key={c.id} cr={c} onClick={() => nav('creature', c.id)}/>
            ))}
          </div>
        )}
      </div>
    </div>
  );
}

function CreatureCard({ cr, onClick }) {
  const locked = !cr.discovered;
  return (
    <RarityFrame rarity={cr.rarity} style={{ cursor: 'pointer' }}>
      <div onClick={onClick} style={{ position: 'relative' }}>
        {(cr.rarity === 'legendary' || cr.rarity === 'mythic') && cr.discovered && (
          <div style={{ position: 'absolute', top: -4, right: -4, padding: '2px 6px', borderRadius: 6, background: 'linear-gradient(135deg, #D7B46A, #FBE6A6)', color: '#2a1c00', fontSize: 9, fontWeight: 800, letterSpacing: '0.08em' }}>{cr.rarity === 'mythic' ? 'MYTH' : 'LEG'}</div>
        )}
        <div style={{ height: 100, display: 'grid', placeItems: 'center', filter: locked ? 'brightness(0.25) contrast(0.6)' : 'none' }}>
          <CrSilhouette id={cr.id} color={locked ? '#1B3A56' : `var(--r-color)`} size={110}/>
        </div>
        <div style={{ marginTop: 4 }}>
          <div style={{ fontSize: 9.5, fontWeight: 700, letterSpacing: '0.1em', color: locked ? 'var(--ink-4)' : 'var(--r-color)', textTransform: 'uppercase' }}>
            {locked ? '— Not yet found' : RARITY_LABEL[cr.rarity]}
          </div>
          <div style={{ fontSize: 12.5, fontWeight: 700, color: locked ? 'var(--ink-3)' : '#fff', marginTop: 2, lineHeight: 1.2 }}>
            {locked ? '???' : cr.commonName}
          </div>
          {!locked && (
            <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginTop: 6, fontSize: 10.5 }}>
              <span style={{ color: 'var(--ink-3)' }}>×<span className="dd-num" style={{ color: 'var(--ink-2)' }}>{cr.sightingsCount || 0}</span></span>
              <span style={{ color: 'var(--ink-3)', fontSize: 10 }}>{cr.firstSeenBy ? (cr.firstSeenBy === 'rick' ? 'Rick' : 'Siet') : ''}</span>
            </div>
          )}
        </div>
      </div>
    </RarityFrame>
  );
}

function CreatureDetailScreen({ nav, id }) {
  const { state } = useDiveDex();
  const cr = state.creatures.find(c => c.id === id) || state.creatures[0];
  if (!cr) {
    return (
      <div className="dd-screen" style={{ paddingBottom: 110 }}>
        <TopBar title="???" left={<IconBtn onClick={() => nav('dex')}>‹</IconBtn>}/>
        <div style={{ padding: '20px 18px', color: 'var(--ink-3)', fontSize: 13 }}>Creature not found.</div>
      </div>
    );
  }
  // Build the sighting history from state: join sightings → dives for site/date.
  const history = state.sightings
    .filter(s => s.creatureId === cr.id)
    .map(s => {
      const dive = state.dives.find(d => d.id === s.diveId);
      return {
        id: s.id,
        site: (dive && dive.site) || 'Unknown',
        when: s.createdAt,
        count: s.count,
        spotter: s.firstSpotter,
        legendary: cr.rarity === 'legendary' || cr.rarity === 'mythic',
      };
    })
    .sort((a, b) => (a.when < b.when ? 1 : -1));
  const fmtWhen = (iso) => {
    const d = new Date(iso);
    const date = d.toLocaleDateString(undefined, { month: 'short', day: 'numeric' });
    const time = d.toLocaleTimeString(undefined, { hour: '2-digit', minute: '2-digit' });
    return `${date} · ${time}`;
  };
  const spotterName = (id) => id === 'rick' ? 'Rick' : id === 'siet' ? 'Siet' : 'Crew';

  const firstSiteName = (() => {
    if (!cr.firstSeenDiveId) return '—';
    const d = state.dives.find(x => x.id === cr.firstSeenDiveId);
    return d && d.site ? d.site.split(' ')[0] : '—';
  })();
  // Related achievements: any whose name mentions the common name, or whose
  // condition references this creature id, or that are in the same family.
  const related = state.achievements.filter(a => {
    if (a.conditionType && a.conditionType.indexOf(cr.id) >= 0) return true;
    if (a.name && a.name.toLowerCase().indexOf(cr.commonName.toLowerCase()) >= 0) return true;
    if (cr.familyType === 'shark' && a.conditionType && a.conditionType.indexOf('shark') >= 0) return true;
    return false;
  }).slice(0, 3);

  return (
    <div className="dd-screen" style={{ paddingBottom: 110 }}>
      <TopBar
        title={cr.discovered ? cr.commonName : '???'}
        sub={cr.discovered ? RARITY_LABEL[cr.rarity].toUpperCase() : 'NOT FOUND'}
        left={<IconBtn onClick={() => nav('dex')}>‹</IconBtn>}
        right={<IconBtn>★</IconBtn>}
      />
      <div className={`dd-rarity-${cr.rarity}`} style={{ padding: '0 18px 4px' }}>
        {/* Hero portrait */}
        <div style={{ position: 'relative', borderRadius: 24, overflow: 'hidden', padding: '24px 18px 18px',
          background: cr.rarity === 'mythic'
            ? 'conic-gradient(from 0deg, rgba(130,236,255,0.3), rgba(99,255,210,0.3), rgba(215,180,106,0.3), rgba(255,122,107,0.3), rgba(139,123,255,0.3), rgba(130,236,255,0.3))'
            : `radial-gradient(80% 60% at 50% 20%, var(--r-glow), transparent 70%), linear-gradient(180deg, rgba(11,42,68,0.6), rgba(5,11,20,0.85))`,
          border: '1px solid var(--r-color)',
        }}>
          <div style={{ position: 'relative' }}>
            <div style={{ display:'grid', placeItems:'center', padding: '10px 0 14px' }}>
              <CrSilhouette id={cr.id} size={210}/>
            </div>
            <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline' }}>
              <div>
                <div style={{ fontSize: 11, fontWeight: 700, letterSpacing: '0.14em', color: 'var(--r-color)' }}>
                  {cr.discovered ? RARITY_LABEL[cr.rarity].toUpperCase() : '— LOCKED —'}
                </div>
                <div className="dd-h1" style={{ fontSize: 24, marginTop: 3 }}>{cr.discovered ? cr.commonName : 'Unknown species'}</div>
                {cr.discovered && cr.scientificName && <div style={{ fontSize: 12, color: '#82ECFF', fontStyle: 'italic', marginTop: 3 }}>{cr.scientificName}</div>}
                {cr.discovered && cr.flavorText && <div style={{ fontSize: 11.5, color: 'var(--ink-2)', marginTop: 6, maxWidth: 220, lineHeight: 1.4 }}>{cr.flavorText}</div>}
              </div>
              <div style={{ textAlign: 'right' }}>
                <div className="dd-num" style={{ fontSize: 24, fontWeight: 700, color: 'var(--r-color)' }}>×{cr.sightingsCount || 0}</div>
                <div style={{ fontSize: 10, color: 'var(--ink-3)', letterSpacing: '0.08em' }}>SIGHTINGS</div>
              </div>
            </div>
          </div>
        </div>

        {/* Stats */}
        <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr 1fr', gap: 8, marginTop: 12 }}>
          <GlassCard style={{ padding: 10, borderRadius: 12 }}><StatCell label="Size" value={cr.maxSize || '—'} mono={false}/></GlassCard>
          <GlassCard style={{ padding: 10, borderRadius: 12 }}><StatCell label="First" value={cr.firstSeenBy ? spotterName(cr.firstSeenBy) : '—'} mono={false}/></GlassCard>
          <GlassCard style={{ padding: 10, borderRadius: 12 }}><StatCell label="Site" value={firstSiteName} mono={false}/></GlassCard>
        </div>

        {/* Sighting history */}
        <div className="dd-h-section" style={{ marginTop: 18, marginBottom: 8 }}>Sighting log</div>
        {history.length > 0 ? (
          <GlassCard style={{ padding: 0, borderRadius: 16 }}>
            {history.map((s, i, a) => (
              <div key={s.id} style={{ display: 'flex', alignItems: 'center', gap: 12, padding: '12px 14px', borderBottom: i === a.length - 1 ? 'none' : '1px solid rgba(130,236,255,0.06)' }}>
                <div style={{ width: 8, height: 8, borderRadius: '50%', background: s.legendary ? '#D7B46A' : '#31D7FF', boxShadow: s.legendary ? '0 0 10px #D7B46A' : '0 0 8px #31D7FF' }}/>
                <div style={{ flex: 1 }}>
                  <div style={{ fontSize: 12.5, fontWeight: 600 }}>{s.site}</div>
                  <div style={{ fontSize: 10.5, color: 'var(--ink-3)' }}>{fmtWhen(s.when)} · spotted by {spotterName(s.spotter)}</div>
                </div>
                <span className="dd-num" style={{ fontSize: 12, color: s.legendary ? '#D7B46A' : '#82ECFF', fontWeight: 700 }}>×{s.count}</span>
              </div>
            ))}
          </GlassCard>
        ) : (
          <GlassCard style={{ padding: 14, borderRadius: 16, fontSize: 12, color: 'var(--ink-3)', textAlign: 'center', lineHeight: 1.5 }}>
            {cr.discovered ? 'No sightings logged yet.' : 'Log a dive that spots this creature to unlock it.'}
          </GlassCard>
        )}

        {/* Related badges */}
        {related.length > 0 && <>
          <div className="dd-h-section" style={{ marginTop: 18, marginBottom: 8 }}>Related badges</div>
          <div style={{ display: 'flex', gap: 8 }}>
            {related.map(a => (
              <div key={a.id} className="dd-glass" style={{ flex: 1, padding: 10, borderRadius: 12, textAlign: 'center', opacity: a.unlocked ? 1 : 0.55 }}>
                <div style={{ fontSize: 16, color: a.unlocked ? '#D7B46A' : 'var(--ink-3)' }}>{a.icon || '✦'}</div>
                <div style={{ fontSize: 10, fontWeight: 600, marginTop: 4, lineHeight: 1.2 }}>{a.name}</div>
              </div>
            ))}
          </div>
        </>}
      </div>
    </div>
  );
}

function TeamScreen({ nav }) {
  const { state } = useDiveDex();

  // Team-level totals
  const teamXp = state.xp.teamXp;
  const teamTitle = 'Luxury Shark Expedition Duo';
  const nextTitleAt = 8000;
  // Today's XP: sum totalXp on dives created today (local day)
  const todayKey = new Date().toDateString();
  const todayXp = state.dives
    .filter(d => d.createdAt && new Date(d.createdAt).toDateString() === todayKey)
    .reduce((sum, d) => sum + (d.totalXp || 0), 0);

  // Player views — sorted highest XP first (rank #1 wins)
  const playerViews = ['rick', 'siet']
    .map(uid => buildPlayerView(state, uid))
    .filter(Boolean)
    .sort((a, b) => b.xp - a.xp);

  // Air Zen — count of bonus dives + total XP earned that way
  const airZenDives = state.dives.filter(d => d.airZen).length;
  const airZenXpTotal = airZenDives * (window.DD_STATE.SCORING_CONFIG.xp.airZenBonus || 125);

  // First-spotter aggregation
  const spotsByUser = state.sightings.reduce((acc, s) => {
    if (!s.firstSpotter) { acc.unattributed = (acc.unattributed || 0) + 1; return acc; }
    acc[s.firstSpotter] = (acc[s.firstSpotter] || 0) + 1;
    return acc;
  }, {});
  const totalSpots = Object.values(spotsByUser).reduce((a, b) => a + b, 0);
  const pct = (n) => totalSpots > 0 ? Math.round((n / totalSpots) * 100) : 0;
  const spotterBars = [
    { id: 'rick', name: 'Rick', color: '#31D7FF', wins: spotsByUser.rick || 0, pct: pct(spotsByUser.rick || 0) },
    { id: 'siet', name: 'Siet', color: '#63FFD2', wins: spotsByUser.siet || 0, pct: pct(spotsByUser.siet || 0) },
    ...((spotsByUser.unattributed || 0) > 0
      ? [{ id: 'crew', name: 'Crew / unattributed', color: '#D7B46A', wins: spotsByUser.unattributed, pct: pct(spotsByUser.unattributed) }]
      : []),
  ];

  // Recent first-spots — last 5 attributed sightings, newest first
  const fmtWhen = (iso) => {
    if (!iso) return '—';
    const d = new Date(iso);
    const day = d.toLocaleDateString(undefined, { month: 'short', day: 'numeric' });
    const time = d.toLocaleTimeString(undefined, { hour: '2-digit', minute: '2-digit' });
    return `${day} · ${time}`;
  };
  const recentSpots = state.sightings
    .filter(s => s.firstSpotter)
    .slice()
    .sort((a, b) => (a.createdAt < b.createdAt ? 1 : -1))
    .slice(0, 5)
    .map(s => {
      const c = state.creatures.find(x => x.id === s.creatureId);
      const d = state.dives.find(x => x.id === s.diveId);
      return {
        id: s.id, creatureId: s.creatureId,
        commonName: c ? c.commonName : s.creatureId,
        rarity: c ? c.rarity : 'common',
        firstSpotter: s.firstSpotter, count: s.count,
        when: fmtWhen(s.createdAt),
        diveSite: (d && d.site) || 'Unknown',
      };
    });

  // Recent bonuses — last 4 unlocked achievements, newest first
  const recentBonuses = state.achievements
    .filter(a => a.unlocked)
    .slice()
    .sort((a, b) => (a.unlockedAt < b.unlockedAt ? 1 : -1))
    .slice(0, 4)
    .map(a => ({ ...a, when: fmtWhen(a.unlockedAt) }));

  return (
    <div className="dd-screen" style={{ paddingBottom: 110 }}>
      <TopBar title="Team" sub={teamTitle} left={<IconBtn onClick={() => nav('dashboard')}>‹</IconBtn>} right={<IconBtn>＋</IconBtn>}/>
      <div style={{ padding: '0 18px' }}>
        {/* Quick jump to Achievements + Side Quests — the only entry points
            otherwise live in Memories event taps, which isn't discoverable. */}
        <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 8, marginBottom: 12 }}>
          <button onClick={() => nav('achievements')} className="dd-btn dd-btn-ghost" style={{ height: 40, borderRadius: 12, fontSize: 12.5, fontWeight: 700, display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 6 }}>
            <span style={{ color: '#D7B46A' }}>✦</span> Achievements
          </button>
          <button onClick={() => nav('sidequests')} className="dd-btn dd-btn-ghost" style={{ height: 40, borderRadius: 12, fontSize: 12.5, fontWeight: 700, display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 6 }}>
            <span style={{ color: '#C8B6FF' }}>◆</span> Side Quests
          </button>
        </div>
        <GlassCard accent="#82ECFF" style={{ padding: 18, borderRadius: 22, background: 'linear-gradient(160deg, rgba(49,215,255,0.16), rgba(11,42,68,0.5), rgba(5,11,20,0.7))', borderColor: 'rgba(49,215,255,0.3)' }}>
          <div className="dd-h-section" style={{ color: '#82ECFF' }}>TEAM XP</div>
          <div style={{ display: 'flex', alignItems: 'baseline', gap: 8, marginTop: 2 }}>
            <span className="dd-num" style={{ fontSize: 44, fontWeight: 700, color: '#fff', letterSpacing: '-0.03em' }}>{teamXp.toLocaleString()}</span>
            {todayXp > 0 && <span style={{ color: '#63FFD2', fontWeight: 700, fontSize: 12 }}>+{todayXp.toLocaleString()} today</span>}
          </div>
          <div className="dd-shimmer" style={{ marginTop: 6, fontFamily: 'Space Grotesk', fontSize: 16, fontWeight: 700 }}>{teamTitle}</div>
          <div style={{ marginTop: 10 }}><XPBar value={Math.min(teamXp, nextTitleAt)} max={nextTitleAt} color="#82ECFF" height={8} label={null} sub={null}/></div>
          <div style={{ fontSize: 11, color: 'var(--ink-3)', marginTop: 6 }}>{Math.max(0, nextTitleAt - teamXp).toLocaleString()} XP to next title · <span style={{ color:'#82ECFF', fontWeight: 600 }}>Pelagic Pair</span></div>
        </GlassCard>

        {/* Player splits — rank by per-user XP */}
        <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 10, marginTop: 12 }}>
          {playerViews.map((pv, i) => <PlayerCard key={pv.id} person={pv} rank={i + 1}/>)}
        </div>

        {/* Air Zen — counts from actual dives */}
        <div className="dd-h-section" style={{ marginTop: 18, marginBottom: 8 }}>Air Zen</div>
        <GlassCard style={{ padding: 14, borderRadius: 18, background: 'linear-gradient(160deg, rgba(99,255,210,0.10), rgba(11,42,68,0.4))', borderColor: 'rgba(99,255,210,0.25)' }}>
          <div style={{ display: 'flex', alignItems: 'center', gap: 12, marginBottom: 12 }}>
            <div style={{ width: 44, height: 44, borderRadius: 14, background: 'radial-gradient(circle at 30% 30%, #63FFD2, #1c5c4a)', display: 'grid', placeItems: 'center', color: '#011d14', fontWeight: 800, fontSize: 18 }}>◯</div>
            <div style={{ flex: 1 }}>
              <div style={{ fontSize: 13, fontWeight: 700 }}>Calm dive bonuses</div>
              <div style={{ fontSize: 11, color: 'var(--ink-3)', lineHeight: 1.4, marginTop: 2 }}>{airZenDives === 0 ? 'Surface with at least 50 bar in the tank to earn the bonus.' : `${airZenDives} of ${state.dives.length} dive${state.dives.length === 1 ? '' : 's'} earned the bonus.`}</div>
            </div>
            <span className="dd-num" style={{ fontSize: 18, fontWeight: 700, color: '#63FFD2' }}>+{airZenXpTotal.toLocaleString()}</span>
          </div>
          <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 8 }}>
            <AirZenRow label={`Bonus dives · ×${airZenDives}`} value={`+${(airZenDives * 125).toLocaleString()}`}/>
            <AirZenRow label={`Pearls from Air Zen · ×${airZenDives}`} value={`+${(airZenDives * 10).toLocaleString()}`}/>
            <AirZenRow label="Safe reserve threshold" value={`${state.settings.safeReserveBar} bar`}/>
            <AirZenRow label="Per-dive XP" value="+125"/>
          </div>
        </GlassCard>

        {/* First spotter — who calls what */}
        <div className="dd-h-section" style={{ marginTop: 18, marginBottom: 8 }}>First spotter</div>
        <GlassCard style={{ padding: 14, borderRadius: 16 }}>
          {spotterBars.map(b => <SpotterBar key={b.id} name={b.name} pct={b.pct} color={b.color} wins={b.wins}/>)}
          {totalSpots === 0 && <div style={{ fontSize: 11.5, color: 'var(--ink-3)', textAlign: 'center', padding: '4px 0' }}>No sightings logged yet. The R / S toggle on each sighting attributes the first-spot.</div>}
        </GlassCard>

        {/* Recent first-spots — visual feed: who spotted what */}
        {recentSpots.length > 0 && (
          <>
            <div className="dd-h-section" style={{ marginTop: 18, marginBottom: 8 }}>Recent first-spots</div>
            <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
              {recentSpots.map(s => {
                const user = state.users.find(u => u.id === s.firstSpotter);
                if (!user) return null;
                return (
                  <div key={s.id} onClick={() => nav('creature', s.creatureId)} className="dd-glass" style={{
                    padding: 10, borderRadius: 14, cursor: 'pointer',
                    display: 'flex', alignItems: 'center', gap: 10,
                    background: `linear-gradient(90deg, ${user.color}14, rgba(11,42,68,0.4) 60%)`,
                    borderColor: `${user.color}44`,
                  }}>
                    <RarityRing rarity={s.rarity} size={44}>
                      <CrSilhouette id={s.creatureId} size={32}/>
                    </RarityRing>
                    <div style={{ flex: 1, minWidth: 0 }}>
                      <div style={{ fontSize: 12.5, fontWeight: 700, lineHeight: 1.15 }}>{s.commonName}</div>
                      <div style={{ fontSize: 10.5, color: 'var(--ink-3)', marginTop: 2 }}>
                        <span style={{ color: user.color, fontWeight: 700 }}>{user.displayName}</span> spotted it · {s.diveSite} · {s.when}
                      </div>
                    </div>
                    <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'flex-end', gap: 2 }}>
                      <Avatar name={user.displayName} color={user.color} size={26}/>
                      <span className="dd-num" style={{ fontSize: 10, color: 'var(--ink-3)', fontWeight: 700 }}>×{s.count}</span>
                    </div>
                  </div>
                );
              })}
            </div>
          </>
        )}

        {/* Recent bonuses — latest unlocked achievements */}
        {recentBonuses.length > 0 && (
          <>
            <div className="dd-h-section" style={{ marginTop: 18, marginBottom: 8 }}>Recent bonuses</div>
            <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
              {recentBonuses.map(b => (
                <div key={b.id} className="dd-glass" style={{ padding: '10px 12px', borderRadius: 12, display: 'flex', alignItems: 'center', gap: 10 }}>
                  <div style={{ width: 26, height: 26, borderRadius: 8, background: 'rgba(215,180,106,0.18)', border: '1px solid rgba(215,180,106,0.4)', color: '#FBE6A6', display: 'grid', placeItems: 'center', fontSize: 13, fontWeight: 700 }}>{b.icon || '✦'}</div>
                  <div style={{ flex: 1 }}>
                    <div style={{ fontSize: 12.5, fontWeight: 600 }}>{b.name}</div>
                    <div style={{ fontSize: 10.5, color: 'var(--ink-3)' }}>{b.category} · {b.when}</div>
                  </div>
                  <span className="dd-num" style={{ color: '#D7B46A', fontSize: 12, fontWeight: 700 }}>+{(b.rewardXp || 0).toLocaleString()}</span>
                </div>
              ))}
            </div>
          </>
        )}
      </div>
    </div>
  );
}

// Build a player view object from state — used by Team + Settings PlayerCards.
function buildPlayerView(state, userId) {
  const u = state.users.find(x => x.id === userId);
  if (!u) return null;
  const xp = state.xp.userXp[userId] || 0;
  const firstSpots = state.sightings.filter(s => s.firstSpotter === userId).length;
  const equipped = state.equippedItems[userId] || {};
  // Prefer equipped title cosmetic name, otherwise the seeded defaultTitle.
  let primaryTitle = u.defaultTitle;
  if (equipped.title) {
    const titleItem = (window.BOUTIQUE_ITEMS || []).find(i => i.id === equipped.title);
    if (titleItem) primaryTitle = titleItem.name;
  }
  const titles = [primaryTitle, u.secondaryTitle].filter(Boolean);
  return { id: userId, name: u.displayName, color: u.color, xp, titles, firstSpots };
}

function PlayerCard({ person, rank }) {
  return (
    <GlassCard style={{ padding: 14, borderRadius: 18 }}>
      <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
        <Avatar name={person.name} color={person.color} size={40}/>
        <div style={{ flex: 1, minWidth: 0 }}>
          <div style={{ fontSize: 13.5, fontWeight: 700 }}>{person.name}</div>
          <div className="dd-num" style={{ fontSize: 15, color: person.color, fontWeight: 700 }}>{person.xp.toLocaleString()} XP</div>
          {typeof person.firstSpots === 'number' && person.firstSpots > 0 && (
            <div style={{ fontSize: 10, color: 'var(--ink-3)', marginTop: 1 }}>
              <span className="dd-num" style={{ color: person.color, fontWeight: 700 }}>{person.firstSpots}</span> first-spot{person.firstSpots === 1 ? '' : 's'}
            </div>
          )}
        </div>
        <span className="dd-num" style={{ fontSize: 11, color: 'var(--ink-3)', fontWeight: 600 }}>#{rank}</span>
      </div>
      <div style={{ display: 'flex', flexWrap: 'wrap', gap: 4, marginTop: 10 }}>
        {(person.titles || []).slice(0, 2).map(t => <Chip key={t} style={{ fontSize: 10, padding: '3px 7px' }}>{t}</Chip>)}
      </div>
    </GlassCard>
  );
}

function AirZenRow({ label, value }) {
  return (
    <div style={{ display: 'flex', justifyContent: 'space-between', padding: '6px 0', borderTop: '1px dashed rgba(99,255,210,0.18)' }}>
      <span style={{ fontSize: 11.5, color: 'var(--ink-2)' }}>{label}</span>
      <span className="dd-num" style={{ fontSize: 11.5, color: '#63FFD2', fontWeight: 700 }}>{value}</span>
    </div>
  );
}

function SpotterBar({ name, pct, color, wins }) {
  return (
    <div style={{ marginBottom: 10 }}>
      <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline', marginBottom: 5 }}>
        <span style={{ fontSize: 12, fontWeight: 600 }}>{name}</span>
        <span className="dd-num" style={{ fontSize: 11, color: 'var(--ink-3)' }}>{wins} first · {pct}%</span>
      </div>
      <div style={{ height: 6, borderRadius: 3, background: 'rgba(255,255,255,0.05)', overflow: 'hidden' }}>
        <div style={{ width: `${pct}%`, height: '100%', background: `linear-gradient(90deg, ${color}99, ${color})`, boxShadow: `0 0 8px ${color}88` }}/>
      </div>
    </div>
  );
}

function AchievementsScreen({ nav }) {
  const { state } = useDiveDex();
  const all = state.achievements;
  const cats = ['All', ...Array.from(new Set(all.map(a => a.category)))];
  const [cat, setCat] = useState('All');
  const items = cat === 'All' ? all : all.filter(b => b.category === cat);
  const got = all.filter(b => b.unlocked).length;
  return (
    <div className="dd-screen" style={{ paddingBottom: 110 }}>
      <TopBar title="Achievements" sub={`${got} of ${all.length} unlocked`} left={<IconBtn onClick={() => nav('dashboard')}>‹</IconBtn>} right={<IconBtn>⌕</IconBtn>}/>
      <div style={{ padding: '0 18px' }}>
        <GlassCard style={{ padding: 14, borderRadius: 16 }}>
          <XPBar value={got} max={all.length} color="#D7B46A" height={8} label="Trip badge collection" sub={`${got}/${all.length}`}/>
        </GlassCard>
        <div style={{ display: 'flex', gap: 6, overflowX: 'auto', margin: '14px -18px 12px', padding: '0 18px', scrollbarWidth: 'none' }}>
          {cats.map(c => (
            <button key={c} onClick={() => setCat(c)} className="dd-btn" style={{
              flex: '0 0 auto', padding: '7px 12px', borderRadius: 999, fontSize: 11.5, fontWeight: 600,
              background: cat === c ? 'rgba(215,180,106,0.16)' : 'rgba(255,255,255,0.04)',
              color: cat === c ? '#D7B46A' : 'var(--ink-2)',
              border: cat === c ? '1px solid rgba(215,180,106,0.32)' : '1px solid rgba(130,236,255,0.10)',
            }}>{c}</button>
          ))}
        </div>
        <div style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: 8 }}>
          {items.map(b => <BadgeTile key={b.id} b={b}/>)}
        </div>
      </div>
    </div>
  );
}

function BadgeTile({ b }) {
  // On locked tiles, show the unlock condition (description) so the user
  // knows what to chase — no more guessing.
  return (
    <RarityFrame rarity={b.rarity} style={{ opacity: b.unlocked ? 1 : 0.6 }}>
      <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 6, minWidth: 0 }}>
        <div style={{ width: 44, height: 44, flex: '0 0 44px', borderRadius: 14, background: 'rgba(0,0,0,0.4)', border: '1px solid var(--r-color)', boxShadow: b.unlocked ? '0 0 14px var(--r-glow)' : 'none', display: 'grid', placeItems: 'center', color: 'var(--r-color)', fontSize: 18, fontWeight: 800 }}>{b.unlocked ? b.icon : '🔒'}</div>
        <div style={{ fontSize: 10.5, fontWeight: 700, textAlign: 'center', lineHeight: 1.15, color: b.unlocked ? '#fff' : 'var(--ink-3)', overflowWrap: 'break-word', maxWidth: '100%' }}>{b.name}</div>
        <div style={{ fontSize: 8.5, color: 'var(--r-color)', textTransform: 'uppercase', letterSpacing: '0.1em', fontWeight: 700 }}>{b.rarity}</div>
        {!b.unlocked && b.description && (
          <div style={{ fontSize: 9, fontStyle: 'italic', color: 'var(--ink-3)', textAlign: 'center', lineHeight: 1.25, marginTop: 2, padding: '0 2px', overflowWrap: 'break-word' }}>{b.description}</div>
        )}
      </div>
    </RarityFrame>
  );
}

function SideQuestsScreen({ nav }) {
  const { state, actions } = useDiveDex();
  const quests = state.sideQuests;
  const active = quests.filter(q => q.state === 'active');
  const completed = quests.filter(q => q.state === 'completed');
  const locked = quests.filter(q => q.state === 'locked');

  // Count how many times each repeatable quest has been logged, for the
  // "Repeatable · ×N" chip on the card.
  const repeatCounts = (state.sideQuestCompletions || []).reduce((acc, c) => {
    acc[c.questId] = (acc[c.questId] || 0) + 1;
    return acc;
  }, {});

  // Inline "just completed" banner so tapping a quest doesn't feel like a
  // silent no-op. Computes the delta from before/after the action so we can
  // show bridged achievement + cosmetic grants too (e.g. Seven-Eleven Snack
  // Run unlocks Snack Logistics Champion → Snack Pack + Title).
  const [banner, setBanner] = React.useState(null);
  const onCompleteWithFeedback = (questId) => {
    const before = {
      teamXp: state.xp.teamXp,
      rickXp: state.xp.userXp.rick,
      sietXp: state.xp.userXp.siet,
      pearls: state.currencies.pearls,
      unlocks: state.unlockedAchievementIds.slice(),
      inventory: state.inventory.slice(),
    };
    actions.completeSideQuest(questId);
    // Read latest state on next frame so deltas reflect the action.
    setTimeout(() => {
      const now = window.__DD_STATE__ || state;
      const newUnlocks = now.unlockedAchievementIds.filter(id => before.unlocks.indexOf(id) < 0);
      const newItems = now.inventory.filter(id => before.inventory.indexOf(id) < 0);
      const teamDelta = now.xp.teamXp - before.teamXp;
      const rickDelta = now.xp.userXp.rick - before.rickXp;
      const sietDelta = now.xp.userXp.siet - before.sietXp;
      const pearlDelta = now.currencies.pearls - before.pearls;
      const quest = quests.find(q => q.id === questId);
      const newAchievements = newUnlocks.map(aid => now.achievements.find(a => a.id === aid)).filter(Boolean);
      const newCosmetics = newItems.map(iid => (window.BOUTIQUE_ITEMS || []).find(x => x.id === iid)).filter(Boolean);
      setBanner({
        questTitle: quest ? quest.title : 'Side quest',
        attribution: quest ? (quest.attribution || 'team') : 'team',
        repeatable: !!(quest && quest.repeatable),
        teamDelta, rickDelta, sietDelta, pearlDelta, newAchievements, newCosmetics,
      });
      setTimeout(() => setBanner(null), 4200);
    }, 80);
  };

  return (
    <div className="dd-screen" style={{ paddingBottom: 110 }}>
      <TopBar title="Side Quests" sub={`Japan · ${quests.length} quests`} left={<IconBtn onClick={() => nav('dashboard')}>‹</IconBtn>}/>
      <div style={{ padding: '0 18px' }}>
        {banner && (() => {
          const attrColor = banner.attribution === 'rick' ? '#31D7FF' : banner.attribution === 'siet' ? '#63FFD2' : '#D7B46A';
          const headerLabel = banner.repeatable ? 'QUEST LOGGED' : 'QUEST COMPLETE';
          return (
            <div className="dd-glass dd-reveal" style={{
              marginBottom: 12, padding: 12, borderRadius: 14,
              background: `linear-gradient(160deg, ${attrColor}29, rgba(11,42,68,0.4))`,
              borderColor: `${attrColor}55`,
            }}>
              <div style={{ display: 'flex', alignItems: 'baseline', justifyContent: 'space-between', gap: 8 }}>
                <div style={{ fontSize: 10.5, fontWeight: 700, color: attrColor, letterSpacing: '0.14em' }}>{headerLabel}</div>
                <div className="dd-num" style={{ fontSize: 12, fontWeight: 700, color: attrColor }}>
                  +{banner.teamDelta.toLocaleString()} XP · +{banner.pearlDelta} Pearls
                </div>
              </div>
              <div style={{ fontSize: 14, fontWeight: 700, color: '#fff', marginTop: 2 }}>{banner.questTitle}</div>
              {/* Per-user XP delta — shows who actually pocketed the XP */}
              <div style={{ marginTop: 6, display: 'flex', gap: 8, fontSize: 11 }}>
                {banner.rickDelta > 0 && (
                  <span style={{ color: '#31D7FF', fontWeight: 700 }}>Rick +{banner.rickDelta}</span>
                )}
                {banner.sietDelta > 0 && (
                  <span style={{ color: '#63FFD2', fontWeight: 700 }}>Siet +{banner.sietDelta}</span>
                )}
              </div>
              {(banner.newAchievements.length > 0 || banner.newCosmetics.length > 0) && (
                <div style={{ marginTop: 8, display: 'flex', flexWrap: 'wrap', gap: 6 }}>
                  {banner.newAchievements.map(a => <Chip key={a.id} variant="gold" icon={a.icon || '✦'}>{a.name}</Chip>)}
                  {banner.newCosmetics.map(it => <Chip key={it.id} variant="foam" icon="◈">{it.name}</Chip>)}
                </div>
              )}
            </div>
          );
        })()}
        {active.length > 0 && <>
          <div className="dd-h-section" style={{ marginBottom: 8 }}>Active</div>
          <div style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>
            {active.map(q => <SideQuestCard key={q.id} q={q} repeatCount={repeatCounts[q.id] || 0} onComplete={() => onCompleteWithFeedback(q.id)}/>)}
          </div>
        </>}
        {completed.length > 0 && <>
          <div className="dd-h-section" style={{ marginTop: 18, marginBottom: 8 }}>Completed</div>
          <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
            {completed.map(q => <SideQuestCard key={q.id} q={q}/>)}
          </div>
        </>}
        {locked.length > 0 && <>
          <div className="dd-h-section" style={{ marginTop: 18, marginBottom: 8 }}>Locked</div>
          <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
            {locked.map(q => <SideQuestCard key={q.id} q={q}/>)}
          </div>
        </>}
        <div className="dd-glass" style={{ marginTop: 18, padding: 12, borderRadius: 12, fontSize: 11, color: 'var(--ink-3)', lineHeight: 1.5 }}>
          Side quests are optional. Diving comes first — these are travel moments that round out the level. Tap an active quest to mark it complete.
        </div>
      </div>
    </div>
  );
}

function SideQuestCard({ q, onComplete, repeatCount = 0 }) {
  const isComp = q.state === 'completed';
  const isLocked = q.state === 'locked';
  const xp = q.rewardXp ?? q.xp ?? 0;
  const attribution = q.attribution || 'team';
  const isRick = attribution === 'rick';
  const isSiet = attribution === 'siet';
  const attrColor = isRick ? '#31D7FF' : isSiet ? '#63FFD2' : '#D7B46A';
  const attrLabel = isRick ? 'Rick XP' : isSiet ? 'Siet XP' : 'Team XP';
  return (
    <div onClick={onComplete && !isLocked && (q.repeatable || !isComp) ? onComplete : undefined} className="dd-glass" style={{
      padding: 14, borderRadius: 16, display: 'flex', alignItems: 'center', gap: 12,
      opacity: isLocked ? 0.55 : 1,
      cursor: onComplete && !isLocked && (q.repeatable || !isComp) ? 'pointer' : 'default',
      ...(q.state === 'active' ? { background: 'linear-gradient(160deg, rgba(49,215,255,0.10), rgba(11,42,68,0.4))', borderColor: 'rgba(49,215,255,0.22)' } : {}),
    }}>
      <div style={{ width: 44, height: 44, flex: '0 0 44px', borderRadius: 14, background: 'rgba(255,255,255,0.05)', display: 'grid', placeItems: 'center', fontSize: 22, border: '1px solid rgba(130,236,255,0.10)' }}>{q.badge}</div>
      <div style={{ flex: 1, minWidth: 0 }}>
        <div style={{ fontSize: 13.5, fontWeight: 700, lineHeight: 1.15 }}>{q.title}</div>
        <div style={{ fontSize: 11, color: 'var(--ink-3)', marginTop: 2 }}>{q.sub}</div>
        <div style={{ display: 'flex', gap: 6, marginTop: 6, flexWrap: 'wrap' }}>
          {attribution !== 'team' && (
            <span style={{
              fontSize: 9.5, fontWeight: 700, letterSpacing: '0.1em', textTransform: 'uppercase',
              padding: '2px 7px', borderRadius: 999, color: attrColor,
              background: `${attrColor}1A`, border: `1px solid ${attrColor}55`,
            }}>{attrLabel}</span>
          )}
          {q.repeatable && (
            <span style={{
              fontSize: 9.5, fontWeight: 700, letterSpacing: '0.1em', textTransform: 'uppercase',
              padding: '2px 7px', borderRadius: 999, color: 'var(--ink-2)',
              background: 'rgba(255,255,255,0.04)', border: '1px solid rgba(130,236,255,0.18)',
            }}>Repeatable{repeatCount > 0 ? ` · ×${repeatCount}` : ''}</span>
          )}
        </div>
      </div>
      <div style={{ textAlign: 'right', flex: '0 0 auto' }}>
        <span className="dd-num" style={{ color: attrColor, fontWeight: 700, fontSize: 13 }}>+{xp}</span>
        <div style={{ marginTop: 4 }}>
          {isLocked && <Chip style={{ fontSize: 9.5, padding: '2px 6px' }}>Locked</Chip>}
          {!isLocked && q.repeatable && <Chip variant="aqua" style={{ fontSize: 9.5, padding: '2px 6px' }}>Tap to log</Chip>}
          {!isLocked && !q.repeatable && isComp && <Chip variant="foam" style={{ fontSize: 9.5, padding: '2px 6px' }}>✓ Done</Chip>}
          {!isLocked && !q.repeatable && !isComp && <Chip variant="aqua" style={{ fontSize: 9.5, padding: '2px 6px' }}>Tap to complete</Chip>}
        </div>
      </div>
    </div>
  );
}

// Build a chronological event stream from state. Each entry is an event the
// user generated (dive logged, creature discovered, achievement unlocked,
// side quest completed, cosmetic granted). Newest day on top; events within
// a day sorted oldest first so the day reads naturally top-to-bottom.
function buildMemoryEvents(state) {
  const events = [];
  const dayKey = (iso) => iso ? new Date(iso).toISOString().slice(0, 10) : 'unknown';
  const userName = (id) => id === 'rick' ? 'Rick' : id === 'siet' ? 'Siet' : 'Crew';
  const creatureById = (id) => state.creatures.find(c => c.id === id);
  const catalog = window.BOUTIQUE_ITEMS || [];

  // Dives
  state.dives.forEach(d => {
    events.push({
      id: 'dive-' + d.id, kind: 'dive', when: d.createdAt, dayKey: dayKey(d.createdAt),
      title: `Dive ${d.no} · ${d.site || 'Unknown site'}`,
      subtitle: `${d.maxDepth || '?'}m · ${d.time || '?'}min · ${d.gas || ''}`.trim(),
      xp: d.totalXp || 0, pearls: d.pearls || 0,
      legendary: !!(d.unlockedAchievementIds || []).some(a => {
        const ach = state.achievements.find(x => x.id === a);
        return ach && (ach.rarity === 'legendary' || ach.rarity === 'mythic');
      }),
      onClick: (nav) => nav('recap', d.id),
    });
  });

  // First-spot discoveries (one event per newly discovered creature)
  state.creatures.filter(c => c.discovered && c.firstSeenAt).forEach(c => {
    events.push({
      id: 'disc-' + c.id, kind: 'discovery', when: c.firstSeenAt, dayKey: dayKey(c.firstSeenAt),
      title: `Discovered · ${c.commonName}`,
      subtitle: c.firstSeenBy ? `Spotted by ${userName(c.firstSeenBy)}` : 'Discovered',
      creatureId: c.id, rarity: c.rarity,
      legendary: c.rarity === 'legendary' || c.rarity === 'mythic',
      onClick: (nav) => nav('creature', c.id),
    });
  });

  // Achievements
  state.achievements.filter(a => a.unlocked && a.unlockedAt).forEach(a => {
    events.push({
      id: 'ach-' + a.id, kind: 'achievement', when: a.unlockedAt, dayKey: dayKey(a.unlockedAt),
      title: `Achievement · ${a.name}`,
      subtitle: a.description || a.category,
      xp: a.rewardXp || 0, pearls: a.rewardPearls || 0,
      icon: a.icon, rarity: a.rarity,
      legendary: a.rarity === 'legendary' || a.rarity === 'mythic',
      onClick: (nav) => nav('achievements'),
    });
  });

  // Side quests
  state.sideQuests.filter(q => q.state === 'completed' && q.completedAt).forEach(q => {
    events.push({
      id: 'sq-' + q.id, kind: 'sidequest', when: q.completedAt, dayKey: dayKey(q.completedAt),
      title: `Side quest · ${q.title}`,
      subtitle: q.sub || '',
      xp: q.rewardXp || 0, pearls: q.rewardPearls || 0,
      icon: q.badge,
      onClick: (nav) => nav('sidequests'),
    });
  });

  // Cosmetic bridge — one event per item granted by each dive
  state.dives.forEach(d => {
    (d.bridgedItemIds || []).forEach(iid => {
      const item = catalog.find(x => x.id === iid);
      if (!item) return;
      events.push({
        id: 'cosm-' + d.id + '-' + iid, kind: 'cosmetic', when: d.createdAt, dayKey: dayKey(d.createdAt),
        title: `Unlocked · ${item.name}`,
        subtitle: `${item.cat} · ${item.rarity}`,
        rarity: item.rarity, itemId: iid,
        legendary: item.rarity === 'legendary' || item.rarity === 'mythic',
        onClick: (nav) => nav('item', iid),
      });
    });
  });

  // Sort newest day first; within a day, oldest first
  const byDay = {};
  events.forEach(e => { (byDay[e.dayKey] = byDay[e.dayKey] || []).push(e); });
  Object.values(byDay).forEach(arr => arr.sort((a, b) => (a.when < b.when ? -1 : 1)));
  const days = Object.keys(byDay).sort((a, b) => (a < b ? 1 : -1));
  return { days, byDay };
}

function MemoriesScreen({ nav }) {
  const { state } = useDiveDex();
  const expedition = state.expeditions.find(e => e.id === state.activeExpeditionId) || state.expeditions[0];
  const teamTitle = 'Luxury Shark Expedition Duo';

  // Real stats for the hero card
  const totalXp = state.xp.teamXp;
  const region = expedition ? expedition.region : null;
  const regionalCreatures = region
    ? state.creatures.filter(c => (c.regions || []).indexOf(region) >= 0)
    : state.creatures;
  const speciesFound = regionalCreatures.filter(c => c.discovered).length;
  const speciesTotal = regionalCreatures.length;
  const dayCount = state.dives.length === 0 ? 0 : (() => {
    const days = new Set(state.dives.map(d => d.createdAt && d.createdAt.slice(0, 10)).filter(Boolean));
    return days.size;
  })();

  // Highlights: discovered creatures, newest first, top 9
  const highlights = state.creatures
    .filter(c => c.discovered)
    .slice()
    .sort((a, b) => ((a.firstSeenAt || '') < (b.firstSeenAt || '') ? 1 : -1))
    .slice(0, 9);

  // Timeline events
  const { days, byDay } = buildMemoryEvents(state);

  const fmtDay = (key) => {
    if (key === 'unknown') return 'Earlier';
    const d = new Date(key + 'T00:00:00');
    return d.toLocaleDateString(undefined, { month: 'short', day: 'numeric', weekday: 'short' });
  };
  const fmtTime = (iso) => iso ? new Date(iso).toLocaleTimeString(undefined, { hour: '2-digit', minute: '2-digit' }) : '';

  // Kind → visual config
  const kindStyle = {
    dive:        { bg: 'rgba(49,215,255,0.16)',  fg: '#82ECFF', icon: '🤿', label: 'DIVE' },
    discovery:   { bg: 'rgba(99,255,210,0.14)',  fg: '#63FFD2', icon: '✦',  label: 'NEW SPECIES' },
    achievement: { bg: 'rgba(215,180,106,0.18)', fg: '#D7B46A', icon: '✸',  label: 'BADGE' },
    sidequest:   { bg: 'rgba(139,123,255,0.16)', fg: '#C8B6FF', icon: '◆',  label: 'SIDE QUEST' },
    cosmetic:    { bg: 'rgba(255,122,107,0.14)', fg: '#FF9D8E', icon: '◈',  label: 'COSMETIC' },
  };

  const latestDiveId = state.dives.length ? state.dives[state.dives.length - 1].id : null;
  const empty = days.length === 0;

  return (
    <div className="dd-screen" style={{ paddingBottom: 110 }}>
      <TopBar
        title="Memories"
        sub={expedition ? expedition.title : 'Trip'}
        left={<IconBtn onClick={() => nav('dashboard')}>‹</IconBtn>}
        right={latestDiveId ? <IconBtn onClick={() => nav('story', latestDiveId)}>↗</IconBtn> : null}
      />
      <div style={{ padding: '0 18px' }}>

        {/* Hero recap — real stats */}
        <GlassCard style={{ padding: 0, borderRadius: 22, overflow: 'hidden' }}>
          <div style={{ position: 'relative', minHeight: 168,
            background: 'radial-gradient(120% 80% at 80% 0%, rgba(49,215,255,0.28), rgba(11,42,68,0.5) 50%, rgba(5,11,20,0.95))',
          }}>
            <div style={{ position: 'absolute', top: -10, right: -20, opacity: 0.55 }}>
              <CrSilhouette id={expedition?.hero || 'scalloped-hammerhead'} color="#31D7FF" size={170}/>
            </div>
            <div style={{ position: 'relative', padding: '20px 18px 16px' }}>
              <div style={{ fontSize: 10, fontWeight: 700, color: '#82ECFF', letterSpacing: '0.18em' }}>TRIP</div>
              <div className="dd-h1" style={{ fontSize: 22, lineHeight: 1.05, marginTop: 4 }}>{teamTitle}</div>
              <div style={{ display:'flex', gap: 6, marginTop: 10, flexWrap: 'wrap' }}>
                {dayCount > 0 && <Chip variant="aqua">{dayCount} {dayCount === 1 ? 'day' : 'days'}</Chip>}
                <Chip variant="gold">{totalXp.toLocaleString()} XP</Chip>
                <Chip variant="foam">{speciesFound}/{speciesTotal} species</Chip>
                <Chip>{state.dives.length} {state.dives.length === 1 ? 'dive' : 'dives'}</Chip>
              </div>
            </div>
          </div>
        </GlassCard>

        {/* Trip map — all logged dives as pins. Each pin opens that dive's recap. */}
        {(() => {
          const pins = state.dives
            .map(d => ({ dive: d, geo: window.resolveDiveSiteGeo && window.resolveDiveSiteGeo(d.site) }))
            .filter(x => x.geo)
            .map(x => ({ name: x.geo.name, lat: x.geo.lat, lng: x.geo.lng, _diveId: x.dive.id, highlight: x.dive.id === latestDiveId }));
          if (pins.length === 0) return null;
          // Pins may share the same site — dedupe by name for the marker layer,
          // but keep the original list to attribute the latest dive's id to its tap.
          const byName = {};
          pins.forEach(p => {
            if (!byName[p.name] || p.highlight) byName[p.name] = p;
          });
          const uniq = Object.values(byName);
          const onTap = (name) => {
            const hit = pins.slice().reverse().find(p => p.name === name);
            if (hit) nav('recap', hit._diveId);
          };
          return (
            <>
              <div className="dd-h-section" style={{ marginTop: 18, marginBottom: 8 }}>Trip map</div>
              <DiveSiteMap region={uniq[0] && (window.DD_STATE.SEED_DIVE_SITE_GEO[uniq[0].name] || {}).region}
                           sites={uniq} height={220} onSiteTap={onTap}/>
              <div style={{ marginTop: 6, fontSize: 10.5, color: 'var(--ink-3)', lineHeight: 1.5 }}>
                Tap a pin to jump to that dive's recap. Gold pin · latest dive.
              </div>
            </>
          );
        })()}

        {/* Trip gallery — all attached photos across logged dives. */}
        <TripGallery diveIds={state.dives.slice().reverse().map(d => d.id)}/>

        {/* Highlights — real discovered creatures */}
        {highlights.length > 0 && (
          <>
            <div className="dd-h-section" style={{ marginTop: 18, marginBottom: 8 }}>Highlights</div>
            <div style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: 6 }}>
              {highlights.map(c => (
                <div key={c.id} onClick={() => nav('creature', c.id)} className={`dd-glass dd-rarity-${c.rarity}`} style={{
                  aspectRatio: '1 / 1', borderRadius: 12, display: 'grid', placeItems: 'center',
                  cursor: 'pointer', position: 'relative', overflow: 'hidden',
                  borderColor: 'var(--r-color)',
                }}>
                  <CrSilhouette id={c.id} size={88}/>
                  <div style={{ position: 'absolute', left: 6, right: 6, bottom: 6, fontSize: 9, fontWeight: 700, color: '#EAF5FF', textAlign: 'center', textShadow: '0 1px 4px rgba(0,0,0,0.6)', lineHeight: 1.1 }}>{c.commonName}</div>
                </div>
              ))}
            </div>
          </>
        )}

        {/* Timeline — real events grouped by day */}
        <div className="dd-h-section" style={{ marginTop: 18, marginBottom: 10 }}>Timeline</div>

        {empty && (
          <GlassCard style={{ padding: 16, borderRadius: 14, fontSize: 12.5, color: 'var(--ink-3)', textAlign: 'center', lineHeight: 1.5 }}>
            Nothing here yet. Log a dive, complete a side quest, or unlock a badge — they'll all show up on the timeline.
          </GlassCard>
        )}

        {!empty && (
          <div style={{ position: 'relative', paddingLeft: 18 }}>
            <div style={{ position: 'absolute', left: 6, top: 4, bottom: 4, width: 1, background: 'linear-gradient(180deg, rgba(49,215,255,0.5), rgba(99,255,210,0.4), rgba(215,180,106,0.4))' }}/>
            {days.map(dKey => (
              <div key={dKey} style={{ position: 'relative', marginBottom: 16 }}>
                <div style={{ position: 'absolute', left: -18, top: 5, width: 13, height: 13, borderRadius: '50%', background: '#0B2A44', border: '2px solid #31D7FF', boxShadow: '0 0 10px rgba(49,215,255,0.6)' }}/>
                <div style={{ fontSize: 11, color: 'var(--ink-3)', fontWeight: 600 }}>{fmtDay(dKey)}</div>
                <div className="dd-h2" style={{ fontSize: 14, marginTop: 2 }}>{byDay[dKey].length} event{byDay[dKey].length === 1 ? '' : 's'}</div>
                <div style={{ display: 'flex', flexDirection: 'column', gap: 6, marginTop: 8 }}>
                  {byDay[dKey].map(ev => {
                    const k = kindStyle[ev.kind] || kindStyle.dive;
                    return (
                      <div key={ev.id} onClick={() => ev.onClick && ev.onClick(nav)} className="dd-glass" style={{
                        padding: '10px 12px', borderRadius: 12, display: 'flex', alignItems: 'center', gap: 10,
                        cursor: ev.onClick ? 'pointer' : 'default',
                        ...(ev.legendary ? { borderColor: 'rgba(215,180,106,0.35)', background: 'linear-gradient(120deg, rgba(215,180,106,0.10), rgba(11,42,68,0.3))' } : {}),
                      }}>
                        {ev.kind === 'discovery' && ev.creatureId ? (
                          <div style={{ width: 30, height: 30, display: 'grid', placeItems: 'center' }}>
                            <CrSilhouette id={ev.creatureId} size={28}/>
                          </div>
                        ) : (
                          <div style={{ width: 28, height: 28, borderRadius: 8, background: k.bg, display: 'grid', placeItems: 'center', fontSize: 13, color: k.fg, fontWeight: 700 }}>
                            {ev.icon || k.icon}
                          </div>
                        )}
                        <div style={{ flex: 1, minWidth: 0 }}>
                          <div style={{ fontSize: 9.5, fontWeight: 700, color: k.fg, letterSpacing: '0.12em', lineHeight: 1.0 }}>{k.label}{ev.when ? ' · ' + fmtTime(ev.when) : ''}</div>
                          <div style={{ fontSize: 12.5, fontWeight: 700, marginTop: 2, lineHeight: 1.2 }}>{ev.title}</div>
                          {ev.subtitle && <div style={{ fontSize: 10.5, color: 'var(--ink-3)', marginTop: 1, lineHeight: 1.2 }}>{ev.subtitle}</div>}
                        </div>
                        {(ev.xp > 0 || ev.pearls > 0) && (
                          <div style={{ textAlign: 'right' }}>
                            {ev.xp > 0 && <div className="dd-num" style={{ fontSize: 11, color: ev.legendary ? '#D7B46A' : '#82ECFF', fontWeight: 700 }}>+{ev.xp.toLocaleString()}</div>}
                            {ev.pearls > 0 && <div className="dd-num" style={{ fontSize: 9.5, color: '#EAF5FF', fontWeight: 700, opacity: 0.7 }}>+{ev.pearls}P</div>}
                          </div>
                        )}
                      </div>
                    );
                  })}
                </div>
              </div>
            ))}
          </div>
        )}

        {latestDiveId && (
          <button onClick={() => nav('story', latestDiveId)} className="dd-btn dd-btn-primary" style={{ marginTop: 12, width: '100%', height: 50, borderRadius: 14, fontSize: 14 }}>
            ✦  Generate Story Card from latest dive
          </button>
        )}
      </div>
    </div>
  );
}

function StoryCardScreen({ nav, id }) {
  const { state } = useDiveDex();
  const dive = id ? selectDiveById(state, id) : selectLatestDive(state);
  const { creature: heroCreature, sighting: heroSighting } = selectDiveHero(state, dive);

  // Only play the reveal cue when there's actually a legendary/mythic to reveal.
  React.useEffect(() => {
    if (!window.DiveDexAudio) return;
    if (heroCreature && (heroCreature.rarity === 'legendary' || heroCreature.rarity === 'mythic')) {
      window.DiveDexAudio.playSfx('creature.hammerheadReveal');
      return () => window.DiveDexAudio.stopAudio('creature.hammerheadReveal', 600);
    }
  }, [heroCreature && heroCreature.id]);

  if (!dive) {
    return (
      <div className="dd-screen" style={{ paddingBottom: 110 }}>
        <TopBar title="Story Card" left={<IconBtn onClick={() => nav('memories')}>‹</IconBtn>}/>
        <div style={{ padding: '20px 18px', color: 'var(--ink-3)', fontSize: 13 }}>
          No dives to render yet. Log a dive first to generate a Story Card.
        </div>
      </div>
    );
  }
  return <StoryCardScreenInner nav={nav} dive={dive} heroCreature={heroCreature} heroSighting={heroSighting}/>;
}

function StoryCardScreenInner({ nav, dive, heroCreature, heroSighting }) {
  const { state } = useDiveDex();
  const [tpl, setTpl] = useState('dive');
  const [aspect, setAspect] = useState('9:16');
  const templates = [
    { id: 'dive', label: 'Dive Recap' },
    { id: 'species', label: 'Species' },
    { id: 'level', label: 'Level' },
    { id: 'quest', label: 'Side Quest' },
  ];

  const rarityColor = {
    common: '#B7CCDE', uncommon: '#63FFD2', rare: '#31D7FF',
    epic: '#8B7BFF', legendary: '#D7B46A', mythic: '#82ECFF',
  };
  const heroLabel = heroCreature
    ? (heroCreature.rarity === 'mythic' || heroCreature.rarity === 'legendary' ? 'LEGENDARY ENCOUNTER'
       : heroCreature.rarity === 'epic' ? 'EPIC SIGHTING' : 'BEST SIGHTING')
    : 'DIVE LOGGED';
  const heroName = heroCreature ? heroCreature.commonName : (dive.site || 'Dive');
  // Split a 2-word name across two lines if it fits naturally, otherwise leave inline.
  const heroNameParts = (() => {
    const parts = heroName.split(' ');
    if (parts.length === 2 && parts[0].length >= 4 && parts[1].length >= 4) return parts;
    return [heroName];
  })();
  const heroColor = heroCreature ? (rarityColor[heroCreature.rarity] || '#82ECFF') : '#82ECFF';
  // Prefer the explicit quote field; fall back to notes; final fallback is a generic line.
  const quote = (dive.quote && dive.quote.trim())
    || (dive.notes && dive.notes.trim())
    || 'Log after surfacing. Sharks can wait.';
  const spotter = heroSighting ? heroSighting.firstSpotter : null;
  const spotterUser = spotter ? state.users.find(u => u.id === spotter) : null;

  // Equipped frame cosmetic preview — if either user has a frame equipped, use
  // their rarity hue as the card's accent border so the cosmetic is visible.
  const equippedFrameId = (state.equippedItems.rick && state.equippedItems.rick.frame)
    || (state.equippedItems.siet && state.equippedItems.siet.frame) || null;
  const equippedFrame = equippedFrameId && (window.BOUTIQUE_ITEMS || []).find(i => i.id === equippedFrameId);
  const frameRarityColor = equippedFrame ? (rarityColor[equippedFrame.rarity] || '#82ECFF') : null;
  const cardBorderColor = frameRarityColor || 'rgba(130,236,255,0.18)';

  // Template content — what to put in the lower-middle of the card. The hero
  // imagery on top stays the same across templates so the card always reads as
  // "this dive"; the body swaps to emphasize what the template is about.
  const expedition = state.expeditions.find(e => e.id === state.activeExpeditionId) || state.expeditions[0];
  const region = expedition ? expedition.region : null;
  const regionalCreatures = region ? state.creatures.filter(c => (c.regions || []).indexOf(region) >= 0) : state.creatures;
  const speciesFound = regionalCreatures.filter(c => c.discovered).length;
  const speciesTotal = regionalCreatures.length;
  const latestQuest = state.sideQuests
    .filter(q => q.state === 'completed' && q.completedAt)
    .slice()
    .sort((a, b) => (a.completedAt < b.completedAt ? 1 : -1))[0];

  // Aspect → CSS aspectRatio + a label positioning tweak so the body sits
  // sensibly across portrait/square/feed shapes.
  const aspectMap = {
    '9:16': { ratio: '9 / 16', bodyBottom: 70, quoteBottom: 14 },
    '1:1':  { ratio: '1 / 1',  bodyBottom: 56, quoteBottom: 12 },
    '4:5':  { ratio: '4 / 5',  bodyBottom: 60, quoteBottom: 12 },
  };
  const A = aspectMap[aspect] || aspectMap['9:16'];

  // Body content per template
  let bodyLabel, bodyTitle, bodyStats;
  if (tpl === 'species' && heroCreature) {
    bodyLabel = `SPECIES · ${heroCreature.rarity.toUpperCase()}`;
    bodyTitle = heroNameParts;
    bodyStats = [
      { n: heroCreature.sightingsCount || (heroSighting ? heroSighting.count : 1), l: 'SIGHTINGS' },
      { n: heroCreature.maxSize || '—', l: 'MAX SIZE' },
      { n: '+' + (dive.totalXp || 0).toLocaleString(), l: 'XP', c: heroColor },
    ];
  } else if (tpl === 'level' && expedition) {
    bodyLabel = `LEVEL · ${expedition.emoji || ''} ${expedition.region.toUpperCase()}`;
    bodyTitle = [expedition.title];
    bodyStats = [
      { n: `${speciesFound}/${speciesTotal}`, l: 'SPECIES' },
      { n: state.dives.length, l: 'DIVES' },
      { n: '+' + (state.xp.teamXp || 0).toLocaleString(), l: 'TEAM XP', c: heroColor },
    ];
  } else if (tpl === 'quest' && latestQuest) {
    bodyLabel = `SIDE QUEST · ${(latestQuest.region || 'TRIP').toUpperCase()}`;
    bodyTitle = [latestQuest.title];
    bodyStats = [
      { n: latestQuest.badge || '◆', l: 'BADGE' },
      { n: '+' + (latestQuest.rewardXp || 0), l: 'XP', c: heroColor },
      { n: '+' + (latestQuest.rewardPearls || 0), l: 'PEARLS' },
    ];
  } else {
    // Default: 'dive' template, or fallback if a template can't render
    bodyLabel = heroLabel;
    bodyTitle = heroNameParts;
    bodyStats = [
      { n: dive.maxDepth, u: 'm', l: 'DEPTH' },
      { n: dive.time, u: 'min', l: 'TIME' },
      { n: '+' + (dive.totalXp || 0).toLocaleString(), l: 'XP', c: heroColor },
    ];
  }

  // Honest "share" affordance: copy a short text summary to clipboard.
  // (A real image-export would need html-to-canvas; out of scope for MVP.)
  const [shareBanner, setShareBanner] = useState(null);
  const onShare = async () => {
    const summary = [
      `${expedition.title} · Dive ${dive.no}`,
      `${dive.site || ''} · ${dive.maxDepth || '?'}m · ${dive.time || '?'}min`,
      heroCreature ? `${heroLabel}: ${heroCreature.commonName}` : null,
      `+${(dive.totalXp || 0).toLocaleString()} Team XP`,
      `"${quote}"`,
      `via DiveDex`,
    ].filter(Boolean).join('\n');
    try {
      if (navigator.share) {
        await navigator.share({ title: 'DiveDex story', text: summary });
        setShareBanner('Shared.');
      } else if (navigator.clipboard && navigator.clipboard.writeText) {
        await navigator.clipboard.writeText(summary);
        setShareBanner('Copied to clipboard.');
      } else {
        setShareBanner('Sharing not supported on this device.');
      }
    } catch (e) {
      setShareBanner('Share cancelled.');
    }
    setTimeout(() => setShareBanner(null), 2500);
  };

  return (
    <div className="dd-screen" style={{ paddingBottom: 110 }}>
      <TopBar
        title="Story Card"
        sub={`${dive.site || 'Dive'} · Dive ${dive.no}`}
        left={<IconBtn onClick={() => nav('memories')}>‹</IconBtn>}
        right={<IconBtn onClick={onShare}>⇪</IconBtn>}
      />
      <div style={{ padding: '0 18px' }}>

        {/* Story preview */}
        <div style={{ aspectRatio: A.ratio, borderRadius: 22, overflow: 'hidden', position: 'relative',
          background: `radial-gradient(120% 70% at 50% 0%, ${heroColor}66, rgba(11,42,68,0.7) 40%, #050B14 100%)`,
          border: `1px solid ${cardBorderColor}`,
          boxShadow: `0 20px 60px -20px ${heroColor}66`,
        }}>
          <div style={{ position: 'absolute', inset: 0, background: 'repeating-linear-gradient(135deg, rgba(255,255,255,0.04) 0 2px, transparent 2px 12px)' }}/>
          <div style={{ position: 'absolute', top: -40, left: -40, width: 220, height: 220, borderRadius: '50%', background: `radial-gradient(circle, ${heroColor}80, transparent 60%)` }}/>
          <div style={{ position: 'absolute', top: 14, left: 14, right: 14, display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
            <Logo size={18}/>
            <span className="dd-num" style={{ fontSize: 9, color: 'var(--ink-3)', letterSpacing: '0.08em' }}>{(expedition.region || '').toUpperCase()} · DIVE {dive.no}</span>
          </div>
          <div style={{ position: 'absolute', top: aspect === '1:1' ? '12%' : '14%', left: 0, right: 0, display: 'grid', placeItems: 'center' }}>
            <div style={{ position: 'relative' }}>
              <div className="dd-sonar-ring" style={{ width: 140, height: 140, position: 'absolute', inset: 'auto', top: -20, left: -20 }}/>
              {heroCreature && <CrSilhouette id={heroCreature.id} color={heroColor} size={aspect === '1:1' ? 100 : 120}/>}
            </div>
          </div>
          <div style={{ position: 'absolute', bottom: A.bodyBottom, left: 16, right: 16 }}>
            <div style={{ fontSize: 10, fontWeight: 700, color: heroColor, letterSpacing: '0.16em' }}>{bodyLabel}</div>
            <div className="dd-h1 dd-shimmer" style={{ fontSize: aspect === '1:1' ? 18 : 22, lineHeight: 1.05, marginTop: 2 }}>
              {bodyTitle.map((part, i) => <React.Fragment key={i}>{i > 0 && <br/>}{part}</React.Fragment>)}
            </div>
            <div style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: 6, marginTop: 10, borderTop: '1px solid rgba(130,236,255,0.12)', paddingTop: 10 }}>
              {bodyStats.map((s, i) => <SCStat key={i} n={s.n} u={s.u} l={s.l} c={s.c}/>)}
            </div>
          </div>
          <div style={{ position: 'absolute', bottom: A.quoteBottom, left: 16, right: 16, display: 'flex', justifyContent: 'space-between', alignItems: 'flex-end', gap: 8 }}>
            <span style={{ fontSize: 10, color: 'var(--ink-3)', fontStyle: 'italic', maxWidth: '70%', lineHeight: 1.3 }}>“{quote}”</span>
            {spotterUser && <Avatar name={spotterUser.displayName} color={spotterUser.color} size={22}/>}
          </div>
        </div>

        {/* Equipped frame note */}
        {equippedFrame && (
          <div style={{ marginTop: 10, fontSize: 10.5, color: 'var(--ink-3)', textAlign: 'center', letterSpacing: '0.08em' }}>
            FRAME · <span style={{ color: frameRarityColor, fontWeight: 700 }}>{equippedFrame.name}</span>
          </div>
        )}

        {/* Templates — Side Quest tile dims if no completed quests yet */}
        <div className="dd-h-section" style={{ marginTop: 18, marginBottom: 8 }}>Template</div>
        <div style={{ display: 'grid', gridTemplateColumns: 'repeat(4, 1fr)', gap: 6 }}>
          {templates.map(t => {
            const disabled = (t.id === 'quest' && !latestQuest) || (t.id === 'species' && !heroCreature);
            return (
              <button key={t.id} disabled={disabled} onClick={() => setTpl(t.id)} className="dd-btn" style={{
                padding: '10px 4px', borderRadius: 12, fontSize: 10.5, fontWeight: 600,
                background: tpl === t.id ? 'rgba(49,215,255,0.14)' : 'rgba(255,255,255,0.04)',
                color: disabled ? 'var(--ink-4)' : (tpl === t.id ? '#82ECFF' : 'var(--ink-2)'),
                border: tpl === t.id ? '1px solid rgba(49,215,255,0.32)' : '1px solid rgba(130,236,255,0.10)',
                opacity: disabled ? 0.45 : 1, cursor: disabled ? 'not-allowed' : 'pointer',
              }}>{t.label}</button>
            );
          })}
        </div>

        {/* Aspect — actually changes the preview shape now */}
        <div className="dd-h-section" style={{ marginTop: 14, marginBottom: 8 }}>Aspect</div>
        <SegSwitch value={aspect} onChange={setAspect} options={[{value:'9:16',label:'9:16 Story'},{value:'1:1',label:'1:1 Post'},{value:'4:5',label:'4:5 Feed'}]}/>

        <button onClick={onShare} className="dd-btn dd-btn-primary" style={{ marginTop: 16, width: '100%', height: 50, borderRadius: 14, fontSize: 14 }}>
          ⇪  Share summary
        </button>
        <div style={{ fontSize: 10.5, color: 'var(--ink-3)', textAlign: 'center', marginTop: 6, lineHeight: 1.4 }}>
          Copies a text summary to clipboard (or opens the native share sheet). Image export coming later.
        </div>
        {shareBanner && (
          <div style={{
            marginTop: 8, padding: '8px 12px', borderRadius: 10, fontSize: 11.5, fontWeight: 600,
            background: 'rgba(99,255,210,0.10)', border: '1px solid rgba(99,255,210,0.28)', color: '#9bffdf',
            textAlign: 'center',
          }}>{shareBanner}</div>
        )}
      </div>
    </div>
  );
}

function SCStat({ n, u, l, c = '#fff' }) {
  return (
    <div>
      <div style={{ display: 'flex', alignItems: 'baseline', gap: 2 }}>
        <span className="dd-num" style={{ fontSize: 16, fontWeight: 700, color: c }}>{n}</span>
        {u && <span style={{ fontSize: 9, color: 'var(--ink-3)' }}>{u}</span>}
      </div>
      <div style={{ fontSize: 8, color: 'var(--ink-3)', letterSpacing: '0.12em' }}>{l}</div>
    </div>
  );
}

// Account block on Settings — switch user, set/change/clear password, lock now.
function AccountSection({ state, actions, flash }) {
  const activeUser = state.users.find(u => u.id === state.activeUserId) || null;
  const hasPassword = !!state.authPassword;
  const [pwEditing, setPwEditing] = React.useState(false);
  const [pwInput, setPwInput] = React.useState('');
  const [pwConfirm, setPwConfirm] = React.useState('');

  const onSwitchUser = (uid) => {
    if (uid === state.activeUserId) return;
    actions.setActiveUser(uid);
    flash('ok', `Switched to ${uid === 'rick' ? 'Rick' : 'Siet'}.`);
  };
  const onLock = () => {
    actions.lockApp();
    // App re-renders to LockScreen automatically.
  };
  const onSavePassword = () => {
    if (pwInput !== pwConfirm) {
      flash('err', 'Passwords don\'t match.');
      return;
    }
    actions.updateAuthPassword(pwInput);
    setPwEditing(false);
    setPwInput(''); setPwConfirm('');
    flash('ok', pwInput ? 'Password updated.' : 'Password cleared.');
  };
  const onClearPassword = () => {
    if (!window.confirm('Clear the shared password? Anyone with this device will be able to unlock.')) return;
    actions.updateAuthPassword('');
    flash('ok', 'Password cleared.');
  };

  return (
    <>
      <div className="dd-h-section" style={{ marginTop: 18, marginBottom: 8 }}>Account</div>
      <GlassCard style={{ padding: 0, borderRadius: 16 }}>
        {/* Switch user */}
        <div style={{ padding: '12px 14px', borderBottom: '1px solid rgba(130,236,255,0.06)' }}>
          <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
            <span style={{ fontSize: 13, fontWeight: 600 }}>Active diver</span>
            <span style={{ fontSize: 11, color: activeUser ? activeUser.color : 'var(--ink-3)', fontWeight: 700 }}>
              {activeUser ? activeUser.displayName : '—'}
            </span>
          </div>
          <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 6, marginTop: 8 }}>
            {['rick','siet'].map(uid => {
              const u = state.users.find(x => x.id === uid);
              if (!u) return null;
              const isOn = state.activeUserId === uid;
              return (
                <button key={uid} onClick={() => onSwitchUser(uid)} className="dd-btn" style={{
                  height: 38, borderRadius: 10, fontSize: 12, fontWeight: 700,
                  background: isOn ? `${u.color}22` : 'rgba(255,255,255,0.04)',
                  color: isOn ? u.color : 'var(--ink-2)',
                  border: `1px solid ${isOn ? u.color + '55' : 'rgba(130,236,255,0.12)'}`,
                }}>{u.displayName}</button>
              );
            })}
          </div>
        </div>

        {/* Password */}
        <div style={{ padding: '12px 14px', borderBottom: '1px solid rgba(130,236,255,0.06)' }}>
          <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
            <div>
              <div style={{ fontSize: 13, fontWeight: 600 }}>Shared password</div>
              <div style={{ fontSize: 10.5, color: 'var(--ink-3)', marginTop: 2 }}>
                {hasPassword ? 'Set — required on unlock' : 'Not set — anyone can unlock'}
              </div>
            </div>
            {!pwEditing && (
              <button onClick={() => { setPwEditing(true); setPwInput(''); setPwConfirm(''); }} className="dd-btn" style={{
                padding: '4px 10px', borderRadius: 10, fontSize: 11, fontWeight: 700,
                background: 'rgba(130,236,255,0.10)', color: '#82ECFF',
                border: '1px solid rgba(130,236,255,0.25)',
              }}>{hasPassword ? 'Change' : 'Set'}</button>
            )}
          </div>
          {pwEditing && (
            <div style={{ marginTop: 10, display: 'flex', flexDirection: 'column', gap: 6 }}>
              <input type="password" value={pwInput} onChange={e => setPwInput(e.target.value)} placeholder="New password (leave empty to clear)" style={{
                width: '100%', padding: '8px 10px', borderRadius: 10, fontSize: 13,
                background: 'rgba(255,255,255,0.04)', border: '1px solid rgba(130,236,255,0.14)',
                color: '#EAF5FF', outline: 'none',
              }}/>
              <input type="password" value={pwConfirm} onChange={e => setPwConfirm(e.target.value)} placeholder="Confirm" style={{
                width: '100%', padding: '8px 10px', borderRadius: 10, fontSize: 13,
                background: 'rgba(255,255,255,0.04)', border: '1px solid rgba(130,236,255,0.14)',
                color: '#EAF5FF', outline: 'none',
              }}/>
              <div style={{ display: 'flex', gap: 6 }}>
                <button onClick={() => { setPwEditing(false); setPwInput(''); setPwConfirm(''); }} className="dd-btn dd-btn-ghost" style={{ flex: 1, height: 36, borderRadius: 10, fontSize: 12, fontWeight: 700 }}>Cancel</button>
                <button onClick={onSavePassword} className="dd-btn dd-btn-primary" style={{ flex: 1, height: 36, borderRadius: 10, fontSize: 12, fontWeight: 700 }}>Save</button>
              </div>
              {hasPassword && (
                <button onClick={onClearPassword} className="dd-btn" style={{
                  height: 32, marginTop: 2, borderRadius: 10, fontSize: 11, fontWeight: 700,
                  background: 'rgba(255,122,107,0.08)', border: '1px solid rgba(255,122,107,0.26)', color: '#FF7A6B',
                }}>Clear password</button>
              )}
            </div>
          )}
        </div>

        {/* Lock now */}
        <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', padding: '12px 14px' }}>
          <div>
            <div style={{ fontSize: 13, fontWeight: 600 }}>Lock app</div>
            <div style={{ fontSize: 10.5, color: 'var(--ink-3)', marginTop: 2, lineHeight: 1.4 }}>
              {hasPassword ? 'Returns to the password screen until unlocked.' : 'Returns to the diver picker until you continue.'}
            </div>
          </div>
          <button onClick={onLock} className="dd-btn" style={{
            padding: '6px 12px', borderRadius: 10, fontSize: 11.5, fontWeight: 700,
            background: 'rgba(255,255,255,0.04)', color: 'var(--ink)',
            border: '1px solid rgba(130,236,255,0.18)',
          }}>Lock now</button>
        </div>
      </GlassCard>
    </>
  );
}

function SettingsScreen({ nav }) {
  const [aPrefs, setAPrefs] = React.useState(() => (window.DiveDexAudio ? window.DiveDexAudio.getPrefs() : { sfxOn:true, musicOn:false, volume:'medium' }));
  React.useEffect(() => window.DiveDexAudio && window.DiveDexAudio.onPrefsChange(setAPrefs), []);
  // Audio prefs are mirrored into state.settings so they survive backup/
  // import. The audio manager is the live engine; state.settings is the
  // backup-safe source of truth.
  const setSfx = (v) => {
    window.DiveDexAudio && window.DiveDexAudio.setSoundEnabled(v);
    actions.updateSettings({ soundEnabled: v });
  };
  const setMus = (v) => {
    window.DiveDexAudio && window.DiveDexAudio.setMusicEnabled(v);
    actions.updateSettings({ musicEnabled: v });
  };
  const setVol = (v) => {
    window.DiveDexAudio && window.DiveDexAudio.setMasterVolume(v);
    actions.updateSettings({ volume: v });
  };

  // Backup & Data — context-driven
  const { state, actions } = useDiveDex();
  const fileInputRef = React.useRef(null);
  const [banner, setBanner] = React.useState(null); // { kind, msg }
  const flash = (kind, msg) => {
    setBanner({ kind, msg });
    setTimeout(() => setBanner(b => (b && b.msg === msg ? null : b)), 4200);
  };
  const onExport = () => {
    actions.exportBackup();
    flash('ok', 'Backup downloaded. Keep it somewhere safe.');
  };
  const onPickImport = () => fileInputRef.current && fileInputRef.current.click();
  const onImport = async (e) => {
    const file = e.target.files && e.target.files[0];
    e.target.value = '';
    if (!file) return;
    const ok = window.confirm('Import this backup? Your current DiveDex progress on this device will be replaced.');
    if (!ok) return;
    try {
      await actions.importBackupFromFile(file);
      flash('ok', 'Backup imported. Progress restored.');
    } catch (err) {
      flash('err', err.message || 'Could not import backup.');
    }
  };
  const onReset = () => {
    const ok = window.confirm('This will delete local DiveDex progress on this device. Continue?');
    if (!ok) return;
    actions.resetDemoData();
    flash('ok', 'Demo data reset.');
  };
  const lastBackupLabel = window.DD_BACKUP ? window.DD_BACKUP.formatLastBackup(state.lastBackupAt) : 'unknown';
  const backupStale = window.DD_BACKUP ? window.DD_BACKUP.isBackupStale(state.lastBackupAt) : true;

  return (
    <div className="dd-screen" style={{ paddingBottom: 110 }}>
      <TopBar title="Settings" left={<IconBtn onClick={() => nav('dashboard')}>‹</IconBtn>}/>
      <div style={{ padding: '0 18px' }}>
        <div className="dd-h-section" style={{ marginBottom: 8 }}>Profiles</div>
        <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 10 }}>
          {['rick','siet']
            .map(uid => buildPlayerView(state, uid))
            .filter(Boolean)
            .sort((a, b) => b.xp - a.xp)
            .map((pv, i) => <PlayerCard key={pv.id} person={pv} rank={i + 1}/>)}
        </div>

        {/* Account — active user, switch user, password, lock now */}
        <AccountSection state={state} actions={actions} flash={flash}/>

        <div className="dd-h-section" style={{ marginTop: 18, marginBottom: 8 }}>Insights</div>
        <GlassCard onClick={() => nav('trends')} style={{ padding: '14px 16px', borderRadius: 16, cursor: 'pointer', display: 'flex', alignItems: 'center', gap: 12 }}>
          <div style={{ width: 36, height: 36, borderRadius: 12, background: 'linear-gradient(160deg, rgba(99,255,210,0.22), rgba(11,42,68,0.4))', border: '1px solid rgba(99,255,210,0.32)', display: 'grid', placeItems: 'center', color: '#63FFD2', fontSize: 18, fontWeight: 700 }}>▤</div>
          <div style={{ flex: 1 }}>
            <div style={{ fontSize: 13.5, fontWeight: 700 }}>Trends</div>
            <div style={{ fontSize: 11, color: 'var(--ink-3)', marginTop: 2 }}>Depth profile · gas mix · species/dive · first-spotter race</div>
          </div>
          <span style={{ color: 'var(--ink-3)' }}>›</span>
        </GlassCard>

        <div className="dd-h-section" style={{ marginTop: 18, marginBottom: 8 }}>Audio</div>
        <GlassCard style={{ padding: 0, borderRadius: 16 }}>
          <div style={{ display:'flex', justifyContent:'space-between', alignItems:'center', padding:'12px 14px', borderBottom:'1px solid rgba(130,236,255,0.06)' }}>
            <span style={{ fontSize:13, fontWeight:600 }}>Sound effects</span>
            <button onClick={() => setSfx(!aPrefs.sfxOn)} className="dd-btn" style={{ padding:'4px 10px', borderRadius:10, background: aPrefs.sfxOn?'rgba(99,255,210,0.18)':'rgba(255,255,255,0.04)', color: aPrefs.sfxOn?'#63FFD2':'var(--ink-3)', fontSize:11, fontWeight:700, border:'1px solid '+(aPrefs.sfxOn?'rgba(99,255,210,0.36)':'rgba(130,236,255,0.12)') }}>{aPrefs.sfxOn?'ON':'OFF'}</button>
          </div>
          <div style={{ display:'flex', justifyContent:'space-between', alignItems:'center', padding:'12px 14px', borderBottom:'1px solid rgba(130,236,255,0.06)' }}>
            <span style={{ fontSize:13, fontWeight:600 }}>Expedition music</span>
            <button onClick={() => setMus(!aPrefs.musicOn)} className="dd-btn" style={{ padding:'4px 10px', borderRadius:10, background: aPrefs.musicOn?'rgba(49,215,255,0.18)':'rgba(255,255,255,0.04)', color: aPrefs.musicOn?'#82ECFF':'var(--ink-3)', fontSize:11, fontWeight:700, border:'1px solid '+(aPrefs.musicOn?'rgba(49,215,255,0.36)':'rgba(130,236,255,0.12)') }}>{aPrefs.musicOn?'ON':'OFF'}</button>
          </div>
          <div style={{ display:'flex', justifyContent:'space-between', alignItems:'center', padding:'12px 14px' }}>
            <span style={{ fontSize:13, fontWeight:600 }}>Volume</span>
            <div style={{ display:'flex', gap:4 }}>
              {['low','medium','high'].map(v => (
                <button key={v} onClick={()=>setVol(v)} className="dd-btn" style={{ padding:'4px 8px', borderRadius:8, fontSize:10, fontWeight:700, textTransform:'uppercase', background: aPrefs.volume===v?'rgba(130,236,255,0.18)':'rgba(255,255,255,0.04)', color: aPrefs.volume===v?'#82ECFF':'var(--ink-3)', border:'1px solid '+(aPrefs.volume===v?'rgba(130,236,255,0.36)':'rgba(130,236,255,0.10)') }}>{v}</button>
              ))}
            </div>
          </div>
        </GlassCard>

        {/* Audio test panel — preview each cue without triggering full flows */}
        <div className="dd-h-section" style={{ marginTop: 18, marginBottom: 8 }}>Audio test</div>
        <GlassCard style={{ padding: 12, borderRadius: 16 }}>
          <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 6 }}>
            {[
              { id: 'achievement.unlock',        label: 'Test Unlock',          kind: 'sfx' },
              { id: 'airZen.bonus',              label: 'Test Air Zen',         kind: 'sfx' },
              { id: 'creature.hammerheadReveal', label: 'Test Hammerhead',      kind: 'sfx' },
              { id: 'achievement.sharknado',     label: 'Test Sharknado',       kind: 'sfx' },
              { id: 'music.japanExpedition',     label: 'Test Japan Theme',     kind: 'music' },
            ].map(t => (
              <button key={t.id} onClick={() => {
                if (!window.DiveDexAudio) return;
                if (t.kind === 'music') window.DiveDexAudio.playMusic(t.id);
                else window.DiveDexAudio.playSfx(t.id);
              }} className="dd-btn dd-btn-ghost" style={{ height: 38, borderRadius: 10, fontSize: 11.5, fontWeight: 700 }}>
                {t.label}
              </button>
            ))}
            <button onClick={() => window.DiveDexAudio && window.DiveDexAudio.stopAll()} className="dd-btn" style={{
              height: 38, borderRadius: 10, fontSize: 11.5, fontWeight: 700,
              background: 'rgba(255,122,107,0.08)', border: '1px solid rgba(255,122,107,0.28)', color: '#FF7A6B',
            }}>Stop All Audio</button>
          </div>
          <div style={{ fontSize: 10.5, color: 'var(--ink-3)', marginTop: 8, lineHeight: 1.4 }}>
            Music respects the "Expedition music" toggle above. Test Japan Theme is a no-op when music is off.
          </div>
        </GlassCard>

        <div className="dd-h-section" style={{ marginTop: 18, marginBottom: 8 }}>Units</div>
        <GlassCard style={{ padding: 0, borderRadius: 16 }}>
          <SettingRow label="Depth" value="Meters"/>
          <SettingRow label="Pressure" value="Bar"/>
          <SettingRow label="Temperature" value="°C"/>
          <SettingRow label="Safe reserve" value="50 bar" mono last/>
        </GlassCard>

        <div className="dd-h-section" style={{ marginTop: 18, marginBottom: 8 }}>Gas presets</div>
        <GlassCard style={{ padding: 0, borderRadius: 16 }}>
          <SettingRow label="Air (21%)" value="default" check/>
          <SettingRow label="Nitrox 32" value="active" check/>
          <SettingRow label="Nitrox 36" value="off" last/>
        </GlassCard>

        <div className="dd-h-section" style={{ marginTop: 18, marginBottom: 8 }}>Reminders</div>
        <GlassCard style={{ padding: 0, borderRadius: 16 }}>
          <SettingRow label="No-fly window" value="24h after last dive" mono/>
          <SettingRow label="Post-dive log nudge" value="On"/>
          <SettingRow label="Suunto auto-import" value="Off" last/>
        </GlassCard>

        <div className="dd-h-section" style={{ marginTop: 18, marginBottom: 8 }}>Backup &amp; Data</div>
        <GlassCard style={{ padding: 14, borderRadius: 16 }}>
          <div style={{ fontSize: 11.5, color: 'var(--ink-2)', lineHeight: 1.5 }}>
            DiveDex currently saves progress on this device. Clearing browser data can remove local progress. Export a backup to keep it safe.
          </div>
          <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline', marginTop: 10, fontSize: 11 }}>
            <span style={{ color: 'var(--ink-3)' }}>Last backup</span>
            <span className="dd-num" style={{ color: backupStale ? '#FF7A6B' : '#63FFD2', fontWeight: 700 }}>{lastBackupLabel}</span>
          </div>
        </GlassCard>

        <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 8, marginTop: 10 }}>
          <button onClick={onExport} className="dd-btn dd-btn-primary" style={{ height: 44, borderRadius: 14, fontSize: 12.5, fontWeight: 700 }}>Export Backup</button>
          <button onClick={onPickImport} className="dd-btn dd-btn-ghost" style={{ height: 44, borderRadius: 14, fontSize: 12.5, fontWeight: 700 }}>Import Backup</button>
        </div>
        <input ref={fileInputRef} type="file" accept="application/json,.json" onChange={onImport} style={{ display: 'none' }}/>

        <button onClick={onReset} className="dd-btn" style={{
          width: '100%', height: 40, marginTop: 8, borderRadius: 12,
          background: 'rgba(255,122,107,0.08)', border: '1px solid rgba(255,122,107,0.28)',
          color: '#FF7A6B', fontSize: 12, fontWeight: 700,
        }}>Reset Demo Data</button>

        {banner && (
          <div style={{
            marginTop: 10, padding: '10px 12px', borderRadius: 12, fontSize: 12, fontWeight: 600,
            background: banner.kind === 'ok' ? 'rgba(99,255,210,0.10)' : 'rgba(255,122,107,0.10)',
            border: '1px solid ' + (banner.kind === 'ok' ? 'rgba(99,255,210,0.32)' : 'rgba(255,122,107,0.32)'),
            color: banner.kind === 'ok' ? '#9bffdf' : '#FF7A6B',
          }}>{banner.msg}</div>
        )}

        <div className="dd-glass" style={{ marginTop: 18, padding: 14, borderRadius: 14, borderColor: 'rgba(255,122,107,0.18)' }}>
          <div style={{ display: 'flex', alignItems: 'center', gap: 10, marginBottom: 8 }}>
            <div style={{ width: 24, height: 24, borderRadius: 8, background: 'rgba(255,122,107,0.15)', display: 'grid', placeItems: 'center', color: '#FF7A6B', fontWeight: 700 }}>!</div>
            <div style={{ fontSize: 12, fontWeight: 700 }}>Safety disclaimer</div>
          </div>
          <div style={{ fontSize: 11, color: 'var(--ink-2)', lineHeight: 1.5 }}>
            DiveDex is a post-dive logbook and memory app. It does not replace dive training, professional guidance, dive planning or a dive computer.
          </div>
        </div>
        <div style={{ textAlign: 'center', padding: '20px 0 4px', fontSize: 10.5, color: 'var(--ink-4)' }}>DiveDex v0.1 · MVP</div>
      </div>
    </div>
  );
}

function SettingRow({ label, value, last, check, mono, coral }) {
  return (
    <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', padding: '12px 14px', borderBottom: last ? 'none' : '1px solid rgba(130,236,255,0.06)' }}>
      <span style={{ fontSize: 13, fontWeight: 600 }}>{label}</span>
      <span className={mono ? 'dd-num' : ''} style={{ fontSize: 12, color: coral ? '#FF7A6B' : check && value === 'active' ? '#63FFD2' : 'var(--ink-3)', fontWeight: 600 }}>{value} {check && value === 'active' ? '✓' : ''} ›</span>
    </div>
  );
}

// TrendsScreen — derived analytics over the full dive log. Pure read; no
// state mutations. Charts are hand-rolled SVG so we don't pull in a charting
// library (everything in DiveDex stays vendored / no-build).
function TrendsScreen({ nav }) {
  const { state } = useDiveDex();
  const dives = state.dives;
  const empty = dives.length === 0;
  const rick = state.users.find(u => u.id === 'rick');
  const siet = state.users.find(u => u.id === 'siet');

  // ── Totals (header strip) ─────────────────────────────────────────────
  const totalDives = dives.length;
  const totalBottomTime = dives.reduce((s, d) => s + (+d.time || 0), 0);
  const deepest = dives.reduce((m, d) => Math.max(m, +d.maxDepth || 0), 0);
  const longest = dives.reduce((m, d) => Math.max(m, +d.time || 0), 0);
  const avgDepth = totalDives ? Math.round(dives.reduce((s, d) => s + (+d.maxDepth || 0), 0) / totalDives) : 0;
  const avgTime = totalDives ? Math.round(totalBottomTime / totalDives) : 0;

  // ── Gas mix breakdown ─────────────────────────────────────────────────
  const gasCounts = dives.reduce((acc, d) => {
    const nitrox = +d.nitrox || (d.gas === 'Nitrox' ? 32 : 21);
    const k = nitrox > 21 ? `Nitrox ${nitrox}` : 'Air';
    acc[k] = (acc[k] || 0) + 1;
    return acc;
  }, {});
  const gasRows = Object.entries(gasCounts)
    .map(([label, count]) => ({ label, count, pct: totalDives ? count / totalDives : 0 }))
    .sort((a, b) => b.count - a.count);
  const gasColors = { 'Air': '#82ECFF', 'Nitrox 32': '#63FFD2', 'Nitrox 36': '#D7B46A' };

  // ── Per-dive depth bar chart ──────────────────────────────────────────
  const depthMax = Math.max(deepest, 30);
  const depthBars = dives.slice(-12).map((d, i) => ({
    label: d.no || (i + 1),
    depth: +d.maxDepth || 0,
    time: +d.time || 0,
  }));

  // ── Species per dive ──────────────────────────────────────────────────
  const sightingsByDive = state.sightings.reduce((acc, s) => {
    acc[s.diveId] = (acc[s.diveId] || 0) + 1;
    return acc;
  }, {});
  const speciesPerDive = dives.map(d => ({ no: d.no, c: sightingsByDive[d.id] || 0 }));
  const maxSpecies = Math.max(1, ...speciesPerDive.map(x => x.c));

  // ── First-spotter race ────────────────────────────────────────────────
  const firstSpotter = { rick: 0, siet: 0 };
  state.creatures.forEach(c => {
    if (c.discovered && c.firstSeenBy && firstSpotter[c.firstSeenBy] !== undefined) {
      firstSpotter[c.firstSeenBy]++;
    }
  });
  const totalFirsts = firstSpotter.rick + firstSpotter.siet;

  return (
    <div className="dd-screen" style={{ paddingBottom: 110 }}>
      <TopBar title="Trends" sub="Derived from your log" left={<IconBtn onClick={() => nav('settings')}>‹</IconBtn>}/>
      <div style={{ padding: '4px 18px' }}>
        {empty && (
          <GlassCard style={{ padding: 16, borderRadius: 14, fontSize: 12.5, color: 'var(--ink-3)', textAlign: 'center', lineHeight: 1.5 }}>
            Trends light up once you've logged a few dives.
          </GlassCard>
        )}

        {!empty && <>
          {/* Top stat strip */}
          <GlassCard style={{ padding: 14, borderRadius: 18 }}>
            <div style={{ display: 'grid', gridTemplateColumns: 'repeat(4, 1fr)', gap: 10 }}>
              <StatCell label="Dives" value={totalDives}/>
              <StatCell label="Bottom time" value={totalBottomTime} unit="min"/>
              <StatCell label="Deepest" value={deepest} unit="m" color="#82ECFF"/>
              <StatCell label="Longest" value={longest} unit="min" color="#63FFD2"/>
            </div>
            <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 10, marginTop: 12 }}>
              <StatCell label="Avg depth" value={avgDepth} unit="m"/>
              <StatCell label="Avg dive time" value={avgTime} unit="min"/>
            </div>
          </GlassCard>

          {/* Per-dive depth bars */}
          <div className="dd-h-section" style={{ marginTop: 18, marginBottom: 8 }}>Depth profile · last {depthBars.length}</div>
          <GlassCard style={{ padding: 14, borderRadius: 18 }}>
            <div style={{ display: 'flex', alignItems: 'stretch', gap: 6, height: 140 }}>
              {depthBars.map(b => (
                <div key={b.label} style={{ flex: 1, display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 4, minWidth: 0 }}>
                  <div style={{ width: '100%', flex: 1, position: 'relative', display: 'flex', alignItems: 'flex-end' }}>
                    <div title={`${b.depth}m · ${b.time}min`} style={{
                      width: '100%',
                      height: `${Math.max(4, Math.min(100, (b.depth / depthMax) * 100))}%`,
                      borderRadius: '6px 6px 2px 2px',
                      background: 'linear-gradient(180deg, rgba(49,215,255,0.85), rgba(49,215,255,0.35))',
                      border: '1px solid rgba(49,215,255,0.45)',
                      boxShadow: 'inset 0 -6px 12px rgba(49,215,255,0.18)',
                    }}/>
                    <div className="dd-num" style={{
                      position: 'absolute', left: 0, right: 0, top: 0, textAlign: 'center',
                      fontSize: 9.5, color: '#82ECFF', fontWeight: 700, pointerEvents: 'none',
                    }}>{b.depth}</div>
                  </div>
                  <div className="dd-num" style={{ fontSize: 9.5, color: 'var(--ink-3)' }}>#{b.label}</div>
                </div>
              ))}
            </div>
            <div style={{ marginTop: 8, fontSize: 10.5, color: 'var(--ink-3)' }}>
              Bar height = max depth · Y axis caps at {depthMax}m.
            </div>
          </GlassCard>

          {/* Gas mix breakdown */}
          <div className="dd-h-section" style={{ marginTop: 18, marginBottom: 8 }}>Gas mix breakdown</div>
          <GlassCard style={{ padding: 14, borderRadius: 18 }}>
            <div style={{ display: 'flex', borderRadius: 8, overflow: 'hidden', height: 14, marginBottom: 12 }}>
              {gasRows.map(r => (
                <div key={r.label} style={{
                  width: (r.pct * 100) + '%',
                  background: gasColors[r.label] || '#82ECFF',
                  borderRight: '1px solid rgba(5,11,20,0.5)',
                }}/>
              ))}
            </div>
            <div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
              {gasRows.map(r => (
                <div key={r.label} style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', fontSize: 12.5 }}>
                  <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
                    <div style={{ width: 10, height: 10, borderRadius: 3, background: gasColors[r.label] || '#82ECFF' }}/>
                    <span style={{ color: 'var(--ink-2)' }}>{r.label}</span>
                  </div>
                  <span className="dd-num" style={{ color: '#fff', fontWeight: 700 }}>{r.count} · {Math.round(r.pct * 100)}%</span>
                </div>
              ))}
            </div>
          </GlassCard>

          {/* Species per dive trend */}
          <div className="dd-h-section" style={{ marginTop: 18, marginBottom: 8 }}>Species per dive</div>
          <GlassCard style={{ padding: 14, borderRadius: 18 }}>
            <svg viewBox={`0 0 ${Math.max(speciesPerDive.length * 24, 240)} 80`} preserveAspectRatio="none" style={{ width: '100%', height: 80, display: 'block' }}>
              {speciesPerDive.length > 1 && (
                <polyline
                  fill="none"
                  stroke="#63FFD2"
                  strokeWidth="2"
                  strokeLinecap="round"
                  strokeLinejoin="round"
                  points={speciesPerDive.map((p, i) =>
                    `${i * 24 + 12},${80 - (p.c / maxSpecies) * 70 - 4}`
                  ).join(' ')}
                />
              )}
              {speciesPerDive.map((p, i) => (
                <g key={p.no || i}>
                  <circle cx={i * 24 + 12} cy={80 - (p.c / maxSpecies) * 70 - 4} r="3.5" fill="#63FFD2"/>
                  <text x={i * 24 + 12} y={78} fontSize="8" textAnchor="middle" fill="rgba(234,245,255,0.5)" fontFamily="JetBrains Mono">{p.no || i + 1}</text>
                </g>
              ))}
            </svg>
            <div style={{ display: 'flex', justifyContent: 'space-between', marginTop: 6, fontSize: 10.5, color: 'var(--ink-3)' }}>
              <span>Max on one dive: <span style={{ color: '#63FFD2', fontWeight: 700 }}>{maxSpecies}</span></span>
              <span>Avg/dive: <span style={{ color: '#fff', fontWeight: 700 }}>{totalDives ? (speciesPerDive.reduce((s, x) => s + x.c, 0) / totalDives).toFixed(1) : '0'}</span></span>
            </div>
          </GlassCard>

          {/* First-spotter race */}
          <div className="dd-h-section" style={{ marginTop: 18, marginBottom: 8 }}>First-spotter race</div>
          <GlassCard style={{ padding: 14, borderRadius: 18 }}>
            {totalFirsts === 0 ? (
              <div style={{ fontSize: 12.5, color: 'var(--ink-3)' }}>
                No first-spotter attributions yet. Mark "first to spot" on a sighting to start the race.
              </div>
            ) : <>
              <div style={{ display: 'flex', borderRadius: 8, overflow: 'hidden', height: 18 }}>
                <div style={{
                  width: ((firstSpotter.rick / totalFirsts) * 100) + '%',
                  background: rick.color, color: '#fff', fontSize: 10.5, fontWeight: 700,
                  display: 'grid', placeItems: 'center',
                }}>{firstSpotter.rick > 0 ? firstSpotter.rick : ''}</div>
                <div style={{
                  width: ((firstSpotter.siet / totalFirsts) * 100) + '%',
                  background: siet.color, color: '#fff', fontSize: 10.5, fontWeight: 700,
                  display: 'grid', placeItems: 'center',
                }}>{firstSpotter.siet > 0 ? firstSpotter.siet : ''}</div>
              </div>
              <div style={{ display: 'flex', justifyContent: 'space-between', marginTop: 10, fontSize: 12.5 }}>
                <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
                  <Avatar name={rick.displayName} color={rick.color} size={24}/>
                  <div>
                    <div style={{ fontWeight: 700, color: '#fff' }}>{rick.displayName}</div>
                    <div className="dd-num" style={{ fontSize: 10.5, color: rick.color, fontWeight: 700 }}>{firstSpotter.rick} firsts</div>
                  </div>
                </div>
                <div style={{ display: 'flex', alignItems: 'center', gap: 8, textAlign: 'right' }}>
                  <div>
                    <div style={{ fontWeight: 700, color: '#fff' }}>{siet.displayName}</div>
                    <div className="dd-num" style={{ fontSize: 10.5, color: siet.color, fontWeight: 700 }}>{firstSpotter.siet} firsts</div>
                  </div>
                  <Avatar name={siet.displayName} color={siet.color} size={24}/>
                </div>
              </div>
            </>}
          </GlassCard>

          <div style={{ height: 14 }}/>
        </>}
      </div>
    </div>
  );
}

Object.assign(window, {
  RecapScreen, DexScreen, CreatureCard, CreatureDetailScreen,
  TeamScreen, AchievementsScreen, SideQuestsScreen,
  MemoriesScreen, StoryCardScreen, SettingsScreen, TrendsScreen,
});
