diff --git a/protected/index.html b/protected/index.html index ff193ab..f69c113 100644 --- a/protected/index.html +++ b/protected/index.html @@ -62,7 +62,17 @@ pointer-events: auto; } - /* ───────── Site wordmark — top-left masthead ───────── */ + /* ───────── Site wordmark — top-left masthead, clickable "home" link ───────── */ + .site-mark, + .site-mark:link, + .site-mark:visited, + .site-mark:hover, + .site-mark:active, + .site-mark:focus, + .site-mark:focus-visible { + text-decoration: none; + border-bottom: 0; + } .site-mark { position: fixed; top: 28px; @@ -70,17 +80,34 @@ width: 118px; height: auto; z-index: 50; - pointer-events: none; opacity: 0.85; + display: block; + cursor: pointer; + transition: opacity var(--dur, 200ms) var(--ease, ease); + } + .site-mark img { + display: block; + width: 100%; + height: auto; + pointer-events: none; /* keeps clicks on the anchor, not the image */ + } + .site-mark:hover, + .site-mark:focus-visible { + opacity: 1; + } + .site-mark:focus-visible { + outline: 2px solid var(--walnut, #785f53); + outline-offset: 6px; } @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 overline title — large, sits in the upper third of the viewport + so the long page-sub body below it has room without clipping. */ .page-title { position: absolute; - left: 80px; top: 42vh; + left: 80px; top: 22vh; font-family: "Newsreader", Georgia, serif; font-weight: 400; font-size: 54px; @@ -98,17 +125,21 @@ } .page-title + .page-sub { position: absolute; - left: 80px; top: calc(42vh + 220px); - max-width: 560px; + left: 80px; top: calc(22vh + 160px); + max-width: 720px; + /* Upright serif editorial body, ink colour, 23px (about 15% + larger than the welcome-page body) so the long intro reads + at ease. Only the spans carry italics for emphasis. */ font-family: "Newsreader", Georgia, serif; - font-style: italic; - font-size: 19px; - line-height: 1.5; - color: var(--ink-soft); + font-weight: 400; + font-size: 23px; + line-height: 1.52; + color: var(--ink); z-index: 15; opacity: 1; transition: opacity 520ms var(--ease), transform 520ms var(--ease); } + .page-sub em { font-style: italic; font-weight: 700; color: var(--ink); } /* Once the timeline has been advanced, the front matter steps aside */ .page-timeline.is-scrolled .page-title, .page-timeline.is-scrolled .page-sub { @@ -117,6 +148,71 @@ transform: translateY(-12px); } + /* ───────── First-load scroll hint ───────── + Right-edge prompt telling the reader to begin scrolling the + horizontal timeline. Vertically centered, horizontal layout + (label then arrow), editorial ink colour. Arrow pulses + rightward to hint at the scroll direction. Fades out as soon + as the reader advances — same `.is-scrolled` class the + front-matter uses. */ + .timeline-scroll-hint { + position: absolute; + right: clamp(32px, 3.8vw, 72px); + top: 50%; + transform: translateY(-50%); + z-index: 20; + display: flex; + flex-direction: row; + align-items: center; + gap: 22px; + color: var(--ink); + pointer-events: none; + opacity: 0; + animation: ts-fade-in 700ms 500ms var(--ease) forwards; + transition: opacity 360ms var(--ease), transform 360ms var(--ease); + } + .ts-label { + font-family: "Newsreader", Georgia, serif; + font-style: italic; + font-size: clamp(22px, 2.1vw, 30px); + letter-spacing: -0.005em; + line-height: 1; + white-space: nowrap; + } + .ts-arrow { + width: clamp(84px, 8vw, 120px); + height: auto; + color: var(--ink); + animation: ts-arrow-nudge 1800ms cubic-bezier(0.4, 0, 0.2, 1) infinite; + } + @keyframes ts-fade-in { + from { opacity: 0; transform: translate(14px, -50%); } + to { opacity: 1; transform: translate(0, -50%); } + } + @keyframes ts-arrow-nudge { + 0%, 100% { transform: translateX(0); opacity: 0.9; } + 50% { transform: translateX(14px); opacity: 1; } + } + /* Fade out on the very first wheel tick (`.hint-dismissed` is set + in timeline.js's onWheel handler, independent of the 40px + `.is-scrolled` threshold so the hint leaves instantly). + `animation: none` is required to override the entry ts-fade-in + animation (which has forwards fill-mode and outranks this rule + otherwise); ts-arrow-nudge is also stopped so the pulse doesn't + keep ticking behind the fade. */ + .page-timeline.hint-dismissed .timeline-scroll-hint { + animation: none; + opacity: 0; + transform: translate(14px, -50%); + } + .page-timeline.hint-dismissed .ts-arrow { + animation: none; + } + @media (prefers-reduced-motion: reduce) { + .timeline-scroll-hint { animation: none; opacity: 1; } + .ts-arrow { animation: none; } + } + /* ───────── Dot-nav ───────── 5px dots, filled when active, outlined ring otherwise. Labels hidden by default and appear as a floating tooltip above the dot on hover. @@ -438,17 +534,19 @@ align-items: center; gap: 28px; padding: 32px 44px; - background: var(--paper-high); - color: var(--ink); + /* Inverted palette — crimson field, paper ink — so the button + pops out against the timeline's warm paper background. */ + background: var(--crimson); + color: var(--paper); cursor: pointer; z-index: 30; opacity: 0; transform: translate(36px, -50%); pointer-events: none; box-shadow: - 0 0 0 0.5px rgba(56,56,49,0.08), - 0 26px 48px -22px rgba(56,56,49,0.28), - 0 3px 10px -4px rgba(56,56,49,0.10); + 0 0 0 0.5px rgba(138,58,47,0.22), + 0 28px 52px -20px rgba(138,58,47,0.45), + 0 4px 12px -5px rgba(138,58,47,0.24); transition: opacity 520ms var(--ease), transform 520ms var(--ease), @@ -466,11 +564,12 @@ 50% { transform: translate(6px, -50%); } } .continue-btn:hover { - background: #fffbf2; + /* Slight darken on hover so the inverted card still signals it's interactive. */ + background: #7a3229; box-shadow: - 0 0 0 0.5px rgba(56,56,49,0.12), - 0 32px 56px -22px rgba(56,56,49,0.34), - 0 4px 12px -5px rgba(56,56,49,0.12); + 0 0 0 0.5px rgba(122,50,41,0.32), + 0 34px 60px -20px rgba(122,50,41,0.55), + 0 5px 14px -5px rgba(122,50,41,0.32); } .continue-btn .c-icon { display: inline-flex; @@ -478,7 +577,7 @@ justify-content: center; width: 72px; height: 72px; - color: var(--crimson); + color: var(--paper); flex: 0 0 auto; transition: transform var(--dur) var(--ease); } @@ -495,14 +594,15 @@ font-size: 36px; font-weight: 400; letter-spacing: -0.015em; - color: var(--ink); + color: var(--paper); line-height: 1.08; max-width: 13ch; } .continue-btn .c-label em { font-style: italic; font-weight: 700; - color: var(--crimson); + /* Italic emphasis stays on paper colour so it reads against the crimson field. */ + color: var(--paper); } /* ───────── Overview page ───────── */ @@ -676,8 +776,8 @@ /* 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); } + .page-title { font-size: 36px; max-width: 640px; top: 18vh; } + .page-title + .page-sub { font-size: 21px; top: calc(18vh + 160px); } } @media (max-height: 500px) { .page-title { display: none; } @@ -732,8 +832,11 @@ padding-bottom: clamp(6rem, 16vh, 11rem); } #page-overview #hero .hero-wrap { - /* Constrain to the left column so Europe is visible to its right. */ - max-width: 62ch; + /* Constrain to the left column so Europe is visible to its right. + Widened from 62ch to 74ch (~+20%) so the new longer hero lede + ("Fenja AI is a sovereign AI platform…") sits comfortably on + fewer lines. */ + max-width: 74ch; /* Zeroed: wrap padding-top was adding down-drift inside the centered container, pushing the hero-foot toward the dot-nav. Vertical position is now controlled entirely by #hero's asymmetric padding. */ @@ -769,7 +872,7 @@ --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-hero: clamp(2rem, 5.1vw, 4.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); @@ -850,11 +953,11 @@ html { font-family: var(--type-display); font-weight: 330; font-size: var(--step-hero); - line-height: 1.02; - letter-spacing: -0.03em; + line-height: 1.06; + letter-spacing: -0.025em; color: var(--ink); margin: 0; - max-width: 22ch; + max-width: 30ch; } .hero-title em { font-style: italic; @@ -1879,15 +1982,21 @@ html { } .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; + /* Matched to .join-cta .join-headline so pressing the CTA doesn't + shrink the page's visual anchor — the confirmation keeps the + same stature, only the copy changes. */ + 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; - max-width: 22ch; + max-width: 20ch; } .join-confirmation .confirm-headline em { + /* Italics preserved for emphasis; accent colour keeps the editorial + weight without the aurora gradient (which is reserved for S4 + and the pre-click CTA). */ font-style: italic; color: var(--accent); font-weight: 380; @@ -2203,12 +2312,17 @@ html { - + + Fenja +
-
From the promise of AI to the loss of sovereignty.
-
Twenty-three headlines, quietly laid across a tinted map. Scroll the wheel — the map turns with you.
+
When AI runs Europe, who runs the AI?
+
+ We’ve spent years building data and AI across Denmark and Europe, watching one dependency harden after another. AI is different. The United States has made that clear. China has made that clear. You cannot stand strong in this century on AI you do not control — and for the first time in a generation, Europe has both the reason and the moment to build its own. The window is closing faster than most realise. It is open now. It will not be open long.

+ As AI moves into our hospitals, our courts, our defence, our schools — can we afford for the switch to sit in Washington? +
@@ -2231,6 +2345,20 @@ html { + + +
diff --git a/protected/timeline.js b/protected/timeline.js index bab7110..c679fda 100644 --- a/protected/timeline.js +++ b/protected/timeline.js @@ -137,6 +137,12 @@ function onWheel(e) { state.target = clamp(state.target + dy * 1.1, 0, state.max); e.preventDefault(); kick(); + + // Dismiss the scroll hint on the very first wheel tick — instant fade. + // A separate class from `.is-scrolled` (which keys off 40px of travel + // and controls the front-matter) so the hint goes the moment the + // reader commits, not after inertia catches up. + document.getElementById('page-timeline')?.classList.add('hint-dismissed'); } function kick() { @@ -444,6 +450,7 @@ document.querySelectorAll('.dot-btn').forEach(btn => { }); }); + /* ───────────────────────────────────────────────────────────── First name propagation — fetched from /auth/me on load. Used by Scene 3 ("This is why we've invited you, [Name]."). diff --git a/public/entrance.html b/public/entrance.html index 764e2f2..c94efae 100644 --- a/public/entrance.html +++ b/public/entrance.html @@ -59,10 +59,18 @@ position: relative; z-index: 10; width: 100%; + min-height: 100vh; padding: 0 112px; + /* Vertically center the active step (email or welcome). Body-level + flex also centers, but an extra flex layer here ensures the + welcome step — which is taller than the email step — stays + anchored to the viewport centre rather than drifting down. */ + display: flex; + align-items: center; } .entrance-inner { - max-width: 560px; + max-width: 620px; + width: 100%; } /* ───── Steps ───── */ @@ -162,15 +170,47 @@ .welcome-body { font-family: "Newsreader", Georgia, "Times New Roman", serif; font-weight: 400; - font-size: 18px; + font-size: 20px; line-height: 1.55; color: var(--ink); - max-width: 540px; + max-width: 620px; margin: 0 0 20px 0; text-wrap: pretty; } .welcome-body em { font-style: italic; font-weight: 700; } + /* Two-line definition block: bold-italic term + short definition. */ + .welcome-define { + max-width: 620px; + margin: 0 0 18px 0; + } + .welcome-term { + font-family: "Newsreader", Georgia, "Times New Roman", serif; + font-weight: 500; + font-size: 22px; + line-height: 1.2; + color: var(--ink); + margin: 0 0 2px 0; + } + .welcome-term em { + font-style: italic; + font-weight: 700; + } + .welcome-def { + font-family: "Newsreader", Georgia, "Times New Roman", serif; + font-weight: 400; + font-size: 19px; + line-height: 1.5; + color: var(--ink-soft); + margin: 0; + text-wrap: pretty; + } + .welcome-def em { + font-style: italic; + font-weight: 700; + color: var(--ink); + } + .welcome-cta { all: unset; display: inline-flex; @@ -246,7 +286,7 @@ .tagline { font-size: 26px; margin-bottom: 32px; } .currents { opacity: 0.5; } .welcome-title { font-size: 38px; } - .welcome-body { font-size: 16.5px; } + .welcome-body { font-size: 18px; } .welcome-logo { display: none; } } @@ -291,16 +331,20 @@ Thank you for your interest.

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

-

- What follows is a timeline: twelve moments that explain why - this matters now, and — at the end — a note on how - Fenja AI addresses it. + This is a personal invitation because we believe your perspective + can make a meaningful contribution to an important mission: building + trusted, sovereign AI for Denmark and Europe. In this short web + experience, we will explain why this matters, what Fenja AI is, and + how you, through Project Bifrost, can help shape its future.

+
+

Fenja AI

+

The company and platform for sovereign and safe AI.

+
+
+

Project Bifrost

+

The initiative created to ensure that Fenja AI is built not just for organisations like yours, but with you.

+