first-commit
ci / Validate workspace (push) Has been cancelled
landing-page-ci / Validate landing page (push) Has been cancelled
landing-page-deploy / Deploy landing page (push) Has been cancelled
github-metrics / Generate repository metrics SVG (push) Has been cancelled
refresh-contributors-wall / Refresh contributors wall cache bust (push) Waiting to run

This commit is contained in:
Zakaria
2026-05-04 14:58:14 -04:00
commit a46764fb1b
1210 changed files with 233231 additions and 0 deletions
+120
View File
@@ -0,0 +1,120 @@
---
name: simple-deck
description: |
Single-file horizontal-swipe HTML deck. Built by copying the seed
`assets/template.html` (which carries the proven 5-rule iframe nav script)
and pasting slide layouts from `references/layouts.md`. Pitch decks,
product overviews, study material — when you don't need the magazine
aesthetic of `magazine-web-ppt`.
triggers:
- "deck"
- "slides"
- "ppt"
- "presentation"
- "幻灯"
- "ppt 模板"
od:
mode: deck
scenario: product
preview:
type: html
entry: index.html
design_system:
requires: true
sections: [color, typography, layout, components]
---
# Simple Deck Skill
Produce a single-file horizontal-swipe HTML deck using the seed and layout library.
## Resource map
```
simple-deck/
├── SKILL.md ← you're reading this
├── assets/
│ └── template.html ← seed: tokens + slide primitives + proven nav script (READ FIRST)
└── references/
├── layouts.md ← 8 paste-ready slide layouts + theme-rhythm rules
└── checklist.md ← P0/P1/P2 self-review (rhythm spot-check at bottom)
```
## Workflow
### Step 0 — Pre-flight
1. **Read `assets/template.html`** end-to-end through the `<style>` block AND the `<script>` block. The script solves five iframe-specific bugs (real scroller detection, dual capture-phase listeners, auto-focus, no `scrollIntoView`, position persistence) — do not rewrite it.
2. **Read `references/layouts.md`** so you know the 8 layouts. Pay special attention to the "Theme rhythm" section — it's the rule that prevents the deck from feeling sleepy.
3. **Read the active DESIGN.md** — map its tokens to the six `:root` variables in the seed.
### Step 1 — Copy the seed
Copy `assets/template.html` to the project root as `index.html`. Replace the six `:root` variables with the active design system's tokens. Replace the page `<title>`.
### Step 2 — Decide slide count + theme rhythm BEFORE writing any slide
Default: 6 slides unless the brief says otherwise.
| Audience / format | Slides |
|---|---|
| Product overview / lightning talk (510 min) | 6 |
| Pitch deck (15 min) | 810 |
| Investor update / longer talk (2030 min) | 1218 |
Then write out the rhythm before any HTML — for example, 8 slides:
```
01 hero light center Cover
02 light Problem
03 hero dark center Big stat
04 light Three points
05 dark Pipeline
06 hero light center Quote
07 light Before / after
08 hero dark center Ask
```
A healthy sequence has:
- No 3+ same theme in a row
- ≥ 1 `hero dark` AND ≥ 1 `hero light` (for 8+ slides)
- Alternating breath every 34 slides
Show this rhythm sketch to the user *before* writing slide HTML — they can redirect cheaply.
### Step 3 — Paste and fill
For each planned slide, copy the matching `<section>` from `layouts.md` into the body. Replace bracketed text with real, specific copy. **No filler / no lorem.** If a slide feels empty, the layout is wrong — pick a different one.
Tag each slide with `data-screen-label="01 Cover"`, `"02 Problem"`, etc., in the order you wrote them. (The seed's first three slides already do this — extend the pattern.)
### Step 4 — Self-check
Run through `references/checklist.md`. The "Theme rhythm spot-check" at the end is non-negotiable:
```bash
grep 'class="slide' index.html
```
Read the resulting class list. If you see `light × 4 in a row`, swap one to `dark`. If no `hero dark` exists in an 8+ slide deck, promote one big-stat or closing slide.
### Step 5 — Emit the artifact
```
<artifact identifier="deck-slug" type="text/html" title="Deck Title">
<!doctype html>
<html>...</html>
</artifact>
```
One sentence before the artifact. Stop after `</artifact>`.
## Hard rules
- **Theme class on every slide** (`light` | `dark` | `hero light` | `hero dark`). Bare `class="slide"` = regression.
- **No 3+ same theme in a row.**
- **Display = serif via `var(--font-display)`.** `.h-hero` / `.h-xl` / `.h-md` already enforce.
- **One accent per slide, used at most twice.**
- **Don't rewrite the nav script.** It's proven.
- **No `scrollIntoView()`.** Breaks iframe.
- **`data-screen-label` on every slide.**
+353
View File
@@ -0,0 +1,353 @@
<!doctype html>
<!--
OD simple-deck seed.
Single-file horizontal-swipe HTML deck. Each `<section class="slide">`
is one slide; the body uses CSS scroll-snap to lock to slide boundaries.
Keyboard nav (← / → / Space / Home / End) is wired in via the script at
the bottom — DO NOT replace it, it solves five iframe-specific problems
that the naive version silently breaks (see comments in the script).
Theme tokens at the top mirror the web-prototype seed; bind them to the
active DESIGN.md and stop. A deck has TWO surfaces — `.slide.light` and
`.slide.dark` — that swap fg / bg without touching the accent. Use both;
alternating creates the rhythm that prevents visual fatigue.
-->
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>[REPLACE] Deck title · subtitle</title>
<style>
:root {
--bg: #fafaf7;
--surface: #ffffff;
--fg: #1a1916;
--muted: #6b6964;
--border: #e8e5df;
--accent: #c96442;
--accent-soft: color-mix(in oklch, var(--accent) 14%, transparent);
--font-display: 'Iowan Old Style', 'Charter', Georgia, serif;
--font-body: -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif;
--font-mono: ui-monospace, 'JetBrains Mono', 'SF Mono', Menlo, monospace;
}
*, *::before, *::after { box-sizing: border-box; }
html, body { margin: 0; height: 100%; }
body {
background: var(--bg);
color: var(--fg);
font-family: var(--font-body);
font-size: 18px;
line-height: 1.5;
-webkit-font-smoothing: antialiased;
display: flex;
overflow-x: auto;
overflow-y: hidden;
scroll-snap-type: x mandatory;
scroll-behavior: smooth;
}
body::-webkit-scrollbar { display: none; }
p { text-wrap: pretty; }
h1, h2, h3 { text-wrap: balance; }
/* ─── slide surface ─────────────────────────────────────────────── */
.slide {
flex: 0 0 100vw;
width: 100vw;
height: 100vh;
scroll-snap-align: start;
padding: clamp(48px, 7vw, 96px) clamp(48px, 8vw, 112px);
display: flex;
flex-direction: column;
justify-content: center;
position: relative;
overflow: hidden;
}
.slide.light { background: var(--bg); color: var(--fg); }
.slide.dark { background: var(--fg); color: var(--bg); }
.slide.dark .muted { color: rgba(250,250,247,0.7); }
.slide.dark .border { border-color: rgba(250,250,247,0.18); }
.slide.hero { padding-block: clamp(64px, 9vw, 128px); }
/* center alignment variant — for cover, big-stat, big-quote */
.slide.center { align-items: center; text-align: center; justify-content: center; }
.slide.center .body { margin-inline: auto; }
/* ─── eyebrow / kicker ──────────────────────────────────────────── */
.eyebrow {
font-family: var(--font-mono);
font-size: 12px;
letter-spacing: 0.1em;
text-transform: uppercase;
color: var(--accent);
margin: 0 0 28px;
}
/* ─── type ──────────────────────────────────────────────────────── */
.h-hero {
font-family: var(--font-display);
font-size: clamp(56px, 8vw, 112px);
line-height: 1.02;
letter-spacing: -0.025em;
margin: 0 0 20px;
max-width: 16ch;
}
.h-xl {
font-family: var(--font-display);
font-size: clamp(40px, 5vw, 64px);
line-height: 1.1;
letter-spacing: -0.02em;
margin: 0 0 20px;
max-width: 20ch;
}
.h-md {
font-family: var(--font-display);
font-size: clamp(28px, 3vw, 36px);
line-height: 1.2;
letter-spacing: -0.015em;
margin: 0 0 16px;
}
.lead {
font-size: clamp(20px, 1.6vw, 24px);
line-height: 1.55;
color: var(--muted);
max-width: 56ch;
margin: 0;
}
.slide.dark .lead { color: rgba(250,250,247,0.78); }
.meta { font-family: var(--font-mono); font-size: 13px; color: var(--muted); }
/* ─── big-stat layout ───────────────────────────────────────────── */
.stat-num {
font-family: var(--font-display);
font-size: clamp(140px, 22vw, 280px);
line-height: 0.9;
letter-spacing: -0.04em;
color: var(--accent);
font-weight: 600;
margin: 0 0 16px;
}
.stat-num .unit { font-size: 0.4em; opacity: 0.85; margin-left: 4px; }
.stat-caption {
font-size: clamp(20px, 1.7vw, 26px);
color: var(--muted);
max-width: 26ch;
margin: 0;
}
/* ─── big-quote layout ──────────────────────────────────────────── */
.quote-mark {
font-family: var(--font-display);
font-size: clamp(140px, 16vw, 200px);
line-height: 0.6;
color: var(--accent);
opacity: 0.18;
margin: 0 0 -32px;
}
.quote-text {
font-family: var(--font-display);
font-size: clamp(28px, 3vw, 44px);
line-height: 1.3;
letter-spacing: -0.01em;
max-width: 28ch;
margin: 0 0 28px;
}
.quote-author { font-size: 14px; color: var(--muted); }
/* ─── 3-column point layout ─────────────────────────────────────── */
.pt-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 32px;
margin-top: 40px;
}
.pt {
border-top: 2px solid var(--accent);
padding-top: 16px;
}
.pt h3 { font-size: 19px; font-weight: 500; margin: 0 0 8px; letter-spacing: -0.005em; }
.pt p { color: var(--muted); margin: 0; font-size: 16px; line-height: 1.5; }
.slide.dark .pt p { color: rgba(250,250,247,0.7); }
/* ─── pipeline (numbered steps) ─────────────────────────────────── */
.pipeline {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 24px;
margin-top: 40px;
}
.step .nb { font-family: var(--font-mono); font-size: 13px; color: var(--accent); letter-spacing: 0.06em; }
.step h3 { font-family: var(--font-display); font-size: 22px; font-weight: 500; margin: 6px 0 6px; line-height: 1.2; }
.step p { color: var(--muted); margin: 0; font-size: 14px; line-height: 1.5; }
/* ─── before/after ──────────────────────────────────────────────── */
.ba-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 64px; margin-top: 32px; }
.ba-col .ba-label { font-family: var(--font-mono); font-size: 12px; letter-spacing: 0.08em; text-transform: uppercase; color: var(--muted); margin: 0 0 12px; }
.ba-col h3 { font-family: var(--font-display); font-size: 32px; line-height: 1.15; margin: 0 0 16px; max-width: 16ch; }
.ba-col p { color: var(--muted); font-size: 17px; line-height: 1.5; margin: 0; }
/* ─── image placeholder ─────────────────────────────────────────── */
.ph-img {
background:
linear-gradient(135deg, var(--accent-soft), color-mix(in oklch, var(--fg) 6%, transparent)),
var(--surface);
border: 1px solid var(--border);
border-radius: 12px;
aspect-ratio: 16 / 10;
display: grid; place-items: center;
color: var(--muted);
font-family: var(--font-mono);
font-size: 12px;
letter-spacing: 0.04em;
}
.slide.dark .ph-img { border-color: rgba(250,250,247,0.18); color: rgba(250,250,247,0.5); }
.ph-img.wide { aspect-ratio: 16 / 9; }
.ph-img.tall { aspect-ratio: 3 / 4; }
/* ─── chrome (counter, hint, progress) — fixed, never scrolls ──── */
.deck-counter {
position: fixed;
bottom: 24px; right: 32px;
font-family: var(--font-mono);
font-size: 12px;
color: var(--fg);
background: var(--surface);
padding: 6px 12px;
border-radius: 999px;
border: 1px solid var(--border);
letter-spacing: 0.04em;
z-index: 10;
}
.deck-hint {
position: fixed;
bottom: 24px; left: 32px;
font-family: var(--font-mono);
font-size: 11px;
color: var(--muted);
letter-spacing: 0.04em;
z-index: 10;
}
.deck-progress {
position: fixed;
top: 0; left: 0;
height: 3px;
background: var(--accent);
width: 0;
z-index: 10;
transition: width 0.18s ease;
}
</style>
</head>
<body>
<!--
┌─────────────────────────────────────────────────────────────────┐
│ PASTE SLIDES FROM references/layouts.md HERE. │
│ ► Each `<section class="slide">` must include a class from: │
│ light | dark | hero light | hero dark │
│ ► No 3+ same-theme in a row. │
│ ► For 8+ slides: ≥ 1 `hero dark` AND ≥ 1 `hero light`. │
│ ► End with a clear CTA / takeaway slide. │
└─────────────────────────────────────────────────────────────────┘
-->
<section class="slide hero light center" data-screen-label="01 Cover">
<div class="eyebrow">[REPLACE] Eyebrow · context · date</div>
<h1 class="h-hero">[REPLACE] One sharp sentence.</h1>
<p class="lead">[REPLACE] One subhead — concrete, not corporate.</p>
</section>
<section class="slide light" data-screen-label="02 Body">
<p class="eyebrow">[REPLACE] Section eyebrow</p>
<h2 class="h-xl">[REPLACE] Body slide headline.</h2>
<p class="lead">[REPLACE] Two sentences explaining the idea — no more.</p>
</section>
<section class="slide hero dark center" data-screen-label="03 Closing">
<div class="eyebrow">[REPLACE] Eyebrow</div>
<h2 class="h-hero">[REPLACE] The take-away.</h2>
</section>
<!-- chrome -->
<div class="deck-progress" id="deck-progress" aria-hidden></div>
<div class="deck-counter" id="deck-counter">1 / 3</div>
<div class="deck-hint">← / → · scroll · swipe</div>
<script>
/*
Five hard rules for deck nav inside an iframe (the OD preview is one).
The naive `document.body.scrollLeft` pattern silently fails: clicks
register, but keyboard does nothing, and the counter freezes at "1 / N"
while the user is on slide 6.
1. Detect the real scroller — body OR documentElement, depending.
2. Listen for scroll on BOTH window and document, capture phase.
3. Listen for keydown on BOTH window and document, capture phase.
4. Auto-focus body so arrow keys work without an upfront click.
5. Never use Element.scrollIntoView — it can yank the host page.
*/
(function () {
var slides = document.querySelectorAll('.slide');
var counter = document.getElementById('deck-counter');
var progress = document.getElementById('deck-progress');
var KEY = 'od-deck-pos';
var active = 0;
function scroller() {
if (document.body.scrollWidth > document.body.clientWidth + 1) return document.body;
return document.scrollingElement || document.documentElement;
}
function setActive(i) {
active = i;
if (counter) counter.textContent = (i + 1) + ' / ' + slides.length;
if (progress) progress.style.width = (((i + 1) / slides.length) * 100) + '%';
try { localStorage.setItem(KEY, String(i)); } catch (_) {}
}
function go(i) {
var next = Math.max(0, Math.min(slides.length - 1, i));
setActive(next);
scroller().scrollTo({ left: next * window.innerWidth, behavior: 'smooth' });
}
function syncFromScroll() {
var i = Math.round(scroller().scrollLeft / window.innerWidth);
if (i !== active && i >= 0 && i < slides.length) setActive(i);
}
function onKey(e) {
var t = e.target;
if (t && (t.tagName === 'INPUT' || t.tagName === 'TEXTAREA')) return;
if (e.key === 'ArrowRight' || e.key === ' ' || e.key === 'PageDown') { e.preventDefault(); go(active + 1); }
else if (e.key === 'ArrowLeft' || e.key === 'PageUp') { e.preventDefault(); go(active - 1); }
else if (e.key === 'Home') { e.preventDefault(); go(0); }
else if (e.key === 'End') { e.preventDefault(); go(slides.length - 1); }
}
// listen on both surfaces, capture phase
window.addEventListener('keydown', onKey, true);
document.addEventListener('keydown', onKey, true);
document.addEventListener('scroll', syncFromScroll, { passive: true, capture: true });
window.addEventListener('scroll', syncFromScroll, { passive: true });
// auto-focus
document.body.setAttribute('tabindex', '-1');
document.body.style.outline = 'none';
function focusDeck() { try { window.focus(); document.body.focus({ preventScroll: true }); } catch (_) {} }
document.addEventListener('mousedown', focusDeck);
window.addEventListener('load', focusDeck);
focusDeck();
// restore last-seen position
try {
var saved = parseInt(localStorage.getItem(KEY) || '0', 10);
if (!isNaN(saved) && saved >= 0 && saved < slides.length) {
setActive(saved);
scroller().scrollTo({ left: saved * window.innerWidth, behavior: 'instant' });
} else {
setActive(0);
}
} catch (_) { setActive(0); }
})();
</script>
</body>
</html>
+141
View File
@@ -0,0 +1,141 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Filebase · Investor deck — Q2 2026</title>
<style>
:root {
--bg: #fafaf9; --fg: #1c1b1a; --muted: #6b6964; --accent: #c96442; --surface: #ffffff;
}
* { box-sizing: border-box; }
html, body { margin: 0; height: 100%; }
body {
background: var(--bg);
color: var(--fg);
font: 18px/1.5 -apple-system, system-ui, sans-serif;
display: flex;
overflow-x: auto;
overflow-y: hidden;
scroll-snap-type: x mandatory;
scroll-behavior: smooth;
}
body::-webkit-scrollbar { display: none; }
.slide {
flex: 0 0 100vw;
height: 100vh;
scroll-snap-align: start;
padding: 80px 96px;
display: flex;
flex-direction: column;
justify-content: center;
position: relative;
}
.slide.title { background: var(--fg); color: var(--bg); }
.eyebrow { font-size: 12px; letter-spacing: 0.1em; text-transform: uppercase; color: var(--accent); margin-bottom: 28px; }
.slide h1 { font-size: clamp(48px, 7vw, 96px); line-height: 1.05; letter-spacing: -0.025em; margin: 0 0 20px; max-width: 16ch; }
.slide h2 { font-size: clamp(32px, 4vw, 48px); letter-spacing: -0.015em; margin: 0 0 20px; max-width: 20ch; }
.slide .body { font-size: 22px; color: var(--muted); max-width: 56ch; }
.slide.title .body { color: rgba(250,250,249,0.7); }
.slide.big-stat .number { font-size: clamp(120px, 22vw, 280px); line-height: 0.9; letter-spacing: -0.04em; color: var(--accent); margin-bottom: 16px; font-weight: 600; }
.slide.big-stat .caption { font-size: 24px; color: var(--muted); max-width: 24ch; }
.quote-mark { font-family: Georgia, serif; font-size: 200px; line-height: 0.7; color: var(--accent); opacity: 0.18; margin-bottom: -40px; }
.quote-text { font-family: Georgia, serif; font-size: 36px; line-height: 1.3; max-width: 26ch; margin: 0 0 28px; }
.quote-author { font-size: 14px; color: var(--muted); }
.grid-3 { display: grid; grid-template-columns: repeat(3, 1fr); gap: 32px; margin-top: 40px; }
.grid-3 .pt { border-top: 2px solid var(--accent); padding-top: 16px; }
.grid-3 .pt .h { font-size: 18px; font-weight: 500; margin: 0 0 8px; }
.grid-3 .pt .p { color: var(--muted); margin: 0; font-size: 16px; }
.counter { position: fixed; bottom: 24px; right: 32px; font-family: ui-monospace, monospace; font-size: 12px; color: var(--muted); background: var(--surface); padding: 4px 10px; border-radius: 999px; border: 1px solid #e6e4e0; }
.hint { position: fixed; bottom: 24px; left: 32px; font-size: 11px; color: var(--muted); }
</style>
</head>
<body>
<section class="slide title" data-od-id="slide-1">
<div class="eyebrow" style="color:#c96442;">Filebase · Series B · Q2 2026</div>
<h1>The bandwidth bill is the bug.</h1>
<p class="body">A sync engine that ships only what changed. Backed by 3,184 paying teams.</p>
</section>
<section class="slide" data-od-id="slide-2">
<div class="eyebrow">Problem</div>
<h2>Every other tool re-uploads the whole file.</h2>
<p class="body">Edit one frame in a 4 GB Final Cut project; today's tools sync all 4 GB. The video, post-production, and design industries are eating multi-thousand-dollar bandwidth bills they shouldn't be.</p>
</section>
<section class="slide big-stat" data-od-id="slide-3">
<div class="number">38×</div>
<div class="caption">less data moved over the wire vs. naive sync, on real customer workloads.</div>
</section>
<section class="slide" data-od-id="slide-4">
<div class="eyebrow">Why now</div>
<h2>Three shifts make this market real.</h2>
<div class="grid-3">
<div class="pt"><h3 class="h">Remote post-production</h3><p class="p">Editors don't sit in one room any more. Cloud sync went from convenient to load-bearing.</p></div>
<div class="pt"><h3 class="h">AI workflows</h3><p class="p">Diffusion checkpoints are 7 GB. Engineers iterate on them daily. Existing tools choke.</p></div>
<div class="pt"><h3 class="h">Bandwidth pricing</h3><p class="p">Egress costs 4× what it did in 2022. Storage is cheap; movement is expensive.</p></div>
</div>
</section>
<section class="slide" data-od-id="slide-5">
<div class="quote-mark">"</div>
<p class="quote-text">Filebase pays for itself in the first month. We were going to hire a dedicated DevOps person to babysit our sync — instead we just switched.</p>
<p class="quote-author">— Mira Hassan, CTO at Northwind Studios</p>
</section>
<section class="slide title" data-od-id="slide-6">
<div class="eyebrow" style="color:#c96442;">Ask</div>
<h1>$22M to ship the next sync engine.</h1>
<p class="body">18-month runway, hire 14, expand to enterprise on-prem.</p>
</section>
<div class="counter" id="counter">1 / 6</div>
<div class="hint">← / → to navigate</div>
<script>
const slides = document.querySelectorAll('.slide');
const counter = document.getElementById('counter');
let active = 0;
// Detect the real scroller — when body has `display: flex` + `overflow-x: auto`
// the scroller can be body OR documentElement depending on the host (in
// particular, the OD srcdoc iframe). Pick whichever actually overflows.
function scroller() {
if (document.body.scrollWidth > document.body.clientWidth + 1) return document.body;
return document.scrollingElement || document.documentElement;
}
function go(i) {
const next = Math.max(0, Math.min(slides.length - 1, i));
active = next;
counter.textContent = (next + 1) + ' / ' + slides.length;
scroller().scrollTo({ left: next * window.innerWidth, behavior: 'smooth' });
}
function syncFromScroll() {
const i = Math.round(scroller().scrollLeft / window.innerWidth);
if (i !== active && i >= 0 && i < slides.length) {
active = i;
counter.textContent = (i + 1) + ' / ' + slides.length;
}
}
function onKey(e) {
if (e.target && (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA')) return;
if (e.key === 'ArrowRight' || e.key === ' ' || e.key === 'PageDown') { e.preventDefault(); go(active + 1); }
else if (e.key === 'ArrowLeft' || e.key === 'PageUp') { e.preventDefault(); go(active - 1); }
else if (e.key === 'Home') { e.preventDefault(); go(0); }
else if (e.key === 'End') { e.preventDefault(); go(slides.length - 1); }
}
// Listen on both window and document in capture phase so the handler
// fires regardless of which element holds focus inside the iframe.
window.addEventListener('keydown', onKey, true);
document.addEventListener('keydown', onKey, true);
// And listen for scroll on both surfaces — same reason.
document.addEventListener('scroll', syncFromScroll, { passive: true, capture: true });
window.addEventListener('scroll', syncFromScroll, { passive: true });
// Auto-focus body so arrow keys work without a click.
document.body.setAttribute('tabindex', '-1');
document.body.style.outline = 'none';
function focusDeck() { try { window.focus(); document.body.focus({ preventScroll: true }); } catch (_) {} }
document.addEventListener('mousedown', focusDeck);
window.addEventListener('load', focusDeck);
focusDeck();
</script>
</body>
</html>
@@ -0,0 +1,55 @@
# Simple deck checklist
Run before emitting `<artifact>`. P0 must pass.
## P0 — must pass
- [ ] **Every `<section class="slide">` has a theme class.** Each is exactly one of: `light`, `dark`, `hero light`, `hero dark`. No bare `class="slide"`. No bare `class="slide hero"`.
- [ ] **No 3+ same-theme slides in a row.** Mentally list the classes from slide 1 to N — if you see `light light light` anywhere, change the middle one.
- [ ] **For 8+ slides: at least one `hero dark` AND at least one `hero light`.** A long all-light deck is sleepy; a long all-dark deck is heavy.
- [ ] **Display headlines use `var(--font-display)` (serif).** `.h-hero`, `.h-xl`, `.h-md` and `.quote-text` all enforce this — don't override.
- [ ] **No raw hex outside `:root`.** Every color is `var(--bg)` / `--fg` / `--muted` / `--border` / `--accent` / `--surface`. Grep `#[0-9a-fA-F]{3,8}` outside `:root{}` should return nothing.
- [ ] **Accent appears at most twice on any single slide.** On stat slides, the number itself is the only accent. Don't also color the eyebrow + a button + a border.
- [ ] **The 5-rule nav script is intact.** Don't replace `scroller()` with `document.body`. Don't drop one of the dual capture-phase listeners. Don't use `scrollIntoView()`. (The seed has the working version — leave it.)
- [ ] **No `scrollIntoView()` calls.** Breaks iframe boundaries.
- [ ] **`data-screen-label` on every slide** (e.g. `"01 Cover"`, `"05 Big stat"`). Used by chat for "edit slide 5".
- [ ] **No invented metrics.** Numbers come from the brief or a real source. "10× faster" / "99.9% uptime" without source = remove.
- [ ] **No emoji icons / no purple gradients / no rounded boxes with left-border accent.** Anti-slop trio.
## P1 — should pass
- [ ] **Cover is `hero light center`.** Inverting cover-to-dark works only when the entire deck is dark.
- [ ] **Cover h1 ≤ 8 words.** A long cover headline is the writing's job, not the design's.
- [ ] **Body lead text under 56ch.** `max-width: 56ch` enforces this — don't override.
- [ ] **Big-stat slides have one number, not three.** If you have 3 numbers, give them 3 slides.
- [ ] **One quote per deck.** Two pull-quote slides feel like a brochure; one feels like a punctuation mark.
- [ ] **Closing slide is decisive.** A clear ask, a takeaway sentence, a date — not a "thank you".
- [ ] **Numerics in mono.** Stats, prices, version numbers, dates use `font-family: var(--font-mono)` (the `.stat-num` already does; `.meta` does).
- [ ] **At 1280×800 and 1440×900, no overflow.** Test by setting the browser to those sizes; nothing clips.
## P2 — nice to have
- [ ] **Position persists across refresh** (the seed's `localStorage` save/restore handles this).
- [ ] **Top progress bar fills as you advance** (already in seed).
- [ ] **Counter pill is visible at all times** (already in seed).
## Theme rhythm spot-check
After you finish, run:
```
grep 'class="slide' index.html
```
Read the class list as a single sequence. The healthy patterns look like:
- `hero light` `light` `hero dark` `light` `dark` `hero light` `light` `hero dark`
- `hero light` `light` `light` `dark` `hero light` `dark` `hero dark`
Bad patterns:
- `light light light light light light` — flat
- `dark dark dark dark dark dark` — heavy
- `hero hero hero hero` — no rest
If your sequence is bad, swap a few middle slides to rebalance.
+201
View File
@@ -0,0 +1,201 @@
# Simple deck slide layouts
**8 paste-ready slide skeletons.** Drop into `<body>` of `assets/template.html`. Don't write slides from scratch — pick the closest layout, paste, swap copy.
## Pre-flight
1. **Read `assets/template.html`** end-to-end — every class below is defined in its `<style>` block. The fixed counter, progress bar, hint, and the 5-rule nav script at the bottom are already wired up; do not re-implement them.
2. **Plan the slide list AND theme rhythm before pasting any slide.** See "Theme rhythm" below — this is the single biggest determinant of whether the deck feels alive or sleepy.
3. **Read the active DESIGN.md** — map its tokens to the six `:root` variables in the seed.
## Theme rhythm — the rule that prevents 6-slide sleep
Every `<section class="slide">` MUST include exactly one of:
- `light` — default white-paper surface
- `dark` — inverted, fg-on-bg
- `hero light` — same as light + extra padding (for cover, big stat, big quote)
- `hero dark` — same as dark + extra padding
**Rules:**
- No 3+ same-theme slides in a row. `light light light` → boring.
- For decks with **8+ slides**: at least one `hero dark` AND at least one `hero light`.
- A `dark` slide every 34 slides creates the "breath" that makes the next light slide hit harder.
- The cover is almost always `hero light`. The closing is often `hero dark` or `hero light`.
Before emitting, run mentally: list every slide's class. If you see `light × 5 in a row`, change one to `dark`.
## Class inventory
> `slide` `light` `dark` `hero` `center` `eyebrow` `h-hero` `h-xl` `h-md` `lead` `meta` `stat-num` `unit` `stat-caption` `quote-mark` `quote-text` `quote-author` `pt-grid` `pt` `pipeline` `step` `nb` `ba-grid` `ba-col` `ba-label` `ph-img` `wide` `tall`
If you reach for a class not on this list, define it in the seed's `<style>` first.
---
## Layout 1 — Cover (slide 1)
`hero light center`. One eyebrow with date/context, one big serif headline (≤ 8 words for the punch), one lead sentence.
```html
<section class="slide hero light center" data-screen-label="01 Cover">
<div class="eyebrow">Filebase · Series B · Q2 2026</div>
<h1 class="h-hero">The bandwidth bill is the bug.</h1>
<p class="lead">A sync engine that ships only what changed. Backed by 3,184 paying teams.</p>
</section>
```
## Layout 2 — Body slide (eyebrow + headline + lead)
The workhorse. Use 36× per deck. Vary `light` / `dark` for rhythm.
```html
<section class="slide light" data-screen-label="04 Why now">
<p class="eyebrow">Why now</p>
<h2 class="h-xl">Three shifts make this market real.</h2>
<p class="lead">Remote post-production. AI workflows. Bandwidth pricing up 4× since 2022. Storage is cheap; movement is expensive.</p>
</section>
```
## Layout 3 — Big stat (data billboard)
`hero light center` or `hero dark center`. One number. Don't put 3 numbers on one slide — split into 3 stat slides.
```html
<section class="slide hero dark center" data-screen-label="05 Big stat">
<div class="stat-num">38<span class="unit">×</span></div>
<p class="stat-caption">less data moved over the wire vs. naive sync, on real customer workloads.</p>
</section>
```
## Layout 4 — Three-point row
A small headline above three rule-topped points. Each point ≤ 2 sentences.
```html
<section class="slide light" data-screen-label="04 Why now">
<p class="eyebrow">Why now</p>
<h2 class="h-xl">Three shifts make this market real.</h2>
<div class="pt-grid">
<div class="pt">
<h3>Remote post-production</h3>
<p>Editors don't sit in one room any more. Cloud sync went from convenient to load-bearing.</p>
</div>
<div class="pt">
<h3>AI workflows</h3>
<p>Diffusion checkpoints are 7 GB. Engineers iterate on them daily. Existing tools choke.</p>
</div>
<div class="pt">
<h3>Bandwidth pricing</h3>
<p>Egress costs 4× what it did in 2022. Storage is cheap; movement is expensive.</p>
</div>
</div>
</section>
```
## Layout 5 — Pipeline (numbered steps)
Workflow / process / how-it-works. Up to 4 steps; if you need more, split across two slides.
```html
<section class="slide dark" data-screen-label="06 Pipeline">
<p class="eyebrow">How it works</p>
<h2 class="h-md">Four passes, end to end.</h2>
<div class="pipeline">
<div class="step">
<span class="nb">01</span>
<h3>Watch</h3>
<p>FS events from kernel, debounced 50ms.</p>
</div>
<div class="step">
<span class="nb">02</span>
<h3>Chunk</h3>
<p>Content-defined splitting, ~64KB target.</p>
</div>
<div class="step">
<span class="nb">03</span>
<h3>Diff</h3>
<p>Bloom-filtered hash compare against remote.</p>
</div>
<div class="step">
<span class="nb">04</span>
<h3>Ship</h3>
<p>Only the chunks the remote doesn't have.</p>
</div>
</div>
</section>
```
## Layout 6 — Big quote / pull quote
`hero light center`. One quote, one attribution. Italic-feel via the serif display, not actual `<em>`.
```html
<section class="slide hero light center" data-screen-label="07 Quote">
<div class="quote-mark">"</div>
<p class="quote-text">Filebase pays for itself in the first month. We were going to hire a dedicated DevOps person — instead we just switched.</p>
<p class="quote-author">— Mira Hassan, CTO at Northwind Studios</p>
</section>
```
## Layout 7 — Before / after (comparison)
Two columns, same shape, contrasting state. Don't decorate the columns — the contrast comes from copy and from picking one column to tint with the accent.
```html
<section class="slide light" data-screen-label="08 Before / after">
<p class="eyebrow">The shift</p>
<h2 class="h-md">From whole-file sync to chunk-level sync.</h2>
<div class="ba-grid">
<div class="ba-col">
<p class="ba-label">Before · 2022</p>
<h3>Edit one frame, ship the whole 4 GB project.</h3>
<p>$1,800 / month bandwidth bill on a single Final Cut workflow. Editors waiting 12 minutes per save.</p>
</div>
<div class="ba-col">
<p class="ba-label" style="color: var(--accent);">After · 2026</p>
<h3>Edit one frame, ship 240 KB.</h3>
<p>$200 / month on the same workflow. Save-to-remote completes inside the editor's auto-save window.</p>
</div>
</div>
</section>
```
## Layout 8 — Closing / CTA
`hero dark center` or `hero light center`. One sentence on the ask, one supporting line. The audience leaves remembering this.
```html
<section class="slide hero dark center" data-screen-label="09 Ask">
<div class="eyebrow">Ask</div>
<h2 class="h-hero">$22M to ship the next sync engine.</h2>
<p class="lead">18-month runway, hire 14, expand to enterprise on-prem.</p>
</section>
```
---
## Default arcs
**6-slide pitch (the minimum):**
1. `hero light center` — Cover (Layout 1)
2. `light` — Problem body (Layout 2)
3. `hero dark center` — Big stat (Layout 3)
4. `light` — Three points (Layout 4)
5. `hero light center`— Quote (Layout 6)
6. `hero dark center` — Ask (Layout 8)
**10-slide narrative:**
1. `hero light center` — Cover
2. `light` — Problem
3. `hero dark center` — Big stat 1
4. `light` — Three points
5. `dark` — Pipeline (Layout 5)
6. `hero light center`— Quote
7. `light` — Before / after (Layout 7)
8. `hero dark center` — Big stat 2
9. `light` — Team / metrics
10. `hero dark center`— Ask
After laying out, mentally read the class list — `light dark light dark` should show alternation, not blocks of the same theme.