refactor: rebuild home as welcome and pitch page

This commit is contained in:
Jonathan 2026-04-19 20:30:14 +02:00
parent 4bed3a5fe0
commit d9c75a1921

View file

@ -1,190 +1,457 @@
---
import AppLayout from '../layouts/AppLayout.astro';
import ProjectLockup from '../components/ProjectLockup.astro';
import { hasJoinRequest } from '../lib/db';
import { existsSync } from 'fs';
import { join } from 'path';
const user = Astro.locals.user;
const firstName = user.name.split(' ')[0];
const alreadyJoined = hasJoinRequest(user.id);
const innofoundarLogoExists = existsSync(join(process.cwd(), 'public/innofounder-logo.svg'));
---
<AppLayout title="Welcome" user={user}>
<AppLayout title="Home" user={user}>
<div class="page">
<!-- Greeting -->
<section class="greeting-zone">
<p class="label-md eyebrow">Project Bifrost</p>
<h1 class="display-lg greeting">
Welcome, <em class="name">{user.name.split(' ')[0]}.</em>
</h1>
<p class="lead framing">
This is the private working space for the Bifrost pilot — where you follow what we are building,
shape it with your experience, and meet the people building it with you.
<!-- ── Hero ──────────────────────────────────────────────────── -->
<section class="hero">
<ProjectLockup size="hero" alignment="left" />
<p class="hero-greeting">Welcome, <em>{firstName}.</em></p>
</section>
<!-- ── Opening statement ─────────────────────────────────────── -->
<section class="opening">
<p class="opening-statement">
Secure and sovereign AI for regulated environments.
Project Bifrost is where we build it — together with the organisations
who will depend on it.
</p>
</section>
<!-- Navigation cards -->
<nav class="cards" aria-label="Quick navigation">
<a href="/updates" class="card">
<span class="label-sm card-label">Latest update</span>
<h2 class="card-title">
See what <em class="card-em">changed.</em>
</h2>
<p class="body-sm card-body">
Progress notes from Fenja — what shipped, what shifted, what is next.
<!-- ── What Fenja is ─────────────────────────────────────────── -->
<section class="section fenja-section">
<div class="section-text">
<h2 class="section-heading label-sm">What Fenja is</h2>
<div class="prose">
<p class="body-lg">
Fenja is a platform for running advanced AI inside the customer's own infrastructure.
We are building the premier solution for public authorities and organisations in
heavily regulated industries — where trust, auditability, and data residency are not
preferences but legal requirements.
</p>
<span class="card-arrow" aria-hidden="true">↗</span>
</a>
<p class="body-lg">
Standard AI solutions hosted on foreign-owned public clouds are simply not a viable
or compliant option for organisations in the public sector, defence, pharmaceuticals,
or finance. Fenja enables advanced, customer-hosted AI within the client's own secure
infrastructure. Data remains under absolute, localised control, with the traceability
and documentation the market demands.
</p>
</div>
</div>
<a href="/calendar" class="card">
<span class="label-sm card-label">Next meeting</span>
<h2 class="card-title">
The CAB <em class="card-em">calendar.</em>
</h2>
<p class="body-sm card-body">
Upcoming sessions, agendas, and notes from past meetings.
</p>
<span class="card-arrow" aria-hidden="true">↗</span>
</a>
<figure class="stat-figure" aria-label="Key statistic">
<p class="stat-figure__number">97%</p>
<figcaption class="stat-figure__label">
of AI foundational model usage falls outside EU jurisdiction
</figcaption>
<p class="stat-source label-sm">— Stanford AI Index, 2024</p>
</figure>
</section>
<a href="/contribute" class="card">
<span class="label-sm card-label">Contribute</span>
<h2 class="card-title">
Share an <em class="card-em">idea.</em>
</h2>
<p class="body-sm card-body">
Post an idea, a question, or something that inspired you. Everyone in the hub sees it.
<!-- ── What Project Bifrost is ───────────────────────────────── -->
<section class="section bifrost-section">
<h2 class="section-heading label-sm">What Project Bifrost is</h2>
<div class="prose">
<p class="body-lg">
Project Bifrost is the first pilot — a working collaboration between Fenja and five
organisations who are shaping the platform they will ultimately rely on. Two are running
paid-but-subsidised pilots; the remaining three are on the Customer Advisory Board,
following the work closely and shaping direction through quarterly conversations.
</p>
<span class="card-arrow" aria-hidden="true">↗</span>
</a>
<p class="body-lg">
This is not a focus group. Participants are involved in real decisions — about what
the minimum viable product looks like, how the traceability layer works, what the
knowledge base needs to understand. Their input is built in, not bolted on.
</p>
</div>
</section>
<a href="/roadmap" class="card">
<span class="label-sm card-label">Roadmap</span>
<h2 class="card-title">
What is <em class="card-em">next.</em>
</h2>
<p class="body-sm card-body">
In progress, coming soon, and further out — the full picture of where we are going.
<!-- ── Backed by Innofounder ──────────────────────────────────── -->
<section class="section innofounder-section">
<div class="innofounder-header">
{innofoundarLogoExists ? (
<img src="/innofounder-logo.svg" alt="Innofounder — Innovation Fund Denmark" class="innofounder-logo" />
) : (
<div class="innofounder-placeholder" aria-label="Innofounder logo — add file">
<span class="label-sm">Innofounder logo — add <code>public/innofounder-logo.svg</code></span>
</div>
)}
<h2 class="section-heading label-sm">Backed by Innofounder</h2>
</div>
<div class="prose">
<p class="body-lg">
Fenja is backed by Innofounder — Innovation Fund Denmark's programme for
early-stage deep-tech ventures. This matters for you directly: because this
work is publicly funded, we are using that funding to subsidise pilot
participants' own projects.
</p>
<span class="card-arrow" aria-hidden="true">↗</span>
</a>
<p class="body-lg">
Participants in Project Bifrost are not just getting early access to the platform.
They are helping to define what the Danish standard for sovereign AI looks like —
and in return, they receive a substantially discounted sovereign AI deployment for
their own organisation once we reach the agreed MVP. The public funding creates the
conditions for a genuine win-win: you shape the platform, and the platform works for you.
</p>
</div>
</section>
<a href="/preview" class="card">
<span class="label-sm card-label">Product preview</span>
<h2 class="card-title">
See the <em class="card-em">platform.</em>
</h2>
<p class="body-sm card-body">
Screenshots and walkthroughs of the sovereign AI platform you are piloting.
</p>
<span class="card-arrow" aria-hidden="true">↗</span>
</a>
<!-- ── What we ask ───────────────────────────────────────────── -->
<section class="section ask-section">
<h2 class="section-heading label-sm">What we ask</h2>
<div class="ask-grid">
<div class="ask-card">
<h3 class="ask-card-title body-lg">Pilot participants</h3>
<ul class="ask-list body-md">
<li>A few hours a month over three to four months</li>
<li>Running the software in your own environment</li>
<li>Direct feedback on what works and what does not</li>
<li>Input into the minimum viable shape of the product</li>
<li>A fraction of the real value of the deployment, once we reach agreed MVP</li>
</ul>
</div>
<div class="ask-card">
<h3 class="ask-card-title body-lg">Advisory Board members</h3>
<ul class="ask-list body-md">
<li>Quarterly conversations to shape direction</li>
<li>Access to all updates, roadmap, and meetings in this hub</li>
<li>No software deployment required</li>
<li>Advisory Board participation is free</li>
</ul>
</div>
</div>
</section>
<a href="/participants" class="card">
<span class="label-sm card-label">Participants</span>
<h2 class="card-title">
The <em class="card-em">people.</em>
</h2>
<p class="body-sm card-body">
Everyone in the pilot — who they are, where they are from, and what they bring.
<!-- ── Join CTA ───────────────────────────────────────────────── -->
<section class="join-section" id="join-section">
{alreadyJoined ? (
<div class="join-confirmed">
<h2 class="join-confirmed-title">Welcome to Project Bifrost.</h2>
<p class="body-lg join-confirmed-body">
We will be in touch shortly to arrange the next step.
</p>
<span class="card-arrow" aria-hidden="true">↗</span>
</a>
</nav>
</div>
) : (
<div class="join-cta" id="join-cta">
<button class="join-btn" id="join-btn" type="button">
I want to join Project Bifrost.
</button>
<p class="join-hint body-sm">
We will follow up personally to discuss the right level of involvement for your organisation.
</p>
</div>
)}
</section>
</div>
</AppLayout>
<script>
const btn = document.getElementById('join-btn');
if (btn) {
btn.addEventListener('click', async () => {
btn.setAttribute('disabled', 'true');
btn.textContent = 'Registering...';
try {
const res = await fetch('/api/join', { method: 'POST' });
if (res.ok) {
const section = document.getElementById('join-cta');
if (section) {
section.innerHTML = `
<div class="join-confirmed">
<h2 class="join-confirmed-title">Welcome to Project Bifrost.</h2>
<p class="join-confirmed-body">
We will be in touch shortly to arrange the next step.
</p>
</div>
`;
}
} else {
btn.removeAttribute('disabled');
btn.textContent = 'I want to join Project Bifrost.';
}
} catch {
btn.removeAttribute('disabled');
btn.textContent = 'I want to join Project Bifrost.';
}
});
}
</script>
<style>
.page {
padding: var(--space-12) var(--space-20) var(--space-16);
max-width: var(--content-max);
margin: 0 auto;
padding: 0 var(--space-20) var(--space-16);
}
/* ── Greeting ─────────────────────────────────────────────────── */
.greeting-zone {
max-width: 44rem;
margin-bottom: var(--space-12);
}
.eyebrow {
color: var(--on-surface-muted);
letter-spacing: var(--tracking-wider);
text-transform: uppercase;
margin-bottom: var(--space-4);
}
.greeting {
margin-bottom: var(--space-6);
}
.name {
font-weight: 700;
font-style: italic;
}
.framing {
max-width: var(--reading-max);
color: var(--on-surface-variant);
}
/* ── Navigation cards ─────────────────────────────────────────── */
.cards {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: var(--space-4);
}
.card {
position: relative;
/* ── Hero ────────────────────────────────────────────────────────── */
.hero {
padding-top: var(--space-16);
padding-bottom: var(--space-10);
display: flex;
flex-direction: column;
gap: var(--space-3);
padding: var(--space-6);
background: var(--surface-container-lowest);
border-radius: var(--radius-md);
color: var(--on-surface);
border-bottom: none;
text-decoration: none;
transition: background var(--duration-med) var(--ease-standard);
}
.card:hover {
background: var(--surface-container-low);
border-bottom: none;
color: var(--on-surface);
gap: var(--space-5);
}
.card-label {
color: var(--on-surface-muted);
letter-spacing: var(--tracking-wide);
text-transform: uppercase;
}
.card-title {
font-size: var(--text-headline-sm);
.hero-greeting {
font-family: var(--font-serif);
font-size: var(--text-headline-md);
font-style: italic;
font-weight: 400;
color: var(--on-surface-variant);
margin: 0;
letter-spacing: var(--tracking-snug);
}
/* ── Opening statement ───────────────────────────────────────────── */
.opening {
padding-bottom: var(--space-12);
border-bottom: var(--ghost-border);
max-width: 52rem;
}
.opening-statement {
font-family: var(--font-serif);
font-size: var(--text-headline-md);
font-weight: 400;
letter-spacing: var(--tracking-snug);
line-height: var(--leading-snug);
margin: 0;
color: var(--on-surface);
margin: 0;
}
.card-em {
font-weight: 700;
font-style: italic;
/* ── Sections ────────────────────────────────────────────────────── */
.section {
padding: var(--space-12) 0;
border-bottom: var(--ghost-border);
}
.card-body {
.section-heading {
letter-spacing: var(--tracking-wider);
text-transform: uppercase;
color: var(--on-surface-muted);
margin-bottom: var(--space-6);
}
/* ── Fenja section — two-column layout ──────────────────────────── */
.fenja-section {
display: grid;
grid-template-columns: 1fr 18rem;
gap: var(--space-12);
align-items: start;
}
.section-text {
display: flex;
flex-direction: column;
}
.section-text .section-heading {
margin-bottom: var(--space-6);
}
/* ── Prose ───────────────────────────────────────────────────────── */
.prose {
display: flex;
flex-direction: column;
gap: var(--space-5);
}
.prose .body-lg {
margin: 0;
color: var(--on-surface-variant);
flex: 1;
line-height: var(--leading-relaxed);
}
.card-arrow {
/* ── Stat figure ─────────────────────────────────────────────────── */
.stat-figure {
margin: var(--space-8) 0 0;
display: flex;
flex-direction: column;
gap: var(--space-3);
}
.stat-figure__number {
font-family: var(--font-serif);
font-size: var(--text-body-sm);
font-size: var(--text-display-xl);
font-weight: 400;
letter-spacing: var(--tracking-tight);
line-height: 1;
color: var(--on-surface-muted);
margin: 0;
}
.stat-figure__label {
font-family: var(--font-sans);
font-size: var(--text-body-md);
color: var(--on-surface-variant);
max-width: 16rem;
line-height: var(--leading-relaxed);
}
.stat-source {
color: var(--on-surface-muted);
letter-spacing: var(--tracking-wide);
margin: 0;
}
/* ── Innofounder section ─────────────────────────────────────────── */
.innofounder-section {
display: flex;
flex-direction: column;
}
.innofounder-header {
display: flex;
align-items: center;
gap: var(--space-5);
margin-bottom: var(--space-6);
}
.innofounder-logo {
height: 32px;
width: auto;
display: block;
}
.innofounder-placeholder {
display: flex;
align-items: center;
justify-content: center;
height: 32px;
padding: 0 var(--space-4);
background: var(--surface-container);
border-radius: var(--radius-sm);
}
.innofounder-placeholder .label-sm {
color: var(--on-surface-muted);
letter-spacing: var(--tracking-wide);
}
.innofounder-placeholder code {
font-family: var(--font-mono);
font-size: 0.9em;
color: var(--on-surface-variant);
}
.innofounder-section .section-heading {
margin-bottom: 0;
}
.innofounder-section .prose {
margin-top: 0;
}
/* ── What we ask — two cards ─────────────────────────────────────── */
.ask-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: var(--space-6);
max-width: 52rem;
}
.ask-card {
background: var(--surface-container-lowest);
border-radius: var(--radius-md);
padding: var(--space-6);
display: flex;
flex-direction: column;
gap: var(--space-4);
}
.ask-card-title {
font-family: var(--font-serif);
font-weight: 700;
font-style: italic;
letter-spacing: var(--tracking-snug);
color: var(--on-surface);
margin: 0;
}
.ask-list {
padding-left: var(--space-5);
margin: 0;
color: var(--on-surface-variant);
display: flex;
flex-direction: column;
gap: var(--space-2);
line-height: var(--leading-relaxed);
}
/* ── Join CTA ────────────────────────────────────────────────────── */
.join-section {
padding: var(--space-16) 0 var(--space-12);
max-width: 44rem;
}
.join-cta {
display: flex;
flex-direction: column;
gap: var(--space-4);
}
.join-btn {
display: inline-block;
background: none;
border: none;
cursor: pointer;
font-family: var(--font-serif);
font-size: var(--text-headline-lg);
font-weight: 700;
font-style: italic;
letter-spacing: var(--tracking-tight);
line-height: var(--leading-snug);
color: var(--secondary);
align-self: flex-end;
padding: 0;
text-align: left;
transition: color var(--duration-fast) var(--ease-standard);
}
.card:hover .card-arrow {
.join-btn:hover {
color: var(--secondary-dim);
}
.join-btn:disabled {
opacity: 0.6;
cursor: default;
}
.join-hint {
color: var(--on-surface-muted);
margin: 0;
max-width: 36rem;
line-height: var(--leading-relaxed);
}
/* Confirmed state (injected by JS or server-rendered) */
.join-confirmed {
display: flex;
flex-direction: column;
gap: var(--space-4);
}
.join-confirmed-title {
font-family: var(--font-serif);
font-size: var(--text-headline-lg);
font-weight: 700;
font-style: italic;
letter-spacing: var(--tracking-tight);
color: var(--on-surface);
margin: 0;
}
.join-confirmed-body {
color: var(--on-surface-variant);
margin: 0;
line-height: var(--leading-relaxed);
}
</style>