// screens2.jsx — Roadmap, Community, Profile + Habit Tracker

const { useState: useState2, useEffect: useEffect2 } = React;
const { HomeScreen: _HS } = window.LAUNCH_SCREENS_1; // ensure load order
const { Card, Chip, CoverArt, Waveform, TopBar, IconBtn, DisplayHeading, Icon, Avatar } = window.LAUNCH_SCREENS_1;

// ─────────────────────────────────────────────────────────────
// HABIT TRACKER — songwriting log
// 7 days × 5 weeks heatmap, with today highlighted, tap-to-log
// ─────────────────────────────────────────────────────────────
// The habit log maps a REAL date → 1 (wrote a song that day). Keyed by date,
// not position, so marks stick to their actual day and the 5-week window slides
// forward on its own. Persists in localStorage; today stays driven by the app's
// `logged` state so the "LOG TODAY" button is the source of truth for today.
const HABIT_KEY = 'launch.habit';
function habitDateKey(d) {
  return d.getFullYear() + '-' + String(d.getMonth() + 1).padStart(2, '0') + '-' + String(d.getDate()).padStart(2, '0');
}
function habitStartOfDay(d) { return new Date(d.getFullYear(), d.getMonth(), d.getDate()); }
function habitStartOfWeekMon(d) {
  const x = habitStartOfDay(d);
  const dow = (x.getDay() + 6) % 7; // Mon=0 … Sun=6
  x.setDate(x.getDate() - dow);
  return x;
}
const HABIT_MONTHS_FULL = ['JANUARY', 'FEBRUARY', 'MARCH', 'APRIL', 'MAY', 'JUNE', 'JULY', 'AUGUST', 'SEPTEMBER', 'OCTOBER', 'NOVEMBER', 'DECEMBER'];
// Build any month as a Monday-first calendar: a few leading blanks so day 1
// lands under the right weekday, then 1 → end of month.
function habitMonth(year, month) {
  const offset = (new Date(year, month, 1).getDay() + 6) % 7; // blanks before the 1st (Mon-first)
  const daysInMonth = new Date(year, month + 1, 0).getDate();
  const cells = [];
  for (let i = 0; i < offset; i++) cells.push(null);
  for (let day = 1; day <= daysInMonth; day++) cells.push(new Date(year, month, day));
  return {
    cells,
    monthLabel: HABIT_MONTHS_FULL[month] + ' ' + year,
    monthAbbr: HABIT_MONTHS[month],
  };
}
function loadHabit() {
  try {
    const r = JSON.parse(localStorage.getItem(HABIT_KEY) || 'null');
    if (r && typeof r === 'object' && !Array.isArray(r)) return r;
  } catch (e) {}
  return {};
}
function saveHabit(map) {
  try { localStorage.setItem(HABIT_KEY, JSON.stringify(map)); } catch (e) {}
  try { window.dispatchEvent(new Event('launch.habitchange')); } catch (e) {}
}
const HABIT_MONTHS = ['JAN', 'FEB', 'MAR', 'APR', 'MAY', 'JUN', 'JUL', 'AUG', 'SEP', 'OCT', 'NOV', 'DEC'];
function habitShort(d) { return HABIT_MONTHS[d.getMonth()] + ' ' + d.getDate(); }

function HabitTracker({ theme, compact = false, logged, onToggleToday }) {
  // Real "today" is fixed; `view` is the month being looked at (navigable).
  const [todayInfo] = useState2(() => {
    const t = habitStartOfDay(new Date());
    return { today: t, todayKey: habitDateKey(t), y: t.getFullYear(), m: t.getMonth() };
  });
  const { today, todayKey } = todayInfo;
  const [view, setView] = useState2({ y: todayInfo.y, m: todayInfo.m });
  const isCurrentMonth = view.y === todayInfo.y && view.m === todayInfo.m;
  const { cells, monthLabel, monthAbbr } = habitMonth(view.y, view.m);
  const goPrev = () => setView(v => { const d = new Date(v.y, v.m - 1, 1); return { y: d.getFullYear(), m: d.getMonth() }; });
  const goNext = () => { if (isCurrentMonth) return; setView(v => { const d = new Date(v.y, v.m + 1, 1); return { y: d.getFullYear(), m: d.getMonth() }; }); };
  const navBtn = {
    width: 18, height: 18, borderRadius: 6, padding: 0,
    background: theme.surface2, border: `1px solid ${theme.border}`,
    color: theme.text, fontFamily: theme.fontMono, fontSize: 12, fontWeight: 700,
    lineHeight: 1, display: 'flex', alignItems: 'center', justifyContent: 'center',
  };

  const [map, setMap] = useState2(loadHabit);
  const firstSync = React.useRef(true);
  const days = ['M', 'T', 'W', 'T', 'F', 'S', 'S'];
  const cell = compact ? 26 : 30;
  const gap = compact ? 4 : 5;

  const ping = () => { try { window.dispatchEvent(new Event('launch.habitchange')); } catch (e) {} };

  // Load the saved log (Supabase in live mode, localStorage in demo) on mount,
  // and reload when another HabitTracker instance changes it.
  useEffect2(() => {
    let alive = true;
    const load = () => window.LAUNCH_USER.fetchHabitLog().then(m => { if (alive) setMap(m || {}); });
    load();
    window.addEventListener('launch.habitchange', load);
    window.addEventListener('storage', load);
    return () => {
      alive = false;
      window.removeEventListener('launch.habitchange', load);
      window.removeEventListener('storage', load);
    };
  }, []);

  // Today's cell follows the app's LOG TODAY button. Skip the first run so we
  // don't overwrite the value we just loaded before the user touches anything.
  useEffect2(() => {
    if (firstSync.current) { firstSync.current = false; return; }
    const want = logged ? 1 : 0;
    setMap(m => ((m[todayKey] ? 1 : 0) === want ? m : { ...m, [todayKey]: want }));
    window.LAUNCH_USER.setHabitDay(todayKey, !!logged).then(res => {
      if (res && res.error) console.warn('[LAUNCH] habit not synced:', res.error);
      ping();
    });
  }, [logged]);

  const toggleDate = (key, future) => {
    if (future) return;                                          // day hasn't happened yet
    if (key === todayKey) { onToggleToday && onToggleToday(); return; } // today → app state (effect persists)
    const on = !map[key];
    setMap(m => ({ ...m, [key]: on ? 1 : 0 }));                  // optimistic
    window.LAUNCH_USER.setHabitDay(key, on).then(res => {
      if (res && res.error) console.warn('[LAUNCH] habit not synced:', res.error);
      ping();
    });
  };

  // streak: consecutive logged days counting back from today
  let streak = 0;
  for (let d = new Date(today); map[habitDateKey(d)]; d.setDate(d.getDate() - 1)) streak++;
  const total = cells.reduce((a, d) => a + (d && map[habitDateKey(d)] ? 1 : 0), 0);

  return (
    <Card theme={theme} style={{ padding: compact ? 14 : 16, overflow: 'hidden' }}>
      {/* header */}
      <div style={{
        display: 'flex', alignItems: 'flex-start', justifyContent: 'space-between',
        marginBottom: 14, gap: 10
      }}>
        <div>
          <div style={{
            fontFamily: theme.fontMono, fontSize: 10, color: theme.muted,
            letterSpacing: 1, fontWeight: 700
          }}>SONGWRITING HABIT</div>
          <div style={{ display: 'flex', alignItems: 'center', gap: 6, marginTop: 4 }}>
            <button onClick={goPrev} aria-label="Previous month" style={{ ...navBtn, cursor: 'pointer' }}>‹</button>
            <div style={{
              fontFamily: theme.fontMono, fontSize: 9, color: theme.faint,
              letterSpacing: 0.5, fontWeight: 700, minWidth: 96, textAlign: 'center'
            }}>{monthLabel}</div>
            <button onClick={goNext} disabled={isCurrentMonth} aria-label="Next month"
              style={{ ...navBtn, opacity: isCurrentMonth ? 0.3 : 1, cursor: isCurrentMonth ? 'default' : 'pointer' }}>›</button>
          </div>
          <div style={{
            fontFamily: theme.fontDisplay, fontSize: compact ? 20 : 24,
            color: theme.text, textTransform: 'uppercase', letterSpacing: 0.2,
            lineHeight: 1, marginTop: 6
          }}>
            {streak > 0 ?
            <>{streak} DAYS <span style={{ ...{ color: theme.accent }, color: "rgb(199, 33, 8)" }}>IN A ROW</span></> :
            <>LOG TODAY <span style={{ color: theme.accent }}>↓</span></>}
          </div>
        </div>
        <div style={{ textAlign: 'right' }}>
          <div style={{
            fontFamily: theme.fontDisplay, fontSize: compact ? 26 : 30,
            color: theme.accent, lineHeight: 1
          }}>{total}</div>
          <div style={{
            fontFamily: theme.fontMono, fontSize: 9, color: theme.muted,
            letterSpacing: 0.8, marginTop: 2, fontWeight: 600
          }}>SONGS · {monthAbbr}</div>
        </div>
      </div>

      {/* day labels */}
      <div style={{
        display: 'grid', gridTemplateColumns: `repeat(7, ${cell}px)`,
        gap, marginBottom: 6, justifyContent: 'space-between'
      }}>
        {days.map((d, i) =>
        <div key={i} style={{
          fontFamily: theme.fontMono, fontSize: 9, color: theme.faint,
          textAlign: 'center', fontWeight: 600, letterSpacing: 0.5
        }}>{d}</div>
        )}
      </div>

      {/* grid */}
      <div style={{
        display: 'grid', gridTemplateColumns: `repeat(7, ${cell}px)`, gap,
        justifyContent: 'space-between'
      }}>
        {cells.map((d, i) => {
          if (!d) return <div key={'blank-' + i} style={{ width: cell, height: cell }} />;
          const key = habitDateKey(d);
          const isToday = key === todayKey;
          const future = key > todayKey;
          const wrote = !!map[key];
          return (
            <button
              key={key}
              onClick={() => toggleDate(key, future)}
              disabled={future}
              title={d.toLocaleDateString(undefined, { weekday: 'short', month: 'short', day: 'numeric' })}
              style={{
                width: cell, height: cell, borderRadius: 7,
                border: isToday ?
                `2px solid ${theme.text}` :
                `1px solid ${wrote ? 'transparent' : theme.border}`,
                background: wrote ? theme.accent : 'transparent',
                opacity: future ? 0.35 : 1,
                cursor: future ? 'default' : 'pointer',
                padding: 0, position: 'relative',
                display: 'flex', alignItems: 'center', justifyContent: 'center'
              }}
              aria-label={key}>

              {wrote &&
              <svg width="11" height="11" viewBox="0 0 14 14" fill="none">
                  <path d="M2 7l3.5 3.5L12 4" stroke={theme.onAccent} strokeWidth="2.4" strokeLinecap="round" strokeLinejoin="round" />
                </svg>
              }
              {!wrote && isToday &&
              <span style={{
                fontFamily: theme.fontMono, fontSize: 9, fontWeight: 700,
                color: theme.text, letterSpacing: 0.5
              }}>+</span>
              }
              {!wrote && !isToday &&
              <span style={{
                fontFamily: theme.fontMono, fontSize: 8, fontWeight: 600,
                color: theme.faint
              }}>{d.getDate()}</span>
              }
            </button>);

        })}
      </div>

      {/* CTA strip */}
      <div style={{
        marginTop: 14, paddingTop: 12, borderTop: `1px solid ${theme.border}`,
        display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 10
      }}>
        <div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
          <div style={{ width: 10, height: 10, borderRadius: 3, background: theme.accent }} />
          <span style={{ fontFamily: theme.fontMono, fontSize: 10, color: theme.muted, letterSpacing: 0.5, fontWeight: 600 }}>
            WROTE A SONG
          </span>
        </div>
        <button onClick={onToggleToday} style={{
          background: logged ? 'transparent' : theme.text,
          color: logged ? theme.text : theme.bg,
          border: logged ? `1px solid ${theme.border}` : 'none',
          fontFamily: theme.fontMono, fontSize: 10, fontWeight: 700,
          letterSpacing: 1, padding: '8px 12px', borderRadius: 99, cursor: 'pointer',
          textTransform: 'uppercase'
        }}>
          {logged ? '✓ LOGGED TODAY' : '+ LOG TODAY'}
        </button>
      </div>
    </Card>);

}

