// app.jsx — LAUNCH app shell: tab nav, theme, tweaks wiring
const { useState: useStateApp } = React;
const S1 = window.LAUNCH_SCREENS_1;
const S2 = window.LAUNCH_SCREENS_2;
const { HomeScreen, LessonScreen, useTheme, COPY, TabBar } = S1;
const { RoadmapScreen, FeedScreen, ProfileScreen } = S2;
const S3 = window.LAUNCH_SCREENS_3;
const { ReviewScreen, ProReviewCTA } = S3;
const S4 = window.LAUNCH_SCREENS_4;
const { PlansScreen } = S4;
const S5 = window.LAUNCH_SCREENS_5;
const { MessagesScreen, ThreadScreen, MemberProfileScreen, THREADS: INITIAL_THREADS, MEMBERS: DEMO_MEMBERS } = S5;
const S6 = window.LAUNCH_SCREENS_6;
const { LeaderboardScreen } = S6;
const S7 = window.LAUNCH_SCREENS_7;
const { OnboardingFlow } = S7;
const S8 = window.LAUNCH_SCREENS_8;
const { SettingsScreen, EditProfileScreen } = S8;
const S9 = window.LAUNCH_SCREENS_9;
const { CreateScreen } = S9;
const S10 = window.LAUNCH_SCREENS_10;
const { MyEvaluationsScreen } = S10;
const { PaymentScreen, AdminDashboard } = window.LAUNCH_PAYMENT;
const { BookingScreen, MySessionsScreen } = window.LAUNCH_BOOKING;
const { CoachQueueScreen } = window.LAUNCH_REVIEW_QUEUE;

const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
  "accent": "#C72820",
  "fontPair": "anton-grotesk",
  "dark": true,
  "density": "regular",
  "tone": "hype"
}/*EDITMODE-END*/;

const FONT_PAIRS = {
  'anton-grotesk':  { display: 'Anton, "Archivo Black", system-ui', body: '"Space Grotesk", system-ui, sans-serif' },
  'archivo-inter':  { display: '"Archivo Black", system-ui',         body: '"DM Sans", system-ui, sans-serif' },
  'serif-mono':     { display: '"Fraunces", Georgia, serif',         body: '"IBM Plex Mono", monospace' },
  'condensed-sans': { display: '"Bebas Neue", "Anton", system-ui',   body: '"Manrope", system-ui, sans-serif' },
};

const ACCENT_OPTIONS = [
  '#C72820', // mars
  '#4A7FD8', // neptune
  '#7A5AE0', // nebula
  '#E8A736', // solar
];

