refactor: home page structure and pacing

This commit is contained in:
Jonathan 2026-04-19 20:46:06 +02:00
parent f6e6fb255d
commit bc8e08d022

View file

@ -19,36 +19,37 @@ const innofoundarLogoExists = existsSync(join(process.cwd(), 'public/innofounder
<p class="hero-greeting">Welcome, <em>{firstName}.</em></p> <p class="hero-greeting">Welcome, <em>{firstName}.</em></p>
</section> </section>
<!-- ── Opening statement ─────────────────────────────────────── --> <!-- ── Opening ───────────────────────────────────────────────── -->
<section class="opening"> <div class="reading-col opening">
<p class="opening-statement"> <p class="opening-statement">
Secure and sovereign AI for regulated environments. Secure and sovereign AI for regulated environments.
Project Bifrost is where we build it — together with the organisations Project Bifrost is where we build it — together with the organisations
who will depend on it. who will depend on it.
</p> </p>
</section> </div>
<!-- ── What Fenja is ─────────────────────────────────────────── --> <!-- ── Pulled quote ───────────────────────────────────────────── -->
<section class="section fenja-section"> <div class="quote-break">
<div class="section-text"> <blockquote class="pulled-quote">
<h2 class="section-heading label-sm">What Fenja is</h2> <p class="pulled-quote__text">
<div class="prose"> We can't build our digital<br />fortress on foreign clouds.
</p>
<footer class="pulled-quote__attribution">— The Project Bifrost premise</footer>
</blockquote>
</div>
<!-- ── What Fenja is + 97% stat ──────────────────────────────── -->
<div class="fenja-row">
<div class="fenja-text">
<h2 class="section-label">What Fenja is</h2>
<p class="body-lg"> <p class="body-lg">
Fenja is a platform for running advanced AI inside the customer's own infrastructure. 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 We build for public authorities and regulated industries — pharmaceuticals, defence,
heavily regulated industries — where trust, auditability, and data residency are not finance — where standard AI on foreign-owned public clouds is simply not a viable
preferences but legal requirements. or compliant option. Data remains under absolute, localised control,
</p> with the traceability and documentation the market demands.
<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> </p>
</div> </div>
</div>
<figure class="stat-figure" aria-label="Key statistic"> <figure class="stat-figure" aria-label="Key statistic">
<p class="stat-figure__number">97%</p> <p class="stat-figure__number">97%</p>
<figcaption class="stat-figure__label"> <figcaption class="stat-figure__label">
@ -56,11 +57,11 @@ const innofoundarLogoExists = existsSync(join(process.cwd(), 'public/innofounder
</figcaption> </figcaption>
<p class="stat-source label-sm">— Stanford AI Index, 2024</p> <p class="stat-source label-sm">— Stanford AI Index, 2024</p>
</figure> </figure>
</section> </div>
<!-- ── What Project Bifrost is ───────────────────────────────── --> <!-- ── What Project Bifrost is ───────────────────────────────── -->
<section class="section bifrost-section"> <div class="reading-col bifrost-block">
<h2 class="section-heading label-sm">What Project Bifrost is</h2> <h2 class="section-label">What Project Bifrost is</h2>
<div class="prose"> <div class="prose">
<p class="body-lg"> <p class="body-lg">
Project Bifrost is the first pilot — a working collaboration between Fenja and five Project Bifrost is the first pilot — a working collaboration between Fenja and five
@ -74,20 +75,20 @@ const innofoundarLogoExists = existsSync(join(process.cwd(), 'public/innofounder
knowledge base needs to understand. Their input is built in, not bolted on. knowledge base needs to understand. Their input is built in, not bolted on.
</p> </p>
</div> </div>
</section> </div>
<!-- ── Backed by Innofounder ──────────────────────────────────── --> <!-- ── Backed by Innofounder ──────────────────────────────────── -->
<section class="section innofounder-section"> <div class="reading-col innofounder-block">
<div class="innofounder-header"> <div class="innofounder-logo-row">
{innofoundarLogoExists ? ( {innofoundarLogoExists ? (
<img src="/innofounder-logo.png" alt="Innofounder — Innovation Fund Denmark" class="innofounder-logo" /> <img src="/innofounder-logo.png" alt="Innofounder — Innovation Fund Denmark" class="innofounder-logo" />
) : ( ) : (
<div class="innofounder-placeholder" aria-label="Innofounder logo — add file"> <div class="innofounder-placeholder" aria-label="Innofounder logo — add file">
<span class="label-sm">Innofounder logo — add <code>public/innofounder-logo.svg</code></span> <span class="label-sm">Innofounder logo — add <code>public/innofounder-logo.png</code></span>
</div> </div>
)} )}
<h2 class="section-heading label-sm">Backed by Innofounder</h2>
</div> </div>
<h2 class="section-label">Backed by Innofounder</h2>
<div class="prose"> <div class="prose">
<p class="body-lg"> <p class="body-lg">
Fenja is backed by Innofounder — Innovation Fund Denmark's programme for Fenja is backed by Innofounder — Innovation Fund Denmark's programme for
@ -103,15 +104,17 @@ const innofoundarLogoExists = existsSync(join(process.cwd(), 'public/innofounder
conditions for a genuine win-win: you shape the platform, and the platform works for you. conditions for a genuine win-win: you shape the platform, and the platform works for you.
</p> </p>
</div> </div>
</section> </div>
<!-- ── What we ask ───────────────────────────────────────────── --> <!-- ── What we ask / What you get ────────────────────────────── -->
<section class="section ask-section"> <div class="ask-break">
<h2 class="section-heading label-sm">What we ask</h2> <h2 class="ask-headline">
What we ask.<br /><em>What you get.</em>
</h2>
<div class="ask-grid"> <div class="ask-grid">
<div class="ask-card"> <div class="ask-card">
<h3 class="ask-card-title body-lg">Pilot participants</h3> <h3 class="ask-card-title">Pilot participants</h3>
<ul class="ask-list body-md"> <ul class="ask-list">
<li>A few hours a month over three to four months</li> <li>A few hours a month over three to four months</li>
<li>Running the software in your own environment</li> <li>Running the software in your own environment</li>
<li>Direct feedback on what works and what does not</li> <li>Direct feedback on what works and what does not</li>
@ -120,8 +123,8 @@ const innofoundarLogoExists = existsSync(join(process.cwd(), 'public/innofounder
</ul> </ul>
</div> </div>
<div class="ask-card"> <div class="ask-card">
<h3 class="ask-card-title body-lg">Advisory Board members</h3> <h3 class="ask-card-title">Advisory Board members</h3>
<ul class="ask-list body-md"> <ul class="ask-list">
<li>Quarterly conversations to shape direction</li> <li>Quarterly conversations to shape direction</li>
<li>Access to all updates, roadmap, and meetings in this hub</li> <li>Access to all updates, roadmap, and meetings in this hub</li>
<li>No software deployment required</li> <li>No software deployment required</li>
@ -129,7 +132,7 @@ const innofoundarLogoExists = existsSync(join(process.cwd(), 'public/innofounder
</ul> </ul>
</div> </div>
</div> </div>
</section> </div>
<!-- ── Join CTA ───────────────────────────────────────────────── --> <!-- ── Join CTA ───────────────────────────────────────────────── -->
<section class="join-section" id="join-section"> <section class="join-section" id="join-section">
@ -169,7 +172,7 @@ const innofoundarLogoExists = existsSync(join(process.cwd(), 'public/innofounder
section.innerHTML = ` section.innerHTML = `
<div class="join-confirmed"> <div class="join-confirmed">
<h2 class="join-confirmed-title">Welcome to Project Bifrost.</h2> <h2 class="join-confirmed-title">Welcome to Project Bifrost.</h2>
<p class="join-confirmed-body"> <p class="join-confirmed-body body-lg">
We will be in touch shortly to arrange the next step. We will be in touch shortly to arrange the next step.
</p> </p>
</div> </div>
@ -194,6 +197,11 @@ const innofoundarLogoExists = existsSync(join(process.cwd(), 'public/innofounder
padding: 0 var(--space-20) var(--space-16); padding: 0 var(--space-20) var(--space-16);
} }
/* ── Reading column — prose-density sections ─────────────────────── */
.reading-col {
max-width: var(--reading-max);
}
/* ── Hero ────────────────────────────────────────────────────────── */ /* ── Hero ────────────────────────────────────────────────────────── */
.hero { .hero {
padding-top: var(--space-16); padding-top: var(--space-16);
@ -213,11 +221,9 @@ const innofoundarLogoExists = existsSync(join(process.cwd(), 'public/innofounder
letter-spacing: var(--tracking-snug); letter-spacing: var(--tracking-snug);
} }
/* ── Opening statement ───────────────────────────────────────────── */ /* ── Opening ─────────────────────────────────────────────────────── */
.opening { .opening {
padding-bottom: var(--space-12); margin-bottom: var(--space-10);
border-bottom: var(--ghost-border);
max-width: 52rem;
} }
.opening-statement { .opening-statement {
@ -230,52 +236,76 @@ const innofoundarLogoExists = existsSync(join(process.cwd(), 'public/innofounder
margin: 0; margin: 0;
} }
/* ── Sections ────────────────────────────────────────────────────── */ /* ── Pulled quote — full-width breakout ──────────────────────────── */
.section { .quote-break {
padding: var(--space-12) 0; margin: var(--space-20) 0;
border-bottom: var(--ghost-border);
} }
.section-heading { .pulled-quote {
margin: 0;
padding: 0 0 0 var(--space-6);
border-left: 3px solid var(--secondary);
display: flex;
flex-direction: column;
gap: var(--space-4);
max-width: 52rem;
}
.pulled-quote__text {
font-family: var(--font-serif);
font-size: var(--text-display-md);
font-style: italic;
font-weight: 400;
letter-spacing: var(--tracking-tight);
line-height: var(--leading-snug);
color: var(--on-surface);
margin: 0;
}
.pulled-quote__attribution {
font-family: var(--font-sans);
font-size: var(--text-label-md);
letter-spacing: var(--tracking-wider); letter-spacing: var(--tracking-wider);
text-transform: uppercase; text-transform: uppercase;
color: var(--on-surface-muted); color: var(--on-surface-muted);
margin-bottom: var(--space-6);
} }
/* ── Fenja section — two-column layout ──────────────────────────── */ /* ── Fenja row — asymmetric two-column ───────────────────────────── */
.fenja-section { .fenja-row {
display: grid; display: grid;
grid-template-columns: 1fr 18rem; grid-template-columns: 1fr 16rem;
gap: var(--space-12); gap: var(--space-12);
align-items: start; align-items: start;
margin-bottom: var(--space-10);
} }
.section-text { .fenja-text {
display: flex;
flex-direction: column;
}
.section-text .section-heading {
margin-bottom: var(--space-6);
}
/* ── Prose ───────────────────────────────────────────────────────── */
.prose {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: var(--space-5); gap: var(--space-5);
max-width: 36rem;
} }
.prose .body-lg { .fenja-text .body-lg {
margin: 0; margin: 0;
color: var(--on-surface-variant); color: var(--on-surface-variant);
line-height: var(--leading-relaxed); line-height: var(--leading-relaxed);
} }
/* ── Section label ───────────────────────────────────────────────── */
.section-label {
font-family: var(--font-sans);
font-size: var(--text-label-md);
font-weight: 500;
letter-spacing: var(--tracking-wider);
text-transform: uppercase;
color: var(--on-surface-muted);
margin: 0 0 var(--space-4);
}
/* ── Stat figure ─────────────────────────────────────────────────── */ /* ── Stat figure ─────────────────────────────────────────────────── */
.stat-figure { .stat-figure {
margin: var(--space-8) 0 0; margin: var(--space-4) 0 0;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: var(--space-3); gap: var(--space-3);
@ -295,7 +325,7 @@ const innofoundarLogoExists = existsSync(join(process.cwd(), 'public/innofounder
font-family: var(--font-sans); font-family: var(--font-sans);
font-size: var(--text-body-md); font-size: var(--text-body-md);
color: var(--on-surface-variant); color: var(--on-surface-variant);
max-width: 16rem; max-width: 14rem;
line-height: var(--leading-relaxed); line-height: var(--leading-relaxed);
} }
@ -305,93 +335,141 @@ const innofoundarLogoExists = existsSync(join(process.cwd(), 'public/innofounder
margin: 0; margin: 0;
} }
/* ── Innofounder section ─────────────────────────────────────────── */ /* ── Prose ───────────────────────────────────────────────────────── */
.innofounder-section { .prose {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: var(--space-5);
} }
.innofounder-header { .prose .body-lg {
display: flex; margin: 0;
align-items: center; color: var(--on-surface-variant);
gap: var(--space-5); line-height: var(--leading-relaxed);
margin-bottom: var(--space-6); }
/* ── Bifrost block ───────────────────────────────────────────────── */
.bifrost-block {
margin-bottom: var(--space-10);
}
/* ── Innofounder block ───────────────────────────────────────────── */
.innofounder-block {
margin-bottom: 0;
}
.innofounder-logo-row {
margin-bottom: var(--space-5);
} }
.innofounder-logo { .innofounder-logo {
height: 32px; width: 120px;
width: auto; height: auto;
display: block; display: block;
} }
.innofounder-placeholder { .innofounder-placeholder {
display: flex; display: inline-flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
height: 32px; width: 120px;
padding: 0 var(--space-4); height: 40px;
background: var(--surface-container); background: var(--surface-container);
border-radius: var(--radius-sm); border-radius: var(--radius-sm);
padding: 0 var(--space-3);
} }
.innofounder-placeholder .label-sm { .innofounder-placeholder .label-sm {
color: var(--on-surface-muted); color: var(--on-surface-muted);
letter-spacing: var(--tracking-wide); letter-spacing: var(--tracking-normal);
font-size: var(--text-label-sm);
text-align: center;
line-height: var(--leading-snug);
} }
.innofounder-placeholder code { .innofounder-placeholder code {
font-family: var(--font-mono); font-family: var(--font-mono);
font-size: 0.9em; font-size: 0.9em;
color: var(--on-surface-variant);
} }
.innofounder-section .section-heading { /* ── What we ask — full-width breakout ───────────────────────────── */
.ask-break {
margin-top: var(--space-24);
margin-bottom: 0; margin-bottom: 0;
} }
.innofounder-section .prose { .ask-headline {
margin-top: 0; font-family: var(--font-serif);
font-size: var(--text-display-md);
font-weight: 400;
letter-spacing: var(--tracking-tight);
line-height: var(--leading-snug);
color: var(--on-surface);
margin: 0 0 var(--space-10);
}
.ask-headline em {
font-weight: 700;
font-style: italic;
} }
/* ── What we ask — two cards ─────────────────────────────────────── */
.ask-grid { .ask-grid {
display: grid; display: grid;
grid-template-columns: 1fr 1fr; grid-template-columns: 1fr 1fr;
gap: var(--space-6); gap: var(--space-8);
max-width: 52rem; max-width: 56rem;
} }
.ask-card { .ask-card {
background: var(--surface-container-lowest);
border-radius: var(--radius-md);
padding: var(--space-6);
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: var(--space-4); gap: var(--space-5);
padding: var(--space-8);
background: var(--surface-container-lowest);
border-radius: var(--radius-md);
} }
.ask-card-title { .ask-card-title {
font-family: var(--font-serif); font-family: var(--font-serif);
font-size: var(--text-headline-sm);
font-weight: 700; font-weight: 700;
font-style: italic; font-style: italic;
letter-spacing: var(--tracking-snug); letter-spacing: var(--tracking-snug);
line-height: var(--leading-snug);
color: var(--on-surface); color: var(--on-surface);
margin: 0; margin: 0;
} }
.ask-list { .ask-list {
padding-left: var(--space-5); list-style: none;
padding: 0;
margin: 0; margin: 0;
color: var(--on-surface-variant);
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: var(--space-2); gap: var(--space-4);
}
.ask-list li {
font-family: var(--font-sans);
font-size: var(--text-body-lg);
color: var(--on-surface-variant);
line-height: var(--leading-relaxed); line-height: var(--leading-relaxed);
padding-left: var(--space-5);
position: relative;
}
.ask-list li::before {
content: '—';
position: absolute;
left: 0;
color: var(--on-surface-muted);
font-family: var(--font-serif);
} }
/* ── Join CTA ────────────────────────────────────────────────────── */ /* ── Join CTA ────────────────────────────────────────────────────── */
.join-section { .join-section {
padding: var(--space-16) 0 var(--space-12); margin-top: var(--space-24);
padding-bottom: var(--space-16);
max-width: 44rem; max-width: 44rem;
} }
@ -417,13 +495,8 @@ const innofoundarLogoExists = existsSync(join(process.cwd(), 'public/innofounder
text-align: left; text-align: left;
transition: color var(--duration-fast) var(--ease-standard); transition: color var(--duration-fast) var(--ease-standard);
} }
.join-btn:hover { .join-btn:hover { color: var(--secondary-dim); }
color: var(--secondary-dim); .join-btn:disabled { opacity: 0.6; cursor: default; }
}
.join-btn:disabled {
opacity: 0.6;
cursor: default;
}
.join-hint { .join-hint {
color: var(--on-surface-muted); color: var(--on-surface-muted);
@ -432,7 +505,6 @@ const innofoundarLogoExists = existsSync(join(process.cwd(), 'public/innofounder
line-height: var(--leading-relaxed); line-height: var(--leading-relaxed);
} }
/* Confirmed state (injected by JS or server-rendered) */
.join-confirmed { .join-confirmed {
display: flex; display: flex;
flex-direction: column; flex-direction: column;