// ─────────────────────────────────────────────────────────────
// 3. ROADMAP — career journey
// ─────────────────────────────────────────────────────────────
function RoadmapScreen({ theme, copy }) {
  const phases = [
  { n: '01', title: 'FIND YOUR SOUND', sub: 'Identity · references · niche', state: 'done', goals: 4, done: 4 },
  { n: '02', title: 'BUILD THE TOOLKIT', sub: 'DAW · mixing · songwriting reps', state: 'done', goals: 6, done: 6 },
  { n: '03', title: 'FIRST RELEASE', sub: 'Single ready for distribution', state: 'current', goals: 5, done: 3 },
  { n: '04', title: 'GROW AN AUDIENCE', sub: 'Content · socials · email list', state: 'next', goals: 5, done: 0 },
  { n: '05', title: 'FIRST LIVE SHOW', sub: 'Set · rehearsal · booking', state: 'locked', goals: 4, done: 0 },
  { n: '06', title: 'LAUNCH PROJECT', sub: 'EP · campaign · press', state: 'locked', goals: 7, done: 0 }];


  const goals = [
  { t: 'Finalize cover art', done: true },
  { t: 'Master the single', done: true },
  { t: 'Distribute via DistroKid', done: true },
  { t: 'Pitch to 5 playlists', done: false, due: 'FRI' },
  { t: 'Schedule release day post', done: false, due: 'NEXT WEEK' }];


  return (
    <div style={{ paddingBottom: 110 }}>
      <TopBar theme={theme}
      left={
      <div>
            <div style={{ fontFamily: theme.fontMono, fontSize: 10, color: theme.muted, letterSpacing: 1, fontWeight: 600 }}>
              {copy.roadmapKicker}
            </div>
            <DisplayHeading theme={theme} size={28} style={{ marginTop: 4 }}>
              {copy.roadmapTitle}
            </DisplayHeading>
          </div>
      }
      right={<IconBtn theme={theme}>{Icon.plus(theme.text)}</IconBtn>} />
      

      {/* Overall progress */}
      <div style={{ padding: `${theme.gap}px ${theme.pad}px 0` }}>
        <Card theme={theme} style={{ padding: 16 }}>
          <div style={{ display: 'flex', alignItems: 'baseline', justifyContent: 'space-between' }}>
            <div style={{ fontFamily: theme.fontMono, fontSize: 10, color: theme.muted, letterSpacing: 1, fontWeight: 700 }}>
              OVERALL PROGRESS
            </div>
            <div style={{ fontFamily: theme.fontDisplay, fontSize: 22, color: theme.accent, lineHeight: 1 }}>
              38<span style={{ color: theme.muted, fontSize: 14 }}>%</span>
            </div>
          </div>
          <div style={{
            marginTop: 12, height: 8, borderRadius: 99,
            background: theme.surface2, position: 'relative', overflow: 'hidden'
          }}>
            <div style={{
              width: '38%', height: '100%', background: theme.accent, borderRadius: 99
            }} />
          </div>
          <div style={{
            marginTop: 10, fontFamily: theme.fontBody, fontSize: 13, color: theme.muted
          }}>
            <span style={{ color: theme.text, fontWeight: 600 }}>13 of 31 goals</span> hit. Phase 03 is up next →
          </div>
        </Card>
      </div>

      {/* Phase ladder */}
      <div style={{ padding: `${theme.gap + 6}px ${theme.pad}px 0` }}>
        <div style={{
          fontFamily: theme.fontMono, fontSize: 11, color: theme.muted,
          letterSpacing: 1, fontWeight: 600, marginBottom: 10
        }}>PHASES</div>
        <div style={{ position: 'relative' }}>
          {/* spine */}
          <div style={{
            position: 'absolute', left: 19, top: 18, bottom: 18,
            width: 2, background: theme.border
          }} />
          {phases.map((p, i) => {
            const colors = {
              done: { dot: theme.accent, ring: 'transparent', txt: theme.text, sub: theme.muted },
              current: { dot: theme.text, ring: theme.accent, txt: theme.text, sub: theme.muted },
              next: { dot: theme.surface2, ring: theme.border, txt: theme.text, sub: theme.muted },
              locked: { dot: theme.surface2, ring: theme.border, txt: theme.faint, sub: theme.faint }
            }[p.state];
            return (
              <div key={i} style={{
                display: 'flex', alignItems: 'flex-start', gap: 14,
                paddingBottom: 14, position: 'relative'
              }}>
                <div style={{
                  width: 40, height: 40, borderRadius: 99,
                  background: colors.dot,
                  border: colors.ring !== 'transparent' ? `2px solid ${colors.ring}` : 'none',
                  display: 'flex', alignItems: 'center', justifyContent: 'center',
                  flexShrink: 0, zIndex: 1,
                  boxShadow: p.state === 'current' ? `0 0 0 6px ${theme.accent}20` : 'none'
                }}>
                  {p.state === 'done' ? Icon.check(theme.onAccent) :
                  p.state === 'locked' ? Icon.lock(theme.faint) :
                  <span style={{
                    fontFamily: theme.fontMono, fontSize: 11, fontWeight: 700,
                    color: p.state === 'current' ? theme.bg : theme.muted
                  }}>{p.n}</span>}
                </div>
                <Card theme={theme} style={{
                  flex: 1, padding: 14,
                  borderColor: p.state === 'current' ? theme.accent : theme.border,
                  opacity: p.state === 'locked' ? 0.55 : 1
                }}>
                  <div style={{ display: 'flex', alignItems: 'baseline', justifyContent: 'space-between', gap: 8 }}>
                    <div style={{
                      fontFamily: theme.fontDisplay, fontSize: 18,
                      color: colors.txt, lineHeight: 1, letterSpacing: 0.2,
                      textTransform: 'uppercase'
                    }}>{p.title}</div>
                    {p.state === 'current' && <Chip theme={theme} accent>ACTIVE</Chip>}
                    {p.state === 'done' &&
                    <span style={{ fontFamily: theme.fontMono, fontSize: 10, color: theme.accent, fontWeight: 700, letterSpacing: 0.8 }}>
                        COMPLETE
                      </span>
                    }
                  </div>
                  <div style={{
                    fontFamily: theme.fontBody, fontSize: 12, color: colors.sub, marginTop: 4
                  }}>{p.sub}</div>
                  <div style={{
                    marginTop: 10, display: 'flex', alignItems: 'center', gap: 8
                  }}>
                    <div style={{
                      flex: 1, height: 4, borderRadius: 99,
                      background: theme.surface2, overflow: 'hidden'
                    }}>
                      <div style={{
                        width: `${p.done / p.goals * 100}%`, height: '100%',
                        background: p.state === 'done' ? theme.accent : p.state === 'current' ? theme.accent : theme.faint
                      }} />
                    </div>
                    <span style={{ fontFamily: theme.fontMono, fontSize: 10, color: theme.muted, fontWeight: 600 }}>
                      {p.done}/{p.goals}
                    </span>
                  </div>
                </Card>
              </div>);

          })}
        </div>
      </div>

      {/* Active phase goals */}
      <div style={{ padding: `${theme.gap}px ${theme.pad}px 0` }}>
        <div style={{
          fontFamily: theme.fontMono, fontSize: 11, color: theme.muted,
          letterSpacing: 1, fontWeight: 600, marginBottom: 10
        }}>PHASE 03 · GOALS</div>
        <Card theme={theme} style={{ padding: 4 }}>
          {goals.map((g, i) =>
          <div key={i} style={{
            padding: '12px 12px', display: 'flex', alignItems: 'center', gap: 12,
            borderBottom: i < goals.length - 1 ? `1px solid ${theme.border}` : 'none'
          }}>
              <div style={{
              width: 22, height: 22, borderRadius: 7,
              background: g.done ? theme.accent : 'transparent',
              border: g.done ? 'none' : `1.5px solid ${theme.faint}`,
              display: 'flex', alignItems: 'center', justifyContent: 'center',
              flexShrink: 0
            }}>
                {g.done && Icon.check(theme.onAccent)}
              </div>
              <div style={{
              flex: 1, fontFamily: theme.fontBody, fontSize: 14,
              color: g.done ? theme.muted : theme.text,
              fontWeight: 500,
              textDecoration: g.done ? 'line-through' : 'none',
              textDecorationColor: theme.faint
            }}>{g.t}</div>
              {g.due && <Chip theme={theme}>{g.due}</Chip>}
            </div>
          )}
        </Card>
      </div>
    </div>);

}

// ─────────────────────────────────────────────────────────────
// 4. COMMUNITY FEED
// ─────────────────────────────────────────────────────────────
// Sample posts shown in demo mode (no Supabase keys) and as a fallback.
const DEMO_POSTS = [
  {
    kind: 'track', avatarLbl: 'KP', avatarColor: '#C72820', name: 'kai.park',
    sub: '2H · drop · mixed today',
    title: 'NEON HOURS (rough mix)',
    coverPal: ['#C72820', '#0A0A0A'], coverLbl: 'KP', coverSub: 'rough · 2:47',
    body: 'mixed this in 3 hrs after today\'s lesson — gain staging changed everything fr',
    audio: true, likes: 47, comments: 12
  },
  {
    kind: 'win', avatarLbl: 'SV', avatarColor: '#CAFF33', name: 'sasha.vee',
    sub: '5H · win 🚀',
    body: 'GOT ON AN OFFICIAL SPOTIFY EDITORIAL!! "fresh finds" 🥹 thanks to Noah for the pitch template lesson',
    tag: 'PLAYLIST PLACE',
    likes: 312, comments: 48
  },
  {
    kind: 'ask', avatarLbl: 'TM', avatarColor: '#7A5AE0', name: 'theo.m',
    sub: '6H · ask',
    body: 'what\'s everyone using for vocal compression rn? been trying to get that pillowy r&b feel and my track sounds flat 😭',
    tag: 'NEED ADVICE',
    likes: 28, comments: 31
  },
  {
    kind: 'track', avatarLbl: 'IL', avatarColor: '#FBBF24', name: 'isla.loop',
    sub: 'yesterday · feedback wanted',
    title: 'LOW TIDE',
    coverPal: ['#FBBF24', '#7A5AE0'], coverLbl: 'IL', coverSub: 'final · 3:18',
    body: 'mastered version of low tide. brutally honest feedback only pls',
    audio: true, likes: 89, comments: 24
  }];

