From f4cdbf4fd4ffce40fecab44ab301b5a2b5f02aa3 Mon Sep 17 00:00:00 2001 From: Arlind Ukshini Date: Thu, 23 Apr 2026 11:40:44 +0200 Subject: [PATCH] remove archive and other changes --- CHANGES.md | 118 ++++++++++++ protected/archive.html | 59 ------ protected/archive.js | 15 -- protected/bifrost.js | 154 +++++++++++++++- protected/index.html | 401 ++++++++++++++++------------------------- protected/timeline.js | 115 ++++++++---- 6 files changed, 504 insertions(+), 358 deletions(-) create mode 100644 CHANGES.md delete mode 100644 protected/archive.html delete mode 100644 protected/archive.js diff --git a/CHANGES.md b/CHANGES.md new file mode 100644 index 0000000..c3d1180 --- /dev/null +++ b/CHANGES.md @@ -0,0 +1,118 @@ +# Site update — dot-nav redesign, archive removal, S2/S3 copy rewrite + +Everything happens inside `protected/`. Server-side (src/, server.js, auth) +is untouched. + +## Files to replace + +| File | Action | +|---|---| +| `protected/index.html` | **Overwrite** — dot-nav CSS/markup, S2 cards + panels rewritten, S3 sentence rewritten, archive page + CSS removed, Fenja footer logo sized correctly, footer bottom padding increased | +| `protected/timeline.js` | **Overwrite** — `buildArchive()` deleted, `activatePage()` now takes a scroll target, scroll-spy integration, fetches `/auth/me` for first name | +| `protected/bifrost.js` | **Overwrite** — S3 sentence rebuilt with first name at init, `scrollTo()` exposed via `window.__bifrost`, scroll-spy updates active dot | + +## Files to delete + +| File | Why | +|---|---| +| `protected/archive.html` | Legacy standalone archive placeholder, no longer referenced | +| `protected/archive.js` | Companion to the above | + +On the VPS: `sudo rm /opt/fenja/protected/archive.html /opt/fenja/protected/archive.js` +or via rsync with `--delete`, which also picks them up automatically. + +## What changed, by section + +### Dot-nav + +- **5px dots**, inline flex with 10px hit-padding each (so they're still tappable). +- **Default state**: transparent fill, 1px `var(--ink-dim)` ring (outlined). +- **Hover**: ring darkens to `var(--ink)`. +- **Active**: fills with `var(--ink)` (solid). +- **Label**: hidden by default, appears as a paper-toned tooltip ABOVE the dot on hover or keyboard focus. +- **Nav composition (flat, 7 dots):** + 1. Timeline (`page-timeline`) + 2. Hero (`page-overview` → scrolls to `#hero`) + 3. Architecture (`page-overview` → `#stack-scene`) + 4. Words (`page-overview` → `#words-scene`) + 5. Bifrost (`page-overview` → `#bifrost`) + 6. Participate (`page-overview` → `#bifrost-meaning`) + 7. Join (`page-overview` → `#bifrost-join`) +- **Scroll-spy**: as the user scrolls within Overview, the currently-visible scene's dot becomes active automatically. +- **Dot-nav tray gradient** (paper fade at page bottom) is suppressed on the Overview page only — so the S6 footer reads as a hard terminus. + +### Footer + +- **Fenja logo**: previously stretched to fill the center column (`width: 100%`). Now height-matched to the other two footer items via the existing `--foot-h` variable — same size as "Project Bifrost" wordmark and the Innovationsfonden placeholder. +- **Bottom padding**: bumped from `clamp(0.5rem, 2vh, 1.5rem)` to `clamp(5rem, 10vh, 8rem)` so the three logos sit clearly above the dot-nav even on short viewports. + +### Archive + +- `
` removed from `index.html`. +- All `.archive` CSS removed (~155 lines). +- `buildArchive()` IIFE removed from `timeline.js`. +- Standalone `protected/archive.html` / `archive.js` — deletion noted above. + +### S2 — Architecture + +**Four cards (new copy throughout):** + +1. **The AI** — *"An **open-source** model, running on your **own hardware.**"* + A state-of-the-art open-source language model deployed directly in your environment. It gives you powerful AI capabilities with full control over data, performance, and security. + +2. **The Knowledge** — *"The business context that makes **AI understand your world.**"* + A built-in knowledge layer that helps the platform understand your terminology, processes, and data. It retains what matters, improves over time, and gives the AI the context needed to deliver relevant and accurate results. + +3. **The Tools** — *"How AI **acts** — not just what it **knows.**"* + The capabilities that let the platform do real work across your environment. From search and retrieval to data access, automation, and analysis, these are the tools the AI uses to solve tasks in practice. + +4. **The Agents** — *"Specialized AI agents **working together** around **real tasks.**"* + Purpose-built agents designed to handle distinct roles and workflows. Fenja AI includes both ready-made agents and the framework to build new ones, so you can orchestrate AI the same way your organisation already works — through specialisation and coordination. + +**Three copy panels (left side, during grid phase):** + +- Panel A (*One complete platform*): "Everything you need **in one place.**" Fenja AI brings models, knowledge, tools, and agents together in one platform for using and scaling AI across your organisation. +- Panel B (*Full control*): "Your **infrastructure.** / Your **rules.**" Fenja AI is installed in your own client-managed environment, giving you full control over data, security, and governance. +- Panel C (*Sovereignty*): "Built in **Denmark.** / Ready for **Europe.**" Fenja AI is built in Denmark for European organisations that want trusted, sovereign AI on their own terms. + +### S3 — Words fly in + +Sentence is now personalised per user. `timeline.js` fetches `/auth/me` on load; `bifrost.js` reads `window.__fenjaFirstName` at init and rebuilds the `.words` paragraph in place before the fly-in animation captures the spans. + +- **With first name**: *"This is why we've invited you, **Erik.** To ensure Fenja AI is not just built for you — but **with you.**"* — Bold Italic emphasis on `Erik.` and `with you.` +- **No first name (fallback)**: *"This is why we've invited **you.** To ensure Fenja AI is not just built for you — but **with you.**"* — Bold Italic emphasis on `you.` (after invited) and `with you.` +- Animation preserved: each word flies in from a scatter, with emphasis (`.hi`) words coming in from center with extra weight. + +## Spot-check after deploy + +- [ ] Open `/timeline`. Bottom of screen: 7 small dots, no labels. +- [ ] Hover any dot → paper-toned tooltip appears ABOVE it with the section name. +- [ ] Timeline dot is filled (active); others are outlined. +- [ ] Click "Architecture" dot → switches to Overview, smooth-scrolls to the stack scene. +- [ ] Click "Bifrost" dot → Overview → smooth-scrolls to the aurora arc scene. +- [ ] Inside Overview, manually scroll. The active dot changes as each scene passes the viewport midline. +- [ ] Go to S3 (Words scene). With a named invite (e.g. "Erik"), the sentence reads: *"This is why we've invited you, **Erik.** To ensure Fenja AI is not just built for you — but **with you.**"* +- [ ] With a no-name invite (like the existing `quka93@gmail.com`), the sentence reads: *"This is why we've invited **you.** To ensure Fenja AI is not just built for you — but **with you.**"* +- [ ] Scroll to S6 footer. The three brand marks (Project Bifrost / Fenja logo / Innovationsfonden) all read as equal height. +- [ ] Logos have clear space below them — not hidden by the dot-nav tray fade. +- [ ] On Overview page only, the bottom paper-fade tray is gone — footer meets the bottom of the viewport cleanly. +- [ ] On Timeline page (scroll to the end of the 23 headlines), the paper-fade tray is still there behind the dot-nav (so cards fade smoothly into it). +- [ ] Clicking "Read the editor's note" from P1 still jumps to S1 Hero of the Overview (behaves like the Hero dot). +- [ ] No console errors, no CSP violations, no 404s. + +## Things NOT touched + +- `src/` (auth, db, mail, sessions, middleware) — untouched. +- `server.js` — untouched. +- `public/entrance.html`, `public/entrance.js` — untouched. +- `protected/fenja/*` — colors, fonts, logos, illustrations all untouched. +- `protected/vendor/*` — untouched (lenis, gsap, scrolltrigger, d3, topojson, countries data). +- Server-side CSP, auth gate, rate limits — untouched. + +## Open items (for a future iteration if you want them) + +- The "Read the editor's note" button on P1 currently wires straight to the Overview dot's click handler. It now scrolls to `#hero` (top of Overview) — which matches the previous behaviour. Fine as-is. +- The S3 sentence uses the Overview's internal scroller for its fly-in, but the current fly-in distances (scatter radius 220×160 px) are unchanged from the old 16-word sentence. With 21 words, the animation may feel slightly faster. If it feels too fast, we can bump the `0.055` stagger in bifrost.js to `0.07`. +- If you change the 7-dot nav order or add/remove scenes, update: + - The `data-scroll-to` attributes in `index.html`'s `
- -
-
-
-

All twenty-three entries, in order of publication.

-

- Dates, sources and plate numbers for every card in the catalog. Hover a row to - lift it from the paper. -

+ - - - -
-
-
- - + data-target : page id to activate + data-scroll-to : (optional) element id inside #overview-scroll to + scroll to AFTER the page switch. Scroll runs on the + Overview's internal scroller via Lenis (if booted) + or scroller.scrollTo() as a fallback. -->
diff --git a/protected/timeline.js b/protected/timeline.js index 90bbafb..7b14d99 100644 --- a/protected/timeline.js +++ b/protected/timeline.js @@ -1,8 +1,9 @@ // ───────────────────────────────────────────────────────────── -// protected/timeline.js — timeline scroll, dot-nav, globe, archive. -// This file now also boots the Bifrost scenes (bifrost.js) the first +// protected/timeline.js — timeline scroll, dot-nav, globe. +// This file also boots the Bifrost scenes (bifrost.js) the first // time the Overview page is activated via the dot-nav or the -// "Read the editor's note" button. +// "Read the editor's note" button, and fetches the user's first +// name from /auth/me so Scene 3 can personalise its sentence. // ───────────────────────────────────────────────────────────── // Signal to the Bifrost CSS that JS is running (enables the scroll- @@ -409,49 +410,89 @@ function buildGlobe(wrap, opts) { }); } -/* ───────────────────────────────────────────────────────────── - Archive table - ───────────────────────────────────────────────────────────── */ -(function buildArchive() { - const tbody = document.getElementById('archive-body'); - EVENTS.forEach((e, i) => { - const tr = document.createElement('tr'); - tr.dataset.accent = e.accent; - tr.innerHTML = ` - ${String(i + 1).padStart(2, '0')} - ${e.date} - ${e.kind} - ${e.hed} - ${e.source} - `; - tbody.appendChild(tr); - }); -})(); - /* ───────────────────────────────────────────────────────────── Dot-nav + Bifrost lazy-boot on Overview activation ───────────────────────────────────────────────────────────── */ -// Switch active page. When Overview becomes active, fire -// window.__bifrost.init() (idempotent) so the scenes wire up -// exactly once, on first visit. Subsequent activations just -// refresh ScrollTrigger in case the window was resized. -function activatePage(targetId) { +/** + * Switch active page. When Overview becomes active, boot Bifrost (once) + * and optionally scroll the Overview's internal scroller to a target. + * + * @param {string} targetId e.g. "page-timeline" or "page-overview" + * @param {string?} scrollToId (Overview only) id of a scene to land on: + * "hero", "stack-scene", "words-scene", + * "bifrost", "bifrost-meaning", "bifrost-join" + */ +function activatePage(targetId, scrollToId) { document.querySelectorAll('.page').forEach(p => { p.classList.toggle('is-active', p.id === targetId); }); - document.querySelectorAll('.dot-btn').forEach(b => { - b.classList.toggle('is-active', b.dataset.target === targetId); - }); - if (targetId === 'page-overview' && window.__bifrost && typeof window.__bifrost.init === 'function') { - // Let the page transition start painting first, then init. - // 60ms is about one-and-a-half frames at 120Hz, plenty for the - // .page-overview.is-active class to flip. Short enough that the - // user can't scroll before ScrollTriggers are wired up. - setTimeout(() => window.__bifrost.init(), 60); + + if (targetId === 'page-overview') { + if (window.__bifrost && typeof window.__bifrost.init === 'function') { + // Let the page transition start painting first, then init. + // 60ms is about 1.5 frames at 120Hz — plenty for the + // .page-overview.is-active class to flip, short enough that + // the user can't scroll before ScrollTriggers are wired up. + setTimeout(() => { + window.__bifrost.init(); + // After init resolves, scroll to the requested scene (or top + // if none specified). Bifrost exposes scrollTo() which drives + // Lenis on the overview's internal scroller. + if (window.__bifrost.scrollTo) { + // One more frame so Lenis has picked up its first measurements. + requestAnimationFrame(() => window.__bifrost.scrollTo(scrollToId || 'hero')); + } + }, 60); + } } } +/** + * Update the .dot-btn.is-active highlight. Called on nav clicks (forward + * activation) and will be called from bifrost.js on scroll to track which + * Overview scene is currently visible. + */ +function setActiveDot(targetId, scrollToId) { + document.querySelectorAll('.dot-btn').forEach(b => { + const pageMatch = b.dataset.target === targetId; + const scrollMatch = (b.dataset.scrollTo || '') === (scrollToId || ''); + // Timeline dot has no data-scroll-to — it's active when page-timeline + // is active. Overview dots only match their specific scrollToId. + const isActive = targetId === 'page-overview' + ? pageMatch && scrollMatch + : pageMatch; + b.classList.toggle('is-active', isActive); + }); +} +// Expose so bifrost.js can call it from its scroll-spy. Reads happen from +// non-module code, so the global is the simplest integration surface. +window.__setActiveDot = setActiveDot; + document.querySelectorAll('.dot-btn').forEach(btn => { - btn.addEventListener('click', () => activatePage(btn.dataset.target)); + btn.addEventListener('click', () => { + const targetId = btn.dataset.target; + const scrollToId = btn.dataset.scrollTo || null; + setActiveDot(targetId, scrollToId); + activatePage(targetId, scrollToId); + }); }); + +/* ───────────────────────────────────────────────────────────── + First name propagation — fetched from /auth/me on load. + Used by Scene 3 ("This is why we've invited you, [Name]."). + Bifrost.js looks for window.__fenjaFirstName at init time and + rewrites the .words sentence before priming the fly-in animation. + ───────────────────────────────────────────────────────────── */ +(async function fetchFirstName() { + try { + const res = await fetch('/auth/me', { credentials: 'same-origin' }); + if (res.ok) { + const data = await res.json().catch(() => ({})); + window.__fenjaFirstName = data.firstName || null; + } + } catch { + // Offline — leave undefined; bifrost.js falls back to the + // no-name variant of the sentence. + } +})();