function App() {
  const [t, setTweak] = useTweaks(TWEAK_DEFAULTS);
  const [tab, setTab] = useStateApp('home');
  const [onboarding, setOnboarding] = useStateApp(() => {
    // Only show onboarding on a fresh tab session; refreshes within a session skip it.
    try {
      return sessionStorage.getItem('launch.appOpened') !== '1';
    } catch (e) { return true; }
  });
  React.useEffect(() => {
    try { sessionStorage.setItem('launch.appOpened', '1'); } catch (e) {}
  }, []);
  // Returning from Stripe Checkout: ?checkout=success&session_id=… → verify the
  // payment server-side, then land the user on their reviews. Always strip the
  // query string afterwards so a refresh doesn't re-trigger it.
  React.useEffect(() => {
    let params;
    try { params = new URLSearchParams(window.location.search); } catch (e) { return; }
    const co = params.get('checkout');
    if (!co) return;
    if (co === 'success') {
      const sid = params.get('session_id');
      setOnboarding(false);
      setTab('home');
      // Verify server-side. If it was a 1:1 session, the booking is now scheduled
      // (Google event created) — land the user on My Sessions to see the Meet link.
      if (sid) {
        window.LAUNCH_USER.confirmCheckout(sid).then((r) => {
          if (r && r.bookingId) setTab('sessions');
        });
      }
    }
    try { window.history.replaceState({}, '', window.location.origin + window.location.pathname); } catch (e) {}
  }, []);
  // Returning from a Google / Apple OAuth redirect: the provider bounces the
  // browser back here with a session in the URL. Finish sign-in (seed the
  // profile from the provider identity), skip onboarding, land on home, then
  // strip the OAuth tokens from the URL so a refresh doesn't re-trigger it.
  React.useEffect(() => {
    let pending = false;
    try { pending = !!localStorage.getItem('launch.oauthPending'); } catch (e) {}
    if (!pending) return;
    try { localStorage.removeItem('launch.oauthPending'); } catch (e) {}
    window.LAUNCH_USER.completeOAuthSignIn().then((res) => {
      if (res && res.signedIn) {
        setOnboarding(false);
        setTab('home');
      }
      try { window.history.replaceState({}, '', window.location.origin + window.location.pathname); } catch (e) {}
    });
  }, []);
  const [loggedToday, setLoggedToday] = useStateApp(true);
  const [currentPlan, setCurrentPlan] = useStateApp('orbit');
  const liveMode = window.LAUNCH_USER.isConfigured();
  const [following, setFollowing] = useStateApp(liveMode ? [] : ['sasha.vee', 'kai.park']);
  const [threads, setThreads] = useStateApp(liveMode ? [] : INITIAL_THREADS);
  const [activeThread, setActiveThread] = useStateApp(null);
  const [activeMember, setActiveMember] = useStateApp(null);
  // Live chat: member directory (handle→member) + online handles (presence).
  // Null/empty until loaded; in demo mode we fall back to the sample directory.
  const [liveMembers, setLiveMembers] = useStateApp(null);
  const [online, setOnline] = useStateApp(() => new Set());
  const [onlineCount, setOnlineCount] = useStateApp(0);
  // Refs so the realtime subscription (set up once) reads current values.
  const liveMembersRef = React.useRef(null);
  const activeThreadRef = React.useRef(null);
  React.useEffect(() => { liveMembersRef.current = liveMembers; }, [liveMembers]);
  React.useEffect(() => { activeThreadRef.current = activeThread; }, [activeThread]);

  // Load the live chat backend once: directory, existing threads, realtime
  // inbox subscription, and presence. All no-ops in demo mode.
  React.useEffect(() => {
    const U = window.LAUNCH_USER;
    if (!U.isConfigured()) return;
    let alive = true;
    U.fetchMembersDirectory().then(dir => { if (alive && dir) setLiveMembers(dir); });
    U.fetchThreads().then(ths => { if (alive && ths) setThreads(ths); });
    U.fetchFollowing().then(f => { if (alive && f) setFollowing(f); });
    const onIncoming = (m) => {
      const dir = liveMembersRef.current || {};
      let entry = Object.values(dir).find(x => x.id === m.sender_id);
      if (!entry) { U.fetchMembersDirectory().then(d => { if (alive && d) setLiveMembers(d); }); }
      const handle = entry ? entry.handle : m.sender_id;
      const msg = { from: 'them', t: m.body, ts: 'now' };
      setThreads(prev => {
        const exists = prev.find(t => t.id === handle);
        if (exists) {
          return prev.map(t => t.id === handle
            ? { ...t, messages: [...t.messages, msg], lastTime: 'NOW',
                unread: activeThreadRef.current === handle ? 0 : (t.unread + 1) }
            : t);
        }
        return [{ id: handle, member: handle, otherId: m.sender_id, unread: 1, lastTime: 'NOW', messages: [msg] }, ...prev];
      });
    };
    const unsub = U.subscribeMessages(onIncoming);
    const leave = U.joinPresence((ids, count) => { if (alive) { setOnline(new Set(ids)); setOnlineCount(count || 0); } });
    return () => { alive = false; try { unsub(); } catch (e) {} try { leave(); } catch (e) {} };
  }, []);

  // The resolved member directory passed to every social screen: live profiles
  // with presence merged in, or the demo directory when there's no backend.
  const members = React.useMemo(() => {
    if (!liveMode) return DEMO_MEMBERS;
    const src = liveMembers || {};
    const out = {};
    Object.keys(src).forEach(h => { const m = src[h]; out[h] = { ...m, online: online.has(h) || (m.id && online.has(m.id)) }; });
    return out;
  }, [liveMode, liveMembers, online]);

  const toggleFollow = (handle) => {
    if (!canInteract) { openPlans(); return; }   // no plan → can't follow
    const isFollowing = following.includes(handle);
    setFollowing(prev => isFollowing ? prev.filter(h => h !== handle) : [...prev, handle]);
    if (liveMode) {
      const target = (liveMembers && liveMembers[handle]) ? liveMembers[handle].id : null;
      if (target) {
        if (isFollowing) window.LAUNCH_USER.unfollowUser(target);
        else window.LAUNCH_USER.followUser(target);
        // Optimistically reflect the follower count on the directory so the
        // member profile updates immediately (DB has the real count for next load).
        setLiveMembers(prev => {
          if (!prev || !prev[handle]) return prev;
          const delta = isFollowing ? -1 : 1;
          return { ...prev, [handle]: { ...prev[handle], followers: Math.max(0, (prev[handle].followers || 0) + delta) } };
        });
      }
    }
  };
  const sendMessage = (threadId, text) => {
    if (!canInteract) { openPlans(); return; }   // no plan → can't chat
    setThreads(prev => prev.map(th =>
      th.id === threadId
        ? { ...th, messages: [...th.messages, { from: 'me', t: text, ts: 'now' }], lastTime: 'NOW', unread: 0 }
        : th
    ));
    if (liveMode) {
      const th = threads.find(t => t.id === threadId);
      const otherId = (th && th.otherId) || (liveMembers && liveMembers[threadId] && liveMembers[threadId].id);
      if (otherId) window.LAUNCH_USER.sendMessageLive(otherId, text);
    }
  };
  const openMessages = () => { setTab('messages'); setActiveThread(null); };
  const openLeaderboard = () => setTab('leaderboard');
  const openSettings = () => setTab('settings');
  const openThread = (id) => {
    setThreads(prev => prev.map(th => th.id === id ? { ...th, unread: 0 } : th));
    setActiveThread(id);
    setTab('thread');
    if (liveMode) {
      const th = threads.find(t => t.id === id);
      if (th && th.otherId) window.LAUNCH_USER.markThreadRead(th.otherId);
    }
  };
  const openMember = (handle) => {
    // Tapping your OWN avatar → go to your Profile tab. You're excluded from the
    // members directory, so MemberProfileScreen would otherwise render blank.
    const mh = me && me.handle;
    if (handle && mh && String(handle).toLowerCase() === String(mh).toLowerCase()) { setTab('profile'); return; }
    setActiveMember(handle); setTab('member');
  };
  const startThreadWith = (handle) => {
    if (!canInteract) { openPlans(); return; }   // no plan → can't start a chat
    if (!threads.find(t => t.id === handle)) {
      const otherId = liveMembers && liveMembers[handle] ? liveMembers[handle].id : undefined;
      setThreads(prev => [
        { id: handle, member: handle, otherId, unread: 0, lastTime: 'NOW', messages: [] },
        ...prev,
      ]);
    }
    openThread(handle);
  };

  const fonts = FONT_PAIRS[t.fontPair] || FONT_PAIRS['anton-grotesk'];
  const theme = useTheme({
    accent: t.accent,
    dark: t.dark,
    density: t.density,
    fontDisplay: fonts.display,
    fontBody: fonts.body,
  });
  const copy = COPY[t.tone] || COPY.hype;

  const toggleToday = () => setLoggedToday(v => !v);

  const openReview = () => setTab('review');
  const openEvaluations = () => setTab('evaluations');
  const openPlans = () => setTab('plans');
  const [payItem, setPayItem] = useStateApp('mentor-jay');
  const [payBookingId, setPayBookingId] = useStateApp(null);
  const openPayment = (item, bookingId) => {
    const it = item || 'mentor-jay';
    // Google Play / App Store policy: digital memberships can't be charged
    // through Stripe inside the native app. On native we never open Stripe
    // checkout for a subscription — we send the user to the Plans screen, where
    // they buy through the store instead. (1:1 sessions & evaluations are
    // real-world services and stay allowed in-app.)
    if ((it === 'sub-basic' || it === 'sub-vip')
        && window.LAUNCH_USER.isNativeApp()) {
      setTab('plans');
      return;
    }
    setPayItem(it);
    setPayBookingId(bookingId || null);
    setTab('payment');
  };
  const [bookingMentor, setBookingMentor] = useStateApp('mentor-jay');
  const [bookingClass, setBookingClass] = useStateApp(false);
  const openBooking = (mentorItem) => {
    setBookingClass(false);
    setBookingMentor(mentorItem || 'mentor-jay');
    setTab('booking');
  };
  // VIP: book a free 20-min class with a coach.
  const openClassBooking = () => {
    setBookingClass(true);
    setBookingMentor('mentor-jay');
    setTab('booking');
  };
  const openAdmin = () => setTab('admin');
  const openCoachQueue = () => setTab('coachQueue');
  const openSessions = () => setTab('sessions');
  // Who can see the coach review queue. In live mode it's gated to coach/admin
  // roles; in demo mode (no backend) it's always reachable so the flow can be
  // tested end-to-end in the preview.
  const me = window.LAUNCH_USER.useUser();
  const plan = window.LAUNCH_USER.usePlan();
  // Only an active paid plan (basic/vip) can interact with anything social.
  const canInteract = !!(plan && plan.active);
  // Subscribe flow: 'free' clears the plan; basic/vip route through payment → activate.
  // Buying a membership. Inside the native app it has to go through the App
  // Store / Play Store (guideline 3.1.1); on the web it goes through Stripe.
  // Returns a result so the Plans screen can show progress and errors.
  const handleSubscribe = async (planId) => {
    if (planId === 'free') { await window.LAUNCH_USER.subscribe('free'); return { ok: true }; }
    if (window.LAUNCH_USER.canBuyInApp()) {
      return await window.LAUNCH_USER.purchasePlanInApp(planId);
    }
    if (window.LAUNCH_USER.isNativeApp()) {
      // Native build without store billing available — we can't sell here and
      // must not point the user to the website to buy.
      return { error: 'Memberships are unavailable in the app right now. Please try again later.' };
    }
    openPayment('sub-' + planId);
    return { ok: true, redirected: true };
  };
  const coachAccess = !window.LAUNCH_USER.isConfigured() || ['coach', 'admin'].includes(me.role);
  // Sign out → clear the session and drop back into onboarding so a new
  // account can be created / signed in.
  const logOut = async () => {
    try { await window.LAUNCH_USER.signOut(); } catch (e) {}
    setTab('home');
    setOnboarding(true);
  };
  const screens = {
    home:    <HomeScreen     theme={theme} copy={copy} onOpenLesson={() => setTab('lesson')}
                             logged={loggedToday} onToggleToday={toggleToday}
                             onOpenReview={openReview}
                             onOpenMessages={openMessages}
                             onOpenLeaderboard={openLeaderboard}
                             onOpenCoachQueue={coachAccess ? openCoachQueue : null}
                             onOpenEvaluations={openEvaluations}
                             onOpenPlans={openPlans}
                             unreadCount={threads.reduce((a,t)=>a+t.unread, 0)} />,
    lesson:  <LessonScreen   theme={theme} copy={copy} onBack={() => setTab('home')}
                             onOpenReview={openReview} />,
    feed:    <FeedScreen     theme={theme} copy={copy} onOpenReview={openReview}
                             onOpenMember={openMember} members={members}
                             online={online} onlineCount={onlineCount} liveMode={liveMode}
                             following={following} onToggleFollow={toggleFollow}
                             canInteract={canInteract} onNeedPlan={openPlans} />,
    create:  <CreateScreen   theme={theme} copy={copy} />,
    profile: <ProfileScreen  theme={theme} copy={copy}
                             logged={loggedToday} onToggleToday={toggleToday}
                             onOpenReview={openReview}
                             currentPlanId={plan.plan} planExpiresAt={plan.expiresAt} planActive={plan.active}
                             onOpenPlans={openPlans}
                             onOpenMessages={openMessages}
                             onOpenSettings={openSettings}
                             onOpenPayment={openPayment}
                             onOpenBooking={openBooking}
                             onOpenClassBooking={openClassBooking}
                             onOpenSessions={openSessions}
                             onOpenEvaluations={openEvaluations}
                             onLogOut={logOut}
                             unreadCount={threads.reduce((a,t)=>a+t.unread, 0)}
                             followingCount={following.length} />,
    review:  <ReviewScreen   theme={theme} copy={copy} onBack={() => setTab('home')}
                             onOpenEvaluations={openEvaluations} />,
    evaluations: <MyEvaluationsScreen theme={theme} onBack={() => setTab('review')} />,
    plans:   <PlansScreen    theme={theme} currentPlanId={plan.plan}
                             onBack={() => setTab('profile')} onSubscribe={handleSubscribe} />,
    messages: <MessagesScreen theme={theme} threads={threads} members={members}
                              onBack={() => setTab('home')}
                              onOpenThread={openThread}
                              onOpenMember={openMember}
                              following={following} onToggleFollow={toggleFollow} />,
    thread:   <ThreadScreen   theme={theme} threadId={activeThread} threads={threads} members={members}
                              onBack={() => setTab('messages')}
                              onSend={sendMessage}
                              onOpenMember={openMember}
                              following={following} onToggleFollow={toggleFollow} />,
    member:   <MemberProfileScreen theme={theme} handle={activeMember} members={members}
                              onBack={() => setTab(threads.find(t=>t.id===activeMember) ? 'messages' : 'feed')}
                              following={following} onToggleFollow={toggleFollow}
                              onMessage={startThreadWith} />,
    leaderboard: <LeaderboardScreen theme={theme} onBack={() => setTab('home')} />,
    settings: <SettingsScreen theme={theme}
                              onBack={() => setTab('profile')}
                              dark={t.dark} onChangeDark={(v) => setTweak('dark', v)}
                              accent={t.accent} onChangeAccent={(v) => setTweak('accent', v)}
                              onEditProfile={() => setTab('editProfile')}
                              onOpenSubscription={openPlans}
                              onOpenAdmin={openAdmin}
                              onOpenCoachQueue={coachAccess ? openCoachQueue : null} />,
    editProfile: <EditProfileScreen theme={theme}
                              onBack={() => setTab('settings')} />,
    payment: <PaymentScreen theme={theme} item={payItem} bookingId={payBookingId}
                              onBack={() => setTab(payItem.startsWith('sub-') ? 'plans' : payItem.startsWith('mentor') ? 'booking' : 'review')}
                              onSuccess={() => setTab(payItem.startsWith('sub-') ? 'profile' : payItem.startsWith('mentor') ? 'sessions' : 'review')} />,
    booking: <BookingScreen theme={theme} mentorId={bookingMentor} classMode={bookingClass}
                              onBack={() => setTab('profile')}
                              onConfirm={(req) => req.classMode ? setTab('sessions') : openPayment(req.mentorId, req.bookingId)} />,
    sessions: <MySessionsScreen theme={theme}
                              onBack={() => setTab('profile')}
                              onOpenBooking={() => openBooking()} />,
    admin:   <AdminDashboard theme={theme} onBack={() => setTab('settings')} />,
    coachQueue: <CoachQueueScreen theme={theme} onBack={() => setTab('settings')} />,
  };

  // Inject global font for body/display via style tag
  const fontStyle = `body, button { font-family: ${fonts.body}; }`;

  // Which tabs show the bottom tab bar (sub-screens hide it).
  const showTabBar = !['review','plans','messages','thread','member','leaderboard','settings','editProfile','payment','admin','booking','coachQueue','evaluations','sessions'].includes(tab);

  // PRODUCTION = served from a real domain (not localhost / file://). Force with
  // ?prod=1 or ?preview=1. In production we drop the iPhone mockup + the dev
  // tweaks panel and render full-screen + responsive (mobile-first column).
  const PRODUCTION = (() => {
    try {
      const q = new URLSearchParams(window.location.search);
      if (q.get('preview') === '1') return false;
      if (q.get('prod') === '1') return true;
      const h = window.location.hostname;
      return !(window.location.protocol === 'file:' || h === 'localhost' || h === '127.0.0.1');
    } catch (e) { return false; }
  })();

  if (PRODUCTION) {
    return (
      <div style={{
        minHeight: '100vh', width: '100%', display: 'flex', justifyContent: 'center',
        background: t.dark ? '#0A0A0A' : '#DDD4C2',
        backgroundImage: t.dark
          ? 'radial-gradient(circle at 30% 20%, #161618 0, #060607 60%)'
          : 'radial-gradient(circle at 30% 20%, #EDE7DC 0, #C9BFA8 70%)',
      }}>
        <style>{fontStyle}</style>
        <div style={{
          width: '100%', maxWidth: 480, height: '100vh', position: 'relative',
          background: theme.bg, color: theme.text, overflow: 'hidden', fontFamily: fonts.body,
          boxShadow: '0 0 60px rgba(0,0,0,0.4)',
        }}>
          {onboarding ? (
            <div style={{ height: '100%', overflow: 'hidden' }}>
              <OnboardingFlow theme={theme} onDone={() => setOnboarding(false)} />
            </div>
          ) : (
            <>
              <div style={{
                height: '100%', overflowY: 'auto', overflowX: 'hidden', scrollbarWidth: 'none',
              }} className="launch-scroll">
                {screens[tab]}
              </div>
              {showTabBar && <TabBar active={tab} onChange={setTab} theme={theme} />}
            </>
          )}
        </div>
      </div>
    );
  }

  return (
    <div style={{
      minHeight: '100vh', width: '100%',
      display: 'flex', alignItems: 'center', justifyContent: 'center',
      padding: 24, boxSizing: 'border-box',
      background: t.dark ? '#0A0A0A' : '#DDD4C2',
      backgroundImage: t.dark
        ? 'radial-gradient(circle at 30% 20%, #161618 0, #060607 60%)'
        : 'radial-gradient(circle at 30% 20%, #EDE7DC 0, #C9BFA8 70%)',
    }}>
      <style>{fontStyle}</style>

      <div data-screen-label={`01 ${tab}`} style={{
        position: 'relative',
        transition: 'transform .25s ease',
      }}>
        <IOSDevice width={402} height={874} dark={t.dark}>
          {onboarding ? (
            <div style={{ height: '100%', overflow: 'hidden' }}>
              <OnboardingFlow theme={theme} onDone={() => setOnboarding(false)} />
            </div>
          ) : (
            <>
              <div style={{
                height: '100%', background: theme.bg, color: theme.text,
                overflowY: 'auto', overflowX: 'hidden',
                fontFamily: fonts.body,
                scrollbarWidth: 'none',
              }} className="launch-scroll">
                {screens[tab]}
              </div>
              {showTabBar && <TabBar active={tab} onChange={setTab} theme={theme} />}
            </>
          )}
        </IOSDevice>
      </div>

      <TweaksPanel>
        <TweakSection label="Vibe" />
        <TweakColor label="Accent" value={t.accent}
                    options={ACCENT_OPTIONS}
                    onChange={(v) => setTweak('accent', v)} />
        <TweakToggle label="Dark mode" value={t.dark}
                     onChange={(v) => setTweak('dark', v)} />

        <TweakSection label="Typography" />
        <TweakSelect label="Font pair" value={t.fontPair}
                     options={[
                       { value: 'anton-grotesk',  label: 'Anton × Space Grotesk' },
                       { value: 'archivo-inter',  label: 'Archivo Black × DM Sans' },
                       { value: 'condensed-sans', label: 'Bebas × Manrope' },
                       { value: 'serif-mono',     label: 'Fraunces × IBM Plex Mono' },
                     ]}
                     onChange={(v) => setTweak('fontPair', v)} />

        <TweakSection label="Layout" />
        <TweakRadio label="Density" value={t.density}
                    options={['compact', 'regular', 'comfy']}
                    onChange={(v) => setTweak('density', v)} />

        <TweakSection label="Voice" />
        <TweakRadio label="Copy tone" value={t.tone}
                    options={['hype', 'pro']}
                    onChange={(v) => setTweak('tone', v)} />

        <TweakSection label="Onboarding" />
        <div style={{ display: 'flex', gap: 4 }}>
          <button onClick={() => setOnboarding(true)} style={{
            flex: 1, padding: '8px 6px', borderRadius: 6,
            background: onboarding ? '#29261b' : 'rgba(0,0,0,0.05)',
            color: onboarding ? '#fff' : '#29261b',
            border: 0, cursor: 'pointer',
            fontSize: 10, fontWeight: 600,
            fontFamily: 'ui-sans-serif, system-ui',
            textTransform: 'uppercase', letterSpacing: 0.5,
          }}>↻ Replay onboarding</button>
          <button onClick={() => setOnboarding(false)} style={{
            flex: 1, padding: '8px 6px', borderRadius: 6,
            background: !onboarding ? '#29261b' : 'rgba(0,0,0,0.05)',
            color: !onboarding ? '#fff' : '#29261b',
            border: 0, cursor: 'pointer',
            fontSize: 10, fontWeight: 600,
            fontFamily: 'ui-sans-serif, system-ui',
            textTransform: 'uppercase', letterSpacing: 0.5,
          }}>Skip to app</button>
        </div>

        <TweakSection label="Navigate" />
        <div style={{ display: 'grid', gridTemplateColumns: 'repeat(4, 1fr)', gap: 4 }}>
          {['home','review','plans','messages'].map(s => (
            <button key={s} onClick={() => setTab(s)} style={{
              padding: '6px 4px', borderRadius: 6,
              background: tab === s ? '#29261b' : 'rgba(0,0,0,0.05)',
              color: tab === s ? '#fff' : '#29261b',
              border: 0, cursor: 'pointer',
              fontSize: 10, fontWeight: 600,
              fontFamily: 'ui-sans-serif, system-ui',
              textTransform: 'uppercase', letterSpacing: 0.5,
            }}>{s.slice(0,4)}</button>
          ))}
        </div>
      </TweaksPanel>
    </div>
  );
}

ReactDOM.createRoot(document.getElementById('root')).render(<App />);
