feat: rebuild marketing site with full component suite and preview

This commit is contained in:
SinachPat
2026-08-27 16:53:12 +01:00
parent 0a2ae76f34
commit 3580c00fa1
24 changed files with 2883 additions and 0 deletions
+41
View File
@@ -0,0 +1,41 @@
const AUTHORS = [
{ tile: "RE", name: "Real Estate & Finance", role: "Market briefs, compliant summaries", status: "confirmed", label: "Co-author confirmed" },
{ tile: "CS", name: "Fractional C-Suite", role: "Strategy & board workflows", status: "confirmed", label: "Co-author confirmed" },
{ tile: "TE", name: "Technology & RAAI", role: "Agent patterns & safe defaults", status: "confirmed", label: "Co-author confirmed" },
{ tile: "HE", name: "Higher Education", role: "Teaching & research integrity", status: "tba", label: "To be announced" },
{ tile: "CO", name: "Communities", role: "Moderation & member programs", status: "tba", label: "To be announced" },
];
export default function Authors() {
return (
<section className="section authors" id="authors">
<div className="wrap">
<span className="spec reveal">Who’s writing this</span>
<h2 className="reveal">Practitioners, one per vertical.</h2>
<p className="lede reveal">
Each chapter is owned by someone who does the work — not a generalist writing about
it. Names are announced as co-authors confirm.
</p>
<div className="auth-grid">
{AUTHORS.map((a) => (
<div className="auth reveal" key={a.tile}>
<div className="mono-tile">{a.tile}</div>
<h3>{a.name}</h3>
<div className="role">{a.role}</div>
<div className={`status ${a.status}`}>
<i></i>
{a.label}
</div>
</div>
))}
<div className="auth auth-join reveal">
<div>
<div className="spec" style={{ marginBottom: "8px" }}>Your name?</div>
<p>Contribute a workflow after launch and earn a byline in the guide.</p>
</div>
</div>
</div>
</div>
</section>
);
}
+35
View File
@@ -0,0 +1,35 @@
/**
* Pure-CSS 3D book styled as the field guide. Decorative, so aria-hidden.
* Faces (cover / spine / fore-edge / top / back) are assembled with
* transform-style: preserve-3d; see `.book3d` in globals.css.
*/
export default function Book3D() {
return (
<div className="book3d" aria-hidden="true">
<div className="book3d__inner">
<div className="book3d__face book3d__back" />
<div className="book3d__face book3d__pages" />
<div className="book3d__face book3d__top" />
<div className="book3d__face book3d__spine">
<span>AIBook.fun · Responsible Agentic AI</span>
</div>
<div className="book3d__face book3d__cover">
<span className="book3d__kicker">The Field Guide</span>
<h3 className="book3d__title">
Responsible
<br />
Agentic AI
</h3>
<span className="book3d__line" />
<span className="book3d__sub">Tested workflows you can actually run</span>
<svg className="book3d__mark" viewBox="0 0 40 40" fill="none">
<circle cx="20" cy="20" r="17.5" stroke="#efe9db" strokeWidth="1" opacity="0.7" />
<path d="M20 5 L22.6 20 L20 35 L17.4 20 Z" fill="#efe9db" />
<path d="M5 20 L20 17.4 L35 20 L20 22.6 Z" fill="#efe9db" opacity="0.55" />
</svg>
<span className="book3d__brand">AIBook.fun</span>
</div>
</div>
</div>
);
}
+22
View File
@@ -0,0 +1,22 @@
import EmailForm from "./EmailForm";
export default function Capture() {
return (
<section className="section capture" id="get">
<div className="capture-in">
<span className="spec reveal">Be first</span>
<h2 className="reveal">Get the preview chapter free.</h2>
<p className="reveal">
Join the pre-launch list. You’ll get one full workflow to keep, a heads-up the morning
we launch on Kickstarter, and early-backer pricing. Three emails. No spam, ever.
</p>
<div className="reveal">
<EmailForm cta="Send it to me" />
</div>
<p className="fine reveal">
Launching Sept 26, 2026 · unsubscribe anytime · we track sign-ups, never your work
</p>
</div>
</section>
);
}
+29
View File
@@ -0,0 +1,29 @@
const ITEMS = [
{ num: "01", name: "Real Estate", desc: "Market briefs & listings, without leaking client data" },
{ num: "02", name: "Financial Advisors", desc: "Portfolio summaries that stay compliant & reviewed" },
{ num: "03", name: "Fractional C-Suite", desc: "Board updates & plans with real escalation paths" },
{ num: "04", name: "Higher Education", desc: "Course & research workflows with academic integrity" },
{ num: "05", name: "Communities", desc: "Moderation & programming that respects members" },
{ num: "06", name: "Technology", desc: "Agent patterns, evals & the responsible defaults" },
];
export default function Contents() {
return (
<section className="section contents" id="contents">
<div className="wrap">
<span className="spec reveal">The founding library</span>
<h2 className="reveal">Six practices. Written by people who do the work.</h2>
<div className="toc reveal">
{ITEMS.map((x) => (
<a href="#get" key={x.num}>
<span className="num">{x.num}</span>
<span className="name">{x.name}</span>
<span className="desc">{x.desc}</span>
<span className="arrow" aria-hidden="true">→</span>
</a>
))}
</div>
</div>
</section>
);
}
+43
View File
@@ -0,0 +1,43 @@
import { useState } from "react";
/**
* Pre-launch email capture. Front-end demo only: on submit it shows a success
* state. Wire `postEmail` to the real CRM endpoint (FluentCRM tag `prelaunch`)
* when the backend is ready.
*/
export default function EmailForm({ className = "", cta = "Send it to me" }) {
const [email, setEmail] = useState("");
const [done, setDone] = useState(false);
async function onSubmit(e) {
e.preventDefault();
if (!email) return;
// TODO: await postEmail(email) — POST to CRM, then setDone(true)
setDone(true);
}
if (done) {
return (
<p className="ok-msg" role="status">
✓ You’re on the list — check your inbox for the preview chapter.
</p>
);
}
return (
<form className={className} onSubmit={onSubmit} noValidate>
<input
className="field"
type="email"
required
placeholder="you@work.com"
aria-label="Email address"
value={email}
onChange={(e) => setEmail(e.target.value)}
/>
<button className="btn btn-primary" type="submit">
{cta}
</button>
</form>
);
}
+44
View File
@@ -0,0 +1,44 @@
const QA = [
{
q: "Is this AI theory, or something I can actually run?",
a: "Something you run. Every workflow shows the real tool output, lists the real steps, and ends in a ~15-minute Try-it on non-confidential sample data. If you can’t run it, it doesn’t ship.",
open: true,
},
{
q: "How is this different from a course?",
a: "A course teaches concepts; this is a field guide of tested recipes you apply to your real job this week. It refreshes annually because AI moves fast — stale advice is worthless.",
},
{
q: "What does “responsible by design” actually mean?",
a: "Every recipe includes data-handling rules, human-oversight steps, failure modes, and escalation paths. They’re part of the template — not an appendix, not an afterthought.",
},
{
q: "Can I contribute a workflow after backing?",
a: "Yes — open contribution opens once the founding library is live. We’re finalizing how contributors get paid (not just a byline) before launch.",
},
{
q: "Why subscribe instead of buy once?",
a: "Because a static AI book is out of date in months. The subscription is what keeps every workflow current — and it’s what funds the practitioners keeping them that way.",
},
];
export default function FAQ() {
return (
<section className="section faq" id="faq">
<div className="wrap" style={{ maxWidth: "760px" }}>
<h2 className="reveal">Frequently asked.</h2>
<div className="qa reveal">
{QA.map((x, i) => (
<details key={i} open={x.open || undefined}>
<summary>
{x.q}
<span className="pl" aria-hidden="true">+</span>
</summary>
<div className="ans">{x.a}</div>
</details>
))}
</div>
</div>
</section>
);
}
+35
View File
@@ -0,0 +1,35 @@
const NOTES = [
{
n: "01 / Stays current",
h: "A subscription, not a static book",
p: "AI moves too fast for a book you buy once. Each annual edition is refreshed so the workflows still work when you open them.",
},
{
n: "02 / Authored, then open",
h: "Written by practitioners first",
p: "The first edition is seeded by co-authors — a real founding library, not an empty shelf. Open, paid contribution opens once it’s worth browsing.",
},
{
n: "03 / Responsible by design",
h: "Guardrails in every recipe",
p: "Data handling, human oversight, failure modes, escalation — part of the template on every workflow, never an appendix.",
},
];
export default function FieldNotes() {
return (
<section className="notes">
<div className="wrap">
<div className="notes-grid">
{NOTES.map((x) => (
<div className="note reveal" key={x.n}>
<div className="n">{x.n}</div>
<h3>{x.h}</h3>
<p>{x.p}</p>
</div>
))}
</div>
</div>
</section>
);
}
+32
View File
@@ -0,0 +1,32 @@
export default function Footer() {
return (
<footer className="footer">
<div className="foot-in">
<div className="foot-top">
<div>
<div className="logo">
AIBook<b>.</b>
<span className="dot">fun</span>
</div>
<p className="foot-blurb">
A living, crowd-written field guide to using AI agents responsibly at work. Part
of the WeOwnNet ecosystem.
</p>
</div>
<div className="foot-links">
<a href="#recipe">A recipe</a>
<a href="#contents">Contents</a>
<a href="#get">Preview</a>
<a href="#faq">FAQ</a>
</div>
</div>
<div className="colophon">
© 2026 WeOwnAI · MIT-licensed docs · Launching on Kickstarter, Sept 26, 2026
<br />
Set in Fraunces, Hanken Grotesk & JetBrains Mono. This is a pre-launch page — pledge
tiers and pricing are finalized at launch.
</div>
</div>
</footer>
);
}
+42
View File
@@ -0,0 +1,42 @@
import EmailForm from "./EmailForm";
import Book3D from "./Book3D";
export default function Hero() {
return (
<section className="hero" id="top">
<div className="hero-tabs" aria-hidden="true">
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
</div>
<div className="wrap hero-grid">
<div className="hero-visual">
<Book3D />
</div>
<div className="hero-copy">
<div className="eyebrow">
<span className="tick"></span>
<span className="spec">
Field guide · Edition&nbsp;001 · Kickstarter&nbsp;Sept&nbsp;26,&nbsp;2026
</span>
</div>
<h1>
The field guide to using AI agents{" "}
<span className="serif-it">responsibly</span> at work.
</h1>
<p className="hero-sub">
Not theory. Not hype. A subscription guide of{" "}
<span className="hl">tested workflows you can actually run</span> — each with the
real tool output, the guardrails, and a 15-minute exercise so you learn by doing.
</p>
<EmailForm className="hero-form" cta="Send me the preview chapter" />
<p className="hero-note">
Free preview + first access at launch · <b>3 emails, no spam</b>
</p>
</div>
</div>
</section>
);
}
+24
View File
@@ -0,0 +1,24 @@
export default function Letter() {
return (
<section className="section letter" id="letter">
<div className="letter-in">
<span className="spec reveal">A note on why</span>
<p className="letter-body reveal">
Most AI advice is either hype or theory — and neither survives contact with real
work. We’re writing the guide we wish we’d had: workflows that <em>actually run</em>,
with the <span className="mark">guardrails built in</span>, kept honest by the people
who use them every day. Fund the first edition, and you don’t just get the book — you
help decide what goes in it.
</p>
<div className="sign reveal">
<span className="scrawl">The co-authors</span>
<span className="who">
AIBook.fun founding team
<br />
Real estate · finance · leadership · tech
</span>
</div>
</div>
</section>
);
}
+83
View File
@@ -0,0 +1,83 @@
import { useEffect, useRef, useState } from "react";
const LINKS = [
{ href: "#recipe", label: "A recipe" },
{ href: "#contents", label: "Contents" },
{ href: "#letter", label: "Why" },
{ href: "#faq", label: "FAQ" },
];
export default function Nav() {
const [scrolled, setScrolled] = useState(false);
const [open, setOpen] = useState(false);
const navRef = useRef(null);
useEffect(() => {
const onScroll = () => setScrolled(window.scrollY > 12);
onScroll();
window.addEventListener("scroll", onScroll, { passive: true });
return () => window.removeEventListener("scroll", onScroll);
}, []);
// While the mobile menu is open: close on Escape or an outside tap.
useEffect(() => {
if (!open) return;
const onKey = (e) => e.key === "Escape" && setOpen(false);
const onDown = (e) => {
if (navRef.current && !navRef.current.contains(e.target)) setOpen(false);
};
document.addEventListener("keydown", onKey);
document.addEventListener("pointerdown", onDown);
return () => {
document.removeEventListener("keydown", onKey);
document.removeEventListener("pointerdown", onDown);
};
}, [open]);
const close = () => setOpen(false);
return (
<nav ref={navRef} className={`nav${scrolled ? " scrolled" : ""}${open ? " open" : ""}`}>
<div className="nav-in">
<a className="logo" href="#top" onClick={close}>
AIBook<b>.</b>
<span className="dot">fun</span>
</a>
<div className="nav-links">
{LINKS.map((l) => (
<a key={l.href} href={l.href}>
{l.label}
</a>
))}
</div>
<a href="#get" className="btn btn-primary nav-cta">
Get the preview
</a>
<button
type="button"
className="nav-toggle"
aria-label={open ? "Close menu" : "Open menu"}
aria-expanded={open}
aria-controls="mobile-menu"
onClick={() => setOpen((v) => !v)}
>
<span className="bars" aria-hidden="true"></span>
</button>
</div>
<div className="nav-menu" id="mobile-menu">
{LINKS.map((l) => (
<a key={l.href} href={l.href} onClick={close}>
{l.label}
</a>
))}
<a href="#get" className="btn btn-primary" onClick={close}>
Get the preview
</a>
</div>
</nav>
);
}
+120
View File
@@ -0,0 +1,120 @@
function ShieldCheck() {
return (
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true">
<path
d="M8 1.5 2.5 4v3.5c0 3 2.3 5.4 5.5 7 3.2-1.6 5.5-4 5.5-7V4L8 1.5Z"
stroke="#1C5D4C"
strokeWidth="1.3"
strokeLinejoin="round"
/>
<path
d="m5.6 8 1.7 1.7L10.6 6"
stroke="#1C5D4C"
strokeWidth="1.3"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
);
}
export default function Recipe() {
return (
<section className="section" id="recipe">
<div className="wrap">
<div className="recipe-head reveal">
<span className="spec">Read an actual recipe</span>
<h2>Every entry is a workflow you can run.</h2>
<p>
Here’s a real one from the founding library — the whole thing, exactly as it
appears in the guide. This is the format behind every workflow.
</p>
</div>
<div className="recipe reveal">
<div className="recipe-top">
<span className="recipe-id">
recipe <b>RE-04</b> · v1.2 · ~15 min
</span>
<span className="recipe-vert">Real Estate</span>
</div>
<div className="recipe-body">
<h3 className="recipe-title">
Draft a property-market brief with an AI agent — without leaking client data.
</h3>
<div className="row">
<div className="k">Problem</div>
<div className="v">
You need a sharp neighborhood market brief for a client meeting, fast — but
the listing sheet and client notes can’t go into a public model.{" "}
<span className="sub">
Job: turn messy local data into a credible one-pager in minutes, safely.
</span>
</div>
</div>
<div className="row">
<div className="k">Method</div>
<div className="v">
<ol className="steps">
<li>Strip identifiers from the source data locally (a provided script does this).</li>
<li>Prompt the agent with the anonymized sample and a fixed brief template.</li>
<li>Review the draft against the checklist before anything leaves your machine.</li>
</ol>
<div className="term" role="img" aria-label="Sample terminal output from the workflow">
<div className="term-bar">
<i></i>
<i></i>
<i></i>
<span>agent — property-brief · sample dataset (non-confidential)</span>
</div>
<pre>
<span className="c-dim">{"$ "}</span>
<span className="c-user">{"brief"}</span>
{" --input "}
<span className="c-key">{"sample_listings.csv"}</span>
{" --template market-1pager\n\n"}
<span className="c-dim">{"✓ redacted 3 fields (owner_name, phone, exact_address)\n✓ 41 comps in radius · median $/sqft computed\n\n"}</span>
<span className="c-ok">{"MARKET BRIEF — Maple Heights (sample)\n"}</span>
{" · Median list: $420/sqft (▲ 4.1% QoQ)\n · Days on market: 22 (was 31)\n · Inventory: 1.8 mo — seller-leaning\n · Read: price to comp, expect <2 wks at ask\n\n"}
<span className="c-dim">{"# cost this run: $0.03 · model: claude · 2,140 tokens"}</span>
</pre>
</div>
</div>
</div>
<div className="row">
<div className="k">Safeguards</div>
<div className="v">
<div className="guard">
<ShieldCheck />
<span>
Redaction runs <b style={{ color: "var(--color-ink)" }}>on your machine</b> —
raw client data never reaches the model.
</span>
</div>
<div className="guard">
<ShieldCheck />
<span>
A human review step is required before the brief is shared. The checklist
is part of the recipe.
</span>
</div>
</div>
</div>
<div className="row">
<div className="k">Try it</div>
<div className="v">
Run the same brief on the included sample dataset (no real client data) in
about 15 minutes — then swap in your own, safely.{" "}
<span className="chip">◷ cost to try ≈ $0.03</span>
</div>
</div>
</div>
</div>
</div>
</section>
);
}
+50
View File
@@ -0,0 +1,50 @@
export default function Tiers() {
return (
<section className="section" id="back">
<div className="wrap">
<div className="tiers-head">
<div className="reveal">
<span className="spec">When we launch</span>
<h2>What backing will look like.</h2>
</div>
<p className="tiers-note reveal">
Preview of planned pledge tiers. Final pricing is confirmed at the Kickstarter
launch — nothing here is a live checkout yet.
</p>
</div>
<div className="tier-grid">
<div className="tier reveal">
<div className="t-name">Supporter</div>
<div className="t-price">$10</div>
<div className="t-unit">one-time</div>
<div className="t-desc">
The preview chapter, community updates, and an early look at the founding library
as it’s written.
</div>
</div>
<div className="tier feature reveal">
<div className="t-tag">The one to get</div>
<div className="t-name">The Book</div>
<div className="t-price">
$47 <small>/yr</small>
</div>
<div className="t-unit">annual subscription</div>
<div className="t-desc">
The full guide — PDF + web reader — and every annual update while you’re
subscribed. Recurring value, no lifetime gimmick.
</div>
</div>
<div className="tier reveal">
<div className="t-name">Founding Sponsor</div>
<div className="t-price">Founding</div>
<div className="t-unit">limited · final terms at launch</div>
<div className="t-desc">
A named acknowledgment in the guide, a private cohort seat, and a hand in shaping
the first edition.
</div>
</div>
</div>
</div>
</section>
);
}