258 lines
11 KiB
JavaScript
258 lines
11 KiB
JavaScript
// 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 & 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>
|
||
);
|
||
}
|