Six scroll-bound scenes (hero, architecture stack, words fly-in,
aurora arc, treasure-map, join CTA) now live inside page-overview,
above the existing 23-headline timeline. The Europe map stays as a
static background that fades with scroll.
- protected/index.html: rewrote #page-overview only; timeline and
archive sections unchanged. Site-2 palette re-mapped to site-1
Nordic Editorial tokens, Fraunces to Newsreader, tokens scoped
to #page-overview.
- protected/timeline.js: dot-nav boots window.__bifrost.init()
on first Overview activation. Added .js class on documentElement.
- protected/bifrost.js (new): Lenis + ScrollTrigger wired to the
overview's internal scroller via scrollerProxy; drives Europe
map opacity on scroll.
- protected/vendor/{lenis,gsap,scrolltrigger}.min.js (new):
extracted from site-2's inlined vendor blobs; CSP-compliant.
- protected/fenja/illustrations/{community,council,pilot}.svg
(new): treasure-map stop images.
No changes to src/, server.js, deploy/, or public/. CSP stays
strict (script-src 'self'); zero inline scripts added. Auth gate
and session model untouched.
2677 lines
84 KiB
HTML
2677 lines
84 KiB
HTML
<!doctype html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="utf-8" />
|
||
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
||
<title>A Catalog of Sovereignty — 2022–2026</title>
|
||
<link rel="stylesheet" href="/fenja/colors_and_type.css" />
|
||
<script src="/vendor/d3-array.min.js"></script>
|
||
<script src="/vendor/d3-geo.min.js"></script>
|
||
<script src="/vendor/topojson-client.min.js"></script>
|
||
<style>
|
||
@view-transition { navigation: auto; }
|
||
|
||
:root{
|
||
--paper: #faf6ee;
|
||
--paper-high: #fffcf7;
|
||
--paper-mid: #f4efe2;
|
||
--paper-low: #ece5d2;
|
||
--ink: #383831;
|
||
--ink-soft: #5f5e5e;
|
||
--ink-dim: #8a887f;
|
||
|
||
--copper: #6d8c7c; /* copper green */
|
||
--ochre: #c29d59;
|
||
--terracotta: #b96b58;
|
||
--crimson: #8a3a2f; /* deep crimson */
|
||
|
||
--ease: cubic-bezier(0.2, 0, 0, 1);
|
||
--dur: 240ms;
|
||
}
|
||
|
||
*, *::before, *::after { box-sizing: border-box; }
|
||
|
||
html, body {
|
||
margin: 0; padding: 0;
|
||
height: 100%;
|
||
background: var(--paper);
|
||
color: var(--ink);
|
||
font-family: "Manrope", system-ui, sans-serif;
|
||
overflow: hidden;
|
||
-webkit-font-smoothing: antialiased;
|
||
}
|
||
|
||
body {
|
||
/* Subtle tonal shift across the entire surface — not a gradient on chrome,
|
||
just the paper catching light. */
|
||
background:
|
||
radial-gradient(1200px 800px at 18% 45%, #fffcf7 0%, var(--paper) 55%, #f4efe2 100%);
|
||
view-transition-name: paper;
|
||
}
|
||
|
||
/* ───── Page scaffolding ───── */
|
||
.page {
|
||
position: fixed; inset: 0;
|
||
opacity: 0;
|
||
pointer-events: none;
|
||
transition: opacity 380ms var(--ease);
|
||
will-change: opacity;
|
||
}
|
||
.page.is-active {
|
||
opacity: 1;
|
||
pointer-events: auto;
|
||
}
|
||
|
||
/* ───────── Site wordmark — top-left masthead ───────── */
|
||
.site-mark {
|
||
position: fixed;
|
||
top: 28px;
|
||
left: 36px;
|
||
width: 118px;
|
||
height: auto;
|
||
z-index: 50;
|
||
pointer-events: none;
|
||
opacity: 0.85;
|
||
}
|
||
@media (max-width: 720px) {
|
||
.site-mark { width: 90px; top: 20px; left: 22px; }
|
||
}
|
||
|
||
/* Page overline title — large, sits lower on the front matter so it reads */
|
||
.page-title {
|
||
position: absolute;
|
||
left: 80px; top: 42vh;
|
||
font-family: "Newsreader", Georgia, serif;
|
||
font-weight: 400;
|
||
font-size: 54px;
|
||
letter-spacing: -0.022em;
|
||
color: var(--ink);
|
||
line-height: 1.08;
|
||
z-index: 15;
|
||
max-width: 820px;
|
||
text-wrap: pretty;
|
||
opacity: 1;
|
||
transition: opacity 520ms var(--ease), transform 520ms var(--ease);
|
||
}
|
||
.page-title em {
|
||
font-style: italic; font-weight: 700;
|
||
}
|
||
.page-title + .page-sub {
|
||
position: absolute;
|
||
left: 80px; top: calc(42vh + 220px);
|
||
max-width: 560px;
|
||
font-family: "Newsreader", Georgia, serif;
|
||
font-style: italic;
|
||
font-size: 19px;
|
||
line-height: 1.5;
|
||
color: var(--ink-soft);
|
||
z-index: 15;
|
||
opacity: 1;
|
||
transition: opacity 520ms var(--ease), transform 520ms var(--ease);
|
||
}
|
||
/* Once the timeline has been advanced, the front matter steps aside */
|
||
.page-timeline.is-scrolled .page-title,
|
||
.page-timeline.is-scrolled .page-sub {
|
||
opacity: 0;
|
||
pointer-events: none;
|
||
transform: translateY(-12px);
|
||
}
|
||
|
||
/* ───────── Dot-nav ───────── */
|
||
.dot-nav-tray {
|
||
position: fixed;
|
||
left: 0; right: 0; bottom: 0;
|
||
height: 110px;
|
||
z-index: 35;
|
||
pointer-events: none;
|
||
background: linear-gradient(to bottom,
|
||
rgba(250,246,238,0) 0%,
|
||
rgba(250,246,238,0.88) 45%,
|
||
rgba(250,246,238,0.98) 100%);
|
||
}
|
||
.dot-nav {
|
||
position: fixed;
|
||
bottom: 36px; left: 50%;
|
||
transform: translateX(-50%);
|
||
display: flex; gap: 44px;
|
||
z-index: 40;
|
||
}
|
||
.dot-btn {
|
||
all: unset;
|
||
display: flex; flex-direction: column; align-items: center;
|
||
gap: 10px;
|
||
cursor: pointer;
|
||
color: var(--ink-dim);
|
||
transition: color var(--dur) var(--ease);
|
||
}
|
||
.dot-btn:hover { color: var(--ink); }
|
||
.dot-btn .dot {
|
||
width: 7px; height: 7px;
|
||
border-radius: 50%;
|
||
background: var(--ink-dim);
|
||
transition: background var(--dur) var(--ease), transform var(--dur) var(--ease);
|
||
}
|
||
.dot-btn.is-active { color: var(--ink); }
|
||
.dot-btn.is-active .dot {
|
||
background: var(--crimson);
|
||
transform: scale(1.15);
|
||
}
|
||
.dot-btn .label {
|
||
font-size: 10.5px;
|
||
letter-spacing: 0.24em;
|
||
text-transform: uppercase;
|
||
font-weight: 500;
|
||
}
|
||
|
||
/* ───────── Globe ghost ───────── */
|
||
.globe-wrap {
|
||
position: absolute;
|
||
/* 15% larger than the original 58vw ≈ 66.7vw.
|
||
Shifted ~20% of its width toward the page center: from left:-8%
|
||
to roughly left:+5%. */
|
||
left: 5%; top: 0;
|
||
width: 66.7vw;
|
||
height: 100%;
|
||
pointer-events: none;
|
||
z-index: 1;
|
||
opacity: 0.5;
|
||
transition: opacity 280ms var(--ease);
|
||
/* Mask top and fade the bottom third so the timeline rests on clean paper */
|
||
-webkit-mask-image: linear-gradient(to bottom,
|
||
transparent 0%, #000 22%, #000 58%, transparent 72%);
|
||
mask-image: linear-gradient(to bottom,
|
||
transparent 0%, #000 22%, #000 58%, transparent 72%);
|
||
}
|
||
.globe-wrap svg {
|
||
width: 100%; height: 100%;
|
||
display: block;
|
||
}
|
||
|
||
/* ───────── Timeline ───────── */
|
||
.timeline-viewport {
|
||
position: absolute; inset: 0;
|
||
z-index: 5;
|
||
}
|
||
|
||
.timeline-track {
|
||
position: absolute;
|
||
top: 0; left: 0; height: 100%;
|
||
will-change: transform;
|
||
transform: translate3d(0,0,0);
|
||
display: flex; align-items: center;
|
||
padding: 0 120px;
|
||
--spine-y: 64%;
|
||
}
|
||
|
||
.spine {
|
||
position: absolute;
|
||
top: var(--spine-y); left: 0;
|
||
height: 1px;
|
||
width: 100%;
|
||
background: linear-gradient(to right,
|
||
transparent 0,
|
||
rgba(56,56,49,0.22) 60px,
|
||
rgba(56,56,49,0.22) calc(100% - 60px),
|
||
transparent);
|
||
z-index: 2;
|
||
}
|
||
|
||
.year-tick {
|
||
position: absolute;
|
||
top: var(--spine-y);
|
||
transform: translate(-50%, -50%);
|
||
display: flex; flex-direction: column; align-items: center;
|
||
gap: 10px;
|
||
z-index: 3;
|
||
color: var(--ink-dim);
|
||
}
|
||
.year-tick::before {
|
||
content: "";
|
||
display: block;
|
||
width: 1px; height: 28px;
|
||
background: rgba(56,56,49,0.28);
|
||
}
|
||
.year-tick .y {
|
||
font-family: "Newsreader", Georgia, serif;
|
||
font-style: italic;
|
||
font-size: 22px;
|
||
color: var(--ink-soft);
|
||
letter-spacing: 0;
|
||
font-weight: 400;
|
||
}
|
||
|
||
/* Card */
|
||
.evt {
|
||
position: absolute;
|
||
width: 320px;
|
||
padding: 16px 20px 18px;
|
||
background: var(--paper-high);
|
||
/* Tonal surface shifts instead of 1px borders */
|
||
box-shadow:
|
||
0 0 0 0.5px rgba(56,56,49,0.05),
|
||
0 14px 28px -18px rgba(56,56,49,0.18),
|
||
0 2px 6px -3px rgba(56,56,49,0.08);
|
||
color: var(--ink);
|
||
opacity: 0;
|
||
/* Pop-in: small scale + downward lift for a more tactile entrance */
|
||
transform: translateY(28px) scale(0.96);
|
||
transform-origin: center top;
|
||
}
|
||
.evt.above {
|
||
transform: translateY(-28px) scale(0.96);
|
||
transform-origin: center bottom;
|
||
}
|
||
/* Only animate after first paint — prevents the initial card from
|
||
getting stuck at opacity 0 while the transition starts pre-layout. */
|
||
.evt.can-animate {
|
||
transition:
|
||
opacity 640ms var(--ease),
|
||
transform 640ms cubic-bezier(0.16, 1, 0.3, 1),
|
||
box-shadow 320ms var(--ease);
|
||
}
|
||
.evt.is-near {
|
||
opacity: 1;
|
||
transform: translateY(0) scale(1);
|
||
}
|
||
.evt.above { bottom: calc(100% - var(--spine-y) + 48px); }
|
||
.evt.below { top: calc(var(--spine-y) + 48px); }
|
||
|
||
/* Connector from card to spine */
|
||
.evt::after {
|
||
content: "";
|
||
position: absolute;
|
||
left: 26px;
|
||
width: 1px;
|
||
background: rgba(56,56,49,0.28);
|
||
}
|
||
.evt.above::after { top: 100%; height: 40px; }
|
||
.evt.below::after { bottom: 100%; height: 40px; }
|
||
|
||
/* Node on the spine — tiny dot */
|
||
.evt .node {
|
||
position: absolute;
|
||
left: 20px;
|
||
width: 13px; height: 13px;
|
||
border-radius: 50%;
|
||
background: var(--paper-high);
|
||
z-index: 1;
|
||
}
|
||
.evt.above .node { top: calc(100% + 40px - 6px); }
|
||
.evt.below .node { bottom: calc(100% + 40px - 6px); }
|
||
.evt .node::after {
|
||
content: "";
|
||
position: absolute;
|
||
inset: 3.5px;
|
||
border-radius: 50%;
|
||
background: var(--ink-soft);
|
||
}
|
||
|
||
.evt[data-accent="copper"] .node::after { background: var(--copper); }
|
||
.evt[data-accent="ochre"] .node::after { background: var(--ochre); }
|
||
.evt[data-accent="terracotta"] .node::after { background: var(--terracotta); }
|
||
.evt[data-accent="crimson"] .node::after { background: var(--crimson); }
|
||
|
||
.evt .tag-row {
|
||
display: flex; gap: 12px; align-items: center;
|
||
margin-bottom: 8px;
|
||
}
|
||
.evt .date {
|
||
font-family: "Newsreader", Georgia, serif;
|
||
font-style: italic;
|
||
font-size: 13px;
|
||
color: var(--ink-soft);
|
||
letter-spacing: 0;
|
||
}
|
||
.evt .kind {
|
||
font-size: 9px;
|
||
letter-spacing: 0.22em;
|
||
text-transform: uppercase;
|
||
color: var(--ink-dim);
|
||
font-weight: 600;
|
||
}
|
||
.evt[data-accent="copper"] .kind { color: var(--copper); }
|
||
.evt[data-accent="ochre"] .kind { color: var(--ochre); }
|
||
.evt[data-accent="terracotta"] .kind { color: var(--terracotta); }
|
||
.evt[data-accent="crimson"] .kind { color: var(--crimson); }
|
||
|
||
.evt h3 {
|
||
font-family: "Newsreader", Georgia, serif;
|
||
font-weight: 400;
|
||
font-size: 17px;
|
||
line-height: 1.22;
|
||
letter-spacing: -0.01em;
|
||
color: var(--ink);
|
||
margin: 0 0 8px 0;
|
||
text-wrap: pretty;
|
||
}
|
||
.evt h3 em {
|
||
font-style: italic;
|
||
font-weight: 700;
|
||
}
|
||
|
||
.evt p {
|
||
margin: 0;
|
||
font-size: 12px;
|
||
line-height: 1.5;
|
||
color: var(--ink-soft);
|
||
text-wrap: pretty;
|
||
}
|
||
|
||
.evt .source {
|
||
margin-top: 10px;
|
||
font-size: 9.5px;
|
||
letter-spacing: 0.2em;
|
||
text-transform: uppercase;
|
||
color: var(--ink-dim);
|
||
font-weight: 500;
|
||
}
|
||
|
||
/* ───────── Continue button ───────── */
|
||
.continue-btn {
|
||
all: unset;
|
||
position: absolute;
|
||
right: 72px;
|
||
bottom: 140px;
|
||
display: inline-flex;
|
||
align-items: baseline;
|
||
gap: 22px;
|
||
padding: 20px 28px;
|
||
background: var(--paper-high);
|
||
color: var(--ink);
|
||
cursor: pointer;
|
||
z-index: 30;
|
||
opacity: 0;
|
||
transform: translateX(36px);
|
||
pointer-events: none;
|
||
box-shadow:
|
||
0 0 0 0.5px rgba(56,56,49,0.06),
|
||
0 18px 32px -18px rgba(56,56,49,0.22),
|
||
0 2px 6px -3px rgba(56,56,49,0.08);
|
||
transition:
|
||
opacity 520ms var(--ease),
|
||
transform 520ms var(--ease),
|
||
box-shadow var(--dur) var(--ease),
|
||
background var(--dur) var(--ease);
|
||
}
|
||
.continue-btn.is-visible {
|
||
opacity: 1;
|
||
transform: translateX(0);
|
||
pointer-events: auto;
|
||
animation: continue-breath 2800ms cubic-bezier(0.2, 0, 0, 1) infinite;
|
||
}
|
||
@keyframes continue-breath {
|
||
0%, 100% { transform: translateX(0); }
|
||
50% { transform: translateX(6px); }
|
||
}
|
||
.continue-btn:hover {
|
||
background: #fffbf2;
|
||
box-shadow:
|
||
0 0 0 0.5px rgba(56,56,49,0.10),
|
||
0 24px 40px -20px rgba(56,56,49,0.28),
|
||
0 3px 8px -4px rgba(56,56,49,0.10);
|
||
}
|
||
.continue-btn .c-label {
|
||
font-family: "Newsreader", Georgia, serif;
|
||
font-size: 20px;
|
||
font-weight: 400;
|
||
letter-spacing: -0.01em;
|
||
color: var(--ink);
|
||
line-height: 1;
|
||
}
|
||
.continue-btn .c-label em {
|
||
font-style: italic; font-weight: 700;
|
||
}
|
||
.continue-btn .c-arrow {
|
||
font-family: "Newsreader", Georgia, serif;
|
||
font-style: italic;
|
||
font-size: 22px;
|
||
color: var(--crimson);
|
||
line-height: 1;
|
||
transition: transform var(--dur) var(--ease);
|
||
}
|
||
.continue-btn:hover .c-arrow {
|
||
transform: translateX(4px);
|
||
}
|
||
|
||
/* ───────── Overview page ───────── */
|
||
|
||
/* Globe background behind the overview — same SVG style as the timeline's,
|
||
but centered on Europe. It begins at the timeline's size/position so
|
||
that when the page is entered, the CSS transition zooms it into place. */
|
||
.overview-globe {
|
||
position: absolute; inset: 0;
|
||
pointer-events: none;
|
||
z-index: 1;
|
||
overflow: hidden;
|
||
/* Soft fade at top + bottom so the paper reads as the surface, not the sphere */
|
||
-webkit-mask-image: linear-gradient(to bottom,
|
||
transparent 0%, #000 10%, #000 82%, transparent 100%);
|
||
mask-image: linear-gradient(to bottom,
|
||
transparent 0%, #000 10%, #000 82%, transparent 100%);
|
||
}
|
||
.overview-globe svg {
|
||
position: absolute;
|
||
left: 65%; top: 55%;
|
||
/* Smaller than before — 92vmax is enough to show Europe at the framing we want,
|
||
and it paints fast enough not to block the page fade-in. */
|
||
width: 92vmax;
|
||
height: 92vmax;
|
||
max-width: none;
|
||
transform: translate(-50%, -50%) scale(0.78);
|
||
transform-origin: 50% 50%;
|
||
opacity: 0.22;
|
||
transition:
|
||
transform 1200ms cubic-bezier(0.22, 1, 0.36, 1),
|
||
opacity 900ms var(--ease);
|
||
}
|
||
/* When the overview page becomes active, zoom onto Europe */
|
||
.page-overview.is-active .overview-globe svg {
|
||
transform: translate(-50%, -50%) scale(1.35);
|
||
opacity: 0.42;
|
||
}
|
||
|
||
.overview {
|
||
position: absolute; inset: 0;
|
||
overflow: auto;
|
||
padding: 160px 80px 180px;
|
||
scrollbar-width: thin;
|
||
scrollbar-color: rgba(56,56,49,0.18) transparent;
|
||
z-index: 5;
|
||
}
|
||
.overview .col-wrap {
|
||
max-width: 1280px; margin: 0 auto;
|
||
/* Text on the left; globe occupies the right half of the spread. */
|
||
display: grid;
|
||
grid-template-columns: minmax(420px, 560px) 1fr;
|
||
column-gap: 80px;
|
||
row-gap: 24px;
|
||
align-items: start;
|
||
}
|
||
.overview h1 {
|
||
grid-column: 1;
|
||
font-family: "Newsreader", Georgia, serif;
|
||
font-weight: 400;
|
||
font-size: 56px;
|
||
line-height: 1.05;
|
||
letter-spacing: -0.025em;
|
||
margin: 0 0 18px 0;
|
||
text-wrap: balance;
|
||
color: var(--ink);
|
||
}
|
||
.overview h1 em {
|
||
font-style: italic; font-weight: 700;
|
||
}
|
||
.overview .lede {
|
||
grid-column: 1;
|
||
font-family: "Newsreader", Georgia, serif;
|
||
font-style: italic;
|
||
font-size: 20px;
|
||
line-height: 1.5;
|
||
color: var(--ink-soft);
|
||
max-width: 780px;
|
||
margin-bottom: 28px;
|
||
}
|
||
.overview .rule {
|
||
grid-column: 1;
|
||
height: 1px;
|
||
background: rgba(56,56,49,0.18);
|
||
margin: 14px 0 8px 0;
|
||
}
|
||
.overview p {
|
||
grid-column: 1;
|
||
font-size: 15px;
|
||
line-height: 1.7;
|
||
color: var(--ink);
|
||
margin: 0 0 14px 0;
|
||
text-wrap: pretty;
|
||
}
|
||
.overview p.drop::first-letter {
|
||
font-family: "Newsreader", Georgia, serif;
|
||
font-weight: 700;
|
||
font-size: 58px;
|
||
line-height: 0.9;
|
||
float: left;
|
||
padding: 4px 10px 0 0;
|
||
color: var(--ink);
|
||
}
|
||
.overview .meta-strip {
|
||
grid-column: 1;
|
||
display: grid;
|
||
grid-template-columns: repeat(2, 1fr);
|
||
gap: 28px 40px;
|
||
margin-top: 32px;
|
||
padding-top: 24px;
|
||
border-top: 0;
|
||
background:
|
||
linear-gradient(to right, rgba(56,56,49,0.18), rgba(56,56,49,0.18)) top / 100% 1px no-repeat;
|
||
}
|
||
.overview .meta-strip .cell {
|
||
display: flex; flex-direction: column; gap: 8px;
|
||
}
|
||
.overview .meta-strip .k {
|
||
font-size: 10.5px;
|
||
letter-spacing: 0.24em;
|
||
text-transform: uppercase;
|
||
color: var(--ink-dim);
|
||
font-weight: 600;
|
||
}
|
||
.overview .meta-strip .v {
|
||
font-family: "Newsreader", Georgia, serif;
|
||
font-size: 22px;
|
||
letter-spacing: -0.01em;
|
||
color: var(--ink);
|
||
}
|
||
.overview .meta-strip .v em { font-style: italic; font-weight: 700; }
|
||
|
||
/* ───────── Archive page ───────── */
|
||
.archive {
|
||
position: absolute; inset: 0;
|
||
overflow: auto;
|
||
padding: 120px 80px 180px;
|
||
}
|
||
.archive .inner {
|
||
max-width: 1180px; margin: 0 auto;
|
||
}
|
||
.archive .headline {
|
||
font-family: "Newsreader", Georgia, serif;
|
||
font-weight: 400;
|
||
font-size: 60px;
|
||
line-height: 1.05;
|
||
letter-spacing: -0.02em;
|
||
margin: 0 0 14px 0;
|
||
text-wrap: balance;
|
||
}
|
||
.archive .headline em { font-style: italic; font-weight: 700; }
|
||
.archive .sub {
|
||
font-family: "Newsreader", Georgia, serif;
|
||
font-style: italic;
|
||
font-size: 19px;
|
||
color: var(--ink-soft);
|
||
margin: 0 0 48px 0;
|
||
max-width: 700px;
|
||
}
|
||
|
||
.archive table {
|
||
width: 100%;
|
||
border-collapse: collapse;
|
||
font-size: 13.5px;
|
||
}
|
||
.archive thead th {
|
||
text-align: left;
|
||
padding: 10px 12px;
|
||
font-size: 10.5px;
|
||
letter-spacing: 0.24em;
|
||
text-transform: uppercase;
|
||
color: var(--ink-dim);
|
||
font-weight: 600;
|
||
background:
|
||
linear-gradient(to right, rgba(56,56,49,0.28), rgba(56,56,49,0.28))
|
||
bottom / 100% 1px no-repeat;
|
||
}
|
||
.archive tbody tr {
|
||
transition: background var(--dur) var(--ease);
|
||
background:
|
||
linear-gradient(to right, rgba(56,56,49,0.10), rgba(56,56,49,0.10))
|
||
bottom / 100% 1px no-repeat;
|
||
}
|
||
.archive tbody tr:hover {
|
||
background:
|
||
var(--paper-high)
|
||
linear-gradient(to right, rgba(56,56,49,0.10), rgba(56,56,49,0.10))
|
||
bottom / 100% 1px no-repeat;
|
||
}
|
||
.archive tbody td {
|
||
padding: 18px 12px;
|
||
vertical-align: top;
|
||
color: var(--ink);
|
||
line-height: 1.45;
|
||
}
|
||
.archive td.num {
|
||
font-family: "Newsreader", Georgia, serif;
|
||
font-style: italic;
|
||
color: var(--ink-dim);
|
||
font-size: 13px;
|
||
width: 48px;
|
||
}
|
||
.archive td.date {
|
||
font-family: "Newsreader", Georgia, serif;
|
||
font-style: italic;
|
||
color: var(--ink-soft);
|
||
width: 130px;
|
||
white-space: nowrap;
|
||
}
|
||
.archive td.kind {
|
||
width: 150px;
|
||
font-size: 10.5px;
|
||
letter-spacing: 0.22em;
|
||
text-transform: uppercase;
|
||
font-weight: 600;
|
||
color: var(--ink-dim);
|
||
}
|
||
.archive tr[data-accent="copper"] td.kind { color: var(--copper); }
|
||
.archive tr[data-accent="ochre"] td.kind { color: var(--ochre); }
|
||
.archive tr[data-accent="terracotta"] td.kind { color: var(--terracotta); }
|
||
.archive tr[data-accent="crimson"] td.kind { color: var(--crimson); }
|
||
.archive td.hed {
|
||
font-family: "Newsreader", Georgia, serif;
|
||
font-size: 16px;
|
||
letter-spacing: -0.005em;
|
||
color: var(--ink);
|
||
max-width: 520px;
|
||
}
|
||
.archive td.hed em { font-style: italic; font-weight: 700; }
|
||
.archive td.src {
|
||
color: var(--ink-dim);
|
||
font-size: 12px;
|
||
font-family: "Newsreader", Georgia, serif;
|
||
font-style: italic;
|
||
width: 160px;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
/* Mark on key rows — tonal, no border */
|
||
.archive tr[data-accent] td:first-child {
|
||
position: relative;
|
||
}
|
||
.archive tr[data-accent] td:first-child::before {
|
||
content: "";
|
||
position: absolute;
|
||
left: -10px; top: 50%;
|
||
transform: translateY(-50%);
|
||
width: 5px; height: 5px;
|
||
border-radius: 50%;
|
||
background: var(--ink-dim);
|
||
}
|
||
.archive tr[data-accent="copper"] td:first-child::before { background: var(--copper); }
|
||
.archive tr[data-accent="ochre"] td:first-child::before { background: var(--ochre); }
|
||
.archive tr[data-accent="terracotta"] td:first-child::before { background: var(--terracotta); }
|
||
.archive tr[data-accent="crimson"] td:first-child::before { background: var(--crimson); }
|
||
|
||
/* Archive footer */
|
||
.archive .footer {
|
||
margin-top: 72px;
|
||
padding-top: 28px;
|
||
background:
|
||
linear-gradient(to right, rgba(56,56,49,0.18), rgba(56,56,49,0.18))
|
||
top / 100% 1px no-repeat;
|
||
display: flex; justify-content: space-between;
|
||
color: var(--ink-dim);
|
||
font-size: 11px;
|
||
letter-spacing: 0.22em;
|
||
text-transform: uppercase;
|
||
font-weight: 500;
|
||
}
|
||
.archive .footer em {
|
||
font-family: "Newsreader", Georgia, serif;
|
||
font-style: italic;
|
||
font-size: 13px;
|
||
color: var(--ink-soft);
|
||
letter-spacing: 0;
|
||
text-transform: none;
|
||
font-weight: 400;
|
||
}
|
||
|
||
/* Short-viewport safety: collapse the page-title block so cards never collide */
|
||
@media (max-height: 620px) {
|
||
.page-title { font-size: 36px; max-width: 640px; top: 38vh; }
|
||
.page-title + .page-sub { font-size: 16px; top: calc(38vh + 160px); }
|
||
}
|
||
@media (max-height: 500px) {
|
||
.page-title { display: none; }
|
||
.page-sub { display: none; }
|
||
}
|
||
.overview::-webkit-scrollbar,
|
||
.archive::-webkit-scrollbar { width: 6px; }
|
||
.overview::-webkit-scrollbar-thumb,
|
||
.archive::-webkit-scrollbar-thumb {
|
||
background: rgba(56,56,49,0.18);
|
||
border-radius: 3px;
|
||
}
|
||
|
||
/* Folio marks removed for a cleaner page. */
|
||
|
||
/* ============================================================
|
||
BIFROST OVERLAY — scenes inside the Overview page
|
||
============================================================ */
|
||
|
||
/* Internal scroller — sits inside #page-overview. Hosts the six
|
||
scenes. Scrolls vertically. The Europe map (overview-globe)
|
||
stays as fixed-position background behind it. */
|
||
#overview-scroll {
|
||
position: absolute;
|
||
inset: 0;
|
||
overflow-y: auto;
|
||
overflow-x: hidden;
|
||
z-index: 5;
|
||
scrollbar-width: thin;
|
||
scrollbar-color: rgba(56,56,49,0.18) transparent;
|
||
}
|
||
#overview-scroll::-webkit-scrollbar { width: 6px; }
|
||
#overview-scroll::-webkit-scrollbar-thumb {
|
||
background: rgba(56,56,49,0.18);
|
||
border-radius: 3px;
|
||
}
|
||
|
||
/* Site-1 overview-globe base rule kept; bifrost.js drives opacity
|
||
inline after init, so transition is suppressed by JS. Until JS
|
||
runs, CSS handles the 900ms fade-in on page activation. */
|
||
|
||
/* Hero scene anchored over the Europe map. The map's right-of-
|
||
centre framing is preserved from site 1 (overview-globe CSS at
|
||
left:65%, top:55%, scale 1.35 when .is-active). The hero text
|
||
lives in the left column; the map fills the right ~2/3. */
|
||
#page-overview #hero {
|
||
min-height: 100vh;
|
||
padding-inline: var(--edge);
|
||
display: grid;
|
||
align-items: center;
|
||
position: relative;
|
||
}
|
||
#page-overview #hero .hero-wrap {
|
||
/* Constrain to the left column so Europe is visible to its right. */
|
||
max-width: 62ch;
|
||
padding-top: clamp(6rem, 16vh, 12rem);
|
||
}
|
||
|
||
/* Make sure scenes don't accidentally inherit `main { position: relative }` */
|
||
#overview-scroll > section { position: relative; z-index: 2; }
|
||
|
||
|
||
/* ============================================================
|
||
BIFROST SCENES — tokens (scoped to #page-overview only, so
|
||
they never leak to the timeline or archive pages).
|
||
Palette reconciled with site 1's Nordic Editorial system.
|
||
============================================================ */
|
||
#page-overview {
|
||
--ink: #383831; /* site 1 --on-surface (charcoal slate) */
|
||
--ink-soft: #5f5e5e; /* site 1 --on-surface-variant */
|
||
--ink-mute: #8a887f; /* site 1 --on-surface-muted */
|
||
--ink-faint: #ddd6c3; /* site 1 --surface-container-highest */
|
||
--paper: #faf6ee; /* site 1 --background */
|
||
--paper-2: #f6f2e8; /* site 1 --surface-container-low */
|
||
--paper-3: #efeadc; /* site 1 --surface-container */
|
||
--accent: #b96b58; /* site 1 --pigment-terracotta */
|
||
--ring: #6d8c7c; /* site 1 --pigment-copper */
|
||
|
||
/* aurora gradient — site-1 Archival Pigments, kept exclusive to Scene 4 */
|
||
--aurora-1: #c29d59; /* site 1 --pigment-ochre */
|
||
--aurora-2: #b96b58; /* site 1 --pigment-terracotta */
|
||
--aurora-3: #5a6d83; /* site 1 --pigment-indigo */
|
||
--aurora-4: #8d7a85; /* site 1 --pigment-heather */
|
||
|
||
--type-body: "Manrope", ui-sans-serif, system-ui, sans-serif;
|
||
--type-display: "Newsreader", Georgia, serif;
|
||
|
||
--step-hero: clamp(2.4rem, 6.2vw, 5.4rem);
|
||
--step-xl: clamp(1.8rem, 4.8vw, 4rem);
|
||
--step-lg: clamp(1.35rem, 3vw, 2.2rem);
|
||
--step-md: clamp(1rem, 1.4vw, 1.15rem);
|
||
--step-sm: clamp(0.85rem, 1vw, 0.95rem);
|
||
|
||
--edge: clamp(1.5rem, 4vw, 4rem);
|
||
}
|
||
|
||
/* ============================================================
|
||
TOKENS
|
||
============================================================ */
|
||
*, *::before, *::after { box-sizing: border-box; }
|
||
html {
|
||
-webkit-font-smoothing: antialiased;
|
||
-moz-osx-font-smoothing: grayscale;
|
||
text-rendering: optimizeLegibility;
|
||
scroll-behavior: auto; /* Lenis handles it */
|
||
}
|
||
|
||
/* Paper-grain noise overlay for tactile warmth */
|
||
/* Faint contour lines in the background for the whole page — Nordic-map motif */
|
||
/* ============================================================
|
||
LAYOUT PRIMITIVES
|
||
============================================================ */
|
||
.scene {
|
||
position: relative;
|
||
min-height: 100vh;
|
||
padding-inline: var(--edge);
|
||
display: grid;
|
||
align-items: center;
|
||
}
|
||
|
||
/* ============================================================
|
||
SCENE 1 — HERO
|
||
============================================================ */
|
||
#hero {
|
||
min-height: 100vh;
|
||
padding-top: clamp(7rem, 14vh, 11rem);
|
||
grid-template-columns: 1fr;
|
||
align-items: start;
|
||
}
|
||
.hero-wrap {
|
||
display: grid;
|
||
grid-template-columns: 1fr;
|
||
gap: clamp(2rem, 6vw, 4rem);
|
||
align-items: end;
|
||
max-width: 1600px;
|
||
width: 100%;
|
||
margin-inline: auto;
|
||
padding-top: clamp(2rem, 6vh, 4rem);
|
||
}
|
||
|
||
.eyebrow {
|
||
font-size: var(--step-sm);
|
||
letter-spacing: 0.22em;
|
||
text-transform: uppercase;
|
||
color: var(--ink-mute);
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 0.75rem;
|
||
margin-bottom: clamp(1.4rem, 4vh, 2.4rem);
|
||
font-weight: 500;
|
||
}
|
||
.eyebrow::before {
|
||
content: "";
|
||
width: 28px; height: 1px;
|
||
background: var(--ink-mute);
|
||
}
|
||
|
||
.hero-title {
|
||
font-family: var(--type-display);
|
||
font-weight: 330;
|
||
font-size: var(--step-hero);
|
||
line-height: 1.02;
|
||
letter-spacing: -0.03em;
|
||
color: var(--ink);
|
||
margin: 0;
|
||
max-width: 22ch;
|
||
}
|
||
.hero-title em {
|
||
font-style: italic;
|
||
font-weight: 340;
|
||
color: var(--accent);
|
||
}
|
||
|
||
.hero-lede {
|
||
max-width: 46ch;
|
||
font-size: var(--step-lg);
|
||
font-weight: 300;
|
||
line-height: 1.35;
|
||
color: var(--ink-soft);
|
||
margin-top: clamp(1.5rem, 4vh, 2.5rem);
|
||
letter-spacing: -0.01em;
|
||
}
|
||
|
||
.hero-foot {
|
||
position: absolute;
|
||
bottom: var(--edge);
|
||
left: var(--edge);
|
||
right: var(--edge);
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: flex-end;
|
||
font-size: var(--step-sm);
|
||
color: var(--ink-mute);
|
||
letter-spacing: 0.04em;
|
||
}
|
||
.support {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 0.75rem;
|
||
font-size: var(--step-sm);
|
||
text-transform: uppercase;
|
||
letter-spacing: 0.18em;
|
||
}
|
||
.support svg { height: 18px; width: auto; }
|
||
|
||
.scroll-hint {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 0.6rem;
|
||
text-transform: uppercase;
|
||
letter-spacing: 0.22em;
|
||
font-size: var(--step-sm);
|
||
color: var(--ink-soft);
|
||
}
|
||
.scroll-hint .arrow {
|
||
width: 28px; height: 1px; background: currentColor;
|
||
position: relative;
|
||
animation: hint 2.2s ease-in-out infinite;
|
||
}
|
||
.scroll-hint .arrow::after {
|
||
content: ""; position: absolute; right: -1px; top: -3px;
|
||
width: 7px; height: 7px; border-right: 1px solid currentColor; border-bottom: 1px solid currentColor;
|
||
transform: rotate(-45deg);
|
||
}
|
||
@keyframes hint {
|
||
0%, 100% { transform: translateX(0); opacity: 0.5; }
|
||
50% { transform: translateX(6px); opacity: 1; }
|
||
}
|
||
|
||
/* ============================================================
|
||
SCENE 2 — ARCHITECTURE (pinned, scrubbed)
|
||
4 layer-cards that fall in, stack with an offset revealing
|
||
each previous layer's bottom strip, then rearrange into a
|
||
2x2 grid on the right while explanatory copy appears on
|
||
the left.
|
||
============================================================ */
|
||
#stack-scene { position: relative; }
|
||
.stack-pin {
|
||
position: relative;
|
||
height: 100vh;
|
||
padding-inline: var(--edge);
|
||
max-width: none;
|
||
margin-inline: auto;
|
||
display: grid;
|
||
place-items: center;
|
||
padding-top: clamp(6rem, 11vh, 8.5rem); /* keep clear of brand mark */
|
||
}
|
||
|
||
/* Theatre — holds cards absolutely positioned. GSAP drives positions. */
|
||
.layer-theatre {
|
||
position: relative;
|
||
width: 100%;
|
||
height: 100%;
|
||
max-width: none;
|
||
}
|
||
|
||
/* LEFT COPY (visible only during grid phase) */
|
||
.copy-stage {
|
||
position: absolute;
|
||
left: 0;
|
||
top: 0;
|
||
bottom: 0; /* full theatre height so copy-layer can vertically center */
|
||
width: 42%;
|
||
max-width: 46ch;
|
||
z-index: 10;
|
||
pointer-events: none;
|
||
}
|
||
.copy-layer {
|
||
position: absolute;
|
||
left: 0;
|
||
right: 0;
|
||
top: 50%; /* vertically center */
|
||
will-change: opacity, transform;
|
||
}
|
||
.js .copy-layer { opacity: 0; }
|
||
.copy-layer h2 {
|
||
font-family: var(--type-display);
|
||
font-weight: 340;
|
||
font-size: var(--step-xl);
|
||
line-height: 1.04;
|
||
letter-spacing: -0.025em;
|
||
color: var(--ink);
|
||
margin: 0 0 1rem;
|
||
}
|
||
.copy-layer h2 em { font-style: italic; color: var(--accent); font-weight: 400; }
|
||
.copy-layer h2 strong { font-weight: 600; font-style: normal; }
|
||
.copy-layer p {
|
||
font-size: var(--step-lg);
|
||
font-weight: 300;
|
||
line-height: 1.35;
|
||
color: var(--ink-soft);
|
||
margin: 0;
|
||
max-width: 38ch;
|
||
letter-spacing: -0.005em;
|
||
}
|
||
.copy-layer .tag {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 0.6rem;
|
||
font-size: var(--step-sm);
|
||
text-transform: uppercase;
|
||
letter-spacing: 0.22em;
|
||
color: var(--ink-mute);
|
||
margin-bottom: 1rem;
|
||
font-weight: 500;
|
||
}
|
||
.copy-layer .tag::before {
|
||
content: "";
|
||
width: 20px; height: 1px;
|
||
background: currentColor;
|
||
}
|
||
|
||
/* -------- Layer cards -------- */
|
||
.layer-card {
|
||
position: absolute;
|
||
left: 0;
|
||
right: 0;
|
||
top: 50%;
|
||
width: auto;
|
||
transform: translateY(-50%);
|
||
transform-origin: center center;
|
||
will-change: transform, opacity;
|
||
}
|
||
.layer-card .card-eyebrow {
|
||
display: block;
|
||
font-size: var(--step-sm);
|
||
letter-spacing: 0.28em;
|
||
text-transform: uppercase;
|
||
color: var(--ink-soft);
|
||
margin: 0 0 0.9rem 0.25rem;
|
||
font-weight: 500;
|
||
will-change: opacity;
|
||
}
|
||
|
||
.card-box {
|
||
position: relative;
|
||
border-radius: 22px;
|
||
padding: clamp(1.75rem, 3.2vw, 2.8rem);
|
||
display: grid;
|
||
grid-template-columns: minmax(0, 1.2fr) minmax(0, 0.9fr);
|
||
gap: clamp(1rem, 2.5vw, 2.25rem);
|
||
align-items: center;
|
||
overflow: hidden;
|
||
/* contain: paint forces transformed children (with will-change:
|
||
transform creating compositing layers) to respect this box's
|
||
overflow clipping. Without it, the brain's counter-scale
|
||
transform during the morph escapes the box bounds. */
|
||
contain: paint;
|
||
box-shadow: 0 22px 48px -18px rgba(46,46,40,0.28), 0 8px 22px -8px rgba(46,46,40,0.16);
|
||
min-height: 240px;
|
||
}
|
||
.card-content { min-width: 0; }
|
||
.card-title {
|
||
font-family: var(--type-display);
|
||
font-weight: 330;
|
||
font-size: clamp(2.08rem, 4.3vw, 3.32rem);
|
||
line-height: 1.06;
|
||
letter-spacing: -0.018em;
|
||
margin: 0 0 1.1rem;
|
||
color: #fffcf7;
|
||
}
|
||
.card-title b { font-weight: 640; font-style: normal; }
|
||
.card-title em { font-style: italic; font-weight: 640; }
|
||
.card-body {
|
||
font-size: clamp(1.2rem, 1.5vw, 1.4rem);
|
||
line-height: 1.4;
|
||
color: #fffcf7;
|
||
margin: 0;
|
||
max-width: 38ch;
|
||
font-weight: 400;
|
||
opacity: 0.88;
|
||
will-change: opacity;
|
||
}
|
||
|
||
/* Brain icon — CSS mask filled with cream color; looks good on any card tint */
|
||
.card-brain {
|
||
width: 100%;
|
||
aspect-ratio: 20 / 17;
|
||
background-color: var(--paper);
|
||
-webkit-mask-image: var(--brain-mask);
|
||
mask-image: var(--brain-mask);
|
||
-webkit-mask-size: contain;
|
||
mask-size: contain;
|
||
-webkit-mask-position: center right;
|
||
mask-position: center right;
|
||
-webkit-mask-repeat: no-repeat;
|
||
mask-repeat: no-repeat;
|
||
opacity: 0.9;
|
||
margin-right: clamp(-3.5rem, -3vw, -1.5rem); /* bleed off right edge */
|
||
pointer-events: none;
|
||
will-change: transform;
|
||
}
|
||
|
||
/* Per-layer colours — muted Nordic mid-tones.
|
||
Dark ink text + cream brain both read well on each. */
|
||
.layer-card[data-layer="0"] .card-box { background: #7a8c70; } /* sage — AI Model */
|
||
.layer-card[data-layer="1"] .card-box { background: #7b9399; } /* slate — Knowledge */
|
||
.layer-card[data-layer="2"] .card-box { background: #b07556; } /* clay — Tools */
|
||
.layer-card[data-layer="3"] .card-box { background: #8a7a92; } /* plum — Agents */
|
||
|
||
/* z-stacking — later layers appear on top */
|
||
.layer-card[data-layer="0"] { z-index: 1; }
|
||
.layer-card[data-layer="1"] { z-index: 2; }
|
||
.layer-card[data-layer="2"] { z-index: 3; }
|
||
.layer-card[data-layer="3"] { z-index: 4; }
|
||
|
||
/* -------- GRID PHASE — cards become aligned SQUARES -------- */
|
||
/* When the .in-grid class is toggled on .layer-theatre, each card-box
|
||
becomes a 20vw square (via .in-grid .card-box), centered in its
|
||
full-width parent. Inside, the layout switches: the outside eyebrow
|
||
hides; a dedicated grid-label shows at the top; the long title and
|
||
body hide; the brain fills the rest centered.
|
||
|
||
The grid-label is ALWAYS in the DOM (absolutely-positioned inside
|
||
card-box with opacity:0 by default) so GSAP can fade it in smoothly
|
||
during the morph transition, rather than it snapping on when the
|
||
.in-grid class applies. */
|
||
|
||
.card-grid-label {
|
||
position: absolute;
|
||
left: clamp(1rem, 1.4vw, 1.4rem);
|
||
top: clamp(1rem, 1.4vw, 1.4rem);
|
||
font-size: clamp(0.75rem, 0.95vw, 0.95rem);
|
||
letter-spacing: 0.22em;
|
||
text-transform: uppercase;
|
||
color: #fffcf7;
|
||
opacity: 0;
|
||
font-weight: 500;
|
||
text-align: left;
|
||
line-height: 1;
|
||
pointer-events: none;
|
||
z-index: 2;
|
||
}
|
||
|
||
.in-grid .card-box {
|
||
max-width: 20vw;
|
||
width: 20vw;
|
||
aspect-ratio: 1 / 1;
|
||
margin: 0 auto;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: stretch;
|
||
justify-content: flex-start;
|
||
grid-template-columns: unset;
|
||
grid-template-rows: unset;
|
||
padding: clamp(1rem, 1.4vw, 1.4rem);
|
||
gap: 0;
|
||
min-height: 0;
|
||
border-radius: 16px;
|
||
}
|
||
|
||
/* In grid state the label is visible; position resets inside the
|
||
flex column */
|
||
.in-grid .card-grid-label {
|
||
opacity: 0.88;
|
||
position: relative;
|
||
left: auto;
|
||
top: auto;
|
||
margin: 0 0 0.5rem;
|
||
}
|
||
|
||
/* Hide long title + body in grid phase */
|
||
.in-grid .card-content { display: none; }
|
||
|
||
/* Brain fills remaining space, centered. */
|
||
.in-grid .card-brain {
|
||
margin: 0;
|
||
flex: 1 1 auto;
|
||
width: 100%;
|
||
aspect-ratio: auto;
|
||
-webkit-mask-position: center;
|
||
mask-position: center;
|
||
-webkit-mask-size: 90% auto;
|
||
mask-size: 90% auto;
|
||
opacity: 0.85;
|
||
}
|
||
|
||
/* Hide the outside-box eyebrow during grid phase */
|
||
.in-grid .layer-card .card-eyebrow {
|
||
opacity: 0 !important;
|
||
pointer-events: none;
|
||
}
|
||
|
||
/* ============================================================
|
||
SCENE 3 — SLIDE 11 — words fly in
|
||
============================================================ */
|
||
#words-scene {
|
||
position: relative;
|
||
height: 260vh;
|
||
}
|
||
.words-pin {
|
||
position: sticky;
|
||
top: 0;
|
||
height: 100vh;
|
||
display: grid;
|
||
place-items: center;
|
||
padding-inline: var(--edge);
|
||
}
|
||
.words {
|
||
font-family: var(--type-display);
|
||
font-weight: 320;
|
||
font-size: clamp(2rem, 6vw, 5.2rem);
|
||
line-height: 1.06;
|
||
letter-spacing: -0.025em;
|
||
color: var(--ink);
|
||
max-width: 22ch;
|
||
margin: 0 auto;
|
||
text-align: left;
|
||
}
|
||
.words .w {
|
||
display: inline-block;
|
||
will-change: transform, opacity;
|
||
margin-right: 0.25em;
|
||
}
|
||
.js .words .w { opacity: 0; }
|
||
.words .w.hi {
|
||
font-style: italic;
|
||
color: var(--accent);
|
||
font-weight: 420;
|
||
}
|
||
|
||
/* ============================================================
|
||
SCENE 4 — PROJECT BIFROST REVEAL
|
||
============================================================ */
|
||
#bifrost {
|
||
position: relative;
|
||
min-height: 200vh;
|
||
}
|
||
.bifrost-pin {
|
||
position: sticky;
|
||
top: 0;
|
||
height: 100vh;
|
||
display: grid;
|
||
place-items: center;
|
||
overflow: hidden;
|
||
}
|
||
.bifrost-stage {
|
||
position: relative;
|
||
width: 100%;
|
||
height: 100%;
|
||
display: grid;
|
||
place-items: center;
|
||
}
|
||
|
||
/* the arc — bifrost bridge */
|
||
.arc-wrap {
|
||
position: absolute;
|
||
left: 50%;
|
||
top: 50%;
|
||
width: min(120vw, 1400px);
|
||
translate: -50% -50%;
|
||
pointer-events: none;
|
||
will-change: opacity, transform;
|
||
}
|
||
.js .arc-wrap { opacity: 0; }
|
||
.arc-wrap svg { display: block; width: 100%; height: auto; overflow: visible; }
|
||
|
||
.bifrost-text {
|
||
position: relative;
|
||
z-index: 2;
|
||
text-align: center;
|
||
max-width: 90vw;
|
||
padding: 0 var(--edge);
|
||
}
|
||
.bifrost-eyebrow {
|
||
font-size: var(--step-sm);
|
||
text-transform: uppercase;
|
||
letter-spacing: 0.32em;
|
||
color: var(--ink-soft);
|
||
margin-bottom: clamp(1rem, 3vh, 1.8rem);
|
||
will-change: opacity, transform;
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 0.8rem;
|
||
}
|
||
.js .bifrost-eyebrow { opacity: 0; }
|
||
.bifrost-eyebrow::before,
|
||
.bifrost-eyebrow::after {
|
||
content: "";
|
||
width: 28px; height: 1px;
|
||
background: currentColor;
|
||
}
|
||
.bifrost-name {
|
||
font-family: var(--type-display);
|
||
font-weight: 320;
|
||
font-size: clamp(3rem, 10vw, 9rem);
|
||
line-height: 0.95;
|
||
letter-spacing: -0.04em;
|
||
color: var(--ink);
|
||
margin: 0;
|
||
display: flex;
|
||
justify-content: center;
|
||
gap: 0.15em;
|
||
flex-wrap: wrap;
|
||
}
|
||
.bifrost-name .token {
|
||
display: inline-block;
|
||
will-change: transform, opacity, filter;
|
||
}
|
||
.js .bifrost-name .token { opacity: 0; }
|
||
.bifrost-name .token.accent {
|
||
font-style: italic;
|
||
background: linear-gradient(100deg, var(--aurora-1) 0%, var(--aurora-2) 32%, var(--aurora-3) 68%, var(--aurora-4) 100%);
|
||
-webkit-background-clip: text;
|
||
background-clip: text;
|
||
color: transparent;
|
||
}
|
||
.bifrost-sub {
|
||
margin-top: clamp(1.4rem, 4vh, 2.2rem);
|
||
font-size: var(--step-lg);
|
||
font-weight: 300;
|
||
color: var(--ink-soft);
|
||
max-width: 44ch;
|
||
margin-inline: auto;
|
||
line-height: 1.35;
|
||
will-change: opacity, transform;
|
||
}
|
||
.js .bifrost-sub { opacity: 0; }
|
||
.bifrost-sub em {
|
||
font-style: italic;
|
||
color: var(--ink);
|
||
}
|
||
|
||
/* Credits */
|
||
.credits {
|
||
position: relative;
|
||
padding: clamp(4rem, 12vh, 8rem) var(--edge) clamp(2rem, 4vh, 3rem);
|
||
border-top: 1px solid rgba(46,46,40,0.1);
|
||
margin-top: 8vh;
|
||
font-size: var(--step-sm);
|
||
color: var(--ink-mute);
|
||
display: flex;
|
||
justify-content: space-between;
|
||
gap: 2rem;
|
||
flex-wrap: wrap;
|
||
letter-spacing: 0.02em;
|
||
}
|
||
.credits .col a {
|
||
color: var(--ink-soft);
|
||
text-decoration: none;
|
||
border-bottom: 1px solid rgba(46,46,40,0.2);
|
||
padding-bottom: 1px;
|
||
transition: border-color 0.2s, color 0.2s;
|
||
}
|
||
.credits .col a:hover { color: var(--accent); border-color: var(--accent); }
|
||
|
||
/* ============================================================
|
||
RESPONSIVE
|
||
============================================================ */
|
||
@media (max-width: 900px) {
|
||
#page-overview { --edge: 1.25rem; }
|
||
|
||
.meta { display: none; }
|
||
.rail { display: none; }
|
||
.brand-sub { display: none; } /* too cramped on small screens */
|
||
|
||
.stack-pin {
|
||
padding-top: 5rem;
|
||
}
|
||
.copy-stage {
|
||
width: 100%;
|
||
max-width: 100%;
|
||
position: relative;
|
||
top: auto;
|
||
transform: none;
|
||
}
|
||
.copy-layer h2 { font-size: clamp(1.6rem, 5.5vw, 2.2rem); }
|
||
.copy-layer p { font-size: 1rem; }
|
||
|
||
/* Cards shrink on mobile — single column body + smaller brain */
|
||
.layer-card { width: 92%; }
|
||
.card-box {
|
||
grid-template-columns: 1fr;
|
||
min-height: 0;
|
||
padding: 1.4rem;
|
||
}
|
||
.card-title { font-size: clamp(1.25rem, 5vw, 1.7rem); }
|
||
.card-body { font-size: 0.92rem; }
|
||
.card-brain {
|
||
max-width: 180px;
|
||
justify-self: end;
|
||
margin-right: -1rem;
|
||
aspect-ratio: 20 / 14;
|
||
}
|
||
|
||
.hero-foot { position: static; margin-top: 3rem; flex-direction: column; gap: 1.5rem; align-items: flex-start; }
|
||
#hero { padding-top: 5rem; padding-bottom: 3rem; min-height: auto; }
|
||
}
|
||
|
||
@media (max-width: 520px) {
|
||
.hero-title { font-size: clamp(2.2rem, 10vw, 3.2rem); }
|
||
.hero-lede { font-size: 1.05rem; }
|
||
.card-eyebrow { font-size: 0.72rem !important; letter-spacing: 0.2em; }
|
||
}
|
||
|
||
/* ============================================================
|
||
REDUCED MOTION — degraded version reveals all content
|
||
============================================================ */
|
||
@media (prefers-reduced-motion: reduce) {
|
||
#words-scene, #bifrost { height: auto !important; min-height: 0 !important; }
|
||
.stack-pin, .words-pin, .bifrost-pin {
|
||
position: relative !important;
|
||
height: auto !important;
|
||
min-height: auto;
|
||
padding-block: 4rem;
|
||
display: block !important;
|
||
}
|
||
.layer-theatre {
|
||
position: relative !important;
|
||
height: auto !important;
|
||
}
|
||
.layer-card {
|
||
position: relative !important;
|
||
left: auto !important; top: auto !important;
|
||
transform: none !important;
|
||
margin: 0 auto 2rem !important;
|
||
width: 100% !important;
|
||
max-width: 900px;
|
||
}
|
||
.card-body { opacity: 1 !important; }
|
||
.card-eyebrow { opacity: 1 !important; }
|
||
.copy-stage {
|
||
position: relative !important;
|
||
top: auto !important;
|
||
transform: none !important;
|
||
width: 100% !important;
|
||
max-width: 900px;
|
||
margin: 2rem auto 0;
|
||
}
|
||
.copy-layer { position: static !important; opacity: 1 !important; transform: none !important; margin-bottom: 3rem; }
|
||
.brand-sub { opacity: 1 !important; transform: none !important; }
|
||
.words .w { opacity: 1 !important; transform: none !important; }
|
||
.bifrost-eyebrow, .bifrost-sub, .arc-wrap { opacity: 1 !important; transform: translate(-50%, -50%) !important; }
|
||
.arc-wrap { transform: translate(-50%, -50%) !important; }
|
||
.bifrost-name .token { opacity: 1 !important; transform: none !important; filter: none !important; }
|
||
.scroll-hint .arrow { animation: none; }
|
||
}
|
||
/* Illustration data URIs — defined once, referenced by both
|
||
the treasure-map stops and the summary cards below. */
|
||
/* Illustration paths — real SVG files, self-hosted under protected/fenja/illustrations/ */
|
||
#page-overview {
|
||
--illust-community: url("/fenja/illustrations/community.svg");
|
||
--illust-council: url("/fenja/illustrations/council.svg");
|
||
--illust-pilot: url("/fenja/illustrations/pilot.svg");
|
||
}
|
||
/* ============================================================
|
||
SCENE 5 — PROJECT BIFROST · WHAT IT MEANS (treasure-map)
|
||
A meandering path drawn down the page with one intro stop
|
||
and three component stops revealed sequentially as the user
|
||
scrolls. Each component pairs body copy with an illustration
|
||
that fades in alongside it.
|
||
============================================================ */
|
||
#bifrost-meaning {
|
||
position: relative;
|
||
padding: clamp(6rem, 14vh, 12rem) var(--edge) clamp(4rem, 10vh, 8rem);
|
||
overflow: hidden;
|
||
}
|
||
|
||
.map-intro {
|
||
max-width: 60ch;
|
||
margin: 0 auto clamp(5rem, 12vh, 9rem);
|
||
text-align: center;
|
||
position: relative;
|
||
z-index: 2;
|
||
}
|
||
.map-intro .map-eyebrow {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 0.75rem;
|
||
font-size: var(--step-sm);
|
||
letter-spacing: 0.32em;
|
||
text-transform: uppercase;
|
||
color: var(--ink-mute);
|
||
margin-bottom: clamp(1.4rem, 3.5vh, 2rem);
|
||
font-weight: 500;
|
||
}
|
||
.map-intro .map-eyebrow::before,
|
||
.map-intro .map-eyebrow::after {
|
||
content: "";
|
||
width: 28px; height: 1px;
|
||
background: currentColor;
|
||
}
|
||
.map-title {
|
||
font-family: var(--type-display);
|
||
font-weight: 330;
|
||
font-size: var(--step-xl);
|
||
line-height: 1.04;
|
||
letter-spacing: -0.025em;
|
||
color: var(--ink);
|
||
margin: 0 0 clamp(1rem, 2.5vh, 1.5rem);
|
||
}
|
||
.map-title em {
|
||
font-style: italic;
|
||
color: var(--accent);
|
||
font-weight: 400;
|
||
}
|
||
.map-lede {
|
||
font-size: var(--step-lg);
|
||
font-weight: 300;
|
||
line-height: 1.4;
|
||
color: var(--ink-soft);
|
||
margin: 0 auto;
|
||
max-width: 56ch;
|
||
letter-spacing: -0.005em;
|
||
}
|
||
.map-lede em {
|
||
font-style: italic;
|
||
color: var(--ink);
|
||
font-weight: 400;
|
||
}
|
||
|
||
/* The map canvas — relative container holding the path SVG and stops */
|
||
.map-canvas {
|
||
position: relative;
|
||
max-width: 1200px;
|
||
margin: 0 auto;
|
||
}
|
||
|
||
/* Wandering path — SVG stretched to canvas dimensions */
|
||
.map-path {
|
||
position: absolute;
|
||
inset: 0;
|
||
width: 100%;
|
||
height: 100%;
|
||
pointer-events: none;
|
||
z-index: 0;
|
||
overflow: visible;
|
||
}
|
||
.map-path .path-bg {
|
||
fill: none;
|
||
stroke: var(--ink);
|
||
stroke-opacity: 0.18;
|
||
stroke-width: 1.2;
|
||
stroke-dasharray: 4 6;
|
||
stroke-linecap: round;
|
||
}
|
||
.map-path .path-draw {
|
||
fill: none;
|
||
stroke: var(--accent);
|
||
stroke-opacity: 0.7;
|
||
stroke-width: 1.6;
|
||
stroke-linecap: round;
|
||
}
|
||
|
||
/* A stop — three-column grid: text | dot | image (alternating sides) */
|
||
.map-stop {
|
||
position: relative;
|
||
display: grid;
|
||
grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
|
||
align-items: center;
|
||
gap: clamp(2rem, 5vw, 4.5rem);
|
||
margin-bottom: clamp(7rem, 14vh, 11rem);
|
||
z-index: 2;
|
||
}
|
||
.map-stop:last-child { margin-bottom: 0; }
|
||
|
||
/* Intro stop is single-column, centered, no image */
|
||
.map-stop--intro {
|
||
grid-template-columns: 1fr;
|
||
text-align: center;
|
||
margin-bottom: clamp(7rem, 14vh, 11rem);
|
||
justify-items: center;
|
||
}
|
||
.map-stop--intro .stop-content {
|
||
max-width: 46ch;
|
||
margin: 0 auto;
|
||
}
|
||
|
||
/* Dot anchor — sits on the path */
|
||
.dot-anchor {
|
||
position: relative;
|
||
width: 16px;
|
||
height: 16px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
flex-shrink: 0;
|
||
z-index: 3;
|
||
}
|
||
.map-stop[data-side="left"] .dot-anchor { grid-column: 2; }
|
||
.map-stop[data-side="right"] .dot-anchor { grid-column: 2; }
|
||
|
||
.dot {
|
||
position: relative;
|
||
width: 14px;
|
||
height: 14px;
|
||
border-radius: 50%;
|
||
background: var(--paper);
|
||
border: 2px solid var(--accent);
|
||
/* paper-coloured ring masks the path passing under the dot */
|
||
box-shadow: 0 0 0 6px var(--paper);
|
||
z-index: 2;
|
||
}
|
||
/* Soft pulse halo on the dot */
|
||
.dot::after {
|
||
content: "";
|
||
position: absolute;
|
||
inset: -10px;
|
||
border-radius: 50%;
|
||
border: 1px solid var(--accent);
|
||
opacity: 0.35;
|
||
z-index: -1;
|
||
}
|
||
|
||
.map-stop--intro .dot-anchor {
|
||
margin-bottom: clamp(1.5rem, 4vh, 2.5rem);
|
||
}
|
||
|
||
/* Text and image columns — alternating sides */
|
||
.map-stop[data-side="left"] .stop-content { grid-column: 1; text-align: left; }
|
||
.map-stop[data-side="left"] .stop-image { grid-column: 3; justify-self: start; }
|
||
.map-stop[data-side="right"] .stop-image { grid-column: 1; justify-self: end; }
|
||
.map-stop[data-side="right"] .stop-content { grid-column: 3; text-align: left; }
|
||
|
||
.stop-content {
|
||
font-family: var(--type-body);
|
||
}
|
||
.stop-eyebrow {
|
||
font-size: var(--step-sm);
|
||
letter-spacing: 0.22em;
|
||
text-transform: uppercase;
|
||
color: var(--ink-mute);
|
||
font-weight: 500;
|
||
display: block;
|
||
margin-bottom: 0.6rem;
|
||
}
|
||
.stop-title {
|
||
font-family: var(--type-display);
|
||
font-weight: 340;
|
||
font-size: clamp(2rem, 4.2vw, 3.2rem);
|
||
line-height: 1.04;
|
||
letter-spacing: -0.025em;
|
||
color: var(--ink);
|
||
margin: 0 0 clamp(0.6rem, 1.5vh, 1rem);
|
||
}
|
||
.stop-title em {
|
||
font-style: italic;
|
||
color: var(--accent);
|
||
font-weight: 400;
|
||
}
|
||
.stop-sub {
|
||
font-family: var(--type-display);
|
||
font-style: italic;
|
||
font-size: clamp(1.15rem, 1.8vw, 1.5rem);
|
||
font-weight: 300;
|
||
color: var(--ink-soft);
|
||
margin: 0 0 1.1rem;
|
||
letter-spacing: -0.005em;
|
||
line-height: 1.3;
|
||
}
|
||
.stop-body {
|
||
font-size: var(--step-md);
|
||
line-height: 1.55;
|
||
color: var(--ink-soft);
|
||
margin: 0;
|
||
max-width: 42ch;
|
||
font-weight: 400;
|
||
}
|
||
.stop-intro-text {
|
||
font-family: var(--type-display);
|
||
font-style: italic;
|
||
font-weight: 300;
|
||
font-size: clamp(1.4rem, 2.4vw, 2rem);
|
||
line-height: 1.35;
|
||
color: var(--ink-soft);
|
||
margin: 0;
|
||
letter-spacing: -0.01em;
|
||
}
|
||
.stop-intro-text em {
|
||
font-style: normal;
|
||
color: var(--accent);
|
||
font-weight: 400;
|
||
}
|
||
|
||
.stop-image {
|
||
width: 100%;
|
||
max-width: 380px;
|
||
}
|
||
.stop-image img {
|
||
display: block;
|
||
width: 100%;
|
||
height: auto;
|
||
/* faint blend with the cream paper */
|
||
mix-blend-mode: multiply;
|
||
}
|
||
|
||
/* Initial hidden state — only when JS is enabled */
|
||
.js .map-stop .dot {
|
||
opacity: 0;
|
||
transform: scale(0.2);
|
||
transform-origin: center;
|
||
will-change: opacity, transform;
|
||
}
|
||
.js .map-stop .stop-content > *,
|
||
.js .map-stop .stop-image {
|
||
opacity: 0;
|
||
transform: translateY(28px);
|
||
will-change: opacity, transform;
|
||
}
|
||
|
||
/* ============================================================
|
||
SCENE 6 — PROJECT BIFROST · JOIN
|
||
A large call-to-action that, on click, crossfades to a
|
||
confirmation panel listing what happens next. Below, a
|
||
three-column footer row: "Project Bifrost" wordmark (left),
|
||
Fenja AI logo (centre), Innovationsfonden mark (right).
|
||
============================================================ */
|
||
#bifrost-join {
|
||
position: relative;
|
||
padding: clamp(5rem, 12vh, 10rem) var(--edge) clamp(2rem, 5vh, 3.5rem);
|
||
overflow: hidden;
|
||
min-height: 90vh;
|
||
display: flex;
|
||
flex-direction: column;
|
||
}
|
||
|
||
/* Stage holds BOTH the CTA and the confirmation panel, stacked in
|
||
the SAME grid cell. The cell auto-sizes to whichever panel is
|
||
taller (on mobile the confirmation list is much taller than the
|
||
CTA), so neither panel ever overflows into the footer. GSAP's
|
||
opacity/y tweens handle the crossfade. */
|
||
.join-stage {
|
||
position: relative;
|
||
flex: 1 1 auto;
|
||
max-width: 1100px;
|
||
margin: 0 auto;
|
||
width: 100%;
|
||
display: grid;
|
||
place-items: center;
|
||
padding: clamp(1rem, 4vh, 3rem) 0;
|
||
}
|
||
|
||
.join-panel {
|
||
grid-column: 1;
|
||
grid-row: 1;
|
||
width: 100%;
|
||
display: flex;
|
||
flex-direction: column;
|
||
justify-content: center;
|
||
align-items: center;
|
||
text-align: center;
|
||
}
|
||
|
||
/* ---------- CTA state ---------- */
|
||
.join-cta .join-eyebrow {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 0.75rem;
|
||
font-size: var(--step-sm);
|
||
letter-spacing: 0.32em;
|
||
text-transform: uppercase;
|
||
color: var(--ink-mute);
|
||
margin-bottom: clamp(1.4rem, 3.5vh, 2rem);
|
||
font-weight: 500;
|
||
}
|
||
.join-cta .join-eyebrow::before,
|
||
.join-cta .join-eyebrow::after {
|
||
content: "";
|
||
width: 28px; height: 1px;
|
||
background: currentColor;
|
||
}
|
||
|
||
.join-cta .join-headline {
|
||
font-family: var(--type-display);
|
||
font-weight: 320;
|
||
font-size: clamp(2.4rem, 6.2vw, 5.4rem);
|
||
line-height: 1.04;
|
||
letter-spacing: -0.035em;
|
||
color: var(--ink);
|
||
margin: 0 auto clamp(2.6rem, 6vh, 4rem);
|
||
max-width: 20ch;
|
||
}
|
||
.join-cta .join-headline em {
|
||
font-style: italic;
|
||
background: linear-gradient(100deg, var(--aurora-1) 0%, var(--aurora-2) 32%, var(--aurora-3) 68%, var(--aurora-4) 100%);
|
||
-webkit-background-clip: text;
|
||
background-clip: text;
|
||
color: transparent;
|
||
font-weight: 340;
|
||
}
|
||
|
||
/* The button itself — pill shape with the terracotta accent */
|
||
.join-button {
|
||
font-family: var(--type-body);
|
||
font-size: clamp(1.02rem, 1.35vw, 1.18rem);
|
||
font-weight: 600;
|
||
letter-spacing: 0.08em;
|
||
text-transform: uppercase;
|
||
color: var(--paper);
|
||
background: var(--accent);
|
||
border: none;
|
||
padding: clamp(1.1rem, 2.2vh, 1.45rem) clamp(2rem, 3.8vw, 2.8rem);
|
||
border-radius: 100px;
|
||
cursor: pointer;
|
||
position: relative;
|
||
overflow: hidden;
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 0.85rem;
|
||
box-shadow: 0 14px 34px -14px rgba(164, 85, 59, 0.55),
|
||
0 4px 12px -4px rgba(46, 46, 40, 0.2);
|
||
transition: transform 0.25s cubic-bezier(0.2, 0.8, 0.2, 1),
|
||
box-shadow 0.25s ease,
|
||
background-color 0.25s ease;
|
||
}
|
||
.join-button:hover {
|
||
transform: translateY(-2px);
|
||
box-shadow: 0 22px 44px -14px rgba(164, 85, 59, 0.65),
|
||
0 8px 18px -4px rgba(46, 46, 40, 0.25);
|
||
background: #b55e42;
|
||
}
|
||
.join-button:active {
|
||
transform: translateY(0);
|
||
}
|
||
.join-button:focus-visible {
|
||
outline: 2px solid var(--ring);
|
||
outline-offset: 4px;
|
||
}
|
||
.join-button:disabled {
|
||
cursor: default;
|
||
}
|
||
.join-button .arrow {
|
||
display: inline-block;
|
||
width: 18px; height: 1.5px;
|
||
background: currentColor;
|
||
position: relative;
|
||
transition: transform 0.25s cubic-bezier(0.2, 0.8, 0.2, 1);
|
||
}
|
||
.join-button .arrow::after {
|
||
content: "";
|
||
position: absolute;
|
||
right: -1px; top: -4px;
|
||
width: 9px; height: 9px;
|
||
border-right: 1.5px solid currentColor;
|
||
border-bottom: 1.5px solid currentColor;
|
||
transform: rotate(-45deg);
|
||
}
|
||
.join-button:hover .arrow {
|
||
transform: translateX(4px);
|
||
}
|
||
|
||
.join-cta .join-subtext {
|
||
margin: clamp(1.6rem, 4vh, 2.4rem) auto 0;
|
||
font-size: var(--step-sm);
|
||
color: var(--ink-mute);
|
||
letter-spacing: 0.04em;
|
||
max-width: 40ch;
|
||
}
|
||
|
||
/* ---------- Confirmation state ---------- */
|
||
.join-confirmation {
|
||
pointer-events: none; /* enabled by JS after fade-in completes */
|
||
}
|
||
.join-confirmation .confirm-eyebrow {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 0.75rem;
|
||
font-size: var(--step-sm);
|
||
letter-spacing: 0.32em;
|
||
text-transform: uppercase;
|
||
color: var(--accent);
|
||
margin-bottom: clamp(1.4rem, 3vh, 1.8rem);
|
||
font-weight: 500;
|
||
}
|
||
.join-confirmation .confirm-eyebrow::before,
|
||
.join-confirmation .confirm-eyebrow::after {
|
||
content: "";
|
||
width: 22px; height: 1px;
|
||
background: currentColor;
|
||
opacity: 0.7;
|
||
}
|
||
.join-confirmation .confirm-headline {
|
||
font-family: var(--type-display);
|
||
font-weight: 330;
|
||
font-size: clamp(2rem, 5vw, 3.6rem);
|
||
line-height: 1.06;
|
||
letter-spacing: -0.03em;
|
||
color: var(--ink);
|
||
margin: 0 auto;
|
||
max-width: 22ch;
|
||
}
|
||
.join-confirmation .confirm-headline em {
|
||
font-style: italic;
|
||
color: var(--accent);
|
||
font-weight: 380;
|
||
}
|
||
|
||
.confirm-list {
|
||
list-style: none;
|
||
padding: 0;
|
||
margin: clamp(2rem, 5vh, 3.2rem) auto 0;
|
||
max-width: 62ch;
|
||
text-align: left;
|
||
}
|
||
.confirm-list li {
|
||
padding: clamp(1rem, 2.2vh, 1.4rem) 0 clamp(1rem, 2.2vh, 1.4rem) clamp(2.6rem, 4vw, 3.2rem);
|
||
position: relative;
|
||
font-size: var(--step-md);
|
||
line-height: 1.55;
|
||
color: var(--ink-soft);
|
||
border-top: 1px solid rgba(46, 46, 40, 0.12);
|
||
}
|
||
.confirm-list li:first-child { border-top: none; }
|
||
.confirm-list li em {
|
||
font-family: var(--type-display);
|
||
font-style: italic;
|
||
font-weight: 400;
|
||
color: var(--ink);
|
||
}
|
||
/* Terracotta circle marker + cream check — staged with a CSS
|
||
transition that fires when JS adds `.is-checked` to each <li>. */
|
||
.confirm-list li::before {
|
||
content: "";
|
||
position: absolute;
|
||
left: 0;
|
||
top: clamp(0.95rem, 2.2vh, 1.35rem);
|
||
width: clamp(1.4rem, 2.3vw, 1.7rem);
|
||
height: clamp(1.4rem, 2.3vw, 1.7rem);
|
||
background: var(--accent);
|
||
border-radius: 50%;
|
||
opacity: 0;
|
||
transform: scale(0.4);
|
||
transition: opacity 0.35s ease,
|
||
transform 0.4s cubic-bezier(0.3, 1.5, 0.5, 1);
|
||
}
|
||
.confirm-list li::after {
|
||
content: "";
|
||
position: absolute;
|
||
left: clamp(0.42rem, 0.7vw, 0.55rem);
|
||
top: clamp(1.5rem, 3vh, 1.95rem);
|
||
width: clamp(0.58rem, 0.9vw, 0.72rem);
|
||
height: clamp(0.3rem, 0.5vw, 0.38rem);
|
||
border-left: 2px solid var(--paper);
|
||
border-bottom: 2px solid var(--paper);
|
||
transform: rotate(-45deg) scale(0.4);
|
||
transform-origin: center;
|
||
opacity: 0;
|
||
transition: opacity 0.3s ease 0.15s,
|
||
transform 0.3s cubic-bezier(0.3, 1.5, 0.5, 1) 0.15s;
|
||
}
|
||
.confirm-list li.is-checked::before {
|
||
opacity: 1;
|
||
transform: scale(1);
|
||
}
|
||
.confirm-list li.is-checked::after {
|
||
opacity: 1;
|
||
transform: rotate(-45deg) scale(1);
|
||
}
|
||
|
||
/* ---------- Footer row (three brand marks, equal weight) ---------- */
|
||
.join-footer {
|
||
margin-top: auto;
|
||
padding-top: clamp(2.5rem, 6vh, 4rem);
|
||
padding-bottom: clamp(0.5rem, 2vh, 1.5rem);
|
||
border-top: 1px solid rgba(46, 46, 40, 0.1);
|
||
display: grid;
|
||
grid-template-columns: 1fr 1fr 1fr;
|
||
align-items: center;
|
||
gap: clamp(1rem, 3vw, 3rem);
|
||
max-width: 1200px;
|
||
margin-left: auto;
|
||
margin-right: auto;
|
||
width: 100%;
|
||
/* Single source of truth for how tall each footer item should be.
|
||
All three items are sized against this — the Fenja SVG fills it,
|
||
the Innovationsfonden mark scales its icon and text against it,
|
||
and the "Project Bifrost" text picks a font-size from it. */
|
||
--foot-h: clamp(34px, 4.4vh, 52px);
|
||
}
|
||
.join-footer > * {
|
||
height: var(--foot-h);
|
||
display: flex;
|
||
align-items: center;
|
||
}
|
||
|
||
/* Left: "Project Bifrost" in Newsreader, italic accent on Bifrost */
|
||
.join-footer .foot-project {
|
||
justify-self: start;
|
||
font-family: var(--type-display);
|
||
font-weight: 340;
|
||
font-size: calc(var(--foot-h) * 0.54);
|
||
letter-spacing: -0.01em;
|
||
color: var(--ink);
|
||
white-space: nowrap;
|
||
line-height: 1;
|
||
}
|
||
.join-footer .foot-project em {
|
||
font-style: italic;
|
||
color: var(--accent);
|
||
font-weight: 360;
|
||
margin-left: 0.2em;
|
||
}
|
||
|
||
/* Centre: the Fenja AI SVG logo */
|
||
.join-footer .foot-fenja {
|
||
justify-self: center;
|
||
}
|
||
.join-footer .foot-fenja svg {
|
||
height: 100%;
|
||
width: auto;
|
||
display: block;
|
||
}
|
||
|
||
/* Right: Innovationsfonden — hybrid of a small SVG slanted "I" mark
|
||
plus HTML text. Using HTML text instead of SVG <text> avoids
|
||
text-metric overflow clipping at the section's right edge. */
|
||
.join-footer .foot-innov {
|
||
justify-self: end;
|
||
gap: calc(var(--foot-h) * 0.12);
|
||
color: #0e3a48;
|
||
font-family: var(--type-body);
|
||
font-weight: 700;
|
||
font-size: calc(var(--foot-h) * 0.46);
|
||
letter-spacing: -0.015em;
|
||
line-height: 1;
|
||
white-space: nowrap;
|
||
}
|
||
.join-footer .foot-innov .innov-mark {
|
||
height: calc(var(--foot-h) * 0.88);
|
||
width: auto;
|
||
display: block;
|
||
fill: currentColor;
|
||
}
|
||
|
||
/* Hidden initial states when JS enabled (scroll-triggered reveals) */
|
||
.js .join-cta,
|
||
.js .join-confirmation,
|
||
.js .join-footer > * {
|
||
opacity: 0;
|
||
transform: translateY(22px);
|
||
will-change: opacity, transform;
|
||
}
|
||
|
||
/* ============================================================
|
||
RESPONSIVE — additions for the new sections
|
||
(independent of the existing @media block above)
|
||
============================================================ */
|
||
@media (max-width: 900px) {
|
||
/* Treasure map collapses to a centred vertical timeline */
|
||
.map-stop,
|
||
.map-stop--intro {
|
||
grid-template-columns: 1fr;
|
||
text-align: center;
|
||
margin-bottom: clamp(4.5rem, 10vh, 7rem);
|
||
gap: clamp(1.2rem, 3vw, 2rem);
|
||
justify-items: center;
|
||
}
|
||
.map-stop .dot-anchor {
|
||
grid-column: 1 !important;
|
||
margin: 0 auto;
|
||
order: 1;
|
||
}
|
||
.map-stop .stop-image {
|
||
grid-column: 1 !important;
|
||
justify-self: center !important;
|
||
max-width: 280px;
|
||
margin: 0 auto;
|
||
order: 2;
|
||
/* Paper-coloured backdrop hides the centred rail behind the
|
||
illustration on mobile. The illustration uses multiply blend
|
||
which then composites against this paper colour (same as the
|
||
page background) — visually unchanged, but no rail bleed. */
|
||
background: var(--paper);
|
||
}
|
||
.map-stop .stop-content {
|
||
grid-column: 1 !important;
|
||
text-align: center !important;
|
||
order: 3;
|
||
/* Same trick for the text block — keeps the rail from showing
|
||
through inter-line spacing. Generous vertical padding so the
|
||
rail "ducks under" the whole text region cleanly. */
|
||
background: var(--paper);
|
||
padding: 0.75rem 0.5rem;
|
||
max-width: 90%;
|
||
}
|
||
.stop-body {
|
||
max-width: 42ch;
|
||
margin: 0 auto !important;
|
||
}
|
||
/* Same backdrop for the intro paragraph */
|
||
.map-stop--intro .stop-content {
|
||
background: var(--paper);
|
||
padding: 0.75rem 1rem;
|
||
}
|
||
/* Dot already has a paper-coloured ring (box-shadow) so the
|
||
rail doesn't show through it — no extra work needed there. */
|
||
|
||
/* Replace the curving SVG path with a clean vertical rail */
|
||
.map-path { display: none; }
|
||
.map-canvas::before {
|
||
content: "";
|
||
position: absolute;
|
||
left: 50%;
|
||
top: 0;
|
||
bottom: 0;
|
||
width: 1px;
|
||
background: rgba(46, 46, 40, 0.18);
|
||
transform: translateX(-50%);
|
||
z-index: 0;
|
||
}
|
||
.map-canvas::after {
|
||
/* the "drawn" overlay rail in accent — height set by JS */
|
||
content: "";
|
||
position: absolute;
|
||
left: 50%;
|
||
top: 0;
|
||
width: 1.5px;
|
||
height: var(--rail-progress, 0%);
|
||
background: var(--accent);
|
||
opacity: 0.7;
|
||
transform: translateX(-50%);
|
||
z-index: 1;
|
||
transition: none;
|
||
}
|
||
|
||
/* Join footer stacks vertically on mobile, each mark centred */
|
||
.join-footer {
|
||
grid-template-columns: 1fr;
|
||
gap: clamp(1.8rem, 4vh, 2.4rem);
|
||
justify-items: center;
|
||
text-align: center;
|
||
}
|
||
.join-footer > * {
|
||
justify-self: center !important;
|
||
}
|
||
|
||
/* CTA headline + button tighter on phones */
|
||
.join-cta .join-headline {
|
||
font-size: clamp(2rem, 7vw, 3rem);
|
||
}
|
||
.confirm-list {
|
||
max-width: 100%;
|
||
padding: 0 0.25rem;
|
||
}
|
||
#bifrost-join {
|
||
min-height: auto;
|
||
padding: clamp(4rem, 10vh, 7rem) var(--edge) clamp(2rem, 4vh, 3rem);
|
||
}
|
||
.join-stage {
|
||
/* Mobile content (esp. confirmation with 4 wrapping bullets) is
|
||
taller than on desktop; size for the tallest panel so neither
|
||
one overflows into the footer below. */
|
||
min-height: clamp(560px, 82vh, 720px);
|
||
}
|
||
}
|
||
|
||
@media (max-width: 520px) {
|
||
.stop-title { font-size: clamp(1.6rem, 7vw, 2.2rem); }
|
||
.stop-sub { font-size: 1.05rem; }
|
||
.stop-intro-text { font-size: clamp(1.1rem, 4.5vw, 1.4rem); }
|
||
}
|
||
|
||
/* Reduced motion — show everything statically */
|
||
@media (prefers-reduced-motion: reduce) {
|
||
.map-stop .dot,
|
||
.map-stop .stop-content > *,
|
||
.map-stop .stop-image,
|
||
.join-cta,
|
||
.join-confirmation,
|
||
.join-footer > *,
|
||
.confirm-list li::before,
|
||
.confirm-list li::after {
|
||
opacity: 1 !important;
|
||
transform: none !important;
|
||
}
|
||
.map-path .path-draw { stroke-dashoffset: 0 !important; }
|
||
.map-canvas::after { height: 100% !important; }
|
||
}
|
||
|
||
/* Bind illustration custom properties to the rendered surfaces.
|
||
Each surface uses background-image so the underlying base64 data
|
||
stays defined once in :root. */
|
||
.stop-illust {
|
||
width: 100%;
|
||
aspect-ratio: 1 / 1;
|
||
background-repeat: no-repeat;
|
||
background-position: center;
|
||
background-size: contain;
|
||
mix-blend-mode: multiply;
|
||
}
|
||
.stop-illust[data-illust="community"] { background-image: var(--illust-community); }
|
||
.stop-illust[data-illust="council"] { background-image: var(--illust-council); }
|
||
.stop-illust[data-illust="pilot"] { background-image: var(--illust-pilot); }
|
||
|
||
|
||
</style>
|
||
</head>
|
||
<body data-screen-label="01 Timeline">
|
||
|
||
<img class="site-mark" src="/fenja/fenja-wordmark-black.svg" alt="Fenja" aria-hidden="true" />
|
||
|
||
<!-- ───── Page 1 : TIMELINE ───── -->
|
||
<section class="page page-timeline is-active" id="page-timeline" data-screen-label="01 Timeline">
|
||
<div class="page-title">From the promise of AI to the loss of <em>sovereignty.</em></div>
|
||
<div class="page-sub">Twenty-three headlines, quietly laid across a tinted map. Scroll the wheel — the map turns with you.</div>
|
||
|
||
<!-- Globe ghost -->
|
||
<div class="globe-wrap" id="globe-wrap"></div>
|
||
|
||
<!-- Continue to the next page -->
|
||
<button class="continue-btn" id="continue-btn" type="button">
|
||
<span class="c-label">Read the editor’s <em>note</em></span>
|
||
<span class="c-arrow" aria-hidden="true">→</span>
|
||
</button>
|
||
|
||
<div class="timeline-viewport" id="tl-viewport">
|
||
<div class="timeline-track" id="tl-track">
|
||
<div class="spine" id="spine"></div>
|
||
<!-- Year ticks and events injected by JS -->
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- ───── Page 2 : OVERVIEW ───── -->
|
||
<section class="page page-overview" id="page-overview" data-screen-label="02 Overview">
|
||
<!-- Europe map — fades in on activation, drives opacity by scroll position (bifrost.js) -->
|
||
<div class="overview-globe" id="overview-globe"></div>
|
||
|
||
<!-- Internal scroller: the six Project Bifrost scenes live inside this.
|
||
Lenis and ScrollTrigger are wired to this element, not the window. -->
|
||
<div id="overview-scroll">
|
||
|
||
<!-- ============================================================
|
||
SCENE 1 — HERO
|
||
============================================================ -->
|
||
<section id="hero" class="scene" aria-labelledby="hero-title">
|
||
<div class="hero-wrap">
|
||
<div>
|
||
<div class="eyebrow" data-reveal>For regulated environments</div>
|
||
<h1 id="hero-title" class="hero-title" data-reveal-lines>
|
||
Secure & <em>Sovereign</em> AI,<br/>
|
||
hosted where it <em>belongs.</em>
|
||
</h1>
|
||
<p class="hero-lede" data-reveal>
|
||
Enabling highly advanced AI capabilities hosted within the client's own secure infrastructure.
|
||
</p>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="hero-foot">
|
||
<div class="support" aria-label="Supported by Innovationsfonden">
|
||
<span>Supported by</span>
|
||
<!-- Simplified Innovationsfonden wordmark (redrawn — not their official logo, a respectful representation) -->
|
||
<svg viewBox="0 0 190 20" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
|
||
<g fill="#3c6b6b">
|
||
<path d="M4 2 L12 18 L10 2 Z" />
|
||
<text x="18" y="15" font-family="Manrope, sans-serif" font-weight="600" font-size="13" letter-spacing="0.2" fill="#3c6b6b">nnovationsfonden</text>
|
||
</g>
|
||
</svg>
|
||
</div>
|
||
<div class="scroll-hint" aria-hidden="true">
|
||
<span>Scroll</span>
|
||
<span class="arrow"></span>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- ============================================================
|
||
SCENE 2 — ARCHITECTURE (pinned, scrubbed)
|
||
============================================================ -->
|
||
<section id="stack-scene" aria-label="The Fenja AI architecture">
|
||
<div class="stack-pin">
|
||
<div class="layer-theatre">
|
||
|
||
<!-- LEFT SIDE — explanatory copy, visible only during the grid phase. -->
|
||
<div class="copy-stage" aria-live="polite">
|
||
<div class="copy-layer" data-copy="0">
|
||
<span class="tag">The stack</span>
|
||
<h2>All the capabilities to solve business use cases with AI.</h2>
|
||
<p>Four layers. One architecture. Every piece yours to own.</p>
|
||
</div>
|
||
<div class="copy-layer" data-copy="1">
|
||
<span class="tag">Yours, entirely</span>
|
||
<h2>Full client <strong>control.</strong><br/>Complete <em>sovereignty.</em></h2>
|
||
<p>Nothing proprietary above the hardware. Nothing that can be switched off from elsewhere.</p>
|
||
</div>
|
||
<div class="copy-layer" data-copy="2">
|
||
<span class="tag">Origin</span>
|
||
<h2>Built in <strong>Denmark.</strong><br/>For <em>Europe.</em></h2>
|
||
<p>Engineered against the standards that matter here — the regulations, the expectations, the posture.</p>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- LAYER CARDS — drop in, stack, then rearrange to grid -->
|
||
<article class="layer-card" data-layer="0" aria-label="Layer 1: the Foundation">
|
||
<span class="card-eyebrow">The Foundation</span>
|
||
<div class="card-box">
|
||
<span class="card-grid-label" aria-hidden="true">The Foundation</span>
|
||
<div class="card-content">
|
||
<h3 class="card-title"><b>AI</b> - An <b>open-source</b> model, running on your <em>own hardware.</em></h3>
|
||
<p class="card-body">Locally hosted and cutting edge in functionality. Completely safe and ready to support you and your business.</p>
|
||
</div>
|
||
<div class="card-brain" aria-hidden="true"></div>
|
||
</div>
|
||
</article>
|
||
|
||
<article class="layer-card" data-layer="1" aria-label="Layer 2: Knowledge">
|
||
<span class="card-eyebrow">The Knowledge</span>
|
||
<div class="card-box">
|
||
<span class="card-grid-label" aria-hidden="true">The Knowledge</span>
|
||
<div class="card-content">
|
||
<h3 class="card-title">The <b>vocabulary</b> of your business — <em>learned, retained.</em></h3>
|
||
<p class="card-body">The business vocabulary through which the AIs understand your domain, and the capability to learn while you collaborate.</p>
|
||
</div>
|
||
<div class="card-brain" aria-hidden="true"></div>
|
||
</div>
|
||
</article>
|
||
|
||
<article class="layer-card" data-layer="2" aria-label="Layer 3: Tools">
|
||
<span class="card-eyebrow">The Tools</span>
|
||
<div class="card-box">
|
||
<span class="card-grid-label" aria-hidden="true">The Tools</span>
|
||
<div class="card-content">
|
||
<h3 class="card-title">How the AIs <b>act</b> — not just what they <em>know.</em></h3>
|
||
<p class="card-body">Data connections, RAG, semantic search, Python scripts — the instruments your agents reach for when solving real tasks.</p>
|
||
</div>
|
||
<div class="card-brain" aria-hidden="true"></div>
|
||
</div>
|
||
</article>
|
||
|
||
<article class="layer-card" data-layer="3" aria-label="Layer 4: Agents">
|
||
<span class="card-eyebrow">The Agents</span>
|
||
<div class="card-box">
|
||
<span class="card-grid-label" aria-hidden="true">The Agents</span>
|
||
<div class="card-content">
|
||
<h3 class="card-title"><b>Specialists</b>, <em>collaborating</em> to solve distinct tasks.</h3>
|
||
<p class="card-body">Specialized AI agents solving distinct tasks in coordination — the way your people already work.</p>
|
||
</div>
|
||
<div class="card-brain" aria-hidden="true"></div>
|
||
</div>
|
||
</article>
|
||
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
|
||
<!-- ============================================================
|
||
SCENE 3 — SLIDE 11 — WORDS FLY IN ONE AT A TIME
|
||
============================================================ -->
|
||
<section id="words-scene" aria-labelledby="words-head">
|
||
<h3 id="words-head" class="sr-only" style="position:absolute;left:-9999px;">
|
||
But a platform for regulated organisations has to be built with them, not just for them.
|
||
</h3>
|
||
<div class="words-pin">
|
||
<p class="words" aria-hidden="true">
|
||
<!-- JS will replace these spans' opacity/transform; text is already here for no-JS / reduced-motion -->
|
||
<span class="w">But</span>
|
||
<span class="w">a</span>
|
||
<span class="w">platform</span>
|
||
<span class="w">for</span>
|
||
<span class="w">regulated</span>
|
||
<span class="w">organisations</span>
|
||
<span class="w">has</span>
|
||
<span class="w">to</span>
|
||
<span class="w">be</span>
|
||
<span class="w">built</span>
|
||
<span class="w hi">with</span>
|
||
<span class="w hi">them,</span>
|
||
<span class="w">not</span>
|
||
<span class="w">just</span>
|
||
<span class="w">for</span>
|
||
<span class="w">them.</span>
|
||
</p>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- ============================================================
|
||
SCENE 4 — PROJECT BIFROST REVEAL
|
||
============================================================ -->
|
||
<section id="bifrost" aria-labelledby="bifrost-head">
|
||
<div class="bifrost-pin">
|
||
<div class="bifrost-stage">
|
||
|
||
<!-- The bridge arc. Uses a restrained aurora gradient — the ONLY
|
||
place colour appears in the entire site. Norse mythology:
|
||
Bifrost is the bridge between worlds, rendered here as a
|
||
single luminous arc spanning the stage. -->
|
||
<div class="arc-wrap" aria-hidden="true">
|
||
<svg viewBox="0 0 1400 500" xmlns="http://www.w3.org/2000/svg">
|
||
<defs>
|
||
<linearGradient id="auroraGrad" x1="0" x2="1" y1="0" y2="0">
|
||
<stop offset="0%" stop-color="#b48755" stop-opacity="0"/>
|
||
<stop offset="15%" stop-color="#b48755" stop-opacity="0.95"/>
|
||
<stop offset="40%" stop-color="#a4553b" stop-opacity="0.95"/>
|
||
<stop offset="65%" stop-color="#5c7b8e" stop-opacity="0.95"/>
|
||
<stop offset="85%" stop-color="#6e5a86" stop-opacity="0.95"/>
|
||
<stop offset="100%" stop-color="#6e5a86" stop-opacity="0"/>
|
||
</linearGradient>
|
||
<linearGradient id="auroraGradSoft" x1="0" x2="1" y1="0" y2="0">
|
||
<stop offset="0%" stop-color="#b48755" stop-opacity="0"/>
|
||
<stop offset="15%" stop-color="#b48755" stop-opacity="0.35"/>
|
||
<stop offset="40%" stop-color="#a4553b" stop-opacity="0.35"/>
|
||
<stop offset="65%" stop-color="#5c7b8e" stop-opacity="0.35"/>
|
||
<stop offset="85%" stop-color="#6e5a86" stop-opacity="0.35"/>
|
||
<stop offset="100%" stop-color="#6e5a86" stop-opacity="0"/>
|
||
</linearGradient>
|
||
<filter id="softGlow" x="-20%" y="-50%" width="140%" height="200%">
|
||
<feGaussianBlur stdDeviation="8"/>
|
||
</filter>
|
||
</defs>
|
||
|
||
<!-- soft halo -->
|
||
<path id="arcHalo" d="M 60 420 Q 700 -40 1340 420"
|
||
fill="none"
|
||
stroke="url(#auroraGradSoft)"
|
||
stroke-width="24"
|
||
stroke-linecap="round"
|
||
filter="url(#softGlow)"/>
|
||
|
||
<!-- main arc -->
|
||
<path id="arcMain" d="M 60 420 Q 700 -40 1340 420"
|
||
fill="none"
|
||
stroke="url(#auroraGrad)"
|
||
stroke-width="3"
|
||
stroke-linecap="round"/>
|
||
|
||
<!-- secondary thin highlight arc -->
|
||
<path id="arcThin" d="M 80 420 Q 700 -20 1320 420"
|
||
fill="none"
|
||
stroke="url(#auroraGrad)"
|
||
stroke-width="1"
|
||
stroke-linecap="round"
|
||
opacity="0.6"/>
|
||
</svg>
|
||
</div>
|
||
|
||
<div class="bifrost-text">
|
||
<div class="bifrost-eyebrow">Introducing</div>
|
||
<h2 id="bifrost-head" class="bifrost-name">
|
||
<span class="token">Project</span>
|
||
<span class="token accent">Bifrost</span>
|
||
</h2>
|
||
<p class="bifrost-sub">
|
||
The bridge <em>between</em> an industrial-grade AI platform and the realities of regulated organisations — built <em>with</em> them, not just for them.
|
||
</p>
|
||
</div>
|
||
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- ============================================================
|
||
SCENE 5 — PROJECT BIFROST · WHAT IT MEANS
|
||
A treasure-map of participation: an intro stop and three
|
||
component stops revealed sequentially as the user scrolls
|
||
a meandering ink path down the page.
|
||
============================================================ -->
|
||
<section id="bifrost-meaning" aria-labelledby="bifrost-meaning-head">
|
||
<div class="map-intro">
|
||
<span class="map-eyebrow">The invitation</span>
|
||
<h2 id="bifrost-meaning-head" class="map-title">
|
||
What being part of <em>Project Bifrost</em> means
|
||
</h2>
|
||
<p class="map-lede">
|
||
Three ways to <em>shape</em>, to <em>influence</em>, and to <em>build with</em> the platform from the inside — a journey through what participation actually looks like.
|
||
</p>
|
||
</div>
|
||
|
||
<div class="map-canvas">
|
||
|
||
<!-- Wandering path. Stretched to fill the canvas via
|
||
preserveAspectRatio="none". The accent overlay is drawn
|
||
as the user scrolls down through the stops. -->
|
||
<svg class="map-path" viewBox="0 0 100 200" preserveAspectRatio="none" aria-hidden="true">
|
||
<!-- The d attributes are computed at runtime by buildMapPath()
|
||
based on the rendered Y positions of the dots, so the path
|
||
always passes through them regardless of content height.
|
||
The placeholder values below are valid fallback geometry
|
||
that ships if JS fails. -->
|
||
<path id="mapPathBg" class="path-bg"
|
||
vector-effect="non-scaling-stroke"
|
||
d="M 50 6 C 72 28, 72 52, 50 70 C 28 88, 28 112, 50 130 C 72 148, 72 172, 50 194"/>
|
||
<path id="mapPathDraw" class="path-draw"
|
||
vector-effect="non-scaling-stroke"
|
||
d="M 50 6 C 72 28, 72 52, 50 70 C 28 88, 28 112, 50 130 C 72 148, 72 172, 50 194"/>
|
||
</svg>
|
||
|
||
<!-- INTRO STOP -->
|
||
<article class="map-stop map-stop--intro" data-stop="0">
|
||
<div class="dot-anchor"><span class="dot"></span></div>
|
||
<div class="stop-content">
|
||
<p class="stop-intro-text">
|
||
Being part of <em>Project Bifrost</em> means <em>three</em> 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.
|
||
</p>
|
||
</div>
|
||
</article>
|
||
|
||
<!-- STOP 1 — Community (text on left, image on right) -->
|
||
<article class="map-stop" data-stop="1" data-side="left" aria-labelledby="stop-1-title">
|
||
<div class="stop-content">
|
||
<span class="stop-eyebrow">Be part of a</span>
|
||
<h3 id="stop-1-title" class="stop-title"><em>Community</em></h3>
|
||
<p class="stop-sub">Shape the future together</p>
|
||
<p class="stop-body">Join a select community of organisations helping define the future of trusted sovereign AI in Denmark and Europe. At a time when Europe needs greater technological independence, this is an opportunity to contribute to an AI platform built on trust, shared ambition, and a common mission.</p>
|
||
</div>
|
||
<div class="dot-anchor"><span class="dot"></span></div>
|
||
<div class="stop-image">
|
||
<div class="stop-illust" data-illust="community" role="img" aria-label="Six people in discussion around a table"></div>
|
||
</div>
|
||
</article>
|
||
|
||
<!-- STOP 2 — Advisory Council (image on left, text on right) -->
|
||
<article class="map-stop" data-stop="2" data-side="right" aria-labelledby="stop-2-title">
|
||
<div class="stop-image">
|
||
<div class="stop-illust" data-illust="council" role="img" aria-label="A man and a woman in conversation"></div>
|
||
</div>
|
||
<div class="dot-anchor"><span class="dot"></span></div>
|
||
<div class="stop-content">
|
||
<span class="stop-eyebrow">Be part of an</span>
|
||
<h3 id="stop-2-title" class="stop-title"><em>Advisory Council</em></h3>
|
||
<p class="stop-sub">Turn insight into influence</p>
|
||
<p class="stop-body">Take part in regular advisory council sessions where your input directly shapes the product and platform roadmap. Gain first-hand insight into cutting-edge AI developments and help influence what is built, which capabilities are prioritised, and how the platform evolves to meet real organisational needs.</p>
|
||
</div>
|
||
</article>
|
||
|
||
<!-- STOP 3 — Pilot Projects (text on left, image on right) -->
|
||
<article class="map-stop" data-stop="3" data-side="left" aria-labelledby="stop-3-title">
|
||
<div class="stop-content">
|
||
<span class="stop-eyebrow">Be part of</span>
|
||
<h3 id="stop-3-title" class="stop-title"><em>Pilot Projects</em></h3>
|
||
<p class="stop-sub">Access the platform before others</p>
|
||
<p class="stop-body">A select number of Project Bifrost participants will have the opportunity to join pilot projects and gain early access to the platform at a significantly reduced price, subsidised by the Innovation Fund. This gives your organisation the chance to explore cutting-edge sovereign AI early, realise value at low cost, and help shape the platform through real-world use.</p>
|
||
</div>
|
||
<div class="dot-anchor"><span class="dot"></span></div>
|
||
<div class="stop-image">
|
||
<div class="stop-illust" data-illust="pilot" role="img" aria-label="Two people working together at a computer"></div>
|
||
</div>
|
||
</article>
|
||
|
||
</div>
|
||
</section>
|
||
|
||
<!-- ============================================================
|
||
SCENE 6 — PROJECT BIFROST · JOIN
|
||
A large call-to-action ("Join us in shaping the future...")
|
||
that, when clicked, crossfades to a confirmation panel listing
|
||
what happens next. Below, a three-column footer row with three
|
||
equal-weight brand marks: "Project Bifrost" (left-aligned),
|
||
Fenja AI (centred), and Innovationsfonden (right-aligned).
|
||
============================================================ -->
|
||
<section id="bifrost-join" aria-labelledby="bifrost-join-head">
|
||
|
||
<div class="join-stage">
|
||
|
||
<!-- CTA state (visible initially) -->
|
||
<div class="join-panel join-cta" id="joinCTA">
|
||
<div class="join-eyebrow">Ready?</div>
|
||
<h2 id="bifrost-join-head" class="join-headline">
|
||
Join us in shaping the future of <em>trusted sovereign AI.</em>
|
||
</h2>
|
||
<button type="button" class="join-button" id="joinBtn" aria-controls="joinConfirm">
|
||
Join Project Bifrost
|
||
<span class="arrow" aria-hidden="true"></span>
|
||
</button>
|
||
<p class="join-subtext">Built in Denmark. Supported by the Innovation Fund.</p>
|
||
</div>
|
||
|
||
<!-- Confirmation state (revealed after the CTA is clicked) -->
|
||
<div class="join-panel join-confirmation" id="joinConfirm" aria-hidden="true">
|
||
<div class="confirm-eyebrow">You're in</div>
|
||
<h2 class="confirm-headline">
|
||
Thank you for joining <em>Project Bifrost</em>.
|
||
</h2>
|
||
<ul class="confirm-list" role="list">
|
||
<li>The <em>Fenja AI team</em> will reach out to you shortly.</li>
|
||
<li>You’ll receive an invitation to the <em>project portal</em> soon — where all project communication, materials, and updates will live.</li>
|
||
<li>We’re currently setting the date for the <em>first advisory council meeting</em>. You’ll be invited as soon as it’s confirmed.</li>
|
||
<li>We’ll be in touch shortly about your participation in the <em>pilot project</em>.</li>
|
||
</ul>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<div class="join-footer">
|
||
<div class="foot-project" aria-label="Project Bifrost">Project <em>Bifrost</em></div>
|
||
|
||
<div class="foot-fenja" aria-label="Fenja AI">
|
||
<!-- Fenja AI logo — same source geometry as the fixed brand mark
|
||
at top-left, with IDs prefixed "foot-" to avoid collisions. -->
|
||
<img src="/fenja/fenja-wordmark-black.svg" alt="Fenja AI" style="width: 100%; height: auto; display: block;" />
|
||
</div>
|
||
|
||
<div class="foot-innov" aria-label="Innovationsfonden">
|
||
<!-- PLACEHOLDER: redrawn Innovationsfonden mark.
|
||
Hybrid: a small SVG holds the characteristic slanted "I"
|
||
and an HTML <span> renders the rest of the wordmark with
|
||
native text metrics (avoids SVG <text> overflowing its
|
||
viewBox at arbitrary widths). Swap this block for the
|
||
real PNG/SVG when available — the container already
|
||
sizes correctly via --foot-h. -->
|
||
<svg class="innov-mark" viewBox="0 0 60 100" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
|
||
<path d="M 22 4 L 55 4 L 38 96 L 5 96 Z"/>
|
||
</svg><span class="innov-text">nnovationsfonden</span>
|
||
</div>
|
||
</div>
|
||
|
||
</section>
|
||
|
||
|
||
</div><!-- /#overview-scroll -->
|
||
</section>
|
||
|
||
<!-- ───── Page 3 : ARCHIVE ───── -->
|
||
<section class="page page-archive" id="page-archive" data-screen-label="03 Archive">
|
||
<div class="archive">
|
||
<div class="inner">
|
||
<h1 class="headline">All twenty-three entries, in order of <em>publication.</em></h1>
|
||
<p class="sub">
|
||
Dates, sources and plate numbers for every card in the catalog. Hover a row to
|
||
lift it from the paper.
|
||
</p>
|
||
|
||
<table id="archive-table">
|
||
<thead>
|
||
<tr>
|
||
<th>№</th>
|
||
<th>Date</th>
|
||
<th>Register</th>
|
||
<th>Headline</th>
|
||
<th>Source</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody id="archive-body"><!-- filled by JS --></tbody>
|
||
</table>
|
||
|
||
<div class="footer">
|
||
<div>Fenja AI · Field Notes, No. IV</div>
|
||
<em>Catalog closed 14 April 2026.</em>
|
||
<div>Page III of III</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- Dot-nav tray + nav (shared) -->
|
||
<div class="dot-nav-tray"></div>
|
||
<nav class="dot-nav">
|
||
<button class="dot-btn is-active" data-target="page-timeline">
|
||
<span class="dot"></span>
|
||
<span class="label">Timeline</span>
|
||
</button>
|
||
<button class="dot-btn" data-target="page-overview">
|
||
<span class="dot"></span>
|
||
<span class="label">Overview</span>
|
||
</button>
|
||
<button class="dot-btn" data-target="page-archive">
|
||
<span class="dot"></span>
|
||
<span class="label">Archive</span>
|
||
</button>
|
||
</nav>
|
||
|
||
<script src="/vendor/lenis.min.js" defer></script>
|
||
<script src="/vendor/gsap.min.js" defer></script>
|
||
<script src="/vendor/scrolltrigger.min.js" defer></script>
|
||
<script src="/bifrost.js" defer></script>
|
||
<script src="/timeline.js" defer></script>
|
||
|
||
</body>
|
||
</html>
|