// ───────────────────────────────────────────────────────────── // protected/mobile/mobile.js — minimal client for the mobile view. // // Two behaviours, nothing else: // 1. Confirm the session is still valid on page load. If the // session expired since the server rendered the HTML, bounce // to "/" so the user doesn't read gated content without a // session cookie (defensive — requireAuth already gates the // page request itself). // 2. POST /api/bifrost-join on CTA click; swap CTA panel → // confirmation panel on success. // // There is no logout button on the mobile view; the masthead is // logo-only by design. Users who want to log out can do so from a // desktop session, or by clearing cookies. // // No GSAP, no Lenis, no d3. No sharing of globals with the desktop // timeline/bifrost scripts — this file is only loaded by // protected/mobile/index.html and never by the desktop view. // ───────────────────────────────────────────────────────────── (async function checkSession() { try { const res = await fetch('/auth/me', { credentials: 'same-origin' }); if (!res.ok) { window.location.href = '/'; } } catch { // Network error — do not boot the user out; desktop behaviour is // the same. If the next action actually needs the server, we'll // surface the error there. } })(); const joinBtn = document.getElementById('m-join-btn'); const joinCta = document.getElementById('m-join-cta'); const joinConfirm = document.getElementById('m-join-confirm'); if (joinBtn && joinCta && joinConfirm) { joinBtn.addEventListener('click', async () => { joinBtn.disabled = true; try { const res = await fetch('/api/bifrost-join', { method: 'POST', credentials: 'same-origin', headers: { 'content-type': 'application/json' }, // Server reads email + sessionId from the session cookie, body // just needs to be parseable JSON for express.json() to keep // its rhythm. body: '{}', }); if (res.status === 401) { window.location.href = '/'; return; } if (!res.ok) { joinBtn.disabled = false; joinBtn.textContent = 'Try again'; return; } joinCta.hidden = true; joinConfirm.hidden = false; joinConfirm.scrollIntoView({ behavior: 'smooth', block: 'start' }); } catch { joinBtn.disabled = false; joinBtn.textContent = 'Try again'; } }); }