const FEED_COLORS = ['#C72820', '#7A5AE0', '#CAFF33', '#FBBF24', '#22C55E', '#EC4899', '#06B6D4', '#F97316'];
function feedColorFor(handle) {
  let h = 0;
  for (let i = 0; i < (handle || '').length; i++) h = (h * 31 + handle.charCodeAt(i)) >>> 0;
  return FEED_COLORS[h % FEED_COLORS.length];
}
function feedInitials(name, handle) {
  const U = window.LAUNCH_USER;
  if (U && U.initialsFrom) return U.initialsFrom(name, handle);
  return (handle || '??').slice(0, 2).toUpperCase();
}
function feedRelTime(iso) {
  try {
    const then = new Date(iso).getTime();
    const mins = Math.max(0, Math.round((Date.now() - then) / 60000));
    if (mins < 60) return mins + 'M';
    const hrs = Math.round(mins / 60);
    if (hrs < 24) return hrs + 'H';
    return Math.round(hrs / 24) + 'D';
  } catch (e) { return 'NOW'; }
}
// Map a live `feed_posts` row into the shape the card renderer expects.
function mapLivePost(r, theme) {
  return {
    id: r.id,
    kind: r.kind || 'track',
    name: r.author_handle || 'artist',
    avatarLbl: feedInitials(r.author_name, r.author_handle),
    avatarColor: feedColorFor(r.author_handle || ''),
    sub: feedRelTime(r.created_at) + ' · ' + (r.kind || 'post'),
    title: r.title || '',
    body: r.body || '',
    tag: r.tag || '',
    audio: !!r.audio,
    audioPath: r.audio_path || null,
    author_id: r.author_id || null,
    coverPal: (r.cover_pal && r.cover_pal.length) ? r.cover_pal : [theme.accent, '#0A0A0A'],
    coverLbl: r.cover_lbl || feedInitials(r.author_name, r.author_handle),
    coverSub: r.cover_sub || '',
    likes: r.like_count || 0,
    comments: r.comment_count || 0,
    liked_by_me: !!r.liked_by_me,
  };
}

// Peer-rating rubric — the SAME 5 categories as the $11 evaluation (each 1–10 → /50).
const RATE_RUBRIC = [
  { id: 'structure', label: 'SONG STRUCTURE' },
  { id: 'hook', label: 'TITLE / HOOK' },
  { id: 'idea', label: 'IDEA' },
  { id: 'lyrics', label: 'LYRICS' },
  { id: 'melody', label: 'MELODY' },
];
const RATE_ZERO_SCORES = { structure: 7, hook: 7, idea: 7, lyrics: 7, melody: 7 };

// m:ss for the player clock.
function fmtTime(s) {
  s = Math.max(0, Math.floor(s || 0));
  const m = Math.floor(s / 60);
  const ss = s % 60;
  return m + ':' + (ss < 10 ? '0' : '') + ss;
}

// Real audio player for an uploaded track: play/pause toggle, live progress,
// and tap-to-seek on the waveform. The signed URL is resolved lazily on the
// first play/seek so a long feed doesn't fetch every track up front.
function TrackPlayer({ theme, post }) {
  const U = window.LAUNCH_USER;
  const audioRef = React.useRef(null);
  const pendingSeekRef = React.useRef(null);  // frac to apply once metadata loads
  const [url, setUrl] = useState2(null);
  const [loading, setLoading] = useState2(false);
  const [playing, setPlaying] = useState2(false);
  const [cur, setCur] = useState2(0);
  const [dur, setDur] = useState2(0);
  const hasAudio = !!(post.audioPath && U && U.trackAudioUrl);

  const ensureUrl = () => {
    if (url || loading || !hasAudio) return;
    setLoading(true);
    U.trackAudioUrl(post.audioPath).then(u => { setLoading(false); if (u) setUrl(u); });
  };

  const onLoadedMeta = () => {
    const el = audioRef.current; if (!el) return;
    setDur(el.duration || 0);
    if (pendingSeekRef.current != null && el.duration > 0) {
      el.currentTime = Math.max(0, Math.min(el.duration, pendingSeekRef.current * el.duration));
      setCur(el.currentTime);
      pendingSeekRef.current = null;
    }
    if (playing && el.paused) el.play().catch(() => {});
  };

  const toggle = () => {
    if (!hasAudio) return;
    const el = audioRef.current;
    if (!url) { setPlaying(true); ensureUrl(); return; }  // play once the src loads (onLoadedMeta)
    if (!el) return;
    if (el.paused) el.play().catch(() => {}); else el.pause();
  };

  const seekFrac = (frac) => {
    if (!hasAudio) return;
    frac = Math.max(0, Math.min(1, frac));
    const el = audioRef.current;
    if (!url || !el || !(el.duration > 0)) { pendingSeekRef.current = frac; ensureUrl(); return; }
    el.currentTime = frac * el.duration;
    setCur(el.currentTime);
  };

  const frac = dur > 0 ? cur / dur : 0;
  return (
    <div style={{
      display: 'flex', alignItems: 'center', gap: 10,
      padding: 10, borderRadius: 12, background: theme.surface2, marginBottom: 10,
    }}>
      <CoverArt palette={post.coverPal} label={post.coverLbl} sub={post.coverSub} size={56} />
      <button onClick={toggle} disabled={!hasAudio}
        title={hasAudio ? (playing ? 'Pause' : 'Play') : 'Audio unavailable'} style={{
          width: 34, height: 34, borderRadius: 99, background: hasAudio ? theme.accent : theme.surface, border: 'none',
          display: 'flex', alignItems: 'center', justifyContent: 'center',
          flexShrink: 0, cursor: hasAudio ? 'pointer' : 'default', padding: 0,
        }}>
        {playing
          ? <svg width="14" height="14" viewBox="0 0 24 24" fill={theme.onAccent}><path d="M6 5h4v14H6zM14 5h4v14h-4z" /></svg>
          : <svg width="14" height="14" viewBox="0 0 24 24" fill={hasAudio ? theme.onAccent : theme.muted}><path d="M6 4l14 8-14 8V4z" /></svg>}
      </button>
      <div onClick={(e) => { const r = e.currentTarget.getBoundingClientRect(); seekFrac((e.clientX - r.left) / r.width); }}
        title={hasAudio ? 'Tap to seek' : ''}
        style={{ flex: 1, cursor: hasAudio ? 'pointer' : 'default' }}>
        <Waveform accent={theme.accent} muted={theme.faint} bars={28} played={frac} />
      </div>
      <span style={{ fontFamily: theme.fontMono, fontSize: 10, color: theme.muted, fontWeight: 600, minWidth: 56, textAlign: 'right' }}>
        {fmtTime(cur)} / {dur > 0 ? fmtTime(dur) : ((post.coverSub && post.coverSub.match(/\d+:\d\d/)) ? post.coverSub.match(/\d+:\d\d/)[0] : '0:00')}
      </span>
      {hasAudio && url &&
        <audio ref={audioRef} src={url} preload="metadata"
          onLoadedMetadata={onLoadedMeta}
          onTimeUpdate={() => { const el = audioRef.current; if (el) setCur(el.currentTime); }}
          onPlay={() => setPlaying(true)}
          onPause={() => setPlaying(false)}
          onEnded={() => { setPlaying(false); setCur(0); }}
          style={{ display: 'none' }} />
      }
    </div>
  );
}

