Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
1fa3333
Mobile: slides grow with content, proximity snap, graded page tracking
hhff Aug 10, 2026
cb09b12
Snap disables itself when any slide runs taller than the viewport
hhff Aug 10, 2026
f79284f
First-device media row: mosaic grid on small screens
hhff Aug 10, 2026
4c9ccb4
Mobile round: full-width chips, tighter GPU frame, stack cards text-o…
hhff Aug 10, 2026
b94602b
Stack cards adopt the QuoteBox shell; platform tables unified
hhff Aug 10, 2026
033e218
Platform slide: tables side by side on desktop
hhff Aug 10, 2026
3cac2c1
Team: Yatú headshot
hhff Aug 10, 2026
fdc2145
Team: Norm headshot
hhff Aug 10, 2026
c81eb29
Team: Hugh headshot, photo FPO helper retired
hhff Aug 10, 2026
ce01f5e
Copy round 27: act I rewrites, family-archive framing, team credentials
hhff Aug 10, 2026
4229ae9
Locked cover: wordmark, sub, and gate centered
hhff Aug 10, 2026
824d8f6
Family vault: illustrated vault-contents asset replaces the FPO
hhff Aug 10, 2026
b4518f8
Family vault art ships with its transparent background
hhff Aug 10, 2026
2d96671
Family vault art as webp with alpha (1.6MB to a fraction)
hhff Aug 10, 2026
60a7771
Family vault webp actually written via Pillow
hhff Aug 10, 2026
de193c9
Locked cover: more air above the gate
hhff Aug 10, 2026
cd3643e
Cover: centered layout for locked and unlocked alike
hhff Aug 10, 2026
579166a
Unlocked cover: scroll-down cue with a bobbing arrow
hhff Aug 10, 2026
270b178
Local AI slide: frontier line moves into the accent box
hhff Aug 10, 2026
bc85b7a
Cover byline scales down on mobile instead of wrapping
hhff Aug 10, 2026
3071cf0
Gallery slot 3: Mozilla research illustration with caption
hhff Aug 10, 2026
31054ca
Gallery caption reserve grows to fit the Mozilla caption
hhff Aug 10, 2026
fd00b6c
Snapdragon caption tightened; Norm bio expanded
hhff Aug 10, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 22 additions & 2 deletions www/app/opportunity/components/DeckShell.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,26 @@ export default function DeckShell({
}>;
}) {
const [current, setCurrent] = useState(1);
const [noSnap, setNoSnap] = useState(false);

// Snapping only works when every slide fits the viewport; the moment any
// slide runs taller (small screens), snap turns off entirely.
useEffect(() => {
const measure = () => {
const sections = document.querySelectorAll<HTMLElement>(
'section[id^="page-"]'
);
const vh = window.innerHeight;
let tall = false;
sections.forEach(section => {
if (section.offsetHeight > vh + 1) tall = true;
});
setNoSnap(tall);
};
measure();
window.addEventListener('resize', measure);
return () => window.removeEventListener('resize', measure);
}, [pages.length]);

useEffect(() => {
const handler = (e: KeyboardEvent) => {
Expand Down Expand Up @@ -64,15 +84,15 @@ export default function DeckShell({
}
if (best) setCurrent(best.page);
},
{ threshold: 0.6 }
{ threshold: [0.2, 0.4, 0.6] }
);

sections.forEach(section => observer.observe(section));
return () => observer.disconnect();
}, [pages.length]);

return (
<div className="deck">
<div className={`deck${noSnap ? ' deck-no-snap' : ''}`}>
<div className="deck-ambient" aria-hidden="true">
<div
className={`deck-leaves${pageMeta[current - 1]?.leaves ? '' : ' deck-leaves-hidden'}${pageMeta[current - 1]?.dark ? ' deck-leaves-dark' : ''}`}
Expand Down
2 changes: 1 addition & 1 deletion www/app/opportunity/components/TimelineGantt.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,7 @@ export default function TimelineGantt() {
/>
))}
<span
className={`gantt-bar gantt-${task.status}`}
className={`gantt-bar gantt-${task.status}${task.end / LAST_MONTH > 0.6 ? ' gantt-bar-late' : ''}`}
style={{
left: pct(task.start),
width: `calc(${pct(task.end)} - ${pct(task.start)})`,
Expand Down
36 changes: 27 additions & 9 deletions www/app/opportunity/components/archetypes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -310,7 +310,7 @@ export function StatTiles({
}) {
return (
<div
className="mt-6 grid gap-4"
className="deck-stat-tiles mt-6 grid gap-4"
style={{
gridTemplateColumns: stacked ? '1fr' : `repeat(${tiles.length}, 1fr)`,
}}
Expand All @@ -330,6 +330,7 @@ export function CardsPage({
sub,
cards,
columns = 1,
variant,
}: {
title: ReactNode;
sub?: ReactNode;
Expand All @@ -338,9 +339,13 @@ export function CardsPage({
body: ReactNode;
art?: ReactNode;
photo?: ReactNode;
/** Footer tag rendered in the QuoteBox-style strip (quote variant). */
meta?: string;
}>;
/** 2, 3 or 4 lays the cards out as an equal-height grid instead of a stack. */
columns?: 1 | 2 | 3 | 4;
/** 'quote' swaps the ragged fill for the homepage QuoteBox card shell. */
variant?: 'quote';
}) {
const tight = columns !== 1;
const grid =
Expand All @@ -359,16 +364,29 @@ export function CardsPage({
{cards.map(card => (
<div
key={card.heading}
className={`deck-card h-full rounded-[8px] bg-fi-green-200 ${columns === 4 ? 'p-4' : tight ? 'p-5' : 'p-6'}`}
className={`deck-card h-full rounded-[8px] bg-fi-green-200 ${
variant === 'quote' ? 'deck-card-quote ' : ''
}${columns === 4 ? 'p-4' : tight ? 'p-5' : 'p-6'}`}
>
{card.art && <span className="deck-card-icon">{card.art}</span>}
{card.photo && (
<span className="deck-card-photo">{card.photo}</span>
<div
className={
variant === 'quote' ? 'deck-card-quote-main' : undefined
}
>
{card.art && <span className="deck-card-icon">{card.art}</span>}
{card.photo && (
<span className="deck-card-photo">{card.photo}</span>
)}
<h4>{card.heading}</h4>
<p className={tight ? 'deck-card-body-tight' : 'deck-card-body'}>
{card.body}
</p>
</div>
{variant === 'quote' && card.meta && (
<div className="deck-tier-footer">
<span className="deck-tier-meta">{card.meta}</span>
</div>
)}
<h4>{card.heading}</h4>
<p className={tight ? 'deck-card-body-tight' : 'deck-card-body'}>
{card.body}
</p>
</div>
))}
</div>
Expand Down
52 changes: 31 additions & 21 deletions www/app/opportunity/content/act1.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@ const coverLeafStyle = {
} as const;

const coverSubStyle = {
fontSize: 'clamp(20px, 3.2vw, 34px)',
// The floor tracks viewport width so the first line never wraps on phones.
fontSize: 'clamp(12px, 3.2vw, 34px)',
whiteSpace: 'nowrap',
} as const;

/**
Expand All @@ -26,7 +28,7 @@ const coverSubStyle = {
export function coverPage(gate: ReactNode): ReactNode {
return (
<DeckPage key={1} n={1} total={TOTAL}>
<div className="relative mb-auto max-w-3xl">
<div className="relative mx-auto max-w-3xl text-center">
<h1 className="relative inline-block">
Family<span className="tracking-[-0.1em]"> </span>Intelligence
<LeafIcon className="absolute leaf-animate" style={coverLeafStyle} />
Expand All @@ -46,8 +48,14 @@ export function coverPage(gate: ReactNode): ReactNode {
/>
</span>
</p>
{gate && <div className="mx-auto mt-20 w-full max-w-md">{gate}</div>}
</div>
{gate && <div className="deck-cover-gate">{gate}</div>}
{!gate && (
<div className="deck-scroll-hint" aria-hidden="true">
Scroll down
<span className="deck-scroll-hint-arrow">&darr;</span>
</div>
)}
</DeckPage>
);
}
Expand All @@ -56,7 +64,8 @@ const page3 = (
<DeckPage key={2} n={2} total={TOTAL}>
<Split
title="Local AI (finally) runs on consumer hardware"
sub="Open models are just months behind the best."
sub="Open models are trailing just months behind the best."
band="Open models already rival the frontier. Soon they'll be indistinguishable."
media={
<MediaGallery
slides={[
Expand All @@ -70,12 +79,13 @@ const page3 = (
src: '/opportunity/snapdragon-x-elite.jpg',
alt: 'A Snapdragon X Elite chip mounted in a clear acrylic block, lying on grass',
caption:
'Snapdragon X Elite: NPU-equipped consumer silicon like this now ships in most new PCs.',
'Snapdragon X Elite: newly released consumer silicon capable of running local models',
},
{
src: null,
alt: 'FPO: photo 3',
caption: 'FPO caption 3',
src: '/research/family-together.png',
alt: 'Illustration of a family gathered together around the Family Book device',
caption:
'We partnered with Mozilla Foundation to publish our research in February 2026. It was received with overwhelmingly positive reception on social media - at a time when AI devices were a focus of vitriol online.',
},
]}
/>
Expand All @@ -84,10 +94,9 @@ const page3 = (
Open weights keep closing on closed models.
<Ref k="epoch-open-weights" /> Thinking Machines Lab released Inkling,
975B parameters, Apache 2.0.
<Ref k="inkling" />{' '}
<strong>Every upstream advance lands in our stack for free.</strong>{' '}
NPU-equipped AI PCs are roughly 59% of 2026 shipments.
<Ref k="ai-pc-shipments" />
<Ref k="inkling" /> Z.ai&rsquo;s founder predicts a Fable quality open
model before Q1 2027.
<Ref k="zai-fable-prediction" />
</Split>
</DeckPage>
);
Expand All @@ -109,13 +118,12 @@ const page2 = (
</div>
}
>
Compute has made this trip before. The mainframe sat in a room you had to
book.{' '}
Compute has made this trip before.{' '}
<strong>
8% of US households owned a computer in 1984, and 89% did by 2016.
</strong>
<Ref k="census-computer-ownership" /> The datacenter is making the same
move.
<Ref k="census-computer-ownership" /> As the home gets smarter and
smarter, the datacenter will make the same move.
</Split>
</DeckPage>
);
Expand All @@ -126,14 +134,16 @@ const page4 = (
title="7 in 10 Americans don't trust big tech's AI"
sub="But today, there's no alternative."
>
Pew measured America&rsquo;s growing AI disdain in June 2026.
Pew measured America&rsquo;s growing disdain for big tech&rsquo;s AI in
June 2026.
<Ref k="pew-distrust" /> Amazon went the other way, removing the
Echo&rsquo;s only local-processing option in March 2025.
<Ref k="echo-local-removed" />
<br />
<br />
Today, the data center backlash has arrived at a fever pitch that
won&rsquo;t subside, akin to climate dread. America wants an alternative.
Today, the datacenter backlash has arrived at a tenor that won&rsquo;t
subside, solidifying as a cultural phenomenon like climate dread. America
wants an alternative.
</Statement>
<Band narrow>
Demand for private AI is enormous.
Expand Down Expand Up @@ -206,8 +216,8 @@ const page5 = (
<DeckPage key={6} n={6} total={TOTAL}>
<Split
flip
title="Local architecture changes the story"
sub="Our privacy-preserving architecture is what wins customers over."
title="Local architecture wins consumer sentiment"
sub="Privacy-preserving architecture wins customers over."
media={
<div>
<div className="grid grid-cols-2 gap-4">
Expand Down
95 changes: 51 additions & 44 deletions www/app/opportunity/content/act2.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import type { ReactNode } from 'react';
import DeckPage from '../components/DeckPage';
import DeckVideo from '../components/DeckVideo';
import FpoBox from '../components/FpoBox';
import Ref from '../components/Ref';
import {
BigStat,
Expand All @@ -21,11 +20,19 @@ const page7 = (
title="Our first device is for families"
sub="High emotional value, sensitive data, and a GPU in the living room."
>
Families hold the memories worth keeping, and what a household records
carries none of the risk a clinic or a payroll system does.{' '}
<strong>One device, one market.</strong>
Rich in stories, goals, birth certificates, wills, recipes and genetic
histories - families hold rich and precious records worth holding
carefully.
<br />
<br />
Today, LLMs can greatly reduce the massive friction to maintaining an
accurate family archive.{' '}
<strong>
But only if families feel safe sharing their most intimate data with the
system.
</strong>
</Statement>
<div className="mt-10 flex gap-4 items-start">
<div className="mt-10 deck-media-row">
<span className="deck-video-portrait deck-video-frame">
<DeckVideo
src="/opportunity/device-playtest.mp4"
Expand Down Expand Up @@ -55,17 +62,21 @@ const page8 = (
title="Your own family vault"
sub="Weekly check-ins, budgets, school, health, and stories."
media={
<FpoBox
note={
"Week-strip vignette: Sunday check-in → school log → vacation fund → grandmother's story at dinner"
}
// eslint-disable-next-line @next/next/no-img-element
<img
src="/opportunity/family-vault.webp"
alt="The Family Book device surrounded by illustrated vault contents: a storybook, cookbook, document box, photo pile, calendar, piggy bank, and health folder"
className="deck-slide-media"
/>
}
>
<strong>The device earns its place by being useful every week.</strong>{' '}
Then at dinner a grandmother tells the story again, and the house keeps
her voice and her accent. The archive is one beloved feature, never the
whole premise.
<strong>
The device solves age old family archive problems overnight.
</strong>{' '}
The trustworthy filing cabinet for documents often lost, the scribe (and
fact checker) for Grandpa&rsquo;s bewildering tall stories (and history of
glaucoma), the family cookbook, the home media server for photos and
recordings often scattered across devices.
</Split>
</DeckPage>
);
Expand All @@ -91,7 +102,11 @@ const contextPage = (
/>
}
>
The house knows who the plumber is and what the family is saving for.{' '}
The house knows who the plumber is and what the family is saving for. As
homes become smarter, single purpose devices will need access to global
inference that understands the home, and who lives there.
<br />
<br />
<strong>
One local agent holds that memory: an MCP server on the LAN, a
chat-completions endpoint, and local RAG.
Expand Down Expand Up @@ -160,36 +175,28 @@ const page12 = (

const page10 = (
<DeckPage key={12} n={12} total={TOTAL} actClass={ACT_CLASS}>
<div className="grid md:grid-cols-2 gap-10 items-center">
{/* eslint-disable-next-line @next/next/no-img-element */}
<img
src="/opportunity/hub-photo.jpg"
alt="A Google Nest Hub on a side table showing the weather, beside a potted plant"
className="deck-screenshot"
/>
<BigStat
stat="600M+"
title="Home hubs are a proven category."
sub="600M+ Alexa devices sold, all of them cloud-dependent. Ours runs locally."
band="In 2026, you should be able to dim your lights without notifying Jeff Bezos. Home inference (finally) makes that possible."
>
The AI gadget graveyard is littered with attempts to find new ways to
interact.{' '}
<strong>
Instead, we&rsquo;re entering a proven category with a novel new
architecture - already proven compelling to consumers.
</strong>
<br />
<br />
Alexa have sold 600M+ units.
<Ref k="alexa-600m" /> OpenAI paid $6.5B for Jony Ive&rsquo;s startup,
<Ref k="openai-io" /> Amazon bought Bee,
<Ref k="bee-amazon" /> Meta bought Limitless.
<Ref k="limitless-meta" /> 800M+ Google Home units shipped.
<Ref k="google-home-800m" /> All of them a soft-surveillance device
running in someone else&rsquo;s cloud.
</BigStat>
</div>
<BigStat
stat="600M+"
title="Home hubs are a proven category."
sub="600M+ Alexa devices sold, all of them cloud-dependent. Ours runs locally."
band="In 2026, you should be able to dim your lights without notifying Jeff Bezos. Home inference (finally) makes that possible."
>
The AI gadget graveyard is littered with attempts to find new ways to
interact.{' '}
<strong>
Instead, we&rsquo;re entering a proven category with a novel new
architecture - already proven compelling to consumers.
</strong>
<br />
<br />
Alexa have sold 600M+ units.
<Ref k="alexa-600m" /> OpenAI paid $6.5B for Jony Ive&rsquo;s startup,
<Ref k="openai-io" /> Amazon bought Bee,
<Ref k="bee-amazon" /> Meta bought Limitless.
<Ref k="limitless-meta" /> 800M+ Google Home units shipped.
<Ref k="google-home-800m" /> All of them a soft-surveillance device
running in someone else&rsquo;s cloud.
</BigStat>
</DeckPage>
);

Expand Down
Loading
Loading