customer-presentation/advisory-board-post/board-posts.jsx
2026-06-11 14:19:02 +02:00

258 lines
11 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// board-posts.jsx — Four LinkedIn-ready layout variations for an 8-person
// board reveal post. Each variation is a self-contained, sized artboard
// rendered inside the design canvas so they can be compared side-by-side
// and any one can be opened fullscreen.
//
// Shared image-slot ids ("member-1"..."member-8") mean once you drop a
// portrait it appears in every variation. Edit the MEMBERS array below to
// fill in real names and role-at-company lines.
const MEMBERS = [
{ id: 'member-1', name: '[ Full Name ]', title: 'Former CTO', company: '[ Company ]' },
{ id: 'member-2', name: '[ Full Name ]', title: 'Professor', company: '[ Institution ]' },
{ id: 'member-3', name: '[ Full Name ]', title: 'CEO', company: '[ Company ]' },
{ id: 'member-4', name: '[ Full Name ]', title: 'Head of Research', company: '[ Institute ]' },
{ id: 'member-5', name: '[ Full Name ]', title: 'Partner', company: '[ Firm ]' },
{ id: 'member-6', name: '[ Full Name ]', title: 'CEO', company: '[ Company ]' },
{ id: 'member-7', name: '[ Full Name ]', title: 'Chief Scientist', company: '[ Company ]' },
{ id: 'member-8', name: '[ Full Name ]', title: 'Founder', company: '[ Company ]' },
];
// Reusable portrait + caption block. `size` is the portrait square edge.
function Member({ m, size, captionAlign = 'left' }) {
return (
<div className="member" style={{ textAlign: captionAlign }}>
<image-slot
id={m.id}
shape="rounded"
radius="4"
placeholder="Drop portrait"
style={{ width: '100%', height: size + 'px', display: 'block' }}
/>
<div className="name">{m.name}</div>
<div className="title">{m.title}</div>
<div className="company">{m.company}</div>
</div>
);
}
// Footer brand mark — used across variations
function Mark({ light = false }) {
return (
<div className="mark">
<img
src="assets/fenja-logo-full.png"
alt="Fenja AI"
/>
</div>
);
}
// ───────────────────────────────────────────────────────────────────────
// A — Editorial Square (1200 × 1200) — clean 4×2 grid
// ───────────────────────────────────────────────────────────────────────
function PostA() {
return (
<div className="post a-root">
<div className="a-head">
<h1>Meet the Fenja AI <em>Advisory Board</em></h1>
<div className="subtitle">Bridging Industry &amp; Sovereign AI</div>
</div>
<div className="a-grid">
{MEMBERS.map((m) => (
<Member key={m.id} m={m} size={210} />
))}
</div>
<div className="a-foot">
<Mark />
</div>
</div>
);
}
// ───────────────────────────────────────────────────────────────────────
// B — Catalogue Index (1080 × 1350) — numbered vertical list
// ───────────────────────────────────────────────────────────────────────
function PostB() {
return (
<div className="post b-root">
<div className="b-head">
<div className="left">
<p className="eyebrow"><span className="rule" />A note from leadership</p>
<h1 style={{ marginTop: 26 }}>Our <em>board.</em></h1>
<p className="lede b-lede">
Eight quiet experts, each chosen for their depth and discretion.
We are grateful they said yes.
</p>
</div>
<div className="right">
<div className="mark">
<img src="assets/fenja-icon-black.svg" alt="" />
<span>Fenja AI</span>
</div>
<div style={{
marginTop: 18, fontFamily: 'var(--font-serif)', fontStyle: 'italic',
color: 'var(--on-surface-muted)', fontSize: 14, letterSpacing: 0,
}}>
§ 01 MMXXV
</div>
</div>
</div>
<div className="b-list">
{MEMBERS.map((m, i) => (
<div className="b-row" key={m.id}>
<div className="idx">{String(i + 1).padStart(2, '0')}</div>
<image-slot
id={m.id}
shape="rounded"
radius="4"
placeholder=""
style={{ width: 72, height: 72, display: 'block' }}
/>
<div className="role">
<div className="name">{m.name}</div>
<div className="title">{m.title}</div>
<div className="company">{m.company}</div>
</div>
</div>
))}
</div>
<div className="b-foot">
<div className="quiet">
"A board built the way a good archive is: slowly, with care, and with people you can trust at four in the morning."
</div>
<div style={{
fontFamily: 'var(--font-sans)', fontSize: 12, color: 'var(--on-surface-muted)',
letterSpacing: '0.14em', textTransform: 'uppercase',
}}>
fenja.ai / board
</div>
</div>
</div>
);
}
// ───────────────────────────────────────────────────────────────────────
// C — Editorial Landscape (1200 × 627) — left text · right micro grid
// ───────────────────────────────────────────────────────────────────────
function PostC() {
return (
<div className="post c-root">
<div className="c-left">
<div>
<p className="eyebrow"><span className="rule" />Announcement</p>
<h1>Introducing our <em>board.</em></h1>
<p className="lede">
Eight quiet experts, gathered to steward the work ahead in research,
in product, in counsel.
</p>
</div>
<Mark />
</div>
<div className="c-grid">
{MEMBERS.map((m) => (
<Member key={m.id} m={m} size={104} />
))}
</div>
</div>
);
}
// ───────────────────────────────────────────────────────────────────────
// D — Quiet Cover + Strip (1080 × 1350) — text hero with portrait band
// ───────────────────────────────────────────────────────────────────────
function PostD() {
return (
<div className="post d-root">
<div className="d-hero" style={{ position: 'relative' }}>
{/* Topographic currents accent — quiet, off-axis */}
<svg className="currents" style={{ right: -40, top: 80, width: 360, height: 360 }} viewBox="0 0 360 360" fill="none">
{[0,1,2,3,4,5,6,7].map((i) => (
<path key={i}
d={`M ${20 + i*8} ${180 + i*4} C ${100} ${120 - i*6}, ${260} ${240 + i*4}, ${340 - i*8} ${180 - i*6}`}
stroke="#8a887f" strokeWidth="0.8" fill="none" opacity={0.55 - i*0.04}
/>
))}
</svg>
<div>
<p className="eyebrow"><span className="rule" />Introducing board of directors · MMXXV</p>
<h1>Eight people. <em>One quiet table.</em></h1>
<p className="lede">
We are honored to introduce the board of Fenja AI. Together, they bring
decades of experience in research, scholarship, and stewardship
and the patience to do this work well.
</p>
<div className="signoff">
"A study in stillness, and in counsel. We are grateful, every one of us, that they said yes."
</div>
</div>
<div style={{ display: 'flex', alignItems: 'flex-end', justifyContent: 'space-between' }}>
<Mark />
<div style={{
fontFamily: 'var(--font-sans)', fontSize: 12, color: 'var(--on-surface-muted)',
letterSpacing: '0.14em', textTransform: 'uppercase',
}}>
fenja.ai
</div>
</div>
</div>
<div className="d-strip">
<p className="eyebrow d-strip-label"><span className="rule" />The board, in order of seating</p>
<div className="d-strip-grid">
{MEMBERS.map((m) => (
<Member key={m.id} m={m} size={112} />
))}
</div>
</div>
</div>
);
}
// ───────────────────────────────────────────────────────────────────────
// Canvas
// ───────────────────────────────────────────────────────────────────────
function App() {
return (
<DesignCanvas title="Board reveal · LinkedIn" subtitle="Drag portraits onto the slots · double-click any text to edit · click ⤢ on an artboard to view fullscreen.">
<DCSection
id="square"
title="Square — 1200 × 1200"
subtitle="Standard LinkedIn single-image post. The full grid at a glance."
>
<DCArtboard id="a" label="A · Editorial grid" width={1200} height={1200}>
<PostA />
</DCArtboard>
</DCSection>
<DCSection
id="portrait"
title="Portrait — 1080 × 1350"
subtitle="Vertical post. Maximizes feed real estate; best for text-forward variants."
>
<DCArtboard id="b" label="B · Catalogue index" width={1080} height={1350}>
<PostB />
</DCArtboard>
<DCArtboard id="d" label="D · Quiet cover + strip" width={1080} height={1350}>
<PostD />
</DCArtboard>
</DCSection>
<DCSection
id="landscape"
title="Landscape — 1200 × 627"
subtitle="Link-preview aspect ratio. Compact, scannable, lives well on desktop feed."
>
<DCArtboard id="c" label="C · Editorial landscape" width={1200} height={627}>
<PostC />
</DCArtboard>
</DCSection>
</DesignCanvas>
);
}