function FeedScreen({ theme, copy, onOpenMember, following = [], onToggleFollow, members = null, online = null, onlineCount = 0, liveMode = false, canInteract = true, onNeedPlan }) {
  const U = window.LAUNCH_USER;
  // No active plan → can browse but not interact; nudge to subscribe instead.
  const requirePlan = () => { if (!canInteract) { onNeedPlan && onNeedPlan(); return false; } return true; };
  const [filter, setFilter] = useState2('all');
  // Feed posts — start with samples; replaced by live rows in live mode.
  const [feed, setFeed] = useState2(() => DEMO_POSTS.map((p, i) => ({ id: 'demo-' + i, ...p })));
  const [liked, setLiked] = useState2({});          // { [postId]: bool } — current desired like state
  const [thread, setThread] = useState2({});        // { [postId]: [{ name, body }] } existing comments
  const [added, setAdded] = useState2({});          // { [postId]: [{ name, body }] } added this session
  const [openComments, setOpenComments] = useState2(null); // post id whose thread is open
  const [draft, setDraft] = useState2('');
  const [showComposer, setShowComposer] = useState2(false); // in-app "new post" composer
  const [composerText, setComposerText] = useState2('');
  const points = U.usePoints();                     // reactive points balance
  const [redeemMsg, setRedeemMsg] = useState2(null);
  const [redeeming, setRedeeming] = useState2(false); // guards against double-redeem
  const [creditCents, setCreditCents] = useState2(0);  // real account credit (live)
  useEffect2(() => {
    let alive = true;
    const load = () => { if (U.fetchMyReviewCredit) U.fetchMyReviewCredit().then(c => { if (alive) setCreditCents(c || 0); }); };
    load();
    window.addEventListener('launch.creditchange', load);
    return () => { alive = false; window.removeEventListener('launch.creditchange', load); };
  }, []);

  // Peer tracks: upload composer + per-track ratings
  const [trackRatings, setTrackRatings] = useState2({ summary: {}, mine: {} });
  const [localRated, setLocalRated] = useState2({});     // { postId: myTotal } (demo + optimistic)
  const [showUpload, setShowUpload] = useState2(false);
  const [trackTitle, setTrackTitle] = useState2('');
  const [uploading, setUploading] = useState2(false);
  const [uploadMsg, setUploadMsg] = useState2(null);
  const [agreementGate, setAgreementGate] = useState2(false);  // consent gate before upload
  const [consented, setConsented] = useState2(false);  // read+agreed THIS upload — re-prompts every time
  const trackFileRef = React.useRef(null);
  const [ratingOpen, setRatingOpen] = useState2(null);   // postId whose rating form is open
  const [ratingScores, setRatingScores] = useState2(RATE_ZERO_SCORES);

  useEffect2(() => {
    let alive = true;
    const load = () => { if (U.fetchTrackRatings) U.fetchTrackRatings().then(r => { if (alive && r) setTrackRatings(r); }); };
    load();
    return () => { alive = false; };
  }, []);

  // Opening the upload form: show the Song Submission Agreement FIRST if the
  // user hasn't accepted it yet — they can't even reach the file picker until
  // they read it and tick "I agree".
  const openUpload = async () => {
    if (!requirePlan()) return;
    setUploadMsg(null);
    // Prompt on EVERY upload (per-session `consented` flag), not just the first.
    if (!consented) { setAgreementGate(true); return; }
    setShowUpload(true);
  };

  // Upload an unreleased track (+10 to the uploader). Re-checks the agreement as
  // a safety net (the entry gate normally handles it).
  const submitUpload = async () => {
    if (!requirePlan()) return;
    const title = trackTitle.trim();
    if (!title) { setUploadMsg({ ok: false, text: 'Give your song a title.' }); return; }
    if (!consented) { setUploadMsg(null); setAgreementGate(true); return; }
    doUpload();
  };

  // The actual upload — only reached once the agreement is accepted.
  const doUpload = () => {
    const title = trackTitle.trim();
    const file = trackFileRef.current && trackFileRef.current.files && trackFileRef.current.files[0];
    setUploading(true);
    U.uploadTrack({ title, audioFile: file }).then(res => {
      setUploading(false);
      if (res && res.post) {
        const p = res.post;
        const local = {
          id: p.id, kind: 'track', name: myHandle, avatarLbl: feedInitials(me && me.displayName, myHandle),
          avatarColor: feedColorFor(myHandle), sub: 'NOW · track', title: p.title || title, body: '',
          tag: 'UNRELEASED', audio: true, audioPath: p.audio_path || null, author_id: p.author_id || null,
          coverPal: [theme.accent, '#0A0A0A'], coverLbl: feedInitials(me && me.displayName, myHandle), coverSub: 'unreleased',
          likes: 0, comments: 0, liked_by_me: false,
        };
        setFeed(prev => [local, ...prev]);
        setTrackTitle(''); if (trackFileRef.current) trackFileRef.current.value = '';
        setShowUpload(false); setConsented(false);  // re-prompt the agreement on the next upload
        setUploadMsg({ ok: true, text: 'Song uploaded! +10 points. Other artists can now rate it.' });
      } else {
        setUploadMsg({ ok: false, text: (res && res.error) || "Couldn't upload." });
      }
    });
  };

  // Submit a peer rating (+10 to the rater).
  const submitRating = (post) => {
    if (!requirePlan()) return;
    const id = post.id;
    const scores = { ...ratingScores };
    const total = Object.values(scores).reduce((a, b) => a + (Number(b) || 0), 0);
    U.rateTrack(id, scores).then(res => {
      if (res && res.ok) {
        setLocalRated(prev => ({ ...prev, [id]: res.total != null ? res.total : total }));
        setTrackRatings(prev => {
          const s = { ...prev.summary };
          const cur = s[id] || { votes: 0, avg: 0 };
          const votes = cur.votes + 1;
          const avg = Math.round(((cur.avg * cur.votes) + (res.total != null ? res.total : total)) / votes);
          s[id] = { votes, avg };
          return { summary: s, mine: { ...prev.mine, [id]: res.total != null ? res.total : total } };
        });
        setRatingOpen(null);
        setRatingScores(RATE_ZERO_SCORES);
      } else {
        setLocalRated(prev => ({ ...prev, [id]: prev[id] }));
        window.alert((res && res.error) || "Couldn't submit rating.");
      }
    });
  };

  // Delete one of the current user's uploaded tracks. Removes it from the feed
  // and takes back the +10 points earned for uploading it.
  const removeTrack = (post) => {
    if (!requirePlan()) return;
    if (!window.confirm('Delete this song? You will lose the 10 points you earned for uploading it.')) return;
    const id = post.id;
    setFeed(prev => prev.filter(x => x.id !== id));      // optimistic remove
    if (U && U.deleteTrack) {
      U.deleteTrack(post).then(res => {
        if (res && res.error) {
          setFeed(prev => [post, ...prev]);              // restore on failure
          window.alert(res.error);
        }
      });
    }
  };

  const me = U && U.loadProfile ? U.loadProfile() : null;
  const myHandle = (me && me.handle) || 'you';
  const isLive = (postId) => !String(postId).startsWith('demo-');

  // Pull live posts from Supabase (no-op / null in demo mode → keep samples).
  useEffect2(() => {
    let alive = true;
    if (U && U.fetchFeed) {
      U.fetchFeed().then(rows => {
        if (!alive || !rows) return;
        setFeed(rows.map(r => mapLivePost(r, theme)));
        const lk = {};
        rows.forEach(r => { if (r.liked_by_me) lk[r.id] = true; });
        setLiked(lk);
      });
    }
    return () => { alive = false; };
  }, []);

  const toggleLike = (post) => {
    if (!requirePlan()) return;
    const id = post.id;
    const now = (id in liked) ? liked[id] : !!post.liked_by_me;
    const next = !now;
    setLiked(prev => ({ ...prev, [id]: next }));
    if (U && U.awardPoints) U.awardPoints('like', { undo: !next, meta: { postId: id } });
    if (U && U.togglePostLike && isLive(id)) {
      U.togglePostLike(id, now).then(res => {
        if (res && res.error) setLiked(prev => ({ ...prev, [id]: now })); // revert on failure
      });
    }
  };

  const openThread = (post) => {
    const id = post.id;
    if (openComments === id) { setOpenComments(null); return; }
    setOpenComments(id);
    setDraft('');
    if (U && U.fetchComments && isLive(id) && !thread[id]) {
      U.fetchComments(id).then(rows => {
        if (!rows) return;
        setThread(prev => ({ ...prev, [id]: rows.map(c => ({ name: c.author_handle || 'artist', body: c.body })) }));
      });
    }
  };

  const postComment = (post) => {
    if (!requirePlan()) return;
    const body = draft.trim();
    if (!body) return;
    const id = post.id;
    setAdded(prev => ({ ...prev, [id]: [...(prev[id] || []), { name: myHandle, body }] }));
    setDraft('');
    if (U && U.awardPoints) U.awardPoints('comment', { meta: { postId: id } });
    if (U && U.addComment && isLive(id)) {
      U.addComment(id, body).then(res => {
        if (res && res.error) { /* keep optimistic comment; could surface error */ }
      });
    }
  };

  // Create a new post. Opens the in-app composer (no external browser prompt).
  const newPost = () => {
    if (!requirePlan()) return;
    setComposerText('');
    setShowComposer(true);
  };
  // Publish what the in-app composer holds. Prepends optimistically to the feed.
  const submitPost = () => {
    const body = composerText.trim();
    if (!body) return;
    setShowComposer(false);
    setComposerText('');
    if (U && U.awardPoints) U.awardPoints('post', { meta: { body } });
    const local = {
      id: 'demo-new-' + (feed.length + 1),
      kind: 'win', name: myHandle, avatarLbl: feedInitials(me && me.displayName, myHandle),
      avatarColor: feedColorFor(myHandle), sub: 'NOW · win', body,
      title: '', tag: '', audio: false, likes: 0, comments: 0, liked_by_me: false,
    };
    if (U && U.createPost && U.isConfigured && U.isConfigured()) {
      U.createPost({ kind: 'win', body }).then(res => {
        if (res && res.post && res.post.id) {
          // Use the real DB id (so likes/comments persist) + local display info.
          setFeed(prev => [{ ...local, id: res.post.id }, ...prev]);
        } else {
          setFeed(prev => [local, ...prev]); // fallback so the user still sees it
        }
      });
    } else {
      setFeed(prev => [local, ...prev]);
    }
  };

  const filters = [
  { id: 'all', l: 'ALL' },
  { id: 'tracks', l: 'TRACKS' }];

  // Filter chips actually filter now (track/ask/win → kind).
  const kindForFilter = { tracks: 'track' };
  const posts = filter === 'all' ? feed : feed.filter(p => p.kind === kindForFilter[filter]);

  // Points / rewards economy
  const cost = U.reviewCost ? U.reviewCost() : 200;
  const bal = Math.max(0, (points && points.balance) || 0);
  const pct = Math.min(100, Math.round((bal / cost) * 100));
  const canRedeem = bal >= cost;
  const redeemBtnOn = canRedeem && !redeeming;
  const rules = U.pointsRules ? U.pointsRules() : { like: 1, comment: 2, post: 5, song: 10 };
  const onRedeem = () => {
    if (!requirePlan()) return;
    if (redeeming) return;                            // a redemption is already in flight
    if (!canRedeem) { setRedeemMsg({ ok: false, text: `You need ${cost - bal} more points to redeem.` }); return; }
    setRedeeming(true);
    U.redeemReview().then((res) => {
      setRedeeming(false);
      if (res && res.ok) {
        if (res.grantedCents) {
          setCreditCents(res.creditCents || 0);
          setRedeemMsg({ ok: true, text: `Done! $${(res.grantedCents / 100).toFixed(0)} credit added to your account — use it free on your next review. ${cost} pts deducted.` });
        } else {
          setRedeemMsg({ ok: true, text: `Done! You redeemed a $40 review — ${cost} pts deducted. A coach will take it soon.` });
        }
      } else {
        setRedeemMsg({ ok: false, text: (res && res.error) || "Couldn't redeem." });
      }
    });
  };
  // Who's online right now — live directory (presence) when available, else the
  // sample directory from social.jsx.
  const S5 = window.LAUNCH_SCREENS_5;
  const memberDir = members || (S5 && S5.MEMBERS) || {};
  const onlineMembers = Object.values(memberDir).filter(m => m && m.online);
  // How many people are connected live right now (Supabase Realtime presence).
  // The `online` set (from app.jsx) is the source of truth and includes me; fall
  // back to the directory's online flags when the raw set isn't wired/synced yet.
  // Distinct connected users (from presence). `online` now mixes handles+ids for
  // matching, so we count via onlineCount, not online.size.
  const liveCount = onlineCount > 0 ? onlineCount : onlineMembers.length;

  return (
    <div style={{ paddingBottom: 110 }}>
      {/* In-app "new post" composer — replaces the old browser prompt */}
      {showComposer &&
      <div onClick={() => setShowComposer(false)} style={{
        position: 'fixed', inset: 0, zIndex: 200,
        background: 'rgba(0,0,0,0.6)', backdropFilter: 'blur(4px)',
        display: 'flex', alignItems: 'flex-end', justifyContent: 'center',
      }}>
        <div onClick={(e) => e.stopPropagation()} style={{
          width: '100%', maxWidth: 520, background: theme.surface,
          borderTopLeftRadius: 20, borderTopRightRadius: 20,
          border: `1px solid ${theme.border}`, borderBottom: 'none',
          padding: 20, paddingBottom: 28,
        }}>
          <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 12 }}>
            <div style={{ fontFamily: theme.fontDisplay, fontSize: 20, color: theme.text, textTransform: 'uppercase', letterSpacing: 0.3 }}>
              Share with the community
            </div>
            <button onClick={() => setShowComposer(false)} style={{
              background: 'transparent', border: 'none', color: theme.muted, cursor: 'pointer',
              fontFamily: theme.fontMono, fontSize: 18, lineHeight: 1, padding: 4,
            }}>✕</button>
          </div>
          <textarea
            value={composerText}
            onChange={(e) => setComposerText(e.target.value)}
            autoFocus
            placeholder="What do you want to share? A win, an ask, a thought…"
            rows={4}
            style={{
              width: '100%', padding: '14px 16px', borderRadius: 12,
              background: theme.surface2, border: `1px solid ${theme.border}`,
              color: theme.text, fontFamily: theme.fontBody, fontSize: 15,
              outline: 'none', resize: 'none', lineHeight: 1.5, marginBottom: 14,
            }} />
          <div style={{ display: 'flex', gap: 8 }}>
            <button onClick={() => setShowComposer(false)} style={{
              flex: 1, padding: '13px', borderRadius: 12, border: `1px solid ${theme.border}`,
              background: 'transparent', color: theme.muted, cursor: 'pointer',
              fontFamily: theme.fontMono, fontSize: 11, fontWeight: 700, letterSpacing: 0.8, textTransform: 'uppercase',
            }}>Cancel</button>
            <button onClick={submitPost} disabled={!composerText.trim()} style={{
              flex: 2, padding: '13px', borderRadius: 12, border: 'none',
              background: composerText.trim() ? theme.accent : theme.surface2,
              color: composerText.trim() ? theme.onAccent : theme.muted,
              cursor: composerText.trim() ? 'pointer' : 'default',
              fontFamily: theme.fontDisplay, fontSize: 14, letterSpacing: 0.8, textTransform: 'uppercase',
              display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 6,
            }}>Post <span style={{ fontFamily: theme.fontMono, fontSize: 11 }}>+{rules.post}</span></button>
          </div>
        </div>
      </div>
      }
      <TopBar theme={theme}
      left={
      <div>
            <div style={{ fontFamily: theme.fontMono, fontSize: 10, color: liveCount > 0 ? '#22C55E' : theme.muted, letterSpacing: 1, fontWeight: 600 }}>
              {liveCount > 0
                ? `● ${liveCount} ${liveCount === 1 ? 'ARTIST' : 'ARTISTS'} ONLINE NOW`
                : '● LIVE'}
            </div>
            <DisplayHeading theme={theme} size={32} style={{ marginTop: 4 }}>
              {copy.communityTitle}
            </DisplayHeading>
          </div>
      }
      right={<IconBtn theme={theme} onClick={newPost}>{Icon.plus(theme.text)}</IconBtn>} />

      {/* Guest gate — no active plan */}
      {!canInteract &&
      <div style={{ padding: `0 ${theme.pad}px ${theme.gap}px` }}>
        <button onClick={() => onNeedPlan && onNeedPlan()} style={{
          width: '100%', textAlign: 'left', cursor: 'pointer', border: 'none',
          borderRadius: 14, padding: 14, color: '#fff',
          background: 'linear-gradient(135deg, #5B6AC4 0%, #C72820 100%)',
          display: 'flex', alignItems: 'center', gap: 12,
        }}>
          <div style={{ flex: 1 }}>
            <div style={{ fontFamily: theme.fontMono, fontSize: 10, letterSpacing: 1, fontWeight: 700, opacity: 0.85 }}>BROWSING AS GUEST</div>
            <div style={{ fontFamily: theme.fontDisplay, fontSize: 18, marginTop: 3, lineHeight: 1.05 }}>SUBSCRIBE TO JOIN IN</div>
            <div style={{ fontFamily: theme.fontBody, fontSize: 12, opacity: 0.9, marginTop: 3 }}>Post, like, comment, upload, rate, chat &amp; follow.</div>
          </div>
          <span style={{ fontFamily: theme.fontMono, fontSize: 11, fontWeight: 700, letterSpacing: 1, background: 'rgba(255,255,255,0.2)', padding: '8px 12px', borderRadius: 99, whiteSpace: 'nowrap' }}>SEE PLANS →</span>
        </button>
      </div>
      }

      {/* Points & rewards — the social hub's hero */}
      <div style={{ padding: `0 ${theme.pad}px ${theme.gap}px` }}>
        <Card theme={theme} style={{ padding: 16 }}>
          <div style={{ display: 'flex', alignItems: 'flex-end', justifyContent: 'space-between', gap: 12 }}>
            <div>
              <div style={{ fontFamily: theme.fontMono, fontSize: 10, color: theme.muted, letterSpacing: 1, fontWeight: 700 }}>
                YOUR POINTS
              </div>
              <div style={{ display: 'flex', alignItems: 'baseline', gap: 6, marginTop: 2 }}>
                <span style={{ fontFamily: theme.fontDisplay, fontSize: 40, color: theme.text, lineHeight: 1, letterSpacing: -0.5 }}>
                  {bal}
                </span>
                <span style={{ fontFamily: theme.fontMono, fontSize: 12, color: theme.accent, fontWeight: 700 }}>PTS</span>
              </div>
            </div>
            <div style={{ textAlign: 'right' }}>
              <div style={{ fontFamily: theme.fontMono, fontSize: 9, color: theme.muted, letterSpacing: 0.8, fontWeight: 700 }}>
                REDEEM FOR
              </div>
              <div style={{ fontFamily: theme.fontDisplay, fontSize: 18, color: theme.text, lineHeight: 1, marginTop: 2 }}>
                $40 REVIEW
              </div>
              <div style={{ fontFamily: theme.fontMono, fontSize: 9, color: theme.muted, marginTop: 2 }}>
                {cost} PTS · UNRELEASED SONG
              </div>
            </div>
          </div>

          {/* progress to next review */}
          <div style={{ marginTop: 12, height: 8, borderRadius: 99, background: theme.surface2, overflow: 'hidden' }}>
            <div style={{ width: pct + '%', height: '100%', borderRadius: 99, background: theme.accent, transition: 'width .3s ease' }} />
          </div>
          <div style={{ display: 'flex', justifyContent: 'space-between', marginTop: 6 }}>
            <span style={{ fontFamily: theme.fontMono, fontSize: 9, color: theme.muted, fontWeight: 600 }}>
              {canRedeem ? 'READY TO REDEEM!' : `${bal} / ${cost}`}
            </span>
            <span style={{ fontFamily: theme.fontMono, fontSize: 9, color: theme.muted, fontWeight: 600 }}>
              {pct}%
            </span>
          </div>

          {/* redeem button */}
          <button onClick={onRedeem} disabled={!redeemBtnOn} style={{
            marginTop: 12, width: '100%', padding: '12px 16px', borderRadius: 12,
            border: 'none', cursor: redeemBtnOn ? 'pointer' : 'default',
            background: redeemBtnOn ? theme.accent : theme.surface2,
            color: redeemBtnOn ? theme.onAccent : theme.muted,
            fontFamily: theme.fontDisplay, fontSize: 14, letterSpacing: 1, textTransform: 'uppercase',
          }}>
            {redeeming ? 'REDEEMING…' : canRedeem ? 'REDEEM $40 REVIEW' : `${cost - bal} PTS TO GO`}
          </button>

          {creditCents > 0 &&
          <div style={{
            marginTop: 10, display: 'flex', alignItems: 'center', justifyContent: 'space-between',
            padding: '8px 12px', borderRadius: 10, background: theme.surface2,
          }}>
            <span style={{ fontFamily: theme.fontMono, fontSize: 10, color: theme.muted, letterSpacing: 0.8, fontWeight: 700 }}>
              REVIEW CREDIT
            </span>
            <span style={{ fontFamily: theme.fontDisplay, fontSize: 16, color: '#22C55E' }}>
              ${(creditCents / 100).toFixed(2)}
            </span>
          </div>
          }

          {redeemMsg &&
          <div style={{
            marginTop: 10, padding: '10px 12px', borderRadius: 10,
            background: redeemMsg.ok ? 'rgba(34,197,94,0.12)' : theme.surface2,
            border: `1px solid ${redeemMsg.ok ? 'rgba(34,197,94,0.4)' : theme.border}`,
            fontFamily: theme.fontBody, fontSize: 12,
            color: redeemMsg.ok ? '#22C55E' : theme.text,
          }}>{redeemMsg.text}</div>
          }

          {/* how to earn */}
          <div style={{ display: 'flex', flexWrap: 'wrap', gap: 6, marginTop: 12 }}>
            {[
            { l: 'LIKE', p: rules.like },
            { l: 'COMMENT', p: rules.comment },
            { l: 'POST', p: rules.post },
            { l: 'UPLOAD SONG', p: rules.track },
            { l: 'RATE', p: rules.rate }].
            map((r) =>
            <span key={r.l} style={{
              display: 'inline-flex', alignItems: 'center', gap: 5,
              padding: '5px 9px', borderRadius: 99, background: theme.surface2,
              fontFamily: theme.fontMono, fontSize: 9, fontWeight: 700, letterSpacing: 0.5,
              color: theme.muted,
            }}>
                <span style={{ color: theme.accent }}>+{r.p}</span>{r.l}
              </span>
            )}
          </div>
        </Card>
      </div>

      {/* Active now — who's online */}
      {onlineMembers.length > 0 &&
      <div style={{ padding: `0 ${theme.pad}px ${theme.gap}px` }}>
        <div style={{ fontFamily: theme.fontMono, fontSize: 10, color: '#22C55E', letterSpacing: 1, fontWeight: 700, marginBottom: 8 }}>
          ● {liveCount} ONLINE NOW
        </div>
        <div style={{ display: 'flex', gap: 12, overflowX: 'auto', margin: `0 -${theme.pad}px`, padding: `0 ${theme.pad}px 2px` }}>
          {onlineMembers.map((m) =>
          <button key={m.handle} onClick={() => onOpenMember && onOpenMember(m.handle)} style={{
            background: 'transparent', border: 'none', cursor: 'pointer',
            display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 6, padding: 0, flexShrink: 0,
          }}>
              <div style={{ position: 'relative' }}>
                <Avatar m={m} size={52} fontSize={17} theme={theme} />
                <div style={{
                position: 'absolute', bottom: 0, right: 0, width: 13, height: 13, borderRadius: 99,
                background: '#22C55E', border: `2px solid ${theme.bg}`,
              }} />
              </div>
              <span style={{ fontFamily: theme.fontMono, fontSize: 9, color: theme.text, letterSpacing: 0.3, fontWeight: 600 }}>
                {m.handle.split('.')[0]}
              </span>
            </button>
          )}
        </div>
      </div>
      }

      {/* Filter chips */}
      <div style={{
        display: 'flex', gap: 6, padding: `2px ${theme.pad}px ${theme.gap}px`,
        overflowX: 'auto'
      }}>
        {filters.map((f) =>
        <button key={f.id} onClick={() => setFilter(f.id)} style={{
          padding: '8px 14px', borderRadius: 99, cursor: 'pointer',
          border: 'none',
          background: filter === f.id ? theme.text : 'transparent',
          color: filter === f.id ? theme.bg : theme.text,
          fontFamily: theme.fontMono, fontSize: 11, fontWeight: 700,
          letterSpacing: 0.8, textTransform: 'uppercase',
          boxShadow: filter === f.id ? 'none' : `inset 0 0 0 1px ${theme.border}`,
          whiteSpace: 'nowrap'
        }}>{f.l}</button>
        )}
      </div>

      {/* Upload your unreleased track (TRACKS filter) */}
      {filter === 'tracks' &&
      <div style={{ padding: `0 ${theme.pad}px ${theme.gap}px` }}>
        <Card theme={theme} style={{ padding: 14 }}>
          {!showUpload ?
          <button onClick={openUpload} style={{
            width: '100%', padding: '12px 16px', borderRadius: 12, border: `1px dashed ${theme.border}`,
            background: theme.surface2, color: theme.text, cursor: 'pointer',
            fontFamily: theme.fontDisplay, fontSize: 14, letterSpacing: 0.5, textTransform: 'uppercase',
            display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 8,
          }}>
            ↑ UPLOAD YOUR UNRELEASED SONG <span style={{ color: theme.accent, fontFamily: theme.fontMono, fontSize: 11 }}>+10</span>
          </button>
          :
          <div>
            <div style={{ fontFamily: theme.fontMono, fontSize: 10, color: theme.muted, letterSpacing: 1, fontWeight: 700, marginBottom: 10 }}>
              UPLOAD UNRELEASED SONG
            </div>
            <input
              value={trackTitle}
              onChange={(e) => setTrackTitle(e.target.value)}
              placeholder="Song title"
              style={{
                width: '100%', padding: '11px 13px', borderRadius: 10,
                border: `1px solid ${theme.border}`, background: theme.surface2,
                color: theme.text, fontFamily: theme.fontBody, fontSize: 14, outline: 'none', marginBottom: 10,
              }} />
            <input ref={trackFileRef} type="file" accept="audio/*" style={{
              width: '100%', fontFamily: theme.fontMono, fontSize: 11, color: theme.muted, marginBottom: 12,
            }} />
            <div style={{ display: 'flex', gap: 8 }}>
              <button onClick={() => { setShowUpload(false); setUploadMsg(null); }} style={{
                flex: 1, padding: '11px', borderRadius: 10, border: `1px solid ${theme.border}`,
                background: 'transparent', color: theme.muted, cursor: 'pointer',
                fontFamily: theme.fontMono, fontSize: 11, fontWeight: 700, letterSpacing: 0.8, textTransform: 'uppercase',
              }}>CANCEL</button>
              <button onClick={submitUpload} disabled={uploading || !trackTitle.trim()} style={{
                flex: 2, padding: '11px', borderRadius: 10, border: 'none',
                background: (uploading || !trackTitle.trim()) ? theme.surface2 : theme.accent,
                color: (uploading || !trackTitle.trim()) ? theme.muted : theme.onAccent, cursor: 'pointer',
                fontFamily: theme.fontDisplay, fontSize: 13, letterSpacing: 0.8, textTransform: 'uppercase',
              }}>{uploading ? 'UPLOADING…' : 'UPLOAD (+10)'}</button>
            </div>
          </div>
          }
          {uploadMsg &&
          <div style={{
            marginTop: 10, padding: '9px 12px', borderRadius: 10,
            background: uploadMsg.ok ? 'rgba(34,197,94,0.12)' : theme.surface2,
            border: `1px solid ${uploadMsg.ok ? 'rgba(34,197,94,0.4)' : theme.border}`,
            fontFamily: theme.fontBody, fontSize: 12, color: uploadMsg.ok ? '#22C55E' : theme.text,
          }}>{uploadMsg.text}</div>
          }
        </Card>
      </div>
      }

      {/* Posts */}
      <div style={{ display: 'flex', flexDirection: 'column', gap: 10, padding: `0 ${theme.pad}px` }}>
        {posts.map((p, i) =>
        <Card key={p.id} theme={theme} style={{ padding: 14 }}>
            {/* head */}
            <div style={{ display: 'flex', alignItems: 'center', gap: 10, marginBottom: 10 }}>
              <button onClick={() => onOpenMember && onOpenMember(p.name)} style={{
                background: 'transparent', border: 'none', cursor: 'pointer', padding: 0, flexShrink: 0,
              }}>
                <Avatar src={memberDir[p.name] && memberDir[p.name].avatarUrl} lbl={p.avatarLbl} color={p.avatarColor} size={36} fontSize={13} theme={theme} />
              </button>
              <button onClick={() => onOpenMember && onOpenMember(p.name)} style={{
                flex: 1, minWidth: 0, textAlign: 'left',
                background: 'transparent', border: 'none', cursor: 'pointer', padding: 0,
              }}>
                <div style={{ fontFamily: theme.fontBody, fontSize: 14, color: theme.text, fontWeight: 600 }}>
                  {p.name}
                </div>
                <div style={{ fontFamily: theme.fontMono, fontSize: 10, color: theme.muted, letterSpacing: 0.5 }}>
                  {p.sub.toUpperCase()}
                </div>
              </button>
              {onToggleFollow && !following.includes(p.name) && (
                <button onClick={() => onToggleFollow(p.name)} style={{
                  background: theme.text, color: theme.bg, border: 'none',
                  fontFamily: theme.fontMono, fontSize: 9, fontWeight: 700, letterSpacing: 1,
                  padding: '6px 10px', borderRadius: 99, cursor: 'pointer',
                  textTransform: 'uppercase',
                }}>+ FOLLOW</button>
              )}
              {p.tag && <Chip theme={theme} accent={p.kind === 'win'}>{p.tag}</Chip>}
              {p.kind === 'track' && p.name === myHandle &&
                <button onClick={() => removeTrack(p)} title="Delete song (you lose the +10 points)" style={{
                  background: 'transparent', border: 'none', cursor: 'pointer', padding: 6,
                  color: theme.muted, flexShrink: 0, display: 'flex', alignItems: 'center',
                }}>
                  <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
                    <path d="M3 6h18M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2m2 0v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6" />
                    <line x1="10" y1="11" x2="10" y2="17" /><line x1="14" y1="11" x2="14" y2="17" />
                  </svg>
                </button>
              }
            </div>

            {/* body */}
            {p.title &&
          <div style={{
            fontFamily: theme.fontDisplay, fontSize: 22, color: theme.text,
            textTransform: 'uppercase', letterSpacing: 0.2, lineHeight: 1,
            marginBottom: 10
          }}>{p.title}</div>
          }
            <div style={{
            fontFamily: theme.fontBody, fontSize: 14, color: theme.text,
            lineHeight: 1.45, marginBottom: p.audio ? 12 : 10
          }}>{p.body}</div>

            {p.audio && <TrackPlayer theme={theme} post={p} />}

            {/* Peer rating (track posts only) — same /50 rubric as the $11 eval */}
            {p.kind === 'track' && (() => {
            const sum = trackRatings.summary[p.id];
            const myTotal = (trackRatings.mine[p.id] != null) ? trackRatings.mine[p.id]
              : (localRated[p.id] != null ? localRated[p.id] : null);
            const isMine = p.name === myHandle;
            const open = ratingOpen === p.id;
            const liveTotal = Object.values(ratingScores).reduce((a, b) => a + (Number(b) || 0), 0);
            return (
            <div style={{ marginBottom: 10 }}>
              <div style={{
                display: 'flex', alignItems: 'center', gap: 10, padding: '8px 10px',
                borderRadius: 10, background: theme.surface2,
              }}>
                <span style={{ fontFamily: theme.fontMono, fontSize: 10, color: theme.muted, letterSpacing: 0.8, fontWeight: 700 }}>
                  RATING
                </span>
                <span style={{ fontFamily: theme.fontDisplay, fontSize: 18, color: theme.text }}>
                  {sum && sum.votes ? sum.avg : '—'}<span style={{ fontSize: 11, color: theme.muted }}>/50</span>
                </span>
                <span style={{ fontFamily: theme.fontMono, fontSize: 9, color: theme.muted, fontWeight: 600 }}>
                  {sum && sum.votes ? sum.votes + ' vote' + (sum.votes === 1 ? '' : 's') : 'no votes'}
                </span>
                <div style={{ marginLeft: 'auto' }}>
                  {isMine ?
                  <span style={{ fontFamily: theme.fontMono, fontSize: 9, color: theme.muted, fontWeight: 700 }}>YOUR SONG</span>
                  : myTotal != null ?
                  <span style={{ fontFamily: theme.fontMono, fontSize: 10, color: '#22C55E', fontWeight: 700 }}>YOUR SCORE: {myTotal}/50</span>
                  :
                  <button onClick={() => { setRatingOpen(open ? null : p.id); setRatingScores(RATE_ZERO_SCORES); }} style={{
                    background: open ? theme.surface : theme.accent, color: open ? theme.text : theme.onAccent,
                    border: open ? `1px solid ${theme.border}` : 'none', borderRadius: 99,
                    padding: '7px 12px', cursor: 'pointer',
                    fontFamily: theme.fontMono, fontSize: 10, fontWeight: 700, letterSpacing: 0.8, textTransform: 'uppercase',
                  }}>{open ? 'CLOSE' : 'RATE +10'}</button>
                  }
                </div>
              </div>

              {open && !isMine && myTotal == null &&
              <div style={{ marginTop: 10, padding: 12, borderRadius: 10, border: `1px solid ${theme.border}` }}>
                {RATE_RUBRIC.map(cat =>
                <div key={cat.id} style={{ marginBottom: 10 }}>
                  <div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: 5 }}>
                    <span style={{ fontFamily: theme.fontMono, fontSize: 10, color: theme.text, letterSpacing: 0.5, fontWeight: 700 }}>{cat.label}</span>
                    <span style={{ fontFamily: theme.fontDisplay, fontSize: 14, color: theme.accent }}>
                      {ratingScores[cat.id]}<span style={{ fontSize: 9, color: theme.muted }}>/10</span>
                    </span>
                  </div>
                  <div style={{ display: 'flex', gap: 3 }}>
                    {[1,2,3,4,5,6,7,8,9,10].map(n => {
                      const on = ratingScores[cat.id] >= n;
                      return (
                      <button key={n} onClick={() => setRatingScores(s => ({ ...s, [cat.id]: n }))} style={{
                        flex: 1, height: 24, borderRadius: 5, cursor: 'pointer', padding: 0,
                        background: on ? theme.accent : theme.surface2,
                        border: `1px solid ${on ? theme.accent : theme.border}`,
                        color: on ? theme.onAccent : theme.faint,
                        fontFamily: theme.fontMono, fontSize: 9, fontWeight: 700,
                      }}>{n}</button>);
                    })}
                  </div>
                </div>
                )}
                <button onClick={() => submitRating(p)} style={{
                  width: '100%', marginTop: 4, padding: '11px', borderRadius: 10, border: 'none',
                  background: theme.accent, color: theme.onAccent, cursor: 'pointer',
                  fontFamily: theme.fontDisplay, fontSize: 13, letterSpacing: 0.8, textTransform: 'uppercase',
                }}>SUBMIT RATING · {liveTotal}/50 · +10 PTS</button>
              </div>
              }
            </div>);
          })()}

            {/* actions */}
            {(() => {
            const isLiked = (p.id in liked) ? liked[p.id] : !!p.liked_by_me;
            const likeDelta = (isLiked ? 1 : 0) - (p.liked_by_me ? 1 : 0);
            const likeCount = Math.max(0, p.likes + likeDelta);
            const allComments = [...(thread[p.id] || []), ...(added[p.id] || [])];
            const commentCount = p.comments + (added[p.id] || []).length;
            const commentsOpen = openComments === p.id;
            return (
            <div style={{
            display: 'flex', gap: 18, alignItems: 'center', paddingTop: 6,
            borderTop: `1px solid ${theme.border}`, marginTop: 4
          }}>
              <button onClick={() => toggleLike(p)} style={{
              display: 'inline-flex', alignItems: 'center', gap: 6, padding: '8px 0',
              background: 'transparent', border: 'none', cursor: 'pointer',
              color: isLiked ? theme.accent : theme.muted
            }}>
                {Icon.heart(isLiked ? theme.accent : theme.muted, isLiked)}
                <span style={{ fontFamily: theme.fontMono, fontSize: 12, fontWeight: 600 }}>{likeCount}</span>
              </button>
              <button onClick={() => openThread(p)} style={{
              display: 'inline-flex', alignItems: 'center', gap: 6, padding: '8px 0',
              background: 'transparent', border: 'none', cursor: 'pointer',
              color: commentsOpen ? theme.accent : theme.muted
            }}>
                {Icon.chat(commentsOpen ? theme.accent : theme.muted)}
                <span style={{ fontFamily: theme.fontMono, fontSize: 12, fontWeight: 600 }}>{commentCount}</span>
              </button>
              <button style={{
              marginLeft: 'auto', padding: '8px 0',
              background: 'transparent', border: 'none', cursor: 'pointer',
              color: theme.muted
            }}>
                {Icon.share(theme.muted)}
              </button>
            </div>);

          })()}

            {/* comments thread */}
            {openComments === p.id &&
          <div style={{ marginTop: 10, paddingTop: 10, borderTop: `1px solid ${theme.border}` }}>
                {[...(thread[p.id] || []), ...(added[p.id] || [])].map((c, ci) =>
            <div key={ci} style={{ display: 'flex', gap: 8, marginBottom: 8 }}>
                    <span style={{ fontFamily: theme.fontMono, fontSize: 12, color: theme.accent, fontWeight: 700, flexShrink: 0 }}>
                      @{c.name}
                    </span>
                    <span style={{ fontFamily: theme.fontBody, fontSize: 13, color: theme.text, lineHeight: 1.4 }}>
                      {c.body}
                    </span>
                  </div>
            )}
                <div style={{ display: 'flex', gap: 8, alignItems: 'center', marginTop: 4 }}>
                  <input
                value={openComments === p.id ? draft : ''}
                onChange={(e) => setDraft(e.target.value)}
                onKeyDown={(e) => { if (e.key === 'Enter') postComment(p); }}
                placeholder="Add a comment…"
                style={{
                  flex: 1, minWidth: 0, padding: '10px 12px', borderRadius: 10,
                  border: `1px solid ${theme.border}`, background: theme.surface2,
                  color: theme.text, fontFamily: theme.fontBody, fontSize: 13, outline: 'none'
                }} />
                  <button onClick={() => postComment(p)} disabled={!draft.trim()} style={{
                background: draft.trim() ? theme.accent : theme.surface2,
                color: draft.trim() ? theme.onAccent : theme.muted,
                border: 'none', borderRadius: 10, padding: '10px 14px',
                fontFamily: theme.fontMono, fontSize: 11, fontWeight: 700, letterSpacing: 0.8,
                cursor: draft.trim() ? 'pointer' : 'default', textTransform: 'uppercase'
              }}>POST</button>
                </div>
              </div>
          }
          </Card>
        )}
      </div>

      {/* Consent gate — must be accepted before the upload form even opens */}
      {agreementGate && window.LAUNCH_AGREEMENT &&
        <window.LAUNCH_AGREEMENT.SongAgreementGate
          theme={theme}
          onClose={() => setAgreementGate(false)}
          onAccepted={() => { setAgreementGate(false); setConsented(true); setShowUpload(true); }}
        />
      }
    </div>);

}

