diff --git a/protected/index.html b/protected/index.html index 3e96bba..1b796d2 100644 --- a/protected/index.html +++ b/protected/index.html @@ -4,11 +4,13 @@ A Catalog of Sovereignty — 2022–2026 - - - - + + + + - + + +
@@ -237,6 +345,31 @@ + +
+

Welcome.

+

+ Thank you for joining and for your interest in enabling sovereign AI + in Denmark and Europe. Project Bifrost is a deliberate effort to + advance it — the conviction that how we build these systems, + and where, will shape the next decades. +

+

+ What follows is a timeline: twenty-three moments that explain why + this matters now, and what the path looks like. +

+ +
+
diff --git a/public/entrance.js b/public/entrance.js index 5192d51..b6076da 100644 --- a/public/entrance.js +++ b/public/entrance.js @@ -46,8 +46,9 @@ /* ───── Step transitions ───── */ const steps = { - email: document.getElementById('step-email'), - code: document.getElementById('step-code'), + email: document.getElementById('step-email'), + code: document.getElementById('step-code'), + welcome: document.getElementById('step-welcome'), }; function showStep(name) { Object.entries(steps).forEach(([k, el]) => { @@ -171,8 +172,8 @@ async function submitCode() { }); if (res.ok) { - setAck(codeAck, 'Filed. Opening your archive\u2026', false); - setTimeout(() => { window.location.href = '/'; }, 500); + setAck(codeAck, '', false); + showStep('welcome'); return; } @@ -199,3 +200,29 @@ document.getElementById('use-different').addEventListener('click', () => { showStep('email'); setTimeout(() => emailInput.focus(), 300); }); + +/* ───── Step 3: welcome → timeline ───── */ +document.getElementById('welcome-continue').addEventListener('click', () => { + // Cross-document View Transitions animate this nav automatically on + // supported browsers (Chrome/Safari). Firefox falls back to a plain nav. + window.location.href = '/timeline'; +}); + +/* ───── On-load routing ───── */ +// `/` always serves this entrance shell. Decide which step to show based +// on whether the visitor already has a valid session. +(async function routeOnLoad() { + let authed = false; + try { + const res = await fetch('/auth/me', { credentials: 'same-origin' }); + authed = res.ok; + } catch { /* offline — fall through to email */ } + + if (authed) { + showStep('welcome'); + } else { + showStep('email'); + setTimeout(() => emailInput.focus(), 300); + } + document.body.classList.remove('is-pending'); +})(); diff --git a/public/fenja/fenja-wordmark-black.svg b/public/fenja/fenja-wordmark-black.svg new file mode 100644 index 0000000..a39a16b --- /dev/null +++ b/public/fenja/fenja-wordmark-black.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/public/fenja/man_woman_talking_transparent.svg b/public/fenja/man_woman_talking_transparent.svg new file mode 100644 index 0000000..ba16668 --- /dev/null +++ b/public/fenja/man_woman_talking_transparent.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/fenja/men_by_computer_transparent.svg b/public/fenja/men_by_computer_transparent.svg new file mode 100644 index 0000000..93f54bc --- /dev/null +++ b/public/fenja/men_by_computer_transparent.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/fenja/six_people_discussion_transparent.svg b/public/fenja/six_people_discussion_transparent.svg new file mode 100644 index 0000000..bb3c3db --- /dev/null +++ b/public/fenja/six_people_discussion_transparent.svg @@ -0,0 +1,3 @@ + + + diff --git a/server.js b/server.js index 009d205..e9e0511 100644 --- a/server.js +++ b/server.js @@ -12,7 +12,6 @@ import { fileURLToPath } from 'node:url'; import authRouter from './src/auth.js'; import { requireAuth } from './src/middleware.js'; -import { currentSession } from './src/sessions.js'; import { initMail } from './src/mail.js'; import './src/db.js'; // side-effect import: opens DB + runs schema @@ -74,18 +73,18 @@ app.use((req, res, next) => { app.use('/auth', authRouter); // ─── Root dispatch ─────────────────────────────────────────── -// GET / → timeline (if authed) | entrance (otherwise) -// GET /entrance → always the entrance (useful for "log in as someone else") -// Other paths fall through to the static handlers below. -app.get('/', (req, res, next) => { - if (currentSession(req)) { - // Authed: serve the timeline directly from /protected/index.html - return res.sendFile(path.join(__dirname, 'protected', 'index.html')); - } - // Not authed: serve the entrance +// GET / → always the entrance shell. If authed, entrance.js routes +// the user to the welcome step client-side (preserving the +// email/code UI as the no-session fallback). +// GET /timeline → gated timeline page (protected/index.html). +app.get('/', (req, res) => { return res.sendFile(path.join(__dirname, 'public', 'entrance.html')); }); +app.get('/timeline', requireAuth, (req, res) => { + return res.sendFile(path.join(__dirname, 'protected', 'index.html')); +}); + // ─── Public static assets (entrance.js, etc.) ──────────────── // Fallthrough so Express can still try the routes below if nothing matches. app.use(