site structure

This commit is contained in:
Arlind Ukshini 2026-04-23 12:40:18 +02:00
parent af35862749
commit 8790b6629b

327
SITE_STRUCTURE_REFERENCE.md Normal file
View file

@ -0,0 +1,327 @@
# Project Bifrost — Site Structure Reference
This document labels every editable piece of the site with a short name you can quote when asking for changes, and notes where each piece lives in the code. Use the labels (e.g. **S1 lede**, **P3 footer**) to point at changes precisely.
---
## Top-level structure
The site has **three pages**, each occupying the full viewport. A dot-nav at the bottom switches between them.
```
/timeline → ┌─────────────────────────────────────────────┐
│ [P1] TIMELINE │
│ [P2] OVERVIEW (Project Bifrost scenes) │
│ [P3] ARCHIVE │
└─────────────────────────────────────────────┘
```
Only one page is visible at a time. They are mutually exclusive — switching between them is a class toggle on the page wrapper, not a navigation.
Before reaching any of the three, users see the **entrance** at `/` (email form → welcome step). The entrance is a separate HTML file in `public/`.
---
## [P1] TIMELINE — horizontal scroll catalog
**Files:** `protected/index.html` (`#page-timeline`, lines ~739758) + `protected/timeline.js`
A horizontal mousewheel-driven scroll through 23 editorial cards, set over a slowly rotating orthographic globe. Cards alternate above and below a central spine; each is accented by one of four colours (copper/ochre/terracotta/crimson) denoting register.
| Label | What it is | Where to change |
|---|---|---|
| **P1 page title** | *"From the promise of AI to the loss of **sovereignty.**"* | `index.html` line 740 |
| **P1 subtitle** | *"Twenty-three headlines, quietly laid across a tinted map. Scroll the wheel — the map turns with you."* | `index.html` line 741 |
| **P1 globe** | Rotating orthographic globe that tracks scroll position from N. America → Europe | `timeline.js``buildGlobe()` + `setRotation` in `applyScroll()` |
| **P1 events** | The 23 headline cards. Each has `date`, `kind`, `accent`, `hed`, `body`, `source` | `timeline.js``EVENTS` array, lines 4102 |
| **P1 card styles** | Card layout, above/below-spine positioning, colour accents per `accent` value | `index.html` CSS `.evt`, `.accent-*` |
| **P1 year ticks** | "2022", "2023", … on the spine between groups of cards | `timeline.js``buildTimeline()`, year-tick loop |
| **P1 "Read the editor's note" button** | Bottom-right, appears near the end of the catalog; fires dot-nav switch to Overview | `index.html` line 747 (text) + `.continue-btn` CSS + `timeline.js` click handler |
---
## [P2] OVERVIEW — The Project Bifrost scroll
**Files:** `protected/index.html` (`#page-overview`, lines ~22322619) + `protected/bifrost.js`
A vertically scrolling container that holds six scroll-bound scenes. The Europe map sits as a fixed background behind them all and fades in/out with scroll position.
```
┌─ S1 HERO ──────────────┐
│ "Secure & Sovereign │ ← Europe map fades behind
│ AI, hosted where it │
│ belongs." │
└────────────────────────┘
┌─ S2 ARCHITECTURE ──────┐
│ 4 layer cards fall + │ ← pinned, scrubbed
│ morph into 2x2 grid │
└────────────────────────┘
┌─ S3 WORDS ─────────────┐
│ "...built with them, │ ← words fly in
│ not just for them." │
└────────────────────────┘
┌─ S4 BIFROST ARC ───────┐
│ 🌈 aurora arc draws │ ← "Introducing Project Bifrost"
│ "Project Bifrost" │
└────────────────────────┘
┌─ S5 TREASURE MAP ──────┐
│ Community, Advisory │ ← 3 stops on a winding path
│ Council, Pilot Proj. │
└────────────────────────┘
┌─ S6 JOIN CTA + FOOTER ─┐
│ "Join us in shaping…" │ ← button → confirmation panel
│ [Proj] [Fenja] [Innov]│ ← 3-column footer
└────────────────────────┘
```
### S0 — Europe map background
The map stays static behind all six scenes. It does not rotate with scroll; it only fades.
| Label | What it is | Where |
|---|---|---|
| **P2 Europe map** | Static orthographic globe centered on Europe, sitting behind the hero | `index.html` line 2234 (`.overview-globe`) + CSS line 441 |
| **P2 map opacity ceiling** | Maximum map opacity (currently `0.42`) | `bifrost.js``MAP_MAX_OPACITY` constant |
| **P2 map fade curve** | Fully visible 020% scroll, fades to 0 by 80% scroll, fades back in when scrolling up | `bifrost.js``updateMapOpacity()` function |
### S1 — Hero
| Label | What it is | Where |
|---|---|---|
| **S1 eyebrow** | *"For regulated environments"* | `index.html` line 2246 |
| **S1 headline** | *"Secure & **Sovereign** AI, hosted where it **belongs.**"* | `index.html` lines 22472250 |
| **S1 lede** | *"Enabling highly advanced AI capabilities hosted within the client's own secure infrastructure."* | `index.html` lines 22512253 |
| **S1 supported-by** | Innovationsfonden placeholder logo + *"Supported by"* strip | `index.html` lines 22582267 |
| **S1 scroll hint** | "Scroll →" indicator bottom-right | `index.html` lines 22682271 |
| **S1 hero styles** | Type scale, colours, grid positioning, left-column anchoring | `index.html` CSS `#hero`, `.hero-title`, `.hero-lede`, `.hero-foot` |
| **S1 hero animation** | Word-by-word reveal of the headline on scene entry | `bifrost.js` — search `HERO — staggered intro` |
### S2 — Architecture stack
Four cards fall into a stack one at a time, then rearrange into a 2x2 grid. As they do, three copy panels crossfade on the left.
| Label | What it is | Where |
|---|---|---|
| **S2 copy panel A** | *"All the capabilities to solve business use cases with AI. Four layers. One architecture. Every piece yours to own."* | `index.html` lines 22842288 (`data-copy="0"`) |
| **S2 copy panel B** | *"Full client **control.** Complete **sovereignty.**"* + *"Nothing proprietary above the hardware…"* | `index.html` lines 22892292 (`data-copy="1"`) |
| **S2 copy panel C** | *"Built in **Denmark.** For **Europe.**"* + *"Engineered against the standards that matter here…"* | `index.html` lines 22932297 (`data-copy="2"`) |
| **S2 card 1 — Foundation** | *"AI — An **open-source** model, running on your **own hardware.**"* | `index.html` lines 23022310 (`data-layer="0"`) |
| **S2 card 2 — Knowledge** | *"The **vocabulary** of your business — **learned, retained.**"* | `index.html` lines 23122320 (`data-layer="1"`) |
| **S2 card 3 — Tools** | *"How the AIs **act** — not just what they **know.**"* | `index.html` lines 23222330 (`data-layer="2"`) |
| **S2 card 4 — Agents** | *"**Specialists**, **collaborating** to solve distinct tasks."* | `index.html` lines 23322340 (`data-layer="3"`) |
| **S2 stack animation** | The fall, stack, morph-to-grid scrub sequence | `bifrost.js` — search `ARCHITECTURE — two-phase scrubbed sequence` |
| **S2 card styles** | Shape, padding, "brain" illustration, eyebrow, grid-mode styles | `index.html` CSS `.layer-card`, `.card-box`, `.card-brain`, `.in-grid` |
### S3 — Words fly in
Sixteen individual words fly in from random directions as the user scrolls, assembling into one full sentence. Two words (`with` and `them,`) are given extra visual weight.
| Label | What it is | Where |
|---|---|---|
| **S3 full sentence** | *"But a platform for regulated organisations has to be built with them, not just for them."* | `index.html` lines 23612376 |
| **S3 highlight words** | `with`, `them,` — the two words with emphasis class `hi` | `index.html` lines 23712372 |
| **S3 word styles + animation** | Individual word reveal with scatter/scale/blur | `bifrost.js` — search `WORDS fly in`; CSS `.words`, `.w`, `.w.hi` |
### S4 — Bifrost aurora arc
A rainbow arc draws across the scene, then the "Project Bifrost" title fades in below it. This is the only place on the site where a gradient is used.
| Label | What it is | Where |
|---|---|---|
| **S4 eyebrow** | *"Introducing"* | `index.html` line 2441 |
| **S4 name** | *"**Project Bifrost**"* (split into two tokens — "Project" + "Bifrost") | `index.html` lines 24422445 |
| **S4 subtitle** | *"The bridge **between** an industrial-grade AI platform and the realities of regulated organisations — built **with** them, not just for them."* | `index.html` lines 24462448 |
| **S4 arc** | The rainbow arc that draws across the scene | `index.html` lines 23912436 — `<svg>` with `#arcHalo`, `#arcMain`, `#arcThin` |
| **S4 arc colors** | The aurora gradient stops (ochre → terracotta → indigo → heather) | `index.html` lines 23932408 — `<linearGradient>` stops |
| **S4 arc animation** | Stroke-dashoffset draw-in on scroll | `bifrost.js` — search `PROJECT BIFROST REVEAL` |
### S5 — Treasure map (3 stops)
A winding path draws down the scene, passing through three "stops" — each an illustrated card about one way to participate in Project Bifrost. Stops alternate left / right of the path.
| Label | What it is | Where |
|---|---|---|
| **S5 intro title** | *"What being part of **Project Bifrost** means"* | `index.html` lines 24692471 |
| **S5 intro lede** | *"Three ways to **shape**, to **influence**, and to **build with** the platform from the inside — a journey through what participation actually looks like."* | `index.html` lines 24722474 |
| **S5 winding path** | The bezier curve drawn between stops; rebuilt at runtime to pass through dot positions | `index.html` lines 24822494 (SVG shell); `bifrost.js``buildMapPath()` |
| **S5 intro stop** | *"Being part of **Project Bifrost** means **three** things — a community to shape the future with, a council to influence the platform through, and pilot projects that put it in your hands first."* (centered introductory copy) | `index.html` lines 24972504 |
| **S5 stop 1 — Community** | Eyebrow *"Be part of a"* + title ***"Community"*** + sub *"Shape the future together"* + body copy + illustration of six people in discussion | `index.html` lines 25072518; illustration `fenja/illustrations/community.svg` |
| **S5 stop 2 — Advisory Council** | Eyebrow *"Be part of an"* + title ***"Advisory Council"*** + sub *"Turn insight into influence"* + body copy + illustration of a man and a woman in conversation | `index.html` lines 25212532; illustration `fenja/illustrations/council.svg` |
| **S5 stop 3 — Pilot Projects** | Eyebrow *"Be part of"* + title ***"Pilot Projects"*** + sub *"Access the platform before others"* + body copy + illustration of two people at a computer | `index.html` lines 25352546; illustration `fenja/illustrations/pilot.svg` |
| **S5 path animation + per-stop reveals** | Draw-in of path; dot pops in + text/image fade on reach | `bifrost.js` — search `Treasure-map path draw` |
| **S5 styles** | Layout, alternating left/right stops, dots, path styling | `index.html` CSS `.map-intro`, `.map-canvas`, `.map-stop`, `.dot-anchor`, `.stop-*` |
### S6 — Join CTA + footer
The final scene is a large call-to-action headline with a single button. Clicking the button crossfades to a confirmation panel listing what happens next. Below, a three-column footer with brand marks.
| Label | What it is | Where |
|---|---|---|
| **S6 CTA eyebrow** | *"Ready?"* | `index.html` line 2565 |
| **S6 CTA headline** | *"Join us in shaping the future of **trusted sovereign AI.**"* | `index.html` lines 25662568 |
| **S6 CTA button text** | *"Join Project Bifrost"* | `index.html` lines 25692572 |
| **S6 CTA subtext** | *"Built in Denmark. Supported by the Innovation Fund."* | `index.html` line 2573 |
| **S6 confirmation eyebrow** | *"You're in"* | `index.html` line 2578 |
| **S6 confirmation headline** | *"Thank you for joining **Project Bifrost**."* | `index.html` lines 25792581 |
| **S6 confirmation list item 1** | *"The **Fenja AI team** will reach out to you shortly."* | `index.html` line 2583 |
| **S6 confirmation list item 2** | *"You'll receive an invitation to the **project portal** soon…"* | `index.html` line 2584 |
| **S6 confirmation list item 3** | *"We're currently setting the date for the **first advisory council meeting**…"* | `index.html` line 2585 |
| **S6 confirmation list item 4** | *"We'll be in touch shortly about your participation in the **pilot project**."* | `index.html` line 2586 |
| **S6 footer left — "Project Bifrost"** | Wordmark rendered in Newsreader with italic emphasis on "Bifrost" | `index.html` line 2593 |
| **S6 footer center — Fenja logo** | `<img>` pointing to `/fenja/fenja-wordmark-black.svg` | `index.html` lines 25952599 |
| **S6 footer right — Innovationsfonden** | Placeholder slanted-I mark + "nnovationsfonden" text (to be swapped for real asset) | `index.html` lines 26012612 |
| **S6 click handler** | CTA → confirmation crossfade + staggered checkmarks on list items | `bifrost.js` — search `SCENE 6 — Join section` |
---
## [P3] ARCHIVE — 23-row table
**Files:** `protected/index.html` (`#page-archive`, lines ~26222651) + `protected/timeline.js` archive-builder IIFE
A tabular view of the same 23 events from the timeline, sorted chronologically. Each row fades its background on hover.
| Label | What it is | Where |
|---|---|---|
| **P3 headline** | *"All twenty-three entries, in order of **publication.**"* | `index.html` line 2625 |
| **P3 sub** | *"Dates, sources and plate numbers for every card in the catalog. Hover a row to lift it from the paper."* | `index.html` lines 26262629 |
| **P3 table** | 5 columns: №, Date, Register, Headline, Source | `index.html` table markup + `timeline.js` archive-builder IIFE (lines 406420) |
| **P3 data** | Same `EVENTS` array as the timeline | `timeline.js``EVENTS` |
| **P3 footer** | *"Fenja AI · Field Notes, No. IV / Catalog closed 14 April 2026 / Page III of III"* | `index.html` lines 26442648 |
---
## Shared chrome — visible on all three pages
**Files:** `protected/index.html` (lines 2207 + 26532668) + `protected/timeline.js`
| Label | What it is | Where |
|---|---|---|
| **Site wordmark** | Top-left Fenja logo, 118px wide | `index.html` line 2207 |
| **Dot-nav tray** | Subtle gradient fade at the bottom edge of the viewport | `index.html` `.dot-nav-tray` |
| **Dot-nav** | Bottom-center "Timeline / Overview / Archive" page switcher | `index.html` lines 26532668 |
| **Dot-nav logic** | Page activation + bifrost lazy-init on first Overview visit | `timeline.js``activatePage()` function, lines 439457 |
---
## Entrance — before login
**Files:** `public/entrance.html` + `public/entrance.js`
The only public-facing page. A single email field; on submit, if the email is on the invite list, advances to a welcome step. Otherwise shows an inline "not invited" message.
| Label | What it is | Where |
|---|---|---|
| **Entrance tagline** | *"Thank you for your commitment and willingness to contribute."* | `entrance.html` step-email `.tagline` |
| **Entrance email input** | The single input field with "your email" placeholder | `entrance.html` step-email `<form>` |
| **Entrance topographic currents** | Concentric-circle SVG background in the top-right | `entrance.js``drawCurrents()` IIFE |
| **Entrance error messages** | *"Please enter a valid email address."* / *"This email is not on the invite list."* / *"Too many attempts…"* | `entrance.js` — inside the submit handler |
| **Welcome title** | *"Thanks for your interest, **[Name].**"* (or *"Thank you for your **interest.**"* when no first name) — set by JS after a successful login | `entrance.js``setWelcomeTitle()` |
| **Welcome body, paragraph 1** | *"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."* | `entrance.html` step-welcome `.welcome-body` (first one) |
| **Welcome body, paragraph 2** | *"What follows is a timeline: twenty-three moments that explain why this matters now, and what the path looks like."* | `entrance.html` step-welcome `.welcome-body` (second one) |
| **"Learn more" button** | The button that routes to `/timeline` | `entrance.html` step-welcome `#welcome-continue` |
| **Welcome logo ghost** | Faint Fenja wordmark occupying the right half of the viewport | `entrance.html` `.welcome-logo` |
---
## Auth — server-side
**Files:** `src/auth.js` + `src/sessions.js` + `src/db.js` + `bin/invite.js`
For completeness — not UI, but labelled here so you can point at it when relevant.
| Label | What it is | Where |
|---|---|---|
| **Login endpoint** | `POST /auth/login` — accepts `{email}`, returns `{ok, firstName}` on success or `{error: "not_invited"}` with 403 | `src/auth.js` |
| **Login rate limit** | 30 attempts per IP per hour | `src/auth.js``rateLimit({…})` on `/login` route |
| **Logout endpoint** | `POST /auth/logout` — clears session cookie | `src/auth.js` |
| **Me endpoint** | `GET /auth/me` — returns `{email, firstName}` for current session or 401 | `src/auth.js` |
| **Invite CLI** | `node bin/invite.js add <email> [FirstName]` / `remove` / `list` | `bin/invite.js` |
| **Invite schema** | `invites(email, first_name, invited_at, invited_by)` | `src/db.js` |
| **Session duration** | 30 days | `src/sessions.js``SESSION_TTL_MS` |
---
## Design tokens — safe to change, affect everything cohesively
**File:** `protected/fenja/colors_and_type.css`
| Token | Value | Meaning |
|---|---|---|
| `--paper` / `--background` | `#faf6ee` | Primary paper background |
| `--ink` / `--on-surface` | `#383831` | Primary text colour |
| `--ink-soft` / `--on-surface-variant` | `#5f5e5e` | Muted text |
| `--ink-dim` / `--on-surface-muted` | `#8a887f` | Very muted text |
| `--walnut` / `--secondary` | `#785f53` | Primary accent (links, focus rings) |
| `--crimson` | `#8a3a2f` | Destructive / error |
| `--pigment-terracotta` | `#b96b58` | Warnings, S4 Bifrost accent |
| `--pigment-copper` | `#6d8c7c` | Success, organic / growth |
| `--pigment-ochre` | `#c29d59` | Cautions, tertiary |
| `--pigment-indigo` | `#5a6d83` | Info (only "blue" allowed) |
| `--pigment-heather` | `#8d7a85` | Categorical, supportive |
| Aurora gradient stops | ochre → terracotta → indigo → heather | S4 arc only |
Fonts: **Newsreader** (serif, for intent) and **Manrope** (sans, for execution). Both self-hosted as variable fonts in `protected/fenja/fonts/`.
The "no line" rule applies throughout: depth comes from tonal surface shifts, not 1px borders. Emphasis is applied via the Fenja rule — last keyword in Newsreader Bold Italic, followed by the absolute period.
---
## How to ask for changes
Pick a label from the tables above and tell me what you want changed. Examples:
- *"Change **S1 lede** to say …"*
- *"Swap the colour of the **S4 arc** from terracotta to copper"*
- *"Remove **S5 stop 2** entirely; renumber the path"*
- *"Add a 5th item to the **S6 confirmation list**"*
- *"Reorder: move **S3 Words** to come after **S4 Bifrost Arc**"*
- *"Make the **P2 Europe map** fully fade out by 40% scroll instead of 80%"*
- *"Change the **Welcome title** to read differently when the user has no first name"*
- *"Update **P1 events** entry #14 (the November 2024 rupture) to say …"*
If a change crosses multiple sections (for example, "add a fourth treasure map stop" or "reorganise the six scenes into five"), I'll lay out the pieces that need to move in a plan before writing code, so you can sign off.
If something you want to change isn't on this map, quote a phrase from the site and I'll find it.
---
## File index — where each label's code lives
```
protected/
├── index.html [P1], [P2 except animations], [P3], shared chrome
├── timeline.js [P1 events + globe + card builder], [P3 table builder],
│ dot-nav logic + bifrost lazy-init
├── bifrost.js All [P2] scene animations + Europe map fade
├── vendor/
│ ├── lenis.min.js Smooth scroll (for [P2])
│ ├── gsap.min.js Animation engine
│ ├── scrolltrigger.min.js Scroll-bound timelines
│ ├── d3-array.min.js Globe math (used by timeline.js)
│ ├── d3-geo.min.js Globe projection
│ ├── topojson-client.min.js World topology parser
│ └── countries-110m.json World country shapes
└── fenja/
├── colors_and_type.css Design tokens
├── fonts/ Manrope + Newsreader (variable fonts)
├── fenja-wordmark-black.svg Logo — used by site-mark + S6 footer
└── illustrations/
├── community.svg S5 stop 1
├── council.svg S5 stop 2
└── pilot.svg S5 stop 3
public/
├── entrance.html Entrance + welcome steps (before login)
└── entrance.js Entrance form behaviour, welcome title logic
src/
├── auth.js /auth/login, /auth/logout, /auth/me
├── db.js SQLite schema + prepared queries
├── sessions.js Session cookie lifecycle
└── middleware.js rateLimit() + requireAuth()
bin/
└── invite.js CLI: add / remove / list invites
server.js Entry point, CSP, routing
```