// ─────────────────────────────────────────────────────────────
// 5. PROFILE
// ─────────────────────────────────────────────────────────────
function ProfileAvatar({ theme, initials = 'MS', color = '#C72820', photo: photoProp = null }) {
  const U = window.LAUNCH_USER;
  const [photo, setPhoto] = useState2(photoProp || null);
  const [busy, setBusy] = useState2(false);
  const fileRef = React.useRef(null);
  // Keep in sync with the saved profile photo (loaded from the backend).
  React.useEffect(() => { setPhoto(photoProp || null); }, [photoProp]);

  const onPick = (e) => {
    const file = e.target.files && e.target.files[0];
    if (e.target) e.target.value = ''; // allow re-picking the same file
    if (!file) return;
    // Optimistic preview while it uploads.
    const reader = new FileReader();
    reader.onload = (ev) => setPhoto(ev.target.result);
    reader.readAsDataURL(file);
    setBusy(true);
    Promise.resolve(U && U.uploadAvatar ? U.uploadAvatar(file) : { error: 'Unavailable' }).then((res) => {
      setBusy(false);
      if (res && res.error) { alert(res.error); setPhoto(photoProp || null); }
      else if (res && res.url) setPhoto(res.url);
    });
  };

  const removePhoto = (e) => {
    e.stopPropagation();
    setPhoto(null);
    if (U && U.removeAvatar) U.removeAvatar();
  };

  return (
    <div style={{ position: 'relative', flexShrink: 0 }}>
      <input
        ref={fileRef}
        type="file"
        accept="image/*"
        onChange={onPick}
        style={{ display: 'none' }}
      />
      <button
        onClick={() => fileRef.current && fileRef.current.click()}
        style={{
          padding: 0, border: 'none', background: 'transparent',
          cursor: 'pointer', position: 'relative', borderRadius: 16,
          overflow: 'hidden', display: 'block',
        }}>
        {photo ? (
          <div style={{
            width: 92, height: 92, borderRadius: 16, overflow: 'hidden',
            backgroundImage: `url(${photo})`, backgroundSize: 'cover', backgroundPosition: 'center',
            position: 'relative',
          }} />
        ) : (
          <CoverArt palette={[color, '#FBBF24']} label={initials} sub="EST · 2025" size={92} radius={16} />
        )}
        {/* Camera badge overlay */}
        <div style={{
          position: 'absolute', bottom: 4, right: 4,
          width: 28, height: 28, borderRadius: 99,
          background: theme.accent, color: theme.onAccent,
          display: 'flex', alignItems: 'center', justifyContent: 'center',
          border: `2px solid ${theme.bg}`,
          boxShadow: '0 2px 6px rgba(0,0,0,0.3)',
        }}>
          <svg width="14" height="14" viewBox="0 0 24 24" fill="none">
            <path d="M3 7h4l2-3h6l2 3h4v13H3V7z" stroke={theme.onAccent} strokeWidth="2" strokeLinejoin="round"/>
            <circle cx="12" cy="13" r="4" stroke={theme.onAccent} strokeWidth="2"/>
          </svg>
        </div>
      </button>
      {photo && (
        <button
          onClick={removePhoto}
          style={{
            position: 'absolute', top: -6, right: -6,
            width: 22, height: 22, borderRadius: 99,
            background: '#0A0A0A', color: '#fff', border: `2px solid ${theme.bg}`,
            cursor: 'pointer', padding: 0,
            display: 'flex', alignItems: 'center', justifyContent: 'center',
          }}>
          <svg width="10" height="10" viewBox="0 0 14 14" fill="none">
            <path d="M2 2l10 10M12 2L2 12" stroke="#fff" strokeWidth="2.2" strokeLinecap="round"/>
          </svg>
        </button>
      )}
    </div>
  );
}

