Design Spec — /story Page
Design Spec: /story Page
Date: 2026-03-12
Status: Approved
URL: dorkos.ai/story
Dual purpose: Live presentation at No Edges event (March 12, 2026) + permanent DorkOS origin story page
Overview
A scroll-driven narrative page that tells the origin story of DorkOS through Dorian's personal arc -- from wanting a to-do list to running coordinated autonomous agents across four companies in two months of evenings. The page serves a live presentation today and lives permanently on dorkos.ai as the "why behind the what."
The vibe throughout: less obligation, more life. The technology is in service of presence -- time back with family, focus on what matters, the machine handling what it doesn't need a human for.
Decisions Summary
| Decision | Choice |
|---|---|
| URL | /story |
| Scroll mode | Dual: continuous scroll normally, ?present=true for presentation snap |
| Section 1 visualization | Dashboard cards that populate (8-card grid) |
| Section 3 visualization | Equation reveal (X = Y, one at a time) |
| Architecture | Extend layers/features/marketing/ with new story sections |
| Opening line | "What if the most powerful thing you could do with AI was get Thursday afternoon back?" |
| Section 3 headline | "Platforms will just be prompts." |
| Section 3 closer | "Code isn't the scarce thing anymore. Knowing what to ask -- and what to remember -- is." |
| Section 4 top line | "Anyone has access to the same AI. Not everyone has thought hard about what they actually want." |
| Section 4 close | "I built this so the machine could handle the obligations. So I could focus on the parts that are irreplaceable." |
File Structure
apps/site/src/
├── app/(marketing)/story/
│ ├── layout.tsx # metadata, OG tags
│ └── page.tsx # server component, composes sections
└── layers/features/marketing/
├── ui/
│ ├── story/
│ │ ├── StoryHero.tsx # Slide 0: opening title card
│ │ ├── MondayMorningSection.tsx # Slide 1: boot dashboard
│ │ ├── HowItBuiltSection.tsx # Slide 2: 4-step timeline
│ │ ├── JustPromptsSection.tsx # Slide 3: equation reveal
│ │ ├── CloseSection.tsx # Slide 4: minimal close
│ │ └── FutureVisionSection.tsx # Slide 5: page-only, hidden in present mode
│ └── PresentationShell.tsx # Wraps the page; handles ?present=true
└── lib/
├── use-presentation-mode.ts # Hook: reads useSearchParams for ?present=true
└── story-data.ts # Boot cards, timeline steps, equation itemsNew exports added to layers/features/marketing/index.ts barrel.
Page Component
app/(marketing)/story/page.tsx is a server component (no 'use client'). Metadata lives in layout.tsx. Client interactivity is encapsulated in section components and PresentationShell.
import { Suspense } from 'react'
import {
PresentationShell,
StoryHero,
MondayMorningSection,
HowItBuiltSection,
JustPromptsSection,
CloseSection,
FutureVisionSection,
MarketingFooter,
} from '@/layers/features/marketing'
export default function StoryPage() {
return (
<Suspense fallback={null}>
<PresentationShell>
<StoryHero />
<MondayMorningSection id="morning" />
<HowItBuiltSection id="timeline" />
<JustPromptsSection id="prompts" />
<CloseSection id="close" />
<FutureVisionSection id="vision" />
<MarketingFooter ... />
</PresentationShell>
</Suspense>
)
}<Suspense> is required by Next.js because PresentationShell calls useSearchParams() internally.
Metadata
app/(marketing)/story/layout.tsx:
export const metadata: Metadata = {
title: 'The Story | DorkOS',
description:
'How one person built an AI operating system for their whole life -- in two months of evenings.',
openGraph: {
title: 'The Story | DorkOS',
description:
'How one person built an AI operating system for their whole life -- in two months of evenings.',
url: '/story',
type: 'website',
},
};Presentation Mode
Activation
?present=true query parameter. URL: dorkos.ai/story?present=true
use-presentation-mode.ts
'use client';
import { useSearchParams } from 'next/navigation';
export function usePresentationMode() {
const params = useSearchParams();
return params.get('present') === 'true';
}PresentationShell.tsx
Client component. When presentation mode is active:
- Adds
data-presentattribute to wrapper div - CSS via
globals.cssor inline:scroll-snap-type: y mandatory; overflow-y: scroll; height: 100vh - Each child section receives
scroll-snap-align: start; min-height: 100vh(applied via thedata-presentselector on the parent) MarketingHeaderhidden viadata-present [data-marketing-header] { display: none }- Keyboard listener (
useEffect):ArrowRight/Spacescrolls to next section by index;ArrowLeftscrolls to previous - Progress dots: fixed bottom-right, one dot per section (excluding
FutureVisionSection), current section highlighted in orange - Font sizes boosted ~1.3x on slide headings via
data-present h2, data-present h3 { font-size: calc(var(--heading-size) * 1.3) }
Keyboard navigation implementation
const sectionIds = ['morning', 'timeline', 'prompts', 'close'];
// FutureVisionSection excluded from keyboard nav (page-only)
useEffect(() => {
if (!isPresent) return;
const handler = (e: KeyboardEvent) => {
if (e.key === 'ArrowRight' || e.key === ' ') {
e.preventDefault();
scrollToSection(currentIndex + 1);
}
if (e.key === 'ArrowLeft') {
e.preventDefault();
scrollToSection(currentIndex - 1);
}
};
window.addEventListener('keydown', handler);
return () => window.removeEventListener('keydown', handler);
}, [isPresent, currentIndex]);Current section tracked via IntersectionObserver on each section element.
Section Designs
Slide 0: StoryHero
- Background:
bg-charcoal(#1A1814) - Eyebrow:
ORIGIN STORY(monospace, orange, uppercase, tracked) - Headline: "What if the most powerful thing you could do with AI was get Thursday afternoon back?"
- Large, light weight (
font-weight: 300), max-width 580px, centered
- Large, light weight (
- Divider: 32px orange horizontal rule
- Attribution:
DORIAN COLLIER -- 144 STUDIO -- AUSTIN TX(monospace, small, warm-gray) - Animation: REVEAL + STAGGER (standard marketing pattern)
Slide 1: MondayMorningSection
- Background:
#0f0e0c(near-black, slightly warmer than charcoal) - Eyebrow:
A MONDAY MORNING - Headline: "Before you touched anything."
- Subline: "While you slept, the system ran." (warm-gray)
- Boot cards grid: 4-column grid, 8 cards, staggered entrance animation
| Card | Category color | Title | Detail |
|---|---|---|---|
| Health | orange border | Synced | HRV · sleep · steps |
| Companies | blue | 4 loaded | tasks · projects |
| ⚑ Overdue | orange border + orange text | 15 days | flagged for you |
| Calendar | purple | 3 preps | meetings identified |
| Family | blue border | Liam · Thu | therapy · brief outdated |
| Energy | green | 4 dims | phys · mental · emo · spirit |
| Coaching | orange | Fear check | priorities → 3 |
| Output | warm-gray | Ready | calendar · habits · audio |
Cards animate in sequentially (staggered, ~80ms apart) using STAGGER + REVEAL.
- Landing line (below border): "This isn't ChatGPT. This is a personal operating system." — italic, centered, cream-white
Slide 2: HowItBuiltSection
- Background:
bg-cream-primary(#F5F0E6) - Eyebrow:
TWO MONTHS OF EVENINGS - Headline: "Each step hit a ceiling. Each ceiling became the next build."
- Timeline: Vertical, 4 numbered steps
Each step:
[number circle] PRODUCT -- DURATION (monospace, small, colored)
Description sentence (IBM Plex Sans, medium weight)
Ceiling hit: limitation that drove the next step (monospace, warm-gray-light)Steps:
| # | Color | Label | Description | Ceiling |
|---|---|---|---|---|
| 1 | orange | LifeOS -- A weekend | Calendar, todos, journaling, coaching. Built for my life -- not for any company. | Needed to manage multiple AI projects at once. |
| 2 | charcoal | DorkOS -- A few weeks | A command layer across all my agents. One place to run everything. | Still had to be awake for any of it to run. |
| 3 | charcoal | Pulse -- A few weeks | Scheduled tasks. The system fires overnight. Texts briefings before I wake up. | Agents couldn't talk to each other. |
| 4 | charcoal | Mesh -- A few weeks | Four companies, each with its own agent. They find each other and coordinate. | (closing statement, no ceiling) |
- Footer: "Total calendar time from 'I want a to-do list' to 'my agents coordinate while I sleep' -- two months of evenings." — italic, warm-gray, border-top
Slide 3: JustPromptsSection
- Background:
bg-charcoal film-grain - Eyebrow:
HERE'S THE THING - Headline: "Platforms will just be prompts." (bold, large)
- Subline: "All open source. Here's what it actually is." (warm-gray)
- Equation items (animate in one at a time, ~200ms each):
50+ skills = text files
~100 coaching Qs = one markdown doc
board of advisors = configuration
automated hooks = small scriptsLeft column: cream-white, monospace, right-aligned. = in orange. Right column: warm-gray, monospace, left-aligned.
- Divider (border-top, dark)
- Closing moment:
- "Platforms will just be prompts." (medium weight, cream-white)
- "Code isn't the scarce thing anymore. Knowing what to ask -- and what to remember -- is." (warm-gray, line-height 1.6)
Slide 4: CloseSection
- Background:
bg-charcoal - Maximum whitespace. Centered, no grid.
- Top line: "Anyone has access to the same AI. Not everyone has thought hard about what they actually want." (warm-gray, slightly smaller)
- Orange divider
- Close: "I built this so the machine could handle the obligations. So I could focus on the parts that are irreplaceable." (cream-white, light weight, large)
- Footer:
FUNDAMENTALS FIRST -- 2026(monospace, very small, warm-gray)
Slide 5: FutureVisionSection (page-only)
- Background:
bg-cream-secondary - Hidden in
?present=trueviadata-present [data-future-vision] { display: none } - Eyebrow:
WHERE THIS IS GOING - Headline: "The next layer is already building."
- Three cards: Autonomous (orange), Connected (blue), Commerce (green)
- Each: category label, short title, 1-2 sentence description referencing the DorkOS subsystem
Data File: story-data.ts
export const bootCards = [
{
id: 'health',
label: 'Health',
value: 'Synced',
detail: 'HRV · sleep · steps',
color: 'orange',
priority: 'high',
},
{
id: 'companies',
label: 'Companies',
value: '4 loaded',
detail: 'tasks · projects',
color: 'blue',
priority: 'normal',
},
{
id: 'overdue',
label: '⚑ Overdue',
value: '15 days',
detail: 'flagged for you',
color: 'orange',
priority: 'urgent',
},
{
id: 'calendar',
label: 'Calendar',
value: '3 preps',
detail: 'meetings identified',
color: 'purple',
priority: 'normal',
},
{
id: 'family',
label: 'Family',
value: 'Liam · Thu',
detail: 'therapy · brief outdated',
color: 'blue',
priority: 'high',
},
{
id: 'energy',
label: 'Energy',
value: '4 dims',
detail: 'phys · mental · emo · spirit',
color: 'green',
priority: 'normal',
},
{
id: 'coaching',
label: 'Coaching',
value: 'Fear check',
detail: 'priorities → 3',
color: 'orange',
priority: 'normal',
},
{
id: 'output',
label: 'Output',
value: 'Ready',
detail: 'calendar · habits · audio',
color: 'gray',
priority: 'normal',
},
];
export const evolutionSteps = [
{
step: 1,
product: 'LifeOS',
duration: 'A weekend',
description: 'Calendar, todos, journaling, coaching. Built for my life -- not for any company.',
ceiling: 'Needed to manage multiple AI projects at once.',
color: 'orange',
},
{
step: 2,
product: 'DorkOS',
duration: 'A few weeks',
description: 'A command layer across all my agents. One place to run everything.',
ceiling: 'Still had to be awake for any of it to run.',
color: 'charcoal',
},
{
step: 3,
product: 'Pulse',
duration: 'A few weeks',
description: 'Scheduled tasks. The system fires overnight. Texts briefings before I wake up.',
ceiling: "Agents couldn't talk to each other.",
color: 'charcoal',
},
{
step: 4,
product: 'Mesh',
duration: 'A few weeks',
description: 'Four companies, each with its own agent. They find each other and coordinate.',
ceiling: null,
color: 'charcoal',
},
];
export const equationItems = [
{ lhs: '50+ skills', rhs: 'text files' },
{ lhs: '~100 coaching Qs', rhs: 'one markdown doc' },
{ lhs: 'board of advisors', rhs: 'configuration' },
{ lhs: 'automated hooks', rhs: 'small scripts' },
];
export const futureCards = [
{
id: 'autonomous',
label: 'Autonomous',
title: 'Agents that run',
description: 'Pulse. Already shipping. Your agents work while you sleep.',
color: 'orange',
},
{
id: 'connected',
label: 'Connected',
title: 'Agents that talk',
description: 'Mesh. Agent-to-agent discovery and coordination across teams.',
color: 'blue',
},
{
id: 'commerce',
label: 'Commerce',
title: 'Agents that transact',
description: 'HTTP 402. Agents negotiate, purchase, settle. The economy reshapes.',
color: 'green',
},
];Animation Patterns
All animations use existing motion-variants.ts exports:
REVEAL-- fade + slide-up (opacity: 0, y: 20→opacity: 1, y: 0)STAGGER-- staggers children at 80msSPRING-- overdamped spring (stiffness: 100, damping: 20)VIEWPORT--{ once: true, amount: 0.2 }
Boot cards use a custom stagger delay per card index:
<motion.div
variants={REVEAL}
transition={{ delay: index * 0.08 }}
>Equation items animate in sequentially using the same pattern.
Design Conventions
All sections follow existing site conventions:
'use client'at top of every section componentimport { motion } from 'motion/react'import { REVEAL, STAGGER, SPRING, VIEWPORT } from '../lib/motion-variants'- Use design token class names, never hardcoded colors (
bg-charcoal, not#1A1814) - IBM Plex Sans for body/headings, IBM Plex Mono for labels/code/data
- No em dashes -- double hyphens (
--) instead - File size target: < 300 lines per component; split if exceeded
Navigation
The MarketingNav is hidden in presentation mode. In normal reading mode, it is omitted from this page (the story has its own internal flow; a floating nav would compete with the scroll narrative). The MarketingHeader is present in normal mode, hidden in presentation mode.
A simple "back to home" link lives in the MarketingFooter.
Responsive
- Desktop (primary): Full grid layouts, large typography
- Tablet: 2-column boot card grid (from 4-column), timeline unchanged
- Mobile: Single-column boot cards, reduced heading sizes
- Presentation mode: Desktop-only. On mobile/tablet,
?present=truedegrades gracefully to normal scroll (no snap, no keyboard nav, no dots)
Success Criteria
Presentation (today)
- Page loads at
dorkos.ai/story?present=true - Each section fills the viewport
- Arrow keys / spacebar advance and retreat between sections
- Progress dots visible in bottom-right
- All text readable from 15+ feet on a large TV
- MarketingHeader hidden in present mode
Permanent page
- Feels native to dorkos.ai -- same typography, motion, color tokens
- Tells the DorkOS origin story as a personal narrative
- Works as a standalone page for first-time visitors
- FutureVisionSection visible in normal reading mode, hidden in present mode
- Page is linked from MarketingFooter or MarketingNav