function ProfileScreen({ theme, copy, logged, onToggleToday, onOpenReview, currentPlanId, planExpiresAt, planActive, onOpenPlans, onOpenMessages, onOpenSettings, onOpenPayment, onOpenBooking, onOpenClassBooking, onOpenSessions, onOpenEvaluations, onLogOut, unreadCount = 0, followingCount = 0 }) {
  const ProReviewCTA = window.LAUNCH_SCREENS_3 && window.LAUNCH_SCREENS_3.ProReviewCTA;
  const ProfilePlanCard = window.LAUNCH_SCREENS_4 && window.LAUNCH_SCREENS_4.ProfilePlanCard;
  const user = window.LAUNCH_USER.useUser();
  return (
    <div style={{ paddingBottom: 110 }}>
      <TopBar theme={theme}
      left={
      <div style={{ fontFamily: theme.fontMono, fontSize: 10, color: theme.muted, letterSpacing: 1, fontWeight: 600 }}>
            @{user.handle.toUpperCase()}
          </div>
      }
      right={<>
          {onLogOut && (
            <button onClick={onLogOut} title="Log out" aria-label="Log out" style={{
              height: 38, padding: '0 12px', borderRadius: 12,
              background: theme.surface, border: `1px solid ${theme.border}`,
              display: 'inline-flex', alignItems: 'center', gap: 6,
              cursor: 'pointer',
              fontFamily: theme.fontMono, fontSize: 10, fontWeight: 700,
              letterSpacing: 1, color: theme.text, textTransform: 'uppercase',
            }}>
              <svg width="16" height="16" viewBox="0 0 24 24" fill="none">
                <path d="M14 3h4a2 2 0 012 2v14a2 2 0 01-2 2h-4" stroke={theme.text} strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/>
                <path d="M9 12h11M16 8l4 4-4 4" stroke={theme.text} strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/>
              </svg>
              LOG OUT
            </button>
          )}
          <button onClick={onOpenSettings} style={{
            width: 38, height: 38, borderRadius: 12,
            background: theme.surface, border: `1px solid ${theme.border}`,
            display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
            cursor: 'pointer', padding: 0,
          }}>
            <svg width="20" height="20" viewBox="0 0 24 24" fill="none">
              <circle cx="12" cy="12" r="3" stroke={theme.text} strokeWidth="2" />
              <path d="M19 12a7 7 0 00-.1-1.2l2-1.5-2-3.5-2.4.8a7 7 0 00-2-1.2L14 3h-4l-.5 2.4c-.7.3-1.4.7-2 1.2l-2.4-.8-2 3.5 2 1.5A7 7 0 005 12c0 .4 0 .8.1 1.2l-2 1.5 2 3.5 2.4-.8c.6.5 1.3.9 2 1.2L10 21h4l.5-2.4c.7-.3 1.4-.7 2-1.2l2.4.8 2-3.5-2-1.5c.1-.4.1-.8.1-1.2z" stroke={theme.text} strokeWidth="2" strokeLinejoin="round" />
            </svg>
          </button>
        </>} />
      

      {/* Identity block */}
      <div style={{ padding: `0 ${theme.pad}px` }}>
        <div style={{ display: 'flex', alignItems: 'flex-end', gap: 14, marginBottom: 14 }}>
          <ProfileAvatar theme={theme} initials={user.initials} color={user.color} photo={user.avatarUrl} />
          <div style={{ flex: 1, minWidth: 0 }}>
            <div style={{
              fontFamily: theme.fontMono, fontSize: 10, color: theme.accent,
              letterSpacing: 1, fontWeight: 700
            }}>{user.tier}</div>
            <div style={{
              fontFamily: theme.fontDisplay, fontSize: 34, color: theme.text,
              letterSpacing: 0.2, lineHeight: 0.95, marginTop: 2,
              textTransform: 'uppercase'
            }}>{user.firstName}{user.lastName ? <><br />{user.lastName}</> : null}</div>
            <div style={{
              fontFamily: theme.fontMono, fontSize: 10, color: theme.muted,
              letterSpacing: 0.5, marginTop: 4
            }}>{user.tierSub}</div>
          </div>
        </div>
        <div style={{
          fontFamily: theme.fontBody, fontSize: 14, color: theme.text,
          lineHeight: 1.45
        }}>
          {user.bio}
        </div>

        {/* Follower / messages strip */}
        <div style={{
          display: 'flex', alignItems: 'center', gap: 8,
          marginTop: 14, paddingTop: 14,
          borderTop: `1px solid ${theme.border}`,
        }}>
          <div style={{ display: 'flex', gap: 16, flex: 1 }}>
            <div>
              <div style={{ fontFamily: theme.fontDisplay, fontSize: 22, color: theme.text, lineHeight: 1, letterSpacing: -0.2 }}>
                {user.followers.toLocaleString()}
              </div>
              <div style={{ fontFamily: theme.fontMono, fontSize: 9, color: theme.muted, letterSpacing: 0.8, fontWeight: 600, marginTop: 3 }}>
                FOLLOWERS
              </div>
            </div>
            <div>
              <div style={{ fontFamily: theme.fontDisplay, fontSize: 22, color: theme.text, lineHeight: 1, letterSpacing: -0.2 }}>
                {followingCount || 47}
              </div>
              <div style={{ fontFamily: theme.fontMono, fontSize: 9, color: theme.muted, letterSpacing: 0.8, fontWeight: 600, marginTop: 3 }}>
                FOLLOWING
              </div>
            </div>
          </div>
          {onOpenMessages && (
            <button onClick={onOpenMessages} style={{
              position: 'relative',
              background: theme.accent, color: theme.onAccent,
              border: 'none', borderRadius: 12,
              padding: '10px 14px', cursor: 'pointer',
              fontFamily: theme.fontDisplay, fontSize: 13, letterSpacing: 0.8,
              textTransform: 'uppercase',
              display: 'inline-flex', alignItems: 'center', gap: 6,
            }}>
              <svg width="14" height="14" viewBox="0 0 24 24" fill="none">
                <path d="M4 5h16v11h-9l-4 4v-4H4V5z" stroke={theme.onAccent} strokeWidth="2.2" strokeLinejoin="round"/>
              </svg>
              MESSAGES
              {unreadCount > 0 && (
                <span style={{
                  background: theme.bg, color: theme.text,
                  fontFamily: theme.fontMono, fontSize: 10, fontWeight: 700,
                  padding: '2px 6px', borderRadius: 99, marginLeft: 2,
                }}>{unreadCount}</span>
              )}
            </button>
          )}
        </div>
      </div>

      {/* Plan card */}
      {ProfilePlanCard && onOpenPlans &&
      <div style={{ padding: `${theme.gap + 4}px ${theme.pad}px 0` }}>
          <div style={{
          fontFamily: theme.fontMono, fontSize: 11, color: theme.muted,
          letterSpacing: 1, fontWeight: 600, marginBottom: 10
        }}>PLAN</div>
          <ProfilePlanCard theme={theme} currentPlanId={currentPlanId} expiresAt={planExpiresAt} active={planActive} onOpen={onOpenPlans} />
          {currentPlanId === 'vip' && planActive && onOpenClassBooking &&
          <button onClick={onOpenClassBooking} style={{
            width: '100%', marginTop: 10, padding: '13px 16px', borderRadius: 12, border: 'none',
            background: theme.surface, boxShadow: `inset 0 0 0 1px ${theme.border}`, color: theme.text,
            cursor: 'pointer', display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 8,
            fontFamily: theme.fontDisplay, fontSize: 13, letterSpacing: 0.8, textTransform: 'uppercase',
          }}>
            🎓 Book your free 20-min class
          </button>
          }
        </div>
      }

      {/* Habit tracker — full version */}
      <div style={{ padding: `${theme.gap + 4}px ${theme.pad}px 0` }}>
        <HabitTracker theme={theme} logged={logged} onToggleToday={onToggleToday} />
      </div>

      {/* Stats grid */}
      <div style={{
        padding: `${theme.gap}px ${theme.pad}px 0`,
        display: 'grid', gridTemplateColumns: 'repeat(2, 1fr)', gap: 8
      }}>
        {[
        { n: String(user.streak), l: 'STREAK', sub: 'CONSECUTIVE DAYS' },
        { n: String(user.reviewsReceived), l: 'REVIEWS', sub: 'RECEIVED' },
        { n: String(user.sessionsTotal), l: 'SESSIONS', sub: 'WITH MENTORS' },
        { n: String(user.releases), l: 'RELEASES', sub: 'OUT IN THE WILD' }].
        map((s, i) =>
        <Card key={i} theme={theme} style={{ padding: 14 }}>
            <div style={{ fontFamily: theme.fontDisplay, fontSize: 36, color: theme.text, lineHeight: 1, letterSpacing: -0.5 }}>
              {s.n}
            </div>
            <div style={{
            fontFamily: theme.fontDisplay, fontSize: 14, color: theme.text,
            letterSpacing: 0.4, marginTop: 6, textTransform: 'uppercase'
          }}>{s.l}</div>
            <div style={{
            fontFamily: theme.fontMono, fontSize: 9, color: theme.muted,
            letterSpacing: 0.8, marginTop: 2, fontWeight: 600
          }}>{s.sub}</div>
          </Card>
        )}
      </div>

      {/* Releases */}
      <div style={{ padding: `${theme.gap + 6}px ${theme.pad}px 0` }}>
        <div style={{
          display: 'flex', alignItems: 'baseline', justifyContent: 'space-between',
          marginBottom: 10
        }}>
          <span style={{ fontFamily: theme.fontMono, fontSize: 11, color: theme.muted, letterSpacing: 1, fontWeight: 600 }}>
            RELEASES
          </span>
          <span style={{ fontFamily: theme.fontMono, fontSize: 10, color: theme.accent, letterSpacing: 0.5, fontWeight: 700 }}>
            {user.releases} OUT · {user.upcoming} UPCOMING
          </span>
        </div>
        <div style={{
          display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: 8
        }}>
          {[
          { lbl: 'WL', pal: ['#C72820', '#7A5AE0'], t: 'WINTER LIGHT' },
          { lbl: 'BD', pal: ['#CAFF33', '#C72820'], t: 'BLUE DOORS' },
          { lbl: 'HS', pal: ['#FBBF24', '#0A0A0A'], t: 'HALF SLEEP' },
          { lbl: 'OR', pal: ['#7A5AE0', '#FBBF24'], t: 'ORBIT' },
          { lbl: '??', pal: ['#1F1F1F', '#1F1F1F'], t: 'UNTITLED', upcoming: true },
          { lbl: '??', pal: ['#1F1F1F', '#1F1F1F'], t: 'UNTITLED', upcoming: true }].
          map((r, i) =>
          <div key={i} style={{ position: 'relative' }}>
              <CoverArt palette={r.pal} label={r.lbl} size={104} radius={10} />
              {r.upcoming &&
            <div style={{
              position: 'absolute', inset: 0, borderRadius: 10,
              border: `1.5px dashed ${theme.faint}`,
              display: 'flex', alignItems: 'center', justifyContent: 'center',
              background: 'rgba(0,0,0,0.4)'
            }}>
                  <span style={{
                fontFamily: theme.fontMono, fontSize: 10, color: theme.muted,
                letterSpacing: 1, fontWeight: 700
              }}>SOON</span>
                </div>
            }
              <div style={{
              fontFamily: theme.fontMono, fontSize: 9, color: r.upcoming ? theme.faint : theme.text,
              letterSpacing: 0.5, fontWeight: 600, marginTop: 6
            }}>{r.t}</div>
            </div>
          )}
        </div>
      </div>

      {/* Pro review CTA */}
      {ProReviewCTA && onOpenReview &&
      <div style={{ padding: `${theme.gap + 6}px ${theme.pad}px 0` }}>
          <ProReviewCTA theme={theme} onOpen={onOpenReview} />
        </div>
      }

      {/* Sign out */}
      <div style={{ padding: `${theme.gap + 6}px ${theme.pad}px 0` }}>
        <button
          onClick={() => {
            const done = () => window.location.reload();
            try {
              const r = window.LAUNCH_USER.signOut();
              if (r && typeof r.then === 'function') { r.then(done); return; }
            } catch (e) {}
            done();
          }}
          style={{
            width: '100%', padding: '14px 16px', borderRadius: 14,
            background: 'transparent', border: `1px solid ${theme.border}`,
            color: theme.muted, cursor: 'pointer',
            fontFamily: theme.fontMono, fontSize: 11, fontWeight: 700,
            letterSpacing: 1, textTransform: 'uppercase',
            display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 8,
          }}>
          <svg width="14" height="14" viewBox="0 0 24 24" fill="none">
            <path d="M15 4h4a2 2 0 012 2v12a2 2 0 01-2 2h-4M10 8l-4 4 4 4M6 12h12" stroke={theme.muted} strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/>
          </svg>
          SIGN OUT
        </button>
      </div>

      {/* My booked sessions — entry point */}
      {onOpenSessions && (
        <div style={{ padding: `${theme.gap + 6}px ${theme.pad}px 0` }}>
          <button onClick={onOpenSessions} style={{
            width: '100%', padding: '14px 16px', borderRadius: 14,
            background: theme.surface, border: `1px solid ${theme.border}`,
            display: 'flex', alignItems: 'center', gap: 12, cursor: 'pointer',
          }}>
            <span style={{ fontSize: 18 }}>📅</span>
            <div style={{ flex: 1, textAlign: 'left' }}>
              <div style={{ fontFamily: theme.fontDisplay, fontSize: 15, color: theme.text, letterSpacing: 0.3, textTransform: 'uppercase' }}>
                MY SESSIONS
              </div>
              <div style={{ fontFamily: theme.fontMono, fontSize: 9, color: theme.muted, letterSpacing: 0.5, marginTop: 2 }}>
                BOOKED 1:1s · GOOGLE MEET LINKS
              </div>
            </div>
            <span style={{ fontFamily: theme.fontDisplay, fontSize: 16, color: theme.accent }}>→</span>
          </button>
        </div>
      )}

      {/* My reviews (coach feedback) — entry point */}
      {onOpenEvaluations && (
        <div style={{ padding: `${theme.gap}px ${theme.pad}px 0` }}>
          <button onClick={onOpenEvaluations} style={{
            width: '100%', padding: '14px 16px', borderRadius: 14,
            background: theme.surface, border: `1px solid ${theme.border}`,
            display: 'flex', alignItems: 'center', gap: 12, cursor: 'pointer',
          }}>
            <span style={{ fontSize: 18 }}>📝</span>
            <div style={{ flex: 1, textAlign: 'left' }}>
              <div style={{ fontFamily: theme.fontDisplay, fontSize: 15, color: theme.text, letterSpacing: 0.3, textTransform: 'uppercase' }}>
                MY REVIEWS
              </div>
              <div style={{ fontFamily: theme.fontMono, fontSize: 9, color: theme.muted, letterSpacing: 0.5, marginTop: 2 }}>
                COACH FEEDBACK · SCORES & NOTES
              </div>
            </div>
            <span style={{ fontFamily: theme.fontDisplay, fontSize: 16, color: theme.accent }}>→</span>
          </button>
        </div>
      )}

      {/* Mentors */}
      <div style={{ padding: `${theme.gap + 6}px ${theme.pad}px 0` }}>
        {window.LAUNCH_MENTORS && <window.LAUNCH_MENTORS.MentorsBlock theme={theme} onBookSession={onOpenBooking || onOpenPayment} />}
      </div>

      {/* (legacy mentor list — hidden) */}
      <div style={{ display: 'none' }}>
        <Card theme={theme} style={{ padding: 4 }}>
          {[
          { lbl: 'NK', name: 'Noah Kessler', role: 'Production', color: '#7A5AE0' },
          { lbl: 'JX', name: 'Jules Xavier', role: 'Sound design', color: '#C72820' },
          { lbl: 'AT', name: 'Aria Tao', role: 'Songwriting', color: '#CAFF33' }].
          map((m, i, arr) =>
          <div key={i} style={{
            padding: '12px', display: 'flex', alignItems: 'center', gap: 12,
            borderBottom: i < arr.length - 1 ? `1px solid ${theme.border}` : 'none'
          }}>
              <div style={{
              width: 40, height: 40, borderRadius: 99, background: m.color,
              display: 'flex', alignItems: 'center', justifyContent: 'center',
              fontFamily: theme.fontDisplay, color: '#0A0A0A', fontSize: 14
            }}>{m.lbl}</div>
              <div style={{ flex: 1 }}>
                <div style={{ fontFamily: theme.fontBody, fontSize: 14, color: theme.text, fontWeight: 600 }}>
                  {m.name}
                </div>
                <div style={{ fontFamily: theme.fontMono, fontSize: 10, color: theme.muted, letterSpacing: 0.5 }}>
                  {m.role.toUpperCase()}
                </div>
              </div>
              <button style={{
              background: 'transparent', border: `1px solid ${theme.border}`,
              color: theme.text, padding: '6px 10px', borderRadius: 99,
              fontFamily: theme.fontMono, fontSize: 10, fontWeight: 700, letterSpacing: 0.8,
              cursor: 'pointer', textTransform: 'uppercase'
            }}>BOOK</button>
            </div>
          )}
        </Card>
      </div>
    </div>);

}

window.LAUNCH_SCREENS_2 = { RoadmapScreen, FeedScreen, ProfileScreen, HabitTracker };